# 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
    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   inline 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   inline void UpdateZPositionAndInclination()
    00137   {
    00138     this->z_pos = GetSlopePixelZ(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 = GetSlopePixelZ((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
    00161   inline void UpdateZPosition()
    00162   {
    00163 #if 0
    00164     /* The following code does this: */
    00165
    00166     if (HasBit(this->gv_flags, GVF_GOINGUP_BIT)) {
    00167       switch (this->direction) {
    00168         case DIR_NE:
    00169           this->z_pos += (this->x_pos & 1); break;
    00170         case DIR_SW:
    00171           this->z_pos += (this->x_pos & 1) ^ 1; break;
    00172         case DIR_NW:
    00173           this->z_pos += (this->y_pos & 1); break;
    00174         case DIR_SE:
    00175           this->z_pos += (this->y_pos & 1) ^ 1; break;
    00176         default: break;
    00177       }
    00178     } else if (HasBit(this->gv_flags, GVF_GOINGDOWN_BIT)) {
    00179       switch (this->direction) {
    00180         case DIR_NE:
    00181           this->z_pos -= (this->x_pos & 1); break;
    00182         case DIR_SW:
    00183           this->z_pos -= (this->x_pos & 1) ^ 1; break;
    00184         case DIR_NW:
    00185           this->z_pos -= (this->y_pos & 1); break;
    00186         case DIR_SE:
    00187           this->z_pos -= (this->y_pos & 1) ^ 1; break;
    00188         default: break;
    00189       }
    00190     }
    00191
    00192     /* But gcc 4.4.5 isn't able to nicely optimise it, and the resulting
    00193      * code is full of conditional jumps. */
    00194 #endif
    00195
    00196     /* Vehicle's Z position can change only if it has GVF_GOINGUP_BIT or GVF_GOINGDOWN_BIT set.
    00197      * Furthermore, if this function is called once every time the vehicle's position changes,
    00198      * we know the Z position changes by +/-1 at certain moments - when x_pos, y_pos is odd/even,
    00199      * depending on orientation of the slope and vehicle's direction */
    00200
    00201     if (HasBit(this->gv_flags, GVF_GOINGUP_BIT) || HasBit(this->gv_flags, GVF_GOINGDOWN_BIT)) {
    00202       if (T::From(this)->HasToUseGetSlopePixelZ()) {
    00203         /* In some cases, we have to use GetSlopePixelZ() */
    00204         this->z_pos = GetSlopePixelZ(this->x_pos, this->y_pos);
    00205         return;
    00206       }
    00207       /* DirToDiagDir() is a simple right shift */
    00208       DiagDirection dir = DirToDiagDir(this->direction);
    00209       /* Read variables, so the compiler knows the access doesn't trap */
    00210       int8 x_pos = this->x_pos;
    00211       int8 y_pos = this->y_pos;
    00212       /* DiagDirToAxis() is a simple mask */
    00213       int8 d = DiagDirToAxis(dir) == AXIS_X ? x_pos : y_pos;
    00214       /* We need only the least significant bit */
    00215       d &= 1;
    00216       /* Conditional "^ 1". Optimised to "(dir - 1) <= 1". */
    00217       d ^= (int8)(dir == DIAGDIR_SW || dir == DIAGDIR_SE);
    00218       /* Subtraction instead of addition because we are testing for GVF_GOINGUP_BIT.
    00219        * GVF_GOINGUP_BIT is used because it's bit 0, so simple AND can be used,
    00220        * without any shift */
    00221       this->z_pos += HasBit(this->gv_flags, GVF_GOINGUP_BIT) ? d : -d;
    00222     }
    00223
    00224     assert(this->z_pos == GetSlopePixelZ(this->x_pos, this->y_pos));
    00225   }
    00226
    00233   inline byte UpdateInclination(bool new_tile, bool update_delta)
    00234   {
    00235     byte old_z = this->z_pos;
    00236
    00237     if (new_tile) {
    00238       this->UpdateZPositionAndInclination();
    00239     } else {
    00240       this->UpdateZPosition();
    00241     }
    00242
    00243     this->UpdateViewport(true, update_delta);
    00244     return old_z;
    00245   }
    00246
    00250   inline void SetFrontEngine() { SetBit(this->subtype, GVSF_FRONT); }
    00251
    00255   inline void ClearFrontEngine() { ClrBit(this->subtype, GVSF_FRONT); }
    00256
    00260   inline void SetArticulatedPart() { SetBit(this->subtype, GVSF_ARTICULATED_PART); }
    00261
    00265   inline void ClearArticulatedPart() { ClrBit(this->subtype, GVSF_ARTICULATED_PART); }
    00266
    00270   inline void SetWagon() { SetBit(this->subtype, GVSF_WAGON); }
    00271
    00275   inline void ClearWagon() { ClrBit(this->subtype, GVSF_WAGON); }
    00276
    00280   inline void SetEngine() { SetBit(this->subtype, GVSF_ENGINE); }
    00281
    00285   inline void ClearEngine() { ClrBit(this->subtype, GVSF_ENGINE); }
    00286
    00290   inline void SetFreeWagon() { SetBit(this->subtype, GVSF_FREE_WAGON); }
    00291
    00295   inline void ClearFreeWagon() { ClrBit(this->subtype, GVSF_FREE_WAGON); }
    00296
    00300   inline void SetMultiheaded() { SetBit(this->subtype, GVSF_MULTIHEADED); }
    00301
    00305   inline void ClearMultiheaded() { ClrBit(this->subtype, GVSF_MULTIHEADED); }
    00306
    00311   inline bool IsFreeWagon() const { return HasBit(this->subtype, GVSF_FREE_WAGON); }
    00312
    00317   inline bool IsEngine() const { return HasBit(this->subtype, GVSF_ENGINE); }
    00318
    00323   inline bool IsWagon() const { return HasBit(this->subtype, GVSF_WAGON); }
    00324
    00329   inline bool IsMultiheaded() const { return HasBit(this->subtype, GVSF_MULTIHEADED); }
    00330
    00335   inline bool IsRearDualheaded() const { return this->IsMultiheaded() && !this->IsEngine(); }
    00336
    00342   inline void SetLastSpeed()
    00343   {
    00344     if (this->cur_speed != this->gcache.last_speed) {
    00345       SetWindowWidgetDirty(WC_VEHICLE_VIEW, this->index, WID_VV_START_STOP);
    00346       this->gcache.last_speed = this->cur_speed;
    00347     }
    00348   }
    00349
    00350 protected:
    00364   inline uint DoUpdateSpeed(uint accel, int min_speed, int max_speed)
    00365   {
    00366     uint spd = this->subspeed + accel;
    00367     this->subspeed = (byte)spd;
    00368
    00369     /* When we are going faster than the maximum speed, reduce the speed
    00370      * somewhat gradually. But never lower than the maximum speed. */
    00371     int tempmax = max_speed;
    00372     if (this->cur_speed > max_speed) {
    00373       tempmax = max(this->cur_speed - (this->cur_speed / 10) - 1, max_speed);
    00374     }
    00375
    00376     /* Enforce a maximum and minimum speed. Normally we would use something like
    00377      * Clamp for this, but in this case min_speed might be below the maximum speed
    00378      * threshold for some reason. That makes acceleration fail and assertions
    00379      * happen in Clamp. So make it explicit that min_speed overrules the maximum
    00380      * speed by explicit ordering of min and max. */
    00381     this->cur_speed = spd = max(min(this->cur_speed + ((int)spd >> 8), tempmax), min_speed);
    00382
    00383     int scaled_spd = this->GetAdvanceSpeed(spd);
    00384
    00385     scaled_spd += this->progress;
    00386     this->progress = 0; // set later in *Handler or *Controller
    00387     return scaled_spd;
    00388   }
    00389 };
    00390
    00391 #endif /* GROUND_VEHICLE_HPP */