-
Notifications
You must be signed in to change notification settings - Fork 0
/
map.c
493 lines (427 loc) · 13.6 KB
/
map.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
#include <math.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>
#include "color.h"
#include "map.h"
const unsigned int PROPERTY_WALKABLE = 0x01;
const int minFightProperty = 0;
const int maxFightProperty = 10;
/*
* Local functions
*/
int fasterFloor(float value);
float dotproduct(float grad[], float x, float y);
s_color lerp(s_color c1, s_color c2, float value);
unsigned int get_random_int(unsigned int max);
/**
* Creates a s_map instance, set its dimensions and the global size of the grid
*/
s_map initMap(int width, int height, int x, int y)
{
s_map map;
map.width = width;
map.height = height;
map.x = x;
map.y = y;
map.grid = (s_cell*) malloc(height * width * sizeof(s_cell));
return map;
}
/**
* Frees the map's grid
*/
void clearMap(s_map* map)
{
free((*map).grid);
}
/**
* Generate the map using the simplex noise algorithm.
*/
void fillMap(s_map* map)
{
//set up some variables
int octaves = 16;
//these variables are used by the fBm part, but not extensively in the noise part
float pixel_value,
amplitude, frequency,
gain = 0.65f, lacunarity = 2.0f;//physics terms. gain affects the amplitude each octave, lacunarity affects the frequency
//these are all variables used only by the noise function, not the fBm part
float disbx, disby,//distances to the three corners
dismx, dismy,// b = bottom-left corner, m = middle corner, t = top-right corner
distx, disty,
noiseb,noisem,noiset,//noise contributions from the three corners
tempdis,x,y,
skew_value,unskew_value,
general_skew = (sqrt(3.0f)-1.0f)*0.5f, //these two are some complex math to convert between square and simplex space
general_unskew = (3.0f-sqrt(3.0f))/6.0f;
int cornerbx, cornerby,
cornermx, cornermy,
cornertx, cornerty,
gradb, gradm, gradt;//arrays should be used with all of these, but this is easier to read
float min = 100000.0f;
float max = -100000.0f;
//set up the gradient table with 8 equally distributed angles around the unit circle
float gradients[8][2];
int i;
for (i = 0; i < 8; ++i) {
gradients[i][0] = cos(0.785398163f * (float)i);// 0.785398163 is PI/4.
gradients[i][1] = sin(0.785398163f * (float)i);
}
int size = 256;
int permutations[size];
//set up the random numbers table
// make it as long as the largest dimension
for (i = 0; i < size; ++i) {
// put each number in once
permutations[i] = i;
}
//randomize the random numbers table
int j, k;
for (i = 0; i < size; ++i) {
j = get_random_int(size);
k = permutations[i];
permutations[i] = permutations[j];
permutations[j] = k;
}
//for each pixel...
for (j = 0; j < (*map).height; ++j) {
for (i = 0; i < (*map).width; ++i) {
//get the value for this pixel by adding successive layers
amplitude = 1.0f;
frequency = 1.0f / (float) (*map).width;
pixel_value = 0.0f;
// use threads here
for (k = 0; k < octaves; ++k) {
//get the x and y values. These are values from the grid in normal (simplex) space
x = (float)(map->x + i) * frequency;
y = (float)(map->y + j) * frequency;
//get the bottom-left corner of the simplex in skewed space
skew_value = (x + y) * general_skew;
cornerbx = fasterFloor(x + skew_value);
cornerby = fasterFloor(y + skew_value);
//get the distance from the bottom corner in normal (simplex) space
unskew_value = (float)(cornerbx + cornerby) * general_unskew;
disbx = x - (float)cornerbx + unskew_value;
disby = y - (float)cornerby + unskew_value;
//get the middle corner in skewed space
if (disbx > disby) {
cornermx = 1 + cornerbx;// lower triangle
cornermy = cornerby;
}
else {
cornermx = cornerbx;//upper triangle
cornermy = 1 + cornerby;
}
//get the top corner in skewed space
cornertx = 1 + cornerbx;
cornerty = 1 + cornerby;
// get the distance from the other two corners
dismx = disbx - (float)(cornermx - cornerbx) + general_unskew;
dismy = disby - (float)(cornermy - cornerby) + general_unskew;
distx = disbx - 1.0f + 2 * general_unskew;
disty = disby - 1.0f + 2 * general_unskew;
// get the gradients indices
gradb = permutations[(cornerbx + permutations[cornerby & 255]) & 255] & 7;
gradm = permutations[(cornermx + permutations[cornermy & 255]) & 255] & 7;
gradt = permutations[(cornertx + permutations[cornerty & 255]) & 255] & 7;
// get the noise from each corner using an attenuation function
// first the bottom corner
tempdis = 0.5f - disbx * disbx - disby * disby;
if (tempdis < 0.0f) {
noiseb = 0.0f;
}
else {
noiseb = pow(tempdis, 4.0f) * dotproduct(gradients[gradb], disbx, disby);
}
// then the middle corner
tempdis = 0.5f - dismx * dismx - dismy * dismy;
if (tempdis < 0.0f) {
noisem = 0.0f;
}
else {
noisem = pow(tempdis, 4.0f) * dotproduct(gradients[gradm], dismx, dismy);
}
// last the top corner
tempdis = 0.5f - distx * distx - disty * disty;
if (tempdis < 0.0f) {
noiset = 0.0f;
}
else {
noiset = pow(tempdis, 4.0f) * dotproduct(gradients[gradt], distx, disty);
}
// finally, add it in and adjust for the next layer
// notice that no interpolation is needed, just straight summation
pixel_value += (noiseb + noisem + noiset) * amplitude;
amplitude *= gain;
frequency *= lacunarity;
}
// put it in the map
(*map).grid[i + j * (*map).width].x = i;
(*map).grid[i + j * (*map).width].y = j;
(*map).grid[i + j * (*map).width].altitude = pixel_value;
// do some quick checks
if (pixel_value < min) {
min = pixel_value;
}
else if (pixel_value > max) {
max = pixel_value;
}
}
}
// Define the ground types
float diff = max - min;
map->minAltitude = min;
map->maxAltitude = max;
map->floodAltitude = 0.5f * diff;
map->mountAltitude = 0.75f * diff;
map->snowAltitude = 0.9f * diff;
s_cell* current;
for (j = 0; j < (*map).height; ++j) {
for (i = 0; i < (*map).width; ++i) {
int currentIndex = i + j * map->width;
current = &(map->grid[currentIndex]);
current->altitude -= min;
//if this point is below the floodline...
if (current->altitude < map->floodAltitude) {
current->ground_type = GROUND_WATER;
}
//if this is above the mountain line...
else if (current->altitude > map->snowAltitude) {
current->ground_type = GROUND_SNOW;
}
//if this is above the mountain line...
else if (current->altitude > map->mountAltitude) {
current->ground_type = GROUND_MOUNTAIN;
}
//if this is regular land
else {
current->ground_type = GROUND_LAND;
}
}
}
}
/**
* Print the map in a BMP file
*/
int printMap(s_map* map, char* filename, int filename_len) {
int i,j,k;
char bmpfile[filename_len + 4];
strcpy(bmpfile, filename);
strcat(bmpfile, ".bmp");
//these can be changed for interesting results
s_color waterlow, waterhigh, landlow, landhigh, mountlow, mounthigh, snowlow, snowhigh;
waterlow = color(0, 0, 55);
waterhigh = color(0, 53, 106);
landlow = color(0, 64, 0);
landhigh = color(133, 182, 116);
mountlow = color(66, 75, 92);
mounthigh = color(184, 173, 153);
snowlow = color(167, 157, 147);
snowhigh = color(216, 223, 226);
//3.0 output to file
//3.1 Begin the file
//3.1.1 open output file
FILE *bmp;
bmp = fopen(bmpfile, "wb");
if (bmp == NULL){
printf("Target file opening error");
return 1;
}
//3.1.2 copy the header
//3.1.2.1 magic number
fputc((char)66, bmp);
fputc((char)77, bmp);
//~//3.1.2.2 filsize/unused space
for (i = 0; i < 8; i++) {
fputc((char)0, bmp);
}
//~//3.1.2.3 data offset
fputc((char)54, bmp);
//~//3.1.2.4 unused space
for (i = 0; i < 3; i++) {
fputc((char)0, bmp);
}
//~//3.1.2.5 header size
fputc((char)40, bmp);
//~//3.1.2.6 unused space
for (i = 0; i < 3; i++) {
fputc((char)0, bmp);
}
//~//3.1.2.7 file width (trickier)
fputc((char)((*map).width % 256), bmp);
fputc((char)(((*map).width>>8)%256), bmp);
fputc((char)(((*map).width>>16)%256), bmp);
fputc((char)(((*map).width>>24)%256), bmp);
//~//3.1.2.8 file height (trickier)
fputc((char)((*map).height%256), bmp);
fputc((char)(((*map).height>>8)%256), bmp);
fputc((char)(((*map).height>>16)%256), bmp);
fputc((char)(((*map).height>>24)%256), bmp);
//~//3.1.2.9 color planes
fputc((char)1, bmp);
fputc((char)0, bmp);
//~//3.1.2.10 bit depth
fputc((char)24, bmp);
//~//3.1.2.11 the rest
for (i = 0; i < 25; i++) {
fputc((char)0, bmp);
}
float diff = map->maxAltitude - map->minAltitude;
float deltaSnow = diff - map->snowAltitude,
deltaMount = diff - map->mountAltitude,
deltaFlood = diff - map->floodAltitude;
//3.2 put in the elements of the array
for (j = map->height - 1; j >= 0; j--) {//bitmaps start with the bottom row, and work their way up...
for (i = 0; i < map->width; i++) {//...but still go left to right
s_color newcolor = color(0, 0, 0);
int currentIndex = i + j * map->width;
s_cell* current = &(map->grid[currentIndex]);
//if this point is below the floodline...
if (current->ground_type == GROUND_WATER) {
newcolor = lerp(waterlow, waterhigh, current->altitude / map->floodAltitude);
}
//if this is above the mountain line...
else if (current->ground_type == GROUND_SNOW) {
newcolor = lerp(snowlow, snowhigh, (current->altitude - map->snowAltitude) / deltaSnow);
}
//if this is above the mountain line...
else if (current->ground_type == GROUND_MOUNTAIN) {
newcolor = lerp(mountlow, mounthigh, (current->altitude - map->mountAltitude) / deltaMount);
}
//if this is regular land
else {
newcolor = lerp(landlow, landhigh, (current->altitude - map->floodAltitude) / deltaFlood);
}
fputc((char)(newcolor.v[2]), bmp);//blue
fputc((char)(newcolor.v[1]), bmp);//green
fputc((char)(newcolor.v[0]), bmp);//red
}
//round off the row
for (k = 0; k < ((*map).width % 4); k++) {
fputc((char)0, bmp);
}
}
//3.3 end the file
fclose(bmp);
return 0;
}
int exportMapToTiled(s_map* map, char* filename, int filename_len, time_t seed) {
char txtfile[filename_len + 4];
strcpy(txtfile, filename);
strcat(txtfile, ".tmx");
FILE *txt;
txt = fopen(txtfile, "w");
if (txt == NULL) {
printf("Target file opening error");
return 1;
}
printf("Export map\n");
fprintf(txt, "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n");
fprintf(txt, "<!-- seed: %ld -->\n", seed);
// XXX make tile width and height configurable
fprintf(txt, "<map version=\"1.5\" tiledversion=\"1.7.2\" orientation=\"orthogonal\" renderorder=\"right-down\" width=\"%d\" height=\"%d\" tilewidth=\"16\" tileheight=\"16\" infinite=\"0\">\n", map->width, map->height);
fprintf(txt, "<tileset firstgid=\"1\" source=\"tiles.tsx\"/>\n");
fprintf(txt, "<layer id=\"1\" name=\"ground\" width=\"%d\" height=\"%d\">\n", map->width, map->height);
fprintf(txt, "<properties>\n");
fprintf(txt, "<property name=\"Z\" type=\"float\" value=\"0\"/>\n");
fprintf(txt, "</properties>\n");
fprintf(txt, "<data encoding=\"csv\">\n");
for (int j = 0; j < map->height; j++) {
for (int i = 0; i < map->width; i++) {
int currentIndex = i + j * map->width;
s_cell* current = &(map->grid[currentIndex]);
// The ground types in tiled are 1-indexed (so +1 is needed) and the
// 1st (value 0) is reserved for empty tile (so +1 is needed again)
fprintf(txt, "%d", current->ground_type);
if (j < map->height - 1 || i < map->width - 1) {
fprintf(txt, ",");
}
}
fprintf(txt, "\n");
}
fprintf(txt, "</data>\n");
fprintf(txt, "</layer>\n");
printf("Export properties\n");
fprintf(txt, "<layer id=\"2\" name=\"property-walkable\" width=\"%d\" height=\"%d\">\n", map->width, map->height);
fprintf(txt, "<data encoding=\"csv\">\n");
for (int j = 0; j < map->height; j++) {
for (int i = 0; i < map->width; i++) {
int currentIndex = i + j * map->width;
s_cell* current = &(map->grid[currentIndex]);
int properties = 0;
if (current->ground_type != GROUND_WATER) {
properties = 1;
}
fprintf(txt, "%d", properties);
if (j < map->height - 1 || i < map->width - 1) {
fprintf(txt, ",");
}
}
fprintf(txt, "\n");
}
fprintf(txt, "</data>\n");
fprintf(txt, "</layer>\n");
fprintf(txt, "<layer id=\"3\" name=\"property-fightProbability\" width=\"%d\" height=\"%d\">\n", map->width, map->height);
fprintf(txt, "<data encoding=\"csv\">\n");
for (int j = 0; j < map->height; j++) {
for (int i = 0; i < map->width; i++) {
int currentIndex = i + j * map->width;
s_cell* current = &(map->grid[currentIndex]);
int properties = 0;
if (current->ground_type != GROUND_WATER) {
properties = rand() % (maxFightProperty - minFightProperty) + minFightProperty;
}
fprintf(txt, "%d", properties);
if (j < map->height - 1 || i < map->width - 1) {
fprintf(txt, ",");
}
}
fprintf(txt, "\n");
}
fprintf(txt, "</data>\n");
fprintf(txt, "</layer>\n");
fprintf(txt, "</map>\n");
fclose(txt);
return 0;
}
/**
* Faster version of the floor function
*/
int fasterFloor(float value)
{
return value >= 0 ? (int) value : (int) value - 1;
}
/**
* Dotproduct between a gradient value and the current position
*/
float dotproduct(float grad[], float x, float y)
{
return grad[0] * x + grad[1] * y;
}
/**
* Calculate a color result of a linear interpolation from two colors and a
* ratio
*/
s_color lerp(s_color c1, s_color c2, float value)
{
s_color tcolor;
tcolor.v[0] = tcolor.v[1] = tcolor.v[2] = 0;
int g;
for (g = 0; g < 3; g++) {
tcolor.v[g] = MIN(c1.v[g], c2.v[g]) + (unsigned char) abs((float)(c2.v[g] - c1.v[g]) * value);
}
return tcolor;
}
/**
* Generate pseudo random integer between 0 and max
*/
unsigned int get_random_int(unsigned int max)
{
int r;
float s;
r = rand();
s = (float)(r & 0x7fff)/(float)0x7fff;
return (s * max);
}