(svn r1721) -Feature: It is now possible to build multiple road stations (up to 8) on
authorcelestar
Sat, 29 Jan 2005 19:41:44 +0000
changeset 1217 ab9f02a224ab
parent 1216 ab4d046ee9d7
child 1218 353a7773bc3c
(svn r1721) -Feature: It is now possible to build multiple road stations (up to 8) on
a single station.
Thanks to: Truelight for the saveload code, Darkvater and Hackykid for
network testing and Tron for proof-reading 1500 lines of diff.
ai.c
ai_build.c
command.c
command.h
functions.h
lang/english.txt
misc.c
oldloader.c
order_cmd.c
road_gui.c
roadveh_cmd.c
saveload.c
saveload.h
station.h
station_cmd.c
ttd.c
vehicle.c
vehicle.h
--- a/ai.c	Sat Jan 29 18:22:07 2005 +0000
+++ b/ai.c	Sat Jan 29 19:41:44 2005 +0000
@@ -2567,10 +2567,10 @@
 		} else if (p->mode == 1) {
 			if (_want_road_truck_station) {
 				// Truck station
-				r = DoCommandByTile(c, p->attr, 0, flag | DC_AUTO | DC_NO_WATER | DC_AI_BUILDING, CMD_BUILD_TRUCK_STATION);
+				r = DoCommandByTile(c, p->attr, RS_TRUCK, flag | DC_AUTO | DC_NO_WATER | DC_AI_BUILDING, CMD_BUILD_ROAD_STOP);
 			} else {
 				// Bus station
-				r = DoCommandByTile(c, p->attr, 0, flag | DC_AUTO | DC_NO_WATER | DC_AI_BUILDING, CMD_BUILD_BUS_STATION);
+				r = DoCommandByTile(c, p->attr, RS_BUS, flag | DC_AUTO | DC_NO_WATER | DC_AI_BUILDING, CMD_BUILD_ROAD_STOP);
 			}
 clear_town_stuff:;
 
@@ -3627,8 +3627,8 @@
 	used=in_use;
 	FOR_ALL_STATIONS(st) {
 		if (st->xy != 0 && st->owner == _current_player && !*used &&
-				((tile = st->bus_tile) != 0 ||
-					(tile = st->lorry_tile) != 0 ||
+				( (st->bus_stops != NULL && (tile = st->bus_stops->xy) != 0) ||
+					(st->truck_stops != NULL && (tile = st->truck_stops->xy)) != 0 ||
 					(tile = st->train_tile) != 0 ||
 					(tile = st->dock_tile) != 0 ||
 					(tile = st->airport_tile) != 0)) {
--- a/ai_build.c	Sat Jan 29 18:22:07 2005 +0000
+++ b/ai_build.c	Sat Jan 29 19:41:44 2005 +0000
@@ -5,6 +5,7 @@
 #include "command.h"
 #include "ai.h"
 #include "engine.h"
+#include "station.h"
 
 // Build HQ
 //  Params:
@@ -28,9 +29,9 @@
 	if (type == AI_TRAIN)
 		return DoCommandByTile(tile, direction + (numtracks << 8) + (length << 16), 0, flag | DC_AUTO | DC_NO_WATER, CMD_BUILD_RAILROAD_STATION);
 	else if (type == AI_BUS)
-		return DoCommandByTile(tile, direction, 0, flag | DC_AUTO | DC_NO_WATER, CMD_BUILD_BUS_STATION);
+		return DoCommandByTile(tile, direction, RS_BUS, flag | DC_AUTO | DC_NO_WATER, CMD_BUILD_ROAD_STOP);
 	else
-		return DoCommandByTile(tile, direction, 0, flag | DC_AUTO | DC_NO_WATER, CMD_BUILD_TRUCK_STATION);
+		return DoCommandByTile(tile, direction, RS_TRUCK, flag | DC_AUTO | DC_NO_WATER, CMD_BUILD_ROAD_STOP);
 }
 
 // Builds a brdige. The second best out of the ones available for this player
--- a/command.c	Sat Jan 29 18:22:07 2005 +0000
+++ b/command.c	Sat Jan 29 19:41:44 2005 +0000
@@ -37,9 +37,7 @@
 DEF_COMMAND(CmdRenameWaypoint);
 DEF_COMMAND(CmdRemoveTrainWaypoint);
 
-DEF_COMMAND(CmdBuildTruckStation);
-
-DEF_COMMAND(CmdBuildBusStation);
+DEF_COMMAND(CmdBuildRoadStop);
 
 DEF_COMMAND(CmdBuildLongRoad);
 DEF_COMMAND(CmdRemoveLongRoad);
@@ -190,9 +188,9 @@
 	CmdBuildTrainWaypoint,				/* 16 */
 	CmdRenameWaypoint,						/* 17 */
 	CmdRemoveTrainWaypoint,				/* 18 */
-	CmdBuildTruckStation,					/* 19 */
+	NULL,                         /* 19 */
 	NULL,													/* 20 */
-	CmdBuildBusStation,						/* 21 */
+	CmdBuildRoadStop,							/* 21 */
 	NULL,													/* 22 */
 	CmdBuildLongRoad,							/* 23 */
 	CmdRemoveLongRoad,						/* 24 */
--- a/command.h	Sat Jan 29 18:22:07 2005 +0000
+++ b/command.h	Sat Jan 29 19:41:44 2005 +0000
@@ -24,8 +24,7 @@
 	CMD_RENAME_WAYPOINT = 17,
 	CMD_REMOVE_TRAIN_WAYPOINT = 18,
 
-	CMD_BUILD_TRUCK_STATION = 19,
-	CMD_BUILD_BUS_STATION = 21,
+	CMD_BUILD_ROAD_STOP = 21,
 	CMD_BUILD_LONG_ROAD = 23,
 	CMD_REMOVE_LONG_ROAD = 24,
 	CMD_BUILD_ROAD = 25,
--- a/functions.h	Sat Jan 29 18:22:07 2005 +0000
+++ b/functions.h	Sat Jan 29 19:41:44 2005 +0000
@@ -290,4 +290,5 @@
 void DeterminePaths(void);
 char * CDECL str_fmt(const char *str, ...);
 
+void bubblesort(void *base, size_t nmemb, size_t size, int(*compar)(const void *, const void *));
 #endif /* FUNCTIONS_H */
--- a/lang/english.txt	Sat Jan 29 18:22:07 2005 +0000
+++ b/lang/english.txt	Sat Jan 29 19:41:44 2005 +0000
@@ -1616,6 +1616,8 @@
 STR_3006_ADJOINS_MORE_THAN_ONE_EXISTING				:{WHITE}Adjoins more than one existing station/loading area
 STR_3007_TOO_MANY_STATIONS_LOADING				:{WHITE}Too many stations/loading areas in this town
 STR_3008_TOO_MANY_STATIONS_LOADING				:{WHITE}Too many stations/loading areas
+STR_3008A_TOO_MANY_BUS_STOPS								:{WHITE}Too many bus stops
+STR_3008B_TOO_MANY_TRUCK_STOPS							:{WHITE}Too many lorry stations
 STR_3009_TOO_CLOSE_TO_ANOTHER_STATION				:{WHITE}Too close to another station/loading area
 STR_300A_0							:{WHITE}{STATION} {STRINL 0x30D1}
 STR_300B_MUST_DEMOLISH_RAILROAD					:{WHITE}Must demolish railway station first
--- a/misc.c	Sat Jan 29 18:22:07 2005 +0000
+++ b/misc.c	Sat Jan 29 19:41:44 2005 +0000
@@ -743,6 +743,35 @@
 	return i;
 }
 
+//!We're writing an own sort algorithm here, as
+//!qsort isn't stable
+//!Since the number of elements will be low, a
+//!simple bubble sort will have to do :)
+
+void bubblesort(void *base, size_t nmemb, size_t size, int(*compar)(const void *, const void *))
+{
+	uint i,k;
+	void *buffer = malloc(size);
+	char *start = base;
+
+	nmemb--;
+
+	for (i = 0; i < nmemb; i++) {
+		for (k = 0; k < nmemb; k++) {
+			void *a, *b;
+			a = start + size * k;
+			b = start + size * (k + 1);
+			if (compar(a, b) > 0) {
+				memcpy(buffer, a, size);
+				memcpy(a, b, size);
+				memcpy(b, buffer, size);
+			}
+		}
+	}
+
+	free(buffer);
+	buffer = NULL;
+}
 
 static void Save_NAME(void)
 {
--- a/oldloader.c	Sat Jan 29 18:22:07 2005 +0000
+++ b/oldloader.c	Sat Jan 29 19:41:44 2005 +0000
@@ -711,8 +711,16 @@
 
 		s->xy = o->xy;
 		s->town = REMAP_TOWN_PTR(o->town);
-		s->bus_tile = o->bus_tile;
-		s->lorry_tile = o->lorry_tile;
+		if (o->bus_tile != 0) {
+			s->bus_stops = GetFirstFreeRoadStop();
+			s->bus_stops->xy = o->bus_tile;
+		} else
+			s->bus_stops = NULL;
+		if (o->lorry_tile != 0) {
+			s->truck_stops = GetFirstFreeRoadStop();
+			s->truck_stops->xy = o->lorry_tile;
+		} else
+			s->truck_stops = 0;
 		s->train_tile = o->train_tile;
 		s->airport_tile = o->airport_tile;
 		s->dock_tile = o->dock_tile;
@@ -734,8 +742,10 @@
 		s->owner = o->owner;
 		s->facilities = o->facilities;
 		s->airport_type = o->airport_type;
-		s->truck_stop_status = o->truck_stop_status;
-		s->bus_stop_status = o->bus_stop_status;
+		if (s->truck_stops != NULL)
+			s->truck_stops->status = o->truck_stop_status;
+		if (s->bus_stops != NULL)
+			s->bus_stops->status = o->bus_stop_status;
 		s->blocked_months_obsolete = o->blocked_months_obsolete;
 		s->airport_flags = o->airport_flags;
 		s->last_vehicle = o->last_vehicle;
--- a/order_cmd.c	Sat Jan 29 18:22:07 2005 +0000
+++ b/order_cmd.c	Sat Jan 29 19:41:44 2005 +0000
@@ -332,6 +332,12 @@
 
 			if (v->type == VEH_Train)
 				v->u.rail.days_since_order_progr = 0;
+
+			if (v->type == VEH_Road && v->u.road.slot != NULL) {
+				//Clear the slot
+				v->u.road.slot->slot[v->u.road.slotindex] = 0;
+				v->u.road.slot = NULL;
+			}
 		}
 
 		/* NON-stop flag is misused to see if a train is in a station that is
@@ -482,7 +488,7 @@
 				FOR_VEHICLE_ORDERS(src, order) {
 					if (order->type == OT_GOTO_STATION) {
 						const Station *st = GetStation(order->station);
-						required_dst = (dst->cargo_type == CT_PASSENGERS) ? st->bus_tile : st->lorry_tile;
+						required_dst = (dst->cargo_type == CT_PASSENGERS) ? st->bus_stops->xy : st->truck_stops->xy;
 						/* This station has not the correct road-bay, so we can't copy! */
 						if (!required_dst)
 							return CMD_ERROR;
--- a/road_gui.c	Sat Jan 29 18:22:07 2005 +0000
+++ b/road_gui.c	Sat Jan 29 19:41:44 2005 +0000
@@ -89,12 +89,12 @@
 
 static void PlaceRoad_BusStation(uint tile)
 {
-	DoCommandP(tile, _road_station_picker_orientation, 0, CcRoadDepot, CMD_BUILD_BUS_STATION | CMD_AUTO | CMD_NO_WATER | CMD_MSG(STR_1808_CAN_T_BUILD_BUS_STATION));
+	DoCommandP(tile, _road_station_picker_orientation, RS_BUS, CcRoadDepot, CMD_BUILD_ROAD_STOP | CMD_AUTO | CMD_NO_WATER | CMD_MSG(STR_1808_CAN_T_BUILD_BUS_STATION));
 }
 
 static void PlaceRoad_TruckStation(uint tile)
 {
-	DoCommandP(tile, _road_station_picker_orientation, 0, CcRoadDepot, CMD_BUILD_TRUCK_STATION | CMD_AUTO | CMD_NO_WATER | CMD_MSG(STR_1809_CAN_T_BUILD_TRUCK_STATION));
+	DoCommandP(tile, _road_station_picker_orientation, RS_TRUCK, CcRoadDepot, CMD_BUILD_ROAD_STOP | CMD_AUTO | CMD_NO_WATER | CMD_MSG(STR_1809_CAN_T_BUILD_TRUCK_STATION));
 }
 
 static void PlaceRoad_DemolishArea(uint tile)
--- a/roadveh_cmd.c	Sat Jan 29 18:22:07 2005 +0000
+++ b/roadveh_cmd.c	Sat Jan 29 19:41:44 2005 +0000
@@ -164,6 +164,10 @@
 //	v->u.road.unk2 = 0;
 //	v->u.road.overtaking = 0;
 
+		v->u.road.slot = NULL;
+		v->u.road.slotindex = 0;
+		v->u.road.slot_age = 0;
+
 		v->last_station_visited = 0xFFFF;
 		v->max_speed = rvi->max_speed;
 		v->engine_type = (byte)p1;
@@ -409,10 +413,10 @@
 static void ClearCrashedStation(Vehicle *v)
 {
 	uint tile = v->tile;
-	Station *st = GetStation(_map2[tile]);
 	byte *b, bb;
 
-	b = (_map5[tile] >= 0x47) ? &st->bus_stop_status : &st->truck_stop_status;
+	RoadStop *rs = GetRoadStopByTile(tile, GetRoadStopType(tile));
+	b = &rs->status;
 
 	bb = *b;
 
@@ -607,9 +611,34 @@
 	if (order->type == OT_GOTO_STATION) {
 		if (order->station == v->last_station_visited)
 			v->last_station_visited = 0xFFFF;
+		st = GetStation(order->station);
 
-		st = GetStation(order->station);
-		v->dest_tile = v->cargo_type == CT_PASSENGERS ? st->bus_tile : st->lorry_tile;
+		{
+			int32 *dist;
+			int32 mindist = 0xFFFFFFFF;
+			int num;
+			RoadStopType type;
+			RoadStop *rs;
+
+			type = (v->cargo_type == CT_PASSENGERS) ? RS_BUS : RS_TRUCK;
+			num = GetNumRoadStops(st, type);
+			rs = GetPrimaryRoadStop(st, type);
+
+			assert (rs != NULL);
+
+			dist = malloc(num * sizeof(int32));
+
+			do {
+				*dist = GetTileDistAdv(v->tile, rs->xy);
+				if (*dist < mindist) {
+					v->dest_tile = rs->xy;
+				}
+				rs = rs->next;
+			} while ( rs != NULL );
+
+			free(dist);
+			dist = NULL;
+		}
 	} else if (order->type == OT_GOTO_DEPOT) {
 		v->dest_tile = _depots[order->station].xy;
 	}
@@ -990,10 +1019,10 @@
 			Station *st = GetStation(_map2[tile]);
 			byte val = _map5[tile];
 			if (v->cargo_type != CT_PASSENGERS) {
-				if (IS_BYTE_INSIDE(val, 0x43, 0x47) && (_patches.roadveh_queue || st->truck_stop_status&3))
+				if (IS_BYTE_INSIDE(val, 0x43, 0x47) && (_patches.roadveh_queue || st->truck_stops->status&3))
 					bitmask |= _road_veh_fp_ax_or[(val-0x43)&3];
 			} else {
-				if (IS_BYTE_INSIDE(val, 0x47, 0x4B) && (_patches.roadveh_queue || st->bus_stop_status&3))
+				if (IS_BYTE_INSIDE(val, 0x47, 0x4B) && (_patches.roadveh_queue || st->bus_stops->status&3))
 					bitmask |= _road_veh_fp_ax_or[(val-0x47)&3];
 			}
 		}
@@ -1073,6 +1102,29 @@
 	return best_track;
 }
 
+static int RoadFindPathToStation(const Vehicle *v, TileIndex tile)
+{
+	FindRoadToChooseData frd;
+	int i, best_track = -1;
+	uint best_dist = (uint) -1, best_maxlen = (uint) -1;
+
+	frd.dest = tile;
+	frd.maxtracklen = (uint) -1;
+	frd.mindist = (uint) -1;
+
+	for (i = 0; i < 4; i++) {
+		FollowTrack(v->tile, 0x2000 | TRANSPORT_ROAD, i, (TPFEnumProc*)EnumRoadTrackFindDist, NULL, &frd);
+
+		if (frd.mindist < best_dist || (frd.mindist == best_dist && frd.maxtracklen < best_maxlen )) {
+			best_dist = frd.mindist;
+			best_maxlen = frd.maxtracklen;
+			best_track = i;
+		}
+	}
+	return best_maxlen;
+}
+
+
 typedef struct RoadDriveEntry {
 	byte x,y;
 } RoadDriveEntry;
@@ -1088,6 +1140,13 @@
 
 static const byte _roadveh_data_2[4] = { 0,1,8,9 };
 
+static inline void ClearSlot(Vehicle *v, RoadStop *rs)
+{
+	v->u.road.slot = NULL;
+	v->u.road.slot_age = 0;
+	rs->slot[v->u.road.slotindex] = INVALID_SLOT;
+}
+
 static void RoadVehEventHandler(Vehicle *v)
 {
 	GetNewVehiclePosResult gp;
@@ -1247,15 +1306,12 @@
 		if (IS_BYTE_INSIDE(v->u.road.state, 0x20, 0x30) && IsTileType(v->tile, MP_STATION)) {
 			if ((tmp&7) >= 6) { v->cur_speed = 0; return; }
 			if (IS_BYTE_INSIDE(_map5[v->tile], 0x43, 0x4B)) {
-				Station *st = GetStation(_map2[v->tile]);
-				byte *b;
+				RoadStop *rs = GetRoadStopByTile(v->tile, GetRoadStopType(v->tile));
+				byte *b = &rs->status;
 
-				if (_map5[v->tile] >= 0x47) {
-					b = &st->bus_stop_status;
-				} else {
-					b = &st->truck_stop_status;
-				}
-				*b = (*b | ((v->u.road.state&2)?2:1)) & 0x7F;
+				//we have reached a loading bay, mark it as used
+				//and clear the usage bit (0x80) of the stop
+				*b = (*b | ((v->u.road.state&2)?2:1)) & ~0x80;
 			}
 		}
 
@@ -1341,10 +1397,10 @@
 
 	if (v->u.road.state >= 0x20 &&
 			_road_veh_data_1[v->u.road.state - 0x20 + (_opt.road_side<<4)] == v->u.road.frame) {
-		byte *b;
+		RoadStop *rs = GetRoadStopByTile(v->tile, GetRoadStopType(v->tile));
+		byte *b = &rs->status;
 
 		st = GetStation(_map2[v->tile]);
-		b = IS_BYTE_INSIDE(_map5[v->tile], 0x43, 0x47) ? &st->truck_stop_status : &st->bus_stop_status;
 
 		if (v->current_order.type != OT_LEAVESTATION &&
 				v->current_order.type != OT_GOTO_DEPOT) {
@@ -1385,6 +1441,17 @@
 		}
 		*b |= 0x80;
 
+		if (rs == v->u.road.slot) {
+			//we have arrived at the correct station
+			ClearSlot(v, rs);
+		} else if (v->u.road.slot != NULL) {
+			//we have arrived at the wrong station
+			//XXX The question is .. what to do? Actually we shouldn't be here
+			//but I guess we need to clear the slot
+			DEBUG(misc, 2) ("Multistop: Wrong station, force a slot clearing");
+			ClearSlot(v, rs);
+		}
+
 		StartRoadVehSound(v);
 		InvalidateWindowWidget(WC_VEHICLE_VIEW, v->index, STATUS_BAR);
 	}
@@ -1482,6 +1549,10 @@
 			(v->current_order.flags & (OF_FULL_LOAD | OF_UNLOAD)) != 0)
 		return;
 
+	//If we already got a slot at a stop, use that FIRST, and go to a depot later
+	if (v->u.road.slot != NULL)
+		return;
+
 	i = FindClosestRoadDepot(v);
 
 	if (i < 0 || GetTileDist(v->tile, (&_depots[i])->xy) > 12) {
@@ -1508,11 +1579,15 @@
 	InvalidateWindowWidget(WC_VEHICLE_VIEW, v->index, STATUS_BAR);
 }
 
+int dist_compare(const void *a, const void *b)
+{
+	return ( *(const uint32 *)a) - ( *(const uint32 *) b);
+}
+
 void OnNewDay_RoadVeh(Vehicle *v)
 {
 	int32 cost;
 	Station *st;
-	uint tile;
 
 	if ((++v->day_counter & 7) == 0)
 		DecreaseVehicleValue(v);
@@ -1527,9 +1602,86 @@
 
 	/* update destination */
 	if (v->current_order.type == OT_GOTO_STATION) {
+		RoadStop *rs;
+		uint32 mindist = 0xFFFFFFFF;
+		int num;
+		RoadStopType type = (v->cargo_type == CT_PASSENGERS) ? RS_BUS : RS_TRUCK;
+
+		typedef struct {
+			uint32 dist;
+			RoadStop *rs;
+		} StopStruct;
+
+		StopStruct *stop, *firststop;
+
 		st = GetStation(v->current_order.station);
-		if ((tile=(v->cargo_type==CT_PASSENGERS ? st->bus_tile : st->lorry_tile)) != 0)
-			v->dest_tile = tile;
+		rs = GetPrimaryRoadStop(st, type);
+		num = GetNumRoadStops(st, type);
+
+		firststop = stop = malloc(num * sizeof(StopStruct));
+
+		//Current slot has expired
+		if ( (v->u.road.slot_age++ <= 0) && (v->u.road.slot != NULL)) {
+			ClearSlot(v, v->u.road.slot);
+		}
+
+		//We do not have a slot, so make one
+		if (v->u.road.slot == NULL) {
+			//first we need to find out how far our stations are away.
+			assert( rs != NULL);
+
+			do {
+				stop->dist = 0xFFFFFFFF;
+
+				//FIXME This doesn't fully work yet, as it only goes
+				//to one tile BEFORE the stop in question and doesn't
+				//regard the direction of the exit
+				stop->dist = RoadFindPathToStation(v, rs->xy);
+				stop->rs = rs;
+
+				if (stop->dist < mindist) {
+					mindist = stop->dist;
+				}
+
+				stop++;
+				rs = rs->next;
+			} while (rs != NULL);
+
+			if (mindist < 120) {	//if we're reasonably close, get us a slot
+				int k;
+				bubblesort(firststop, num, sizeof(StopStruct), dist_compare);
+
+				stop = firststop;
+				for (k = 0; k < num; k++) {
+					int i;
+					for (i = 0; i < NUM_SLOTS; i++) {
+						if ((stop->rs->slot[i] == INVALID_SLOT) && (stop->dist < 120)) {
+
+							//Hooray we found a free slot. Assign it
+							stop->rs->slot[i] = v->index;
+							v->u.road.slot = stop->rs;
+
+							v->dest_tile = stop->rs->xy;
+							v->u.road.slot_age = -30;
+							v->u.road.slotindex = i;
+
+							goto have_slot;	//jump out of BOTH loops
+
+						}
+					}
+					stop++;
+				}
+			}
+
+have_slot:
+		//now we couldn't assign a slot for one reason or another.
+		//so we just go to the nearest station
+		if (v->u.road.slot == NULL)
+			v->dest_tile = firststop->rs->xy;
+		}
+
+		free(firststop);
+		firststop = stop = NULL;
 	}
 
 	if (v->vehstatus & VS_STOPPED)
--- a/saveload.c	Sat Jan 29 18:22:07 2005 +0000
+++ b/saveload.c	Sat Jan 29 19:41:44 2005 +0000
@@ -7,8 +7,8 @@
 #include "saveload.h"
 
 enum {
-	SAVEGAME_MAJOR_VERSION = 5,
-	SAVEGAME_MINOR_VERSION = 2,
+	SAVEGAME_MAJOR_VERSION = 6,
+	SAVEGAME_MINOR_VERSION = 0,
 
 	SAVEGAME_LOADABLE_VERSION = (SAVEGAME_MAJOR_VERSION << 8) + SAVEGAME_MINOR_VERSION
 };
@@ -918,6 +918,10 @@
 		case REF_TOWN:    return ((Town *)v)->index + 1;
 		case REF_ORDER:   return ((Order *)v)->index + 1;
 
+		case REF_ROADSTOPS:
+			//return ((byte*)v - (byte*)_roadstops) / sizeof(_roadstops[0]) + 1;
+			return (RoadStop *)v - _roadstops + 1;
+
 		default:
 			NOT_REACHED();
 	}
@@ -942,6 +946,9 @@
 		case REF_STATION: return GetStation(r - 1);
 		case REF_TOWN:    return GetTown(r - 1);
 
+		case REF_ROADSTOPS:
+			//return (byte*)_roadstops    + (r - 1) * sizeof(_roadstops[0]);
+			return &_roadstops[r - 1];
 		case REF_VEHICLE_OLD: {
 			/* Old vehicles were saved differently: invalid vehicle was 0xFFFF,
 			    and the index was not - 1.. correct for this */
--- a/saveload.h	Sat Jan 29 18:22:07 2005 +0000
+++ b/saveload.h	Sat Jan 29 19:41:44 2005 +0000
@@ -74,7 +74,8 @@
 	REF_VEHICLE     = 1,
 	REF_STATION     = 2,
 	REF_TOWN        = 3,
-	REF_VEHICLE_OLD = 4
+	REF_VEHICLE_OLD = 4,
+	REF_ROADSTOPS   = 5
 };
 
 
@@ -151,7 +152,7 @@
 #define SLE_VARX(t,c) 0x00 | ((t) & 0xF), (t) >> 4, c
 #define SLE_REFX(t,c) 0x10 | ((t) & 0xF), (t) >> 4, c
 #define SLE_CONDVARX(t,c,from,to) 0x40 | ((t) & 0xF), (t) >> 4, c, from, to
-#define SLE_CONDREFX(t,c,co) 0x50 | ((t) & 0xF), (t) >> 4, c, co
+#define SLE_CONDREFX(t,c,from,to) 0x50 | ((t) & 0xF), (t) >> 4, c, from, to
 #define SLE_WRITEBYTEX(t,b) 0x80 | ((t) & 0xF), (t) >> 4, b
 #define SLE_INCLUDEX(t,c) 0x90 | ((t) & 0xF), (t) >> 4, c
 
--- a/station.h	Sat Jan 29 18:22:07 2005 +0000
+++ b/station.h	Sat Jan 29 19:41:44 2005 +0000
@@ -14,10 +14,32 @@
 	byte last_age;
 } GoodsEntry;
 
+typedef enum RoadStopType {
+	RS_BUS,
+	RS_TRUCK
+} RoadStopType;
+
+enum { NUM_ROAD_STOPS = 250 };
+enum { ROAD_STOP_LIMIT = 8 };
+enum { NUM_SLOTS = 2 };
+enum { INVALID_SLOT = 0xFFFF };
+
+typedef struct RoadStop {
+	TileIndex xy;
+	bool used;
+	byte status;
+	uint32 index;
+	uint16 slot[NUM_SLOTS];
+	uint16 station;		//XXX should be StationIndex
+	uint8 type;
+	struct RoadStop *next;
+	struct RoadStop *prev;
+} RoadStop;
+
 struct Station {
 	TileIndex xy;
-	TileIndex bus_tile;
-	TileIndex lorry_tile;
+	RoadStop *bus_stops;
+	RoadStop *truck_stops;
 	TileIndex train_tile;
 	TileIndex airport_tile;
 	TileIndex dock_tile;
@@ -36,9 +58,6 @@
 	byte owner;
 	byte facilities;
 	byte airport_type;
-	byte truck_stop_status;
-	byte bus_stop_status;
-	byte blocked_months_obsolete;
 
 	// trainstation width/height
 	byte trainst_w, trainst_h;
@@ -53,6 +72,14 @@
 
 	VehicleID last_vehicle;
 	GoodsEntry goods[NUM_CARGO];
+
+	/* Stuff that is no longer used, but needed for conversion */
+	TileIndex bus_tile_obsolete;
+	TileIndex lorry_tile_obsolete;
+
+	byte truck_stop_status_obsolete;
+	byte bus_stop_status_obsolete;
+	byte blocked_months_obsolete;
 };
 
 enum {
@@ -68,7 +95,7 @@
 	HVOT_TRAIN = 1<<1,
 	HVOT_BUS = 1 << 2,
 	HVOT_TRUCK = 1 << 3,
-	HVOT_AIRCRAFT = 1<<4,
+	HVOT_AIRCRAFT = 1 << 4,
 	HVOT_SHIP = 1 << 5,
 	HVOT_BUOY = 1 << 6
 };
@@ -93,7 +120,9 @@
 void ShowStationViewWindow(int station);
 void UpdateAllStationVirtCoord(void);
 
+VARDEF RoadStop _roadstops[NUM_ROAD_STOPS * 2];
 VARDEF Station _stations[250];
+VARDEF uint _roadstops_size;
 VARDEF uint _stations_size;
 
 VARDEF SortStruct *_station_sort;
@@ -189,4 +218,10 @@
 uint32 GetCustomStationRelocation(struct StationSpec *spec, struct Station *stat, byte ctype);
 int GetCustomStationsCount(enum StationClass sclass);
 
+RoadStop * GetRoadStopByTile(TileIndex tile, RoadStopType type);
+inline int GetRoadStopType(TileIndex tile);
+uint GetNumRoadStops(const Station *st, RoadStopType type);
+RoadStop * GetPrimaryRoadStop(const Station *st, RoadStopType type);
+RoadStop * GetFirstFreeRoadStop( void );
+
 #endif /* STATION_H */
--- a/station_cmd.c	Sat Jan 29 18:22:07 2005 +0000
+++ b/station_cmd.c	Sat Jan 29 19:41:44 2005 +0000
@@ -43,14 +43,79 @@
 	}
 }
 
+static void InitializeRoadStop(RoadStop *road_stop, RoadStop *previous, TileIndex tile, uint index)
+{
+	road_stop->xy = tile;
+	road_stop->used = true;
+	road_stop->status = 3; //stop is free
+	road_stop->slot[0] = road_stop->slot[1] = INVALID_SLOT;
+	road_stop->next = NULL;
+	road_stop->prev = previous;
+	road_stop->station = index;
+}
+
+inline int GetRoadStopType(TileIndex tile)
+{
+	return (_map5[tile] < 0x47) ? RS_TRUCK : RS_BUS;
+}
+
+RoadStop * GetPrimaryRoadStop(const Station *st, RoadStopType type)
+{
+	switch (type) {
+		case RS_BUS: return st->bus_stops;
+		case RS_TRUCK: return st->truck_stops;
+		default:
+			NOT_REACHED();
+	}
+
+	return NULL;
+}
+
+RoadStop * GetRoadStopByTile(TileIndex tile, RoadStopType type)
+{
+	const Station *st = GetStation(_map2[tile]);
+	RoadStop *rs;
+
+	for ( rs = GetPrimaryRoadStop(st, type); rs->xy != tile; rs = rs->next)
+		assert(rs->next != NULL);
+
+	return rs;
+}
+
+uint GetNumRoadStops(const Station *st, RoadStopType type)
+{
+	int num = 0;
+	const RoadStop *rs;
+
+	assert(st != NULL);
+	for ( rs = GetPrimaryRoadStop(st, type); rs != NULL; num++, rs = rs->next);
+
+	return num;
+}
+
+RoadStop * GetFirstFreeRoadStop( void )
+{
+	RoadStop *rs = _roadstops;
+	int i = 0;
+
+	for ( i = 0; i < NUM_ROAD_STOPS; i++, rs++) {
+		if (!rs->used) {
+			rs->index = i;
+			return rs;
+		}
+	}
+
+	return NULL;
+}
+
 /* Calculate the radius of the station. Basicly it is the biggest
     radius that is available within the station */
 static byte FindCatchmentRadius(Station *st)
 {
 	byte ret = 0;
 
-	if (st->bus_tile)   ret = max(ret, CA_BUS);
-	if (st->lorry_tile) ret = max(ret, CA_TRUCK);
+	if (st->bus_stops != NULL)   ret = max(ret, CA_BUS);
+	if (st->truck_stops != NULL) ret = max(ret, CA_TRUCK);
 	if (st->train_tile) ret = max(ret, CA_TRAIN);
 	if (st->dock_tile)  ret = max(ret, CA_DOCK);
 
@@ -101,7 +166,7 @@
 		case VEH_Train: 		return st->train_tile;
 		case VEH_Aircraft:	return st->airport_tile;
 		case VEH_Ship:			return st->dock_tile;
-		case VEH_Road:			return (v->cargo_type == CT_PASSENGERS) ? st->bus_tile : st->lorry_tile;
+		case VEH_Road:			return (v->cargo_type == CT_PASSENGERS) ? st->bus_stops->xy : st->truck_stops->xy;
 		default:
 			assert(false);
 			return 0;
@@ -326,7 +391,8 @@
 	GoodsEntry *ge;
 
 	st->xy = tile;
-	st->bus_tile = st->lorry_tile = st->airport_tile = st->dock_tile = st->train_tile = 0;
+	st->airport_tile = st->dock_tile = st->train_tile = 0;
+	st->bus_stops = st->truck_stops = NULL;
 	st->had_vehicle_of_type = 0;
 	st->time_since_load = 255;
 	st->time_since_unload = 255;
@@ -352,8 +418,9 @@
 static void UpdateStationVirtCoord(Station *st)
 {
 	Point pt = RemapCoords2(TileX(st->xy) * 16, TileY(st->xy) * 16);
+
 	pt.y -= 32;
-	if (st->facilities&FACIL_AIRPORT && st->airport_type==AT_OILRIG) pt.y -= 16;
+	if (st->facilities & FACIL_AIRPORT && st->airport_type == AT_OILRIG) pt.y -= 16;
 
 	SetDParam(0, st->index);
 	SetDParam(1, st->facilities);
@@ -498,10 +565,13 @@
 static void UpdateStationAcceptance(Station *st, bool show_msg)
 {
 	uint old_acc, new_acc;
-	TileIndex span[1+1+2+2+1];
+	TileIndex *span;
+	RoadStop *cur_rs;
 	int i;
 	int min_x, min_y, max_x, max_y;
 	int rad = 4;	//Put this to surpress a compiler warning
+	int num = 0;
+	int num_bus, num_truck;
 	uint accepts[NUM_CARGO];
 
 	// Don't update acceptance for a buoy
@@ -511,33 +581,62 @@
 	/* old accepted goods types */
 	old_acc = GetAcceptanceMask(st);
 
+	if (st->train_tile != 0) num += 2;
+	if (st->airport_tile != 0) num += 2;
+	if (st->dock_tile != 0) num++;
+
+	num_bus = GetNumRoadStops(st, RS_BUS);
+	num_truck = GetNumRoadStops(st, RS_TRUCK);
+
+	num += (num_bus + num_truck);
+
+	span = malloc(num * sizeof(*span));
+	if (span == NULL)
+		error("UpdateStationAcceptance: Could not allocate memory");
+
 	// Put all the tiles that span an area in the table.
-	span[3] = span[5] = 0;
-	span[0] = st->bus_tile;
-	span[1] = st->lorry_tile;
-	span[2] = st->train_tile;
 	if (st->train_tile != 0) {
-		span[3] = st->train_tile + TILE_XY(st->trainst_w-1, st->trainst_h-1);
+		*span++ = st->train_tile;
+		*span++ = st->train_tile + TILE_XY(st->trainst_w-1, st->trainst_h-1);
 	}
-	span[4] = st->airport_tile;
+
 	if (st->airport_tile != 0) {
-		span[5] = st->airport_tile + TILE_XY(_airport_size_x[st->airport_type]-1, _airport_size_y[st->airport_type]-1);
+		*span++ = st->airport_tile;
+		*span++ = st->airport_tile + TILE_XY(_airport_size_x[st->airport_type]-1, _airport_size_y[st->airport_type]-1);
 	}
-	span[6] = st->dock_tile;
+
+	if (st->dock_tile != 0)
+		*span++ = st->dock_tile;
+
+	cur_rs = st->bus_stops;
+	for (i = 0; i < num_bus; i++) {
+		*span++ = cur_rs->xy;
+		cur_rs = cur_rs->next;
+	}
+
+	cur_rs = st->truck_stops;
+	for (i = 0; i < num_truck; i++) {
+		*span++ = cur_rs->xy;
+		cur_rs = cur_rs->next;
+	}
 
 	// Construct a rectangle from those points
 	min_x = min_y = 0x7FFFFFFF;
 	max_x = max_y = 0;
 
-	for(i=0; i!=7; i++) {
-		uint tile = span[i];
-		if (tile) {
+	for(; num != 0; num--) {
+		TileIndex tile = *(--span);
+		if (tile != 0) {	//assume there is no station at (0, 0)
 			min_x = min(min_x, TileX(tile));
 			max_x = max(max_x, TileX(tile));
 			min_y = min(min_y, TileY(tile));
 			max_y = max(max_y, TileY(tile));
 		}
 	}
+
+	free(span);
+ span = NULL;
+
 	if (_patches.modified_catchment) {
 		rad = FindCatchmentRadius(st);
 	} else {
@@ -546,7 +645,7 @@
 
 	// And retrieve the acceptance.
 	if (max_x != 0) {
-		GetAcceptanceAroundTiles(accepts, TILE_XY(min_x, min_y), max_x - min_x + 1, max_y-min_y+1,rad);
+		GetAcceptanceAroundTiles(accepts, TILE_XY(min_x, min_y), max_x - min_x + 1, max_y-min_y+1, rad);
 	} else {
 		memset(accepts, 0, sizeof(accepts));
 	}
@@ -1127,10 +1226,10 @@
 								value = stat->airport_type;
 								break;
 							case 0x82:
-								value = stat->truck_stop_status;
+								value = stat->truck_stops->status;
 								break;
 							case 0x83:
-								value = stat->bus_stop_status;
+								value = stat->bus_stops->status;
 								break;
 							case 0x86:
 								value = stat->airport_flags & 0xFFFF;
@@ -1260,16 +1359,47 @@
 	return _price.build_rail >> 1;
 }
 
+void FindRoadStationSpot(bool truck_station, Station *st, RoadStop ***currstop, RoadStop **prev)
+{
+	RoadStop **primary_stop;
+
+	primary_stop = (truck_station) ? &st->truck_stops : &st->bus_stops;
+
+	if (*primary_stop == NULL) {
+		//we have no station of the type yet, so write a "primary station"
+		//(the one at st->foo_stops)
+		*currstop = primary_stop;
+	} else {
+		//there are stops already, so append to the end of the list
+		*prev = *primary_stop;
+		*currstop = &(*primary_stop)->next;
+		while (**currstop != NULL) {
+			*prev = (*prev)->next;
+			*currstop = &(**currstop)->next;
+		}
+	}
+}
+
 /* Build a bus station
- * p1 - direction
- * p2 - unused
+ * direction - direction of the stop exit
+ * type - 0 for Bus stops, 1 for truck stops
  */
 
-int32 CmdBuildBusStation(int x, int y, uint32 flags, uint32 p1, uint32 p2)
+int32 CmdBuildRoadStop(int x, int y, uint32 flags, uint32 direction, uint32 type)
 {
+	RoadStop *road_stop;
+	RoadStop **currstop;
+	RoadStop *prev = NULL;
 	uint tile;
 	int32 cost;
 	Station *st;
+	//Bus stops have a _map5 value of 0x47 + direction
+	//Truck stops have 0x43 + direction
+	byte gfxbase = (type) ? 0x43 : 0x47;
+
+	//saveguard the parameters
+	if (direction > 3 || type > 1)
+		return CMD_ERROR;
 
 	SET_EXPENSES_TYPE(EXPENSES_CONSTRUCTION);
 
@@ -1278,7 +1408,8 @@
 	if (!(flags & DC_NO_TOWN_RATING) && !CheckIfAuthorityAllows(tile))
 		return CMD_ERROR;
 
-	if ((cost=CheckFlatLandBelow(tile, 1, 1, flags, 1 << p1, NULL)) == CMD_ERROR)
+	cost = CheckFlatLandBelow(tile, 1, 1, flags, 1 << direction, NULL);
+	if (cost == CMD_ERROR)
 		return CMD_ERROR;
 
 	st = GetStationAround(tile, 1, 1, -1);
@@ -1291,6 +1422,14 @@
 		if (st!=NULL && st->facilities) st = NULL;
 	}
 
+	//give us a road stop in the list, and check if something went wrong
+	road_stop = GetFirstFreeRoadStop();
+	if (road_stop == NULL)
+		return_cmd_error( (type) ? STR_3008B_TOO_MANY_TRUCK_STOPS : STR_3008A_TOO_MANY_BUS_STOPS);
+
+	if ( st != NULL && (GetNumRoadStops(st, RS_BUS) + GetNumRoadStops(st, RS_TRUCK) >= ROAD_STOP_LIMIT))
+		return_cmd_error( (type) ? STR_3008B_TOO_MANY_TRUCK_STOPS : STR_3008A_TOO_MANY_BUS_STOPS);
+
 	if (st != NULL) {
 		if (st->owner != OWNER_NONE && st->owner != _current_player)
 			return_cmd_error(STR_3009_TOO_CLOSE_TO_ANOTHER_STATION);
@@ -1298,8 +1437,7 @@
 		if (!CheckStationSpreadOut(st, tile, 1, 1))
 			return CMD_ERROR;
 
-		if (st->bus_tile != 0)
-			return_cmd_error(STR_3044_TOO_CLOSE_TO_ANOTHER_BUS);
+		FindRoadStationSpot(type, st, &currstop, &prev);
 	} else {
 		Town *t;
 
@@ -1309,6 +1447,8 @@
 
 		st->town = t = ClosestTownFromTile(tile, (uint)-1);
 
+		FindRoadStationSpot(type, st, &currstop, &prev);
+
 		if (_current_player < MAX_PLAYERS && flags&DC_EXEC)
 			SETBIT(t->have_ratings, _current_player);
 
@@ -1321,13 +1461,17 @@
 			StationInitialize(st, tile);
 	}
 
-	cost += _price.build_bus_station;
+	cost += (type) ? _price.build_truck_station : _price.build_bus_station;
 
 	if (flags & DC_EXEC) {
-		st->bus_tile = tile;
+		//point to the correct item in the _busstops or _truckstops array
+		*currstop = road_stop;
+
+		//initialize an empty station
+		InitializeRoadStop(road_stop, prev, tile, st->index);
+		(*currstop)->type = type;
 		if (!st->facilities) st->xy = tile;
-		st->facilities |= FACIL_BUS_STOP;
-		st->bus_stop_status = 3;
+		st->facilities |= (type) ? FACIL_TRUCK_STOP : FACIL_BUS_STOP;
 		st->owner = _current_player;
 
 		st->build_date = _date;
@@ -1336,7 +1480,7 @@
 			MP_SETTYPE(MP_STATION) | MP_MAPOWNER_CURRENT |
 			MP_MAP2 | MP_MAP5 | MP_MAP3LO_CLEAR | MP_MAP3HI_CLEAR,
 			st->index,			/* map2 parameter */
-			p1 + 0x47       /* map5 parameter */
+			gfxbase + direction       /* map5 parameter */
 		);
 
 		UpdateStationVirtCoordDirty(st);
@@ -1347,14 +1491,24 @@
 }
 
 // Remove a bus station
-static int32 RemoveBusStation(Station *st, uint32 flags)
+static int32 RemoveRoadStop(Station *st, uint32 flags, TileIndex tile)
 {
-	uint tile;
+	RoadStop **primary_stop;
+	RoadStop *cur_stop;
+	bool is_truck = _map5[tile] < 0x47;
 
 	if (_current_player != OWNER_WATER && !CheckOwnership(st->owner))
 		return CMD_ERROR;
 
-	tile = st->bus_tile;
+	if (is_truck) {	//truck stop
+		primary_stop = &st->truck_stops;
+		cur_stop = GetRoadStopByTile(tile, RS_TRUCK);
+	} else {
+		primary_stop = &st->bus_stops;
+		cur_stop = GetRoadStopByTile(tile, RS_BUS);
+	}
+
+	assert(cur_stop != NULL);
 
 	if (!EnsureNoVehicle(tile))
 		return CMD_ERROR;
@@ -1362,127 +1516,34 @@
 	if (flags & DC_EXEC) {
 		DoClearSquare(tile);
 
-		st->bus_tile = 0;
-		st->facilities &= ~FACIL_BUS_STOP;
+		cur_stop->used = false;
+		if (cur_stop->prev != NULL)	//alter previous stop
+			cur_stop->prev->next = cur_stop->next;
+
+		if (cur_stop->next != NULL)	//alter next stop
+			cur_stop->next->prev = cur_stop->prev;
+
+		//we only had one stop left
+		if (cur_stop->next == NULL && cur_stop->prev == NULL) {
+
+			//so we remove ALL stops
+			*primary_stop = NULL;
+			st->facilities &= (is_truck) ? ~FACIL_TRUCK_STOP : ~FACIL_BUS_STOP;
+
+		} else if (cur_stop == *primary_stop) {
+			//removed the first stop in the list
+			//need to set the primary element to the next stop
+			*primary_stop = (*primary_stop)->next;
+		}
 
 		UpdateStationVirtCoordDirty(st);
 		DeleteStationIfEmpty(st);
 	}
 
-	return _price.remove_bus_station;
+	return (is_truck) ? _price.remove_truck_station : _price.remove_bus_station;
 }
 
 
-/* Build a truck station
- * p1 - direction
- * p2 - unused
- */
-int32 CmdBuildTruckStation(int x, int y, uint32 flags, uint32 p1, uint32 p2)
-{
-	uint tile;
-	int32 cost = 0;
-	Station *st;
-
-	SET_EXPENSES_TYPE(EXPENSES_CONSTRUCTION);
-
-	tile = TILE_FROM_XY(x,y);
-
-	if (!(flags & DC_NO_TOWN_RATING) && !CheckIfAuthorityAllows(tile))
-		return CMD_ERROR;
-
-	if ((cost=CheckFlatLandBelow(tile, 1, 1, flags, 1 << p1, NULL)) == CMD_ERROR)
-		return CMD_ERROR;
-
-	st = GetStationAround(tile, 1, 1, -1);
-	if (st == CHECK_STATIONS_ERR)
-		return CMD_ERROR;
-
-	/* Find a station close to us */
-	if (st == NULL) {
-		st = GetClosestStationFromTile(tile, 8, _current_player);
-		if (st!=NULL && st->facilities) st = NULL;
-	}
-
-	if (st != NULL) {
-		if (st->owner != OWNER_NONE && st->owner != _current_player)
-			return_cmd_error(STR_3009_TOO_CLOSE_TO_ANOTHER_STATION);
-
-		if (!CheckStationSpreadOut(st, tile, 1, 1))
-			return CMD_ERROR;
-
-		if (st->lorry_tile != 0)
-			return_cmd_error(STR_3045_TOO_CLOSE_TO_ANOTHER_TRUCK);
-	} else {
-		Town *t;
-
-		st = AllocateStation();
-		if (st == NULL)
-			return CMD_ERROR;
-
-		st->town = t = ClosestTownFromTile(tile, (uint)-1);
-
-		if (_current_player < MAX_PLAYERS && flags&DC_EXEC)
-			SETBIT(t->have_ratings, _current_player);
-
-		st->sign.width_1 = 0;
-
-		if (!GenerateStationName(st, tile, 0))
-			return CMD_ERROR;
-
-		if (flags & DC_EXEC)
-			StationInitialize(st, tile);
-	}
-
-	cost += _price.build_truck_station;
-
-	if (flags & DC_EXEC) {
-		st->lorry_tile = tile;
-		if (!st->facilities) st->xy = tile;
-		st->facilities |= FACIL_TRUCK_STOP;
-		st->truck_stop_status = 3;
-		st->owner = _current_player;
-
-		st->build_date = _date;
-
-		ModifyTile(tile,
-			MP_SETTYPE(MP_STATION) | MP_MAPOWNER_CURRENT |
-			MP_MAP2 | MP_MAP3LO_CLEAR | MP_MAP3HI_CLEAR | MP_MAP5,
-			st->index,			/* map2 parameter */
-			p1 + 0x43       /* map5 parameter */
-		);
-
-		UpdateStationVirtCoordDirty(st);
-		UpdateStationAcceptance(st, false);
-		InvalidateWindow(WC_STATION_LIST, st->owner);
-	}
-	return cost;
-}
-
-// Remove a truck station
-static int32 RemoveTruckStation(Station *st, uint32 flags)
-{
-	uint tile;
-
-	if (_current_player != OWNER_WATER && !CheckOwnership(st->owner))
-		return CMD_ERROR;
-
-	tile = st->lorry_tile;
-
-	if (!EnsureNoVehicle(tile))
-		return CMD_ERROR;
-
-	if (flags & DC_EXEC) {
-		DoClearSquare(tile);
-
-		st->lorry_tile = 0;
-		st->facilities &= ~FACIL_TRUCK_STOP;
-
-		UpdateStationVirtCoordDirty(st);
-		DeleteStationIfEmpty(st);
-	}
-
-	return _price.remove_truck_station;
-}
 
 // FIXME -- need to move to its corresponding Airport variable
 // Country Airfield (small)
@@ -2215,7 +2276,7 @@
 
 static uint32 VehicleEnter_Station(Vehicle *v, uint tile, int x, int y)
 {
-	uint16 station_id;
+	uint16 station_id;	//XXX should be stationindex
 	byte dir;
 	uint16 spd;
 
@@ -2252,12 +2313,12 @@
 		}
 	} else if (v->type == VEH_Road) {
 		if (v->u.road.state < 16 && (v->u.road.state&4)==0 && v->u.road.frame==0) {
-			Station *st = GetStation(_map2[tile]);
 			byte m5 = _map5[tile];
 			byte *b, bb,state;
 
 			if (IS_BYTE_INSIDE(m5, 0x43, 0x4B)) {
-				b = (m5 >= 0x47) ? &st->bus_stop_status : &st->truck_stop_status;
+				RoadStop *rs = GetRoadStopByTile(tile, GetRoadStopType(tile));
+				b = &rs->status;
 
 				bb = *b;
 
@@ -2275,6 +2336,8 @@
 					bb &= ~2;
 					state += 2;
 				}
+
+				bb |= 0x80;
 				*b = bb;
 				v->u.road.state = state;
 			}
@@ -2625,7 +2688,8 @@
 	/* several stations around, find the two with the highest rating */
 	st2 = st1 = NULL;
 	best_rating = best_rating2 = 0;
-	for(i=0; i!=8 && around[i] != 0xFF; i++) {
+
+	for( i = 0; i != 8 && around[i] != 0xFF; i++) {
 		if (around_ptr[i]->goods[type].rating >= best_rating) {
 			best_rating2 = best_rating;
 			st2 = st1;
@@ -2686,8 +2750,8 @@
       st->airport_flags = 0;
 			st->airport_type = AT_OILRIG;
 			st->xy = tile;
-			st->bus_tile = 0;
-			st->lorry_tile = 0;
+			st->bus_stops = NULL;
+			st->truck_stops = NULL;
 			st->airport_tile = tile;
 			st->dock_tile = tile;
 			st->train_tile = 0;
@@ -2768,11 +2832,8 @@
 	if (m5 < 0x43 || ( m5 >= 83 && m5 <= 114) )
 		return RemoveAirport(st, flags);
 
-	if (m5 < 0x47)
-		return RemoveTruckStation(st, flags);
-
 	if (m5 < 0x4B)
-		return RemoveBusStation(st, flags);
+		return RemoveRoadStop(st, flags, tile);
 
 	if (m5 == 0x52)
 		return RemoveBuoy(st, flags);
@@ -2789,6 +2850,7 @@
 	int i;
 	Station *s;
 
+	memset(_roadstops, 0, sizeof(_roadstops));
 	memset(_stations, 0, sizeof(_stations[0]) * _stations_size);
 
 	i = 0;
@@ -2820,14 +2882,27 @@
 	GetSlopeTileh_Station,			/* get_slope_tileh_proc */
 };
 
+static const byte _roadstop_desc[] = {
+	SLE_VAR(RoadStop,xy,           SLE_UINT32),
+	SLE_VAR(RoadStop,used,         SLE_UINT8),
+	SLE_VAR(RoadStop,status,       SLE_UINT8),
+	SLE_VAR(RoadStop,index,        SLE_UINT32),
+	SLE_VAR(RoadStop,station,      SLE_UINT16),
+	SLE_VAR(RoadStop,type,         SLE_UINT8),
+
+	SLE_REF(RoadStop,next,         REF_ROADSTOPS),
+	SLE_REF(RoadStop,prev,         REF_ROADSTOPS),
+
+ SLE_ARR(RoadStop,slot,         SLE_UINT16, NUM_SLOTS),
+
+	SLE_END()
+};
 
 static const byte _station_desc[] = {
 	SLE_CONDVAR(Station, xy,           SLE_FILE_U16 | SLE_VAR_U32, 0, 5),
 	SLE_CONDVAR(Station, xy,           SLE_UINT32, 6, 255),
-	SLE_CONDVAR(Station, bus_tile,     SLE_FILE_U16 | SLE_VAR_U32, 0, 5),
-	SLE_CONDVAR(Station, bus_tile,     SLE_UINT32, 6, 255),
-	SLE_CONDVAR(Station, lorry_tile,   SLE_FILE_U16 | SLE_VAR_U32, 0, 5),
-	SLE_CONDVAR(Station, lorry_tile,   SLE_UINT32, 6, 255),
+	SLE_CONDVAR(Station, bus_tile_obsolete,    SLE_FILE_U16 | SLE_VAR_U32, 0, 5),
+	SLE_CONDVAR(Station, lorry_tile_obsolete,  SLE_FILE_U16 | SLE_VAR_U32, 0, 5),
 	SLE_CONDVAR(Station, train_tile,   SLE_FILE_U16 | SLE_VAR_U32, 0, 5),
 	SLE_CONDVAR(Station, train_tile,   SLE_UINT32, 6, 255),
 	SLE_CONDVAR(Station, airport_tile, SLE_FILE_U16 | SLE_VAR_U32, 0, 5),
@@ -2850,8 +2925,10 @@
 	SLE_VAR(Station,owner,							SLE_UINT8),
 	SLE_VAR(Station,facilities,					SLE_UINT8),
 	SLE_VAR(Station,airport_type,				SLE_UINT8),
-	SLE_VAR(Station,truck_stop_status,	SLE_UINT8),
-	SLE_VAR(Station,bus_stop_status,		SLE_UINT8),
+
+	// truck/bus_stop_status was stored here in savegame format 0 - 6
+	SLE_CONDVAR(Station,truck_stop_status_obsolete,	SLE_UINT8, 0, 5),
+	SLE_CONDVAR(Station,bus_stop_status_obsolete,		SLE_UINT8, 0, 5),
 
 	// blocked_months was stored here in savegame format 0 - 4.0
 	SLE_CONDVAR(Station,blocked_months_obsolete,	SLE_UINT8, 0, 4),
@@ -2865,7 +2942,10 @@
 	SLE_CONDVAR(Station,stat_id,				SLE_UINT8, 3, 255),
 	SLE_CONDVAR(Station,build_date,			SLE_UINT16, 3, 255),
 
-	// reserve extra space in savegame here. (currently 32 bytes)
+	SLE_CONDREF(Station,bus_stops,					REF_ROADSTOPS, 6, 255),
+	SLE_CONDREF(Station,truck_stops,				REF_ROADSTOPS, 6, 255),
+
+	// reserve extra space in savegame here. (currently 28 bytes)
 	SLE_CONDARR(NullStruct,null,SLE_FILE_U8 | SLE_VAR_NULL, 32, 2, 255),
 
 	SLE_END()
@@ -2887,8 +2967,9 @@
 static void SaveLoad_STNS(Station *st)
 {
 	int i;
+
 	SlObject(st, _station_desc);
-	for(i=0; i!=NUM_CARGO; i++)
+	for (i = 0; i != NUM_CARGO; i++)
 		SlObject(&st->goods[i], _goods_desc);
 }
 
@@ -2920,6 +3001,32 @@
 			st->trainst_w = w;
 			st->trainst_h = h;
 		}
+
+		if (_sl.full_version < 0x600) {
+			/* Convert old bus and truck tile to new-ones */
+			RoadStop **currstop;
+			RoadStop *prev = NULL;
+			RoadStop *road_stop;
+
+			if (st->bus_tile_obsolete != 0) {
+				road_stop = GetFirstFreeRoadStop();
+				if (road_stop == NULL)
+					error("Station: too many busstations in savegame");
+
+				FindRoadStationSpot(RS_BUS, st, &currstop, &prev);
+				*currstop = road_stop;
+				InitializeRoadStop(road_stop, prev, st->bus_tile_obsolete, st->index);
+			}
+			if (st->lorry_tile_obsolete != 0) {
+				road_stop = GetFirstFreeRoadStop();
+				if (road_stop == NULL)
+					error("Station: too many truckstations in savegame");
+
+				FindRoadStationSpot(RS_TRUCK, st, &currstop, &prev);
+				*currstop = road_stop;
+				InitializeRoadStop(road_stop, prev, st->lorry_tile_obsolete, st->index);
+			}
+		}
 	}
 
 	/* This is to ensure all pointers are within the limits of
@@ -2928,7 +3035,28 @@
 		_station_tick_ctr = 0;
 }
 
+static void Save_ROADSTOP( void )
+{
+	uint i;
+
+	for (i = 0; i < lengthof(_roadstops); i++) {
+		if (_roadstops[i].used) {
+			SlSetArrayIndex(i);
+			SlObject(&_roadstops[i], _roadstop_desc);
+		}
+	}
+}
+
+static void Load_ROADSTOP( void )
+{
+	int index;
+
+	while ((index = SlIterateArray()) != -1)
+		SlObject(&_roadstops[index], _roadstop_desc);
+}
+
 const ChunkHandler _station_chunk_handlers[] = {
-	{ 'STNS', Save_STNS, Load_STNS, CH_ARRAY | CH_LAST},
+	{ 'STNS', Save_STNS,      Load_STNS,      CH_ARRAY },
+	{ 'ROAD', Save_ROADSTOP,  Load_ROADSTOP,  CH_ARRAY | CH_LAST},
 };
 
--- a/ttd.c	Sat Jan 29 18:22:07 2005 +0000
+++ b/ttd.c	Sat Jan 29 19:41:44 2005 +0000
@@ -496,6 +496,8 @@
 	_stations_size = lengthof(_stations);
 	_station_sort = NULL;
 
+	_roadstops_size = lengthof(_roadstops);
+
 	_vehicles_size = lengthof(_vehicles);
 	_vehicle_sort = NULL;
 
--- a/vehicle.c	Sat Jan 29 18:22:07 2005 +0000
+++ b/vehicle.c	Sat Jan 29 19:41:44 2005 +0000
@@ -1886,6 +1886,9 @@
 	SLE_VARX(offsetof(Vehicle,u)+offsetof(VehicleRoad,crashed_ctr),		SLE_UINT16),
 	SLE_VARX(offsetof(Vehicle,u)+offsetof(VehicleRoad,reverse_ctr),			SLE_UINT8),
 
+	SLE_CONDREFX(offsetof(Vehicle,u)+offsetof(VehicleRoad,slot), REF_ROADSTOPS, 6, 255),
+	SLE_CONDVARX(offsetof(Vehicle,u)+offsetof(VehicleRoad,slotindex), SLE_UINT8, 6, 255),
+	SLE_CONDVARX(offsetof(Vehicle,u)+offsetof(VehicleRoad,slot_age), SLE_UINT8, 6, 255),
 	// reserve extra space in savegame here. (currently 16 bytes)
 	SLE_CONDARR(NullStruct,null,SLE_FILE_U64 | SLE_VAR_NULL, 2, 2, 255),
 
--- a/vehicle.h	Sat Jan 29 18:22:07 2005 +0000
+++ b/vehicle.h	Sat Jan 29 19:41:44 2005 +0000
@@ -47,6 +47,9 @@
 	byte overtaking_ctr;
 	uint16 crashed_ctr;
 	byte reverse_ctr;
+	struct RoadStop *slot;
+	byte slotindex;
+	byte slot_age;
 } VehicleRoad;
 
 typedef struct VehicleSpecial {