1387 lines
33 KiB
C
1387 lines
33 KiB
C
/**
|
|
* @file engine.c
|
|
*/
|
|
|
|
#include "utils.h"
|
|
#include "engine.h"
|
|
|
|
|
|
/*
|
|
* Typedefs
|
|
*/
|
|
struct room_wall_e {
|
|
s32 x;
|
|
s32 y;
|
|
u8 b; //true = south, false = east
|
|
struct room_wall_e* prev;
|
|
struct room_wall_e* next;
|
|
};
|
|
|
|
typedef struct room_wall_e room_wall_t;
|
|
|
|
typedef struct labelpair_e {
|
|
u32 k;
|
|
u32 v;
|
|
} labelpair_t;
|
|
|
|
typedef struct lpt_e {
|
|
labelpair_t* lp;
|
|
u32 size;
|
|
} lpt_t;
|
|
|
|
|
|
#define s16_MAX 2147483647
|
|
#define WR_SIZE 1
|
|
#define SLOT_MASK 16
|
|
#define PART_MASK 24
|
|
|
|
|
|
/*************************************************************************/
|
|
/* FUNCTIONS */
|
|
/*************************************************************************/
|
|
|
|
/*************************************************************************/
|
|
/* ROOM UTILS FUNCTIONS */
|
|
/*************************************************************************/
|
|
void shift_mod_reset(s32 shift, s32 mod, s32* a, s32* b, u8 p)
|
|
{
|
|
*b = p ? 0 : mod - 1;
|
|
*a += shift;
|
|
|
|
if (mod == 0) {
|
|
*a = 0;
|
|
} else {
|
|
*a = (*a + mod) % mod;
|
|
}
|
|
}
|
|
|
|
void to_range(s32* x, s32* y, s32 size)
|
|
{
|
|
s32 sv = size / 2;
|
|
|
|
if (*y >= size) {
|
|
shift_mod_reset(-sv, size, x, y, true);
|
|
} else if (*y < 0) {
|
|
shift_mod_reset(+sv, size, x, y, false);
|
|
}
|
|
|
|
if (*x >= size) {
|
|
shift_mod_reset(-sv, size, y, x, true);
|
|
} else if (*x < 0) {
|
|
shift_mod_reset(+sv, size, y, x, false);
|
|
}
|
|
}
|
|
|
|
room_t* get_player_current_room(const maze_t* const maze, const player_t* const player)
|
|
{
|
|
|
|
return &(maze->list[maze->size * (player->y) + (player->x)]);
|
|
}
|
|
|
|
room_t* select_random_room_from_list(maze_t* maze)
|
|
{
|
|
|
|
return &(maze->list[rand_value(maze->size * maze->size)]);
|
|
}
|
|
|
|
room_t* select_room_from_list(const maze_t* const maze, s32 x, s32 y)
|
|
{
|
|
to_range(&x, &y, maze->size);
|
|
return &(maze->list[maze->size * y + x]);
|
|
}
|
|
|
|
room_t* get_room_from_idx(const maze_t* const maze, s32 x, s32 y)
|
|
{
|
|
return select_room_from_list(maze, x, y);
|
|
}
|
|
|
|
void add_element_to_room(room_t* const room, const room_elements element)
|
|
{
|
|
|
|
*room |= element;
|
|
}
|
|
|
|
void remove_element_from_room(room_t* const room, const room_elements element)
|
|
{
|
|
*room &= ~(element);
|
|
}
|
|
|
|
void add_part_to_room(room_t* const room, const part_t part)
|
|
{
|
|
remove_element_from_room(room, RPART);
|
|
add_element_to_room(room, (part << PART_MASK) & RPART);
|
|
}
|
|
|
|
void add_slot_to_room(room_t* const room, const part_t slot)
|
|
{
|
|
remove_element_from_room(room, RSLOT);
|
|
add_element_to_room(room, (slot << SLOT_MASK) & RSLOT);
|
|
}
|
|
|
|
void add_element_to_idx(maze_t* const maze, s16 x, s16 y, const room_elements element, const part_t p, const u8 c)
|
|
{
|
|
room_t* room = select_room_from_list(maze, x, y);
|
|
|
|
switch (element) {
|
|
case RPART:
|
|
|
|
if (!check_room_has_element(*room, RPART)) {
|
|
add_part_to_room(room, p);
|
|
}
|
|
|
|
break;
|
|
|
|
case RSLOT:
|
|
if (!check_room_has_element(*room, RSLOT)) {
|
|
if (c)
|
|
++maze->slot_left;
|
|
|
|
add_slot_to_room(room, p);
|
|
}
|
|
|
|
break;
|
|
|
|
default:
|
|
add_element_to_room(room, element);
|
|
break;
|
|
}
|
|
}
|
|
|
|
u8 check_player_has_part(const player_t* const player)
|
|
{
|
|
return player->part > 0;
|
|
}
|
|
|
|
u8 check_room_has_element(room_t room, room_elements element)
|
|
{
|
|
|
|
return (room & element) != 0;
|
|
}
|
|
|
|
part_t get_slot_id(room_t room)
|
|
{
|
|
return (room & RSLOT) >> SLOT_MASK;
|
|
}
|
|
|
|
part_t get_part_id(room_t room)
|
|
{
|
|
return (room & RPART) >> PART_MASK;
|
|
}
|
|
|
|
void add_wall(maze_t* maze, room_wall_t* cur_wall)
|
|
{
|
|
if (cur_wall->b) {
|
|
add_element_to_room(select_room_from_list(maze, cur_wall->x, cur_wall->y), SWALL);
|
|
add_element_to_room(select_room_from_list(maze, cur_wall->x, cur_wall->y + 1), NWALL);
|
|
} else {
|
|
add_element_to_room(select_room_from_list(maze, cur_wall->x, cur_wall->y), EWALL);
|
|
add_element_to_room(select_room_from_list(maze, cur_wall->x + 1, cur_wall->y), WWALL);
|
|
}
|
|
}
|
|
|
|
/*************************************************************************/
|
|
/* MAZE FUNCTIONS */
|
|
/*************************************************************************/
|
|
void free_maze(maze_t* maze)
|
|
{
|
|
free(maze->list);
|
|
free(maze);
|
|
}
|
|
|
|
maze_t* alloc_maze(s16 size)
|
|
{
|
|
maze_t* maze = calloc(1, sizeof(maze_t));
|
|
maze->size = size;
|
|
maze->slot_left = 0;
|
|
maze->level = size;
|
|
maze->list = calloc(size * size, sizeof(room_t));
|
|
|
|
return maze;
|
|
}
|
|
|
|
/*************************************************************************/
|
|
/* WALL FUNCTIONS */
|
|
/*************************************************************************/
|
|
room_wall_t* alloc_wall(s16 x, s16 y, u8 b, room_wall_t* prev)
|
|
{
|
|
room_wall_t* wall = calloc(1, sizeof(room_wall_t));
|
|
wall->x = x;
|
|
wall->y = y;
|
|
wall->b = b;
|
|
wall->prev = prev;
|
|
wall->next = NULL;
|
|
return wall;
|
|
}
|
|
|
|
void free_wall(room_wall_t* wall)
|
|
{
|
|
|
|
free(wall);
|
|
}
|
|
|
|
room_wall_t* build_wall_list(u32 size)
|
|
{
|
|
room_wall_t* head = NULL;
|
|
room_wall_t* cur = head;
|
|
u32 y, x, i;
|
|
|
|
for (y = 0; y < size; ++y) {
|
|
for (x = 0; x < size; ++x) {
|
|
for (i = 0; i < 2; ++i) {
|
|
room_wall_t* next = alloc_wall(x, y, i == 1, cur);
|
|
|
|
if (head == NULL) {
|
|
head = next;
|
|
cur = head;
|
|
} else {
|
|
cur->next = next;
|
|
cur = cur->next;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return head;
|
|
}
|
|
|
|
void free_wall_list(room_wall_t* list)
|
|
{
|
|
room_wall_t* head = list;
|
|
room_wall_t* cur = head;
|
|
room_wall_t* next = NULL;
|
|
|
|
while (cur != NULL) {
|
|
next = cur->next;
|
|
free(cur);
|
|
cur = next;
|
|
}
|
|
}
|
|
|
|
room_wall_t* add_wall_to_wall_list(room_wall_t** list, room_wall_t* wall)
|
|
{
|
|
room_wall_t** head = list;
|
|
|
|
if (*head == NULL) {
|
|
*head = wall;
|
|
} else {
|
|
wall->next = *head;
|
|
*head = wall;
|
|
}
|
|
|
|
return *head;
|
|
}
|
|
|
|
|
|
u8 wall_compare(const room_wall_t* const w1, const room_wall_t* const w2)
|
|
{
|
|
return ((w1->x == w2->x) &&
|
|
(w1->y == w2->y) &&
|
|
(w1->b == w2->b));
|
|
}
|
|
|
|
|
|
|
|
room_wall_t* remove_wall_from_wall_list(room_wall_t* list, room_wall_t * wall)
|
|
{
|
|
room_wall_t* head = list;
|
|
room_wall_t* cur = head;
|
|
room_wall_t* next = head;
|
|
|
|
while (cur != NULL && wall_compare(wall, next)) {
|
|
cur = next;
|
|
next = cur->next;
|
|
|
|
}
|
|
|
|
if (cur == NULL)
|
|
return head;
|
|
|
|
if (wall_compare(wall, next)) {
|
|
if (wall == head) {
|
|
return head->next;
|
|
}
|
|
|
|
cur->next = next->next;
|
|
return head;
|
|
}
|
|
|
|
return head;
|
|
}
|
|
|
|
room_wall_t* select_and_remove_random_wall_from_list(room_wall_t** first, s16 length)
|
|
{
|
|
s16 i = rand_value(length);
|
|
room_wall_t* cur = *first;
|
|
|
|
while (i > 0 && cur->next != NULL) {
|
|
cur = cur->next;
|
|
--i;
|
|
}
|
|
|
|
if (cur == NULL) return NULL;
|
|
|
|
if (cur->prev == NULL) {
|
|
if (cur->next != NULL) {
|
|
cur->next->prev = NULL;
|
|
}
|
|
|
|
*first = cur->next;
|
|
} else {
|
|
cur->prev->next = cur->next;
|
|
}
|
|
|
|
if (cur->next == NULL) {
|
|
if (cur->prev != NULL) {
|
|
cur->prev->next = NULL;
|
|
}
|
|
} else {
|
|
cur->next->prev = cur->prev;
|
|
}
|
|
|
|
return cur;
|
|
}
|
|
|
|
const room_wall_t* get_wall_from_list(const room_wall_t* first, s16 idx)
|
|
{
|
|
s16 i = idx;
|
|
const room_wall_t* cur = first;
|
|
|
|
while (i > 0 && cur->next != NULL) {
|
|
cur = cur->next;
|
|
--i;
|
|
}
|
|
|
|
if (cur == NULL) return NULL;
|
|
|
|
return cur;
|
|
}
|
|
|
|
|
|
room_wall_t* select_and_remove_wall_from_list(room_wall_t** first, s16 idx)
|
|
{
|
|
s16 i = idx;
|
|
room_wall_t* cur = *first;
|
|
|
|
while (i > 0 && cur->next != NULL) {
|
|
cur = cur->next;
|
|
--i;
|
|
}
|
|
|
|
if (cur == NULL) return NULL;
|
|
|
|
if (cur->prev == NULL) {
|
|
if (cur->next != NULL) {
|
|
cur->next->prev = NULL;
|
|
}
|
|
|
|
*first = cur->next;
|
|
} else {
|
|
cur->prev->next = cur->next;
|
|
}
|
|
|
|
if (cur->next == NULL) {
|
|
if (cur->prev != NULL) {
|
|
cur->prev->next = NULL;
|
|
}
|
|
} else {
|
|
cur->next->prev = cur->prev;
|
|
}
|
|
|
|
return cur;
|
|
}
|
|
|
|
/*************************************************************************/
|
|
/* WALL PLACING FUNCTIONS */
|
|
/*************************************************************************/
|
|
#define BMASK 0x1
|
|
|
|
u8 count_open_sides(room_t room)
|
|
{
|
|
return (((room >> 0) & BMASK) +
|
|
((room >> 1) & BMASK) +
|
|
((room >> 2) & BMASK) +
|
|
((room >> 3) & BMASK));
|
|
}
|
|
|
|
|
|
|
|
u8 is_dead_end(room_t room)
|
|
{
|
|
return count_open_sides(room) >= 3;
|
|
/*return (((room & SWALL) && (room & NWALL) && (room & WWALL)) ||
|
|
((room & SWALL) && (room & NWALL) && (room & EWALL)) ||
|
|
((room & SWALL) && (room & WWALL) && (room & EWALL)) ||
|
|
((room & NWALL) && (room & WWALL) && (room & EWALL)));*/
|
|
}
|
|
|
|
void push(s32* stack, s32 item, s64 size, s32* top)
|
|
{
|
|
if (*top >= size - 1) {
|
|
//fprs16f(stderr, "MEMERROR\n");
|
|
return;
|
|
} else {
|
|
++(*top);
|
|
stack[*top] = item;
|
|
}
|
|
}
|
|
|
|
s32 pop(s32* stack, s64 size, s32* top)
|
|
{
|
|
s32 ret = 0;
|
|
|
|
if (size == 0) {
|
|
return ret;
|
|
}
|
|
|
|
if (*top == -1) {
|
|
ret = 0;
|
|
} else {
|
|
ret = stack[*top];
|
|
--(*top);
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
u8 stack_has_elem(s32* stackx, s32* stacky, s32 x, s32 y, s32 topx, s32 topy)
|
|
{
|
|
s32 top = MIN(topx, topy);
|
|
s32 i;
|
|
|
|
for (i = 0; i < top; ++i) {
|
|
if (stackx[i] == x && stacky[i] == y)
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
u8* select_flood_from_list(u8* flood, s64 size, s32 x, s32 y)
|
|
{
|
|
to_range(&x, &y, size);
|
|
return &flood[size * y + x];
|
|
}
|
|
|
|
void flood_fill(maze_t* const maze, const room_wall_t* const cur_wall, u8* flood)
|
|
{
|
|
room_t croom = 0;
|
|
s32 topx = -1, topy = -1;
|
|
s32 x = (maze->size >> 1), y = (maze->size >> 1);
|
|
|
|
s32 xx = x;
|
|
s32 yy = y;
|
|
|
|
s64 size = MAX(64, (maze->size) * (maze->size));
|
|
s32* fstackx = calloc(size, sizeof(s32));
|
|
s32* fstacky = calloc(size, sizeof(s32));
|
|
|
|
push(fstackx, x, size, &topx);
|
|
push(fstacky, y, size, &topy);
|
|
|
|
while (topx > -1 && topy > -1) {
|
|
x = pop(fstackx, size, &topx);
|
|
y = pop(fstacky, size, &topy);
|
|
|
|
if (*select_flood_from_list(flood, maze->size, x, y) == 0) {
|
|
*select_flood_from_list(flood, maze->size, x, y) = 1;
|
|
croom = *select_room_from_list(maze, x, y);
|
|
|
|
if (cur_wall->b) {
|
|
xx = cur_wall->x;
|
|
yy = cur_wall->y;
|
|
to_range(&xx, &yy, maze->size);
|
|
|
|
if (xx == x && yy == y) add_element_to_room(&croom, SWALL);
|
|
|
|
xx = cur_wall->x;
|
|
yy = cur_wall->y + 1;
|
|
to_range(&xx, &yy, maze->size);
|
|
|
|
if (xx == x && yy == y) add_element_to_room(&croom, NWALL);
|
|
} else {
|
|
xx = cur_wall->x;
|
|
yy = cur_wall->y;
|
|
to_range(&xx, &yy, maze->size);
|
|
|
|
if (xx == x && yy == y) add_element_to_room(&croom, EWALL);
|
|
|
|
xx = cur_wall->x + 1;
|
|
yy = cur_wall->y;
|
|
to_range(&xx, &yy, maze->size);
|
|
|
|
if (xx == x && yy == y) add_element_to_room(&croom, WWALL);
|
|
}
|
|
|
|
if (!check_room_has_element(croom, NWALL) &&
|
|
*select_flood_from_list(flood, maze->size, x, y - 1) == 0) {
|
|
xx = x;
|
|
yy = y - 1;
|
|
to_range(&xx, &yy, maze->size);
|
|
|
|
if (!stack_has_elem(fstackx, fstacky, xx, yy, topx, topy)) {
|
|
push(fstackx, xx, size, &topx);
|
|
push(fstacky, yy, size, &topy);
|
|
}
|
|
}
|
|
|
|
if (!check_room_has_element(croom, WWALL) &&
|
|
*select_flood_from_list(flood, maze->size, x - 1, y) == 0) {
|
|
xx = x - 1;
|
|
yy = y;
|
|
to_range(&xx, &yy, maze->size);
|
|
|
|
if (!stack_has_elem(fstackx, fstacky, xx, yy, topx, topy)) {
|
|
push(fstackx, xx, size, &topx);
|
|
push(fstacky, yy, size, &topy);
|
|
}
|
|
}
|
|
|
|
if (!check_room_has_element(croom, SWALL) &&
|
|
*select_flood_from_list(flood, maze->size, x, y + 1) == 0) {
|
|
xx = x;
|
|
yy = y + 1;
|
|
to_range(&xx, &yy, maze->size);
|
|
|
|
if (!stack_has_elem(fstackx, fstacky, xx, yy, topx, topy)) {
|
|
push(fstackx, xx, size, &topx);
|
|
push(fstacky, yy, size, &topy);
|
|
}
|
|
}
|
|
|
|
if (!check_room_has_element(croom, EWALL) &&
|
|
*select_flood_from_list(flood, maze->size, x + 1, y) == 0) {
|
|
xx = x + 1;
|
|
yy = y;
|
|
to_range(&xx, &yy, maze->size);
|
|
|
|
if (!stack_has_elem(fstackx, fstacky, xx, yy, topx, topy)) {
|
|
push(fstackx, xx, size, &topx);
|
|
push(fstacky, yy, size, &topy);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
free(fstackx);
|
|
free(fstacky);
|
|
}
|
|
|
|
void flood_fill_enhanced(maze_t* const maze, const room_wall_t* const cur_wall, const room_wall_t* const any_wall, u8* flood)
|
|
{
|
|
room_t croom = 0;
|
|
s32 topx = -1, topy = -1;
|
|
s32 x = (maze->size >> 1), y = (maze->size >> 1);
|
|
|
|
s32 xx = x;
|
|
s32 yy = y;
|
|
|
|
s64 size = MAX(64, (maze->size) * (maze->size));
|
|
s32* fstackx = calloc(size, sizeof(s32));
|
|
s32* fstacky = calloc(size, sizeof(s32));
|
|
|
|
push(fstackx, x, size, &topx);
|
|
push(fstacky, y, size, &topy);
|
|
|
|
while (topx > -1 && topy > -1) {
|
|
x = pop(fstackx, size, &topx);
|
|
y = pop(fstacky, size, &topy);
|
|
|
|
if (*select_flood_from_list(flood, maze->size, x, y) == 0) {
|
|
*select_flood_from_list(flood, maze->size, x, y) = 1;
|
|
croom = *select_room_from_list(maze, x, y);
|
|
|
|
if (cur_wall->b) {
|
|
xx = cur_wall->x;
|
|
yy = cur_wall->y;
|
|
to_range(&xx, &yy, maze->size);
|
|
|
|
if (xx == x && yy == y) add_element_to_room(&croom, SWALL);
|
|
|
|
xx = cur_wall->x;
|
|
yy = cur_wall->y + 1;
|
|
to_range(&xx, &yy, maze->size);
|
|
|
|
if (xx == x && yy == y) add_element_to_room(&croom, NWALL);
|
|
} else {
|
|
xx = cur_wall->x;
|
|
yy = cur_wall->y;
|
|
to_range(&xx, &yy, maze->size);
|
|
|
|
if (xx == x && yy == y) add_element_to_room(&croom, EWALL);
|
|
|
|
xx = cur_wall->x + 1;
|
|
yy = cur_wall->y;
|
|
to_range(&xx, &yy, maze->size);
|
|
|
|
if (xx == x && yy == y) add_element_to_room(&croom, WWALL);
|
|
}
|
|
|
|
if (any_wall->b) {
|
|
|
|
xx = any_wall->x;
|
|
yy = any_wall->y;
|
|
to_range(&xx, &yy, maze->size);
|
|
|
|
if (xx == x && yy == y) add_element_to_room(&croom, SWALL);
|
|
|
|
|
|
xx = any_wall->x;
|
|
yy = any_wall->y + 1;
|
|
to_range(&xx, &yy, maze->size);
|
|
|
|
if (xx == x && yy == y) add_element_to_room(&croom, NWALL);
|
|
} else {
|
|
xx = any_wall->x;
|
|
yy = any_wall->y;
|
|
to_range(&xx, &yy, maze->size);
|
|
|
|
if (xx == x && yy == y) add_element_to_room(&croom, EWALL);
|
|
|
|
xx = any_wall->x + 1;
|
|
yy = any_wall->y;
|
|
to_range(&xx, &yy, maze->size);
|
|
|
|
if (xx == x && yy == y) add_element_to_room(&croom, WWALL);
|
|
}
|
|
|
|
if (!check_room_has_element(croom, NWALL) &&
|
|
*select_flood_from_list(flood, maze->size, x, y - 1) == 0) {
|
|
xx = x;
|
|
yy = y - 1;
|
|
to_range(&xx, &yy, maze->size);
|
|
|
|
if (!stack_has_elem(fstackx, fstacky, xx, yy, topx, topy)) {
|
|
push(fstackx, xx, size, &topx);
|
|
push(fstacky, yy, size, &topy);
|
|
}
|
|
}
|
|
|
|
if (!check_room_has_element(croom, WWALL) &&
|
|
*select_flood_from_list(flood, maze->size, x - 1, y) == 0) {
|
|
xx = x - 1;
|
|
yy = y;
|
|
to_range(&xx, &yy, maze->size);
|
|
|
|
if (!stack_has_elem(fstackx, fstacky, xx, yy, topx, topy)) {
|
|
push(fstackx, xx, size, &topx);
|
|
push(fstacky, yy, size, &topy);
|
|
}
|
|
}
|
|
|
|
if (!check_room_has_element(croom, SWALL) &&
|
|
*select_flood_from_list(flood, maze->size, x, y + 1) == 0) {
|
|
xx = x;
|
|
yy = y + 1;
|
|
to_range(&xx, &yy, maze->size);
|
|
|
|
if (!stack_has_elem(fstackx, fstacky, xx, yy, topx, topy)) {
|
|
push(fstackx, xx, size, &topx);
|
|
push(fstacky, yy, size, &topy);
|
|
}
|
|
}
|
|
|
|
if (!check_room_has_element(croom, EWALL) &&
|
|
*select_flood_from_list(flood, maze->size, x + 1, y) == 0) {
|
|
xx = x + 1;
|
|
yy = y;
|
|
to_range(&xx, &yy, maze->size);
|
|
|
|
if (!stack_has_elem(fstackx, fstacky, xx, yy, topx, topy)) {
|
|
push(fstackx, xx, size, &topx);
|
|
push(fstacky, yy, size, &topy);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
free(fstackx);
|
|
free(fstacky);
|
|
}
|
|
|
|
|
|
u8 check_wall(maze_t* const maze, const room_wall_t* const cur_wall, const room_wall_t* const orig_wall_list)
|
|
{
|
|
if (maze->size == 0) return true;
|
|
|
|
if (cur_wall->b) {
|
|
if (is_dead_end(*select_room_from_list(maze, cur_wall->x, cur_wall->y) | SWALL)) return false;
|
|
|
|
if (is_dead_end(*select_room_from_list(maze, cur_wall->x, cur_wall->y + 1) | NWALL)) return false;
|
|
} else {
|
|
if (is_dead_end(*select_room_from_list(maze, cur_wall->x, cur_wall->y) | EWALL)) return false;
|
|
|
|
if (is_dead_end(*select_room_from_list(maze, cur_wall->x + 1, cur_wall->y) | WWALL)) return false;
|
|
}
|
|
|
|
|
|
u8* flood = calloc(maze->size * maze->size, sizeof(u8));
|
|
|
|
if (maze->size <= 16) {
|
|
u64 i;
|
|
|
|
for (i = 0; i < maze->size * maze->size * 2; ++i) {//this can be done in parallel
|
|
const room_wall_t * any_wall = get_wall_from_list(orig_wall_list, i);
|
|
|
|
memset(flood, 0, maze->size * maze->size * sizeof(u8));
|
|
|
|
flood_fill_enhanced(maze, cur_wall, any_wall, flood);
|
|
|
|
s8 v = 0;
|
|
u32 y, x;
|
|
|
|
for (y = 0; y < maze->size; ++y) {
|
|
for (x = 0; x < maze->size; ++x) {
|
|
if (x == 0 && y == 0) {
|
|
v = flood[y * maze->size + x];
|
|
} else if (v != flood[y * maze->size + x]) {
|
|
free(flood);
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
memset(flood, 0, maze->size * maze->size * sizeof(u8));
|
|
|
|
flood_fill(maze, cur_wall, flood);
|
|
|
|
s16 v = 0;
|
|
u32 x, y;
|
|
|
|
for (y = 0; y < maze->size; ++y) {
|
|
for (x = 0; x < maze->size; ++x) {
|
|
if (x == 0 && y == 0) {
|
|
v = flood[y * maze->size + x];
|
|
} else if (v != flood[y * maze->size + x]) {
|
|
free(flood);
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
|
|
free(flood);
|
|
return true;
|
|
}
|
|
|
|
/*************************************************************************/
|
|
/* MAZE BUILDING FUNCTIONS */
|
|
/*************************************************************************/
|
|
maze_t* build_maze(maze_t* maze)
|
|
{
|
|
room_wall_t* wall_list = build_wall_list(maze->size);
|
|
room_wall_t* orig_wall_list = build_wall_list(maze->size);
|
|
|
|
room_wall_t* cur_wall = NULL;
|
|
s32 list_size = maze->size * maze->size * 2;
|
|
|
|
while (list_size > 0 && wall_list != NULL) {
|
|
cur_wall = select_and_remove_random_wall_from_list(&wall_list, list_size);
|
|
|
|
if (check_wall(maze, cur_wall, orig_wall_list)) {
|
|
add_wall(maze, cur_wall);
|
|
} else {
|
|
}
|
|
|
|
free_wall(cur_wall);
|
|
--list_size;
|
|
}
|
|
|
|
free_wall_list(orig_wall_list);
|
|
|
|
return maze;
|
|
}
|
|
|
|
void set_room_as_filled(room_t* room)
|
|
{
|
|
|
|
*room = 1;
|
|
}
|
|
|
|
s16 select_random_empty_room(maze_t* room_list, s16 rooms_left)
|
|
{
|
|
s16 i = 0;
|
|
s16 index = rand_value(rooms_left);
|
|
|
|
while (i <= index) {
|
|
if (room_list->list[i] == 1) {
|
|
++index;
|
|
}
|
|
|
|
++i;
|
|
}
|
|
|
|
return index;
|
|
}
|
|
|
|
void add_element_to_random_empty_room(maze_t* empty_room_list, maze_t* maze, s16* rooms_left, room_elements element, part_t id)
|
|
{
|
|
s16 index = select_random_empty_room(empty_room_list, *rooms_left);
|
|
--*rooms_left;
|
|
set_room_as_filled(&empty_room_list->list[index]);
|
|
|
|
switch (element) {
|
|
case RPART:
|
|
add_part_to_room(&maze->list[index], id);
|
|
break;
|
|
|
|
case RSLOT:
|
|
add_slot_to_room(&maze->list[index], id);
|
|
break;
|
|
|
|
default:
|
|
add_element_to_room(&maze->list[index], element);
|
|
break;
|
|
|
|
}
|
|
}
|
|
|
|
maze_t* populate_maze(maze_t* maze)
|
|
{
|
|
maze_t* empty_room_list = alloc_maze(maze->size);
|
|
s16 rooms_left = maze->size * maze->size;
|
|
u32 i;
|
|
|
|
for (i = 0; i < 1; ++i) {
|
|
add_element_to_random_empty_room(empty_room_list, maze, &rooms_left, REXIT, 0);
|
|
}
|
|
|
|
for (i = 0; i < maze->size / 2 && rooms_left > 0; ++i) {
|
|
add_element_to_random_empty_room(empty_room_list, maze, &rooms_left, RPART, i + 1);
|
|
add_element_to_random_empty_room(empty_room_list, maze, &rooms_left, RSLOT, i + 1);
|
|
|
|
if (rooms_left > 0)maze->slot_left++;
|
|
}
|
|
|
|
for (i = 0; i < maze->size / 4 && rooms_left > 0; ++i) {
|
|
add_element_to_random_empty_room(empty_room_list, maze, &rooms_left, RTRAP, 0);
|
|
}
|
|
|
|
free_maze(empty_room_list);
|
|
return maze;
|
|
}
|
|
|
|
maze_t* maze_init(s16 size, u8 pop)
|
|
{
|
|
//return maze_init_wr();
|
|
maze_t* maze = alloc_maze(size);
|
|
|
|
if (maze == NULL || maze->list == NULL) {
|
|
return maze;
|
|
}
|
|
|
|
build_maze(maze);
|
|
|
|
if (pop) {
|
|
populate_maze(maze);
|
|
}
|
|
|
|
|
|
return maze;
|
|
}
|
|
|
|
maze_t* maze_init_wr()
|
|
{
|
|
maze_t* maze = alloc_maze(WR_SIZE);
|
|
|
|
if (maze == NULL || maze->list == NULL) {
|
|
return maze;
|
|
}
|
|
|
|
part_t id = WR_SIZE;
|
|
|
|
maze->list[0] = RTRAP | REXIT;//NWALL | SWALL | EWALL | WWALL;
|
|
add_part_to_room(maze->list, id);
|
|
|
|
add_slot_to_room(maze->list, id);
|
|
|
|
return maze;
|
|
}
|
|
|
|
/*************************************************************************/
|
|
/* ACTION FUNCTIONS */
|
|
/*************************************************************************/
|
|
u8 valid_action(room_t room, action_t action)
|
|
{
|
|
switch (action) {
|
|
case UP:
|
|
if (check_room_has_element(room, NWALL)) return false;
|
|
|
|
return true;
|
|
break;
|
|
|
|
case DOWN:
|
|
if (check_room_has_element(room, SWALL)) return false;
|
|
|
|
return true;
|
|
break;
|
|
|
|
case LEFT:
|
|
if (check_room_has_element(room, WWALL)) return false;
|
|
|
|
return true;
|
|
break;
|
|
|
|
case RIGHT:
|
|
if (check_room_has_element(room, EWALL)) return false;
|
|
|
|
return true;
|
|
break;
|
|
|
|
case INTERACT_PICKUP:
|
|
case INTERACT_DROP:
|
|
if (check_room_has_element(room, RPART) ||
|
|
check_room_has_element(room, RSLOT)) return true;
|
|
|
|
break;
|
|
|
|
case INTERACT_EXIT:
|
|
if (check_room_has_element(room, REXIT)) return true;
|
|
|
|
break;
|
|
|
|
|
|
|
|
break;
|
|
|
|
case TAUNT:
|
|
return true;
|
|
break;
|
|
|
|
case UNKNOWN:
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
|
|
void update_player_room(maze_t* maze, player_t* player)
|
|
{
|
|
//room_t* room = &player->room;
|
|
player->room = *get_player_current_room(maze, player);
|
|
|
|
/*if (check_room_has_element(*room, RTRAP)) {
|
|
if (check_room_has_element(*room, NWALL) && rand_value(2) == 1) {
|
|
remove_element_from_room(room, NWALL);
|
|
}
|
|
|
|
if (check_room_has_element(*room, SWALL) && rand_value(2) == 1) {
|
|
remove_element_from_room(room, SWALL);
|
|
}
|
|
|
|
if (check_room_has_element(*room, WWALL) && rand_value(2) == 1) {
|
|
remove_element_from_room(room, WWALL);
|
|
}
|
|
|
|
if (check_room_has_element(*room, EWALL) && rand_value(2) == 1) {
|
|
remove_element_from_room(room, EWALL);
|
|
}
|
|
}*/
|
|
}
|
|
|
|
void move_player_to_random_room(player_t* player, s16 size)
|
|
{
|
|
player->y = rand_value(size);
|
|
player->x = rand_value(size);
|
|
}
|
|
|
|
u8 apply_action_room(maze_t* maze, player_t* player, action_t action, gs_t* status)
|
|
{
|
|
if (!valid_action(player->room, action)) return false;
|
|
|
|
room_t* room = get_player_current_room(maze, player);
|
|
|
|
switch (action) {
|
|
case UP:
|
|
if (valid_action(*room, UP)) {
|
|
player->y--;
|
|
to_range(&player->x, &player->y, maze->level);
|
|
} else {
|
|
//move_player_to_random_room(game->player, game->maze->size);
|
|
}
|
|
|
|
break;
|
|
|
|
case DOWN:
|
|
if (valid_action(*room, DOWN)) {
|
|
player->y++;
|
|
to_range(&player->x, &player->y, maze->level);
|
|
} else {
|
|
//move_player_to_random_room(game->player, game->maze->size);
|
|
}
|
|
|
|
break;
|
|
|
|
case LEFT:
|
|
if (valid_action(*room, LEFT)) {
|
|
player->x--;
|
|
to_range(&player->x, &player->y, maze->level);
|
|
} else {
|
|
//move_player_to_random_room(game->player, game->maze->size);
|
|
}
|
|
|
|
break;
|
|
|
|
case RIGHT:
|
|
if (valid_action(*room, RIGHT)) {
|
|
player->x++;
|
|
to_range(&player->x, &player->y, maze->level);
|
|
} else {
|
|
//move_player_to_random_room(game->player, game->maze->size);
|
|
}
|
|
|
|
break;
|
|
|
|
case INTERACT_PICKUP:
|
|
if (check_room_has_element(*room, RSLOT) && check_room_has_element(*room, RPART) && get_slot_id(*room) == get_part_id(*room)) {
|
|
|
|
if (player->part <= 0) {
|
|
player->part = get_part_id(*room);
|
|
remove_element_from_room(room, RPART);
|
|
++maze->slot_left;
|
|
}
|
|
} else if (check_room_has_element(*room, RPART)) {
|
|
if (player->part == 0) {
|
|
player->part = get_part_id(*room);
|
|
remove_element_from_room(room, RPART);
|
|
}
|
|
}
|
|
|
|
break;
|
|
|
|
case INTERACT_DROP :
|
|
if (check_room_has_element(*room, RSLOT) && (get_slot_id(*room) == player->part)) {
|
|
|
|
add_part_to_room(room, player->part);
|
|
player->part = 0;
|
|
--maze->slot_left;
|
|
|
|
if (maze->slot_left < 0) maze->slot_left = 0;
|
|
} else if (check_room_has_element(*room, RPART)) {
|
|
if (player->part > 0) {
|
|
//gen_new_to_replace_old
|
|
add_part_to_room(room, player->part);
|
|
player->part = 0;
|
|
}
|
|
} else {
|
|
add_part_to_room(room, player->part);
|
|
player->part = 0;
|
|
}
|
|
|
|
case INTERACT_EXIT :
|
|
|
|
//break;
|
|
if (check_room_has_element(*room, REXIT) && (maze->slot_left == 0)) {
|
|
*status = FINISHED;
|
|
}
|
|
|
|
break;
|
|
|
|
case TAUNT:
|
|
return false;
|
|
break;
|
|
|
|
case UNKNOWN:
|
|
return false;
|
|
break;
|
|
|
|
default:
|
|
return false;
|
|
}
|
|
|
|
update_player_room(maze, player);
|
|
return true;
|
|
}
|
|
|
|
|
|
|
|
u8 apply_action_pos(maze_t* maze, player_t* player, action_t action, gs_t* status)
|
|
{
|
|
|
|
room_t* room = get_player_current_room(maze, player);
|
|
const float32 dx = fixedToFloat(sinLerp(degreesToAngle(player->pos.r)), 12);
|
|
const float32 dy = fixedToFloat(cosLerp(degreesToAngle(player->pos.r)), 12);
|
|
position_t new_pos = player->pos;
|
|
|
|
switch (action) {
|
|
case UP:
|
|
player->taunt = false;
|
|
new_pos.y += dy;
|
|
new_pos.x += dx;
|
|
break;
|
|
|
|
case DOWN:
|
|
player->taunt = false;
|
|
new_pos.y -= dy;
|
|
new_pos.x -= dx;
|
|
break;
|
|
|
|
case LEFT:
|
|
player->taunt = false;
|
|
new_pos.y += dx;
|
|
new_pos.x -= dy;
|
|
break;
|
|
|
|
case RIGHT:
|
|
player->taunt = false;
|
|
new_pos.y -= dx;
|
|
new_pos.x += dy;
|
|
break;
|
|
|
|
case INTERACT_PICKUP:
|
|
player->taunt = false;
|
|
|
|
if (new_pos.x >= DOOR_OFFSET && new_pos.x <= ROOM_SIZE - DOOR_OFFSET &&
|
|
new_pos.y >= DOOR_OFFSET && new_pos.y <= ROOM_SIZE - DOOR_OFFSET && check_room_has_element(*room, RPART) && !check_player_has_part(player)) {
|
|
if (check_room_has_element(*room, RSLOT) && get_slot_id(*room) == get_part_id(*room)) {//pick up completed slot key
|
|
++maze->slot_left;
|
|
}
|
|
|
|
player->part = get_part_id(*room);
|
|
remove_element_from_room(room, RPART);
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
break;
|
|
|
|
case INTERACT_DROP:
|
|
player->taunt = false;
|
|
|
|
if (check_player_has_part(player)) { //if player has a key
|
|
if (check_room_has_element(*room, RSLOT)) {
|
|
if (check_room_has_element(*room, RPART) &&
|
|
(get_slot_id(*room) == get_part_id(*room)) &&
|
|
(get_slot_id(*room) != player->part)) {
|
|
++maze->slot_left;
|
|
} else if (get_slot_id(*room) == player->part) {
|
|
--maze->slot_left;
|
|
|
|
if (maze->slot_left < 0) maze->slot_left = 0;
|
|
}
|
|
}
|
|
|
|
add_part_to_room(room, player->part);
|
|
player->part = 0;
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
break;
|
|
|
|
case INTERACT_EXIT:
|
|
player->taunt = false;
|
|
|
|
if (check_room_has_element(*room, REXIT) && maze->slot_left == 0) {
|
|
*status = FINISHED;
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
|
|
break;
|
|
|
|
case TAUNT:
|
|
player->taunt = !player->taunt;
|
|
return true;
|
|
break;
|
|
|
|
case UNKNOWN:
|
|
return false;
|
|
break;
|
|
|
|
default:
|
|
return false;
|
|
}
|
|
|
|
|
|
room_elements elem = is_door(new_pos.x, new_pos.y, *room);
|
|
|
|
if (is_wall(new_pos.x, new_pos.y, *room)) {
|
|
return false;
|
|
}
|
|
|
|
player->pos.x = new_pos.x;
|
|
player->pos.y = new_pos.y;
|
|
const position_t old_pos = player->pos;
|
|
|
|
switch (elem) {
|
|
case NWALL:
|
|
player->pos.y += ROOM_SIZE - 1.0;
|
|
|
|
if (apply_action_room(maze, player, UP, status)) {
|
|
return true;
|
|
}
|
|
|
|
break;
|
|
|
|
case EWALL:
|
|
player->pos.x -= ROOM_SIZE - 1.0;
|
|
|
|
if (apply_action_room(maze, player, RIGHT, status)) {
|
|
return true;
|
|
}
|
|
|
|
break;
|
|
|
|
case SWALL:
|
|
player->pos.y -= ROOM_SIZE - 1.0;
|
|
|
|
if (apply_action_room(maze, player, DOWN, status)) {
|
|
return true;
|
|
}
|
|
|
|
break;
|
|
|
|
case WWALL:
|
|
player->pos.x += ROOM_SIZE - 1.0;
|
|
|
|
if (apply_action_room(maze, player, LEFT, status)) {
|
|
return true;
|
|
}
|
|
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
|
|
}
|
|
|
|
player->pos.x = old_pos.x;
|
|
player->pos.y = old_pos.y;
|
|
|
|
return false;
|
|
}
|
|
|
|
|
|
u8 is_wall(const s16 x, const s16 y, const room_t room)
|
|
{
|
|
if(x < 0 || x > ROOM_SIZE || y < 0 || y > ROOM_SIZE) return 1;
|
|
|
|
if (!check_room_has_element(room, WWALL) && (x <= 1) && (y >= DOOR_OFFSET && y <= ROOM_SIZE - DOOR_OFFSET)) {
|
|
return 0;
|
|
}
|
|
|
|
if (!check_room_has_element(room, EWALL) && (x >= ROOM_SIZE - 1) && (y >= DOOR_OFFSET && y <= ROOM_SIZE - DOOR_OFFSET)) {
|
|
return 0;
|
|
}
|
|
|
|
if (!check_room_has_element(room, NWALL) && (y <= 1) && (x >= DOOR_OFFSET && x <= ROOM_SIZE - DOOR_OFFSET)) {
|
|
return 0;
|
|
}
|
|
|
|
if (!check_room_has_element(room, SWALL) && (y >= ROOM_SIZE - 1) && (x >= DOOR_OFFSET && x <= ROOM_SIZE - DOOR_OFFSET)) {
|
|
return 0;
|
|
}
|
|
|
|
if (x <= 1 || x >= ROOM_SIZE - 1 || y <= 1 || y >= ROOM_SIZE - 1) {
|
|
return 1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
room_elements is_door(const s16 x, const s16 y, const room_t room)
|
|
{
|
|
if (!check_room_has_element(room, WWALL) && (x <= 0) && (y >= DOOR_OFFSET && y <= ROOM_SIZE - DOOR_OFFSET)) {
|
|
return WWALL;
|
|
}
|
|
|
|
if (!check_room_has_element(room, EWALL) && (x >= ROOM_SIZE) && (y >= DOOR_OFFSET && y <= ROOM_SIZE - DOOR_OFFSET)) {
|
|
return EWALL;
|
|
}
|
|
|
|
if (!check_room_has_element(room, NWALL) && (y <= 0) && (x >= DOOR_OFFSET && x <= ROOM_SIZE - DOOR_OFFSET)) {
|
|
return NWALL;
|
|
}
|
|
|
|
if (!check_room_has_element(room, SWALL) && (y >= ROOM_SIZE) && (x >= DOOR_OFFSET && x <= ROOM_SIZE - DOOR_OFFSET)) {
|
|
return SWALL;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
player_t* player_alloc()
|
|
{
|
|
|
|
return calloc(1, sizeof(player_t));
|
|
}
|
|
|
|
player_t* player_init(player_t* player, char* name, u32 maze_size)
|
|
{
|
|
if (player == NULL) return NULL;
|
|
|
|
if (name == NULL)
|
|
name = "++";
|
|
|
|
move_player_to_random_room(player, maze_size);
|
|
player->room = 0;
|
|
|
|
if (player->name != name)
|
|
strcpy(player->name, name);
|
|
|
|
player->pos.x = (ROOM_SIZE+1) / 2.0 ;
|
|
player->pos.y = (ROOM_SIZE+1) / 2.0 ;
|
|
player->pos.r = 90 * rand_value(3);
|
|
//strcpy(player->name,name);
|
|
player->part = false;
|
|
|
|
return player;
|
|
}
|
|
|
|
game_t* game_alloc()
|
|
{
|
|
return calloc(1, sizeof(game_t));
|
|
}
|
|
|
|
void game_reset(game_t* game, u32 next_lvl, char* name, gs_t* status)
|
|
{
|
|
if (game == NULL) return;
|
|
|
|
u32 prev_lvl = game->maze != NULL ? game->maze->level : MAZE_SIZE;
|
|
|
|
if (game->maze != NULL) free_maze(game->maze);
|
|
|
|
if (prev_lvl * DIFINC > MAX_MAZE_SIZE) {
|
|
prev_lvl = next_lvl;
|
|
|
|
if (prev_lvl) {}
|
|
|
|
return;
|
|
}
|
|
|
|
if (next_lvl <= 1) {
|
|
game->maze = maze_init_wr();
|
|
} else {
|
|
game->maze = maze_init(next_lvl, true);
|
|
}
|
|
|
|
game->maze->level = next_lvl;
|
|
|
|
if (game->player == NULL) game->player = player_alloc();
|
|
|
|
|
|
player_init(game->player, name, game->maze->level);
|
|
*status = RUN;
|
|
}
|
|
|
|
game_t* game_init(game_t* game, u32 dims, char* name, gs_t* status)
|
|
{
|
|
game_reset(game, (dims == 0) ? MAZE_SIZE : dims, name, status);
|
|
return game;
|
|
}
|
|
|
|
void game_end(game_t* game)
|
|
{
|
|
free(game->player);
|
|
free_maze(game->maze);
|
|
free(game);
|
|
}
|
|
|