/** * @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); }