/*!
@header Dialog.h
@brief Various widgets
@version 0.0.1
@author John Ryland
@copyright Copyright 2017 John Ryland. All rights reserved.
*/
// BlockyFroggy
// Copyright © 2017 John Ryland.
// All rights reserved.
#pragma once
#ifndef DIALOG_H
#define DIALOG_H
#include <vector>
#include <stack>
#include <functional>
#include <string>
#include <map>
#include <cmath>
#include "Animation.h"
#include "Lua.h"
#include "GLProgram.h"
#include "Math.h"
//! GameUi is the namespace for the Ui subsystem.
//! It include Widget types and a Ui stack system
//! as well as loading from a JSON description.
namespace GameUi {
class Ui;
typedef std::map<std::string, std::vector<std::string> > ActionMap;
typedef std::map<std::string, std::string> StringMap;
struct Point
{
int x, y;
};
struct Rectangle
{
int x, y, w, h;
bool isInside(Point pt) const;
};
/*!
@struct Color
@brief The Color Struct
@discussion Holds a 32bit integer color with RGBA channels.
*/
struct Color
{
Color(uint32_t a_c) { color = a_c; }
//! @todo remove union and make accessor functions to get and set the components - should be more straight forward
union {
uint32_t color;
#if __BYTE_ORDER == __LITTLE_ENDIAN
struct { uint8_t r, g, b, a; }; // LE
#elif __BYTE_ORDER == __BIG_ENDIAN
struct { uint8_t b, g, r, a; }; // BE
#else
# error "if on _WIN32, perhaps can assume LE"
#endif
};
};
struct Text
{
Text() { length = 0; }
Text(const char *a_text) : input(a_text), formatted(a_text) { length = input.length(); }
Text& operator=(const char *a_text) { input = a_text; formatted = a_text; return *this; }
void update(StringMap& variables);
size_t size() const { return length; }
const char* c_str() const { return formatted.c_str(); }
private:
std::string input;
std::string formatted;
size_t length;
};
struct PixelData
{
//! @todo add some initializer and accessors
Color* m_data;
size_t m_stride;
size_t m_rows;
};
struct AnimationParameters
{
std::string m_event;
std::string m_parameter;
//std::string m_curve;
CurveFunction m_curve;
float m_timeScale = 1.0;
float m_timeOffset = 0.0;
bool m_loop = false;
int m_t0 = 0;
int m_t1 = 1;
};
typedef std::vector<AnimationParameters> AnimationList;
struct TextureLoadJob
{
std::string assetName;
uint8_t texIdx;
};
struct MouseState
{
Point pos;
bool down;
};
//! @todo Does UiVertex need to be exposed in the header?
DECLARE_ATTRIB_TYPE(vec2i, int16_t, x, y)
DECLARE_ATTRIB_TYPE(tex3i, uint8_t, u, v, texIdx)
DECLARE_VERTEX(UiVertex)
DECLARE_ATTRIB(vec2i, position, GL_FALSE)
DECLARE_ATTRIB(col4i, color, GL_TRUE)
DECLARE_ATTRIB(tex3i, textureCoords, GL_FALSE) // Fix the font and ui control elements in to a 256x256 texture
DECLARE_VERTEX_END
/// @cond INTERNAL
static_assert(sizeof(UiVertex) == 16, "Bad size");
/// @endcond
struct UpdateState
{
uint8_t getTextureIdForAsset(const char* assetName);
//std::map<std::string, uint8_t> mappedTextureIds;
std::vector<TextureLoadJob> m_loadTexturesQueue;
StringMap m_variables; // Similar to environment variables
// name->value pairs for substitution when adding strings,
// eg: m_variables["BLAH"] = "foo"; addString("${BLAH}");
// This will result in "foo" being drawn instead of "${BLAH}" as m_variables maps it to this.
};
class DrawItemsInterface
{
public:
virtual void addQuad(const Rectangle& r, const Rectangle& tex, Color col, uint8_t texId = 0) = 0;
virtual void addIcon(int x, int y, char icon, Color col, int scale = 2) = 0;
virtual void addChar(int x, int y, char ch, Color col, int scale = 2) = 0;
virtual void addString(int x, int y, const Text& a_text, Color col, int scale = 2) = 0;
virtual void addContrastString(int x, int y, const Text& a_text, int scale = 2) = 0;
virtual void add9Slice(const Rectangle& r, const Rectangle& t, int radius, Color col) = 0;
};
class DrawItems : public DrawItemsInterface
{
public:
DrawItems() { m_items.reserve(65536); }
void addQuad(const Rectangle& r, const Rectangle& tex, Color col, uint8_t texId = 0) override;
void addIcon(int x, int y, char icon, Color col, int scale = 2) override;
void addChar(int x, int y, char ch, Color col, int scale = 2) override;
void addString(int x, int y, const Text& a_text, Color col, int scale = 2) override;
void addContrastString(int x, int y, const Text& a_text, int scale = 2) override;
void add9Slice(const Rectangle& r, const Rectangle& t, int radius, Color col) override;
std::vector<UiVertex> m_items;
};
class UiControl
{
public:
virtual ~UiControl(){}
bool handleMouse(MouseState ms);
/*
enum MouseState {
ButtonState = 1,
InsideState = 2,
PositionState = 4
};
virtual void onMouseStateChange() {}
*/
// Other possible events:
// onHide/Close
// onShow/Open
// onKeyPress
// onKeyRelease
// onMinimize
// onMaximize
// onEvent // generic messaging system
// onHover
// onEnter
// onLeave
virtual void onMousePress() { genericDispatch("onMousePress"); }
virtual void onMouseRelease() { genericDispatch("onMouseRelease"); }
virtual void onMouseMove() { genericDispatch("onMouseMove"); }
virtual void onMouseEnter() { genericDispatch("onMouseEnter"); }
virtual void onMouseLeave() { genericDispatch("onMouseLeave"); }
virtual void onMouseClicked() { genericDispatch("onMouseClicked"); }
virtual void onUpdate(UpdateState& variables, float elapsed) {}
virtual void onDraw(DrawItemsInterface& drawItems) {}
/*
void setRequiresUpdate() { m_needsUpdate = true; }
void clearRequiresUpdate() { m_needsUpdate = false; }
bool requiresUpdate() { return m_needsUpdate; }
const char* name() { return m_name.c_str(); }
*/
bool m_needsUpdate = true;
std::string m_name = "null";
AnimationList m_animationList;
ActionMap m_actionMap;
Ui* m_ui = nullptr;
protected:
Rectangle m_geometry;
bool m_mouseDownInside = false;
bool m_mouseDownLast = false;
bool m_mouseInsideLast = false;
MouseState m_mouseState;
void genericDispatch(const char* event);
};
//! @todo too many hard coded numbers in the UiControl subclasses
class Label : public UiControl
{
public:
Label(int x, int y, int alignmentFlags, const char* a_text) : m_x(x), m_y(y), m_flags(alignmentFlags), m_text(a_text) {
m_geometry = Rectangle{x,y,uint16_t(m_text.size()*10),16};
m_name = "label";
}
void onUpdate(UpdateState& state, float elapsed) override {
m_text.update(state.m_variables);
UiControl::onUpdate(state, elapsed);
}
void onDraw(DrawItemsInterface& items) override {
if (m_flags) {
const int chWidth = 20;
int strWidth = (int)m_text.size() * chWidth;
items.addContrastString(m_x - strWidth, m_y, m_text);
} else
items.addContrastString(m_x, m_y, m_text);
UiControl::onDraw(items);
}
private:
int m_x, m_y, m_flags;
Text m_text;
};
class ButtonBase : public UiControl
{
public:
void onMousePress() override { if (!m_pressed) { m_pressed = true; m_needsUpdate = true; } UiControl::onMousePress(); }
void onMouseRelease() override { if (m_pressed) { m_pressed = false; m_needsUpdate = true; } UiControl::onMouseRelease(); }
void onMouseClicked() override { if (m_isToggle) { m_on = !m_on; m_needsUpdate = true; } UiControl::onMouseClicked(); }
//std::function<void()> m_clicked;
bool m_pressed = false;
bool m_isToggle = false;
bool m_on = false;
};
class PixmapEditor : public ButtonBase
{
public:
PixmapEditor(Rectangle screenGeometry, PixelData pixmap, Rectangle pixmapRect) {
m_geometry = screenGeometry;
m_pixmapSlice = pixmapRect;
m_pixmap = pixmap;
m_name = "pixmapeditor";
}
void onUpdate(UpdateState& state, float elapsed) override {
UiControl::onUpdate(state, elapsed);
}
void onDraw(DrawItemsInterface& items) override {
if (!m_pixmapSlice.w || !m_pixmapSlice.h)
return;
int pixW = m_geometry.w / m_pixmapSlice.w;
int pixH = m_geometry.h / m_pixmapSlice.h;
for (int j = 0; j < m_pixmapSlice.h; j++)
for (int i = 0; i < m_pixmapSlice.w; i++) {
int x = m_geometry.x + pixW * i;
int y = m_geometry.y + pixH * j;
size_t idx = (m_pixmapSlice.y + j) * m_pixmap.m_stride + m_pixmapSlice.x + i;
if (idx < m_pixmap.m_stride * m_pixmap.m_rows)
items.addQuad(Rectangle{x, y, pixW, pixH}, Rectangle{179,113,30,30}, m_pixmap.m_data[idx]);
}
UiControl::onDraw(items);
}
private:
PixelData m_pixmap;
Rectangle m_pixmapSlice;
};
class ToolButton : public ButtonBase
{
public:
ToolButton(int x, int y, int icon) : m_x(x), m_y(y), m_icon(icon) {
// , std::function<void()> clicked) : m_x(x), m_y(y), m_icon(icon) {
//m_clicked = clicked;
m_geometry = Rectangle{x,y,50,50};
m_name = "toolbutton";
}
void onUpdate(UpdateState& state, float elapsed) override {
UiControl::onUpdate(state, elapsed);
}
void onDraw(DrawItemsInterface& items) override {
// Tool Button
int state = (m_pressed) ? 31 : ((m_on) ? 61 : 1);
items.add9Slice(Rectangle{m_x, m_y, 50, 50}, Rectangle{state, 114, 30, 30}, 10, 0xaadd9999);
// Icon
items.addIcon(m_x+5, m_y+6+(m_pressed?1:0), m_icon, 0xFF772222);
//int tx1 = 1 + 20*m_icon, ty1 = 5*19;//64;
//items.addQuad(Rectangle{m_x+4, m_y+5+(m_pressed?1:0), 40, 38}, Rectangle{tx1, ty1, 20, 19}, 0xFF772222);
UiControl::onDraw(items);
}
private:
int m_x, m_y, m_icon;
};
static const int buttonHeight = 70;
class Button : public ButtonBase
{
public:
Button(int x, int y, const char* a_text) : m_x(x), m_y(y), m_text(a_text) {
// , std::function<void()> clicked) : m_x(x), m_y(y), m_text(a_text) {
//m_clicked = clicked;
m_geometry = Rectangle{x,y,250,buttonHeight};
m_name = "button";
}
void onUpdate(UpdateState& state, float elapsed) override {
m_text.update(state.m_variables);
UiControl::onUpdate(state, elapsed);
}
void onDraw(DrawItemsInterface& items) override {
Log(LL_Trace, "button update");
int state = (m_pressed) ? 31 : ((m_on) ? 61 : 1);
items.add9Slice(Rectangle{m_x, m_y, 250, buttonHeight}, Rectangle{state, 114, 30, 30}, 15, 0xff550011);// 0xaa550000);
items.addContrastString(m_x+125-(int)m_text.size()*10, m_y+18+(m_pressed?1:0), m_text);
UiControl::onDraw(items);
}
private:
int m_x, m_y;
Text m_text;
};
class CheckBox : public UiControl
{
public:
CheckBox(int x, int y, const char* a_text, std::string binding) : m_x(x), m_y(y), m_text(a_text), m_dataBinding(binding) {
m_geometry = Rectangle{x,y,250,50};
m_name = "checkbox";
m_mouseState.down = false;
m_lastMouseState.down = false;
}
void onUpdate(UpdateState& state, float elapsed) override {
if (!m_mouseState.down) {
m_down = false;
m_on = state.m_variables[m_dataBinding] == "on";
} else if (!m_down && !m_lastMouseState.down && m_geometry.isInside(m_mouseState.pos)) {
m_down = true;
m_on = !m_on;
}
m_lastMouseState = m_mouseState;
state.m_variables[m_dataBinding] = (m_on) ? "on" : "off";
m_text.update(state.m_variables);
UiControl::onUpdate(state, elapsed);
}
void onDraw(DrawItemsInterface& items) override {
char icon = 10 + ((m_on) ? 1 : 0);
items.addIcon(m_x, m_y, icon, 0xff550011);
items.addContrastString(m_x+45, m_y, m_text);
UiControl::onDraw(items);
}
private:
MouseState m_lastMouseState;
bool m_down = false;
bool m_on = false;
int m_x, m_y;
Text m_text;
std::string m_dataBinding;
};
class Slider : public UiControl
{
public:
Slider(int x, int y, int height, std::string binding) : m_x(x), m_y(y), m_h(height), m_dataBinding(binding) {
m_geometry = Rectangle{x-15,y-20,50,50};
m_name = "slider";
m_mouseState.down = false;
m_lastMouseState.down = false;
m_down = false;
}
void onUpdate(UpdateState& state, float elapsed) override {
if (!m_mouseState.down)
m_down = false;
else if (!m_down && !m_lastMouseState.down && m_geometry.isInside(m_mouseState.pos))
m_down = true;
if (m_down && m_mouseState.down)
//m_geometry.y += m_mouseState.pos.y - m_lastMouseState.pos.y; // Usual mouse kind of control
m_geometry.y = m_mouseState.pos.y - 20; // Perhaps more touch screen style of control
m_lastMouseState = m_mouseState;
m_geometry.y = Math::clamp(m_geometry.y, m_y-20, m_y+m_h-20);
float percent = float(m_geometry.y - m_y + 20) / float(m_h);
state.m_variables[m_dataBinding] = std::to_string(percent);
UiControl::onUpdate(state, elapsed);
}
void onDraw(DrawItemsInterface& items) override {
items.add9Slice(Rectangle{m_x, m_y, 20, m_h}, Rectangle{211, 114, 30, 30}, 10, 0xff550011);
int state = (m_down) ? 31 : 1;
items.add9Slice(m_geometry, Rectangle{state, 114, 30, 30}, 10, 0xff550011);
UiControl::onDraw(items);
}
private:
MouseState m_lastMouseState;
bool m_down;
int m_x, m_y, m_h;
std::string m_dataBinding;
};
class HSlider : public UiControl
{
public:
HSlider(int x, int y, int width, std::string binding) : m_x(x), m_y(y), m_w(width), m_dataBinding(binding) {
m_geometry = Rectangle{x-15,y-20,50,50};
m_name = "hslider";
m_mouseState.down = false;
m_lastMouseState.down = false;
m_down = false;
}
void onUpdate(UpdateState& state, float elapsed) override {
if (!m_mouseState.down)
m_down = false;
else if (!m_down && !m_lastMouseState.down && m_geometry.isInside(m_mouseState.pos))
m_down = true;
if (m_down && m_mouseState.down)
//m_geometry.x += m_mouseState.pos.x - m_lastMouseState.pos.x; // Usual mouse kind of control
m_geometry.x = m_mouseState.pos.x - 20; // Perhaps more touch screen style of control
m_lastMouseState = m_mouseState;
m_geometry.x = Math::clamp(m_geometry.x, m_x-20, m_x+m_w-20);
float percent = float(m_geometry.x - m_x + 20) / float(m_w);
state.m_variables[m_dataBinding] = std::to_string(percent);
UiControl::onUpdate(state, elapsed);
}
void onDraw(DrawItemsInterface& items) override {
items.add9Slice(Rectangle{m_x, m_y+3, m_w, 5}, Rectangle{211, 114, 30, 30}, 2, 0xff550011);
int state = (m_down) ? 31 : 1;
items.add9Slice(m_geometry, Rectangle{state, 114, 30, 30}, 10, 0xff550011);
UiControl::onDraw(items);
}
private:
MouseState m_lastMouseState;
bool m_down;
int m_x, m_y, m_w;
std::string m_dataBinding;
};
class Frame : public UiControl
{
public:
Frame(int x, int y, int w, int h, const char* a_text) : m_text(a_text) {
m_geometry = Rectangle{x,y,w,h};
m_name = "frame";
}
void onUpdate(UpdateState& state, float elapsed) override {
m_text.update(state.m_variables);
UiControl::onUpdate(state, elapsed);
}
void onDraw(DrawItemsInterface& items) override {
int x = m_geometry.x, y = m_geometry.y, w = m_geometry.w, h = m_geometry.h;
items.add9Slice({ x-3, y-35, 120, 50}, { 1, 114, 30, 30}, 10, 0xff553333);
items.add9Slice({ x+0, y+0, w+0, h+ 0}, { 90, 114, 30, 30}, 20, 0xff000000);
items.addQuad( { x+3, y+3, w-6, h- 6}, {150, 114, 30, 30}, 0xffffffff);
items.addContrastString( x+15, y-25, m_text, 1);
UiControl::onDraw(items);
}
private:
Text m_text;
};
class Panel : public UiControl
{
public:
Panel(int w, int h, Color col) : m_w(w), m_h(h), m_col(col) {
m_geometry = Rectangle{0,0,w,h};
m_name = "panel";
}
void onUpdate(UpdateState& state, float elapsed) override {
UiControl::onUpdate(state, elapsed);
}
void onDraw(DrawItemsInterface& items) override {
items.addQuad(Rectangle{ 0, 0, m_w, m_h}, Rectangle{150, 114, 30, 30}, 0x33333333);
items.add9Slice(Rectangle{ 50, 50, m_w-100, m_h-200}, Rectangle{90, 114, 30, 30}, 50, 0xdd223322);//0x77CF77);
UiControl::onDraw(items);
}
private:
int m_w, m_h;
Color m_col;
};
class RectangleShape : public UiControl
{
public:
RectangleShape(int x, int y, int w, int h, Color col) : m_col(col) {
m_geometry = Rectangle{x,y,w,h};
}
void onUpdate(UpdateState& state, float elapsed) override {
m_t += elapsed;
for (size_t i = 0; i < m_animationList.size(); i++) {
AnimationParameters params = m_animationList[i];
double tmp, ratio = m_t * 100.0 / params.m_timeScale;
if (ratio >= 1.0) {
if (params.m_loop)
ratio = modf(ratio, &tmp);
else
ratio = 1.0;
}
int value = Math::lerp(params.m_t0, params.m_t1, params.m_curve(ratio));
Log(LL_Trace, "applying anim param %s = %i", params.m_parameter.c_str(), value);
if (params.m_parameter == "x")
m_geometry.x = value;
else if (params.m_parameter == "y")
m_geometry.y = value;
else if (params.m_parameter == "w")
m_geometry.w = value;
else if (params.m_parameter == "h")
m_geometry.h = value;
else if (params.m_parameter == "r")
m_col.r = value;
else if (params.m_parameter == "g")
m_col.g = value;
else if (params.m_parameter == "b")
m_col.b = value;
else if (params.m_parameter == "a")
m_col.a = value;
}
UiControl::onUpdate(state, elapsed);
}
void onDraw(DrawItemsInterface& items) override {
items.addQuad(m_geometry, Rectangle{180+1, 114+1, 30-2, 30-2}, m_col);
UiControl::onDraw(items);
}
private:
float m_t;
Color m_col;
};
class ImageShape : public UiControl
{
public:
ImageShape(int x, int y, const char* assetName) {
int w = 255, h = 255;
m_asset = assetName;
Log(LL_Trace, "image loading with asset: %s", assetName);
// load asset and set m_texIdx
m_geometry = Rectangle{x,y,w,h};
}
void onUpdate(UpdateState& state, float elapsed) override {
m_texIdx = state.getTextureIdForAsset(m_asset.c_str());
UiControl::onUpdate(state, elapsed);
}
void onDraw(DrawItemsInterface& items) override {
items.addQuad(m_geometry, Rectangle{0,0,m_geometry.w,m_geometry.h}, 0xff223344, m_texIdx);
UiControl::onDraw(items);
}
private:
std::string m_asset;
int m_texIdx = 1;
};
class DynamicImageShape : public UiControl
{
public:
DynamicImageShape(int x, int y, int texId) {
int w = 512, h = 512;
m_texIdx = texId;
m_geometry = Rectangle{x,y,w,h};
}
void onUpdate(UpdateState& state, float elapsed) override {
UiControl::onUpdate(state, elapsed);
}
void onDraw(DrawItemsInterface& items) override {
items.addQuad(m_geometry, Rectangle{0,0,m_geometry.w,m_geometry.h}, 0xff223344, m_texIdx);
UiControl::onDraw(items);
}
private:
int m_texIdx = 1;
};
class View
{
public:
View(int x, int y, int w, int h, const char* view) : m_geometry{x,y,w,h}, m_view(view) {}
Rectangle m_geometry;
std::string m_view;
};
class Dialog
{
public:
Dialog() {}
Dialog(int w, int h) {
m_controls.push_back(new Panel(w,h,0xaa770000));
}
~Dialog() {
for (UiControl* ctrl : m_controls) {
delete ctrl;
}
}
bool handleMouse(MouseState ms, bool& consumed) {
m_needsUpdate = false;
// for (UiControl* ctrl : m_controls) {
for (long i = m_controls.size()-1; i >= 0; i--) {
UiControl* ctrl = m_controls[i];
consumed = ctrl->handleMouse(ms);
if (ctrl->m_needsUpdate)
Log(LL_Trace, "need update for ctrl %s", ctrl->m_name.c_str());
m_needsUpdate |= ctrl->m_needsUpdate;
if (consumed)
break; // Only let one control consume the mouse event (perhaps should do this in reverse ctrl order)
}
return m_needsUpdate;
}
void update(UpdateState& state, float elapsed) {
for (UiControl* ctrl : m_background) {
Log(LL_Trace, "update for ctrl %s", ctrl->m_name.c_str());
ctrl->onUpdate(state, elapsed);
ctrl->m_needsUpdate = false;
}
for (UiControl* ctrl : m_controls) {
Log(LL_Trace, "update for ctrl %s", ctrl->m_name.c_str());
ctrl->onUpdate(state, elapsed);
ctrl->m_needsUpdate = false;
}
m_needsUpdate = false;
}
void drawBackgroundItems(DrawItemsInterface& items) {
for (UiControl* ctrl : m_background)
ctrl->onDraw(items);
}
void drawForegroundItems(DrawItemsInterface& items) {
for (UiControl* ctrl : m_controls)
ctrl->onDraw(items);
}
// void close() { m_onClose(); }
// std::function<void()> m_onClose;
std::vector<UiControl*> m_background;
std::vector<View*> m_views;
std::vector<UiControl*> m_controls;
bool m_needsUpdate = false;
ActionMap m_actionMap;
std::string m_name;
std::string m_stage; // expected c++ level
};
class Ui
{
public:
~Ui() {
// Probably need to remove then from the DialogMap, not the stack
//for (Dialog* dialog : m_stack)
// delete dialog;
}
void loadDialogsFromAsset(const char* assetName);
void dialogRedirect(std::string redirect);
bool handleMouse(MouseState ms) { bool consumed = false;
if (ms.down != m_lastMouse.down) {
float dx = m_lastMouse.pos.x - ms.pos.x;
if (ms.down)
dialogDispatch("onTap");
else if (dx > 5)
dialogDispatch("onSwipeRight");
else if (dx < -5)
dialogDispatch("onSwipeLeft");
m_lastMouse = ms;
}
if (m_stack.size())
m_needsUpdate = current().handleMouse(ms, consumed);
return consumed;
}
void update(float a_elapsed) { if (m_stack.size()) { current().update(m_state, a_elapsed); } m_needsUpdate = false; }
void drawBackgroundItems(DrawItemsInterface& items) { if (m_stack.size()) { current().drawBackgroundItems(items); } }
void drawForegroundItems(DrawItemsInterface& items) { if (m_stack.size()) { current().drawForegroundItems(items); } }
std::vector<View*>* getCurrentViews() { if (m_stack.size()) { return ¤t().m_views; } return nullptr; }
//void getCurrentViews(std::vector<View*>& views) { if (m_stack.size()) { views = current().m_views; } }
void pushDialog(Dialog* dialog) { m_stack.push_back(dialog); /* dialog->m_onClose = [this](){ pop(); }; */ m_needsUpdate = true; }
void pop() { m_stack.pop_back(); m_needsUpdate = true; }
void closeCurrent() { if (m_stack.size()) pop(); } //current().close(); }
void exitCurrent() { dialogDispatch("onExit"); }
std::string currentStage() { if (m_stack.size()) return current().m_stage; return "blank"; }
void resetState() { m_state.m_loadTexturesQueue.clear(); }
UpdateState& state() { return m_state; }
bool needsUpdate() { return m_needsUpdate; }
void onLaunch() { genericDispatch("onLaunch"); }
void onGameOver() { genericDispatch("onGameOver"); }
void bindFunctionToLua(const char* name, lua_CFunction fn) {
m_luaVM.bindCFunction(name, fn);
}
private:
MouseState m_lastMouse = {{0,0},false};
ActionMap m_gameFlowSteps;
void genericDispatch(const char* event); // events that are mapped to actions that are ui.json wide / game flow steps
void dialogDispatch(const char* event) { // events that are mapped to actions for particular dialogs
if (m_stack.size()) {
if (current().m_actionMap.count(event))
for (auto action : current().m_actionMap[event])
dialogRedirect(action);
}
}
Dialog& current() { return *m_stack.back(); }
std::vector<Dialog*> m_stack;
bool m_needsUpdate = true;
typedef std::map<std::string, Dialog*> DialogMap;
DialogMap m_dialogMap;
float m_t = 0.0;
LuaVM m_luaVM;
UpdateState m_state;
};
}
#endif // DIALOG_H