Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

dungeon generation #72

Open
wants to merge 9 commits into
base: terra
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 27 additions & 0 deletions scripts/underground/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
CC=gcc
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Закомментить

CFLAGS=-g -std=gnu99 -Wall -Wextra -Werror
LDFLAGS=

CAVES_SOURCES=gen_cave.c gen_underground.c
CAVES_OBJECTS=$(CAVES_SOURCES:.c=.o)
CAVES_EXECUTABLE=gencaves

DUNGEON_SOURCES=gen_dungeon.c gen_dungeon_parts.c gen_underground.c
DUNGEON_OBJECTS=$(DUNGEON_SOURCES:.c=.o)
DUNGEON_EXECUTABLE=gendungeon

all: $(CAVES_EXECUTABLE) $(DUNGEON_EXECUTABLE)

$(CAVES_EXECUTABLE): $(CAVES_OBJECTS)
$(CC) $(CAVES_OBJECTS) -o $@ $(LDFLAGS)

$(DUNGEON_EXECUTABLE): $(DUNGEON_OBJECTS)
$(CC) $(DUNGEON_OBJECTS) -o $@ $(LDFLAGS)

.c.o:
$(CC) $(CFLAGS) -c $< -o $@

clean:
rm -f $(CAVES_OBJECTS) $(DUNGEON_OBJECTS)
rm $(CAVES_EXECUTABLE)
rm $(DUNGEON_EXECUTABLE)
220 changes: 220 additions & 0 deletions scripts/underground/gen_cave.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,220 @@
// vim: sw=4 ts=4 et :
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <time.h>
#include "gen_underground.h"

/*
* Recursively encases specified zone in stone.
*/
void encase_zone(level_t *l, coords_t a, int zone) {
if (!VALID_COORDS(l, a)) return;
if (!is_floor(AT(l, a.x, a.y, a.z))) return;
if (l->zone_info.map[a.x + a.y*l->w ] != zone) return;

l->zone_info.map[a.x + a.y*l->w] = 0;
AT(l, a.x, a.y, a.z) = '#';
l->zone_info.zones[zone-1].size--;

if (l->zone_info.zones[zone-1].size == 0) {
// Should also update zone info map values
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is it kind of TODO?

// and remove zone from zone array
return;
}

for (int i = 0; i < 16; i += 2)
encase_zone(l, (coords_t){a.x+dirs[i], a.y+dirs[i+1], a.z}, zone);
}

/*
* Encases all zones that are too small.
*/
void encase_small_zones(level_t *l, int threshold) {
for(size_t i = 0; i < l->zone_info.count; ++i) {
if(l->zone_info.zones[i].size < threshold) {
encase_zone(l, l->zone_info.zones[i].at, i+1);
}
}
}

/*
* Populates zone info by floodfill-testing empty spaces.
*/
void zone_test(level_t *l, int z) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The most disgusting variable name, ever given by developer. 1+l.

int zone;
ssize_t rtm_size = l->w * l->h * sizeof(int);

l->zone_info.count = 0;
l->zone_info.map = realloc(l->zone_info.map, rtm_size);
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Check

memset(l->zone_info.map, 0, rtm_size);

zone = 1;

for(int j = 0; j < l->h; ++j) {
for(int i = 0; i < l->w; ++i) {
if(AT(l, i, j, z) == '.' && l->zone_info.map[i + j*l->w] == 0) {
add_zone(l, (struct coords){i, j, z});
zone_flood_fill(l, (struct coords){i, j, z}, zone++);
}
}
}

}

/*
* Smoothes wall formations at specified z-level.
*/
void smooth_pass(level_t *l, int z, int close_threshold, int open_threshold) {
level_t temp;
temp.map = NULL;

if (z < 0 || z >= l->d) return;

copy_level(l, &temp);

for (int j = 0; j < l->h; ++j ) {
for (int i = 0; i < l->w; ++i) {
if (AT(l, i, j, z) != '#' && AT(l, i, j, z) != '.') continue;
if (i == 0 || j == 0 || i == l->w-1 || j == l->h-1 )
continue;

int c = 0;

for (int k = 0; k < 16; k += 2)
if (is_floor(AT(l, i+dirs[k], j+dirs[k+1], z))) ++c;

if (c >= open_threshold) AT(&temp, i, j, z) = '.';
if (c <= close_threshold) AT(&temp, i, j, z) = '#';
}
}

copy_level(&temp, l);
}

void simple_noise(level_t *l, int wall_rate) {
for (int k = 0; k < l->d; ++k) {
for (int j = 0; j < l->h; ++j ) {
for (int i = 0; i < l->w; ++i) {
if ((rand() % 100) > wall_rate) {
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

s/rand()/random()

AT(l, i, j, k) = '#';
} else {
AT(l, i, j, k) = '.';
}
}
}
}
}

/*
* Returns suitable (random) stair placement on specified z-level.
*/
coords_t place_stairs(level_t *l, int z, char down) {
int try = 1000000;
int x, y;

while (try--) {
x = rand() % l->w;
y = rand() % l->h;
if (AT(l, x, y, z) == '.') {
AT(l, x, y, z) = (down ? '>' : '<');
return (coords_t){ x, y, z };
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is it standard defined method to construct an initialized structure?

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

А точно есть в стандарте?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Есть, это compound literal, который появился в C99.
A postfix expression that consists of a parenthesized type name followed by a brace-enclosed list of initializers is a compound literal. It provides an unnamed object whose value is given by the initializer list.

}
}

// If everything is walled, let's forcibly make a staircase
for (int k = 0; k < 8; ++k) {
AT(l, x+dirs[2*k], y+dirs[2*k+1], z) = '.';
}
AT(l, x,y,z) = down ? '>' : '<';
return (coords_t){ x, y, z};
}

/*
* Builds a corridor between two positions.
*/
int do_corridor(level_t *l, coords_t a, coords_t b) {
int tx = a.x, ty = a.y;
int dx, dy;
if (!VALID_COORDS(l, a) || !VALID_COORDS(l, b))
return 1;

// 2d corridors only
if (a.z != b.z || (a.x == b.x && a.y == b.y))
return 2;

dx = b.x - a.x > 0 ? 1 : -1;
dy = b.y - a.y > 0 ? 1 : -1;

while (tx != b.x || ty != b.y) {
if (abs(tx - b.x) >= abs(ty - b.y)) {
tx += dx;
} else {
ty += dy;
}

if (!is_floor(AT(l, tx, ty, a.z))) {
AT(l, tx, ty, a.z) = '.';
}
}

return 0;
}

/*
* Generates a cave system at specified z-level.
*/
int gen_cave(level_t *l, int z, coords_t stairs_pos, int openness) {
coords_t downstairs;

if (!VALID_COORDS(l, stairs_pos)) return 1;

simple_noise(l, 40);

for (int k = z; k < l->d; ++k) {
AT(l, stairs_pos.x, stairs_pos.y, k) = '<';
smooth_pass(l, k, 1, 4);

zone_test(l, k);
encase_small_zones(l, 50);

downstairs = place_stairs(l, z, 1);

if (ZONE_AT(l, stairs_pos.x, stairs_pos.y)
!= ZONE_AT(l, downstairs.x, downstairs.y)) {
do_corridor(l, stairs_pos, downstairs);
}

for (int i = 0; i < openness; ++i) {
smooth_pass(l, k, 2, 5);
}

stairs_pos = downstairs;
stairs_pos.z = k + 1;
}

return 0;
}

int main(int argc, char* argv[]) {
(void)argc;
(void)argv;
int err;
level_t *l = read_level(0);

srand(time(NULL));

coords_t upstairs = (coords_t) {
rand() % l->w,
rand() % l->h,
0
};

if ((err = gen_cave(l, 0, upstairs, 2))) {
fprintf(stderr, "failed to generate caves");
return err;
}
write_level(l, 1);
return EXIT_SUCCESS;
}
Loading