From aa5522497beb0c1b4130b7595e3ee61a18e13168 Mon Sep 17 00:00:00 2001 From: Daniel Evans Date: Thu, 26 Dec 2013 23:18:55 +0000 Subject: [PATCH] Initial game state tracking --- tests/test_state.cpp | 59 +++++++++++++++++ viewer/MenuSystem.hpp | 4 +- viewer/State.hpp | 109 ++++++++++++++++++++++++++++++ viewer/main.cpp | 149 +++++++++++++++++++++++++++++++++++------- 4 files changed, 294 insertions(+), 27 deletions(-) create mode 100644 tests/test_state.cpp create mode 100644 viewer/State.hpp diff --git a/tests/test_state.cpp b/tests/test_state.cpp new file mode 100644 index 00000000..fc1b7288 --- /dev/null +++ b/tests/test_state.cpp @@ -0,0 +1,59 @@ +#include +#include "test_globals.hpp" +#include + +BOOST_AUTO_TEST_SUITE(StateUnitTests) + +BOOST_AUTO_TEST_CASE(state_test_generic) +{ + bool entered = false; + bool exited = false; + bool ticked = false; + + GenericState ls( + [&](State*) { entered = true; }, + [&](State*, float) { ticked = true; }, + [&](State*) { exited = true; }, + [](State*, const sf::Event&){} + ); + + ls.enter(); + + BOOST_CHECK( entered ); + + ls.tick(1.f); + + BOOST_CHECK( ticked ); + + ls.exit(); + + BOOST_CHECK( exited ); +} + +BOOST_AUTO_TEST_CASE(state_test_switch) +{ + bool entered = false; + bool exited = false; + bool ticked = false; + + GenericState ls( + [&](State*) { entered = true; }, + [&](State*, float) { ticked = true; }, + [&](State*) { exited = true; }, + [](State*, const sf::Event&){} + ); + + StateManager::get().enter(&ls); + + BOOST_CHECK( entered ); + + StateManager::get().tick(1.f); + + BOOST_CHECK( ticked ); + + StateManager::get().exit(); + + BOOST_CHECK( exited ); +} + +BOOST_AUTO_TEST_SUITE_END() diff --git a/viewer/MenuSystem.hpp b/viewer/MenuSystem.hpp index b81cabdf..ce9ed8e8 100644 --- a/viewer/MenuSystem.hpp +++ b/viewer/MenuSystem.hpp @@ -20,7 +20,7 @@ public: MenuEntry(const std::string& n) : name(n) {} - float getHeight() { return 50.f; } + float getHeight() { return 30.f; } virtual void draw(const sf::Font& font, sf::RenderWindow& window, sf::Vector2f& basis) { @@ -29,7 +29,7 @@ public: t.setPosition(basis); t.setString(name); window.draw(t); - basis.y += 50.f; + basis.y += getHeight(); } virtual void activate(sf::Vector2f click) = 0; diff --git a/viewer/State.hpp b/viewer/State.hpp new file mode 100644 index 00000000..ff523e15 --- /dev/null +++ b/viewer/State.hpp @@ -0,0 +1,109 @@ +#ifndef _GAME_STATE_HPP_ +#define _GAME_STATE_HPP_ +#include +#include +#include +#include "MenuSystem.hpp" + +struct State +{ + // Helper for global menu behaviour + Menu* currentMenu; + + State() + : currentMenu(nullptr) {} + + virtual void enter() = 0; + virtual void exit() = 0; + + virtual void tick(float dt) = 0; + + virtual void draw(sf::RenderWindow& w) + { + if(currentMenu) { + currentMenu->draw(w); + } + } + + void enterMenu(Menu* menu) + { + currentMenu = menu; + } + + virtual void handleEvent(const sf::Event& e) + { + switch(e.type) { + case sf::Event::MouseButtonReleased: + if(currentMenu) { + currentMenu->click(e.mouseButton.x, e.mouseButton.y); + } + break; + default: break; + }; + } +}; + +struct GenericState : public State +{ + typedef std::function StateChange; + typedef std::function Tick; + typedef std::function Event; + + StateChange enter_lambda; + Tick tick_lambda; + StateChange exit_lambda; + Event event_lambda; + + GenericState(StateChange start, Tick think, StateChange end, Event event) + : enter_lambda(start), tick_lambda(think), + exit_lambda(end), event_lambda(event) {} + + virtual void enter() { enter_lambda(this); } + virtual void exit() { exit_lambda(this); } + + virtual void tick(float dt) { tick_lambda(this, dt); } + + virtual void handleEvent(const sf::Event& event) { + event_lambda(this, event); + State::handleEvent(event); + } +}; + +struct StateManager +{ + static StateManager& get() + { + static StateManager m; + return m; + } + + std::deque states; + + void enter(State* state) + { + states.push_back(state); + state->enter(); + } + + void tick(float dt) + { + states.back()->tick(dt); + } + + void draw(sf::RenderWindow& w) + { + states.back()->draw(w); + } + + void exit() + { + // TODO: Resole states being leaked. + states.back()->exit(); + states.pop_back(); + if(states.back()) { + states.back()->enter(); + } + } +}; + +#endif \ No newline at end of file diff --git a/viewer/main.cpp b/viewer/main.cpp index dbdc69d4..cda8a7cb 100644 --- a/viewer/main.cpp +++ b/viewer/main.cpp @@ -15,11 +15,13 @@ #include #include "MenuSystem.hpp" +#include "State.hpp" #include #include #include #include +#include constexpr int WIDTH = 800, HEIGHT = 600; @@ -46,7 +48,7 @@ int debugMode = 0; sf::Font font; -bool showControls = true; +bool showControls = false; bool hitWorldRay(glm::vec3& hit, glm::vec3& normal, GTAObject** object = nullptr) { @@ -73,6 +75,12 @@ bool hitWorldRay(glm::vec3& hit, glm::vec3& normal, GTAObject** object = nullptr return false; } +void lockCursor(bool lock) +{ + mouseGrabbed = lock; + window.setMouseCursorVisible(! lock); +} + // Commands. std::map> Commands = { {"pedestrian-vehicle", @@ -266,12 +274,6 @@ void handleGlobalEvent(sf::Event &event) { switch (event.type) { case sf::Event::KeyPressed: - switch (event.key.code) { - case sf::Keyboard::Escape: - window.close(); - break; - default: break; - } break; case sf::Event::GainedFocus: inFocus = true; @@ -297,8 +299,7 @@ void handleInputEvent(sf::Event &event) } break; case sf::Keyboard::M: - mouseGrabbed = ! mouseGrabbed; - window.setMouseCursorVisible(! mouseGrabbed); + lockCursor(! mouseGrabbed); break; case sf::Keyboard::P: debugMode+=1; @@ -383,7 +384,9 @@ void handleCommandEvent(sf::Event &event) command("object-info"); break; break; + default: break; } + default: break; } } @@ -631,6 +634,110 @@ void render() } } +GenericState pauseState( + [](State* self) + { + Menu *m = new Menu(font); + m->offset = sf::Vector2f(50.f, 100.f); + m->addEntry(Menu::lambda("Continue", [] { StateManager::get().exit(); })); + m->addEntry(Menu::lambda("Options", [] { std::cout << "Options" << std::endl; })); + m->addEntry(Menu::lambda("Exit", [] { window.close(); })); + self->currentMenu = m; + lockCursor(false); + }, + [](State* self, float dt) + { + + }, + [](State* self) + { + delete self->currentMenu; + }, + [](State* self, const sf::Event& e) + { + switch(e.type) { + case sf::Event::KeyPressed: + switch(e.key.code) { + case sf::Keyboard::Escape: + StateManager::get().exit(); + break; + default: break; + } + break; + default: break; + } + } +); + +GenericState gameState( + [](State* self) + { + lockCursor(true); + // TODO: create game state object + // so we can track if we already + // Started or not. + if(! player) { + command("player"); + } + }, + [](State* self, float dt) + { + + }, + [](State* self) + { + + }, + [](State* self, const sf::Event& e) + { + switch(e.type) { + case sf::Event::KeyPressed: + switch(e.key.code) { + case sf::Keyboard::Escape: + StateManager::get().enter(&pauseState); + break; + default: break; + } + break; + default: break; + } + } +); + +GenericState menuState( + [](State* self) + { + Menu *m = new Menu(font); + m->offset = sf::Vector2f(50.f, 100.f); + m->addEntry(Menu::lambda("Test", [] { StateManager::get().enter(&gameState); })); + m->addEntry(Menu::lambda("Options", [] { std::cout << "Options" << std::endl; })); + m->addEntry(Menu::lambda("Exit", [] { window.close(); })); + self->currentMenu = m; + lockCursor(false); + }, + [](State* self, float dt) + { + + }, + [](State* self) + { + delete self->currentMenu; + }, + [](State* self, const sf::Event& e) + { + switch(e.type) { + case sf::Event::KeyPressed: + switch(e.key.code) { + case sf::Keyboard::Escape: + StateManager::get().exit(); + default: break; + } + break; + default: break; + } + } +); + int main(int argc, char *argv[]) { if (argc < 2) { @@ -664,7 +771,7 @@ int main(int argc, char *argv[]) sf::ContextSettings cs; cs.depthBits = 32; - window.create(sf::VideoMode(w, h), "GTA3 Viewer", sf::Style::Close, cs); + window.create(sf::VideoMode(w, h), "", sf::Style::Default, cs); window.setVerticalSyncEnabled(true); window.setMouseCursorVisible(false); @@ -672,29 +779,20 @@ int main(int argc, char *argv[]) sf::Clock clock; - /*Menu mainMenu(font); - mainMenu.offset = sf::Vector2f(50.f, 100.f); - mainMenu.addEntry(Menu::lambda("Test", [] { std::cout << "Test" << std::endl; })); - mainMenu.addEntry(Menu::lambda("Options", [] { std::cout << "Options" << std::endl; })); - mainMenu.addEntry(Menu::lambda("Exit", [] { window.close(); }));*/ + StateManager::get().enter(&menuState); float accum = 0.f; float ts = 1.f / 60.f; - while (window.isOpen()) { + // Loop until the window is closed or we run out of state. + while (window.isOpen() && StateManager::get().states.size()) { sf::Event event; while (window.pollEvent(event)) { handleGlobalEvent(event); handleCommandEvent(event); handleInputEvent(event); - if(! mouseGrabbed) { - switch(event.type) { - case sf::Event::MouseButtonPressed: - mainMenu.click(event.mouseButton.x, event.mouseButton.y); - break; - } - } + StateManager::get().states.back()->handleEvent(event); } accum += clock.restart().asSeconds(); @@ -705,8 +803,9 @@ int main(int argc, char *argv[]) } render(); - - mainMenu.draw(window); + + StateManager::get().draw(window); + window.display(); }