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

void _FixSingle(int x, int y, short *TileAdrPtr);
void _FixZone(int x, int y, short *TileAdrPtr);
int _LayDoze(int x, int y, short *TileAdrPtr);
int _LayRoad(int x, int y, short *TileAdrPtr);
int _LayRail(int x, int y, short *TileAdrPtr);
int _LayWire(int x, int y, short *TileAdrPtr);


short _RoadTable[16] = {
  66, 67, 66, 68,
  67, 67, 69, 73,
  66, 71, 66, 72,
  70, 75, 74, 76
};

short _RailTable[16] = {
  226, 227, 226, 228,
  227, 227, 229, 233,
  226, 231, 226, 232,
  230, 235, 234, 236
};

short _WireTable[16] = {
  210, 211, 210, 212,
  211, 211, 213, 217,
  210, 215, 210, 216,
  214, 219, 218, 220
};


#define NeutralizeRoad(Tile) \
  if (((Tile &= LOMASK) >= 64) && \
      ((Tile & LOMASK) <= 207)) { \
    Tile = (Tile & 0x000F) + 64; \
  }


/* comefrom: check3Border check4Border check5Border processWand */
int
ConnecTile(short x, short y, short *TileAdrPtr, short Command)
{
  short Tile;
  int result = 1;

  /* make sure the array subscripts are in bounds */
  if (!TestBounds(x, y)) {
    return (0);
  }

  /* AutoDoze */
  if ((Command >= 2) && (Command <= 4)) {

    if ((autoBulldoze != 0) &&
	(TotalFunds > 0) &&
	((Tile = (*TileAdrPtr)) & BULLBIT)) {
      NeutralizeRoad(Tile);
      /* Maybe this should check BULLBIT instead of checking tile values? */
      if (((Tile >= TINYEXP) && (Tile <= LASTTINYEXP)) ||
	  ((Tile < 64) && (Tile != 0))) {
	Spend(1);
	(*TileAdrPtr) = 0;
      }
    }
  }

  switch (Command) {

  case 0:	/* Fix zone */
    _FixZone(x, y, TileAdrPtr);
    break;
    
  case 1:	/* Doze zone */
    result = _LayDoze(x, y, TileAdrPtr);
    _FixZone(x, y, TileAdrPtr);
    break;
    
  case 2:	/* Lay Road */
    result = _LayRoad(x, y, TileAdrPtr);
    _FixZone(x, y, TileAdrPtr);
    break;
    
  case 3:	/* Lay Rail */
    result = _LayRail(x, y, TileAdrPtr);
    _FixZone(x, y, TileAdrPtr);
    break;
    
  case 4:	/* Lay Wire */
    result = _LayWire(x, y, TileAdrPtr);
    _FixZone(x, y, TileAdrPtr);
    break;

  }
  
  return result;
}

/* comefrom: ConnecTile */
int
_LayDoze(int x, int y, short *TileAdrPtr)
{
  short Tile;

  if (!(TotalFunds)) {
    return -2;			/* no mas dinero. */
  }

  Tile = (*TileAdrPtr);

  if (!(Tile & BULLBIT)) {
    return 0;			/* Check dozeable bit. */
  }

  NeutralizeRoad(Tile);

  switch (Tile) {
  case HBRIDGE:
  case VBRIDGE:
  case BRWV:
  case BRWH:
  case HBRDG0:
  case HBRDG1:
  case HBRDG2:
  case HBRDG3:
  case VBRDG0:
  case VBRDG1:
  case VBRDG2:
  case VBRDG3:
  case HPOWER:
  case VPOWER:
  case HRAIL:
  case VRAIL:		/* Dozing over water, replace with water. */
    (*TileAdrPtr) = RIVER;
    break;

  default:		/* Dozing on land, replace with land.  Simple, eh? */
    (*TileAdrPtr) = DIRT;
    break;
  }

  Spend(1);			/* Costs $1.00....*/
  return 1;
}


/* comefrom: ConnecTile */
int
_LayRoad(int x, int y, short *TileAdrPtr)
{
  short Tile;
  int cost = 10;

  if (TotalFunds < 10) {
    return -2;
  }

  Tile = (*TileAdrPtr) & LOMASK;

  switch (Tile) {

  case DIRT:
    (*TileAdrPtr) = ROADS | BULLBIT | BURNBIT;
    break;
			
  case RIVER:			/* Road on Water */
  case REDGE:
  case CHANNEL:			/* Check how to build bridges, if possible. */
    if (TotalFunds < 50) {
      return -2;
    }

    cost = 50;

    if (x < (WORLD_X - 1)) {
      Tile = TileAdrPtr[WORLD_Y];
      NeutralizeRoad(Tile);
      if ((Tile == VRAILROAD) ||
	  (Tile == HBRIDGE) ||
	  ((Tile >= ROADS) &&
	   (Tile <= HROADPOWER))) {
	(*TileAdrPtr) = HBRIDGE | BULLBIT;
	break;
      }
    }

    if (x > 0) {
      Tile = TileAdrPtr[-WORLD_Y];
      NeutralizeRoad(Tile);
      if ((Tile == VRAILROAD) ||
	  (Tile == HBRIDGE) ||
	  ((Tile >= ROADS) &&
	   (Tile <= INTERSECTION))) {
	(*TileAdrPtr) = HBRIDGE | BULLBIT;
	break;
      }
    }

    if (y < (WORLD_Y - 1)) {
      Tile = TileAdrPtr[1];
      NeutralizeRoad(Tile);
      if ((Tile == HRAILROAD) ||
	  (Tile == VROADPOWER) ||
	  ((Tile >= VBRIDGE) &&
	   (Tile <= INTERSECTION))) {
	(*TileAdrPtr) = VBRIDGE | BULLBIT;
	break;
      }
    }

    if (y > 0) {
      Tile = TileAdrPtr[-1];
      NeutralizeRoad(Tile);
      if ((Tile == HRAILROAD) ||
	  (Tile == VROADPOWER) ||
	  ((Tile >= VBRIDGE) &&
	   (Tile <= INTERSECTION))) {
	(*TileAdrPtr) = VBRIDGE | BULLBIT;
	break;
      }
    }

    /* Can't do road... */
    return 0;

  case LHPOWER:		/* Road on power */
    (*TileAdrPtr) = VROADPOWER | CONDBIT | BURNBIT | BULLBIT;
    break;

  case LVPOWER:		/* Road on power #2 */
    (*TileAdrPtr) = HROADPOWER | CONDBIT | BURNBIT | BULLBIT;
    break;

  case LHRAIL:		/* Road on rail */
    (*TileAdrPtr) = HRAILROAD | BURNBIT | BULLBIT;
    break;

  case LVRAIL:		/* Road on rail #2 */
    (*TileAdrPtr) = VRAILROAD | BURNBIT | BULLBIT;
    break;

  default:		/* Can't do road */
    return 0;

  }

  Spend(cost);

  return 1;
}


/* comefrom: ConnecTile */
int
_LayRail(int x, int y, short *TileAdrPtr)
{
  short Tile;
  int cost = 20;

  if (TotalFunds < 20) {
    return -2;
  }

  Tile = (*TileAdrPtr) & LOMASK;
  NeutralizeRoad(Tile);

  switch (Tile) {
  case 0:			/* Rail on Dirt */
    (*TileAdrPtr) = 226 | BULLBIT | BURNBIT;
    break;

  case 2:			/* Rail on Water */
  case 3:
  case 4:			/* Check how to build underwater tunnel, if possible. */
    if (TotalFunds < 100) {
      return -2;
    }
    cost = 100;

    if (x < (WORLD_X - 1)) {
      Tile = TileAdrPtr[WORLD_Y];
      NeutralizeRoad(Tile);
      if ((Tile == 221) || (Tile == 224) || ((Tile >= 226) && (Tile <= 237))) {
	(*TileAdrPtr) = 224 | BULLBIT;
	break;
      }
    }
    
    if (x > 0) {
      Tile = TileAdrPtr[-WORLD_Y];
      NeutralizeRoad(Tile);
      if ((Tile == 221) || (Tile == 224) || ((Tile > 225) && (Tile < 238))) {
	(*TileAdrPtr) = 224 | BULLBIT;
	break;
      }
    }

    if (y < (WORLD_Y - 1)) {
      Tile = TileAdrPtr[1];
      NeutralizeRoad(Tile);
      if ((Tile == 222) || (Tile == 238) || ((Tile > 224) && (Tile < 237))) {
	(*TileAdrPtr) = 225 | BULLBIT;
	break;
      }
    }

    if (y > 0) {
      Tile = TileAdrPtr[-1];
      NeutralizeRoad(Tile);
      if ((Tile == 222) || (Tile == 238) || ((Tile > 224) && (Tile < 237))) {
	(*TileAdrPtr) = 225 | BULLBIT;
	break;
      }
    }

    /* Can't do rail... */
    return 0;

  case 210:		/* Rail on power */
    (*TileAdrPtr) = 222 | CONDBIT | BURNBIT | BULLBIT;
    break;

  case 211:		/* Rail on power #2 */
    (*TileAdrPtr) = 221 | CONDBIT | BURNBIT | BULLBIT;
    break;

  case 66:		/* Rail on road */
    (*TileAdrPtr) = 238 | BURNBIT | BULLBIT;
    break;

  case 67:		/* Rail on road #2 */
    (*TileAdrPtr) = 237 | BURNBIT | BULLBIT;
    break;

  default:		/* Can't do rail */
    return 0;
  }

  Spend(cost);
  return 1;
}


/* comefrom: ConnecTile */
int
_LayWire(int x, int y, short *TileAdrPtr)
{
  short Tile;
  int cost = 5;

  if (TotalFunds < 5) {
    return -2;
  }

  Tile = (*TileAdrPtr) & LOMASK;
  NeutralizeRoad(Tile);

  switch (Tile) {
  case 0:			/* Wire on Dirt */
    (*TileAdrPtr) = 210 | CONDBIT | BURNBIT | BULLBIT;
    break;

  case 2:			/* Wire on Water */
  case 3:
  case 4:			/* Check how to lay underwater wire, if possible. */
    if (TotalFunds < 25)
      return -2;
    cost = 25;

    if (x < (WORLD_X - 1)) {
      Tile = TileAdrPtr[WORLD_Y];
      if (Tile & CONDBIT) {
	NeutralizeRoad(Tile);
	if ((Tile != 77) && (Tile != 221) && (Tile != 208)) {
	  (*TileAdrPtr) = 209 | CONDBIT | BULLBIT;
	  break;		
	}
      }
    }

    if (x > 0) {
      Tile = TileAdrPtr[-WORLD_Y];
      if (Tile & CONDBIT) {
	NeutralizeRoad(Tile);		
	if ((Tile != 77) && (Tile != 221) && (Tile != 208)) {
	  (*TileAdrPtr) = 209 | CONDBIT | BULLBIT;
	  break;		
	}
      }
    }

    if (y < (WORLD_Y - 1)) {
      Tile = TileAdrPtr[1];
      if (Tile & CONDBIT) {
	NeutralizeRoad(Tile);		
	if ((Tile != 78) && (Tile != 222) && (Tile != 209)) {
	  (*TileAdrPtr) = 208 | CONDBIT | BULLBIT;
	  break;
	}
      }
    }

    if (y > 0) {
      Tile = TileAdrPtr[-1];
      if (Tile & CONDBIT) {
	NeutralizeRoad(Tile);		
	if ((Tile != 78) && (Tile != 222) && (Tile != 209)) {
	  (*TileAdrPtr) = 208 | CONDBIT | BULLBIT;
	  break;		
	}
      }
    }

    /* Can't do wire... */
    return 0;

  case 66:		/* Wire on Road */
    (*TileAdrPtr) = 77 | CONDBIT | BURNBIT | BULLBIT;
    break;

  case 67:		/* Wire on Road #2 */
    (*TileAdrPtr) = 78 | CONDBIT | BURNBIT | BULLBIT;
			break;

  case 226:		/* Wire on rail */
    (*TileAdrPtr) = 221 | CONDBIT | BURNBIT | BULLBIT;
    break;

  case 227:		/* Wire on rail #2 */
    (*TileAdrPtr) = 222 | CONDBIT | BURNBIT | BULLBIT;
    break;

  default:		/* Can't do wire */
    return 0;
  }

  Spend(cost);
  return 1;
}


/* comefrom: ConnecTile */
void
_FixZone(int x, int y, short *TileAdrPtr)
{
  _FixSingle(x,y, &TileAdrPtr[0]);

  if (y > 0) {
    _FixSingle(x, y-1, &TileAdrPtr[-1]);
  }

  if (x < (WORLD_X - 1)) {
    _FixSingle(x+1, y, &TileAdrPtr[WORLD_Y]);
  }

  if (y < (WORLD_Y - 1)) {
    _FixSingle(x, y+1, &TileAdrPtr[1]);
  }

  if (x > 0) {
    _FixSingle(x-1, y, &TileAdrPtr[-WORLD_Y]);
  }

}


/* comefrom: _FixZone */
void
_FixSingle(int x, int y, short *TileAdrPtr)
{
  short Tile;
  short adjTile = 0;

  Tile = (*TileAdrPtr) & LOMASK;
  NeutralizeRoad(Tile);
  if ((Tile >= 66) && (Tile <= 76)) {		/* Cleanup Road */

    if (y > 0) {
      Tile = TileAdrPtr[-1];
      NeutralizeRoad(Tile);
      if (((Tile == 237) || ((Tile >= 64) && (Tile <= 78))) &&
	  (Tile != 77) && (Tile != 238) && (Tile != 64))
	adjTile |= 0x0001;
    }

    if (x < (WORLD_X - 1)) {
      Tile = TileAdrPtr[WORLD_Y];
      NeutralizeRoad(Tile);
      if (((Tile == 238) || ((Tile >= 64) && (Tile <= 78))) &&
	  (Tile != 78) && (Tile != 237) && (Tile != 65))
	adjTile |= 0x0002;
    }

    if (y < (WORLD_Y - 1)) {
      Tile = TileAdrPtr[1];
      NeutralizeRoad(Tile);
      if (((Tile == 237) || ((Tile >= 64) && (Tile <= 78))) &&
	  (Tile != 77) && (Tile != 238) && (Tile != 64))
	adjTile |= 0x0004;
    }

    if (x > 0) {
      Tile = TileAdrPtr[-WORLD_Y];
      NeutralizeRoad(Tile);
      if (((Tile == 238) || ((Tile >= 64) && (Tile <= 78))) &&
	  (Tile != 78) && (Tile != 237) && (Tile != 65))
	adjTile |= 0x0008;
    }

    (*TileAdrPtr) = _RoadTable[adjTile] | BULLBIT | BURNBIT;
    return;
  }

  if ((Tile >= 226) && (Tile <= 236)) {		/* Cleanup Rail */

    if (y > 0) {
      Tile = TileAdrPtr[-1];
      NeutralizeRoad(Tile);
      if ((Tile >= 221) && (Tile <= 238) &&
	  (Tile != 221) && (Tile != 237) && (Tile != 224))
	adjTile |= 0x0001;
    }

    if (x < (WORLD_X - 1)) {
      Tile = TileAdrPtr[WORLD_Y];
      NeutralizeRoad(Tile);
      if ((Tile >= 221) && (Tile <= 238) &&
	  (Tile != 222) && (Tile != 238) && (Tile != 225))
	adjTile |= 0x0002;
    }

    if (y < (WORLD_Y - 1)) {
      Tile = TileAdrPtr[1];
      NeutralizeRoad(Tile);
      if ((Tile >= 221) && (Tile <= 238) &&
	  (Tile != 221) && (Tile != 237) && (Tile != 224))
	adjTile |= 0x0004;
    }

    if (x > 0) {
      Tile = TileAdrPtr[-WORLD_Y];
      NeutralizeRoad(Tile);
      if ((Tile >= 221) && (Tile <= 238) &&
	  (Tile != 222) && (Tile != 238) && (Tile != 225))
	adjTile |= 0x0008;
    }

    (*TileAdrPtr) = _RailTable[adjTile] | BULLBIT | BURNBIT;
    return;
  }

  if ((Tile >= 210) && (Tile <= 220)) {		/* Cleanup Wire */

    if (y > 0) {
      Tile = TileAdrPtr[-1];
      if (Tile & CONDBIT) {
	NeutralizeRoad(Tile);
	if ((Tile != 209) && (Tile != 78) && (Tile != 222))
	  adjTile |= 0x0001;
      }
    }

    if (x < (WORLD_X - 1)) {
      Tile = TileAdrPtr[WORLD_Y];
      if (Tile & CONDBIT) {
	NeutralizeRoad(Tile);
	if ((Tile != 208) && (Tile != 77) && (Tile != 221))
	  adjTile |= 0x0002;
      }
    }

    if (y < (WORLD_Y - 1)) {
      Tile = TileAdrPtr[1];
      if (Tile & CONDBIT) {
	NeutralizeRoad(Tile);
	if ((Tile != 209) && (Tile != 78) && (Tile != 222))
	  adjTile |= 0x0004;
      }
    }

    if (x > 0) {
      Tile = TileAdrPtr[-WORLD_Y];
      if (Tile & CONDBIT) {
	NeutralizeRoad(Tile);
	if ((Tile != 208) && (Tile != 77) && (Tile != 221))
	  adjTile |= 0x0008;
      }
    }

    (*TileAdrPtr) = _WireTable[adjTile] | BULLBIT | BURNBIT | CONDBIT;
    return;
  }
}

