1 #include "relic.h"
2 
3 #include <algorithm>
4 #include <cmath>
5 #include <cstdlib>
6 #include <set>
7 #include <string>
8 
9 #include "calendar.h"
10 #include "character.h"
11 #include "creature.h"
12 #include "debug.h"
13 #include "enums.h"
14 #include "generic_factory.h"
15 #include "json.h"
16 #include "magic.h"
17 #include "magic_enchantment.h"
18 #include "map.h"
19 #include "rng.h"
20 #include "string_id.h"
21 #include "translations.h"
22 #include "type_id.h"
23 #include "weather.h"
24 #include "weather_type.h"
25 
26 /*
27  * A little helper function to tell if you can load one ammo into a gun.
28  * Checks if you can load ammo directly into the gun, or if you can load it into a magazine in the gun.
29  */
item_can_not_load_ammo(const item & gun)30 static bool item_can_not_load_ammo( const item &gun )
31 {
32     return ( gun.is_magazine() && gun.remaining_ammo_capacity() == 0 ) ||
33            ( !gun.is_magazine() && gun.magazine_current() == nullptr ) ||
34            ( gun.magazine_current() != nullptr && gun.magazine_current()->remaining_ammo_capacity() == 0 );
35 }
36 
37 namespace io
38 {
39     // *INDENT-OFF*
40     template<>
enum_to_string(relic_procgen_data::type data)41     std::string enum_to_string<relic_procgen_data::type>( relic_procgen_data::type data )
42     {
43         switch( data ) {
44         case relic_procgen_data::type::active_enchantment: return "active_enchantment";
45         case relic_procgen_data::type::hit_me: return "hit_me";
46         case relic_procgen_data::type::hit_you: return "hit_you";
47         case relic_procgen_data::type::passive_enchantment_add: return "passive_enchantment_add";
48         case relic_procgen_data::type::passive_enchantment_mult: return "passive_enchantment_mult";
49         case relic_procgen_data::type::last: break;
50         }
51         debugmsg( "Invalid enchantment::has" );
52         abort();
53     }
54     // *INDENT-ON*
55 template<>
enum_to_string(relic_recharge type)56 std::string enum_to_string<relic_recharge>( relic_recharge type )
57 {
58     // *INDENT-OFF*
59     switch( type ) {
60         case relic_recharge::none: return "none";
61         case relic_recharge::periodic: return "periodic";
62         case relic_recharge::solar_sunny: return "solar_sunny";
63         case relic_recharge::num: break;
64     }
65     // *INDENT-ON*
66     debugmsg( "Invalid relic recharge type" );
67     abort();
68 }
69 } // namespace io
70 
71 namespace
72 {
73 generic_factory<relic_procgen_data> relic_procgen_data_factory( "relic_procgen_data" );
74 } // namespace
75 
76 template<>
obj() const77 const relic_procgen_data &string_id<relic_procgen_data>::obj() const
78 {
79     return relic_procgen_data_factory.obj( *this );
80 }
81 
82 template<>
is_valid() const83 bool string_id<relic_procgen_data>::is_valid() const
84 {
85     return relic_procgen_data_factory.is_valid( *this );
86 }
87 
load_relic_procgen_data(const JsonObject & jo,const std::string & src)88 void relic_procgen_data::load_relic_procgen_data( const JsonObject &jo, const std::string &src )
89 {
90     relic_procgen_data_factory.load( jo, src );
91 }
92 
add_active_effect(const fake_spell & sp)93 void relic::add_active_effect( const fake_spell &sp )
94 {
95     active_effects.emplace_back( sp );
96 }
97 
add_passive_effect(const enchantment & nench)98 void relic::add_passive_effect( const enchantment &nench )
99 {
100     for( enchantment &ench : passive_effects ) {
101         if( ench.add( nench ) ) {
102             return;
103         }
104     }
105     passive_effects.emplace_back( nench );
106 }
107 
108 template<typename T>
load(const JsonObject & jo)109 void relic_procgen_data::enchantment_value_passive<T>::load( const JsonObject &jo )
110 {
111     mandatory( jo, was_loaded, "type", type );
112     optional( jo, was_loaded, "power_per_increment", power_per_increment, 1 );
113     optional( jo, was_loaded, "increment", increment, 1 );
114     optional( jo, was_loaded, "min_value", min_value, 0 );
115     optional( jo, was_loaded, "max_value", max_value, 0 );
116 }
117 
118 template<typename T>
deserialize(JsonIn & jsin)119 void relic_procgen_data::enchantment_value_passive<T>::deserialize( JsonIn &jsin )
120 {
121     JsonObject jobj = jsin.get_object();
122     load( jobj );
123 }
124 
load(const JsonObject & jo)125 void relic_procgen_data::enchantment_active::load( const JsonObject &jo )
126 {
127     mandatory( jo, was_loaded, "spell_id", activated_spell );
128     optional( jo, was_loaded, "base_power", base_power, 0 );
129     optional( jo, was_loaded, "power_per_increment", power_per_increment, 1 );
130     optional( jo, was_loaded, "increment", increment, 1 );
131     optional( jo, was_loaded, "min_level", min_level, 0 );
132     optional( jo, was_loaded, "max_level", max_level, 0 );
133 }
134 
deserialize(JsonIn & jsin)135 void relic_procgen_data::enchantment_active::deserialize( JsonIn &jsin )
136 {
137     JsonObject jobj = jsin.get_object();
138     load( jobj );
139 }
140 
load(const JsonObject & jo,const std::string &)141 void relic_procgen_data::load( const JsonObject &jo, const std::string & )
142 {
143     for( const JsonObject jo_inner : jo.get_array( "passive_add_procgen_values" ) ) {
144         int weight = 0;
145         mandatory( jo_inner, was_loaded, "weight", weight );
146         relic_procgen_data::enchantment_value_passive<int> val;
147         val.load( jo_inner );
148 
149         passive_add_procgen_values.add( val, weight );
150     }
151 
152     for( const JsonObject jo_inner : jo.get_array( "passive_mult_procgen_values" ) ) {
153         int weight = 0;
154         mandatory( jo_inner, was_loaded, "weight", weight );
155         relic_procgen_data::enchantment_value_passive<float> val;
156         val.load( jo_inner );
157 
158         passive_mult_procgen_values.add( val, weight );
159     }
160 
161     for( const JsonObject jo_inner : jo.get_array( "type_weights" ) ) {
162         int weight = 0;
163         mandatory( jo_inner, was_loaded, "weight", weight );
164         relic_procgen_data::type val = relic_procgen_data::type::last;
165         mandatory( jo_inner, was_loaded, "value", val );
166 
167         type_weights.add( val, weight );
168     }
169 
170     for( const JsonObject jo_inner : jo.get_array( "items" ) ) {
171         int weight = 0;
172         mandatory( jo_inner, was_loaded, "weight", weight );
173         itype_id it;
174         mandatory( jo_inner, was_loaded, "item", it );
175 
176         item_weights.add( it, weight );
177     }
178 
179     for( const JsonObject jo_inner : jo.get_array( "active_procgen_values" ) ) {
180         int weight = 0;
181         mandatory( jo_inner, was_loaded, "weight", weight );
182         relic_procgen_data::enchantment_active val;
183         val.load( jo_inner );
184 
185         active_procgen_values.add( val, weight );
186     }
187 
188     for( const JsonObject jo_inner : jo.get_array( "charge_types" ) ) {
189         int weight = 0;
190         mandatory( jo_inner, was_loaded, "weight", weight );
191         relic_charge_template charge;
192         charge.load( jo_inner );
193 
194         charge_values.add( charge, weight );
195     }
196 }
197 
load(const JsonObject & jo)198 void relic_procgen_data::generation_rules::load( const JsonObject &jo )
199 {
200     mandatory( jo, was_loaded, "power_level", power_level );
201     mandatory( jo, was_loaded, "max_attributes", max_attributes );
202     optional( jo, was_loaded, "max_negative_power", max_negative_power, 0 );
203 }
204 
deserialize(JsonIn & jsin)205 void relic_procgen_data::generation_rules::deserialize( JsonIn &jsin )
206 {
207     JsonObject jo = jsin.get_object();
208     load( jo );
209 }
210 
deserialize(JsonIn & jsin)211 void relic_procgen_data::deserialize( JsonIn &jsin )
212 {
213     JsonObject jobj = jsin.get_object();
214     load( jobj );
215 }
216 
deserialize(JsonIn & jsin)217 void relic_charge_template::deserialize( JsonIn &jsin )
218 {
219     load( jsin.get_object() );
220 }
221 
load(const JsonObject & jo)222 void relic_charge_template::load( const JsonObject &jo )
223 {
224     int tmp_power = 0;
225 
226     const JsonObject max_charge = jo.get_object( "max_charges" );
227     max_charge.read( "range", max_charges );
228     max_charge.read( "power", tmp_power );
229     power_level += tmp_power;
230 
231     const JsonObject charge = jo.get_object( "charges" );
232     charge.read( "range", init_charges );
233     charge.read( "power", tmp_power );
234     power_level += tmp_power;
235 
236     const JsonObject init_charge = jo.get_object( "charges_per_use" );
237     init_charge.read( "range", charges_per_use );
238     init_charge.read( "power", tmp_power );
239     power_level += tmp_power;
240 
241     jo.read( "recharge_type", type );
242     jo.read( "time", time );
243 }
244 
generate() const245 relic_charge_info relic_charge_template::generate() const
246 {
247     relic_charge_info ret;
248     ret.max_charges = rng( max_charges.first, max_charges.second );
249     ret.charges_per_use = rng( charges_per_use.first, charges_per_use.second );
250     ret.charges = std::min( rng( init_charges.first, init_charges.second ), ret.max_charges );
251     ret.activation_time = rng( time.first, time.second );
252     ret.type = type;
253     ret.power = power_level;
254     return ret;
255 }
256 
deserialize(JsonIn & jsin)257 void relic_charge_info::deserialize( JsonIn &jsin )
258 {
259     load( jsin.get_object() );
260 }
261 
load(const JsonObject & jo)262 void relic_charge_info::load( const JsonObject &jo )
263 {
264     jo.read( "charges", charges );
265     jo.read( "charges_per_use", charges_per_use );
266     jo.read( "max_charges", max_charges );
267     jo.read( "recharge_type", type );
268     jo.read( "regenerate_ammo", regenerate_ammo );
269     jo.read( "activation_accumulator", activation_accumulator );
270     jo.read( "time", activation_time );
271 }
272 
serialize(JsonOut & jsout) const273 void relic_charge_info::serialize( JsonOut &jsout ) const
274 {
275     jsout.start_object();
276     jsout.member( "charges", charges );
277     jsout.member( "charges_per_use", charges_per_use );
278     jsout.member( "max_charges", max_charges );
279     jsout.member( "regenerate_ammo", regenerate_ammo );
280     jsout.member( "recharge_type", type );
281     jsout.member( "activation_accumulator", activation_accumulator );
282     jsout.member( "time", activation_time );
283     jsout.end_object();
284 }
285 
accumulate_charge(item & parent)286 void relic_charge_info::accumulate_charge( item &parent )
287 {
288     const bool time = activation_time == 0_seconds;
289     const bool regen_ammo = regenerate_ammo && item_can_not_load_ammo( parent );
290     const bool has_max_charges = !regenerate_ammo && charges >= max_charges && max_charges != 0;
291     if( time || regen_ammo || has_max_charges ) {
292         return;
293     }
294 
295     activation_accumulator += 1_seconds;
296     if( activation_accumulator >= activation_time ) {
297         activation_accumulator -= activation_time;
298         if( regenerate_ammo ) {
299             item *current_magazine = &parent;
300             if( parent.magazine_current() ) {
301                 current_magazine = parent.magazine_current();
302             }
303             const itype_id current_ammo = current_magazine->ammo_current();
304             if( current_ammo == itype_id::NULL_ID() ) {
305                 current_magazine->ammo_set( current_magazine->ammo_default(), 1 );
306             } else {
307                 current_magazine->ammo_set( current_ammo, current_magazine->ammo_remaining() + 1 );
308             }
309         } else {
310             charges++;
311         }
312     }
313 }
314 
load(const JsonObject & jo)315 void relic::load( const JsonObject &jo )
316 {
317     if( jo.has_array( "active_effects" ) ) {
318         for( JsonObject jobj : jo.get_array( "active_effects" ) ) {
319             fake_spell sp;
320             sp.load( jobj );
321             add_active_effect( sp );
322         }
323     }
324     if( jo.has_array( "passive_effects" ) ) {
325         for( JsonObject jobj : jo.get_array( "passive_effects" ) ) {
326             enchantment ench;
327             ench.load( jobj );
328             if( !ench.id.is_empty() ) {
329                 ench = ench.id.obj();
330             }
331             add_passive_effect( ench );
332         }
333     }
334     jo.read( "charge_info", charge );
335     if( jo.has_member( "charges_per_activation" ) ) {
336         charge.charges_per_use = jo.get_int( "charges_per_activation", 1 );
337     }
338     jo.read( "name", item_name_override );
339     moves = jo.get_int( "moves", 100 );
340 }
341 
deserialize(JsonIn & jsin)342 void relic::deserialize( JsonIn &jsin )
343 {
344     JsonObject jobj = jsin.get_object();
345     load( jobj );
346 }
347 
serialize(JsonOut & jsout) const348 void relic::serialize( JsonOut &jsout ) const
349 {
350     jsout.start_object();
351 
352     jsout.member( "moves", moves );
353     // item_name_override is not saved, in case the original json text changes:
354     // in such case names read back from a save wouold no longer be properly translated.
355 
356     if( !passive_effects.empty() ) {
357         jsout.member( "passive_effects" );
358         jsout.start_array();
359         for( const enchantment &ench : passive_effects ) {
360             ench.serialize( jsout );
361         }
362         jsout.end_array();
363     }
364 
365     if( !active_effects.empty() ) {
366         jsout.member( "active_effects" );
367         jsout.start_array();
368         for( const fake_spell &sp : active_effects ) {
369             sp.serialize( jsout );
370         }
371         jsout.end_array();
372     }
373 
374     jsout.member( "charge_info", charge );
375 
376     jsout.end_object();
377 }
378 
activate(Creature & caster,const tripoint & target)379 int relic::activate( Creature &caster, const tripoint &target )
380 {
381     if( charge.charges_per_use != 0 && charges() - charge.charges_per_use < 0 ) {
382         caster.add_msg_if_player( m_bad, _( "This artifact lacks the charges to activate." ) );
383         return 0;
384     }
385     caster.moves -= moves;
386     for( const fake_spell &sp : active_effects ) {
387         spell casting = sp.get_spell( sp.level );
388         casting.cast_all_effects( caster, target );
389         caster.add_msg_if_player( casting.message() );
390     }
391     charge.charges -= charge.charges_per_use;
392     return charge.charges_per_use;
393 }
394 
charges() const395 int relic::charges() const
396 {
397     return charge.charges;
398 }
399 
charges_per_use() const400 int relic::charges_per_use() const
401 {
402     return charge.charges_per_use;
403 }
404 
max_charges() const405 int relic::max_charges() const
406 {
407     return charge.max_charges;
408 }
409 
has_recharge() const410 bool relic::has_recharge() const
411 {
412     return charge.type != relic_recharge::none;
413 }
414 
415 // checks if the relic is in the appropriate location to be able to recharge from the weather.
416 // does not check the weather type, that job is relegated to the switch in relic::try_recharge()
can_recharge_solar(const item & it,Character * carrier,const tripoint & pos)417 static bool can_recharge_solar( const item &it, Character *carrier, const tripoint &pos )
418 {
419     return get_map().is_outside( pos ) && is_day( calendar::turn ) &&
420            ( carrier == nullptr ||
421              carrier->is_worn( it ) || carrier->is_wielding( it ) );
422 }
423 
try_recharge(item & parent,Character * carrier,const tripoint & pos)424 void relic::try_recharge( item &parent, Character *carrier, const tripoint &pos )
425 {
426     if( charge.regenerate_ammo && item_can_not_load_ammo( parent ) ) {
427         return;
428     } else if( !charge.regenerate_ammo && charge.charges >= charge.max_charges ) {
429         return;
430     }
431 
432     switch( charge.type ) {
433         case relic_recharge::none: {
434             return;
435         }
436         case relic_recharge::periodic: {
437             charge.accumulate_charge( parent );
438             return;
439         }
440         case relic_recharge::solar_sunny: {
441             if( can_recharge_solar( parent, carrier, pos ) &&
442                 get_weather().weather_id->light_modifier >= 0 ) {
443                 charge.accumulate_charge( parent );
444             }
445             return;
446         }
447         case relic_recharge::num: {
448             debugmsg( "Attempted to recharge relic with invalid recharge type" );
449             return;
450         }
451     }
452 }
453 
overwrite_charge(const relic_charge_info & info)454 void relic::overwrite_charge( const relic_charge_info &info )
455 {
456     charge = info;
457 }
458 
modify_value(const enchant_vals::mod value_type,const int value) const459 int relic::modify_value( const enchant_vals::mod value_type, const int value ) const
460 {
461     int add_modifier = 0;
462     double multiply_modifier = 0.0;
463     for( const enchantment &ench : passive_effects ) {
464         add_modifier += ench.get_value_add( value_type );
465         multiply_modifier += ench.get_value_multiply( value_type );
466     }
467     multiply_modifier = std::max( multiply_modifier + 1.0, 0.0 );
468     int modified_value;
469     if( multiply_modifier < 1.0 ) {
470         modified_value = std::floor( multiply_modifier * value );
471     } else {
472         modified_value = std::ceil( multiply_modifier * value );
473     }
474     return modified_value + add_modifier;
475 }
476 
name() const477 std::string relic::name() const
478 {
479     return item_name_override.translated();
480 }
481 
get_enchantments() const482 std::vector<enchantment> relic::get_enchantments() const
483 {
484     return passive_effects;
485 }
486 
power_level(const relic_procgen_id & ruleset) const487 int relic::power_level( const relic_procgen_id &ruleset ) const
488 {
489     int total_power_level = 0;
490     for( const enchantment &ench : passive_effects ) {
491         total_power_level += ruleset->power_level( ench );
492     }
493     for( const fake_spell &sp : active_effects ) {
494         total_power_level += ruleset->power_level( sp );
495     }
496     total_power_level += charge.power;
497     return total_power_level;
498 }
499 
has_activation() const500 bool relic::has_activation() const
501 {
502     return !active_effects.empty();
503 }
504 
power_level(const enchantment & ench) const505 int relic_procgen_data::power_level( const enchantment &ench ) const
506 {
507     int power = 0;
508 
509     for( const weighted_object<int, relic_procgen_data::enchantment_value_passive<int>>
510          &add_val_passive : passive_add_procgen_values ) {
511         int val = ench.get_value_add( add_val_passive.obj.type );
512         if( val != 0 ) {
513             power += static_cast<float>( add_val_passive.obj.power_per_increment ) /
514                      static_cast<float>( add_val_passive.obj.increment ) * val;
515         }
516     }
517 
518     for( const weighted_object<int, relic_procgen_data::enchantment_value_passive<float>>
519          &mult_val_passive : passive_mult_procgen_values ) {
520         float val = ench.get_value_multiply( mult_val_passive.obj.type );
521         if( val != 0.0f ) {
522             power += mult_val_passive.obj.power_per_increment / mult_val_passive.obj.increment * val;
523         }
524     }
525 
526     return power;
527 }
528 
power_level(const fake_spell & sp) const529 int relic_procgen_data::power_level( const fake_spell &sp ) const
530 {
531     for( const weighted_object<int, relic_procgen_data::enchantment_active> &vals :
532          active_procgen_values ) {
533         if( vals.obj.activated_spell == sp.id ) {
534             return vals.obj.calc_power( sp.level );
535         }
536     }
537     return 0;
538 }
539 
create_item(const relic_procgen_data::generation_rules & rules) const540 item relic_procgen_data::create_item( const relic_procgen_data::generation_rules &rules ) const
541 {
542     const itype_id *it_id = item_weights.pick();
543     if( it_id == nullptr ) {
544         debugmsg( "ERROR: %s procgen data does not have items", id.c_str() );
545         return null_item_reference();
546     }
547 
548     item it( *it_id, calendar::turn );
549 
550     it.overwrite_relic( generate( rules, *it_id ) );
551 
552     return it;
553 }
554 
generate(const relic_procgen_data::generation_rules & rules,const itype_id & it_id) const555 relic relic_procgen_data::generate( const relic_procgen_data::generation_rules &rules,
556                                     const itype_id &it_id ) const
557 {
558     relic ret;
559     int num_attributes = 0;
560     int negative_attribute_power = 0;
561     const bool is_armor = item( it_id ).is_armor();
562 
563     while( rules.max_attributes > num_attributes && rules.power_level > ret.power_level( id ) ) {
564         switch( *type_weights.pick() ) {
565             case relic_procgen_data::type::active_enchantment: {
566                 const relic_procgen_data::enchantment_active *active = active_procgen_values.pick();
567                 if( active != nullptr ) {
568                     fake_spell active_sp;
569                     active_sp.id = active->activated_spell;
570                     active_sp.level = rng( active->min_level, active->max_level );
571                     num_attributes++;
572                     int power = power_level( active_sp );
573                     if( power < 0 ) {
574                         if( rules.max_negative_power > negative_attribute_power ) {
575                             break;
576                         }
577                         negative_attribute_power += power;
578                     }
579                     ret.add_active_effect( active_sp );
580                 }
581                 break;
582             }
583             case relic_procgen_data::type::passive_enchantment_add: {
584                 const relic_procgen_data::enchantment_value_passive<int> *add = passive_add_procgen_values.pick();
585                 if( add != nullptr ) {
586                     enchantment ench;
587                     int value = rng( add->min_value, add->max_value );
588                     if( value == 0 ) {
589                         break;
590                     }
591                     ench.add_value_add( add->type, value );
592                     num_attributes++;
593                     int negative_ench_attribute = power_level( ench );
594                     if( negative_ench_attribute < 0 ) {
595                         if( rules.max_negative_power > negative_attribute_power ) {
596                             break;
597                         }
598                         negative_attribute_power += negative_ench_attribute;
599                     }
600                     if( is_armor ) {
601                         ench.set_has( enchantment::has::WORN );
602                     } else {
603                         ench.set_has( enchantment::has::WIELD );
604                     }
605                     ret.add_passive_effect( ench );
606                 }
607                 break;
608             }
609             case relic_procgen_data::type::passive_enchantment_mult: {
610                 const relic_procgen_data::enchantment_value_passive<float> *mult =
611                     passive_mult_procgen_values.pick();
612                 if( mult != nullptr ) {
613                     enchantment ench;
614                     float value = rng( mult->min_value, mult->max_value );
615                     ench.add_value_mult( mult->type, value );
616                     num_attributes++;
617                     int negative_ench_attribute = power_level( ench );
618                     if( negative_ench_attribute < 0 ) {
619                         if( rules.max_negative_power > negative_attribute_power ) {
620                             break;
621                         }
622                         negative_attribute_power += negative_ench_attribute;
623                     }
624                     if( is_armor ) {
625                         ench.set_has( enchantment::has::WORN );
626                     } else {
627                         ench.set_has( enchantment::has::WIELD );
628                     }
629                     ret.add_passive_effect( ench );
630                 }
631                 break;
632             }
633             case relic_procgen_data::type::hit_me: {
634                 const relic_procgen_data::enchantment_active *active = passive_hit_me.pick();
635                 if( active != nullptr ) {
636                     fake_spell active_sp;
637                     active_sp.id = active->activated_spell;
638                     active_sp.level = rng( active->min_level, active->max_level );
639                     num_attributes++;
640                     enchantment ench;
641                     ench.add_hit_me( active_sp );
642                     int power = power_level( ench );
643                     if( power < 0 ) {
644                         if( rules.max_negative_power > negative_attribute_power ) {
645                             break;
646                         }
647                         negative_attribute_power += power;
648                     }
649                     if( is_armor ) {
650                         ench.set_has( enchantment::has::WORN );
651                     } else {
652                         ench.set_has( enchantment::has::WIELD );
653                     }
654                     ret.add_passive_effect( ench );
655                 }
656                 break;
657             }
658             case relic_procgen_data::type::hit_you: {
659                 const relic_procgen_data::enchantment_active *active = passive_hit_you.pick();
660                 if( active != nullptr ) {
661                     fake_spell active_sp;
662                     active_sp.id = active->activated_spell;
663                     active_sp.level = rng( active->min_level, active->max_level );
664                     num_attributes++;
665                     enchantment ench;
666                     ench.add_hit_you( active_sp );
667                     int power = power_level( ench );
668                     if( power < 0 ) {
669                         if( rules.max_negative_power > negative_attribute_power ) {
670                             break;
671                         }
672                         negative_attribute_power += power;
673                     }
674                     if( is_armor ) {
675                         ench.set_has( enchantment::has::WORN );
676                     } else {
677                         ench.set_has( enchantment::has::WIELD );
678                     }
679                     ret.add_passive_effect( ench );
680                 }
681                 break;
682             }
683             case relic_procgen_data::type::last: {
684                 debugmsg( "ERROR: invalid relic procgen type" );
685                 break;
686             }
687         }
688     }
689 
690     if( ret.has_activation() ) {
691         const relic_charge_template *charge = charge_values.pick();
692         if( charge != nullptr ) {
693             ret.overwrite_charge( charge->generate() );
694         }
695     }
696 
697     return ret;
698 }
699 
operator ==(const relic & source_relic,const relic & target_relic)700 bool operator==( const relic &source_relic, const relic &target_relic )
701 {
702     bool is_the_same = true;
703     is_the_same &= ( source_relic.charges() == target_relic.charges() );
704     is_the_same &= ( source_relic.charges_per_use() == target_relic.charges_per_use() );
705     is_the_same &= ( source_relic.has_activation() == target_relic.has_activation() );
706     is_the_same &= ( source_relic.has_recharge() == target_relic.has_recharge() );
707     is_the_same &= ( source_relic.max_charges() == target_relic.max_charges() );
708     is_the_same &= ( source_relic.name() == target_relic.name() );
709 
710     is_the_same &= ( source_relic.get_enchantments().size() == target_relic.get_enchantments().size() );
711     if( is_the_same ) {
712         for( std::size_t i = 0; i < source_relic.get_enchantments().size(); i++ ) {
713             is_the_same &= source_relic.get_enchantments()[i] == target_relic.get_enchantments()[i];
714         }
715     }
716     return is_the_same;
717 }
718