/** * Attack the monster at the given location * * We get blows until energy drops below that required for another blow, or * until the target monster dies. Each blow is handled by py_attack_real(). * We don't allow @ to spend more than 100 energy in one go, to avoid slower * monsters getting double moves. */ public static void py_attack(int y, int x) { int blow_energy = 10000 / Misc.p_ptr.state.num_blows; int blows = 0; bool fear = false; Monster.Monster m_ptr = Cave.cave_monster(Cave.cave, Cave.cave.m_idx[y][x]); /* disturb the player */ Cave.disturb(Misc.p_ptr, 0, 0); /* Initialize the energy used */ Misc.p_ptr.energy_use = 0; /* Attack until energy runs out or enemy dies. We limit energy use to 100 * to avoid giving monsters a possible double move. */ while (Misc.p_ptr.energy >= blow_energy * (blows + 1)) { bool stop = py_attack_real(y, x, ref fear); Misc.p_ptr.energy_use += (short)blow_energy; if (stop || Misc.p_ptr.energy_use + blow_energy > 100) { break; } blows++; } /* Hack - delay fear messages */ if (fear && m_ptr.ml) { //char m_name[80]; string m_name = m_ptr.monster_desc(0); Monster_Message.add_monster_message(m_name, Cave.cave.m_idx[y][x], MON_MSG.FLEE_IN_TERROR, true); } }
public static void wr_monsters() { int i; int j; if (Misc.p_ptr.is_dead) { return; } /* Total monsters */ wr_u16b((ushort)Cave.cave_monster_max(Cave.cave)); /* Dump the monsters */ for (i = 1; i < Cave.cave_monster_max(Cave.cave); i++) { byte unaware = 0; Monster.Monster m_ptr = Cave.cave_monster(Cave.cave, i); wr_s16b(m_ptr.r_idx); wr_byte(m_ptr.fy); wr_byte(m_ptr.fx); wr_s16b(m_ptr.hp); wr_s16b(m_ptr.maxhp); wr_byte(m_ptr.mspeed); wr_byte(m_ptr.energy); wr_byte((byte)Misc.MON_TMD.MAX); for (j = 0; j < (byte)Misc.MON_TMD.MAX; j++) { wr_s16b(m_ptr.m_timed[j]); } if (m_ptr.unaware) { unaware |= 0x01; } wr_byte(unaware); for (j = 0; j < Object.Object_Flag.BYTES && j < Object.Object_Flag.SIZE; j++) { wr_byte(m_ptr.known_pflags[j]); } if (j < Object.Object_Flag.BYTES) { Savefile.pad_bytes(Object.Object_Flag.BYTES - j); } wr_byte(0); } }
/* * Redraw the "monster health bar" * * The "monster health bar" provides visual feedback on the "health" * of the monster currently being "tracked". There are several ways * to "track" a monster, including targetting it, attacking it, and * affecting it (and nobody else) with a ranged attack. When nothing * is being tracked, we clear the health bar. If the monster being * tracked is not currently visible, a special health bar is shown. */ static void prt_health(int row, int col) { ConsoleColor attr = monster_health_attr(); Monster.Monster mon; /* Not tracking */ if (Misc.p_ptr.health_who == 0) { /* Erase the health bar */ Term.erase(col, row, 12); return; } mon = Cave.cave_monster(Cave.cave, Misc.p_ptr.health_who); /* Tracking an unseen, hallucinatory, or dead monster */ if ((!mon.ml) || /* Unseen */ (Misc.p_ptr.timed[(int)Timed_Effect.IMAGE] != 0) || /* Hallucination */ (mon.hp < 0)) /* Dead (?) */ { /* The monster health is "unknown" */ Term.putstr(col, row, 12, attr, "[----------]"); } /* Tracking a visible monster */ else { int pct, len; Monster.Monster m_ptr = Cave.cave_monster(Cave.cave, Misc.p_ptr.health_who); /* Extract the "percent" of health */ pct = (int)(100L * m_ptr.hp / m_ptr.maxhp); /* Convert percent into "health" */ len = (pct < 10) ? 1 : (pct < 90) ? (pct / 10 + 1) : 10; /* Default to "unknown" */ Term.putstr(col, row, 12, ConsoleColor.White, "[----------]"); /* Dump the current "health" (use '*' symbols) */ Term.putstr(col + 1, row, len, attr, "**********"); } }
/** * Helper function used with ranged_helper by do_cmd_fire. */ public static attack_result make_ranged_shot(Object.Object o_ptr, int y, int x) { attack_result result = new attack_result(false, 0, 0, "hit"); Object.Object j_ptr = Misc.p_ptr.inventory[Misc.INVEN_BOW]; Monster.Monster m_ptr = Cave.cave_monster(Cave.cave, Cave.cave.m_idx[y][x]); Monster_Race r_ptr = Misc.r_info[m_ptr.r_idx]; int bonus = Misc.p_ptr.state.to_h + o_ptr.to_h + j_ptr.to_h; int chance = Misc.p_ptr.state.skills[(int)Skill.TO_HIT_BOW] + bonus * Misc.BTH_PLUS_ADJ; int chance2 = chance - Cave.distance(Misc.p_ptr.py, Misc.p_ptr.px, y, x); int multiplier = Misc.p_ptr.state.ammo_mult; Slay best_s_ptr = null; /* Did we hit it (penalize distance travelled) */ if (!test_hit(chance2, r_ptr.ac, m_ptr.ml)) { return(result); } result.success = true; Slay.improve_attack_modifier(o_ptr, m_ptr, ref best_s_ptr, true, false); Slay.improve_attack_modifier(j_ptr, m_ptr, ref best_s_ptr, true, false); /* If we have a slay, modify the multiplier appropriately */ if (best_s_ptr != null) { result.hit_verb = best_s_ptr.range_verb; multiplier += best_s_ptr.mult; } /* Apply damage: multiplier, slays, criticals, bonuses */ result.dmg = Random.damroll(o_ptr.dd, o_ptr.ds); result.dmg += o_ptr.to_d + j_ptr.to_d; result.dmg *= multiplier; result.dmg = critical_shot(o_ptr.weight, o_ptr.to_h, result.dmg, ref result.msg_type); Misc.p_ptr.inventory[Misc.INVEN_BOW].notice_attack_plusses(); return(result); }
/* * Hack -- compare the "strength" of two monsters XXX XXX XXX */ static int compare_monsters(Monster m_ptr, Monster n_ptr) { Monster_Race r_ptr; int mexp1, mexp2; /* Race 1 */ r_ptr = Misc.r_info[m_ptr.r_idx]; /* Extract mexp */ mexp1 = r_ptr.mexp; /* Race 2 */ r_ptr = Misc.r_info[n_ptr.r_idx]; /* Extract mexp */ mexp2 = r_ptr.mexp; /* Compare */ if (mexp1 < mexp2) return (-1); if (mexp1 > mexp2) return (1); /* Assume equal */ return (0); }
internal void COPY(Monster n_ptr) { throw new NotImplementedException(); }
/** * Place a copy of a monster in the dungeon XXX XXX */ public static short place_monster(int y, int x, Monster n_ptr, byte origin) { short m_idx; Monster_Race r_ptr; /* Paranoia XXX XXX */ if (Cave.cave.m_idx[y][x] != 0) return 0; /* Get a new record */ m_idx = mon_pop(); if (m_idx == 0) return 0; n_ptr.midx = m_idx; /* Make a new monster */ Cave.cave.m_idx[y][x] = m_idx; /* Get the new monster */ //m_ptr = Cave.cave_monster(Cave.cave, m_idx); /* Copy the monster XXX */ //m_ptr.COPY(n_ptr); //Nick: Set the new monster... Much better than the above two steps... Cave.cave_monster_set(Cave.cave, m_idx, n_ptr); /* Location */ n_ptr.fy = (byte)y; n_ptr.fx = (byte)x; /* Update the monster */ Monster.update_mon(m_idx, true); /* Get the new race */ r_ptr = Misc.r_info[n_ptr.r_idx]; /* Hack -- Count the number of "reproducers" */ if (r_ptr.flags.has(Monster_Flag.MULTIPLY.value)) Misc.num_repro++; /* Count racial occurances */ r_ptr.cur_num++; /* Create the monster's drop, if any */ if(origin != 0) { mon_create_drop(m_idx, origin); } /* Make mimics start mimicking */ if (origin != 0 && r_ptr.mimic_kinds != null) { throw new NotImplementedException(); //object_type *i_ptr; //object_type object_type_body; //object_kind *kind = r_ptr.mimic_kinds.kind; //struct monster_mimic *mimic_kind; //int i = 1; ///* Pick a random object kind to mimic */ //for (mimic_kind = r_ptr.mimic_kinds; mimic_kind; mimic_kind = mimic_kind.next, i++) { // if (one_in_(i)) kind = mimic_kind.kind; //} //i_ptr = &object_type_body; //if (kind.tval == TV_GOLD) { // make_gold(i_ptr, p_ptr.depth, kind.sval); //} else { // object_prep(i_ptr, kind, r_ptr.level, RANDOMISE); // apply_magic(i_ptr, r_ptr.level, true, false, false); // i_ptr.number = 1; //} //i_ptr.mimicking_m_idx = m_idx; //m_ptr.mimicked_o_idx = floor_carry(cave, y, x, i_ptr); } /* Result */ return m_idx; }
/* * Attempt to place a monster of the given race at the given location. * * To give the player a sporting chance, any monster that appears in * line-of-sight and is extremely dangerous can be marked as * "FORCE_SLEEP", which will cause them to be placed with low energy, * which often (but not always) lets the player move before they do. * * This routine refuses to place out-of-depth "FORCE_DEPTH" monsters. * * XXX XXX XXX Use special "here" and "dead" flags for unique monsters, * remove old "cur_num" and "max_num" fields. * * XXX XXX XXX Actually, do something similar for artifacts, to simplify * the "preserve" mode, and to make the "what artifacts" flag more useful. * * This is the only function which may place a monster in the dungeon, * except for the savefile loading code. */ static bool place_new_monster_one(int y, int x, int r_idx, bool slp, byte origin) { int i; Monster_Race r_ptr; Monster n_ptr; //Monster monster_type_body; string name; /* Paranoia */ if (!Cave.in_bounds(y, x)) return (false); /* Require empty space */ if (!Cave.cave_empty_bold(y, x)) return (false); /* No creation on glyph of warding */ if (Cave.cave.feat[y][x] == Cave.FEAT_GLYPH) return (false); /* Paranoia */ if (r_idx == 0) return (false); /* Race */ r_ptr = Misc.r_info[r_idx]; if (r_ptr == null) return (false); /* Paranoia */ if (r_ptr.Name == null) return (false); name = r_ptr.Name; /* "unique" monsters must be "unique" */ if (r_ptr.flags.has(Monster_Flag.UNIQUE.value) && r_ptr.cur_num >= r_ptr.max_num) return (false); /* Depth monsters may NOT be created out of depth */ if (r_ptr.flags.has(Monster_Flag.FORCE_DEPTH.value) && Misc.p_ptr.depth < r_ptr.level) return (false); /* Add to level feeling */ Cave.cave.mon_rating += (uint)(r_ptr.power / 20); /* Check out-of-depth-ness */ if (r_ptr.level > Misc.p_ptr.depth) { if (r_ptr.flags.has(Monster_Flag.UNIQUE.value)) { /* OOD unique */ if (Option.cheat_hear.value) Utilities.msg("Deep Unique (%s).", name); } else { /* Normal monsters but OOD */ if (Option.cheat_hear.value) Utilities.msg("Deep Monster (%s).", name); } /* Boost rating by power per 10 levels OOD */ Cave.cave.mon_rating += (uint)((r_ptr.level - Misc.p_ptr.depth) * r_ptr.power / 200); } /* Note uniques for cheaters */ else if (r_ptr.flags.has(Monster_Flag.UNIQUE.value) && Option.cheat_hear.value) Utilities.msg("Unique (%s).", name); /* Get local monster */ n_ptr = new Monster();//&monster_type_body; /* Clean out the monster */ n_ptr.WIPE(); //(void)WIPE(n_ptr, monster_type); /* Save the race */ n_ptr.r_idx = (short)r_idx; /* Enforce sleeping if needed */ if (slp && r_ptr.sleep != 0) { int val = r_ptr.sleep; n_ptr.m_timed[(int)Misc.MON_TMD.SLEEP] = (short)((val * 2) + Random.randint1(val * 10)); } /* Uniques get a fixed amount of HP */ if (r_ptr.flags.has(Monster_Flag.UNIQUE.value)) n_ptr.maxhp = (short)r_ptr.avg_hp; else { n_ptr.maxhp = (short)mon_hp(r_ptr, aspect.RANDOMISE); n_ptr.maxhp = (short)Math.Max((int)n_ptr.maxhp, 1); } /* And start out fully healthy */ n_ptr.hp = n_ptr.maxhp; /* Extract the monster base speed */ n_ptr.mspeed = r_ptr.speed; /* Hack -- small racial variety */ if (!r_ptr.flags.has(Monster_Flag.UNIQUE.value)) { /* Allow some small variation per monster */ i = Misc.extract_energy[r_ptr.speed] / 10; if (i != 0) n_ptr.mspeed += (byte)Random.rand_spread(0, i); } /* Give a random starting energy */ n_ptr.energy = (byte)Random.randint0(50); /* Force monster to wait for player */ if (r_ptr.flags.has(Monster_Flag.FORCE_SLEEP.value)) n_ptr.mflag |= (Monster_Flag.MFLAG_NICE); /* Radiate light? */ if (r_ptr.flags.has(Monster_Flag.HAS_LIGHT.value)) Misc.p_ptr.update |= Misc.PU_UPDATE_VIEW; /* Is this obviously a monster? (Mimics etc. aren't) */ if (r_ptr.flags.has(Monster_Flag.UNAWARE.value)) n_ptr.unaware = true; else n_ptr.unaware = false; /* Set the color if necessary */ if (r_ptr.flags.has(Monster_Flag.ATTR_RAND.value)) n_ptr.attr = Utilities.num_to_attr(Random.randint1(Enum.GetValues(typeof(ConsoleColor)).Length - 1)); /* Place the monster in the dungeon */ if (place_monster(y, x, n_ptr, origin) == 0) return (false); /* Success */ return (true); }
static int mdam(Monster.Monster m) { return m.Race.blow[0].d_dice; }
/* * Update the current "run" path * * Return true if the running should be stopped */ static bool run_test() { int py = Misc.p_ptr.py; int px = Misc.p_ptr.px; int prev_dir; int new_dir; int row, col; int i, max; bool inv; int option, option2; /* No options yet */ option = 0; option2 = 0; /* Where we came from */ prev_dir = Misc.p_ptr.run_old_dir; /* Range of newly adjacent grids */ max = (prev_dir & 0x01) + 1; /* Look at every newly adjacent square. */ for (i = -max; i <= max; i++) { Object.Object o_ptr; /* New direction */ new_dir = cycle[chome[prev_dir] + i]; /* New location */ row = py + Misc.ddy[new_dir]; col = px + Misc.ddx[new_dir]; /* Visible monsters abort running */ if (Cave.cave.m_idx[row][col] > 0) { Monster.Monster m_ptr = Cave.cave_monster(Cave.cave, Cave.cave.m_idx[row][col]); /* Visible monster */ if (m_ptr.ml) { return(true); } } /* Visible objects abort running */ for (o_ptr = Object.Object.get_first_object(row, col); o_ptr != null; o_ptr = Object.Object.get_next_object(o_ptr)) { /* Visible object */ if (o_ptr.marked != 0 && !Squelch.item_ok(o_ptr)) { return(true); } } /* Assume unknown */ inv = true; /* Check memorized grids */ if ((Cave.cave.info[row][col] & (Cave.CAVE_MARK)) != 0) { bool notice = true; /* Examine the terrain */ switch (Cave.cave.feat[row][col]) { /* Floors */ case Cave.FEAT_FLOOR: /* Invis traps */ case Cave.FEAT_INVIS: /* Secret doors */ case Cave.FEAT_SECRET: /* Normal veins */ case Cave.FEAT_MAGMA: case Cave.FEAT_QUARTZ: /* Hidden treasure */ case Cave.FEAT_MAGMA_H: case Cave.FEAT_QUARTZ_H: /* Walls */ case Cave.FEAT_WALL_EXTRA: case Cave.FEAT_WALL_INNER: case Cave.FEAT_WALL_OUTER: case Cave.FEAT_WALL_SOLID: case Cave.FEAT_PERM_EXTRA: case Cave.FEAT_PERM_INNER: case Cave.FEAT_PERM_OUTER: case Cave.FEAT_PERM_SOLID: { /* Ignore */ notice = false; /* Done */ break; } } /* Interesting feature */ if (notice) { return(true); } /* The grid is "visible" */ inv = false; } /* Analyze unknown grids and floors */ if (inv || Cave.cave_floor_bold(row, col)) { /* Looking for open area */ if (Misc.p_ptr.run_open_area) { /* Nothing */ } /* The first new direction. */ else if (option == 0) { option = new_dir; } /* Three new directions. Stop running. */ else if (option2 != 0) { return(true); } /* Two non-adjacent new directions. Stop running. */ else if (option != cycle[chome[prev_dir] + i - 1]) { return(true); } /* Two new (adjacent) directions (case 1) */ else if ((new_dir & 0x01) != 0) { option2 = new_dir; } /* Two new (adjacent) directions (case 2) */ else { option2 = option; option = new_dir; } } /* Obstacle, while looking for open area */ else { if (Misc.p_ptr.run_open_area) { if (i < 0) { /* Break to the right */ Misc.p_ptr.run_break_right = true; } else if (i > 0) { /* Break to the left */ Misc.p_ptr.run_break_left = true; } } } } /* Look at every soon to be newly adjacent square. */ for (i = -max; i <= max; i++) { /* New direction */ new_dir = cycle[chome[prev_dir] + i]; /* New location */ row = py + Misc.ddy[prev_dir] + Misc.ddy[new_dir]; col = px + Misc.ddx[prev_dir] + Misc.ddx[new_dir]; /* HACK: Ugh. Sometimes we come up with illegal bounds. This will * treat the symptom but not the disease. */ if (row >= Cave.DUNGEON_HGT || col >= Cave.DUNGEON_WID) { continue; } if (row < 0 || col < 0) { continue; } /* Visible monsters abort running */ if (Cave.cave.m_idx[row][col] > 0) { Monster.Monster m_ptr = Cave.cave_monster(Cave.cave, Cave.cave.m_idx[row][col]); /* Visible monster */ if (m_ptr.ml) { return(true); } } } /* Looking for open area */ if (Misc.p_ptr.run_open_area) { /* Hack -- look again */ for (i = -max; i < 0; i++) { new_dir = cycle[chome[prev_dir] + i]; row = py + Misc.ddy[new_dir]; col = px + Misc.ddx[new_dir]; /* Unknown grid or non-wall */ /* Was: cave_floor_bold(row, col) */ if ((Cave.cave.info[row][col] & (Cave.CAVE_MARK)) == 0 || (Cave.cave.feat[row][col] < Cave.FEAT_SECRET)) { /* Looking to break right */ if (Misc.p_ptr.run_break_right) { return(true); } } /* Obstacle */ else { /* Looking to break left */ if (Misc.p_ptr.run_break_left) { return(true); } } } /* Hack -- look again */ for (i = max; i > 0; i--) { new_dir = cycle[chome[prev_dir] + i]; row = py + Misc.ddy[new_dir]; col = px + Misc.ddx[new_dir]; /* Unknown grid or non-wall */ /* Was: cave_floor_bold(row, col) */ if ((Cave.cave.info[row][col] & (Cave.CAVE_MARK)) == 0 || (Cave.cave.feat[row][col] < Cave.FEAT_SECRET)) { /* Looking to break left */ if (Misc.p_ptr.run_break_left) { return(true); } } /* Obstacle */ else { /* Looking to break right */ if (Misc.p_ptr.run_break_right) { return(true); } } } } /* Not looking for open area */ else { /* No options */ if (option == 0) { return(true); } /* One option */ else if (option2 == 0) { /* Primary option */ Misc.p_ptr.run_cur_dir = (short)option; /* No other options */ Misc.p_ptr.run_old_dir = (short)option; } /* Two options, examining corners */ else { /* Primary option */ Misc.p_ptr.run_cur_dir = (short)option; /* Hack -- allow curving */ Misc.p_ptr.run_old_dir = (short)option2; } } /* About to hit a known wall, stop */ if (see_wall(Misc.p_ptr.run_cur_dir, py, px)) { return(true); } /* Failure */ return(false); }
/* * Calculate the monster bar color separately, for ports. */ static ConsoleColor monster_health_attr() { ConsoleColor attr = ConsoleColor.White; /* Not tracking */ if (Misc.p_ptr.health_who == 0) { attr = ConsoleColor.Gray; //TERM_DARK; } /* Tracking an unseen, hallucinatory, or dead monster */ else if ((!Cave.cave_monster(Cave.cave, Misc.p_ptr.health_who).ml) || (Misc.p_ptr.timed[(int)Timed_Effect.IMAGE]) != 0 || (Cave.cave_monster(Cave.cave, Misc.p_ptr.health_who).hp < 0)) { /* The monster health is "unknown" */ attr = ConsoleColor.White; } else { Monster.Monster mon = Cave.cave_monster(Cave.cave, Misc.p_ptr.health_who); int pct; /* Default to almost dead */ attr = ConsoleColor.Red; /* Extract the "percent" of health */ pct = (int)(100L * mon.hp / mon.maxhp); /* Badly wounded */ if (pct >= 10) { attr = ConsoleColor.Red; } /* Wounded */ if (pct >= 25) { attr = ConsoleColor.DarkYellow; } /* Somewhat Wounded */ if (pct >= 60) { attr = ConsoleColor.Yellow; } /* Healthy */ if (pct >= 100) { attr = ConsoleColor.Green; } /* Afraid */ if (mon.m_timed[(int)Misc.MON_TMD.FEAR] != 0) { attr = ConsoleColor.Magenta; } /* Confused */ if (mon.m_timed[(int)Misc.MON_TMD.CONF] != 0) { attr = ConsoleColor.DarkRed; //UMBER } /* Stunned */ if (mon.m_timed[(int)Misc.MON_TMD.STUN] != 0) { attr = ConsoleColor.Blue; //Light Blue } /* Asleep */ if (mon.m_timed[(int)Misc.MON_TMD.SLEEP] != 0) { attr = ConsoleColor.DarkBlue; //Blue } } return(attr); }
/** * Set a monster on the current level by its index. */ public static void cave_monster_set(Cave c, int idx, Monster.Monster m) { c.monsters[idx] = m; }
/** * Attempts to set the timer of the given monster effect to `timer`. * * Checks to see if the monster resists the effect, using mon_resist_effect(). * If not, the effect is set to `timer` turns. If `timer` is 0, or if the * effect timer was 0, or if MON_TMD_FLG_NOTIFY is set in `flag`, then a * message is printed, unless MON_TMD_FLG_NOMESSAGE is set in `flag`. * * Set a timed monster event to 'v'. Give messages if the right flags are set. * Check if the monster is able to resist the spell. Mark the lore. * Returns true if the monster was affected. * Return false if the monster was unaffected. */ static bool mon_set_timed(Monster m_ptr, Misc.MON_TMD ef_idx, int timer, ushort flag, bool id) { mon_timed_effect effect; MON_MSG m_note = 0; int resisted; int old_timer; Misc.assert(ef_idx >= 0 && ef_idx < Misc.MON_TMD.MAX); effect = effects[(int)ef_idx]; Misc.assert(m_ptr != null); old_timer = m_ptr.m_timed[(int)ef_idx]; /* Ignore dead monsters */ if (m_ptr.r_idx == 0) return false; /* No change */ if (old_timer == timer) return false; if (timer == 0) { /* Turning off, usually mention */ m_note = effect.message_end; flag |= Misc.MON_TMD_FLG_NOTIFY; } else if (old_timer == 0) { /* Turning on, usually mention */ flag |= Misc.MON_TMD_FLG_NOTIFY; m_note = effect.message_begin; } else if (timer > old_timer) { /* Different message for increases, but don't automatically mention. */ m_note = effect.message_increase; } /* Determine if the monster resisted or not */ resisted = mon_resist_effect(m_ptr, ef_idx, timer, flag)?1:0; if (resisted != 0) m_note = MON_MSG.UNAFFECTED; else m_ptr.m_timed[(int)ef_idx] = (short)timer; if (Misc.p_ptr.health_who == m_ptr.midx) Misc.p_ptr.redraw |= (Misc.PR_HEALTH); /* Update the visuals, as appropriate. */ Misc.p_ptr.redraw |= (Misc.PR_MONLIST); /* Print a message if there is one, if the effect allows for it, and if * either the monster is visible, or we're trying to ID something */ if (m_note != 0 && (m_ptr.ml || id) && (flag & Misc.MON_TMD_FLG_NOMESSAGE) == 0 && (flag & Misc.MON_TMD_FLG_NOTIFY) != 0) { //char m_name[80]; string m_name; m_name = m_ptr.monster_desc((Desc)0x04); Monster_Message.add_monster_message(m_name, m_ptr.midx, m_note, true); } return resisted == 0; }
/** * Determines whether the given monster successfully resists the given effect. * * If MON_TMD_FLG_NOFAIL is set in `flag`, this returns false. * Then we determine if the monster resists the effect for some racial * reason. For example, the monster might have the NO_SLEEP flag, in which * case it always resists sleep. Or if it breathes chaos, it always resists * confusion. If the given monster doesn't resist for any of these reasons, * then it makes a saving throw. If MON_TMD_MON_SOURCE is set in `flag`, * indicating that another monster caused this effect, then the chance of * success on the saving throw just depends on the monster's native depth. * Otherwise, the chance of success decreases as `timer` increases. * * Also marks the lore for any appropriate resists. */ static bool mon_resist_effect(Monster m_ptr, Misc.MON_TMD ef_idx, int timer, ushort flag) { mon_timed_effect effect; int resist_chance; Monster_Race r_ptr; Monster_Lore l_ptr; Misc.assert(ef_idx >= 0 && ef_idx < Misc.MON_TMD.MAX); effect = effects[(int)ef_idx]; Misc.assert(m_ptr != null); r_ptr = Misc.r_info[m_ptr.r_idx]; l_ptr = Misc.l_list[m_ptr.r_idx]; /* Hasting never fails */ if (ef_idx == Misc.MON_TMD.FAST) return (false); /* Some effects are marked to never fail */ if ((flag & Misc.MON_TMD_FLG_NOFAIL) != 0) return (false); /* A sleeping monster resists further sleeping */ if (ef_idx == (int)Misc.MON_TMD.SLEEP && m_ptr.m_timed[(int)ef_idx] != 0) return (true); /* If the monster resists innately, learn about it */ if (r_ptr.flags.has((int)effect.flag_resist)) { if (m_ptr.ml) l_ptr.flags.on((int)effect.flag_resist); return (true); } /* Monsters with specific breaths resist stunning*/ if (ef_idx == Misc.MON_TMD.STUN && (r_ptr.spell_flags.has(Monster_Spell_Flag.BR_SOUN.value) || r_ptr.spell_flags.has(Monster_Spell_Flag.BR_WALL.value))) { /* Add the lore */ if (m_ptr.ml) { if (r_ptr.spell_flags.has(Monster_Spell_Flag.BR_SOUN.value)) l_ptr.spell_flags.on(Monster_Spell_Flag.BR_SOUN.value); if (r_ptr.spell_flags.has(Monster_Spell_Flag.BR_WALL.value)) l_ptr.spell_flags.on(Monster_Spell_Flag.BR_WALL.value); } return (true); } /* Monsters with specific breaths resist confusion */ if ((ef_idx == Misc.MON_TMD.CONF) && ((r_ptr.spell_flags.has(Monster_Spell_Flag.BR_CHAO.value)) || (r_ptr.spell_flags.has(Monster_Spell_Flag.BR_CONF.value))) ) { /* Add the lore */ if (m_ptr.ml) { if (r_ptr.spell_flags.has(Monster_Spell_Flag.BR_CHAO.value)) l_ptr.spell_flags.on(Monster_Spell_Flag.BR_CHAO.value); if (r_ptr.spell_flags.has(Monster_Spell_Flag.BR_CONF.value)) l_ptr.spell_flags.on(Monster_Spell_Flag.BR_CONF.value); } return (true); } /* Inertia breathers resist slowing */ if (ef_idx == Misc.MON_TMD.SLOW && r_ptr.spell_flags.has(Monster_Spell_Flag.BR_INER.value)) { l_ptr.spell_flags.on(Monster_Spell_Flag.BR_INER.value); return (true); } /* Calculate the chance of the monster making its saving throw. */ if (ef_idx == (int)Misc.MON_TMD.SLEEP) timer /= 25; /* Hack - sleep uses much bigger numbers */ if ((flag & (int)Misc.MON_TMD_MON_SOURCE) != 0) resist_chance = r_ptr.level; else resist_chance = r_ptr.level + 40 - (timer / 2); if (Random.randint0(100) < resist_chance) return (true); /* Uniques are doubly hard to affect */ if (r_ptr.flags.has(Monster_Flag.UNIQUE.value)) if (Random.randint0(100) < resist_chance) return (true); return (false); }
/** * This is a helper function used by do_cmd_throw and do_cmd_fire. * * It abstracts out the projectile path, display code, identify and clean up * logic, while using the 'attack' parameter to do work particular to each * kind of attack. */ public static void ranged_helper(int item, int dir, int range, int shots, ranged_attack attack) { /* Get the ammo */ Object.Object o_ptr = Object.Object.object_from_item_idx(item); int i, j; ConsoleColor missile_attr = o_ptr.object_attr(); char missile_char = o_ptr.object_char(); //object_type object_type_body; Object.Object i_ptr = new Object.Object(); //&object_type_body; //char o_name[80]; string o_name; int path_n; List <ushort> path_g = new List <ushort>(); //[256]; int msec = Player.Player_Other.instance.delay_factor; /* Start at the player */ int x = Misc.p_ptr.px; int y = Misc.p_ptr.py; /* Predict the "target" location */ short ty = (short)(y + 99 * Misc.ddy[dir]); short tx = (short)(x + 99 * Misc.ddx[dir]); bool hit_target = false; /* Check for target validity */ if ((dir == 5) && Target.okay()) { int taim; //char msg[80]; string msg; Target.get(out tx, out ty); taim = Cave.distance(y, x, ty, tx); if (taim > range) { msg = String.Format("Target out of range by {0} squares. Fire anyway? ", taim - range); if (!Utilities.get_check(msg)) { return; } } } /* Sound */ //sound(MSG_SHOOT); //later o_ptr.notice_on_firing(); /* Describe the object */ o_name = o_ptr.object_desc(Object.Object.Detail.FULL | Object.Object.Detail.SINGULAR); /* Actually "fire" the object -- Take a partial turn */ Misc.p_ptr.energy_use = (short)(100 / shots); /* Calculate the path */ path_n = Cave.project_path(out path_g, range, y, x, ty, tx, 0); /* Hack -- Handle stuff */ Misc.p_ptr.handle_stuff(); /* Start at the player */ x = Misc.p_ptr.px; y = Misc.p_ptr.py; /* Project along the path */ for (i = 0; i < path_n; ++i) { int ny = Cave.GRID_Y(path_g[i]); int nx = Cave.GRID_X(path_g[i]); /* Hack -- Stop before hitting walls */ if (!Cave.cave_floor_bold(ny, nx)) { break; } /* Advance */ x = nx; y = ny; /* Only do visuals if the player can "see" the missile */ if (Cave.player_can_see_bold(y, x)) { Cave.print_rel(missile_char, missile_attr, y, x); Cave.move_cursor_relative(y, x); Term.fresh(); if (Misc.p_ptr.redraw != 0) { Misc.p_ptr.redraw_stuff(); } Term.xtra(TERM_XTRA.DELAY, msec); Cave.cave_light_spot(Cave.cave, y, x); Term.fresh(); if (Misc.p_ptr.redraw != 0) { Misc.p_ptr.redraw_stuff(); } } else { /* Delay anyway for consistency */ Term.xtra(TERM_XTRA.DELAY, msec); } /* Handle monster */ if (Cave.cave.m_idx[y][x] > 0) { break; } } /* Try the attack on the monster at (x, y) if any */ if (Cave.cave.m_idx[y][x] > 0) { Monster.Monster m_ptr = Cave.cave_monster(Cave.cave, Cave.cave.m_idx[y][x]); Monster_Race r_ptr = Misc.r_info[m_ptr.r_idx]; bool visible = m_ptr.ml; bool fear = false; //char m_name[80]; string m_name; string note_dies = r_ptr.monster_is_unusual() ? " is destroyed." : " dies."; attack_result result = attack(o_ptr, y, x); int dmg = result.dmg; Message_Type msg_type = result.msg_type; string hit_verb = result.hit_verb; if (result.success) { hit_target = true; /* Get "the monster" or "it" */ m_name = m_ptr.monster_desc(0); o_ptr.notice_attack_plusses(); /* No negative damage; change verb if no damage done */ if (dmg <= 0) { dmg = 0; hit_verb = "fail to harm"; } if (!visible) { /* Invisible monster */ Utilities.msgt(Message_Type.MSG_SHOOT_HIT, "The {0} finds a mark.", o_name); } else { /* Visible monster */ if ((Message_Type)msg_type == Message_Type.MSG_SHOOT_HIT) { Utilities.msgt(Message_Type.MSG_SHOOT_HIT, "The {0} {1} {2}.", o_name, hit_verb, m_name); } else if ((Message_Type)msg_type == Message_Type.MSG_HIT_GOOD) { Utilities.msgt(Message_Type.MSG_HIT_GOOD, "The {0} {1} {2}. {3}", o_name, hit_verb, m_name, "It was a good hit!"); } else if ((Message_Type)msg_type == Message_Type.MSG_HIT_GREAT) { Utilities.msgt(Message_Type.MSG_HIT_GREAT, "The {0} {1} {2}. {3}", o_name, hit_verb, m_name, "It was a great hit!"); } else if ((Message_Type)msg_type == Message_Type.MSG_HIT_SUPERB) { Utilities.msgt(Message_Type.MSG_HIT_SUPERB, "The {0} {1} {2}. {3}", o_name, hit_verb, m_name, "It was a superb hit!"); } /* Track this monster */ if (m_ptr.ml) { Cave.monster_race_track(m_ptr.r_idx); } if (m_ptr.ml) { Cave.health_track(Misc.p_ptr, Cave.cave.m_idx[y][x]); } } /* Complex message */ if (Misc.p_ptr.wizard) { Utilities.msg("You do {0} (out of {1}) damage.", dmg, m_ptr.hp); } /* Hit the monster, check for death */ if (!Monster_Make.mon_take_hit(Cave.cave.m_idx[y][x], dmg, ref fear, note_dies)) { Monster_Message.message_pain(Cave.cave.m_idx[y][x], dmg); if (fear && m_ptr.ml) { Monster_Message.add_monster_message(m_name, Cave.cave.m_idx[y][x], MON_MSG.FLEE_IN_TERROR, true); } } } } /* Obtain a local object */ i_ptr = o_ptr.copy(); i_ptr.split(o_ptr, 1); /* See if the ammunition broke or not */ j = i_ptr.breakage_chance(hit_target); /* Drop (or break) near that location */ Object.Object.drop_near(Cave.cave, i_ptr, j, y, x, true); if (item >= 0) { /* The ammo is from the inventory */ Object.Object.inven_item_increase(item, -1); Object.Object.inven_item_describe(item); Object.Object.inven_item_optimize(item); } else { /* The ammo is from the floor */ Object.Object.floor_item_increase(0 - item, -1); Object.Object.floor_item_optimize(0 - item); } }
static int take1(Player.Player p, Monster.Monster m, RBM blow, RBE eff) { int oldv, newv; m.Race.blow[0].effect = eff; m.Race.blow[0].method = blow; p.chp = p.mhp; oldv = p.chp; m.make_attack_normal(p); newv = p.chp; p.chp = p.mhp; return oldv - newv; }
/** * Attack the monster at the given location with a single blow. */ static bool py_attack_real(int y, int x, ref bool fear) { /* Information about the target of the attack */ Monster.Monster m_ptr = Cave.cave_monster(Cave.cave, Cave.cave.m_idx[y][x]); Monster_Race r_ptr = Misc.r_info[m_ptr.r_idx]; //char m_name[80]; string m_name; bool stop = false; /* The weapon used */ Object.Object o_ptr = Misc.p_ptr.inventory[Misc.INVEN_WIELD]; /* Information about the attack */ int bonus = Misc.p_ptr.state.to_h + o_ptr.to_h; int chance = Misc.p_ptr.state.skills[(int)Skill.TO_HIT_MELEE] + bonus * Misc.BTH_PLUS_ADJ; bool do_quake = false; bool success = false; /* Default to punching for one damage */ string hit_verb = "punch"; int dmg = 1; Message_Type msg_type = Message_Type.MSG_HIT; /* Extract monster name (or "it") */ m_name = m_ptr.monster_desc(0); /* Auto-Recall if possible and visible */ if (m_ptr.ml) { Cave.monster_race_track(m_ptr.r_idx); } /* Track a new monster */ if (m_ptr.ml) { Cave.health_track(Misc.p_ptr, Cave.cave.m_idx[y][x]); } /* Handle player fear (only for invisible monsters) */ if (Misc.p_ptr.check_state(Object_Flag.AFRAID, Misc.p_ptr.state.flags)) { Utilities.msgt(Message_Type.MSG_AFRAID, "You are too afraid to attack {0}!", m_name); return(false); } /* Disturb the monster */ Monster.Monster.mon_clear_timed(Cave.cave.m_idx[y][x], (int)Misc.MON_TMD.SLEEP, Misc.MON_TMD_FLG_NOMESSAGE, false); /* See if the player hit */ success = test_hit(chance, (int)r_ptr.ac, m_ptr.ml); /* If a miss, skip this hit */ if (!success) { Utilities.msgt(Message_Type.MSG_MISS, "You miss {0}.", m_name); return(false); } /* Handle normal weapon */ if (o_ptr.kind != null) { int i; Slay best_s_ptr = null; hit_verb = "hit"; /* Get the best attack from all slays or * brands on all non-launcher equipment */ for (i = Misc.INVEN_LEFT; i < Misc.INVEN_TOTAL; i++) { Object.Object obj = Misc.p_ptr.inventory[i]; if (obj.kind != null) { Slay.improve_attack_modifier(obj, m_ptr, ref best_s_ptr, true, false); } } Slay.improve_attack_modifier(o_ptr, m_ptr, ref best_s_ptr, true, false); if (best_s_ptr != null) { hit_verb = best_s_ptr.melee_verb; } dmg = Random.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, ref msg_type); /* Learn by use for the weapon */ o_ptr.notice_attack_plusses(); if (Misc.p_ptr.check_state(Object_Flag.IMPACT, Misc.p_ptr.state.flags) && dmg > 50) { do_quake = true; Object.Object.wieldeds_notice_flag(Misc.p_ptr, Object_Flag.IMPACT.value); } } /* Learn by use for other equipped items */ Object.Object.wieldeds_notice_on_attack(); /* Apply the player damage bonuses */ dmg += Misc.p_ptr.state.to_d; /* No negative damage */ if (dmg <= 0) { dmg = 0; } /* Tell the player what happened */ if (dmg <= 0) { Utilities.msgt(Message_Type.MSG_MISS, "You fail to harm {0}.", m_name); } else if (msg_type == Message_Type.MSG_HIT) { Utilities.msgt(Message_Type.MSG_HIT, "You {0} {1}.", hit_verb, m_name); } else if (msg_type == Message_Type.MSG_HIT_GOOD) { Utilities.msgt(Message_Type.MSG_HIT_GOOD, "You {0} {1}. {2}", hit_verb, m_name, "It was a good hit!"); } else if (msg_type == Message_Type.MSG_HIT_GREAT) { Utilities.msgt(Message_Type.MSG_HIT_GREAT, "You {0} {1}. {2}", hit_verb, m_name, "It was a great hit!"); } else if (msg_type == Message_Type.MSG_HIT_SUPERB) { Utilities.msgt(Message_Type.MSG_HIT_SUPERB, "You {0} {1}. {2}", hit_verb, m_name, "It was a superb hit!"); } else if (msg_type == Message_Type.MSG_HIT_HI_GREAT) { Utilities.msgt(Message_Type.MSG_HIT_HI_GREAT, "You {0} {1}. {2}", hit_verb, m_name, "It was a *GREAT* hit!"); } else if (msg_type == Message_Type.MSG_HIT_HI_SUPERB) { Utilities.msgt(Message_Type.MSG_HIT_HI_SUPERB, "You {0} {1}. {2}", hit_verb, m_name, "It was a *SUPERB* hit!"); } /* Complex message */ if (Misc.p_ptr.wizard) { Utilities.msg("You do {0} (out of {1}) damage.", dmg, m_ptr.hp); } /* Confusion attack */ if (Misc.p_ptr.confusing != 0) { Misc.p_ptr.confusing = 0; //false; Utilities.msg("Your hands stop glowing."); Monster.Monster.mon_inc_timed(Cave.cave.m_idx[y][x], Misc.MON_TMD.CONF, (10 + Random.randint0(Misc.p_ptr.lev) / 10), Misc.MON_TMD_FLG_NOTIFY, false); } /* Damage, check for fear and death */ stop = Monster.Monster_Make.mon_take_hit(Cave.cave.m_idx[y][x], dmg, ref fear, null); if (stop) { fear = false; } /* Apply earthquake brand */ if (do_quake) { throw new NotImplementedException(); //earthquake(Misc.p_ptr.py, Misc.p_ptr.px, 10); //if (Cave.cave.m_idx[y][x] == 0) stop = true; } return(stop); }
/** * 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; } } }