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