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

Port over mutations granting effects (and fix) #180

Merged
merged 1 commit into from
Nov 3, 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.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions data/json/effects.json
Original file line number Diff line number Diff line change
Expand Up @@ -624,6 +624,13 @@
"desc": [ "You are invisible." ],
"flags": [ "EFFECT_INVISIBLE" ]
},
{
"type": "effect_type",
"id": "debug_clairvoyance",
"name": [ "Debug Clairvoyance" ],
"desc": [ "You can see through everything!" ],
"flags": [ "EFFECT_SUPER_CLAIRVOYANCE" ]
},
{
"type": "effect_type",
"id": "took_flumed"
Expand Down
18 changes: 18 additions & 0 deletions data/json/flags.json
Original file line number Diff line number Diff line change
Expand Up @@ -733,6 +733,24 @@
"context": [ ],
"type": "json_flag"
},
{
"id": "EFFECT_CLAIRVOYANCE",
"context": [ ],
"type": "json_flag",
"//": "Effect flag, grants same abilities as a source of AEP_CLAIRVOYANCE would."
},
{
"id": "EFFECT_CLAIRVOYANCE_PLUS",
"context": [ ],
"type": "json_flag",
"//": "Effect flag, grants same abilities as a source of VISION_CLAIRVOYANCE_PLUS would."
},
{
"id": "EFFECT_SUPER_CLAIRVOYANCE",
"context": [ ],
"type": "json_flag",
"//": "Effect flag, grants same abilities as a source of AEP_SUPER_CLAIRVOYANCE would."
},
{
"id": "ELECTRIC_IMMUNE",
"context": [ ],
Expand Down
62 changes: 62 additions & 0 deletions data/json/items/tool/misc.json
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,68 @@
"type": "transform"
}
},
{
"id": "debug_active_relic_test",
"type": "TOOL",
"name": { "str": "Cube of Shame", "str_pl": "Cubes of Shame" },
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

src/translations.cpp:530 [translation::deserialize(JsonIn&)::<lambda(const string&, int)>] 
line 203:40: Please use "str_sp" instead of "str" and "str_pl" for text with identical singular and plural forms

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Plural string is "cubes of shame" which is not identical?

Copy link
Contributor

@olanti-p olanti-p Nov 3, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"description": "This is a debug-only item for testing relic properties, powered by batteries. Turn it on to lose intelligence and perception, but gain clairvoyance",
"material": [ "steel" ],
"symbol": ";",
"color": "blue",
"weight": "400 g",
"volume": "500 ml",
"charges_per_use": 1,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It doesn't seem to be introduced here, but there is a bug with the system: charges_per_use do nothing here.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Artifact from having based the item off a standard electronic item (fairly certain it was a flashlight), a lot of transforming items in the game have a charges per use but don't seem to respect it, seen in both DDA and BN.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh right: charge consumption occurs at the end of the function, but in transformation, this would consume charges from the transformed item, which may not work.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That could be it. Placing it earlier in the order would fix that, but it might be good to ensure that the check for needs_charges still happens earlier. Otherwise, presumably an item that checks for having a certain number of charges would (if it's exactly at the threshold for passing the needs_charges check) consume its charges, then fail the check.

"ammo": "battery",
"use_action": {
"type": "transform",
"msg": "You inject yourself into the Cube of Shame.",
"target": "debug_active_relic_test_on",
"active": true,
"need_charges": 1,
"need_charges_msg": "Batteries not included."
},
"magazines": [
[
"battery",
[
"light_disposable_cell",
"light_minus_disposable_cell",
"light_battery_cell",
"light_plus_battery_cell",
"light_minus_battery_cell",
"light_atomic_battery_cell",
"light_minus_atomic_battery_cell"
]
]
],
"magazine_well": 1,
"relic_data": {
"passive_effects": [
{ "has": "HELD", "condition": "ALWAYS", "mutations": [ "SCHIZOPHRENIC" ] },
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

SCHIZOPHRENIC is a terrible mutation for testing because it's RNG-dependent.
Try something like carnivore.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

True, was a bit of waiting. Had picked it more for thematic purposes, but can change to Carnivore next time I alter its properties.

{
"has": "HELD",
"condition": "ACTIVE",
"values": [ { "value": "INTELLIGENCE", "add": -4 }, { "value": "PERCEPTION", "add": -4 } ],
"ench_effects": [ { "effect": "debug_clairvoyance", "intensity": 1 } ]
}
]
}
},
{
"id": "debug_active_relic_test_on",
"copy-from": "debug_active_relic_test",
"type": "TOOL",
"name": { "str": "Cube of Shame (on)", "str_pl": "Cube of Shame (on)" },
"description": "This is a debug-only item for testing relic properties, powered by batteries. Drop it or turn it off to retrieve your mind.",
"power_draw": 10000,
"revert_to": "debug_active_relic_test",
"use_action": {
"menu_text": "Turn off",
"type": "transform",
"msg": "You extract yourself from the Cube of Shame.",
"target": "debug_active_relic_test"
}
},
{
"id": "e_tool",
"type": "TOOL",
Expand Down
3 changes: 3 additions & 0 deletions doc/MAGIC.md
Original file line number Diff line number Diff line change
Expand Up @@ -205,9 +205,11 @@ You can assign a spell as a special attack for a monster.
| id | Unique ID. Must be one continuous word, use underscores if necessary.
| has | How an enchantment determines if it is in the right location in order to qualify for being active. "WIELD" - when wielded in your hand * "WORN" - when worn as armor * "HELD" - when in your inventory
| condition | How an enchantment determines if you are in the right environments in order for the enchantment to qualify for being active. * "ALWAYS" - Always and forevermore * "UNDERGROUND" - When the owner of the item is below Z-level 0 * "UNDERWATER" - When the owner is in swimmable terrain * "ACTIVE" - whenever the item, mutation, bionic, or whatever the enchantment is attached to is active.
| ench_effects | Grants effects of specified "intensity" as long as this enchantment is applicable. Use intensity of 1 will work for effects that do not actually have intensities.
| hit_you_effect | A spell that activates when you melee_attack a creature. The spell is centered on the location of the creature unless self = true, then it is centered on your location. Follows the template for defining "fake_spell"
| hit_me_effect | A spell that activates when you are hit by a creature. The spell is centered on your location. Follows the template for defining "fake_spell"
| intermittent_activation | Spells that activate centered on you depending on the duration. The spells follow the "fake_spell" template.
| mutations | Which mutations are granted by this enchantment, mimicking their effects.
| values | Anything that is a number that can be modified. The id field is required, and "add" and "multiply" are optional. A "multiply" value of -1 is -100% and a multiply of 2.5 is +250%. Add is always before multiply. See allowed id below.


Expand All @@ -234,6 +236,7 @@ You can assign a spell as a special attack for a monster.
"has": "WIELD",
"hit_you_effect": [ { "id": "AEA_FIREBALL" } ],
"hit_me_effect": [ { "id": "AEA_HEAL" } ],
"mutations": [ "KILLER", "PARKOUR" ],
"values": [ { "value": "STRENGTH", "multiply": 1.1, "add": -5 } ],
"intermittent_activation": {
"effects": [
Expand Down
43 changes: 26 additions & 17 deletions src/character.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1691,15 +1691,11 @@ void Character::recalc_sight_limits()
vision_mode_cache.set( IR_VISION );
}

if( has_artifact_with( AEP_SUPER_CLAIRVOYANCE ) ) {
if( has_artifact_with( AEP_SUPER_CLAIRVOYANCE ) || has_effect_with_flag( "EFFECT_SUPER_CLAIRVOYANCE" ) ) {
vision_mode_cache.set( VISION_CLAIRVOYANCE_SUPER );
}

if( has_artifact_with( AEP_CLAIRVOYANCE_PLUS ) ) {
} else if( has_artifact_with( AEP_CLAIRVOYANCE_PLUS ) || has_effect_with_flag( "EFFECT_CLAIRVOYANCE_PLUS" ) ) {
vision_mode_cache.set( VISION_CLAIRVOYANCE_PLUS );
}

if( has_artifact_with( AEP_CLAIRVOYANCE ) ) {
} else if( has_artifact_with( AEP_CLAIRVOYANCE ) || has_effect_with_flag( "EFFECT_CLAIRVOYANCE" ) ) {
vision_mode_cache.set( VISION_CLAIRVOYANCE );
}
}
Expand Down Expand Up @@ -7620,19 +7616,14 @@ void Character::recalculate_enchantment_cache()
// start by resetting the cache to all inventory items
enchantment_cache = inv.get_active_enchantment_cache( *this );

for( const enchantment &ench : weapon.get_enchantments() ) {
if( ench.is_active( *this, weapon ) ) {
enchantment_cache.force_add( ench );
}
}

for( const item &worn_it : worn ) {
for( const enchantment &ench : worn_it.get_enchantments() ) {
if( ench.is_active( *this, worn_it ) ) {
visit_items( [&]( const item * it ) {
for( const enchantment &ench : it->get_enchantments() ) {
if( ench.is_active( *this, *it ) ) {
enchantment_cache.force_add( ench );
}
}
}
return VisitResponse::NEXT;
} );

// get from traits/ mutations
for( const std::pair<const trait_id, trait_data> &mut_map : my_mutations ) {
Expand Down Expand Up @@ -9382,11 +9373,29 @@ void Character::on_worn_item_washed( const item &it )

void Character::on_item_wear( const item &it )
{
for( const trait_id &mut : it.mutations_from_wearing( *this ) ) {
mutation_effect( mut, true );
recalc_sight_limits();
calc_encumbrance();

// If the stamina is higher than the max (Languorous), set it back to max
if( get_stamina() > get_stamina_max() ) {
set_stamina( get_stamina_max() );
}
}
morale->on_item_wear( it );
}

void Character::on_item_takeoff( const item &it )
{
for( const trait_id &mut : it.mutations_from_wearing( *this ) ) {
mutation_loss_effect( mut );
recalc_sight_limits();
calc_encumbrance();
if( get_stamina() > get_stamina_max() ) {
set_stamina( get_stamina_max() );
}
}
morale->on_item_takeoff( it );
}

Expand Down
2 changes: 1 addition & 1 deletion src/character.h
Original file line number Diff line number Diff line change
Expand Up @@ -945,7 +945,7 @@ class Character : public Creature, public visitable<Character>
float mabuff_attack_cost_mult() const;

/** Handles things like destruction of armor, etc. */
void mutation_effect( const trait_id &mut );
void mutation_effect( const trait_id &mut, bool worn_destroyed_override );
/** Handles what happens when you lose a mutation. */
void mutation_loss_effect( const trait_id &mut );

Expand Down
28 changes: 28 additions & 0 deletions src/item.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8470,6 +8470,34 @@ void item::process_artifact( player *carrier, const tripoint & /*pos*/ )
}
}


std::vector<trait_id> item::mutations_from_wearing( const Character &guy ) const
{
if( !is_relic() ) {
return std::vector<trait_id> {};
}
std::vector<trait_id> muts;

for( const enchantment &ench : relic_data->get_enchantments() ) {
for( const trait_id &mut : ench.get_mutations() ) {
// this may not be perfectly accurate due to conditions
muts.push_back( mut );
}
}

for( const trait_id &char_mut : guy.get_mutations() ) {
for( auto iter = muts.begin(); iter != muts.end(); ) {
if( char_mut == *iter ) {
iter = muts.erase( iter );
} else {
++iter;
}
}
}

return muts;
}

void item::process_relic( Character *carrier )
{
if( !is_relic() ) {
Expand Down
2 changes: 2 additions & 0 deletions src/item.h
Original file line number Diff line number Diff line change
Expand Up @@ -1280,6 +1280,8 @@ class item : public visitable<item>
*/
void on_damage( int qty, damage_type dt );

std::vector<trait_id> mutations_from_wearing( const Character &guy ) const;

/**
* Name of the item type (not the item), with proper plural.
* This is only special when the item itself has a special name ("name" entry in
Expand Down
8 changes: 8 additions & 0 deletions src/magic_enchantment.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -250,6 +250,8 @@ void enchantment::load( const JsonObject &jo, const std::string & )
ench_effects.emplace( efftype_id( jsobj.get_string( "effect" ) ), jsobj.get_int( "intensity" ) );
}

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

if( jo.has_array( "values" ) ) {
for( const JsonObject value_obj : jo.get_array( "values" ) ) {
const enchantment::mod value = io::string_to_enum<mod>( value_obj.get_string( "value" ) );
Expand Down Expand Up @@ -304,6 +306,8 @@ void enchantment::serialize( JsonOut &jsout ) const
jsout.end_object();
}

jsout.member( "mutations", mutations );

jsout.member( "values" );
jsout.start_array();
for( int value = 0; value < mod::NUM_MOD; value++ ) {
Expand Down Expand Up @@ -361,6 +365,10 @@ void enchantment::force_add( const enchantment &rhs )
emitter = rhs.emitter;
}

for( const trait_id &branch : rhs.mutations ) {
mutations.emplace( branch );
}

for( const std::pair<const time_duration, std::vector<fake_spell>> &act_pair :
rhs.intermittent_activation ) {
for( const fake_spell &fake : act_pair.second ) {
Expand Down
5 changes: 5 additions & 0 deletions src/magic_enchantment.h
Original file line number Diff line number Diff line change
Expand Up @@ -144,7 +144,12 @@ class enchantment
void cast_hit_you( Character &caster, const Creature &target ) const;
// casts all the hit_me_effects on self or a target depending on the enchantment definition
void cast_hit_me( Character &caster, const Creature *target ) const;

std::set<trait_id> get_mutations() const {
return mutations;
}
private:
std::set<trait_id> mutations;
cata::optional<emit_id> emitter;
std::map<efftype_id, int> ench_effects;
// values that add to the base value
Expand Down
15 changes: 10 additions & 5 deletions src/mutation.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,12 @@ std::string enum_to_string<mutagen_technique>( mutagen_technique data )

bool Character::has_trait( const trait_id &b ) const
{
return my_mutations.count( b ) > 0;
for( const trait_id &mut : get_mutations() ) {
if( mut == b ) {
return true;
}
}
return false;
}

bool Character::has_trait_flag( const std::string &b ) const
Expand Down Expand Up @@ -160,7 +165,7 @@ void Character::set_mutation( const trait_id &trait )
}
my_mutations.emplace( trait, trait_data{} );
cached_mutations.push_back( &trait.obj() );
mutation_effect( trait );
mutation_effect( trait, false );
recalc_sight_limits();
reset_encumbrance();
}
Expand Down Expand Up @@ -192,7 +197,7 @@ void Character::switch_mutations( const trait_id &switched, const trait_id &targ

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

int Character::get_mod( const trait_id &mut, const std::string &arg ) const
Expand Down Expand Up @@ -259,7 +264,7 @@ m_size calculate_size( const Character &c )
return MS_MEDIUM;
}

void Character::mutation_effect( const trait_id &mut )
void Character::mutation_effect( const trait_id &mut, const bool worn_destroyed_override )
{
if( mut == "GLASSJAW" ) {
recalc_hp();
Expand Down Expand Up @@ -308,7 +313,7 @@ void Character::mutation_effect( const trait_id &mut )
if( !branch.conflicts_with_item( armor ) ) {
return false;
}
if( branch.destroys_gear ) {
if( !worn_destroyed_override && branch.destroys_gear ) {
add_msg_player_or_npc( m_bad,
_( "Your %s is destroyed!" ),
_( "<npcname>'s %s is destroyed!" ),
Expand Down
14 changes: 14 additions & 0 deletions src/newcharacter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2771,6 +2771,20 @@ std::vector<trait_id> Character::get_mutations( bool include_hidden ) const
result.push_back( t.first );
}
}
for( const trait_id &ench_trait : enchantment_cache.get_mutations() ) {
if( include_hidden || ench_trait->player_display ) {
bool found = false;
for( const trait_id &exist : result ) {
if( exist == ench_trait ) {
found = true;
break;
}
}
if( !found ) {
result.push_back( ench_trait );
}
}
}
return result;
}

Expand Down