/** * 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); } }
/* * Dump a message describing a monster's reaction to damage */ public static void message_pain(int m_idx, int dam) { long oldhp, newhp, tmp; int percentage; Monster m_ptr = Cave.cave_monster(Cave.cave, m_idx); MON_MSG msg_code = MON_MSG.UNHARMED; //char m_name[80]; string m_name; /* Get the monster name */ m_name = m_ptr.monster_desc(0); /* Notice non-damage */ if (dam == 0) { add_monster_message(m_name, m_idx, msg_code, false); return; } /* Note -- subtle fix -CFT */ newhp = (long)(m_ptr.hp); oldhp = newhp + (long)(dam); tmp = (newhp * 100L) / oldhp; percentage = (int)(tmp); if (percentage > 95) { msg_code = MON_MSG.MON_MSG_95; } else if (percentage > 75) { msg_code = MON_MSG.MON_MSG_75; } else if (percentage > 50) { msg_code = MON_MSG.MON_MSG_50; } else if (percentage > 35) { msg_code = MON_MSG.MON_MSG_35; } else if (percentage > 20) { msg_code = MON_MSG.MON_MSG_20; } else if (percentage > 10) { msg_code = MON_MSG.MON_MSG_10; } else { msg_code = MON_MSG.MON_MSG_0; } add_monster_message(m_name, m_idx, msg_code, false); }
/** * 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); }
/** * 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); } }
/** * 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; }
/** * 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); }