Let HandleWindowDragging return a boolean status
[openttd/fttd.git] / src / roadstop.cpp
blob34892f2990448d5e3f38ede6939649acb60e7113
1 /* $Id$ */
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 */
10 /** @file roadstop.cpp Implementation of the roadstop base class. */
12 #include "stdafx.h"
13 #include "roadveh.h"
14 #include "core/pool_func.hpp"
15 #include "roadstop_base.h"
16 #include "station_base.h"
17 #include "map/road.h"
19 /** The pool of roadstops. */
20 template<> RoadStop::Pool RoadStop::PoolItem::pool ("RoadStop");
21 INSTANTIATE_POOL_METHODS(RoadStop)
23 /**
24 * De-Initializes RoadStops.
26 RoadStop::~RoadStop()
28 /* When we are the head we need to free the entries */
29 if (HasBit(this->status, RSSFB_BASE_ENTRY)) {
30 delete this->platform;
33 if (CleaningPool()) return;
36 /**
37 * Join this road stop to another 'base' road stop if possible;
38 * fill all necessary data to become an actual drive through road stop.
39 * Also update the length etc.
41 void RoadStop::MakeDriveThrough()
43 assert (this->platform == NULL);
45 RoadStopType rst = GetRoadStopType(this->xy);
46 /* AxisToDiagDir always returns the direction that heads south. */
47 TileIndexDiff offset = TileOffsByDiagDir(AxisToDiagDir(GetRoadStopAxis(this->xy)));
49 /* Information about the tile north of us */
50 TileIndex north_tile = this->xy - offset;
51 bool north = IsDriveThroughRoadStopContinuation(this->xy, north_tile);
52 RoadStop *rs_north = north ? RoadStop::GetByTile(north_tile, rst) : NULL;
54 /* Information about the tile south of us */
55 TileIndex south_tile = this->xy + offset;
56 bool south = IsDriveThroughRoadStopContinuation(this->xy, south_tile);
57 RoadStop *rs_south = south ? RoadStop::GetByTile(south_tile, rst) : NULL;
59 /* Amount of road stops that will be added to the 'northern' head */
60 int added = 1;
61 if (north && rs_north->platform != NULL) {
62 /* There is a more northern one, so this can join them */
63 this->platform = rs_north->platform;
65 if (south && rs_south->platform != NULL) {
66 /* There more southern tiles too, they must 'join' us too */
67 ClrBit(rs_south->status, RSSFB_BASE_ENTRY);
68 this->platform->occupied_east += rs_south->platform->occupied_east;
69 this->platform->occupied_west += rs_south->platform->occupied_west;
71 /* Free the now unneeded entry structs */
72 delete rs_south->platform;
74 /* Make all 'children' of the southern tile take the new master */
75 for (; IsDriveThroughRoadStopContinuation(this->xy, south_tile); south_tile += offset) {
76 rs_south = RoadStop::GetByTile(south_tile, rst);
77 if (rs_south->platform == NULL) break;
78 rs_south->platform = rs_north->platform;
79 added++;
82 } else if (south && rs_south->platform != NULL) {
83 /* There is one to the south, but not to the north... so we become 'parent' */
84 this->platform = rs_south->platform;
85 SetBit(this->status, RSSFB_BASE_ENTRY);
86 ClrBit(rs_south->status, RSSFB_BASE_ENTRY);
87 } else {
88 /* We are the only... so we are automatically the master */
89 this->platform = new Platform();
90 SetBit(this->status, RSSFB_BASE_ENTRY);
93 /* Now update the lengths */
94 added *= TILE_SIZE;
95 this->platform->length += added;
98 /**
99 * Prepare for removal of this stop; update other neighbouring stops
100 * if needed. Also update the length etc.
102 void RoadStop::ClearDriveThrough()
104 assert (this->platform != NULL);
106 RoadStopType rst = GetRoadStopType(this->xy);
107 /* AxisToDiagDir always returns the direction that heads south. */
108 TileIndexDiff offset = TileOffsByDiagDir(AxisToDiagDir(GetRoadStopAxis(this->xy)));
110 /* Information about the tile north of us */
111 TileIndex north_tile = this->xy - offset;
112 bool north = IsDriveThroughRoadStopContinuation(this->xy, north_tile);
113 RoadStop *rs_north = north ? RoadStop::GetByTile(north_tile, rst) : NULL;
115 /* Information about the tile south of us */
116 TileIndex south_tile = this->xy + offset;
117 bool south = IsDriveThroughRoadStopContinuation(this->xy, south_tile);
118 RoadStop *rs_south = south ? RoadStop::GetByTile(south_tile, rst) : NULL;
120 /* Must only be cleared after we determined which neighbours are
121 * part of our little entry 'queue' */
122 DoClearSquare(this->xy);
124 if (north) {
125 /* There is a tile to the north, so we can't clear ourselves. */
126 if (south) {
127 /* There are more southern tiles too, they must be split;
128 * first make the new southern 'base' */
129 SetBit(rs_south->status, RSSFB_BASE_ENTRY);
130 rs_south->platform = new Platform();
132 /* Keep track of the base because we need it later on */
133 RoadStop *rs_south_base = rs_south;
134 TileIndex base_tile = south_tile;
136 /* Make all (even more) southern stops part of the new entry queue */
137 for (south_tile += offset; IsDriveThroughRoadStopContinuation(base_tile, south_tile); south_tile += offset) {
138 rs_south = RoadStop::GetByTile(south_tile, rst);
139 rs_south->platform = rs_south_base->platform;
142 /* Find the other end; the northern most tile */
143 for (; IsDriveThroughRoadStopContinuation(base_tile, north_tile); north_tile -= offset) {
144 rs_north = RoadStop::GetByTile(north_tile, rst);
147 /* We have to rebuild the entries because we cannot easily determine
148 * how full each part is. So instead of keeping and maintaining a list
149 * of vehicles and using that to 'rebuild' the occupied state we just
150 * rebuild it from scratch as that removes lots of maintenance code
151 * for the vehicle list and it's faster in real games as long as you
152 * do not keep split and merge road stop every tick by the millions. */
153 rs_south_base->Rebuild();
155 assert(HasBit(rs_north->status, RSSFB_BASE_ENTRY));
156 rs_north->Rebuild();
157 } else {
158 /* Only we left, so simple update the length. */
159 rs_north->platform->length -= TILE_SIZE;
161 } else if (south) {
162 /* There is only something to the south. Hand over the base entry */
163 SetBit(rs_south->status, RSSFB_BASE_ENTRY);
164 rs_south->platform->length -= TILE_SIZE;
165 } else {
166 /* We were the last */
167 delete this->platform;
170 /* Make sure we don't get used for something 'incorrect' */
171 ClrBit(this->status, RSSFB_BASE_ENTRY);
172 this->platform = NULL;
176 * Find a roadstop at given tile
177 * @param tile tile with roadstop
178 * @param type roadstop type
179 * @return pointer to RoadStop
180 * @pre there has to be roadstop of given type there!
182 /* static */ RoadStop *RoadStop::GetByTile(TileIndex tile, RoadStopType type)
184 const Station *st = Station::GetByTile(tile);
186 for (RoadStop *rs = st->GetPrimaryRoadStop(type);; rs = rs->next) {
187 if (rs->xy == tile) return rs;
188 assert(rs->next != NULL);
193 * Checks whether the 'next' tile is still part of the road same drive through
194 * stop 'rs' in the same direction for the same vehicle.
195 * @param rs the road stop tile to check against
196 * @param next the 'next' tile to check
197 * @return true if the 'next' tile is part of the road stop at 'next'.
199 /* static */ bool RoadStop::IsDriveThroughRoadStopContinuation(TileIndex rs, TileIndex next)
201 return IsStationTile(next) &&
202 GetStationIndex(next) == GetStationIndex(rs) &&
203 GetStationType(next) == GetStationType(rs) &&
204 IsDriveThroughStopTile(next) &&
205 GetRoadStopAxis(next) == GetRoadStopAxis(rs);
209 * Rebuild, from scratch, the vehicles and other metadata on this platform.
210 * @param tile The northernmost tile of the platform
212 void RoadStop::Platform::Rebuild (TileIndex tile)
214 typedef std::list<const RoadVehicle *> RVList; ///< A list of road vehicles
216 DiagDirection dir = GetRoadStopDir (tile);
217 TileIndexDiff offset = abs(TileOffsByDiagDir(dir));
218 Direction dir_east = DiagDirToDir (dir);
220 int length = 0;
221 RVList list_east, list_west;
222 for (TileIndex t = tile; IsDriveThroughRoadStopContinuation (tile, t); t += offset) {
223 length += TILE_SIZE;
224 VehicleTileIterator iter (t);
225 while (!iter.finished()) {
226 Vehicle *v = iter.next();
228 /* Not a RV or not primary or crashed :( */
229 if (v->type != VEH_ROAD || !v->IsPrimaryVehicle() || (v->vehstatus & VS_CRASHED) != 0) continue;
231 RoadVehicle *rv = RoadVehicle::From(v);
232 /* Don't add ones not in a road stop */
233 if (rv->state < RVSB_IN_ROAD_STOP) continue;
235 assert (v->direction == dir_east || v->direction == ReverseDir (dir_east));
236 RVList *list = (v->direction == dir_east) ? &list_east : &list_west;
238 /* Do not add duplicates! */
239 RVList::iterator it;
240 for (it = list->begin(); it != list->end(); it++) {
241 if (rv == *it) break;
244 if (it == list->end()) list->push_back(rv);
248 this->length = length;
250 this->occupied_east = 0;
251 for (RVList::iterator it = list_east.begin(); it != list_east.end(); it++) {
252 this->occupied_east += (*it)->gcache.cached_total_length;
255 this->occupied_west = 0;
256 for (RVList::iterator it = list_west.begin(); it != list_west.end(); it++) {
257 this->occupied_west += (*it)->gcache.cached_total_length;
262 * Check the integrity of the data in this drive-through road stop.
264 void RoadStop::CheckIntegrity (void) const
266 if (!HasBit (this->status, RSSFB_BASE_ENTRY)) return;
268 /* The tile 'before' the road stop must not be part of this 'line' */
269 assert (!IsDriveThroughRoadStopContinuation (this->xy, this->xy - TileOffsByDiagDir (AxisToDiagDir (GetRoadStopAxis (this->xy)))));
271 Platform temp;
272 temp.Rebuild (this->xy);
274 assert (this->platform->length == temp.length);
275 assert (this->platform->occupied_east == temp.occupied_east);
276 assert (this->platform->occupied_west == temp.occupied_west);