jedi-academy/code/qcommon/cm_randomterrain.cpp
2013-04-23 15:21:39 +10:00

1086 lines
22 KiB
C++

#include "../server/exe_headers.h"
#include "cm_local.h"
#include "cm_patch.h"
#include "cm_landscape.h"
#include "../game/genericparser2.h"
#include "cm_randomterrain.h"
#define NOISE_SIZE 256
#define NOISE_MASK (NOISE_SIZE - 1)
static float noiseTable[NOISE_SIZE];
static int noisePerm[NOISE_SIZE];
static void CM_NoiseInit( CCMLandScape *landscape )
{
int i;
for ( i = 0; i < NOISE_SIZE; i++ )
{
noiseTable[i] = landscape->flrand(-1.0f, 1.0f);
noisePerm[i] = (byte)landscape->irand(0, 255);
}
}
#define VAL( a ) noisePerm[ ( a ) & ( NOISE_MASK )]
#define INDEX( x, y, z, t ) VAL( x + VAL( y + VAL( z + VAL( t ) ) ) )
#define LERP( a, b, w ) ( a * ( 1.0f - w ) + b * w )
static float GetNoiseValue( int x, int y, int z, int t )
{
int index = INDEX( ( int ) x, ( int ) y, ( int ) z, ( int ) t );
return noiseTable[index];
}
#if 0
static float GetNoiseTime( int t )
{
int index = VAL( t );
return (1 + noiseTable[index]);
}
#endif
static float CM_NoiseGet4f( float x, float y, float z, float t )
{
int i;
int ix, iy, iz, it;
float fx, fy, fz, ft;
float front[4];
float back[4];
float fvalue, bvalue, value[2], finalvalue;
ix = ( int ) floor( x );
fx = x - ix;
iy = ( int ) floor( y );
fy = y - iy;
iz = ( int ) floor( z );
fz = z - iz;
it = ( int ) floor( t );
ft = t - it;
for ( i = 0; i < 2; i++ )
{
front[0] = GetNoiseValue( ix, iy, iz, it + i );
front[1] = GetNoiseValue( ix+1, iy, iz, it + i );
front[2] = GetNoiseValue( ix, iy+1, iz, it + i );
front[3] = GetNoiseValue( ix+1, iy+1, iz, it + i );
back[0] = GetNoiseValue( ix, iy, iz + 1, it + i );
back[1] = GetNoiseValue( ix+1, iy, iz + 1, it + i );
back[2] = GetNoiseValue( ix, iy+1, iz + 1, it + i );
back[3] = GetNoiseValue( ix+1, iy+1, iz + 1, it + i );
fvalue = LERP( LERP( front[0], front[1], fx ), LERP( front[2], front[3], fx ), fy );
bvalue = LERP( LERP( back[0], back[1], fx ), LERP( back[2], back[3], fx ), fy );
value[i] = LERP( fvalue, bvalue, fz );
}
finalvalue = LERP( value[0], value[1], ft );
return finalvalue;
}
/****** lincrv.c ******/
/* Ken Shoemake, 1994 */
/* Perform a generic vector unary operation. */
#define V_Op(vdst,gets,vsrc,n) {register int V_i;\
for(V_i=(n)-1;V_i>=0;V_i--) (vdst)[V_i] gets ((vsrc)[V_i]);}
static void lerp(float t, float a0, float a1, vec4_t p0, vec4_t p1, int m, vec4_t p)
{
register float t0=(a1-t)/(a1-a0), t1=1-t0;
register int i;
for (i=m-1; i>=0; i--) p[i] = t0*p0[i] + t1*p1[i];
}
/* DialASpline(t,a,p,m,n,work,Cn,interp,val) computes a point val at parameter
t on a spline with knot values a and control points p. The curve will have
Cn continuity, and if interp is TRUE it will interpolate the control points.
Possibilities include Langrange interpolants, Bezier curves, Catmull-Rom
interpolating splines, and B-spline curves. Points have m coordinates, and
n+1 of them are provided. The work array must have room for n+1 points.
*/
static int DialASpline(float t, float a[], vec4_t p[], int m, int n, vec4_t work[],
unsigned int Cn, bool interp, vec4_t val)
{
register int i, j, k, h, lo, hi;
if (Cn>n-1) Cn = n-1; /* Anything greater gives one polynomial */
for (k=0; t> a[k]; k++); /* Find enclosing knot interval */
for (h=k; t==a[k]; k++); /* May want to use fewer legs */
if (k>n) {k = n; if (h>k) h = k;}
h = 1+Cn - (k-h); k--;
lo = k-Cn; hi = k+1+Cn;
if (interp) { /* Lagrange interpolation steps */
int drop=0;
if (lo<0) {lo = 0; drop += Cn-k;
if (hi-lo<Cn) {drop += Cn-hi; hi = Cn;}}
if (hi>n) {hi = n; drop += k+1+Cn-n;
if (hi-lo<Cn) {drop += lo-(n-Cn); lo = n-Cn;}}
for (i=lo; i<=hi; i++) V_Op(work[i],=,p[i],m);
for (j=1; j<=Cn; j++) {
for (i=lo; i<=hi-j; i++) {
lerp(t,a[i],a[i+j],work[i],work[i+1],m,work[i]);
}
}
h = 1+Cn-drop;
} else { /* Prepare for B-spline steps */
if (lo<0) {h += lo; lo = 0;}
for (i=lo; i<=lo+h; i++) V_Op(work[i],=,p[i],m);
if (h<0) h = 0;
}
for (j=0; j<h; j++) {
int tmp = 1+Cn-j;
for (i=h-1; i>=j; i--) {
lerp(t,a[lo+i],a[lo+i+tmp],work[lo+i],work[lo+i+1],m,work[lo+i+1]);
}
}
V_Op(val,=,work[lo+h],m);
return (k);
}
#define BIG (1.0e12)
static vec_t Vector2Normalize( vec2_t v )
{
float length, ilength;
length = v[0]*v[0] + v[1]*v[1];
length = sqrt (length);
if ( length )
{
ilength = 1/length;
v[0] *= ilength;
v[1] *= ilength;
}
return length;
}
CPathInfo::CPathInfo(CCMLandScape *landscape, int numPoints, float bx, float by, float ex, float ey,
float minWidth, float maxWidth, float depth, float deviation, float breadth,
CPathInfo *Connected, unsigned CreationFlags) :
mNumPoints(numPoints),
mMinWidth(minWidth),
mMaxWidth(maxWidth),
mDepth(depth),
mDeviation(deviation),
mBreadth(breadth)
{
int i, numConnected, index;
float position, goal, deltaGoal;
// float random, delta;
bool horizontal;
float *point;
float currentWidth;
float currentPosition;
vec2_t testPoint, percPoint, diffPoint, normalizedPath;
float distance, length;
CreateCircle();
numConnected = -1;
if (Connected)
{ // we are connecting to an existing spline
numConnected = Connected->GetNumPoints();
if (numConnected >= SPLINE_MERGE_SIZE)
{ // plenty of points to choose from
mNumPoints += SPLINE_MERGE_SIZE;
}
else
{ // the existing spline doesn't have enough points
mNumPoints += numConnected;
}
}
mPoints = (vec4_t *)malloc(sizeof(vec4_t) * mNumPoints);
mWork = (vec4_t *)malloc(sizeof(vec4_t) * (mNumPoints+1));
mWeights = (vec_t *)malloc(sizeof(vec_t) * (mNumPoints+1));
length = sqrt((ex-bx)*(ex-bx) + (ey-by)*(ey-by));
if (fabs(ex - bx) >= fabs(ey - by))
{ // this appears to be a horizontal path
mInc = 1.0 / fabs(ex - bx);
horizontal = true;
position = by;
goal = ey;
deltaGoal = (ey-by) / (numPoints-1);
}
else
{ // this appears to be a vertical path
mInc = 1.0 / fabs(ey - by);
horizontal = false;
position = bx;
goal = ex;
deltaGoal = (ex-bx) / (numPoints-1);
}
normalizedPath[0] = (ex-bx);
normalizedPath[1] = (ey-by);
Vector2Normalize(normalizedPath);
// approx calculate how much we need to iterate through the spline to hit every point
mInc /= 16;
currentWidth = landscape->flrand(minWidth, maxWidth);
currentPosition = 0.0;
for(i=0;i<mNumPoints;i++)
{
// weights are evenly distributed
mWeights[i] = (float)i / (mNumPoints-1);
if (i < numConnected && i < SPLINE_MERGE_SIZE)
{ // we are connecting to an existing spline, so copy over the first few points
if (CreationFlags & PATH_CREATION_CONNECT_FRONT)
{ // copy from the front
index = i;
}
else
{ // copy from the end
index = numConnected-SPLINE_MERGE_SIZE+i;
}
point = Connected->GetPoint(index);
mPoints[i][0] = point[0];
mPoints[i][1] = point[1];
mPoints[i][3] = point[3];
}
else
{
if (horizontal)
{ // we appear to be going horizontal, so spread the randomness across the vertical
mPoints[i][0] = ((ex - bx) * currentPosition) + bx;
mPoints[i][1] = position;
}
else
{ // we appear to be going vertical, so spread the randomness across the horizontal
mPoints[i][0] = position;
mPoints[i][1] = ((ey - by) * currentPosition) + by;
}
currentPosition += 1.0 / (numPoints-1);
// set the width of the spline
mPoints[i][3] = currentWidth;
currentWidth += landscape->flrand(-0.1f, 0.1f);
if (currentWidth < minWidth)
{
currentWidth = minWidth;
}
else if (currentWidth > maxWidth)
{
currentWidth = maxWidth;
}
// see how far we are from the goal
/* delta = (goal - position) * currentPosition;
// calculate the randomness we are allowed at this place
random = landscape->flrand(-mDeviation/1.0, mDeviation/1.0) * (1.0 - currentPosition);
position += delta + random;*/
if (i == mNumPoints-2)
{ // -2 because we are calculating for the next point
position = goal;
}
else
{
if (i == 0)
{
position += deltaGoal + landscape->flrand(-mDeviation/10.0, mDeviation/10.0);
}
else
{
position += deltaGoal + landscape->flrand(-mDeviation*1.5, mDeviation*1.5);
}
}
if (position > 0.9)
{ // too far over, so move back a bit
position = 0.9 - landscape->flrand(0.02f, 0.1f);
}
if (position < 0.1)
{ // too near, so move bakc a bit
position = 0.1 + landscape->flrand(0.02f, 0.1f);
}
// check our deviation from the straight line to the end
if (horizontal)
{
testPoint[0] = ((ex - bx) * currentPosition) + bx;
testPoint[1] = position;
}
else
{
testPoint[0] = position;
testPoint[1] = ((ey - by) * currentPosition) + by;
}
// dot product of the normal of the path to the point we are at
distance = ((testPoint[0]-bx)*normalizedPath[0]) + ((testPoint[1]-by)*normalizedPath[1]);
// find the perpendicular place that is intersected by the point and the path
percPoint[0] = (distance * normalizedPath[0]) + bx;
percPoint[1] = (distance * normalizedPath[1]) + by;
// calculate the difference between the perpendicular point and the test point
diffPoint[0] = testPoint[0] - percPoint[0];
diffPoint[1] = testPoint[1] - percPoint[1];
// calculate the distance
distance = sqrt((diffPoint[0]*diffPoint[0]) + (diffPoint[1]*diffPoint[1]));
if (distance > mDeviation)
{ // we are beyond our allowed deviation, so head back
if (horizontal)
{
position = (ey-by) * currentPosition + by;
}
else
{
position = (ex-bx) * currentPosition + bx;
}
position += landscape->flrand(-mDeviation/2.0, mDeviation/2.0);
}
}
}
mWeights[mNumPoints] = (float)BIG;
}
CPathInfo::~CPathInfo(void)
{
free(mWeights);
free(mWork);
free(mPoints);
}
void CPathInfo::CreateCircle(void)
{
int x, y;
float r, d;
memset(mCircleStamp, 0, sizeof(mCircleStamp));
r = CIRCLE_STAMP_SIZE;
for(x=0;x<CIRCLE_STAMP_SIZE;x++)
{
for(y=0;y<CIRCLE_STAMP_SIZE;y++)
{
d = sqrt((float)(x*x + y*y));
if (d > r)
{
mCircleStamp[y][x] = 255;
}
else
{
mCircleStamp[y][x] = pow(sin((float)(d / r * M_PI / 2)), mBreadth) * 255;
}
}
}
}
void CPathInfo::Stamp(int x, int y, int size, int depth, unsigned char *Data, int DataWidth, int DataHeight)
{
// int xPos;
// float yPos;
int dx, dy, fx, fy;
float offset;
byte value;
byte invDepth;
offset = (float)(CIRCLE_STAMP_SIZE-1) / size;
invDepth = 255-depth;
for(dx = -size; dx <= size; dx++)
{
for ( dy = -size; dy <= size; dy ++ )
{
float d;
d = dx * dx + dy * dy ;
if ( d > size * size )
{
continue;
}
fx = x + dx;
if (fx < 2 || fx > DataWidth-2)
{
continue;
}
fy = y + dy;
if (fy < 2 || fy > DataHeight-2)
{
continue;
}
value = pow ( sin ( (float)(d / (size * size) * M_PI / 2)), mBreadth ) * invDepth + depth;
if (value < Data[(fy * DataWidth) + fx])
{
Data[(fy * DataWidth) + fx] = value;
}
}
}
/*
fx = x + dx;
if (fx < 2 || fx > DataWidth-2)
{
continue;
}
xPos = abs((int)(dx*offset));
yPos = offset*size + offset;
for(dy = -size; dy < 0; dy++)
{
yPos -= offset;
fy = y + dy;
if (fy < 2 || fy > DataHeight-2)
{
continue;
}
value = (invDepth * mCircleStamp[(int)yPos][xPos] / 256) + depth;
if (value < Data[(fy * DataWidth) + fx])
{
Data[(fy * DataWidth) + fx] = value;
}
}
yPos = -offset;
for(; dy <= size; dy++)
{
yPos += offset;
fy = y + dy;
if (fy < 2 || fy > DataHeight-2)
{
continue;
}
value = (invDepth * mCircleStamp[(int)yPos][xPos] / 256) + depth;
if (value < Data[(fy * DataWidth) + fx])
{
Data[(fy * DataWidth) + fx] = value;
}
}
}
*/
}
void CPathInfo::GetInfo(float PercentInto, vec4_t Coord, vec4_t Vector)
{
vec4_t before, after;
float testPercent;
DialASpline(PercentInto, mWeights, mPoints, sizeof(vec4_t) / sizeof(vec_t), mNumPoints-1, mWork, 2, true, Coord);
testPercent = PercentInto - 0.01;
if (testPercent < 0)
{
testPercent = 0;
}
DialASpline(testPercent, mWeights, mPoints, sizeof(vec4_t) / sizeof(vec_t), mNumPoints-1, mWork, 2, true, before);
testPercent = PercentInto + 0.01;
if (testPercent > 1.0)
{
testPercent = 1.0;
}
DialASpline(testPercent, mWeights, mPoints, sizeof(vec4_t) / sizeof(vec_t), mNumPoints-1, mWork, 2, true, after);
Coord[2] = mDepth;
Vector[0] = after[0] - before[0];
Vector[1] = after[1] - before[1];
}
void CPathInfo::DrawPath(unsigned char *Data, int DataWidth, int DataHeight )
{
float t;
vec4_t val, vector;//, perp;
int size;
float inc;
int x, y, lastX, lastY;
float depth;
inc = mInc / DataWidth;
lastX = lastY = -999;
for (t=0.0; t<=1.0; t+=inc)
{
GetInfo(t, val, vector);
/* perp[0] = -vector[1];
perp[1] = vector[0];
if (fabs(perp[0]) > fabs(perp[1]))
{
perp[1] /= fabs(perp[0]);
perp[0] /= fabs(perp[0]);
}
else
{
perp[0] /= fabs(perp[1]);
perp[1] /= fabs(perp[1]);
}
*/
x = val[0] * DataWidth;
y = val[1] * DataHeight;
if (x == lastX && y == lastY)
{
continue;
}
lastX = x;
lastY = y;
size = val[3] * DataWidth;
depth = mDepth * 255.0f;
Stamp(x, y, size, (int)depth, Data, DataWidth, DataHeight);
}
}
CRandomTerrain::CRandomTerrain(void)
{
memset(mPaths, 0, sizeof(mPaths));
}
CRandomTerrain::~CRandomTerrain(void)
{
Shutdown();
}
void CRandomTerrain::Init(CCMLandScape *landscape, byte *grid, int width, int height)
{
Shutdown();
mLandScape = landscape;
mWidth = width;
mHeight = height;
mArea = mWidth * mHeight;
mBorder = (width + height) >> 6;
mGrid = grid;
}
void CRandomTerrain::ClearPaths(void)
{
int i;
for(i=0;i<MAX_RANDOM_PATHS;i++)
{
if (mPaths[i])
{
delete mPaths[i];
mPaths[i] = 0;
}
}
memset(mPaths, 0, sizeof(mPaths));
}
void CRandomTerrain::Shutdown(void)
{
ClearPaths ( );
}
bool CRandomTerrain::CreatePath(int PathID, int ConnectedID, unsigned CreationFlags, int numPoints,
float bx, float by, float ex, float ey,
float minWidth, float maxWidth, float depth, float deviation, float breadth )
{
CPathInfo *connected = 0;
if (PathID < 0 || PathID >= MAX_RANDOM_PATHS || mPaths[PathID])
{
return false;
}
if (ConnectedID >= 0 && ConnectedID < MAX_RANDOM_PATHS)
{
connected = mPaths[ConnectedID];
}
mPaths[PathID] = new CPathInfo(mLandScape, numPoints, bx, by, ex, ey,
minWidth, maxWidth, depth, deviation, breadth,
connected, CreationFlags );
return true;
}
bool CRandomTerrain::GetPathInfo(int PathNum, float PercentInto, vec4_t Coord, vec4_t Vector)
{
if (PathNum < 0 || PathNum >= MAX_RANDOM_PATHS || !mPaths[PathNum])
{
return false;
}
mPaths[PathNum]->GetInfo(PercentInto, Coord, Vector);
return true;
}
void CRandomTerrain::ParseGenerate(const char *GenerateFile)
{
}
void CRandomTerrain::Smooth ( void )
{
// Scale down to 1/4 size then back up to smooth out the terrain
byte *temp;
int x, y, o;
temp = mLandScape->GetFlattenMap ( );
// Copy over anything in the flatten map
if (temp)
{
for ( o = 0; o < mHeight * mWidth; o++)
{
if ( temp[o] > 0 )
{
mGrid[o] = (byte)temp[o] & 0x7F;
}
}
}
temp = (byte *)Z_Malloc(mWidth * mHeight, TAG_TEMP_WORKSPACE, qfalse);
#if 1
unsigned total, count;
for(x=1;x<mWidth-1;x++)
{
for(y=1;y<mHeight-1;y++)
{
total = 0;
count = 2;
// Left
total += mGrid[((y)*mWidth)+(x-1)];
count++;
// Right
total += mGrid[((y)*mWidth)+(x+1)];
count++;
// Up
total += mGrid[((y-1)*mWidth)+(x)];
count++;
// Down
total += mGrid[((y+1)*mWidth)+(x)];
count++;
// Up-Left
total += mGrid[((y-1)*mWidth)+(x-1)];
count++;
// Down-Left
total += mGrid[((y+1)*mWidth)+(x-1)];
count++;
// Up-Right
total += mGrid[((y-1)*mWidth)+(x+1)];
count++;
// Down-Right
total += mGrid[((y+1)*mWidth)+(x+1)];
count++;
total += (unsigned)mGrid[((y)*mWidth)+(x)] * 2;
temp[((y)*mWidth)+(x)] = total / count;
}
}
memcpy(mGrid, temp, mWidth * mHeight);
#else
float smoothKernel[FILTER_SIZE][FILTER_SIZE];
int xx, yy, dx, dy;
float total, num;
R_Resample(mGrid, mWidth, mHeight, temp, mWidth >> 1, mHeight >> 1, 1);
R_Resample(temp, mWidth >> 1, mHeight >> 1, mGrid, mWidth, mHeight, 1);
// now lets filter it.
memcpy(temp, mGrid, mWidth * mHeight);
for (dy = -KERNEL_SIZE; dy <= KERNEL_SIZE; dy++)
{
for (dx = -KERNEL_SIZE; dx <= KERNEL_SIZE; dx++)
{
smoothKernel[dy + KERNEL_SIZE][dx + KERNEL_SIZE] =
1.0f / (1.0f + fabs(float(dx) * float(dx) * float(dx)) + fabs(float(dy) * float(dy) * float(dy)));
}
}
for (y = 0; y < mHeight; y++)
{
for (x = 0; x < mWidth; x++)
{
total = 0.0f;
num = 0.0f;
for (dy = -KERNEL_SIZE; dy <= KERNEL_SIZE; dy++)
{
for (dx = -KERNEL_SIZE; dx <= KERNEL_SIZE; dx++)
{
xx = x + dx;
if (xx >= 0 && xx < mWidth)
{
yy = y + dy;
if (yy >= 0 && yy < mHeight)
{
total += smoothKernel[dy + KERNEL_SIZE][dx + KERNEL_SIZE] * (float)temp[yy * mWidth + xx];
num += smoothKernel[dy + KERNEL_SIZE][dx + KERNEL_SIZE];
}
}
}
}
total /= num;
mGrid[y * mWidth + x] = (byte)Com_Clamp(0, 255, (int)Round(total));
}
}
#endif
Z_Free(temp);
/* Uncomment to see the symmetry line on the map
for ( x = 0; x < mWidth; x ++ )
{
mGrid[x * mWidth + x] = 255;
}
*/
}
void CRandomTerrain::Generate(int symmetric)
{
int i,j;
// Clear out all existing data
memset(mGrid, 255, mArea);
// make landscape a little bumpy
float t1 = mLandScape->flrand(0, 2);
float t2 = mLandScape->flrand(0, 2);
float t3 = mLandScape->flrand(0, 2);
CM_NoiseInit(mLandScape);
int x, y;
for (y = 0; y < mHeight; y++)
for (x = 0; x < mWidth; x++)
{
i = x + y*mWidth;
byte val = (byte)Com_Clamp(0, 255, (int)(220 + (CM_NoiseGet4f( x*0.25, y*0.25, 0, t3 ) * 20)) + (CM_NoiseGet4f( x*0.5, y*0.5, 0, t2 ) * 15));
mGrid[i] = val;
}
for ( i = 0; mPaths[i] != 0; i ++ )
{
mPaths[i]->DrawPath(mGrid, mWidth, mHeight);
}
for (y = 0; y < mHeight; y++)
for (x = 0; x < mWidth; x++)
{
i = x + y*mWidth;
byte val = (byte)Com_Clamp(0, 255, (int)(mGrid[i] + (CM_NoiseGet4f( x, y, 0, t1 ) * 5)));
mGrid[i] = val;
}
// if symmetric, do this now
if (symmetric)
{
assert (mWidth == mHeight); // must be square
for (y = 0; y < mHeight; y++)
for (x = 0; x < (mWidth-y); x++)
{
i = x + y*mWidth;
j = (mWidth-1 - x) + (mHeight-1 - y)*mWidth;
byte val = mGrid[i] < mGrid[j] ? mGrid[i] : mGrid[j];
mGrid[i] = mGrid[j] = val;
}
}
}
typedef enum
{
RMG_CP_NONE = -1,
RMG_CP_CONSONANT,
RMG_CP_COMPLEX_CONSONANT,
RMG_CP_VOWEL,
RMG_CP_COMPLEX_VOWEL,
RMG_CP_ENDING,
RMG_CP_NUM_PIECES,
} ECPType;
typedef struct SCharacterPiece
{
char *mPiece;
int mCommonality;
} TCharacterPiece;
static TCharacterPiece Consonants[] =
{
{ "b", 6 },
{ "c", 8 },
{ "d", 6 },
{ "f", 5 },
{ "g", 4 },
{ "h", 5 },
{ "j", 2 },
{ "k", 4 },
{ "l", 4 },
{ "m", 7 },
{ "n", 7 },
{ "r", 6 },
{ "s", 10 },
{ "t", 10 },
{ "v", 1 },
{ "w", 2 },
{ "x", 1 },
{ "z", 1 },
{ 0, 0 }
};
static TCharacterPiece ComplexConsonants[] =
{
{ "st", 10 },
{ "ck", 10 },
{ "ss", 10 },
{ "tt", 7 },
{ "ll", 8 },
{ "nd", 10 },
{ "rn", 6 },
{ "nc", 6 },
{ "mp", 4 },
{ "sc", 10 },
{ "sl", 10 },
{ "tch", 6 },
{ "th", 4 },
{ "rn", 5 },
{ "cl", 10 },
{ "sp", 10 },
{ "st", 10 },
{ "fl", 4 },
{ "sh", 7 },
{ "ng", 4 },
// { "" },
{ 0, 0 }
};
static TCharacterPiece Vowels[] =
{
{ "a", 10 },
{ "e", 10 },
{ "i", 10 },
{ "o", 10 },
{ "u", 2 },
// { "" },
{ 0, 0 }
};
static TCharacterPiece ComplexVowels[] =
{
{ "ea", 10 },
{ "ue", 3 },
{ "oi", 10 },
{ "ai", 8 },
{ "oo", 10 },
{ "io", 10 },
{ "oe", 10 },
{ "au", 3 },
{ "ee", 7 },
{ "ei", 7 },
{ "ou", 7 },
{ "ia", 4 },
// { "" },
{ 0, 0 }
};
static TCharacterPiece Endings[] =
{
{ "ing", 10 },
{ "ed", 10 },
{ "ute", 10 },
{ "ance", 10 },
{ "ey", 10 },
{ "ation", 10 },
{ "ous", 10 },
{ "ent", 10 },
{ "ate", 10 },
{ "ible", 10 },
{ "age", 10 },
{ "ity", 10 },
{ "ist", 10 },
{ "ism", 10 },
{ "ime", 10 },
{ "ic", 10 },
{ "ant", 10 },
{ "etry", 10 },
{ "ious", 10 },
{ "ative", 10 },
{ "er", 10 },
{ "ize", 10 },
{ "able", 10 },
{ "itude", 10 },
// { "" },
{ 0, 0 }
};
static void FindPiece(ECPType type, char *&pos)
{
TCharacterPiece *search, *start;
int count = 0;
switch(type)
{
case RMG_CP_CONSONANT:
default:
start = Consonants;
break;
case RMG_CP_COMPLEX_CONSONANT:
start = ComplexConsonants;
break;
case RMG_CP_VOWEL:
start = Vowels;
break;
case RMG_CP_COMPLEX_VOWEL:
start = ComplexVowels;
break;
case RMG_CP_ENDING:
start = Endings;
break;
}
search = start;
while(search->mPiece)
{
count += search->mCommonality;
search++;
}
count = Q_irand(0, count-1);
search = start;
while(count > search->mCommonality)
{
count -= search->mCommonality;
search++;
}
strcpy(pos, search->mPiece);
pos += strlen(search->mPiece);
}
unsigned RMG_CreateSeed(char *TextSeed)
{
int Length;
char Ending[256], *pos;
int ComplexVowelChance, ComplexConsonantChance;
ECPType LookingFor;
unsigned SeedValue = 0, high;
Length = Q_irand(4, 9);
if (Q_irand(0, 100) < 20)
{
LookingFor = RMG_CP_VOWEL;
}
else
{
LookingFor = RMG_CP_CONSONANT;
}
Ending[0] = 0;
if (Q_irand(0, 100) < 55)
{
pos = Ending;
FindPiece(RMG_CP_ENDING, pos);
Length -= (pos - Ending);
}
pos = TextSeed;
*pos = 0;
ComplexVowelChance = -1;
ComplexConsonantChance = -1;
while((pos - TextSeed) < Length || LookingFor == RMG_CP_CONSONANT)
{
if (LookingFor == RMG_CP_VOWEL)
{
if (Q_irand(0, 100) < ComplexVowelChance)
{
ComplexVowelChance = -1;
LookingFor = RMG_CP_COMPLEX_VOWEL;
}
else
{
ComplexVowelChance += 10;
}
FindPiece(LookingFor, pos);
LookingFor = RMG_CP_CONSONANT;
}
else
{
if (Q_irand(0, 100) < ComplexConsonantChance)
{
ComplexConsonantChance = -1;
LookingFor = RMG_CP_COMPLEX_CONSONANT;
}
else
{
ComplexConsonantChance += 45;
}
FindPiece(LookingFor, pos);
LookingFor = RMG_CP_VOWEL;
}
}
if (Ending[0])
{
strcpy(pos, Ending);
}
pos = TextSeed;
while(*pos)
{
high = SeedValue >> 28;
SeedValue ^= (SeedValue << 4) + ((*pos)-'a');
SeedValue ^= high;
pos++;
}
return SeedValue;
}