diff --git a/src/lang/english.txt b/src/lang/english.txt index 5e724223f1..534168789c 100644 --- a/src/lang/english.txt +++ b/src/lang/english.txt @@ -2402,7 +2402,8 @@ STR_NEWGRF_UNPAUSE_WARNING :{WHITE}Unpausin # NewGRF 'it's broken' warnings STR_NEWGRF_BROKEN :{WHITE}Behaviour of NewGRF '{0:RAW_STRING}' is likely to cause desyncs and/or crashes. -STR_NEWGRF_BROKEN_VEHICLE_LENGTH :{WHITE}It changes vehicle length for '{1:ENGINE}' when not inside a depot. +STR_NEWGRF_BROKEN_POWERED_WAGON :{WHITE}It changed powered-wagon state for '{1:ENGINE}' when not inside a depot. +STR_NEWGRF_BROKEN_VEHICLE_LENGTH :{WHITE}It changed vehicle length for '{1:ENGINE}' when not inside a depot. STR_BROKEN_VEHICLE_LENGTH :{WHITE}Train '{VEHICLE}' belonging to '{COMPANY}' has invalid length. It is probably caused by problems with NewGRFs. Game may desync or crash. STR_NEWGRF_BUGGY :{WHITE}NewGRF '{0:RAW_STRING}' provides incorrect information. diff --git a/src/newgrf_config.h b/src/newgrf_config.h index fe3eab37ed..cc4fe72e64 100644 --- a/src/newgrf_config.h +++ b/src/newgrf_config.h @@ -38,8 +38,9 @@ enum GRFStatus { /** Encountered GRF bugs */ enum GRFBugs { - GBUG_VEH_LENGTH, ///< Length of rail vehicle changes when not inside a depot - GBUG_VEH_REFIT, ///< Articulated vehicles carry different cargos resp. are differently refittable than specified in purchase list + GBUG_VEH_LENGTH, ///< Length of rail vehicle changes when not inside a depot + GBUG_VEH_REFIT, ///< Articulated vehicles carry different cargos resp. are differently refittable than specified in purchase list + GBUG_VEH_POWERED_WAGON, ///< Powered wagon changed poweredness state when not inside a depot }; /** Status of post-gameload GRF compatibility check */ diff --git a/src/newgrf_engine.cpp b/src/newgrf_engine.cpp index bcc729e7b1..34abf99fc8 100644 --- a/src/newgrf_engine.cpp +++ b/src/newgrf_engine.cpp @@ -691,9 +691,10 @@ static uint32 VehicleGetVariable(const ResolverObject *object, byte variable, by if (v->type == VEH_TRAIN) { const Train *t = Train::From(v); - const Train *u = t->IsWagon() && HasBit(t->vehicle_flags, VRF_POWEREDWAGON) ? t->First() : t; + bool is_powered_wagon = HasBit(t->flags, VRF_POWEREDWAGON); + const Train *u = is_powered_wagon ? t->First() : t; // for powered wagons the engine defines the type of engine (i.e. railtype) RailType railtype = GetRailType(v->tile); - bool powered = t->IsEngine() || (t->IsWagon() && HasBit(t->vehicle_flags, VRF_POWEREDWAGON)); + bool powered = t->IsEngine() || is_powered_wagon; bool has_power = HasPowerOnRail(u->railtype, railtype); if (powered && has_power) SetBit(modflags, 5); diff --git a/src/rail_cmd.cpp b/src/rail_cmd.cpp index b882e30a54..bee91a1fd3 100644 --- a/src/rail_cmd.cpp +++ b/src/rail_cmd.cpp @@ -1346,7 +1346,6 @@ static Vehicle *UpdateTrainPowerProc(Vehicle *v, void *data) { if (v->type != VEH_TRAIN) return NULL; - /* Similar checks as in Train::PowerChanged() */ TrainList *affected_trains = static_cast(data); affected_trains->Include(Train::From(v)->First()); @@ -1532,8 +1531,7 @@ CommandCost CmdConvertRail(TileIndex tile, DoCommandFlag flags, uint32 p1, uint3 if (flags & DC_EXEC) { /* Railtype changed, update trains as when entering different track */ for (Train **v = affected_trains.Begin(); v != affected_trains.End(); v++) { - (*v)->PowerChanged(); - (*v)->UpdateAcceleration(); + (*v)->RailtypeChanged(); } } diff --git a/src/train.h b/src/train.h index aadb489819..2fa7da17cc 100644 --- a/src/train.h +++ b/src/train.h @@ -161,6 +161,8 @@ struct Train : public SpecializedVehicle { void CargoChanged(); void PowerChanged(); + void RailtypeChanged(); + int UpdateSpeed(); void UpdateAcceleration(); @@ -389,6 +391,8 @@ struct Train : public SpecializedVehicle { protected: // These functions should not be called outside acceleration code. + void UpdateVisualEffect(bool allow_power_change); + /** * Allows to know the power value that this vehicle will use. * @return Power value from the engine in HP, or zero if the vehicle is not powered. @@ -412,7 +416,8 @@ protected: // These functions should not be called outside acceleration code. */ FORCEINLINE uint16 GetPoweredPartPower(const Train *head) const { - if (HasBit(this->flags, VRF_POWEREDWAGON) && HasPowerOnRail(head->railtype, GetRailType(head->tile))) { + /* For powered wagons the engine defines the type of engine (i.e. railtype) */ + if (HasBit(this->flags, VRF_POWEREDWAGON) && HasPowerOnRail(head->railtype, GetRailType(this->tile))) { return RailVehInfo(this->tcache.first_engine)->pow_wag_power; } diff --git a/src/train_cmd.cpp b/src/train_cmd.cpp index da04fbad16..88937591e8 100644 --- a/src/train_cmd.cpp +++ b/src/train_cmd.cpp @@ -196,6 +196,57 @@ void CheckTrainsLengths() } } +/** + * Update visual effect, power and acceleration caches. + * Called when a vehicle in the consist enters a different railtype. + */ +void Train::RailtypeChanged() +{ + for (Train *u = this; u != NULL; u = u->Next()) { + /* The wagon-is-powered-state should not change, so the weight does not change. */ + u->UpdateVisualEffect(false); + } + this->PowerChanged(); + if (this->IsFrontEngine()) this->UpdateAcceleration(); +} + +/** + * Update the cached visual effect. + * @param allow_power_change true if the wagon-is-powered-state may change. + */ +void Train::UpdateVisualEffect(bool allow_power_change) +{ + byte powered_before = this->tcache.cached_vis_effect & 0x80; + + const Engine *e = Engine::Get(this->engine_type); + if (e->u.rail.visual_effect != 0) { + this->tcache.cached_vis_effect = e->u.rail.visual_effect; + } else { + if (this->IsWagon() || this->IsArticulatedPart()) { + /* Wagons and articulated parts have no effect by default */ + this->tcache.cached_vis_effect = 0x40; + } else if (e->u.rail.engclass == 0) { + /* Steam is offset by -4 units */ + this->tcache.cached_vis_effect = 4; + } else { + /* Diesel fumes and sparks come from the centre */ + this->tcache.cached_vis_effect = 8; + } + } + + /* Check powered wagon / visual effect callback */ + if (HasBit(e->info.callback_mask, CBM_TRAIN_WAGON_POWER)) { + uint16 callback = GetVehicleCallback(CBID_TRAIN_WAGON_POWER, 0, 0, this->engine_type, this); + + if (callback != CALLBACK_FAILED) this->tcache.cached_vis_effect = GB(callback, 0, 8); + } + + if (!allow_power_change && powered_before != (this->tcache.cached_vis_effect & 0x80)) { + this->tcache.cached_vis_effect ^= 0x80; + ShowNewGrfVehicleError(this->engine_type, STR_NEWGRF_BROKEN, STR_NEWGRF_BROKEN_POWERED_WAGON, GBUG_VEH_POWERED_WAGON, false); + } +} + /** * Recalculates the cached stuff of a train. Should be called each time a vehicle is added * to/removed from the chain, and when the game is loaded. @@ -252,27 +303,8 @@ void Train::ConsistChanged(bool same_length) /* Reset colour map */ u->colourmap = PAL_NONE; - if (rvi_u->visual_effect != 0) { - u->tcache.cached_vis_effect = rvi_u->visual_effect; - } else { - if (u->IsWagon() || u->IsArticulatedPart()) { - /* Wagons and articulated parts have no effect by default */ - u->tcache.cached_vis_effect = 0x40; - } else if (rvi_u->engclass == 0) { - /* Steam is offset by -4 units */ - u->tcache.cached_vis_effect = 4; - } else { - /* Diesel fumes and sparks come from the centre */ - u->tcache.cached_vis_effect = 8; - } - } - - /* Check powered wagon / visual effect callback */ - if (HasBit(e_u->info.callback_mask, CBM_TRAIN_WAGON_POWER)) { - uint16 callback = GetVehicleCallback(CBID_TRAIN_WAGON_POWER, 0, 0, u->engine_type, u); - - if (callback != CALLBACK_FAILED) u->tcache.cached_vis_effect = GB(callback, 0, 8); - } + /* Update powered-wagon-status and visual effect */ + u->UpdateVisualEffect(true); if (rvi_v->pow_wag_power != 0 && rvi_u->railveh_type == RAILVEH_WAGON && UsesWagonOverride(u) && !HasBit(u->tcache.cached_vis_effect, 7)) { @@ -1669,7 +1701,7 @@ static void ReverseTrainSwapVeh(Train *v, int l, int r) } /* Update train's power incase tiles were different rail type */ - v->PowerChanged(); + v->RailtypeChanged(); } @@ -3450,8 +3482,7 @@ static void TrainController(Train *v, Vehicle *nomove) v->tile = gp.new_tile; if (GetTileRailType(gp.new_tile) != GetTileRailType(gp.old_tile)) { - v->First()->PowerChanged(); - v->First()->UpdateAcceleration(); + v->First()->RailtypeChanged(); } v->track = chosen_track;