OpenTTD
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 
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 
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 
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 
125  { DIAGDIR_NE, INVALID_DIAGDIR, DIAGDIR_SW, INVALID_DIAGDIR },
129  { DIAGDIR_NW, DIAGDIR_SW, INVALID_DIAGDIR, INVALID_DIAGDIR },
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();
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 }