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