/* s_gen.c
 *
 * Micropolis, Unix Version.  This game was released for the Unix platform
 * in or about 1990 and has been modified for inclusion in the One Laptop
 * Per Child program.  Copyright (C) 1989 - 2007 Electronic Arts Inc.  If
 * you need assistance with this program, you may contact:
 *   http://wiki.laptop.org/go/Micropolis  or email  micropolis@laptop.org.
 * 
 * This program 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 3 of the License, or (at
 * your option) any later version.
 * 
 * This program 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 this program.  If
 * not, see <http://www.gnu.org/licenses/>.
 * 
 *             ADDITIONAL TERMS per GNU GPL Section 7
 * 
 * No trademark or publicity rights are granted.  This license does NOT
 * give you any right, title or interest in the trademark SimCity or any
 * other Electronic Arts trademark.  You may not distribute any
 * modification of this program using the trademark SimCity or claim any
 * affliation or association with Electronic Arts Inc. or its employees.
 * 
 * Any propagation or conveyance of this program must include this
 * copyright notice and these terms.
 * 
 * If you convey this program (or any modifications of it) and assume
 * contractual liability for the program to recipients of it, you agree
 * to indemnify Electronic Arts for any liability that those contractual
 * assumptions impose on Electronic Arts.
 * 
 * You may not misrepresent the origins of this program; modified
 * versions of the program must be marked as such and not identified as
 * the original program.
 * 
 * This disclaimer supplements the one included in the General Public
 * License.  TO THE FULLEST EXTENT PERMISSIBLE UNDER APPLICABLE LAW, THIS
 * PROGRAM IS PROVIDED TO YOU "AS IS," WITH ALL FAULTS, WITHOUT WARRANTY
 * OF ANY KIND, AND YOUR USE IS AT YOUR SOLE RISK.  THE ENTIRE RISK OF
 * SATISFACTORY QUALITY AND PERFORMANCE RESIDES WITH YOU.  ELECTRONIC ARTS
 * DISCLAIMS ANY AND ALL EXPRESS, IMPLIED OR STATUTORY WARRANTIES,
 * INCLUDING IMPLIED WARRANTIES OF MERCHANTABILITY, SATISFACTORY QUALITY,
 * FITNESS FOR A PARTICULAR PURPOSE, NONINFRINGEMENT OF THIRD PARTY
 * RIGHTS, AND WARRANTIES (IF ANY) ARISING FROM A COURSE OF DEALING,
 * USAGE, OR TRADE PRACTICE.  ELECTRONIC ARTS DOES NOT WARRANT AGAINST
 * INTERFERENCE WITH YOUR ENJOYMENT OF THE PROGRAM; THAT THE PROGRAM WILL
 * MEET YOUR REQUIREMENTS; THAT OPERATION OF THE PROGRAM WILL BE
 * UNINTERRUPTED OR ERROR-FREE, OR THAT THE PROGRAM WILL BE COMPATIBLE
 * WITH THIRD PARTY SOFTWARE OR THAT ANY ERRORS IN THE PROGRAM WILL BE
 * CORRECTED.  NO ORAL OR WRITTEN ADVICE PROVIDED BY ELECTRONIC ARTS OR
 * ANY AUTHORIZED REPRESENTATIVE SHALL CREATE A WARRANTY.  SOME
 * JURISDICTIONS DO NOT ALLOW THE EXCLUSION OF OR LIMITATIONS ON IMPLIED
 * WARRANTIES OR THE LIMITATIONS ON THE APPLICABLE STATUTORY RIGHTS OF A
 * CONSUMER, SO SOME OR ALL OF THE ABOVE EXCLUSIONS AND LIMITATIONS MAY
 * NOT APPLY TO YOU.
 */
#include "sim.h"


/* Generate Map */


#define WATER_LOW	RIVER /* 2 */
#define WATER_HIGH	LASTRIVEDGE /* 20 */
#define WOODS_LOW	TREEBASE /* 21 */
#define WOODS_HIGH	UNUSED_TRASH2 /* 39 */


short XStart, YStart, MapX, MapY;
short Dir, LastDir;
int TreeLevel = -1;		/* level for tree creation */
int LakeLevel = -1;		/* level for lake creation */
int CurveLevel = -1;		/* level for river curviness */
int CreateIsland = -1;		/* -1 => 10%, 0 => never, 1 => always */

void DoBRiv(void);
void DoSRiv(void);
void BRivPlop(void);
void SRivPlop(void);
void DoRivers(void);
void DoTrees(void);
void GetRandStart(void);
void MakeLakes(void);
void MakeIsland(void);
void MakeNakedIsland(void);
void GenerateMap(int r);


void
GenerateNewCity(void) 
{
  GenerateSomeCity(Rand16());
}


void
GenerateSomeCity(int r)
{
  if (CityFileName != NULL) {
    ckfree(CityFileName);
    CityFileName = NULL;
  }

  gettimeofday(&start_time, NULL);

  GenerateMap(r);
  ScenarioID = 0;
  CityTime = 0;
  InitSimLoad = 2;
  DoInitialEval = 0;

  InitWillStuff();
  ResetMapState();
  ResetEditorState();
  InvalidateEditors();
  InvalidateMaps();
  UpdateFunds();
  DoSimInit();
  Eval("UIDidGenerateNewCity");
  Kick();
}


int
ERand(short limit)
{
  short x, z;

  z = Rand(limit);
  x = Rand(limit);
  if (z < x)
    return (z);
  return (x);
}


void
GenerateMap(int r)
{
  SeedRand(r);

  if (CreateIsland < 0) {
    if (Rand(100) < 10) { /* chance that island is generated */
      MakeIsland();
      return;
    }
  }
  if (CreateIsland == 1) {
    MakeNakedIsland();
  } else {
    ClearMap();
  }
  GetRandStart();
  if (CurveLevel != 0) {
    DoRivers();
  }
  if (LakeLevel != 0) {
    MakeLakes();
  }
  SmoothRiver();
  if (TreeLevel != 0) {
    DoTrees();
  }
  RandomlySeedRand();
}


void
ClearMap(void)
{
  register short x, y;

  for (x = 0; x < WORLD_X; x++)
    for (y = 0; y < WORLD_Y; y++)
      Map[x][y] = DIRT;
}


void
ClearUnnatural(void)
{
  register short x, y;

  for (x = 0; x < WORLD_X; x++) {
    for (y = 0; y < WORLD_Y; y++) {
      if (Map[x][y] > WOODS) {
	Map[x][y] = DIRT;
      }
    }
  }
}


#define RADIUS 18

void
MakeNakedIsland(void)
{
  register int x, y;

  for (x = 0; x < WORLD_X; x++)
    for (y = 0; y < WORLD_Y; y++)
      Map[x][y] = RIVER;
  for (x = 5; x < WORLD_X - 5; x++)
    for (y = 5; y < WORLD_Y - 5; y++)
      Map[x][y] = DIRT;
  for (x = 0; x < WORLD_X - 5; x += 2) {
    MapX = x ;
    MapY = ERand(RADIUS);
    BRivPlop();
    MapY = (WORLD_Y - 10) - ERand(RADIUS);
    BRivPlop();
    MapY = 0;
    SRivPlop();
    MapY = (WORLD_Y - 6);
    SRivPlop();
  }
  for (y = 0; y < WORLD_Y - 5; y += 2) {
    MapY = y ;
    MapX = ERand(RADIUS);
    BRivPlop();
    MapX = (WORLD_X - 10) - ERand(RADIUS);
    BRivPlop();
    MapX = 0;
    SRivPlop();
    MapX = (WORLD_X - 6);
    SRivPlop();
  }
}


void
MakeIsland(void)
{
  MakeNakedIsland();
  SmoothRiver();
  DoTrees();
}


void
MakeLakes(void)
{
  short Lim1, Lim2, t, z;
  register short x, y;

  if (LakeLevel < 0) {
    Lim1 = Rand(10);
  } else {
    Lim1 = LakeLevel / 2;
  }
  for (t = 0; t < Lim1; t++) {
    x = Rand(WORLD_X - 21) + 10;
    y = Rand(WORLD_Y - 20) + 10;
    Lim2 = Rand(12) + 2;
    for (z = 0; z < Lim2; z++) {
      MapX = x - 6 + Rand(12);
      MapY = y - 6 + Rand(12);
      if (Rand(4))
	SRivPlop();
      else
	BRivPlop();
    }
  }
}


void
GetRandStart(void)
{
  XStart = 40 + Rand(WORLD_X - 80);
  YStart = 33 + Rand(WORLD_Y - 67);
  MapX = XStart;
  MapY = YStart;
}


void
MoveMap(short dir)
{
  static short DirTab[2][8] = { { 0, 1, 1, 1, 0, -1, -1, -1},
				{-1,-1, 0, 1, 1,  1,  0, -1} };
  dir = dir & 7;
  MapX += DirTab[0][dir];
  MapY += DirTab[1][dir];
}


void
TreeSplash(short xloc, short yloc)
{
  short dis, dir;
  register short z;

  if (TreeLevel < 0) {
    dis = Rand(150) + 50;
  } else {
    dis = Rand(100 + (TreeLevel * 2)) + 50;
  }
  MapX = xloc;
  MapY = yloc;
  for (z = 0; z < dis; z++) {
    dir = Rand(7);
    MoveMap(dir);
    if (!(TestBounds(MapX, MapY)))
      return;
    if ((Map[MapX][MapY] & LOMASK) == DIRT)
      Map[MapX][MapY] = WOODS + BLBNBIT;
  }
}


void
DoTrees(void)
{
  short Amount, x, xloc, yloc;

  if (TreeLevel < 0) {
    Amount = Rand(100) + 50;
  } else {
    Amount = TreeLevel + 3;
  }
  for(x = 0; x < Amount; x++) {
    xloc = Rand(WORLD_X - 1);
    yloc = Rand(WORLD_Y - 1);
    TreeSplash(xloc, yloc);
  }
  SmoothTrees();
  SmoothTrees();
}


void
SmoothRiver(void)
{
  static short DX[4] = {-1, 0, 1, 0};
  static short DY[4] = { 0, 1, 0,-1};
  static short REdTab[16] = {
    13+BULLBIT,	13+BULLBIT,	17+BULLBIT,	15+BULLBIT,
    5+BULLBIT,	2,		19+BULLBIT,	17+BULLBIT,
    9+BULLBIT,	11+BULLBIT,	2,		13+BULLBIT,
    7+BULLBIT,	9+BULLBIT,	5+BULLBIT,	2 };
  short bitindex, z, Xtem, Ytem;
  register short temp, MapX, MapY;

  for (MapX = 0; MapX < WORLD_X; MapX++) {
    for (MapY = 0; MapY < WORLD_Y; MapY++) {
      if (Map[MapX][MapY] == REDGE) {
	bitindex = 0;
	for (z = 0; z < 4; z++) {
	  bitindex = bitindex << 1;
	  Xtem = MapX + DX[z];
	  Ytem = MapY + DY[z];
	  if (TestBounds(Xtem, Ytem) &&
	      ((Map[Xtem][Ytem] & LOMASK) != DIRT) &&
	      (((Map[Xtem][Ytem]&LOMASK) < WOODS_LOW) ||
	       ((Map[Xtem][Ytem]&LOMASK) > WOODS_HIGH)))
	      bitindex++;
	}
	temp = REdTab[bitindex & 15];
	if ((temp != RIVER) && (Rand(1)))
	  temp++;
	Map[MapX][MapY] = temp;
      }
    }
  }
}


int
IsTree(int cell)
{
  if (((cell & LOMASK) >= WOODS_LOW) &&
      ((cell & LOMASK) <= WOODS_HIGH))
    return TRUE;
  return FALSE;
}
 

void
SmoothTrees(void)
{
  static short DX[4] = {-1, 0, 1, 0};
  static short DY[4] = { 0, 1, 0,-1};
  static short TEdTab[16] = { 0, 0, 0, 34,
			      0, 0, 36, 35,
			      0, 32, 0, 33,
			      30, 31, 29, 37 };
  short   bitindex, z, Xtem, Ytem;
  register short temp, MapX, MapY;

  for (MapX = 0; MapX < WORLD_X; MapX++) {
    for (MapY = 0; MapY < WORLD_Y; MapY++) {
      if (IsTree(Map[MapX][MapY])) {
	bitindex = 0;
	for (z = 0; z < 4; z++) {
	  bitindex = bitindex << 1;
	  Xtem = MapX + DX[z];
	  Ytem = MapY + DY[z];
	  if (TestBounds(Xtem, Ytem) &&
	      IsTree(Map[Xtem][Ytem])) {
	    bitindex++;
	  }
	}
	temp = TEdTab[bitindex & 15];
	if (temp) {
	  if (temp != WOODS)
	    if ((MapX + MapY) & 1)
	      temp = temp - 8;
	  Map[MapX][MapY] = temp + BLBNBIT;
	}
	else Map[MapX][MapY] = temp;
      }
    }
  }
}


void
DoRivers(void)
{	

  LastDir = Rand(3);
  Dir = LastDir;
  DoBRiv();
  MapX = XStart;
  MapY = YStart;
  LastDir = LastDir ^ 4;
  Dir = LastDir;
  DoBRiv();	
  MapX = XStart;
  MapY = YStart;
  LastDir = Rand(3);
  DoSRiv();
}


void
DoBRiv(void)
{
  int r1, r2;

  if (CurveLevel < 0) {
    r1 = 100;
    r2 = 200;
  } else {
    r1 = CurveLevel + 10;
    r2 = CurveLevel + 100;
  }

  while (TestBounds (MapX + 4, MapY + 4)) {
    BRivPlop();
    if (Rand(r1) < 10) {
      Dir = LastDir;
    } else {
      if (Rand(r2) > 90) Dir++;
      if (Rand(r2) > 90) Dir--;
    }
    MoveMap(Dir);
  }
}


void
DoSRiv(void)
{
  int r1, r2;

  if (CurveLevel < 0) {
    r1 = 100;
    r2 = 200;
  } else {
    r1 = CurveLevel + 10;
    r2 = CurveLevel + 100;
  }

  while (TestBounds (MapX + 3, MapY + 3)) {
    SRivPlop();
    if (Rand(r1) < 10) {
      Dir = LastDir;
    } else {
      if (Rand(r2) > 90) Dir++;
      if (Rand(r2) > 90) Dir--;
    }
    MoveMap(Dir);
  }
}


void
PutOnMap(short Mchar, short Xoff, short Yoff)
{
  register short Xloc, Yloc, temp;

  if (Mchar == 0)
    return;
  Xloc = MapX + Xoff;
  Yloc = MapY + Yoff;
  if (TestBounds(Xloc, Yloc) == FALSE)
    return;
  if ((temp = Map[Xloc][Yloc])) {
    temp = temp & LOMASK;
    if (temp == RIVER)
      if (Mchar != CHANNEL)
	return;
    if (temp == CHANNEL)
      return;
  }
  Map[Xloc][Yloc] = Mchar;	
}


void
BRivPlop(void)
{
  static short BRMatrix[9][9] = {
    { 0, 0, 0, 3, 3, 3, 0, 0, 0 },
    { 0, 0, 3, 2, 2, 2, 3, 0, 0 },
    { 0, 3, 2, 2, 2, 2, 2, 3, 0 },
    { 3, 2, 2, 2, 2, 2, 2, 2, 3 },
    { 3, 2, 2, 2, 4, 2, 2, 2, 3 },
    { 3, 2, 2, 2, 2, 2, 2, 2, 3 },
    { 0, 3, 2, 2, 2, 2, 2, 3, 0 },
    { 0, 0, 3, 2, 2, 2, 3, 0, 0 },
    { 0, 0, 0, 3, 3, 3, 0, 0, 0 } };
  short x, y;

  for (x = 0; x < 9; x++)
    for (y = 0; y < 9; y++)
      PutOnMap(BRMatrix[y][x], x, y);
}


void
SRivPlop(void)
{
  static short SRMatrix[6][6] = {
    { 0, 0, 3, 3, 0, 0 },
    { 0, 3, 2, 2, 3, 0 },
    { 3, 2, 2, 2, 2, 3 },
    { 3, 2, 2, 2, 2, 3 },
    { 0, 3, 2, 2, 3, 0 },
    { 0, 0, 3, 3, 0, 0 } };
  short x, y;

  for (x = 0; x < 6; x++)
    for (y = 0; y < 6; y++)
      PutOnMap(SRMatrix[y][x], x, y);
}


void
SmoothWater(void)
{
  int x, y;

  for(x = 0; x < WORLD_X; x++) {
    for(y = 0; y < WORLD_Y; y++) {
      /* If water: */
      if (((Map[x][y] & LOMASK) >= WATER_LOW) &&
	  ((Map[x][y] & LOMASK) <= WATER_HIGH)) {
	if (x > 0) {
	  /* If nearest object is not water: */
	  if (((Map[x - 1][y] & LOMASK) < WATER_LOW) ||
	      ((Map[x - 1][y] & LOMASK) > WATER_HIGH)) {
	    goto edge;
	  }
	}
	if (x < (WORLD_X - 1)) {
	  /* If nearest object is not water: */
	  if (((Map[x+1][y]&LOMASK) < WATER_LOW) ||
	      ((Map[x+1][y]&LOMASK) > WATER_HIGH)) {
	    goto edge;
	  }
	}
	if (y > 0) {
	  /* If nearest object is not water: */
	  if (((Map[x][y - 1] & LOMASK) < WATER_LOW) ||
	      ((Map[x][y-1]&LOMASK) > WATER_HIGH)) {
	    goto edge;
	  }
	}
	if (y < (WORLD_Y - 1)) {
	  /* If nearest object is not water: */
	  if (((Map[x][y + 1] & LOMASK) < WATER_LOW) ||
	      ((Map[x][y + 1] & LOMASK) > WATER_HIGH)) {
	  edge:
	    Map[x][y]=REDGE; /* set river edge */
	    continue;
	  }
	}
      }
    }
  }
  for (x = 0; x < WORLD_X; x++) {
    for (y = 0; y < WORLD_Y; y++) {
      /* If water which is not a channel: */
      if (((Map[x][y] & LOMASK) != CHANNEL) &&
	  ((Map[x][y] & LOMASK) >= WATER_LOW) &&
	  ((Map[x][y] & LOMASK) <= WATER_HIGH)) {
	if (x > 0) {
	  /* If nearest object is not water; */
	  if (((Map[x - 1][y] & LOMASK) < WATER_LOW) ||
	      ((Map[x - 1][y] & LOMASK) > WATER_HIGH)) {
	    continue;
	  }
	}
	if (x < (WORLD_X - 1)) {
	  /* If nearest object is not water: */
	  if (((Map[x + 1][y] & LOMASK) < WATER_LOW) ||
	      ((Map[x + 1][y] & LOMASK) > WATER_HIGH)) {
	    continue;
	  }
	}
	if (y > 0) {
	  /* If nearest object is not water: */
	  if (((Map[x][y - 1] & LOMASK) < WATER_LOW) ||
	      ((Map[x][y - 1] & LOMASK) > WATER_HIGH)) {
	    continue;
	  }
	}
	if (y < (WORLD_Y - 1)) {
	  /* If nearest object is not water: */
	  if (((Map[x][y + 1] & LOMASK) < WATER_LOW) ||
	      ((Map[x][y + 1] & LOMASK) > WATER_HIGH)) {
	    continue;
	  }
	}
	Map[x][y] = RIVER; /* make it a river */
      }
    }
  }
  for (x = 0; x < WORLD_X; x++) {
    for (y = 0; y < WORLD_Y; y++) {
      /* If woods: */
      if (((Map[x][y] & LOMASK) >= WOODS_LOW) &&
	  ((Map[x][y] & LOMASK) <= WOODS_HIGH)) {
	if (x > 0) {
	  /* If nearest object is water: */
	  if ((Map[x - 1][y] == RIVER) ||
	      (Map[x - 1][y] == CHANNEL)) {
	    Map[x][y] = REDGE; /* make it water's edge */
	    continue;
	  }
	}
	if (x < (WORLD_X - 1)) {
	  /* If nearest object is water: */
	  if ((Map[x + 1][y] == RIVER) ||
	      (Map[x + 1][y] == CHANNEL)) {
	    Map[x][y] = REDGE; /* make it water's edge */
	    continue;
	  }
	}
	if (y > 0) {
	  /* If nearest object is water: */
	  if ((Map[x][y - 1] == RIVER) ||
	      (Map[x][y - 1] == CHANNEL)) {
	    Map[x][y] = REDGE; /* make it water's edge */
	    continue;
	  }
	}
	if (y < (WORLD_Y - 1)) {
	  /* If nearest object is water; */
	  if ((Map[x][y + 1] == RIVER) ||
	      (Map[x][y + 1] == CHANNEL)) {
	    Map[x][y] = REDGE; /* make it water's edge */
	    continue;
	  }
	}
      }
    }
  }
}
