#include <stdint.h> #include <stdlib.h> #include "character.h" #include "dungeon.h" #include "util.h" void init_dungeon(dungeon_t *d) { int i, j; for (i = 0; i < MAP_HEIGHT; i++) { for (j = 0; j < MAP_WIDTH; j++) { d->characters[i][j] = NULL; } } } void gen_random_dungeon(dungeon_t* d) { init_dungeon_hardness(d); // Generate rooms generate_rooms(d); // Generate corridors generate_corridors(d); // Generate player position // Pick a random room int r = randrange(0, d->room_count-1); d->player_pos.x = randrange(d->room_list[r].x, d->room_list[r].x + d->room_list[r].w - 1); d->player_pos.y = randrange(d->room_list[r].y, d->room_list[r].y + d->room_list[r].h - 1); // Generate stairs generate_stairs(d); // Create the pc in the character array d->characters[d->player_pos.y][d->player_pos.x] = generate_pc(d->player_pos.x, d->player_pos.y); } void gen_monsters(dungeon_t *d, int nummon) { int open_cells = 0; int i, j, k; // Count the open cells for (i = 0; i < MAP_HEIGHT; i++) { for (j = 0; j < MAP_WIDTH; j++) { if (d->hardness[i][j] == 0 && d->characters[i][j] == NULL) { open_cells++; } } } // Record the positions of the open cells position_t *monster_positions = malloc(open_cells * sizeof(position_t)); k = 0; for (i = 0; i < MAP_HEIGHT; i++) { for (j = 0; j < MAP_WIDTH; j++) { if (d->hardness[i][j] == 0 && d->characters[i][j] == NULL) { monster_positions[k].x = j; monster_positions[k].y = i; k++; } } } // Shuffle the list of open cells for (i = 0; i < open_cells; i++) { int swap_index = rand() % open_cells; position_t tmp = monster_positions[i]; monster_positions[i] = monster_positions[swap_index]; monster_positions[swap_index] = tmp; } // Create the monsters for (i = 0; i < nummon && i < open_cells; i++) { int x = monster_positions[i].x; int y = monster_positions[i].y; d->characters[y][x] = generate_monster(x, y); } free(monster_positions); } void init_turn_heap(dungeon_t *d, heap_t *h) { int i, j; init_character_turn_heap(h); for (i = 0; i < MAP_HEIGHT; i++) { for (j = 0; j < MAP_WIDTH; j++) { if (d->characters[i][j]) { heap_insert(h, d->characters[i][j]); } } } } void free_dungeon(dungeon_t* d) { int i, j; free(d->room_list); free(d->upstair_list); free(d->downstair_list); for (i = 0; i < MAP_HEIGHT; i++) { for (j = 0; j < MAP_WIDTH; j++) { if (d->characters[i][j]) { if (d->characters[i][j]->player) { free(d->characters[i][j]->player); } if (d->characters[i][j]->monster) { free(d->characters[i][j]->monster); } free(d->characters[i][j]); } } } } void init_dungeon_hardness(dungeon_t* d) { int i, j; for (i = 0; i < MAP_HEIGHT; i++) { for (j = 0; j < MAP_WIDTH; j++) { if (i == 0 || i == (MAP_HEIGHT - 1) || j == 0 || j == (MAP_WIDTH - 1)) { d->hardness[i][j] = 255; } else { d->hardness[i][j] = randrange(1, 254); } } } } void generate_stairs(dungeon_t* d) { int found_pos, i, j; // Up stairs int num_up_stairs = randrange(1, 2); d->upstair_count = num_up_stairs; d->upstair_list = malloc(num_up_stairs * sizeof(position_t)); for (i = 0; i < num_up_stairs; i++) { found_pos = 0; while (!found_pos) { int x = randrange(1, MAP_WIDTH-2); int y = randrange(1, MAP_HEIGHT-2); if (d->hardness[y][x] == 0) { // This is an open space, let's see if it is already a stair int valid = 1; for (j = 0; j < i; j++) { if (d->upstair_list[j].x == x && d->upstair_list[j].y == y) { valid = 0; } } if (x == d->player_pos.x && y == d->player_pos.y) { valid = 0; } // If not valid, keep trying if (!valid) continue; d->upstair_list[i].x = x; d->upstair_list[i].y = y; found_pos = 1; } } } // Down stairs int num_down_stairs = randrange(1, 2); d->downstair_count = num_down_stairs; d->downstair_list = malloc(num_down_stairs * sizeof(position_t)); for (i = 0; i < num_down_stairs; i++) { found_pos = 0; while (!found_pos) { int x = randrange(1, MAP_WIDTH-2); int y = randrange(1, MAP_HEIGHT-2); if (d->hardness[y][x] == 0) { // This is an open space, let's see if it is already a stair // Have to check both up stairs and down stairs now int valid = 1; for (j = 0; j < d->upstair_count; j++) { if (d->upstair_list[j].x == x && d->upstair_list[j].y == y) { valid = 0; } } for (j = 0; j < i; j++) { if (d->downstair_list[j].x == x && d->downstair_list[j].y == y) { valid = 0; } } if (x == d->player_pos.x && y == d->player_pos.y) { valid = 0; } // If not valid, keep trying if (!valid) continue; d->downstair_list[i].x = x; d->downstair_list[i].y = y; found_pos = 1; } } } } void generate_corridors(dungeon_t* d) { int i, j; int* connected = malloc(d->room_count * sizeof(int)); for (i = 0; i < d->room_count; i++) { connected[i] = 0; } connected[0] = 1; while(1) { // Check if all the rooms are connected int flag = 1; for (i = 0; i < d->room_count; i++) { if (!connected[i]) flag = 0; } if (flag) break; // Find the closest connected/disconnected pair of rooms int minDistFact = 10000; int room1 = 0; int room2 = 0; for (i = 0; i < d->room_count; i++) { if (connected[i]) continue; for (j = 0; j < d->room_count; j++) { if (!connected[j]) continue; // Compare distance, no need to square root since we // don't care about the actual distance int xFact = d->room_list[j].x - d->room_list[i].x; int yFact = d->room_list[j].y - d->room_list[i].y; int distFact = xFact * xFact + yFact * yFact; if (distFact < minDistFact) { minDistFact = distFact; room1 = i; //disconnected room room2 = j; //connected room } } } // Connect the rooms with corridors connect_rooms(d, room1, room2); connected[room1] = 1; } free(connected); } void connect_rooms(dungeon_t* d, int room1, int room2) { int i; // Start position int currX = randrange(d->room_list[room1].x, d->room_list[room1].x + d->room_list[room1].w - 1); int currY = randrange(d->room_list[room1].y, d->room_list[room1].y + d->room_list[room1].h - 1); // End position int targetX = randrange(d->room_list[room2].x, d->room_list[room2].x + d->room_list[room2].w - 1); int targetY = randrange(d->room_list[room2].y, d->room_list[room2].y + d->room_list[room2].h - 1); while(1) { // Where we need to travel to get there int dx = targetX - currX; int dy = targetY - currY; // Select which direction and how far to go if (randchance(0.5)) { dx = 0; dy = sign(dy) * randrange(0, abs(dy)/2+1); } else { dy = 0; dx = sign(dx) * randrange(0, abs(dx)/2+1); } // Number of iterations in this leg of the corridor int dist = abs(dx + dy); // Draw each cell along the way, avoiding rooms // If we cross an existing corridor, exit - it is connected for (i = 0; i < dist; i++) { currX += sign(dx); currY += sign(dy); d->hardness[currY][currX] = 0; } // Once we have reached our target, exit if (currX == targetX && currY == targetY) { return; } } } void generate_rooms(dungeon_t* d) { int i, j, k; int room_eligible[MAP_HEIGHT][MAP_WIDTH]; for (i = 0; i < MAP_HEIGHT; i++) { for (j = 0; j < MAP_WIDTH; j++) { room_eligible[i][j] = !(i == 0 || i == (MAP_HEIGHT-1) || j == 0 || j == (MAP_WIDTH-1)); } } // Choose how many rooms to make int rooms_to_make = randrange(MIN_ROOMS, MAX_ROOMS); // Initialize the dungeon with that information d->room_count = rooms_to_make; d->room_list = malloc(rooms_to_make * sizeof(room_t)); for (k = 0; k < rooms_to_make; k++) { int foundRoom = 0; int x, y, w, h; // Until we have found a valid position for the room while(!foundRoom) { // Generate random parameters for the room w = randrange(4, 10); h = randrange(3, 8); x = randrange(1, MAP_WIDTH - w); y = randrange(1, MAP_HEIGHT - h); // Assume that this is good foundRoom = 1; // Check every cell in the new room to see if it is eligible // If not, set the flag to false so we will try again for (i = y; i < y+h; i++) { for (j = x; j < x+w; j++) { if (!room_eligible[i][j]) foundRoom = 0; } } } // Save the parameters of the room in the array d->room_list[k].x = x; d->room_list[k].y = y; d->room_list[k].w = w; d->room_list[k].h = h; // Mark this room and the border around it as ineligible for room placement for (i = y-1; i < y+h+1; i++) { for (j = x-1; j < x+w+1; j++) { room_eligible[i][j] = 0; } } // Mark the cells in the map as room cells for (i = y; i < y+h; i++) { for (j = x; j < x+w; j++) { d->hardness[i][j] = 0; } } } }