Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

butchery requirements 2 #42654

Merged
merged 1 commit into from Aug 6, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
55 changes: 55 additions & 0 deletions data/json/butchery_requirements.json
@@ -0,0 +1,55 @@
[
{
"type": "butchery_requirement",
"id": "default",
"requirements": {
"1.0": [
{
"FIELD_DRESS": "field_dress",
"QUARTER": "field_dress",
"SKIN": "field_dress",
"DISMEMBER": "field_dress",
"DISSECT": "dissect_small",
"FULL": "butchery_small",
"QUICK": "butchery_small"
},
{
"FIELD_DRESS": "field_dress",
"QUARTER": "field_dress",
"SKIN": "field_dress",
"DISMEMBER": "field_dress",
"DISSECT": "dissect_small",
"FULL": "butchery_small",
"QUICK": "butchery_small"
},
{
"FIELD_DRESS": "field_dress",
"QUARTER": "field_dress",
"SKIN": "field_dress",
"DISMEMBER": "field_dress",
"DISSECT": "dissect_small",
"FULL": "butchery_small",
"QUICK": "butchery_small"
},
{
"FIELD_DRESS": "field_dress",
"QUARTER": "field_dress",
"SKIN": "field_dress",
"DISMEMBER": "field_dress",
"DISSECT": "dissect_large",
"FULL": "full_butchery_large",
"QUICK": "butchery_large"
},
{
"FIELD_DRESS": "field_dress",
"QUARTER": "field_dress",
"SKIN": "field_dress",
"DISMEMBER": "field_dress",
"DISSECT": "dissect_large",
"FULL": "full_butchery_large",
"QUICK": "butchery_large"
}
]
}
}
]
76 changes: 40 additions & 36 deletions src/activity_handlers.cpp
Expand Up @@ -21,6 +21,7 @@
#include "bionics.h"
#include "basecamp.h"
#include "bodypart.h"
#include "butchery_requirements.h"
#include "calendar.h"
#include "cata_utility.h"
#include "character.h"
Expand Down Expand Up @@ -413,6 +414,30 @@ activity_handlers::finish_functions = {
{ ACT_STUDY_SPELL, study_spell_finish }
};


namespace io
{
// *INDENT-OFF*
template<>
std::string enum_to_string<butcher_type>( butcher_type data )
{
switch( data ) {
case butcher_type::DISMEMBER: return "DISMEMBER";
case butcher_type::DISSECT: return "DISSECT";
case butcher_type::FIELD_DRESS: return "FIELD_DRESS";
case butcher_type::FULL: return "FULL";
case butcher_type::QUARTER: return "QUARTER";
case butcher_type::QUICK: return "QUICK";
case butcher_type::SKIN: return "SKIN";
case butcher_type::NUM_TYPES: break;
}
debugmsg( "Invalid valid_target" );
abort();
}
// *INDENT-ON*

} // namespace io

bool activity_handlers::resume_for_multi_activities( player &p )
{
if( !p.backlog.empty() ) {
Expand Down Expand Up @@ -583,42 +608,12 @@ static void set_up_butchery( player_activity &act, player &u, butcher_type actio
}
}

const bool big_corpse = corpse.size >= creature_size::medium;
const std::pair<float, requirement_id> butchery_requirements =
corpse.harvest->get_butchery_requirements().get_fastest_requirements( u.crafting_inventory(),
corpse.size, action );

// Requirements for the various types
requirement_id butchery_requirement;
switch( action ) {
case butcher_type::FIELD_DRESS:
case butcher_type::QUARTER:
case butcher_type::SKIN:
case butcher_type::DISMEMBER: {
butchery_requirement = requirement_id( "field_dress" );
break;
}
case butcher_type::DISSECT: {
if( big_corpse ) {
butchery_requirement = requirement_id( "dissect_large" );
} else {
butchery_requirement = requirement_id( "dissect_small" );
}
break;
}
case butcher_type::FULL: {
if( big_corpse ) {
butchery_requirement = requirement_id( "full_butchery_large" );
} else {
butchery_requirement = requirement_id( "butchery_small" );
}
break;
}
case butcher_type::QUICK: {
if( big_corpse ) {
butchery_requirement = requirement_id( "butchery_large" );
} else {
butchery_requirement = requirement_id( "butchery_small" );
}
break;
}
}
const requirement_id butchery_requirement = butchery_requirements.second;

if( !butchery_requirement->can_make_with_inventory(
u.crafting_inventory( u.pos(), PICKUP_RANGE ), is_crafting_component ) ) {
Expand Down Expand Up @@ -712,7 +707,7 @@ static void set_up_butchery( player_activity &act, player &u, butcher_type actio
}
}

act.moves_left = butcher_time_to_cut( u, corpse_item, action );
act.moves_left = butcher_time_to_cut( u, corpse_item, action ) * butchery_requirements.first;

// We have a valid target, so preform the full finish function
// instead of just selecting the next valid target
Expand Down Expand Up @@ -742,6 +737,9 @@ int butcher_time_to_cut( const player &u, const item &corpse_item, const butcher
case creature_size::huge:
time_to_cut = 1800;
break;
case creature_size::num_sizes:
debugmsg( "ERROR: Invalid creature_size on %s", corpse.nname() );
break;
}

// At factor 0, base 100 time_to_cut remains 100. At factor 50, it's 50 , at factor 75 it's 25
Expand Down Expand Up @@ -779,6 +777,9 @@ int butcher_time_to_cut( const player &u, const item &corpse_item, const butcher
case butcher_type::DISSECT:
time_to_cut *= 6;
break;
case butcher_type::NUM_TYPES:
debugmsg( "ERROR: Invalid butcher_type" );
break;
}

if( corpse_item.has_flag( flag_QUARTERED ) ) {
Expand Down Expand Up @@ -1451,6 +1452,9 @@ void activity_handlers::butcher_finish( player_activity *act, player *p )
act->targets.pop_back();
}
break;
case butcher_type::NUM_TYPES:
debugmsg( "ERROR: Invalid butcher_type" );
break;
}

// Ready to move on to the next item, if there is one (for example if multibutchering)
Expand Down
3 changes: 2 additions & 1 deletion src/activity_handlers.h
Expand Up @@ -34,7 +34,8 @@ enum class butcher_type : int {
SKIN, // skinning a corpse
QUARTER, // quarter a corpse
DISMEMBER, // destroy a corpse
DISSECT // dissect a corpse for CBMs
DISSECT, // dissect a corpse for CBMs
NUM_TYPES // always keep at the end, number of butchery types
};

enum class do_activity_reason : int {
Expand Down
108 changes: 108 additions & 0 deletions src/butchery_requirements.cpp
@@ -0,0 +1,108 @@
#include "butchery_requirements.h"

#include "activity_handlers.h"
#include "creature.h"
#include "debug.h"
#include "generic_factory.h"
#include "inventory.h"
#include "requirements.h"

namespace
{
generic_factory<butchery_requirements> butchery_req_factory( "butchery_requirements" );
} // namespace

template<>
const butchery_requirements &string_id<butchery_requirements>::obj() const
{
return butchery_req_factory.obj( *this );
}

template<>
bool string_id<butchery_requirements>::is_valid() const
{
return butchery_req_factory.is_valid( *this );
}

void butchery_requirements::load_butchery_req( const JsonObject &jo, const std::string &src )
{
butchery_req_factory.load( jo, src );
}

const std::vector<butchery_requirements> &butchery_requirements::get_all()
{
return butchery_req_factory.get_all();
}

void butchery_requirements::reset_all()
{
butchery_req_factory.reset();
}

bool butchery_requirements::is_valid() const
{
return butchery_req_factory.is_valid( this->id );
}

void butchery_requirements::load( const JsonObject &jo, const std::string & )
{
mandatory( jo, was_loaded, "id", id );

for( const JsonMember &member : jo.get_object( "requirements" ) ) {
float modifier = std::stof( member.name() );
requirements.emplace( modifier, std::map<creature_size, std::map<butcher_type, requirement_id>> {} );

int critter_size = 1;
for( JsonObject butcher_jo : member.get_array() ) {
if( critter_size == creature_size::num_sizes ) {
debugmsg( "ERROR: %s has too many creature sizes. must have exactly %d",
id.c_str(), creature_size::num_sizes - 1 );
break;
}
std::map<butcher_type, requirement_id> temp;
requirements[modifier].emplace( static_cast<creature_size>( critter_size ),
std::map<butcher_type, requirement_id> {} );

for( int i_butchery_type = 0; i_butchery_type < static_cast<int>( butcher_type::NUM_TYPES );
i_butchery_type++ ) {
butcher_type converted = static_cast<butcher_type>( i_butchery_type );
requirements[modifier][static_cast<creature_size>( critter_size )][converted] =
requirement_id( butcher_jo.get_string( io::enum_to_string( converted ) ) );
}

critter_size++;
}
}
}

void butchery_requirements::check_consistency()
{
for( const butchery_requirements &req : get_all() ) {
for( const auto &size_req : req.requirements ) {
if( size_req.second.size() != creature_size::num_sizes - 1 ) {
debugmsg( "ERROR: %s needs exactly %d entries to cover all creature sizes",
req.id.c_str(), static_cast<int>( creature_size::num_sizes ) - 1 );
}
for( const auto &butcher_req : size_req.second ) {
if( butcher_req.second.size() != static_cast<size_t>( butcher_type::NUM_TYPES ) ) {
debugmsg( "ERROR: %s needs exactly %d entries to cover all butchery types",
req.id.c_str(), static_cast<int>( butcher_type::NUM_TYPES ) );
}
}
}
}
}

std::pair<float, requirement_id> butchery_requirements::get_fastest_requirements(
const inventory &crafting_inv, creature_size size, butcher_type butcher ) const
{
for( auto riter = requirements.rbegin(); riter != requirements.rend(); ++riter ) {
if( riter->second.at( size ).at( butcher )->can_make_with_inventory( crafting_inv,
is_crafting_component ) ) {
return std::make_pair( riter->first, riter->second.at( size ).at( butcher ) );
}
}
// we didn't find anything we could "craft", so return the requirement that's the fastest
const auto first = requirements.begin();
return std::make_pair( first->first, first->second.at( size ).at( butcher ) );
}
45 changes: 45 additions & 0 deletions src/butchery_requirements.h
@@ -0,0 +1,45 @@
#pragma once
#ifndef CATA_SRC_BUTCHERY_REQUIREMENTS_H
#define CATA_SRC_BUTCHERY_REQUIREMENTS_H

#include <map>
#include <string>
#include <vector>

#include "optional.h"
#include "string_id.h"
#include "type_id.h"

class inventory;
class JsonObject;

enum class butcher_type : int;
enum class creature_size : int;

/**
* Contains several requirements, each with a speed modifier.
* The goal is to iterate from highest speed modifier to lowest
* to find the first requirement satisfied to butcher a corpse.
*/
class butchery_requirements
{
public:
bool was_loaded = false;
string_id<butchery_requirements> id;

// tries to find the requirement with the highest speed bonus. if it fails it returns cata::nullopt
std::pair<float, requirement_id> get_fastest_requirements(
const inventory &crafting_inv, creature_size size, butcher_type butcher ) const;

static void load_butchery_req( const JsonObject &jo, const std::string &src );
void load( const JsonObject &jo, const std::string & );
static const std::vector<butchery_requirements> &get_all();
static void check_consistency();
static void reset_all();
bool is_valid() const;
private:
// int is speed bonus
std::map<float, std::map<creature_size, std::map<butcher_type, requirement_id>>> requirements;
};

#endif // CATA_SRC_BUTCHERY_REQUIREMENTS_H
3 changes: 3 additions & 0 deletions src/character.cpp
Expand Up @@ -7608,6 +7608,9 @@ int Character::height() const
return init_height + 50;
case creature_size::huge:
return init_height + 100;
case creature_size::num_sizes:
debugmsg( "ERROR: Character has invalid size class." );
return 0;
}

debugmsg( "Invalid size class" );
Expand Down
6 changes: 6 additions & 0 deletions src/creature.cpp
Expand Up @@ -257,6 +257,9 @@ bool Creature::sees( const Creature &critter ) const
case creature_size::huge:
size_modifier = 0.15f;
break;
case creature_size::num_sizes:
debugmsg( "ERROR: Creature has invalid size class." );
break;
}
const int vision_modifier = 30 - 0.5 * coverage * size_modifier;
if( vision_modifier > 1 ) {
Expand Down Expand Up @@ -482,6 +485,9 @@ int Creature::size_melee_penalty() const
return -10;
case creature_size::huge:
return -20;
case creature_size::num_sizes:
debugmsg( "ERROR: Creature has invalid size class." );
return 0;
}

debugmsg( "Invalid target size %d", get_size() );
Expand Down
4 changes: 3 additions & 1 deletion src/creature.h
Expand Up @@ -65,7 +65,9 @@ enum class creature_size : int {
// Cow
large,
// TAAAANK
huge
huge,
// must always be at the end, is actually number + 1 since we start counting at 1
num_sizes
};

using I = std::underlying_type_t<creature_size>;
Expand Down
4 changes: 4 additions & 0 deletions src/game.cpp
Expand Up @@ -6167,6 +6167,10 @@ void game::print_all_tile_info( const tripoint &lp, const catacurses::window &w_
case creature_size::huge:
size_str = pgettext( "infrared size", "huge" );
break;
case creature_size::num_sizes:
debugmsg( "Creature has invalid size class." );
size_str = "invalid";
break;
}
mvwprintw( w_look, point( 1, ++line ), _( "You see a figure radiating heat." ) );
mvwprintw( w_look, point( 1, ++line ), _( "It is %s in size." ),
Expand Down