Skip to content

Commit

Permalink
Substitutes, tests
Browse files Browse the repository at this point in the history
  • Loading branch information
Venera3 committed Apr 13, 2024
1 parent 0095b30 commit d92c87e
Show file tree
Hide file tree
Showing 7 changed files with 127 additions and 21 deletions.
Original file line number Diff line number Diff line change
@@ -1,4 +1,9 @@
[
{
"type": "effect_on_condition",
"id": "eoc_debug_mutate",
"effect": [ { "u_mutate": 0 } ]
},
{
"type": "effect_on_condition",
"id": "mut_eyestalk",
Expand Down
73 changes: 53 additions & 20 deletions data/json/mutations/mutations.json
Original file line number Diff line number Diff line change
Expand Up @@ -5211,6 +5211,7 @@
"prereqs": [ "WINGS_STUB" ],
"prereqs2": [ "HOLLOW_BONES" ],
"threshreq": [ "THRESH_BIRD" ],
"strict_threshreq": true,
"category": [ "BIRD" ],
"restricts_gear": [ "arm_l", "arm_r", "hand_l", "hand_r" ],
"flags": [ "WINGS_2", "WING_GLIDE", "ARM_WINGS" ]
Expand Down Expand Up @@ -8126,7 +8127,8 @@
"description": "You sometimes look back on your days before your tail came in. But you're better now.",
"valid": false,
"purifiable": false,
"threshold": true
"threshold": true,
"threshold_substitutes": [ "THRESH_CHIMERA" ]
},
{
"type": "mutation",
Expand All @@ -8137,7 +8139,8 @@
"flags": [ "INVERTEBRATEBLOOD" ],
"valid": false,
"purifiable": false,
"threshold": true
"threshold": true,
"threshold_substitutes": [ "THRESH_CHIMERA" ]
},
{
"type": "mutation",
Expand All @@ -8147,7 +8150,8 @@
"description": "You're sure you'll fly someday. In the meantime, there are still nests to build.",
"valid": false,
"purifiable": false,
"threshold": true
"threshold": true,
"threshold_substitutes": [ "THRESH_CHIMERA" ]
},
{
"type": "mutation",
Expand All @@ -8157,7 +8161,8 @@
"description": "Ninety percent of the planet, and it's yours to explore. And colonize. And enjoy. What was that about a surface?",
"valid": false,
"purifiable": false,
"threshold": true
"threshold": true,
"threshold_substitutes": [ "THRESH_CHIMERA" ]
},
{
"type": "mutation",
Expand All @@ -8167,7 +8172,8 @@
"description": "It's about time you grew out. Now that you've matured, it is time to make something of yourself.",
"valid": false,
"purifiable": false,
"threshold": true
"threshold": true,
"threshold_substitutes": [ "THRESH_CHIMERA" ]
},
{
"type": "mutation",
Expand All @@ -8177,7 +8183,8 @@
"description": "Stalking prey, eating well, and lying in the sun. Mmm, all you could ever desire.",
"valid": false,
"purifiable": false,
"threshold": true
"threshold": true,
"threshold_substitutes": [ "THRESH_CHIMERA" ]
},
{
"type": "mutation",
Expand All @@ -8187,7 +8194,8 @@
"description": "You're the perfect candidate to lead a pack.",
"valid": false,
"purifiable": false,
"threshold": true
"threshold": true,
"threshold_substitutes": [ "THRESH_CHIMERA" ]
},
{
"type": "mutation",
Expand All @@ -8197,7 +8205,8 @@
"description": "So the humans died; what's the worry? Now they won't ruin the woods.",
"valid": false,
"purifiable": false,
"threshold": true
"threshold": true,
"threshold_substitutes": [ "THRESH_CHIMERA" ]
},
{
"type": "mutation",
Expand All @@ -8207,7 +8216,8 @@
"description": "Civilization collapsed? Great! You and your kin will never have to worry about a slaughterhouse again.",
"valid": false,
"purifiable": false,
"threshold": true
"threshold": true,
"threshold_substitutes": [ "THRESH_CHIMERA" ]
},
{
"type": "mutation",
Expand All @@ -8218,7 +8228,8 @@
"flags": [ "INSECTBLOOD" ],
"valid": false,
"purifiable": false,
"threshold": true
"threshold": true,
"threshold_substitutes": [ "THRESH_CHIMERA" ]
},
{
"type": "mutation",
Expand All @@ -8229,7 +8240,8 @@
"flags": [ "PLANTBLOOD" ],
"valid": false,
"purifiable": false,
"threshold": true
"threshold": true,
"threshold_substitutes": [ "THRESH_CHIMERA" ]
},
{
"type": "mutation",
Expand All @@ -8249,7 +8261,8 @@
"description": "Not much point to rebuilding up in that horribly bright, roofless wasteland. Now that you've become accustomed to your new digs, there's the beginning of a great empire right here, underground.",
"valid": false,
"purifiable": false,
"threshold": true
"threshold": true,
"threshold_substitutes": [ "THRESH_CHIMERA" ]
},
{
"type": "mutation",
Expand All @@ -8260,7 +8273,8 @@
"flags": [ "INVERTEBRATEBLOOD" ],
"valid": false,
"purifiable": false,
"threshold": true
"threshold": true,
"threshold_substitutes": [ "THRESH_CHIMERA" ]
},
{
"type": "mutation",
Expand All @@ -8271,7 +8285,8 @@
"flags": [ "INSECTBLOOD" ],
"valid": false,
"purifiable": false,
"threshold": true
"threshold": true,
"threshold_substitutes": [ "THRESH_CHIMERA" ]
},
{
"type": "mutation",
Expand All @@ -8281,7 +8296,8 @@
"description": "Hey. Civilization fell. You're still around. 'Rat' just isn't respectful.",
"valid": false,
"purifiable": false,
"threshold": true
"threshold": true,
"threshold_substitutes": [ "THRESH_CHIMERA" ]
},
{
"type": "mutation",
Expand Down Expand Up @@ -8319,6 +8335,7 @@
"name": { "str": "Chaos" },
"points": 1,
"description": "You can't tell what you are anymore. Everything and yet nothing, like you weren't meant to exist. But you do, and you're a force, no matter what happens.",
"//": "Acceptable substitute for all lines except the human-based ones and slime - the former are more controlled, the latter will diverge too much in anatomy.",
"valid": false,
"purifiable": false,
"threshold": true
Expand All @@ -8331,7 +8348,8 @@
"description": "The chance to undo not one but TWO extinction events. You're confident you'll do fine.",
"valid": false,
"purifiable": false,
"threshold": true
"threshold": true,
"threshold_substitutes": [ "THRESH_CHIMERA" ]
},
{
"type": "mutation",
Expand All @@ -8341,7 +8359,8 @@
"description": "A perfect ambush predator, if only there were less competition.",
"valid": false,
"purifiable": false,
"threshold": true
"threshold": true,
"threshold_substitutes": [ "THRESH_CHIMERA" ]
},
{
"type": "mutation",
Expand All @@ -8351,7 +8370,8 @@
"description": "So much food, everywhere! And nobody's even guarding it anymore! These are good times.",
"valid": false,
"purifiable": false,
"threshold": true
"threshold": true,
"threshold_substitutes": [ "THRESH_CHIMERA" ]
},
{
"type": "mutation",
Expand Down Expand Up @@ -8386,7 +8406,8 @@
"flags": [ "INVERTEBRATEBLOOD" ],
"valid": false,
"purifiable": false,
"threshold": true
"threshold": true,
"threshold_substitutes": [ "THRESH_CHIMERA" ]
},
{
"type": "mutation",
Expand All @@ -8396,7 +8417,8 @@
"description": "You long for a colony, the cover of darkness, and a meal on the wing.",
"valid": false,
"purifiable": false,
"threshold": true
"threshold": true,
"threshold_substitutes": [ "THRESH_CHIMERA" ]
},
{
"type": "mutation",
Expand Down Expand Up @@ -8884,6 +8906,17 @@
"enchantments": [ "ENCH_DEBUG_BIG_HEAD" ],
"debug": true
},
{
"type": "mutation",
"id": "DEBUG_MUTATE",
"name": { "str": "Debug Genetic Instability" },
"points": 99,
"valid": false,
"description": "You just can't wait to turn into a bug! Triggers a mutation using the normal mutation rules (requiring primer but no catalyst) every five seconds.",
"debug": true,
"processed_eocs": [ "eoc_debug_mutate" ],
"time": "5 s"
},
{
"type": "mutation",
"id": "DEBUG_NIGHTVISION",
Expand Down
4 changes: 3 additions & 1 deletion doc/MUTATIONS.md
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,9 @@ Note that **all new traits that can be obtained through mutation must be purifia
"profession": true, // Trait is a starting profession special trait (default: false).
"debug": false, // Trait is for debug purposes (default: false).
"dummy": false, // Dummy mutations are special; they're not gained through normal mutating, and will instead be targeted for the purposes of removing conflicting mutations
"threshold": false //True if it's a threshold itself, and shouldn't be obtained *easily*.
"threshold": false, //True if it's a threshold itself, and shouldn't be obtained *easily*. Disallows mutating this trait directly
"threshold_substitutes": [ "FOO", "BAR" ], // The listed traits are accepted in place of this threshold trait for the purposes of gaining post-threshold mutations
"strict_thresreq": false, // This trait needs an *exact* threshold match (ie. ignores threshold substitutions)
"player_display": true, // Trait is displayed in the `@` player display menu and mutations screen.
"vanity": false, // Trait can be changed any time with no cost, like hair, eye color and skin color.
"variants": [ // Cosmetic variants of this mutation.
Expand Down
13 changes: 13 additions & 0 deletions src/mutation.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1636,6 +1636,19 @@ bool Character::mutate_towards( const trait_id &mut, const mutation_category_id
threshreq[i].c_str() );
c_has_threshreq = true;
}
for( const trait_id &subst : threshreq[i]->threshold_substitutes ) {
if( has_trait( subst ) ) {
add_msg_debug( debugmode::DF_MUTATION, "mutate_towards: substitute threshold %s found",
subst.c_str() );
if( mdata.strict_threshreq ) {
add_msg_debug( debugmode::DF_MUTATION,
"mutate_towards: ...but no threshold substitutions allowed for trait %s",
subst.c_str(), mdata.name() );

Check failure on line 1646 in src/mutation.cpp

View workflow job for this annotation

GitHub Actions / build (src)

ellipsis preferred over three dots. [cata-text-style,-warnings-as-errors]
continue;
}
c_has_threshreq = true;
}
}
}

// No crossing The Threshold by simply not having it
Expand Down
4 changes: 4 additions & 0 deletions src/mutation.h
Original file line number Diff line number Diff line change
Expand Up @@ -179,6 +179,10 @@ struct mutation_branch {
bool purifiable = false;
// True if it's a threshold itself, and shouldn't be obtained *easily* (False by default).
bool threshold = false;
// Other threshold traits that are taken as acceptable replacements for this threshold
std::vector<trait_id> threshold_substitutes;
// Disallow threshold substitution for this trait in particular
bool strict_threshreq = false;
// True if this is a trait associated with professional training/experience, so profession/quest ONLY.
bool profession = false;
// True if the mutation is obtained through the debug menu
Expand Down
2 changes: 2 additions & 0 deletions src/mutation_data.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -367,6 +367,8 @@ void mutation_branch::load( const JsonObject &jo, const std::string &src )
}

optional( jo, was_loaded, "threshold", threshold, false );
optional( jo, was_loaded, "threshold_substitutes", threshold_substitutes );
optional( jo, was_loaded, "strict_threshreq", strict_threshreq );
optional( jo, was_loaded, "profession", profession, false );
optional( jo, was_loaded, "debug", debug, false );
optional( jo, was_loaded, "player_display", player_display, true );
Expand Down
47 changes: 47 additions & 0 deletions tests/mutation_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ static const effect_on_condition_id effect_on_condition_changing_mutate2( "chang
static const morale_type morale_perm_debug( "morale_perm_debug" );

static const mutation_category_id mutation_category_ALPHA( "ALPHA" );
static const mutation_category_id mutation_category_BIRD( "BIRD" );
static const mutation_category_id mutation_category_CHIMERA( "CHIMERA" );
static const mutation_category_id mutation_category_FELINE( "FELINE" );
static const mutation_category_id mutation_category_HUMAN( "HUMAN" );
Expand All @@ -26,6 +27,8 @@ static const mutation_category_id mutation_category_RAPTOR( "RAPTOR" );
static const mutation_category_id mutation_category_REMOVAL_TEST( "REMOVAL_TEST" );
static const mutation_category_id mutation_category_TROGLOBITE( "TROGLOBITE" );

static const trait_id trait_BEAK( "BEAK" );
static const trait_id trait_BEAK_PECK( "BEAK_PECK" );
static const trait_id trait_EAGLEEYED( "EAGLEEYED" );
static const trait_id trait_FELINE_EARS( "FELINE_EARS" );
static const trait_id trait_GOURMAND( "GOURMAND" );
Expand All @@ -34,6 +37,7 @@ static const trait_id trait_QUICK( "QUICK" );
static const trait_id trait_SMELLY( "SMELLY" );
static const trait_id trait_STR_UP( "STR_UP" );
static const trait_id trait_STR_UP_2( "STR_UP_2" );
static const trait_id trait_STR_ALPHA( "STR_ALPHA" );
static const trait_id trait_TEST_OVERMAP_SIGHT_5( "TEST_OVERMAP_SIGHT_5" );
static const trait_id trait_TEST_OVERMAP_SIGHT_MINUS_10( "TEST_OVERMAP_SIGHT_MINUS_10" );
static const trait_id trait_TEST_REMOVAL_0( "TEST_REMOVAL_0" );
Expand All @@ -42,7 +46,11 @@ static const trait_id trait_TEST_TRIGGER( "TEST_TRIGGER" );
static const trait_id trait_TEST_TRIGGER_2( "TEST_TRIGGER_2" );
static const trait_id trait_TEST_TRIGGER_2_active( "TEST_TRIGGER_2_active" );
static const trait_id trait_TEST_TRIGGER_active( "TEST_TRIGGER_active" );
static const trait_id trait_THRESH_ALPHA( "THRESH_ALPHA" );
static const trait_id trait_THRESH_BIRD( "THRESH_BIRD" );
static const trait_id trait_THRESH_CHIMERA( "THRESH_CHIMERA" );
static const trait_id trait_UGLY( "UGLY" );
static const trait_id trait_WINGS_BIRD( "WINGS_BIRD" );

static const vitamin_id vitamin_mutagen( "mutagen" );
static const vitamin_id vitamin_mutagen_human( "mutagen_human" );
Expand Down Expand Up @@ -723,3 +731,42 @@ TEST_CASE( "The_mutation_flags_are_associated_to_the_corresponding_base_mutation
verify_mutation_flag( dummy, "COLDBLOOD4", "ECTOTHERM" );
}

// Test that threshold substitutes work
// Tested using Chimera for the mainline use case
TEST_CASE( "Threshold_substitutions", "[mutations]" )
{
Character &dummy = get_player_character();
clear_avatar();
// Check our assumptions
const std::vector<trait_id> bird_subst = trait_THRESH_BIRD->threshold_substitutes;
const std::vector<trait_id> alpha_subst = trait_THRESH_ALPHA->threshold_substitutes;
REQUIRE( std::find( bird_subst.begin(), bird_subst.end(),
trait_THRESH_CHIMERA ) != bird_subst.end() );
REQUIRE( std::find( alpha_subst.begin(), alpha_subst.end(),
trait_THRESH_CHIMERA ) == alpha_subst.end() );
REQUIRE( !trait_BEAK_PECK->threshreq.empty() );
REQUIRE( !trait_WINGS_BIRD->threshreq.empty() );
REQUIRE( !trait_BEAK_PECK->strict_threshreq );
REQUIRE( trait_WINGS_BIRD->strict_threshreq );

// Character tries to gain post-thresh traits
for( int i = 0; i < 10; i++ ) {
dummy.mutate_towards( trait_BEAK_PECK, mutation_category_BIRD, nullptr, false, false );
dummy.mutate_towards( trait_WINGS_BIRD, mutation_category_BIRD, nullptr, false, false );
dummy.mutate_towards( trait_STR_ALPHA, mutation_category_ALPHA, nullptr, false, false );
}
// We didn't gain any, filled up on prereqs though
CHECK( !dummy.has_trait( trait_BEAK_PECK ) );
CHECK( !dummy.has_trait( trait_WINGS_BIRD ) );
CHECK( !dummy.has_trait( trait_STR_ALPHA ) );
CHECK( dummy.has_trait( trait_BEAK ) );
// After gaining Chimera we can mutate a fancy beak, but no wings or Alpha traits
dummy.set_mutation( trait_THRESH_CHIMERA );
dummy.mutate_towards( trait_BEAK_PECK, mutation_category_BIRD, nullptr, false, false );
dummy.mutate_towards( trait_WINGS_BIRD, mutation_category_BIRD, nullptr, false, false );
dummy.mutate_towards( trait_STR_ALPHA, mutation_category_ALPHA, nullptr, false, false );
CHECK( dummy.has_trait( trait_BEAK_PECK ) );
CHECK( !dummy.has_trait( trait_WINGS_BIRD ) );
CHECK( !dummy.has_trait( trait_STR_ALPHA ) );
}

0 comments on commit d92c87e

Please sign in to comment.