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 };

Generated on Wed Oct 1 17:03:22 2008 for openttd by  doxygen 1.5.6