/** * Apply side effects from a spell attack to the player * * \param spell is the attack type * \param dam is the amount of damage caused by the attack * \param m_idx is the attacking monster * \param rlev is its level * \param seen is whether @ can see it */ static void do_side_effects(int spell, int dam, int m_idx, bool seen) { monster_type *m_ptr = cave_monster(cave, m_idx); monster_race *r_ptr = &r_info[m_ptr->r_idx]; const struct spell_effect *re_ptr; const struct mon_spell *rs_ptr = &mon_spell_table[spell]; int i, choice[99], dur = 0, j = 0, count = 0; s32b d = 0; bool sustain = FALSE, perma = FALSE, chosen[RSE_MAX] = { 0 }; /* Extract the monster level */ int rlev = ((r_ptr->level >= 1) ? r_ptr->level : 1); /* First we note all the effects we'll be doing. */ for (re_ptr = spell_effect_table; re_ptr->index < RSE_MAX; re_ptr++) { if ((re_ptr->method && (re_ptr->method == rs_ptr->index)) || (re_ptr->gf && (re_ptr->gf == rs_ptr->gf))) { /* If we have a choice of effects, we create a cum freq table */ if (re_ptr->chance) { for (i = j; i < (j + re_ptr->chance); i++) choice[i] = re_ptr->index; j = i; } else chosen[re_ptr->index] = TRUE; } } /* If we have built a cum freq table, choose an effect from it */ if (j) chosen[choice[randint0(j)]] = TRUE; /* Now we cycle through again to activate the chosen effects */ for (re_ptr = spell_effect_table; re_ptr->index < RSE_MAX; re_ptr++) { if (chosen[re_ptr->index]) { /* * Check for resistance - there are three possibilities: * 1. Immunity to the attack type if side_immune is TRUE * 2. Resistance to the attack type if it affords no immunity * 3. Resistance to the specific side-effect * * TODO - add interesting messages to the RSE_ and GF_ tables * to replace the generic ones below. (See #1376) */ if (re_ptr->res_flag) update_smart_learn(m_ptr, p_ptr, re_ptr->res_flag); if ((rs_ptr->gf && check_side_immune(rs_ptr->gf)) || check_state(p_ptr, re_ptr->res_flag, p_ptr->state.flags)) { msg("You resist the effect!"); continue; } /* Allow saving throw if available */ if (re_ptr->save && randint0(100) < p_ptr->state.skills[SKILL_SAVE]) { msg("You avoid the effect!"); continue; } /* Implement the effect */ if (re_ptr->timed) { /* Calculate base duration (m_bonus is not used) */ dur = randcalc(re_ptr->base, 0, RANDOMISE); /* Calculate the damage-dependent duration (m_bonus is * used as a cap) */ dur += damcalc(re_ptr->dam.dice, re_ptr->dam.sides * dam / 100, RANDOMISE); if (re_ptr->dam.m_bonus && (dur > re_ptr->dam.m_bonus)) dur = re_ptr->dam.m_bonus; /* Apply the effect - we have already checked for resistance */ (void)player_inc_timed(p_ptr, re_ptr->flag, dur, TRUE, FALSE); } else { switch (re_ptr->flag) { case S_INV_DAM: if (dam > 0) inven_damage(p_ptr, re_ptr->gf, MIN(dam * randcalc(re_ptr->dam, 0, RANDOMISE), 300)); break; case S_TELEPORT: /* m_bonus is used as a clev filter */ if (!re_ptr->dam.m_bonus || randint1(re_ptr->dam.m_bonus) > p_ptr->lev) teleport_player(randcalc(re_ptr->base, 0, RANDOMISE)); break; case S_TELE_TO: teleport_player_to(m_ptr->fy, m_ptr->fx); break; case S_TELE_LEV: teleport_player_level(); break; case S_TELE_SELF: teleport_away(m_ptr, randcalc(re_ptr->base, 0, RANDOMISE)); break; case S_DRAIN_LIFE: d = re_ptr->base.base + (p_ptr->exp * re_ptr->base.sides / 100) * MON_DRAIN_LIFE; msg("You feel your life force draining away!"); player_exp_lose(p_ptr, d, FALSE); break; case S_DRAIN_STAT: /* m_bonus is used as a flag */ if (re_ptr->dam.m_bonus > 0) sustain = TRUE; if (abs(re_ptr->dam.m_bonus) > 1) perma = TRUE; drain_stats(randcalc(re_ptr->base, 0, RANDOMISE), sustain, perma); break; case S_SWAP_STAT: swap_stats(); break; case S_DRAIN_ALL: msg("You're not as powerful as you used to be..."); for (i = 0; i < A_MAX; i++) player_stat_dec(p_ptr, i, FALSE); break; case S_DISEN: (void)apply_disenchant(0); break; case S_DRAIN_MANA: drain_mana(m_idx, rlev, seen); break; case S_HEAL: heal_self(m_idx, rlev, seen); break; case S_DARKEN: (void)unlight_area(0, 3); break; case S_TRAPS: (void)trap_creation(); break; case S_AGGRAVATE: aggravate_monsters(m_idx); break; case S_KIN: summon_kin_type = r_ptr->d_char; case S_MONSTER: case S_MONSTERS: case S_SPIDER: case S_HOUND: case S_HYDRA: case S_AINU: case S_ANIMAL: case S_DEMON: case S_HI_DEMON: case S_UNDEAD: case S_HI_UNDEAD: case S_WRAITH: case S_DRAGON: case S_HI_DRAGON: case S_UNIQUE: count = summon_monster_aux(re_ptr->flag, m_idx, rlev, re_ptr->base.base); /* In the special case that uniques or wraiths were summoned but all were dead S_HI_UNDEAD is used instead */ if ((!count) && ((re_ptr->flag == S_WRAITH) || (re_ptr->flag == S_UNIQUE))) count = summon_monster_aux(S_HI_UNDEAD, m_idx, rlev, re_ptr->base.base); if (count && p_ptr->timed[TMD_BLIND]) msgt(rs_ptr->msgt, "You hear %s appear nearby.", (count > 1 ? "many things" : "something")); default: break; } } } } return; }
/* * Attack the player via physical attacks. */ bool make_attack_normal(int m_idx) { monster_type *m_ptr = &mon_list[m_idx]; monster_race *r_ptr = &r_info[m_ptr->r_idx]; monster_lore *l_ptr = &l_list[m_ptr->r_idx]; int ap_cnt; int i, k, tmp, ac, rlev; int do_cut, do_stun; s32b gold; object_type *o_ptr; char o_name[80]; char m_name[80]; char ddesc[80]; bool blinked; /* Not allowed to attack */ if (r_ptr->flags1 & (RF1_NEVER_BLOW)) return (FALSE); /* Total armor */ ac = p_ptr->ac + p_ptr->to_a; /* Extract the effective monster level */ rlev = ((r_ptr->level >= 1) ? r_ptr->level : 1); /* Get the monster name (or "it") */ monster_desc(m_name, sizeof(m_name), m_ptr, 0); /* Get the "died from" information (i.e. "a kobold") */ monster_desc(ddesc, sizeof(ddesc), m_ptr, 0x88); /* Assume no blink */ blinked = FALSE; /* Scan through all blows */ for (ap_cnt = 0; ap_cnt < MONSTER_BLOW_MAX; ap_cnt++) { bool visible = FALSE; bool obvious = FALSE; int power = 0; int damage = 0; cptr act = NULL; /* Extract the attack infomation */ int effect = r_ptr->blow[ap_cnt].effect; int method = r_ptr->blow[ap_cnt].method; int d_dice = r_ptr->blow[ap_cnt].d_dice; int d_side = r_ptr->blow[ap_cnt].d_side; /* Hack -- no more attacks */ if (!method) break; /* Handle "leaving" */ if (p_ptr->leaving) break; /* Extract visibility (before blink) */ if (m_ptr->ml) visible = TRUE; /* Extract the attack "power" */ switch (effect) { case RBE_HURT: power = 60; break; case RBE_POISON: power = 5; break; case RBE_UN_BONUS: power = 20; break; case RBE_UN_POWER: power = 15; break; case RBE_EAT_GOLD: power = 5; break; case RBE_EAT_ITEM: power = 5; break; case RBE_EAT_FOOD: power = 5; break; case RBE_EAT_LITE: power = 5; break; case RBE_ACID: power = 0; break; case RBE_ELEC: power = 10; break; case RBE_FIRE: power = 10; break; case RBE_COLD: power = 10; break; case RBE_BLIND: power = 2; break; case RBE_CONFUSE: power = 10; break; case RBE_TERRIFY: power = 10; break; case RBE_PARALYZE: power = 2; break; case RBE_LOSE_STR: power = 0; break; case RBE_LOSE_DEX: power = 0; break; case RBE_LOSE_CON: power = 0; break; case RBE_LOSE_INT: power = 0; break; case RBE_LOSE_WIS: power = 0; break; case RBE_LOSE_CHR: power = 0; break; case RBE_LOSE_ALL: power = 2; break; case RBE_SHATTER: power = 60; break; case RBE_EXP_10: power = 5; break; case RBE_EXP_20: power = 5; break; case RBE_EXP_40: power = 5; break; case RBE_EXP_80: power = 5; break; case RBE_HALLU: power = 10; break; } /* Monster hits player */ if (!effect || check_hit(power, rlev)) { /* Always disturbing */ disturb(1, 0); /* Hack -- Apply "protection from evil" */ if ((p_ptr->protevil > 0) && (r_ptr->flags3 & (RF3_EVIL)) && (p_ptr->lev >= rlev) && ((rand_int(100) + p_ptr->lev) > 50)) { /* Remember the Evil-ness */ if (m_ptr->ml) { l_ptr->flags3 |= (RF3_EVIL); } /* Message */ msg_format("%^s is repelled.", m_name); /* Hack -- Next attack */ continue; } /* Assume no cut or stun */ do_cut = do_stun = 0; /* Describe the attack method */ switch (method) { case RBM_HIT: { act = "hits you."; do_cut = do_stun = 1; break; } case RBM_TOUCH: { act = "touches you."; break; } case RBM_PUNCH: { act = "punches you."; do_stun = 1; break; } case RBM_KICK: { act = "kicks you."; do_stun = 1; break; } case RBM_CLAW: { act = "claws you."; do_cut = 1; break; } case RBM_BITE: { act = "bites you."; do_cut = 1; break; } case RBM_STING: { act = "stings you."; break; } case RBM_XXX1: { act = "XXX1's you."; break; } case RBM_BUTT: { act = "butts you."; do_stun = 1; break; } case RBM_CRUSH: { act = "crushes you."; do_stun = 1; break; } case RBM_ENGULF: { act = "engulfs you."; break; } case RBM_XXX2: { act = "XXX2's you."; break; } case RBM_CRAWL: { act = "crawls on you."; break; } case RBM_DROOL: { act = "drools on you."; break; } case RBM_SPIT: { act = "spits on you."; break; } case RBM_XXX3: { act = "XXX3's on you."; break; } case RBM_GAZE: { act = "gazes at you."; break; } case RBM_WAIL: { act = "wails at you."; break; } case RBM_SPORE: { act = "releases spores at you."; break; } case RBM_XXX4: { act = "projects XXX4's at you."; break; } case RBM_BEG: { act = "begs you for money."; break; } case RBM_INSULT: { act = desc_insult[rand_int(MAX_DESC_INSULT)]; break; } case RBM_MOAN: { act = desc_moan[rand_int(MAX_DESC_MOAN)]; break; } case RBM_XXX5: { act = "XXX5's you."; break; } } /* Message */ if (act) msg_format("%^s %s", m_name, act); /* Hack -- assume all attacks are obvious */ obvious = TRUE; /* Roll out the damage */ damage = damroll(d_dice, d_side); /* Apply appropriate damage */ switch (effect) { case 0: { /* Hack -- Assume obvious */ obvious = TRUE; /* Hack -- No damage */ damage = 0; break; } case RBE_HURT: { /* Obvious */ obvious = TRUE; /* Hack -- Player armor reduces total damage */ damage -= (damage * ((ac < 150) ? ac : 150) / 250); /* Take damage */ take_hit(damage, ddesc); break; } case RBE_POISON: { /* Take damage */ take_hit(damage, ddesc); /* Take "poison" effect */ if (!(p_ptr->resist_pois || p_ptr->oppose_pois)) { if (set_poisoned(p_ptr->poisoned + randint(rlev) + 5)) { obvious = TRUE; } } /* Learn about the player */ update_smart_learn(m_idx, DRS_RES_POIS); break; } case RBE_UN_BONUS: { /* Take damage */ take_hit(damage, ddesc); /* Allow complete resist */ if (!p_ptr->resist_disen) { /* Apply disenchantment */ if (apply_disenchant(0)) obvious = TRUE; } /* Learn about the player */ update_smart_learn(m_idx, DRS_RES_DISEN); break; } case RBE_UN_POWER: { /* Take damage */ take_hit(damage, ddesc); /* Find an item */ for (k = 0; k < 10; k++) { /* Pick an item */ i = rand_int(INVEN_PACK); /* Obtain the item */ o_ptr = &inventory[i]; /* Skip non-objects */ if (!o_ptr->k_idx) continue; /* Drain charged wands/staffs */ if (((o_ptr->tval == TV_STAFF) || (o_ptr->tval == TV_WAND)) && (o_ptr->pval > 0)) { /* Calculate healed hitpoints */ int heal = rlev * o_ptr->pval * o_ptr->number; /* Don't heal more than max hp */ heal = MIN(heal, m_ptr->maxhp - m_ptr->hp); /* Message */ msg_print("Energy drains from your pack!"); /* Obvious */ obvious = TRUE; /* Heal */ m_ptr->hp += heal; /* Redraw (later) if needed */ if (p_ptr->health_who == m_idx) p_ptr->redraw |= (PR_HEALTH); /* Uncharge */ o_ptr->pval = 0; /* Combine / Reorder the pack */ p_ptr->notice |= (PN_COMBINE | PN_REORDER); /* Window stuff */ p_ptr->window |= (PW_INVEN); /* Done */ break; } } break; } case RBE_EAT_GOLD: { /* Take damage */ take_hit(damage, ddesc); /* Obvious */ obvious = TRUE; /* Saving throw (unless paralyzed) based on dex and level */ if (!p_ptr->paralyzed && (rand_int(100) < (adj_dex_safe[p_ptr->stat_ind[A_DEX]] + p_ptr->lev))) { /* Saving throw message */ msg_print("You quickly protect your money pouch!"); /* Occasional blink anyway */ if (rand_int(3)) blinked = TRUE; } /* Eat gold */ else { gold = (p_ptr->au / 10) + randint(25); if (gold < 2) gold = 2; if (gold > 5000) gold = (p_ptr->au / 20) + randint(3000); if (gold > p_ptr->au) gold = p_ptr->au; p_ptr->au -= gold; if (gold <= 0) { msg_print("Nothing was stolen."); } else if (p_ptr->au) { msg_print("Your purse feels lighter."); msg_format("%ld coins were stolen!", (long)gold); } else { msg_print("Your purse feels lighter."); msg_print("All of your coins were stolen!"); } /* Redraw gold */ p_ptr->redraw |= (PR_GOLD); /* Window stuff */ p_ptr->window |= (PW_PLAYER_0 | PW_PLAYER_1); /* Blink away */ blinked = TRUE; } break; } case RBE_EAT_ITEM: { /* Take damage */ take_hit(damage, ddesc); /* Saving throw (unless paralyzed) based on dex and level */ if (!p_ptr->paralyzed && (rand_int(100) < (adj_dex_safe[p_ptr->stat_ind[A_DEX]] + p_ptr->lev))) { /* Saving throw message */ msg_print("You grab hold of your backpack!"); /* Occasional "blink" anyway */ blinked = TRUE; /* Obvious */ obvious = TRUE; /* Done */ break; } /* Find an item */ for (k = 0; k < 10; k++) { object_type *i_ptr; object_type object_type_body; /* Pick an item */ i = rand_int(INVEN_PACK); /* Obtain the item */ o_ptr = &inventory[i]; /* Skip non-objects */ if (!o_ptr->k_idx) continue; /* Skip artifacts */ if (artifact_p(o_ptr)) continue; /* Get a description */ object_desc(o_name, sizeof(o_name), o_ptr, FALSE, 3); /* Message */ msg_format("%sour %s (%c) was stolen!", ((o_ptr->number > 1) ? "One of y" : "Y"), o_name, index_to_label(i)); /* Get local object */ i_ptr = &object_type_body; /* Obtain local object */ object_copy(i_ptr, o_ptr); /* Modify number */ i_ptr->number = 1; /* Carry the object */ (void)monster_carry(m_idx, i_ptr); /* Steal the items */ inven_item_increase(i, -1); inven_item_optimize(i); /* Obvious */ obvious = TRUE; /* Blink away */ blinked = TRUE; /* Done */ break; } break; } case RBE_EAT_FOOD: { /* Take damage */ take_hit(damage, ddesc); /* Steal some food */ for (k = 0; k < 10; k++) { /* Pick an item from the pack */ i = rand_int(INVEN_PACK); /* Get the item */ o_ptr = &inventory[i]; /* Skip non-objects */ if (!o_ptr->k_idx) continue; /* Skip non-food objects */ if (o_ptr->tval != TV_FOOD) continue; /* Get a description */ object_desc(o_name, sizeof(o_name), o_ptr, FALSE, 0); /* Message */ msg_format("%sour %s (%c) was eaten!", ((o_ptr->number > 1) ? "One of y" : "Y"), o_name, index_to_label(i)); /* Steal the items */ inven_item_increase(i, -1); inven_item_optimize(i); /* Obvious */ obvious = TRUE; /* Done */ break; } break; } case RBE_EAT_LITE: { /* Take damage */ take_hit(damage, ddesc); /* Get the lite */ o_ptr = &inventory[INVEN_LITE]; /* Drain fuel */ if ((o_ptr->pval > 0) && (!artifact_p(o_ptr))) { /* Reduce fuel */ o_ptr->pval -= (250 + randint(250)); if (o_ptr->pval < 1) o_ptr->pval = 1; /* Notice */ if (!p_ptr->blind) { msg_print("Your light dims."); obvious = TRUE; } /* Window stuff */ p_ptr->window |= (PW_EQUIP); } break; } case RBE_ACID: { /* Obvious */ obvious = TRUE; /* Message */ msg_print("You are covered in acid!"); /* Special damage */ acid_dam(damage, ddesc); /* Learn about the player */ update_smart_learn(m_idx, DRS_RES_ACID); break; } case RBE_ELEC: { /* Obvious */ obvious = TRUE; /* Message */ msg_print("You are struck by electricity!"); /* Take damage (special) */ elec_dam(damage, ddesc); /* Learn about the player */ update_smart_learn(m_idx, DRS_RES_ELEC); break; } case RBE_FIRE: { /* Obvious */ obvious = TRUE; /* Message */ msg_print("You are enveloped in flames!"); /* Take damage (special) */ fire_dam(damage, ddesc); /* Learn about the player */ update_smart_learn(m_idx, DRS_RES_FIRE); break; } case RBE_COLD: { /* Obvious */ obvious = TRUE; /* Message */ msg_print("You are covered with frost!"); /* Take damage (special) */ cold_dam(damage, ddesc); /* Learn about the player */ update_smart_learn(m_idx, DRS_RES_COLD); break; } case RBE_BLIND: { /* Take damage */ take_hit(damage, ddesc); /* Increase "blind" */ if (!p_ptr->resist_blind) { if (set_blind(p_ptr->blind + 10 + randint(rlev))) { obvious = TRUE; } } /* Learn about the player */ update_smart_learn(m_idx, DRS_RES_BLIND); break; } case RBE_CONFUSE: { /* Take damage */ take_hit(damage, ddesc); /* Increase "confused" */ if (!p_ptr->resist_confu) { if (set_confused(p_ptr->confused + 3 + randint(rlev))) { obvious = TRUE; } } /* Learn about the player */ update_smart_learn(m_idx, DRS_RES_CONFU); break; } case RBE_TERRIFY: { /* Take damage */ take_hit(damage, ddesc); /* Increase "afraid" */ if (p_ptr->resist_fear) { msg_print("You stand your ground!"); obvious = TRUE; } else if (rand_int(100) < p_ptr->skill_sav) { msg_print("You stand your ground!"); obvious = TRUE; } else { if (set_afraid(p_ptr->afraid + 3 + randint(rlev))) { obvious = TRUE; } } /* Learn about the player */ update_smart_learn(m_idx, DRS_RES_FEAR); break; } case RBE_PARALYZE: { /* Hack -- Prevent perma-paralysis via damage */ if (p_ptr->paralyzed && (damage < 1)) damage = 1; /* Take damage */ take_hit(damage, ddesc); /* Increase "paralyzed" */ if (p_ptr->free_act) { msg_print("You are unaffected!"); obvious = TRUE; } else if (rand_int(100) < p_ptr->skill_sav) { msg_print("You resist the effects!"); obvious = TRUE; } else { if (set_paralyzed(p_ptr->paralyzed + 3 + randint(rlev))) { obvious = TRUE; } } /* Learn about the player */ update_smart_learn(m_idx, DRS_FREE); break; } case RBE_LOSE_STR: { /* Take damage */ take_hit(damage, ddesc); /* Damage (stat) */ if (do_dec_stat(A_STR)) obvious = TRUE; break; } case RBE_LOSE_INT: { /* Take damage */ take_hit(damage, ddesc); /* Damage (stat) */ if (do_dec_stat(A_INT)) obvious = TRUE; break; } case RBE_LOSE_WIS: { /* Take damage */ take_hit(damage, ddesc); /* Damage (stat) */ if (do_dec_stat(A_WIS)) obvious = TRUE; break; } case RBE_LOSE_DEX: { /* Take damage */ take_hit(damage, ddesc); /* Damage (stat) */ if (do_dec_stat(A_DEX)) obvious = TRUE; break; } case RBE_LOSE_CON: { /* Take damage */ take_hit(damage, ddesc); /* Damage (stat) */ if (do_dec_stat(A_CON)) obvious = TRUE; break; } case RBE_LOSE_CHR: { /* Take damage */ take_hit(damage, ddesc); /* Damage (stat) */ if (do_dec_stat(A_CHR)) obvious = TRUE; break; } case RBE_LOSE_ALL: { /* Take damage */ take_hit(damage, ddesc); /* Damage (stats) */ if (do_dec_stat(A_STR)) obvious = TRUE; if (do_dec_stat(A_DEX)) obvious = TRUE; if (do_dec_stat(A_CON)) obvious = TRUE; if (do_dec_stat(A_INT)) obvious = TRUE; if (do_dec_stat(A_WIS)) obvious = TRUE; if (do_dec_stat(A_CHR)) obvious = TRUE; break; } case RBE_SHATTER: { /* Obvious */ obvious = TRUE; /* Hack -- Reduce damage based on the player armor class */ damage -= (damage * ((ac < 150) ? ac : 150) / 250); /* Take damage */ take_hit(damage, ddesc); /* Radius 8 earthquake centered at the monster */ if (damage > 23) earthquake(m_ptr->fy, m_ptr->fx, 8); break; } case RBE_EXP_10: { /* Obvious */ obvious = TRUE; /* Take damage */ take_hit(damage, ddesc); if (p_ptr->hold_life && (rand_int(100) < 95)) { msg_print("You keep hold of your life force!"); } else { s32b d = damroll(10, 6) + (p_ptr->exp/100) * MON_DRAIN_LIFE; if (p_ptr->hold_life) { msg_print("You feel your life slipping away!"); lose_exp(d/10); } else { msg_print("You feel your life draining away!"); lose_exp(d); } } break; } case RBE_EXP_20: { /* Obvious */ obvious = TRUE; /* Take damage */ take_hit(damage, ddesc); if (p_ptr->hold_life && (rand_int(100) < 90)) { msg_print("You keep hold of your life force!"); } else { s32b d = damroll(20, 6) + (p_ptr->exp / 100) * MON_DRAIN_LIFE; if (p_ptr->hold_life) { msg_print("You feel your life slipping away!"); lose_exp(d / 10); } else { msg_print("You feel your life draining away!"); lose_exp(d); } } break; } case RBE_EXP_40: { /* Obvious */ obvious = TRUE; /* Take damage */ take_hit(damage, ddesc); if (p_ptr->hold_life && (rand_int(100) < 75)) { msg_print("You keep hold of your life force!"); } else { s32b d = damroll(40, 6) + (p_ptr->exp / 100) * MON_DRAIN_LIFE; if (p_ptr->hold_life) { msg_print("You feel your life slipping away!"); lose_exp(d / 10); } else { msg_print("You feel your life draining away!"); lose_exp(d); } } break; } case RBE_EXP_80: { /* Obvious */ obvious = TRUE; /* Take damage */ take_hit(damage, ddesc); if (p_ptr->hold_life && (rand_int(100) < 50)) { msg_print("You keep hold of your life force!"); } else { s32b d = damroll(80, 6) + (p_ptr->exp / 100) * MON_DRAIN_LIFE; if (p_ptr->hold_life) { msg_print("You feel your life slipping away!"); lose_exp(d / 10); } else { msg_print("You feel your life draining away!"); lose_exp(d); } } break; } case RBE_HALLU: { /* Take damage */ take_hit(damage, ddesc); /* Increase "image" */ if (!p_ptr->resist_chaos) { if (set_image(p_ptr->image + 3 + randint(rlev / 2))) { obvious = TRUE; } } /* Learn about the player */ update_smart_learn(m_idx, DRS_RES_CHAOS); break; } } /* Hack -- only one of cut or stun */ if (do_cut && do_stun) { /* Cancel cut */ if (rand_int(100) < 50) { do_cut = 0; } /* Cancel stun */ else { do_stun = 0; } } /* Handle cut */ if (do_cut) { int k; /* Critical hit (zero if non-critical) */ tmp = monster_critical(d_dice, d_side, damage); /* Roll for damage */ switch (tmp) { case 0: k = 0; break; case 1: k = randint(5); break; case 2: k = randint(5) + 5; break; case 3: k = randint(20) + 20; break; case 4: k = randint(50) + 50; break; case 5: k = randint(100) + 100; break; case 6: k = 300; break; default: k = 500; break; } /* Apply the cut */ if (k) (void)set_cut(p_ptr->cut + k); } /* Handle stun */ if (do_stun) { int k; /* Critical hit (zero if non-critical) */ tmp = monster_critical(d_dice, d_side, damage); /* Roll for damage */ switch (tmp) { case 0: k = 0; break; case 1: k = randint(5); break; case 2: k = randint(10) + 10; break; case 3: k = randint(20) + 20; break; case 4: k = randint(30) + 30; break; case 5: k = randint(40) + 40; break; case 6: k = 100; break; default: k = 200; break; } /* Apply the stun */ if (k) (void)set_stun(p_ptr->stun + k); } } /* Monster missed player */ else { /* Analyze failed attacks */ switch (method) { case RBM_HIT: case RBM_TOUCH: case RBM_PUNCH: case RBM_KICK: case RBM_CLAW: case RBM_BITE: case RBM_STING: case RBM_XXX1: case RBM_BUTT: case RBM_CRUSH: case RBM_ENGULF: case RBM_XXX2: /* Visible monsters */ if (m_ptr->ml) { /* Disturbing */ disturb(1, 0); /* Message */ msg_format("%^s misses you.", m_name); } break; } } /* Analyze "visible" monsters only */ if (visible) { /* Count "obvious" attacks (and ones that cause damage) */ if (obvious || damage || (l_ptr->blows[ap_cnt] > 10)) { /* Count attacks of this type */ if (l_ptr->blows[ap_cnt] < MAX_UCHAR) { l_ptr->blows[ap_cnt]++; } } } } /* Blink away */ if (blinked) { msg_print("There is a puff of smoke!"); teleport_away(m_idx, MAX_SIGHT * 2 + 5); } /* Always notice cause of death */ if (p_ptr->is_dead && (l_ptr->deaths < MAX_SHORT)) { l_ptr->deaths++; } /* Assume we attacked */ return (TRUE); }
/* * Attack the player via physical attacks. */ bool make_attack_normal(int m_idx) { monster_type *m_ptr = &m_list[m_idx]; monster_race *r_ptr = &r_info[m_ptr->r_idx]; monster_lore *l_ptr = &l_list[m_ptr->r_idx]; int ap_cnt; int tmp, ac, rlev; int do_cut, do_stun, touched; char m_name[80]; char ddesc[80]; bool blinked; /* Not allowed to attack */ if (r_ptr->flags1 & (RF1_NEVER_BLOW)) return (FALSE); /* Total armor */ ac = p_ptr->ac + p_ptr->to_a; /* Extract the effective monster level */ rlev = ((r_ptr->level >= 1) ? r_ptr->level : 1); /* Get the monster name (or "it") */ monster_desc(m_name, m_ptr, 0); /* Get the "died from" information (i.e. "a goblin") */ monster_desc(ddesc, m_ptr, 0x88); /* Assume no blink */ blinked = FALSE; /* Scan through all four blows */ for (ap_cnt = 0; ap_cnt < 4; ap_cnt++) { bool visible = FALSE; bool obvious = FALSE; int power = 0; int damage = 0; cptr act = NULL; /* Extract the attack infomation */ int effect = r_ptr->blow[ap_cnt].effect; int method = r_ptr->blow[ap_cnt].method; int d_dice = r_ptr->blow[ap_cnt].d_dice; int d_side = r_ptr->blow[ap_cnt].d_side; /* Hack -- no more attacks */ if (!method) break; /* Handle "leaving" */ if (p_ptr->leaving) break; /* Extract visibility (before blink) */ if (m_ptr->ml) visible = TRUE; /* Skip 'tricky' attacks */ if (method > RBM_MAX_NORMAL) continue; /* Assume no cut or stun or touched */ do_cut = do_stun = touched = 0; /* Extract the attack "power". Elemental attacks upgraded. */ switch (effect) { case GF_HURT: power = 60; break; case GF_WOUND: power = 60; break; case GF_BATTER: power = 60; break; case GF_SHATTER: power = 60; break; case GF_UN_BONUS: power = 20; break; case GF_UN_POWER: power = 15; break; case GF_LOSE_MANA: power = 45; break; case GF_EAT_GOLD: power = 5; break; case GF_EAT_ITEM: power = 5; break; case GF_EAT_FOOD: power = 45; break; case GF_EAT_LITE: power = 45; break; case GF_HUNGER: power = 45; break; case GF_POIS: power = 25; break; case GF_ACID: power = 50; break; case GF_ELEC: power = 50; break; case GF_FIRE: power = 50; break; case GF_COLD: power = 50; break; case GF_BLIND: power = 5; break; case GF_CONFUSION: power = 10; break; case GF_TERRIFY: power = 10; break; case GF_PARALYZE: power = 5; break; case GF_HALLU: power = 10; break; case GF_DISEASE: power = 10; break; case GF_LOSE_STR: power = 0; break; case GF_LOSE_DEX: power = 0; break; case GF_LOSE_CON: power = 0; break; case GF_LOSE_INT: power = 0; break; case GF_LOSE_WIS: power = 0; break; case GF_LOSE_CHR: power = 0; break; case GF_LOSE_ALL: power = 2; break; case GF_EXP_10: power = 5; break; case GF_EXP_20: power = 5; break; case GF_EXP_40: power = 5; break; case GF_EXP_80: power = 5; break; /* Need to add extra flavours in here */ } /* Roll out the damage */ damage = damroll(d_dice, d_side); /* Describe the attack method */ switch (method) { case RBM_HIT: { /* Handle special effect types */ if (effect == GF_WOUND) { if (damage >= 30) act = "gouges you"; else if (damage >= 20) act = "slashes you"; else if (damage >= 5) act = "cuts you"; else act = "scratches you"; /* Usually don't stun */ if (!rand_int(5)) do_stun = 1; do_cut = touched = 1; } else if (effect == GF_BATTER) { if (damage >= 30) act = "bludgeons you"; else if (damage >= 20) act = "batters you"; else if (damage >= 5) act = "bashes you"; else act = "hits you"; /* Usually don't cut */ if (!rand_int(5)) do_cut = 1; do_stun = touched = 1; } else { act = "hits you"; do_cut = do_stun = touched = 1; } break; } case RBM_TOUCH: { act = "touches you"; touched = 1; break; } case RBM_PUNCH: { act = "punches you"; do_stun = touched = 1; break; } case RBM_KICK: { act = "kicks you"; do_stun = touched = 1; break; } case RBM_CLAW: { if (damage >= 25) act = "slashes you"; else if (damage >= 5) act = "claws you"; else act = "scratches you"; do_cut = touched = 1; break; } case RBM_BITE: { if (damage >= 5) act = "bites you"; else act = "nips you"; do_cut = touched = 1; break; } case RBM_PECK: { act = "pecks you"; do_stun = touched = 1; break; } case RBM_STING: { act = "stings you"; touched = 1; break; } case RBM_VOMIT: { act = "vomits on you"; touched = 1; break; } case RBM_BUTT: { if (damage >= rand_range(10, 20)) act = "tramples you"; else act = "butts you"; do_stun = touched = 1; break; } case RBM_CRUSH: { if (damage >= 10) act = "crushes you"; else act = "squeezes you"; do_stun = touched = 1; break; } case RBM_ENGULF: { if (damage >= randint(50)) act = "envelops you"; else act = "engulfs you"; touched = 1; break; } case RBM_CRAWL: { act = "crawls on you"; touched = 1; break; } case RBM_DROOL: { act = "drools on you"; break; } case RBM_SLIME: { act = "slimes you!"; break; } case RBM_SPIT: { act = "spits on you"; break; } case RBM_GAZE: { if (damage >= rand_range(20, 30)) act = "glares at you terribly"; else if (damage >= rand_range(5, 30)) act = "gazes upon you"; else act = "gazes at you"; break; } case RBM_WAIL: { act = "wails horribly"; break; } case RBM_SPORE: { act = "releases a cloud of spores"; break; } case RBM_LASH: { act = "lashes you with a whip"; touched = 1; break; } case RBM_BEG: { act = "begs you for money"; break; } case RBM_INSULT: { act = desc_insult[rand_int(8)]; break; } case RBM_MOAN: { act = desc_moan[rand_int(4)]; break; } } /* Monster hits player */ if (!effect || check_hit(power, rlev, m_idx)) { /* Always disturbing */ disturb(1, 0); /* Hack -- Apply "protection from evil" */ if ((p_ptr->protevil > 0) && (r_ptr->flags3 & (RF3_EVIL)) && (p_ptr->lev >= rlev) && ((rand_int(100) + p_ptr->lev) > 50)) { /* Remember the Evil-ness */ if (m_ptr->ml) { l_ptr->flags3 |= (RF3_EVIL); } /* Message */ msg_format("%^s is repelled.", m_name); /* Hack -- Next attack */ continue; } /* Message */ if (act) { if (damage > p_ptr->chp / 3) msg_format("%^s %s!", m_name, act); else msg_format("%^s %s.", m_name, act); } /* Check for usage */ if (rand_int(100)<damage) { int slot; /* Pick a (possibly empty) inventory slot */ switch (randint(6)) { case 1: slot = INVEN_BODY; break; case 2: slot = INVEN_ARM; break; case 3: slot = INVEN_OUTER; break; case 4: slot = INVEN_HANDS; break; case 5: slot = INVEN_HEAD; break; case 6: slot = INVEN_FEET; break; } /* Object used? */ object_usage(INVEN_WIELD); } if (effect) { /* New result routine */ obvious = project_p(m_idx,0,p_ptr->py,p_ptr->px,damage,effect); } else { obvious = TRUE; } /* Hack -- only one of cut or stun */ if (do_cut && do_stun) { /* Cancel cut */ if (rand_int(100) < 50) { do_cut = 0; } /* Cancel stun */ else { do_stun = 0; } } /* Handle cut */ if (do_cut) { int k; /* Critical hit (zero if non-critical) */ tmp = monster_critical(d_dice, d_side, damage, effect); /* Roll for damage */ switch (tmp) { case 0: k = 0; break; case 1: k = randint(5); break; case 2: k = randint(5) + 5; break; case 3: k = randint(20) + 20; break; case 4: k = randint(50) + 50; break; case 5: k = randint(100) + 100; break; case 6: k = 300; break; default: k = 500; break; } /* Apply the cut */ if (k) (void)set_cut(p_ptr->cut + k); } /* Handle stun */ if (do_stun) { int k; /* Critical hit (zero if non-critical) */ tmp = monster_critical(d_dice, d_side, damage, effect); /* Roll for damage */ switch (tmp) { case 0: k = 0; break; case 1: k = randint(5); break; case 2: k = randint(8) + 8; break; case 3: k = randint(15) + 15; break; case 4: k = randint(25) + 25; break; case 5: k = randint(35) + 35; break; case 6: k = randint(45) + 45; break; default: k = 100; break; } /* Apply the stun */ if (k) (void)set_stun(p_ptr->stun + k); } } /* Monster missed player */ else if (touched) { /* Visible monsters */ if (m_ptr->ml) { /* Disturbing */ disturb(1, 0); /* Message */ msg_format("%^s misses you.", m_name); } } /* Analyze "visible" monsters only */ if (visible) { /* Count "obvious" attacks (and ones that cause damage) */ if (obvious || damage || (l_ptr->blows[ap_cnt] > 10)) { /* Count attacks of this type */ if (l_ptr->blows[ap_cnt] < MAX_UCHAR) { l_ptr->blows[ap_cnt]++; } } } } /* Blink away */ if (blinked) { msg_print("There is a puff of smoke!"); teleport_away(m_idx, MAX_SIGHT * 2 + 5); } /* Always notice cause of death */ if (p_ptr->is_dead && (l_ptr->deaths < MAX_SHORT)) { l_ptr->deaths++; } /* Assume we attacked */ return (TRUE); }