OpenTTD

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

- File List
- Globals

ground_vehicle.hpp
Go to the documentation of this file.
1 /* $Id: ground_vehicle.hpp 26888 2014-09-21 12:44:38Z 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 #ifndef GROUND_VEHICLE_HPP
13 #define GROUND_VEHICLE_HPP
14
15 #include "vehicle_base.h"
16 #include "vehicle_gui.h"
17 #include "landscape.h"
18 #include "window_func.h"
19 #include "widgets/vehicle_widget.h"
20
22 enum AccelStatus {
23 AS_ACCEL,
24 AS_BRAKE,
25 };
26
31 struct GroundVehicleCache {
32 /* Cached acceleration values, recalculated when the cargo on a vehicle changes (in addition to the conditions below) */
33 uint32 cached_weight;
34 uint32 cached_slope_resistance;
35 uint32 cached_max_te;
36 uint16 cached_axle_resistance;
37
38 /* Cached acceleration values, recalculated on load and each time a vehicle is added to/removed from the consist. */
39 uint16 cached_max_track_speed;
40 uint32 cached_power;
41 uint32 cached_air_drag;
42
43 /* Cached NewGRF values, recalculated on load and each time a vehicle is added to/removed from the consist. */
44 uint16 cached_total_length;
45 EngineID first_engine;
46 uint8 cached_veh_length;
47
48 /* Cached UI information. */
49 uint16 last_speed;
50 };
51
53 enum GroundVehicleFlags {
54 GVF_GOINGUP_BIT = 0,
55 GVF_GOINGDOWN_BIT = 1,
56 GVF_SUPPRESS_IMPLICIT_ORDERS = 2,
57 };
58
80 template <class T, VehicleType Type>
81 struct GroundVehicle : public SpecializedVehicle<T, Type> {
82 GroundVehicleCache gcache;
83 uint16 gv_flags;
84
85 typedef GroundVehicle<T, Type> GroundVehicleBase;
86
90 GroundVehicle() : SpecializedVehicle<T, Type>() {}
91
92 void PowerChanged();
93 void CargoChanged();
94 int GetAcceleration() const;
95 bool IsChainInDepot() const;
96
102 /* virtual */ uint Crash(bool flooded)
103 {
104 /* Crashed vehicles aren't going up or down */
105 for (T *v = T::From(this); v != NULL; v = v->Next()) {
106 ClrBit(v->gv_flags, GVF_GOINGUP_BIT);
107 ClrBit(v->gv_flags, GVF_GOINGDOWN_BIT);
108 }
109 return this->Vehicle::Crash(flooded);
110 }
111
116 inline int64 GetSlopeResistance() const
117 {
118 int64 incl = 0;
119
120 for (const T *u = T::From(this); u != NULL; u = u->Next()) {
121 if (HasBit(u->gv_flags, GVF_GOINGUP_BIT)) {
122 incl += u->gcache.cached_slope_resistance;
123 } else if (HasBit(u->gv_flags, GVF_GOINGDOWN_BIT)) {
124 incl -= u->gcache.cached_slope_resistance;
125 }
126 }
127
128 return incl;
129 }
130
137 inline void UpdateZPositionAndInclination()
138 {
139 this->z_pos = GetSlopePixelZ(this->x_pos, this->y_pos);
140 ClrBit(this->gv_flags, GVF_GOINGUP_BIT);
141 ClrBit(this->gv_flags, GVF_GOINGDOWN_BIT);
142
143 if (T::From(this)->TileMayHaveSlopedTrack()) {
144 /* To check whether the current tile is sloped, and in which
145 * direction it is sloped, we get the 'z' at the center of
146 * the tile (middle_z) and the edge of the tile (old_z),
147 * which we then can compare. */
148 int middle_z = GetSlopePixelZ((this->x_pos & ~TILE_UNIT_MASK) | (TILE_SIZE / 2), (this->y_pos & ~TILE_UNIT_MASK) | (TILE_SIZE / 2));
149
150 if (middle_z != this->z_pos) {
151 SetBit(this->gv_flags, (middle_z > this->z_pos) ? GVF_GOINGUP_BIT : GVF_GOINGDOWN_BIT);
152 }
153 }
154 }
155
162 inline void UpdateZPosition()
163 {
164 #if 0
165 /* The following code does this: */
166
167 if (HasBit(this->gv_flags, GVF_GOINGUP_BIT)) {
168 switch (this->direction) {
169 case DIR_NE:
170 this->z_pos += (this->x_pos & 1); break;
171 case DIR_SW:
172 this->z_pos += (this->x_pos & 1) ^ 1; break;
173 case DIR_NW:
174 this->z_pos += (this->y_pos & 1); break;
175 case DIR_SE:
176 this->z_pos += (this->y_pos & 1) ^ 1; break;
177 default: break;
178 }
179 } else if (HasBit(this->gv_flags, GVF_GOINGDOWN_BIT)) {
180 switch (this->direction) {
181 case DIR_NE:
182 this->z_pos -= (this->x_pos & 1); break;
183 case DIR_SW:
184 this->z_pos -= (this->x_pos & 1) ^ 1; break;
185 case DIR_NW:
186 this->z_pos -= (this->y_pos & 1); break;
187 case DIR_SE:
188 this->z_pos -= (this->y_pos & 1) ^ 1; break;
189 default: break;
190 }
191 }
192
193 /* But gcc 4.4.5 isn't able to nicely optimise it, and the resulting
194 * code is full of conditional jumps. */
195 #endif
196
197 /* Vehicle's Z position can change only if it has GVF_GOINGUP_BIT or GVF_GOINGDOWN_BIT set.
198 * Furthermore, if this function is called once every time the vehicle's position changes,
199 * we know the Z position changes by +/-1 at certain moments - when x_pos, y_pos is odd/even,
200 * depending on orientation of the slope and vehicle's direction */
201
202 if (HasBit(this->gv_flags, GVF_GOINGUP_BIT) || HasBit(this->gv_flags, GVF_GOINGDOWN_BIT)) {
203 if (T::From(this)->HasToUseGetSlopePixelZ()) {
204 /* In some cases, we have to use GetSlopePixelZ() */
205 this->z_pos = GetSlopePixelZ(this->x_pos, this->y_pos);
206 return;
207 }
208 /* DirToDiagDir() is a simple right shift */
209 DiagDirection dir = DirToDiagDir(this->direction);
210 /* Read variables, so the compiler knows the access doesn't trap */
211 int8 x_pos = this->x_pos;
212 int8 y_pos = this->y_pos;
213 /* DiagDirToAxis() is a simple mask */
214 int8 d = DiagDirToAxis(dir) == AXIS_X ? x_pos : y_pos;
215 /* We need only the least significant bit */
216 d &= 1;
217 /* Conditional "^ 1". Optimised to "(dir - 1) <= 1". */
218 d ^= (int8)(dir == DIAGDIR_SW || dir == DIAGDIR_SE);
219 /* Subtraction instead of addition because we are testing for GVF_GOINGUP_BIT.
220 * GVF_GOINGUP_BIT is used because it's bit 0, so simple AND can be used,
221 * without any shift */
222 this->z_pos += HasBit(this->gv_flags, GVF_GOINGUP_BIT) ? d : -d;
223 }
224
225 assert(this->z_pos == GetSlopePixelZ(this->x_pos, this->y_pos));
226 }
227
234 inline int UpdateInclination(bool new_tile, bool update_delta)
235 {
236 int old_z = this->z_pos;
237
238 if (new_tile) {
239 this->UpdateZPositionAndInclination();
240 } else {
241 this->UpdateZPosition();
242 }
243
244 this->UpdateViewport(true, update_delta);
245 return old_z;
246 }
247
251 inline void SetFrontEngine() { SetBit(this->subtype, GVSF_FRONT); }
252
256 inline void ClearFrontEngine() { ClrBit(this->subtype, GVSF_FRONT); }
257
261 inline void SetArticulatedPart() { SetBit(this->subtype, GVSF_ARTICULATED_PART); }
262
266 inline void ClearArticulatedPart() { ClrBit(this->subtype, GVSF_ARTICULATED_PART); }
267
271 inline void SetWagon() { SetBit(this->subtype, GVSF_WAGON); }
272
276 inline void ClearWagon() { ClrBit(this->subtype, GVSF_WAGON); }
277
281 inline void SetEngine() { SetBit(this->subtype, GVSF_ENGINE); }
282
286 inline void ClearEngine() { ClrBit(this->subtype, GVSF_ENGINE); }
287
291 inline void SetFreeWagon() { SetBit(this->subtype, GVSF_FREE_WAGON); }
292
296 inline void ClearFreeWagon() { ClrBit(this->subtype, GVSF_FREE_WAGON); }
297
301 inline void SetMultiheaded() { SetBit(this->subtype, GVSF_MULTIHEADED); }
302
306 inline void ClearMultiheaded() { ClrBit(this->subtype, GVSF_MULTIHEADED); }
307
312 inline bool IsFreeWagon() const { return HasBit(this->subtype, GVSF_FREE_WAGON); }
313
318 inline bool IsEngine() const { return HasBit(this->subtype, GVSF_ENGINE); }
319
324 inline bool IsWagon() const { return HasBit(this->subtype, GVSF_WAGON); }
325
330 inline bool IsMultiheaded() const { return HasBit(this->subtype, GVSF_MULTIHEADED); }
331
336 inline bool IsRearDualheaded() const { return this->IsMultiheaded() && !this->IsEngine(); }
337
343 inline void SetLastSpeed()
344 {
345 if (this->cur_speed != this->gcache.last_speed) {
346 SetWindowWidgetDirty(WC_VEHICLE_VIEW, this->index, WID_VV_START_STOP);
347 this->gcache.last_speed = this->cur_speed;
348 }
349 }
350
351 protected:
365 inline uint DoUpdateSpeed(uint accel, int min_speed, int max_speed)
366 {
367 uint spd = this->subspeed + accel;
368 this->subspeed = (byte)spd;
369
370 /* When we are going faster than the maximum speed, reduce the speed
371 * somewhat gradually. But never lower than the maximum speed. */
372 int tempmax = max_speed;
373 if (this->cur_speed > max_speed) {
374 tempmax = max(this->cur_speed - (this->cur_speed / 10) - 1, max_speed);
375 }
376
377 /* Enforce a maximum and minimum speed. Normally we would use something like
378 * Clamp for this, but in this case min_speed might be below the maximum speed
379 * threshold for some reason. That makes acceleration fail and assertions
380 * happen in Clamp. So make it explicit that min_speed overrules the maximum
381 * speed by explicit ordering of min and max. */
382 this->cur_speed = spd = max(min(this->cur_speed + ((int)spd >> 8), tempmax), min_speed);
383
384 int scaled_spd = this->GetAdvanceSpeed(spd);
385
386 scaled_spd += this->progress;
387 this->progress = 0; // set later in *Handler or *Controller
388 return scaled_spd;
389 }
390 };
391
392 #endif /* GROUND_VEHICLE_HPP */