/** * 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); } }
/* * Let the floor carry an object, deleting old squelched items if necessary */ public static short floor_carry(Cave c, int y, int x, Object j_ptr) { int n = 0; short o_idx; short this_o_idx, next_o_idx = 0; /* Scan objects in that grid for combination */ for (this_o_idx = c.o_idx[y][x]; this_o_idx != 0; this_o_idx = next_o_idx) { Object o_ptr = byid(this_o_idx); if(o_ptr == null) continue; /* Get the next object */ next_o_idx = o_ptr.next_o_idx; /* Check for combination */ if (o_ptr.similar(j_ptr, object_stack_t.OSTACK_FLOOR)) { /* Combine the items */ o_ptr.absorb(j_ptr); /* Result */ return (this_o_idx); } /* Count objects */ n++; } /* Option -- disallow stacking */ if (Option.birth_no_stacking.value && n != 0) return (0); /* The stack is already too large */ if (n >= Misc.MAX_FLOOR_STACK) { throw new NotImplementedException(); ///* Squelch the oldest squelched object */ //short squelch_idx = floor_get_idx_oldest_squelched(y, x); //if (squelch_idx) // delete_object_idx(squelch_idx); //else // return 0; } /* Make an object */ o_idx = o_pop(); /* Success */ if (o_idx != 0) { Object o_ptr; /* Get the object */ o_ptr = byid(o_idx); /* Structure Copy */ o_ptr = j_ptr.copy(); /* Location */ o_ptr.iy = (byte)y; o_ptr.ix = (byte)x; /* Forget monster */ o_ptr.held_m_idx = 0; /* Link the object to the pile */ o_ptr.next_o_idx = c.o_idx[y][x]; /* Link the floor to the object */ c.o_idx[y][x] = o_idx; Cave.cave_note_spot(c, y, x); Cave.cave_light_spot(c, y, x); } /* Result */ return (o_idx); }
/* * Wield or wear a single item from the pack or floor */ public void wield_item(int item, int slot) { //If wierd things happen, add o_ptr back as a parameter instead of this //Object object_type_body; Object i_ptr = new Object();//&object_type_body; string fmt; //char o_name[80]; string o_name; bool combined_ammo = false; bool track_wielded_item = false; int num = 1; /* If we are stacking ammo in the quiver */ if (is_ammo()) { num = number; combined_ammo = similar(Misc.p_ptr.inventory[slot], object_stack_t.OSTACK_QUIVER); } /* Take a turn */ Misc.p_ptr.energy_use = 100; /* Obtain local object */ i_ptr = copy(); /* Modify quantity */ i_ptr.number = (byte)num; /* Update object_idx if necessary, once object is in slot */ if (Cave.tracked_object_is(item)) { track_wielded_item = true; } /* Decrease the item (from the pack) */ if (item >= 0) { inven_item_increase(item, -num); inven_item_optimize(item); } /* Decrease the item (from the floor) */ else { floor_item_increase(0 - item, -num); floor_item_optimize(0 - item); } /* Get the wield slot */ //Things might get wonky here. We stop using "this" and start using "o_ptr" form here on. Object o_ptr = Misc.p_ptr.inventory[slot]; if (combined_ammo) { /* Add the new ammo to the already-quiver-ed ammo */ o_ptr.absorb(i_ptr); } else { /* Take off existing item */ if (o_ptr.kind != null) inven_takeoff(slot, 255); /* If we are wielding ammo we may need to "open" the slot by shifting * later ammo up the quiver; this is because we already called the * inven_item_optimize() function. */ if (slot >= Misc.QUIVER_START) open_quiver_slot(slot); /* Wear the new stuff */ o_ptr = i_ptr.copy(); /* Increment the equip counter by hand */ Misc.p_ptr.equip_cnt++; } /* Increase the weight */ Misc.p_ptr.total_weight += i_ptr.weight * num; /* Track object if necessary */ if (track_wielded_item) { Cave.track_object(slot); } /* Do any ID-on-wield */ o_ptr.notice_on_wield(); /* Where is the item now */ if (slot == Misc.INVEN_WIELD) fmt = "You are wielding {0} ({1})."; else if (slot == Misc.INVEN_BOW) fmt = "You are shooting with {0} ({1})."; else if (slot == Misc.INVEN_LIGHT) fmt = "Your light source is {0} ({1})."; else if (combined_ammo) fmt = "You combine {0} in your quiver ({1})."; else if (slot >= Misc.QUIVER_START && slot < Misc.QUIVER_END) fmt = "You add {0} to your quiver ({1})."; else fmt = "You are wearing {0} ({1})."; /* Describe the result */ o_name = o_ptr.object_desc(Detail.PREFIX | Detail.FULL); /* Message */ Utilities.msgt(Message_Type.MSG_WIELD, fmt, o_name, index_to_label(slot)); /* Cursed! */ if (Object_Flag.cursed_p(o_ptr.flags)) { /* Warn the player */ Utilities.msgt(Message_Type.MSG_CURSED, "Oops! It feels deathly cold!"); /* Sense the object */ o_ptr.notice_curses(); } /* Save quiver size */ save_quiver_size(Misc.p_ptr); /* See if we have to overflow the pack */ pack_overflow(); /* Recalculate bonuses, torch, mana */ Misc.p_ptr.notice |= Misc.PN_SORT_QUIVER; Misc.p_ptr.update |= (Misc.PU_BONUS | Misc.PU_TORCH | Misc.PU_MANA); Misc.p_ptr.redraw |= (Misc.PR_INVEN | Misc.PR_EQUIP); }
/* * Prepare an object `dst` representing `amt` objects, based on an existing * object `src` representing at least `amt` objects. * * Takes care of the charge redistribution concerns of stacked items. */ public static void copy_amt(ref Object dst, Object src, int amt) { //this is the dest int charge_time = Random.randcalc(src.kind.time, 0, aspect.AVERAGE), max_time; /* Get a copy of the object */ dst = src.copy(); /* Modify quantity */ dst.number = (byte)amt; dst.note = src.note; /* * If the item has charges/timeouts, set them to the correct level * too. We split off the same amount as distribute_charges. */ if (src.tval == TVal.TV_WAND || src.tval == TVal.TV_STAFF) { dst.pval[Misc.DEFAULT_PVAL] = (short)(src.pval[Misc.DEFAULT_PVAL] * amt / src.number); } if (src.tval == TVal.TV_ROD) { max_time = charge_time * amt; if (src.timeout > max_time) dst.timeout = (short)max_time; else dst.timeout = src.timeout; } }