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

Transforming a mutation into another on activation #39523

Merged
merged 14 commits into from
Apr 17, 2020
28 changes: 24 additions & 4 deletions data/json/mutations/mutations.json
Original file line number Diff line number Diff line change
Expand Up @@ -2263,6 +2263,7 @@
"points": 2,
"visibility": 8,
"ugliness": 4,
"pierce_dmg_bonus": 2,
"description": "Your skin is covered in small, woody thorns. Whenever an unarmed opponent strikes a part of your body that is not covered by clothing, they will receive minor damage. Your punches may also deal extra damage.",
"prereqs": [ "BARK" ],
"category": [ "PLANT" ]
Expand Down Expand Up @@ -2450,6 +2451,7 @@
"name": { "str": "Long Fingernails" },
"points": 1,
"visibility": 1,
"pierce_dmg_bonus": 0.5,
"description": "Your fingernails are long and sharp. If you aren't wearing gloves, your unarmed attacks deal a minor amount of cutting damage.",
"types": [ "CLAWS" ],
"changes_to": [ "CLAWS", "TALONS" ],
Expand All @@ -2464,6 +2466,8 @@
"visibility": 3,
"ugliness": 2,
"cut_dmg_bonus": 3,
"pierce_dmg_bonus": 3,
"butchering_quality": 4,
"description": "You have claws on the ends of your fingers. If you aren't wearing gloves, your unarmed attacks deal a minor amount of cutting damage.",
"types": [ "CLAWS" ],
"prereqs": [ "NAILS" ],
Expand All @@ -2479,6 +2483,7 @@
"visibility": 3,
"ugliness": 4,
"cut_dmg_bonus": 1,
"butchering_quality": 4,
"flags": [ "UNARMED_BONUS" ],
"description": "Your claws have grown tougher and slightly gnarled.",
"types": [ "CLAWS" ],
Expand All @@ -2497,6 +2502,8 @@
"valid": false,
"purifiable": false,
"cut_dmg_bonus": 1,
"pierce_dmg_bonus": 3,
"butchering_quality": 8,
"flags": [ "UNARMED_BONUS" ],
"description": "Your paws are bone, muscle, and claw with a thin layer of skin and fur. They might as well be made of stainless steel.",
"types": [ "CLAWS" ],
Expand All @@ -2511,15 +2518,26 @@
"id": "CLAWS_RETRACT",
"name": { "str": "Retractable Claws" },
"points": 2,
"ugliness": 1,
"cut_dmg_bonus": 3,
"flags": [ "NEED_ACTIVE_TO_MELEE" ],
"description": "You have claws on the ends of your fingers, and can extend or retract them as desired. Gloves will still get in the way, though.",
"types": [ "CLAWS" ],
"prereqs": [ "CLAWS" ],
"cancels": [ "ARM_TENTACLES", "ARM_TENTACLES_4", "ARM_TENTACLES_8" ],
"category": [ "FELINE" ],
"active": true,
"transform": { "target": "CLAWS_RETRACT_active", "msg_transform": "You extend your claws.", "active": false, "moves": 10 },
"cost": 0
},
{
"type": "mutation",
"id": "CLAWS_RETRACT_active",
"name": { "str": "Extended Claws" },
"copy-from": "CLAWS_RETRACT",
"valid": false,
"ugliness": 1,
"cut_dmg_bonus": 3,
"pierce_dmg_bonus": 3,
"butchering_quality": 4,
"description": "Sharp claws are exten from the end of your fingers.",
"transform": { "target": "CLAWS_RETRACT", "msg_transform": "You retract your claws.", "active": false, "moves": 10 },
"cost": 0
},
{
Expand Down Expand Up @@ -2555,6 +2573,7 @@
"visibility": 4,
"ugliness": 3,
"cut_dmg_bonus": 3,
"butchering_quality": 4,
"flags": [ "UNARMED_BONUS" ],
"mixed_effect": true,
"restricts_gear": [ "HAND_L", "HAND_R" ],
Expand Down Expand Up @@ -3633,6 +3652,7 @@
"points": 1,
"visibility": 8,
"ugliness": 6,
"butchering_quality": 4,
"mixed_effect": true,
"description": "A set of insect-like mandibles have grown around your mouth. They allow you to eat faster and provide a slicing unarmed attack, but prevent wearing mouthwear. Slightly reduces wet effects.",
"types": [ "TEETH", "MUZZLE" ],
Expand Down
1 change: 0 additions & 1 deletion doc/JSON_FLAGS.md
Original file line number Diff line number Diff line change
Expand Up @@ -1092,7 +1092,6 @@ Also see `monster_attacks.json` for more special attacks, for example, impale an
#### Flags

- ```UNARMED_BONUS``` You get a bonus to unarmed bash and cut damage equal to unarmed_skill/2 up to 4.
- ```NEED_ACTIVE_TO_MELEE``` This mutation gives bonus to unarmed melee only if it's active.
- ```NO_DISEASE``` This mutation grants immunity to diseases.
- ```NO_THIRST``` Your thirst is not modified by food or drinks.
- ```NO_RADIATION``` This mutation grants immunity to radiations.
Expand Down
7 changes: 7 additions & 0 deletions doc/JSON_INFO.md
Original file line number Diff line number Diff line change
Expand Up @@ -1239,7 +1239,9 @@ an `event_statistic`. For example:
"visibility": 0, // Visibility of the trait for purposes of NPC interaction (default: 0)
"ugliness": 0, // Ugliness of the trait for purposes of NPC interaction (default: 0)
"cut_dmg_bonus": 3, // Bonus to unarmed cut damage (default: 0)
"pierce_dmg_bonus": 3, // Bonus to unarmed pierce damage (default: 0.0)
"bash_dmg_bonus": 3, // Bonus to unarmed bash damage (default: 0)
"butchering_quality": 4, // Butchering quality of this mutations (default: 0)
"rand_cut_bonus": { "min": 2, "max": 3 }, // Random bonus to unarmed cut damage between min and max.
"rand_bash_bonus": { "min": 2, "max": 3 }, // Random bonus to unarmed bash damage between min and max.
"bodytemp_modifiers" : [100, 150], // Range of additional bodytemp units (these units are described in 'weather.h'. First value is used if the person is already overheated, second one if it's not.
Expand Down Expand Up @@ -1320,6 +1322,11 @@ an `event_statistic`. For example:
"healing_awake": 1.0, // Healing rate per turn while awake.
"healing_resting": 0.5, // Healing rate per turn while resting.
"mending_modifier": 1.2 // Multiplier on how fast your limbs mend - This value would make your limbs mend 20% faster
"transform": { "target": "BIOLUM1", // Trait_id of the mutation this one will transfomr into
"msg_transform": "You turn your photophore OFF.", // message displayed upon transformation
"active": false , // Will the target mutation start powered ( turn ON ).
"moves": 100 // how many moves this costs. (default: 0)
}
```

### Vehicle Groups
Expand Down
2 changes: 2 additions & 0 deletions src/character.h
Original file line number Diff line number Diff line change
Expand Up @@ -658,6 +658,8 @@ class Character : public Creature, public visitable<Character>
/** Add or removes a mutation on the player, but does not trigger mutation loss/gain effects. */
void set_mutation( const trait_id & );
void unset_mutation( const trait_id & );
/**Unset switched mutation and set target mutation instead*/
void switch_mutations( const trait_id &switched, const trait_id &target, bool start_powered );

// Trigger and disable mutations that can be so toggled.
void activate_mutation( const trait_id &mutation );
Expand Down
24 changes: 7 additions & 17 deletions src/melee.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -91,9 +91,6 @@ static const efftype_id effect_narcosis( "narcosis" );
static const efftype_id effect_poison( "poison" );
static const efftype_id effect_stunned( "stunned" );

static const trait_id trait_CLAWS( "CLAWS" );
static const trait_id trait_CLAWS_RETRACT( "CLAWS_RETRACT" );
static const trait_id trait_CLAWS_ST( "CLAWS_ST" );
static const trait_id trait_CLAWS_TENTACLE( "CLAWS_TENTACLE" );
static const trait_id trait_CLUMSY( "CLUMSY" );
static const trait_id trait_DEBUG_NIGHTVISION( "DEBUG_NIGHTVISION" );
Expand Down Expand Up @@ -943,6 +940,7 @@ void player::roll_cut_damage( bool crit, damage_instance &di, bool average, cons
if( has_bionic( bionic_id( "bio_razors" ) ) ) {
per_hand += 2;
}

for( const trait_id &mut : get_mutations() ) {
if( mut->flags.count( "NEED_ACTIVE_TO_MELEE" ) > 0 && !has_active_mutation( mut ) ) {
continue;
Expand Down Expand Up @@ -1010,27 +1008,19 @@ void player::roll_stab_damage( bool crit, damage_instance &di, bool /*average*/,
weap.is_null();
if( left_empty || right_empty ) {
float per_hand = 0.0f;
if( has_trait( trait_CLAWS ) || has_active_mutation( trait_CLAWS_RETRACT ) ) {
per_hand += 3;
}

if( has_trait( trait_NAILS ) ) {
per_hand += .5;
}
for( const trait_id &mut : get_mutations() ) {
per_hand += mut->pierce_dmg_bonus;

if( has_bionic( bionic_id( "bio_razors" ) ) ) {
per_hand += 2;
if( mut->flags.count( "UNARMED_BONUS" ) > 0 && cut_bonus > 0 ) {
per_hand += std::min( unarmed_skill / 2, 4 );
}
}

if( has_trait( trait_THORNS ) ) {
if( has_bionic( bionic_id( "bio_razors" ) ) ) {
per_hand += 2;
}

if( has_trait( trait_CLAWS_ST ) ) {
/** @EFFECT_UNARMED increases stabbing damage with CLAWS_ST */
per_hand += 3 + unarmed_skill / 2.0;
}

cut_dam += per_hand; // First hand
if( left_empty && right_empty ) {
// Second hand
Expand Down
24 changes: 24 additions & 0 deletions src/mutation.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -171,6 +171,17 @@ void Character::unset_mutation( const trait_id &trait_ )
reset_encumbrance();
}

void Character::switch_mutations( const trait_id &switched, const trait_id &target,
bool start_powered )
{
unset_mutation( switched );
mutation_loss_effect( switched );

set_mutation( target );
my_mutations[target].powered = start_powered;
mutation_effect( target );
}

int Character::get_mod( const trait_id &mut, const std::string &arg ) const
{
auto &mod_data = mut->mods;
Expand Down Expand Up @@ -494,6 +505,13 @@ void Character::activate_mutation( const trait_id &mut )
recalc_sight_limits();
}

if( mdata.transform ) {
const cata::value_ptr<mut_transform> trans = mdata.transform;
mod_moves( - trans->moves );
switch_mutations( mut, trans->target, trans->active );
return;
}

if( mut == trait_WEB_WEAVER ) {
g->m.add_field( pos(), fd_web, 1 );
add_msg_if_player( _( "You start spinning web with your spinnerets!" ) );
Expand Down Expand Up @@ -619,6 +637,12 @@ void Character::deactivate_mutation( const trait_id &mut )
// Handle stat changes from deactivation
apply_mods( mut, false );
recalc_sight_limits();
const mutation_branch &mdata = mut.obj();
if( mdata.transform ) {
const cata::value_ptr<mut_transform> trans = mdata.transform;
mod_moves( -trans->moves );
switch_mutations( mut, trans->target, trans->active );
}
}

trait_id Character::trait_by_invlet( const int ch ) const
Expand Down
19 changes: 19 additions & 0 deletions src/mutation.h
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,20 @@ struct mut_attack {
bool hardcoded_effect = false;
};

struct mut_transform {

trait_id target;

/** displayed if player sees transformation with %s replaced by item name */
translation msg_transform;
/** used to set the active property of the transformed @ref target */
bool active = false;
/** subtracted from @ref Creature::moves when transformation is successful */
int moves = 0;
mut_transform();
bool load( const JsonObject &jsobj, const std::string &member );
};

struct mutation_branch {
trait_id id;
bool was_loaded = false;
Expand Down Expand Up @@ -131,6 +145,7 @@ struct mutation_branch {
float str_modifier = 0.0f;
//melee bonuses
int cut_dmg_bonus = 0;
float pierce_dmg_bonus = 0.0;
std::pair<int, int> rand_cut_bonus;
int bash_dmg_bonus = 0;
std::pair<int, int> rand_bash_bonus;
Expand All @@ -151,6 +166,10 @@ struct mutation_branch {
cata::optional<int> scent_mask;
int bleed_resist = 0;

int butchering_quality = 0;

cata::value_ptr<mut_transform> transform;

/**Map of crafting skills modifiers, can be negative*/
std::map<skill_id, int> craft_skill_bonus;

Expand Down
27 changes: 27 additions & 0 deletions src/mutation_data.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -260,6 +260,20 @@ void mutation_branch::load_trait( const JsonObject &jo, const std::string &src )
trait_factory.load( jo, src );
}

mut_transform::mut_transform() : active( false ), moves( 0 ) {}

bool mut_transform::load( const JsonObject &jsobj, const std::string &member )
{
JsonObject j = jsobj.get_object( member );

assign( j, "target", target );
assign( j, "msg_transform", msg_transform );
assign( j, "active", active );
assign( j, "moves", moves );

return true;
}

void mutation_branch::load( const JsonObject &jo, const std::string & )
{
mandatory( jo, was_loaded, "id", id );
Expand Down Expand Up @@ -293,6 +307,10 @@ void mutation_branch::load( const JsonObject &jo, const std::string & )
optional( si, was_loaded, "type", ranged_mutation );
optional( si, was_loaded, "message", raw_ranged_mutation_message );
}
if( jo.has_object( "transform" ) ) {
transform = cata::make_value<mut_transform>();
transform->load( jo, "transform" );
}
optional( jo, was_loaded, "initial_ma_styles", initial_ma_styles );

if( jo.has_array( "bodytemp_modifiers" ) ) {
Expand Down Expand Up @@ -331,6 +349,7 @@ void mutation_branch::load( const JsonObject &jo, const std::string & )
optional( jo, was_loaded, "stealth_modifier", stealth_modifier, 0.0f );
optional( jo, was_loaded, "str_modifier", str_modifier, 0.0f );
optional( jo, was_loaded, "cut_dmg_bonus", cut_dmg_bonus, 0 );
optional( jo, was_loaded, "pierce_dmg_bonus", pierce_dmg_bonus, 0.0f );
optional( jo, was_loaded, "bash_dmg_bonus", bash_dmg_bonus, 0 );
optional( jo, was_loaded, "dodge_modifier", dodge_modifier, 0.0f );
optional( jo, was_loaded, "speed_modifier", speed_modifier, 1.0f );
Expand Down Expand Up @@ -367,6 +386,8 @@ void mutation_branch::load( const JsonObject &jo, const std::string & )
optional( jo, was_loaded, "can_only_heal_with", can_only_heal_with );
optional( jo, was_loaded, "can_heal_with", can_heal_with );

optional( jo, was_loaded, "butchering_quality", butchering_quality, 0 );

optional( jo, was_loaded, "allowed_category", allowed_category );

optional( jo, was_loaded, "mana_modifier", mana_modifier, 0 );
Expand Down Expand Up @@ -535,6 +556,12 @@ void mutation_branch::check_consistency()
debugmsg( "mutation %s refers to undefined mutation type %s", mid.c_str(), type );
}
}
if( mid->transform ) {
const trait_id tid = mid->transform->target;
if( !tid.is_valid() ) {
debugmsg( "mutation %s transform uses undefined target %s", mid.c_str(), tid.c_str() );
}
}
for( const std::pair<species_id, int> elem : an_id ) {
if( !elem.first.is_valid() ) {
debugmsg( "mutation %s refers to undefined species id %s", mid.c_str(), elem.first.c_str() );
Expand Down
18 changes: 14 additions & 4 deletions src/mutation_ui.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ void player::power_mutations()
std::vector<trait_id> passive;
std::vector<trait_id> active;
for( std::pair<const trait_id, trait_data> &mut : my_mutations ) {
if( !mut.first->activated ) {
if( !mut.first->activated && ! mut.first->transform ) {
passive.push_back( mut.first );
} else {
active.push_back( mut.first );
Expand Down Expand Up @@ -290,10 +290,15 @@ void player::power_mutations()
break;
}
const auto &mut_data = mut_id.obj();
const cata::value_ptr<mut_transform> &trans = mut_data.transform;
if( menu_mode == "activating" ) {
if( mut_data.activated ) {
if( mut_data.activated || trans ) {
if( my_mutations[mut_id].powered ) {
add_msg_if_player( m_neutral, _( "You stop using your %s." ), mut_data.name() );
if( trans && !trans->msg_transform.empty() ) {
add_msg_if_player( m_neutral, trans->msg_transform );
} else {
add_msg_if_player( m_neutral, _( "You stop using your %s." ), mut_data.name() );
}

deactivate_mutation( mut_id );
// Action done, leave screen
Expand All @@ -303,7 +308,12 @@ void player::power_mutations()
( !mut_data.fatigue || get_fatigue() <= 400 ) ) {

g->draw();
add_msg_if_player( m_neutral, _( "You activate your %s." ), mut_data.name() );
if( trans && !trans->msg_transform.empty() ) {
add_msg_if_player( m_neutral, trans->msg_transform );
} else {
add_msg_if_player( m_neutral, _( "You activate your %s." ), mut_data.name() );
}

activate_mutation( mut_id );
// Action done, leave screen
break;
Expand Down
9 changes: 3 additions & 6 deletions src/visitable.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@

#include "active_item_cache.h"
#include "bionics.h"
#include "mutation.h"
#include "character.h"
#include "colony.h"
#include "debug.h"
Expand Down Expand Up @@ -278,12 +279,8 @@ int visitable<Character>::max_quality( const quality_id &qual ) const
}

if( qual == qual_BUTCHER ) {
if( self->has_trait( trait_CLAWS_ST ) ) {
res = std::max( res, 8 );
} else if( self->has_trait( trait_TALONS ) || self->has_trait( trait_MANDIBLES ) ||
self->has_trait( trait_CLAWS ) || self->has_trait( trait_CLAWS_RETRACT ) ||
self->has_trait( trait_CLAWS_RAT ) ) {
res = std::max( res, 4 );
for( const trait_id &mut : self->get_mutations() ) {
res = std::max( res, mut->butchering_quality );
}
}

Expand Down