File DialogBuild.cpp         CEGUI build dialog - implementation

DialogBuild.cpp

#include "stdafx.h"
#include "DialogBuild.h"
#include "InputHandler.h"
#include "PlayState.h"
#include "Common/StaticEntity.h"
#include "Common/EntityMgr.h"

// these must match the IDs assigned in the layout
const unsigned int DialogBuild::buttonCancelID     = 1;
const unsigned int DialogBuild::buttonStructuresID = 21;
const unsigned int DialogBuild::buttonBioUnitsID   = 22;
const unsigned int DialogBuild::buttonMechsID      = 23;
const unsigned int DialogBuild::buttonVehiclesID   = 24;
const unsigned int DialogBuild::scrollBarID        = 2;
const unsigned int DialogBuild::buttonID[DIALOG_BUTTON_COUNT]      = { 3, 5, 7, 9, 11, 13 };
const unsigned int DialogBuild::buttonLabelID[DIALOG_BUTTON_COUNT] = { 4, 6, 8, 10, 12, 14 };
const unsigned int DialogBuild::buttonTextID[DIALOG_BUTTON_COUNT]  = { 15, 16, 17, 18, 19, 20 };

// Size of each Render-To-Texture texture, in pixels
#define RTT_WINDOW_SIZE    128

// World height at which to place each Entity
#define OBJECT_WORLD_POS_Y    (-500.0)

// Type of Entity to be displayed when first opening the dialog box
#define INITIAL_ITEM_TYPE     ItemType_Building

DialogBuild::DialogBuild() : rootWindow(CEGUI::WindowManager::getSingleton().loadWindowLayout("DialogBuild.layout", "Build"))
   {
   using namespace CEGUI;

   Window* parent;

   itemTypeToDisplay = INITIAL_ITEM_TYPE;
   scrollOffset = 0;
   scrollbar = (Scrollbar *)rootWindow->getChild(scrollBarID);

   // we will destroy the DialogBuild box windows ourselves
   rootWindow->setDestroyedByParent(false);

   // Do events wire-up
   scrollbar->subscribeEvent(Scrollbar::EventScrollPositionChanged, Event::Subscriber(&DialogBuild::handleScrollChanged, this));
   rootWindow->subscribeEvent(Window::EventKeyDown, Event::Subscriber(&DialogBuild::HandleKeyDownEvents, this));

   rootWindow->getChild(buttonCancelID)->subscribeEvent(PushButton::EventClicked, Event::Subscriber(&DialogBuild::handleButtonCancel, this));

   rootWindow->getChild(buttonStructuresID)->subscribeEvent(PushButton::EventClicked, Event::Subscriber(&DialogBuild::handleButtonStructures, this));
   rootWindow->getChild(buttonBioUnitsID)->subscribeEvent(PushButton::EventClicked, Event::Subscriber(&DialogBuild::handleButtonBioUnits, this));
   rootWindow->getChild(buttonMechsID)->subscribeEvent(PushButton::EventClicked, Event::Subscriber(&DialogBuild::handleButtonMechs, this));
   rootWindow->getChild(buttonVehiclesID)->subscribeEvent(PushButton::EventClicked, Event::Subscriber(&DialogBuild::handleButtonVehicles, this));

   rootWindow->getChild(buttonID[0])->subscribeEvent(PushButton::EventClicked, Event::Subscriber(&DialogBuild::handleButton00, this));
   rootWindow->getChild(buttonID[1])->subscribeEvent(PushButton::EventClicked, Event::Subscriber(&DialogBuild::handleButton01, this));
   rootWindow->getChild(buttonID[2])->subscribeEvent(PushButton::EventClicked, Event::Subscriber(&DialogBuild::handleButton02, this));
   rootWindow->getChild(buttonID[3])->subscribeEvent(PushButton::EventClicked, Event::Subscriber(&DialogBuild::handleButton03, this));
   rootWindow->getChild(buttonID[4])->subscribeEvent(PushButton::EventClicked, Event::Subscriber(&DialogBuild::handleButton04, this));
   rootWindow->getChild(buttonID[5])->subscribeEvent(PushButton::EventClicked, Event::Subscriber(&DialogBuild::handleButton05, this));

   int            buttonIndex;
   Ogre::String   cameraName;
   Ogre::String   texName;
   Ogre::String   matName;
   Ogre::String   lightName;
   CEGUI::String  imageSetName;

   for (buttonIndex = 0; buttonIndex < DIALOG_BUTTON_COUNT; buttonIndex++)
      {
      displayedEntity[buttonIndex] = NULL;

      // Create Render-To-Texture texture
      texName = "RttTex_" + StringConverter::toString(buttonIndex);
      rttTex[buttonIndex] = global->root->getRenderSystem()->createRenderTexture(texName, RTT_WINDOW_SIZE, RTT_WINDOW_SIZE, TEX_TYPE_2D, PF_R8G8B8);
      rttTex[buttonIndex]->setActive(false);

      cameraName = "ButtonCam_" + StringConverter::toString(buttonIndex);
      rttCam[buttonIndex] = global->sceneMgr->createCamera(cameraName);
      rttCam[buttonIndex]->setAspectRatio((Real)global->renderWindow->getViewport(0)->getActualWidth() / (Real)global->renderWindow->getViewport(0)->getActualHeight());

      rttNode[buttonIndex] = global->sceneMgr->getRootSceneNode()->createChildSceneNode(cameraName);
      rttNode[buttonIndex]->setFixedYawAxis(TRUE);

      matName = "RttMat_" + StringConverter::toString(buttonIndex);
      MaterialPtr material = MaterialManager::getSingleton().create(matName, ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME);
      material->getTechnique(0)->getPass(0)->setDepthCheckEnabled(false);
      material->getTechnique(0)->getPass(0)->setDepthWriteEnabled(false); 

      rttTex[buttonIndex]->addListener(this);

      Viewport *v = rttTex[buttonIndex]->addViewport(rttCam[buttonIndex]);
      v->setClearEveryFrame(true);
      v->setBackgroundColour(ColourValue(0.1, 0.2, 0.7));
      v->setOverlaysEnabled(false);

      // Set the CEGUI Button's image to our new RTT
      Ogre::TexturePtr ogreTex = Ogre::TextureManager::getSingletonPtr()->getByName(texName);
      OgreCEGUITexture* ceguiTex = (OgreCEGUITexture*)global->guiRenderer->createTexture();
      ceguiTex->setOgreTexture(ogreTex);
      imageSetName = "ButtonTextureImageset_" + StringConverter::toString(buttonIndex);
      Imageset* textureImageSet = ImagesetManager::getSingleton().createImageset(imageSetName, ceguiTex);
      textureImageSet->defineImage("RttTex", Point(0.0f, 0.0f), Size(ceguiTex->getWidth(), ceguiTex->getHeight()), Point(0.0f,0.0f));
      imageSetName = "set:ButtonTextureImageset_" + StringConverter::toString(buttonIndex) + " image:RttTex";
      rootWindow->getChild(buttonID[buttonIndex])->setProperty("NormalImage", imageSetName);
      rootWindow->getChild(buttonID[buttonIndex])->setProperty("HoverImage", imageSetName);
      rootWindow->getChild(buttonID[buttonIndex])->setProperty("PushedImage", imageSetName);

      // Create a spotlight for each Entity
      lightName = "RttLight_" + StringConverter::toString(buttonIndex);
      spotLight[buttonIndex] = global->sceneMgr->createLight(lightName);
      spotLight[buttonIndex]->setType(Light::LT_SPOTLIGHT);
      spotLight[buttonIndex]->setDiffuseColour(0.93, 0.93, 0.93);
      spotLight[buttonIndex]->setSpecularColour(0.3, 0.3, 0.3); 
      spotLight[buttonIndex]->setAttenuation(5000.0, 1.0, 0.01, 0.0);
      spotLight[buttonIndex]->setSpotlightRange(Degree(30), Degree(50), 1.0);
      spotLight[buttonIndex]->setVisible(false);
      }

   hideObjectsNode = global->sceneMgr->getRootSceneNode()->createChildSceneNode("HiddenButtonEntityNode");
   hideObjectsNode->setVisible(false);

   // attach this window if parent is valid
   parent = System::getSingleton().getGUISheet();
   if (parent)
      parent->addChildWindow(rootWindow);

   rootWindow->hide();
   isShown = FALSE;
   }

DialogBuild::~DialogBuild()
   {
   // destroy the windows that we loaded earlier
   CEGUI::WindowManager::getSingleton().destroyWindow(rootWindow);
   }

bool DialogBuild::HandleKeyDownEvents(const CEGUI::EventArgs& args)
   {
   if (isShown)
      {
      SDLKey lastKeyHit = InputHandler::getInstance()->GetLastKeyHit();
      if (lastKeyHit == SDLK_ESCAPE)
         hide();
      return TRUE;
      }
   else
      {
      return FALSE;
      }
   }

void DialogBuild::FrameStarted(Real timeElapsed)
   {
   if (!isShown)
      return;

   int buttonIndex;

   // Spin each of the objects around their axis
   for (buttonIndex = 0; buttonIndex < DIALOG_BUTTON_COUNT; buttonIndex++)
      rttNode[buttonIndex]->yaw((Radian)(timeElapsed * 0.8));
   }

void DialogBuild::preRenderTargetUpdate(const RenderTargetEvent& evt)
   {
   if (isShown)
      {
#if USE_SEPERATE_ROOT_NODE
      global->rootNode->setVisible(false);
#endif

      global->sceneMgr->addSpecialCaseRenderQueue(RENDER_QUEUE_MAIN);
      global->sceneMgr->setSpecialCaseRenderQueueMode(SceneManager::SCRQM_INCLUDE);
      }
   }

void DialogBuild::postRenderTargetUpdate(const RenderTargetEvent& evt)
   {
   if (isShown)
      {
#if USE_SEPERATE_ROOT_NODE
      global->rootNode->setVisible(true);
#endif

      global->sceneMgr->clearSpecialCaseRenderQueues();
      global->sceneMgr->setSpecialCaseRenderQueueMode(SceneManager::SCRQM_EXCLUDE);
      }
   }

bool DialogBuild::handleScrollChanged(const CEGUI::EventArgs& args)
   {
   int newScrollOffset;

   // This event gets thrown when the scroll bar position moves even a little bit, 
   // and may not cause a change in the scrollOffset.
   // Only do an update if scrollOffset actually changes.
   newScrollOffset = (int)scrollbar->getScrollPosition() * DIALOG_BUTTON_COLUMNS;
   if (newScrollOffset != scrollOffset)
      {
      scrollOffset = newScrollOffset;
      PopulateButtons();
      }
   return true;
   }

bool DialogBuild::handleButtonCancel(const CEGUI::EventArgs& args)
   {
   hide();
   return true;
   }

bool DialogBuild::handleButtonStructures(const CEGUI::EventArgs& args)
   {
   if (itemTypeToDisplay == ItemType_Building)
      return true;

   itemTypeToDisplay = ItemType_Building;
   InitializeForItemType();
   PopulateButtons();
   return true;
   }

bool DialogBuild::handleButtonBioUnits(const CEGUI::EventArgs& args)
   {
   if (itemTypeToDisplay == ItemType_Character)
      return true;

   itemTypeToDisplay = ItemType_Character;
   InitializeForItemType();
   PopulateButtons();
   return true;
   }

bool DialogBuild::handleButtonMechs(const CEGUI::EventArgs& args)
   {
   if (itemTypeToDisplay == ItemType_Mech)
      return true;

   itemTypeToDisplay = ItemType_Mech;
   InitializeForItemType();
   PopulateButtons();
   return true;
   }

bool DialogBuild::handleButtonVehicles(const CEGUI::EventArgs& args)
   {
   if (itemTypeToDisplay == ItemType_Vehicle)
      return true;

   itemTypeToDisplay = ItemType_Vehicle;
   InitializeForItemType();
   PopulateButtons();
   return true;
   }

// User hit the Entity button at Row 0, Column 0
bool DialogBuild::handleButton00(const CEGUI::EventArgs& args)
   {
   hide();
   global->playState->SetPlaceEntityType(displayedEntityType[0]);
   return true;
   }

bool DialogBuild::handleButton01(const CEGUI::EventArgs& args)
   {
   hide();
   global->playState->SetPlaceEntityType(displayedEntityType[1]);
   return true;
   }

bool DialogBuild::handleButton02(const CEGUI::EventArgs& args)
   {
   hide();
   global->playState->SetPlaceEntityType(displayedEntityType[2]);
   return true;
   }

bool DialogBuild::handleButton03(const CEGUI::EventArgs& args)
   {
   hide();
   global->playState->SetPlaceEntityType(displayedEntityType[3]);
   return true;
   }

bool DialogBuild::handleButton04(const CEGUI::EventArgs& args)
   {
   hide();
   global->playState->SetPlaceEntityType(displayedEntityType[4]);
   return true;
   }

bool DialogBuild::handleButton05(const CEGUI::EventArgs& args)
   {
   hide();
   global->playState->SetPlaceEntityType(displayedEntityType[5]);
   return true;
   }

// The itemType to be displayed changed, either by the dialog being initialized for the first time,
// or the user pressed a button to change to a different itemType.
void DialogBuild::InitializeForItemType(void)
   {
   int               entityIndex;
   int               entityCount;
   EntityTemplate *  entityTemplate;

   scrollOffset = 0;

   // Count the total number of Entities eligible to be displayed in the Build dialog.
   entityCount = 0;
   for (entityIndex = 0; entityIndex < global->entityMgr->GetEntityTemplateCount(); entityIndex++)
      {
      entityTemplate = global->entityMgr->GetEntityTemplateFromIndex(entityIndex);
      if (entityTemplate && (entityTemplate->itemType == itemTypeToDisplay))
         entityCount += 1;
      }

   if (entityCount <= DIALOG_BUTTON_COUNT)
      {
      // There are fewer or equal number of Entities to be displayed than there are buttons, so we don't need a scrollbar
      rootWindow->getChild(scrollBarID)->hide();
      return;
      }

   scrollbar->show();
   scrollbar->setScrollPosition(0.0);
   scrollbar->setProperty("DocumentSize", StringConverter::toString((int)(entityCount / DIALOG_BUTTON_COLUMNS) + 1));
   scrollbar->setProperty("PageSize", StringConverter::toString(DIALOG_BUTTON_ROWS));
   scrollbar->setProperty("StepSize", CEGUI::String("1"));
   scrollbar->setProperty("OverlapSize", CEGUI::String("1"));
   }

void DialogBuild::PopulateButtons(void)
   {
   int               buttonIndex;
   int               entityIndex;
   int               scrollCount;
   Real              cameraDist;
   char              meshName[50];
   Ogre::String      modelName;
   Ogre::Vector3     cameraPosition;
   Ogre::Vector3     cameraTarget;
   Ogre::Vector3     lightDirection;
   EntityTemplate *  entityTemplate;

   // Move entityIndex up to the value of scrollOffset, and continue from there
   entityIndex = 0;
   scrollCount = 0;
   while ((scrollCount < (scrollOffset)) && (entityIndex < global->entityMgr->GetEntityTemplateCount()))
      {
      entityTemplate = global->entityMgr->GetEntityTemplateFromIndex(entityIndex);
      if (entityTemplate && (entityTemplate->itemType == itemTypeToDisplay))
         scrollCount += 1;
      entityIndex += 1;
      }

   // De-initialize all our buttons to make them ready to display a new Entity or be hidden if not needed.
   // They are all hidden here, and unhidden later if needed.
   for (buttonIndex = 0; buttonIndex < DIALOG_BUTTON_COUNT; buttonIndex++)
      {
      if (displayedEntity[buttonIndex] != NULL)
         {
         // Move any existing Entities that we already created for our buttons to the hidden sceneNode.
         rttNode[buttonIndex]->detachObject(displayedEntity[buttonIndex]);
         hideObjectsNode->attachObject(displayedEntity[buttonIndex]);
         displayedEntity[buttonIndex] = NULL;
         }

      rttTex[buttonIndex]->setActive(false);
      spotLight[buttonIndex]->setVisible(false);
      rootWindow->getChild(buttonID[buttonIndex])->hide();
      rootWindow->getChild(buttonLabelID[buttonIndex])->hide();
      rootWindow->getChild(buttonTextID[buttonIndex])->hide();

      displayedEntityType[buttonIndex] = 0;
      }

   for (buttonIndex = 0; buttonIndex < DIALOG_BUTTON_COUNT; buttonIndex++)
      {
      if (entityIndex < global->entityMgr->GetEntityTemplateCount())
         {
         // Find the next EntityTemplate that should be displayed on a button.
         while (entityIndex < global->entityMgr->GetEntityTemplateCount())
            {
            entityTemplate = global->entityMgr->GetEntityTemplateFromIndex(entityIndex);
            if (entityTemplate && (entityTemplate->itemType == itemTypeToDisplay))
               break;
            entityIndex += 1;
            }

         if (entityTemplate && (entityIndex < global->entityMgr->GetEntityTemplateCount()))
            {
            strcpy(meshName, entityTemplate->fileName1);
            strcat(meshName, ".mesh");

            rttTex[buttonIndex]->setActive(true);
            rootWindow->getChild(buttonID[buttonIndex])->show();
            rootWindow->getChild(buttonLabelID[buttonIndex])->show();
            rootWindow->getChild(buttonTextID[buttonIndex])->show();

            displayedEntityType[buttonIndex] = entityIndex;

            modelName = entityTemplate->name;
            modelName += "_DialogBuild_" + StringConverter::toString(entityIndex);

            // Try to grab a saved copy of the Entity we want from the hidden sceneNode.
            try
               {
               displayedEntity[buttonIndex] = (Ogre::Entity *)hideObjectsNode->getAttachedObject(modelName);
               hideObjectsNode->detachObject(displayedEntity[buttonIndex]);
               }
            catch(...)
               {
               // Did not already create this type Entity.  Create a new one.
               displayedEntity[buttonIndex] = global->sceneMgr->createEntity(modelName, meshName);
               assert(_T("DialogBuild::show myModel == NULL") && (displayedEntity != NULL));
               }

            rttNode[buttonIndex]->attachObject(displayedEntity[buttonIndex]);
            rttNode[buttonIndex]->setPosition(Ogre::Vector3(100.0 * entityIndex, OBJECT_WORLD_POS_Y, 0));

            if (entityTemplate->collisionHeight > entityTemplate->collisionRadius)
               cameraDist = entityTemplate->collisionHeight * 1.5;
            else
               cameraDist = entityTemplate->collisionRadius * 2.5;

            cameraPosition = Ogre::Vector3(100.0 * entityIndex, OBJECT_WORLD_POS_Y + entityTemplate->collisionHeight * 1.2, cameraDist);
            cameraTarget   = Ogre::Vector3(100.0 * entityIndex, OBJECT_WORLD_POS_Y + entityTemplate->collisionHeight * 0.4, 0.0);
            lightDirection = cameraTarget - cameraPosition;
            lightDirection.normalise();

            rttCam[buttonIndex]->setPosition(cameraPosition);
            rttCam[buttonIndex]->lookAt(cameraTarget);
            spotLight[buttonIndex]->setPosition(cameraPosition);
            spotLight[buttonIndex]->setVisible(true);
            spotLight[buttonIndex]->setDirection(lightDirection);

            rootWindow->getChild(buttonLabelID[buttonIndex])->setText(entityTemplate->name);

            // TODO: populate this text box with real statistics regarding this Entity.
            rootWindow->getChild(buttonTextID[buttonIndex])->setText("Cost: " + StringConverter::toString(randInt(500,1000)));
            }
         else
            {
            // Finished going through all our EntityTemplates and could find no more to display.
            return;
            }
         }

      entityIndex += 1;
      }
   }

void DialogBuild::show(void)
   {
   if (isShown)
      return;
   isShown = TRUE;

   // Force the Dialog to display the first "page" every time it opens.
   itemTypeToDisplay = INITIAL_ITEM_TYPE;
   InitializeForItemType();
   PopulateButtons();

   rootWindow->show();
   rootWindow->moveToFront();
   InputHandler::getInstance()->SetGUIHasFocus(TRUE);
   }

void DialogBuild::hide(void)
   {
   rootWindow->hide();
   InputHandler::getInstance()->SetGUIHasFocus(FALSE);

   if (!isShown)
      return;
   isShown = FALSE;

   int buttonIndex;

   for (buttonIndex = 0; buttonIndex < DIALOG_BUTTON_COUNT; buttonIndex++)
      {
      rttTex[buttonIndex]->setActive(false);

      if (displayedEntity[buttonIndex] != NULL)
         {
         // Move any existing Entities that we already created for our buttons to the hidden sceneNode.
         rttNode[buttonIndex]->detachObject(displayedEntity[buttonIndex]);
         hideObjectsNode->attachObject(displayedEntity[buttonIndex]);
         displayedEntity[buttonIndex] = NULL;
         }
      }
   }

void DialogBuild::toggleVisibility()
   {
   if (isShown)
      hide();
   else
      show();
   }

bool DialogBuild::isVisible() const
   {
   return isShown;
   }