diff --git a/src/fileio/fileio_party.cpp b/src/fileio/fileio_party.cpp index e22fa337..403c64e0 100644 --- a/src/fileio/fileio_party.cpp +++ b/src/fileio/fileio_party.cpp @@ -347,17 +347,10 @@ bool load_party_v2(fs::path file_to_load, cUniverse& real_univ){ showError("Loading Blades of Exile save file failed."); return false; } - uint16_t magic; - fin.read((char*)&magic, 2); - fin.read((char*)&univ.party.setup, sizeof(univ.party.setup)); - if(magic == 0x0E0B) { // should be 0x0B0E! - for(auto& i : univ.party.setup) { - for(auto& j : i) { - for(auto& k : j) { - flip_short(reinterpret_cast(&k)); - } - } - } + file.readFrom(fin); + auto& page = file[0]; + for(size_t i = 0; i < univ.party.setup.size(); i++) { + page[std::to_string(i)].extract(univ.party.setup[i]); } } @@ -461,9 +454,13 @@ bool save_party(fs::path dest_file, const cUniverse& univ) { { std::ostream& fout = partyOut.newFile("save/setup.dat"); - static uint16_t magic = 0x0B0E; - fout.write((char*)&magic, 2); - fout.write((char*)&univ.party.setup, sizeof(univ.party.setup)); + auto& page = file.add(); + page.add("OBOE"); + for(size_t i = 0; i < univ.party.setup.size(); i++) { + page[std::to_string(i)].encode(univ.party.setup[i]); + } + file.writeTo(fout); + file.clear(); } if(univ.party.town_num < 200) { diff --git a/src/game/boe.combat.cpp b/src/game/boe.combat.cpp index cffb8842..fdfa7387 100644 --- a/src/game/boe.combat.cpp +++ b/src/game/boe.combat.cpp @@ -274,10 +274,6 @@ void start_outdoor_combat(cOutdoors::cCreature encounter,location where,short nu // Basically, in outdoor combat, we create kind of a 48x48 town for // the combat to take place in - for(short i = 0; i < 48; i++) - for(short j = 0; j < 48; j++) { - univ.town.fields[i][j] = 0; - } univ.town.prep_arena(); univ.town->in_town_rect = town_rect; diff --git a/src/game/boe.monster.cpp b/src/game/boe.monster.cpp index cfc579ed..ecc5b6a5 100644 --- a/src/game/boe.monster.cpp +++ b/src/game/boe.monster.cpp @@ -715,8 +715,6 @@ bool combat_move_monster(short which,location destination) { location find_clear_spot(location from_where,short mode) { location loc,store_loc; short num_tries = 0,r1; - // Here 254 indicates the low byte of the town fields, minus explored spaces (which is lowest bit). - unsigned long blocking_fields = SPECIAL_SPOT | OBJECT_CRATE | OBJECT_BARREL | OBJECT_BLOCK | FIELD_QUICKFIRE | 254; while(num_tries < 75) { num_tries++; @@ -729,7 +727,7 @@ location find_clear_spot(location from_where,short mode) { && can_see_light(from_where,loc,combat_obscurity) == 0 && (!is_combat() || univ.target_there(loc,TARG_PC) == nullptr) && (!(is_town()) || (loc != univ.party.town_loc)) - && (!(univ.town.fields[loc.x][loc.y] & blocking_fields))) { + && (!univ.town.is_summon_safe(loc.x, loc.y))) { if((mode == 0) || ((mode == 1) && (adjacent(from_where,loc)))) return loc; else store_loc = loc; diff --git a/src/game/boe.town.cpp b/src/game/boe.town.cpp index cf34f2fa..79894cee 100644 --- a/src/game/boe.town.cpp +++ b/src/game/boe.town.cpp @@ -75,7 +75,6 @@ void start_town_mode(short which_town, short entry_dir) { short at_which_save_slot,former_town; bool monsters_loaded = false,town_toast = false; location loc; - unsigned short temp; bool play_town_sound = false; if(town_force < 200) @@ -230,15 +229,9 @@ void start_town_mode(short which_town, short entry_dir) { } } - for(short j = 0; j < univ.town->max_dim; j++) - for(short k = 0; k < univ.town->max_dim; k++) { // now load in saved setup, - // except that pushable things restore to orig locs - // TODO: THIS IS A TEMPORARY HACK TO GET i VALUE - int i = std::find_if(univ.party.creature_save.begin(), univ.party.creature_save.end(), [&pop](cPopulation& p) {return &p == &pop;}) - univ.party.creature_save.begin(); - temp = univ.party.setup[i][j][k] << 8; - temp &= ~(OBJECT_CRATE | OBJECT_BARREL | OBJECT_BLOCK); - univ.town.fields[j][k] |= temp; - } + // TODO: THIS IS A TEMPORARY HACK TO GET i VALUE + int i = std::find_if(univ.party.creature_save.begin(), univ.party.creature_save.end(), [&pop](cPopulation& p) {return &p == &pop;}) - univ.party.creature_save.begin(); + univ.town.update_fields(univ.party.setup[i]); } if(!monsters_loaded) { @@ -547,16 +540,13 @@ location end_town_mode(short switching_level,location destination) { // returns pop = univ.town.monst; // TODO: THIS IS A TEMPORARY HACK TO GET i VALUE int i = std::find_if(univ.party.creature_save.begin(), univ.party.creature_save.end(), [&pop](cPopulation& p) {return &p == &pop;}) - univ.party.creature_save.begin(); - for(short j = 0; j < univ.town->max_dim; j++) - for(short k = 0; k < univ.town->max_dim; k++) - univ.party.setup[i][j][k] = (univ.town.fields[j][k] & 0xff00) >> 8; + univ.town.save_setup(univ.party.setup[i]); data_saved = true; + break; } if(!data_saved) { univ.party.creature_save[univ.party.at_which_save_slot] = univ.town.monst; - for(short j = 0; j < univ.town->max_dim; j++) - for(short k = 0; k < univ.town->max_dim; k++) - univ.party.setup[univ.party.at_which_save_slot][j][k] = (univ.town.fields[j][k] & 0xff00) >> 8; + univ.town.save_setup(univ.party.setup[univ.party.at_which_save_slot]); univ.party.at_which_save_slot = (univ.party.at_which_save_slot == 3) ? 0 : univ.party.at_which_save_slot + 1; } @@ -930,7 +920,6 @@ void create_out_combat_terrain(short ter_type,short num_walls,bool is_road) { for(short i = 0; i < 48; i++) for(short j = 0; j < 48; j++) { - univ.town.fields[i][j] = 0; if((j <= 8) || (j >= 35) || (i <= 8) || (i >= 35)) univ.town->terrain(i,j) = 90; else univ.town->terrain(i,j) = ter_base[arena]; diff --git a/src/tools/vector2d.hpp b/src/tools/vector2d.hpp index 74c2af09..79f51302 100644 --- a/src/tools/vector2d.hpp +++ b/src/tools/vector2d.hpp @@ -69,6 +69,12 @@ public: assign_from_container(list); return *this; } + void fill(Type val) { + row_ref& me = *this; + for(int i = 0; i < ref.width(); i++) { + me[i] = val; + } + } // Seems like defining a move assignment operator deletes the copy constructor. Don't want that. row_ref(const row_ref&) = default; }; @@ -117,6 +123,12 @@ public: assign_from_container(list); return *this; } + void fill(Type val) { + col_ref& me = *this; + for(int i = 0; i < ref.width(); i++) { + me[i] = val; + } + } // Seems like defining a move assignment operator deletes the copy constructor. Don't want that. col_ref(const col_ref&) = default; }; @@ -244,6 +256,13 @@ public: bool empty() const { return data.empty(); } + void fill(Type val) { + for(size_t x = 0; x < w; x++) { + for(size_t y = 0; y < h; y++) { + (*this)[x][y] = val; + } + } + } vector2d() : w(0), h(0) {} vector2d(size_t w, size_t h) : vector2d() { resize(w,h); diff --git a/src/universe/party.cpp b/src/universe/party.cpp index a544fcc7..fb14d267 100644 --- a/src/universe/party.cpp +++ b/src/universe/party.cpp @@ -92,6 +92,7 @@ cParty::cParty(const cParty& other) , total_xp_gained(other.total_xp_gained) , total_dam_taken(other.total_dam_taken) , scen_name(other.scen_name) + , setup(other.setup) , stored_items(other.stored_items) , summons(other.summons) , scen_won(other.scen_won) @@ -100,7 +101,6 @@ cParty::cParty(const cParty& other) , 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].reset(new cPlayer(*this, *other.adven[i])); } @@ -162,6 +162,7 @@ void cParty::swap(cParty& other) { std::swap(total_dam_taken, other.total_dam_taken); std::swap(scen_name, other.scen_name); std::swap(adven, other.adven); + std::swap(setup, other.setup); std::swap(stored_items, other.stored_items); std::swap(summons, other.summons); std::swap(scen_won, other.scen_won); @@ -173,9 +174,6 @@ void cParty::swap(cParty& other) { 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)); for(size_t i = 0; i < adven.size(); i++) { std::swap(adven[i], other.adven[i]); } diff --git a/src/universe/party.hpp b/src/universe/party.hpp index dd51cb30..27234463 100644 --- a/src/universe/party.hpp +++ b/src/universe/party.hpp @@ -129,7 +129,7 @@ public: private: std::array,6> adven; public: - unsigned short setup[4][64][64]; // formerly setup_save_type + std::array, 4> setup; // formerly setup_save_type std::array,3> stored_items; // formerly stored_items_list_type std::vector summons; // an array of monsters which can be summoned by the party's items yet don't originate from this scenario diff --git a/src/universe/universe.cpp b/src/universe/universe.cpp index a68d7e11..aad4dcd3 100644 --- a/src/universe/universe.cpp +++ b/src/universe/universe.cpp @@ -108,10 +108,8 @@ cTown& cCurTown::operator * (){ void cCurTown::place_preset_fields() { // Initialize barriers, etc. Note non-sfx gets forgotten if this is a town recently visited. - for(int i = 0; i < 64; i++) - for(int j = 0; j < 64; j++) { - fields[i][j] = 0; - } + fields.resize(record()->max_dim, record()->max_dim); + fields.fill(0); for(size_t i = 0; i < record()->preset_fields.size(); i++) { switch(record()->preset_fields[i].type){ case OBJECT_BLOCK: @@ -176,6 +174,26 @@ void cCurTown::place_preset_fields() { } } +void cCurTown::update_fields(const vector2d& setup) { + for(short i = 0; i < record()->max_dim && i < setup.width(); i++) { + for(short j = 0; j < record()->max_dim && j < setup.height(); j++) { + // except that pushable things restore to orig locs + unsigned short temp = setup[i][j] << 8; + temp &= ~(OBJECT_CRATE | OBJECT_BARREL | OBJECT_BLOCK); + univ.town.fields[i][j] |= temp; + } + } +} + +void cCurTown::save_setup(vector2d& setup) const { + setup.resize(record()->max_dim, record()->max_dim); + for(short i = 0; i < record()->max_dim; i++) { + for(short j = 0; j < record()->max_dim; j++) { + setup[i][j] = fields[i][j] >> 8; + } + } +} + cSpeech& cCurTown::cur_talk() { // Make sure we actually have a valid speech stored return univ.scenario.towns[cur_talk_loaded]->talking; @@ -190,6 +208,8 @@ bool cCurTown::prep_talk(short which) { void cCurTown::prep_arena() { if(arena != nullptr) delete arena; arena = new cTown(univ.scenario, AREA_MEDIUM); + fields.resize(AREA_MEDIUM, AREA_MEDIUM); + fields.fill(0); } cCurTown::~cCurTown() { @@ -201,6 +221,13 @@ cTown*const cCurTown::record() const { return univ.scenario.towns[univ.party.town_num]; } +bool cCurTown::is_summon_safe(short x, short y) const { + if(x > record()->max_dim || y > record()->max_dim) return false; + // Here 254 indicates the low byte of the town fields, minus explored spaces (which is lowest bit). + static const unsigned long blocking_fields = SPECIAL_SPOT | OBJECT_CRATE | OBJECT_BARREL | OBJECT_BLOCK | FIELD_QUICKFIRE | 254; + return fields[x][y] & blocking_fields; +} + bool cCurTown::is_explored(short x, short y) const{ if(x > record()->max_dim || y > record()->max_dim) return false; return fields[x][y] & SPECIAL_EXPLORED; @@ -817,14 +844,7 @@ void cCurTown::writeTo(cTagFile& file) const { } } auto& fields_page = file.add(); - vector2d> fields_tmp; - fields_tmp.resize(64, 64); - for(size_t x = 0; x < 64; x++) { - for(size_t y = 0; y < 64; y++) { - fields_tmp[x][y] = fields[x][y]; - } - } - fields_page["FIELDS"].encode(fields_tmp); + fields_page["FIELDS"].encode(fields); fields_page["TERRAIN"].encode(record()->terrain); // TODO: Do we need to save special_spot? } @@ -837,13 +857,11 @@ void cCurTown::readFrom(const cTagFile& file){ monst.hostile = page.contains("HOSTILE"); page["AT"] >> univ.party.town_loc.x >> univ.party.town_loc.y; } else if(page.getFirstKey() == "FIELDS" || page.getFirstKey() == "TERRAIN") { - vector2d> fields_tmp; - page["FIELDS"].extract(fields_tmp); + page["FIELDS"].extract(fields); page["TERRAIN"].extract(record()->terrain); - fields_tmp.resize(64, 64); - for(size_t x = 0; x < 64; x++) { - for(size_t y = 0; y < 64; y++) { - fields[x][y] = fields_tmp[x][y]; + fields.resize(record()->max_dim, record()->max_dim); + for(size_t x = 0; x < record()->max_dim; x++) { + for(size_t y = 0; y < record()->max_dim; y++) { if(is_quickfire(x, y)) { quickfire_present = true; } @@ -876,9 +894,6 @@ void cCurTown::readFrom(const cTagFile& file){ cCurTown::cCurTown(cUniverse& univ) : univ(univ) { arena = nullptr; univ.party.town_num = 200; - for(int i = 0; i < 64; i++) - for(int j = 0; j < 64; j++) - fields[i][j] = 0L; } cCurOut::cCurOut(cUniverse& univ) : univ(univ) {} @@ -973,7 +988,7 @@ void cCurTown::copy(const cCurTown& other) { difficulty = other.difficulty; monst = other.monst; items = other.items; - memcpy(fields, other.fields, sizeof(fields)); + fields = other.fields; } void cCurTown::swap(cCurTown& other) { @@ -983,10 +998,7 @@ void cCurTown::swap(cCurTown& other) { 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)); + fields.swap(other.fields); } void cUniverse::check_monst(cMonster& monst) { diff --git a/src/universe/universe.hpp b/src/universe/universe.hpp index 64c218d0..aa280948 100644 --- a/src/universe/universe.hpp +++ b/src/universe/universe.hpp @@ -39,6 +39,7 @@ class cCurTown { cUniverse& univ; cTown* arena; cTown*const record() const; + vector2d fields; public: bool quickfire_present = false, belt_present = false; // formerly current_town_type @@ -47,8 +48,6 @@ public: std::vector items; // formerly town_item_list type - unsigned long fields[64][64]; - void import_legacy(legacy::current_town_type& old); void import_legacy(legacy::town_item_list& old); void import_legacy(unsigned char(& old_sfx)[64][64], unsigned char(& old_misc_i)[64][64]); @@ -62,6 +61,9 @@ public: bool prep_talk(short which); // Prepare for loading specified speech, returning true if already loaded void prep_arena(); // Set up for a combat arena void place_preset_fields(); + void update_fields(const vector2d& setup); + void save_setup(vector2d& setup) const; + bool is_summon_safe(short x, short y) const; bool is_explored(short x, short y) const; bool is_force_wall(short x, short y) const;