Big refactor, FSM for game state, input event callbacks

This commit is contained in:
Adrian Hedqvist 2017-09-21 18:57:12 +02:00
parent 328fd40a55
commit 2e299dbcfa
12 changed files with 624 additions and 432 deletions

1
.gitignore vendored
View file

@ -32,3 +32,4 @@ build
*.log
imgui.ini
*.sublime*
*.dll

160
src/App.cpp Normal file
View file

@ -0,0 +1,160 @@
//
// Created by Adrian on 2017-09-19.
//
#include "App.h"
#include <SDL.h>
#include <ctime>
#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<unsigned int>(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;
}

24
src/App.h Normal file
View file

@ -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

View file

@ -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);
}
}

View file

@ -1,8 +0,0 @@
#pragma once
class Renderer;
class Input;
void load(Renderer* rend, Input* inp);
void update(double dt);
void draw(double alpha);

10
src/Gamestate.cpp Normal file
View file

@ -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;
}

24
src/Gamestate.h Normal file
View file

@ -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

View file

@ -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;
}

View file

@ -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<Bind, InputAction> 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);

View file

@ -1,139 +1,12 @@
//#define _ALLOW_ITERATOR_DEBUG_LEVEL_MISMATCH
//#define _ITERATOR_DEBUG_LEVEL 0
#include "App.h"
#include <SDL.h>
#include <stdio.h>
#include <math.h>
#include <string>
#include <time.h>
#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;
}

268
src/PlayState.cpp Normal file
View file

@ -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");
}

35
src/PlayState.h Normal file
View file

@ -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