00001
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
00045 byte _player_colors[MAX_PLAYERS];
00046 PlayerFace _player_face;
00047 HighScore _highscore_table[5][5];
00048
00055 void SetLocalPlayer(PlayerID new_player)
00056 {
00057
00058 assert(IsValidPlayer(new_player) || new_player == PLAYER_SPECTATOR || new_player == OWNER_NONE);
00059
00060 _local_player = new_player;
00061
00062
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
00081
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;
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) {
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:
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
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
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
00370 for (i = 0; i != 16; i++) colors[i] = i;
00371
00372
00373 n = 100;
00374 do {
00375 r = Random();
00376 COLOR_SWAP(GB(r, 0, 4), GB(r, 4, 4));
00377 } while (--n);
00378
00379
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
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
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
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
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;
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);
00508
00509
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
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
00542 n = 0;
00543 FOR_ALL_PLAYERS(p) {
00544 if (p->is_active && p->is_ai) n++;
00545 }
00546
00547
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
00554
00555 DoCommandP(0, 1, 0, NULL, CMD_PLAYER_CTRL);
00556 }
00557
00558
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
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
00709
00710 if (!IsEngineIndex(new_engine_type))
00711 return CMD_ERROR;
00712
00713
00714 if (GetEngine(old_engine_type)->type != GetEngine(new_engine_type)->type)
00715 return CMD_ERROR;
00716
00717
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
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: {
00794
00795
00796
00797
00798
00799
00800
00801 Player *p;
00802 #ifdef ENABLE_NETWORK
00803 uint16 cid = p2;
00804 #endif
00805
00806
00807 if (!_networking) return CMD_ERROR;
00808
00809
00810 if (!(flags & DC_EXEC)) return CommandCost();
00811 #ifdef ENABLE_NETWORK
00812 if (cid >= MAX_CLIENT_INFO) return CommandCost();
00813 #endif
00814
00815
00816 DeleteWindowById(WC_NETWORK_STATUS_WINDOW, 0);
00817
00818 p = DoStartupNewPlayer(false);
00819
00820
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
00831 break;
00832 }
00833
00834
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
00844
00845 _current_player = _local_player;
00846
00847
00848
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
00862
00863
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
00873
00874
00875
00876
00877
00878
00879
00880
00881
00882
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
00890 } break;
00891
00892 case 1:
00893 if (!(flags & DC_EXEC)) return CommandCost();
00894
00895 DoStartupNewPlayer(true);
00896 break;
00897
00898 case 2: {
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
00908 if (IsHumanPlayer(p->index)) {
00909
00910 DeletePlayerWindows(p->index);
00911
00912
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
00917 ChangeOwnershipOfPlayerItems(p->index, PLAYER_SPECTATOR);
00918 p->is_active = false;
00919 }
00920 } break;
00921
00922 case 3: {
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
00987 if (CheatHasBeenUsed()) return -1;
00988
00989 for (i = 0; i < lengthof(_highscore_table[0]); i++) {
00990
00991 if (hs[i].score <= score) {
00992
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));
00997 hs[i].score = score;
00998 hs[i].title = EndGameGetPerformanceTitleFromValue(score);
00999 return i;
01000 }
01001 }
01002
01003 return -1;
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
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
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
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));
01040 hs->score = pl[i]->old_economy[0].performance_history;
01041 hs->title = EndGameGetPerformanceTitleFromValue(hs->score);
01042
01043
01044 if (pl[i]->index == _local_player) player = i;
01045 }
01046 }
01047
01048
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++) {
01062 for (hs = _highscore_table[i]; hs != endof(_highscore_table[i]); hs++) {
01063
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);
01067 fwrite(hs->company, length, 1, fp);
01068 fwrite(&hs->score, sizeof(hs->score), 1, fp);
01069 fwrite("", 2, 1, fp);
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++) {
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);
01095 hs->title = EndGameGetPerformanceTitleFromValue(hs->score);
01096 }
01097 }
01098 fclose(fp);
01099 }
01100
01101
01102 _patches.ending_year = 2051;
01103 }
01104
01105
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
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
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
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),
01161
01162
01163 SLE_CONDNULL(63, 2, SL_MAX_VERSION),
01164
01165 SLE_END()
01166 };
01167
01168 static const SaveLoad _player_economy_desc[] = {
01169
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
01197 if (!IsHumanPlayer(p->index)) {
01198 SaveLoad_AI(p->index);
01199 }
01200
01201
01202 SlObject(&p->cur_economy, _player_economy_desc);
01203
01204
01205 for (i = 0; i < p->num_valid_stat_ent; i++) {
01206 SlObject(&p->old_economy[i], _player_economy_desc);
01207 }
01208
01209
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
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
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
01249 if (p->is_ai && (!_networking || _network_server) && _ai.enabled) {
01250
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 };