Allow taking items from a scenario even if they summon monsters or have custom graphics. Breaks older saved games.

(Older saved games might still work if not in a scenario.)
- Remove items that call a special node when entering a new scenario
- Remove stone block if a monster is placed on it
- Monsters captured by Capture Soul now persist across scenarios
- Fix barrels/crates not being restored when re-entering the town
- Fix issue when saving monster status effects
- Fix version number stored in saved game file; also, it's now stored in hexadecimal
- Fix issue with saving which would have caused all but the first timer to be ignored when loading the saved game
- Fix timers being written to out-of-bounds memory when loading a saved game
- Fix use of std::skipws where std::ws was intended
- Fix issue with the fields array being shifted right by one tile on loading; also, fields array is now saved as hexadecimal
- Fields and terrain array use the town's dimension instead of dimension hard-coded (in the case of fields) or stored in the file
- Fix PC editor remove from scenario option not working properly
- Reconstruct the universe when loading a saved game to ensure there isn't leakage from the previous universe
- Fix excess padding in output tarballs when the filesize is a multiple of 512
- Add hasFile function to tarball class
This commit is contained in:
2015-01-08 17:46:54 -05:00
parent 095ab3be49
commit 8350a22ecb
24 changed files with 575 additions and 179 deletions

View File

@@ -739,6 +739,9 @@ bool load_party_v1(fs::path file_to_load, cUniverse& univ, bool town_restore, bo
fin.close();
univ.~cUniverse();
new(&univ) cUniverse();
if(in_scen){
fs::path path;
path = progDir/"Blades of Exile Scenarios"/univ.party.scen_name;
@@ -879,9 +882,19 @@ bool load_party_v2(fs::path file_to_load, cUniverse& univ, bool town_restore, bo
for(int k = 0; k < 48; k++)
univ.out_maps[i][j][k] = fin.get();
}
}
} else univ.party.scen_name = "";
// TODO: Also get the party custom graphics sheet
if(partyIn.hasFile("save/export.png")) {
std::istream& fin = partyIn.getFile("save/export.png");
sf::Image party_sheet;
StdInputStream stream(fin);
if(party_sheet.loadFromStream(stream)) {
//party_sheet.flipVertically();
spec_scen_g.party_sheet.reset(new sf::Texture);
spec_scen_g.party_sheet->create(party_sheet.getSize().x, party_sheet.getSize().y);
spec_scen_g.party_sheet->update(party_sheet);
} else giveError("There was an error loading the party custom graphics.");
}
return true;
}
@@ -947,7 +960,16 @@ bool save_party(fs::path dest_file, const cUniverse& univ) {
}
}
// TODO: Add the party graphics sheet
if(spec_scen_g.party_sheet) {
sf::Image party_pics = spec_scen_g.party_sheet->copyToImage();
party_pics.flipVertically();
fs::path tempPath = tempDir/"temp.png";
party_pics.saveToFile(tempPath.string());
std::ostream& pic_out = partyOut.newFile("save/export.png");
std::ifstream fin(tempPath.string().c_str(), std::ios::binary);
pic_out << fin.rdbuf();
fin.close();
}
// Write out the compressed data
fs::path tempPath = tempDir/"savetemp.exg";
@@ -979,7 +1001,7 @@ bool save_party(fs::path dest_file, const cUniverse& univ) {
std::string read_maybe_quoted_string(std::istream& from) {
std::string result;
from >> std::skipws;
from >> std::ws;
if(from.peek() == '"' || from.peek() == '\'') {
char delim = from.get();
getline(from, result, delim);

View File

@@ -9,6 +9,7 @@
#include <string>
#include <vector>
#include <sstream>
#include <SFML/System/InputStream.hpp>
#include <boost/filesystem/path.hpp>
class cScenario;
@@ -30,10 +31,10 @@ std::string maybe_quote_string(std::string which);
template<typename T, int D>
void writeArray(std::ostream& to, const T(* array)[D], int width, int height) {
using int_type = decltype(T() + 1);
for(int i = 0; i < width; i++) {
to << array[i][0];
for(int j = 1; j < height; j++)
to << '\t' << int_type(array[i][j]);
for(int y = 0; y < height; y++) {
to << array[0][y];
for(int x = 1; x < width; x++)
to << '\t' << int_type(array[x][y]);
to << '\n';
}
to << '\f';
@@ -42,15 +43,45 @@ void writeArray(std::ostream& to, const T(* array)[D], int width, int height) {
template<typename T, int D>
void readArray(std::istream& from, T(* array)[D], int width, int height) {
using int_type = decltype(T() + 1);
from >> std::ws;
std::string arrayContents;
getline(from, arrayContents, '\f');
std::istringstream arrayIn(arrayContents);
for(int i = 0; i < width; i++) {
for(int y = 0; y < height; y++) {
std::string line;
getline(arrayIn, line);
std::istringstream lineIn(line);
for(int j = 0; j < height; j++)
lineIn >> array[i][j];
lineIn.flags(from.flags());
int_type temp;
for(int x = 0; x < width; x++)
lineIn >> temp, array[x][y] = temp;
if(!arrayIn) break;
}
}
// SFML doesn't support standard C++ streams, so I need to do this in order to load images from a stream.
class StdInputStream : public sf::InputStream {
std::istream& stream;
public:
StdInputStream(std::istream& stream) : stream(stream) {}
virtual ~StdInputStream() {}
// TODO: All four of these functions should return -1 on failure, but I'm not quite sure which conditions should be counted as failure
virtual sf::Int64 read(void *data, sf::Int64 size) {
stream.read((char*)data, size);
return stream.gcount();
}
virtual sf::Int64 seek(sf::Int64 position) {
stream.seekg(position);
return tell();
}
virtual sf::Int64 tell() {
return stream.tellg();
}
virtual sf::Int64 getSize() {
auto was_at = stream.tellg();
stream.seekg(0, std::ios::end);
auto pos = stream.tellg();
stream.seekg(was_at);
return pos;
}
};

View File

@@ -661,29 +661,94 @@ rectangle calc_rect(short i, short j){
graf_pos cCustomGraphics::find_graphic(pic_num_t which_rect, bool party) {
short sheet = which_rect / 100;
if(is_old) sheet = 0;
if(is_old || party) sheet = 0;
else which_rect %= 100;
rectangle store_rect = {0,0,36,28};
store_rect.offset(28 * (which_rect % 10),36 * (which_rect / 10));
return std::make_pair(party ? this->party : &sheets[sheet],store_rect);
return std::make_pair(party ? party_sheet.get() : &sheets[sheet],store_rect);
}
size_t cCustomGraphics::count() {
if(sheets == NULL) return 0;
else if(is_old) {
rectangle bounds(sheets[0]);
size_t cCustomGraphics::count(bool party) {
if(!party && sheets == NULL) return 0;
else if(party && party_sheet == NULL) return 0;
else if(is_old || party) {
rectangle bounds(party ? *party_sheet : sheets[0]);
if(bounds.width() < 280) return bounds.width() / 28;
return bounds.height() / 36;
return 10 * bounds.height() / 36;
} else {
size_t count = 100 * (numSheets - 1);
rectangle bounds(sheets[numSheets - 1]);
if(bounds.width() < 280) count += bounds.width() / 28;
else count += bounds.height() / 36;
else count += 10 * bounds.height() / 36;
return count;
}
}
void cCustomGraphics::copy_graphic(pic_num_t dest, pic_num_t src, size_t numSlots) {
if(numSlots < 1) return;
if(!party_sheet) {
sf::Image empty;
empty.create(280, 180, sf::Color::Transparent);
party_sheet.reset(new sf::Texture);
party_sheet->create(280, 180);
party_sheet->update(empty);
numSheets = 1;
}
size_t havePics = count();
if(havePics < dest + numSlots) {
int addRows = 1;
while(havePics + 10 * addRows < dest + numSlots)
addRows++;
sf::RenderTexture temp;
temp.create(280, party_sheet->getSize().y + 36 * addRows);
temp.clear(sf::Color::Transparent);
rect_draw_some_item(*party_sheet, rectangle(*party_sheet), temp, rectangle(*party_sheet));
*party_sheet = temp.getTexture();
}
sf::Texture* from_sheet;
sf::Texture* to_sheet;
sf::Texture* last_src = nullptr;
sf::RenderTexture temp;
rectangle from_rect, to_rect;
for(size_t i = 0; i < numSlots; i++) {
graf_pos_ref(from_sheet, from_rect) = find_graphic(src + i);
graf_pos_ref(to_sheet, to_rect) = find_graphic(dest + i, true);
if(to_sheet != last_src) {
if(last_src) *last_src = temp.getTexture();
last_src = to_sheet;
temp.create(to_sheet->getSize().x, to_sheet->getSize().y);
rect_draw_some_item(*to_sheet, rectangle(*to_sheet), temp, rectangle(*to_sheet));
}
rect_draw_some_item(*from_sheet, from_rect, temp, to_rect);
}
*last_src = temp.getTexture();
}
void cCustomGraphics::convert_sheets() {
if(!is_old) return;
int num_graphics = count();
is_old = false;
sf::Image old_graph = sheets[0].copyToImage();
delete[] sheets;
numSheets = num_graphics / 100;
if(num_graphics % 100) numSheets++;
sheets = new sf::Texture[numSheets];
for(size_t i = 0; i < numSheets; i++) {
sf::IntRect subrect;
subrect.top = i * 280;
subrect.width = 280;
subrect.height = 360;
sf::Image sheet;
sheet.create(280, 360);
sheet.copy(old_graph, 0, 0, subrect);
sheets[i].create(280, 360);
sheets[i].update(sheet);
}
}
// TODO: This doesn't belong in this file
std::string get_str(std::string list, short j){
if(j == 0) return list;

View File

@@ -10,6 +10,7 @@
#define GRAPHTOOL_H
#include <string>
#include <memory>
#include <functional>
#include <boost/filesystem/path.hpp>
#include <SFML/Graphics.hpp>
@@ -65,20 +66,22 @@ using hilite_t = std::pair<size_t,size_t>;
struct cCustomGraphics {
size_t numSheets;
sf::Texture* sheets = NULL;
sf::Texture* party = NULL;
std::shared_ptr<sf::Texture> party_sheet;
bool is_old = false;
void clear() {
if(sheets != NULL) delete[] sheets;
sheets = NULL;
}
~cCustomGraphics() {
if(party != NULL) delete party;
clear();
}
explicit operator bool() {
return sheets;
}
void convert_sheets();
void copy_graphic(pic_num_t dest, pic_num_t src, size_t numSlots);
graf_pos find_graphic(pic_num_t pic, bool party = false);
size_t count();
size_t count(bool party = false);
};
struct snippet_t {

View File

@@ -65,7 +65,8 @@ void tarball::writeTo(std::ostream& out) {
entry.header = generateTarHeader(entry.filename, size);
out.write((char*)&entry.header, sizeof(header_posix_ustar));
out << entry.contents.rdbuf();
out.write(padding, padLength);
if(padLength < 512)
out.write(padding, padLength);
}
}
@@ -88,7 +89,8 @@ void tarball::readFrom(std::istream& in) {
// Skip past the padding without using seekg.
// This is because the gzstreams don't support seekg.
// We're done with the data in this buffer, anyway, so just dump the padding here.
in.read(buf, padLength);
if(padLength < 512)
in.read(buf, padLength);
}
}
@@ -111,3 +113,12 @@ std::istream& tarball::getFile(std::string fname) {
empty.seekg(0);
return empty;
}
bool tarball::hasFile(std::string fname) {
for(tarfile& entry : files) {
if(entry.filename == fname) {
return true;
}
}
return false;
}

View File

@@ -52,6 +52,7 @@ public:
std::ostream& newFile(std::string fname);
void newDirectory(std::string dname);
std::istream& getFile(std::string fname);
bool hasFile(std::string fname);
};