/* orxonox - the future of 3D-vertical-scrollers Copyright (C) 2004 orx This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2, or (at your option) any later version. ### File Specific: main-programmer: Patrick Boenzli co-programmer: ... */ #include "game_loader.h" #include "shell_command.h" #include "campaign.h" #include "world.h" #include "orxonox.h" #include "camera.h" #include "vector.h" #include "resource_manager.h" #include "factory.h" #include "event.h" #include "event_handler.h" #include using namespace std; SHELL_COMMAND(quit, GameLoader, stop) ->describe("quits the game") ->setAlias("orxoquit"); GameLoader* GameLoader::singletonRef = NULL; /** * simple constructor */ GameLoader::GameLoader () { this->setClassID(CL_GAME_LOADER, "GameLoader"); this->setName("GameLoader"); } /** * simple deconstructor */ GameLoader::~GameLoader () { if( this->currentCampaign) delete this->currentCampaign; this->currentCampaign = NULL; } /** * this class is a singleton class * @returns an instance of itself if you are unsure about singleton classes, check the theory out on the internet :) */ GameLoader* GameLoader::getInstance() { if(singletonRef == NULL) singletonRef = new GameLoader(); return singletonRef; } /** * initializes the GameLoader */ ErrorMessage GameLoader::init() { if(this->currentCampaign != NULL) this->currentCampaign->init(); this->eventHandler = EventHandler::getInstance(); this->eventHandler->subscribe(this, ES_GAME, KeyMapper::PEV_PAUSE); this->eventHandler->subscribe(this, ES_ALL, KeyMapper::PEV_QUIT); this->eventHandler->subscribe(this, ES_GAME, KeyMapper::PEV_NEXT_WORLD); this->eventHandler->subscribe(this, ES_GAME, KeyMapper::PEV_PREVIOUS_WORLD); } /** * reads a campaign definition file into a campaign class * @param fileName to be loaded * @returns the loaded campaign this will interprete the map/campaign files and recursivly load a tree of worlds/campaigns */ ErrorMessage GameLoader::loadCampaign(const char* fileName) { ErrorMessage errorCode; char* campaignName = ResourceManager::getFullName(fileName); if (campaignName) { this->currentCampaign = this->fileToCampaign(campaignName); delete campaignName; } // World* world0 = new World(DEBUG_WORLD_0); // world0->setNextStoryID(WORLD_ID_GAMEEND); // this->currentCampaign->addEntity(world0, WORLD_ID_2); } /** * loads a debug campaign for test purposes only. * @param campaignID the identifier of the campaign. * @returns error message if not able to do so. */ ErrorMessage GameLoader::loadDebugCampaign(Uint32 campaignID) { switch(campaignID) { /* Debug Level 0: Debug level used to test the base frame work. As you can see, all storyentity data is allocated before game start. the storyentity will load themselfs shortly before start through the StoryEntity::init() funtion. */ case DEBUG_CAMPAIGN_0: { Campaign* debugCampaign = new Campaign(); World* world0 = new World(DEBUG_WORLD_0); world0->setNextStoryID(WORLD_ID_1); debugCampaign->addEntity(world0, WORLD_ID_0); World* world1 = new World(DEBUG_WORLD_1); world1->setNextStoryID(WORLD_ID_2); debugCampaign->addEntity(world1, WORLD_ID_1); World* world2 = new World(DEBUG_WORLD_2); world2->setNextStoryID(WORLD_ID_GAMEEND); debugCampaign->addEntity(world2, WORLD_ID_2); this->currentCampaign = debugCampaign; break; } } } /** * starts the current entity * @returns error code if this action has caused a error */ ErrorMessage GameLoader::start() { if(this->currentCampaign != NULL) this->currentCampaign->start(); } /** * stops the current entity * @returns error code if this action has caused a error ATTENTION: this function shouldn't call other functions, or if so, they must return after finishing. If you ignore or forget to do so, the current entity is not able to terminate and it will run in the background or the ressources can't be freed or even worse: are freed and the program will end in a segmentation fault! hehehe, have ya seen it... :) */ void GameLoader::stop() { if(this->currentCampaign != NULL) this->currentCampaign->stop(); } /** * pause the current entity * @returns error code if this action has caused a error this pauses the current entity or passes this call forth to the running entity. */ ErrorMessage GameLoader::pause() { this->isPaused = true; if(this->currentCampaign != NULL) this->currentCampaign->pause(); } /** * resumes a pause * @returns error code if this action has caused a error this resumess the current entity or passes this call forth to the running entity. */ ErrorMessage GameLoader::resume() { this->isPaused = false; if(this->currentCampaign != NULL) this->currentCampaign->resume(); } /** * release the mem ATTENTION: not implemented */ ErrorMessage GameLoader::destroy() { } /** * reads a campaign definition file into a campaign class * @param fileName to be loaded * @returns the loaded campaign this will interprete the map/campaign files and recursivly load a tree of worlds/campaigns */ Campaign* GameLoader::fileToCampaign(const char* fileName) { /* do not entirely load the campaign. just the current world before start of each world, it has to be initialized so it can load everything it needs into memory then. */ if( fileName == NULL) { PRINTF(2)("No filename specified for loading"); return NULL; } TiXmlDocument* XMLDoc = new TiXmlDocument( fileName); // load the campaign document if( !XMLDoc->LoadFile()) { // report an error PRINTF(1)("Could not load XML File %s: %s @ %d:%d\n", fileName, XMLDoc->ErrorDesc(), XMLDoc->ErrorRow(), XMLDoc->ErrorCol()); delete XMLDoc; return NULL; } // check basic validity TiXmlElement* root = XMLDoc->RootElement(); assert( root != NULL); if( strcmp( root->Value(), "Campaign")) { // report an error PRINTF(2)("Specified XML File is not an orxonox campaign file (Campaign element missing)\n"); delete XMLDoc; return NULL; } // construct campaign Campaign* c = new Campaign( root); // free the XML data delete XMLDoc; return c; } /** * handle keyboard commands * @param event the event to handle */ void GameLoader::process(const Event& event) { if( event.type == KeyMapper::PEV_NEXT_WORLD) { if( likely(event.bPressed)) { this->nextLevel(); } } else if( event.type == KeyMapper::PEV_PREVIOUS_WORLD) { if( likely(event.bPressed)) { this->previousLevel(); } } else if( event.type == KeyMapper::PEV_PAUSE) { if( likely(event.bPressed)) { if(this->isPaused) this->resume(); else this->pause(); } } else if( event.type == KeyMapper::PEV_QUIT) { if( event.bPressed) this->stop(); } } /** \brief this changes to the next level */ void GameLoader::nextLevel() { if(this->currentCampaign != NULL) this->currentCampaign->nextLevel(); } /** \brief change to the previous level - not implemented this propably useless */ void GameLoader::previousLevel() { if(this->currentCampaign != NULL) this->currentCampaign->previousLevel(); } /** * load a StoryEntity * @param element a XMLElement containing all the needed info */ BaseObject* GameLoader::fabricate(const TiXmlElement* element) { assert( element != NULL); if( Factory::getFirst() == NULL) { PRINTF(1)("GameLoader does not know any factories, fabricate() failed\n"); return NULL; } if( element->Value() != NULL) { PRINTF(4)("Attempting fabrication of a '%s'\n", element->Value()); BaseObject* b = Factory::getFirst()->fabricate( element); if( b == NULL) PRINTF(2)("Failed to fabricate a '%s'\n", element->Value()); else PRINTF(4)("Successfully fabricated a '%s'\n", element->Value()); return b; } PRINTF(2)("Fabricate failed, TiXmlElement did not contain a value\n"); return NULL; }