@@ -762,6 +762,68 @@ void revive_type::deserialize( const JsonObject &jo )
762
762
}
763
763
}
764
764
765
+ // this is a really gross reader - special_attacks and special_attacks_names really need to be one struct
766
+ // Then this gets a lot less gross and a lot more safe!
767
+ struct special_attacks_reader : generic_typed_reader<special_attacks_reader> {
768
+ friend class MonsterGenerator ;
769
+ // For ordering purposes, we want to also grab the names when we load a special attack
770
+ std::vector<std::string> &names;
771
+
772
+ // special attacks load with src
773
+ std::string_view src;
774
+ mtype_id id;
775
+
776
+ special_attacks_reader ( std::vector<std::string> &name_vec, std::string_view _src,
777
+ mtype_id mtype ) : names( name_vec ), src( _src ), id( mtype ) {}
778
+
779
+ void report_double_def ( const std::string &name, const JsonValue &jv ) const {
780
+ if ( std::find ( names.begin (), names.end (), name ) != names.end () ) {
781
+ jv.throw_error (
782
+ string_format ( " %s specifies more than one attack of (sub)type %s, ignoring all but the last. Add different `id`s to each attack of this type to prevent this." ,
783
+ id.c_str (), name ) );
784
+ }
785
+ }
786
+
787
+ std::pair<std::string, mtype_special_attack> get_next ( const JsonValue &jv ) const {
788
+ MonsterGenerator &gen = MonsterGenerator::generator ();
789
+ // only for delete
790
+ if ( jv.test_string () ) {
791
+ std::string name = jv.get_string ();
792
+ auto it = std::find ( names.begin (), names.end (), name );
793
+ if ( it == names.end () ) {
794
+ jv.throw_error (
795
+ string_format ( " Invalid special attack format or does not exist to delete for %s on %s" ,
796
+ name, id.str () ) );
797
+ }
798
+ names.erase ( it );
799
+ return std::make_pair ( name, mtype_special_attack ( std::make_unique<invalid_mattack_actor>() ) );
800
+ }
801
+ if ( jv.test_object () ) {
802
+ mtype_special_attack new_attack = gen.create_actor ( jv.get_object (), std::string ( src ) );
803
+ report_double_def ( new_attack->id , jv );
804
+ names.push_back ( new_attack->id );
805
+ return std::make_pair ( new_attack->id , new_attack );
806
+ }
807
+ JsonArray inner = jv.get_array ();
808
+ std::string name = inner.get_string ( 0 );
809
+ const auto iter = gen.attack_map .find ( name );
810
+ if ( iter == gen.attack_map .end () ) {
811
+ inner.throw_error ( " Invalid special_attacks" );
812
+ }
813
+ report_double_def ( name, jv );
814
+
815
+ mtype_special_attack new_attack = mtype_special_attack ( iter->second );
816
+ if ( inner.has_array ( 1 ) ) {
817
+ new_attack.actor ->cooldown .min = get_dbl_or_var_part ( inner.get_array ( 1 )[0 ] );
818
+ new_attack.actor ->cooldown .max = get_dbl_or_var_part ( inner.get_array ( 1 )[1 ] );
819
+ } else {
820
+ new_attack.actor ->cooldown .min = get_dbl_or_var_part ( inner[1 ] );
821
+ }
822
+ names.push_back ( name );
823
+ return std::make_pair ( name, new_attack );
824
+ }
825
+ };
826
+
765
827
void mtype::load ( const JsonObject &jo, const std::string &src )
766
828
{
767
829
MonsterGenerator &gen = MonsterGenerator::generator ();
@@ -958,24 +1020,18 @@ void mtype::load( const JsonObject &jo, const std::string &src )
958
1020
def_chance = 0 ;
959
1021
}
960
1022
961
- if ( !was_loaded || jo. has_member ( " special_attacks" ) ) {
962
- special_attacks. clear ();
1023
+ // this is really gross, and special_attacks + special_attacks_names should be combined into one struct
1024
+ if ( jo. has_member ( " special_attacks " ) ) {
963
1025
special_attacks_names.clear ();
964
- add_special_attacks ( jo, " special_attacks" , src );
965
- } else {
966
- // Note: special_attacks left as is, new attacks are added to it!
967
- // Note: member name prefixes are compatible with those used by generic_typed_reader
968
- if ( jo.has_object ( " extend" ) ) {
969
- JsonObject tmp = jo.get_object ( " extend" );
970
- tmp.allow_omitted_members ();
971
- add_special_attacks ( tmp, " special_attacks" , src );
972
- }
973
- if ( jo.has_object ( " delete" ) ) {
974
- JsonObject tmp = jo.get_object ( " delete" );
975
- tmp.allow_omitted_members ();
976
- remove_special_attacks ( tmp, " special_attacks" , src );
1026
+ }
1027
+ optional ( jo, was_loaded, " special_attacks" , special_attacks, special_attacks_reader{special_attacks_names, src, id} );
1028
+ // to be extra safe
1029
+ for ( const std::pair<const std::string, mtype_special_attack> &atk : special_attacks ) {
1030
+ if ( dynamic_cast <const invalid_mattack_actor *>( atk.second .get () ) != nullptr ) {
1031
+ jo.throw_error ( string_format ( " Invalid special attack format for %s" , atk.first ) );
977
1032
}
978
1033
}
1034
+
979
1035
optional ( jo, was_loaded, " melee_training_cap" , melee_training_cap, std::min ( melee_skill + 2 ,
980
1036
MAX_SKILL ) );
981
1037
optional ( jo, was_loaded, " chat_topics" , chat_topics );
@@ -1326,6 +1382,8 @@ mtype_special_attack MonsterGenerator::create_actor( const JsonObject &obj,
1326
1382
new_attack = std::make_unique<gun_actor>();
1327
1383
} else if ( attack_type == " spell" ) {
1328
1384
new_attack = std::make_unique<mon_spellcasting_actor>();
1385
+ } else if ( attack_type == " invalid" ) {
1386
+ new_attack = std::make_unique<invalid_mattack_actor>();
1329
1387
} else {
1330
1388
obj.throw_error_at ( " attack_type" , " unknown monster attack" );
1331
1389
}
@@ -1356,87 +1414,6 @@ void MonsterGenerator::load_monster_attack( const JsonObject &jo, const std::str
1356
1414
add_attack ( create_actor ( jo, src ) );
1357
1415
}
1358
1416
1359
- void mtype::add_special_attack ( const JsonObject &obj, const std::string &src )
1360
- {
1361
- mtype_special_attack new_attack = MonsterGenerator::generator ().create_actor ( obj, src );
1362
-
1363
- if ( special_attacks.count ( new_attack->id ) > 0 ) {
1364
- special_attacks.erase ( new_attack->id );
1365
- const auto iter = std::find ( special_attacks_names.begin (), special_attacks_names.end (),
1366
- new_attack->id );
1367
- if ( iter != special_attacks_names.end () ) {
1368
- special_attacks_names.erase ( iter );
1369
- }
1370
- debugmsg ( " %s specifies more than one attack of (sub)type %s, ignoring all but the last. Add different `id`s to each attack of this type to prevent this." ,
1371
- id.c_str (), new_attack->id .c_str () );
1372
- }
1373
-
1374
- special_attacks.emplace ( new_attack->id , new_attack );
1375
- special_attacks_names.push_back ( new_attack->id );
1376
- }
1377
-
1378
- void mtype::add_special_attack ( const JsonArray &inner, std::string_view )
1379
- {
1380
- MonsterGenerator &gen = MonsterGenerator::generator ();
1381
- const std::string name = inner.get_string ( 0 );
1382
- const auto iter = gen.attack_map .find ( name );
1383
- if ( iter == gen.attack_map .end () ) {
1384
- inner.throw_error ( " Invalid special_attacks" );
1385
- }
1386
-
1387
- if ( special_attacks.count ( name ) > 0 ) {
1388
- special_attacks.erase ( name );
1389
- const auto iter = std::find ( special_attacks_names.begin (), special_attacks_names.end (), name );
1390
- if ( iter != special_attacks_names.end () ) {
1391
- special_attacks_names.erase ( iter );
1392
- }
1393
- if ( test_mode ) {
1394
- debugmsg ( " %s specifies more than one attack of (sub)type %s, ignoring all but the last" ,
1395
- id.c_str (), name );
1396
- }
1397
- }
1398
- mtype_special_attack new_attack = mtype_special_attack ( iter->second );
1399
- if ( inner.has_array ( 1 ) ) {
1400
- new_attack.actor ->cooldown .min = get_dbl_or_var_part ( inner.get_array ( 1 )[0 ] );
1401
- new_attack.actor ->cooldown .max = get_dbl_or_var_part ( inner.get_array ( 1 )[1 ] );
1402
- } else {
1403
- new_attack.actor ->cooldown .min = get_dbl_or_var_part ( inner[1 ] );
1404
- }
1405
- special_attacks.emplace ( name, new_attack );
1406
- special_attacks_names.push_back ( name );
1407
- }
1408
-
1409
- void mtype::add_special_attacks ( const JsonObject &jo, std::string_view member,
1410
- const std::string &src )
1411
- {
1412
-
1413
- if ( !jo.has_array ( member ) ) {
1414
- return ;
1415
- }
1416
-
1417
- for ( const JsonValue entry : jo.get_array ( member ) ) {
1418
- if ( entry.test_array () ) {
1419
- add_special_attack ( entry.get_array (), src );
1420
- } else if ( entry.test_object () ) {
1421
- add_special_attack ( entry.get_object (), src );
1422
- } else {
1423
- entry.throw_error ( " array element is neither array nor object." );
1424
- }
1425
- }
1426
- }
1427
-
1428
- void mtype::remove_special_attacks ( const JsonObject &jo, std::string_view member_name,
1429
- std::string_view )
1430
- {
1431
- for ( const std::string &name : jo.get_tags ( member_name ) ) {
1432
- special_attacks.erase ( name );
1433
- const auto iter = std::find ( special_attacks_names.begin (), special_attacks_names.end (), name );
1434
- if ( iter != special_attacks_names.end () ) {
1435
- special_attacks_names.erase ( iter );
1436
- }
1437
- }
1438
- }
1439
-
1440
1417
void MonsterGenerator::check_monster_definitions () const
1441
1418
{
1442
1419
for ( const mtype &mon : mon_templates->get_all () ) {
0 commit comments