示例#1
0
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, 2_minutes );
            }
            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, 5_minutes );
            } else if( !u.has_effect( effect_hallu ) && rng( 10, 1600 ) < in ) {
                u.add_effect( effect_hallu, 6_hours );
            }
            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_turns ) {
                add.sated += 1_turns;
            }
            break;

        case ADD_PKILLER:
            if( calendar::once_every( time_duration::from_turns( 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_turns;
                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, 2_minutes + in * 5_turns );
            } 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 * 2_minutes );
            } 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, 6_hours );
            }
        }
        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( trait_id( "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;
    }
}
示例#2
0
// General movement.
// Currently, priority goes:
// 1) Special Attack
// 2) Sight-based tracking
// 3) Scent-based tracking
// 4) Sound-based tracking
void monster::move()
{
    // We decrement wandf no matter what.  We'll save our wander_to plans until
    // after we finish out set_dest plans, UNLESS they time out first.
    if( wandf > 0 ) {
        wandf--;
    }

    //Hallucinations have a chance of disappearing each turn
    if( is_hallucination() && one_in( 25 ) ) {
        die( nullptr );
        return;
    }

    //The monster can consume objects it stands on. Check if there are any.
    //If there are. Consume them.
    if( !is_hallucination() && has_flag( MF_ABSORBS ) && !g->m.has_flag( TFLAG_SEALED, pos() ) &&
        g->m.has_items( pos() ) ) {
        if( g->u.sees( *this ) ) {
            add_msg( _( "The %s flows around the objects on the floor and they are quickly dissolved!" ),
                     name().c_str() );
        }
        static const auto volume_per_hp = units::from_milliliter( 250 );
        for( auto &elem : g->m.i_at( pos() ) ) {
            hp += elem.volume() / volume_per_hp; // Yeah this means it can get more HP than normal.
        }
        g->m.i_clear( pos() );
    }

    const bool pacified = has_effect( effect_pacified );

    // First, use the special attack, if we can!
    // The attack may change `monster::special_attacks` (e.g. by transforming
    // this into another monster type). Therefor we can not iterate over it
    // directly and instead iterate over the map from the monster type
    // (properties of monster types should never change).
    for( const auto &sp_type : type->special_attacks ) {
        const std::string &special_name = sp_type.first;
        const auto local_iter = special_attacks.find( special_name );
        if( local_iter == special_attacks.end() ) {
            continue;
        }
        mon_special_attack &local_attack_data = local_iter->second;
        if( !local_attack_data.enabled ) {
            continue;
        }

        if( local_attack_data.cooldown > 0 ) {
            local_attack_data.cooldown--;
        }

        if( local_attack_data.cooldown == 0 && !pacified && !is_hallucination() ) {
            if( !sp_type.second->call( *this ) ) {
                continue;
            }

            // `special_attacks` might have changed at this point. Sadly `reset_special`
            // doesn't check the attack name, so we need to do it here.
            if( special_attacks.count( special_name ) == 0 ) {
                continue;
            }
            reset_special( special_name );
        }
    }

    // The monster can sometimes hang in air due to last fall being blocked
    const bool can_fly = has_flag( MF_FLIES );
    if( !can_fly && g->m.has_flag( TFLAG_NO_FLOOR, pos() ) ) {
        g->m.creature_on_trap( *this, false );
    }

    if( moves < 0 ) {
        return;
    }

    // TODO: Move this to attack_at/move_to/etc. functions
    bool attacking = false;
    if( !move_effects( attacking ) ) {
        moves = 0;
        return;
    }
    if( has_flag( MF_IMMOBILE ) ) {
        moves = 0;
        return;
    }
    if( has_effect( effect_stunned ) ) {
        stumble();
        moves = 0;
        return;
    }
    if( friendly > 0 ) {
        --friendly;
    }

    // Set attitude to attitude to our current target
    monster_attitude current_attitude = attitude( nullptr );
    if( !wander() ) {
        if( goal == g->u.pos() ) {
            current_attitude = attitude( &( g->u ) );
        } else {
            for( auto &i : g->active_npc ) {
                if( goal == i->pos() ) {
                    current_attitude = attitude( i );
                }
            }
        }
    }

    if( current_attitude == MATT_IGNORE ||
        ( current_attitude == MATT_FOLLOW && rl_dist( pos(), goal ) <= MONSTER_FOLLOW_DIST ) ) {
        moves -= 100;
        stumble();
        return;
    }

    bool moved = false;
    tripoint destination;

    // If true, don't try to greedily avoid locally bad paths
    bool pathed = false;
    if( !wander() ) {
        while( !path.empty() && path.front() == pos() ) {
            path.erase( path.begin() );
        }

        const auto &pf_settings = get_pathfinding_settings();
        if( pf_settings.max_dist >= rl_dist( pos(), goal ) &&
            ( path.empty() || rl_dist( pos(), path.front() ) >= 2 || path.back() != goal ) ) {
            // We need a new path
            path = g->m.route( pos(), goal, pf_settings, get_path_avoid() );
        }

        // Try to respect old paths, even if we can't pathfind at the moment
        if( !path.empty() && path.back() == goal ) {
            destination = path.front();
            moved = true;
            pathed = true;
        } else {
            // Straight line forward, probably because we can't pathfind (well enough)
            destination = goal;
            moved = true;
        }
    }
    if( !moved && has_flag( MF_SMELLS ) ) {
        // No sight... or our plans are invalid (e.g. moving through a transparent, but
        //  solid, square of terrain).  Fall back to smell if we have it.
        unset_dest();
        tripoint tmp = scent_move();
        if( tmp.x != -1 ) {
            destination = tmp;
            moved = true;
        }
    }
    if( wandf > 0 && !moved ) { // No LOS, no scent, so as a fall-back follow sound
        unset_dest();
        if( wander_pos != pos() ) {
            destination = wander_pos;
            moved = true;
        }
    }

    if( !g->m.has_zlevels() ) {
        // Otherwise weird things happen
        destination.z = posz();
    }

    tripoint next_step;
    const bool staggers = has_flag( MF_STUMBLES );
    if( moved ) {
        // Implement both avoiding obstacles and staggering.
        moved = false;
        float switch_chance = 0.0;
        const bool can_bash = bash_skill() > 0;
        // This is a float and using trig_dist() because that Does the Right Thing(tm)
        // in both circular and roguelike distance modes.
        const float distance_to_target = trig_dist( pos(), destination );
        for( const tripoint &candidate : squares_closer_to( pos(), destination ) ) {
            if( candidate.z != posz() ) {
                bool can_z_move = true;
                if( !g->m.valid_move( pos(), candidate, false, true ) ) {
                    // Can't phase through floor
                    can_z_move = false;
                }

                if( can_z_move && !can_fly && candidate.z > posz() && !g->m.has_floor_or_support( candidate ) ) {
                    // Can't "jump" up a whole z-level
                    can_z_move = false;
                }

                // Last chance - we can still do the z-level stair teleport bullshit that isn't removed yet
                // @todo Remove z-level stair bullshit teleport after aligning all stairs
                if( !can_z_move &&
                    posx() / ( SEEX * 2 ) == candidate.x / ( SEEX * 2 ) &&
                    posy() / ( SEEY * 2 ) == candidate.y / ( SEEY * 2 ) ) {
                    const tripoint &upper = candidate.z > posz() ? candidate : pos();
                    const tripoint &lower = candidate.z > posz() ? pos() : candidate;
                    if( g->m.has_flag( TFLAG_GOES_DOWN, upper ) && g->m.has_flag( TFLAG_GOES_UP, lower ) ) {
                        can_z_move = true;
                    }
                }

                if( !can_z_move ) {
                    continue;
                }
            }

            // A flag to allow non-stumbling critters to stumble when the most direct choice is bad.
            bool bad_choice = false;

            const Creature *target = g->critter_at( candidate, is_hallucination() );
            if( target != nullptr ) {
                const Creature::Attitude att = attitude_to( *target );
                if( att == A_HOSTILE ) {
                    // When attacking an adjacent enemy, we're direct.
                    moved = true;
                    next_step = candidate;
                    break;
                } else if( att == A_FRIENDLY && ( target->is_player() || target->is_npc() ) ) {
                    continue; // Friendly firing the player or an NPC is illegal for gameplay reasons
                } else if( !has_flag( MF_ATTACKMON ) && !has_flag( MF_PUSH_MON ) ) {
                    // Bail out if there's a non-hostile monster in the way and we're not pushy.
                    continue;
                }
                // Friendly fire and pushing are always bad choices - they take a lot of time
                bad_choice = true;
            }

            // Bail out if we can't move there and we can't bash.
            if( !pathed && !can_move_to( candidate ) ) {
                if( !can_bash ) {
                    continue;
                }

                const int estimate = g->m.bash_rating( bash_estimate(), candidate );
                if( estimate <= 0 ) {
                    continue;
                }

                if( estimate < 5 ) {
                    bad_choice = true;
                }
            }

            const float progress = distance_to_target - trig_dist( candidate, destination );
            // The x2 makes the first (and most direct) path twice as likely,
            // since the chance of switching is 1/1, 1/4, 1/6, 1/8
            switch_chance += progress * 2;
            // Randomly pick one of the viable squares to move to weighted by distance.
            if( moved == false || x_in_y( progress, switch_chance ) ) {
                moved = true;
                next_step = candidate;
                // If we stumble, pick a random square, otherwise take the first one,
                // which is the most direct path.
                // Except if the direct path is bad, then check others
                // Or if the path is given by pathfinder
                if( !staggers && ( !bad_choice || pathed ) ) {
                    break;
                }
            }
        }
    }
    // Finished logic section.  By this point, we should have chosen a square to
    //  move to (moved = true).
    if( moved ) { // Actual effects of moving to the square we've chosen
        const bool did_something =
            ( !pacified && attack_at( next_step ) ) ||
            ( !pacified && bash_at( next_step ) ) ||
            ( !pacified && push_to( next_step, 0, 0 ) ) ||
            move_to( next_step, false, get_stagger_adjust( pos(), destination, next_step ) );

        if( !did_something ) {
            moves -= 100; // If we don't do this, we'll get infinite loops.
        }
    } else {
        moves -= 100;
        stumble();
        path.clear();
    }
}
示例#3
0
/**
 * Add charge(s) of rain to given container, possibly contaminating it.
 */
void item::add_rain_to_container(bool acid, int charges)
{
    if( charges <= 0) {
        return;
    }
    const char *typeId = acid ? "water_acid" : "water";
    long max = dynamic_cast<it_container *>(type)->contains;
    long orig = 0;
    long added = charges;
    if (contents.empty()) {
        // This is easy. Just add 1 charge of the rain liquid to the container.
        item ret(typeId, 0);
        if (!acid) {
            // Funnels aren't always clean enough for water. // todo; disinfectant squeegie->funnel
            ret.poison = one_in(10) ? 1 : 0;
        }
        ret.charges = ( charges > max ? max : charges );
        put_in(ret);
    } else {
        // The container already has a liquid.
        item &liq = contents[0];
        orig = liq.charges;
        max -= liq.charges;
        added = ( charges > max ? max : charges );
        if (max > 0 ) {
            liq.charges += added;
        }

        if (liq.typeId() == typeId || liq.typeId() == "water_acid_weak") {
            // The container already contains this liquid or weakly acidic water.
            // Don't do anything special -- we already added liquid.
        } else {
            // The rain is different from what's in the container.
            // Turn the container's liquid into weak acid with a probability
            // based on its current volume.

            // If it's raining acid and this container started with 7
            // charges of water, the liquid will now be 1/8th acid or,
            // equivalently, 1/4th weak acid (the rest being water). A
            // stochastic approach gives the liquid a 1 in 4 (or 2 in
            // liquid.charges) chance of becoming weak acid.
            const bool transmute = x_in_y(2 * added, liq.charges);

            if (transmute) {
                item transmuted("water_acid_weak", 0);
                transmuted.charges = liq.charges;
                contents[0] = transmuted;
            } else if (liq.typeId() == "water") {
                // The container has water, and the acid rain didn't turn it
                // into weak acid. Poison the water instead, assuming 1
                // charge of acid would act like a charge of water with poison 5.
                long total_poison = liq.poison * (orig) + (5 * added);
                liq.poison = total_poison / liq.charges;
                long leftover_poison = total_poison - liq.poison * liq.charges;
                if (leftover_poison > rng(0, liq.charges)) {
                    liq.poison++;
                }
            }
        }
    }
}
示例#4
0
bool Character::move_effects()
{
    if (has_effect("downed")) {
        if (rng(0, 40) > get_dex() + int(get_str() / 2)) {
            add_msg_if_player(_("You struggle to stand."));
        } else {
            add_msg_player_or_npc(m_good, _("You stand up."),
                                    _("<npcname> stands up."));
            remove_effect("downed");
        }
        return false;
    }
    if (has_effect("webbed")) {
        if (x_in_y(get_str(), 6 * get_effect_int("webbed"))) {
            add_msg_player_or_npc(m_good, _("You free yourself from the webs!"),
                                    _("<npcname> frees themselves from the webs!"));
            remove_effect("webbed");
        } else {
            add_msg_if_player(_("You try to free yourself from the webs, but can't get loose!"));
        }
        return false;
    }
    if (has_effect("lightsnare")) {
        if(x_in_y(get_str(), 12) || x_in_y(get_dex(), 8)) {
            remove_effect("lightsnare");
            add_msg_player_or_npc(m_good, _("You free yourself from the light snare!"),
                                    _("<npcname> frees themselves from the light snare!"));
            item string("string_36", calendar::turn);
            item snare("snare_trigger", calendar::turn);
            g->m.add_item_or_charges(posx(), posy(), string);
            g->m.add_item_or_charges(posx(), posy(), snare);
        } else {
            add_msg_if_player(m_bad, _("You try to free yourself from the light snare, but can't get loose!"));
        }
        return false;
    }
    if (has_effect("heavysnare")) {
        if(x_in_y(get_str(), 32) || x_in_y(get_dex(), 16)) {
            remove_effect("heavysnare");
            add_msg_player_or_npc(m_good, _("You free yourself from the heavy snare!"),
                                    _("<npcname> frees themselves from the heavy snare!"));
            item rope("rope_6", calendar::turn);
            item snare("snare_trigger", calendar::turn);
            g->m.add_item_or_charges(posx(), posy(), rope);
            g->m.add_item_or_charges(posx(), posy(), snare);
        } else {
            add_msg_if_player(m_bad, _("You try to free yourself from the heavy snare, but can't get loose!"));
        }
        return false;
    }
    if (has_effect("beartrap")) {
        /* Real bear traps can't be removed without the proper tools or immense strength; eventually this should
           allow normal players two options: removal of the limb or removal of the trap from the ground
           (at which point the player could later remove it from the leg with the right tools).
           As such we are currently making it a bit easier for players and NPC's to get out of bear traps.
        */
        if(x_in_y(get_str(), 100)) {
            remove_effect("beartrap");
            add_msg_player_or_npc(m_good, _("You free yourself from the bear trap!"),
                                    _("<npcname> frees themselves from the bear trap!"));
            item beartrap("beartrap", calendar::turn);
            g->m.add_item_or_charges(posx(), posy(), beartrap);
        } else {
            add_msg_if_player(m_bad, _("You try to free yourself from the bear trap, but can't get loose!"));
        }
        return false;
    }
    if (has_effect("crushed")) {
        // Strength helps in getting free, but dex also helps you worm your way out of the rubble
        if(x_in_y(get_str() + get_dex() / 4, 100)) {
            remove_effect("crushed");
            add_msg_player_or_npc(m_good, _("You free yourself from the rubble!"),
                                    _("<npcname> frees themselves from the rubble!"));
        } else {
            add_msg_if_player(m_bad, _("You try to free yourself from the rubble, but can't get loose!"));
        }
        return false;
    }
    if (has_effect("amigara")) {
        int curdist = 999, newdist = 999;
        for (int cx = 0; cx < SEEX * MAPSIZE; cx++) {
            for (int cy = 0; cy < SEEY * MAPSIZE; cy++) {
                if (g->m.ter(cx, cy) == t_fault) {
                    int dist = rl_dist(cx, cy, posx(), posy());
                    if (dist < curdist) {
                        curdist = dist;
                    }
                    dist = rl_dist(cx, cy, posx(), posy());
                    if (dist < newdist) {
                        newdist = dist;
                    }
                }
            }
        }
        if (newdist > curdist) {
            add_msg_if_player(m_info, _("You cannot pull yourself away from the faultline..."));
            return false;
        }
    }
    // Below this point are things that allow for movement if they succeed

    // Currently we only have one thing that forces movement if you succeed, should we get more
    // than this will need to be reworked to only have success effects if /all/ checks succeed
    if (has_effect("in_pit")) {
        if (rng(0, 40) > get_str() + int(get_dex() / 2)) {
            add_msg_if_player(m_bad, _("You try to escape the pit, but slip back in."));
            return false;
        } else {
            add_msg_player_or_npc(m_good, _("You escape the pit!"),
                                    _("<npcname> escapes the pit!"));
            remove_effect("in_pit");
        }
    }
    return Creature::move_effects();
}
示例#5
0
// General movement.
// Currently, priority goes:
// 1) Special Attack
// 2) Sight-based tracking
// 3) Scent-based tracking
// 4) Sound-based tracking
void monster::move()
{
    // We decrement wandf no matter what.  We'll save our wander_to plans until
    // after we finish out set_dest plans, UNLESS they time out first.
    if( wandf > 0 ) {
        wandf--;
    }

    //Hallucinations have a chance of disappearing each turn
    if( is_hallucination() && one_in( 25 ) ) {
        die( nullptr );
        return;
    }

    //The monster can consume objects it stands on. Check if there are any.
    //If there are. Consume them.
    if( !is_hallucination() && has_flag( MF_ABSORBS ) && !g->m.has_flag( TFLAG_SEALED, pos() ) ) {
        if( !g->m.i_at( pos3() ).empty() ) {
            add_msg( _( "The %s flows around the objects on the floor and they are quickly dissolved!" ),
                     name().c_str() );
            for( auto &elem : g->m.i_at( pos3() ) ) {
                hp += elem.volume(); // Yeah this means it can get more HP than normal.
            }
            g->m.i_clear( pos3() );
        }
    }

    static const std::string pacified_string = "pacified";
    const bool pacified = has_effect( pacified_string );

    // First, use the special attack, if we can!
    for( size_t i = 0; i < sp_timeout.size(); ++i ) {
        if( sp_timeout[i] > 0 ) {
            sp_timeout[i]--;
        }

        if( sp_timeout[i] == 0 && !pacified && !is_hallucination() ) {
            type->sp_attack[i]( this, i );
        }
    }

    // The monster can sometimes hang in air due to last fall being blocked
    const bool can_fly = has_flag( MF_FLIES );
    if( !can_fly && g->m.has_flag( TFLAG_NO_FLOOR, pos() ) ) {
        g->m.creature_on_trap( *this, false );
    }

    if( moves < 0 ) {
        return;
    }

    // TODO: Move this to attack_at/move_to/etc. functions
    bool attacking = false;
    if( !move_effects(attacking) ) {
        moves = 0;
        return;
    }
    if( has_flag( MF_IMMOBILE ) ) {
        moves = 0;
        return;
    }
    static const std::string stun_string = "stunned";
    if( has_effect( stun_string ) ) {
        stumble();
        moves = 0;
        return;
    }
    if( friendly > 0 ) {
        --friendly;
    }

    // Set attitude to attitude to our current target
    monster_attitude current_attitude = attitude( nullptr );
    if( !wander() ) {
        if( goal == g->u.pos3() ) {
            current_attitude = attitude( &( g->u ) );
        } else {
            for( auto &i : g->active_npc ) {
                if( goal == i->pos3() ) {
                    current_attitude = attitude( i );
                }
            }
        }
    }

    if( current_attitude == MATT_IGNORE ||
        ( current_attitude == MATT_FOLLOW && rl_dist(pos(), goal) <= MONSTER_FOLLOW_DIST ) ) {
        moves -= 100;
        stumble();
        return;
    }

    bool moved = false;
    tripoint destination;

    // CONCRETE PLANS - Most likely based on sight
    if( !wander() ) {
        destination = goal;
        moved = true;
    }
    if( !moved && has_flag( MF_SMELLS ) ) {
        // No sight... or our plans are invalid (e.g. moving through a transparent, but
        //  solid, square of terrain).  Fall back to smell if we have it.
        unset_dest();
        tripoint tmp = scent_move();
        if( tmp.x != -1 ) {
            destination = tmp;
            moved = true;
        }
    }
    if( wandf > 0 && !moved ) { // No LOS, no scent, so as a fall-back follow sound
        unset_dest();
        tripoint tmp = wander_next();
        if( tmp != pos() ) {
            destination = tmp;
            moved = true;
        }
    }

    tripoint next_step;
    if( moved ) {
        // Implement both avoiding obstacles and staggering.
        moved = false;
        float switch_chance = 0.0;
        const bool can_bash = has_flag( MF_BASHES ) || has_flag( MF_BORES );
        // This is a float and using trig_dist() because that Does the Right Thing(tm)
        // in both circular and roguelike distance modes.
        const float distance_to_target = trig_dist( pos(), destination );
        for( const tripoint &candidate : squares_closer_to( pos(), destination ) ) {
            const Creature *target = g->critter_at( candidate, is_hallucination() );
            // When attacking an adjacent enemy, we're direct.
            if( target != nullptr && attitude_to( *target ) == A_HOSTILE ) {
                moved = true;
                next_step = candidate;
                break;
            }
            // Bail out if we can't move there and we can't bash.
            if( !can_move_to( candidate ) &&
                !(can_bash && g->m.bash_rating( bash_estimate(), candidate ) >= 0 ) ) {
                continue;
            }
            // Bail out if there's a non-hostile monster in the way and we're not pushy.
            if( target != nullptr && attitude_to( *target ) != A_HOSTILE &&
                !has_flag( MF_ATTACKMON ) && !has_flag( MF_PUSH_MON ) ) {
                continue;
            }
            float progress = distance_to_target - trig_dist( candidate, destination );
            switch_chance += progress;
            // Randomly pick one of the viable squares to move to weighted by distance.
            if( x_in_y( progress, switch_chance ) ) {
                moved = true;
                next_step = candidate;
                // If we stumble, pick a random square, otherwise take the first one,
                // which is the most direct path.
                if( !has_flag( MF_STUMBLES ) ) {
                    break;
                }
            }
        }
    }
    // Finished logic section.  By this point, we should have chosen a square to
    //  move to (moved = true).
    if( moved ) { // Actual effects of moving to the square we've chosen
        // move_to() uses the slope to determine some move speed scaling.
        const float slope = (destination.x > destination.y) ?
            (float)destination.y / (float)destination.x :
            (float)destination.x / (float)destination.y;
        const bool did_something =
            ( !pacified && attack_at( next_step ) ) ||
            ( !pacified && bash_at( next_step ) ) ||
            ( !pacified && push_to( next_step, 0, 0 ) ) ||
            move_to( next_step, false, slope );
        if( !did_something ) {
            moves -= 100; // If we don't do this, we'll get infinite loops.
        }
    } else {
        moves -= 100;
        stumble();
    }
}
示例#6
0
bool monster::move_effects()
{
    bool u_see_me = g->u.sees(*this);
    if (has_effect("tied")) {
        return false;
    }
    if (has_effect("downed")) {
        remove_effect("downed");
        if (u_see_me) {
            add_msg(_("The %s climbs to it's feet!"), name().c_str());
        }
        return false;
    }
    if (has_effect("webbed")) {
        if (x_in_y(type->melee_dice * type->melee_sides, 6 * get_effect_int("webbed"))) {
            if (u_see_me) {
                add_msg(_("The %s breaks free of the webs!"), name().c_str());
            }
            remove_effect("webbed");
        }
        return false;
    }
    if (has_effect("lightsnare")) {
        if(x_in_y(type->melee_dice * type->melee_sides, 12)) {
            remove_effect("lightsnare");
            g->m.spawn_item(posx(), posy(), "string_36");
            g->m.spawn_item(posx(), posy(), "snare_trigger");
            if (u_see_me) {
                add_msg(_("The %s escapes the light snare!"), name().c_str());
            }
        }
        return false;
    }
    if (has_effect("heavysnare")) {
        if (type->melee_dice * type->melee_sides >= 7) {
            if(x_in_y(type->melee_dice * type->melee_sides, 32)) {
                remove_effect("heavysnare");
                g->m.spawn_item(posx(), posy(), "rope_6");
                g->m.spawn_item(posx(), posy(), "snare_trigger");
                if (u_see_me) {
                    add_msg(_("The %s escapes the heavy snare!"), name().c_str());
                }
            }
        }
        return false;
    }
    if (has_effect("beartrap")) {
        if (type->melee_dice * type->melee_sides >= 18) {
            if(x_in_y(type->melee_dice * type->melee_sides, 200)) {
                remove_effect("beartrap");
                g->m.spawn_item(posx(), posy(), "beartrap");
                if (u_see_me) {
                    add_msg(_("The %s escapes the bear trap!"), name().c_str());
                }
            }
        }
        return false;
    }
    if (has_effect("crushed")) {
        // Strength helps in getting free, but dex also helps you worm your way out of the rubble
        if(x_in_y(type->melee_dice * type->melee_sides, 100)) {
            remove_effect("crushed");
            if (u_see_me) {
                add_msg(_("The %s frees itself from the rubble!"), name().c_str());
            }
        }
        return false;
    }

    // If we ever get more effects that force movement on success this will need to be reworked to
    // only trigger success effects if /all/ rolls succeed
    if (has_effect("in_pit")) {
        if (rng(0, 40) > type->melee_dice * type->melee_sides) {
            return false;
        } else {
            if (u_see_me) {
                add_msg(_("The %s escapes the pit!"), name().c_str());
            }
            remove_effect("in_pit");
        }
    }
    return Creature::move_effects();
}
void activity_handlers::pulp_do_turn( player_activity *act, player *p )
{
    const tripoint &pos = act->placement;
    static const int full_pulp_threshold = 4;
    const int move_cost = int(p->weapon.is_null() ? 80 : p->weapon.attack_time() * 0.8);

    // numbers logic: a str 8 character with a butcher knife (4 bash, 18 cut)
    // should have at least a 50% chance of damaging an intact zombie corpse (75 volume).
    // a str 8 character with a baseball bat (28 bash, 0 cut) should have around a 25% chance.

    int cut_power = p->weapon.type->melee_cut;
    // stabbing weapons are a lot less effective at pulping
    if( p->weapon.has_flag("STAB") || p->weapon.has_flag("SPEAR") ) {
        cut_power /= 2;
    }
    double pulp_power = sqrt((double)(p->str_cur + p->weapon.type->melee_dam)) *
        std::min(1.0, sqrt((double)(cut_power + 1)));
    pulp_power = std::min(pulp_power, (double)p->str_cur);
    pulp_power *= 20; // constant multiplier to get the chance right
    int moves = 0;
    int &num_corpses = act->index; // use this to collect how many corpse are pulped
    auto corpse_pile = g->m.i_at(pos);
    for( auto corpse = corpse_pile.begin(); corpse != corpse_pile.end(); ++corpse ) {
        if( !(corpse->is_corpse() && corpse->damage < full_pulp_threshold) ) {
            continue; // no corpse or already pulped
        }
        int damage = pulp_power / corpse->volume();
        //Determine corpse's blood type.
        field_id type_blood = corpse->get_mtype()->bloodType();
        do {
            moves += move_cost;
            const int mod_sta = ( (p->weapon.weight() / 100 ) + 20) * -1;
            p->mod_stat("stamina", mod_sta);
            // Increase damage as we keep smashing,
            // to insure that we eventually smash the target.
            if( x_in_y(pulp_power, corpse->volume())  ) {
                corpse->damage++;
                p->handle_melee_wear();
            }
            // Splatter some blood around
            tripoint tmp = pos;
            if( type_blood != fd_null ) {
                for( tmp.x = pos.x - 1; tmp.x <= pos.x + 1; tmp.x++ ) {
                    for( tmp.y = pos.y - 1; tmp.y <= pos.y + 1; tmp.y++ ) {
                        if( !one_in(damage + 1) && type_blood != fd_null ) {
                            g->m.add_field( tmp, type_blood, 1, 0 );
                        }
                    }
                }
            }
            if( corpse->damage >= full_pulp_threshold ) {
                corpse->damage = full_pulp_threshold;
                corpse->active = false;
                num_corpses++;
            }
            if( moves >= p->moves ) {
                // enough for this turn;
                p->moves -= moves;
                return;
            }
        } while( corpse->damage < full_pulp_threshold );
    }
    // If we reach this, all corpses have been pulped, finish the activity
    act->moves_left = 0;
    // TODO: Factor in how long it took to do the smashing.
    add_msg(ngettext("The corpse is thoroughly pulped.",
                     "The corpses are thoroughly pulped.", num_corpses));
}
bool fungal_effects::spread_fungus( const tripoint &p )
{
    int growth = 1;
    for( int i = p.x - 1; i <= p.x + 1; i++ ) {
        for( int j = p.y - 1; j <= p.y + 1; j++ ) {
            if( i == p.x && j == p.y ) {
                continue;
            }
            if( m.has_flag( "FUNGUS", tripoint( i, j, p.z ) ) ) {
                growth += 1;
            }
        }
    }

    bool converted = false;
    if( !m.has_flag_ter( "FUNGUS", p ) ) {
        // Terrain conversion
        if( m.has_flag_ter( "DIGGABLE", p ) ) {
            if( x_in_y( growth * 10, 100 ) ) {
                m.ter_set( p, t_fungus );
                converted = true;
            }
        } else if( m.has_flag( "FLAT", p ) ) {
            if( m.has_flag( TFLAG_INDOORS, p ) ) {
                if( x_in_y( growth * 10, 500 ) ) {
                    m.ter_set( p, t_fungus_floor_in );
                    converted = true;
                }
            } else if( m.has_flag( TFLAG_SUPPORTS_ROOF, p ) ) {
                if( x_in_y( growth * 10, 1000 ) ) {
                    m.ter_set( p, t_fungus_floor_sup );
                    converted = true;
                }
            } else {
                if( x_in_y( growth * 10, 2500 ) ) {
                    m.ter_set( p, t_fungus_floor_out );
                    converted = true;
                }
            }
        } else if( m.has_flag( "SHRUB", p ) ) {
            if( x_in_y( growth * 10, 200 ) ) {
                m.ter_set( p, t_shrub_fungal );
                converted = true;
            } else if( x_in_y( growth, 1000 ) ) {
                m.ter_set( p, t_marloss );
                converted = true;
            }
        } else if( m.has_flag( "THIN_OBSTACLE", p ) ) {
            if( x_in_y( growth * 10, 150 ) ) {
                m.ter_set( p, t_fungus_mound );
                converted = true;
            }
        } else if( m.has_flag( "YOUNG", p ) ) {
            if( x_in_y( growth * 10, 500 ) ) {
                m.ter_set( p, t_tree_fungal_young );
                converted = true;
            }
        } else if( m.has_flag( "WALL", p ) ) {
            if( x_in_y( growth * 10, 5000 ) ) {
                converted = true;
                m.ter_set( p, t_fungus_wall );
            }
        }
        // Furniture conversion
        if( converted ) {
            if( m.has_flag( "FLOWER", p ) ) {
                m.furn_set( p, f_flower_fungal );
            } else if( m.has_flag( "ORGANIC", p ) ) {
                if( m.furn( p ).obj().movecost == -10 ) {
                    m.furn_set( p, f_fungal_mass );
                } else {
                    m.furn_set( p, f_fungal_clump );
                }
            } else if( m.has_flag( "PLANT", p ) ) {
                // Replace the (already existing) seed
                m.i_at( p )[0] = item( "fungal_seeds", calendar::turn );
            }
        }
        return true;
    } else {
        // Everything is already fungus
        if( growth == 9 ) {
            return false;
        }
        for( int i = p.x - 1; i <= p.x + 1; i++ ) {
            for( int j = p.y - 1; j <= p.y + 1; j++ ) {
                tripoint dest( i, j, p.z );
                // One spread on average
                if( !m.has_flag( "FUNGUS", dest ) && one_in( 9 - growth ) ) {
                    //growth chance is 100 in X simplified
                    if( m.has_flag( "DIGGABLE", dest ) ) {
                        m.ter_set( dest, t_fungus );
                        converted = true;
                    } else if( m.has_flag( "FLAT", dest ) ) {
                        if( m.has_flag( TFLAG_INDOORS, dest ) ) {
                            if( one_in( 5 ) ) {
                                m.ter_set( dest, t_fungus_floor_in );
                                converted = true;
                            }
                        } else if( m.has_flag( TFLAG_SUPPORTS_ROOF, dest ) ) {
                            if( one_in( 10 ) ) {
                                m.ter_set( dest, t_fungus_floor_sup );
                                converted = true;
                            }
                        } else {
                            if( one_in( 25 ) ) {
                                m.ter_set( dest, t_fungus_floor_out );
                                converted = true;
                            }
                        }
                    } else if( m.has_flag( "SHRUB", dest ) ) {
                        if( one_in( 2 ) ) {
                            m.ter_set( dest, t_shrub_fungal );
                            converted = true;
                        } else if( one_in( 25 ) ) {
                            m.ter_set( dest, t_marloss );
                            converted = true;
                        }
                    } else if( m.has_flag( "THIN_OBSTACLE", dest ) ) {
                        if( x_in_y( 10, 15 ) ) {
                            m.ter_set( dest, t_fungus_mound );
                            converted = true;
                        }
                    } else if( m.has_flag( "YOUNG", dest ) ) {
                        if( one_in( 5 ) ) {
                            if( m.get_field_strength( p, fd_fungal_haze ) != 0 ) {
                                if( one_in( 8 ) ) { // young trees are vulnerable
                                    m.ter_set( dest, t_fungus );
                                    gm.summon_mon( mon_fungal_blossom, p );
                                    if( gm.u.sees( p ) ) {
                                        add_msg( m_warning, _( "The young tree blooms forth into a fungal blossom!" ) );
                                    }
                                } else if( one_in( 4 ) ) {
                                    m.ter_set( dest, t_marloss_tree );
                                }
                            } else {
                                m.ter_set( dest, t_tree_fungal_young );
                            }
                            converted = true;
                        }
                    } else if( m.has_flag( "TREE", dest ) ) {
                        if( one_in( 10 ) ) {
                            if( m.get_field_strength( p, fd_fungal_haze ) != 0 ) {
                                if( one_in( 10 ) ) {
                                    m.ter_set( dest, t_fungus );
                                    gm.summon_mon( mon_fungal_blossom, p );
                                    if( gm.u.sees( p ) ) {
                                        add_msg( m_warning, _( "The tree blooms forth into a fungal blossom!" ) );
                                    }
                                } else if( one_in( 6 ) ) {
                                    m.ter_set( dest, t_marloss_tree );
                                }
                            } else {
                                m.ter_set( dest, t_tree_fungal );
                            }
                            converted = true;
                        }
                    } else if( m.has_flag( "WALL", dest ) ) {
                        if( one_in( 50 ) ) {
                            converted = true;
                            m.ter_set( dest, t_fungus_wall );
                        }
                    }

                    if( converted ) {
                        if( m.has_flag( "FLOWER", dest ) ) {
                            m.furn_set( dest, f_flower_fungal );
                        } else if( m.has_flag( "ORGANIC", dest ) ) {
                            if( m.furn( dest ).obj().movecost == -10 ) {
                                m.furn_set( dest, f_fungal_mass );
                            } else {
                                m.furn_set( dest, f_fungal_clump );
                            }
                        } else if( m.has_flag( "PLANT", dest ) ) {
                            // Replace the (already existing) seed
                            m.i_at( p )[0] = item( "fungal_seeds", calendar::turn );
                        }
                    }
                }
            }
        }
        return false;
    }
}
示例#9
0
void fill_funnels(game *g, int rain_depth_mm_per_hour, bool acid, trap_id t)
{
    int funnel_radius_mm = 0;
    switch (t) {
        case tr_funnel:             funnel_radius_mm = 380; break;
        case tr_makeshift_funnel:   funnel_radius_mm =  85; break;
        default: return;
    }

    // How many turns should it take for us to collect 1 charge of rainwater?
    item water(item_controller->find_template("water"), 0);
    const double charge_ml = (double) (water.weight()) / water.charges;
    const double PI = 3.14159265358979f;
    const double surface_area_mm2 = PI * (funnel_radius_mm * funnel_radius_mm);
    const double vol_mm3_per_hour = surface_area_mm2 * rain_depth_mm_per_hour;
    const double vol_mm3_per_turn = vol_mm3_per_hour / 600;
    const double ml_to_mm3 = 1000;
    const double turns_per_charge = charge_ml * ml_to_mm3 / vol_mm3_per_turn;

    item *c = NULL;
    char maxcontains = 0;

    // Give each funnel on the map a chance to collect the rain.
    std::set<point> funnel_locs = g->m.trap_locations(t);
    std::set<point>::iterator i;
    for (i = funnel_locs.begin(); i != funnel_locs.end(); i++) {
        point loc = *i;
        std::vector<item>& items = g->m.i_at(loc.x, loc.y);
        if (one_in(turns_per_charge)) {
            // This funnel has collected some rain! Put the rain in the largest
            // container here which is either empty or contains some mixture of
            // impure water and acid.
            for (int j = 0; j < items.size(); j++) {
                item *it = &(items[j]);
                if (it->is_container() && it->has_flag("WATERTIGHT") && it->has_flag("SEALS")) {
                    it_container* ct = dynamic_cast<it_container*>(it->type);
                    if (ct->contains > maxcontains && (
                            it->contents.empty() ||
                            it->contents[0].typeId() == "water" ||
                            it->contents[0].typeId() == "water_acid" ||
                            it->contents[0].typeId() == "water_acid_weak")) {
                        c = it;
                        maxcontains = ct->contains;
                    }
                }
            }
        }
    }

    // If we found an eligible container, add some rain to it (or consider
    // contaminating its current liquid).
    if (c != NULL) {
        const char *typeId = acid ? "water_acid" : "water";
        if (c->contents.empty()) {
            // This is easy. Just add 1 charge of the rain liquid to the container.
            item ret(item_controller->find_template(typeId), 0);
            if (!acid) {
                // Funnels aren't always clean enough for water.
                ret.poison = one_in(10) ? 1 : 0;
            }
            c->put_in(ret);
        } else {
            // The container already has a liquid.
            item &liq = c->contents[0];

            it_container* ct = dynamic_cast<it_container*>(c->type);
            if (liq.charges < ct->contains) {
                liq.charges++;
            }

            if (liq.typeId() == typeId || liq.typeId() == "water_acid_weak") {
                // The container already contains this liquid or weakly acidic water.
                // Don't do anything special -- we already added liquid.
            } else {
                // The rain is different from what's in the container.
                // Turn the container's liquid into weak acid with a probability
                // based on its current volume.

                // If it's raining acid and this container started with 7
                // charges of water, the liquid will now be 1/8th acid or,
                // equivalently, 1/4th weak acid (the rest being water). A
                // stochastic approach gives the liquid a 1 in 4 (or 2 in
                // liquid.charges) chance of becoming weak acid.
                const bool transmute = x_in_y(2, liq.charges);

                if (transmute) {
                    item transmuted(item_controller->find_template("water_acid_weak"), 0);
                    transmuted.charges = liq.charges;
                    c->contents[0] = transmuted;
                } else if (liq.typeId() == "water") {
                    // The container has water, and the acid rain didn't turn it
                    // into weak acid. Poison the water instead, assuming 1
                    // charge of acid would act like a charge of water with poison 5.
                    int total_poison = liq.poison * (liq.charges - 1) + 5;
                    liq.poison = total_poison / liq.charges;
                    int leftover_poison = total_poison - liq.poison * liq.charges;
                    if (leftover_poison > rng(0, liq.charges)) {
                        liq.poison++;
                    }
                }
            }
        }
    }
}
示例#10
0
bool Character::move_effects(bool attacking)
{
    if (has_effect( effect_downed )) {
        ///\EFFECT_DEX increases chance to stand up when knocked down

        ///\EFFECT_STR increases chance to stand up when knocked down, slightly
        if (rng(0, 40) > get_dex() + get_str() / 2) {
            add_msg_if_player(_("You struggle to stand."));
        } else {
            add_msg_player_or_npc(m_good, _("You stand up."),
                                    _("<npcname> stands up."));
            remove_effect( effect_downed);
        }
        return false;
    }
    if (has_effect( effect_webbed )) {
        ///\EFFECT_STR increases chance to escape webs
        if (x_in_y(get_str(), 6 * get_effect_int( effect_webbed ))) {
            add_msg_player_or_npc(m_good, _("You free yourself from the webs!"),
                                    _("<npcname> frees themselves from the webs!"));
            remove_effect( effect_webbed);
        } else {
            add_msg_if_player(_("You try to free yourself from the webs, but can't get loose!"));
        }
        return false;
    }
    if (has_effect( effect_lightsnare )) {
        ///\EFFECT_STR increases chance to escape light snare

        ///\EFFECT_DEX increases chance to escape light snare
        if(x_in_y(get_str(), 12) || x_in_y(get_dex(), 8)) {
            remove_effect( effect_lightsnare);
            add_msg_player_or_npc(m_good, _("You free yourself from the light snare!"),
                                    _("<npcname> frees themselves from the light snare!"));
            item string("string_36", calendar::turn);
            item snare("snare_trigger", calendar::turn);
            g->m.add_item_or_charges(posx(), posy(), string);
            g->m.add_item_or_charges(posx(), posy(), snare);
        } else {
            add_msg_if_player(m_bad, _("You try to free yourself from the light snare, but can't get loose!"));
        }
        return false;
    }
    if (has_effect( effect_heavysnare )) {
        ///\EFFECT_STR increases chance to escape heavy snare, slightly

        ///\EFFECT_DEX increases chance to escape light snare
        if(x_in_y(get_str(), 32) || x_in_y(get_dex(), 16)) {
            remove_effect( effect_heavysnare);
            add_msg_player_or_npc(m_good, _("You free yourself from the heavy snare!"),
                                    _("<npcname> frees themselves from the heavy snare!"));
            item rope("rope_6", calendar::turn);
            item snare("snare_trigger", calendar::turn);
            g->m.add_item_or_charges(posx(), posy(), rope);
            g->m.add_item_or_charges(posx(), posy(), snare);
        } else {
            add_msg_if_player(m_bad, _("You try to free yourself from the heavy snare, but can't get loose!"));
        }
        return false;
    }
    if (has_effect( effect_beartrap )) {
        /* Real bear traps can't be removed without the proper tools or immense strength; eventually this should
           allow normal players two options: removal of the limb or removal of the trap from the ground
           (at which point the player could later remove it from the leg with the right tools).
           As such we are currently making it a bit easier for players and NPC's to get out of bear traps.
        */
        ///\EFFECT_STR increases chance to escape bear trap
        if(x_in_y(get_str(), 100)) {
            remove_effect( effect_beartrap);
            add_msg_player_or_npc(m_good, _("You free yourself from the bear trap!"),
                                    _("<npcname> frees themselves from the bear trap!"));
            item beartrap("beartrap", calendar::turn);
            g->m.add_item_or_charges(posx(), posy(), beartrap);
        } else {
            add_msg_if_player(m_bad, _("You try to free yourself from the bear trap, but can't get loose!"));
        }
        return false;
    }
    if (has_effect( effect_crushed )) {
        ///\EFFECT_STR increases chance to escape crushing rubble

        ///\EFFECT_DEX increases chance to escape crushing rubble, slightly
        if(x_in_y(get_str() + get_dex() / 4, 100)) {
            remove_effect( effect_crushed);
            add_msg_player_or_npc(m_good, _("You free yourself from the rubble!"),
                                    _("<npcname> frees themselves from the rubble!"));
        } else {
            add_msg_if_player(m_bad, _("You try to free yourself from the rubble, but can't get loose!"));
        }
        return false;
    }
    // Below this point are things that allow for movement if they succeed

    // Currently we only have one thing that forces movement if you succeed, should we get more
    // than this will need to be reworked to only have success effects if /all/ checks succeed
    if (has_effect( effect_in_pit )) {
        ///\EFFECT_STR increases chance to escape pit

        ///\EFFECT_DEX increases chance to escape pit, slightly
        if (rng(0, 40) > get_str() + get_dex() / 2) {
            add_msg_if_player(m_bad, _("You try to escape the pit, but slip back in."));
            return false;
        } else {
            add_msg_player_or_npc(m_good, _("You escape the pit!"),
                                    _("<npcname> escapes the pit!"));
            remove_effect( effect_in_pit);
        }
    }
    if( has_effect( effect_grabbed ) && !attacking ) {
        int zed_number = 0;
        for( auto &&dest : g->m.points_in_radius( pos(), 1, 0 ) ){
            if( g->mon_at( dest ) != -1 &&
                ( g->zombie( g->mon_at( dest ) ).has_flag( MF_GRABS ) ||
                  g->zombie( g->mon_at( dest ) ).type->has_special_attack( "GRAB" ) ) ) {
                zed_number ++;
            }
        }
        if( zed_number == 0 ) {
            add_msg_player_or_npc( m_good, _( "You find yourself no longer grabbed." ),
                                   _( "<npcname> finds themselves no longer grabbed." ) );
            remove_effect( effect_grabbed );
        ///\EFFECT_DEX increases chance to escape grab, if >STR

        ///\EFFECT_STR increases chance to escape grab, if >DEX
        } else if( rng( 0, std::max( get_dex(), get_str() ) ) < rng( get_effect_int( effect_grabbed ), 8 ) ) {
            // Randomly compare higher of dex or str to grab intensity.
            add_msg_player_or_npc( m_bad, _( "You try break out of the grab, but fail!" ),
                                   _( "<npcname> tries to break out of the grab, but fails!" ) );
            return false;
        } else {
            add_msg_player_or_npc( m_good, _( "You break out of the grab!" ),
                                   _( "<npcname> breaks out of the grab!" ) );
            remove_effect( effect_grabbed );
        }
    }
    return Creature::move_effects( attacking );
}
示例#11
0
bool Character::move_effects(bool attacking)
{
    if (has_effect("downed")) {
        if (rng(0, 40) > get_dex() + int(get_str() / 2)) {
            add_msg_if_player(_("You struggle to stand."));
        } else {
            add_msg_player_or_npc(m_good, _("You stand up."),
                                    _("<npcname> stands up."));
            remove_effect("downed");
        }
        return false;
    }
    if (has_effect("webbed")) {
        if (x_in_y(get_str(), 6 * get_effect_int("webbed"))) {
            add_msg_player_or_npc(m_good, _("You free yourself from the webs!"),
                                    _("<npcname> frees themselves from the webs!"));
            remove_effect("webbed");
        } else {
            add_msg_if_player(_("You try to free yourself from the webs, but can't get loose!"));
        }
        return false;
    }
    if (has_effect("lightsnare")) {
        if(x_in_y(get_str(), 12) || x_in_y(get_dex(), 8)) {
            remove_effect("lightsnare");
            add_msg_player_or_npc(m_good, _("You free yourself from the light snare!"),
                                    _("<npcname> frees themselves from the light snare!"));
            item string("string_36", calendar::turn);
            item snare("snare_trigger", calendar::turn);
            g->m.add_item_or_charges(posx(), posy(), string);
            g->m.add_item_or_charges(posx(), posy(), snare);
        } else {
            add_msg_if_player(m_bad, _("You try to free yourself from the light snare, but can't get loose!"));
        }
        return false;
    }
    if (has_effect("heavysnare")) {
        if(x_in_y(get_str(), 32) || x_in_y(get_dex(), 16)) {
            remove_effect("heavysnare");
            add_msg_player_or_npc(m_good, _("You free yourself from the heavy snare!"),
                                    _("<npcname> frees themselves from the heavy snare!"));
            item rope("rope_6", calendar::turn);
            item snare("snare_trigger", calendar::turn);
            g->m.add_item_or_charges(posx(), posy(), rope);
            g->m.add_item_or_charges(posx(), posy(), snare);
        } else {
            add_msg_if_player(m_bad, _("You try to free yourself from the heavy snare, but can't get loose!"));
        }
        return false;
    }
    if (has_effect("beartrap")) {
        /* Real bear traps can't be removed without the proper tools or immense strength; eventually this should
           allow normal players two options: removal of the limb or removal of the trap from the ground
           (at which point the player could later remove it from the leg with the right tools).
           As such we are currently making it a bit easier for players and NPC's to get out of bear traps.
        */
        if(x_in_y(get_str(), 100)) {
            remove_effect("beartrap");
            add_msg_player_or_npc(m_good, _("You free yourself from the bear trap!"),
                                    _("<npcname> frees themselves from the bear trap!"));
            item beartrap("beartrap", calendar::turn);
            g->m.add_item_or_charges(posx(), posy(), beartrap);
        } else {
            add_msg_if_player(m_bad, _("You try to free yourself from the bear trap, but can't get loose!"));
        }
        return false;
    }
    if (has_effect("crushed")) {
        // Strength helps in getting free, but dex also helps you worm your way out of the rubble
        if(x_in_y(get_str() + get_dex() / 4, 100)) {
            remove_effect("crushed");
            add_msg_player_or_npc(m_good, _("You free yourself from the rubble!"),
                                    _("<npcname> frees themselves from the rubble!"));
        } else {
            add_msg_if_player(m_bad, _("You try to free yourself from the rubble, but can't get loose!"));
        }
        return false;
    }
    // Below this point are things that allow for movement if they succeed

    // Currently we only have one thing that forces movement if you succeed, should we get more
    // than this will need to be reworked to only have success effects if /all/ checks succeed
    if (has_effect("in_pit")) {
        if (rng(0, 40) > get_str() + int(get_dex() / 2)) {
            add_msg_if_player(m_bad, _("You try to escape the pit, but slip back in."));
            return false;
        } else {
            add_msg_player_or_npc(m_good, _("You escape the pit!"),
                                    _("<npcname> escapes the pit!"));
            remove_effect("in_pit");
        }
    }
    if (has_effect("grabbed")){
        int zed_number = 0;
        for( auto &&dest : g->m.points_in_radius( pos(), 1, 0 ) ){
            if (g->mon_at(dest) != -1){
                zed_number ++;
            }
        }
        if (attacking == true || zed_number == 0){
            return true;
        }
        if (get_dex() > get_str() ? rng(0, get_dex()) : rng( 0, get_str()) < rng( get_effect_int("grabbed") , 8) ){
            add_msg_player_or_npc(m_bad, _("You try break out of the grab, but fail!"),
                                            _("<npcname> tries to break out of the grab, but fails!"));
            return false;
        } else {
            add_msg_player_or_npc(m_good, _("You break out of the grab!"),
                                            _("<npcname> breaks out of the grab!"));
            remove_effect("grabbed");
        }
    }
    return Creature::move_effects(attacking);
}