diff --git a/rwengine/include/engine/GameData.hpp b/rwengine/include/engine/GameData.hpp index ed01e663..eb91083e 100644 --- a/rwengine/include/engine/GameData.hpp +++ b/rwengine/include/engine/GameData.hpp @@ -2,6 +2,8 @@ #ifndef _GAMEDATA_HPP_ #define _GAMEDATA_HPP_ +class Logger; + #include #include #include @@ -41,13 +43,15 @@ private: std::string datpath; std::string splash; + Logger* logger; + public: /** * ctor * @param path Path to the root of the game data. */ - GameData(const std::string& path = ""); + GameData(Logger* log, const std::string& path = ""); ~GameData(); GameWorld* engine; @@ -273,6 +277,11 @@ public: float getWaveHeightAt(const glm::vec3& ws) const; GameTexts texts; + + /** + * Determines whether the given path is a valid game directory. + */ + static bool isValidGameDirectory(const std::string& path); }; #endif diff --git a/rwengine/include/engine/GameWorld.hpp b/rwengine/include/engine/GameWorld.hpp index 0ce18501..0b0c3714 100644 --- a/rwengine/include/engine/GameWorld.hpp +++ b/rwengine/include/engine/GameWorld.hpp @@ -2,7 +2,7 @@ #ifndef _GAMEWORLD_HPP_ #define _GAMEWORLD_HPP_ -#include +class Logger; #include #include @@ -59,16 +59,11 @@ class GameWorld { public: - GameWorld(const std::string& gamepath); + GameWorld(Logger* log, const std::string& gamepath); ~GameWorld(); - - /** - * Loads the game data - */ - bool load(); - Logger logger; + Logger* logger; /** * Loads an IDE into the game diff --git a/rwengine/include/render/GameRenderer.hpp b/rwengine/include/render/GameRenderer.hpp index 6b535b74..3c5fa061 100644 --- a/rwengine/include/render/GameRenderer.hpp +++ b/rwengine/include/render/GameRenderer.hpp @@ -1,6 +1,8 @@ #ifndef _GAMERENDERER_HPP_ #define _GAMERENDERER_HPP_ +class Logger; + #define GLEW_STATIC #include #include @@ -43,6 +45,9 @@ class GameRenderer { /** Pointer to the world instance */ GameWorld* engine; + + /** Logger to output messages */ + Logger* logger; /** The low-level drawing interface to use */ Renderer* renderer; @@ -86,7 +91,7 @@ class GameRenderer public: - GameRenderer(GameWorld*); + GameRenderer(Logger* log, GameWorld*); ~GameRenderer(); /** Number of culling events */ diff --git a/rwengine/src/engine/GameData.cpp b/rwengine/src/engine/GameData.cpp index 98b07403..e794ee8d 100644 --- a/rwengine/src/engine/GameData.cpp +++ b/rwengine/src/engine/GameData.cpp @@ -13,6 +13,7 @@ #include #include #include +#include #include #include @@ -79,10 +80,9 @@ std::string fixPath(std::string path) { } -GameData::GameData(const std::string& path) -: datpath(path), engine(nullptr) +GameData::GameData(Logger* log, const std::string& path) +: datpath(path), logger(log), engine(nullptr) { - } GameData::~GameData() @@ -124,7 +124,7 @@ void GameData::parseDAT(const std::string& path) if(!datfile.is_open()) { - engine->logger.error("Data", "Failed to open game file " + path); + logger->error("Data", "Failed to open game file " + path); } else { @@ -221,12 +221,12 @@ bool GameData::loadZone(const std::string& path) for(auto& z : ipll.zones) { zones.insert({z.name, z}); } - engine->logger.info("Data", "Loaded " + std::to_string(ipll.zones.size()) + " zones from " + path); + logger->info("Data", "Loaded " + std::to_string(ipll.zones.size()) + " zones from " + path); return true; } } else { - engine->logger.error("Data", "Failed to load zones from " + path); + logger->error("Data", "Failed to load zones from " + path); } return false; @@ -491,7 +491,7 @@ bool GameData::loadAudioClip(const std::string& name) if ( name.find(".mp3") != name.npos ) { - engine->logger.error("Data", "MP3 Audio unsupported outside cutscenes"); + logger->error("Data", "MP3 Audio unsupported outside cutscenes"); return false; } @@ -501,7 +501,7 @@ bool GameData::loadAudioClip(const std::string& name) if (! r ) { - engine->logger.error("Data", "Error loading audio clip " + fname); + logger->error("Data", "Error loading audio clip " + fname); delete engine->missionAudio; engine->missionAudio = nullptr; } @@ -524,7 +524,7 @@ FileHandle GameData::openFile(const std::string &name) auto file = index.openFile(name); if( file == nullptr ) { - engine->logger.error("Data", "Unable to open file: " + name); + logger->error("Data", "Unable to open file: " + name); } return file; } @@ -559,3 +559,20 @@ float GameData::getWaveHeightAt(const glm::vec3 &ws) const { return (1+sin(engine->gameTime + (ws.x + ws.y) * WATER_SCALE)) * WATER_HEIGHT; } + +bool GameData::isValidGameDirectory(const std::string& path) +{ + if(path.empty()) + { + return false; + } + + LoaderIMG i; + if(! i.load(path + "/models/gta3.img") ) + { + return false; + } + + return true; +} + diff --git a/rwengine/src/engine/GameWorld.cpp b/rwengine/src/engine/GameWorld.cpp index 53bbf06d..7dfb6d46 100644 --- a/rwengine/src/engine/GameWorld.cpp +++ b/rwengine/src/engine/GameWorld.cpp @@ -1,4 +1,7 @@ #include + +#include + #include #include #include @@ -75,12 +78,22 @@ public: } }; -GameWorld::GameWorld(const std::string& path) - : gameTime(0.f), gameData(path), randomEngine(rand()), +GameWorld::GameWorld(Logger* log, const std::string& path) + : logger(log), gameTime(0.f), gameData(log, path), randomEngine(rand()), _work( new WorkContext( this ) ), script(nullptr), cutsceneAudio(nullptr), missionAudio(nullptr), paused(false) { gameData.engine = this; + + collisionConfig = new btDefaultCollisionConfiguration; + collisionDispatcher = new WorldCollisionDispatcher(collisionConfig); + broadphase = new btDbvtBroadphase(); + solver = new btSequentialImpulseConstraintSolver; + dynamicsWorld = new btDiscreteDynamicsWorld(collisionDispatcher, broadphase, solver, collisionConfig); + dynamicsWorld->setGravity(btVector3(0.f, 0.f, -9.81f)); + broadphase->getOverlappingPairCache()->setInternalGhostPairCallback(new btGhostPairCallback()); + gContactProcessedCallback = ContactProcessedCallback; + dynamicsWorld->setInternalTickCallback(PhysicsTickCallback, this); } GameWorld::~GameWorld() @@ -100,23 +113,6 @@ GameWorld::~GameWorld() /// @todo delete other things. } -bool GameWorld::load() -{ - collisionConfig = new btDefaultCollisionConfiguration; - collisionDispatcher = new WorldCollisionDispatcher(collisionConfig); - broadphase = new btDbvtBroadphase(); - solver = new btSequentialImpulseConstraintSolver; - dynamicsWorld = new btDiscreteDynamicsWorld(collisionDispatcher, broadphase, solver, collisionConfig); - dynamicsWorld->setGravity(btVector3(0.f, 0.f, -9.81f)); - broadphase->getOverlappingPairCache()->setInternalGhostPairCallback(new btGhostPairCallback()); - gContactProcessedCallback = ContactProcessedCallback; - dynamicsWorld->setInternalTickCallback(PhysicsTickCallback, this); - - gameData.load(); - - return true; -} - bool GameWorld::defineItems(const std::string& name) { auto i = gameData.ideLocations.find(name); @@ -142,7 +138,7 @@ bool GameWorld::defineItems(const std::string& name) } } else { - logger.error("Data", "Failed to load IDE " + path); + logger->error("Data", "Failed to load IDE " + path); } return false; @@ -162,7 +158,7 @@ void GameWorld::runScript(const std::string &name) script = new ScriptMachine(this, f, opcodes); } else { - logger.error("World", "Failed to load SCM: " + name); + logger->error("World", "Failed to load SCM: " + name); } } @@ -179,7 +175,7 @@ bool GameWorld::placeItems(const std::string& name) for( size_t i = 0; i < ipll.m_instances.size(); ++i) { std::shared_ptr inst = ipll.m_instances[i]; if(! createInstance(inst->id, inst->pos, inst->rot)) { - logger.error("World", "No object data for instance " + std::to_string(inst->id) + " in " + path); + logger->error("World", "No object data for instance " + std::to_string(inst->id) + " in " + path); } } @@ -200,7 +196,7 @@ bool GameWorld::placeItems(const std::string& name) } else { - logger.error("Data", "Failed to load IPL " + path); + logger->error("Data", "Failed to load IPL " + path); return false; } @@ -238,7 +234,7 @@ InstanceObject *GameWorld::createInstance(const uint16_t id, const glm::vec3& po } if( modelname.empty() ) { - logger.warning("World", "Instance with missing model: " + std::to_string(id)); + logger->warning("World", "Instance with missing model: " + std::to_string(id)); } auto instance = new InstanceObject( @@ -306,6 +302,7 @@ uint16_t GameWorld::findModelDefinition(const std::string model) } #include +#include CutsceneObject *GameWorld::createCutsceneObject(const uint16_t id, const glm::vec3 &pos, const glm::quat &rot) { std::string modelname; @@ -351,7 +348,7 @@ CutsceneObject *GameWorld::createCutsceneObject(const uint16_t id, const glm::ve // Ensure the relevant data is loaded. if( modelname.empty() ) { - logger.error("World", "Cutscene object " + std::to_string(id) + " has no model"); + logger->error("World", "Cutscene object " + std::to_string(id) + " has no model"); return nullptr; } @@ -381,7 +378,7 @@ VehicleObject *GameWorld::createVehicle(const uint16_t id, const glm::vec3& pos, { auto vti = findObjectType(id); if( vti ) { - logger.info("World", "Creating Vehicle ID " + std::to_string(id) + " (" + vti->gameName + ")"); + logger->info("World", "Creating Vehicle ID " + std::to_string(id) + " (" + vti->gameName + ")"); if(! vti->modelName.empty()) { gameData.loadDFF(vti->modelName + ".dff"); @@ -399,7 +396,7 @@ VehicleObject *GameWorld::createVehicle(const uint16_t id, const glm::vec3& pos, sec = gameData.vehicleColours[palit->second[set].second]; } else { - logger.warning("World", "No colour palette for vehicle " + vti->modelName); + logger->warning("World", "No colour palette for vehicle " + vti->modelName); } auto wi = findObjectType(vti->wheelModelID); @@ -705,7 +702,7 @@ void GameWorld::loadCutscene(const std::string &name) if ( !cutsceneAudioLoaded ) { - logger.warning("Data", "Failed to load cutscene audio: " + name); + logger->warning("Data", "Failed to load cutscene audio: " + name); } @@ -714,7 +711,7 @@ void GameWorld::loadCutscene(const std::string &name) } state.currentCutscene = cutscene; state.currentCutscene->meta.name = name; - logger.info("World", "Loaded cutscene: " + name); + logger->info("World", "Loaded cutscene: " + name); } void GameWorld::startCutscene() diff --git a/rwengine/src/render/GameRenderer.cpp b/rwengine/src/render/GameRenderer.cpp index 24345aeb..e51dc0e4 100644 --- a/rwengine/src/render/GameRenderer.cpp +++ b/rwengine/src/render/GameRenderer.cpp @@ -19,6 +19,7 @@ #include #include +#include #include #include @@ -65,11 +66,11 @@ std::vector sspaceRect = { GeometryBuffer ssRectGeom; DrawBuffer ssRectDraw; -GameRenderer::GameRenderer(GameWorld* engine) - : engine(engine), renderer(new OpenGLRenderer), _renderAlpha(0.f), +GameRenderer::GameRenderer(Logger* log, GameWorld* engine) + : engine(engine), logger(log), renderer(new OpenGLRenderer), _renderAlpha(0.f), map(engine, renderer), water(this), text(engine, this) { - engine->logger.info("Renderer", renderer->getIDString()); + logger->info("Renderer", renderer->getIDString()); worldProg = renderer->createShader( GameShaders::WorldObject::VertexShader, @@ -504,7 +505,7 @@ void GameRenderer::renderVehicle(VehicleObject *vehicle) { if(!vehicle->model) { - engine->logger.warning("Renderer", "Vehicle model " + vehicle->vehicle->modelName + " not loaded!"); + logger->warning("Renderer", "Vehicle model " + vehicle->vehicle->modelName + " not loaded!"); } glm::mat4 matrixModel = vehicle->getTimeAdjustedTransform( _renderAlpha ); @@ -679,7 +680,7 @@ void GameRenderer::renderPickup(PickupObject *pickup) itemModel = weapons->resource->findFrame(odata->modelName + "_l0"); if ( ! itemModel ) { - engine->logger.error("Renderer", "Weapon frame " + odata->modelName + " not in model"); + logger->error("Renderer", "Weapon frame " + odata->modelName + " not in model"); } } } @@ -693,7 +694,7 @@ void GameRenderer::renderPickup(PickupObject *pickup) } else { - engine->logger.error("Renderer", "Pickup model " + odata->modelName + " not loaded"); + logger->error("Renderer", "Pickup model " + odata->modelName + " not loaded"); } } @@ -762,11 +763,11 @@ void GameRenderer::renderProjectile(ProjectileObject *projectile) renderFrame(weapons->resource, itemModel, modelMatrix * matrix, nullptr, 1.f); } else { - engine->logger.error("Renderer", "Weapon frame " + odata->modelName + " not in model"); + logger->error("Renderer", "Weapon frame " + odata->modelName + " not in model"); } } else { - engine->logger.error("Renderer", "Weapon.dff not loaded"); + logger->error("Renderer", "Weapon.dff not loaded"); } } @@ -806,11 +807,11 @@ void GameRenderer::renderItem(InventoryItem *item, const glm::mat4 &modelMatrix) renderFrame(weapons->resource, itemModel, modelMatrix * matrix, nullptr, 1.f); } else { - engine->logger.error("Renderer", "Weapon frame " + odata->modelName + " not in model"); + logger->error("Renderer", "Weapon frame " + odata->modelName + " not in model"); } } else { - engine->logger.error("Renderer", "Weapon model not loaded"); + logger->error("Renderer", "Weapon model not loaded"); } } @@ -841,7 +842,7 @@ void GameRenderer::renderGeometry(Model* model, size_t g, const glm::mat4& model tex = engine->gameData.findTexture(tC, tA); if( ! tex ) { - //engine->logger.warning("Renderer", "Missing texture: " + tC + " " + tA); + //logger->warning("Renderer", "Missing texture: " + tC + " " + tA); } mat.textures[0].texture = tex; } diff --git a/rwengine/src/script/modules/GameModule.cpp b/rwengine/src/script/modules/GameModule.cpp index db03fcea..72af4223 100644 --- a/rwengine/src/script/modules/GameModule.cpp +++ b/rwengine/src/script/modules/GameModule.cpp @@ -19,6 +19,7 @@ #include #include #include +#include #include @@ -91,7 +92,7 @@ void game_create_vehicle_generator(const ScriptArguments& args) if(args[4].type == TString) { - args.getVM()->getWorld()->logger.error("SCM", "Model names not supported for vehicle generator"); + args.getVM()->getWorld()->logger->error("SCM", "Model names not supported for vehicle generator"); return; } @@ -381,7 +382,7 @@ void game_create_garage(const ScriptArguments& args) int garageType = args[6].integer; auto garageHandle = args[7].handle; - args.getVM()->getWorld()->logger.warning("SCM", "Garages Unimplemented! " + std::to_string(garageType)); + args.getVM()->getWorld()->logger->warning("SCM", "Garages Unimplemented! " + std::to_string(garageType)); } void game_disable_ped_paths(const ScriptArguments& args) @@ -438,7 +439,7 @@ bool game_model_loaded(const ScriptArguments& args) void game_restart_critical_mission(const ScriptArguments& args) { - args.getVM()->getWorld()->logger.info("SCM", "Restarting Critical Mission"); + args.getVM()->getWorld()->logger->info("SCM", "Restarting Critical Mission"); // Reset player state. glm::vec3 position(args[0].real, args[1].real, args[2].real + 1.f); @@ -553,7 +554,7 @@ void game_create_cutscene_object(const ScriptArguments& args) *args[1].handle = object; if( object == nullptr ) { - args.getVM()->getWorld()->logger.error("SCM", "Could not create cutscene object " + std::to_string(id)); + args.getVM()->getWorld()->logger->error("SCM", "Could not create cutscene object " + std::to_string(id)); } } void game_set_cutscene_anim(const ScriptArguments& args) @@ -566,7 +567,7 @@ void game_set_cutscene_anim(const ScriptArguments& args) object->animator->setAnimation(anim, false); } else { - args.getVM()->getWorld()->logger.error("SCM", "Failed to load cutscene anim: " + animName); + args.getVM()->getWorld()->logger->error("SCM", "Failed to load cutscene anim: " + animName); } } void game_start_cutscene(const ScriptArguments& args) @@ -632,7 +633,7 @@ void game_set_head_animation(const ScriptArguments& args) object->animator->setAnimation(anim, false); } else { - args.getVM()->getWorld()->logger.error("SCM", "Failed to load cutscene anim: " + animName); + args.getVM()->getWorld()->logger->error("SCM", "Failed to load cutscene anim: " + animName); } } @@ -721,7 +722,7 @@ void game_load_audio(const ScriptArguments& args) { if(! args.getVM()->getWorld()->gameData.loadAudioClip(name + ".mp3") ) { - args.getVM()->getWorld()->logger.error("SCM", "Failed to load audio: " + name); + args.getVM()->getWorld()->logger->error("SCM", "Failed to load audio: " + name); } } } @@ -756,7 +757,7 @@ void game_play_music_id(const ScriptArguments& args) // TODO play anything other than Miscom.wav if(! gw->gameData.loadAudioClip( name + ".wav" ) ) { - args.getVM()->getWorld()->logger.error("SCM", "Error loading audio " + name); + args.getVM()->getWorld()->logger->error("SCM", "Error loading audio " + name); return; } else if ( args.getVM()->getWorld()->missionAudio ) diff --git a/rwengine/src/script/modules/ObjectModule.cpp b/rwengine/src/script/modules/ObjectModule.cpp index 91a9a34b..9dd44c0a 100644 --- a/rwengine/src/script/modules/ObjectModule.cpp +++ b/rwengine/src/script/modules/ObjectModule.cpp @@ -21,6 +21,7 @@ #include #include #include +#include #include @@ -430,7 +431,7 @@ bool game_vehicle_stopped(const ScriptArguments& args) void game_make_object_important(const ScriptArguments& args) { auto inst = (InstanceObject*)(*args[0].handle); - args.getVM()->getWorld()->logger.warning("SCM", "Object pinning unimplemented!"); + args.getVM()->getWorld()->logger->warning("SCM", "Object pinning unimplemented!"); } bool game_character_in_area_on_foot(const ScriptArguments& args) @@ -777,7 +778,7 @@ void game_create_object_world(const ScriptArguments& args) auto& modelname = args.getVM()->getFile()->getModels()[-id]; id = args.getVM()->getWorld()->findModelDefinition(modelname); if( id == (uint16_t)-1 ) { - args.getVM()->getWorld()->logger.error("SCM", "Failed to find model " + modelname); + args.getVM()->getWorld()->logger->error("SCM", "Failed to find model " + modelname); } } diff --git a/rwgame/RWGame.cpp b/rwgame/RWGame.cpp index 88982b74..477b68cf 100644 --- a/rwgame/RWGame.cpp +++ b/rwgame/RWGame.cpp @@ -64,25 +64,33 @@ RWGame::RWGame(const std::string& gamepath, int argc, char* argv[]) glewExperimental = GL_TRUE; glewInit(); + + log.addReciever(&logPrinter); + log.info("Game", "Game directory: " + gamepath); + + if(! GameData::isValidGameDirectory(gamepath) ) + { + std::string envname(ENV_GAME_PATH_NAME); + throw std::runtime_error("Invalid game directory path, is " +envname+ " set?"); + } - engine = new GameWorld(gamepath); - engine->logger.addReciever(&logPrinter); + engine = new GameWorld(&log, gamepath); // Initalize all the archives. engine->gameData.loadIMG("/models/gta3"); //engine->gameData.loadIMG("/models/txd"); engine->gameData.loadIMG("/anim/cuts"); + engine->gameData.load(); + // Initialize renderer - renderer = new GameRenderer(engine); + renderer = new GameRenderer(&log, engine); // Set up text renderer renderer->text.setFontTexture(0, "pager"); renderer->text.setFontTexture(1, "font1"); renderer->text.setFontTexture(2, "font2"); - /// @TODO expand this here. - engine->load(); debug = new DebugDraw; debug->setDebugMode(btIDebugDraw::DBG_DrawWireframe | btIDebugDraw::DBG_DrawConstraints | btIDebugDraw::DBG_DrawConstraintLimits); engine->dynamicsWorld->setDebugDrawer(debug); @@ -113,7 +121,7 @@ RWGame::RWGame(const std::string& gamepath, int argc, char* argv[]) StateManager::get().enter(loading); - engine->logger.info("Game", "Started"); + log.info("Game", "Started"); } RWGame::~RWGame() @@ -244,7 +252,7 @@ void RWGame::tick(float dt) } catch( SCMException& ex ) { std::cerr << ex.what() << std::endl; - engine->logger.error( "Script", ex.what() ); + log.error( "Script", ex.what() ); throw; } } diff --git a/rwgame/RWGame.hpp b/rwgame/RWGame.hpp index d690fb90..4a459d56 100644 --- a/rwgame/RWGame.hpp +++ b/rwgame/RWGame.hpp @@ -1,5 +1,7 @@ #ifndef _RWGAME_HPP_ #define _RWGAME_HPP_ + +#include #include #include #include "game.hpp" @@ -8,6 +10,7 @@ class RWGame { + Logger log; GameWorld* engine; // must be allocated after Logger setup. GameRenderer* renderer; diff --git a/tests/test_globals.hpp b/tests/test_globals.hpp index fa949726..d258888c 100644 --- a/tests/test_globals.hpp +++ b/tests/test_globals.hpp @@ -3,6 +3,7 @@ #include #include +#include #include #define ENV_GAME_PATH_NAME ("OPENRW_GAME_PATH") @@ -39,11 +40,12 @@ public: wnd.create(sf::VideoMode(640, 360), "Testing"); glewExperimental = GL_TRUE; glewInit(); - e = new GameWorld(getGamePath()); + Logger log; + e = new GameWorld(&log, getGamePath()); e->gameData.loadIMG("/models/gta3"); e->gameData.loadIMG("/anim/cuts"); - e->load(); + e->gameData.load(); for(std::map::iterator it = e->gameData.ideLocations.begin(); it != e->gameData.ideLocations.end(); ++it) {