OpenTTD
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 
25 };
26 
32  /* Cached acceleration values, recalculated when the cargo on a vehicle changes (in addition to the conditions below) */
33  uint32 cached_weight;
35  uint32 cached_max_te;
37 
38  /* Cached acceleration values, recalculated on load and each time a vehicle is added to/removed from the consist. */
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. */
47 
48  /* Cached UI information. */
49  uint16 last_speed;
50 };
51 
57 };
58 
80 template <class T, VehicleType Type>
81 struct GroundVehicle : public SpecializedVehicle<T, Type> {
83  uint16 gv_flags;
84 
86 
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 
138  {
139  this->z_pos = GetSlopePixelZ(this->x_pos, this->y_pos);
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 
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) {
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 
262 
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) {
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 */