# articulated_vehicles.cpp

Go to the documentation of this file.

    00001 /* $Id: articulated_vehicles.cpp 15701 2009-03-13 21:28:40Z frosch $ */
    00002
    00005 #include "stdafx.h"
    00006 #include "train.h"
    00007 #include "roadveh.h"
    00008 #include "aircraft.h"
    00009 #include "newgrf_engine.h"
    00010 #include "vehicle_func.h"
    00011
    00012 #include "table/strings.h"
    00013
    00014 static const uint MAX_ARTICULATED_PARTS = 100;
    00015
    00016 uint CountArticulatedParts(EngineID engine_type, bool purchase_window)
    00017 {
    00018   if (!HasBit(EngInfo(engine_type)->callbackmask, CBM_VEHICLE_ARTIC_ENGINE)) return 0;
    00019
    00020   /* If we can't allocate a vehicle now, we can't allocate it in the command
    00021    * either, so it doesn't matter how many articulated parts there are. */
    00022   if (!Vehicle::CanAllocateItem()) return 0;
    00023
    00024   Vehicle *v = NULL;;
    00025   if (!purchase_window) {
    00026     v = new InvalidVehicle();
    00027     v->engine_type = engine_type;
    00028   }
    00029
    00030   uint i;
    00031   for (i = 1; i < MAX_ARTICULATED_PARTS; i++) {
    00032     uint16 callback = GetVehicleCallback(CBID_VEHICLE_ARTIC_ENGINE, i, 0, engine_type, v);
    00033     if (callback == CALLBACK_FAILED || GB(callback, 0, 8) == 0xFF) break;
    00034   }
    00035
    00036   delete v;
    00037
    00038   return i - 1;
    00039 }
    00040
    00041
    00049 static inline uint16 GetVehicleDefaultCapacity(EngineID engine, VehicleType type, CargoID *cargo_type)
    00050 {
    00051   const Engine *e = GetEngine(engine);
    00052   CargoID cargo = (e->CanCarryCargo() ? e->GetDefaultCargoType() : (CargoID)CT_INVALID);
    00053   if (cargo_type != NULL) *cargo_type = cargo;
    00054   if (cargo == CT_INVALID) return 0;
    00055   switch (type) {
    00056     case VEH_TRAIN:
    00057       return GetEngineProperty(engine, 0x14, e->u.rail.capacity) + (e->u.rail.railveh_type == RAILVEH_MULTIHEAD ? e->u.rail.capacity : 0);
    00058
    00059     case VEH_ROAD:
    00060       return GetEngineProperty(engine, 0x0F, e->u.road.capacity);
    00061
    00062     case VEH_SHIP:
    00063       return GetEngineProperty(engine, 0x0D, e->u.ship.capacity);
    00064
    00065     case VEH_AIRCRAFT:
    00066       return AircraftDefaultCargoCapacity(cargo, &e->u.air);
    00067
    00068     default: NOT_REACHED();
    00069   }
    00070
    00071 }
    00072
    00080 static inline uint32 GetAvailableVehicleCargoTypes(EngineID engine, VehicleType type, bool include_initial_cargo_type)
    00081 {
    00082   uint32 cargos = 0;
    00083   CargoID initial_cargo_type;
    00084
    00085   if (GetVehicleDefaultCapacity(engine, type, &initial_cargo_type) > 0) {
    00086     if (type != VEH_SHIP || ShipVehInfo(engine)->refittable) {
    00087       const EngineInfo *ei = EngInfo(engine);
    00088       cargos = ei->refit_mask;
    00089     }
    00090     if (include_initial_cargo_type && initial_cargo_type < NUM_CARGO) SetBit(cargos, initial_cargo_type);
    00091   }
    00092
    00093   return cargos;
    00094 }
    00095
    00096 uint16 *GetCapacityOfArticulatedParts(EngineID engine, VehicleType type)
    00097 {
    00098   static uint16 capacity[NUM_CARGO];
    00099   memset(capacity, 0, sizeof(capacity));
    00100
    00101   CargoID cargo_type;
    00102   uint16 cargo_capacity = GetVehicleDefaultCapacity(engine, type, &cargo_type);
    00103   if (cargo_type < NUM_CARGO) capacity[cargo_type] = cargo_capacity;
    00104
    00105   if (type != VEH_TRAIN && type != VEH_ROAD) return capacity;
    00106
    00107   if (!HasBit(EngInfo(engine)->callbackmask, CBM_VEHICLE_ARTIC_ENGINE)) return capacity;
    00108
    00109   for (uint i = 1; i < MAX_ARTICULATED_PARTS; i++) {
    00110     uint16 callback = GetVehicleCallback(CBID_VEHICLE_ARTIC_ENGINE, i, 0, engine, NULL);
    00111     if (callback == CALLBACK_FAILED || GB(callback, 0, 8) == 0xFF) break;
    00112
    00113     EngineID artic_engine = GetNewEngineID(GetEngineGRF(engine), type, GB(callback, 0, 7));
    00114
    00115     cargo_capacity = GetVehicleDefaultCapacity(artic_engine, type, &cargo_type);
    00116     if (cargo_type < NUM_CARGO) capacity[cargo_type] += cargo_capacity;
    00117   }
    00118
    00119   return capacity;
    00120 }
    00121
    00127 bool IsArticulatedVehicleRefittable(EngineID engine)
    00128 {
    00129   if (IsEngineRefittable(engine)) return true;
    00130
    00131   const Engine *e = GetEngine(engine);
    00132   if (e->type != VEH_TRAIN && e->type != VEH_ROAD) return false;
    00133
    00134   if (!HasBit(e->info.callbackmask, CBM_VEHICLE_ARTIC_ENGINE)) return false;
    00135
    00136   for (uint i = 1; i < MAX_ARTICULATED_PARTS; i++) {
    00137     uint16 callback = GetVehicleCallback(CBID_VEHICLE_ARTIC_ENGINE, i, 0, engine, NULL);
    00138     if (callback == CALLBACK_FAILED || GB(callback, 0, 8) == 0xFF) break;
    00139
    00140     EngineID artic_engine = GetNewEngineID(GetEngineGRF(engine), e->type, GB(callback, 0, 7));
    00141     if (IsEngineRefittable(artic_engine)) return true;
    00142   }
    00143
    00144   return false;
    00145 }
    00146
    00154 uint32 GetUnionOfArticulatedRefitMasks(EngineID engine, VehicleType type, bool include_initial_cargo_type)
    00155 {
    00156   uint32 cargos = GetAvailableVehicleCargoTypes(engine, type, include_initial_cargo_type);
    00157
    00158   if (type != VEH_TRAIN && type != VEH_ROAD) return cargos;
    00159
    00160   if (!HasBit(EngInfo(engine)->callbackmask, CBM_VEHICLE_ARTIC_ENGINE)) return cargos;
    00161
    00162   for (uint i = 1; i < MAX_ARTICULATED_PARTS; i++) {
    00163     uint16 callback = GetVehicleCallback(CBID_VEHICLE_ARTIC_ENGINE, i, 0, engine, NULL);
    00164     if (callback == CALLBACK_FAILED || GB(callback, 0, 8) == 0xFF) break;
    00165
    00166     EngineID artic_engine = GetNewEngineID(GetEngineGRF(engine), type, GB(callback, 0, 7));
    00167     cargos |= GetAvailableVehicleCargoTypes(artic_engine, type, include_initial_cargo_type);
    00168   }
    00169
    00170   return cargos;
    00171 }
    00172
    00180 uint32 GetIntersectionOfArticulatedRefitMasks(EngineID engine, VehicleType type, bool include_initial_cargo_type)
    00181 {
    00182   uint32 cargos = UINT32_MAX;
    00183
    00184   uint32 veh_cargos = GetAvailableVehicleCargoTypes(engine, type, include_initial_cargo_type);
    00185   if (veh_cargos != 0) cargos &= veh_cargos;
    00186
    00187   if (type != VEH_TRAIN && type != VEH_ROAD) return cargos;
    00188
    00189   if (!HasBit(EngInfo(engine)->callbackmask, CBM_VEHICLE_ARTIC_ENGINE)) return cargos;
    00190
    00191   for (uint i = 1; i < MAX_ARTICULATED_PARTS; i++) {
    00192     uint16 callback = GetVehicleCallback(CBID_VEHICLE_ARTIC_ENGINE, i, 0, engine, NULL);
    00193     if (callback == CALLBACK_FAILED || GB(callback, 0, 8) == 0xFF) break;
    00194
    00195     EngineID artic_engine = GetNewEngineID(GetEngineGRF(engine), type, GB(callback, 0, 7));
    00196     veh_cargos = GetAvailableVehicleCargoTypes(artic_engine, type, include_initial_cargo_type);
    00197     if (veh_cargos != 0) cargos &= veh_cargos;
    00198   }
    00199
    00200   return cargos;
    00201 }
    00202
    00203
    00211 bool IsArticulatedVehicleCarryingDifferentCargos(const Vehicle *v, CargoID *cargo_type)
    00212 {
    00213   CargoID first_cargo = CT_INVALID;
    00214
    00215   do {
    00216     if (v->cargo_cap > 0 && v->cargo_type != CT_INVALID) {
    00217       if (first_cargo == CT_INVALID) first_cargo = v->cargo_type;
    00218       if (first_cargo != v->cargo_type) {
    00219         if (cargo_type != NULL) *cargo_type = CT_INVALID;
    00220         return true;
    00221       }
    00222     }
    00223
    00224     switch (v->type) {
    00225       case VEH_TRAIN:
    00226         v = (EngineHasArticPart(v) ? GetNextArticPart(v) : NULL);
    00227         break;
    00228
    00229       case VEH_ROAD:
    00230         v = (RoadVehHasArticPart(v) ? v->Next() : NULL);
    00231         break;
    00232
    00233       default:
    00234         v = NULL;
    00235         break;
    00236     }
    00237   } while (v != NULL);
    00238
    00239   if (cargo_type != NULL) *cargo_type = first_cargo;
    00240   return false;
    00241 }
    00242
    00251 void CheckConsistencyOfArticulatedVehicle(const Vehicle *v)
    00252 {
    00253   const Engine *engine = GetEngine(v->engine_type);
    00254
    00255   uint32 purchase_refit_union = GetUnionOfArticulatedRefitMasks(v->engine_type, v->type, true);
    00256   uint32 purchase_refit_intersection = GetIntersectionOfArticulatedRefitMasks(v->engine_type, v->type, true);
    00257   uint16 *purchase_default_capacity = GetCapacityOfArticulatedParts(v->engine_type, v->type);
    00258
    00259   uint32 real_refit_union = 0;
    00260   uint32 real_refit_intersection = UINT_MAX;
    00261   uint16 real_default_capacity[NUM_CARGO];
    00262   memset(real_default_capacity, 0, sizeof(real_default_capacity));
    00263
    00264   do {
    00265     uint32 refit_mask = GetAvailableVehicleCargoTypes(v->engine_type, v->type, true);
    00266     real_refit_union |= refit_mask;
    00267     if (refit_mask != 0) real_refit_intersection &= refit_mask;
    00268
    00269     assert(v->cargo_type < NUM_CARGO);
    00270     real_default_capacity[v->cargo_type] += v->cargo_cap;
    00271
    00272     switch (v->type) {
    00273       case VEH_TRAIN:
    00274         v = (EngineHasArticPart(v) ? GetNextArticPart(v) : NULL);
    00275         break;
    00276
    00277       case VEH_ROAD:
    00278         v = (RoadVehHasArticPart(v) ? v->Next() : NULL);
    00279         break;
    00280
    00281       default:
    00282         v = NULL;
    00283         break;
    00284     }
    00285   } while (v != NULL);
    00286
    00287   /* Check whether the vehicle carries more cargos than expected */
    00288   bool carries_more = false;
    00289   for (CargoID cid = 0; cid < NUM_CARGO; cid++) {
    00290     if (real_default_capacity[cid] != 0 && purchase_default_capacity[cid] == 0) {
    00291       carries_more = true;
    00292       break;
    00293     }
    00294   }
    00295
    00296   /* show a warning once for each GRF after each game load */
    00297   if (real_refit_union != purchase_refit_union || real_refit_intersection != purchase_refit_intersection || carries_more) {
    00298     ShowNewGrfVehicleError(engine->index, STR_NEWGRF_BUGGY, STR_NEWGRF_BUGGY_ARTICULATED_CARGO, GBUG_VEH_REFIT, false);
    00299   }
    00300 }
    00301
    00302 void AddArticulatedParts(Vehicle **vl, VehicleType type)
    00303 {
    00304   const Vehicle *v = vl[0];
    00305   Vehicle *u = vl[0];
    00306
    00307   if (!HasBit(EngInfo(v->engine_type)->callbackmask, CBM_VEHICLE_ARTIC_ENGINE)) return;
    00308
    00309   for (uint i = 1; i < MAX_ARTICULATED_PARTS; i++) {
    00310     uint16 callback = GetVehicleCallback(CBID_VEHICLE_ARTIC_ENGINE, i, 0, v->engine_type, v);
    00311     if (callback == CALLBACK_FAILED || GB(callback, 0, 8) == 0xFF) return;
    00312
    00313     /* Attempt to use pre-allocated vehicles until they run out. This can happen
    00314      * if the callback returns different values depending on the cargo type. */
    00315     u->SetNext(vl[i]);
    00316     if (u->Next() == NULL) return;
    00317
    00318     Vehicle *previous = u;
    00319     u = u->Next();
    00320
    00321     EngineID engine_type = GetNewEngineID(GetEngineGRF(v->engine_type), type, GB(callback, 0, 7));
    00322     bool flip_image = HasBit(callback, 7);
    00323
    00324     const Engine *e_artic = GetEngine(engine_type);
    00325     switch (type) {
    00326       default: NOT_REACHED();
    00327
    00328       case VEH_TRAIN:
    00329         u = new (u) Train();
    00330         u->subtype = 0;
    00331         previous->SetNext(u);
    00332         u->u.rail.track = v->u.rail.track;
    00333         u->u.rail.railtype = v->u.rail.railtype;
    00334         u->u.rail.first_engine = v->engine_type;
    00335
    00336         u->spritenum = e_artic->u.rail.image_index;
    00337         if (e_artic->CanCarryCargo()) {
    00338           u->cargo_type = e_artic->GetDefaultCargoType();
    00339           u->cargo_cap = e_artic->u.rail.capacity;  // Callback 36 is called when the consist is finished
    00340         } else {
    00341           u->cargo_type = v->cargo_type; // Needed for livery selection
    00342           u->cargo_cap = 0;
    00343         }
    00344
    00345         SetArticulatedPart(u);
    00346         break;
    00347
    00348       case VEH_ROAD:
    00349         u = new (u) RoadVehicle();
    00350         u->subtype = 0;
    00351         previous->SetNext(u);
    00352         u->u.road.first_engine = v->engine_type;
    00353         u->u.road.cached_veh_length = 8; // Callback is called when the consist is finished
    00354         u->u.road.state = RVSB_IN_DEPOT;
    00355
    00356         u->u.road.roadtype = v->u.road.roadtype;
    00357         u->u.road.compatible_roadtypes = v->u.road.compatible_roadtypes;
    00358
    00359         u->spritenum = e_artic->u.road.image_index;
    00360         if (e_artic->CanCarryCargo()) {
    00361           u->cargo_type = e_artic->GetDefaultCargoType();
    00362           u->cargo_cap = e_artic->u.road.capacity;  // Callback 36 is called when the consist is finished
    00363         } else {
    00364           u->cargo_type = v->cargo_type; // Needed for livery selection
    00365           u->cargo_cap = 0;
    00366         }
    00367
    00368         SetRoadVehArticPart(u);
    00369         break;
    00370     }
    00371
    00372     /* get common values from first engine */
    00373     u->direction = v->direction;
    00374     u->owner = v->owner;
    00375     u->tile = v->tile;
    00376     u->x_pos = v->x_pos;
    00377     u->y_pos = v->y_pos;
    00378     u->z_pos = v->z_pos;
    00379     u->build_year = v->build_year;
    00380     u->vehstatus = v->vehstatus & ~VS_STOPPED;
    00381
    00382     u->cargo_subtype = 0;
    00383     u->max_speed = 0;
    00384     u->max_age = 0;
    00385     u->engine_type = engine_type;
    00386     u->value = 0;
    00387     u->cur_image = 0xAC2;
    00388     u->random_bits = VehicleRandomBits();
    00389
    00390     if (flip_image) u->spritenum++;
    00391
    00392     VehicleMove(u, false);
    00393   }
    00394 }