# players.cpp

Go to the documentation of this file.

    00001 /* $Id: players.cpp 14347 2008-09-16 20:29:09Z rubidium $ */
    00002
    00005 #include "stdafx.h"
    00006 #include "openttd.h"
    00007 #include "engine.h"
    00008 #include "player_func.h"
    00009 #include "player_gui.h"
    00010 #include "town.h"
    00011 #include "station.h"
    00012 #include "news.h"
    00013 #include "saveload.h"
    00014 #include "command_func.h"
    00015 #include "network/network.h"
    00016 #include "network/network_internal.h"
    00017 #include "variables.h"
    00018 #include "engine.h"
    00019 #include "ai/ai.h"
    00020 #include "player_face.h"
    00021 #include "group.h"
    00022 #include "window_func.h"
    00023 #include "tile_map.h"
    00024 #include "strings_func.h"
    00025 #include "gfx_func.h"
    00026 #include "functions.h"
    00027 #include "date_func.h"
    00028 #include "vehicle_func.h"
    00029 #include "sound_func.h"
    00030 #include "autoreplace_func.h"
    00031 #include "autoreplace_gui.h"
    00032 #include "string_func.h"
    00033 #include "ai/default/default.h"
    00034 #include "ai/trolly/trolly.h"
    00035 #include "road_func.h"
    00036 #include "rail.h"
    00037
    00038 #include "table/strings.h"
    00039 #include "table/sprites.h"
    00040
    00041 Player _players[MAX_PLAYERS];
    00042 PlayerByte _local_player;
    00043 PlayerByte _current_player;
    00044 /* NOSAVE: can be determined from player structs */
    00045 byte _player_colors[MAX_PLAYERS];
    00046 PlayerFace _player_face;
    00047 HighScore _highscore_table[5][5]; // 4 difficulty-settings (+ network); top 5
    00048
    00055 void SetLocalPlayer(PlayerID new_player)
    00056 {
    00057   /* Player could also be PLAYER_SPECTATOR or OWNER_NONE */
    00058   assert(IsValidPlayer(new_player) || new_player == PLAYER_SPECTATOR || new_player == OWNER_NONE);
    00059
    00060   _local_player = new_player;
    00061
    00062   /* Do not update the patches if we are in the intro GUI */
    00063   if (IsValidPlayer(new_player) && _game_mode != GM_MENU) {
    00064     const Player *p = GetPlayer(new_player);
    00065     _patches.autorenew        = p->engine_renew;
    00066     _patches.autorenew_months = p->engine_renew_months;
    00067     _patches.autorenew_money  = p->engine_renew_money;
    00068     InvalidateWindow(WC_GAME_OPTIONS, 0);
    00069   }
    00070 }
    00071
    00072 bool IsHumanPlayer(PlayerID pi)
    00073 {
    00074   return !GetPlayer(pi)->is_ai;
    00075 }
    00076
    00077
    00078 uint16 GetDrawStringPlayerColor(PlayerID player)
    00079 {
    00080   /* Get the color for DrawString-subroutines which matches the color
    00081    * of the player */
    00082   if (!IsValidPlayer(player)) return _colour_gradient[COLOUR_WHITE][4] | IS_PALETTE_COLOR;
    00083   return (_colour_gradient[_player_colors[player]][4]) | IS_PALETTE_COLOR;
    00084 }
    00085
    00086 void DrawPlayerIcon(PlayerID p, int x, int y)
    00087 {
    00088   DrawSprite(SPR_PLAYER_ICON, PLAYER_SPRITE_COLOR(p), x, y);
    00089 }
    00090
    00109 PlayerFace ConvertFromOldPlayerFace(uint32 face)
    00110 {
    00111   PlayerFace pf = 0;
    00112   GenderEthnicity ge = GE_WM;
    00113
    00114   if (HasBit(face, 31)) SetBit(ge, GENDER_FEMALE);
    00115   if (HasBit(face, 27) && (HasBit(face, 26) == HasBit(face, 19))) SetBit(ge, ETHNICITY_BLACK);
    00116
    00117   SetPlayerFaceBits(pf, PFV_GEN_ETHN,    ge, ge);
    00118   SetPlayerFaceBits(pf, PFV_HAS_GLASSES, ge, GB(face, 28, 3) <= 1);
    00119   SetPlayerFaceBits(pf, PFV_EYE_COLOUR,  ge, HasBit(ge, ETHNICITY_BLACK) ? 0 : ClampU(GB(face, 20, 3), 5, 7) - 5);
    00120   SetPlayerFaceBits(pf, PFV_CHIN,        ge, ScalePlayerFaceValue(PFV_CHIN,     ge, GB(face,  4, 2)));
    00121   SetPlayerFaceBits(pf, PFV_EYEBROWS,    ge, ScalePlayerFaceValue(PFV_EYEBROWS, ge, GB(face,  6, 4)));
    00122   SetPlayerFaceBits(pf, PFV_HAIR,        ge, ScalePlayerFaceValue(PFV_HAIR,     ge, GB(face, 16, 4)));
    00123   SetPlayerFaceBits(pf, PFV_JACKET,      ge, ScalePlayerFaceValue(PFV_JACKET,   ge, GB(face, 20, 2)));
    00124   SetPlayerFaceBits(pf, PFV_COLLAR,      ge, ScalePlayerFaceValue(PFV_COLLAR,   ge, GB(face, 22, 2)));
    00125   SetPlayerFaceBits(pf, PFV_GLASSES,     ge, GB(face, 28, 1));
    00126
    00127   uint lips = GB(face, 10, 4);
    00128   if (!HasBit(ge, GENDER_FEMALE) && lips < 4) {
    00129     SetPlayerFaceBits(pf, PFV_HAS_MOUSTACHE, ge, true);
    00130     SetPlayerFaceBits(pf, PFV_MOUSTACHE,     ge, max(lips, 1U) - 1);
    00131   } else {
    00132     if (!HasBit(ge, GENDER_FEMALE)) {
    00133       lips = lips * 15 / 16;
    00134       lips -= 3;
    00135       if (HasBit(ge, ETHNICITY_BLACK) && lips > 8) lips = 0;
    00136     } else {
    00137       lips = ScalePlayerFaceValue(PFV_LIPS, ge, lips);
    00138     }
    00139     SetPlayerFaceBits(pf, PFV_LIPS, ge, lips);
    00140
    00141     uint nose = GB(face, 13, 3);
    00142     if (ge == GE_WF) {
    00143       nose = (nose * 3 >> 3) * 3 >> 2; // There is 'hole' in the nose sprites for females
    00144     } else {
    00145       nose = ScalePlayerFaceValue(PFV_NOSE, ge, nose);
    00146     }
    00147     SetPlayerFaceBits(pf, PFV_NOSE, ge, nose);
    00148   }
    00149
    00150   uint tie_earring = GB(face, 24, 4);
    00151   if (!HasBit(ge, GENDER_FEMALE) || tie_earring < 3) { // Not all females have an earring
    00152     if (HasBit(ge, GENDER_FEMALE)) SetPlayerFaceBits(pf, PFV_HAS_TIE_EARRING, ge, true);
    00153     SetPlayerFaceBits(pf, PFV_TIE_EARRING, ge, HasBit(ge, GENDER_FEMALE) ? tie_earring : ScalePlayerFaceValue(PFV_TIE_EARRING, ge, tie_earring / 2));
    00154   }
    00155
    00156   return pf;
    00157 }
    00158
    00165 bool IsValidPlayerFace(PlayerFace pf)
    00166 {
    00167   if (!ArePlayerFaceBitsValid(pf, PFV_GEN_ETHN, GE_WM)) return false;
    00168
    00169   GenderEthnicity ge   = (GenderEthnicity)GetPlayerFaceBits(pf, PFV_GEN_ETHN, GE_WM);
    00170   bool has_moustache   = !HasBit(ge, GENDER_FEMALE) && GetPlayerFaceBits(pf, PFV_HAS_MOUSTACHE,   ge) != 0;
    00171   bool has_tie_earring = !HasBit(ge, GENDER_FEMALE) || GetPlayerFaceBits(pf, PFV_HAS_TIE_EARRING, ge) != 0;
    00172   bool has_glasses     = GetPlayerFaceBits(pf, PFV_HAS_GLASSES, ge) != 0;
    00173
    00174   if (!ArePlayerFaceBitsValid(pf, PFV_EYE_COLOUR, ge)) return false;
    00175   for (PlayerFaceVariable pfv = PFV_CHEEKS; pfv < PFV_END; pfv++) {
    00176     switch (pfv) {
    00177       case PFV_MOUSTACHE:   if (!has_moustache)   continue; break;
    00178       case PFV_LIPS:        /* FALL THROUGH */
    00179       case PFV_NOSE:        if (has_moustache)    continue; break;
    00180       case PFV_TIE_EARRING: if (!has_tie_earring) continue; break;
    00181       case PFV_GLASSES:     if (!has_glasses)     continue; break;
    00182       default: break;
    00183     }
    00184     if (!ArePlayerFaceBitsValid(pf, pfv, ge)) return false;
    00185   }
    00186
    00187   return true;
    00188 }
    00189
    00190 void InvalidatePlayerWindows(const Player *p)
    00191 {
    00192   PlayerID pid = p->index;
    00193
    00194   if (pid == _local_player) InvalidateWindow(WC_STATUS_BAR, 0);
    00195   InvalidateWindow(WC_FINANCES, pid);
    00196 }
    00197
    00198 bool CheckPlayerHasMoney(CommandCost cost)
    00199 {
    00200   if (cost.GetCost() > 0) {
    00201     PlayerID pid = _current_player;
    00202     if (IsValidPlayer(pid) && cost.GetCost() > GetPlayer(pid)->player_money) {
    00203       SetDParam(0, cost.GetCost());
    00204       _error_message = STR_0003_NOT_ENOUGH_CASH_REQUIRES;
    00205       return false;
    00206     }
    00207   }
    00208   return true;
    00209 }
    00210
    00211 static void SubtractMoneyFromAnyPlayer(Player *p, CommandCost cost)
    00212 {
    00213   if (cost.GetCost() == 0) return;
    00214   assert(cost.GetExpensesType() != INVALID_EXPENSES);
    00215
    00216   p->player_money -= cost.GetCost();
    00217   p->yearly_expenses[0][cost.GetExpensesType()] += cost.GetCost();
    00218
    00219   if (HasBit(1 << EXPENSES_TRAIN_INC    |
    00220              1 << EXPENSES_ROADVEH_INC  |
    00221              1 << EXPENSES_AIRCRAFT_INC |
    00222              1 << EXPENSES_SHIP_INC, cost.GetExpensesType())) {
    00223     p->cur_economy.income -= cost.GetCost();
    00224   } else if (HasBit(1 << EXPENSES_TRAIN_RUN    |
    00225                     1 << EXPENSES_ROADVEH_RUN  |
    00226                     1 << EXPENSES_AIRCRAFT_RUN |
    00227                     1 << EXPENSES_SHIP_RUN     |
    00228                     1 << EXPENSES_PROPERTY     |
    00229                     1 << EXPENSES_LOAN_INT, cost.GetExpensesType())) {
    00230     p->cur_economy.expenses -= cost.GetCost();
    00231   }
    00232
    00233   InvalidatePlayerWindows(p);
    00234 }
    00235
    00236 void SubtractMoneyFromPlayer(CommandCost cost)
    00237 {
    00238   PlayerID pid = _current_player;
    00239
    00240   if (IsValidPlayer(pid)) SubtractMoneyFromAnyPlayer(GetPlayer(pid), cost);
    00241 }
    00242
    00243 void SubtractMoneyFromPlayerFract(PlayerID player, CommandCost cst)
    00244 {
    00245   Player *p = GetPlayer(player);
    00246   byte m = p->player_money_fraction;
    00247   Money cost = cst.GetCost();
    00248
    00249   p->player_money_fraction = m - (byte)cost;
    00250   cost >>= 8;
    00251   if (p->player_money_fraction > m) cost++;
    00252   if (cost != 0) SubtractMoneyFromAnyPlayer(p, CommandCost(cst.GetExpensesType(), cost));
    00253 }
    00254
    00255 void GetNameOfOwner(Owner owner, TileIndex tile)
    00256 {
    00257   SetDParam(2, owner);
    00258
    00259   if (owner != OWNER_TOWN) {
    00260     if (!IsValidPlayer(owner)) {
    00261       SetDParam(0, STR_0150_SOMEONE);
    00262     } else {
    00263       const Player* p = GetPlayer(owner);
    00264
    00265       SetDParam(0, STR_COMPANY_NAME);
    00266       SetDParam(1, p->index);
    00267     }
    00268   } else {
    00269     const Town* t = ClosestTownFromTile(tile, (uint)-1);
    00270
    00271     SetDParam(0, STR_TOWN);
    00272     SetDParam(1, t->index);
    00273   }
    00274 }
    00275
    00276
    00277 bool CheckOwnership(PlayerID owner)
    00278 {
    00279   assert(owner < OWNER_END);
    00280
    00281   if (owner == _current_player) return true;
    00282   _error_message = STR_013B_OWNED_BY;
    00283   GetNameOfOwner(owner, 0);
    00284   return false;
    00285 }
    00286
    00287 bool CheckTileOwnership(TileIndex tile)
    00288 {
    00289   Owner owner = GetTileOwner(tile);
    00290
    00291   assert(owner < OWNER_END);
    00292
    00293   if (owner == _current_player) return true;
    00294   _error_message = STR_013B_OWNED_BY;
    00295
    00296   /* no need to get the name of the owner unless we're the local player (saves some time) */
    00297   if (IsLocalPlayer()) GetNameOfOwner(owner, tile);
    00298   return false;
    00299 }
    00300
    00301 static void GenerateCompanyName(Player *p)
    00302 {
    00303   TileIndex tile;
    00304   Town *t;
    00305   StringID str;
    00306   Player *pp;
    00307   uint32 strp;
    00308   char buffer[100];
    00309
    00310   if (p->name_1 != STR_SV_UNNAMED) return;
    00311
    00312   tile = p->last_build_coordinate;
    00313   if (tile == 0) return;
    00314
    00315   t = ClosestTownFromTile(tile, (uint)-1);
    00316
    00317   if (t->name == NULL && IsInsideMM(t->townnametype, SPECSTR_TOWNNAME_START, SPECSTR_TOWNNAME_LAST + 1)) {
    00318     str = t->townnametype - SPECSTR_TOWNNAME_START + SPECSTR_PLAYERNAME_START;
    00319     strp = t->townnameparts;
    00320
    00321 verify_name:;
    00322     /* No player must have this name already */
    00323     FOR_ALL_PLAYERS(pp) {
    00324       if (pp->name_1 == str && pp->name_2 == strp) goto bad_town_name;
    00325     }
    00326
    00327     GetString(buffer, str, lastof(buffer));
    00328     if (strlen(buffer) >= 32 || GetStringBoundingBox(buffer).width >= 150)
    00329       goto bad_town_name;
    00330
    00331 set_name:;
    00332     p->name_1 = str;
    00333     p->name_2 = strp;
    00334
    00335     MarkWholeScreenDirty();
    00336
    00337     if (!IsHumanPlayer(p->index)) {
    00338       SetDParam(0, t->index);
    00339       AddNewsItem((StringID)(p->index | NB_BNEWCOMPANY), NEWS_FLAGS(NM_CALLBACK, NF_TILE, NT_COMPANY_INFO, DNC_BANKRUPCY), p->last_build_coordinate, 0);
    00340     }
    00341     return;
    00342   }
    00343 bad_town_name:;
    00344
    00345   if (p->president_name_1 == SPECSTR_PRESIDENT_NAME) {
    00346     str = SPECSTR_ANDCO_NAME;
    00347     strp = p->president_name_2;
    00348     goto set_name;
    00349   } else {
    00350     str = SPECSTR_ANDCO_NAME;
    00351     strp = Random();
    00352     goto verify_name;
    00353   }
    00354 }
    00355
    00356 #define COLOR_SWAP(i,j) do { byte t=colors[i];colors[i]=colors[j];colors[j]=t; } while(0)
    00357
    00358 static const byte _color_sort[16] = {2, 2, 3, 2, 3, 2, 3, 2, 3, 2, 2, 2, 3, 1, 1, 1};
    00359 static const byte _color_similar_1[16] = {8, 6, 255, 12,  255, 0, 1, 1, 0, 13,  11,  10, 3,   9,  15, 14};
    00360 static const byte _color_similar_2[16] = {5, 7, 255, 255, 255, 8, 7, 6, 5, 12, 255, 255, 9, 255, 255, 255};
    00361
    00362 static byte GeneratePlayerColor()
    00363 {
    00364   byte colors[16], pcolor, t2;
    00365   int i,j,n;
    00366   uint32 r;
    00367   Player *p;
    00368
    00369   /* Initialize array */
    00370   for (i = 0; i != 16; i++) colors[i] = i;
    00371
    00372   /* And randomize it */
    00373   n = 100;
    00374   do {
    00375     r = Random();
    00376     COLOR_SWAP(GB(r, 0, 4), GB(r, 4, 4));
    00377   } while (--n);
    00378
    00379   /* Bubble sort it according to the values in table 1 */
    00380   i = 16;
    00381   do {
    00382     for (j = 0; j != 15; j++) {
    00383       if (_color_sort[colors[j]] < _color_sort[colors[j + 1]]) {
    00384         COLOR_SWAP(j, j + 1);
    00385       }
    00386     }
    00387   } while (--i);
    00388
    00389   /* Move the colors that look similar to each player's color to the side */
    00390   FOR_ALL_PLAYERS(p) if (p->is_active) {
    00391     pcolor = p->player_color;
    00392     for (i=0; i!=16; i++) if (colors[i] == pcolor) {
    00393       colors[i] = 0xFF;
    00394
    00395       t2 = _color_similar_1[pcolor];
    00396       if (t2 == 0xFF) break;
    00397       for (i=0; i!=15; i++) {
    00398         if (colors[i] == t2) {
    00399           do COLOR_SWAP(i,i+1); while (++i != 15);
    00400           break;
    00401         }
    00402       }
    00403
    00404       t2 = _color_similar_2[pcolor];
    00405       if (t2 == 0xFF) break;
    00406       for (i = 0; i != 15; i++) {
    00407         if (colors[i] == t2) {
    00408           do COLOR_SWAP(i, i + 1); while (++i != 15);
    00409           break;
    00410         }
    00411       }
    00412       break;
    00413     }
    00414   }
    00415
    00416   /* Return the first available color */
    00417   for (i = 0;; i++) {
    00418     if (colors[i] != 0xFF) return colors[i];
    00419   }
    00420 }
    00421
    00422 static void GeneratePresidentName(Player *p)
    00423 {
    00424   Player *pp;
    00425   char buffer[100], buffer2[40];
    00426
    00427   for (;;) {
    00428 restart:;
    00429
    00430     p->president_name_2 = Random();
    00431     p->president_name_1 = SPECSTR_PRESIDENT_NAME;
    00432
    00433     SetDParam(0, p->index);
    00434     GetString(buffer, STR_PLAYER_NAME, lastof(buffer));
    00435     if (strlen(buffer) >= 32 || GetStringBoundingBox(buffer).width >= 94)
    00436       continue;
    00437
    00438     FOR_ALL_PLAYERS(pp) {
    00439       if (pp->is_active && p != pp) {
    00440         SetDParam(0, pp->index);
    00441         GetString(buffer2, STR_PLAYER_NAME, lastof(buffer2));
    00442         if (strcmp(buffer2, buffer) == 0)
    00443           goto restart;
    00444       }
    00445     }
    00446     return;
    00447   }
    00448 }
    00449
    00450 static Player *AllocatePlayer()
    00451 {
    00452   Player *p;
    00453   /* Find a free slot */
    00454   FOR_ALL_PLAYERS(p) {
    00455     if (!p->is_active) {
    00456       free(p->name);
    00457       free(p->president_name);
    00458       PlayerID i = p->index;
    00459       memset(p, 0, sizeof(Player));
    00460       memset(&_players_ai[i], 0, sizeof(PlayerAI));
    00461       memset(&_players_ainew[i], 0, sizeof(PlayerAiNew));
    00462       p->index = i;
    00463       return p;
    00464     }
    00465   }
    00466   return NULL;
    00467 }
    00468
    00469 void ResetPlayerLivery(Player *p)
    00470 {
    00471   for (LiveryScheme scheme = LS_BEGIN; scheme < LS_END; scheme++) {
    00472     p->livery[scheme].in_use  = false;
    00473     p->livery[scheme].colour1 = p->player_color;
    00474     p->livery[scheme].colour2 = p->player_color;
    00475   }
    00476 }
    00477
    00484 Player *DoStartupNewPlayer(bool is_ai)
    00485 {
    00486   Player *p;
    00487
    00488   p = AllocatePlayer();
    00489   if (p == NULL) return NULL;
    00490
    00491   /* Make a color */
    00492   p->player_color = GeneratePlayerColor();
    00493   ResetPlayerLivery(p);
    00494   _player_colors[p->index] = p->player_color;
    00495   p->name_1 = STR_SV_UNNAMED;
    00496   p->is_active = true;
    00497
    00498   p->player_money = p->current_loan = 100000;
    00499
    00500   p->is_ai = is_ai;
    00501   _players_ai[p->index].state = 5; // AIS_WANT_NEW_ROUTE
    00502   p->share_owners[0] = p->share_owners[1] = p->share_owners[2] = p->share_owners[3] = PLAYER_SPECTATOR;
    00503
    00504   p->avail_railtypes = GetPlayerRailtypes(p->index);
    00505   p->avail_roadtypes = GetPlayerRoadtypes(p->index);
    00506   p->inaugurated_year = _cur_year;
    00507   RandomPlayerFaceBits(p->face, (GenderEthnicity)Random(), false); // create a random player face
    00508
    00509   /* Engine renewal settings */
    00510   p->engine_renew_list = NULL;
    00511   p->renew_keep_length = false;
    00512   p->engine_renew = _patches_newgame.autorenew;
    00513   p->engine_renew_months = _patches_newgame.autorenew_months;
    00514   p->engine_renew_money = _patches_newgame.autorenew_money;
    00515
    00516   GeneratePresidentName(p);
    00517
    00518   InvalidateWindow(WC_GRAPH_LEGEND, 0);
    00519   InvalidateWindow(WC_TOOLBAR_MENU, 0);
    00520   InvalidateWindow(WC_CLIENT_LIST, 0);
    00521
    00522   if (is_ai && (!_networking || _network_server) && _ai.enabled)
    00523     AI_StartNewAI(p->index);
    00524
    00525   memset(p->num_engines, 0, sizeof(p->num_engines));
    00526
    00527   return p;
    00528 }
    00529
    00530 void StartupPlayers()
    00531 {
    00532   /* The AI starts like in the setting with +2 month max */
    00533   _next_competitor_start = _opt.diff.competitor_start_time * 90 * DAY_TICKS + RandomRange(60 * DAY_TICKS) + 1;
    00534 }
    00535
    00536 static void MaybeStartNewPlayer()
    00537 {
    00538   uint n;
    00539   Player *p;
    00540
    00541   /* count number of competitors */
    00542   n = 0;
    00543   FOR_ALL_PLAYERS(p) {
    00544     if (p->is_active && p->is_ai) n++;
    00545   }
    00546
    00547   /* when there's a lot of computers in game, the probability that a new one starts is lower */
    00548   if (n < (uint)_opt.diff.max_no_competitors &&
    00549       n < (_network_server ?
    00550         InteractiveRandomRange(_opt.diff.max_no_competitors + 2) :
    00551         RandomRange(_opt.diff.max_no_competitors + 2)
    00552       )) {
    00553     /* Send a command to all clients to start up a new AI.
    00554      * Works fine for Multiplayer and Singleplayer */
    00555     DoCommandP(0, 1, 0, NULL, CMD_PLAYER_CTRL);
    00556   }
    00557
    00558   /* The next AI starts like the difficulty setting said, with +2 month max */
    00559   _next_competitor_start = _opt.diff.competitor_start_time * 90 * DAY_TICKS + 1;
    00560   _next_competitor_start += _network_server ? InteractiveRandomRange(60 * DAY_TICKS) : RandomRange(60 * DAY_TICKS);
    00561 }
    00562
    00563 void InitializePlayers()
    00564 {
    00565   memset(_players, 0, sizeof(_players));
    00566   for (PlayerID i = PLAYER_FIRST; i != MAX_PLAYERS; i++) {
    00567     _players[i].index = i;
    00568     for (uint j = 0; j < 4; j++) _players[i].share_owners[j] = PLAYER_SPECTATOR;
    00569   }
    00570   _cur_player_tick_index = 0;
    00571 }
    00572
    00573 void OnTick_Players()
    00574 {
    00575   Player *p;
    00576
    00577   if (_game_mode == GM_EDITOR) return;
    00578
    00579   p = GetPlayer((PlayerID)_cur_player_tick_index);
    00580   _cur_player_tick_index = (_cur_player_tick_index + 1) % MAX_PLAYERS;
    00581   if (p->name_1 != 0) GenerateCompanyName(p);
    00582
    00583   if (AI_AllowNewAI() && _game_mode != GM_MENU && !--_next_competitor_start)
    00584     MaybeStartNewPlayer();
    00585 }
    00586
    00587 extern void ShowPlayerFinances(PlayerID player);
    00588
    00589 void PlayersYearlyLoop()
    00590 {
    00591   Player *p;
    00592
    00593   /* Copy statistics */
    00594   FOR_ALL_PLAYERS(p) {
    00595     if (p->is_active) {
    00596       memmove(&p->yearly_expenses[1], &p->yearly_expenses[0], sizeof(p->yearly_expenses) - sizeof(p->yearly_expenses[0]));
    00597       memset(&p->yearly_expenses[0], 0, sizeof(p->yearly_expenses[0]));
    00598       InvalidateWindow(WC_FINANCES, p->index);
    00599     }
    00600   }
    00601
    00602   if (_patches.show_finances && _local_player != PLAYER_SPECTATOR) {
    00603     ShowPlayerFinances(_local_player);
    00604     p = GetPlayer(_local_player);
    00605     if (p->num_valid_stat_ent > 5 && p->old_economy[0].performance_history < p->old_economy[4].performance_history) {
    00606       SndPlayFx(SND_01_BAD_YEAR);
    00607     } else {
    00608       SndPlayFx(SND_00_GOOD_YEAR);
    00609     }
    00610   }
    00611 }
    00612
    00613 static void DeletePlayerStuff(PlayerID pi)
    00614 {
    00615   Player *p;
    00616
    00617   DeletePlayerWindows(pi);
    00618   p = GetPlayer(pi);
    00619   p->name_1 = STR_NULL;
    00620   p->president_name_1 = STR_NULL;
    00621   free(p->name);
    00622   free(p->president_name);
    00623   p->name = NULL;
    00624   p->president_name = NULL;
    00625 }
    00626
    00655 CommandCost CmdSetAutoReplace(TileIndex tile, uint32 flags, uint32 p1, uint32 p2)
    00656 {
    00657   Player *p;
    00658   if (!IsValidPlayer(_current_player)) return CMD_ERROR;
    00659
    00660   p = GetPlayer(_current_player);
    00661   switch (GB(p1, 0, 3)) {
    00662     case 0:
    00663       if (p->engine_renew == HasBit(p2, 0))
    00664         return CMD_ERROR;
    00665
    00666       if (flags & DC_EXEC) {
    00667         p->engine_renew = HasBit(p2, 0);
    00668         if (IsLocalPlayer()) {
    00669           _patches.autorenew = p->engine_renew;
    00670           InvalidateWindow(WC_GAME_OPTIONS, 0);
    00671         }
    00672       }
    00673       break;
    00674     case 1:
    00675       if (Clamp((int16)p2, -12, 12) != (int16)p2) return CMD_ERROR;
    00676       if (p->engine_renew_months == (int16)p2)
    00677         return CMD_ERROR;
    00678
    00679       if (flags & DC_EXEC) {
    00680         p->engine_renew_months = (int16)p2;
    00681         if (IsLocalPlayer()) {
    00682           _patches.autorenew_months = p->engine_renew_months;
    00683           InvalidateWindow(WC_GAME_OPTIONS, 0);
    00684         }
    00685       }
    00686       break;
    00687     case 2:
    00688       if (ClampU(p2, 0, 2000000) != p2) return CMD_ERROR;
    00689       if (p->engine_renew_money == (uint32)p2)
    00690         return CMD_ERROR;
    00691
    00692       if (flags & DC_EXEC) {
    00693         p->engine_renew_money = (uint32)p2;
    00694         if (IsLocalPlayer()) {
    00695           _patches.autorenew_money = p->engine_renew_money;
    00696           InvalidateWindow(WC_GAME_OPTIONS, 0);
    00697         }
    00698       }
    00699       break;
    00700     case 3: {
    00701       EngineID old_engine_type = GB(p2, 0, 16);
    00702       EngineID new_engine_type = GB(p2, 16, 16);
    00703       GroupID id_g = GB(p1, 16, 16);
    00704       CommandCost cost;
    00705
    00706       if (!IsValidGroupID(id_g) && !IsAllGroupID(id_g) && !IsDefaultGroupID(id_g)) return CMD_ERROR;
    00707       if (new_engine_type != INVALID_ENGINE) {
    00708         /* First we make sure that it's a valid type the user requested
    00709          * check that it's an engine that is in the engine array */
    00710         if (!IsEngineIndex(new_engine_type))
    00711           return CMD_ERROR;
    00712
    00713         /* check that the new vehicle type is the same as the original one */
    00714         if (GetEngine(old_engine_type)->type != GetEngine(new_engine_type)->type)
    00715           return CMD_ERROR;
    00716
    00717         /* make sure that we do not replace a plane with a helicopter or vise versa */
    00718         if (GetEngine(new_engine_type)->type == VEH_AIRCRAFT &&
    00719             (AircraftVehInfo(old_engine_type)->subtype & AIR_CTOL) != (AircraftVehInfo(new_engine_type)->subtype & AIR_CTOL))
    00720           return CMD_ERROR;
    00721
    00722         /* make sure that the player can actually buy the new engine */
    00723         if (!HasBit(GetEngine(new_engine_type)->player_avail, _current_player))
    00724           return CMD_ERROR;
    00725
    00726         cost = AddEngineReplacementForPlayer(p, old_engine_type, new_engine_type, id_g, flags);
    00727       } else {
    00728         cost = RemoveEngineReplacementForPlayer(p, old_engine_type,id_g, flags);
    00729       }
    00730
    00731       if (IsLocalPlayer()) InvalidateAutoreplaceWindow(old_engine_type, id_g);
    00732
    00733       return cost;
    00734     }
    00735
    00736     case 4:
    00737       if (Clamp((int16)GB(p1, 16, 16), -12, 12) != (int16)GB(p1, 16, 16)) return CMD_ERROR;
    00738       if (ClampU(p2, 0, 2000000) != p2) return CMD_ERROR;
    00739       if (flags & DC_EXEC) {
    00740         p->engine_renew = HasBit(p1, 15);
    00741         p->engine_renew_months = (int16)GB(p1, 16, 16);
    00742         p->engine_renew_money = (uint32)p2;
    00743
    00744         if (IsLocalPlayer()) {
    00745           _patches.autorenew = p->engine_renew;
    00746           _patches.autorenew_months = p->engine_renew_months;
    00747           _patches.autorenew_money = p->engine_renew_money;
    00748           InvalidateWindow(WC_GAME_OPTIONS, 0);
    00749         }
    00750       }
    00751       break;
    00752     case 5:
    00753       if (p->renew_keep_length == HasBit(p2, 0))
    00754         return CMD_ERROR;
    00755
    00756       if (flags & DC_EXEC) {
    00757         p->renew_keep_length = HasBit(p2, 0);
    00758         if (IsLocalPlayer()) {
    00759           InvalidateWindow(WC_REPLACE_VEHICLE, VEH_TRAIN);
    00760         }
    00761       }
    00762     break;
    00763
    00764   }
    00765   return CommandCost();
    00766 }
    00767
    00788 CommandCost CmdPlayerCtrl(TileIndex tile, uint32 flags, uint32 p1, uint32 p2)
    00789 {
    00790   if (flags & DC_EXEC) _current_player = OWNER_NONE;
    00791
    00792   switch (p1) {
    00793   case 0: { /* Create a new player */
    00794     /* Joining Client:
    00795      * _local_player: PLAYER_SPECTATOR
    00796      * _network_playas/cid = requested company/player
    00797      *
    00798      * Other client(s)/server:
    00799      * _local_player/_network_playas: what they play as
    00800      * cid = requested company/player of joining client */
    00801     Player *p;
    00802 #ifdef ENABLE_NETWORK
    00803     uint16 cid = p2; // ClientID
    00804 #endif /* ENABLE_NETWORK */
    00805
    00806     /* This command is only executed in a multiplayer game */
    00807     if (!_networking) return CMD_ERROR;
    00808
    00809     /* Has the network client a correct ClientID? */
    00810     if (!(flags & DC_EXEC)) return CommandCost();
    00811 #ifdef ENABLE_NETWORK
    00812     if (cid >= MAX_CLIENT_INFO) return CommandCost();
    00813 #endif /* ENABLE_NETWORK */
    00814
    00815     /* Delete multiplayer progress bar */
    00816     DeleteWindowById(WC_NETWORK_STATUS_WINDOW, 0);
    00817
    00818     p = DoStartupNewPlayer(false);
    00819
    00820     /* A new player could not be created, revert to being a spectator */
    00821     if (p == NULL) {
    00822 #ifdef ENABLE_NETWORK
    00823       if (_network_server) {
    00824         NetworkClientInfo *ci = &_network_client_info[cid];
    00825         ci->client_playas = PLAYER_SPECTATOR;
    00826         NetworkUpdateClientInfo(ci->client_index);
    00827       } else if (_local_player == PLAYER_SPECTATOR) {
    00828         _network_playas = PLAYER_SPECTATOR;
    00829       }
    00830 #endif /* ENABLE_NETWORK */
    00831       break;
    00832     }
    00833
    00834     /* This is the joining client who wants a new company */
    00835     if (_local_player != _network_playas && _network_playas == p->index) {
    00836       assert(_local_player == PLAYER_SPECTATOR);
    00837       SetLocalPlayer(p->index);
    00838 #ifdef ENABLE_NETWORK
    00839       if (!StrEmpty(_network_default_company_pass)) {
    00840         char *password = _network_default_company_pass;
    00841         NetworkChangeCompanyPassword(1, &password);
    00842       }
    00843 #endif /* ENABLE_NETWORK */
    00844
    00845       _current_player = _local_player;
    00846
    00847       /* Now that we have a new player, broadcast our autorenew settings to
    00848        * all clients so everything is in sync */
    00849       NetworkSend_Command(0,
    00850         (_patches_newgame.autorenew << 15 ) | (_patches_newgame.autorenew_months << 16) | 4,
    00851         _patches_newgame.autorenew_money,
    00852         CMD_SET_AUTOREPLACE,
    00853         NULL
    00854       );
    00855
    00856       MarkWholeScreenDirty();
    00857     }
    00858
    00859 #ifdef ENABLE_NETWORK
    00860     if (_network_server) {
    00861       /* XXX - UGLY! p2 (pid) is mis-used to fetch the client-id, done at
    00862        * server-side in network_server.c:838, function
    00863        * DEF_SERVER_RECEIVE_COMMAND(PACKET_CLIENT_COMMAND) */
    00864       NetworkClientInfo *ci = &_network_client_info[cid];
    00865       ci->client_playas = p->index;
    00866       NetworkUpdateClientInfo(ci->client_index);
    00867
    00868       if (IsValidPlayer(ci->client_playas)) {
    00869         PlayerID player_backup = _local_player;
    00870         _network_player_info[p->index].months_empty = 0;
    00871
    00872         /* XXX - When a client joins, we automatically set its name to the
    00873          * player's name (for some reason). As it stands now only the server
    00874          * knows the client's name, so it needs to send out a "broadcast" to
    00875          * do this. To achieve this we send a network command. However, it
    00876          * uses _local_player to execute the command as.  To prevent abuse
    00877          * (eg. only yourself can change your name/company), we 'cheat' by
    00878          * impersonation _local_player as the server. Not the best solution;
    00879          * but it works.
    00880          * TODO: Perhaps this could be improved by when the client is ready
    00881          * with joining to let it send itself the command, and not the server?
    00882          * For example in network_client.c:534? */
    00883         _cmd_text = ci->client_name;
    00884         _local_player = ci->client_playas;
    00885         NetworkSend_Command(0, 0, 0, CMD_CHANGE_PRESIDENT_NAME, NULL);
    00886         _local_player = player_backup;
    00887       }
    00888     }
    00889 #endif /* ENABLE_NETWORK */
    00890   } break;
    00891
    00892   case 1: /* Make a new AI player */
    00893     if (!(flags & DC_EXEC)) return CommandCost();
    00894
    00895     DoStartupNewPlayer(true);
    00896     break;
    00897
    00898   case 2: { /* Delete a player */
    00899     Player *p;
    00900
    00901     if (!IsValidPlayer((PlayerID)p2)) return CMD_ERROR;
    00902
    00903     if (!(flags & DC_EXEC)) return CommandCost();
    00904
    00905     p = GetPlayer((PlayerID)p2);
    00906
    00907     /* Only allow removal of HUMAN companies */
    00908     if (IsHumanPlayer(p->index)) {
    00909       /* Delete any open window of the company */
    00910       DeletePlayerWindows(p->index);
    00911
    00912       /* Show the bankrupt news */
    00913       SetDParam(0, p->index);
    00914       AddNewsItem( (StringID)(p->index | NB_BBANKRUPT), NEWS_FLAGS(NM_CALLBACK, 0, NT_COMPANY_INFO, DNC_BANKRUPCY),0,0);
    00915
    00916       /* Remove the company */
    00917       ChangeOwnershipOfPlayerItems(p->index, PLAYER_SPECTATOR);
    00918       p->is_active = false;
    00919     }
    00920   } break;
    00921
    00922   case 3: { /* Merge a company (#1) into another company (#2), elimination company #1 */
    00923     PlayerID pid_old = (PlayerID)GB(p2,  0, 16);
    00924     PlayerID pid_new = (PlayerID)GB(p2, 16, 16);
    00925
    00926     if (!IsValidPlayer(pid_old) || !IsValidPlayer(pid_new)) return CMD_ERROR;
    00927
    00928     if (!(flags & DC_EXEC)) return CMD_ERROR;
    00929
    00930     ChangeOwnershipOfPlayerItems(pid_old, pid_new);
    00931     DeletePlayerStuff(pid_old);
    00932   } break;
    00933
    00934   default: return CMD_ERROR;
    00935   }
    00936
    00937   return CommandCost();
    00938 }
    00939
    00940 static const StringID _endgame_perf_titles[] = {
    00941   STR_0213_BUSINESSMAN,
    00942   STR_0213_BUSINESSMAN,
    00943   STR_0213_BUSINESSMAN,
    00944   STR_0213_BUSINESSMAN,
    00945   STR_0213_BUSINESSMAN,
    00946   STR_0214_ENTREPRENEUR,
    00947   STR_0214_ENTREPRENEUR,
    00948   STR_0215_INDUSTRIALIST,
    00949   STR_0215_INDUSTRIALIST,
    00950   STR_0216_CAPITALIST,
    00951   STR_0216_CAPITALIST,
    00952   STR_0217_MAGNATE,
    00953   STR_0217_MAGNATE,
    00954   STR_0218_MOGUL,
    00955   STR_0218_MOGUL,
    00956   STR_0219_TYCOON_OF_THE_CENTURY
    00957 };
    00958
    00959 StringID EndGameGetPerformanceTitleFromValue(uint value)
    00960 {
    00961   value = minu(value / 64, lengthof(_endgame_perf_titles) - 1);
    00962
    00963   return _endgame_perf_titles[value];
    00964 }
    00965
    00967 static bool CheatHasBeenUsed()
    00968 {
    00969   const Cheat* cht = (Cheat*)&_cheats;
    00970   const Cheat* cht_last = &cht[sizeof(_cheats) / sizeof(Cheat)];
    00971
    00972   for (; cht != cht_last; cht++) {
    00973     if (cht->been_used) return true;
    00974   }
    00975
    00976   return false;
    00977 }
    00978
    00980 int8 SaveHighScoreValue(const Player *p)
    00981 {
    00982   HighScore *hs = _highscore_table[_opt.diff_level];
    00983   uint i;
    00984   uint16 score = p->old_economy[0].performance_history;
    00985
    00986   /* Exclude cheaters from the honour of being in the highscore table */
    00987   if (CheatHasBeenUsed()) return -1;
    00988
    00989   for (i = 0; i < lengthof(_highscore_table[0]); i++) {
    00990     /* You are in the TOP5. Move all values one down and save us there */
    00991     if (hs[i].score <= score) {
    00992       /* move all elements one down starting from the replaced one */
    00993       memmove(&hs[i + 1], &hs[i], sizeof(HighScore) * (lengthof(_highscore_table[0]) - i - 1));
    00994       SetDParam(0, p->index);
    00995       SetDParam(1, p->index);
    00996       GetString(hs[i].company, STR_HIGHSCORE_NAME, lastof(hs[i].company)); // get manager/company name string
    00997       hs[i].score = score;
    00998       hs[i].title = EndGameGetPerformanceTitleFromValue(score);
    00999       return i;
    01000     }
    01001   }
    01002
    01003   return -1; // too bad; we did not make it into the top5
    01004 }
    01005
    01007 static int CDECL HighScoreSorter(const void *a, const void *b)
    01008 {
    01009   const Player *pa = *(const Player* const*)a;
    01010   const Player *pb = *(const Player* const*)b;
    01011
    01012   return pb->old_economy[0].performance_history - pa->old_economy[0].performance_history;
    01013 }
    01014
    01015 /* Save the highscores in a network game when it has ended */
    01016 #define LAST_HS_ITEM lengthof(_highscore_table) - 1
    01017 int8 SaveHighScoreValueNetwork()
    01018 {
    01019   const Player* p;
    01020   const Player* pl[MAX_PLAYERS];
    01021   size_t count = 0;
    01022   int8 player = -1;
    01023
    01024   /* Sort all active players with the highest score first */
    01025   FOR_ALL_PLAYERS(p) if (p->is_active) pl[count++] = p;
    01026   qsort((Player*)pl, count, sizeof(pl[0]), HighScoreSorter);
    01027
    01028   {
    01029     uint i;
    01030
    01031     memset(_highscore_table[LAST_HS_ITEM], 0, sizeof(_highscore_table[0]));
    01032
    01033     /* Copy over Top5 companies */
    01034     for (i = 0; i < lengthof(_highscore_table[LAST_HS_ITEM]) && i < count; i++) {
    01035       HighScore* hs = &_highscore_table[LAST_HS_ITEM][i];
    01036
    01037       SetDParam(0, pl[i]->index);
    01038       SetDParam(1, pl[i]->index);
    01039       GetString(hs->company, STR_HIGHSCORE_NAME, lastof(hs->company)); // get manager/company name string
    01040       hs->score = pl[i]->old_economy[0].performance_history;
    01041       hs->title = EndGameGetPerformanceTitleFromValue(hs->score);
    01042
    01043       /* get the ranking of the local player */
    01044       if (pl[i]->index == _local_player) player = i;
    01045     }
    01046   }
    01047
    01048   /* Add top5 players to highscore table */
    01049   return player;
    01050 }
    01051
    01053 void SaveToHighScore()
    01054 {
    01055   FILE *fp = fopen(_highscore_file, "wb");
    01056
    01057   if (fp != NULL) {
    01058     uint i;
    01059     HighScore *hs;
    01060
    01061     for (i = 0; i < LAST_HS_ITEM; i++) { // don't save network highscores
    01062       for (hs = _highscore_table[i]; hs != endof(_highscore_table[i]); hs++) {
    01063         /* First character is a command character, so strlen will fail on that */
    01064         byte length = min(sizeof(hs->company), (hs->company[0] == '\0') ? 0 : (int)strlen(&hs->company[1]) + 1);
    01065
    01066         fwrite(&length, sizeof(length), 1, fp); // write away string length
    01067         fwrite(hs->company, length, 1, fp);
    01068         fwrite(&hs->score, sizeof(hs->score), 1, fp);
    01069         fwrite("", 2, 1, fp); // XXX - placeholder for hs->title, not saved anymore; compatibility
    01070       }
    01071     }
    01072     fclose(fp);
    01073   }
    01074 }
    01075
    01077 void LoadFromHighScore()
    01078 {
    01079   FILE *fp = fopen(_highscore_file, "rb");
    01080
    01081   memset(_highscore_table, 0, sizeof(_highscore_table));
    01082
    01083   if (fp != NULL) {
    01084     uint i;
    01085     HighScore *hs;
    01086
    01087     for (i = 0; i < LAST_HS_ITEM; i++) { // don't load network highscores
    01088       for (hs = _highscore_table[i]; hs != endof(_highscore_table[i]); hs++) {
    01089         byte length;
    01090         fread(&length, sizeof(length), 1, fp);
    01091
    01092         fread(hs->company, 1, length, fp);
    01093         fread(&hs->score, sizeof(hs->score), 1, fp);
    01094         fseek(fp, 2, SEEK_CUR); // XXX - placeholder for hs->title, not saved anymore; compatibility
    01095         hs->title = EndGameGetPerformanceTitleFromValue(hs->score);
    01096       }
    01097     }
    01098     fclose(fp);
    01099   }
    01100
    01101   /* Initialize end of game variable (when to show highscore chart) */
    01102   _patches.ending_year = 2051;
    01103 }
    01104
    01105 /* Save/load of players */
    01106 static const SaveLoad _player_desc[] = {
    01107       SLE_VAR(Player, name_2,          SLE_UINT32),
    01108       SLE_VAR(Player, name_1,          SLE_STRINGID),
    01109   SLE_CONDSTR(Player, name,            SLE_STR, 0,                       84, SL_MAX_VERSION),
    01110
    01111       SLE_VAR(Player, president_name_1,SLE_UINT16),
    01112       SLE_VAR(Player, president_name_2,SLE_UINT32),
    01113   SLE_CONDSTR(Player, president_name,  SLE_STR, 0,                       84, SL_MAX_VERSION),
    01114
    01115       SLE_VAR(Player, face,            SLE_UINT32),
    01116
    01117   /* money was changed to a 64 bit field in savegame version 1. */
    01118   SLE_CONDVAR(Player, player_money,          SLE_VAR_I64 | SLE_FILE_I32,  0, 0),
    01119   SLE_CONDVAR(Player, player_money,          SLE_INT64,                   1, SL_MAX_VERSION),
    01120
    01121   SLE_CONDVAR(Player, current_loan,          SLE_VAR_I64 | SLE_FILE_I32,  0, 64),
    01122   SLE_CONDVAR(Player, current_loan,          SLE_INT64,                  65, SL_MAX_VERSION),
    01123
    01124       SLE_VAR(Player, player_color,          SLE_UINT8),
    01125       SLE_VAR(Player, player_money_fraction, SLE_UINT8),
    01126   SLE_CONDVAR(Player, avail_railtypes,       SLE_UINT8,                   0, 57),
    01127       SLE_VAR(Player, block_preview,         SLE_UINT8),
    01128
    01129       SLE_VAR(Player, cargo_types,           SLE_UINT16),
    01130   SLE_CONDVAR(Player, location_of_house,     SLE_FILE_U16 | SLE_VAR_U32,  0,  5),
    01131   SLE_CONDVAR(Player, location_of_house,     SLE_UINT32,                  6, SL_MAX_VERSION),
    01132   SLE_CONDVAR(Player, last_build_coordinate, SLE_FILE_U16 | SLE_VAR_U32,  0,  5),
    01133   SLE_CONDVAR(Player, last_build_coordinate, SLE_UINT32,                  6, SL_MAX_VERSION),
    01134   SLE_CONDVAR(Player, inaugurated_year,      SLE_FILE_U8  | SLE_VAR_I32,  0, 30),
    01135   SLE_CONDVAR(Player, inaugurated_year,      SLE_INT32,                  31, SL_MAX_VERSION),
    01136
    01137       SLE_ARR(Player, share_owners,          SLE_UINT8, 4),
    01138
    01139       SLE_VAR(Player, num_valid_stat_ent,    SLE_UINT8),
    01140
    01141       SLE_VAR(Player, quarters_of_bankrupcy, SLE_UINT8),
    01142       SLE_VAR(Player, bankrupt_asked,        SLE_UINT8),
    01143       SLE_VAR(Player, bankrupt_timeout,      SLE_INT16),
    01144   SLE_CONDVAR(Player, bankrupt_value,        SLE_VAR_I64 | SLE_FILE_I32,  0, 64),
    01145   SLE_CONDVAR(Player, bankrupt_value,        SLE_INT64,                  65, SL_MAX_VERSION),
    01146
    01147   /* yearly expenses was changed to 64-bit in savegame version 2. */
    01148   SLE_CONDARR(Player, yearly_expenses,       SLE_FILE_I32 | SLE_VAR_I64, 3 * 13, 0, 1),
    01149   SLE_CONDARR(Player, yearly_expenses,       SLE_INT64, 3 * 13,                  2, SL_MAX_VERSION),
    01150
    01151   SLE_CONDVAR(Player, is_ai,                 SLE_BOOL, 2, SL_MAX_VERSION),
    01152   SLE_CONDVAR(Player, is_active,             SLE_BOOL, 4, SL_MAX_VERSION),
    01153
    01154   /* Engine renewal settings */
    01155   SLE_CONDNULL(512, 16, 18),
    01156   SLE_CONDREF(Player, engine_renew_list,     REF_ENGINE_RENEWS,          19, SL_MAX_VERSION),
    01157   SLE_CONDVAR(Player, engine_renew,          SLE_BOOL,                   16, SL_MAX_VERSION),
    01158   SLE_CONDVAR(Player, engine_renew_months,   SLE_INT16,                  16, SL_MAX_VERSION),
    01159   SLE_CONDVAR(Player, engine_renew_money,    SLE_UINT32,                 16, SL_MAX_VERSION),
    01160   SLE_CONDVAR(Player, renew_keep_length,     SLE_BOOL,                    2, SL_MAX_VERSION), // added with 16.1, but was blank since 2
    01161
    01162   /* reserve extra space in savegame here. (currently 63 bytes) */
    01163   SLE_CONDNULL(63, 2, SL_MAX_VERSION),
    01164
    01165   SLE_END()
    01166 };
    01167
    01168 static const SaveLoad _player_economy_desc[] = {
    01169   /* these were changed to 64-bit in savegame format 2 */
    01170   SLE_CONDVAR(PlayerEconomyEntry, income,              SLE_FILE_I32 | SLE_VAR_I64, 0, 1),
    01171   SLE_CONDVAR(PlayerEconomyEntry, income,              SLE_INT64,                  2, SL_MAX_VERSION),
    01172   SLE_CONDVAR(PlayerEconomyEntry, expenses,            SLE_FILE_I32 | SLE_VAR_I64, 0, 1),
    01173   SLE_CONDVAR(PlayerEconomyEntry, expenses,            SLE_INT64,                  2, SL_MAX_VERSION),
    01174   SLE_CONDVAR(PlayerEconomyEntry, company_value,       SLE_FILE_I32 | SLE_VAR_I64, 0, 1),
    01175   SLE_CONDVAR(PlayerEconomyEntry, company_value,       SLE_INT64,                  2, SL_MAX_VERSION),
    01176
    01177       SLE_VAR(PlayerEconomyEntry, delivered_cargo,     SLE_INT32),
    01178       SLE_VAR(PlayerEconomyEntry, performance_history, SLE_INT32),
    01179
    01180   SLE_END()
    01181 };
    01182
    01183 static const SaveLoad _player_livery_desc[] = {
    01184   SLE_CONDVAR(Livery, in_use,  SLE_BOOL,  34, SL_MAX_VERSION),
    01185   SLE_CONDVAR(Livery, colour1, SLE_UINT8, 34, SL_MAX_VERSION),
    01186   SLE_CONDVAR(Livery, colour2, SLE_UINT8, 34, SL_MAX_VERSION),
    01187   SLE_END()
    01188 };
    01189
    01190 static void SaveLoad_PLYR(Player* p)
    01191 {
    01192   int i;
    01193
    01194   SlObject(p, _player_desc);
    01195
    01196   /* Write AI? */
    01197   if (!IsHumanPlayer(p->index)) {
    01198     SaveLoad_AI(p->index);
    01199   }
    01200
    01201   /* Write economy */
    01202   SlObject(&p->cur_economy, _player_economy_desc);
    01203
    01204   /* Write old economy entries. */
    01205   for (i = 0; i < p->num_valid_stat_ent; i++) {
    01206     SlObject(&p->old_economy[i], _player_economy_desc);
    01207   }
    01208
    01209   /* Write each livery entry. */
    01210   int num_liveries = CheckSavegameVersion(63) ? LS_END - 4 : (CheckSavegameVersion(85) ? LS_END - 2: LS_END);
    01211   for (i = 0; i < num_liveries; i++) {
    01212     SlObject(&p->livery[i], _player_livery_desc);
    01213   }
    01214
    01215   if (num_liveries < LS_END) {
    01216     /* We want to insert some liveries somewhere in between. This means some have to be moved. */
    01217     memmove(&p->livery[LS_FREIGHT_WAGON], &p->livery[LS_PASSENGER_WAGON_MONORAIL], (LS_END - LS_FREIGHT_WAGON) * sizeof(p->livery[0]));
    01218     p->livery[LS_PASSENGER_WAGON_MONORAIL] = p->livery[LS_MONORAIL];
    01219     p->livery[LS_PASSENGER_WAGON_MAGLEV]   = p->livery[LS_MAGLEV];
    01220   }
    01221
    01222   if (num_liveries == LS_END - 4) {
    01223     /* Copy bus/truck liveries over to trams */
    01224     p->livery[LS_PASSENGER_TRAM] = p->livery[LS_BUS];
    01225     p->livery[LS_FREIGHT_TRAM]   = p->livery[LS_TRUCK];
    01226   }
    01227 }
    01228
    01229 static void Save_PLYR()
    01230 {
    01231   Player *p;
    01232   FOR_ALL_PLAYERS(p) {
    01233     if (p->is_active) {
    01234       SlSetArrayIndex(p->index);
    01235       SlAutolength((AutolengthProc*)SaveLoad_PLYR, p);
    01236     }
    01237   }
    01238 }
    01239
    01240 static void Load_PLYR()
    01241 {
    01242   int index;
    01243   while ((index = SlIterateArray()) != -1) {
    01244     Player *p = GetPlayer((PlayerID)index);
    01245     SaveLoad_PLYR(p);
    01246     _player_colors[index] = p->player_color;
    01247
    01248     /* This is needed so an AI is attached to a loaded AI */
    01249     if (p->is_ai && (!_networking || _network_server) && _ai.enabled) {
    01250       /* Clear the memory of the new AI, otherwise we might be doing wrong things. */
    01251       memset(&_players_ainew[index], 0, sizeof(PlayerAiNew));
    01252       AI_StartNewAI(p->index);
    01253     }
    01254   }
    01255 }
    01256
    01257 extern const ChunkHandler _player_chunk_handlers[] = {
    01258   { 'PLYR', Save_PLYR, Load_PLYR, CH_ARRAY | CH_LAST},
    01259 };