Exemple #1
0
double calculate_missed_by(player &p, int trange, item* weapon)
{
 // No type for gunmods,so use player weapon.
 it_gun* firing = dynamic_cast<it_gun*>(p.weapon.type);
// Calculate deviation from intended target (assuming we shoot for the head)
  double deviation = 0.; // Measured in quarter-degrees
// Up to 1.5 degrees for each skill point < 4; up to 1.25 for each point > 4
  if (p.skillLevel(firing->skill_used) < 4)
    deviation += rng(0, 6 * (4 - p.skillLevel(firing->skill_used)));
  else if (p.skillLevel(firing->skill_used) > 4)
    deviation -= rng(0, 5 * (p.skillLevel(firing->skill_used) - 4));

  if (p.skillLevel("gun") < 3)
    deviation += rng(0, 3 * (3 - p.skillLevel("gun")));
  else
    deviation -= rng(0, 2 * (p.skillLevel("gun") - 3));

  deviation += p.ranged_dex_mod();
  deviation += p.ranged_per_mod();

  deviation += rng(0, 2 * p.encumb(bp_arms)) + rng(0, 4 * p.encumb(bp_eyes));

  deviation += rng(0, weapon->curammo->accuracy);
  // item::accuracy() doesn't support gunmods.
  deviation += rng(0, p.weapon.accuracy());
  int adj_recoil = p.recoil + p.driving_recoil;
  deviation += rng(int(adj_recoil / 4), adj_recoil);

// .013 * trange is a computationally cheap version of finding the tangent.
// (note that .00325 * 4 = .013; .00325 is used because deviation is a number
//  of quarter-degrees)
// It's also generous; missed_by will be rather short.
  return (.00325 * deviation * trange);
}
double calculate_missed_by(player &p, int trange, item* weapon)
{
    // No type for gunmods,so use player weapon.
    it_gun* firing = dynamic_cast<it_gun*>(p.weapon.type);
    // Calculate deviation from intended target (assuming we shoot for the head)
    double deviation = 0.; // Measured in quarter-degrees.
    // Up to 0.75 degrees for each skill point < 8.
    if (p.skillLevel(firing->skill_used) < 8) {
        deviation += rng(0, 3 * (8 - p.skillLevel(firing->skill_used)));
    }

    // Up to 0.25 deg per each skill point < 9.
    if (p.skillLevel("gun") < 9) { deviation += rng(0, 9 - p.skillLevel("gun")); }

    deviation += rng(0, p.ranged_dex_mod());
    deviation += rng(0, p.ranged_per_mod());

    deviation += rng(0, 2 * p.encumb(bp_arms)) + rng(0, 4 * p.encumb(bp_eyes));

    deviation += rng(0, weapon->curammo->dispersion);
    // item::dispersion() doesn't support gunmods.
    deviation += rng(0, p.weapon.dispersion());
    int adj_recoil = p.recoil + p.driving_recoil;
    deviation += rng(int(adj_recoil / 4), adj_recoil);

    if (deviation < 0) { return 0; }
    // .013 * trange is a computationally cheap version of finding the tangent.
    // (note that .00325 * 4 = .013; .00325 is used because deviation is a number
    //  of quarter-degrees)
    // It's also generous; missed_by will be rather short.
    return (.00325 * deviation * trange);
}
Exemple #3
0
int item::reload_time(player &u)
{
 int ret = 0;

 if (is_gun()) {
  it_gun* reloading = dynamic_cast<it_gun*>(type);
  ret = reloading->reload_time;
  if (charges == 0) {
   int spare_mag = has_gunmod(itm_spare_mag);
   if (spare_mag != -1 && contents[spare_mag].charges > 0)
    ret -= double(ret) * 0.9;
  }
  double skill_bonus = double(u.sklevel[reloading->skill_used]) * .075;
  if (skill_bonus > .75)
   skill_bonus = .75;
  ret -= double(ret) * skill_bonus;
 } else if (is_tool())
  ret = 100 + volume() + weight();

 if (has_flag(IF_STR_RELOAD))
  ret -= u.str_cur * 20;
 if (ret < 25)
  ret = 25;
 ret += u.encumb(bp_hands) * 30;
 return ret;
}
Exemple #4
0
bool trap::detect_trap( const tripoint &pos, const player &p ) 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...
    ///\EFFECT_PER increases chance of detecting a trap
    return ( p.per_cur - ( p.encumb( bp_eyes ) / 10 ) ) +
           // ...small bonus from stimulants...
           ( p.stim > 10 ? rng( 1, 2 ) : 0 ) +
           // ...bonus from trap skill...
           ///\EFFECT_TRAPS increases chance of detecting a trap
           ( p.get_skill_level( skill_traps ) * 2 ) +
           // ...luck, might be good, might be bad...
           rng( -4, 4 ) -
           // ...malus if we are tired...
           ( p.has_effect( effect_lack_sleep ) ? rng( 1, 5 ) : 0 ) -
           // ...malus farther we are from trap...
           rl_dist( p.pos(), pos ) +
           // Police are trained to notice Something Wrong.
           ( p.has_trait( trait_id( "PROF_POLICE" ) ) ? 1 : 0 ) +
           ( p.has_trait( trait_id( "PROF_PD_DET" ) ) ? 2 : 0 ) >
           // ...must all be greater than the trap visibility.
           visibility;
}
Exemple #5
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 - 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) +
           // Police are trained to notice Something Wrong.
           (p.has_trait("PROF_POLICE") ? 1 : 0) +
           (p.has_trait("PROF_PD_DET") ? 2 : 0) >
           // ...must all be greater than the trap visibility.
           visibility;
}
Exemple #6
0
int attack_speed(player &u, bool missed)
{
 int move_cost = u.weapon.attack_time() + 20 * u.encumb(bp_torso);
 if (u.has_trait(PF_LIGHT_BONES))
  move_cost *= .9;
 if (u.has_trait(PF_HOLLOW_BONES))
  move_cost *= .8;

 move_cost -= u.disease_intensity(DI_SPEED_BOOST);

 if (move_cost < 25)
  return 25;

 return move_cost;
}
Exemple #7
0
int item::reload_time(player &u)
{
 int ret = 0;

 if (is_gun()) {
  it_gun* reloading = dynamic_cast<it_gun*>(type);
  ret = reloading->reload_time;
  double skill_bonus = double(u.sklevel[reloading->skill_used]) * .075;
  if (skill_bonus > .75)
   skill_bonus = .75;
  ret -= double(ret) * skill_bonus;
 } else if (is_tool())
  ret = 100 + volume() + weight();

 if (has_flag(IF_STR_RELOAD))
  ret -= u.str_cur * 20;
 if (ret < 25)
  ret = 25;
 ret += u.encumb(bp_hands) * 30;
 return ret;
}
Exemple #8
0
void monster::hit_player(game *g, player &p, bool can_grab)
{
    moves -= 100;

    if (type->melee_dice == 0) // We don't attack, so just return
    {
        return;
    }
    add_effect(ME_HIT_BY_PLAYER, 3); // Make us a valid target for a few turns
    if (has_flag(MF_HIT_AND_RUN))
    {
        add_effect(ME_RUN, 4);
    }
    bool is_npc = p.is_npc();
    bool u_see = (!is_npc || g->u_see(p.posx, p.posy));
    std::string you  = (is_npc ? p.name : "you");
    std::string You  = (is_npc ? p.name : "You");
    std::string your = (is_npc ? p.name + "'s" : "your");
    std::string Your = (is_npc ? p.name + "'s" : "Your");
    body_part bphit;
    int side = rng(0, 1);
    int dam = hit(g, p, bphit), cut = type->melee_cut, stab = 0;
    technique_id tech = p.pick_defensive_technique(g, this, NULL);
    p.perform_defensive_technique(tech, g, this, NULL, bphit, side, dam, cut, stab);

    //110*e^(-.3*[melee skill of monster]) = % chance to miss. *100 to track .01%'s
    //Returns ~80% at 1, drops quickly to 33% at 4, then slowly to 5% at 10 and 1% at 16
    if (rng(0, 10000) < 11000 * exp(-.3 * type->melee_skill))
    {
        g->add_msg("The %s misses.", name().c_str());
    }
    else
    {
        //Reduce player's ability to dodge by monster's ability to hit
        int dodge_ii = p.dodge(g) - rng(0, type->melee_skill);
        if (dodge_ii < 0)
        {
            dodge_ii = 0;
        }

        // 100/(1+99*e^(-.6*[dodge() return modified by monster's skill])) = % chance to dodge
        // *100 to track .01%'s
        // 1% minimum, scales slowly to 16% at 5, then rapidly to 80% at 10,
        // then returns less with each additional point, reaching 99% at 16
        if (rng(0, 10000) < 10000/(1 + 99 * exp(-.6 * dodge_ii)))
        {
            g->add_msg("%s dodge the %s.", You.c_str(), name().c_str());
            p.practice(g->turn, "dodge", type->melee_skill * 2); //Better monster = more skill gained
        }

        //Successful hit with damage
        else if (dam > 0)
        {
            p.practice(g->turn, "dodge", type->melee_skill);
            if (u_see && tech != TEC_BLOCK)
            {
                g->add_msg("The %s hits %s %s.", name().c_str(), your.c_str(),
                           body_part_name(bphit, side).c_str());
            }

            // Attempt defensive moves
            if (!is_npc)
            {
                if (g->u.activity.type == ACT_RELOAD)
                {
                    g->add_msg("You stop reloading.");
                }
                else if (g->u.activity.type == ACT_READ)
                {
                    g->add_msg("You stop reading.");
                }
                else if (g->u.activity.type == ACT_CRAFT || g->u.activity.type == ACT_LONGCRAFT)
                {
                    g->add_msg("You stop crafting.");
                    g->u.activity.type = ACT_NULL;
                }
            }
            if (p.has_active_bionic("bio_ods"))
            {
                if (u_see)
                {
                    g->add_msg("%s offensive defense system shocks it!", Your.c_str());
                }
                if (hurt(rng(10, 40)))
                    die(g);
            }
            if (p.encumb(bphit) == 0 &&(p.has_trait(PF_SPINES) || p.has_trait(PF_QUILLS)))
            {
                int spine = rng(1, (p.has_trait(PF_QUILLS) ? 20 : 8));
                g->add_msg("%s %s puncture it!", Your.c_str(),
                           (g->u.has_trait(PF_QUILLS) ? "quills" : "spines"));
                if (hurt(spine))
                    die(g);
            }

            if (dam + cut <= 0)
            {
                return; // Defensive technique canceled damage.
            }

            //Hurt the player
            dam = p.hit(g, bphit, side, dam, cut);

            //Monster effects
            if (dam > 0 && has_flag(MF_VENOM))
            {
                if (!is_npc)
                {
                    g->add_msg("You're poisoned!");
                }
                p.add_disease("poison", 30);
            }
            else if (dam > 0 && has_flag(MF_BADVENOM))
            {
                if (!is_npc)
                {
                    g->add_msg("You feel poison flood your body, wracking you with pain...");
                }
                p.add_disease("badpoison", 40);
            }
            if (has_flag(MF_BLEED) && dam > 6 && cut > 0)
            {
                if (!is_npc)
                {
                    g->add_msg("You're Bleeding!");
                }
                p.add_disease("bleed", 60);
            }

            //Same as monster's chance to not miss
            if (can_grab && has_flag(MF_GRABS) && (rng(0, 10000) > 11000 * exp(-.3 * type->melee_skill)))
            {
                if (!is_npc)
                {
                    g->add_msg("The %s grabs you!", name().c_str());
                }
                if (p.weapon.has_technique(TEC_BREAK, &p) &&
                    dice(p.dex_cur + p.skillLevel("melee"), 12) > dice(type->melee_dice, 10))
                {
                    if (!is_npc)
                    {
                        g->add_msg("You break the grab!");
                    }
                }
                else
                    hit_player(g, p, false); //We grabed, so hit them again
            }

            //Counter-attack?
            if (tech == TEC_COUNTER && !is_npc)
            {
                g->add_msg("Counter-attack!");
                // A counterattack is a free action to avoid stunlocking the player.
                int player_moves = p.moves;
                hurt( p.hit_mon(g, this) );
                p.moves = player_moves;
            }
        }
    }

    // if dam > 0
    if (is_npc)
    {
        if (p.hp_cur[hp_head] <= 0 || p.hp_cur[hp_torso] <= 0)
        {
            npc* tmp = dynamic_cast<npc*>(&p);
            tmp->die(g);
            int index = g->npc_at(p.posx, p.posy);
            if (index != -1 && index < g->active_npc.size())
            {
                g->active_npc.erase(g->active_npc.begin() + index);
            }
            plans.clear();
        }
    }

    // Adjust anger/morale of same-species monsters, if appropriate
    int anger_adjust = 0, morale_adjust = 0;
    for (int i = 0; i < type->anger.size(); i++)
    {
        if (type->anger[i] == MTRIG_FRIEND_ATTACKED)
        {
            anger_adjust += 15;
        }
    }
    for (int i = 0; i < type->placate.size(); i++)
    {
        if (type->placate[i] == MTRIG_FRIEND_ATTACKED)
        {
            anger_adjust -= 15;
        }
    }
    for (int i = 0; i < type->fear.size(); i++)
    {
        if (type->fear[i] == MTRIG_FRIEND_ATTACKED)
        {
            morale_adjust -= 15;
        }
    }
    if (anger_adjust != 0 && morale_adjust != 0)
    {
        for (int i = 0; i < g->z.size(); i++)
        {
            g->z[i].morale += morale_adjust;
            g->z[i].anger += anger_adjust;
        }
    }
}
void monster::hit_player(game *g, player &p, bool can_grab)
{
    moves -= 100;

    if (type->melee_dice == 0) // We don't attack, so just return
    {
        return;
    }
    add_effect(ME_HIT_BY_PLAYER, 3); // Make us a valid target for a few turns
    if (has_flag(MF_HIT_AND_RUN))
    {
        add_effect(ME_RUN, 4);
    }
    bool is_npc = p.is_npc();
    bool u_see = (!is_npc || g->u_see(p.posx, p.posy));
    std::string you  = (is_npc ? p.name : "you");
    std::string You  = (is_npc ? p.name : "You");
    std::string your = (is_npc ? p.name + "'s" : "your");
    std::string Your = (is_npc ? p.name + "'s" : "Your");
    body_part bphit;
    int dam = hit(g, p, bphit), cut = type->melee_cut, stab = 0;
    int side = random_side(bphit);

    //110*e^(-.3*[melee skill of monster]) = % chance to miss. *100 to track .01%'s
    //Returns ~80% at 1, drops quickly to 33% at 4, then slowly to 5% at 10 and 1% at 16
    if (rng(0, 10000) < 11000 * exp(-.3 * type->melee_skill))
    {
        if (u_see) {
            g->add_msg(_("The %s misses."), name().c_str());
        }
    }
    else
    {
        if (!g->u.uncanny_dodge())
        {
            //Reduce player's ability to dodge by monster's ability to hit
            int dodge_ii = p.dodge(g) - rng(0, type->melee_skill);
            if (dodge_ii < 0)
            {
                dodge_ii = 0;
            }

            // 100/(1+99*e^(-.6*[dodge() return modified by monster's skill])) = % chance to dodge
            // *100 to track .01%'s
            // 1% minimum, scales slowly to 16% at 5, then rapidly to 80% at 10,
            // then returns less with each additional point, reaching 99% at 16
            if (rng(0, 10000) < 10000/(1 + 99 * exp(-.6 * dodge_ii)))
            {
                if (is_npc) {
                    if(u_see) {
                        g->add_msg(_("%1$s dodges the %2$s."), p.name.c_str(), name().c_str());
                    }
                } else {
                    g->add_msg(_("You dodge the %s."), name().c_str());
                }
                p.practice(g->turn, "dodge", type->melee_skill * 2); //Better monster = more skill gained
            }

            //Successful hit with damage
            else if (dam > 0)
            {
                p.practice(g->turn, "dodge", type->melee_skill);

                if(!p.block_hit(g, this, NULL, bphit, side, dam, cut, stab) && u_see) {
                    if (is_npc) {
                        if( u_see ) {
                            g->add_msg(_("The %1$s hits %2$s's %3$s."), name().c_str(),
                                       p.name.c_str(), body_part_name(bphit, side).c_str());
                        }
                    } else {
                        g->add_msg(_("The %1$s hits your %2$s."), name().c_str(),
                                   body_part_name(bphit, side).c_str());
                    }
                }

                // Attempt defensive moves
                if (!is_npc)
                {
                    if (g->u.activity.type == ACT_RELOAD)
                    {
                        g->add_msg(_("You stop reloading."));
                    }
                    else if (g->u.activity.type == ACT_READ)
                    {
                        g->add_msg(_("You stop reading."));
                    }
                    else if (g->u.activity.type == ACT_CRAFT || g->u.activity.type == ACT_LONGCRAFT)
                    {
                        g->add_msg(_("You stop crafting."));
                        g->u.activity.type = ACT_NULL;
                    }
                }

                if (p.has_active_bionic("bio_ods"))
                {
                    if (!is_npc) {
                        g->add_msg(_("Your offensive defense system shocks it!"),
                                   p.name.c_str());
                    } else if (u_see) {
                        g->add_msg(_("%s's offensive defense system shocks it!"),
                                   p.name.c_str());
                    }
                    hurt(rng(10, 40));
                }
                if (p.encumb(bphit) == 0 &&(p.has_trait("SPINES") || p.has_trait("QUILLS")))
                {
                    int spine = rng(1, (p.has_trait("QUILLS") ? 20 : 8));
                    if (is_npc) {
                        if( u_see ) {
                            g->add_msg(_("%1$s's %2$s puncture it!"), p.name.c_str(),
                                       (g->u.has_trait("QUILLS") ? _("quills") : _("spines")));
                        }
                    } else {
                        g->add_msg(_("Your %s puncture it!"),
                                   (g->u.has_trait("QUILLS") ? _("quills") : _("spines")));
                    }
                    hurt(spine);
                }

                if (dam + cut <= 0)
                {
                    return; // Defensive technique canceled damage.
                }

                //Hallucinations don't actually hurt the player, but do produce the message
                if(is_hallucination()) {

                    //~14% chance of vanishing after hitting the player
                    if(one_in(7)) {
                      die(g);
                      return;
                    }

                } else {

                    //Hurt the player
                    dam = p.hit(g, bphit, side, dam, cut);

                    //Monster effects
                    if (dam > 0 && has_flag(MF_VENOM)) {
                        g->add_msg_if_player(&p, _("You're poisoned!"));
                        p.add_disease("poison", 30);
                    } else if (dam > 0 && has_flag(MF_BADVENOM)) {
                        g->add_msg_if_player(&p, _("You feel poison flood your body, wracking you with pain..."));
                        p.add_disease("badpoison", 40);
                    } else if (dam > 0 && has_flag(MF_PARALYZE)) {
                        g->add_msg_if_player(&p, _("You feel poison enter your body!"));
                        p.add_disease("paralyzepoison", 100, false, 1, 20, 100);
                    }

                    if (has_flag(MF_BLEED) && dam > 6 && cut > 0) {
                        g->add_msg_if_player(&p, _("You're Bleeding!"));
                        p.add_disease("bleed", 60, false, 1, 3, 120, 1, bphit, side, true);
                    }

                    //Same as monster's chance to not miss
                    if (can_grab && has_flag(MF_GRABS) &&
                        (rng(0, 10000) > 11000 * exp(-.3 * type->melee_skill)))
                    {
                        g->add_msg(_("The %s grabs you!"), name().c_str());
                        if (p.has_grab_break_tec() &&
                            dice(p.dex_cur + p.skillLevel("melee"), 12) > dice(type->melee_dice, 10))
                        {
                            g->add_msg_if_player(&p, _("You break the grab!"));
                        } else {
                            hit_player(g, p, false); //We grabed, so hit them again
                        }
                    }

                }
                // TODO: readd with counter mechanic
            }
        }
    }

    // if dam > 0
    if (is_npc)
    {
        if (p.hp_cur[hp_head] <= 0 || p.hp_cur[hp_torso] <= 0)
        {
            npc* tmp = dynamic_cast<npc*>(&p);
            tmp->die(g);
            int index = g->npc_at(p.posx, p.posy);
            if (index != -1 && index < g->active_npc.size())
            {
                g->active_npc.erase(g->active_npc.begin() + index);
            }
            plans.clear();
        }
    }

    // Adjust anger/morale of same-species monsters, if appropriate
    int anger_adjust = 0, morale_adjust = 0;
    if (type->has_anger_trigger(MTRIG_FRIEND_ATTACKED)){
        anger_adjust += 15;
    }
    if (type->has_fear_trigger(MTRIG_FRIEND_ATTACKED)){
        morale_adjust -= 15;
    }
    if (type->has_placate_trigger(MTRIG_FRIEND_ATTACKED)){
        anger_adjust -= 15;
    }

    if (anger_adjust != 0 && morale_adjust != 0)
    {
        for (int i = 0; i < g->num_zombies(); i++)
        {
            g->zombie(i).morale += morale_adjust;
            g->zombie(i).anger += anger_adjust;
        }
    }
}
Exemple #10
0
void game::throw_item(player &p, int tarx, int tary, item &thrown,
                      std::vector<point> &trajectory)
{
 int deviation = 0;
 int trange = 1.5 * rl_dist(p.posx, p.posy, tarx, tary);

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

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

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

 deviation += p.throw_dex_mod();

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

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

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

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

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

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

 int i = 0, tx = 0, ty = 0;
 for (i = 0; i < trajectory.size() && dam > -10; i++) {
  message = "";
  double goodhit = missed_by;
  tx = trajectory[i].x;
  ty = trajectory[i].y;
// If there's a monster in the path of our item, and either our aim was true,
//  OR it's not the monster we were aiming at and we were lucky enough to hit it
  if (mon_at(tx, ty) != -1 &&
      (!missed || one_in(7 - int(z[mon_at(tx, ty)].type->size)))) {
   if (rng(0, 100) < 20 + skillLevel * 12 &&
       thrown.type->melee_cut > 0) {
    if (!p.is_npc()) {
     message += " You cut the ";
     message += z[mon_at(tx, ty)].name();
     message += "!";
    }
    if (thrown.type->melee_cut > z[mon_at(tx, ty)].armor_cut())
     dam += (thrown.type->melee_cut - z[mon_at(tx, ty)].armor_cut());
   }
   if (thrown.made_of(GLASS) && !thrown.active && // active = molotov, etc.
       rng(0, thrown.volume() + 8) - rng(0, p.str_cur) < thrown.volume()) {
    if (u_see(tx, ty))
     add_msg("The %s shatters!", thrown.tname().c_str());
    for (int i = 0; i < thrown.contents.size(); i++)
     m.add_item(tx, ty, thrown.contents[i]);
    sound(tx, ty, 16, "glass breaking!");
    int glassdam = rng(0, thrown.volume() * 2);
    if (glassdam > z[mon_at(tx, ty)].armor_cut())
     dam += (glassdam - z[mon_at(tx, ty)].armor_cut());
   } else
    m.add_item(tx, ty, thrown);
   if (i < trajectory.size() - 1)
    goodhit = double(double(rand() / RAND_MAX) / 2);
   if (goodhit < .1 && !z[mon_at(tx, ty)].has_flag(MF_NOHEAD)) {
    message = "Headshot!";
    dam = rng(dam, dam * 3);
    p.practice(turn, "throw", 5);
   } else if (goodhit < .2) {
    message = "Critical!";
    dam = rng(dam, dam * 2);
    p.practice(turn, "throw", 2);
   } else if (goodhit < .4)
    dam = rng(int(dam / 2), int(dam * 1.5));
   else if (goodhit < .5) {
    message = "Grazing hit.";
    dam = rng(0, dam);
   }
   if (!p.is_npc())
    add_msg("%s You hit the %s for %d damage.",
            message.c_str(), z[mon_at(tx, ty)].name().c_str(), dam);
   else if (u_see(tx, ty))
    add_msg("%s hits the %s for %d damage.", message.c_str(),
            z[mon_at(tx, ty)].name().c_str(), dam);
   if (z[mon_at(tx, ty)].hurt(dam))
    kill_mon(mon_at(tx, ty), !p.is_npc());
   return;
  } else // No monster hit, but the terrain might be.
   m.shoot(this, tx, ty, dam, false, 0);
  if (m.move_cost(tx, ty) == 0) {
   if (i > 0) {
    tx = trajectory[i - 1].x;
    ty = trajectory[i - 1].y;
   } else {
    tx = u.posx;
    ty = u.posy;
   }
   i = trajectory.size();
  }
 }
 if (m.move_cost(tx, ty) == 0) {
  if (i > 1) {
   tx = trajectory[i - 2].x;
   ty = trajectory[i - 2].y;
  } else {
   tx = u.posx;
   ty = u.posy;
  }
 }
 if (thrown.made_of(GLASS) && !thrown.active && // active means molotov, etc
     rng(0, thrown.volume() + 8) - rng(0, p.str_cur) < thrown.volume()) {
  if (u_see(tx, ty))
   add_msg("The %s shatters!", thrown.tname().c_str());
  for (int i = 0; i < thrown.contents.size(); i++)
   m.add_item(tx, ty, thrown.contents[i]);
  sound(tx, ty, 16, "glass breaking!");
 } else {
  sound(tx, ty, 8, "thud.");
  m.add_item(tx, ty, thrown);
 }
}
Exemple #11
0
void monster::hit_player(game *g, player &p, bool can_grab)
{
 if (type->melee_dice == 0) // We don't attack, so just return
  return;
 add_effect(ME_HIT_BY_PLAYER, 3); // Make us a valid target for a few turns
 if (has_flag(MF_HIT_AND_RUN))
  add_effect(ME_RUN, 4);
 bool is_npc = p.is_npc();
 int  junk;
 bool u_see = (!is_npc || g->u_see(p.posx, p.posy, junk));
 std::string you  = (is_npc ? p.name : "you");
 std::string You  = (is_npc ? p.name : "You");
 std::string your = (is_npc ? p.name + "'s" : "your");
 std::string Your = (is_npc ? p.name + "'s" : "Your");
 body_part bphit;
 int side = rng(0, 1);
 int dam = hit(g, p, bphit), cut = type->melee_cut, stab = 0;
 technique_id tech = p.pick_defensive_technique(g, this, NULL);
 p.perform_defensive_technique(tech, g, this, NULL, bphit, side,
                               dam, cut, stab);
 if (dam == 0 && u_see)
  g->add_msg("The %s misses %s.", name().c_str(), you.c_str());
 else if (dam > 0) {
  if (u_see && tech != TEC_BLOCK)
   g->add_msg("The %s hits %s %s.", name().c_str(), your.c_str(),
              body_part_name(bphit, side).c_str());
// Attempt defensive moves

  if (!is_npc) {
   if (g->u.activity.type == ACT_RELOAD)
    g->add_msg("You stop reloading.");
   else if (g->u.activity.type == ACT_READ)
    g->add_msg("You stop reading.");
   else if (g->u.activity.type == ACT_CRAFT)
    g->add_msg("You stop crafting.");
   g->u.activity.type = ACT_NULL;
  }
  if (p.has_active_bionic(bio_ods)) {
   if (u_see)
    g->add_msg("%s offensive defense system shocks it!", Your.c_str());
   hurt(rng(10, 40));
  }
  if (p.encumb(bphit) == 0 &&
      (p.has_trait(PF_SPINES) || p.has_trait(PF_QUILLS))) {
   int spine = rng(1, (p.has_trait(PF_QUILLS) ? 20 : 8));
   g->add_msg("%s %s puncture it!", Your.c_str(),
              (g->u.has_trait(PF_QUILLS) ? "quills" : "spines"));
   hurt(spine);
  }

  if (dam + cut <= 0)
   return; // Defensive technique canceled damage.

  p.hit(g, bphit, side, dam, cut);
  if (has_flag(MF_VENOM)) {
   if (!is_npc)
    g->add_msg("You're poisoned!");
   p.add_disease(DI_POISON, 30, g);
  } else if (has_flag(MF_BADVENOM)) {
   if (!is_npc)
    g->add_msg("You feel poison flood your body, wracking you with pain...");
   p.add_disease(DI_BADPOISON, 40, g);
  }
  if (has_flag(MF_BLEED) && dam > 6 && cut > 0) {
   if (!is_npc)
    g->add_msg("You're Bleeding!");
   p.add_disease(DI_BLEED, 30, g);
  }
  if (can_grab && has_flag(MF_GRABS) &&
      dice(type->melee_dice, 10) > dice(p.dodge(g), 10)) {
   if (!is_npc)
    g->add_msg("The %s grabs you!", name().c_str());
   if (p.weapon.has_technique(TEC_BREAK, &p) &&
       dice(p.dex_cur + p.skillLevel("melee").level(), 12) > dice(type->melee_dice, 10)){
    if (!is_npc)
     g->add_msg("You break the grab!");
   } else
    hit_player(g, p, false);
  }

  if (tech == TEC_COUNTER && !is_npc) {
   g->add_msg("Counter-attack!");
   hurt( p.hit_mon(g, this) );
  }
 } // if dam > 0
 if (is_npc) {
  if (p.hp_cur[hp_head] <= 0 || p.hp_cur[hp_torso] <= 0) {
   npc* tmp = dynamic_cast<npc*>(&p);
   tmp->die(g);
   int index = g->npc_at(p.posx, p.posy);
   if (index != -1 && index < g->active_npc.size())
    g->active_npc.erase(g->active_npc.begin() + index);
   plans.clear();
  }
 }
// Adjust anger/morale of same-species monsters, if appropriate
 int anger_adjust = 0, morale_adjust = 0;
 for (unsigned int i = 0; i < type->anger.size(); i++) {
  if (type->anger[i] == MTRIG_FRIEND_ATTACKED)
   anger_adjust += 15;
 }
 for (unsigned int i = 0; i < type->placate.size(); i++) {
  if (type->placate[i] == MTRIG_FRIEND_ATTACKED)
   anger_adjust -= 15;
 }
 for (unsigned int i = 0; i < type->fear.size(); i++) {
  if (type->fear[i] == MTRIG_FRIEND_ATTACKED)
   morale_adjust -= 15;
 }
 if (anger_adjust != 0 && morale_adjust != 0) {
  for (unsigned int i = 0; i < g->z.size(); i++) {
   g->z[i].morale += morale_adjust;
   g->z[i].anger += anger_adjust;
  }
 }
}
Exemple #12
0
void monster::hit_player(game *g, player &p)
{
 if (type->melee_dice == 0) // We don't attack, so just return
  return;
 bool is_npc = p.is_npc();
 int  junk;
 bool u_see = (!is_npc || g->u_see(p.posx, p.posy, junk));
 std::string you  = (is_npc ? p.name : "you");
 std::string your = (is_npc ? p.name + "'s" : "your");
 std::string Your = (is_npc ? p.name + "'s" : "Your");
 body_part bphit;
 int side = rng(0, 1);
 int dam = hit(p, bphit);
 if (dam == 0 && u_see)
  g->add_msg("The %s misses %s.", name().c_str(), you.c_str());
 else if (dam > 0) {
  if (u_see)
   g->add_msg("The %s hits %s %s.", name().c_str(), your.c_str(),
              body_part_name(bphit, side).c_str());
  if (!is_npc) {
   if (g->u.activity.type == ACT_RELOAD)
    g->add_msg("You stop reloading.");
   else if (g->u.activity.type == ACT_READ)
    g->add_msg("You stop reading.");
   else if (g->u.activity.type == ACT_CRAFT)
    g->add_msg("You stop crafting.");
   g->u.activity.type = ACT_NULL;
  }
  if (p.has_active_bionic(bio_ods)) {
   if (u_see)
    g->add_msg("%s offensive defense system shocks it!", Your.c_str());
   hurt(rng(10, 40));
  }
  if (p.encumb(bphit) == 0 &&
      (p.has_trait(PF_SPINES) || p.has_trait(PF_QUILLS))) {
   int spine = rng(1, (p.has_trait(PF_QUILLS) ? 20 : 8));
   g->add_msg("%s %s puncture it!", Your.c_str(),
              (g->u.has_trait(PF_QUILLS) ? "quills" : "spines"));
   hurt(spine);
  }
  p.hit(g, bphit, side, dam, type->melee_cut);
  if (has_flag(MF_VENOM)) {
   if (!is_npc)
    g->add_msg("You're poisoned!");
   p.add_disease(DI_POISON, 30, g);
  } else if (has_flag(MF_BADVENOM)) {
   if (!is_npc)
    g->add_msg("You feel poison flood your body, wracking you with pain...");
   p.add_disease(DI_BADPOISON, 40, g);
  }
 }
 if (is_npc) {
  if (p.hp_cur[hp_head] <= 0 || p.hp_cur[hp_torso] <= 0) {
   npc* tmp = dynamic_cast<npc*>(&p);
   tmp->die(g);
   int index = g->npc_at(p.posx, p.posy);
   g->active_npc.erase(g->active_npc.begin() + index);
   plans.clear();
  }
 }
}
Exemple #13
0
void map::drawsq(WINDOW* w, player &u, int x, int y, bool invert,
                 bool show_items)
{
    if (!inbounds(x, y))
        return;	// Out of bounds
    int k = x + SEEX - u.posx;
    int j = y + SEEY - u.posy;
    nc_color tercol;
    char sym = terlist[ter(x, y)].sym;
    bool hi = false;
    if (u.has_disease(DI_BOOMERED))
        tercol = c_magenta;
    else if ((u.is_wearing(itm_goggles_nv) && u.has_active_item(itm_UPS_on)) ||
             u.has_active_bionic(bio_night_vision))
        tercol = c_ltgreen;
    else
        tercol = terlist[ter(x, y)].color;
    if (move_cost(x, y) == 0 && has_flag(swimmable, x, y) && !u.underwater)
        show_items = false;	// Can only see underwater items if WE are underwater
// If there's a trap here, and we have sufficient perception, draw that instead
    if (tr_at(x, y) != tr_null &&
            u.per_cur - u.encumb(bp_eyes) >= (*traps)[tr_at(x, y)]->visibility) {
        tercol = (*traps)[tr_at(x, y)]->color;
        if ((*traps)[tr_at(x, y)]->sym == '%') {
            switch(rng(1, 5)) {
            case 1:
                sym = '*';
                break;
            case 2:
                sym = '0';
                break;
            case 3:
                sym = '8';
                break;
            case 4:
                sym = '&';
                break;
            case 5:
                sym = '+';
                break;
            }
        } else
            sym = (*traps)[tr_at(x, y)]->sym;
    }
// If there's a field here, draw that instead (unless its symbol is %)
    if (field_at(x, y).type != fd_null) {
        tercol = fieldlist[field_at(x, y).type].color[field_at(x, y).density - 1];
        if (fieldlist[field_at(x, y).type].sym == '*') {
            switch (rng(1, 5)) {
            case 1:
                sym = '*';
                break;
            case 2:
                sym = '0';
                break;
            case 3:
                sym = '8';
                break;
            case 4:
                sym = '&';
                break;
            case 5:
                sym = '+';
                break;
            }
        } else if (fieldlist[field_at(x, y).type].sym != '%')
            sym = fieldlist[field_at(x, y).type].sym;
    }
// If there's items here, draw those instead
    if (show_items && i_at(x, y).size() > 0 && field_at(x, y).is_null()) {
        if ((terlist[ter(x, y)].sym != '.'))
            hi = true;
        else {
            tercol = i_at(x, y)[i_at(x, y).size() - 1].color();
            if (i_at(x, y).size() > 1)
                invert = !invert;
            sym = i_at(x, y)[i_at(x, y).size() - 1].symbol();
        }
    }
    if (invert)
        mvwputch_inv(w, j, k, tercol, sym);
    else if (hi)
        mvwputch_hi (w, j, k, tercol, sym);
    else
        mvwputch    (w, j, k, tercol, sym);
}
Exemple #14
0
void game::throw_item(player &p, int tarx, int tary, item &thrown,
                      std::vector<point> &trajectory)
{
    int deviation = 0;
    int trange = 1.5 * rl_dist(p.posx, p.posy, tarx, tary);
    std::set<std::string> no_effects;

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

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

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

    deviation += p.throw_dex_mod();

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

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

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

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

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

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

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

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

        const int zid = mon_at(tx, ty);

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

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

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

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

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

        for (int i = 0; i < thrown.contents.size(); i++) {
            m.add_item_or_charges(tx, ty, thrown.contents[i]);
        }
        sound(tx, ty, 16, _("glass breaking!"));
    } else {
        if(m.has_flag("LIQUID", tx, ty)) {
            sound(tx, ty, 10, _("splash!"));
        } else {
            sound(tx, ty, 8, _("thud."));
        }
        m.add_item_or_charges(tx, ty, thrown);
    }
}