From 470ed2643e8887c46e98e2950a3ca3b92311e858 Mon Sep 17 00:00:00 2001 From: translators Date: Fri, 8 Jun 2018 19:45:41 +0200 Subject: [PATCH 01/26] Update: Translations from eints croatian: 4 changes by VoyagerOne french: 3 changes by glx polish: 2 changes by McZapkie --- src/lang/croatian.txt | 4 ++++ src/lang/french.txt | 3 +++ src/lang/polish.txt | 2 ++ 3 files changed, 9 insertions(+) diff --git a/src/lang/croatian.txt b/src/lang/croatian.txt index eab38ae16a..8a673fc4b2 100644 --- a/src/lang/croatian.txt +++ b/src/lang/croatian.txt @@ -3083,6 +3083,7 @@ STR_TOWN_POPULATION :{BLACK}Svjetsko STR_TOWN_VIEW_TOWN_CAPTION :{WHITE}{TOWN} STR_TOWN_VIEW_CITY_CAPTION :{WHITE}{TOWN} (Metropola) STR_TOWN_VIEW_POPULATION_HOUSES :{BLACK}Stanovništvo: {ORANGE}{COMMA}{BLACK} Kuće: {ORANGE}{COMMA} +STR_TOWN_VIEW_CARGO_LAST_MONTH_MAX :{BLACK}{CARGO_LIST} prošli mjesec: {ORANGE}{COMMA}{BLACK} maks: {ORANGE}{COMMA} STR_TOWN_VIEW_CARGO_FOR_TOWNGROWTH :{BLACK}Potrebno tereta za rast grada: STR_TOWN_VIEW_CARGO_FOR_TOWNGROWTH_REQUIRED_GENERAL :{ORANGE}{STRING}{RED} potrebno STR_TOWN_VIEW_CARGO_FOR_TOWNGROWTH_REQUIRED_WINTER :{ORANGE}{STRING}{BLACK} potrebno zimi @@ -4329,6 +4330,7 @@ STR_ERROR_DRIVE_THROUGH_ON_TOWN_ROAD :{WHITE}... ovo STR_ERROR_DRIVE_THROUGH_DIRECTION :{WHITE}... cesta je orijentirana u krivom smjeru STR_ERROR_DRIVE_THROUGH_CORNER :{WHITE}... prolazne postaje ne mogu imati zavoje STR_ERROR_DRIVE_THROUGH_JUNCTION :{WHITE}... prolazne postaje ne mogu imati raskrižja +STR_ERROR_DRIVE_THROUGH_ON_ONEWAY_ROAD :{WHITE}... cesta je jednosmjerna ili je blokirana # Station destruction related errors STR_ERROR_CAN_T_REMOVE_PART_OF_STATION :{WHITE}Nije moguće ukloniti dio postaje... @@ -4580,6 +4582,8 @@ STR_BASESOUNDS_DOS_DESCRIPTION :Originalni zvuk STR_BASESOUNDS_WIN_DESCRIPTION :Originalni zvukovi za Transport Tycoon Deluxe Windows izdanje. STR_BASESOUNDS_NONE_DESCRIPTION :Zvučni paket bez ikakvih zvukova. STR_BASEMUSIC_WIN_DESCRIPTION :Originalna glazba za Transport Tycoon Deluxe Windows izdanje. +STR_BASEMUSIC_DOS_DESCRIPTION :Originalna glazba za Transport Tycoon Deluxe DOS izdanje. +STR_BASEMUSIC_TTO_DESCRIPTION :Originalna glazba za Transport Tycoon (original/editor svijeta) DOS izdanje. STR_BASEMUSIC_NONE_DESCRIPTION :Glazbeni paket bez ikakve glazbe. ##id 0x2000 diff --git a/src/lang/french.txt b/src/lang/french.txt index 1012e7ff63..368f12e3e7 100644 --- a/src/lang/french.txt +++ b/src/lang/french.txt @@ -4235,6 +4235,7 @@ STR_ERROR_DRIVE_THROUGH_ON_TOWN_ROAD :{WHITE}... cett STR_ERROR_DRIVE_THROUGH_DIRECTION :{WHITE}... mauvaise orientation de la route STR_ERROR_DRIVE_THROUGH_CORNER :{WHITE}... les arrêts ne peuvent pas avoir de virages STR_ERROR_DRIVE_THROUGH_JUNCTION :{WHITE}... les arrêts ne peuvent pas avoir de jonctions +STR_ERROR_DRIVE_THROUGH_ON_ONEWAY_ROAD :{WHITE}... la route est à sens unique ou bloquée # Station destruction related errors STR_ERROR_CAN_T_REMOVE_PART_OF_STATION :{WHITE}Impossible de supprimer une partie de la gare... @@ -4486,6 +4487,8 @@ STR_BASESOUNDS_DOS_DESCRIPTION :Sons originaux STR_BASESOUNDS_WIN_DESCRIPTION :Sons originaux de Transport Tycoon Deluxe (version Windows). STR_BASESOUNDS_NONE_DESCRIPTION :Un pack de sons sans sons. STR_BASEMUSIC_WIN_DESCRIPTION :Musiques originales de Transport Tycoon Deluxe (version Windows). +STR_BASEMUSIC_DOS_DESCRIPTION :Musiques originales de Transport Tycoon Deluxe (version DOS). +STR_BASEMUSIC_TTO_DESCRIPTION :Musiques originales de Transport Tycoon (version Originale/World Editor). STR_BASEMUSIC_NONE_DESCRIPTION :Un pack de musiques sans musiques. ##id 0x2000 diff --git a/src/lang/polish.txt b/src/lang/polish.txt index 629f936b43..36ad07d926 100644 --- a/src/lang/polish.txt +++ b/src/lang/polish.txt @@ -3362,6 +3362,7 @@ STR_TOWN_POPULATION :{BLACK}Populacj STR_TOWN_VIEW_TOWN_CAPTION :{WHITE}{TOWN} STR_TOWN_VIEW_CITY_CAPTION :{WHITE}{TOWN} (Miasto) STR_TOWN_VIEW_POPULATION_HOUSES :{BLACK}Populacja: {ORANGE}{COMMA}{BLACK} Domów: {ORANGE}{COMMA} +STR_TOWN_VIEW_CARGO_LAST_MONTH_MAX :{BLACK}{CARGO_LIST} w ostatnim miesiącu: {ORANGE}{COMMA}{BLACK} najwięcej: {ORANGE}{COMMA} STR_TOWN_VIEW_CARGO_FOR_TOWNGROWTH :{BLACK}Towar potrzebny do rozwoju miasta: STR_TOWN_VIEW_CARGO_FOR_TOWNGROWTH_REQUIRED_GENERAL :{RED}Wymagana {ORANGE}{STRING} STR_TOWN_VIEW_CARGO_FOR_TOWNGROWTH_REQUIRED_WINTER :{ORANGE}{STRING}{BLACK} wymagane zimą @@ -4614,6 +4615,7 @@ STR_ERROR_DRIVE_THROUGH_ON_TOWN_ROAD :{WHITE}... ta d STR_ERROR_DRIVE_THROUGH_DIRECTION :{WHITE}... droga jest zorientowana w złym kierunku STR_ERROR_DRIVE_THROUGH_CORNER :{WHITE}... przystanki przelotowe nie mogą mieć zakrętów STR_ERROR_DRIVE_THROUGH_JUNCTION :{WHITE}... przystanki przelotowe nie mogą mieć skrzyżowań +STR_ERROR_DRIVE_THROUGH_ON_ONEWAY_ROAD :{WHITE}... droga jest jednokierunkowa lub zablokowana # Station destruction related errors STR_ERROR_CAN_T_REMOVE_PART_OF_STATION :{WHITE}Nie można usunąć części stacji... From 7ae1e3e1e2a23b5cbd09bc88f9abd7ccdde23201 Mon Sep 17 00:00:00 2001 From: translators Date: Tue, 12 Jun 2018 19:45:40 +0200 Subject: [PATCH 02/26] Update: Translations from eints korean: 15 changes by telk5093 greek: 4 changes by Jubilee --- src/lang/greek.txt | 4 ++++ src/lang/korean.txt | 16 +++++++++++++++- 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/src/lang/greek.txt b/src/lang/greek.txt index 8ce81fa2a2..548a7caa29 100644 --- a/src/lang/greek.txt +++ b/src/lang/greek.txt @@ -3092,6 +3092,7 @@ STR_TOWN_POPULATION :{BLACK}Παγκ STR_TOWN_VIEW_TOWN_CAPTION :{WHITE}{TOWN} STR_TOWN_VIEW_CITY_CAPTION :{WHITE}{TOWN} (Πόλη) STR_TOWN_VIEW_POPULATION_HOUSES :{BLACK}Πληθυσμός: {ORANGE}{COMMA}{BLACK} Σπίτια: {ORANGE}{COMMA} +STR_TOWN_VIEW_CARGO_LAST_MONTH_MAX :{BLACK}{CARGO_LIST} προηγούμενος μήνας: {ORANGE}{COMMA}{BLACK} μέγιστο: {ORANGE}{COMMA} STR_TOWN_VIEW_CARGO_FOR_TOWNGROWTH :{BLACK}Εμπορεύματα που χρειάζονται για την επέκταση της πόλης: STR_TOWN_VIEW_CARGO_FOR_TOWNGROWTH_REQUIRED_GENERAL :{ORANGE}{STRING}{RED} απαιτείται STR_TOWN_VIEW_CARGO_FOR_TOWNGROWTH_REQUIRED_WINTER :{ORANGE}{STRING}{BLACK} απαιτείται τον χειμώνα @@ -4344,6 +4345,7 @@ STR_ERROR_DRIVE_THROUGH_ON_TOWN_ROAD :{WHITE}... αυ STR_ERROR_DRIVE_THROUGH_DIRECTION :{WHITE}... ο δρόμος βλέπει σε λάθος κατεύθυνση STR_ERROR_DRIVE_THROUGH_CORNER :{WHITE}... οι μη τερματικοί σταθμοί δε μπορούν να έχουν στροφές STR_ERROR_DRIVE_THROUGH_JUNCTION :{WHITE}... οι μη τερματικοί σταθμοί δε μπορούν να έχουν διασταυρώσεις +STR_ERROR_DRIVE_THROUGH_ON_ONEWAY_ROAD :{WHITE}... ο δρόμος είναι μονόδρομος η μπλοκαρισμένος # Station destruction related errors STR_ERROR_CAN_T_REMOVE_PART_OF_STATION :{WHITE}Δεν μπορεί να αφαιρεθεί μέρος του σταθμού... @@ -4595,6 +4597,8 @@ STR_BASESOUNDS_DOS_DESCRIPTION :Αρχικοί STR_BASESOUNDS_WIN_DESCRIPTION :Αρχικοί ήχοι από το Transport Tycoon Deluxe έκδοση Windows. STR_BASESOUNDS_NONE_DESCRIPTION :Ένα πάκετο ήχων χώρις ήχους. STR_BASEMUSIC_WIN_DESCRIPTION :Αρχική μουσική από το Transport Tycoon Deluxe έκδοση Windows. +STR_BASEMUSIC_DOS_DESCRIPTION :Αρχική μουσική από το Transport Tycoon Deluxe έκδοση DOS. +STR_BASEMUSIC_TTO_DESCRIPTION :Αρχική μουσική από το Transport Tycoon (Αρχικός Επεξεργαστής Κόσμου) έκδοση DOS. STR_BASEMUSIC_NONE_DESCRIPTION :Ένα πάκετο μουσικής χωρίς πραγματική μουσική. ##id 0x2000 diff --git a/src/lang/korean.txt b/src/lang/korean.txt index d53a40a40e..e2d85bee6e 100644 --- a/src/lang/korean.txt +++ b/src/lang/korean.txt @@ -651,6 +651,7 @@ STR_MUSIC_RULER_MARKER :{TINY_FONT}{BLA STR_MUSIC_TRACK_NONE :{TINY_FONT}{DKGREEN}-- STR_MUSIC_TRACK_DIGIT :{TINY_FONT}{DKGREEN}{ZEROFILL_NUM} STR_MUSIC_TITLE_NONE :{TINY_FONT}{DKGREEN}------ +STR_MUSIC_TITLE_NOMUSIC :{TINY_FONT}{DKGREEN}사용 가능한 음악 없음 STR_MUSIC_TITLE_NAME :{TINY_FONT}{DKGREEN}"{STRING}" STR_MUSIC_TRACK :{TINY_FONT}{BLACK}트랙 STR_MUSIC_XTITLE :{TINY_FONT}{BLACK}제목 @@ -671,11 +672,14 @@ STR_MUSIC_TOOLTIP_TOGGLE_PROGRAM_SHUFFLE :{BLACK}프로 STR_MUSIC_TOOLTIP_SHOW_MUSIC_TRACK_SELECTION :{BLACK}배경 음악 트랙을 선택할 수 있는 창을 엽니다. # Playlist window +STR_PLAYLIST_MUSIC_SELECTION_SETNAME :{WHITE}음악 프로그램 - '{STRING}' STR_PLAYLIST_TRACK_NAME :{TINY_FONT}{LTBLUE}{ZEROFILL_NUM} "{STRING}" STR_PLAYLIST_TRACK_INDEX :{TINY_FONT}{BLACK}음악 목록 STR_PLAYLIST_PROGRAM :{TINY_FONT}{BLACK}프로그램 - '{STRING}' STR_PLAYLIST_CLEAR :{TINY_FONT}{BLACK}초기화 +STR_PLAYLIST_CHANGE_SET :{BLACK}세트 변경 STR_PLAYLIST_TOOLTIP_CLEAR_CURRENT_PROGRAM_CUSTOM1 :{BLACK}현재 배경 음악 프로그램을 초기화합니다. (사용자1, 사용자2에서만 선택 가능) +STR_PLAYLIST_TOOLTIP_CHANGE_SET :{BLACK}배경 음악 세트를 이미 설치된 다른 세트로 변경합니다. STR_PLAYLIST_TOOLTIP_CLICK_TO_ADD_TRACK :{BLACK}배경 음악 목록에 음악을 추가하려면 클릭하세요. (사용자1, 사용자2에서만 사용 가능) STR_PLAYLIST_TOOLTIP_CLICK_TO_REMOVE_TRACK :{BLACK}배경 음악 목록에서 음악을 제거하려면 클릭하세요. (사용자1, 사용자2에서만 사용 가능) @@ -1335,6 +1339,12 @@ STR_CONFIG_SETTING_SMALLMAP_LAND_COLOUR_HELPTEXT :소형지도 STR_CONFIG_SETTING_SMALLMAP_LAND_COLOUR_GREEN :녹색 STR_CONFIG_SETTING_SMALLMAP_LAND_COLOUR_DARK_GREEN :어두운 녹색 STR_CONFIG_SETTING_SMALLMAP_LAND_COLOUR_VIOLET :보라색 +STR_CONFIG_SETTING_SCROLLMODE :외부 화면 스크롤 행동: {STRING} +STR_CONFIG_SETTING_SCROLLMODE_HELPTEXT :지도를 스크롤 할 때의 행동을 선택합니다. +STR_CONFIG_SETTING_SCROLLMODE_DEFAULT :마우스 오른쪽 클릭으로 외부 화면 이동 (마우스 위치는 고정) +STR_CONFIG_SETTING_SCROLLMODE_RMB_LOCKED :마우스 오른쪽 클릭으로 지도 이동 (마우스 위치는 고정) +STR_CONFIG_SETTING_SCROLLMODE_RMB :마우스 오른쪽 클릭으로 지도 이동 +STR_CONFIG_SETTING_SCROLLMODE_LMB :마우스 왼쪽 클릭으로 지도 이동 STR_CONFIG_SETTING_SMOOTH_SCROLLING :게임 화면을 이동시킬 때 부드럽게 이동: {STRING} STR_CONFIG_SETTING_SMOOTH_SCROLLING_HELPTEXT :소형 지도를 클릭하여 특정 지역으로 스크롤되거나 지도 상의 특정 장소로 이동하는 경우에 주 화면이 어떻게 스크롤 되는지를 설정합니다. 이 설정을 켜면, 화면이 목표 지점까지 부드럽게 이동하고, 설정을 끄면 목표 지점으로 곧바로 넘어가게 됩니다. STR_CONFIG_SETTING_MEASURE_TOOLTIP :건설도구 사용시 거리 도움말 표시: {STRING} @@ -1347,7 +1357,7 @@ STR_CONFIG_SETTING_LIVERIES_ALL :모든 회사 STR_CONFIG_SETTING_PREFER_TEAMCHAT :엔터(ENTER) 키로 같은 팀끼리 채팅: {STRING} STR_CONFIG_SETTING_PREFER_TEAMCHAT_HELPTEXT :이 설정을 켜면, 멀티 플레이시 같은 회사 간의 채팅을 키 대신 키로 할 수 있게 됩니다. STR_CONFIG_SETTING_SCROLLWHEEL_SCROLLING :마우스 휠 동작: {STRING} -STR_CONFIG_SETTING_SCROLLWHEEL_SCROLLING_HELPTEXT :상하좌우로 회전 가능한 마우스 휠(2차원 마우스휠)로 지도를 스크롤할 수 있게 허용합니다. +STR_CONFIG_SETTING_SCROLLWHEEL_SCROLLING_HELPTEXT :상하좌우로 회전 가능한 마우스 휠(2차원 마우스 휠)로 지도를 스크롤할 수 있게 허용합니다. STR_CONFIG_SETTING_SCROLLWHEEL_ZOOM :화면 확대/축소 STR_CONFIG_SETTING_SCROLLWHEEL_SCROLL :지도 스크롤 STR_CONFIG_SETTING_SCROLLWHEEL_OFF :끄기 @@ -2978,6 +2988,7 @@ STR_TOWN_POPULATION :{BLACK}총 인 STR_TOWN_VIEW_TOWN_CAPTION :{WHITE}{TOWN} STR_TOWN_VIEW_CITY_CAPTION :{WHITE}{TOWN} (대도시) STR_TOWN_VIEW_POPULATION_HOUSES :{BLACK}인구: {ORANGE}{COMMA}{BLACK} 가구수: {ORANGE}{COMMA} +STR_TOWN_VIEW_CARGO_LAST_MONTH_MAX :{BLACK}{CARGO_LIST} 지난 달: {ORANGE}{COMMA}{BLACK} 최대: {ORANGE}{COMMA} STR_TOWN_VIEW_CARGO_FOR_TOWNGROWTH :{BLACK}도시가 성장하기 위해 필요한 화물: STR_TOWN_VIEW_CARGO_FOR_TOWNGROWTH_REQUIRED_GENERAL :{ORANGE}{STRING}{RED}{G 0 "이" "가"} 필요함 STR_TOWN_VIEW_CARGO_FOR_TOWNGROWTH_REQUIRED_WINTER :겨울에는 {ORANGE}{STRING}{BLACK}{G 0 "이" "가"} 필요함 @@ -4224,6 +4235,7 @@ STR_ERROR_DRIVE_THROUGH_ON_TOWN_ROAD :{WHITE}... 여 STR_ERROR_DRIVE_THROUGH_DIRECTION :{WHITE}... 도로의 방향과 일치하지 않습니다. STR_ERROR_DRIVE_THROUGH_CORNER :{WHITE}... 도로 통과 정류장은 곡선도로에 건설할 수 없습니다. STR_ERROR_DRIVE_THROUGH_JUNCTION :{WHITE}... 도로 통과 정류장은 교차로에 건설할 수 없습니다. +STR_ERROR_DRIVE_THROUGH_ON_ONEWAY_ROAD :{WHITE}... 도로가 일방통행이거나 막혔습니다 # Station destruction related errors STR_ERROR_CAN_T_REMOVE_PART_OF_STATION :{WHITE}역의 일부를 제거할 수 없습니다... @@ -4475,6 +4487,8 @@ STR_BASESOUNDS_DOS_DESCRIPTION :오리지널 STR_BASESOUNDS_WIN_DESCRIPTION :오리지널 트랜스포트 타이쿤 디럭스 윈도 에디션의 효과음입니다. STR_BASESOUNDS_NONE_DESCRIPTION :아무런 효과음도 없는 효과음 팩입니다. STR_BASEMUSIC_WIN_DESCRIPTION :오리지널 트랜스포트 타이쿤 디럭스 윈도 에디션의 음악입니다. +STR_BASEMUSIC_DOS_DESCRIPTION :오리지널 트랜스포트 타이쿤 디럭스 DOS 에디션의 음악입니다. +STR_BASEMUSIC_TTO_DESCRIPTION :오리지널 트랜스포트 타이쿤 (오리지널/월드 에디터) DOS 에디션의 음악입니다. STR_BASEMUSIC_NONE_DESCRIPTION :실제 음악이 없는 음악 목록입니다. ##id 0x2000 From c2a77280842b5d752ef6c744adab20d9d1b67857 Mon Sep 17 00:00:00 2001 From: PeterN Date: Thu, 14 Jun 2018 09:25:39 +0100 Subject: [PATCH 03/26] Fix: Prevent ships moving into docks after finishing (un)loading. (#6791) --- src/ship_cmd.cpp | 4 ++++ src/track_func.h | 21 +++++++++++++++++++++ src/train_cmd.cpp | 33 ++++++--------------------------- 3 files changed, 31 insertions(+), 27 deletions(-) diff --git a/src/ship_cmd.cpp b/src/ship_cmd.cpp index 967cd4e1d6..0bdcf7041a 100644 --- a/src/ship_cmd.cpp +++ b/src/ship_cmd.cpp @@ -537,6 +537,10 @@ static void ShipController(Ship *v) if (v->current_order.IsType(OT_LEAVESTATION)) { v->current_order.Free(); SetWindowWidgetDirty(WC_VEHICLE_VIEW, v->index, WID_VV_START_STOP); + /* Test if continuing forward would lead to a dead-end, moving into the dock. */ + DiagDirection exitdir = VehicleExitDir(v->direction, v->state); + TileIndex tile = TileAddByDiagDir(v->tile, exitdir); + if (TrackStatusToTrackBits(GetTileTrackStatus(tile, TRANSPORT_WATER, 0, exitdir)) == TRACK_BIT_NONE) goto reverse_direction; } else if (v->dest_tile != 0) { /* We have a target, let's see if we reached it... */ if (v->current_order.IsType(OT_GOTO_WAYPOINT) && diff --git a/src/track_func.h b/src/track_func.h index 68de827ef1..5e1585c086 100644 --- a/src/track_func.h +++ b/src/track_func.h @@ -692,4 +692,25 @@ static inline bool IsUphillTrackdir(Slope slope, Trackdir dir) return HasBit(_uphill_trackdirs[RemoveHalftileSlope(slope)], dir); } +/** + * Determine the side in which the vehicle will leave the tile + * + * @param direction vehicle direction + * @param track vehicle track bits + * @return side of tile the vehicle will leave + */ +static inline DiagDirection VehicleExitDir(Direction direction, TrackBits track) +{ + static const TrackBits state_dir_table[DIAGDIR_END] = { TRACK_BIT_RIGHT, TRACK_BIT_LOWER, TRACK_BIT_LEFT, TRACK_BIT_UPPER }; + + DiagDirection diagdir = DirToDiagDir(direction); + + /* Determine the diagonal direction in which we will exit this tile */ + if (!HasBit(direction, 0) && track != state_dir_table[diagdir]) { + diagdir = ChangeDiagDir(diagdir, DIAGDIRDIFF_90LEFT); + } + + return diagdir; +} + #endif /* TRACK_FUNC_H */ diff --git a/src/train_cmd.cpp b/src/train_cmd.cpp index 9f4ecbea6e..daebb9789c 100644 --- a/src/train_cmd.cpp +++ b/src/train_cmd.cpp @@ -57,27 +57,6 @@ bool IsValidImageIndex(uint8 image_index) return image_index < lengthof(_engine_sprite_base); } -/** - * Determine the side in which the train will leave the tile - * - * @param direction vehicle direction - * @param track vehicle track bits - * @return side of tile the train will leave - */ -static inline DiagDirection TrainExitDir(Direction direction, TrackBits track) -{ - static const TrackBits state_dir_table[DIAGDIR_END] = { TRACK_BIT_RIGHT, TRACK_BIT_LOWER, TRACK_BIT_LEFT, TRACK_BIT_UPPER }; - - DiagDirection diagdir = DirToDiagDir(direction); - - /* Determine the diagonal direction in which we will exit this tile */ - if (!HasBit(direction, 0) && track != state_dir_table[diagdir]) { - diagdir = ChangeDiagDir(diagdir, DIAGDIRDIFF_90LEFT); - } - - return diagdir; -} - /** * Return the cargo weight multiplier to use for a rail vehicle @@ -1872,9 +1851,9 @@ void ReverseTrainDirection(Train *v) return; } - /* TrainExitDir does not always produce the desired dir for depots and + /* VehicleExitDir does not always produce the desired dir for depots and * tunnels/bridges that is needed for UpdateSignalsOnSegment. */ - DiagDirection dir = TrainExitDir(v->direction, v->track); + DiagDirection dir = VehicleExitDir(v->direction, v->track); if (IsRailDepotTile(v->tile) || IsTileType(v->tile, MP_TUNNELBRIDGE)) dir = INVALID_DIAGDIR; if (UpdateSignalsOnSegment(v->tile, dir, v->owner) == SIGSEG_PBS || _settings_game.pf.reserve_paths) { @@ -3098,7 +3077,7 @@ static Vehicle *CheckTrainAtSignal(Vehicle *v, void *data) /* not front engine of a train, inside wormhole or depot, crashed */ if (!t->IsFrontEngine() || !(t->track & TRACK_BIT_MASK)) return NULL; - if (t->cur_speed > 5 || TrainExitDir(t->direction, t->track) != exitdir) return NULL; + if (t->cur_speed > 5 || VehicleExitDir(t->direction, t->track) != exitdir) return NULL; return t; } @@ -3681,7 +3660,7 @@ static TileIndex TrainApproachingCrossingTile(const Train *v) if (!TrainCanLeaveTile(v)) return INVALID_TILE; - DiagDirection dir = TrainExitDir(v->direction, v->track); + DiagDirection dir = VehicleExitDir(v->direction, v->track); TileIndex tile = v->tile + TileOffsByDiagDir(dir); /* not a crossing || wrong axis || unusable rail (wrong type or owner) */ @@ -3718,7 +3697,7 @@ static bool TrainCheckIfLineEnds(Train *v, bool reverse) if (!TrainCanLeaveTile(v)) return true; /* Determine the non-diagonal direction in which we will exit this tile */ - DiagDirection dir = TrainExitDir(v->direction, v->track); + DiagDirection dir = VehicleExitDir(v->direction, v->track); /* Calculate next tile */ TileIndex tile = v->tile + TileOffsByDiagDir(dir); @@ -3786,7 +3765,7 @@ static bool TrainLocoHandler(Train *v, bool mode) /* Try to reserve a path when leaving the station as we * might not be marked as wanting a reservation, e.g. * when an overlength train gets turned around in a station. */ - DiagDirection dir = TrainExitDir(v->direction, v->track); + DiagDirection dir = VehicleExitDir(v->direction, v->track); if (IsRailDepotTile(v->tile) || IsTileType(v->tile, MP_TUNNELBRIDGE)) dir = INVALID_DIAGDIR; if (UpdateSignalsOnSegment(v->tile, dir, v->owner) == SIGSEG_PBS || _settings_game.pf.reserve_paths) { From f5f2125238187eb7b8fa5c5e93a31027338ce240 Mon Sep 17 00:00:00 2001 From: translators Date: Fri, 15 Jun 2018 19:45:39 +0200 Subject: [PATCH 04/26] Update: Translations from eints russian: 11 changes by Lone_Wolf --- src/lang/russian.txt | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/src/lang/russian.txt b/src/lang/russian.txt index 65bc0e1b8a..b8c8037e64 100644 --- a/src/lang/russian.txt +++ b/src/lang/russian.txt @@ -3167,6 +3167,7 @@ STR_TOWN_POPULATION :{BLACK}Насе STR_TOWN_VIEW_TOWN_CAPTION :{WHITE}{TOWN} STR_TOWN_VIEW_CITY_CAPTION :{WHITE}{TOWN} (Мегаполис) STR_TOWN_VIEW_POPULATION_HOUSES :{BLACK}Население: {ORANGE}{COMMA}{BLACK} Зданий: {ORANGE}{COMMA} +STR_TOWN_VIEW_CARGO_LAST_MONTH_MAX :{BLACK}{CARGO_LIST} в прошлом месяце: {ORANGE}{COMMA}{BLACK} Макс.: {ORANGE}{COMMA} STR_TOWN_VIEW_CARGO_FOR_TOWNGROWTH :{BLACK}Груз, необходимый для роста города: STR_TOWN_VIEW_CARGO_FOR_TOWNGROWTH_REQUIRED_GENERAL :{ORANGE}{STRING}{RED} требу{G 0 е е е ю}тся STR_TOWN_VIEW_CARGO_FOR_TOWNGROWTH_REQUIRED_WINTER :{ORANGE}{STRING}{BLACK} требу{G 0 е е е ю}тся зимой @@ -4423,6 +4424,7 @@ STR_ERROR_DRIVE_THROUGH_ON_TOWN_ROAD :{WHITE}... эт STR_ERROR_DRIVE_THROUGH_DIRECTION :{WHITE}... неверное направление дороги STR_ERROR_DRIVE_THROUGH_CORNER :{WHITE}... на проходных остановках нельзя делать повороты STR_ERROR_DRIVE_THROUGH_JUNCTION :{WHITE}... на проходных остановках нельзя делать перекрёстки +STR_ERROR_DRIVE_THROUGH_ON_ONEWAY_ROAD :{WHITE}... дорога односторонняя или заблокирована # Station destruction related errors STR_ERROR_CAN_T_REMOVE_PART_OF_STATION :{WHITE}Невозможно удалить часть станции... @@ -4491,7 +4493,7 @@ STR_ERROR_IMPOSSIBLE_TRACK_COMBINATION :{WHITE}Недо STR_ERROR_MUST_REMOVE_SIGNALS_FIRST :{WHITE}Сначала удалите сигналы STR_ERROR_NO_SUITABLE_RAILROAD_TRACK :{WHITE}Нет подходящих рельсов STR_ERROR_MUST_REMOVE_RAILROAD_TRACK :{WHITE}Сначала удалите рельсы -STR_ERROR_CROSSING_ON_ONEWAY_ROAD :{WHITE}Дорога односторонняя или блокирована +STR_ERROR_CROSSING_ON_ONEWAY_ROAD :{WHITE}Дорога односторонняя или заблокирована STR_ERROR_CROSSING_DISALLOWED :{WHITE}Через этот вид рельсов запрещено строить переезды STR_ERROR_CAN_T_BUILD_SIGNALS_HERE :{WHITE}Здесь невозможно поставить сигнал... STR_ERROR_CAN_T_BUILD_RAILROAD_TRACK :{WHITE}Здесь невозможно проложить рельсы... @@ -4667,13 +4669,15 @@ STR_ERROR_CAN_T_DELETE_SIGN :{WHITE}Не у STR_DESKTOP_SHORTCUT_COMMENT :Экономический симулятор на основе игры «Transport Tycoon Deluxe» # Translatable descriptions in media/baseset/*.ob* files -STR_BASEGRAPHICS_DOS_DESCRIPTION :Оригинальная графика из Transport Tycoon Deluxe для DOS. -STR_BASEGRAPHICS_DOS_DE_DESCRIPTION :Оригинальная графика из немецкой версии Transport Tycoon Deluxe для DOS. -STR_BASEGRAPHICS_WIN_DESCRIPTION :Оригинальная графика из Transport Tycoon Deluxe для Windows. -STR_BASESOUNDS_DOS_DESCRIPTION :Оригинальный набор звукового оформления из игры Transport Tycoon Deluxe для DOS. -STR_BASESOUNDS_WIN_DESCRIPTION :Оригинальный набор звукового оформления из игры Transport Tycoon Deluxe для Windows. +STR_BASEGRAPHICS_DOS_DESCRIPTION :Графика из Transport Tycoon Deluxe для DOS. +STR_BASEGRAPHICS_DOS_DE_DESCRIPTION :Графика из немецкой версии Transport Tycoon Deluxe для DOS. +STR_BASEGRAPHICS_WIN_DESCRIPTION :Графика из Transport Tycoon Deluxe для Windows. +STR_BASESOUNDS_DOS_DESCRIPTION :Набор звукового оформления из игры Transport Tycoon Deluxe для DOS. +STR_BASESOUNDS_WIN_DESCRIPTION :Набор звукового оформления из игры Transport Tycoon Deluxe для Windows. STR_BASESOUNDS_NONE_DESCRIPTION :"Пустой" набор звукового оформления, не содержащий никаких звуков. -STR_BASEMUSIC_WIN_DESCRIPTION :Оригинальный набор музыкального оформления из игры Transport Tycoon Deluxe для Windows. +STR_BASEMUSIC_WIN_DESCRIPTION :Набор музыкального оформления из игры Transport Tycoon Deluxe для Windows. +STR_BASEMUSIC_DOS_DESCRIPTION :Набор музыкального оформления из игры Transport Tycoon Deluxe для DOS. +STR_BASEMUSIC_TTO_DESCRIPTION :Набор музыкального оформления из игры Transport Tycoon Deluxe для DOS. STR_BASEMUSIC_NONE_DESCRIPTION :"Пустой" набор музыкального оформления, не содержащий никакой музыки. ##id 0x2000 From 836d25e738b78d1c8820ecab1f7bd90b0833ca17 Mon Sep 17 00:00:00 2001 From: Niels Martin Hansen Date: Sun, 10 Jun 2018 15:36:55 +0200 Subject: [PATCH 05/26] Codechange: Address some MSVC compiler warnings --- src/3rdparty/squirrel/squirrel/sqdebug.cpp | 2 +- src/genworld_gui.cpp | 10 +++++----- src/newgrf.cpp | 2 +- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/3rdparty/squirrel/squirrel/sqdebug.cpp b/src/3rdparty/squirrel/squirrel/sqdebug.cpp index b163fae52e..2f24e83b66 100644 --- a/src/3rdparty/squirrel/squirrel/sqdebug.cpp +++ b/src/3rdparty/squirrel/squirrel/sqdebug.cpp @@ -115,7 +115,7 @@ void SQVM::Raise_ParamTypeError(SQInteger nparam,SQInteger typemask,SQInteger ty SQInteger found = 0; for(SQInteger i=0; i<16; i++) { - SQInteger mask = 0x00000001 << i; + SQInteger mask = 0x00000001LL << i; if(typemask & (mask)) { if(found>0) StringCat(exptypes,SQString::Create(_ss(this), "|", -1), exptypes); found ++; diff --git a/src/genworld_gui.cpp b/src/genworld_gui.cpp index 359709e361..04eea8e660 100644 --- a/src/genworld_gui.cpp +++ b/src/genworld_gui.cpp @@ -288,7 +288,7 @@ static DropDownList *BuildMapsizeDropDown() for (uint i = MIN_MAP_SIZE_BITS; i <= MAX_MAP_SIZE_BITS; i++) { DropDownListParamStringItem *item = new DropDownListParamStringItem(STR_JUST_INT, i, false); - item->SetParam(0, 1 << i); + item->SetParam(0, 1LL << i); *list->Append() = item; } @@ -336,8 +336,8 @@ struct GenerateLandscapeWindow : public Window { { switch (widget) { case WID_GL_START_DATE_TEXT: SetDParam(0, ConvertYMDToDate(_settings_newgame.game_creation.starting_year, 0, 1)); break; - case WID_GL_MAPSIZE_X_PULLDOWN: SetDParam(0, 1 << _settings_newgame.game_creation.map_x); break; - case WID_GL_MAPSIZE_Y_PULLDOWN: SetDParam(0, 1 << _settings_newgame.game_creation.map_y); break; + case WID_GL_MAPSIZE_X_PULLDOWN: SetDParam(0, 1LL << _settings_newgame.game_creation.map_x); break; + case WID_GL_MAPSIZE_Y_PULLDOWN: SetDParam(0, 1LL << _settings_newgame.game_creation.map_y); break; case WID_GL_MAX_HEIGHTLEVEL_TEXT: SetDParam(0, _settings_newgame.construction.max_heightlevel); break; case WID_GL_SNOW_LEVEL_TEXT: SetDParam(0, _settings_newgame.game_creation.snow_line_height); break; @@ -896,11 +896,11 @@ struct CreateScenarioWindow : public Window break; case WID_CS_MAPSIZE_X_PULLDOWN: - SetDParam(0, 1 << _settings_newgame.game_creation.map_x); + SetDParam(0, 1LL << _settings_newgame.game_creation.map_x); break; case WID_CS_MAPSIZE_Y_PULLDOWN: - SetDParam(0, 1 << _settings_newgame.game_creation.map_y); + SetDParam(0, 1LL << _settings_newgame.game_creation.map_y); break; case WID_CS_FLAT_LAND_HEIGHT_TEXT: diff --git a/src/newgrf.cpp b/src/newgrf.cpp index d249b5e155..4cd61b95cf 100644 --- a/src/newgrf.cpp +++ b/src/newgrf.cpp @@ -4741,7 +4741,7 @@ static void NewSpriteGroup(ByteReader *buf) } } - group->num_ranges = optimised.size(); + group->num_ranges = (uint)optimised.size(); // cast is safe, there should never be 2**31 elements here if (group->num_ranges > 0) { group->ranges = MallocT(group->num_ranges); MemCpyT(group->ranges, &optimised.front(), group->num_ranges); From 276192f714d6816088791435c1b70dfa7122cdbd Mon Sep 17 00:00:00 2001 From: Niels Martin Hansen Date: Sun, 4 Mar 2018 23:34:02 +0100 Subject: [PATCH 06/26] Change #6684: Cutting point overrides for music base sets This improves bad looping of title screen song from Windows TTD, and fixes a long silence at the end of "Can't get there from here" from Windows TTD. --- bin/baseset/orig_win.obm | 12 ++++++++++++ media/baseset/orig_win.obm | 12 ++++++++++++ src/base_media_base.h | 2 ++ src/music.cpp | 17 ++++++++++++++--- src/music/dmusic.cpp | 20 ++++++++++---------- src/music/win32_m.cpp | 12 ++++++------ 6 files changed, 56 insertions(+), 19 deletions(-) diff --git a/bin/baseset/orig_win.obm b/bin/baseset/orig_win.obm index 8e2053e043..c8c4923e0e 100644 --- a/bin/baseset/orig_win.obm +++ b/bin/baseset/orig_win.obm @@ -142,5 +142,17 @@ GM_TT19.GM = Funk Central GM_TT20.GM = Jammit GM_TT21.GM = Movin' On +; MIDI timecodes where the playback should attemp to start and stop short. +; This is to allow fixing undesired silences in original MIDI files. +; However not all music drivers may support this. +[timingtrim] +; Theme has two beats silence at the beginning which prevents clean looping. +GM_TT00.GM = 768:53760 +; Can't Get There From Here from the Windows version has a long silence at the end, +; followed by a solo repeat. This isn't in the original DOS version music and is likely +; unintentional from the people who converted the music from the DOS version. +; Actual song ends after measure 152. +GM_TT10.GM = 0:235008 + [origin] default = You can find it on your Transport Tycoon Deluxe CD-ROM. diff --git a/media/baseset/orig_win.obm b/media/baseset/orig_win.obm index 8b35b6de9d..b5d4b024ed 100644 --- a/media/baseset/orig_win.obm +++ b/media/baseset/orig_win.obm @@ -90,5 +90,17 @@ GM_TT19.GM = Funk Central GM_TT20.GM = Jammit GM_TT21.GM = Movin' On +; MIDI timecodes where the playback should attemp to start and stop short. +; This is to allow fixing undesired silences in original MIDI files. +; However not all music drivers may support this. +[timingtrim] +; Theme has two beats silence at the beginning which prevents clean looping. +GM_TT00.GM = 768:53760 +; Can't Get There From Here from the Windows version has a long silence at the end, +; followed by a solo repeat. This isn't in the original DOS version music and is likely +; unintentional from the people who converted the music from the DOS version. +; Actual song ends after measure 152. +GM_TT10.GM = 0:235008 + [origin] default = You can find it on your Transport Tycoon Deluxe CD-ROM. diff --git a/src/base_media_base.h b/src/base_media_base.h index 407dcda86a..2974db5cb4 100644 --- a/src/base_media_base.h +++ b/src/base_media_base.h @@ -301,6 +301,8 @@ struct MusicSongInfo { const char *filename; ///< file on disk containing song (when used in MusicSet class, this pointer is owned by MD5File object for the file) MusicTrackType filetype; ///< decoder required for song file int cat_index; ///< entry index in CAT file, for filetype==MTT_MPSMIDI + int override_start; ///< MIDI ticks to skip over in beginning + int override_end; ///< MIDI tick to end the song at (0 if no override) }; /** All data of a music set. */ diff --git a/src/music.cpp b/src/music.cpp index e131c02103..65e35f955d 100644 --- a/src/music.cpp +++ b/src/music.cpp @@ -125,6 +125,7 @@ bool MusicSet::FillSetDetails(IniFile *ini, const char *path, const char *full_f this->num_available = 0; IniGroup *names = ini->GetGroup("names"); IniGroup *catindex = ini->GetGroup("catindex"); + IniGroup *timingtrim = ini->GetGroup("timingtrim"); for (uint i = 0, j = 1; i < lengthof(this->songinfo); i++) { const char *filename = this->files[i].filename; if (names == NULL || StrEmpty(filename)) { @@ -150,15 +151,16 @@ bool MusicSet::FillSetDetails(IniFile *ini, const char *path, const char *full_f this->songinfo[i].filetype = MTT_STANDARDMIDI; } + const char *trimmed_filename = filename; /* As we possibly add a path to the filename and we compare * on the filename with the path as in the .obm, we need to * keep stripping path elements until we find a match. */ - for (const char *p = filename; p != NULL; p = strchr(p, PATHSEPCHAR)) { + for (; trimmed_filename != NULL; trimmed_filename = strchr(trimmed_filename, PATHSEPCHAR)) { /* Remove possible double path separator characters from * the beginning, so we don't start reading e.g. root. */ - while (*p == PATHSEPCHAR) p++; + while (*trimmed_filename == PATHSEPCHAR) trimmed_filename++; - item = names->GetItem(p, false); + item = names->GetItem(trimmed_filename, false); if (item != NULL && !StrEmpty(item->value)) break; } @@ -173,6 +175,15 @@ bool MusicSet::FillSetDetails(IniFile *ini, const char *path, const char *full_f this->num_available++; this->songinfo[i].tracknr = j++; + + item = timingtrim->GetItem(trimmed_filename, false); + if (item != NULL && !StrEmpty(item->value)) { + const char *endpos = strchr(item->value, ':'); + if (endpos != NULL) { + this->songinfo[i].override_start = atoi(item->value); + this->songinfo[i].override_end = atoi(endpos + 1); + } + } } } return ret; diff --git a/src/music/dmusic.cpp b/src/music/dmusic.cpp index ce76d22ef4..3b6ae34549 100644 --- a/src/music/dmusic.cpp +++ b/src/music/dmusic.cpp @@ -723,14 +723,6 @@ static void MidiThreadProc(void *) while (current_block < current_file.blocks.size()) { MidiFile::DataBlock &block = current_file.blocks[current_block]; - /* check that block is not in the future */ - REFERENCE_TIME playback_time = current_time - playback_start_time; - if (block.realtime * MIDITIME_TO_REFTIME > playback_time + 3 *_playback.preload_time * MS_TO_REFTIME) { - /* Stop the thread loop until we are at the preload time of the next block. */ - next_timeout = Clamp(((int64)block.realtime * MIDITIME_TO_REFTIME - playback_time) / MS_TO_REFTIME - _playback.preload_time, 0, 1000); - DEBUG(driver, 9, "DMusic thread: Next event in %u ms (music %u, ref %lld)", next_timeout, block.realtime * MIDITIME_TO_REFTIME, playback_time); - break; - } /* check that block isn't at end-of-song override */ if (current_segment.end > 0 && block.ticktime >= current_segment.end) { if (current_segment.loop) { @@ -743,6 +735,14 @@ static void MidiThreadProc(void *) next_timeout = 0; break; } + /* check that block is not in the future */ + REFERENCE_TIME playback_time = current_time - playback_start_time; + if (block.realtime * MIDITIME_TO_REFTIME > playback_time + 3 *_playback.preload_time * MS_TO_REFTIME) { + /* Stop the thread loop until we are at the preload time of the next block. */ + next_timeout = Clamp(((int64)block.realtime * MIDITIME_TO_REFTIME - playback_time) / MS_TO_REFTIME - _playback.preload_time, 0, 1000); + DEBUG(driver, 9, "DMusic thread: Next event in %u ms (music %u, ref %lld)", next_timeout, block.realtime * MIDITIME_TO_REFTIME, playback_time); + break; + } /* Timestamp of the current block. */ block_time = playback_start_time + block.realtime * MIDITIME_TO_REFTIME; @@ -1232,8 +1232,8 @@ void MusicDriver_DMusic::PlaySong(const MusicSongInfo &song) if (!_playback.next_file.LoadSong(song)) return; - _playback.next_segment.start = 0; - _playback.next_segment.end = 0; + _playback.next_segment.start = song.override_start; + _playback.next_segment.end = song.override_end; _playback.next_segment.loop = false; _playback.do_start = true; diff --git a/src/music/win32_m.cpp b/src/music/win32_m.cpp index 51528133b6..93991d88bb 100644 --- a/src/music/win32_m.cpp +++ b/src/music/win32_m.cpp @@ -209,10 +209,6 @@ void CALLBACK TimerCallback(UINT uTimerID, UINT, DWORD_PTR dwUser, DWORD_PTR, DW while (_midi.current_block < _midi.current_file.blocks.size()) { MidiFile::DataBlock &block = _midi.current_file.blocks[_midi.current_block]; - /* check that block is not in the future */ - if (block.realtime / 1000 > playback_time) { - break; - } /* check that block isn't at end-of-song override */ if (_midi.current_segment.end > 0 && block.ticktime >= _midi.current_segment.end) { if (_midi.current_segment.loop) { @@ -223,6 +219,10 @@ void CALLBACK TimerCallback(UINT uTimerID, UINT, DWORD_PTR dwUser, DWORD_PTR, DW } break; } + /* check that block is not in the future */ + if (block.realtime / 1000 > playback_time) { + break; + } byte *data = block.data.Begin(); size_t remaining = block.data.Length(); @@ -315,8 +315,8 @@ void MusicDriver_Win32::PlaySong(const MusicSongInfo &song) return; } - _midi.next_segment.start = 0; - _midi.next_segment.end = 0; + _midi.next_segment.start = song.override_start; + _midi.next_segment.end = song.override_end; _midi.next_segment.loop = false; DEBUG(driver, 2, "Win32-MIDI: PlaySong: setting flag"); From 11a846e3d517c367112287f797065341e5e5c158 Mon Sep 17 00:00:00 2001 From: Niels Martin Hansen Date: Wed, 6 Jun 2018 18:20:22 +0200 Subject: [PATCH 07/26] Change: Compensate for MIDI transmission time when skipping start of song --- src/music/dmusic.cpp | 3 +++ src/music/win32_m.cpp | 9 +++++++-- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/src/music/dmusic.cpp b/src/music/dmusic.cpp index 3b6ae34549..3f5ca2255b 100644 --- a/src/music/dmusic.cpp +++ b/src/music/dmusic.cpp @@ -693,6 +693,9 @@ static void MidiThreadProc(void *) current_segment.start_block = bl; break; } else { + /* Skip the transmission delay compensation performed in the Win32 MIDI driver. + * The DMusic driver will most likely be used with the MS softsynth, which is not subject to transmission delays. + */ DEBUG(driver, 2, "DMusic: timer: start from block %d (ticktime %d, realtime %.3f, bytes %d)", (int)bl, (int)block.ticktime, ((int)block.realtime) / 1000.0, (int)preload_bytes); playback_start_time -= block.realtime * MIDITIME_TO_REFTIME; break; diff --git a/src/music/win32_m.cpp b/src/music/win32_m.cpp index 93991d88bb..3c059ebedd 100644 --- a/src/music/win32_m.cpp +++ b/src/music/win32_m.cpp @@ -184,7 +184,7 @@ void CALLBACK TimerCallback(UINT uTimerID, UINT, DWORD_PTR dwUser, DWORD_PTR, DW /* find first block after start time and pretend playback started earlier * this is to allow all blocks prior to the actual start to still affect playback, * as they may contain important controller and program changes */ - size_t preload_bytes = 0; + uint preload_bytes = 0; for (size_t bl = 0; bl < _midi.current_file.blocks.size(); bl++) { MidiFile::DataBlock &block = _midi.current_file.blocks[bl]; preload_bytes += block.data.Length(); @@ -194,8 +194,13 @@ void CALLBACK TimerCallback(UINT uTimerID, UINT, DWORD_PTR dwUser, DWORD_PTR, DW _midi.current_segment.start_block = bl; break; } else { + /* Calculate offset start time for playback. + * The preload_bytes are used to compensate for delay in transmission over traditional serial MIDI interfaces, + * which have a bitrate of 31,250 bits/sec, and transmit 1+8+1 start/data/stop bits per byte. + * The delay compensation is needed to avoid time-compression of following messages. + */ DEBUG(driver, 2, "Win32-MIDI: timer: start from block %d (ticktime %d, realtime %.3f, bytes %d)", (int)bl, (int)block.ticktime, ((int)block.realtime) / 1000.0, (int)preload_bytes); - _midi.playback_start_time -= block.realtime / 1000; + _midi.playback_start_time -= block.realtime / 1000 - preload_bytes * 1000 / 3125; break; } } From a1b7812c7e5e7a25a9eb51fd397b19606451f8e6 Mon Sep 17 00:00:00 2001 From: Niels Martin Hansen Date: Wed, 6 Jun 2018 20:58:06 +0200 Subject: [PATCH 08/26] Change: Improved looping of title song --- src/base_media_base.h | 1 + src/music/dmusic.cpp | 6 +++--- src/music/win32_m.cpp | 6 +++--- src/music_gui.cpp | 1 + 4 files changed, 8 insertions(+), 6 deletions(-) diff --git a/src/base_media_base.h b/src/base_media_base.h index 2974db5cb4..891ce4fb99 100644 --- a/src/base_media_base.h +++ b/src/base_media_base.h @@ -301,6 +301,7 @@ struct MusicSongInfo { const char *filename; ///< file on disk containing song (when used in MusicSet class, this pointer is owned by MD5File object for the file) MusicTrackType filetype; ///< decoder required for song file int cat_index; ///< entry index in CAT file, for filetype==MTT_MPSMIDI + bool loop; ///< song should play in a tight loop if possible, never ending int override_start; ///< MIDI ticks to skip over in beginning int override_end; ///< MIDI tick to end the song at (0 if no override) }; diff --git a/src/music/dmusic.cpp b/src/music/dmusic.cpp index 3f5ca2255b..d48f51e85f 100644 --- a/src/music/dmusic.cpp +++ b/src/music/dmusic.cpp @@ -832,8 +832,8 @@ static void MidiThreadProc(void *) /* end? */ if (current_block == current_file.blocks.size()) { if (current_segment.loop) { - current_block = 0; - clock->GetTime(&playback_start_time); + current_block = current_segment.start_block; + playback_start_time = block_time - current_file.blocks[current_block].realtime * MIDITIME_TO_REFTIME; } else { _playback.do_stop = true; } @@ -1237,7 +1237,7 @@ void MusicDriver_DMusic::PlaySong(const MusicSongInfo &song) _playback.next_segment.start = song.override_start; _playback.next_segment.end = song.override_end; - _playback.next_segment.loop = false; + _playback.next_segment.loop = song.loop; _playback.do_start = true; SetEvent(_thread_event); diff --git a/src/music/win32_m.cpp b/src/music/win32_m.cpp index 3c059ebedd..a32318db12 100644 --- a/src/music/win32_m.cpp +++ b/src/music/win32_m.cpp @@ -302,8 +302,8 @@ void CALLBACK TimerCallback(UINT uTimerID, UINT, DWORD_PTR dwUser, DWORD_PTR, DW /* end? */ if (_midi.current_block == _midi.current_file.blocks.size()) { if (_midi.current_segment.loop) { - _midi.current_block = 0; - _midi.playback_start_time = timeGetTime(); + _midi.current_block = _midi.current_segment.start_block; + _midi.playback_start_time = timeGetTime() - _midi.current_file.blocks[_midi.current_block].realtime / 1000; } else { _midi.do_stop = true; } @@ -322,7 +322,7 @@ void MusicDriver_Win32::PlaySong(const MusicSongInfo &song) _midi.next_segment.start = song.override_start; _midi.next_segment.end = song.override_end; - _midi.next_segment.loop = false; + _midi.next_segment.loop = song.loop; DEBUG(driver, 2, "Win32-MIDI: PlaySong: setting flag"); _midi.do_stop = _midi.playing; diff --git a/src/music_gui.cpp b/src/music_gui.cpp index 4333e889c7..c5cfb3baea 100644 --- a/src/music_gui.cpp +++ b/src/music_gui.cpp @@ -191,6 +191,7 @@ static void DoPlaySong() FioFindFullPath(filename, lastof(filename), OLD_GM_DIR, songinfo.filename); } songinfo.filename = filename; // non-owned pointer + songinfo.loop = (_game_mode == GM_MENU) && (_music_wnd_cursong == 1); MusicDriver::GetInstance()->PlaySong(songinfo); SetWindowDirty(WC_MUSIC_WINDOW, 0); } From 5ab06ef8a3a5291ddc9bbee7f73f04bd077272e0 Mon Sep 17 00:00:00 2001 From: Niels Martin Hansen Date: Thu, 7 Jun 2018 21:34:24 +0200 Subject: [PATCH 09/26] Fix: Don't complain if CAT music files are missing entirely Just complain if an index into a CAT file that exists is invalid. --- src/base_media_base.h | 2 ++ src/base_media_func.h | 3 ++- src/music.cpp | 5 +++-- 3 files changed, 7 insertions(+), 3 deletions(-) diff --git a/src/base_media_base.h b/src/base_media_base.h index 891ce4fb99..b040abcf9d 100644 --- a/src/base_media_base.h +++ b/src/base_media_base.h @@ -26,6 +26,7 @@ struct ContentInfo; struct MD5File { /** The result of a checksum check */ enum ChecksumResult { + CR_UNKNOWN, ///< The file has not been checked yet CR_MATCH, ///< The file did exist and the md5 checksum did match CR_MISMATCH, ///< The file did exist, just the md5 checksum did not match CR_NO_FILE, ///< The file did not exist @@ -34,6 +35,7 @@ struct MD5File { const char *filename; ///< filename uint8 hash[16]; ///< md5 sum of the file const char *missing_warning; ///< warning when this file is missing + ChecksumResult check_result; ///< cached result of md5 check ChecksumResult CheckMD5(Subdirectory subdir, size_t max_size) const; }; diff --git a/src/base_media_func.h b/src/base_media_func.h index f45956f765..f30824ad2d 100644 --- a/src/base_media_func.h +++ b/src/base_media_func.h @@ -129,7 +129,8 @@ bool BaseSet::FillSetDetails(IniFile *ini, const file->missing_warning = stredup(item->value); } - switch (T::CheckMD5(file, BASESET_DIR)) { + file->check_result = T::CheckMD5(file, BASESET_DIR); + switch (file->check_result) { case MD5File::CR_MATCH: this->valid_files++; this->found_files++; diff --git a/src/music.cpp b/src/music.cpp index 65e35f955d..ffb1d7590a 100644 --- a/src/music.cpp +++ b/src/music.cpp @@ -128,7 +128,7 @@ bool MusicSet::FillSetDetails(IniFile *ini, const char *path, const char *full_f IniGroup *timingtrim = ini->GetGroup("timingtrim"); for (uint i = 0, j = 1; i < lengthof(this->songinfo); i++) { const char *filename = this->files[i].filename; - if (names == NULL || StrEmpty(filename)) { + if (names == NULL || StrEmpty(filename) || this->files[i].check_result == MD5File::CR_NO_FILE) { this->songinfo[i].songname[0] = '\0'; continue; } @@ -143,7 +143,8 @@ bool MusicSet::FillSetDetails(IniFile *ini, const char *path, const char *full_f char *songname = GetMusicCatEntryName(filename, this->songinfo[i].cat_index); if (songname == NULL) { DEBUG(grf, 0, "Base music set song missing from CAT file: %s/%d", filename, this->songinfo[i].cat_index); - return false; + this->songinfo[i].songname[0] = '\0'; + continue; } strecpy(this->songinfo[i].songname, songname, lastof(this->songinfo[i].songname)); free(songname); From 574f547aca42c5c712de98fb424258d45a3ef6df Mon Sep 17 00:00:00 2001 From: Michael Lutz Date: Sat, 16 Jun 2018 00:35:12 +0200 Subject: [PATCH 10/26] Fix 768a31b: When cascading to another text layouter, clear the old font run state left over from the previous (failed) layout attempt. --- src/gfx_layout.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/gfx_layout.cpp b/src/gfx_layout.cpp index 1d9fc7c313..0a9f5a0b13 100644 --- a/src/gfx_layout.cpp +++ b/src/gfx_layout.cpp @@ -597,6 +597,7 @@ static inline void GetLayouter(Layouter::LineCacheItem &line, const char *&str, Font *f = Layouter::GetFont(state.fontsize, state.cur_colour); line.buffer = buff_begin; + fontMapping.Clear(); /* * Go through the whole string while adding Font instances to the font map From 4099506093e842c6b48b93572c125c8f8efc5efa Mon Sep 17 00:00:00 2001 From: translators Date: Sat, 16 Jun 2018 19:45:39 +0200 Subject: [PATCH 11/26] Update: Translations from eints chinese (simplified): 5 changes by xiangyigao --- src/lang/simplified_chinese.txt | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/lang/simplified_chinese.txt b/src/lang/simplified_chinese.txt index 3ff0f3e578..dfa999bab7 100644 --- a/src/lang/simplified_chinese.txt +++ b/src/lang/simplified_chinese.txt @@ -674,7 +674,9 @@ STR_PLAYLIST_TRACK_NAME :{TINY_FONT}{LTB STR_PLAYLIST_TRACK_INDEX :{TINY_FONT}{BLACK}全部音轨列表 STR_PLAYLIST_PROGRAM :{TINY_FONT}{BLACK}当前选用'{STRING}'列表 STR_PLAYLIST_CLEAR :{TINY_FONT}{BLACK}清除 +STR_PLAYLIST_CHANGE_SET :更改设置 STR_PLAYLIST_TOOLTIP_CLEAR_CURRENT_PROGRAM_CUSTOM1 :{BLACK}清除当前列表中曲目{}(仅限自定义1或自定义2) +STR_PLAYLIST_TOOLTIP_CHANGE_SET :{BLACK}选择另一种已安装的音乐 STR_PLAYLIST_TOOLTIP_CLICK_TO_ADD_TRACK :{BLACK}点击音乐曲目以加入当前播放列表{}(仅限自定义1或自定义2) STR_PLAYLIST_TOOLTIP_CLICK_TO_REMOVE_TRACK :{BLACK}点击音乐曲目以从当前播放列表中删除{}(仅限自定义1或自定义2) @@ -1334,6 +1336,7 @@ STR_CONFIG_SETTING_SMALLMAP_LAND_COLOUR_HELPTEXT :设置缩略地 STR_CONFIG_SETTING_SMALLMAP_LAND_COLOUR_GREEN :绿色 STR_CONFIG_SETTING_SMALLMAP_LAND_COLOUR_DARK_GREEN :深绿色 STR_CONFIG_SETTING_SMALLMAP_LAND_COLOUR_VIOLET :紫色 +STR_CONFIG_SETTING_SCROLLMODE_RMB :鼠标右键移动地图 STR_CONFIG_SETTING_SMOOTH_SCROLLING :平滑视角滚动: {STRING} STR_CONFIG_SETTING_SMOOTH_SCROLLING_HELPTEXT :设置在缩略图上点击或者发出转到特定目标的命令时主视角的转换方式,如果“打开”本选项,视角平缓滚动,“关闭”时直接跳转到目标位置 STR_CONFIG_SETTING_MEASURE_TOOLTIP :建设时显示测量数据:{STRING} @@ -3647,6 +3650,7 @@ STR_VEHICLE_INFO_AGE :{COMMA} 年 ({C STR_VEHICLE_INFO_AGE_RED :{RED}{COMMA} 年 ({COMMA}) STR_VEHICLE_INFO_MAX_SPEED :{BLACK}最大速度:{LTBLUE}{VELOCITY} +STR_VEHICLE_INFO_MAX_SPEED_TYPE :{BLACK}最高速度: {LTBLUE}{VELOCITY} {BLACK}飞机种类: {LTBLUE}{STRING} STR_VEHICLE_INFO_WEIGHT_POWER_MAX_SPEED :{BLACK}重量:{LTBLUE}{WEIGHT_SHORT} {BLACK}功率:{LTBLUE}{POWER}{BLACK} 最大速度:{LTBLUE}{VELOCITY} STR_VEHICLE_INFO_WEIGHT_POWER_MAX_SPEED_MAX_TE :{BLACK}重量:{LTBLUE}{WEIGHT_SHORT} {BLACK}功率:{LTBLUE}{POWER}{BLACK} 最大速度:{LTBLUE}{VELOCITY} {BLACK}最大牵引力:{LTBLUE}{FORCE} @@ -4464,6 +4468,7 @@ STR_BASESOUNDS_DOS_DESCRIPTION :运输大亨DOS STR_BASESOUNDS_WIN_DESCRIPTION :Transport Tycoon Deluxe Windows (运输大亨Windows豪华版)的原版音效包. STR_BASESOUNDS_NONE_DESCRIPTION :一个空的音效包. STR_BASEMUSIC_WIN_DESCRIPTION :Transport Tycoon Deluxe(运输大亨Windows豪华版)的原版音乐包 +STR_BASEMUSIC_DOS_DESCRIPTION :运输大亨DOS豪华版原版音乐。 STR_BASEMUSIC_NONE_DESCRIPTION :一个没有实际内容的音乐包. ##id 0x2000 From ae467ffc8ac17a978241c4909301ffdacac54667 Mon Sep 17 00:00:00 2001 From: Michael Lutz Date: Sun, 17 Jun 2018 01:37:38 +0200 Subject: [PATCH 12/26] Fix: Scale default FreeType font size selection by UI zoom level. --- src/fontcache.cpp | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/src/fontcache.cpp b/src/fontcache.cpp index 1b070e1e5e..3a5dd886c1 100644 --- a/src/fontcache.cpp +++ b/src/fontcache.cpp @@ -209,6 +209,7 @@ bool SpriteFontCache::GetDrawGlyphShadow() class FreeTypeFontCache : public FontCache { private: FT_Face face; ///< The font face associated with this font. + int req_size; ///< Requested font size. typedef SmallMap > FontTable; ///< Table with font table cache FontTable font_tables; ///< Cached font tables. @@ -237,6 +238,7 @@ private: GlyphEntry *GetGlyphPtr(GlyphID key); void SetGlyphPtr(GlyphID key, const GlyphEntry *glyph, bool duplicate = false); + void SetFontSize(FontSize fs, FT_Face face, int pixels); public: FreeTypeFontCache(FontSize fs, FT_Face face, int pixels); @@ -267,20 +269,26 @@ static const byte SHADOW_COLOUR = 2; * @param face The font that has to be loaded. * @param pixels The number of pixels this font should be high. */ -FreeTypeFontCache::FreeTypeFontCache(FontSize fs, FT_Face face, int pixels) : FontCache(fs), face(face), glyph_to_sprite(NULL) +FreeTypeFontCache::FreeTypeFontCache(FontSize fs, FT_Face face, int pixels) : FontCache(fs), face(face), req_size(pixels), glyph_to_sprite(NULL) { assert(face != NULL); + this->SetFontSize(fs, face, pixels); +} + +void FreeTypeFontCache::SetFontSize(FontSize fs, FT_Face face, int pixels) +{ if (pixels == 0) { /* Try to determine a good height based on the minimal height recommended by the font. */ - pixels = _default_font_height[this->fs]; + int scaled_height = ScaleGUITrad(_default_font_height[this->fs]); + pixels = scaled_height; TT_Header *head = (TT_Header *)FT_Get_Sfnt_Table(this->face, ft_sfnt_head); if (head != NULL) { /* Font height is minimum height plus the difference between the default * height for this font size and the small size. */ - int diff = _default_font_height[this->fs] - _default_font_height[FS_SMALL]; - pixels = Clamp(min(head->Lowest_Rec_PPEM, 20) + diff, _default_font_height[this->fs], MAX_FONT_SIZE); + int diff = scaled_height - ScaleGUITrad(_default_font_height[FS_SMALL]); + pixels = Clamp(min(head->Lowest_Rec_PPEM, 20) + diff, scaled_height, MAX_FONT_SIZE); } } @@ -395,6 +403,7 @@ found_face: FreeTypeFontCache::~FreeTypeFontCache() { FT_Done_Face(this->face); + this->face = NULL; this->ClearFontCache(); for (FontTable::iterator iter = this->font_tables.Begin(); iter != this->font_tables.End(); iter++) { @@ -424,6 +433,9 @@ void FreeTypeFontCache::ClearFontCache() this->glyph_to_sprite = NULL; Layouter::ResetFontCache(this->fs); + + /* GUI scaling might have changed, determine font size anew if it was automatically selected. */ + if (this->face != NULL && this->req_size == 0) this->SetFontSize(this->fs, this->face, this->req_size); } FreeTypeFontCache::GlyphEntry *FreeTypeFontCache::GetGlyphPtr(GlyphID key) From 10d8b83038413761fda96b022813ff788260287d Mon Sep 17 00:00:00 2001 From: glx Date: Sun, 17 Jun 2018 01:28:08 +0200 Subject: [PATCH 13/26] Fix: library detection on MSYS2 file system --- config.lib | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/config.lib b/config.lib index 67ec692110..50cfa28d2b 100644 --- a/config.lib +++ b/config.lib @@ -2667,6 +2667,11 @@ detect_library() { eval "res=\$$2" if [ -z "$res" ]; then log 2 " trying /mingw/include/$4$5... no" + eval "$2=`ls -1 /mingw$cpu_type/include/$4*.h 2>/dev/null | egrep \"\/$5\$\"`" + fi + eval "res=\$$2" + if [ -z "$res" ]; then + log 2 " trying /mingw$cpu_type/include/$4$5... no" eval "$2=`ls -1 /opt/local/include/$4*.h 2>/dev/null | egrep \"\/$5\$\"`" fi eval "res=\$$2" @@ -2715,6 +2720,11 @@ detect_library() { eval "res=\$$2" if [ -z "$res" ]; then log 2 " trying /mingw/lib/$3... no" + eval "$2=`ls /mingw$cpu_type/lib/*.a 2>/dev/null | egrep \"\/$3\$\"`" + fi + eval "res=\$$2" + if [ -z "$res" ]; then + log 2 " trying /mingw$cpu_type/lib/$3... no" log 1 "configure: error: $2 couldn't be found" log 1 "configure: error: you requested a static link, but I can't find $3" From 21ac11548a6c8905e4e80b678357064fba23fd29 Mon Sep 17 00:00:00 2001 From: translators Date: Fri, 22 Jun 2018 19:45:39 +0200 Subject: [PATCH 14/26] Update: Translations from eints korean: 1 change by telk5093 --- src/lang/korean.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lang/korean.txt b/src/lang/korean.txt index e2d85bee6e..b8d8cf1c4c 100644 --- a/src/lang/korean.txt +++ b/src/lang/korean.txt @@ -4483,7 +4483,7 @@ STR_DESKTOP_SHORTCUT_COMMENT :트랜스포트 STR_BASEGRAPHICS_DOS_DESCRIPTION :오리지널 트랜스포트 타이쿤 디럭스 도스 에디션의 그래픽입니다. STR_BASEGRAPHICS_DOS_DE_DESCRIPTION :오리지널 트랜스포트 타이쿤 디럭스 도스 에디션(독일)의 그래픽입니다. STR_BASEGRAPHICS_WIN_DESCRIPTION :오리지널 트랜스포트 타이쿤 디럭스 윈도 에디션의 그래픽입니다. -STR_BASESOUNDS_DOS_DESCRIPTION :오리지널 트랜스포트 타이쿤 도스 에디션의 효과음입니다. +STR_BASESOUNDS_DOS_DESCRIPTION :오리지널 트랜스포트 타이쿤 디럭스 도스 에디션의 효과음입니다. STR_BASESOUNDS_WIN_DESCRIPTION :오리지널 트랜스포트 타이쿤 디럭스 윈도 에디션의 효과음입니다. STR_BASESOUNDS_NONE_DESCRIPTION :아무런 효과음도 없는 효과음 팩입니다. STR_BASEMUSIC_WIN_DESCRIPTION :오리지널 트랜스포트 타이쿤 디럭스 윈도 에디션의 음악입니다. From 1a5a95aa49bd191901301f9241a21c02a0579c00 Mon Sep 17 00:00:00 2001 From: Alberth Date: Sat, 23 Jun 2018 09:27:09 +0200 Subject: [PATCH 15/26] Fix: Make switch on MD5File::ChecksumResult complete to avoid compiler warning --- src/base_media_func.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/base_media_func.h b/src/base_media_func.h index f30824ad2d..2cb751fe0c 100644 --- a/src/base_media_func.h +++ b/src/base_media_func.h @@ -131,6 +131,9 @@ bool BaseSet::FillSetDetails(IniFile *ini, const file->check_result = T::CheckMD5(file, BASESET_DIR); switch (file->check_result) { + case MD5File::CR_UNKNOWN: + break; + case MD5File::CR_MATCH: this->valid_files++; this->found_files++; From 11ba094582ae3eb8ed20f2f47938e18a827d6163 Mon Sep 17 00:00:00 2001 From: Alberth289346 Date: Sat, 23 Jun 2018 14:02:50 +0200 Subject: [PATCH 16/26] Feature #4186: Append '(City)' behind cities in the town directory (sbr) --- src/lang/english.txt | 1 + src/town_gui.cpp | 14 ++++++++++++-- 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/src/lang/english.txt b/src/lang/english.txt index 15acb5578d..0b5243c893 100644 --- a/src/lang/english.txt +++ b/src/lang/english.txt @@ -2980,6 +2980,7 @@ STR_EDIT_SIGN_SIGN_OSKTITLE :{BLACK}Enter a STR_TOWN_DIRECTORY_CAPTION :{WHITE}Towns STR_TOWN_DIRECTORY_NONE :{ORANGE}- None - STR_TOWN_DIRECTORY_TOWN :{ORANGE}{TOWN}{BLACK} ({COMMA}) +STR_TOWN_DIRECTORY_CITY :{ORANGE}{TOWN}{YELLOW} (City){BLACK} ({COMMA}) STR_TOWN_DIRECTORY_LIST_TOOLTIP :{BLACK}Town names - click on name to centre main view on town. Ctrl+Click opens a new viewport on town location STR_TOWN_POPULATION :{BLACK}World population: {COMMA} diff --git a/src/town_gui.cpp b/src/town_gui.cpp index 16de241c62..17449854d6 100644 --- a/src/town_gui.cpp +++ b/src/town_gui.cpp @@ -747,6 +747,16 @@ public: } } + /** + * Get the string to draw the town name. + * @param t Town to draw. + * @return The string to use. + */ + static StringID GetTownString(const Town *t) + { + return t->larger_town ? STR_TOWN_DIRECTORY_CITY : STR_TOWN_DIRECTORY_TOWN; + } + virtual void DrawWidget(const Rect &r, int widget) const { switch (widget) { @@ -785,7 +795,7 @@ public: SetDParam(0, t->index); SetDParam(1, t->cache.population); - DrawString(text_left, text_right, y + (this->resize.step_height - FONT_HEIGHT_NORMAL) / 2, STR_TOWN_DIRECTORY_TOWN); + DrawString(text_left, text_right, y + (this->resize.step_height - FONT_HEIGHT_NORMAL) / 2, GetTownString(t)); y += this->resize.step_height; if (++n == this->vscroll->GetCapacity()) break; // max number of towns in 1 window @@ -824,7 +834,7 @@ public: SetDParam(0, t->index); SetDParamMaxDigits(1, 8); - d = maxdim(d, GetStringBoundingBox(STR_TOWN_DIRECTORY_TOWN)); + d = maxdim(d, GetStringBoundingBox(GetTownString(t))); } Dimension icon_size = GetSpriteSize(SPR_TOWN_RATING_GOOD); d.width += icon_size.width + 2; From 4fb76db42f886ff21a9f60fc89c786de79d79705 Mon Sep 17 00:00:00 2001 From: Niels Martin Hansen Date: Thu, 24 May 2018 19:40:54 +0200 Subject: [PATCH 17/26] Feature #986: Automatic save when losing connection to a network game --- src/network/core/tcp_game.cpp | 2 ++ src/network/network_client.cpp | 21 +++++++++++++++++++++ src/settings_type.h | 1 + src/table/settings.ini | 6 ++++++ 4 files changed, 30 insertions(+) diff --git a/src/network/core/tcp_game.cpp b/src/network/core/tcp_game.cpp index 9b3f7b5ef3..caa378fc4c 100644 --- a/src/network/core/tcp_game.cpp +++ b/src/network/core/tcp_game.cpp @@ -45,6 +45,8 @@ NetworkRecvStatus NetworkGameSocketHandler::CloseConnection(bool error) { /* Clients drop back to the main menu */ if (!_network_server && _networking) { + extern void ClientNetworkEmergencySave(); // from network_client.cpp + ClientNetworkEmergencySave(); _switch_mode = SM_MENU; _networking = false; ShowErrorMessage(STR_NETWORK_ERROR_LOSTCONNECTION, INVALID_STRING_ID, WL_CRITICAL); diff --git a/src/network/network_client.cpp b/src/network/network_client.cpp index e3bcbb2d87..4b066681d7 100644 --- a/src/network/network_client.cpp +++ b/src/network/network_client.cpp @@ -119,6 +119,19 @@ struct PacketReader : LoadFilter { }; +/** + * Create an emergency savegame when the network connection is lost. + */ +void ClientNetworkEmergencySave() +{ + if (!_settings_client.gui.autosave_on_network_disconnect) return; + + const char *filename = "netsave.sav"; + DEBUG(net, 0, "Client: Performing emergency save (%s)", filename); + SaveOrLoad(filename, SLO_SAVE, DFT_GAME_FILE, AUTOSAVE_DIR, false); +} + + /** * Create a new socket for the client side of the game connection. * @param s The socket to connect with. @@ -670,6 +683,9 @@ NetworkRecvStatus ClientNetworkGameSocketHandler::Receive_SERVER_ERROR(Packet *p ShowErrorMessage(err, INVALID_STRING_ID, WL_CRITICAL); + /* Perform an emergency save if we had already entered the game */ + if (this->status == STATUS_ACTIVE) ClientNetworkEmergencySave(); + DeleteWindowById(WC_NETWORK_STATUS_WINDOW, WN_NETWORK_STATUS_WINDOW_JOIN); return NETWORK_RECV_STATUS_SERVER_ERROR; @@ -1051,6 +1067,8 @@ NetworkRecvStatus ClientNetworkGameSocketHandler::Receive_SERVER_SHUTDOWN(Packet ShowErrorMessage(STR_NETWORK_MESSAGE_SERVER_SHUTDOWN, INVALID_STRING_ID, WL_CRITICAL); } + if (this->status == STATUS_ACTIVE) ClientNetworkEmergencySave(); + return NETWORK_RECV_STATUS_SERVER_ERROR; } @@ -1066,6 +1084,8 @@ NetworkRecvStatus ClientNetworkGameSocketHandler::Receive_SERVER_NEWGAME(Packet ShowErrorMessage(STR_NETWORK_MESSAGE_SERVER_REBOOT, INVALID_STRING_ID, WL_CRITICAL); } + if (this->status == STATUS_ACTIVE) ClientNetworkEmergencySave(); + return NETWORK_RECV_STATUS_SERVER_ERROR; } @@ -1153,6 +1173,7 @@ void ClientNetworkGameSocketHandler::CheckConnection() if (lag > 20) { this->NetworkGameSocketHandler::CloseConnection(); ShowErrorMessage(STR_NETWORK_ERROR_LOSTCONNECTION, INVALID_STRING_ID, WL_CRITICAL); + ClientNetworkEmergencySave(); return; } diff --git a/src/settings_type.h b/src/settings_type.h index fc4059c7bf..f9cc00f3a3 100644 --- a/src/settings_type.h +++ b/src/settings_type.h @@ -114,6 +114,7 @@ struct GUISettings { bool threaded_saves; ///< should we do threaded saves? bool keep_all_autosave; ///< name the autosave in a different way bool autosave_on_exit; ///< save an autosave when you quit the game, but do not ask "Do you really want to quit?" + bool autosave_on_network_disconnect; ///< save an autosave when you get disconnected from a network game with an error? uint8 date_format_in_default_names; ///< should the default savegame/screenshot name use long dates (31th Dec 2008), short dates (31-12-2008) or ISO dates (2008-12-31) byte max_num_autosaves; ///< controls how many autosavegames are made before the game starts to overwrite (names them 0 to max_num_autosaves - 1) bool population_in_label; ///< show the population of a town in his label? diff --git a/src/table/settings.ini b/src/table/settings.ini index 693c8246c4..c061c394f0 100644 --- a/src/table/settings.ini +++ b/src/table/settings.ini @@ -2987,6 +2987,12 @@ flags = SLF_NOT_IN_SAVE | SLF_NO_NETWORK_SYNC def = false cat = SC_BASIC +[SDTC_BOOL] +var = gui.autosave_on_network_disconnect +flags = SLF_NOT_IN_SAVE | SLF_NO_NETWORK_SYNC +def = true +cat = SC_EXPERT + [SDTC_VAR] var = gui.max_num_autosaves type = SLE_UINT8 From 388bca6a42f09147419abdd6c54cc91baf7b6303 Mon Sep 17 00:00:00 2001 From: Greg-21 <34160838+Greg-21@users.noreply.github.com> Date: Wed, 20 Jun 2018 09:14:36 +0200 Subject: [PATCH 18/26] Update: Changed some things in Readme file Changed HTTP links to HTTPS, fixed some links to articles on OpenTTD Wiki, added Windows 10 and 8.1 to info section about path structure in these OSes, removed some unnecessary whitespaces (and added several others), and some other less important changes. --- README.md | 192 +++++++++++++++++++++++++++--------------------------- 1 file changed, 96 insertions(+), 96 deletions(-) diff --git a/README.md b/README.md index 62007b7072..7f5d244f58 100644 --- a/README.md +++ b/README.md @@ -42,9 +42,10 @@ The easiest way to contact the OpenTTD team is by submitting bug reports or posting comments in our forums. You can also chat with us on IRC (#openttd on irc.oftc.net). -The OpenTTD homepage is [http://www.openttd.org/](http://www.openttd.org/). +The OpenTTD homepage is https://www.openttd.org. -You can also find the OpenTTD forums at [http://forum.openttd.org/](http://forum.openttd.org/). +You can also find the OpenTTD forums at +[https://www.tt-forums.net](https://www.tt-forums.net/viewforum.php?f=55). ### 2.1) Reporting bugs @@ -53,11 +54,11 @@ through the file called 'known-bugs.txt' which is distributed with OpenTTD like this readme. For tracking our bugs we are using GitHub's issue tracker. You can find -the tracker at [https://github.com/OpenTTD/OpenTTD/issues](https://github.com/OpenTTD/OpenTTD/issues). Before actually reporting take a look -through the already reported bugs there to see if the bug is already known. -The 'known-bugs.txt' file might be a bit outdated at the moment you are -reading it as only bugs known before the release are documented there. Also -look through the recently closed bugs. +the tracker at https://github.com/OpenTTD/OpenTTD/issues. Before actually +reporting take a look through the already reported bugs there to see if +the bug is already known. The 'known-bugs.txt' file might be a bit outdated +at the moment you are reading it as only bugs known before the release +are documented there. Also look through the recently closed bugs. When you are sure it is not already reported you should: @@ -80,8 +81,8 @@ following information in your bug report: - Bug details, including instructions how to reproduce it - Platform (Windows, Linux, FreeBSD, …) and compiler (including version) if you compiled OpenTTD yourself. -- The processor architecture of your OS (32 bits Windows, 64 bits Windows, - Linux on an ARM, Mac OS X on a PowerPC, …) +- The processor architecture of your OS (32-bit Windows, 64-bit Windows, + Linux on an ARM, Mac OS X on a PowerPC, etc.) - Attach a saved game **and** a screenshot if possible - If this bug only occurred recently please note the last version without the bug and the first version including the bug. That way we can fix it @@ -154,7 +155,7 @@ platforms are: - DOS (Allegro) - FreeBSD (SDL) - Linux (SDL or Allegro) -- MacOS X (universal) (Cocoa video and sound drivers) +- macOS (universal) (Cocoa video and sound drivers) - MorphOS (SDL) - OpenBSD (SDL) - OS/2 (SDL) @@ -210,9 +211,9 @@ when using other versions of the game. The free data files, split into OpenGFX for graphics, OpenSFX for sounds and OpenMSX for music can be found at: -- [http://www.openttd.org/download-opengfx](http://www.openttd.org/download-opengfx) for OpenGFX -- [http://www.openttd.org/download-opensfx](http://www.openttd.org/download-opensfx) for OpenSFX -- [http://www.openttd.org/download-openmsx](http://www.openttd.org/download-openmsx) for OpenMSX +- https://www.openttd.org/download-opengfx for OpenGFX +- https://www.openttd.org/download-opensfx for OpenSFX +- https://www.openttd.org/download-openmsx for OpenMSX Please follow the readme of these packages about the installation procedure. The Windows installer can optionally download and install these packages. @@ -224,20 +225,20 @@ have to copy the data files from the CD-ROM into the baseset/ directory. It does not matter whether you copy them from the DOS or Windows version of Transport Tycoon Deluxe. The Windows install can optionally copy these files. You need to copy the following files: - - sample.cat - - trg1r.grf or TRG1.GRF - - trgcr.grf or TRGC.GRF - - trghr.grf or TRGH.GRF - - trgir.grf or TRGI.GRF - - trgtr.grf or TRGT.GRF +- sample.cat +- trg1r.grf or TRG1.GRF +- trgcr.grf or TRGC.GRF +- trghr.grf or TRGH.GRF +- trgir.grf or TRGI.GRF +- trgtr.grf or TRGT.GRF #### 4.1.3) Original Transport Tycoon Deluxe music If you want the Transport Tycoon Deluxe music, copy the appropriate files from the original game into the baseset folder. - - TTD for Windows: All files in the gm/ folder (gm_tt00.gm up to gm_tt21.gm) - - TTD for DOS: The GM.CAT file - - Transport Tycoon Original: The GM.CAT file, but rename it to GM-TTO.CAT +- TTD for Windows: All files in the gm/ folder (gm_tt00.gm up to gm_tt21.gm) +- TTD for DOS: The GM.CAT file +- Transport Tycoon Original: The GM.CAT file, but rename it to GM-TTO.CAT #### 4.1.4) AIs @@ -246,10 +247,9 @@ not possible or you want to use an AI that has not been uploaded to the content download system download the tar file and place it in the ai/ directory. If the AI needs libraries you will have to download those too and put them in the ai/library/ directory. All AIs and AI Libraries that have been uploaded to -the content download system can be found at http://noai.openttd.org/downloads/ +the content download system can be found at https://noai.openttd.org/downloads. The AIs and libraries can be found their in the form of .tar.gz packages. -OpenTTD can read inside tar files but it does not extract .tar.gz files by -itself. +OpenTTD can read inside tar files but it does not extract .tar.gz files by itself. To figure out which libraries you need for an AI you have to start the AI and wait for an error message to pop up. The error message will tell you @@ -278,48 +278,49 @@ your operating system: 1. The current working directory (from where you started OpenTTD) - For non-Windows operating systems OpenTTD will not scan for files in this - directory if it is your personal directory, i.e. '~/', or when it is the - root directory, i.e. '/'. + For non-Windows operating systems OpenTTD will not scan for files in this + directory if it is your personal directory, i.e. '~/', or when it is the + root directory, i.e. '/'. + 2. Your personal directory - Windows: - `C:\My Documents\OpenTTD` (95, 98, ME) - `C:\Documents and Settings\\My Documents\OpenTTD` (2000, XP) - - `C:\Users\\Documents\OpenTTD` (Vista, 7) - - Mac OSX: `~/Documents/OpenTTD` + - `C:\Users\\Documents\OpenTTD` (Vista, 7, 8.1, 10) + - macOS: `~/Documents/OpenTTD` - Linux: `$XDG_DATA_HOME/openttd` which is usually `~/.local/share/openttd` - when built with XDG base directory support, otherwise `~/.openttd` + when built with XDG base directory support, otherwise `~/.openttd` 3. The shared directory - Windows: - `C:\Documents and Settings\All Users\Shared Documents\OpenTTD` (2000, XP) - - `C:\Users\Public\Documents\OpenTTD` (Vista, 7) - - Mac OSX: `/Library/Application Support/OpenTTD` + - `C:\Users\Public\Documents\OpenTTD` (Vista, 7, 8.1, 10) + - macOS: `/Library/Application Support/OpenTTD` - Linux: not available 4. The binary directory (where the OpenTTD executable is) - Windows: `C:\Program Files\OpenTTD` - Linux: `/usr/games` 5. The installation directory (Linux only) - Linux: `/usr/share/games/openttd` -6. The application bundle (Mac OSX only) +6. The application bundle (macOS only) - It includes the OpenTTD files (grf+lng) and it will work as long as they - are not touched + It includes the OpenTTD files (grf+lng) and it will work as long as they + are not touched Different types of data or extensions go into different subdirectories of the chosen main OpenTTD directory: -| data type | directory | additional info | +| data type | directory | additional info | | --- | --- | --- | | Config File | (no subdirectory) | | | Screenshots | screenshot | | -| Base Graphics | baseset| (or a subdirectory thereof) | -| Sound Sets | baseset| (or a subdirectory thereof) | -| NewGRFs | newgrf| (or a subdirectory thereof) | -| 32bpp Sets | newgrf| (or a subdirectory thereof) | -| Music Sets | baseset| (or a subdirectory thereof) | -| AIs | ai| (or a subdirectory thereof) | -| AI Libraries | ai/library| (or a subdirectory thereof) | -| Game Scripts (GS) | game| (or a subdirectory thereof) | +| Base Graphics | baseset | (or a subdirectory thereof) | +| Sound Sets | baseset | (or a subdirectory thereof) | +| NewGRFs | newgrf | (or a subdirectory thereof) | +| 32bpp Sets | newgrf | (or a subdirectory thereof) | +| Music Sets | baseset | (or a subdirectory thereof) | +| AIs | ai | (or a subdirectory thereof) | +| AI Libraries | ai/library | (or a subdirectory thereof) | +| Game Scripts (GS) | game | (or a subdirectory thereof) | | GS Libraries | game/library | (or a subdirectory thereof) | | Savegames | save | | | Automatic Savegames | save/autosave | | @@ -331,16 +332,16 @@ use and no files should be added to it or its subdirectories manually. #### Notes: - Linux in the previous list means .deb, but most paths should be similar for - others. + others. - The previous search order is also used for NewGRFs and openttd.cfg. - If openttd.cfg is not found, then it will be created using the 2, 4, 1, 3, - 5 order. When built with XDG base directory support, openttd.cfg will be - created in $XDG_CONFIG_HOME/openttd which is usually ~/.config/openttd. + 5 order. When built with XDG base directory support, openttd.cfg will be + created in $XDG_CONFIG_HOME/openttd which is usually ~/.config/openttd. - Savegames will be relative to the config file only if there is no save/ - directory in paths with higher priority than the config file path, but - autosaves and screenshots will always be relative to the config file. - Unless the configuration file is in $XDG_CONFIG_HOME/openttd, then all - other files will be saved under $XDG_DATA_HOME/openttd. + directory in paths with higher priority than the config file path, but + autosaves and screenshots will always be relative to the config file. + Unless the configuration file is in $XDG_CONFIG_HOME/openttd, then all + other files will be saved under $XDG_DATA_HOME/openttd. #### The preferred setup: @@ -390,19 +391,18 @@ OpenTTD has a lot of features going beyond the original Transport Tycoon Deluxe emulation. Unfortunately, there is currently no comprehensive list of features, but there is a basic features list on the web, and some optional features can be controlled through the Advanced Settings dialog. We also implement some -features known from [TTDPatch](http://www.ttdpatch.net/). +features known from [TTDPatch](https://www.ttdpatch.net). Several important non-standard controls: - Ctrl modifies many commands and makes them more powerful. For example Ctrl - clicking on signals with the build signal tool changes their behaviour, holding - Ctrl while the track build tool is activated changes it to the track removal - tool, and so on. See [http://wiki.openttd.org/Hidden_features](http://wiki.openttd.org/Hidden_features) - for a non-comprehensive list or look at the tooltips. -- Ingame console. More information at - [http://wiki.openttd.org/index.php/Console](http://wiki.openttd.org/index.php/Console) + clicking on signals with the build signal tool changes their behaviour, + holding Ctrl while the track build tool is activated changes it to the track + removal tool, and so on. See https://wiki.openttd.org/Hidden_features + for a non-comprehensive list or look at the tooltips. +- Ingame console. More information at https://wiki.openttd.org/Console - Hovering over a GUI element shows tooltips. This can be changed to right click - via the advanced settings. + via the advanced settings. ### 5.1) Logging of potentially dangerous actions @@ -423,7 +423,7 @@ Information logged: - Changing NewGRF parameters, loading compatible NewGRF - Changing game mode (scenario editor <-> normal game) - Loading game saved in a different OpenTTD / TTDPatch / Transport Tycoon Deluxe / - original Transport Tycoon version + original Transport Tycoon version - Running a modified OpenTTD build - Changing settings affecting NewGRF behaviour (non-network-safe settings) - Triggering NewGRF bugs @@ -452,7 +452,7 @@ you need to add WITH_SDL to the project settings. PNG (WITH_PNG) and ZLIB (WITH_ZLIB) support is enabled by default. For these to work you need their development files. For best results, download the -openttd-useful.zip file from [http://www.openttd.org/download-openttd-useful](http://www.openttd.org/download-openttd-useful) +openttd-useful.zip file from https://www.openttd.org/download-openttd-useful. Put the header files into your compiler's include/ directory and the library (.lib) files into the lib/ directory. For more help with VS see docs/Readme_Windows_MSVC.txt. @@ -469,7 +469,7 @@ Use '`gmake`', but do a '`./configure`' before the first build. OpenTTD can be built with GNU '`make`'. On non-GNU systems it is called '`gmake`'. However, for the first build one has to do a '`./configure`' first. -### MacOS X: +### macOS: Use '`make`' or Xcode (which will then call make for you) This will give you a binary for your CPU type (PPC/Intel) @@ -510,19 +510,19 @@ The following libraries are used by OpenTTD for: - libSDL/liballegro: hardware access (video, sound, mouse) - zlib: (de)compressing of old (0.3.0-1.0.5) savegames, content downloads, - heightmaps + heightmaps - liblzo2: (de)compressing of old (pre 0.3.0) savegames - liblzma: (de)compressing of savegames (1.1.0 and later) - libpng: making screenshots and loading heightmaps - libfreetype: loading generic fonts and rendering them - libfontconfig: searching for fonts, resolving font names to actual fonts - libicu: handling of right-to-left scripts (e.g. Arabic and Persian) and - natural sorting of strings. + natural sorting of strings. OpenTTD does not require any of the libraries to be present, but without liblzma you cannot open most recent savegames and without zlib you cannot open most older savegames or use the content downloading system. -Without libSDL/liballegro on non-Windows and non-MacOS X machines you have +Without libSDL/liballegro on non-Windows and non-macOS machines you have no graphical user interface; you would be building a dedicated server. ### 7.2) Supported compilers @@ -530,25 +530,25 @@ no graphical user interface; you would be building a dedicated server. The following compilers are known to compile OpenTTD: - Microsoft Visual C++ (MSVC) 2005, 2008 and 2010. - Version 2005 gives bogus warnings about scoping issues. + Version 2005 gives bogus warnings about scoping issues. - GNU Compiler Collection (GCC) 3.3 - 4.4, 4.6 - 4.8. - Versions 4.1 and earlier give bogus warnings about uninitialised variables. - Versions 4.4, 4.6 give bogus warnings about freeing non-heap objects. - Versions 4.6 and later give invalid warnings when lto is enabled. - Intel C++ Compiler (ICC) 12.0. - Clang/LLVM 2.9 - 3.0 - Version 2.9 gives bogus warnings about code nonconformity. + Version 2.9 gives bogus warnings about code nonconformity. The following compilers are known not to compile OpenTTD: - Microsoft Visual C++ (MSVC) 2003 and earlier. - GNU Compiler Collection (GCC) 3.2 and earlier. - These old versions fail due to OpenTTD's template usage. + These old versions fail due to OpenTTD's template usage. - GNU Compiler Collection (GCC) 4.5. It optimizes enums too aggressively. - See http://bugs.openttd.org/task/5513 and references therein. + See https://github.com/OpenTTD/OpenTTD/issues/5513 and references therein. - Intel C++ Compiler (ICC) 11.1 and earlier. - Version 10.0 and earlier fail a configure check and fail with recent - system headers. + system headers. - Version 10.1 fails to compile station_gui.cpp. - Version 11.1 fails with an internal error when compiling network.cpp. - Clang/LLVM 2.8 and earlier. @@ -561,7 +561,7 @@ Patches to support more compilers are welcome. To recompile the extra graphics needed to play with the original Transport Tycoon Deluxe graphics you need GRFCodec (which includes NFORenum) as well. -GRFCodec can be found at: [http://www.openttd.org/download-grfcodec](http://www.openttd.org/download-grfcodec) +GRFCodec can be found at https://www.openttd.org/download-grfcodec. The compilation of these extra graphics does generally not happen, unless you remove the graphics file using '`make maintainer-clean`'. @@ -574,17 +574,16 @@ modification of the base set files by the build process. ## 8.0) Translating -See [http://www.openttd.org/development](http://www.openttd.org/development) for up-to-date information. +See https://www.openttd.org/development for up-to-date information. The use of the online Translator service, located at -[http://translator.openttd.org/](http://translator.openttd.org/), is highly -encouraged. For getting an account simply follow the guidelines in the FAQ of -the translator website. +https://translator.openttd.org, is highly encouraged. For getting an account +simply follow the guidelines in the FAQ of the translator website. If for some reason the website is down for a longer period of time, the information below might be of help. -Please contact the translations manager ([http://www.openttd.org/contact](http://www.openttd.org/contact)) +Please contact the translations manager (https://www.openttd.org/contact) before beginning the translation process! This avoids double work, as someone else may have already started translating to the same language. @@ -603,8 +602,8 @@ Note: Do not alter the following parts of the file: - String identifiers (the first word on each line) - Parts of the strings which are in curly braces (such as {STRING}) -- Lines beginning with ## (such as ##id), other than the first two lines of - the file +- Lines beginning with ## (such as ##id), other than the first two lines + of the file ### 8.2) Previewing @@ -636,7 +635,7 @@ If the game is acting strange and you feel adventurous you can try the debugging output. The 'name' variable can help you to display only some type of debugging messages. This is mostly undocumented so best is to look in the source code file debug.c for the various debugging types. For more information -look at [http://wiki.openttd.org/index.php/Command_line](http://wiki.openttd.org/index.php/Command_line). +look at https://wiki.openttd.org/Command_line. The most frequent problem is missing data files. Please install OpenGFX and possibly OpenSFX and OpenMSX. See section 4.1.1 for more information. @@ -660,16 +659,16 @@ and add a suitable font for the small, medium and / or large font, e.g.: You should use a font name like 'Tahoma' or a path to the desired font. -Any NewGRF file used in a game is stored inside the savegame and will refuse -to load if you do not have that NewGRF file available. A list of missing files -can be viewed in the NewGRF window accessible from the file load dialogue window. +Any NewGRF file used in a game is stored inside the savegame and will refuse to +load if you do not have that NewGRF file available. A list of missing files can +be viewed in the NewGRF window accessible from the file load dialogue window. You can try to obtain the missing files from that NewGRF dialogue or – if they -are not available online – you can search manually through our [forum's graphics -development section](http://www.tt-forums.net/viewforum.php?f=66) or GrfCrawler -(http://grfcrawler.tt-forums.net/). Put the NewGRF files in OpenTTD's newgrf folder -(see section 4.2 'OpenTTD directories') and rescan the list of available NewGRFs. -Once you have all missing files, you are set to go. +are not available online – you can search manually through our +[forum's graphics development section](https://www.tt-forums.net/viewforum.php?f=66) +or [GRFCrawler](https://grfcrawler.tt-forums.net). Put the NewGRF files in +OpenTTD's newgrf folder (see section 4.2 'OpenTTD directories') and rescan the +list of available NewGRFs. Once you have all missing files, you are set to go. ## 10.0) Licensing @@ -698,11 +697,13 @@ os/dos/exe2coff/copying.dj for the exact licensing terms. The CWSDPMI implementation in os/dos/cwsdpmi is distributed under a custom binary-only license that prohibits modification. The exact licensing terms can be found in os/dos/cwsdpmi/cwsdpmi.txt. The sources -for these files can be downloaded at its author site, at: -[http://homer.rice.edu/~sandmann/cwsdpmi/csdpmi5s.zip](http://homer.rice.edu/~sandmann/cwsdpmi/csdpmi5s.zip) +for these files can be downloaded at its author site, at +http://homer.rice.edu/~sandmann/cwsdpmi/csdpmi5s.zip. -CONTRIBUTING.md is adapted from [Bootstrap](https://github.com/twbs/bootstrap/blob/master/CONTRIBUTING.md) -under the [Creative Commons Attribution 3.0 Unported License](https://github.com/twbs/bootstrap/blob/master/docs/LICENSE) +CONTRIBUTING.md is adapted from +[Bootstrap](https://github.com/twbs/bootstrap/blob/master/CONTRIBUTING.md) +under the [Creative Commons Attribution 3.0 Unported +License](https://github.com/twbs/bootstrap/blob/master/docs/LICENSE) terms for Bootstrap documentation. ## X.X) Credits @@ -726,7 +727,7 @@ terms for Bootstrap documentation. ### Inactive Developers: - Jean-François Claeys (Belugas) - GUI, newindustries and more (0.4.5 - 1.0) -- Bjarni Corfitzen (Bjarni) - MacOSX port, coder and vehicles (0.3 - 0.7) +- Bjarni Corfitzen (Bjarni) - macOS port, coder and vehicles (0.3 - 0.7) - Victor Fischer (Celestar) - Programming everywhere you need him to (0.3 - 0.6) - Jaroslav Mazanec (KUDr) - YAPG (Yet Another Pathfinder God) ;) (0.4.5 - 0.6) - Jonathan Coome (Maedhros) - High priest of the NewGRF Temple (0.5 - 0.6) @@ -740,10 +741,10 @@ terms for Bootstrap documentation. - Tamás Faragó (Darkvater) - Ex-Lead coder (0.3 - 0.5) - Dominik Scherer (dominik81) - Lead programmer, GUI expert (0.3 - 0.3) -- Emil Djupfeld (egladil) - MacOSX port (0.4 - 0.6) +- Emil Djupfeld (egladil) - macOS port (0.4 - 0.6) - Simon Sasburg (HackyKid) - Bug fixer (0.4 - 0.4.5) - Ludvig Strigeus (ludde) - Original author of OpenTTD, main coder (0.1 - 0.3) -- Cian Duffy (MYOB) - BeOS port / manual writing (0.1 - 0.3) +- Cian Duffy (MYOB) - BeOS port / manual writing (0.1 - 0.3) - Petr Baudiš (pasky) - Many patches, newgrf support, etc. (0.3 - 0.3) - Benedikt Brüggemeier (skidd13) - Bug fixer and code reworker (0.6 - 0.7) - Serge Paquet (vurlix) - 2nd contributor after ludde (0.1 - 0.3) @@ -765,4 +766,3 @@ terms for Bootstrap documentation. - All Translators - For their support to make OpenTTD a truly international game - Bug Reporters - Thanks for all bug reports - Chris Sawyer - For an amazing game! - From 38f29fd5d6871d46eb6a4f285f548edc628b740d Mon Sep 17 00:00:00 2001 From: Greg-21 <34160838+Greg-21@users.noreply.github.com> Date: Sun, 24 Jun 2018 00:56:11 +0200 Subject: [PATCH 19/26] Update: Updated changelog based on... (#6835) ...the changelog in release 1.8 branch. --- changelog.txt | 41 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) diff --git a/changelog.txt b/changelog.txt index 8edba15fb4..5e1540a1e0 100644 --- a/changelog.txt +++ b/changelog.txt @@ -1,3 +1,44 @@ +1.8.0 (2018-04-01) +------------------------------------------------------------------------ +(None) + + +1.8.0-RC1 (2018-03-21) +------------------------------------------------------------------------ +- Feature: [GFX] Climate-specific Action5 extra airport sprites [FS#6664] (r27976) +- Feature: Draw vertical separators at tile distance in the train depot GUI (r27938, r27899) +- Feature: [Build] MSVC 2017 project file generator. Most noticeable, std:c++latest is enabled (r27920, r27919, r27918, r27917) +- Feature: [Build] Project file generator for kdevelop 4/5 [FS#6577] (r27897) +- Feature: Add option to close windows with right click [FS#4950] (r27826, r27825) +- Feature: Vehicle Group Info: Add profits and occupancy display to group vehicle list (r27822) +- Feature: Display aircraft type in vehicle preview/purchase/detail windows (r27802, r27799, r27797) +- Change: [NewGRF] Various performance improvements to resolving VA2 (r27989, r27985, r27984, r27983, r27982) +- Change: [NewGRF] Increase maximum allowed vehicle sprite size to reduce clipping of ships (r27987) +- Change: Check companies for bankruptcy before subtracting reoccuring monthly costs [FS#6679] (r27981) +- Change: [GFX] Replace the office building sprite on various toyland airports with a better fitting sprite [FS#6664] (r27977) +- Change: [GFX] The switch-toolbar icon contained pixels from the fire cycle. Replace the whole icon with a new version [FS#6654] (r27961) +- Change: Reword texts in industry view, when stockpiling is used (r27952) +- Change: Remove the gap between windows when positioning them after opening [FS#6568] (r27934, r27900) +- Change: [Build] Enable usage of static_assert for MSVC (r27916) +- Change: [Build] Preserve PKG_CONFIG_PATH and PKG_CONFIG_LIBDIR environment variables in config.cache file [FS#6614] (r27902) +- Change: Do not cancel headquarter construction and engine-preview-query when shift-clicking (r27889) +- Change: Parse extmidi command string for parameters to pass on (r27834) +- Change: Draw images in centre of buttons (r27829, r27821) +- Fix: Store the map variety setting in the savegame like the other mapgen settings, so restarting maps considers it [FS#6673] (r27978) +- Fix: Hair selection was missing one option [FS#6642] (r27975) +- Fix: Avoid tile operations outside map border when building lock [FS#6662] (r27973) +- Fix: Catenary sprites got mixed up for depots [FS#6670] (r27972) +- Fix: Make automatic window-positioning RTL-aware (r27934, r27900) +- Fix: Automatic window-positioning now uses GUI-scale/style dependent sizes/distances instead of fixed pixel values (r27934, r27900) +- Fix: [NewGRF] While executing random triggers, var 5F should include the new triggers (r27928) +- Fix: [NewGRF] Reset used random triggers only after all A123 chains have been resolved, so that all RA2 in all chains can test the shared triggers (r27928) +- Fix: [NewGRF] Industry random triggers are stored per tile, even when randomising the shared random bits of the parent industry (r27928) +- Fix: [NPF] Reserved track bits were not accounted for when trying to find any safe position (r27912) +- Fix: Do not modify argv[0] [FS#6575] (r27886) +- Fix: Do not search directories when opening ini files as we already have their full path [FS#6421] (r27816) +- Fix: Road tunnel/bridge heads have no trackbits wrt. catenary drawing (r27812) + + 1.7.2 (2017-12-24) ------------------------------------------------------------------------ (None) From 7fed8fe004b488c3cdb15de81b4ed213d22acab4 Mon Sep 17 00:00:00 2001 From: Alberth Date: Sun, 24 Jun 2018 06:52:50 +0200 Subject: [PATCH 20/26] Fix #6553: Make viewport button text unambiguous. --- src/lang/english.txt | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/lang/english.txt b/src/lang/english.txt index 0b5243c893..bd9639075d 100644 --- a/src/lang/english.txt +++ b/src/lang/english.txt @@ -881,10 +881,10 @@ STR_NEWS_EXCLUSIVE_RIGHTS_DESCRIPTION :{BIG_FONT}{BLAC # Extra view window STR_EXTRA_VIEW_PORT_TITLE :{WHITE}Viewport {COMMA} -STR_EXTRA_VIEW_MOVE_VIEW_TO_MAIN :{BLACK}Copy to viewport +STR_EXTRA_VIEW_MOVE_VIEW_TO_MAIN :{BLACK}Change viewport STR_EXTRA_VIEW_MOVE_VIEW_TO_MAIN_TT :{BLACK}Copy the location of the main view to this viewport -STR_EXTRA_VIEW_MOVE_MAIN_TO_VIEW :{BLACK}Paste from viewport -STR_EXTRA_VIEW_MOVE_MAIN_TO_VIEW_TT :{BLACK}Paste the location of this viewport to the main view +STR_EXTRA_VIEW_MOVE_MAIN_TO_VIEW :{BLACK}Change main view +STR_EXTRA_VIEW_MOVE_MAIN_TO_VIEW_TT :{BLACK}Copy the location of this viewport to the main view # Game options window STR_GAME_OPTIONS_CAPTION :{WHITE}Game Options From 458bc90678b93c0a087d0dda1e877166dac25a77 Mon Sep 17 00:00:00 2001 From: Jonathan G Rennison Date: Sun, 24 Jun 2018 17:34:42 +0100 Subject: [PATCH 21/26] Fix: Poor contrast in cargo dest flow legend window cargo labels. Select foreground colour depending on the brightness of the background. Previously all cargo labels were rendered using black text, even the background cargo colour was dark/black. As an example: FIRS coal was black text on a black background. --- src/gfx.cpp | 7 ++++--- src/gfx_func.h | 2 +- src/linkgraph/linkgraph_gui.cpp | 2 +- 3 files changed, 6 insertions(+), 5 deletions(-) diff --git a/src/gfx.cpp b/src/gfx.cpp index 187d197a3d..c9c36019ce 100644 --- a/src/gfx.cpp +++ b/src/gfx.cpp @@ -1111,16 +1111,17 @@ void DoPaletteAnimations() /** * Determine a contrasty text colour for a coloured background. * @param background Background colour. + * @param threshold Background colour brightness threshold below which the background is considered dark and TC_WHITE is returned, range: 0 - 255, default 128. * @return TC_BLACK or TC_WHITE depending on what gives a better contrast. */ -TextColour GetContrastColour(uint8 background) +TextColour GetContrastColour(uint8 background, uint8 threshold) { Colour c = _cur_palette.palette[background]; /* Compute brightness according to http://www.w3.org/TR/AERT#color-contrast. * The following formula computes 1000 * brightness^2, with brightness being in range 0 to 255. */ uint sq1000_brightness = c.r * c.r * 299 + c.g * c.g * 587 + c.b * c.b * 114; - /* Compare with threshold brightness 128 (50%) */ - return sq1000_brightness < 128 * 128 * 1000 ? TC_WHITE : TC_BLACK; + /* Compare with threshold brightness which defaults to 128 (50%) */ + return sq1000_brightness < ((uint) threshold) * ((uint) threshold) * 1000 ? TC_WHITE : TC_BLACK; } /** diff --git a/src/gfx_func.h b/src/gfx_func.h index 99461f9079..9f7cb9153d 100644 --- a/src/gfx_func.h +++ b/src/gfx_func.h @@ -187,7 +187,7 @@ int GetCharacterHeight(FontSize size); extern DrawPixelInfo *_cur_dpi; -TextColour GetContrastColour(uint8 background); +TextColour GetContrastColour(uint8 background, uint8 threshold = 128); /** * All 16 colour gradients diff --git a/src/linkgraph/linkgraph_gui.cpp b/src/linkgraph/linkgraph_gui.cpp index 015dc52ffa..7cb9e50912 100644 --- a/src/linkgraph/linkgraph_gui.cpp +++ b/src/linkgraph/linkgraph_gui.cpp @@ -496,7 +496,7 @@ void LinkGraphLegendWindow::DrawWidget(const Rect &r, int widget) const if (this->IsWidgetDisabled(widget)) return; CargoSpec *cargo = CargoSpec::Get(widget - WID_LGL_CARGO_FIRST); GfxFillRect(r.left + 2, r.top + 2, r.right - 2, r.bottom - 2, cargo->legend_colour); - DrawString(r.left, r.right, (r.top + r.bottom + 1 - FONT_HEIGHT_SMALL) / 2, cargo->abbrev, TC_BLACK, SA_HOR_CENTER); + DrawString(r.left, r.right, (r.top + r.bottom + 1 - FONT_HEIGHT_SMALL) / 2, cargo->abbrev, GetContrastColour(cargo->legend_colour, 73), SA_HOR_CENTER); } } From 889175f7adaeae9e23041363fe2d3f685b6df695 Mon Sep 17 00:00:00 2001 From: translators Date: Sun, 24 Jun 2018 19:45:46 +0200 Subject: [PATCH 22/26] Update: Translations from eints croatian: 1 change by VoyagerOne italian: 1 change by lorenzodv french: 1 change by glx --- src/lang/croatian.txt | 1 + src/lang/french.txt | 1 + src/lang/italian.txt | 1 + 3 files changed, 3 insertions(+) diff --git a/src/lang/croatian.txt b/src/lang/croatian.txt index 8a673fc4b2..3d55e1ce4e 100644 --- a/src/lang/croatian.txt +++ b/src/lang/croatian.txt @@ -3076,6 +3076,7 @@ STR_EDIT_SIGN_SIGN_OSKTITLE :{BLACK}Unesi im STR_TOWN_DIRECTORY_CAPTION :{WHITE}Gradovi STR_TOWN_DIRECTORY_NONE :{ORANGE}- Ništa - STR_TOWN_DIRECTORY_TOWN :{ORANGE}{TOWN}{BLACK} ({COMMA}) +STR_TOWN_DIRECTORY_CITY :{ORANGE}{TOWN}{YELLOW} (Grad){BLACK} ({COMMA}) STR_TOWN_DIRECTORY_LIST_TOOLTIP :{BLACK}Imena gradova - klikni na ime kako bi centrirao pogled na grad. Ctrl+klik otvara novi prozor sa lokacijom grada STR_TOWN_POPULATION :{BLACK}Svjetsko stanovništvo: {COMMA} diff --git a/src/lang/french.txt b/src/lang/french.txt index 368f12e3e7..4232e43877 100644 --- a/src/lang/french.txt +++ b/src/lang/french.txt @@ -2981,6 +2981,7 @@ STR_EDIT_SIGN_SIGN_OSKTITLE :{BLACK}Entrer u STR_TOWN_DIRECTORY_CAPTION :{WHITE}Villes STR_TOWN_DIRECTORY_NONE :{ORANGE}− Aucune − STR_TOWN_DIRECTORY_TOWN :{ORANGE}{TOWN}{BLACK} ({COMMA}) +STR_TOWN_DIRECTORY_CITY :{ORANGE}{TOWN}{YELLOW} (Métropole){BLACK} ({COMMA}) STR_TOWN_DIRECTORY_LIST_TOOLTIP :{BLACK}Noms des villes - Cliquer sur un nom pour centrer la vue principale sur la ville. Ctrl-clic pour ouvrir une nouvelle vue sur la ville. STR_TOWN_POPULATION :{BLACK}Population mondiale{NBSP}: {COMMA} diff --git a/src/lang/italian.txt b/src/lang/italian.txt index 460fff8919..4222a34554 100644 --- a/src/lang/italian.txt +++ b/src/lang/italian.txt @@ -3010,6 +3010,7 @@ STR_EDIT_SIGN_SIGN_OSKTITLE :{BLACK}Inserire STR_TOWN_DIRECTORY_CAPTION :{WHITE}Città STR_TOWN_DIRECTORY_NONE :{ORANGE}- Nessuna - STR_TOWN_DIRECTORY_TOWN :{ORANGE}{TOWN}{BLACK} ({COMMA}) +STR_TOWN_DIRECTORY_CITY :{ORANGE}{TOWN}{YELLOW} (Metropoli){BLACK} ({COMMA}) STR_TOWN_DIRECTORY_LIST_TOOLTIP :{BLACK}Nomi delle città - fare clic su un nome per centrare la visuale principale sulla città. CTRL+clic la mostra in una mini visuale. STR_TOWN_POPULATION :{BLACK}Popolazione mondiale: {COMMA} From 6298b9657103f5dca0f85c1117e720530cc4b037 Mon Sep 17 00:00:00 2001 From: Niels Martin Hansen Date: Sun, 24 Jun 2018 20:06:05 +0200 Subject: [PATCH 23/26] Change: Modernise music control logic implementation (#6839) Rewrite of almost the entire music control logic to a more modern style, hopefully also easier to understand. The old playlist handling made it look like arcane magic, which it doesn't have to be. - Playlists are now stored in std::vector of objects instead of arrays of bytes with magic sentinel values, that need to be rotated around all the time. Position in playlist is stored as a simple index. - The theme song is now reserved for the title screen, it doesn't play on any of the standard playlists, but is still available for use on custom playlists. - When the player enters/leaves the game from the main menu, the music always restarts. - Playback state (playing or not) is kept even if music becomes unavailable due to an empty playlist (or an empty music set), so it can restart immediately if music becomes available again. - The shuffle algorithm was changed to a standard Fisher-Yates. - Possibly better behavior when editing a custom playlist while it's playing. - Custom playlists should be compatible. - Framework for supporting custom playlists with songs from multiple music sets. --- src/music.cpp | 10 +- src/music_gui.cpp | 696 +++++++++++++++++++++++++--------------------- src/openttd.cpp | 3 +- 3 files changed, 387 insertions(+), 322 deletions(-) diff --git a/src/music.cpp b/src/music.cpp index ffb1d7590a..3d0e40bf9b 100644 --- a/src/music.cpp +++ b/src/music.cpp @@ -126,7 +126,8 @@ bool MusicSet::FillSetDetails(IniFile *ini, const char *path, const char *full_f IniGroup *names = ini->GetGroup("names"); IniGroup *catindex = ini->GetGroup("catindex"); IniGroup *timingtrim = ini->GetGroup("timingtrim"); - for (uint i = 0, j = 1; i < lengthof(this->songinfo); i++) { + uint tracknr = 1; + for (uint i = 0; i < lengthof(this->songinfo); i++) { const char *filename = this->files[i].filename; if (names == NULL || StrEmpty(filename) || this->files[i].check_result == MD5File::CR_NO_FILE) { this->songinfo[i].songname[0] = '\0'; @@ -175,7 +176,12 @@ bool MusicSet::FillSetDetails(IniFile *ini, const char *path, const char *full_f } this->num_available++; - this->songinfo[i].tracknr = j++; + /* Number the theme song (if any) track 0, rest are normal */ + if (i == 0) { + this->songinfo[i].tracknr = 0; + } else { + this->songinfo[i].tracknr = tracknr++; + } item = timingtrim->GetItem(trimmed_filename, false); if (item != NULL && !StrEmpty(item->value)) { diff --git a/src/music_gui.cpp b/src/music_gui.cpp index c5cfb3baea..1edfbab41f 100644 --- a/src/music_gui.cpp +++ b/src/music_gui.cpp @@ -10,6 +10,7 @@ /** @file music_gui.cpp GUI for the music playback. */ #include "stdafx.h" +#include #include "openttd.h" #include "base_media_base.h" #include "music/music_driver.hpp" @@ -35,247 +36,385 @@ #include "safeguards.h" -/** - * Get the name of the song. - * @param index of the song. - * @return the name of the song. - */ -static const char *GetSongName(int index) -{ - return BaseMusic::GetUsedSet()->songinfo[index].songname; -} -/** - * Get the track number of the song. - * @param index of the song. - * @return the track number of the song. - */ -static int GetTrackNumber(int index) -{ - return BaseMusic::GetUsedSet()->songinfo[index].tracknr; -} +struct MusicSystem { + struct PlaylistEntry : MusicSongInfo { + const MusicSet *set; ///< music set the song comes from + uint set_index; ///< index of song in set -/** The currently played song */ -static byte _music_wnd_cursong = 1; -/** Whether a song is currently played */ -static bool _song_is_active = false; + PlaylistEntry(const MusicSet *set, uint set_index) : MusicSongInfo(set->songinfo[set_index]), set(set), set_index(set_index) { } + bool IsValid() const { return !StrEmpty(this->songname); } + }; + typedef std::vector Playlist; -/** Indices of the songs in the current playlist */ -static byte _cur_playlist[NUM_SONGS_PLAYLIST + 1]; + enum PlaylistChoices { + PLCH_ALLMUSIC, + PLCH_OLDSTYLE, + PLCH_NEWSTYLE, + PLCH_EZYSTREET, + PLCH_CUSTOM1, + PLCH_CUSTOM2, + PLCH_THEMEONLY, + PLCH_MAX, + }; -/** Indices of all songs */ -static byte _playlist_all[NUM_SONGS_AVAILABLE + 1]; -/** Indices of all old style songs */ -static byte _playlist_old_style[NUM_SONGS_CLASS + 1]; -/** Indices of all new style songs */ -static byte _playlist_new_style[NUM_SONGS_CLASS + 1]; -/** Indices of all ezy street songs */ -static byte _playlist_ezy_street[NUM_SONGS_CLASS + 1]; + Playlist active_playlist; ///< current play order of songs, including any shuffle + Playlist displayed_playlist; ///< current playlist as displayed in GUI, never in shuffled order + Playlist music_set; ///< all songs in current music set, in set order -assert_compile(lengthof(_settings_client.music.custom_1) == NUM_SONGS_PLAYLIST + 1); -assert_compile(lengthof(_settings_client.music.custom_2) == NUM_SONGS_PLAYLIST + 1); + PlaylistChoices selected_playlist; -/** The different playlists that can be played. */ -static byte * const _playlists[] = { - _playlist_all, - _playlist_old_style, - _playlist_new_style, - _playlist_ezy_street, - _settings_client.music.custom_1, - _settings_client.music.custom_2, + void BuildPlaylists(); + + void ChangePlaylist(PlaylistChoices pl); + void ChangeMusicSet(const char *set_name); + void Shuffle(); + void Unshuffle(); + + void Play(); + void Stop(); + void Next(); + void Prev(); + void CheckStatus(); + + bool IsPlaying() const; + bool IsShuffle() const; + PlaylistEntry GetCurrentSong() const; + + bool IsCustomPlaylist() const; + void PlaylistAdd(size_t song_index); + void PlaylistRemove(size_t song_index); + void PlaylistClear(); + +private: + void ChangePlaylistPosition(int ofs); + int playlist_position; + + void SaveCustomPlaylist(PlaylistChoices pl); + + Playlist standard_playlists[PLCH_MAX]; }; -/** - * Validate a playlist. - * @param playlist The playlist to validate. - * @param last The last location in the list. - */ -void ValidatePlaylist(byte *playlist, byte *last) -{ - while (*playlist != 0 && playlist <= last) { - /* Song indices are saved off-by-one so 0 is "nothing". */ - if (*playlist <= NUM_SONGS_AVAILABLE && !StrEmpty(GetSongName(*playlist - 1))) { - playlist++; - continue; - } - for (byte *p = playlist; *p != 0 && p <= last; p++) { - p[0] = p[1]; - } - } +MusicSystem _music; - /* Make sure the list is null terminated. */ - *last = 0; -} -/** Prepare the playlists */ -void InitializeMusic() +/** Rebuild all playlists for the current music set */ +void MusicSystem::BuildPlaylists() { - uint j = 0; + const MusicSet *set = BaseMusic::GetUsedSet(); + + /* Clear current playlists */ + for (size_t i = 0; i < lengthof(this->standard_playlists); ++i) this->standard_playlists[i].clear(); + this->music_set.clear(); + + /* Build standard playlists, and a list of available music */ for (uint i = 0; i < NUM_SONGS_AVAILABLE; i++) { - if (StrEmpty(GetSongName(i))) continue; - _playlist_all[j++] = i + 1; - } - /* Terminate the list */ - _playlist_all[j] = 0; + PlaylistEntry entry(set, i); + if (!entry.IsValid()) continue; - /* Now make the 'styled' playlists */ - for (uint k = 0; k < NUM_SONG_CLASSES; k++) { - j = 0; - for (uint i = 0; i < NUM_SONGS_CLASS; i++) { - int id = k * NUM_SONGS_CLASS + i + 1; - if (StrEmpty(GetSongName(id))) continue; - _playlists[k + 1][j++] = id + 1; + this->music_set.push_back(entry); + + /* Add theme song to theme-only playlist */ + if (i == 0) this->standard_playlists[PLCH_THEMEONLY].push_back(entry); + + /* Don't add the theme song to standard playlists */ + if (i > 0) { + this->standard_playlists[PLCH_ALLMUSIC].push_back(entry); + uint theme = (i - 1) / NUM_SONGS_CLASS; + this->standard_playlists[PLCH_OLDSTYLE + theme].push_back(entry); } - /* Terminate the list */ - _playlists[k + 1][j] = 0; } - ValidatePlaylist(_settings_client.music.custom_1, lastof(_settings_client.music.custom_1)); - ValidatePlaylist(_settings_client.music.custom_2, lastof(_settings_client.music.custom_2)); - - if (BaseMusic::GetUsedSet()->num_available < _music_wnd_cursong) { - /* If there are less songs than the currently played song, - * just pause and reset to no song. */ - _music_wnd_cursong = 0; - _song_is_active = false; - } -} - -static void SkipToPrevSong() -{ - byte *b = _cur_playlist; - byte *p = b; - byte t; - - if (b[0] == 0) return; // empty playlist - - do p++; while (p[0] != 0); // find the end - - t = *--p; // and copy the bytes - while (p != b) { - p--; - p[1] = p[0]; - } - *b = t; - - _song_is_active = false; -} - -static void SkipToNextSong() -{ - byte *b = _cur_playlist; - byte t; - - t = b[0]; - if (t != 0) { - while (b[1] != 0) { - b[0] = b[1]; - b++; + /* Load custom playlists + * Song index offsets are 1-based, zero indicates invalid/end-of-list value */ + for (uint i = 0; i < NUM_SONGS_PLAYLIST; i++) { + if (_settings_client.music.custom_1[i] > 0) { + PlaylistEntry entry(set, _settings_client.music.custom_1[i] - 1); + if (entry.IsValid()) this->standard_playlists[PLCH_CUSTOM1].push_back(entry); } - b[0] = t; + if (_settings_client.music.custom_2[i] > 0) { + PlaylistEntry entry(set, _settings_client.music.custom_2[i] - 1); + if (entry.IsValid()) this->standard_playlists[PLCH_CUSTOM2].push_back(entry); + } + } +} + +/** + * Switch to another playlist, or reload the current one. + * @param pl Playlist to select + */ +void MusicSystem::ChangePlaylist(PlaylistChoices pl) +{ + assert(pl < PLCH_MAX && pl >= PLCH_ALLMUSIC); + + this->displayed_playlist = this->standard_playlists[pl]; + this->active_playlist = this->displayed_playlist; + this->selected_playlist = pl; + this->playlist_position = 0; + + if (this->selected_playlist != PLCH_THEMEONLY) _settings_client.music.playlist = this->selected_playlist; + + if (_settings_client.music.shuffle) { + this->Shuffle(); + /* Shuffle() will also Play() if necessary, only start once */ + } else if (_settings_client.music.playing) { + this->Play(); } - _song_is_active = false; + InvalidateWindowData(WC_MUSIC_TRACK_SELECTION, 0); + InvalidateWindowData(WC_MUSIC_WINDOW, 0); } -static void MusicVolumeChanged(byte new_vol) +/** + * Change to named music set, and reset playback. + * @param set_name Name of music set to select + */ +void MusicSystem::ChangeMusicSet(const char *set_name) { - MusicDriver::GetInstance()->SetVolume(new_vol); + BaseMusic::SetSet(set_name); + + this->BuildPlaylists(); + this->ChangePlaylist(this->selected_playlist); + + InvalidateWindowData(WC_GAME_OPTIONS, WN_GAME_OPTIONS_GAME_OPTIONS, 0, true); } -static void DoPlaySong() +/** Enable shuffle mode and restart playback */ +void MusicSystem::Shuffle() { - char filename[MAX_PATH]; - MusicSongInfo songinfo = BaseMusic::GetUsedSet()->songinfo[_music_wnd_cursong - 1]; // copy - if (FioFindFullPath(filename, lastof(filename), BASESET_DIR, songinfo.filename) == NULL) { - FioFindFullPath(filename, lastof(filename), OLD_GM_DIR, songinfo.filename); + _settings_client.music.shuffle = true; + + this->active_playlist = this->displayed_playlist; + for (size_t i = 0; i < this->active_playlist.size(); i++) { + size_t shuffle_index = InteractiveRandom() % (this->active_playlist.size() - i); + std::swap(this->active_playlist[i], this->active_playlist[i + shuffle_index]); } - songinfo.filename = filename; // non-owned pointer - songinfo.loop = (_game_mode == GM_MENU) && (_music_wnd_cursong == 1); - MusicDriver::GetInstance()->PlaySong(songinfo); - SetWindowDirty(WC_MUSIC_WINDOW, 0); + + if (_settings_client.music.playing) this->Play(); + + InvalidateWindowData(WC_MUSIC_WINDOW, 0); } -static void DoStopMusic() +/** Disable shuffle and restart playback */ +void MusicSystem::Unshuffle() +{ + _settings_client.music.shuffle = false; + this->active_playlist = this->displayed_playlist; + + if (_settings_client.music.playing) this->Play(); + + InvalidateWindowData(WC_MUSIC_WINDOW, 0); +} + +/** Start/restart playback at current song */ +void MusicSystem::Play() +{ + /* Always set the playing flag, even if there is no music */ + _settings_client.music.playing = true; + MusicDriver::GetInstance()->StopSong(); + /* Make sure playlist_position is a valid index, if playlist has changed etc. */ + this->ChangePlaylistPosition(0); + + /* If there is no music, don't try to play it */ + if (this->active_playlist.empty()) return; + + MusicSongInfo song = this->active_playlist[this->playlist_position]; + if (_game_mode == GM_MENU && this->selected_playlist == PLCH_THEMEONLY) song.loop = true; + MusicDriver::GetInstance()->PlaySong(song); + + InvalidateWindowData(WC_MUSIC_WINDOW, 0); +} + +/** Stop playback and set flag that we don't intend to play music */ +void MusicSystem::Stop() { MusicDriver::GetInstance()->StopSong(); - SetWindowDirty(WC_MUSIC_WINDOW, 0); + _settings_client.music.playing = false; + + InvalidateWindowData(WC_MUSIC_WINDOW, 0); } -/** Reload the active playlist data from playlist selection and shuffle setting */ -static void ResetPlaylist() +/** Skip to next track */ +void MusicSystem::Next() { - uint i = 0; - uint j = 0; + this->ChangePlaylistPosition(+1); + if (_settings_client.music.playing) this->Play(); - memset(_cur_playlist, 0, sizeof(_cur_playlist)); - do { - /* File is the index into the file table of the music set. The play list uses 0 as 'no entry', - * so we need to subtract 1. In case of 'no entry' (file = -1), just skip adding it outright. */ - int file = _playlists[_settings_client.music.playlist][i] - 1; - if (file >= 0) { - const char *filename = BaseMusic::GetUsedSet()->files[file].filename; - /* We are now checking for the existence of that file prior - * to add it to the list of available songs */ - if (!StrEmpty(filename) && FioCheckFileExists(filename, BASESET_DIR)) { - _cur_playlist[j] = _playlists[_settings_client.music.playlist][i]; - j++; - } + InvalidateWindowData(WC_MUSIC_WINDOW, 0); +} + +/** Skip to previous track */ +void MusicSystem::Prev() +{ + this->ChangePlaylistPosition(-1); + if (_settings_client.music.playing) this->Play(); + + InvalidateWindowData(WC_MUSIC_WINDOW, 0); +} + +/** Check that music is playing if it should, and that appropriate playlist is active for game/main menu */ +void MusicSystem::CheckStatus() +{ + if ((_game_mode == GM_MENU) != (this->selected_playlist == PLCH_THEMEONLY)) { + /* Make sure the theme-only playlist is active when on the title screen, and not during gameplay */ + this->ChangePlaylist((_game_mode == GM_MENU) ? PLCH_THEMEONLY : (PlaylistChoices)_settings_client.music.playlist); + } + if (this->active_playlist.empty()) return; + /* If we were supposed to be playing, but music has stopped, move to next song */ + if (this->IsPlaying() && !MusicDriver::GetInstance()->IsSongPlaying()) this->Next(); +} + +/** Is the player getting music right now? */ +bool MusicSystem::IsPlaying() const +{ + return _settings_client.music.playing && !this->active_playlist.empty(); +} + +/** Is shuffle mode enabled? */ +bool MusicSystem::IsShuffle() const +{ + return _settings_client.music.shuffle; +} + +/** Return the current song, or a dummy if none */ +MusicSystem::PlaylistEntry MusicSystem::GetCurrentSong() const +{ + if (!this->IsPlaying()) return PlaylistEntry(BaseMusic::GetUsedSet(), 0); + return this->active_playlist[this->playlist_position]; +} + +/** Is one of the custom playlists selected? */ +bool MusicSystem::IsCustomPlaylist() const +{ + return (this->selected_playlist == PLCH_CUSTOM1) || (this->selected_playlist == PLCH_CUSTOM2); +} + +/** + * Append a song to a custom playlist. + * Always adds to the currently active playlist. + * @param song_index Index of song in the current music set to add + */ +void MusicSystem::PlaylistAdd(size_t song_index) +{ + if (!this->IsCustomPlaylist()) return; + + /* Pick out song from the music set */ + if (song_index >= this->music_set.size()) return; + PlaylistEntry entry = this->music_set[song_index]; + + /* Check for maximum length */ + if (this->standard_playlists[this->selected_playlist].size() >= NUM_SONGS_PLAYLIST) return; + + /* Add it to the appropriate playlist, and the display */ + this->standard_playlists[this->selected_playlist].push_back(entry); + this->displayed_playlist.push_back(entry); + + /* Add it to the active playlist, if playback is shuffled select a random position to add at */ + if (this->active_playlist.empty()) { + this->active_playlist.push_back(entry); + if (this->IsPlaying()) this->Play(); + } else if (this->IsShuffle()) { + /* Generate a random position between 0 and n (inclusive, new length) to insert at */ + size_t maxpos = this->displayed_playlist.size(); + size_t newpos = InteractiveRandom() % maxpos; + this->active_playlist.insert(this->active_playlist.begin() + newpos, entry); + /* Make sure to shift up the current playback position if the song was inserted before it */ + if ((int)newpos <= this->playlist_position) this->playlist_position++; + } else { + this->active_playlist.push_back(entry); + } + + this->SaveCustomPlaylist(this->selected_playlist); + + InvalidateWindowData(WC_MUSIC_TRACK_SELECTION, 0); +} + +/** + * Remove a song from a custom playlist. + * @param song_index Index in the custom playlist to remove. + */ +void MusicSystem::PlaylistRemove(size_t song_index) +{ + if (!this->IsCustomPlaylist()) return; + + Playlist &pl = this->standard_playlists[this->selected_playlist]; + if (song_index >= pl.size()) return; + + /* Remove from "simple" playlists */ + PlaylistEntry song = pl[song_index]; + pl.erase(pl.begin() + song_index); + this->displayed_playlist.erase(this->displayed_playlist.begin() + song_index); + + /* Find in actual active playlist (may be shuffled) and remove, + * if it's the current song restart playback */ + for (size_t i = 0; i < this->active_playlist.size(); i++) { + Playlist::iterator s2 = this->active_playlist.begin() + i; + if (s2->filename == song.filename && s2->cat_index == song.cat_index) { + this->active_playlist.erase(s2); + if ((int)i == this->playlist_position && this->IsPlaying()) this->Play(); + break; } - } while (_playlists[_settings_client.music.playlist][++i] != 0 && j < lengthof(_cur_playlist) - 1); + } - /* Do not shuffle when on the intro-start window, as the song to play has to be the original TTD Theme*/ - if (_settings_client.music.shuffle && _game_mode != GM_MENU) { - i = 500; - do { - uint32 r = InteractiveRandom(); - byte *a = &_cur_playlist[GB(r, 0, 5)]; - byte *b = &_cur_playlist[GB(r, 8, 5)]; + this->SaveCustomPlaylist(this->selected_playlist); - if (*a != 0 && *b != 0) { - byte t = *a; - *a = *b; - *b = t; - } - } while (--i); + InvalidateWindowData(WC_MUSIC_TRACK_SELECTION, 0); +} + +/** + * Remove all songs from the current custom playlist. + * Effectively stops playback too. + */ +void MusicSystem::PlaylistClear() +{ + if (!this->IsCustomPlaylist()) return; + + this->standard_playlists[this->selected_playlist].clear(); + this->ChangePlaylist(this->selected_playlist); + + this->SaveCustomPlaylist(this->selected_playlist); +} + +/** + * Change playlist position pointer by the given offset, making sure to keep it within valid range. + * If the playlist is empty, position is always set to 0. + * @param ofs Amount to move playlist position by. + */ +void MusicSystem::ChangePlaylistPosition(int ofs) +{ + if (this->active_playlist.empty()) { + this->playlist_position = 0; + } else { + this->playlist_position += ofs; + while (this->playlist_position >= (int)this->active_playlist.size()) this->playlist_position -= (int)this->active_playlist.size(); + while (this->playlist_position < 0) this->playlist_position += (int)this->active_playlist.size(); } } -static void StopMusic() +/** + * Save a custom playlist to settings after modification. + * @param pl Playlist to store back + */ +void MusicSystem::SaveCustomPlaylist(PlaylistChoices pl) { - _music_wnd_cursong = 0; - DoStopMusic(); - _song_is_active = false; - SetWindowWidgetDirty(WC_MUSIC_WINDOW, 0, 9); -} - -/** Begin playing the next song on the playlist */ -static void PlayPlaylistSong() -{ - if (_cur_playlist[0] == 0) { - ResetPlaylist(); - /* if there is not songs in the playlist, it may indicate - * no file on the gm folder, or even no gm folder. - * Stop the playback, then */ - if (_cur_playlist[0] == 0) { - _song_is_active = false; - _music_wnd_cursong = 0; - _settings_client.music.playing = false; - return; - } + byte *settings_pl; + if (pl == PLCH_CUSTOM1) { + settings_pl = _settings_client.music.custom_1; + } else if (pl == PLCH_CUSTOM2) { + settings_pl = _settings_client.music.custom_2; + } else { + return; } - _music_wnd_cursong = _cur_playlist[0]; - DoPlaySong(); - _song_is_active = true; - SetWindowWidgetDirty(WC_MUSIC_WINDOW, 0, 9); + size_t num = 0; + MemSetT(settings_pl, 0, NUM_SONGS_PLAYLIST); + + for (Playlist::const_iterator song = this->standard_playlists[pl].begin(); song != this->standard_playlists[pl].end(); ++song) { + /* Music set indices in the settings playlist are 1-based, 0 means unused slot */ + settings_pl[num++] = (byte)song->set_index + 1; + } } -void ResetMusic() -{ - _music_wnd_cursong = 1; - DoPlaySong(); -} /** * Check music playback status and start/stop/song-finished. @@ -283,30 +422,7 @@ void ResetMusic() */ void MusicLoop() { - if (!_settings_client.music.playing && _song_is_active) { - StopMusic(); - } else if (_settings_client.music.playing && !_song_is_active) { - PlayPlaylistSong(); - } - - if (!_song_is_active) return; - - if (!MusicDriver::GetInstance()->IsSongPlaying()) { - if (_game_mode != GM_MENU) { - StopMusic(); - SkipToNextSong(); - PlayPlaylistSong(); - } else { - ResetMusic(); - } - } -} - -static void SelectPlaylist(byte list) -{ - _settings_client.music.playlist = list; - InvalidateWindowData(WC_MUSIC_TRACK_SELECTION, 0); - InvalidateWindowData(WC_MUSIC_WINDOW, 0); + _music.CheckStatus(); } /** @@ -316,29 +432,20 @@ static void SelectPlaylist(byte list) void ChangeMusicSet(int index) { if (BaseMusic::GetIndexOfUsedSet() == index) return; - - /* Resume playback after switching? - * Always if music is already playing, and also if the user is switching - * away from an empty music set. - * If the user switches away from an empty set, assume it's because they - * want to hear music now. */ - bool shouldplay = _song_is_active || (BaseMusic::GetUsedSet()->num_available == 0); - StopMusic(); - const char *name = BaseMusic::GetSet(index)->name; - BaseMusic::SetSet(name); - free(BaseMusic::ini_set); - BaseMusic::ini_set = stredup(name); - - InitializeMusic(); - ResetPlaylist(); - _settings_client.music.playing = shouldplay; - - InvalidateWindowData(WC_MUSIC_TRACK_SELECTION, 0); - InvalidateWindowData(WC_MUSIC_WINDOW, 0); - InvalidateWindowData(WC_GAME_OPTIONS, WN_GAME_OPTIONS_GAME_OPTIONS, 0, true); + _music.ChangeMusicSet(name); } +/** + * Prepare the music system for use. + * Called from \c InitializeGame + */ +void InitializeMusic() +{ + _music.BuildPlaylists(); +} + + struct MusicTrackSelectionWindow : public Window { MusicTrackSelectionWindow(WindowDesc *desc, WindowNumber number) : Window(desc) { @@ -395,13 +502,10 @@ struct MusicTrackSelectionWindow : public Window { case WID_MTS_LIST_LEFT: case WID_MTS_LIST_RIGHT: { Dimension d = {0, 0}; - for (uint i = 0; i < NUM_SONGS_AVAILABLE; i++) { - const char *song_name = GetSongName(i); - if (StrEmpty(song_name)) continue; - - SetDParam(0, GetTrackNumber(i)); + for (MusicSystem::Playlist::const_iterator song = _music.music_set.begin(); song != _music.music_set.end(); ++song) { + SetDParam(0, song->tracknr); SetDParam(1, 2); - SetDParamStr(2, GetSongName(i)); + SetDParamStr(2, song->songname); Dimension d2 = GetStringBoundingBox(STR_PLAYLIST_TRACK_NAME); d.width = max(d.width, d2.width); d.height += d2.height; @@ -421,13 +525,10 @@ struct MusicTrackSelectionWindow : public Window { GfxFillRect(r.left + 1, r.top + 1, r.right - 1, r.bottom - 1, PC_BLACK); int y = r.top + WD_FRAMERECT_TOP; - for (uint i = 0; i < NUM_SONGS_AVAILABLE; i++) { - const char *song_name = GetSongName(i); - if (StrEmpty(song_name)) continue; - - SetDParam(0, GetTrackNumber(i)); + for (MusicSystem::Playlist::const_iterator song = _music.music_set.begin(); song != _music.music_set.end(); ++song) { + SetDParam(0, song->tracknr); SetDParam(1, 2); - SetDParamStr(2, song_name); + SetDParamStr(2, song->songname); DrawString(r.left + WD_FRAMERECT_LEFT, r.right - WD_FRAMERECT_RIGHT, y, STR_PLAYLIST_TRACK_NAME); y += FONT_HEIGHT_SMALL; } @@ -438,11 +539,10 @@ struct MusicTrackSelectionWindow : public Window { GfxFillRect(r.left + 1, r.top + 1, r.right - 1, r.bottom - 1, PC_BLACK); int y = r.top + WD_FRAMERECT_TOP; - for (const byte *p = _playlists[_settings_client.music.playlist]; *p != 0; p++) { - uint i = *p - 1; - SetDParam(0, GetTrackNumber(i)); + for (MusicSystem::Playlist::const_iterator song = _music.music_set.begin(); song != _music.music_set.end(); ++song) { + SetDParam(0, song->tracknr); SetDParam(1, 2); - SetDParamStr(2, GetSongName(i)); + SetDParamStr(2, song->songname); DrawString(r.left + WD_FRAMERECT_LEFT, r.right - WD_FRAMERECT_RIGHT, y, STR_PLAYLIST_TRACK_NAME); y += FONT_HEIGHT_SMALL; } @@ -456,42 +556,13 @@ struct MusicTrackSelectionWindow : public Window { switch (widget) { case WID_MTS_LIST_LEFT: { // add to playlist int y = this->GetRowFromWidget(pt.y, widget, 0, FONT_HEIGHT_SMALL); - - if (_settings_client.music.playlist < 4) return; - if (!IsInsideMM(y, 0, BaseMusic::GetUsedSet()->num_available)) return; - - byte *p = _playlists[_settings_client.music.playlist]; - for (uint i = 0; i != NUM_SONGS_PLAYLIST - 1; i++) { - if (p[i] == 0) { - /* Find the actual song number */ - for (uint j = 0; j < NUM_SONGS_AVAILABLE; j++) { - if (GetTrackNumber(j) == y + 1) { - p[i] = j + 1; - break; - } - } - p[i + 1] = 0; - this->SetDirty(); - ResetPlaylist(); - break; - } - } + _music.PlaylistAdd(y); break; } case WID_MTS_LIST_RIGHT: { // remove from playlist int y = this->GetRowFromWidget(pt.y, widget, 0, FONT_HEIGHT_SMALL); - - if (_settings_client.music.playlist < 4) return; - if (!IsInsideMM(y, 0, NUM_SONGS_PLAYLIST)) return; - - byte *p = _playlists[_settings_client.music.playlist]; - for (uint i = y; i != NUM_SONGS_PLAYLIST - 1; i++) { - p[i] = p[i + 1]; - } - - this->SetDirty(); - ResetPlaylist(); + _music.PlaylistRemove(y); break; } @@ -503,17 +574,12 @@ struct MusicTrackSelectionWindow : public Window { } case WID_MTS_CLEAR: // clear - for (uint i = 0; _playlists[_settings_client.music.playlist][i] != 0; i++) _playlists[_settings_client.music.playlist][i] = 0; - this->SetDirty(); - StopMusic(); - ResetPlaylist(); + _music.PlaylistClear(); break; case WID_MTS_ALL: case WID_MTS_OLD: case WID_MTS_NEW: case WID_MTS_EZY: case WID_MTS_CUSTOM1: case WID_MTS_CUSTOM2: // set playlist - SelectPlaylist(widget - WID_MTS_ALL); - StopMusic(); - ResetPlaylist(); + _music.ChangePlaylist((MusicSystem::PlaylistChoices)(widget - WID_MTS_ALL)); break; } } @@ -628,8 +694,8 @@ struct MusicWindow : public Window { case WID_M_TRACK_NAME: { Dimension d = GetStringBoundingBox(STR_MUSIC_TITLE_NONE); - for (uint i = 0; i < NUM_SONGS_AVAILABLE; i++) { - SetDParamStr(0, GetSongName(i)); + for (MusicSystem::Playlist::const_iterator song = _music.music_set.begin(); song != _music.music_set.end(); ++song) { + SetDParamStr(0, song->songname); d = maxdim(d, GetStringBoundingBox(STR_MUSIC_TITLE_NAME)); } d.width += WD_FRAMERECT_LEFT + WD_FRAMERECT_RIGHT; @@ -655,8 +721,8 @@ struct MusicWindow : public Window { break; } StringID str = STR_MUSIC_TRACK_NONE; - if (_song_is_active != 0 && _music_wnd_cursong != 0) { - SetDParam(0, GetTrackNumber(_music_wnd_cursong - 1)); + if (_music.IsPlaying()) { + SetDParam(0, _music.GetCurrentSong().tracknr); SetDParam(1, 2); str = STR_MUSIC_TRACK_DIGIT; } @@ -669,9 +735,9 @@ struct MusicWindow : public Window { StringID str = STR_MUSIC_TITLE_NONE; if (BaseMusic::GetUsedSet()->num_available == 0) { str = STR_MUSIC_TITLE_NOMUSIC; - } else if (_song_is_active != 0 && _music_wnd_cursong != 0) { + } else if (_music.IsPlaying()) { str = STR_MUSIC_TITLE_NAME; - SetDParamStr(0, GetSongName(_music_wnd_cursong - 1)); + SetDParamStr(0, _music.GetCurrentSong().songname); } DrawString(r.left + WD_FRAMERECT_LEFT, r.right - WD_FRAMERECT_RIGHT, r.top + WD_FRAMERECT_TOP, str, TC_FROMSTRING, SA_HOR_CENTER); break; @@ -711,23 +777,19 @@ struct MusicWindow : public Window { { switch (widget) { case WID_M_PREV: // skip to prev - if (!_song_is_active) return; - SkipToPrevSong(); - this->SetDirty(); + _music.Prev(); break; case WID_M_NEXT: // skip to next - if (!_song_is_active) return; - SkipToNextSong(); - this->SetDirty(); + _music.Next(); break; case WID_M_STOP: // stop playing - _settings_client.music.playing = false; + _music.Stop(); break; case WID_M_PLAY: // start playing - _settings_client.music.playing = true; + _music.Play(); break; case WID_M_MUSIC_VOL: case WID_M_EFFECT_VOL: { // volume sliders @@ -742,7 +804,7 @@ struct MusicWindow : public Window { if (new_vol < 3) new_vol = 0; if (new_vol != *vol) { *vol = new_vol; - if (widget == WID_M_MUSIC_VOL) MusicVolumeChanged(new_vol); + if (widget == WID_M_MUSIC_VOL) MusicDriver::GetInstance()->SetVolume(new_vol); this->SetDirty(); } @@ -751,12 +813,13 @@ struct MusicWindow : public Window { } case WID_M_SHUFFLE: // toggle shuffle - _settings_client.music.shuffle ^= 1; - this->SetWidgetLoweredState(WID_M_SHUFFLE, _settings_client.music.shuffle); + if (_music.IsShuffle()) { + _music.Unshuffle(); + } else { + _music.Shuffle(); + } + this->SetWidgetLoweredState(WID_M_SHUFFLE, _music.IsShuffle()); this->SetWidgetDirty(WID_M_SHUFFLE); - StopMusic(); - ResetPlaylist(); - this->SetDirty(); break; case WID_M_PROGRAMME: // show track selection @@ -765,10 +828,7 @@ struct MusicWindow : public Window { case WID_M_ALL: case WID_M_OLD: case WID_M_NEW: case WID_M_EZY: case WID_M_CUSTOM1: case WID_M_CUSTOM2: // playlist - SelectPlaylist(widget - WID_M_ALL); - StopMusic(); - ResetPlaylist(); - this->SetDirty(); + _music.ChangePlaylist((MusicSystem::PlaylistChoices)(widget - WID_M_ALL)); break; } } diff --git a/src/openttd.cpp b/src/openttd.cpp index c97fb61638..43c6b1170b 100644 --- a/src/openttd.cpp +++ b/src/openttd.cpp @@ -341,8 +341,7 @@ static void LoadIntroGame(bool load_newgrfs = true) CheckForMissingGlyphs(); - /* Play main theme */ - if (MusicDriver::GetInstance()->IsSongPlaying()) ResetMusic(); + MusicLoop(); // ensure music is correct } void MakeNewgameSettingsLive() From dc8fff2c4da52036f881e6e9bb58a867b78124d0 Mon Sep 17 00:00:00 2001 From: Jonathan G Rennison Date: Sun, 24 Jun 2018 12:00:41 +0100 Subject: [PATCH 24/26] Add: Hover tool-tips to cargo dest flow legend window. This is to improve the usability of the window. The two-letter abbreviations are not always clear, in particular when using a large number of cargoes. The company colours can be ambiguous when there are a large number of companies. --- src/lang/english.txt | 1 + src/linkgraph/linkgraph_gui.cpp | 40 ++++++++++++++++++++++++++++++++- src/linkgraph/linkgraph_gui.h | 4 ++++ 3 files changed, 44 insertions(+), 1 deletion(-) diff --git a/src/lang/english.txt b/src/lang/english.txt index bd9639075d..5344ef7d41 100644 --- a/src/lang/english.txt +++ b/src/lang/english.txt @@ -2288,6 +2288,7 @@ STR_LINKGRAPH_LEGEND_CAPTION :{BLACK}Cargo Fl STR_LINKGRAPH_LEGEND_ALL :{BLACK}All STR_LINKGRAPH_LEGEND_NONE :{BLACK}None STR_LINKGRAPH_LEGEND_SELECT_COMPANIES :{BLACK}Select companies to be displayed +STR_LINKGRAPH_LEGEND_COMPANY_TOOLTIP :{BLACK}{STRING}{}{COMPANY} # Linkgraph legend window and linkgraph legend in smallmap STR_LINKGRAPH_LEGEND_UNUSED :{TINY_FONT}{BLACK}unused diff --git a/src/linkgraph/linkgraph_gui.cpp b/src/linkgraph/linkgraph_gui.cpp index 7cb9e50912..c91fa20588 100644 --- a/src/linkgraph/linkgraph_gui.cpp +++ b/src/linkgraph/linkgraph_gui.cpp @@ -319,7 +319,7 @@ void LinkGraphOverlay::SetCompanyMask(uint32 company_mask) /** Make a number of rows with buttons for each company for the linkgraph legend window. */ NWidgetBase *MakeCompanyButtonRowsLinkGraphGUI(int *biggest_index) { - return MakeCompanyButtonRows(biggest_index, WID_LGL_COMPANY_FIRST, WID_LGL_COMPANY_LAST, 3, STR_LINKGRAPH_LEGEND_SELECT_COMPANIES); + return MakeCompanyButtonRows(biggest_index, WID_LGL_COMPANY_FIRST, WID_LGL_COMPANY_LAST, 3, STR_NULL); } NWidgetBase *MakeSaturationLegendLinkGraphGUI(int *biggest_index) @@ -500,6 +500,44 @@ void LinkGraphLegendWindow::DrawWidget(const Rect &r, int widget) const } } +bool LinkGraphLegendWindow::OnHoverCommon(Point pt, int widget, TooltipCloseCondition close_cond) +{ + if (IsInsideMM(widget, WID_LGL_COMPANY_FIRST, WID_LGL_COMPANY_LAST + 1)) { + if (this->IsWidgetDisabled(widget)) { + GuiShowTooltips(this, STR_LINKGRAPH_LEGEND_SELECT_COMPANIES, 0, NULL, close_cond); + } else { + uint64 params[2]; + CompanyID cid = (CompanyID)(widget - WID_LGL_COMPANY_FIRST); + params[0] = STR_LINKGRAPH_LEGEND_SELECT_COMPANIES; + params[1] = cid; + GuiShowTooltips(this, STR_LINKGRAPH_LEGEND_COMPANY_TOOLTIP, 2, params, close_cond); + } + return true; + } + if (IsInsideMM(widget, WID_LGL_CARGO_FIRST, WID_LGL_CARGO_LAST + 1)) { + if (this->IsWidgetDisabled(widget)) return false; + CargoSpec *cargo = CargoSpec::Get(widget - WID_LGL_CARGO_FIRST); + uint64 params[1]; + params[0] = cargo->name; + GuiShowTooltips(this, STR_BLACK_STRING, 1, params, close_cond); + return true; + } + return false; +} + +void LinkGraphLegendWindow::OnHover(Point pt, int widget) +{ + this->OnHoverCommon(pt, widget, TCC_HOVER); +} + +bool LinkGraphLegendWindow::OnRightClick(Point pt, int widget) +{ + if (_settings_client.gui.hover_delay_ms == 0) { + return this->OnHoverCommon(pt, widget, TCC_RIGHT_CLICK); + } + return false; +} + /** * Update the overlay with the new company selection. */ diff --git a/src/linkgraph/linkgraph_gui.h b/src/linkgraph/linkgraph_gui.h index 12f1f6e736..a933bfc683 100644 --- a/src/linkgraph/linkgraph_gui.h +++ b/src/linkgraph/linkgraph_gui.h @@ -15,6 +15,7 @@ #include "../company_func.h" #include "../station_base.h" #include "../widget_type.h" +#include "../window_gui.h" #include "linkgraph_base.h" #include #include @@ -101,6 +102,8 @@ public: virtual void UpdateWidgetSize(int widget, Dimension *size, const Dimension &padding, Dimension *fill, Dimension *resize); virtual void DrawWidget(const Rect &r, int widget) const; + virtual void OnHover(Point pt, int widget); + virtual bool OnRightClick(Point pt, int widget); virtual void OnClick(Point pt, int widget, int click_count); virtual void OnInvalidateData(int data = 0, bool gui_scope = true); @@ -109,6 +112,7 @@ private: void UpdateOverlayCompanies(); void UpdateOverlayCargoes(); + bool OnHoverCommon(Point pt, int widget, TooltipCloseCondition close_cond); }; #endif /* LINKGRAPH_GUI_H */ From 9fc32126790e6e8a558c95f403cde759a5c2cd8b Mon Sep 17 00:00:00 2001 From: Pavel Stupnikov Date: Sun, 24 Jun 2018 21:55:48 +0300 Subject: [PATCH 25/26] Feature #6397: Keep town growth rate in sync with house count Takes some code and ideas from #6378 patch, but doesn't change anything GS-related. --- src/town_cmd.cpp | 137 +++++++++++++++++++++++++++++++---------------- 1 file changed, 91 insertions(+), 46 deletions(-) diff --git a/src/town_cmd.cpp b/src/town_cmd.cpp index 0575b0a37b..92a3991ba9 100644 --- a/src/town_cmd.cpp +++ b/src/town_cmd.cpp @@ -1575,6 +1575,9 @@ void UpdateTownMaxPass(Town *t) t->supplied[CT_MAIL].old_max = t->cache.population >> 4; } +static void UpdateTownGrowthRate(Town *t); +static void UpdateTownGrowth(Town *t); + /** * Does the actual town creation. * @@ -1654,6 +1657,7 @@ static void DoCreateTown(Town *t, TileIndex tile, uint32 townnameparts, TownSize t->cache.num_houses -= x; UpdateTownRadius(t); + UpdateTownGrowthRate(t); UpdateTownMaxPass(t); UpdateAirportsNoise(); } @@ -2412,6 +2416,7 @@ static bool BuildTownHouse(Town *t, TileIndex tile) MakeTownHouse(tile, t, construction_counter, construction_stage, house, random_bits); UpdateTownRadius(t); + UpdateTownGrowthRate(t); UpdateTownCargoes(t, tile); return true; @@ -2547,8 +2552,6 @@ const CargoSpec *FindFirstCargoWithTownEffect(TownEffect effect) return NULL; } -static void UpdateTownGrowRate(Town *t); - /** * Change the cargo goal of a town. * @param tile Unused. @@ -2577,7 +2580,7 @@ CommandCost CmdTownCargoGoal(TileIndex tile, DoCommandFlag flags, uint32 p1, uin if (flags & DC_EXEC) { t->goal[te] = p2; - UpdateTownGrowRate(t); + UpdateTownGrowth(t); InvalidateWindowData(WC_TOWN_VIEW, index); } @@ -2627,7 +2630,7 @@ CommandCost CmdTownGrowthRate(TileIndex tile, DoCommandFlag flags, uint32 p1, ui if (flags & DC_EXEC) { if (p2 == 0) { - /* Just clear the flag, UpdateTownGrowRate will determine a proper growth rate */ + /* Just clear the flag, UpdateTownGrowth will determine a proper growth rate */ ClrBit(t->flags, TOWN_CUSTOM_GROWTH); } else { uint old_rate = t->growth_rate; @@ -2641,7 +2644,7 @@ CommandCost CmdTownGrowthRate(TileIndex tile, DoCommandFlag flags, uint32 p1, ui t->growth_rate = p2; SetBit(t->flags, TOWN_CUSTOM_GROWTH); } - UpdateTownGrowRate(t); + UpdateTownGrowth(t); InvalidateWindowData(WC_TOWN_VIEW, p1); } @@ -2928,7 +2931,7 @@ static CommandCost TownActionFundBuildings(Town *t, DoCommandFlag flags) t->fund_buildings_months = 3; /* Enable growth (also checking GameScript's opinion) */ - UpdateTownGrowRate(t); + UpdateTownGrowth(t); /* Build a new house, but add a small delay to make sure * that spamming funding doesn't let town grow any faster @@ -3132,8 +3135,87 @@ static void UpdateTownRating(Town *t) SetWindowDirty(WC_TOWN_AUTHORITY, t->index); } -static void UpdateTownGrowRate(Town *t) + +/** + * Updates town grow counter after growth rate change. + * Preserves relative house builting progress whenever it can. + * @param town The town to calculate grow counter for + * @param prev_growth_rate Town growth rate before it changed (one that was used with grow counter to be updated) + */ +static void UpdateTownGrowCounter(Town *t, uint16 prev_growth_rate) { + if (t->growth_rate == TOWN_GROWTH_RATE_NONE) return; + if (prev_growth_rate == TOWN_GROWTH_RATE_NONE) { + t->grow_counter = min(t->growth_rate, t->grow_counter); + return; + } + t->grow_counter = RoundDivSU((uint32)t->grow_counter * (t->growth_rate + 1), prev_growth_rate + 1); +} + +/** + * Calculates amount of active stations in the range of town (HZB_TOWN_EDGE). + * @param town The town to calculate stations for + * @returns Amount of active stations + */ +static int CountActiveStations(Town *t) +{ + int n = 0; + const Station *st; + FOR_ALL_STATIONS(st) { + if (DistanceSquare(st->xy, t->xy) <= t->cache.squared_town_zone_radius[0]) { + if (st->time_since_load <= 20 || st->time_since_unload <= 20) { + n++; + } + } + } + return n; +} + +/** + * Calculates town growth rate in normal conditions (custom growth rate not set). + * If town growth speed is set to None(0) returns the same rate as if it was Normal(2). + * @param town The town to calculate growth rate for + * @returns Calculated growth rate + */ +static uint GetNormalGrowthRate(Town *t) +{ + static const uint16 _grow_count_values[2][6] = { + { 120, 120, 120, 100, 80, 60 }, // Fund new buildings has been activated + { 320, 420, 300, 220, 160, 100 } // Normal values + }; + + int n = CountActiveStations(t); + uint16 m = _grow_count_values[t->fund_buildings_months != 0 ? 0 : 1][min(n, 5)]; + + uint growth_multiplier = _settings_game.economy.town_growth_rate != 0 ? _settings_game.economy.town_growth_rate - 1 : 1; + + m >>= growth_multiplier; + if (t->larger_town) m /= 2; + + return TownTicksToGameTicks(m / (t->cache.num_houses / 50 + 1)); +} + +/** + * Updates town growth rate. + * @param town The town to update growth rate for + */ +static void UpdateTownGrowthRate(Town *t) +{ + if (HasBit(t->flags, TOWN_CUSTOM_GROWTH)) return; + uint old_rate = t->growth_rate; + t->growth_rate = GetNormalGrowthRate(t); + UpdateTownGrowCounter(t, old_rate); + SetWindowDirty(WC_TOWN_VIEW, t->index); +} + +/** + * Updates town growth state (whether it is growing or not). + * @param town The town to update growth for + */ +static void UpdateTownGrowth(Town *t) +{ + UpdateTownGrowthRate(t); + ClrBit(t->flags, TOWN_IS_GROWING); SetWindowDirty(WC_TOWN_VIEW, t->index); @@ -3162,44 +3244,7 @@ static void UpdateTownGrowRate(Town *t) return; } - /** - * Towns are processed every TOWN_GROWTH_TICKS ticks, and this is the - * number of times towns are processed before a new building is built. - */ - static const uint16 _grow_count_values[2][6] = { - { 120, 120, 120, 100, 80, 60 }, // Fund new buildings has been activated - { 320, 420, 300, 220, 160, 100 } // Normal values - }; - - int n = 0; - - const Station *st; - FOR_ALL_STATIONS(st) { - if (DistanceSquare(st->xy, t->xy) <= t->cache.squared_town_zone_radius[0]) { - if (st->time_since_load <= 20 || st->time_since_unload <= 20) { - n++; - } - } - } - - uint16 m; - - if (t->fund_buildings_months != 0) { - m = _grow_count_values[0][min(n, 5)]; - } else { - m = _grow_count_values[1][min(n, 5)]; - if (n == 0 && !Chance16(1, 12)) return; - } - - /* Use the normal growth rate values if new buildings have been funded in - * this town and the growth rate is set to none. */ - uint growth_multiplier = _settings_game.economy.town_growth_rate != 0 ? _settings_game.economy.town_growth_rate - 1 : 1; - - m >>= growth_multiplier; - if (t->larger_town) m /= 2; - - t->growth_rate = TownTicksToGameTicks(m / (t->cache.num_houses / 50 + 1)); - t->grow_counter = min(t->growth_rate, t->grow_counter); + if (t->fund_buildings_months == 0 && CountActiveStations(t) == 0 && !Chance16(1, 12)) return; SetBit(t->flags, TOWN_IS_GROWING); SetWindowDirty(WC_TOWN_VIEW, t->index); @@ -3433,8 +3478,8 @@ void TownsMonthlyLoop() } UpdateTownAmounts(t); + UpdateTownGrowth(t); UpdateTownRating(t); - UpdateTownGrowRate(t); UpdateTownUnwanted(t); UpdateTownCargoes(t); } From 336d6cab68c29c4b3960dd70826082b35a6fff2a Mon Sep 17 00:00:00 2001 From: Niels Martin Hansen Date: Sun, 24 Jun 2018 23:36:55 +0200 Subject: [PATCH 26/26] Fix 6298b96: Playlist window not drawing playlist Copy-paste error in change to remove C++11 usage... --- src/music_gui.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/music_gui.cpp b/src/music_gui.cpp index 1edfbab41f..e7552fe02f 100644 --- a/src/music_gui.cpp +++ b/src/music_gui.cpp @@ -539,7 +539,7 @@ struct MusicTrackSelectionWindow : public Window { GfxFillRect(r.left + 1, r.top + 1, r.right - 1, r.bottom - 1, PC_BLACK); int y = r.top + WD_FRAMERECT_TOP; - for (MusicSystem::Playlist::const_iterator song = _music.music_set.begin(); song != _music.music_set.end(); ++song) { + for (MusicSystem::Playlist::const_iterator song = _music.active_playlist.begin(); song != _music.active_playlist.end(); ++song) { SetDParam(0, song->tracknr); SetDParam(1, 2); SetDParamStr(2, song->songname);