1 // Associated headers here are the ones for which their only non-inline
2 // functions are serialization functions.  This allows IWYU to check the
3 // includes in such headers.
4 
5 #include "enums.h" // IWYU pragma: associated
6 #include "npc_favor.h" // IWYU pragma: associated
7 #include "pldata.h" // IWYU pragma: associated
8 
9 #include <algorithm>
10 #include <array>
11 #include <bitset>
12 #include <climits>
13 #include <cmath>
14 #include <cstdint>
15 #include <cstdlib>
16 #include <functional>
17 #include <iterator>
18 #include <limits>
19 #include <list>
20 #include <map>
21 #include <memory>
22 #include <new>
23 #include <numeric>
24 #include <set>
25 #include <sstream>
26 #include <stack>
27 #include <string>
28 #include <tuple>
29 #include <unordered_map>
30 #include <unordered_set>
31 #include <utility>
32 #include <vector>
33 
34 #include "active_item_cache.h"
35 #include "activity_actor.h"
36 #include "activity_type.h"
37 #include "assign.h"
38 #include "auto_pickup.h"
39 #include "avatar.h"
40 #include "basecamp.h"
41 #include "bionics.h"
42 #include "bodypart.h"
43 #include "calendar.h"
44 #include "cata_io.h"
45 #include "cata_utility.h"
46 #include "cata_variant.h"
47 #include "character.h"
48 #include "character_id.h"
49 #include "character_martial_arts.h"
50 #include "clone_ptr.h"
51 #include "clzones.h"
52 #include "colony.h"
53 #include "computer.h"
54 #include "construction.h"
55 #include "coordinates.h"
56 #include "craft_command.h"
57 #include "creature.h"
58 #include "creature_tracker.h"
59 #include "damage.h"
60 #include "debug.h"
61 #include "dialogue_chatbin.h"
62 #include "effect.h"
63 #include "effect_source.h"
64 #include "event.h"
65 #include "faction.h"
66 #include "field.h"
67 #include "field_type.h"
68 #include "flag.h"
69 #include "flat_set.h"
70 #include "game.h"
71 #include "game_constants.h"
72 #include "inventory.h"
73 #include "item.h"
74 #include "item_contents.h"
75 #include "item_factory.h"
76 #include "item_location.h"
77 #include "item_pocket.h"
78 #include "itype.h"
79 #include "json.h"
80 #include "kill_tracker.h"
81 #include "lru_cache.h"
82 #include "magic.h"
83 #include "magic_teleporter_list.h"
84 #include "map.h"
85 #include "map_memory.h"
86 #include "mapdata.h"
87 #include "mattack_common.h"
88 #include "memory_fast.h"
89 #include "mission.h"
90 #include "monster.h"
91 #include "morale.h"
92 #include "morale_types.h"
93 #include "mtype.h"
94 #include "npc.h"
95 #include "npc_class.h"
96 #include "optional.h"
97 #include "options.h"
98 #include "overmapbuffer.h"
99 #include "pimpl.h"
100 #include "player.h"
101 #include "player_activity.h"
102 #include "point.h"
103 #include "profession.h"
104 #include "proficiency.h"
105 #include "recipe.h"
106 #include "recipe_dictionary.h"
107 #include "relic.h"
108 #include "requirements.h"
109 #include "ret_val.h"
110 #include "rng.h"
111 #include "scenario.h"
112 #include "skill.h"
113 #include "stats_tracker.h"
114 #include "stomach.h"
115 #include "submap.h"
116 #include "text_snippets.h"
117 #include "tileray.h"
118 #include "units.h"
119 #include "value_ptr.h"
120 #include "veh_type.h"
121 #include "vehicle.h"
122 #include "vitamin.h"
123 #include "vpart_position.h"
124 #include "vpart_range.h"
125 #include "weather.h"
126 
127 struct mutation_branch;
128 struct oter_type_t;
129 
130 static const efftype_id effect_riding( "riding" );
131 
132 static const itype_id itype_rad_badge( "rad_badge" );
133 static const itype_id itype_radio( "radio" );
134 static const itype_id itype_radio_on( "radio_on" );
135 static const itype_id itype_usb_drive( "usb_drive" );
136 
137 static const ter_str_id ter_t_ash( "t_ash" );
138 static const ter_str_id ter_t_rubble( "t_rubble" );
139 static const ter_str_id ter_t_pwr_sb_support_l( "t_pwr_sb_support_l" );
140 static const ter_str_id ter_t_pwr_sb_switchgear_l( "t_pwr_sb_switchgear_l" );
141 static const ter_str_id ter_t_pwr_sb_switchgear_s( "t_pwr_sb_switchgear_s" );
142 static const ter_str_id ter_t_wreckage( "t_wreckage" );
143 
144 static const std::array<std::string, static_cast<size_t>( object_type::NUM_OBJECT_TYPES )>
145 obj_type_name = { { "OBJECT_NONE", "OBJECT_ITEM", "OBJECT_ACTOR", "OBJECT_PLAYER",
146         "OBJECT_NPC", "OBJECT_MONSTER", "OBJECT_VEHICLE", "OBJECT_TRAP", "OBJECT_FIELD",
147         "OBJECT_TERRAIN", "OBJECT_FURNITURE"
148     }
149 };
150 
151 // TODO: investigate serializing other members of the Creature class hierarchy
serialize(const weak_ptr_fast<monster> & obj,JsonOut & jsout)152 static void serialize( const weak_ptr_fast<monster> &obj, JsonOut &jsout )
153 {
154     if( const auto monster_ptr = obj.lock() ) {
155         jsout.start_object();
156 
157         jsout.member( "monster_at", monster_ptr->pos() );
158         // TODO: if monsters/Creatures ever get unique ids,
159         // create a differently named member, e.g.
160         //     jsout.member("unique_id", monster_ptr->getID());
161         jsout.end_object();
162     } else {
163         // Monster went away. It's up the activity handler to detect this.
164         jsout.write_null();
165     }
166 }
167 
deserialize(weak_ptr_fast<monster> & obj,JsonIn & jsin)168 static void deserialize( weak_ptr_fast<monster> &obj, JsonIn &jsin )
169 {
170     JsonObject data = jsin.get_object();
171     data.allow_omitted_members();
172     tripoint temp_pos;
173 
174     obj.reset();
175     if( data.read( "monster_at", temp_pos ) ) {
176         const auto monp = g->critter_tracker->find( temp_pos );
177 
178         if( monp == nullptr ) {
179             debugmsg( "no monster found at %d,%d,%d", temp_pos.x, temp_pos.y, temp_pos.z );
180             return;
181         }
182 
183         obj = monp;
184     }
185 
186     // TODO: if monsters/Creatures ever get unique ids,
187     // look for a differently named member, e.g.
188     //     data.read( "unique_id", unique_id );
189     //     obj = g->id_registry->from_id( unique_id)
190     //    }
191 }
192 
serialize(JsonOut & json) const193 void item_contents::serialize( JsonOut &json ) const
194 {
195     if( !contents.empty() ) {
196         json.start_object();
197 
198         json.member( "contents", contents );
199 
200         json.end_object();
201     }
202 }
203 
deserialize(JsonIn & jsin)204 void item_contents::deserialize( JsonIn &jsin )
205 {
206     JsonObject data = jsin.get_object();
207     data.allow_omitted_members();
208     data.read( "contents", contents );
209 }
210 
serialize(JsonOut & json) const211 void item_pocket::serialize( JsonOut &json ) const
212 {
213     json.start_object();
214     json.member( "pocket_type", data->type );
215     json.member( "contents", contents );
216     json.member( "_sealed", _sealed );
217     if( !this->settings.is_null() ) {
218         json.member( "favorite_settings", this->settings );
219     }
220     json.end_object();
221 }
222 
deserialize(JsonIn & jsin)223 void item_pocket::deserialize( JsonIn &jsin )
224 {
225     JsonObject data = jsin.get_object();
226     data.allow_omitted_members();
227     data.read( "contents", contents );
228     int saved_type_int;
229     data.read( "pocket_type", saved_type_int );
230     _saved_type = static_cast<item_pocket::pocket_type>( saved_type_int );
231     data.read( "_sealed", _sealed );
232     _saved_sealed = _sealed;
233     if( data.has_member( "favorite_settings" ) ) {
234         data.read( "favorite_settings", this->settings );
235     } else {
236         this->settings.clear();
237     }
238 }
239 
serialize(JsonOut & json) const240 void item_pocket::favorite_settings::serialize( JsonOut &json ) const
241 {
242     json.start_object();
243     json.member( "priority", priority_rating );
244     json.member( "item_whitelist", item_whitelist );
245     json.member( "item_blacklist", item_blacklist );
246     json.member( "category_whitelist", category_whitelist );
247     json.member( "category_blacklist", category_blacklist );
248     json.end_object();
249 }
250 
deserialize(JsonIn & jsin)251 void item_pocket::favorite_settings::deserialize( JsonIn &jsin )
252 {
253     JsonObject data = jsin.get_object();
254     data.allow_omitted_members();
255     data.read( "priority", priority_rating );
256     data.read( "item_whitelist", item_whitelist );
257     data.read( "item_blacklist", item_blacklist );
258     data.read( "category_whitelist", category_whitelist );
259     data.read( "category_blacklist", category_blacklist );
260 }
261 
deserialize(JsonIn & jsin)262 void pocket_data::deserialize( JsonIn &jsin )
263 {
264     JsonObject data = jsin.get_object();
265     data.allow_omitted_members();
266     load( data );
267 }
268 
deserialize(JsonIn & jsin)269 void sealable_data::deserialize( JsonIn &jsin )
270 {
271     JsonObject data = jsin.get_object();
272     data.allow_omitted_members();
273     load( data );
274 }
275 
276 ////////////////////////////////////////////////////////////////////////////////////////////////////
277 ///// player_activity.h
278 
serialize(JsonOut & json) const279 void player_activity::serialize( JsonOut &json ) const
280 {
281     json.start_object();
282     json.member( "type", type );
283 
284     if( !type.is_null() ) {
285         json.member( "actor", actor );
286         json.member( "moves_left", moves_left );
287         json.member( "index", index );
288         json.member( "position", position );
289         json.member( "coords", coords );
290         json.member( "coord_set", coord_set );
291         json.member( "name", name );
292         json.member( "targets", targets );
293         json.member( "placement", placement );
294         json.member( "values", values );
295         json.member( "str_values", str_values );
296         json.member( "auto_resume", auto_resume );
297         json.member( "monsters", monsters );
298     }
299     json.end_object();
300 }
301 
deserialize(JsonIn & jsin)302 void player_activity::deserialize( JsonIn &jsin )
303 {
304     JsonObject data = jsin.get_object();
305     data.allow_omitted_members();
306     std::string tmptype;
307     int tmppos = 0;
308 
309     bool is_obsolete = false;
310     std::set<std::string> obs_activities {
311         "ACT_MAKE_ZLAVE" // Remove after 0.F
312     };
313     if( !data.read( "type", tmptype ) ) {
314         // Then it's a legacy save.
315         int tmp_type_legacy = data.get_int( "type" );
316         deserialize_legacy_type( tmp_type_legacy, type );
317     } else if( !obs_activities.count( tmptype ) ) {
318         type = activity_id( tmptype );
319     } else {
320         is_obsolete = true;
321     }
322 
323     if( type.is_null() ) {
324         return;
325     }
326 
327     const bool has_actor = activity_actors::deserialize_functions.find( type ) !=
328                            activity_actors::deserialize_functions.end();
329 
330     // Handle migration of pre-activity_actor activities
331     // ACT_MIGRATION_CANCEL will clear the backlog and reset npc state
332     // this may cause inconvenience but should avoid any lasting damage to npcs
333     if( is_obsolete || ( has_actor && ( data.has_null( "actor" ) || !data.has_member( "actor" ) ) ) ) {
334         type = activity_id( "ACT_MIGRATION_CANCEL" );
335     }
336 
337     if( !data.read( "position", tmppos ) ) {
338         tmppos = INT_MIN;  // If loading a save before position existed, hope.
339     }
340 
341     data.read( "actor", actor );
342     data.read( "moves_left", moves_left );
343     data.read( "index", index );
344     position = tmppos;
345     data.read( "coords", coords );
346     data.read( "coord_set", coord_set );
347     data.read( "name", name );
348     data.read( "targets", targets );
349     data.read( "placement", placement );
350     values = data.get_int_array( "values" );
351     str_values = data.get_string_array( "str_values" );
352     data.read( "auto_resume", auto_resume );
353     data.read( "monsters", monsters );
354 
355 }
356 
357 ////////////////////////////////////////////////////////////////////////////////////////////////////
358 ///// requirements.h
serialize(JsonOut & json) const359 void requirement_data::serialize( JsonOut &json ) const
360 {
361     json.start_object();
362 
363     if( !is_null() ) {
364         json.member( "blacklisted", blacklisted );
365         const std::vector<std::vector<item_comp>> req_comps = get_components();
366         const std::vector<std::vector<tool_comp>> tool_comps = get_tools();
367         const std::vector<std::vector<quality_requirement>> quality_comps = get_qualities();
368 
369         json.member( "req_comps_total", req_comps );
370 
371         json.member( "tool_comps_total", tool_comps );
372 
373         json.member( "quality_comps_total", quality_comps );
374     }
375     json.end_object();
376 }
377 
deserialize(JsonIn & jsin)378 void requirement_data::deserialize( JsonIn &jsin )
379 {
380     JsonObject data = jsin.get_object();
381     data.allow_omitted_members();
382 
383     data.read( "blacklisted", blacklisted );
384 
385     data.read( "req_comps_total", components );
386     data.read( "tool_comps_total", tools );
387     data.read( "quality_comps_total", qualities );
388 
389 }
390 
391 ////////////////////////////////////////////////////////////////////////////////////////////////////
392 ///// skill.h
serialize(JsonOut & json) const393 void SkillLevel::serialize( JsonOut &json ) const
394 {
395     json.start_object();
396     json.member( "level", level() );
397     json.member( "exercise", exercise( true ) );
398     json.member( "istraining", isTraining() );
399     json.member( "lastpracticed", _lastPracticed );
400     json.member( "highestlevel", highestLevel() );
401     json.end_object();
402 }
403 
deserialize(JsonIn & jsin)404 void SkillLevel::deserialize( JsonIn &jsin )
405 {
406     JsonObject data = jsin.get_object();
407     data.allow_omitted_members();
408     data.read( "level", _level );
409     data.read( "exercise", _exercise );
410     data.read( "istraining", _isTraining );
411     if( !data.read( "lastpracticed", _lastPracticed ) ) {
412         _lastPracticed = calendar::start_of_cataclysm + time_duration::from_hours(
413                              get_option<int>( "INITIAL_TIME" ) );
414     }
415     data.read( "highestlevel", _highestLevel );
416     if( _highestLevel < _level ) {
417         _highestLevel = _level;
418     }
419 }
420 
421 ////////////////////////////////////////////////////////////////////////////////////////////////////
422 ///// character_id.h
423 
serialize(JsonOut & jsout) const424 void character_id::serialize( JsonOut &jsout ) const
425 {
426     jsout.write( value );
427 }
428 
deserialize(JsonIn & jsin)429 void character_id::deserialize( JsonIn &jsin )
430 {
431     value = jsin.get_int();
432 }
433 
434 ////////////////////////////////////////////////////////////////////////////////////////////////////
435 ///// effect_source.h
436 
serialize(JsonOut & json) const437 void effect_source::serialize( JsonOut &json ) const
438 {
439     json.start_object();
440     json.member( "character_id", this->character );
441     json.member( "faction_id", this->fac );
442     if( this->mfac ) {
443         json.member( "mfaction_id", this->mfac->id().str() );
444     }
445     json.end_object();
446 }
447 
deserialize(JsonIn & jsin)448 void effect_source::deserialize( JsonIn &jsin )
449 {
450     JsonObject data = jsin.get_object();
451     data.allow_omitted_members();
452     data.read( "character_id", this->character );
453     data.read( "faction_id", this->fac );
454     const std::string mfac_id = data.get_string( "mfaction_id", "" );
455     this->mfac = !mfac_id.empty()
456                  ? cata::optional<mfaction_id>( mfaction_str_id( mfac_id ).id() )
457                  : cata::optional<mfaction_id>();
458 }
459 
460 ////////////////////////////////////////////////////////////////////////////////////////////////////
461 ///// Character.h, avatar + npc
462 
serialize(JsonOut & json) const463 void Character::trait_data::serialize( JsonOut &json ) const
464 {
465     json.start_object();
466     json.member( "key", key );
467     json.member( "charge", charge );
468     json.member( "powered", powered );
469     json.end_object();
470 }
471 
deserialize(JsonIn & jsin)472 void Character::trait_data::deserialize( JsonIn &jsin )
473 {
474     JsonObject data = jsin.get_object();
475     data.allow_omitted_members();
476     data.read( "key", key );
477     data.read( "charge", charge );
478     data.read( "powered", powered );
479 }
480 
serialize(JsonOut & json) const481 void consumption_event::serialize( JsonOut &json ) const
482 {
483     json.start_object();
484     json.member( "time", time );
485     json.member( "type_id", type_id );
486     json.member( "component_hash", component_hash );
487     json.end_object();
488 }
489 
deserialize(JsonIn & jsin)490 void consumption_event::deserialize( JsonIn &jsin )
491 {
492     JsonObject jo = jsin.get_object();
493     jo.allow_omitted_members();
494     jo.read( "time", time );
495     jo.read( "type_id", type_id );
496     jo.read( "component_hash", component_hash );
497 }
498 
serialize(JsonOut & json) const499 void activity_tracker::serialize( JsonOut &json ) const
500 {
501     json.start_object();
502     json.member( "current_activity", current_activity );
503     json.member( "accumulated_activity", accumulated_activity );
504     json.member( "previous_activity", previous_activity );
505     json.member( "current_turn", current_turn );
506     json.member( "activity_reset", activity_reset );
507     json.member( "num_events", num_events );
508 
509     json.member( "tracker", tracker );
510     json.member( "intake", intake );
511     json.member( "low_activity_ticks", low_activity_ticks );
512     json.member( "tick_counter", tick_counter );
513     json.end_object();
514 }
515 
deserialize(JsonIn & jsin)516 void activity_tracker::deserialize( JsonIn &jsin )
517 {
518     JsonObject jo = jsin.get_object();
519 
520     jo.allow_omitted_members();
521     jo.read( "current_activity", current_activity );
522     jo.read( "accumulated_activity", accumulated_activity );
523     jo.read( "previous_activity", previous_activity );
524     jo.read( "current_turn", current_turn );
525     jo.read( "activity_reset", activity_reset );
526     jo.read( "num_events", num_events );
527 
528     jo.read( "tracker", tracker );
529     jo.read( "intake", intake );
530     jo.read( "low_activity_ticks", low_activity_ticks );
531     jo.read( "tick_counter", tick_counter );
532 }
533 
534 // migration handling of items that used to have charges instead of real items.
535 // remove this migration funciton after 0.F
migrate_item_charges(item & it)536 static void migrate_item_charges( item &it )
537 {
538     if( it.charges != 0 && it.contents.has_pocket_type( item_pocket::pocket_type::MAGAZINE ) ) {
539         it.ammo_set( it.ammo_default(), it.charges );
540         it.charges = 0;
541     }
542 }
543 
544 /**
545  * Gather variables for saving. These variables are common to both the avatar and NPCs.
546  */
load(const JsonObject & data)547 void Character::load( const JsonObject &data )
548 {
549     data.allow_omitted_members();
550     Creature::load( data );
551 
552     if( !data.read( "posx", position.x ) ) {  // uh-oh.
553         debugmsg( "BAD PLAYER/NPC JSON: no 'posx'?" );
554     }
555     data.read( "posy", position.y );
556     if( !data.read( "posz", position.z ) && g != nullptr ) {
557         position.z = get_map().get_abs_sub().z;
558     }
559     // stats
560     data.read( "str_cur", str_cur );
561     data.read( "str_max", str_max );
562     data.read( "dex_cur", dex_cur );
563     data.read( "dex_max", dex_max );
564     data.read( "int_cur", int_cur );
565     data.read( "int_max", int_max );
566     data.read( "per_cur", per_cur );
567     data.read( "per_max", per_max );
568 
569     data.read( "str_bonus", str_bonus );
570     data.read( "dex_bonus", dex_bonus );
571     data.read( "per_bonus", per_bonus );
572     data.read( "int_bonus", int_bonus );
573     data.read( "omt_path", omt_path );
574 
575     data.read( "base_age", init_age );
576     data.read( "base_height", init_height );
577     if( !data.read( "blood_type", my_blood_type ) ||
578         !data.read( "blood_rh_factor", blood_rh_factor ) ) {
579         randomize_blood();
580     }
581 
582     data.read( "custom_profession", custom_profession );
583 
584     // needs
585     data.read( "thirst", thirst );
586     data.read( "hunger", hunger );
587     data.read( "fatigue", fatigue );
588     // Legacy read, remove after 0.F
589     data.read( "weary", activity_history );
590     data.read( "activity_history", activity_history );
591     data.read( "sleep_deprivation", sleep_deprivation );
592     data.read( "stored_calories", stored_calories );
593     // stored_calories was changed from being in kcal to being in just cal
594     if( savegame_loading_version <= 31 ) {
595         stored_calories *= 1000;
596     }
597     data.read( "radiation", radiation );
598     data.read( "oxygen", oxygen );
599     data.read( "pkill", pkill );
600 
601     data.read( "type_of_scent", type_of_scent );
602 
603     if( data.has_array( "ma_styles" ) ) {
604         std::vector<matype_id> temp_styles;
605         data.read( "ma_styles", temp_styles );
606         bool temp_keep_hands_free = false;
607         data.read( "keep_hands_free", temp_keep_hands_free );
608         matype_id temp_selected_style;
609         data.read( "style_selected", temp_selected_style );
610         if( !temp_selected_style.is_valid() ) {
611             temp_selected_style = matype_id( "style_none" );
612         }
613         *martial_arts_data = character_martial_arts( temp_styles, temp_selected_style,
614                              temp_keep_hands_free );
615     } else {
616         data.read( "martial_arts_data", martial_arts_data );
617     }
618 
619     JsonObject vits = data.get_object( "vitamin_levels" );
620     vits.allow_omitted_members();
621     for( const std::pair<const vitamin_id, vitamin> &v : vitamin::all() ) {
622         if( vits.has_member( v.first.str() ) ) {
623             int lvl = vits.get_int( v.first.str() );
624             vitamin_levels[v.first] = clamp( lvl, v.first->min(), v.first->max() );
625         }
626     }
627     data.read( "consumption_history", consumption_history );
628     data.read( "activity", activity );
629     data.read( "destination_activity", destination_activity );
630     data.read( "stashed_outbounds_activity", stashed_outbounds_activity );
631     data.read( "stashed_outbounds_backlog", stashed_outbounds_backlog );
632 
633     if( data.has_array( "backlog" ) ) {
634         data.read( "backlog", backlog );
635     }
636     if( !backlog.empty() && !backlog.front().str_values.empty() && ( ( activity &&
637             activity.id() == activity_id( "ACT_FETCH_REQUIRED" ) ) || ( destination_activity &&
638                     destination_activity.id() == activity_id( "ACT_FETCH_REQUIRED" ) ) ) ) {
639         requirement_data fetch_reqs;
640         data.read( "fetch_data", fetch_reqs );
641         const requirement_id req_id( backlog.front().str_values.back() );
642         requirement_data::save_requirement( fetch_reqs, req_id );
643     }
644     // npc activity on vehicles.
645     data.read( "activity_vehicle_part_index", activity_vehicle_part_index );
646     // health
647     data.read( "healthy", healthy );
648     data.read( "healthy_mod", healthy_mod );
649 
650     // Remove check after 0.F
651     if( savegame_loading_version >= 30 ) {
652         if( data.has_array( "proficiencies" ) ) {
653             _proficiencies->deserialize_legacy( data.get_array( "proficiencies" ) );
654         } else {
655             data.read( "proficiencies", _proficiencies );
656         }
657     }
658 
659     //energy
660     data.read( "stim", stim );
661     data.read( "stamina", stamina );
662 
663     data.read( "magic", magic );
664 
665     data.read( "underwater", underwater );
666 
667     data.read( "traits", my_traits );
668     for( auto it = my_traits.begin(); it != my_traits.end(); ) {
669         const auto &tid = *it;
670         if( tid.is_valid() ) {
671             ++it;
672             // Remove after 0.F
673         } else if( tid == trait_id( "PROF_HELI_PILOT" ) ) {
674             it = my_traits.erase( it );
675             add_proficiency( proficiency_id( "prof_helicopter_pilot" ) );
676         } else {
677             debugmsg( "character %s has invalid trait %s, it will be ignored", name, tid.c_str() );
678             my_traits.erase( it++ );
679         }
680     }
681 
682     data.read( "mutations", my_mutations );
683 
684     for( auto it = my_mutations.begin(); it != my_mutations.end(); ) {
685         const trait_id &mid = it->first;
686         if( mid.is_valid() ) {
687             on_mutation_gain( mid );
688             cached_mutations.push_back( &mid.obj() );
689             ++it;
690             // Remove after 0.F
691         } else if( mid == trait_id( "PROF_HELI_PILOT" ) ) {
692             it = my_mutations.erase( it );
693             add_proficiency( proficiency_id( "prof_helicopter_pilot" ) );
694         } else {
695             debugmsg( "character %s has invalid mutation %s, it will be ignored", name, mid.c_str() );
696             it = my_mutations.erase( it );
697         }
698     }
699     recalculate_size();
700 
701     data.read( "my_bionics", *my_bionics );
702 
703     for( auto &w : worn ) {
704         w.on_takeoff( *this );
705     }
706     worn.clear();
707     data.read( "worn", worn );
708     for( auto &w : worn ) {
709         on_item_wear( w );
710     }
711 
712     // TEMPORARY until 0.F
713     if( data.has_array( "hp_cur" ) ) {
714         set_anatomy( anatomy_id( "human_anatomy" ) );
715         set_body();
716         std::array<int, 6> hp_cur;
717         data.read( "hp_cur", hp_cur );
718         std::array<int, 6> hp_max;
719         data.read( "hp_max", hp_max );
720         set_part_hp_cur( bodypart_id( "head" ), hp_cur[0] );
721         set_part_hp_max( bodypart_id( "head" ), hp_max[0] );
722         set_part_hp_cur( bodypart_id( "torso" ), hp_cur[1] );
723         set_part_hp_max( bodypart_id( "torso" ), hp_max[1] );
724         set_part_hp_cur( bodypart_id( "arm_l" ), hp_cur[2] );
725         set_part_hp_max( bodypart_id( "arm_l" ), hp_max[2] );
726         set_part_hp_cur( bodypart_id( "arm_r" ), hp_cur[3] );
727         set_part_hp_max( bodypart_id( "arm_r" ), hp_max[3] );
728         set_part_hp_cur( bodypart_id( "leg_l" ), hp_cur[4] );
729         set_part_hp_max( bodypart_id( "leg_l" ), hp_max[4] );
730         set_part_hp_cur( bodypart_id( "leg_r" ), hp_cur[5] );
731         set_part_hp_max( bodypart_id( "leg_r" ), hp_max[5] );
732     }
733     if( data.has_array( "damage_bandaged" ) ) {
734         set_anatomy( anatomy_id( "human_anatomy" ) );
735         set_body();
736         std::array<int, 6> damage_bandaged;
737         data.read( "damage_bandaged", damage_bandaged );
738         set_part_damage_bandaged( bodypart_id( "head" ), damage_bandaged[0] );
739         set_part_damage_bandaged( bodypart_id( "torso" ), damage_bandaged[1] );
740         set_part_damage_bandaged( bodypart_id( "arm_l" ), damage_bandaged[2] );
741         set_part_damage_bandaged( bodypart_id( "arm_r" ), damage_bandaged[3] );
742         set_part_damage_bandaged( bodypart_id( "leg_l" ), damage_bandaged[4] );
743         set_part_damage_bandaged( bodypart_id( "leg_r" ), damage_bandaged[5] );
744     }
745     if( data.has_array( "damage_disinfected" ) ) {
746         set_anatomy( anatomy_id( "human_anatomy" ) );
747         set_body();
748         std::array<int, 6> damage_disinfected;
749         data.read( "damage_disinfected", damage_disinfected );
750         set_part_damage_disinfected( bodypart_id( "head" ), damage_disinfected[0] );
751         set_part_damage_disinfected( bodypart_id( "torso" ), damage_disinfected[1] );
752         set_part_damage_disinfected( bodypart_id( "arm_l" ), damage_disinfected[2] );
753         set_part_damage_disinfected( bodypart_id( "arm_r" ), damage_disinfected[3] );
754         set_part_damage_disinfected( bodypart_id( "leg_l" ), damage_disinfected[4] );
755         set_part_damage_disinfected( bodypart_id( "leg_r" ), damage_disinfected[5] );
756     }
757     if( data.has_array( "healed_24h" ) ) {
758         set_anatomy( anatomy_id( "human_anatomy" ) );
759         set_body();
760         std::array<int, 6> healed_total;
761         data.read( "healed_24h", healed_total );
762         set_part_healed_total( bodypart_id( "head" ), healed_total[0] );
763         set_part_healed_total( bodypart_id( "torso" ), healed_total[1] );
764         set_part_healed_total( bodypart_id( "arm_l" ), healed_total[2] );
765         set_part_healed_total( bodypart_id( "arm_r" ), healed_total[3] );
766         set_part_healed_total( bodypart_id( "leg_l" ), healed_total[4] );
767         set_part_healed_total( bodypart_id( "leg_r" ), healed_total[5] );
768     }
769     if( data.has_array( "body_wetness" ) ) {
770         set_anatomy( anatomy_id( "human_anatomy" ) );
771         set_body();
772         std::array<int, 12> body_wetness;
773         body_wetness.fill( 0 );
774         data.read( "body_wetness", body_wetness );
775         set_part_wetness( bodypart_id( "torso" ), body_wetness[0] );
776         set_part_wetness( bodypart_id( "head" ), body_wetness[1] );
777         set_part_wetness( bodypart_id( "eyes" ), body_wetness[2] );
778         set_part_wetness( bodypart_id( "mouth" ), body_wetness[3] );
779         set_part_wetness( bodypart_id( "arm_l" ), body_wetness[4] );
780         set_part_wetness( bodypart_id( "arm_r" ), body_wetness[5] );
781         set_part_wetness( bodypart_id( "hand_l" ), body_wetness[6] );
782         set_part_wetness( bodypart_id( "hand_r" ), body_wetness[7] );
783         set_part_wetness( bodypart_id( "leg_l" ), body_wetness[8] );
784         set_part_wetness( bodypart_id( "leg_r" ), body_wetness[9] );
785         set_part_wetness( bodypart_id( "foot_l" ), body_wetness[10] );
786         set_part_wetness( bodypart_id( "foot_r" ), body_wetness[11] );
787     }
788     if( data.has_array( "temp_cur" ) ) {
789         set_anatomy( anatomy_id( "human_anatomy" ) );
790         set_body();
791         std::array<int, 12> temp_cur;
792         temp_cur.fill( BODYTEMP_NORM );
793         data.read( "temp_cur", temp_cur );
794         set_part_temp_cur( bodypart_id( "torso" ), temp_cur[0] );
795         set_part_temp_cur( bodypart_id( "head" ), temp_cur[1] );
796         set_part_temp_cur( bodypart_id( "eyes" ), temp_cur[2] );
797         set_part_temp_cur( bodypart_id( "mouth" ), temp_cur[3] );
798         set_part_temp_cur( bodypart_id( "arm_l" ), temp_cur[4] );
799         set_part_temp_cur( bodypart_id( "arm_r" ), temp_cur[5] );
800         set_part_temp_cur( bodypart_id( "hand_l" ), temp_cur[6] );
801         set_part_temp_cur( bodypart_id( "hand_r" ), temp_cur[7] );
802         set_part_temp_cur( bodypart_id( "leg_l" ), temp_cur[8] );
803         set_part_temp_cur( bodypart_id( "leg_r" ), temp_cur[9] );
804         set_part_temp_cur( bodypart_id( "foot_l" ), temp_cur[10] );
805         set_part_temp_cur( bodypart_id( "foot_r" ), temp_cur[11] );
806     }
807     if( data.has_array( "temp_conv" ) ) {
808         set_anatomy( anatomy_id( "human_anatomy" ) );
809         set_body();
810         std::array<int, 12> temp_conv;
811         temp_conv.fill( BODYTEMP_NORM );
812         data.read( "temp_conv", temp_conv );
813         set_part_temp_conv( bodypart_id( "torso" ), temp_conv[0] );
814         set_part_temp_conv( bodypart_id( "head" ), temp_conv[1] );
815         set_part_temp_conv( bodypart_id( "eyes" ), temp_conv[2] );
816         set_part_temp_conv( bodypart_id( "mouth" ), temp_conv[3] );
817         set_part_temp_conv( bodypart_id( "arm_l" ), temp_conv[4] );
818         set_part_temp_conv( bodypart_id( "arm_r" ), temp_conv[5] );
819         set_part_temp_conv( bodypart_id( "hand_l" ), temp_conv[6] );
820         set_part_temp_conv( bodypart_id( "hand_r" ), temp_conv[7] );
821         set_part_temp_conv( bodypart_id( "leg_l" ), temp_conv[8] );
822         set_part_temp_conv( bodypart_id( "leg_r" ), temp_conv[9] );
823         set_part_temp_conv( bodypart_id( "foot_l" ), temp_conv[10] );
824         set_part_temp_conv( bodypart_id( "foot_r" ), temp_conv[11] );
825     }
826     if( data.has_array( "frostbite_timer" ) ) {
827         set_anatomy( anatomy_id( "human_anatomy" ) );
828         set_body();
829         std::array<int, 12> frostbite_timer;
830         frostbite_timer.fill( 0 );
831         data.read( "frostbite_timer", frostbite_timer );
832         set_part_frostbite_timer( bodypart_id( "torso" ), frostbite_timer[0] );
833         set_part_frostbite_timer( bodypart_id( "head" ), frostbite_timer[1] );
834         set_part_frostbite_timer( bodypart_id( "eyes" ), frostbite_timer[2] );
835         set_part_frostbite_timer( bodypart_id( "mouth" ), frostbite_timer[3] );
836         set_part_frostbite_timer( bodypart_id( "arm_l" ), frostbite_timer[4] );
837         set_part_frostbite_timer( bodypart_id( "arm_r" ), frostbite_timer[5] );
838         set_part_frostbite_timer( bodypart_id( "hand_l" ), frostbite_timer[6] );
839         set_part_frostbite_timer( bodypart_id( "hand_r" ), frostbite_timer[7] );
840         set_part_frostbite_timer( bodypart_id( "leg_l" ), frostbite_timer[8] );
841         set_part_frostbite_timer( bodypart_id( "leg_r" ), frostbite_timer[9] );
842         set_part_frostbite_timer( bodypart_id( "foot_l" ), frostbite_timer[10] );
843         set_part_frostbite_timer( bodypart_id( "foot_r" ), frostbite_timer[11] );
844     }
845 
846     inv->clear();
847     if( data.has_member( "inv" ) ) {
848         JsonIn *invin = data.get_raw( "inv" );
849         inv->json_load_items( *invin );
850     }
851     // this is after inventory is loaded to make it more obvious that
852     // it needs to be changed again when Character::i_at is removed for nested containers
853     if( savegame_loading_version < 28 ) {
854         activity.migrate_item_position( *this );
855         destination_activity.migrate_item_position( *this );
856         stashed_outbounds_activity.migrate_item_position( *this );
857         stashed_outbounds_backlog.migrate_item_position( *this );
858     }
859 
860     weapon = item();
861     data.read( "weapon", weapon );
862 
863     data.read( "move_mode", move_mode );
864 
865     if( has_effect( effect_riding ) ) {
866         int temp_id;
867         if( data.read( "mounted_creature", temp_id ) ) {
868             mounted_creature_id = temp_id;
869             mounted_creature = g->critter_tracker->from_temporary_id( temp_id );
870         } else {
871             mounted_creature = nullptr;
872         }
873     }
874 
875     morale->load( data );
876 
877     _skills->clear();
878     JsonObject skill_data = data.get_object( "skills" );
879     skill_data.allow_omitted_members();
880     for( const JsonMember member : skill_data ) {
881         member.read( ( *_skills )[skill_id( member.name() )] );
882     }
883     if( savegame_loading_version <= 28 ) {
884         if( !skill_data.has_member( "chemistry" ) && skill_data.has_member( "cooking" ) ) {
885             skill_data.get_member( "cooking" ).read( ( *_skills )[skill_id( "chemistry" )] );
886         }
887     }
888 
889     on_stat_change( "thirst", thirst );
890     on_stat_change( "hunger", hunger );
891     on_stat_change( "fatigue", fatigue );
892     on_stat_change( "sleep_deprivation", sleep_deprivation );
893     on_stat_change( "pkill", pkill );
894     on_stat_change( "perceived_pain", get_perceived_pain() );
895     recalc_sight_limits();
896     calc_encumbrance();
897 
898     assign( data, "power_level", power_level, false, 0_kJ );
899     assign( data, "max_power_level", max_power_level, false, 0_kJ );
900 
901     // Bionic power should not be negative!
902     if( power_level < 0_mJ ) {
903         power_level = 0_mJ;
904     }
905 
906     JsonArray overmap_time_array = data.get_array( "overmap_time" );
907     overmap_time.clear();
908     while( overmap_time_array.has_more() ) {
909         point_abs_omt pt;
910         overmap_time_array.read_next( pt );
911         time_duration tdr = 0_turns;
912         overmap_time_array.read_next( tdr );
913         overmap_time[pt] = tdr;
914     }
915     data.read( "stomach", stomach );
916     data.read( "guts", guts );
917     data.read( "automoveroute", auto_move_route );
918 
919     known_traps.clear();
920     for( JsonObject pmap : data.get_array( "known_traps" ) ) {
921         pmap.allow_omitted_members();
922         const tripoint p( pmap.get_int( "x" ), pmap.get_int( "y" ), pmap.get_int( "z" ) );
923         const std::string t = pmap.get_string( "trap" );
924         known_traps.insert( trap_map::value_type( p, t ) );
925     }
926 
927     // remove after 0.F
928     if( savegame_loading_version < 33 ) {
929         visit_items( []( item * it, item * ) {
930             migrate_item_charges( *it );
931             return VisitResponse::NEXT;
932         } );
933     }
934 }
935 
936 /**
937  * Load variables from json into object. These variables are common to both the avatar and NPCs.
938  */
store(JsonOut & json) const939 void Character::store( JsonOut &json ) const
940 {
941     Creature::store( json );
942 
943     // assumes already in Character object
944     // positional data
945     json.member( "posx", position.x );
946     json.member( "posy", position.y );
947     json.member( "posz", position.z );
948 
949     // stat
950     json.member( "str_cur", str_cur );
951     json.member( "str_max", str_max );
952     json.member( "dex_cur", dex_cur );
953     json.member( "dex_max", dex_max );
954     json.member( "int_cur", int_cur );
955     json.member( "int_max", int_max );
956     json.member( "per_cur", per_cur );
957     json.member( "per_max", per_max );
958 
959     json.member( "str_bonus", str_bonus );
960     json.member( "dex_bonus", dex_bonus );
961     json.member( "per_bonus", per_bonus );
962     json.member( "int_bonus", int_bonus );
963 
964     json.member( "base_age", init_age );
965     json.member( "base_height", init_height );
966     json.member_as_string( "blood_type", my_blood_type );
967     json.member( "blood_rh_factor", blood_rh_factor );
968 
969     json.member( "custom_profession", custom_profession );
970 
971     // health
972     json.member( "healthy", healthy );
973     json.member( "healthy_mod", healthy_mod );
974 
975     // needs
976     json.member( "thirst", thirst );
977     json.member( "hunger", hunger );
978     json.member( "fatigue", fatigue );
979     json.member( "activity_history", activity_history );
980     json.member( "sleep_deprivation", sleep_deprivation );
981     json.member( "stored_calories", stored_calories );
982     json.member( "radiation", radiation );
983     json.member( "stamina", stamina );
984     json.member( "vitamin_levels", vitamin_levels );
985     json.member( "pkill", pkill );
986     json.member( "omt_path", omt_path );
987     json.member( "consumption_history", consumption_history );
988 
989     // crafting etc
990     json.member( "destination_activity", destination_activity );
991     json.member( "activity", activity );
992     json.member( "stashed_outbounds_activity", stashed_outbounds_activity );
993     json.member( "stashed_outbounds_backlog", stashed_outbounds_backlog );
994     json.member( "backlog", backlog );
995     json.member( "activity_vehicle_part_index", activity_vehicle_part_index ); // NPC activity
996 
997     // handling for storing activity requirements
998     if( !backlog.empty() && !backlog.front().str_values.empty() && ( ( activity &&
999             activity.id() == activity_id( "ACT_FETCH_REQUIRED" ) ) || ( destination_activity &&
1000                     destination_activity.id() == activity_id( "ACT_FETCH_REQUIRED" ) ) ) ) {
1001         requirement_data things_to_fetch = requirement_id( backlog.front().str_values.back() ).obj();
1002         json.member( "fetch_data", things_to_fetch );
1003     }
1004 
1005     json.member( "stim", stim );
1006     json.member( "type_of_scent", type_of_scent );
1007 
1008     // breathing
1009     json.member( "underwater", underwater );
1010     json.member( "oxygen", oxygen );
1011 
1012     // traits: permanent 'mutations' more or less
1013     json.member( "traits", my_traits );
1014     json.member( "mutations", my_mutations );
1015     json.member( "magic", magic );
1016     json.member( "martial_arts_data", martial_arts_data );
1017     // "Fracking Toasters" - Saul Tigh, toaster
1018     json.member( "my_bionics", *my_bionics );
1019 
1020     json.member_as_string( "move_mode",  move_mode );
1021 
1022     // storing the mount
1023     if( is_mounted() ) {
1024         json.member( "mounted_creature", g->critter_tracker->temporary_id( *mounted_creature ) );
1025     }
1026 
1027     morale->store( json );
1028 
1029     // skills
1030     json.member( "skills" );
1031     json.start_object();
1032     for( const auto &pair : *_skills ) {
1033         json.member( pair.first.str(), pair.second );
1034     }
1035     json.end_object();
1036 
1037     json.member( "proficiencies", _proficiencies );
1038 
1039     // npc; unimplemented
1040     if( power_level < 1_J ) {
1041         json.member( "power_level", std::to_string( units::to_millijoule( power_level ) ) + " mJ" );
1042     } else if( power_level < 1_kJ ) {
1043         json.member( "power_level", std::to_string( units::to_joule( power_level ) ) + " J" );
1044     } else {
1045         json.member( "power_level", units::to_kilojoule( power_level ) );
1046     }
1047     json.member( "max_power_level", units::to_kilojoule( max_power_level ) );
1048 
1049     if( !overmap_time.empty() ) {
1050         json.member( "overmap_time" );
1051         json.start_array();
1052         for( const std::pair<const point_abs_omt, time_duration> &pr : overmap_time ) {
1053             json.write( pr.first );
1054             json.write( pr.second );
1055         }
1056         json.end_array();
1057     }
1058     json.member( "stomach", stomach );
1059     json.member( "guts", guts );
1060     json.member( "automoveroute", auto_move_route );
1061     json.member( "known_traps" );
1062     json.start_array();
1063     for( const auto &elem : known_traps ) {
1064         json.start_object();
1065         json.member( "x", elem.first.x );
1066         json.member( "y", elem.first.y );
1067         json.member( "z", elem.first.z );
1068         json.member( "trap", elem.second );
1069         json.end_object();
1070     }
1071     json.end_array();
1072 }
1073 
1074 ////////////////////////////////////////////////////////////////////////////////////////////////////
1075 ///// player.h, avatar + npc
1076 
1077 /**
1078  * Gather variables for saving. These variables are common to both the avatar and npcs.
1079  */
store(JsonOut & json) const1080 void player::store( JsonOut &json ) const
1081 {
1082     Character::store( json );
1083 
1084     // energy
1085     json.member( "last_sleep_check", last_sleep_check );
1086     // misc levels
1087     json.member( "slow_rad", slow_rad );
1088     json.member( "scent", static_cast<int>( scent ) );
1089 
1090     // gender
1091     json.member( "male", male );
1092 
1093     json.member( "cash", cash );
1094     json.member( "recoil", recoil );
1095     json.member( "in_vehicle", in_vehicle );
1096     json.member( "id", getID() );
1097 
1098     // "Looks like I picked the wrong week to quit smoking." - Steve McCroskey
1099     json.member( "addictions", addictions );
1100     json.member( "followers", follower_ids );
1101 
1102     json.member( "worn", worn ); // also saves contents
1103     json.member( "inv" );
1104     inv->json_save_items( json );
1105 
1106     if( !weapon.is_null() ) {
1107         json.member( "weapon", weapon ); // also saves contents
1108     }
1109 
1110     if( const auto lt_ptr = last_target.lock() ) {
1111         if( const npc *const guy = dynamic_cast<const npc *>( lt_ptr.get() ) ) {
1112             json.member( "last_target", guy->getID() );
1113             json.member( "last_target_type", +1 );
1114         } else if( const monster *const mon = dynamic_cast<const monster *>( lt_ptr.get() ) ) {
1115             // monsters don't have IDs, so get its index in the Creature_tracker instead
1116             json.member( "last_target", g->critter_tracker->temporary_id( *mon ) );
1117             json.member( "last_target_type", -1 );
1118         }
1119     } else {
1120         json.member( "last_target_pos", last_target_pos );
1121     }
1122 
1123     json.member( "destination_point", destination_point );
1124 
1125     // faction warnings
1126     json.member( "faction_warnings" );
1127     json.start_array();
1128     for( const auto &elem : warning_record ) {
1129         json.start_object();
1130         json.member( "fac_warning_id", elem.first );
1131         json.member( "fac_warning_num", elem.second.first );
1132         json.member( "fac_warning_time", elem.second.second );
1133         json.end_object();
1134     }
1135     json.end_array();
1136 
1137     json.member( "ammo_location", ammo_location );
1138 
1139     json.member( "camps" );
1140     json.start_array();
1141     for( const tripoint_abs_omt &bcpt : camps ) {
1142         json.start_object();
1143         json.member( "pos", bcpt );
1144         json.end_object();
1145     }
1146     json.end_array();
1147 }
1148 
1149 /**
1150  * Load variables from json into object. These variables are common to both the avatar and NPCs.
1151  */
load(const JsonObject & data)1152 void player::load( const JsonObject &data )
1153 {
1154     Character::load( data );
1155 
1156     JsonArray parray;
1157     character_id tmpid;
1158 
1159     data.read( "slow_rad", slow_rad );
1160     data.read( "scent", scent );
1161     data.read( "male", male );
1162     data.read( "cash", cash );
1163     data.read( "recoil", recoil );
1164     data.read( "in_vehicle", in_vehicle );
1165     data.read( "last_sleep_check", last_sleep_check );
1166     if( data.read( "id", tmpid ) && tmpid.is_valid() ) {
1167         // Templates have invalid ids, so we only assign here when valid.
1168         // When the game starts, a new valid id will be assigned if not already
1169         // present.
1170         setID( tmpid );
1171     }
1172 
1173     data.read( "addictions", addictions );
1174     data.read( "followers", follower_ids );
1175 
1176     // Add the earplugs.
1177     if( has_bionic( bionic_id( "bio_ears" ) ) && !has_bionic( bionic_id( "bio_earplugs" ) ) ) {
1178         add_bionic( bionic_id( "bio_earplugs" ) );
1179     }
1180 
1181     // Add the blindfold.
1182     if( has_bionic( bionic_id( "bio_sunglasses" ) ) && !has_bionic( bionic_id( "bio_blindfold" ) ) ) {
1183         add_bionic( bionic_id( "bio_blindfold" ) );
1184     }
1185 
1186     // Fixes bugged characters for CBM's preventing mutations.
1187     for( const bionic_id &bid : get_bionics() ) {
1188         for( const trait_id &mid : bid->canceled_mutations ) {
1189             if( has_trait( mid ) ) {
1190                 remove_mutation( mid );
1191             }
1192         }
1193     }
1194 
1195     if( data.has_array( "faction_warnings" ) ) {
1196         for( JsonObject warning_data : data.get_array( "faction_warnings" ) ) {
1197             warning_data.allow_omitted_members();
1198             std::string fac_id = warning_data.get_string( "fac_warning_id" );
1199             int warning_num = warning_data.get_int( "fac_warning_num" );
1200             time_point warning_time = calendar::before_time_starts;
1201             warning_data.read( "fac_warning_time", warning_time );
1202             warning_record[faction_id( fac_id )] = std::make_pair( warning_num, warning_time );
1203         }
1204     }
1205 
1206     int tmptar;
1207     int tmptartyp = 0;
1208 
1209     data.read( "last_target", tmptar );
1210     data.read( "last_target_type", tmptartyp );
1211     data.read( "last_target_pos", last_target_pos );
1212     data.read( "ammo_location", ammo_location );
1213     // Fixes savefile with invalid last_target_pos.
1214     if( last_target_pos && *last_target_pos == tripoint_min ) {
1215         last_target_pos = cata::nullopt;
1216     }
1217     if( tmptartyp == +1 ) {
1218         // Use overmap_buffer because game::active_npc is not filled yet.
1219         last_target = overmap_buffer.find_npc( character_id( tmptar ) );
1220     } else if( tmptartyp == -1 ) {
1221         // Need to do this *after* the monsters have been loaded!
1222         last_target = g->critter_tracker->from_temporary_id( tmptar );
1223     }
1224     data.read( "destination_point", destination_point );
1225     camps.clear();
1226     for( JsonObject bcdata : data.get_array( "camps" ) ) {
1227         bcdata.allow_omitted_members();
1228         tripoint_abs_omt bcpt;
1229         bcdata.read( "pos", bcpt );
1230         camps.insert( bcpt );
1231     }
1232 }
1233 
1234 ////////////////////////////////////////////////////////////////////////////////////////////////////
1235 ///// avatar.h
1236 
serialize(JsonOut & json) const1237 void avatar::serialize( JsonOut &json ) const
1238 {
1239     json.start_object();
1240 
1241     store( json );
1242 
1243     json.end_object();
1244 }
1245 
store(JsonOut & json) const1246 void avatar::store( JsonOut &json ) const
1247 {
1248     player::store( json );
1249 
1250     // player-specific specifics
1251     if( prof != nullptr ) {
1252         json.member( "profession", prof->ident() );
1253     }
1254     if( get_scenario() != nullptr ) {
1255         json.member( "scenario", get_scenario()->ident() );
1256     }
1257     // someday, npcs may drive
1258     json.member( "controlling_vehicle", controlling_vehicle );
1259 
1260     // shopping carts, furniture etc
1261     json.member( "grab_point", grab_point );
1262     json.member( "grab_type", obj_type_name[static_cast<int>( grab_type ) ] );
1263 
1264     // misc player specific stuff
1265     json.member( "focus_pool", focus_pool );
1266 
1267     // stats through kills
1268     json.member( "str_upgrade", std::abs( str_upgrade ) );
1269     json.member( "dex_upgrade", std::abs( dex_upgrade ) );
1270     json.member( "int_upgrade", std::abs( int_upgrade ) );
1271     json.member( "per_upgrade", std::abs( per_upgrade ) );
1272 
1273     // npc: unimplemented, potentially useful
1274     json.member( "learned_recipes", *learned_recipes );
1275 
1276     // Player only, books they have read at least once.
1277     json.member( "items_identified", items_identified );
1278 
1279     json.member( "translocators", translocators );
1280 
1281     // mission stuff
1282     json.member( "active_mission", active_mission == nullptr ? -1 : active_mission->get_id() );
1283 
1284     json.member( "active_missions", mission::to_uid_vector( active_missions ) );
1285     json.member( "completed_missions", mission::to_uid_vector( completed_missions ) );
1286     json.member( "failed_missions", mission::to_uid_vector( failed_missions ) );
1287 
1288     json.member( "show_map_memory", show_map_memory );
1289 
1290     json.member( "assigned_invlet" );
1291     json.start_array();
1292     for( const auto &iter : inv->assigned_invlet ) {
1293         json.start_array();
1294         json.write( iter.first );
1295         json.write( iter.second );
1296         json.end_array();
1297     }
1298     json.end_array();
1299 
1300     json.member( "invcache" );
1301     inv->json_save_invcache( json );
1302 
1303     json.member( "calorie_diary", calorie_diary );
1304 
1305     json.member( "preferred_aiming_mode", preferred_aiming_mode );
1306 }
1307 
deserialize(JsonIn & jsin)1308 void avatar::deserialize( JsonIn &jsin )
1309 {
1310     JsonObject data = jsin.get_object();
1311     data.allow_omitted_members();
1312     load( data );
1313 }
1314 
load(const JsonObject & data)1315 void avatar::load( const JsonObject &data )
1316 {
1317     player::load( data );
1318 
1319     // Remove after 0.F
1320     // Exists to prevent failed to visit member errors
1321     if( data.has_member( "reactor_plut" ) ) {
1322         data.get_int( "reactor_plut" );
1323     }
1324     if( data.has_member( "tank_plut" ) ) {
1325         data.get_int( "tank_plut" );
1326     }
1327 
1328     std::string prof_ident = "(null)";
1329     if( data.read( "profession", prof_ident ) && string_id<profession>( prof_ident ).is_valid() ) {
1330         prof = &string_id<profession>( prof_ident ).obj();
1331     } else {
1332         //We are likely an older profession which has since been removed so just set to default.  This is only cosmetic after game start.
1333         prof = profession::generic();
1334     }
1335 
1336     data.read( "controlling_vehicle", controlling_vehicle );
1337 
1338     data.read( "grab_point", grab_point );
1339     std::string grab_typestr = "OBJECT_NONE";
1340     if( grab_point.x != 0 || grab_point.y != 0 ) {
1341         grab_typestr = "OBJECT_VEHICLE";
1342         data.read( "grab_type", grab_typestr );
1343     } else {
1344         // we just want to read, but ignore grab_type
1345         std::string fake;
1346         data.read( "grab_type", fake );
1347     }
1348 
1349     const auto iter = std::find( obj_type_name.begin(), obj_type_name.end(), grab_typestr );
1350     grab( iter == obj_type_name.end() ?
1351           object_type::NONE : static_cast<object_type>( std::distance( obj_type_name.begin(), iter ) ),
1352           grab_point );
1353 
1354     data.read( "focus_pool", focus_pool );
1355 
1356     // stats through kills
1357     data.read( "str_upgrade", str_upgrade );
1358     data.read( "dex_upgrade", dex_upgrade );
1359     data.read( "int_upgrade", int_upgrade );
1360     data.read( "per_upgrade", per_upgrade );
1361 
1362     // this is so we don't need to call get_option in a draw function
1363     if( !get_option<bool>( "STATS_THROUGH_KILLS" ) )         {
1364         str_upgrade = -str_upgrade;
1365         dex_upgrade = -dex_upgrade;
1366         int_upgrade = -int_upgrade;
1367         per_upgrade = -per_upgrade;
1368     }
1369 
1370     data.read( "magic", magic );
1371 
1372     set_highest_cat_level();
1373     drench_mut_calc();
1374     std::string scen_ident = "(null)";
1375     if( data.read( "scenario", scen_ident ) && string_id<scenario>( scen_ident ).is_valid() ) {
1376         set_scenario( &string_id<scenario>( scen_ident ).obj() );
1377 
1378         if( !get_scenario()->allowed_start( start_location ) ) {
1379             start_location = get_scenario()->random_start_location();
1380         }
1381     } else {
1382         const scenario *generic_scenario = scenario::generic();
1383 
1384         debugmsg( "Tried to use non-existent scenario '%s'. Setting to generic '%s'.",
1385                   scen_ident.c_str(), generic_scenario->ident().c_str() );
1386         set_scenario( generic_scenario );
1387     }
1388 
1389     data.read( "learned_recipes", *learned_recipes );
1390     valid_autolearn_skills->clear(); // Invalidates the cache
1391 
1392     items_identified.clear();
1393     data.read( "items_identified", items_identified );
1394 
1395     data.read( "translocators", translocators );
1396 
1397     std::vector<int> tmpmissions;
1398     if( data.read( "active_missions", tmpmissions ) ) {
1399         active_missions = mission::to_ptr_vector( tmpmissions );
1400     }
1401     if( data.read( "failed_missions", tmpmissions ) ) {
1402         failed_missions = mission::to_ptr_vector( tmpmissions );
1403     }
1404     if( data.read( "completed_missions", tmpmissions ) ) {
1405         completed_missions = mission::to_ptr_vector( tmpmissions );
1406     }
1407 
1408     int tmpactive_mission = 0;
1409     if( data.read( "active_mission", tmpactive_mission ) ) {
1410         if( tmpactive_mission != -1 ) {
1411             active_mission = mission::find( tmpactive_mission );
1412         }
1413     }
1414 
1415     // Normally there is only one player character loaded, so if a mission that is assigned to
1416     // another character (not the current one) fails, the other character(s) are not informed.
1417     // We must inform them when they get loaded the next time.
1418     // Only active missions need checking, failed/complete will not change anymore.
1419     const auto last = std::remove_if( active_missions.begin(),
1420     active_missions.end(), []( mission const * m ) {
1421         return m->has_failed();
1422     } );
1423     std::copy( last, active_missions.end(), std::back_inserter( failed_missions ) );
1424     active_missions.erase( last, active_missions.end() );
1425     if( active_mission && active_mission->has_failed() ) {
1426         if( active_missions.empty() ) {
1427             active_mission = nullptr;
1428         } else {
1429             active_mission = active_missions.front();
1430         }
1431     }
1432 
1433     //Load from legacy map_memory save location (now in its own file <playername>.mm)
1434     if( data.has_member( "map_memory_tiles" ) || data.has_member( "map_memory_curses" ) ) {
1435         player_map_memory.load( data );
1436     }
1437     data.read( "show_map_memory", show_map_memory );
1438 
1439     for( JsonArray pair : data.get_array( "assigned_invlet" ) ) {
1440         inv->assigned_invlet[static_cast<char>( pair.get_int( 0 ) )] =
1441             itype_id( pair.get_string( 1 ) );
1442     }
1443 
1444     if( data.has_member( "invcache" ) ) {
1445         JsonIn *jip = data.get_raw( "invcache" );
1446         inv->json_load_invcache( *jip );
1447     }
1448 
1449     data.read( "calorie_diary", calorie_diary );
1450 
1451     data.read( "preferred_aiming_mode", preferred_aiming_mode );
1452 }
1453 
1454 ////////////////////////////////////////////////////////////////////////////////////////////////////
1455 ///// npc.h
1456 
serialize(JsonOut & json) const1457 void npc_follower_rules::serialize( JsonOut &json ) const
1458 {
1459     json.start_object();
1460     json.member( "engagement", static_cast<int>( engagement ) );
1461     json.member( "aim", static_cast<int>( aim ) );
1462     json.member( "cbm_reserve", static_cast<int>( cbm_reserve ) );
1463     json.member( "cbm_recharge", static_cast<int>( cbm_recharge ) );
1464 
1465     // serialize the flags so they can be changed between save games
1466     for( const auto &rule : ally_rule_strs ) {
1467         json.member( "rule_" + rule.first, has_flag( rule.second.rule, false ) );
1468     }
1469     for( const auto &rule : ally_rule_strs ) {
1470         json.member( "override_enable_" + rule.first, has_override_enable( rule.second.rule ) );
1471     }
1472     for( const auto &rule : ally_rule_strs ) {
1473         json.member( "override_" + rule.first, has_override( rule.second.rule ) );
1474     }
1475 
1476     json.member( "pickup_whitelist", *pickup_whitelist );
1477 
1478     json.end_object();
1479 }
1480 
deserialize(JsonIn & jsin)1481 void npc_follower_rules::deserialize( JsonIn &jsin )
1482 {
1483     JsonObject data = jsin.get_object();
1484     data.allow_omitted_members();
1485     int tmpeng = 0;
1486     data.read( "engagement", tmpeng );
1487     engagement = static_cast<combat_engagement>( tmpeng );
1488     int tmpaim = 0;
1489     data.read( "aim", tmpaim );
1490     aim = static_cast<aim_rule>( tmpaim );
1491     int tmpreserve = 50;
1492     data.read( "cbm_reserve", tmpreserve );
1493     cbm_reserve = static_cast<cbm_reserve_rule>( tmpreserve );
1494     int tmprecharge = 50;
1495     data.read( "cbm_recharge", tmprecharge );
1496     cbm_recharge = static_cast<cbm_recharge_rule>( tmprecharge );
1497 
1498     // deserialize the flags so they can be changed between save games
1499     for( const auto &rule : ally_rule_strs ) {
1500         bool tmpflag = false;
1501         // legacy to handle rules that were saved before overrides
1502         data.read( rule.first, tmpflag );
1503         if( tmpflag ) {
1504             set_flag( rule.second.rule );
1505         } else {
1506             clear_flag( rule.second.rule );
1507         }
1508         data.read( "rule_" + rule.first, tmpflag );
1509         if( tmpflag ) {
1510             set_flag( rule.second.rule );
1511         } else {
1512             clear_flag( rule.second.rule );
1513         }
1514         data.read( "override_enable_" + rule.first, tmpflag );
1515         if( tmpflag ) {
1516             enable_override( rule.second.rule );
1517         } else {
1518             disable_override( rule.second.rule );
1519         }
1520         data.read( "override_" + rule.first, tmpflag );
1521         if( tmpflag ) {
1522             set_override( rule.second.rule );
1523         } else {
1524             clear_override( rule.second.rule );
1525         }
1526 
1527         // This and the following two entries are for legacy save game handling.
1528         // "avoid_combat" was renamed "follow_close" to better reflect behavior.
1529         if( data.has_member( "rule_avoid_combat" ) ) {
1530             data.read( "rule_avoid_combat", tmpflag );
1531             if( tmpflag ) {
1532                 set_flag( ally_rule::follow_close );
1533             } else {
1534                 clear_flag( ally_rule::follow_close );
1535             }
1536         }
1537         if( data.has_member( "override_enable_avoid_combat" ) ) {
1538             data.read( "override_enable_avoid_combat", tmpflag );
1539             if( tmpflag ) {
1540                 enable_override( ally_rule::follow_close );
1541             } else {
1542                 disable_override( ally_rule::follow_close );
1543             }
1544         }
1545         if( data.has_member( "override_avoid_combat" ) ) {
1546             data.read( "override_avoid_combat", tmpflag );
1547             if( tmpflag ) {
1548                 set_override( ally_rule::follow_close );
1549             } else {
1550                 clear_override( ally_rule::follow_close );
1551             }
1552         }
1553     }
1554 
1555     data.read( "pickup_whitelist", *pickup_whitelist );
1556 }
1557 
serialize(JsonOut & json) const1558 void dialogue_chatbin::serialize( JsonOut &json ) const
1559 {
1560     json.start_object();
1561     json.member( "first_topic", first_topic );
1562     if( mission_selected != nullptr ) {
1563         json.member( "mission_selected", mission_selected->get_id() );
1564     }
1565     json.member( "skill", skill );
1566     json.member( "style", style );
1567     json.member( "missions", mission::to_uid_vector( missions ) );
1568     json.member( "missions_assigned", mission::to_uid_vector( missions_assigned ) );
1569     json.end_object();
1570 }
1571 
deserialize(JsonIn & jsin)1572 void dialogue_chatbin::deserialize( JsonIn &jsin )
1573 {
1574     JsonObject data = jsin.get_object();
1575     data.allow_omitted_members();
1576 
1577     if( data.has_int( "first_topic" ) ) {
1578         int tmptopic = 0;
1579         data.read( "first_topic", tmptopic );
1580         first_topic = convert_talk_topic( static_cast<talk_topic_enum>( tmptopic ) );
1581     } else {
1582         data.read( "first_topic", first_topic );
1583     }
1584 
1585     data.read( "skill", skill );
1586     data.read( "style", style );
1587 
1588     std::vector<int> tmpmissions;
1589     data.read( "missions", tmpmissions );
1590     missions = mission::to_ptr_vector( tmpmissions );
1591     std::vector<int> tmpmissions_assigned;
1592     data.read( "missions_assigned", tmpmissions_assigned );
1593     missions_assigned = mission::to_ptr_vector( tmpmissions_assigned );
1594 
1595     int tmpmission_selected = 0;
1596     mission_selected = nullptr;
1597     if( data.read( "mission_selected", tmpmission_selected ) && tmpmission_selected != -1 ) {
1598         mission_selected = mission::find( tmpmission_selected );
1599     }
1600 }
1601 
deserialize(JsonIn & jsin)1602 void npc_personality::deserialize( JsonIn &jsin )
1603 {
1604     JsonObject data = jsin.get_object();
1605     data.allow_omitted_members();
1606     int tmpagg = 0;
1607     int tmpbrav = 0;
1608     int tmpcol = 0;
1609     int tmpalt = 0;
1610     if( data.read( "aggression", tmpagg ) &&
1611         data.read( "bravery", tmpbrav ) &&
1612         data.read( "collector", tmpcol ) &&
1613         data.read( "altruism", tmpalt ) ) {
1614         aggression = static_cast<signed char>( tmpagg );
1615         bravery = static_cast<signed char>( tmpbrav );
1616         collector = static_cast<signed char>( tmpcol );
1617         altruism = static_cast<signed char>( tmpalt );
1618     } else {
1619         debugmsg( "npc_personality: bad data" );
1620     }
1621 }
1622 
serialize(JsonOut & json) const1623 void npc_personality::serialize( JsonOut &json ) const
1624 {
1625     json.start_object();
1626     json.member( "aggression", static_cast<int>( aggression ) );
1627     json.member( "bravery", static_cast<int>( bravery ) );
1628     json.member( "collector", static_cast<int>( collector ) );
1629     json.member( "altruism", static_cast<int>( altruism ) );
1630     json.end_object();
1631 }
1632 
deserialize(JsonIn & jsin)1633 void npc_opinion::deserialize( JsonIn &jsin )
1634 {
1635     JsonObject data = jsin.get_object();
1636     data.allow_omitted_members();
1637     data.read( "trust", trust );
1638     data.read( "fear", fear );
1639     data.read( "value", value );
1640     data.read( "anger", anger );
1641     data.read( "owed", owed );
1642 }
1643 
serialize(JsonOut & json) const1644 void npc_opinion::serialize( JsonOut &json ) const
1645 {
1646     json.start_object();
1647     json.member( "trust", trust );
1648     json.member( "fear", fear );
1649     json.member( "value", value );
1650     json.member( "anger", anger );
1651     json.member( "owed", owed );
1652     json.end_object();
1653 }
1654 
deserialize(JsonIn & jsin)1655 void npc_favor::deserialize( JsonIn &jsin )
1656 {
1657     JsonObject jo = jsin.get_object();
1658     jo.allow_omitted_members();
1659     type = static_cast<npc_favor_type>( jo.get_int( "type" ) );
1660     jo.read( "value", value );
1661     jo.read( "itype_id", item_id );
1662     if( jo.has_int( "skill_id" ) ) {
1663         skill = Skill::from_legacy_int( jo.get_int( "skill_id" ) );
1664     } else if( jo.has_string( "skill_id" ) ) {
1665         skill = skill_id( jo.get_string( "skill_id" ) );
1666     } else {
1667         skill = skill_id::NULL_ID();
1668     }
1669 }
1670 
serialize(JsonOut & json) const1671 void npc_favor::serialize( JsonOut &json ) const
1672 {
1673     json.start_object();
1674     json.member( "type", static_cast<int>( type ) );
1675     json.member( "value", value );
1676     json.member( "itype_id", static_cast<std::string>( item_id ) );
1677     json.member( "skill_id", skill );
1678     json.end_object();
1679 }
1680 
serialize(JsonOut & json) const1681 void job_data::serialize( JsonOut &json ) const
1682 {
1683     json.start_object();
1684     json.member( "task_priorities", task_priorities );
1685     json.end_object();
1686 }
deserialize(JsonIn & jsin)1687 void job_data::deserialize( JsonIn &jsin )
1688 {
1689     if( jsin.test_object() ) {
1690         JsonObject jo = jsin.get_object();
1691         jo.allow_omitted_members();
1692         jo.read( "task_priorities", task_priorities );
1693     }
1694 }
1695 
1696 /*
1697  * load npc
1698  */
deserialize(JsonIn & jsin)1699 void npc::deserialize( JsonIn &jsin )
1700 {
1701     JsonObject data = jsin.get_object();
1702     load( data );
1703 }
1704 
load(const JsonObject & data)1705 void npc::load( const JsonObject &data )
1706 {
1707     player::load( data );
1708 
1709     int misstmp = 0;
1710     int classtmp = 0;
1711     int atttmp = 0;
1712     std::string facID;
1713     std::string comp_miss_id;
1714     std::string comp_miss_role;
1715     tripoint_abs_omt comp_miss_pt;
1716     std::string classid;
1717     std::string companion_mission_role;
1718     time_point companion_mission_t = calendar::turn_zero;
1719     time_point companion_mission_t_r = calendar::turn_zero;
1720     std::string act_id;
1721 
1722     // Remove after 0.F
1723     // Exists to prevent failed to visit member errors
1724     if( data.has_member( "reactor_plut" ) ) {
1725         data.get_int( "reactor_plut" );
1726     }
1727     if( data.has_member( "tank_plut" ) ) {
1728         data.get_int( "tank_plut" );
1729     }
1730 
1731     data.read( "name", name );
1732     data.read( "marked_for_death", marked_for_death );
1733     data.read( "dead", dead );
1734     data.read( "patience", patience );
1735     if( data.has_number( "myclass" ) ) {
1736         data.read( "myclass", classtmp );
1737         myclass = npc_class::from_legacy_int( classtmp );
1738     } else if( data.has_string( "myclass" ) ) {
1739         data.read( "myclass", classid );
1740         myclass = npc_class_id( classid );
1741     }
1742     data.read( "known_to_u", known_to_u );
1743     data.read( "personality", personality );
1744     if( !data.read( "submap_coords", submap_coords ) ) {
1745         // Old submap coordinates are for the point (0, 0, 0) on local map
1746         // New ones are for submap that contains pos
1747         point old_coords;
1748         data.read( "mapx", old_coords.x );
1749         data.read( "mapy", old_coords.y );
1750         int o = 0;
1751         if( data.read( "omx", o ) ) {
1752             old_coords.x += o * OMAPX * 2;
1753         }
1754         if( data.read( "omy", o ) ) {
1755             old_coords.y += o * OMAPY * 2;
1756         }
1757         submap_coords = old_coords + point( posx() / SEEX, posy() / SEEY );
1758     }
1759 
1760     if( !data.read( "mapz", position.z ) ) {
1761         data.read( "omz", position.z ); // omz/mapz got moved to position.z
1762     }
1763 
1764     if( data.has_member( "plx" ) ) {
1765         last_player_seen_pos.emplace();
1766         data.read( "plx", last_player_seen_pos->x );
1767         data.read( "ply", last_player_seen_pos->y );
1768         if( !data.read( "plz", last_player_seen_pos->z ) ) {
1769             last_player_seen_pos->z = posz();
1770         }
1771         // old code used tripoint_min to indicate "not a valid point"
1772         if( *last_player_seen_pos == tripoint_min ) {
1773             last_player_seen_pos.reset();
1774         }
1775     } else {
1776         data.read( "last_player_seen_pos", last_player_seen_pos );
1777     }
1778 
1779     data.read( "goalx", goal.x() );
1780     data.read( "goaly", goal.y() );
1781     data.read( "goalz", goal.z() );
1782 
1783     data.read( "guardx", guard_pos.x );
1784     data.read( "guardy", guard_pos.y );
1785     data.read( "guardz", guard_pos.z );
1786     if( data.read( "current_activity_id", act_id ) ) {
1787         current_activity_id = activity_id( act_id );
1788     } else if( activity ) {
1789         current_activity_id = activity.id();
1790     }
1791 
1792     if( data.has_member( "pulp_locationx" ) ) {
1793         pulp_location.emplace();
1794         data.read( "pulp_locationx", pulp_location->x );
1795         data.read( "pulp_locationy", pulp_location->y );
1796         data.read( "pulp_locationz", pulp_location->z );
1797         // old code used tripoint_min to indicate "not a valid point"
1798         if( *pulp_location == tripoint_min ) {
1799             pulp_location.reset();
1800         }
1801     } else {
1802         data.read( "pulp_location", pulp_location );
1803     }
1804     data.read( "assigned_camp", assigned_camp );
1805     data.read( "chair_pos", chair_pos );
1806     data.read( "wander_pos", wander_pos );
1807     data.read( "job", job );
1808     if( data.read( "mission", misstmp ) ) {
1809         mission = static_cast<npc_mission>( misstmp );
1810         static const std::set<npc_mission> legacy_missions = {{
1811                 NPC_MISSION_LEGACY_1, NPC_MISSION_LEGACY_2,
1812                 NPC_MISSION_LEGACY_3
1813             }
1814         };
1815         if( legacy_missions.count( mission ) > 0 ) {
1816             mission = NPC_MISSION_NULL;
1817         }
1818     }
1819     if( data.read( "previous_mission", misstmp ) ) {
1820         previous_mission = static_cast<npc_mission>( misstmp );
1821         static const std::set<npc_mission> legacy_missions = {{
1822                 NPC_MISSION_LEGACY_1, NPC_MISSION_LEGACY_2,
1823                 NPC_MISSION_LEGACY_3
1824             }
1825         };
1826         if( legacy_missions.count( mission ) > 0 ) {
1827             previous_mission = NPC_MISSION_NULL;
1828         }
1829     }
1830 
1831     if( data.read( "my_fac", facID ) ) {
1832         fac_id = faction_id( facID );
1833     }
1834     int temp_fac_api_ver = 0;
1835     if( data.read( "faction_api_ver", temp_fac_api_ver ) ) {
1836         faction_api_version = temp_fac_api_ver;
1837     } else {
1838         faction_api_version = 0;
1839     }
1840 
1841     if( data.read( "attitude", atttmp ) ) {
1842         attitude = static_cast<npc_attitude>( atttmp );
1843         static const std::set<npc_attitude> legacy_attitudes = {{
1844                 NPCATT_LEGACY_1, NPCATT_LEGACY_2, NPCATT_LEGACY_3,
1845                 NPCATT_LEGACY_4, NPCATT_LEGACY_5, NPCATT_LEGACY_6
1846             }
1847         };
1848         if( legacy_attitudes.count( attitude ) > 0 ) {
1849             attitude = NPCATT_NULL;
1850         }
1851     }
1852     if( data.read( "previous_attitude", atttmp ) ) {
1853         previous_attitude = static_cast<npc_attitude>( atttmp );
1854         static const std::set<npc_attitude> legacy_attitudes = {{
1855                 NPCATT_LEGACY_1, NPCATT_LEGACY_2, NPCATT_LEGACY_3,
1856                 NPCATT_LEGACY_4, NPCATT_LEGACY_5, NPCATT_LEGACY_6
1857             }
1858         };
1859         if( legacy_attitudes.count( attitude ) > 0 ) {
1860             previous_attitude = NPCATT_NULL;
1861         }
1862     }
1863 
1864     if( data.read( "comp_mission_id", comp_miss_id ) ) {
1865         comp_mission.mission_id = comp_miss_id;
1866     }
1867 
1868     if( data.read( "comp_mission_pt", comp_miss_pt ) ) {
1869         comp_mission.position = comp_miss_pt;
1870     }
1871 
1872     if( data.read( "comp_mission_role", comp_miss_role ) ) {
1873         comp_mission.role_id = comp_miss_role;
1874     }
1875 
1876     if( data.read( "companion_mission_role_id", companion_mission_role ) ) {
1877         companion_mission_role_id = companion_mission_role;
1878     }
1879 
1880     std::vector<tripoint_abs_omt> companion_mission_pts;
1881     data.read( "companion_mission_points", companion_mission_pts );
1882     if( !companion_mission_pts.empty() ) {
1883         for( auto pt : companion_mission_pts ) {
1884             companion_mission_points.push_back( pt );
1885         }
1886     }
1887 
1888     if( !data.read( "companion_mission_time", companion_mission_t ) ) {
1889         companion_mission_time = calendar::before_time_starts;
1890     } else {
1891         companion_mission_time = companion_mission_t;
1892     }
1893 
1894     if( !data.read( "companion_mission_time_ret", companion_mission_t_r ) ) {
1895         companion_mission_time_ret = calendar::before_time_starts;
1896     } else {
1897         companion_mission_time_ret = companion_mission_t_r;
1898     }
1899 
1900     companion_mission_inv.clear();
1901     if( data.has_member( "companion_mission_inv" ) ) {
1902         JsonIn *invin_mission = data.get_raw( "companion_mission_inv" );
1903         companion_mission_inv.json_load_items( *invin_mission );
1904     }
1905 
1906     if( !data.read( "restock", restock ) ) {
1907         restock = calendar::before_time_starts;
1908     }
1909 
1910     data.read( "op_of_u", op_of_u );
1911     data.read( "chatbin", chatbin );
1912     if( !data.read( "rules", rules ) ) {
1913         data.read( "misc_rules", rules );
1914         data.read( "combat_rules", rules );
1915     }
1916     real_weapon = item();
1917     data.read( "real_weapon", real_weapon );
1918     cbm_weapon_index = -1;
1919     data.read( "cbm_weapon_index", cbm_weapon_index );
1920 
1921     complaints.clear();
1922     for( const JsonMember member : data.get_object( "complaints" ) ) {
1923         // TODO: time_point does not have a default constructor, need to read in the map manually
1924         time_point p = calendar::turn_zero;
1925         member.read( p );
1926         complaints.emplace( member.name(), p );
1927     }
1928 }
1929 
1930 /*
1931  * save npc
1932  */
serialize(JsonOut & json) const1933 void npc::serialize( JsonOut &json ) const
1934 {
1935     json.start_object();
1936     // This must be after the json object has been started, so any super class
1937     // puts their data into the same json object.
1938     store( json );
1939     json.end_object();
1940 }
1941 
store(JsonOut & json) const1942 void npc::store( JsonOut &json ) const
1943 {
1944     player::store( json );
1945 
1946     json.member( "name", name );
1947     json.member( "marked_for_death", marked_for_death );
1948     json.member( "dead", dead );
1949     json.member( "patience", patience );
1950     json.member( "myclass", myclass.str() );
1951     json.member( "known_to_u", known_to_u );
1952     json.member( "personality", personality );
1953 
1954     json.member( "submap_coords", submap_coords );
1955 
1956     json.member( "last_player_seen_pos", last_player_seen_pos );
1957 
1958     json.member( "goalx", goal.x() );
1959     json.member( "goaly", goal.y() );
1960     json.member( "goalz", goal.z() );
1961 
1962     json.member( "guardx", guard_pos.x );
1963     json.member( "guardy", guard_pos.y );
1964     json.member( "guardz", guard_pos.z );
1965     json.member( "current_activity_id", current_activity_id.str() );
1966     json.member( "pulp_location", pulp_location );
1967     json.member( "assigned_camp", assigned_camp );
1968     json.member( "chair_pos", chair_pos );
1969     json.member( "wander_pos", wander_pos );
1970     json.member( "job", job );
1971     // TODO: stringid
1972     json.member( "mission", mission );
1973     json.member( "previous_mission", previous_mission );
1974     json.member( "faction_api_ver", faction_api_version );
1975     if( !fac_id.str().empty() ) { // set in constructor
1976         json.member( "my_fac", fac_id.c_str() );
1977     }
1978     json.member( "attitude", static_cast<int>( attitude ) );
1979     json.member( "previous_attitude", static_cast<int>( previous_attitude ) );
1980     json.member( "op_of_u", op_of_u );
1981     json.member( "chatbin", chatbin );
1982     json.member( "rules", rules );
1983 
1984     if( !real_weapon.is_null() ) {
1985         json.member( "real_weapon", real_weapon ); // also saves contents
1986     }
1987     json.member( "cbm_weapon_index", cbm_weapon_index );
1988 
1989     json.member( "comp_mission_id", comp_mission.mission_id );
1990     json.member( "comp_mission_pt", comp_mission.position );
1991     json.member( "comp_mission_role", comp_mission.role_id );
1992     json.member( "companion_mission_role_id", companion_mission_role_id );
1993     json.member( "companion_mission_points", companion_mission_points );
1994     json.member( "companion_mission_time", companion_mission_time );
1995     json.member( "companion_mission_time_ret", companion_mission_time_ret );
1996     json.member( "companion_mission_inv" );
1997     companion_mission_inv.json_save_items( json );
1998     json.member( "restock", restock );
1999 
2000     json.member( "complaints", complaints );
2001 }
2002 
2003 ////////////////////////////////////////////////////////////////////////////////////////////////////
2004 ///// inventory.h
2005 /*
2006  * Save invlet cache
2007  */
json_save_invcache(JsonOut & json) const2008 void inventory::json_save_invcache( JsonOut &json ) const
2009 {
2010     json.start_array();
2011     for( const auto &elem : invlet_cache.get_invlets_by_id() ) {
2012         json.start_object();
2013         json.member( elem.first.str() );
2014         json.start_array();
2015         for( const auto &_sym : elem.second ) {
2016             json.write( static_cast<int>( _sym ) );
2017         }
2018         json.end_array();
2019         json.end_object();
2020     }
2021     json.end_array();
2022 }
2023 
2024 /*
2025  * Invlet cache: player specific, thus not wrapped in inventory::json_load/save
2026  */
json_load_invcache(JsonIn & jsin)2027 void inventory::json_load_invcache( JsonIn &jsin )
2028 {
2029     try {
2030         std::unordered_map<itype_id, std::string> map;
2031         for( JsonObject jo : jsin.get_array() ) {
2032             jo.allow_omitted_members();
2033             for( const JsonMember member : jo ) {
2034                 std::string invlets;
2035                 for( const int i : member.get_array() ) {
2036                     invlets.push_back( i );
2037                 }
2038                 map[itype_id( member.name() )] = invlets;
2039             }
2040         }
2041         invlet_cache = invlet_favorites{ map };
2042     } catch( const JsonError &jsonerr ) {
2043         debugmsg( "bad invcache json:\n%s", jsonerr.c_str() );
2044     }
2045 }
2046 
2047 /*
2048  * save all items. Just this->items, invlet cache saved separately
2049  */
json_save_items(JsonOut & json) const2050 void inventory::json_save_items( JsonOut &json ) const
2051 {
2052     json.start_array();
2053     for( const auto &elem : items ) {
2054         for( const auto &elem_stack_iter : elem ) {
2055             elem_stack_iter.serialize( json );
2056         }
2057     }
2058     json.end_array();
2059 }
2060 
json_load_items(JsonIn & jsin)2061 void inventory::json_load_items( JsonIn &jsin )
2062 {
2063     jsin.start_array();
2064     while( !jsin.end_array() ) {
2065         item tmp;
2066         tmp.deserialize( jsin );
2067         add_item( tmp, true, false );
2068     }
2069 }
2070 
2071 ////////////////////////////////////////////////////////////////////////////////////////////////////
2072 ///// monster.h
2073 
deserialize(JsonIn & jsin)2074 void monster::deserialize( JsonIn &jsin )
2075 {
2076     JsonObject data = jsin.get_object();
2077     data.allow_omitted_members();
2078     load( data );
2079 }
2080 
load(const JsonObject & data)2081 void monster::load( const JsonObject &data )
2082 {
2083     Creature::load( data );
2084 
2085     std::string sidtmp;
2086     // load->str->int
2087     data.read( "typeid", sidtmp );
2088     type = &mtype_id( sidtmp ).obj();
2089 
2090     data.read( "unique_name", unique_name );
2091     data.read( "posx", position.x );
2092     data.read( "posy", position.y );
2093     if( !data.read( "posz", position.z ) ) {
2094         position.z = get_map().get_abs_sub().z;
2095     }
2096 
2097     data.read( "wandf", wandf );
2098     data.read( "wandx", wander_pos.x );
2099     data.read( "wandy", wander_pos.y );
2100     if( data.read( "wandz", wander_pos.z ) ) {
2101         wander_pos.z = position.z;
2102     }
2103     if( data.has_object( "tied_item" ) ) {
2104         JsonIn *tied_item_json = data.get_raw( "tied_item" );
2105         item newitem;
2106         newitem.deserialize( *tied_item_json );
2107         tied_item = cata::make_value<item>( newitem );
2108     }
2109     if( data.has_object( "tack_item" ) ) {
2110         JsonIn *tack_item_json = data.get_raw( "tack_item" );
2111         item newitem;
2112         newitem.deserialize( *tack_item_json );
2113         tack_item = cata::make_value<item>( newitem );
2114     }
2115     if( data.has_object( "armor_item" ) ) {
2116         JsonIn *armor_item_json = data.get_raw( "armor_item" );
2117         item newitem;
2118         newitem.deserialize( *armor_item_json );
2119         armor_item = cata::make_value<item>( newitem );
2120     }
2121     if( data.has_object( "storage_item" ) ) {
2122         JsonIn *storage_item_json = data.get_raw( "storage_item" );
2123         item newitem;
2124         newitem.deserialize( *storage_item_json );
2125         storage_item = cata::make_value<item>( newitem );
2126     }
2127     if( data.has_object( "battery_item" ) ) {
2128         JsonIn *battery_item_json = data.get_raw( "battery_item" );
2129         item newitem;
2130         newitem.deserialize( *battery_item_json );
2131         battery_item = cata::make_value<item>( newitem );
2132     }
2133     data.read( "hp", hp );
2134 
2135     // sp_timeout indicates an old save, prior to the special_attacks refactor
2136     if( data.has_array( "sp_timeout" ) ) {
2137         JsonArray parray = data.get_array( "sp_timeout" );
2138         size_t index = 0;
2139         int ptimeout = 0;
2140         while( parray.has_more() && index < type->special_attacks_names.size() ) {
2141             if( parray.read_next( ptimeout ) ) {
2142                 // assume timeouts saved in same order as current monsters.json listing
2143                 const std::string &aname = type->special_attacks_names[index++];
2144                 auto &entry = special_attacks[aname];
2145                 if( ptimeout >= 0 ) {
2146                     entry.cooldown = ptimeout;
2147                 } else { // -1 means disabled, unclear what <-1 values mean in old saves
2148                     entry.cooldown = type->special_attacks.at( aname )->cooldown;
2149                     entry.enabled = false;
2150                 }
2151             }
2152         }
2153     }
2154 
2155     // special_attacks indicates a save after the special_attacks refactor
2156     if( data.has_object( "special_attacks" ) ) {
2157         for( const JsonMember member : data.get_object( "special_attacks" ) ) {
2158             JsonObject saobject = member.get_object();
2159             saobject.allow_omitted_members();
2160             auto &entry = special_attacks[member.name()];
2161             entry.cooldown = saobject.get_int( "cooldown" );
2162             entry.enabled = saobject.get_bool( "enabled" );
2163         }
2164     }
2165 
2166     // make sure the loaded monster has every special attack its type says it should have
2167     for( const auto &sa : type->special_attacks ) {
2168         const std::string &aname = sa.first;
2169         if( special_attacks.find( aname ) == special_attacks.end() ) {
2170             auto &entry = special_attacks[aname];
2171             entry.cooldown = rng( 0, sa.second->cooldown );
2172         }
2173     }
2174 
2175     data.read( "friendly", friendly );
2176     if( data.has_member( "mission_ids" ) ) {
2177         data.read( "mission_ids", mission_ids );
2178     } else if( data.has_member( "mission_id" ) ) {
2179         const int mission_id = data.get_int( "mission_id" );
2180         if( mission_id > 0 ) {
2181             mission_ids = { mission_id };
2182         } else {
2183             mission_ids.clear();
2184         }
2185     }
2186     data.read( "mission_fused", mission_fused );
2187     data.read( "no_extra_death_drops", no_extra_death_drops );
2188     data.read( "dead", dead );
2189     data.read( "anger", anger );
2190     data.read( "morale", morale );
2191     data.read( "hallucination", hallucination );
2192     data.read( "stairscount", staircount ); // really?
2193     data.read( "fish_population", fish_population );
2194     data.read( "summon_time_limit", summon_time_limit );
2195 
2196     // This is relative to the monster so it isn't invalidated by map shifting.
2197     tripoint destination;
2198     data.read( "destination", destination );
2199     goal = pos() + destination;
2200 
2201     upgrades = data.get_bool( "upgrades", type->upgrades );
2202     upgrade_time = data.get_int( "upgrade_time", -1 );
2203 
2204     reproduces = data.get_bool( "reproduces", type->reproduces );
2205     baby_timer.reset();
2206     data.read( "baby_timer", baby_timer );
2207     if( baby_timer && *baby_timer == calendar::before_time_starts ) {
2208         baby_timer.reset();
2209     }
2210 
2211     biosignatures = data.get_bool( "biosignatures", type->biosignatures );
2212     biosig_timer = time_point( data.get_int( "biosig_timer", -1 ) );
2213 
2214     data.read( "udder_timer", udder_timer );
2215 
2216     horde_attraction = static_cast<monster_horde_attraction>( data.get_int( "horde_attraction", 0 ) );
2217 
2218     data.read( "inv", inv );
2219     data.read( "dragged_foe_id", dragged_foe_id );
2220 
2221     data.read( "ammo", ammo );
2222 
2223     // TODO: Remove blob migration after 0.F
2224     const std::string faction_string = data.get_string( "faction", "" );
2225     faction = mfaction_str_id( faction_string == "blob" ? "slime" : faction_string );
2226     data.read( "mounted_player_id", mounted_player_id );
2227     data.read( "path", path );
2228 }
2229 
2230 /*
2231  * Save, json ed; serialization that won't break as easily. In theory.
2232  */
serialize(JsonOut & json) const2233 void monster::serialize( JsonOut &json ) const
2234 {
2235     json.start_object();
2236     // This must be after the json object has been started, so any super class
2237     // puts their data into the same json object.
2238     store( json );
2239     json.end_object();
2240 }
2241 
store(JsonOut & json) const2242 void monster::store( JsonOut &json ) const
2243 {
2244     Creature::store( json );
2245     json.member( "typeid", type->id );
2246     json.member( "unique_name", unique_name );
2247     json.member( "posx", position.x );
2248     json.member( "posy", position.y );
2249     json.member( "posz", position.z );
2250     json.member( "wandx", wander_pos.x );
2251     json.member( "wandy", wander_pos.y );
2252     json.member( "wandz", wander_pos.z );
2253     json.member( "wandf", wandf );
2254     json.member( "hp", hp );
2255     json.member( "special_attacks", special_attacks );
2256     json.member( "friendly", friendly );
2257     json.member( "fish_population", fish_population );
2258     json.member( "faction", faction.id().str() );
2259     json.member( "mission_ids", mission_ids );
2260     json.member( "mission_fused", mission_fused );
2261     json.member( "no_extra_death_drops", no_extra_death_drops );
2262     json.member( "dead", dead );
2263     json.member( "anger", anger );
2264     json.member( "morale", morale );
2265     json.member( "hallucination", hallucination );
2266     json.member( "stairscount", staircount );
2267     if( tied_item ) {
2268         json.member( "tied_item", *tied_item );
2269     }
2270     if( tack_item ) {
2271         json.member( "tack_item", *tack_item );
2272     }
2273     if( armor_item ) {
2274         json.member( "armor_item", *armor_item );
2275     }
2276     if( storage_item ) {
2277         json.member( "storage_item", *storage_item );
2278     }
2279     if( battery_item ) {
2280         json.member( "battery_item", *battery_item );
2281     }
2282     // Store the relative position of the goal so it loads correctly after a map shift.
2283     json.member( "destination", goal - pos() );
2284     json.member( "ammo", ammo );
2285     json.member( "underwater", underwater );
2286     json.member( "upgrades", upgrades );
2287     json.member( "upgrade_time", upgrade_time );
2288     json.member( "reproduces", reproduces );
2289     json.member( "baby_timer", baby_timer );
2290     json.member( "biosignatures", biosignatures );
2291     json.member( "biosig_timer", biosig_timer );
2292     json.member( "udder_timer", udder_timer );
2293 
2294     json.member( "summon_time_limit", summon_time_limit );
2295 
2296     if( horde_attraction > MHA_NULL && horde_attraction < NUM_MONSTER_HORDE_ATTRACTION ) {
2297         json.member( "horde_attraction", horde_attraction );
2298     }
2299     json.member( "inv", inv );
2300 
2301     json.member( "dragged_foe_id", dragged_foe_id );
2302     // storing the rider
2303     json.member( "mounted_player_id", mounted_player_id );
2304     json.member( "path", path );
2305 }
2306 
serialize(JsonOut & json) const2307 void mon_special_attack::serialize( JsonOut &json ) const
2308 {
2309     json.start_object();
2310     json.member( "cooldown", cooldown );
2311     json.member( "enabled", enabled );
2312     json.end_object();
2313 }
2314 
serialize(JsonOut & jsout) const2315 void time_point::serialize( JsonOut &jsout ) const
2316 {
2317     jsout.write( turn_ );
2318 }
2319 
deserialize(JsonIn & jsin)2320 void time_point::deserialize( JsonIn &jsin )
2321 {
2322     turn_ = jsin.get_int();
2323 }
2324 
serialize(JsonOut & jsout) const2325 void time_duration::serialize( JsonOut &jsout ) const
2326 {
2327     jsout.write( turns_ );
2328 }
2329 
deserialize(JsonIn & jsin)2330 void time_duration::deserialize( JsonIn &jsin )
2331 {
2332     if( jsin.test_string() ) {
2333         *this = read_from_json_string<time_duration>( jsin, time_duration::units );
2334     } else {
2335         turns_ = jsin.get_int();
2336     }
2337 }
2338 
2339 ////////////////////////////////////////////////////////////////////////////////////////////////////
2340 ///// item.h
2341 
serialize(JsonOut & jsout) const2342 void item::craft_data::serialize( JsonOut &jsout ) const
2343 {
2344     jsout.start_object();
2345     jsout.member( "making", making->ident().str() );
2346     jsout.member( "disassembly", disassembly );
2347     jsout.member( "comps_used", comps_used );
2348     jsout.member( "next_failure_point", next_failure_point );
2349     jsout.member( "tools_to_continue", tools_to_continue );
2350     jsout.member( "cached_tool_selections", cached_tool_selections );
2351     jsout.end_object();
2352 }
2353 
deserialize(JsonIn & jsin)2354 void item::craft_data::deserialize( JsonIn &jsin )
2355 {
2356     deserialize( jsin.get_object() );
2357 }
2358 
deserialize(const JsonObject & obj)2359 void item::craft_data::deserialize( const JsonObject &obj )
2360 {
2361     obj.allow_omitted_members();
2362     std::string recipe_string = obj.get_string( "making" );
2363     disassembly = obj.get_bool( "disassembly", false );
2364     if( disassembly ) {
2365         making = &recipe_dictionary::get_uncraft( itype_id( recipe_string ) );
2366     } else {
2367         making = &recipe_id( recipe_string ).obj();
2368     }
2369     obj.read( "comps_used", comps_used );
2370     next_failure_point = obj.get_int( "next_failure_point", -1 );
2371     tools_to_continue = obj.get_bool( "tools_to_continue", false );
2372     obj.read( "cached_tool_selections", cached_tool_selections );
2373 }
2374 
2375 // Template parameter because item::craft_data is private and I don't want to make it public.
2376 template<typename T>
load_legacy_craft_data(io::JsonObjectInputArchive & archive,T & value)2377 static void load_legacy_craft_data( io::JsonObjectInputArchive &archive, T &value )
2378 {
2379     archive.allow_omitted_members();
2380     if( archive.has_member( "making" ) ) {
2381         value = cata::make_value<typename T::element_type>();
2382         value->deserialize( archive );
2383     }
2384 }
2385 
2386 // Dummy function as we never load anything from an output archive.
2387 template<typename T>
load_legacy_craft_data(io::JsonObjectOutputArchive &,T &)2388 static void load_legacy_craft_data( io::JsonObjectOutputArchive &, T & )
2389 {
2390 }
2391 
2392 static std::set<itype_id> charge_removal_blacklist;
2393 
load_charge_removal_blacklist(const JsonObject & jo,const std::string &)2394 void load_charge_removal_blacklist( const JsonObject &jo, const std::string &/*src*/ )
2395 {
2396     jo.allow_omitted_members();
2397     jo.read( "list", charge_removal_blacklist );
2398 }
2399 
2400 template<typename Archive>
io(Archive & archive)2401 void item::io( Archive &archive )
2402 {
2403 
2404     itype_id orig; // original ID as loaded from JSON
2405     const auto load_type = [&]( const std::string & id ) {
2406         orig = itype_id( id );
2407         convert( item_controller->migrate_id( orig ) );
2408     };
2409 
2410     const auto load_curammo = [this]( const std::string & id ) {
2411         curammo = item::find_type( item_controller->migrate_id( itype_id( id ) ) );
2412     };
2413     const auto load_corpse = [this]( const std::string & id ) {
2414         if( itype_id( id ).is_null() ) {
2415             // backwards compatibility, nullptr should not be stored at all
2416             corpse = nullptr;
2417         } else {
2418             corpse = &mtype_id( id ).obj();
2419         }
2420     };
2421     archive.template io<const itype>( "typeid", type, load_type, []( const itype & i ) {
2422         return i.get_id().str();
2423     }, io::required_tag() );
2424 
2425     // normalize legacy saves to always have charges >= 0
2426     archive.io( "charges", charges, 0 );
2427     charges = std::max( charges, 0 );
2428 
2429     archive.io( "energy", energy, 0_mJ );
2430 
2431     int cur_phase = static_cast<int>( current_phase );
2432     archive.io( "burnt", burnt, 0 );
2433     archive.io( "poison", poison, 0 );
2434     archive.io( "frequency", frequency, 0 );
2435     archive.io( "snip_id", snip_id, snippet_id::NULL_ID() );
2436     // NB! field is named `irridation` in legacy files
2437     archive.io( "irridation", irradiation, 0 );
2438     archive.io( "bday", bday, calendar::start_of_cataclysm );
2439     archive.io( "mission_id", mission_id, -1 );
2440     archive.io( "player_id", player_id, -1 );
2441     archive.io( "item_vars", item_vars, io::empty_default_tag() );
2442     // TODO: change default to empty string
2443     archive.io( "name", corpse_name, std::string() );
2444     archive.io( "owner", owner, owner.NULL_ID() );
2445     archive.io( "old_owner", old_owner, old_owner.NULL_ID() );
2446     archive.io( "invlet", invlet, '\0' );
2447     archive.io( "damaged", damage_, 0 );
2448     archive.io( "active", active, false );
2449     archive.io( "is_favorite", is_favorite, false );
2450     archive.io( "item_counter", item_counter, static_cast<decltype( item_counter )>( 0 ) );
2451     archive.io( "rot", rot, 0_turns );
2452     archive.io( "last_temp_check", last_temp_check, calendar::start_of_cataclysm );
2453     archive.io( "current_phase", cur_phase, static_cast<int>( type->phase ) );
2454     archive.io( "techniques", techniques, io::empty_default_tag() );
2455     archive.io( "faults", faults, io::empty_default_tag() );
2456     archive.io( "item_tags", item_tags, io::empty_default_tag() );
2457     archive.io( "components", components, io::empty_default_tag() );
2458     archive.io( "specific_energy", specific_energy, -10 );
2459     archive.io( "temperature", temperature, 0 );
2460     archive.io( "recipe_charges", recipe_charges, 1 );
2461     // Legacy: remove flag check/unset after 0.F
2462     archive.io( "ethereal", ethereal, has_flag( flag_ETHEREAL_ITEM ) );
2463     unset_flag( flag_ETHEREAL_ITEM );
2464     archive.template io<const itype>( "curammo", curammo, load_curammo,
2465     []( const itype & i ) {
2466         return i.get_id().str();
2467     } );
2468     archive.template io<const mtype>( "corpse", corpse, load_corpse,
2469     []( const mtype & i ) {
2470         return i.id.str();
2471     } );
2472     archive.io( "craft_data", craft_data_, decltype( craft_data_ )() );
2473     archive.io( "light", light.luminance, nolight.luminance );
2474     archive.io( "light_width", light.width, nolight.width );
2475     archive.io( "light_dir", light.direction, nolight.direction );
2476 
2477     static const cata::value_ptr<relic> null_relic_ptr = nullptr;
2478     archive.io( "relic_data", relic_data, null_relic_ptr );
2479 
2480     item_controller->migrate_item( orig, *this );
2481 
2482     if( !Archive::is_input::value ) {
2483         return;
2484     }
2485     /* Loading has finished, following code is to ensure consistency and fixes bugs in saves. */
2486 
2487     load_legacy_craft_data( archive, craft_data_ );
2488 
2489     double float_damage = 0;
2490     if( archive.read( "damage", float_damage ) ) {
2491         damage_ = std::min( std::max( min_damage(),
2492                                       static_cast<int>( float_damage * itype::damage_scale ) ),
2493                             max_damage() );
2494     }
2495 
2496     int note = 0;
2497     const bool note_read = archive.read( "note", note );
2498 
2499     // Old saves used to only contain one of those values (stored under "poison"), it would be
2500     // loaded into a union of those members. Now they are separate members and must be set separately.
2501     if( poison != 0 && note == 0 && !type->snippet_category.empty() ) {
2502         std::swap( note, poison );
2503     }
2504     if( poison != 0 && frequency == 0 && ( typeId() == itype_radio_on || typeId() == itype_radio ) ) {
2505         std::swap( frequency, poison );
2506     }
2507     if( poison != 0 && irradiation == 0 && typeId() == itype_rad_badge ) {
2508         std::swap( irradiation, poison );
2509     }
2510 
2511     // erase all invalid flags (not defined in flags.json)
2512     // warning was generated earlier on load
2513     erase_if( item_tags, [&]( const flag_id & f ) {
2514         return !f.is_valid();
2515     } );
2516 
2517     if( note_read ) {
2518         snip_id = SNIPPET.migrate_hash_to_id( note );
2519     } else {
2520         cata::optional<std::string> snip;
2521         if( archive.read( "snippet_id", snip ) && snip ) {
2522             snip_id = snippet_id( snip.value() );
2523         }
2524     }
2525 
2526     // Compatibility for item type changes: for example soap changed from being a generic item
2527     // (item::charges -1 or 0 or anything else) to comestible (and thereby counted by charges),
2528     // old saves still have invalid charges, this fixes the charges value to the default charges.
2529     if( count_by_charges() && charges <= 0 ) {
2530         charges = item( type, calendar::turn_zero ).charges;
2531     }
2532     if( is_food() ) {
2533         active = true;
2534     }
2535     if( !active && ( has_own_flag( flag_HOT ) || has_own_flag( flag_COLD ) ||
2536                      has_own_flag( flag_WET ) ) ) {
2537         // Some hot/cold items from legacy saves may be inactive
2538         active = true;
2539     }
2540     std::string mode;
2541     if( archive.read( "mode", mode ) ) {
2542         // only for backward compatibility (nowadays mode is stored in item_vars)
2543         gun_set_mode( gun_mode_id( mode ) );
2544     }
2545 
2546     // Books without any chapters don't need to store a remaining-chapters
2547     // counter, it will always be 0 and it prevents proper stacking.
2548     if( get_chapters() == 0 ) {
2549         for( auto it = item_vars.begin(); it != item_vars.end(); ) {
2550             if( it->first.compare( 0, 19, "remaining-chapters-" ) == 0 ) {
2551                 item_vars.erase( it++ );
2552             } else {
2553                 ++it;
2554             }
2555         }
2556     }
2557 
2558     // Remove stored translated gerund in favor of storing the inscription tool type
2559     item_vars.erase( "item_label_type" );
2560     item_vars.erase( "item_note_type" );
2561 
2562     current_phase = static_cast<phase_id>( cur_phase );
2563     // override phase if frozen, needed for legacy save
2564     if( has_own_flag( flag_FROZEN ) && current_phase == phase_id::LIQUID ) {
2565         current_phase = phase_id::SOLID;
2566     }
2567 
2568     // Activate corpses from old saves
2569     if( is_corpse() && !active ) {
2570         active = true;
2571     }
2572 
2573     if( charges != 0 && !type->can_have_charges() ) {
2574         // Types that are known to have charges, but should not have them.
2575         // We fix it here, but it's expected from bugged saves and does not require a message.
2576         if( charge_removal_blacklist.count( type->get_id() ) == 0 ) {
2577             debugmsg( "Item %s was loaded with charges, but can not have any!",
2578                       type->get_id().str() );
2579         }
2580         charges = 0;
2581     }
2582 }
2583 
migrate_content_item(const item & contained)2584 void item::migrate_content_item( const item &contained )
2585 {
2586     if( contained.is_gunmod() || contained.is_toolmod() ) {
2587         put_in( contained, item_pocket::pocket_type::MOD );
2588     } else if( typeId() == itype_usb_drive ) {
2589         // as of this migration, only usb_drive has any software in it.
2590         put_in( contained, item_pocket::pocket_type::SOFTWARE );
2591     } else if( contents.insert_item( contained, item_pocket::pocket_type::MAGAZINE ).success() ) {
2592         // left intentionally blank
2593     } else if( contents.insert_item( contained, item_pocket::pocket_type::MAGAZINE_WELL ).success() ) {
2594         // left intentionally blank
2595     } else if( is_corpse() ) {
2596         put_in( contained, item_pocket::pocket_type::CORPSE );
2597     } else if( can_contain( contained ) ) {
2598         put_in( contained, item_pocket::pocket_type::CONTAINER );
2599     } else {
2600         // we want this to silently fail - the contents will fall out later
2601         put_in( contained, item_pocket::pocket_type::MIGRATION );
2602     }
2603 }
2604 
deserialize(JsonIn & jsin)2605 void item::deserialize( JsonIn &jsin )
2606 {
2607     const JsonObject data = jsin.get_object();
2608     data.allow_omitted_members();
2609     io::JsonObjectInputArchive archive( data );
2610     io( archive );
2611     archive.allow_omitted_members();
2612     data.copy_visited_members( archive );
2613     // first half of the if statement is for migration to nested containers. remove after 0.F
2614     if( data.has_array( "contents" ) ) {
2615         std::list<item> items;
2616         data.read( "contents", items );
2617         for( const item &it : items ) {
2618             migrate_content_item( it );
2619         }
2620     } else if( data.has_object( "contents" ) ) { // non-empty contents
2621         item_contents read_contents;
2622         data.read( "contents", read_contents );
2623         contents.read_mods( read_contents );
2624         update_modified_pockets();
2625         contents.combine( read_contents );
2626 
2627         if( data.has_object( "contents" ) ) {
2628             JsonObject tested = data.get_object( "contents" );
2629             tested.allow_omitted_members();
2630             if( tested.has_array( "items" ) ) {
2631                 // migration for nested containers. leave until after 0.F
2632                 std::list<item> items;
2633                 tested.read( "items", items );
2634                 for( const item &it : items ) {
2635                     migrate_content_item( it );
2636                 }
2637             }
2638         }
2639         // contents may not be empty if other migration happened in item::io
2640     } else if( contents.empty() ) { // empty contents was not serialized, recreate pockets from the type
2641         contents = item_contents( type->pockets );
2642     }
2643 
2644     // Remove after 0.F: artifact migration code
2645     if( typeId().str().substr( 0, 9 ) == "artifact_" ) {
2646         static const relic_procgen_id proc_cult( "cult" );
2647         relic_procgen_data::generation_rules rules;
2648         rules.max_attributes = 5;
2649         rules.max_negative_power = -1000;
2650         rules.power_level = 2000;
2651 
2652         item_contents temp_migrate( contents );
2653 
2654         *this = proc_cult->create_item( rules );
2655 
2656         if( !temp_migrate.empty() ) {
2657             for( const item *it : temp_migrate.all_items_top() ) {
2658                 contents.insert_item( *it, item_pocket::pocket_type::MIGRATION );
2659             }
2660         }
2661     }
2662 }
2663 
serialize(JsonOut & json) const2664 void item::serialize( JsonOut &json ) const
2665 {
2666     // Remove after 0.F: artifact migration code
2667     if( typeId().str().substr( 0, 9 ) == "artifact_" ) {
2668         static const relic_procgen_id proc_cult( "cult" );
2669         relic_procgen_data::generation_rules rules;
2670         rules.max_attributes = 5;
2671         rules.max_negative_power = -1000;
2672         rules.power_level = 2000;
2673 
2674         proc_cult->create_item( rules ).serialize( json );
2675         return;
2676     }
2677 
2678     io::JsonObjectOutputArchive archive( json );
2679     const_cast<item *>( this )->io( archive );
2680     if( !contents.empty_real() ) {
2681         json.member( "contents", contents );
2682     }
2683 }
2684 
2685 ////////////////////////////////////////////////////////////////////////////////////////////////////
2686 ///// vehicle.h
2687 
2688 /*
2689  * vehicle_part
2690  */
deserialize(JsonIn & jsin)2691 void vehicle_part::deserialize( JsonIn &jsin )
2692 {
2693     JsonObject data = jsin.get_object();
2694     data.allow_omitted_members();
2695     vpart_id pid;
2696     data.read( "id", pid );
2697 
2698     std::map<std::string, std::pair<std::string, std::string>> deprecated = {
2699         { "laser_gun", { "laser_rifle", "none" } },
2700         { "seat_nocargo", { "seat", "none" } },
2701         { "engine_plasma", { "minireactor", "none" } },
2702         { "battery_truck", { "battery_car", "battery" } },
2703 
2704         { "diesel_tank_little", { "tank_little", "diesel" } },
2705         { "diesel_tank_small", { "tank_small", "diesel" } },
2706         { "diesel_tank_medium", { "tank_medium", "diesel" } },
2707         { "diesel_tank", { "tank", "diesel" } },
2708         { "external_diesel_tank_small", { "external_tank_small", "diesel" } },
2709         { "external_diesel_tank", { "external_tank", "diesel" } },
2710 
2711         { "gas_tank_little", { "tank_little", "gasoline" } },
2712         { "gas_tank_small", { "tank_small", "gasoline" } },
2713         { "gas_tank_medium", { "tank_medium", "gasoline" } },
2714         { "gas_tank", { "tank", "gasoline" } },
2715         { "external_gas_tank_small", { "external_tank_small", "gasoline" } },
2716         { "external_gas_tank", { "external_tank", "gasoline" } },
2717 
2718         { "water_dirty_tank_little", { "tank_little", "water" } },
2719         { "water_dirty_tank_small", { "tank_small", "water" } },
2720         { "water_dirty_tank_medium", { "tank_medium", "water" } },
2721         { "water_dirty_tank", { "tank", "water" } },
2722         { "external_water_dirty_tank_small", { "external_tank_small", "water" } },
2723         { "external_water_dirty_tank", { "external_tank", "water" } },
2724         { "dirty_water_tank_barrel", { "tank_barrel", "water" } },
2725 
2726         { "water_tank_little", { "tank_little", "water_clean" } },
2727         { "water_tank_small", { "tank_small", "water_clean" } },
2728         { "water_tank_medium", { "tank_medium", "water_clean" } },
2729         { "water_tank", { "tank", "water_clean" } },
2730         { "external_water_tank_small", { "external_tank_small", "water_clean" } },
2731         { "external_water_tank", { "external_tank", "water_clean" } },
2732         { "water_tank_barrel", { "tank_barrel", "water_clean" } },
2733 
2734         { "napalm_tank", { "tank", "napalm" } },
2735 
2736         { "hydrogen_tank", { "tank", "none" } }
2737     };
2738 
2739     auto dep = deprecated.find( pid.str() );
2740     if( dep != deprecated.end() ) {
2741         pid = vpart_id( dep->second.first );
2742     }
2743 
2744     std::tie( pid, variant ) = get_vpart_id_variant( pid );
2745 
2746     // if we don't know what type of part it is, it'll cause problems later.
2747     if( !pid.is_valid() ) {
2748         if( pid.str() == "wheel_underbody" ) {
2749             pid = vpart_id( "wheel_wide" );
2750         } else {
2751             data.throw_error( "bad vehicle part", "id" );
2752         }
2753     }
2754     id = pid;
2755     if( variant.empty() ) {
2756         data.read( "variant", variant );
2757     }
2758 
2759     if( data.has_object( "base" ) ) {
2760         data.read( "base", base );
2761     } else {
2762         // handle legacy format which didn't include the base item
2763         base = item( id.obj().base_item );
2764     }
2765 
2766     data.read( "mount_dx", mount.x );
2767     data.read( "mount_dy", mount.y );
2768     data.read( "open", open );
2769     int direction_int;
2770     data.read( "direction", direction_int );
2771     direction = units::from_degrees( direction_int );
2772     data.read( "blood", blood );
2773     data.read( "enabled", enabled );
2774     data.read( "flags", flags );
2775     data.read( "passenger_id", passenger_id );
2776     if( data.has_int( "z_offset" ) ) {
2777         int z_offset = data.get_int( "z_offset" );
2778         if( std::abs( z_offset ) > 10 ) {
2779             data.throw_error( "z_offset out of range", "z_offset" );
2780         }
2781         precalc[0].z = z_offset;
2782         precalc[1].z = z_offset;
2783     }
2784     JsonArray ja = data.get_array( "carry" );
2785     // count down from size - 1, then stop after unsigned long 0 - 1 becomes MAX_INT
2786     for( size_t index = ja.size() - 1; index < ja.size(); index-- ) {
2787         carry_names.push( ja.get_string( index ) );
2788     }
2789     data.read( "crew_id", crew_id );
2790     data.read( "items", items );
2791     data.read( "target_first_x", target.first.x );
2792     data.read( "target_first_y", target.first.y );
2793     data.read( "target_first_z", target.first.z );
2794     data.read( "target_second_x", target.second.x );
2795     data.read( "target_second_y", target.second.y );
2796     data.read( "target_second_z", target.second.z );
2797     data.read( "ammo_pref", ammo_pref );
2798 
2799     if( data.has_int( "hp" ) && id.obj().durability > 0 ) {
2800         // migrate legacy savegames exploiting that all base items at that time had max_damage() of 4
2801         base.set_damage( 4 * itype::damage_scale - 4 * itype::damage_scale * data.get_int( "hp" ) /
2802                          id.obj().durability );
2803     }
2804 
2805     // legacy turrets loaded ammo via a pseudo CARGO space
2806     if( is_turret() && !items.empty() ) {
2807         const int qty = std::accumulate( items.begin(), items.end(), 0, []( int lhs, const item & rhs ) {
2808             return lhs + rhs.charges;
2809         } );
2810         ammo_set( items.begin()->ammo_current(), qty );
2811         items.clear();
2812     }
2813 }
2814 
serialize(JsonOut & json) const2815 void vehicle_part::serialize( JsonOut &json ) const
2816 {
2817     json.start_object();
2818     json.member( "id", id.str() );
2819     if( !variant.empty() ) {
2820         json.member( "variant", variant );
2821     }
2822     json.member( "base", base );
2823     json.member( "mount_dx", mount.x );
2824     json.member( "mount_dy", mount.y );
2825     json.member( "open", open );
2826     json.member( "direction", std::lround( to_degrees( direction ) ) );
2827     json.member( "blood", blood );
2828     json.member( "enabled", enabled );
2829     json.member( "flags", flags );
2830     if( !carry_names.empty() ) {
2831         std::stack<std::string, std::vector<std::string> > carry_copy = carry_names;
2832         json.member( "carry" );
2833         json.start_array();
2834         while( !carry_copy.empty() ) {
2835             json.write( carry_copy.top() );
2836             carry_copy.pop();
2837         }
2838         json.end_array();
2839     }
2840     json.member( "passenger_id", passenger_id );
2841     json.member( "crew_id", crew_id );
2842     if( precalc[0].z ) {
2843         json.member( "z_offset", precalc[0].z );
2844     }
2845     json.member( "items", items );
2846     if( target.first != tripoint_min ) {
2847         json.member( "target_first_x", target.first.x );
2848         json.member( "target_first_y", target.first.y );
2849         json.member( "target_first_z", target.first.z );
2850     }
2851     if( target.second != tripoint_min ) {
2852         json.member( "target_second_x", target.second.x );
2853         json.member( "target_second_y", target.second.y );
2854         json.member( "target_second_z", target.second.z );
2855     }
2856     json.member( "ammo_pref", ammo_pref );
2857     json.end_object();
2858 }
2859 
2860 /*
2861  * label
2862  */
deserialize(JsonIn & jsin)2863 void label::deserialize( JsonIn &jsin )
2864 {
2865     JsonObject data = jsin.get_object();
2866     data.allow_omitted_members();
2867     data.read( "x", x );
2868     data.read( "y", y );
2869     data.read( "text", text );
2870 }
2871 
serialize(JsonOut & json) const2872 void label::serialize( JsonOut &json ) const
2873 {
2874     json.start_object();
2875     json.member( "x", x );
2876     json.member( "y", y );
2877     json.member( "text", text );
2878     json.end_object();
2879 }
2880 
deserialize(JsonIn & jsin)2881 void smart_controller_config::deserialize( JsonIn &jsin )
2882 {
2883     JsonObject data = jsin.get_object();
2884     data.allow_omitted_members();
2885     data.read( "bat_lo", battery_lo );
2886     data.read( "bat_hi", battery_hi );
2887 }
2888 
serialize(JsonOut & json) const2889 void smart_controller_config::serialize( JsonOut &json ) const
2890 {
2891     json.start_object();
2892     json.member( "bat_lo", battery_lo );
2893     json.member( "bat_hi", battery_hi );
2894     json.end_object();
2895 }
2896 
2897 /*
2898  * Load vehicle from a json blob that might just exceed player in size.
2899  */
deserialize(JsonIn & jsin)2900 void vehicle::deserialize( JsonIn &jsin )
2901 {
2902     JsonObject data = jsin.get_object();
2903     data.allow_omitted_members();
2904 
2905     int fdir = 0;
2906     int mdir = 0;
2907 
2908     data.read( "type", type );
2909     data.read( "posx", pos.x );
2910     data.read( "posy", pos.y );
2911     data.read( "om_id", om_id );
2912     data.read( "faceDir", fdir );
2913     data.read( "moveDir", mdir );
2914     int turn_dir_int;
2915     data.read( "turn_dir", turn_dir_int );
2916     turn_dir = units::from_degrees( turn_dir_int );
2917     data.read( "velocity", velocity );
2918     data.read( "falling", is_falling );
2919     data.read( "floating", is_floating );
2920     data.read( "flying", is_flying );
2921     data.read( "cruise_velocity", cruise_velocity );
2922     data.read( "vertical_velocity", vertical_velocity );
2923     data.read( "cruise_on", cruise_on );
2924     data.read( "engine_on", engine_on );
2925     data.read( "tracking_on", tracking_on );
2926     data.read( "skidding", skidding );
2927     data.read( "of_turn_carry", of_turn_carry );
2928     data.read( "is_locked", is_locked );
2929     data.read( "is_alarm_on", is_alarm_on );
2930     data.read( "camera_on", camera_on );
2931     if( !data.read( "last_update_turn", last_update ) ) {
2932         last_update = calendar::turn;
2933     }
2934 
2935     units::angle fdir_angle = units::from_degrees( fdir );
2936     face.init( fdir_angle );
2937     move.init( units::from_degrees( mdir ) );
2938     data.read( "name", name );
2939     std::string temp_id;
2940     std::string temp_old_id;
2941     data.read( "owner", temp_id );
2942     data.read( "old_owner", temp_old_id );
2943     // for savegames before the change to faction_id for ownership.
2944     if( temp_id.empty() ) {
2945         owner = faction_id::NULL_ID();
2946     } else {
2947         owner = faction_id( temp_id );
2948     }
2949     if( temp_old_id.empty() ) {
2950         old_owner = faction_id::NULL_ID();
2951     } else {
2952         old_owner = faction_id( temp_old_id );
2953     }
2954     data.read( "theft_time", theft_time );
2955 
2956     data.read( "parts", parts );
2957     // we persist the pivot anchor so that if the rules for finding
2958     // the pivot change, existing vehicles do not shift around.
2959     // Loading vehicles that predate the pivot logic is a special
2960     // case of this, they will load with an anchor of (0,0) which
2961     // is what they're expecting.
2962     data.read( "pivot", pivot_anchor[0] );
2963     pivot_anchor[1] = pivot_anchor[0];
2964     pivot_rotation[1] = pivot_rotation[0] = fdir_angle;
2965     data.read( "is_following", is_following );
2966     data.read( "is_patrolling", is_patrolling );
2967     data.read( "autodrive_local_target", autodrive_local_target );
2968     data.read( "airworthy", flyable );
2969     data.read( "summon_time_limit", summon_time_limit );
2970     data.read( "magic", magic );
2971 
2972     smart_controller_cfg = cata::nullopt;
2973     data.read( "smart_controller", smart_controller_cfg );
2974 
2975     // Need to manually backfill the active item cache since the part loader can't call its vehicle.
2976     for( const vpart_reference &vp : get_any_parts( VPFLAG_CARGO ) ) {
2977         auto it = vp.part().items.begin();
2978         auto end = vp.part().items.end();
2979         for( ; it != end; ++it ) {
2980             if( it->needs_processing() ) {
2981                 active_items.add( *it, vp.mount() );
2982             }
2983             // remove after 0.F
2984             if( savegame_loading_version < 33 ) {
2985                 migrate_item_charges( *it );
2986             }
2987         }
2988     }
2989 
2990     for( const vpart_reference &vp : get_any_parts( "TURRET" ) ) {
2991         install_part( vp.mount(), vpart_id( "turret_mount" ) );
2992 
2993         //Forcibly set turrets' targeting mode to manual if no turret control unit is
2994         //present on turret's tile on loading save
2995         if( !has_part( global_part_pos3( vp.part() ), "TURRET_CONTROLS" ) ) {
2996             vp.part().enabled = false;
2997         }
2998         //Set turret control unit's state equal to turret's targeting mode on loading save
2999         for( const vpart_reference &turret_part : get_any_parts( "TURRET_CONTROLS" ) ) {
3000             turret_part.part().enabled = vp.part().enabled;
3001         }
3002     }
3003 
3004     refresh();
3005 
3006     data.read( "tags", tags );
3007     data.read( "labels", labels );
3008 
3009     point p;
3010     zone_data zd;
3011     for( JsonObject sdata : data.get_array( "zones" ) ) {
3012         sdata.allow_omitted_members();
3013         sdata.read( "point", p );
3014         sdata.read( "zone", zd );
3015         loot_zones.emplace( p, zd );
3016     }
3017     data.read( "other_tow_point", tow_data.other_towing_point );
3018     // Note that it's possible for a vehicle to be loaded midway
3019     // through a turn if the player is driving REALLY fast and their
3020     // own vehicle motion takes them in range. An undefined value for
3021     // on_turn caused occasional weirdness if the undefined value
3022     // happened to be positive.
3023     //
3024     // Setting it to zero means it won't get to move until the start
3025     // of the next turn, which is what happens anyway if it gets
3026     // loaded anywhere but midway through a driving cycle.
3027     //
3028     // Something similar to vehicle::gain_moves() would be ideal, but
3029     // that can't be used as it currently stands because it would also
3030     // make it instantly fire all its turrets upon load.
3031     of_turn = 0;
3032 
3033     /** Legacy saved games did not store part enabled status within parts */
3034     const auto set_legacy_state = [&]( const std::string & var, const std::string & flag ) {
3035         if( data.get_bool( var, false ) ) {
3036             for( const vpart_reference &vp : get_any_parts( flag ) ) {
3037                 vp.part().enabled = true;
3038             }
3039         }
3040     };
3041 
3042     set_legacy_state( "stereo_on", "STEREO" );
3043     set_legacy_state( "chimes_on", "CHIMES" );
3044     set_legacy_state( "fridge_on", "FRIDGE" );
3045     set_legacy_state( "reaper_on", "REAPER" );
3046     set_legacy_state( "planter_on", "PLANTER" );
3047     set_legacy_state( "recharger_on", "RECHARGE" );
3048     set_legacy_state( "scoop_on", "SCOOP" );
3049     set_legacy_state( "plow_on", "PLOW" );
3050     set_legacy_state( "reactor_on", "REACTOR" );
3051 }
3052 
serialize(JsonOut & json) const3053 void vehicle::serialize( JsonOut &json ) const
3054 {
3055     json.start_object();
3056     json.member( "type", type );
3057     json.member( "posx", pos.x );
3058     json.member( "posy", pos.y );
3059     json.member( "om_id", om_id );
3060     json.member( "faceDir", std::lround( to_degrees( face.dir() ) ) );
3061     json.member( "moveDir", std::lround( to_degrees( move.dir() ) ) );
3062     json.member( "turn_dir", std::lround( to_degrees( turn_dir ) ) );
3063     json.member( "velocity", velocity );
3064     json.member( "falling", is_falling );
3065     json.member( "floating", is_floating );
3066     json.member( "flying", is_flying );
3067     json.member( "cruise_velocity", cruise_velocity );
3068     json.member( "vertical_velocity", vertical_velocity );
3069     json.member( "cruise_on", cruise_on );
3070     json.member( "engine_on", engine_on );
3071     json.member( "tracking_on", tracking_on );
3072     json.member( "skidding", skidding );
3073     json.member( "of_turn_carry", of_turn_carry );
3074     json.member( "name", name );
3075     json.member( "owner", owner );
3076     json.member( "old_owner", old_owner );
3077     json.member( "theft_time", theft_time );
3078     json.member( "parts", parts );
3079     json.member( "tags", tags );
3080     json.member( "labels", labels );
3081     json.member( "zones" );
3082     json.start_array();
3083     for( auto const &z : loot_zones ) {
3084         json.start_object();
3085         json.member( "point", z.first );
3086         json.member( "zone", z.second );
3087         json.end_object();
3088     }
3089     json.end_array();
3090     tripoint other_tow_temp_point;
3091     if( is_towed() ) {
3092         vehicle *tower = tow_data.get_towed_by();
3093         if( tower ) {
3094             other_tow_temp_point = tower->global_part_pos3( tower->get_tow_part() );
3095         }
3096     }
3097     json.member( "other_tow_point", other_tow_temp_point );
3098 
3099     json.member( "is_locked", is_locked );
3100     json.member( "is_alarm_on", is_alarm_on );
3101     json.member( "camera_on", camera_on );
3102     json.member( "last_update_turn", last_update );
3103     json.member( "pivot", pivot_anchor[0] );
3104     json.member( "is_following", is_following );
3105     json.member( "is_patrolling", is_patrolling );
3106     json.member( "autodrive_local_target", autodrive_local_target );
3107     json.member( "airworthy", flyable );
3108     json.member( "summon_time_limit", summon_time_limit );
3109     json.member( "magic", magic );
3110     json.member( "smart_controller", smart_controller_cfg );
3111 
3112     json.end_object();
3113 }
3114 
3115 ////////////////// mission.h
3116 ////
deserialize(JsonIn & jsin)3117 void mission::deserialize( JsonIn &jsin )
3118 {
3119     JsonObject jo = jsin.get_object();
3120     jo.allow_omitted_members();
3121 
3122     if( jo.has_int( "type_id" ) ) {
3123         type = &mission_type::from_legacy( jo.get_int( "type_id" ) ).obj();
3124     } else if( jo.has_string( "type_id" ) ) {
3125         type = &mission_type_id( jo.get_string( "type_id" ) ).obj();
3126     } else {
3127         debugmsg( "Saved mission has no type" );
3128         type = &mission_type::get_all().front();
3129     }
3130 
3131     bool failed;
3132     bool was_started;
3133     std::string status_string;
3134     if( jo.read( "status", status_string ) ) {
3135         status = status_from_string( status_string );
3136     } else if( jo.read( "failed", failed ) && failed ) {
3137         status = mission_status::failure;
3138     } else if( jo.read( "was_started", was_started ) && !was_started ) {
3139         status = mission_status::yet_to_start;
3140     } else {
3141         // Note: old code had no idea of successful missions!
3142         // We can't check properly here, since most of the game isn't loaded
3143         status = mission_status::in_progress;
3144     }
3145 
3146     jo.read( "value", value );
3147     jo.read( "kill_count_to_reach", kill_count_to_reach );
3148     jo.read( "reward", reward );
3149     jo.read( "uid", uid );
3150     JsonArray ja = jo.get_array( "target" );
3151     if( ja.size() == 3 ) {
3152         target.x() = ja.get_int( 0 );
3153         target.y() = ja.get_int( 1 );
3154         target.z() = ja.get_int( 2 );
3155     } else if( ja.size() == 2 ) {
3156         target.x() = ja.get_int( 0 );
3157         target.y() = ja.get_int( 1 );
3158     }
3159 
3160     if( jo.has_int( "follow_up" ) ) {
3161         follow_up = mission_type::from_legacy( jo.get_int( "follow_up" ) );
3162     } else if( jo.has_string( "follow_up" ) ) {
3163         follow_up = mission_type_id( jo.get_string( "follow_up" ) );
3164     }
3165 
3166     jo.read( "item_id", item_id );
3167 
3168     const std::string omid = jo.get_string( "target_id", "" );
3169     if( !omid.empty() ) {
3170         target_id = string_id<oter_type_t>( omid );
3171     }
3172 
3173     if( jo.has_int( "recruit_class" ) ) {
3174         recruit_class = npc_class::from_legacy_int( jo.get_int( "recruit_class" ) );
3175     } else {
3176         recruit_class = npc_class_id( jo.get_string( "recruit_class", "NC_NONE" ) );
3177     }
3178 
3179     jo.read( "target_npc_id", target_npc_id );
3180     jo.read( "monster_type", monster_type );
3181     jo.read( "monster_species", monster_species );
3182     jo.read( "monster_kill_goal", monster_kill_goal );
3183     jo.read( "deadline", deadline );
3184     jo.read( "step", step );
3185     jo.read( "item_count", item_count );
3186     jo.read( "npc_id", npc_id );
3187     jo.read( "good_fac_id", good_fac_id );
3188     jo.read( "bad_fac_id", bad_fac_id );
3189     jo.read( "player_id", player_id );
3190 }
3191 
serialize(JsonOut & json) const3192 void mission::serialize( JsonOut &json ) const
3193 {
3194     json.start_object();
3195 
3196     json.member( "type_id", type->id );
3197     json.member( "status", status_to_string( status ) );
3198     json.member( "value", value );
3199     json.member( "kill_count_to_reach", kill_count_to_reach );
3200     json.member( "reward", reward );
3201     json.member( "uid", uid );
3202 
3203     json.member( "target" );
3204     json.start_array();
3205     json.write( target.x() );
3206     json.write( target.y() );
3207     json.write( target.z() );
3208     json.end_array();
3209 
3210     json.member( "item_id", item_id );
3211     json.member( "item_count", item_count );
3212     json.member( "target_id", target_id.str() );
3213     json.member( "recruit_class", recruit_class );
3214     json.member( "target_npc_id", target_npc_id );
3215     json.member( "monster_type", monster_type );
3216     json.member( "monster_species", monster_species );
3217     json.member( "monster_kill_goal", monster_kill_goal );
3218     json.member( "deadline", deadline );
3219     json.member( "npc_id", npc_id );
3220     json.member( "good_fac_id", good_fac_id );
3221     json.member( "bad_fac_id", bad_fac_id );
3222     json.member( "step", step );
3223     json.member( "follow_up", follow_up );
3224     json.member( "player_id", player_id );
3225 
3226     json.end_object();
3227 }
3228 
3229 ////////////////// faction.h
3230 ////
deserialize(JsonIn & jsin)3231 void faction::deserialize( JsonIn &jsin )
3232 {
3233     JsonObject jo = jsin.get_object();
3234     jo.allow_omitted_members();
3235 
3236     jo.read( "id", id );
3237     jo.read( "name", name );
3238     jo.read( "likes_u", likes_u );
3239     jo.read( "respects_u", respects_u );
3240     jo.read( "known_by_u", known_by_u );
3241     jo.read( "size", size );
3242     jo.read( "power", power );
3243     if( !jo.read( "food_supply", food_supply ) ) {
3244         food_supply = 100;
3245     }
3246     if( !jo.read( "wealth", wealth ) ) {
3247         wealth = 100;
3248     }
3249     if( jo.has_array( "opinion_of" ) ) {
3250         opinion_of = jo.get_int_array( "opinion_of" );
3251     }
3252     load_relations( jo );
3253 }
3254 
serialize(JsonOut & json) const3255 void faction::serialize( JsonOut &json ) const
3256 {
3257     json.start_object();
3258 
3259     json.member( "id", id );
3260     json.member( "name", name );
3261     json.member( "likes_u", likes_u );
3262     json.member( "respects_u", respects_u );
3263     json.member( "known_by_u", known_by_u );
3264     json.member( "size", size );
3265     json.member( "power", power );
3266     json.member( "food_supply", food_supply );
3267     json.member( "wealth", wealth );
3268     json.member( "opinion_of", opinion_of );
3269     json.member( "relations" );
3270     json.start_object();
3271     for( const auto &rel_data : relations ) {
3272         json.member( rel_data.first );
3273         json.start_object();
3274         for( const auto &rel_flag : npc_factions::relation_strs ) {
3275             json.member( rel_flag.first, rel_data.second.test( rel_flag.second ) );
3276         }
3277         json.end_object();
3278     }
3279     json.end_object();
3280 
3281     json.end_object();
3282 }
3283 
store(JsonOut & jsout) const3284 void Creature::store( JsonOut &jsout ) const
3285 {
3286     jsout.member( "moves", moves );
3287     jsout.member( "pain", pain );
3288 
3289     // killer is not stored, it's temporary anyway, any creature that has a non-null
3290     // killer is dead (as per definition) and should not be stored.
3291 
3292     jsout.member( "effects", *effects );
3293 
3294     jsout.member( "damage_over_time_map", damage_over_time_map );
3295     jsout.member( "values", values );
3296 
3297     jsout.member( "blocks_left", num_blocks );
3298     jsout.member( "dodges_left", num_dodges );
3299     jsout.member( "num_blocks_bonus", num_blocks_bonus );
3300     jsout.member( "num_dodges_bonus", num_dodges_bonus );
3301 
3302     jsout.member( "armor_bash_bonus", armor_bash_bonus );
3303     jsout.member( "armor_cut_bonus", armor_cut_bonus );
3304     jsout.member( "armor_bullet_bonus", armor_bullet_bonus );
3305 
3306     jsout.member( "speed", speed_base );
3307 
3308     jsout.member( "speed_bonus", speed_bonus );
3309     jsout.member( "dodge_bonus", dodge_bonus );
3310     jsout.member( "block_bonus", block_bonus );
3311     jsout.member( "hit_bonus", hit_bonus );
3312     jsout.member( "bash_bonus", bash_bonus );
3313     jsout.member( "cut_bonus", cut_bonus );
3314 
3315     jsout.member( "bash_mult", bash_mult );
3316     jsout.member( "cut_mult", cut_mult );
3317     jsout.member( "melee_quiet", melee_quiet );
3318 
3319     jsout.member( "throw_resist", throw_resist );
3320 
3321     jsout.member( "last_updated", last_updated );
3322 
3323     jsout.member( "body", body );
3324 
3325     // fake is not stored, it's temporary anyway, only used to fire with a gun.
3326 }
3327 
load(const JsonObject & jsin)3328 void Creature::load( const JsonObject &jsin )
3329 {
3330     jsin.allow_omitted_members();
3331     jsin.read( "moves", moves );
3332     jsin.read( "pain", pain );
3333 
3334     killer = nullptr; // see Creature::load
3335 
3336     // TEMPORARY until 0.F
3337     if( savegame_loading_version < 31 ) {
3338         if( jsin.has_object( "effects" ) ) {
3339             // Because JSON requires string keys we need to convert back to our bp keys
3340             std::unordered_map<std::string, std::unordered_map<std::string, effect>> tmp_map;
3341             jsin.read( "effects", tmp_map );
3342             int key_num = 0;
3343             for( const auto &maps : tmp_map ) {
3344                 const efftype_id id( maps.first );
3345                 if( !id.is_valid() ) {
3346                     debugmsg( "Invalid effect: %s", id.c_str() );
3347                     continue;
3348                 }
3349                 for( const auto &i : maps.second ) {
3350                     if( !( std::istringstream( i.first ) >> key_num ) ) {
3351                         key_num = 0;
3352                     }
3353                     const bodypart_str_id &bp = convert_bp( static_cast<body_part>( key_num ) );
3354                     const effect &e = i.second;
3355 
3356                     ( *effects )[id][bp] = e;
3357                     on_effect_int_change( id, e.get_intensity(), bp );
3358                 }
3359             }
3360         }
3361     } else {
3362         jsin.read( "effects", *effects );
3363     }
3364 
3365     // Remove legacy vitamin effects - they don't do anything, and can't be removed
3366     // Remove this code whenever they actually do anything (0.F or later)
3367     std::set<efftype_id> blacklisted = {
3368         efftype_id( "hypocalcemia" ),
3369         efftype_id( "hypovitA" ),
3370         efftype_id( "hypovitB" ),
3371         efftype_id( "scurvy" )
3372     };
3373     for( const efftype_id &remove : blacklisted ) {
3374         remove_effect( remove );
3375     }
3376 
3377     jsin.read( "values", values );
3378 
3379     jsin.read( "damage_over_time_map", damage_over_time_map );
3380 
3381     jsin.read( "blocks_left", num_blocks );
3382     jsin.read( "dodges_left", num_dodges );
3383     jsin.read( "num_blocks_bonus", num_blocks_bonus );
3384     jsin.read( "num_dodges_bonus", num_dodges_bonus );
3385 
3386     jsin.read( "armor_bash_bonus", armor_bash_bonus );
3387     jsin.read( "armor_cut_bonus", armor_cut_bonus );
3388     jsin.read( "armor_bullet_bonus", armor_bullet_bonus );
3389 
3390     jsin.read( "speed", speed_base );
3391 
3392     jsin.read( "speed_bonus", speed_bonus );
3393     jsin.read( "dodge_bonus", dodge_bonus );
3394     jsin.read( "block_bonus", block_bonus );
3395     jsin.read( "hit_bonus", hit_bonus );
3396     jsin.read( "bash_bonus", bash_bonus );
3397     jsin.read( "cut_bonus", cut_bonus );
3398 
3399     jsin.read( "bash_mult", bash_mult );
3400     jsin.read( "cut_mult", cut_mult );
3401     jsin.read( "melee_quiet", melee_quiet );
3402 
3403     jsin.read( "throw_resist", throw_resist );
3404 
3405     if( !jsin.read( "last_updated", last_updated ) ) {
3406         last_updated = calendar::turn;
3407     }
3408 
3409     jsin.read( "underwater", underwater );
3410 
3411     jsin.read( "body", body );
3412 
3413     fake = false; // see Creature::load
3414 
3415     on_stat_change( "pain", pain );
3416 }
3417 
deserialize(JsonIn & jsin)3418 void player_morale::morale_point::deserialize( JsonIn &jsin )
3419 {
3420     JsonObject jo = jsin.get_object();
3421     jo.allow_omitted_members();
3422     if( !jo.read( "type", type ) ) {
3423         type = morale_type_data::convert_legacy( jo.get_int( "type_enum" ) );
3424     }
3425     itype_id tmpitype;
3426     if( jo.read( "item_type", tmpitype ) && item::type_is_defined( tmpitype ) ) {
3427         item_type = item::find_type( tmpitype );
3428     }
3429     jo.read( "bonus", bonus );
3430     jo.read( "duration", duration );
3431     jo.read( "decay_start", decay_start );
3432     jo.read( "age", age );
3433 }
3434 
serialize(JsonOut & json) const3435 void player_morale::morale_point::serialize( JsonOut &json ) const
3436 {
3437     json.start_object();
3438     json.member( "type", type );
3439     if( item_type != nullptr ) {
3440         // TODO: refactor player_morale to not require this hack
3441         json.member( "item_type", item_type->get_id() );
3442     }
3443     json.member( "bonus", bonus );
3444     json.member( "duration", duration );
3445     json.member( "decay_start", decay_start );
3446     json.member( "age", age );
3447     json.end_object();
3448 }
3449 
store(JsonOut & jsout) const3450 void player_morale::store( JsonOut &jsout ) const
3451 {
3452     jsout.member( "morale", points );
3453 }
3454 
load(const JsonObject & jsin)3455 void player_morale::load( const JsonObject &jsin )
3456 {
3457     jsin.allow_omitted_members();
3458     jsin.read( "morale", points );
3459 }
3460 
store(JsonOut & jsout) const3461 void map_memory::store( JsonOut &jsout ) const
3462 {
3463     jsout.start_array();
3464     jsout.start_array();
3465     for( const auto &elem : tile_cache.list() ) {
3466         jsout.start_array();
3467         jsout.write( elem.first.x );
3468         jsout.write( elem.first.y );
3469         jsout.write( elem.first.z );
3470         jsout.write( elem.second.tile );
3471         jsout.write( elem.second.subtile );
3472         jsout.write( elem.second.rotation );
3473         jsout.end_array();
3474     }
3475     jsout.end_array();
3476 
3477     jsout.start_array();
3478     for( const auto &elem : symbol_cache.list() ) {
3479         jsout.start_array();
3480         jsout.write( elem.first.x );
3481         jsout.write( elem.first.y );
3482         jsout.write( elem.first.z );
3483         jsout.write( elem.second );
3484         jsout.end_array();
3485     }
3486     jsout.end_array();
3487     jsout.end_array();
3488 }
3489 
load(JsonIn & jsin)3490 void map_memory::load( JsonIn &jsin )
3491 {
3492     // Legacy loading of object version.
3493     if( jsin.test_object() ) {
3494         JsonObject jsobj = jsin.get_object();
3495         jsobj.allow_omitted_members();
3496         load( jsobj );
3497     } else {
3498         // This file is large enough that it's more than called for to minimize the
3499         // amount of data written and read and make it a bit less "friendly",
3500         // and use the streaming interface.
3501         jsin.start_array();
3502         tile_cache.clear();
3503         jsin.start_array();
3504         while( !jsin.end_array() ) {
3505             jsin.start_array();
3506             tripoint p;
3507             p.x = jsin.get_int();
3508             p.y = jsin.get_int();
3509             p.z = jsin.get_int();
3510             const std::string tile = jsin.get_string();
3511             const int subtile = jsin.get_int();
3512             const int rotation = jsin.get_int();
3513             memorize_tile( std::numeric_limits<int>::max(), p,
3514                            tile, subtile, rotation );
3515             jsin.end_array();
3516         }
3517         symbol_cache.clear();
3518         jsin.start_array();
3519         while( !jsin.end_array() ) {
3520             jsin.start_array();
3521             tripoint p;
3522             p.x = jsin.get_int();
3523             p.y = jsin.get_int();
3524             p.z = jsin.get_int();
3525             const int symbol = jsin.get_int();
3526             memorize_symbol( std::numeric_limits<int>::max(), p, symbol );
3527             jsin.end_array();
3528         }
3529         jsin.end_array();
3530     }
3531 }
3532 
3533 // Deserializer for legacy object-based memory map.
load(const JsonObject & jsin)3534 void map_memory::load( const JsonObject &jsin )
3535 {
3536     tile_cache.clear();
3537     for( JsonObject pmap : jsin.get_array( "map_memory_tiles" ) ) {
3538         pmap.allow_omitted_members();
3539         const tripoint p( pmap.get_int( "x" ), pmap.get_int( "y" ), pmap.get_int( "z" ) );
3540         memorize_tile( std::numeric_limits<int>::max(), p, pmap.get_string( "tile" ),
3541                        pmap.get_int( "subtile" ), pmap.get_int( "rotation" ) );
3542     }
3543 
3544     symbol_cache.clear();
3545     for( JsonObject pmap : jsin.get_array( "map_memory_curses" ) ) {
3546         pmap.allow_omitted_members();
3547         const tripoint p( pmap.get_int( "x" ), pmap.get_int( "y" ), pmap.get_int( "z" ) );
3548         memorize_symbol( std::numeric_limits<int>::max(), p, pmap.get_int( "symbol" ) );
3549     }
3550 }
3551 
deserialize(JsonIn & jsin)3552 void point::deserialize( JsonIn &jsin )
3553 {
3554     jsin.start_array();
3555     x = jsin.get_int();
3556     y = jsin.get_int();
3557     jsin.end_array();
3558 }
3559 
serialize(JsonOut & jsout) const3560 void point::serialize( JsonOut &jsout ) const
3561 {
3562     jsout.start_array();
3563     jsout.write( x );
3564     jsout.write( y );
3565     jsout.end_array();
3566 }
3567 
deserialize(JsonIn & jsin)3568 void tripoint::deserialize( JsonIn &jsin )
3569 {
3570     jsin.start_array();
3571     x = jsin.get_int();
3572     y = jsin.get_int();
3573     z = jsin.get_int();
3574     jsin.end_array();
3575 }
3576 
serialize(JsonOut & jsout) const3577 void tripoint::serialize( JsonOut &jsout ) const
3578 {
3579     jsout.start_array();
3580     jsout.write( x );
3581     jsout.write( y );
3582     jsout.write( z );
3583     jsout.end_array();
3584 }
3585 
serialize(JsonOut & json) const3586 void addiction::serialize( JsonOut &json ) const
3587 {
3588     json.start_object();
3589     json.member( "type_enum", type );
3590     json.member( "intensity", intensity );
3591     json.member( "sated", sated );
3592     json.end_object();
3593 }
3594 
deserialize(JsonIn & jsin)3595 void addiction::deserialize( JsonIn &jsin )
3596 {
3597     JsonObject jo = jsin.get_object();
3598     jo.allow_omitted_members();
3599     type = static_cast<add_type>( jo.get_int( "type_enum" ) );
3600     intensity = jo.get_int( "intensity" );
3601     jo.read( "sated", sated );
3602 }
3603 
serialize(const recipe_subset & value,JsonOut & jsout)3604 void serialize( const recipe_subset &value, JsonOut &jsout )
3605 {
3606     jsout.start_array();
3607     for( const auto &entry : value ) {
3608         jsout.write( entry->ident() );
3609     }
3610     jsout.end_array();
3611 }
3612 
deserialize(recipe_subset & value,JsonIn & jsin)3613 void deserialize( recipe_subset &value, JsonIn &jsin )
3614 {
3615     value.clear();
3616     jsin.start_array();
3617     while( !jsin.end_array() ) {
3618         value.include( &recipe_id( jsin.get_string() ).obj() );
3619     }
3620 }
3621 
serialize(const item_comp & value,JsonOut & jsout)3622 static void serialize( const item_comp &value, JsonOut &jsout )
3623 {
3624     jsout.start_object();
3625 
3626     jsout.member( "type", value.type );
3627     jsout.member( "count", value.count );
3628     jsout.member( "recoverable", value.recoverable );
3629 
3630     jsout.end_object();
3631 }
3632 
serialize(const tool_comp & value,JsonOut & jsout)3633 static void serialize( const tool_comp &value, JsonOut &jsout )
3634 {
3635     jsout.start_object();
3636 
3637     jsout.member( "type", value.type );
3638     jsout.member( "count", value.count );
3639     jsout.member( "recoverable", value.recoverable );
3640 
3641     jsout.end_object();
3642 }
3643 
deserialize(item_comp & value,JsonIn & jsin)3644 static void deserialize( item_comp &value, JsonIn &jsin )
3645 {
3646     JsonObject jo = jsin.get_object();
3647     jo.allow_omitted_members();
3648     jo.read( "type", value.type );
3649     jo.read( "count", value.count );
3650     jo.read( "recoverable", value.recoverable );
3651 }
3652 
deserialize(tool_comp & value,JsonIn & jsin)3653 static void deserialize( tool_comp &value, JsonIn &jsin )
3654 {
3655     JsonObject jo = jsin.get_object();
3656     jo.allow_omitted_members();
3657     jo.read( "type", value.type );
3658     jo.read( "count", value.count );
3659     jo.read( "recoverable", value.recoverable );
3660 }
3661 
serialize(const quality_requirement & value,JsonOut & jsout)3662 static void serialize( const quality_requirement &value, JsonOut &jsout )
3663 {
3664     jsout.start_object();
3665 
3666     jsout.member( "type", value.type );
3667     jsout.member( "count", value.count );
3668     jsout.member( "level", value.level );
3669 
3670     jsout.end_object();
3671 }
3672 
deserialize(quality_requirement & value,JsonIn & jsin)3673 static void deserialize( quality_requirement &value, JsonIn &jsin )
3674 {
3675     JsonObject jo = jsin.get_object();
3676     jo.allow_omitted_members();
3677     jo.read( "type", value.type );
3678     jo.read( "count", value.count );
3679     jo.read( "level", value.level );
3680 }
3681 
3682 // basecamp
serialize(JsonOut & json) const3683 void basecamp::serialize( JsonOut &json ) const
3684 {
3685     if( omt_pos != tripoint_abs_omt() ) {
3686         json.start_object();
3687         json.member( "name", name );
3688         json.member( "pos", omt_pos );
3689         json.member( "bb_pos", bb_pos );
3690         json.member( "expansions" );
3691         json.start_array();
3692         for( const auto &expansion : expansions ) {
3693             json.start_object();
3694             json.member( "dir", expansion.first );
3695             json.member( "type", expansion.second.type );
3696             json.member( "provides" );
3697             json.start_array();
3698             for( const auto &provide : expansion.second.provides ) {
3699                 json.start_object();
3700                 json.member( "id", provide.first );
3701                 json.member( "amount", provide.second );
3702                 json.end_object();
3703             }
3704             json.end_array();
3705             json.member( "in_progress" );
3706             json.start_array();
3707             for( const auto &working : expansion.second.in_progress ) {
3708                 json.start_object();
3709                 json.member( "id", working.first );
3710                 json.member( "amount", working.second );
3711                 json.end_object();
3712             }
3713             json.end_array();
3714             json.member( "pos", expansion.second.pos );
3715             json.end_object();
3716         }
3717         json.end_array();
3718         json.member( "fortifications" );
3719         json.start_array();
3720         for( const auto &fortification : fortifications ) {
3721             json.start_object();
3722             json.member( "pos", fortification );
3723             json.end_object();
3724         }
3725         json.end_array();
3726         json.end_object();
3727     } else {
3728         return;
3729     }
3730 }
3731 
deserialize(JsonIn & jsin)3732 void basecamp::deserialize( JsonIn &jsin )
3733 {
3734     JsonObject data = jsin.get_object();
3735     data.allow_omitted_members();
3736     data.read( "name", name );
3737     data.read( "pos", omt_pos );
3738     data.read( "bb_pos", bb_pos );
3739     for( JsonObject edata : data.get_array( "expansions" ) ) {
3740         edata.allow_omitted_members();
3741         expansion_data e;
3742         point dir;
3743         if( edata.has_string( "dir" ) ) {
3744             // old save compatibility
3745             const std::string dir_id = edata.get_string( "dir" );
3746             dir = base_camps::direction_from_id( dir_id );
3747         } else {
3748             edata.read( "dir", dir );
3749         }
3750         edata.read( "type", e.type );
3751         if( edata.has_int( "cur_level" ) ) {
3752             edata.read( "cur_level", e.cur_level );
3753         }
3754         if( edata.has_array( "provides" ) ) {
3755             e.cur_level = -1;
3756             for( JsonObject provide_data : edata.get_array( "provides" ) ) {
3757                 provide_data.allow_omitted_members();
3758                 std::string id = provide_data.get_string( "id" );
3759                 int amount = provide_data.get_int( "amount" );
3760                 e.provides[ id ] = amount;
3761             }
3762         }
3763         // incase of save corruption, sanity check provides from expansions
3764         const std::string &initial_provide = base_camps::faction_encode_abs( e, 0 );
3765         if( e.provides.find( initial_provide ) == e.provides.end() ) {
3766             e.provides[ initial_provide ] = 1;
3767         }
3768         for( JsonObject in_progress_data : edata.get_array( "in_progress" ) ) {
3769             in_progress_data.allow_omitted_members();
3770             std::string id = in_progress_data.get_string( "id" );
3771             int amount = in_progress_data.get_int( "amount" );
3772             e.in_progress[ id ] = amount;
3773         }
3774         edata.read( "pos", e.pos );
3775         expansions[ dir ] = e;
3776         if( dir != base_camps::base_dir ) {
3777             directions.push_back( dir );
3778         }
3779     }
3780     for( JsonObject edata : data.get_array( "fortifications" ) ) {
3781         edata.allow_omitted_members();
3782         tripoint_abs_omt restore_pos;
3783         edata.read( "pos", restore_pos );
3784         fortifications.push_back( restore_pos );
3785     }
3786 }
3787 
serialize(JsonOut & jsout) const3788 void kill_tracker::serialize( JsonOut &jsout ) const
3789 {
3790     jsout.start_object();
3791     jsout.member( "kills" );
3792     jsout.start_object();
3793     for( const auto &elem : kills ) {
3794         jsout.member( elem.first.str(), elem.second );
3795     }
3796     jsout.end_object();
3797 
3798     jsout.member( "npc_kills" );
3799     jsout.start_array();
3800     for( const auto &elem : npc_kills ) {
3801         jsout.write( elem );
3802     }
3803     jsout.end_array();
3804     jsout.end_object();
3805 }
3806 
deserialize(JsonIn & jsin)3807 void kill_tracker::deserialize( JsonIn &jsin )
3808 {
3809     JsonObject data = jsin.get_object();
3810     data.allow_omitted_members();
3811     for( const JsonMember member : data.get_object( "kills" ) ) {
3812         kills[mtype_id( member.name() )] = member.get_int();
3813     }
3814 
3815     for( const std::string npc_name : data.get_array( "npc_kills" ) ) {
3816         npc_kills.push_back( npc_name );
3817     }
3818 }
3819 
serialize(JsonOut & jsout) const3820 void cata_variant::serialize( JsonOut &jsout ) const
3821 {
3822     jsout.start_array();
3823     jsout.write_as_string( type_ );
3824     jsout.write( value_ );
3825     jsout.end_array();
3826 }
3827 
deserialize(JsonIn & jsin)3828 void cata_variant::deserialize( JsonIn &jsin )
3829 {
3830     if( jsin.test_int() ) {
3831         *this = cata_variant::make<cata_variant_type::int_>( jsin.get_int() );
3832     } else if( jsin.test_bool() ) {
3833         *this = cata_variant::make<cata_variant_type::bool_>( jsin.get_bool() );
3834     } else {
3835         jsin.start_array();
3836         if( !( jsin.read( type_ ) && jsin.read( value_ ) ) ) {
3837             jsin.error( "Failed to read cata_variant" );
3838         }
3839         jsin.end_array();
3840     }
3841 }
3842 
serialize(JsonOut & jsout) const3843 void event_multiset::serialize( JsonOut &jsout ) const
3844 {
3845     jsout.start_object();
3846     std::vector<summaries_type::value_type> copy( summaries_.begin(), summaries_.end() );
3847     jsout.member( "event_counts", copy );
3848     jsout.end_object();
3849 }
3850 
deserialize(JsonIn & jsin)3851 void event_multiset::deserialize( JsonIn &jsin )
3852 {
3853     JsonObject jo = jsin.get_object();
3854     jo.allow_omitted_members();
3855     JsonArray events = jo.get_array( "event_counts" );
3856     if( !events.empty() && events.get_array( 0 ).has_int( 1 ) ) {
3857         // TEMPORARY until 0.F
3858         // Read legacy format with just ints
3859         std::vector<std::pair<cata::event::data_type, int>> copy;
3860         jo.read( "event_counts", copy );
3861         summaries_.clear();
3862         for( const std::pair<cata::event::data_type, int> &p : copy ) {
3863             event_summary summary{ p.second, calendar::start_of_game, calendar::start_of_game };
3864             summaries_.emplace( p.first, summary );
3865         }
3866     } else {
3867         // Read actual summaries
3868         std::vector<std::pair<cata::event::data_type, event_summary>> copy;
3869         jo.read( "event_counts", copy );
3870         summaries_ = { copy.begin(), copy.end() };
3871     }
3872 }
3873 
serialize(JsonOut & jsout) const3874 void stats_tracker::serialize( JsonOut &jsout ) const
3875 {
3876     jsout.start_object();
3877     jsout.member( "data", data );
3878     jsout.member( "initial_scores", initial_scores );
3879     jsout.end_object();
3880 }
3881 
deserialize(JsonIn & jsin)3882 void stats_tracker::deserialize( JsonIn &jsin )
3883 {
3884     JsonObject jo = jsin.get_object();
3885     jo.allow_omitted_members();
3886     jo.read( "data", data );
3887     for( std::pair<const event_type, event_multiset> &d : data ) {
3888         d.second.set_type( d.first );
3889     }
3890     jo.read( "initial_scores", initial_scores );
3891 }
3892 
store(JsonOut & jsout) const3893 void submap::store( JsonOut &jsout ) const
3894 {
3895     jsout.member( "turn_last_touched", last_touched );
3896     jsout.member( "temperature", temperature );
3897 
3898     // Terrain is saved using a simple RLE scheme.  Legacy saves don't have
3899     // this feature but the algorithm is backward compatible.
3900     jsout.member( "terrain" );
3901     jsout.start_array();
3902     std::string last_id;
3903     int num_same = 1;
3904     for( int j = 0; j < SEEY; j++ ) {
3905         // NOLINTNEXTLINE(modernize-loop-convert)
3906         for( int i = 0; i < SEEX; i++ ) {
3907             const std::string this_id = ter[i][j].obj().id.str();
3908             if( !last_id.empty() ) {
3909                 if( this_id == last_id ) {
3910                     num_same++;
3911                 } else {
3912                     if( num_same == 1 ) {
3913                         // if there's only one element don't write as an array
3914                         jsout.write( last_id );
3915                     } else {
3916                         jsout.start_array();
3917                         jsout.write( last_id );
3918                         jsout.write( num_same );
3919                         jsout.end_array();
3920                         num_same = 1;
3921                     }
3922                     last_id = this_id;
3923                 }
3924             } else {
3925                 last_id = this_id;
3926             }
3927         }
3928     }
3929     // Because of the RLE scheme we have to do one last pass
3930     if( num_same == 1 ) {
3931         jsout.write( last_id );
3932     } else {
3933         jsout.start_array();
3934         jsout.write( last_id );
3935         jsout.write( num_same );
3936         jsout.end_array();
3937     }
3938     jsout.end_array();
3939 
3940     // Write out the radiation array in a simple RLE scheme.
3941     // written in intensity, count pairs
3942     jsout.member( "radiation" );
3943     jsout.start_array();
3944     int lastrad = -1;
3945     int count = 0;
3946     for( int j = 0; j < SEEY; j++ ) {
3947         for( int i = 0; i < SEEX; i++ ) {
3948             const point p( i, j );
3949             // Save radiation, re-examine this because it doesn't look like it works right
3950             int r = get_radiation( p );
3951             if( r == lastrad ) {
3952                 count++;
3953             } else {
3954                 if( count ) {
3955                     jsout.write( count );
3956                 }
3957                 jsout.write( r );
3958                 lastrad = r;
3959                 count = 1;
3960             }
3961         }
3962     }
3963     jsout.write( count );
3964     jsout.end_array();
3965 
3966     jsout.member( "furniture" );
3967     jsout.start_array();
3968     for( int j = 0; j < SEEY; j++ ) {
3969         for( int i = 0; i < SEEX; i++ ) {
3970             const point p( i, j );
3971             // Save furniture
3972             if( get_furn( p ) ) {
3973                 jsout.start_array();
3974                 jsout.write( p.x );
3975                 jsout.write( p.y );
3976                 jsout.write( get_furn( p ).obj().id );
3977                 jsout.end_array();
3978             }
3979         }
3980     }
3981     jsout.end_array();
3982 
3983     jsout.member( "items" );
3984     jsout.start_array();
3985     for( int j = 0; j < SEEY; j++ ) {
3986         for( int i = 0; i < SEEX; i++ ) {
3987             if( itm[i][j].empty() ) {
3988                 continue;
3989             }
3990             jsout.write( i );
3991             jsout.write( j );
3992             jsout.write( itm[i][j] );
3993         }
3994     }
3995     jsout.end_array();
3996 
3997     jsout.member( "traps" );
3998     jsout.start_array();
3999     for( int j = 0; j < SEEY; j++ ) {
4000         for( int i = 0; i < SEEX; i++ ) {
4001             const point p( i, j );
4002             // Save traps
4003             if( get_trap( p ) ) {
4004                 jsout.start_array();
4005                 jsout.write( p.x );
4006                 jsout.write( p.y );
4007                 // TODO: jsout should support writing an id like jsout.write( trap_id )
4008                 jsout.write( get_trap( p ).id().str() );
4009                 jsout.end_array();
4010             }
4011         }
4012     }
4013     jsout.end_array();
4014 
4015     jsout.member( "fields" );
4016     jsout.start_array();
4017     for( int j = 0; j < SEEY; j++ ) {
4018         for( int i = 0; i < SEEX; i++ ) {
4019             // Save fields
4020             if( fld[i][j].field_count() > 0 ) {
4021                 jsout.write( i );
4022                 jsout.write( j );
4023                 jsout.start_array();
4024                 for( const auto &elem : fld[i][j] ) {
4025                     const field_entry &cur = elem.second;
4026                     jsout.write( cur.get_field_type().id() );
4027                     jsout.write( cur.get_field_intensity() );
4028                     jsout.write( cur.get_field_age() );
4029                 }
4030                 jsout.end_array();
4031             }
4032         }
4033     }
4034     jsout.end_array();
4035 
4036     // Write out as array of arrays of single entries
4037     jsout.member( "cosmetics" );
4038     jsout.start_array();
4039     for( const auto &cosm : cosmetics ) {
4040         jsout.start_array();
4041         jsout.write( cosm.pos.x );
4042         jsout.write( cosm.pos.y );
4043         jsout.write( cosm.type );
4044         jsout.write( cosm.str );
4045         jsout.end_array();
4046     }
4047     jsout.end_array();
4048 
4049     // Output the spawn points
4050     jsout.member( "spawns" );
4051     jsout.start_array();
4052     for( const auto &elem : spawns ) {
4053         jsout.start_array();
4054         // TODO: json should know how to write string_ids
4055         jsout.write( elem.type.str() );
4056         jsout.write( elem.count );
4057         jsout.write( elem.pos.x );
4058         jsout.write( elem.pos.y );
4059         jsout.write( elem.faction_id );
4060         jsout.write( elem.mission_id );
4061         jsout.write( elem.friendly );
4062         jsout.write( elem.name );
4063         jsout.end_array();
4064     }
4065     jsout.end_array();
4066 
4067     jsout.member( "vehicles" );
4068     jsout.start_array();
4069     for( const auto &elem : vehicles ) {
4070         // json lib doesn't know how to turn a vehicle * into a vehicle,
4071         // so we have to iterate manually.
4072         jsout.write( *elem );
4073     }
4074     jsout.end_array();
4075 
4076     jsout.member( "partial_constructions" );
4077     jsout.start_array();
4078     for( const auto &elem : partial_constructions ) {
4079         jsout.write( elem.first.x );
4080         jsout.write( elem.first.y );
4081         jsout.write( elem.first.z );
4082         jsout.write( elem.second.counter );
4083         jsout.write( elem.second.id.id() );
4084         jsout.start_array();
4085         for( const auto &it : elem.second.components ) {
4086             jsout.write( it );
4087         }
4088         jsout.end_array();
4089     }
4090     jsout.end_array();
4091 
4092     if( legacy_computer ) {
4093         // it's possible that no access to computers has been made and legacy_computer
4094         // is not cleared
4095         jsout.member( "computers", *legacy_computer );
4096     } else if( !computers.empty() ) {
4097         jsout.member( "computers" );
4098         jsout.start_array();
4099         for( const auto &elem : computers ) {
4100             jsout.write( elem.first );
4101             jsout.write( elem.second );
4102         }
4103         jsout.end_array();
4104     }
4105 
4106     // Output base camp if any
4107     if( camp ) {
4108         jsout.member( "camp", *camp );
4109     }
4110 }
4111 
load(JsonIn & jsin,const std::string & member_name,int version)4112 void submap::load( JsonIn &jsin, const std::string &member_name, int version )
4113 {
4114     bool rubpow_update = version < 22;
4115     if( member_name == "turn_last_touched" ) {
4116         last_touched = time_point( jsin.get_int() );
4117     } else if( member_name == "temperature" ) {
4118         temperature = jsin.get_int();
4119     } else if( member_name == "terrain" ) {
4120         // TODO: try block around this to error out if we come up short?
4121         jsin.start_array();
4122         // Small duplication here so that the update check is only performed once
4123         if( rubpow_update ) {
4124             item rock = item( "rock", calendar::turn_zero );
4125             item chunk = item( "steel_chunk", calendar::turn_zero );
4126             for( int j = 0; j < SEEY; j++ ) {
4127                 for( int i = 0; i < SEEX; i++ ) {
4128                     const ter_str_id tid( jsin.get_string() );
4129 
4130                     if( tid == ter_t_rubble ) {
4131                         ter[i][j] = ter_id( "t_dirt" );
4132                         frn[i][j] = furn_id( "f_rubble" );
4133                         itm[i][j].insert( rock );
4134                         itm[i][j].insert( rock );
4135                     } else if( tid == ter_t_wreckage ) {
4136                         ter[i][j] = ter_id( "t_dirt" );
4137                         frn[i][j] = furn_id( "f_wreckage" );
4138                         itm[i][j].insert( chunk );
4139                         itm[i][j].insert( chunk );
4140                     } else if( tid == ter_t_ash ) {
4141                         ter[i][j] = ter_id( "t_dirt" );
4142                         frn[i][j] = furn_id( "f_ash" );
4143                     } else if( tid == ter_t_pwr_sb_support_l ) {
4144                         ter[i][j] = ter_id( "t_support_l" );
4145                     } else if( tid == ter_t_pwr_sb_switchgear_l ) {
4146                         ter[i][j] = ter_id( "t_switchgear_l" );
4147                     } else if( tid == ter_t_pwr_sb_switchgear_s ) {
4148                         ter[i][j] = ter_id( "t_switchgear_s" );
4149                     } else {
4150                         ter[i][j] = tid.id();
4151                     }
4152                 }
4153             }
4154         } else {
4155             // terrain is encoded using simple RLE
4156             int remaining = 0;
4157             int_id<ter_t> iid;
4158             for( int j = 0; j < SEEY; j++ ) {
4159                 // NOLINTNEXTLINE(modernize-loop-convert)
4160                 for( int i = 0; i < SEEX; i++ ) {
4161                     if( !remaining ) {
4162                         if( jsin.test_string() ) {
4163                             iid = ter_str_id( jsin.get_string() ).id();
4164                         } else if( jsin.test_array() ) {
4165                             jsin.start_array();
4166                             iid = ter_str_id( jsin.get_string() ).id();
4167                             remaining = jsin.get_int() - 1;
4168                             jsin.end_array();
4169                         } else {
4170                             debugmsg( "Mapbuffer terrain data is corrupt, expected string or array." );
4171                         }
4172                     } else {
4173                         --remaining;
4174                     }
4175                     ter[i][j] = iid;
4176                 }
4177             }
4178             if( remaining ) {
4179                 debugmsg( "Mapbuffer terrain data is corrupt, tile data remaining." );
4180             }
4181         }
4182         jsin.end_array();
4183     } else if( member_name == "radiation" ) {
4184         int rad_cell = 0;
4185         jsin.start_array();
4186         while( !jsin.end_array() ) {
4187             int rad_strength = jsin.get_int();
4188             int rad_num = jsin.get_int();
4189             for( int i = 0; i < rad_num; ++i ) {
4190                 if( rad_cell < SEEX * SEEY ) {
4191                     set_radiation( { rad_cell % SEEX, rad_cell / SEEX }, rad_strength );
4192                     rad_cell++;
4193                 }
4194             }
4195         }
4196     } else if( member_name == "furniture" ) {
4197         jsin.start_array();
4198         while( !jsin.end_array() ) {
4199             jsin.start_array();
4200             int i = jsin.get_int();
4201             int j = jsin.get_int();
4202             frn[i][j] = furn_id( jsin.get_string() );
4203             jsin.end_array();
4204         }
4205     } else if( member_name == "items" ) {
4206         jsin.start_array();
4207         while( !jsin.end_array() ) {
4208             int i = jsin.get_int();
4209             int j = jsin.get_int();
4210             const point p( i, j );
4211 
4212             if( !jsin.read( itm[p.x][p.y], false ) ) {
4213                 debugmsg( "Items array is corrupt in submap at: %s, skipping", p.to_string() );
4214             }
4215             // some portion could've been read even if error occurred
4216             for( item &it : itm[p.x][p.y] ) {
4217                 if( it.is_emissive() ) {
4218                     update_lum_add( p, it );
4219                 }
4220                 if( it.needs_processing() ) {
4221                     active_items.add( it, p );
4222                 }
4223                 if( savegame_loading_version < 33 ) {
4224                     // remove after 0.F
4225                     migrate_item_charges( it );
4226                 }
4227             }
4228         }
4229     } else if( member_name == "traps" ) {
4230         jsin.start_array();
4231         while( !jsin.end_array() ) {
4232             jsin.start_array();
4233             int i = jsin.get_int();
4234             int j = jsin.get_int();
4235             const point p( i, j );
4236             // TODO: jsin should support returning an id like jsin.get_id<trap>()
4237             const trap_str_id trid( jsin.get_string() );
4238             trp[p.x][p.y] = trid.id();
4239             jsin.end_array();
4240         }
4241     } else if( member_name == "fields" ) {
4242         jsin.start_array();
4243         while( !jsin.end_array() ) {
4244             // Coordinates loop
4245             int i = jsin.get_int();
4246             int j = jsin.get_int();
4247             jsin.start_array();
4248             while( !jsin.end_array() ) {
4249                 // TODO: Check enum->string migration below
4250                 int type_int = 0;
4251                 std::string type_str;
4252                 if( jsin.test_int() ) {
4253                     type_int = jsin.get_int();
4254                 } else {
4255                     type_str = jsin.get_string();
4256                 }
4257                 int intensity = jsin.get_int();
4258                 int age = jsin.get_int();
4259                 field_type_id ft;
4260                 if( !type_str.empty() ) {
4261                     ft = field_type_id( type_str );
4262                 } else {
4263                     ft = field_types::get_field_type_by_legacy_enum( type_int ).id;
4264                 }
4265                 if( fld[i][j].add_field( ft, intensity, time_duration::from_turns( age ) ) ) {
4266                     field_count++;
4267                 }
4268             }
4269         }
4270     } else if( member_name == "graffiti" ) {
4271         jsin.start_array();
4272         while( !jsin.end_array() ) {
4273             jsin.start_array();
4274             int i = jsin.get_int();
4275             int j = jsin.get_int();
4276             const point p( i, j );
4277             set_graffiti( p, jsin.get_string() );
4278             jsin.end_array();
4279         }
4280     } else if( member_name == "cosmetics" ) {
4281         jsin.start_array();
4282         std::map<std::string, std::string> tcosmetics;
4283 
4284         while( !jsin.end_array() ) {
4285             jsin.start_array();
4286             int i = jsin.get_int();
4287             int j = jsin.get_int();
4288             const point p( i, j );
4289             std::string type, str;
4290             // Try to read as current format
4291             if( jsin.test_string() ) {
4292                 type = jsin.get_string();
4293                 str = jsin.get_string();
4294                 insert_cosmetic( p, type, str );
4295             } else {
4296                 // Otherwise read as most recent old format
4297                 jsin.read( tcosmetics );
4298                 for( auto &cosm : tcosmetics ) {
4299                     insert_cosmetic( p, cosm.first, cosm.second );
4300                 }
4301                 tcosmetics.clear();
4302             }
4303 
4304             jsin.end_array();
4305         }
4306     } else if( member_name == "spawns" ) {
4307         jsin.start_array();
4308         while( !jsin.end_array() ) {
4309             jsin.start_array();
4310             // TODO: json should know how to read an string_id
4311             const mtype_id type = mtype_id( jsin.get_string() );
4312             int count = jsin.get_int();
4313             int i = jsin.get_int();
4314             int j = jsin.get_int();
4315             const point p( i, j );
4316             int faction_id = jsin.get_int();
4317             int mission_id = jsin.get_int();
4318             bool friendly = jsin.get_bool();
4319             std::string name = jsin.get_string();
4320             jsin.end_array();
4321             spawn_point tmp( type, count, p, faction_id, mission_id, friendly, name );
4322             spawns.push_back( tmp );
4323         }
4324     } else if( member_name == "vehicles" ) {
4325         jsin.start_array();
4326         while( !jsin.end_array() ) {
4327             std::unique_ptr<vehicle> tmp = std::make_unique<vehicle>();
4328             jsin.read( *tmp );
4329             vehicles.push_back( std::move( tmp ) );
4330         }
4331     } else if( member_name == "partial_constructions" ) {
4332         jsin.start_array();
4333         while( !jsin.end_array() ) {
4334             partial_con pc;
4335             int i = jsin.get_int();
4336             int j = jsin.get_int();
4337             int k = jsin.get_int();
4338             tripoint pt = tripoint( i, j, k );
4339             pc.counter = jsin.get_int();
4340             if( jsin.test_int() ) {
4341                 // Oops, int id incorrectly saved by legacy code, just load it and hope for the best
4342                 pc.id = construction_id( jsin.get_int() );
4343             } else {
4344                 pc.id = construction_str_id( jsin.get_string() ).id();
4345             }
4346             jsin.start_array();
4347             while( !jsin.end_array() ) {
4348                 item tmp;
4349                 jsin.read( tmp );
4350                 pc.components.push_back( tmp );
4351             }
4352             partial_constructions[pt] = pc;
4353         }
4354     } else if( member_name == "computers" ) {
4355         if( jsin.test_array() ) {
4356             jsin.start_array();
4357             while( !jsin.end_array() ) {
4358                 point loc;
4359                 jsin.read( loc );
4360                 auto new_comp_it = computers.emplace( loc, computer( "BUGGED_COMPUTER", -100 ) ).first;
4361                 jsin.read( new_comp_it->second );
4362             }
4363         } else {
4364             // only load legacy data here, but do not update to std::map, since
4365             // the terrain may not have been loaded yet.
4366             legacy_computer = std::make_unique<computer>( "BUGGED_COMPUTER", -100 );
4367             jsin.read( *legacy_computer );
4368         }
4369     } else if( member_name == "camp" ) {
4370         camp = std::make_unique<basecamp>();
4371         jsin.read( *camp );
4372     } else {
4373         jsin.skip_value();
4374     }
4375 }
4376