OpenTTD: src/pathfinder/opf/opf_ship.cpp Source File

OpenTTD

- Main Page
- Related Pages
- Modules
- Namespaces
- Data Structures
- Files

- File List
- Globals

opf_ship.cpp
Go to the documentation of this file.
1 /* $Id: opf_ship.cpp 26482 2014-04-23 20:13:33Z rubidium $ */
2
3 /*
4 * This file is part of OpenTTD.
5 * 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.
6 * 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.
7 * 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/>.
8 */
9
12 #include "../../stdafx.h"
13 #include "../../tunnelbridge_map.h"
14 #include "../../tunnelbridge.h"
15 #include "../../ship.h"
16 #include "../../core/random_func.hpp"
17
18 #include "../../safeguards.h"
19
20 struct RememberData {
21 uint16 cur_length;
22 byte depth;
23 Track last_choosen_track;
24 };
25
26 struct TrackPathFinder {
27 TileIndex skiptile;
28 TileIndex dest_coords;
29 uint best_bird_dist;
30 uint best_length;
31 RememberData rd;
32 TrackdirByte the_dir;
33 };
34
35 static bool ShipTrackFollower(TileIndex tile, TrackPathFinder *pfs, uint length)
36 {
37 /* Found dest? */
38 if (tile == pfs->dest_coords) {
39 pfs->best_bird_dist = 0;
40
41 pfs->best_length = minu(pfs->best_length, length);
42 return true;
43 }
44
45 /* Skip this tile in the calculation */
46 if (tile != pfs->skiptile) {
47 pfs->best_bird_dist = minu(pfs->best_bird_dist, DistanceMaxPlusManhattan(pfs->dest_coords, tile));
48 }
49
50 return false;
51 }
52
53 static void TPFModeShip(TrackPathFinder *tpf, TileIndex tile, DiagDirection direction)
54 {
55 if (IsTileType(tile, MP_TUNNELBRIDGE)) {
56 /* wrong track type */
57 if (GetTunnelBridgeTransportType(tile) != TRANSPORT_WATER) return;
58
59 DiagDirection dir = GetTunnelBridgeDirection(tile);
60 /* entering tunnel / bridge? */
61 if (dir == direction) {
62 TileIndex endtile = GetOtherTunnelBridgeEnd(tile);
63
64 tpf->rd.cur_length += GetTunnelBridgeLength(tile, endtile) + 1;
65
66 tile = endtile;
67 } else {
68 /* leaving tunnel / bridge? */
69 if (ReverseDiagDir(dir) != direction) return;
70 }
71 }
72
73 /* This addition will sometimes overflow by a single tile.
74 * The use of TILE_MASK here makes sure that we still point at a valid
75 * tile, and then this tile will be in the sentinel row/col, so GetTileTrackStatus will fail. */
76 tile = TILE_MASK(tile + TileOffsByDiagDir(direction));
77
78 if (++tpf->rd.cur_length > 50) return;
79
80 TrackBits bits = TrackStatusToTrackBits(GetTileTrackStatus(tile, TRANSPORT_WATER, 0)) & DiagdirReachesTracks(direction);
81 if (bits == TRACK_BIT_NONE) return;
82
83 assert(TileX(tile) != MapMaxX() && TileY(tile) != MapMaxY());
84
85 bool only_one_track = true;
86 do {
87 Track track = RemoveFirstTrack(&bits);
88 if (bits != TRACK_BIT_NONE) only_one_track = false;
89 RememberData rd = tpf->rd;
90
91 /* Change direction 4 times only */
92 if (!only_one_track && track != tpf->rd.last_choosen_track) {
93 if (++tpf->rd.depth > 4) {
94 tpf->rd = rd;
95 return;
96 }
97 tpf->rd.last_choosen_track = track;
98 }
99
100 tpf->the_dir = TrackEnterdirToTrackdir(track, direction);
101
102 if (!ShipTrackFollower(tile, tpf, tpf->rd.cur_length)) {
103 TPFModeShip(tpf, tile, TrackdirToExitdir(tpf->the_dir));
104 }
105
106 tpf->rd = rd;
107 } while (bits != TRACK_BIT_NONE);
108 }
109
110 static void OPFShipFollowTrack(TileIndex tile, DiagDirection direction, TrackPathFinder *tpf)
111 {
112 assert(IsValidDiagDirection(direction));
113
114 /* initialize path finder variables */
115 tpf->rd.cur_length = 0;
116 tpf->rd.depth = 0;
117 tpf->rd.last_choosen_track = INVALID_TRACK;
118
119 ShipTrackFollower(tile, tpf, 0);
120 TPFModeShip(tpf, tile, direction);
121 }
122
124 static const DiagDirection _ship_search_directions[6][4] = {
125 { DIAGDIR_NE, INVALID_DIAGDIR, DIAGDIR_SW, INVALID_DIAGDIR },
126 { INVALID_DIAGDIR, DIAGDIR_SE, INVALID_DIAGDIR, DIAGDIR_NW },
127 { INVALID_DIAGDIR, DIAGDIR_NE, DIAGDIR_NW, INVALID_DIAGDIR },
128 { DIAGDIR_SE, INVALID_DIAGDIR, INVALID_DIAGDIR, DIAGDIR_SW },
129 { DIAGDIR_NW, DIAGDIR_SW, INVALID_DIAGDIR, INVALID_DIAGDIR },
130 { INVALID_DIAGDIR, INVALID_DIAGDIR, DIAGDIR_SE, DIAGDIR_NE },
131 };
132
134 static const byte _pick_shiptrack_table[6] = {DIR_NE, DIR_SE, DIR_E, DIR_E, DIR_N, DIR_N};
135
136 static uint FindShipTrack(const Ship *v, TileIndex tile, DiagDirection dir, TrackBits bits, TileIndex skiptile, Track *track)
137 {
138 TrackPathFinder pfs;
139 uint best_bird_dist = 0;
140 uint best_length = 0;
141 byte ship_dir = v->direction & 3;
142
143 pfs.dest_coords = v->dest_tile;
144 pfs.skiptile = skiptile;
145
146 Track best_track = INVALID_TRACK;
147
148 do {
149 Track i = RemoveFirstTrack(&bits);
150
151 pfs.best_bird_dist = UINT_MAX;
152 pfs.best_length = UINT_MAX;
153
154 OPFShipFollowTrack(tile, _ship_search_directions[i][dir], &pfs);
155
156 if (best_track != INVALID_TRACK) {
157 if (pfs.best_bird_dist != 0) {
158 /* neither reached the destination, pick the one with the smallest bird dist */
159 if (pfs.best_bird_dist > best_bird_dist) goto bad;
160 if (pfs.best_bird_dist < best_bird_dist) goto good;
161 } else {
162 if (pfs.best_length > best_length) goto bad;
163 if (pfs.best_length < best_length) goto good;
164 }
165
166 /* if we reach this position, there's two paths of equal value so far.
167 * pick one randomly. */
168 uint r = GB(Random(), 0, 8);
169 if (_pick_shiptrack_table[i] == ship_dir) r += 80;
170 if (_pick_shiptrack_table[best_track] == ship_dir) r -= 80;
171 if (r <= 127) goto bad;
172 }
173 good:;
174 best_track = i;
175 best_bird_dist = pfs.best_bird_dist;
176 best_length = pfs.best_length;
177 bad:;
178
179 } while (bits != 0);
180
181 *track = best_track;
182 return best_bird_dist;
183 }
184
190 Track OPFShipChooseTrack(const Ship *v, TileIndex tile, DiagDirection enterdir, TrackBits tracks, bool &path_found)
191 {
192 assert(IsValidDiagDirection(enterdir));
193
194 TileIndex tile2 = TILE_ADD(tile, -TileOffsByDiagDir(enterdir));
195 Track track;
196
197 /* Let's find out how far it would be if we would reverse first */
198 Trackdir trackdir = v->GetVehicleTrackdir();
199 TrackBits b = TrackStatusToTrackBits(GetTileTrackStatus(tile2, TRANSPORT_WATER, 0)) & DiagdirReachesTracks(ReverseDiagDir(enterdir)) & TrackdirBitsToTrackBits(TrackdirToTrackdirBits(trackdir));
200
201 uint distr = UINT_MAX; // distance if we reversed
202 if (b != 0) {
203 distr = FindShipTrack(v, tile2, ReverseDiagDir(enterdir), b, tile, &track);
204 if (distr != UINT_MAX) distr++; // penalty for reversing
205 }
206
207 /* And if we would not reverse? */
208 uint dist = FindShipTrack(v, tile, enterdir, tracks, 0, &track);
209
210 /* Due to the way this pathfinder works we cannot determine whether we're lost or not. */
211 path_found = true;
212 if (dist <= distr) return track;
213 return INVALID_TRACK; // We could better reverse
214 }