mirror of
https://github.com/rwengine/openrw.git
synced 2024-11-09 12:22:34 +01:00
Proof of concept loading save data from save files
This commit is contained in:
parent
5399948e05
commit
cd7bb3af15
@ -10,6 +10,7 @@
|
|||||||
|
|
||||||
class GameWorld;
|
class GameWorld;
|
||||||
class GameObject;
|
class GameObject;
|
||||||
|
class ScriptMachine;
|
||||||
class PlayerController;
|
class PlayerController;
|
||||||
struct CutsceneData;
|
struct CutsceneData;
|
||||||
|
|
||||||
@ -97,6 +98,39 @@ struct BlipData
|
|||||||
{ }
|
{ }
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Data for garages
|
||||||
|
*/
|
||||||
|
struct GarageInfo
|
||||||
|
{
|
||||||
|
enum /*GarageType*/ {
|
||||||
|
GARAGE_MISSION = 1,
|
||||||
|
GARAGE_BOMBSHOP1 = 2,
|
||||||
|
GARAGE_BOMBSHOP2 = 3,
|
||||||
|
GARAGE_BOMBSHOP3 = 4,
|
||||||
|
GARAGE_RESPRAY = 5,
|
||||||
|
GARAGE_INVALID = 6,
|
||||||
|
GARAGE_SPECIFIC_CARS_ONLY = 7, /* See Opcode 0x21B */
|
||||||
|
GARAGE_COLLECTCARS1 = 8, /* See Opcode 0x03D4 */
|
||||||
|
GARAGE_COLLECTCARS2 = 9,
|
||||||
|
GARAGE_COLLECTCARS3 = 10, /* Unused */
|
||||||
|
GARAGE_OPENFOREXIT = 11,
|
||||||
|
GARAGE_INVALID2 = 12,
|
||||||
|
GARAGE_CRUSHER = 13, /* Unused */
|
||||||
|
GARAGE_MISSION_KEEPCAR = 14,
|
||||||
|
GARAGE_FOR_SCRIPT = 15,
|
||||||
|
GARAGE_HIDEOUT_ONE = 16, /* Portland */
|
||||||
|
GARAGE_HIDEOUT_TWO = 17, /* Staunton */
|
||||||
|
GARAGE_HIDEOUT_THREE = 18, /* Shoreside */
|
||||||
|
GARAGE_FOR_SCRIPT2 = 19,
|
||||||
|
GARAGE_OPENS_FOR_SPECIFIC_CAR = 20,
|
||||||
|
GARAGE_OPENS_ONCE = 21
|
||||||
|
};
|
||||||
|
glm::vec3 min;
|
||||||
|
glm::vec3 max;
|
||||||
|
int type;
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gameplay state object that holds persistent state, and references runtime
|
* Gameplay state object that holds persistent state, and references runtime
|
||||||
* world state.
|
* world state.
|
||||||
@ -177,11 +211,18 @@ struct GameState
|
|||||||
|
|
||||||
std::map<int, BlipData> radarBlips;
|
std::map<int, BlipData> radarBlips;
|
||||||
|
|
||||||
|
std::vector<GarageInfo> garages;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* World to use for this state, this isn't saved, just used at runtime
|
* World to use for this state, this isn't saved, just used at runtime
|
||||||
*/
|
*/
|
||||||
GameWorld* world;
|
GameWorld* world;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Script Machine associated with this state if it exists.
|
||||||
|
*/
|
||||||
|
ScriptMachine* script;
|
||||||
|
|
||||||
GameState();
|
GameState();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -17,6 +17,25 @@ class SaveGame
|
|||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Writes the entire game state to a file format that closely approximates
|
||||||
|
* the format used in GTA III
|
||||||
|
*/
|
||||||
|
static void writeGame(GameState& state, const std::string& file);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Loads an entire Game State from a file, using a format similar to the
|
||||||
|
* format used by GTA III.
|
||||||
|
*
|
||||||
|
* It assumes that the state already has a world and script that have been
|
||||||
|
* initalized to the same state as the game being loaded.
|
||||||
|
* @return status, false if failure occured.
|
||||||
|
*/
|
||||||
|
static bool loadGame(GameState& state, const std::string& file);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Writes the current game state out into a file suitable for loading later.
|
||||||
|
*/
|
||||||
static void writeState(GameState& state, const std::string& file);
|
static void writeState(GameState& state, const std::string& file);
|
||||||
|
|
||||||
static bool loadState(GameState& state, const std::string& file);
|
static bool loadState(GameState& state, const std::string& file);
|
||||||
|
@ -17,7 +17,7 @@
|
|||||||
* Changing this will break saves.
|
* Changing this will break saves.
|
||||||
*/
|
*/
|
||||||
#define SCM_VARIABLE_SIZE 4
|
#define SCM_VARIABLE_SIZE 4
|
||||||
#define SCM_STACK_DEPTH 32
|
#define SCM_STACK_DEPTH 4
|
||||||
|
|
||||||
class GameState;
|
class GameState;
|
||||||
|
|
||||||
|
@ -26,7 +26,8 @@ minute(0),
|
|||||||
cameraNear(0.1f),
|
cameraNear(0.1f),
|
||||||
cameraFixed(false),
|
cameraFixed(false),
|
||||||
cameraTarget(0),
|
cameraTarget(0),
|
||||||
world(nullptr)
|
world(nullptr),
|
||||||
|
script(nullptr)
|
||||||
{
|
{
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -6,6 +6,7 @@
|
|||||||
#include <objects/CharacterObject.hpp>
|
#include <objects/CharacterObject.hpp>
|
||||||
#include <script/ScriptMachine.hpp>
|
#include <script/ScriptMachine.hpp>
|
||||||
#include <script/SCMFile.hpp>
|
#include <script/SCMFile.hpp>
|
||||||
|
#include <ai/PlayerController.hpp>
|
||||||
|
|
||||||
#include <fstream>
|
#include <fstream>
|
||||||
#include <cereal/cereal.hpp>
|
#include <cereal/cereal.hpp>
|
||||||
@ -88,6 +89,16 @@ void serialize(Archive& archive,
|
|||||||
s.display);
|
s.display);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template<class Archive>
|
||||||
|
void serialize(Archive& archive,
|
||||||
|
GarageInfo& s)
|
||||||
|
{
|
||||||
|
archive(
|
||||||
|
s.min,
|
||||||
|
s.max,
|
||||||
|
s.type);
|
||||||
|
}
|
||||||
|
|
||||||
template<class Archive>
|
template<class Archive>
|
||||||
void serialize(Archive& archive,
|
void serialize(Archive& archive,
|
||||||
GameState& s)
|
GameState& s)
|
||||||
@ -313,3 +324,310 @@ bool SaveGame::loadObjects(GameWorld& world, const std::string& file)
|
|||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Original save game file data structures
|
||||||
|
typedef uint16_t BlockWord;
|
||||||
|
typedef uint32_t BlockDword;
|
||||||
|
typedef BlockDword BlockSize;
|
||||||
|
|
||||||
|
struct Block0Data {
|
||||||
|
uint16_t saveName[24];
|
||||||
|
BlockWord year;
|
||||||
|
BlockWord month;
|
||||||
|
BlockWord weekday;
|
||||||
|
BlockWord day;
|
||||||
|
BlockWord hour;
|
||||||
|
BlockWord minute;
|
||||||
|
BlockWord second;
|
||||||
|
BlockWord milliseconds;
|
||||||
|
BlockDword unknown;
|
||||||
|
BlockDword islandNumber;
|
||||||
|
glm::vec3 cameraPosition;
|
||||||
|
BlockDword gameMinuteMS;
|
||||||
|
BlockDword lastTick;
|
||||||
|
uint8_t gameHour;
|
||||||
|
uint8_t _align0[3];
|
||||||
|
uint8_t gameMinute;
|
||||||
|
uint8_t _align1[3];
|
||||||
|
BlockWord padMode;
|
||||||
|
uint8_t _align2[2];
|
||||||
|
BlockDword timeMS;
|
||||||
|
float timeScale;
|
||||||
|
float timeStep;
|
||||||
|
float timeStep_unclipped; // Unknown purpose
|
||||||
|
BlockDword frameCounter;
|
||||||
|
float timeStep2;
|
||||||
|
float framesPerUpdate;
|
||||||
|
float timeScale2;
|
||||||
|
BlockWord lastWeather;
|
||||||
|
uint8_t _align3[2];
|
||||||
|
BlockWord nextWeather;
|
||||||
|
uint8_t _align4[2];
|
||||||
|
BlockWord forcedWeather;
|
||||||
|
uint8_t _align5[2];
|
||||||
|
float weatherInterpolation;
|
||||||
|
uint8_t dateTime[24]; // Unused
|
||||||
|
BlockDword weatherType;
|
||||||
|
float cameraData;
|
||||||
|
float cameraData2;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Block0ContactInfo {
|
||||||
|
BlockDword missionFlag;
|
||||||
|
BlockDword baseBrief;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Block0BuildingSwap {
|
||||||
|
BlockDword type;
|
||||||
|
BlockDword handle;
|
||||||
|
BlockDword newModel;
|
||||||
|
BlockDword oldModel;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Block0InvisibilitySettings {
|
||||||
|
BlockDword type;
|
||||||
|
BlockDword handle;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Block0RunningScript {
|
||||||
|
uint32_t nextPointer;
|
||||||
|
uint32_t prevPointer;
|
||||||
|
char name[8];
|
||||||
|
BlockDword programCounter;
|
||||||
|
BlockDword stack[4];
|
||||||
|
BlockDword unknown0;
|
||||||
|
BlockDword unknown1;
|
||||||
|
BlockWord stackCounter;
|
||||||
|
BlockWord unknown2;
|
||||||
|
BlockDword scriptVariables[16];
|
||||||
|
BlockDword timerA;
|
||||||
|
BlockDword timerB;
|
||||||
|
uint8_t ifFlag;
|
||||||
|
uint8_t unknown3;
|
||||||
|
uint8_t unknown4;
|
||||||
|
uint8_t _align0;
|
||||||
|
BlockDword wakeTimer;
|
||||||
|
BlockWord ifNumber; // ?
|
||||||
|
uint8_t unknown[6];
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Block0ScriptData {
|
||||||
|
BlockDword onMissionOffset;
|
||||||
|
Block0ContactInfo contactInfo[16];
|
||||||
|
uint8_t unknown[0x100];
|
||||||
|
BlockDword lastMissionPassedTime;
|
||||||
|
Block0BuildingSwap buildingSwap[25];
|
||||||
|
Block0InvisibilitySettings invisibilitySettings[20];
|
||||||
|
uint8_t scriptRunning;
|
||||||
|
uint8_t _align0[3];
|
||||||
|
BlockDword mainSize;
|
||||||
|
BlockDword largestMissionSize;
|
||||||
|
BlockWord missionCount;
|
||||||
|
uint8_t _align1[2];
|
||||||
|
};
|
||||||
|
|
||||||
|
struct StructWeaponSlot {
|
||||||
|
BlockDword weaponId;
|
||||||
|
BlockDword unknown0;
|
||||||
|
BlockDword inClip;
|
||||||
|
BlockDword totalBullets;
|
||||||
|
BlockDword unknown1;
|
||||||
|
BlockDword unknown2;
|
||||||
|
};
|
||||||
|
struct StructPed {
|
||||||
|
uint8_t unknown0_[52];
|
||||||
|
glm::vec3 position;
|
||||||
|
uint8_t unknown1[640];
|
||||||
|
float health;
|
||||||
|
float armour;
|
||||||
|
uint8_t unknown2[148];
|
||||||
|
StructWeaponSlot weapons[13];
|
||||||
|
uint8_t unknown3[348];
|
||||||
|
};
|
||||||
|
|
||||||
|
// 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 maxWantedLevel;
|
||||||
|
BlockDword maxChaosLevel;
|
||||||
|
uint8_t modelName[24];
|
||||||
|
uint8_t align[2];
|
||||||
|
};
|
||||||
|
|
||||||
|
void SaveGame::writeGame(GameState& state, const std::string& file)
|
||||||
|
{
|
||||||
|
std::FILE* saveFile = std::fopen(file.c_str(), "w");
|
||||||
|
|
||||||
|
// BLOCK 0 - Variables
|
||||||
|
Block0Data block0Data = {};
|
||||||
|
//strcpy(block0Data.saveName, "OpenRW Save Game");
|
||||||
|
block0Data.islandNumber = 1;
|
||||||
|
block0Data.cameraPosition = glm::vec3(0.f);
|
||||||
|
block0Data.gameMinuteMS = 1000;
|
||||||
|
block0Data.lastTick = 1;
|
||||||
|
block0Data.gameHour = 12;
|
||||||
|
block0Data.gameMinute = 13;
|
||||||
|
block0Data.padMode = 1;
|
||||||
|
block0Data.timeMS = 10000;
|
||||||
|
block0Data.timeScale = 1.0;
|
||||||
|
block0Data.timeStep = 1.0/60.f;
|
||||||
|
block0Data.timeStep_unclipped = block0Data.timeStep;
|
||||||
|
block0Data.frameCounter = 1000;
|
||||||
|
block0Data.timeStep2 = 1.0;
|
||||||
|
block0Data.framesPerUpdate = 1.0;
|
||||||
|
block0Data.timeScale2 = 1.0;
|
||||||
|
block0Data.lastWeather = 1;
|
||||||
|
block0Data.nextWeather = 1;
|
||||||
|
block0Data.weatherInterpolation = 1.0;
|
||||||
|
block0Data.weatherType = 1;
|
||||||
|
|
||||||
|
BlockSize block0Size = sizeof(block0Data); // TODO calculate correctly.
|
||||||
|
fwrite(&block0Size, sizeof(BlockSize), 1, saveFile);
|
||||||
|
fwrite(&block0Data, sizeof(block0Data), 1, saveFile);
|
||||||
|
|
||||||
|
// BLOCK 0 - 0 Script
|
||||||
|
const char header[4] = "SCR";
|
||||||
|
BlockSize block0ScriptSize = sizeof(Block0ScriptData);
|
||||||
|
BlockSize block0ScriptHeaderSize = block0ScriptSize + sizeof(char) * 4 + sizeof(BlockDword);
|
||||||
|
fwrite(header, sizeof(char), 4, saveFile);
|
||||||
|
fwrite(&block0ScriptHeaderSize, sizeof(BlockSize), 1, saveFile);
|
||||||
|
BlockDword scriptVariablesCount = state.script->getGlobalData().size();
|
||||||
|
fwrite(&scriptVariablesCount, sizeof(BlockDword), 1, saveFile);
|
||||||
|
fwrite(state.script->getGlobals(), sizeof(SCMByte), scriptVariablesCount, saveFile);
|
||||||
|
|
||||||
|
BlockDword scriptDataSize = 0x03C8;
|
||||||
|
fwrite(&scriptDataSize, sizeof(BlockDword), 1, saveFile);
|
||||||
|
|
||||||
|
Block0ScriptData block0ScriptData = {};
|
||||||
|
block0ScriptData.onMissionOffset = (BlockDword)((SCMByte*)state.scriptOnMissionFlag - state.script->getGlobals());
|
||||||
|
block0ScriptData.lastMissionPassedTime = 0;
|
||||||
|
block0ScriptData.scriptRunning = 1;
|
||||||
|
block0ScriptData.mainSize = state.script->getFile()->getMainSize();
|
||||||
|
block0ScriptData.largestMissionSize = state.script->getFile()->getLargestMissionSize();
|
||||||
|
block0ScriptData.missionCount = state.script->getFile()->getMissionOffsets().size();
|
||||||
|
fwrite(&block0ScriptData, sizeof(block0ScriptData), 1, saveFile);
|
||||||
|
|
||||||
|
BlockDword scriptCount = state.script->getThreads().size();
|
||||||
|
fwrite(&scriptCount, sizeof(BlockDword), 1, saveFile);
|
||||||
|
|
||||||
|
for(SCMThread& thread : state.script->getThreads())
|
||||||
|
{
|
||||||
|
Block0RunningScript script = {};
|
||||||
|
strcpy(script.name, thread.name);
|
||||||
|
script.programCounter = thread.programCounter;
|
||||||
|
for(int i = 0; i < SCM_STACK_DEPTH; i++) {
|
||||||
|
script.stack[i] = thread.calls[i];
|
||||||
|
}
|
||||||
|
script.stackCounter = thread.stackDepth;
|
||||||
|
for(int i = 0; i < 16; i++) {
|
||||||
|
script.scriptVariables[i] = *(((BlockDword*)thread.locals.data())+i);
|
||||||
|
}
|
||||||
|
script.timerA = *(BlockDword*)(thread.locals.data() + 16 * sizeof ( SCMByte ) * 4);
|
||||||
|
script.timerB = *(BlockDword*)(thread.locals.data() + 16 * sizeof ( SCMByte ) * 4);
|
||||||
|
script.ifFlag = thread.conditionResult;
|
||||||
|
script.wakeTimer = thread.wakeCounter;
|
||||||
|
script.ifNumber = thread.conditionCount;
|
||||||
|
fwrite(&script, sizeof(block0ScriptData), 1, saveFile);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const size_type DWORDSZ = sizeof(BlockDword);
|
||||||
|
BlockDword readDword(std::FILE* file)
|
||||||
|
{
|
||||||
|
BlockDword sz;
|
||||||
|
fread(&sz, sizeof(BlockDword), 1, file);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool SaveGame::loadGame(GameState& state, 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);
|
||||||
|
|
||||||
|
BlockDword scriptBlockSize;
|
||||||
|
fread(&scriptBlockSize, sizeof(BlockDword), 1, loadFile);
|
||||||
|
|
||||||
|
char signature[4];
|
||||||
|
fread(signature, sizeof(char), 4, loadFile);
|
||||||
|
if( std::strncmp(signature, "SCR", 3) != 0 ) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
fread(&scriptBlockSize, sizeof(BlockDword), 1, loadFile);
|
||||||
|
|
||||||
|
BlockDword scriptVars;
|
||||||
|
fread(&scriptVars, sizeof(BlockDword), 1, loadFile);
|
||||||
|
|
||||||
|
SCMByte bytes[scriptVars];
|
||||||
|
fread(bytes, sizeof(SCMByte), scriptVars, loadFile);
|
||||||
|
|
||||||
|
BlockDword scriptDataBlockSize;
|
||||||
|
fread(&scriptDataBlockSize, sizeof(BlockDword), 1, loadFile);
|
||||||
|
if( scriptDataBlockSize != 0x03C8 ) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
Block0ScriptData scriptData;
|
||||||
|
fread(&scriptData, sizeof(Block0ScriptData), 1, loadFile);
|
||||||
|
|
||||||
|
BlockDword numScripts;
|
||||||
|
fread(&numScripts, DWORDSZ, 1, loadFile);
|
||||||
|
Block0RunningScript scripts[numScripts];
|
||||||
|
fread(scripts, sizeof(Block0RunningScript), numScripts, loadFile);
|
||||||
|
|
||||||
|
// BLOCK 1
|
||||||
|
BlockDword playerBlockSize = readDword(loadFile);
|
||||||
|
BlockDword playerInfoSize = readDword(loadFile);
|
||||||
|
|
||||||
|
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);
|
||||||
|
|
||||||
|
// Insert Game State.
|
||||||
|
state.hour = block0Data.gameHour;
|
||||||
|
state.minute = block0Data.gameMinute;
|
||||||
|
state.gameTime = block0Data.timeMS / 1000.f;
|
||||||
|
state.currentWeather = block0Data.nextWeather;
|
||||||
|
state.cameraPosition = block0Data.cameraPosition;
|
||||||
|
|
||||||
|
for(int v = 0; v < scriptVars; ++v) {
|
||||||
|
state.script->getGlobals()[v] = bytes[v];
|
||||||
|
}
|
||||||
|
|
||||||
|
state.scriptOnMissionFlag = (unsigned int*)state.script->getGlobals() + (size_t)scriptData.onMissionOffset;
|
||||||
|
|
||||||
|
auto& threads = state.script->getThreads();
|
||||||
|
for(int s = 0; s < numScripts; ++s) {
|
||||||
|
state.script->startThread(scripts[s].programCounter);
|
||||||
|
SCMThread& thread = threads.back();
|
||||||
|
// thread.baseAddress // ??
|
||||||
|
thread.conditionResult = scripts[s].ifFlag;
|
||||||
|
thread.conditionCount = scripts[s].ifNumber;
|
||||||
|
thread.stackDepth = scripts[s].stackCounter;
|
||||||
|
for(int i = 0; i < SCM_STACK_DEPTH; ++i) {
|
||||||
|
thread.calls[i] = scripts[s].stack[i];
|
||||||
|
}
|
||||||
|
thread.wakeCounter = scripts[s].wakeTimer;
|
||||||
|
}
|
||||||
|
|
||||||
|
if( playerCount > 0 ) {
|
||||||
|
auto player = state.world->createPlayer(players[0].info.position);
|
||||||
|
player->mHealth = players[0].info.health;
|
||||||
|
state.playerObject = player->getGameObjectID();
|
||||||
|
state.maxWantedLevel = players[0].maxWantedLevel;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@ -373,15 +373,17 @@ void game_get_player(const ScriptArguments& args)
|
|||||||
|
|
||||||
void game_create_garage(const ScriptArguments& args)
|
void game_create_garage(const ScriptArguments& args)
|
||||||
{
|
{
|
||||||
|
// http://www.gtamodding.com/index.php?title=Garage#GTA_III
|
||||||
glm::vec3 min(args[0].real, args[1].real, args[2].real);
|
glm::vec3 min(args[0].real, args[1].real, args[2].real);
|
||||||
glm::vec3 max(args[3].real, args[4].real, args[5].real);
|
glm::vec3 max(args[3].real, args[4].real, args[5].real);
|
||||||
|
|
||||||
/// @todo http://www.gtamodding.com/index.php?title=Garage#GTA_III
|
|
||||||
int garageType = args[6].integer;
|
int garageType = args[6].integer;
|
||||||
// TODO actually store the garage information and return the handle
|
|
||||||
*args[7].globalInteger = 0;
|
|
||||||
|
|
||||||
args.getWorld()->logger->warning("SCM", "Garages Unimplemented! " + std::to_string(garageType));
|
args.getWorld()->state->garages.push_back({
|
||||||
|
min, max, garageType
|
||||||
|
});
|
||||||
|
|
||||||
|
// TODO actually store the garage information and return the handle
|
||||||
|
*args[7].globalInteger = args.getWorld()->state->garages.size()-1;
|
||||||
}
|
}
|
||||||
|
|
||||||
void game_disable_ped_paths(const ScriptArguments& args)
|
void game_disable_ped_paths(const ScriptArguments& args)
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
#include <boost/test/unit_test.hpp>
|
#include <boost/test/unit_test.hpp>
|
||||||
#include <engine/GameState.hpp>
|
#include <engine/GameState.hpp>
|
||||||
#include <engine/SaveGame.hpp>
|
#include <engine/SaveGame.hpp>
|
||||||
|
#include <script/ScriptMachine.hpp>
|
||||||
#include <test_globals.hpp>
|
#include <test_globals.hpp>
|
||||||
|
|
||||||
BOOST_AUTO_TEST_SUITE(SaveGameTests)
|
BOOST_AUTO_TEST_SUITE(SaveGameTests)
|
||||||
@ -44,6 +45,22 @@ BOOST_AUTO_TEST_CASE(test_write_state)
|
|||||||
BOOST_CHECK_EQUAL( loaded.overrideNextStart, state.overrideNextStart );
|
BOOST_CHECK_EQUAL( loaded.overrideNextStart, state.overrideNextStart );
|
||||||
BOOST_CHECK_EQUAL( loaded.hour, state.hour );
|
BOOST_CHECK_EQUAL( loaded.hour, state.hour );
|
||||||
BOOST_CHECK_EQUAL( loaded.minute, state.minute );
|
BOOST_CHECK_EQUAL( loaded.minute, state.minute );
|
||||||
|
|
||||||
|
// Check Garage data + garage vehicle restoration
|
||||||
|
}
|
||||||
|
|
||||||
|
BOOST_AUTO_TEST_CASE(test_load_game)
|
||||||
|
{
|
||||||
|
GameState state;
|
||||||
|
GameWorld world();
|
||||||
|
SCMOpcodes s;
|
||||||
|
auto file = Global::get().d->loadSCM("data/main.scm");
|
||||||
|
ScriptMachine machine(&state, file, &s);
|
||||||
|
|
||||||
|
state.world = Global::get().e;
|
||||||
|
state.script = &machine;
|
||||||
|
|
||||||
|
BOOST_REQUIRE( SaveGame::loadGame(state, "GTA3sf1.b") );
|
||||||
}
|
}
|
||||||
|
|
||||||
BOOST_AUTO_TEST_SUITE_END()
|
BOOST_AUTO_TEST_SUITE_END()
|
@ -187,26 +187,17 @@ void RWGame::saveGame(const std::string& savename)
|
|||||||
void RWGame::loadGame(const std::string& savename)
|
void RWGame::loadGame(const std::string& savename)
|
||||||
{
|
{
|
||||||
delete state->world;
|
delete state->world;
|
||||||
//delete state;
|
delete state->script;
|
||||||
state = nullptr;
|
state = nullptr;
|
||||||
|
|
||||||
newGame();
|
newGame();
|
||||||
|
|
||||||
startScript("data/main.scm");
|
startScript("data/main.scm");
|
||||||
|
|
||||||
if(! SaveGame::loadScript(*script, savename+".script") )
|
if(! SaveGame::loadGame(*state, "GTA3sf1.b") )
|
||||||
{
|
{
|
||||||
log.error("Game", "Failed to restore Script");
|
log.error("Game", "Failed to load game");
|
||||||
}
|
}
|
||||||
if(! SaveGame::loadState(*state, savename+".state") )
|
|
||||||
{
|
|
||||||
log.error("Game", "Failed to restore State");
|
|
||||||
}
|
|
||||||
if(! SaveGame::loadObjects(*world, savename+".world") )
|
|
||||||
{
|
|
||||||
log.error("Game", "Failed to restore World");
|
|
||||||
}
|
|
||||||
// TODO objects.
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void RWGame::startScript(const std::string& name)
|
void RWGame::startScript(const std::string& name)
|
||||||
@ -246,6 +237,7 @@ void RWGame::startScript(const std::string& name)
|
|||||||
log.info("Script", ss.str());
|
log.info("Script", ss.str());
|
||||||
});
|
});
|
||||||
script->addBreakpoint(0);
|
script->addBreakpoint(0);
|
||||||
|
state->script = script;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
log.error("Game", "Failed to load SCM: " + name);
|
log.error("Game", "Failed to load SCM: " + name);
|
||||||
|
Loading…
Reference in New Issue
Block a user