# 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
    00021 enum AccelStatus {
    00022   AS_ACCEL,
    00023   AS_BRAKE
    00024 };
    00025
    00030 struct GroundVehicleCache {
    00031   /* Cached acceleration values, recalculated when the cargo on a vehicle changes (in addition to the conditions below) */
    00032   uint32 cached_weight;
    00033   uint32 cached_slope_resistance;
    00034   uint32 cached_max_te;
    00035   uint16 cached_axle_resistance;
    00036
    00037   /* Cached acceleration values, recalculated on load and each time a vehicle is added to/removed from the consist. */
    00038   uint16 cached_max_track_speed;
    00039   uint32 cached_power;
    00040   uint32 cached_air_drag;
    00041
    00042   /* Cached NewGRF values, recalculated on load and each time a vehicle is added to/removed from the consist. */
    00043   uint16 cached_total_length;
    00044   EngineID first_engine;
    00045   uint8 cached_veh_length;
    00046
    00047   /* Cached UI information. */
    00048   uint16 last_speed;
    00049 };
    00050
    00052 enum GroundVehicleFlags {
    00053   GVF_GOINGUP_BIT               = 0,
    00054   GVF_GOINGDOWN_BIT             = 1,
    00055   GVF_SUPPRESS_IMPLICIT_ORDERS = 2,
    00056 };
    00057
    00079 template <class T, VehicleType Type>
    00080 struct GroundVehicle : public SpecializedVehicle<T, Type> {
    00081   GroundVehicleCache gcache;
    00082   uint16 gv_flags;
    00083
    00084   typedef GroundVehicle<T, Type> GroundVehicleBase;
    00085
    00089   GroundVehicle() : SpecializedVehicle<T, Type>() {}
    00090
    00091   void PowerChanged();
    00092   void CargoChanged();
    00093   void CalculatePower(uint32& power, uint32& max_te, bool breakdowns) const;
    00094   int GetAcceleration();
    00095
    00101   /* virtual */ uint Crash(bool flooded)
    00102   {
    00103     /* Crashed vehicles aren't going up or down */
    00104     for (T *v = T::From(this); v != NULL; v = v->Next()) {
    00105       ClrBit(v->gv_flags, GVF_GOINGUP_BIT);
    00106       ClrBit(v->gv_flags, GVF_GOINGDOWN_BIT);
    00107     }
    00108     return this->Vehicle::Crash(flooded);
    00109   }
    00110
    00115   FORCEINLINE int32 GetSlopeResistance() const
    00116   {
    00117     int32 incl = 0;
    00118
    00119     for (const T *u = T::From(this); u != NULL; u = u->Next()) {
    00120       if (HasBit(u->gv_flags, GVF_GOINGUP_BIT)) {
    00121         incl += u->gcache.cached_slope_resistance;
    00122       } else if (HasBit(u->gv_flags, GVF_GOINGDOWN_BIT)) {
    00123         incl -= u->gcache.cached_slope_resistance;
    00124       }
    00125     }
    00126
    00127     return incl;
    00128   }
    00129
    00136   FORCEINLINE void UpdateZPositionAndInclination()
    00137   {
    00138     this->z_pos = GetSlopeZ(this->x_pos, this->y_pos);
    00139     ClrBit(this->gv_flags, GVF_GOINGUP_BIT);
    00140     ClrBit(this->gv_flags, GVF_GOINGDOWN_BIT);
    00141
    00142     if (T::From(this)->TileMayHaveSlopedTrack()) {
    00143       /* To check whether the current tile is sloped, and in which
    00144        * direction it is sloped, we get the 'z' at the center of
    00145        * the tile (middle_z) and the edge of the tile (old_z),
    00146        * which we then can compare. */
    00147       int middle_z = GetSlopeZ((this->x_pos & ~TILE_UNIT_MASK) | HALF_TILE_SIZE, (this->y_pos & ~TILE_UNIT_MASK) | HALF_TILE_SIZE);
    00148
    00149       if (middle_z != this->z_pos) {
    00150         SetBit(this->gv_flags, (middle_z > this->z_pos) ? GVF_GOINGUP_BIT : GVF_GOINGDOWN_BIT);
    00151       }
    00152     }
    00153   }
    00154
    00159   FORCEINLINE void UpdateZPosition()
    00160   {
    00161     /* Vehicle's Z position can change only if it has GVF_GOINGUP_BIT or GVF_GOINGDOWN_BIT set */
    00162     if (HasBit(this->gv_flags, GVF_GOINGUP_BIT) || HasBit(this->gv_flags, GVF_GOINGDOWN_BIT)) {
    00163       this->z_pos = (int)GetSlopeZ(this->x_pos, this->y_pos);
    00164     } else {
    00165       /* Verify that assumption. */
    00166       assert(this->z_pos == (int)GetSlopeZ(this->x_pos, this->y_pos));
    00167     }
    00168   }
    00169
    00176   FORCEINLINE int32 UpdateInclination(bool new_tile, bool turned)
    00177   {
    00178     int32 old_z = this->z_pos;
    00179
    00180     if (new_tile) {
    00181       this->UpdateZPositionAndInclination();
    00182     } else {
    00183       this->UpdateZPosition();
    00184     }
    00185
    00186     this->UpdateViewport(true, turned);
    00187     return old_z;
    00188   }
    00189
    00193   FORCEINLINE void SetFrontEngine() { SetBit(this->subtype, GVSF_FRONT); }
    00194
    00198   FORCEINLINE void ClearFrontEngine() { ClrBit(this->subtype, GVSF_FRONT); }
    00199
    00203   FORCEINLINE void SetArticulatedPart() { SetBit(this->subtype, GVSF_ARTICULATED_PART); }
    00204
    00208   FORCEINLINE void ClearArticulatedPart() { ClrBit(this->subtype, GVSF_ARTICULATED_PART); }
    00209
    00213   FORCEINLINE void SetWagon() { SetBit(this->subtype, GVSF_WAGON); }
    00214
    00218   FORCEINLINE void ClearWagon() { ClrBit(this->subtype, GVSF_WAGON); }
    00219
    00223   FORCEINLINE void SetEngine() { SetBit(this->subtype, GVSF_ENGINE); }
    00224
    00228   FORCEINLINE void ClearEngine() { ClrBit(this->subtype, GVSF_ENGINE); }
    00229
    00233   FORCEINLINE void SetFreeWagon() { SetBit(this->subtype, GVSF_FREE_WAGON); }
    00234
    00238   FORCEINLINE void ClearFreeWagon() { ClrBit(this->subtype, GVSF_FREE_WAGON); }
    00239
    00243   FORCEINLINE void SetMultiheaded() { SetBit(this->subtype, GVSF_MULTIHEADED); }
    00244
    00248   FORCEINLINE void ClearMultiheaded() { ClrBit(this->subtype, GVSF_MULTIHEADED); }
    00249
    00254   FORCEINLINE bool IsFreeWagon() const { return HasBit(this->subtype, GVSF_FREE_WAGON); }
    00255
    00260   FORCEINLINE bool IsEngine() const { return HasBit(this->subtype, GVSF_ENGINE); }
    00261
    00266   FORCEINLINE bool IsWagon() const { return HasBit(this->subtype, GVSF_WAGON); }
    00267
    00272   FORCEINLINE bool IsMultiheaded() const { return HasBit(this->subtype, GVSF_MULTIHEADED); }
    00273
    00278   FORCEINLINE bool IsRearDualheaded() const { return this->IsMultiheaded() && !this->IsEngine(); }
    00279
    00285   FORCEINLINE void SetLastSpeed()
    00286   {
    00287     if (this->cur_speed != this->gcache.last_speed) {
    00288       SetWindowWidgetDirty(WC_VEHICLE_VIEW, this->index, VVW_WIDGET_START_STOP_VEH);
    00289       this->gcache.last_speed = this->cur_speed;
    00290     }
    00291   }
    00292
    00293 protected:
    00307   FORCEINLINE uint DoUpdateSpeed(uint accel, int min_speed, int max_speed)
    00308   {
    00309     uint spd = this->subspeed + accel;
    00310     this->subspeed = (byte)spd;
    00311
    00312     /* When we are going faster than the maximum speed, reduce the speed
    00313      * somewhat gradually. But never lower than the maximum speed. */
    00314     int tempmax = ((this->breakdown_ctr == 1) ? this->cur_speed : max_speed);
    00315
    00316     if (this->breakdown_ctr == 1) {
    00317       if (this->breakdown_type == BREAKDOWN_LOW_POWER) {
    00318         if((this->tick_counter & 0x7) == 0) {
    00319           if(this->cur_speed > (this->breakdown_severity * max_speed) >> 8) {
    00320             tempmax = this->cur_speed - (this->cur_speed / 10) - 1;
    00321           } else {
    00322             tempmax = (this->breakdown_severity * max_speed) >> 8;
    00323           }
    00324         }
    00325       }
    00326
    00327       if(this->breakdown_type == BREAKDOWN_LOW_SPEED)
    00328         tempmax = min(max_speed, this->breakdown_severity);
    00329     }
    00330
    00331     if (this->cur_speed > max_speed) {
    00332       tempmax = max(this->cur_speed - (this->cur_speed / 10) - 1, max_speed);
    00333     }
    00334
    00335     /* Enforce a maximum and minimum speed. Normally we would use something like
    00336      * Clamp for this, but in this case min_speed might be below the maximum speed
    00337      * threshold for some reason. That makes acceleration fail and assertions
    00338      * happen in Clamp. So make it explicit that min_speed overrules the maximum
    00339      * speed by explicit ordering of min and max. */
    00340     this->cur_speed = spd = max(min(this->cur_speed + ((int)spd >> 8), tempmax), min_speed);
    00341
    00342     int scaled_spd = this->GetAdvanceSpeed(spd);
    00343
    00344     scaled_spd += this->progress;
    00345     this->progress = 0; // set later in *Handler or *Controller
    00346     return scaled_spd;
    00347   }
    00348 };
    00349
    00350 #endif /* GROUND_VEHICLE_HPP */