/** * Notice curses on an object. * * \param o_ptr is the object to notice curses on */ bool notice_curses() { Bitflag f = new Bitflag(Object_Flag.SIZE); Bitflag f2 = new Bitflag(Object_Flag.SIZE); object_flags(ref f); /* Gather whatever curse flags there are to know */ Object_Flag.create_mask(f2, false, Object_Flag.object_flag_type.CURSE); /* Remove everything except the curse flags */ f.inter(f2); /* give knowledge of which curses are present */ notice_flags(f); check_for_ident(); Misc.p_ptr.notice |= Misc.PN_SQUELCH; return(!f.is_empty()); }
/* * Remove the "bad" spells from a spell list */ static void remove_bad_spells(int m_idx, Bitflag f) { Monster m_ptr = Cave.cave_monster(Cave.cave, m_idx); Monster_Race r_ptr = Misc.r_info[m_ptr.r_idx]; Bitflag f2 = new Bitflag(Monster_Spell_Flag.SIZE); Bitflag ai_flags = new Bitflag(Object_Flag.SIZE); int i; uint smart = 0; /* Stupid monsters act randomly */ if (r_ptr.flags.has(Monster_Flag.STUPID.value)) { return; } /* Take working copy of spell flags */ f2.copy(f); /* Don't heal if full */ if (m_ptr.hp >= m_ptr.maxhp) { f2.off(Monster_Spell_Flag.HEAL.value); } /* Don't haste if hasted with time remaining */ if (m_ptr.m_timed[(int)Misc.MON_TMD.FAST] > 10) { f2.off(Monster_Spell_Flag.HASTE.value); } /* Don't teleport to if the player is already next to us */ if (m_ptr.cdis == 1) { f2.off(Monster_Spell_Flag.TELE_TO.value); } /* Update acquired knowledge */ ai_flags.wipe(); if (Option.birth_ai_learn.value) { /* Occasionally forget player status */ if (Random.one_in_(100)) { m_ptr.known_pflags.wipe(); } /* Use the memorized flags */ smart = m_ptr.smart; ai_flags.copy(m_ptr.known_pflags); } /* Cheat if requested */ if (Option.birth_ai_cheat.value) { for (i = 0; i < Object_Flag.MAX.value; i++) { if (Misc.p_ptr.check_state(Object_Flag.list[i], Misc.p_ptr.state.flags)) { ai_flags.on(i); } } if (Misc.p_ptr.msp == 0) { smart |= Misc.SM_IMM_MANA; } } /* Cancel out certain flags based on knowledge */ if (!ai_flags.is_empty()) { throw new NotImplementedException(); //unset_spells(f2, ai_flags, r_ptr); } if ((smart & Misc.SM_IMM_MANA) != 0 && Random.randint0(100) < 50 * (r_ptr.flags.has(Monster_Flag.SMART.value) ? 2 : 1)) { f2.off(Monster_Spell_Flag.DRAIN_MANA.value); } /* use working copy of spell flags */ f.copy(f2); }
/* * Creatures can cast spells, shoot missiles, and breathe. * * Returns "true" if a spell (or whatever) was (successfully) cast. * * XXX XXX XXX This function could use some work, but remember to * keep it as optimized as possible, while retaining generic code. * * Verify the various "blind-ness" checks in the code. * * XXX XXX XXX Note that several effects should really not be "seen" * if the player is blind. * * Perhaps monsters should breathe at locations *near* the player, * since this would allow them to inflict "partial" damage. * * Perhaps smart monsters should decline to use "bolt" spells if * there is a monster in the way, unless they wish to kill it. * * It will not be possible to "correctly" handle the case in which a * monster attempts to attack a location which is thought to contain * the player, but which in fact is nowhere near the player, since this * might induce all sorts of messages about the attack itself, and about * the effects of the attack, which the player might or might not be in * a position to observe. Thus, for simplicity, it is probably best to * only allow "faulty" attacks by a monster if one of the important grids * (probably the initial or final grid) is in fact in view of the player. * It may be necessary to actually prevent spell attacks except when the * monster actually has line of sight to the player. Note that a monster * could be left in a bizarre situation after the player ducked behind a * pillar and then teleported away, for example. * * Note that this function attempts to optimize the use of spells for the * cases in which the monster has no spells, or has spells but cannot use * them, or has spells but they will have no "useful" effect. Note that * this function has been an efficiency bottleneck in the past. * * Note the special "MFLAG_NICE" flag, which prevents a monster from using * any spell attacks until the player has had a single chance to move. */ static bool make_attack_spell(int m_idx) { int chance, thrown_spell, rlev, failrate; Bitflag f = new Bitflag(Monster_Spell_Flag.SIZE); Monster m_ptr = Cave.cave_monster(Cave.cave, m_idx); Monster_Race r_ptr = Misc.r_info[m_ptr.r_idx]; Monster_Lore l_ptr = Misc.l_list[m_ptr.r_idx]; //char m_name[80], m_poss[80], ddesc[80]; string m_name, m_poss, ddesc; /* Player position */ int px = Misc.p_ptr.px; int py = Misc.p_ptr.py; /* Extract the blind-ness */ bool blind = (Misc.p_ptr.timed[(int)Timed_Effect.BLIND] != 0 ? true : false); /* Extract the "see-able-ness" */ bool seen = (!blind && m_ptr.ml); /* Assume "normal" target */ bool normal = true; /* Handle "leaving" */ if (Misc.p_ptr.leaving) return false; /* Cannot cast spells when confused */ if (m_ptr.m_timed[(int)Misc.MON_TMD.CONF] != 0) return (false); /* Cannot cast spells when nice */ if ((m_ptr.mflag & Monster_Flag.MFLAG_NICE) != 0) return false; /* Hack -- Extract the spell probability */ chance = (r_ptr.freq_innate + r_ptr.freq_spell) / 2; /* Not allowed to cast spells */ if (chance == 0) return false; /* Only do spells occasionally */ if (Random.randint0(100) >= chance) return false; /* Hack -- require projectable player */ if (normal) { /* Check range */ if (m_ptr.cdis > Misc.MAX_RANGE) return false; /* Check path */ if (!Cave.projectable(m_ptr.fy, m_ptr.fx, py, px, Spell.PROJECT_NONE)) return false; } /* Extract the monster level */ rlev = ((r_ptr.level >= 1) ? r_ptr.level : 1); /* Extract the racial spell flags */ f.copy(r_ptr.spell_flags); /* Allow "desperate" spells */ if (r_ptr.flags.has(Monster_Flag.SMART.value) && m_ptr.hp < m_ptr.maxhp / 10 && Random.randint0(100) < 50){ throw new NotImplementedException(); ///* Require intelligent spells */ //set_spells(f, RST_HASTE | RST_ANNOY | RST_ESCAPE | RST_HEAL | RST_TACTIC | RST_SUMMON); } /* Remove the "ineffective" spells */ remove_bad_spells(m_idx, f); /* Check whether summons and bolts are worth it. */ if (!r_ptr.flags.has(Monster_Flag.STUPID.value)) { /* Check for a clean bolt shot */ if (Monster_Spell_Flag.test_spells(f, Monster_Spell_Flag.mon_spell_type.RST_BOLT) && !clean_shot(m_ptr.fy, m_ptr.fx, py, px)) /* Remove spells that will only hurt friends */ Monster_Spell_Flag.set_spells(f, ~Monster_Spell_Flag.mon_spell_type.RST_BOLT); /* Check for a possible summon */ if (!(summon_possible(m_ptr.fy, m_ptr.fx))) /* Remove summoning spells */ Monster_Spell_Flag.set_spells(f, ~Monster_Spell_Flag.mon_spell_type.RST_SUMMON); } /* No spells left */ if (f.is_empty()) return false; /* Get the monster name (or "it") */ m_name = m_ptr.monster_desc(0x00); //Nick: Capatilize the first letter. m_name = Char.ToUpper(m_name[0]) + m_name.Substring(1); /* Get the monster possessive ("his"/"her"/"its") */ m_poss = m_ptr.monster_desc(Desc.PRO2 | Desc.POSS); /* Get the "died from" name */ ddesc = m_ptr.monster_desc(Desc.SHOW | Desc.IND2); /* Choose a spell to cast */ thrown_spell = choose_attack_spell(m_idx, f); /* Abort if no spell was chosen */ if (thrown_spell == 0) return false; /* If we see an unaware monster try to cast a spell, become aware of it */ if (m_ptr.unaware) become_aware(m_idx); /* Calculate spell failure rate */ failrate = 25 - (rlev + 3) / 4; if (m_ptr.m_timed[(int)Misc.MON_TMD.FEAR] != 0) failrate += 20; /* Stupid monsters will never fail (for jellies and such) */ if (Option.birth_ai_smart.value || r_ptr.flags.has(Monster_Flag.STUPID.value)) failrate = 0; /* Check for spell failure (innate attacks never fail) */ if ((thrown_spell >= Monster_Spell_Flag.MIN_NONINNATE_SPELL) && (Random.randint0(100) < failrate)) { /* Message */ Utilities.msg("{0} tries to cast a spell, but fails.", m_name); return true; } /* Cast the spell. */ Cave.disturb(Misc.p_ptr, 1, 0); /* Special case RSF_HASTE until TMD_* and MON_TMD_* are rationalised */ if (thrown_spell == Monster_Spell_Flag.HASTE.value) { if (blind) Utilities.msg("{0} mumbles.", m_name); else Utilities.msg("{0} concentrates on {1} body.", m_name, m_poss); mon_inc_timed(m_idx, Misc.MON_TMD.FAST, 50, 0, false); } else Monster_Spell_Flag.do_mon_spell(thrown_spell, m_idx, seen); /* Remember what the monster did to us */ if (seen) { l_ptr.spell_flags.on(thrown_spell); /* Innate spell */ if (thrown_spell < Monster_Spell_Flag.MIN_NONINNATE_SPELL) { if (l_ptr.cast_innate < byte.MaxValue) l_ptr.cast_innate++; } else { /* Bolt or Ball, or Special spell */ if (l_ptr.cast_spell < byte.MaxValue) l_ptr.cast_spell++; } } /* Always take note of monsters that kill you */ if (Misc.p_ptr.is_dead && (l_ptr.deaths < short.MaxValue)) { l_ptr.deaths++; } /* A spell was cast */ return true; }
/** * Notice curses on an object. * * \param o_ptr is the object to notice curses on */ bool notice_curses() { Bitflag f = new Bitflag(Object_Flag.SIZE); Bitflag f2 = new Bitflag(Object_Flag.SIZE); object_flags(ref f); /* Gather whatever curse flags there are to know */ Object_Flag.create_mask(f2, false, Object_Flag.object_flag_type.CURSE); /* Remove everything except the curse flags */ f.inter(f2); /* give knowledge of which curses are present */ notice_flags(f); check_for_ident(); Misc.p_ptr.notice |= Misc.PN_SQUELCH; return !f.is_empty(); }
/** * Add a pval to an object, rearranging flags as necessary. Returns true * if the number of pvals is now different, false if it is the same. * * \param o_ptr is the object we're adjusting * \param pval is the pval we are adding * \param flag is the flag to which we are adding the pval */ bool add_pval(int pval, int flag) { Bitflag f = new Bitflag(Object_Flag.SIZE); int a = -1, best_pval; /* Sanity check (we may be called with 0 - see ticket #1451) */ if (pval == 0) return false; Object_Flag.create_mask(f, false, Object_Flag.object_flag_type.PVAL, Object_Flag.object_flag_type.STAT); if (flags.has(flag)) { /* See if any other flags are associated with this pval */ a = which_pval(flag); f.off(flag); f.inter(pval_flags[a]); if (f.is_empty()) { /* Safe to increment and finish */ this.pval[a] += (short)pval; if (this.pval[a] == 0) { /* Remove the flag */ flags.off(flag); pval_flags[a].off(flag); } return (object_dedup_pvals()); } } /* So it doesn't have the flag, or it does but that pval also has others */ /* Create a new pval if we can */ if (this.num_pvals < Misc.MAX_PVALS) { this.pval[this.num_pvals] = (short)pval; this.pval_flags[this.num_pvals].on(flag); if (a != -1) { /* then we need to move the flag to the new pval */ this.pval[this.num_pvals] += this.pval[a]; this.pval_flags[a].off(flag); } else /* We need to add it to object_flags */ this.flags.on(flag); this.num_pvals++; /* We do this last because pvals start from zero */ /* We invert the logic because we've already added a pval */ return (!object_dedup_pvals()); } else { /* we use the closest existing pval */ best_pval = object_closest_pval((pval + (a == -1 ? 0 : this.pval[a]))); if (best_pval != a) { /* turn on the flag on the new pval */ this.pval_flags[best_pval].on(flag); if(a != -1) /* turn it off on its old pval */ this.pval_flags[a].off(flag); else /* add it to object_flags */ this.flags.on(flag); } return false; /* We haven't changed any pvals, so no need to de-dup */ } }
/* * Given an object, return a short identifier which gives some idea of what * the item is. */ public obj_pseudo_t pseudo() { Bitflag flags = new Bitflag(Object_Flag.SIZE); Bitflag f2 = new Bitflag(Object_Flag.SIZE); /* Get the known and obvious flags on the object, * not including curses or properties of the kind. */ object_flags_known(ref flags); Object_Flag.create_mask(f2, true, Object_Flag.object_flag_id.WIELD); /* FA on gloves is obvious to mage casters */ if (FA_would_be_obvious()) f2.on(Object_Flag.FREE_ACT.value); /* Now we remove the non-obvious known flags */ flags.inter(f2); /* Now we remove the cursed flags and the kind flags */ Object_Flag.create_mask(f2, false, Object_Flag.object_flag_type.CURSE); flags.diff(f2); flags.diff(kind.flags); if ((ident & IDENT_INDESTRUCT) != 0) return obj_pseudo_t.INSCRIP_SPECIAL; if ((was_sensed() || was_worn()) && artifact != null) return obj_pseudo_t.INSCRIP_SPECIAL; /* jewelry does not pseudo */ if (is_jewelry()) return obj_pseudo_t.INSCRIP_null; /* XXX Eddie should also check for flags with pvals where the pval exceeds * the base pval for things like picks of digging, though for now acid brand gets those */ if (!flags.is_empty()) return obj_pseudo_t.INSCRIP_SPLENDID; if (!is_known() && !was_sensed()) return obj_pseudo_t.INSCRIP_null; if (ego != null) { /* uncursed bad egos are not excellent */ if (ego.flags.is_inter(f2)) return obj_pseudo_t.INSCRIP_STRANGE; /* XXX Eddie need something worse */ else return obj_pseudo_t.INSCRIP_EXCELLENT; } if (to_a == Random.randcalc(kind.to_a, 0, aspect.MINIMISE) && to_h == Random.randcalc(kind.to_h, 0, aspect.MINIMISE) && to_d == Random.randcalc(kind.to_d, 0, aspect.MINIMISE)) return obj_pseudo_t.INSCRIP_AVERAGE; if (to_a >= Random.randcalc(kind.to_a, 0, aspect.MINIMISE) && to_h >= Random.randcalc(kind.to_h, 0, aspect.MINIMISE) && to_d >= Random.randcalc(kind.to_d, 0, aspect.MINIMISE)) return obj_pseudo_t.INSCRIP_MAGICAL; if (to_a <= Random.randcalc(kind.to_a, 0, aspect.MINIMISE) && to_h <= Random.randcalc(kind.to_h, 0, aspect.MINIMISE) && to_d <= Random.randcalc(kind.to_d, 0, aspect.MINIMISE)) return obj_pseudo_t.INSCRIP_MAGICAL; return obj_pseudo_t.INSCRIP_STRANGE; }
/** * Create a cache of slay combinations found on ego items, and the values of * these combinations. This is to speed up slay_power(), which will be called * many times for ego items during the game. * * \param items is the set of ego types from which we are extracting slay * combinations */ public static int create_slay_cache(Ego_Item[] items) { int count = 0; Bitflag cacheme = new Bitflag(Object_Flag.SIZE); Bitflag slay_mask = new Bitflag(Object_Flag.SIZE); /* Build the slay mask */ Object_Flag.create_mask(slay_mask, false, Object_Flag.object_flag_type.SLAY, Object_Flag.object_flag_type.KILL, Object_Flag.object_flag_type.BRAND); /* Calculate necessary size of slay_cache */ Bitflag[] dupcheck = new Bitflag[Misc.z_info.e_max]; for (int i = 0; i < Misc.z_info.e_max; i++) { dupcheck[i] = new Bitflag(Object_Flag.SIZE); Ego_Item e_ptr = items[i]; //Some items are null... I guess we should just skip them...? //TODO: Find out why they are null, and see if we actually should just skip them... if (e_ptr == null) { continue; } /* Find the slay flags on this ego */ cacheme.copy(e_ptr.flags); cacheme.inter(slay_mask); /* Only consider non-empty combinations of slay flags */ if (!cacheme.is_empty()) { /* Skip previously scanned combinations */ for (int j = 0; j < i; j++) { if (cacheme.is_equal(dupcheck[j])) { continue; } } /* msg("Found a new slay combo on an ego item"); */ count++; dupcheck[i].copy(cacheme); } } /* Allocate slay_cache with an extra empty element for an iteration stop */ slay_cache = new flag_cache[count + 1]; count = 0; /* Populate the slay_cache */ for (int i = 0; i < Misc.z_info.e_max; i++) { if (!dupcheck[i].is_empty()) { slay_cache[count] = new flag_cache(); slay_cache[count].flags.copy(dupcheck[i]); slay_cache[count].value = 0; count++; /*msg("Cached a slay combination");*/ } } //From a time without garbage collection... /*for (i = 0; i < z_info.e_max; i++) * FREE(dupcheck[i]); * FREE(dupcheck);*/ /* Success */ return(0); }
/* * Remove the "bad" spells from a spell list */ static void remove_bad_spells(int m_idx, Bitflag f) { Monster m_ptr = Cave.cave_monster(Cave.cave, m_idx); Monster_Race r_ptr = Misc.r_info[m_ptr.r_idx]; Bitflag f2 = new Bitflag(Monster_Spell_Flag.SIZE); Bitflag ai_flags = new Bitflag(Object_Flag.SIZE); int i; uint smart = 0; /* Stupid monsters act randomly */ if (r_ptr.flags.has(Monster_Flag.STUPID.value)) return; /* Take working copy of spell flags */ f2.copy(f); /* Don't heal if full */ if (m_ptr.hp >= m_ptr.maxhp) f2.off(Monster_Spell_Flag.HEAL.value); /* Don't haste if hasted with time remaining */ if (m_ptr.m_timed[(int)Misc.MON_TMD.FAST] > 10) f2.off(Monster_Spell_Flag.HASTE.value); /* Don't teleport to if the player is already next to us */ if (m_ptr.cdis == 1) f2.off(Monster_Spell_Flag.TELE_TO.value); /* Update acquired knowledge */ ai_flags.wipe(); if (Option.birth_ai_learn.value) { /* Occasionally forget player status */ if (Random.one_in_(100)) m_ptr.known_pflags.wipe(); /* Use the memorized flags */ smart = m_ptr.smart; ai_flags.copy(m_ptr.known_pflags); } /* Cheat if requested */ if (Option.birth_ai_cheat.value) { for (i = 0; i < Object_Flag.MAX.value; i++) if (Misc.p_ptr.check_state(Object_Flag.list[i], Misc.p_ptr.state.flags)) ai_flags.on(i); if (Misc.p_ptr.msp == 0) smart |= Misc.SM_IMM_MANA; } /* Cancel out certain flags based on knowledge */ if(!ai_flags.is_empty()) { throw new NotImplementedException(); //unset_spells(f2, ai_flags, r_ptr); } if ((smart & Misc.SM_IMM_MANA) != 0 && Random.randint0(100) < 50 * (r_ptr.flags.has(Monster_Flag.SMART.value) ? 2 : 1)) f2.off(Monster_Spell_Flag.DRAIN_MANA.value); /* use working copy of spell flags */ f.copy(f2); }
/** * Create a cache of slay combinations found on ego items, and the values of * these combinations. This is to speed up slay_power(), which will be called * many times for ego items during the game. * * \param items is the set of ego types from which we are extracting slay * combinations */ public static int create_slay_cache(Ego_Item[] items) { int count = 0; Bitflag cacheme = new Bitflag(Object_Flag.SIZE); Bitflag slay_mask = new Bitflag(Object_Flag.SIZE); /* Build the slay mask */ Object_Flag.create_mask(slay_mask, false, Object_Flag.object_flag_type.SLAY, Object_Flag.object_flag_type.KILL, Object_Flag.object_flag_type.BRAND); /* Calculate necessary size of slay_cache */ Bitflag[] dupcheck = new Bitflag[Misc.z_info.e_max]; for (int i = 0; i < Misc.z_info.e_max; i++) { dupcheck[i] = new Bitflag(Object_Flag.SIZE); Ego_Item e_ptr = items[i]; //Some items are null... I guess we should just skip them...? //TODO: Find out why they are null, and see if we actually should just skip them... if(e_ptr == null) { continue; } /* Find the slay flags on this ego */ cacheme.copy(e_ptr.flags); cacheme.inter(slay_mask); /* Only consider non-empty combinations of slay flags */ if (!cacheme.is_empty()) { /* Skip previously scanned combinations */ for (int j = 0; j < i; j++) if (cacheme.is_equal(dupcheck[j])) continue; /* msg("Found a new slay combo on an ego item"); */ count++; dupcheck[i].copy(cacheme); } } /* Allocate slay_cache with an extra empty element for an iteration stop */ slay_cache = new flag_cache[count + 1]; count = 0; /* Populate the slay_cache */ for (int i = 0; i < Misc.z_info.e_max; i++) { if (!dupcheck[i].is_empty()) { slay_cache[count] = new flag_cache(); slay_cache[count].flags.copy(dupcheck[i]); slay_cache[count].value = 0; count++; /*msg("Cached a slay combination");*/ } } //From a time without garbage collection... /*for (i = 0; i < z_info.e_max; i++) FREE(dupcheck[i]); FREE(dupcheck);*/ /* Success */ return 0; }