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