diff --git a/CMakeLists.txt b/CMakeLists.txt index 0ca9622..750b7cc 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -176,6 +176,7 @@ add_executable(breakhack src/artifact src/screen src/hiscore + src/object ) # Sqlite has some warnings that I we don't need to see diff --git a/data/maproombuilder.lua b/data/maproombuilder.lua index 6a57259..ff23c84 100644 --- a/data/maproombuilder.lua +++ b/data/maproombuilder.lua @@ -431,10 +431,13 @@ local function build_normal_room(room) add_level_exit(room) end - if CURRENT_LEVEL > 3 and random(10) == 1 then + if CURRENT_LEVEL > 3 and random(8) == 1 then directions = { "LEFT", "RIGHT", "UP", "DOWN" } room.modifier.type = "WINDY" room.modifier.arg = directions[random(#directions)] + elseif CURRENT_LEVEL > 5 and random(8) == 1 then + room.modifier.type = "FIRE" + room.modifier.arg = "" end return room diff --git a/src/defines.h b/src/defines.h index 32bfad1..c190d64 100644 --- a/src/defines.h +++ b/src/defines.h @@ -19,6 +19,7 @@ #ifndef DEFINES_H_ #define DEFINES_H_ +#include #include "config.h" /* Room/Map dimensions */ @@ -76,6 +77,13 @@ #define min(a, b) (a < b ? a : b) #endif // _MSC_VER +typedef int8_t Sint8; +typedef uint8_t Uint8; +typedef int16_t Sint16; +typedef uint16_t Uint16; +typedef int32_t Sint32; +typedef uint32_t Uint32; + typedef enum Direction_t { UP, DOWN, LEFT, RIGHT } Direction; diff --git a/src/main.c b/src/main.c index 38b1146..46d94e8 100644 --- a/src/main.c +++ b/src/main.c @@ -523,16 +523,13 @@ run_game_update(void) if (gGameState == IN_GAME_MENU) menu_update(inGameMenu, &input); - map_clear_dead_monsters(gMap, gPlayer); - map_clear_collected_items(gMap); - map_clear_collected_artifacts(gMap); - populateUpdateData(&updateData, deltaTime); if (playerLevel != gPlayer->stats.lvl) { playerLevel = gPlayer->stats.lvl; skillbar_check_skill_activation(gSkillBar, gPlayer); } + map_clear_expired_entities(gMap, gPlayer); if (gGameState == PLAYING && currentTurn == PLAYER) player_update(&updateData); @@ -550,6 +547,7 @@ run_game_update(void) if (player_turn_over(gPlayer)) { currentTurn = MONSTER; player_reset_steps(gPlayer); + map_on_new_turn(gMap); repopulate_roommatrix(); } } else if (currentTurn == MONSTER) { diff --git a/src/map.c b/src/map.c index b146e61..e19044c 100644 --- a/src/map.c +++ b/src/map.c @@ -55,6 +55,7 @@ map_create(void) map->monsters = linkedlist_create(); map->items = linkedlist_create(); map->artifacts = linkedlist_create(); + map->objects = linkedlist_create(); map->currentRoom = (Position) { 0, 0 }; map->renderTimer = timer_create(); map->monsterMoveTimer = timer_create(); @@ -128,9 +129,9 @@ map_add_trap(Map *map, Position *pos, Trap *trap) } void -map_clear_dead_monsters(Map *map, Player *player) +map_clear_expired_entities(Map *map, Player *player) { - LinkedList *cleared = linkedlist_create(); + LinkedList *filtered = linkedlist_create(); while (map->monsters) { Monster *monster = linkedlist_pop(&map->monsters); @@ -138,17 +139,12 @@ map_clear_dead_monsters(Map *map, Player *player) monster_drop_loot(monster, map, player); monster_destroy(monster); } else { - linkedlist_append(&cleared, monster); + linkedlist_append(&filtered, monster); } } - map->monsters = cleared; -} - -void -map_clear_collected_items(Map *map) -{ - LinkedList *filtered = linkedlist_create(); + map->monsters = filtered; + filtered = linkedlist_create(); while (map->items) { Item *item = linkedlist_pop(&map->items); if (item->collected) @@ -157,12 +153,8 @@ map_clear_collected_items(Map *map) linkedlist_append(&filtered, item); } map->items = filtered; -} -void -map_clear_collected_artifacts(Map *map) -{ - LinkedList *filtered = linkedlist_create(); + filtered = linkedlist_create(); while (map->artifacts) { Artifact *a = linkedlist_pop(&map->artifacts); if (!a->collected) @@ -171,6 +163,16 @@ map_clear_collected_artifacts(Map *map) artifact_destroy(a); } map->artifacts = filtered; + + filtered = linkedlist_create(); + while (map->objects) { + Object *o = linkedlist_pop(&map->objects); + if (o->dead) + object_destroy(o); + else + linkedlist_append(&filtered, o); + } + map->objects = filtered; } void @@ -204,7 +206,7 @@ map_move_monsters(Map *map, RoomMatrix *rm) && position_proximity(1, &rm->playerRoomPos, &pos)) continue; - allDone = allDone && monster_move(monster, rm); + allDone = allDone && monster_move(monster, rm, map); } if (allDone) @@ -259,6 +261,17 @@ void map_tile_render(Map *map, MapTile *tile, Position *pos, Camera *cam) } +void +map_on_new_turn(Map *map) +{ + LinkedList *objects = map->objects; + while (objects) { + Object *o = objects->data; + objects = objects->next; + object_step(o); + } +} + void map_update(UpdateData *data) { @@ -304,6 +317,8 @@ void map_render(Map *map, Camera *cam) } if (room->modifier.type == RMOD_TYPE_WINDY) { particle_engine_wind(room->modifier.data.wind.direction); + } else if (room->modifier.type == RMOD_TYPE_FIRE) { + particle_engine_heat(); } } @@ -317,6 +332,12 @@ map_render_mid_layer(Map *map, Camera *cam) items = items->next; } + LinkedList *objects = map->objects; + while (objects != NULL) { + object_render(objects->data, cam); + objects = objects->next; + } + LinkedList *artifacts = map->artifacts; while (artifacts != NULL) { artifact_render(artifacts->data, cam); @@ -410,6 +431,9 @@ void map_destroy(Map *map) while (map->artifacts != NULL) artifact_destroy(linkedlist_pop(&map->artifacts)); + while (map->objects != NULL) + artifact_destroy(linkedlist_pop(&map->objects)); + timer_destroy(map->renderTimer); timer_destroy(map->monsterMoveTimer); free(map); diff --git a/src/map.h b/src/map.h index 294bc4a..a769607 100644 --- a/src/map.h +++ b/src/map.h @@ -31,6 +31,7 @@ #include "monster.h" #include "player.h" #include "map_room_modifiers.h" +#include "object.h" typedef struct UpdateData UpdateData; typedef struct Trap Trap; @@ -58,6 +59,7 @@ typedef struct Map_t { LinkedList *monsters; LinkedList *items; LinkedList *artifacts; + LinkedList *objects; Position currentRoom; Timer *renderTimer; Timer *monsterMoveTimer; @@ -89,13 +91,10 @@ bool map_move_monsters(Map*, RoomMatrix*); void -map_clear_dead_monsters(Map*, Player*); +map_clear_expired_entities(Map*, Player*); void -map_clear_collected_items(Map*); - -void -map_clear_collected_artifacts(Map*); +map_on_new_turn(Map*); void map_update(UpdateData*); diff --git a/src/map_lua.c b/src/map_lua.c index 37df660..5719434 100644 --- a/src/map_lua.c +++ b/src/map_lua.c @@ -116,6 +116,9 @@ l_map_set_current_room_modifier(lua_State *L) Room *room = map->rooms[map->currentRoom.x][map->currentRoom.y]; room->modifier.type = RMOD_TYPE_WINDY; room->modifier.data.wind.direction = dir; + } else if (strcmp(modifier, "FIRE") == 0) { + Room *room = map->rooms[map->currentRoom.x][map->currentRoom.y]; + room->modifier.type = RMOD_TYPE_FIRE; } else { luaL_error(L, "Unknown room modifier: %s", modifier); return 1; diff --git a/src/map_room_modifiers.h b/src/map_room_modifiers.h index 8f0b9b5..d0a8fe7 100644 --- a/src/map_room_modifiers.h +++ b/src/map_room_modifiers.h @@ -27,10 +27,11 @@ typedef struct RoomMatrix_t RoomMatrix; typedef enum RoomModifierType_e { RMOD_TYPE_NONE, - RMOD_TYPE_WINDY + RMOD_TYPE_WINDY, + RMOD_TYPE_FIRE } RoomModifierType; -typedef struct WindData_t { +typedef struct WindData { Vector2d direction; } WindData; @@ -38,7 +39,7 @@ typedef union RoomModifierDataContainer_t { WindData wind; } RoomModifierDataContainer; -typedef struct RoomModifierData_t { +typedef struct RoomModifierData { RoomModifierType type; RoomModifierDataContainer data; } RoomModifierData; diff --git a/src/monster.c b/src/monster.c index 8761ab2..2cdd3c4 100644 --- a/src/monster.c +++ b/src/monster.c @@ -236,7 +236,7 @@ has_collided(Monster *monster, RoomMatrix *matrix, Vector2d direction) monster_behaviour_check_post_attack(monster); } - return space->occupied || space->lethal || space->trap; + return space->occupied || space->lethal || space->trap || space->damaging; } static bool @@ -376,7 +376,7 @@ monster_coward_walk(Monster *m, RoomMatrix *rm) } bool -monster_move(Monster *m, RoomMatrix *rm) +monster_move(Monster *m, RoomMatrix *rm, Map *map) { if (m->state.forceCount) { if (m->state.stepsSinceChange >= m->state.forceCount) { @@ -392,10 +392,11 @@ monster_move(Monster *m, RoomMatrix *rm) monster_behaviour_check(m, rm); - Position originalPosition = + Position origPos = m->sprite->pos; + Position originalMPos = position_to_matrix_coords(&m->sprite->pos); - rm->spaces[originalPosition.x][originalPosition.y].occupied = false; - rm->spaces[originalPosition.x][originalPosition.y].monster = NULL; + rm->spaces[originalMPos.x][originalMPos.y].occupied = false; + rm->spaces[originalMPos.x][originalMPos.y].monster = NULL; switch (m->state.current) { case PASSIVE: @@ -420,7 +421,7 @@ monster_move(Monster *m, RoomMatrix *rm) rm->spaces[newPos.x][newPos.y].occupied = true; rm->spaces[newPos.x][newPos.y].monster = m; - if (!position_equals(&originalPosition, &newPos)) { + if (!position_equals(&originalMPos, &newPos)) { Player *p = rm->spaces[rm->playerRoomPos.x][rm->playerRoomPos.y].player; if (p) { Uint32 range = 3 + player_has_artifact(p, IMPROVED_HEARING) * 2; @@ -434,6 +435,14 @@ monster_move(Monster *m, RoomMatrix *rm) actiontextbuilder_create_text("!", C_WHITE, &m->sprite->pos); } } + + } + + if (!position_equals(&origPos, &m->sprite->pos) && rm->modifier->type == RMOD_TYPE_FIRE) { + Object *o = object_create_fire(); + o->sprite->pos = origPos; + o->damage *= m->stats.lvl; + linkedlist_push(&map->objects, o); } m->steps++; @@ -576,6 +585,9 @@ monster_render(Monster *m, Camera *cam) void monster_render_top_layer(Monster *m, Camera *cam) { + if (m->stats.hp <= 0) + return; + if (m->stateIndicator.displayCount != 0) sprite_render(m->stateIndicator.sprite, cam); } diff --git a/src/monster.h b/src/monster.h index b47adb9..e107643 100644 --- a/src/monster.h +++ b/src/monster.h @@ -77,7 +77,7 @@ void monster_update_pos(Monster*, Position); bool -monster_move(Monster*, RoomMatrix*); +monster_move(Monster*, RoomMatrix*, Map*); void monster_render(Monster*, Camera*); diff --git a/src/object.c b/src/object.c new file mode 100644 index 0000000..3cd9098 --- /dev/null +++ b/src/object.c @@ -0,0 +1,82 @@ +/* + * BreakHack - A dungeone crawler RPG + * Copyright (C) 2018 Linus Probert + * + * 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 . + */ + +#include "object.h" +#include "util.h" +#include "mixer.h" +#include "random.h" +#include "texturecache.h" + +Object * +object_create(void) +{ + Object *o = ec_malloc(sizeof(Object)); + o->sprite = sprite_create(); + o->blocking = false; + o->damage = 0; + o->timeout = -1; + o->dead = false; + return o; +} + +Object * +object_create_fire() +{ + 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(16, 21*16); + o->damage = 3; + o->timeout = 5; + return o; +} + +void +object_render(Object *o, Camera *cam) +{ + sprite_render(o->sprite, cam); +} + +void +object_step(Object *o) +{ + if (o->timeout < 0) + return; + o->timeout--; + if (o->timeout <= 0) + o->dead = true; +} + +void +object_damage(Object *o, Player *p) +{ + if (!o->damage) + return; + p->stats.hp -= o->damage; + player_hit(p, o->damage); +} + +void +object_destroy(Object *o) +{ + sprite_destroy(o->sprite); + free(o); +} diff --git a/src/object.h b/src/object.h new file mode 100644 index 0000000..4ca2190 --- /dev/null +++ b/src/object.h @@ -0,0 +1,51 @@ +/* + * BreakHack - A dungeone crawler RPG + * Copyright (C) 2018 Linus Probert + * + * 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 . + */ +#pragma once + +#include +#include +#include "camera.h" +#include "player.h" +#include "sprite.h" +#include "player.h" + +typedef struct Object { + Sprite *sprite; + bool blocking; + Uint32 damage; + Sint32 timeout; + bool dead; +} Object; + +Object * +object_create(void); + +Object * +object_create_fire(void); + +void +object_render(Object*, Camera*); + +void +object_damage(Object*, Player*); + +void +object_step(Object*); + +void +object_destroy(Object*); diff --git a/src/particle_engine.c b/src/particle_engine.c index afefceb..b86ffb2 100644 --- a/src/particle_engine.c +++ b/src/particle_engine.c @@ -294,6 +294,49 @@ particle_engine_wind(Vector2d direction) } } +void +particle_engine_heat() +{ + unsigned int count = 5; + + Position pos = { 0, 0 }; + Dimension dim = { GAME_VIEW_WIDTH, GAME_VIEW_HEIGHT }; + + if (dim.width == 0 || dim.height == 0) + return; + + for (unsigned int i = 0; i < count; ++i) { + int x, y; + int w, h; + unsigned int lt; + Particle *p; + int yvel, xvel; + + x = get_random(dim.width) + pos.x; + y = get_random(dim.height) + pos.y; + w = get_random(2) + 2; + h = get_random(2) + 2; + + yvel = get_random(50) - 200; + xvel = get_random(100) * -get_random(1); + + lt = get_random(500); + + p = create_rect_particle(); + p->particle.rect.pos = (Position) { x, y }; + p->particle.rect.dim = (Dimension) { w, h }; + p->velocity = (Vector2d) { (float) xvel, (float) yvel }; + p->movetime = lt; + p->lifetime = lt; + if (get_random(1) == 0) + p->color = C_YELLOW; + else + p->color = C_RED; + p->fixed = true; + linkedlist_append(&engine->game_particles, p); + } +} + static void move_particle(Particle *particle, float deltaTime) { diff --git a/src/particle_engine.h b/src/particle_engine.h index 55f0d48..0e05c32 100644 --- a/src/particle_engine.h +++ b/src/particle_engine.h @@ -46,6 +46,9 @@ particle_engine_sparkle(Position, Dimension, SDL_Color, bool global); void particle_engine_wind(Vector2d direction); +void +particle_engine_heat(void); + void particle_engine_update(float deltatime); diff --git a/src/player.c b/src/player.c index 19c42f7..913d24a 100644 --- a/src/player.c +++ b/src/player.c @@ -194,6 +194,14 @@ has_collided(Player *player, RoomMatrix *matrix, Vector2d direction) } } + if (space->objects && !collided) { + LinkedList *objects = space->objects; + while (objects) { + object_damage(objects->data, player); + objects = objects->next; + } + } + if (space->lethal && !collided) { mixer_play_effect(FALL0 + get_random(2) - 1); player->state = FALLING; @@ -294,6 +302,7 @@ handle_next_move(UpdateData *data) static unsigned int step = 1; Player *player = data->player; + Position originPos = player->sprite->pos; // Don't move when projectiles are still moving if (linkedlist_size(player->projectiles) > 0) @@ -304,6 +313,14 @@ handle_next_move(UpdateData *data) if (!vector2d_equals(nextDir, VECTOR2D_NODIR)) move(player, matrix, nextDir); + if (!position_equals(&originPos, &player->sprite->pos) + && matrix->modifier->type == RMOD_TYPE_FIRE) { + Object *o = object_create_fire(); + o->damage *= player->stats.lvl; + o->sprite->pos = originPos; + linkedlist_append(&data->map->objects, o); + } + map_room_modifier_player_effect(player, matrix, &nextDir, move); if (!vector2d_equals(VECTOR2D_NODIR, nextDir)) { diff --git a/src/roommatrix.c b/src/roommatrix.c index 7f43e1b..519b37e 100644 --- a/src/roommatrix.c +++ b/src/roommatrix.c @@ -27,6 +27,7 @@ #include "update_data.h" #include "defines.h" #include "trap.h" +#include "object.h" static void roommatrix_reset(RoomMatrix *m) @@ -39,6 +40,7 @@ roommatrix_reset(RoomMatrix *m) space = &m->spaces[i][j]; space->occupied = false; space->lethal = false; + space->damaging = false; space->lightsource = false; space->light = 0; space->monster = NULL; @@ -48,6 +50,8 @@ roommatrix_reset(RoomMatrix *m) linkedlist_pop(&space->items); while (space->artifacts != NULL) linkedlist_pop(&space->artifacts); + while (space->objects != NULL) + linkedlist_pop(&space->objects); } } m->roomPos = (Position) { 0, 0 }; @@ -62,6 +66,7 @@ RoomMatrix* roommatrix_create(void) for (j = 0; j < MAP_ROOM_HEIGHT; ++j) { m->spaces[i][j].items = linkedlist_create();; m->spaces[i][j].artifacts = linkedlist_create();; + m->spaces[i][j].objects = linkedlist_create();; } } roommatrix_reset(m); @@ -168,6 +173,21 @@ void roommatrix_populate_from_map(RoomMatrix *rm, Map *m) position = position_to_matrix_coords(&a->sprite->pos); linkedlist_push(&rm->spaces[position.x][position.y].artifacts, a); } + + LinkedList *objects = m->objects; + while (objects) { + Object *o = objects->data; + objects = objects->next; + + if (!position_in_room(&o->sprite->pos, &m->currentRoom)) + continue; + + position = position_to_matrix_coords(&o->sprite->pos); + RoomSpace *space = &rm->spaces[position.x][position.y]; + linkedlist_push(&space->objects, o); + space->occupied = space->occupied || o->blocking; + space->damaging = o->damage > 0; + } } void @@ -275,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); } } diff --git a/src/roommatrix.h b/src/roommatrix.h index 01884c4..dd2dde6 100644 --- a/src/roommatrix.h +++ b/src/roommatrix.h @@ -33,19 +33,22 @@ typedef struct Player Player; typedef struct Item_t Item; typedef struct Node LinkedList; typedef struct Trap Trap; +typedef struct Object Object; struct UpdateData; -typedef struct { +typedef struct RoomSpace { bool occupied; bool lethal; bool lightsource; + bool damaging; int light; Monster *monster; Player *player; Trap *trap; LinkedList *items; LinkedList *artifacts; + LinkedList *objects; } RoomSpace; typedef struct RoomMatrix_t {