# ground_vehicle.hpp

Go to the documentation of this file.

    00001 /* $Id$ */
    00002
    00003 /*
    00004  * This file is part of OpenTTD.
    00005  * 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.
    00006  * 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.
    00007  * 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/>.
    00008  */
    00009
    00012 #ifndef GROUND_VEHICLE_HPP
    00013 #define GROUND_VEHICLE_HPP
    00014
    00015 #include "vehicle_base.h"
    00016 #include "vehicle_gui.h"
    00017 #include "landscape.h"
    00018 #include "window_func.h"
    00019 #include "widgets/vehicle_widget.h"
    00020
    00022 enum AccelStatus {
    00023   AS_ACCEL,
    00024   AS_BRAKE,
    00025 };
    00026
    00031 struct GroundVehicleCache {
    00032   /* Cached acceleration values, recalculated when the cargo on a vehicle changes (in addition to the conditions below) */
    00033   uint32 cached_weight;
    00034   uint32 cached_slope_resistance;
    00035   uint32 cached_max_te;
    00036   uint16 cached_axle_resistance;
    00037
    00038   /* Cached acceleration values, recalculated on load and each time a vehicle is added to/removed from the consist. */
    00039   uint16 cached_max_track_speed;
    00040   uint32 cached_power;
    00041   uint32 cached_air_drag;
    00042
    00043   /* Cached NewGRF values, recalculated on load and each time a vehicle is added to/removed from the consist. */
    00044   uint16 cached_total_length;
    00045   EngineID first_engine;
    00046   uint8 cached_veh_length;
    00047
    00048   /* Cached UI information. */
    00049   uint16 last_speed;
    00050 };
    00051
    00053 enum GroundVehicleFlags {
    00054   GVF_GOINGUP_BIT              = 0,
    00055   GVF_GOINGDOWN_BIT            = 1,
    00056   GVF_SUPPRESS_IMPLICIT_ORDERS = 2,
    00057 };
    00058
    00080 template <class T, VehicleType Type>
    00081 struct GroundVehicle : public SpecializedVehicle<T, Type> {
    00082   GroundVehicleCache gcache;
    00083   uint16 gv_flags;
    00084
    00085   typedef GroundVehicle<T, Type> GroundVehicleBase;
    00086
    00090   GroundVehicle() : SpecializedVehicle<T, Type>() {}
    00091
    00092   void PowerChanged();
    00093   void CargoChanged();
    00094   int GetAcceleration() const;
    00095   bool IsChainInDepot() const;
    00096
    00102   /* virtual */ uint Crash(bool flooded)
    00103   {
    00104     /* Crashed vehicles aren't going up or down */
    00105     for (T *v = T::From(this); v != NULL; v = v->Next()) {
    00106       ClrBit(v->gv_flags, GVF_GOINGUP_BIT);
    00107       ClrBit(v->gv_flags, GVF_GOINGDOWN_BIT);
    00108     }
    00109     return this->Vehicle::Crash(flooded);
    00110   }
    00111
    00116   inline int32 GetSlopeResistance() const
    00117   {
    00118     int32 incl = 0;
    00119
    00120     for (const T *u = T::From(this); u != NULL; u = u->Next()) {
    00121       if (HasBit(u->gv_flags, GVF_GOINGUP_BIT)) {
    00122         incl += u->gcache.cached_slope_resistance;
    00123       } else if (HasBit(u->gv_flags, GVF_GOINGDOWN_BIT)) {
    00124         incl -= u->gcache.cached_slope_resistance;
    00125       }
    00126     }
    00127
    00128     return incl;
    00129   }
    00130
    00137   inline void UpdateZPositionAndInclination()
    00138   {
    00139     this->z_pos = GetSlopePixelZ(this->x_pos, this->y_pos);
    00140     ClrBit(this->gv_flags, GVF_GOINGUP_BIT);
    00141     ClrBit(this->gv_flags, GVF_GOINGDOWN_BIT);
    00142
    00143     if (T::From(this)->TileMayHaveSlopedTrack()) {
    00144       /* To check whether the current tile is sloped, and in which
    00145        * direction it is sloped, we get the 'z' at the center of
    00146        * the tile (middle_z) and the edge of the tile (old_z),
    00147        * which we then can compare. */
    00148       int middle_z = GetSlopePixelZ((this->x_pos & ~TILE_UNIT_MASK) | (TILE_SIZE / 2), (this->y_pos & ~TILE_UNIT_MASK) | (TILE_SIZE / 2));
    00149
    00150       if (middle_z != this->z_pos) {
    00151         SetBit(this->gv_flags, (middle_z > this->z_pos) ? GVF_GOINGUP_BIT : GVF_GOINGDOWN_BIT);
    00152       }
    00153     }
    00154   }
    00155
    00162   inline void UpdateZPosition()
    00163   {
    00164 #if 0
    00165     /* The following code does this: */
    00166
    00167     if (HasBit(this->gv_flags, GVF_GOINGUP_BIT)) {
    00168       switch (this->direction) {
    00169         case DIR_NE:
    00170           this->z_pos += (this->x_pos & 1); break;
    00171         case DIR_SW:
    00172           this->z_pos += (this->x_pos & 1) ^ 1; break;
    00173         case DIR_NW:
    00174           this->z_pos += (this->y_pos & 1); break;
    00175         case DIR_SE:
    00176           this->z_pos += (this->y_pos & 1) ^ 1; break;
    00177         default: break;
    00178       }
    00179     } else if (HasBit(this->gv_flags, GVF_GOINGDOWN_BIT)) {
    00180       switch (this->direction) {
    00181         case DIR_NE:
    00182           this->z_pos -= (this->x_pos & 1); break;
    00183         case DIR_SW:
    00184           this->z_pos -= (this->x_pos & 1) ^ 1; break;
    00185         case DIR_NW:
    00186           this->z_pos -= (this->y_pos & 1); break;
    00187         case DIR_SE:
    00188           this->z_pos -= (this->y_pos & 1) ^ 1; break;
    00189         default: break;
    00190       }
    00191     }
    00192
    00193     /* But gcc 4.4.5 isn't able to nicely optimise it, and the resulting
    00194      * code is full of conditional jumps. */
    00195 #endif
    00196
    00197     /* Vehicle's Z position can change only if it has GVF_GOINGUP_BIT or GVF_GOINGDOWN_BIT set.
    00198      * Furthermore, if this function is called once every time the vehicle's position changes,
    00199      * we know the Z position changes by +/-1 at certain moments - when x_pos, y_pos is odd/even,
    00200      * depending on orientation of the slope and vehicle's direction */
    00201
    00202     if (HasBit(this->gv_flags, GVF_GOINGUP_BIT) || HasBit(this->gv_flags, GVF_GOINGDOWN_BIT)) {
    00203       if (T::From(this)->HasToUseGetSlopePixelZ()) {
    00204         /* In some cases, we have to use GetSlopePixelZ() */
    00205         this->z_pos = GetSlopePixelZ(this->x_pos, this->y_pos);
    00206         return;
    00207       }
    00208       /* DirToDiagDir() is a simple right shift */
    00209       DiagDirection dir = DirToDiagDir(this->direction);
    00210       /* Read variables, so the compiler knows the access doesn't trap */
    00211       int8 x_pos = this->x_pos;
    00212       int8 y_pos = this->y_pos;
    00213       /* DiagDirToAxis() is a simple mask */
    00214       int8 d = DiagDirToAxis(dir) == AXIS_X ? x_pos : y_pos;
    00215       /* We need only the least significant bit */
    00216       d &= 1;
    00217       /* Conditional "^ 1". Optimised to "(dir - 1) <= 1". */
    00218       d ^= (int8)(dir == DIAGDIR_SW || dir == DIAGDIR_SE);
    00219       /* Subtraction instead of addition because we are testing for GVF_GOINGUP_BIT.
    00220        * GVF_GOINGUP_BIT is used because it's bit 0, so simple AND can be used,
    00221        * without any shift */
    00222       this->z_pos += HasBit(this->gv_flags, GVF_GOINGUP_BIT) ? d : -d;
    00223     }
    00224
    00225     assert(this->z_pos == GetSlopePixelZ(this->x_pos, this->y_pos));
    00226   }
    00227
    00234   inline byte UpdateInclination(bool new_tile, bool update_delta)
    00235   {
    00236     byte old_z = this->z_pos;
    00237
    00238     if (new_tile) {
    00239       this->UpdateZPositionAndInclination();
    00240     } else {
    00241       this->UpdateZPosition();
    00242     }
    00243
    00244     this->UpdateViewport(true, update_delta);
    00245     return old_z;
    00246   }
    00247
    00251   inline void SetFrontEngine() { SetBit(this->subtype, GVSF_FRONT); }
    00252
    00256   inline void ClearFrontEngine() { ClrBit(this->subtype, GVSF_FRONT); }
    00257
    00261   inline void SetArticulatedPart() { SetBit(this->subtype, GVSF_ARTICULATED_PART); }
    00262
    00266   inline void ClearArticulatedPart() { ClrBit(this->subtype, GVSF_ARTICULATED_PART); }
    00267
    00271   inline void SetWagon() { SetBit(this->subtype, GVSF_WAGON); }
    00272
    00276   inline void ClearWagon() { ClrBit(this->subtype, GVSF_WAGON); }
    00277
    00281   inline void SetEngine() { SetBit(this->subtype, GVSF_ENGINE); }
    00282
    00286   inline void ClearEngine() { ClrBit(this->subtype, GVSF_ENGINE); }
    00287
    00291   inline void SetFreeWagon() { SetBit(this->subtype, GVSF_FREE_WAGON); }
    00292
    00296   inline void ClearFreeWagon() { ClrBit(this->subtype, GVSF_FREE_WAGON); }
    00297
    00301   inline void SetMultiheaded() { SetBit(this->subtype, GVSF_MULTIHEADED); }
    00302
    00306   inline void ClearMultiheaded() { ClrBit(this->subtype, GVSF_MULTIHEADED); }
    00307
    00312   inline bool IsFreeWagon() const { return HasBit(this->subtype, GVSF_FREE_WAGON); }
    00313
    00318   inline bool IsEngine() const { return HasBit(this->subtype, GVSF_ENGINE); }
    00319
    00324   inline bool IsWagon() const { return HasBit(this->subtype, GVSF_WAGON); }
    00325
    00330   inline bool IsMultiheaded() const { return HasBit(this->subtype, GVSF_MULTIHEADED); }
    00331
    00336   inline bool IsRearDualheaded() const { return this->IsMultiheaded() && !this->IsEngine(); }
    00337
    00343   inline void SetLastSpeed()
    00344   {
    00345     if (this->cur_speed != this->gcache.last_speed) {
    00346       SetWindowWidgetDirty(WC_VEHICLE_VIEW, this->index, WID_VV_START_STOP);
    00347       this->gcache.last_speed = this->cur_speed;
    00348     }
    00349   }
    00350
    00351 protected:
    00365   inline uint DoUpdateSpeed(uint accel, int min_speed, int max_speed)
    00366   {
    00367     uint spd = this->subspeed + accel;
    00368     this->subspeed = (byte)spd;
    00369
    00370     /* When we are going faster than the maximum speed, reduce the speed
    00371      * somewhat gradually. But never lower than the maximum speed. */
    00372     int tempmax = max_speed;
    00373     if (this->cur_speed > max_speed) {
    00374       tempmax = max(this->cur_speed - (this->cur_speed / 10) - 1, max_speed);
    00375     }
    00376
    00377     /* Enforce a maximum and minimum speed. Normally we would use something like
    00378      * Clamp for this, but in this case min_speed might be below the maximum speed
    00379      * threshold for some reason. That makes acceleration fail and assertions
    00380      * happen in Clamp. So make it explicit that min_speed overrules the maximum
    00381      * speed by explicit ordering of min and max. */
    00382     this->cur_speed = spd = max(min(this->cur_speed + ((int)spd >> 8), tempmax), min_speed);
    00383
    00384     int scaled_spd = this->GetAdvanceSpeed(spd);
    00385
    00386     scaled_spd += this->progress;
    00387     this->progress = 0; // set later in *Handler or *Controller
    00388     return scaled_spd;
    00389   }
    00390 };
    00391
    00392 #endif /* GROUND_VEHICLE_HPP */