/** * Apply blow side effects */ static void blow_side_effects(struct player *p, struct monster *mon) { /* Confusion attack */ if (p->confusing) { p->confusing = FALSE; msg("Your hands stop glowing."); mon_inc_timed(mon, MON_TMD_CONF, (10 + randint0(p->lev) / 10), MON_TMD_FLG_NOTIFY, FALSE); } }
/** * Attack the monster at the given location with a single blow. */ static bool py_attack_real(int y, int x, bool *fear) { /* Information about the target of the attack */ monster_type *m_ptr = cave_monster(cave, cave->m_idx[y][x]); monster_race *r_ptr = &r_info[m_ptr->r_idx]; char m_name[80]; bool stop = FALSE; /* The weapon used */ object_type *o_ptr = &p_ptr->inventory[INVEN_WIELD]; /* Information about the attack */ int bonus = p_ptr->state.to_h + o_ptr->to_h; int chance = p_ptr->state.skills[SKILL_TO_HIT_MELEE] + bonus * BTH_PLUS_ADJ; bool do_quake = FALSE; bool success = FALSE; /* Default to punching for one damage */ const char *hit_verb = "punch"; int dmg = 1; u32b msg_type = MSG_HIT; /* Extract monster name (or "it") */ monster_desc(m_name, sizeof(m_name), m_ptr, 0); /* Auto-Recall if possible and visible */ if (m_ptr->ml) monster_race_track(m_ptr->r_idx); /* Track a new monster */ if (m_ptr->ml) health_track(p_ptr, cave->m_idx[y][x]); /* Handle player fear (only for invisible monsters) */ if (check_state(p_ptr, OF_AFRAID, p_ptr->state.flags)) { msgt(MSG_AFRAID, "You are too afraid to attack %s!", m_name); return FALSE; } /* Disturb the monster */ mon_clear_timed(cave->m_idx[y][x], MON_TMD_SLEEP, MON_TMD_FLG_NOMESSAGE); /* See if the player hit */ success = test_hit(chance, r_ptr->ac, m_ptr->ml); /* If a miss, skip this hit */ if (!success) { msgt(MSG_MISS, "You miss %s.", m_name); return FALSE; } /* Handle normal weapon */ if (o_ptr->kind) { int i; const struct slay *best_s_ptr = NULL; hit_verb = "hit"; /* Get the best attack from all slays or * brands on all non-launcher equipment */ for (i = INVEN_LEFT; i < INVEN_TOTAL; i++) { struct object *obj = &p_ptr->inventory[i]; if (obj->kind) improve_attack_modifier(obj, m_ptr, &best_s_ptr, TRUE, FALSE); } improve_attack_modifier(o_ptr, m_ptr, &best_s_ptr, TRUE, FALSE); if (best_s_ptr != NULL) hit_verb = best_s_ptr->melee_verb; dmg = damroll(o_ptr->dd, o_ptr->ds); dmg *= (best_s_ptr == NULL) ? 1 : best_s_ptr->mult; dmg += o_ptr->to_d; dmg = critical_norm(o_ptr->weight, o_ptr->to_h, dmg, &msg_type); /* Learn by use for the weapon */ object_notice_attack_plusses(o_ptr); if (check_state(p_ptr, OF_IMPACT, p_ptr->state.flags) && dmg > 50) { do_quake = TRUE; wieldeds_notice_flag(p_ptr, OF_IMPACT); } } /* Learn by use for other equipped items */ wieldeds_notice_on_attack(); /* Apply the player damage bonuses */ dmg += p_ptr->state.to_d; /* No negative damage */ if (dmg <= 0) dmg = 0; /* Tell the player what happened */ if (dmg <= 0) msgt(MSG_MISS, "You fail to harm %s.", m_name); else if (msg_type == MSG_HIT) msgt(MSG_HIT, "You %s %s.", hit_verb, m_name); else if (msg_type == MSG_HIT_GOOD) msgt(MSG_HIT_GOOD, "You %s %s. %s", hit_verb, m_name, "It was a good hit!"); else if (msg_type == MSG_HIT_GREAT) msgt(MSG_HIT_GREAT, "You %s %s. %s", hit_verb, m_name, "It was a great hit!"); else if (msg_type == MSG_HIT_SUPERB) msgt(MSG_HIT_SUPERB, "You %s %s. %s", hit_verb, m_name, "It was a superb hit!"); else if (msg_type == MSG_HIT_HI_GREAT) msgt(MSG_HIT_HI_GREAT, "You %s %s. %s", hit_verb, m_name, "It was a *GREAT* hit!"); else if (msg_type == MSG_HIT_HI_SUPERB) msgt(MSG_HIT_HI_SUPERB, "You %s %s. %s", hit_verb, m_name, "It was a *SUPERB* hit!"); /* Complex message */ if (p_ptr->wizard) msg("You do %d (out of %d) damage.", dmg, m_ptr->hp); /* Confusion attack */ if (p_ptr->confusing) { p_ptr->confusing = FALSE; msg("Your hands stop glowing."); mon_inc_timed(cave->m_idx[y][x], MON_TMD_CONF, (10 + randint0(p_ptr->lev) / 10), MON_TMD_FLG_NOTIFY); } /* Damage, check for fear and death */ stop = mon_take_hit(cave->m_idx[y][x], dmg, fear, NULL); if (stop) (*fear) = FALSE; /* Apply earthquake brand */ if (do_quake) { earthquake(p_ptr->py, p_ptr->px, 10); if (cave->m_idx[y][x] == 0) stop = TRUE; } return stop; }
/** * Places a monster (of the specified "type") near the given * location. Return the siummoned monster's level iff a monster was * actually summoned. * * We will attempt to place the monster up to 10 times before giving up. * * This function takes the "monster level" * of the summoning monster as a parameter, and use that, along with * the current dungeon level, to help determine the level of the * desired monster. Note that this is an upper bound, and also * tends to "prefer" monsters of that level. Currently, we use * the average of the dungeon and monster levels, and then add * five to allow slight increases in monster power. * * Note that we use the new "monster allocation table" creation code * to restrict the "get_mon_num()" function to the set of "legal" * monsters, making this function much faster and more reliable. * * Note that this function may not succeed, though this is very rare. */ int summon_specific(int y1, int x1, int lev, int type, bool delay, bool call) { int i, x = 0, y = 0; struct monster *mon; struct monster_race *race; /* Look for a location, allow up to 4 squares away */ for (i = 0; i < 60; ++i) { /* Pick a distance */ int d = (i / 15) + 1; /* Pick a location */ scatter(cave, &y, &x, y1, x1, d, true); /* Require "empty" floor grid */ if (!square_isempty(cave, y, x)) continue; /* No summon on glyphs */ if (square_iswarded(cave, y, x) || square_isdecoyed(cave, y, x)) { continue; } /* Okay */ break; } /* Failure */ if (i == 60) return (0); /* Save the "summon" type */ summon_specific_type = type; /* Use the new calling scheme if requested */ if (call && (type != summon_name_to_idx("UNIQUE")) && (type != summon_name_to_idx("WRAITH"))) { return (call_monster(y, x)); } /* Prepare allocation table */ get_mon_num_prep(summon_specific_okay); /* Pick a monster, using the level calculation */ race = get_mon_num((player->depth + lev) / 2 + 5); /* Prepare allocation table */ get_mon_num_prep(NULL); /* Handle failure */ if (!race) return (0); /* Attempt to place the monster (awake, don't allow groups) */ if (!place_new_monster(cave, y, x, race, false, false, ORIGIN_DROP_SUMMON)) return (0); /* Success, return the level of the monster */ mon = square_monster(cave, y, x); /* If delay, try to let the player act before the summoned monsters, * including slowing down faster monsters for one turn */ /* XXX should this now be hold monster for a turn? */ if (delay) { mon->energy = 0; if (mon->race->speed > player->state.speed) mon_inc_timed(mon, MON_TMD_SLOW, 1, MON_TMD_FLG_NOMESSAGE, false); } return (mon->race->level); }
/** * Decreases a monster's hit points by `dam` and handle monster death. * * Hack -- we "delay" fear messages by passing around a "fear" flag. * * We announce monster death (using an optional "death message" (`note`) * if given, and a otherwise a generic killed/destroyed message). * * Returns TRUE if the monster has been killed (and deleted). * * TODO: 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. XXX XXX XXX **/ bool mon_take_hit(struct monster *m_ptr, int dam, bool *fear, const char *note) { s32b div, new_exp, new_exp_frac; monster_lore *l_ptr = get_lore(m_ptr->race); /* Redraw (later) if needed */ if (p_ptr->health_who == m_ptr) p_ptr->redraw |= (PR_HEALTH); /* Wake it up */ mon_clear_timed(m_ptr, MON_TMD_SLEEP, MON_TMD_FLG_NOMESSAGE, FALSE); /* Become aware of its presence */ if (m_ptr->unaware) become_aware(m_ptr); /* Hurt it */ m_ptr->hp -= dam; /* It is dead now */ if (m_ptr->hp < 0) { char m_name[80]; char buf[80]; /* Assume normal death sound */ int soundfx = MSG_KILL; /* Play a special sound if the monster was unique */ if (rf_has(m_ptr->race->flags, RF_UNIQUE)) { if (m_ptr->race->base == lookup_monster_base("Morgoth")) soundfx = MSG_KILL_KING; else soundfx = MSG_KILL_UNIQUE; } /* Extract monster name */ monster_desc(m_name, sizeof(m_name), m_ptr, 0); /* Death by Missile/Spell attack */ if (note) { /* Hack -- allow message suppression */ if (strlen(note) <= 1) { /* Be silent */ } else { char *str = format("%s%s", m_name, note); my_strcap(str); msgt(soundfx, "%s", str); } } /* Death by physical attack -- invisible monster */ else if (!m_ptr->ml) msgt(soundfx, "You have killed %s.", m_name); /* Death by Physical attack -- non-living monster */ else if (monster_is_unusual(m_ptr->race)) msgt(soundfx, "You have destroyed %s.", m_name); /* Death by Physical attack -- living monster */ else msgt(soundfx, "You have slain %s.", m_name); /* Player level */ div = p_ptr->lev; /* Give some experience for the kill */ new_exp = ((long)m_ptr->race->mexp * m_ptr->race->level) / div; /* Handle fractional experience */ new_exp_frac = ((((long)m_ptr->race->mexp * m_ptr->race->level) % div) * 0x10000L / div) + p_ptr->exp_frac; /* Keep track of experience */ if (new_exp_frac >= 0x10000L) { new_exp++; p_ptr->exp_frac = (u16b)(new_exp_frac - 0x10000L); } else p_ptr->exp_frac = (u16b)new_exp_frac; /* When the player kills a Unique, it stays dead */ if (rf_has(m_ptr->race->flags, RF_UNIQUE)) { char unique_name[80]; m_ptr->race->max_num = 0; /* * This gets the correct name if we slay an invisible * unique and don't have See Invisible. */ monster_desc(unique_name, sizeof(unique_name), m_ptr, MDESC_SHOW | MDESC_IND2); /* Log the slaying of a unique */ strnfmt(buf, sizeof(buf), "Killed %s", unique_name); history_add(buf, HISTORY_SLAY_UNIQUE, 0); } /* Gain experience */ player_exp_gain(p_ptr, new_exp); /* Generate treasure */ monster_death(m_ptr, FALSE); /* Recall even invisible uniques or winners */ if (m_ptr->ml || rf_has(m_ptr->race->flags, RF_UNIQUE)) { /* Count kills this life */ if (l_ptr->pkills < MAX_SHORT) l_ptr->pkills++; /* Count kills in all lives */ if (l_ptr->tkills < MAX_SHORT) l_ptr->tkills++; /* Hack -- Auto-recall */ monster_race_track(m_ptr->race); } /* Delete the monster */ delete_monster_idx(m_ptr->midx); /* Not afraid */ (*fear) = FALSE; /* Monster is dead */ return (TRUE); } /* Mega-Hack -- Pain cancels fear */ if (!(*fear) && m_ptr->m_timed[MON_TMD_FEAR] && (dam > 0)) { int tmp = randint1(dam); /* Cure a little fear */ if (tmp < m_ptr->m_timed[MON_TMD_FEAR]) { /* Reduce fear */ mon_dec_timed(m_ptr, MON_TMD_FEAR, tmp, MON_TMD_FLG_NOMESSAGE, FALSE); } /* Cure all the fear */ else { /* Cure fear */ mon_clear_timed(m_ptr, MON_TMD_FEAR, MON_TMD_FLG_NOMESSAGE, FALSE); /* No more fear */ (*fear) = FALSE; } } /* Sometimes a monster gets scared by damage */ if (!m_ptr->m_timed[MON_TMD_FEAR] && !rf_has(m_ptr->race->flags, RF_NO_FEAR) && dam > 0) { 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 ((randint1(10) >= percentage) || ((dam >= m_ptr->hp) && (randint0(100) < 80))) { int timer = randint1(10) + (((dam >= m_ptr->hp) && (percentage > 7)) ? 20 : ((11 - percentage) * 5)); /* Hack -- note fear */ (*fear) = TRUE; mon_inc_timed(m_ptr, MON_TMD_FEAR, timer, MON_TMD_FLG_NOMESSAGE | MON_TMD_FLG_NOFAIL, FALSE); } } /* Not dead yet */ return (FALSE); }