ground_vehicle.hpp

Go to the documentation of this file.
00001 /* $Id: ground_vehicle.hpp 22520 2011-05-29 17:55:15Z rubidium $ */
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   int GetAcceleration() const;
00094 
00100   /* virtual */ uint Crash(bool flooded)
00101   {
00102     /* Crashed vehicles aren't going up or down */
00103     for (T *v = T::From(this); v != NULL; v = v->Next()) {
00104       ClrBit(v->gv_flags, GVF_GOINGUP_BIT);
00105       ClrBit(v->gv_flags, GVF_GOINGDOWN_BIT);
00106     }
00107     return this->Vehicle::Crash(flooded);
00108   }
00109 
00114   FORCEINLINE int32 GetSlopeResistance() const
00115   {
00116     int32 incl = 0;
00117 
00118     for (const T *u = T::From(this); u != NULL; u = u->Next()) {
00119       if (HasBit(u->gv_flags, GVF_GOINGUP_BIT)) {
00120         incl += u->gcache.cached_slope_resistance;
00121       } else if (HasBit(u->gv_flags, GVF_GOINGDOWN_BIT)) {
00122         incl -= u->gcache.cached_slope_resistance;
00123       }
00124     }
00125 
00126     return incl;
00127   }
00128 
00135   FORCEINLINE void UpdateZPositionAndInclination()
00136   {
00137     this->z_pos = GetSlopeZ(this->x_pos, this->y_pos);
00138     ClrBit(this->gv_flags, GVF_GOINGUP_BIT);
00139     ClrBit(this->gv_flags, GVF_GOINGDOWN_BIT);
00140 
00141     if (T::From(this)->TileMayHaveSlopedTrack()) {
00142       /* To check whether the current tile is sloped, and in which
00143        * direction it is sloped, we get the 'z' at the center of
00144        * the tile (middle_z) and the edge of the tile (old_z),
00145        * which we then can compare. */
00146       byte middle_z = GetSlopeZ((this->x_pos & ~TILE_UNIT_MASK) | HALF_TILE_SIZE, (this->y_pos & ~TILE_UNIT_MASK) | HALF_TILE_SIZE);
00147 
00148       if (middle_z != this->z_pos) {
00149         SetBit(this->gv_flags, (middle_z > this->z_pos) ? GVF_GOINGUP_BIT : GVF_GOINGDOWN_BIT);
00150       }
00151     }
00152   }
00153 
00160   FORCEINLINE void UpdateZPosition()
00161   {
00162 #if 0
00163     /* The following code does this: */
00164 
00165     if (HasBit(this->gv_flags, GVF_GOINGUP_BIT)) {
00166       switch (this->direction) {
00167         case DIR_NE:
00168           this->z_pos += (this->x_pos & 1); break;
00169         case DIR_SW:
00170           this->z_pos += (this->x_pos & 1) ^ 1; break;
00171         case DIR_NW:
00172           this->z_pos += (this->y_pos & 1); break;
00173         case DIR_SE:
00174           this->z_pos += (this->y_pos & 1) ^ 1; break;
00175         default: break;
00176       }
00177     } else if (HasBit(this->gv_flags, GVF_GOINGDOWN_BIT)) {
00178       switch (this->direction) {
00179         case DIR_NE:
00180           this->z_pos -= (this->x_pos & 1); break;
00181         case DIR_SW:
00182           this->z_pos -= (this->x_pos & 1) ^ 1; break;
00183         case DIR_NW:
00184           this->z_pos -= (this->y_pos & 1); break;
00185         case DIR_SE:
00186           this->z_pos -= (this->y_pos & 1) ^ 1; break;
00187         default: break;
00188       }
00189     }
00190 
00191     /* But gcc 4.4.5 isn't able to nicely optimise it, and the resulting
00192      * code is full of conditional jumps. */
00193 #endif
00194 
00195     /* Vehicle's Z position can change only if it has GVF_GOINGUP_BIT or GVF_GOINGDOWN_BIT set.
00196      * Furthermore, if this function is called once every time the vehicle's position changes,
00197      * we know the Z position changes by +/-1 at certain moments - when x_pos, y_pos is odd/even,
00198      * depending on orientation of the slope and vehicle's direction */
00199 
00200     if (HasBit(this->gv_flags, GVF_GOINGUP_BIT) || HasBit(this->gv_flags, GVF_GOINGDOWN_BIT)) {
00201       if (T::From(this)->HasToUseGetSlopeZ()) {
00202         /* In some cases, we have to use GetSlopeZ() */
00203         this->z_pos = GetSlopeZ(this->x_pos, this->y_pos);
00204         return;
00205       }
00206       /* DirToDiagDir() is a simple right shift */
00207       DiagDirection dir = DirToDiagDir(this->direction);
00208       /* Read variables, so the compiler knows the access doesn't trap */
00209       int8 x_pos = this->x_pos;
00210       int8 y_pos = this->y_pos;
00211       /* DiagDirToAxis() is a simple mask */
00212       int8 d = DiagDirToAxis(dir) == AXIS_X ? x_pos : y_pos;
00213       /* We need only the least significant bit */
00214       d &= 1;
00215       /* Conditional "^ 1". Optimised to "(dir - 1) <= 1". */
00216       d ^= (int8)(dir == DIAGDIR_SW || dir == DIAGDIR_SE);
00217       /* Subtraction instead of addition because we are testing for GVF_GOINGUP_BIT.
00218        * GVF_GOINGUP_BIT is used because it's bit 0, so simple AND can be used,
00219        * without any shift */
00220       this->z_pos += HasBit(this->gv_flags, GVF_GOINGUP_BIT) ? d : -d;
00221     }
00222 
00223     assert(this->z_pos == GetSlopeZ(this->x_pos, this->y_pos));
00224   }
00225 
00232   FORCEINLINE byte UpdateInclination(bool new_tile, bool turned)
00233   {
00234     byte old_z = this->z_pos;
00235 
00236     if (new_tile) {
00237       this->UpdateZPositionAndInclination();
00238     } else {
00239       this->UpdateZPosition();
00240     }
00241 
00242     this->UpdateViewport(true, turned);
00243     return old_z;
00244   }
00245 
00249   FORCEINLINE void SetFrontEngine() { SetBit(this->subtype, GVSF_FRONT); }
00250 
00254   FORCEINLINE void ClearFrontEngine() { ClrBit(this->subtype, GVSF_FRONT); }
00255 
00259   FORCEINLINE void SetArticulatedPart() { SetBit(this->subtype, GVSF_ARTICULATED_PART); }
00260 
00264   FORCEINLINE void ClearArticulatedPart() { ClrBit(this->subtype, GVSF_ARTICULATED_PART); }
00265 
00269   FORCEINLINE void SetWagon() { SetBit(this->subtype, GVSF_WAGON); }
00270 
00274   FORCEINLINE void ClearWagon() { ClrBit(this->subtype, GVSF_WAGON); }
00275 
00279   FORCEINLINE void SetEngine() { SetBit(this->subtype, GVSF_ENGINE); }
00280 
00284   FORCEINLINE void ClearEngine() { ClrBit(this->subtype, GVSF_ENGINE); }
00285 
00289   FORCEINLINE void SetFreeWagon() { SetBit(this->subtype, GVSF_FREE_WAGON); }
00290 
00294   FORCEINLINE void ClearFreeWagon() { ClrBit(this->subtype, GVSF_FREE_WAGON); }
00295 
00299   FORCEINLINE void SetMultiheaded() { SetBit(this->subtype, GVSF_MULTIHEADED); }
00300 
00304   FORCEINLINE void ClearMultiheaded() { ClrBit(this->subtype, GVSF_MULTIHEADED); }
00305 
00310   FORCEINLINE bool IsFreeWagon() const { return HasBit(this->subtype, GVSF_FREE_WAGON); }
00311 
00316   FORCEINLINE bool IsEngine() const { return HasBit(this->subtype, GVSF_ENGINE); }
00317 
00322   FORCEINLINE bool IsWagon() const { return HasBit(this->subtype, GVSF_WAGON); }
00323 
00328   FORCEINLINE bool IsMultiheaded() const { return HasBit(this->subtype, GVSF_MULTIHEADED); }
00329 
00334   FORCEINLINE bool IsRearDualheaded() const { return this->IsMultiheaded() && !this->IsEngine(); }
00335 
00341   FORCEINLINE void SetLastSpeed()
00342   {
00343     if (this->cur_speed != this->gcache.last_speed) {
00344       SetWindowWidgetDirty(WC_VEHICLE_VIEW, this->index, VVW_WIDGET_START_STOP_VEH);
00345       this->gcache.last_speed = this->cur_speed;
00346     }
00347   }
00348 
00349 protected:
00363   FORCEINLINE uint DoUpdateSpeed(uint accel, int min_speed, int max_speed)
00364   {
00365     uint spd = this->subspeed + accel;
00366     this->subspeed = (byte)spd;
00367 
00368     /* When we are going faster than the maximum speed, reduce the speed
00369      * somewhat gradually. But never lower than the maximum speed. */
00370     int tempmax = max_speed;
00371     if (this->cur_speed > max_speed) {
00372       tempmax = max(this->cur_speed - (this->cur_speed / 10) - 1, max_speed);
00373     }
00374 
00375     /* Enforce a maximum and minimum speed. Normally we would use something like
00376      * Clamp for this, but in this case min_speed might be below the maximum speed
00377      * threshold for some reason. That makes acceleration fail and assertions
00378      * happen in Clamp. So make it explicit that min_speed overrules the maximum
00379      * speed by explicit ordering of min and max. */
00380     this->cur_speed = spd = max(min(this->cur_speed + ((int)spd >> 8), tempmax), min_speed);
00381 
00382     int scaled_spd = this->GetAdvanceSpeed(spd);
00383 
00384     scaled_spd += this->progress;
00385     this->progress = 0; // set later in *Handler or *Controller
00386     return scaled_spd;
00387   }
00388 };
00389 
00390 #endif /* GROUND_VEHICLE_HPP */