1
0
mirror of https://github.com/rwengine/openrw.git synced 2024-09-15 15:02:34 +02:00

Expand Save Game loading to garage data

This commit is contained in:
Daniel Evans 2015-06-14 18:08:55 +01:00
parent cd7bb3af15
commit 77ca5d96ea
7 changed files with 299 additions and 10 deletions

View File

@ -5,11 +5,18 @@
#include <engine/RWTypes.hpp>
#include <string>
#include <vector>
struct GameState;
class GameWorld;
class ScriptMachine;
struct SaveGameInfo
{
std::string saveName;
std::string savePath;
};
/**
* Reads and Writes GameStates to disk, restoring the required state information
*/
@ -33,6 +40,13 @@ public:
*/
static bool loadGame(GameState& state, const std::string& file);
static SaveGameInfo getSaveInfo(const std::string& file);
/**
* Returns save game information for all found saves
*/
static std::vector<SaveGameInfo> getAllSaveGameInfo();
/**
* Writes the current game state out into a file suitable for loading later.
*/

View File

@ -114,6 +114,9 @@ public:
void applyWaterFloat(const glm::vec3& relPt);
void setPrimaryColour(uint8_t color);
void setSecondaryColour(uint8_t color);
private:
void registerPart(ModelFrame* mf);

View File

@ -4,6 +4,7 @@
#include <objects/GameObject.hpp>
#include <objects/VehicleObject.hpp>
#include <objects/CharacterObject.hpp>
#include <objects/InstanceObject.hpp>
#include <script/ScriptMachine.hpp>
#include <script/SCMFile.hpp>
#include <ai/PlayerController.hpp>
@ -447,16 +448,119 @@ struct StructPed {
// NOTE commented members are read manually, due to alignment.
struct Block1PlayerPed {
//BlockDword unknown0;
//BlockWord unknown1;
alignas(uint8_t) BlockDword reference; // 0x0A
alignas(uint8_t) StructPed info; // 0x0C
BlockDword unknown0;
BlockWord unknown1;
BlockDword reference;
StructPed info;
BlockDword maxWantedLevel;
BlockDword maxChaosLevel;
uint8_t modelName[24];
uint8_t align[2];
};
template<class T> void read(std::FILE* str, T& out) {
std::fread(&out, sizeof(out), 1, str);
}
struct StructStoredCar {
BlockDword modelId;
glm::vec3 position;
glm::vec3 rotation;
BlockDword immunities;
uint8_t colorFG;
uint8_t colorBG;
uint8_t radio;
uint8_t variantA;
uint8_t variantB;
uint8_t bombType;
uint8_t align0[2];
// TODO Migrate to more available location (GameConstants?)
enum /*VehicleImmunities*/ {
Bulletproof = 1 << 0,
Fireproof = 1 << 1,
Explosionproof = 1 << 2,
CollisionProof = 1 << 3,
UnknownProof = 1 << 4
};
enum /*VehicleBombType*/ {
NoBomb = 0,
TimerBomb = 1,
IgnitionBomb = 2,
RemoteBomb = 3,
TimerBombArmed = 4,
IgnitionBombArmed = 5
};
};
struct StructGarage {
uint8_t type;
uint8_t unknown0;
uint8_t unknown1;
uint8_t unknown2;
uint8_t unknown3;
uint8_t unknown4;
uint8_t unknown5;
uint8_t align0[2];
BlockDword unknown6;
BlockDword unknown7;
uint8_t unknown8;
uint8_t unknown9;
uint8_t unknown10;
uint8_t unknown11;
uint8_t unknown12;
uint8_t unknown13;
uint8_t unknown14;
uint8_t align1;
float x1;
float x2;
float y1;
float y2;
float z1;
float z2;
float doorOpenStart;
float doorOpenAngle;
glm::vec2 unknownCoord1;
glm::vec2 unknownCoord2;
float doorAZ;
float doorBZ;
BlockDword unknown15;
uint8_t unknown16;
uint8_t align2[3];
BlockDword unknown17;
BlockDword unknown18;
BlockDword unknown19;
float unknown20;
float unknown21;
float unknown22;
float unknown23;
float unknown24;
float unknown25;
BlockDword unknown26;
uint8_t unknown27;
uint8_t unknown28;
uint8_t unknown29;
uint8_t unknown30;
uint8_t unknown31;
uint8_t unknown32;
uint8_t align3[2];
};
struct Block2GarageData {
BlockDword garageCount;
BlockDword freeBombs;
BlockDword freeResprays;
BlockDword unknown0;
BlockDword unknown1;
BlockDword unknown2;
BlockDword bfImportExportPortland;
BlockDword bfImportExportShoreside;
BlockDword bfImportExportUnused;
BlockDword GA_21lastTime;
StructStoredCar cars[18];
};
void SaveGame::writeGame(GameState& state, const std::string& file)
{
std::FILE* saveFile = std::fopen(file.c_str(), "w");
@ -589,10 +693,26 @@ bool SaveGame::loadGame(GameState& state, const std::string& file)
BlockDword playerCount = readDword(loadFile);
Block1PlayerPed players[playerCount];
BlockDword unknownPlayerValue = readDword(loadFile);
BlockWord unknownPlayerValue2;
fread(&unknownPlayerValue2, sizeof(BlockWord), 1, loadFile);
fread(players, sizeof(Block1PlayerPed), playerCount, loadFile);
for(int p = 0; p < playerCount; ++p) {
read(loadFile, players[p].unknown0);
read(loadFile, players[p].unknown1);
read(loadFile, players[p].reference);
read(loadFile, players[p].info);
read(loadFile, players[p].maxWantedLevel);
read(loadFile, players[p].maxChaosLevel);
read(loadFile, players[p].modelName);
read(loadFile, players[p].align);
}
// BLOCK 2
BlockDword garageBlockSize = readDword(loadFile);
BlockDword garageDataSize = readDword(loadFile);
Block2GarageData garageData;
fread(&garageData, sizeof(Block2GarageData), 1, loadFile);
StructGarage garages[garageData.garageCount];
fread(garages, sizeof(StructGarage), garageData.garageCount, loadFile);
// Insert Game State.
state.hour = block0Data.gameHour;
@ -627,7 +747,108 @@ bool SaveGame::loadGame(GameState& state, const std::string& file)
state.playerObject = player->getGameObjectID();
state.maxWantedLevel = players[0].maxWantedLevel;
}
// TODO restore garage data
// http://gtaforums.com/topic/758692-gta-iii-save-file-documentation/
for(int g = 0; g < garageData.garageCount; ++g) {
auto& garage = garages[g];
state.garages.push_back({
glm::vec3(garage.x1, garage.y1, garage.z1),
glm::vec3(garage.x2, garage.y2, garage.z2),
garage.type
});
auto& gameGarage = state.garages.back();
auto center = (gameGarage.min + gameGarage.max)/2.f;
// Find the nearest dynamic instance?
float distance = std::numeric_limits<float>::max();
GameObject* nearinst = nullptr;
for(std::pair<GameObjectID, GameObject*> object : state.world->objects) {
if( object.second->type() == GameObject::Instance ) {
auto instance = static_cast<InstanceObject*>(object.second);
if( instance->dynamics ) {
float idist = glm::distance(center, instance->getPosition());
if( idist < distance ) {
distance = idist;
nearinst = instance;
}
}
}
}
// Nearinst is probably the garage door.
}
for(int c = 0; c < 18; ++c) {
if(garageData.cars[c].modelId == 0) continue;
auto& car = garageData.cars[c];
glm::quat rotation(-glm::vec3(car.rotation.z, car.rotation.y, car.rotation.x));
VehicleObject* vehicle = state.world->createVehicle(car.modelId, car.position, rotation);
vehicle->setPrimaryColour(car.colorFG);
vehicle->setSecondaryColour(car.colorBG);
}
std::fclose(loadFile);
return true;
}
#include <iconv.h>
#include <dirent.h>
SaveGameInfo SaveGame::getSaveInfo(const std::string& file)
{
std::FILE* loadFile = std::fopen(file.c_str(), "r");
// BLOCK 0
BlockDword blockSize;
fread(&blockSize, sizeof(BlockDword), 1, loadFile);
Block0Data block0Data;
fread(&block0Data, sizeof(block0Data), 1, loadFile);
std::fclose(loadFile);
size_t bytes = 0;
for(;; bytes++ ) {
if(block0Data.saveName[bytes-1] == 0 && block0Data.saveName[bytes] == 0) break;
}
size_t len = bytes/2;
size_t outSize = 24;
char outBuff[outSize];
char* outCur = outBuff;
auto icv = iconv_open("UTF-8", "UTF-16");
char* saveName = (char*)block0Data.saveName;
iconv(icv, &saveName, &bytes, &outCur, &outSize);
return { std::string(outBuff), file };
}
std::vector< SaveGameInfo > SaveGame::getAllSaveGameInfo()
{
// TODO consider windows
auto homedir = getenv("HOME");
if( homedir == nullptr ) {
std::cerr << "Unable to determine home directory" << std::endl;
return {};
}
const char gameDir[] = "GTA3 User Files";
std::string gamePath(homedir);
gamePath.append("/");
gamePath.append(gameDir);
DIR* dp = opendir(gamePath.c_str());
dirent* ep;
std::string realName;
if ( dp == NULL ) {
return {};
}
std::vector<SaveGameInfo> infos;
while( (ep = readdir(dp)) )
{
if ( ep->d_type == DT_REG ) {
realName = ep->d_name;
if(realName.find(".b") != realName.npos) {
infos.push_back(getSaveInfo(realName));
}
}
}
closedir(dp);
return infos;
}

View File

@ -717,6 +717,16 @@ void VehicleObject::destroyObjectHinge(Part* part)
}
}
void VehicleObject::setPrimaryColour(uint8_t color)
{
colourPrimary = engine->data->vehicleColours[color];
}
void VehicleObject::setSecondaryColour(uint8_t color)
{
colourSecondary = engine->data->vehicleColours[color];
}
void *VehicleRaycaster::castRay(const btVector3 &from, const btVector3 &to, btVehicleRaycaster::btVehicleRaycasterResult &result)
{
ClosestNotMeRayResultCallback rayCallback( _vehicle->physBody, from, to );

View File

@ -194,7 +194,7 @@ void RWGame::loadGame(const std::string& savename)
startScript("data/main.scm");
if(! SaveGame::loadGame(*state, "GTA3sf1.b") )
if(! SaveGame::loadGame(*state, savename) )
{
log.error("Game", "Failed to load game");
}
@ -636,6 +636,22 @@ void RWGame::renderDebugPaths(float time)
}
}
// Draw Garage bounds
for(size_t g = 0; g < state->garages.size(); ++g) {
auto& garage = state->garages[g];
btVector3 minColor(1.f, 0.f, 0.f);
btVector3 maxColor(0.f, 1.f, 0.f);
btVector3 min(garage.min.x,garage.min.y,garage.min.z);
btVector3 max(garage.max.x,garage.max.y,garage.max.z);
debug->drawLine(min, min + btVector3(0.5f, 0.f, 0.f), minColor);
debug->drawLine(min, min + btVector3(0.f, 0.5f, 0.f), minColor);
debug->drawLine(min, min + btVector3(0.f, 0.f, 0.5f), minColor);
debug->drawLine(max, max - btVector3(0.5f, 0.f, 0.f), maxColor);
debug->drawLine(max, max - btVector3(0.f, 0.5f, 0.f), maxColor);
debug->drawLine(max, max - btVector3(0.f, 0.f, 0.5f), maxColor);
}
debug->flush(renderer);
}

View File

@ -3,23 +3,45 @@
#include "ingamestate.hpp"
#include "RWGame.hpp"
#include <engine/SaveGame.hpp>
MenuState::MenuState(RWGame* game)
: State(game)
{
enterMainMenu();
}
void MenuState::enterMainMenu()
{
Menu *m = new Menu(2);
m->offset = glm::vec2(200.f, 200.f);
m->addEntry(Menu::lambda("Start", [=] { StateManager::get().enter(new IngameState(game)); }));
m->addEntry(Menu::lambda("Resume", [=] {
StateManager::get().enter(new IngameState(game, false));
game->loadGame("quicksave");
}));
m->addEntry(Menu::lambda("Start", [=] { StateManager::get().enter(new IngameState(game)); }));
m->addEntry(Menu::lambda("Load Game", [=] { enterLoadMenu(); }));
m->addEntry(Menu::lambda("Test", [=] { StateManager::get().enter(new IngameState(game, true, true)); }));
m->addEntry(Menu::lambda("Options", [] { std::cout << "Options" << std::endl; }));
m->addEntry(Menu::lambda("Exit", [=] { getWindow().close(); }));
this->enterMenu(m);
}
void MenuState::enterLoadMenu()
{
Menu *m = new Menu(2);
m->offset = glm::vec2(200.f, 200.f);
m->addEntry(Menu::lambda("Back", [=] { enterMainMenu(); }));
auto saves = SaveGame::getAllSaveGameInfo();
for(SaveGameInfo& save : saves) {
m->addEntry(Menu::lambda(save.saveName, [=] {
StateManager::get().enter(new IngameState(game, false));
game->loadGame(save.savePath);
}));
}
this->enterMenu(m);
}
void MenuState::enter()
{

View File

@ -13,6 +13,9 @@ public:
virtual void tick(float dt);
virtual void enterMainMenu();
virtual void enterLoadMenu();
virtual void handleEvent(const sf::Event& event);
};