bool Creature_tracker::update_pos(const monster &critter, const int new_x_pos, const int new_y_pos)
{
    if (critter.posx() == new_x_pos && critter.posy() == new_y_pos) {
        return true; // success?
    }
    bool success = false;
    const int dead_critter_id = dead_mon_at(point(critter.posx(), critter.posy()));
    const int live_critter_id = mon_at(point(critter.posx(), critter.posy()));
    const int critter_id = critter.dead ? dead_critter_id : live_critter_id;
    const int new_critter_id = mon_at(new_x_pos, new_y_pos);
    if (new_critter_id >= 0 && !_old_monsters_list[new_critter_id]->dead) {
        debugmsg("update_zombie_pos: new location %d,%d already has zombie %d",
                 new_x_pos, new_y_pos, new_critter_id);
    } else if (critter_id >= 0) {
        if (&critter == _old_monsters_list[critter_id]) {
            _old_monsters_by_location.erase(point(critter.posx(), critter.posy()));
            _old_monsters_by_location[point(new_x_pos, new_y_pos)] = critter_id;
            success = true;
        } else {
            debugmsg("update_zombie_pos: old location %d,%d had zombie %d instead",
                     critter.posx(), critter.posy(), critter_id);
        }
    } else {
        // We're changing the x/y coordinates of a zombie that hasn't been added
        // to the game yet. add_zombie() will update _old_monsters_by_location for us.
        debugmsg("update_zombie_pos: no such zombie at %d,%d (moving to %d,%d)",
                 critter.posx(), critter.posy(), new_x_pos, new_y_pos);
    }
    return success;
}
bool Creature_tracker::update_pos( const monster &critter, const tripoint &new_pos )
{
    const auto old_pos = critter.pos();
    if( critter.is_dead() ) {
        // mon_at ignores dead critters anyway, changing their position in the
        // monsters_by_location map is useless.
        remove_from_location_map( critter );
        return true;
    }

    const int critter_id = mon_at( old_pos );
    const int new_critter_id = mon_at( new_pos );
    if( new_critter_id >= 0 ) {
        auto &othermon = *monsters_list[new_critter_id];
        if( othermon.is_hallucination() ) {
            othermon.die( nullptr );
        } else {
            debugmsg( "update_zombie_pos: wanted to move %s to %d,%d,%d, but new location already has %s",
                      critter.disp_name().c_str(),
                      new_pos.x, new_pos.y, new_pos.z, othermon.disp_name().c_str() );
            return false;
        }
    }

    if( critter_id >= 0 ) {
        if( &critter == monsters_list[critter_id] ) {
            monsters_by_location.erase( old_pos );
            monsters_by_location[new_pos] = critter_id;
            return true;
        } else {
            const auto &othermon = *monsters_list[critter_id];
            debugmsg( "update_zombie_pos: wanted to move %s from old location %d,%d,%d, but it had %s instead",
                      critter.disp_name().c_str(),
                      old_pos.x, old_pos.y, old_pos.z, othermon.disp_name().c_str() );
            return false;
        }
    } else {
        // We're changing the x/y/z coordinates of a zombie that hasn't been added
        // to the game yet. add_zombie() will update monsters_by_location for us.
        debugmsg( "update_zombie_pos: no %s at %d,%d,%d (moving to %d,%d,%d)",
                  critter.disp_name().c_str(),
                  old_pos.x, old_pos.y, old_pos.z, new_pos.x, new_pos.y, new_pos.z );
        // Rebuild cache in case the monster actually IS in the game, just bugged
        rebuild_cache();
        return false;
    }

    return false;
}
Esempio n. 3
0
void game::draw_line(const int x, const int y, const point center_point, std::vector<point> ret)
{
    if (u_see( x, y))
    {
        for (int i = 0; i < ret.size(); i++)
        {
            int mondex = mon_at(ret[i].x, ret[i].y),
            npcdex = npc_at(ret[i].x, ret[i].y);

            // NPCs and monsters get drawn with inverted colors
            if (mondex != -1 && u_see(&(_active_monsters[mondex])))
            {
                _active_monsters[mondex].draw(w_terrain, center_point.x, center_point.y, true);
            }
            else if (npcdex != -1)
            {
                active_npc[npcdex]->draw(w_terrain, center_point.x, center_point.y, true);
            }
            else
            {
                m.drawsq(w_terrain, u, ret[i].x, ret[i].y, true,true,center_point.x, center_point.y);
            }
        }
    }
}
bool Creature_tracker::add( monster &critter )
{
    if( critter.type->id.is_null() ) { // Don't wanna spawn null monsters o.O
        return false;
    }

    if( critter.type->has_flag( MF_VERMIN ) ) {
        // Don't spawn vermin, they aren't implemented yet
        return false;
    }

    const int critter_id = mon_at( critter.pos() );
    if( critter_id != -1 ) {
        // We can spawn stuff on hallucinations, but we need to kill them first
        if( monsters_list[critter_id]->is_hallucination() ) {
            monsters_list[critter_id]->die( nullptr );
            // But don't remove - that would change the monster order and could segfault
        } else if( critter.is_hallucination() ) {
            return false;
        } else {
            debugmsg( "add_zombie: there's already a monster at %d,%d,%d",
                      critter.posx(), critter.posy(), critter.posz() );
            return false;
        }
    }

    if( MonsterGroupManager::monster_is_blacklisted( critter.type->id ) ) {
        return false;
    }

    monsters_by_location[critter.pos()] = monsters_list.size();
    monsters_list.push_back( new monster( critter ) );
    return true;
}
void game::draw_line(const int x, const int y, const point center_point, std::vector<point> ret)
{
    if (u_see( x, y))
    {
        for (size_t i = 0; i < ret.size(); i++)
        {
            int mondex = mon_at(ret[i].x, ret[i].y),
            npcdex = npc_at(ret[i].x, ret[i].y);

            // NPCs and monsters get drawn with inverted colors
            if (mondex != -1 && u_see(&(critter_tracker.find(mondex))))
            {
                critter_tracker.find(mondex).draw(w_terrain, center_point.x, center_point.y, true);
            }
            else if (npcdex != -1)
            {
                active_npc[npcdex]->draw(w_terrain, center_point.x, center_point.y, true);
            }
            else
            {
                m.drawsq(w_terrain, u, ret[i].x, ret[i].y, true,true,center_point.x, center_point.y);
            }
        }
    }
    tilecontext->init_draw_line(x,y,ret,"line_target", true);
}
Esempio n. 6
0
static Boolean teleok(Short x, Short y)
{	/* might throw him into a POOL */
  return( !OUT_OF_BOUNDS(x,y) &&
	  !IS_ROCK(get_cell_type(floor_info[x][y])) &&
	  !mon_at(x,y) &&
	  !sobj_at(ENORMOUS_ROCK,x,y) &&
	  !trap_at(x,y)
	  );
  /* Note: gold is permitted (because of vaults) */
}
bool Creature_tracker::add(monster &critter)
{
    if (critter.type->id == "mon_null") { // Don't wanna spawn null monsters o.O
        return false;
    }
    if (-1 != mon_at(critter.pos())) {
        debugmsg("add_zombie: there's already a monster at %d,%d", critter.posx(), critter.posy());
        return false;
    }
    _old_monsters_by_location[point(critter.posx(), critter.posy())] = _old_monsters_list.size();
    _old_monsters_list.push_back(new monster(critter));
    return true;
}
void Creature_tracker::swap_positions( monster &first, monster &second )
{
    const int first_mdex = mon_at( first.pos() );
    const int second_mdex = mon_at( second.pos() );
    remove_from_location_map( first );
    remove_from_location_map( second );
    bool ok = true;
    if( first_mdex == -1 || second_mdex == -1 || first_mdex == second_mdex ) {
        debugmsg( "Tried to swap monsters with invalid positions" );
        ok = false;
    }

    tripoint temp = second.pos();
    second.spawn( first.pos() );
    first.spawn( temp );
    if( ok ) {
        monsters_by_location[first.pos()] = first_mdex;
        monsters_by_location[second.pos()] = second_mdex;
    } else {
        // Try to avoid spamming error messages if something weird happens
        rebuild_cache();
    }
}
Esempio n. 9
0
// ok, the SDOOR/SCORR part works.  however, none of the rest is tested
// (traps, mimics, swallowed, etc)
void do_search() // was dosearch
{
  Int8 x,y;
  trap_t *trap;
  monst_t *mtmp;
  UChar floor_type, tmp_type;

  if (you.uswallow) {
    message("What are you looking for? The exit?");
    return;
  }
  for (x = you.ux - 1 ; x <= you.ux + 1 ; x++)
    for (y = you.uy - 1 ; y <= you.uy + 1 ; y++) {
      if (x == you.ux && y == you.uy) continue;
      floor_type = get_cell_type(floor_info[x][y]);
      if (floor_type == SDOOR || floor_type == SCORR) {
	if (rund(7)) continue;
	tmp_type = (floor_type == SDOOR) ? DOOR : CORR;
	set_cell_type(floor_info[x][y], tmp_type);
	floor_info[x][y] &= ~SEEN_CELL;	/* force prl */
	prl(x,y);
	nomul(0);
      } else {
	/* Be careful not to find anything in an SCORR or SDOOR */
	mtmp = mon_at(x,y);
	if (mtmp && (mtmp->bitflags & M_IS_MIMIC)) {
	  see_mimic(mtmp);
	  message("You find a mimic.");
	  return;
	}
	for (trap = ftrap ; trap ; trap = trap->ntrap)
	  if (trap->tx == x && trap->ty == y &&
	      !(get_trap_seen(trap->trap_info)) && !rund(8)) {
	    nomul(0);
	    tmp_type = get_trap_type(trap->trap_info);
	    StrPrintF(ScratchBuffer, "You find a%s.", traps[tmp_type]);
	    message(ScratchBuffer);
	    if (tmp_type == PIERC) {
	      deltrap(trap);
	      makemon(PM_PIERCER, x, y);
	      return;
	    }
	    trap->trap_info |= SEEN_TRAP;
	    if (!vism_at(x,y))
	      print(x, y, '^');
	  }
      }
    }
}
bool Creature_tracker::update_pos(const monster &critter, const tripoint &new_pos)
{
    const auto old_pos = critter.pos3();
    if( critter.is_dead() ) {
        // mon_at ignores dead critters anyway, changing their position in the
        // monsters_by_location map is useless.
        remove_from_location_map( critter );
        return true;
    }

    bool success = false;
    const int critter_id = mon_at( old_pos );
    const int new_critter_id = mon_at( new_pos );
    if( new_critter_id >= 0 ) {
        debugmsg("update_zombie_pos: new location %d,%d,%d already has zombie %d",
                 new_pos.x, new_pos.y, new_pos.z, new_critter_id);
    } else if( critter_id >= 0 ) {
        if( &critter == monsters_list[critter_id] ) {
            monsters_by_location.erase( old_pos );
            monsters_by_location[new_pos] = critter_id;
            success = true;
        } else {
            debugmsg("update_zombie_pos: old location %d,%d had zombie %d instead",
                     old_pos.x, old_pos.y, critter_id);
        }
    } else {
        // We're changing the x/y/z coordinates of a zombie that hasn't been added
        // to the game yet. add_zombie() will update monsters_by_location for us.
        debugmsg("update_zombie_pos: no such zombie at %d,%d,%d (moving to %d,%d,%d)",
                 old_pos.x, old_pos.y, old_pos.z, new_pos.x, new_pos.y, new_pos.z );
        // Rebuild cache in case the monster actually IS in the game, just bugged
        rebuild_cache();
    }

    return success;
}
Esempio n. 11
0
Short findit()   /* returns number of things found */
{
  Short num;
  UChar zx,zy;
  trap_t *ttmp;
  monst_t *mtmp;
  UChar lx,hx,ly,hy;

  if (you.uswallow) return false;
  for (lx = you.ux;
       (num = get_cell_type(floor_info[lx-1][you.uy])) && num != CORR; lx--) ;
  for (hx = you.ux;
       (num = get_cell_type(floor_info[hx+1][you.uy])) && num != CORR; hx++) ;
  for (ly = you.uy;
       (num = get_cell_type(floor_info[you.ux][ly-1])) && num != CORR; ly--) ;
  for (hy = you.uy;
       (num = get_cell_type(floor_info[you.ux][hy+1])) && num != CORR; hy++) ;
  num = 0;
  for (zy = ly; zy <= hy; zy++) {
    for (zx = lx; zx <= hx; zx++) {
      if (get_cell_type(floor_info[zx][zy]) == SDOOR) {
	set_cell_type(floor_info[zx][zy], DOOR);
	print(zx, zy, DOOR_SYM);
	num++;
      } else if (get_cell_type(floor_info[zx][zy]) == SCORR) {
	set_cell_type(floor_info[zx][zy], CORR);
	print(zx, zy, CORR_SYM);
	num++;
      } else if ((ttmp = trap_at(zx, zy))) {
	if (get_trap_type(ttmp->trap_info) == PIERC) {
	  makemon(PM_PIERCER, zx, zy);
	  num++;
	  deltrap(ttmp);
	} else if (!get_trap_seen(ttmp->trap_info)) {
	  ttmp->trap_info |= SEEN_TRAP;
	  if (!vism_at(zx, zy))
	    print(zx,zy,'^');
	  num++;
	}
      } else if ((mtmp = mon_at(zx,zy)) && (mtmp->bitflags & M_IS_MIMIC)) {
	see_mimic(mtmp);
	num++;
      }
    }
  }
  return num;
}
Esempio n. 12
0
// TODO: Shunt redundant drawing code elsewhere
std::vector<point> game::target(int &x, int &y, int lowx, int lowy, int hix,
                                int hiy, std::vector <monster> t, int &target,
                                item *relevent)
{
 std::vector<point> ret;
 int tarx, tary, junk, tart;
 int range=(hix-u.posx);
// First, decide on a target among the monsters, if there are any in range
 if (!t.empty()) {
// Check for previous target
  if (target == -1) {
// If no previous target, target the closest there is
   double closest = -1;
   double dist;
   for (int i = 0; i < t.size(); i++) {
    dist = rl_dist(t[i].posx(), t[i].posy(), u.posx, u.posy);
    if (closest < 0 || dist < closest) {
     closest = dist;
     target = i;
    }
   }
  }
  x = t[target].posx();
  y = t[target].posy();
 } else
  target = -1; // No monsters in range, don't use target, reset to -1

 bool sideStyle = use_narrow_sidebar();
 int height = 13;
 int width  = getmaxx(w_messages);
 int top    = sideStyle ? getbegy(w_messages) : (getbegy(w_minimap) + getmaxy(w_minimap));
 int left   = getbegx(w_messages);
 WINDOW* w_target = newwin(height, width, top, left);
 draw_border(w_target);
 mvwprintz(w_target, 0, 2, c_white, "< ");
 if (!relevent) { // currently targetting vehicle to refill with fuel
   wprintz(w_target, c_red, _("Select a vehicle"));
 } else {
   if (relevent == &u.weapon && relevent->is_gun()) {
     wprintz(w_target, c_red, _("Firing %s (%d)"), // - %s (%d)",
            u.weapon.tname().c_str(),// u.weapon.curammo->name.c_str(),
            u.weapon.charges);
   } else {
     wprintz(w_target, c_red, _("Throwing %s"), relevent->tname().c_str());
   }
 }
 wprintz(w_target, c_white, " >");
/* Annoying clutter @ 2 3 4. */
 int text_y = getmaxy(w_target) - 4;
 if (is_mouse_enabled()) {
     --text_y;
 }
 mvwprintz(w_target, text_y++, 1, c_white,
           _("Move cursor to target with directional keys"));
 if (relevent) {
  mvwprintz(w_target, text_y++, 1, c_white,
            _("'<' '>' Cycle targets; 'f' or '.' to fire"));
  mvwprintz(w_target, text_y++, 1, c_white,
            _("'0' target self; '*' toggle snap-to-target"));
 }

 if (is_mouse_enabled()) {
     mvwprintz(w_target, text_y++, 1, c_white,
         _("Mouse: LMB: Target, Wheel: Cycle, RMB: Fire"));
 }

 wrefresh(w_target);
 bool snap_to_target = OPTIONS["SNAP_TO_TARGET"];
 do {
  if (m.sees(u.posx, u.posy, x, y, -1, tart))
    ret = line_to(u.posx, u.posy, x, y, tart);
  else
    ret = line_to(u.posx, u.posy, x, y, 0);

  if(trigdist && trig_dist(u.posx,u.posy, x,y) > range) {
    bool cont=true;
    int cx=x;
    int cy=y;
    for (int i = 0; i < ret.size() && cont; i++) {
      if(trig_dist(u.posx,u.posy, ret[i].x, ret[i].y) > range) {
        ret.resize(i);
        cont=false;
      } else {
        cx=0+ret[i].x; cy=0+ret[i].y;
      }
    }
    x=cx;y=cy;
  }
  point center;
  if (snap_to_target)
   center = point(x, y);
  else
   center = point(u.posx + u.view_offset_x, u.posy + u.view_offset_y);
  // Clear the target window.
  for (int i = 1; i < getmaxy(w_target) - 5; i++) {
   for (int j = 1; j < getmaxx(w_target) - 2; j++)
    mvwputch(w_target, i, j, c_white, ' ');
  }
  /* Start drawing w_terrain things -- possibly move out to centralized draw_terrain_window function as they all should be roughly similar*/
  m.build_map_cache(); // part of the SDLTILES drawing code
  m.draw(w_terrain, center); // embedded in SDL drawing code
  // Draw the Monsters
  for (int i = 0; i < num_zombies(); i++) {
   if (u_see(&(zombie(i)))) {
    zombie(i).draw(w_terrain, center.x, center.y, false);
   }
  }
  // Draw the NPCs
  for (int i = 0; i < active_npc.size(); i++) {
   if (u_see(active_npc[i]->posx, active_npc[i]->posy))
    active_npc[i]->draw(w_terrain, center.x, center.y, false);
  }
  if (x != u.posx || y != u.posy) {

   // Draw the player
   int atx = POSX + u.posx - center.x, aty = POSY + u.posy - center.y;
   if (atx >= 0 && atx < TERRAIN_WINDOW_WIDTH && aty >= 0 && aty < TERRAIN_WINDOW_HEIGHT)
    mvwputch(w_terrain, aty, atx, u.color(), '@');

   // Only draw a highlighted trajectory if we can see the endpoint.
   // Provides feedback to the player, and avoids leaking information about tiles they can't see.
   draw_line(x, y, center, ret);
/*
   if (u_see( x, y)) {
    for (int i = 0; i < ret.size(); i++) {
      int mondex = mon_at(ret[i].x, ret[i].y),
          npcdex = npc_at(ret[i].x, ret[i].y);
      // NPCs and monsters get drawn with inverted colors
      if (mondex != -1 && u_see(&(zombie(mondex))))
       zombie(mondex).draw(w_terrain, center.x, center.y, true);
      else if (npcdex != -1)
       active_npc[npcdex]->draw(w_terrain, center.x, center.y, true);
      else
       m.drawsq(w_terrain, u, ret[i].x, ret[i].y, true,true,center.x, center.y);
    }
   }
//*/
            // Print to target window
            if (!relevent) {
                // currently targetting vehicle to refill with fuel
                vehicle *veh = m.veh_at(x, y);
                if (veh) {
                    mvwprintw(w_target, 1, 1, _("There is a %s"),
                              veh->name.c_str());
                }
            } else if (relevent == &u.weapon && relevent->is_gun()) {
                // firing a gun
                mvwprintw(w_target, 1, 1, _("Range: %d"),
                          rl_dist(u.posx, u.posy, x, y));
                // get the current weapon mode or mods
                std::string mode = "";
                if (u.weapon.mode == "MODE_BURST") {
                    mode = _("Burst");
                } else {
                    item* gunmod = u.weapon.active_gunmod();
                    if (gunmod != NULL) {
                        mode = gunmod->type->name;
                    }
                }
                if (mode != "") {
                    mvwprintw(w_target, 1, 14, _("Firing mode: %s"),
                              mode.c_str());
                }
            } else {
                // throwing something
                mvwprintw(w_target, 1, 1, _("Range: %d"),
                          rl_dist(u.posx, u.posy, x, y));
            }

   const int zid = mon_at(x, y);
   if (zid == -1) {
    if (snap_to_target)
     mvwputch(w_terrain, POSY, POSX, c_red, '*');
    else
     mvwputch(w_terrain, POSY + y - center.y, POSX + x - center.x, c_red, '*');
   } else {
    if (u_see(&(zombie(zid)))) {
     zombie(zid).print_info(w_target,2);
    }
   }
  }
  wrefresh(w_target);
  wrefresh(w_terrain);
  wrefresh(w_status);
  refresh();

  input_context ctxt("TARGET");
  // "ANY_INPUT" should be added before any real help strings
  // Or strings will be writen on window border.
  ctxt.register_action("ANY_INPUT");
  ctxt.register_directions();
  ctxt.register_action("COORDINATE");
  ctxt.register_action("SELECT");
  ctxt.register_action("FIRE");
  ctxt.register_action("NEXT_TARGET");
  ctxt.register_action("PREV_TARGET");
  ctxt.register_action("WAIT");
  ctxt.register_action("CENTER");
  ctxt.register_action("TOGGLE_SNAP_TO_TARGET");
  ctxt.register_action("HELP_KEYBINDINGS");
  ctxt.register_action("QUIT");

  const std::string& action = ctxt.handle_input();


  tarx = 0; tary = 0;
  // Our coordinates will either be determined by coordinate input(mouse),
  // by a direction key, or by the previous value.
  if (action == "SELECT" && ctxt.get_coordinates(g->w_terrain, tarx, tary)) {
      if (!OPTIONS["USE_TILES"] && snap_to_target) {
          // Snap to target doesn't currently work with tiles.
          tarx += x - u.posx;
          tary += y - u.posy;
      }
      tarx -= x;
      tary -= y;


  } else {
    ctxt.get_direction(tarx, tary, action);
    if(tarx == -2) {
        tarx = 0;
        tary = 0;
    }
  }

  /* More drawing to terrain */
  if (tarx != 0 || tary != 0) {
   int mondex = mon_at(x, y), npcdex = npc_at(x, y);
   if (mondex != -1 && u_see(&(zombie(mondex))))
    zombie(mondex).draw(w_terrain, center.x, center.y, false);
   else if (npcdex != -1)
    active_npc[npcdex]->draw(w_terrain, center.x, center.y, false);
   else if (m.sees(u.posx, u.posy, x, y, -1, junk))
    m.drawsq(w_terrain, u, x, y, false, true, center.x, center.y);
   else
    mvwputch(w_terrain, POSY, POSX, c_black, 'X');
   x += tarx;
   y += tary;
   if (x < lowx)
    x = lowx;
   else if (x > hix)
    x = hix;
   if (y < lowy)
    y = lowy;
   else if (y > hiy)
    y = hiy;
  } else if ((action == "PREV_TARGET") && (target != -1)) {
   target--;
   if (target == -1) target = t.size() - 1;
   x = t[target].posx();
   y = t[target].posy();
  } else if ((action == "NEXT_TARGET") && (target != -1)) {
   target++;
   if (target == t.size()) target = 0;
   x = t[target].posx();
   y = t[target].posy();
  } else if (action == "WAIT" || action == "FIRE") {
   for (int i = 0; i < t.size(); i++) {
    if (t[i].posx() == x && t[i].posy() == y)
     target = i;
   }
   if (u.posx == x && u.posy == y)
       ret.clear();
   break;
  } else if (action == "CENTER") {
   x = u.posx;
   y = u.posy;
   ret.clear();
  } else if (action == "TOGGLE_SNAP_TO_TARGET")
   snap_to_target = !snap_to_target;
  else if (action == "QUIT") { // return empty vector (cancel)
   ret.clear();
   break;
  }
 } while (true);

 return ret;
}
Esempio n. 13
0
/*
 * shk_move: return 1: he moved  0: he didnt  -1: let m_move do it
 * (what about "return 2" ???
 */
Short shk_move(monst_t *shkp)
{
  monst_t *mtmp;
  permonst_t *mdat = shkp->data;
  UChar gx,gy,omx,omy,nx,ny,nix,niy;
  Int8 appr,i;
  Short udist;
  Short z;
  Int8 shkroom,chi,chcnt,cnt;
  Boolean uondoor=false, satdoor, avoid=false, badinv;
  coord poss[9];
  Short info[9];
  obj_t *ib = NULL;

  omx = shkp->mx;
  omy = shkp->my;

  if ((udist = dist(omx,omy)) < 3) {
    if (ANGRY(shkp)) {
      hit_you(shkp, dice(mdat->damn, mdat->damd)+1);
      return 0;
    }
    if (ESHK(shkp)->following) {
      if (StrNCompare(ESHK(shkp)->customer, plname, PL_NSIZ)) {
	StrPrintF(ScratchBuffer, "Hello %s! I was looking for %s.",
		  plname, ESHK(shkp)->customer);
	message(ScratchBuffer);
	ESHK(shkp)->following = false;
	return 0;
      }
      if (!ESHK(shkp)->robbed) {	/* impossible? */
	ESHK(shkp)->following = false;
	return 0;
      }
      if (moves > followmsg+4) {
	StrPrintF(ScratchBuffer, "Hello %s! Didn't you forget to pay?",
		  plname);
	message(ScratchBuffer);
	followmsg = moves;
      }
      if (udist < 2)
	return 0;
    }
  }

  shkroom = inroom(omx,omy);
  appr = 1;
  gx = ESHK(shkp)->shk.x;
  gy = ESHK(shkp)->shk.y;
  satdoor = (gx == omx && gy == omy);
  if (ESHK(shkp)->following || ((z = holetime()) >= 0 && z*z <= udist)){
    gx = you.ux;
    gy = you.uy;
    if (shkroom < 0 || shkroom != inroom(you.ux,you.uy))
      if (udist > 4)
	return -1;	/* leave it to m_move */
  } else if (ANGRY(shkp)) {
    Long saveBlind = Blind;
    Blind = 0;
    if ((shkp->mcansee_and_blinded & M_CAN_SEE) && !Invis && cansee(omx,omy)) {
      gx = you.ux;
      gy = you.uy;
    }
    Blind = saveBlind;
    avoid = false;
  } else {
#define	GDIST(x,y)	((x-gx)*(x-gx)+(y-gy)*(y-gy))
    if (Invis)
      avoid = false;
    else {
      uondoor = (you.ux == ESHK(shkp)->shd.x &&
		 you.uy == ESHK(shkp)->shd.y);
      if (uondoor) {
	if (ESHK(shkp)->billct) {
	  StrPrintF(ScratchBuffer,
		    "Hello %s! Will you please pay before leaving?", plname);
	  message(ScratchBuffer);
	}
	badinv = (carrying(PICK_AXE) || carrying(ICE_BOX));
	if (satdoor && badinv)
	  return 0;
	avoid = !badinv;
      } else {
	avoid = (you.uinshop && dist(gx,gy) > 8);
	badinv = false;
      }

      if (((!ESHK(shkp)->robbed && !ESHK(shkp)->billct) || avoid)
	  && GDIST(omx,omy) < 3) {
	if (!badinv && !online(omx,omy))
	  return 0;
	if (satdoor)
	  appr = gx = gy = 0;
      }
    }
  }
  if (omx == gx && omy == gy)
    return 0;
  if (shkp->bitflags & M_IS_CONFUSED) {
    avoid = false;
    appr = 0;
  }
  nix = omx;
  niy = omy;
  cnt = mfindpos(shkp,poss,info,ALLOW_SSM);
  if (avoid && uondoor) {		/* perhaps we cannot avoid him */
    for (i=0; i<cnt; i++)
      if (!(info[i] & NOTONL)) goto notonl_ok;
    avoid = false;
  notonl_ok:
    ;
  }
  chi = -1;
  chcnt = 0;
  for (i = 0 ; i < cnt ; i++) {
    nx = poss[i].x;
    ny = poss[i].y;
    if (get_cell_type(floor_info[nx][ny]) == ROOM
	|| shkroom != ESHK(shkp)->shoproom
	|| ESHK(shkp)->following) {
#ifdef STUPID
      /* cater for stupid compilers */
      Short zz;
#endif STUPID
      if (uondoor && (ib = sobj_at(ICE_BOX, nx, ny))) {
	nix = nx; niy = ny; chi = i; break;
      }
      if (avoid && (info[i] & NOTONL))
	continue;
      if ((!appr && !rund(++chcnt)) ||
#ifdef STUPID
	  (appr && (zz = GDIST(nix,niy)) && zz > GDIST(nx,ny))
#else
	  (appr && GDIST(nx,ny) < GDIST(nix,niy))
#endif STUPID
	  ) {
	nix = nx;
	niy = ny;
	chi = i;
      }
    }
  }
  if (nix != omx || niy != omy) {
    if (info[chi] & ALLOW_M){
      mtmp = mon_at(nix,niy);
      if (hitmm(shkp,mtmp) == 1 && rund(3) &&
	  hitmm(mtmp,shkp) == 2) return 2;
      return 0;
    } else if (info[chi] & ALLOW_U){
      hit_you(shkp, dice(mdat->damn, mdat->damd)+1);
      return 0;
    }
    shkp->mx = nix;
    shkp->my = niy;
    pmon(shkp);
    if (ib) {
      unlink_obj(ib);//freeobj
      mpickobj(shkp, ib);
    }
    return 1;
  }
  return 0;
}
Esempio n. 14
0
std::vector<point> game::target(int &x, int &y, int lowx, int lowy, int hix,
                                int hiy, std::vector <monster> t, int &target,
                                item *relevent)
{
 std::vector<point> ret;
 int tarx, tary, tart, junk;
 // TODO: [lightmap] Enable auto targeting based on lightmap
 int sight_dist = u.sight_range(light_level());

// First, decide on a target among the monsters, if there are any in range
 if (t.size() > 0) {
// Check for previous target
  if (target == -1) {
// If no previous target, target the closest there is
   double closest = -1;
   double dist;
   for (int i = 0; i < t.size(); i++) {
    dist = rl_dist(t[i].posx, t[i].posy, u.posx, u.posy);
    if (closest < 0 || dist < closest) {
     closest = dist;
     target = i;
    }
   }
  }
  x = t[target].posx;
  y = t[target].posy;
 } else
  target = -1;	// No monsters in range, don't use target, reset to -1

 WINDOW* w_target = newwin(13, 48, VIEW_OFFSET_Y + MINIMAP_HEIGHT, TERRAIN_WINDOW_WIDTH + 7 + VIEW_OFFSET_X);
 wborder(w_target, LINE_XOXO, LINE_XOXO, LINE_OXOX, LINE_OXOX,
                 LINE_OXXO, LINE_OOXX, LINE_XXOO, LINE_XOOX );
 mvwprintz(w_target, 0, 2, c_white, "< ");
 if (!relevent) { // currently targetting vehicle to refill with fuel
   wprintz(w_target, c_red, "Select a vehicle");
 } else {
   if (relevent == &u.weapon && relevent->is_gun()) {
     wprintz(w_target, c_red, "Firing %s (%d)", // - %s (%d)",
            u.weapon.tname().c_str(),// u.weapon.curammo->name.c_str(),
            u.weapon.charges);
   } else {
     wprintz(w_target, c_red, "Throwing %s", relevent->tname().c_str());
   }
 } 
 wprintz(w_target, c_white, " >");
/* Annoying clutter @ 2 3 4. */
 mvwprintz(w_target, 9, 1, c_white,
           "Move cursor to target with directional keys.");
 if (relevent) {
  mvwprintz(w_target, 10, 1, c_white,
            "'<' '>' Cycle targets; 'f' or '.' to fire.");
  mvwprintz(w_target, 11, 1, c_white,
            "'0' target self; '*' toggle snap-to-target");
 }

 wrefresh(w_target);
 char ch;
 bool snap_to_target = OPTIONS[OPT_SNAP_TO_TARGET];
// The main loop.
 do {
  point center;
  if (snap_to_target)
   center = point(x, y);
  else
   center = point(u.posx, u.posy);
// Clear the target window.
//  for (int i = 5; i < 12; i++) {
  for (int i = 1; i < 8; i++) {
   for (int j = 1; j < 46; j++)
    mvwputch(w_target, i, j, c_white, ' ');
  }
  m.build_map_cache(this);
  m.draw(this, w_terrain, center);
// Draw the Monsters
  for (int i = 0; i < z.size(); i++) {
   if (u_see(&(z[i])) && z[i].posx >= lowx && z[i].posy >= lowy &&
                         z[i].posx <=  hix && z[i].posy <=  hiy)
    z[i].draw(w_terrain, center.x, center.y, false);
  }
// Draw the NPCs
  for (int i = 0; i < active_npc.size(); i++) {
   if (u_see(active_npc[i]->posx, active_npc[i]->posy))
    active_npc[i]->draw(w_terrain, center.x, center.y, false);
  }
  if (x != u.posx || y != u.posy) {
// Calculate the return vector (and draw it too)
/*
   for (int i = 0; i < ret.size(); i++)
    m.drawsq(w_terrain, u, ret[i].x, ret[i].y, false, true, center.x, center.y);
*/
// Draw the player
   int atx = VIEWX + u.posx - center.x, aty = VIEWY + u.posy - center.y;
   if (atx >= 0 && atx < TERRAIN_WINDOW_WIDTH && aty >= 0 && aty < TERRAIN_WINDOW_HEIGHT)
    mvwputch(w_terrain, aty, atx, u.color(), '@');

   if (m.sees(u.posx, u.posy, x, y, -1, tart)) {// Selects a valid line-of-sight
    ret = line_to(u.posx, u.posy, x, y, tart); // Sets the vector to that LOS
// Draw the trajectory
    for (int i = 0; i < ret.size(); i++) {
     if (abs(ret[i].x - u.posx) <= sight_dist &&
         abs(ret[i].y - u.posy) <= sight_dist   ) {
      int mondex = mon_at(ret[i].x, ret[i].y),
          npcdex = npc_at(ret[i].x, ret[i].y);
// NPCs and monsters get drawn with inverted colors
      if (mondex != -1 && u_see(&(z[mondex])))
       z[mondex].draw(w_terrain, center.x, center.y, true);
      else if (npcdex != -1)
       active_npc[npcdex]->draw(w_terrain, center.x, center.y, true);
      else
       m.drawsq(w_terrain, u, ret[i].x, ret[i].y, true,true,center.x, center.y);
     }
    }
   }

   if (!relevent) { // currently targetting vehicle to refill with fuel
    vehicle *veh = m.veh_at(x, y);
    if (veh)
     mvwprintw(w_target, 1, 1, "There is a %s", veh->name.c_str());
   } else
    mvwprintw(w_target, 1, 1, "Range: %d", rl_dist(u.posx, u.posy, x, y));

   if (mon_at(x, y) == -1) {
// what?    mvwprintw(w_status, 0, 9, "                             ");
    if (snap_to_target)
     mvwputch(w_terrain, VIEWY, VIEWX, c_red, '*');
    else
     mvwputch(w_terrain, y + VIEWY - u.posy, x + VIEWX - u.posx, c_red, '*');
   } else if (u_see(&(z[mon_at(x, y)]))) {
//    mvwprintw(w_target, 0, 1, "< %s >", z[mon_at(x, y)].name().c_str() );
    z[mon_at(x, y)].print_info(this, w_target,2);
   }
  }
  wrefresh(w_target);
  wrefresh(w_terrain);
  wrefresh(w_status);
  refresh();
  ch = input();
  get_direction(this, tarx, tary, ch);
  if (tarx != -2 && tary != -2 && ch != '.') {	// Direction character pressed
   int mondex = mon_at(x, y), npcdex = npc_at(x, y);
   if (mondex != -1 && u_see(&(z[mondex])))
    z[mondex].draw(w_terrain, center.x, center.y, false);
   else if (npcdex != -1)
    active_npc[npcdex]->draw(w_terrain, center.x, center.y, false);
   else if (m.sees(u.posx, u.posy, x, y, -1, junk))
    m.drawsq(w_terrain, u, x, y, false, true, center.x, center.y);
   else
    mvwputch(w_terrain, VIEWY, VIEWX, c_black, 'X');
   x += tarx;
   y += tary;
   if (x < lowx)
    x = lowx;
   else if (x > hix)
    x = hix;
   if (y < lowy)
    y = lowy;
   else if (y > hiy)
    y = hiy;
  } else if ((ch == '<') && (target != -1)) {
   target--;
   if (target == -1) target = t.size() - 1;
   x = t[target].posx;
   y = t[target].posy;
  } else if ((ch == '>') && (target != -1)) {
   target++;
   if (target == t.size()) target = 0;
   x = t[target].posx;
   y = t[target].posy;
  } else if (ch == '.' || ch == 'f' || ch == 'F' || ch == '\n') {
   for (int i = 0; i < t.size(); i++) {
    if (t[i].posx == x && t[i].posy == y)
     target = i;
   }
   return ret;
  } else if (ch == '0') {
   x = u.posx;
   y = u.posy;
   ret.clear();
  } else if (ch == '*')
   snap_to_target = !snap_to_target;
  else if (ch == KEY_ESCAPE || ch == 'q') { // return empty vector (cancel)
   ret.clear();
   return ret;
  }
 } while (true);
}
Esempio n. 15
0
void game::throw_item(player &p, int tarx, int tary, item &thrown,
                      std::vector<point> &trajectory)
{
 int deviation = 0;
 int trange = 1.5 * rl_dist(p.posx, p.posy, tarx, tary);

// Throwing attempts below "Basic Competency" level are extra-bad
 int skillLevel = p.skillLevel("throw");

 if (skillLevel < 3)
  deviation += rng(0, 8 - skillLevel);

 if (skillLevel < 8)
  deviation += rng(0, 8 - skillLevel);
 else
  deviation -= skillLevel - 6;

 deviation += p.throw_dex_mod();

 if (p.per_cur < 6)
  deviation += rng(0, 8 - p.per_cur);
 else if (p.per_cur > 8)
  deviation -= p.per_cur - 8;

 deviation += rng(0, p.encumb(bp_hands) * 2 + p.encumb(bp_eyes) + 1);
 if (thrown.volume() > 5)
  deviation += rng(0, 1 + (thrown.volume() - 5) / 4);
 if (thrown.volume() == 0)
  deviation += rng(0, 3);

 deviation += rng(0, 1 + abs(p.str_cur - thrown.weight()));

 double missed_by = .01 * deviation * trange;
 bool missed = false;
 int tart;

 if (missed_by >= 1) {
// We missed D:
// Shoot a random nearby space?
  if (missed_by > 9)
   missed_by = 9;
  tarx += rng(0 - int(sqrt(double(missed_by))), int(sqrt(double(missed_by))));
  tary += rng(0 - int(sqrt(double(missed_by))), int(sqrt(double(missed_by))));
  if (m.sees(p.posx, p.posy, tarx, tary, -1, tart))
   trajectory = line_to(p.posx, p.posy, tarx, tary, tart);
  else
   trajectory = line_to(p.posx, p.posy, tarx, tary, 0);
  missed = true;
  if (!p.is_npc())
   add_msg("You miss!");
 } else if (missed_by >= .6) {
// Hit the space, but not necessarily the monster there
  missed = true;
  if (!p.is_npc())
   add_msg("You barely miss!");
 }

 std::string message;
 int dam = (thrown.weight() / 4 + thrown.type->melee_dam / 2 + p.str_cur / 2) /
            double(2 + double(thrown.volume() / 4));
 if (dam > thrown.weight() * 3)
  dam = thrown.weight() * 3;

 int i = 0, tx = 0, ty = 0;
 for (i = 0; i < trajectory.size() && dam > -10; i++) {
  message = "";
  double goodhit = missed_by;
  tx = trajectory[i].x;
  ty = trajectory[i].y;
// If there's a monster in the path of our item, and either our aim was true,
//  OR it's not the monster we were aiming at and we were lucky enough to hit it
  if (mon_at(tx, ty) != -1 &&
      (!missed || one_in(7 - int(z[mon_at(tx, ty)].type->size)))) {
   if (rng(0, 100) < 20 + skillLevel * 12 &&
       thrown.type->melee_cut > 0) {
    if (!p.is_npc()) {
     message += " You cut the ";
     message += z[mon_at(tx, ty)].name();
     message += "!";
    }
    if (thrown.type->melee_cut > z[mon_at(tx, ty)].armor_cut())
     dam += (thrown.type->melee_cut - z[mon_at(tx, ty)].armor_cut());
   }
   if (thrown.made_of(GLASS) && !thrown.active && // active = molotov, etc.
       rng(0, thrown.volume() + 8) - rng(0, p.str_cur) < thrown.volume()) {
    if (u_see(tx, ty))
     add_msg("The %s shatters!", thrown.tname().c_str());
    for (int i = 0; i < thrown.contents.size(); i++)
     m.add_item(tx, ty, thrown.contents[i]);
    sound(tx, ty, 16, "glass breaking!");
    int glassdam = rng(0, thrown.volume() * 2);
    if (glassdam > z[mon_at(tx, ty)].armor_cut())
     dam += (glassdam - z[mon_at(tx, ty)].armor_cut());
   } else
    m.add_item(tx, ty, thrown);
   if (i < trajectory.size() - 1)
    goodhit = double(double(rand() / RAND_MAX) / 2);
   if (goodhit < .1 && !z[mon_at(tx, ty)].has_flag(MF_NOHEAD)) {
    message = "Headshot!";
    dam = rng(dam, dam * 3);
    p.practice(turn, "throw", 5);
   } else if (goodhit < .2) {
    message = "Critical!";
    dam = rng(dam, dam * 2);
    p.practice(turn, "throw", 2);
   } else if (goodhit < .4)
    dam = rng(int(dam / 2), int(dam * 1.5));
   else if (goodhit < .5) {
    message = "Grazing hit.";
    dam = rng(0, dam);
   }
   if (!p.is_npc())
    add_msg("%s You hit the %s for %d damage.",
            message.c_str(), z[mon_at(tx, ty)].name().c_str(), dam);
   else if (u_see(tx, ty))
    add_msg("%s hits the %s for %d damage.", message.c_str(),
            z[mon_at(tx, ty)].name().c_str(), dam);
   if (z[mon_at(tx, ty)].hurt(dam))
    kill_mon(mon_at(tx, ty), !p.is_npc());
   return;
  } else // No monster hit, but the terrain might be.
   m.shoot(this, tx, ty, dam, false, 0);
  if (m.move_cost(tx, ty) == 0) {
   if (i > 0) {
    tx = trajectory[i - 1].x;
    ty = trajectory[i - 1].y;
   } else {
    tx = u.posx;
    ty = u.posy;
   }
   i = trajectory.size();
  }
 }
 if (m.move_cost(tx, ty) == 0) {
  if (i > 1) {
   tx = trajectory[i - 2].x;
   ty = trajectory[i - 2].y;
  } else {
   tx = u.posx;
   ty = u.posy;
  }
 }
 if (thrown.made_of(GLASS) && !thrown.active && // active means molotov, etc
     rng(0, thrown.volume() + 8) - rng(0, p.str_cur) < thrown.volume()) {
  if (u_see(tx, ty))
   add_msg("The %s shatters!", thrown.tname().c_str());
  for (int i = 0; i < thrown.contents.size(); i++)
   m.add_item(tx, ty, thrown.contents[i]);
  sound(tx, ty, 16, "glass breaking!");
 } else {
  sound(tx, ty, 8, "thud.");
  m.add_item(tx, ty, thrown);
 }
}
Esempio n. 16
0
void game::fire(player &p, int tarx, int tary, std::vector<point> &trajectory,
                bool burst)
{
 item ammotmp;
 item* gunmod = p.weapon.active_gunmod();
 it_ammo *curammo = NULL;
 item *weapon = NULL;

 if (p.weapon.has_flag(IF_CHARGE)) { // It's a charger gun, so make up a type
// Charges maxes out at 8.
  int charges = p.weapon.num_charges();
  it_ammo *tmpammo = dynamic_cast<it_ammo*>(itypes["charge_shot"]);

  tmpammo->damage = charges * charges;
  tmpammo->pierce = (charges >= 4 ? (charges - 3) * 2.5 : 0);
  tmpammo->range = 5 + charges * 5;
  if (charges <= 4)
   tmpammo->accuracy = 14 - charges * 2;
  else // 5, 12, 21, 32
   tmpammo->accuracy = charges * (charges - 4);
  tmpammo->recoil = tmpammo->accuracy * .8;
  tmpammo->ammo_effects = 0;
  if (charges == 8)
   tmpammo->ammo_effects |= mfb(AMMO_EXPLOSIVE_BIG);
  else if (charges >= 6)
   tmpammo->ammo_effects |= mfb(AMMO_EXPLOSIVE);
  if (charges >= 5)
   tmpammo->ammo_effects |= mfb(AMMO_FLAME);
  else if (charges >= 4)
   tmpammo->ammo_effects |= mfb(AMMO_INCENDIARY);

  if (gunmod != NULL) {
   weapon = gunmod;
  } else {
   weapon = &p.weapon;
  }
  curammo = tmpammo;
  weapon->curammo = tmpammo;
  weapon->active = false;
  weapon->charges = 0;
 } else if (gunmod != NULL) {
  weapon = gunmod;
  curammo = weapon->curammo;
 } else {// Just a normal gun. If we're here, we know curammo is valid.
  curammo = p.weapon.curammo;
  weapon = &p.weapon;
 }

 ammotmp = item(curammo, 0);
 ammotmp.charges = 1;

 if (!weapon->is_gun() && !weapon->is_gunmod()) {
  debugmsg("%s tried to fire a non-gun (%s).", p.name.c_str(),
                                               weapon->tname().c_str());
  return;
 }

 bool is_bolt = false;
 unsigned int effects = curammo->ammo_effects;
// Bolts and arrows are silent
 if (curammo->type == AT_BOLT || curammo->type == AT_ARROW)
  is_bolt = true;

 int x = p.posx, y = p.posy;
 // Have to use the gun, gunmods don't have a type
 it_gun* firing = dynamic_cast<it_gun*>(p.weapon.type);
 if (p.has_trait(PF_TRIGGERHAPPY) && one_in(30))
  burst = true;
 if (burst && weapon->burst_size() < 2)
  burst = false; // Can't burst fire a semi-auto

 bool u_see_shooter = u_see(p.posx, p.posy);
// Use different amounts of time depending on the type of gun and our skill
 p.moves -= time_to_fire(p, firing);
// Decide how many shots to fire
 int num_shots = 1;
 if (burst)
  num_shots = weapon->burst_size();
 if (num_shots > weapon->num_charges() && !weapon->has_flag(IF_CHARGE))
  num_shots = weapon->num_charges();

 if (num_shots == 0)
  debugmsg("game::fire() - num_shots = 0!");

// Set up a timespec for use in the nanosleep function below
 timespec ts;
 ts.tv_sec = 0;
 ts.tv_nsec = BULLET_SPEED;

 // Use up some ammunition
 int trange = trig_dist(p.posx, p.posy, tarx, tary);
 if (trange < int(firing->volume / 3) && firing->ammo != AT_SHOT)
  trange = int(firing->volume / 3);
 else if (p.has_bionic("bio_targeting")) {
  if (trange > LONG_RANGE)
   trange = int(trange * .65);
  else
   trange = int(trange * .8);
 }
 if (firing->skill_used == Skill::skill("rifle") && trange > LONG_RANGE)
  trange = LONG_RANGE + .6 * (trange - LONG_RANGE);
 std::string message = "";

 bool missed = false;
 int tart;
 for (int curshot = 0; curshot < num_shots; curshot++) {
// Burst-fire weapons allow us to pick a new target after killing the first
  if (curshot > 0 &&
      (mon_at(tarx, tary) == -1 || z[mon_at(tarx, tary)].hp <= 0)) {
   std::vector<point> new_targets;
   int mondex;
   for (int radius = 1; radius <= 2 + p.skillLevel("gun") && new_targets.empty();
        radius++) {
    for (int diff = 0 - radius; diff <= radius; diff++) {
     mondex = mon_at(tarx + diff, tary - radius);
     if (mondex != -1 && z[mondex].hp > 0 && z[mondex].friendly == 0)
      new_targets.push_back( point(tarx + diff, tary - radius) );

     mondex = mon_at(tarx + diff, tary + radius);
     if (mondex != -1 && z[mondex].hp > 0 && z[mondex].friendly == 0)
      new_targets.push_back( point(tarx + diff, tary + radius) );

     if (diff != 0 - radius && diff != radius) { // Corners were already checked
      mondex = mon_at(tarx - radius, tary + diff);
      if (mondex != -1 && z[mondex].hp > 0 && z[mondex].friendly == 0)
       new_targets.push_back( point(tarx - radius, tary + diff) );

      mondex = mon_at(tarx + radius, tary + diff);
      if (mondex != -1 && z[mondex].hp > 0 && z[mondex].friendly == 0)
       new_targets.push_back( point(tarx + radius, tary + diff) );
     }
    }
   }
   if (!new_targets.empty()) {
    int target_picked = rng(0, new_targets.size() - 1);
    tarx = new_targets[target_picked].x;
    tary = new_targets[target_picked].y;
    if (m.sees(p.posx, p.posy, tarx, tary, 0, tart))
     trajectory = line_to(p.posx, p.posy, tarx, tary, tart);
    else
     trajectory = line_to(p.posx, p.posy, tarx, tary, 0);
   } else if ((!p.has_trait(PF_TRIGGERHAPPY) || one_in(3)) &&
              (p.skillLevel("gun") >= 7 || one_in(7 - p.skillLevel("gun"))))
    return; // No targets, so return
  }

  // Drop a shell casing if appropriate.
  itype_id casing_type = "null";
  switch(curammo->type) {
  case AT_SHOT: casing_type = "shot_hull"; break;
  case AT_9MM: casing_type = "9mm_casing"; break;
  case AT_22: casing_type = "22_casing"; break;
  case AT_38: casing_type = "38_casing"; break;
  case AT_40: casing_type = "40_casing"; break;
  case AT_44: casing_type = "44_casing"; break;
  case AT_45: casing_type = "45_casing"; break;
  case AT_57: casing_type = "57mm_casing"; break;
  case AT_46: casing_type = "46mm_casing"; break;
  case AT_762: casing_type = "762_casing"; break;
  case AT_223: casing_type = "223_casing"; break;
  case AT_3006: casing_type = "3006_casing"; break;
  case AT_308: casing_type = "308_casing"; break;
  case AT_40MM: casing_type = "40mm_casing"; break;
  default: /*No casing for other ammo types.*/ break;
  }
  if (casing_type != "null") {
   int x = p.posx - 1 + rng(0, 2);
   int y = p.posy - 1 + rng(0, 2);
   std::vector<item>& items = m.i_at(x, y);
   int i;
   for (i = 0; i < items.size(); i++)
    if (items[i].typeId() == casing_type &&
        items[i].charges < (dynamic_cast<it_ammo*>(items[i].type))->count) {
     items[i].charges++;
     break;
    }
   if (i == items.size()) {
    item casing;
    casing.make(itypes[casing_type]);
    // Casing needs a charges of 1 to stack properly with other casings.
    casing.charges = 1;
    m.add_item(x, y, casing);
   }
  }

  // Use up a round (or 100)
  if (weapon->has_flag(IF_FIRE_100))
   weapon->charges -= 100;
  else
   weapon->charges--;

  // Current guns have a durability between 5 and 9.
  // Misfire chance is between 1/64 and 1/1024.
  if (one_in(2 << firing->durability)) {
   add_msg("Your weapon misfired!");
   return;
  }

  make_gun_sound_effect(this, p, burst, weapon);
  int trange = calculate_range(p, tarx, tary);
  double missed_by = calculate_missed_by(p, trange, weapon);
// Calculate a penalty based on the monster's speed
  double monster_speed_penalty = 1.;
  int target_index = mon_at(tarx, tary);
  if (target_index != -1) {
   monster_speed_penalty = double(z[target_index].speed) / 80.;
   if (monster_speed_penalty < 1.)
    monster_speed_penalty = 1.;
  }

  if (curshot > 0) {
   if (recoil_add(p) % 2 == 1)
    p.recoil++;
   p.recoil += recoil_add(p) / 2;
  } else
   p.recoil += recoil_add(p);

  if (missed_by >= 1.) {
// We missed D:
// Shoot a random nearby space?
   tarx += rng(0 - int(sqrt(double(missed_by))), int(sqrt(double(missed_by))));
   tary += rng(0 - int(sqrt(double(missed_by))), int(sqrt(double(missed_by))));
   if (m.sees(p.posx, p.posy, x, y, -1, tart))
    trajectory = line_to(p.posx, p.posy, tarx, tary, tart);
   else
    trajectory = line_to(p.posx, p.posy, tarx, tary, 0);
   missed = true;
   if (!burst) {
    if (&p == &u)
     add_msg("You miss!");
    else if (u_see_shooter)
     add_msg("%s misses!", p.name.c_str());
   }
  } else if (missed_by >= .7 / monster_speed_penalty) {
// Hit the space, but not necessarily the monster there
   missed = true;
   if (!burst) {
    if (&p == &u)
     add_msg("You barely miss!");
    else if (u_see_shooter)
     add_msg("%s barely misses!", p.name.c_str());
   }
  }

  int dam = weapon->gun_damage();
  for (int i = 0; i < trajectory.size() &&
       (dam > 0 || (effects & AMMO_FLAME)); i++) {
   if (i > 0)
    m.drawsq(w_terrain, u, trajectory[i-1].x, trajectory[i-1].y, false, true);
// Drawing the bullet uses player u, and not player p, because it's drawn
// relative to YOUR position, which may not be the gunman's position.
   if (u_see(trajectory[i].x, trajectory[i].y)) {
    char bullet = '*';
    if (effects & mfb(AMMO_FLAME))
     bullet = '#';
    mvwputch(w_terrain, trajectory[i].y + VIEWY - u.posy,
                        trajectory[i].x + VIEWX - u.posx, c_red, bullet);
    wrefresh(w_terrain);
    if (&p == &u)
     nanosleep(&ts, NULL);
   }

   if (dam <= 0) { // Ran out of momentum.
    ammo_effects(this, trajectory[i].x, trajectory[i].y, effects);
    if (is_bolt &&
        ((curammo->m1 == WOOD && !one_in(4)) ||
         (curammo->m1 != WOOD && !one_in(15))))
     m.add_item(trajectory[i].x, trajectory[i].y, ammotmp);
    if (weapon->num_charges() == 0)
     weapon->curammo = NULL;
    return;
   }

   int tx = trajectory[i].x, ty = trajectory[i].y;
// If there's a monster in the path of our bullet, and either our aim was true,
//  OR it's not the monster we were aiming at and we were lucky enough to hit it
   int mondex = mon_at(tx, ty);
// If we shot us a monster...
   if (mondex != -1 && (!z[mondex].has_flag(MF_DIGS) ||
       rl_dist(p.posx, p.posy, z[mondex].posx, z[mondex].posy) <= 1) &&
       ((!missed && i == trajectory.size() - 1) ||
        one_in((5 - int(z[mondex].type->size))))) {

    double goodhit = missed_by;
    if (i < trajectory.size() - 1) // Unintentional hit
     goodhit = double(rand() / (RAND_MAX + 1.0)) / 2;

// Penalize for the monster's speed
    if (z[mondex].speed > 80)
     goodhit *= double( double(z[mondex].speed) / 80.);

    std::vector<point> blood_traj = trajectory;
    blood_traj.insert(blood_traj.begin(), point(p.posx, p.posy));
    splatter(this, blood_traj, dam, &z[mondex]);
    shoot_monster(this, p, z[mondex], dam, goodhit, weapon);

   } else if ((!missed || one_in(3)) &&
              (npc_at(tx, ty) != -1 || (u.posx == tx && u.posy == ty)))  {
    double goodhit = missed_by;
    if (i < trajectory.size() - 1) // Unintentional hit
     goodhit = double(rand() / (RAND_MAX + 1.0)) / 2;
    player *h;
    if (u.posx == tx && u.posy == ty)
     h = &u;
    else
     h = active_npc[npc_at(tx, ty)];

    std::vector<point> blood_traj = trajectory;
    blood_traj.insert(blood_traj.begin(), point(p.posx, p.posy));
    splatter(this, blood_traj, dam);
    shoot_player(this, p, h, dam, goodhit);

   } else
    m.shoot(this, tx, ty, dam, i == trajectory.size() - 1, effects);
  } // Done with the trajectory!

  int lastx = trajectory[trajectory.size() - 1].x;
  int lasty = trajectory[trajectory.size() - 1].y;
  ammo_effects(this, lastx, lasty, effects);

  if (m.move_cost(lastx, lasty) == 0) {
   lastx = trajectory[trajectory.size() - 2].x;
   lasty = trajectory[trajectory.size() - 2].y;
  }
  if (is_bolt &&
      ((curammo->m1 == WOOD && !one_in(5)) ||
       (curammo->m1 != WOOD && !one_in(15))  ))
    m.add_item(lastx, lasty, ammotmp);
 }

 if (weapon->num_charges() == 0)
  weapon->curammo = NULL;
}
Esempio n. 17
0
void game::throw_item(player &p, int tarx, int tary, item &thrown,
                      std::vector<point> &trajectory)
{
    int deviation = 0;
    int trange = 1.5 * rl_dist(p.posx, p.posy, tarx, tary);
    std::set<std::string> no_effects;

    // Throwing attempts below "Basic Competency" level are extra-bad
    int skillLevel = p.skillLevel("throw");

    if (skillLevel < 3) {
        deviation += rng(0, 8 - skillLevel);
    }

    if (skillLevel < 8) {
        deviation += rng(0, 8 - skillLevel);
    } else {
        deviation -= skillLevel - 6;
    }

    deviation += p.throw_dex_mod();

    if (p.per_cur < 6) {
        deviation += rng(0, 8 - p.per_cur);
    } else if (p.per_cur > 8) {
        deviation -= p.per_cur - 8;
    }

    deviation += rng(0, p.encumb(bp_hands) * 2 + p.encumb(bp_eyes) + 1);
    if (thrown.volume() > 5) {
        deviation += rng(0, 1 + (thrown.volume() - 5) / 4);
    }
    if (thrown.volume() == 0) {
        deviation += rng(0, 3);
    }

    deviation += rng(0, std::max( 0, p.str_cur - thrown.weight() / 113 ) );

    double missed_by = .01 * deviation * trange;
    bool missed = false;
    int tart;

    if (missed_by >= 1) {
        // We missed D:
        // Shoot a random nearby space?
        if (missed_by > 9) {
            missed_by = 9;
        }

        tarx += rng(0 - int(sqrt(double(missed_by))), int(sqrt(double(missed_by))));
        tary += rng(0 - int(sqrt(double(missed_by))), int(sqrt(double(missed_by))));
        if (m.sees(p.posx, p.posy, tarx, tary, -1, tart)) {
            trajectory = line_to(p.posx, p.posy, tarx, tary, tart);
        } else {
            trajectory = line_to(p.posx, p.posy, tarx, tary, 0);
        }
        missed = true;
        add_msg_if_player(&p,_("You miss!"));
    } else if (missed_by >= .6) {
        // Hit the space, but not necessarily the monster there
        missed = true;
        add_msg_if_player(&p,_("You barely miss!"));
    }

    std::string message;
    int real_dam = (thrown.weight() / 452 + thrown.type->melee_dam / 2 + p.str_cur / 2) /
               double(2 + double(thrown.volume() / 4));
    if (real_dam > thrown.weight() / 40) {
        real_dam = thrown.weight() / 40;
    }
    if (p.has_active_bionic("bio_railgun") && (thrown.made_of("iron") || thrown.made_of("steel"))) {
        real_dam *= 2;
    }
    int dam = real_dam;

    int i = 0, tx = 0, ty = 0;
    for (i = 0; i < trajectory.size() && dam >= 0; i++) {
        message = "";
        double goodhit = missed_by;
        tx = trajectory[i].x;
        ty = trajectory[i].y;

        const int zid = mon_at(tx, ty);

        // If there's a monster in the path of our item, and either our aim was true,
        //  OR it's not the monster we were aiming at and we were lucky enough to hit it
        if (zid != -1 && (!missed || one_in(7 - int(zombie(zid).type->size)))) {
            monster &z = zombie(zid);
            if (rng(0, 100) < 20 + skillLevel * 12 && thrown.type->melee_cut > 0) {
                if (!p.is_npc()) {
                    message += string_format(_(" You cut the %s!"), z.name().c_str());
                }
                if (thrown.type->melee_cut > z.get_armor_cut(bp_torso)) {
                    dam += (thrown.type->melee_cut - z.get_armor_cut(bp_torso));
                }
            }
            if (thrown.made_of("glass") && !thrown.active && // active = molotov, etc.
                rng(0, thrown.volume() + 8) - rng(0, p.str_cur) < thrown.volume()) {
                if (u_see(tx, ty)) {
                    add_msg(_("The %s shatters!"), thrown.tname().c_str());
                }

                for (int i = 0; i < thrown.contents.size(); i++) {
                    m.add_item_or_charges(tx, ty, thrown.contents[i]);
                }

                sound(tx, ty, 16, _("glass breaking!"));
                int glassdam = rng(0, thrown.volume() * 2);
                if (glassdam > z.get_armor_cut(bp_torso)) {
                    dam += (glassdam - z.get_armor_cut(bp_torso));
                }
            } else {
                m.add_item_or_charges(tx, ty, thrown);
            }

            if (i < trajectory.size() - 1) {
                goodhit = double(double(rand() / RAND_MAX) / 2);
            }

            if (goodhit < .1 && !z.has_flag(MF_NOHEAD)) {
                message = _("Headshot!");
                dam = rng(dam, dam * 3);
                p.practice(turn, "throw", 5);
                p.lifetime_stats()->headshots++;
            } else if (goodhit < .2) {
                message = _("Critical!");
                dam = rng(dam, dam * 2);
                p.practice(turn, "throw", 2);
            } else if (goodhit < .4) {
                dam = rng(int(dam / 2), int(dam * 1.5));
            } else if (goodhit < .5) {
                message = _("Grazing hit.");
                dam = rng(0, dam);
            }
            if (u_see(tx, ty)) {
                g->add_msg_player_or_npc(&p,
                    _("%s You hit the %s for %d damage."),
                    _("%s <npcname> hits the %s for %d damage."),
                    message.c_str(), z.name().c_str(), dam);
            }
            if (z.hurt(dam, real_dam)) {
                z.die(&p);
            }
            return;
        } else { // No monster hit, but the terrain might be.
            m.shoot(tx, ty, dam, false, no_effects);
        }
        // Collide with impassable terrain
        if (m.move_cost(tx, ty) == 0) {
            if (i > 0) {
                tx = trajectory[i - 1].x;
                ty = trajectory[i - 1].y;
            } else {
                tx = u.posx;
                ty = u.posy;
            }
            i = trajectory.size();
        }
        if (p.has_active_bionic("bio_railgun") &&
            (thrown.made_of("iron") || thrown.made_of("steel"))) {
            m.add_field(tx, ty, fd_electricity, rng(2,3));
        }
    }
    if (thrown.made_of("glass") && !thrown.active && // active means molotov, etc
        rng(0, thrown.volume() + 8) - rng(0, p.str_cur) < thrown.volume()) {
        if (u_see(tx, ty)) {
            add_msg(_("The %s shatters!"), thrown.tname().c_str());
        }

        for (int i = 0; i < thrown.contents.size(); i++) {
            m.add_item_or_charges(tx, ty, thrown.contents[i]);
        }
        sound(tx, ty, 16, _("glass breaking!"));
    } else {
        if(m.has_flag("LIQUID", tx, ty)) {
            sound(tx, ty, 10, _("splash!"));
        } else {
            sound(tx, ty, 8, _("thud."));
        }
        m.add_item_or_charges(tx, ty, thrown);
    }
}
Esempio n. 18
0
int Creature_tracker::mon_at(int x_pos, int y_pos) const
{
    return mon_at(point(x_pos, y_pos));
}
Esempio n. 19
0
std::vector<point> game::target(int &x, int &y, int lowx, int lowy, int hix,
                                int hiy, std::vector <monster> t, int &target,
                                item *relevent)
{
 std::vector<point> ret;
 int tarx, tary, junk, tart;
 int range=(hix-u.posx);
// First, decide on a target among the monsters, if there are any in range
 if (t.size() > 0) {
// Check for previous target
  if (target == -1) {
// If no previous target, target the closest there is
   double closest = -1;
   double dist;
   for (int i = 0; i < t.size(); i++) {
    dist = rl_dist(t[i].posx, t[i].posy, u.posx, u.posy);
    if (closest < 0 || dist < closest) {
     closest = dist;
     target = i;
    }
   }
  }
  x = t[target].posx;
  y = t[target].posy;
 } else
  target = -1;	// No monsters in range, don't use target, reset to -1

 int sideStyle = (OPTIONS["SIDEBAR_STYLE"] == "Narrow");
 int height = 13;
 int width  = getmaxx(w_messages);
 int top    = sideStyle ? getbegy(w_messages) : (getbegy(w_minimap) + getmaxy(w_minimap));
 int left   = getbegx(w_messages);
 WINDOW* w_target = newwin(height, width, top, left);
 wborder(w_target, LINE_XOXO, LINE_XOXO, LINE_OXOX, LINE_OXOX,
                 LINE_OXXO, LINE_OOXX, LINE_XXOO, LINE_XOOX );
 mvwprintz(w_target, 0, 2, c_white, "< ");
 if (!relevent) { // currently targetting vehicle to refill with fuel
   wprintz(w_target, c_red, _("Select a vehicle"));
 } else {
   if (relevent == &u.weapon && relevent->is_gun()) {
     wprintz(w_target, c_red, _("Firing %s (%d)"), // - %s (%d)",
            u.weapon.tname().c_str(),// u.weapon.curammo->name.c_str(),
            u.weapon.charges);
   } else {
     wprintz(w_target, c_red, _("Throwing %s"), relevent->tname().c_str());
   }
 }
 wprintz(w_target, c_white, " >");
/* Annoying clutter @ 2 3 4. */
 mvwprintz(w_target, getmaxy(w_target) - 4, 1, c_white,
           _("Move cursor to target with directional keys"));
 if (relevent) {
  mvwprintz(w_target, getmaxy(w_target) - 3, 1, c_white,
            _("'<' '>' Cycle targets; 'f' or '.' to fire"));
  mvwprintz(w_target, getmaxy(w_target) - 2, 1, c_white,
            _("'0' target self; '*' toggle snap-to-target"));
 }

 wrefresh(w_target);
 char ch;
 bool snap_to_target = OPTIONS["SNAP_TO_TARGET"];
 do {
  if (m.sees(u.posx, u.posy, x, y, -1, tart))
    ret = line_to(u.posx, u.posy, x, y, tart);
  else
    ret = line_to(u.posx, u.posy, x, y, 0);

  if(trigdist && trig_dist(u.posx,u.posy, x,y) > range) {
    bool cont=true;
    int cx=x;
    int cy=y;
    for (int i = 0; i < ret.size() && cont; i++) {
      if(trig_dist(u.posx,u.posy, ret[i].x, ret[i].y) > range) {
        ret.resize(i);
        cont=false;
      } else {
        cx=0+ret[i].x; cy=0+ret[i].y;
      }
    }
    x=cx;y=cy;
  }
  point center;
  if (snap_to_target)
   center = point(x, y);
  else
   center = point(u.posx + u.view_offset_x, u.posy + u.view_offset_y);
  // Clear the target window.
  for (int i = 1; i < getmaxy(w_target) - 5; i++) {
   for (int j = 1; j < getmaxx(w_target) - 2; j++)
    mvwputch(w_target, i, j, c_white, ' ');
  }
  m.build_map_cache(this);
  m.draw(this, w_terrain, center);
  // Draw the Monsters
  for (int i = 0; i < z.size(); i++) {
   if (u_see(&(z[i]))) {
    z[i].draw(w_terrain, center.x, center.y, false);
   }
  }
  // Draw the NPCs
  for (int i = 0; i < active_npc.size(); i++) {
   if (u_see(active_npc[i]->posx, active_npc[i]->posy))
    active_npc[i]->draw(w_terrain, center.x, center.y, false);
  }
  if (x != u.posx || y != u.posy) {

   // Draw the player
   int atx = VIEWX + u.posx - center.x, aty = VIEWY + u.posy - center.y;
   if (atx >= 0 && atx < TERRAIN_WINDOW_WIDTH && aty >= 0 && aty < TERRAIN_WINDOW_HEIGHT)
    mvwputch(w_terrain, aty, atx, u.color(), '@');

   // Only draw a highlighted trajectory if we can see the endpoint.
   // Provides feedback to the player, and avoids leaking information about tiles they can't see.
   if (u_see( x, y)) {
    for (int i = 0; i < ret.size(); i++) {
      int mondex = mon_at(ret[i].x, ret[i].y),
          npcdex = npc_at(ret[i].x, ret[i].y);
      // NPCs and monsters get drawn with inverted colors
      if (mondex != -1 && u_see(&(z[mondex])))
       z[mondex].draw(w_terrain, center.x, center.y, true);
      else if (npcdex != -1)
       active_npc[npcdex]->draw(w_terrain, center.x, center.y, true);
      else
       m.drawsq(w_terrain, u, ret[i].x, ret[i].y, true,true,center.x, center.y);
    }
   }

   if (!relevent) { // currently targetting vehicle to refill with fuel
    vehicle *veh = m.veh_at(x, y);
    if (veh)
     mvwprintw(w_target, 1, 1, _("There is a %s"), veh->name.c_str());
   } else
    mvwprintw(w_target, 1, 1, _("Range: %d"), rl_dist(u.posx, u.posy, x, y));

   if (mon_at(x, y) == -1) {
    if (snap_to_target)
     mvwputch(w_terrain, VIEWY, VIEWX, c_red, '*');
    else
     mvwputch(w_terrain, VIEWY + y - center.y, VIEWX + x - center.x, c_red, '*');
   } else if (u_see(&(z[mon_at(x, y)]))) {
    z[mon_at(x, y)].print_info(this, w_target,2);
   }
  }
  wrefresh(w_target);
  wrefresh(w_terrain);
  wrefresh(w_status);
  refresh();
  ch = input();
  get_direction(this, tarx, tary, ch);
  if (tarx != -2 && tary != -2 && ch != '.') {	// Direction character pressed
   int mondex = mon_at(x, y), npcdex = npc_at(x, y);
   if (mondex != -1 && u_see(&(z[mondex])))
    z[mondex].draw(w_terrain, center.x, center.y, false);
   else if (npcdex != -1)
    active_npc[npcdex]->draw(w_terrain, center.x, center.y, false);
   else if (m.sees(u.posx, u.posy, x, y, -1, junk))
    m.drawsq(w_terrain, u, x, y, false, true, center.x, center.y);
   else
    mvwputch(w_terrain, VIEWY, VIEWX, c_black, 'X');
   x += tarx;
   y += tary;
   if (x < lowx)
    x = lowx;
   else if (x > hix)
    x = hix;
   if (y < lowy)
    y = lowy;
   else if (y > hiy)
    y = hiy;
  } else if ((ch == '<') && (target != -1)) {
   target--;
   if (target == -1) target = t.size() - 1;
   x = t[target].posx;
   y = t[target].posy;
  } else if ((ch == '>') && (target != -1)) {
   target++;
   if (target == t.size()) target = 0;
   x = t[target].posx;
   y = t[target].posy;
  } else if (ch == '.' || ch == 'f' || ch == 'F' || ch == '\n') {
   for (int i = 0; i < t.size(); i++) {
    if (t[i].posx == x && t[i].posy == y)
     target = i;
   }
   if (u.posx == x && u.posy == y)
       ret.clear();
   break;
  } else if (ch == '0') {
   x = u.posx;
   y = u.posy;
   ret.clear();
  } else if (ch == '*')
   snap_to_target = !snap_to_target;
  else if (ch == KEY_ESCAPE || ch == 'q') { // return empty vector (cancel)
   ret.clear();
   break;
  }
 } while (true);

 return ret;
}
Esempio n. 20
0
void game::fire(player &p, int tarx, int tary, std::vector<point> &trajectory,
                bool burst)
{
 item ammotmp;
 item* gunmod = p.weapon.active_gunmod();
 it_ammo *curammo = NULL;
 item *weapon = NULL;

 if (p.weapon.has_flag("CHARGE")) { // It's a charger gun, so make up a type
// Charges maxes out at 8.
  int charges = p.weapon.num_charges();
  it_ammo *tmpammo = dynamic_cast<it_ammo*>(itypes["charge_shot"]);

  tmpammo->damage = charges * charges;
  tmpammo->pierce = (charges >= 4 ? (charges - 3) * 2.5 : 0);
  tmpammo->range = 5 + charges * 5;
  if (charges <= 4)
   tmpammo->dispersion = 14 - charges * 2;
  else // 5, 12, 21, 32
   tmpammo->dispersion = charges * (charges - 4);
  tmpammo->recoil = tmpammo->dispersion * .8;
  if (charges == 8) { tmpammo->ammo_effects.insert("EXPLOSIVE_BIG"); }
  else if (charges >= 6) { tmpammo->ammo_effects.insert("EXPLOSIVE"); }

  if (charges >= 5){ tmpammo->ammo_effects.insert("FLAME"); }
  else if (charges >= 4) { tmpammo->ammo_effects.insert("INCENDIARY"); }

  if (gunmod != NULL) {
   weapon = gunmod;
  } else {
   weapon = &p.weapon;
  }
  curammo = tmpammo;
  weapon->curammo = tmpammo;
  weapon->active = false;
  weapon->charges = 0;
 } else if (gunmod != NULL) {
  weapon = gunmod;
  curammo = weapon->curammo;
 } else {// Just a normal gun. If we're here, we know curammo is valid.
  curammo = p.weapon.curammo;
  weapon = &p.weapon;
 }

 ammotmp = item(curammo, 0);
 ammotmp.charges = 1;

 if (!weapon->is_gun() && !weapon->is_gunmod()) {
  debugmsg("%s tried to fire a non-gun (%s).", p.name.c_str(),
                                               weapon->tname().c_str());
  return;
 }

 bool is_bolt = false;
 std::set<std::string> *effects = &curammo->ammo_effects;
 // Bolts and arrows are silent
 if (curammo->type == "bolt" || curammo->type == "arrow")
  is_bolt = true;

 int x = p.posx, y = p.posy;
 // Have to use the gun, gunmods don't have a type
 it_gun* firing = dynamic_cast<it_gun*>(p.weapon.type);
 if (p.has_trait(PF_TRIGGERHAPPY) && one_in(30))
  burst = true;
 if (burst && weapon->burst_size() < 2)
  burst = false; // Can't burst fire a semi-auto

// Use different amounts of time depending on the type of gun and our skill
 if (!effects->count("BOUNCE")) {
     p.moves -= time_to_fire(p, firing);
 }
// Decide how many shots to fire
 int num_shots = 1;
 if (burst)
  num_shots = weapon->burst_size();
 if (num_shots > weapon->num_charges() && !weapon->has_flag("CHARGE"))
  num_shots = weapon->num_charges();

 if (num_shots == 0)
  debugmsg("game::fire() - num_shots = 0!");

// Set up a timespec for use in the nanosleep function below
 timespec ts;
 ts.tv_sec = 0;
 ts.tv_nsec = BULLET_SPEED;

 // Use up some ammunition
int trange = rl_dist(p.posx, p.posy, tarx, tary);

 if (trange < int(firing->volume / 3) && firing->ammo != "shot")
  trange = int(firing->volume / 3);
 else if (p.has_bionic("bio_targeting")) {
  if (trange > LONG_RANGE)
   trange = int(trange * .65);
  else
   trange = int(trange * .8);
 }
 if (firing->skill_used == Skill::skill("rifle") && trange > LONG_RANGE)
  trange = LONG_RANGE + .6 * (trange - LONG_RANGE);
 std::string message = "";

 bool missed = false;
 int tart;

 const bool debug_retarget = false;  // this will inevitably be needed
 const bool wildly_spraying = false; // stub for now. later, rng based on stress/skill/etc at the start,
 int weaponrange = p.weapon.range(); // this is expensive, let's cache. todo: figure out if we need p.weapon.range(&p);

 for (int curshot = 0; curshot < num_shots; curshot++) {
 // Burst-fire weapons allow us to pick a new target after killing the first
     if ( curshot > 0 && (mon_at(tarx, tary) == -1 || z[mon_at(tarx, tary)].hp <= 0) ) {
       std::vector<point> new_targets;
       new_targets.clear();

       if ( debug_retarget == true ) {
          mvprintz(curshot,5,c_red,"[%d] %s: retarget: mon_at(%d,%d)",curshot,p.name.c_str(),tarx,tary);
          if(mon_at(tarx, tary) == -1) {
            printz(c_red, " = -1");
          } else {
            printz(c_red, ".hp=%d",
              z[mon_at(tarx, tary)].hp
            );
          }
       }

       for (
         int radius = 0;                        /* range from last target, not shooter! */
         radius <= 2 + p.skillLevel("gun") &&   /* more skill: wider burst area? */
         radius <= weaponrange &&               /* this seems redundant */
         ( new_targets.empty() ||               /* got target? stop looking. However this breaks random selection, aka, wildly spraying, so: */
            wildly_spraying == true );          /* lets set this based on rng && stress or whatever elsewhere */
         radius++
       ) {                                      /* iterate from last target's position: makes sense for burst fire.*/

           for (std::vector<monster>::iterator it = z.begin(); it != z.end(); it++) {
               int nt_range_to_me = rl_dist(p.posx, p.posy, it->posx, it->posy);
               int dummy;
               if (nt_range_to_me == 0 || nt_range_to_me > weaponrange ||
                   !pl_sees(&p, &(*it), dummy)) {
                   /* reject out of range and unseen targets as well as MY FACE */
                   continue;
               }

               int nt_range_to_lt = rl_dist(tarx,tary,it->posx,it->posy);
               /* debug*/ if ( debug_retarget && nt_range_to_lt <= 5 ) printz(c_red, " r:%d/l:%d/m:%d ..", radius, nt_range_to_lt, nt_range_to_me );
               if (nt_range_to_lt != radius) {
                   continue;                    /* we're spiralling outward, catch you next iteration (maybe) */
               }
               if (it->hp >0 && it->friendly == 0) {
                   new_targets.push_back(point(it->posx, it->posy)); /* oh you're not dead and I don't like you. Hello! */
               }
           }
       }
       if ( new_targets.empty() == false ) {    /* new victim! or last victim moved */
          int target_picked = rng(0, new_targets.size() - 1); /* 1 victim list unless wildly spraying */
          tarx = new_targets[target_picked].x;
          tary = new_targets[target_picked].y;
          if (m.sees(p.posx, p.posy, tarx, tary, 0, tart)) {
              trajectory = line_to(p.posx, p.posy, tarx, tary, tart);
          } else {
              trajectory = line_to(p.posx, p.posy, tarx, tary, 0);
          }

          /* debug */ if (debug_retarget) printz(c_ltgreen, " NEW:(%d:%d,%d) %d,%d (%s)[%d] hp: %d",
              target_picked, new_targets[target_picked].x, new_targets[target_picked].y,
              tarx, tary, z[mon_at(tarx, tary)].name().c_str(), mon_at(tarx, tary), z[mon_at(tarx, tary)].hp);

       } else if (
          (
             !p.has_trait(PF_TRIGGERHAPPY) ||   /* double tap. TRIPLE TAP! wait, no... */
             one_in(3)                          /* on second though...everyone double-taps at times. */
          ) && (
             p.skillLevel("gun") >= 7 ||        /* unless trained */
             one_in(7 - p.skillLevel("gun"))    /* ...sometimes */
          ) ) {
          return;                               // No targets, so return
       } else if (debug_retarget) {
          printz(c_red, " new targets.empty()!");
       }
  } else if (debug_retarget) {
    mvprintz(curshot,5,c_red,"[%d] %s: target == mon_at(%d,%d)[%d] %s hp %d",curshot, p.name.c_str(), tarx ,tary,
       mon_at(tarx, tary),
       z[mon_at(tarx, tary)].name().c_str(),
       z[mon_at(tarx, tary)].hp);
  }

  // Drop a shell casing if appropriate.
  itype_id casing_type = "null";
  if( curammo->type == "shot" ) casing_type = "shot_hull";
  else if( curammo->type == "9mm" ) casing_type = "9mm_casing";
  else if( curammo->type == "22" ) casing_type = "22_casing";
  else if( curammo->type == "38" ) casing_type = "38_casing";
  else if( curammo->type == "40" ) casing_type = "40_casing";
  else if( curammo->type == "44" ) casing_type = "44_casing";
  else if( curammo->type == "45" ) casing_type = "45_casing";
  else if( curammo->type == "454" ) casing_type = "454_casing";
  else if( curammo->type == "500" ) casing_type = "500_casing";
  else if( curammo->type == "57" ) casing_type = "57mm_casing";
  else if( curammo->type == "46" ) casing_type = "46mm_casing";
  else if( curammo->type == "762" ) casing_type = "762_casing";
  else if( curammo->type == "223" ) casing_type = "223_casing";
  else if( curammo->type == "3006" ) casing_type = "3006_casing";
  else if( curammo->type == "308" ) casing_type = "308_casing";
  else if( curammo->type == "40mm" ) casing_type = "40mm_casing";

  if (casing_type != "null") {
   item casing;
   casing.make(itypes[casing_type]);
   // Casing needs a charges of 1 to stack properly with other casings.
   casing.charges = 1;
    if( weapon->has_gunmod("brass_catcher") != -1 ) {
        p.i_add( casing );
    } else {
       int x = p.posx - 1 + rng(0, 2);
       int y = p.posy - 1 + rng(0, 2);
       m.add_item_or_charges(x, y, casing);
    }
   }

  // Use up a round (or 100)
  if (weapon->has_flag("FIRE_100"))
   weapon->charges -= 100;
  else
   weapon->charges--;

  if (firing->skill_used != Skill::skill("archery") &&
      firing->skill_used != Skill::skill("throw"))
  {
      // Current guns have a durability between 5 and 9.
      // Misfire chance is between 1/64 and 1/1024.
      if (one_in(2 << firing->durability)) {
          add_msg_player_or_npc( &p, _("Your weapon misfires!"),
                                 _("<npcname>'s weapon misfires!") );
          return;
      }
  }

  make_gun_sound_effect(this, p, burst, weapon);
  int trange = calculate_range(p, tarx, tary);
  double missed_by = calculate_missed_by(p, trange, weapon);
// Calculate a penalty based on the monster's speed
  double monster_speed_penalty = 1.;
  int target_index = mon_at(tarx, tary);
  if (target_index != -1) {
   monster_speed_penalty = double(z[target_index].speed) / 80.;
   if (monster_speed_penalty < 1.)
    monster_speed_penalty = 1.;
  }

  if (curshot > 0) {
   if (recoil_add(p) % 2 == 1)
    p.recoil++;
   p.recoil += recoil_add(p) / 2;
  } else
   p.recoil += recoil_add(p);

  if (missed_by >= 1.) {
// We missed D:
// Shoot a random nearby space?
   int mtarx = tarx + rng(0 - int(sqrt(double(missed_by))), int(sqrt(double(missed_by))));
   int mtary = tary + rng(0 - int(sqrt(double(missed_by))), int(sqrt(double(missed_by))));
   if (m.sees(p.posx, p.posy, x, y, -1, tart))
    trajectory = line_to(p.posx, p.posy, mtarx, mtary, tart);
   else
    trajectory = line_to(p.posx, p.posy, mtarx, mtary, 0);
   missed = true;
   if (!burst) {
       add_msg_player_or_npc( &p, _("You miss!"), _("<npcname> misses!") );
   }
  } else if (missed_by >= .8 / monster_speed_penalty) {
// Hit the space, but not necessarily the monster there
   missed = true;
   if (!burst) {
       add_msg_player_or_npc( &p, _("You barely miss!"), _("<npcname> barely misses!") );
   }
  }

  int dam = weapon->gun_damage();
  int tx = trajectory[0].x;
  int ty = trajectory[0].y;
  int px = trajectory[0].x;
  int py = trajectory[0].y;
  for (int i = 0; i < trajectory.size() &&
         (dam > 0 || (effects->count("FLAME"))); i++) {
      px = tx;
      py = ty;
      tx = trajectory[i].x;
      ty = trajectory[i].y;
// Drawing the bullet uses player u, and not player p, because it's drawn
// relative to YOUR position, which may not be the gunman's position.
   if (u_see(tx, ty)) {
    if (i > 0)
    {
        m.drawsq(w_terrain, u, trajectory[i-1].x, trajectory[i-1].y, false,
                 true, u.posx + u.view_offset_x, u.posy + u.view_offset_y);
    }
    char bullet = '*';
    if (effects->count("FLAME"))
     bullet = '#';
    mvwputch(w_terrain, ty + VIEWY - u.posy - u.view_offset_y,
             tx + VIEWX - u.posx - u.view_offset_x, c_red, bullet);
    wrefresh(w_terrain);
    if (&p == &u)
     nanosleep(&ts, NULL);
   }

   if (dam <= 0 && !(effects->count("FLAME"))) { // Ran out of momentum.
    ammo_effects(this, tx, ty, *effects);
    if (is_bolt && !(effects->count("IGNITE")) &&
        !(effects->count("EXPLOSIVE")) &&
        ((curammo->m1 == "wood" && !one_in(4)) ||
         (curammo->m1 != "wood" && !one_in(15))))
     m.add_item_or_charges(tx, ty, ammotmp);
    if (weapon->num_charges() == 0)
     weapon->curammo = NULL;
    return;
   }

// If there's a monster in the path of our bullet, and either our aim was true,
//  OR it's not the monster we were aiming at and we were lucky enough to hit it
   int mondex = mon_at(tx, ty);
// If we shot us a monster...
   if (mondex != -1 && (!z[mondex].has_flag(MF_DIGS) ||
       rl_dist(p.posx, p.posy, z[mondex].posx, z[mondex].posy) <= 1) &&
       ((!missed && i == trajectory.size() - 1) ||
        one_in((5 - int(z[mondex].type->size))))) {

    double goodhit = missed_by;
    if (i < trajectory.size() - 1) // Unintentional hit
     goodhit = double(rand() / (RAND_MAX + 1.0)) / 2;

// Penalize for the monster's speed
    if (z[mondex].speed > 80)
     goodhit *= double( double(z[mondex].speed) / 80.);

    std::vector<point> blood_traj = trajectory;
    blood_traj.insert(blood_traj.begin(), point(p.posx, p.posy));
    splatter(this, blood_traj, dam, &z[mondex]);
    shoot_monster(this, p, z[mondex], dam, goodhit, weapon);

   } else if ((!missed || one_in(3)) &&
              (npc_at(tx, ty) != -1 || (u.posx == tx && u.posy == ty)))  {
    double goodhit = missed_by;
    if (i < trajectory.size() - 1) // Unintentional hit
     goodhit = double(rand() / (RAND_MAX + 1.0)) / 2;
    player *h;
    if (u.posx == tx && u.posy == ty)
     h = &u;
    else
     h = active_npc[npc_at(tx, ty)];
    if (h->power_level >= 10 && h->uncanny_dodge()) {
     h->power_level -= 7; // dodging bullets costs extra
    }
    else {
     std::vector<point> blood_traj = trajectory;
     blood_traj.insert(blood_traj.begin(), point(p.posx, p.posy));
     splatter(this, blood_traj, dam);
     shoot_player(this, p, h, dam, goodhit);
    }
   } else
    m.shoot(this, tx, ty, dam, i == trajectory.size() - 1, *effects);
  } // Done with the trajectory!

  ammo_effects(this, tx, ty, *effects);
  if (effects->count("BOUNCE"))
  {
    for (int i = 0; i < z.size(); i++)
    {
        // search for monsters in radius 4 around impact site
        if (rl_dist(z[i].posx, z[i].posy, tx, ty) <= 4)
        {
            // don't hit targets that have already been hit
            if (!z[i].has_effect(ME_BOUNCED) && !z[i].dead)
            {
                add_msg(_("The attack bounced to %s!"), z[i].name().c_str());
                trajectory = line_to(tx, ty, z[i].posx, z[i].posy, 0);
                if (weapon->charges > 0)
                    fire(p, z[i].posx, z[i].posy, trajectory, false);
                break;
            }
        }
    }
  }

  if (m.move_cost(tx, ty) == 0) {
      tx = px;
      ty = py;
  }
  if (is_bolt && !(effects->count("IGNITE")) &&
      !(effects->count("EXPLOSIVE")) &&
      ((curammo->m1 == "wood" && !one_in(5)) ||
       (curammo->m1 != "wood" && !one_in(15))  ))
    m.add_item_or_charges(tx, ty, ammotmp);
 }

 if (weapon->num_charges() == 0)
  weapon->curammo = NULL;
}