示例#1
0
bool monster::is_fleeing(player &u)
{
// fleefactor is by default the agressiveness of the animal, minus the
//  percentage of remaining HP times four.  So, aggresiveness of 5 has a
//  fleefactor of 2 AT MINIMUM.
    if (type->hp == 0) {
        debugmsg("%s has type->hp of 0!", type->name.c_str());
        return false;
    }

    if (friendly != 0)
        return false;

    int fleefactor = type->agro - ((4 * (type->hp - hp)) / type->hp);
    if (u.has_trait(PF_ANIMALEMPATH) && has_flag(MF_ANIMAL)) {
        if (type->agro > 0)	// Agressive animals flee instead
            fleefactor -= 5;
        else			// Scared animals approach you
            return false;
    }
    if (u.has_trait(PF_TERRIFYING))
        fleefactor -= 1;
    if (fleefactor > 0)
        return false;
    return true;
}
示例#2
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;
}
示例#3
0
文件: wish.cpp 项目: 8Z/Cataclysm-DDA
 virtual bool key(int key, int entnum, uimenu *menu) {
     if ( key == 't' && p->has_trait( vTraits[ entnum ] ) ) {
          if ( p->has_base_trait( vTraits[ entnum ] ) ) {
               p->toggle_trait( vTraits[ entnum ] );
               p->toggle_mutation( vTraits[ entnum ] );
          } else {
               p->toggle_mutation( vTraits[ entnum ] );
               p->toggle_trait( vTraits[ entnum ] );
          }
          menu->entries[ entnum ].text_color = ( p->has_trait( vTraits[ entnum ] ) ? c_green : menu->text_color );
          menu->entries[ entnum ].extratxt.txt= ( p->has_base_trait( vTraits[ entnum ] ) ? "T" : "" );
          return true;
     }
     return false;
 }
示例#4
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;
}
示例#5
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;
}
示例#6
0
        bool key( const input_event &event, int entnum, uimenu *menu ) override {
            if( event.get_first_input() == 't' && p->has_trait( vTraits[ entnum ] ) ) {
                if( p->has_base_trait( vTraits[ entnum ] ) ) {
                    p->toggle_trait( vTraits[ entnum ] );
                    p->unset_mutation( vTraits[ entnum ] );

                } else {
                    p->set_mutation( vTraits[ entnum ] );
                    p->toggle_trait( vTraits[ entnum ] );
                }
                menu->entries[ entnum ].text_color = ( p->has_trait( vTraits[ entnum ] ) ? c_green :
                                                       menu->text_color );
                menu->entries[ entnum ].extratxt.txt = ( p->has_base_trait( vTraits[ entnum ] ) ? "T" : "" );
                return true;
            }
            return false;
        }
示例#7
0
std::string addiction_text(player const& u, addiction const &cur)
{
    int const strpen = 1 + cur.intensity / 7;
    switch (cur.type) {
    case ADD_CIG:
        return _("Intelligence - 1;   Occasional cravings");

    case ADD_CAFFEINE:
        return _("Strength - 1;   Slight sluggishness;   Occasional cravings");

    case ADD_ALCOHOL:
        return _("Perception - 1;   Intelligence - 1;   Occasional Cravings;\n"
                 "Risk of delirium tremens");

    case ADD_SLEEP:
        return _("You may find it difficult to sleep without medication.");

    case ADD_PKILLER: {
        if (u.has_trait("NOPAIN")) {
            return string_format(_( "Strength - %d;   Perception - 1;   Dexterity - 1;\n"
                                    "Depression.  Frequent cravings.  Vomiting."), strpen);
        } else {
            return string_format(_( "Strength - %d;   Perception - 1;   Dexterity - 1;\n"
                                    "Depression and physical pain to some degree.  Frequent cravings.  Vomiting."), strpen);
        }
    }

    case ADD_SPEED:
        return _("Strength - 1;   Intelligence - 1;\n"
                 "Movement rate reduction.  Depression.  Weak immune system.  Frequent cravings.");

    case ADD_COKE:
        return _("Perception - 1;   Intelligence - 1;  Frequent cravings.");

    case ADD_CRACK:
        return _("Perception - 2;   Intelligence - 2;  Frequent cravings.");

    case ADD_MUTAGEN:
        return _("You've gotten a taste for mutating and the chemicals that cause it.  But you can stop, yeah, any time you want.");

    case ADD_DIAZEPAM:
        return _("Perception - 1;   Intelligence - 1;\n"
                 "Anxiety, nausea, hallucinations, and general malaise.");

    case ADD_MARLOSS_R:
        return _("You should try some of those pink berries.");

    case ADD_MARLOSS_B:
        return _("You should try some of those cyan seeds.");

    case ADD_MARLOSS_Y:
        return _("You should try some of that golden gel.");

    default:
        return "";
    }
}
示例#8
0
bool player_can_build( player &p, const inventory &pinv, const construction &con )
{
    if( p.has_trait( "DEBUG_HS" ) ) {
        return true;
    }

    if( p.get_skill_level( con.skill ) < con.difficulty ) {
        return false;
    }
    return con.requirements->can_make_with_inventory( pinv );
}
示例#9
0
int stumble(player &u)
{
 int stumble_pen = 2 * u.weapon.volume() + u.weapon.weight();
 if (u.has_trait(PF_DEFT))
  stumble_pen = int(stumble_pen * .3) - 10;
 if (stumble_pen < 0)
  stumble_pen = 0;
// TODO: Reflect high strength bonus in newcharacter.cpp
 if (stumble_pen > 0 && (u.str_cur >= 15 || u.dex_cur >= 21 ||
                         one_in(16 - u.str_cur) || one_in(22 - u.dex_cur)))
  stumble_pen = rng(0, stumble_pen);

 return stumble_pen;
}
void addict_effect( player &u, addiction &add )
{
    const int in = std::min( 20, add.intensity );

    switch( add.type ) {
        case ADD_CIG:
            if( !one_in( 2000 - 20 * in ) ) {
                break;
            }

            u.add_msg_if_player( rng( 0, 6 ) < in ?
                                 _( "You need some nicotine." ) :
                                 _( "You could use some nicotine." ) );
            u.add_morale( MORALE_CRAVING_NICOTINE, -15, -3 * in );
            if( one_in( 800 - 50 * in ) ) {
                u.mod_fatigue( 1 );
            }
            if( u.stim > -5 * in && one_in( 400 - 20 * in ) ) {
                u.stim--;
            }
            break;

        case ADD_CAFFEINE:
            if( !one_in( 2000 - 20 * in ) ) {
                break;
            }

            u.add_msg_if_player( m_warning, _( "You want some caffeine." ) );
            u.add_morale( MORALE_CRAVING_CAFFEINE, -5, -30 );
            if( u.stim > -10 * in && rng( 0, 10 ) < in ) {
                u.stim--;
            }
            if( rng( 8, 400 ) < in ) {
                u.add_msg_if_player( m_bad, _( "Your hands start shaking... you need it bad!" ) );
                u.add_effect( effect_shakes, 20 );
            }
            break;

        case ADD_ALCOHOL:
        case ADD_DIAZEPAM: {
            static const std::string alc_1 = _( "You could use a drink. " );
            static const std::string alc_2 = _( "Your hands start shaking... you need a drink bad!" ) ;
            static const std::string dia_1 = _( "You could use some diazepam." );
            static const std::string dia_2 = _( "You're shaking... you need some diazepam!" );

            const std::string &msg_1 = add.type == ADD_ALCOHOL ? alc_1 : dia_1;
            const std::string &msg_2 = add.type == ADD_ALCOHOL ? alc_2 : dia_2;

            const auto morale_type = add.type == ADD_ALCOHOL ? MORALE_CRAVING_ALCOHOL : MORALE_CRAVING_DIAZEPAM;

            u.mod_per_bonus( -1 );
            u.mod_int_bonus( -1 );
            if( x_in_y( in, HOURS( 2 ) ) ) {
                u.mod_healthy_mod( -1, -in * 10 );
            }
            if( one_in( 20 ) && rng( 0, 20 ) < in ) {
                u.add_msg_if_player( m_warning, msg_1.c_str() );
                u.add_morale( morale_type, -35, -10 * in );
            } else if( rng( 8, 300 ) < in ) {
                u.add_msg_if_player( m_bad, msg_2.c_str() );
                u.add_morale( morale_type, -35, -10 * in );
                u.add_effect( effect_shakes, 50 );
            } else if( !u.has_effect( effect_hallu ) && rng( 10, 1600 ) < in ) {
                u.add_effect( effect_hallu, 3600 );
            }
            break;
        }

        case ADD_SLEEP:
            // No effects here--just in player::can_sleep()
            // EXCEPT!  Prolong this addiction longer than usual.
            if( one_in( 2 ) && add.sated < 0 ) {
                add.sated++;
            }
            break;

        case ADD_PKILLER:
            if( calendar::once_every( 100 - in * 4 ) && u.get_painkiller() > 20 - in ) {
                u.mod_painkiller( -1 );    // Tolerance increases!
            }
            if( u.get_painkiller() >= 35 ) { // No further effects if we're doped up.
                add.sated = 0;
                break;
            }

            u.mod_str_bonus( -1 );
            u.mod_per_bonus( -1 );
            u.mod_dex_bonus( -1 );
            if( u.get_pain() < in * 2 ) {
                u.mod_pain( 1 );
            }
            if( one_in( 1200 - 30 * in ) ) {
                u.mod_healthy_mod( -1, -in * 30 );
            }
            if( one_in( 20 ) && dice( 2, 20 ) < in ) {
                u.add_msg_if_player( m_bad, _( "Your hands start shaking... you need some painkillers." ) );
                u.add_morale( MORALE_CRAVING_OPIATE, -40, -10 * in );
                u.add_effect( effect_shakes, 20 + in * 5 );
            } else if( one_in( 20 ) && dice( 2, 30 ) < in ) {
                u.add_msg_if_player( m_bad, _( "You feel anxious.  You need your painkillers!" ) );
                u.add_morale( MORALE_CRAVING_OPIATE, -30, -10 * in );
            } else if( one_in( 50 ) && dice( 3, 50 ) < in ) {
                u.vomit();
            }
            break;

        case ADD_SPEED: {
            u.mod_int_bonus( -1 );
            u.mod_str_bonus( -1 );
            if( u.stim > -100 && x_in_y( in, 20 ) ) {
                u.stim--;
            }
            if( rng( 0, 150 ) <= in ) {
                u.mod_healthy_mod( -1, -in );
            }
            if( dice( 2, 100 ) < in ) {
                u.add_msg_if_player( m_warning, _( "You feel depressed.  Speed would help." ) );
                u.add_morale( MORALE_CRAVING_SPEED, -25, -20 * in );
            } else if( one_in( 10 ) && dice( 2, 80 ) < in ) {
                u.add_msg_if_player( m_bad, _( "Your hands start shaking... you need a pick-me-up." ) );
                u.add_morale( MORALE_CRAVING_SPEED, -25, -20 * in );
                u.add_effect( effect_shakes, in * 20 );
            } else if( one_in( 50 ) && dice( 2, 100 ) < in ) {
                u.add_msg_if_player( m_bad, _( "You stop suddenly, feeling bewildered." ) );
                u.moves -= 300;
            } else if( !u.has_effect( effect_hallu ) && one_in( 20 ) && 8 + dice( 2, 80 ) < in ) {
                u.add_effect( effect_hallu, 3600 );
            }
        }
        break;

        case ADD_COKE:
        case ADD_CRACK: {
            static const std::string coke_msg = _( "You feel like you need a bump." );
            static const std::string crack_msg = _( "You're shivering, you need some crack." );
            const std::string &cur_msg = add.type == ADD_COKE ? coke_msg : crack_msg;
            const auto morale_type = add.type == ADD_COKE ? MORALE_CRAVING_COCAINE : MORALE_CRAVING_CRACK;
            u.mod_int_bonus( -1 );
            u.mod_per_bonus( -1 );
            if( one_in( 900 - 30 * in ) ) {
                u.add_msg_if_player( m_warning, cur_msg.c_str() );
                u.add_morale( morale_type, -20, -15 * in );
            }
            if( dice( 2, 80 ) <= in ) {
                u.add_msg_if_player( m_warning, cur_msg.c_str() );
                u.add_morale( morale_type, -20, -15 * in );
                if( u.stim > -150 ) {
                    u.stim -= 3;
                }
            }
            break;
        }

        case ADD_MUTAGEN:
            if( u.has_trait( "MUT_JUNKIE" ) ) {
                if( one_in( 600 - 50 * in ) ) {
                    u.add_msg_if_player( m_warning, rng( 0,
                                                         6 ) < in ? _( "You so miss the exquisite rainbow of post-humanity." ) :
                                         _( "Your body is SOO booorrrring.  Just a little sip to liven things up?" ) );
                    u.add_morale( MORALE_CRAVING_MUTAGEN, -20, -200 );
                }
                if( u.focus_pool > 40 && one_in( 800 - 20 * in ) ) {
                    u.focus_pool -= ( in );
                    u.add_msg_if_player( m_warning,
                                         _( "You daydream what it'd be like if you were *different*.  Different is good." ) );
                }
            } else if( in > 5 || one_in( ( 500 - 15 * in ) ) ) {
                u.add_msg_if_player( m_warning, rng( 0, 6 ) < in ? _( "You haven't had any mutagen lately." ) :
                                     _( "You could use some new parts..." ) );
                u.add_morale( MORALE_CRAVING_MUTAGEN, -5, -50 );
            }
            break;
        case ADD_MARLOSS_R:
            marloss_add( u, in, _( "You daydream about luscious pink berries as big as your fist." ) );
            break;
        case ADD_MARLOSS_B:
            marloss_add( u, in, _( "You daydream about nutty cyan seeds as big as your hand." ) );
            break;
        case ADD_MARLOSS_Y:
            marloss_add( u, in, _( "You daydream about succulent, pale golden gel, sweet but light." ) );
            break;
        case ADD_NULL:
            break;
    }
}
示例#11
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 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;
        }
    }
}
示例#12
0
void game::fire(player &p, int tarx, int tary, std::vector<point> &trajectory,
                bool burst)
{
 item ammotmp;
 item* gunmod = p.weapon.active_gunmod();
 it_ammo *curammo = NULL;
 item *weapon = NULL;

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

 if (weapon->num_charges() == 0)
  weapon->curammo = NULL;
}
示例#13
0
        virtual void select(int entnum, uimenu *menu) {
            if ( ! started ) {
                started = true;
                padding = std::string(menu->pad_right - 1, ' ');
                for (std::map<std::string, trait>::iterator iter = traits.begin(); iter != traits.end(); ++iter) {
                    vTraits.push_back(iter->first);
                    pTraits[iter->first] = ( p->has_trait( iter->first ) );
                }
            }

            int startx = menu->w_width - menu->pad_right;
            for ( int i = 1; i < lastlen; i++ ) {
                mvwprintw(menu->window, i, startx, "%s", padding.c_str() );
            }

            mvwprintw(menu->window, 1, startx,
                      mutation_data[vTraits[ entnum ]].valid ? _("Valid") : _("Nonvalid"));
            int line2 = 2;

            if ( !mutation_data[vTraits[entnum]].prereqs.empty() ) {
                line2++;
                mvwprintz(menu->window, line2, startx, c_ltgray, _("Prereqs:"));
                for (int j = 0; j < mutation_data[vTraits[ entnum ]].prereqs.size(); j++) {
                    std::string mstr = mutation_data[vTraits[ entnum ]].prereqs[j];
                    mvwprintz(menu->window, line2, startx + 11, mcolor(mstr), "%s", traits[ mstr ].name.c_str());
                    line2++;
                }
            }

            if ( !mutation_data[vTraits[entnum]].prereqs2.empty() ) {
                line2++;
                mvwprintz(menu->window, line2, startx, c_ltgray, _("Prereqs, 2d:"));
                for (int j = 0; j < mutation_data[vTraits[ entnum ]].prereqs2.size(); j++) {
                    std::string mstr = mutation_data[vTraits[ entnum ]].prereqs2[j];
                    mvwprintz(menu->window, line2, startx + 15, mcolor(mstr), "%s", traits[ mstr ].name.c_str());
                    line2++;
                }
            }

            if ( !mutation_data[vTraits[entnum]].threshreq.empty() ) {
                line2++;
                mvwprintz(menu->window, line2, startx, c_ltgray, _("Thresholds required:"));
                for (int j = 0; j < mutation_data[vTraits[ entnum ]].threshreq.size(); j++) {
                    std::string mstr = mutation_data[vTraits[ entnum ]].threshreq[j];
                    mvwprintz(menu->window, line2, startx + 21, mcolor(mstr), "%s", traits[ mstr ].name.c_str());
                    line2++;
                }
            }

            if ( !mutation_data[vTraits[entnum]].cancels.empty() ) {
                line2++;
                mvwprintz(menu->window, line2, startx, c_ltgray, _("Cancels:"));
                for (int j = 0; j < mutation_data[vTraits[ entnum ]].cancels.size(); j++) {
                    std::string mstr = mutation_data[vTraits[ entnum ]].cancels[j];
                    mvwprintz(menu->window, line2, startx + 11, mcolor(mstr), "%s", traits[ mstr ].name.c_str());
                    line2++;
                }
            }

            if ( !mutation_data[vTraits[entnum]].replacements.empty() ) {
                line2++;
                mvwprintz(menu->window, line2, startx, c_ltgray, _("Becomes:"));
                for (int j = 0; j < mutation_data[vTraits[ entnum ]].replacements.size(); j++) {
                    std::string mstr = mutation_data[vTraits[ entnum ]].replacements[j];
                    mvwprintz(menu->window, line2, startx + 11, mcolor(mstr), "%s", traits[ mstr ].name.c_str());
                    line2++;
                }
            }

            if ( !mutation_data[vTraits[entnum]].additions.empty() ) {
                line2++;
                mvwprintz(menu->window, line2, startx, c_ltgray, _("Add-ons:"));
                for (int j = 0; j < mutation_data[vTraits[ entnum ]].additions.size(); j++) {
                    std::string mstr = mutation_data[vTraits[ entnum ]].additions[j];
                    mvwprintz(menu->window, line2, startx + 11, mcolor(mstr), "%s", traits[ mstr ].name.c_str());
                    line2++;
                }
            }

            if ( !mutation_data[vTraits[entnum]].category.empty() ) {
                line2++;
                mvwprintz(menu->window, line2, startx, c_ltgray,  _("Category:"));
                for (int j = 0; j < mutation_data[vTraits[ entnum ]].category.size(); j++) {
                    mvwprintw(menu->window, line2, startx + 11, "%s", mutation_data[vTraits[ entnum ]].category[j].c_str());
                    line2++;
                }
            }
            line2 += 2;

            mvwprintz(menu->window, line2, startx, c_ltgray, "pts: %d vis: %d ugly: %d",
                      traits[vTraits[entnum]].points,
                      traits[vTraits[entnum]].visibility,
                      traits[vTraits[entnum]].ugliness
                     );
            line2 += 2;

            std::vector<std::string> desc = foldstring( traits[vTraits[ entnum ]].description,
                                            menu->pad_right - 1 );
            for( size_t j = 0; j < desc.size(); ++j ) {
                mvwprintz(menu->window, line2, startx, c_ltgray, "%s", desc[j].c_str() );
                line2++;
            }
            lastlen = line2 + 1;

            mvwprintz(menu->window, menu->w_height - 3, startx, c_green, "%s", msg.c_str());
            msg = padding;
            mvwprintw(menu->window, menu->w_height - 2, startx, _("[/] find, [q]uit"));

        };
示例#14
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();
  }
 }
}
示例#15
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;
        }
    }
}
示例#16
0
        void select( int entnum, uimenu *menu ) override {
            if( ! started ) {
                started = true;
                padding = std::string( menu->pad_right - 1, ' ' );
                for( auto &traits_iter : mutation_branch::get_all() ) {
                    vTraits.push_back( traits_iter.first );
                    pTraits[traits_iter.first] = ( p->has_trait( traits_iter.first ) );
                }
            }
            const mutation_branch &mdata = vTraits[entnum].obj();

            int startx = menu->w_width - menu->pad_right;
            for( int i = 2; i < lastlen; i++ ) {
                mvwprintw( menu->window, i, startx, padding );
            }

            mvwprintw( menu->window, 3, startx,
                       mdata.valid ? _( "Valid" ) : _( "Nonvalid" ) );
            int line2 = 4;

            if( !mdata.prereqs.empty() ) {
                line2++;
                mvwprintz( menu->window, line2, startx, c_light_gray, _( "Prereqs:" ) );
                for( auto &j : mdata.prereqs ) {
                    mvwprintz( menu->window, line2, startx + 11, mcolor( j ),
                               mutation_branch::get_name( j ) );
                    line2++;
                }
            }

            if( !mdata.prereqs2.empty() ) {
                line2++;
                mvwprintz( menu->window, line2, startx, c_light_gray, _( "Prereqs, 2d:" ) );
                for( auto &j : mdata.prereqs2 ) {
                    mvwprintz( menu->window, line2, startx + 15, mcolor( j ),
                               mutation_branch::get_name( j ) );
                    line2++;
                }
            }

            if( !mdata.threshreq.empty() ) {
                line2++;
                mvwprintz( menu->window, line2, startx, c_light_gray, _( "Thresholds required:" ) );
                for( auto &j : mdata.threshreq ) {
                    mvwprintz( menu->window, line2, startx + 21, mcolor( j ),
                               mutation_branch::get_name( j ) );
                    line2++;
                }
            }

            if( !mdata.cancels.empty() ) {
                line2++;
                mvwprintz( menu->window, line2, startx, c_light_gray, _( "Cancels:" ) );
                for( auto &j : mdata.cancels ) {
                    mvwprintz( menu->window, line2, startx + 11, mcolor( j ),
                               mutation_branch::get_name( j ) );
                    line2++;
                }
            }

            if( !mdata.replacements.empty() ) {
                line2++;
                mvwprintz( menu->window, line2, startx, c_light_gray, _( "Becomes:" ) );
                for( auto &j : mdata.replacements ) {
                    mvwprintz( menu->window, line2, startx + 11, mcolor( j ),
                               mutation_branch::get_name( j ) );
                    line2++;
                }
            }

            if( !mdata.additions.empty() ) {
                line2++;
                mvwprintz( menu->window, line2, startx, c_light_gray, _( "Add-ons:" ) );
                for( auto &j : mdata.additions ) {
                    mvwprintz( menu->window, line2, startx + 11, mcolor( j ),
                               mutation_branch::get_name( j ) );
                    line2++;
                }
            }

            if( !mdata.types.empty() ) {
                line2++;
                mvwprintz( menu->window, line2, startx, c_light_gray,  _( "Type:" ) );
                for( auto &j : mdata.types ) {
                    mvwprintw( menu->window, line2, startx + 11, j );
                    line2++;
                }
            }

            if( !mdata.category.empty() ) {
                line2++;
                mvwprintz( menu->window, line2, startx, c_light_gray,  _( "Category:" ) );
                for( auto &j : mdata.category ) {
                    mvwprintw( menu->window, line2, startx + 11, j );
                    line2++;
                }
            }
            line2 += 2;

            mvwprintz( menu->window, line2, startx, c_light_gray, "pts: %d vis: %d ugly: %d",
                       mdata.points,
                       mdata.visibility,
                       mdata.ugliness
                     );
            line2 += 2;

            std::vector<std::string> desc = foldstring( mdata.description,
                                            menu->pad_right - 1 );
            for( auto &elem : desc ) {
                mvwprintz( menu->window, line2, startx, c_light_gray, elem );
                line2++;
            }
            lastlen = line2 + 1;

            mvwprintz( menu->window, menu->w_height - 3, startx, c_green, msg );
            msg = padding;
            input_context ctxt( "UIMENU" );
            mvwprintw( menu->window, menu->w_height - 2, startx,
                       _( "[%s] find, [%s] quit, [t] toggle base trait" ),
                       ctxt.get_desc( "FILTER" ).c_str(), ctxt.get_desc( "QUIT" ).c_str() );

        };
示例#17
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;
  }
 }
}
示例#18
0
void addict_effect(player &u, addiction &add,
                   std::function<void (char const*)> const &cancel_activity)
{
    int const in = add.intensity;

    switch (add.type) {
    case ADD_CIG:
        if (in > 20 || one_in((500 - 20 * in))) {
            u.add_msg_if_player(rng(0, 6) < in ? _("You need some nicotine.") :
                    _("You could use some nicotine."));
            u.add_morale(MORALE_CRAVING_NICOTINE, -15, -50);
            if (one_in(800 - 50 * in)) {
                u.fatigue++;
            }
            if (u.stim > -50 && one_in(400 - 20 * in)) {
                u.stim--;
            }
        }
        break;

    case ADD_CAFFEINE:
        u.moves -= 2;
        if (in > 20 || one_in((500 - 20 * in))) {
            u.add_msg_if_player(m_warning, _("You want some caffeine."));
            u.add_morale(MORALE_CRAVING_CAFFEINE, -5, -30);
            if (u.stim > -150 && rng(0, 10) < in) {
                u.stim--;
            }
            if (rng(8, 400) < in) {
                u.add_msg_if_player(m_bad, _("Your hands start shaking... you need it bad!"));
                u.add_effect("shakes", 20);
            }
        }
        break;

    case ADD_ALCOHOL:
        u.mod_per_bonus(-1);
        u.mod_int_bonus(-1);
        if (rng(40, 1200) <= in * 10) {
            u.mod_healthy_mod(-1, -in * 10);
        }
        if (one_in(20) && rng(0, 20) < in) {
            u.add_msg_if_player(m_warning, _("You could use a drink."));
            u.add_morale(MORALE_CRAVING_ALCOHOL, -35, -120);
        } else if (rng(8, 300) < in) {
            u.add_msg_if_player(m_bad, _("Your hands start shaking... you need a drink bad!"));
            u.add_morale(MORALE_CRAVING_ALCOHOL, -35, -120);
            u.add_effect("shakes", 50);
        } else if (!u.has_effect("hallu") && rng(10, 1600) < in) {
            u.add_effect("hallu", 3600);
        }
        break;

    case ADD_SLEEP:
        // No effects here--just in player::can_sleep()
        // EXCEPT!  Prolong this addiction longer than usual.
        if (one_in(2) && add.sated < 0) {
            add.sated++;
        }
        break;

    case ADD_PKILLER:
        if ((in >= 25 || int(calendar::turn) % (100 - in * 4) == 0) && u.pkill > 0) {
            u.pkill--;    // Tolerance increases!
        }
        if (u.pkill >= 35) { // No further effects if we're doped up.
            add.sated = 0;
        } else {
            u.mod_str_bonus(-(1 + int(in / 7)));
            u.mod_per_bonus(-1);
            u.mod_dex_bonus(-1);
            if (u.pain < in * 3) {
                u.mod_pain(1);
            }
            if (in >= 40 || one_in(1200 - 30 * in)) {
                u.mod_healthy_mod(-1, -in * 30);
            }
            if (one_in(20) && dice(2, 20) < in) {
                u.add_msg_if_player(m_bad, _("Your hands start shaking... you need some painkillers."));
                u.add_morale(MORALE_CRAVING_OPIATE, -40, -200);
                u.add_effect("shakes", 20 + in * 5);
            } else if (one_in(20) && dice(2, 30) < in) {
                u.add_msg_if_player(m_bad, _("You feel anxious.  You need your painkillers!"));
                u.add_morale(MORALE_CRAVING_OPIATE, -30, -200);
            } else if (one_in(50) && dice(3, 50) < in) {
                u.add_msg_if_player(m_bad, _("You throw up heavily!"));
                cancel_activity(_("Throwing up."));
                u.vomit();
            }
        }
        break;

    case ADD_SPEED: {
        // Minimal speed of PC is 0.25 * base speed, that is
        // usually 25 moves, this ensures that even at minimal speed
        // the PC gets 5 moves per turn.
        int move_pen = std::min(in * 5, 20);
        u.moves -= move_pen;
        u.mod_int_bonus(-1);
        u.mod_str_bonus(-1);
        if (u.stim > -100 && (in >= 20 || int(calendar::turn) % (100 - in * 5) == 0)) {
            u.stim--;
        }
        if (rng(0, 150) <= in) {
            u.mod_healthy_mod(-1, -in);
        }
        if (dice(2, 100) < in) {
            u.add_msg_if_player(m_warning, _("You feel depressed.  Speed would help."));
            u.add_morale(MORALE_CRAVING_SPEED, -25, -200);
        } else if (one_in(10) && dice(2, 80) < in) {
            u.add_msg_if_player(m_bad, _("Your hands start shaking... you need a pick-me-up."));
            u.add_morale(MORALE_CRAVING_SPEED, -25, -200);
            u.add_effect("shakes", in * 20);
        } else if (one_in(50) && dice(2, 100) < in) {
            u.add_msg_if_player(m_bad, _("You stop suddenly, feeling bewildered."));
            cancel_activity(nullptr);
            u.moves -= 300;
        } else if (!u.has_effect("hallu") && one_in(20) && 8 + dice(2, 80) < in) {
            u.add_effect("hallu", 3600);
        }
    }
    break;

    case ADD_COKE:
        u.mod_int_bonus(-1);
        u.mod_per_bonus(-1);
        if (in >= 30 || one_in((900 - 30 * in))) {
            u.add_msg_if_player(m_warning, _("You feel like you need a bump."));
            u.add_morale(MORALE_CRAVING_COCAINE, -20, -250);
        }
        if (dice(2, 80) <= in) {
            u.add_msg_if_player(m_warning, _("You feel like you need a bump."));
            u.add_morale(MORALE_CRAVING_COCAINE, -20, -250);
            if (u.stim > -150) {
                u.stim -= 3;
            }
        }
        break;

    case ADD_CRACK:
        u.mod_int_bonus(-1);
        u.mod_per_bonus(-1);
        if (in >= 30 || one_in((900 - 30 * in))) {
            u.add_msg_if_player(m_bad, _("You're shivering, you need some crack."));
            u.add_morale(MORALE_CRAVING_CRACK, -80, -250);
        }
        if (dice(2, 80) <= in) {
            u.add_msg_if_player(m_bad, _("You're shivering, you need some crack."));
            u.add_morale(MORALE_CRAVING_CRACK, -80, -250);
            if (u.stim > -150) {
                u.stim -= 3;
            }
        }
        break;

    case ADD_MUTAGEN:
        if (u.has_trait("MUT_JUNKIE")) {
            if (one_in(600 - 50 * in)) {
                u.add_msg_if_player(m_warning, rng(0, 6) < in ? _("You so miss the exquisite rainbow of post-humanity.") :
                        _("Your body is SOO booorrrring.  Just a little sip to liven things up?"));
                u.add_morale(MORALE_CRAVING_MUTAGEN, -20, -200);
            }
            if (u.focus_pool > 40 && one_in(800 - 20 * in)) {
                u.focus_pool -= (in);
                u.add_msg_if_player(m_warning,
                        _("You daydream what it'd be like if you were *different*.  Different is good."));
            }
        } else if (in > 5 || one_in((500 - 15 * in))) {
            u.add_msg_if_player(m_warning, rng(0, 6) < in ? _("You haven't had any mutagen lately.") :
                    _("You could use some new parts..."));
            u.add_morale(MORALE_CRAVING_MUTAGEN, -5, -50);
        }
        break;

    case ADD_DIAZEPAM:
        u.mod_per_bonus(-1);
        u.mod_int_bonus(-1);
        if (rng(40, 1200) <= in * 10) {
            u.mod_healthy_mod(-1, -in * 10);
        }
        if (one_in(20) && rng(0, 20) < in) {
            u.add_msg_if_player(m_warning, _("You could use some diazepam."));
            u.add_morale(MORALE_CRAVING_DIAZEPAM, -35, -120);
        } else if (rng(8, 200) < in) {
            u.add_msg_if_player(m_bad, _("You're shaking... you need some diazepam!"));
            u.add_morale(MORALE_CRAVING_DIAZEPAM, -35, -120);
            u.add_effect("shakes", 50);
        } else if (!u.has_effect("hallu") && rng(10, 3200) < in) {
            u.add_effect("hallu", 3600);
        } else if (one_in(50) && dice(3, 50) < in) {
            u.add_msg_if_player(m_bad, _("You throw up heavily!"));
            cancel_activity(_("Throwing up."));
            u.vomit();
        }
        break;
    case ADD_MARLOSS_R:
        if (one_in(800 - 20 * in)) {
            u.add_morale(MORALE_CRAVING_MARLOSS, -5, -25);
            u.add_msg_if_player(m_info, _("You daydream about luscious pink berries as big as your fist."));
            if (u.focus_pool > 40) {
                u.focus_pool -= (in);
            }
        }
        break;
    case ADD_MARLOSS_B:
        if (one_in(800 - 20 * in)) {
            u.add_morale(MORALE_CRAVING_MARLOSS, -5, -25);
            u.add_msg_if_player(m_info, _("You daydream about nutty cyan seeds as big as your hand."));
            if (u.focus_pool > 40) {
                u.focus_pool -= (in);
            }
        }
        break;
    case ADD_MARLOSS_Y:
        if (one_in(800 - 20 * in)) {
            u.add_morale(MORALE_CRAVING_MARLOSS, -5, -25);
            u.add_msg_if_player(m_info, _("You daydream about succulent, pale golden gel, sweet but light."));
            if (u.focus_pool > 40) {
                u.focus_pool -= (in);
            }
        }
        break;

    //for any other unhandled cases
    default:
        break;

    }
}
示例#19
0
        virtual void select(int entnum, uimenu *menu) override
        {
            if ( ! started ) {
                started = true;
                padding = std::string(menu->pad_right - 1, ' ');
                for( auto &traits_iter : mutation_branch::get_all() ) {
                    vTraits.push_back( traits_iter.first );
                    pTraits[traits_iter.first] = ( p->has_trait( traits_iter.first ) );
                }
            }
            auto &mdata = mutation_branch::get( vTraits[entnum] );

            int startx = menu->w_width - menu->pad_right;
            for ( int i = 2; i < lastlen; i++ ) {
                mvwprintw(menu->window, i, startx, "%s", padding.c_str() );
            }

            mvwprintw(menu->window, 3, startx,
                      mdata.valid ? _("Valid") : _("Nonvalid"));
            int line2 = 4;

            if ( !mdata.prereqs.empty() ) {
                line2++;
                mvwprintz(menu->window, line2, startx, c_ltgray, _("Prereqs:"));
                for (auto &j : mdata.prereqs) {
                    mvwprintz(menu->window, line2, startx + 11, mcolor(j), "%s", mutation_branch::get_name( j ).c_str());
                    line2++;
                }
            }

            if ( !mdata.prereqs2.empty() ) {
                line2++;
                mvwprintz(menu->window, line2, startx, c_ltgray, _("Prereqs, 2d:"));
                for (auto &j : mdata.prereqs2) {
                    mvwprintz(menu->window, line2, startx + 15, mcolor(j), "%s", mutation_branch::get_name( j ).c_str());
                    line2++;
                }
            }

            if ( !mdata.threshreq.empty() ) {
                line2++;
                mvwprintz(menu->window, line2, startx, c_ltgray, _("Thresholds required:"));
                for (auto &j : mdata.threshreq) {
                    mvwprintz(menu->window, line2, startx + 21, mcolor(j), "%s", mutation_branch::get_name( j ).c_str());
                    line2++;
                }
            }

            if ( !mdata.cancels.empty() ) {
                line2++;
                mvwprintz(menu->window, line2, startx, c_ltgray, _("Cancels:"));
                for (auto &j : mdata.cancels) {
                    mvwprintz(menu->window, line2, startx + 11, mcolor(j), "%s", mutation_branch::get_name( j ).c_str());
                    line2++;
                }
            }

            if ( !mdata.replacements.empty() ) {
                line2++;
                mvwprintz(menu->window, line2, startx, c_ltgray, _("Becomes:"));
                for (auto &j : mdata.replacements) {
                    mvwprintz(menu->window, line2, startx + 11, mcolor(j), "%s", mutation_branch::get_name( j ).c_str());
                    line2++;
                }
            }

            if ( !mdata.additions.empty() ) {
                line2++;
                mvwprintz(menu->window, line2, startx, c_ltgray, _("Add-ons:"));
                for (auto &j : mdata.additions) {
                    mvwprintz(menu->window, line2, startx + 11, mcolor(j), "%s", mutation_branch::get_name( j ).c_str());
                    line2++;
                }
            }

            if ( !mdata.category.empty() ) {
                line2++;
                mvwprintz(menu->window, line2, startx, c_ltgray,  _("Category:"));
                for (auto &j : mdata.category) {
                    mvwprintw(menu->window, line2, startx + 11, "%s", j.c_str());
                    line2++;
                }
            }
            line2 += 2;

            mvwprintz(menu->window, line2, startx, c_ltgray, "pts: %d vis: %d ugly: %d",
                      mdata.points,
                      mdata.visibility,
                      mdata.ugliness
                     );
            line2 += 2;

            std::vector<std::string> desc = foldstring( mdata.description,
                                            menu->pad_right - 1 );
            for( auto &elem : desc ) {
                mvwprintz( menu->window, line2, startx, c_ltgray, "%s", elem.c_str() );
                line2++;
            }
            lastlen = line2 + 1;

            mvwprintz(menu->window, menu->w_height - 3, startx, c_green, "%s", msg.c_str());
            msg = padding;
            mvwprintw(menu->window, menu->w_height - 2, startx, _("[/] find, [q]uit"));

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

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

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

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

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

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

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

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

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

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

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

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

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

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

 bool missed = false;
 int tart;

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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