breakhack/src/main.c

431 lines
8.8 KiB
C
Raw Normal View History

2017-11-30 21:00:47 +01:00
#include <stdio.h>
#include <stdbool.h>
#include <SDL2/SDL.h>
#include <SDL2/SDL_image.h>
#include "linkedlist.h"
2017-11-30 21:00:47 +01:00
#include "player.h"
#include "screenresolution.h"
#include "dimension.h"
2017-12-01 16:03:19 +01:00
#include "camera.h"
#include "map.h"
2017-12-02 23:32:40 +01:00
#include "map_lua.h"
2017-12-05 08:30:08 +01:00
#include "timer.h"
#include "roommatrix.h"
2017-12-22 06:27:58 +01:00
#include "gamestate.h"
#include "gui.h"
#include "util.h"
#include "item_builder.h"
2018-01-31 09:15:33 +01:00
#include "pointer.h"
#include "gui_button.h"
2018-02-03 23:39:49 +01:00
#include "particle_engine.h"
#include "menu.h"
2017-11-30 21:00:47 +01:00
static SDL_Window *gWindow = NULL;
static SDL_Renderer *gRenderer = NULL;
static Player *gPlayer = NULL;
2017-12-01 16:03:19 +01:00
static Map *gMap = NULL;
static RoomMatrix *gRoomMatrix = NULL;
static Gui *gGui = NULL;
2018-01-31 09:15:33 +01:00
static Pointer *gPointer = NULL;
2018-01-23 12:14:44 +01:00
static unsigned int cLevel = 1;
2018-02-03 23:39:49 +01:00
static float deltaTime = 1.0;
2018-01-23 12:14:44 +01:00
static double renderScale = 1.0;
static Menu *mainMenu = NULL;
static GameState gGameState;
2017-12-01 16:03:19 +01:00
static Camera gCamera;
2018-01-23 12:14:44 +01:00
static SDL_Rect gameViewport;
static SDL_Rect bottomGuiViewport;
static SDL_Rect rightGuiViewport;
2017-11-30 21:00:47 +01:00
static void resetGame(void);
2017-11-30 21:00:47 +01:00
static
bool initSDL(void)
2017-11-30 21:00:47 +01:00
{
int imgFlags = IMG_INIT_PNG;
2017-12-03 11:09:57 +01:00
Dimension dim = getScreenDimensions();
2017-11-30 21:00:47 +01:00
2017-12-22 06:27:58 +01:00
if (dim.height > 1080) {
info("Hi resolution screen detected (%u x %u)", dim.width, dim.height);
2018-01-24 08:52:50 +01:00
renderScale = ((double) dim.height)/1080;
2018-01-24 18:06:10 +01:00
if (renderScale > 2)
renderScale = 3;
else if (renderScale > 1)
renderScale = 2;
info("Scaling by %f", renderScale);
2017-11-30 21:00:47 +01:00
}
if (SDL_Init(SDL_INIT_VIDEO) < 0)
{
error("Could not initiate SDL2: %s", SDL_GetError());
2017-11-30 21:00:47 +01:00
return false;
}
2017-12-22 06:27:58 +01:00
if ( (IMG_Init(imgFlags) & imgFlags) == 0 ) {
error("Unable to initiate img loading: %s",
2017-12-22 06:27:58 +01:00
IMG_GetError());
return false;
}
if ( TTF_Init() == -1 ) {
error("Unable to initiate ttf library: %s",
2017-12-22 06:27:58 +01:00
TTF_GetError());
return false;
}
2017-11-30 21:00:47 +01:00
gWindow = SDL_CreateWindow("Breakhack",
SDL_WINDOWPOS_UNDEFINED,
SDL_WINDOWPOS_UNDEFINED,
2018-01-23 12:14:44 +01:00
(int)(SCREEN_WIDTH * renderScale),
(int)(SCREEN_HEIGHT * renderScale),
2017-11-30 21:00:47 +01:00
SDL_WINDOW_SHOWN);
if (gWindow == NULL)
{
error("Unable to create window: %s", SDL_GetError());
2017-11-30 21:00:47 +01:00
return false;
}
gRenderer = SDL_CreateRenderer(gWindow, -1, SDL_RENDERER_ACCELERATED);
if (gRenderer == NULL)
{
error("Unable to create renderer: %s", SDL_GetError());
2017-11-30 21:00:47 +01:00
return false;
}
2017-12-10 19:51:24 +01:00
if (SDL_SetRenderDrawBlendMode(gRenderer, SDL_BLENDMODE_BLEND) < 0) {
error("Unable to set blend mode: %s", SDL_GetError());
2017-12-10 19:51:24 +01:00
return false;
}
2017-11-30 21:00:47 +01:00
if (SDL_RenderSetLogicalSize(gRenderer, SCREEN_WIDTH, SCREEN_HEIGHT) < 0)
{
error("Unable to initiate scaling: %s",
2017-12-15 15:03:29 +01:00
SDL_GetError());
2017-11-30 21:00:47 +01:00
return false;
}
return true;
}
2018-01-23 12:14:44 +01:00
static void
initViewports(void)
{
gameViewport = (SDL_Rect) { 0, 0,
GAME_VIEW_WIDTH, GAME_VIEW_HEIGHT };
bottomGuiViewport = (SDL_Rect) { 0, GAME_VIEW_HEIGHT,
BOTTOM_GUI_WIDTH, BOTTOM_GUI_WIDTH };
rightGuiViewport = (SDL_Rect) { GAME_VIEW_WIDTH, 0,
RIGHT_GUI_WIDTH, RIGHT_GUI_HEIGHT };
}
static bool
initGame(void)
2017-11-30 21:00:47 +01:00
{
initViewports();
gCamera.renderer = gRenderer;
gRoomMatrix = roommatrix_create();
gGui = gui_create(gRenderer);
item_builder_init(gRenderer);
gPointer = pointer_create(gRenderer);
particle_engine_init();
2018-01-31 09:15:33 +01:00
return true;
2017-11-30 21:00:47 +01:00
}
static void
startGame(void *unused)
{
UNUSED(unused);
gGameState = PLAYING;
resetGame();
}
static void
exitGame(void *unused)
{
UNUSED(unused);
gGameState = QUIT;
}
static void
initMainMenu(void)
{
2018-02-09 09:36:24 +01:00
static SDL_Color C_DEFAULT = { 255, 255, 0, 0 };
static SDL_Color C_HOVER = { 255, 0, 0, 0 };
struct MENU_ITEM {
char label[20];
void (*callback)(void*);
};
struct MENU_ITEM menu_items[] = {
{ "PLAY", startGame },
{ "QUIT", exitGame },
};
mainMenu = menu_create();
for (unsigned int i = 0; i < 2; ++i) {
Sprite *s1 = sprite_create();
2018-02-09 09:36:24 +01:00
sprite_load_text_texture(s1, "assets/GUI/SDS_8x8.ttf", 0, 20);
texture_load_from_text(s1->textures[0], menu_items[i].label,
2018-02-09 09:36:24 +01:00
C_DEFAULT, gRenderer);
s1->pos = (Position) { 200, 100 + (i*50) };
s1->fixed = true;
Sprite *s2 = sprite_create();
2018-02-09 09:36:24 +01:00
sprite_load_text_texture(s2, "assets/GUI/SDS_8x8.ttf", 0, 20);
texture_load_from_text(s2->textures[0], menu_items[i].label,
2018-02-09 09:36:24 +01:00
C_HOVER, gRenderer);
s2->pos = (Position) { 200, 100 + (i*50) };
s2->fixed = true;
menu_item_add(mainMenu, s1, s2, menu_items[i].callback);
}
}
static void
resetGame(void)
{
2018-02-09 10:18:22 +01:00
SDL_FlushEvents(SDL_FIRSTEVENT, SDL_LASTEVENT);
if (gMap)
map_destroy(gMap);
info("Building new map");
2018-02-09 09:36:24 +01:00
gMap = map_lua_generator_run(cLevel, gRenderer);
gPlayer->sprite->pos = (Position) {
TILE_DIMENSION, TILE_DIMENSION };
2018-02-09 09:36:24 +01:00
map_set_current_room(gMap, &gPlayer->sprite->pos);
camera_follow_position(&gCamera, &gPlayer->sprite->pos);
}
static bool
init(void)
2017-11-30 21:00:47 +01:00
{
bool result = true;
result = result && initSDL();
result = result && initGame();
initMainMenu();
2017-12-17 13:43:41 +01:00
gCamera.pos = (Position) { 0, 0 };
gGameState = MENU;
2017-12-22 06:27:58 +01:00
2017-11-30 21:00:47 +01:00
return result;
}
static void
loadMedia(void)
2017-11-30 21:00:47 +01:00
{
2018-02-02 17:05:41 +01:00
gPlayer = player_create(WARRIOR, gRenderer);
2017-11-30 21:00:47 +01:00
}
2017-12-03 11:09:57 +01:00
static
bool handle_events(void)
2017-12-03 11:09:57 +01:00
{
static SDL_Event event;
bool quit = false;
while (SDL_PollEvent(&event) != 0) {
if (event.type == SDL_QUIT) {
quit = true;
} else if (gGameState == PLAYING) {
2017-12-15 08:08:45 +01:00
gPlayer->handle_event(gPlayer,
gRoomMatrix,
&event);
camera_follow_position(&gCamera, &gPlayer->sprite->pos);
map_set_current_room(gMap, &gPlayer->sprite->pos);
roommatrix_handle_event(gRoomMatrix, &event);
} else if (gGameState == MENU) {
menu_handle_event(mainMenu, &event);
2017-12-03 11:09:57 +01:00
}
2018-01-31 09:15:33 +01:00
pointer_handle_event(gPointer, &event);
2017-12-03 11:09:57 +01:00
}
2017-12-03 11:09:57 +01:00
return quit;
}
static bool
check_if_dead(void)
{
if (gPlayer->stats.hp <= 0) {
gui_log("The dungeon consumed you");
return true;
}
return false;
}
2017-12-22 06:27:58 +01:00
static void
check_next_level(void)
{
Room *room = gMap->rooms[gMap->currentRoom.x][gMap->currentRoom.y];
Position pos = position_to_matrix_coords(&gPlayer->sprite->pos);
MapTile *tile = room->tiles[pos.x][pos.y];
if (!tile) {
error("Looks like we are out of place");
2017-12-22 06:27:58 +01:00
return;
}
if (tile->levelExit) {
2018-02-09 09:36:24 +01:00
++cLevel;
resetGame();
2017-12-22 06:27:58 +01:00
}
}
static void
run_game(void)
{
map_clear_dead_monsters(gMap);
map_clear_collected_items(gMap);
roommatrix_populate_from_map(gRoomMatrix, gMap);
roommatrix_add_lightsource(gRoomMatrix,
&gPlayer->sprite->pos);
2018-01-31 09:15:33 +01:00
roommatrix_build_lightmap(gRoomMatrix);
2018-02-01 09:04:19 +01:00
gui_update_player_stats(gGui, gPlayer, gMap, gRenderer);
2018-02-03 23:39:49 +01:00
particle_engine_update(deltaTime);
if (gPlayer->steps >= gPlayer->stats.speed) {
player_reset_steps(gPlayer);
roommatrix_update_with_player(gRoomMatrix, gPlayer);
map_move_monsters(gMap, gRoomMatrix);
}
2018-02-09 09:36:24 +01:00
SDL_SetRenderDrawColor(gRenderer, 0, 0, 0, 0);
SDL_RenderClear(gRenderer);
2018-01-23 12:14:44 +01:00
SDL_RenderSetViewport(gRenderer, &gameViewport);
map_render(gMap, &gCamera);
2018-02-03 23:39:49 +01:00
particle_engine_render(&gCamera);
player_render(gPlayer, &gCamera);
if (gPlayer->class == MAGE || gPlayer->class == PALADIN)
roommatrix_render_mouse_square(gRoomMatrix, &gCamera);
roommatrix_render_lightmap(gRoomMatrix, &gCamera);
2018-01-23 12:14:44 +01:00
SDL_RenderSetViewport(gRenderer, &rightGuiViewport);
gui_render_panel(gGui, RIGHT_GUI_WIDTH,
RIGHT_GUI_HEIGHT, &gCamera);
SDL_RenderSetViewport(gRenderer, &bottomGuiViewport);
gui_render_log(gGui, BOTTOM_GUI_WIDTH,
BOTTOM_GUI_HEIGHT, &gCamera);
2018-01-31 09:15:33 +01:00
SDL_RenderSetViewport(gRenderer, NULL);
pointer_render(gPointer, &gCamera);
SDL_RenderPresent(gRenderer);
if (check_if_dead())
gGameState = GAME_OVER;
check_next_level();
}
static void
run_menu(void)
{
2018-02-09 09:36:24 +01:00
SDL_SetRenderDrawColor(gRenderer, 0, 0, 0, 0);
SDL_RenderClear(gRenderer);
SDL_RenderSetViewport(gRenderer, NULL);
menu_render(mainMenu, &gCamera);
pointer_render(gPointer, &gCamera);
SDL_RenderPresent(gRenderer);
}
2017-11-30 21:00:47 +01:00
static
void run(void)
2017-11-30 21:00:47 +01:00
{
2018-02-03 23:39:49 +01:00
static int oldTime = 0;
static int currentTime = 0;
2017-11-30 21:00:47 +01:00
bool quit = false;
2017-12-05 08:30:08 +01:00
Timer* fpsTimer = timer_create();
2017-11-30 21:00:47 +01:00
2018-01-25 16:42:57 +01:00
gui_log("The Dungeon Crawl begins!");
2017-11-30 21:00:47 +01:00
while (!quit)
{
2017-12-05 08:30:08 +01:00
timer_start(fpsTimer);
2017-11-30 21:00:47 +01:00
2017-12-03 11:09:57 +01:00
quit = handle_events();
2017-11-30 21:00:47 +01:00
switch (gGameState) {
case PLAYING:
run_game();
break;
case MENU:
run_menu();
break;
case IN_GAME_MENU:
fatal("IN_GAME_MENU not implemented");
break;
case GAME_OVER:
fatal("GAME_OVER not implemented");
break;
case QUIT:
quit = true;
break;
default:
break;
}
2017-12-22 06:27:58 +01:00
2017-12-05 08:30:08 +01:00
int ticks = timer_get_ticks(fpsTimer);
2017-11-30 21:00:47 +01:00
if (ticks < 1000/60)
SDL_Delay((1000/60) - ticks);
2017-12-05 08:30:08 +01:00
timer_stop(fpsTimer);
2018-02-03 23:39:49 +01:00
if (currentTime == 0)
currentTime = SDL_GetTicks();
else {
oldTime = currentTime;
currentTime = SDL_GetTicks();
deltaTime = (currentTime - oldTime) / 1000.0;
}
2017-11-30 21:00:47 +01:00
}
2017-12-05 08:30:08 +01:00
timer_destroy(fpsTimer);
2017-11-30 21:00:47 +01:00
}
static
void close(void)
2017-11-30 21:00:47 +01:00
{
2018-02-09 09:36:24 +01:00
if (gPlayer)
player_destroy(gPlayer);
if (gMap)
map_destroy(gMap);
if (mainMenu)
menu_destroy(mainMenu);
roommatrix_destroy(gRoomMatrix);
gui_destroy(gGui);
2018-01-31 09:15:33 +01:00
pointer_destroy(gPointer);
item_builder_close();
2018-02-03 23:39:49 +01:00
particle_engine_close();
SDL_DestroyRenderer(gRenderer);
2017-11-30 21:00:47 +01:00
SDL_DestroyWindow(gWindow);
gWindow = NULL;
TTF_Quit();
2017-11-30 21:00:47 +01:00
IMG_Quit();
SDL_Quit();
}
int main(int argc, char *argv[])
2017-11-30 21:00:47 +01:00
{
UNUSED(argc);
UNUSED(argv);
2017-11-30 21:00:47 +01:00
if (!init())
return 1;
loadMedia();
run();
close();
return 0;
}