Skip to content

Commit

Permalink
Add enchantments to bionics (#39563)
Browse files Browse the repository at this point in the history
* Add enchantments to bionics

* test json

* update cache

* move recalculate_enchantment_cache()

* update force_add to consider emitter and ench_effects

* Revert "test json"

This reverts commit 7ce05e0.

* Jsonize bionic invisibility effect with an enchantment

* Doc

* fix

Co-authored-by: KorGgenT <[email protected]>
  • Loading branch information
Fris0uman and KorGgenT authored Apr 18, 2020
1 parent 5cfff67 commit f94cfe1
Show file tree
Hide file tree
Showing 10 changed files with 189 additions and 2 deletions.
2 changes: 2 additions & 0 deletions data/json/bionics.json
Original file line number Diff line number Diff line change
Expand Up @@ -261,6 +261,7 @@
"act_cost": "30 kJ",
"react_cost": "30 kJ",
"time": 1,
"enchantments": [ "ENCH_INVISIBILITY" ],
"flags": [ "BIONIC_TOGGLED" ]
},
{
Expand Down Expand Up @@ -709,6 +710,7 @@
"description": "When active, this bionic eliminates all light within a 2 tile radius through destructive interference.",
"occupied_bodyparts": [ [ "TORSO", 16 ] ],
"flags": [ "BIONIC_TOGGLED" ],
"enchantments": [ "ENCH_INVISIBILITY" ],
"act_cost": "9 kJ",
"react_cost": "9 kJ",
"time": 1
Expand Down
7 changes: 7 additions & 0 deletions data/json/effects.json
Original file line number Diff line number Diff line change
Expand Up @@ -629,6 +629,13 @@
"rating": "bad",
"base_mods": { "vomit_chance": [ 500 ] }
},
{
"type": "effect_type",
"id": "invisibility",
"name": [ "Invisible" ],
"desc": [ "You are invisible." ],
"flags": [ "EFFECT_INVISIBLE" ]
},
{
"type": "effect_type",
"id": "took_flumed"
Expand Down
8 changes: 8 additions & 0 deletions data/json/enchantments.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
[
{
"type": "enchantment",
"id": "ENCH_INVISIBILITY",
"condition": "ALWAYS",
"ench_effects": [ { "effect": "invisibility", "intensity": 1 } ]
}
]
1 change: 1 addition & 0 deletions doc/JSON_INFO.md
Original file line number Diff line number Diff line change
Expand Up @@ -452,6 +452,7 @@ This section describes each json file and their contents. Each json has their ow
| coverage_power_gen_penalty | (_optional_) Fraction of coverage diminishing fuel_efficiency. Float between 0.0 and 1.0. (default: `nullopt`)
| power_gen_emission | (_optional_) `emit_id` of the field emitted by this bionic when it produces energy. Emit_ids are defined in `emit.json`.
| stat_bonus | (_optional_) List of passive stat bonus. Stat are designated as follow: "DEX", "INT", "STR", "PER".
| enchantments | (_optional_) List of enchantments applied by this CBM (see MAGIC.md for instructions on enchantment. NB: enchantments are not necessarily magic.)
```C++
{
Expand Down
119 changes: 119 additions & 0 deletions doc/MAGIC.md
Original file line number Diff line number Diff line change
Expand Up @@ -198,3 +198,122 @@ You can assign a spell as a special attack for a monster.
* spell_id: the id for the spell being cast.
* spell_level: the level at which the spell is cast. Spells cast by monsters do not gain levels like player spells.
* cooldown: how often the monster can cast this spell

### Enchantments
| Identifier | Description
|--- |---
| 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
| 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.
| 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.



```C++
{
"type": "enchantment",
"id": "MEP_INK_GLAND_SPRAY",
"hit_me_effect": [
{
"id": "generic_blinding_spray_1",
"hit_self": false,
"once_in": 15,
"message": "Your ink glands spray some ink into %2$s's eyes.",
"npc_message": "%1$s's ink glands spay some ink into %2$s's eyes."
}
]
},
{
"type": "enchantment",
"id": "ENCH_INVISIBILITY",
"condition": "ALWAYS",
"ench_effects": [ { "effect": "invisibility", "intensity": 1 } ]
"has": "WIELD",
"hit_you_effect": [ { "id": "AEA_FIREBALL" } ],
"hit_me_effect": [ { "id": "AEA_HEAL" } ],
"values": [ { "value": "STRENGTH", "multiply": 1.1, "add": -5 } ],
"intermittent_activation": {
"effects": [
{
"frequency": "1 hour",
"spell_effects": [
{ "id": "AEA_ADRENALINE" }
]
}
]
}
}

```
### Allowed id for values
The allowed values are as follows:
- Effects for the character that has the enchantment:
* STRENGTH
* DEXTERITY
* PERCEPTION
* INTELLIGENCE
* SPEED
* ATTACK_COST
* ATTACK_SPEED
* MOVE_COST
* METABOLISM
* MAX_MANA
* REGEN_MANA
* BIONIC_POWER
* MAX_STAMINA
* REGEN_STAMINA
* MAX_HP
* REGEN_HP
* THIRST
* FATIGUE
* PAIN
* BONUS_DODGE
* BONUS_BLOCK
* BONUS_DAMAGE
* ATTACK_NOISE
* SPELL_NOISE
* SHOUT_NOISE
* FOOTSTEP_NOISE
* SIGHT_RANGE
* CARRY_WEIGHT
* CARRY_VOLUME
* SOCIAL_LIE
* SOCIAL_PERSUADE
* SOCIAL_INTIMIDATE
* ARMOR_BASH
* ARMOR_CUT
* ARMOR_STAB
* ARMOR_HEAT
* ARMOR_COLD
* ARMOR_ELEC
* ARMOR_ACID
* ARMOR_BIO
- Effects for the item that has the enchantment:
* ITEM_DAMAGE_BASH
* ITEM_DAMAGE_CUT
* ITEM_DAMAGE_STAB
* ITEM_DAMAGE_HEAT
* ITEM_DAMAGE_COLD
* ITEM_DAMAGE_ELEC
* ITEM_DAMAGE_ACID
* ITEM_DAMAGE_BIO
* ITEM_DAMAGE_AP
* ITEM_ARMOR_BASH
* ITEM_ARMOR_CUT
* ITEM_ARMOR_STAB
* ITEM_ARMOR_HEAT
* ITEM_ARMOR_COLD
* ITEM_ARMOR_ELEC
* ITEM_ARMOR_ACID
* ITEM_ARMOR_BIO
* ITEM_WEIGHT
* ITEM_ENCUMBRANCE
* ITEM_VOLUME
* ITEM_COVERAGE
* ITEM_ATTACK_SPEED
* ITEM_WET_PROTECTION
19 changes: 19 additions & 0 deletions src/bionics.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -377,6 +377,9 @@ bool Character::activate_bionic( int b, bool eff_only )
if( bio.info().charge_time > 0 ) {
bio.charge_timer = bio.info().charge_time;
}
if( !bio.id->enchantments.empty() ) {
recalculate_enchantment_cache();
}
}

auto add_msg_activate = [&]() {
Expand Down Expand Up @@ -977,6 +980,9 @@ bool Character::deactivate_bionic( int b, bool eff_only )
// Recalculate stats (strength, mods from pain etc.) that could have been affected
reset_encumbrance();
reset();
if( !bio.id->enchantments.empty() ) {
recalculate_enchantment_cache();
}

// Also reset crafting inventory cache if this bionic spawned a fake item
if( !bio.info().fake_item.empty() ) {
Expand Down Expand Up @@ -2436,6 +2442,9 @@ void Character::add_bionic( const bionic_id &b )

reset_encumbrance();
recalc_sight_limits();
if( !b->enchantments.empty() ) {
recalculate_enchantment_cache();
}
}

void Character::remove_bionic( const bionic_id &b )
Expand All @@ -2456,6 +2465,9 @@ void Character::remove_bionic( const bionic_id &b )
*my_bionics = new_my_bionics;
reset_encumbrance();
recalc_sight_limits();
if( !b->enchantments.empty() ) {
recalculate_enchantment_cache();
}
}

int player::num_bionics() const
Expand Down Expand Up @@ -2571,6 +2583,8 @@ void load_bionic( const JsonObject &jsobj )

new_bionic.weight_capacity_modifier = jsobj.get_float( "weight_capacity_modifier", 1.0 );

assign( jsobj, "enchantments", new_bionic.enchantments );

assign( jsobj, "weight_capacity_bonus", new_bionic.weight_capacity_bonus, false, 0_gram );
assign( jsobj, "exothermic_power_gen", new_bionic.exothermic_power_gen );
assign( jsobj, "power_gen_emission", new_bionic.power_gen_emission );
Expand Down Expand Up @@ -2640,6 +2654,11 @@ void check_bionics()
bio.first.c_str(), mid.c_str() );
}
}
for( const enchantment_id &eid : bio.first->enchantments ) {
if( !eid.is_valid() ) {
debugmsg( "Bionic %s uses undefined enchantment %s", bio.first.c_str(), eid.c_str() );
}
}
for( const bionic_id &bid : bio.second.included_bionics ) {
if( !bid.is_valid() ) {
debugmsg( "Bionic %s includes undefined bionic %s",
Expand Down
3 changes: 3 additions & 0 deletions src/bionics.h
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,9 @@ struct bionic_data {
/**Amount of cut protection offered by this bionic*/
std::map<body_part, size_t> cut_protec;

/** bionic enchantments */
std::vector<enchantment_id> enchantments;

/**
* Body part slots used to install this bionic, mapped to the amount of space required.
*/
Expand Down
18 changes: 16 additions & 2 deletions src/character.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1548,6 +1548,8 @@ void Character::process_turn()
}

Creature::process_turn();

enchantment_cache.activate_passive( *this );
}

void Character::recalc_hp()
Expand Down Expand Up @@ -6311,8 +6313,6 @@ bool Character::is_invisible() const
{
return (
has_effect_with_flag( flag_EFFECT_INVISIBLE ) ||
has_active_bionic( str_bio_cloak ) ||
has_active_bionic( str_bio_night ) ||
is_wearing_active_optcloak() ||
has_trait( trait_DEBUG_CLOAK ) ||
has_artifact_with( AEP_INVISIBLE )
Expand Down Expand Up @@ -7961,6 +7961,20 @@ void Character::recalculate_enchantment_cache()
}
}
}

for( const bionic &bio : *my_bionics ) {
const bionic_id &bid = bio.id;
if( bid->toggled && !bio.powered ) {
continue;
}

for( const enchantment_id &ench_id : bid->enchantments ) {
const enchantment &ench = ench_id.obj();
if( ench.is_active( *this ) ) {
enchantment_cache.force_add( ench );
}
}
}
}

double Character::calculate_by_enchantment( double modify, enchantment::mod value,
Expand Down
13 changes: 13 additions & 0 deletions src/magic_enchantment.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -240,6 +240,10 @@ void enchantment::load( const JsonObject &jo, const std::string & )
active_conditions.second = io::string_to_enum<condition>( jo.get_string( "condition",
"ALWAYS" ) );

for( JsonObject jsobj : jo.get_array( "ench_effects" ) ) {
ench_effects.emplace( efftype_id( jsobj.get_string( "effect" ) ), jsobj.get_int( "intensity" ) );
}

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 @@ -345,6 +349,12 @@ void enchantment::force_add( const enchantment &rhs )

hit_you_effect.insert( hit_you_effect.end(), rhs.hit_you_effect.begin(), rhs.hit_you_effect.end() );

ench_effects.insert( rhs.ench_effects.begin(), rhs.ench_effects.end() );

if( rhs.emitter ) {
emitter = rhs.emitter;
}

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 Expand Up @@ -399,6 +409,9 @@ void enchantment::activate_passive( Character &guy ) const
if( emitter ) {
g->m.emit_field( guy.pos(), *emitter );
}
for( const std::pair<efftype_id, int> eff : ench_effects ) {
guy.add_effect( eff.first, 1_seconds, num_bp, false, eff.second );
}
for( const std::pair<const time_duration, std::vector<fake_spell>> &activation :
intermittent_activation ) {
// a random approximation!
Expand Down
1 change: 1 addition & 0 deletions src/magic_enchantment.h
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,7 @@ class enchantment
void cast_hit_me( Character &caster, const Creature *target ) const;
private:
cata::optional<emit_id> emitter;
std::map<efftype_id, int> ench_effects;
// values that add to the base value
std::map<mod, int> values_add;
// values that get multiplied to the base value
Expand Down

0 comments on commit f94cfe1

Please sign in to comment.