From af60511164dc44d529171e6eca6837a508191829 Mon Sep 17 00:00:00 2001 From: Fris0uman <41293484+Fris0uman@users.noreply.github.com> Date: Fri, 17 Apr 2020 09:34:08 +0200 Subject: [PATCH] Transforming a mutation into another on activation (#39523) --- data/json/mutations/mutations.json | 28 ++++++++++++++++++++++++---- doc/JSON_FLAGS.md | 1 - doc/JSON_INFO.md | 7 +++++++ src/character.h | 2 ++ src/melee.cpp | 24 +++++++----------------- src/mutation.cpp | 24 ++++++++++++++++++++++++ src/mutation.h | 19 +++++++++++++++++++ src/mutation_data.cpp | 27 +++++++++++++++++++++++++++ src/mutation_ui.cpp | 18 ++++++++++++++---- src/visitable.cpp | 9 +++------ 10 files changed, 127 insertions(+), 32 deletions(-) diff --git a/data/json/mutations/mutations.json b/data/json/mutations/mutations.json index 2fabbc7fe4791..d619108decc10 100644 --- a/data/json/mutations/mutations.json +++ b/data/json/mutations/mutations.json @@ -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" ] @@ -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" ], @@ -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" ], @@ -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" ], @@ -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" ], @@ -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 }, { @@ -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" ], @@ -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" ], diff --git a/doc/JSON_FLAGS.md b/doc/JSON_FLAGS.md index 13d71c75ec717..71fe1034d739c 100644 --- a/doc/JSON_FLAGS.md +++ b/doc/JSON_FLAGS.md @@ -1091,7 +1091,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. diff --git a/doc/JSON_INFO.md b/doc/JSON_INFO.md index 71efc7b44446e..a74af528e6197 100644 --- a/doc/JSON_INFO.md +++ b/doc/JSON_INFO.md @@ -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. @@ -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 diff --git a/src/character.h b/src/character.h index 6af7d15329aa3..51f5460d5a194 100644 --- a/src/character.h +++ b/src/character.h @@ -751,6 +751,8 @@ class Character : public Creature, public visitable /** 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 ); diff --git a/src/melee.cpp b/src/melee.cpp index e1e806031b856..432e626fefe68 100644 --- a/src/melee.cpp +++ b/src/melee.cpp @@ -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" ); @@ -947,6 +944,7 @@ void Character::roll_cut_damage( bool crit, damage_instance &di, bool average, 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; @@ -1014,27 +1012,19 @@ void Character::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 diff --git a/src/mutation.cpp b/src/mutation.cpp index 76e5b51860e11..6fef2103caaa6 100644 --- a/src/mutation.cpp +++ b/src/mutation.cpp @@ -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; @@ -494,6 +505,13 @@ void Character::activate_mutation( const trait_id &mut ) recalc_sight_limits(); } + if( mdata.transform ) { + const cata::value_ptr 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!" ) ); @@ -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 trans = mdata.transform; + mod_moves( -trans->moves ); + switch_mutations( mut, trans->target, trans->active ); + } } trait_id Character::trait_by_invlet( const int ch ) const diff --git a/src/mutation.h b/src/mutation.h index 0e93f0889f506..6984fadd0ab86 100644 --- a/src/mutation.h +++ b/src/mutation.h @@ -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; @@ -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 rand_cut_bonus; int bash_dmg_bonus = 0; std::pair rand_bash_bonus; @@ -151,6 +166,10 @@ struct mutation_branch { cata::optional scent_mask; int bleed_resist = 0; + int butchering_quality = 0; + + cata::value_ptr transform; + /**Map of crafting skills modifiers, can be negative*/ std::map craft_skill_bonus; diff --git a/src/mutation_data.cpp b/src/mutation_data.cpp index fd2bb857eae97..33f69b50856e7 100644 --- a/src/mutation_data.cpp +++ b/src/mutation_data.cpp @@ -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 ); @@ -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(); + transform->load( jo, "transform" ); + } optional( jo, was_loaded, "initial_ma_styles", initial_ma_styles ); if( jo.has_array( "bodytemp_modifiers" ) ) { @@ -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 ); @@ -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 ); @@ -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 elem : an_id ) { if( !elem.first.is_valid() ) { debugmsg( "mutation %s refers to undefined species id %s", mid.c_str(), elem.first.c_str() ); diff --git a/src/mutation_ui.cpp b/src/mutation_ui.cpp index 0dbbaff0e31b2..d92413d213b6b 100644 --- a/src/mutation_ui.cpp +++ b/src/mutation_ui.cpp @@ -71,7 +71,7 @@ void player::power_mutations() std::vector passive; std::vector active; for( std::pair &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 ); @@ -290,10 +290,15 @@ void player::power_mutations() break; } const auto &mut_data = mut_id.obj(); + const cata::value_ptr &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 @@ -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; diff --git a/src/visitable.cpp b/src/visitable.cpp index aa98f0fadd2bf..0c7ef75e4b603 100644 --- a/src/visitable.cpp +++ b/src/visitable.cpp @@ -10,6 +10,7 @@ #include "active_item_cache.h" #include "bionics.h" +#include "mutation.h" #include "character.h" #include "colony.h" #include "debug.h" @@ -278,12 +279,8 @@ int visitable::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 ); } }