diff --git a/dungeon.vcxproj b/dungeon.vcxproj index 89e7d94..049998e 100644 --- a/dungeon.vcxproj +++ b/dungeon.vcxproj @@ -116,6 +116,7 @@ + @@ -131,7 +132,6 @@ - @@ -139,11 +139,11 @@ - - - - - + + + + + @@ -156,9 +156,11 @@ + + @@ -178,7 +180,6 @@ - @@ -187,11 +188,11 @@ - - - - - + + + + + @@ -203,16 +204,18 @@ - - - + + + + + diff --git a/dungeon.vcxproj.filters b/dungeon.vcxproj.filters index 366940c..4a58cee 100644 --- a/dungeon.vcxproj.filters +++ b/dungeon.vcxproj.filters @@ -54,9 +54,6 @@ Source Files - - Source Files - Source Files @@ -78,21 +75,6 @@ Source Files - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - Source Files @@ -144,6 +126,30 @@ Source Files + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + @@ -188,9 +194,6 @@ Header Files - - Header Files - Header Files @@ -215,21 +218,6 @@ Header Files - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - Header Files @@ -260,15 +248,6 @@ Header Files - - Header Files - - - Header Files - - - Header Files - Header Files @@ -302,5 +281,35 @@ Header Files + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + \ No newline at end of file diff --git a/src/App.cpp b/src/App.cpp index 0933ba8..820ec1b 100644 --- a/src/App.cpp +++ b/src/App.cpp @@ -9,11 +9,12 @@ #include #include #include "Renderer.h" -#include "imgui.h" +#include "imgui/imgui.h" #include "Input.h" -#include "Gamestate.h" #include "PlayState.h" #include "LuaHandler.h" +#include "Time.h" +#include Uint64 perfFreq; double currTime() { @@ -108,12 +109,8 @@ int App::start() { double accumulator = dt; bool show_log = false; - bool running = true; - Gamestate* nextstate = nullptr; - - current = new PlayState(); - current->init(this); - current->load(); + running = true; + statestack.push(std::unique_ptr(new PlayState(this))); while (running) { double newTime = currTime(); @@ -122,19 +119,12 @@ int App::start() { frametime = 1.0/30; } - if (nextstate != nullptr) { - current->quit(); - delete current; - current = nextstate; - current->init(this); - current->load(); - } - currentTime = newTime; accumulator += frametime; SDL_Event ev{}; ImGuiIO &io = ImGui::GetIO(); + renderer->ImguiNewFrame(); while (SDL_PollEvent(&ev)) { //renderer->ImguiProcessEvents(&ev); @@ -154,31 +144,31 @@ int App::start() { case SDL_MOUSEMOTION: if (!io.WantCaptureMouse) { InputEvent inputEvent = input->set_mouse_pos(ev.motion.x, ev.motion.y, ev.motion.xrel, ev.motion.yrel); - current->inputevent(&inputEvent); + statestack.input(inputEvent); } break; case SDL_MOUSEBUTTONDOWN: if (!io.WantCaptureMouse) { InputEvent inputEvent = input->set_mouse_button(ev.button.button, ev.button.x, ev.button.y, true); - current->inputevent(&inputEvent); + statestack.input(inputEvent); } break; case SDL_MOUSEBUTTONUP: if (!io.WantCaptureMouse) { InputEvent inputEvent = input->set_mouse_button(ev.button.button, ev.button.x, ev.button.y, false); - current->inputevent(&inputEvent); + statestack.input(inputEvent); } break; case SDL_KEYDOWN: if (!io.WantCaptureKeyboard) { InputEvent inputEvent = input->set_key(ev.key.keysym.sym, (SDL_Keymod) ev.key.keysym.mod, true); - current->inputevent(&inputEvent); + statestack.input(inputEvent); } break; case SDL_KEYUP: if (!io.WantCaptureKeyboard){ InputEvent inputEvent = input->set_key(ev.key.keysym.sym, (SDL_Keymod) ev.key.keysym.mod, false); - current->inputevent(&inputEvent); + statestack.input(inputEvent); } break; case SDL_QUIT: @@ -189,13 +179,13 @@ int App::start() { } } while (running && accumulator >= dt) { - nextstate = current->update(dt); + statestack.update(dt); input->new_frame(); - + Time::tick_timers(dt); accumulator -= dt; } renderer->Clear(); - current->draw(accumulator / dt); + statestack.draw(); renderer->Present(); SDL_Delay(1); } diff --git a/src/App.h b/src/App.h index a5f0f04..03a7474 100644 --- a/src/App.h +++ b/src/App.h @@ -5,7 +5,8 @@ #ifndef DUNGEON_APP_H #define DUNGEON_APP_H -class Gamestate; +#include "statemachine/StateStack.h" + class Renderer; class Input; @@ -14,9 +15,10 @@ class Input; #define ADD_QUOTES(s) ADD_QUOTES_HELPER(s) class App { + bool running = true; public: const char* version = ADD_QUOTES(GIT_CUR_COMMIT); - Gamestate* current; + StateStack statestack; Renderer* renderer; Input* input; bool init(); diff --git a/src/Gamestate.cpp b/src/Gamestate.cpp deleted file mode 100644 index 152ff53..0000000 --- a/src/Gamestate.cpp +++ /dev/null @@ -1,10 +0,0 @@ -// -// Created by Adrian on 2017-09-19. -// - -#include "Gamestate.h" -#include "App.h" - -void Gamestate::init(App *app) { - this->app = app; -} diff --git a/src/Gamestate.h b/src/Gamestate.h deleted file mode 100644 index 69c0211..0000000 --- a/src/Gamestate.h +++ /dev/null @@ -1,25 +0,0 @@ -// -// Created by Adrian on 2017-09-19. -// - -#ifndef DUNGEON_GAMESTATE_H -#define DUNGEON_GAMESTATE_H - -class App; -struct InputEvent; - -class Gamestate { -protected: - App* app; -public: - //virtual ~Gamestate() {}; - void init(App* app); - virtual void load() = 0; - virtual Gamestate* update(double delta) = 0; - virtual void draw(double delta) = 0; - virtual void quit() = 0; - virtual void inputevent(InputEvent* event) = 0; -}; - - -#endif //DUNGEON_GAMESTATE_H diff --git a/src/Logger.cpp b/src/Logger.cpp new file mode 100644 index 0000000..bf96a23 --- /dev/null +++ b/src/Logger.cpp @@ -0,0 +1,39 @@ +#include "Logger.h" +#include +#include + +Log::LogLevel log_level = Log::LogLevel::Warning; + +void Log::set_log_level(LogLevel level) { + log_level = level; +} + +void Log::error(string tag, string message) { + if (log_level <= LogLevel::Error) { + printf("[ERR] <%s> %s\n", tag, message); + } +} + +void Log::warning(string tag, string message) { + if (log_level <= LogLevel::Warning) { + printf("[WRN] <%s> %s\n", tag, message); + } +} + +void Log::info(string tag, string message) { + if (log_level <= LogLevel::Info) { + printf("[INF] <%s> %s\n", tag, message); + } +} + +void Log::debug(string tag, string message) { + if (log_level <= LogLevel::Debug) { + printf("[DBG] <%s> %s\n", tag, message); + } +} + +void Log::trace(string tag, string message) { + if (log_level <= LogLevel::Trace) { + printf("[TRC] <%s> %s\n", tag, message); + } +} diff --git a/src/Logger.h b/src/Logger.h index a00b49d..ccc175a 100644 --- a/src/Logger.h +++ b/src/Logger.h @@ -1,5 +1,23 @@ #pragma once +#include -namespace Logger { +namespace Log { + using std::string; -} \ No newline at end of file + enum LogLevel { + Error, + Warning, + Info, + Debug, + Trace, + }; + + void set_log_level(LogLevel level); + //void enable_log_to_file(bool enable); + //void set_log_path(string path); + void error(string tag, string message); + void warning(string tag, string message); + void info(string tag, string message); + void debug(string tag, string message); + void trace(string tag, string message); +} diff --git a/src/PlayState.cpp b/src/PlayState.cpp index a9b5c5f..fe555be 100644 --- a/src/PlayState.cpp +++ b/src/PlayState.cpp @@ -10,7 +10,7 @@ #include "SpriteAtlas.h" #include "Mapgen.h" #include "FieldOfView.h" -#include "imgui.h" +#include "imgui/imgui.h" #include "Hero.h" #include "Goblin.h" #include "Shaman.h" @@ -23,13 +23,14 @@ InputAction player_action; TileSet tileset; LuaHandler handler; -void PlayState::load() { +void PlayState::enter() { SDL_LogVerbose(SDL_LOG_CATEGORY_SYSTEM, "Creating ascii tileset...\n"); ascii = new SpriteAtlas(app->renderer, "./assets/12x12.bmp", 192, 192, 12, 12); SDL_LogVerbose(SDL_LOG_CATEGORY_SYSTEM, "Created ascii tileset.\n"); handler.load_module("data", &tileset); + // TODO: Load all this from Lua app->input->bind_key(SDLK_ESCAPE, ACTION_ESCAPE_MENU); // Movement: keypad @@ -71,6 +72,10 @@ void PlayState::load() { new_game(); } +PlayState::PlayState(App * app) { + this->app = app; +} + void PlayState::new_game() { player_action = ACTION_NONE; @@ -104,7 +109,7 @@ void PlayState::new_game() { } } -Gamestate *PlayState::update(double delta) { +StateResult PlayState::update(float delta) { while (!is_player_turn || player_action != ACTION_NONE) { std::vector* actors = tilemap->get_actor_list(); Actor* actor = actors->at(current_entity_index); @@ -128,14 +133,17 @@ Gamestate *PlayState::update(double delta) { if (tilemap->get_tile(pos.x, pos.y).has_tag("exit")) { current_level++; tilemap = &world.GetMap(current_level, tileset); - return nullptr; + return StateResult::None(); } else { - return nullptr; + return StateResult::None(); } break; } - default: player_action = ACTION_NONE; SDL_LogVerbose(SDL_LOG_CATEGORY_SYSTEM, "Turn aborted: no player action.\n"); return nullptr; // abort turn + default: + player_action = ACTION_NONE; + SDL_LogVerbose(SDL_LOG_CATEGORY_SYSTEM, "Turn aborted: no player action.\n"); + return StateResult::None(); // abort turn } if (dir != vec2i(0,0)) { if (!actor->move(dir.x, dir.y, tilemap)) { @@ -152,7 +160,7 @@ Gamestate *PlayState::update(double delta) { if(!attacked) { SDL_LogVerbose(SDL_LOG_CATEGORY_SYSTEM, "Turn aborted: invalid player action.\n"); player_action = ACTION_NONE; - return nullptr; // unable to move and nothing to attack == abort turn + return StateResult::None(); // unable to move and nothing to attack == abort turn } } } @@ -173,10 +181,10 @@ Gamestate *PlayState::update(double delta) { fov.calc(player_actor->get_position(), 6); } } - return nullptr; + return StateResult::None(); } -void PlayState::draw(double delta) { +void PlayState::draw() { if (debug) { { ImGui::BeginMainMenuBar(); @@ -286,17 +294,17 @@ void PlayState::draw(double delta) { } } -void PlayState::quit() { +void PlayState::exit() { } -void PlayState::inputevent(InputEvent *event) { - if (event->type == INPUT_KEY_EVENT && event->pressed) { - switch (event->action) { +void PlayState::input(InputEvent &event) { + if (event.type == INPUT_KEY_EVENT && event.pressed) { + switch (event.action) { case ACTION_TOGGLE_DEBUG: debug = !debug; break; case ACTION_RESET: new_game(); break; case ACTION_ESCAPE_MENU: break; // TODO case ACTION_NONE: break; - default: player_action = event->action; break; + default: player_action = event.action; break; } } } diff --git a/src/PlayState.h b/src/PlayState.h index a6465cf..ca63db9 100644 --- a/src/PlayState.h +++ b/src/PlayState.h @@ -1,14 +1,15 @@ #pragma once -#include "Gamestate.h" +#include "statemachine/StateStack.h" #include "Tilemap.h" #include "FieldOfView.h" #include "World.h" class SpriteAtlas; class Actor; +class App; -class PlayState : public Gamestate { +class PlayState : public IState { SpriteAtlas* ascii; World world; Tilemap* tilemap; @@ -25,11 +26,14 @@ class PlayState : public Gamestate { bool debug_settings = false; bool debug_disable_fov = false; + App* app; + public: + PlayState(App* app); void new_game(); - void load() override; - Gamestate* update(double delta) override; - void draw(double delta) override; - void quit() override; - void inputevent(InputEvent* event) override; + void enter() override; + StateResult update(float delta) override; + void draw() override; + void exit() override; + void input(InputEvent& event) override; }; diff --git a/src/Renderer.cpp b/src/Renderer.cpp index a73d5f4..3be6024 100644 --- a/src/Renderer.cpp +++ b/src/Renderer.cpp @@ -6,8 +6,8 @@ #include #include -#include "imgui.h" -#include "imgui_impl_sdl_gl3.h" +#include "imgui/imgui.h" +#include "imgui/imgui_impl_sdl_gl3.h" #include #include #include diff --git a/src/Time.cpp b/src/Time.cpp new file mode 100644 index 0000000..bab77b2 --- /dev/null +++ b/src/Time.cpp @@ -0,0 +1,67 @@ +#include "Time.h" +#include + +std::vector timers; + +void Time::Timer::tick(float dt) { + time_current += dt; + if (continuous) { + callback(); + } + else if (time_current >= duration) { + callback(); + if (loop && !is_finished()) { + count++; + time_current -= duration; + } + } +} + +bool Time::Timer::is_finished() { + return (loop && count >= limit) || (!loop && time_current >= duration); +} + +void Time::tick_timers(float dt) { + for (int i = timers.size() - 1; i >= 0; i--) { + timers[i].tick(dt); + if (timers[i].is_finished()) { + timers.erase(timers.begin() + i); + } + } +} + +void Time::after(float seconds, TimerCallback callback) { + timers.emplace_back(Timer { + false, + false, + 0, + 0, + 0, + seconds, + callback, + }); +} + +void Time::every(float seconds, int limit, TimerCallback callback) { + timers.emplace_back(Timer{ + true, + false, + limit, + 0, + 0, + seconds, + callback, + }); +} + +void Time::during(float seconds, TimerCallback callback) { + timers.emplace_back(Timer{ + false, + true, + 0, + 0, + 0, + seconds, + callback, + }); +} diff --git a/src/Time.h b/src/Time.h new file mode 100644 index 0000000..37906bd --- /dev/null +++ b/src/Time.h @@ -0,0 +1,23 @@ +#pragma once + +#include + +namespace Time { + typedef std::function TimerCallback; + struct Timer { + bool loop; + bool continuous; + int limit; + int count; + float time_current; + float duration; + TimerCallback callback; + void tick(float dt); + bool is_finished(); + }; + + void tick_timers(float dt); + void after(float seconds, TimerCallback callback); + void every(float seconds, int limit, TimerCallback callback); + void during(float seconds, TimerCallback callback); +} diff --git a/src/imconfig.h b/src/imgui/imconfig.h similarity index 100% rename from src/imconfig.h rename to src/imgui/imconfig.h diff --git a/src/imgui.cpp b/src/imgui/imgui.cpp similarity index 100% rename from src/imgui.cpp rename to src/imgui/imgui.cpp diff --git a/src/imgui.h b/src/imgui/imgui.h similarity index 100% rename from src/imgui.h rename to src/imgui/imgui.h diff --git a/src/imgui_demo.cpp b/src/imgui/imgui_demo.cpp similarity index 100% rename from src/imgui_demo.cpp rename to src/imgui/imgui_demo.cpp diff --git a/src/imgui_draw.cpp b/src/imgui/imgui_draw.cpp similarity index 100% rename from src/imgui_draw.cpp rename to src/imgui/imgui_draw.cpp diff --git a/src/imgui_impl_sdl_gl3.cpp b/src/imgui/imgui_impl_sdl_gl3.cpp similarity index 100% rename from src/imgui_impl_sdl_gl3.cpp rename to src/imgui/imgui_impl_sdl_gl3.cpp diff --git a/src/imgui_impl_sdl_gl3.h b/src/imgui/imgui_impl_sdl_gl3.h similarity index 100% rename from src/imgui_impl_sdl_gl3.h rename to src/imgui/imgui_impl_sdl_gl3.h diff --git a/src/imgui_internal.h b/src/imgui/imgui_internal.h similarity index 100% rename from src/imgui_internal.h rename to src/imgui/imgui_internal.h diff --git a/src/imgui_user.cpp b/src/imgui/imgui_user.cpp similarity index 100% rename from src/imgui_user.cpp rename to src/imgui/imgui_user.cpp diff --git a/src/imgui_user.h b/src/imgui/imgui_user.h similarity index 100% rename from src/imgui_user.h rename to src/imgui/imgui_user.h diff --git a/src/stb_rect_pack.h b/src/imgui/stb_rect_pack.h similarity index 100% rename from src/stb_rect_pack.h rename to src/imgui/stb_rect_pack.h diff --git a/src/stb_textedit.h b/src/imgui/stb_textedit.h similarity index 100% rename from src/stb_textedit.h rename to src/imgui/stb_textedit.h diff --git a/src/stb_truetype.h b/src/imgui/stb_truetype.h similarity index 100% rename from src/stb_truetype.h rename to src/imgui/stb_truetype.h diff --git a/src/statemachine/StateStack.cpp b/src/statemachine/StateStack.cpp new file mode 100644 index 0000000..f2976ed --- /dev/null +++ b/src/statemachine/StateStack.cpp @@ -0,0 +1,69 @@ +#include "StateStack.h" + +void StateStack::update(float dt) { + update_state(stack.size() - 1, dt); +} + +void StateStack::draw() { + draw_state(stack.size() - 1); +} + +void StateStack::push(std::unique_ptr state) { + state->enter(); + stack.emplace_back(std::move(state)); +} + +void StateStack::swap(std::unique_ptr state) { + pop(); + push(std::move(state)); +} + +void StateStack::pop() { + stack.back()->exit(); + stack.pop_back(); +} + +void StateStack::input(InputEvent & input_event) { + input_state(stack.size() - 1, input_event); +} + +void StateStack::draw_state(unsigned int i) { + IState &state = *stack[i]; + if (state.should_draw_previous() && i > 0) { + draw_state(i - 1); + } + state.draw(); +} + +void StateStack::update_state(unsigned int i, float dt) { + IState &state = *stack[i]; + if (state.should_update_previous() && i > 0) { + update_state(i - 1, dt); + } + StateResult r = state.update(dt); + + + if (r.action != StateAction::None && i+1 != stack.size()) { + while (i < stack.size()) { + pop(); + } + } + switch (r.action) { + case StateAction::Pop: + pop(); + case StateAction::Push: + push(std::move(r.state.value())); + case StateAction::Swap: + swap(std::move(r.state.value())); + default: + break; + } +} + +void StateStack::input_state(unsigned int i, InputEvent & input_event) { + IState &state = *stack[i]; + if (state.should_update_previous() && i > 0) { + input_state(i - 1, input_event); + } + state.input(input_event); +} diff --git a/src/statemachine/StateStack.h b/src/statemachine/StateStack.h new file mode 100644 index 0000000..a09b8e2 --- /dev/null +++ b/src/statemachine/StateStack.h @@ -0,0 +1,66 @@ +#pragma once + +#include +#include +#include + +#include "../Input.h" + +class StateStack; +class IState; + +enum class StateAction { + None, + Push, + Pop, + Swap, +}; + +struct StateResult { + StateResult(StateAction action, std::optional> state) { + this->action = action; + this->state.swap(state); + } + + StateAction action; + std::optional> state; + + static StateResult None() { + return StateResult(StateAction::None, nullptr); + } + + static StateResult Pop() { + return StateResult(StateAction::Pop, nullptr); + } +}; + + class IState { +public: + IState() = default; + virtual ~IState() = default; + virtual void enter() = 0; + virtual void exit() = 0; + virtual StateResult update(float dt) = 0; + virtual bool should_update_previous() { return false; }; + virtual void draw() = 0; + virtual bool should_draw_previous() { return false; }; + virtual void input(InputEvent &const event) = 0; + virtual bool should_input_previous() { return false; }; +}; + +class StateStack { +public: + StateStack() = default; + ~StateStack() = default; + void update(float dt); + void draw(); + void push(std::unique_ptr state); + void swap(std::unique_ptr state); + void pop(); + void input(InputEvent& input_event); +private: + void draw_state(unsigned int i); + void update_state(unsigned int i, float dt); + void input_state(unsigned int i, InputEvent &input_event); + std::vector> stack; +};