/** * Remove slays which are duplicates, i.e. they have exactly the same "monster * flag" and the same "resist flag". The one with highest multiplier is kept. * * \param flags is the flagset from which to remove duplicates. * count is the number of dups removed. */ public static int dedup_slays(ref Bitflag flags) { int i, j; int count = 0; for (i = 0; i < list.Count(); i++) { Slay s_ptr = list[i]; if (flags.has(s_ptr.object_flag.value)) { for (j = i + 1; j < list.Count(); j++) { Slay t_ptr = list[j]; if (flags.has(t_ptr.object_flag.value) && (t_ptr.monster_flag == s_ptr.monster_flag) && (t_ptr.resist_flag == s_ptr.resist_flag) && (t_ptr.mult != s_ptr.mult)) { count++; if (t_ptr.mult > s_ptr.mult) { flags.off(s_ptr.object_flag.value); } else { flags.off(t_ptr.object_flag.value); } } } } } return(count); }
/** * Extract the multiplier from a given object hitting a given monster. * * \param o_ptr is the object being used to attack * \param m_ptr is the monster being attacked * \param best_s_ptr is the best applicable slay_table entry, or null if no * slay already known * \param real is whether this is a real attack (where we update lore) or a * simulation (where we don't) * \param known_only is whether we are using all the object flags, or only * the ones we *already* know about */ //Best_s_ptr was slay** public static void improve_attack_modifier(Object o_ptr, Monster.Monster m_ptr, ref Slay best_s_ptr, bool real, bool known_only) { Monster_Race r_ptr = Misc.r_info[m_ptr.r_idx]; Monster_Lore l_ptr = Misc.l_list[m_ptr.r_idx]; Bitflag f = new Bitflag(Object_Flag.SIZE); Bitflag known_f = new Bitflag(Object_Flag.SIZE); Bitflag note_f = new Bitflag(Object_Flag.SIZE); int i; o_ptr.object_flags(ref f); o_ptr.object_flags_known(ref known_f); for (i = 0; i < Slay.MAX.value; i++) { Slay s_ptr = list[i]; if ((known_only && !known_f.has(s_ptr.object_flag.value)) || (!known_only && !f.has(s_ptr.object_flag.value))) { continue; } /* In a real attack, learn about monster resistance or slay match if: * EITHER the slay flag on the object is known, * OR the monster is vulnerable to the slay/brand */ if (real && (known_f.has(s_ptr.object_flag.value) || (s_ptr.monster_flag != Monster_Flag.NONE && r_ptr.flags.has(s_ptr.monster_flag.value)) || (s_ptr.resist_flag != Monster_Flag.NONE && !r_ptr.flags.has(s_ptr.resist_flag.value)))) { /* notice any brand or slay that would affect monster */ note_f.wipe(); note_f.on(s_ptr.object_flag.value); object_notice_slays(o_ptr, note_f); if (m_ptr.ml && s_ptr.monster_flag != Monster_Flag.NONE) { l_ptr.flags.on(s_ptr.monster_flag.value); } if (m_ptr.ml && s_ptr.resist_flag != Monster_Flag.NONE) { l_ptr.flags.on(s_ptr.resist_flag.value); } } /* If the monster doesn't resist or the slay flag matches */ if ((s_ptr.brand != null && s_ptr.brand.Length != 0 && !r_ptr.flags.has(s_ptr.resist_flag.value)) || (s_ptr.monster_flag != Monster_Flag.NONE && r_ptr.flags.has(s_ptr.monster_flag.value))) { /* compare multipliers to determine best attack */ if ((best_s_ptr == null) || ((best_s_ptr).mult < s_ptr.mult)) { best_s_ptr = s_ptr; } } } }
/** * Match slays in flags against a chosen flag mask * * count is the number of matches * \param flags is the flagset to analyse for matches * \param mask is the flagset against which to test * \param desc[] is the array of descriptions of matching slays - can be null * \param brand[] is the array of descriptions of brands - can be null * \param mult[] is the array of multipliers of those slays - can be null * \param dedup is whether or not to remove duplicates * * desc[], brand[] and mult[] must be >= SL_MAX in size */ //Bitflags were size Object_Flag.SIZE, might be an out param public static int list_slays(Bitflag flags, Bitflag mask, string[] desc, string[] brand, int[] mult, bool dedup) { int i, count = 0; Bitflag f = new Bitflag(Object_Flag.SIZE); /* We are only interested in the flags specified in mask */ f.copy(flags); f.inter(mask); /* Remove "duplicate" flags if desired */ if (dedup) { dedup_slays(ref f); } /* Collect slays */ for (i = 0; i < list.Count(); i++) { Slay s_ptr = list[i]; if (f.has(s_ptr.object_flag.value)) { if (mult != null) { mult[count] = s_ptr.mult; } if (brand != null) { brand[count] = s_ptr.brand; } if (desc != null) { desc[count] = s_ptr.desc; } count++; } } return(count); }
/* * Evaluate the object's overall power level. */ public int object_power(bool verbose, StreamWriter log_file, bool known) { int p = 0, q = 0, slay_pwr = 0, dice_pwr = 0; int i, j; int extra_stat_bonus = 0, mult = 1, num_slays = 0, k = 1; Bitflag flags = new Bitflag(Object_Flag.SIZE); Bitflag mask = new Bitflag(Object_Flag.SIZE); /* Zero the flag counts */ for (i = 0; i < sets.Length; i++) { sets[i].count = 0; } /* Extract the flags */ if (known) { // log_file.Write("Object is deemed knwon\n"); object_flags(ref flags); } else { // log_file.Write("Object may not be fully known\n"); object_flags_known(ref flags); } /* Log the flags in human-readable form */ //if (verbose) //log_flags(flags, log_file); //meh /* Get the slay power and number of slay/brand types */ Object_Flag.create_mask(mask, false, Object_Flag.object_flag_type.SLAY, Object_Flag.object_flag_type.KILL, Object_Flag.object_flag_type.BRAND); num_slays = Slay.list_slays(flags, mask, null, null, null, true); if (num_slays != 0) { slay_pwr = slay_power(verbose, log_file, known); } /* Start with any damage boost from the item itself */ p += (to_d * DAMAGE_POWER / 2); //file_putf(log_file, "Adding power from to_dam, total is %d\n", p); /* Add damage from dice for any wieldable weapon or ammo */ if (wield_slot() == Misc.INVEN_WIELD || is_ammo()) { dice_pwr = (dd * (ds + 1) * DAMAGE_POWER / 4); // file_putf(log_file, "Adding %d power for dam dice\n", dice_pwr); /* Add 2nd lot of damage power for nonweapons */ } else if (wield_slot() != Misc.INVEN_BOW) { p += (to_d * DAMAGE_POWER); // file_putf(log_file, "Adding power from nonweap to_dam, total is %d\n", p); /* Add power boost for nonweapons with combat flags */ if (num_slays != 0 || flags.has(Object_Flag.BLOWS.value) || flags.has(Object_Flag.SHOTS.value) || flags.has(Object_Flag.MIGHT.value)) { dice_pwr = (WEAP_DAMAGE * DAMAGE_POWER); // file_putf(log_file, "Adding %d power for nonweap combat flags\n", dice_pwr); } } p += dice_pwr; /* Add ammo damage for launchers, get multiplier and rescale */ if (wield_slot() == Misc.INVEN_BOW) { p += (archery[sval / 10].ammo_dam * DAMAGE_POWER / 2); // file_putf(log_file, "Adding power from ammo, total is %d\n", p); mult = bow_multiplier(sval); // file_putf(log_file, "Base mult for this weapon is %d\n", mult); } /* Add launcher bonus for ego ammo, multiply for launcher and rescale */ if (is_ammo()) { if (ego != null) { p += (archery[tval - TVal.TV_SHOT].launch_dam * DAMAGE_POWER / 2); } p = p * archery[tval - TVal.TV_SHOT].launch_mult / (2 * MAX_BLOWS); // file_putf(log_file, "After multiplying ammo and rescaling, power is %d\n", p); } /* Add power for extra blows */ if (flags.has(Object_Flag.BLOWS.value)) { j = which_pval(Object_Flag.BLOWS.value); if (known || this_pval_is_visible(j)) { if (pval[j] >= INHIBIT_BLOWS) { p += INHIBIT_POWER; // file_putf(log_file, "INHIBITING - too many extra blows - quitting\n"); return(p); } else { p = p * (MAX_BLOWS + pval[j]) / MAX_BLOWS; /* Add boost for assumed off-weapon damage */ p += (NONWEAP_DAMAGE * pval[j] * DAMAGE_POWER / 2); // file_putf(log_file, "Adding power for extra blows, total is %d\n", p); } } } /* Add power for extra shots - note that we cannot handle negative shots */ if (flags.has(Object_Flag.SHOTS.value)) { j = which_pval(Object_Flag.SHOTS.value); if (known || this_pval_is_visible(j)) { if (pval[j] >= INHIBIT_SHOTS) { p += INHIBIT_POWER; // file_putf(log_file, "INHIBITING - too many extra shots - quitting\n"); return(p); } else if (pval[j] > 0) { p = (p * (1 + pval[j])); // file_putf(log_file, "Extra shots: multiplying power by 1 + %d, total is %d\n", o_ptr.pval[j], p); } } } /* Add power for extra might */ if (flags.has(Object_Flag.MIGHT.value)) { j = which_pval(Object_Flag.MIGHT.value); if (known || this_pval_is_visible(j)) { if (pval[j] >= INHIBIT_MIGHT) { p += INHIBIT_POWER; mult = 1; /* don't overflow */ // file_putf(log_file, "INHIBITING - too much extra might - quitting\n"); return(p); } else { mult += pval[j]; } // file_putf(log_file, "Mult after extra might is %d\n", mult); } } p *= mult; //file_putf(log_file, "After multiplying power for might, total is %d\n", p); /* Apply the correct slay multiplier */ if (slay_pwr != 0) { p += (dice_pwr * (slay_pwr / 100)) / (Eval.tot_mon_power / 100); // file_putf(log_file, "Adjusted for slay power, total is %d\n", p); } /* Melee weapons assume MAX_BLOWS per turn, so we must divide by MAX_BLOWS * to get equal ratings for launchers. */ if (wield_slot() == Misc.INVEN_BOW) { p /= MAX_BLOWS; // file_putf(log_file, "Rescaling bow power, total is %d\n", p); } /* Add power for +to_hit */ p += (to_h * TO_HIT_POWER / 2); //file_putf(log_file, "Adding power for to hit, total is %d\n", p); /* Add power for base AC and adjust for weight */ if (ac != 0) { p += BASE_ARMOUR_POWER; q += (ac * BASE_AC_POWER / 2); // file_putf(log_file, "Adding %d power for base AC value\n", q); /* Add power for AC per unit weight */ if (weight > 0) { i = 750 * (ac + to_a) / weight; /* Avoid overpricing Elven Cloaks */ if (i > 450) { i = 450; } q *= i; q /= 100; /* Weightless (ethereal) armour items get fixed boost */ } else { q *= 5; } p += q; // file_putf(log_file, "Adding power for AC per unit weight, now %d\n", p); } /* Add power for +to_ac */ p += (to_a * TO_AC_POWER / 2); //file_putf(log_file, "Adding power for to_ac of %d, total is %d\n", o_ptr.to_a, p); if (to_a > HIGH_TO_AC) { p += ((to_a - (HIGH_TO_AC - 1)) * TO_AC_POWER); // file_putf(log_file, "Adding power for high to_ac value, total is %d\n", p); } if (to_a > VERYHIGH_TO_AC) { p += ((to_a - (VERYHIGH_TO_AC - 1)) * TO_AC_POWER * 2); // file_putf(log_file, "Adding power for very high to_ac value, total is %d\n", p); } if (to_a >= INHIBIT_AC) { p += INHIBIT_POWER; // file_putf(log_file, "INHIBITING: AC bonus too high\n"); } /* Add power for light sources by radius XXX Hack - rewrite calc_torch! */ if (wield_slot() == Misc.INVEN_LIGHT) { p += BASE_LIGHT_POWER; /* Artifact lights have larger radius so add more */ if (artifact != null) { p += BASE_LIGHT_POWER; } // file_putf(log_file, "Adding power for light radius, total is %d\n", p); } /* Add base power for jewelry */ if (is_jewelry()) { p += BASE_JEWELRY_POWER; // file_putf(log_file, "Adding power for jewelry, total is %d\n", p); } /* Add power for non-derived flags (derived flags have flag_power 0) */ for (i = flags.next(Bitflag.FLAG_START); i != Bitflag.FLAG_null; i = flags.next(i + 1)) { if (Object_Flag.flag_uses_pval(i)) { j = which_pval(i); if (known || this_pval_is_visible(j)) { k = pval[j]; extra_stat_bonus += (k * Object_Flag.pval_mult(i)); } } else { k = 1; } if (Object_Flag.flag_power(i) != 0) { p += (k * Object_Flag.flag_power(i) * Object_Flag.slot_mult(i, wield_slot())); //file_putf(log_file, "Adding power for %s, total is %d\n", flag_name(i), p); } /* Track combinations of flag types - note we ignore SUST_CHR */ for (j = 0; j < sets.Length; j++) { if ((sets[j].type == Object_Flag.obj_flag_type(i)) && (i != Object_Flag.SUST_CHR.value)) { sets[j].count++; } } } /* Add extra power term if there are a lot of ability bonuses */ if (extra_stat_bonus > 249) { // file_putf(log_file, "Inhibiting! (Total ability bonus of %d is too high)\n", extra_stat_bonus); p += INHIBIT_POWER; } else { p += ability_power[extra_stat_bonus / 10]; // file_putf(log_file, "Adding power for pval total of %d, total is %d\n", extra_stat_bonus, p); } /* Add extra power for multiple flags of the same type */ for (i = 0; i < sets.Length; i++) { if (sets[i].count > 1) { q = (sets[i].factor * sets[i].count * sets[i].count); /* Scale damage-dependent set bonuses by damage dice power */ if (sets[i].dam_dep) { q = q * dice_pwr / (DAMAGE_POWER * 5); } p += q; // file_putf(log_file, "Adding power for multiple %s, total is %d\n", sets[i].desc, p); } /* Add bonus if item has a full set of these flags */ if (sets[i].count == sets[i].size) { p += sets[i].bonus; // file_putf(log_file, "Adding power for full set of %s, total is %d\n", sets[i].desc, p); } } /* add power for effect */ if (known || effect_is_known()) { if (artifact != null && artifact.effect != null) { p += artifact.effect.effect_power(); // file_putf(log_file, "Adding power for artifact activation, total is %d\n", p); } else { p += kind.effect.effect_power(); // file_putf(log_file, "Adding power for item activation, total is %d\n", p); } } //file_putf(log_file, "FINAL POWER IS %d\n", p); return(p); }
/** * Calculate the rating for a given slay combination */ int slay_power(bool verbose, StreamWriter log_file, bool known) { Bitflag s_index = new Bitflag(Object_Flag.SIZE); Bitflag f = new Bitflag(Object_Flag.SIZE); Bitflag f2 = new Bitflag(Object_Flag.SIZE); int sv = 0; //uint int i, j; int mult; Slay best_s_ptr = null; Monster_Race r_ptr; Monster.Monster m_ptr; //monster_type monster_type_body; string[] desc = new string[Slay.MAX.value]; // = { 0 }, * string[] brand = new string[Slay.MAX.value]; // = { 0 }; int[] s_mult = new int[Slay.MAX.value]; // = { 0 }; if (known) { object_flags(ref f); } else { object_flags_known(ref f); } /* Combine the slay bytes into an index value, return if there are none */ s_index.copy(f); Object_Flag.create_mask(f2, false, Object_Flag.object_flag_type.SLAY, Object_Flag.object_flag_type.KILL, Object_Flag.object_flag_type.BRAND); if (!s_index.is_inter(f2)) { return(Eval.tot_mon_power); } else { s_index.inter(f2); } /* Look in the cache to see if we know this one yet */ sv = Slay.check_slay_cache(s_index); /* If it's cached (or there are no slays), return the value */ if (sv != 0) { //file_putf(log_file, "Slay cache hit\n"); return(sv); } /* * Otherwise we need to calculate the expected average multiplier * for this combination (multiplied by the total number of * monsters, which we'll divide out later). */ for (i = 0; i < Misc.z_info.r_max; i++) { best_s_ptr = null; mult = 1; r_ptr = Misc.r_info[i]; if (r_ptr == null) { continue; } m_ptr = new Monster.Monster(); m_ptr.r_idx = (short)i; /* Find the best multiplier against this monster */ Slay.improve_attack_modifier(this, m_ptr, ref best_s_ptr, false, !known); if (best_s_ptr != null) { mult = best_s_ptr.mult; } /* Add the multiple to sv */ sv += (int)(mult * r_ptr.scaled_power); } /* * To get the expected damage for this weapon, multiply the * average damage from base dice by sv, and divide by the * total number of monsters. */ if (verbose) { /* Write info about the slay combination and multiplier */ //file_putf(log_file, "Slay multiplier for: "); j = Slay.list_slays(s_index, s_index, desc, brand, s_mult, false); //for (i = 0; i < j; i++) { // if (brand[i]) { // file_putf(log_file, brand[i]); // } else { // file_putf(log_file, desc[i]); // } // file_putf(log_file, "x%d ", s_mult[i]); //} //file_putf(log_file, "\nsv is: %d\n", sv); //file_putf(log_file, " and t_m_p is: %d \n", tot_mon_power); //file_putf(log_file, "times 1000 is: %d\n", (1000 * sv) / tot_mon_power); } /* Add to the cache */ if (Slay.fill_slay_cache(s_index, sv)) { //file_putf(log_file, "Added to slay cache\n"); } return(sv); }
/* * Determine whether a weapon or missile weapon is obviously {excellent} when * worn. * * XXX Eddie should messages be adhoc all over the place? perhaps the main * loop should check for change in inventory/wieldeds and all messages be * printed from one place */ public void notice_on_wield() { Bitflag f = new Bitflag(Object_Flag.SIZE); Bitflag f2 = new Bitflag(Object_Flag.SIZE); Bitflag obvious_mask = new Bitflag(Object_Flag.SIZE); bool obvious = false; Object_Flag.create_mask(obvious_mask, true, Object_Flag.object_flag_id.WIELD); /* Save time of wield for later */ object_last_wield = Misc.turn; /* Only deal with un-ID'd items */ if (is_known()) { return; } /* Wear it */ flavor_tried(); if (add_ident_flags(IDENT_WORN)) { check_for_ident(); } /* CC: may wish to be more subtle about this once we have ego lights * with multiple pvals */ if (is_light() && ego != null) { notice_ego(); } if (flavor_is_aware() && easy_know()) { notice_everything(); return; } /* Automatically sense artifacts upon wield */ sense_artifact(); /* Note artifacts when found */ if (artifact != null) { History.add_artifact(artifact, is_known(), true); } /* special case FA, needed at least for mages wielding gloves */ if (FA_would_be_obvious()) { obvious_mask.on(Object_Flag.FREE_ACT.value); } /* Extract the flags */ object_flags(ref f); /* Find obvious things (disregarding curses) - why do we remove the curses?? */ Object_Flag.create_mask(f2, false, Object_Flag.object_flag_type.CURSE); obvious_mask.diff(f2); if (f.is_inter(obvious_mask)) { obvious = true; } Object_Flag.create_mask(obvious_mask, true, Object_Flag.object_flag_id.WIELD); /* Notice any obvious brands or slays */ Slay.object_notice_slays(this, obvious_mask); /* Learn about obvious flags */ known_flags.union(obvious_mask); /* XXX Eddie should these next NOT call object_check_for_ident due to worries about repairing? */ /* XXX Eddie this is a small hack, but jewelry with anything noticeable really is obvious */ /* XXX Eddie learn =soulkeeping vs =bodykeeping when notice sustain_str */ if (is_jewelry()) { /* Learn the flavor of jewelry with obvious flags */ if (EASY_LEARN && obvious) { flavor_aware(); } /* Learn all flags on any aware non-artifact jewelry */ if (flavor_is_aware() && artifact == null) { know_all_flags(); } } check_for_ident(); if (!obvious) { return; } /* XXX Eddie need to add stealth here, also need to assert/double-check everything is covered */ /* CC: also need to add FA! */ if (f.has(Object_Flag.STR.value)) { Utilities.msg("You feel %s!", pval[which_pval( Object_Flag.STR.value)] > 0 ? "stronger" : "weaker"); } if (f.has(Object_Flag.INT.value)) { Utilities.msg("You feel %s!", pval[which_pval( Object_Flag.INT.value)] > 0 ? "smarter" : "more stupid"); } if (f.has(Object_Flag.WIS.value)) { Utilities.msg("You feel %s!", pval[which_pval( Object_Flag.WIS.value)] > 0 ? "wiser" : "more naive"); } if (f.has(Object_Flag.DEX.value)) { Utilities.msg("You feel %s!", pval[which_pval( Object_Flag.DEX.value)] > 0 ? "more dextrous" : "clumsier"); } if (f.has(Object_Flag.CON.value)) { Utilities.msg("You feel %s!", pval[which_pval( Object_Flag.CON.value)] > 0 ? "healthier" : "sicklier"); } if (f.has(Object_Flag.CHR.value)) { Utilities.msg("You feel %s!", pval[which_pval( Object_Flag.CHR.value)] > 0 ? "cuter" : "uglier"); } if (f.has(Object_Flag.SPEED.value)) { Utilities.msg("You feel strangely %s.", pval[which_pval( Object_Flag.SPEED.value)] > 0 ? "quick" : "sluggish"); } if (f.has(Object_Flag.BLOWS.value)) { Utilities.msg("Your weapon %s in your hands.", pval[which_pval(Object_Flag.BLOWS.value)] > 0 ? "tingles" : "aches"); } if (f.has(Object_Flag.SHOTS.value)) { Utilities.msg("Your bow %s in your hands.", pval[which_pval(Object_Flag.SHOTS.value)] > 0 ? "tingles" : "aches"); } if (f.has(Object_Flag.INFRA.value)) { Utilities.msg("Your eyes tingle."); } if (f.has(Object_Flag.LIGHT.value)) { Utilities.msg("It glows!"); } if (f.has(Object_Flag.TELEPATHY.value)) { Utilities.msg("Your mind feels strangely sharper!"); } /* WARNING -- masking f by obvious mask -- this should be at the end of this function */ /* CC: I think this can safely go, but just in case ... */ /*flags_mask(f, OF_SIZE, OF_OBVIOUS_MASK, FLAG_END); */ /* Remember the flags */ notice_sensing(); /* XXX Eddie should we check_for_ident here? */ }
/** * Extract the multiplier from a given object hitting a given monster. * * \param o_ptr is the object being used to attack * \param m_ptr is the monster being attacked * \param best_s_ptr is the best applicable slay_table entry, or null if no * slay already known * \param real is whether this is a real attack (where we update lore) or a * simulation (where we don't) * \param known_only is whether we are using all the object flags, or only * the ones we *already* know about */ //Best_s_ptr was slay** public static void improve_attack_modifier(Object o_ptr, Monster.Monster m_ptr, ref Slay best_s_ptr, bool real, bool known_only) { Monster_Race r_ptr = Misc.r_info[m_ptr.r_idx]; Monster_Lore l_ptr = Misc.l_list[m_ptr.r_idx]; Bitflag f = new Bitflag(Object_Flag.SIZE); Bitflag known_f = new Bitflag(Object_Flag.SIZE); Bitflag note_f = new Bitflag(Object_Flag.SIZE); int i; o_ptr.object_flags(ref f); o_ptr.object_flags_known(ref known_f); for (i = 0; i < Slay.MAX.value; i++) { Slay s_ptr = list[i]; if ((known_only && !known_f.has(s_ptr.object_flag.value)) || (!known_only && !f.has(s_ptr.object_flag.value))) continue; /* In a real attack, learn about monster resistance or slay match if: * EITHER the slay flag on the object is known, * OR the monster is vulnerable to the slay/brand */ if (real && (known_f.has(s_ptr.object_flag.value) || (s_ptr.monster_flag != Monster_Flag.NONE && r_ptr.flags.has(s_ptr.monster_flag.value)) || (s_ptr.resist_flag != Monster_Flag.NONE && !r_ptr.flags.has(s_ptr.resist_flag.value)))) { /* notice any brand or slay that would affect monster */ note_f.wipe(); note_f.on(s_ptr.object_flag.value); object_notice_slays(o_ptr, note_f); if (m_ptr.ml && s_ptr.monster_flag != Monster_Flag.NONE) l_ptr.flags.on(s_ptr.monster_flag.value); if (m_ptr.ml && s_ptr.resist_flag != Monster_Flag.NONE) l_ptr.flags.on(s_ptr.resist_flag.value); } /* If the monster doesn't resist or the slay flag matches */ if ((s_ptr.brand != null && s_ptr.brand.Length != 0 && !r_ptr.flags.has(s_ptr.resist_flag.value)) || (s_ptr.monster_flag != Monster_Flag.NONE && r_ptr.flags.has(s_ptr.monster_flag.value))) { /* compare multipliers to determine best attack */ if ((best_s_ptr == null) || ((best_s_ptr).mult < s_ptr.mult)) best_s_ptr = s_ptr; } } }