diff --git a/src/character.cpp b/src/character.cpp index a24dfd6b0bfe8..ba75c8edce508 100644 --- a/src/character.cpp +++ b/src/character.cpp @@ -15,6 +15,7 @@ #include "bionics.h" #include "cata_utility.h" #include "construction.h" +#include "coordinate_conversions.h" #include "debug.h" #include "effect.h" #include "event_bus.h" @@ -32,6 +33,8 @@ #include "messages.h" #include "mission.h" #include "monster.h" +#include "morale.h" +#include "morale_types.h" #include "mtype.h" #include "mutation.h" #include "options.h" @@ -107,6 +110,8 @@ const efftype_id effect_riding( "riding" ); const efftype_id effect_sleep( "sleep" ); const efftype_id effect_slept_through_alarm( "slept_through_alarm" ); const efftype_id effect_tied( "tied" ); +const efftype_id effect_took_prozac( "took_prozac" ); +const efftype_id effect_took_xanax( "took_xanax" ); const efftype_id effect_webbed( "webbed" ); const efftype_id effect_winded( "winded" ); @@ -129,6 +134,7 @@ static const trait_id trait_PROF_FOODP( "PROF_FOODP" ); static const trait_id trait_GILLS( "GILLS" ); static const trait_id trait_GILLS_CEPH( "GILLS_CEPH" ); static const trait_id trait_GLASSJAW( "GLASSJAW" ); +static const trait_id trait_HOARDER( "HOARDER" ); static const trait_id trait_HOLLOW_BONES( "HOLLOW_BONES" ); static const trait_id trait_LIGHT_BONES( "LIGHT_BONES" ); static const trait_id trait_MEMBRANE( "MEMBRANE" ); @@ -136,6 +142,9 @@ static const trait_id trait_MYOPIC( "MYOPIC" ); static const trait_id trait_NIGHTVISION2( "NIGHTVISION2" ); static const trait_id trait_NIGHTVISION3( "NIGHTVISION3" ); static const trait_id trait_NIGHTVISION( "NIGHTVISION" ); +static const trait_id trait_NOMAD( "NOMAD" ); +static const trait_id trait_NOMAD2( "NOMAD2" ); +static const trait_id trait_NOMAD3( "NOMAD3" ); static const trait_id trait_PACKMULE( "PACKMULE" ); static const trait_id trait_PER_SLIME_OK( "PER_SLIME_OK" ); static const trait_id trait_PER_SLIME( "PER_SLIME" ); @@ -3529,6 +3538,21 @@ bool Character::made_of_any( const std::set &ms ) const } ); } +tripoint Character::global_square_location() const +{ + return g->m.getabs( position ); +} + +tripoint Character::global_sm_location() const +{ + return ms_to_sm_copy( global_square_location() ); +} + +tripoint Character::global_omt_location() const +{ + return ms_to_omt_copy( global_square_location() ); +} + bool Character::is_blind() const { return ( worn_with_flag( "BLIND" ) || @@ -5132,6 +5156,159 @@ double Character::footwear_factor() const return ret; } +void Character::update_morale() +{ + morale->decay( 1_minutes ); + apply_persistent_morale(); +} + +void Character::apply_persistent_morale() +{ + // Hoarders get a morale penalty if they're not carrying a full inventory. + if( has_trait( trait_HOARDER ) ) { + int pen = ( volume_capacity() - volume_carried() ) / 125_ml; + if( pen > 70 ) { + pen = 70; + } + if( pen <= 0 ) { + pen = 0; + } + if( has_effect( effect_took_xanax ) ) { + pen = pen / 7; + } else if( has_effect( effect_took_prozac ) ) { + pen = pen / 2; + } + if( pen > 0 ) { + add_morale( MORALE_PERM_HOARDER, -pen, -pen, 1_minutes, 1_minutes, true ); + } + } + // Nomads get a morale penalty if they stay near the same overmap tiles too long. + if( has_trait( trait_NOMAD ) || has_trait( trait_NOMAD2 ) || has_trait( trait_NOMAD3 ) ) { + const tripoint ompos = global_omt_location(); + float total_time = 0; + // Check how long we've stayed in any overmap tile within 5 of us. + const int max_dist = 5; + for( const tripoint &pos : points_in_radius( ompos, max_dist ) ) { + const float dist = rl_dist( ompos, pos ); + if( dist > max_dist ) { + continue; + } + const auto iter = overmap_time.find( pos.xy() ); + if( iter == overmap_time.end() ) { + continue; + } + // Count time in own tile fully, tiles one away as 4/5, tiles two away as 3/5, etc. + total_time += to_moves( iter->second ) * ( max_dist - dist ) / max_dist; + } + // Characters with higher tiers of Nomad suffer worse morale penalties, faster. + int max_unhappiness; + float min_time, max_time; + if( has_trait( trait_NOMAD ) ) { + max_unhappiness = 20; + min_time = to_moves( 12_hours ); + max_time = to_moves( 1_days ); + } else if( has_trait( trait_NOMAD2 ) ) { + max_unhappiness = 40; + min_time = to_moves( 4_hours ); + max_time = to_moves( 8_hours ); + } else { // traid_NOMAD3 + max_unhappiness = 60; + min_time = to_moves( 1_hours ); + max_time = to_moves( 2_hours ); + } + // The penalty starts at 1 at min_time and scales up to max_unhappiness at max_time. + const float t = ( total_time - min_time ) / ( max_time - min_time ); + const int pen = ceil( lerp_clamped( 0, max_unhappiness, t ) ); + if( pen > 0 ) { + add_morale( MORALE_PERM_NOMAD, -pen, -pen, 1_minutes, 1_minutes, true ); + } + } + + if( has_trait( trait_PROF_FOODP ) ) { + // Loosing your face is distressing + if( !( is_wearing( itype_id( "foodperson_mask" ) ) || + is_wearing( itype_id( "foodperson_mask_on" ) ) ) ) { + add_morale( MORALE_PERM_NOFACE, -20, -20, 1_minutes, 1_minutes, true ); + } else if( is_wearing( itype_id( "foodperson_mask" ) ) || + is_wearing( itype_id( "foodperson_mask_on" ) ) ) { + rem_morale( MORALE_PERM_NOFACE ); + } + + if( is_wearing( itype_id( "foodperson_mask_on" ) ) ) { + add_morale( MORALE_PERM_FPMODE_ON, 10, 10, 1_minutes, 1_minutes, true ); + } else { + rem_morale( MORALE_PERM_FPMODE_ON ); + } + } +} + +int Character::get_morale_level() const +{ + return morale->get_level(); +} + +void Character::add_morale( const morale_type &type, int bonus, int max_bonus, + const time_duration &duration, const time_duration &decay_start, + bool capped, const itype *item_type ) +{ + morale->add( type, bonus, max_bonus, duration, decay_start, capped, item_type ); +} + +int Character::has_morale( const morale_type &type ) const +{ + return morale->has( type ); +} + +void Character::rem_morale( const morale_type &type, const itype *item_type ) +{ + morale->remove( type, item_type ); +} + +void Character::clear_morale() +{ + morale->clear(); +} + +bool Character::has_morale_to_read() const +{ + return get_morale_level() >= -40; +} + +void Character::check_and_recover_morale() +{ + player_morale test_morale; + + for( const auto &wit : worn ) { + test_morale.on_item_wear( wit ); + } + + for( const auto &mut : my_mutations ) { + test_morale.on_mutation_gain( mut.first ); + } + + for( auto &elem : *effects ) { + for( auto &_effect_it : elem.second ) { + const effect &e = _effect_it.second; + test_morale.on_effect_int_change( e.get_id(), e.get_intensity(), e.get_bp() ); + } + } + + test_morale.on_stat_change( "hunger", get_hunger() ); + test_morale.on_stat_change( "thirst", get_thirst() ); + test_morale.on_stat_change( "fatigue", get_fatigue() ); + test_morale.on_stat_change( "pain", get_pain() ); + test_morale.on_stat_change( "pkill", get_painkiller() ); + test_morale.on_stat_change( "perceived_pain", get_perceived_pain() ); + + apply_persistent_morale(); + + if( !morale->consistent_with( test_morale ) ) { + *morale = player_morale( test_morale ); // Recover consistency + add_msg( m_debug, "%s morale was recovered.", disp_name( true ) ); + } +} + + void Character::start_hauling() { add_msg( _( "You start hauling items along the ground." ) ); diff --git a/src/character.h b/src/character.h index 1d633e193acc4..883f414f0aba9 100644 --- a/src/character.h +++ b/src/character.h @@ -50,6 +50,7 @@ class JsonOut; class vehicle; struct mutation_branch; class bionic_collection; +class player_morale; struct points_left; class faction; struct construction; @@ -569,6 +570,21 @@ class Character : public Creature, public visitable inline void setpos( const tripoint &p ) override { position = p; } + + /** + * Global position, expressed in map square coordinate system + * (the most detailed coordinate system), used by the @ref map. + */ + virtual tripoint global_square_location() const; + /** + * Returns the location of the player in global submap coordinates. + */ + tripoint global_sm_location() const; + /** + * Returns the location of the player in global overmap terrain coordinates. + */ + tripoint global_omt_location() const; + private: /** Retrieves a stat mod of a mutation. */ int get_mod( const trait_id &mut, const std::string &arg ) const; @@ -1227,6 +1243,29 @@ class Character : public Creature, public visitable double footwear_factor() const; /** Returns true if the player is wearing something on their feet that is not SKINTIGHT */ bool is_wearing_shoes( const side &which_side = side::BOTH ) const; + + /** Ticks down morale counters and removes them */ + void update_morale(); + /** Ensures persistent morale effects are up-to-date */ + void apply_persistent_morale(); + /** Used to apply morale modifications from food and medication **/ + void modify_morale( item &food, int nutr = 0 ); + int get_morale_level() const; // Modified by traits, &c + void add_morale( const morale_type &type, int bonus, int max_bonus = 0, + const time_duration &duration = 1_hours, + const time_duration &decay_start = 30_minutes, bool capped = false, + const itype *item_type = nullptr ); + int has_morale( const morale_type &type ) const; + void rem_morale( const morale_type &type, const itype *item_type = nullptr ); + void clear_morale(); + bool has_morale_to_read() const; + bool has_morale_to_craft() const; + /** Checks permanent morale for consistency and recovers it when an inconsistency is found. */ + void check_and_recover_morale(); + + /** Handles the enjoyability value for a comestible. First value is enjoyability, second is cap. **/ + std::pair fun_for( const item &comest ) const; + protected: Character(); Character( Character && ); @@ -1315,6 +1354,8 @@ class Character : public Creature, public visitable /** Current deficiency/excess quantity for each vitamin */ std::map vitamin_levels; + pimpl morale; + private: // a cache of all active enchantment values. // is recalculated every turn in Character::recalculate_enchantment_cache @@ -1339,6 +1380,10 @@ class Character : public Creature, public visitable int stim; int pkill; + + protected: + /** Amount of time the player has spent in each overmap tile. */ + std::unordered_map overmap_time; }; template<> diff --git a/src/consumption.cpp b/src/consumption.cpp index edab1a06ed2a9..bec14267c885b 100644 --- a/src/consumption.cpp +++ b/src/consumption.cpp @@ -162,7 +162,7 @@ int player::nutrition_for( const item &comest ) const return kcal_for( comest ) / islot_comestible::kcal_per_nutr; } -std::pair player::fun_for( const item &comest ) const +std::pair Character::fun_for( const item &comest ) const { static const trait_id trait_GOURMAND( "GOURMAND" ); static const trait_id trait_SAPROPHAGE( "SAPROPHAGE" ); @@ -1007,7 +1007,7 @@ void player::modify_addiction( const islot_comestible &comest ) } } -void player::modify_morale( item &food, const int nutr ) +void Character::modify_morale( item &food, const int nutr ) { time_duration morale_time = 2_hours; if( food.has_flag( "HOT" ) && food.has_flag( "EATEN_HOT" ) ) { diff --git a/src/crafting.cpp b/src/crafting.cpp index 6fadcb1698843..375f17cdf3342 100644 --- a/src/crafting.cpp +++ b/src/crafting.cpp @@ -278,7 +278,7 @@ float player::crafting_speed_multiplier( const item &craft, const tripoint &loc return total_multi; } -bool player::has_morale_to_craft() const +bool Character::has_morale_to_craft() const { return get_morale_level() >= -50; } diff --git a/src/player.cpp b/src/player.cpp index 865b1295fb88f..d14198f95c1e1 100644 --- a/src/player.cpp +++ b/src/player.cpp @@ -183,9 +183,7 @@ const efftype_id effect_stim( "stim" ); const efftype_id effect_stim_overdose( "stim_overdose" ); const efftype_id effect_stunned( "stunned" ); const efftype_id effect_tapeworm( "tapeworm" ); -const efftype_id effect_took_prozac( "took_prozac" ); const efftype_id effect_took_thorazine( "took_thorazine" ); -const efftype_id effect_took_xanax( "took_xanax" ); const efftype_id effect_valium( "valium" ); const efftype_id effect_visuals( "visuals" ); const efftype_id effect_weed_high( "weed_high" ); @@ -308,7 +306,6 @@ static const trait_id trait_GILLS_CEPH( "GILLS_CEPH" ); static const trait_id trait_HATES_BOOKS( "HATES_BOOKS" ); static const trait_id trait_HEAVYSLEEPER( "HEAVYSLEEPER" ); static const trait_id trait_HEAVYSLEEPER2( "HEAVYSLEEPER2" ); -static const trait_id trait_HOARDER( "HOARDER" ); static const trait_id trait_HOLLOW_BONES( "HOLLOW_BONES" ); static const trait_id trait_HOOVES( "HOOVES" ); static const trait_id trait_HORNS_POINTED( "HORNS_POINTED" ); @@ -713,93 +710,6 @@ void player::action_taken() nv_cached = false; } -void player::update_morale() -{ - morale->decay( 1_minutes ); - apply_persistent_morale(); -} - -void player::apply_persistent_morale() -{ - // Hoarders get a morale penalty if they're not carrying a full inventory. - if( has_trait( trait_HOARDER ) ) { - int pen = ( volume_capacity() - volume_carried() ) / 125_ml; - if( pen > 70 ) { - pen = 70; - } - if( pen <= 0 ) { - pen = 0; - } - if( has_effect( effect_took_xanax ) ) { - pen = pen / 7; - } else if( has_effect( effect_took_prozac ) ) { - pen = pen / 2; - } - if( pen > 0 ) { - add_morale( MORALE_PERM_HOARDER, -pen, -pen, 1_minutes, 1_minutes, true ); - } - } - // Nomads get a morale penalty if they stay near the same overmap tiles too long. - if( has_trait( trait_NOMAD ) || has_trait( trait_NOMAD2 ) || has_trait( trait_NOMAD3 ) ) { - const tripoint ompos = global_omt_location(); - float total_time = 0; - // Check how long we've stayed in any overmap tile within 5 of us. - const int max_dist = 5; - for( const tripoint &pos : points_in_radius( ompos, max_dist ) ) { - const float dist = rl_dist( ompos, pos ); - if( dist > max_dist ) { - continue; - } - const auto iter = overmap_time.find( pos.xy() ); - if( iter == overmap_time.end() ) { - continue; - } - // Count time in own tile fully, tiles one away as 4/5, tiles two away as 3/5, etc. - total_time += to_moves( iter->second ) * ( max_dist - dist ) / max_dist; - } - // Characters with higher tiers of Nomad suffer worse morale penalties, faster. - int max_unhappiness; - float min_time, max_time; - if( has_trait( trait_NOMAD ) ) { - max_unhappiness = 20; - min_time = to_moves( 12_hours ); - max_time = to_moves( 1_days ); - } else if( has_trait( trait_NOMAD2 ) ) { - max_unhappiness = 40; - min_time = to_moves( 4_hours ); - max_time = to_moves( 8_hours ); - } else { // traid_NOMAD3 - max_unhappiness = 60; - min_time = to_moves( 1_hours ); - max_time = to_moves( 2_hours ); - } - // The penalty starts at 1 at min_time and scales up to max_unhappiness at max_time. - const float t = ( total_time - min_time ) / ( max_time - min_time ); - const int pen = ceil( lerp_clamped( 0, max_unhappiness, t ) ); - if( pen > 0 ) { - add_morale( MORALE_PERM_NOMAD, -pen, -pen, 1_minutes, 1_minutes, true ); - } - } - - if( has_trait( trait_PROF_FOODP ) ) { - // Loosing your face is distressing - if( !( is_wearing( itype_id( "foodperson_mask" ) ) || - is_wearing( itype_id( "foodperson_mask_on" ) ) ) ) { - add_morale( MORALE_PERM_NOFACE, -20, -20, 1_minutes, 1_minutes, true ); - } else if( is_wearing( itype_id( "foodperson_mask" ) ) || - is_wearing( itype_id( "foodperson_mask_on" ) ) ) { - rem_morale( MORALE_PERM_NOFACE ); - } - - if( is_wearing( itype_id( "foodperson_mask_on" ) ) ) { - add_morale( MORALE_PERM_FPMODE_ON, 10, 10, 1_minutes, 1_minutes, true ); - } else { - rem_morale( MORALE_PERM_FPMODE_ON ); - } - } - -} - /* Here lies the intended effects of body temperature Assumption 1 : a naked person is comfortable at 19C/66.2F (31C/87.8F at rest). @@ -2150,21 +2060,6 @@ float player::active_light() const return lumination; } -tripoint player::global_square_location() const -{ - return g->m.getabs( position ); -} - -tripoint player::global_sm_location() const -{ - return ms_to_sm_copy( global_square_location() ); -} - -tripoint player::global_omt_location() const -{ - return ms_to_omt_copy( global_square_location() ); -} - const tripoint &player::pos() const { return position; @@ -6104,72 +5999,6 @@ void player::update_body_wetness( const w_point &weather ) // TODO: Make clothing slow down drying } -int player::get_morale_level() const -{ - return morale->get_level(); -} - -void player::add_morale( const morale_type &type, int bonus, int max_bonus, - const time_duration &duration, const time_duration &decay_start, - bool capped, const itype *item_type ) -{ - morale->add( type, bonus, max_bonus, duration, decay_start, capped, item_type ); -} - -int player::has_morale( const morale_type &type ) const -{ - return morale->has( type ); -} - -void player::rem_morale( const morale_type &type, const itype *item_type ) -{ - morale->remove( type, item_type ); -} - -void player::clear_morale() -{ - morale->clear(); -} - -bool player::has_morale_to_read() const -{ - return get_morale_level() >= -40; -} - -void player::check_and_recover_morale() -{ - player_morale test_morale; - - for( const auto &wit : worn ) { - test_morale.on_item_wear( wit ); - } - - for( const auto &mut : my_mutations ) { - test_morale.on_mutation_gain( mut.first ); - } - - for( auto &elem : *effects ) { - for( auto &_effect_it : elem.second ) { - const effect &e = _effect_it.second; - test_morale.on_effect_int_change( e.get_id(), e.get_intensity(), e.get_bp() ); - } - } - - test_morale.on_stat_change( "hunger", get_hunger() ); - test_morale.on_stat_change( "thirst", get_thirst() ); - test_morale.on_stat_change( "fatigue", get_fatigue() ); - test_morale.on_stat_change( "pain", get_pain() ); - test_morale.on_stat_change( "pkill", get_painkiller() ); - test_morale.on_stat_change( "perceived_pain", get_perceived_pain() ); - - apply_persistent_morale(); - - if( !morale->consistent_with( test_morale ) ) { - *morale = player_morale( test_morale ); // Recover consistency - add_msg( m_debug, "%s morale was recovered.", disp_name( true ) ); - } -} - void player::on_worn_item_transform( const item &old_it, const item &new_it ) { morale->on_worn_item_transform( old_it, new_it ); diff --git a/src/player.h b/src/player.h index f15e4aa32969d..51a86ff50fad1 100644 --- a/src/player.h +++ b/src/player.h @@ -134,8 +134,6 @@ struct special_attack { damage_instance damage; }; -class player_morale; - // The maximum level recoil will ever reach. // This corresponds to the level of accuracy of a "snap" or "hip" shot. extern const double MAX_RECOIL; @@ -227,10 +225,6 @@ class player : public Character void recalc_speed_bonus(); /** Called after every action, invalidates player caches */ void action_taken(); - /** Ticks down morale counters and removes them */ - void update_morale(); - /** Ensures persistent morale effects are up-to-date */ - void apply_persistent_morale(); /** Maintains body temperature */ void update_bodytemp(); /** Define color for displaying the body temperature */ @@ -775,8 +769,6 @@ class player : public Character void modify_stimulation( const islot_comestible &comest ); /** Used to apply addiction modifications from food and medication **/ void modify_addiction( const islot_comestible &comest ); - /** Used to apply morale modifications from food and medication **/ - void modify_morale( item &food, int nutr = 0 ); /** Can the food be [theoretically] eaten no matter the consequences? */ ret_val can_eat( const item &food ) const; @@ -798,8 +790,6 @@ class player : public Character int kcal_for( const item &comest ) const; /** Handles the nutrition value for a comestible **/ int nutrition_for( const item &comest ) const; - /** Handles the enjoyability value for a comestible. First value is enjoyability, second is cap. **/ - std::pair fun_for( const item &comest ) const; /** Handles the enjoyability value for a book. **/ int book_fun_for( const item &book, const player &p ) const; /** @@ -1111,17 +1101,6 @@ class player : public Character /** This handles warning the player that there current activity will not give them xp */ void handle_skill_warning( const skill_id &id, bool force_warning = false ); - int get_morale_level() const; // Modified by traits, &c - void add_morale( const morale_type &type, int bonus, int max_bonus = 0, - const time_duration &duration = 1_hours, - const time_duration &decay_start = 30_minutes, bool capped = false, - const itype *item_type = nullptr ); - int has_morale( const morale_type &type ) const; - void rem_morale( const morale_type &type, const itype *item_type = nullptr ); - void clear_morale(); - bool has_morale_to_read() const; - /** Checks permanent morale for consistency and recovers it when an inconsistency is found. */ - void check_and_recover_morale(); void on_worn_item_transform( const item &old_it, const item &new_it ); /** Get the formatted name of the currently wielded item (if any) @@ -1258,7 +1237,6 @@ class player : public Character int expected_time_to_craft( const recipe &rec, int batch_size = 1, bool in_progress = false ) const; std::vector get_eligible_containers_for_crafting() const; bool check_eligible_containers_for_crafting( const recipe &rec, int batch_size = 1 ) const; - bool has_morale_to_craft() const; bool can_make( const recipe *r, int batch_size = 1 ); // have components? /** * Returns true if the player can start crafting the recipe with the given batch size @@ -1367,20 +1345,6 @@ class player : public Character bool defer_move( const tripoint &next ); void shift_destination( const point &shift ); - /** - * Global position, expressed in map square coordinate system - * (the most detailed coordinate system), used by the @ref map. - */ - virtual tripoint global_square_location() const; - /** - * Returns the location of the player in global submap coordinates. - */ - tripoint global_sm_location() const; - /** - * Returns the location of the player in global overmap terrain coordinates. - */ - tripoint global_omt_location() const; - // ---------------VALUES----------------- tripoint view_offset; @@ -1641,11 +1605,6 @@ class player : public Character struct weighted_int_list melee_miss_reasons; - protected: - // TODO: move this to avatar - pimpl morale; - private: - /** smart pointer to targeting data stored for aiming the player's weapon across turns. */ std::shared_ptr tdata; @@ -1656,10 +1615,6 @@ class player : public Character /** Stamp of skills. @ref learned_recipes are valid only with this set of skills. */ mutable decltype( _skills ) valid_autolearn_skills; - private: - - /** Amount of time the player has spent in each overmap tile. */ - std::unordered_map overmap_time; }; #endif diff --git a/src/savegame_json.cpp b/src/savegame_json.cpp index 4f4ed496ff0f0..077f26190bdcc 100644 --- a/src/savegame_json.cpp +++ b/src/savegame_json.cpp @@ -510,6 +510,8 @@ void Character::load( JsonObject &data ) } } + morale->load( data ); + _skills->clear(); JsonObject pmap = data.get_object( "skills" ); for( const std::string &member : pmap.get_member_names() ) { @@ -545,6 +547,16 @@ void Character::load( JsonObject &data ) if( power_level < 0_mJ ) { power_level = 0_mJ; } + + JsonArray overmap_time_array = data.get_array( "overmap_time" ); + overmap_time.clear(); + while( overmap_time_array.has_more() ) { + point pt; + overmap_time_array.read_next( pt ); + time_duration tdr = 0_turns; + overmap_time_array.read_next( tdr ); + overmap_time[pt] = tdr; + } } /** @@ -612,6 +624,9 @@ void Character::store( JsonOut &json ) const if( is_mounted() ) { json.member( "mounted_creature", g->critter_tracker->temporary_id( *mounted_creature ) ); } + + morale->store( json ); + // skills json.member( "skills" ); json.start_object(); @@ -629,6 +644,16 @@ void Character::store( JsonOut &json ) const json.member( "power_level", units::to_kilojoule( power_level ) ); } json.member( "max_power_level", units::to_kilojoule( max_power_level ) ); + + if( !overmap_time.empty() ) { + json.member( "overmap_time" ); + json.start_array(); + for( const std::pair &pr : overmap_time ) { + json.write( pr.first ); + json.write( pr.second ); + } + json.end_array(); + } } //////////////////////////////////////////////////////////////////////////////////////////////////// @@ -724,16 +749,6 @@ void player::store( JsonOut &json ) const json.end_object(); } json.end_array(); - - if( !overmap_time.empty() ) { - json.member( "overmap_time" ); - json.start_array(); - for( const std::pair &pr : overmap_time ) { - json.write( pr.first ); - json.write( pr.second ); - } - json.end_array(); - } } /** @@ -844,15 +859,6 @@ void player::load( JsonObject &data ) bcdata.read( "pos", bcpt ); camps.insert( bcpt ); } - JsonArray overmap_time_array = data.get_array( "overmap_time" ); - overmap_time.clear(); - while( overmap_time_array.has_more() ) { - point pt; - overmap_time_array.read_next( pt ); - time_duration tdr = 0_turns; - overmap_time_array.read_next( tdr ); - overmap_time[pt] = tdr; - } } //////////////////////////////////////////////////////////////////////////////////////////////////// @@ -916,8 +922,6 @@ void avatar::store( JsonOut &json ) const json.member( "translocators", translocators ); - morale->store( json ); - // mission stuff json.member( "active_mission", active_mission == nullptr ? -1 : active_mission->get_id() ); @@ -1039,8 +1043,6 @@ void avatar::load( JsonObject &data ) data.read( "translocators", translocators ); - morale->load( data ); - std::vector tmpmissions; if( data.read( "active_missions", tmpmissions ) ) { active_missions = mission::to_ptr_vector( tmpmissions );