Newer
Older
Import / applications / HighwayDash / ports / Game / GameScreen.cpp
#include "Graphics.h"
#include "LuaBindings.h"
#include "ResourceLoader.h"
#include "Log.h"
#include "GameScreen.h"
#include "GameState.h"
#include "BlankState.h"
#include "MotdState.h"
#include "WelcomeState.h"
#include "EditorState.h"
#include "SelectorState.h"


bool debugUi = false;

/*
class EndScreenState : public BaseGameState
{
public:
  void Enter() override;
  void Exit() override;
};
*/


GameScreen::GameScreen() : m_graphicsPtr(new GameGraphics()), m_graphics(*m_graphicsPtr)
{
  m_stageMap["game"] = new GameState;
  m_stageMap["blank"] = new BlankState;
  m_stageMap["welcome"] = new WelcomeState;
  m_stageMap["editor"] = new EditorState;
  m_stageMap["motd"] = new MotdState;
  m_stageMap["selector"] = new SelectorState;

  m_stage = "blank";
  m_currentState = m_stageMap[m_stage];
}


GameScreen::~GameScreen()
{
  delete m_graphicsPtr;
  //delete m_currentState;
  // need to delete the items in the map
}


void GameScreen::initialize()
{
  m_currentState->Enter(m_graphics);
  m_graphics.initialize();
  m_ui.LoadDialogsFromAsset("ui.json");
}


void GameScreen::shutdown()
{
  m_graphics.shutdown();
}


void GameScreen::stateTransition(std::string oldState, std::string newState)
{
  // Stage change required
  m_currentState->Exit(m_graphics);

  // TODO: perhaps need to unbind lua functions when exiting a stage
  m_stage = m_ui.currentStage();
  if (!m_stageMap.count(m_stage))
    m_stage = "blank";
  m_currentState = m_stageMap[m_stage];
  m_currentState->Enter(m_graphics);

  const struct luaL_Reg* luaFuncList = 0;
  int luaFuncCount = 0;
  m_currentState->GetScriptCallableFunctions(luaFuncList, luaFuncCount);
  for (int i = 0; i < luaFuncCount; i++)
    m_ui.bindFunctionToLua(luaFuncList[i].name, luaFuncList[i].func);
}


void GameScreen::update(float a_elapsed, float a_aspect)
{
  // stage
  //fprintf(stderr, "Stage: %s\n", m_ui.currentStage().c_str());

  // Transition states
  if (m_ui.currentStage() != m_stage)
    stateTransition(m_stage, m_ui.currentStage());

  // probably could move this up to an application level update (this is not screen related)
  ResourceLoaderUpdate(a_elapsed);

  if (debugUi)
  {
    // This continually refreshes the ui from the file - probably only want to do this quite periodically
    static int tick = 0;
    if (tick++ >= 100) {
      // etag checking would make this faster / better
      m_ui.LoadDialogsFromAsset("ui.json");
      tick = 0;
    }
  }

  //std::map<std::string, std::string> m_variables; // Similar to environment variables

  static GameUi::DrawItems items;
  items.loadTexturesQueue.clear();
  items.m_items.clear();

  if (!m_currentState->Update(items, a_elapsed)) {
    m_ui.exitCurrent();
  }
  //m_currentState->UpdateVariables(items.m_variables);

  //if (m_hudEnabled)
    // Need to work out if something has made the UI dirty and the display list / vertex data needs updating
    // The ui knows if the state of any buttons etc changed, so that is fine, but there are also the mapped
    // variables displayed by labels etc which could change so that is outside the ui's ability to detect
    // so perhaps here can reduce calls to glBufferData if can be aware when those values change. Perhaps
    // control of setting those through and API might be the way to go
    //if (m_ui.needsUpdate())
      m_ui.update(items, a_elapsed);

  float playerPos[3]; // hack - remove and get state to return a camera
  float camera[8], light[8], debug[8];
  m_currentState->GetPlayerPos(playerPos);
  m_currentState->GetCameras(camera, light, debug, a_elapsed);
  m_graphics.update(a_elapsed, a_aspect, items, playerPos, camera, light, debug);
}


void GameScreen::draw()
{
  m_graphics.preDraw(m_currentState->GetTime());
  glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

  GameUi::DrawItems items;
  m_ui.backgroundItems(items);
  m_graphics.drawAuxItems(items);

  std::vector<GameUi::View*> views;
  m_ui.getCurrentViews(views);
  m_graphics.drawViews(views);

  m_currentState->Draw(m_graphics);

  // Draws the ui elements defined in ui.json for the dialog
  m_graphics.drawHUD();

  /*
  GameUi::DrawItems items;
  m_ui.update(items, a_elapsed);
  graphics.drawAuxItems(items);
  */
}

/*
void BlankState::Draw(GameGraphics& graphics)
{
  graphics.prepareScreenContext();
}


void WelcomeState::Draw(GameGraphics& graphics)
{
  graphics.prepareScreenContext();
  graphics.drawRotatingFrog();
}


void GameState::Draw(GameGraphics& graphics)
{
  if (m_needReset) {
    // reset the vertex buffers
    graphics.resetVertexBuffers(m_sim.objectLists());
    m_needReset = false;
  }
  
  //graphics.prepareScreenContext();
  //glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
  graphics.draw();
}


void EditorState::Draw(GameGraphics& graphics)
{
  static int lastLayer = -11;
  int editLayerI = (pixD-1) - int((pixD-1) * editLayer + 0.0);
  if (editLayerI != lastLayer || !pixels.size())
    graphics.getEditObjectPixels(editLayer, pixels, pixW, pixH, pixD);
  else
    graphics.setEditObjectPixels(editLayer, pixels, pixW, pixH);
  lastLayer = editLayerI;
  if (!palette.size())
    graphics.getPalettePixels(palette);
  int pw = paletteRect.w/16;
  int ph = paletteRect.h/16;
  int px = palIdx%16;
  int py = palIdx/16;

  // Draw bottom ui layer
  GameUi::DrawItems itemsUnder;
  addDialogFrame(itemsUnder,   6,  55, 308, 350, "Preview");
  addDialogFrame(itemsUnder, 324,  55, 308, 350, "Layer");
  addDialogFrame(itemsUnder,   6, 445, 308, 350, "Color");
  addDialogFrame(itemsUnder, 324, 445, 308, 350, "Editor");
  addPixmapData(itemsUnder, paletteRect, { palette.data(),   16,   16 }, { 0, 0,   16,   16 });
  addPixmapData(itemsUnder, editorRect,  { pixels.data(), size_t(pixW), size_t(pixH) }, { 0, 0, pixW, pixH });

  int pixW = (paletteRect.w / 8) - 1;
  int pixH = (paletteRect.h / 8) - 1;
  for (int i = 0; i < 8; i++)
    itemsUnder.addQuad(Rectangle{paletteRect.x + i*pixW, paletteRect.y + 8*pixH, pixW, pixH}, Rectangle{179,113,30,30}, palette[palIdxMru[i]]);

  itemsUnder.addQuad({paletteRect.x + px*pw, paletteRect.y + py*ph, pw, ph}, { 160, 141, 2, 2 }, 0xff123456, 0);  
  itemsUnder.addIcon(paletteRect.x, paletteRect.y, 23, 0xff123456);
  itemsUnder.addContrastString( 50,  160, "Text under");

  GameUi::ToolButton toolButton(330, 475, 23);
  toolButton.onUpdate(itemsUnder, 0.0);
  GameUi::ToolButton toolButton2(380, 475, 23);
  toolButton2.onUpdate(itemsUnder, 0.0);

  graphics.drawAuxItems(itemsUnder);

  // Draw 3D parts
  graphics.prepareScreenContext();

//  clipRendering(  6+2, 55+27, 304, 347 );

  GLint vp[4];
  glGetIntegerv(GL_VIEWPORT, vp);

  //glViewport(6+2, 55+27, 304, 347);
  glViewport(6+2, vp[3]-(55+27+347), 304, 347);

  graphics.drawRotatingEditObject(editLayer);

  glViewport(vp[0], vp[1], vp[2], vp[3]);

  glClear(GL_DEPTH_BUFFER_BIT);
  clipRendering(324+2, 55+27, 304, 347 );
  graphics.drawRotatingEditObject(editLayer);
  disableClip();

  // Draw ui overlay over everything
  GameUi::DrawItems itemsOver;
  itemsOver.addContrastString( 70,  220, "Text over");
  graphics.drawAuxItems(itemsOver);

}
*/

void GameScreen::freeSpace()
{
}


void GameScreen::touchDown(float a_x, float a_y)
{
  if (m_ui.handleMouse(GameUi::MouseState{int(a_x),int(a_y),true}))
    return;
  m_currentState->TouchesChanged(a_x, a_y, TouchDown);
}


void GameScreen::touchMove(float a_x, float a_y)
{
  if (m_ui.handleMouse(GameUi::MouseState{int(a_x),int(a_y),true}))
    return;
  m_currentState->TouchesChanged(a_x, a_y, TouchMove);
}


void GameScreen::touchUp(float a_x, float a_y)
{
  if (m_ui.handleMouse(GameUi::MouseState{int(a_x),int(a_y),false}))
    return;
  m_currentState->TouchesChanged(a_x, a_y, TouchUp);
}