/*
* This file is part of Dune Legacy.
*
* Dune Legacy is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* Dune Legacy is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Dune Legacy. If not, see .
*/
#include
#include
#include
#include "MapSeed.h"
// global seed value
static Uint32 Seed;
// a point that has 2 coordinates
typedef struct {
Uint16 x;
Uint16 y;
} Point;
// some values
const Uint8 BoolArray[] = {0,1,0,0,1,1,1,1,0,0,1,1,1,1,0,0,0,0,1,0,0};
// some offsets
const Sint8 OffsetArray1[]={
0,-1,1,-16,16,
-17,17,-15,15,
-2, 2,-32,32,
-4, 4,-64,64,
-30,30,-34,34
};
// some more offsets
const Uint8 OffsetArray2[]={
0,0,4,0,4,0,4,4,0,0,0,4,0,4,4,4,0,0,0,2,0,
2,0,4,0,0,2,0,2,0,4,0,4,0,4,2,4,2,4,4,0,4,
2,4,2,4,4,4,0,0,4,4,2,0,2,2,0,0,2,2,4,0,2,
2,0,2,2,2,2,2,4,2,2,2,0,4,2,2,4,4,2,2,2,4,
0,0,4,0,4,0,4,4,0,0,0,4,0,4,4,4,0,0,0,2,0,
2,0,4,0,0,2,0,2,0,4,0,4,0,4,2,4,2,4,4,0,4,
2,4,2,4,4,4,4,0,0,4,2,0,2,2,0,0,2,2,4,0,2,
2,0,2,2,2,2,2,4,2,2,2,0,4,2,2,4,4,2,2,2,4
};
// TileTypes
const Sint16 TileTypes[] = {
220, 221, 222, 229, 230, 231, 213, 214, 215, 223, 224, 225, 232, 233, 234, 216,
217, 218, 226, 227, 228, 235, 236, 237, 219, 217, 218, 226, 227, 228, 235, 236,
237, 238, 239, 244, 245, 125, 240, 246, 247, 241, 242, 248, 249, 241, 243, 248,
249, 241, 242, 248, 250, 241, 243, 248, 250, 251, 252, 253, 258, 259, 260, 223,
224, 225, 232, 233, 234, 254, 255, 256, 261, 262, 263, 257, 255, 256, 261, 262,
263, 254, 255, 256, 261, 264, 265, 257, 255, 256, 261, 264, 265, 254, 255, 256,
261, 266, 267, 257, 255, 256, 261, 266, 267, 210, 268, 269, 273, 274, 275, 223,
224, 225, 232, 233, 234, 270, 271, 272, 276, 277, 278, 270, 271, 272, 279, 277,
278, 270, 271, 272, 276, 277, 278, 270, 271, 272, 279, 277, 278, 270, 271, 272,
276, 277, 278, 270, 271, 272, 279, 277, 278, 238, 239, 244, 245, 125, 240, 246,
247, 280, 281, 282, 283, 280, 281, 282, 284, 238, 239, 244, 245, 125, 240, 246,
247, 285, 286, 288, 289, 287, 286, 290, 289, 143, 291, 295, 296, 125, 240, 246,
247, 292, 293, 297, 298, 294, 293, 297, 298, 238, 239, 244, 245, 125, 240, 246,
247, 299, 300, 301, 302, 299, 300, 301, 303, 238, 239, 244, 245, 125, 240, 246,
247, 304, 305, 306, 307, 304, 305, 306, 308, 210, 211, 212, 220, 221, 222, 229,
230, 231, 213, 214, 215, 223, 313, 225, 232, 233, 234, 309, 310, 311, 314, 315,
316, 319, 320, 321, 312, 310, 311, 314, 315, 316, 319, 320, 321, 309, 310, 311
};
// sinus[index] = 127 * sin(pi * index/128)
static const Sint8 sinus[256] = {
0, 3, 6, 9, 12, 15, 18, 21, 24, 27, 30, 33, 36, 39, 42, 45,
48, 51, 54, 57, 59, 62, 65, 67, 70, 73, 75, 78, 80, 82, 85, 87,
89, 91, 94, 96, 98, 100, 101, 103, 105, 107, 108, 110, 111, 113, 114, 116,
117, 118, 119, 120, 121, 122, 123, 123, 124, 125, 125, 126, 126, 126, 126, 126,
127, 126, 126, 126, 126, 126, 125, 125, 124, 123, 123, 122, 121, 120, 119, 118,
117, 116, 114, 113, 112, 110, 108, 107, 105, 103, 102, 100, 98, 96, 94, 91,
89, 87, 85, 82, 80, 78, 75, 73, 70, 67, 65, 62, 59, 57, 54, 51,
48, 45, 42, 39, 36, 33, 30, 27, 24, 21, 18, 15, 12, 9, 6, 3,
0, -3, -6, -9, -12, -15, -18, -21, -24, -27, -30, -33, -36, -39, -42, -45,
-48, -51, -54, -57, -59, -62, -65, -67, -70, -73, -75, -78, -80, -82, -85, -87,
-89, -91, -94, -96, -98, -100, -102, -103, -105, -107, -108, -110, -111, -113, -114, -116,
-117, -118, -119, -120, -121, -122, -123, -123, -124, -125, -125, -126, -126, -126, -126, -126,
-126, -126, -126, -126, -126, -126, -125, -125, -124, -123, -123, -122, -121, -120, -119, -118,
-117, -116, -114, -113, -112, -110, -108, -107, -105, -103, -102, -100, -98, -96, -94, -91,
-89, -87, -85, -82, -80, -78, -75, -73, -70, -67, -65, -62, -59, -57, -54, -51,
-48, -45, -42, -39, -36, -33, -30, -27, -24, -21, -18, -15, -12, -9, -6, -3
};
/**
Converts a 2d coordinate to a 1d.
\param Xcoord The coordinate in x-direction
\param Ycoord The coordinate in y-direction
\return The 1-dimensional coordinate
*/
static Sint16 MapArray2DToMapArray1D(Sint16 Xcoord,Sint16 Ycoord) {
return Xcoord | (Ycoord << 6);
}
/**
Converts a 2d coordinate to a 1d. This function is basically the same
as MapArray2DToMapArray1D() but limits the Xcoord to (0-63).
\param Xcoord The coordinate in x-direction
\param Ycoord The coordinate in y-direction
\return The 1-dimensional coordinate
*/
static Sint16 MapArray2DToMapArray1D_OOB(Sint16 Xcoord,Sint16 Ycoord) {
return MapArray2DToMapArray1D(Xcoord & 0x3F,Ycoord);
}
/**
This function smooth the map in the local neighbourhood of index.
\param index The position which should be smoothed
\param pMapArray Pointer to the map that should be smoothed
\return none
*/
static void SmoothNeighbourhood(Sint16 index, Uint32* pMapArray) {
Sint16 TileType;
Sint16 Xcoord;
Sint16 Ycoord;
Sint16 Pos;
TileType = (Sint16) pMapArray[index];
if(TileType == 8) {
pMapArray[index] = 9;
SmoothNeighbourhood(index,pMapArray);
} else if (TileType == 9) {
for(Ycoord = -1; Ycoord <= 1; Ycoord++) {
for(Xcoord = -1; Xcoord <= 1; Xcoord++) {
Pos = MapArray2DToMapArray1D( (index & 0x3F)+Xcoord,((index >> 6) & 0x3F)+Ycoord);
if(Pos < 0)
continue;
if(Pos >= 64*64)
continue;
if(BoolArray[pMapArray[Pos]] == 1) {
pMapArray[index] = 8;
continue;
} else {
if(pMapArray[Pos] == 9)
continue;
pMapArray[Pos]=8;
}
}
}
} else {
if(BoolArray[TileType] == 0) {
pMapArray[index] = 8;
}
}
}
/**
Creates new random value.
\return The new random value
*/
static Uint16 SeedRand() {
Uint8 a;
Uint8 carry;
Uint8 old_carry;
// little endian is more useful for this algorithm
Seed = SDL_SwapLE32(Seed);
Uint8* pSeed = (Uint8*) &Seed;
// shift right
a = pSeed[0];
a = a >> 1;
// shift right in carry
carry = a & 0x01;
a = a >> 1;
// rotate left through carry
old_carry = carry;
carry = (pSeed[2] & 0x80) >> 7;
pSeed[2] = pSeed[2] << 1;
pSeed[2] = pSeed[2] | old_carry;
// rotate left through carry
old_carry = carry;
carry = (pSeed[1] & 0x80) >> 7;
pSeed[1] = pSeed[1] << 1;
pSeed[1] = pSeed[1] | old_carry;
// invert carry
carry = (carry == 1 ? 0 : 1);
// subtract with carry
a = ((Uint16) a) - (((Uint16) pSeed[0]) + ((Uint16) carry));
// shift right
carry = a & 0x01;
// a = a >> 1; //< Not needed anymore
// rotate right through carry
pSeed[0] = (pSeed[0] >> 1) | (carry << 7);
// xor
a = pSeed[0] ^ pSeed[1];
// convert back to native endianess
Seed = SDL_SwapLE32(Seed);
return ((Uint16) a);
}
/**
Creates a random map.
\param Para_Seed Seed of the map
\param pResultMap Should be Uint16[64*64]
*/
void createMapWithSeed(Uint32 Para_Seed,Uint16 *pResultMap)
{
Uint8 Array4x4TerrainGrid[16*16+16+1];
Uint32 MapArray[65*65];
Point point;
Uint16 randNum;
Uint16 randNum2;
Uint16 randNum3;
Sint16 index;
Sint16 i,j;
Sint16 Xcoord;
Sint16 Ycoord;
Uint16 max;
Sint16 Point1;
Sint16 Point2;
Uint16 pos;
Uint16 curMapRow[0x80];
Uint16 oldMapRow[0x80];
Uint32 Area[3][3];
Seed = Para_Seed;
// clear map
memset(MapArray,0,sizeof(MapArray));
for(i = 0; i < 16*16+16 ; i++) {
Array4x4TerrainGrid[i] = SeedRand() & 0x0F;
if(Array4x4TerrainGrid[i] <= 0x0A)
continue;
Array4x4TerrainGrid[i] = 0x0A;
}
for(i = SeedRand() & 0x0F;i >= 0 ;i--) {
randNum = SeedRand() & 0xFF;
for(j = 0; j < 21; j++) {
index = randNum + OffsetArray1[j];
index = index >= 0 ? index : 0;
index = index <= (16*16+16) ? index : (16*16+16);
Array4x4TerrainGrid[index] = ((Uint16) Array4x4TerrainGrid[index] + (SeedRand() & 0x0F)) & 0x0F;
}
}
for(i = SeedRand() & 0x03; i >= 0; i--) {
randNum = SeedRand() & 0xFF;
for(j = 0; j < 21; j++) {
index = randNum + OffsetArray1[j];
index = index >= 0 ? index : 0;
index = index <= (16*16+16) ? index : (16*16+16);
Array4x4TerrainGrid[index] = SeedRand() & 0x03;
}
}
for(Ycoord = 0; Ycoord < 64; Ycoord+=4) {
for(Xcoord = 0; Xcoord < 64; Xcoord+=4) {
MapArray[MapArray2DToMapArray1D(Xcoord,Ycoord)] = Array4x4TerrainGrid[Ycoord*4+Xcoord/4];
}
}
for(Ycoord = 0; Ycoord < 64; Ycoord+=4) {
for(Xcoord = 0; Xcoord < 64; Xcoord+=4) {
for(i = (Xcoord % 8 == 0 ? 21 : 0) ; (Xcoord % 8 == 0 ? 21 : 0) + 21 > i; i++) {
Point1 = MapArray2DToMapArray1D(Xcoord + OffsetArray2[4*i],Ycoord + OffsetArray2[4*i+1]);
Point2 = MapArray2DToMapArray1D(Xcoord + OffsetArray2[4*i+2],Ycoord + OffsetArray2[4*i+3]);
pos = (Point1 + Point2) / 2;
if(pos >= 64*64)
continue;
Point1 = MapArray2DToMapArray1D_OOB(Xcoord + OffsetArray2[4*i],Ycoord + OffsetArray2[4*i+1]);
Point2 = MapArray2DToMapArray1D_OOB(Xcoord + OffsetArray2[4*i+2],Ycoord + OffsetArray2[4*i+3]);
MapArray[pos] = (MapArray[Point1] + MapArray[Point2] + 1) / 2;
}
}
}
memset(curMapRow,0,sizeof(curMapRow));
// apply box-filter to the map
for(Ycoord = 0; Ycoord < 64; Ycoord++) {
// save the old row
memcpy(oldMapRow,curMapRow,sizeof(curMapRow));
for(i = 0;i < 64; i++) {
curMapRow[i] = (Uint16) MapArray[Ycoord*64+i];
}
for(Xcoord = 0; Xcoord < 64; Xcoord++) {
Area[0][0] = ((Xcoord > 0) && (Ycoord > 0)) ? oldMapRow[Xcoord-1] : curMapRow[Xcoord];
Area[1][0] = (Ycoord > 0) ? oldMapRow[Xcoord] : curMapRow[Xcoord];
Area[2][0] = ((Xcoord < 63) && (Ycoord > 0)) ? oldMapRow[Xcoord+1] : curMapRow[Xcoord];
Area[0][1] = (Xcoord > 0) ? curMapRow[Xcoord-1] : curMapRow[Xcoord];
Area[1][1] = curMapRow[Xcoord];
Area[2][1] = (Xcoord < 63) ? curMapRow[Xcoord+1] : curMapRow[Xcoord];
Area[0][2] = ((Xcoord > 0) && (Ycoord < 63)) ? MapArray[(Ycoord+1)*64 + Xcoord-1] : curMapRow[Xcoord];
Area[1][2] = (Ycoord < 63) ? MapArray[(Ycoord+1)*64 + Xcoord] : curMapRow[Xcoord];
Area[2][2] = ((Xcoord < 63) && (Ycoord < 63)) ? MapArray[(Ycoord+1)*64 + Xcoord+1] : curMapRow[Xcoord];
MapArray[Ycoord*64 + Xcoord] = ( Area[0][0] + Area[1][0] + Area[2][0]
+ Area[0][1] + Area[1][1] + Area[1][2]
+ Area[0][2] + Area[2][1] + Area[2][2] )/9;
}
}
randNum = SeedRand() & 0x0F;
randNum = (randNum < 8 ? 8 : randNum);
randNum = (randNum > 0x0C ? 0x0C : randNum);
point.y = (SeedRand() & 0x03) - 1;
point.y = ( (randNum-3) < point.y ? randNum-3 : point.y);
for(i = 0; i < 64*64; i++) {
point.x = (Uint16) MapArray[i];
if( (randNum+4) < point.x) {
MapArray[i] = 0x06;
} else if(point.x >= randNum) {
MapArray[i] = 0x04;
} else if(point.x <= point.y) {
MapArray[i] = 0x02;
} else {
MapArray[i] = 0x00;
}
}
for(i = SeedRand() & 0x2F; i != 0; i--) {
point.y = SeedRand() & 0x3F;
point.x = SeedRand() & 0x3F;
index = MapArray2DToMapArray1D(point.x,point.y);
if(BoolArray[MapArray[index]] == 1) {
i++;
continue;
}
randNum = SeedRand() & 0x1F;
for(j=0; j < randNum; j++) {
max = SeedRand() & 0x3F;
if(max == 0) {
pos = index;
} else {
point.y = ((index << 2) & 0xFF00) | 0x80;
point.x = ((index & 0x3F) << 8) | 0x80;
randNum2 = SeedRand() & 0xFF;
while(randNum2 > max)
randNum2 = randNum2 >> 1;
randNum3 = SeedRand() & 0xFF;
point.x = point.x + (((sinus[randNum3] * randNum2) >> 7) << 4);
point.y = point.y + ((((-1) * sinus[(randNum3+64) % 256] * randNum2) >> 7) << 4);
if(/*(point.x < 0) || */(point.x > 0x4000) || /*(point.y < 0) ||*/ (point.y > 0x4000)) {
pos = index;
} else {
pos = ((point.y & 0xFF00) >> 2) | (point.x >> 8);
}
}
if(pos >= 64*64) {
j--;
continue;
}
/*
if(pos < 0) {
j--;
continue;
}*/
SmoothNeighbourhood(pos,MapArray);
}
}
//smoothing
for(i = 0; i < 64; i++) {
curMapRow[i] = (Uint16) MapArray[i];
}
for(Ycoord = 0; Ycoord < 64; Ycoord++) {
memcpy(oldMapRow,curMapRow,sizeof(curMapRow));
for(i = 0; i < 64; i++) {
curMapRow[i] = (Uint16) MapArray[Ycoord*64+i];
}
for(Xcoord = 0; Xcoord < 64; Xcoord++) {
Area[1][0] = (Ycoord > 0) ? oldMapRow[Xcoord] : MapArray[Ycoord*64+Xcoord];
Area[0][1] = (Xcoord > 0) ? curMapRow[Xcoord-1] : MapArray[Ycoord*64+Xcoord];
Area[1][1] = MapArray[Ycoord*64+Xcoord];
Area[2][1] = (Xcoord < 63) ? curMapRow[Xcoord+1] : MapArray[Ycoord*64+Xcoord];
Area[1][2] = (Ycoord < 63) ? MapArray[(Ycoord+1)*64+Xcoord] : MapArray[Ycoord*64+Xcoord];
MapArray[Ycoord*64+Xcoord] = 0;
switch(Area[1][1]) {
case 4:
if( (Area[1][0] == 4) || (Area[1][0] == 6) )
MapArray[Ycoord*64+Xcoord] |= 0x01;
if( (Area[2][1] == 4) || (Area[2][1] == 6) )
MapArray[Ycoord*64+Xcoord] |= 0x02;
if( (Area[1][2] == 4) || (Area[1][2] == 6) )
MapArray[Ycoord*64+Xcoord] |= 0x04;
if( (Area[0][1] == 4) || (Area[0][1] == 6) )
MapArray[Ycoord*64+Xcoord] |= 0x08;
break;
case 8:
if( (Area[1][0] == 8) || (Area[1][0] == 9) )
MapArray[Ycoord*64+Xcoord] |= 0x01;
if( (Area[2][1] == 8) || (Area[2][1] == 9) )
MapArray[Ycoord*64+Xcoord] |= 0x02;
if( (Area[1][2] == 8) || (Area[1][2] == 9) )
MapArray[Ycoord*64+Xcoord] |= 0x04;
if( (Area[0][1] == 8) || (Area[0][1] == 9) )
MapArray[Ycoord*64+Xcoord] |= 0x08;
break;
default:
if(Area[1][0] == Area[1][1])
MapArray[Ycoord*64+Xcoord] |= 0x01;
if(Area[2][1] == Area[1][1])
MapArray[Ycoord*64+Xcoord] |= 0x02;
if(Area[1][2] == Area[1][1])
MapArray[Ycoord*64+Xcoord] |= 0x04;
if(Area[0][1] == Area[1][1])
MapArray[Ycoord*64+Xcoord] |= 0x08;
break;
}
if(Area[1][1] == 0)
MapArray[Ycoord*64+Xcoord] = 0;
if(Area[1][1] == 4)
MapArray[Ycoord*64+Xcoord]++;
if(Area[1][1] == 2)
MapArray[Ycoord*64+Xcoord] += 0x11;
if(Area[1][1] == 6)
MapArray[Ycoord*64+Xcoord] += 0x21;
if(Area[1][1] == 8)
MapArray[Ycoord*64+Xcoord] += 0x31;
if(Area[1][1] == 9)
MapArray[Ycoord*64+Xcoord] += 0x41;
}
}
//create resulting array
for(i = 0; i < 64*64; i++) {
MapArray[i] = (MapArray[i] & 0xFE00) | ((MapArray[i] <= 85) ? (MapArray[i]+127) : TileTypes[MapArray[i]-85]) | 0xF800;
pResultMap[i] = MapArray[i] & 0x1FF;
}
}
MapData createMapWithSeed(Uint32 Para_Seed, int mapscale) {
Uint16 SeedMap[64*64];
createMapWithSeed(Para_Seed,SeedMap);
int sizeX = 0;
int sizeY = 0;
int logicalOffsetX = 0;
int logicalOffsetY = 0;
switch(mapscale) {
case 0: {
sizeX = 62;
sizeY = 62;
logicalOffsetX = 1;
logicalOffsetY = 1;
} break;
case 1: {
sizeX = 32;
sizeY = 32;
logicalOffsetX = 16;
logicalOffsetY = 16;
} break;
case 2:
default: {
sizeX = 21;
sizeY = 21;
logicalOffsetX = 11;
logicalOffsetY = 11;
} break;
}
MapData mapData(sizeX, sizeY);
for(int y = 0; y < sizeY; y++) {
for(int x = 0; x < sizeX; x++) {
TERRAINTYPE terrainType = Terrain_Sand;
unsigned char seedmaptype = SeedMap[(y+logicalOffsetY)*64+x+logicalOffsetX] >> 4;
switch(seedmaptype) {
case 0x7: {
// Normal sand
terrainType = Terrain_Sand;
} break;
case 0x2:
case 0x8: {
// Rock or building
terrainType = Terrain_Rock;
} break;
case 0x9: {
// Sand dunes
terrainType = Terrain_Dunes;
} break;
case 0xa: {
// Mountain
terrainType = Terrain_Mountain;
} break;
case 0xb: {
// Spice
terrainType = Terrain_Spice;
} break;
case 0xc: {
// Thick spice
terrainType = Terrain_ThickSpice;
} break;
}
mapData(x,y) = terrainType;
}
}
return mapData;
}