diff --git a/src/train.h b/src/train.h
index 590af67e30..57d040b187 100644
--- a/src/train.h
+++ b/src/train.h
@@ -365,8 +365,6 @@ struct Train : public GroundVehicle<Train, VEH_TRAIN> {
 
 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.
diff --git a/src/train_cmd.cpp b/src/train_cmd.cpp
index aeeb3c4ebc..a1fd9b8a04 100644
--- a/src/train_cmd.cpp
+++ b/src/train_cmd.cpp
@@ -138,50 +138,6 @@ void Train::RailtypeChanged()
 	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)
-{
-	bool powered_before = HasBit(this->vcache.cached_vis_effect, VE_DISABLE_WAGON_POWER);
-	this->vcache.cached_vis_effect = 0;
-
-	const Engine *e = Engine::Get(this->engine_type);
-	if (this->type == VEH_TRAIN) {
-		if (e->u.rail.visual_effect != 0) {
-			this->vcache.cached_vis_effect = e->u.rail.visual_effect;
-		} else {
-			Train *t = Train::From(this);
-			if (t->IsWagon() || t->IsArticulatedPart()) {
-				/* Wagons and articulated parts have no effect by default */
-				SetBit(this->vcache.cached_vis_effect, VE_DISABLE_EFFECT);
-			} else if (e->u.rail.engclass == 0) {
-				/* Steam is offset by -4 units */
-				SB(this->vcache.cached_vis_effect, VE_OFFSET_START, VE_OFFSET_COUNT, VE_OFFSET_CENTRE - 4);
-			} else {
-				/* Diesel fumes and sparks come from the centre */
-				SB(this->vcache.cached_vis_effect, VE_OFFSET_START, VE_OFFSET_COUNT, VE_OFFSET_CENTRE);
-			}
-		}
-	} else {
-		/* Non-trains do not have a visual effect by default. */
-		SetBit(this->vcache.cached_vis_effect, VE_DISABLE_EFFECT);
-	}
-
-	/* Check powered wagon / visual effect callback */
-	if (HasBit(e->info.callback_mask, CBM_VEHICLE_VISUAL_EFFECT)) {
-		uint16 callback = GetVehicleCallback(CBID_VEHICLE_VISUAL_EFFECT, 0, 0, this->engine_type, this);
-
-		if (callback != CALLBACK_FAILED) this->vcache.cached_vis_effect = GB(callback, 0, 8);
-	}
-
-	if (!allow_power_change && powered_before != HasBit(this->vcache.cached_vis_effect, VE_DISABLE_WAGON_POWER)) {
-		ToggleBit(this->vcache.cached_vis_effect, VE_DISABLE_WAGON_POWER);
-		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.
diff --git a/src/vehicle.cpp b/src/vehicle.cpp
index 75ef97ecd6..75ef370f5b 100644
--- a/src/vehicle.cpp
+++ b/src/vehicle.cpp
@@ -1855,6 +1855,47 @@ CommandCost Vehicle::SendToDepot(DoCommandFlag flags, DepotCommand command)
 
 }
 
+void Vehicle::UpdateVisualEffect(bool allow_power_change)
+{
+	bool powered_before = HasBit(this->vcache.cached_vis_effect, VE_DISABLE_WAGON_POWER);
+	this->vcache.cached_vis_effect = 0;
+
+	const Engine *e = Engine::Get(this->engine_type);
+	if (this->type == VEH_TRAIN) {
+		if (e->u.rail.visual_effect != 0) {
+			this->vcache.cached_vis_effect = e->u.rail.visual_effect;
+		} else {
+			Train *t = Train::From(this);
+			if (t->IsWagon() || t->IsArticulatedPart()) {
+				/* Wagons and articulated parts have no effect by default */
+				SetBit(this->vcache.cached_vis_effect, VE_DISABLE_EFFECT);
+			} else if (e->u.rail.engclass == 0) {
+				/* Steam is offset by -4 units */
+				SB(this->vcache.cached_vis_effect, VE_OFFSET_START, VE_OFFSET_COUNT, VE_OFFSET_CENTRE - 4);
+			} else {
+				/* Diesel fumes and sparks come from the centre */
+				SB(this->vcache.cached_vis_effect, VE_OFFSET_START, VE_OFFSET_COUNT, VE_OFFSET_CENTRE);
+			}
+		}
+	} else {
+		/* Non-trains do not have a visual effect by default. */
+		SetBit(this->vcache.cached_vis_effect, VE_DISABLE_EFFECT);
+	}
+
+	/* Check powered wagon / visual effect callback */
+	if (HasBit(e->info.callback_mask, CBM_VEHICLE_VISUAL_EFFECT)) {
+		uint16 callback = GetVehicleCallback(CBID_VEHICLE_VISUAL_EFFECT, 0, 0, this->engine_type, this);
+
+		if (callback != CALLBACK_FAILED) this->vcache.cached_vis_effect = GB(callback, 0, 8);
+	}
+
+	if (!allow_power_change && powered_before != HasBit(this->vcache.cached_vis_effect, VE_DISABLE_WAGON_POWER)) {
+		ToggleBit(this->vcache.cached_vis_effect, VE_DISABLE_WAGON_POWER);
+		ShowNewGrfVehicleError(this->engine_type, STR_NEWGRF_BROKEN, STR_NEWGRF_BROKEN_POWERED_WAGON, GBUG_VEH_POWERED_WAGON, false);
+	}
+}
+
+
 void Vehicle::SetNext(Vehicle *next)
 {
 	assert(this != next);
diff --git a/src/vehicle_base.h b/src/vehicle_base.h
index 55e81b2f03..7db794c1e7 100644
--- a/src/vehicle_base.h
+++ b/src/vehicle_base.h
@@ -612,6 +612,12 @@ public:
 	 */
 	CommandCost SendToDepot(DoCommandFlag flags, DepotCommand command);
 
+	/**
+	 * Update the cached visual effect.
+	 * @param allow_power_change true if the wagon-is-powered-state may change.
+	 */
+	void UpdateVisualEffect(bool allow_power_change = true);
+
 	/**
 	 * Increments cur_order_index, keeps care of the wrap-around and invalidates the GUI.
 	 * Note: current_order is not invalidated.