Just refactored player::consume so I can now add food consumption from nested / mixed containers without going insane. I wonder how can someone find anything in these walls of code.
Original
bool player::consume(int target_position)
{
item *to_eat = NULL;
it_comest *comest = NULL;
int which = -3; // Helps us know how to delete the item which got eaten
if(target_position == INT_MIN) {
add_msg_if_player( m_info, _("You do not have that item."));
return false;
} if (is_underwater()) {
add_msg_if_player( m_info, _("You can't do that while underwater."));
return false;
} else if( target_position < -1 ) {
add_msg_if_player( m_info, _( "You can't eat worn items, you have to take them off." ) );
return false;
} else if (target_position == -1) {
// Consume your current weapon
if (weapon.is_food_container(this)) {
to_eat = &weapon.contents[0];
which = -2;
if (weapon.contents[0].is_food()) {
comest = dynamic_cast<it_comest*>(weapon.contents[0].type);
}
} else if (weapon.is_food(this)) {
to_eat = &weapon;
which = -1;
comest = dynamic_cast<it_comest*>(weapon.type);
} else {
add_msg_if_player(m_info, _("You can't eat your %s."), weapon.tname().c_str());
if(is_npc()) {
debugmsg("%s tried to eat a %s", name.c_str(), weapon.tname().c_str());
}
return false;
}
} else {
// Consume item from inventory
item& it = inv.find_item(target_position);
if (it.is_food_container(this)) {
to_eat = &(it.contents[0]);
which = 1;
if (it.contents[0].is_food()) {
comest = dynamic_cast<it_comest*>(it.contents[0].type);
}
} else if (it.is_food(this)) {
to_eat = ⁢
which = 0;
comest = dynamic_cast<it_comest*>(it.type);
} else {
add_msg_if_player(m_info, _("You can't eat your %s."), it.tname().c_str());
if(is_npc()) {
debugmsg("%s tried to eat a %s", name.c_str(), it.tname().c_str());
}
return false;
}
}
if(to_eat == NULL) {
debugmsg("Consumed item is lost!");
return false;
}
int amount_used = 1;
bool was_consumed = false;
if (comest != NULL) {
if (comest->comesttype == "FOOD" || comest->comesttype == "DRINK") {
was_consumed = eat(to_eat, comest);
if (!was_consumed) {
return was_consumed;
}
} else if (comest->comesttype == "MED") {
if (comest->tool != "null") {
// Check tools
bool has = has_amount(comest->tool, 1);
// Tools with charges need to have charges, not just be present.
if (item_controller->count_by_charges( comest->tool )) {
has = has_charges(comest->tool, 1);
}
if (!has) {
add_msg_if_player(m_info, _("You need a %s to consume that!"),
item_controller->nname( comest->tool ).c_str());
return false;
}
use_charges(comest->tool, 1); // Tools like lighters get used
}
if (comest->has_use()) {
//Check special use
amount_used = comest->invoke(this, to_eat, false, pos());
if( amount_used <= 0 ) {
return false;
}
}
consume_effects(to_eat, comest);
moves -= 250;
was_consumed = true;
} else {
debugmsg("Unknown comestible type of item: %s\n", to_eat->tname().c_str());
}
} else {
// Consume other type of items.
// For when bionics let you eat fuel
if (to_eat->is_ammo() && has_active_bionic("bio_batteries") &&
dynamic_cast<it_ammo*>(to_eat->type)->type == "battery") {
const int factor = 1;
int max_change = max_power_level - power_level;
if (max_change == 0) {
add_msg_if_player(m_info, _("Your internal power storage is fully powered."));
}
charge_power(to_eat->charges / factor);
to_eat->charges -= max_change * factor; //negative charges seem to be okay
to_eat->charges++; //there's a flat subtraction later
} else if (!to_eat->type->is_food() && !to_eat->is_food_container(this)) {
if (to_eat->type->is_book()) {
it_book* book = dynamic_cast<it_book*>(to_eat->type);
if (book->type != NULL && !query_yn(_("Really eat %s?"), to_eat->tname().c_str())) {
return false;
}
}
int charge = (to_eat->volume() + to_eat->weight()) / 9;
if (to_eat->made_of("leather")) {
charge /= 4;
}
if (to_eat->made_of("wood")) {
charge /= 2;
}
charge_power(charge);
to_eat->charges = 0;
add_msg_player_or_npc( _("You eat your %s."), _("<npcname> eats a %s."),
to_eat->tname().c_str());
}
moves -= 250;
was_consumed = true;
}
if (!was_consumed) {
return false;
}
// Actions after consume
to_eat->charges -= amount_used;
if (to_eat->charges <= 0) {
if (which == -1) {
weapon = ret_null;
} else if (which == -2) {
weapon.contents.erase(weapon.contents.begin());
add_msg_if_player(_("You are now wielding an empty %s."), weapon.tname().c_str());
} else if (which == 0) {
inv.remove_item(target_position);
} else if (which >= 0) {
item& it = inv.find_item(target_position);
it.contents.erase(it.contents.begin());
const bool do_restack = inv.const_stack(target_position).size() > 1;
if (!is_npc()) {
bool drop_it = false;
if (OPTIONS["DROP_EMPTY"] == "no") {
drop_it = false;
} else if (OPTIONS["DROP_EMPTY"] == "watertight") {
drop_it = it.is_container() && !(it.has_flag("WATERTIGHT") && it.has_flag("SEALS"));
} else if (OPTIONS["DROP_EMPTY"] == "all") {
drop_it = true;
}
if (drop_it) {
add_msg(_("You drop the empty %s."), it.tname().c_str());
g->m.add_item_or_charges(posx, posy, inv.remove_item(target_position));
} else {
add_msg(m_info, _("%c - an empty %s"), it.invlet, it.tname().c_str());
}
}
if (do_restack) {
inv.restack(this);
}
inv.unsort();
}
}
return true;
}
New
bool player::consume( int target_position )
{
item *it = nullptr;
it_comest *comest = nullptr;
consume_loc loc = CONSUME_NONE; // Helps us know how to delete the item which got eaten
it = consume_choose_item( target_position, loc );
if( it == nullptr ) {
return false;
}
int used_amount = 0;
bool consumed = false;
comest = dynamic_cast<it_comest*>( it->type );
if( comest != nullptr ) {
if( comest->comesttype == "FOOD" || comest->comesttype == "DRINK" ) {
consumed = consume_food( it, comest, used_amount );
} else if( comest->comesttype == "MED" ) {
consumed = consume_med( it, comest, used_amount );
} else {
debugmsg( "Unknown comestible type of item: %s\n", it->tname().c_str() );
}
} else {
consumed = consume_bio( it, used_amount );
}
if( !consumed ) {
return false;
}
consume_cleanup( loc, target_position, it, used_amount );
return true;
}
item* player::consume_choose_item( const int target_position, consume_loc &loc )
{
item *to_eat = nullptr;
if( target_position == INT_MIN ) {
add_msg_if_player( m_info, _( "You do not have that item." ) );
return nullptr;
}
if( is_underwater() ) {
add_msg_if_player( m_info, _( "You can't do that while underwater." ) );
return nullptr;
}
if( target_position < -1 ) {
add_msg_if_player( m_info, _( "You can't eat worn items, you have to take them off." ) );
return nullptr;
}
if( target_position == -1 ) {
// Consume your current weapon
if( weapon.is_food_container( this ) ) {
to_eat = &weapon.contents[0];
loc = CONSUME_WEAPON_CONTAINER;
} else if( weapon.is_food( this ) ) {
to_eat = &weapon;
loc = CONSUME_WEAPON;
} else {
add_msg_if_player( m_info, _( "You can't eat your %s." ), weapon.tname().c_str() );
if( is_npc() ) {
debugmsg( "%s tried to eat a %s", name.c_str(), weapon.tname().c_str() );
}
}
} else {
// Consume item from inventory
item& it = inv.find_item( target_position );
if( it.is_food_container( this ) ) {
to_eat = &( it.contents[0] );
loc = CONSUME_INVENTORY_CONTAINER;
} else if( it.is_food( this ) ) {
to_eat = ⁢
loc = CONSUME_INVENTORY;
} else {
add_msg_if_player( m_info, _( "You can't eat your %s." ), it.tname().c_str() );
if( is_npc() ) {
debugmsg( "%s tried to eat a %s", name.c_str(), it.tname().c_str() );
}
}
}
return to_eat;
}
bool player::consume_food( item *it, it_comest *comest, int &used_amount )
{
used_amount = 1;
return eat( it, comest );
}
bool player::consume_med( item *it, it_comest *comest, int &used_amount )
{
// Check tools
if( comest->tool != "null" ) {
bool has = has_amount( comest->tool, 1 );
// Tools with charges need to have charges, not just be present.
if( item_controller->count_by_charges( comest->tool ) ) {
has = has_charges( comest->tool, 1 );
}
if( !has ) {
add_msg_if_player( m_info, _( "You need a %s to consume that!" ),
item_controller->nname( comest->tool ).c_str() );
return false;
}
use_charges( comest->tool, 1 ); // Tools like lighters get used
}
used_amount = 1;
//Check special use
if( comest->has_use() ) {
used_amount = comest->invoke( this, it, false, pos() );
if( used_amount <= 0 ) {
return false;
}
}
consume_effects( it, comest );
moves -= 250;
return true;
}
bool player::consume_bio( item *it, int &used_amount )
{
if( it->type->is_food() ) {
debugmsg("player::consume_bio( %s ); is food!", it->tname().c_str());
return false;
}
if( it->is_food_container( this ) ) {
debugmsg("player::consume_bio( %s ); is food container!", it->tname().c_str());
return false;
}
if( it->is_ammo() && has_active_bionic( "bio_batteries" ) &&
dynamic_cast<it_ammo*>( it->type )->type == "battery" ) {
int missing_power = max_power_level - power_level;
used_amount = std::min( (long)missing_power, it->charges );
charge_power( used_amount );
if( power_level == max_power_level ) {
add_msg_if_player( m_info, _( "Your internal power storage is fully powered." ) );
}
} else {
if( it->type->is_book() ) {
it_book* book = dynamic_cast<it_book*>( it->type );
if( book->type != NULL && !query_yn( _( "Really eat %s?" ), it->tname().c_str() ) ) {
return false;
}
}
int charge = ( it->volume() + it->weight() ) / 9;
if( it->made_of( "leather" ) ) {
charge /= 4;
}
if( it->made_of( "wood" ) ) {
charge /= 2;
}
charge_power( charge );
used_amount = it->charges;
add_msg_player_or_npc( _( "You eat your %s." ), _( "<npcname> eats a %s." ),
it->tname().c_str() );
}
moves -= 250;
return true;
}
void player::consume_cleanup( consume_loc loc, int target_position, item *it, int used_amount )
{
it->charges -= used_amount;
if( it->charges <= 0 ) {
switch ( loc ) {
case CONSUME_WEAPON:
weapon = ret_null;
break;
case CONSUME_WEAPON_CONTAINER:
weapon.contents.erase( weapon.contents.begin() );
add_msg_if_player( _( "You are now wielding an empty %s." ), weapon.tname().c_str() );
break;
case CONSUME_INVENTORY:
inv.remove_item( target_position );
break;
case CONSUME_INVENTORY_CONTAINER:
item &cont = inv.find_item( target_position );
cont.contents.erase( cont.contents.begin() );
const bool do_restack = inv.const_stack( target_position ).size() > 1;
if( !is_npc() ) {
if( options_drop_container( cont ) ) {
add_msg( _( "You drop the empty %s." ), cont.tname().c_str() );
g->m.add_item_or_charges( posx, posy, inv.remove_item( target_position ) );
} else {
add_msg( m_info, _( "%c - an empty %s" ), cont.invlet, cont.tname().c_str() );
}
}
if( do_restack ) {
inv.restack( this );
}
inv.unsort();
break;
}
}
}
bool player::options_drop_container( const item &it ) const
{
if ( it.is_container_empty() ) {
if( OPTIONS["DROP_EMPTY"] == "all" ) {
return true;
}
if( OPTIONS["DROP_EMPTY"] == "watertight" ) {
return it.is_watertight_container();
}
}
return false;
}