Merge branch 'dev'

This commit is contained in:
Linus Probert 2018-08-23 09:45:53 +02:00
commit c23927c4fa
44 changed files with 1418 additions and 349 deletions

View File

@ -7,7 +7,7 @@ project(breakhack C)
set(breakhack_GAME_TITLE "BreakHack")
set(breakhack_MAJOR_VERSION 0)
set(breakhack_MINOR_VERSION 2)
set(breakhack_PATCH_VERSION 1)
set(breakhack_PATCH_VERSION 0)
set(breakhack_RELEASE_TYPE "(beta)")
include(FindLua)
@ -100,6 +100,7 @@ include_directories(
${SDL2_IMAGE_INCLUDE_DIR}
${SDL2_TTF_INCLUDE_DIR}
${SDL2_MIXER_INCLUDE_DIR}
sqlite3
)
if (CMOCKA_FOUND)
@ -167,7 +168,7 @@ add_executable(breakhack
src/projectile
src/vector2d
src/map_room_modifiers
src/sqlite3
sqlite3/sqlite3
src/db
src/settings
src/actiontextbuilder
@ -177,10 +178,12 @@ add_executable(breakhack
src/screen
src/hiscore
src/object
src/gui_util
src/tooltip
)
# Sqlite has some warnings that I we don't need to see
set_source_files_properties(src/sqlite3.c COMPILE_FLAGS -w)
set_source_files_properties(sqlite3/sqlite3.c COMPILE_FLAGS -w)
target_link_libraries(breakhack
${CMAKE_DL_LIBS} # Sqlite needs DL libs
@ -241,7 +244,7 @@ ENDIF ()
# LINT:
if (CPPCHECK_FOUND)
add_custom_target(lint
COMMAND ${CPPCHECK_EXECUTABLE} --force --language=c --template=gcc --error-exitcode=1 --quiet --enable=warning,style,performance,portability,information,missingInclude --suppress=*:src/sqlite3.? -isrc/sqlite3.c src/
COMMAND ${CPPCHECK_EXECUTABLE} --force --language=c --template=gcc --error-exitcode=1 --quiet --enable=warning,style,performance,portability,information,missingInclude src/
WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}
COMMENT "Run cppcheck"
)

View File

@ -0,0 +1,20 @@
f5a88fc Introduces texture blending
52913af Completes #42 Add boss 2 & 3
449cc36 Fixes some minor buggs
f67aab0 Completes #43 Add win state
8c261fd Added the undead monsters for level > 10
6326a64 Begins #42 Add boss 2 & 3
f1b0045 Introduces the "orc levels"
ffe1736 Minor buggfixes and typos
8bf9329 Version bump and badges
8c25693 Moves sqlite3 files to separate dir
bec9eb4 Fixed a typo in skill tooltip
43f021a Created an FPS counter for debug mode
dbc36aa Fixes #41, Minimap
c390c02 Fixes #40, Prevent adjecant traps before lvl 4
9ef97c0 Fixes #38, Artifact inventory
358c0c7 Fixes #37 and #39 Adds tooltips to everything
5f754d5 Merge branch 'master' into dev
30058ea Creates gui_util and moves some code out from gui.c
854f2c0 Prevent levels > 10 from crashing the game.
549f47a Minor code fixes and a slight tweak to the blue color

View File

@ -45,7 +45,9 @@ local behaviour = {
guerilla = 3,
coward = 4,
sentinel = 5,
fire_demon = 6
fire_demon = 6,
sorcerer = 7,
assassin = 8
}
local stats = {
@ -84,19 +86,26 @@ local stats = {
def = 0,
speed = 1
},
orc = {
hp = 20,
dmg = 2,
atk = 0,
def = 0,
speed = 1
},
boss = {
hp = 60,
dmg = 4,
dmg = 3,
atk = 1,
def = 0,
speed = 1
},
platino = {
hp = 60,
dmg = 60,
atk = 60,
def = 60,
speed = 3
hp = 90,
dmg = 1,
atk = 0,
def = 0,
speed = 1
}
}
@ -134,6 +143,16 @@ for i=1,#pests do
pests[i] = concat({ texturePaths.pest0, texturePaths.pest1 }, pests[i])
end
local avian = {
{ stats.default, 0, 11*16, "A Small Brown Bat", behaviour.pacifist },
{ stats.default, 16, 11*16, "A Big Brown Bat", behaviour.normal },
{ stats.default, 32, 11*16, "A Vampire Bat", behaviour.guerilla },
{ stats.default, 48, 11*16, "A Rabid Bat", behaviour.hostile },
}
for i=1,#avian do
avian[i] = concat({ texturePaths.avian0, texturePaths.avian1 }, avian[i])
end
local misc = {
{ stats.misc, 0, 0, "A Giant Black Rat", behaviour.sentinel },
{ stats.misc, 16, 0, "A Giant White Rat", behaviour.sentinel },
@ -143,13 +162,33 @@ for i=1,#misc do
misc[i] = concat({ texturePaths.misc0, texturePaths.misc1 }, misc[i])
end
local undead = {
-- UNDEAD
local reanimated = {
{ stats.undead, 0, 32, "A Skeleton", behaviour.normal },
{ stats.undead, 48, 32, "A Black Skeleton", behaviour.normal },
{ stats.undead, 64, 32, "A Zombie", behaviour.normal },
{ stats.undead, 80, 32, "A Zombie", behaviour.normal }
}
for i=1,#reanimated do
reanimated[i] = concat({ texturePaths.undead0, texturePaths.undead1 }, reanimated[i])
end
local undead = {
{ stats.undead, 5*16, 16, "A Mummy", behaviour.normal },
{ stats.undead, 6*16, 16, "A Two Headed Mummy", behaviour.sentinel },
{ stats.undead, 0*16, 32, "A Skeleton", behaviour.normal },
{ stats.misc, 1*16, 32, "A Burning Skeleton", behaviour.fire_demon },
{ stats.misc, 2*16, 32, "An Eldritch Skeleton", behaviour.sorcerer },
{ stats.misc, 3*16, 32, "A Black Skeleton", behaviour.guerilla },
{ stats.misc, 4*16, 32, "A Zombie", behaviour.coward },
{ stats.misc, 5*16, 32, "A Pale Zombie", behaviour.coward },
{ stats.misc, 7*16, 32, "A Scorched Zombie", behaviour.fire_demon },
{ stats.undead, 0*16, 4*16, "A Whight", behaviour.coward },
{ stats.undead, 1*16, 4*16, "A Ghast", behaviour.sentinel },
{ stats.misc, 1*16, 4*16, "A Ghost", behaviour.guerilla },
{ stats.misc, 0*16, 5*16, "A Spectre", behaviour.sentinel },
{ stats.undead, 1*16, 5*16, "An Eldritch Spectre", behaviour.sorcerer },
{ stats.undead, 2*16, 5*16, "A Scorched Spectre", behaviour.fire_demon },
}
for i=1,#undead do
undead[i] = concat({ texturePaths.undead0, texturePaths.undead1 }, undead[i])
end
@ -188,10 +227,36 @@ for i=1,#demon do
demon[i] = concat({ texturePaths.demon0, texturePaths.demon1 }, demon[i])
end
local orcs = {
{ stats.orc, 0, 4*16, "An Orc Guard", behaviour.normal },
{ stats.orc, 16, 4*16, "An Orc Seargeant", behaviour.coward },
{ stats.orc, 32, 4*16, "An Orc Militia", behaviour.hostile },
{ stats.orc, 48, 4*16, "An Orc Sentry", behaviour.sentinel },
{ stats.orc, 64, 4*16, "An Orc Brute", behaviour.guerilla },
{ stats.orc, 80, 4*16, "An Orc Captain", behaviour.hostile },
{ stats.orc, 96, 4*16, "An Orc Pyro", behaviour.fire_demon },
}
for i=1,#orcs do
orcs[i] = concat({ texturePaths.humanoid0, texturePaths.humanoid1 }, orcs[i])
end
local assassins = {
{ stats.misc, 1*16, 6*16, "A Reaper", behaviour.assassin },
{ stats.misc, 0*16, 7*16, "An Assassin", behaviour.assassin },
{ stats.misc, 1*16, 7*16, "A Royal Assassin", behaviour.assassin },
}
for i=1,#assassins do
assassins[i] = concat({ texturePaths.undead0, texturePaths.undead1 }, assassins[i])
end
local bosses = {
{ stats.boss, 16, 5*16, "The Hell Hound", behaviour.fire_demon, true }
{ stats.boss, 16, 5*16, "The Hell Hound", behaviour.fire_demon, true },
{ stats.boss, 16, 23*16, "The Cleric", behaviour.sorcerer, true },
{ stats.boss, 16, 8*16, "The Shadow", behaviour.assassin, true },
}
bosses[1] = concat({ texturePaths.dog0, texturePaths.dog1 }, bosses[1])
bosses[2] = concat({ texturePaths.humanoid0, texturePaths.humanoid1 }, bosses[2])
bosses[3] = concat({ texturePaths.undead0, texturePaths.undead1 }, bosses[3])
local platino = {
{
@ -201,7 +266,8 @@ local platino = {
48,
12*16,
"Platino",
behaviour.sentinel
behaviour.sentinel,
true
}
}
@ -224,29 +290,24 @@ if(CURRENT_LEVEL > 0) then
if (CURRENT_LEVEL == 1) then
enemies = concat(enemies, pests)
enemies = concat(enemies, misc)
enemies = concat(enemies, dogs)
elseif (CURRENT_LEVEL > 15) then
enemies = {}
enemies = concat(enemies, undead)
enemies = concat(enemies, orcs)
enemies = concat(enemies, assassins)
elseif (CURRENT_LEVEL > 10) then
enemies = {}
enemies = concat(enemies, undead)
enemies = concat(enemies, avian)
elseif (CURRENT_LEVEL > 5) then
enemies = {}
enemies = concat(enemies, demon)
enemies = concat(enemies, undead)
enemies = concat(enemies, reptile)
enemies = concat(enemies, misc)
elseif (CURRENT_LEVEL > 3) then
enemies = {}
enemies = concat(enemies, undead)
enemies = concat(enemies, reptile)
enemies = concat(enemies, misc)
enemies = concat(enemies, dogs)
elseif (CURRENT_LEVEL > 2) then
enemies = {}
enemies = concat(enemies, undead)
enemies = concat(enemies, reptile)
enemies = concat(enemies, misc)
enemies = concat(enemies, dogs)
enemies = concat(enemies, orcs)
enemies = concat(enemies, avian)
elseif (CURRENT_LEVEL > 1) then
enemies = {}
enemies = concat(enemies, undead)
enemies = concat(enemies, reanimated)
enemies = concat(enemies, reptile)
enemies = concat(enemies, avian)
enemies = concat(enemies, misc)
enemies = concat(enemies, dogs)
end
@ -279,7 +340,7 @@ function module.add_monsters_to_room(room, roomx, roomy)
end
function module.add_boss_to_room(room, roomx, roomy)
local boss = bosses[1]
local boss = bosses[CURRENT_LEVEL / 5]
local success = false
while not success do
local rx = random(13) + 1
@ -309,3 +370,4 @@ function module.load_monsters(map, monsters)
end
return module

View File

@ -35,9 +35,25 @@ function module.add_traps_to_room(room)
while i < count do
local rx = random(13) + 1
local ry = random(9) + 1
if room_builder.is_tile_avilable(room, rx, ry) then
room.traps[rx][ry] = traps[random(#traps)]
i = i + 1
if CURRENT_LEVEL < 4 then
if room_builder.is_tile_avilable(room, rx, ry)
and not room.traps[rx+1][ry]
and not room.traps[rx-1][ry]
and not room.traps[rx][ry+1]
and not room.traps[rx][ry-1]
and not room.traps[rx+1][ry+1]
and not room.traps[rx+1][ry-1]
and not room.traps[rx-1][ry+1]
and not room.traps[rx-1][ry-1]
then
room.traps[rx][ry] = traps[random(#traps)]
i = i + 1
end
else
if room_builder.is_tile_avilable(room, rx, ry) then
room.traps[rx][ry] = traps[random(#traps)]
i = i + 1
end
end
end
end

View File

@ -26,65 +26,39 @@
static void
artifact_set_effect(Artifact *a, MagicalEffect effect)
{
Texture *t;
a->effect = effect;
switch (effect) {
case IMPROVED_HEARING:
a->info.name = "Potion of ear juice";
a->info.desc = "Your hearing is slightly improved";
t = texturecache_add("Items/Potion.png");
sprite_set_texture(a->sprite, t, 0);
a->sprite->clip = CLIP16(7*16, 4*16);
break;
case TRAP_AVOIDANCE:
a->info.name = "Boot with nails inside";
a->info.desc = "You are lighter on your feet";
t = texturecache_add("Items/Boot.png");
sprite_set_texture(a->sprite, t, 0);
a->sprite->clip = CLIP16(5*16, 0);
break;
case PIERCING_DAGGERS:
a->info.name = "Whetstone";
a->info.desc = "Your daggers are sharper";
t = texturecache_add("Items/Rock.png");
sprite_set_texture(a->sprite, t, 0);
a->sprite->clip = CLIP16(0, 0);
break;
case CHARGE_THROUGH:
a->info.name = "Greasy shield";
a->info.desc = "You glide through obstructions";
t = texturecache_add("Items/Shield.png");
sprite_set_texture(a->sprite, t, 0);
a->sprite->clip = CLIP16(16, 0);
break;
case PUSH_BACK:
a->info.name = "Glove of strength";
a->info.desc = "Your arm is stronger";
t = texturecache_add("Items/Glove.png");
sprite_set_texture(a->sprite, t, 0);
a->sprite->clip = CLIP16(0, 0);
break;
case DAGGER_RECOVERY:
a->info.name = "Forging hammer";
a->info.desc = "Your daggers are more durable";
t = texturecache_add("Items/LongWep.png");
sprite_set_texture(a->sprite, t, 0);
a->sprite->clip = CLIP16(0, 6*16);
break;
case INCREASED_STUN:
a->info.name = "Solid shield";
a->info.desc = "Your shield is harder";
t = texturecache_add("Items/Shield.png");
sprite_set_texture(a->sprite, t, 0);
a->sprite->clip = CLIP16(4*16, 0);
break;
case FEAR_INDUCING:
a->info.name = "Ugly shirt";
a->info.desc = "You look disgusting";
t = texturecache_add("Items/Armor.png");
sprite_set_texture(a->sprite, t, 0);
a->sprite->clip = CLIP16(6*16, 8*16);
break;
default:
break;
@ -107,11 +81,63 @@ artifact_create_random(Player *p, Uint8 level)
return a;
}
Sprite *
artifact_sprite_for(MagicalEffect effect)
{
Sprite *sprite = sprite_create();
Texture *t;
switch (effect) {
case IMPROVED_HEARING:
t = texturecache_add("Items/Potion.png");
sprite_set_texture(sprite, t, 0);
sprite->clip = CLIP16(7*16, 4*16);
break;
case TRAP_AVOIDANCE:
t = texturecache_add("Items/Boot.png");
sprite_set_texture(sprite, t, 0);
sprite->clip = CLIP16(5*16, 0);
break;
case PIERCING_DAGGERS:
t = texturecache_add("Items/Rock.png");
sprite_set_texture(sprite, t, 0);
sprite->clip = CLIP16(0, 0);
break;
case CHARGE_THROUGH:
t = texturecache_add("Items/Shield.png");
sprite_set_texture(sprite, t, 0);
sprite->clip = CLIP16(16, 0);
break;
case PUSH_BACK:
t = texturecache_add("Items/Glove.png");
sprite_set_texture(sprite, t, 0);
sprite->clip = CLIP16(0, 0);
break;
case DAGGER_RECOVERY:
t = texturecache_add("Items/LongWep.png");
sprite_set_texture(sprite, t, 0);
sprite->clip = CLIP16(0, 6*16);
break;
case INCREASED_STUN:
t = texturecache_add("Items/Shield.png");
sprite_set_texture(sprite, t, 0);
sprite->clip = CLIP16(4*16, 0);
break;
case FEAR_INDUCING:
t = texturecache_add("Items/Armor.png");
sprite_set_texture(sprite, t, 0);
sprite->clip = CLIP16(6*16, 8*16);
break;
default:
break;
}
return sprite;
}
Artifact *
artifact_create(MagicalEffect effect)
{
Artifact *a = ec_malloc(sizeof(Artifact));
a->sprite = sprite_create();
a->sprite = artifact_sprite_for(effect);
a->sprite->dim = GAME_DIMENSION;
a->collected = false;
a->level = 1;

View File

@ -45,6 +45,9 @@ typedef struct Artifact {
int level;
} Artifact;
Sprite *
artifact_sprite_for(MagicalEffect);
Artifact *
artifact_create_random(Player*, Uint8 level);

View File

@ -33,20 +33,21 @@
#define SPRITE_DIMENSION 16
/* Display stuff */
#define GAME_VIEW_WIDTH (MAP_ROOM_WIDTH * TILE_DIMENSION)
#define GAME_VIEW_HEIGHT (MAP_ROOM_HEIGHT * TILE_DIMENSION)
#define GAME_VIEW_WIDTH (MAP_ROOM_WIDTH * TILE_DIMENSION) // 16 * 32
#define GAME_VIEW_HEIGHT (MAP_ROOM_HEIGHT * TILE_DIMENSION) // 12 * 32
#define SKILL_BAR_WIDTH GAME_VIEW_WIDTH
#define SKILL_BAR_HEIGHT 32
#define RIGHT_GUI_WIDTH (10 * SPRITE_DIMENSION)
#define RIGHT_GUI_HEIGHT (GAME_VIEW_HEIGHT + SKILL_BAR_HEIGHT)
#define RIGHT_GUI_WIDTH (10 * SPRITE_DIMENSION) // 10 * 16
#define MINIMAP_GUI_HEIGHT 128
#define STATS_GUI_HEIGHT (GAME_VIEW_HEIGHT + SKILL_BAR_HEIGHT - MINIMAP_GUI_HEIGHT)
#define BOTTOM_GUI_HEIGHT (10 * SPRITE_DIMENSION)
#define BOTTOM_GUI_WIDTH (GAME_VIEW_WIDTH + RIGHT_GUI_WIDTH)
#define SCREEN_WIDTH (GAME_VIEW_WIDTH + RIGHT_GUI_WIDTH)
#define SCREEN_HEIGHT (RIGHT_GUI_HEIGHT + BOTTOM_GUI_HEIGHT)
#define SCREEN_HEIGHT (GAME_VIEW_HEIGHT + SKILL_BAR_HEIGHT + BOTTOM_GUI_HEIGHT)
/* Quality of life stuff */
#define DEFAULT_DIMENSION (Dimension) { 16, 16 }
@ -65,7 +66,7 @@
#define C_WHITE (SDL_Color) { 255, 255, 255, 255 }
#define C_RED (SDL_Color) { 255, 0, 0, 255 }
#define C_GREEN (SDL_Color) { 0, 255, 0, 255 }
#define C_BLUE (SDL_Color) { 0, 0, 255, 255 }
#define C_BLUE (SDL_Color) { 60, 134, 252, 255 }
#define C_YELLOW (SDL_Color) { 255, 255, 0, 255 }
#define C_BLACK (SDL_Color) { 0, 0, 0, 255 }
#define C_PURPLE (SDL_Color) { 137, 16, 229, 255 }

View File

@ -19,6 +19,8 @@
#ifndef DIMENSION_H_
#define DIMENSION_H_
#define DIM(x, y) (Dimension) { x, y }
typedef struct {
unsigned int width;
unsigned int height;

View File

@ -26,6 +26,7 @@ typedef enum GameState_t {
PLAYING,
IN_GAME_MENU,
GAME_OVER,
COMPLETED,
QUIT
} GameState;

158
src/gui.c
View File

@ -26,6 +26,8 @@
#include "util.h"
#include "map.h"
#include "texturecache.h"
#include "gui_util.h"
#include "tooltip.h"
#define DEFAULT_LOG { NULL, LOG_LINES_COUNT, 0, 200 }
#define DEFAULT_EVENT_MESSAGES { NULL, 5, 0, 200 }
@ -33,16 +35,6 @@
#define POS_Y_COLLECTABLES 64
#define POS_Y_XPBAR 128
static SDL_Rect frame_top_left = { 16, 160, 16, 16 };
static SDL_Rect frame_top_right = { 48, 160, 16, 16 };
static SDL_Rect frame_bottom_left = { 16, 192, 16, 16 };
static SDL_Rect frame_bottom_right = { 48, 192, 16, 16 };
static SDL_Rect frame_top = { 32, 160, 16, 16 };
static SDL_Rect frame_bottom = { 32, 192, 16, 16 };
static SDL_Rect frame_center = { 32, 176, 16, 16 };
static SDL_Rect frame_left = { 16, 176, 16, 16 };
static SDL_Rect frame_right = { 48, 176, 16, 16 };
static struct LogData_t {
char **log;
unsigned int len;
@ -50,16 +42,13 @@ static struct LogData_t {
unsigned int strlen;
} log_data = DEFAULT_LOG;
static struct GuiEventMsgData_t {
static struct GuiEventMsgs {
char **messages;
unsigned int len;
unsigned int count;
unsigned int strlen;
} event_messages = DEFAULT_EVENT_MESSAGES;
static Sprite*
gui_create_frame(unsigned int width, unsigned int height, Camera *cam);
static void
gui_malloc_log(void)
{
@ -174,12 +163,16 @@ init_sprites(Gui *gui, Camera *cam)
s->pos = (Position) { 16, POS_Y_COLLECTABLES + 32 };
linkedlist_append(&gui->sprites, s);
gui->rightFrame = gui_create_frame(RIGHT_GUI_WIDTH/16,
RIGHT_GUI_HEIGHT/16,
cam);
gui->bottomFrame = gui_create_frame(BOTTOM_GUI_WIDTH/16,
BOTTOM_GUI_HEIGHT/16,
cam);
gui->statsFrame = gui_util_create_frame_sprite(RIGHT_GUI_WIDTH/16,
STATS_GUI_HEIGHT/16,
cam);
gui->bottomFrame = gui_util_create_frame_sprite(BOTTOM_GUI_WIDTH/16,
BOTTOM_GUI_HEIGHT/16,
cam);
gui->miniMapFrame = gui_util_create_frame_sprite(RIGHT_GUI_WIDTH/16,
MINIMAP_GUI_HEIGHT/16,
cam);
}
Gui*
@ -192,6 +185,7 @@ gui_create(Camera *cam)
gui->sprites = linkedlist_create();
gui->health = linkedlist_create();
gui->xp_bar = linkedlist_create();
gui->activeTooltip = NULL;
for (i = 0; i < LOG_LINES_COUNT; ++i) {
t = texture_create();
@ -400,91 +394,10 @@ gui_update_player_stats(Gui *gui, Player *player, Map *map, SDL_Renderer *render
}
}
static Sprite*
gui_create_frame(unsigned int width, unsigned int height, Camera *cam)
{
Sprite *frame = sprite_create();
Texture *texture = texture_create();
texture->dim = (Dimension) {
width * 16,
height * 16
};
frame->textures[0] = texture;
frame->destroyTextures = true;
frame->pos = (Position) { 0, 0 };
frame->dim = (Dimension) { width*16, height*16 };
frame->fixed = true;
texture_create_blank(texture,
SDL_TEXTUREACCESS_TARGET,
cam->renderer);
Texture *source = texturecache_get("GUI/GUI0.png");
SDL_SetRenderTarget(cam->renderer, texture->texture);
SDL_RenderClear(cam->renderer);
SDL_Rect box = { 0, 0, 16, 16 };
unsigned int i, j;
for (i = 0; i < width; ++i) {
for (j = 0; j < height; ++j) {
box.x = i * 16;
box.y = j * 16;
if (i == 0 && j == 0) {
texture_render_clip(source,
&box,
&frame_top_left,
cam);
} else if (i == (width - 1) && j == 0) {
texture_render_clip(source,
&box,
&frame_top_right,
cam);
} else if (i == 0 && j == (height - 1)) {
texture_render_clip(source,
&box,
&frame_bottom_left,
cam);
} else if (i == (width - 1) && j == (height - 1)) {
texture_render_clip(source,
&box,
&frame_bottom_right,
cam);
} else if (i == 0) {
texture_render_clip(source,
&box,
&frame_left,
cam);
} else if (i == (width - 1)) {
texture_render_clip(source,
&box,
&frame_right,
cam);
} else if (j == 0) {
texture_render_clip(source,
&box,
&frame_top,
cam);
} else if (j == (height - 1)) {
texture_render_clip(source,
&box,
&frame_bottom,
cam);
} else {
texture_render_clip(source,
&box,
&frame_center,
cam);
}
}
}
SDL_SetRenderTarget(cam->renderer, NULL);
return frame;
}
void
gui_render_panel(Gui *gui, Camera *cam)
{
sprite_render(gui->rightFrame, cam);
sprite_render(gui->statsFrame, cam);
LinkedList *item = gui->health;
while (item != NULL) {
Sprite *s = item->data;
@ -508,6 +421,30 @@ gui_render_panel(Gui *gui, Camera *cam)
sprite_render(gui->labels[i], cam);
}
void
gui_render_minimap(Gui *gui, Map *map, Camera *cam)
{
sprite_render(gui->miniMapFrame, cam);
SDL_Rect box = { 0, 0, 12, 8 };
for (Uint8 i = 0; i < MAP_H_ROOM_COUNT; ++i) {
for (Uint8 j = 0; j < MAP_V_ROOM_COUNT; ++j) {
Room *room = map->rooms[i][j];
box.x = i*14 + 10;
box.y = j*10 + 14;
if (room && room->visited) {
if (map->currentRoom.x == i && map->currentRoom.y == j)
SDL_SetRenderDrawColor(cam->renderer, 0, 255, 255, 255);
else
SDL_SetRenderDrawColor(cam->renderer, 255, 255, 255, 255);
SDL_RenderFillRect(cam->renderer, &box);
SDL_SetRenderDrawColor(cam->renderer, 60, 134, 252, 255);
SDL_RenderDrawRect(cam->renderer, &box);
}
}
}
}
void
gui_log(const char *fmt, ...)
{
@ -557,6 +494,14 @@ gui_event_message(const char *fmt, ...)
event_messages.count++;
}
void
gui_render_tooltip(Gui *gui, Camera *cam)
{
if (gui->activeTooltip) {
sprite_render(gui->activeTooltip, cam);
}
}
void
gui_render_log(Gui *gui, Camera *cam)
{
@ -613,8 +558,9 @@ gui_render_event_message(Gui *gui, Camera *cam)
void
gui_clear_message_log(void)
{
for (size_t i = 0; i < event_messages.count; ++i)
for (size_t i = 0; i < event_messages.count; ++i) {
free(event_messages.messages[i]);
}
event_messages.count = 0;
for (size_t i = 0; i < log_data.count; ++i)
@ -642,8 +588,9 @@ destroy_event_messages(void)
if (event_messages.messages == NULL)
return;
for (unsigned int i = 0; i < event_messages.count; ++i)
for (unsigned int i = 0; i < event_messages.count; ++i) {
free(event_messages.messages[i]);
}
free(event_messages.messages);
event_messages.messages = NULL;
@ -659,7 +606,8 @@ gui_destroy(Gui *gui)
texture_destroy(gui->event_message);
sprite_destroy(gui->bottomFrame);
sprite_destroy(gui->rightFrame);
sprite_destroy(gui->statsFrame);
sprite_destroy(gui->miniMapFrame);
while (gui->sprites != NULL)
sprite_destroy(linkedlist_pop(&gui->sprites));

View File

@ -42,13 +42,15 @@ typedef enum Label_e {
LABEL_COUNT
} LabelIndex;
typedef struct {
typedef struct Gui {
LinkedList *sprites;
LinkedList *health;
LinkedList *xp_bar;
Sprite *bottomFrame;
Sprite *rightFrame;
Sprite *statsFrame;
Sprite *miniMapFrame;
Sprite *labels[LABEL_COUNT];
Sprite *activeTooltip;
Texture *log_lines[LOG_LINES_COUNT];
Texture *event_message;
Timer *event_message_timer;
@ -63,12 +65,18 @@ gui_update_player_stats(Gui*, Player*, Map*, SDL_Renderer*);
void
gui_render_panel(Gui*, Camera*);
void
gui_render_minimap(Gui*, Map*, Camera*);
void
gui_render_log(Gui*, Camera*);
void
gui_render_event_message(Gui*, Camera*);
void
gui_render_tooltip(Gui*, Camera*);
void
gui_log(const char *fmt, ...);

132
src/gui_util.c Normal file
View File

@ -0,0 +1,132 @@
/*
* BreakHack - A dungeone crawler RPG
* Copyright (C) 2018 Linus Probert <linus.probert@gmail.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "texturecache.h"
#include "gui_util.h"
static Sprite *
render_frame_on_texture(Uint32 width,
Uint32 height,
Position offset,
Camera *cam)
{
Sprite *frame = sprite_create();
Texture *texture = texture_create();
texture->dim = (Dimension) {
width * 16,
height * 16
};
frame->textures[0] = texture;
frame->destroyTextures = true;
frame->pos = (Position) { 0, 0 };
frame->dim = (Dimension) { width*16, height*16 };
frame->fixed = true;
texture_create_blank(texture,
SDL_TEXTUREACCESS_TARGET,
cam->renderer);
SDL_SetRenderTarget(cam->renderer, texture->texture);
SDL_RenderClear(cam->renderer);
SDL_Rect frame_top_left = CLIP16(offset.x, offset.y);
SDL_Rect frame_top_right = CLIP16(offset.x + 32, offset.y);
SDL_Rect frame_bottom_left = CLIP16(offset.x, offset.y + 32);
SDL_Rect frame_bottom_right = CLIP16(offset.x + 32, offset.y + 32);
SDL_Rect frame_top = CLIP16(offset.x + 16, offset.y);
SDL_Rect frame_bottom = CLIP16(offset.x + 16, offset.y + 32);
SDL_Rect frame_center = CLIP16(offset.x + 16, offset.y + 16);
SDL_Rect frame_left = CLIP16(offset.x, offset.y + 16);
SDL_Rect frame_right = CLIP16(offset.x + 32, offset.y + 16);
Texture *source = texturecache_get("GUI/GUI0.png");
SDL_Rect box = { 0, 0, 16, 16 };
unsigned int i, j;
for (i = 0; i < width; ++i) {
for (j = 0; j < height; ++j) {
box.x = i * 16;
box.y = j * 16;
if (i == 0 && j == 0) {
texture_render_clip(source,
&box,
&frame_top_left,
cam);
} else if (i == (width - 1) && j == 0) {
texture_render_clip(source,
&box,
&frame_top_right,
cam);
} else if (i == 0 && j == (height - 1)) {
texture_render_clip(source,
&box,
&frame_bottom_left,
cam);
} else if (i == (width - 1) && j == (height - 1)) {
texture_render_clip(source,
&box,
&frame_bottom_right,
cam);
} else if (i == 0) {
texture_render_clip(source,
&box,
&frame_left,
cam);
} else if (i == (width - 1)) {
texture_render_clip(source,
&box,
&frame_right,
cam);
} else if (j == 0) {
texture_render_clip(source,
&box,
&frame_top,
cam);
} else if (j == (height - 1)) {
texture_render_clip(source,
&box,
&frame_bottom,
cam);
} else {
texture_render_clip(source,
&box,
&frame_center,
cam);
}
}
}
SDL_SetRenderTarget(cam->renderer, NULL);
return frame;
}
Sprite *
gui_util_create_frame_sprite(Uint32 width,
Uint32 height,
Camera *cam)
{
return render_frame_on_texture(width, height, POS(16, 16*10), cam);
}
Sprite *
gui_util_create_tooltip_frame_sprite(Uint32 width,
Uint32 height,
Camera *cam)
{
return render_frame_on_texture(width, height, POS(16*13, 16*13), cam);
}

32
src/gui_util.h Normal file
View File

@ -0,0 +1,32 @@
/*
* BreakHack - A dungeone crawler RPG
* Copyright (C) 2018 Linus Probert <linus.probert@gmail.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include "sprite.h"
#include "camera.h"
Sprite *
gui_util_create_frame_sprite(Uint32 width,
Uint32 height,
Camera*);
Sprite *
gui_util_create_tooltip_frame_sprite(Uint32 width,
Uint32 height,
Camera*);

View File

@ -107,6 +107,21 @@ get_event_modkey(SDL_Event *event)
key = KEY_CTRL_S; break;
case SDLK_m:
key = KEY_CTRL_M; break;
case SDLK_d:
key = KEY_CTRL_D; break;
}
} else if (event->key.keysym.mod & (KMOD_LSHIFT | KMOD_RSHIFT)) {
switch (event->key.keysym.sym) {
case SDLK_1:
key = KEY_SHIFT_NUM1; break;
case SDLK_2:
key = KEY_SHIFT_NUM2; break;
case SDLK_3:
key = KEY_SHIFT_NUM3; break;
case SDLK_4:
key = KEY_SHIFT_NUM4; break;
case SDLK_5:
key = KEY_SHIFT_NUM5; break;
default:
key = 0; break;
}

View File

@ -39,8 +39,14 @@
#define KEY_ESC 16384
#define KEY_ENTER 32768
#define KEY_CTRL_M 1
#define KEY_CTRL_S 2
#define KEY_CTRL_M 0x1
#define KEY_CTRL_S 0x2
#define KEY_CTRL_D 0x4
#define KEY_SHIFT_NUM1 0x8
#define KEY_SHIFT_NUM2 0x10
#define KEY_SHIFT_NUM3 0x20
#define KEY_SHIFT_NUM4 0x40
#define KEY_SHIFT_NUM5 0x80
#define MBUTTON_LEFT 1
#define MBUTTON_MIDDLE 2

View File

@ -52,38 +52,116 @@
#include "screen.h"
#include "hiscore.h"
#include "io_util.h"
#include "tooltip.h"
static char *artifacts_tooltip[] = {
"CONGRATULATIONS!",
"",
" You just picked up your first artifact!",
"",
" Your current artifacts and corresponding level are",
" listed next to your skills."
"",
"",
" Artifacts have mystical effects that improve your offensive",
" or defensive advantage in the dungeon. However it is sometimes",
" hard to know what effect an artifact has.",
"",
"",
" Perhaps an experienced dungeoner will know more?",
"",
"",
"Press ESC to close",
NULL
};
static char *skills_tooltip[] = {
"CONGRATULATIONS!",
"",
" You have aquired a new level and a new skill!",
"",
" Skills are listed in the bar below the game screen.",
"",
"",
" SKILL INFO: SHIFT + <NUM>",
" Where <NUM> is the skill number (1-5)",
"",
" DISABLE TOOLTIPS: CTRL + D",
"",
"",
"Press ESC to close",
NULL
};
static char *how_to_play_tooltip[] = {
"HOW TO PLAY",
"",
" NAVIGATION: Use ARROWS or WASD or HJKL to move",
"",
" ATTACK: Walk into a monster to attack it",
"",
" THROW DAGGER: Press 4 then chose a direction (navigation keys)",
"",
" DRINK HEALTH: Press 5 (if you need health and have potions)",
"",
" TOGGLE MUSIC: CTRL + M",
"",
" TOGGLE SOUND: CTRL + S",
"",
" TOGGLE MENU: ESC",
"",
" Your stats and inventory are listed in the right panel",
"",
"",
" GOOD LUCK!",
" May your death be quick and painless...",
"",
"",
"",
"Press ESC to close",
NULL
};
typedef enum Turn_t {
PLAYER,
MONSTER
} Turn;
static SDL_Window *gWindow = NULL;
static SDL_Renderer *gRenderer = NULL;
static Player *gPlayer = NULL;
static Map *gMap = NULL;
static RoomMatrix *gRoomMatrix = NULL;
static Gui *gGui = NULL;
static SkillBar *gSkillBar = NULL;
static Pointer *gPointer = NULL;
static Menu *mainMenu = NULL;
static Menu *inGameMenu = NULL;
static Timer *menuTimer = NULL;
static Camera *gCamera = NULL;
static Screen *creditsScreen = NULL;
static Screen *scoreScreen = NULL;
static unsigned int cLevel = 1;
static float deltaTime = 1.0;
static double renderScale = 1.0;
static SDL_Window *gWindow = NULL;
static SDL_Renderer *gRenderer = NULL;
static Player *gPlayer = NULL;
static Map *gMap = NULL;
static RoomMatrix *gRoomMatrix = NULL;
static Gui *gGui = NULL;
static SkillBar *gSkillBar = NULL;
static Menu *mainMenu = NULL;
static Menu *inGameMenu = NULL;
static Timer *menuTimer = NULL;
static Camera *gCamera = NULL;
static Screen *creditsScreen = NULL;
static Screen *scoreScreen = NULL;
static Sprite *new_skill_tooltip = NULL;
static Sprite *howto_tooltip = NULL;
static Sprite *new_artifact_tooltip = NULL;
static unsigned int cLevel = 1;
static float deltaTime = 1.0;
static double renderScale = 1.0;
static Turn currentTurn = PLAYER;
static GameState gGameState;
static SDL_Rect gameViewport;
static SDL_Rect skillBarViewport;
static SDL_Rect bottomGuiViewport;
static SDL_Rect rightGuiViewport;
static SDL_Rect statsGuiViewport;
static SDL_Rect minimapViewport;
static SDL_Rect menuViewport;
static Turn currentTurn = PLAYER;
static Input input;
#ifdef DEBUG
static Sprite *fpsSprite = NULL;
static Pointer *gPointer = NULL;
#endif // DEBUG
static SDL_Color C_MENU_DEFAULT = { 255, 255, 0, 255 };
static SDL_Color C_MENU_OUTLINE_DEFAULT = { 0, 0, 0, 255 };
static SDL_Color C_MENU_HOVER = { 255, 0, 0, 255 };
@ -180,8 +258,11 @@ initViewports(void)
bottomGuiViewport = (SDL_Rect) { 0, GAME_VIEW_HEIGHT + SKILL_BAR_HEIGHT,
BOTTOM_GUI_WIDTH, BOTTOM_GUI_WIDTH };
rightGuiViewport = (SDL_Rect) { GAME_VIEW_WIDTH, 0,
RIGHT_GUI_WIDTH, RIGHT_GUI_HEIGHT };
statsGuiViewport = (SDL_Rect) { GAME_VIEW_WIDTH, 0,
RIGHT_GUI_WIDTH, STATS_GUI_HEIGHT };
minimapViewport = (SDL_Rect) { GAME_VIEW_WIDTH, STATS_GUI_HEIGHT,
RIGHT_GUI_WIDTH, MINIMAP_GUI_HEIGHT };
menuViewport = (SDL_Rect) {
(SCREEN_WIDTH - GAME_VIEW_WIDTH)/2,
@ -200,13 +281,22 @@ initGame(void)
gCamera = camera_create(gRenderer);
gRoomMatrix = roommatrix_create();
gGui = gui_create(gCamera);
gSkillBar = skillbar_create(gRenderer);
gSkillBar = skillbar_create(gCamera);
item_builder_init(gRenderer);
#ifdef DEBUG
gPointer = pointer_create(gRenderer);
#endif // DEBUG
particle_engine_init();
menuTimer = timer_create();
actiontextbuilder_init(gRenderer);
#ifdef DEBUG
fpsSprite = sprite_create();
sprite_load_text_texture(fpsSprite, "GUI/SDS_8x8.ttf", 0, 14, 1);
fpsSprite->pos = POS(16, 16);
fpsSprite->fixed = true;
#endif // DEBUG
return true;
}
@ -218,20 +308,18 @@ startGame(void *unused)
gGameState = PLAYING;
if (gPlayer)
player_destroy(gPlayer);
gPlayer = player_create(WARRIOR, gRenderer);
gPlayer = player_create(WARRIOR, gCamera);
mixer_play_music(GAME_MUSIC0 + get_random(2));
#ifdef DEBUG
// This block is for testing
cLevel = 1;
if (cLevel % 5 == 0)
mixer_play_music(BOSS_MUSIC0);
for (size_t i = 1; i < cLevel; ++i)
player_levelup(gPlayer);
#endif // DEBUG
resetGame();
skillbar_reset(gSkillBar);
gui_clear_message_log();
gui_log("The Dungeon Crawl begins!");
gui_event_message("Welcome to the dungeon!");
Settings *settings = settings_get();
if (!settings->howto_tooltip_shown)
gGui->activeTooltip = howto_tooltip;
settings->howto_tooltip_shown = true;
}
static void
@ -245,10 +333,14 @@ static void
toggleInGameMenu(void *unused)
{
UNUSED(unused);
if (gGameState == PLAYING || gGameState == GAME_OVER)
if (gGameState == PLAYING ||
gGameState == GAME_OVER ||
gGameState == COMPLETED)
gGameState = IN_GAME_MENU;
else if (is_player_dead())
gGameState = GAME_OVER;
else if (cLevel >= 20)
gGameState = COMPLETED;
else
gGameState = PLAYING;
}
@ -257,6 +349,7 @@ static void
goToMainMenu(void *unused)
{
UNUSED(unused);
gui_clear_message_log();
gGameState = MENU;
menu_destroy(inGameMenu);
inGameMenu = NULL;
@ -299,16 +392,25 @@ createMenu(Menu **menu, struct MENU_ITEM menu_items[], unsigned int size)
}
}
static void
showHowToTooltip(void *unused)
{
UNUSED(unused);
toggleInGameMenu(NULL);
gGui->activeTooltip = howto_tooltip;
}
static void
initInGameMenu(void)
{
struct MENU_ITEM menu_items[] = {
{ "RESUME", toggleInGameMenu },
{ "HOW TO PLAY", showHowToTooltip },
{ "MAIN MENU", goToMainMenu },
{ "QUIT", exitGame },
};
createMenu(&inGameMenu, menu_items, 3);
createMenu(&inGameMenu, menu_items, 4);
}
static void
@ -388,8 +490,10 @@ resetGame(void)
screen_destroy(scoreScreen);
scoreScreen = NULL;
if (!inGameMenu)
initInGameMenu();
if (inGameMenu)
menu_destroy(inGameMenu);
inGameMenu = NULL;
initInGameMenu();
if (gMap)
map_destroy(gMap);
@ -420,6 +524,10 @@ init(void)
hiscore_init();
initMainMenu();
howto_tooltip = tooltip_create(how_to_play_tooltip, gCamera);
new_skill_tooltip = tooltip_create(skills_tooltip, gCamera);
new_artifact_tooltip = tooltip_create(artifacts_tooltip, gCamera);
gCamera->pos = (Position) { 0, 0 };
gGameState = MENU;
@ -432,9 +540,10 @@ handle_main_input(void)
{
if (gGameState == PLAYING
|| gGameState == IN_GAME_MENU
|| gGameState == GAME_OVER)
|| gGameState == GAME_OVER
|| gGameState == COMPLETED)
{
if (input_key_is_pressed(&input, KEY_ESC))
if (!gGui->activeTooltip && input_key_is_pressed(&input, KEY_ESC))
toggleInGameMenu(NULL);
}
@ -444,6 +553,8 @@ handle_main_input(void)
gGameState = MENU;
else if (gGameState == MENU && input_key_is_pressed(&input, KEY_ESC))
gGameState = QUIT;
else if (gGui->activeTooltip && input_key_is_pressed(&input, KEY_ESC))
gGui->activeTooltip = NULL;
if (input_modkey_is_pressed(&input, KEY_CTRL_M)) {
if (mixer_toggle_music(&gGameState))
@ -458,6 +569,15 @@ handle_main_input(void)
else
gui_log("Sound disabled");
}
if (input_modkey_is_pressed(&input, KEY_CTRL_D)) {
Settings *s = settings_get();
s->tooltips_enabled = !s->tooltips_enabled;
if (s->tooltips_enabled)
gui_log("Tooltips enabled");
else
gui_log("Tooltips disabled");
}
}
static bool
@ -491,15 +611,33 @@ handle_events(void)
static bool
is_player_dead(void)
{
#ifdef DEBUG
gPlayer->stats.hp = gPlayer->stats.hp > 0 ? gPlayer->stats.hp : 1;
#endif // DEBUG
if (gPlayer->stats.hp <= 0) {
return true;
}
return false;
}
static void
end_game_details(void)
{
gui_log("You earned %.2f gold", gPlayer->gold);
gui_event_message("You earned %.2f gold", gPlayer->gold);
if (hiscore_get_top_gold() < gPlayer->gold) {
gui_event_message("NEW HIGHSCORE");
gui_log("NEW HIGHSCORE");
}
}
static void
check_next_level(void)
{
if (cLevel >= 20) {
return;
}
Room *room = gMap->rooms[gMap->currentRoom.x][gMap->currentRoom.y];
Position pos = position_to_matrix_coords(&gPlayer->sprite->pos);
@ -511,11 +649,18 @@ check_next_level(void)
if (tile->levelExit) {
mixer_play_effect(NEXT_LEVEL);
++cLevel;
if (cLevel % 5 == 0)
if (cLevel > 19) {
mixer_play_music(BOSS_MUSIC0);
else
} else if (cLevel % 5 == 0) {
gui_log("You sense something powerful in the vicinity");
mixer_play_music(BOSS_MUSIC0);
} else {
mixer_play_music(GAME_MUSIC0 + get_random(2));
resetGame();
}
if (cLevel < 20) {
resetGame();
}
}
}
@ -526,7 +671,9 @@ populateUpdateData(UpdateData *data, float deltatime)
data->map = gMap;
data->matrix = gRoomMatrix;
data->input = &input;
data->gui = gGui;
data->deltatime = deltatime;
data->cam = gCamera;
}
static void
@ -534,17 +681,28 @@ run_game_update(void)
{
static UpdateData updateData;
static unsigned int playerLevel = 1;
static bool artifactTooltipShown = false;
if (gGameState == IN_GAME_MENU)
menu_update(inGameMenu, &input);
populateUpdateData(&updateData, deltaTime);
bool skillActivated = false;
if (playerLevel != gPlayer->stats.lvl) {
playerLevel = gPlayer->stats.lvl;
skillbar_check_skill_activation(gSkillBar, gPlayer);
skillActivated = skillbar_check_skill_activation(gSkillBar,
gPlayer);
}
Settings *settings = settings_get();
if (skillActivated && settings->tooltips_enabled && playerLevel < 5) {
gGui->activeTooltip = new_skill_tooltip;
}
if (!artifactTooltipShown && gPlayer->equipment.hasArtifacts && settings->tooltips_enabled) {
artifactTooltipShown = true;
gGui->activeTooltip = new_artifact_tooltip;
}
map_clear_expired_entities(gMap, gPlayer);
if (gGameState == PLAYING && currentTurn == PLAYER)
player_update(&updateData);
@ -558,19 +716,85 @@ run_game_update(void)
map_set_current_room(gMap, &gPlayer->sprite->pos);
map_update(&updateData);
bool turnSwitch = false;
if (currentTurn == PLAYER) {
if (player_turn_over(gPlayer)) {
currentTurn = MONSTER;
player_reset_steps(gPlayer);
map_on_new_turn(gMap);
repopulate_roommatrix();
turnSwitch = true;
}
} else if (currentTurn == MONSTER) {
if (map_move_monsters(gMap, gRoomMatrix)) {
currentTurn = PLAYER;
repopulate_roommatrix();
turnSwitch = true;
}
}
map_clear_expired_entities(gMap, gPlayer);
if (turnSwitch)
repopulate_roommatrix();
}
static void
render_gui(void)
{
SDL_RenderSetViewport(gRenderer, &statsGuiViewport);
gui_render_panel(gGui, gCamera);
SDL_RenderSetViewport(gRenderer, &minimapViewport);
gui_render_minimap(gGui, gMap, gCamera);
SDL_RenderSetViewport(gRenderer, &skillBarViewport);
skillbar_render(gSkillBar, gPlayer, gCamera);
SDL_RenderSetViewport(gRenderer, &bottomGuiViewport);
gui_render_log(gGui, gCamera);
SDL_RenderSetViewport(gRenderer, NULL);
}
static void
render_game_completed(void)
{
SDL_RenderSetViewport(gRenderer, &gameViewport);
if (!is_player_dead()) {
player_render(gPlayer, gCamera);
player_render_toplayer(gPlayer, gCamera);
}
actiontextbuilder_render(gCamera);
gui_render_event_message(gGui, gCamera);
if (gGameState == IN_GAME_MENU) {
SDL_Rect dimmer = { 0, 0, SCREEN_WIDTH, SCREEN_HEIGHT };
SDL_SetRenderDrawColor(gRenderer, 0, 0, 0, 150);
SDL_RenderFillRect(gRenderer, &dimmer);
menu_render(inGameMenu, gCamera);
}
#ifdef DEBUG
sprite_render(fpsSprite, gCamera);
pointer_render(gPointer, gCamera);
#endif // DEBUG
}
static void
render_game(void)
{
SDL_RenderSetViewport(gRenderer, &gameViewport);
map_render(gMap, gCamera);
particle_engine_render_game(gCamera);
map_render_mid_layer(gMap, gCamera);
if (!is_player_dead()) {
player_render(gPlayer, gCamera);
player_render_toplayer(gPlayer, gCamera);
}
map_render_top_layer(gMap, gRoomMatrix, gCamera);
if (gPlayer->class == MAGE || gPlayer->class == PALADIN)
roommatrix_render_mouse_square(gRoomMatrix, gCamera);
roommatrix_render_lightmap(gRoomMatrix, gCamera);
actiontextbuilder_render(gCamera);
gui_render_event_message(gGui, gCamera);
}
static void
@ -579,36 +803,11 @@ run_game_render(void)
SDL_SetRenderDrawColor(gRenderer, 0, 0, 0, 0);
SDL_RenderClear(gRenderer);
SDL_RenderSetViewport(gRenderer, &gameViewport);
map_render(gMap, gCamera);
particle_engine_render_game(gCamera);
map_render_mid_layer(gMap, gCamera);
render_game();
render_gui();
if (!is_player_dead()) {
player_render(gPlayer, gCamera);
player_render_toplayer(gPlayer, gCamera);
}
map_render_top_layer(gMap, gCamera);
if (gPlayer->class == MAGE || gPlayer->class == PALADIN)
roommatrix_render_mouse_square(gRoomMatrix, gCamera);
roommatrix_render_lightmap(gRoomMatrix, gCamera);
actiontextbuilder_render(gCamera);
gui_render_event_message(gGui, gCamera);
SDL_RenderSetViewport(gRenderer, &rightGuiViewport);
gui_render_panel(gGui, gCamera);
SDL_RenderSetViewport(gRenderer, &skillBarViewport);
skillbar_render(gSkillBar, gPlayer, gCamera);
SDL_RenderSetViewport(gRenderer, &bottomGuiViewport);
gui_render_log(gGui, gCamera);
SDL_RenderSetViewport(gRenderer, NULL);
particle_engine_render_global(gCamera);
gui_render_tooltip(gGui, gCamera);
if (gGameState == IN_GAME_MENU) {
SDL_Rect dimmer = { 0, 0, SCREEN_WIDTH, SCREEN_HEIGHT };
@ -616,7 +815,10 @@ run_game_render(void)
SDL_RenderFillRect(gRenderer, &dimmer);
menu_render(inGameMenu, gCamera);
}
#ifdef DEBUG
sprite_render(fpsSprite, gCamera);
pointer_render(gPointer, gCamera);
#endif // DEBUG
SDL_RenderPresent(gRenderer);
}
@ -626,19 +828,21 @@ run_game(void)
{
run_game_update();
run_game_render();
if (cLevel >= 20) {
SDL_SetRenderDrawColor(gRenderer, 0, 0, 0, 255);
SDL_RenderClear(gRenderer);
render_game_completed();
render_gui();
SDL_RenderPresent(gRenderer);
} else {
run_game_render();
}
if (gGameState == PLAYING && is_player_dead()) {
camera_shake(VECTOR2D_RIGHT, 800);
gui_log("The dungeon consumed you");
gui_log("You earned %.2f gold", gPlayer->gold);
gui_event_message("You died!");
gui_event_message("You earned %.2f gold", gPlayer->gold);
if (hiscore_get_top_gold() < gPlayer->gold) {
gui_event_message("NEW HIGHSCORE");
gui_log("NEW HIGHSCORE");
}
gui_event_message("Press ESC to open menu");
end_game_details();
mixer_play_effect(SPLAT);
gGameState = GAME_OVER;
createInGameGameOverMenu();
@ -647,6 +851,15 @@ run_game(void)
} else {
check_next_level();
}
if (gGameState == PLAYING && cLevel >= 20) {
gGameState = COMPLETED;
createInGameGameOverMenu();
gui_event_message("Your break is over!");
gui_log("Your break is over!");
gui_event_message("Well done!");
end_game_details();
}
}
static void
@ -672,7 +885,7 @@ run_menu(void)
SDL_RenderSetViewport(gRenderer, &menuViewport);
map_render(gMap, gCamera);
map_render_mid_layer(gMap, gCamera);
map_render_top_layer(gMap, gCamera);
map_render_top_layer(gMap, gRoomMatrix, gCamera);
roommatrix_render_lightmap(gRoomMatrix, gCamera);
SDL_RenderSetViewport(gRenderer, NULL);
@ -684,19 +897,31 @@ run_menu(void)
else if (gGameState == SCORE_SCREEN)
screen_render(scoreScreen, gCamera);
#ifdef DEBUG
sprite_render(fpsSprite, gCamera);
pointer_render(gPointer, gCamera);
#endif // DEBUG
SDL_RenderPresent(gRenderer);
}
static
void run(void)
static void
run(void)
{
static int oldTime = 0;
static int currentTime = 0;
bool quit = false;
Timer* fpsTimer = timer_create();
#ifdef DEBUG
Uint32 frame = 0;
Timer *fpsTime = timer_create();
Timer *updateTimer = timer_create();
timer_start(fpsTime);
timer_start(updateTimer);
#endif // DEBUG
Timer *fpsTimer = timer_create();
while (!quit)
{
@ -704,12 +929,15 @@ void run(void)
quit = handle_events();
handle_main_input();
#ifdef DEBUG
pointer_handle_input(gPointer, &input);
#endif // DEBUG
switch (gGameState) {
case PLAYING:
case IN_GAME_MENU:
case GAME_OVER:
case COMPLETED:
run_game();
break;
case MENU:
@ -736,9 +964,23 @@ void run(void)
currentTime = SDL_GetTicks();
deltaTime = (float) ((currentTime - oldTime) / 1000.0);
}
#ifdef DEBUG
frame++;
if (timer_get_ticks(updateTimer) > 1000) {
char buffer[20];
m_sprintf(buffer, 20, "FPS: %u", frame / (timer_get_ticks(fpsTime) / 1000));
texture_load_from_text(fpsSprite->textures[0], buffer, C_RED, C_WHITE, gRenderer);
fpsSprite->dim = fpsSprite->textures[0]->dim;
timer_start(updateTimer);
}
#endif // DEBUG
}
timer_destroy(fpsTimer);
#ifdef DEBUG
timer_destroy(fpsTime);
timer_destroy(updateTimer);
#endif // DEBUG
}
static
@ -758,11 +1000,16 @@ void close(void)
if (inGameMenu)
menu_destroy(inGameMenu);
sprite_destroy(howto_tooltip);
sprite_destroy(new_skill_tooltip);
camera_destroy(gCamera);
roommatrix_destroy(gRoomMatrix);
gui_destroy(gGui);
skillbar_destroy(gSkillBar);
#ifdef DEBUG
pointer_destroy(gPointer);
sprite_destroy(fpsSprite);
#endif // DEBUG
actiontextbuilder_close();
item_builder_close();
particle_engine_close();

View File

@ -27,8 +27,8 @@
#include "update_data.h"
#include "trap.h"
static
Room* create_room(void)
static Room*
create_room(void)
{
int i, j;
Room *room;
@ -40,6 +40,7 @@ Room* create_room(void)
room->tiles[i][j] = NULL;
room->decorations[i][j] = NULL;
room->traps[i][j] = NULL;
room->visited = false;
}
}
return room;
@ -205,14 +206,18 @@ map_move_monsters(Map *map, RoomMatrix *rm)
if (monster->state.current == PASSIVE
&& position_proximity(1, &rm->playerRoomPos, &pos))
continue;
if (monster->steps >= monster->stats.speed)
continue;
allDone = allDone && monster_move(monster, rm, map);
}
if (allDone)
if (allDone) {
timer_stop(map->monsterMoveTimer);
else
linkedlist_each(&map->monsters, (void (*)(void*)) monster_reset_steps);
} else {
timer_start(map->monsterMoveTimer);
}
return allDone;
}
@ -353,13 +358,13 @@ map_render_mid_layer(Map *map, Camera *cam)
}
void
map_render_top_layer(Map *map, Camera *cam)
map_render_top_layer(Map *map, RoomMatrix *rm, Camera *cam)
{
LinkedList *monsterItem = map->monsters;
while (monsterItem != NULL) {
Monster *monster = monsterItem->data;
monsterItem = monsterItem->next;
monster_render_top_layer(monster, cam);
monster_render_top_layer(monster, rm, cam);
}
}
@ -388,6 +393,8 @@ void map_set_current_room(Map *map, Position *pos)
map->currentRoom.x = MAP_H_ROOM_COUNT - 1;
if (map->currentRoom.y >= MAP_V_ROOM_COUNT)
map->currentRoom.y = MAP_V_ROOM_COUNT - 1;
map->rooms[map->currentRoom.x][map->currentRoom.y]->visited = true;
}
static

View File

@ -51,6 +51,7 @@ typedef struct Room_t {
MapTile* decorations[MAP_ROOM_WIDTH][MAP_ROOM_HEIGHT];
Trap* traps[MAP_ROOM_WIDTH][MAP_ROOM_HEIGHT];
RoomModifierData modifier;
bool visited;
} Room;
typedef struct Map_t {
@ -106,7 +107,7 @@ void
map_render_mid_layer(Map*, Camera*);
void
map_render_top_layer(Map*, Camera*);
map_render_top_layer(Map*, RoomMatrix*, Camera*);
void
map_set_current_room(Map*, Position*);

View File

@ -106,6 +106,8 @@ monster_behaviour_check_post_hit(Monster *m)
monster_state_change(m, SCARED);
break;
case GUERILLA:
case ASSASSIN:
case SORCERER:
case FIRE_DEMON:
break;
default:
@ -114,14 +116,61 @@ monster_behaviour_check_post_hit(Monster *m)
}
}
static void
damage_surroundings(Monster *m, RoomMatrix *rm)
{
Position roomPos = position_to_matrix_coords(&m->sprite->pos);
for (Sint32 i = -1; i <= 1; ++i) {
for (Sint32 j = -1; j <= 1; ++j) {
if (i == 0 && j == 0)
continue;
RoomSpace *r = &rm->spaces[roomPos.x + i][roomPos.y + j];
if (r->monster) {
int dmg = stats_fight(&m->stats, &r->monster->stats);
monster_hit(r->monster, dmg);
gui_log("%s takes %d damage from the explosion", r->monster->label, dmg);
} else if (r->player) {
int dmg = stats_fight(&m->stats, &r->player->stats);
player_hit(r->player, dmg);
gui_log("You take %d damage from the explosion", dmg);
}
}
}
}
static void
sorcerer_blast(Monster *m, RoomMatrix *rm)
{
gui_log("%s creates a magical explosion", m->label);
particle_engine_eldritch_explosion(m->sprite->pos, DIM(TILE_DIMENSION, TILE_DIMENSION));
damage_surroundings(m, rm);
}
static void
assassin_cloak_effect(Monster *m, bool cloak)
{
if (cloak)
gui_log("%s dissappears from sight", m->label);
else
gui_log("%s reappears, filled with rage", m->label);
particle_engine_fire_explosion(m->sprite->pos, DIM(TILE_DIMENSION, TILE_DIMENSION));
}
static void
monster_behaviour_check_post_attack(Monster *m)
{
switch (m->behaviour) {
case GUERILLA:
case SORCERER:
case FIRE_DEMON:
monster_state_change(m, SCARED);
break;
case ASSASSIN:
assassin_cloak_effect(m, true);
monster_state_change(m, SCARED);
break;
default:
break;
}
@ -154,12 +203,20 @@ monster_behaviour_check(Monster *m, RoomMatrix *rm)
{
switch (m->behaviour) {
case GUERILLA:
case SORCERER:
case FIRE_DEMON:
if (m->state.stepsSinceChange > 8
if (m->state.stepsSinceChange > 5
&& m->state.current == SCARED) {
monster_state_change(m, AGRESSIVE);
}
break;
case ASSASSIN:
if (m->state.stepsSinceChange > 5
&& m->state.current == SCARED) {
assassin_cloak_effect(m, false);
monster_state_change(m, AGRESSIVE);
}
break;
case SENTINEL:
handle_sentinel_behaviour(m, rm);
break;
@ -395,10 +452,18 @@ monster_move(Monster *m, RoomMatrix *rm, Map *map)
}
monster_behaviour_check(m, rm);
Position origPos = m->sprite->pos;
Position originalMPos =
position_to_matrix_coords(&m->sprite->pos);
if (m->state.current == AGRESSIVE && m->behaviour == SORCERER) {
if (position_proximity(1, &originalMPos, &rm->playerRoomPos)) {
sorcerer_blast(m, rm);
monster_behaviour_check_post_attack(m);
return true;
}
}
rm->spaces[originalMPos.x][originalMPos.y].occupied = false;
rm->spaces[originalMPos.x][originalMPos.y].monster = NULL;
@ -436,18 +501,28 @@ monster_move(Monster *m, RoomMatrix *rm, Map *map)
RoomSpace *space = &rm->spaces[newPos.x][newPos.y];
if (space->light < 100 && withinHearingDist) {
actiontextbuilder_create_text("!", C_WHITE, &m->sprite->pos);
Position alertPos = m->sprite->pos;
alertPos.x += TILE_DIMENSION >> 1;
alertPos.y += TILE_DIMENSION >> 1;
actiontextbuilder_create_text("!", C_WHITE, &alertPos);
}
}
}
if (!position_equals(&origPos, &m->sprite->pos)
&& (rm->modifier->type == RMOD_TYPE_FIRE || m->behaviour == FIRE_DEMON)) {
Object *o = object_create_fire();
o->sprite->pos = origPos;
o->damage *= m->stats.lvl;
linkedlist_push(&map->objects, o);
if (!position_equals(&origPos, &m->sprite->pos)) {
if (rm->modifier->type == RMOD_TYPE_FIRE || m->behaviour == FIRE_DEMON) {
Object *o = object_create_fire();
o->sprite->pos = origPos;
o->damage *= m->stats.lvl;
linkedlist_push(&map->objects, o);
}
if (m->behaviour == SORCERER) {
Object *o = object_create_green_gas();
o->sprite->pos = origPos;
o->damage *= m->stats.lvl;
linkedlist_push(&map->objects, o);
}
}
m->steps++;
@ -455,13 +530,18 @@ monster_move(Monster *m, RoomMatrix *rm, Map *map)
if (m->stateIndicator.displayCount > 0)
m->stateIndicator.displayCount -= 1;
m->state.stepsSinceChange += 1;
m->steps = 0;
return true;
}
return false;
}
void
monster_reset_steps(Monster *m)
{
m->steps = 0;
}
void
monster_update(Monster *m, UpdateData *data)
{
@ -542,7 +622,7 @@ monster_drop_loot(Monster *monster, Map *map, Player *player)
linkedlist_append(&map->items, treasure);
}
if (monster->stats.lvl > 2 && get_random(19) == 0) {
if (monster->stats.lvl > 2 && get_random(29) == 0) {
Artifact *a = artifact_create_random(player, 1);
a->sprite->pos = monster->sprite->pos;
linkedlist_append(&map->artifacts, a);
@ -593,17 +673,31 @@ monster_render(Monster *m, Camera *cam)
if (m->stats.hp <= 0)
return;
if (m->behaviour == ASSASSIN && m->state.current != AGRESSIVE)
return;
sprite_render(m->sprite, cam);
}
void
monster_render_top_layer(Monster *m, Camera *cam)
monster_render_top_layer(Monster *m, RoomMatrix *rm, Camera *cam)
{
if (m->stats.hp <= 0)
return;
if (m->behaviour == ASSASSIN && m->state.current != AGRESSIVE)
return;
Position mPos = position_to_matrix_coords(&m->sprite->pos);
mPos.y -= 1;
if (rm->spaces[mPos.x][mPos.y].player) {
sprite_set_alpha(m->stateIndicator.sprite, 110);
}
if (m->stateIndicator.displayCount != 0)
sprite_render(m->stateIndicator.sprite, cam);
if (rm->spaces[mPos.x][mPos.y].player) {
sprite_set_alpha(m->stateIndicator.sprite, 255);
}
}
void
@ -613,6 +707,8 @@ monster_set_behaviour(Monster *m, MonsterBehaviour behaviour)
switch (behaviour) {
case HOSTILE:
case GUERILLA:
case ASSASSIN:
case SORCERER:
case COWARD:
case FIRE_DEMON:
m->state.current = AGRESSIVE;

View File

@ -35,7 +35,9 @@ typedef enum {
GUERILLA,
COWARD,
SENTINEL,
FIRE_DEMON
FIRE_DEMON,
SORCERER,
ASSASSIN
} MonsterBehaviour;
typedef enum {
@ -85,7 +87,7 @@ void
monster_render(Monster*, Camera*);
void
monster_render_top_layer(Monster*, Camera*);
monster_render_top_layer(Monster*, RoomMatrix*, Camera*);
void
monster_hit(Monster*, unsigned int dmg);
@ -108,6 +110,9 @@ monster_set_state(Monster *m, StateType state, Uint8 forceCount);
void
monster_push(Monster *, RoomMatrix*, Vector2d dir);
void
monster_reset_steps(Monster *m);
void
monster_destroy(Monster*);

View File

@ -49,6 +49,21 @@ object_create_fire()
return o;
}
Object *
object_create_green_gas()
{
Object *o = object_create();
Texture *t0 = texturecache_add("Objects/Effect0.png");
Texture *t1 = texturecache_add("Objects/Effect1.png");
sprite_set_texture(o->sprite, t0, 0);
sprite_set_texture(o->sprite, t1, 1);
o->sprite->dim = GAME_DIMENSION;
o->sprite->clip = CLIP16(32, 24*16);
o->damage = 3;
o->timeout = 3;
return o;
}
void
object_render(Object *o, Camera *cam)
{

View File

@ -38,6 +38,9 @@ object_create(void);
Object *
object_create_fire(void);
Object *
object_create_green_gas(void);
void
object_render(Object*, Camera*);

View File

@ -156,10 +156,10 @@ create_explosion(Position pos, Dimension dim, unsigned int c_count, ...)
x = get_random(dim.width) + pos.x;
y = get_random(dim.height) + pos.y;
xv = get_random(600) - 300;
yv = get_random(600) - 300;
xv = get_random(500) - 300;
yv = get_random(500) - 300;
lt = get_random(10);
lt = get_random(20);
p = create_rect_particle();
p->particle.rect.pos = (Position) { x, y };
@ -167,6 +167,7 @@ create_explosion(Position pos, Dimension dim, unsigned int c_count, ...)
p->velocity = (Vector2d) { (float) xv, (float) yv };
p->movetime = lt;
p->lifetime = lt;
p->blend_mode = SDL_BLENDMODE_BLEND;
p->color = colors[get_random((unsigned int) c_count-1)];
linkedlist_append(&engine->game_particles, p);
}
@ -318,7 +319,7 @@ particle_engine_heat()
h = get_random(2) + 2;
yvel = get_random(50) - 200;
xvel = get_random(100) * -get_random(1);
xvel = get_random(100) * -((int) get_random(1));
lt = get_random(500);

View File

@ -428,7 +428,7 @@ build_sword_animation(Player *p, SDL_Renderer *renderer)
}
Player*
player_create(class_t class, SDL_Renderer *renderer)
player_create(class_t class, Camera *cam)
{
Player *player = malloc(sizeof(Player));
player->sprite = sprite_create();
@ -450,8 +450,9 @@ player_create(class_t class, SDL_Renderer *renderer)
player->projectiles = linkedlist_create();
player->animationTimer = timer_create();
player->swordAnimation = animation_create(5);
player->equipment.hasArtifacts = false;
build_sword_animation(player, renderer);
build_sword_animation(player, cam->renderer);
memset(&player->skills,
0, PLAYER_SKILL_COUNT * sizeof(Skill*));
@ -480,16 +481,16 @@ player_create(class_t class, SDL_Renderer *renderer)
case WARRIOR:
m_strcpy(asset, 100, "Commissions/Warrior.png");
player->stats = (Stats) WARRIOR_STATS;
player->skills[0] = skill_create(FLURRY);
player->skills[1] = skill_create(BASH);
player->skills[2] = skill_create(CHARGE);
player->skills[3] = skill_create(DAGGER_THROW);
player->skills[0] = skill_create(FLURRY, cam);
player->skills[1] = skill_create(BASH, cam);
player->skills[2] = skill_create(CHARGE, cam);
player->skills[3] = skill_create(DAGGER_THROW, cam);
break;
}
player->skills[4] = skill_create(SIP_HEALTH);
player->skills[4] = skill_create(SIP_HEALTH, cam);
sprite_load_texture(player->sprite, asset, 0, renderer);
sprite_load_texture(player->sprite, asset, 0, cam->renderer);
player->sprite->pos = (Position) { TILE_DIMENSION, TILE_DIMENSION };
player->sprite->dim = GAME_DIMENSION;
player->sprite->clip = (SDL_Rect) { 0, 0, 16, 16 };
@ -530,7 +531,7 @@ player_hit(Player *p, unsigned int dmg)
Position pos = p->sprite->pos;
pos.x += 8;
pos.y += 8;
particle_engine_bloodspray(pos, (Dimension) { 8, 8 }, dmg);
particle_engine_bloodspray(pos, DIM(8, 8), dmg);
mixer_play_effect(PLAYER_HIT0 + get_random(2));
char msg[5];
m_sprintf(msg, 5, "-%d", dmg);
@ -624,6 +625,7 @@ player_reset(Player *player)
{
for (size_t i = 0; i < LAST_ARTIFACT_EFFECT; ++i)
player->equipment.artifacts[i].level = 0;
player->equipment.hasArtifacts = false;
while (player->projectiles)
projectile_destroy(linkedlist_pop(&player->projectiles));
@ -677,4 +679,5 @@ player_add_artifact(Player *p, Artifact *a)
gui_log("You pick an ancient %s", ad->name);
gui_log("%s (%u)", ad->desc, ad->level);
p->equipment.hasArtifacts = true;
}

View File

@ -59,6 +59,7 @@ typedef struct ArtifactData {
typedef struct PlayerEquipment {
ArtifactData artifacts[LAST_ARTIFACT_EFFECT];
bool hasArtifacts;
} PlayerEquipment;
typedef struct Player {
@ -79,7 +80,7 @@ typedef struct Player {
} Player;
Player*
player_create(class_t, SDL_Renderer*);
player_create(class_t, Camera*);
ExperienceData
player_get_xp_data(Player*);

View File

@ -21,6 +21,8 @@
#include <stdbool.h>
#define POS(x, y) (Position) { x, y }
typedef struct {
int x;
int y;

View File

@ -295,6 +295,8 @@ void roommatrix_destroy(RoomMatrix *m)
linkedlist_pop(&space->items);
while (space->artifacts)
linkedlist_pop(&space->artifacts);
while (space->objects)
linkedlist_pop(&space->objects);
}
}

View File

@ -27,8 +27,10 @@
static sqlite3 *db = NULL;
static Settings settings;
static const char *KEY_MUSIC_ENABLED = "music_enabled";
static const char *KEY_SOUND_ENABLED = "sound_enabled";
static const char *KEY_MUSIC_ENABLED = "music_enabled";
static const char *KEY_SOUND_ENABLED = "sound_enabled";
static const char *KEY_TOOLTIPS_ENABLED = "tooltips_enabled";
static const char *KEY_HOW_TO_PLAY_SHOWN = "how_to_play_shown";
static
DbQuery MIGRATE_COMMANDS[] = {
@ -49,6 +51,8 @@ set_default_settings(void)
{
settings.music_enabled = true;
settings.sound_enabled = true;
settings.tooltips_enabled = true;
settings.howto_tooltip_shown = false;
}
static void
@ -82,6 +86,14 @@ load_settings_cb(void *unused, int count, char **values, char **colNames)
settings.music_enabled = (bool)atoi(values[i + 1]);
i += 2;
}
else if (!strcmp(KEY_HOW_TO_PLAY_SHOWN, values[i])) {
settings.howto_tooltip_shown = (bool)atoi(values[i + 1]);
i += 2;
}
else if (!strcmp(KEY_TOOLTIPS_ENABLED, values[i])) {
settings.tooltips_enabled = (bool)atoi(values[i + 1]);
i += 2;
}
}
return 0;
}
@ -127,6 +139,8 @@ settings_save(void)
{
save_setting_int(KEY_SOUND_ENABLED, settings.sound_enabled);
save_setting_int(KEY_MUSIC_ENABLED, settings.music_enabled);
save_setting_int(KEY_TOOLTIPS_ENABLED, settings.tooltips_enabled);
save_setting_int(KEY_HOW_TO_PLAY_SHOWN, settings.howto_tooltip_shown);
}
Settings *

View File

@ -24,6 +24,8 @@
typedef struct Settings {
bool music_enabled;
bool sound_enabled;
bool tooltips_enabled;
bool howto_tooltip_shown;
} Settings;
void

View File

@ -35,6 +35,106 @@
#include "animation.h"
#include "artifact.h"
#include "trap.h"
#include "tooltip.h"
static char *flurry_tooltip[] = {
"FLURRY",
"",
" Hits an adjecant enemy with a flurry of three strikes.",
" Each strike has the same odds of hitting as a regular attack",
"",
"COOLDOWN:",
" 5 turns",
"",
"USAGE:",
" activate the skill (press 1)",
" followed by a direction (left, right, up or down)",
"",
"",
"Press ESC to close",
NULL
};
static char *bash_tooltip[] = {
"BASH",
"",
" Bashes an adjecant enemy with your shield",
" On a successful hit the target will be stunned for 2 turns",
"",
"COOLDOWN:",
" 2 turns",
"",
"USAGE:",
" activate the skill (press 2)",
" followed by a direction (left, right, up or down)",
"",
"",
"Press ESC to close",
NULL
};
static char *charge_tooltip[] = {
"CHARGE",
"",
" You charge in a chosen direction into the first obstructing",
" object. Charging into an enemy can deliver massive damage.",
"",
" Damage is affected by charge distance.",
" Longer distance, more damage.",
"",
"COOLDOWN:",
" 5 turns",
"",
"USAGE:",
" activate the skill (press 3)",
" followed by a direction (left, right, up or down)",
"",
"",
"Press ESC to close",
NULL
};
static char *dagger_tooltip[] = {
"THROW DAGGER",
"",
" You throw a dagger in the chosen direction.",
"",
" Damage is affected by throwing distance.",
" Longer distance, more damage.",
"",
" Dagger supply is not infinite, your current dagger",
" inventory is displayed in the panel to the right.",
"",
"COOLDOWN:",
" 0 turns",
"",
"USAGE:",
" activate the skill (press 4)",
" followed by a direction (left, right, up or down)",
"",
"",
"Press ESC to close",
NULL
};
static char *health_tooltip[] = {
"DRINK HEALTH",
"",
" You take a sip from your health vial",
"",
" The current amount of sips in your vials is",
" dsplayed in the panel to the right.",
"",
"COOLDOWN:",
" 0 turns",
"",
"USAGE:",
" Sip health (press 5)",
"",
"",
"Press ESC to close",
NULL
};
static Skill *
create_default(const char *s_label, Sprite *s)
@ -50,6 +150,7 @@ create_default(const char *s_label, Sprite *s)
skill->available = NULL;
skill->use = NULL;
skill->levelcap = 1;
skill->tooltip = NULL;
return skill;
}
@ -410,24 +511,29 @@ create_charge(void)
}
Skill*
skill_create(enum SkillType t)
skill_create(enum SkillType t, Camera *cam)
{
Skill *skill;
switch (t) {
case FLURRY:
skill = create_flurry();
skill->tooltip = tooltip_create(flurry_tooltip, cam);
break;
case SIP_HEALTH:
skill = create_sip_health();
skill->tooltip = tooltip_create(health_tooltip, cam);
break;
case CHARGE:
skill = create_charge();
skill->tooltip = tooltip_create(charge_tooltip, cam);
break;
case DAGGER_THROW:
skill = create_throw_dagger();
skill->tooltip = tooltip_create(dagger_tooltip, cam);
break;
case BASH:
skill = create_bash();
skill->tooltip = tooltip_create(bash_tooltip, cam);
break;
default:
fatal("Unknown SkillType %u", (unsigned int) t);
@ -444,5 +550,7 @@ void
skill_destroy(Skill *skill)
{
sprite_destroy(skill->icon);
if (skill->tooltip)
sprite_destroy(skill->tooltip);
free(skill);
}

View File

@ -52,10 +52,11 @@ typedef struct Skill_t {
bool active;
bool (*available)(Player*);
bool (*use)(struct Skill_t*, SkillData*);
Sprite *tooltip;
} Skill;
Skill*
skill_create(enum SkillType);
skill_create(enum SkillType, Camera *cam);
void
skill_destroy(Skill*);

View File

@ -26,6 +26,7 @@
#include "texturecache.h"
#include "particle_engine.h"
#include "update_data.h"
#include "gui.h"
static void
load_texture(SkillBar *bar, const char *path, SDL_Renderer *renderer)
@ -34,7 +35,7 @@ load_texture(SkillBar *bar, const char *path, SDL_Renderer *renderer)
t->dim.width = 16;
t->dim.height = 16;
for (unsigned int i = 0; i < 10; ++i) {
for (unsigned int i = 0; i < 5; ++i) {
char buffer[4];
Sprite *s = sprite_create();
s->pos = (Position) { i * 32 + 20, 20 };
@ -52,7 +53,7 @@ load_countdown_sprites(SkillBar *bar)
{
for (int i = 0; i < PLAYER_SKILL_COUNT; ++i) {
Sprite *s = sprite_create();
sprite_load_text_texture(s, "GUI/SDS_8x8.ttf", 0, 16, 0);
sprite_load_text_texture(s, "GUI/SDS_8x8.ttf", 0, 14, 1);
s->fixed = true;
s->pos = (Position) { 8 + (32 * i), 8 };
s->dim = (Dimension) { 16, 16 };
@ -60,45 +61,36 @@ load_countdown_sprites(SkillBar *bar)
}
}
SkillBar *
skillbar_create(SDL_Renderer *renderer)
{
SkillBar *bar = ec_malloc(sizeof(SkillBar));
bar->sprites = linkedlist_create();
bar->activationTimer = timer_create();
bar->skillSparkleTimer = timer_create();
bar->lastActivation = 0;
load_texture(bar, "GUI/GUI0.png", renderer);
load_countdown_sprites(bar);
return bar;
}
void
skillbar_check_skill_activation(SkillBar *bar, Player *player)
{
for (int i = 0; i < PLAYER_SKILL_COUNT; ++i) {
if (!player->skills[i])
continue;
if (player->skills[i]->levelcap != player->stats.lvl)
continue;
timer_start(bar->skillSparkleTimer);
}
}
static void
render_frame(Camera *cam)
static Sprite*
create_frame_sprite(Camera *cam)
{
static SDL_Rect c_top_left = { 1*16, 10*16, 16, 16 };
static SDL_Rect c_top_right = { 3*16, 10*16, 16, 16 };
static SDL_Rect c_center_top = { 2*16, 10*16, 16, 16 };
static SDL_Rect c_center_bottom = { 2*16, 12*16, 16, 16 };
static SDL_Rect c_bottom_left = { 1*16, 12*16, 16, 16 };
static SDL_Rect c_bottom_right = { 3*16, 12*16, 16, 16 };
Sprite *frame = sprite_create();
Texture *texture = texture_create();
texture->dim = (Dimension) { GAME_VIEW_WIDTH, 32 };
frame->textures[0] = texture;
frame->destroyTextures = true;
frame->pos = (Position) { 0, 0 };
frame->dim = (Dimension) { GAME_VIEW_WIDTH, 32 };
frame->fixed = true;
texture_create_blank(texture,
SDL_TEXTUREACCESS_TARGET,
cam->renderer);
SDL_SetRenderTarget(cam->renderer, texture->texture);
SDL_RenderClear(cam->renderer);
Texture *t = texturecache_get("GUI/GUI0.png");
SDL_Rect box = { 0, 0, 16, 16 };
for (unsigned int i = 0; i < MAP_ROOM_WIDTH; ++i) {
// Render skill squares
for (Uint32 i = 0; i < 5; ++i) {
box.x = i*32;
box.y = 0;
texture_render_clip(t, &box, &c_top_left, cam);
@ -111,6 +103,93 @@ render_frame(Camera *cam)
box.y = 16;
texture_render_clip(t, &box, &c_bottom_right, cam);
}
// Render inventory box
box.x = 5 * 32;
box.y = 0;
texture_render_clip(t, &box, &c_top_left, cam);
box.y = 16;
texture_render_clip(t, &box, &c_bottom_left, cam);
box.x = 5 * 32 + 16;
box.y = 0;
texture_render_clip(t, &box, &c_center_top, cam);
box.y = 16;
texture_render_clip(t, &box, &c_center_bottom, cam);
for (Uint32 i = 6; i < MAP_ROOM_WIDTH - 1; ++i) {
box.x = i*32;
box.y = 0;
texture_render_clip(t, &box, &c_center_top, cam);
box.y = 16;
texture_render_clip(t, &box, &c_center_bottom, cam);
box.x = i * 32 + 16;
box.y = 0;
texture_render_clip(t, &box, &c_center_top, cam);
box.y = 16;
texture_render_clip(t, &box, &c_center_bottom, cam);
}
box.x = (MAP_ROOM_WIDTH - 1) * 32;
box.y = 0;
texture_render_clip(t, &box, &c_center_top, cam);
box.y = 16;
texture_render_clip(t, &box, &c_center_bottom, cam);
box.x = (MAP_ROOM_WIDTH - 1) * 32 + 16;
box.y = 0;
texture_render_clip(t, &box, &c_top_right, cam);
box.y = 16;
texture_render_clip(t, &box, &c_bottom_right, cam);
SDL_SetRenderTarget(cam->renderer, NULL);
return frame;
}
SkillBar *
skillbar_create(Camera *cam)
{
SkillBar *bar = ec_malloc(sizeof(SkillBar));
bar->sprites = linkedlist_create();
bar->activationTimer = timer_create();
bar->skillSparkleTimer = timer_create();
bar->lastActivation = 0;
bar->frame = create_frame_sprite(cam);
bar->artifactDisplayOffset = 5 * 32 + 8;
load_texture(bar, "GUI/GUI0.png", cam->renderer);
load_countdown_sprites(bar);
for (Uint32 i = 0; i < LAST_ARTIFACT_EFFECT; ++i) {
bar->artifacts[i].aSprite = artifact_sprite_for(i);
bar->artifacts[i].aSprite->fixed = true;
bar->artifacts[i].lvl = 0;
Sprite *lvlSprite = sprite_create();
lvlSprite->fixed = true;
lvlSprite->dim = DIM(9, 9);
sprite_load_text_texture(lvlSprite, "GUI/SDS_8x8.ttf", 0, 9, 0);
bar->artifacts[i].lvlSprite = lvlSprite;
}
return bar;
}
bool
skillbar_check_skill_activation(SkillBar *bar, Player *player)
{
if (player->stats.lvl == 1)
return false;
for (int i = 0; i < PLAYER_SKILL_COUNT; ++i) {
if (!player->skills[i])
continue;
if (player->skills[i]->levelcap != player->stats.lvl)
continue;
timer_start(bar->skillSparkleTimer);
}
return timer_started(bar->skillSparkleTimer);
}
static void
@ -177,6 +256,18 @@ render_skills(Player *player, Camera *cam)
}
}
static void
render_artifacts(SkillBar *bar, Camera *cam)
{
UNUSED(bar);
for (size_t i = 0; i < LAST_ARTIFACT_EFFECT; ++i) {
if (bar->artifacts[i].lvl == 0)
continue;
sprite_render(bar->artifacts[i].aSprite, cam);
sprite_render(bar->artifacts[i].lvlSprite, cam);
}
}
static void
render_skill_unavailable(SkillBar *bar, Player *player, Camera *cam)
{
@ -242,8 +333,9 @@ render_skill_sparkles(SkillBar *bar, Player *player)
void
skillbar_render(SkillBar *bar, Player *player, Camera *cam)
{
render_frame(cam);
sprite_render(bar->frame, cam);
render_skills(player, cam);
render_artifacts(bar, cam);
render_sprites(bar, cam);
render_skill_unavailable(bar, player, cam);
render_activation_indicator(bar, cam);
@ -256,8 +348,17 @@ skillbar_update(SkillBar *bar, UpdateData *data)
{
Input *input = data->input;
unsigned int key = 0;
for (int i = 0; i < 10; ++i) {
for (int i = 0; i < 5; ++i) {
if (!data->player->skills[i])
continue;
if (input_modkey_is_pressed(input, KEY_SHIFT_NUM1 << i)) {
data->gui->activeTooltip = data->player->skills[i]->tooltip;
return;
}
}
Uint32 key = 0;
for (int i = 0; i < 5; ++i) {
if (!input_key_is_pressed(input, KEY_NUM0 << i))
continue;
key = i;
@ -268,6 +369,35 @@ skillbar_update(SkillBar *bar, UpdateData *data)
bar->lastActivation = key;
timer_start(bar->activationTimer);
}
for (size_t i = 0; i < LAST_ARTIFACT_EFFECT; ++i) {
if (data->player->equipment.artifacts[i].level == bar->artifacts[i].lvl)
continue;
Uint32 origLevel = bar->artifacts[i].lvl;
bar->artifacts[i].lvl = data->player->equipment.artifacts[i].level;
char lvl[4];
m_sprintf(lvl, 4, "%u", bar->artifacts[i].lvl);
texture_load_from_text(bar->artifacts[i].lvlSprite->textures[0],
lvl, C_PURPLE, C_WHITE, data->cam->renderer);
// Only update position if this is the first pickup
if (origLevel == 0) {
bar->artifacts[i].lvlSprite->pos.x = bar->artifactDisplayOffset + 12;
bar->artifacts[i].lvlSprite->pos.y = 16;
bar->artifacts[i].aSprite->pos.x = bar->artifactDisplayOffset;
bar->artifacts[i].aSprite->pos.y = 8;
bar->artifactDisplayOffset += 32;
}
}
}
void
skillbar_reset(SkillBar *bar)
{
bar->artifactDisplayOffset = 5 * 32 + 8;
}
void
@ -275,9 +405,14 @@ skillbar_destroy(SkillBar *bar)
{
while (bar->sprites)
sprite_destroy(linkedlist_pop(&bar->sprites));
for (unsigned int i = 0; i < PLAYER_SKILL_COUNT; ++i)
for (Uint32 i = 0; i < PLAYER_SKILL_COUNT; ++i)
if (bar->countdowns[i])
sprite_destroy(bar->countdowns[i]);
for (Uint32 i = 0; i < LAST_ARTIFACT_EFFECT; ++i) {
sprite_destroy(bar->artifacts[i].aSprite);
sprite_destroy(bar->artifacts[i].lvlSprite);
}
sprite_destroy(bar->frame);
timer_destroy(bar->activationTimer);
timer_destroy(bar->skillSparkleTimer);
free(bar);

View File

@ -28,18 +28,27 @@
struct UpdateData;
typedef struct SkillBar_t {
typedef struct ArtifactDisplay {
Sprite *aSprite;
Sprite *lvlSprite;
Uint32 lvl;
} ArtifactDisplay;
typedef struct SkillBar {
LinkedList *sprites;
ArtifactDisplay artifacts[LAST_ARTIFACT_EFFECT];
Uint32 artifactDisplayOffset;
Sprite *countdowns[PLAYER_SKILL_COUNT];
Sprite *frame;
Timer *activationTimer;
Timer *skillSparkleTimer;
unsigned int lastActivation;
Uint32 lastActivation;
} SkillBar;
SkillBar *
skillbar_create(SDL_Renderer*);
skillbar_create(Camera*);
void
bool
skillbar_check_skill_activation(SkillBar*, Player*);
void
@ -48,6 +57,9 @@ skillbar_render(SkillBar*, Player*, Camera*);
void
skillbar_update(SkillBar*, struct UpdateData*);
void
skillbar_reset(SkillBar*);
void
skillbar_destroy(SkillBar*);

View File

@ -94,6 +94,24 @@ sprite_set_texture(Sprite *s, Texture *t, int index)
s->textures[index] = t;
}
void
sprite_set_blend_mode(Sprite *s, SDL_BlendMode mode)
{
if (s->textures[0])
texture_set_blend_mode(s->textures[0], mode);
if (s->textures[1])
texture_set_blend_mode(s->textures[1], mode);
}
void
sprite_set_alpha(Sprite *s, Uint8 alpha)
{
if (s->textures[0])
texture_set_alpha(s->textures[0], alpha);
if (s->textures[1])
texture_set_alpha(s->textures[1], alpha);
}
void
sprite_render(Sprite *s, Camera *cam)
{

View File

@ -59,6 +59,12 @@ sprite_set_texture(Sprite *, Texture *, int index);
void
sprite_render(Sprite*, Camera*);
void
sprite_set_blend_mode(Sprite*, SDL_BlendMode);
void
sprite_set_alpha(Sprite*, Uint8);
void
sprite_destroy(Sprite *);

View File

@ -67,16 +67,17 @@ stats_fight(Stats *attacker, Stats *defender)
bool critical = false;
int atkRoll = get_attack_roll(attacker);
if (atkRoll - attacker->atk == 20)
critical = true;
int defRoll = get_defence_roll(defender);
if (atkRoll - attacker->atk == 20)
critical = get_attack_roll(attacker) > defRoll;
int dmgRoll = 0;
if (atkRoll >= defRoll) {
if (attacker->dmg > 0)
dmgRoll = get_random(attacker->dmg) + 1;
else
dmgRoll = get_random(attacker->dmg - 1) + 1;
else
dmgRoll = 1;
if (critical) {
dmgRoll = dmgRoll * 2;

View File

@ -223,6 +223,20 @@ texture_load_from_text_blended(Texture *t, const char * text, SDL_Color fg, SDL_
load_from_surface(t, surface, renderer);
}
void
texture_set_blend_mode(Texture *t, SDL_BlendMode mode)
{
assert(t->texture);
SDL_SetTextureBlendMode(t->texture, mode);
}
void
texture_set_alpha(Texture *t, Uint8 alpha)
{
assert(t->texture);
SDL_SetTextureAlphaMod(t->texture, alpha);
}
void
texture_render(Texture *texture, SDL_Rect *box, Camera *cam)
{

View File

@ -25,7 +25,7 @@
#include "position.h"
#include "camera.h"
typedef struct {
typedef struct Texture {
SDL_Texture *texture;
TTF_Font *font;
TTF_Font *outlineFont;
@ -76,6 +76,12 @@ texture_load_from_text_blended(Texture*,
SDL_Color,
SDL_Renderer*);
void
texture_set_blend_mode(Texture*, SDL_BlendMode);
void
texture_set_alpha(Texture*, Uint8);
void
texture_render(Texture*, SDL_Rect*, Camera*);

65
src/tooltip.c Normal file
View File

@ -0,0 +1,65 @@
/*
* BreakHack - A dungeone crawler RPG
* Copyright (C) 2018 Linus Probert <linus.probert@gmail.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <string.h>
#include "tooltip.h"
#include "gui_util.h"
#include "defines.h"
#include "gui.h"
Sprite *
tooltip_create(char **content, Camera *cam)
{
int rowCount = 0;
char **contentIndex = content;
while (*contentIndex) {
rowCount++;
contentIndex++;
}
Sprite *sprite = gui_util_create_tooltip_frame_sprite(BOTTOM_GUI_WIDTH/16 - 6,
(Uint32) ((rowCount * 10 + 48)/16),
cam);
sprite->pos.x = 48;
sprite->pos.y = 96;
Texture *texture = sprite->textures[0];
Texture *text = texture_create();
texture_load_font(text, "GUI/SDS_8x8.ttf", LOG_FONT_SIZE, 0);
SDL_SetRenderTarget(cam->renderer, texture->texture);
SDL_Rect renderBox = { 16, 16, 0, 0 };
while (*content) {
if (strlen(*content) > 0) {
texture_load_from_text(text,
*content,
C_WHITE,
C_WHITE,
cam->renderer);
renderBox.w = text->dim.width;
renderBox.h = text->dim.height;
texture_render(text, &renderBox, cam);
}
renderBox.y += 10;
content++;
}
SDL_SetRenderTarget(cam->renderer, NULL);
texture_destroy(text);
return sprite;
}

25
src/tooltip.h Normal file
View File

@ -0,0 +1,25 @@
/*
* BreakHack - A dungeone crawler RPG
* Copyright (C) 2018 Linus Probert <linus.probert@gmail.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include "camera.h"
#include "sprite.h"
Sprite *
tooltip_create(char **content, Camera*);

View File

@ -22,11 +22,15 @@
#include "player.h"
#include "map.h"
#include "roommatrix.h"
#include "gui.h"
#include "camera.h"
typedef struct UpdateData {
Player *player;
Map *map;
RoomMatrix *matrix;
Gui *gui;
Camera *cam;
Input *input;
float deltatime;
} UpdateData;

View File

@ -34,8 +34,8 @@
#define debug(...) do {} while(0)
#define info(...) do {} while(0)
#endif // DEBUG
#define error(...) log_print(stderr, "ERROR", __FNAME__, __LINE__, __func__, __VA_ARGS__)
#define error(...) log_print(stderr, "ERROR", __FNAME__, __LINE__, __func__, __VA_ARGS__)
#ifdef DEBUG
#define fatal(...) \
{ \
@ -44,7 +44,7 @@
getchar(); \
exit(-1); \
}
#else
#else // DEBUG
#define fatal(...) \
{ \
log_print(stderr, "FATAL", __FNAME__, __LINE__, __func__, __VA_ARGS__); \