From 20f22d7952e6bad7720925103d176ce0f0bb793a Mon Sep 17 00:00:00 2001 From: Celtic Minstrel Date: Fri, 2 Sep 2016 18:56:47 -0400 Subject: [PATCH] Properly implement copying for universe and scenario This allows a failed load to not clobber the currently-loaded save or scenario. --- src/classes/creatlist.cpp | 6 ++ src/classes/creatlist.hpp | 1 + src/classes/party.cpp | 138 +++++++++++++++++++++++++++++++++++++ src/classes/party.hpp | 5 ++ src/classes/pc.cpp | 59 ++++++++++++++++ src/classes/pc.hpp | 6 ++ src/classes/regtown.cpp | 78 +++++++++++++++++++++ src/classes/regtown.hpp | 15 ++++ src/classes/scenario.cpp | 128 ++++++++++++++++++++++++++++++---- src/classes/scenario.hpp | 8 ++- src/classes/town.cpp | 82 ++++++++++++++++++++++ src/classes/town.hpp | 7 ++ src/classes/universe.cpp | 81 ++++++++++++++++++++++ src/classes/universe.hpp | 27 ++++++++ src/tools/fileio_party.cpp | 15 ++-- src/tools/vector2d.hpp | 1 + 16 files changed, 635 insertions(+), 22 deletions(-) diff --git a/src/classes/creatlist.cpp b/src/classes/creatlist.cpp index 73d31658..a83b8c18 100644 --- a/src/classes/creatlist.cpp +++ b/src/classes/creatlist.cpp @@ -67,6 +67,12 @@ void cPopulation::assign(size_t n, const cTownperson& other, const cMonster& bas dudes[n].summon_time = 0; } +void cPopulation::swap(cPopulation& other) { + std::swap(dudes, other.dudes); + std::swap(which_town, other.which_town); + std::swap(hostile, other.hostile); +} + void cPopulation::readFrom(std::istream& in, size_t n) { if(n >= dudes.size()) dudes.resize(n + 1); dudes[n].readFrom(in); diff --git a/src/classes/creatlist.hpp b/src/classes/creatlist.hpp index 51f3a2ab..cf44966b 100644 --- a/src/classes/creatlist.hpp +++ b/src/classes/creatlist.hpp @@ -35,6 +35,7 @@ public: cPopulation() : which_town(200) {} // Apparently Visual Studio needs this to work cPopulation& operator=(const cPopulation& other) = default; + void swap(cPopulation& other); }; #endif diff --git a/src/classes/party.cpp b/src/classes/party.cpp index 0238d705..4f9b47fa 100644 --- a/src/classes/party.cpp +++ b/src/classes/party.cpp @@ -53,6 +53,144 @@ cParty::~cParty() { } } +cParty::cParty(const cParty& other) + : iLiving(other) + , next_pc_id(other.next_pc_id) + , age(other.age) + , gold(other.gold) + , food(other.food) + , hostiles_present(other.hostiles_present) + , easy_mode(other.easy_mode) + , less_wm(other.less_wm) + , magic_ptrs(other.magic_ptrs) + , light_level(other.light_level) + , outdoor_corner(other.outdoor_corner) + , i_w_c(other.i_w_c) + , out_loc(other.out_loc) + , town_loc(other.town_loc) + , loc_in_sec(other.loc_in_sec) + , town_num(other.town_num) + , boats(other.boats) + , horses(other.horses) + , creature_save(other.creature_save) + , in_boat(other.in_boat) + , in_horse(other.in_horse) + , out_c(other.out_c) + , magic_store_items(other.magic_store_items) + , store_limited_stock(other.store_limited_stock) + , job_banks(other.job_banks) + , imprisoned_monst(other.imprisoned_monst) + , m_noted(other.m_noted) + , m_seen(other.m_seen) + , journal(other.journal) + , special_notes(other.special_notes) + , talk_save(other.talk_save) + , status(other.status) + , quest_status(other.quest_status) + , quest_start(other.quest_start) + , quest_source(other.quest_source) + , left_at(other.left_at) + , left_in(other.left_in) + , direction(other.direction) + , at_which_save_slot(other.at_which_save_slot) + , alchemy(other.alchemy) + , key_times(other.key_times) + , party_event_timers(other.party_event_timers) + , spec_items(other.spec_items) + , total_m_killed(other.total_m_killed) + , total_dam_done(other.total_dam_done) + , total_xp_gained(other.total_xp_gained) + , total_dam_taken(other.total_dam_taken) + , scen_name(other.scen_name) + , stored_items(other.stored_items) + , summons(other.summons) + , scen_won(other.scen_won) + , scen_played(other.scen_played) + , campaign_flags(other.campaign_flags) + , pointers(other.pointers) +{ + memcpy(stuff_done, other.stuff_done, sizeof(stuff_done)); + memcpy(setup, other.setup, sizeof(setup)); + for(int i = 0; i < 6; i++) { + adven[i] = new cPlayer(*other.adven[i]); + adven[i]->join_party(*this); + } +} + +cParty::cParty(cParty&& other) : cParty() { + swap(other); +} + +cParty& cParty::operator=(cParty other) { + swap(other); + return *this; +} + +void cParty::swap(cParty& other) { + std::swap(next_pc_id, other.next_pc_id); + std::swap(age, other.age); + std::swap(gold, other.gold); + std::swap(food, other.food); + std::swap(hostiles_present, other.hostiles_present); + std::swap(easy_mode, other.easy_mode); + std::swap(less_wm, other.less_wm); + std::swap(magic_ptrs, other.magic_ptrs); + std::swap(light_level, other.light_level); + std::swap(outdoor_corner, other.outdoor_corner); + std::swap(i_w_c, other.i_w_c); + std::swap(out_loc, other.out_loc); + std::swap(town_loc, other.town_loc); + std::swap(loc_in_sec, other.loc_in_sec); + std::swap(town_num, other.town_num); + std::swap(boats, other.boats); + std::swap(horses, other.horses); + std::swap(creature_save, other.creature_save); + std::swap(in_boat, other.in_boat); + std::swap(in_horse, other.in_horse); + std::swap(out_c, other.out_c); + std::swap(magic_store_items, other.magic_store_items); + std::swap(store_limited_stock, other.store_limited_stock); + std::swap(job_banks, other.job_banks); + std::swap(imprisoned_monst, other.imprisoned_monst); + std::swap(m_noted, other.m_noted); + std::swap(m_seen, other.m_seen); + std::swap(journal, other.journal); + std::swap(special_notes, other.special_notes); + std::swap(talk_save, other.talk_save); + std::swap(status, other.status); + std::swap(quest_status, other.quest_status); + std::swap(quest_start, other.quest_start); + std::swap(quest_source, other.quest_source); + std::swap(left_at, other.left_at); + std::swap(left_in, other.left_in); + std::swap(direction, other.direction); + std::swap(at_which_save_slot, other.at_which_save_slot); + std::swap(alchemy, other.alchemy); + std::swap(key_times, other.key_times); + std::swap(party_event_timers, other.party_event_timers); + std::swap(spec_items, other.spec_items); + std::swap(total_m_killed, other.total_m_killed); + std::swap(total_dam_done, other.total_dam_done); + std::swap(total_xp_gained, other.total_xp_gained); + std::swap(total_dam_taken, other.total_dam_taken); + std::swap(scen_name, other.scen_name); + std::swap(adven, other.adven); + std::swap(stored_items, other.stored_items); + std::swap(summons, other.summons); + std::swap(scen_won, other.scen_won); + std::swap(scen_played, other.scen_played); + std::swap(campaign_flags, other.campaign_flags); + std::swap(pointers, other.pointers); + unsigned char temp_sdf[350][50]; + memcpy(temp_sdf, stuff_done, sizeof(stuff_done)); + memcpy(stuff_done, other.stuff_done, sizeof(stuff_done)); + memcpy(other.stuff_done, temp_sdf, sizeof(stuff_done)); + unsigned short temp_setup[4][64][64]; + memcpy(temp_setup, setup, sizeof(setup)); + memcpy(setup, other.setup, sizeof(setup)); + memcpy(other.setup, temp_setup, sizeof(setup)); +} + void cParty::import_legacy(legacy::party_record_type& old, cUniverse& univ){ age = old.age; gold = old.gold; diff --git a/src/classes/party.hpp b/src/classes/party.hpp index 8851e0a8..1bd5b20b 100644 --- a/src/classes/party.hpp +++ b/src/classes/party.hpp @@ -213,6 +213,11 @@ public: typedef std::vector::iterator talkIter; cParty(long party_preset = 'dflt'); ~cParty(); + // Copy-and-swap + void swap(cParty& other); + cParty(const cParty& other); + cParty(cParty&& other); + cParty& operator=(cParty other); }; bool operator==(const cParty::cConvers& one, const cParty::cConvers& two); diff --git a/src/classes/pc.cpp b/src/classes/pc.cpp index e71fcef2..f916f724 100644 --- a/src/classes/pc.cpp +++ b/src/classes/pc.cpp @@ -1099,6 +1099,65 @@ cPlayer::cPlayer(cParty& party,long key,short slot) : cPlayer(party) { } } +cPlayer::cPlayer(const cPlayer& other) + : iLiving(other) + , party(other.party) + , main_status(other.main_status) + , name(other.name) + , skills(other.skills) + , max_health(other.max_health) + , cur_health(other.cur_health) + , max_sp(other.max_sp) + , cur_sp(other.cur_sp) + , experience(other.experience) + , skill_pts(other.skill_pts) + , level(other.level) + , items(other.items) + , equip(other.equip) + , priest_spells(other.priest_spells) + , mage_spells(other.mage_spells) + , which_graphic(other.which_graphic) + , weap_poisoned(*this, other.weap_poisoned.slot) + , traits(other.traits) + , race(other.race) + , unique_id(party->next_pc_id++) + , last_cast(other.last_cast) + , combat_pos(other.combat_pos) + , parry(other.parry) + , last_attacked(nullptr) +{} + +cPlayer::cPlayer(cPlayer&& other) : weap_poisoned(*this, other.weap_poisoned.slot) { + swap(other); +} + +void cPlayer::swap(cPlayer& other) { + std::swap(party, other.party); + std::swap(main_status, other.main_status); + std::swap(name, other.name); + std::swap(skills, other.skills); + std::swap(max_health, other.max_health); + std::swap(cur_health, other.cur_health); + std::swap(max_sp, other.max_sp); + std::swap(cur_sp, other.cur_sp); + std::swap(experience, other.experience); + std::swap(skill_pts, other.skill_pts); + std::swap(level, other.level); + std::swap(items, other.items); + std::swap(equip, other.equip); + std::swap(priest_spells, other.priest_spells); + std::swap(mage_spells, other.mage_spells); + std::swap(which_graphic, other.which_graphic); + std::swap(weap_poisoned.slot, other.weap_poisoned.slot); + std::swap(traits, other.traits); + std::swap(race, other.race); + std::swap(unique_id, other.unique_id); + std::swap(last_cast, other.last_cast); + std::swap(combat_pos, other.combat_pos); + std::swap(parry, other.parry); + std::swap(last_attacked, other.last_attacked); +} + void operator += (eMainStatus& stat, eMainStatus othr){ if(othr == eMainStatus::SPLIT) stat = (eMainStatus) (10 + (int)stat); diff --git a/src/classes/pc.hpp b/src/classes/pc.hpp index 1229b0bd..4c6120d8 100644 --- a/src/classes/pc.hpp +++ b/src/classes/pc.hpp @@ -159,6 +159,12 @@ public: void writeTo(std::ostream& file) const; void readFrom(std::istream& file); virtual ~cPlayer() = default; + // Copy-and-swap + void swap(cPlayer& other); + cPlayer(const cPlayer& other); + cPlayer(cPlayer&& other); + // For now, not assignable because of an issue of how to handle the unique_id + cPlayer& operator=(cPlayer other) = delete; }; void operator += (eMainStatus& stat, eMainStatus othr); diff --git a/src/classes/regtown.cpp b/src/classes/regtown.cpp index 33e42ffa..6b3f8783 100644 --- a/src/classes/regtown.cpp +++ b/src/classes/regtown.cpp @@ -390,6 +390,84 @@ cTinyTown::cTinyTown(cScenario& scenario) : cTown(scenario) { init_start(); } +cBigTown::cBigTown(const cBigTown& other) : cTown(other) { + memcpy(ter, other.ter, sizeof(ter)); + memcpy(light, other.light, sizeof(light)); +} + +cBigTown::cBigTown(cBigTown&& other) : cTown(*other.scenario) { + swap(other); +} + +cMedTown::cMedTown(const cMedTown& other) : cTown(other) { + memcpy(ter, other.ter, sizeof(ter)); + memcpy(light, other.light, sizeof(light)); +} + +cMedTown::cMedTown(cMedTown&& other) : cTown(*other.scenario) { + swap(other); +} + +cTinyTown::cTinyTown(const cTinyTown& other) : cTown(other) { + memcpy(ter, other.ter, sizeof(ter)); + memcpy(light, other.light, sizeof(light)); +} + +cTinyTown::cTinyTown(cTinyTown&& other) : cTown(*other.scenario) { + swap(other); +} + +void cBigTown::swap(cTown& with) { + cTown::swap(with); + cBigTown& other = dynamic_cast(with); + ter_num_t temp_ter[64][64]; + memcpy(temp_ter, ter, sizeof(ter)); + memcpy(ter, other.ter, sizeof(ter)); + memcpy(other.ter, temp_ter, sizeof(ter)); + unsigned char temp_light[8][64]; + memcpy(temp_light, light, sizeof(light)); + memcpy(light, other.light, sizeof(light)); + memcpy(other.light, temp_light, sizeof(light)); +} + +void cMedTown::swap(cTown& with) { + cTown::swap(with); + cMedTown& other = dynamic_cast(with); + ter_num_t temp_ter[48][48]; + memcpy(temp_ter, ter, sizeof(ter)); + memcpy(ter, other.ter, sizeof(ter)); + memcpy(other.ter, temp_ter, sizeof(ter)); + unsigned char temp_light[6][48]; + memcpy(temp_light, light, sizeof(light)); + memcpy(light, other.light, sizeof(light)); + memcpy(other.light, temp_light, sizeof(light)); +} + +void cTinyTown::swap(cTown& with) { + cTown::swap(with); + cTinyTown& other = dynamic_cast(with); + ter_num_t temp_ter[32][32]; + memcpy(temp_ter, ter, sizeof(ter)); + memcpy(ter, other.ter, sizeof(ter)); + memcpy(other.ter, temp_ter, sizeof(ter)); + unsigned char temp_light[4][32]; + memcpy(temp_light, light, sizeof(light)); + memcpy(light, other.light, sizeof(light)); + memcpy(other.light, temp_light, sizeof(light)); +} + +cBigTown* cBigTown::clone() { + return new cBigTown(*this); +} + +cMedTown* cMedTown::clone() { + return new cMedTown(*this); +} + +cTinyTown* cTinyTown::clone() { + return new cTinyTown(*this); +} + size_t cBigTown::max_dim() const { return 64; } diff --git a/src/classes/regtown.hpp b/src/classes/regtown.hpp index 47910e9b..a64367f2 100644 --- a/src/classes/regtown.hpp +++ b/src/classes/regtown.hpp @@ -34,6 +34,11 @@ public: explicit cBigTown(cScenario& scenario); void writeTerrainTo(std::ostream& file); void readTerrainFrom(std::istream& file); + // Copy-and-swap + cBigTown* clone() override; + void swap(cTown& other) override; + cBigTown(const cBigTown& other); + cBigTown(cBigTown&& other); }; class cMedTown : public virtual cTown { // formerly ave_tr_type @@ -48,6 +53,11 @@ public: explicit cMedTown(cScenario& scenario); void writeTerrainTo(std::ostream& file); void readTerrainFrom(std::istream& file); + // Copy-and-swap + cMedTown* clone() override; + void swap(cTown& other) override; + cMedTown(const cMedTown& other); + cMedTown(cMedTown&& other); }; class cTinyTown : public virtual cTown { // formerly tiny_tr_type @@ -62,6 +72,11 @@ public: explicit cTinyTown(cScenario& scenario); void writeTerrainTo(std::ostream& file); void readTerrainFrom(std::istream& file); + // Copy-and-swap + cTinyTown* clone() override; + void swap(cTown& other) override; + cTinyTown(const cTinyTown& other); + cTinyTown(cTinyTown&& other); }; #endif diff --git a/src/classes/scenario.cpp b/src/classes/scenario.cpp index 7adffd44..b9d60af3 100644 --- a/src/classes/scenario.cpp +++ b/src/classes/scenario.cpp @@ -46,18 +46,6 @@ void cScenario::destroy_terrain() { } } -cScenario& cScenario::operator=(cScenario&& other) { - // If self-assignment, do nothing. - if(this == &other) return *this; - // First, free any held pointers. - destroy_terrain(); - // Resize the outdoors to ensure the assigned outdoors fits - outdoors.resize(other.outdoors.width(), other.outdoors.height()); - // Then forward to the default assignment operator. - // Use const_cast to ensure the right overload is selected. - return *this = const_cast(other); -} - cScenario::cScenario() { std::string temp_str; @@ -94,6 +82,122 @@ cScenario::cScenario() { contact_info[1] = "Contact info"; } +cScenario::cScenario(const cScenario& other) + : difficulty(other.difficulty) + , intro_pic(other.intro_pic) + , default_ground(other.default_ground) + , bg_out(other.bg_out) + , bg_fight(other.bg_fight) + , bg_town(other.bg_town) + , bg_dungeon(other.bg_dungeon) + , intro_mess_pic(other.intro_mess_pic) + , where_start(other.where_start) + , out_sec_start(other.out_sec_start) + , out_start(other.out_start) + , which_town_start(other.which_town_start) + , init_spec(other.init_spec) + , town_mods(other.town_mods) + , store_item_rects(other.store_item_rects) + , store_item_towns(other.store_item_towns) + , special_items(other.special_items) + , quests(other.quests) + , shops(other.shops) + , uses_custom_graphics(other.uses_custom_graphics) + , rating(other.rating) + , custom_graphics(other.custom_graphics) + , scen_monsters(other.scen_monsters) + , boats(other.boats) + , horses(other.horses) + , ter_types(other.ter_types) + , scenario_timers(other.scenario_timers) + , scen_specials(other.scen_specials) + , storage_shortcuts(other.storage_shortcuts) + , last_out_edited(other.last_out_edited) + , last_town_edited(other.last_town_edited) + , format(other.format) + , campaign_id(other.campaign_id) + , scen_items(other.scen_items) + , scen_name(other.scen_name) + , who_wrote{other.who_wrote[0], other.who_wrote[1]} + , contact_info{other.contact_info[0], other.contact_info[1]} + , intro_strs(other.intro_strs) + , journal_strs(other.journal_strs) + , spec_strs(other.spec_strs) + , snd_names(other.snd_names) + , adjust_diff(other.adjust_diff) + , is_legacy(other.is_legacy) + , scen_file(other.scen_file) + , towns(other.towns.size()) + , outdoors(other.outdoors.width(), other.outdoors.height()) +{ + // Copy towns and sectors + for(size_t i = 0; i < towns.size(); i++) + towns[i] = other.towns[i]->clone(); + for(size_t i = 0; i < outdoors.width(); i++) + for(size_t j = 0; j < outdoors.height(); j++) + outdoors[i][j] = new cOutdoors(*other.outdoors[i][j]); +} + +cScenario::cScenario(cScenario&& other) { + swap(other); +} + +cScenario& cScenario::operator=(cScenario other) { + swap(other); + return *this; +} + +void cScenario::swap(cScenario& other) { + std::swap(difficulty, other.difficulty); + std::swap(intro_pic, other.intro_pic); + std::swap(default_ground, other.default_ground); + std::swap(bg_out, other.bg_out); + std::swap(bg_fight, other.bg_fight); + std::swap(bg_town, other.bg_town); + std::swap(bg_dungeon, other.bg_dungeon); + std::swap(intro_mess_pic, other.intro_mess_pic); + std::swap(where_start, other.where_start); + std::swap(out_sec_start, other.out_sec_start); + std::swap(out_start, other.out_start); + std::swap(which_town_start, other.which_town_start); + std::swap(init_spec, other.init_spec); + std::swap(town_mods, other.town_mods); + std::swap(store_item_rects, other.store_item_rects); + std::swap(store_item_towns, other.store_item_towns); + std::swap(special_items, other.special_items); + std::swap(quests, other.quests); + std::swap(shops, other.shops); + std::swap(uses_custom_graphics, other.uses_custom_graphics); + std::swap(rating, other.rating); + std::swap(custom_graphics, other.custom_graphics); + std::swap(scen_monsters, other.scen_monsters); + std::swap(boats, other.boats); + std::swap(horses, other.horses); + std::swap(ter_types, other.ter_types); + std::swap(scenario_timers, other.scenario_timers); + std::swap(scen_specials, other.scen_specials); + std::swap(storage_shortcuts, other.storage_shortcuts); + std::swap(last_out_edited, other.last_out_edited); + std::swap(last_town_edited, other.last_town_edited); + std::swap(format, other.format); + std::swap(campaign_id, other.campaign_id); + std::swap(scen_items, other.scen_items); + std::swap(scen_name, other.scen_name); + std::swap(who_wrote[0], other.who_wrote[0]); + std::swap(who_wrote[1], other.who_wrote[1]); + std::swap(contact_info[0], other.contact_info[0]); + std::swap(contact_info[1], other.contact_info[1]); + std::swap(intro_strs, other.intro_strs); + std::swap(journal_strs, other.journal_strs); + std::swap(spec_strs, other.spec_strs); + std::swap(snd_names, other.snd_names); + std::swap(adjust_diff, other.adjust_diff); + std::swap(is_legacy, other.is_legacy); + std::swap(scen_file, other.scen_file); + std::swap(outdoors, other.outdoors); + std::swap(towns, other.towns); +} + cScenario::cItemStorage::cItemStorage() : ter_type(-1), property(0) { for(int i = 0; i < 10; i++) item_num[i] = -1; diff --git a/src/classes/scenario.hpp b/src/classes/scenario.hpp index b45b1cd9..3e575977 100644 --- a/src/classes/scenario.hpp +++ b/src/classes/scenario.hpp @@ -57,7 +57,6 @@ public: cItemStorage(); cItemStorage& operator = (legacy::item_storage_shortcut_type& old); }; - cScenario& operator=(const cScenario& other) = default; void destroy_terrain(); public: unsigned short difficulty,intro_pic,default_ground; @@ -120,10 +119,13 @@ public: cItem pull_item_of_type(unsigned int loot_max,short min_val,short max_val,const std::vector& types,bool allow_junk_treasure=false); void reset_version(); - cScenario& operator=(cScenario&& other); - cScenario(cScenario&) = delete; explicit cScenario(); ~cScenario(); + // Copy-and-swap + void swap(cScenario& other); + cScenario(const cScenario& other); + cScenario(cScenario&& other); + cScenario& operator=(cScenario other); }; std::istream& operator>> (std::istream& in, eContentRating& rating); diff --git a/src/classes/town.cpp b/src/classes/town.cpp index bf9e2d95..1631e192 100644 --- a/src/classes/town.cpp +++ b/src/classes/town.cpp @@ -112,6 +112,88 @@ cTown::cTown(cScenario& scenario) : scenario(&scenario) { } } +cTown::cTown(const cTown& other) + : town_chop_time(other.town_chop_time) + , town_chop_key(other.town_chop_key) + , bg_town(other.bg_town) + , bg_fight(other.bg_fight) + , wandering(other.wandering) + , wandering_locs(other.wandering_locs) + , special_locs(other.special_locs) + , sign_locs(other.sign_locs) + , lighting_type(other.lighting_type) + , start_locs(other.start_locs) + , exits(other.exits) + , in_town_rect(other.in_town_rect) + , preset_items(other.preset_items) + , creatures(other.creatures) + , max_num_monst(other.max_num_monst) + , preset_fields(other.preset_fields) + , spec_on_entry(other.spec_on_entry) + , spec_on_entry_if_dead(other.spec_on_entry_if_dead) + , spec_on_hostile(other.spec_on_hostile) + , timers(other.timers) + , specials(other.specials) + , strong_barriers(other.strong_barriers) + , defy_mapping(other.defy_mapping) + , defy_scrying(other.defy_scrying) + , is_hidden(other.is_hidden) + , has_tavern(other.has_tavern) + , difficulty(other.difficulty) + , town_name(other.town_name) + , room_rect(other.room_rect) + , comment(other.comment) + , spec_strs(other.spec_strs) + , talking(other.talking) + , maps(other.maps) + , item_taken(other.item_taken) + , can_find(other.can_find) + , m_killed(other.m_killed) +{} + +cTown::cTown(cTown&& other) { + swap(other); +} + +void cTown::swap(cTown& other) { + std::swap(town_chop_time, other.town_chop_time); + std::swap(town_chop_key, other.town_chop_key); + std::swap(bg_town, other.bg_town); + std::swap(bg_fight, other.bg_fight); + std::swap(wandering, other.wandering); + std::swap(wandering_locs, other.wandering_locs); + std::swap(special_locs, other.special_locs); + std::swap(sign_locs, other.sign_locs); + std::swap(lighting_type, other.lighting_type); + std::swap(start_locs, other.start_locs); + std::swap(exits, other.exits); + std::swap(in_town_rect, other.in_town_rect); + std::swap(preset_items, other.preset_items); + std::swap(creatures, other.creatures); + std::swap(max_num_monst, other.max_num_monst); + std::swap(preset_fields, other.preset_fields); + std::swap(spec_on_entry, other.spec_on_entry); + std::swap(spec_on_entry_if_dead, other.spec_on_entry_if_dead); + std::swap(spec_on_hostile, other.spec_on_hostile); + std::swap(timers, other.timers); + std::swap(specials, other.specials); + std::swap(strong_barriers, other.strong_barriers); + std::swap(defy_mapping, other.defy_mapping); + std::swap(defy_scrying, other.defy_scrying); + std::swap(is_hidden, other.is_hidden); + std::swap(has_tavern, other.has_tavern); + std::swap(difficulty, other.difficulty); + std::swap(town_name, other.town_name); + std::swap(room_rect, other.room_rect); + std::swap(comment, other.comment); + std::swap(spec_strs, other.spec_strs); + std::swap(talking, other.talking); + std::swap(maps, other.maps); + std::swap(item_taken, other.item_taken); + std::swap(can_find, other.can_find); + std::swap(m_killed, other.m_killed); +} + void cTown::init_start() { short s = this->max_dim(); start_locs[0].x = s / 2; diff --git a/src/classes/town.hpp b/src/classes/town.hpp index 828d13a0..2ae23ce5 100644 --- a/src/classes/town.hpp +++ b/src/classes/town.hpp @@ -115,6 +115,13 @@ public: void reattach(cScenario& to); virtual void writeTerrainTo(std::ostream& file) = 0; virtual void readTerrainFrom(std::istream& file) = 0; + virtual cTown* clone() = 0; + // Copy-and-swap + // However, it's protected so that it can only be used by the clone implementations +protected: + virtual void swap(cTown& other); + cTown(const cTown& other); + cTown(cTown&& other); }; std::ostream& operator<< (std::ostream& out, eLighting light); diff --git a/src/classes/universe.cpp b/src/classes/universe.cpp index 92d0541f..1bc7f181 100644 --- a/src/classes/universe.cpp +++ b/src/classes/universe.cpp @@ -899,6 +899,87 @@ bool cCurOut::is_road(int x, int y) { cUniverse::cUniverse(long party_type) : party(party_type), out(*this), town(*this) {} +cUniverse::cUniverse(const cUniverse& other) + : strbuf(other.strbuf) + , extrabufs(other.extrabufs) + , cur_pc(other.cur_pc) + , scenario(other.scenario) + , party(other.party) + , stored_pcs(other.stored_pcs) + , town(*this) + , out(*this) + , file(other.file) + , debug_mode(other.debug_mode) + , ghost_mode(other.ghost_mode) + , node_step_through(other.node_step_through) +{ + for(auto& p : stored_pcs) { + p.second = new cPlayer(*p.second); + } + town.copy(other.town); + out.copy(other.out); +} + +cUniverse::cUniverse(cUniverse&& other) : town(*this), out(*this) { + swap(other); +} + +cUniverse& cUniverse::operator=(cUniverse other) { + swap(other); + return *this; +} + +void cUniverse::swap(cUniverse& other) { + party.swap(other.party); + town.swap(other.town); + out.swap(other.out); + scenario.swap(other.scenario); + std::swap(stored_pcs, other.stored_pcs); + std::swap(file, other.file); + std::swap(debug_mode, other.debug_mode); + std::swap(ghost_mode, other.ghost_mode); + std::swap(node_step_through, other.node_step_through); + std::swap(cur_pc, other.cur_pc); + std::swap(strbuf, other.strbuf); + std::swap(extrabufs, other.extrabufs); +} + +void cCurOut::copy(const cCurOut& other) { + memcpy(expl, other.expl, sizeof(expl)); + memcpy(out, other.out, sizeof(out)); + memcpy(out_e, other.out_e, sizeof(out_e)); +} + +void cCurOut::swap(cCurOut& other) { + cCurOut temp(univ); + temp.copy(other); + other.copy(*this); + copy(temp); +} + +void cCurTown::copy(const cCurTown& other) { + cur_talk_loaded = other.cur_talk_loaded; + quickfire_present = other.quickfire_present; + belt_present = other.belt_present; + difficulty = other.difficulty; + monst = other.monst; + items = other.items; + memcpy(fields, other.fields, sizeof(fields)); +} + +void cCurTown::swap(cCurTown& other) { + std::swap(cur_talk_loaded, other.cur_talk_loaded); + std::swap(quickfire_present, other.quickfire_present); + std::swap(belt_present, other.belt_present); + std::swap(difficulty, other.difficulty); + monst.swap(other.monst); + std::swap(items, other.items); + unsigned long temp[64][64]; + memcpy(temp, other.fields, sizeof(fields)); + memcpy(other.fields, fields, sizeof(fields)); + memcpy(fields, temp, sizeof(fields)); +} + void cUniverse::check_monst(cMonster& monst) { if(monst.see_spec == -2) return; // Avoid infinite recursion monst.see_spec = -2; diff --git a/src/classes/universe.hpp b/src/classes/universe.hpp index 8331d359..b3729ea8 100644 --- a/src/classes/universe.hpp +++ b/src/classes/universe.hpp @@ -121,6 +121,16 @@ public: void readFrom(std::istream& file); ~cCurTown(); + // It's not directly copyable due to the cUniverse reference, which must always point to the cUniverse that contains it. + // The cUniverse copy constructor is thus responsible for performing the copy. + cCurTown(const cCurTown&) = delete; + cCurTown& operator=(const cCurTown&) = delete; + // Not movable for similar reasons + cCurTown(const cCurTown&& other) = delete; + cCurTown& operator=(const cCurTown&& other) = delete; + // This implements the actual copy/move. + void copy(const cCurTown& other); + void swap(cCurTown& other); }; class cCurOut { @@ -143,12 +153,23 @@ public: void readFrom(std::istream& file); cOutdoors* operator->(); explicit cCurOut(cUniverse& univ); + // It's not directly copyable due to the cUniverse reference, which must always point to the cUniverse that contains it. + // The cUniverse copy constructor is thus responsible for performing the copy. + cCurOut(const cCurOut&) = delete; + cCurOut& operator=(const cCurOut&) = delete; + // Not movable for similar reasons + cCurOut(const cCurOut&& other) = delete; + cCurOut& operator=(const cCurOut&& other) = delete; + // This implements the actual copy/move. + void copy(const cCurOut& other); + void swap(cCurOut& other); }; enum eTargetType {TARG_ANY, TARG_PC, TARG_MONST}; class cUniverse{ template using update_info = std::set; + // All these maps are transient data that doesn't need to be saved std::map> update_items; std::map> update_monsters; std::map> update_pcs; @@ -157,6 +178,7 @@ class cUniverse{ pic_num_t addGraphic(pic_num_t pic, ePicType type); void check_monst(cMonster& monst); void check_item(cItem& item); + // The string buffer currently isn't saved std::string strbuf; std::map extrabufs; cItem get_random_store_item(int loot_type, bool allow_junk_treasure); @@ -193,6 +215,11 @@ public: short difficulty_adjust() const; explicit cUniverse(long party_type = 'dflt'); ~cUniverse(); + // Copy-and-swap + void swap(cUniverse& other); + cUniverse(const cUniverse& other); + cUniverse(cUniverse&& other); + cUniverse& operator=(cUniverse other); static void(* print_result)(std::string); }; diff --git a/src/tools/fileio_party.cpp b/src/tools/fileio_party.cpp index 12875e00..860e9329 100644 --- a/src/tools/fileio_party.cpp +++ b/src/tools/fileio_party.cpp @@ -131,7 +131,7 @@ bool load_party(fs::path file_to_load, cUniverse& univ){ return true; } -bool load_party_v1(fs::path file_to_load, cUniverse& univ, bool town_restore, bool in_scen, bool maps_there, bool must_port){ +bool load_party_v1(fs::path file_to_load, cUniverse& real_univ, bool town_restore, bool in_scen, bool maps_there, bool must_port){ std::ifstream fin(file_to_load.c_str(), std::ios_base::binary); fin.seekg(3*sizeof(short),std::ios_base::beg); // skip the header, which is 6 bytes in the old format @@ -192,7 +192,7 @@ bool load_party_v1(fs::path file_to_load, cUniverse& univ, bool town_restore, bo len = (long) sizeof(legacy::town_item_list); fin.read((char*)&t_i, len); - }else univ.party.town_num = 200; + } // LOAD STORED ITEMS for(int i = 0; i < 3; i++) { @@ -219,8 +219,7 @@ bool load_party_v1(fs::path file_to_load, cUniverse& univ, bool town_restore, bo fin.close(); - univ.~cUniverse(); - new(&univ) cUniverse(' '); + cUniverse univ; if(in_scen){ fs::path path; @@ -248,6 +247,7 @@ bool load_party_v1(fs::path file_to_load, cUniverse& univ, bool town_restore, bo univ.import_legacy(town_maps); univ.import_legacy(o_maps); univ.town.import_legacy(sfx, misc_i); + if(!town_restore) univ.party.town_num = 200; // Check items in crates/barrels for(int i = 0; i < univ.town->max_dim(); i++) { for(int j = 0; j < univ.town->max_dim(); j++) { @@ -261,18 +261,18 @@ bool load_party_v1(fs::path file_to_load, cUniverse& univ, bool town_restore, bo } } + real_univ = std::move(univ); return true; } extern fs::path scenDir; -bool load_party_v2(fs::path file_to_load, cUniverse& univ){ +bool load_party_v2(fs::path file_to_load, cUniverse& real_univ){ igzstream zin(file_to_load.string().c_str()); tarball partyIn; partyIn.readFrom(zin); zin.close(); - univ.~cUniverse(); - new(&univ) cUniverse(' '); + cUniverse univ; { // Load main party data first std::istream& fin = partyIn.getFile("save/party.txt"); @@ -375,6 +375,7 @@ bool load_party_v2(fs::path file_to_load, cUniverse& univ){ } else showWarning("There was an error loading the party custom graphics."); } + real_univ = std::move(univ); return true; } diff --git a/src/tools/vector2d.hpp b/src/tools/vector2d.hpp index fa927186..d9108677 100644 --- a/src/tools/vector2d.hpp +++ b/src/tools/vector2d.hpp @@ -162,6 +162,7 @@ public: return data.empty(); } vector2d() : w(0), h(0) {} + vector2d(size_t w, size_t h) : w(w), h(h) {} }; #endif