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

Generated on Thu Apr 14 00:48:13 2011 for OpenTTD by  doxygen 1.6.1