2018-02-16 18:11:26 +01:00
|
|
|
/*
|
|
|
|
* 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/>.
|
|
|
|
*/
|
|
|
|
|
2017-12-21 11:57:12 +01:00
|
|
|
#include <stdlib.h>
|
2017-12-01 16:03:19 +01:00
|
|
|
#include "map.h"
|
2017-12-02 23:32:40 +01:00
|
|
|
#include "map_lua.h"
|
2017-12-01 16:03:19 +01:00
|
|
|
#include "util.h"
|
2018-01-24 21:14:34 +01:00
|
|
|
#include "item.h"
|
|
|
|
#include "item_builder.h"
|
|
|
|
#include "gui.h"
|
2018-03-15 11:30:18 +01:00
|
|
|
#include "particle_engine.h"
|
2018-05-15 11:16:56 +02:00
|
|
|
#include "update_data.h"
|
2018-08-06 00:28:23 +02:00
|
|
|
#include "trap.h"
|
2017-12-01 16:03:19 +01:00
|
|
|
|
|
|
|
static
|
2017-12-19 21:00:02 +01:00
|
|
|
Room* create_room(void)
|
2017-12-01 16:03:19 +01:00
|
|
|
{
|
|
|
|
int i, j;
|
|
|
|
Room *room;
|
|
|
|
|
|
|
|
room = ec_malloc(sizeof(Room));
|
2018-03-15 11:30:18 +01:00
|
|
|
room->modifier.type = RMOD_TYPE_NONE;
|
2017-12-05 08:13:28 +01:00
|
|
|
for (i=0; i < MAP_ROOM_WIDTH; ++i) {
|
|
|
|
for (j=0; j < MAP_ROOM_HEIGHT; ++j) {
|
2017-12-01 16:03:19 +01:00
|
|
|
room->tiles[i][j] = NULL;
|
2017-12-08 09:45:57 +01:00
|
|
|
room->decorations[i][j] = NULL;
|
2018-08-06 00:28:23 +02:00
|
|
|
room->traps[i][j] = NULL;
|
2017-12-01 16:03:19 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
return room;
|
|
|
|
}
|
|
|
|
|
2018-03-25 23:30:26 +02:00
|
|
|
Map*
|
|
|
|
map_create(void)
|
2017-12-01 16:03:19 +01:00
|
|
|
{
|
|
|
|
int i, j;
|
|
|
|
|
|
|
|
Map *map = ec_malloc(sizeof(Map));
|
2017-12-02 16:24:31 +01:00
|
|
|
map->textures = linkedlist_create();
|
2017-12-13 14:26:30 +01:00
|
|
|
map->monsters = linkedlist_create();
|
2018-01-24 21:14:34 +01:00
|
|
|
map->items = linkedlist_create();
|
2018-08-08 00:14:24 +02:00
|
|
|
map->artifacts = linkedlist_create();
|
2017-12-01 16:03:19 +01:00
|
|
|
map->currentRoom = (Position) { 0, 0 };
|
2017-12-08 14:40:33 +01:00
|
|
|
map->renderTimer = timer_create();
|
2018-02-19 15:09:04 +01:00
|
|
|
map->monsterMoveTimer = timer_create();
|
2017-12-01 16:03:19 +01:00
|
|
|
map->level = 1;
|
|
|
|
|
2017-12-05 08:13:28 +01:00
|
|
|
for (i=0; i < MAP_H_ROOM_COUNT; ++i) {
|
|
|
|
for (j=0; j < MAP_V_ROOM_COUNT; ++j) {
|
2017-12-01 16:03:19 +01:00
|
|
|
map->rooms[i][j] = create_room();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return map;
|
|
|
|
}
|
|
|
|
|
2018-03-25 23:30:26 +02:00
|
|
|
MapTile*
|
|
|
|
map_create_tile(void)
|
|
|
|
{
|
|
|
|
MapTile *tile = ec_malloc(sizeof(MapTile));
|
|
|
|
tile->textureIndex0 = -1;
|
|
|
|
tile->textureIndex1 = -1;
|
|
|
|
tile->clip = CLIP16(0, 0);
|
|
|
|
tile->collider = false;
|
|
|
|
tile->lethal = false;
|
|
|
|
tile->lightsource = false;
|
|
|
|
tile->levelExit = false;
|
|
|
|
return tile;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
map_add_tile(Map *map, Position *tile_pos, MapTile *tile)
|
2017-12-02 23:32:40 +01:00
|
|
|
{
|
2017-12-05 08:13:28 +01:00
|
|
|
const Position *cr = &map->currentRoom;
|
2017-12-22 06:27:58 +01:00
|
|
|
Room *room = map->rooms[cr->x][cr->y];
|
|
|
|
MapTile **oldTile = &room->tiles[tile_pos->x][tile_pos->y];
|
|
|
|
|
|
|
|
// Clear possible decoration
|
|
|
|
if (tile->levelExit && room->tiles[tile_pos->x][tile_pos->y]) {
|
|
|
|
MapTile **decoration = &room->tiles[tile_pos->x][tile_pos->y];
|
|
|
|
free(*decoration);
|
|
|
|
*decoration = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Clear possible tile
|
2017-12-02 23:32:40 +01:00
|
|
|
if (*oldTile != NULL) {
|
|
|
|
free(*oldTile);
|
|
|
|
*oldTile = NULL;
|
|
|
|
}
|
|
|
|
*oldTile = tile;
|
|
|
|
}
|
|
|
|
|
2017-12-08 09:45:57 +01:00
|
|
|
void map_add_decoration(Map *map, Position *tile_pos, MapTile *tile)
|
|
|
|
{
|
|
|
|
const Position *cr = &map->currentRoom;
|
|
|
|
MapTile **oldTile = &map->rooms[cr->x][cr->y]->decorations[tile_pos->x][tile_pos->y];
|
|
|
|
if (*oldTile != NULL) {
|
|
|
|
free(*oldTile);
|
|
|
|
*oldTile = NULL;
|
|
|
|
}
|
|
|
|
*oldTile = tile;
|
|
|
|
}
|
|
|
|
|
2018-08-06 00:28:23 +02:00
|
|
|
void
|
|
|
|
map_add_trap(Map *map, Position *pos, Trap *trap)
|
|
|
|
{
|
|
|
|
const Position *cr = &map->currentRoom;
|
|
|
|
Trap **oldTrap = &map->rooms[cr->x][cr->y]->traps[pos->x][pos->y];
|
|
|
|
if (*oldTrap)
|
|
|
|
trap_destroy(*oldTrap);
|
|
|
|
|
|
|
|
*oldTrap = trap;
|
|
|
|
}
|
|
|
|
|
2017-12-15 08:08:45 +01:00
|
|
|
void
|
2018-02-24 21:15:13 +01:00
|
|
|
map_clear_dead_monsters(Map *map, Player *player)
|
2017-12-15 08:08:45 +01:00
|
|
|
{
|
2018-03-13 21:47:49 +01:00
|
|
|
LinkedList *cleared = linkedlist_create();
|
2018-01-25 10:45:05 +01:00
|
|
|
|
2018-03-13 21:47:49 +01:00
|
|
|
while (map->monsters) {
|
|
|
|
Monster *monster = linkedlist_pop(&map->monsters);
|
|
|
|
if (monster->stats.hp <= 0) {
|
2018-02-24 21:15:13 +01:00
|
|
|
monster_drop_loot(monster, map, player);
|
2018-01-24 21:14:34 +01:00
|
|
|
monster_destroy(monster);
|
2018-03-13 21:47:49 +01:00
|
|
|
} else {
|
|
|
|
linkedlist_append(&cleared, monster);
|
2018-01-24 21:14:34 +01:00
|
|
|
}
|
|
|
|
}
|
2018-03-13 21:47:49 +01:00
|
|
|
map->monsters = cleared;
|
2018-01-24 21:14:34 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
map_clear_collected_items(Map *map)
|
|
|
|
{
|
2018-03-13 21:47:49 +01:00
|
|
|
LinkedList *filtered = linkedlist_create();
|
|
|
|
|
|
|
|
while (map->items) {
|
|
|
|
Item *item = linkedlist_pop(&map->items);
|
|
|
|
if (item->collected)
|
|
|
|
item_destroy(item);
|
|
|
|
else
|
|
|
|
linkedlist_append(&filtered, item);
|
2017-12-15 08:08:45 +01:00
|
|
|
}
|
2018-03-13 21:47:49 +01:00
|
|
|
map->items = filtered;
|
2017-12-15 08:08:45 +01:00
|
|
|
}
|
|
|
|
|
2018-08-08 00:14:24 +02:00
|
|
|
void
|
|
|
|
map_clear_collected_artifacts(Map *map)
|
|
|
|
{
|
|
|
|
LinkedList *filtered = linkedlist_create();
|
|
|
|
while (map->artifacts) {
|
|
|
|
Artifact *a = linkedlist_pop(&map->artifacts);
|
|
|
|
if (!a->collected)
|
|
|
|
linkedlist_append(&filtered, a);
|
|
|
|
else
|
|
|
|
artifact_destroy(a);
|
|
|
|
}
|
|
|
|
map->artifacts = filtered;
|
|
|
|
}
|
|
|
|
|
2017-12-13 20:31:04 +01:00
|
|
|
void
|
|
|
|
map_add_monster(Map *map, Monster *m)
|
|
|
|
{
|
2018-01-30 15:16:14 +01:00
|
|
|
monster_update_stats_for_level(m, map->level);
|
2017-12-13 20:31:04 +01:00
|
|
|
linkedlist_append(&map->monsters, m);
|
|
|
|
}
|
|
|
|
|
2018-02-19 15:09:04 +01:00
|
|
|
bool
|
2017-12-17 13:43:41 +01:00
|
|
|
map_move_monsters(Map *map, RoomMatrix *rm)
|
|
|
|
{
|
|
|
|
LinkedList *m = map->monsters;
|
2018-02-19 15:09:04 +01:00
|
|
|
bool allDone = true;
|
|
|
|
|
|
|
|
if (timer_started(map->monsterMoveTimer)
|
|
|
|
&& timer_get_ticks(map->monsterMoveTimer) < 100)
|
|
|
|
return false;
|
|
|
|
|
2017-12-17 13:43:41 +01:00
|
|
|
while (m) {
|
|
|
|
Monster *monster = m->data;
|
|
|
|
m = m->next;
|
2018-03-13 23:36:39 +01:00
|
|
|
if (monster->stats.hp <= 0)
|
|
|
|
continue;
|
2017-12-17 13:43:41 +01:00
|
|
|
if (!position_in_room(&monster->sprite->pos, &map->currentRoom))
|
|
|
|
continue;
|
2018-03-19 10:25:32 +01:00
|
|
|
|
|
|
|
// Prevent passive monsters from being "dodgy"
|
|
|
|
Position pos = position_to_matrix_coords(&monster->sprite->pos);
|
|
|
|
if (monster->state.current == PASSIVE
|
|
|
|
&& position_proximity(1, &rm->playerRoomPos, &pos))
|
|
|
|
continue;
|
|
|
|
|
2018-02-19 15:09:04 +01:00
|
|
|
allDone = allDone && monster_move(monster, rm);
|
2017-12-17 13:43:41 +01:00
|
|
|
}
|
2018-02-19 15:09:04 +01:00
|
|
|
|
|
|
|
if (allDone)
|
|
|
|
timer_stop(map->monsterMoveTimer);
|
|
|
|
else
|
|
|
|
timer_start(map->monsterMoveTimer);
|
|
|
|
|
|
|
|
return allDone;
|
2017-12-17 13:43:41 +01:00
|
|
|
}
|
|
|
|
|
2017-12-02 23:32:40 +01:00
|
|
|
int map_add_texture(Map *map, const char *path, SDL_Renderer *renderer)
|
|
|
|
{
|
2017-12-15 15:03:29 +01:00
|
|
|
Texture *t = texture_create();
|
|
|
|
texture_load_from_file(t, path, renderer);
|
2017-12-13 20:31:04 +01:00
|
|
|
linkedlist_append(&map->textures, t);
|
2017-12-02 23:32:40 +01:00
|
|
|
return linkedlist_size(map->textures) - 1;
|
|
|
|
}
|
|
|
|
|
2017-12-02 16:24:31 +01:00
|
|
|
static
|
|
|
|
void map_tile_render(Map *map, MapTile *tile, Position *pos, Camera *cam)
|
|
|
|
{
|
2017-12-08 14:40:33 +01:00
|
|
|
static bool second_texture = false;
|
|
|
|
|
2017-12-02 16:24:31 +01:00
|
|
|
if (tile == NULL)
|
|
|
|
return;
|
2017-12-02 23:32:40 +01:00
|
|
|
|
2017-12-08 14:40:33 +01:00
|
|
|
if (timer_get_ticks(map->renderTimer) > 300) {
|
|
|
|
timer_start(map->renderTimer);
|
|
|
|
second_texture = !second_texture;
|
|
|
|
}
|
|
|
|
|
2017-12-02 23:32:40 +01:00
|
|
|
Position camPos = camera_to_camera_position(cam, pos);
|
|
|
|
SDL_Rect draw_box = (SDL_Rect) {
|
|
|
|
camPos.x,
|
|
|
|
camPos.y,
|
2017-12-03 11:09:57 +01:00
|
|
|
TILE_DIMENSION,
|
|
|
|
TILE_DIMENSION
|
2017-12-02 23:32:40 +01:00
|
|
|
};
|
|
|
|
|
2017-12-08 14:40:33 +01:00
|
|
|
Texture *texture;
|
|
|
|
if (tile->textureIndex1 >= 0 && second_texture) {
|
|
|
|
texture = linkedlist_get(&map->textures, tile->textureIndex1);
|
|
|
|
} else {
|
|
|
|
texture = linkedlist_get(&map->textures, tile->textureIndex0);
|
|
|
|
}
|
2017-12-02 23:32:40 +01:00
|
|
|
|
|
|
|
SDL_RenderCopy(cam->renderer,
|
|
|
|
texture->texture,
|
|
|
|
&tile->clip,
|
|
|
|
&draw_box
|
|
|
|
);
|
|
|
|
|
2017-12-02 16:24:31 +01:00
|
|
|
}
|
|
|
|
|
2018-05-15 11:16:56 +02:00
|
|
|
void
|
|
|
|
map_update(UpdateData *data)
|
|
|
|
{
|
|
|
|
Map *map = data->map;
|
|
|
|
LinkedList *monster = map->monsters;
|
|
|
|
while (monster) {
|
|
|
|
Monster *m = monster->data;
|
|
|
|
monster = monster->next;
|
|
|
|
monster_update(m, data);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-12-01 16:03:19 +01:00
|
|
|
void map_render(Map *map, Camera *cam)
|
|
|
|
{
|
2017-12-17 13:43:41 +01:00
|
|
|
unsigned int i, j;
|
2017-12-01 16:03:19 +01:00
|
|
|
Room *room;
|
2017-12-08 14:40:33 +01:00
|
|
|
|
|
|
|
if (!timer_started(map->renderTimer)) {
|
|
|
|
timer_start(map->renderTimer);
|
|
|
|
}
|
|
|
|
|
2017-12-01 16:03:19 +01:00
|
|
|
Position roomPos = { map->currentRoom.x, map->currentRoom.y };
|
|
|
|
Position roomCords = {
|
2017-12-03 11:09:57 +01:00
|
|
|
roomPos.x * MAP_ROOM_WIDTH * TILE_DIMENSION,
|
|
|
|
roomPos.y * MAP_ROOM_HEIGHT * TILE_DIMENSION
|
2017-12-01 16:03:19 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
room = map->rooms[roomPos.x][roomPos.y];
|
2017-12-05 08:13:28 +01:00
|
|
|
for (i=0; i < MAP_ROOM_WIDTH; ++i) {
|
|
|
|
for (j=0; j < MAP_ROOM_HEIGHT; ++j) {
|
2017-12-02 23:32:40 +01:00
|
|
|
Position tilePos = (Position) {
|
2017-12-05 08:13:28 +01:00
|
|
|
roomCords.x + i*TILE_DIMENSION,
|
|
|
|
roomCords.y + j*TILE_DIMENSION
|
2017-12-02 23:32:40 +01:00
|
|
|
};
|
|
|
|
map_tile_render(map, room->tiles[i][j], &tilePos, cam);
|
2017-12-13 20:31:04 +01:00
|
|
|
map_tile_render(map,
|
|
|
|
room->decorations[i][j],
|
|
|
|
&tilePos,
|
|
|
|
cam);
|
2018-08-06 00:28:23 +02:00
|
|
|
if (room->traps[i][j])
|
|
|
|
trap_render(room->traps[i][j], cam);
|
2017-12-01 16:03:19 +01:00
|
|
|
}
|
|
|
|
}
|
2018-03-15 11:30:18 +01:00
|
|
|
if (room->modifier.type == RMOD_TYPE_WINDY) {
|
|
|
|
particle_engine_wind(room->modifier.data.wind.direction);
|
|
|
|
}
|
2017-12-13 20:31:04 +01:00
|
|
|
|
2018-05-17 21:41:23 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2018-08-11 15:15:53 +02:00
|
|
|
map_render_mid_layer(Map *map, Camera *cam)
|
2018-05-17 21:41:23 +02:00
|
|
|
{
|
|
|
|
LinkedList *items = map->items;
|
2018-01-24 21:14:34 +01:00
|
|
|
while (items != NULL) {
|
2018-08-08 00:14:24 +02:00
|
|
|
item_render(items->data, cam);
|
2018-01-24 21:14:34 +01:00
|
|
|
items = items->next;
|
2018-08-08 00:14:24 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
LinkedList *artifacts = map->artifacts;
|
|
|
|
while (artifacts != NULL) {
|
|
|
|
artifact_render(artifacts->data, cam);
|
|
|
|
artifacts = artifacts->next;
|
2018-01-24 21:14:34 +01:00
|
|
|
}
|
2018-08-11 15:15:53 +02:00
|
|
|
|
|
|
|
LinkedList *monsterItem = map->monsters;
|
|
|
|
while (monsterItem != NULL) {
|
|
|
|
Monster *monster = monsterItem->data;
|
|
|
|
monsterItem = monsterItem->next;
|
|
|
|
monster_render(monster, cam);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
map_render_top_layer(Map *map, Camera *cam)
|
|
|
|
{
|
|
|
|
LinkedList *monsterItem = map->monsters;
|
|
|
|
while (monsterItem != NULL) {
|
|
|
|
Monster *monster = monsterItem->data;
|
|
|
|
monsterItem = monsterItem->next;
|
|
|
|
monster_render_top_layer(monster, cam);
|
|
|
|
}
|
2017-12-01 16:03:19 +01:00
|
|
|
}
|
|
|
|
|
2017-12-03 11:09:57 +01:00
|
|
|
void map_set_current_room(Map *map, Position *pos)
|
|
|
|
{
|
|
|
|
unsigned int room_width, room_height;
|
|
|
|
|
|
|
|
room_width = MAP_ROOM_WIDTH * TILE_DIMENSION;
|
|
|
|
room_height = MAP_ROOM_HEIGHT * TILE_DIMENSION;
|
|
|
|
|
|
|
|
if (pos->x <= 0) {
|
|
|
|
map->currentRoom.x = 0;
|
|
|
|
} else {
|
|
|
|
unsigned int room_cord_x = pos->x - (pos->x % room_width);
|
|
|
|
map->currentRoom.x = room_cord_x / room_width;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (pos->y <= 0) {
|
|
|
|
map->currentRoom.y = 0;
|
|
|
|
} else {
|
|
|
|
unsigned int room_cord_y = pos->y - (pos->y % room_height);
|
|
|
|
map->currentRoom.y = room_cord_y / room_height;
|
|
|
|
}
|
2017-12-05 08:13:28 +01:00
|
|
|
|
|
|
|
if (map->currentRoom.x >= MAP_H_ROOM_COUNT)
|
|
|
|
map->currentRoom.x = MAP_H_ROOM_COUNT - 1;
|
|
|
|
if (map->currentRoom.y >= MAP_V_ROOM_COUNT)
|
|
|
|
map->currentRoom.y = MAP_V_ROOM_COUNT - 1;
|
2017-12-03 11:09:57 +01:00
|
|
|
}
|
|
|
|
|
2017-12-01 16:03:19 +01:00
|
|
|
static
|
|
|
|
void map_room_destroy(Room *room)
|
|
|
|
{
|
|
|
|
int i, j;
|
2017-12-05 08:13:28 +01:00
|
|
|
for (i=0; i < MAP_ROOM_WIDTH; ++i) {
|
|
|
|
for (j=0; j < MAP_ROOM_HEIGHT; ++j) {
|
2017-12-01 16:03:19 +01:00
|
|
|
if (room->tiles[i][j]) {
|
2017-12-02 16:24:31 +01:00
|
|
|
free(room->tiles[i][j]);
|
2017-12-01 16:03:19 +01:00
|
|
|
}
|
2017-12-08 09:45:57 +01:00
|
|
|
if (room->decorations[i][j]) {
|
|
|
|
free(room->decorations[i][j]);
|
|
|
|
}
|
2018-08-06 00:28:23 +02:00
|
|
|
if (room->traps[i][j]) {
|
|
|
|
trap_destroy(room->traps[i][j]);
|
|
|
|
}
|
2017-12-01 16:03:19 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
free(room);
|
|
|
|
}
|
|
|
|
|
|
|
|
void map_destroy(Map *map)
|
|
|
|
{
|
|
|
|
int i, j;
|
2017-12-05 08:13:28 +01:00
|
|
|
for (i=0; i < MAP_H_ROOM_COUNT; ++i) {
|
|
|
|
for (j=0; j < MAP_V_ROOM_COUNT; ++j) {
|
2017-12-01 16:03:19 +01:00
|
|
|
map_room_destroy(map->rooms[i][j]);
|
|
|
|
}
|
|
|
|
}
|
2018-01-24 21:14:34 +01:00
|
|
|
|
|
|
|
while (map->textures != NULL)
|
2017-12-13 14:26:30 +01:00
|
|
|
texture_destroy(linkedlist_pop(&map->textures));
|
2018-01-24 21:14:34 +01:00
|
|
|
|
|
|
|
while (map->monsters != NULL)
|
2017-12-13 23:20:54 +01:00
|
|
|
monster_destroy(linkedlist_pop(&map->monsters));
|
2018-01-24 21:14:34 +01:00
|
|
|
|
|
|
|
while (map->items != NULL)
|
|
|
|
item_destroy(linkedlist_pop(&map->items));
|
|
|
|
|
2018-08-08 00:14:24 +02:00
|
|
|
while (map->artifacts != NULL)
|
|
|
|
artifact_destroy(linkedlist_pop(&map->artifacts));
|
|
|
|
|
2017-12-08 14:40:33 +01:00
|
|
|
timer_destroy(map->renderTimer);
|
2018-03-06 13:50:43 +01:00
|
|
|
timer_destroy(map->monsterMoveTimer);
|
2017-12-01 16:03:19 +01:00
|
|
|
free(map);
|
|
|
|
}
|