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