Ejemplo n.º 1
0
// firing is the item that is fired. It may be the wielded gun, but it can also be an attached
// gunmod. p is the character that is firing, this may be a pseudo-character (used by monattack/
// vehicle turrets) or a NPC.
void sfx::generate_gun_sound( const player &p, const item &firing )
{
    end_sfx_timestamp = std::chrono::high_resolution_clock::now();
    sfx_time = end_sfx_timestamp - start_sfx_timestamp;
    if( std::chrono::duration_cast<std::chrono::milliseconds> ( sfx_time ).count() < 80 ) {
        return;
    }
    const tripoint source = p.pos();
    int heard_volume = get_heard_volume( source );
    if( heard_volume <= 30 ) {
        heard_volume = 30;
    }

    itype_id weapon_id = firing.typeId();
    int angle;
    int distance;
    std::string selected_sound;
    // this does not mean p == g->u (it could be a vehicle turret)
    if( g->u.pos() == source ) {
        angle = 0;
        distance = 0;
        selected_sound = "fire_gun";

        const auto mods = firing.gunmods();
        if( std::any_of( mods.begin(), mods.end(), []( const item *e ) { return e->type->gunmod->loudness < 0; } ) ) {
            weapon_id = "weapon_fire_suppressed";
        }

    } else {
        angle = get_heard_angle( source );
        distance = rl_dist( g->u.pos(), source );
        if( distance <= 17 ) {
            selected_sound = "fire_gun";
        } else {
            selected_sound = "fire_gun_distant";
        }
    }

    play_variant_sound( selected_sound, weapon_id, heard_volume, angle, 0.8, 1.2 );
    start_sfx_timestamp = std::chrono::high_resolution_clock::now();
}
Ejemplo n.º 2
0
int monster::group_bash_skill( point target )
{
    if( !has_flag(MF_GROUP_BASH) ) {
        return bash_skill();
    }
    int bashskill = 0;

    // pileup = more bashskill, but only help bashing mob directly infront of target
    const int max_helper_depth = 5;
    const std::vector<point> bzone = get_bashing_zone( target, pos(), max_helper_depth );

    for( point candidate : bzone ) {
        // Drawing this line backwards excludes the target and includes the candidate.
        std::vector<point> path_to_target = line_to( target, candidate, 0 );
        bool connected = true;
        int mondex = -1;
        for( point in_path : path_to_target ) {
            // If any point in the line from zombie to target is not a cooperating zombie,
            // it can't contribute.
            mondex = g->mon_at( in_path );
            if( mondex == -1 ) {
                connected = false;
                break;
            }
            monster &helpermon = g->zombie( mondex );
            if( !helpermon.has_flag(MF_GROUP_BASH) || helpermon.is_hallucination() ) {
                connected = false;
                break;
            }
        }
        if( !connected ) {
            continue;
        }
        // If we made it here, the last monster checked was the candidate.
        monster &helpermon = g->zombie( mondex );
        // Contribution falls off rapidly with distance from target.
        bashskill += helpermon.bash_skill() / rl_dist( candidate, target );
    }

    return bashskill;
}
Ejemplo n.º 3
0
void mdeath::jabberwock( monster &z )
{
    player *ch = dynamic_cast<player *>( z.get_killer() );

    bool vorpal = ch && ch->is_player() &&
                  rl_dist( z.pos(), ch->pos() ) <= 1 &&
                  ch->weapon.has_flag( "DIAMOND" ) &&
                  ch->weapon.volume() > units::from_milliliter( 750 );

    if( vorpal && !ch->weapon.has_technique( matec_id( "VORPAL" ) ) ) {
        if( ch->sees( z ) ) {
            //~ %s is the possessive form of the monster's name
            ch->add_msg_if_player( m_info,
                                   _( "As the flames in %s eyes die out, your weapon seems to shine slightly brighter." ),
                                   z.disp_name( true ) );
        }
        ch->weapon.add_technique( matec_id( "VORPAL" ) );
    }

    mdeath::normal( z );
}
Ejemplo n.º 4
0
void mdefense::zapback(monster *m, const projectile *proj)
{
    int j;
    if (rl_dist(m->posx(), m->posy(), g->u.posx, g->u.posy) > 1 ||
        !g->sees_u(m->posx(), m->posy(), j)) {
        return; // Out of range
    }
    if (proj != NULL) {
        return; // Not a melee attack
    }
    if ((!g->u.has_active_bionic("bio_faraday") && !g->u.worn_with_flag("ELECTRIC_IMMUNE") &&
         !g->u.has_artifact_with(AEP_RESIST_ELECTRICITY)) &&
        (g->u.weapon.conductive() || g->u.unarmed_attack()) && (rng(0, 100) <= m->def_chance)) {
        damage_instance shock;
        shock.add_damage(DT_ELECTRIC, rng(1, 5));
        g->u.deal_damage(m, bp_arm_l, shock);
        g->u.deal_damage(m, bp_arm_r, shock);
        add_msg(m_bad, _("Striking the %s shocks you!"), m->name().c_str());
    }
    return;
}
Ejemplo n.º 5
0
void mdeath::guilt(game *g, monster *z)
{
 if (g->u.has_trait(PF_CANNIBAL))
  return;	// We don't give a shit!
 if (rl_dist(z->posx, z->posy, g->u.posx, g->u.posy) > 5)
  return;	// Too far away, we can deal with it
 if (z->hp >= 0)
  return;	// It probably didn't die from damage
 g->add_msg("You feel terrible for killing %s!", z->name().c_str());
 if(z->type->id == mon_hallu_mom)
 {
 g->u.add_morale(MORALE_KILLED_MONSTER, -50, -250);
 }
 else if(z->type->id == mon_zombie_child)
 {
 g->u.add_morale(MORALE_KILLED_MONSTER, -5, -250);
 }
 else
 {
 return;
 }
}
Ejemplo n.º 6
0
void mdeath::boomer(game *g, monster *z)
{
 std::string tmp;
 g->sound(z->posx, z->posy, 24, "a boomer explode!");
 for (int i = -1; i <= 1; i++) {
  for (int j = -1; j <= 1; j++) {
   g->m.bash(z->posx + i, z->posy + j, 10, tmp);
   if (g->m.field_at(z->posx + i, z->posy + j).type == fd_bile &&
       g->m.field_at(z->posx + i, z->posy + j).density < 3)
    g->m.field_at(z->posx + i, z->posy + j).density++;
   else
    g->m.add_field(g, z->posx + i, z->posy + j, fd_bile, 1);
   int mondex = g->mon_at(z->posx + i, z->posy +j);
   if (mondex != -1) {
    g->z[mondex].stumble(g, false);
    g->z[mondex].moves -= 250;
   }
  }
 }
 if (rl_dist(z->posx, z->posy, g->u.posx, g->u.posy) == 1)
  g->u.infect(DI_BOOMERED, bp_eyes, 2, 24, g);
}
Ejemplo n.º 7
0
void talk_function::give_all_aid( npc &p )
{
    p.add_effect( effect_currently_busy, 30_minutes );
    give_aid( p );
    for( npc &guy : g->all_npcs() ) {
        if( rl_dist( guy.pos(), g->u.pos() ) < PICKUP_RANGE && guy.is_friend() ) {
            for( int i = 0; i < num_hp_parts; i++ ) {
                const body_part bp_healed = player::hp_to_bp( hp_part( i ) );
                guy.heal( hp_part( i ), 5 * rng( 2, 5 ) );
                if( guy.has_effect( effect_bite, bp_healed ) ) {
                    guy.remove_effect( effect_bite, bp_healed );
                }
                if( guy.has_effect( effect_bleed, bp_healed ) ) {
                    guy.remove_effect( effect_bleed, bp_healed );
                }
                if( guy.has_effect( effect_infected, bp_healed ) ) {
                    guy.remove_effect( effect_infected, bp_healed );
                }
            }
        }
    }
}
Ejemplo n.º 8
0
bool gun_actor::call( monster &z ) const
{
    Creature *target;

    if( z.friendly ) {
        int max_range = 0;
        for( const auto &e : ranges ) {
            max_range = std::max( std::max( max_range, e.first.first ), e.first.second );
        }

        int hostiles; // hostiles which cannot be engaged without risking friendly fire
        target = z.auto_find_hostile_target( max_range, hostiles );
        if( !target ) {
            if( hostiles > 0 && g->u.sees( z ) ) {
                add_msg( m_warning, ngettext( "Pointed in your direction, the %s emits an IFF warning beep.",
                                              "Pointed in your direction, the %s emits %d annoyed sounding beeps.",
                                              hostiles ),
                         z.name(), hostiles );
            }
            return false;
        }

    } else {
        target = z.attack_target();
        if( !target || !z.sees( *target ) ) {
            return false;
        }
    }

    int dist = rl_dist( z.pos(), target->pos() );
    for( const auto &e : ranges ) {
        if( dist >= e.first.first && dist <= e.first.second ) {
            shoot( z, *target, e.second );
            return true;
        }
    }
    return false;
}
Ejemplo n.º 9
0
city_reference overmapbuffer::closest_city( const tripoint &center )
{
    // a whole overmap (because it's in submap coordinates, OMAPX is overmap terrain coordinates)
    auto const radius = OMAPX * 2;
    // Starting with distance = INT_MAX, so the first city is already closer
    city_reference result{ nullptr, nullptr, tripoint( 0, 0, 0 ), INT_MAX };
    for( auto &om : get_overmaps_near( center, radius ) ) {
        const auto abs_pos_om = om_to_sm_copy( om->pos() );
        for( auto &city : om->cities ) {
            const auto rel_pos_city = omt_to_sm_copy( point( city.x, city.y ) );
            // TODO: Z-level cities. This 0 has to be here until mapgen understands non-0 zlev cities
            const auto abs_pos_city = tripoint( abs_pos_om + rel_pos_city, 0 );
            const auto distance = rl_dist( abs_pos_city, center );
            const city_reference cr{ om, &city, abs_pos_city, distance };
            if( distance < result.distance ) {
                result = cr;
            } else if( distance == result.distance && result.city->s < city.s ) {
                result = cr;
            }
        }
    }
    return result;
}
Ejemplo n.º 10
0
bool trap::detect_trap(const player &p, int x, int y) const
{
    // Some decisions are based around:
    // * Starting, and thus average perception, is 8.
    // * Buried landmines, the silent killer, has a visibility of 10.
    // * There will always be a distance malus of 1 unless you're on top of the trap.
    // * ...and an average character should at least have a minor chance of
    //   noticing a buried landmine if standing right next to it.
            // Effective Perception...
    return (p.per_cur - const_cast<player&>(p).encumb(bp_eyes)) +
            // ...small bonus from stimulants...
            (p.stim > 10 ? rng(1, 2) : 0) +
            // ...bonus from trap skill...
            (const_cast<player&>(p).skillLevel("traps") * 2) +
            // ...luck, might be good, might be bad...
            rng(-4, 4) -
            // ...malus if we are tired...
            (p.has_disease("lack_sleep") ? rng(1, 5) : 0) -
            // ...malus farther we are from trap...
            rl_dist(p.posx, p.posy, x, y) >
            // ...must all be greater than the trap visibility.
            visibility;
}
Ejemplo n.º 11
0
std::vector<city_reference> overmapbuffer::get_cities_near( const tripoint &location, int radius )
{
    std::vector<city_reference> result;

    for( const auto om : get_overmaps_near( location, radius ) ) {
        const auto abs_pos_om = om_to_sm_copy( om->pos() );
        result.reserve( result.size() + om->cities.size() );
        std::transform( om->cities.begin(), om->cities.end(), std::back_inserter( result ),
        [&]( city & element ) {
            const auto rel_pos_city = omt_to_sm_copy( element.pos );
            const auto abs_pos_city = tripoint( rel_pos_city + abs_pos_om, 0 );
            const auto distance = rl_dist( abs_pos_city, location );

            return city_reference{ &element, abs_pos_city, distance };
        } );
    }

    std::sort( result.begin(), result.end(), []( const city_reference & lhs,
    const city_reference & rhs ) {
        return lhs.get_distance_from_bounds() < rhs.get_distance_from_bounds();
    } );

    return result;
}
Ejemplo n.º 12
0
bool Creature::sees( const tripoint &t, int &bresen1, int &bresen2 ) const
{
    // TODO: FoV update
    bresen2 = 0;
    if( posz() != t.z ) {
        return false;
    }

    const int range_cur = sight_range( g->m.ambient_light_at(t) );
    const int range_day = sight_range( DAYLIGHT_LEVEL );
    const int range_min = std::min( range_cur, range_day );
    const int wanted_range = rl_dist( pos3(), t );
    if( wanted_range <= range_min ||
        ( wanted_range <= range_day &&
          g->m.ambient_light_at( t ) > g->natural_light_level() ) ) {
        if( g->m.ambient_light_at( t ) > g->natural_light_level() ) {
            return g->m.sees( pos3(), t, wanted_range, bresen1, bresen2 );
        } else {
            return g->m.sees( pos3(), t, range_min, bresen1, bresen2 );
        }
    } else {
        return false;
    }
}
Ejemplo n.º 13
0
void event::actualize()
{
    switch( type ) {
        case EVENT_HELP:
            debugmsg("Currently disabled while NPC and monster factions are being rewritten.");
        /*
        {
            int num = 1;
            if( faction_id >= 0 ) {
                num = rng( 1, 6 );
            }
            for( int i = 0; i < num; i++ ) {
                npc *temp = new npc();
                temp->normalize();
                if( faction_id != -1 ) {
                    faction *fac = g->faction_by_id( faction_id );
                    if( fac ) {
                        temp->randomize_from_faction( fac );
                    } else {
                        debugmsg( "EVENT_HELP run with invalid faction_id" );
                        temp->randomize();
                    }
                } else {
                    temp->randomize();
                }
                temp->attitude = NPCATT_DEFEND;
                // important: npc::spawn_at must be called to put the npc into the overmap
                temp->spawn_at( g->get_abs_levx(), g->get_abs_levy(), g->get_abs_levz() );
                // spawn at the border of the reality bubble, outside of the players view
                if( one_in( 2 ) ) {
                    temp->posx = rng( 0, SEEX * MAPSIZE - 1 );
                    temp->posy = rng( 0, 1 ) * SEEY * MAPSIZE;
                } else {
                    temp->posx = rng( 0, 1 ) * SEEX * MAPSIZE;
                    temp->posy = rng( 0, SEEY * MAPSIZE - 1 );
                }
                // And tell the npc to go to the player.
                temp->goal.x = g->om_global_location().x;
                temp->goal.y = g->om_global_location().y;
                // The npcs will be loaded later by game::load_npcs()
            }
        }
        */
        break;

  case EVENT_ROBOT_ATTACK: {
   if (rl_dist(g->get_abs_levx(), g->get_abs_levy(), map_point.x, map_point.y) <= 4) {
    mtype *robot_type = GetMType("mon_tripod");
    if (faction_id == 0) { // The cops!
     if (one_in(2)) {
         robot_type = GetMType("mon_copbot");
     } else {
         robot_type = GetMType("mon_riotbot");
     }

     g->u.add_memorial_log(pgettext("memorial_male", "Became wanted by the police!"),
                           pgettext("memorial_female", "Became wanted by the police!"));
    }
    monster robot(robot_type);
    int robx = (g->get_abs_levx() > map_point.x ? 0 - SEEX * 2 : SEEX * 4),
        roby = (g->get_abs_levy() > map_point.y ? 0 - SEEY * 2 : SEEY * 4);
    robot.spawn(robx, roby);
    g->add_zombie(robot);
   }
  } break;

  case EVENT_SPAWN_WYRMS: {
   if (g->levz >= 0)
    return;
   g->u.add_memorial_log(pgettext("memorial_male", "Awoke a group of dark wyrms!"),
                         pgettext("memorial_female", "Awoke a group of dark wyrms!"));
   monster wyrm(GetMType("mon_dark_wyrm"));
   int num_wyrms = rng(1, 4);
   for (int i = 0; i < num_wyrms; i++) {
    int tries = 0;
    int monx = -1, mony = -1;
    do {
     monx = rng(0, SEEX * MAPSIZE);
     mony = rng(0, SEEY * MAPSIZE);
     tries++;
    } while (tries < 10 && !g->is_empty(monx, mony) &&
             rl_dist(g->u.posx, g->u.posy, monx, mony) <= 2);
    if (tries < 10) {
     wyrm.spawn(monx, mony);
     g->add_zombie(wyrm);
    }
   }
   if (!one_in(25)) // They just keep coming!
    g->add_event(EVENT_SPAWN_WYRMS, int(calendar::turn) + rng(15, 25));
  } break;

  case EVENT_AMIGARA: {
   g->u.add_memorial_log(pgettext("memorial_male", "Angered a group of amigara horrors!"),
                         pgettext("memorial_female", "Angered a group of amigara horrors!"));
   int num_horrors = rng(3, 5);
   int faultx = -1, faulty = -1;
   bool horizontal = false;
   for (int x = 0; x < SEEX * MAPSIZE && faultx == -1; x++) {
    for (int y = 0; y < SEEY * MAPSIZE && faulty == -1; y++) {
     if (g->m.ter(x, y) == t_fault) {
      faultx = x;
      faulty = y;
      if (g->m.ter(x - 1, y) == t_fault || g->m.ter(x + 1, y) == t_fault)
       horizontal = true;
      else
       horizontal = false;
     }
    }
   }
   monster horror(GetMType("mon_amigara_horror"));
   for (int i = 0; i < num_horrors; i++) {
    int tries = 0;
    int monx = -1, mony = -1;
    do {
     if (horizontal) {
      monx = rng(faultx, faultx + 2 * SEEX - 8);
      for (int n = -1; n <= 1; n++) {
       if (g->m.ter(monx, faulty + n) == t_rock_floor)
        mony = faulty + n;
      }
     } else { // Vertical fault
      mony = rng(faulty, faulty + 2 * SEEY - 8);
      for (int n = -1; n <= 1; n++) {
       if (g->m.ter(faultx + n, mony) == t_rock_floor)
        monx = faultx + n;
      }
     }
     tries++;
    } while ((monx == -1 || mony == -1 || g->is_empty(monx, mony)) &&
             tries < 10);
    if (tries < 10) {
     horror.spawn(monx, mony);
     g->add_zombie(horror);
    }
   }
  } break;

  case EVENT_ROOTS_DIE:
   g->u.add_memorial_log(pgettext("memorial_male", "Destroyed a triffid grove."),
                         pgettext("memorial_female", "Destroyed a triffid grove."));
   for (int x = 0; x < SEEX * MAPSIZE; x++) {
    for (int y = 0; y < SEEY * MAPSIZE; y++) {
     if (g->m.ter(x, y) == t_root_wall && one_in(3))
      g->m.ter_set(x, y, t_underbrush);
    }
   }
   break;

  case EVENT_TEMPLE_OPEN: {
   g->u.add_memorial_log(pgettext("memorial_male", "Opened a strange temple."),
                         pgettext("memorial_female", "Opened a strange temple."));
   bool saw_grate = false;
   for (int x = 0; x < SEEX * MAPSIZE; x++) {
    for (int y = 0; y < SEEY * MAPSIZE; y++) {
     if (g->m.ter(x, y) == t_grate) {
      g->m.ter_set(x, y, t_stairs_down);
      if (!saw_grate && g->u_see(x, y))
       saw_grate = true;
     }
    }
   }
   if (saw_grate)
    add_msg(_("The nearby grates open to reveal a staircase!"));
  } break;

  case EVENT_TEMPLE_FLOOD: {
   bool flooded = false;

   ter_id flood_buf[SEEX*MAPSIZE][SEEY*MAPSIZE];
   for (int x = 0; x < SEEX * MAPSIZE; x++) {
    for (int y = 0; y < SEEY * MAPSIZE; y++)
     flood_buf[x][y] = g->m.ter(x, y);
   }
   for (int x = 0; x < SEEX * MAPSIZE; x++) {
    for (int y = 0; y < SEEY * MAPSIZE; y++) {
     if (g->m.ter(x, y) == t_water_sh) {
      bool deepen = false;
      for (int wx = x - 1;  wx <= x + 1 && !deepen; wx++) {
       for (int wy = y - 1;  wy <= y + 1 && !deepen; wy++) {
        if (g->m.ter(wx, wy) == t_water_dp)
         deepen = true;
       }
      }
      if (deepen) {
       flood_buf[x][y] = t_water_dp;
       flooded = true;
      }
     } else if (g->m.ter(x, y) == t_rock_floor) {
      bool flood = false;
      for (int wx = x - 1;  wx <= x + 1 && !flood; wx++) {
       for (int wy = y - 1;  wy <= y + 1 && !flood; wy++) {
        if (g->m.ter(wx, wy) == t_water_dp || g->m.ter(wx, wy) == t_water_sh)
         flood = true;
       }
      }
      if (flood) {
       flood_buf[x][y] = t_water_sh;
       flooded = true;
      }
     }
    }
   }
   if (!flooded)
    return; // We finished flooding the entire chamber!
// Check if we should print a message
   if (flood_buf[g->u.posx][g->u.posy] != g->m.ter(g->u.posx, g->u.posy)) {
    if (flood_buf[g->u.posx][g->u.posy] == t_water_sh) {
     add_msg(m_warning, _("Water quickly floods up to your knees."));
     g->u.add_memorial_log(pgettext("memorial_male", "Water level reached knees."),
                           pgettext("memorial_female", "Water level reached knees."));
    } else { // Must be deep water!
     add_msg(m_warning, _("Water fills nearly to the ceiling!"));
     g->u.add_memorial_log(pgettext("memorial_male", "Water level reached the ceiling."),
                           pgettext("memorial_female", "Water level reached the ceiling."));
     g->plswim(g->u.posx, g->u.posy);
    }
   }
// flood_buf is filled with correct tiles; now copy them back to g->m
   for (int x = 0; x < SEEX * MAPSIZE; x++) {
    for (int y = 0; y < SEEY * MAPSIZE; y++)
       g->m.ter_set(x, y, flood_buf[x][y]);
   }
   g->add_event(EVENT_TEMPLE_FLOOD, int(calendar::turn) + rng(2, 3));
  } break;

  case EVENT_TEMPLE_SPAWN: {
   std::string montype = "mon_null";
   switch (rng(1, 4)) {
    case 1: montype = "mon_sewer_snake";  break;
    case 2: montype = "mon_centipede";    break;
    case 3: montype = "mon_dermatik";     break;
    case 4: montype = "mon_spider_widow_giant"; break;
   }
   monster spawned( GetMType(montype) );
   int tries = 0, x, y;
   do {
    x = rng(g->u.posx - 5, g->u.posx + 5);
    y = rng(g->u.posy - 5, g->u.posy + 5);
    tries++;
   } while (tries < 20 && !g->is_empty(x, y) &&
            rl_dist(x, y, g->u.posx, g->u.posy) <= 2);
   if (tries < 20) {
    spawned.spawn(x, y);
    g->add_zombie(spawned);
   }
  } break;

  default:
   break; // Nothing happens for other events
 }
}
Ejemplo n.º 14
0
bool leap_actor::call( monster &z ) const
{
    if( !z.can_act() ) {
        return false;
    }

    std::vector<tripoint> options;
    tripoint target = z.move_target();
    float best_float = trig_dist( z.pos(), target );
    if( best_float < min_consider_range || best_float > max_consider_range ) {
        return false;
    }

    // We wanted the float for range check
    // int here will make the jumps more random
    int best = ( int )best_float;
    if( !allow_no_target && z.attack_target() == nullptr ) {
        return false;
    }

    for( const tripoint &dest : g->m.points_in_radius( z.pos(), max_range ) ) {
        if( dest == z.pos() ) {
            continue;
        }
        if( !z.sees( dest ) ) {
            continue;
        }
        if( !g->is_empty( dest ) ) {
            continue;
        }
        int cur_dist = rl_dist( target, dest );
        if( cur_dist > best ) {
            continue;
        }
        if( trig_dist( z.pos(), dest ) < min_range ) {
            continue;
        }
        bool blocked_path = false;
        // check if monster has a clear path to the proposed point
        std::vector<tripoint> line = g->m.find_clear_path( z.pos(), dest );
        for( auto &i : line ) {
            if( g->m.impassable( i ) ) {
                blocked_path = true;
                break;
            }
        }
        if( blocked_path ) {
            continue;
        }

        if( cur_dist < best ) {
            // Better than any earlier one
            options.clear();
        }

        options.push_back( dest );
        best = cur_dist;
    }

    if( options.empty() ) {
        return false;    // Nowhere to leap!
    }

    z.moves -= move_cost;
    const tripoint chosen = random_entry( options );
    bool seen = g->u.sees( z ); // We can see them jump...
    z.setpos( chosen );
    seen |= g->u.sees( z ); // ... or we can see them land
    if( seen ) {
        add_msg( _( "The %s leaps!" ), z.name().c_str() );
    }

    return true;
}
Ejemplo n.º 15
0
void gun_actor::shoot( monster &z, Creature &target ) const
{
    // Make sure our ammo isn't weird.
    if( z.ammo[ammo_type] > max_ammo ) {
        debugmsg( "Generated too much ammo (%d) of type %s for %s in gun_actor::shoot",
                  z.ammo[ammo_type], ammo_type.c_str(), z.name().c_str() );
        z.ammo[ammo_type] = max_ammo;
    }

    const bool require_targeting = ( require_targeting_player && target.is_player() ) ||
                                   ( require_targeting_npc && target.is_npc() ) ||
                                   ( require_targeting_monster && target.is_monster() );
    const bool not_targeted = require_targeting && !z.has_effect( effect_targeted );
    const bool not_laser_locked = require_targeting && laser_lock &&
                                  !target.has_effect( effect_was_laserlocked );

    if( not_targeted || not_laser_locked ) {
        if( !targeting_sound.empty() ) {
            sounds::sound( z.pos(), targeting_volume, _( targeting_sound.c_str() ) );
        }
        if( not_targeted ) {
            z.add_effect( effect_targeted, targeting_timeout );
        }
        if( not_laser_locked ) {
            target.add_effect( effect_laserlocked, 5 );
            target.add_effect( effect_was_laserlocked, 5 );
            target.add_msg_if_player( m_warning,
                                      _( "You're not sure why you've got a laser dot on you..." ) );
        }

        z.moves -= targeting_cost;
        return;
    }

    // It takes a while
    z.moves -= move_cost;

    if( z.ammo[ammo_type] <= 0 && !no_ammo_sound.empty() ) {
        sounds::sound( z.pos(), 10, _( no_ammo_sound.c_str() ) );
        return;
    }

    if( g->u.sees( z ) ) {
        add_msg( m_warning, _( description.c_str() ) );
    }

    npc tmp;
    tmp.name = _( "The " ) + z.name();
    tmp.set_fake( true );
    tmp.recoil = 0;
    tmp.driving_recoil = 0;
    tmp.setpos( z.pos() );
    tmp.str_max = fake_str;
    tmp.dex_max = fake_dex;
    tmp.int_max = fake_int;
    tmp.per_max = fake_per;
    tmp.str_cur = fake_str;
    tmp.dex_cur = fake_dex;
    tmp.int_cur = fake_int;
    tmp.per_cur = fake_per;
    tmp.weapon = item( gun_type, 0 );
    tmp.weapon.set_curammo( ammo_type );
    tmp.weapon.charges = z.ammo[ammo_type];
    if( z.friendly != 0 ) {
        tmp.attitude = NPCATT_DEFEND;
    } else {
        tmp.attitude = NPCATT_KILL;
    }

    for( const auto &pr : fake_skills ) {
        tmp.skillLevel( pr.first ).level( pr.second );
    }

    const auto distance = rl_dist( z.pos(), target.pos() );
    int burst_size = std::min( burst_limit, tmp.weapon.burst_size() );
    if( distance > range_no_burst || burst_size < 1 ) {
        burst_size = 1;
    }

    tmp.fire_gun( target.pos(), burst_size );
    z.ammo[ammo_type] = tmp.weapon.charges;
    if( require_targeting ) {
        z.add_effect( effect_targeted, targeting_timeout_extend );
    }

    if( laser_lock ) {
        // To prevent spamming laser locks when the player can tank that stuff somehow
        target.add_effect( effect_was_laserlocked, 5 );
    }
}
Ejemplo n.º 16
0
void monster::plan(const std::vector<int> &friendlies)
{
    int sightrange = g->light_level();
    int closest = -1;
    int dist = 1000;
    int tc = 0;
    int stc = 0;
    bool fleeing = false;
    if (friendly != 0) { // Target monsters, not the player!
        for (int i = 0, numz = g->num_zombies(); i < numz; i++) {
            monster *tmp = &(g->zombie(i));
            if (tmp->friendly == 0) {
                int d = rl_dist(posx(), posy(), tmp->posx(), tmp->posy());
                if (d < dist && g->m.sees(posx(), posy(), tmp->posx(), tmp->posy(), sightrange, tc)) {
                    closest = i;
                    dist = d;
                    stc = tc;
                }
            }
        }

        if (has_effect("docile")) {
            closest = -1;
        }

        if (closest >= 0) {
            set_dest(g->zombie(closest).posx(), g->zombie(closest).posy(), stc);
        } else if (friendly > 0 && one_in(3)) {
            // Grow restless with no targets
            friendly--;
        } else if (friendly < 0 &&  sees_player( tc ) ) {
            if (rl_dist(posx(), posy(), g->u.posx, g->u.posy) > 2) {
                set_dest(g->u.posx, g->u.posy, tc);
            } else {
                plans.clear();
            }
        }
        return;
    }

    // If we can see, and we can see a character, move toward them or flee.
    if (can_see() && sees_player( tc ) ) {
        dist = rl_dist(posx(), posy(), g->u.posx, g->u.posy);
        if (is_fleeing(g->u)) {
            // Wander away.
            fleeing = true;
            set_dest(posx() * 2 - g->u.posx, posy() * 2 - g->u.posy, tc);
        } else {
            // Chase the player.
            closest = -2;
            stc = tc;
        }
    }

    for (int i = 0; i < g->active_npc.size(); i++) {
        npc *me = (g->active_npc[i]);
        int medist = rl_dist(posx(), posy(), me->posx, me->posy);
        if ((medist < dist || (!fleeing && is_fleeing(*me))) &&
                (can_see() &&
                 g->m.sees(posx(), posy(), me->posx, me->posy, sightrange, tc))) {
            if (is_fleeing(*me)) {
                fleeing = true;
                set_dest(posx() * 2 - me->posx, posy() * 2 - me->posy, tc);
                \
            } else {
                closest = i;
                stc = tc;
            }
            dist = medist;
        }
    }

    if (!fleeing) {
        fleeing = attitude() == MATT_FLEE;
        if (can_see()) {
            for (int f = 0, numf = friendlies.size(); f < numf; f++) {
                const int i = friendlies[f];
                monster *mon = &(g->zombie(i));
                int mondist = rl_dist(posx(), posy(), mon->posx(), mon->posy());
                if (mondist < dist &&
                        g->m.sees(posx(), posy(), mon->posx(), mon->posy(), sightrange, tc)) {
                    dist = mondist;
                    if (fleeing) {
                        wandx = posx() * 2 - mon->posx();
                        wandy = posy() * 2 - mon->posy();
                        wandf = 40;
                    } else {
                        closest = -3 - i;
                        stc = tc;
                    }
                }
            }
        }

        if (closest == -2) {
            if (one_in(2)) {//random for the diversity of the trajectory
                ++stc;
            } else {
                --stc;
            }
            set_dest(g->u.posx, g->u.posy, stc);
        }
        else if (closest <= -3)
            set_dest(g->zombie(-3 - closest).posx(), g->zombie(-3 - closest).posy(), stc);
        else if (closest >= 0)
            set_dest(g->active_npc[closest]->posx, g->active_npc[closest]->posy, stc);
    }
}
Ejemplo n.º 17
0
void monster::plan(game *g)
{
 int sightrange = g->light_level();
 int closest = -1;
 int dist = 1000;
 int tc, stc;
 bool fleeing = false;
 if (friendly != 0) {	// Target monsters, not the player!
  for (int i = 0; i < g->z.size(); i++) {
   monster *tmp = &(g->z[i]);
   if (tmp->friendly == 0 && rl_dist(posx, posy, tmp->posx, tmp->posy) < dist &&
       g->m.sees(posx, posy, tmp->posx, tmp->posy, sightrange, tc)) {
    closest = i;
    dist = rl_dist(posx, posy, tmp->posx, tmp->posy);
    stc = tc;
   }
  }
  if (has_effect(ME_DOCILE))
   closest = -1;
  if (closest >= 0)
   set_dest(g->z[closest].posx, g->z[closest].posy, stc);
  else if (friendly > 0 && one_in(3))	// Grow restless with no targets
   friendly--;
  else if (friendly < 0 && g->sees_u(posx, posy, tc)) {
   if (rl_dist(posx, posy, g->u.posx, g->u.posy) > 2)
    set_dest(g->u.posx, g->u.posy, tc);
   else
    plans.clear();
  }
  return;
 }
 if (is_fleeing(g->u) && can_see() && g->sees_u(posx, posy, tc)) {
  fleeing = true;
  wandx = posx * 2 - g->u.posx;
  wandy = posy * 2 - g->u.posy;
  wandf = 40;
  dist = rl_dist(posx, posy, g->u.posx, g->u.posy);
 }
// If we can see, and we can see a character, start moving towards them
 if (!is_fleeing(g->u) && can_see() && g->sees_u(posx, posy, tc)) {
  dist = rl_dist(posx, posy, g->u.posx, g->u.posy);
  closest = -2;
  stc = tc;
 }
 for (int i = 0; i < g->active_npc.size(); i++) {
  npc *me = (g->active_npc[i]);
  int medist = rl_dist(posx, posy, me->posx, me->posy);
  if ((medist < dist || (!fleeing && is_fleeing(*me))) &&
      (can_see() &&
       g->m.sees(posx, posy, me->posx, me->posy, sightrange, tc))) {
   if (is_fleeing(*me)) {
    fleeing = true;
    wandx = posx * 2 - me->posx;
    wandy = posy * 2 - me->posy;
    wandf = 40;
    dist = medist;
   } else if (can_see() &&
              g->m.sees(posx, posy, me->posx, me->posy, sightrange, tc)) {
    dist = rl_dist(posx, posy, me->posx, me->posy);
    closest = i;
    stc = tc;
   }
  }
 }
 if (!fleeing) {
  fleeing = attitude() == MATT_FLEE;
  for (int i = 0; i < g->z.size(); i++) {
   monster *mon = &(g->z[i]);
   int mondist = rl_dist(posx, posy, mon->posx, mon->posy);
   if (mon->friendly != 0 && mondist < dist && can_see() &&
       g->m.sees(posx, posy, mon->posx, mon->posy, sightrange, tc)) {
    dist = mondist;
    if (fleeing) {
     wandx = posx * 2 - mon->posx;
     wandy = posy * 2 - mon->posy;
     wandf = 40;
    } else {
     closest = -3 - i;
     stc = tc;
    }
   }
  }
 }
 if (!fleeing) {
  if (closest == -2)
   set_dest(g->u.posx, g->u.posy, stc);
  else if (closest <= -3)
   set_dest(g->z[-3 - closest].posx, g->z[-3 - closest].posy, stc);
  else if (closest >= 0)
   set_dest(g->active_npc[closest]->posx, g->active_npc[closest]->posy, stc);
 }
}
Ejemplo n.º 18
0
bool Creature::compare_by_dist_to_point::operator()( const Creature* const a, const Creature* const b ) const
{
    return rl_dist( a->pos(), center ) < rl_dist( b->pos(), center );
}
Ejemplo n.º 19
0
void sounds::process_sound_markers( player *p )
{
    bool is_deaf = p->is_deaf();
    const float volume_multiplier = p->hearing_ability();
    const int safe_volume = p->worn_with_flag("PARTIAL_DEAF") ? 100 : 9999;
    const int weather_vol = weather_data( g->weather ).sound_attn;
    for( const auto &sound_event_pair : sounds_since_last_turn ) {
        const int volume = std::min(safe_volume, (int)(sound_event_pair.second.volume * volume_multiplier));
        const std::string& sfx_id = sound_event_pair.second.id;
        const std::string& sfx_variant = sound_event_pair.second.variant;
        const int max_volume = std::max( volume, sound_event_pair.second.volume );  // For deafness checks
        int dist = rl_dist( p->pos(), sound_event_pair.first );
        bool ambient = sound_event_pair.second.ambient;
        // Too far away, we didn't hear it!
        if( dist > volume ) {
            continue;
        }
        if( is_deaf ) {
            // Has to be here as well to work for stacking deafness (loud noises prolong deafness)
            if( !p->is_immune_effect( effect_deaf )
                    && rng( ( max_volume - dist ) / 2, ( max_volume - dist ) ) >= 150 ) {
                // Prolong deafness, but not as much as if it was freshly applied
                int duration = std::min( 40, ( max_volume - dist - 130 ) / 8 );
                p->add_effect( effect_deaf, duration );
                if( !p->has_trait( "DEADENED" ) ) {
                    p->add_msg_if_player( m_bad, _( "Your eardrums suddenly ache!" ) );
                    if( p->get_pain() < 10 ) {
                        p->mod_pain( rng( 0, 2 ) );
                    }
                }
            }
            // We're deaf, skip rest of processing.
            continue;
        }
        // Player volume meter includes all sounds from their tile and adjacent tiles
        // TODO: Add noises from vehicle player is in.
        if( dist <= 1 ) {
            p->volume = std::max( p->volume, volume );
        }
        // Check for deafness
        if( !p->is_immune_effect( effect_deaf ) && rng((max_volume - dist) / 2, (max_volume - dist)) >= 150 ) {
            int duration = (max_volume - dist - 130) / 4;
            p->add_effect( effect_deaf, duration );
            if( p->is_deaf() ) {
                // Need to check for actual deafness
                is_deaf = true;
                sfx::do_hearing_loss( duration );
                continue;
            }
        }
        // At this point we are dealing with attention (as opposed to physical effects)
        // so reduce volume by the amount of ambient noise from the weather.
        const int mod_vol = ( sound_event_pair.second.volume - weather_vol ) * volume_multiplier;
        // The noise was drowned out by the surroundings.
        if( mod_vol - dist < 0 ) {
            continue;
        }
        // See if we need to wake someone up
        if( p->has_effect( effect_sleep ) ) {
            if( ( !( p->has_trait( "HEAVYSLEEPER" ) ||
                     p->has_trait( "HEAVYSLEEPER2" ) ) && dice( 2, 15 ) < mod_vol - dist ) ||
                    ( p->has_trait( "HEAVYSLEEPER" ) && dice( 3, 15 ) < mod_vol - dist ) ||
                    ( p->has_trait( "HEAVYSLEEPER2" ) && dice( 6, 15 ) < mod_vol - dist ) ) {
                //Not kidding about sleep-thru-firefight
                p->wake_up();
                add_msg( m_warning, _( "Something is making noise." ) );
            } else {
                continue;
            }
        }
        const tripoint &pos = sound_event_pair.first;
        const std::string &description = sound_event_pair.second.description;
        if( !ambient && ( pos != p->pos() ) && !g->m.pl_sees( pos, dist ) ) {
            if( p->activity.ignore_trivial != true ) {
                std::string query;
                if( description.empty() ) {
                    query = _( "Heard a noise!" );
                } else {
                    query = string_format( _( "Heard %s!" ),
                                           sound_event_pair.second.description.c_str() );
                }
                if( g->cancel_activity_or_ignore_query( query.c_str() ) ) {
                    p->activity.ignore_trivial = true;
                    for( auto activity : p->backlog ) {
                        activity.ignore_trivial = true;
                    }
                }
            }
        }
        // Only print a description if it exists
        if( !description.empty() ) {
            // If it came from us, don't print a direction
            if( pos == p->pos() ) {
                add_msg( _( "You hear %s" ), description.c_str() );
            } else {
                // Else print a direction as well
                std::string direction = direction_name( direction_from( p->pos(), pos ) );
                add_msg( m_warning, _( "From the %s you hear %s" ), direction.c_str(), description.c_str() );
            }
        }
        // Play the sound effect, if any.
        if( !sfx_id.empty() ) {
            // for our sfx API, 100 is "normal" volume, so scale accordingly
            int heard_volume = sfx::get_heard_volume( pos );
            sfx::play_variant_sound( sfx_id, sfx_variant, heard_volume );
            //add_msg("Playing sound effect %s, %s, %d", sfx_id.c_str(), sfx_variant.c_str(), heard_volume);
        }
        // If Z coord is different, draw even when you can see the source
        const bool diff_z = pos.z != p->posz();
        // Place footstep markers.
        if( pos == p->pos() || p->sees( pos ) ) {
            // If we are or can see the source, don't draw a marker.
            continue;
        }
        int err_offset;
        if( mod_vol / dist < 2 ) {
            err_offset = 3;
        } else if( mod_vol / dist < 3 ) {
            err_offset = 2;
        } else {
            err_offset = 1;
        }
        // Enumerate the valid points the player *cannot* see.
        // Unless the source is on a different z-level, then any point is fine
        std::vector<tripoint> unseen_points;
        tripoint newp = pos;
        int &newx = newp.x;
        int &newy = newp.y;
        for( newx = pos.x - err_offset; newx <= pos.x + err_offset; newx++ ) {
            for( newy = pos.y - err_offset; newy <= pos.y + err_offset; newy++ ) {
                if( diff_z || !p->sees( newp ) ) {
                    unseen_points.emplace_back( newp );
                }
            }
        }
        // Then place the sound marker in a random one.
        if( !unseen_points.empty() ) {
            sound_markers.emplace( random_entry( unseen_points ),
                                   sound_event_pair.second );
        }
    }
    sounds_since_last_turn.clear();
}
Ejemplo n.º 20
0
void map::generate_lightmap()
{
    memset(lm, 0, sizeof(lm));
    memset(sm, 0, sizeof(sm));

    /* Bulk light sources wastefully cast rays into neighbors; a burning hospital can produce
         significant slowdown, so for stuff like fire and lava:
     * Step 1: Store the position and luminance in buffer via add_light_source, for efficient
         checking of neighbors.
     * Step 2: After everything else, iterate buffer and apply_light_source only in non-redundant
         directions
     * Step 3: Profit!
     */
    memset(light_source_buffer, 0, sizeof(light_source_buffer));

    constexpr int dir_x[] = {  0, -1 , 1, 0 };   //    [0]
    constexpr int dir_y[] = { -1,  0 , 0, 1 };   // [1][X][2]
    constexpr int dir_d[] = { 180, 270, 0, 90 }; //    [3]

    const bool  u_is_inside    = !is_outside(g->u.posx(), g->u.posy());
    const float natural_light  = g->natural_light_level();
    const float hl             = natural_light / 2;

    if (natural_light > LIGHT_SOURCE_BRIGHT) {
        // Apply sunlight, first light source so just assign
        for (int sx = DAYLIGHT_LEVEL - hl; sx < LIGHTMAP_CACHE_X - hl; ++sx) {
            for (int sy = DAYLIGHT_LEVEL - hl; sy < LIGHTMAP_CACHE_Y - hl; ++sy) {
                // In bright light indoor light exists to some degree
                if (!is_outside(sx, sy)) {
                    lm[sx][sy] = LIGHT_AMBIENT_LOW;
                } else if (g->u.posx() == sx && g->u.posy() == sy ) {
                    //Only apply daylight on square where player is standing to avoid flooding
                    // the lightmap  when in less than total sunlight.
                    lm[sx][sy] = natural_light;
                }
            }
        }
    }

    apply_character_light( g->u );
    for( auto &n : g->active_npc ) {
        apply_character_light( *n );
    }

    // LIGHTMAP_CACHE_X = MAPSIZE * SEEX
    // LIGHTMAP_CACHE_Y = MAPSIZE * SEEY
    // Traverse the submaps in order
    for (int smx = 0; smx < my_MAPSIZE; ++smx) {
        for (int smy = 0; smy < my_MAPSIZE; ++smy) {
            auto const cur_submap = get_submap_at_grid( smx, smy );

            for (int sx = 0; sx < SEEX; ++sx) {
                for (int sy = 0; sy < SEEY; ++sy) {
                    const int x = sx + smx * SEEX;
                    const int y = sy + smy * SEEY;
                    // When underground natural_light is 0, if this changes we need to revisit
                    // Only apply this whole thing if the player is inside,
                    // buildings will be shadowed when outside looking in.
                    if (natural_light > LIGHT_SOURCE_BRIGHT && u_is_inside && !is_outside(x, y)) {
                        // Apply light sources for external/internal divide
                        for(int i = 0; i < 4; ++i) {
                            if (INBOUNDS(x + dir_x[i], y + dir_y[i]) &&
                                is_outside(x + dir_x[i], y + dir_y[i])) {
                                lm[x][y] = natural_light;

                                if (light_transparency(x, y) > LIGHT_TRANSPARENCY_SOLID) {
                                    apply_light_arc(x, y, dir_d[i], natural_light);
                                }
                            }
                        }
                    }

                    if (cur_submap->lum[sx][sy]) {
                        auto items = i_at(x, y);
                        add_light_from_items(x, y, items.begin(), items.end());
                    }

                    const ter_id terrain = cur_submap->ter[sx][sy];
                    if (terrain == t_lava) {
                        add_light_source(x, y, 50 );
                    } else if (terrain == t_console) {
                        add_light_source(x, y, 3 );
                    } else if (terrain == t_utility_light) {
                        add_light_source(x, y, 35 );
                    }

                    for( auto &fld : cur_submap->fld[sx][sy] ) {
                        const field_entry *cur = &fld.second;
                        // TODO: [lightmap] Attach light brightness to fields
                        switch(cur->getFieldType()) {
                        case fd_fire:
                            if (3 == cur->getFieldDensity()) {
                                add_light_source(x, y, 160);
                            } else if (2 == cur->getFieldDensity()) {
                                add_light_source(x, y, 60);
                            } else {
                                add_light_source(x, y, 16);
                            }
                            break;
                        case fd_fire_vent:
                        case fd_flame_burst:
                            add_light_source(x, y, 8);
                            break;
                        case fd_electricity:
                        case fd_plasma:
                            if (3 == cur->getFieldDensity()) {
                                add_light_source(x, y, 8);
                            } else if (2 == cur->getFieldDensity()) {
                                add_light_source(x, y, 1);
                            } else {
                                apply_light_source(x, y, LIGHT_SOURCE_LOCAL,
                                                   trigdist);    // kinda a hack as the square will still get marked
                            }
                            break;
                        case fd_incendiary:
                            if (3 == cur->getFieldDensity()) {
                                add_light_source(x, y, 30);
                            } else if (2 == cur->getFieldDensity()) {
                                add_light_source(x, y, 16);
                            } else {
                                add_light_source(x, y, 8);
                            }
                            break;
                        case fd_laser:
                            apply_light_source(x, y, 1, trigdist);
                            break;
                        case fd_spotlight:
                            add_light_source(x, y, 20);
                            break;
                        case fd_dazzling:
                            add_light_source(x, y, 2);
                            break;
                        default:
                            //Suppress warnings
                            break;
                        }
                    }
                }
            }
        }
    }

    for (size_t i = 0; i < g->num_zombies(); ++i) {
        auto &critter = g->zombie(i);
        if(critter.is_hallucination()) {
            continue;
        }
        int mx = critter.posx();
        int my = critter.posy();
        if (INBOUNDS(mx, my)) {
            if (critter.has_effect("onfire")) {
                apply_light_source(mx, my, 3, trigdist);
            }
            // TODO: [lightmap] Attach natural light brightness to creatures
            // TODO: [lightmap] Allow creatures to have light attacks (ie: eyebot)
            // TODO: [lightmap] Allow creatures to have facing and arc lights
            if (critter.type->luminance > 0) {
                apply_light_source(mx, my, critter.type->luminance, trigdist);
            }
        }
    }

    // Apply any vehicle light sources
    VehicleList vehs = get_vehicles();
    for( auto &vv : vehs ) {
        vehicle *v = vv.v;
        if(v->lights_on) {
            int dir = v->face.dir();
            float veh_luminance = 0.0;
            float iteration = 1.0;
            std::vector<int> light_indices = v->all_parts_with_feature(VPFLAG_CONE_LIGHT);
            for( auto &light_indice : light_indices ) {
                veh_luminance += ( v->part_info( light_indice ).bonus / iteration );
                iteration = iteration * 1.1;
            }
            if (veh_luminance > LL_LIT) {
                for( auto &light_indice : light_indices ) {
                    int px = vv.x + v->parts[light_indice].precalc[0].x;
                    int py = vv.y + v->parts[light_indice].precalc[0].y;
                    if(INBOUNDS(px, py)) {
                        add_light_source(px, py, SQRT_2); // Add a little surrounding light
                        apply_light_arc( px, py, dir + v->parts[light_indice].direction,
                                         veh_luminance, 45 );
                    }
                }
            }
        }
        if(v->overhead_lights_on) {
            std::vector<int> light_indices = v->all_parts_with_feature(VPFLAG_CIRCLE_LIGHT);
            for( auto &light_indice : light_indices ) {
                if( ( calendar::turn % 2 &&
                      v->part_info( light_indice ).has_flag( VPFLAG_ODDTURN ) ) ||
                    ( !( calendar::turn % 2 ) &&
                      v->part_info( light_indice ).has_flag( VPFLAG_EVENTURN ) ) ||
                    ( !v->part_info( light_indice ).has_flag( VPFLAG_EVENTURN ) &&
                      !v->part_info( light_indice ).has_flag( VPFLAG_ODDTURN ) ) ) {
                    int px = vv.x + v->parts[light_indice].precalc[0].x;
                    int py = vv.y + v->parts[light_indice].precalc[0].y;
                    if(INBOUNDS(px, py)) {
                        add_light_source( px, py, v->part_info( light_indice ).bonus );
                    }
                }
            }
        }
        // why reinvent the [lightmap] wheel
        if(v->dome_lights_on) {
            std::vector<int> light_indices = v->all_parts_with_feature(VPFLAG_DOME_LIGHT);
            for( auto &light_indice : light_indices ) {
                int px = vv.x + v->parts[light_indice].precalc[0].x;
                int py = vv.y + v->parts[light_indice].precalc[0].y;
                if(INBOUNDS(px, py)) {
                    add_light_source( px, py, v->part_info( light_indice ).bonus );
                }
            }
        }
        if(v->aisle_lights_on) {
            std::vector<int> light_indices = v->all_parts_with_feature(VPFLAG_AISLE_LIGHT);
            for( auto &light_indice : light_indices ) {
                int px = vv.x + v->parts[light_indice].precalc[0].x;
                int py = vv.y + v->parts[light_indice].precalc[0].y;
                if(INBOUNDS(px, py)) {
                    add_light_source( px, py, v->part_info( light_indice ).bonus );
                }
            }
        }
        if(v->has_atomic_lights) {
            // atomic light is always on
            std::vector<int> light_indices = v->all_parts_with_feature(VPFLAG_ATOMIC_LIGHT);
            for( auto &light_indice : light_indices ) {
                int px = vv.x + v->parts[light_indice].precalc[0].x;
                int py = vv.y + v->parts[light_indice].precalc[0].y;
                if(INBOUNDS(px, py)) {
                    add_light_source( px, py, v->part_info( light_indice ).bonus );
                }
            }
        }
        for( size_t p = 0; p < v->parts.size(); ++p ) {
            int px = vv.x + v->parts[p].precalc[0].x;
            int py = vv.y + v->parts[p].precalc[0].y;
            if( !INBOUNDS( px, py ) ) {
                continue;
            }
            if( v->part_flag( p, VPFLAG_CARGO ) && !v->part_flag( p, "COVERED" ) ) {
                add_light_from_items( px, py, v->get_items(p).begin(), v->get_items(p).end() );
            }
        }
    }

    /* Now that we have position and intensity of all bulk light sources, apply_ them
      This may seem like extra work, but take a 12x12 raging inferno:
        unbuffered: (12^2)*(160*4) = apply_light_ray x 92160
        buffered:   (12*4)*(160)   = apply_light_ray x 7680
    */
    for(int sx = 0; sx < LIGHTMAP_CACHE_X; ++sx) {
        for(int sy = 0; sy < LIGHTMAP_CACHE_Y; ++sy) {
            if ( light_source_buffer[sx][sy] > 0. ) {
                apply_light_source(sx, sy, light_source_buffer[sx][sy],
                                   ( trigdist && light_source_buffer[sx][sy] > 3. ) );
            }
        }
    }


    if (g->u.has_active_bionic("bio_night") ) {
        for(int sx = 0; sx < LIGHTMAP_CACHE_X; ++sx) {
            for(int sy = 0; sy < LIGHTMAP_CACHE_Y; ++sy) {
                if (rl_dist(sx, sy, g->u.posx(), g->u.posy()) < 15) {
                    lm[sx][sy] = 0;
                }
            }
        }
    }
}
Ejemplo n.º 21
0
Creature *Creature::auto_find_hostile_target( int range, int &boo_hoo, int area )
{
    Creature *target = nullptr;
    player &u = g->u; // Could easily protect something that isn't the player
    constexpr int hostile_adj = 2; // Priority bonus for hostile targets
    const int iff_dist = ( range + area ) * 3 / 2 + 6; // iff check triggers at this distance
    int iff_hangle = 15 + area; // iff safety margin (degrees). less accuracy, more paranoia
    float best_target_rating = -1.0f; // bigger is better
    int u_angle = 0;         // player angle relative to turret
    boo_hoo = 0;         // how many targets were passed due to IFF. Tragically.
    bool self_area_iff = false; // Need to check if the target is near the vehicle we're a part of
    bool area_iff = false;      // Need to check distance from target to player
    bool angle_iff = true;      // Need to check if player is in a cone between us and target
    int pldist = rl_dist( pos(), g->u.pos() );
    vehicle *in_veh = is_fake() ? veh_pointer_or_null( g->m.veh_at( pos() ) ) : nullptr;
    if( pldist < iff_dist && sees( g->u ) ) {
        area_iff = area > 0;
        angle_iff = true;
        // Player inside vehicle won't be hit by shots from the roof,
        // so we can fire "through" them just fine.
        const optional_vpart_position vp = g->m.veh_at( u.pos() );
        if( in_veh && veh_pointer_or_null( vp ) == in_veh && vp->is_inside() ) {
            angle_iff = false; // No angle IFF, but possibly area IFF
        } else if( pldist < 3 ) {
            iff_hangle = (pldist == 2 ? 30 : 60);    // granularity increases with proximity
        }
        u_angle = g->m.coord_to_angle(posx(), posy(), u.posx(), u.posy());
    }

    if( area > 0 && in_veh != nullptr ) {
        self_area_iff = true;
    }

    std::vector<Creature*> targets = g->get_creatures_if( [&]( const Creature &critter ) {
        if( const monster *const mon_ptr = dynamic_cast<const monster*>( &critter ) ) {
            // friendly to the player, not a target for us
            return mon_ptr->friendly == 0;
        }
        if( const npc *const npc_ptr = dynamic_cast<const npc*>( &critter ) ) {
            // friendly to the player, not a target for us
            return npc_ptr->get_attitude() == NPCATT_KILL;
        }
        //@todo: what about g->u?
        return false;
    } );
    for( auto &m : targets ) {
        if( !sees( *m ) ) {
            // can't see nor sense it
            continue;
        }
        int dist = rl_dist( pos(), m->pos() ) + 1; // rl_dist can be 0
        if( dist > range + 1 || dist < area ) {
            // Too near or too far
            continue;
        }
        // Prioritize big, armed and hostile stuff
        float mon_rating = m->power_rating();
        float target_rating = mon_rating / dist;
        if( mon_rating + hostile_adj <= 0 ) {
            // We wouldn't attack it even if it was hostile
            continue;
        }

        if( in_veh != nullptr && veh_pointer_or_null( g->m.veh_at( m->pos() ) ) == in_veh ) {
            // No shooting stuff on vehicle we're a part of
            continue;
        }
        if( area_iff && rl_dist( u.pos(), m->pos() ) <= area ) {
            // Player in AoE
            boo_hoo++;
            continue;
        }
        // Hostility check can be expensive, but we need to inform the player of boo_hoo
        // only when the target is actually "hostile enough"
        bool maybe_boo = false;
        if( angle_iff ) {
            int tangle = g->m.coord_to_angle(posx(), posy(), m->posx(), m->posy());
            int diff = abs(u_angle - tangle);
            // Player is in the angle and not too far behind the target
            if( ( diff + iff_hangle > 360 || diff < iff_hangle ) &&
                ( dist * 3 / 2 + 6 > pldist ) ) {
                maybe_boo = true;
            }
        }
        if( !maybe_boo && ( ( mon_rating + hostile_adj ) / dist <= best_target_rating ) ) {
            // "Would we skip the target even if it was hostile?"
            // Helps avoid (possibly expensive) attitude calculation
            continue;
        }
        if( m->attitude_to( u ) == A_HOSTILE ) {
            target_rating = ( mon_rating + hostile_adj ) / dist;
            if( maybe_boo ) {
                boo_hoo++;
                continue;
            }
        }
        if( target_rating <= best_target_rating || target_rating <= 0 ) {
            continue; // Handle this late so that boo_hoo++ can happen
        }
        // Expensive check for proximity to vehicle
        if( self_area_iff && overlaps_vehicle( in_veh->get_points(), m->pos(), area ) ) {
            continue;
        }

        target = m;
        best_target_rating = target_rating;
    }
    return target;
}
Ejemplo n.º 22
0
bool mission::is_complete( const int _npc_id ) const
{
    if( status == mission_status::success ) {
        return true;
    }

    auto &u = g->u;
    switch( type->goal ) {
        case MGOAL_GO_TO:
            {
                const tripoint cur_pos = g->u.global_omt_location();
                return ( rl_dist( cur_pos, target ) <= 1 );
            }
            break;

        case MGOAL_GO_TO_TYPE:
            {
                const auto cur_ter = overmap_buffer.ter( g->u.global_omt_location() );
                return cur_ter == type->target_id;
            }
            break;

        case MGOAL_FIND_ITEM:
        {
            inventory tmp_inv = u.crafting_inventory();
            // TODO: check for count_by_charges and use appropriate player::has_* function
            if (!tmp_inv.has_amount(type->item_id, item_count)) {
                return tmp_inv.has_amount( type->item_id, 1 ) && tmp_inv.has_charges( type->item_id, item_count );
            }
            if( npc_id != -1 && npc_id != _npc_id ) {
                return false;
            }
        }
            return true;

        case MGOAL_FIND_ANY_ITEM:
            return u.has_mission_item( uid ) && ( npc_id == -1 || npc_id == _npc_id );

        case MGOAL_FIND_MONSTER:
            if( npc_id != -1 && npc_id != _npc_id ) {
                return false;
            }
            for( size_t i = 0; i < g->num_zombies(); i++ ) {
                if( g->zombie( i ).mission_id == uid ) {
                    return true;
                }
            }
            return false;

        case MGOAL_RECRUIT_NPC:
            {
                npc *p = g->find_npc( target_npc_id );
                return p != nullptr && p->attitude == NPCATT_FOLLOW;
            }

        case MGOAL_RECRUIT_NPC_CLASS:
            {
                const auto npcs = overmap_buffer.get_npcs_near_player( 100 );
                for( auto & npc : npcs ) {
                    if( npc->myclass == recruit_class && npc->attitude == NPCATT_FOLLOW ) {
                        return true;
                    }
                }
                return false;
            }

        case MGOAL_FIND_NPC:
            return npc_id == _npc_id;

        case MGOAL_ASSASSINATE:
            return step >= 1;

        case MGOAL_KILL_MONSTER:
            return step >= 1;

        case MGOAL_KILL_MONSTER_TYPE:
            return g->kill_count( mtype_id( monster_type ) ) >= monster_kill_goal;

        case MGOAL_COMPUTER_TOGGLE:
            return step >= 1;

        default:
            return false;
    }
    return false;
}
Ejemplo n.º 23
0
void map::generate_lightmap()
{
    memset(lm, 0, sizeof(lm));
    memset(sm, 0, sizeof(sm));

    /* Bulk light sources wastefully cast rays into neighbors; a burning hospital can produce
         significant slowdown, so for stuff like fire and lava:
     * Step 1: Store the position and luminance in buffer via add_light_source, for efficient
         checking of neighbors.
     * Step 2: After everything else, iterate buffer and apply_light_source only in non-redundant
         directions
     * Step 3: Profit!
     */
    memset(light_source_buffer, 0, sizeof(light_source_buffer));


    const int dir_x[] = { 1, 0 , -1,  0 };
    const int dir_y[] = { 0, 1 ,  0, -1 };
    const int dir_d[] = { 180, 270, 0, 90 };
    const float held_luminance = g->u.active_light();
    const float natural_light = g->natural_light_level();

    if (natural_light > LIGHT_SOURCE_BRIGHT) {
        // Apply sunlight, first light source so just assign
        for(int sx = DAYLIGHT_LEVEL - (natural_light / 2);
            sx < LIGHTMAP_CACHE_X - (natural_light / 2); ++sx) {
            for(int sy = DAYLIGHT_LEVEL - (natural_light / 2);
                sy < LIGHTMAP_CACHE_Y - (natural_light / 2); ++sy) {
                // In bright light indoor light exists to some degree
                if (!is_outside(sx, sy)) {
                    lm[sx][sy] = LIGHT_AMBIENT_LOW;
                } else if (g->u.posx == sx && g->u.posy == sy ) {
                    //Only apply daylight on square where player is standing to avoid flooding
                    // the lightmap  when in less than total sunlight.
                    lm[sx][sy] = natural_light;
                }
            }
        }
    }

    // Apply player light sources
    if (held_luminance > LIGHT_AMBIENT_LOW) {
        apply_light_source(g->u.posx, g->u.posy, held_luminance, trigdist);
    }
    for(int sx = 0; sx < LIGHTMAP_CACHE_X; ++sx) {
        for(int sy = 0; sy < LIGHTMAP_CACHE_Y; ++sy) {
            const ter_id terrain = ter(sx, sy);
            const std::vector<item> &items = i_at(sx, sy);
            field &current_field = field_at(sx, sy);
            // When underground natural_light is 0, if this changes we need to revisit
            // Only apply this whole thing if the player is inside,
            // buildings will be shadowed when outside looking in.
            if (natural_light > LIGHT_AMBIENT_LOW && !is_outside(g->u.posx, g->u.posy) ) {
                if (!is_outside(sx, sy)) {
                    // Apply light sources for external/internal divide
                    for(int i = 0; i < 4; ++i) {
                        if (INBOUNDS(sx + dir_x[i], sy + dir_y[i]) &&
                            is_outside(sx + dir_x[i], sy + dir_y[i])) {
                            lm[sx][sy] = natural_light;

                            if (light_transparency(sx, sy) > LIGHT_TRANSPARENCY_SOLID) {
                                apply_light_arc(sx, sy, dir_d[i], natural_light);
                            }
                        }
                    }
                }
            }
            for( std::vector<item>::const_iterator itm = items.begin(); itm != items.end(); ++itm ) {

                float ilum = 0.0; // brightness
                int iwidth = 0; // 0-360 degrees. 0 is a circular light_source
                int idir = 0;   // otherwise, it's a light_arc pointed in this direction
                if ( itm->getlight(ilum, iwidth, idir ) ) {
                    if ( iwidth > 0 ) {
                        apply_light_arc( sx, sy, idir, ilum, iwidth );
                    } else {
                        add_light_source(sx, sy, ilum);
                    }
                }
            }
            if(terrain == t_lava) {
                add_light_source(sx, sy, 50 );
            }

            if(terrain == t_console) {
                add_light_source(sx, sy, 3 );
            }

            if(terrain == t_emergency_light) {
                add_light_source(sx, sy, 3 );
            }

            if(terrain == t_utility_light) {
                add_light_source(sx, sy, 35 );
            }

            field_entry *cur = NULL;
            for(std::map<field_id, field_entry *>::iterator field_list_it = current_field.getFieldStart();
                field_list_it != current_field.getFieldEnd(); ++field_list_it) {
                cur = field_list_it->second;

                if(cur == NULL) {
                    continue;
                }
                // TODO: [lightmap] Attach light brightness to fields
                switch(cur->getFieldType()) {
                case fd_fire:
                    if (3 == cur->getFieldDensity()) {
                        add_light_source(sx, sy, 160);
                    } else if (2 == cur->getFieldDensity()) {
                        add_light_source(sx, sy, 60);
                    } else {
                        add_light_source(sx, sy, 16);
                    }
                    break;
                case fd_fire_vent:
                case fd_flame_burst:
                    add_light_source(sx, sy, 8);
                    break;
                case fd_electricity:
                case fd_plasma:
                    if (3 == cur->getFieldDensity()) {
                        add_light_source(sx, sy, 8);
                    } else if (2 == cur->getFieldDensity()) {
                        add_light_source(sx, sy, 1);
                    } else {
                        apply_light_source(sx, sy, LIGHT_SOURCE_LOCAL,
                                           trigdist);    // kinda a hack as the square will still get marked
                    }
                    break;
                case fd_incendiary:
                    if (3 == cur->getFieldDensity()) {
                        add_light_source(sx, sy, 30);
                    } else if (2 == cur->getFieldDensity()) {
                        add_light_source(sx, sy, 16);
                    } else {
                        add_light_source(sx, sy, 8);
                    }
                    break;
                case fd_laser:
                    apply_light_source(sx, sy, 1, trigdist);
                    break;
                case fd_spotlight:
                    add_light_source(sx, sy, 20);
                    break;
                case fd_dazzling:
                    add_light_source(sx, sy, 2);
                    break;
                default:
                    //Suppress warnings
                    break;
                }
            }
        }
    }

    for (size_t i = 0; i < g->num_zombies(); ++i) {
        int mx = g->zombie(i).posx();
        int my = g->zombie(i).posy();
        if (INBOUNDS(mx, my)) {
            if (g->zombie(i).has_effect("onfire")) {
                apply_light_source(mx, my, 3, trigdist);
            }
            // TODO: [lightmap] Attach natural light brightness to creatures
            // TODO: [lightmap] Allow creatures to have light attacks (ie: eyebot)
            // TODO: [lightmap] Allow creatures to have facing and arc lights
            if (g->zombie(i).type->luminance > 0) {
                apply_light_source(mx, my, g->zombie(i).type->luminance, trigdist);
            }
        }
    }

    // Apply any vehicle light sources
    VehicleList vehs = get_vehicles();
    for( size_t v = 0; v < vehs.size(); ++v ) {
        if(vehs[v].v->lights_on) {
            int dir = vehs[v].v->face.dir();
            float veh_luminance = 0.0;
            float iteration = 1.0;
            std::vector<int> light_indices = vehs[v].v->all_parts_with_feature(VPFLAG_CONE_LIGHT);
            for (std::vector<int>::iterator part = light_indices.begin();
                 part != light_indices.end(); ++part) {
                veh_luminance += ( vehs[v].v->part_info(*part).bonus / iteration );
                iteration = iteration * 1.1;
            }
            if (veh_luminance > LL_LIT) {
                for (std::vector<int>::iterator part = light_indices.begin();
                     part != light_indices.end(); ++part) {
                    int px = vehs[v].x + vehs[v].v->parts[*part].precalc_dx[0];
                    int py = vehs[v].y + vehs[v].v->parts[*part].precalc_dy[0];
                    if(INBOUNDS(px, py)) {
                        apply_light_arc(px, py, dir + vehs[v].v->parts[*part].direction, veh_luminance, 45);
                    }
                }
            }
        }
        if(vehs[v].v->overhead_lights_on) {
            std::vector<int> light_indices = vehs[v].v->all_parts_with_feature(VPFLAG_CIRCLE_LIGHT);
            for (std::vector<int>::iterator part = light_indices.begin();
                 part != light_indices.end(); ++part) {
                if((calendar::turn % 2 && vehs[v].v->part_info(*part).has_flag(VPFLAG_ODDTURN)) ||
                   (!(calendar::turn % 2) && vehs[v].v->part_info(*part).has_flag(VPFLAG_EVENTURN)) ||
                   (!vehs[v].v->part_info(*part).has_flag(VPFLAG_EVENTURN) &&
                    !vehs[v].v->part_info(*part).has_flag(VPFLAG_ODDTURN))) {
                    int px = vehs[v].x + vehs[v].v->parts[*part].precalc_dx[0];
                    int py = vehs[v].y + vehs[v].v->parts[*part].precalc_dy[0];
                    if(INBOUNDS(px, py)) {
                        add_light_source( px, py, vehs[v].v->part_info(*part).bonus );
                    }
                }
            }
        }
    }

    /* Now that we have position and intensity of all bulk light sources, apply_ them
      This may seem like extra work, but take a 12x12 raging inferno:
        unbuffered: (12^2)*(160*4) = apply_light_ray x 92160
        buffered:   (12*4)*(160)   = apply_light_ray x 7680
    */
    for(int sx = 0; sx < LIGHTMAP_CACHE_X; ++sx) {
        for(int sy = 0; sy < LIGHTMAP_CACHE_Y; ++sy) {
            if ( light_source_buffer[sx][sy] > 0. ) {
                apply_light_source(sx, sy, light_source_buffer[sx][sy],
                                   ( trigdist && light_source_buffer[sx][sy] > 3. ) );
            }
        }
    }


    if (g->u.has_active_bionic("bio_night") ) {
        for(int sx = 0; sx < LIGHTMAP_CACHE_X; ++sx) {
            for(int sy = 0; sy < LIGHTMAP_CACHE_Y; ++sy) {
                if (rl_dist(sx, sy, g->u.posx, g->u.posy) < 15) {
                    lm[sx][sy] = 0;
                }
            }
        }
    }
}
Ejemplo n.º 24
0
std::vector<tripoint> map::route( const tripoint &f, const tripoint &t,
                                  const pathfinding_settings &settings,
                                  const std::set<tripoint> &pre_closed ) const
{
    /* TODO: If the origin or destination is out of bound, figure out the closest
     * in-bounds point and go to that, then to the real origin/destination.
     */
    std::vector<tripoint> ret;

    if( f == t || !inbounds( f ) ) {
        return ret;
    }

    if( !inbounds( t ) ) {
        tripoint clipped = t;
        clip_to_bounds( clipped );
        return route( f, clipped, settings, pre_closed );
    }
    // First, check for a simple straight line on flat ground
    // Except when the line contains a pre-closed tile - we need to do regular pathing then
    static const auto non_normal = PF_SLOW | PF_WALL | PF_VEHICLE | PF_TRAP;
    if( f.z == t.z ) {
        const auto line_path = line_to( f, t );
        const auto &pf_cache = get_pathfinding_cache_ref( f.z );
        // Check all points for any special case (including just hard terrain)
        if( std::all_of( line_path.begin(), line_path.end(), [&pf_cache]( const tripoint & p ) {
        return !( pf_cache.special[p.x][p.y] & non_normal );
        } ) ) {
            const std::set<tripoint> sorted_line( line_path.begin(), line_path.end() );

            if( is_disjoint( sorted_line, pre_closed ) ) {
                return line_path;
            }
        }
    }

    // If expected path length is greater than max distance, allow only line path, like above
    if( rl_dist( f, t ) > settings.max_dist ) {
        return ret;
    }

    int max_length = settings.max_length;
    int bash = settings.bash_strength;
    int climb_cost = settings.climb_cost;
    bool doors = settings.allow_open_doors;
    bool trapavoid = settings.avoid_traps;

    const int pad = 16;  // Should be much bigger - low value makes pathfinders dumb!
    int minx = std::min( f.x, t.x ) - pad;
    int miny = std::min( f.y, t.y ) - pad;
    int minz = std::min( f.z, t.z ); // TODO: Make this way bigger
    int maxx = std::max( f.x, t.x ) + pad;
    int maxy = std::max( f.y, t.y ) + pad;
    int maxz = std::max( f.z, t.z ); // Same TODO as above
    clip_to_bounds( minx, miny, minz );
    clip_to_bounds( maxx, maxy, maxz );

    pathfinder pf( minx, miny, maxx, maxy );
    // Make NPCs not want to path through player
    // But don't make player pathing stop working
    for( const auto &p : pre_closed ) {
        if( p.x >= minx && p.x < maxx && p.y >= miny && p.y < maxy ) {
            pf.close_point( p );
        }
    }

    // Start and end must not be closed
    pf.unclose_point( f );
    pf.unclose_point( t );
    pf.add_point( 0, 0, f, f );

    bool done = false;

    do {
        auto cur = pf.get_next();

        const int parent_index = flat_index( cur.x, cur.y );
        auto &layer = pf.get_layer( cur.z );
        auto &cur_state = layer.state[parent_index];
        if( cur_state == ASL_CLOSED ) {
            continue;
        }

        if( layer.gscore[parent_index] > max_length ) {
            // Shortest path would be too long, return empty vector
            return std::vector<tripoint>();
        }

        if( cur == t ) {
            done = true;
            break;
        }

        cur_state = ASL_CLOSED;

        const auto &pf_cache = get_pathfinding_cache_ref( cur.z );
        const auto cur_special = pf_cache.special[cur.x][cur.y];

        // 7 3 5
        // 1 . 2
        // 6 4 8
        constexpr std::array<int, 8> x_offset{{ -1,  1,  0,  0,  1, -1, -1, 1 }};
        constexpr std::array<int, 8> y_offset{{  0,  0, -1,  1, -1,  1, -1, 1 }};
        for( size_t i = 0; i < 8; i++ ) {
            const tripoint p( cur.x + x_offset[i], cur.y + y_offset[i], cur.z );
            const int index = flat_index( p.x, p.y );

            // @todo: Remove this and instead have sentinels at the edges
            if( p.x < minx || p.x >= maxx || p.y < miny || p.y >= maxy ) {
                continue;
            }

            if( layer.state[index] == ASL_CLOSED ) {
                continue;
            }

            // Penalize for diagonals or the path will look "unnatural"
            int newg = layer.gscore[parent_index] + ( ( cur.x != p.x && cur.y != p.y ) ? 1 : 0 );

            const auto p_special = pf_cache.special[p.x][p.y];
            // @todo: De-uglify, de-huge-n
            if( !( p_special & non_normal ) ) {
                // Boring flat dirt - the most common case above the ground
                newg += 2;
            } else {
                int part = -1;
                const maptile &tile = maptile_at_internal( p );
                const auto &terrain = tile.get_ter_t();
                const auto &furniture = tile.get_furn_t();
                const vehicle *veh = veh_at_internal( p, part );

                const int cost = move_cost_internal( furniture, terrain, veh, part );
                // Don't calculate bash rating unless we intend to actually use it
                const int rating = ( bash == 0 || cost != 0 ) ? -1 :
                                   bash_rating_internal( bash, furniture, terrain, false, veh, part );

                if( cost == 0 && rating <= 0 && ( !doors || !terrain.open ) && veh == nullptr && climb_cost <= 0 ) {
                    layer.state[index] = ASL_CLOSED; // Close it so that next time we won't try to calculate costs
                    continue;
                }

                newg += cost;
                if( cost == 0 ) {
                    if( climb_cost > 0 && p_special & PF_CLIMBABLE ) {
                        // Climbing fences
                        newg += climb_cost;
                    } else if( doors && terrain.open &&
                               ( !terrain.has_flag( "OPENCLOSE_INSIDE" ) || !is_outside( cur ) ) ) {
                        // Only try to open INSIDE doors from the inside
                        // To open and then move onto the tile
                        newg += 4;
                    } else if( veh != nullptr ) {
                        part = veh->obstacle_at_part( part );
                        int dummy = -1;
                        if( doors && veh->part_flag( part, VPFLAG_OPENABLE ) &&
                            ( !veh->part_flag( part, "OPENCLOSE_INSIDE" ) ||
                              veh_at_internal( cur, dummy ) == veh ) ) {
                            // Handle car doors, but don't try to path through curtains
                            newg += 10; // One turn to open, 4 to move there
                        } else if( part >= 0 && bash > 0 ) {
                            // Car obstacle that isn't a door
                            // @todo: Account for armor
                            int hp = veh->parts[part].hp();
                            if( hp / 20 > bash ) {
                                // Threshold damage thing means we just can't bash this down
                                layer.state[index] = ASL_CLOSED;
                                continue;
                            } else if( hp / 10 > bash ) {
                                // Threshold damage thing means we will fail to deal damage pretty often
                                hp *= 2;
                            }

                            newg += 2 * hp / bash + 8 + 4;
                        } else if( part >= 0 ) {
                            if( !doors || !veh->part_flag( part, VPFLAG_OPENABLE ) ) {
                                // Won't be openable, don't try from other sides
                                layer.state[index] = ASL_CLOSED;
                            }

                            continue;
                        }
                    } else if( rating > 1 ) {
                        // Expected number of turns to bash it down, 1 turn to move there
                        // and 5 turns of penalty not to trash everything just because we can
                        newg += ( 20 / rating ) + 2 + 10;
                    } else if( rating == 1 ) {
                        // Desperate measures, avoid whenever possible
                        newg += 500;
                    } else {
                        // Unbashable and unopenable from here
                        if( !doors || !terrain.open ) {
                            // Or anywhere else for that matter
                            layer.state[index] = ASL_CLOSED;
                        }

                        continue;
                    }
                }

                if( trapavoid && p_special & PF_TRAP ) {
                    const auto &ter_trp = terrain.trap.obj();
                    const auto &trp = ter_trp.is_benign() ? tile.get_trap_t() : ter_trp;
                    if( !trp.is_benign() ) {
                        // For now make them detect all traps
                        if( has_zlevels() && terrain.has_flag( TFLAG_NO_FLOOR ) ) {
                            // Special case - ledge in z-levels
                            // Warning: really expensive, needs a cache
                            if( valid_move( p, tripoint( p.x, p.y, p.z - 1 ), false, true ) ) {
                                tripoint below( p.x, p.y, p.z - 1 );
                                if( !has_flag( TFLAG_NO_FLOOR, below ) ) {
                                    // Otherwise this would have been a huge fall
                                    auto &layer = pf.get_layer( p.z - 1 );
                                    // From cur, not p, because we won't be walking on air
                                    pf.add_point( layer.gscore[parent_index] + 10,
                                                  layer.score[parent_index] + 10 + 2 * rl_dist( below, t ),
                                                  cur, below );
                                }

                                // Close p, because we won't be walking on it
                                layer.state[index] = ASL_CLOSED;
                                continue;
                            }
                        } else if( trapavoid ) {
                            // Otherwise it's walkable
                            newg += 500;
                        }
                    }
                }
            }

            // If not visited, add as open
            // If visited, add it only if we can do so with better score
            if( layer.state[index] == ASL_NONE || newg < layer.gscore[index] ) {
                pf.add_point( newg, newg + 2 * rl_dist( p, t ), cur, p );
            }
        }

        if( !has_zlevels() || !( cur_special & PF_UPDOWN ) || !settings.allow_climb_stairs ) {
            // The part below is only for z-level pathing
            continue;
        }

        const maptile &parent_tile = maptile_at_internal( cur );
        const auto &parent_terrain = parent_tile.get_ter_t();
        if( settings.allow_climb_stairs && cur.z > minz && parent_terrain.has_flag( TFLAG_GOES_DOWN ) ) {
            tripoint dest( cur.x, cur.y, cur.z - 1 );
            dest = vertical_move_destination<TFLAG_GOES_UP>( *this, dest );
            if( inbounds( dest ) ) {
                auto &layer = pf.get_layer( dest.z );
                pf.add_point( layer.gscore[parent_index] + 2,
                              layer.score[parent_index] + 2 * rl_dist( dest, t ),
                              cur, dest );
            }
        }
        if( settings.allow_climb_stairs && cur.z < maxz && parent_terrain.has_flag( TFLAG_GOES_UP ) ) {
            tripoint dest( cur.x, cur.y, cur.z + 1 );
            dest = vertical_move_destination<TFLAG_GOES_DOWN>( *this, dest );
            if( inbounds( dest ) ) {
                auto &layer = pf.get_layer( dest.z );
                pf.add_point( layer.gscore[parent_index] + 2,
                              layer.score[parent_index] + 2 * rl_dist( dest, t ),
                              cur, dest );
            }
        }
        if( cur.z < maxz && parent_terrain.has_flag( TFLAG_RAMP ) &&
            valid_move( cur, tripoint( cur.x, cur.y, cur.z + 1 ), false, true ) ) {
            auto &layer = pf.get_layer( cur.z + 1 );
            for( size_t it = 0; it < 8; it++ ) {
                const tripoint above( cur.x + x_offset[it], cur.y + y_offset[it], cur.z + 1 );
                pf.add_point( layer.gscore[parent_index] + 4,
                              layer.score[parent_index] + 4 + 2 * rl_dist( above, t ),
                              cur, above );
            }
        }
    } while( !done && !pf.empty() );

    if( done ) {
        ret.reserve( rl_dist( f, t ) * 2 );
        tripoint cur = t;
        // Just to limit max distance, in case something weird happens
        for( int fdist = max_length; fdist != 0; fdist-- ) {
            const int cur_index = flat_index( cur.x, cur.y );
            const auto &layer = pf.get_layer( cur.z );
            const tripoint &par = layer.parent[cur_index];
            if( cur == f ) {
                break;
            }

            ret.push_back( cur );
            // Jumps are acceptable on 1 z-level changes
            // This is because stairs teleport the player too
            if( rl_dist( cur, par ) > 1 && abs( cur.z - par.z ) != 1 ) {
                debugmsg( "Jump in our route! %d:%d:%d->%d:%d:%d",
                          cur.x, cur.y, cur.z, par.x, par.y, par.z );
                return ret;
            }

            cur = par;
        }

        std::reverse( ret.begin(), ret.end() );
    }

    return ret;
}
Ejemplo n.º 25
0
void mdeath::guilt(monster *z)
{
    const int MAX_GUILT_DISTANCE = 5;
    int kill_count = g->kill_count(z->type->id);
    int maxKills = 100; // this is when the player stop caring altogether.

    // different message as we kill more of the same monster
    std::string msg = _("You feel guilty for killing %s."); // default guilt message
    game_message_type msgtype = m_bad; // default guilt message type
    std::map<int, std::string> guilt_tresholds;
    guilt_tresholds[75] = _("You feel ashamed for killing %s.");
    guilt_tresholds[50] = _("You regret killing %s.");
    guilt_tresholds[25] = _("You feel remorse for killing %s.");

    if (g->u.has_trait("PSYCHOPATH") || g->u.has_trait("PRED3") || g->u.has_trait("PRED4") ) {
        return;
    }
    if (rl_dist(z->posx(), z->posy(), g->u.posx, g->u.posy) > MAX_GUILT_DISTANCE) {
        // Too far away, we can deal with it.
        return;
    }
    if (z->hp >= 0) {
        // We probably didn't kill it
        return;
    }
    if (kill_count >= maxKills) {
        // player no longer cares
        if (kill_count == maxKills) {
            //~ Message after killing a lot of monsters which would normally affect the morale negatively. %s is the monster name, it will be pluralized with a number of 100.
            add_msg(m_good, _("After killing so many bloody %s you no longer care "
                              "about their deaths anymore."), z->name(maxKills).c_str());
        }
        return;
    } else if ((g->u.has_trait("PRED1")) || (g->u.has_trait("PRED2"))) {
        msg = (_("Culling the weak is distasteful, but necessary."));
        msgtype = m_neutral;
    } else {
        msgtype = m_bad;
        for (std::map<int, std::string>::iterator it = guilt_tresholds.begin();
             it != guilt_tresholds.end(); it++) {
            if (kill_count >= it->first) {
                msg = it->second;
                break;
            }
        }
    }

    add_msg(msgtype, msg.c_str(), z->name().c_str());

    int moraleMalus = -50 * (1.0 - ((float) kill_count / maxKills));
    int maxMalus = -250 * (1.0 - ((float) kill_count / maxKills));
    int duration = 300 * (1.0 - ((float) kill_count / maxKills));
    int decayDelay = 30 * (1.0 - ((float) kill_count / maxKills));
    if (z->type->in_species("ZOMBIE")) {
        moraleMalus /= 10;
        if (g->u.has_trait("PACIFIST")) {
            moraleMalus *= 5;
        } else if (g->u.has_trait("PRED1")) {
            moraleMalus /= 4;
        } else if (g->u.has_trait("PRED2")) {
            moraleMalus /= 5;
        }
    }
    g->u.add_morale(MORALE_KILLED_MONSTER, moraleMalus, maxMalus, duration, decayDelay);

}
Ejemplo n.º 26
0
int monster::trigger_sum(std::set<monster_trigger> *triggers)
{
 int ret = 0;
 bool check_terrain = false, check_meat = false, check_fire = false;
 for (std::set<monster_trigger>::iterator trig = triggers->begin(); trig != triggers->end(); ++trig)
 {
     switch (*trig){
      case MTRIG_STALK:
       if (anger > 0 && one_in(20))
        ret++;
       break;

      case MTRIG_MEAT:
       check_terrain = true;
       check_meat = true;
       break;

      case MTRIG_PLAYER_CLOSE:
       if (rl_dist(_posx, _posy, g->u.posx, g->u.posy) <= 5)
        ret += 5;
       for (int i = 0; i < g->active_npc.size(); i++) {
        if (rl_dist(_posx, _posy, g->active_npc[i]->posx, g->active_npc[i]->posy) <= 5)
         ret += 5;
       }
       break;

      case MTRIG_FIRE:
       check_terrain = true;
       check_fire = true;
       break;

      case MTRIG_PLAYER_WEAK:
       if (g->u.hp_percentage() <= 70)
        ret += 10 - int(g->u.hp_percentage() / 10);
       break;

      default:
       break; // The rest are handled when the impetus occurs
    }
 }

 if (check_terrain) {
  for (int x = _posx - 3; x <= _posx + 3; x++) {
   for (int y = _posy - 3; y <= _posy + 3; y++) {
    if (check_meat) {
     std::vector<item> *items = &(g->m.i_at(x, y));
     for (int n = 0; n < items->size(); n++) {
      if ((*items)[n].type->id == "corpse" ||
          (*items)[n].type->id == "meat" ||
          (*items)[n].type->id == "meat_cooked" ||
          (*items)[n].type->id == "human_flesh") {
       ret += 3;
       check_meat = false;
      }
     }
    }
    if (check_fire) {
      ret += ( 5 * g->m.get_field_strength( point(x, y), fd_fire) );
    }
   }
  }
  if (check_fire) {
   if (g->u.has_amount("torch_lit", 1))
    ret += 49;
  }
 }

 return ret;
}
Ejemplo n.º 27
0
Path Pathfinder::path_a_star(Tripoint start, Tripoint end)
{
  int x_size = map.get_size_x();
  int y_size = map.get_size_y();
  int z_size = map.get_size_z();

  start.x -= map.x_offset;
  start.y -= map.y_offset;
  start.z -= map.z_offset;
  end.x   -= map.x_offset;
  end.y   -= map.y_offset;
  end.z   -= map.z_offset;

  if (x_size == 0 || y_size == 0 || z_size == 0) {
    debugmsg("A* generated; %s => %s (size %d, %d, %d)",
             start.str().c_str(), end.str().c_str(),
             x_size, y_size, z_size);
    return Path();
  }

  std::vector<Tripoint> open_points;
  A_star_status status[x_size][y_size][z_size];
  int           gscore[x_size][y_size][z_size];
  int           hscore[x_size][y_size][z_size];
  Tripoint      parent[x_size][y_size][z_size];

  if (border > 0) {
    int x0 = (start.x < end.x ? start.x : end.x);
    int y0 = (start.y < end.y ? start.y : end.y);
    int z0 = (start.z < end.z ? start.z : end.z);
    int x1 = (start.x > end.x ? start.x : end.x);
    int y1 = (start.y > end.y ? start.y : end.y);
    int z1 = (start.z > end.z ? start.z : end.z);

    set_bounds(x0 - border, y0 - border, z0 - border,
               x1 + border, y1 + border, z1 + border);
  }

// Init everything to 0
  for (int x = 0; x < x_size; x++) {
    for (int y = 0; y < y_size; y++) {
      for (int z = 0; z < z_size; z++) {
        status[x][y][z] = ASTAR_NONE;
        gscore[x][y][z] = 0;
        hscore[x][y][z] = 0;
        parent[x][y][z] = Tripoint(-1, -1, -1);
      }
    }
  }

  status[start.x][start.y][start.z] = ASTAR_OPEN;
  open_points.push_back(start);

  bool done = false;


  while (!done && !open_points.empty()) {
// 1) Find the lowest cost in open_points, and set (current) to that point
// (if multiple points are tied, randomly select one)
    int lowest_cost = -1, point_index = -1;
    Tripoint current;
    int current_g = 0;
    std::vector<int> lowest_indices;
    for (int i = 0; i < open_points.size(); i++) {
      Tripoint p = open_points[i];
      int score = gscore[p.x][p.y][p.z] + hscore[p.x][p.y][p.z];
      if (i == 0 || score < lowest_cost) {
        lowest_cost = score;
        lowest_indices.clear();
        lowest_indices.push_back(i);
      } else if (score == lowest_cost) {
        lowest_indices.push_back(i);
      }
    }
    if (lowest_indices.empty()) { // Should never happen
      point_index = 0;
    } else {
      point_index = lowest_indices[ rng(0, lowest_indices.size() - 1) ];
    }
    current = open_points[point_index];
    current_g = gscore[current.x][current.y][current.z];
// 2) Check if (current) is the endpoint
    if (current == end) {
      done = true;
    } else {
// 3) Set (current) to be closed
      open_points.erase(open_points.begin() + point_index);
      status[current.x][current.y][current.z] = ASTAR_CLOSED;
// 4) Examine all adjacent points on the same z-level
      for (int x = current.x - 1; x <= current.x + 1; x++) {
        for (int y = current.y - 1; y <= current.y + 1; y++) {
          if (x == current.x && y == current.y) {
            y++; // Skip the current tile
          }
          int z = current.z;
// If it's not diagonal, or diagonals are allowed...
// ...and if it's in-bounds and not blocked...
          if ((allow_diag || x == current.x || y == current.y) &&
              (in_bounds(x, y, z) && !map.blocked(x, y, z))) {
            int g = current_g + map.get_cost(x, y, z);
// If it's unexamined, make it open and set its values
            if (status[x][y][z] == ASTAR_NONE) {
              status[x][y][z] = ASTAR_OPEN;
              gscore[x][y][z] = g;
              if (allow_diag) {
                hscore[x][y][z] = map.get_cost(x, y, z) *
                                  rl_dist(x, y, z, end.x, end.y, end.z);
              } else {
                hscore[x][y][z] = map.get_cost(x, y, z) *
                                  manhattan_dist(x, y, z, end.x, end.y, end.z);
              }
              parent[x][y][z] = current;
              open_points.push_back( Tripoint(x, y, z) );
// Otherwise, if it's open and we're a better parent, make us the parent
            } else if (status[x][y][z] == ASTAR_OPEN && g < gscore[x][y][z]) {
              gscore[x][y][z] = g;
              parent[x][y][z] = current;
            }
          }
        }
      }
// 5.  Examine adjacent points on adjacent Z-levels
// TODO: Allow diagonal movement across Z-levels? For flying monsters?
// Examine above
      if (map.allow_z_up(current)) {
        int z = current.z + 1;
        int x = current.x, y = current.y;
        if ((in_bounds(x, y, z) && !map.blocked(x, y, z))) {
          int g = current_g + map.get_cost(x, y, z);
// If it's unexamined, make it open and set its values
          if (status[x][y][z] == ASTAR_NONE) {
            status[x][y][z] = ASTAR_OPEN;
            gscore[x][y][z] = g;
            if (allow_diag) {
              hscore[x][y][z] = map.get_cost(x, y, z) *
                                rl_dist(x, y, z, end.x, end.y, end.z);
            } else {
              hscore[x][y][z] = map.get_cost(x, y, z) *
                                manhattan_dist(x, y, z, end.x, end.y, end.z);
            }
            parent[x][y][z] = current;
            open_points.push_back( Tripoint(x, y, z) );
// If it's open and we're a better parent, make us the parent
          } else if (status[x][y][z] == ASTAR_OPEN && g < gscore[x][y][z]) {
            gscore[x][y][z] = g;
            parent[x][y][z] = current;
          }
        }
      }
// Examine below (code duplication, sorry mom)
      if (map.allow_z_down(current)) {
        int z = current.z - 1;
        int x = current.x, y = current.y;
        if ((in_bounds(x, y, z) && !map.blocked(x, y, z))) {
          int g = current_g + map.get_cost(x, y, z);
// If it's unexamined, make it open and set its values
          if (status[x][y][z] == ASTAR_NONE) {
debugmsg("A*'d over Z-level");
            status[x][y][z] = ASTAR_OPEN;
            gscore[x][y][z] = g;
            if (allow_diag) {
              hscore[x][y][z] = map.get_cost(x, y, z) *
                                rl_dist(x, y, z, end.x, end.y, end.z);
            } else {
              hscore[x][y][z] = map.get_cost(x, y, z) *
                                manhattan_dist(x, y, z, end.x, end.y, end.z);
            }
            parent[x][y][z] = current;
            open_points.push_back( Tripoint(x, y, z) );
// If it's open and we're a better parent, make us the parent
          } else if (status[x][y][z] == ASTAR_OPEN && g < gscore[x][y][z]) {
            gscore[x][y][z] = g;
            parent[x][y][z] = current;
          }
        }
      }
    }
  }

  Path ret;
  if (open_points.empty()) {
    return ret;
  }
  Tripoint cur = end;
  ret.add_step(cur, map.get_cost(cur));
  while (parent[cur.x][cur.y][cur.z] != start) {
    cur = parent[cur.x][cur.y][cur.z];
    ret.add_step(cur, map.get_cost(cur));
  }
  ret.reverse();
// Add the offsets back in.
  ret.offset(map.x_offset, map.y_offset, map.z_offset);

  return ret;
}
Ejemplo n.º 28
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 );
        }
    }
}
Ejemplo n.º 29
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()
{
    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 );
        }
    }
}
Ejemplo n.º 30
0
void castLight( bool (&output_cache)[MAPSIZE*SEEX][MAPSIZE*SEEY],
                const float (&input_array)[MAPSIZE*SEEX][MAPSIZE*SEEY],
                const int offsetX, const int offsetY, const int offsetDistance,
                const int row, float start, const float end )
{
    float newStart = 0.0f;
    float radius = 60.0f - offsetDistance;
    if( start < end ) {
        return;
    }
    // Making these static prevents them from being needlessly constructed/destructed all the time.
    static const tripoint origin(0, 0, 0);
    tripoint delta(0, 0, 0);
    for( int distance = row; distance <= radius; distance++ ) {
        delta.y = -distance;
        bool started_row = false;
        float current_transparency = 0.0;
        for( delta.x = -distance; delta.x <= 0; delta.x++ ) {
            int currentX = offsetX + delta.x * xx + delta.y * xy;
            int currentY = offsetY + delta.x * yx + delta.y * yy;
            float leadingEdge = (delta.x - 0.5f) / (delta.y + 0.5f);
            float trailingEdge = (delta.x + 0.5f) / (delta.y - 0.5f);

            if( !(currentX >= 0 && currentY >= 0 && currentX < SEEX * MAPSIZE &&
                  currentY < SEEY * MAPSIZE) || start < trailingEdge ) {
                continue;
            } else if( end > leadingEdge ) {
                break;
            }
            if( !started_row ) {
                started_row = true;
                current_transparency = input_array[ currentX ][ currentY ];
            }

            //check if it's within the visible area and mark visible if so
            if( rl_dist(origin, delta) <= radius ) {
                /*
                float bright = (float) (1 - (rStrat.radius(delta.x, delta.y) / radius));
                lightMap[currentX][currentY] = bright;
                */
                // TODO: handle circular distance.
                output_cache[ currentX ][ currentY] = true;
            }

            float new_transparency = input_array[ currentX ][ currentY ];

            if( new_transparency != current_transparency ) {
                // Only cast recursively if previous span was not opaque.
                if( current_transparency != LIGHT_TRANSPARENCY_SOLID ) {
                    castLight<xx, xy, yx, yy>( output_cache, input_array,
                               offsetX, offsetY, offsetDistance, distance + 1, start, leadingEdge);
                    newStart = trailingEdge;
                }
                // We either recursed into a transparent span, or did NOT recurse into an opaque span,
                // either way the new span starts at the trailing edge of the previous square.
                if( new_transparency != LIGHT_TRANSPARENCY_SOLID ) {
                    start = newStart;
                }
                current_transparency = new_transparency;
            } else if( current_transparency == LIGHT_TRANSPARENCY_SOLID ) {
                newStart = trailingEdge;
            }
        }
        if( current_transparency == LIGHT_TRANSPARENCY_SOLID ) {
            // If we reach the end of the span with terrain being opaque, we don't iterate further.
            break;
        }
    }
}