#include <stdio.h>
#include <stdlib.h>
#ifndef MS_WIN
#include <unistd.h>

#else

int sleep(int n)
{
  return 0;
}

#endif

#include "defs.h"
#include "world.h"
#include "player.h"
#include "trans.h"
#include "move.h"
#include "units.h"
#include "graph.h"
#include "keyboard.h"
#include "MsgQ.h"
#include "city.h"
#include "display.h"
#include "misc.h"
#include "rules.h"
#include "savefile.h"

void NetFail(char *str)
{
  printf("Network failure %s\n");
  exit(1);
}

static void FixX(int &x)
{
  x = (x-HorizSpace)/SquareWidth;
  if (x < 0) x = 0;
  if (x >= SquaresWide) x = SquaresWide-1;
}

static void FixY(int &y)
{
  y = (y-VertSpace)/SquareHeight;
  if (y < 0) y = 0;
  if (y >= SquaresHigh) y = SquaresHigh-1;
}

static void ShowPoint(int x, int y)
{
  int wx, wy;
  if (x >= HorizSpace || y < VertSpace || y >= VertSpace+64) {
    FixX(x);
    FixY(y);
    display->TranslateWorld(x, y, wx, wy);
  }
  else {
    y -= VertSpace;
    wx = (x*world->MaxX())/HorizSpace;
    wy = (y*world->MaxY())/64;
  }
  display->Center(wx, wy);
}

// colors for players
static char *color_tbl[] = {
  "#e8e8e8e8e8e8", "#0c0ce0e0e8e8",
  "#ad00ff002f00", "#e8e8e8e8e8e8",
  "NavyBlue", "#e8e8e8e8e8e8",
  "DarkGray", "#e8e8e0e0e8e8" };

Player::Player(char *Name, int Id)
{
  name = Name;
  sprintf(color1Str, "x c %s", color_tbl[Id*2]);
  sprintf(color2Str, "x c %s", color_tbl[Id*2+1]);
  id = Id;
  cityPic = 0;
  doneMoving = 0;
  govt = DESPOT;
  tax = 50;
  money = 0;
  initiatedSave = 0;

  templeEffect = 1;
  cathedralEffect = 4;

  if (!savedGame)
    InitialScience(id, discovered, canDiscover, canBuild);
  researching = NULL;

  scienceAcc = 0;
  needScience = 10;

  state = NORMAL;
}

Player::~Player()
{
  delete name;
}

int Player::HasWonder(char *name)
{
  for (Lister<charp> l = wonders; l; l.Next())
    if (strcmp(l.Elem(), name) == 0) return 1;
  return 0;
}

void Player::DeleteWonder(char *name)
{
  for (Lister<charp> l = wonders; l; l.Next())
    if (strcmp(l.Elem(), name) == 0) {
      l.Delete();
      return;
    }
}

long Player::CompilePic(char **pic)
{
  char *ptr1 = pic[1], *ptr2 = pic[2];
  color1Str[0] = ptr1[0];
  color2Str[0] = ptr2[0];
  pic[1] = color1Str;
  pic[2] = color2Str;
  long handle = screen->CompilePixmap(pic);
  pic[1] = ptr1;
  pic[2] = ptr2;
  return handle;
}

// create the first settler for the player
void Player::Setup()
{
  // find a nice random place on the map which has at least a
  // 5x5 block of land around it
  Debug('i', "Trying to setup\n");
  int x, y;
  while (1) {
    x = rand() % world->MaxX();
    y = rand() % world->MaxY();
    int goodspot = 1;
    for (int x1 = -2; x1 <= 2; ++x1) for (int y1 = -2; y1 <= 2; ++y1)
      if (world->Terrain((x+x1+world->MaxX())%world->MaxX(),
			 (y+y1+world->MaxY())%world->MaxY()) == WATER)
	{ goodspot = 0; break; }
    if (goodspot) break;
  }
  // Unit constructor automatically adds it to the world and to the
  // list of units for the player
  Debug('i', "found spot %d %d\n", x, y);
  Unit *settler = NewUnit(SETTLER, id, trans->New(), 0, 0, x, y);
  moveQ = new MsgQ;
  *moveQ << PieceMove(settler->id, PIECE_CREATE, x, y, SETTLER,
		      0, 0);
}

// send all the moves across
void Player::SendMoves(MsgQ *q)
{
  *q << "moves";

  Debug('p', "sending moves\n");
  *q << *moveQ;
  delete moveQ;
  moveQ = NULL;
}

// show the moves made
void Player::ShowMoves(MsgQ *q, MsgQ *outQ)
{
  City *city;
  Unit *unit;
  char *name;
  int fortStat;
  while (q->Count() > 0) {
    Msg m;
    *q >> m;
    PieceMove *move = (PieceMove*)m.buf;
    move->HostOrder();
    switch (move->oper) {
    case PIECE_CREATE:
      unit = NewUnit(move->type, id, move->id, move->veteran,
		     move->city, move->x, move->y);
      Debug('u', "Created new unit type %d, id %ld, at %d %d\n", move->type,
	    unit->id, move->x, move->y);
      break;
    case PIECE_MOVE:
      // moving into a city automatically captures it
      // if a city is captured and it is the city for the player whose
      // civ this is, MoveUnit will tell the city to send its info across
      // it must tell the server to send the message to the owner of the
      // piece only
      MoveUnit(trans->TransUnit(move->id), move->x, move->y, outQ);
      sleep(1);
      break;
    case PIECE_DIE:
      delete trans->TransUnit(move->id);
      break;
    case CITY_CREATE:
      *q >> name;
      city = new City(name, move->id, move->x, move->y, id);
      break;
    case CITY_DIE:
      delete trans->TransCity(move->id);
      break;
    case CITY_SIZE:
      city = trans->TransCity(move->id);
      city->size = move->x;
      city->DrawIfVisible();
      break;
    case CITY_WORK_MAP:
      city = trans->TransCity(move->id);
      city->TransWorkMap(q);
      break;
    case UNIT_UPDATE:
      unit = trans->TransUnit(move->id);
      fortStat = unit->Fortified();
      if (move->x)
	unit->Fortify();
      else
	unit->UnFortify();
      if (move->y) unit->MakeVeteran();
      if (fortStat != unit->Fortified())
	unit->DrawIfVisible();
      break;
    case DISCOVERY: // some player has discovered something
      {
	*q >> name;
	Player *p = players[move->id];
	Discover(move->id, name, p->discovered, p->canDiscover,
		 p->canBuild);
	delete name;
	break;
      }
    case WONDER: // someone else has built a wonder
      {
	*q >> name;
	Player *p = players[move->id];
	BuildObject *obj = buildObjects.Find(StrKey(name));
	obj->built = 1;
	p->wonders.Insert(obj->name);
	AddMessage(players[playerId]->messages, "%s build %s\n",
		   p->name, obj->name);
	delete name;
      }
    case TERRAIN_CHANGE:
      world->Set(move->x, move->y, move->type);
      if (world->Visible(move->x, move->y) &&
	  display->Visible(move->x, move->y)) {
	int sx, sy;
	display->TranslateScreen(move->x, move->y, sx, sy);
	world->Draw(move->x, move->y, 1, 1, sx, sy);
      }
      break;
    }
    delete move;
  }
}

void Player::InitMoveTurn()
{
  Debug('p', "initmoveturn for %d\n", id);
  if (initiatedSave) {
    MsgQ *q = new MsgQ;
    *q << "save_game";
    SendQ(q);
    CreateSaveFile();
    acks_left = numPlayers-1;
    screen->DisplayMessage("Waiting for save", 0);
  }
  else {
    MsgQ *q = new MsgQ;
    *q << "myturn";
    *q << id;
    SendQ(q);
    moveQ = new MsgQ;
    acks_left = numPlayers-1;
    screen->DisplayMessage("Your Turn", 0);
    doneMoving = 0;
  }
}

void Player::MoveTurnMesg(MsgQ *q)
{
  char *mesg;
  *q >> mesg;
  if (initiatedSave && playerId == 0 && strcmp(mesg, "done_save") == 0) {
    if (--acks_left <= 0)
      exit(0);
  }
  if (strcmp(mesg, "yourturn_ack") == 0) {
    if (--acks_left <= 0) {
      // startup moveturn

      if (researching == NULL) {
	state = IN_SELECT;
	screen->CreateChoiceFrame("Choose Research", canDiscover);
      }

      UpdateCities();
      display->ShowPlayerInfo();

      currUnit = NULL;
      Lister<ulong> unitl = units;
      while (unitl) {
	Unit *ptr = trans->TransUnit(unitl.Elem());
	ptr->numMoves = ptr->Moves();
	ptr->CompleteOrder();
	if (currUnit == NULL && ptr->numMoves > 0) currUnit = ptr;
	unitl.Next();
      }
      if (currUnit == NULL)
	screen->DisplayMessage("End of Turn, press Enter", 0);
      else if (state != IN_SELECT) {
	display->ShowPiece(currUnit->x, currUnit->y, currUnit->id);
	display->EnableCursor(currUnit->x, currUnit->y, currUnit->id);
      }
    }
  }
  else if (strcmp(mesg, "got_moves") == 0) {
    // if we took over any cities of this guy there will be more messages
    if (q->Count() > 0)
      GetCapturedCity(q, this);
    if (--acks_left <= 0) {
      currTurn = (currTurn+1) % numPlayers;
      doneMoving = 0;
      InitCityTurn();
    }
  }
  else if (strcmp(mesg, "city_info") == 0) {
    // get captured city info here
  }
  delete mesg;
  delete q;
  return;
}

void Player::MoveTurnKey(int event, int x, int y, char *data)
{
  int showPiece = 1;
  display->DisableCursor();
  if (state == IN_SELECT && event == SELECT) { // selecting science
    Debug('p', "Selected science %s\n", data);
    Science *ptr = sciences.Find(StrKey(data));
    researching = ptr->name;
    display->ShowPlayerInfo();
    state = NORMAL;
  }
  else if (event == MOUSE_RIGHT) { // right button, center the square
    ShowPoint(x, y);
    showPiece = 0; // if invisible don't show
  }
  else if (event == MOUSE_LEFT && !doneMoving) {
    // check for fortified or sleeping pieces and wake them up
    FixX(x); FixY(y);
    int wx, wy;
    Unit *found = NULL;
    display->TranslateWorld(x, y, wx, wy);
    for (Lister<ulong> l = world->Units(wx, wy); l; l.Next()) {
      Unit *ptr = trans->TransUnit(l.Elem());
      if (ptr->currOrder == 's') {
	ptr->currOrder = ' ';
	ptr->numMoves = ptr->Moves();
	found = ptr;
      }
      else if (ptr->currOrder == 'f') {
	ptr->currOrder = ' ';
	ptr->UnFortify();
	ptr->numMoves = ptr->Moves();
	found = ptr;
	// tell other players
	*moveQ << PieceMove(ptr->id, UNIT_UPDATE, 0,
			    ptr->Veteran() != 0 ? 1 : 0);
      }
    }
    if ((currUnit == NULL || currUnit->numMoves <= 0) && found != NULL)
      currUnit = found;
  }
  else if (event == KEYBOARD && !doneMoving) {
    int key = x;
    Lister<ulong> unitl = units;
    if (currUnit != NULL) {
      if (MovementKey(key)) {
	int newx = (currUnit->x+MoveXDir(key)+world->MaxX()) % world->MaxX();
	int newy = (currUnit->y+MoveYDir(key)+world->MaxY()) % world->MaxY();
	int terrain = world->Terrain(newx, newy);

	if (currUnit->CanMoveOn(terrain, newx, newy)) {
	  double cost = currUnit->MoveCost(terrain, currUnit->numMoves,
					   currUnit->x, currUnit->y,
					   newx, newy);
	  if (cost < 0)
	    currUnit->numMoves = 0;
	  else {
	    currUnit->numMoves -= cost;
	    if (!MoveUnit(currUnit, newx, newy, NULL))
	      currUnit = NULL;
	    sleep(1);
	  }
	}
	else {
	  // see if the unit can attack an enemy unit
	  Lister<ulong> unitsl = world->Units(newx, newy);
	  int canAttack = 0;
	  while (unitsl && !canAttack) {
	    Unit *ptr = trans->TransUnit(unitsl.Elem());
	    if (ptr->ownerId != playerId && currUnit->CanAttack(ptr->Type()))
	      canAttack = 1;
	    unitsl.Next();
	  }
	  if (canAttack) {
	    --currUnit->numMoves;
	    if (!MoveUnit(currUnit, newx, newy, NULL))
	      currUnit = NULL;
	    sleep(1);
	  }
	}
	if (currUnit != NULL && currUnit->numMoves <= 0)
	  key = 'n';
      }
      if (key == 'n' && currUnit != NULL) { // select next unit
	Lister<ulong> unitl = units;
	while (unitl && unitl.Elem() != currUnit->id) unitl.Next();
	if (unitl) unitl.Next();
	currUnit = NULL;
	while (unitl &&
	       (currUnit = trans->TransUnit(unitl.Elem()))->numMoves <= 0)
	  unitl.Next();
      }
      if (currUnit != NULL && !currUnit->Order(key)) {
	Debug('p', "Killing unit %d\n", currUnit->id);
	delete currUnit;
	currUnit = NULL;
      }
    }
    if (currUnit == NULL || currUnit->numMoves <= 0) {
      unitl = units;
      while (unitl &&
	     (currUnit = trans->TransUnit(unitl.Elem()))->numMoves <= 0)
	unitl.Next();
    }
    if (currUnit == NULL || currUnit->numMoves <= 0) {
      // move turn is done
      currUnit = NULL;
      screen->DisplayMessage("Waiting for Acknowledgement", 0);
      MsgQ *q = new MsgQ;
      *q << "turn_done";
      *q << id;
      SendMoves(q);
      SendQ(q);
      acks_left = numPlayers-1;
      doneMoving = 1;
    }
  }
  if (!doneMoving && state != IN_SELECT && currUnit != NULL && currUnit->numMoves > 0) {
    if (showPiece)
      display->ShowPiece(currUnit->x, currUnit->y, currUnit->id);
    if (display->Visible(currUnit->x, currUnit->y))
      display->EnableCursor(currUnit->x, currUnit->y, currUnit->id);
  }
}

City *Player::FindCityWithMsg()
{
  for (Lister<ulong> l = citys; l; l.Next()) {
    City *city = trans->TransCity(l.Elem());
    if (city->messages) return city;
  }
  return NULL;
}

void Player::InitCityTurn()
{
  Debug('p', "InitCityTurn for %d\n", id);
  screen->DisplayMessage(players[currTurn]->name, 0);
  City *city;
  if ((city = FindCityWithMsg()) != NULL) {
    display->ShowCityInfo(city);
    screen->MessageBox(city->messages.RemoveHead());
  }
  else if (messages)
    screen->MessageBox(messages.RemoveHead());
}

// called when a key is pressed in the city turn
void Player::CityTurnKey(int event, int x, int y, char *data)
{
  if (event == MESG_READ) {
    if (messages)
      screen->MessageBox(messages.RemoveHead());
    else {
      City *city = display->CurrCity();
      if (city->messages)
	screen->MessageBox(city->messages.RemoveHead());
    }
    return;
  }
  if (state == IN_SELECT) {
    if (event != SELECT) return;
    if (selecting == TAX_RATE) {
      sscanf(data, "Tax %d\n", &tax);
      for (Lister<ulong> l = citys; l; l.Next())
	trans->TransCity(l.Elem())->ComputeEcon();
      display->ShowPlayerInfo();
      state = NORMAL;
    }
    else {
      Debug('p', "Selected building %s\n", data);
      City *city = display->CurrCity();
      BuildObject *obj = buildObjects.Find(StrKey(data));
      if (obj != NULL)
	Debug('p', "Found obj %s\n", obj->name);
      city->WorkOn(obj->name);
      state = NORMAL;
      city->ShowWorkInProg();
    }
    return;
  }
  if (event == MOUSE_LEFT && display->InMap()) {
    FixX(x); FixY(y);
    int wx, wy;
    display->TranslateWorld(x, y, wx, wy);
    if (world->WhichCity(wx, wy) != 0) {
      City *city = trans->TransCity(world->WhichCity(wx, wy));
      if (city->ownerId != id) return;
      display->ShowCityInfo(city);
    }
    return;
  }
  if (event == MOUSE_LEFT && display->InCity()) {
    x = (x-120)/SquareWidth;
    y = (y-24)/SquareHeight;
    if (x < 0 || x > 4 || y < 0 || y > 4) return;
    x -= 2;
    y -= 2;
    Debug('c', "Clicked on square %d %d\n", x, y);
    if (x == 0 && y == 0) return;
    City *city = display->CurrCity();
    int wx = (city->x+x+world->MaxX())%world->MaxX();
    int wy = (city->y+y+world->MaxY())%world->MaxY();

    if (world->Working(wx, wy) == city->id) { // take off
      world->Working(wx, wy) = 0;
      city->ComputeEcon();
      city->DrawCityMap();
      city->ShowPeople();
      city->ShowEcon();
    }
    else if (world->Working(wx, wy) == 0) {
      city->ComputeEcon();
      if (city->elvi <= 0) return;
      world->Working(wx,  wy) = city->id;
      city->ComputeEcon();
      city->DrawCityMap();
      city->ShowPeople();
      city->ShowEcon();
    }
    return;
  }
  if (event == MOUSE_RIGHT && display->InMap()) {
    ShowPoint(x, y);
    return;
  }
  if (event != KEYBOARD) return;
  int ch = x;
  if (ch == 'a' && players[currTurn]->doneMoving) {
    // accepted his moves, show them, if he captured our city
    // we will send the info for it across too
    display->ShowMap();
    Debug('p', "showing players moves\n");
    players[currTurn]->doneMoving = 0;

    MsgQ *q = new MsgQ;
    *q << "got_moves";
    players[currTurn]->ShowMoves(players[currTurn]->unDispQ, q);
    SendQ(q);

    // next player's turn
    currTurn = (currTurn+1) % numPlayers;
    Debug('p', "currTurn %d, my id %d\n", currTurn, id);
    if (currTurn == id)
      InitMoveTurn();
  }
  else if (ch == 'c' && display->InCity()) { // change working on
    City *city = display->CurrCity();
    List<charp> build = city->CanBuild();
    state = IN_SELECT;
    selecting = CITY_BUILDING;
    screen->CreateChoiceFrame("Select ...", build);
    build.Delete();
  }
  else if (ch == 't' && display->InMap()) {
    List<charp> taxl;
    for (int i = 10; i >= 0; --i) {
      char *str = new char[20];
      sprintf(str, "Tax %d%%", i*10);
      taxl.Insert(str);
    }
    state = IN_SELECT;
    selecting = TAX_RATE;
    screen->CreateChoiceFrame("Set Tax Rate ...", taxl);
    while (taxl) delete taxl.RemoveHead();
  }
  else if (ch == 'r') {
    world->DrawMainMap();
    screen->Refresh();
  }
  else if (MovementKey(ch) && display->InMap()) {
    int xinc = MoveXDir(ch), yinc = MoveYDir(ch);
    if (yinc < 0) display->ScrollUp();
    if (yinc > 0) display->ScrollDown();
    if (xinc < 0) display->ScrollLeft();
    if (xinc > 0) display->ScrollRight();
    screen->Refresh();
  }
  else if (ch == 'm' && display->InCity()) {
    City *city;
    if ((city = FindCityWithMsg()) != NULL) {
      display->ShowCityInfo(city);
      screen->MessageBox(city->messages.RemoveHead());
    }
    else
      display->ShowMap();
  }
  else if (ch == 'S') {
    if (playerId == 0)
      initiatedSave = 1;
    else if (initiatedSave) {
      CreateSaveFile();
      exit(0);
    }
  }
}

// message in city turn
void Player::CityTurnMesg(MsgQ *q)
{
  char *mesg;
  *q >> mesg;
  if (strcmp(mesg, "myturn") == 0) { // acknowledge a moveturn
    int player;
    *q >> player;
    if (player != currTurn)
      NetFail("CityTurn");
    MsgQ *q1 = new MsgQ;
    *q1 << "yourturn_ack";
    SendQ(q1);
    delete q;
  }
  else if (strcmp(mesg, "turn_done") == 0) {
    int player;
    *q >> player;
    if (player != currTurn)
      NetFail("CityTurn");
    char *str;
    *q >> str;
    delete str;
    players[currTurn]->unDispQ = q;
    players[currTurn]->doneMoving = 1;
    screen->DisplayMessage("Done", 0);
    Debug('p', "got turn done from %d\n", currTurn);
  }
  else if (playerId != 0 && strcmp(mesg, "save_game") == 0) {
    screen->DisplayMessage("Got Save Request", 0);
    initiatedSave = 1;
  }
  else
    delete q;
  delete mesg;
}

// update cities
// send the list of places where they are working to everyone too
void Player::UpdateCities()
{
  Lister<ulong> cityl = citys;
  while (cityl) {
    City *city = trans->TransCity(cityl.Elem());
    if (city->ownerId != id || !city->Update()) { // lost this city
      while (city->units) {
	Unit *unit = trans->TransUnit(city->units.RemoveHead());
	*moveQ << PieceMove(unit->id, PIECE_DIE, 0, 0);
	delete unit;
      }
      if (city->ownerId == id) { // city died
	*moveQ << PieceMove(city->id, CITY_DIE, 0, 0);
	cityl.Delete(); // MUST do this first
	delete city;
      }
      else
	cityl.Delete();
    }
    else {
      *moveQ << PieceMove(city->id, CITY_WORK_MAP, 0, 0);
      int len;
      char *cmap = city->MakeWorkMap(len);
      *moveQ << Msg(cmap, len);
      cityl.Next();
    }
  }
  if (scienceAcc >= needScience && researching != NULL) {
    scienceAcc -= needScience;
    if (scienceAcc > needScience)
      scienceAcc = needScience;
    Discover(id, researching, discovered, canDiscover, canBuild);
    *moveQ << PieceMove(id, DISCOVERY, 0, 0);
    *moveQ << researching;
    researching = NULL;
  }
}

void Player::WonderEffect(City *city)
{
  for (Lister<charp> l = wonders; l; l.Next()) {
    BuildObject *obj = buildObjects.Find(StrKey(l.Elem()));
    if (WonderObsolete(obj, id)) {
      switch (obj->type) {
      case COLOSSUS:
	if (city->HasBuilding(obj->name))
	  city->tradeBonus = 0;
	break;
      case MICHELANGELO:
	cathedralEffect = 4;
	break;
      case ORACLE:
	templeEffect = 2;
      }
      continue;
    }
    switch (obj->type) {
    case COPERNICUS:
      if (city->HasBuilding(obj->name))
	city->science *= 2;
      break;
    case CUREFORCANCER:
      ++city->happy;
      break;
    case HANGINGGARDENS:
      ++city->happy;
      break;
    case HOOVERDAM:
      if (city->HasBuilding("Factory"))
	city->prod = int(city->prod*1.5);
      break;
    case ISAACNEWTON:
      if (city->HasBuilding("University"))
	city->science = int(city->science*1.5);
      break;
    case JSBACH:
      city->goondas -= 2;
      if (city->goondas < 0) city->goondas = 0;
      break;
    case MICHELANGELO:
      cathedralEffect = 6;
    case ORACLE:
      templeEffect = 4;
      break;
    case SETIPROGRAM:
      city->science = int(city->science*1.5);
      break;
    case SHAKESPEARE:
      if (city->HasBuilding(obj->name))
	city->goondas = 0;
      break;
    }
  }
}

static char *LookupScience(char *str)
{
  Debug('p', "Looking up science %s\n", str);
  Science *sc = sciences.Find(StrKey(str));
  delete str;
  if (sc == NULL)
    return NULL;
  return sc->name;
}

void Player::Save()
{
  WriteUShort(govt);
  WriteUShort(tax);
  WriteUShort(money);

  WriteUShort(scienceAcc);
  WriteUShort(needScience);
  WriteString(researching == NULL ? "X" : researching);

  WriteUChar(templeEffect);
  WriteUChar(cathedralEffect);

  WriteUShort(discovered.Count());
  for (Lister<charp> l = discovered; l; l.Next())
    WriteString(l.Elem());

  WriteUShort(canDiscover.Count());
  for (l = canDiscover; l; l.Next())
    WriteString(l.Elem());

  WriteUShort(canBuild.Count());
  for (l = canBuild; l; l.Next())
    WriteString(l.Elem());

  WriteUShort(wonders.Count());
  for (l = wonders; l; l.Next())
    WriteString(l.Elem());

  WriteUShort(citys.Count());
  for (Lister<ulong> l1 = citys; l1; l1.Next())
    trans->TransCity(l1.Elem())->Save();

  WriteUShort(units.Count());
  for (l1 = units; l1; l1.Next())
    trans->TransUnit(l1.Elem())->Save();
}

void Player::Restore()
{
  Debug('s', "Restoring player %d\n", id);
  govt = ReadUShort();
  tax = ReadUShort();
  money = ReadUShort();

  scienceAcc = ReadUShort();
  needScience = ReadUShort();
  researching = LookupScience(ReadString());
  Debug('s', "Restored upto researching %s\n", researching);

  templeEffect = ReadUChar();
  cathedralEffect = ReadUChar();

  int n = ReadUShort();
  Debug('s', "Restoring %d discovered\n", n);
  for (; n > 0; --n) {
    char *name = LookupScience(ReadString());
    discovered.Insert(name);
    Science *sc = sciences.Find(StrKey(name));
    sc->discovered |= 1 << id;
    if (sc->wonderObsolete != NULL)
      buildObjects.Find(StrKey(sc->wonderObsolete))->isObsolete = 1;
  }

  n = ReadUShort();
  Debug('s', "Restoring %d can discover\n", n);
  for (; n > 0; --n)
    canDiscover.Insert(LookupScience(ReadString()));

  n = ReadUShort();
  Debug('s', "Restoring %d can build\n", n);
  for (; n > 0; --n) {
    char *str = ReadString();
    BuildObject *obj = buildObjects.Find(StrKey(str));
    delete str;
    canBuild.Insert(obj->name);
  }

  n = ReadUShort();
  Debug('s', "Restoring %d wonders\n", n);
  for (; n > 0; --n) {
    char *str = ReadString();
    BuildObject *obj = buildObjects.Find(StrKey(str));
    delete str;
    wonders.Insert(obj->name);
    obj->built = 1;
  }

  n = ReadUShort();
  Debug('s', "Restoring %d cities\n", n);
  for (; n > 0; --n)
    City *city = new City; // default constructor reads from save file

  n = ReadUShort();
  Debug('s', "Restoring %d units\n", n);
  for (; n > 0; --n)
    Unit *unit = new Unit; // default constructor reads from save file

  // compute economies
  if (playerId == id)
    for (Lister<ulong> l = citys; l; l.Next())
      trans->TransCity(l.Elem())->ComputeEcon();
}
