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;
    }
    ///\xrefitem Stat_Effects_Strength "" "" Strength increases pulping power, with diminishing returns
    double pulp_power = sqrt((double)(p->str_cur + p->weapon.type->melee_dam)) *
        std::min(1.0, sqrt((double)(cut_power + 1)));
    ///\xrefitem Stat_Effects_Strength "" "" Strength caps pulping power
    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;
    if( num_corpses == 0 ) {
        add_msg(m_bad, _("The corpse moved before you could finish smashing it!"));
        return;
    }
    // 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));
}
void activity_handlers::butcher_finish( player_activity *act, player *p )
{
    // Corpses can disappear (rezzing!), so check for that
    auto items_here = g->m.i_at( p->pos() );
    if( static_cast<int>( items_here.size() ) <= act->index ||
        !( items_here[act->index].is_corpse() ) ) {
        add_msg(m_info, _("There's no corpse to butcher!"));
        return;
    }

    item &corpse_item = items_here[act->index];
    const mtype *corpse = corpse_item.get_mtype();
    std::vector<item> contents = corpse_item.contents;
    const int age = corpse_item.bday;
    g->m.i_rem( p->pos(), act->index );

    const int factor = p->butcher_factor();
    int pieces = 0;
    int skins = 0;
    int bones = 0;
    int fats = 0;
    int sinews = 0;
    int feathers = 0;
    int wool = 0;
    bool stomach = false;

    switch (corpse->size) {
    case MS_TINY:
        pieces = 1;
        skins = 1;
        bones = 1;
        fats = 1;
        sinews = 1;
        feathers = 2;
        wool = 1;
        break;
    case MS_SMALL:
        pieces = 2;
        skins = 2;
        bones = 4;
        fats = 2;
        sinews = 4;
        feathers = 6;
        wool = 2;
        break;
    case MS_MEDIUM:
        pieces = 4;
        skins = 4;
        bones = 9;
        fats = 4;
        sinews = 9;
        feathers = 11;
        wool = 4;
        break;
    case MS_LARGE:
        pieces = 8;
        skins = 8;
        bones = 14;
        fats = 8;
        sinews = 14;
        feathers = 17;
        wool = 8;
        break;
    case MS_HUGE:
        pieces = 16;
        skins = 16;
        bones = 21;
        fats = 16;
        sinews = 21;
        feathers = 24;
        wool = 16;
        break;
    }

    const int skill_level = p->skillLevel( skill_survival );

    auto roll_butchery = [&] () {
        double skill_shift = 0.0;
        ///\xrefitem Skill_Effects_Survival "" "" Survival above 3 randomly increases Butcher rolls, below 3 decreases
        skill_shift += rng_float( 0, skill_level - 3 );
        ///\xrefitem Stat_Effects_Dexterity "" "" Dexterity above 8 randomly increases Butcher rolls, slightly, below 8 decreases
        skill_shift += rng_float( 0, p->dex_cur - 8 ) / 4.0;
        ///\xrefitem Stat_Effects_Strength "" "" Strength below 4 randomly decreases Butcher rolls, slightly
        if( p->str_cur < 4 ) {
            skill_shift -= rng_float( 0, 5 * ( 4 - p->str_cur ) ) / 4.0;
        }

        if( factor < 0 ) {
            skill_shift -= rng_float( 0, -factor / 5.0 );
        }

        return static_cast<int>( round( skill_shift ) );
    };

    int practice = std::max( 0, 4 + pieces + roll_butchery());

    p->practice( skill_survival, practice );

    // Lose some meat, skins, etc if the rolls are low
    pieces +=   std::min( 0, roll_butchery() );
    skins +=    std::min( 0, roll_butchery() - 4 );
    bones +=    std::min( 0, roll_butchery() - 2 );
    fats +=     std::min( 0, roll_butchery() - 4 );
    sinews +=   std::min( 0, roll_butchery() - 8 );
    feathers += std::min( 0, roll_butchery() - 1 );
    wool +=     std::min( 0, roll_butchery() );
    stomach = roll_butchery() >= 0;

    if( bones > 0 ) {
        if( corpse->has_material("veggy") ) {
            g->m.spawn_item(p->pos(), "plant_sac", bones, 0, age);
            add_msg(m_good, _("You harvest some fluid bladders!"));
        } else if( corpse->has_flag(MF_BONES) && corpse->has_flag(MF_POISON) ) {
            g->m.spawn_item(p->pos(), "bone_tainted", bones / 2, 0, age);
            add_msg(m_good, _("You harvest some salvageable bones!"));
        } else if( corpse->has_flag(MF_BONES) && corpse->has_flag(MF_HUMAN) ) {
            g->m.spawn_item(p->pos(), "bone_human", bones, 0, age);
            add_msg(m_good, _("You harvest some salvageable bones!"));
        } else if( corpse->has_flag(MF_BONES) ) {
            g->m.spawn_item(p->pos(), "bone", bones, 0, age);
            add_msg(m_good, _("You harvest some usable bones!"));
        }
    }

    if( sinews > 0 ) {
        if( corpse->has_flag(MF_BONES) && !corpse->has_flag(MF_POISON) ) {
            g->m.spawn_item(p->pos(), "sinew", sinews, 0, age);
            add_msg(m_good, _("You harvest some usable sinews!"));
        } else if( corpse->has_material("veggy") ) {
            g->m.spawn_item(p->pos(), "plant_fibre", sinews, 0, age);
            add_msg(m_good, _("You harvest some plant fibers!"));
        }
    }

    if( stomach ) {
        const itype_id meat = corpse->get_meat_itype();
        if( meat == "meat" ) {
            if( corpse->size == MS_SMALL || corpse->size == MS_MEDIUM ) {
                g->m.spawn_item(p->pos(), "stomach", 1, 0, age);
                add_msg(m_good, _("You harvest the stomach!"));
            } else if( corpse->size == MS_LARGE || corpse->size == MS_HUGE ) {
                g->m.spawn_item(p->pos(), "stomach_large", 1, 0, age);
                add_msg(m_good, _("You harvest the stomach!"));
            }
        } else if( meat == "human_flesh" ) {
            if( corpse->size == MS_SMALL || corpse->size == MS_MEDIUM ) {
                g->m.spawn_item(p->pos(), "hstomach", 1, 0, age);
                add_msg(m_good, _("You harvest the stomach!"));
            } else if( corpse->size == MS_LARGE || corpse->size == MS_HUGE ) {
                g->m.spawn_item(p->pos(), "hstomach_large", 1, 0, age);
                add_msg(m_good, _("You harvest the stomach!"));
            }
        }
    }

    if( (corpse->has_flag(MF_FUR) || corpse->has_flag(MF_LEATHER) ||
         corpse->has_flag(MF_CHITIN)) && skins > 0 ) {
        add_msg(m_good, _("You manage to skin the %s!"), corpse->nname().c_str());
        int fur = 0;
        int leather = 0;
        int human_leather = 0;
        int chitin = 0;

        while (skins > 0 ) {
            if( corpse->has_flag(MF_CHITIN) ) {
                chitin = rng(0, skins);
                skins -= chitin;
                skins = std::max(skins, 0);
            }
            if( corpse->has_flag(MF_FUR) ) {
                fur = rng(0, skins);
                skins -= fur;
                skins = std::max(skins, 0);
            }
            if( corpse->has_flag(MF_LEATHER) ) {
                if( corpse->has_flag(MF_HUMAN) ) {
                    human_leather = rng(0, skins);
                    skins -= human_leather;
                } else {
                    leather = rng(0, skins);
                    skins -= leather;
                }
                skins = std::max(skins, 0);
            }
        }

        if( chitin > 0 ) {
            g->m.spawn_item(p->pos(), "chitin_piece", chitin, 0, age);
        }
        if( fur > 0 ) {
            g->m.spawn_item(p->pos(), "raw_fur", fur, 0, age);
        }
        if( leather > 0 ) {
            g->m.spawn_item(p->pos(), "raw_leather", leather, 0, age);
        }
        if( human_leather ) {
            g->m.spawn_item(p->pos(), "raw_hleather", leather, 0, age);
        }
    }

    if( feathers > 0 ) {
        if( corpse->has_flag(MF_FEATHER) ) {
            g->m.spawn_item(p->pos(), "feather", feathers, 0, age);
            add_msg(m_good, _("You harvest some feathers!"));
        }
    }

    if( wool > 0 ) {
        if( corpse->has_flag(MF_WOOL) ) {
            g->m.spawn_item(p->pos(), "wool_staple", wool, 0, age);
            add_msg(m_good, _("You harvest some wool staples!"));
        }
    }

    if( fats > 0 ) {
        if( corpse->has_flag(MF_FAT) && corpse->has_flag(MF_POISON) ) {
            g->m.spawn_item(p->pos(), "fat_tainted", fats, 0, age);
            add_msg(m_good, _("You harvest some gooey fat!"));
        } else if( corpse->has_flag(MF_FAT) ) {
            g->m.spawn_item(p->pos(), "fat", fats, 0, age);
            add_msg(m_good, _("You harvest some fat!"));
        }
    }

    //Add a chance of CBM recovery. For shocker and cyborg corpses.
    //As long as the factor is above -4 (the sinew cutoff), you will be able to extract cbms
    if( corpse->has_flag(MF_CBM_CIV) ) {
        butcher_cbm_item( "bio_power_storage", p->pos(), age, roll_butchery() );
        butcher_cbm_group( "bionics_common", p->pos(), age, roll_butchery() );
    }

    // Zombie scientist bionics
    if( corpse->has_flag(MF_CBM_SCI) ) {
        butcher_cbm_item( "bio_power_storage", p->pos(), age, roll_butchery() );
        butcher_cbm_group( "bionics_sci", p->pos(), age, roll_butchery() );
    }

    // Zombie technician bionics
    if( corpse->has_flag(MF_CBM_TECH) ) {
        butcher_cbm_item( "bio_power_storage", p->pos(), age, roll_butchery() );
        butcher_cbm_group( "bionics_tech", p->pos(), age, roll_butchery() );
    }

    // Substation mini-boss bionics
    if( corpse->has_flag(MF_CBM_SUBS) ) {
        butcher_cbm_item( "bio_power_storage", p->pos(), age, roll_butchery() );
        butcher_cbm_group( "bionics_subs", p->pos(), age, roll_butchery() );
        butcher_cbm_group( "bionics_subs", p->pos(), age, roll_butchery() );
    }

    // Payoff for butchering the zombie bio-op
    if( corpse->has_flag(MF_CBM_OP) ) {
        butcher_cbm_item( "bio_power_storage_mkII", p->pos(), age, roll_butchery() );
        butcher_cbm_group( "bionics_op", p->pos(), age, roll_butchery() );
    }

    //Add a chance of CBM power storage recovery.
    if( corpse->has_flag(MF_CBM_POWER) ) {
        butcher_cbm_item( "bio_power_storage", p->pos(), age, roll_butchery() );
    }


    // Recover hidden items
    for( auto &content : contents  ) {
        if( ( roll_butchery() + 10 ) * 5 > rng( 0, 100 ) ) {
            //~ %1$s - item name, %2$s - monster name
            add_msg( m_good, _( "You discover a %1$s in the %2$s!" ), content.tname().c_str(),
                     corpse->nname().c_str() );
            g->m.add_item_or_charges( p->pos(), content );
        } else if( content.is_bionic()  ) {
            g->m.spawn_item(p->pos(), "burnt_out_bionic", 1, 0, age);
        }
    }

    if( pieces <= 0 ) {
        add_msg(m_bad, _("Your clumsy butchering destroys the meat!"));
    } else {
        add_msg(m_good, _("You butcher the corpse."));
        const itype_id meat = corpse->get_meat_itype();
        if( meat == "null" ) {
            return;
        }
        item tmpitem(meat, age);
        tmpitem.set_mtype( corpse );
        while ( pieces > 0 ) {
            pieces--;
            g->m.add_item_or_charges(p->pos(), tmpitem);
        }
    }
}
void activity_handlers::make_zlave_finish( player_activity *act, player *p )
{
    static const int full_pulp_threshold = 4;

    auto items = g->m.i_at(p->pos());
    std::string corpse_name = act->str_values[0];
    item *body = NULL;

    for( auto it = items.begin(); it != items.end(); ++it ) {
        if( it->display_name() == corpse_name ) {
            body = &*it;
        }
    }

    if( body == NULL ) {
        add_msg(m_info, _("There's no corpse to make into a zombie slave!"));
        return;
    }

    int success = act->values[0];

    if( success > 0 ) {

        p->practice( skill_firstaid, rng(2, 5) );
        p->practice( skill_survival, rng(2, 5) );

        p->add_msg_if_player(m_good,
                             _("You slice muscles and tendons, and remove body parts until you're confident the zombie won't be able to attack you when it reainmates."));

        body->set_var( "zlave", "zlave" );
        //take into account the chance that the body yet can regenerate not as we need.
        if( one_in(10) ) {
            body->set_var( "zlave", "mutilated" );
        }

    } else {

        if( success > -20 ) {

            p->practice( skill_firstaid, rng(3, 6) );
            p->practice( skill_survival, rng(3, 6) );

            p->add_msg_if_player(m_warning,
                                 _("You hack into the corpse and chop off some body parts.  You think the zombie won't be able to attack when it reanimates."));

            success += rng(1, 20);

            if( success > 0 && !one_in(5) ) {
                body->set_var( "zlave", "zlave" );
            } else {
                body->set_var( "zlave", "mutilated" );
            }

        } else {

            p->practice( skill_firstaid, rng(1, 8) );
            p->practice( skill_survival, rng(1, 8) );

            int pulp = rng(1, full_pulp_threshold);

            body->damage += pulp;

            if( body->damage >= full_pulp_threshold ) {
                body->damage = full_pulp_threshold;
                body->active = false;

                p->add_msg_if_player(m_warning, _("You cut up the corpse too much, it is thoroughly pulped."));
            } else {
                p->add_msg_if_player(m_warning,
                                     _("You cut into the corpse trying to make it unable to attack, but you don't think you have it right."));
            }
        }
    }
}
Esempio n. 4
0
void monster::knock_back_from(int x, int y)
{
 if (x == posx() && y == posy())
  return; // No effect
 point to(posx(), posy());
 if (x < posx())
  to.x++;
 if (x > posx())
  to.x--;
 if (y < posy())
  to.y++;
 if (y > posy())
  to.y--;

 bool u_see = g->u_see(to.x, to.y);

// First, see if we hit another monster
 int mondex = g->mon_at(to.x, to.y);
 if (mondex != -1) {
  monster *z = &(g->zombie(mondex));
  apply_damage( z, bp_torso, z->type->size );
  add_effect("stunned", 1);
  if (type->size > 1 + z->type->size) {
   z->knock_back_from(posx(), posy()); // Chain reaction!
   z->apply_damage( this, bp_torso, type->size );
   z->add_effect("stunned", 1);
  } else if (type->size > z->type->size) {
   z->apply_damage( this, bp_torso, type->size );
   z->add_effect("stunned", 1);
  }

  if (u_see)
   add_msg(_("The %s bounces off a %s!"), name().c_str(), z->name().c_str());

  return;
 }

 int npcdex = g->npc_at(to.x, to.y);
 if (npcdex != -1) {
  npc *p = g->active_npc[npcdex];
  apply_damage( p, bp_torso, 3 );
  add_effect("stunned", 1);
  p->deal_damage( this, bp_torso, damage_instance( DT_BASH, type->size ) );
  if (u_see)
   add_msg(_("The %s bounces off %s!"), name().c_str(), p->name.c_str());

  return;
 }

// If we're still in the function at this point, we're actually moving a tile!
 if (g->m.ter_at(to.x, to.y).has_flag(TFLAG_DEEP_WATER)) {
  if (g->m.has_flag("LIQUID", to.x, to.y) && can_drown()) {
   die( nullptr );
   if (u_see) {
    add_msg(_("The %s drowns!"), name().c_str());
   }

  } else if (has_flag(MF_AQUATIC)) { // We swim but we're NOT in water
   die( nullptr );
   if (u_see) {
    add_msg(_("The %s flops around and dies!"), name().c_str());
   }
  }
 }

 if (g->m.move_cost(to.x, to.y) == 0) {

   // It's some kind of wall.
   apply_damage( nullptr, bp_torso, type->size );
   add_effect("stunned", 2);
   if (u_see) {
    add_msg(_("The %s bounces off a %s."), name().c_str(),
               g->m.tername(to.x, to.y).c_str());
   }

 } else { // It's no wall
  setpos(to);
 }
}
Esempio n. 5
0
int vehicle::automatic_fire_turret( vehicle_part &pt )
{
    auto gun = turret_query( pt );
    if( gun.query() != turret_data::status::ready ) {
        return 0;
    }

    tripoint pos = global_part_pos3( pt );

    npc tmp;
    tmp.set_fake( true );
    tmp.name = rmp_format( _( "<veh_player>The %s" ), pt.name().c_str() );
    tmp.set_skill_level( gun.base()->gun_skill(), 8 );
    tmp.set_skill_level( skill_id( "gun" ), 4 );
    tmp.recoil = abs( velocity ) / 100 / 4;
    tmp.setpos( pos );
    tmp.str_cur = 16;
    tmp.dex_cur = 8;
    tmp.per_cur = 12;
    // Assume vehicle turrets are friendly to the player.
    tmp.attitude = NPCATT_FOLLOW;

    int area = aoe_size( gun.ammo_effects() );
    if( area > 0 ) {
        area += area == 1 ? 1 : 2; // Pad a bit for less friendly fire
    }

    tripoint targ = pos;
    auto &target = pt.target;
    if( target.first == target.second ) {
        // Manual target not set, find one automatically
        const bool u_see = g->u.sees( pos );
        int boo_hoo;

        // @todo calculate chance to hit and cap range based upon this
        int range = std::min( gun.range(), 12 );
        Creature *auto_target = tmp.auto_find_hostile_target( range, boo_hoo, area );
        if( auto_target == nullptr ) {
            if( u_see && boo_hoo ) {
                add_msg( m_warning, ngettext( "%s points in your direction and emits an IFF warning beep.",
                                              "%s points in your direction and emits %d annoyed sounding beeps.",
                                              boo_hoo ),
                         tmp.name.c_str(), boo_hoo );
            }
            return 0;
        }

        targ = auto_target->pos();
    } else if( target.first != target.second ) {
        // Target set manually
        // Make sure we didn't move between aiming and firing (it's a bug if we did)
        if( targ != target.first ) {
            target.second = target.first;
            return 0;
        }

        targ = target.second;
        // Remove the target
        target.second = target.first;
    } else {
        // Shouldn't happen
        target.first = target.second;
        return 0;
    }

    auto shots = gun.fire( tmp, targ );

    if( g->u.sees( pos ) && shots ) {
        add_msg( _( "The %1$s fires its %2$s!" ), name.c_str(), pt.name().c_str() );
    }

    return shots;
}
Esempio n. 6
0
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;
    }
}
void activity_on_turn_move_loot( player_activity &, player &p )
{
    auto &mgr = zone_manager::get_manager();
    if( g->m.check_vehicle_zones( g->get_levz() ) ) {
        mgr.cache_vzones();
    }
    const auto abspos = g->m.getabs( p.pos() );
    const auto &src_set = mgr.get_near( zone_type_id( "LOOT_UNSORTED" ), abspos );
    vehicle *src_veh, *dest_veh;
    int src_part, dest_part;

    // Nuke the current activity, leaving the backlog alone.
    p.activity = player_activity();

    // sort source tiles by distance
    const auto &src_sorted = get_sorted_tiles_by_distance( abspos, src_set );

    if( !mgr.is_sorting() ) {
        mgr.start_sort( src_sorted );
    }

    for( auto &src : src_sorted ) {
        const auto &src_loc = g->m.getlocal( src );
        bool is_adjacent_or_closer = square_dist( p.pos(), src_loc ) <= 1;

        // skip tiles in IGNORE zone and tiles on fire (to prevent taking out wood off the lit brazier)
        // and inaccessible furniture, like filled charcoal kiln
        if( mgr.has( zone_type_id( "LOOT_IGNORE" ), src ) ||
            g->m.get_field( src_loc, fd_fire ) != nullptr ||
            !g->m.can_put_items_ter_furn( src_loc ) ) {
            continue;
        }

        auto items = std::vector<item *>();

        //Check source for cargo part
        //map_stack and vehicle_stack are different types but inherit from item_stack
        //TODO: use one for loop
        if( const cata::optional<vpart_reference> vp = g->m.veh_at( src_loc ).part_with_feature( "CARGO",
                false ) ) {
            src_veh = &vp->vehicle();
            src_part = vp->part_index();
            for( auto &it : src_veh->get_items( src_part ) ) {
                if( !it.made_of_from_type( LIQUID ) ) { // skip unpickable liquid
                    items.push_back( &it );
                }
            }
        } else {
            src_veh = nullptr;
            src_part = -1;
            for( auto &it : g->m.i_at( src_loc ) ) {
                if( !it.made_of_from_type( LIQUID ) ) { // skip unpickable liquid
                    items.push_back( &it );
                }
            }
        }

        //Skip items that have already been processed
        for( auto it = items.begin() + mgr.get_num_processed( src ); it < items.end(); it++ ) {

            mgr.increment_num_processed( src );

            const auto id = mgr.get_near_zone_type_for_item( **it, abspos );

            // checks whether the item is already on correct loot zone or not
            // if it is, we can skip such item, if not we move the item to correct pile
            // think empty bag on food pile, after you ate the content
            if( !mgr.has( id, src ) ) {
                const auto &dest_set = mgr.get_near( id, abspos );

                for( auto &dest : dest_set ) {
                    const auto &dest_loc = g->m.getlocal( dest );

                    //Check destination for cargo part
                    if( const cata::optional<vpart_reference> vp = g->m.veh_at( dest_loc ).part_with_feature( "CARGO",
                            false ) ) {
                        dest_veh = &vp->vehicle();
                        dest_part = vp->part_index();
                    } else {
                        dest_veh = nullptr;
                        dest_part = -1;
                    }

                    // skip tiles with inaccessible furniture, like filled charcoal kiln
                    if( !g->m.can_put_items_ter_furn( dest_loc ) ) {
                        continue;
                    }

                    units::volume free_space;
                    // if there's a vehicle with space do not check the tile beneath
                    if( dest_veh ) {
                        free_space = dest_veh->free_volume( dest_part );
                    } else {
                        free_space = g->m.free_volume( dest_loc );
                    }
                    // check free space at destination
                    if( free_space >= ( *it )->volume() ) {
                        // before we move any item, check if player is at or adjacent to the loot source tile
                        if( !is_adjacent_or_closer ) {
                            std::vector<tripoint> route;
                            bool adjacent = false;

                            // get either direct route or route to nearest adjacent tile if source tile is impassable
                            if( g->m.passable( src_loc ) ) {
                                route = g->m.route( p.pos(), src_loc, p.get_pathfinding_settings(), p.get_path_avoid() );
                            } else {
                                // immpassable source tile (locker etc.), get route to nerest adjacent tile instead
                                route = route_adjacent( p, src_loc );
                                adjacent = true;
                            }

                            // check if we found path to source / adjacent tile
                            if( route.empty() ) {
                                add_msg( m_info, _( "You can't reach the source tile. Try to sort out loot without a cart." ) );
                                return;
                            }

                            // shorten the route to adjacent tile, if necessary
                            if( !adjacent ) {
                                route.pop_back();
                            }

                            // set the destination and restart activity after player arrives there
                            // we don't need to check for safe mode, activity will be restarted only if
                            // player arrives on destination tile
                            p.set_destination( route, player_activity( activity_id( "ACT_MOVE_LOOT" ) ) );

                            // didn't actually process so decrement
                            mgr.decrement_num_processed( src );
                            return;
                        }
                        move_item( **it, ( *it )->count(), src_loc, dest_loc, src_veh, src_part );

                        // moved item away from source so decrement
                        mgr.decrement_num_processed( src );

                        break;
                    }
                }
                if( p.moves <= 0 ) {
                    // Restart activity and break from cycle.
                    p.assign_activity( activity_id( "ACT_MOVE_LOOT" ) );
                    return;
                }
            }
        }
    }

    // If we got here without restarting the activity, it means we're done
    add_msg( m_info, _( "You sorted out every item you could." ) );
    mgr.end_sort();
}
Esempio n. 8
0
//++++++++++++++++++++++++++++++++++++++++++++++++
//CONMAN: do_fifo_cmd(): exercises command sent by fifo
//++++++++++++++++++++++++++++++++++++++++++++++++
int do_fifo_cmd() {

	//terminate netfilter program
	if(cmcmd.cmd == 'Q') {
		terminate_loop = 1;
		return 0;
	}

	//find session: find max session index if no session provided
	struct session *curr_sess, *try_sess, *tmp_sess;
	curr_sess = NULL;
	unsigned max_index = 0;
	HASH_ITER(hh, sess_hash, try_sess, tmp_sess) {
		if(try_sess != NULL){	
			if(cmcmd.sess == -1){
				if(try_sess->index >= max_index){
					curr_sess = try_sess;
					max_index = try_sess->index;			
				}
			}
		}
		if((int)try_sess->index == cmcmd.sess) {
			curr_sess = try_sess;
			break;
		}
	}
	
	if(curr_sess==NULL) {
		sprintf(msg_buf,"do_fifo_cmd: sess with id=%d not found - FIFO CMD ABORTED", cmcmd.sess);
		add_msg(msg_buf);	
		return 0;
	}
	
	if(curr_sess->conman_state != '0') {
		sprintf(msg_buf,"do_fifo_cmd: sess->conman_state=%c != 0 - FIFO CMD ABORTED", curr_sess->conman_state);	
		add_msg(msg_buf);
		return 0;
	}
	if(curr_sess->sess_state < ESTABLISHED || curr_sess->sess_state >= TIME_WAIT) {
		curr_sess->conman_state = '0';
		add_msg(msg_buf);
		sprintf(msg_buf,"do_fifo_cmd: sess_state=%d not established - FIFO CMD ABORTED", curr_sess->sess_state);
		return 0;
	} 	

		
	switch(cmcmd.cmd) {
	case 'A':
		return add_sfl_fifo(curr_sess);
		break;
	case 'D':
		return delete_sfl_fifo(curr_sess);
		break;
	case 'S':
		return switch_sfl_fifo(curr_sess);
		break;
	case 'B':
		return break_sfl_fifo(curr_sess);
		break;
	default: 
		return 0;
	}
}
Esempio n. 9
0
//++++++++++++++++++++++++++++++++++++++++++++++++
//CONMAN: do_break(struct session *sess)
//++++++++++++++++++++++++++++++++++++++++++++++++
int break_sfl_fifo(struct session *sess) {

	//present active subflow will be interrupted
	//if candidate is up, it becomes active subflow
	//if no candidate exists but loc IP address is provided, a new subflow is created
	//if no candidate and no new local IP address but USE_PORT_FOR_NEW_SUBFLOWS, new port on existing loc IP is used
	//otherwise nothing happpens
	//break event is set whenever a new subflow has to be created

	//find candidate subflow
	struct subflow *new_sfl = NULL;
	new_sfl = find_subflow_in_session(sess, (cmcmd.sfl>-1), (size_t) cmcmd.sfl, 1);

	//if new_sfl == NULL try to create new subflow
	if(new_sfl == NULL) {
		sprintf(msg_buf, "break_sfl_fifo: candidate subflow not found -> creating new subflow");
		add_msg(msg_buf);

		//get fourtuple for new subflow (all except active)
		//note: act_subflow != NULL at this point
		cmcmd.ip_loc = 0;
		cmcmd.prt_loc = 0;
		cmcmd.ip_rem = 0;
		cmcmd.prt_rem = 0;
		struct fourtuple ft;
		if(!determine_fourtuple(sess, &ft)) {

			sprintf(msg_buf,"break_sfl_fifo: new subflow cannot be created - FIFO CMD ABORTED");
			add_msg(msg_buf);
			return 0;
		}

		if(UPDATE_DEFAULT_ROUTE) update_default_route(ft.ip_loc);

		//set last subflow to active subflow
		sess->act_subflow->broken = 1;
		sess->cdsn_loc = sess->highest_dan_loc-1;

		unsigned char backup = 0;
		if(initiate_cand_subflow(sess, &ft, backup) == 0){
			sprintf(msg_buf,"break_sfl_fifo: initiating new subflow creates error - FIFO CMD ABORTED");
			add_msg(msg_buf);
			return 0;
		}
	
	} else {
		sprintf(msg_buf,"break_sfl_fifo: sess id=%zu - deleting old sfl id=%zu and using new sfl id=%zu", 
				sess->index, sess->act_subflow->index, new_sfl->index);
		add_msg(msg_buf);

		break_active_sfl(sess, new_sfl);//switches to new subflow in "break" manner

		//send break ack: TPprio for new subflow and REMOVE_ADDR on old address
		send_break_ack(sess->act_subflow, sess->last_subflow->addr_id_loc);

		if(sess->act_subflow->addr_id_loc == sess->last_subflow->addr_id_loc) send_reset_subflow(sess->last_subflow);		

		create_prio_event(&sess->ft, sess->last_subflow->addr_id_loc);//to resend breal ack

		return 1;
	}


	return 1;
}
Esempio n. 10
0
std::vector<item> game::multidrop(std::vector<item> &dropped_worn, int &freed_volume_capacity)
{
    WINDOW* w_inv = newwin(TERRAIN_WINDOW_HEIGHT, TERRAIN_WINDOW_WIDTH + (use_narrow_sidebar() ? 45 : 55), VIEW_OFFSET_Y, VIEW_OFFSET_X);
    const int maxitems = TERRAIN_WINDOW_HEIGHT - 5;
    freed_volume_capacity = 0;

    u.inv.restack(&u);
    u.inv.sort();
    int drp_line_width=getmaxx(w_inv)-90;
    const std::string drp_line_padding = ( drp_line_width > 1 ? std::string(drp_line_width, ' ') : " ");
    std::map<int, int> dropping; // Count of how many we'll drop from each position
    int count = 0; // The current count
    std::vector<char> dropped_armor; // Always single, not counted
    int dropped_weapon = 0;
    bool warned_about_bionic = false; // Printed add_msg re: dropping bionics
    print_inv_statics(w_inv, _("Multidrop:"), dropped_armor, dropped_weapon);
    int base_weight = u.weight_carried();
    int base_volume = u.volume_carried();

    int ch = (int)'.';
    int start = 0, cur_it = 0, max_it;
    indexed_invslice stacks = u.inv.slice_filter();
    CategoriesVector CATEGORIES;
    std::vector<int> firsts = find_firsts(stacks, CATEGORIES);
    int selected = -1;
    int selected_pos = INT_MIN;

    int next_category_at = 0;
    int prev_category_at = 0;
    bool inCategoryMode = false;

    std::vector<int> category_order;
    category_order.reserve(firsts.size());

    // Items are not guaranteed to be in the same order as their categories, in fact they almost never are.
    // So we sort the categories by which items actually show up first in the inventory.
    for (int current_item = 0; current_item < u.inv.size(); ++current_item) {
        for (int i = 1; i < CATEGORIES.size(); ++i) {
            if (current_item == firsts[i - 1]) {
                category_order.push_back(i - 1);
            }
        }
    }

    do {
        // Find the inventory position of the first item in the previous and next category (in relation
        // to the currently selected category).
        for (int i = 0; i < category_order.size(); ++i) {
            if (selected > firsts[category_order[i]] && prev_category_at <= firsts[category_order[i]]) {
                prev_category_at = firsts[category_order[i]];
            }

            if (selected < firsts[category_order[i]] && next_category_at <= selected) {
                next_category_at = firsts[category_order[i]];
            }
        }

        inventory drop_subset = u.inv.subset(dropping);
        int new_weight = base_weight - drop_subset.weight();
        int new_volume = base_volume - drop_subset.volume();
        for (int i = 0; i < dropped_armor.size(); ++i) {
            new_weight -= u.i_at(dropped_armor[i]).weight();
        }
        if (dropped_weapon == -1) {
            new_weight -= u.weapon.weight();
        } else if (dropped_weapon > 0) {
            item tmp(u.weapon);
            tmp.charges = dropped_weapon;
            new_weight -= tmp.weight();
        }
        print_inv_weight_vol(w_inv, new_weight, new_volume, calc_volume_capacity(dropped_armor));
        int cur_line = 2;
        max_it = 0;
        int drp_line = 1;
        // Print weapon to be dropped, the first position is reserved for high visibility
        mvwprintw(w_inv, 0, 90, "%s", drp_line_padding.c_str());
        if (dropped_weapon != 0) {
            if (dropped_weapon == -1) {
                mvwprintz(w_inv, 0, 90, c_ltblue, "%c + %s", u.weapon.invlet, u.weapname().c_str());
            } else {
                mvwprintz(w_inv, 0, 90, c_ltblue, "%c # %s {%d}", u.weapon.invlet, u.weapon.tname().c_str(), dropped_weapon);
            }
            mvwprintw(w_inv, drp_line, 90, "%s", drp_line_padding.c_str());
            drp_line++;
        }
        // Print worn items to be dropped
        bool dropping_a = false;
        if (u.worn.size() > 0){
            for (int k = 0; k < u.worn.size(); k++) {
                bool dropping_w = false;
                for (int j = 0; j < dropped_armor.size() && !dropping_w; j++) {
                    if (dropped_armor[j] == u.worn[k].invlet) {
                        dropping_w = true;
                        dropping_a = true;
                        mvwprintw(w_inv, drp_line, 90, "%s", drp_line_padding.c_str());
                        mvwprintz(w_inv, drp_line, 90, c_cyan, "%c + %s", u.worn[k].invlet, u.worn[k].tname().c_str());
                        drp_line++;
                    }
                }
            }
        }
        if(dropping_a) {
            mvwprintw(w_inv, drp_line, 90, "%s", drp_line_padding.c_str());
            drp_line++;
        }
        for (cur_it = start; cur_it < start + maxitems && cur_line < maxitems+3; cur_it++) {
            // Clear the current line;
            mvwprintw(w_inv, cur_line, 0, "                                             ");
            mvwprintw(w_inv, drp_line, 90, "%s", drp_line_padding.c_str());
            mvwprintw(w_inv, drp_line + 1, 90, "%s", drp_line_padding.c_str());
            // Print category header
            for (int i = 1; i < CATEGORIES.size(); i++) {
                if (cur_it == firsts[i-1]) {
                    mvwprintz(w_inv, cur_line, 0, c_magenta, CATEGORIES[i].name.c_str());
                    cur_line++;
                }
            }

            if ( selected < start && selected > -1 ) selected = start;

            if (cur_it < stacks.size()) {
                item& it = stacks[cur_it].first->front();
                if( cur_it == selected) {
                    selected_pos = stacks[cur_it].second;
                }
                const char invlet = it.invlet == 0 ? ' ' : it.invlet;
                nc_color selected_line_color = inCategoryMode ? c_white_red : h_white;
                mvwputch (w_inv, cur_line, 0, (cur_it == selected ? selected_line_color : c_white), invlet);
                char icon = '-';
                if (dropping[cur_it] >= (it.count_by_charges() ? it.charges : stacks[cur_it].first->size())) {
                    icon = '+';
                } else if (dropping[cur_it] > 0) {
                    icon = '#';
                }
                nc_color col = ( cur_it == selected ? selected_line_color : it.color_in_inventory() );
                mvwprintz(w_inv, cur_line, 1, col, " %c %s", icon,
                          it.tname().c_str());
                if (stacks[cur_it].first->size() > 1) {
                    wprintz(w_inv, col, " x %d", stacks[cur_it].first->size());
                }
                if (it.charges > 0) {
                    wprintz(w_inv, col, " (%d)", it.charges);
                } else if (it.contents.size() == 1 &&
                           it.contents[0].charges > 0) {
                    wprintw(w_inv, " (%d)", it.contents[0].charges);
                }
                if (icon=='+'||icon=='#') {
                    mvwprintz(w_inv, drp_line, 90, col, "%c %c %s", invlet, icon, it.tname().c_str());
                    if (icon=='+') {
                        if (stacks[cur_it].first->size() > 1) {
                            wprintz(w_inv, col, " x %d", stacks[cur_it].first->size());
                        }
                        if (it.charges > 0) {
                            wprintz(w_inv, col, " (%d)", it.charges);
                        }
                    }
                    if (icon=='#') {
                        wprintz(w_inv, col, " {%d}", dropping[cur_it]);
                    }
                    drp_line++;
                }
            }
            cur_line++;
            max_it=cur_it;
        }

        if (inCategoryMode) {
            mvwprintz(w_inv, maxitems + 4, 32, c_white_red, _("In category select mode! Press [TAB] to enter item select mode."));
        } else {
            mvwprintz(w_inv, maxitems + 4, 32, h_white, _("In item select mode! Press [TAB] to enter category select mode."));
        }

        if (start > 0) {
            mvwprintw(w_inv, maxitems + 4, 0, _("< Go Back"));
        }
        if (cur_it < u.inv.size()) {
            mvwprintw(w_inv, maxitems + 4, 12, _("> More items"));
        }
        wrefresh(w_inv);
/* back to (int)getch() as input() mangles arrow keys
  ch = input();
*/
        ch = getch();

        if (ch == '\t') {
            inCategoryMode = !inCategoryMode;
        }
        else if ( ch == '<' || ch == KEY_PPAGE ) {
            if( start > 0) {
                for (int i = 1; i < maxitems+4; i++)
                    mvwprintz(w_inv, i, 0, c_black, "                                             ");
                start -= maxitems;
                if (start < 0)
                    start = 0;
                mvwprintw(w_inv, maxitems + 4, 0, "         ");
                if ( selected > -1 ) selected = start; // oy, the cheese
            }
        } else if ( ch == '>' || ch == KEY_NPAGE ) {
            if ( cur_it < u.inv.size()) {
                start = cur_it;
                mvwprintw(w_inv, maxitems + 4, 12, "            ");
                for (int i = 1; i < maxitems+4; i++)
                    mvwprintz(w_inv, i, 0, c_black, "                                             ");
                if ( selected < start && selected > -1 ) selected = start;
            }
        } else if ( ch == KEY_DOWN ) {
            if ( selected < 0 ) {
                selected = start;
            } else {
                if (inCategoryMode) {
                    selected < firsts[category_order[category_order.size() - 1]] ? selected = next_category_at : 0;
                } else {
                    selected++;
                }

                next_category_at = prev_category_at = 0;
            }

            if ( selected > max_it ) {
                if( cur_it < u.inv.size() ) {
                    start = cur_it;
                    mvwprintw(w_inv, maxitems + 4, 12, "            ");
                    for (int i = 1; i < maxitems+4; i++)
                        mvwprintz(w_inv, i, 0, c_black, "                                             ");
                } else {
                    selected = u.inv.size() - 1; // wraparound?
                }
            }
        } else if ( ch == KEY_UP ) {
            inCategoryMode ? selected = prev_category_at : selected--;
            next_category_at = prev_category_at = 0;

            if ( selected < -1 ) {
                selected = -1; // wraparound?
            } else if ( selected < start ) {
                if ( start > 0 ) {
                    for (int i = 1; i < maxitems+4; i++)
                        mvwprintz(w_inv, i, 0, c_black, "                                             ");
                    start -= maxitems;
                    if (start < 0)
                        start = 0;
                    mvwprintw(w_inv, maxitems + 4, 0, "         ");
                }
            }
        } else if (ch >= '0'&& ch <= '9') {
            ch = (char)ch - '0';
            count *= 10;
            count += ch;
        } else { // todo: reformat and maybe rewrite
            item* it;
            int it_pos;
            if ( ch == '\t' || ch == KEY_RIGHT || ch == KEY_LEFT ) {
                it_pos = selected_pos;
                it = &u.inv.find_item(it_pos);
            } else {
                it = &u.inv.item_by_letter((char)ch);
                it_pos = u.inv.position_by_item(it);
            }
            if (it == 0 || it->is_null()) { // Not from inventory
                int found = false;
                for (int i = 0; i < dropped_armor.size() && !found; i++) {
                    if (dropped_armor[i] == ch) {
                        dropped_armor.erase(dropped_armor.begin() + i);
                        found = true;
                        print_inv_statics(w_inv, _("Multidrop:"), dropped_armor, dropped_weapon);
                    }
                }
                if (!found && ch == u.weapon.invlet && !u.weapon.is_null()) {
                    if (u.weapon.has_flag("NO_UNWIELD")) {
                        if (!warned_about_bionic) {
                            add_msg(_("You cannot drop your %s."), u.weapon.tname().c_str());
                            warned_about_bionic = true;
                        }
                    } else {
                        // user selected weapon, which is a normal item
                        if (count == 0) {
                            // No count given, invert the selection status: drop all <-> drop none
                            if (dropped_weapon == 0) {
                                dropped_weapon = -1;
                            } else {
                                dropped_weapon = 0;
                            }
                        } else if (u.weapon.count_by_charges() && count < u.weapon.charges) {
                            // can drop part of weapon and count is valid for this
                            dropped_weapon = count;
                        } else {
                            dropped_weapon = -1;
                        }
                        count = 0;
                        print_inv_statics(w_inv, _("Multidrop:"), dropped_armor, dropped_weapon);
                    }
                } else if (!found) {
                    dropped_armor.push_back(ch);
                    print_inv_statics(w_inv, _("Multidrop:"), dropped_armor, dropped_weapon);
                }
            } else {
                int index = -1;
                for (int i = 0; i < stacks.size(); ++i) {
                    if (&(stacks[i].first->front()) == it) {
                        index = i;
                        break;
                    }
                }
                if (index == -1) {
                    debugmsg("Inventory got out of sync with inventory slice?");
                }
                if (count == 0) {
                    if (it->count_by_charges()) {
                        if (dropping[it_pos] == 0) {
                            dropping[it_pos] = -1;
                        } else {
                            dropping[it_pos] = 0;
                        }
                    } else {
                        if (dropping[it_pos] == 0) {
                            dropping[it_pos] = stacks[index].first->size();
                        } else {
                            dropping[it_pos] = 0;
                        }
                    }
                } else if (count >= stacks[index].first->size() && !it->count_by_charges()) {
                    dropping[it_pos] = stacks[index].first->size();
                } else {
                    dropping[it_pos] = count;
                }

                count = 0;
            }
        }
    } while (ch != '\n' && ch != KEY_ESCAPE && ch != ' ');
    werase(w_inv);
    delwin(w_inv);
    erase();
    refresh_all();

    std::vector<item> ret;

    if (ch != '\n')
        return ret; // Canceled!

    // We iterate backwards because deletion will invalidate later indices.
    for (std::map<int,int>::reverse_iterator it = dropping.rbegin(); it != dropping.rend(); ++it) {
        if (it->second == -1)
            ret.push_back( u.inv.remove_item( it->first));
        else if (it->second && u.inv.find_item( it->first).count_by_charges()) {
            int charges = u.inv.find_item( it->first).charges;// >= it->second ? : it->second;
            ret.push_back( u.inv.reduce_charges( it->first, it->second > charges ? charges : it->second));
        } else if (it->second)
            for (int j = it->second; j > 0; j--)
                ret.push_back( u.inv.remove_item( it->first));
    }
    if (dropped_weapon == -1) {
        ret.push_back(u.remove_weapon());
    } else if (dropped_weapon > 0) {
        ret.push_back(u.weapon);
        u.weapon.charges -= dropped_weapon;
        ret.back().charges = dropped_weapon;
    }

    for (int i = 0; i < dropped_armor.size(); i++)
    {
        int wornpos = u.invlet_to_position(dropped_armor[i]);
        const it_armor *ita = dynamic_cast<const it_armor *>(u.i_at(dropped_armor[i]).type);
        if (wornpos == INT_MIN || !u.takeoff(wornpos, true))
        {
            continue;
        }
        u.moves -= 250; // same as in game::takeoff
        if(ita != 0) {
            freed_volume_capacity += ita->storage;
        }
        // Item could have been dropped after taking it off
        if (&u.inv.item_by_letter(dropped_armor[i]) != &u.inv.nullitem)
        {
            dropped_worn.push_back(u.i_rem(dropped_armor[i]));
        }
    }

    return ret;
}
void game::place_construction(constructable *con)
{
 refresh_all();
 inventory total_inv = crafting_inventory();

 std::vector<point> valid;
 for (int x = u.posx - 1; x <= u.posx + 1; x++) {
  for (int y = u.posy - 1; y <= u.posy + 1; y++) {
   if (x == u.posx && y == u.posy)
    y++;
   construct test;
   bool place_okay = (test.*(con->able))(this, point(x, y));
   for (int i = 0; i < con->stages.size() && !place_okay; i++) {
    if (m.ter(x, y) == con->stages[i].terrain)
     place_okay = true;
   }

   if (place_okay) {
// Make sure we're not trying to continue a construction that we can't finish
    int starting_stage = 0, max_stage = -1;
    for (int i = 0; i < con->stages.size(); i++) {
     if (m.ter(x, y) == con->stages[i].terrain)
      starting_stage = i + 1;
    }
    for(int i = starting_stage; i < con->stages.size(); i++) {
     if (player_can_build(u, total_inv, con, i, true, true))
       max_stage = i;
     else
       break;
    }
    if (max_stage >= starting_stage) {
     valid.push_back(point(x, y));
     m.drawsq(w_terrain, u, x, y, true, false);
     wrefresh(w_terrain);
    }
   }
  }
 }
 mvprintz(0, 0, c_red, "Pick a direction in which to construct:");
 int dirx, diry;
 get_direction(this, dirx, diry, input());
 if (dirx == -2) {
  add_msg("Invalid direction.");
  return;
 }
 dirx += u.posx;
 diry += u.posy;
 bool point_is_okay = false;
 for (int i = 0; i < valid.size() && !point_is_okay; i++) {
  if (valid[i].x == dirx && valid[i].y == diry)
   point_is_okay = true;
 }
 if (!point_is_okay) {
  add_msg("You cannot build there!");
  return;
 }

// Figure out what stage to start at, and what stage is the maximum
 int starting_stage = 0, max_stage = 0;
 for (int i = 0; i < con->stages.size(); i++) {
  if (m.ter(dirx, diry) == con->stages[i].terrain)
   starting_stage = i + 1;
  if (player_can_build(u, total_inv, con, i, true))
   max_stage = i;
 }

 u.assign_activity(ACT_BUILD, con->stages[starting_stage].time * 1000, con->id);

 u.moves = 0;
 std::vector<int> stages;
 for (int i = starting_stage; i <= max_stage; i++)
  stages.push_back(i);
 u.activity.values = stages;
 u.activity.placement = point(dirx, diry);
}
Esempio n. 12
0
void Creature::add_effect( const efftype_id &eff_id, int dur, body_part bp,
                           bool permanent, int intensity, bool force )
{
    // Check our innate immunity
    if( !force && is_immune_effect( eff_id ) ) {
        return;
    }

    if( !eff_id.is_valid() ) {
        debugmsg( "Invalid effect, ID: %s", eff_id.c_str() );
        return;
    }
    const effect_type &type = eff_id.obj();

    // Mutate to a main (HP'd) body_part if necessary.
    if (type.get_main_parts()) {
        bp = mutate_to_main_part(bp);
    }

    bool found = false;
    // Check if we already have it
    auto matching_map = effects.find(eff_id);
    if (matching_map != effects.end()) {
        auto &bodyparts = matching_map->second;
        auto found_effect = bodyparts.find(bp);
        if (found_effect != bodyparts.end()) {
            found = true;
            effect &e = found_effect->second;
            // If we do, mod the duration, factoring in the mod value
            e.mod_duration(dur * e.get_dur_add_perc() / 100);
            // Limit to max duration
            if (e.get_max_duration() > 0 && e.get_duration() > e.get_max_duration()) {
                e.set_duration(e.get_max_duration());
            }
            // Adding a permanent effect makes it permanent
            if( e.is_permanent() ) {
                e.pause_effect();
            }
            // Set intensity if value is given
            if (intensity > 0) {
                e.set_intensity(intensity);
            // Else intensity uses the type'd step size if it already exists
            } else if (e.get_int_add_val() != 0) {
                e.mod_intensity(e.get_int_add_val());
            }

            // Bound intensity by [1, max intensity]
            if (e.get_intensity() < 1) {
                add_msg( m_debug, "Bad intensity, ID: %s", e.get_id().c_str() );
                e.set_intensity(1);
            } else if (e.get_intensity() > e.get_max_intensity()) {
                e.set_intensity(e.get_max_intensity());
            }
        }
    }

    if( found == false ) {
        // If we don't already have it then add a new one

        // Then check if the effect is blocked by another
        for( auto &elem : effects ) {
            for( auto &_effect_it : elem.second ) {
                for( const auto blocked_effect : _effect_it.second.get_blocks_effects() ) {
                    if (blocked_effect == eff_id) {
                        // The effect is blocked by another, return
                        return;
                    }
                }
            }
        }

        // Now we can make the new effect for application
        effect e(&type, dur, bp, permanent, intensity, calendar::turn);
        // Bound to max duration
        if (e.get_max_duration() > 0 && e.get_duration() > e.get_max_duration()) {
            e.set_duration(e.get_max_duration());
        }

        // Force intensity if it is duration based
        if( e.get_int_dur_factor() != 0 ) {
            // + 1 here so that the lowest is intensity 1, not 0
             e.set_intensity( ( e.get_duration() / e.get_int_dur_factor() ) + 1 );
        }
        // Bound new effect intensity by [1, max intensity]
        if (e.get_intensity() < 1) {
            add_msg( m_debug, "Bad intensity, ID: %s", e.get_id().c_str() );
            e.set_intensity(1);
        } else if (e.get_intensity() > e.get_max_intensity()) {
            e.set_intensity(e.get_max_intensity());
        }
        effects[eff_id][bp] = e;
        if (is_player()) {
            // Only print the message if we didn't already have it
            if(type.get_apply_message() != "") {
                     add_msg(type.gain_game_message_type(),
                             _(type.get_apply_message().c_str()));
            }
            add_memorial_log(pgettext("memorial_male",
                                           type.get_apply_memorial_log().c_str()),
                                  pgettext("memorial_female",
                                           type.get_apply_memorial_log().c_str()));
        }
        // Perform any effect addition effects.
        bool reduced = resists_effect(e);
        add_eff_effects(e, reduced);
    }
}
Esempio n. 13
0
/**
 * Attempts to harm a creature with a projectile.
 *
 * @param source Pointer to the creature who shot the projectile.
 * @param attack A structure describing the attack and its results.
 */
void Creature::deal_projectile_attack( Creature *source, dealt_projectile_attack &attack )
{
    const double missed_by = attack.missed_by;
    if( missed_by >= 1.0 ) {
        // Total miss
        return;
    }

    const projectile &proj = attack.proj;
    dealt_damage_instance &dealt_dam = attack.dealt_dam;
    const auto &proj_effects = proj.proj_effects;

    const bool u_see_this = g->u.sees(*this);

    const int avoid_roll = dodge_roll();
    // Do dice(10, speed) instead of dice(speed, 10) because speed could potentially be > 10000
    const int diff_roll = dice( 10, proj.speed );
    // Partial dodge, capped at [0.0, 1.0], added to missed_by
    const double dodge_rescaled = avoid_roll / static_cast<double>( diff_roll );
    const double goodhit = missed_by + std::max( 0.0, std::min( 1.0, dodge_rescaled ) ) ;

    if( goodhit >= 1.0 ) {
        // "Avoid" rather than "dodge", because it includes removing self from the line of fire
        //  rather than just Matrix-style bullet dodging
        if( source != nullptr && g->u.sees( *source ) ) {
            add_msg_player_or_npc(
                m_warning,
                _("You avoid %s projectile!"),
                _("<npcname> avoids %s projectile."),
                source->disp_name(true).c_str() );
        } else {
            add_msg_player_or_npc(
                m_warning,
                _("You avoid an incoming projectile!"),
                _("<npcname> avoids an incoming projectile.") );
        }

        attack.missed_by = 1.0; // Arbitrary value
        return;
    }

    // Bounce applies whether it does damage or not.
    if( proj.proj_effects.count( "BOUNCE" ) ) {
        add_effect( effect_bounced, 1);
    }

    body_part bp_hit;
    double hit_value = missed_by + rng_float(-0.5, 0.5);
    // Headshots considered elsewhere
    if( hit_value <= 0.4 ) {
        bp_hit = bp_torso;
    } else if (one_in(4)) {
        if( one_in(2)) {
            bp_hit = bp_leg_l;
        } else {
            bp_hit = bp_leg_r;
        }
    } else {
        if( one_in(2)) {
            bp_hit = bp_arm_l;
        } else {
            bp_hit = bp_arm_r;
        }
    }

    double damage_mult = 1.0;

    std::string message = "";
    game_message_type gmtSCTcolor = m_neutral;

    if( goodhit < 0.1 ) {
        message = _("Headshot!");
        gmtSCTcolor = m_headshot;
        damage_mult *= rng_float(2.45, 3.35);
        bp_hit = bp_head; // headshot hits the head, of course
    } else if( goodhit < 0.2 ) {
        message = _("Critical!");
        gmtSCTcolor = m_critical;
        damage_mult *= rng_float(1.75, 2.3);
    } else if( goodhit < 0.4 ) {
        message = _("Good hit!");
        gmtSCTcolor = m_good;
        damage_mult *= rng_float(1, 1.5);
    } else if( goodhit < 0.6 ) {
        damage_mult *= rng_float(0.5, 1);
    } else if( goodhit < 0.8 ) {
        message = _("Grazing hit.");
        gmtSCTcolor = m_grazing;
        damage_mult *= rng_float(0, .25);
    } else {
        damage_mult *= 0;
    }

    if( source != nullptr && !message.empty() ) {
        source->add_msg_if_player(m_good, message.c_str());
    }

    attack.missed_by = goodhit;

    // copy it, since we're mutating
    damage_instance impact = proj.impact;
    if( proj_effects.count("NOGIB") > 0 ) {
        impact.add_effect("NOGIB");
    }
    impact.mult_damage(damage_mult);

    dealt_dam = deal_damage(source, bp_hit, impact);
    dealt_dam.bp_hit = bp_hit;

    // Apply ammo effects to target.
    const std::string target_material = get_material();
    if (proj.proj_effects.count("FLAME")) {
        if (0 == target_material.compare("veggy") || 0 == target_material.compare("cotton") ||
            0 == target_material.compare("wool") || 0 == target_material.compare("paper") ||
            0 == target_material.compare("wood" ) ) {
            add_effect( effect_onfire, rng(8, 20));
        } else if (0 == target_material.compare("flesh") || 0 == target_material.compare("iflesh") ) {
            add_effect( effect_onfire, rng(5, 10));
        }
    } else if (proj.proj_effects.count("INCENDIARY") ) {
        if (0 == target_material.compare("veggy") || 0 == target_material.compare("cotton") ||
            0 == target_material.compare("wool") || 0 == target_material.compare("paper") ||
            0 == target_material.compare("wood") ) {
            add_effect( effect_onfire, rng(2, 6));
        } else if ( (0 == target_material.compare("flesh") || 0 == target_material.compare("iflesh") ) &&
                    one_in(4) ) {
            add_effect( effect_onfire, rng(1, 4));
        }
    } else if (proj.proj_effects.count("IGNITE")) {
        if (0 == target_material.compare("veggy") || 0 == target_material.compare("cotton") ||
            0 == target_material.compare("wool") || 0 == target_material.compare("paper") ||
            0 == target_material.compare("wood") ) {
            add_effect( effect_onfire, rng(6, 6));
        } else if (0 == target_material.compare("flesh") || 0 == target_material.compare("iflesh") ) {
            add_effect( effect_onfire, rng(10, 10));
        }
    }

    if( bp_hit == bp_head && proj_effects.count( "BLINDS_EYES" ) ) {
        // TODO: Change this to require bp_eyes
        add_env_effect( effect_blind, bp_eyes, 5, rng( 3, 10 ) );
    }

    if( proj_effects.count( "APPLY_SAP" ) ) {
        add_effect( effect_sap, dealt_dam.total_damage() );
    }

    int stun_strength = 0;
    if (proj.proj_effects.count("BEANBAG")) {
        stun_strength = 4;
    }
    if (proj.proj_effects.count("LARGE_BEANBAG")) {
        stun_strength = 16;
    }
    if( stun_strength > 0 ) {
        switch( get_size() ) {
        case MS_TINY:
            stun_strength *= 4;
            break;
        case MS_SMALL:
            stun_strength *= 2;
            break;
        case MS_MEDIUM:
        default:
            break;
        case MS_LARGE:
            stun_strength /= 2;
            break;
        case MS_HUGE:
            stun_strength /= 4;
            break;
        }
        add_effect( effect_stunned, rng(stun_strength / 2, stun_strength) );
    }

    if(u_see_this) {
        if( damage_mult == 0 ) {
            if( source != nullptr ) {
                add_msg( source->is_player() ? _("You miss!") : _("The shot misses!") );
            }
        } else if( dealt_dam.total_damage() == 0 ) {
            //~ 1$ - monster name, 2$ - monster's bodypart
            add_msg(_("The shot reflects off %1$s %2$s!"), disp_name(true).c_str(),
                    skin_name().c_str());
        } else if( is_player() ) {
                //monster hits player ranged
                //~ Hit message. 1$s is bodypart name in accusative. 2$d is damage value.
                add_msg_if_player(m_bad, _( "You were hit in the %1$s for %2$d damage." ),
                                  body_part_name_accusative(bp_hit).c_str( ),
                                  dealt_dam.total_damage());
        } else if( source != nullptr ) {
            if( source->is_player() ) {
                //player hits monster ranged
                SCT.add(posx(), posy(),
                        direction_from(0, 0, posx() - source->posx(), posy() - source->posy()),
                        get_hp_bar(dealt_dam.total_damage(), get_hp_max(), true).first,
                        m_good, message, gmtSCTcolor);

                if (get_hp() > 0) {
                    SCT.add(posx(), posy(),
                            direction_from(0, 0, posx() - source->posx(), posy() - source->posy()),
                            get_hp_bar(get_hp(), get_hp_max(), true).first, m_good,
                            //~ "hit points", used in scrolling combat text
                            _("hp"), m_neutral, "hp");
                } else {
                    SCT.removeCreatureHP();
                }

                add_msg(m_good, _("You hit %s for %d damage."),
                        disp_name().c_str(), dealt_dam.total_damage());
            } else if( u_see_this ) {
                //~ 1$ - shooter, 2$ - target
                add_msg(_("%1$s shoots %2$s."),
                        source->disp_name().c_str(), disp_name().c_str());
            }
        }
    }
    check_dead_state();
    attack.hit_critter = this;
    attack.missed_by = goodhit;
}
Esempio n. 14
0
body_part Creature::select_body_part(Creature *source, int hit_roll) const
{
    // Get size difference (-1,0,1);
    int szdif = source->get_size() - get_size();
    if(szdif < -1) {
        szdif = -1;
    } else if (szdif > 1) {
        szdif = 1;
    }

    add_msg( m_debug, "hit roll = %d", hit_roll);
    add_msg( m_debug, "source size = %d", source->get_size() );
    add_msg( m_debug, "target size = %d", get_size() );
    add_msg( m_debug, "difference = %d", szdif );

    std::map<body_part, double> hit_weights = default_hit_weights[szdif];

    // If the target is on the ground, even small/tiny creatures may target eyes/head. Also increases chances of larger creatures.
    // Any hit modifiers to locations should go here. (Tags, attack style, etc)
    if(is_on_ground()) {
        hit_weights[bp_eyes] += 1;
        hit_weights[bp_head] += 5;
    }

    //Adjust based on hit roll: Eyes, Head & Torso get higher, while Arms and Legs get lower.
    //This should eventually be replaced with targeted attacks and this being miss chances.
    // pow() is unstable at 0, so don't apply any changes.
    if( hit_roll != 0 ) {
        hit_weights[bp_eyes] *= std::pow(hit_roll, 1.15);
        hit_weights[bp_head] *= std::pow(hit_roll, 1.35);
        hit_weights[bp_torso] *= std::pow(hit_roll, 1);
        hit_weights[bp_arm_l] *= std::pow(hit_roll, 0.95);
        hit_weights[bp_arm_r] *= std::pow(hit_roll, 0.95);
        hit_weights[bp_leg_l] *= std::pow(hit_roll, 0.975);
        hit_weights[bp_leg_r] *= std::pow(hit_roll, 0.975);
    }

    // Debug for seeing weights.
    add_msg( m_debug, "eyes = %f", hit_weights.at( bp_eyes ) );
    add_msg( m_debug, "head = %f", hit_weights.at( bp_head ) );
    add_msg( m_debug, "torso = %f", hit_weights.at( bp_torso ) );
    add_msg( m_debug, "arm_l = %f", hit_weights.at( bp_arm_l ) );
    add_msg( m_debug, "arm_r = %f", hit_weights.at( bp_arm_r ) );
    add_msg( m_debug, "leg_l = %f", hit_weights.at( bp_leg_l ) );
    add_msg( m_debug, "leg_r = %f", hit_weights.at( bp_leg_r ) );

    double totalWeight = 0;
    for( const auto &hit_weight : hit_weights ) {
        totalWeight += hit_weight.second;
    }

    double roll = rng_float(0, totalWeight);
    body_part selected_part = bp_torso;

    for( const auto &hit_candidate : hit_weights) {
        roll -= hit_candidate.second;
        if(roll <= 0) {
            selected_part = hit_candidate.first;
            break;
        }
    }

    add_msg( m_debug, "selected part: %s", body_part_name(selected_part).c_str() );

    return selected_part;
}
Esempio n. 15
0
static void on_receive_block(struct r3964_info *pInfo)
{
   unsigned int length;
   struct r3964_client_info *pClient;
   struct r3964_block_header *pBlock;
   
   length=pInfo->rx_position;

   /* compare byte checksum characters: */
   if(pInfo->flags & R3964_BCC)
   {
      if(pInfo->bcc!=pInfo->last_rx)
      {
         TRACE_PE("checksum error - got %x but expected %x",
                pInfo->last_rx, pInfo->bcc);
         pInfo->flags |= R3964_CHECKSUM;
      }
   }

   /* check for errors (parity, overrun,...): */
   if(pInfo->flags & R3964_ERROR)
   {
      TRACE_PE("on_receive_block - transmission failed error %x",
             pInfo->flags & R3964_ERROR);
      
      put_char(pInfo, NAK);
      flush(pInfo);
      if(pInfo->nRetry<R3964_MAX_RETRIES)
      {
         pInfo->state=R3964_WAIT_FOR_RX_REPEAT;
         pInfo->nRetry++;
	 mod_timer(&pInfo->tmr, jiffies + R3964_TO_RX_PANIC);
      }
      else
      {
         TRACE_PE("on_receive_block - failed after max retries");
         pInfo->state=R3964_IDLE;
      }
      return;
   }

   
   /* received block; submit DLE: */
   put_char(pInfo, DLE);
   flush(pInfo);
   del_timer_sync(&pInfo->tmr);
   TRACE_PS(" rx success: got %d chars", length);

   /* prepare struct r3964_block_header: */
   pBlock = kmalloc(length+sizeof(struct r3964_block_header), GFP_KERNEL);
   TRACE_M("on_receive_block - kmalloc %x",(int)pBlock);

   if(pBlock==NULL)
      return;

   pBlock->length = length;
   pBlock->data   = ((unsigned char*)pBlock)+sizeof(struct r3964_block_header);
   pBlock->locks  = 0;
   pBlock->next   = NULL;
   pBlock->owner  = NULL;

   memcpy(pBlock->data, pInfo->rx_buf, length);

   /* queue block into rx_queue: */
   add_rx_queue(pInfo, pBlock);

   /* notify attached client processes: */
   for(pClient=pInfo->firstClient; pClient; pClient=pClient->next)
   {
      if(pClient->sig_flags & R3964_SIG_DATA)
      {
         add_msg(pClient, R3964_MSG_DATA, length, R3964_OK, pBlock);
      }
   }
   wake_up_interruptible (&pInfo->read_wait);
   
   pInfo->state = R3964_IDLE;

   trigger_transmit(pInfo);
}
Esempio n. 16
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;
    }

    // First, use the special attack, if we can!
    if (sp_timeout > 0) {
        sp_timeout--;
    }
    //If this monster has the ability to heal in combat, do it now.
    if (has_flag(MF_REGENERATES_50)) {
        if (hp < type->hp) {
            if (one_in(2)) {
                add_msg(m_warning, _("The %s is visibly regenerating!"), name().c_str());
            }
            hp += 50;
            if(hp > type->hp) {
                hp = type->hp;
            }
        }
    }
    if (has_flag(MF_REGENERATES_10)) {
        if (hp < type->hp) {
            if (one_in(2)) {
                add_msg(m_warning, _("The %s seems a little healthier."), name().c_str());
            }
            hp += 10;
            if(hp > type->hp) {
                hp = type->hp;
            }
        }
    }

    //The monster can consume objects it stands on. Check if there are any.
    //If there are. Consume them.
    if (has_flag(MF_ABSORBS)) {
        if(!g->m.i_at(posx(), posy()).empty()) {
            add_msg(_("The %s flows around the objects on the floor and they are quickly dissolved!"), name().c_str());
            std::vector<item> items_absorbed = g->m.i_at(posx(), posy());
            for( size_t i = 0; i < items_absorbed.size(); ++i ) {
                hp += items_absorbed.at(i).volume(); //Yeah this means it can get more HP than normal.
            }
            g->m.i_clear(posx(), posy());
        }
    }

    //Monster will regen morale and aggression if it is on max HP
    //It regens more morale and aggression if is currently fleeing.
    if(has_flag(MF_REGENMORALE) && hp >= type->hp){
        if(is_fleeing(g->u)){
            morale = type->morale;
            anger = type->agro;
        }
        if(morale <= type->morale)
            morale += 1;
        if(anger <= type->agro)
            anger += 1;
        if(morale < 0)
            morale += 5;
        if(anger < 0)
            anger += 5;
    }

    // If this critter dies in sunlight, check & assess damage.
    if (g->is_in_sunlight(posx(), posy()) && has_flag(MF_SUNDEATH)) {
        add_msg(_("The %s burns horribly in the sunlight!"), name().c_str());
        hp -= 100;
        if(hp < 0) {
            hp = 0  ;
        }
    }

    if( sp_timeout == 0 && (friendly == 0 || has_flag(MF_FRIENDLY_SPECIAL)) &&
        !has_effect("pacified") ) {
        mattack ma;
        if(!is_hallucination()) {
            (ma.*type->sp_attack)(this);
        }
    }
    if (moves < 0) {
        return;
    }
    if (has_flag(MF_IMMOBILE)) {
        moves = 0;
        return;
    }
    if (has_effect("stunned")) {
        stumble(false);
        moves = 0;
        return;
    }
    if (has_effect("downed")) {
        moves = 0;
        return;
    }
    if (friendly != 0) {
        if (friendly > 0) {
            friendly--;
        }
        friendly_move();
        return;
    }

    bool moved = false;
    point next;
    int mondex = (!plans.empty() ? g->mon_at(plans[0].x, plans[0].y) : -1);

    monster_attitude current_attitude = attitude();
    if (friendly == 0) {
        current_attitude = attitude(&(g->u));
    }
    // If our plans end in a player, set our attitude to consider that player
    if (!plans.empty()) {
        if (plans.back().x == g->u.posx && plans.back().y == g->u.posy) {
            current_attitude = attitude(&(g->u));
        } else {
            for (auto &i : g->active_npc) {
                if (plans.back().x == i->posx && plans.back().y == i->posy) {
                    current_attitude = attitude(i);
                }
            }
        }
    }

    if (current_attitude == MATT_IGNORE ||
          (current_attitude == MATT_FOLLOW && plans.size() <= MONSTER_FOLLOW_DIST)) {
        moves -= 100;
        stumble(false);
        return;
    }

    if (!plans.empty() &&
        (mondex == -1 || g->zombie(mondex).friendly != 0 || has_flag(MF_ATTACKMON)) &&
        (can_move_to(plans[0].x, plans[0].y) ||
         (plans[0].x == g->u.posx && plans[0].y == g->u.posy) ||
         (has_flag(MF_BASHES) && g->m.bash_rating(bash_skill(), plans[0].x, plans[0].y) > 0))){
        // CONCRETE PLANS - Most likely based on sight
        next = plans[0];
        moved = true;
    } else if (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.
        plans.clear();
        point tmp = scent_move();
        if (tmp.x != -1) {
            next = tmp;
            moved = true;
        }
    }
    if (wandf > 0 && !moved) { // No LOS, no scent, so as a fall-back follow sound
        plans.clear();
        point tmp = wander_next();
        if (tmp.x != posx() || tmp.y != posy()) {
            next = tmp;
            moved = true;
        }
    }

    // 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
        // Note: The below works because C++ in A() || B() won't call B() if A() is true
        int& x = next.x; int& y = next.y; // Define alias for x and y
        bool did_something = attack_at(x, y) || bash_at(x, y) || move_to(x, y);
        if(!did_something) {
            moves -= 100; // If we don't do this, we'll get infinite loops.
        }
    } else {
        moves -= 100;
    }

    // If we're close to our target, we get focused and don't stumble
    if ((has_flag(MF_STUMBLES) && (plans.size() > 3 || plans.empty())) ||
          !moved) {
        stumble(moved);
    }
}
Esempio n. 17
0
void event::actualize()
{
    switch( type ) {
        case EVENT_HELP:
            debugmsg("Currently disabled while NPC and monster factions are being rewritten.");
        break;

    case EVENT_ROBOT_ATTACK: {
        const auto u_pos = g->u.global_sm_location();
        if (rl_dist(u_pos, map_point) <= 4) {
            const mtype_id& robot_type = one_in( 2 ) ? mon_copbot : mon_riotbot;

            g->u.add_memorial_log( pgettext("memorial_male", "Became wanted by the police!"),
                                    pgettext("memorial_female", "Became wanted by the police!"));
            int robx = (u_pos.x > map_point.x ? 0 - SEEX * 2 : SEEX * 4);
            int roby = (u_pos.y > map_point.y ? 0 - SEEY * 2 : SEEY * 4);
            g->summon_mon(robot_type, tripoint(robx, roby, g->u.posz()));
        }
    } break;

    case EVENT_SPAWN_WYRMS: {
        if (g->get_levz() >= 0) {
            return;
        }
        g->u.add_memorial_log(pgettext("memorial_male", "Drew the attention of more dark wyrms!"),
                                pgettext("memorial_female", "Drew the attention of more dark wyrms!"));
        int num_wyrms = rng(1, 4);
        for (int i = 0; i < num_wyrms; i++) {
            int tries = 0;
            tripoint monp = g->u.pos();
            do {
                monp.x = rng(0, SEEX * MAPSIZE);
                monp.y = rng(0, SEEY * MAPSIZE);
                tries++;
            } while (tries < 10 && !g->is_empty(monp) &&
                    rl_dist(g->u.pos(), monp) <= 2);
            if (tries < 10) {
                g->m.ter_set(monp, t_rock_floor);
                g->summon_mon(mon_dark_wyrm, monp);
            }
        }
        // You could drop the flag, you know.
        if (g->u.has_amount("petrified_eye", 1)) {
            sounds::sound(g->u.pos(), 60, "");
            if (!g->u.is_deaf()) {
                add_msg(_("The eye you're carrying lets out a tortured scream!"));
                g->u.add_morale(MORALE_SCREAM, -15, 0, 300, 5);
            }
        }
        if (!one_in(25)) { // They just keep coming!
            g->add_event(EVENT_SPAWN_WYRMS, int(calendar::turn) + rng(15, 25));
        }
    } break;

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

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

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

  case EVENT_TEMPLE_FLOOD: {
   bool flooded = false;

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

    case EVENT_TEMPLE_SPAWN: {
        static const std::array<mtype_id, 4> temple_monsters = { {
            mon_sewer_snake, mon_dermatik, mon_spider_widow_giant, mon_spider_cellar_giant
        } };
        const mtype_id &montype = random_entry( temple_monsters );
        int tries = 0, x, y;
        do {
            x = rng(g->u.posx() - 5, g->u.posx() + 5);
            y = rng(g->u.posy() - 5, g->u.posy() + 5);
            tries++;
        } while (tries < 20 && !g->is_empty({x, y, g->u.posz()}) &&
                    rl_dist(x, y, g->u.posx(), g->u.posy()) <= 2);
        if (tries < 20) {
            g->summon_mon(montype, tripoint(x, y, g->u.posz()));
        }
    } break;

  default:
   break; // Nothing happens for other events
 }
}
Esempio n. 18
0
int monster::move_to(int x, int y, bool force)
{
    // Make sure that we can move there, unless force is true.
    if(!force) if(!g->is_empty(x, y) || !can_move_to(x, y)) {
        return 0;
    }

    if (has_effect("beartrap") || has_effect("tied")) {
        moves = 0;
        return 0;
    }

    if (!plans.empty()) {
        plans.erase(plans.begin());
    }

    if (!force) {
        moves -= calc_movecost(posx(), posy(), x, y);
    }

    //Check for moving into/out of water
    bool was_water = g->m.is_divable(posx(), posy());
    bool will_be_water = g->m.is_divable(x, y);

    if(was_water && !will_be_water && g->u_see(x, y)) {
        //Use more dramatic messages for swimming monsters
        add_msg(m_warning, _("A %s %s from the %s!"), name().c_str(),
                   has_flag(MF_SWIMS) || has_flag(MF_AQUATIC) ? _("leaps") : _("emerges"),
                   g->m.tername(posx(), posy()).c_str());
    } else if(!was_water && will_be_water && g->u_see(x, y)) {
        add_msg(m_warning, _("A %s %s into the %s!"), name().c_str(),
                   has_flag(MF_SWIMS) || has_flag(MF_AQUATIC) ? _("dives") : _("sinks"),
                   g->m.tername(x, y).c_str());
    }

    setpos(x, y);
    footsteps(x, y);
    if(is_hallucination()) {
        //Hallucinations don't do any of the stuff after this point
        return 1;
    }
    if (type->size != MS_TINY && g->m.has_flag("SHARP", posx(), posy()) && !one_in(4)) {
        apply_damage( nullptr, bp_torso, rng( 2, 3 ) );
    }
    if (type->size != MS_TINY && g->m.has_flag("ROUGH", posx(), posy()) && one_in(6)) {
        apply_damage( nullptr, bp_torso, rng( 1, 2 ) );
    }
    if (g->m.has_flag("UNSTABLE", x, y)) {
        add_effect("bouldering", 1, 1, true);
    } else if (has_effect("bouldering")) {
        remove_effect("bouldering");
    }
    if (!digging() && !has_flag(MF_FLIES) &&
          g->m.tr_at(posx(), posy()) != tr_null) { // Monster stepped on a trap!
        trap* tr = traplist[g->m.tr_at(posx(), posy())];
        if (dice(3, type->sk_dodge + 1) < dice(3, tr->get_avoidance())) {
            tr->trigger(this, posx(), posy());
        }
    }
    // Diggers turn the dirt into dirtmound
    if (digging()){
        int factor = 0;
        switch (type->size) {
        case MS_TINY:
            factor = 100;
            break;
        case MS_SMALL:
            factor = 30;
            break;
        case MS_MEDIUM:
            factor = 6;
            break;
        case MS_LARGE:
            factor = 3;
            break;
        case MS_HUGE:
            factor = 1;
            break;
        }
        if (has_flag(MF_VERMIN)) {
            factor *= 100;
        }
        if (one_in(factor)) {
            g->m.ter_set(posx(), posy(), t_dirtmound);
        }
    }
    // Acid trail monsters leave... a trail of acid
    if (has_flag(MF_ACIDTRAIL)){
        g->m.add_field(posx(), posy(), fd_acid, 3);
    }

    if (has_flag(MF_SLUDGETRAIL)) {
        for (int dx = -1; dx <= 1; dx++) {
            for (int dy = -1; dy <= 1; dy++) {
                const int fstr = 3 - (abs(dx) + abs(dy));
                if (fstr >= 2) {
                    g->m.add_field(posx() + dx, posy() + dy, fd_sludge, fstr);
                }
            }
        }
    }
    if (has_flag(MF_LEAKSGAS)){
        if (one_in(6)){
        g->m.add_field(posx() + rng(-1,1), posy() + rng(-1, 1), fd_toxic_gas, 3);
        }
    }

    return 1;
}
Esempio n. 19
0
void mission_start::place_npc_software(mission *miss)
{
 npc* dev = g->find_npc(miss->npc_id);
 if (dev == NULL) {
  debugmsg("Couldn't find NPC! %d", miss->npc_id);
  return;
 }
 g->u.i_add( item("usb_drive", 0) );
 add_msg(_("%s gave you a USB drive."), dev->name.c_str());

 std::string type = "house";

 switch (dev->myclass) {
 case NC_HACKER:
  miss->item_id = "software_hacking";
  break;
 case NC_DOCTOR:
  miss->item_id = "software_medical";
  type = "s_pharm";
  miss->follow_up = MISSION_GET_ZOMBIE_BLOOD_ANAL;
  break;
 case NC_SCIENTIST:
  miss->item_id = "software_math";
  break;
 default:
  miss->item_id = "software_useless";
 }

    int dist = 0;
    point place;
    if (type == "house") {
        place = random_house_in_closest_city();
    } else {
        place = overmap_buffer.find_closest(dev->global_omt_location(), type, dist, false);
    }
    miss->target = place;
    overmap_buffer.reveal(place, 6, g->get_levz());

 tinymap compmap;
 compmap.load(place.x * 2, place.y * 2, g->get_levz(), false);
 point comppoint;

    oter_id oter = overmap_buffer.ter(place.x, place.y, 0);
    if( is_ot_type("house", oter) || is_ot_type("s_pharm", oter) || oter == "" ) {
        std::vector<point> valid;
        for (int x = 0; x < SEEX * 2; x++) {
            for (int y = 0; y < SEEY * 2; y++) {
                if (compmap.ter(x, y) == t_floor && compmap.furn(x, y) == f_null) {
                    bool okay = false;
                    int wall = 0;
                    for (int x2 = x - 1; x2 <= x + 1 && !okay; x2++) {
                        for (int y2 = y - 1; y2 <= y + 1 && !okay; y2++) {
                            if (compmap.furn(x2, y2) == f_bed || compmap.furn(x2, y2) == f_dresser) {
                                okay = true;
                                valid.push_back( point(x, y) );
                            }
                            if ( compmap.has_flag_ter("WALL", x2, y2) ) {
                                wall++;
                            }
                        }
                    }
                    if ( wall == 5 ) {
                        if ( compmap.is_last_ter_wall( true, x, y, SEEX * 2, SEEY * 2, NORTH ) &&
                             compmap.is_last_ter_wall( true, x, y, SEEX * 2, SEEY * 2, SOUTH ) &&
                             compmap.is_last_ter_wall( true, x, y, SEEX * 2, SEEY * 2, WEST ) &&
                             compmap.is_last_ter_wall( true, x, y, SEEX * 2, SEEY * 2, EAST ) ) {
                            valid.push_back( point(x, y) );
                        }
                    }
                }
            }
        }
        if (valid.empty()) {
            comppoint = point( rng(6, SEEX * 2 - 7), rng(6, SEEY * 2 - 7) );
        } else {
            comppoint = valid[rng(0, valid.size() - 1)];
        }
    }

 compmap.ter_set(comppoint.x, comppoint.y, t_console);
 computer *tmpcomp = compmap.add_computer( tripoint( comppoint, g->get_levz() ), string_format(_("%s's Terminal"), dev->name.c_str()), 0);
 tmpcomp->mission_id = miss->uid;
 tmpcomp->add_option(_("Download Software"), COMPACT_DOWNLOAD_SOFTWARE, 0);
 compmap.save();
}
Esempio n. 20
0
edible_rating player::can_eat( const item &food, bool interactive, bool force ) const
{
    if( is_npc() || force ) {
        // Just to be sure
        interactive = false;
    }

    const std::string &itname = food.tname();
    // Helper to avoid ton of `if( interactive )`
    // Prints if interactive is true, does nothing otherwise
    const auto maybe_print = [interactive, &itname]
    ( game_message_type type, const char *str ) {
        if( interactive ) {
            add_msg( type, str, itname.c_str() );
        }
    };
    // As above, but for queries
    // Asks if interactive and not force
    // Always true if force
    // Never true otherwise
    const auto maybe_query = [force, interactive, &itname, this]( const char *str ) {
        if( force ) {
            return true;
        } else if( !interactive ) {
            return false;
        }

        return query_yn( str, itname.c_str() );
    };

    const auto comest = food.type->comestible.get();
    if( comest == nullptr ) {
        maybe_print( m_info, _( "That doesn't look edible." ) );
        return INEDIBLE;
    }

    if( comest->tool != "null" ) {
        bool has = has_amount( comest->tool, 1 );
        if( item::count_by_charges( comest->tool ) ) {
            has = has_charges( comest->tool, 1 );
        }
        if( !has ) {
            if( interactive ) {
                add_msg_if_player( m_info, _( "You need a %s to consume that!" ),
                                   item::nname( comest->tool ).c_str() );
            }
            return NO_TOOL;
        }
    }

    if( is_underwater() ) {
        maybe_print( m_info, _( "You can't do that while underwater." ) );
        return INEDIBLE;
    }
    // For all those folks who loved eating marloss berries.  D:< mwuhahaha
    if( has_trait( "M_DEPENDENT" ) && food.type->id != "mycus_fruit" ) {
        maybe_print( m_info, _( "We can't eat that.  It's not right for us." ) );
        return INEDIBLE_MUTATION;
    }

    const bool drinkable = comest->comesttype == "DRINK" && !food.has_flag( "USE_EAT_VERB" );
    // Here's why PROBOSCIS is such a negative trait.
    if( has_trait( "PROBOSCIS" ) && !drinkable ) {
        maybe_print( m_info, _( "Ugh, you can't drink that!" ) );
        return INEDIBLE_MUTATION;
    }

    int capacity = stomach_capacity();

    // TODO: Move this cache to a structure and pass it around
    // to speed up checking entire inventory for edibles
    const bool gourmand = has_trait( "GOURMAND" );
    const bool hibernate = has_active_mutation( "HIBERNATE" );
    const bool eathealth = has_trait( "EATHEALTH" );
    const bool slimespawner = has_trait( "SLIMESPAWNER" );
    const int nutr = nutrition_for( food.type );
    const int quench = comest->quench;
    bool spoiled = food.rotten();

    const int temp_hunger = get_hunger() - nutr;
    const int temp_thirst = get_thirst() - quench;

    const bool overeating = get_hunger() < 0 && nutr >= 5 && !gourmand && !eathealth && !slimespawner &&
                            !hibernate;

    if( interactive && hibernate &&
        ( get_hunger() >= -60 && get_thirst() >= -60 ) &&
        ( temp_hunger < -60 || temp_thirst < -60 ) ) {
        if( !maybe_query( _( "You're adequately fueled. Prepare for hibernation?" ) ) ) {
            return TOO_FULL;
        }
    }

    const bool carnivore = has_trait( "CARNIVORE" );
    if( carnivore && nutr > 0 &&
        food.has_any_flag( carnivore_blacklist ) && !food.has_flag( "CARNIVORE_OK" ) ) {
        maybe_print( m_info, _( "Eww.  Inedible plant stuff!" ) );
        return INEDIBLE_MUTATION;
    }

    if( ( has_trait( "HERBIVORE" ) || has_trait( "RUMINANT" ) ) &&
        food.has_any_flag( herbivore_blacklist ) ) {
        // Like non-cannibal, but more strict!
        maybe_print( m_info, _( "The thought of eating that makes you feel sick.  You decide not to." ) );
        return INEDIBLE_MUTATION;
    }

    if( food.has_flag( "CANNIBALISM" ) ) {
        if( !has_trait_flag( "CANNIBAL" ) &&
            !maybe_query( _( "The thought of eating that makes you feel sick.  Really do it?" ) ) ) {
            return CANNIBALISM;
        }
    }

    if( is_allergic( food ) &&
        !maybe_query( _( "Really eat that %s?  Your stomach won't be happy." ) ) ) {
        return ALLERGY;
    }

    if( carnivore && food.has_flag( "ALLERGEN_JUNK" ) && !food.has_flag( "CARNIVORE_OK" ) &&
        !maybe_query( _( "Really eat that %s?  Your stomach won't be happy." ) ) ) {
        return ALLERGY;
    }

    const bool saprophage = has_trait( "SAPROPHAGE" );
    // The item is solid food
    const bool chew = comest->comesttype == "FOOD" || food.has_flag( "USE_EAT_VERB" );
    if( spoiled ) {
        if( !saprophage && !has_trait( "SAPROVORE" ) &&
            !maybe_query( _( "This %s smells awful!  Eat it?" ) ) ) {
            return ROTTEN;
        }
    } else if( saprophage && chew && !food.has_flag( "FERTILIZER" ) &&
               !maybe_query( _( "Really eat that %s?  Your stomach won't be happy." ) ) ) {
        // Note: We're allowing all non-solid "food". This includes drugs
        // Hardcoding fertilizer for now - should be a separate flag later
        //~ No, we don't eat "rotten" food. We eat properly aged food, like a normal person.
        //~ Semantic difference, but greatly facilitates people being proud of their character.
        maybe_print( m_info, _( "It's too fresh, let it age a little first." ) );
        return ROTTEN;
    }

    // Print at most one of those
    bool overfull = false;
    if( overeating ) {
        overfull = !maybe_query( _( "You're full.  Force yourself to eat?" ) );
    } else if( ( ( nutr > 0 && temp_hunger < capacity ) ||
                 ( comest->quench > 0 && temp_thirst < capacity ) ) &&
               !eathealth && !slimespawner ) {
        overfull = !maybe_query( _( "You will not be able to finish it all.  Consume it?" ) );
    }

    if( overfull ) {
        return TOO_FULL;
    }

    // All checks ended, it's edible (or we're pretending it is)
    return EDIBLE;
}