247 lines
8.5 KiB
C++
247 lines
8.5 KiB
C++
#include <functional>
|
|
#include <cstddef>
|
|
#include <functional>
|
|
#include <list>
|
|
#include <memory>
|
|
#include <set>
|
|
#include <sstream>
|
|
#include <unordered_map>
|
|
#include <utility>
|
|
#include <vector>
|
|
|
|
#include "avatar.h"
|
|
#include "cata_catch.h"
|
|
#include "inventory.h"
|
|
#include "item.h"
|
|
#include "iuse.h"
|
|
#include "mutation.h"
|
|
#include "pimpl.h"
|
|
#include "profession.h"
|
|
#include "scenario.h"
|
|
#include "string_formatter.h"
|
|
#include "type_id.h"
|
|
#include "visitable.h"
|
|
|
|
static const trait_id trait_ALBINO( "ALBINO" );
|
|
static const trait_id trait_ANTIFRUIT( "ANTIFRUIT" );
|
|
static const trait_id trait_ANTIJUNK( "ANTIJUNK" );
|
|
static const trait_id trait_ANTIWHEAT( "ANTIWHEAT" );
|
|
static const trait_id trait_ASTHMA( "ASTHMA" );
|
|
static const trait_id trait_LACTOSE( "LACTOSE" );
|
|
static const trait_id trait_MEATARIAN( "MEATARIAN" );
|
|
static const trait_id trait_TAIL_FLUFFY( "TAIL_FLUFFY" );
|
|
static const trait_id trait_VEGETARIAN( "VEGETARIAN" );
|
|
static const trait_id trait_WOOLALLERGY( "WOOLALLERGY" );
|
|
|
|
static std::ostream &operator<<( std::ostream &s, const std::vector<trait_id> &v )
|
|
{
|
|
for( const auto &e : v ) {
|
|
s << e.c_str() << " ";
|
|
}
|
|
return s;
|
|
}
|
|
|
|
static std::vector<trait_id> next_subset( const std::vector<trait_id> &set )
|
|
{
|
|
// Doing it this way conveniently returns a vector containing solely set[foo] before
|
|
// it returns any other vectors with set[foo] in it
|
|
static unsigned bitset = 0;
|
|
std::vector<trait_id> ret;
|
|
|
|
++bitset;
|
|
// Check each bit position for a match
|
|
for( size_t idx = 0; idx < set.size(); idx++ ) {
|
|
if( bitset & ( 1 << idx ) ) {
|
|
ret.push_back( set[idx] );
|
|
}
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
static bool try_set_traits( const std::vector<trait_id> &traits )
|
|
{
|
|
avatar &player_character = get_avatar();
|
|
player_character.clear_mutations();
|
|
player_character.add_traits(); // mandatory prof/scen traits
|
|
std::vector<trait_id> oked_traits;
|
|
for( const trait_id &tr : traits ) {
|
|
if( player_character.has_conflicting_trait( tr ) ||
|
|
!get_scenario()->traitquery( tr ) ) {
|
|
return false;
|
|
} else if( !player_character.has_trait( tr ) ) {
|
|
oked_traits.push_back( tr );
|
|
}
|
|
}
|
|
player_character.set_mutations( oked_traits );
|
|
return true;
|
|
}
|
|
|
|
static avatar get_sanitized_player()
|
|
{
|
|
// You'd think that this hp stuff would be in the c'tor...
|
|
avatar ret = avatar();
|
|
ret.set_body();
|
|
ret.recalc_hp();
|
|
|
|
// Set these insanely high so can_eat doesn't return TOO_FULL
|
|
ret.set_hunger( 10000 );
|
|
ret.set_thirst( 10000 );
|
|
return ret;
|
|
}
|
|
|
|
static int get_item_count( const std::set<const item *> &items )
|
|
{
|
|
int sum = 0;
|
|
for( const item *it : items ) {
|
|
sum += it->count();
|
|
}
|
|
return sum;
|
|
}
|
|
|
|
struct failure {
|
|
string_id<profession> prof;
|
|
std::vector<trait_id> mut;
|
|
itype_id item_name;
|
|
std::string reason;
|
|
};
|
|
|
|
namespace std
|
|
{
|
|
template<>
|
|
struct less<failure> {
|
|
bool operator()( const failure &lhs, const failure &rhs ) const {
|
|
return lhs.prof < rhs.prof;
|
|
}
|
|
};
|
|
} // namespace std
|
|
|
|
// TODO: According to profiling (interrupt, backtrace, wait a few seconds, repeat) with a sample
|
|
// size of 20, 70% of the time is due to the call to Character::set_mutation in try_set_traits.
|
|
// When the mutation stuff isn't commented out, the test takes 110 minutes (not a typo)!
|
|
|
|
/**
|
|
* Disabled temporarily because 3169 profession combinations do not work and need to be fixed in json
|
|
*/
|
|
TEST_CASE( "starting_items", "[slow]" )
|
|
{
|
|
// Every starting trait that interferes with food/clothing
|
|
const std::vector<trait_id> mutations = {
|
|
trait_ALBINO,
|
|
trait_ANTIFRUIT,
|
|
trait_ANTIJUNK,
|
|
trait_ANTIWHEAT,
|
|
//trait_id( "ARM_TENTACLES" ),
|
|
//trait_id( "BEAK" ),
|
|
//trait_id( "CARNIVORE" ),
|
|
//trait_id( "HERBIVORE" ),
|
|
//trait_id( "HOOVES" ),
|
|
trait_LACTOSE,
|
|
//trait_id( "LEG_TENTACLES" ),
|
|
trait_MEATARIAN,
|
|
trait_ASTHMA,
|
|
//trait_id( "RAP_TALONS" ),
|
|
//trait_id( "TAIL_FLUFFY" ),
|
|
//trait_id( "TAIL_LONG" ),
|
|
trait_VEGETARIAN,
|
|
trait_WOOLALLERGY
|
|
};
|
|
// Prof/scen combinations that need to be checked.
|
|
std::unordered_map<const scenario *, std::vector<string_id<profession>>> scen_prof_combos;
|
|
for( const auto &id : scenario::generic()->permitted_professions() ) {
|
|
scen_prof_combos[scenario::generic()].push_back( id );
|
|
}
|
|
|
|
std::set<failure> failures;
|
|
|
|
avatar &player_character = get_avatar();
|
|
player_character = get_sanitized_player();
|
|
// Avoid false positives from ingredients like salt and cornmeal.
|
|
const avatar control = get_sanitized_player();
|
|
|
|
std::vector<trait_id> traits = next_subset( mutations );
|
|
for( ; !traits.empty(); traits = next_subset( mutations ) ) {
|
|
CAPTURE( traits );
|
|
for( const auto &pair : scen_prof_combos ) {
|
|
set_scenario( pair.first );
|
|
INFO( "Scenario = " + pair.first->ident().str() );
|
|
for( const string_id<profession> &prof : pair.second ) {
|
|
CAPTURE( prof );
|
|
player_character.prof = &prof.obj();
|
|
if( !try_set_traits( traits ) ) {
|
|
continue; // Trait conflict: this prof/scen/trait combo is impossible to attain
|
|
}
|
|
for( int i = 0; i < 2; i++ ) {
|
|
player_character.worn.clear();
|
|
player_character.remove_weapon();
|
|
player_character.inv->clear();
|
|
player_character.calc_encumbrance();
|
|
player_character.male = i == 0;
|
|
|
|
player_character.add_profession_items();
|
|
std::set<const item *> items_visited;
|
|
const auto visitable_counter = [&items_visited]( const item * it, auto ) {
|
|
items_visited.emplace( it );
|
|
return VisitResponse::NEXT;
|
|
};
|
|
player_character.visit_items( visitable_counter );
|
|
player_character.inv->visit_items( visitable_counter );
|
|
const int num_items_pre_migration = get_item_count( items_visited );
|
|
items_visited.clear();
|
|
|
|
player_character.migrate_items_to_storage( true );
|
|
player_character.visit_items( visitable_counter );
|
|
const int num_items_post_migration = get_item_count( items_visited );
|
|
items_visited.clear();
|
|
|
|
if( num_items_pre_migration != num_items_post_migration ) {
|
|
failure cur_fail;
|
|
cur_fail.prof = player_character.prof->ident();
|
|
cur_fail.mut = player_character.get_mutations();
|
|
cur_fail.reason = string_format( "does not have enough space to store all items." );
|
|
|
|
failures.insert( cur_fail );
|
|
}
|
|
CAPTURE( player_character.prof->ident().c_str() );
|
|
CHECK( num_items_pre_migration == num_items_post_migration );
|
|
} // all genders
|
|
} // all profs
|
|
} // all scens
|
|
}
|
|
std::stringstream failure_messages;
|
|
for( const failure &f : failures ) {
|
|
failure_messages << f.prof.c_str() << " " << f.mut <<
|
|
" " << f.item_name.str() << ": " << f.reason << "\n";
|
|
}
|
|
INFO( failure_messages.str() );
|
|
REQUIRE( failures.empty() );
|
|
}
|
|
|
|
TEST_CASE( "Generated character with category mutations", "[mutation]" )
|
|
{
|
|
REQUIRE( !trait_TAIL_FLUFFY.obj().category.empty() );
|
|
avatar u = get_sanitized_player();
|
|
REQUIRE( u.get_mutations().empty() );
|
|
REQUIRE( u.get_base_traits().empty() );
|
|
REQUIRE( u.mutation_category_level.empty() );
|
|
|
|
SECTION( "Mutations have category levels" ) {
|
|
u.toggle_trait_deps( trait_TAIL_FLUFFY );
|
|
CHECK( u.has_trait( trait_TAIL_FLUFFY ) );
|
|
CHECK( !u.get_mutations().empty() );
|
|
CHECK( u.get_base_traits().empty() );
|
|
CHECK( !u.mutation_category_level.empty() );
|
|
u.toggle_trait_deps( trait_TAIL_FLUFFY );
|
|
CHECK( !u.has_trait( trait_TAIL_FLUFFY ) );
|
|
CHECK( u.get_mutations().empty() );
|
|
CHECK( u.get_base_traits().empty() );
|
|
CHECK( u.mutation_category_level.empty() );
|
|
}
|
|
|
|
SECTION( "Category mutations can be removed" ) {
|
|
u.toggle_trait_deps( trait_TAIL_FLUFFY );
|
|
CHECK( u.has_trait( trait_TAIL_FLUFFY ) );
|
|
u.remove_mutation( trait_TAIL_FLUFFY );
|
|
CHECK( !u.has_trait( trait_TAIL_FLUFFY ) );
|
|
}
|
|
}
|