/* s_scan.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"


/* Map Updates */


short NewMap;
short NewMapFlags[NMAPS];
short CCx, CCy, CCx2, CCy2;
short PolMaxX, PolMaxY;
short CrimeMaxX, CrimeMaxY;
QUAD DonDither = 0;

void DistIntMarket(void);
void SmoothPSMap(void);
void SmoothFSMap(void);
void ClrTemArray(void);
void DoSmooth (void);
void DoSmooth2 (void);
void SmoothTerrain(void);
int GetPValue(int loc);
int GetDisCC(int x, int y);
int GetPDen(int Ch9);


/* comefrom: Simulate SpecialInit */
void
FireAnalysis(void)		/* Make firerate map from firestation map  */
{
  register int x,y;

  SmoothFSMap();
  SmoothFSMap();
  SmoothFSMap();
  for (x = 0; x < SmX; x++)
    for (y = 0; y < SmY; y++)
      FireRate[x][y] = FireStMap[x][y];

  NewMapFlags[DYMAP] = NewMapFlags[FIMAP] = 1;
}


/* comefrom: Simulate SpecialInit */
void
PopDenScan(void)		/*  sets: PopDensity, , , ComRate  */
{
  QUAD Xtot, Ytot, Ztot;
  register short x, y, z;

  ClrTemArray();
  Xtot = 0;
  Ytot = 0;
  Ztot = 0;
  for (x = 0; x < WORLD_X; x++)
    for (y = 0; y < WORLD_Y; y++) {
      z = Map[x][y];
      if (z & ZONEBIT) {
	z = z & LOMASK;
	SMapX = x;
	SMapY = y;
	z = GetPDen(z) <<3;
	if (z > 254)
	  z = 254;
	tem[x >>1][y >>1] = z;
	Xtot += x;
	Ytot += y;
	Ztot++;
      }
    }
  DoSmooth();			/* T1 -> T2 */
  DoSmooth2();			/* T2 -> T1 */
  DoSmooth();			/* T1 -> T2 */

  for (x = 0; x < HWLDX; x++)
    for (y = 0; y < HWLDY; y++)
      PopDensity[x][y] = tem2[x][y] <<1;

  DistIntMarket();		/* set ComRate w/ (/ComMap) */

  if (Ztot) {			/* Find Center of Mass for City */
    CCx = Xtot / Ztot;
    CCy = Ytot / Ztot;
  } else {
    CCx = HWLDX;		/* if pop=0 center of Map is CC */
    CCy = HWLDY;
  }
  CCx2 = CCx >>1;
  CCy2 = CCy >>1;
  NewMapFlags[DYMAP] = NewMapFlags[PDMAP] = NewMapFlags[RGMAP] = 1;
}


/* comefrom: PopDenScan */
int
GetPDen(int Ch9)
{
  register int pop;

  if (Ch9 == FREEZ) {
    pop = DoFreePop(/*Ch9*/);
    return (pop);
  }
  if (Ch9 < COMBASE) {
    pop = RZPop(Ch9);
    return (pop);
  }
  if (Ch9 < INDBASE) {
    pop = (CZPop(Ch9) <<3);
    return (pop);
  }
  if (Ch9 < PORTBASE) {
    pop = (IZPop(Ch9) <<3);
    return (pop);
  }
  return (0);
}


/* comefrom: Simulate SpecialInit */
void
PTLScan(void)   	/* Does pollution, terrain, land value   */
{
  QUAD ptot, LVtot;
  register int x, y, z, dis;
  int Plevel, LVflag, loc, zx, zy, Mx, My, pnum, LVnum, pmax;

  for (x = 0; x < QWX; x++)
    for (y = 0; y < QWY; y++)
      Qtem[x][y] = 0;
  LVtot = 0;
  LVnum = 0;
  for (x = 0; x < HWLDX; x++)
    for (y = 0; y < HWLDY; y++) {
      Plevel = 0;
      LVflag = 0;
      zx = x <<1;
      zy = y <<1;
      for (Mx = zx; Mx <= zx + 1; Mx++)
	for (My = zy; My <= zy + 1; My++) {
	  if ((loc = (Map[Mx][My] & LOMASK))) {
	    if (loc < RUBBLE) {
	      Qtem[x >>1][y >>1] += 15;	/* inc terrainMem */
	      continue;
	    }
	    Plevel += GetPValue(loc);
	    if (loc >= ROADBASE)
	      LVflag++;
	  }
	}
/* XXX ???
      if (Plevel < 0)
	Plevel = 250;
*/
      if (Plevel > 255)
	Plevel = 255;
      tem[x][y] = Plevel;
      if (LVflag) {			/* LandValue Equation */
	dis = 34 - GetDisCC(x, y);
	dis = dis <<2;
	dis += (TerrainMem[x >>1][y >>1] );
	dis -= (PollutionMem[x][y]);
	if (CrimeMem[x][y] > 190) dis -= 20;
	if (dis > 250) dis = 250;
	if (dis < 1) dis = 1;
	LandValueMem[x][y] = dis;
	LVtot += dis;
	LVnum++;
      } else
	LandValueMem[x][y] = 0;
    }

  if (LVnum)
    LVAverage = LVtot / LVnum;
  else
    LVAverage = 0;

  DoSmooth();
  DoSmooth2();
  pmax = 0;
  pnum = 0;
  ptot = 0;
  for (x = 0; x < HWLDX; x++) {
    for (y = 0; y < HWLDY; y++)  {
      z = tem[x][y];
      PollutionMem[x][y] = z;
      if (z) {				/*  get pollute average  */
	pnum++;
	ptot += z;
	/* find max pol for monster  */
	if ((z > pmax) ||
	    ((z == pmax) && (!(Rand16() & 3)))) {
	  pmax = z;
	  PolMaxX = x <<1;
	  PolMaxY = y <<1;
	}
      }
    }
  }
  if (pnum)
    PolluteAverage = ptot / pnum;
  else
    PolluteAverage = 0;

  SmoothTerrain();

  NewMapFlags[DYMAP] = NewMapFlags[PLMAP] = NewMapFlags[LVMAP] = 1;
}


/* comefrom: PTLScan */
int
GetPValue(int loc)
{
  if (loc < POWERBASE) {
    if (loc >= HTRFBASE) return (/* 25 */ 75);	/* heavy traf  */
    if (loc >= LTRFBASE) return (/* 10 */ 50);	/* light traf  */
    if (loc <  ROADBASE) {
      if (loc > FIREBASE) return (/* 60 */ 90);
      /* XXX: Why negative pollution from radiation? */
      if (loc >= RADTILE) return (/* -40 */ 255);	/* radioactivity  */
    }
    return (0);
  }
  if (loc <= LASTIND) return (0);
  if (loc < PORTBASE) return (50);	/* Ind  */
  if (loc <= LASTPOWERPLANT) return (/* 60 */ 100);	/* prt, aprt, cpp */
  return (0);
}


/* comefrom: PTLScan DistIntMarket */
int
GetDisCC(int x, int y)
{
  short xdis, ydis, z;

  if (x > CCx2)
    xdis = x - CCx2;
  else
    xdis = CCx2 - x;

  if (y > CCy2)
    ydis = y - CCy2;
  else
    ydis = CCy2 - y;

  z = (xdis + ydis);
  if (z > 32)
    return (32);
  else
    return (z);
}


/* comefrom: Simulate SpecialInit */
void
CrimeScan(void)
{
  short numz;
  QUAD totz;
  register short x, y, z;
  short cmax;

  SmoothPSMap();
  SmoothPSMap();
  SmoothPSMap();
  totz = 0;
  numz = 0;
  cmax = 0;
  for (x = 0; x < HWLDX; x++)
    for (y = 0; y < HWLDY; y++) {
      if ((z = LandValueMem[x][y])) {
	++numz;
	z = 128 - z;
	z += PopDensity[x][y];
	if (z > 300) z = 300;
	z -= PoliceMap[x >>2][y >>2];
	if (z > 250) z = 250;
	if (z < 0) z = 0;
	CrimeMem[x][y] = z;
	totz += z;
	if ((z > cmax) ||
	    ((z == cmax) && (!(Rand16() & 3)))) {
	  cmax = z;
	  CrimeMaxX = x <<1;
	  CrimeMaxY = y <<1;
	}
      } else {
	CrimeMem[x][y] = 0;
      }
    }
  if (numz)
    CrimeAverage = totz / numz;
  else
    CrimeAverage = 0;
  for (x = 0; x < SmX; x++)
    for (y = 0; y < SmY; y++)
      PoliceMapEffect[x][y] = PoliceMap[x][y];
  NewMapFlags[DYMAP] = NewMapFlags[CRMAP] = NewMapFlags[POMAP] = 1;
}


/* comefrom: PTLScan */
void
SmoothTerrain(void)
{
  if (DonDither & 1) {
    register int x, y = 0, z = 0, dir = 1;

    for (x = 0; x < QWX; x++) {
      for (; y != QWY && y != -1; y += dir) {
	z += Qtem[(x == 0) ? x : (x - 1)][y] +
	     Qtem[(x == (QWX - 1)) ? x : (x + 1)][y] +
	     Qtem[x][(y == 0) ? (0) : (y - 1)] +
	     Qtem[x][(y == (QWY - 1)) ? y : (y + 1)] +
	     (Qtem[x][y] <<2);
	TerrainMem[x][y] = (unsigned char)(((unsigned)z) >>3);
	z &= 0x7;
      }
      dir = -dir;
      y += dir;
    }
  } else {
    register short x,y,z;

    for (x = 0; x < QWX; x++)
      for (y = 0; y < QWY; y++) {
	z = 0;
	if (x > 0) z += Qtem[x - 1][y];
	if (x < (QWX - 1)) z += Qtem[x + 1][y];
	if (y > 0) z += Qtem[x][y - 1];
	if (y < (QWY - 1)) z += Qtem[x][y + 1];
	TerrainMem[x][y] = (unsigned char)((z >>2) + Qtem[x][y]) >>1;
      }
  }
}

/* comefrom: PopDenScan */
void
DoSmooth (void)        /* smooths data in tem[x][y] into tem2[x][y]  */
{
  if (DonDither & 2) {
    register int x, y = 0, z = 0, dir = 1;

    for (x = 0; x < HWLDX; x++) {
      for (; y != HWLDY && y != -1; y += dir) {
/*
	z += tem[(x == 0) ? x : (x - 1)][y] +
	     tem[(x == (HWLDX - 1)) ? x : (x + 1)][y] +
	     tem[x][(y == 0) ? (0) : (y - 1)] +
	     tem[x][(y == (HWLDY - 1)) ? y : (y + 1)] +
	     tem[x][y];
	tem2[x][y] = (unsigned char)(((unsigned int)z) >>2);
	z &= 0x3;
*/
	z += tem[(x == 0) ? x : (x - 1)][y] +
	     tem[(x == (HWLDX - 1)) ? x : (x + 1)][y] +
	     tem[x][(y == 0) ? (0) : (y - 1)] +
	     tem[x][(y == (HWLDY - 1)) ? y : (y + 1)] +
	     tem[x][y];
	tem2[x][y] = (unsigned char)(((unsigned int)z) >>2);
	z &= 3;
      }
      dir = -dir;
      y += dir;
    }
  } else {
    register short x,y,z;

    for (x = 0; x < HWLDX; x++) {
      for (y = 0; y < HWLDY; y++) {
	z = 0;
	if (x > 0) z += tem[x - 1][y];
	if (x < (HWLDX - 1)) z += tem[x + 1][y];
	if (y > 0) z += tem[x][y - 1];
	if (y < (HWLDY - 1)) z += tem[x][y + 1];
	z = (z + tem[x][y]) >>2;
	if (z > 255) z = 255;
	tem2[x][y] = (unsigned char)z;
      }
    }
  }
}


/* comefrom: PopDenScan */
void
DoSmooth2 (void)        /* smooths data in tem2[x][y] into tem[x][y]  */
{
  if (DonDither & 4) {
    register int x, y = 0, z = 0, dir = 1;

    for (x = 0; x < HWLDX; x++) {
      for (; y != HWLDY && y != -1; y += dir) {
/*
	z += tem2[(x == 0) ? x : (x - 1)][y] +
	     tem2[(x == (HWLDX - 1)) ? x : (x + 1)][y] +
	     tem2[x][(y == 0) ? (0) : (y - 1)] +
	     tem2[x][(y == (HWLDY - 1)) ? y : (y + 1)] +
	     tem2[x][y];
	tem[x][y] = (unsigned char)(z >>2);
	z &= 0x3;
*/
	z += tem2[(x == 0) ? x : (x - 1)][y] +
	    tem2[(x == (HWLDX - 1)) ? x : (x + 1)][y] +
	    tem2[x][(y == 0) ? (0) : (y - 1)] +
	    tem2[x][(y == (HWLDY - 1)) ? y : (y + 1)] +
	    tem2[x][y];
	tem[x][y] = (unsigned char)(((unsigned char)z) >>2);
	z &= 3;
      }
      dir = -dir;
      y += dir;
    }
  } else {
    register short x,y,z;

    for (x = 0; x < HWLDX; x++) {
      for (y = 0; y < HWLDY; y++) {
	z = 0;
	if (x > 0) z += tem2[x - 1][y];
	if (x < (HWLDX - 1)) z += tem2[x + 1][y];
	if (y > 0) z += tem2[x][y - 1];
	if (y < (HWLDY - 1)) z += tem2[x][y + 1];
	z = (z + tem2[x][y]) >>2;
	if (z > 255) z = 255;
	tem[x][y] = (unsigned char)z;
      }
    }
  }
}


/* comefrom: PopDenScan */
void
ClrTemArray(void)
{
  register short x, y, z;

  z = 0;
  for (x = 0; x < HWLDX; x++)
    for (y = 0; y < HWLDY; y++)
      tem[x][y] = z;
}


/* comefrom: FireAnalysis */
void
SmoothFSMap(void)
{
  register short x, y, edge;

  for (x = 0; x < SmX; x++)
    for (y = 0; y < SmY; y++) {
      edge = 0;
      if (x) edge += FireStMap[x - 1][y];
      if (x < (SmX - 1)) edge += FireStMap[x + 1][y];
      if (y) edge += FireStMap[x][y - 1];
      if (y < (SmY - 1)) edge += FireStMap[x][y + 1];
      edge = (edge >>2) + FireStMap[x][y];
      STem[x][y] = edge >>1;
    }
  for (x = 0; x < SmX; x++)
    for (y = 0; y < SmY; y++)
      FireStMap[x][y] = STem[x][y];
}


/* comefrom: CrimeScan */
void
SmoothPSMap(void)
{
  register int x, y, edge;

  for (x = 0; x < SmX; x++)
    for (y = 0; y < SmY; y++) {
      edge = 0;
      if (x) edge += PoliceMap[x - 1][y];
      if (x < (SmX - 1)) edge += PoliceMap[x + 1][y];
      if (y) edge += PoliceMap[x][y - 1];
      if (y < (SmY - 1)) edge += PoliceMap[x][y + 1];
      edge = (edge >>2) + PoliceMap[x][y];
      STem[x][y] = edge >>1;
    }
  for (x = 0; x < SmX; x++)
    for (y = 0; y < SmY; y++)
      PoliceMap[x][y] = STem[x][y];
}


/* comefrom: PopDenScan */
void
DistIntMarket(void)
{
  register short x, y, z;

  for (x = 0; x < SmX; x++)
    for (y = 0; y < SmY; y++) {
      z = GetDisCC(x <<2,y <<2);
      z = z <<2;
      z = 64 - z;
      ComRate[x][y] = z;
  }
}
