diff --git a/functions.h b/functions.h index 437bf6c04a..6ddcf05ba0 100644 --- a/functions.h +++ b/functions.h @@ -234,7 +234,7 @@ void ShowNetworkNeedGamePassword(); void ShowNetworkNeedCompanyPassword(); void ShowRenameWaypointWindow(Waypoint *cp); int FindFirstBit(uint32 x); -void ShowHighscoreTable(int difficulty, int rank); +void ShowHighscoreTable(int difficulty, int8 rank); void ShowEndGameChart(void); TileIndex AdjustTileCoordRandomly(TileIndex a, byte rng); diff --git a/lang/english.txt b/lang/english.txt index d8cd1f982e..e3b975dfd4 100644 --- a/lang/english.txt +++ b/lang/english.txt @@ -564,7 +564,8 @@ STR_020D_CHANGES_OF_CARGO_ACCEPTANCE :{YELLOW}Changes to cargo acceptance STR_020E_SUBSIDIES :{YELLOW}Subsidies STR_020F_GENERAL_INFORMATION :{YELLOW}General information STR_0210_TOO_FAR_FROM_PREVIOUS_DESTINATIO :{WHITE}...too far from previous destination -STR_0211_TOP_COMPANIES_WHO_REACHED :{BIGFONT}{BLACK}Top companies who reached 2050{}({STRING} Level) +STR_0211_TOP_COMPANIES_WHO_REACHED :{BIGFONT}{BLACK}Top companies who reached {NUMU16}{}({STRING} Level) +STR_TOP_COMPANIES_NETWORK_GAME :{BIGFONT}{BLACK}Company League Table in {NUMU16} STR_0212 :{BIGFONT}{COMMA16}. STR_0213_BUSINESSMAN :Businessman STR_0214_ENTREPRENEUR :Entrepreneur @@ -1047,6 +1048,7 @@ STR_CONFIG_PATCHES_SERVINT_SHIPS_DISABLED :{LTBLUE}Default service interval fo STR_CONFIG_PATCHES_COLORED_NEWS_DATE :{LTBLUE}Coloured news appears in: {ORANGE}{STRING} STR_CONFIG_PATCHES_STARTING_DATE :{LTBLUE}Starting date: {ORANGE}{STRING} +STR_CONFIG_PATCHES_ENDING_DATE :{LTBLUE}End game in: {ORANGE}{STRING} STR_CONFIG_PATCHES_SMOOTH_ECONOMY :{LTBLUE}Enable smooth economy (more, smaller changes) STR_CONFIG_PATCHES_ALLOW_SHARES :{LTBLUE}Allow buying shares from other companies STR_CONFIG_PATCHES_DRAG_SIGNALS_DENSITY :{LTBLUE}When dragging, place signals every: {ORANGE}{STRING} tile(s) diff --git a/misc.c b/misc.c index cb84f3ac70..a0f5165f44 100644 --- a/misc.c +++ b/misc.c @@ -702,8 +702,8 @@ void IncreaseDate() NetworkServerYearlyLoop(); #endif /* ENABLE_NETWORK */ - /* check if we reached 2090, that's the maximum year. */ - if (_cur_year == 131) { // end of game on 31 dec 2050 + /* check if we reached end of the game (31 dec 2050) */ + if (_cur_year == _patches.ending_date - MAX_YEAR_BEGIN_REAL) { ShowEndGameChart(); /* check if we reached 2090 (MAX_YEAR_END_REAL), that's the maximum year. */ } else if (_cur_year == (MAX_YEAR_END + 1)) { diff --git a/misc_gui.c b/misc_gui.c index 28bb9ad16e..804b5eaba2 100644 --- a/misc_gui.c +++ b/misc_gui.c @@ -207,14 +207,14 @@ static void AboutWindowProc(Window *w, WindowEvent *e) { switch(e->event) { case WE_CREATE: /* Set up window counter and start position of scroller */ - WP(w, general_d).i = 0; - WP(w, general_d).j = w->height - 40; + WP(w, scroller_d).counter = 0; + WP(w, scroller_d).height = w->height - 40; break; case WE_PAINT: { const char *str; char buffer[100]; uint i; - int y = WP(w, general_d).j; + int y = WP(w, scroller_d).height; DrawWindowWidgets(w); // Show original copyright and revision version @@ -239,13 +239,13 @@ static void AboutWindowProc(Window *w, WindowEvent *e) } // If the last text has scrolled start anew from the start - if (y < 50) WP(w, general_d).j = w->height - 40; + if (y < 50) WP(w, scroller_d).height = w->height - 40; DrawStringMultiCenter(200, w->height - 15, STR_00BA_COPYRIGHT_OPENTTD, 398); } break; case WE_MOUSELOOP: /* Timer to scroll the text and adjust the new top */ - if (WP(w, general_d).i++ % 3 == 0) { - WP(w, general_d).j--; + if (WP(w, scroller_d).counter++ % 3 == 0) { + WP(w, scroller_d).height--; SetWindowDirty(w); } break; diff --git a/player.h b/player.h index 52752e1b66..2c93c2b0ee 100644 --- a/player.h +++ b/player.h @@ -207,9 +207,10 @@ typedef struct HighScore { uint16 score; } HighScore; -VARDEF HighScore _highscore_table[4][5]; // 4 difficulty-settings; top 5 +VARDEF HighScore _highscore_table[5][5]; // 4 difficulty-settings (+ network); top 5 void SaveToHighScore(void); void LoadFromHighScore(void); -int SaveHighScoreValue(const Player *p); +int8 SaveHighScoreValue(const Player *p); +int8 SaveHighScoreValueNetwork(void); #endif /* PLAYER_H */ diff --git a/player_gui.c b/player_gui.c index 4749a09952..da4e2eaa58 100644 --- a/player_gui.c +++ b/player_gui.c @@ -763,7 +763,7 @@ static void SetupHighScoreEndWindow(Window *w, uint *x, uint *y) *x = max(0, (_screen.width / 2) - (640 / 2)); *y = max(0, (_screen.height / 2) - (480 / 2)); for (i = 0; i < 10; i++) // the image is split into 10 50px high parts - DrawSprite(WP(w, general_d).i + i, *x, *y + (i * 50)); + DrawSprite(WP(w, highscore_d).background_img + i, *x, *y + (i * 50)); } extern StringID EndGameGetPerformanceTitleFromValue(uint value); @@ -780,7 +780,7 @@ static void EndGameWndProc(Window *w, WindowEvent *e) /* We need to get performance from last year because the image is shown * at the start of the new year when these things have already been copied */ - if (WP(w, general_d).i == SPR_TYCOON_IMG2_BEGIN) { // Tycoon of the century \o/ + if (WP(w, highscore_d).background_img == SPR_TYCOON_IMG2_BEGIN) { // Tycoon of the century \o/ SetDParam(0, p->president_name_1); SetDParam(1, p->president_name_2); SetDParam(2, p->name_1); @@ -796,7 +796,7 @@ static void EndGameWndProc(Window *w, WindowEvent *e) } break; case WE_CLICK: /* OnClick show the highscore chart */ DoCommandP(0, 0, 0, NULL, CMD_PAUSE); - ShowHighscoreTable(w->window_number, WP(w, general_d).j); + ShowHighscoreTable(w->window_number, WP(w, highscore_d).rank); DeleteWindow(w); } } @@ -806,12 +806,14 @@ static void HighScoreWndProc(Window *w, WindowEvent *e) switch (e->event) { case WE_PAINT: { const HighScore *hs = _highscore_table[w->window_number]; - uint i, x, y; + uint x, y; + uint8 i; SetupHighScoreEndWindow(w, &x, &y); - SetDParam(0, w->window_number + STR_6801_EASY); - DrawStringMultiCenter(x + (640 / 2), y + 62, STR_0211_TOP_COMPANIES_WHO_REACHED, 640); + SetDParam(0, _patches.ending_date); + SetDParam(1, w->window_number + STR_6801_EASY); + DrawStringMultiCenter(x + (640 / 2), y + 62, !_networking ? STR_0211_TOP_COMPANIES_WHO_REACHED : STR_TOP_COMPANIES_NETWORK_GAME, 500); /* Draw Highscore peepz */ for (i = 0; i < lengthof(_highscore_table[0]); i++) { @@ -819,7 +821,7 @@ static void HighScoreWndProc(Window *w, WindowEvent *e) DrawString(x + 40, y + 140 + (i * 55), STR_0212, 0x10); if (hs[i].company[0] != '\0') { - uint16 colour = (WP(w, general_d).j == i) ? 0x3 : 0x10; // draw new highscore in red + uint16 colour = (WP(w, highscore_d).rank == (int8)i) ? 0x3 : 0x10; // draw new highscore in red DoDrawString(hs[i].company, x + 71, y + 140 + (i * 55), colour); SetDParam(0, hs[i].title); @@ -830,7 +832,7 @@ static void HighScoreWndProc(Window *w, WindowEvent *e) } break; case WE_CLICK: /* Onclick get back all hidden windows */ - if (_game_mode != GM_MENU) + if (_game_mode != GM_MENU && !_networking) ShowVitalWindows(); DoCommandP(0, 0, 0, NULL, CMD_PAUSE); @@ -863,16 +865,16 @@ static const WindowDesc _endgame_desc = { /* Show the highscore table for a given difficulty. When called from * endgame ranking is set to the top5 element that was newly added * and is thus highlighted */ -void ShowHighscoreTable(int difficulty, int ranking) +void ShowHighscoreTable(int difficulty, int8 ranking) { Window *w; - /* Close all always on-top windows to get a clean screen */ - if (_game_mode != GM_MENU) - HideVitalWindows(); - - if (!_networking) // pause game to show chart + if (!_networking) { // pause game to show chart DoCommandP(0, 1, 0, NULL, CMD_PAUSE); + /* Close all always on-top windows to get a clean screen */ + if (_game_mode != GM_MENU) + HideVitalWindows(); + } DeleteWindowById(WC_HIGHSCORE_ENDSCREEN, 0); w = AllocateWindowDesc(&_highscore_desc); @@ -880,8 +882,8 @@ void ShowHighscoreTable(int difficulty, int ranking) if (w != NULL) { MarkWholeScreenDirty(); w->window_number = difficulty; // show highscore chart for difficulty... - WP(w, general_d).i = SPR_HIGHSCORE_CHART_BEGIN; // which background to show - WP(w, general_d).j = ranking; + WP(w, highscore_d).background_img = SPR_HIGHSCORE_CHART_BEGIN; // which background to show + WP(w, highscore_d).rank = ranking; } } @@ -902,8 +904,10 @@ void ShowEndGameChart(void) if (w != NULL) { MarkWholeScreenDirty(); - w->window_number = _opt.diff_level; // show highscore chart for difficulty... - WP(w, general_d).i = (p->old_economy[0].performance_history == SCORE_MAX) ? SPR_TYCOON_IMG2_BEGIN : SPR_TYCOON_IMG1_BEGIN; // which background to show - WP(w, general_d).j = SaveHighScoreValue(p); + /* In a network game show the endscores of the custom difficulty 'network' which is the last one + * as well as generate a TOP5 of that game, and not an all-time top5 */ + w->window_number = (!_networking) ? _opt.diff_level : lengthof(_highscore_table) - 1; + WP(w, highscore_d).background_img = (p->old_economy[0].performance_history == SCORE_MAX) ? SPR_TYCOON_IMG2_BEGIN : SPR_TYCOON_IMG1_BEGIN; // which background to show + WP(w, highscore_d).rank = (!_networking) ? SaveHighScoreValue(p) : SaveHighScoreValueNetwork(); } } diff --git a/players.c b/players.c index 9fe6593499..35b0e5b522 100644 --- a/players.c +++ b/players.c @@ -746,7 +746,7 @@ inline StringID EndGameGetPerformanceTitleFromValue(uint value) } /* Save the highscore for the player */ -int SaveHighScoreValue(const Player *p) +int8 SaveHighScoreValue(const Player *p) { HighScore *hs = _highscore_table[_opt.diff_level]; uint i; @@ -774,6 +774,62 @@ int SaveHighScoreValue(const Player *p) return -1; // too bad; we did not make it into the top5 } +/* Sort all players given their performance */ +static int CDECL HighScoreSorter(const void *a, const void *b) +{ + const Player *pa = *(const Player* const*)a; + const Player *pb = *(const Player* const*)b; + + return pb->old_economy[0].performance_history - pa->old_economy[0].performance_history; +} + +/* Save the highscores in a network game when it has ended */ +#define LAST_HS_ITEM lengthof(_highscore_table) - 1 +int8 SaveHighScoreValueNetwork(void) +{ + Player *p, *player_sort[MAX_PLAYERS]; + size_t count = 0; + int8 player = -1; + + /* Sort all active players with the highest score first */ + FOR_ALL_PLAYERS(p) { + if (p->is_active) + player_sort[count++] = p; + } + qsort(player_sort, count, sizeof(player_sort[0]), HighScoreSorter); + + { + HighScore *hs; + byte buf[sizeof(_highscore_table[0]->company)]; + Player* const *p_cur = &player_sort[0]; + uint8 i; + + memset(_highscore_table[LAST_HS_ITEM], 0, sizeof(_highscore_table[0])); + + /* Copy over Top5 companies */ + for (i = 0; i < lengthof(_highscore_table[LAST_HS_ITEM]) && i < (uint8)count; i++) { + hs = &_highscore_table[LAST_HS_ITEM][i]; + SetDParam(0, (*p_cur)->president_name_1); + SetDParam(1, (*p_cur)->president_name_2); + SetDParam(2, (*p_cur)->name_1); + SetDParam(3, (*p_cur)->name_1); + GetString(buf, STR_HIGHSCORE_NAME); // get manager/company name string + ttd_strlcpy(hs->company, buf, sizeof(buf)); + hs->score = (*p_cur)->old_economy[0].performance_history; + hs->title = EndGameGetPerformanceTitleFromValue(hs->score); + + // get the ranking of the local player + if ((*p_cur)->index == (int8)_local_player) + player = i; + + p_cur++; + } + } + + /* Add top5 players to highscore table */ + return player; +} + /* Save HighScore table to file */ void SaveToHighScore(void) { @@ -783,7 +839,7 @@ void SaveToHighScore(void) uint i; HighScore *hs; - for (i = 0; i < lengthof(_highscore_table); i++) { + for (i = 0; i < LAST_HS_ITEM; i++) { // don't save network highscores for (hs = _highscore_table[i]; hs != endof(_highscore_table[i]); hs++) { /* First character is a command character, so strlen will fail on that */ byte length = min(sizeof(hs->company), (hs->company[0] == '\0') ? 0 : strlen(&hs->company[1]) + 1); @@ -809,7 +865,7 @@ void LoadFromHighScore(void) uint i; HighScore *hs; - for (i = 0; i < lengthof(_highscore_table); i++) { + for (i = 0; i < LAST_HS_ITEM; i++) { // don't load network highscores for (hs = _highscore_table[i]; hs != endof(_highscore_table[i]); hs++) { byte length; fread(&length, sizeof(length), 1, fp); @@ -821,6 +877,9 @@ void LoadFromHighScore(void) } fclose(fp); } + + /* Initialize end of game variable (when to show highscore chart) */ + _patches.ending_date = 2051; } // Save/load of players diff --git a/settings.c b/settings.c index 91be6cbb59..6d95a37b18 100644 --- a/settings.c +++ b/settings.c @@ -886,6 +886,7 @@ const SettingDesc patch_settings[] = { {"ai_disable_veh_aircraft",SDT_BOOL,(void*)false, &_patches.ai_disable_veh_aircraft,NULL}, {"ai_disable_veh_ship", SDT_BOOL, (void*)false, &_patches.ai_disable_veh_ship, NULL}, {"starting_date", SDT_UINT32, (void*)1950, &_patches.starting_date, NULL}, + {"ending_date", SDT_UINT32, (void*)2051, &_patches.ending_date, NULL}, {"colored_news_date", SDT_UINT32, (void*)2000, &_patches.colored_news_date, NULL}, diff --git a/settings_gui.c b/settings_gui.c index 7722d03d02..42e1ef2e2d 100644 --- a/settings_gui.c +++ b/settings_gui.c @@ -345,9 +345,12 @@ static void GameDifficultyWndProc(Window *w, WindowEvent *e) w->click_state = (1 << 3) << _opt_mod_temp.diff_level; w->disabled_state = (_game_mode != GM_NORMAL) ? 0 : (1 << 3) | (1 << 4) | (1 << 5) | (1 << 6); - // Disable save-button in multiplayer (and if client) - if (_networking && !_network_server) - SETBIT(w->disabled_state, 10); + if (_networking) { + SETBIT(w->disabled_state, 7); // disable highscore chart in multiplayer + if (!_network_server) + SETBIT(w->disabled_state, 10); // Disable save-button in multiplayer (and if client) + } + DrawWindowWidgets(w); click_a = _difficulty_click_a; @@ -598,10 +601,11 @@ enum { PE_INT32 = 4, PE_CURRENCY = 5, - PF_0ISDIS = 1, - PF_NOCOMMA = 2, - PF_MULTISTRING = 4, - PF_PLAYERBASED = 8, // This has to match the entries that are in settings.c, patch_player_settings + PF_0ISDIS = 1 << 0, + PF_NOCOMMA = 1 << 1, + PF_MULTISTRING = 1 << 2, + PF_PLAYERBASED = 1 << 3, // This has to match the entries that are in settings.c, patch_player_settings + PF_NETWORK_ONLY = 1 << 4, // this setting only applies to network games }; static const PatchEntry _patches_ui[] = { @@ -680,7 +684,8 @@ static const PatchEntry _patches_economy[] = { {PE_UINT8, 0, STR_CONFIG_PATCHES_SNOWLINE_HEIGHT, "snow_line_height", &_patches.snow_line_height, 2, 13, 1, NULL}, {PE_INT32, PF_NOCOMMA, STR_CONFIG_PATCHES_COLORED_NEWS_DATE, "colored_new_data", &_patches.colored_news_date, 1900, 2200, 5, NULL}, - {PE_INT32, PF_NOCOMMA, STR_CONFIG_PATCHES_STARTING_DATE, "starting_date", &_patches.starting_date, 1920, MAX_YEAR_END_REAL, 1, NULL}, + {PE_INT32, PF_NOCOMMA, STR_CONFIG_PATCHES_STARTING_DATE, "starting_date", &_patches.starting_date, MAX_YEAR_BEGIN_REAL, MAX_YEAR_END_REAL, 1, NULL}, + {PE_INT32, PF_NOCOMMA | PF_NETWORK_ONLY, STR_CONFIG_PATCHES_ENDING_DATE, "ending_date", &_patches.ending_date, MAX_YEAR_BEGIN_REAL, MAX_YEAR_END_REAL, 1, NULL}, {PE_BOOL, 0, STR_CONFIG_PATCHES_SMOOTH_ECONOMY, "smooth_economy", &_patches.smooth_economy, 0, 0, 0, NULL}, {PE_BOOL, 0, STR_CONFIG_PATCHES_ALLOW_SHARES, "allow_shares", &_patches.allow_shares, 0, 0, 0, NULL}, @@ -795,9 +800,13 @@ static void PatchesSelectionWndProc(Window *w, WindowEvent *e) y = 46; clk = WP(w,def_d).data_2; page = &_patches_page[WP(w,def_d).data_1]; - for(i=0,pe=page->entries; i!=page->num; i++,pe++) { + for (i = 0, pe = page->entries; i != page->num; i++, pe++) { bool disabled = false; bool editable = true; + + if ((pe->flags & PF_NETWORK_ONLY) && !_networking) + editable = false; + // We do not allow changes of some items when we are a client in a networkgame if (!(pe->flags & PF_PLAYERBASED) && _networking && !_network_server) editable = false; @@ -863,7 +872,8 @@ static void PatchesSelectionWndProc(Window *w, WindowEvent *e) x = e->click.pt.x - 5; if (x < 0) return; - if (!(pe->flags & PF_PLAYERBASED) && _networking && !_network_server) + if (((pe->flags & PF_NETWORK_ONLY) && !_networking) || // return if action is only active in network + (!(pe->flags & PF_PLAYERBASED) && _networking && !_network_server)) // return if only server can change it return; if (x < 21) { // clicked on the icon on the left side. Either scroller or bool on/off diff --git a/variables.h b/variables.h index 78ac261447..a743e3dde2 100644 --- a/variables.h +++ b/variables.h @@ -160,6 +160,7 @@ typedef struct Patches { bool ai_disable_veh_aircraft; // disable types for AI bool ai_disable_veh_ship; // disable types for AI uint32 starting_date; // starting date + uint32 ending_date; // end of the game (just show highscore) uint32 colored_news_date; // when does newspaper become colored? bool keep_all_autosave; // name the autosave in a different way. diff --git a/window.h b/window.h index ca2691508b..4f59dab9ed 100644 --- a/window.h +++ b/window.h @@ -365,9 +365,14 @@ typedef struct { } news_d; typedef struct { - int i; - int j; -} general_d; + uint32 background_img; + int8 rank; +} highscore_d; + +typedef struct { + int height; + uint16 counter; +} scroller_d; typedef enum VehicleListFlags { VL_DESC = 0x01,