/** * Helper function for display monlist. Prints the number of creatures, * followed by either a singular or plural version of the race name as * appropriate. */ void get_mon_name(char *output_name, size_t max, const monster_race *r_ptr, int num) { assert(r_ptr); /* Unique names don't have a number */ if (rf_has(r_ptr->flags, RF_UNIQUE)) { my_strcpy(output_name, "[U] ", max); my_strcat(output_name, r_ptr->name, max); return; } my_strcpy(output_name, format("%3d ", num), max); if (num == 1) { my_strcat(output_name, r_ptr->name, max); return; } if (r_ptr->plural != NULL) { my_strcat(output_name, r_ptr->plural, max); } else { char race_name[80]; my_strcpy(race_name, r_ptr->name, sizeof(race_name)); plural_aux(race_name, sizeof(race_name)); my_strcat(output_name, race_name, max); } }
/** * Show and delete the stacked monster messages. * Some messages are delayed so that they show up after everything else. * This is to avoid things like "The snaga dies. The snaga runs in fear!" * So we only flush messages matching the delay parameter. */ static void flush_monster_messages(bool delay) { int i, r_idx, count; const monster_race *r_ptr; char buf[512]; char *action; bool action_only; /* Show every message */ for (i = 0; i < size_mon_msg; i++) { if (mon_msg[i].delay != delay) continue; /* Cache the monster count */ count = mon_msg[i].mon_count; /* Paranoia */ if (count < 1) continue; /* Start with an empty string */ buf[0] = '\0'; /* Cache the race index */ r_idx = mon_msg[i].mon_race; /* Get the proper message action */ action = get_mon_msg_action(mon_msg[i].msg_code, (count > 1), &r_info[r_idx]); /* Is it a regular race? */ if (r_idx > 0) { /* Get the race */ r_ptr = &r_info[r_idx]; } /* It's the special mark for non-visible monsters */ else { /* No race */ r_ptr = NULL; } /* Monster is marked as invisible */ if(mon_msg[i].mon_flags & 0x04) r_ptr = NULL; /* Special message? */ action_only = (*action == '~'); /* Format the proper message for visible monsters */ if (r_ptr && !action_only) { char race_name[80]; /* Get the race name */ my_strcpy(race_name, r_ptr->name, sizeof(race_name)); /* Uniques */ if (rf_has(r_ptr->flags, RF_UNIQUE)) { /* Just copy the race name */ my_strcpy(buf, (r_ptr->name), sizeof(buf)); } /* We have more than one monster */ else if (count > 1) { /* Get the plural of the race name */ plural_aux(race_name, sizeof(race_name)); /* Put the count and the race name together */ strnfmt(buf, sizeof(buf), "%d %s", count, race_name); } /* Normal lonely monsters */ else { /* Just add a slight flavor */ strnfmt(buf, sizeof(buf), "the %s", race_name); } } /* Format the message for non-viewable monsters if necessary */ else if (!r_ptr && !action_only) { if (count > 1) { /* Show the counter */ strnfmt(buf, sizeof(buf), "%d monsters", count); } else { /* Just one non-visible monster */ my_strcpy(buf, "it", sizeof(buf)); } } /* Special message. Nuke the mark */ if (action_only) ++action; /* Regular message */ else { /* Add special mark. Monster is offscreen */ if (mon_msg[i].mon_flags & 0x02) my_strcat(buf, " (offscreen)", sizeof(buf)); /* Add the separator */ my_strcat(buf, " ", sizeof(buf)); } /* Append the action to the message */ my_strcat(buf, action, sizeof(buf)); /* Capitalize the message */ *buf = toupper((unsigned char)*buf); /* Hack - play sound for fear message */ if (mon_msg[i].msg_code == MON_MSG_FLEE_IN_TERROR) sound(MSG_FLEE); /* Show the message */ msg(buf); } }
/** * Show and delete the stacked monster messages. * Some messages are delayed so that they show up after everything else. * This is to avoid things like "The snaga dies. The snaga runs in fear!" * So we only flush messages matching the delay parameter. */ static void flush_monster_messages(bool delay, byte delay_tag) { const monster_race *r_ptr; int i, count; char buf[512]; char *action; bool action_only; /* Show every message */ for (i = 0; i < size_mon_msg; i++) { int type = MSG_GENERIC; if (mon_msg[i].delay != delay) continue; /* Skip if we are delaying and the tags don't match */ if (mon_msg[i].delay && mon_msg[i].delay_tag != delay_tag) continue; /* Cache the monster count */ count = mon_msg[i].mon_count; /* Paranoia */ if (count < 1) continue; /* Start with an empty string */ buf[0] = '\0'; /* Cache the race index */ r_ptr = mon_msg[i].race; /* Get the proper message action */ action = get_mon_msg_action(mon_msg[i].msg_code, (count > 1), r_ptr); /* Monster is marked as invisible */ if (mon_msg[i].mon_flags & MON_MSG_FLAG_INVISIBLE) r_ptr = NULL; /* Special message? */ action_only = (*action == '~'); /* Format the proper message depending on type, number and visibility */ if (r_ptr && !action_only) { char race_name[80]; /* Get the race name */ my_strcpy(race_name, r_ptr->name, sizeof(race_name)); /* Uniques, multiple monsters, or just one */ if (rf_has(r_ptr->flags, RF_UNIQUE)) { /* Just copy the race name */ my_strcpy(buf, (r_ptr->name), sizeof(buf)); } else if (count > 1) { /* Get the plural of the race name */ if (r_ptr->plural != NULL) { my_strcpy(race_name, r_ptr->plural, sizeof(race_name)); } else { plural_aux(race_name, sizeof(race_name)); } /* Put the count and the race name together */ strnfmt(buf, sizeof(buf), "%d %s", count, race_name); } else { /* Just add a slight flavor */ strnfmt(buf, sizeof(buf), "the %s", race_name); } } else if (!r_ptr && !action_only) { if (count > 1) { /* Show the counter */ strnfmt(buf, sizeof(buf), "%d monsters", count); } else { /* Just one non-visible monster */ my_strcpy(buf, "it", sizeof(buf)); } } /* Special message. Nuke the mark */ if (action_only) ++action; /* Regular message */ else { /* Add special mark. Monster is offscreen */ if (mon_msg[i].mon_flags & MON_MSG_FLAG_OFFSCREEN) my_strcat(buf, " (offscreen)", sizeof(buf)); /* Add the separator */ my_strcat(buf, " ", sizeof(buf)); } /* Append the action to the message */ my_strcat(buf, action, sizeof(buf)); /* Capitalize the message */ *buf = toupper((unsigned char)*buf); switch (mon_msg[i].msg_code) { case MON_MSG_FLEE_IN_TERROR: type = MSG_FLEE; break; case MON_MSG_MORIA_DEATH: case MON_MSG_DESTROYED: case MON_MSG_DIE: case MON_MSG_SHRIVEL_LIGHT: case MON_MSG_DISENTEGRATES: case MON_MSG_FREEZE_SHATTER: case MON_MSG_DISSOLVE: { /* Assume normal death sound */ type = MSG_KILL; /* Play a special sound if the monster was unique */ if (r_ptr != NULL && rf_has(r_ptr->flags, RF_UNIQUE)) { if (r_ptr->base == lookup_monster_base("Morgoth")) type = MSG_KILL_KING; else type = MSG_KILL_UNIQUE; } break; } } /* Show the message */ msgt(type, "%s", buf); } }
/* * 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 or quest monsters. * * 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 who) { int i, j, y, x; int dump_item = 0; int dump_gold = 0; int number_drops = 0; int total = 0; bool questlevel = FALSE; bool completed = FALSE; bool fixedquest = FALSE; bool writenote = TRUE; bool need_stairs = FALSE; s16b set_object_level; s16b this_o_idx, next_o_idx = 0; monster_type *m_ptr = &mon_list[m_idx]; monster_race *r_ptr = &r_info[m_ptr->r_idx]; bool visible = (m_ptr->ml || (r_ptr->flags1 & (RF1_UNIQUE))); bool chest = (r_ptr->flags1 & (RF1_DROP_CHEST)) ? TRUE : FALSE; 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 *i_ptr; object_type object_type_body; /* 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; /* Get the object */ o_ptr = &o_list[this_o_idx]; /*Remove the mark to hide when monsters carry this object*/ o_ptr->ident &= ~(IDENT_HIDE_CARRY); /* Get the next object */ next_o_idx = o_ptr->next_o_idx; /* Paranoia */ o_ptr->held_m_idx = 0; /* Get local object */ i_ptr = &object_type_body; /* Copy the object */ object_copy(i_ptr, o_ptr); /* Delete the object */ delete_object_idx(this_o_idx); /* Drop it */ drop_near(i_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 */ i_ptr = &object_type_body; /* Mega-Hack -- Prepare to make "Grond" */ object_prep(i_ptr, lookup_kind(TV_HAFTED, SV_GROND)); /* Mega-Hack -- Mark this item as "Grond" */ i_ptr->art_num = ART_GROND; /* Mega-Hack -- Actually create "Grond" */ apply_magic(i_ptr, -1, TRUE, TRUE, TRUE, FALSE); /* Remember history */ object_history(i_ptr, ORIGIN_MORGOTH, 0); /* Drop it in the dungeon */ drop_near(i_ptr, -1, y, x); /* Get local object */ i_ptr = &object_type_body; /* Mega-Hack -- Prepare to make "Morgoth's crown" */ object_prep(i_ptr, lookup_kind(TV_CROWN, SV_MORGOTH)); /* Mega-Hack -- Mark this item as "Morgoth" */ i_ptr->art_num = ART_MORGOTH; /* Mega-Hack -- Actually create "Morgoth" */ apply_magic(i_ptr, -1, TRUE, TRUE, TRUE, FALSE); /* Remember history */ object_history(i_ptr, ORIGIN_MORGOTH, 0); /* Drop it in the dungeon */ drop_near(i_ptr, -1, y, x); } /* Determine how much we can drop */ if ((r_ptr->flags1 & (RF1_DROP_60)) && (rand_int(100) < 60)) number_drops++; if ((r_ptr->flags1 & (RF1_DROP_90)) && (rand_int(100) < 90)) number_drops++; if (r_ptr->flags1 & (RF1_DROP_1D2)) number_drops += damroll(1, 2); if (r_ptr->flags1 & (RF1_DROP_2D2)) number_drops += damroll(2, 2); if (r_ptr->flags1 & (RF1_DROP_3D2)) number_drops += damroll(3, 2); if (r_ptr->flags1 & (RF1_DROP_4D2)) number_drops += damroll(4, 2); /* Hack -- handle creeping coins */ coin_type = force_coin; /* Average dungeon and monster levels */ set_object_level = object_level = (effective_depth(p_ptr->depth) + r_ptr->level) / 2; /* Drop some objects */ for (j = 0; j < number_drops; j++) { bool interesting = FALSE; /* Re-set the object level */ object_level = set_object_level; /* Get local object */ i_ptr = &object_type_body; /* Wipe the object */ object_wipe(i_ptr); /* work on the "too much junk" problem, large drops sometimes are less items with a "boost". */ if ((randint(750) < (number_drops * number_drops)) && (!(r_ptr->flags1 & (RF1_UNIQUE)))) { interesting = TRUE; number_drops -= 5; object_level += 5; /*Boundry Control*/ if (number_drops < 0) number_drops = 0; if (object_level > MAX_DEPTH) object_level = MAX_DEPTH; } /* Make Gold */ if (do_gold && (!chest) && (!do_item || (rand_int(100) < 70))) { /* Make some gold */ if (!make_gold(i_ptr)) continue; /* Assume seen XXX XXX XXX */ dump_gold++; } /* Make Object */ else { if (chest) { if (!make_object(i_ptr, good, great, DROP_TYPE_CHEST, FALSE)) continue; } /* Make an object */ else if (!make_object(i_ptr, good, great, DROP_TYPE_UNTHEMED, interesting)) continue; /* Remember history */ if (visible) object_history(i_ptr, ORIGIN_DROP_KNOWN, m_ptr->r_idx); else object_history(i_ptr, ORIGIN_DROP_UNKNOWN, 0); /* Assume seen XXX XXX XXX */ dump_item++; } /* Drop it in the dungeon */ drop_near(i_ptr, -1, y, x); } /* Re-set the object level */ object_level = set_object_level; /*If marked for a bonus item, create it and drop it */ if (m_ptr->mflag & (MFLAG_BONUS_ITEM)) { bool this_good = good; bool this_great = great; bool this_chest = chest; bool interesting = FALSE; char o_name[80]; /* Get local object */ i_ptr = &object_type_body; /* Wipe the object */ object_wipe(i_ptr); if (one_in_(50)) this_chest = TRUE; if (one_in_(15)) this_great = TRUE; if (one_in_(5)) this_good = TRUE; if ((!this_good) && (!this_great) && (!this_chest)) { object_level += 5; if (object_level > MAX_DEPTH) object_level = MAX_DEPTH; interesting = TRUE; } if (this_chest) { while (!make_object(i_ptr, TRUE, TRUE, DROP_TYPE_CHEST, FALSE)) continue; } /* Make an object */ else while (!make_object(i_ptr, this_good, this_good, DROP_TYPE_UNTHEMED, interesting)) continue; /* Remember history */ if (visible) object_history(i_ptr, ORIGIN_DROP_KNOWN, m_ptr->r_idx); else object_history(i_ptr, ORIGIN_DROP_UNKNOWN, 0); object_desc(o_name, sizeof(o_name), i_ptr, ODESC_PREFIX | ODESC_FULL); /* Drop it in the dungeon */ drop_near(i_ptr, -1, y, x); } /* Reset the object level */ object_level = effective_depth(p_ptr->depth); /* 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); } /* Update monster list window */ p_ptr->redraw |= (PR_MONLIST); /* Count incomplete quests */ for (i = 0; i < z_info->q_max; i++) { quest_type *q_ptr = &q_info[i]; /* * Hack - don't count if player didn't kill, or on a town level * This assumes only a player can kill quest monsters!!!!! * This line is also ugly coding. :) */ if (((who != SOURCE_PLAYER) && (who != SOURCE_TRAP)) || (!p_ptr->depth)) continue; /* Quest level? */ if ((q_ptr->active_level == p_ptr->depth) && (p_ptr->depth > 0)) { /* One on the level */ questlevel = TRUE; /* Require "Quest Monsters" */ if (q_ptr->mon_idx == m_ptr->r_idx) { char race_name[80]; /* Get the monster race name (singular)*/ monster_desc_race(race_name, sizeof(race_name), q_ptr->mon_idx); /* Mark kills */ q_ptr->cur_num++; /* Redraw quest indicator */ p_ptr->redraw |= (PR_QUEST_ST); /* Completed quest? */ if (q_ptr->cur_num == q_ptr->max_num) { /* Mark complete */ q_ptr->active_level = 0; /* Mark fixed quests */ if ((q_ptr->q_type == QUEST_FIXED) || (q_ptr->q_type == QUEST_FIXED_U)) fixedquest = TRUE; if (q_ptr->q_type == QUEST_GUARDIAN) need_stairs = TRUE; /* One complete */ completed = TRUE; /*make a note of the completed quest, but not for fixed or * fixed unique quests */ if ((adult_take_notes) && (!fixedquest)) { char note[120]; /* Multiple quest monsters */ if (q_ptr->max_num > 1) { plural_aux(race_name, sizeof(race_name)); } if (r_ptr->flags1 & (RF1_UNIQUE)) { /*write note*/ if monster_nonliving(r_ptr) sprintf(note, "Quest: Destroyed %s", race_name); else sprintf(note, "Quest: Killed %s", race_name); } else { /* Write note */ if monster_nonliving(r_ptr)