breakhack/src/map.c

350 lines
7.6 KiB
C

/*
* 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 <stdlib.h>
#include "map.h"
#include "map_lua.h"
#include "util.h"
#include "item.h"
#include "item_builder.h"
#include "gui.h"
static
Room* create_room(void)
{
int i, j;
Room *room;
room = ec_malloc(sizeof(Room));
for (i=0; i < MAP_ROOM_WIDTH; ++i) {
for (j=0; j < MAP_ROOM_HEIGHT; ++j) {
room->tiles[i][j] = NULL;
room->decorations[i][j] = NULL;
}
}
return room;
}
Map* map_create()
{
int i, j;
Map *map = ec_malloc(sizeof(Map));
map->textures = linkedlist_create();
map->monsterTextures = ht_create(30);
map->monsters = linkedlist_create();
map->items = linkedlist_create();
map->currentRoom = (Position) { 0, 0 };
map->renderTimer = timer_create();
map->level = 1;
for (i=0; i < MAP_H_ROOM_COUNT; ++i) {
for (j=0; j < MAP_V_ROOM_COUNT; ++j) {
map->rooms[i][j] = create_room();
}
}
return map;
}
void map_add_tile(Map *map, Position *tile_pos, MapTile *tile)
{
const Position *cr = &map->currentRoom;
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
if (*oldTile != NULL) {
free(*oldTile);
*oldTile = NULL;
}
*oldTile = tile;
}
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;
}
Texture*
map_add_monster_texture(Map *map, const char *path, SDL_Renderer *renderer)
{
Texture *t;
t = ht_get(map->monsterTextures, path);
if (!t) {
t = texture_create();
texture_load_from_file(t, path, renderer);
ht_set(map->monsterTextures, path, t);
}
return t;
}
void
map_clear_dead_monsters(Map *map)
{
LinkedList *last, *current, *next;
last = NULL;
current = map->monsters;
while (current != NULL) {
if (((Monster*) current->data)->stats.hp <= 0) {
if (last == NULL)
map->monsters = current->next;
else
last->next = current->next;
Monster *monster = current->data;
// Loot drops
monster_drop_loot(monster, map);
monster_destroy(monster);
current->data = NULL;
next = current->next;
current->next = NULL;
linkedlist_destroy(&current);
current = next;
continue;
}
last = current;
current = current->next;
}
}
void
map_clear_collected_items(Map *map)
{
LinkedList *last, *current, *next;
last = NULL;
current = map->items;
while (current != NULL) {
if (((Item*) current->data)->collected) {
if (last == NULL)
map->items = current->next;
else
last->next = current->next;
item_destroy(current->data);
current->data = NULL;
next = current->next;
current->next = NULL;
linkedlist_destroy(&current);
current = next;
continue;
}
last = current;
current = current->next;
}
}
void
map_add_monster(Map *map, Monster *m)
{
monster_update_stats_for_level(m, map->level);
linkedlist_append(&map->monsters, m);
}
void
map_move_monsters(Map *map, RoomMatrix *rm)
{
LinkedList *m = map->monsters;
while (m) {
Monster *monster = m->data;
m = m->next;
if (!position_in_room(&monster->sprite->pos, &map->currentRoom))
continue;
monster_move(monster, rm);
}
}
int map_add_texture(Map *map, const char *path, SDL_Renderer *renderer)
{
Texture *t = texture_create();
texture_load_from_file(t, path, renderer);
linkedlist_append(&map->textures, t);
return linkedlist_size(map->textures) - 1;
}
static
void map_tile_render(Map *map, MapTile *tile, Position *pos, Camera *cam)
{
static bool second_texture = false;
if (tile == NULL)
return;
if (timer_get_ticks(map->renderTimer) > 300) {
timer_start(map->renderTimer);
second_texture = !second_texture;
}
Position camPos = camera_to_camera_position(cam, pos);
SDL_Rect draw_box = (SDL_Rect) {
camPos.x,
camPos.y,
TILE_DIMENSION,
TILE_DIMENSION
};
Texture *texture;
if (tile->textureIndex1 >= 0 && second_texture) {
texture = linkedlist_get(&map->textures, tile->textureIndex1);
} else {
texture = linkedlist_get(&map->textures, tile->textureIndex0);
}
SDL_RenderCopy(cam->renderer,
texture->texture,
&tile->clip,
&draw_box
);
}
void map_render(Map *map, Camera *cam)
{
unsigned int i, j;
LinkedList *monsterItem;
LinkedList *items;
Room *room;
if (!timer_started(map->renderTimer)) {
timer_start(map->renderTimer);
}
Position roomPos = { map->currentRoom.x, map->currentRoom.y };
Position roomCords = {
roomPos.x * MAP_ROOM_WIDTH * TILE_DIMENSION,
roomPos.y * MAP_ROOM_HEIGHT * TILE_DIMENSION
};
room = map->rooms[roomPos.x][roomPos.y];
for (i=0; i < MAP_ROOM_WIDTH; ++i) {
for (j=0; j < MAP_ROOM_HEIGHT; ++j) {
Position tilePos = (Position) {
roomCords.x + i*TILE_DIMENSION,
roomCords.y + j*TILE_DIMENSION
};
map_tile_render(map, room->tiles[i][j], &tilePos, cam);
map_tile_render(map,
room->decorations[i][j],
&tilePos,
cam);
}
}
monsterItem = map->monsters;
while (monsterItem != NULL) {
Monster *monster = monsterItem->data;
monsterItem = monsterItem->next;
monster_render(monster, cam);
}
items = map->items;
while (items != NULL) {
Item *item = items->data;
items = items->next;
item_render(item, cam);
}
}
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;
}
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;
}
static
void map_room_destroy(Room *room)
{
int i, j;
for (i=0; i < MAP_ROOM_WIDTH; ++i) {
for (j=0; j < MAP_ROOM_HEIGHT; ++j) {
if (room->tiles[i][j]) {
free(room->tiles[i][j]);
}
if (room->decorations[i][j]) {
free(room->decorations[i][j]);
}
}
}
free(room);
}
void map_destroy(Map *map)
{
int i, j;
for (i=0; i < MAP_H_ROOM_COUNT; ++i) {
for (j=0; j < MAP_V_ROOM_COUNT; ++j) {
map_room_destroy(map->rooms[i][j]);
}
}
while (map->textures != NULL)
texture_destroy(linkedlist_pop(&map->textures));
while (map->monsters != NULL)
monster_destroy(linkedlist_pop(&map->monsters));
while (map->items != NULL)
item_destroy(linkedlist_pop(&map->items));
ht_destroy_custom(map->monsterTextures, (void (*)(void*)) texture_destroy);
timer_destroy(map->renderTimer);
free(map);
}