dungeon/src/PlayState.cpp
2017-12-27 22:53:48 +01:00

354 lines
13 KiB
C++

//
// Created by Adrian on 2017-09-21.
//
#include "PlayState.h"
#include "Input.h"
#include "Renderer.h"
#include "Actor.h"
#include "App.h"
#include "Tileset.h"
#include "Mapgen.h"
#include "FieldOfView.h"
#include "imgui.h"
#include "Hero.h"
#include "Goblin.h"
#include "Shaman.h"
const int mapwidth = 32;
InputAction action;
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->bind_key(SDLK_ESCAPE, ACTION_ESCAPE_MENU);
// Movement: keypad
app->input->bind_key(SDLK_KP_8, ACTION_MOVE_NORTH);
app->input->bind_key(SDLK_KP_7, ACTION_MOVE_NORTHWEST);
app->input->bind_key(SDLK_KP_9, ACTION_MOVE_NORTHEAST);
app->input->bind_key(SDLK_KP_4, ACTION_MOVE_WEST);
app->input->bind_key(SDLK_KP_6, ACTION_MOVE_EAST);
app->input->bind_key(SDLK_KP_2, ACTION_MOVE_SOUTH);
app->input->bind_key(SDLK_KP_1, ACTION_MOVE_SOUTHWEST);
app->input->bind_key(SDLK_KP_3, ACTION_MOVE_SOUTHEAST);
app->input->bind_key(SDLK_KP_5, ACTION_WAIT);
// Movement: arrow-keys
app->input->bind_key(SDLK_UP, ACTION_MOVE_NORTH);
app->input->bind_key(SDLK_DOWN, ACTION_MOVE_SOUTH);
app->input->bind_key(SDLK_LEFT, ACTION_MOVE_WEST);
app->input->bind_key(SDLK_RIGHT, ACTION_MOVE_EAST);
// Movement: vim-keys
app->input->bind_key(SDLK_k, ACTION_MOVE_NORTH);
app->input->bind_key(SDLK_y, ACTION_MOVE_NORTHWEST);
app->input->bind_key(SDLK_u, ACTION_MOVE_NORTHEAST);
app->input->bind_key(SDLK_h, ACTION_MOVE_WEST);
app->input->bind_key(SDLK_l, ACTION_MOVE_EAST);
app->input->bind_key(SDLK_j, ACTION_MOVE_SOUTH);
app->input->bind_key(SDLK_n, ACTION_MOVE_SOUTHWEST);
app->input->bind_key(SDLK_m, ACTION_MOVE_SOUTHEAST);
// debug
app->input->bind_key(SDLK_F1, ACTION_TOGGLE_DEBUG);
app->input->bind_key(SDLK_r, ACTION_RESET);
SDL_LogVerbose(SDL_LOG_CATEGORY_SYSTEM, "Keybinds bound.\n");
new_game();
}
void PlayState::new_game() {
action = ACTION_NONE;
if (hero != nullptr) {
delete hero;
hero = nullptr;
}
std::string map =
"# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #"
"# @ . . . # # # # # # # # . . . . . . . . . . . . . . . . . # #"
"# . . . . . . . . # # # # . # # . # # # # # # . # # # # # . # #"
"# . . . . # # # . . . . . . # . g . # # # # # . # # # . . g . #"
"# . . . . # # # # # # # # . # . . . # # . . . . . . . . . . . #"
"# # # . # # # # # # # # # . . . . g # # . # # # . # # . . g . #"
"# . . . . . . . . . . . . . # # # # # . . . # # . # # # # # # #"
"# . # # # # # # # # . # # . # # # # # . g . # # . # . . g . . #"
"# . . . . g # # . . . # . . . # # # # . . . # # . # . . . . . #"
"# . . g . . # # . # # # . s . . . # # # # # # # . . . . s . . #"
"# . . . . . # # . . . # . . . # . . . . . . . . . # . g . . . #"
"# # . # # # # # . # . # # # # # # # # # . # # # # # # # # . # #"
"# . . . . . . . . # . . . . . . . . . . . . . . . . . . . . . #"
"# . # # # # # # # # # # . # . # # # # # # # # # # . # # # # . #"
"# . . . . . . . . . . . . # . # . . . . # . . . . . # # # . . #"
"# # # # # # . # # # . # # # . # . . . . # . . . # . # # # . # #"
"# . . . . # . # . . . . . # . . . . . . . . . . # . # # . . . #"
"# . . . . # . # . . . . . # . # . . . . # # # # # . . . . . . #"
"# . . . . . . # . . . . . # . # # # # # # . . . . . # # . . . #"
"# . . . . # . # # # # # # # . . . . . . . . # # # # # # # # # #"
"# . . . . # . . . . . . . . . # # # # # # # # # # # # # # # # #"
"# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #"
"# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #"
"# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #"
"# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #"
"# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #"
"# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #"
"# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #"
"# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #"
"# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #"
"# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #"
"# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #"
"# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #";
SDL_LogVerbose(SDL_LOG_CATEGORY_SYSTEM, "Creating tilemap...\n");
/*
tilemap = new Tilemap(32, 32);
int y = 0;
int x = 0;
for (char i : map) {
if (y >= 32) {
break;
}
if (x >= mapwidth) {
y++;
x = 0;
}
if (i == ' ' || i == '\t' || i == '\n') {
continue;
}
if (i == '@') {
hero = new Hero(tilemap, vec2i(x, y));
tilemap.add_entity(hero);
tilemap.set_tile(x, y, '.');
}
else if (i == 'g') {
tilemap.add_entity(new Goblin(tilemap, vec2i(x, y)));
tilemap.set_tile(x, y, '.');
}
else if (i == 's') {
tilemap.add_entity(new Shaman(tilemap, vec2i(x, y)));
tilemap.set_tile(x, y, '.');
}
else {
tilemap.set_tile(x, y, i);
}
x++;
}
*/
tilemap = generate_level(1, 32, 32);
hero = new Hero(&tilemap, vec2i(4,4));
tilemap.add_entity(hero);
SDL_LogVerbose(SDL_LOG_CATEGORY_SYSTEM, "Done.\n");
SDL_LogVerbose(SDL_LOG_CATEGORY_SYSTEM, "Calculating initial FOV...\n");
fov = FieldOfView(&tilemap);
fov.calc(hero->get_position(), 6);
SDL_LogVerbose(SDL_LOG_CATEGORY_SYSTEM, "Done.\n");
}
Gamestate *PlayState::update(double delta) {
if (action != ACTION_NONE) {
SDL_LogVerbose(SDL_LOG_CATEGORY_SYSTEM, "Starting turn...\n");
if (hero && hero->is_alive()) {
vec2i dir;
switch (action) {
case ACTION_MOVE_NORTH: dir = {0, -1}; break;
case ACTION_MOVE_NORTHWEST: dir = {-1, -1}; break;
case ACTION_MOVE_NORTHEAST: dir = {1, -1}; break;
case ACTION_MOVE_WEST: dir = {-1, 0}; break;
case ACTION_MOVE_EAST: dir = {1, 0}; break;
case ACTION_MOVE_SOUTH: dir = {0, 1}; break;
case ACTION_MOVE_SOUTHWEST: dir = {-1, 1}; break;
case ACTION_MOVE_SOUTHEAST: dir = {1, 1}; break;
case ACTION_WAIT: dir = {0, 0}; break;
default: action = ACTION_NONE; SDL_LogVerbose(SDL_LOG_CATEGORY_SYSTEM, "Turn aborted: no player action.\n"); return nullptr; // abort turn
}
if (dir != vec2i(0,0)) {
if (!hero->move(dir.x, dir.y)) {
vec2i heropos = hero->get_position();
auto acts = tilemap.get_entities(heropos.x + dir.x, heropos.y + dir.y, 0, ENTITY_ACTOR);
if(acts.empty()) {
SDL_LogVerbose(SDL_LOG_CATEGORY_SYSTEM, "Turn aborted: invalid player action.\n");
action = ACTION_NONE;
return nullptr; // unable to move and nothing to attack == abort turn
}
for (auto ent : acts) {
auto act = (Actor*)ent;
if (act->is_alive() && act->get_actor_faction() != hero->get_actor_faction()) {
hero->attack(act);
break;
}
}
}
}
hero->update();
fov.calc(hero->get_position(), 6);
}
auto actors = tilemap.get_entity_list();
for (Entity* var : *actors) {
if (var == hero) continue;
if (var->entity_type() == ENTITY_ACTOR) {
((Actor*)var)->update();
}
}
/* // We got enough memory, we can leave the corpses on the field.
unsigned int actor_size = actors->size();
for (unsigned int i = actor_size - 1; i <= actor_size; i--) { // Woo unsigned int underflow abuse!
if (!actors->at(i)->is_alive()) {
if (actors->at(i) == hero) {
hero = nullptr;
}
delete actors->at(i);
actors->erase(actors->begin() + i);
}
}
*/
action = ACTION_NONE;
SDL_LogVerbose(SDL_LOG_CATEGORY_SYSTEM, "Turn finished.\n");
}
return nullptr;
}
bool debug_actors = false;
bool debug_settings = false;
void PlayState::draw(double delta) {
if (debug) {
{
ImGui::BeginMainMenuBar();
if (ImGui::BeginMenu("View")) {
ImGui::MenuItem("Settings", nullptr, &debug_settings);
ImGui::MenuItem("Actors", nullptr, &debug_actors);
ImGui::EndMenu();
}
ImGui::EndMainMenuBar();
}
if (debug_settings) {
ImGui::Begin("Settings", &debug_settings);
bool wireframe = app->renderer->is_wireframes_enabled();
ImGui::Checkbox("Wireframes", &wireframe);
app->renderer->set_wireframes_enabled(wireframe);
bool vsync = app->renderer->is_vsync_enabled();
ImGui::Checkbox("VSync", &vsync);
app->renderer->set_vsync_enabled(vsync);
ImGui::End();
}
if (debug_actors) {
ImGui::Begin("Actors", &debug_actors);
auto actors = tilemap.get_entity_list();
const char* headers[] {
"id", "name", "health", "strength"
};
static float widths[4]{};
ImGui::BeginTable("ActorColumns", headers, widths, 4);
for (Entity* ent : *actors) {
if (ent->entity_type() == ENTITY_ACTOR) {
auto act = (Actor*) ent;
ImGui::Text("%d", act->id); ImGui::NextColumn();
ImGui::Text(act->name.c_str()); ImGui::NextColumn();
ImGui::Text("%d/%d", act->get_health(), act->get_health_max()); ImGui::NextColumn();
ImGui::Text("%d", act->get_strength()); ImGui::NextColumn();
}
}
ImGui::EndTable();
ImGui::End();
}
}
vec2i asciisize = {
ascii->get_tile_width(),
ascii->get_tile_height(),
};
vec2i tilesize = {
app->renderer->get_renderer_width() / ascii->get_tile_width(),
app->renderer->get_renderer_height() / ascii->get_tile_height(),
};
vec2i margin = {
(app->renderer->get_renderer_width() - tilesize.x * asciisize.x)/2,
(app->renderer->get_renderer_height() - tilesize.y * asciisize.y)/2,
};
vec2i heropos = {0,0};
if (hero) {
heropos = hero->get_position();
}
vec2i offset = {
(tilesize.x/2-heropos.x),
(tilesize.y/2-heropos.y),
};
tilemap.draw(app->renderer, ascii, margin.x, margin.y, -offset.x, -offset.y, tilesize.x, tilesize.y, &fov);
auto entities = tilemap.get_entity_list();
// Draw dead actors
for (Entity* var : *entities) {
if (var->entity_type() == ENTITY_ACTOR && ((Actor*)var)->is_alive()) continue;
vec2i pos = var->get_position();
if (fov.can_see(pos)) {
app->renderer->set_color(0, 0, 0, 1);
app->renderer->draw_sprite(ascii->get_sprite(219), margin.x + (offset.x + pos.x) * asciisize.x, margin.y + (offset.y + pos.y) * asciisize.y);
int sprite = var->get_sprite_id();
app->renderer->set_color(var->get_sprite_color()*0.35f);
app->renderer->draw_sprite(ascii->get_sprite(sprite), margin.x + (offset.x + pos.x) * asciisize.x, margin.y + (offset.y + pos.y) * asciisize.y);
}
}
// Draw the rest of the entities
for (Entity* var : *entities) {
if (var->entity_type() == ENTITY_ACTOR && !((Actor*)var)->is_alive()) continue;
vec2i pos = var->get_position();
if (fov.can_see(pos)) {
app->renderer->set_color(0, 0, 0, 1);
app->renderer->draw_sprite(ascii->get_sprite(219), margin.x + (offset.x + pos.x) * asciisize.x, margin.y + (offset.y + pos.y) * asciisize.y);
int sprite = var->get_sprite_id();
app->renderer->set_color(var->get_sprite_color());
app->renderer->draw_sprite(ascii->get_sprite(sprite), margin.x + (offset.x + pos.x) * asciisize.x, margin.y + (offset.y + pos.y) * asciisize.y);
}
}
if (hero != nullptr) {
app->renderer->set_color(155, 5, 5, 255);
for (int i = 0; i < hero->get_health(); i++) {
app->renderer->set_color(0, 0, 0, 255);
app->renderer->draw_sprite(ascii->get_sprite(219), (i+1) * asciisize.x, asciisize.y);
app->renderer->set_color(255, 0, 0, 255);
app->renderer->draw_sprite(ascii->get_sprite(3), (i+1) * asciisize.x, asciisize.y);
}
}
}
void PlayState::quit() {
}
void PlayState::inputevent(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: action = event->action; break;
}
}
}