1
0
Fork 0

Compare commits

...

5 Commits

Author SHA1 Message Date
SamuXarick 83d232232b
Merge 8031d68d01 into 7c8759552a 2025-07-28 04:48:31 +00:00
translators 7c8759552a Update: Translations from eints
japanese: 40 changes by akaregi
hungarian: 6 changes by nemesbala
2025-07-28 04:48:20 +00:00
SamuXarick 8031d68d01 Change: Ships may reverse on find closest depot order
Ships may be allowed to reverse when:
- manually ordered to find the closest ship depot.
- executing an updated order to find the closest ship depot after departing from a station.
2025-05-24 18:08:46 +01:00
SamuXarick 7df9426000 Change: Find nearest depot to entry edge in intermediate region
When using an intermediate region, find the depot closest to the edge where it entered the region from.
2025-05-24 18:08:46 +01:00
Samu 0b44deedd0 Fix #5713: Use pathfinder to find closest ship depot
When ships are asked to find the closest depot, the depot that is provided is not always reachable. This patch provides the closest reachable ship depot, by utilizing the pathfinder.
2025-05-24 18:05:59 +01:00
20 changed files with 314 additions and 106 deletions

View File

@ -110,7 +110,7 @@ struct Aircraft final : public SpecializedVehicle<Aircraft, VEH_AIRCRAFT> {
uint Crash(bool flooded = false) override;
TileIndex GetOrderStationLocation(StationID station) override;
TileIndex GetCargoTile() const override { return this->First()->tile; }
ClosestDepot FindClosestDepot() override;
ClosestDepot FindClosestDepot(bool may_reverse = false) override;
/**
* Check if the aircraft type is a normal flying device; eg

View File

@ -393,7 +393,7 @@ CommandCost CmdBuildAircraft(DoCommandFlags flags, TileIndex tile, const Engine
}
ClosestDepot Aircraft::FindClosestDepot()
ClosestDepot Aircraft::FindClosestDepot([[maybe_unused]] bool may_reverse)
{
const Station *st = GetTargetAirportIfValid(this);
/* If the station is not a valid airport or if it has no hangars */

View File

@ -697,8 +697,11 @@ STR_GRAPH_CARGO_TOOLTIP_DISABLE_ALL :{BLACK}Ne mutas
STR_GRAPH_CARGO_PAYMENT_TOGGLE_CARGO :{BLACK}Adott rakomány grafikonjának mutatása be/ki
STR_GRAPH_CARGO_PAYMENT_CARGO :{TINY_FONT}{BLACK}{STRING}
STR_GRAPH_INDUSTRY_CAPTION :{WHITE}{INDUSTRY} - Rakománytörténet
STR_GRAPH_INDUSTRY_RANGE_PRODUCED :Előállított
STR_GRAPH_INDUSTRY_RANGE_TRANSPORTED :Szállítva
STR_GRAPH_INDUSTRY_RANGE_DELIVERED :Leszállítva
STR_GRAPH_INDUSTRY_RANGE_WAITING :Várakozik
STR_GRAPH_PERFORMANCE_DETAIL_TOOLTIP :{BLACK}Részletes teljesítményértékelés mutatása
@ -4087,6 +4090,8 @@ STR_INDUSTRY_VIEW_PRODUCTION_LAST_MONTH_TITLE :{BLACK}Múlt ha
STR_INDUSTRY_VIEW_PRODUCTION_LAST_MINUTE_TITLE :{BLACK}Termelés az elmúlt percben:
STR_INDUSTRY_VIEW_TRANSPORTED :{YELLOW}{CARGO_LONG}{STRING}{BLACK} ({COMMA}% elszállítva)
STR_INDUSTRY_VIEW_LOCATION_TOOLTIP :{BLACK}A fő nézetet a gazdasági épületre állítja. Ctrl+kattintással új nézet nyílik a gazdasági épület helyzeténél
STR_INDUSTRY_VIEW_CARGO_GRAPH :{BLACK}Rakomány grafikon
STR_INDUSTRY_VIEW_CARGO_GRAPH_TOOLTIP :{BLACK}Megmutatja az iparág rakománytörténetének grafikonját
STR_INDUSTRY_VIEW_PRODUCTION_LEVEL :{BLACK}Termelési szint: {YELLOW}{COMMA}%
STR_INDUSTRY_VIEW_INDUSTRY_ANNOUNCED_CLOSURE :{YELLOW}A gyár bejelentette a közelgő bezárását!
@ -5062,6 +5067,7 @@ STR_ERROR_FLAT_LAND_REQUIRED :{WHITE}Sima tal
STR_ERROR_LAND_SLOPED_IN_WRONG_DIRECTION :{WHITE}Rossz irányba lejt a föld
STR_ERROR_CAN_T_DO_THIS :{WHITE}Nem teheted ezt...
STR_ERROR_BUILDING_MUST_BE_DEMOLISHED :{WHITE}Előbb le kell rombolnod az épületet
STR_ERROR_BUILDING_IS_PROTECTED :{WHITE}... a(z) épület védett
STR_ERROR_CAN_T_CLEAR_THIS_AREA :{WHITE}Nem tisztíthatod meg ezt a területet...
STR_ERROR_SITE_UNSUITABLE :{WHITE}... nem alkalmas rá a hely
STR_ERROR_ALREADY_BUILT :{WHITE}... már van itt

View File

@ -437,6 +437,7 @@ STR_SETTINGS_MENU_NEWGRF_SETTINGS :NewGRFの設定
STR_SETTINGS_MENU_TRANSPARENCY_OPTIONS :透過表示設定
STR_SETTINGS_MENU_TOWN_NAMES_DISPLAYED :街名を表示
STR_SETTINGS_MENU_STATION_NAMES_DISPLAYED :駅名を表示
STR_SETTINGS_MENU_STATION_NAMES_BUS :バス停
STR_SETTINGS_MENU_WAYPOINTS_DISPLAYED :中継駅名を表示
STR_SETTINGS_MENU_SIGNS_DISPLAYED :標識を表示
STR_SETTINGS_MENU_SHOW_COMPETITOR_SIGNS :競争者の標識と名前を表示
@ -522,6 +523,7 @@ STR_ABOUT_MENU_ABOUT_OPENTTD :OpenTTDにつ
STR_ABOUT_MENU_SPRITE_ALIGNER :スプライトを整列
STR_ABOUT_MENU_TOGGLE_BOUNDING_BOXES :バウンディングボックスの表示切替
STR_ABOUT_MENU_TOGGLE_DIRTY_BLOCKS :ダーティーブロックの色付け切替
STR_ABOUT_MENU_TOGGLE_WIDGET_OUTLINES :ウィジェットの枠線の表示切替
###length 31
STR_DAY_NUMBER_1ST :1
@ -1203,12 +1205,14 @@ STR_CONFIG_SETTING_HORIZONTAL_POS_CENTER :中央
STR_CONFIG_SETTING_HORIZONTAL_POS_RIGHT :右
STR_CONFIG_SETTING_INFINITE_MONEY :無限の資金: {STRING}
STR_CONFIG_SETTING_INFINITE_MONEY_HELPTEXT :無制限の支出を許容し、会社の破産も無効にします
STR_CONFIG_SETTING_MAXIMUM_INITIAL_LOAN :初期の借入最大額: {STRING}
STR_CONFIG_SETTING_MAXIMUM_INITIAL_LOAN_HELPTEXT :初期の借入限度額を設定します (インフレは考慮されません)
STR_CONFIG_SETTING_MAXIMUM_INITIAL_LOAN_HELPTEXT :初期の借入限度額を設定します (インフレは考慮されません)。「借入金なし」に設定した場合、ゲームスクリプトか「無限の資金」オプションによる提供がない限り、資金は利用できなくなります。
STR_CONFIG_SETTING_MAXIMUM_INITIAL_LOAN_VALUE :{CURRENCY_LONG}
###setting-zero-is-special
STR_CONFIG_SETTING_MAXIMUM_INITIAL_LOAN_DISABLED :借入金なし {RED}ゲームスクリプトで資金を受給する必要があります
STR_CONFIG_SETTING_MAXIMUM_INITIAL_LOAN_DISABLED :借入金なし
STR_CONFIG_SETTING_INTEREST_RATE :金利: {STRING}
STR_CONFIG_SETTING_INTEREST_RATE_HELPTEXT :借入利率を設定します (インフレ設定を有効にしたときのインフレ率にも影響します)
@ -1247,7 +1251,7 @@ STR_CONFIG_SETTING_TRAIN_REVERSING_HELPTEXT :設定を有効
STR_CONFIG_SETTING_DISASTERS :災害: {STRING}
STR_CONFIG_SETTING_DISASTERS_HELPTEXT :設定を有効にすると時折、乗り物や交通インフラを遮断・破壊する災害が起きるようになります
STR_CONFIG_SETTING_CITY_APPROVAL :議会の姿勢: {STRING}
STR_CONFIG_SETTING_CITY_APPROVAL :地方自治体の姿勢: {STRING}
STR_CONFIG_SETTING_CITY_APPROVAL_HELPTEXT :会社が街で引き起こした騒音(主に空港)や環境破壊がどの程度、街での評価や更なる建設行為に影響するかを設定します
STR_CONFIG_SETTING_MAP_HEIGHT_LIMIT :マップ高さ限界: {STRING}
@ -1351,10 +1355,10 @@ STR_CONFIG_SETTING_AUTOSCROLL_MAIN_VIEWPORT_FULLSCREEN :する (フル
STR_CONFIG_SETTING_AUTOSCROLL_MAIN_VIEWPORT :する
STR_CONFIG_SETTING_AUTOSCROLL_EVERY_VIEWPORT :する (ビューポートでも有効)
STR_CONFIG_SETTING_BRIBE :議会の買収: {STRING}
STR_CONFIG_SETTING_BRIBE :地方自治体の贈賄: {STRING}
###length 2
STR_CONFIG_SETTING_BRIBE_HELPTEXT :街で議会買収を企てられるようになります。成功すれば街での評判が良くなりますが、地元当局に事が発覚した場合罰金を受けた上評判が悪くなり、その上その街では半年間何もできなくなります
STR_CONFIG_SETTING_BRIBE_HELPTEXT_MINUTES :会社が議会買収を企てられるようにします。もし当局に事が発覚した場合、買収した会社は当該自治体では半年間何もできなくなります
STR_CONFIG_SETTING_BRIBE_HELPTEXT :地方自治体への贈賄を企てられるようにします。もし当局に事が発覚した場合、贈賄を試みた会社は当該自治体では半年間何もできなくなります
STR_CONFIG_SETTING_BRIBE_HELPTEXT_MINUTES :地方自治体への贈賄を企てられるようにします。もし当局に事が発覚した場合、贈賄を試みた会社は当該自治体では半年間何もできなくなります
STR_CONFIG_SETTING_ALLOW_EXCLUSIVE :独占運送契約の締結: {STRING}
###length 2
@ -1424,6 +1428,7 @@ STR_CONFIG_SETTING_NEVER_EXPIRE_VEHICLES_HELPTEXT :有効にする
###setting-zero-is-special
STR_CONFIG_SETTING_CARGO_SCALE_VALUE :{NUM}%
STR_CONFIG_SETTING_AUTORENEW_VEHICLE :老朽車両の自動交換: {STRING}
STR_CONFIG_SETTING_AUTORENEW_VEHICLE_HELPTEXT :有効にすると、耐用年数を越えた輸送機器は自動で更新されるようになります(交換には一度格納施設に戻る必要があります)。具体的な交換時期は下の設定で変更できます。
@ -1858,7 +1863,7 @@ STR_CONFIG_SETTING_ALLOW_TOWN_ROADS_HELPTEXT :街の自治体
STR_CONFIG_SETTING_ALLOW_TOWN_LEVEL_CROSSINGS :街路との平面交差を許可: {STRING}
STR_CONFIG_SETTING_ALLOW_TOWN_LEVEL_CROSSINGS_HELPTEXT :有効にすると、会社が作る道路と街路とが交差できるようになります
STR_CONFIG_SETTING_NOISE_LEVEL :空港建設に対する街の騒音レベル規制: {STRING}
STR_CONFIG_SETTING_NOISE_LEVEL :騒音レベルに基づいた空港建設の制限: {STRING}
STR_CONFIG_SETTING_NOISE_LEVEL_HELPTEXT :この設定を無効にすると、街に作れる空港は最大2つまでになります。有効にした場合は、各空港の騒音レベルの総和が街の騒音許容レベル以下になるようにしか建てられません。騒音レベルは空港の大きさ・街からの距離により、騒音許容レベルは街の人口により左右されます
STR_CONFIG_SETTING_TOWN_FOUNDING :ゲーム中での街新設: {STRING}
@ -1936,9 +1941,9 @@ STR_CONFIG_SETTING_CITY_SIZE_MULTIPLIER :初期の都市
STR_CONFIG_SETTING_CITY_SIZE_MULTIPLIER_HELPTEXT :ゲーム開始時に都市が普通の街に比べて平均して何倍の人口規模になるかを設定します
STR_CONFIG_SETTING_LINKGRAPH_RECALC_INTERVAL :行先分配グラフを{STRING}秒毎に更新
STR_CONFIG_SETTING_LINKGRAPH_RECALC_INTERVAL_HELPTEXT :行先分配グラフの更新の間の待ち時間。各更新はグラフの一部だけのルートを再計算します。即ち、この設定を〇〇秒に指定する場合、全グラフが〇〇秒毎に更新されるのではありません。間隔が短ければ短いほどCPU時間を消費します。長ければ長いほど貨物流通の新規ルートの行先分配に時間がかかります。
STR_CONFIG_SETTING_LINKGRAPH_RECALC_INTERVAL_HELPTEXT :行先分配グラフの再計算間隔。各計算タイミングでは一部のグラフのみが再計算されます。よって、ここ設定した X 秒ごとにグラフ全体が更新されるというわけではありません。再計算の間隔が短ければ短いほどCPU時間を消費します。長ければ長いほど貨物流通の新規ルートの行先分配に時間がかかります。
STR_CONFIG_SETTING_LINKGRAPH_RECALC_TIME :行先分配の更新に{STRING}秒を割り当て
STR_CONFIG_SETTING_LINKGRAPH_RECALC_TIME_HELPTEXT :行先分配グラフの更新に割り当てられる時間。更新が始まると、スレッドが生成されて、指定の時間に実行させられます。短ければ短いほど間に合わない可能性が高まります。その場合、ゲームは停止して計算完了を待ちます。長ければ長いほどルート変更の際に行先分配の更新に時間がかかります。
STR_CONFIG_SETTING_LINKGRAPH_RECALC_TIME_HELPTEXT :行先分配グラフの再計算に割り当てられる時間。再計算が始まると、グラフの処理のために指定した生存秒数だけ実行可能なスレッドが作られます。生存秒数が短ければ短いほど再計算が間に合わない可能性が高まります。その際、ゲームは一時停止して計算完了を待ちます。生存秒数が長ければ長いほど、ルート変更の際に行先分配の更新に時間がかかります。
STR_CONFIG_SETTING_DISTRIBUTION_PAX :旅客の行先分配法: {STRING}
STR_CONFIG_SETTING_DISTRIBUTION_PAX_HELPTEXT :旅客がどのように行き先別に分配されるかを設定します。「対称」ではAからBへ向かう乗客とほぼ同数が、BからAに向かうようになります。 「非対称」ではそれぞれの方向に向かう旅客数は独立に決められます。「無効」では行き先別分配をしなくなります。
@ -1954,7 +1959,7 @@ STR_CONFIG_SETTING_DISTRIBUTION_ASYMMETRIC :非対称
STR_CONFIG_SETTING_DISTRIBUTION_SYMMETRIC :対称
STR_CONFIG_SETTING_LINKGRAPH_ACCURACY :分配精度: {STRING}
STR_CONFIG_SETTING_LINKGRAPH_ACCURACY_HELPTEXT :この値を高くすると、リンクグラフ演算の為CPUへの負荷が大きくなります。演算に時間がかかりすぎると、目に見えてタイムラグが起こる場合があります。しかし低い値に設定すると、分配が不正確になり、望まれる場所に貨物が送られなくなる場合があります
STR_CONFIG_SETTING_LINKGRAPH_ACCURACY_HELPTEXT :この設定値を高く設定すればするほど、行先分配グラフの計算に要するCPU時間が長くなります。計算に時間がかかりすぎると遅延が発生する可能性があります。一方、設定値を低く設定すると分配が不正確になり、荷物が意図された場所に送られない場合があります。
STR_CONFIG_SETTING_DEMAND_DISTANCE :距離効果: {STRING}
STR_CONFIG_SETTING_DEMAND_DISTANCE_HELPTEXT :0より大きい値に設定すると、ある貨物の生産先Aと受取可能先Bとの距離がAからBへ送られる貨物量に影響を及ぼすようになります。高い値を設定すればするほど、遠い施設に送られる貨物量は少なくなり、近場の施設に送られる量が大きくなります
@ -2058,8 +2063,10 @@ STR_CONFIG_ERROR_INVALID_BASE_MUSIC_NOT_FOUND :{WHITE}ファ
# Video initalization errors
STR_VIDEO_DRIVER_ERROR :{WHITE}ビデオ設定にエラーがあります...
STR_VIDEO_DRIVER_ERROR_NO_HARDWARE_ACCELERATION :{WHITE}... 対応する GPU が見つかりません。ハードウェアアクセラレーションは無効になります。
STR_VIDEO_DRIVER_ERROR_HARDWARE_ACCELERATION_CRASH :{WHITE}... GPUドライバーがゲームをクラッシュさせました。ハードウェアアクセラレーションは無効になりました
# Intro window
STR_INTRO_CAPTION :{WHITE}OpenTTD
STR_INTRO_NEW_GAME :{BLACK}新しいゲーム
STR_INTRO_LOAD_GAME :{BLACK}ロード
@ -2070,6 +2077,7 @@ STR_INTRO_MULTIPLAYER :{BLACK}マル
STR_INTRO_GAME_OPTIONS :{BLACK}基本設定
STR_INTRO_HIGHSCORE :{BLACK}ハイスコア
STR_INTRO_HELP :{BLACK}ヘルプとマニュアル
STR_INTRO_ONLINE_CONTENT :{BLACK}オンラインコンテンツの確認
STR_INTRO_QUIT :{BLACK}終了
@ -2121,14 +2129,14 @@ STR_HELP_WINDOW_COMMUNITY :{BLACK}コミ
STR_CHEATS :{WHITE}サンドボックスのオプション
STR_CHEAT_MONEY :{LTBLUE}預金残高を{CURRENCY_LONG}増やす
STR_CHEAT_CHANGE_COMPANY :{LTBLUE}会社: {ORANGE}{COMMA}を乗っ取ってプレイする
STR_CHEAT_EXTRA_DYNAMITE :{LTBLUE}魔法のブルドーザー(産業拠点等、何でも撤去できる): {ORANGE}{STRING}
STR_CHEAT_EXTRA_DYNAMITE :{LTBLUE}魔法のブルドーザー(産業拠点など何でも撤去可能): {ORANGE}{STRING}
STR_CHEAT_CROSSINGTUNNELS :{LTBLUE}トンネルの平面交差を許容: {ORANGE}{STRING}
STR_CHEAT_NO_JETCRASH :{LTBLUE}ジェット機の小型空港での墜落率を減少: {ORANGE}{STRING}
STR_CHEAT_EDIT_MAX_HL :{LTBLUE}マップの最高高度を変更: {ORANGE}{NUM}
STR_CHEAT_EDIT_MAX_HL_QUERY_CAPT :{WHITE}マップの最大高度
STR_CHEAT_CHANGE_DATE :{LTBLUE}日付を変更: {ORANGE}{DATE_SHORT}
STR_CHEAT_CHANGE_DATE_QUERY_CAPT :{WHITE}現在日時を変更
STR_CHEAT_SETUP_PROD :{LTBLUE}生産量変更: {ORANGE}{STRING}
STR_CHEAT_SETUP_PROD :{LTBLUE}産業の生産量変更を有効化: {ORANGE}{STRING}
# Livery window
STR_LIVERY_CAPTION :{WHITE}{COMPANY} - 配色
@ -2457,6 +2465,7 @@ STR_NETWORK_SERVER_MESSAGE_GAME_REASON_LINK_GRAPH :リンクグラ
STR_NETWORK_MESSAGE_CLIENT_LEAVING :退出
STR_NETWORK_MESSAGE_CLIENT_JOINED :*** {STRING} が参加してきました
STR_NETWORK_MESSAGE_CLIENT_JOINED_ID :*** {STRING} がゲームに参加してきました (クライアント #{NUM})
STR_NETWORK_MESSAGE_CLIENT_COMPANY_JOIN :*** {0:STRING} が{STRING}の経営に参画してきました
STR_NETWORK_MESSAGE_CLIENT_COMPANY_SPECTATE :*** {STRING} がゲームを観覧し始めました
STR_NETWORK_MESSAGE_CLIENT_COMPANY_NEW :*** {STRING} が新会社 (#{NUM}) を設立しました
STR_NETWORK_MESSAGE_CLIENT_LEFT :*** {STRING} が退出しました({STRING})
@ -2672,6 +2681,7 @@ STR_SELECT_ROAD_BRIDGE_CAPTION :{WHITE}道路
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}{STRING}{} {WHITE}{CURRENCY_LONG}
STR_SELECT_BRIDGE_INFO_NAME_MAX_SPEED_COST :{GOLD}{STRING}{} {VELOCITY} {WHITE}{CURRENCY_LONG}
STR_BRIDGE_NAME_SUSPENSION_STEEL :吊橋(S造)
STR_BRIDGE_NAME_GIRDER_STEEL :桁橋(S造)
@ -3458,7 +3468,7 @@ STR_LOCAL_AUTHORITY_ACTION_ROAD_RECONSTRUCTION :道路補修に
STR_LOCAL_AUTHORITY_ACTION_STATUE_OF_COMPANY :社長の彫像を建設
STR_LOCAL_AUTHORITY_ACTION_NEW_BUILDINGS :市街地開発に出資
STR_LOCAL_AUTHORITY_ACTION_EXCLUSIVE_TRANSPORT :独占運送契約を締結
STR_LOCAL_AUTHORITY_ACTION_BRIBE :議会を買収
STR_LOCAL_AUTHORITY_ACTION_BRIBE :地方自治体への贈賄
###next-name-looks-similar
STR_LOCAL_AUTHORITY_ACTION_TOOLTIP_SMALL_ADVERTISING :{PUSH_COLOUR}{YELLOW}旅客と貨物を確保する為に、街で新聞広告を実施します。{}より多くの旅客と貨物が自社の交通網を利用するようになります。{}{POP_COLOUR}費用: {CURRENCY_LONG}
@ -3466,7 +3476,7 @@ STR_LOCAL_AUTHORITY_ACTION_TOOLTIP_MEDIUM_ADVERTISING :{PUSH_COLOUR}{Y
STR_LOCAL_AUTHORITY_ACTION_TOOLTIP_LARGE_ADVERTISING :{PUSH_COLOUR}{YELLOW}旅客と貨物を確保する為に、街でTV-CMを開始します{}街の中心部の大範囲にある駅の評価が一時的に高まります。{}{POP_COLOUR}費用: {CURRENCY_LONG}
STR_LOCAL_AUTHORITY_ACTION_TOOLTIP_STATUE_OF_COMPANY :{PUSH_COLOUR}{YELLOW}社を称える彫像を建設します{}この街の駅の評価を恒久的に高めます。{}{POP_COLOUR}費用: {CURRENCY_LONG}
STR_LOCAL_AUTHORITY_ACTION_TOOLTIP_NEW_BUILDINGS :{PUSH_COLOUR}{YELLOW}市街地の開発に出資します{}街の成長速度が一時的に早まります。{}{POP_COLOUR}費用: {CURRENCY_LONG}
STR_LOCAL_AUTHORITY_ACTION_TOOLTIP_BRIBE :{PUSH_COLOUR}{YELLOW}買収を行い、議会内の評判を高めます。注意: 露見した場合は処罰されます{}{POP_COLOUR}費用: {CURRENCY_LONG}
STR_LOCAL_AUTHORITY_ACTION_TOOLTIP_BRIBE :{PUSH_COLOUR}{YELLOW}贈賄を通じて地方自治体内の評判を高めます。注意: 露見した場合は処罰されます{}{POP_COLOUR}費用: {CURRENCY_LONG}
# Goal window
STR_GOALS_CAPTION :{WHITE}{COMPANY} 目標
@ -4374,6 +4384,7 @@ STR_ORDER_REFIT_STOP_ORDER :({STRING}に改
STR_ORDER_STOP_ORDER :(運用停止)
STR_ORDER_GO_TO_STATION :{STRING} {STATION}
STR_ORDER_GO_TO_STATION_CAN_T_USE_STATION :{PUSH_COLOUR}{RED}(駅を使用できません){POP_COLOUR} {STRING} {STATION}
STR_ORDER_IMPLICIT :(自動)
@ -4432,6 +4443,7 @@ STR_TIMETABLE_TOOLTIP :{BLACK}ダイ
STR_TIMETABLE_NO_TRAVEL :運行計画無
STR_TIMETABLE_NOT_TIMETABLEABLE :該当区間を運行 (次の手動指令により自動設定)
STR_TIMETABLE_TRAVEL_NOT_TIMETABLED :該当区間を運行 (ダイヤ設定無)
STR_TIMETABLE_TRAVEL_NOT_TIMETABLED_SPEED :最高速度{VELOCITY}で該当区間を運行 (ダイヤ設定無)
STR_TIMETABLE_TRAVEL_FOR :{STRING}で該当区間を運行
STR_TIMETABLE_TRAVEL_FOR_SPEED :{STRING}で該当区間を運行(最高速度{VELOCITY})
STR_TIMETABLE_TRAVEL_FOR_ESTIMATED :運行({0:STRING}・ダイヤ設定無)
@ -4525,14 +4537,14 @@ STR_AI_CONFIG_MOVE_DOWN :{BLACK}下に
STR_AI_CONFIG_MOVE_DOWN_TOOLTIP :{BLACK}選択したAIの順位を下げる
STR_AI_CONFIG_GAMESCRIPT :{SILVER}ゲームスクリプト
STR_AI_CONFIG_GAMESCRIPT_PARAM :{SILVER}パラメータ
STR_AI_CONFIG_GAMESCRIPT_PARAM :{SILVER}パラメータ
STR_AI_CONFIG_AI :{SILVER}AI
STR_AI_CONFIG_CHANGE_AI :{BLACK}AIを選択
STR_AI_CONFIG_CHANGE_GAMESCRIPT :{BLACK}ゲームスクリプトを選択
STR_AI_CONFIG_CHANGE_TOOLTIP :{BLACK}他のスクリプトをロードします。Ctrl+クリックで全ての利用可能バージョンを表示します
STR_AI_CONFIG_CONFIGURE :{BLACK}設定
STR_AI_CONFIG_CONFIGURE_TOOLTIP :{BLACK}スクリプトのパラメータを設定します
STR_AI_CONFIG_CONFIGURE_TOOLTIP :{BLACK}スクリプトのパラメータを設定します
# Available AIs window
STR_AI_LIST_CAPTION :{WHITE}使用可能な{STRING}
@ -4556,8 +4568,8 @@ STR_SCREENSHOT_HEIGHTMAP_SCREENSHOT :{BLACK}ハイ
STR_SCREENSHOT_MINIMAP_SCREENSHOT :{BLACK}ミニマップのスクリーンショット
# Script Parameters
STR_AI_SETTINGS_CAPTION_AI :AI
STR_AI_SETTINGS_CAPTION_GAMESCRIPT :ゲームスクリプト
STR_AI_SETTINGS_CAPTION_AI :{WHITE}AIのパラメーター
STR_AI_SETTINGS_CAPTION_GAMESCRIPT :{WHITE}ゲームスクリプトのパラメーター
STR_AI_SETTINGS_RESET :{BLACK}リセット
STR_AI_SETTINGS_SETTING :{STRING}: {ORANGE}{STRING}
@ -4638,6 +4650,7 @@ STR_ERROR_SCREENSHOT_FAILED :{WHITE}スク
# Error message titles
STR_ERROR_MESSAGE_CAPTION :{YELLOW}メッセージ
STR_ERROR_MESSAGE_CAPTION_OTHER_COMPANY :{YELLOW}{COMPANY}からのメッセージ
# Generic construction errors
STR_ERROR_OFF_EDGE_OF_MAP :{WHITE}マップからはみ出します
@ -4656,12 +4669,13 @@ STR_ERROR_TERRAFORM_LIMIT_REACHED :{WHITE}一度
STR_ERROR_CLEARING_LIMIT_REACHED :{WHITE}一度にできる撤去量を越えています
STR_ERROR_TREE_PLANT_LIMIT_REACHED :{WHITE}木の本数が多すぎます
STR_ERROR_NAME_MUST_BE_UNIQUE :{WHITE}名前は重複してはいけません
STR_ERROR_GENERIC_OBJECT_IN_THE_WAY :{WHITE}{STRING}があります
STR_ERROR_NOT_ALLOWED_WHILE_PAUSED :{WHITE}ポーズ中にはできない行動です
# Local authority errors
STR_ERROR_LOCAL_AUTHORITY_REFUSES_TO_ALLOW_THIS :{WHITE}{TOWN}議会が反対しています
STR_ERROR_LOCAL_AUTHORITY_REFUSES_AIRPORT :{WHITE}{TOWN}議会はこれ以上の空港建設を認可しない方針です
STR_ERROR_LOCAL_AUTHORITY_REFUSES_NOISE :{WHITE}{TOWN}の地元民が騒音公害を理由に空港建設に反対しています
STR_ERROR_LOCAL_AUTHORITY_REFUSES_TO_ALLOW_THIS :{WHITE}{TOWN}の地方自治体が反対しています
STR_ERROR_LOCAL_AUTHORITY_REFUSES_AIRPORT :{WHITE}{TOWN}の地方自治体はこれ以上の空港建設を許可しません
STR_ERROR_LOCAL_AUTHORITY_REFUSES_NOISE :{WHITE}{TOWN}の地方自治体は騒音公害の懸念から空港建設を許可しません
STR_ERROR_BRIBE_FAILED :{WHITE}あなたの行った贈収賄が地元当局に露見しました!
# Levelling errors
@ -5535,3 +5549,8 @@ STR_SHIP :{BLACK}{SHIP}
STR_TOOLBAR_RAILTYPE_VELOCITY :{STRING} ({VELOCITY})
STR_BADGE_CONFIG_MENU_TOOLTIP :バッジ設定を開く
STR_BADGE_CONFIG_RESET :リセット
STR_BADGE_CONFIG_ICONS :{WHITE}バッジのアイコン
STR_BADGE_CONFIG_FILTERS :{WHITE}バッジのフィルター
STR_BADGE_CONFIG_NAME :名前

View File

@ -1896,10 +1896,11 @@ VehicleOrderID ProcessConditionalOrder(const Order *order, const Vehicle *v)
* Update the vehicle's destination tile from an order.
* @param order the order the vehicle currently has
* @param v the vehicle to update
* @param may_reverse Whether the vehicle is allowed to reverse when executing the updated order.
* @param conditional_depth the depth (amount of steps) to go with conditional orders. This to prevent infinite loops.
* @param pbs_look_ahead Whether we are forecasting orders for pbs reservations in advance. If true, the order indices must not be modified.
*/
bool UpdateOrderDest(Vehicle *v, const Order *order, int conditional_depth, bool pbs_look_ahead)
bool UpdateOrderDest(Vehicle *v, const Order *order, bool may_reverse, int conditional_depth, bool pbs_look_ahead)
{
if (conditional_depth > v->GetNumOrders()) {
v->current_order.Free();
@ -1926,7 +1927,7 @@ bool UpdateOrderDest(Vehicle *v, const Order *order, int conditional_depth, bool
if (v->dest_tile == 0 && TimerGameEconomy::date_fract != (v->index % Ticks::DAY_TICKS)) break;
/* We need to search for the nearest depot (hangar). */
ClosestDepot closest_depot = v->FindClosestDepot();
ClosestDepot closest_depot = v->FindClosestDepot(may_reverse);
if (closest_depot.found) {
/* PBS reservations cannot reverse */
@ -2018,7 +2019,7 @@ bool UpdateOrderDest(Vehicle *v, const Order *order, int conditional_depth, bool
}
v->current_order = *order;
return UpdateOrderDest(v, order, conditional_depth + 1, pbs_look_ahead);
return UpdateOrderDest(v, order, may_reverse, conditional_depth + 1, pbs_look_ahead);
}
/**
@ -2116,7 +2117,7 @@ bool ProcessOrders(Vehicle *v)
break;
}
return UpdateOrderDest(v, order) && may_reverse;
return UpdateOrderDest(v, order, may_reverse) && may_reverse;
}
/**

View File

@ -20,7 +20,7 @@ void InvalidateVehicleOrder(const Vehicle *v, int data);
void CheckOrders(const Vehicle*);
void DeleteVehicleOrders(Vehicle *v, bool keep_orderlist = false, bool reset_order_indices = true);
bool ProcessOrders(Vehicle *v);
bool UpdateOrderDest(Vehicle *v, const Order *order, int conditional_depth = 0, bool pbs_look_ahead = false);
bool UpdateOrderDest(Vehicle *v, const Order *order, bool may_reverse = false, int conditional_depth = 0, bool pbs_look_ahead = false);
VehicleOrderID ProcessConditionalOrder(const Order *order, const Vehicle *v);
uint GetOrderDistance(VehicleOrderID prev, VehicleOrderID cur, const Vehicle *v, int conditional_depth = 0);

View File

@ -430,3 +430,83 @@ void PrintWaterRegionDebugInfo(TileIndex tile)
{
GetUpdatedWaterRegion(tile).PrintDebugInfo();
}
/**
* Tests the provided callback function on all tiles of the water patch of the region
* and returns true on the first tile that passes the callback test.
* @param callback The test function that will be called for the water patch.
* @param water_region_patch Water patch within the water region to test the callback.
* @return true if it passes the callback test, or false if the callback failed.
*/
bool TestTileInWaterRegionPatch(const WaterRegionPatchDesc &water_region_patch, TestTileIndexCallBack &callback)
{
const WaterRegion region = GetUpdatedWaterRegion(water_region_patch.x, water_region_patch.y);
/* Check if the region has a tile which passes the callback test. */
for (const TileIndex tile : region) {
if (region.GetLabel(tile) != water_region_patch.label || !callback(tile)) continue;
return true;
}
return false;
}
/**
* Tests the provided callback function on all tiles of the current water patch of the region, collects the
* tiles which passed the callback and returns the tile closest to the edge from where the region is entered from.
* @param high_level_path A span containing at least current and parent water patches.
* @param callback The test function that will be called for each tile in the water patch.
* @return The tile closest to the edge from where it came from that passed the callback test, or INVALID_TILE if no tile passed.
*/
TileIndex FindClosestEnteringTile(const std::span<WaterRegionPatchDesc> high_level_path, TestTileIndexCallBack &callback)
{
assert(high_level_path.size() > 1);
const WaterRegionPatchDesc &current_water_region_patch = high_level_path.back();
const WaterRegion current_region = GetUpdatedWaterRegion(current_water_region_patch.x, current_water_region_patch.y);
/* Check if the current region has a tile which passes the callback test. */
std::vector<TileIndex> tile_list;
for (const TileIndex tile : current_region) {
if (current_region.GetLabel(tile) != current_water_region_patch.label || !callback(tile)) continue;
/* We collect the tiles when we know which region we came from for further evaluation. */
tile_list.push_back(tile);
}
/* If there aren't any tiles that passed the callback, return with an invalid tile. */
if (tile_list.empty()) return INVALID_TILE;
/* If there's only one, just return it. */
if (tile_list.size() == 1) return tile_list.front();
const TileIndex top_tile = current_region.begin();
const TileIndex bot_tile = TileAddXY(top_tile, WATER_REGION_EDGE_LENGTH - 1, WATER_REGION_EDGE_LENGTH - 1);
/* Get the side from which the current region is entered from. */
const WaterRegionPatchDesc &parent_water_region_patch = high_level_path[high_level_path.size() - 2];
const WaterRegion parent_region = GetUpdatedWaterRegion(parent_water_region_patch.x, parent_water_region_patch.y);
const DiagDirection side = DiagdirBetweenTiles(top_tile, parent_region.begin());
/* Depending on the side, determine which corner tile to use to extract their x or y coordinates. */
const bool is_at_top = side == DIAGDIR_NE || side == DIAGDIR_NW;
const TileIndex edge_tile = is_at_top ? top_tile : bot_tile;
const bool is_axis_x = DiagDirToAxis(side) == AXIS_X;
const int x_or_y_edge = is_axis_x ? TileX(edge_tile) : TileY(edge_tile);
/* With more than one tile passing the callback, calculate the tile that is closest to the edge from whence it came. */
TileIndex best_tile = INVALID_TILE;
int best_dist = WATER_REGION_EDGE_LENGTH;
for (const TileIndex &tile : tile_list) {
const int x_or_y_tile = is_axis_x ? TileX(tile) : TileY(tile);
const int dist_to_edge = std::abs(x_or_y_tile - x_or_y_edge);
assert(dist_to_edge < WATER_REGION_EDGE_LENGTH);
if (dist_to_edge >= best_dist) continue;
best_dist = dist_to_edge;
best_tile = tile;
}
return best_tile;
}

View File

@ -64,4 +64,8 @@ void AllocateWaterRegions();
void PrintWaterRegionDebugInfo(TileIndex tile);
using TestTileIndexCallBack = std::function<bool(const TileIndex)>;
bool TestTileInWaterRegionPatch(const WaterRegionPatchDesc &water_region_patch, TestTileIndexCallBack &callback);
TileIndex FindClosestEnteringTile(const std::span<WaterRegionPatchDesc> high_level_path, TestTileIndexCallBack &callback);
#endif /* WATER_REGIONS_H */

View File

@ -34,6 +34,17 @@ Track YapfShipChooseTrack(const Ship *v, TileIndex tile, bool &path_found, ShipP
*/
bool YapfShipCheckReverse(const Ship *v, Trackdir *trackdir);
/**
* Used when user sends ship to the nearest depot or if ship needs servicing using YAPF.
* @param v ship that needs to go to some depot
* @param max_penalty max distance (in pathfinder penalty) from the current ship position
* (used also as optimization - the pathfinder can stop path finding if max_penalty
* was reached and no depot was seen)
* @param may_reverse whether the ship is allowed to reverse
* @return the data about the depot
*/
FindDepotData YapfShipFindNearestDepot(const Ship *v, int max_penalty, bool may_reverse);
/**
* Finds the best path for given road vehicle using YAPF.
* @param v the RV that needs to find a path

View File

@ -36,6 +36,7 @@ protected:
TileIndex dest_tile;
TrackdirBits dest_trackdirs;
StationID dest_station;
bool any_ship_depot = false;
bool has_intermediate_dest = false;
TileIndex intermediate_dest_tile;
@ -55,6 +56,11 @@ public:
}
}
void SetAnyShipDepotDestination()
{
this->any_ship_depot = true;
}
void SetIntermediateDestination(const WaterRegionPatchDesc &water_region_patch)
{
this->has_intermediate_dest = true;
@ -69,10 +75,16 @@ protected:
return *static_cast<Tpf*>(this);
}
TestTileIndexCallBack detect_ship_depot = [&](const TileIndex tile)
{
return IsShipDepotTile(tile) && GetShipDepotPart(tile) == DEPOT_PART_NORTH && IsTileOwner(tile, Yapf().GetVehicle()->owner);
};
public:
/** Called by YAPF to detect if node ends in the desired destination. */
inline bool PfDetectDestination(Node &n)
{
if (this->any_ship_depot) return this->detect_ship_depot(n.key.tile);
return this->PfDetectDestinationTile(n.segment_last_tile, n.segment_last_td);
}
@ -89,6 +101,11 @@ public:
return tile == this->dest_tile && ((this->dest_trackdirs & TrackdirToTrackdirBits(trackdir)) != TRACKDIR_BIT_NONE);
}
inline TileIndex GetShipDepotDestination(const std::span<WaterRegionPatchDesc> high_level_path)
{
return FindClosestEnteringTile(high_level_path, this->detect_ship_depot);
}
/**
* 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::estimate.
@ -99,7 +116,7 @@ public:
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 (this->PfDetectDestination(n)) {
if (this->any_ship_depot || this->PfDetectDestination(n)) {
n.estimate = n.cost;
return true;
}
@ -158,7 +175,7 @@ public:
}
/** 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)
inline void RestrictSearch(const std::span<WaterRegionPatchDesc> &path)
{
this->water_region_corridor.clear();
for (const WaterRegionPatchDesc &path_entry : path) this->water_region_corridor.push_back(path_entry);
@ -211,16 +228,20 @@ public:
return result;
}
static Trackdir ChooseShipTrack(const Ship *v, TileIndex tile, TrackdirBits forward_dirs, TrackdirBits reverse_dirs,
static Trackdir ChooseShipTrack(const Ship *v, TileIndex &tile, TrackdirBits forward_dirs, TrackdirBits reverse_dirs, int max_penalty,
bool &path_found, ShipPathCache &path_cache, Trackdir &best_origin_dir)
{
const std::vector<WaterRegionPatchDesc> high_level_path = YapfShipFindWaterRegionPath(v, tile, NUMBER_OR_WATER_REGIONS_LOOKAHEAD + 1);
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, path_cache, SHIP_LOST_PATH_LENGTH);
}
const bool find_closest_depot = tile == INVALID_TILE;
if (find_closest_depot) tile = v->tile;
const bool automatic_servicing = find_closest_depot && max_penalty != 0;
/* 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. */
@ -229,13 +250,28 @@ public:
/* Set origin and destination nodes */
pf.SetOrigin(v->tile, forward_dirs | reverse_dirs);
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());
if (find_closest_depot) {
pf.SetAnyShipDepotDestination();
} else {
pf.SetDestination(v);
}
pf.SetMaxCost(max_penalty);
const std::span<WaterRegionPatchDesc> high_level_path_span(high_level_path.data(), std::min<size_t>(high_level_path.size(), NUMBER_OR_WATER_REGIONS_LOOKAHEAD + 1));
const bool is_intermediate_destination = static_cast<int>(high_level_path_span.size()) >= NUMBER_OR_WATER_REGIONS_LOOKAHEAD + 1;
if (is_intermediate_destination) {
if (automatic_servicing) {
/* Automatic servicing requires a valid path cost from start to end.
* However, when an intermediate destination is set, the resulting cost
* cannot be used to determine if it falls within the maximum allowed penalty. */
return INVALID_TRACKDIR;
}
pf.SetIntermediateDestination(high_level_path_span.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);
if (attempt > 0) pf.RestrictSearch(high_level_path_span);
/* Find best path. */
path_found = pf.FindPath(v);
@ -245,6 +281,12 @@ public:
/* Make the ship move around aimlessly. This prevents repeated pathfinder calls and clearly indicates that the ship is lost. */
if (!path_found) return CreateRandomPath(v, path_cache, SHIP_LOST_PATH_LENGTH);
/* Return early when only searching for the closest depot tile. */
if (find_closest_depot) {
tile = is_intermediate_destination ? pf.GetShipDepotDestination(high_level_path) : node->GetTile();
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. */
@ -254,7 +296,7 @@ public:
while (node->parent) {
const WaterRegionPatchDesc node_water_patch = GetWaterRegionPatchInfo(node->GetTile());
const bool node_water_patch_on_high_level_path = std::ranges::find(high_level_path, node_water_patch) != high_level_path.end();
const bool node_water_patch_on_high_level_path = std::ranges::find(high_level_path_span, node_water_patch) != high_level_path_span.end();
const bool add_full_path = !is_intermediate_destination && node_water_patch != end_water_patch;
/* The cached path must always lead to a region patch that's on the high level path.
@ -303,6 +345,7 @@ public:
{
bool path_found = false;
ShipPathCache dummy_cache;
TileIndex tile = v->tile;
Trackdir best_origin_dir = INVALID_TRACKDIR;
if (trackdir == nullptr) {
@ -310,17 +353,45 @@ public:
const Trackdir reverse_dir = ReverseTrackdir(v->GetVehicleTrackdir());
const TrackdirBits forward_dirs = TrackdirToTrackdirBits(v->GetVehicleTrackdir());
const TrackdirBits reverse_dirs = TrackdirToTrackdirBits(reverse_dir);
(void)ChooseShipTrack(v, v->tile, forward_dirs, reverse_dirs, path_found, dummy_cache, best_origin_dir);
(void)ChooseShipTrack(v, tile, forward_dirs, reverse_dirs, 0, path_found, dummy_cache, best_origin_dir);
return path_found && best_origin_dir == reverse_dir;
} else {
/* This gets called when a ship suddenly can't move forward, e.g. due to terraforming. */
const DiagDirection entry = ReverseDiagDir(VehicleExitDir(v->direction, v->state));
const TrackdirBits reverse_dirs = DiagdirReachesTrackdirs(entry) & TrackStatusToTrackdirBits(GetTileTrackStatus(v->tile, TRANSPORT_WATER, 0, entry));
(void)ChooseShipTrack(v, v->tile, TRACKDIR_BIT_NONE, reverse_dirs, path_found, dummy_cache, best_origin_dir);
(void)ChooseShipTrack(v, tile, TRACKDIR_BIT_NONE, reverse_dirs, 0, path_found, dummy_cache, best_origin_dir);
*trackdir = path_found && best_origin_dir != INVALID_TRACKDIR ? best_origin_dir : GetRandomTrackdir(reverse_dirs);
return true;
}
}
/**
* Find the best depot for a ship.
* @param v Ship
* @param max_penalty maximum pathfinder cost.
* @param may_reverse whether the ship is allowed to reverse.
* @return FindDepotData with the best depot tile, cost and whether to reverse.
*/
static inline FindDepotData FindNearestDepot(const Ship *v, int max_penalty, bool may_reverse)
{
FindDepotData depot;
bool path_found = false;
ShipPathCache dummy_cache;
TileIndex tile = INVALID_TILE;
Trackdir best_origin_dir = INVALID_TRACKDIR;
const bool search_both_ways = may_reverse && max_penalty == 0;
const Trackdir forward_dir = v->GetVehicleTrackdir();
const Trackdir reverse_dir = ReverseTrackdir(forward_dir);
const TrackdirBits forward_dirs = TrackdirToTrackdirBits(forward_dir);
const TrackdirBits reverse_dirs = search_both_ways ? TrackdirToTrackdirBits(reverse_dir) : TRACKDIR_BIT_NONE;
(void)ChooseShipTrack(v, tile, forward_dirs, reverse_dirs, max_penalty, path_found, dummy_cache, best_origin_dir);
if (path_found) {
assert(tile != INVALID_TILE);
depot.tile = tile;
}
return depot;
}
};
/** Cost Provider module of YAPF for ships. */
@ -333,6 +404,11 @@ public:
typedef typename Types::NodeList::Item Node; ///< this will be our node type.
typedef typename Node::Key Key; ///< key to hash tables.
protected:
int max_cost;
CYapfCostShipT() : max_cost(0) {}
/** to access inherited path finder */
Tpf &Yapf()
{
@ -340,6 +416,11 @@ public:
}
public:
inline void SetMaxCost(int cost)
{
this->max_cost = cost;
}
inline int CurveCost(Trackdir td1, Trackdir td2)
{
assert(IsValidTrackdir(td1));
@ -384,6 +465,10 @@ public:
uint8_t 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->tiles_skipped) * speed_frac / (256 - speed_frac);
/* Finish if we already exceeded the maximum path cost (i.e. when
* searching for the nearest depot). */
if (this->max_cost > 0 && (n.parent->cost + c) > this->max_cost) return false;
/* Apply it. */
n.cost = n.parent->cost + c;
return true;
@ -422,7 +507,7 @@ Track YapfShipChooseTrack(const Ship *v, TileIndex tile, bool &path_found, ShipP
{
Trackdir best_origin_dir = INVALID_TRACKDIR;
const TrackdirBits origin_dirs = TrackdirToTrackdirBits(v->GetVehicleTrackdir());
const Trackdir td_ret = CYapfShip::ChooseShipTrack(v, tile, origin_dirs, TRACKDIR_BIT_NONE, path_found, path_cache, best_origin_dir);
const Trackdir td_ret = CYapfShip::ChooseShipTrack(v, tile, origin_dirs, TRACKDIR_BIT_NONE, 0, path_found, path_cache, best_origin_dir);
return (td_ret != INVALID_TRACKDIR) ? TrackdirToTrack(td_ret) : INVALID_TRACK;
}
@ -430,3 +515,8 @@ bool YapfShipCheckReverse(const Ship *v, Trackdir *trackdir)
{
return CYapfShip::CheckShipReverse(v, trackdir);
}
FindDepotData YapfShipFindNearestDepot(const Ship *v, int max_penalty, bool may_reverse)
{
return CYapfShip::FindNearestDepot(v, max_penalty, may_reverse);
}

View File

@ -119,6 +119,7 @@ public:
protected:
Key dest;
bool any_ship_depot = false;
public:
void SetDestination(const WaterRegionPatchDesc &water_region_patch)
@ -126,18 +127,32 @@ public:
this->dest.Set(water_region_patch);
}
void SetAnyShipDepotDestination()
{
this->any_ship_depot = true;
}
protected:
TestTileIndexCallBack detect_ship_depot = [&](const TileIndex tile)
{
return IsShipDepotTile(tile) && GetShipDepotPart(tile) == DEPOT_PART_NORTH && IsTileOwner(tile, Yapf().GetVehicle()->owner);
};
Tpf &Yapf() { return *static_cast<Tpf*>(this); }
public:
inline bool PfDetectDestination(Node &n) const
inline bool PfDetectDestination(Node &n)
{
if (this->any_ship_depot) {
return TestTileInWaterRegionPatch(n.key.water_region_patch, this->detect_ship_depot);
}
return n.key == this->dest;
}
inline bool PfCalcEstimate(Node &n)
{
if (this->PfDetectDestination(n)) {
if (this->any_ship_depot || this->PfDetectDestination(n)) {
n.estimate = n.cost;
return true;
}
@ -218,6 +233,31 @@ public:
assert(!path.empty());
return path;
}
static std::vector<WaterRegionPatchDesc> FindShipDepotRegionPath(const Ship *v)
{
const WaterRegionPatchDesc start_water_region_patch = GetWaterRegionPatchInfo(v->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.AddOrigin(start_water_region_patch);
pf.SetAnyShipDepotDestination();
/* Find best path. */
if (!pf.FindPath(v)) return {}; // Path not found.
std::vector<WaterRegionPatchDesc> path;
Node *node = pf.GetBestNode();
while (node != nullptr) {
path.push_back(node->key.water_region_patch);
node = node->parent;
}
assert(!path.empty());
std::ranges::reverse(path);
return path;
}
};
/** Cost Provider of YAPF for water regions. */
@ -296,5 +336,8 @@ struct CYapfRegionWater : CYapfT<CYapfRegion_TypesT<CYapfRegionWater, CRegionNod
*/
std::vector<WaterRegionPatchDesc> YapfShipFindWaterRegionPath(const Ship *v, TileIndex start_tile, int max_returned_path_length)
{
const bool find_closest_depot = start_tile == INVALID_TILE;
if (find_closest_depot) return CYapfRegionWater::FindShipDepotRegionPath(v);
return CYapfRegionWater::FindWaterRegionPath(v, start_tile, max_returned_path_length);
}

View File

@ -16,5 +16,6 @@
struct Ship;
std::vector<WaterRegionPatchDesc> YapfShipFindWaterRegionPath(const Ship *v, TileIndex start_tile, int max_returned_path_length);
std::vector<WaterRegionPatchDesc> YapfFindShipDepotRegionPath(const Ship *v);
#endif /* YAPF_SHIP_REGIONS_H */

View File

@ -132,7 +132,7 @@ struct RoadVehicle final : public GroundVehicle<RoadVehicle, VEH_ROAD> {
uint Crash(bool flooded = false) override;
Trackdir GetVehicleTrackdir() const override;
TileIndex GetOrderStationLocation(StationID station) override;
ClosestDepot FindClosestDepot() override;
ClosestDepot FindClosestDepot(bool may_reverse = false) override;
bool IsBus() const;

View File

@ -346,7 +346,7 @@ static FindDepotData FindClosestRoadDepot(const RoadVehicle *v, int max_distance
return YapfRoadVehicleFindNearestDepot(v, max_distance);
}
ClosestDepot RoadVehicle::FindClosestDepot()
ClosestDepot RoadVehicle::FindClosestDepot([[maybe_unused]] bool may_reverse)
{
FindDepotData rfdd = FindClosestRoadDepot(this, 0);
if (rfdd.best_length == UINT_MAX) return ClosestDepot();

View File

@ -57,7 +57,7 @@ struct Ship final : public SpecializedVehicle<Ship, VEH_SHIP> {
void OnNewEconomyDay() override;
Trackdir GetVehicleTrackdir() const override;
TileIndex GetOrderStationLocation(StationID station) override;
ClosestDepot FindClosestDepot() override;
ClosestDepot FindClosestDepot(bool may_reverse = false) override;
void UpdateCache();
void SetDestTile(TileIndex tile) override;
};

View File

@ -17,7 +17,6 @@
#include "station_base.h"
#include "newgrf_engine.h"
#include "pathfinder/yapf/yapf.h"
#include "pathfinder/yapf/yapf_ship_regions.h"
#include "newgrf_sound.h"
#include "spritecache.h"
#include "strings_func.h"
@ -39,13 +38,8 @@
#include "table/strings.h"
#include <unordered_set>
#include "safeguards.h"
/** Max distance in tiles (as the crow flies) to search for depots when user clicks "go to depot". */
constexpr int MAX_SHIP_DEPOT_SEARCH_DISTANCE = 80;
/**
* Determine the effective #WaterClass for a ship travelling on a tile.
* @param tile Tile of interest
@ -148,57 +142,15 @@ void Ship::GetImage(Direction direction, EngineImageType image_type, VehicleSpri
result->Set(_ship_sprites[spritenum] + direction);
}
static const Depot *FindClosestShipDepot(const Vehicle *v, uint max_distance)
static const Depot *FindClosestShipDepot(const Vehicle *v, uint max_distance, bool may_reverse = false)
{
const int max_region_distance = (max_distance / WATER_REGION_EDGE_LENGTH) + 1;
const TileIndex tile = v->tile;
if (IsShipDepotTile(tile) && IsTileOwner(tile, v->owner)) return Depot::GetByTile(tile);
static std::unordered_set<int> visited_patch_hashes;
static std::deque<WaterRegionPatchDesc> patches_to_search;
visited_patch_hashes.clear();
patches_to_search.clear();
FindDepotData sfdd = YapfShipFindNearestDepot(Ship::From(v), max_distance, may_reverse);
/* Step 1: find a set of reachable Water Region Patches using BFS. */
const WaterRegionPatchDesc start_patch = GetWaterRegionPatchInfo(v->tile);
patches_to_search.push_back(start_patch);
visited_patch_hashes.insert(CalculateWaterRegionPatchHash(start_patch));
while (!patches_to_search.empty()) {
/* Remove first patch from the queue and make it the current patch. */
const WaterRegionPatchDesc current_node = patches_to_search.front();
patches_to_search.pop_front();
/* Add neighbours of the current patch to the search queue. */
VisitWaterRegionPatchCallback visit_func = [&](const WaterRegionPatchDesc &water_region_patch) {
/* Note that we check the max distance per axis, not the total distance. */
if (std::abs(water_region_patch.x - start_patch.x) > max_region_distance ||
std::abs(water_region_patch.y - start_patch.y) > max_region_distance) return;
const int hash = CalculateWaterRegionPatchHash(water_region_patch);
if (visited_patch_hashes.count(hash) == 0) {
visited_patch_hashes.insert(hash);
patches_to_search.push_back(water_region_patch);
}
};
VisitWaterRegionPatchNeighbours(current_node, visit_func);
}
/* Step 2: Find the closest depot within the reachable Water Region Patches. */
const Depot *best_depot = nullptr;
uint best_dist_sq = std::numeric_limits<uint>::max();
for (const Depot *depot : Depot::Iterate()) {
const TileIndex tile = depot->xy;
if (IsShipDepotTile(tile) && IsTileOwner(tile, v->owner)) {
const uint dist_sq = DistanceSquare(tile, v->tile);
if (dist_sq < best_dist_sq && dist_sq <= max_distance * max_distance &&
visited_patch_hashes.count(CalculateWaterRegionPatchHash(GetWaterRegionPatchInfo(tile))) > 0) {
best_dist_sq = dist_sq;
best_depot = depot;
}
}
}
return best_depot;
if (sfdd.tile == INVALID_TILE) return nullptr;
return Depot::GetByTile(sfdd.tile);
}
static void CheckIfShipNeedsService(Vehicle *v)
@ -209,7 +161,7 @@ static void CheckIfShipNeedsService(Vehicle *v)
return;
}
uint max_distance = _settings_game.pf.yapf.maximum_go_to_depot_penalty / YAPF_TILE_LENGTH;
uint max_distance = _settings_game.pf.yapf.maximum_go_to_depot_penalty;
const Depot *depot = FindClosestShipDepot(v, max_distance);
@ -948,9 +900,9 @@ CommandCost CmdBuildShip(DoCommandFlags flags, TileIndex tile, const Engine *e,
return CommandCost();
}
ClosestDepot Ship::FindClosestDepot()
ClosestDepot Ship::FindClosestDepot(bool may_reverse)
{
const Depot *depot = FindClosestShipDepot(this, MAX_SHIP_DEPOT_SEARCH_DISTANCE);
const Depot *depot = FindClosestShipDepot(this, 0, may_reverse);
if (depot == nullptr) return ClosestDepot();
return ClosestDepot(depot->xy, depot->index);

View File

@ -129,7 +129,7 @@ struct Train final : public GroundVehicle<Train, VEH_TRAIN> {
uint Crash(bool flooded = false) override;
Trackdir GetVehicleTrackdir() const override;
TileIndex GetOrderStationLocation(StationID station) override;
ClosestDepot FindClosestDepot() override;
ClosestDepot FindClosestDepot(bool may_reverse = false) override;
void ReserveTrackUnderConsist() const;

View File

@ -2185,7 +2185,7 @@ static FindDepotData FindClosestTrainDepot(Train *v, int max_distance)
return YapfTrainFindNearestDepot(v, max_distance);
}
ClosestDepot Train::FindClosestDepot()
ClosestDepot Train::FindClosestDepot([[maybe_unused]] bool may_reverse)
{
FindDepotData tfdd = FindClosestTrainDepot(this, 0);
if (tfdd.best_length == UINT_MAX) return ClosestDepot();

View File

@ -2594,7 +2594,7 @@ CommandCost Vehicle::SendToDepot(DoCommandFlags flags, DepotCommandFlags command
return CommandCost();
}
ClosestDepot closest_depot = this->FindClosestDepot();
ClosestDepot closest_depot = this->FindClosestDepot(true);
static const StringID no_depot[] = {STR_ERROR_UNABLE_TO_FIND_ROUTE_TO, STR_ERROR_UNABLE_TO_FIND_LOCAL_DEPOT, STR_ERROR_UNABLE_TO_FIND_LOCAL_DEPOT, STR_ERROR_CAN_T_SEND_AIRCRAFT_TO_HANGAR};
if (!closest_depot.found) return CommandCost(no_depot[this->type]);

View File

@ -787,9 +787,10 @@ public:
/**
* Find the closest depot for this vehicle and tell us the location,
* DestinationID and whether we should reverse.
* @param may_reverse Whether the vehicle is allowed to reverse.
* @return A structure with information about the closest depot, if found.
*/
virtual ClosestDepot FindClosestDepot() { return {}; }
virtual ClosestDepot FindClosestDepot([[maybe_unused]] bool may_reverse = false) { return {}; }
virtual void SetDestTile(TileIndex tile) { this->dest_tile = tile; }