From 2e299dbcfa52c1489de27bf17c89fa96be1ea1b4 Mon Sep 17 00:00:00 2001 From: Adrian Hedqvist Date: Thu, 21 Sep 2017 18:57:12 +0200 Subject: [PATCH] Big refactor, FSM for game state, input event callbacks --- .gitignore | 1 + src/App.cpp | 160 +++++++++++++++++++++++++++ src/App.h | 24 +++++ src/Game.cpp | 265 --------------------------------------------- src/Game.h | 8 -- src/Gamestate.cpp | 10 ++ src/Gamestate.h | 24 +++++ src/Input.cpp | 69 ++++++++---- src/Input.h | 51 ++++++++- src/Main.cpp | 141 ++---------------------- src/PlayState.cpp | 268 ++++++++++++++++++++++++++++++++++++++++++++++ src/PlayState.h | 35 ++++++ 12 files changed, 624 insertions(+), 432 deletions(-) create mode 100644 src/App.cpp create mode 100644 src/App.h delete mode 100644 src/Game.cpp delete mode 100644 src/Game.h create mode 100644 src/Gamestate.cpp create mode 100644 src/Gamestate.h create mode 100644 src/PlayState.cpp create mode 100644 src/PlayState.h diff --git a/.gitignore b/.gitignore index b0db429..4e7f5cf 100644 --- a/.gitignore +++ b/.gitignore @@ -32,3 +32,4 @@ build *.log imgui.ini *.sublime* +*.dll diff --git a/src/App.cpp b/src/App.cpp new file mode 100644 index 0000000..22b9c98 --- /dev/null +++ b/src/App.cpp @@ -0,0 +1,160 @@ +// +// Created by Adrian on 2017-09-19. +// + +#include "App.h" +#include +#include +#include "Config.h" +#include "Renderer.h" +#include "imgui.h" +#include "Input.h" +#include "Gamestate.h" +#include "PlayState.h" + +Uint64 perfFreq; +double currTime() { + return SDL_GetPerformanceCounter() / (double)perfFreq; +} + +bool App::init() { + //setenv("MESA_DEBUG", "", 0); + + Config cfg = Config("dungeon.cfg"); + cfg.load(); + + int windowWidth = cfg.getInt("ResolutionX", 1280); + int windowHeight = cfg.getInt("ResolutionY", 720); + bool vsync = cfg.getBool("VSync", false); + bool wireframe = cfg.getBool("Wireframes", false); + + cfg.save(); + + int err = 0; + err = SDL_Init(SDL_INIT_TIMER | SDL_INIT_VIDEO); + SDL_LogSetAllPriority(SDL_LOG_PRIORITY_DEBUG); + if (err != 0) { + const char* error = SDL_GetError(); + fprintf(stderr, error); + SDL_Quit(); + return false; + } + SDL_LogVerbose(SDL_LOG_CATEGORY_SYSTEM,"SDL initialized.\n"); + + renderer = new Renderer(); + if (!renderer->Init("Dungeon", windowWidth, windowHeight)) { + const char* error = SDL_GetError(); + SDL_LogCritical(SDL_LOG_CATEGORY_SYSTEM, "%s", error); + SDL_ShowSimpleMessageBox(0, "Error", error, nullptr); + SDL_Quit(); + return false; + } + + renderer->SetVSyncEnabled(vsync); + renderer->SetWireframesEnabled(wireframe); + + input = new Input(); + srand(static_cast(time(nullptr))); + perfFreq = SDL_GetPerformanceFrequency(); + return true; +} + +int App::start() { + current = new PlayState(); + current->init(this); + current->load(); + + double dt = 1.0/30; + double currentTime = currTime(); + double accumulator = dt; + + bool running = true; + Gamestate* nextstate = nullptr; + + while (running) { + double newTime = currTime(); + double frametime = newTime - currentTime; + if (frametime > 1.0/30) { + 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); + switch (ev.type) { + case SDL_WINDOWEVENT: + switch (ev.window.event) { + case SDL_WINDOWEVENT_CLOSE: + running = false; + break; + default: + break; + } + break; + 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); + } + 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); + } + 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); + } + break; + case SDL_KEYDOWN: + if (!io.WantCaptureKeyboard) { + InputEvent inputEvent = input->setkey(ev.key.keysym.sym, (SDL_Keymod)ev.key.keysym.mod, true); + current->inputevent(&inputEvent); + } + break; + case SDL_KEYUP: + if (!io.WantCaptureKeyboard){ + InputEvent inputEvent = input->setkey(ev.key.keysym.sym, (SDL_Keymod)ev.key.keysym.mod, false); + current->inputevent(&inputEvent); + } + break; + case SDL_QUIT: + running = false; + break; + default: + break; + } + } + while (running && accumulator >= dt) { + nextstate = current->update(dt); + input->newframe(); + + accumulator -= dt; + } + + renderer->Clear(); + current->draw(accumulator / dt); + renderer->Present(); + SDL_Delay(1); + } + delete renderer; + SDL_Quit(); + SDL_LogVerbose(SDL_LOG_CATEGORY_SYSTEM, "Quit."); + return 0; +} diff --git a/src/App.h b/src/App.h new file mode 100644 index 0000000..3676dc1 --- /dev/null +++ b/src/App.h @@ -0,0 +1,24 @@ +// +// Created by Adrian on 2017-09-19. +// + +#ifndef DUNGEON_APP_H +#define DUNGEON_APP_H + +#pragma once + +class Gamestate; +class Renderer; +class Input; + +class App { +public: + Gamestate* current; + Renderer* renderer; + Input* input; + bool init(); + int start(); +}; + + +#endif //DUNGEON_APP_H diff --git a/src/Game.cpp b/src/Game.cpp deleted file mode 100644 index f4ae676..0000000 --- a/src/Game.cpp +++ /dev/null @@ -1,265 +0,0 @@ -#include "Game.h" - -#include "imgui.h" -#include "Input.h" -#include "Renderer.h" - -#include "Tilemap.h" -#include "Tileset.h" -#include "Hero.h" -#include "Goblin.h" -#include "Shaman.h" - -const int mapwidth = 32; -const std::string map = -"# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #" -"# @ . . . # # # # # # # # . . . . . . . . . . . . . . . . . # #" -"# . . . . . . . . # # # # . # # . # # # # # # . # # # # # . # #" -"# . . . . # # # . . . . . . # . g . # # # # # . # # # . . g . #" -"# . . . . # # # # # # # # . # . . . # # . . . . . . . . . . . #" -"# # # . # # # # # # # # # . . . . g # # . # # # . # # . . g . #" -"# . . . . . . . . . . . . . # # # # # . . . # # . # # # # # # #" -"# . # # # # # # # # . # # . # # # # # . g . # # . # . . g . . #" -"# . . . . g # # . . . # . . . # # # # . . . # # . # . . . . . #" -"# . . g . . # # . # # # . s . . . # # # # # # # . . . . s . . #" -"# . . . . . # # . . . # . . . # . . . . . . . . . # . g . . . #" -"# # . # # # # # . # . # # # # # # # # # . # # # # # # # # . # #" -"# . . . . . . . . # . . . . . . . . . . . . . . . . . . . . . #" -"# . # # # # # # # # # # . # . # # # # # # # # # # . # # # # . #" -"# . . . . . . . . . . . . # . # . . . . # . . . . . # # # . . #" -"# # # # # # . # # # . # # # . # . . . . # . . . # . # # # . # #" -"# . . . . # . # . . . . . # . . . . . . . . . . # . # # . . . #" -"# . . . . # . # . . . . . # . # . . . . # # # # # . . . . . . #" -"# . . . . . . # . . . . . # . # # # # # # . . . . . # # . . . #" -"# . . . . # . # # # # # # # . . . . . . . . # # # # # # # # # #" -"# . . . . # . . . . . . . . . # # # # # # # # # # # # # # # # #" -"# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #" -"# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #" -"# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #" -"# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #" -"# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #" -"# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #" -"# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #" -"# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #" -"# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #" -"# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #" -"# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #" -"# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #"; - -Renderer* renderer; -Input* input; - -Tileset* ascii; -Tilemap* tilemap; -Hero* hero; - -bool paused; -bool debug = false; - -void init() { - paused = true; - - if (tilemap != nullptr) { - delete tilemap; - tilemap = nullptr; - hero = nullptr; - } - - if (hero != nullptr) { - delete hero; - hero = nullptr; - } - - SDL_LogVerbose(SDL_LOG_CATEGORY_SYSTEM, "Creating tilemap...\n"); - tilemap = new Tilemap(32, 32); - int y = 0; - int x = 0; - for (int i = 0; i < map.length(); i++) { - - if (y >= 32) { - break; - } - if (x >= mapwidth) { - y++; - x = 0; - } - if (map[i] == ' ' || map[i] == '\t' || map[i] == '\n') { - continue; - } - - if (map[i] == '@') { - hero = new Hero(tilemap, vec2i(x, y)); - tilemap->AddActor(hero); - tilemap->SetTile(x, y, '.'); - } - else if (map[i] == 'g') { - tilemap->AddActor(new Goblin(tilemap, vec2i(x, y))); - tilemap->SetTile(x, y, '.'); - } - else if (map[i] == 's') { - tilemap->AddActor(new Shaman(tilemap, vec2i(x, y))); - tilemap->SetTile(x, y, '.'); - } - else { - tilemap->SetTile(x, y, map[i]); - } - x++; - } - SDL_LogVerbose(SDL_LOG_CATEGORY_SYSTEM, "Done.\n"); - SDL_LogVerbose(SDL_LOG_CATEGORY_SYSTEM, "Calculating initial FOV...\n"); - hero->CalcFOV(); - SDL_LogVerbose(SDL_LOG_CATEGORY_SYSTEM, "Done.\n"); -} - -void load(Renderer* rend, Input* inp) { - renderer = rend; - input = inp; - SDL_LogVerbose(SDL_LOG_CATEGORY_SYSTEM, "Creating ascii tileset...\n"); - ascii = new Tileset(renderer, "./assets/12x12.bmp", 192, 192, 12, 12); - SDL_LogVerbose(SDL_LOG_CATEGORY_SYSTEM, "Created ascii tileset.\n"); - input->bindkey(SDLK_r, ACTION_RESET); - input->bindkey(SDLK_SPACE, ACTION_PAUSE); - input->bindkey(SDLK_RETURN, ACTION_STEP); - input->bindkey(SDLK_F1, ACTION_TOGGLE_DEBUG); - SDL_LogVerbose(SDL_LOG_CATEGORY_SYSTEM, "Keybinds bound.\n"); - init(); -} - -float delay = .5; -double timer; - -void update(double dt) { - if (input->wasJustPressed(ACTION_TOGGLE_DEBUG)) { - debug = !debug; - } - if (input->wasJustPressed(ACTION_PAUSE)) { - paused = !paused; - } - if (input->wasJustPressed(ACTION_RESET)) { - init(); - } - - timer += dt; - if (!paused || input->wasJustPressed(ACTION_STEP)) { - if (timer >= delay) { - timer = 0; - - auto actors = tilemap->GetActorList(); - for (Actor* var : *actors) { - var->Update(); - } - for (int i = (int)actors->size() - 1; i >= 0; i--) { - if (!actors->at(i)->alive) { - if (actors->at(i) == hero) { - hero = nullptr; - } - delete actors->at(i); - actors->erase(actors->begin() + i); - } - } - } - } -} - -void draw(double alpha) { - - - if (debug) { - bool wireframe = renderer->IsWireframesEnabled(); - ImGui::Checkbox("Wireframes", &wireframe); - renderer->SetWireframesEnabled(wireframe); - bool vsync = renderer->IsVSyncEnabled(); - ImGui::Checkbox("VSync", &vsync); - renderer->SetVSyncEnabled(vsync); - - ImGui::BeginMainMenuBar(); - - if (ImGui::BeginMenu("File")) { - ImGui::EndMenu(); - } - - if (ImGui::BeginMenu("Edit")) { - ImGui::EndMenu(); - } - - if (ImGui::BeginMenu("View")) { - ImGui::EndMenu(); - } - - ImGui::EndMainMenuBar(); - - ImGui::SliderFloat("turndelay", &delay, .01f, 1); - } - - - for (int x = 0; x < 32; x++) { - for (int y = 0; y < 32; y++) { - if (hero == nullptr || hero->HasSeen(x, y)) { - renderer->SetColor(1, 1, 1, 1); - renderer->DrawSprite(ascii->GetSprite(tilemap->GetTile(x, y)), x * 12, y * 12); - if (hero != nullptr && !hero->CanSee(x, y)) { - renderer->SetColor(0, 0, 0, .5f); - renderer->DrawSprite(ascii->GetSprite(219), x * 12, y * 12); - } - } - } - } - auto actors = tilemap->GetActorList(); - for (Actor* var : *actors) { - vec2i pos = var->getPosition(); - if (hero == nullptr || hero->CanSee(pos.x, pos.y)) { - renderer->SetColor(0, 0, 0, 255); - renderer->DrawSprite(ascii->GetSprite(219), pos.x * 12, pos.y * 12); - - int sprite; - switch (var->Type()) { - case ACT_HERO: - renderer->SetColor(.2f, .6f, 1, 1); - sprite = '@'; - break; - case ACT_GOBLIN: - renderer->SetColor(.6f, 1, .2f, 1); - sprite = 'g'; - break; - case ACT_SHAMAN: - renderer->SetColor(.2f, 1, .6f, 1); - sprite = 's'; - break; - default: - renderer->SetColor(1, 1, 1, 1); - sprite = 2; - break; - } - renderer->DrawSprite(ascii->GetSprite(sprite), pos.x * 12, pos.y * 12); - } - } - if (hero != nullptr) { - renderer->SetColor(155, 5, 5, 255); - for (int i = 0; i < hero->health; i++) { - renderer->SetColor(0, 0, 0, 255); - renderer->DrawSprite(ascii->GetSprite(219), i * 12, 0); - renderer->SetColor(255, 0, 0, 255); - renderer->DrawSprite(ascii->GetSprite(3), i * 12, 0); - } - } - if (paused) { - renderer->SetColor(255, 0, 0, 255); - renderer->DrawSprite(ascii->GetSprite(219), 12 * 0, 0); - renderer->DrawSprite(ascii->GetSprite(219), 12 * 1, 0); - renderer->DrawSprite(ascii->GetSprite(219), 12 * 2, 0); - renderer->DrawSprite(ascii->GetSprite(219), 12 * 3, 0); - renderer->DrawSprite(ascii->GetSprite(219), 12 * 4, 0); - renderer->DrawSprite(ascii->GetSprite(219), 12 * 5, 0); - renderer->DrawSprite(ascii->GetSprite(219), 12 * 6, 0); - renderer->DrawSprite(ascii->GetSprite(219), 12 * 7, 0); - renderer->SetColor(0, 0, 0, 255); - renderer->DrawSprite(ascii->GetSprite('-'), 12 * 0, 0); - renderer->DrawSprite(ascii->GetSprite('P'), 12 * 1, 0); - renderer->DrawSprite(ascii->GetSprite('A'), 12 * 2, 0); - renderer->DrawSprite(ascii->GetSprite('U'), 12 * 3, 0); - renderer->DrawSprite(ascii->GetSprite('S'), 12 * 4, 0); - renderer->DrawSprite(ascii->GetSprite('E'), 12 * 5, 0); - renderer->DrawSprite(ascii->GetSprite('D'), 12 * 6, 0); - renderer->DrawSprite(ascii->GetSprite('-'), 12 * 7, 0); - } -} diff --git a/src/Game.h b/src/Game.h deleted file mode 100644 index d5d3c2e..0000000 --- a/src/Game.h +++ /dev/null @@ -1,8 +0,0 @@ -#pragma once - -class Renderer; -class Input; - -void load(Renderer* rend, Input* inp); -void update(double dt); -void draw(double alpha); diff --git a/src/Gamestate.cpp b/src/Gamestate.cpp new file mode 100644 index 0000000..152ff53 --- /dev/null +++ b/src/Gamestate.cpp @@ -0,0 +1,10 @@ +// +// 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 new file mode 100644 index 0000000..6bb8beb --- /dev/null +++ b/src/Gamestate.h @@ -0,0 +1,24 @@ +// +// 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: + 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/Input.cpp b/src/Input.cpp index fa4623e..4e27a9e 100644 --- a/src/Input.cpp +++ b/src/Input.cpp @@ -16,17 +16,21 @@ Input::~Input() { } -void Input::newframe() -{ - for(int i = 0; i < INPUTACTION_END; i++) - { +void Input::newframe() { + for(int i = 0; i < INPUTACTION_END; i++) { justpressed[i] = false; justreleased[i] = false; } } -void Input::setkey(SDL_Keycode key, SDL_Keymod mod, bool pressed) +InputEvent Input::setkey(SDL_Keycode key, SDL_Keymod mod, bool pressed) { + InputEvent event; + event.type = INPUT_KEY_EVENT; + event.key_press_event.key = key; + event.key_press_event.mod = mod; + event.pressed = pressed; + if (mod & KMOD_NUM) { // let's ignore numlock mod = (SDL_Keymod)(mod ^ KMOD_NUM); } @@ -37,45 +41,66 @@ void Input::setkey(SDL_Keycode key, SDL_Keymod mod, bool pressed) if (binds.find(bind) != binds.end()) { InputAction action = binds[bind]; - - if (ispressed[action] != pressed) // Ignore keypress/release if it's already pressed/released, this disables repeats. - { + event.action = action; + event.key_press_event.echo = ispressed[action] != pressed; + if (event.key_press_event.echo) { // Ignore keypress/release if it's already pressed/released, this disables repeats. ispressed[action] = pressed; - if (pressed) - { + if (pressed) { justpressed[action] = true; } - else - { + else { justreleased[action] = true; } } } + return event; } -void Input::bindkey(SDL_Keycode key, InputAction action, SDL_Keymod mod) -{ +void Input::bindkey(SDL_Keycode key, InputAction action, SDL_Keymod mod) { Bind bind = { key, mod }; binds[bind] = action; } -void Input::unbindkey(SDL_Keycode key, SDL_Keymod mod) -{ +void Input::unbindkey(SDL_Keycode key, SDL_Keymod mod) { Bind bind = { key, mod }; binds.erase(bind); } -bool Input::isPressed(InputAction action) -{ +bool Input::isPressed(InputAction action) { return ispressed[action]; } -bool Input::wasJustPressed(InputAction action) -{ +bool Input::wasJustPressed(InputAction action) { return justpressed[action]; } -bool Input::wasJustReleased(InputAction action) -{ +bool Input::wasJustReleased(InputAction action) { return justreleased[action]; } + +InputEvent Input::set_mouse_pos(int x, int y, int dx, int dy) { + mouse_x = x; + mouse_y = y; + + InputEvent event; + event.type = INPUT_MOUSE_MOVE_EVENT; + event.mouse_move_event.x = x; + event.mouse_move_event.y = y; + event.mouse_move_event.dx = dx; + event.mouse_move_event.dy = dy; + return event; +} + +InputEvent Input::set_mouse_button(int button, int x, int y, bool pressed) { + mouse_x = x; + mouse_y = y; + + InputEvent event; + event.type = INPUT_MOUSE_CLICK_EVENT; + event.action = ACTION_NONE; + event.pressed = pressed; + event.mouse_click_event.button = button; + event.mouse_click_event.x = x; + event.mouse_click_event.y = y; + return event; +} diff --git a/src/Input.h b/src/Input.h index 3f27363..a8a8756 100644 --- a/src/Input.h +++ b/src/Input.h @@ -5,10 +5,17 @@ enum InputAction { ACTION_NONE, - ACTION_PAUSE, + ACTION_ESCAPE_MENU, ACTION_RESET, - ACTION_STEP, ACTION_TOGGLE_DEBUG, + ACTION_MOVE_NORTH, + ACTION_MOVE_NORTHWEST, + ACTION_MOVE_NORTHEAST, + ACTION_MOVE_WEST, + ACTION_MOVE_EAST, + ACTION_MOVE_SOUTH, + ACTION_MOVE_SOUTHWEST, + ACTION_MOVE_SOUTHEAST, INPUTACTION_END // Used to get the length of the enum. Must be the final entry. }; @@ -24,17 +31,55 @@ struct Bind { } }; +enum InputEventType { + INPUT_MOUSE_MOVE_EVENT, + INPUT_MOUSE_CLICK_EVENT, + INPUT_KEY_EVENT, +}; +struct InputEvent { + InputEventType type; + InputAction action; + bool pressed; + + struct MouseMoveEvent { // mouse click; + int x; + int y; + int dx; + int dy; + }; + struct MouseClickEvent { // mouse click; + int x; + int y; + int button; + }; + struct KeyPressEvent { // key + SDL_Keycode key; + SDL_Keymod mod; + bool echo; + }; + + union { + MouseMoveEvent mouse_move_event; + MouseClickEvent mouse_click_event; + KeyPressEvent key_press_event; + }; + +}; + class Input { std::map binds; bool ispressed[INPUTACTION_END]; bool justpressed[INPUTACTION_END]; bool justreleased[INPUTACTION_END]; + int mouse_x, mouse_y; public: Input(); ~Input(); void newframe(); - void setkey(SDL_Keycode key, SDL_Keymod mod, bool pressed); + InputEvent setkey(SDL_Keycode key, SDL_Keymod mod, bool pressed); + InputEvent set_mouse_pos(int x, int y, int dx, int dy); + InputEvent set_mouse_button(int button, int x, int y, bool pressed); void bindkey(SDL_Keycode key, InputAction action, SDL_Keymod mod = KMOD_NONE); void unbindkey(SDL_Keycode key, SDL_Keymod mod); bool isPressed(InputAction action); diff --git a/src/Main.cpp b/src/Main.cpp index 4b35e9c..e8bb657 100644 --- a/src/Main.cpp +++ b/src/Main.cpp @@ -1,139 +1,12 @@ -//#define _ALLOW_ITERATOR_DEBUG_LEVEL_MISMATCH -//#define _ITERATOR_DEBUG_LEVEL 0 +#include "App.h" -#include -#include -#include -#include -#include - -#include "Renderer.h" -#include "Input.h" -#include "Config.h" -#include "Game.h" - -#include "imgui.h" - -Uint64 perfFreq; - -double currTime(); - -#undef main +//#undef main int main(int argc, char* argv[]) { - Renderer* renderer; - Input* input; - - //setenv("MESA_DEBUG", "", 0); - - Config cfg = Config("dungeon.cfg"); - cfg.load(); - - int windowWidth = cfg.getInt("ResolutionX", 1280); - int windowHeight = cfg.getInt("ResolutionY", 720); - bool vsync = cfg.getBool("VSync", false); - bool wireframe = cfg.getBool("Wireframes", false); - - cfg.save(); - - int err = 0; - err = SDL_Init(SDL_INIT_TIMER | SDL_INIT_VIDEO); - SDL_LogSetAllPriority(SDL_LOG_PRIORITY_DEBUG); - if (err != 0) { - const char* error = SDL_GetError(); - SDL_LogCritical(SDL_LOG_CATEGORY_SYSTEM, "%s", error); - SDL_Quit(); - return 1; + App app{}; + int err = 1; + if (app.init()) { + err = app.start(); } - SDL_LogVerbose(SDL_LOG_CATEGORY_SYSTEM,"SDL initialized.\n"); - - renderer = new Renderer(); - if (!renderer->Init("Dungeon", windowWidth, windowHeight)) { - const char* error = SDL_GetError(); - SDL_LogCritical(SDL_LOG_CATEGORY_SYSTEM, "%s", error); - SDL_ShowSimpleMessageBox(0, "Error", error, nullptr); - SDL_Quit(); - return 1; - } - - renderer->SetVSyncEnabled(vsync); - renderer->SetWireframesEnabled(wireframe); - - input = new Input(); - srand(time(nullptr)); - load(renderer, input); - - perfFreq = SDL_GetPerformanceFrequency(); - - double dt = 1.0/30; - double currentTime = currTime(); - double accumulator = dt; - - int currentframe = 0; - bool running = true; - - while (running) { - double newTime = currTime(); - double frametime = newTime - currentTime; - if (frametime > 1.0/30) { - frametime = 1.0/30; - } - currentTime = newTime; - - accumulator += frametime; - - //dt = (SDL_GetTicks() - currentframe) * 0.001f; - currentframe = SDL_GetTicks(); - - SDL_Event ev; - ImGuiIO &io = ImGui::GetIO(); - renderer->ImguiNewFrame(); - while (SDL_PollEvent(&ev)) { - renderer->ImguiProcessEvents(&ev); - switch (ev.type) { - case SDL_WINDOWEVENT: - switch (ev.window.event) { - case SDL_WINDOWEVENT_CLOSE: - running = false; - break; - default: - break; - } - break; - case SDL_KEYDOWN: - if (!io.WantCaptureKeyboard) - input->setkey(ev.key.keysym.sym, (SDL_Keymod)ev.key.keysym.mod, true); - break; - case SDL_KEYUP: - if (!io.WantCaptureKeyboard) - input->setkey(ev.key.keysym.sym, (SDL_Keymod)ev.key.keysym.mod, false); - break; - case SDL_QUIT: - running = false; - break; - default: - break; - } - } - - while (running && accumulator >= dt) { - update(dt); - input->newframe(); - - accumulator -= dt; - } - - renderer->Clear(); - draw(accumulator / dt); - renderer->Present(); - SDL_Delay(1); - } - delete renderer; - SDL_Quit(); - SDL_LogVerbose(SDL_LOG_CATEGORY_SYSTEM, "Quit."); - return 0; -} - -double currTime() { - return SDL_GetPerformanceCounter() / (double)perfFreq; + return err; } diff --git a/src/PlayState.cpp b/src/PlayState.cpp new file mode 100644 index 0000000..213974b --- /dev/null +++ b/src/PlayState.cpp @@ -0,0 +1,268 @@ +// +// Created by Adrian on 2017-09-21. +// + +#include "PlayState.h" +#include "Input.h" +#include "Renderer.h" +#include "Actor.h" +#include "App.h" +#include "Tilemap.h" +#include "Tileset.h" +#include "imgui.h" +#include "Hero.h" +#include "Goblin.h" +#include "Shaman.h" + +const int mapwidth = 32; +const std::string map = + "# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #" + "# @ . . . # # # # # # # # . . . . . . . . . . . . . . . . . # #" + "# . . . . . . . . # # # # . # # . # # # # # # . # # # # # . # #" + "# . . . . # # # . . . . . . # . g . # # # # # . # # # . . g . #" + "# . . . . # # # # # # # # . # . . . # # . . . . . . . . . . . #" + "# # # . # # # # # # # # # . . . . g # # . # # # . # # . . g . #" + "# . . . . . . . . . . . . . # # # # # . . . # # . # # # # # # #" + "# . # # # # # # # # . # # . # # # # # . g . # # . # . . g . . #" + "# . . . . g # # . . . # . . . # # # # . . . # # . # . . . . . #" + "# . . g . . # # . # # # . s . . . # # # # # # # . . . . s . . #" + "# . . . . . # # . . . # . . . # . . . . . . . . . # . g . . . #" + "# # . # # # # # . # . # # # # # # # # # . # # # # # # # # . # #" + "# . . . . . . . . # . . . . . . . . . . . . . . . . . . . . . #" + "# . # # # # # # # # # # . # . # # # # # # # # # # . # # # # . #" + "# . . . . . . . . . . . . # . # . . . . # . . . . . # # # . . #" + "# # # # # # . # # # . # # # . # . . . . # . . . # . # # # . # #" + "# . . . . # . # . . . . . # . . . . . . . . . . # . # # . . . #" + "# . . . . # . # . . . . . # . # . . . . # # # # # . . . . . . #" + "# . . . . . . # . . . . . # . # # # # # # . . . . . # # . . . #" + "# . . . . # . # # # # # # # . . . . . . . . # # # # # # # # # #" + "# . . . . # . . . . . . . . . # # # # # # # # # # # # # # # # #" + "# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #" + "# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #" + "# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #" + "# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #" + "# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #" + "# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #" + "# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #" + "# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #" + "# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #" + "# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #" + "# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #" + "# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #"; + +void PlayState::load() { + SDL_LogVerbose(SDL_LOG_CATEGORY_SYSTEM, "Creating ascii tileset...\n"); + ascii = new Tileset(app->renderer, "./assets/12x12.bmp", 192, 192, 12, 12); + SDL_LogVerbose(SDL_LOG_CATEGORY_SYSTEM, "Created ascii tileset.\n"); + + app->input->bindkey(SDLK_ESCAPE, ACTION_ESCAPE_MENU); + app->input->bindkey(SDLK_KP_8, ACTION_MOVE_NORTH); + app->input->bindkey(SDLK_KP_7, ACTION_MOVE_NORTHWEST); + app->input->bindkey(SDLK_KP_9, ACTION_MOVE_NORTHEAST); + app->input->bindkey(SDLK_KP_4, ACTION_MOVE_WEST); + app->input->bindkey(SDLK_KP_6, ACTION_MOVE_EAST); + app->input->bindkey(SDLK_KP_2, ACTION_MOVE_SOUTH); + app->input->bindkey(SDLK_KP_1, ACTION_MOVE_SOUTHWEST); + app->input->bindkey(SDLK_KP_3, ACTION_MOVE_SOUTHEAST); + app->input->bindkey(SDLK_F1, ACTION_TOGGLE_DEBUG); + + app->input->bindkey(SDLK_r, ACTION_RESET); + + SDL_LogVerbose(SDL_LOG_CATEGORY_SYSTEM, "Keybinds bound.\n"); + new_game(); +} + +Gamestate *PlayState::update(double delta) { + timer += delta; + if (!paused) { + if (timer >= delay) { + timer = 0; + + auto actors = tilemap->GetActorList(); + for (Actor* var : *actors) { + var->Update(); + } + for (int i = (int)actors->size() - 1; i >= 0; i--) { + if (!actors->at(i)->alive) { + if (actors->at(i) == hero) { + hero = nullptr; + } + delete actors->at(i); + actors->erase(actors->begin() + i); + } + } + } + } + return nullptr; +} + +void PlayState::draw(double delta) { + if (debug) { + bool wireframe = app->renderer->IsWireframesEnabled(); + ImGui::Checkbox("Wireframes", &wireframe); + app->renderer->SetWireframesEnabled(wireframe); + bool vsync = app->renderer->IsVSyncEnabled(); + ImGui::Checkbox("VSync", &vsync); + app->renderer->SetVSyncEnabled(vsync); + + ImGui::BeginMainMenuBar(); + + if (ImGui::BeginMenu("File")) { + ImGui::EndMenu(); + } + + if (ImGui::BeginMenu("Edit")) { + ImGui::EndMenu(); + } + + if (ImGui::BeginMenu("View")) { + ImGui::EndMenu(); + } + + ImGui::EndMainMenuBar(); + + ImGui::SliderFloat("turndelay", &delay, .01f, 1); + } + + + for (int x = 0; x < 32; x++) { + for (int y = 0; y < 32; y++) { + if (hero == nullptr || hero->HasSeen(x, y)) { + app->renderer->SetColor(1, 1, 1, 1); + app->renderer->DrawSprite(ascii->GetSprite(tilemap->GetTile(x, y)), x * 12, y * 12); + if (hero != nullptr && !hero->CanSee(x, y)) { + app->renderer->SetColor(0, 0, 0, .5f); + app->renderer->DrawSprite(ascii->GetSprite(219), x * 12, y * 12); + } + } + } + } + auto actors = tilemap->GetActorList(); + for (Actor* var : *actors) { + vec2i pos = var->getPosition(); + if (hero == nullptr || hero->CanSee(pos.x, pos.y)) { + app->renderer->SetColor(0, 0, 0, 255); + app->renderer->DrawSprite(ascii->GetSprite(219), pos.x * 12, pos.y * 12); + + int sprite; + switch (var->Type()) { + case ACT_HERO: + app->renderer->SetColor(.2f, .6f, 1, 1); + sprite = '@'; + break; + case ACT_GOBLIN: + app->renderer->SetColor(.6f, 1, .2f, 1); + sprite = 'g'; + break; + case ACT_SHAMAN: + app->renderer->SetColor(.2f, 1, .6f, 1); + sprite = 's'; + break; + default: + app->renderer->SetColor(1, 1, 1, 1); + sprite = 2; + break; + } + app->renderer->DrawSprite(ascii->GetSprite(sprite), pos.x * 12, pos.y * 12); + } + } + if (hero != nullptr) { + app->renderer->SetColor(155, 5, 5, 255); + for (int i = 0; i < hero->health; i++) { + app->renderer->SetColor(0, 0, 0, 255); + app->renderer->DrawSprite(ascii->GetSprite(219), i * 12, 0); + app->renderer->SetColor(255, 0, 0, 255); + app->renderer->DrawSprite(ascii->GetSprite(3), i * 12, 0); + } + } + if (paused) { + app->renderer->SetColor(255, 0, 0, 255); + app->renderer->DrawSprite(ascii->GetSprite(219), 12 * 0, 0); + app->renderer->DrawSprite(ascii->GetSprite(219), 12 * 1, 0); + app->renderer->DrawSprite(ascii->GetSprite(219), 12 * 2, 0); + app->renderer->DrawSprite(ascii->GetSprite(219), 12 * 3, 0); + app->renderer->DrawSprite(ascii->GetSprite(219), 12 * 4, 0); + app->renderer->DrawSprite(ascii->GetSprite(219), 12 * 5, 0); + app->renderer->DrawSprite(ascii->GetSprite(219), 12 * 6, 0); + app->renderer->DrawSprite(ascii->GetSprite(219), 12 * 7, 0); + app->renderer->SetColor(0, 0, 0, 255); + app->renderer->DrawSprite(ascii->GetSprite('-'), 12 * 0, 0); + app->renderer->DrawSprite(ascii->GetSprite('P'), 12 * 1, 0); + app->renderer->DrawSprite(ascii->GetSprite('A'), 12 * 2, 0); + app->renderer->DrawSprite(ascii->GetSprite('U'), 12 * 3, 0); + app->renderer->DrawSprite(ascii->GetSprite('S'), 12 * 4, 0); + app->renderer->DrawSprite(ascii->GetSprite('E'), 12 * 5, 0); + app->renderer->DrawSprite(ascii->GetSprite('D'), 12 * 6, 0); + app->renderer->DrawSprite(ascii->GetSprite('-'), 12 * 7, 0); + } +} + +void PlayState::quit() { + +} + +void PlayState::inputevent(InputEvent *event) { + if (event->pressed) { + switch (event->action) { + case ACTION_TOGGLE_DEBUG: debug = !debug; break; + case ACTION_ESCAPE_MENU: paused = !paused; break; + case ACTION_RESET: new_game(); break; + default: break; + } + } +} + +void PlayState::new_game() { + paused = true; + + if (tilemap != nullptr) { + delete tilemap; + tilemap = nullptr; + hero = nullptr; + } + + if (hero != nullptr) { + delete hero; + hero = nullptr; + } + + SDL_LogVerbose(SDL_LOG_CATEGORY_SYSTEM, "Creating tilemap...\n"); + tilemap = new Tilemap(32, 32); + int y = 0; + int x = 0; + for (int i = 0; i < map.length(); i++) { + + if (y >= 32) { + break; + } + if (x >= mapwidth) { + y++; + x = 0; + } + if (map[i] == ' ' || map[i] == '\t' || map[i] == '\n') { + continue; + } + + if (map[i] == '@') { + hero = new Hero(tilemap, vec2i(x, y)); + tilemap->AddActor(hero); + tilemap->SetTile(x, y, '.'); + } + else if (map[i] == 'g') { + tilemap->AddActor(new Goblin(tilemap, vec2i(x, y))); + tilemap->SetTile(x, y, '.'); + } + else if (map[i] == 's') { + tilemap->AddActor(new Shaman(tilemap, vec2i(x, y))); + tilemap->SetTile(x, y, '.'); + } + else { + tilemap->SetTile(x, y, map[i]); + } + x++; + } + SDL_LogVerbose(SDL_LOG_CATEGORY_SYSTEM, "Done.\n"); + SDL_LogVerbose(SDL_LOG_CATEGORY_SYSTEM, "Calculating initial FOV...\n"); + hero->CalcFOV(); + SDL_LogVerbose(SDL_LOG_CATEGORY_SYSTEM, "Done.\n"); +} diff --git a/src/PlayState.h b/src/PlayState.h new file mode 100644 index 0000000..89010a5 --- /dev/null +++ b/src/PlayState.h @@ -0,0 +1,35 @@ +// +// Created by Adrian on 2017-09-21. +// + +#ifndef DUNGEON_PLAYSTATE_H +#define DUNGEON_PLAYSTATE_H + + +#include "Gamestate.h" + +class Tileset; +class Tilemap; +class Actor; + +class PlayState : public Gamestate { + Tileset* ascii; + Tilemap* tilemap; + Actor * hero; + + float delay = .5; + double timer; + + bool paused; + bool debug; +public: + void new_game(); + void load() override; + Gamestate* update(double delta) override; + void draw(double delta) override; + void quit() override; + void inputevent(InputEvent* event) override; +}; + + +#endif //DUNGEON_PLAYSTATE_H