mirror of https://github.com/OpenTTD/OpenTTD
Compare commits
2 Commits
56dd8497e0
...
f1e999ec59
Author | SHA1 | Date |
---|---|---|
|
f1e999ec59 | |
|
9a7c4dda52 |
|
@ -35,6 +35,7 @@
|
|||
#include "string_func.h"
|
||||
#include "thread.h"
|
||||
#include "tgp.h"
|
||||
#include "pathfinder/water_regions.h"
|
||||
|
||||
#include "safeguards.h"
|
||||
|
||||
|
@ -174,6 +175,8 @@ static void _GenerateWorld()
|
|||
}
|
||||
}
|
||||
|
||||
InitializeWaterRegions();
|
||||
|
||||
BasePersistentStorageArray::SwitchMode(PSM_LEAVE_GAMELOOP);
|
||||
|
||||
ResetObjectToPlace();
|
||||
|
|
|
@ -890,7 +890,7 @@ STR_GAME_OPTIONS_CURRENCY_UNITS_FRAME :{BLACK}Geldeenh
|
|||
STR_GAME_OPTIONS_CURRENCY_UNITS_DROPDOWN_TOOLTIP :{BLACK}Geld eendheid keuse
|
||||
|
||||
|
||||
###length 42
|
||||
###length 43
|
||||
STR_GAME_OPTIONS_CURRENCY_GBP :Britse Pond
|
||||
STR_GAME_OPTIONS_CURRENCY_USD :Amerikaanse Dollar
|
||||
STR_GAME_OPTIONS_CURRENCY_EUR :Euro
|
||||
|
|
|
@ -884,7 +884,7 @@ STR_GAME_OPTIONS_CURRENCY_UNITS_FRAME :{BLACK}وحدة
|
|||
STR_GAME_OPTIONS_CURRENCY_UNITS_DROPDOWN_TOOLTIP :{BLACK}اختيار وحدة العملة
|
||||
|
||||
|
||||
###length 42
|
||||
###length 43
|
||||
STR_GAME_OPTIONS_CURRENCY_GBP :جنية
|
||||
STR_GAME_OPTIONS_CURRENCY_USD :دولار
|
||||
STR_GAME_OPTIONS_CURRENCY_EUR :يورو
|
||||
|
|
|
@ -866,7 +866,7 @@ STR_GAME_OPTIONS_CURRENCY_UNITS_FRAME :{BLACK}Diru uni
|
|||
STR_GAME_OPTIONS_CURRENCY_UNITS_DROPDOWN_TOOLTIP :{BLACK}Diru unitatearen aukeraketa
|
||||
|
||||
|
||||
###length 42
|
||||
###length 43
|
||||
STR_GAME_OPTIONS_CURRENCY_GBP :Libra
|
||||
STR_GAME_OPTIONS_CURRENCY_USD :Dolar Amerikarrak
|
||||
STR_GAME_OPTIONS_CURRENCY_EUR :Euroa
|
||||
|
|
|
@ -1199,7 +1199,7 @@ STR_GAME_OPTIONS_CURRENCY_UNITS_FRAME :{BLACK}Валю
|
|||
STR_GAME_OPTIONS_CURRENCY_UNITS_DROPDOWN_TOOLTIP :{BLACK}Выбар валюты
|
||||
|
||||
|
||||
###length 42
|
||||
###length 43
|
||||
STR_GAME_OPTIONS_CURRENCY_GBP :Брытанскі фунт
|
||||
STR_GAME_OPTIONS_CURRENCY_USD :Даляр ЗША
|
||||
STR_GAME_OPTIONS_CURRENCY_EUR :Эўра
|
||||
|
|
|
@ -960,7 +960,7 @@ STR_GAME_OPTIONS_CURRENCY_UNITS_DROPDOWN_TOOLTIP :{BLACK}Seleçã
|
|||
|
||||
STR_GAME_OPTIONS_CURRENCY_CODE :{STRING} ({STRING})
|
||||
|
||||
###length 42
|
||||
###length 43
|
||||
STR_GAME_OPTIONS_CURRENCY_GBP :Libras Britânicas
|
||||
STR_GAME_OPTIONS_CURRENCY_USD :Dólares Norte Americanos
|
||||
STR_GAME_OPTIONS_CURRENCY_EUR :Euro
|
||||
|
|
|
@ -872,7 +872,7 @@ STR_GAME_OPTIONS_CURRENCY_UNITS_FRAME :{BLACK}Пари
|
|||
STR_GAME_OPTIONS_CURRENCY_UNITS_DROPDOWN_TOOLTIP :{BLACK}Избор на парична единица
|
||||
|
||||
|
||||
###length 42
|
||||
###length 43
|
||||
STR_GAME_OPTIONS_CURRENCY_GBP :Британска лира
|
||||
STR_GAME_OPTIONS_CURRENCY_USD :Американски долар
|
||||
STR_GAME_OPTIONS_CURRENCY_EUR :Евро
|
||||
|
|
|
@ -960,7 +960,7 @@ STR_GAME_OPTIONS_CURRENCY_UNITS_DROPDOWN_TOOLTIP :{BLACK}Selecci
|
|||
|
||||
STR_GAME_OPTIONS_CURRENCY_CODE :{STRING} ({STRING})
|
||||
|
||||
###length 42
|
||||
###length 43
|
||||
STR_GAME_OPTIONS_CURRENCY_GBP :Lliura esterlina
|
||||
STR_GAME_OPTIONS_CURRENCY_USD :Dòlar americà
|
||||
STR_GAME_OPTIONS_CURRENCY_EUR :Euro
|
||||
|
|
|
@ -474,7 +474,7 @@ STR_NEWS_MESSAGE_CAPTION :{WHITE}Пӗлт
|
|||
|
||||
|
||||
|
||||
###length 42
|
||||
###length 43
|
||||
STR_GAME_OPTIONS_CURRENCY_RUR :Вырӑсла тенкӗ
|
||||
|
||||
STR_GAME_OPTIONS_AUTOSAVE_FRAME :{BLACK}Хӑй управ
|
||||
|
|
|
@ -989,7 +989,7 @@ STR_GAME_OPTIONS_CURRENCY_UNITS_FRAME :{BLACK}Novčane
|
|||
STR_GAME_OPTIONS_CURRENCY_UNITS_DROPDOWN_TOOLTIP :{BLACK}Izbor novčanih jedinica
|
||||
|
||||
|
||||
###length 42
|
||||
###length 43
|
||||
STR_GAME_OPTIONS_CURRENCY_GBP :Funte
|
||||
STR_GAME_OPTIONS_CURRENCY_USD :Američki Dolar
|
||||
STR_GAME_OPTIONS_CURRENCY_EUR :Euro
|
||||
|
|
|
@ -1019,7 +1019,7 @@ STR_GAME_OPTIONS_CURRENCY_UNITS_FRAME :{BLACK}Měna
|
|||
STR_GAME_OPTIONS_CURRENCY_UNITS_DROPDOWN_TOOLTIP :{BLACK}Výběr jednotek měny
|
||||
|
||||
|
||||
###length 42
|
||||
###length 43
|
||||
STR_GAME_OPTIONS_CURRENCY_GBP :Libra
|
||||
STR_GAME_OPTIONS_CURRENCY_USD :Dolar
|
||||
STR_GAME_OPTIONS_CURRENCY_EUR :Euro
|
||||
|
|
|
@ -959,7 +959,7 @@ STR_GAME_OPTIONS_CURRENCY_UNITS_DROPDOWN_TOOLTIP :{BLACK}Valg af
|
|||
|
||||
STR_GAME_OPTIONS_CURRENCY_CODE :{STRING} ({STRING})
|
||||
|
||||
###length 42
|
||||
###length 43
|
||||
STR_GAME_OPTIONS_CURRENCY_GBP :Britiske Pund
|
||||
STR_GAME_OPTIONS_CURRENCY_USD :Amerikanske dollars
|
||||
STR_GAME_OPTIONS_CURRENCY_EUR :Euro
|
||||
|
|
|
@ -959,7 +959,7 @@ STR_GAME_OPTIONS_CURRENCY_UNITS_DROPDOWN_TOOLTIP :{BLACK}Valuta k
|
|||
|
||||
STR_GAME_OPTIONS_CURRENCY_CODE :{STRING} ({STRING})
|
||||
|
||||
###length 42
|
||||
###length 43
|
||||
STR_GAME_OPTIONS_CURRENCY_GBP :Britse Pond
|
||||
STR_GAME_OPTIONS_CURRENCY_USD :Dollar
|
||||
STR_GAME_OPTIONS_CURRENCY_EUR :Euro
|
||||
|
|
|
@ -959,7 +959,7 @@ STR_GAME_OPTIONS_CURRENCY_UNITS_DROPDOWN_TOOLTIP :{BLACK}Currency
|
|||
|
||||
STR_GAME_OPTIONS_CURRENCY_CODE :{STRING} ({STRING})
|
||||
|
||||
###length 42
|
||||
###length 43
|
||||
STR_GAME_OPTIONS_CURRENCY_GBP :British Pound
|
||||
STR_GAME_OPTIONS_CURRENCY_USD :American Dollar
|
||||
STR_GAME_OPTIONS_CURRENCY_EUR :Euro
|
||||
|
@ -2055,14 +2055,17 @@ STR_CONFIG_SETTING_ACCOUNTING :Accounting
|
|||
STR_CONFIG_SETTING_VEHICLES :Vehicles
|
||||
STR_CONFIG_SETTING_VEHICLES_PHYSICS :Physics
|
||||
STR_CONFIG_SETTING_VEHICLES_ROUTING :Routing
|
||||
STR_CONFIG_SETTING_VEHICLES_ORDERS :Orders
|
||||
STR_CONFIG_SETTING_LIMITATIONS :Limitations
|
||||
STR_CONFIG_SETTING_ACCIDENTS :Disasters / Accidents
|
||||
STR_CONFIG_SETTING_GENWORLD :World generation
|
||||
STR_CONFIG_SETTING_ENVIRONMENT :Environment
|
||||
STR_CONFIG_SETTING_ENVIRONMENT_TIME :Time
|
||||
STR_CONFIG_SETTING_ENVIRONMENT_AUTHORITIES :Authorities
|
||||
STR_CONFIG_SETTING_ENVIRONMENT_TOWNS :Towns
|
||||
STR_CONFIG_SETTING_ENVIRONMENT_INDUSTRIES :Industries
|
||||
STR_CONFIG_SETTING_ENVIRONMENT_CARGODIST :Cargo distribution
|
||||
STR_CONFIG_SETTING_ENVIRONMENT_TREES :Trees
|
||||
STR_CONFIG_SETTING_AI :Competitors
|
||||
STR_CONFIG_SETTING_AI_NPC :Computer players
|
||||
STR_CONFIG_SETTING_NETWORK :Network
|
||||
|
|
|
@ -959,7 +959,7 @@ STR_GAME_OPTIONS_CURRENCY_UNITS_DROPDOWN_TOOLTIP :{BLACK}Currency
|
|||
|
||||
STR_GAME_OPTIONS_CURRENCY_CODE :{STRING} ({STRING})
|
||||
|
||||
###length 42
|
||||
###length 43
|
||||
STR_GAME_OPTIONS_CURRENCY_GBP :British Pound
|
||||
STR_GAME_OPTIONS_CURRENCY_USD :American Dollar
|
||||
STR_GAME_OPTIONS_CURRENCY_EUR :Euro
|
||||
|
@ -2055,14 +2055,17 @@ STR_CONFIG_SETTING_ACCOUNTING :Accounting
|
|||
STR_CONFIG_SETTING_VEHICLES :Vehicles
|
||||
STR_CONFIG_SETTING_VEHICLES_PHYSICS :Physics
|
||||
STR_CONFIG_SETTING_VEHICLES_ROUTING :Routing
|
||||
STR_CONFIG_SETTING_VEHICLES_ORDERS :Orders
|
||||
STR_CONFIG_SETTING_LIMITATIONS :Limitations
|
||||
STR_CONFIG_SETTING_ACCIDENTS :Disasters / Accidents
|
||||
STR_CONFIG_SETTING_GENWORLD :World generation
|
||||
STR_CONFIG_SETTING_ENVIRONMENT :Environment
|
||||
STR_CONFIG_SETTING_ENVIRONMENT_TIME :Time
|
||||
STR_CONFIG_SETTING_ENVIRONMENT_AUTHORITIES :Authorities
|
||||
STR_CONFIG_SETTING_ENVIRONMENT_TOWNS :Towns
|
||||
STR_CONFIG_SETTING_ENVIRONMENT_INDUSTRIES :Industries
|
||||
STR_CONFIG_SETTING_ENVIRONMENT_CARGODIST :Cargo distribution
|
||||
STR_CONFIG_SETTING_ENVIRONMENT_TREES :Trees
|
||||
STR_CONFIG_SETTING_AI :Competitors
|
||||
STR_CONFIG_SETTING_AI_NPC :Computer players
|
||||
STR_CONFIG_SETTING_NETWORK :Network
|
||||
|
|
|
@ -1009,7 +1009,7 @@ STR_GAME_OPTIONS_CURRENCY_UNITS_FRAME :{BLACK}Monunuoj
|
|||
STR_GAME_OPTIONS_CURRENCY_UNITS_DROPDOWN_TOOLTIP :{BLACK}Elekto de monunuoj
|
||||
|
||||
|
||||
###length 42
|
||||
###length 43
|
||||
STR_GAME_OPTIONS_CURRENCY_GBP :Brita Pundo
|
||||
STR_GAME_OPTIONS_CURRENCY_USD :Usona Dolaro
|
||||
STR_GAME_OPTIONS_CURRENCY_EUR :Eŭro
|
||||
|
|
|
@ -975,7 +975,7 @@ STR_GAME_OPTIONS_CURRENCY_UNITS_FRAME :{BLACK}Valuuta
|
|||
STR_GAME_OPTIONS_CURRENCY_UNITS_DROPDOWN_TOOLTIP :{BLACK}Valuuta valimine
|
||||
|
||||
|
||||
###length 42
|
||||
###length 43
|
||||
STR_GAME_OPTIONS_CURRENCY_GBP :Suurbritannia nael
|
||||
STR_GAME_OPTIONS_CURRENCY_USD :USA dollar
|
||||
STR_GAME_OPTIONS_CURRENCY_EUR :Euro
|
||||
|
|
|
@ -846,7 +846,7 @@ STR_GAME_OPTIONS_CURRENCY_UNITS_FRAME :{BLACK}Gjaldsoy
|
|||
STR_GAME_OPTIONS_CURRENCY_UNITS_DROPDOWN_TOOLTIP :{BLACK}Úrval av gjaldsoyra eindum
|
||||
|
||||
|
||||
###length 42
|
||||
###length 43
|
||||
STR_GAME_OPTIONS_CURRENCY_GBP :Bretsk Pund
|
||||
STR_GAME_OPTIONS_CURRENCY_USD :Dollarar
|
||||
STR_GAME_OPTIONS_CURRENCY_EUR :Evrir
|
||||
|
|
|
@ -959,7 +959,7 @@ STR_GAME_OPTIONS_CURRENCY_UNITS_DROPDOWN_TOOLTIP :{BLACK}Rahayksi
|
|||
|
||||
STR_GAME_OPTIONS_CURRENCY_CODE :{STRING} ({STRING})
|
||||
|
||||
###length 42
|
||||
###length 43
|
||||
STR_GAME_OPTIONS_CURRENCY_GBP :Englannin punta
|
||||
STR_GAME_OPTIONS_CURRENCY_USD :Yhdysvaltain dollari
|
||||
STR_GAME_OPTIONS_CURRENCY_EUR :Euro
|
||||
|
@ -2055,14 +2055,17 @@ STR_CONFIG_SETTING_ACCOUNTING :Talous
|
|||
STR_CONFIG_SETTING_VEHICLES :Kulkuneuvot
|
||||
STR_CONFIG_SETTING_VEHICLES_PHYSICS :Fysiikka
|
||||
STR_CONFIG_SETTING_VEHICLES_ROUTING :Reititys
|
||||
STR_CONFIG_SETTING_VEHICLES_ORDERS :Käskyt
|
||||
STR_CONFIG_SETTING_LIMITATIONS :Rajoitukset
|
||||
STR_CONFIG_SETTING_ACCIDENTS :Onnettomuudet
|
||||
STR_CONFIG_SETTING_GENWORLD :Maailman luominen
|
||||
STR_CONFIG_SETTING_ENVIRONMENT :Ympäristö
|
||||
STR_CONFIG_SETTING_ENVIRONMENT_TIME :Aika
|
||||
STR_CONFIG_SETTING_ENVIRONMENT_AUTHORITIES :Viranomaiset
|
||||
STR_CONFIG_SETTING_ENVIRONMENT_TOWNS :Kunnat
|
||||
STR_CONFIG_SETTING_ENVIRONMENT_INDUSTRIES :Laitokset
|
||||
STR_CONFIG_SETTING_ENVIRONMENT_CARGODIST :Rahdin jakautuminen
|
||||
STR_CONFIG_SETTING_ENVIRONMENT_TREES :Puut
|
||||
STR_CONFIG_SETTING_AI :Kilpailijat
|
||||
STR_CONFIG_SETTING_AI_NPC :Tietokonepelaajat
|
||||
STR_CONFIG_SETTING_NETWORK :Verkko
|
||||
|
|
|
@ -960,7 +960,7 @@ STR_GAME_OPTIONS_CURRENCY_UNITS_DROPDOWN_TOOLTIP :{BLACK}Sélecti
|
|||
|
||||
STR_GAME_OPTIONS_CURRENCY_CODE :{STRING} ({STRING})
|
||||
|
||||
###length 42
|
||||
###length 43
|
||||
STR_GAME_OPTIONS_CURRENCY_GBP :Livre
|
||||
STR_GAME_OPTIONS_CURRENCY_USD :Dollar
|
||||
STR_GAME_OPTIONS_CURRENCY_EUR :Euro
|
||||
|
@ -1778,6 +1778,8 @@ STR_CONFIG_SETTING_SERVINT_DISABLED :Désactivé
|
|||
STR_CONFIG_SETTING_NOSERVICE :Désactiver l'entretien quand les pannes sont inactives{NBSP}: {STRING}
|
||||
STR_CONFIG_SETTING_NOSERVICE_HELPTEXT :Lorsqu'il est activé, les véhicules ne sont pas entretenus s'ils ne peuvent pas tomber en panne
|
||||
|
||||
STR_CONFIG_SETTING_STATION_LENGTH_LOADING_PENALTY :Pénalité de vitesse de chargement pour les trains plus long que les stations: {STRING}
|
||||
STR_CONFIG_SETTING_STATION_LENGTH_LOADING_PENALTY_HELPTEXT :Quand activé, un train qui est trop long pour une station se charge plus lentement qu'un train qui fait la même taille que la station. Ce paramètre n'affecte pas la recherche de chemin.
|
||||
|
||||
STR_CONFIG_SETTING_WAGONSPEEDLIMITS :Activer la vitesse limite des wagons{NBSP}: {STRING}
|
||||
STR_CONFIG_SETTING_WAGONSPEEDLIMITS_HELPTEXT :Lorsqu'il est activé, utiliser la vitesse limite des wagons pour déterminer la vitesse maximum d'un train
|
||||
|
@ -2054,14 +2056,17 @@ STR_CONFIG_SETTING_ACCOUNTING :Comptabilité
|
|||
STR_CONFIG_SETTING_VEHICLES :Véhicules
|
||||
STR_CONFIG_SETTING_VEHICLES_PHYSICS :Physique
|
||||
STR_CONFIG_SETTING_VEHICLES_ROUTING :Routage
|
||||
STR_CONFIG_SETTING_VEHICLES_ORDERS :Ordres
|
||||
STR_CONFIG_SETTING_LIMITATIONS :Limitations
|
||||
STR_CONFIG_SETTING_ACCIDENTS :Catastrophes / Accidents
|
||||
STR_CONFIG_SETTING_GENWORLD :Création du terrain
|
||||
STR_CONFIG_SETTING_ENVIRONMENT :Environnement
|
||||
STR_CONFIG_SETTING_ENVIRONMENT_TIME :Temps
|
||||
STR_CONFIG_SETTING_ENVIRONMENT_AUTHORITIES :Autorités
|
||||
STR_CONFIG_SETTING_ENVIRONMENT_TOWNS :Villes
|
||||
STR_CONFIG_SETTING_ENVIRONMENT_INDUSTRIES :Industries
|
||||
STR_CONFIG_SETTING_ENVIRONMENT_CARGODIST :Distribution des marchandises
|
||||
STR_CONFIG_SETTING_ENVIRONMENT_TREES :Arbres
|
||||
STR_CONFIG_SETTING_AI :Compétiteurs
|
||||
STR_CONFIG_SETTING_AI_NPC :Intelligence artificielle
|
||||
STR_CONFIG_SETTING_NETWORK :Réseau
|
||||
|
|
|
@ -883,7 +883,7 @@ STR_GAME_OPTIONS_CURRENCY_UNITS_FRAME :{BLACK}Muntienh
|
|||
STR_GAME_OPTIONS_CURRENCY_UNITS_DROPDOWN_TOOLTIP :{BLACK}Muntienheidseleksje
|
||||
|
||||
|
||||
###length 42
|
||||
###length 43
|
||||
STR_GAME_OPTIONS_CURRENCY_GBP :Britske Pûn
|
||||
STR_GAME_OPTIONS_CURRENCY_USD :Amerikaanske Dollars
|
||||
STR_GAME_OPTIONS_CURRENCY_EUR :Euro
|
||||
|
|
|
@ -1079,7 +1079,7 @@ STR_GAME_OPTIONS_CURRENCY_UNITS_FRAME :{BLACK}Aonadan
|
|||
STR_GAME_OPTIONS_CURRENCY_UNITS_DROPDOWN_TOOLTIP :{BLACK}Tagh aonadan airgeadra
|
||||
|
||||
|
||||
###length 42
|
||||
###length 43
|
||||
STR_GAME_OPTIONS_CURRENCY_GBP :Not Breatannach
|
||||
STR_GAME_OPTIONS_CURRENCY_USD :Dolar Aimeireaganach
|
||||
STR_GAME_OPTIONS_CURRENCY_EUR :Eòro
|
||||
|
|
|
@ -957,7 +957,7 @@ STR_GAME_OPTIONS_CURRENCY_UNITS_DROPDOWN_TOOLTIP :{BLACK}Selecci
|
|||
|
||||
STR_GAME_OPTIONS_CURRENCY_CODE :{STRING} ({STRING})
|
||||
|
||||
###length 42
|
||||
###length 43
|
||||
STR_GAME_OPTIONS_CURRENCY_GBP :Libra esterlina
|
||||
STR_GAME_OPTIONS_CURRENCY_USD :Dólar americano
|
||||
STR_GAME_OPTIONS_CURRENCY_EUR :Euro
|
||||
|
|
|
@ -956,7 +956,7 @@ STR_GAME_OPTIONS_CURRENCY_UNITS_DROPDOWN_TOOLTIP :{BLACK}Währung
|
|||
|
||||
STR_GAME_OPTIONS_CURRENCY_CODE :{STRING} ({STRING})
|
||||
|
||||
###length 42
|
||||
###length 43
|
||||
STR_GAME_OPTIONS_CURRENCY_GBP :Britische Pfund
|
||||
STR_GAME_OPTIONS_CURRENCY_USD :US-Dollar
|
||||
STR_GAME_OPTIONS_CURRENCY_EUR :Euro
|
||||
|
|
|
@ -1024,7 +1024,7 @@ STR_GAME_OPTIONS_CURRENCY_UNITS_FRAME :{BLACK}Νομι
|
|||
STR_GAME_OPTIONS_CURRENCY_UNITS_DROPDOWN_TOOLTIP :{BLACK}Επιλογή νομισματικών μονάδων
|
||||
|
||||
|
||||
###length 42
|
||||
###length 43
|
||||
STR_GAME_OPTIONS_CURRENCY_GBP :Στερλίνα Ηνωμένου Βασιλείου
|
||||
STR_GAME_OPTIONS_CURRENCY_USD :Δολλάριο Η.Π.Α.
|
||||
STR_GAME_OPTIONS_CURRENCY_EUR :Ευρώ
|
||||
|
|
|
@ -895,7 +895,7 @@ STR_GAME_OPTIONS_CURRENCY_UNITS_FRAME :{BLACK}מטבע
|
|||
STR_GAME_OPTIONS_CURRENCY_UNITS_DROPDOWN_TOOLTIP :{BLACK}בחירת מטבע
|
||||
|
||||
|
||||
###length 42
|
||||
###length 43
|
||||
STR_GAME_OPTIONS_CURRENCY_GBP :לירה שטרלינג
|
||||
STR_GAME_OPTIONS_CURRENCY_USD :דולר אמריקאי
|
||||
STR_GAME_OPTIONS_CURRENCY_EUR :יורו
|
||||
|
|
|
@ -277,7 +277,7 @@ STR_EXTRA_VIEW_MOVE_MAIN_TO_VIEW_TT :{BLACK}इस
|
|||
|
||||
|
||||
|
||||
###length 42
|
||||
###length 43
|
||||
STR_GAME_OPTIONS_CURRENCY_FIM :फिनलैंड मार्का
|
||||
STR_GAME_OPTIONS_CURRENCY_ISK :आइसलैंडिक क्रोना
|
||||
STR_GAME_OPTIONS_CURRENCY_HKD :हाँग काँग डॉलर
|
||||
|
|
|
@ -1019,7 +1019,7 @@ STR_GAME_OPTIONS_CURRENCY_UNITS_DROPDOWN_TOOLTIP :{BLACK}A haszn
|
|||
|
||||
STR_GAME_OPTIONS_CURRENCY_CODE :{STRING} ({STRING})
|
||||
|
||||
###length 42
|
||||
###length 43
|
||||
STR_GAME_OPTIONS_CURRENCY_GBP :Angol Font
|
||||
STR_GAME_OPTIONS_CURRENCY_USD :Amerikai Dollár
|
||||
STR_GAME_OPTIONS_CURRENCY_EUR :Euró
|
||||
|
|
|
@ -845,7 +845,7 @@ STR_GAME_OPTIONS_CURRENCY_UNITS_FRAME :{BLACK}Gjaldmi
|
|||
STR_GAME_OPTIONS_CURRENCY_UNITS_DROPDOWN_TOOLTIP :{BLACK}Val á gjaldmiðli
|
||||
|
||||
|
||||
###length 42
|
||||
###length 43
|
||||
STR_GAME_OPTIONS_CURRENCY_GBP :Sterlingspund
|
||||
STR_GAME_OPTIONS_CURRENCY_USD :Bandaríkjadalur
|
||||
STR_GAME_OPTIONS_CURRENCY_EUR :Evra
|
||||
|
|
|
@ -464,7 +464,7 @@ STR_GAME_OPTIONS_CAPTION :{WHITE}Ludo Sel
|
|||
|
||||
|
||||
|
||||
###length 42
|
||||
###length 43
|
||||
|
||||
|
||||
# Autosave dropdown
|
||||
|
|
|
@ -955,7 +955,7 @@ STR_GAME_OPTIONS_CURRENCY_UNITS_DROPDOWN_TOOLTIP :{BLACK}Pilih ma
|
|||
|
||||
STR_GAME_OPTIONS_CURRENCY_CODE :{STRING} ({STRING})
|
||||
|
||||
###length 42
|
||||
###length 43
|
||||
STR_GAME_OPTIONS_CURRENCY_GBP :Poundsterling
|
||||
STR_GAME_OPTIONS_CURRENCY_USD :Dollar
|
||||
STR_GAME_OPTIONS_CURRENCY_EUR :Euro
|
||||
|
|
|
@ -907,7 +907,7 @@ STR_GAME_OPTIONS_CURRENCY_UNITS_FRAME :{BLACK}Aonaid a
|
|||
STR_GAME_OPTIONS_CURRENCY_UNITS_DROPDOWN_TOOLTIP :{BLACK}Aonaid airgeadra a roghnú
|
||||
|
||||
|
||||
###length 42
|
||||
###length 43
|
||||
STR_GAME_OPTIONS_CURRENCY_GBP :Punt na Breataine
|
||||
STR_GAME_OPTIONS_CURRENCY_USD :Dollar Mheiriceá
|
||||
STR_GAME_OPTIONS_CURRENCY_EUR :Euro
|
||||
|
|
|
@ -961,7 +961,7 @@ STR_GAME_OPTIONS_CURRENCY_UNITS_DROPDOWN_TOOLTIP :{BLACK}Selezion
|
|||
|
||||
STR_GAME_OPTIONS_CURRENCY_CODE :{STRING} ({STRING})
|
||||
|
||||
###length 42
|
||||
###length 43
|
||||
STR_GAME_OPTIONS_CURRENCY_GBP :Sterlina britannica
|
||||
STR_GAME_OPTIONS_CURRENCY_USD :Dollaro americano
|
||||
STR_GAME_OPTIONS_CURRENCY_EUR :Euro
|
||||
|
|
|
@ -933,7 +933,7 @@ STR_GAME_OPTIONS_CURRENCY_UNITS_FRAME :{BLACK}通貨
|
|||
STR_GAME_OPTIONS_CURRENCY_UNITS_DROPDOWN_TOOLTIP :{BLACK}通貨単位の選択
|
||||
|
||||
|
||||
###length 42
|
||||
###length 43
|
||||
STR_GAME_OPTIONS_CURRENCY_GBP :英ポンド
|
||||
STR_GAME_OPTIONS_CURRENCY_USD :米ドル
|
||||
STR_GAME_OPTIONS_CURRENCY_EUR :欧州連合・ユーロ
|
||||
|
|
|
@ -960,7 +960,7 @@ STR_GAME_OPTIONS_CURRENCY_UNITS_DROPDOWN_TOOLTIP :{BLACK}화폐
|
|||
|
||||
STR_GAME_OPTIONS_CURRENCY_CODE :{STRING} ({STRING})
|
||||
|
||||
###length 42
|
||||
###length 43
|
||||
STR_GAME_OPTIONS_CURRENCY_GBP :영국 파운드
|
||||
STR_GAME_OPTIONS_CURRENCY_USD :미국 달러
|
||||
STR_GAME_OPTIONS_CURRENCY_EUR :유로
|
||||
|
@ -2056,14 +2056,17 @@ STR_CONFIG_SETTING_ACCOUNTING :회계
|
|||
STR_CONFIG_SETTING_VEHICLES :차량
|
||||
STR_CONFIG_SETTING_VEHICLES_PHYSICS :물리
|
||||
STR_CONFIG_SETTING_VEHICLES_ROUTING :경로
|
||||
STR_CONFIG_SETTING_VEHICLES_ORDERS :경로
|
||||
STR_CONFIG_SETTING_LIMITATIONS :제한
|
||||
STR_CONFIG_SETTING_ACCIDENTS :재앙 / 사고
|
||||
STR_CONFIG_SETTING_GENWORLD :세계 제작
|
||||
STR_CONFIG_SETTING_ENVIRONMENT :환경
|
||||
STR_CONFIG_SETTING_ENVIRONMENT_TIME :시간
|
||||
STR_CONFIG_SETTING_ENVIRONMENT_AUTHORITIES :지역 당국
|
||||
STR_CONFIG_SETTING_ENVIRONMENT_TOWNS :도시
|
||||
STR_CONFIG_SETTING_ENVIRONMENT_INDUSTRIES :산업시설
|
||||
STR_CONFIG_SETTING_ENVIRONMENT_CARGODIST :화물 분배
|
||||
STR_CONFIG_SETTING_ENVIRONMENT_TREES :나무
|
||||
STR_CONFIG_SETTING_AI :경쟁자
|
||||
STR_CONFIG_SETTING_AI_NPC :컴퓨터 플레이어
|
||||
STR_CONFIG_SETTING_NETWORK :네트워크
|
||||
|
|
|
@ -1070,7 +1070,7 @@ STR_GAME_OPTIONS_CURRENCY_UNITS_FRAME :{BLACK}Unitates
|
|||
STR_GAME_OPTIONS_CURRENCY_UNITS_DROPDOWN_TOOLTIP :{BLACK}Electio unitatum nummi
|
||||
|
||||
|
||||
###length 42
|
||||
###length 43
|
||||
STR_GAME_OPTIONS_CURRENCY_GBP :Libra Britannica
|
||||
STR_GAME_OPTIONS_CURRENCY_USD :Dollarium CFA
|
||||
STR_GAME_OPTIONS_CURRENCY_EUR :Euro
|
||||
|
|
|
@ -961,7 +961,7 @@ STR_GAME_OPTIONS_CURRENCY_UNITS_DROPDOWN_TOOLTIP :{BLACK}Naudas v
|
|||
|
||||
STR_GAME_OPTIONS_CURRENCY_CODE :{STRING} ({STRING})
|
||||
|
||||
###length 42
|
||||
###length 43
|
||||
STR_GAME_OPTIONS_CURRENCY_GBP :Lielbritānijas sterliņu mārciņas
|
||||
STR_GAME_OPTIONS_CURRENCY_USD :ASV dolāri
|
||||
STR_GAME_OPTIONS_CURRENCY_EUR :Eiro
|
||||
|
|
|
@ -1118,7 +1118,7 @@ STR_GAME_OPTIONS_CURRENCY_UNITS_FRAME :{BLACK}Valiuta
|
|||
STR_GAME_OPTIONS_CURRENCY_UNITS_DROPDOWN_TOOLTIP :{BLACK}Valiutos pasirinkimas
|
||||
|
||||
|
||||
###length 42
|
||||
###length 43
|
||||
STR_GAME_OPTIONS_CURRENCY_GBP :Svarai sterlingų
|
||||
STR_GAME_OPTIONS_CURRENCY_USD :Amerikos Doleris
|
||||
STR_GAME_OPTIONS_CURRENCY_EUR :Eurai
|
||||
|
|
|
@ -918,7 +918,7 @@ STR_GAME_OPTIONS_CURRENCY_UNITS_FRAME :{BLACK}Währung
|
|||
STR_GAME_OPTIONS_CURRENCY_UNITS_DROPDOWN_TOOLTIP :{BLACK}Währungseenheet wielen
|
||||
|
||||
|
||||
###length 42
|
||||
###length 43
|
||||
STR_GAME_OPTIONS_CURRENCY_GBP :Pond
|
||||
STR_GAME_OPTIONS_CURRENCY_USD :Dollar
|
||||
STR_GAME_OPTIONS_CURRENCY_EUR :Euro
|
||||
|
|
|
@ -780,7 +780,7 @@ STR_GAME_OPTIONS_CAPTION :{WHITE}Опци
|
|||
|
||||
|
||||
|
||||
###length 42
|
||||
###length 43
|
||||
|
||||
|
||||
# Autosave dropdown
|
||||
|
|
|
@ -850,7 +850,7 @@ STR_GAME_OPTIONS_CURRENCY_UNITS_FRAME :{BLACK}Unit kew
|
|||
STR_GAME_OPTIONS_CURRENCY_UNITS_DROPDOWN_TOOLTIP :{BLACK}Pilihan unit kewangan
|
||||
|
||||
|
||||
###length 42
|
||||
###length 43
|
||||
STR_GAME_OPTIONS_CURRENCY_GBP :Paun British
|
||||
STR_GAME_OPTIONS_CURRENCY_USD :Dolar Amerika
|
||||
STR_GAME_OPTIONS_CURRENCY_EUR :Euro
|
||||
|
|
|
@ -397,7 +397,7 @@ STR_NEWS_VEHICLE_IS_LOST :{WHITE}{VEHICLE
|
|||
|
||||
|
||||
|
||||
###length 42
|
||||
###length 43
|
||||
|
||||
|
||||
# Autosave dropdown
|
||||
|
|
|
@ -688,7 +688,7 @@ STR_NEWS_NEW_VEHICLE_TYPE :{BIG_FONT}{BLAC
|
|||
|
||||
|
||||
|
||||
###length 42
|
||||
###length 43
|
||||
STR_GAME_OPTIONS_CURRENCY_GBP :पौंड
|
||||
STR_GAME_OPTIONS_CURRENCY_USD :डोल्लर
|
||||
STR_GAME_OPTIONS_CURRENCY_EUR :युरो
|
||||
|
|
|
@ -911,7 +911,7 @@ STR_GAME_OPTIONS_CURRENCY_UNITS_FRAME :{BLACK}Valutaen
|
|||
STR_GAME_OPTIONS_CURRENCY_UNITS_DROPDOWN_TOOLTIP :{BLACK}Velg valutaenhet som skal brukes
|
||||
|
||||
|
||||
###length 42
|
||||
###length 43
|
||||
STR_GAME_OPTIONS_CURRENCY_GBP :Britisk pund
|
||||
STR_GAME_OPTIONS_CURRENCY_USD :Amerikansk dollar
|
||||
STR_GAME_OPTIONS_CURRENCY_EUR :Euro
|
||||
|
|
|
@ -876,7 +876,7 @@ STR_GAME_OPTIONS_CURRENCY_UNITS_FRAME :{BLACK}Valutaei
|
|||
STR_GAME_OPTIONS_CURRENCY_UNITS_DROPDOWN_TOOLTIP :{BLACK}Vel valutaeining
|
||||
|
||||
|
||||
###length 42
|
||||
###length 43
|
||||
STR_GAME_OPTIONS_CURRENCY_GBP :Pund
|
||||
STR_GAME_OPTIONS_CURRENCY_USD :Dollar
|
||||
STR_GAME_OPTIONS_CURRENCY_EUR :Euro
|
||||
|
|
|
@ -866,7 +866,7 @@ STR_GAME_OPTIONS_CURRENCY_UNITS_FRAME :{BLACK}واحد
|
|||
STR_GAME_OPTIONS_CURRENCY_UNITS_DROPDOWN_TOOLTIP :{BLACK}انتخب واحد پول
|
||||
|
||||
|
||||
###length 42
|
||||
###length 43
|
||||
STR_GAME_OPTIONS_CURRENCY_GBP :پوند
|
||||
STR_GAME_OPTIONS_CURRENCY_USD :دلار
|
||||
STR_GAME_OPTIONS_CURRENCY_EUR :یورو
|
||||
|
|
|
@ -1339,7 +1339,7 @@ STR_GAME_OPTIONS_CURRENCY_UNITS_DROPDOWN_TOOLTIP :{BLACK}Wybór w
|
|||
|
||||
STR_GAME_OPTIONS_CURRENCY_CODE :{STRING} ({STRING})
|
||||
|
||||
###length 42
|
||||
###length 43
|
||||
STR_GAME_OPTIONS_CURRENCY_GBP :Funt brytyjski
|
||||
STR_GAME_OPTIONS_CURRENCY_USD :Dolar amerykański
|
||||
STR_GAME_OPTIONS_CURRENCY_EUR :Euro
|
||||
|
|
|
@ -960,7 +960,7 @@ STR_GAME_OPTIONS_CURRENCY_UNITS_DROPDOWN_TOOLTIP :{BLACK}Selecç
|
|||
|
||||
STR_GAME_OPTIONS_CURRENCY_CODE :{STRING} ({STRING})
|
||||
|
||||
###length 42
|
||||
###length 43
|
||||
STR_GAME_OPTIONS_CURRENCY_GBP :Libra Britânica
|
||||
STR_GAME_OPTIONS_CURRENCY_USD :Dólar dos E.U.A.
|
||||
STR_GAME_OPTIONS_CURRENCY_EUR :Euro
|
||||
|
|
|
@ -956,7 +956,7 @@ STR_GAME_OPTIONS_CURRENCY_UNITS_DROPDOWN_TOOLTIP :{BLACK}Alege un
|
|||
|
||||
STR_GAME_OPTIONS_CURRENCY_CODE :{STRING} ({STRING})
|
||||
|
||||
###length 42
|
||||
###length 43
|
||||
STR_GAME_OPTIONS_CURRENCY_GBP :Liră sterlină
|
||||
STR_GAME_OPTIONS_CURRENCY_USD :Dolar american
|
||||
STR_GAME_OPTIONS_CURRENCY_EUR :Euro
|
||||
|
|
|
@ -1104,7 +1104,7 @@ STR_GAME_OPTIONS_CURRENCY_UNITS_DROPDOWN_TOOLTIP :{BLACK}Смен
|
|||
|
||||
STR_GAME_OPTIONS_CURRENCY_CODE :{STRING} ({STRING})
|
||||
|
||||
###length 42
|
||||
###length 43
|
||||
STR_GAME_OPTIONS_CURRENCY_GBP :Английский фунт
|
||||
STR_GAME_OPTIONS_CURRENCY_USD :Доллар США
|
||||
STR_GAME_OPTIONS_CURRENCY_EUR :Евро
|
||||
|
@ -2206,14 +2206,17 @@ STR_CONFIG_SETTING_ACCOUNTING :Финансы
|
|||
STR_CONFIG_SETTING_VEHICLES :Транспорт
|
||||
STR_CONFIG_SETTING_VEHICLES_PHYSICS :Физическая модель
|
||||
STR_CONFIG_SETTING_VEHICLES_ROUTING :Маршруты
|
||||
STR_CONFIG_SETTING_VEHICLES_ORDERS :Задания
|
||||
STR_CONFIG_SETTING_LIMITATIONS :Ограничения
|
||||
STR_CONFIG_SETTING_ACCIDENTS :Аварии и катастрофы
|
||||
STR_CONFIG_SETTING_GENWORLD :Создание карты
|
||||
STR_CONFIG_SETTING_ENVIRONMENT :Окружение
|
||||
STR_CONFIG_SETTING_ENVIRONMENT_TIME :Время
|
||||
STR_CONFIG_SETTING_ENVIRONMENT_AUTHORITIES :Городская администрация
|
||||
STR_CONFIG_SETTING_ENVIRONMENT_TOWNS :Города
|
||||
STR_CONFIG_SETTING_ENVIRONMENT_INDUSTRIES :Предприятия
|
||||
STR_CONFIG_SETTING_ENVIRONMENT_CARGODIST :Распределение грузов
|
||||
STR_CONFIG_SETTING_ENVIRONMENT_TREES :Деревья
|
||||
STR_CONFIG_SETTING_AI :Конкуренты
|
||||
STR_CONFIG_SETTING_AI_NPC :Искусственный интеллект
|
||||
STR_CONFIG_SETTING_NETWORK :Сеть
|
||||
|
|
|
@ -1113,7 +1113,7 @@ STR_GAME_OPTIONS_CURRENCY_UNITS_FRAME :{BLACK}Valuta
|
|||
STR_GAME_OPTIONS_CURRENCY_UNITS_DROPDOWN_TOOLTIP :{BLACK}Izbor valute
|
||||
|
||||
|
||||
###length 42
|
||||
###length 43
|
||||
STR_GAME_OPTIONS_CURRENCY_GBP :Britanska funta
|
||||
STR_GAME_OPTIONS_CURRENCY_USD :Dolar
|
||||
STR_GAME_OPTIONS_CURRENCY_EUR :Evro
|
||||
|
|
|
@ -441,7 +441,7 @@ STR_SETTINGS_MENU_SHOW_COMPETITOR_SIGNS :显示其他公
|
|||
STR_SETTINGS_MENU_FULL_ANIMATION :完全动画
|
||||
STR_SETTINGS_MENU_FULL_DETAIL :完全细节
|
||||
STR_SETTINGS_MENU_TRANSPARENT_BUILDINGS :透明建筑
|
||||
STR_SETTINGS_MENU_TRANSPARENT_SIGNS :透明车站标志
|
||||
STR_SETTINGS_MENU_TRANSPARENT_SIGNS :透明标志
|
||||
|
||||
# File menu
|
||||
STR_FILE_MENU_SAVE_GAME :保存游戏
|
||||
|
@ -959,7 +959,7 @@ STR_GAME_OPTIONS_CURRENCY_UNITS_DROPDOWN_TOOLTIP :{BLACK}货币
|
|||
|
||||
STR_GAME_OPTIONS_CURRENCY_CODE :{STRING} ({STRING})
|
||||
|
||||
###length 42
|
||||
###length 43
|
||||
STR_GAME_OPTIONS_CURRENCY_GBP :英镑
|
||||
STR_GAME_OPTIONS_CURRENCY_USD :美元
|
||||
STR_GAME_OPTIONS_CURRENCY_EUR :欧元
|
||||
|
@ -2055,14 +2055,17 @@ STR_CONFIG_SETTING_ACCOUNTING :财务管理
|
|||
STR_CONFIG_SETTING_VEHICLES :车辆
|
||||
STR_CONFIG_SETTING_VEHICLES_PHYSICS :物理
|
||||
STR_CONFIG_SETTING_VEHICLES_ROUTING :经由
|
||||
STR_CONFIG_SETTING_VEHICLES_ORDERS :调度命令
|
||||
STR_CONFIG_SETTING_LIMITATIONS :限制
|
||||
STR_CONFIG_SETTING_ACCIDENTS :灾难/事故
|
||||
STR_CONFIG_SETTING_GENWORLD :生成地图
|
||||
STR_CONFIG_SETTING_ENVIRONMENT :环境
|
||||
STR_CONFIG_SETTING_ENVIRONMENT_TIME :时间
|
||||
STR_CONFIG_SETTING_ENVIRONMENT_AUTHORITIES :政府监管
|
||||
STR_CONFIG_SETTING_ENVIRONMENT_TOWNS :城镇
|
||||
STR_CONFIG_SETTING_ENVIRONMENT_INDUSTRIES :工业设施
|
||||
STR_CONFIG_SETTING_ENVIRONMENT_CARGODIST :货物分配
|
||||
STR_CONFIG_SETTING_ENVIRONMENT_TREES :树木
|
||||
STR_CONFIG_SETTING_AI :竞争
|
||||
STR_CONFIG_SETTING_AI_NPC :电脑玩家
|
||||
STR_CONFIG_SETTING_NETWORK :网络
|
||||
|
@ -2784,7 +2787,7 @@ STR_BUILD_SIGNAL_DRAG_SIGNALS_DENSITY_INCREASE_TOOLTIP :{BLACK}增加
|
|||
# Bridge selection window
|
||||
STR_SELECT_RAIL_BRIDGE_CAPTION :{WHITE}选择铁路桥
|
||||
STR_SELECT_ROAD_BRIDGE_CAPTION :{WHITE}选择公路桥梁
|
||||
STR_SELECT_BRIDGE_SELECTION_TOOLTIP :{BLACK}选择桥梁 - 点击选择喜欢的桥梁进行建设
|
||||
STR_SELECT_BRIDGE_SELECTION_TOOLTIP :{BLACK}选择桥梁 - 点击选择桥梁进行建设
|
||||
STR_SELECT_BRIDGE_INFO_NAME :{GOLD}{STRING}
|
||||
STR_SELECT_BRIDGE_INFO_NAME_MAX_SPEED :{GOLD}{STRING},{} {VELOCITY}
|
||||
STR_SELECT_BRIDGE_INFO_NAME_COST :{GOLD}{0:STRING},{} {WHITE}{2:CURRENCY_LONG}
|
||||
|
|
|
@ -1016,7 +1016,7 @@ STR_GAME_OPTIONS_CURRENCY_UNITS_DROPDOWN_TOOLTIP :{BLACK}Výber m
|
|||
|
||||
STR_GAME_OPTIONS_CURRENCY_CODE :{STRING} ({STRING})
|
||||
|
||||
###length 42
|
||||
###length 43
|
||||
STR_GAME_OPTIONS_CURRENCY_GBP :Libra šterlingov
|
||||
STR_GAME_OPTIONS_CURRENCY_USD :Americký dolár
|
||||
STR_GAME_OPTIONS_CURRENCY_EUR :Euro
|
||||
|
|
|
@ -1030,7 +1030,7 @@ STR_GAME_OPTIONS_CURRENCY_UNITS_FRAME :{BLACK}Valute
|
|||
STR_GAME_OPTIONS_CURRENCY_UNITS_DROPDOWN_TOOLTIP :{BLACK}Izbira valut
|
||||
|
||||
|
||||
###length 42
|
||||
###length 43
|
||||
STR_GAME_OPTIONS_CURRENCY_GBP :Funt
|
||||
STR_GAME_OPTIONS_CURRENCY_USD :Dolar
|
||||
STR_GAME_OPTIONS_CURRENCY_EUR :Euro
|
||||
|
|
|
@ -919,7 +919,7 @@ STR_GAME_OPTIONS_CURRENCY_UNITS_FRAME :{BLACK}Unidad m
|
|||
STR_GAME_OPTIONS_CURRENCY_UNITS_DROPDOWN_TOOLTIP :{BLACK}Selecciona la nnidad monetaria
|
||||
|
||||
|
||||
###length 42
|
||||
###length 43
|
||||
STR_GAME_OPTIONS_CURRENCY_GBP :Libra británica
|
||||
STR_GAME_OPTIONS_CURRENCY_USD :Dólar estadounidense
|
||||
STR_GAME_OPTIONS_CURRENCY_EUR :Euro
|
||||
|
|
|
@ -919,7 +919,7 @@ STR_GAME_OPTIONS_CURRENCY_UNITS_FRAME :{BLACK}Divisa
|
|||
STR_GAME_OPTIONS_CURRENCY_UNITS_DROPDOWN_TOOLTIP :{BLACK}Elegir divisa
|
||||
|
||||
|
||||
###length 42
|
||||
###length 43
|
||||
STR_GAME_OPTIONS_CURRENCY_GBP :Libra británica
|
||||
STR_GAME_OPTIONS_CURRENCY_USD :Dólar estadounidense
|
||||
STR_GAME_OPTIONS_CURRENCY_EUR :Euro
|
||||
|
|
|
@ -951,7 +951,7 @@ STR_GAME_OPTIONS_CURRENCY_UNITS_DROPDOWN_TOOLTIP :{BLACK}Välj va
|
|||
|
||||
STR_GAME_OPTIONS_CURRENCY_CODE :{STRING} ({STRING})
|
||||
|
||||
###length 42
|
||||
###length 43
|
||||
STR_GAME_OPTIONS_CURRENCY_GBP :Brittiskt pund
|
||||
STR_GAME_OPTIONS_CURRENCY_USD :Amerikansk dollar
|
||||
STR_GAME_OPTIONS_CURRENCY_EUR :Euro
|
||||
|
|
|
@ -878,7 +878,7 @@ STR_GAME_OPTIONS_CURRENCY_UNITS_FRAME :{BLACK}நா
|
|||
STR_GAME_OPTIONS_CURRENCY_UNITS_DROPDOWN_TOOLTIP :{BLACK}நாணய பிரிவு தேர்ந்தெடுத்தல்
|
||||
|
||||
|
||||
###length 42
|
||||
###length 43
|
||||
STR_GAME_OPTIONS_CURRENCY_GBP :பிரித்தானிய பவுண்டு
|
||||
STR_GAME_OPTIONS_CURRENCY_USD :அமெரிக்கன் டாலர்
|
||||
STR_GAME_OPTIONS_CURRENCY_EUR :யுரோ
|
||||
|
|
|
@ -889,7 +889,7 @@ STR_GAME_OPTIONS_CURRENCY_UNITS_FRAME :{BLACK}หน
|
|||
STR_GAME_OPTIONS_CURRENCY_UNITS_DROPDOWN_TOOLTIP :{BLACK}การเลือกสกุลเงิน
|
||||
|
||||
|
||||
###length 42
|
||||
###length 43
|
||||
STR_GAME_OPTIONS_CURRENCY_GBP :ปอนด์สเตอร์ลิง
|
||||
STR_GAME_OPTIONS_CURRENCY_USD :ดอลลาร์สหรัฐ
|
||||
STR_GAME_OPTIONS_CURRENCY_EUR :ยูโร
|
||||
|
|
|
@ -951,7 +951,7 @@ STR_GAME_OPTIONS_CURRENCY_UNITS_DROPDOWN_TOOLTIP :{BLACK}選擇
|
|||
|
||||
STR_GAME_OPTIONS_CURRENCY_CODE :{STRING} ({STRING})
|
||||
|
||||
###length 42
|
||||
###length 43
|
||||
STR_GAME_OPTIONS_CURRENCY_GBP :英鎊
|
||||
STR_GAME_OPTIONS_CURRENCY_USD :美元
|
||||
STR_GAME_OPTIONS_CURRENCY_EUR :歐元
|
||||
|
|
|
@ -960,7 +960,7 @@ STR_GAME_OPTIONS_CURRENCY_UNITS_DROPDOWN_TOOLTIP :{BLACK}Para bir
|
|||
|
||||
STR_GAME_OPTIONS_CURRENCY_CODE :{STRING} ({STRING})
|
||||
|
||||
###length 42
|
||||
###length 43
|
||||
STR_GAME_OPTIONS_CURRENCY_GBP :Sterlin
|
||||
STR_GAME_OPTIONS_CURRENCY_USD :Amerikan Doları
|
||||
STR_GAME_OPTIONS_CURRENCY_EUR :Avro
|
||||
|
|
|
@ -1061,7 +1061,7 @@ STR_GAME_OPTIONS_CURRENCY_UNITS_FRAME :{BLACK}Грош
|
|||
STR_GAME_OPTIONS_CURRENCY_UNITS_DROPDOWN_TOOLTIP :{BLACK}Виберіть грошову одиницю
|
||||
|
||||
|
||||
###length 42
|
||||
###length 43
|
||||
STR_GAME_OPTIONS_CURRENCY_GBP :Британський фунт
|
||||
STR_GAME_OPTIONS_CURRENCY_USD :Американський долар
|
||||
STR_GAME_OPTIONS_CURRENCY_EUR :Євро
|
||||
|
|
|
@ -843,7 +843,7 @@ STR_GAME_OPTIONS_CURRENCY_UNITS_FRAME :{BLACK}کرنس
|
|||
STR_GAME_OPTIONS_CURRENCY_UNITS_DROPDOWN_TOOLTIP :{BLACK}کرنسی کی اکائی اختیار کریں
|
||||
|
||||
|
||||
###length 42
|
||||
###length 43
|
||||
STR_GAME_OPTIONS_CURRENCY_GBP :برطانوی پاونڈ
|
||||
STR_GAME_OPTIONS_CURRENCY_USD :امریکی ڈالر
|
||||
STR_GAME_OPTIONS_CURRENCY_EUR :یورو
|
||||
|
|
|
@ -959,7 +959,7 @@ STR_GAME_OPTIONS_CURRENCY_UNITS_DROPDOWN_TOOLTIP :{BLACK}Lựa ch
|
|||
|
||||
STR_GAME_OPTIONS_CURRENCY_CODE :{STRING} ({STRING})
|
||||
|
||||
###length 42
|
||||
###length 43
|
||||
STR_GAME_OPTIONS_CURRENCY_GBP :Bảng Anh
|
||||
STR_GAME_OPTIONS_CURRENCY_USD :Đô-la Mỹ
|
||||
STR_GAME_OPTIONS_CURRENCY_EUR :Euro
|
||||
|
|
|
@ -896,7 +896,7 @@ STR_GAME_OPTIONS_CURRENCY_UNITS_DROPDOWN_TOOLTIP :{BLACK}Dewis un
|
|||
|
||||
STR_GAME_OPTIONS_CURRENCY_CODE :{STRING} ({STRING})
|
||||
|
||||
###length 42
|
||||
###length 43
|
||||
STR_GAME_OPTIONS_CURRENCY_GBP :Punnoedd Prydeinig
|
||||
STR_GAME_OPTIONS_CURRENCY_USD :Doleri America
|
||||
STR_GAME_OPTIONS_CURRENCY_EUR :Ewro
|
||||
|
|
|
@ -5,4 +5,6 @@ add_files(
|
|||
follow_track.hpp
|
||||
pathfinder_func.h
|
||||
pathfinder_type.h
|
||||
water_regions.h
|
||||
water_regions.cpp
|
||||
)
|
||||
|
|
|
@ -0,0 +1,379 @@
|
|||
/*
|
||||
* This file is part of OpenTTD.
|
||||
* OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
|
||||
* OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
* See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/** @file water_regions.cpp Handles dividing the water in the map into square regions to assist pathfinding. */
|
||||
|
||||
#include "stdafx.h"
|
||||
#include "map_func.h"
|
||||
#include "water_regions.h"
|
||||
#include "map_func.h"
|
||||
#include "tilearea_type.h"
|
||||
#include "track_func.h"
|
||||
#include "transport_type.h"
|
||||
#include "landscape.h"
|
||||
#include "tunnelbridge_map.h"
|
||||
#include "follow_track.hpp"
|
||||
#include "ship.h"
|
||||
|
||||
using TWaterRegionTraversabilityBits = uint16_t;
|
||||
constexpr TWaterRegionPatchLabel FIRST_REGION_LABEL = 1;
|
||||
constexpr TWaterRegionPatchLabel INVALID_WATER_REGION_PATCH = 0;
|
||||
|
||||
static_assert(sizeof(TWaterRegionTraversabilityBits) * 8 == WATER_REGION_EDGE_LENGTH);
|
||||
|
||||
static inline TrackBits GetWaterTracks(TileIndex tile) { return TrackStatusToTrackBits(GetTileTrackStatus(tile, TRANSPORT_WATER, 0)); }
|
||||
static inline bool IsAqueductTile(TileIndex tile) { return IsBridgeTile(tile) && GetTunnelBridgeTransportType(tile) == TRANSPORT_WATER; }
|
||||
|
||||
static inline int GetWaterRegionX(TileIndex tile) { return TileX(tile) / WATER_REGION_EDGE_LENGTH; }
|
||||
static inline int GetWaterRegionY(TileIndex tile) { return TileY(tile) / WATER_REGION_EDGE_LENGTH; }
|
||||
|
||||
static inline int GetWaterRegionMapSizeX() { return Map::SizeX() / WATER_REGION_EDGE_LENGTH; }
|
||||
static inline int GetWaterRegionMapSizeY() { return Map::SizeY() / WATER_REGION_EDGE_LENGTH; }
|
||||
|
||||
static inline TWaterRegionIndex GetWaterRegionIndex(int region_x, int region_y) { return GetWaterRegionMapSizeX() * region_y + region_x; }
|
||||
static inline TWaterRegionIndex GetWaterRegionIndex(TileIndex tile) { return GetWaterRegionIndex(GetWaterRegionX(tile), GetWaterRegionY(tile)); }
|
||||
|
||||
/**
|
||||
* Represents a square section of the map of a fixed size. Within this square individual unconnected patches of water are
|
||||
* identified using a Connected Component Labeling (CCL) algorithm. Note that all information stored in this class applies
|
||||
* only to tiles within the square section, there is no knowledge about the rest of the map. This makes it easy to invalidate
|
||||
* and update a water region if any changes are made to it, such as construction or terraforming.
|
||||
*/
|
||||
class WaterRegion
|
||||
{
|
||||
private:
|
||||
std::array<TWaterRegionTraversabilityBits, DIAGDIR_END> edge_traversability_bits{};
|
||||
bool has_cross_region_aqueducts = false;
|
||||
TWaterRegionPatchLabel number_of_patches = 0; // 0 = no water, 1 = one single patch of water, etc...
|
||||
const OrthogonalTileArea tile_area;
|
||||
std::array<TWaterRegionPatchLabel, WATER_REGION_NUMBER_OF_TILES> tile_patch_labels{};
|
||||
bool initialized = false;
|
||||
|
||||
/**
|
||||
* Returns the local index of the tile within the region. The N corner represents 0,
|
||||
* the x direction is positive in the SW direction, and Y is positive in the SE direction.
|
||||
* @param tile Tile within the water region.
|
||||
* @returns The local index.
|
||||
*/
|
||||
inline int GetLocalIndex(TileIndex tile) const
|
||||
{
|
||||
assert(this->tile_area.Contains(tile));
|
||||
return (TileX(tile) - TileX(this->tile_area.tile)) + WATER_REGION_EDGE_LENGTH * (TileY(tile) - TileY(this->tile_area.tile));
|
||||
}
|
||||
|
||||
public:
|
||||
WaterRegion(int region_x, int region_y)
|
||||
: tile_area(TileXY(region_x * WATER_REGION_EDGE_LENGTH, region_y * WATER_REGION_EDGE_LENGTH), WATER_REGION_EDGE_LENGTH, WATER_REGION_EDGE_LENGTH)
|
||||
{}
|
||||
|
||||
OrthogonalTileIterator begin() const { return this->tile_area.begin(); }
|
||||
OrthogonalTileIterator end() const { return this->tile_area.end(); }
|
||||
|
||||
bool IsInitialized() const { return this->initialized; }
|
||||
|
||||
void Invalidate() { this->initialized = false; }
|
||||
|
||||
/**
|
||||
* Returns a set of bits indicating whether an edge tile on a particular side is traversable or not. These
|
||||
* values can be used to determine whether a ship can enter/leave the region through a particular edge tile.
|
||||
* @see GetLocalIndex() for a description of the coordinate system used.
|
||||
* @param side Which side of the region we want to know the edge traversability of.
|
||||
* @returns A value holding the edge traversability bits.
|
||||
*/
|
||||
TWaterRegionTraversabilityBits GetEdgeTraversabilityBits(DiagDirection side) const { return edge_traversability_bits[side]; }
|
||||
|
||||
/**
|
||||
* @returns The amount of individual water patches present within the water region. A value of
|
||||
* 0 means there is no water present in the water region at all.
|
||||
*/
|
||||
int NumberOfPatches() const { return this->number_of_patches; }
|
||||
|
||||
/**
|
||||
* @returns Whether the water region contains aqueducts that cross the region boundaries.
|
||||
*/
|
||||
bool HasCrossRegionAqueducts() const { return this->has_cross_region_aqueducts; }
|
||||
|
||||
/**
|
||||
* Returns the patch label that was assigned to the tile.
|
||||
* @param tile The tile of which we want to retrieve the label.
|
||||
* @returns The label assigned to the tile.
|
||||
*/
|
||||
TWaterRegionPatchLabel GetLabel(TileIndex tile) const
|
||||
{
|
||||
assert(this->tile_area.Contains(tile));
|
||||
return this->tile_patch_labels[GetLocalIndex(tile)];
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs the connected component labeling and other data gathering.
|
||||
* @see WaterRegion
|
||||
*/
|
||||
void ForceUpdate()
|
||||
{
|
||||
this->has_cross_region_aqueducts = false;
|
||||
|
||||
this->tile_patch_labels.fill(INVALID_WATER_REGION_PATCH);
|
||||
|
||||
for (const TileIndex tile : this->tile_area) {
|
||||
if (IsAqueductTile(tile)) {
|
||||
const TileIndex other_aqueduct_end = GetOtherBridgeEnd(tile);
|
||||
if (!tile_area.Contains(other_aqueduct_end)) {
|
||||
this->has_cross_region_aqueducts = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TWaterRegionPatchLabel current_label = 1;
|
||||
TWaterRegionPatchLabel highest_assigned_label = 0;
|
||||
|
||||
/* Perform connected component labeling. This uses a flooding algorithm that expands until no
|
||||
* additional tiles can be added. Only tiles inside the water region are considered. */
|
||||
for (const TileIndex start_tile : tile_area) {
|
||||
static std::vector<TileIndex> tiles_to_check;
|
||||
tiles_to_check.clear();
|
||||
tiles_to_check.push_back(start_tile);
|
||||
|
||||
bool increase_label = false;
|
||||
while (!tiles_to_check.empty()) {
|
||||
const TileIndex tile = tiles_to_check.back();
|
||||
tiles_to_check.pop_back();
|
||||
|
||||
const TrackdirBits valid_dirs = TrackBitsToTrackdirBits(GetWaterTracks(tile));
|
||||
if (valid_dirs == TRACKDIR_BIT_NONE) continue;
|
||||
|
||||
if (this->tile_patch_labels[GetLocalIndex(tile)] != INVALID_WATER_REGION_PATCH) continue;
|
||||
|
||||
this->tile_patch_labels[GetLocalIndex(tile)] = current_label;
|
||||
highest_assigned_label = current_label;
|
||||
increase_label = true;
|
||||
|
||||
for (const Trackdir dir : SetTrackdirBitIterator(valid_dirs)) {
|
||||
/* By using a TrackFollower we "play by the same rules" as the actual ship pathfinder */
|
||||
CFollowTrackWater ft;
|
||||
if (ft.Follow(tile, dir) && this->tile_area.Contains(ft.m_new_tile)) tiles_to_check.push_back(ft.m_new_tile);
|
||||
}
|
||||
}
|
||||
|
||||
if (increase_label) current_label++;
|
||||
}
|
||||
|
||||
this->number_of_patches = highest_assigned_label;
|
||||
this->initialized = true;
|
||||
|
||||
/* Calculate the traversability (whether the tile can be entered / exited) for all edges. Note that
|
||||
* we always follow the same X and Y scanning direction, this is important for comparisons later on! */
|
||||
this->edge_traversability_bits.fill(0);
|
||||
const int top_x = TileX(tile_area.tile);
|
||||
const int top_y = TileY(tile_area.tile);
|
||||
for (int i = 0; i < WATER_REGION_EDGE_LENGTH; ++i) {
|
||||
if (GetWaterTracks(TileXY(top_x + i, top_y)) & TRACK_BIT_3WAY_NW) SetBit(this->edge_traversability_bits[DIAGDIR_NW], i); // NW edge
|
||||
if (GetWaterTracks(TileXY(top_x + i, top_y + WATER_REGION_EDGE_LENGTH - 1)) & TRACK_BIT_3WAY_SE) SetBit(this->edge_traversability_bits[DIAGDIR_SE], i); // SE edge
|
||||
if (GetWaterTracks(TileXY(top_x, top_y + i)) & TRACK_BIT_3WAY_NE) SetBit(this->edge_traversability_bits[DIAGDIR_NE], i); // NE edge
|
||||
if (GetWaterTracks(TileXY(top_x + WATER_REGION_EDGE_LENGTH - 1, top_y + i)) & TRACK_BIT_3WAY_SW) SetBit(this->edge_traversability_bits[DIAGDIR_SW], i); // SW edge
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the patch labels and other data, but only if the region is not yet initialized.
|
||||
*/
|
||||
inline void UpdateIfNotInitialized()
|
||||
{
|
||||
if (!this->initialized) ForceUpdate();
|
||||
}
|
||||
};
|
||||
|
||||
std::vector<WaterRegion> _water_regions;
|
||||
|
||||
TileIndex GetTileIndexFromLocalCoordinate(int region_x, int region_y, int local_x, int local_y)
|
||||
{
|
||||
assert(local_x >= 0 && local_y < WATER_REGION_EDGE_LENGTH);
|
||||
assert(local_y >= 0 && local_y < WATER_REGION_EDGE_LENGTH);
|
||||
return TileXY(WATER_REGION_EDGE_LENGTH * region_x + local_x, WATER_REGION_EDGE_LENGTH * region_y + local_y);
|
||||
}
|
||||
|
||||
TileIndex GetEdgeTileCoordinate(int region_x, int region_y, DiagDirection side, int x_or_y)
|
||||
{
|
||||
assert(x_or_y >= 0 && x_or_y < WATER_REGION_EDGE_LENGTH);
|
||||
switch (side) {
|
||||
case DIAGDIR_NE: return GetTileIndexFromLocalCoordinate(region_x, region_y, 0, x_or_y);
|
||||
case DIAGDIR_SW: return GetTileIndexFromLocalCoordinate(region_x, region_y, WATER_REGION_EDGE_LENGTH - 1, x_or_y);
|
||||
case DIAGDIR_NW: return GetTileIndexFromLocalCoordinate(region_x, region_y, x_or_y, 0);
|
||||
case DIAGDIR_SE: return GetTileIndexFromLocalCoordinate(region_x, region_y, x_or_y, WATER_REGION_EDGE_LENGTH - 1);
|
||||
default: NOT_REACHED();
|
||||
}
|
||||
}
|
||||
|
||||
WaterRegion &GetUpdatedWaterRegion(uint16_t region_x, uint16_t region_y)
|
||||
{
|
||||
WaterRegion &result = _water_regions[GetWaterRegionIndex(region_x, region_y)];
|
||||
result.UpdateIfNotInitialized();
|
||||
return result;
|
||||
}
|
||||
|
||||
WaterRegion &GetUpdatedWaterRegion(TileIndex tile)
|
||||
{
|
||||
WaterRegion &result = _water_regions[GetWaterRegionIndex(tile)];
|
||||
result.UpdateIfNotInitialized();
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the index of the water region
|
||||
* @param water_region The Water region to return the index for
|
||||
*/
|
||||
TWaterRegionIndex GetWaterRegionIndex(const WaterRegionDesc &water_region)
|
||||
{
|
||||
return GetWaterRegionIndex(water_region.x, water_region.y);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the center tile of a particular water region.
|
||||
* @param water_region The water region to find the center tile for.
|
||||
* @returns The center tile of the water region.
|
||||
*/
|
||||
TileIndex GetWaterRegionCenterTile(const WaterRegionDesc &water_region)
|
||||
{
|
||||
return TileXY(water_region.x * WATER_REGION_EDGE_LENGTH + (WATER_REGION_EDGE_LENGTH / 2), water_region.y * WATER_REGION_EDGE_LENGTH + (WATER_REGION_EDGE_LENGTH / 2));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns basic water region information for the provided tile.
|
||||
* @param tile The tile for which the information will be calculated.
|
||||
*/
|
||||
WaterRegionDesc GetWaterRegionInfo(TileIndex tile)
|
||||
{
|
||||
return WaterRegionDesc{ GetWaterRegionX(tile), GetWaterRegionY(tile) };
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns basic water region patch information for the provided tile.
|
||||
* @param tile The tile for which the information will be calculated.
|
||||
*/
|
||||
WaterRegionPatchDesc GetWaterRegionPatchInfo(TileIndex tile)
|
||||
{
|
||||
WaterRegion ®ion = GetUpdatedWaterRegion(tile);
|
||||
return WaterRegionPatchDesc{ GetWaterRegionX(tile), GetWaterRegionY(tile), region.GetLabel(tile)};
|
||||
}
|
||||
|
||||
/**
|
||||
* Marks the water region that tile is part of as invalid.
|
||||
* @param tile Tile within the water region that we wish to invalidate.
|
||||
*/
|
||||
void InvalidateWaterRegion(TileIndex tile)
|
||||
{
|
||||
const int index = GetWaterRegionIndex(tile);
|
||||
if (index > static_cast<int>(_water_regions.size())) return;
|
||||
_water_regions[index].Invalidate();
|
||||
}
|
||||
|
||||
/**
|
||||
* Calls the provided callback function for all water region patches
|
||||
* accessible from one particular side of the starting patch.
|
||||
* @param water_region_patch Water patch within the water region to start searching from
|
||||
* @param side Side of the water region to look for neigboring patches of water
|
||||
* @param callback The function that will be called for each neighbor that is found
|
||||
*/
|
||||
static inline void VisitAdjacentWaterRegionPatchNeighbors(const WaterRegionPatchDesc &water_region_patch, DiagDirection side, TVisitWaterRegionPatchCallBack &func)
|
||||
{
|
||||
const WaterRegion ¤t_region = GetUpdatedWaterRegion(water_region_patch.x, water_region_patch.y);
|
||||
|
||||
const TileIndexDiffC offset = TileIndexDiffCByDiagDir(side);
|
||||
const int nx = water_region_patch.x + offset.x;
|
||||
const int ny = water_region_patch.y + offset.y;
|
||||
|
||||
if (nx < 0 || ny < 0 || nx >= GetWaterRegionMapSizeX() || ny >= GetWaterRegionMapSizeY()) return;
|
||||
|
||||
const WaterRegion &neighboring_region = GetUpdatedWaterRegion(nx, ny);
|
||||
const DiagDirection opposite_side = ReverseDiagDir(side);
|
||||
|
||||
/* Indicates via which local x or y coordinates (depends on the "side" parameter) we can cross over into the adjacent region. */
|
||||
const TWaterRegionTraversabilityBits traversability_bits = current_region.GetEdgeTraversabilityBits(side)
|
||||
& neighboring_region.GetEdgeTraversabilityBits(opposite_side);
|
||||
if (traversability_bits == 0) return;
|
||||
|
||||
if (current_region.NumberOfPatches() == 1 && neighboring_region.NumberOfPatches() == 1) {
|
||||
func(WaterRegionPatchDesc{ nx, ny, FIRST_REGION_LABEL }); // No further checks needed because we know there is just one patch for both adjacent regions
|
||||
return;
|
||||
}
|
||||
|
||||
/* Multiple water patches can be reached from the current patch. Check each edge tile individually. */
|
||||
static std::vector<TWaterRegionPatchLabel> unique_labels; // static and vector-instead-of-map for performance reasons
|
||||
unique_labels.clear();
|
||||
for (int x_or_y = 0; x_or_y < WATER_REGION_EDGE_LENGTH; ++x_or_y) {
|
||||
if (!HasBit(traversability_bits, x_or_y)) continue;
|
||||
|
||||
const TileIndex current_edge_tile = GetEdgeTileCoordinate(water_region_patch.x, water_region_patch.y, side, x_or_y);
|
||||
const TWaterRegionPatchLabel current_label = current_region.GetLabel(current_edge_tile);
|
||||
if (current_label != water_region_patch.label) continue;
|
||||
|
||||
const TileIndex neighbor_edge_tile = GetEdgeTileCoordinate(nx, ny, opposite_side, x_or_y);
|
||||
const TWaterRegionPatchLabel neighbor_label = neighboring_region.GetLabel(neighbor_edge_tile);
|
||||
if (std::find(unique_labels.begin(), unique_labels.end(), neighbor_label) == unique_labels.end()) unique_labels.push_back(neighbor_label);
|
||||
}
|
||||
for (TWaterRegionPatchLabel unique_label : unique_labels) func(WaterRegionPatchDesc{ nx, ny, unique_label });
|
||||
}
|
||||
|
||||
/**
|
||||
* Calls the provided callback function on all accessible water region patches in
|
||||
* each cardinal direction, plus any others that are reachable via aqueducts.
|
||||
* @param water_region_patch Water patch within the water region to start searching from
|
||||
* @param callback The function that will be called for each accessible water patch that is found
|
||||
*/
|
||||
void VisitWaterRegionPatchNeighbors(const WaterRegionPatchDesc &water_region_patch, TVisitWaterRegionPatchCallBack &callback)
|
||||
{
|
||||
const WaterRegion ¤t_region = GetUpdatedWaterRegion(water_region_patch.x, water_region_patch.y);
|
||||
|
||||
/* Visit adjacent water region patches in each cardinal direction */
|
||||
for (DiagDirection side = DIAGDIR_BEGIN; side < DIAGDIR_END; side++) VisitAdjacentWaterRegionPatchNeighbors(water_region_patch, side, callback);
|
||||
|
||||
/* Visit neigboring water patches accessible via cross-region aqueducts */
|
||||
if (current_region.HasCrossRegionAqueducts()) {
|
||||
for (const TileIndex tile : current_region) {
|
||||
if (GetWaterRegionPatchInfo(tile) == water_region_patch && IsAqueductTile(tile)) {
|
||||
const TileIndex other_end_tile = GetOtherBridgeEnd(tile);
|
||||
if (GetWaterRegionIndex(tile) != GetWaterRegionIndex(other_end_tile)) callback(GetWaterRegionPatchInfo(other_end_tile));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<WaterRegionSaveLoadInfo> GetWaterRegionSaveLoadInfo()
|
||||
{
|
||||
std::vector<WaterRegionSaveLoadInfo> result;
|
||||
for (WaterRegion ®ion : _water_regions) result.push_back({ region.IsInitialized() });
|
||||
return result;
|
||||
}
|
||||
|
||||
void LoadWaterRegions(const std::vector<WaterRegionSaveLoadInfo> &save_load_info)
|
||||
{
|
||||
_water_regions.clear();
|
||||
_water_regions.reserve(save_load_info.size());
|
||||
TWaterRegionIndex index = 0;
|
||||
for (const auto &loaded_region_info : save_load_info) {
|
||||
const int region_x = index % GetWaterRegionMapSizeX();
|
||||
const int region_y = index / GetWaterRegionMapSizeX();
|
||||
WaterRegion ®ion = _water_regions.emplace_back(region_x, region_y);
|
||||
if (loaded_region_info.initialized) region.ForceUpdate();
|
||||
index++;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes all water regions. All water tiles will be scanned and interconnected water patches within regions will be identified.
|
||||
*/
|
||||
void InitializeWaterRegions()
|
||||
{
|
||||
_water_regions.clear();
|
||||
_water_regions.reserve(static_cast<size_t>(GetWaterRegionMapSizeX()) * GetWaterRegionMapSizeY());
|
||||
|
||||
for (int region_y = 0; region_y < GetWaterRegionMapSizeY(); region_y++) {
|
||||
for (int region_x = 0; region_x < GetWaterRegionMapSizeX(); region_x++) {
|
||||
_water_regions.emplace_back(region_x, region_y).ForceUpdate();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,73 @@
|
|||
/*
|
||||
* This file is part of OpenTTD.
|
||||
* OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
|
||||
* OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
* See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/** @file water_regions.h Handles dividing the water in the map into regions to assist pathfinding. */
|
||||
|
||||
#ifndef WATER_REGIONS_H
|
||||
#define WATER_REGIONS_H
|
||||
|
||||
#include "tile_type.h"
|
||||
#include "map_func.h"
|
||||
|
||||
using TWaterRegionPatchLabel = uint8_t;
|
||||
using TWaterRegionIndex = uint;
|
||||
|
||||
constexpr int WATER_REGION_EDGE_LENGTH = 16;
|
||||
constexpr int WATER_REGION_NUMBER_OF_TILES = WATER_REGION_EDGE_LENGTH * WATER_REGION_EDGE_LENGTH;
|
||||
|
||||
/**
|
||||
* Describes a single interconnected patch of water within a particular water region.
|
||||
*/
|
||||
struct WaterRegionPatchDesc
|
||||
{
|
||||
int x; ///< The X coordinate of the water region, i.e. X=2 is the 3rd water region along the X-axis
|
||||
int y; ///< The Y coordinate of the water region, i.e. Y=2 is the 3rd water region along the Y-axis
|
||||
TWaterRegionPatchLabel label; ///< Unique label identifying the patch within the region
|
||||
|
||||
bool operator==(const WaterRegionPatchDesc &other) const { return x == other.x && y == other.y && label == other.label; }
|
||||
bool operator!=(const WaterRegionPatchDesc &other) const { return !(*this == other); }
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Describes a single square water region.
|
||||
*/
|
||||
struct WaterRegionDesc
|
||||
{
|
||||
int x; ///< The X coordinate of the water region, i.e. X=2 is the 3rd water region along the X-axis
|
||||
int y; ///< The Y coordinate of the water region, i.e. Y=2 is the 3rd water region along the Y-axis
|
||||
|
||||
WaterRegionDesc(const int x, const int y) : x(x), y(y) {}
|
||||
WaterRegionDesc(const WaterRegionPatchDesc &water_region_patch) : x(water_region_patch.x), y(water_region_patch.y) {}
|
||||
|
||||
bool operator==(const WaterRegionDesc &other) const { return x == other.x && y == other.y; }
|
||||
bool operator!=(const WaterRegionDesc &other) const { return !(*this == other); }
|
||||
};
|
||||
|
||||
TWaterRegionIndex GetWaterRegionIndex(const WaterRegionDesc &water_region);
|
||||
|
||||
TileIndex GetWaterRegionCenterTile(const WaterRegionDesc &water_region);
|
||||
|
||||
WaterRegionDesc GetWaterRegionInfo(TileIndex tile);
|
||||
WaterRegionPatchDesc GetWaterRegionPatchInfo(TileIndex tile);
|
||||
|
||||
void InvalidateWaterRegion(TileIndex tile);
|
||||
|
||||
using TVisitWaterRegionPatchCallBack = std::function<void(const WaterRegionPatchDesc &)>;
|
||||
void VisitWaterRegionPatchNeighbors(const WaterRegionPatchDesc &water_region_patch, TVisitWaterRegionPatchCallBack &callback);
|
||||
|
||||
void InitializeWaterRegions();
|
||||
|
||||
struct WaterRegionSaveLoadInfo
|
||||
{
|
||||
bool initialized;
|
||||
};
|
||||
|
||||
std::vector<WaterRegionSaveLoadInfo> GetWaterRegionSaveLoadInfo();
|
||||
void LoadWaterRegions(const std::vector<WaterRegionSaveLoadInfo> &save_load_info);
|
||||
|
||||
#endif /* WATER_REGIONS_H */
|
|
@ -16,5 +16,7 @@ add_files(
|
|||
yapf_rail.cpp
|
||||
yapf_road.cpp
|
||||
yapf_ship.cpp
|
||||
yapf_ship_regions.h
|
||||
yapf_ship_regions.cpp
|
||||
yapf_type.hpp
|
||||
)
|
||||
|
|
|
@ -14,46 +14,64 @@
|
|||
|
||||
#include "yapf.hpp"
|
||||
#include "yapf_node_ship.hpp"
|
||||
#include "yapf_ship_regions.h"
|
||||
#include "../water_regions.h"
|
||||
|
||||
#include "../../safeguards.h"
|
||||
|
||||
constexpr int NUMBER_OR_WATER_REGIONS_LOOKAHEAD = 4;
|
||||
constexpr int MAX_SHIP_PF_NODES = (NUMBER_OR_WATER_REGIONS_LOOKAHEAD + 1) * WATER_REGION_NUMBER_OF_TILES * 4; // 4 possible exit dirs per tile.
|
||||
|
||||
constexpr int SHIP_LOST_PATH_LENGTH = 8; // The length of the (aimless) path assigned when a ship is lost.
|
||||
|
||||
template <class Types>
|
||||
class CYapfDestinationTileWaterT
|
||||
{
|
||||
public:
|
||||
typedef typename Types::Tpf Tpf; ///< the pathfinder class (derived from THIS class)
|
||||
typedef typename Types::Tpf Tpf; ///< the pathfinder class (derived from THIS class).
|
||||
typedef typename Types::TrackFollower TrackFollower;
|
||||
typedef typename Types::NodeList::Titem Node; ///< this will be our node type
|
||||
typedef typename Node::Key Key; ///< key to hash tables
|
||||
typedef typename Types::NodeList::Titem Node; ///< this will be our node type.
|
||||
typedef typename Node::Key Key; ///< key to hash tables.
|
||||
|
||||
protected:
|
||||
TileIndex m_destTile;
|
||||
TrackdirBits m_destTrackdirs;
|
||||
StationID m_destStation;
|
||||
|
||||
bool m_has_intermediate_dest = false;
|
||||
TileIndex m_intermediate_dest_tile;
|
||||
WaterRegionPatchDesc m_intermediate_dest_region_patch;
|
||||
|
||||
public:
|
||||
void SetDestination(const Ship *v)
|
||||
{
|
||||
if (v->current_order.IsType(OT_GOTO_STATION)) {
|
||||
m_destStation = v->current_order.GetDestination();
|
||||
m_destTile = CalcClosestStationTile(m_destStation, v->tile, STATION_DOCK);
|
||||
m_destStation = v->current_order.GetDestination();
|
||||
m_destTile = CalcClosestStationTile(m_destStation, v->tile, STATION_DOCK);
|
||||
m_destTrackdirs = INVALID_TRACKDIR_BIT;
|
||||
} else {
|
||||
m_destStation = INVALID_STATION;
|
||||
m_destTile = v->dest_tile;
|
||||
m_destStation = INVALID_STATION;
|
||||
m_destTile = v->dest_tile;
|
||||
m_destTrackdirs = TrackStatusToTrackdirBits(GetTileTrackStatus(v->dest_tile, TRANSPORT_WATER, 0));
|
||||
}
|
||||
}
|
||||
|
||||
void SetIntermediateDestination(const WaterRegionPatchDesc &water_region_patch)
|
||||
{
|
||||
m_has_intermediate_dest = true;
|
||||
m_intermediate_dest_tile = GetWaterRegionCenterTile(water_region_patch);
|
||||
m_intermediate_dest_region_patch = water_region_patch;
|
||||
}
|
||||
|
||||
protected:
|
||||
/** to access inherited path finder */
|
||||
inline Tpf &Yapf()
|
||||
/** To access inherited path finder. */
|
||||
inline Tpf& Yapf()
|
||||
{
|
||||
return *static_cast<Tpf*>(this);
|
||||
}
|
||||
|
||||
public:
|
||||
/** Called by YAPF to detect if node ends in the desired destination */
|
||||
/** Called by YAPF to detect if node ends in the desired destination. */
|
||||
inline bool PfDetectDestination(Node &n)
|
||||
{
|
||||
return PfDetectDestinationTile(n.m_segment_last_tile, n.m_segment_last_td);
|
||||
|
@ -61,21 +79,27 @@ public:
|
|||
|
||||
inline bool PfDetectDestinationTile(TileIndex tile, Trackdir trackdir)
|
||||
{
|
||||
if (m_destStation != INVALID_STATION) {
|
||||
return IsDockingTile(tile) && IsShipDestinationTile(tile, m_destStation);
|
||||
if (m_has_intermediate_dest) {
|
||||
/* GetWaterRegionInfo is much faster than GetWaterRegionPatchInfo so we try that first. */
|
||||
if (GetWaterRegionInfo(tile) != m_intermediate_dest_region_patch) return false;
|
||||
return GetWaterRegionPatchInfo(tile) == m_intermediate_dest_region_patch;
|
||||
}
|
||||
|
||||
if (m_destStation != INVALID_STATION) return IsDockingTile(tile) && IsShipDestinationTile(tile, m_destStation);
|
||||
|
||||
return tile == m_destTile && ((m_destTrackdirs & TrackdirToTrackdirBits(trackdir)) != TRACKDIR_BIT_NONE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Called by YAPF to calculate cost estimate. Calculates distance to the destination
|
||||
* adds it to the actual cost from origin and stores the sum to the Node::m_estimate
|
||||
* adds it to the actual cost from origin and stores the sum to the Node::m_estimate.
|
||||
*/
|
||||
inline bool PfCalcEstimate(Node &n)
|
||||
{
|
||||
static const int dg_dir_to_x_offs[] = {-1, 0, 1, 0};
|
||||
static const int dg_dir_to_y_offs[] = {0, 1, 0, -1};
|
||||
const TileIndex destination_tile = m_has_intermediate_dest ? m_intermediate_dest_tile : m_destTile;
|
||||
|
||||
static const int dg_dir_to_x_offs[] = { -1, 0, 1, 0 };
|
||||
static const int dg_dir_to_y_offs[] = { 0, 1, 0, -1 };
|
||||
if (PfDetectDestination(n)) {
|
||||
n.m_estimate = n.m_cost;
|
||||
return true;
|
||||
|
@ -85,8 +109,8 @@ public:
|
|||
DiagDirection exitdir = TrackdirToExitdir(n.m_segment_last_td);
|
||||
int x1 = 2 * TileX(tile) + dg_dir_to_x_offs[(int)exitdir];
|
||||
int y1 = 2 * TileY(tile) + dg_dir_to_y_offs[(int)exitdir];
|
||||
int x2 = 2 * TileX(m_destTile);
|
||||
int y2 = 2 * TileY(m_destTile);
|
||||
int x2 = 2 * TileX(destination_tile);
|
||||
int y2 = 2 * TileY(destination_tile);
|
||||
int dx = abs(x1 - x2);
|
||||
int dy = abs(y1 - y2);
|
||||
int dmin = std::min(dx, dy);
|
||||
|
@ -98,24 +122,25 @@ public:
|
|||
}
|
||||
};
|
||||
|
||||
|
||||
/** Node Follower module of YAPF for ships */
|
||||
template <class Types>
|
||||
class CYapfFollowShipT
|
||||
{
|
||||
public:
|
||||
typedef typename Types::Tpf Tpf; ///< the pathfinder class (derived from THIS class)
|
||||
typedef typename Types::Tpf Tpf; ///< the pathfinder class (derived from THIS class).
|
||||
typedef typename Types::TrackFollower TrackFollower;
|
||||
typedef typename Types::NodeList::Titem Node; ///< this will be our node type
|
||||
typedef typename Node::Key Key; ///< key to hash tables
|
||||
typedef typename Types::NodeList::Titem Node; ///< this will be our node type.
|
||||
typedef typename Node::Key Key; ///< key to hash tables.
|
||||
|
||||
protected:
|
||||
/** to access inherited path finder */
|
||||
inline Tpf &Yapf()
|
||||
{
|
||||
return *static_cast<Tpf *>(this);
|
||||
return *static_cast<Tpf*>(this);
|
||||
}
|
||||
|
||||
std::vector<WaterRegionDesc> m_water_region_corridor;
|
||||
|
||||
public:
|
||||
/**
|
||||
* Called by YAPF to move from the given node to the next tile. For each
|
||||
|
@ -126,23 +151,57 @@ public:
|
|||
{
|
||||
TrackFollower F(Yapf().GetVehicle());
|
||||
if (F.Follow(old_node.m_key.m_tile, old_node.m_key.m_td)) {
|
||||
Yapf().AddMultipleNodes(&old_node, F);
|
||||
if (m_water_region_corridor.empty()
|
||||
|| std::find(m_water_region_corridor.begin(), m_water_region_corridor.end(),
|
||||
GetWaterRegionInfo(F.m_new_tile)) != m_water_region_corridor.end()) {
|
||||
Yapf().AddMultipleNodes(&old_node, F);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** return debug report character to identify the transportation type */
|
||||
/** Restricts the search by creating corridor or water regions through which the ship is allowed to travel. */
|
||||
inline void RestrictSearch(const std::vector<WaterRegionPatchDesc> &path)
|
||||
{
|
||||
m_water_region_corridor.clear();
|
||||
for (const WaterRegionPatchDesc &path_entry : path) m_water_region_corridor.push_back(path_entry);
|
||||
}
|
||||
|
||||
/** Return debug report character to identify the transportation type. */
|
||||
inline char TransportTypeChar() const
|
||||
{
|
||||
return 'w';
|
||||
}
|
||||
|
||||
/** Creates a random path, avoids 90 degree turns. */
|
||||
static Trackdir CreateRandomPath(const Ship *v, TileIndex tile, Trackdir dir, ShipPathCache &path_cache, int path_length)
|
||||
{
|
||||
for (int i = 0; i < path_length; ++i) {
|
||||
TrackFollower F(v);
|
||||
if (F.Follow(tile, dir)) {
|
||||
tile = F.m_new_tile;
|
||||
TrackdirBits dirs = F.m_new_td_bits & ~TrackdirCrossesTrackdirs(dir);
|
||||
const int strip_amount = _random.Next(CountBits(dirs));
|
||||
for (int s = 0; s < strip_amount; ++s) RemoveFirstTrackdir(&dirs);
|
||||
dir = FindFirstTrackdir(dirs);
|
||||
if (dir == INVALID_TRACKDIR) break;
|
||||
path_cache.push_back(dir);
|
||||
}
|
||||
}
|
||||
|
||||
if (path_cache.empty()) return INVALID_TRACKDIR;
|
||||
|
||||
const Trackdir result = path_cache.front();
|
||||
path_cache.pop_front();
|
||||
return result;
|
||||
}
|
||||
|
||||
static Trackdir ChooseShipTrack(const Ship *v, TileIndex tile, DiagDirection enterdir, TrackBits tracks, bool &path_found, ShipPathCache &path_cache)
|
||||
{
|
||||
/* handle special case - when next tile is destination tile */
|
||||
/* Handle special case - when next tile is destination tile. */
|
||||
if (tile == v->dest_tile) {
|
||||
/* convert tracks to trackdirs */
|
||||
/* Convert tracks to trackdirs */
|
||||
TrackdirBits trackdirs = TrackBitsToTrackdirBits(tracks);
|
||||
/* limit to trackdirs reachable from enterdir */
|
||||
/* Limit to trackdirs reachable from enterdir. */
|
||||
trackdirs &= DiagdirReachesTrackdirs(enterdir);
|
||||
|
||||
/* use vehicle's current direction if that's possible, otherwise use first usable one. */
|
||||
|
@ -150,68 +209,91 @@ public:
|
|||
return (HasTrackdir(trackdirs, veh_dir)) ? veh_dir : (Trackdir)FindFirstBit2x64(trackdirs);
|
||||
}
|
||||
|
||||
/* move back to the old tile/trackdir (where ship is coming from) */
|
||||
/* Move back to the old tile/trackdir (where ship is coming from). */
|
||||
TileIndex src_tile = TileAddByDiagDir(tile, ReverseDiagDir(enterdir));
|
||||
Trackdir trackdir = v->GetVehicleTrackdir();
|
||||
assert(IsValidTrackdir(trackdir));
|
||||
|
||||
/* convert origin trackdir to TrackdirBits */
|
||||
/* Convert origin trackdir to TrackdirBits. */
|
||||
TrackdirBits trackdirs = TrackdirToTrackdirBits(trackdir);
|
||||
|
||||
/* create pathfinder instance */
|
||||
Tpf pf;
|
||||
/* set origin and destination nodes */
|
||||
pf.SetOrigin(src_tile, trackdirs);
|
||||
pf.SetDestination(v);
|
||||
/* find best path */
|
||||
path_found = pf.FindPath(v);
|
||||
|
||||
Trackdir next_trackdir = INVALID_TRACKDIR; // this would mean "path not found"
|
||||
|
||||
Node *pNode = pf.GetBestNode();
|
||||
if (pNode != nullptr) {
|
||||
uint steps = 0;
|
||||
for (Node *n = pNode; n->m_parent != nullptr; n = n->m_parent) steps++;
|
||||
uint skip = 0;
|
||||
if (path_found) skip = YAPF_SHIP_PATH_CACHE_LENGTH / 2;
|
||||
|
||||
/* walk through the path back to the origin */
|
||||
Node *pPrevNode = nullptr;
|
||||
while (pNode->m_parent != nullptr) {
|
||||
steps--;
|
||||
/* Skip tiles at end of path near destination. */
|
||||
if (skip > 0) skip--;
|
||||
if (skip == 0 && steps > 0 && steps < YAPF_SHIP_PATH_CACHE_LENGTH) {
|
||||
path_cache.push_front(pNode->GetTrackdir());
|
||||
}
|
||||
pPrevNode = pNode;
|
||||
pNode = pNode->m_parent;
|
||||
}
|
||||
/* return trackdir from the best next node (direct child of origin) */
|
||||
Node &best_next_node = *pPrevNode;
|
||||
assert(best_next_node.GetTile() == tile);
|
||||
next_trackdir = best_next_node.GetTrackdir();
|
||||
/* remove last element for the special case when tile == dest_tile */
|
||||
if (path_found && !path_cache.empty()) path_cache.pop_back();
|
||||
const std::vector<WaterRegionPatchDesc> high_level_path = YapfShipFindWaterRegionPath(v, tile, NUMBER_OR_WATER_REGIONS_LOOKAHEAD + 1);
|
||||
if (high_level_path.empty()) {
|
||||
path_found = false;
|
||||
/* Make the ship move around aimlessly. This prevents repeated pathfinder calls and clearly indicates that the ship is lost. */
|
||||
return CreateRandomPath(v, src_tile, trackdir, path_cache, SHIP_LOST_PATH_LENGTH);
|
||||
}
|
||||
return next_trackdir;
|
||||
|
||||
/* Try one time without restricting the search area, which generally results in better and more natural looking paths.
|
||||
* However the pathfinder can hit the node limit in certain situations such as long aqueducts or maze-like terrain.
|
||||
* If that happens we run the pathfinder again, but restricted only to the regions provided by the region pathfinder. */
|
||||
for (int attempt = 0; attempt < 2; ++attempt) {
|
||||
Tpf pf(MAX_SHIP_PF_NODES);
|
||||
|
||||
/* Set origin and destination nodes */
|
||||
pf.SetOrigin(src_tile, trackdirs);
|
||||
pf.SetDestination(v);
|
||||
const bool is_intermediate_destination = static_cast<int>(high_level_path.size()) >= NUMBER_OR_WATER_REGIONS_LOOKAHEAD + 1;
|
||||
if (is_intermediate_destination) pf.SetIntermediateDestination(high_level_path.back());
|
||||
|
||||
/* Restrict the search area to prevent the low level pathfinder from expanding too many nodes. This can happen
|
||||
* when the terrain is very "maze-like" or when the high level path "teleports" via a very long aqueduct. */
|
||||
if (attempt > 0) pf.RestrictSearch(high_level_path);
|
||||
|
||||
/* Find best path. */
|
||||
path_found = pf.FindPath(v);
|
||||
Node *node = pf.GetBestNode();
|
||||
if (attempt == 0 && !path_found) continue; // Try again with restricted search area.
|
||||
if (!path_found || !node) return INVALID_TRACKDIR;
|
||||
|
||||
/* Return only the path within the current water region if an intermediate destination was returned. If not, cache the entire path
|
||||
* to the final destination tile. The low-level pathfinder might actually prefer a different docking tile in a nearby region. Without
|
||||
* caching the full path the ship can get stuck in a loop. */
|
||||
const WaterRegionPatchDesc end_water_patch = GetWaterRegionPatchInfo(node->GetTile());
|
||||
const WaterRegionPatchDesc start_water_patch = GetWaterRegionPatchInfo(tile);
|
||||
while (node->m_parent) {
|
||||
const WaterRegionPatchDesc node_water_patch = GetWaterRegionPatchInfo(node->GetTile());
|
||||
if (node_water_patch == start_water_patch || (!is_intermediate_destination && node_water_patch != end_water_patch)) {
|
||||
path_cache.push_front(node->GetTrackdir());
|
||||
}
|
||||
node = node->m_parent;
|
||||
}
|
||||
assert(!path_cache.empty());
|
||||
|
||||
/* Take out the last trackdir as the result. */
|
||||
const Trackdir result = path_cache.front();
|
||||
path_cache.pop_front();
|
||||
|
||||
/* Clear path cache when in final water region patch. This is to allow ships to spread over different docking tiles dynamically. */
|
||||
if (start_water_patch == end_water_patch) path_cache.clear();
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
return INVALID_TRACKDIR;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether a ship should reverse to reach its destination.
|
||||
* Called when leaving depot.
|
||||
* @param v Ship
|
||||
* @param tile Current position
|
||||
* @param td1 Forward direction
|
||||
* @param td2 Reverse direction
|
||||
* @param trackdir [out] the best of all possible reversed trackdirs
|
||||
* @return true if the reverse direction is better
|
||||
* @param v Ship.
|
||||
* @param tile Current position.
|
||||
* @param td1 Forward direction.
|
||||
* @param td2 Reverse direction.
|
||||
* @param trackdir [out] the best of all possible reversed trackdirs.
|
||||
* @return true if the reverse direction is better.
|
||||
*/
|
||||
static bool CheckShipReverse(const Ship *v, TileIndex tile, Trackdir td1, Trackdir td2, Trackdir *trackdir)
|
||||
{
|
||||
/* create pathfinder instance */
|
||||
Tpf pf;
|
||||
/* set origin and destination nodes */
|
||||
const std::vector<WaterRegionPatchDesc> high_level_path = YapfShipFindWaterRegionPath(v, tile, NUMBER_OR_WATER_REGIONS_LOOKAHEAD + 1);
|
||||
if (high_level_path.empty()) {
|
||||
if (trackdir) *trackdir = INVALID_TRACKDIR;
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Create pathfinder instance. */
|
||||
Tpf pf(MAX_SHIP_PF_NODES);
|
||||
/* Set origin and destination nodes. */
|
||||
if (trackdir == nullptr) {
|
||||
pf.SetOrigin(tile, TrackdirToTrackdirBits(td1) | TrackdirToTrackdirBits(td2));
|
||||
} else {
|
||||
|
@ -220,14 +302,16 @@ public:
|
|||
pf.SetOrigin(tile, rtds);
|
||||
}
|
||||
pf.SetDestination(v);
|
||||
/* find best path */
|
||||
if (high_level_path.size() > 1) pf.SetIntermediateDestination(high_level_path.back());
|
||||
pf.RestrictSearch(high_level_path);
|
||||
|
||||
/* Find best path. */
|
||||
if (!pf.FindPath(v)) return false;
|
||||
|
||||
Node *pNode = pf.GetBestNode();
|
||||
if (pNode == nullptr) return false;
|
||||
|
||||
/* path was found
|
||||
* walk through the path back to the origin */
|
||||
/* Path was found, walk through the path back to the origin. */
|
||||
while (pNode->m_parent != nullptr) {
|
||||
pNode = pNode->m_parent;
|
||||
}
|
||||
|
@ -242,21 +326,20 @@ public:
|
|||
}
|
||||
};
|
||||
|
||||
/** Cost Provider module of YAPF for ships */
|
||||
/** Cost Provider module of YAPF for ships. */
|
||||
template <class Types>
|
||||
class CYapfCostShipT
|
||||
{
|
||||
public:
|
||||
typedef typename Types::Tpf Tpf; ///< the pathfinder class (derived from THIS class)
|
||||
typedef typename Types::Tpf Tpf; ///< the pathfinder class (derived from THIS class).
|
||||
typedef typename Types::TrackFollower TrackFollower;
|
||||
typedef typename Types::NodeList::Titem Node; ///< this will be our node type
|
||||
typedef typename Node::Key Key; ///< key to hash tables
|
||||
typedef typename Types::NodeList::Titem Node; ///< this will be our node type.
|
||||
typedef typename Node::Key Key; ///< key to hash tables.
|
||||
|
||||
protected:
|
||||
/** to access inherited path finder */
|
||||
Tpf &Yapf()
|
||||
{
|
||||
return *static_cast<Tpf *>(this);
|
||||
return *static_cast<Tpf*>(this);
|
||||
}
|
||||
|
||||
public:
|
||||
|
@ -266,10 +349,10 @@ public:
|
|||
assert(IsValidTrackdir(td2));
|
||||
|
||||
if (HasTrackdir(TrackdirCrossesTrackdirs(td1), td2)) {
|
||||
/* 90-deg curve penalty */
|
||||
/* 90-deg curve penalty. */
|
||||
return Yapf().PfGetSettings().ship_curve90_penalty;
|
||||
} else if (td2 != NextTrackdir(td1)) {
|
||||
/* 45-deg curve penalty */
|
||||
/* 45-deg curve penalty. */
|
||||
return Yapf().PfGetSettings().ship_curve45_penalty;
|
||||
}
|
||||
return 0;
|
||||
|
@ -277,7 +360,7 @@ public:
|
|||
|
||||
static Vehicle *CountShipProc(Vehicle *v, void *data)
|
||||
{
|
||||
uint *count = (uint *)data;
|
||||
uint *count = (uint*)data;
|
||||
/* Ignore other vehicles (aircraft) and ships inside depot. */
|
||||
if (v->type == VEH_SHIP && (v->vehstatus & VS_HIDDEN) == 0) (*count)++;
|
||||
|
||||
|
@ -286,18 +369,18 @@ public:
|
|||
|
||||
/**
|
||||
* Called by YAPF to calculate the cost from the origin to the given node.
|
||||
* Calculates only the cost of given node, adds it to the parent node cost
|
||||
* and stores the result into Node::m_cost member
|
||||
* Calculates only the cost of given node, adds it to the parent node cost
|
||||
* and stores the result into Node::m_cost member.
|
||||
*/
|
||||
inline bool PfCalcCost(Node &n, const TrackFollower *tf)
|
||||
{
|
||||
/* base tile cost depending on distance */
|
||||
/* Base tile cost depending on distance. */
|
||||
int c = IsDiagonalTrackdir(n.GetTrackdir()) ? YAPF_TILE_LENGTH : YAPF_TILE_CORNER_LENGTH;
|
||||
/* additional penalty for curves */
|
||||
/* Additional penalty for curves. */
|
||||
c += CurveCost(n.m_parent->GetTrackdir(), n.GetTrackdir());
|
||||
|
||||
if (IsDockingTile(n.GetTile())) {
|
||||
/* Check docking tile for occupancy */
|
||||
/* Check docking tile for occupancy. */
|
||||
uint count = 0;
|
||||
HasVehicleOnPos(n.GetTile(), &count, &CountShipProc);
|
||||
c += count * 3 * YAPF_TILE_LENGTH;
|
||||
|
@ -311,7 +394,7 @@ public:
|
|||
byte speed_frac = (GetEffectiveWaterClass(n.GetTile()) == WATER_CLASS_SEA) ? svi->ocean_speed_frac : svi->canal_speed_frac;
|
||||
if (speed_frac > 0) c += YAPF_TILE_LENGTH * (1 + tf->m_tiles_skipped) * speed_frac / (256 - speed_frac);
|
||||
|
||||
/* apply it */
|
||||
/* Apply it. */
|
||||
n.m_cost = n.m_parent->m_cost + c;
|
||||
return true;
|
||||
}
|
||||
|
@ -319,48 +402,35 @@ public:
|
|||
|
||||
/**
|
||||
* Config struct of YAPF for ships.
|
||||
* Defines all 6 base YAPF modules as classes providing services for CYapfBaseT.
|
||||
* Defines all 6 base YAPF modules as classes providing services for CYapfBaseT.
|
||||
*/
|
||||
template <class Tpf_, class Ttrack_follower, class Tnode_list>
|
||||
struct CYapfShip_TypesT
|
||||
{
|
||||
/** Types - shortcut for this struct type */
|
||||
typedef CYapfShip_TypesT<Tpf_, Ttrack_follower, Tnode_list> Types;
|
||||
typedef CYapfShip_TypesT<Tpf_, Ttrack_follower, Tnode_list> Types; ///< Shortcut for this struct type.
|
||||
typedef Tpf_ Tpf; ///< Pathfinder type.
|
||||
typedef Ttrack_follower TrackFollower; ///< Track follower helper class.
|
||||
typedef Tnode_list NodeList;
|
||||
typedef Ship VehicleType;
|
||||
|
||||
/** Tpf - pathfinder type */
|
||||
typedef Tpf_ Tpf;
|
||||
/** track follower helper class */
|
||||
typedef Ttrack_follower TrackFollower;
|
||||
/** node list type */
|
||||
typedef Tnode_list NodeList;
|
||||
typedef Ship VehicleType;
|
||||
/** pathfinder components (modules) */
|
||||
typedef CYapfBaseT<Types> PfBase; // base pathfinder class
|
||||
typedef CYapfFollowShipT<Types> PfFollow; // node follower
|
||||
typedef CYapfOriginTileT<Types> PfOrigin; // origin provider
|
||||
typedef CYapfDestinationTileWaterT<Types> PfDestination; // destination/distance provider
|
||||
typedef CYapfSegmentCostCacheNoneT<Types> PfCache; // segment cost cache provider
|
||||
typedef CYapfCostShipT<Types> PfCost; // cost provider
|
||||
/** Pathfinder components (modules). */
|
||||
typedef CYapfBaseT<Types> PfBase; ///< Base pathfinder class.
|
||||
typedef CYapfFollowShipT<Types> PfFollow; ///< Node follower.
|
||||
typedef CYapfOriginTileT<Types> PfOrigin; ///< Origin provider.
|
||||
typedef CYapfDestinationTileWaterT<Types> PfDestination; ///< Destination/distance provider.
|
||||
typedef CYapfSegmentCostCacheNoneT<Types> PfCache; ///< Segment cost cache provider.
|
||||
typedef CYapfCostShipT<Types> PfCost; ///< Cost provider.
|
||||
};
|
||||
|
||||
/* YAPF type 1 - uses TileIndex/Trackdir as Node key */
|
||||
struct CYapfShip1 : CYapfT<CYapfShip_TypesT<CYapfShip1, CFollowTrackWater , CShipNodeListTrackDir> > {};
|
||||
/* YAPF type 2 - uses TileIndex/DiagDirection as Node key */
|
||||
struct CYapfShip2 : CYapfT<CYapfShip_TypesT<CYapfShip2, CFollowTrackWater , CShipNodeListExitDir > > {};
|
||||
struct CYapfShip : CYapfT<CYapfShip_TypesT<CYapfShip, CFollowTrackWater, CShipNodeListExitDir > >
|
||||
{
|
||||
explicit CYapfShip(int max_nodes) { m_max_search_nodes = max_nodes; }
|
||||
};
|
||||
|
||||
/** Ship controller helper - path finder invoker */
|
||||
/** Ship controller helper - path finder invoker. */
|
||||
Track YapfShipChooseTrack(const Ship *v, TileIndex tile, DiagDirection enterdir, TrackBits tracks, bool &path_found, ShipPathCache &path_cache)
|
||||
{
|
||||
/* default is YAPF type 2 */
|
||||
typedef Trackdir (*PfnChooseShipTrack)(const Ship*, TileIndex, DiagDirection, TrackBits, bool &path_found, ShipPathCache &path_cache);
|
||||
PfnChooseShipTrack pfnChooseShipTrack = CYapfShip2::ChooseShipTrack; // default: ExitDir
|
||||
|
||||
/* check if non-default YAPF type needed */
|
||||
if (_settings_game.pf.yapf.disable_node_optimization) {
|
||||
pfnChooseShipTrack = &CYapfShip1::ChooseShipTrack; // Trackdir
|
||||
}
|
||||
|
||||
Trackdir td_ret = pfnChooseShipTrack(v, tile, enterdir, tracks, path_found, path_cache);
|
||||
Trackdir td_ret = CYapfShip::ChooseShipTrack(v, tile, enterdir, tracks, path_found, path_cache);
|
||||
return (td_ret != INVALID_TRACKDIR) ? TrackdirToTrack(td_ret) : INVALID_TRACK;
|
||||
}
|
||||
|
||||
|
@ -369,16 +439,5 @@ bool YapfShipCheckReverse(const Ship *v, Trackdir *trackdir)
|
|||
Trackdir td = v->GetVehicleTrackdir();
|
||||
Trackdir td_rev = ReverseTrackdir(td);
|
||||
TileIndex tile = v->tile;
|
||||
|
||||
typedef bool (*PfnCheckReverseShip)(const Ship*, TileIndex, Trackdir, Trackdir, Trackdir*);
|
||||
PfnCheckReverseShip pfnCheckReverseShip = CYapfShip2::CheckShipReverse; // default: ExitDir
|
||||
|
||||
/* check if non-default YAPF type needed */
|
||||
if (_settings_game.pf.yapf.disable_node_optimization) {
|
||||
pfnCheckReverseShip = &CYapfShip1::CheckShipReverse; // Trackdir
|
||||
}
|
||||
|
||||
bool reverse = pfnCheckReverseShip(v, tile, td, td_rev, trackdir);
|
||||
|
||||
return reverse;
|
||||
return CYapfShip::CheckShipReverse(v, tile, td, td_rev, trackdir);
|
||||
}
|
||||
|
|
|
@ -0,0 +1,314 @@
|
|||
/*
|
||||
* This file is part of OpenTTD.
|
||||
* OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
|
||||
* OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
* See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/** @file yapf_ship_regions.cpp Implementation of YAPF for water regions, which are used for finding intermediate ship destinations. */
|
||||
|
||||
#include "../../stdafx.h"
|
||||
#include "../../ship.h"
|
||||
|
||||
#include "yapf.hpp"
|
||||
#include "yapf_ship_regions.h"
|
||||
#include "../water_regions.h"
|
||||
|
||||
#include "../../safeguards.h"
|
||||
|
||||
constexpr int DIRECT_NEIGHBOR_COST = 100;
|
||||
constexpr int NODES_PER_REGION = 4;
|
||||
constexpr int MAX_NUMBER_OF_NODES = 65536;
|
||||
|
||||
/** Yapf Node Key that represents a single patch of interconnected water within a water region. */
|
||||
struct CYapfRegionPatchNodeKey {
|
||||
WaterRegionPatchDesc m_water_region_patch;
|
||||
|
||||
static_assert(sizeof(TWaterRegionPatchLabel) == sizeof(byte)); // Important for the hash calculation.
|
||||
|
||||
inline void Set(const WaterRegionPatchDesc &water_region_patch)
|
||||
{
|
||||
m_water_region_patch = water_region_patch;
|
||||
}
|
||||
|
||||
inline int CalcHash() const { return m_water_region_patch.label | GetWaterRegionIndex(m_water_region_patch) << 8; }
|
||||
inline bool operator==(const CYapfRegionPatchNodeKey &other) const { return CalcHash() == other.CalcHash(); }
|
||||
};
|
||||
|
||||
inline uint ManhattanDistance(const CYapfRegionPatchNodeKey &a, const CYapfRegionPatchNodeKey &b)
|
||||
{
|
||||
return (std::abs(a.m_water_region_patch.x - b.m_water_region_patch.x) + std::abs(a.m_water_region_patch.y - b.m_water_region_patch.y)) * DIRECT_NEIGHBOR_COST;
|
||||
}
|
||||
|
||||
/** Yapf Node for water regions. */
|
||||
template <class Tkey_>
|
||||
struct CYapfRegionNodeT {
|
||||
typedef Tkey_ Key;
|
||||
typedef CYapfRegionNodeT<Tkey_> Node;
|
||||
|
||||
Tkey_ m_key;
|
||||
Node *m_hash_next;
|
||||
Node *m_parent;
|
||||
int m_cost;
|
||||
int m_estimate;
|
||||
|
||||
inline void Set(Node *parent, const WaterRegionPatchDesc &water_region_patch)
|
||||
{
|
||||
m_key.Set(water_region_patch);
|
||||
m_hash_next = nullptr;
|
||||
m_parent = parent;
|
||||
m_cost = 0;
|
||||
m_estimate = 0;
|
||||
}
|
||||
|
||||
inline void Set(Node *parent, const Key &key)
|
||||
{
|
||||
Set(parent, key.m_water_region_patch);
|
||||
}
|
||||
|
||||
DiagDirection GetDiagDirFromParent() const
|
||||
{
|
||||
if (!m_parent) return INVALID_DIAGDIR;
|
||||
const int dx = m_key.m_water_region_patch.x - m_parent->m_key.m_water_region_patch.x;
|
||||
const int dy = m_key.m_water_region_patch.y - m_parent->m_key.m_water_region_patch.y;
|
||||
if (dx > 0 && dy == 0) return DIAGDIR_SW;
|
||||
if (dx < 0 && dy == 0) return DIAGDIR_NE;
|
||||
if (dx == 0 && dy > 0) return DIAGDIR_SE;
|
||||
if (dx == 0 && dy < 0) return DIAGDIR_NW;
|
||||
return INVALID_DIAGDIR;
|
||||
}
|
||||
|
||||
inline Node *GetHashNext() { return m_hash_next; }
|
||||
inline void SetHashNext(Node *pNext) { m_hash_next = pNext; }
|
||||
inline const Tkey_ &GetKey() const { return m_key; }
|
||||
inline int GetCost() { return m_cost; }
|
||||
inline int GetCostEstimate() { return m_estimate; }
|
||||
inline bool operator<(const Node &other) const { return m_estimate < other.m_estimate; }
|
||||
};
|
||||
|
||||
/** YAPF origin for water regions. */
|
||||
template <class Types>
|
||||
class CYapfOriginRegionT
|
||||
{
|
||||
public:
|
||||
typedef typename Types::Tpf Tpf; ///< The pathfinder class (derived from THIS class).
|
||||
typedef typename Types::NodeList::Titem Node; ///< This will be our node type.
|
||||
typedef typename Node::Key Key; ///< Key to hash tables.
|
||||
|
||||
protected:
|
||||
inline Tpf &Yapf() { return *static_cast<Tpf*>(this); }
|
||||
|
||||
private:
|
||||
std::vector<CYapfRegionPatchNodeKey> m_origin_keys;
|
||||
|
||||
public:
|
||||
void AddOrigin(const WaterRegionPatchDesc &water_region_patch)
|
||||
{
|
||||
if (!HasOrigin(water_region_patch)) m_origin_keys.push_back(CYapfRegionPatchNodeKey{ water_region_patch });
|
||||
}
|
||||
|
||||
bool HasOrigin(const WaterRegionPatchDesc &water_region_patch)
|
||||
{
|
||||
return std::find(m_origin_keys.begin(), m_origin_keys.end(), CYapfRegionPatchNodeKey{ water_region_patch }) != m_origin_keys.end();
|
||||
}
|
||||
|
||||
void PfSetStartupNodes()
|
||||
{
|
||||
for (const CYapfRegionPatchNodeKey &origin_key : m_origin_keys) {
|
||||
Node &node = Yapf().CreateNewNode();
|
||||
node.Set(nullptr, origin_key);
|
||||
Yapf().AddStartupNode(node);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/** YAPF destination provider for water regions. */
|
||||
template <class Types>
|
||||
class CYapfDestinationRegionT
|
||||
{
|
||||
public:
|
||||
typedef typename Types::Tpf Tpf; ///< The pathfinder class (derived from THIS class).
|
||||
typedef typename Types::NodeList::Titem Node; ///< This will be our node type.
|
||||
typedef typename Node::Key Key; ///< Key to hash tables.
|
||||
|
||||
protected:
|
||||
Key m_dest;
|
||||
|
||||
public:
|
||||
void SetDestination(const WaterRegionPatchDesc &water_region_patch)
|
||||
{
|
||||
m_dest.Set(water_region_patch);
|
||||
}
|
||||
|
||||
protected:
|
||||
Tpf &Yapf() { return *static_cast<Tpf*>(this); }
|
||||
|
||||
public:
|
||||
inline bool PfDetectDestination(Node &n) const
|
||||
{
|
||||
return n.m_key == m_dest;
|
||||
}
|
||||
|
||||
inline bool PfCalcEstimate(Node &n)
|
||||
{
|
||||
if (PfDetectDestination(n)) {
|
||||
n.m_estimate = n.m_cost;
|
||||
return true;
|
||||
}
|
||||
|
||||
n.m_estimate = n.m_cost + ManhattanDistance(n.m_key, m_dest);
|
||||
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
/** YAPF node following for water region pathfinding. */
|
||||
template <class Types>
|
||||
class CYapfFollowRegionT
|
||||
{
|
||||
public:
|
||||
typedef typename Types::Tpf Tpf; ///< The pathfinder class (derived from THIS class).
|
||||
typedef typename Types::TrackFollower TrackFollower;
|
||||
typedef typename Types::NodeList::Titem Node; ///< This will be our node type.
|
||||
typedef typename Node::Key Key; ///< Key to hash tables.
|
||||
|
||||
protected:
|
||||
inline Tpf &Yapf() { return *static_cast<Tpf*>(this); }
|
||||
|
||||
public:
|
||||
inline void PfFollowNode(Node &old_node)
|
||||
{
|
||||
TVisitWaterRegionPatchCallBack visitFunc = [&](const WaterRegionPatchDesc &water_region_patch)
|
||||
{
|
||||
Node &node = Yapf().CreateNewNode();
|
||||
node.Set(&old_node, water_region_patch);
|
||||
Yapf().AddNewNode(node, TrackFollower{});
|
||||
};
|
||||
VisitWaterRegionPatchNeighbors(old_node.m_key.m_water_region_patch, visitFunc);
|
||||
}
|
||||
|
||||
inline char TransportTypeChar() const { return '^'; }
|
||||
|
||||
static std::vector<WaterRegionPatchDesc> FindWaterRegionPath(const Ship *v, TileIndex start_tile, int max_returned_path_length)
|
||||
{
|
||||
const WaterRegionPatchDesc start_water_region_patch = GetWaterRegionPatchInfo(start_tile);
|
||||
|
||||
/* We reserve 4 nodes (patches) per water region. The vast majority of water regions have 1 or 2 regions so this should be a pretty
|
||||
* safe limit. We cap the limit at 65536 which is at a region size of 16x16 is equivalent to one node per region for a 4096x4096 map. */
|
||||
Tpf pf(std::min(static_cast<int>(Map::Size() * NODES_PER_REGION) / WATER_REGION_NUMBER_OF_TILES, MAX_NUMBER_OF_NODES));
|
||||
pf.SetDestination(start_water_region_patch);
|
||||
|
||||
if (v->current_order.IsType(OT_GOTO_STATION)) {
|
||||
DestinationID station_id = v->current_order.GetDestination();
|
||||
const BaseStation *station = BaseStation::Get(station_id);
|
||||
TileArea tile_area;
|
||||
station->GetTileArea(&tile_area, STATION_DOCK);
|
||||
for (const auto &tile : tile_area) {
|
||||
if (IsDockingTile(tile) && IsShipDestinationTile(tile, station_id)) {
|
||||
pf.AddOrigin(GetWaterRegionPatchInfo(tile));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
TileIndex tile = v->dest_tile;
|
||||
pf.AddOrigin(GetWaterRegionPatchInfo(tile));
|
||||
}
|
||||
|
||||
/* If origin and destination are the same we simply return that water patch. */
|
||||
std::vector<WaterRegionPatchDesc> path = { start_water_region_patch };
|
||||
path.reserve(max_returned_path_length);
|
||||
if (pf.HasOrigin(start_water_region_patch)) return path;
|
||||
|
||||
/* Find best path. */
|
||||
if (!pf.FindPath(v)) return {}; // Path not found.
|
||||
|
||||
Node *node = pf.GetBestNode();
|
||||
for (int i = 0; i < max_returned_path_length - 1; ++i) {
|
||||
if (node != nullptr) {
|
||||
node = node->m_parent;
|
||||
if (node != nullptr) path.push_back(node->m_key.m_water_region_patch);
|
||||
}
|
||||
}
|
||||
|
||||
assert(!path.empty());
|
||||
return path;
|
||||
}
|
||||
};
|
||||
|
||||
/** Cost Provider of YAPF for water regions. */
|
||||
template <class Types>
|
||||
class CYapfCostRegionT
|
||||
{
|
||||
public:
|
||||
typedef typename Types::Tpf Tpf; ///< The pathfinder class (derived from THIS class).
|
||||
typedef typename Types::TrackFollower TrackFollower;
|
||||
typedef typename Types::NodeList::Titem Node; ///< This will be our node type.
|
||||
typedef typename Node::Key Key; ///< Key to hash tables.
|
||||
|
||||
protected:
|
||||
/** To access inherited path finder. */
|
||||
Tpf &Yapf() { return *static_cast<Tpf*>(this); }
|
||||
|
||||
public:
|
||||
/**
|
||||
* Called by YAPF to calculate the cost from the origin to the given node.
|
||||
* Calculates only the cost of given node, adds it to the parent node cost
|
||||
* and stores the result into Node::m_cost member.
|
||||
*/
|
||||
inline bool PfCalcCost(Node &n, const TrackFollower *)
|
||||
{
|
||||
n.m_cost = n.m_parent->m_cost + ManhattanDistance(n.m_key, n.m_parent->m_key);
|
||||
|
||||
/* Incentivise zigzagging by adding a slight penalty when the search continues in the same direction. */
|
||||
Node *grandparent = n.m_parent->m_parent;
|
||||
if (grandparent != nullptr) {
|
||||
const DiagDirDiff dir_diff = DiagDirDifference(n.m_parent->GetDiagDirFromParent(), n.GetDiagDirFromParent());
|
||||
if (dir_diff != DIAGDIRDIFF_90LEFT && dir_diff != DIAGDIRDIFF_90RIGHT) n.m_cost += 1;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
/* We don't need a follower but YAPF requires one. */
|
||||
struct DummyFollower : public CFollowTrackWater {};
|
||||
|
||||
/**
|
||||
* Config struct of YAPF for route planning.
|
||||
* Defines all 6 base YAPF modules as classes providing services for CYapfBaseT.
|
||||
*/
|
||||
template <class Tpf_, class Tnode_list>
|
||||
struct CYapfRegion_TypesT
|
||||
{
|
||||
typedef CYapfRegion_TypesT<Tpf_, Tnode_list> Types; ///< Shortcut for this struct type.
|
||||
typedef Tpf_ Tpf; ///< Pathfinder type.
|
||||
typedef DummyFollower TrackFollower; ///< Track follower helper class
|
||||
typedef Tnode_list NodeList;
|
||||
typedef Ship VehicleType;
|
||||
|
||||
/** Pathfinder components (modules). */
|
||||
typedef CYapfBaseT<Types> PfBase; ///< Base pathfinder class.
|
||||
typedef CYapfFollowRegionT<Types> PfFollow; ///< Node follower.
|
||||
typedef CYapfOriginRegionT<Types> PfOrigin; ///< Origin provider.
|
||||
typedef CYapfDestinationRegionT<Types> PfDestination; ///< Destination/distance provider.
|
||||
typedef CYapfSegmentCostCacheNoneT<Types> PfCache; ///< Segment cost cache provider.
|
||||
typedef CYapfCostRegionT<Types> PfCost; ///< Cost provider.
|
||||
};
|
||||
|
||||
typedef CNodeList_HashTableT<CYapfRegionNodeT<CYapfRegionPatchNodeKey>, 12, 12> CRegionNodeListWater;
|
||||
|
||||
struct CYapfRegionWater : CYapfT<CYapfRegion_TypesT<CYapfRegionWater, CRegionNodeListWater>>
|
||||
{
|
||||
explicit CYapfRegionWater(int max_nodes) { m_max_search_nodes = max_nodes; }
|
||||
};
|
||||
|
||||
/**
|
||||
* Finds a path at the water region level. Note that the starting region is always included if the path was found.
|
||||
* @param v The ship to find a path for.
|
||||
* @param start_tile The tile to start searching from.
|
||||
* @param max_returned_path_length The maximum length of the path that will be returned.
|
||||
* @returns A path of water region patches, or an empty vector if no path was found.
|
||||
*/
|
||||
std::vector<WaterRegionPatchDesc> YapfShipFindWaterRegionPath(const Ship *v, TileIndex start_tile, int max_returned_path_length)
|
||||
{
|
||||
return CYapfRegionWater::FindWaterRegionPath(v, start_tile, max_returned_path_length);
|
||||
}
|
|
@ -0,0 +1,21 @@
|
|||
/*
|
||||
* This file is part of OpenTTD.
|
||||
* OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
|
||||
* OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
* See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/** @file yapf_ship_regions.h Implementation of YAPF for water regions, which are used for finding intermediate ship destinations. */
|
||||
|
||||
#ifndef YAPF_SHIP_REGIONS_H
|
||||
#define YAPF_SHIP_REGIONS_H
|
||||
|
||||
#include "../../stdafx.h"
|
||||
#include "../../tile_type.h"
|
||||
#include "../water_regions.h"
|
||||
|
||||
struct Ship;
|
||||
|
||||
std::vector<WaterRegionPatchDesc> YapfShipFindWaterRegionPath(const Ship *v, TileIndex start_tile, int max_returned_path_length);
|
||||
|
||||
#endif /* YAPF_SHIP_REGIONS_H */
|
|
@ -44,4 +44,5 @@ add_files(
|
|||
town_sl.cpp
|
||||
vehicle_sl.cpp
|
||||
waypoint_sl.cpp
|
||||
water_regions_sl.cpp
|
||||
)
|
||||
|
|
|
@ -61,6 +61,7 @@
|
|||
#include "../timer/timer.h"
|
||||
#include "../timer/timer_game_calendar.h"
|
||||
#include "../timer/timer_game_tick.h"
|
||||
#include "../pathfinder/water_regions.h"
|
||||
|
||||
#include "saveload_internal.h"
|
||||
|
||||
|
@ -3296,6 +3297,8 @@ bool AfterLoadGame()
|
|||
}
|
||||
}
|
||||
|
||||
if (IsSavegameVersionBefore(SLV_WATER_REGIONS)) InitializeWaterRegions();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
@ -249,6 +249,7 @@ static const std::vector<ChunkHandlerRef> &ChunkHandlers()
|
|||
extern const ChunkHandlerTable _airport_chunk_handlers;
|
||||
extern const ChunkHandlerTable _object_chunk_handlers;
|
||||
extern const ChunkHandlerTable _persistent_storage_chunk_handlers;
|
||||
extern const ChunkHandlerTable _water_region_chunk_handlers;
|
||||
|
||||
/** List of all chunks in a savegame. */
|
||||
static const ChunkHandlerTable _chunk_handler_tables[] = {
|
||||
|
@ -286,6 +287,7 @@ static const std::vector<ChunkHandlerRef> &ChunkHandlers()
|
|||
_airport_chunk_handlers,
|
||||
_object_chunk_handlers,
|
||||
_persistent_storage_chunk_handlers,
|
||||
_water_region_chunk_handlers,
|
||||
};
|
||||
|
||||
static std::vector<ChunkHandlerRef> _chunk_handlers;
|
||||
|
|
|
@ -366,6 +366,7 @@ enum SaveLoadVersion : uint16_t {
|
|||
SLV_TIMETABLE_START_TICKS, ///< 321 PR#11468 Convert timetable start from a date to ticks.
|
||||
SLV_TIMETABLE_START_TICKS_FIX, ///< 322 PR#11557 Fix for missing convert timetable start from a date to ticks.
|
||||
SLV_TIMETABLE_TICKS_TYPE, ///< 323 PR#11435 Convert timetable current order time to ticks.
|
||||
SLV_WATER_REGIONS, ///< 324 PR#10543 Water Regions for ship pathfinder.
|
||||
|
||||
SL_MAX_VERSION, ///< Highest possible saveload version
|
||||
};
|
||||
|
|
|
@ -0,0 +1,54 @@
|
|||
/*
|
||||
* This file is part of OpenTTD.
|
||||
* OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
|
||||
* OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
* See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/** @file water_regions_sl.cpp Handles saving and loading of water region data */
|
||||
|
||||
#include "../stdafx.h"
|
||||
|
||||
#include "saveload.h"
|
||||
#include "pathfinder/water_regions.h"
|
||||
|
||||
#include "../safeguards.h"
|
||||
|
||||
static const SaveLoad _water_region_desc[] = {
|
||||
SLE_VAR(WaterRegionSaveLoadInfo, initialized, SLE_BOOL),
|
||||
};
|
||||
|
||||
struct WRGNChunkHandler : ChunkHandler {
|
||||
WRGNChunkHandler() : ChunkHandler('WRGN', CH_TABLE) {}
|
||||
|
||||
void Save() const override
|
||||
{
|
||||
SlTableHeader(_water_region_desc);
|
||||
|
||||
int index = 0;
|
||||
for (WaterRegionSaveLoadInfo ®ion : GetWaterRegionSaveLoadInfo()) {
|
||||
SlSetArrayIndex(index++);
|
||||
SlObject(®ion, _water_region_desc);
|
||||
}
|
||||
}
|
||||
|
||||
void Load() const override
|
||||
{
|
||||
const std::vector<SaveLoad> slt = SlTableHeader(_water_region_desc);
|
||||
|
||||
int index;
|
||||
|
||||
std::vector<WaterRegionSaveLoadInfo> loaded_info;
|
||||
while ((index = SlIterateArray()) != -1) {
|
||||
WaterRegionSaveLoadInfo region_info;
|
||||
SlObject(®ion_info, slt);
|
||||
loaded_info.push_back(std::move(region_info));
|
||||
}
|
||||
|
||||
LoadWaterRegions(loaded_info);
|
||||
}
|
||||
};
|
||||
|
||||
static const WRGNChunkHandler WRGN;
|
||||
static const ChunkHandlerRef water_region_chunk_handlers[] = { WRGN };
|
||||
extern const ChunkHandlerTable _water_region_chunk_handlers(water_region_chunk_handlers);
|
|
@ -16,6 +16,7 @@
|
|||
#include "slope_func.h"
|
||||
|
||||
using SetTrackBitIterator = SetBitIterator<Track, TrackBits>;
|
||||
using SetTrackdirBitIterator = SetBitIterator<Trackdir, TrackdirBits>;
|
||||
|
||||
/**
|
||||
* Checks if a Track is valid.
|
||||
|
|
|
@ -86,6 +86,9 @@ enum Trackdir : byte {
|
|||
INVALID_TRACKDIR = 0xFF, ///< Flag for an invalid trackdir
|
||||
};
|
||||
|
||||
/** Allow incrementing of Trackdir variables */
|
||||
DECLARE_POSTFIX_INCREMENT(Trackdir)
|
||||
|
||||
/**
|
||||
* Enumeration of bitmasks for the TrackDirs
|
||||
*
|
||||
|
|
|
@ -20,6 +20,7 @@
|
|||
#include "ship.h"
|
||||
#include "roadveh.h"
|
||||
#include "pathfinder/yapf/yapf_cache.h"
|
||||
#include "pathfinder/water_regions.h"
|
||||
#include "newgrf_sound.h"
|
||||
#include "autoslope.h"
|
||||
#include "tunnelbridge_map.h"
|
||||
|
@ -561,6 +562,8 @@ CommandCost CmdBuildBridge(DoCommandFlag flags, TileIndex tile_end, TileIndex ti
|
|||
MakeAqueductBridgeRamp(tile_end, owner, ReverseDiagDir(dir));
|
||||
CheckForDockingTile(tile_start);
|
||||
CheckForDockingTile(tile_end);
|
||||
InvalidateWaterRegion(tile_start);
|
||||
InvalidateWaterRegion(tile_end);
|
||||
break;
|
||||
|
||||
default:
|
||||
|
|
|
@ -39,6 +39,7 @@
|
|||
#include "industry.h"
|
||||
#include "water_cmd.h"
|
||||
#include "landscape_cmd.h"
|
||||
#include "pathfinder/water_regions.h"
|
||||
|
||||
#include "table/strings.h"
|
||||
|
||||
|
@ -133,6 +134,9 @@ CommandCost CmdBuildShipDepot(DoCommandFlag flags, TileIndex tile, Axis axis)
|
|||
}
|
||||
|
||||
if (flags & DC_EXEC) {
|
||||
InvalidateWaterRegion(tile);
|
||||
InvalidateWaterRegion(tile2);
|
||||
|
||||
Depot *depot = new Depot(tile);
|
||||
depot->build_date = TimerGameCalendar::date;
|
||||
|
||||
|
@ -243,6 +247,7 @@ void MakeWaterKeepingClass(TileIndex tile, Owner o)
|
|||
|
||||
/* Zero map array and terminate animation */
|
||||
DoClearSquare(tile);
|
||||
InvalidateWaterRegion(tile);
|
||||
|
||||
/* Maybe change to water */
|
||||
switch (wc) {
|
||||
|
@ -340,6 +345,10 @@ static CommandCost DoBuildLock(TileIndex tile, DiagDirection dir, DoCommandFlag
|
|||
}
|
||||
|
||||
if (flags & DC_EXEC) {
|
||||
InvalidateWaterRegion(tile);
|
||||
InvalidateWaterRegion(tile + delta);
|
||||
InvalidateWaterRegion(tile - delta);
|
||||
|
||||
/* Update company infrastructure counts. */
|
||||
Company *c = Company::GetIfValid(_current_company);
|
||||
if (c != nullptr) {
|
||||
|
@ -482,6 +491,8 @@ CommandCost CmdBuildCanal(DoCommandFlag flags, TileIndex tile, TileIndex start_t
|
|||
if (!water) cost.AddCost(ret);
|
||||
|
||||
if (flags & DC_EXEC) {
|
||||
InvalidateWaterRegion(current_tile);
|
||||
|
||||
if (IsTileType(current_tile, MP_WATER) && IsCanal(current_tile)) {
|
||||
Owner owner = GetTileOwner(current_tile);
|
||||
if (Company::IsValidID(owner)) {
|
||||
|
@ -529,8 +540,11 @@ CommandCost CmdBuildCanal(DoCommandFlag flags, TileIndex tile, TileIndex start_t
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
static CommandCost ClearTile_Water(TileIndex tile, DoCommandFlag flags)
|
||||
{
|
||||
if (flags & DC_EXEC) InvalidateWaterRegion(tile);
|
||||
|
||||
switch (GetWaterTileType(tile)) {
|
||||
case WATER_TILE_CLEAR: {
|
||||
if (flags & DC_NO_WATER) return_cmd_error(STR_ERROR_CAN_T_BUILD_ON_WATER);
|
||||
|
@ -1161,6 +1175,8 @@ void DoFloodTile(TileIndex target)
|
|||
}
|
||||
|
||||
if (flooded) {
|
||||
InvalidateWaterRegion(target);
|
||||
|
||||
/* Mark surrounding canal tiles dirty too to avoid glitches */
|
||||
MarkCanalsAndRiversAroundDirty(target);
|
||||
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
#include "town.h"
|
||||
#include "waypoint_base.h"
|
||||
#include "pathfinder/yapf/yapf_cache.h"
|
||||
#include "pathfinder/water_regions.h"
|
||||
#include "strings_func.h"
|
||||
#include "viewport_func.h"
|
||||
#include "viewport_kdtree.h"
|
||||
|
@ -346,6 +347,7 @@ CommandCost CmdBuildBuoy(DoCommandFlag flags, TileIndex tile)
|
|||
if (wp->town == nullptr) MakeDefaultName(wp);
|
||||
|
||||
MakeBuoy(tile, wp->index, GetWaterClass(tile));
|
||||
InvalidateWaterRegion(tile);
|
||||
CheckForDockingTile(tile);
|
||||
MarkTileDirtyByTile(tile);
|
||||
|
||||
|
|
Loading…
Reference in New Issue