changeset 9009:870efbdb988d draft

(svn r12804) -Codechange: move the effect vehicle handling out of vehicle.cpp
author rubidium <rubidium@openttd.org>
date Sun, 20 Apr 2008 11:12:07 +0000
parents bc4efc6bfe0d
children b4fd0c0b6129
files projects/openttd_vs80.vcproj projects/openttd_vs90.vcproj source.list src/aircraft_cmd.cpp src/disaster_cmd.cpp src/effectvehicle.cpp src/effectvehicle_base.h src/effectvehicle_func.h src/industry_cmd.cpp src/landscape.cpp src/oldloader.cpp src/road_cmd.cpp src/roadveh_cmd.cpp src/ship_cmd.cpp src/train_cmd.cpp src/vehicle.cpp src/vehicle_base.h src/vehicle_func.h src/vehicle_type.h src/water_cmd.cpp
diffstat 20 files changed, 793 insertions(+), 664 deletions(-) [+]
line wrap: on
line diff
--- a/projects/openttd_vs80.vcproj
+++ b/projects/openttd_vs80.vcproj
@@ -460,6 +460,10 @@
 				>
 			</File>
 			<File
+				RelativePath=".\..\src\animated_tile.cpp"
+				>
+			</File>
+			<File
 				RelativePath=".\..\src\articulated_vehicles.cpp"
 				>
 			</File>
@@ -544,6 +548,10 @@
 				>
 			</File>
 			<File
+				RelativePath=".\..\src\effectvehicle.cpp"
+				>
+			</File>
+			<File
 				RelativePath=".\..\src\elrail.cpp"
 				>
 			</File>
@@ -812,6 +820,10 @@
 				>
 			</File>
 			<File
+				RelativePath=".\..\src\animated_tile_func.h"
+				>
+			</File>
+			<File
 				RelativePath=".\..\src\articulated_vehicles.h"
 				>
 			</File>
@@ -964,6 +976,14 @@
 				>
 			</File>
 			<File
+				RelativePath=".\..\src\effectvehicle_func.h"
+				>
+			</File>
+			<File
+				RelativePath=".\..\src\effectvehicle_base.h"
+				>
+			</File>
+			<File
 				RelativePath=".\..\src\core\endian_func.hpp"
 				>
 			</File>
--- a/projects/openttd_vs90.vcproj
+++ b/projects/openttd_vs90.vcproj
@@ -457,6 +457,10 @@
 				>
 			</File>
 			<File
+				RelativePath=".\..\src\animated_tile.cpp"
+				>
+			</File>
+			<File
 				RelativePath=".\..\src\articulated_vehicles.cpp"
 				>
 			</File>
@@ -541,6 +545,10 @@
 				>
 			</File>
 			<File
+				RelativePath=".\..\src\effectvehicle.cpp"
+				>
+			</File>
+			<File
 				RelativePath=".\..\src\elrail.cpp"
 				>
 			</File>
@@ -809,6 +817,10 @@
 				>
 			</File>
 			<File
+				RelativePath=".\..\src\animated_tile_func.h"
+				>
+			</File>
+			<File
 				RelativePath=".\..\src\articulated_vehicles.h"
 				>
 			</File>
@@ -961,6 +973,14 @@
 				>
 			</File>
 			<File
+				RelativePath=".\..\src\effectvehicle_func.h"
+				>
+			</File>
+			<File
+				RelativePath=".\..\src\effectvehicle_base.h"
+				>
+			</File>
+			<File
 				RelativePath=".\..\src\core\endian_func.hpp"
 				>
 			</File>
--- a/source.list
+++ b/source.list
@@ -23,6 +23,7 @@
 driver.cpp
 widgets/dropdown.cpp
 economy.cpp
+effectvehicle.cpp
 elrail.cpp
 engine.cpp
 fileio.cpp
@@ -164,6 +165,8 @@
 widgets/dropdown_type.h
 economy_func.h
 economy_type.h
+effectvehicle_func.h
+effectvehicle_base.h
 core/endian_func.hpp
 engine_func.h
 engine_type.h
--- a/src/aircraft_cmd.cpp
+++ b/src/aircraft_cmd.cpp
@@ -36,6 +36,7 @@
 #include "player_func.h"
 #include "settings_type.h"
 #include "order_func.h"
+#include "effectvehicle_func.h"
 
 #include "table/strings.h"
 #include "table/sprites.h"
--- a/src/disaster_cmd.cpp
+++ b/src/disaster_cmd.cpp
@@ -39,6 +39,7 @@
 #include "vehicle_func.h"
 #include "vehicle_base.h"
 #include "sound_func.h"
+#include "effectvehicle_func.h"
 
 #include "table/strings.h"
 #include "table/sprites.h"
new file mode 100644
--- /dev/null
+++ b/src/effectvehicle.cpp
@@ -0,0 +1,663 @@
+/* $Id$ */
+
+/** @file vehicle.cpp */
+
+#include "stdafx.h"
+#include "openttd.h"
+#include "road_map.h"
+#include "roadveh.h"
+#include "ship.h"
+#include "spritecache.h"
+#include "tile_cmd.h"
+#include "landscape.h"
+#include "timetable.h"
+#include "viewport_func.h"
+#include "gfx_func.h"
+#include "news_func.h"
+#include "command_func.h"
+#include "saveload.h"
+#include "player_func.h"
+#include "debug.h"
+#include "vehicle_gui.h"
+#include "rail_type.h"
+#include "train.h"
+#include "aircraft.h"
+#include "industry_map.h"
+#include "station_map.h"
+#include "water_map.h"
+#include "network/network.h"
+#include "yapf/yapf.h"
+#include "newgrf_callbacks.h"
+#include "newgrf_engine.h"
+#include "newgrf_sound.h"
+#include "group.h"
+#include "order_func.h"
+#include "strings_func.h"
+#include "zoom_func.h"
+#include "functions.h"
+#include "date_func.h"
+#include "window_func.h"
+#include "vehicle_func.h"
+#include "signal_func.h"
+#include "sound_func.h"
+#include "variables.h"
+#include "autoreplace_func.h"
+#include "autoreplace_gui.h"
+#include "string_func.h"
+#include "settings_type.h"
+#include "oldpool_func.h"
+#include "depot_map.h"
+#include "animated_tile_func.h"
+#include "effectvehicle_base.h"
+#include "effectvehicle_func.h"
+
+#include "table/sprites.h"
+#include "table/strings.h"
+
+static void ChimneySmokeInit(Vehicle *v)
+{
+	uint32 r = Random();
+	v->cur_image = SPR_CHIMNEY_SMOKE_0 + GB(r, 0, 3);
+	v->progress = GB(r, 16, 3);
+}
+
+static void ChimneySmokeTick(Vehicle *v)
+{
+	if (v->progress > 0) {
+		v->progress--;
+	} else {
+		BeginVehicleMove(v);
+
+		TileIndex tile = TileVirtXY(v->x_pos, v->y_pos);
+		if (!IsTileType(tile, MP_INDUSTRY)) {
+			EndVehicleMove(v);
+			delete v;
+			return;
+		}
+
+		if (v->cur_image != SPR_CHIMNEY_SMOKE_7) {
+			v->cur_image++;
+		} else {
+			v->cur_image = SPR_CHIMNEY_SMOKE_0;
+		}
+		v->progress = 7;
+		VehiclePositionChanged(v);
+		EndVehicleMove(v);
+	}
+}
+
+static void SteamSmokeInit(Vehicle *v)
+{
+	v->cur_image = SPR_STEAM_SMOKE_0;
+	v->progress = 12;
+}
+
+static void SteamSmokeTick(Vehicle *v)
+{
+	bool moved = false;
+
+	BeginVehicleMove(v);
+
+	v->progress++;
+
+	if ((v->progress & 7) == 0) {
+		v->z_pos++;
+		moved = true;
+	}
+
+	if ((v->progress & 0xF) == 4) {
+		if (v->cur_image != SPR_STEAM_SMOKE_4) {
+			v->cur_image++;
+		} else {
+			EndVehicleMove(v);
+			delete v;
+			return;
+		}
+		moved = true;
+	}
+
+	if (moved) {
+		VehiclePositionChanged(v);
+		EndVehicleMove(v);
+	}
+}
+
+static void DieselSmokeInit(Vehicle *v)
+{
+	v->cur_image = SPR_DIESEL_SMOKE_0;
+	v->progress = 0;
+}
+
+static void DieselSmokeTick(Vehicle *v)
+{
+	v->progress++;
+
+	if ((v->progress & 3) == 0) {
+		BeginVehicleMove(v);
+		v->z_pos++;
+		VehiclePositionChanged(v);
+		EndVehicleMove(v);
+	} else if ((v->progress & 7) == 1) {
+		BeginVehicleMove(v);
+		if (v->cur_image != SPR_DIESEL_SMOKE_5) {
+			v->cur_image++;
+			VehiclePositionChanged(v);
+			EndVehicleMove(v);
+		} else {
+			EndVehicleMove(v);
+			delete v;
+		}
+	}
+}
+
+static void ElectricSparkInit(Vehicle *v)
+{
+	v->cur_image = SPR_ELECTRIC_SPARK_0;
+	v->progress = 1;
+}
+
+static void ElectricSparkTick(Vehicle *v)
+{
+	if (v->progress < 2) {
+		v->progress++;
+	} else {
+		v->progress = 0;
+		BeginVehicleMove(v);
+		if (v->cur_image != SPR_ELECTRIC_SPARK_5) {
+			v->cur_image++;
+			VehiclePositionChanged(v);
+			EndVehicleMove(v);
+		} else {
+			EndVehicleMove(v);
+			delete v;
+		}
+	}
+}
+
+static void SmokeInit(Vehicle *v)
+{
+	v->cur_image = SPR_SMOKE_0;
+	v->progress = 12;
+}
+
+static void SmokeTick(Vehicle *v)
+{
+	bool moved = false;
+
+	BeginVehicleMove(v);
+
+	v->progress++;
+
+	if ((v->progress & 3) == 0) {
+		v->z_pos++;
+		moved = true;
+	}
+
+	if ((v->progress & 0xF) == 4) {
+		if (v->cur_image != SPR_SMOKE_4) {
+			v->cur_image++;
+		} else {
+			EndVehicleMove(v);
+			delete v;
+			return;
+		}
+		moved = true;
+	}
+
+	if (moved) {
+		VehiclePositionChanged(v);
+		EndVehicleMove(v);
+	}
+}
+
+static void ExplosionLargeInit(Vehicle *v)
+{
+	v->cur_image = SPR_EXPLOSION_LARGE_0;
+	v->progress = 0;
+}
+
+static void ExplosionLargeTick(Vehicle *v)
+{
+	v->progress++;
+	if ((v->progress & 3) == 0) {
+		BeginVehicleMove(v);
+		if (v->cur_image != SPR_EXPLOSION_LARGE_F) {
+			v->cur_image++;
+			VehiclePositionChanged(v);
+			EndVehicleMove(v);
+		} else {
+			EndVehicleMove(v);
+			delete v;
+		}
+	}
+}
+
+static void BreakdownSmokeInit(Vehicle *v)
+{
+	v->cur_image = SPR_BREAKDOWN_SMOKE_0;
+	v->progress = 0;
+}
+
+static void BreakdownSmokeTick(Vehicle *v)
+{
+	v->progress++;
+	if ((v->progress & 7) == 0) {
+		BeginVehicleMove(v);
+		if (v->cur_image != SPR_BREAKDOWN_SMOKE_3) {
+			v->cur_image++;
+		} else {
+			v->cur_image = SPR_BREAKDOWN_SMOKE_0;
+		}
+		VehiclePositionChanged(v);
+		EndVehicleMove(v);
+	}
+
+	v->u.effect.animation_state--;
+	if (v->u.effect.animation_state == 0) {
+		BeginVehicleMove(v);
+		EndVehicleMove(v);
+		delete v;
+	}
+}
+
+static void ExplosionSmallInit(Vehicle *v)
+{
+	v->cur_image = SPR_EXPLOSION_SMALL_0;
+	v->progress = 0;
+}
+
+static void ExplosionSmallTick(Vehicle *v)
+{
+	v->progress++;
+	if ((v->progress & 3) == 0) {
+		BeginVehicleMove(v);
+		if (v->cur_image != SPR_EXPLOSION_SMALL_B) {
+			v->cur_image++;
+			VehiclePositionChanged(v);
+			EndVehicleMove(v);
+		} else {
+			EndVehicleMove(v);
+			delete v;
+		}
+	}
+}
+
+static void BulldozerInit(Vehicle *v)
+{
+	v->cur_image = SPR_BULLDOZER_NE;
+	v->progress = 0;
+	v->u.effect.animation_state = 0;
+	v->u.effect.animation_substate = 0;
+}
+
+struct BulldozerMovement {
+	byte direction:2;
+	byte image:2;
+	byte duration:3;
+};
+
+static const BulldozerMovement _bulldozer_movement[] = {
+	{ 0, 0, 4 },
+	{ 3, 3, 4 },
+	{ 2, 2, 7 },
+	{ 0, 2, 7 },
+	{ 1, 1, 3 },
+	{ 2, 2, 7 },
+	{ 0, 2, 7 },
+	{ 1, 1, 3 },
+	{ 2, 2, 7 },
+	{ 0, 2, 7 },
+	{ 3, 3, 6 },
+	{ 2, 2, 6 },
+	{ 1, 1, 7 },
+	{ 3, 1, 7 },
+	{ 0, 0, 3 },
+	{ 1, 1, 7 },
+	{ 3, 1, 7 },
+	{ 0, 0, 3 },
+	{ 1, 1, 7 },
+	{ 3, 1, 7 }
+};
+
+static const struct {
+	int8 x;
+	int8 y;
+} _inc_by_dir[] = {
+	{ -1,  0 },
+	{  0,  1 },
+	{  1,  0 },
+	{  0, -1 }
+};
+
+static void BulldozerTick(Vehicle *v)
+{
+	v->progress++;
+	if ((v->progress & 7) == 0) {
+		const BulldozerMovement* b = &_bulldozer_movement[v->u.effect.animation_state];
+
+		BeginVehicleMove(v);
+
+		v->cur_image = SPR_BULLDOZER_NE + b->image;
+
+		v->x_pos += _inc_by_dir[b->direction].x;
+		v->y_pos += _inc_by_dir[b->direction].y;
+
+		v->u.effect.animation_substate++;
+		if (v->u.effect.animation_substate >= b->duration) {
+			v->u.effect.animation_substate = 0;
+			v->u.effect.animation_state++;
+			if (v->u.effect.animation_state == lengthof(_bulldozer_movement)) {
+				EndVehicleMove(v);
+				delete v;
+				return;
+			}
+		}
+		VehiclePositionChanged(v);
+		EndVehicleMove(v);
+	}
+}
+
+static void BubbleInit(Vehicle *v)
+{
+	v->cur_image = SPR_BUBBLE_GENERATE_0;
+	v->spritenum = 0;
+	v->progress = 0;
+}
+
+struct BubbleMovement {
+	int8 x:4;
+	int8 y:4;
+	int8 z:4;
+	byte image:4;
+};
+
+#define MK(x, y, z, i) { x, y, z, i }
+#define ME(i) { i, 4, 0, 0 }
+
+static const BubbleMovement _bubble_float_sw[] = {
+	MK(0, 0, 1, 0),
+	MK(1, 0, 1, 1),
+	MK(0, 0, 1, 0),
+	MK(1, 0, 1, 2),
+	ME(1)
+};
+
+
+static const BubbleMovement _bubble_float_ne[] = {
+	MK( 0, 0, 1, 0),
+	MK(-1, 0, 1, 1),
+	MK( 0, 0, 1, 0),
+	MK(-1, 0, 1, 2),
+	ME(1)
+};
+
+static const BubbleMovement _bubble_float_se[] = {
+	MK(0, 0, 1, 0),
+	MK(0, 1, 1, 1),
+	MK(0, 0, 1, 0),
+	MK(0, 1, 1, 2),
+	ME(1)
+};
+
+static const BubbleMovement _bubble_float_nw[] = {
+	MK(0,  0, 1, 0),
+	MK(0, -1, 1, 1),
+	MK(0,  0, 1, 0),
+	MK(0, -1, 1, 2),
+	ME(1)
+};
+
+static const BubbleMovement _bubble_burst[] = {
+	MK(0, 0, 1, 2),
+	MK(0, 0, 1, 7),
+	MK(0, 0, 1, 8),
+	MK(0, 0, 1, 9),
+	ME(0)
+};
+
+static const BubbleMovement _bubble_absorb[] = {
+	MK(0, 0, 1, 0),
+	MK(0, 0, 1, 1),
+	MK(0, 0, 1, 0),
+	MK(0, 0, 1, 2),
+	MK(0, 0, 1, 0),
+	MK(0, 0, 1, 1),
+	MK(0, 0, 1, 0),
+	MK(0, 0, 1, 2),
+	MK(0, 0, 1, 0),
+	MK(0, 0, 1, 1),
+	MK(0, 0, 1, 0),
+	MK(0, 0, 1, 2),
+	MK(0, 0, 1, 0),
+	MK(0, 0, 1, 1),
+	MK(0, 0, 1, 0),
+	MK(0, 0, 1, 2),
+	MK(0, 0, 1, 0),
+	MK(0, 0, 1, 1),
+	MK(0, 0, 1, 0),
+	MK(0, 0, 1, 2),
+	MK(0, 0, 1, 0),
+	MK(0, 0, 1, 1),
+	MK(0, 0, 1, 0),
+	MK(0, 0, 1, 2),
+	MK(0, 0, 1, 0),
+	MK(0, 0, 1, 1),
+	MK(0, 0, 1, 0),
+	MK(0, 0, 1, 2),
+	MK(0, 0, 1, 0),
+	MK(0, 0, 1, 1),
+	MK(0, 0, 1, 0),
+	MK(0, 0, 1, 2),
+	MK(0, 0, 1, 0),
+	MK(0, 0, 1, 1),
+	MK(0, 0, 1, 0),
+	MK(0, 0, 1, 2),
+	MK(0, 0, 1, 0),
+	MK(0, 0, 1, 1),
+	MK(0, 0, 1, 0),
+	MK(0, 0, 1, 2),
+	MK(0, 0, 1, 0),
+	MK(0, 0, 1, 1),
+	MK(0, 0, 1, 0),
+	MK(0, 0, 1, 2),
+	MK(0, 0, 1, 0),
+	MK(0, 0, 1, 1),
+	MK(0, 0, 1, 0),
+	MK(0, 0, 1, 2),
+	MK(0, 0, 1, 0),
+	MK(0, 0, 1, 1),
+	MK(0, 0, 1, 0),
+	MK(0, 0, 1, 2),
+	MK(0, 0, 1, 0),
+	MK(0, 0, 1, 1),
+	MK(0, 0, 1, 0),
+	MK(0, 0, 1, 2),
+	MK(0, 0, 1, 0),
+	MK(0, 0, 1, 1),
+	MK(0, 0, 1, 0),
+	MK(0, 0, 1, 2),
+	MK(0, 0, 1, 0),
+	MK(0, 0, 1, 1),
+	MK(2, 1, 3, 0),
+	MK(1, 1, 3, 1),
+	MK(2, 1, 3, 0),
+	MK(1, 1, 3, 2),
+	MK(2, 1, 3, 0),
+	MK(1, 1, 3, 1),
+	MK(2, 1, 3, 0),
+	MK(1, 0, 1, 2),
+	MK(0, 0, 1, 0),
+	MK(1, 0, 1, 1),
+	MK(0, 0, 1, 0),
+	MK(1, 0, 1, 2),
+	MK(0, 0, 1, 0),
+	MK(1, 0, 1, 1),
+	MK(0, 0, 1, 0),
+	MK(1, 0, 1, 2),
+	ME(2),
+	MK(0, 0, 0, 0xA),
+	MK(0, 0, 0, 0xB),
+	MK(0, 0, 0, 0xC),
+	MK(0, 0, 0, 0xD),
+	MK(0, 0, 0, 0xE),
+	ME(0)
+};
+#undef ME
+#undef MK
+
+static const BubbleMovement * const _bubble_movement[] = {
+	_bubble_float_sw,
+	_bubble_float_ne,
+	_bubble_float_se,
+	_bubble_float_nw,
+	_bubble_burst,
+	_bubble_absorb,
+};
+
+static void BubbleTick(Vehicle *v)
+{
+	/*
+	 * Warning: those effects can NOT use Random(), and have to use
+	 *  InteractiveRandom(), because somehow someone forgot to save
+	 *  spritenum to the savegame, and so it will cause desyncs in
+	 *  multiplayer!! (that is: in ToyLand)
+	 */
+	uint et;
+
+	v->progress++;
+	if ((v->progress & 3) != 0) return;
+
+	BeginVehicleMove(v);
+
+	if (v->spritenum == 0) {
+		v->cur_image++;
+		if (v->cur_image < SPR_BUBBLE_GENERATE_3) {
+			VehiclePositionChanged(v);
+			EndVehicleMove(v);
+			return;
+		}
+		if (v->u.effect.animation_substate != 0) {
+			v->spritenum = GB(InteractiveRandom(), 0, 2) + 1;
+		} else {
+			v->spritenum = 6;
+		}
+		et = 0;
+	} else {
+		et = v->engine_type + 1;
+	}
+
+	const BubbleMovement *b = &_bubble_movement[v->spritenum - 1][et];
+
+	if (b->y == 4 && b->x == 0) {
+		EndVehicleMove(v);
+		delete v;
+		return;
+	}
+
+	if (b->y == 4 && b->x == 1) {
+		if (v->z_pos > 180 || Chance16I(1, 96, InteractiveRandom())) {
+			v->spritenum = 5;
+			SndPlayVehicleFx(SND_2F_POP, v);
+		}
+		et = 0;
+	}
+
+	if (b->y == 4 && b->x == 2) {
+		TileIndex tile;
+
+		et++;
+		SndPlayVehicleFx(SND_31_EXTRACT, v);
+
+		tile = TileVirtXY(v->x_pos, v->y_pos);
+		if (IsTileType(tile, MP_INDUSTRY) && GetIndustryGfx(tile) == GFX_BUBBLE_CATCHER) AddAnimatedTile(tile);
+	}
+
+	v->engine_type = et;
+	b = &_bubble_movement[v->spritenum - 1][et];
+
+	v->x_pos += b->x;
+	v->y_pos += b->y;
+	v->z_pos += b->z;
+	v->cur_image = SPR_BUBBLE_0 + b->image;
+
+	VehiclePositionChanged(v);
+	EndVehicleMove(v);
+}
+
+
+typedef void EffectInitProc(Vehicle *v);
+typedef void EffectTickProc(Vehicle *v);
+
+static EffectInitProc * const _effect_init_procs[] = {
+	ChimneySmokeInit,
+	SteamSmokeInit,
+	DieselSmokeInit,
+	ElectricSparkInit,
+	SmokeInit,
+	ExplosionLargeInit,
+	BreakdownSmokeInit,
+	ExplosionSmallInit,
+	BulldozerInit,
+	BubbleInit,
+};
+
+static EffectTickProc * const _effect_tick_procs[] = {
+	ChimneySmokeTick,
+	SteamSmokeTick,
+	DieselSmokeTick,
+	ElectricSparkTick,
+	SmokeTick,
+	ExplosionLargeTick,
+	BreakdownSmokeTick,
+	ExplosionSmallTick,
+	BulldozerTick,
+	BubbleTick,
+};
+
+
+Vehicle *CreateEffectVehicle(int x, int y, int z, EffectVehicleType type)
+{
+	Vehicle *v = new EffectVehicle();
+	if (v != NULL) {
+		v->subtype = type;
+		v->x_pos = x;
+		v->y_pos = y;
+		v->z_pos = z;
+		v->tile = 0;
+		v->UpdateDeltaXY(INVALID_DIR);
+		v->vehstatus = VS_UNCLICKABLE;
+
+		_effect_init_procs[type](v);
+
+		VehiclePositionChanged(v);
+		BeginVehicleMove(v);
+		EndVehicleMove(v);
+	}
+	return v;
+}
+
+Vehicle *CreateEffectVehicleAbove(int x, int y, int z, EffectVehicleType type)
+{
+	int safe_x = Clamp(x, 0, MapMaxX() * TILE_SIZE);
+	int safe_y = Clamp(y, 0, MapMaxY() * TILE_SIZE);
+	return CreateEffectVehicle(x, y, GetSlopeZ(safe_x, safe_y) + z, type);
+}
+
+Vehicle *CreateEffectVehicleRel(const Vehicle *v, int x, int y, int z, EffectVehicleType type)
+{
+	return CreateEffectVehicle(v->x_pos + x, v->y_pos + y, v->z_pos + z, type);
+}
+
+void EffectVehicle::Tick()
+{
+	_effect_tick_procs[this->subtype](this);
+}
+
+void EffectVehicle::UpdateDeltaXY(Direction direction)
+{
+	this->x_offs        = 0;
+	this->y_offs        = 0;
+	this->x_extent      = 1;
+	this->y_extent      = 1;
+	this->z_extent      = 1;
+}
new file mode 100644
--- /dev/null
+++ b/src/effectvehicle_base.h
@@ -0,0 +1,37 @@
+/* $Id$ */
+
+/** @file effectvehicle_base.h Base class for all effect vehicles. */
+
+#ifndef EFFECTVEHICLE_BASE_H
+#define EFFECTVEHICLE_BASE_H
+
+#include "vehicle_base.h"
+
+/**
+ * This class 'wraps' Vehicle; you do not actually instantiate this class.
+ * You create a Vehicle using AllocateVehicle, so it is added to the pool
+ * and you reinitialize that to a Train using:
+ *   v = new (v) Train();
+ *
+ * As side-effect the vehicle type is set correctly.
+ *
+ * A special vehicle is one of the following:
+ *  - smoke
+ *  - electric sparks for trains
+ *  - explosions
+ *  - bulldozer (road works)
+ *  - bubbles (industry)
+ */
+struct EffectVehicle : public Vehicle {
+	/** Initializes the Vehicle to a special vehicle */
+	EffectVehicle() { this->type = VEH_EFFECT; }
+
+	/** We want to 'destruct' the right class. */
+	virtual ~EffectVehicle() {}
+
+	const char *GetTypeString() const { return "special vehicle"; }
+	void UpdateDeltaXY(Direction direction);
+	void Tick();
+};
+
+#endif /* EFFECTVEHICLE_BASE_H */
new file mode 100644
--- /dev/null
+++ b/src/effectvehicle_func.h
@@ -0,0 +1,28 @@
+/* $Id$ */
+
+/** @file effectvehicle.h Functions related to effect vehicles. */
+
+#ifndef EFFECTVEHICLE_FUNC_H
+#define EFFECTVEHICLE_FUNC_H
+
+#include "vehicle_type.h"
+
+/** Effect vehicle types */
+enum EffectVehicleType {
+	EV_CHIMNEY_SMOKE   = 0,
+	EV_STEAM_SMOKE     = 1,
+	EV_DIESEL_SMOKE    = 2,
+	EV_ELECTRIC_SPARK  = 3,
+	EV_SMOKE           = 4,
+	EV_EXPLOSION_LARGE = 5,
+	EV_BREAKDOWN_SMOKE = 6,
+	EV_EXPLOSION_SMALL = 7,
+	EV_BULLDOZER       = 8,
+	EV_BUBBLE          = 9
+};
+
+Vehicle *CreateEffectVehicle(int x, int y, int z, EffectVehicleType type);
+Vehicle *CreateEffectVehicleAbove(int x, int y, int z, EffectVehicleType type);
+Vehicle *CreateEffectVehicleRel(const Vehicle *v, int x, int y, int z, EffectVehicleType type);
+
+#endif /* EFFECTVEHICLE_FUNC_H */
--- a/src/industry_cmd.cpp
+++ b/src/industry_cmd.cpp
@@ -40,6 +40,7 @@
 #include "station_base.h"
 #include "oldpool_func.h"
 #include "animated_tile_func.h"
+#include "effectvehicle_func.h"
 
 #include "table/strings.h"
 #include "table/sprites.h"
@@ -621,7 +622,7 @@
 	}
 }
 
-static void CreateIndustryEffectSmoke(TileIndex tile)
+static void CreateChimneySmoke(TileIndex tile)
 {
 	uint x = TileX(tile) * TILE_SIZE;
 	uint y = TileY(tile) * TILE_SIZE;
@@ -658,7 +659,7 @@
 
 	switch (gfx) {
 	case GFX_POWERPLANT_CHIMNEY:
-		CreateIndustryEffectSmoke(tile);
+		CreateChimneySmoke(tile);
 		break;
 
 	case GFX_OILRIG_1:
@@ -685,10 +686,10 @@
 {
 	int dir;
 	Vehicle *v;
-	static const int8 _tileloop_ind_case_161[12] = {
-		11,   0, -4, -14,
-		-4, -10, -4,   1,
-		49,  59, 60,  65,
+	static const int8 _bubble_spawn_location[3][4] = {
+		{ 11,   0, -4, -14 },
+		{ -4, -10, -4,   1 },
+		{ 49,  59, 60,  65 },
 	};
 
 	SndPlayTileFx(SND_2E_EXTRACT_AND_POP, tile);
@@ -696,9 +697,9 @@
 	dir = Random() & 3;
 
 	v = CreateEffectVehicleAbove(
-		TileX(tile) * TILE_SIZE + _tileloop_ind_case_161[dir + 0],
-		TileY(tile) * TILE_SIZE + _tileloop_ind_case_161[dir + 4],
-		_tileloop_ind_case_161[dir + 8],
+		TileX(tile) * TILE_SIZE + _bubble_spawn_location[0][dir],
+		TileY(tile) * TILE_SIZE + _bubble_spawn_location[1][dir],
+		_bubble_spawn_location[2][dir],
 		EV_BUBBLE
 	);
 
--- a/src/landscape.cpp
+++ b/src/landscape.cpp
@@ -24,6 +24,7 @@
 #include "vehicle_func.h"
 #include "settings_type.h"
 #include "water.h"
+#include "effectvehicle_func.h"
 
 #include "table/sprites.h"
 
--- a/src/oldloader.cpp
+++ b/src/oldloader.cpp
@@ -25,6 +25,7 @@
 #include "vehicle_func.h"
 #include "variables.h"
 #include "strings_func.h"
+#include "effectvehicle_base.h"
 
 #include "table/strings.h"
 
--- a/src/road_cmd.cpp
+++ b/src/road_cmd.cpp
@@ -37,6 +37,7 @@
 #include "tunnelbridge.h"
 #include "cheat_func.h"
 #include "functions.h"
+#include "effectvehicle_func.h"
 
 #include "table/sprites.h"
 #include "table/strings.h"
--- a/src/roadveh_cmd.cpp
+++ b/src/roadveh_cmd.cpp
@@ -42,6 +42,7 @@
 #include "order_func.h"
 #include "depot_base.h"
 #include "depot_func.h"
+#include "effectvehicle_func.h"
 
 #include "table/strings.h"
 
--- a/src/ship_cmd.cpp
+++ b/src/ship_cmd.cpp
@@ -40,6 +40,7 @@
 #include "gfx_func.h"
 #include "settings_type.h"
 #include "order_func.h"
+#include "effectvehicle_func.h"
 
 #include "table/strings.h"
 
--- a/src/train_cmd.cpp
+++ b/src/train_cmd.cpp
@@ -49,6 +49,7 @@
 #include "settings_type.h"
 #include "order_func.h"
 #include "newgrf_station.h"
+#include "effectvehicle_func.h"
 
 #include "table/strings.h"
 #include "table/train_cmd.h"
--- a/src/vehicle.cpp
+++ b/src/vehicle.cpp
@@ -48,6 +48,7 @@
 #include "oldpool_func.h"
 #include "depot_map.h"
 #include "animated_tile_func.h"
+#include "effectvehicle_base.h"
 
 #include "table/sprites.h"
 #include "table/strings.h"
@@ -846,605 +847,6 @@
 	}
 }
 
-static void ChimneySmokeInit(Vehicle *v)
-{
-	uint32 r = Random();
-	v->cur_image = SPR_CHIMNEY_SMOKE_0 + GB(r, 0, 3);
-	v->progress = GB(r, 16, 3);
-}
-
-static void ChimneySmokeTick(Vehicle *v)
-{
-	if (v->progress > 0) {
-		v->progress--;
-	} else {
-		BeginVehicleMove(v);
-
-		TileIndex tile = TileVirtXY(v->x_pos, v->y_pos);
-		if (!IsTileType(tile, MP_INDUSTRY)) {
-			EndVehicleMove(v);
-			delete v;
-			return;
-		}
-
-		if (v->cur_image != SPR_CHIMNEY_SMOKE_7) {
-			v->cur_image++;
-		} else {
-			v->cur_image = SPR_CHIMNEY_SMOKE_0;
-		}
-		v->progress = 7;
-		VehiclePositionChanged(v);
-		EndVehicleMove(v);
-	}
-}
-
-static void SteamSmokeInit(Vehicle *v)
-{
-	v->cur_image = SPR_STEAM_SMOKE_0;
-	v->progress = 12;
-}
-
-static void SteamSmokeTick(Vehicle *v)
-{
-	bool moved = false;
-
-	BeginVehicleMove(v);
-
-	v->progress++;
-
-	if ((v->progress & 7) == 0) {
-		v->z_pos++;
-		moved = true;
-	}
-
-	if ((v->progress & 0xF) == 4) {
-		if (v->cur_image != SPR_STEAM_SMOKE_4) {
-			v->cur_image++;
-		} else {
-			EndVehicleMove(v);
-			delete v;
-			return;
-		}
-		moved = true;
-	}
-
-	if (moved) {
-		VehiclePositionChanged(v);
-		EndVehicleMove(v);
-	}
-}
-
-static void DieselSmokeInit(Vehicle *v)
-{
-	v->cur_image = SPR_DIESEL_SMOKE_0;
-	v->progress = 0;
-}
-
-static void DieselSmokeTick(Vehicle *v)
-{
-	v->progress++;
-
-	if ((v->progress & 3) == 0) {
-		BeginVehicleMove(v);
-		v->z_pos++;
-		VehiclePositionChanged(v);
-		EndVehicleMove(v);
-	} else if ((v->progress & 7) == 1) {
-		BeginVehicleMove(v);
-		if (v->cur_image != SPR_DIESEL_SMOKE_5) {
-			v->cur_image++;
-			VehiclePositionChanged(v);
-			EndVehicleMove(v);
-		} else {
-			EndVehicleMove(v);
-			delete v;
-		}
-	}
-}
-
-static void ElectricSparkInit(Vehicle *v)
-{
-	v->cur_image = SPR_ELECTRIC_SPARK_0;
-	v->progress = 1;
-}
-
-static void ElectricSparkTick(Vehicle *v)
-{
-	if (v->progress < 2) {
-		v->progress++;
-	} else {
-		v->progress = 0;
-		BeginVehicleMove(v);
-		if (v->cur_image != SPR_ELECTRIC_SPARK_5) {
-			v->cur_image++;
-			VehiclePositionChanged(v);
-			EndVehicleMove(v);
-		} else {
-			EndVehicleMove(v);
-			delete v;
-		}
-	}
-}
-
-static void SmokeInit(Vehicle *v)
-{
-	v->cur_image = SPR_SMOKE_0;
-	v->progress = 12;
-}
-
-static void SmokeTick(Vehicle *v)
-{
-	bool moved = false;
-
-	BeginVehicleMove(v);
-
-	v->progress++;
-
-	if ((v->progress & 3) == 0) {
-		v->z_pos++;
-		moved = true;
-	}
-
-	if ((v->progress & 0xF) == 4) {
-		if (v->cur_image != SPR_SMOKE_4) {
-			v->cur_image++;
-		} else {
-			EndVehicleMove(v);
-			delete v;
-			return;
-		}
-		moved = true;
-	}
-
-	if (moved) {
-		VehiclePositionChanged(v);
-		EndVehicleMove(v);
-	}
-}
-
-static void ExplosionLargeInit(Vehicle *v)
-{
-	v->cur_image = SPR_EXPLOSION_LARGE_0;
-	v->progress = 0;
-}
-
-static void ExplosionLargeTick(Vehicle *v)
-{
-	v->progress++;
-	if ((v->progress & 3) == 0) {
-		BeginVehicleMove(v);
-		if (v->cur_image != SPR_EXPLOSION_LARGE_F) {
-			v->cur_image++;
-			VehiclePositionChanged(v);
-			EndVehicleMove(v);
-		} else {
-			EndVehicleMove(v);
-			delete v;
-		}
-	}
-}
-
-static void BreakdownSmokeInit(Vehicle *v)
-{
-	v->cur_image = SPR_BREAKDOWN_SMOKE_0;
-	v->progress = 0;
-}
-
-static void BreakdownSmokeTick(Vehicle *v)
-{
-	v->progress++;
-	if ((v->progress & 7) == 0) {
-		BeginVehicleMove(v);
-		if (v->cur_image != SPR_BREAKDOWN_SMOKE_3) {
-			v->cur_image++;
-		} else {
-			v->cur_image = SPR_BREAKDOWN_SMOKE_0;
-		}
-		VehiclePositionChanged(v);
-		EndVehicleMove(v);
-	}
-
-	v->u.effect.animation_state--;
-	if (v->u.effect.animation_state == 0) {
-		BeginVehicleMove(v);
-		EndVehicleMove(v);
-		delete v;
-	}
-}
-
-static void ExplosionSmallInit(Vehicle *v)
-{
-	v->cur_image = SPR_EXPLOSION_SMALL_0;
-	v->progress = 0;
-}
-
-static void ExplosionSmallTick(Vehicle *v)
-{
-	v->progress++;
-	if ((v->progress & 3) == 0) {
-		BeginVehicleMove(v);
-		if (v->cur_image != SPR_EXPLOSION_SMALL_B) {
-			v->cur_image++;
-			VehiclePositionChanged(v);
-			EndVehicleMove(v);
-		} else {
-			EndVehicleMove(v);
-			delete v;
-		}
-	}
-}
-
-static void BulldozerInit(Vehicle *v)
-{
-	v->cur_image = SPR_BULLDOZER_NE;
-	v->progress = 0;
-	v->u.effect.animation_state = 0;
-	v->u.effect.animation_substate = 0;
-}
-
-struct BulldozerMovement {
-	byte direction:2;
-	byte image:2;
-	byte duration:3;
-};
-
-static const BulldozerMovement _bulldozer_movement[] = {
-	{ 0, 0, 4 },
-	{ 3, 3, 4 },
-	{ 2, 2, 7 },
-	{ 0, 2, 7 },
-	{ 1, 1, 3 },
-	{ 2, 2, 7 },
-	{ 0, 2, 7 },
-	{ 1, 1, 3 },
-	{ 2, 2, 7 },
-	{ 0, 2, 7 },
-	{ 3, 3, 6 },
-	{ 2, 2, 6 },
-	{ 1, 1, 7 },
-	{ 3, 1, 7 },
-	{ 0, 0, 3 },
-	{ 1, 1, 7 },
-	{ 3, 1, 7 },
-	{ 0, 0, 3 },
-	{ 1, 1, 7 },
-	{ 3, 1, 7 }
-};
-
-static const struct {
-	int8 x;
-	int8 y;
-} _inc_by_dir[] = {
-	{ -1,  0 },
-	{  0,  1 },
-	{  1,  0 },
-	{  0, -1 }
-};
-
-static void BulldozerTick(Vehicle *v)
-{
-	v->progress++;
-	if ((v->progress & 7) == 0) {
-		const BulldozerMovement* b = &_bulldozer_movement[v->u.effect.animation_state];
-
-		BeginVehicleMove(v);
-
-		v->cur_image = SPR_BULLDOZER_NE + b->image;
-
-		v->x_pos += _inc_by_dir[b->direction].x;
-		v->y_pos += _inc_by_dir[b->direction].y;
-
-		v->u.effect.animation_substate++;
-		if (v->u.effect.animation_substate >= b->duration) {
-			v->u.effect.animation_substate = 0;
-			v->u.effect.animation_state++;
-			if (v->u.effect.animation_state == lengthof(_bulldozer_movement)) {
-				EndVehicleMove(v);
-				delete v;
-				return;
-			}
-		}
-		VehiclePositionChanged(v);
-		EndVehicleMove(v);
-	}
-}
-
-static void BubbleInit(Vehicle *v)
-{
-	v->cur_image = SPR_BUBBLE_GENERATE_0;
-	v->spritenum = 0;
-	v->progress = 0;
-}
-
-struct BubbleMovement {
-	int8 x:4;
-	int8 y:4;
-	int8 z:4;
-	byte image:4;
-};
-
-#define MK(x, y, z, i) { x, y, z, i }
-#define ME(i) { i, 4, 0, 0 }
-
-static const BubbleMovement _bubble_float_sw[] = {
-	MK(0, 0, 1, 0),
-	MK(1, 0, 1, 1),
-	MK(0, 0, 1, 0),
-	MK(1, 0, 1, 2),
-	ME(1)
-};
-
-
-static const BubbleMovement _bubble_float_ne[] = {
-	MK( 0, 0, 1, 0),
-	MK(-1, 0, 1, 1),
-	MK( 0, 0, 1, 0),
-	MK(-1, 0, 1, 2),
-	ME(1)
-};
-
-static const BubbleMovement _bubble_float_se[] = {
-	MK(0, 0, 1, 0),
-	MK(0, 1, 1, 1),
-	MK(0, 0, 1, 0),
-	MK(0, 1, 1, 2),
-	ME(1)
-};
-
-static const BubbleMovement _bubble_float_nw[] = {
-	MK(0,  0, 1, 0),
-	MK(0, -1, 1, 1),
-	MK(0,  0, 1, 0),
-	MK(0, -1, 1, 2),
-	ME(1)
-};
-
-static const BubbleMovement _bubble_burst[] = {
-	MK(0, 0, 1, 2),
-	MK(0, 0, 1, 7),
-	MK(0, 0, 1, 8),
-	MK(0, 0, 1, 9),
-	ME(0)
-};
-
-static const BubbleMovement _bubble_absorb[] = {
-	MK(0, 0, 1, 0),
-	MK(0, 0, 1, 1),
-	MK(0, 0, 1, 0),
-	MK(0, 0, 1, 2),
-	MK(0, 0, 1, 0),
-	MK(0, 0, 1, 1),
-	MK(0, 0, 1, 0),
-	MK(0, 0, 1, 2),
-	MK(0, 0, 1, 0),
-	MK(0, 0, 1, 1),
-	MK(0, 0, 1, 0),
-	MK(0, 0, 1, 2),
-	MK(0, 0, 1, 0),
-	MK(0, 0, 1, 1),
-	MK(0, 0, 1, 0),
-	MK(0, 0, 1, 2),
-	MK(0, 0, 1, 0),
-	MK(0, 0, 1, 1),
-	MK(0, 0, 1, 0),
-	MK(0, 0, 1, 2),
-	MK(0, 0, 1, 0),
-	MK(0, 0, 1, 1),
-	MK(0, 0, 1, 0),
-	MK(0, 0, 1, 2),
-	MK(0, 0, 1, 0),
-	MK(0, 0, 1, 1),
-	MK(0, 0, 1, 0),
-	MK(0, 0, 1, 2),
-	MK(0, 0, 1, 0),
-	MK(0, 0, 1, 1),
-	MK(0, 0, 1, 0),
-	MK(0, 0, 1, 2),
-	MK(0, 0, 1, 0),
-	MK(0, 0, 1, 1),
-	MK(0, 0, 1, 0),
-	MK(0, 0, 1, 2),
-	MK(0, 0, 1, 0),
-	MK(0, 0, 1, 1),
-	MK(0, 0, 1, 0),
-	MK(0, 0, 1, 2),
-	MK(0, 0, 1, 0),
-	MK(0, 0, 1, 1),
-	MK(0, 0, 1, 0),
-	MK(0, 0, 1, 2),
-	MK(0, 0, 1, 0),
-	MK(0, 0, 1, 1),
-	MK(0, 0, 1, 0),
-	MK(0, 0, 1, 2),
-	MK(0, 0, 1, 0),
-	MK(0, 0, 1, 1),
-	MK(0, 0, 1, 0),
-	MK(0, 0, 1, 2),
-	MK(0, 0, 1, 0),
-	MK(0, 0, 1, 1),
-	MK(0, 0, 1, 0),
-	MK(0, 0, 1, 2),
-	MK(0, 0, 1, 0),
-	MK(0, 0, 1, 1),
-	MK(0, 0, 1, 0),
-	MK(0, 0, 1, 2),
-	MK(0, 0, 1, 0),
-	MK(0, 0, 1, 1),
-	MK(2, 1, 3, 0),
-	MK(1, 1, 3, 1),
-	MK(2, 1, 3, 0),
-	MK(1, 1, 3, 2),
-	MK(2, 1, 3, 0),
-	MK(1, 1, 3, 1),
-	MK(2, 1, 3, 0),
-	MK(1, 0, 1, 2),
-	MK(0, 0, 1, 0),
-	MK(1, 0, 1, 1),
-	MK(0, 0, 1, 0),
-	MK(1, 0, 1, 2),
-	MK(0, 0, 1, 0),
-	MK(1, 0, 1, 1),
-	MK(0, 0, 1, 0),
-	MK(1, 0, 1, 2),
-	ME(2),
-	MK(0, 0, 0, 0xA),
-	MK(0, 0, 0, 0xB),
-	MK(0, 0, 0, 0xC),
-	MK(0, 0, 0, 0xD),
-	MK(0, 0, 0, 0xE),
-	ME(0)
-};
-#undef ME
-#undef MK
-
-static const BubbleMovement * const _bubble_movement[] = {
-	_bubble_float_sw,
-	_bubble_float_ne,
-	_bubble_float_se,
-	_bubble_float_nw,
-	_bubble_burst,
-	_bubble_absorb,
-};
-
-static void BubbleTick(Vehicle *v)
-{
-	/*
-	 * Warning: those effects can NOT use Random(), and have to use
-	 *  InteractiveRandom(), because somehow someone forgot to save
-	 *  spritenum to the savegame, and so it will cause desyncs in
-	 *  multiplayer!! (that is: in ToyLand)
-	 */
-	uint et;
-
-	v->progress++;
-	if ((v->progress & 3) != 0) return;
-
-	BeginVehicleMove(v);
-
-	if (v->spritenum == 0) {
-		v->cur_image++;
-		if (v->cur_image < SPR_BUBBLE_GENERATE_3) {
-			VehiclePositionChanged(v);
-			EndVehicleMove(v);
-			return;
-		}
-		if (v->u.effect.animation_substate != 0) {
-			v->spritenum = GB(InteractiveRandom(), 0, 2) + 1;
-		} else {
-			v->spritenum = 6;
-		}
-		et = 0;
-	} else {
-		et = v->engine_type + 1;
-	}
-
-	const BubbleMovement *b = &_bubble_movement[v->spritenum - 1][et];
-
-	if (b->y == 4 && b->x == 0) {
-		EndVehicleMove(v);
-		delete v;
-		return;
-	}
-
-	if (b->y == 4 && b->x == 1) {
-		if (v->z_pos > 180 || Chance16I(1, 96, InteractiveRandom())) {
-			v->spritenum = 5;
-			SndPlayVehicleFx(SND_2F_POP, v);
-		}
-		et = 0;
-	}
-
-	if (b->y == 4 && b->x == 2) {
-		TileIndex tile;
-
-		et++;
-		SndPlayVehicleFx(SND_31_EXTRACT, v);
-
-		tile = TileVirtXY(v->x_pos, v->y_pos);
-		if (IsTileType(tile, MP_INDUSTRY) && GetIndustryGfx(tile) == GFX_BUBBLE_CATCHER) AddAnimatedTile(tile);
-	}
-
-	v->engine_type = et;
-	b = &_bubble_movement[v->spritenum - 1][et];
-
-	v->x_pos += b->x;
-	v->y_pos += b->y;
-	v->z_pos += b->z;
-	v->cur_image = SPR_BUBBLE_0 + b->image;
-
-	VehiclePositionChanged(v);
-	EndVehicleMove(v);
-}
-
-
-typedef void EffectInitProc(Vehicle *v);
-typedef void EffectTickProc(Vehicle *v);
-
-static EffectInitProc * const _effect_init_procs[] = {
-	ChimneySmokeInit,
-	SteamSmokeInit,
-	DieselSmokeInit,
-	ElectricSparkInit,
-	SmokeInit,
-	ExplosionLargeInit,
-	BreakdownSmokeInit,
-	ExplosionSmallInit,
-	BulldozerInit,
-	BubbleInit,
-};
-
-static EffectTickProc * const _effect_tick_procs[] = {
-	ChimneySmokeTick,
-	SteamSmokeTick,
-	DieselSmokeTick,
-	ElectricSparkTick,
-	SmokeTick,
-	ExplosionLargeTick,
-	BreakdownSmokeTick,
-	ExplosionSmallTick,
-	BulldozerTick,
-	BubbleTick,
-};
-
-
-Vehicle *CreateEffectVehicle(int x, int y, int z, EffectVehicleType type)
-{
-	Vehicle *v = new EffectVehicle();
-	if (v != NULL) {
-		v->subtype = type;
-		v->x_pos = x;
-		v->y_pos = y;
-		v->z_pos = z;
-		v->tile = 0;
-		v->UpdateDeltaXY(INVALID_DIR);
-		v->vehstatus = VS_UNCLICKABLE;
-
-		_effect_init_procs[type](v);
-
-		VehiclePositionChanged(v);
-		BeginVehicleMove(v);
-		EndVehicleMove(v);
-	}
-	return v;
-}
-
-Vehicle *CreateEffectVehicleAbove(int x, int y, int z, EffectVehicleType type)
-{
-	int safe_x = Clamp(x, 0, MapMaxX() * TILE_SIZE);
-	int safe_y = Clamp(y, 0, MapMaxY() * TILE_SIZE);
-	return CreateEffectVehicle(x, y, GetSlopeZ(safe_x, safe_y) + z, type);
-}
-
-Vehicle *CreateEffectVehicleRel(const Vehicle *v, int x, int y, int z, EffectVehicleType type)
-{
-	return CreateEffectVehicle(v->x_pos + x, v->y_pos + y, v->z_pos + z, type);
-}
-
-void EffectVehicle::Tick()
-{
-	_effect_tick_procs[this->subtype](this);
-}
-
 Vehicle *CheckClickOnVehicle(const ViewPort *vp, int x, int y)
 {
 	Vehicle *found = NULL, *v;
@@ -3282,15 +2684,6 @@
 	}
 }
 
-void EffectVehicle::UpdateDeltaXY(Direction direction)
-{
-	this->x_offs        = 0;
-	this->y_offs        = 0;
-	this->x_extent      = 1;
-	this->y_extent      = 1;
-	this->z_extent      = 1;
-}
-
 void StopAllVehicles()
 {
 	Vehicle *v;
--- a/src/vehicle_base.h
+++ b/src/vehicle_base.h
@@ -539,33 +539,6 @@
  *   v = new (v) Train();
  *
  * As side-effect the vehicle type is set correctly.
- *
- * A special vehicle is one of the following:
- *  - smoke
- *  - electric sparks for trains
- *  - explosions
- *  - bulldozer (road works)
- *  - bubbles (industry)
- */
-struct EffectVehicle : public Vehicle {
-	/** Initializes the Vehicle to a special vehicle */
-	EffectVehicle() { this->type = VEH_EFFECT; }
-
-	/** We want to 'destruct' the right class. */
-	virtual ~EffectVehicle() {}
-
-	const char *GetTypeString() const { return "special vehicle"; }
-	void UpdateDeltaXY(Direction direction);
-	void Tick();
-};
-
-/**
- * This class 'wraps' Vehicle; you do not actually instantiate this class.
- * You create a Vehicle using AllocateVehicle, so it is added to the pool
- * and you reinitialize that to a Train using:
- *   v = new (v) Train();
- *
- * As side-effect the vehicle type is set correctly.
  */
 struct DisasterVehicle : public Vehicle {
 	/** Initializes the Vehicle to a disaster vehicle */
--- a/src/vehicle_func.h
+++ b/src/vehicle_func.h
@@ -1,6 +1,6 @@
 /* $Id$ */
 
-/** @vehicle.h Functions related to vehicles. */
+/** @file vehicle.h Functions related to vehicles. */
 
 #ifndef VEHICLE_FUNC_H
 #define VEHICLE_FUNC_H
@@ -171,13 +171,9 @@
 bool EnsureNoVehicleOnGround(TileIndex tile);
 void StopAllVehicles();
 
-Vehicle *CreateEffectVehicle(int x, int y, int z, EffectVehicleType type);
-Vehicle *CreateEffectVehicleAbove(int x, int y, int z, EffectVehicleType type);
-Vehicle *CreateEffectVehicleRel(const Vehicle *v, int x, int y, int z, EffectVehicleType type);
-
 extern VehicleID _vehicle_id_ctr_day;
 extern Vehicle *_place_clicked_vehicle;
 extern VehicleID _new_vehicle_id;
 extern uint16 _returned_refit_capacity;
 
-#endif /* VEHICLE_H */
+#endif /* VEHICLE_FUNC_H */
--- a/src/vehicle_type.h
+++ b/src/vehicle_type.h
@@ -38,20 +38,6 @@
 
 static const VehicleID INVALID_VEHICLE = 0xFFFF;
 
-/* Effect vehicle types */
-enum EffectVehicleType {
-	EV_CHIMNEY_SMOKE   = 0,
-	EV_STEAM_SMOKE     = 1,
-	EV_DIESEL_SMOKE    = 2,
-	EV_ELECTRIC_SPARK  = 3,
-	EV_SMOKE           = 4,
-	EV_EXPLOSION_LARGE = 5,
-	EV_BREAKDOWN_SMOKE = 6,
-	EV_EXPLOSION_SMALL = 7,
-	EV_BULLDOZER       = 8,
-	EV_BUBBLE          = 9
-};
-
 /** Pathfinding option states */
 enum {
 	VPF_OPF  = 0, ///< The Original PathFinder
--- a/src/water_cmd.cpp
+++ b/src/water_cmd.cpp
@@ -39,6 +39,7 @@
 #include "station_base.h"
 #include "airport.h"
 #include "newgrf_cargo.h"
+#include "effectvehicle_func.h"
 
 #include "table/sprites.h"
 #include "table/strings.h"