563 lines
15 KiB
C
563 lines
15 KiB
C
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
|
|
#define MAX_SIZE 3000
|
|
#define MAX(x,y) ((x)>(y)?(x):(y))
|
|
|
|
#define BASE_ALT -100
|
|
|
|
/* make this a global to avoid stack overflow */
|
|
int altitude[MAX_SIZE][MAX_SIZE];
|
|
|
|
/* This function writes out the crossfire maps. So shoot me for
|
|
* using compiled in constants - I'm not going to use this so much
|
|
* that I wanted to do anything too easy.
|
|
*/
|
|
|
|
#define MAP_FORMAT "world_%03d_%03d"
|
|
|
|
/* Maps are square */
|
|
|
|
#define MAP_SIZE 50
|
|
|
|
/* There will be a total of 2500 maps (eek) - 50 in
|
|
* each direction. startx and starty are where to start
|
|
* numbering from. I chose 100 for a few reasons - 1) it
|
|
* gives room to the left and above to add some things (another
|
|
* continent for that matter), and 2) since the format allows
|
|
* for up to 1000 in each direction, this seemed reasonable.
|
|
* Note - if you do the math, and have 1000 * 1000 maps, each
|
|
* with 50*50 spaces, you have a total of 2.5 billion spaces.
|
|
* So hopefully that should be large enough.
|
|
*/
|
|
|
|
int startx=100, starty=100;
|
|
|
|
typedef enum {
|
|
None=0,
|
|
DeepWater=1,
|
|
MediumWater=2,
|
|
ShallowWater=3,
|
|
Swamp=4,
|
|
DeepSwamp=5,
|
|
Grass=6,
|
|
Desert=7,
|
|
Brush=8,
|
|
EverGreens=9,
|
|
Jungle=10,
|
|
Tree1=11,
|
|
Tree2=12,
|
|
Woods1=13,
|
|
Woods2=14,
|
|
Woods3=15,
|
|
Hills=16,
|
|
HillsRocky=17,
|
|
Steppe=18,
|
|
Mountain=19,
|
|
HighMountain=20,
|
|
WasteLand=21, /*Now just higher mountain */
|
|
Beach = 22,
|
|
VeryHighMountain=23
|
|
} Terrain_Types;
|
|
|
|
char *Terrain_Names[][2] = {
|
|
/* these are the archetype names. They are in the same order
|
|
* as the Terrain_Types above. Note many terrain types are not
|
|
* included because handling them would be too difficult.
|
|
*/
|
|
{"None", "0, 0, 0 "},
|
|
{"deep_sea", "0 0 127 "},
|
|
{"sea", "0 0 192 "},
|
|
{"shallow_sea", "0 0 255 "}, /* wading depth */
|
|
{"swamp", "12 161 64 "},
|
|
{"deep_swamp", "155 175 164 "},
|
|
{"grass", "0 255 0 "},
|
|
{"desert", "222 218 135 "},
|
|
{"brush", "1 144 1 "},
|
|
{"evergreens", "0 128 0 "},
|
|
{"jungle_1", "0 176 0 "},
|
|
{"tree", "4 133 01 "},
|
|
{"evergreen", "20 209 0 "},
|
|
{"woods", "4 115 01 "},
|
|
{"woods_2", "1 182 02 "},
|
|
{"woods_3", "4 153 02 "},
|
|
{"hills", "166 160 70 "},
|
|
{"hills_rocky", "166 155 70 "},
|
|
{"steppe", "150 97 34 "},
|
|
{"mountain", "183 190 190 "},
|
|
{"mountain2", "191 196 185 "},
|
|
{"mountain4", "215 215 215 "},
|
|
/* Maybe need to put in order? */
|
|
{"beach", "232 228 165 "},
|
|
{"mountain5", "255 255 255 "},
|
|
};
|
|
|
|
|
|
|
|
void write_crossfire_maps(int mapy, int mapx)
|
|
{
|
|
int x, y,n,q, nx, ny,r1,r2,ax=0,ay=0, j, k;
|
|
char name[512];
|
|
FILE *fp;
|
|
Terrain_Types *terrain;
|
|
|
|
terrain = calloc(mapx * mapy * sizeof(Terrain_Types), sizeof(Terrain_Types));
|
|
|
|
/* First fill in the water and the highest of peaks */
|
|
for (x=0; x<mapx; x++) {
|
|
for (y=0; y<mapy; y++) {
|
|
if (altitude[y][x] < -5000) {
|
|
terrain[x + y * mapx] = DeepWater;
|
|
} else if (altitude[y][x] < -99) {
|
|
terrain[x + y * mapx] = MediumWater;
|
|
} else if (altitude[y][x] < 1) {
|
|
terrain[x + y * mapx] = ShallowWater;
|
|
} else if (altitude[y][x] >=25000) {
|
|
terrain[x + y * mapx] = VeryHighMountain;
|
|
}
|
|
}
|
|
}
|
|
/* Basically, take a random bit and populate the area with terrain.
|
|
* We do this so it won't be totally monolithic (have several forest types
|
|
* for example), yet patches will be the same thing, eg, a stretch of
|
|
* desert, which wouldn't work very well if we just chose randomly
|
|
* for each space.
|
|
*/
|
|
|
|
for (n=0; n<(mapx * mapy) / 100; n++) {
|
|
do {
|
|
x = random() % mapx;
|
|
y = random() % mapy;
|
|
} while ( terrain[x + y * mapx] == None);
|
|
|
|
nx = x + 40;
|
|
if (nx > mapx) nx=mapx;
|
|
ny = y + 40;
|
|
if (ny > mapy) ny = mapy;
|
|
r1 = random();
|
|
r2 = random();
|
|
for (x = nx-40; x<nx; x++) {
|
|
for (y=ny-40; y<ny; y++) {
|
|
if (terrain[x + y * mapx] != None) continue;
|
|
|
|
/* near the edges, don't always fill in so that hopefully something
|
|
* else will fill in and smooth these out some
|
|
*/
|
|
if ( (x < (nx -30) || y < (ny - 30) || x > (nx -10) || y > (ny - 10)) &&
|
|
random() % 2) continue;
|
|
|
|
if (altitude[y][x] < 10) {
|
|
terrain[x + y * mapx] = Swamp + (r1 % 2);
|
|
}
|
|
else if (altitude[y][x] < 75){
|
|
terrain[x + y * mapx] = Beach;
|
|
}
|
|
else if (altitude[y][x] < 1000) {
|
|
terrain[x + y * mapx] = Grass + (r1 % 3);
|
|
} else if (altitude[y][x] < 3000) {
|
|
terrain[x + y * mapx] = EverGreens + (r1 % 9);
|
|
} else if (altitude[y][x] < 5000) {
|
|
terrain[x + y * mapx] = Hills + (r2 % 3);
|
|
} else if (altitude[y][x] < 9000) {
|
|
terrain[x + y * mapx] = Mountain;
|
|
} else if (altitude[y][x] < 12000) {
|
|
terrain[x + y * mapx] = HighMountain;
|
|
} else if (altitude[y][x] < 25000) {
|
|
/* Not really precisely wasteland, but wastelands are impassable */
|
|
terrain[x + y * mapx] = WasteLand;
|
|
}
|
|
else fprintf(stderr,"altitude %d did not get filled in?\n", altitude[y][x]);
|
|
}
|
|
}
|
|
}
|
|
/* Now just fill in the spaces randomly. */
|
|
n=0;
|
|
r1 = random();
|
|
r2 = random();
|
|
for (x=0; x<mapx; x++) {
|
|
for (y=0; y<mapy; y++) {
|
|
if (terrain[x + y * mapx] != None) continue;
|
|
n++;
|
|
if (altitude[y][x] < 10) {
|
|
terrain[x + y * mapx] = Swamp + (r1 % 2);
|
|
}
|
|
else if (altitude[y][x] < 75){
|
|
terrain[x + y * mapx] = Beach;
|
|
}
|
|
else if (altitude[y][x] < 1000) {
|
|
terrain[x + y * mapx] = Grass + (r1 % 3);
|
|
} else if (altitude[y][x] < 3000) {
|
|
terrain[x + y * mapx] = EverGreens + (r2 % 9);
|
|
} else if (altitude[y][x] < 5000) {
|
|
terrain[x + y * mapx] = Hills + (r2 % 3);
|
|
} else if (altitude[y][x] < 9000) {
|
|
terrain[x + y * mapx] = Mountain;
|
|
} else if (altitude[y][x] < 12000) {
|
|
terrain[x + y * mapx] = HighMountain;
|
|
} else if (altitude[y][x] < 25000) {
|
|
terrain[x + y * mapx] = WasteLand;
|
|
}
|
|
}
|
|
}
|
|
fprintf(stderr,"Filled in %d spaces\n",n);
|
|
if ((mapx / MAP_SIZE) * MAP_SIZE != mapx ||
|
|
(mapy / MAP_SIZE) * MAP_SIZE != mapy) {
|
|
fprintf(stderr,"Warning - generated map does not evenly tile.\n");
|
|
}
|
|
for (nx= startx; nx<(startx + (mapx/ MAP_SIZE)); nx++) {
|
|
for (ny= starty; ny<(starty + (mapy/ MAP_SIZE)); ny++) {
|
|
ax = (nx - startx) * MAP_SIZE;
|
|
ay = (ny - starty) * MAP_SIZE;
|
|
|
|
sprintf(name,MAP_FORMAT,nx,ny);
|
|
if ((fp=fopen(name, "w"))==NULL) {
|
|
fprintf(stderr,"unable to open %s\n", name);
|
|
}
|
|
/* Write the header for the map */
|
|
fprintf(fp,"arch map\n");
|
|
fprintf(fp,"name %s\n", name);
|
|
fprintf(fp,"width %d\n", MAP_SIZE);
|
|
fprintf(fp,"height %d\n", MAP_SIZE);
|
|
/* Not used right now, but useful to include */
|
|
fprintf(fp,"outdoor 1\n");
|
|
|
|
/* don't do difficult, reset time, or enter coordinates */
|
|
/* Set up the tile paths */
|
|
if (ny != starty) {
|
|
fprintf(fp,"tile_path_1 ");
|
|
fprintf(fp,MAP_FORMAT,nx, ny-1);
|
|
fprintf(fp,"\n");
|
|
}
|
|
if ((nx+1) < startx + (mapx/ MAP_SIZE)) {
|
|
fprintf(fp,"tile_path_2 ");
|
|
fprintf(fp,MAP_FORMAT,nx+1, ny);
|
|
fprintf(fp,"\n");
|
|
}
|
|
if ((ny+1) < starty + (mapy/ MAP_SIZE)) {
|
|
fprintf(fp,"tile_path_3 ");
|
|
fprintf(fp,MAP_FORMAT,nx, ny+1);
|
|
fprintf(fp,"\n");
|
|
}
|
|
if (nx != startx) {
|
|
fprintf(fp,"tile_path_4 ");
|
|
fprintf(fp,MAP_FORMAT,nx-1, ny);
|
|
fprintf(fp,"\n");
|
|
}
|
|
fprintf(fp,"end\n");
|
|
for (x = 0; x<50; x++) {
|
|
for (y = 0; y < 50; y++) {
|
|
q = terrain[x + ax + (y + ay)* mapx];
|
|
fprintf(fp, "arch %s\n",Terrain_Names[q][0]);
|
|
fprintf(fp,"x %d\n", x);
|
|
fprintf(fp,"y %d\n", y);
|
|
q = altitude[y + ay ][x + ax];
|
|
if (q< -32000) q = -32000;
|
|
if (q > 32000) q = 32000;
|
|
fprintf(fp,"elevation %d\n", q);
|
|
fprintf(fp,"end\n");
|
|
}
|
|
}
|
|
fclose(fp);
|
|
}
|
|
}
|
|
|
|
fp = fopen("cmap", "w");
|
|
fprintf(fp, "P3 %d %d 255\n", mapy, mapx);
|
|
for (y=0; y < mapy; y++) {
|
|
for (x=0; x < mapx; x++) {
|
|
fprintf(fp, "%s", Terrain_Names[terrain[x + y * mapx]][1]);
|
|
}
|
|
fprintf(fp, "\n");
|
|
}
|
|
exit(0);
|
|
}
|
|
|
|
|
|
|
|
main(int argc, char *argv)
|
|
{
|
|
int x, y, max_x=500, max_y=500, seed, land=300000, npasses=40, newalt, wpasses=50, water=50000;
|
|
int n, i, j, k, l, z, w, r, a, write_maps=0;
|
|
FILE *fp, *lp;
|
|
int junk;
|
|
char c;
|
|
extern char *optarg;
|
|
|
|
seed = time(NULL);
|
|
while ((c = getopt(argc, argv,"x:y:X:Y:s:l:n:w:p:m"))!=-1) {
|
|
switch (c) {
|
|
case 'l':
|
|
land = atoi(optarg);
|
|
if (land < 11 ) {
|
|
fprintf(stderr,"-l must be at least 11\n");
|
|
exit(1);
|
|
}
|
|
break;
|
|
|
|
case 'w':
|
|
water = atoi(optarg);
|
|
if (water < 1 ) {
|
|
fprintf(stderr,"-w must be at least 1\n");
|
|
exit(1);
|
|
}
|
|
break;
|
|
|
|
case 'p':
|
|
wpasses = atoi(optarg);
|
|
if (wpasses < 1 ) {
|
|
fprintf(stderr,"-w must be at least 1\n");
|
|
exit(1);
|
|
}
|
|
break;
|
|
|
|
case 'n':
|
|
npasses = atoi(optarg);
|
|
if (npasses < 10 ) {
|
|
fprintf(stderr,"-n must be at least 10\n");
|
|
exit(1);
|
|
}
|
|
break;
|
|
|
|
case 'x':
|
|
max_x = atoi(optarg);
|
|
break;
|
|
|
|
case 'y':
|
|
max_y = atoi(optarg);
|
|
break;
|
|
|
|
case 'X':
|
|
startx = atoi(optarg);
|
|
|
|
case 'Y':
|
|
starty = atoi(optarg);
|
|
break;
|
|
|
|
case 's':
|
|
seed = atoi(optarg);
|
|
break;
|
|
|
|
case 'm':
|
|
write_maps=1;
|
|
break;
|
|
}
|
|
}
|
|
if (max_x > MAX_SIZE || max_y > MAX_SIZE) {
|
|
fprintf(stderr,"Max X and Y size is %d\n", MAX_SIZE);
|
|
exit(1);
|
|
}
|
|
|
|
fprintf(stderr,"Making %d X %d map, seed %d, land %d, passes = %d\n", max_x, max_y, seed, land, npasses);
|
|
fprintf(stderr,"wpasses =%d, water=%d\n", wpasses, water);
|
|
fprintf(stderr,"-x %d -y %d -X %d -Y %d -s %d -p %d -n %d -w %d -l %d\n",
|
|
max_x, max_y, startx, starty, seed, wpasses, npasses, water, land);
|
|
|
|
srandom(seed);
|
|
|
|
for (x=20; x < max_x-20; x++)
|
|
for (y=20; y < max_y-20; y++)
|
|
altitude[x][y] = BASE_ALT;
|
|
|
|
for (x=0; x<max_x; x++) {
|
|
for (y=0; y<20; y++) {
|
|
altitude[x][y] = (y -20 ) * 100;
|
|
altitude[x][max_y - y] = (y -20 ) * 100;
|
|
}
|
|
}
|
|
|
|
for (y=10; y<max_y-10; y++) {
|
|
for (x=0; x<20; x++) {
|
|
altitude[x][y] = (x - 20) * 100;
|
|
altitude[max_x - x][y] = (x - 20) * 100;
|
|
}
|
|
}
|
|
|
|
/* This basically produces areas of high variance (eg, islands, peaks, valleys, etc) */
|
|
|
|
for (l=0; l<npasses; l++) {
|
|
x = random()%max_x;
|
|
y = random()%max_y;
|
|
/* Weigh our selected starting position a little more towards the center
|
|
* so the continent is more in the center
|
|
*/
|
|
if (random() % 2) {
|
|
x += random()%max_x;
|
|
y += random()%max_y;
|
|
x /=2;
|
|
y /=2;
|
|
}
|
|
n = random()%500+800;
|
|
|
|
/* For some portion, try to find a pixel we have yet to modify */
|
|
if (l> (npasses * 15) / 20) {
|
|
int tries=0;
|
|
while (altitude[x][y] == BASE_ALT) {
|
|
x = random()%max_x;
|
|
y = random()%max_y;
|
|
if (random() % 2) {
|
|
x += random()%max_x;
|
|
y += random()%max_y;
|
|
x /=2;
|
|
y /=2;
|
|
}
|
|
tries++;
|
|
if (tries > 50) {
|
|
fprintf(stderr,"did not find free space within %d tries\n", tries);
|
|
break;
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
for (k=1; k< land ; k++) {
|
|
r = random()%4;
|
|
switch (r) {
|
|
case 0: if (x<max_x-1) x++; else x -= random() % (max_x/2); break;
|
|
case 1: if (y<max_y-1) y++; else y -= random() % (max_y/2); break;
|
|
case 2: if (x) x--; else x+= random() % (max_x/2); break;
|
|
case 3: if (y) y--; else y+= random() % (max_y/2); break;
|
|
}
|
|
altitude[x][y] += n;
|
|
if (random()%k < 100)
|
|
n -= 1;
|
|
}
|
|
}
|
|
|
|
/* Make lakes and ocean trenches.
|
|
* General note - it works better to have more passes, but each
|
|
* pass doing less working - this results in more consistent lakes
|
|
* and ocean trenching.
|
|
*/
|
|
for (l=0; l<wpasses; l++) {
|
|
/* for a small portion, we lower the area we make */
|
|
n = random()%1500-2000;
|
|
|
|
x = random()% max_x;
|
|
y = random()% max_y;
|
|
|
|
while (altitude[x][y] > BASE_ALT || altitude[x][y]<-7000) {
|
|
x = random()% max_x;
|
|
y = random()% max_y;
|
|
}
|
|
for (k=1; k< water ; k++) {
|
|
r = random()%4;
|
|
switch (r) {
|
|
case 0: if (x<max_x-1) x++; break;
|
|
case 1: if (y<max_y-1) y++; break;
|
|
case 2: if (x) x--; break;
|
|
case 3: if (y) y--; break;
|
|
}
|
|
altitude[x][y] += n;
|
|
if (random()%k < 100)
|
|
n += 1; /*less dramatic as things go on */
|
|
}
|
|
}
|
|
|
|
|
|
/* This block seems to average out the spaces somewhat to prevent
|
|
* cliffs and the like.
|
|
*/
|
|
#define NUM_PASSES 3
|
|
r = 10;
|
|
for (k=0; k<NUM_PASSES; k++) {
|
|
for (x=2; x<max_x-2; x++) {
|
|
for (y=2; y<max_y - 2; y++) {
|
|
newalt = (altitude[x][y] * r + altitude[x-1][y] +
|
|
altitude[x][y-1] + altitude[x-1][y-1] +
|
|
altitude[x+1][y] + altitude[x][y+1] +
|
|
altitude[x+1][y+1] + altitude[x+1][y-1] +
|
|
altitude[x-1][y+1]) / (r+8);
|
|
if (altitude[x][y] < 10 || altitude[x][y] > newalt) altitude[x][y] = newalt;
|
|
}
|
|
}
|
|
for (x=max_x-2; x>2; x--) {
|
|
for (y=max_y-2; y>2; y--) {
|
|
newalt = (altitude[x][y] * r + altitude[x-1][y] +
|
|
altitude[x][y-1] + altitude[x-1][y-1] +
|
|
altitude[x+1][y] + altitude[x][y+1] +
|
|
altitude[x+1][y+1] + altitude[x+1][y-1] +
|
|
altitude[x-1][y+1]) / (r+8);
|
|
if (altitude[x][y] < 10 || altitude[x][y] > newalt) altitude[x][y] = newalt;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Make this 100 so that we eliminate/reduce the lakiness of
|
|
* the map that is otherwise generated - otherwise, the map
|
|
* looks like an archipelago
|
|
*/
|
|
#define AVG_PT -10
|
|
|
|
/* water - does the same as above, but try to more equally balance the spaces*/
|
|
r = 1;
|
|
for (k=0; k<40; k++) {
|
|
for (x=2; x<max_x-2; x++) {
|
|
for (y=2; y<max_y -2; y++) {
|
|
if (altitude[x][y] < AVG_PT)
|
|
altitude[x][y] = (altitude[x][y] * r + altitude[x-1][y] +
|
|
altitude[x][y-1] + altitude[x-1][y-1] +
|
|
altitude[x+1][y] + altitude[x][y+1] +
|
|
altitude[x+1][y+1] + altitude[x+1][y-1] +
|
|
altitude[x-1][y+1]) / (r+8);
|
|
}
|
|
}
|
|
for (x=max_x-2; x>2; x--) {
|
|
for (y=max_y-2; y>2; y--) {
|
|
if (altitude[x][y] < AVG_PT)
|
|
altitude[x][y] = (altitude[x][y] * r + altitude[x-1][y] +
|
|
altitude[x][y-1] + altitude[x-1][y-1] +
|
|
altitude[x+1][y] + altitude[x][y+1] +
|
|
altitude[x+1][y+1] + altitude[x+1][y-1] +
|
|
altitude[x-1][y+1]) / (r+8);
|
|
}
|
|
}
|
|
}
|
|
if (write_maps)
|
|
write_crossfire_maps(max_x, max_y);
|
|
|
|
/* Now write the data out */
|
|
|
|
fp = fopen("lmap", "w");
|
|
lp = fopen("pmap", "w");
|
|
fprintf(fp, "P3 %d %d 255\n", max_y, max_x);
|
|
for (j=0; j < max_x; j++) {
|
|
for (k=0; k < max_y; k++) {
|
|
junk = altitude[j][k];
|
|
fprintf(lp, "%d ", altitude[j][k]);
|
|
if (junk < -5000)
|
|
fprintf(fp, "0 0 127 ");
|
|
/* Shallow water should really be just at the coastal
|
|
* area, so put a big gap between shallow and deep.
|
|
* this also evens out the occurrence of the different types
|
|
* to be more equal
|
|
*/
|
|
else if (junk < -99)
|
|
fprintf(fp, "0 0 192 ");
|
|
else if (junk < 1)
|
|
fprintf(fp, "0 0 255 ");
|
|
else if (junk < 1000)
|
|
fprintf(fp, "0 240 0 ");
|
|
else if (junk < 2000)
|
|
fprintf(fp, "0 220 0 ");
|
|
else if (junk < 3000)
|
|
fprintf(fp, "0 200 0 ");
|
|
else if (junk < 4000)
|
|
fprintf(fp, "0 180 0 ");
|
|
else if (junk < 5000)
|
|
fprintf(fp, "0 160 0 ");
|
|
else if (junk < 6000)
|
|
fprintf(fp, "255 130 71 ");
|
|
else if (junk < 8000)
|
|
fprintf(fp, "238 121 66 ");
|
|
else if (junk < 10000)
|
|
fprintf(fp, "205 104 57 ");
|
|
else if (junk < 12000)
|
|
fprintf(fp, "139 71 38 ");
|
|
else
|
|
fprintf(fp, "255 255 255 ");
|
|
}
|
|
fprintf(fp, "\n");
|
|
}
|
|
exit(0);
|
|
}
|