// BlockyFroggy
// Copyright © 2017 John Ryland.
// All rights reserved.
#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)
{
//! @todo this is allocating all of these ahead of time before they are needed. Is this good?
// GameStates could be plugin like if they weren't bound like this. Perhaps they auto register
ListGameStates();
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";
//! @todo create the state as needed - perhaps constructor implies enter and destructor implies exit?
// Or leaving as is allows allocating and deallocating as required in the enter and exit functions
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
Log(LL_Trace, "Stage: %s", 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();
*/
m_ui.resetState();
m_graphics.prepareState(m_ui.state());
if (!m_currentState->update(m_ui.state(), 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 an API might be the way to go
//if (m_ui.needsUpdate())
m_ui.update(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, m_ui.state(), playerPos, camera, light, debug);
// probably want this to be more of a backgrounded task
for (auto job : m_ui.state().m_loadTexturesQueue)
m_graphics.LoadPendingTexture(job);
//! @todo move to drawing and does it need to be static now?
//static
static GameUi::DrawItems items;
items.m_items.resize(0);
m_ui.drawForegroundItems(items);
m_graphics.drawDebug(items);
}
void GameScreen::draw()
{
m_graphics.preDraw(m_currentState->getTime());
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
static GameUi::DrawItems items;
items.m_items.resize(0);
m_ui.drawBackgroundItems(items);
m_graphics.drawAuxItems(items);
std::vector<GameUi::View*>* views = m_ui.getCurrentViews();
if (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);
}