1
0
Fork 0

Compare commits

...

9 Commits

Author SHA1 Message Date
Su 0cf5ebc4ae
Merge a48f491f09 into 6b6caa6fa8 2025-07-16 04:48:57 +00:00
translators 6b6caa6fa8 Update: Translations from eints
chinese (traditional): 1 change by KogentaSan
greek: 1 change by gh658804
hungarian: 1 change by vargaviktor
russian: 1 change by Ln-Wolf
finnish: 1 change by hpiirai
catalan: 49 changes by J0anJosep
portuguese: 2 changes by jcteotonio
portuguese (brazilian): 1 change by pasantoro
polish: 1 change by pAter-exe
2025-07-16 04:46:46 +00:00
Susan a48f491f09 Cleanup: CodeQL fixes 2025-07-11 07:54:29 +01:00
Susan 3ef5783bf9 Codechange: Reorder returns 2025-07-11 07:03:03 +01:00
Susan 7f6b5d3103 Codechange: use BridgePiecePillarFlags in more places 2025-07-11 05:31:49 +01:00
Susan 5a1a8adb3f Codechange: Less indentation 2025-07-11 02:29:58 +01:00
Susan 1000875f88 Codechange: use BridgePiecePillarFlags instead of uint8_t 2025-07-10 03:06:34 +01:00
Susan 1f5ae37e2c Codechange: use BridgeSpecCtrlFlags instead of uint8_t 2025-07-09 23:48:28 +01:00
Susan 3be979cc05 Feature: Stations under bridges 2025-07-09 06:14:43 +01:00
18 changed files with 599 additions and 162 deletions

View File

@ -37,6 +37,30 @@ constexpr uint SPRITES_PER_BRIDGE_PIECE = 32; ///< Number of sprites there are p
typedef uint BridgeType; ///< Bridge spec number.
/**
* Actions that can be performed when the vehicle enters the depot.
*/
enum class BridgePiecePillarFlag : uint8_t {
BPPF_CORNER_W = 1 << 0,
BPPF_CORNER_S = 1 << 1,
BPPF_CORNER_E = 1 << 2,
BPPF_CORNER_N = 1 << 3,
BPPF_ALL_CORNERS = 0xF,
BPPF_EDGE_NE = 1 << 4,
BPPF_EDGE_SE = 1 << 5,
BPPF_EDGE_SW = 1 << 6,
BPPF_EDGE_NW = 1 << 7,
};
using BridgePiecePillarFlags = EnumBitSet<BridgePiecePillarFlag, uint8_t>;
enum class BridgeSpecCtrlFlag : uint8_t{
BSCF_CUSTOM_PILLAR_FLAGS,
BSCF_INVALID_PILLAR_FLAGS,
BSCF_NOT_AVAILABLE_TOWN,
BSCF_NOT_AVAILABLE_AI_GS,
};
using BridgeSpecCtrlFlags = EnumBitSet<BridgeSpecCtrlFlag, uint8_t>;
/**
* Struct containing information about a single bridge type
*/
@ -52,6 +76,8 @@ struct BridgeSpec {
StringID transport_name[2]; ///< description of the bridge, when built for road or rail
std::vector<std::vector<PalSpriteID>> sprite_table; ///< table of sprites for drawing the bridge
uint8_t flags; ///< bit 0 set: disable drawing of far pillars.
BridgeSpecCtrlFlags ctrl_flags; ///< control flags
std::array<BridgePiecePillarFlags, 12> pillar_flags; ///< bridge pillar flags: 6 x pairs of x and y flags
};
extern BridgeSpec _bridge[MAX_BRIDGES];
@ -75,6 +101,15 @@ void DrawBridgeMiddle(const TileInfo *ti);
CommandCost CheckBridgeAvailability(BridgeType bridge_type, uint bridge_len, DoCommandFlags flags = {});
int CalcBridgeLenCostFactor(int x);
BridgePiecePillarFlags GetBridgeTilePillarFlags(TileIndex tile, TileIndex northern_bridge_end, TileIndex southern_bridge_end, BridgeType bridge_type, TransportType bridge_transport_type);
struct BridgePieceDebugInfo {
BridgePieces piece;
BridgePiecePillarFlags pillar_flags;
uint pillar_index;
};
BridgePieceDebugInfo GetBridgePieceDebugInfo(TileIndex tile);
void ResetBridges();
#endif /* BRIDGE_H */

View File

@ -382,6 +382,8 @@ void ShowBuildBridgeWindow(TileIndex start, TileIndex end, TransportType transpo
StringID errmsg = INVALID_STRING_ID;
CommandCost ret = Command<CMD_BUILD_BRIDGE>::Do(CommandFlagsToDCFlags(GetCommandFlags<CMD_BUILD_BRIDGE>()) | DoCommandFlag::QueryCost, end, start, transport_type, 0, road_rail_type);
const bool query_per_bridge_type = ret.Failed() && (ret.GetErrorMessage() == STR_ERROR_BRIDGE_TOO_LOW_FOR_STATION || ret.GetErrorMessage() == STR_ERROR_BRIDGE_PILLARS_OBSTRUCT_STATION);
GUIBridgeList bl;
if (ret.Failed()) {
errmsg = ret.GetErrorMessage();
@ -415,11 +417,13 @@ void ShowBuildBridgeWindow(TileIndex start, TileIndex end, TransportType transpo
}
bool any_available = false;
StringID type_errmsg = INVALID_STRING_ID;
CommandCost type_check;
/* loop for all bridgetypes */
for (BridgeType brd_type = 0; brd_type != MAX_BRIDGES; brd_type++) {
type_check = CheckBridgeAvailability(brd_type, bridge_len);
if (type_check.Succeeded()) {
if (query_per_bridge_type && Command<CMD_BUILD_BRIDGE>::Do(CommandFlagsToDCFlags(GetCommandFlags<CMD_BUILD_BRIDGE>()) | DoCommandFlag::QueryCost, end, start, transport_type, brd_type, road_rail_type).Failed()) continue;
/* bridge is accepted, add to list */
BuildBridgeData &item = bl.emplace_back();
item.index = brd_type;
@ -428,10 +432,12 @@ void ShowBuildBridgeWindow(TileIndex start, TileIndex end, TransportType transpo
* bridge itself (not computed with DoCommandFlag::QueryCost) */
item.cost = ret.GetCost() + (((int64_t)tot_bridgedata_len * _price[PR_BUILD_BRIDGE] * item.spec->price) >> 8) + infra_cost;
any_available = true;
} else if (type_check.GetErrorMessage() != INVALID_STRING_ID && !query_per_bridge_type) {
type_errmsg = type_check.GetErrorMessage();
}
}
/* give error cause if no bridges available here*/
if (!any_available)
if (!any_available && type_errmsg != INVALID_STRING_ID) errmsg = type_errmsg;
{
errmsg = type_check.GetErrorMessage();
}

View File

@ -5002,6 +5002,7 @@ STR_ERROR_FLAT_LAND_REQUIRED :{WHITE}O terren
STR_ERROR_LAND_SLOPED_IN_WRONG_DIRECTION :{WHITE}Terreno inclinado na direção errada
STR_ERROR_CAN_T_DO_THIS :{WHITE}Não é possível fazer isto...
STR_ERROR_BUILDING_MUST_BE_DEMOLISHED :{WHITE}A construção precisa ser demolida primeiro
STR_ERROR_BUILDING_IS_PROTECTED :{WHITE}... construção é protegida
STR_ERROR_CAN_T_CLEAR_THIS_AREA :{WHITE}Não é possível limpar esta área...
STR_ERROR_SITE_UNSUITABLE :{WHITE}... local não adequado
STR_ERROR_ALREADY_BUILT :{WHITE}... já construído

View File

@ -268,6 +268,7 @@ STR_UNITS_YEARS :{NUM}{NBSP}any{
STR_UNITS_PERIODS :{NUM}{NBSP}període{P "" s}
STR_LIST_SEPARATOR :,{SPACE}
STR_TRUNCATION_ELLIPSIS :...
# Common window strings
STR_LIST_FILTER_TITLE :{BLACK}Filtre:
@ -286,7 +287,7 @@ STR_TOOLTIP_CLOSE_WINDOW :{BLACK}Tanca la
STR_TOOLTIP_WINDOW_TITLE_DRAG_THIS :{BLACK}Títol de la finestra: arrossegueu el títol per desplaçar la finestra.
STR_TOOLTIP_SHADE :{BLACK}Ombra de la finestra: mostra només la barra de títol.
STR_TOOLTIP_DEBUG :{BLACK}Mostra la informació de depuració NewGRF
STR_TOOLTIP_DEFSIZE :{BLACK}Redimensiona la finestra a la mida predeterminada. Ctrl+Clic desa la mida actual com a predeterminada.
STR_TOOLTIP_DEFSIZE :{BLACK}Redimensiona la finestra a la mida predeterminada. Amb Ctrl+Clic, es desa la mida actual com a predeterminada. Amb Ctrl+doble clic, es restableix als valors per defecte.
STR_TOOLTIP_STICKY :{BLACK}Marca aquesta finestra com a no eliminable per la tecla «Tanca totes les finestres». Ctrl+Clic per desar també l'estat predeterminat.
STR_TOOLTIP_RESIZE :{BLACK}Clica i arrossega per redimensionar aquesta finestra
STR_TOOLTIP_TOGGLE_LARGE_SMALL_WINDOW :{BLACK}Commuta entre la mida gran i petita de la finestra
@ -1108,7 +1109,7 @@ STR_GAME_OPTIONS_BASE_MUSIC_DESCRIPTION_TOOLTIP :Informació add
STR_GAME_OPTIONS_ONLINE_CONTENT :Descarrega contingut
STR_GAME_OPTIONS_ONLINE_CONTENT_TOOLTIP :Comprova si hi ha continguts nous o actualitzats per a descarregar.
STR_GAME_OPTIONS_SOCIAL_PLUGINS_NONE :{LTBLUE}(no s'han instal·lat complements per a interactuar amb plataformes socials)
STR_GAME_OPTIONS_SOCIAL_PLUGINS_NONE :(no s'han instal·lat complements per a interactuar amb plataformes socials)
STR_GAME_OPTIONS_SOCIAL_PLUGIN_TITLE :{STRING} ({STRING})
STR_GAME_OPTIONS_SOCIAL_PLUGIN_PLATFORM :Plataforma:
@ -1226,7 +1227,7 @@ STR_CITY_APPROVAL_PERMISSIVE :Permissiva (les
STR_WARNING_NO_SUITABLE_AI :{WHITE}No hi ha cap IA disponible...{}Podeu descarregar-ne a través del «Contingut en línia».
# Settings tree in the Game Options window
STR_CONFIG_SETTING_FILTER_TITLE :{G=Masculin}{BLACK}Filtre:
STR_CONFIG_SETTING_FILTER_TITLE :{G=Masculin}Filtre:
STR_CONFIG_SETTING_EXPAND_ALL :Desplega-ho tot
STR_CONFIG_SETTING_COLLAPSE_ALL :Plega-ho tot
STR_CONFIG_SETTING_RESET_ALL :Restableix tots els valors
@ -1302,6 +1303,9 @@ STR_CONFIG_SETTING_INTEREST_RATE_HELPTEXT :Tipus d'interè
STR_CONFIG_SETTING_RUNNING_COSTS :Costos d'utilització: {STRING}
STR_CONFIG_SETTING_RUNNING_COSTS_HELPTEXT :Fixa el nivell de manteniment i els costos d'utilització dels vehicles i infraestructures
###length 3
STR_CONFIG_SETTING_RUNNING_COSTS_LOW :Baix
STR_CONFIG_SETTING_RUNNING_COSTS_MEDIUM :Mitjà
STR_CONFIG_SETTING_RUNNING_COSTS_HIGH :Alt
STR_CONFIG_SETTING_CONSTRUCTION_SPEED :Ritme de construcció: {STRING}
STR_CONFIG_SETTING_CONSTRUCTION_SPEED_HELPTEXT :Limita la quantitat d'accions de construcció per part de les IA
@ -1324,6 +1328,9 @@ STR_CONFIG_SETTING_SUBSIDY_DURATION_DISABLED :Sense subsidis
STR_CONFIG_SETTING_CONSTRUCTION_COSTS :Costos de construcció: {STRING}
STR_CONFIG_SETTING_CONSTRUCTION_COSTS_HELPTEXT :Fixa el nivell de construcció i els preus de compra
###length 3
STR_CONFIG_SETTING_CONSTRUCTION_COSTS_LOW :Baix
STR_CONFIG_SETTING_CONSTRUCTION_COSTS_MEDIUM :Mitjà
STR_CONFIG_SETTING_CONSTRUCTION_COSTS_HIGH :Alt
STR_CONFIG_SETTING_RECESSIONS :Recessions: {STRING}
STR_CONFIG_SETTING_RECESSIONS_HELPTEXT :Si està activat, poden haver recessions periòdicament. Durant una recessió, tota la producció és significativament més baixa, tornant al nivell previ quan s'acabi el període de recessió.
@ -2079,9 +2086,9 @@ STR_CONFIG_SETTING_DISTRIBUTION_ARMOURED_HELPTEXT :La classe de c
STR_CONFIG_SETTING_DISTRIBUTION_DEFAULT :Mode de distribució per altres classes de càrrega: {STRING}
STR_CONFIG_SETTING_DISTRIBUTION_DEFAULT_HELPTEXT :"Asimètric" vol dir que quantitats arbitràries de càrrega poden ser enviades en qualsevol sentit. "Manual" significa que no s'aplicarà cap distribució automàtica a aquestes càrregues.
###length 3
STR_CONFIG_SETTING_DISTRIBUTION_MANUAL :manual
STR_CONFIG_SETTING_DISTRIBUTION_ASYMMETRIC :asimètric
STR_CONFIG_SETTING_DISTRIBUTION_SYMMETRIC :simètric
STR_CONFIG_SETTING_DISTRIBUTION_MANUAL :Manual
STR_CONFIG_SETTING_DISTRIBUTION_ASYMMETRIC :Asimètric
STR_CONFIG_SETTING_DISTRIBUTION_SYMMETRIC :Simètric
STR_CONFIG_SETTING_LINKGRAPH_ACCURACY :Precisió de la distribució: {STRING}
STR_CONFIG_SETTING_LINKGRAPH_ACCURACY_HELPTEXT :Com més alt el valor indicat, més temps de processador requerirà el càlcul del graf de distribució. Si requereix massa temps podeu notar ralentització. Si indiqueu un valor baix, però, la distribució serà poc acurada, i us podeu trobar que la càrrega no és enviada als llocs que espereu.
@ -2331,16 +2338,19 @@ STR_FACE_SIMPLE_TOOLTIP :{BLACK}Selecci
STR_FACE_LOAD :{BLACK}Carrega
STR_FACE_LOAD_TOOLTIP :{BLACK}Carrega la cara preferida
STR_FACE_LOAD_DONE :{WHITE}S'ha carregat la cara personalitzada des de l'arxiu de configuració de l'OpenTTD.
STR_FACE_FACECODE :{BLACK}Número de la cara
STR_FACE_FACECODE_TOOLTIP :{BLACK}Veure i/o assigna el número de la cara del president
STR_FACE_FACECODE_CAPTION :{WHITE}Veure i/o assigna el número de la cara del president
STR_FACE_FACECODE_SET :{WHITE}El número de la nova cara ha estat assignat
STR_FACE_FACECODE_ERR :{WHITE}No s'ha pogut assignar el número de cara del president - ha de ser un nombre entre 0 i 4,294,967,295!
STR_FACE_FACECODE :{BLACK}Codi de la cara
STR_FACE_FACECODE_TOOLTIP :{BLACK}Veure i/o assigna el codi de la cara del president
STR_FACE_FACECODE_CAPTION :{WHITE}Veure i/o assigna el codi de la cara del president
STR_FACE_FACECODE_SET :{WHITE}S'ha assignat el codi de la nova cara del president.
STR_FACE_FACECODE_ERR :{WHITE}No s'ha pogut assignar el codi de cara del president - ha de ser una etiqueta i nombre vàlids.
STR_FACE_SAVE :{BLACK}Desa
STR_FACE_SAVE_TOOLTIP :{BLACK}Desa la cara preferida
STR_FACE_SAVE_DONE :{WHITE}Es desarà aquesta cara personalitzada a l'arxiu de configuració de l'OpenTTD.
STR_FACE_SETTING_TOGGLE :{STRING} {ORANGE}{STRING}
STR_FACE_SETTING_NUMERIC :{STRING} {ORANGE}{NUM} / {NUM}
STR_FACE_YES :Sí
STR_FACE_NO :No
STR_FACE_STYLE :Estil:
STR_FACE_HAIR :Cabell:
STR_FACE_EYEBROWS :Celles:
STR_FACE_EYECOLOUR :Color dels ulls:
@ -2813,6 +2823,10 @@ STR_PICKER_MODE_USED_TOOLTIP :Commuta entre m
STR_PICKER_MODE_SAVED :Desats
STR_PICKER_MODE_SAVED_TOOLTIP :Commuta entre mostrar tots o bé només els elements desats.
STR_PICKER_PREVIEW_SHRINK :
STR_PICKER_PREVIEW_SHRINK_TOOLTIP :Redueix l'alçària de les vistes prèvies. Amb Ctrl+clic, es redueix fins al mínim.
STR_PICKER_PREVIEW_EXPAND :+
STR_PICKER_PREVIEW_EXPAND_TOOLTIP :Augmenta l'alçària de les vistes prèvies. Amb Ctrl+clic, s'augmenta fins al màxim.
STR_PICKER_STATION_CLASS_TOOLTIP :Trieu quina classe d'estació voleu veure.
STR_PICKER_STATION_TYPE_TOOLTIP :Trieu quin tipus d'estació voleu construir. Amb Ctrl+clic, s'afegeix o es trau l'element de la llista de desats.
@ -3053,6 +3067,11 @@ STR_FOUND_TOWN_INITIAL_SIZE_TOOLTIP :{BLACK}Seleccio
STR_FOUND_TOWN_CITY :{BLACK}Ciutat
STR_FOUND_TOWN_CITY_TOOLTIP :{BLACK}Les ciutats creixen més ràpid que els pobles{}Depenent de la configuració, són més grans quan es funden
STR_FOUND_TOWN_EXPAND_MODE :{YELLOW}Expansió de la població:
STR_FOUND_TOWN_EXPAND_BUILDINGS :Edificis
STR_FOUND_TOWN_EXPAND_BUILDINGS_TOOLTIP :Augmenta el nombre d'edificis de les poblacions.
STR_FOUND_TOWN_EXPAND_ROADS :Carreteres
STR_FOUND_TOWN_EXPAND_ROADS_TOOLTIP :Augmenta el nombre de carreteres de la població.
STR_FOUND_TOWN_ROAD_LAYOUT :{YELLOW}Disposició de les carreteres de la població:
STR_FOUND_TOWN_SELECT_LAYOUT_TOOLTIP :{BLACK}Selecciona la disposició de les carreteres utilitzades per a aquesta població
@ -3677,6 +3696,10 @@ STR_TOWN_VIEW_RENAME_TOOLTIP :{BLACK}Canvia e
STR_TOWN_VIEW_EXPAND_BUTTON :{BLACK}Eixampla
STR_TOWN_VIEW_EXPAND_TOOLTIP :{BLACK}Incrementa la mida de la població
STR_TOWN_VIEW_EXPAND_BUILDINGS_BUTTON :{BLACK}Expandeix els edificis
STR_TOWN_VIEW_EXPAND_BUILDINGS_TOOLTIP :{BLACK}Augmenta el nombre d'edificis de la població.
STR_TOWN_VIEW_EXPAND_ROADS_BUTTON :{BLACK}Expandeix les carreteres
STR_TOWN_VIEW_EXPAND_ROADS_TOOLTIP :{BLACK}Augmenta el nombre de carreteres de la població.
STR_TOWN_VIEW_DELETE_BUTTON :{BLACK}Esborra
STR_TOWN_VIEW_DELETE_TOOLTIP :{BLACK}Esborra totalment aquesta població
@ -4415,10 +4438,10 @@ STR_VEHICLE_VIEW_SHIP_ORDERS_TOOLTIP :{BLACK}Mostra l
STR_VEHICLE_VIEW_AIRCRAFT_ORDERS_TOOLTIP :{BLACK}Mostra les ordres de l'avió. Ctrl+Clic per mostrar l'horari de l'avió
###length VEHICLE_TYPES
STR_VEHICLE_VIEW_TRAIN_SHOW_DETAILS_TOOLTIP :{BLACK}Mostra els detalls del tren
STR_VEHICLE_VIEW_ROAD_VEHICLE_SHOW_DETAILS_TOOLTIP :{BLACK}Mostra els detalls del vehicle
STR_VEHICLE_VIEW_SHIP_SHOW_DETAILS_TOOLTIP :{BLACK}Mostra els detalls del vaixell
STR_VEHICLE_VIEW_AIRCRAFT_SHOW_DETAILS_TOOLTIP :{BLACK}Mostra els detalls de l'avió
STR_VEHICLE_VIEW_TRAIN_SHOW_DETAILS_TOOLTIP :{BLACK}Mostra els detalls del tren. Amb Ctrl+clic, es mostra el grup del tren.
STR_VEHICLE_VIEW_ROAD_VEHICLE_SHOW_DETAILS_TOOLTIP :{BLACK}Mostra els detalls del vehicle. Amb Ctrl+clic, es mostra el grup del vehicle.
STR_VEHICLE_VIEW_SHIP_SHOW_DETAILS_TOOLTIP :{BLACK}Mostra els detalls del vaixell. Amb Ctrl+clic, es mostra el grup del vaixell.
STR_VEHICLE_VIEW_AIRCRAFT_SHOW_DETAILS_TOOLTIP :{BLACK}Mostra els detalls de l'aeronau. Amb Ctrl+clic, es mostra el grup de l'aeronau.
###length VEHICLE_TYPES
STR_VEHICLE_VIEW_TRAIN_STATUS_START_STOP_TOOLTIP :{BLACK}Acció actual del tren - Feu clic per parar-lo o engegar-lo.
@ -4742,14 +4765,15 @@ STR_TIMETABLE_TOOLTIP :{BLACK}Horari -
STR_TIMETABLE_NO_TRAVEL :Sense viatge
STR_TIMETABLE_NOT_TIMETABLEABLE :Viatge (automàtic; programat per la següent ordre manual)
STR_TIMETABLE_TRAVEL_NOT_TIMETABLED :Viatge (fora d'horari)
STR_TIMETABLE_TRAVEL_NOT_TIMETABLED_SPEED :Viatja (sense horari) com a molt a {VELOCITY}
STR_TIMETABLE_TRAVEL_FOR :Viatge a {STRING}
STR_TIMETABLE_TRAVEL_FOR_SPEED :Viatja durant {STRING} com a molt a {VELOCITY}
STR_TIMETABLE_TRAVEL_FOR_ESTIMATED :Viatja (durant {STRING}, sense horari)
STR_TIMETABLE_TRAVEL_FOR_SPEED_ESTIMATED :Viatja (durant {STRING}, sense horari) com a molt a {VELOCITY}
STR_TIMETABLE_STAY_FOR_ESTIMATED :{SPACE}(quedar-s'hi durant {STRING}, sense horari)
STR_TIMETABLE_AND_TRAVEL_FOR_ESTIMATED :{SPACE}(viatja durant {STRING}, sense horari)
STR_TIMETABLE_STAY_FOR :i estigues {STRING}
STR_TIMETABLE_AND_TRAVEL_FOR :i viatge per {STRING}
STR_TIMETABLE_STAY_FOR :{SPACE}i estigues {STRING}
STR_TIMETABLE_AND_TRAVEL_FOR :{SPACE}i viatja durant {STRING}
STR_TIMETABLE_APPROX_TIME :{BLACK}L'horari trigara aproximadament {STRING} a completar-se.
STR_TIMETABLE_TOTAL_TIME :{BLACK}L'horari tardarà {STRING} a complir-se
@ -5897,3 +5921,11 @@ STR_SHIP :{BLACK}{SHIP}
STR_TOOLBAR_RAILTYPE_VELOCITY :{STRING} ({VELOCITY})
STR_BADGE_NAME_LIST :{STRING}: {GOLD}{STRING}
STR_BADGE_CONFIG_MENU_TOOLTIP :Obre la configuració d'insígnies
STR_BADGE_CONFIG_RESET :Restableix
STR_BADGE_CONFIG_ICONS :{WHITE}Icones d'insígnies
STR_BADGE_CONFIG_FILTERS :{WHITE}Filtres d'insígnies
STR_BADGE_CONFIG_PREVIEW :Imatge de previsualització
STR_BADGE_CONFIG_NAME :Nom
STR_BADGE_FILTER_ANY_LABEL :Qualsevol {STRING}
STR_BADGE_FILTER_IS_LABEL :{STRING} és {STRING}

View File

@ -5242,6 +5242,8 @@ STR_ERROR_START_AND_END_MUST_BE_IN :{WHITE}Start an
STR_ERROR_ENDS_OF_BRIDGE_MUST_BOTH :{WHITE}... ends of bridge must both be on land
STR_ERROR_BRIDGE_TOO_LONG :{WHITE}... bridge too long
STR_ERROR_BRIDGE_THROUGH_MAP_BORDER :{WHITE}Bridge would end out of the map
STR_ERROR_BRIDGE_TOO_LOW_FOR_STATION :{WHITE}Bridge is too low for station
STR_ERROR_BRIDGE_PILLARS_OBSTRUCT_STATION :{WHITE}Bridge pillars obstruct station
# Tunnel related errors
STR_ERROR_CAN_T_BUILD_TUNNEL_HERE :{WHITE}Can't build tunnel here...

View File

@ -5001,6 +5001,7 @@ STR_ERROR_FLAT_LAND_REQUIRED :{WHITE}Tarvitaa
STR_ERROR_LAND_SLOPED_IN_WRONG_DIRECTION :{WHITE}Maa viettää väärään suuntaan.
STR_ERROR_CAN_T_DO_THIS :{WHITE}Ei onnistu...
STR_ERROR_BUILDING_MUST_BE_DEMOLISHED :{WHITE}Rakennus täytyy purkaa ensin.
STR_ERROR_BUILDING_IS_PROTECTED :{WHITE}… rakennus on suojattu
STR_ERROR_CAN_T_CLEAR_THIS_AREA :{WHITE}Aluetta ei voi tyhjentää...
STR_ERROR_SITE_UNSUITABLE :{WHITE}... maasto on sopimaton
STR_ERROR_ALREADY_BUILT :{WHITE}... se on jo rakennettu

View File

@ -5102,6 +5102,7 @@ STR_ERROR_FLAT_LAND_REQUIRED :{WHITE}Απαι
STR_ERROR_LAND_SLOPED_IN_WRONG_DIRECTION :{WHITE}Το έδαφος έχει λάθος κλίση
STR_ERROR_CAN_T_DO_THIS :{WHITE}Αυτό δεν γίνεται...
STR_ERROR_BUILDING_MUST_BE_DEMOLISHED :{WHITE}Το κτίριο πρέπει πρώτα να κατεδαφιστεί
STR_ERROR_BUILDING_IS_PROTECTED :{WHITE}... το κτίριο προστατεύεται
STR_ERROR_CAN_T_CLEAR_THIS_AREA :{WHITE}Είναι αδύνατο να καθαριστεί αυτή η περιοχή...
STR_ERROR_SITE_UNSUITABLE :{WHITE}... ακατάλληλη περιοχή
STR_ERROR_ALREADY_BUILT :{WHITE}... ήδη κατασκευασμένο

View File

@ -330,6 +330,7 @@ STR_UNITS_YEARS :{NUM}{NBSP}év
STR_UNITS_PERIODS :{NUM}{NBSP}időszak
STR_LIST_SEPARATOR :,{SPACE}
STR_TRUNCATION_ELLIPSIS :...
# Common window strings
STR_LIST_FILTER_TITLE :{BLACK}Szűrő kifejezés:

View File

@ -5387,6 +5387,7 @@ STR_ERROR_FLAT_LAND_REQUIRED :{WHITE}Wymagany
STR_ERROR_LAND_SLOPED_IN_WRONG_DIRECTION :{WHITE}Teren pochylony w złym kierunku
STR_ERROR_CAN_T_DO_THIS :{WHITE}Nie można tego zrobić...
STR_ERROR_BUILDING_MUST_BE_DEMOLISHED :{WHITE}Należy najpierw zburzyć budynek
STR_ERROR_BUILDING_IS_PROTECTED :{WHITE}... budynek jest chroniony
STR_ERROR_CAN_T_CLEAR_THIS_AREA :{WHITE}Nie można wyczyścić terenu...
STR_ERROR_SITE_UNSUITABLE :{WHITE}... nieodpowiednie miejsce
STR_ERROR_ALREADY_BUILT :{WHITE}... już zbudowano

View File

@ -647,7 +647,7 @@ STR_GRAPH_KEY_COMPANY_SELECTION_TOOLTIP :{BLACK}Alternar
# Company league window
STR_COMPANY_LEAGUE_TABLE_CAPTION :{WHITE}Classificação de Empresas
STR_COMPANY_LEAGUE_COMPANY_NAME :{ORANGE}{COMPANY} {BLACK}{COMPANY_NUM} '{STRING}'
STR_COMPANY_LEAGUE_COMPANY_NAME :{ORANGE}{COMPANY} {BLACK}{COMPANY_NUM} "{STRING}"
STR_COMPANY_LEAGUE_COMPANY_RANK :{YELLOW}#{NUM}
STR_COMPANY_LEAGUE_PERFORMANCE_TITLE_ENGINEER :Engenheiro
STR_COMPANY_LEAGUE_PERFORMANCE_TITLE_TRAFFIC_MANAGER :Gestor de Tráfego
@ -5002,6 +5002,7 @@ STR_ERROR_FLAT_LAND_REQUIRED :{WHITE}É neces
STR_ERROR_LAND_SLOPED_IN_WRONG_DIRECTION :{WHITE}Terreno inclinado na direcção incorrecta
STR_ERROR_CAN_T_DO_THIS :{WHITE}Não é possível fazer isto...
STR_ERROR_BUILDING_MUST_BE_DEMOLISHED :{WHITE}O edifício deve ser demolido primeiro
STR_ERROR_BUILDING_IS_PROTECTED :{WHITE}... o edifício está protegido
STR_ERROR_CAN_T_CLEAR_THIS_AREA :{WHITE}Não é possível limpar esta área...
STR_ERROR_SITE_UNSUITABLE :{WHITE}... sítio inadequado
STR_ERROR_ALREADY_BUILT :{WHITE}... já está construído

View File

@ -5188,6 +5188,7 @@ STR_ERROR_FLAT_LAND_REQUIRED :{WHITE}Необ
STR_ERROR_LAND_SLOPED_IN_WRONG_DIRECTION :{WHITE}Неверный уклон земли
STR_ERROR_CAN_T_DO_THIS :{WHITE}Это невозможно...
STR_ERROR_BUILDING_MUST_BE_DEMOLISHED :{WHITE}Сначала снесите здания
STR_ERROR_BUILDING_IS_PROTECTED :{WHITE}... это здание защищено от изменений
STR_ERROR_CAN_T_CLEAR_THIS_AREA :{WHITE}Невозможно расчистить данный участок...
STR_ERROR_SITE_UNSUITABLE :{WHITE}... неподходящее место
STR_ERROR_ALREADY_BUILT :{WHITE}... уже построено

View File

@ -5001,6 +5001,7 @@ STR_ERROR_FLAT_LAND_REQUIRED :{WHITE}需要
STR_ERROR_LAND_SLOPED_IN_WRONG_DIRECTION :{WHITE}地面斜坡方向不對
STR_ERROR_CAN_T_DO_THIS :{WHITE}不能執行以下動作...
STR_ERROR_BUILDING_MUST_BE_DEMOLISHED :{WHITE}必須先摧毀建築物
STR_ERROR_BUILDING_IS_PROTECTED :{WHITE}……建築物受到保護
STR_ERROR_CAN_T_CLEAR_THIS_AREA :{WHITE}不能清除這個地段...
STR_ERROR_SITE_UNSUITABLE :{WHITE}... 地點不適合
STR_ERROR_ALREADY_BUILT :{WHITE}……經已建成

View File

@ -12,6 +12,7 @@
#ifndef NEWGRF_ROADSTATION_H
#define NEWGRF_ROADSTATION_H
#include "bridge.h"
#include "newgrf_animation_type.h"
#include "newgrf_spritegroup.h"
#include "newgrf_badge_type.h"
@ -71,6 +72,12 @@ enum class RoadStopSpecFlag : uint8_t {
};
using RoadStopSpecFlags = EnumBitSet<RoadStopSpecFlag, uint8_t>;
enum class RoadStopSpecIntlFlag : uint8_t {
BridgeHeightsSet, ///< bridge_height[6] is set.
BridgeDisallowedPillarsSet, ///< bridge_disallowed_pillars[6] is set.
};
using RoadStopSpecIntlFlags = EnumBitSet<RoadStopSpecIntlFlag, uint8_t>;
enum RoadStopView : uint8_t {
RSV_BAY_NE = 0, ///< Bay road stop, facing Northeast
RSV_BAY_SE = 1, ///< Bay road stop, facing Southeast
@ -133,17 +140,20 @@ struct RoadStopSpec : NewGRFSpecBase<RoadStopClassID> {
RoadStopDrawModes draw_mode = {RoadStopDrawMode::Road, RoadStopDrawMode::Overlay};
RoadStopCallbackMasks callback_mask{};
RoadStopSpecFlags flags{};
RoadStopSpecIntlFlags internal_flags{};
CargoTypes cargo_triggers = 0; ///< Bitmask of cargo types which cause trigger re-randomizing
AnimationInfo<StationAnimationTriggers> animation;
uint8_t bridge_height[6]; ///< Minimum height for a bridge above, 0 for none
uint8_t bridge_disallowed_pillars[6]; ///< Disallowed pillar flags for a bridge above
BridgePiecePillarFlags bridge_disallowed_pillars[6]; ///< Disallowed pillar flags for a bridge above
uint8_t build_cost_multiplier = 16; ///< Build cost multiplier per tile.
uint8_t clear_cost_multiplier = 16; ///< Clear cost multiplier per tile.
uint8_t height; ///< The height of this structure, in heightlevels; max MAX_TILE_HEIGHT.
std::vector<BadgeID> badges;
/**

View File

@ -10,6 +10,7 @@
#ifndef NEWGRF_STATION_H
#define NEWGRF_STATION_H
#include "bridge.h"
#include "core/enum_type.hpp"
#include "newgrf_animation_type.h"
#include "newgrf_badge_type.h"
@ -118,6 +119,12 @@ enum class StationSpecFlag : uint8_t {
};
using StationSpecFlags = EnumBitSet<StationSpecFlag, uint8_t>;
enum class StationSpecIntlFlag : uint8_t {
BridgeHeightsSet, ///< bridge_height[8] is set.
BridgeDisallowedPillarsSet, ///< bridge_disallowed_pillars[8] is set.
};
using StationSpecIntlFlags = EnumBitSet<StationSpecIntlFlag, uint8_t>;
/** Station specification. */
struct StationSpec : NewGRFSpecBase<StationClassID> {
StationSpec() : name(0),
@ -162,6 +169,12 @@ struct StationSpec : NewGRFSpecBase<StationClassID> {
StationSpecFlags flags; ///< Bitmask of flags, bit 0: use different sprite set; bit 1: divide cargo about by station size
struct BridgeAboveFlags {
uint8_t height = UINT8_MAX; ///< Minimum height for a bridge above, 0 for none
BridgePiecePillarFlags disallowed_pillars = {}; ///< Disallowed pillar flags for a bridge above
};
std::vector<BridgeAboveFlags> bridge_above_flags; ///< List of bridge above flags.
enum class TileFlag : uint8_t {
Pylons = 0, ///< Tile should contain catenary pylons.
NoWires = 1, ///< Tile should NOT contain catenary wires.
@ -172,10 +185,18 @@ struct StationSpec : NewGRFSpecBase<StationClassID> {
AnimationInfo<StationAnimationTriggers> animation;
StationSpecIntlFlags internal_flags{}; ///< Bitmask of internal spec flags
/** Custom platform layouts, keyed by platform and length combined. */
std::unordered_map<uint16_t, std::vector<uint8_t>> layouts;
std::vector<BadgeID> badges;
BridgeAboveFlags GetBridgeAboveFlags(uint gfx) const
{
if (gfx < this->bridge_above_flags.size()) return this->bridge_above_flags[gfx];
return {};
}
};
/** Class containing information relating to station classes. */

View File

@ -7,6 +7,7 @@
/** @file station_cmd.cpp Handling of station tiles. */
#include "core/enum_type.hpp"
#include "stdafx.h"
#include "core/flatset_type.hpp"
#include "aircraft.h"
@ -949,125 +950,6 @@ static CommandCost CheckFlatLandRailStation(TileIndex tile_cur, TileIndex north_
return cost;
}
/**
* Checks if a road stop can be built at the given tile.
* @param cur_tile Tile to check.
* @param allowed_z Height allowed for the tile. If allowed_z is negative, it will be set to the height of this tile.
* @param flags Operation to perform.
* @param invalid_dirs Prohibited directions (set of DiagDirections).
* @param is_drive_through True if trying to build a drive-through station.
* @param station_type Station type (bus, truck or road waypoint).
* @param axis Axis of a drive-through road stop.
* @param station StationID to be queried and returned if available.
* @param rt Road type to build, may be INVALID_ROADTYPE if an existing road is required.
* @return The cost in case of success, or an error code if it failed.
*/
static CommandCost CheckFlatLandRoadStop(TileIndex cur_tile, int &allowed_z, DoCommandFlags flags, DiagDirections invalid_dirs, bool is_drive_through, StationType station_type, Axis axis, StationID *station, RoadType rt)
{
CommandCost cost(EXPENSES_CONSTRUCTION);
CommandCost ret = CheckBuildableTile(cur_tile, invalid_dirs, allowed_z, !is_drive_through);
if (ret.Failed()) return ret;
cost.AddCost(ret.GetCost());
/* If station is set, then we have special handling to allow building on top of already existing stations.
* Station points to StationID::Invalid() if we can build on any station.
* Or it points to a station if we're only allowed to build on exactly that station. */
if (station != nullptr && IsTileType(cur_tile, MP_STATION)) {
if (!IsAnyRoadStop(cur_tile)) {
return ClearTile_Station(cur_tile, DoCommandFlag::Auto); // Get error message.
} else {
if (station_type != GetStationType(cur_tile) ||
is_drive_through != IsDriveThroughStopTile(cur_tile)) {
return ClearTile_Station(cur_tile, DoCommandFlag::Auto); // Get error message.
}
/* Drive-through station in the wrong direction. */
if (is_drive_through && IsDriveThroughStopTile(cur_tile) && GetDriveThroughStopAxis(cur_tile) != axis) {
return CommandCost(STR_ERROR_DRIVE_THROUGH_DIRECTION);
}
StationID st = GetStationIndex(cur_tile);
if (*station == StationID::Invalid()) {
*station = st;
} else if (*station != st) {
return CommandCost(STR_ERROR_ADJOINS_MORE_THAN_ONE_EXISTING);
}
}
} else {
bool build_over_road = is_drive_through && IsNormalRoadTile(cur_tile);
/* Road bits in the wrong direction. */
RoadBits rb = IsNormalRoadTile(cur_tile) ? GetAllRoadBits(cur_tile) : ROAD_NONE;
if (build_over_road && (rb & (axis == AXIS_X ? ROAD_Y : ROAD_X)) != 0) {
/* Someone was pedantic and *NEEDED* three fracking different error messages. */
switch (CountBits(rb)) {
case 1:
return CommandCost(STR_ERROR_DRIVE_THROUGH_DIRECTION);
case 2:
if (rb == ROAD_X || rb == ROAD_Y) return CommandCost(STR_ERROR_DRIVE_THROUGH_DIRECTION);
return CommandCost(STR_ERROR_DRIVE_THROUGH_CORNER);
default: // 3 or 4
return CommandCost(STR_ERROR_DRIVE_THROUGH_JUNCTION);
}
}
if (build_over_road) {
/* There is a road, check if we can build road+tram stop over it. */
RoadType road_rt = GetRoadType(cur_tile, RTT_ROAD);
if (road_rt != INVALID_ROADTYPE) {
Owner road_owner = GetRoadOwner(cur_tile, RTT_ROAD);
if (road_owner == OWNER_TOWN) {
if (!_settings_game.construction.road_stop_on_town_road) return CommandCost(STR_ERROR_DRIVE_THROUGH_ON_TOWN_ROAD);
} else if (!_settings_game.construction.road_stop_on_competitor_road && road_owner != OWNER_NONE) {
ret = CheckOwnership(road_owner);
if (ret.Failed()) return ret;
}
uint num_pieces = CountBits(GetRoadBits(cur_tile, RTT_ROAD));
if (rt != INVALID_ROADTYPE && RoadTypeIsRoad(rt) && !HasPowerOnRoad(rt, road_rt)) return CommandCost(STR_ERROR_NO_SUITABLE_ROAD);
if (GetDisallowedRoadDirections(cur_tile) != DRD_NONE && road_owner != OWNER_TOWN) {
ret = CheckOwnership(road_owner);
if (ret.Failed()) return ret;
}
cost.AddCost(RoadBuildCost(road_rt) * (2 - num_pieces));
} else if (rt != INVALID_ROADTYPE && RoadTypeIsRoad(rt)) {
cost.AddCost(RoadBuildCost(rt) * 2);
}
/* There is a tram, check if we can build road+tram stop over it. */
RoadType tram_rt = GetRoadType(cur_tile, RTT_TRAM);
if (tram_rt != INVALID_ROADTYPE) {
Owner tram_owner = GetRoadOwner(cur_tile, RTT_TRAM);
if (Company::IsValidID(tram_owner) &&
(!_settings_game.construction.road_stop_on_competitor_road ||
/* Disallow breaking end-of-line of someone else
* so trams can still reverse on this tile. */
HasExactlyOneBit(GetRoadBits(cur_tile, RTT_TRAM)))) {
ret = CheckOwnership(tram_owner);
if (ret.Failed()) return ret;
}
uint num_pieces = CountBits(GetRoadBits(cur_tile, RTT_TRAM));
if (rt != INVALID_ROADTYPE && RoadTypeIsTram(rt) && !HasPowerOnRoad(rt, tram_rt)) return CommandCost(STR_ERROR_NO_SUITABLE_ROAD);
cost.AddCost(RoadBuildCost(tram_rt) * (2 - num_pieces));
} else if (rt != INVALID_ROADTYPE && RoadTypeIsTram(rt)) {
cost.AddCost(RoadBuildCost(rt) * 2);
}
} else if (rt == INVALID_ROADTYPE) {
return CommandCost(STR_ERROR_THERE_IS_NO_ROAD);
} else {
ret = Command<CMD_LANDSCAPE_CLEAR>::Do(flags, cur_tile);
if (ret.Failed()) return ret;
cost.AddCost(ret.GetCost());
cost.AddCost(RoadBuildCost(rt) * 2);
}
}
return cost;
}
/**
* Check whether we can expand the rail part of the given station.
@ -1324,6 +1206,230 @@ void SetRailStationTileFlags(TileIndex tile, const StationSpec *statspec)
SetStationTileHaveWires(tile, !flags.Test(StationSpec::TileFlag::NoWires));
}
CommandCost IsRailStationBridgeAboveOk(TileIndex tile, const StationSpec *statspec, uint8_t layout, TileIndex northern_bridge_end, TileIndex southern_bridge_end, int bridge_height,
BridgeType bridge_type, TransportType bridge_transport_type)
{
if (statspec != nullptr && statspec->internal_flags.Test(StationSpecIntlFlag::BridgeHeightsSet)) {
int height_above = statspec->GetBridgeAboveFlags(layout).height;
if (height_above == 0) return CommandCost(INVALID_STRING_ID);
if (GetTileMaxZ(tile) + height_above > bridge_height) {
return CommandCost(STR_ERROR_BRIDGE_TOO_LOW_FOR_STATION);
}
} else if (!statspec) {
/* Default stations/waypoints */
const int height = layout < 4 ? 2 : 5;
if (GetTileMaxZ(tile) + height > bridge_height) return CommandCost(STR_ERROR_BRIDGE_TOO_LOW_FOR_STATION);
} else {
return CommandCost(INVALID_STRING_ID);
}
BridgePiecePillarFlags disallowed_pillar_flags;
if (statspec != nullptr && statspec->internal_flags.Test(StationSpecIntlFlag::BridgeDisallowedPillarsSet)) {
/* Pillar flags set by NewGRF */
disallowed_pillar_flags = statspec->GetBridgeAboveFlags(layout).disallowed_pillars;
} else if (!statspec) {
/* Default stations/waypoints */
if (layout < 8) {
static const BridgePiecePillarFlags st_flags[8] = {
{BridgePiecePillarFlag::BPPF_EDGE_SW, BridgePiecePillarFlag::BPPF_EDGE_NE}, //0x50,
{BridgePiecePillarFlag::BPPF_EDGE_NW, BridgePiecePillarFlag::BPPF_EDGE_SE}, //0xA0,
{BridgePiecePillarFlag::BPPF_EDGE_SW, BridgePiecePillarFlag::BPPF_EDGE_NE}, //0x50,
{BridgePiecePillarFlag::BPPF_EDGE_NW, BridgePiecePillarFlag::BPPF_EDGE_SE}, //0xA0,
{BridgePiecePillarFlag::BPPF_EDGE_SW, BridgePiecePillarFlag::BPPF_EDGE_NE, BridgePiecePillarFlag::BPPF_EDGE_SE, BridgePiecePillarFlag::BPPF_CORNER_E, BridgePiecePillarFlag::BPPF_CORNER_S}, //0x50 | 0x26,
{BridgePiecePillarFlag::BPPF_EDGE_NW, BridgePiecePillarFlag::BPPF_EDGE_SE, BridgePiecePillarFlag::BPPF_EDGE_NE, BridgePiecePillarFlag::BPPF_CORNER_N, BridgePiecePillarFlag::BPPF_CORNER_E}, //0xA0 | 0x1C,
{BridgePiecePillarFlag::BPPF_EDGE_NW, BridgePiecePillarFlag::BPPF_EDGE_SE, BridgePiecePillarFlag::BPPF_EDGE_NW, BridgePiecePillarFlag::BPPF_CORNER_N, BridgePiecePillarFlag::BPPF_CORNER_W}, //0x50 | 0x89,
{BridgePiecePillarFlag::BPPF_EDGE_NW, BridgePiecePillarFlag::BPPF_EDGE_SE, BridgePiecePillarFlag::BPPF_EDGE_SW, BridgePiecePillarFlag::BPPF_CORNER_S, BridgePiecePillarFlag::BPPF_CORNER_W} //0xA0 | 0x43
};
disallowed_pillar_flags = st_flags[layout];
} else {
disallowed_pillar_flags = {};
}
} else if (GetStationTileFlags(layout, statspec).Test(StationSpec::TileFlag::Blocked)) {
/* Non-track station tiles */
disallowed_pillar_flags = {};
} else {
/* Tracked station tiles */
const Axis axis = HasBit(layout, 0) ? AXIS_Y : AXIS_X;
disallowed_pillar_flags = axis == AXIS_X ? BridgePiecePillarFlags({BridgePiecePillarFlag::BPPF_EDGE_SW, BridgePiecePillarFlag::BPPF_EDGE_NE}) : BridgePiecePillarFlags({BridgePiecePillarFlag::BPPF_EDGE_NW, BridgePiecePillarFlag::BPPF_EDGE_SE}); //0x50, 0xA0
}
if (!(GetBridgeTilePillarFlags(tile, northern_bridge_end, southern_bridge_end, bridge_type, bridge_transport_type) & disallowed_pillar_flags).Any()) return CommandCost(STR_ERROR_BRIDGE_PILLARS_OBSTRUCT_STATION);
return CommandCost();
}
CommandCost IsRailStationBridgeAboveOk(TileIndex tile, const StationSpec *statspec, uint8_t layout)
{
if (!IsBridgeAbove(tile)) return CommandCost();
TileIndex southern_bridge_end = GetSouthernBridgeEnd(tile);
TileIndex northern_bridge_end = GetNorthernBridgeEnd(tile);
return IsRailStationBridgeAboveOk(tile, statspec, layout, northern_bridge_end, southern_bridge_end, GetBridgeHeight(southern_bridge_end),
GetBridgeType(southern_bridge_end), GetTunnelBridgeTransportType(southern_bridge_end));
}
CommandCost IsRoadStopBridgeAboveOK(TileIndex tile, const RoadStopSpec *spec, bool drive_through, DiagDirection entrance,
TileIndex northern_bridge_end, TileIndex southern_bridge_end, int bridge_height,
BridgeType bridge_type, TransportType bridge_transport_type)
{
if (spec != nullptr && spec->internal_flags.Test(RoadStopSpecIntlFlag::BridgeHeightsSet)) {
int height = spec->bridge_height[drive_through ? (GFX_TRUCK_BUS_DRIVETHROUGH_OFFSET + DiagDirToAxis(entrance)) : entrance];
if (height == 0) return CommandCost(INVALID_STRING_ID);
if (GetTileMaxZ(tile) + height > bridge_height) {
return CommandCost(STR_ERROR_BRIDGE_TOO_LOW_FOR_STATION);
}
} else {
return CommandCost(INVALID_STRING_ID);
if (GetTileMaxZ(tile) + (drive_through ? 1 : 2) > bridge_height) {
return CommandCost(STR_ERROR_BRIDGE_TOO_LOW_FOR_STATION);
}
}
BridgePiecePillarFlags disallowed_pillar_flags = {};
if (spec != nullptr && spec->internal_flags.Test(RoadStopSpecIntlFlag::BridgeDisallowedPillarsSet)) {
disallowed_pillar_flags = spec->bridge_disallowed_pillars[drive_through ? (GFX_TRUCK_BUS_DRIVETHROUGH_OFFSET + DiagDirToAxis(entrance)) : entrance];
} else if (drive_through) {
disallowed_pillar_flags = DiagDirToAxis(entrance) == AXIS_X ? BridgePiecePillarFlags({BridgePiecePillarFlag::BPPF_EDGE_SW, BridgePiecePillarFlag::BPPF_EDGE_NE}) : BridgePiecePillarFlags({BridgePiecePillarFlag::BPPF_EDGE_NW, BridgePiecePillarFlag::BPPF_EDGE_SE}); //0x50, 0xA0
} else {
disallowed_pillar_flags.Set((BridgePiecePillarFlags) (4 + entrance));
}
if (!(GetBridgeTilePillarFlags(tile, northern_bridge_end, southern_bridge_end, bridge_type, bridge_transport_type) & disallowed_pillar_flags).Any()) return CommandCost(STR_ERROR_BRIDGE_PILLARS_OBSTRUCT_STATION);
return CommandCost();
}
/**
* Checks if a road stop can be built at the given tile.
* @param cur_tile Tile to check.
* @param allowed_z Height allowed for the tile. If allowed_z is negative, it will be set to the height of this tile.
* @param flags Operation to perform.
* @param invalid_dirs Prohibited directions (set of DiagDirections).
* @param is_drive_through True if trying to build a drive-through station.
* @param station_type Station type (bus, truck or road waypoint).
* @param axis Axis of a drive-through road stop.
* @param station StationID to be queried and returned if available.
* @param rt Road type to build, may be INVALID_ROADTYPE if an existing road is required.
* @return The cost in case of success, or an error code if it failed.
*/
static CommandCost CheckFlatLandRoadStop(TileIndex cur_tile, int &allowed_z, const RoadStopSpec *spec, DoCommandFlags flags, DiagDirections invalid_dirs, bool is_drive_through, StationType station_type, Axis axis, StationID *station, RoadType rt)
{
CommandCost cost(EXPENSES_CONSTRUCTION);
bool allow_under_bridge = spec != nullptr && spec->internal_flags.Test(RoadStopSpecIntlFlag::BridgeHeightsSet);
CommandCost ret = CheckBuildableTile(cur_tile, invalid_dirs, allowed_z, !is_drive_through, true);
if (ret.Failed()) return ret;
cost.AddCost(ret.GetCost());
if (allow_under_bridge && IsBridgeAbove(cur_tile)) {
TileIndex southern_bridge_end = GetSouthernBridgeEnd(cur_tile);
TileIndex northern_bridge_end = GetNorthernBridgeEnd(cur_tile);
CommandCost bridge_ret = IsRoadStopBridgeAboveOK(cur_tile, spec, is_drive_through, DiagDirection::DIAGDIR_NE /*obviously wrong, but how do you get the "first bit" from invalid_dirs? and how would that be correct?? */,
northern_bridge_end, southern_bridge_end, GetBridgeHeight(southern_bridge_end),
GetBridgeType(southern_bridge_end), GetTunnelBridgeTransportType(southern_bridge_end));
if (bridge_ret.Failed()) return bridge_ret;
}
/* If station is set, then we have special handling to allow building on top of already existing stations.
* Station points to StationID::Invalid() if we can build on any station.
* Or it points to a station if we're only allowed to build on exactly that station. */
if (station != nullptr && IsTileType(cur_tile, MP_STATION)) {
if (!IsAnyRoadStop(cur_tile)) {
return ClearTile_Station(cur_tile, DoCommandFlag::Auto); // Get error message.
} else {
if (station_type != GetStationType(cur_tile) ||
is_drive_through != IsDriveThroughStopTile(cur_tile)) {
return ClearTile_Station(cur_tile, DoCommandFlag::Auto); // Get error message.
}
/* Drive-through station in the wrong direction. */
if (is_drive_through && IsDriveThroughStopTile(cur_tile) && GetDriveThroughStopAxis(cur_tile) != axis) {
return CommandCost(STR_ERROR_DRIVE_THROUGH_DIRECTION);
}
StationID st = GetStationIndex(cur_tile);
if (*station == StationID::Invalid()) {
*station = st;
} else if (*station != st) {
return CommandCost(STR_ERROR_ADJOINS_MORE_THAN_ONE_EXISTING);
}
}
} else {
bool build_over_road = is_drive_through && IsNormalRoadTile(cur_tile);
/* Road bits in the wrong direction. */
RoadBits rb = IsNormalRoadTile(cur_tile) ? GetAllRoadBits(cur_tile) : ROAD_NONE;
if (build_over_road && (rb & (axis == AXIS_X ? ROAD_Y : ROAD_X)) != 0) {
/* Someone was pedantic and *NEEDED* three fracking different error messages. */
switch (CountBits(rb)) {
case 1:
return CommandCost(STR_ERROR_DRIVE_THROUGH_DIRECTION);
case 2:
if (rb == ROAD_X || rb == ROAD_Y) return CommandCost(STR_ERROR_DRIVE_THROUGH_DIRECTION);
return CommandCost(STR_ERROR_DRIVE_THROUGH_CORNER);
default: // 3 or 4
return CommandCost(STR_ERROR_DRIVE_THROUGH_JUNCTION);
}
}
if (build_over_road) {
/* There is a road, check if we can build road+tram stop over it. */
RoadType road_rt = GetRoadType(cur_tile, RTT_ROAD);
if (road_rt != INVALID_ROADTYPE) {
Owner road_owner = GetRoadOwner(cur_tile, RTT_ROAD);
if (road_owner == OWNER_TOWN) {
if (!_settings_game.construction.road_stop_on_town_road) return CommandCost(STR_ERROR_DRIVE_THROUGH_ON_TOWN_ROAD);
} else if (!_settings_game.construction.road_stop_on_competitor_road && road_owner != OWNER_NONE) {
ret = CheckOwnership(road_owner);
if (ret.Failed()) return ret;
}
uint num_pieces = CountBits(GetRoadBits(cur_tile, RTT_ROAD));
if (rt != INVALID_ROADTYPE && RoadTypeIsRoad(rt) && !HasPowerOnRoad(rt, road_rt)) return CommandCost(STR_ERROR_NO_SUITABLE_ROAD);
if (GetDisallowedRoadDirections(cur_tile) != DRD_NONE && road_owner != OWNER_TOWN) {
ret = CheckOwnership(road_owner);
if (ret.Failed()) return ret;
}
cost.AddCost(RoadBuildCost(road_rt) * (2 - num_pieces));
} else if (rt != INVALID_ROADTYPE && RoadTypeIsRoad(rt)) {
cost.AddCost(RoadBuildCost(rt) * 2);
}
/* There is a tram, check if we can build road+tram stop over it. */
RoadType tram_rt = GetRoadType(cur_tile, RTT_TRAM);
if (tram_rt != INVALID_ROADTYPE) {
Owner tram_owner = GetRoadOwner(cur_tile, RTT_TRAM);
if (Company::IsValidID(tram_owner) &&
(!_settings_game.construction.road_stop_on_competitor_road ||
/* Disallow breaking end-of-line of someone else
* so trams can still reverse on this tile. */
HasExactlyOneBit(GetRoadBits(cur_tile, RTT_TRAM)))) {
ret = CheckOwnership(tram_owner);
if (ret.Failed()) return ret;
}
uint num_pieces = CountBits(GetRoadBits(cur_tile, RTT_TRAM));
if (rt != INVALID_ROADTYPE && RoadTypeIsTram(rt) && !HasPowerOnRoad(rt, tram_rt)) return CommandCost(STR_ERROR_NO_SUITABLE_ROAD);
cost.AddCost(RoadBuildCost(tram_rt) * (2 - num_pieces));
} else if (rt != INVALID_ROADTYPE && RoadTypeIsTram(rt)) {
cost.AddCost(RoadBuildCost(rt) * 2);
}
} else if (rt == INVALID_ROADTYPE) {
return CommandCost(STR_ERROR_THERE_IS_NO_ROAD);
} else {
ret = Command<CMD_LANDSCAPE_CLEAR>::Do(flags, cur_tile);
if (ret.Failed()) return ret;
cost.AddCost(ret.GetCost());
cost.AddCost(RoadBuildCost(rt) * 2);
}
}
return cost;
}
/**
* Build rail station
* @param flags operation to perform
@ -1362,6 +1468,9 @@ CommandCost CmdBuildRailStation(DoCommandFlags flags, TileIndex tile_org, RailTy
w_org = numtracks;
}
/* Check if the first tile and the last tile are valid */
if (!IsValidTile(tile_org) || TileAddWrap(tile_org, w_org - 1, h_org - 1) == INVALID_TILE) return CMD_ERROR;
bool reuse = (station_to_join != NEW_STATION);
if (!reuse) station_to_join = StationID::Invalid();
bool distant_join = (station_to_join != StationID::Invalid());
@ -1376,6 +1485,32 @@ CommandCost CmdBuildRailStation(DoCommandFlags flags, TileIndex tile_org, RailTy
/* Make sure the area below consists of clear tiles. (OR tiles belonging to a certain rail station) */
StationID est = StationID::Invalid();
std::vector<Train *> affected_vehicles;
const StationSpec *statspec = StationClass::Get(spec_class)->GetSpec(spec_index);
TileIndexDiff tile_delta = TileOffsByAxis(axis); // offset to go to the next platform tile
TileIndexDiff track_delta = TileOffsByAxis(OtherAxis(axis)); // offset to go to the next track
std::vector<uint8_t> layout_buffer;
layout_buffer.resize(numtracks * plat_len);
GetStationLayout(&layout_buffer[0], numtracks, plat_len, statspec);
{
TileIndex tile_track = tile_org;
uint8_t *check_layout_ptr = &layout_buffer[0];
for (uint i = 0; i < numtracks; i++) {
TileIndex tile = tile_track;
for (uint j = 0; j < plat_len; j++) {
ret = IsRailStationBridgeAboveOk(tile, statspec, *check_layout_ptr++);
if (ret.Failed()) {
//return CommandCost::DualErrorMessage(STR_ERROR_MUST_DEMOLISH_BRIDGE_FIRST, ret.GetErrorMessage()); //FIXME
return ret;
}
tile += tile_delta;
}
tile_track += track_delta;
}
}
/* Add construction and clearing expenses. */
CommandCost cost = CalculateRailStationCost(new_location, flags, axis, &est, rt, affected_vehicles, spec_class, spec_index, plat_len, numtracks);
if (cost.Failed()) return cost;
@ -1393,7 +1528,6 @@ CommandCost CmdBuildRailStation(DoCommandFlags flags, TileIndex tile_org, RailTy
}
/* Check if we can allocate a custom stationspec to this station */
const StationSpec *statspec = StationClass::Get(spec_class)->GetSpec(spec_index);
int specindex = AllocateSpecToStation(statspec, st, flags.Test(DoCommandFlag::Execute));
if (specindex == -1) return CommandCost(STR_ERROR_TOO_MANY_STATION_SPECS);
@ -1423,8 +1557,6 @@ CommandCost CmdBuildRailStation(DoCommandFlags flags, TileIndex tile_org, RailTy
st->cached_anim_triggers.Set(statspec->animation.triggers);
}
TileIndexDiff tile_delta = TileOffsByAxis(axis); // offset to go to the next platform tile
TileIndexDiff track_delta = TileOffsByAxis(OtherAxis(axis)); // offset to go to the next track
Track track = AxisToTrack(axis);
std::vector<uint8_t> layouts(numtracks * plat_len);
@ -1913,7 +2045,7 @@ static CommandCost FindJoiningRoadStop(StationID existing_stop, StationID statio
* @param unit_cost The cost to build one road stop of the current type.
* @return The cost in case of success, or an error code if it failed.
*/
CommandCost CalculateRoadStopCost(TileArea tile_area, DoCommandFlags flags, bool is_drive_through, StationType station_type, Axis axis, DiagDirection ddir, StationID *est, RoadType rt, Money unit_cost)
CommandCost CalculateRoadStopCost(TileArea tile_area, DoCommandFlags flags, bool is_drive_through, StationType station_type, const RoadStopSpec *roadstopspec, Axis axis, DiagDirection ddir, StationID *est, RoadType rt, Money unit_cost)
{
DiagDirections invalid_dirs{};
if (is_drive_through) {
@ -1927,7 +2059,7 @@ CommandCost CalculateRoadStopCost(TileArea tile_area, DoCommandFlags flags, bool
int allowed_z = -1;
CommandCost cost(EXPENSES_CONSTRUCTION);
for (TileIndex cur_tile : tile_area) {
CommandCost ret = CheckFlatLandRoadStop(cur_tile, allowed_z, flags, invalid_dirs, is_drive_through, station_type, axis, est, rt);
CommandCost ret = CheckFlatLandRoadStop(cur_tile, allowed_z, roadstopspec, flags, invalid_dirs, is_drive_through, station_type, axis, est, rt);
if (ret.Failed()) return ret;
bool is_preexisting_roadstop = IsTileType(cur_tile, MP_STATION) && IsAnyRoadStop(cur_tile);
@ -2008,7 +2140,7 @@ CommandCost CmdBuildRoadStop(DoCommandFlags flags, TileIndex tile, uint8_t width
unit_cost = _price[is_truck_stop ? PR_BUILD_STATION_TRUCK : PR_BUILD_STATION_BUS];
}
StationID est = StationID::Invalid();
CommandCost cost = CalculateRoadStopCost(roadstop_area, flags, is_drive_through, is_truck_stop ? StationType::Truck : StationType::Bus, axis, ddir, &est, rt, unit_cost);
CommandCost cost = CalculateRoadStopCost(roadstop_area, flags, is_drive_through, is_truck_stop ? StationType::Truck : StationType::Bus, roadstopspec, axis, ddir, &est, rt, unit_cost);
if (cost.Failed()) return cost;
Station *st = nullptr;
@ -3380,6 +3512,7 @@ draw_default_foundation:
}
DrawRailTileSeq(ti, t, TO_BUILDINGS, total_offset, relocation, palette);
DrawBridgeMiddle(ti);
}
void StationPickerDrawSprite(int x, int y, StationType st, RailType railtype, RoadType roadtype, int image)

View File

@ -740,8 +740,53 @@ static const std::span<const std::span<const PalSpriteID>> _bridge_sprite_table[
* @param nrl description of the rail bridge in query tool
* @param nrd description of the road bridge in query tool
*/
#define MBR(y, mnl, mxl, p, mxs, spr, plt, dsc, nrl, nrd) \
{TimerGameCalendar::Year{y}, mnl, mxl, p, mxs, spr, plt, dsc, { nrl, nrd }, {}, 0}
#define MBR(y, mnl, mxl, p, mxs, spr, plt, dsc, nrl, nrd, pillars) \
{TimerGameCalendar::Year{y}, mnl, mxl, p, mxs, spr, plt, dsc, { nrl, nrd }, {}, 0, BridgeSpecCtrlFlags(), pillars}
static constexpr std::array<BridgePiecePillarFlags, 12> all_pillars = {
BridgePiecePillarFlag::BPPF_ALL_CORNERS, //0x0F,
BridgePiecePillarFlag::BPPF_ALL_CORNERS, //0x0F,
BridgePiecePillarFlag::BPPF_ALL_CORNERS, //0x0F,
BridgePiecePillarFlag::BPPF_ALL_CORNERS, //0x0F,
BridgePiecePillarFlag::BPPF_ALL_CORNERS, //0x0F,
BridgePiecePillarFlag::BPPF_ALL_CORNERS, //0x0F,
BridgePiecePillarFlag::BPPF_ALL_CORNERS, //0x0F,
BridgePiecePillarFlag::BPPF_ALL_CORNERS, //0x0F,
BridgePiecePillarFlag::BPPF_ALL_CORNERS, //0x0F,
BridgePiecePillarFlag::BPPF_ALL_CORNERS, //0x0F,
BridgePiecePillarFlag::BPPF_ALL_CORNERS, //0x0F,
BridgePiecePillarFlag::BPPF_ALL_CORNERS, //0x0F
};
static constexpr std::array<BridgePiecePillarFlags, 12> susp_pillars = {
BridgePiecePillarFlags({BridgePiecePillarFlag::BPPF_CORNER_W, BridgePiecePillarFlag::BPPF_CORNER_S}), //0x03,
BridgePiecePillarFlags({BridgePiecePillarFlag::BPPF_CORNER_S, BridgePiecePillarFlag::BPPF_CORNER_E}), //0x06,
BridgePiecePillarFlags({BridgePiecePillarFlag::BPPF_CORNER_E, BridgePiecePillarFlag::BPPF_CORNER_N}), //0x0C,
BridgePiecePillarFlags({BridgePiecePillarFlag::BPPF_CORNER_W, BridgePiecePillarFlag::BPPF_CORNER_N}), //0x09,
BridgePiecePillarFlags({BridgePiecePillarFlag::BPPF_CORNER_E, BridgePiecePillarFlag::BPPF_CORNER_N}), //0x0C,
BridgePiecePillarFlags({BridgePiecePillarFlag::BPPF_CORNER_W, BridgePiecePillarFlag::BPPF_CORNER_N}), //0x09,
BridgePiecePillarFlags({BridgePiecePillarFlag::BPPF_CORNER_W, BridgePiecePillarFlag::BPPF_CORNER_S}), //0x03,
BridgePiecePillarFlags({BridgePiecePillarFlag::BPPF_CORNER_S, BridgePiecePillarFlag::BPPF_CORNER_E}), //0x06,
BridgePiecePillarFlags({BridgePiecePillarFlag::BPPF_ALL_CORNERS}), //0x0F,
BridgePiecePillarFlags({BridgePiecePillarFlag::BPPF_ALL_CORNERS}), //0x0F,
BridgePiecePillarFlags({BridgePiecePillarFlag()}), //0x00,
BridgePiecePillarFlags({BridgePiecePillarFlag()}), //0x00
};
static constexpr std::array<BridgePiecePillarFlags, 12> cant_pillars = {
BridgePiecePillarFlags({BridgePiecePillarFlag()}), //0x00,
BridgePiecePillarFlags({BridgePiecePillarFlag()}), //0x00,
BridgePiecePillarFlags({BridgePiecePillarFlag::BPPF_CORNER_E, BridgePiecePillarFlag::BPPF_CORNER_N}), //0x0C,
BridgePiecePillarFlags({BridgePiecePillarFlag::BPPF_CORNER_W, BridgePiecePillarFlag::BPPF_CORNER_N}), //0x09,
BridgePiecePillarFlags({BridgePiecePillarFlag::BPPF_CORNER_E, BridgePiecePillarFlag::BPPF_CORNER_N}), //0x0C,
BridgePiecePillarFlags({BridgePiecePillarFlag::BPPF_CORNER_W, BridgePiecePillarFlag::BPPF_CORNER_N}), //0x09,
BridgePiecePillarFlags({BridgePiecePillarFlag::BPPF_CORNER_E, BridgePiecePillarFlag::BPPF_CORNER_N}), //0x0C,
BridgePiecePillarFlags({BridgePiecePillarFlag::BPPF_CORNER_W, BridgePiecePillarFlag::BPPF_CORNER_N}), //0x09,
BridgePiecePillarFlags({BridgePiecePillarFlag::BPPF_CORNER_E, BridgePiecePillarFlag::BPPF_CORNER_N}), //0x0C,
BridgePiecePillarFlags({BridgePiecePillarFlag::BPPF_CORNER_W, BridgePiecePillarFlag::BPPF_CORNER_N}), //0x09,
BridgePiecePillarFlags({BridgePiecePillarFlag::BPPF_CORNER_E, BridgePiecePillarFlag::BPPF_CORNER_N}), //0x0C,
BridgePiecePillarFlags({BridgePiecePillarFlag::BPPF_CORNER_W, BridgePiecePillarFlag::BPPF_CORNER_N}), //0x09
};
const BridgeSpec _orig_bridge[] = {
/*
@ -755,43 +800,43 @@ const BridgeSpec _orig_bridge[] = {
string with description name on rail name on road
| | | | */
MBR( 0, 0, 0xFFFF, 80, 32, 0xA24, PAL_NONE,
STR_BRIDGE_NAME_WOODEN, STR_LAI_BRIDGE_DESCRIPTION_RAIL_WOODEN, STR_LAI_BRIDGE_DESCRIPTION_ROAD_WOODEN),
STR_BRIDGE_NAME_WOODEN, STR_LAI_BRIDGE_DESCRIPTION_RAIL_WOODEN, STR_LAI_BRIDGE_DESCRIPTION_ROAD_WOODEN, all_pillars),
MBR( 0, 0, 2, 112, 48, 0xA26, PALETTE_TO_STRUCT_RED,
STR_BRIDGE_NAME_CONCRETE, STR_LAI_BRIDGE_DESCRIPTION_RAIL_CONCRETE, STR_LAI_BRIDGE_DESCRIPTION_ROAD_CONCRETE),
STR_BRIDGE_NAME_CONCRETE, STR_LAI_BRIDGE_DESCRIPTION_RAIL_CONCRETE, STR_LAI_BRIDGE_DESCRIPTION_ROAD_CONCRETE, all_pillars),
MBR(1930, 0, 5, 144, 64, 0xA25, PAL_NONE,
STR_BRIDGE_NAME_GIRDER_STEEL, STR_LAI_BRIDGE_DESCRIPTION_RAIL_GIRDER_STEEL, STR_LAI_BRIDGE_DESCRIPTION_ROAD_GIRDER_STEEL),
STR_BRIDGE_NAME_GIRDER_STEEL, STR_LAI_BRIDGE_DESCRIPTION_RAIL_GIRDER_STEEL, STR_LAI_BRIDGE_DESCRIPTION_ROAD_GIRDER_STEEL, all_pillars),
MBR( 0, 2, 10, 168, 80, 0xA22, PALETTE_TO_STRUCT_CONCRETE,
STR_BRIDGE_NAME_SUSPENSION_CONCRETE, STR_LAI_BRIDGE_DESCRIPTION_RAIL_SUSPENSION_CONCRETE, STR_LAI_BRIDGE_DESCRIPTION_ROAD_SUSPENSION_CONCRETE),
STR_BRIDGE_NAME_SUSPENSION_CONCRETE, STR_LAI_BRIDGE_DESCRIPTION_RAIL_SUSPENSION_CONCRETE, STR_LAI_BRIDGE_DESCRIPTION_ROAD_SUSPENSION_CONCRETE, susp_pillars),
MBR(1930, 3, 0xFFFF, 185, 96, 0xA22, PAL_NONE,
STR_BRIDGE_NAME_SUSPENSION_STEEL, STR_LAI_BRIDGE_DESCRIPTION_RAIL_SUSPENSION_STEEL, STR_LAI_BRIDGE_DESCRIPTION_ROAD_SUSPENSION_STEEL),
STR_BRIDGE_NAME_SUSPENSION_STEEL, STR_LAI_BRIDGE_DESCRIPTION_RAIL_SUSPENSION_STEEL, STR_LAI_BRIDGE_DESCRIPTION_ROAD_SUSPENSION_STEEL, susp_pillars),
MBR(1930, 3, 0xFFFF, 192, 112, 0xA22, PALETTE_TO_STRUCT_YELLOW,
STR_BRIDGE_NAME_SUSPENSION_STEEL, STR_LAI_BRIDGE_DESCRIPTION_RAIL_SUSPENSION_STEEL, STR_LAI_BRIDGE_DESCRIPTION_ROAD_SUSPENSION_STEEL),
STR_BRIDGE_NAME_SUSPENSION_STEEL, STR_LAI_BRIDGE_DESCRIPTION_RAIL_SUSPENSION_STEEL, STR_LAI_BRIDGE_DESCRIPTION_ROAD_SUSPENSION_STEEL, susp_pillars),
MBR(1930, 3, 7, 224, 160, 0xA23, PAL_NONE,
STR_BRIDGE_NAME_CANTILEVER_STEEL, STR_LAI_BRIDGE_DESCRIPTION_RAIL_CANTILEVER_STEEL, STR_LAI_BRIDGE_DESCRIPTION_ROAD_CANTILEVER_STEEL),
STR_BRIDGE_NAME_CANTILEVER_STEEL, STR_LAI_BRIDGE_DESCRIPTION_RAIL_CANTILEVER_STEEL, STR_LAI_BRIDGE_DESCRIPTION_ROAD_CANTILEVER_STEEL, cant_pillars),
MBR(1930, 3, 8, 232, 208, 0xA23, PALETTE_TO_STRUCT_BROWN,
STR_BRIDGE_NAME_CANTILEVER_STEEL, STR_LAI_BRIDGE_DESCRIPTION_RAIL_CANTILEVER_STEEL, STR_LAI_BRIDGE_DESCRIPTION_ROAD_CANTILEVER_STEEL),
STR_BRIDGE_NAME_CANTILEVER_STEEL, STR_LAI_BRIDGE_DESCRIPTION_RAIL_CANTILEVER_STEEL, STR_LAI_BRIDGE_DESCRIPTION_ROAD_CANTILEVER_STEEL, cant_pillars),
MBR(1930, 3, 9, 248, 240, 0xA23, PALETTE_TO_STRUCT_RED,
STR_BRIDGE_NAME_CANTILEVER_STEEL, STR_LAI_BRIDGE_DESCRIPTION_RAIL_CANTILEVER_STEEL, STR_LAI_BRIDGE_DESCRIPTION_ROAD_CANTILEVER_STEEL),
STR_BRIDGE_NAME_CANTILEVER_STEEL, STR_LAI_BRIDGE_DESCRIPTION_RAIL_CANTILEVER_STEEL, STR_LAI_BRIDGE_DESCRIPTION_ROAD_CANTILEVER_STEEL, cant_pillars),
MBR(1930, 0, 2, 240, 256, 0xA27, PAL_NONE,
STR_BRIDGE_NAME_GIRDER_STEEL, STR_LAI_BRIDGE_DESCRIPTION_RAIL_GIRDER_STEEL, STR_LAI_BRIDGE_DESCRIPTION_ROAD_GIRDER_STEEL),
STR_BRIDGE_NAME_GIRDER_STEEL, STR_LAI_BRIDGE_DESCRIPTION_RAIL_GIRDER_STEEL, STR_LAI_BRIDGE_DESCRIPTION_ROAD_GIRDER_STEEL, all_pillars),
MBR(1995, 2, 0xFFFF, 255, 320, 0xA28, PAL_NONE,
STR_BRIDGE_NAME_TUBULAR_STEEL, STR_LAI_BRIDGE_DESCRIPTION_RAIL_TUBULAR_STEEL, STR_LAI_BRIDGE_DESCRIPTION_ROAD_TUBULAR_STEEL),
STR_BRIDGE_NAME_TUBULAR_STEEL, STR_LAI_BRIDGE_DESCRIPTION_RAIL_TUBULAR_STEEL, STR_LAI_BRIDGE_DESCRIPTION_ROAD_TUBULAR_STEEL, cant_pillars),
MBR(2005, 2, 0xFFFF, 380, 512, 0xA28, PALETTE_TO_STRUCT_YELLOW,
STR_BRIDGE_NAME_TUBULAR_STEEL, STR_LAI_BRIDGE_DESCRIPTION_RAIL_TUBULAR_STEEL, STR_LAI_BRIDGE_DESCRIPTION_ROAD_TUBULAR_STEEL),
STR_BRIDGE_NAME_TUBULAR_STEEL, STR_LAI_BRIDGE_DESCRIPTION_RAIL_TUBULAR_STEEL, STR_LAI_BRIDGE_DESCRIPTION_ROAD_TUBULAR_STEEL, cant_pillars),
MBR(2010, 2, 0xFFFF, 510, 608, 0xA28, PALETTE_TO_STRUCT_CONCRETE,
STR_BRIDGE_TUBULAR_SILICON, STR_LAI_BRIDGE_DESCRIPTION_RAIL_TUBULAR_STEEL, STR_LAI_BRIDGE_DESCRIPTION_ROAD_TUBULAR_STEEL)
STR_BRIDGE_TUBULAR_SILICON, STR_LAI_BRIDGE_DESCRIPTION_RAIL_TUBULAR_STEEL, STR_LAI_BRIDGE_DESCRIPTION_ROAD_TUBULAR_STEEL, cant_pillars)
};
#undef MBR

View File

@ -39,10 +39,13 @@
#include "object_base.h"
#include "water.h"
#include "company_gui.h"
#include "newgrf_roadstop.h"
#include "station_func.h"
#include "station_map.h"
#include "tunnelbridge_cmd.h"
#include "landscape_cmd.h"
#include "terraform_cmd.h"
#include "newgrf_station.h"
#include "table/strings.h"
#include "table/bridge_land.h"
@ -55,6 +58,12 @@ TileIndex _build_tunnel_endtile; ///< The end of a tunnel; as hidden return from
/** Z position of the bridge sprites relative to bridge height (downwards) */
static const int BRIDGE_Z_START = 3;
extern CommandCost IsRailStationBridgeAboveOk(TileIndex tile, const StationSpec *statspec, uint8_t layout, TileIndex northern_bridge_end, TileIndex southern_bridge_end, int bridge_height,
BridgeType bridge_type, TransportType bridge_transport_type);
extern CommandCost IsRoadStopBridgeAboveOK(TileIndex tile, const RoadStopSpec *spec, bool drive_through, DiagDirection entrance,
TileIndex northern_bridge_end, TileIndex southern_bridge_end, int bridge_height,
BridgeType bridge_type, TransportType bridge_transport_type);
/**
* Mark bridge tiles dirty.
@ -391,6 +400,43 @@ CommandCost CmdBuildBridge(DoCommandFlags flags, TileIndex tile_end, TileIndex t
/* If bridge belonged to bankrupt company, it has a new owner now */
is_new_owner = (owner == OWNER_NONE);
if (is_new_owner) owner = company;
TileIndexDiff delta = (direction == AXIS_X ? TileDiffXY(1, 0) : TileDiffXY(0, 1));
for (TileIndex tile = tile_start + delta; tile != tile_end; tile += delta) {
if (!IsTileType(tile, MP_STATION)) continue;
switch (GetStationType(tile)) {
case StationType::Rail:
case StationType::RailWaypoint: {
CommandCost ret = IsRailStationBridgeAboveOk(tile, GetStationSpec(tile), GetStationGfx(tile), tile_start, tile_end, z_start + 1, bridge_type, transport_type);
if (ret.Failed()) {
if (ret.GetErrorMessage() != INVALID_STRING_ID) return ret;
ret = Command<CMD_LANDSCAPE_CLEAR>::Do(flags, tile);
if (ret.Failed()) return ret;
}
break;
}
case StationType::Bus:
case StationType::Truck:
case StationType::RoadWaypoint: {
CommandCost ret = IsRoadStopBridgeAboveOK(tile, GetRoadStopSpec(tile), IsDriveThroughStopTile(tile), IsDriveThroughStopTile(tile) ? AxisToDiagDir(GetDriveThroughStopAxis(tile)) : GetBayRoadStopDir(tile),
tile_start, tile_end, z_start + 1, bridge_type, transport_type);
if (ret.Failed()) {
if (ret.GetErrorMessage() != INVALID_STRING_ID) return ret;
ret = Command<CMD_LANDSCAPE_CLEAR>::Do(flags, tile);
if (ret.Failed()) return ret;
}
break;
}
case StationType::Buoy:
/* Buoys are always allowed */
break;
default:
break;
}
}
} else {
/* Build a new bridge. */
@ -467,6 +513,45 @@ CommandCost CmdBuildBridge(DoCommandFlags flags, TileIndex tile_end, TileIndex t
break;
}
case MP_STATION: {
switch (GetStationType(tile)) {
case StationType::Airport:
goto not_valid_below;
case StationType::Rail:
case StationType::RailWaypoint: {
CommandCost ret = IsRailStationBridgeAboveOk(tile, GetStationSpec(tile), GetStationGfx(tile), tile_start, tile_end, z_start + 1, bridge_type, transport_type);
if (ret.Failed()) {
if (ret.GetErrorMessage() != INVALID_STRING_ID) return ret;
goto not_valid_below;
}
break;
}
case StationType::Bus:
case StationType::Truck:
case StationType::RoadWaypoint: {
CommandCost ret = IsRoadStopBridgeAboveOK(tile, GetRoadStopSpec(tile), IsDriveThroughStopTile(tile), IsDriveThroughStopTile(tile) ? AxisToDiagDir(GetDriveThroughStopAxis(tile)) : GetBayRoadStopDir(tile),
tile_start, tile_end, z_start + 1, bridge_type, transport_type);
if (ret.Failed()) {
if (ret.GetErrorMessage() != INVALID_STRING_ID) return ret;
goto not_valid_below;
}
break;
}
case StationType::Buoy:
/* Buoys are always allowed */
break;
default:
//if (!(GetStationType(tile) == StationType::Dock && _settings_game.construction.allow_docks_under_bridges)) goto not_valid_below;
break;
}
break;
}
case MP_CLEAR:
break;
@ -1517,6 +1602,49 @@ static BridgePieces CalcBridgePiece(uint north, uint south)
}
}
BridgePiecePillarFlags GetBridgeTilePillarFlags(TileIndex tile, TileIndex northern_bridge_end, TileIndex southern_bridge_end, BridgeType bridge_type, TransportType bridge_transport_type)
{
if (bridge_transport_type == TRANSPORT_WATER) return BridgePiecePillarFlag::BPPF_ALL_CORNERS;
BridgePieces piece = CalcBridgePiece(
GetTunnelBridgeLength(tile, northern_bridge_end) + 1,
GetTunnelBridgeLength(tile, southern_bridge_end) + 1
);
assert(piece < BRIDGE_PIECE_HEAD);
const BridgeSpec *spec = GetBridgeSpec(bridge_type);
const Axis axis = TileX(northern_bridge_end) == TileX(southern_bridge_end) ? AXIS_Y : AXIS_X;
if (!spec->ctrl_flags.Test(BridgeSpecCtrlFlag::BSCF_INVALID_PILLAR_FLAGS)) {
return spec->pillar_flags[piece * 2 + (axis == AXIS_Y ? 1 : 0)];
} else {
uint base_offset;
if (bridge_transport_type == TRANSPORT_RAIL) {
base_offset = GetRailTypeInfo(GetRailType(southern_bridge_end))->bridge_offset;
} else {
base_offset = 8;
}
const PalSpriteID *psid = &GetBridgeSpriteTable(bridge_type, piece)[base_offset];
if (axis == AXIS_Y) psid += 4;
return psid[2].sprite != 0 ? BridgePiecePillarFlag::BPPF_ALL_CORNERS : BridgePiecePillarFlags();
}
}
BridgePieceDebugInfo GetBridgePieceDebugInfo(TileIndex tile)
{
TileIndex rampnorth = GetNorthernBridgeEnd(tile);
TileIndex rampsouth = GetSouthernBridgeEnd(tile);
BridgePieces piece = CalcBridgePiece(
GetTunnelBridgeLength(tile, rampnorth) + 1,
GetTunnelBridgeLength(tile, rampsouth) + 1
);
BridgePiecePillarFlags pillar_flags = GetBridgeTilePillarFlags(tile, rampnorth, rampsouth, GetBridgeType(rampnorth), GetTunnelBridgeTransportType(rampnorth));
const Axis axis = TileX(rampnorth) == TileX(rampsouth) ? AXIS_Y : AXIS_X;
uint pillar_index = piece * 2 + (axis == AXIS_Y ? 1 : 0);
return { piece, pillar_flags, pillar_index };
}
/**
* Draw the middle bits of a bridge.
* @param ti Tile information of the tile to draw it on.

View File

@ -182,7 +182,9 @@ static CommandCost IsValidTileForWaypoint(TileIndex tile, Axis axis, StationID *
extern void GetStationLayout(uint8_t *layout, uint numtracks, uint plat_len, const StationSpec *statspec);
extern CommandCost FindJoiningWaypoint(StationID existing_station, StationID station_to_join, bool adjacent, TileArea ta, Waypoint **wp, bool is_road);
extern CommandCost CanExpandRailStation(const BaseStation *st, TileArea &new_ta);
extern CommandCost CalculateRoadStopCost(TileArea tile_area, DoCommandFlags flags, bool is_drive_through, StationType station_type, Axis axis, DiagDirection ddir, StationID *est, RoadType rt, Money unit_cost);
extern CommandCost CalculateRoadStopCost(TileArea tile_area, DoCommandFlags flags, bool is_drive_through, StationType station_type, const RoadStopSpec *roadstopspec, Axis axis, DiagDirection ddir, StationID *est, RoadType rt, Money unit_cost);
extern CommandCost IsRailStationBridgeAboveOk(TileIndex tile, const StationSpec *statspec, uint8_t layout);
extern CommandCost RemoveRoadWaypointStop(TileIndex tile, DoCommandFlags flags, int replacement_spec_index);
/**
@ -220,6 +222,17 @@ CommandCost CmdBuildRailWaypoint(DoCommandFlags flags, TileIndex start_tile, Axi
if (distant_join && (!_settings_game.station.distant_join_stations || !Waypoint::IsValidID(station_to_join))) return CMD_ERROR;
const StationSpec *spec = StationClass::Get(spec_class)->GetSpec(spec_index);
std::vector<uint8_t> layout_ptr;
layout_ptr.resize(count);
if (spec == nullptr) {
/* The layout must be 0 for the 'normal' waypoints by design. */
//memset(layout_ptr, 0, count); //FIXME
} else {
/* But for NewGRF waypoints we like to have their style. */
GetStationLayout(&layout_ptr[0], count, 1, spec);
}
TileArea new_location(start_tile, width, height);
/* only AddCost for non-existing waypoints */
@ -237,6 +250,10 @@ CommandCost CmdBuildRailWaypoint(DoCommandFlags flags, TileIndex start_tile, Axi
TileIndex tile = start_tile + i * offset;
CommandCost ret = IsValidTileForWaypoint(tile, axis, &est);
if (ret.Failed()) return ret;
ret = IsRailStationBridgeAboveOk(tile, spec, layout_ptr[i]);
if (ret.Failed()) {
return ret;
}
}
Waypoint *wp = nullptr;
@ -364,7 +381,7 @@ CommandCost CmdBuildRoadWaypoint(DoCommandFlags flags, TileIndex start_tile, Axi
unit_cost = _price[PR_BUILD_STATION_TRUCK];
}
StationID est = StationID::Invalid();
CommandCost cost = CalculateRoadStopCost(roadstop_area, flags, true, StationType::RoadWaypoint, axis, AxisToDiagDir(axis), &est, INVALID_ROADTYPE, unit_cost);
CommandCost cost = CalculateRoadStopCost(roadstop_area, flags, true, StationType::RoadWaypoint, roadstopspec, axis, AxisToDiagDir(axis), &est, INVALID_ROADTYPE, unit_cost);
if (cost.Failed()) return cost;
Waypoint *wp = nullptr;