1
0
Fork 0

Compare commits

...

2 Commits

Author SHA1 Message Date
Kuhnovic f1e999ec59
Feature: Region-based pathfinder for ships (#10543) 2024-01-08 20:29:05 +01:00
translators 9a7c4dda52 Update: Translations from eints
english (au): 3 changes by krysclarke
english (us): 3 changes by 2TallTyler
chinese (simplified): 5 changes by WenSimEHRP
korean: 3 changes by telk5093
russian: 3 changes by Ln-Wolf
finnish: 3 changes by hpiirai
french: 5 changes by Lishouuu
2024-01-08 18:40:07 +00:00
82 changed files with 1164 additions and 202 deletions

View File

@ -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();

View File

@ -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

View File

@ -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 :يورو

View File

@ -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

View File

@ -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 :Эўра

View File

@ -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

View File

@ -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 :Евро

View File

@ -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

View File

@ -474,7 +474,7 @@ STR_NEWS_MESSAGE_CAPTION :{WHITE}Пӗлт
###length 42
###length 43
STR_GAME_OPTIONS_CURRENCY_RUR :Вырӑсла тенкӗ
STR_GAME_OPTIONS_AUTOSAVE_FRAME :{BLACK}Хӑй управ

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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 :Ευρώ

View File

@ -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 :יורו

View File

@ -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 :हाँग काँग डॉलर

View File

@ -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ó

View File

@ -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

View File

@ -464,7 +464,7 @@ STR_GAME_OPTIONS_CAPTION :{WHITE}Ludo Sel
###length 42
###length 43
# Autosave dropdown

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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 :欧州連合・ユーロ

View File

@ -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 :네트워크

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -780,7 +780,7 @@ STR_GAME_OPTIONS_CAPTION :{WHITE}Опци
###length 42
###length 43
# Autosave dropdown

View File

@ -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

View File

@ -397,7 +397,7 @@ STR_NEWS_VEHICLE_IS_LOST :{WHITE}{VEHICLE
###length 42
###length 43
# Autosave dropdown

View File

@ -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 :युरो

View File

@ -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

View File

@ -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

View File

@ -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 :یورو

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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 :Сеть

View File

@ -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

View File

@ -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}

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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 :யுரோ

View File

@ -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 :ยูโร

View File

@ -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 :歐元

View File

@ -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

View File

@ -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 :Євро

View File

@ -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 :یورو

View File

@ -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

View File

@ -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

View File

@ -5,4 +5,6 @@ add_files(
follow_track.hpp
pathfinder_func.h
pathfinder_type.h
water_regions.h
water_regions.cpp
)

View File

@ -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 &region = 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 &current_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 &current_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 &region : _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 &region = _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();
}
}
}

View File

@ -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 */

View File

@ -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
)

View File

@ -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);
}

View File

@ -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);
}

View File

@ -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 */

View File

@ -44,4 +44,5 @@ add_files(
town_sl.cpp
vehicle_sl.cpp
waypoint_sl.cpp
water_regions_sl.cpp
)

View File

@ -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;
}

View File

@ -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;

View File

@ -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
};

View File

@ -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 &region : GetWaterRegionSaveLoadInfo()) {
SlSetArrayIndex(index++);
SlObject(&region, _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(&region_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);

View File

@ -16,6 +16,7 @@
#include "slope_func.h"
using SetTrackBitIterator = SetBitIterator<Track, TrackBits>;
using SetTrackdirBitIterator = SetBitIterator<Trackdir, TrackdirBits>;
/**
* Checks if a Track is valid.

View File

@ -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
*

View File

@ -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:

View File

@ -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);

View File

@ -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);