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