(svn r12807) [NewGRF_ports] -Fix: New files failed to get added to svn on last merge. NewGRF_ports
authorrichk
Sun, 20 Apr 2008 17:30:24 +0000
branchNewGRF_ports
changeset 10275 5e46b660ca6c
parent 10274 b3c58f3df92b
child 10280 ffc3a406abe7
(svn r12807) [NewGRF_ports] -Fix: New files failed to get added to svn on last merge.
src/animated_tile.cpp
src/animated_tile_func.h
src/effectvehicle.cpp
src/effectvehicle_base.h
src/effectvehicle_func.h
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/animated_tile.cpp	Sun Apr 20 17:30:24 2008 +0000
@@ -0,0 +1,135 @@
+/* $Id: animated_tile.cpp 12800 2008-04-20 08:22:59Z rubidium $ */
+
+/** @file animated_tile.cpp Everything related to animated tiles. */
+
+#include "stdafx.h"
+#include "openttd.h"
+#include "saveload.h"
+#include "landscape.h"
+#include "core/alloc_func.hpp"
+#include "functions.h"
+
+/** The table/list with animated tiles. */
+TileIndex *_animated_tile_list = NULL;
+/** The number of animated tiles in the current state. */
+uint _animated_tile_count = 0;
+/** The number of slots for animated tiles allocated currently. */
+static uint _animated_tile_allocated = 0;
+
+/**
+ * Removes the given tile from the animated tile table.
+ * @param tile the tile to remove
+ */
+void DeleteAnimatedTile(TileIndex tile)
+{
+	for (TileIndex *ti = _animated_tile_list; ti < _animated_tile_list + _animated_tile_count; ti++) {
+		if (tile == *ti) {
+			/* Remove the hole
+			 * The order of the remaining elements must stay the same, otherwise the animation loop
+			 * may miss a tile; that's why we must use memmove instead of just moving the last element.
+			 */
+			memmove(ti, ti + 1, (_animated_tile_list + _animated_tile_count - (ti + 1)) * sizeof(*ti));
+			_animated_tile_count--;
+			MarkTileDirtyByTile(tile);
+			return;
+		}
+	}
+}
+
+/**
+ * Add the given tile to the animated tile table (if it does not exist
+ * on that table yet). Also increases the size of the table if necessary.
+ * @param tile the tile to make animated
+ */
+void AddAnimatedTile(TileIndex tile)
+{
+	MarkTileDirtyByTile(tile);
+
+	for (const TileIndex *ti = _animated_tile_list; ti < _animated_tile_list + _animated_tile_count; ti++) {
+		if (tile == *ti) return;
+	}
+
+	/* Table not large enough, so make it larger */
+	if (_animated_tile_count == _animated_tile_allocated) {
+		_animated_tile_allocated *= 2;
+		_animated_tile_list = ReallocT<TileIndex>(_animated_tile_list, _animated_tile_allocated);
+	}
+
+	_animated_tile_list[_animated_tile_count] = tile;
+	_animated_tile_count++;
+}
+
+/**
+ * Animate all tiles in the animated tile list, i.e.\ call AnimateTile on them.
+ */
+void AnimateAnimatedTiles()
+{
+	const TileIndex *ti = _animated_tile_list;
+	while (ti < _animated_tile_list + _animated_tile_count) {
+		const TileIndex curr = *ti;
+		AnimateTile(curr);
+		/* During the AnimateTile call, DeleteAnimatedTile could have been called,
+		 * deleting an element we've already processed and pushing the rest one
+		 * slot to the left. We can detect this by checking whether the index
+		 * in the current slot has changed - if it has, an element has been deleted,
+		 * and we should process the current slot again instead of going forward.
+		 * NOTE: this will still break if more than one animated tile is being
+		 *       deleted during the same AnimateTile call, but no code seems to
+		 *       be doing this anyway.
+		 */
+		if (*ti == curr) ++ti;
+	}
+}
+
+/**
+ * Initialize all animated tile variables to some known begin point
+ */
+void InitializeAnimatedTiles()
+{
+	_animated_tile_list = ReallocT<TileIndex>(_animated_tile_list, 256);
+	_animated_tile_count = 0;
+	_animated_tile_allocated = 256;
+}
+
+/**
+ * Save the ANIT chunk.
+ */
+static void Save_ANIT()
+{
+	SlSetLength(_animated_tile_count * sizeof(*_animated_tile_list));
+	SlArray(_animated_tile_list, _animated_tile_count, SLE_UINT32);
+}
+
+/**
+ * Load the ANIT chunk; the chunk containing the animated tiles.
+ */
+static void Load_ANIT()
+{
+	/* Before version 80 we did NOT have a variable length animated tile table */
+	if (CheckSavegameVersion(80)) {
+		/* In pre version 6, we has 16bit per tile, now we have 32bit per tile, convert it ;) */
+		SlArray(_animated_tile_list, 256, CheckSavegameVersion(6) ? (SLE_FILE_U16 | SLE_VAR_U32) : SLE_UINT32);
+
+		for (_animated_tile_count = 0; _animated_tile_count < 256; _animated_tile_count++) {
+			if (_animated_tile_list[_animated_tile_count] == 0) break;
+		}
+		return;
+	}
+
+	_animated_tile_count = SlGetFieldLength() / sizeof(*_animated_tile_list);
+
+	/* Determine a nice rounded size for the amount of allocated tiles */
+	_animated_tile_allocated = 256;
+	while (_animated_tile_allocated < _animated_tile_count) _animated_tile_allocated *= 2;
+
+	_animated_tile_list = ReallocT<TileIndex>(_animated_tile_list, _animated_tile_allocated);
+	SlArray(_animated_tile_list, _animated_tile_count, SLE_UINT32);
+}
+
+/**
+ * "Definition" imported by the saveload code to be able to load and save
+ * the animated tile table.
+ */
+extern const ChunkHandler _animated_tile_chunk_handlers[] = {
+	{ 'ANIT', Save_ANIT, Load_ANIT, CH_RIFF | CH_LAST},
+};
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/animated_tile_func.h	Sun Apr 20 17:30:24 2008 +0000
@@ -0,0 +1,15 @@
+/* $Id: animated_tile_func.h 12800 2008-04-20 08:22:59Z rubidium $ */
+
+/** @file animated_tile.h Tile animation! */
+
+#ifndef ANIMATED_TILE_H
+#define ANIMATED_TILE_H
+
+#include "tile_type.h"
+
+void AddAnimatedTile(TileIndex tile);
+void DeleteAnimatedTile(TileIndex tile);
+void AnimateAnimatedTiles();
+void InitializeAnimatedTiles();
+
+#endif /* ANIMATED_TILE_H */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/effectvehicle.cpp	Sun Apr 20 17:30:24 2008 +0000
@@ -0,0 +1,663 @@
+/* $Id: effectvehicle.cpp 12804 2008-04-20 11:12:07Z rubidium $ */
+
+/** @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;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/effectvehicle_base.h	Sun Apr 20 17:30:24 2008 +0000
@@ -0,0 +1,37 @@
+/* $Id: effectvehicle_base.h 12804 2008-04-20 11:12:07Z rubidium $ */
+
+/** @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 */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/effectvehicle_func.h	Sun Apr 20 17:30:24 2008 +0000
@@ -0,0 +1,28 @@
+/* $Id: effectvehicle_func.h 12804 2008-04-20 11:12:07Z rubidium $ */
+
+/** @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 */