/*

--------
|SKRUZD|
--------

The MIT License

Copyright (c) 2006 Inko Illarramendi

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/

#include <crystalspace.h>

#include <celtool/initapp.h>
#include <propclass/zone.h>
#include <propclass/camera.h>
#include <propclass/mesh.h>
#include <propclass/linmove.h>
#include <propclass/actormove.h>
#include <propclass/input.h>
#include <physicallayer/propclas.h>
#include <propclass/tooltip.h>
#include <propclass/solid.h>
#include <propclass/spawn.h>
#include <propclass/region.h>
#include <propclass/timer.h>
#include <propclass/mover.h>
#include <propclass/prop.h>

#include "ivideo/fontserv.h"

#include "app.h"
#include "behave.h"

MainApp::MainApp ()
{
    SetApplicationName ("SKRUZD");
}

MainApp::~MainApp ()
{}

bool MainApp::LoadLoadingMessage()
{
    if (g2d)
    {
        iFontServer* fs = g2d->GetFontServer ();
        csRef<iFont> font = fs->LoadFont (CSFONT_LARGE);
        csString text ("Loading...");
        int text_width, text_height;
        const char * texts= text.GetData();
        font->GetDimensions (text.GetData(), text_width, text_height);
        int width = g2d->GetWidth ();
        int height = g2d->GetHeight ();
        g2d->BeginDraw ();
        g2d->ClearAll (g2d->FindRGB (0, 0, 0));
        g2d->Write (font, (int)(width/2 - text_width/2),
                    (int)(height/2 - text_height/2), g2d->FindRGB (255, 255, 255), -1,
                    text.GetData ());
        g2d->FinishDraw ();
        g2d->Print (0);
    }
}

bool MainApp::LoadLevel ()
{
    ///Here we create the entity for the level

    level_entity = pl->CreateEntity ("level", bl, "level_behave",
                                     "pczonemanager",
                                     "pcregion",
                                     "pcproperties", //to save the score
                                     "pctooltip", //to print the score
                                     "pctimer",
                                     0);
    if (!level_entity)
        return ReportError ("Error creating level entity!");

    //  Now get the iPcZoneManager interface so we can setup the level.
    csRef<iPcZoneManager> zonemgr = CEL_QUERY_PROPCLASS_ENT (level_entity,
                                    iPcZoneManager);
    //Create the Zone and the Region
    iCelZone* zone = zonemgr->CreateZone ("main");
    iCelRegion* region = zonemgr->CreateRegion ("main");
    zone->LinkRegion (region);

    // Especify the map path and file
    iCelMapFile* mapfile = region->CreateMapFile ();
    mapfile->SetPath ("/this/level");
    mapfile->SetFile ("world");

    //Get the region property and set the map to it
    csRef<iPcRegion> pcregion = CEL_QUERY_PROPCLASS_ENT (level_entity,
                                iPcRegion);
    pcregion-> SetWorldFile ("/this/level", "world");
    pcregion-> SetRegionName ("main");

    // Get the properties property to set the score
    csRef<iPcProperties> pcproperties = CEL_QUERY_PROPCLASS_ENT (
                                            level_entity, iPcProperties);
    long int score=0;
    status=2;
    pcproperties -> SetProperty ("score", score);
    pcproperties -> SetProperty ("status",status );

    // Get the tooltip property to print the score
    csRef<iPcTooltip> pctooltip = CEL_QUERY_PROPCLASS_ENT (
                                      level_entity, iPcTooltip);
    char scorec[3];
    sprintf(scorec,"KILLS %d", score);
    pctooltip->SetBackgroundColor (-1, -1, -1);
    pctooltip->SetTextColor (0,0,0);
    pctooltip->SetText(scorec);
    pctooltip->Show(50,50);

    return true;
}


bool MainApp::LoadMenu ()
{
    ///Here we create the entity for the menu "level"

    level_entity = pl->CreateEntity ("menu", bl, "level_behave",
                                     "pczonemanager",
                                     "pcregion",
                                     "pcdefaultcamera", //camera
                                     "pcproperties", //to save the score
                                     "pctimer",
                                     0);
    if (!level_entity)
        return ReportError ("Error creating level entity!");

    //Get the camera
    csRef<iPcCamera> pccamera = CEL_QUERY_PROPCLASS_ENT (
                                    level_entity, iPcCamera);

    //  Now get the iPcZoneManager interface so we can setup the level.
    csRef<iPcZoneManager> zonemgr = CEL_QUERY_PROPCLASS_ENT (level_entity,
                                    iPcZoneManager);
    //Create the Zone and the Region
    iCelZone* zone = zonemgr->CreateZone ("main");
    iCelRegion* region = zonemgr->CreateRegion ("main");
    zone->LinkRegion (region);

    // Especify the map path and file
    iCelMapFile* mapfile = region->CreateMapFile ();
    mapfile->SetPath ("/this/level");
    mapfile->SetFile ("startmenu");

    //Get the region property and set the map to it
    csRef<iPcRegion> pcregion = CEL_QUERY_PROPCLASS_ENT (level_entity,
                                iPcRegion);
    pcregion-> SetWorldFile ("/this/level", "startmenu");
    pcregion-> SetRegionName ("main");

    //Set starting point
    pccamera->SetZoneManager (zonemgr, true, "main", "Camera");
    //Set the starting point of player and camera
    if (zonemgr->PointMesh ("menu", "main", "Camera"))
        return ReportError ("Can't find region or start position in region!");

    //Get the properties to set the status
    csRef<iPcProperties> pcproperties = CEL_QUERY_PROPCLASS_ENT (
                                            level_entity, iPcProperties);
    status=1; //menu status
    pcproperties -> SetProperty ("status",status );

    return true;
}


bool MainApp::CreatePlayer ()
{
    ///Here the player entity is created

    player_entity = pl->CreateEntity ("player",
                                      bl, "player_behave", //behaviour
                                      "pcdefaultcamera", //camera
                                      "pcmesh",
                                      "pclinearmovement",
                                      "pcactormove", //to control the movement
                                      "pccommandinput", //to read keyboard input
                                      "pctooltip", //to print the lives
                                      "pctimer", //to check collision every frame
                                      "pcproperties", //to store the lives
                                      0);
    if (!player_entity)
        return ReportError ("Error creating player entity!");

    // Get the iPcCamera interface so that we can set the camera.
    csRef<iPcCamera> pccamera = CEL_QUERY_PROPCLASS_ENT (
                                    player_entity, iPcCamera);

    // Get the zone manager from the level entity.
    csRef<iPcZoneManager> pczonemgr = CEL_QUERY_PROPCLASS_ENT (
                                          level_entity, iPcZoneManager);
    pccamera->SetZoneManager (pczonemgr, true, "main", "Camera");

    // Get the iPcMesh interface so we can load the right mesh
    csRef<iPcMesh> pcmesh = CEL_QUERY_PROPCLASS_ENT (
                                player_entity, iPcMesh);
    pcmesh->SetPath ("/this/model");
    pcmesh->SetMesh ("skruzd", "skruzd.cfg");
    if (!pcmesh->GetMesh ())
        return ReportError ("Error loading model!");

    //Set the starting point of player and camera
    if (pczonemgr->PointMesh ("player", "main", "Camera"))
        return ReportError ("Can't find region or start position in region!");

    // Get iPcLinearMovement so we can setup the movement system.
    csRef<iPcLinearMovement> pclinmove = CEL_QUERY_PROPCLASS_ENT (
                                             player_entity, iPcLinearMovement);
    pclinmove->InitCD ( //Collision detection box, for gravity and map collision
        csVector3 (0.5,0.8,0.5),
        csVector3 (0.5,0.4,0.5),
        csVector3 (0,0,0));

    // Get the iPcActorMove interface so that we can set movement speed.
    csRef<iPcActorMove> pcactormove = CEL_QUERY_PROPCLASS_ENT (
                                          player_entity, iPcActorMove);
    pcactormove->SetMovementSpeed (3.0f);
    pcactormove->SetRunningSpeed (5.0f);
    pcactormove->SetRotationSpeed (1.75f);
    pcactormove->SetJumpingVelocity(10.0f);

    // Get iPcCommandInput so we can do key bindings. The behaviour
    // layer will interprete the commands so the actor can move.
    csRef<iPcCommandInput> pcinput = CEL_QUERY_PROPCLASS_ENT (
                                         player_entity, iPcCommandInput);
    pcinput->Bind ("up", "forward");
    pcinput->Bind ("down", "backward");
    pcinput->Bind ("left", "rotateleft");
    pcinput->Bind ("right", "rotateright");
    pcinput->Bind ("a", "cammode");
    pcinput->Bind ("s", "shot");
    pcinput->Bind ("d", "jump");

    //Get iPcProperties so we can store the lives.
    csRef<iPcProperties> pcproperties = CEL_QUERY_PROPCLASS_ENT (
                                            player_entity, iPcProperties);
    long int lives=2;
    pcproperties -> SetProperty ("lives", lives);

    //Get iPcTooltip so we can print the lives in the screen
    csRef<iPcTooltip> pctooltip = CEL_QUERY_PROPCLASS_ENT (
                                      player_entity, iPcTooltip);
    char livesc[1];
    sprintf(livesc, "LIVES %d", lives);
    pctooltip->SetBackgroundColor (-1, -1, -1); //transparent
    pctooltip->SetText(livesc);
    pctooltip->Show(50,25);

    //The procedure to create a collider box, to make solid animated characters
    csRef<iCollideSystem> cd_sys = csQueryRegistry<iCollideSystem> (object_reg);
    iMeshWrapper* mesh = pcmesh -> GetMesh();
    csBox3 box (-0.25, 0, -0.25, 0.25, 1.2, 0.25); //set the box
    csPolygonMeshBox* pmbox = new csPolygonMeshBox (box);
    //set the mesh with the collider box.
    csColliderWrapper* wrapper = new csColliderWrapper (
                                     mesh->QueryObject (), cd_sys, pmbox);
    pmbox->DecRef ();
    wrapper->DecRef ();

    //Get iPcTimer so we can put the timer to activate each frame
    csRef<iPcTimer> pctimer = CEL_QUERY_PROPCLASS_ENT (
                                  player_entity, iPcTimer);
    pctimer-> WakeUpFrame (0);

        //set animation stuff
    csRef<iMeshFactoryWrapper> imeshfact;
    csRef<iMeshWrapper> imesh;
    imesh = pcmesh -> GetMesh ();
    imeshfact = imesh-> GetFactory ();
    // Make it do something.
    csRef<iSpriteCal3DFactoryState> factstate(
        scfQueryInterface<iSpriteCal3DFactoryState> (
            imeshfact->GetMeshObjectFactory()));
    csRef<iSpriteCal3DState> cal3dstate(scfQueryInterface<iSpriteCal3DState> (
                                            imesh->GetMeshObject()));
    //Load "stand" animation
    cal3dstate->SetAnimCycle("stand", 1.0f);

    return true;
}


bool MainApp::CreateWeapon ()
{
    ///Here we create player's weapon entity

    csRef<iCelEntity> weapon_entity;

    weapon_entity = pl->CreateEntity ("weapon", bl, "weapon_behave",
                                      "pcmesh",
                                      "pclinearmovement",
                                      "pctimer",
                                      0);
    if (!weapon_entity)
        return ReportError("Error creating bad entity!");

    // Get the iPcMesh interface so we can load the right mesh for our weapon.
    csRef<iPcMesh> pcmesh = CEL_QUERY_PROPCLASS_ENT (
                                weapon_entity, iPcMesh);
    pcmesh->SetPath ("/this/model");
    pcmesh->SetMesh ("partSphere", "fire");
    if (!pcmesh->GetMesh ())
        return ReportError ("Error loading model!");

    // Get the iPcLinearMovement interface to set position and velocity
    csRef<iPcLinearMovement> pclinmove = CEL_QUERY_PROPCLASS_ENT (
                                             weapon_entity, iPcLinearMovement);
    iSector* room = engine->FindSector ("Scene");
    pclinmove-> SetPosition(
        csVector3 (0,-100,0), //initial position somewhere out of map
        0.0f,
        room);
    pclinmove-> SetVelocity (csVector3(0,0,-4));

    pcmesh-> Hide(); //make it invisible

    return true;

}


bool MainApp::CreateBad ()
{
    /// Here the spawner is created.

    csRef<iCelEntity> spawner_entity;

    spawner_entity = pl->CreateEntity ("spawner",
                                       bl, "spawn_behave", //Behaviour
                                       "pcmesh",
                                       "pclinearmovement",
                                       "pcspawn", //to create new enemies
                                       "pctimer", //to control respawning
                                       0);
    if (!spawner_entity)
        return ReportError("Error creating bad entity!");

    // Get the iPcMesh interface
    csRef<iPcMesh> pcmesh = CEL_QUERY_PROPCLASS_ENT (
                                spawner_entity, iPcMesh);
    pcmesh-> CreateEmptyThing (); //it is not a visual entity

    // Get iPcSpawn interface to set new enemies
    csRef<iPcSpawn> pcspawn = CEL_QUERY_PROPCLASS_ENT (
                                  spawner_entity, iPcSpawn);
    pcspawn-> AddEntityType   (    100,
                                   "spawn",
                                   bl, "bad_behave", //Behaviour
                                   0,
                                   0,
                                   "pcmesh");
    pcspawn-> SetTiming   (true,  //repeat
                           false,
                           3000,4000 //time interval to put new enemy on game
                          );

    pcspawn-> InhibitCount (15);

    // Set the timer to control the respawning
    csRef<iPcTimer> pctimer = CEL_QUERY_PROPCLASS_ENT (
                                  spawner_entity, iPcTimer);
    pctimer-> WakeUp(2000,true);

    return true;
}


bool MainApp::CreateMusic ()
{
    ///Here we create the background music loop

    sndrdr = CS_QUERY_REGISTRY (object_reg, iSndSysRenderer);
    if (sndrdr)
    {
        w_music = loader->LoadSoundWrapper (
                      "music", //we set the virtual name and the file path of the song
                      "/this/music/subatomicglue - globalenemy - 03 - ice overflow.ogg",
                      CS_SND3D_DISABLE);
        if (w_music)
        {
            sndsrc = sndrdr->CreateSource(w_music->GetStream());
            sndsrc -> SetVolume (0.25f);
            w_music->GetStream ()->SetLoopState (CS_SNDSYS_STREAM_LOOP);
            w_music->GetStream ()->Unpause();
        }
    }

    return true;
}


void MainApp::PreProcessFrame ()
{

    if (level_entity)
    {
        csRef<iPcProperties> level_pcproperties = CEL_QUERY_PROPCLASS_ENT (
                    level_entity, iPcProperties);

		//get the status
        status = level_pcproperties->
                 GetPropertyLong (level_pcproperties-> GetPropertyIndex ("status"));


        if (status==0)
        {//the status requires the level, check if we are in the menu
            printf("\nExiting from game\n");
            csRef<iEventQueue> q =
                CS_QUERY_REGISTRY(GetObjectRegistry(), iEventQueue);
            if (q.IsValid())
                q->GetEventOutlet()->Broadcast(
                    csevQuit(GetObjectRegistry()));

        }
        else if (status==1)
        {//the status requires the menu, check if we are in the game level
            if ((!strcmp (level_entity -> GetName (), "level")))
            {//change from game level to menu level
                long int score = level_pcproperties->
                                 GetPropertyLong (level_pcproperties-> GetPropertyIndex ("score"));

                printf("\nchange from level to menu\n");
                printf("\nscore= %d\n", score);

                level_pcproperties=0;

                if (sndrdr)
                {//stop the sound system
                    w_music->GetStream ()->Pause();
                    sndrdr->RemoveSource (sndsrc);
                    sndrdr->RemoveStream (w_music->GetStream ());
                    sndsrc = 0;
                    sndrdr=0;
                    w_music=0;
                }

				//remove the entities
                pl-> RemoveEntities();

                if (player_entity)
                {
                    pl->RemoveEntity (player_entity);
                    player_entity = 0;
                }

                if (level_entity)
                {
                    pl-> RemoveEntity(level_entity);
                    level_entity=0;

                }

                if (!LoadLoadingMessage())
                    ReportError ("Error loading 'loading...' message!");

                if (!LoadMenu ())
                    ReportError ("Error loading Menu!");

            }

        }
        else if (status==2)
        {//the status requires the game level, check if we are in the menu level
            if ((!strcmp (level_entity -> GetName (), "menu")))
            {//
                printf("\nchange from menu to level\n");

                level_pcproperties=0;

				//remove everything and start the game level
                if (sndrdr)
                {//stop the sound system
                    w_music->GetStream ()->Pause();
                    sndrdr->RemoveSource (sndsrc);
                    sndrdr->RemoveStream (w_music->GetStream ());
                    sndsrc = 0;
                    sndrdr=0;
                    w_music=0;
                }

                pl->RemoveEntity (player_entity);
                player_entity = 0;
                pl-> RemoveEntities();
                pl-> RemoveEntity(level_entity);
                level_entity=0;

                if (!LoadLoadingMessage())
                    ReportError ("Error loading 'loading...' message!");

                LoadLevel ();

                CreatePlayer ();

                CreateWeapon();

                CreateBad ();

                CreateMusic ();

            }
        }
    }

    if (player_entity)
    {
        csRef<iPcProperties> player_pcproperties = CEL_QUERY_PROPCLASS_ENT (
                    player_entity, iPcProperties);

        long int  lives = player_pcproperties->
                          GetPropertyLong (player_pcproperties-> GetPropertyIndex ("lives"));
		//if player's lives are below 0 the game is finished, so exit to menu.
        if (lives<0)
        {
            player_pcproperties=0;
            status=1;
            csRef<iPcProperties> level_pcproperties = CEL_QUERY_PROPCLASS_ENT (
                        level_entity, iPcProperties);
            level_pcproperties-> SetProperty ("status", status);
        }
    }
}

void MainApp::FinishFrame ()
{
    // Just tell the 3D renderer that everything has been rendered.
    g3d->FinishDraw ();
    g3d->Print (0);
}

bool MainApp::OnKeyboard(iEvent& ev)
{
    ///Here we will check if the player pressed ESC key.

    // We got a keyboard event.
    csKeyEventType eventtype = csKeyEventHelper::GetEventType(&ev);
    if (eventtype == csKeyEventTypeDown)
    {
        // The user pressed a key (as opposed to releasing it).
        utf32_char code = csKeyEventHelper::GetCookedCode(&ev);
        if (code == CSKEY_ESC)
        {
            // The user pressed escape to exit the application.
            // The proper way to quit a Crystal Space application
            // is by broadcasting a csevQuit event. That will cause the
            // main runloop to stop. To do that we get the event queue from
            // the object registry and then post the event.

            if (status==2)
            {
                status=1;
                csRef<iPcProperties> level_pcproperties = CEL_QUERY_PROPCLASS_ENT (
                            level_entity, iPcProperties);
                level_pcproperties-> SetProperty ("status", status);
            }
            else if (status==1)
            {
                status=0;
                csRef<iPcProperties> level_pcproperties = CEL_QUERY_PROPCLASS_ENT (
                            level_entity, iPcProperties);
                level_pcproperties-> SetProperty ("status", status);
            }

        }
    }

    return false;
}


bool MainApp::OnInitialize (int argc, char* argv[])
{
    ///Here we will request for plugins
    if (!celInitializer::RequestPlugins (object_reg,
                                         CS_REQUEST_VFS,
                                         CS_REQUEST_OPENGL3D,
                                         CS_REQUEST_ENGINE,
                                         CS_REQUEST_FONTSERVER,
                                         CS_REQUEST_IMAGELOADER,
                                         CS_REQUEST_LEVELLOADER,
                                         CS_REQUEST_REPORTER,
                                         CS_REQUEST_REPORTERLISTENER,
                                         CS_REQUEST_PLUGIN ("cel.physicallayer", iCelPlLayer),
                                         CS_REQUEST_PLUGIN ("crystalspace.collisiondetection.opcode",
                                                            iCollideSystem),

                                         ///SOUND PLUGINS
                                         CS_REQUEST_PLUGIN("crystalspace.sndsys.renderer.software",
                                                           iSndSysRenderer),
                                         CS_REQUEST_PLUGIN("crystalspace.sndsys.element.loader",
                                                           iSndSysLoader),

                                         ///MENU PLUGINS
                                         CS_REQUEST_PLUGIN ("crystalspace.documentsystem.xmlread",
                                                            iDocumentSystem),
                                         CS_REQUEST_PLUGIN ("cel.persistence.xml", iCelPersistence),

                                         CS_REQUEST_END))

        return ReportError ("Can't initialize plugins!");

    csBaseEventHandler::Initialize(object_reg);

    if (!RegisterQueue(object_reg, csevAllEvents(object_reg)))
        return ReportError ("Can't setup event handler!");

    return true;
}


bool MainApp::Application ()
{
    ///We will initialize the application, creating the entities.

    if (!OpenApplication (object_reg))
        return ReportError ("Error opening system!");

    g3d = CS_QUERY_REGISTRY (object_reg, iGraphics3D);
    engine = CS_QUERY_REGISTRY (object_reg, iEngine);
    loader = CS_QUERY_REGISTRY (object_reg, iLoader);
    vfs = CS_QUERY_REGISTRY (object_reg, iVFS);
    vc = CS_QUERY_REGISTRY (object_reg, iVirtualClock);
    kbd = CS_QUERY_REGISTRY (object_reg, iKeyboardDriver);
    g2d = CS_QUERY_REGISTRY (object_reg, iGraphics2D);

    pl = CS_QUERY_REGISTRY (object_reg, iCelPlLayer);
    bl.AttachNew (new BehaviourLayer(pl));

    // We also need to register it to the object registry.
    if (!object_reg->Register (bl, "iCelBlLayer"))
        return ReportError ("Can't register our behaviour layer!");

    // Make sure the application dir is mounted at /cel
    vfs->Mount ("cel", "$.$/");

    pl->RegisterBehaviourLayer (bl);

    //Menu behaviour
    plugin_mgr = CS_QUERY_REGISTRY (object_reg, iPluginManager);
    csRef<iCelBlLayer> blxml = CS_LOAD_PLUGIN (plugin_mgr,
                               "cel.behaviourlayer.xml", iCelBlLayer);
    if (!blxml)
        return ReportError ("Can't load XML behaviour layer!");
    object_reg ->Register (blxml, "blxml");
    pl->RegisterBehaviourLayer (blxml);

    //Load the factories for the property classes
    if (!pl->LoadPropertyClassFactory ("cel.pcfactory.zonemanager"))
        return ReportError ("Error loading pczonemanager factory!");
    if (!pl->LoadPropertyClassFactory ("cel.pcfactory.solid"))
        return ReportError ("Error loading pcsolid factory!");
    if (!pl->LoadPropertyClassFactory ("cel.pcfactory.colldet"))
        return ReportError ("Error loading pccolldet factory!");
    if (!pl->LoadPropertyClassFactory ("cel.pcfactory.defaultcamera"))
        return ReportError ("Error loading pcdefaultcamera factory!");
    if (!pl->LoadPropertyClassFactory ("cel.pcfactory.mesh"))
        return ReportError ("Error loading pcmesh factory!");
    if (!pl->LoadPropertyClassFactory ("cel.pcfactory.linmove"))
        return ReportError ("Error loading pclinmove factory!");
    if (!pl->LoadPropertyClassFactory ("cel.pcfactory.pccommandinput"))
        return ReportError ("Error loading pccommandinput factory!");
    if (!pl->LoadPropertyClassFactory ("cel.pcfactory.actormove"))
        return ReportError ("Error loading pcactormove factory!");
    if (!pl->LoadPropertyClassFactory ("cel.pcfactory.properties"))
        return ReportError ("Error loading pcproperties factory!");
    if (!pl->LoadPropertyClassFactory ("cel.pcfactory.timer"))
        return ReportError ("Error loading pctimer factory!");
    if (!pl->LoadPropertyClassFactory ("cel.pcfactory.spawn"))
        return ReportError ("Error loading pcmechobject factory!");
    if (!pl->LoadPropertyClassFactory ("cel.pcfactory.region"))
        return ReportError ("Error loading pcregion factory!");
    if (!pl->LoadPropertyClassFactory ("cel.pcfactory.tooltip"))
        return ReportError ("Error loading pctooltip factory!");
    if (!pl->LoadPropertyClassFactory ("cel.pcfactory.billboard"))
        return ReportError ("Error loading pctooltip factory!");


    //Load the "Loading..." message
    if (!LoadLoadingMessage())
        return ReportError ("Error loading 'loading...' message!");

    //Load the menu
    if (!LoadMenu ())
        return ReportError ("Error loading level!");

    //Call the main loop to start the application.
    Run ();

    return true;
}
