OpenTTD
ground_vehicle.cpp
Go to the documentation of this file.
1 /* $Id: ground_vehicle.cpp 26702 2014-07-22 19:46:10Z 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 "train.h"
14 #include "roadveh.h"
15 #include "depot_map.h"
16 
17 #include "safeguards.h"
18 
22 template <class T, VehicleType Type>
24 {
25  assert(this->First() == this);
26  const T *v = T::From(this);
27 
28  uint32 total_power = 0;
29  uint32 max_te = 0;
30  uint32 number_of_parts = 0;
31  uint16 max_track_speed = v->GetDisplayMaxSpeed();
32 
33  for (const T *u = v; u != NULL; u = u->Next()) {
34  uint32 current_power = u->GetPower() + u->GetPoweredPartPower(u);
35  total_power += current_power;
36 
37  /* Only powered parts add tractive effort. */
38  if (current_power > 0) max_te += u->GetWeight() * u->GetTractiveEffort();
39  number_of_parts++;
40 
41  /* Get minimum max speed for this track. */
42  uint16 track_speed = u->GetMaxTrackSpeed();
43  if (track_speed > 0) max_track_speed = min(max_track_speed, track_speed);
44  }
45 
46  byte air_drag;
47  byte air_drag_value = v->GetAirDrag();
48 
49  /* If air drag is set to zero (default), the resulting air drag coefficient is dependent on max speed. */
50  if (air_drag_value == 0) {
51  uint16 max_speed = v->GetDisplayMaxSpeed();
52  /* Simplification of the method used in TTDPatch. It uses <= 10 to change more steadily from 128 to 196. */
53  air_drag = (max_speed <= 10) ? 192 : max(2048 / max_speed, 1);
54  } else {
55  /* According to the specs, a value of 0x01 in the air drag property means "no air drag". */
56  air_drag = (air_drag_value == 1) ? 0 : air_drag_value;
57  }
58 
59  this->gcache.cached_air_drag = air_drag + 3 * air_drag * number_of_parts / 20;
60 
61  max_te *= 10000; // Tractive effort in (tonnes * 1000 * 10 =) N.
62  max_te /= 256; // Tractive effort is a [0-255] coefficient.
63  if (this->gcache.cached_power != total_power || this->gcache.cached_max_te != max_te) {
64  /* Stop the vehicle if it has no power. */
65  if (total_power == 0) this->vehstatus |= VS_STOPPED;
66 
67  this->gcache.cached_power = total_power;
68  this->gcache.cached_max_te = max_te;
69  SetWindowDirty(WC_VEHICLE_DETAILS, this->index);
71  }
72 
73  this->gcache.cached_max_track_speed = max_track_speed;
74 }
75 
80 template <class T, VehicleType Type>
82 {
83  assert(this->First() == this);
84  uint32 weight = 0;
85 
86  for (T *u = T::From(this); u != NULL; u = u->Next()) {
87  uint32 current_weight = u->GetWeight();
88  weight += current_weight;
89  /* Slope steepness is in percent, result in N. */
90  u->gcache.cached_slope_resistance = current_weight * u->GetSlopeSteepness() * 100;
91  }
92 
93  /* Store consist weight in cache. */
94  this->gcache.cached_weight = max<uint32>(1, weight);
95  /* Friction in bearings and other mechanical parts is 0.1% of the weight (result in N). */
96  this->gcache.cached_axle_resistance = 10 * weight;
97 
98  /* Now update vehicle power (tractive effort is dependent on weight). */
99  this->PowerChanged();
100 }
101 
106 template <class T, VehicleType Type>
108 {
109  /* Templated class used for function calls for performance reasons. */
110  const T *v = T::From(this);
111  /* Speed is used squared later on, so U16 * U16, and then multiplied by other values. */
112  int64 speed = v->GetCurrentSpeed(); // [km/h-ish]
113 
114  /* Weight is stored in tonnes. */
115  int32 mass = this->gcache.cached_weight;
116 
117  /* Power is stored in HP, we need it in watts.
118  * Each vehicle can have U16 power, 128 vehicles, HP -> watt
119  * and km/h to m/s conversion below result in a maxium of
120  * about 1.1E11, way more than 4.3E9 of int32. */
121  int64 power = this->gcache.cached_power * 746ll;
122 
123  /* This is constructed from:
124  * - axle resistance: U16 power * 10 for 128 vehicles.
125  * * 8.3E7
126  * - rolling friction: U16 power * 144 for 128 vehicles.
127  * * 1.2E9
128  * - slope resistance: U16 weight * 100 * 10 (steepness) for 128 vehicles.
129  * * 8.4E9
130  * - air drag: 28 * (U8 drag + 3 * U8 drag * 128 vehicles / 20) * U16 speed * U16 speed
131  * * 6.2E14 before dividing by 1000
132  * Sum is 6.3E11, more than 4.3E9 of int32, so int64 is needed.
133  */
134  int64 resistance = 0;
135 
136  bool maglev = v->GetAccelerationType() == 2;
137 
138  const int area = v->GetAirDragArea();
139  if (!maglev) {
140  /* Static resistance plus rolling friction. */
141  resistance = this->gcache.cached_axle_resistance;
142  resistance += mass * v->GetRollingFriction();
143  }
144  /* Air drag; the air drag coefficient is in an arbitrary NewGRF-unit,
145  * so we need some magic conversion factor. */
146  resistance += (area * this->gcache.cached_air_drag * speed * speed) / 1000;
147 
148  resistance += this->GetSlopeResistance();
149 
150  /* This value allows to know if the vehicle is accelerating or braking. */
151  AccelStatus mode = v->GetAccelerationStatus();
152 
153  const int max_te = this->gcache.cached_max_te; // [N]
154  /* Constructued from power, with need to multiply by 18 and assuming
155  * low speed, it needs to be a 64 bit integer too. */
156  int64 force;
157  if (speed > 0) {
158  if (!maglev) {
159  /* Conversion factor from km/h to m/s is 5/18 to get [N] in the end. */
160  force = power * 18 / (speed * 5);
161  if (mode == AS_ACCEL && force > max_te) force = max_te;
162  } else {
163  force = power / 25;
164  }
165  } else {
166  /* "Kickoff" acceleration. */
167  force = (mode == AS_ACCEL && !maglev) ? min(max_te, power) : power;
168  force = max(force, (mass * 8) + resistance);
169  }
170 
171  if (mode == AS_ACCEL) {
172  /* Easy way out when there is no acceleration. */
173  if (force == resistance) return 0;
174 
175  /* When we accelerate, make sure we always keep doing that, even when
176  * the excess force is more than the mass. Otherwise a vehicle going
177  * down hill will never slow down enough, and a vehicle that came up
178  * a hill will never speed up enough to (eventually) get back to the
179  * same (maximum) speed. */
180  int accel = ClampToI32((force - resistance) / (mass * 4));
181  return force < resistance ? min(-1, accel) : max(1, accel);
182  } else {
183  return ClampToI32(min(-force - resistance, -10000) / mass);
184  }
185 }
186 
191 template <class T, VehicleType Type>
193 {
194  const T *v = this->First();
195  /* Is the front engine stationary in the depot? */
196  assert_compile((int)TRANSPORT_RAIL == (int)VEH_TRAIN);
197  assert_compile((int)TRANSPORT_ROAD == (int)VEH_ROAD);
198  if (!IsDepotTypeTile(v->tile, (TransportType)Type) || v->cur_speed != 0) return false;
199 
200  /* Check whether the rest is also already trying to enter the depot. */
201  for (; v != NULL; v = v->Next()) {
202  if (!v->T::IsInDepot() || v->tile != this->tile) return false;
203  }
204 
205  return true;
206 }
207 
208 /* Instantiation for Train */
209 template struct GroundVehicle<Train, VEH_TRAIN>;
210 /* Instantiation for RoadVehicle */
Road vehicle states.
Vehicle is stopped by the player.
Definition: vehicle_base.h:33
int GetAcceleration() const
Calculates the acceleration of the vehicle under its current conditions.
AccelStatus
What is the status of our acceleration?
void SetWindowDirty(WindowClass cls, WindowNumber number)
Mark window as dirty (in need of repainting)
Definition: window.cpp:3120
Base for the train class.
Start or stop this vehicle, and show information about the current state.
void CargoChanged()
Recalculates the cached weight of a vehicle and its parts.
static T max(const T a, const T b)
Returns the maximum of two values.
Definition: math_func.hpp:26
Map related accessors for depots.
static bool IsDepotTypeTile(TileIndex tile, TransportType type)
Check if a tile is a depot and it is a depot of the given type.
Definition: depot_map.h:20
Definition of base types and functions in a cross-platform compatible way.
A number of safeguards to prevent using unsafe methods.
void PowerChanged()
Recalculates the cached total power of a vehicle.
Vehicle view; Window numbers:
Definition: window_type.h:334
Road vehicle type.
Definition: vehicle_type.h:25
static T min(const T a, const T b)
Returns the minimum of two values.
Definition: math_func.hpp:42
We want to go faster, if possible of course.
Transport by train.
static int32 ClampToI32(const int64 a)
Reduce a signed 64-bit int to a signed 32-bit one.
Definition: math_func.hpp:203
void SetWindowWidgetDirty(WindowClass cls, WindowNumber number, byte widget_index)
Mark a particular widget in a particular window as dirty (in need of repainting)
Definition: window.cpp:3134
TransportType
Available types of transport.
Transport by road vehicle.
Vehicle details; Window numbers:
Definition: window_type.h:195
Train vehicle type.
Definition: vehicle_type.h:24
bool IsChainInDepot() const
Check whether the whole vehicle chain is in the depot.