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 */