Exemple #1
0
/**
 * Calculates the Field Of View for the provided map from the given x, y
 * coordinates. Returns a lightmap for a result where the values represent a
 * percentage of fully lit.
 *
 * A value equal to or below 0 means that cell is not in the
 * field of view, whereas a value equal to or above 1 means that cell is
 * in the field of view.
 *
 * @param startx the horizontal component of the starting location
 * @param starty the vertical component of the starting location
 * @param radius the maximum distance to draw the FOV
 */
void map::build_seen_cache(const tripoint &origin)
{
    memset(seen_cache, false, sizeof(seen_cache));
    seen_cache[origin.x][origin.y] = true;

    castLight<0, 1, 1, 0>( seen_cache, transparency_cache, origin.x, origin.y, 0 );
    castLight<1, 0, 0, 1>( seen_cache, transparency_cache, origin.x, origin.y, 0 );

    castLight<0, -1, 1, 0>( seen_cache, transparency_cache, origin.x, origin.y, 0 );
    castLight<-1, 0, 0, 1>( seen_cache, transparency_cache, origin.x, origin.y, 0 );

    castLight<0, 1, -1, 0>( seen_cache, transparency_cache, origin.x, origin.y, 0 );
    castLight<1, 0, 0, -1>( seen_cache, transparency_cache, origin.x, origin.y, 0 );

    castLight<0, -1, -1, 0>( seen_cache, transparency_cache, origin.x, origin.y, 0 );
    castLight<-1, 0, 0, -1>( seen_cache, transparency_cache, origin.x, origin.y, 0 );

    int part;
    if ( vehicle *veh = veh_at( origin.x, origin.y, part ) ) {
        // We're inside a vehicle. Do mirror calcs.
        std::vector<int> mirrors = veh->all_parts_with_feature(VPFLAG_EXTENDS_VISION, true);
        // Do all the sight checks first to prevent fake multiple reflection
        // from happening due to mirrors becoming visible due to processing order.
        // Cameras are also handled here, so that we only need to get through all veh parts once
        int cam_control = -1;
        for (std::vector<int>::iterator m_it = mirrors.begin(); m_it != mirrors.end(); /* noop */) {
            const auto mirror_pos = veh->global_pos() + veh->parts[*m_it].precalc[0];
            // We can utilize the current state of the seen cache to determine
            // if the player can see the mirror from their position.
            if( !veh->part_info( *m_it ).has_flag( "CAMERA" ) && !g->u.sees( mirror_pos )) {
                m_it = mirrors.erase(m_it);
            } else if( !veh->part_info( *m_it ).has_flag( "CAMERA_CONTROL" ) ) {
                ++m_it;
            } else {
                if( origin.x == mirror_pos.x && origin.y == mirror_pos.y && veh->camera_on ) {
                    cam_control = *m_it;
                }
                m_it = mirrors.erase( m_it );
            }
        }

        for( size_t i = 0; i < mirrors.size(); i++ ) {
            const int &mirror = mirrors[i];
            bool is_camera = veh->part_info( mirror ).has_flag( "CAMERA" );
            if( is_camera && cam_control < 0 ) {
                continue; // Player not at camera control, so cameras don't work
            }

            const auto mirror_pos = veh->global_pos() + veh->parts[mirror].precalc[0];

            // Determine how far the light has already traveled so mirrors
            // don't cheat the light distance falloff.
            int offsetDistance;
            if( !is_camera ) {
                offsetDistance = rl_dist(origin.x, origin.y, mirror_pos.x, mirror_pos.y);
            } else {
                offsetDistance = 60 - veh->part_info( mirror ).bonus *
                                      veh->parts[mirror].hp / veh->part_info( mirror ).durability;
                seen_cache[mirror_pos.x][mirror_pos.y] = true;
            }

            // @todo: Factor in the mirror facing and only cast in the
            // directions the player's line of sight reflects to.
            //
            // The naive solution of making the mirrors act like a second player
            // at an offset appears to give reasonable results though.
            castLight<0, 1, 1, 0>( seen_cache, transparency_cache,
                                   mirror_pos.x, mirror_pos.y, offsetDistance );
            castLight<1, 0, 0, 1>( seen_cache, transparency_cache,
                                   mirror_pos.x, mirror_pos.y, offsetDistance );

            castLight<0, -1, 1, 0>( seen_cache, transparency_cache,
                                    mirror_pos.x, mirror_pos.y, offsetDistance );
            castLight<-1, 0, 0, 1>( seen_cache, transparency_cache,
                                    mirror_pos.x, mirror_pos.y, offsetDistance );

            castLight<0, 1, -1, 0>( seen_cache, transparency_cache,
                                    mirror_pos.x, mirror_pos.y, offsetDistance );
            castLight<1, 0, 0, -1>( seen_cache, transparency_cache,
                                    mirror_pos.x, mirror_pos.y, offsetDistance );

            castLight<0, -1, -1, 0>( seen_cache, transparency_cache,
                                     mirror_pos.x, mirror_pos.y, offsetDistance );
            castLight<-1, 0, 0, -1>( seen_cache, transparency_cache,
                                     mirror_pos.x, mirror_pos.y, offsetDistance );
        }
    }
}
/**
 * Calculates the Field Of View for the provided map from the given x, y
 * coordinates. Returns a lightmap for a result where the values represent a
 * percentage of fully lit.
 *
 * A value equal to or below 0 means that cell is not in the
 * field of view, whereas a value equal to or above 1 means that cell is
 * in the field of view.
 *
 * @param startx the horizontal component of the starting location
 * @param starty the vertical component of the starting location
 * @param radius the maximum distance to draw the FOV
 */
void map::build_seen_cache()
{
    memset(seen_cache, false, sizeof(seen_cache));
    seen_cache[g->u.posx][g->u.posy] = true;

    const int offsetX = g->u.posx;
    const int offsetY = g->u.posy;

    castLight( 1, 1.0f, 0.0f, 0, 1, 1, 0, offsetX, offsetY, 0 );
    castLight( 1, 1.0f, 0.0f, 1, 0, 0, 1, offsetX, offsetY, 0 );

    castLight( 1, 1.0f, 0.0f, 0, -1, 1, 0, offsetX, offsetY, 0 );
    castLight( 1, 1.0f, 0.0f, -1, 0, 0, 1, offsetX, offsetY, 0 );

    castLight( 1, 1.0f, 0.0f, 0, 1, -1, 0, offsetX, offsetY, 0 );
    castLight( 1, 1.0f, 0.0f, 1, 0, 0, -1, offsetX, offsetY, 0 );

    castLight( 1, 1.0f, 0.0f, 0, -1, -1, 0, offsetX, offsetY, 0 );
    castLight( 1, 1.0f, 0.0f, -1, 0, 0, -1, offsetX, offsetY, 0 );

    if (vehicle *veh = veh_at(offsetX, offsetY)) {
        // We're inside a vehicle. Do mirror calcs.
        std::vector<int> mirrors = veh->all_parts_with_feature(VPFLAG_MIRROR, true);
        // Do all the sight checks first to prevent fake multiple reflection
        // from happening due to mirrors becoming visible due to processing order.
        for (std::vector<int>::iterator m_it = mirrors.begin(); m_it != mirrors.end(); /* noop */) {
            const int mirrorX = veh->global_x() + veh->parts[*m_it].precalc_dx[0];
            const int mirrorY = veh->global_y() + veh->parts[*m_it].precalc_dy[0];
            // We can utilize the current state of the seen cache to determine
            // if the player can see the mirror from their position.
            if (!g->u.sees(mirrorX, mirrorY)) {
                m_it = mirrors.erase(m_it);
            } else {
                ++m_it;
            }
        }

        for (std::vector<int>::iterator m_it = mirrors.begin(); m_it != mirrors.end(); ++m_it) {
            const int mirrorX = veh->global_x() + veh->parts[*m_it].precalc_dx[0];
            const int mirrorY = veh->global_y() + veh->parts[*m_it].precalc_dy[0];

            // Determine how far the light has already traveled so mirrors
            // don't cheat the light distance falloff.
            int offsetDistance = rl_dist(offsetX, offsetY, mirrorX, mirrorY);

            // @todo: Factor in the mirror facing and only cast in the
            // directions the player's line of sight reflects to.
            //
            // The naive solution of making the mirrors act like a second player
            // at an offset appears to give reasonable results though.

            castLight( 1, 1.0f, 0.0f, 0, 1, 1, 0, mirrorX, mirrorY, offsetDistance );
            castLight( 1, 1.0f, 0.0f, 1, 0, 0, 1, mirrorX, mirrorY, offsetDistance );

            castLight( 1, 1.0f, 0.0f, 0, -1, 1, 0, mirrorX, mirrorY, offsetDistance );
            castLight( 1, 1.0f, 0.0f, -1, 0, 0, 1, mirrorX, mirrorY, offsetDistance );

            castLight( 1, 1.0f, 0.0f, 0, 1, -1, 0, mirrorX, mirrorY, offsetDistance );
            castLight( 1, 1.0f, 0.0f, 1, 0, 0, -1, mirrorX, mirrorY, offsetDistance );

            castLight( 1, 1.0f, 0.0f, 0, -1, -1, 0, mirrorX, mirrorY, offsetDistance );
            castLight( 1, 1.0f, 0.0f, -1, 0, 0, -1, mirrorX, mirrorY, offsetDistance );
        }
    }
}
Exemple #3
0
bool map::process_fields_in_submap(game *g, int gridn)
{
 bool found_field = false;
 field *cur;
 field_id curtype;
 for (int locx = 0; locx < SEEX; locx++) {
  for (int locy = 0; locy < SEEY; locy++) {
   cur = &(grid[gridn].fld[locx][locy]);
   int x = locx + SEEX * (gridn % my_MAPSIZE),
       y = locy + SEEY * int(gridn / my_MAPSIZE);

   curtype = cur->type;
   if (!found_field && curtype != fd_null)
    found_field = true;
   if (cur->density > 3 || cur->density < 1)
    debugmsg("Whoooooa density of %d", cur->density);

  if (cur->age == 0)	// Don't process "newborn" fields
   curtype = fd_null;

  int part;
  vehicle *veh;
  switch (curtype) {

   case fd_null:
    break;	// Do nothing, obviously.  OBVIOUSLY.

   case fd_blood:
   case fd_bile:
    if (has_flag(swimmable, x, y))	// Dissipate faster in water
     cur->age += 250;
    break;

   case fd_acid:
    if (has_flag(swimmable, x, y))	// Dissipate faster in water
     cur->age += 20;
    for (int i = 0; i < i_at(x, y).size(); i++) {
     item *melting = &(i_at(x, y)[i]);
     if (melting->made_of(LIQUID) || melting->made_of(VEGGY)   ||
         melting->made_of(FLESH)  || melting->made_of(POWDER)  ||
         melting->made_of(COTTON) || melting->made_of(WOOL)    ||
         melting->made_of(PAPER)  || melting->made_of(PLASTIC) ||
         (melting->made_of(GLASS) && !one_in(3)) || one_in(4)) {
// Acid destructable objects here
      melting->damage++;
      if (melting->damage >= 5 ||
          (melting->made_of(PAPER) && melting->damage >= 3)) {
       cur->age += melting->volume();
       for (int m = 0; m < i_at(x, y)[i].contents.size(); m++)
        i_at(x, y).push_back( i_at(x, y)[i].contents[m] );
       i_at(x, y).erase(i_at(x, y).begin() + i);
       i--;
      }
     }
    }
    break;

   case fd_sap:
    break; // It doesn't do anything.

   case fd_fire: {
// Consume items as fuel to help us grow/last longer.
    bool destroyed = false;
    int vol = 0, smoke = 0, consumed = 0;
    for (int i = 0; i < i_at(x, y).size() && consumed < cur->density * 2; i++) {
     destroyed = false;
     vol = i_at(x, y)[i].volume();
     item *it = &(i_at(x, y)[i]);

     if (it->is_ammo() && it->ammo_type() != AT_BATT &&
         it->ammo_type() != AT_NAIL && it->ammo_type() != AT_BB &&
         it->ammo_type() != AT_BOLT && it->ammo_type() != AT_ARROW) {
      cur->age /= 2;
      cur->age -= 600;
      destroyed = true;
      smoke += 6;
      consumed++;

     } else if (it->made_of(PAPER)) {
      destroyed = it->burn(cur->density * 3);
      consumed++;
      if (cur->density == 1)
       cur->age -= vol * 10;
      if (vol >= 4)
       smoke++;

     } else if ((it->made_of(WOOD) || it->made_of(VEGGY))) {
      if (vol <= cur->density * 10 || cur->density == 3) {
       cur->age -= 4;
       destroyed = it->burn(cur->density);
       smoke++;
       consumed++;
      } else if (it->burnt < cur->density) {
       destroyed = it->burn(1);
       smoke++;
      }

     } else if ((it->made_of(COTTON) || it->made_of(WOOL))) {
      if (vol <= cur->density * 5 || cur->density == 3) {
       cur->age--;
       destroyed = it->burn(cur->density);
       smoke++;
       consumed++;
      } else if (it->burnt < cur->density) {
       destroyed = it->burn(1);
       smoke++;
      }

     } else if (it->made_of(FLESH)) {
      if (vol <= cur->density * 5 || (cur->density == 3 && one_in(vol / 20))) {
       cur->age--;
       destroyed = it->burn(cur->density);
       smoke += 3;
       consumed++;
      } else if (it->burnt < cur->density * 5 || cur->density >= 2) {
       destroyed = it->burn(1);
       smoke++;
      }

     } else if (it->made_of(LIQUID)) {
      switch (it->type->id) { // TODO: Make this be not a hack.
       case itm_whiskey:
       case itm_vodka:
       case itm_rum:
       case itm_tequila:
        cur->age -= 300;
        smoke += 6;
        break;
       default:
        cur->age += rng(80 * vol, 300 * vol);
        smoke++;
      }
      destroyed = true;
      consumed++;

     } else if (it->made_of(POWDER)) {
      cur->age -= vol;
      destroyed = true;
      smoke += 2;

     } else if (it->made_of(PLASTIC)) {
      smoke += 3;
      if (it->burnt <= cur->density * 2 || (cur->density == 3 && one_in(vol))) {
       destroyed = it->burn(cur->density);
       if (one_in(vol + it->burnt))
        cur->age--;
      }
     }

     if (destroyed) {
      for (int m = 0; m < i_at(x, y)[i].contents.size(); m++)
       i_at(x, y).push_back( i_at(x, y)[i].contents[m] );
      i_at(x, y).erase(i_at(x, y).begin() + i);
      i--;
     }
    }

    veh = &(veh_at(x, y, part));
    if (veh->type != veh_null && (veh->parts[part].flags & VHP_FUEL_TANK) && veh->fuel_type == AT_GAS)
    {
        if (cur->density > 1 && one_in (8) && veh->fuel > 0)
            veh->explode (g, x, y);
    }
// Consume the terrain we're on
    if (has_flag(explodes, x, y)) {
     ter(x, y) = ter_id(int(ter(x, y)) + 1);
     cur->age = 0;
     cur->density = 3;
     g->explosion(x, y, 40, 0, true);

    } else if (has_flag(inflammable, x, y) && one_in(32 - cur->density * 10)) {
     cur->age -= cur->density * cur->density * 40;
     smoke += 15;
     if (cur->density == 3)
      ter(x, y) = t_ash;
    } else if (has_flag(flammable,  x, y) && one_in(32 - cur->density * 10)) {
     cur->age -= cur->density * cur->density * 40;
     smoke += 15;
     if (cur->density == 3)
      ter(x, y) = t_rubble;
    } else if (has_flag(meltable,  x, y) && one_in(32 - cur->density * 10)) {
     cur->age -= cur->density * cur->density * 40;
     if (cur->density == 3)
      ter(x, y) = t_b_metal;
    } else if (terlist[ter(x, y)].flags & mfb(swimmable))
     cur->age += 800;	// Flames die quickly on water

// If we consumed a lot, the flames grow higher
    while (cur->density < 3 && cur->age < 0) {
     cur->age += 300;
     cur->density++;
    }
// If the flames are in a pit, it can't spread to non-pit
    bool in_pit = (ter(x, y) == t_pit);
// If the flames are REALLY big, they contribute to adjacent flames
    if (cur->density == 3 && cur->age < 0) {
// Randomly offset our x/y shifts by 0-2, to randomly pick a square to spread to
     int starti = rng(0, 2);
     int startj = rng(0, 2);
     for (int i = 0; i < 3 && cur->age < 0; i++) {
      for (int j = 0; j < 3 && cur->age < 0; j++) {
       int fx = x + ((i + starti) % 3) - 1, fy = y + ((j + startj) % 3) - 1;
       if (field_at(fx, fy).type == fd_fire && field_at(fx, fy).density < 3 &&
           (!in_pit || ter(fx, fy) == t_pit)) {
        field_at(fx, fy).density++;
        field_at(fx, fy).age = 0;
        cur->age = 0;
       }
      }
     }
    }
// Consume adjacent fuel / terrain to spread.
// Randomly offset our x/y shifts by 0-2, to randomly pick a square to spread to
    int starti = rng(0, 2);
    int startj = rng(0, 2);
    for (int i = 0; i < 3; i++) {
     for (int j = 0; j < 3; j++) {
      int fx = x + ((i + starti) % 3) - 1, fy = y + ((j + startj) % 3) - 1;
      if (INBOUNDS(fx, fy)) {
       int spread_chance = 20 * (cur->density - 1) + 10 * smoke;
       if (has_flag(explodes, fx, fy) && one_in(8 - cur->density)) {
        ter(fx, fy) = ter_id(int(ter(fx, fy)) + 1);
        g->explosion(fx, fy, 40, 0, true);
       } else if ((i != 0 || j != 0) && rng(1, 100) < spread_chance &&
                  (!in_pit || ter(fx, fy) == t_pit) &&
                  ((cur->density == 3 &&
                    (has_flag(flammable, fx, fy) || one_in(20))) ||
                   flammable_items_at(fx, fy) ||
                   field_at(fx, fy).type == fd_web)) {
        if (field_at(fx, fy).type == fd_smoke ||
            field_at(fx, fy).type == fd_web)
         field_at(fx, fy) = field(fd_fire, 1, 0);
        else
         add_field(g, fx, fy, fd_fire, 1);
       } else {
        bool nosmoke = true;
        for (int ii = -1; ii <= 1; ii++) {
         for (int jj = -1; jj <= 1; jj++) {
          if (field_at(x+ii, y+jj).type == fd_fire &&
              field_at(x+ii, y+jj).density == 3)
           smoke++;
          else if (field_at(x+ii, y+jj).type == fd_smoke)
           nosmoke = false;
         }
        }
// If we're not spreading, maybe we'll stick out some smoke, huh?
        if (move_cost(fx, fy) > 0 &&
            (!one_in(smoke) || (nosmoke && one_in(40))) &&
            rng(3, 35) < cur->density * 10 && cur->age < 1000) {
         smoke--;
         add_field(g, fx, fy, fd_smoke, rng(1, cur->density));
        }
       }
      }
     }
    }
   } break;

   case fd_smoke:
    for (int i = -1; i <= 1; i++) {
     for (int j = -1; j <= 1; j++)
      g->scent(x+i, y+j) = 0;
    }
    if (is_outside(x, y))
     cur->age += 50;
    if (one_in(2)) {
     std::vector <point> spread;
     for (int a = -1; a <= 1; a++) {
      for (int b = -1; b <= 1; b++) {
       if ((field_at(x+a, y+b).type == fd_smoke &&
             field_at(x+a, y+b).density < 3       ) ||
           (field_at(x+a, y+b).is_null() && move_cost(x+a, y+b) > 0))
        spread.push_back(point(x+a, y+b));
      }
     }
     if (cur->density > 0 && cur->age > 0 && spread.size() > 0) {
      point p = spread[rng(0, spread.size() - 1)];
      if (field_at(p.x, p.y).type == fd_smoke &&
          field_at(p.x, p.y).density < 3) {
        field_at(p.x, p.y).density++;
        cur->density--;
      } else if (cur->density > 0 && move_cost(p.x, p.y) > 0 &&
                 add_field(g, p.x, p.y, fd_smoke, 1)){
       cur->density--;
       field_at(p.x, p.y).age = cur->age;
      }
     }
    }
   break;

   case fd_tear_gas:
// Reset nearby scents to zero
    for (int i = -1; i <= 1; i++) {
     for (int j = -1; j <= 1; j++)
      g->scent(x+i, y+j) = 0;
    }
    if (is_outside(x, y))
     cur->age += 30;
// One in three chance that it spreads (less than smoke!)
    if (one_in(3)) {
     std::vector <point> spread;
// Pick all eligible points to spread to
     for (int a = -1; a <= 1; a++) {
      for (int b = -1; b <= 1; b++) {
       if (((field_at(x+a, y+b).type == fd_smoke ||
             field_at(x+a, y+b).type == fd_tear_gas) &&
             field_at(x+a, y+b).density < 3            )      ||
           (field_at(x+a, y+b).is_null() && move_cost(x+a, y+b) > 0))
        spread.push_back(point(x+a, y+b));
      }
     }
// Then, spread to a nearby point
     if (cur->density > 0 && cur->age > 0 && spread.size() > 0) {
      point p = spread[rng(0, spread.size() - 1)];
// Nearby teargas grows thicker
      if (field_at(p.x, p.y).type == fd_tear_gas &&
          field_at(p.x, p.y).density < 3) {
        field_at(p.x, p.y).density++;
        cur->density--;
// Nearby smoke is converted into teargas
      } else if (field_at(p.x, p.y).type == fd_smoke) {
       field_at(p.x, p.y).type = fd_tear_gas;
// Or, just create a new field.
      } else if (cur->density > 0 && move_cost(p.x, p.y) > 0 &&
                 add_field(g, p.x, p.y, fd_tear_gas, 1)) {
       cur->density--;
       field_at(p.x, p.y).age = cur->age;
      }
     }
    }
    break;

   case fd_toxic_gas:
// Reset nearby scents to zero
    for (int i = -1; i <= 1; i++) {
     for (int j = -1; j <= 1; j++)
      g->scent(x+i, y+j) = 0;
    }
    if (is_outside(x, y))
     cur->age += 40;
    if (one_in(2)) {
     std::vector <point> spread;
// Pick all eligible points to spread to
     for (int a = -1; a <= 1; a++) {
      for (int b = -1; b <= 1; b++) {
       if (((field_at(x+a, y+b).type == fd_smoke ||
             field_at(x+a, y+b).type == fd_tear_gas ||
             field_at(x+a, y+b).type == fd_toxic_gas ||
             field_at(x+a, y+b).type == fd_nuke_gas   ) &&
             field_at(x+a, y+b).density < 3            )      ||
           (field_at(x+a, y+b).is_null() && move_cost(x+a, y+b) > 0))
        spread.push_back(point(x+a, y+b));
      }
     }
// Then, spread to a nearby point
     if (cur->density > 0 && cur->age > 0 && spread.size() > 0) {
      point p = spread[rng(0, spread.size() - 1)];
// Nearby toxic gas grows thicker
      if (field_at(p.x, p.y).type == fd_toxic_gas &&
          field_at(p.x, p.y).density < 3) {
        field_at(p.x, p.y).density++;
        cur->density--;
// Nearby smoke & teargas is converted into toxic gas
      } else if (field_at(p.x, p.y).type == fd_smoke ||
                 field_at(p.x, p.y).type == fd_tear_gas) {
       field_at(p.x, p.y).type = fd_toxic_gas;
// Or, just create a new field.
      } else if (cur->density > 0 && move_cost(p.x, p.y) > 0 &&
                 add_field(g, p.x, p.y, fd_toxic_gas, 1)) {
       cur->density--;
       field_at(p.x, p.y).age = cur->age;
      }
     }
    }
    break;


   case fd_nuke_gas:
// Reset nearby scents to zero
    for (int i = -1; i <= 1; i++) {
     for (int j = -1; j <= 1; j++)
      g->scent(x+i, y+j) = 0;
    }
    if (is_outside(x, y))
     cur->age += 40;
// Increase long-term radiation in the land underneath
    radiation(x, y) += rng(0, cur->density);
    if (one_in(2)) {
     std::vector <point> spread;
// Pick all eligible points to spread to
     for (int a = -1; a <= 1; a++) {
      for (int b = -1; b <= 1; b++) {
       if (((field_at(x+a, y+b).type == fd_smoke ||
             field_at(x+a, y+b).type == fd_tear_gas ||
             field_at(x+a, y+b).type == fd_toxic_gas ||
             field_at(x+a, y+b).type == fd_nuke_gas   ) &&
             field_at(x+a, y+b).density < 3            )      ||
           (field_at(x+a, y+b).is_null() && move_cost(x+a, y+b) > 0))
        spread.push_back(point(x+a, y+b));
      }
     }
// Then, spread to a nearby point
     if (cur->density > 0 && cur->age > 0 && spread.size() > 0) {
      point p = spread[rng(0, spread.size() - 1)];
// Nearby nukegas grows thicker
      if (field_at(p.x, p.y).type == fd_nuke_gas &&
          field_at(p.x, p.y).density < 3) {
        field_at(p.x, p.y).density++;
        cur->density--;
// Nearby smoke, tear, and toxic gas is converted into nukegas
      } else if (field_at(p.x, p.y).type == fd_smoke ||
                 field_at(p.x, p.y).type == fd_toxic_gas ||
                 field_at(p.x, p.y).type == fd_tear_gas) {
       field_at(p.x, p.y).type = fd_nuke_gas;
// Or, just create a new field.
      } else if (cur->density > 0 && move_cost(p.x, p.y) > 0 &&
                 add_field(g, p.x, p.y, fd_nuke_gas, 1)) {
       cur->density--;
       field_at(p.x, p.y).age = cur->age;
      }
     }
    }
    break;

   case fd_gas_vent:
    for (int i = x - 1; i <= x + 1; i++) {
     for (int j = y - 1; j <= y + 1; j++) {
      if (field_at(i, j).type == fd_toxic_gas && field_at(i, j).density < 3)
       field_at(i, j).density++;
      else
       add_field(g, i, j, fd_toxic_gas, 3);
     }
    }
    break;

   case fd_fire_vent:
    if (cur->density > 1) {
     if (one_in(3))
      cur->density--;
    } else {
     cur->type = fd_flame_burst;
     cur->density = 3;
    }
    break;

   case fd_flame_burst:
    if (cur->density > 1)
     cur->density--;
    else {
     cur->type = fd_fire_vent;
     cur->density = 3;
    }
    break;

   case fd_electricity:
    if (!one_in(5)) {	// 4 in 5 chance to spread
     std::vector<point> valid;
     if (move_cost(x, y) == 0 && cur->density > 1) { // We're grounded
      int tries = 0;
      while (tries < 10 && cur->age < 50) {
       int cx = x + rng(-1, 1), cy = y + rng(-1, 1);
       if (move_cost(cx, cy) != 0 && field_at(cx, cy).is_null()) {
        add_field(g, cx, cy, fd_electricity, 1);
        cur->density--;
        tries = 0;
       } else
        tries++;
      }
     } else {	// We're not grounded; attempt to ground
      for (int a = -1; a <= 1; a++) {
       for (int b = -1; b <= 1; b++) {
        if (move_cost(x + a, y + b) == 0 && // Grounded tiles first
            field_at(x + a, y + b).is_null())
         valid.push_back(point(x + a, y + b));
       }
      }
      if (valid.size() == 0) {	// Spread to adjacent space, then
       int px = x + rng(-1, 1), py = y + rng(-1, 1);
       if (move_cost(px, py) > 0 && field_at(px, py).type == fd_electricity &&
           field_at(px, py).density < 3)
        field_at(px, py).density++;
       else if (move_cost(px, py) > 0)
        add_field(g, px, py, fd_electricity, 1);
       cur->density--;
      }
      while (valid.size() > 0 && cur->density > 0) {
       int index = rng(0, valid.size() - 1);
       add_field(g, valid[index].x, valid[index].y, fd_electricity, 1);
       cur->density--;
       valid.erase(valid.begin() + index);
      }
     }
    }
    break;

   case fd_fatigue:
    if (cur->density < 3 && int(g->turn) % 3600 == 0 && one_in(10))
     cur->density++;
    else if (cur->density == 3 && one_in(600)) { // Spawn nether creature!
     mon_id type = mon_id(rng(mon_flying_polyp, mon_blank));
     monster creature(g->mtypes[type]);
     creature.spawn(x + rng(-3, 3), y + rng(-3, 3));
     g->z.push_back(creature);
    }
    break;
   }

   cur->age++;
   if (fieldlist[cur->type].halflife > 0) {
    if (cur->age > 0 &&
        dice(3, cur->age) > dice(3, fieldlist[cur->type].halflife)) {
     cur->age = 0;
     cur->density--;
    }
    if (cur->density <= 0) { // Totally dissapated.
     grid[gridn].field_count--;
     grid[gridn].fld[locx][locy] = field();
    }
   }
  }
 }
 return found_field;
}
Exemple #4
0
/**
 * Calculates the Field Of View for the provided map from the given x, y
 * coordinates. Returns a lightmap for a result where the values represent a
 * percentage of fully lit.
 *
 * A value equal to or below 0 means that cell is not in the
 * field of view, whereas a value equal to or above 1 means that cell is
 * in the field of view.
 *
 * @param startx the horizontal component of the starting location
 * @param starty the vertical component of the starting location
 * @param radius the maximum distance to draw the FOV
 */
void map::build_seen_cache( const tripoint &origin, const int target_z )
{
    auto &map_cache = get_cache( target_z );
    float (&transparency_cache)[MAPSIZE*SEEX][MAPSIZE*SEEY] = map_cache.transparency_cache;
    float (&seen_cache)[MAPSIZE*SEEX][MAPSIZE*SEEY] = map_cache.seen_cache;

    std::uninitialized_fill_n(
        &seen_cache[0][0], MAPSIZE*SEEX * MAPSIZE*SEEY, LIGHT_TRANSPARENCY_SOLID);

    if( !fov_3d ) {
        seen_cache[origin.x][origin.y] = LIGHT_TRANSPARENCY_CLEAR;

        castLight<0, 1, 1, 0, sight_calc, sight_check>(
            seen_cache, transparency_cache, origin.x, origin.y, 0 );
        castLight<1, 0, 0, 1, sight_calc, sight_check>(
            seen_cache, transparency_cache, origin.x, origin.y, 0 );

        castLight<0, -1, 1, 0, sight_calc, sight_check>(
            seen_cache, transparency_cache, origin.x, origin.y, 0 );
        castLight<-1, 0, 0, 1, sight_calc, sight_check>(
            seen_cache, transparency_cache, origin.x, origin.y, 0 );

        castLight<0, 1, -1, 0, sight_calc, sight_check>(
            seen_cache, transparency_cache, origin.x, origin.y, 0 );
        castLight<1, 0, 0, -1, sight_calc, sight_check>(
            seen_cache, transparency_cache, origin.x, origin.y, 0 );

        castLight<0, -1, -1, 0, sight_calc, sight_check>(
            seen_cache, transparency_cache, origin.x, origin.y, 0 );
        castLight<-1, 0, 0, -1, sight_calc, sight_check>(
            seen_cache, transparency_cache, origin.x, origin.y, 0 );
    } else {
        if( origin.z == target_z ) {
            seen_cache[origin.x][origin.y] = LIGHT_TRANSPARENCY_CLEAR;
        }

        // Cache the caches (pointers to them)
        std::array<const float (*)[MAPSIZE*SEEX][MAPSIZE*SEEY], OVERMAP_LAYERS> transparency_caches;
        std::array<float (*)[MAPSIZE*SEEX][MAPSIZE*SEEY], OVERMAP_LAYERS> seen_caches;
        std::array<const bool (*)[MAPSIZE*SEEX][MAPSIZE*SEEY], OVERMAP_LAYERS> floor_caches;
        for( int z = -OVERMAP_DEPTH; z <= OVERMAP_HEIGHT; z++ ) {
            auto &cur_cache = get_cache( z );
            transparency_caches[z + OVERMAP_DEPTH] = &cur_cache.transparency_cache;
            seen_caches[z + OVERMAP_DEPTH] = &cur_cache.seen_cache;
            floor_caches[z + OVERMAP_DEPTH] = &cur_cache.floor_cache;
        }

        // Down
        cast_zlight<0, 1, 0, 1, 0, 0, -1, sight_calc, sight_check>(
            seen_caches, transparency_caches, floor_caches, origin, 0 );
        cast_zlight<1, 0, 0, 0, 1, 0, -1, sight_calc, sight_check>(
            seen_caches, transparency_caches, floor_caches, origin, 0 );

        cast_zlight<0, -1, 0, 1, 0, 0, -1, sight_calc, sight_check>(
            seen_caches, transparency_caches, floor_caches, origin, 0 );
        cast_zlight<-1, 0, 0, 0, 1, 0, -1, sight_calc, sight_check>(
            seen_caches, transparency_caches, floor_caches, origin, 0 );

        cast_zlight<0, 1, 0, -1, 0, 0, -1, sight_calc, sight_check>(
            seen_caches, transparency_caches, floor_caches, origin, 0 );
        cast_zlight<1, 0, 0, 0, -1, 0, -1, sight_calc, sight_check>(
            seen_caches, transparency_caches, floor_caches, origin, 0 );

        cast_zlight<0, -1, 0, -1, 0, 0, -1, sight_calc, sight_check>(
            seen_caches, transparency_caches, floor_caches, origin, 0 );
        cast_zlight<-1, 0, 0, 0, -1, 0, -1, sight_calc, sight_check>(
            seen_caches, transparency_caches, floor_caches, origin, 0 );
        cast_zlight<0, 1, 0, 1, 0, 0, 1, sight_calc, sight_check>(
            seen_caches, transparency_caches, floor_caches, origin, 0 );
        cast_zlight<1, 0, 0, 0, 1, 0, 1, sight_calc, sight_check>(
            seen_caches, transparency_caches, floor_caches, origin, 0 );
        // Up
        cast_zlight<0, -1, 0, 1, 0, 0, 1, sight_calc, sight_check>(
            seen_caches, transparency_caches, floor_caches, origin, 0 );
        cast_zlight<-1, 0, 0, 0, 1, 0, 1, sight_calc, sight_check>(
            seen_caches, transparency_caches, floor_caches, origin, 0 );

        cast_zlight<0, 1, 0, -1, 0, 0, 1, sight_calc, sight_check>(
            seen_caches, transparency_caches, floor_caches, origin, 0 );
        cast_zlight<1, 0, 0, 0, -1, 0, 1, sight_calc, sight_check>(
            seen_caches, transparency_caches, floor_caches, origin, 0 );

        cast_zlight<0, -1, 0, -1, 0, 0, 1, sight_calc, sight_check>(
            seen_caches, transparency_caches, floor_caches, origin, 0 );
        cast_zlight<-1, 0, 0, 0, -1, 0, 1, sight_calc, sight_check>(
            seen_caches, transparency_caches, floor_caches, origin, 0 );
    }

    int part;
    vehicle *veh = veh_at( origin, part );
    if( veh == nullptr ) {
        return;
    }

    // We're inside a vehicle. Do mirror calcs.
    std::vector<int> mirrors = veh->all_parts_with_feature(VPFLAG_EXTENDS_VISION, true);
    // Do all the sight checks first to prevent fake multiple reflection
    // from happening due to mirrors becoming visible due to processing order.
    // Cameras are also handled here, so that we only need to get through all veh parts once
    int cam_control = -1;
    for (std::vector<int>::iterator m_it = mirrors.begin(); m_it != mirrors.end(); /* noop */) {
        const auto mirror_pos = veh->global_pos() + veh->parts[*m_it].precalc[0];
        // We can utilize the current state of the seen cache to determine
        // if the player can see the mirror from their position.
        if( !veh->part_info( *m_it ).has_flag( "CAMERA" ) &&
            seen_cache[mirror_pos.x][mirror_pos.y] < LIGHT_TRANSPARENCY_SOLID + 0.1 ) {
            m_it = mirrors.erase(m_it);
        } else if( !veh->part_info( *m_it ).has_flag( "CAMERA_CONTROL" ) ) {
            ++m_it;
        } else {
            if( origin.x == mirror_pos.x && origin.y == mirror_pos.y && veh->camera_on ) {
                cam_control = *m_it;
            }
            m_it = mirrors.erase( m_it );
        }
    }

    for( size_t i = 0; i < mirrors.size(); i++ ) {
        const int &mirror = mirrors[i];
        bool is_camera = veh->part_info( mirror ).has_flag( "CAMERA" );
        if( is_camera && cam_control < 0 ) {
            continue; // Player not at camera control, so cameras don't work
        }

        const auto mirror_pos = veh->global_pos() + veh->parts[mirror].precalc[0];

        // Determine how far the light has already traveled so mirrors
        // don't cheat the light distance falloff.
        int offsetDistance;
        if( !is_camera ) {
            offsetDistance = rl_dist(origin.x, origin.y, mirror_pos.x, mirror_pos.y);
        } else {
            offsetDistance = 60 - veh->part_info( mirror ).bonus *
                                  veh->parts[mirror].hp / veh->part_info( mirror ).durability;
            seen_cache[mirror_pos.x][mirror_pos.y] = LIGHT_TRANSPARENCY_OPEN_AIR;
        }

        // @todo: Factor in the mirror facing and only cast in the
        // directions the player's line of sight reflects to.
        //
        // The naive solution of making the mirrors act like a second player
        // at an offset appears to give reasonable results though.

        castLight<0, 1, 1, 0, sight_calc, sight_check>(
            seen_cache, transparency_cache, mirror_pos.x, mirror_pos.y, offsetDistance );
        castLight<1, 0, 0, 1, sight_calc, sight_check>(
            seen_cache, transparency_cache, mirror_pos.x, mirror_pos.y, offsetDistance );

        castLight<0, -1, 1, 0, sight_calc, sight_check>(
            seen_cache, transparency_cache, mirror_pos.x, mirror_pos.y, offsetDistance );
        castLight<-1, 0, 0, 1, sight_calc, sight_check>(
            seen_cache, transparency_cache, mirror_pos.x, mirror_pos.y, offsetDistance );

        castLight<0, 1, -1, 0, sight_calc, sight_check>(
            seen_cache, transparency_cache, mirror_pos.x, mirror_pos.y, offsetDistance );
        castLight<1, 0, 0, -1, sight_calc, sight_check>(
            seen_cache, transparency_cache, mirror_pos.x, mirror_pos.y, offsetDistance );

        castLight<0, -1, -1, 0, sight_calc, sight_check>(
            seen_cache, transparency_cache, mirror_pos.x, mirror_pos.y, offsetDistance );
        castLight<-1, 0, 0, -1, sight_calc, sight_check>(
            seen_cache, transparency_cache, mirror_pos.x, mirror_pos.y, offsetDistance );
    }
}