changeset 6643:18d58b36b9b3 draft

(svn r9874) -Feature: advanced vehicle lists a.k.a. group interface. Now you can make groups of vehicles and perform all kinds of tasks on that given group. Original code by nycom and graphics by skidd13.
author rubidium <rubidium@openttd.org>
date Sat, 19 May 2007 09:40:18 +0000
parents 1e9b34ce8000
children e329ee28c7ba
files bin/data/group.grf projects/openttd.vcproj projects/openttd_vs80.vcproj source.list src/aircraft_cmd.cpp src/autoreplace_cmd.cpp src/autoreplace_gui.cpp src/build_vehicle_gui.cpp src/command.cpp src/command.h src/economy.cpp src/engine.cpp src/engine.h src/gfxinit.cpp src/group.h src/group_cmd.cpp src/group_gui.cpp src/gui.h src/lang/english.txt src/misc.cpp src/openttd.cpp src/openttd.h src/player.h src/players.cpp src/saveload.cpp src/settings.cpp src/settings_gui.cpp src/strgen/strgen.cpp src/strings.cpp src/table/control_codes.h src/table/files.h src/table/sprites.h src/train_cmd.cpp src/variables.h src/vehicle.cpp src/vehicle.h src/vehicle_gui.cpp src/vehicle_gui.h src/window.h
diffstat 39 files changed, 1678 insertions(+), 67 deletions(-) [+]
line wrap: on
line diff
new file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..586a2bfd16890abe63094dc2d48e36be18b5caeb
GIT binary patch
literal 5704
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
IFc<#yZ#!X8)Bpeg
--- a/projects/openttd.vcproj
+++ b/projects/openttd.vcproj
@@ -453,6 +453,9 @@
 				RelativePath=".\..\src\gfxinit.h">
 			</File>
 			<File
+				RelativePath=".\..\src\group.h">
+			</File>
+			<File
 				RelativePath=".\..\src\gui.h">
 			</File>
 			<File
@@ -703,6 +706,9 @@
 				RelativePath=".\..\src\graph_gui.cpp">
 			</File>
 			<File
+				RelativePath=".\..\src\group_gui.cpp">
+			</File>
+			<File
 				RelativePath=".\..\src\industry_gui.cpp">
 			</File>
 			<File
@@ -791,6 +797,9 @@
 				RelativePath=".\..\src\dummy_land.cpp">
 			</File>
 			<File
+				RelativePath=".\..\src\group_cmd.cpp">
+			</File>
+			<File
 				RelativePath=".\..\src\industry_cmd.cpp">
 			</File>
 			<File
--- a/projects/openttd_vs80.vcproj
+++ b/projects/openttd_vs80.vcproj
@@ -832,6 +832,10 @@
 				>
 			</File>
 			<File
+				RelativePath=".\..\src\group.h"
+				>
+			</File>
+			<File
 				RelativePath=".\..\src\gui.h"
 				>
 			</File>
@@ -1164,6 +1168,10 @@
 				>
 			</File>
 			<File
+				RelativePath=".\..\src\group_gui.cpp"
+				>
+			</File>
+			<File
 				RelativePath=".\..\src\industry_gui.cpp"
 				>
 			</File>
@@ -1280,6 +1288,10 @@
 				>
 			</File>
 			<File
+				RelativePath=".\..\src\group_cmd.cpp"
+				>
+			</File>
+			<File
 				RelativePath=".\..\src\industry_cmd.cpp"
 				>
 			</File>
--- a/source.list
+++ b/source.list
@@ -118,6 +118,7 @@
 genworld.h
 gfx.h
 gfxinit.h
+group.h
 gui.h
 hal.h
 heightmap.h
@@ -202,6 +203,7 @@
 engine_gui.cpp
 genworld_gui.cpp
 graph_gui.cpp
+group_gui.cpp
 industry_gui.cpp
 intro_gui.cpp
 main_gui.cpp
@@ -232,6 +234,7 @@
 clear_cmd.cpp
 disaster_cmd.cpp
 dummy_land.cpp
+group_cmd.cpp
 industry_cmd.cpp
 misc_cmd.cpp
 order_cmd.cpp
--- a/src/aircraft_cmd.cpp
+++ b/src/aircraft_cmd.cpp
@@ -1684,7 +1684,7 @@
 	/* check if the aircraft needs to be replaced or renewed and send it to a hangar if needed
 	 * unless it is due for renewal but the engine is no longer available */
 	if (v->owner == _local_player && (
-				EngineHasReplacementForPlayer(p, v->engine_type) ||
+				EngineHasReplacementForPlayer(p, v->engine_type, v->group_id) ||
 				((p->engine_renew && v->age - v->max_age > p->engine_renew_months * 30) &&
 				HASBIT(GetEngine(v->engine_type)->player_avail, _local_player))
 			)) {
@@ -1742,7 +1742,7 @@
 	if (v->current_order.type != OT_GOTO_DEPOT && v->owner == _local_player) {
 		/* only the vehicle owner needs to calculate the rest (locally) */
 		const Player* p = GetPlayer(v->owner);
-		if (EngineHasReplacementForPlayer(p, v->engine_type) ||
+		if (EngineHasReplacementForPlayer(p, v->engine_type, v->group_id) ||
 			(p->engine_renew && v->age - v->max_age > (p->engine_renew_months * 30))) {
 			/* send the aircraft to the hangar at next airport */
 			_current_player = _local_player;
--- a/src/autoreplace_cmd.cpp
+++ b/src/autoreplace_cmd.cpp
@@ -16,6 +16,7 @@
 #include "train.h"
 #include "aircraft.h"
 #include "cargotype.h"
+#include "group.h"
 
 
 /*
@@ -136,8 +137,17 @@
 	char vehicle_name[32];
 	CargoID replacement_cargo_type;
 
-	new_engine_type = EngineReplacementForPlayer(p, old_v->engine_type);
-	if (new_engine_type == INVALID_ENGINE) new_engine_type = old_v->engine_type;
+	/* If the vehicle belongs to a group, check if the group is protected from the global autoreplace.
+	 *  If not, chek if an global auto replacement is defined */
+	new_engine_type = (IsValidGroupID(old_v->group_id) && GetGroup(old_v->group_id)->replace_protection) ?
+			INVALID_ENGINE :
+			EngineReplacementForPlayer(p, old_v->engine_type, DEFAULT_GROUP);
+
+	/* If we don't set new_egnine_type previously, we try to check if an autoreplacement was defined
+	 *  for the group and the engine_type of the vehicle */
+	if (new_engine_type == INVALID_ENGINE && !IsDefaultGroupID(old_v->group_id)) {
+		new_engine_type = EngineReplacementForPlayer(p, old_v->engine_type, old_v->group_id);
+	}
 
 	replacement_cargo_type = GetNewCargoTypeForReplace(old_v, new_engine_type);
 
@@ -165,6 +175,7 @@
 		new_v = GetVehicle(_new_vehicle_id);
 		*w = new_v; //we changed the vehicle, so MaybeReplaceVehicle needs to work on the new one. Now we tell it what the new one is
 
+		new_v->group_id = old_v->group_id;
 		/* refit if needed */
 		if (replacement_cargo_type != CT_NO_REFIT) {
 			if (CmdFailed(DoCommand(0, new_v->index, replacement_cargo_type, DC_EXEC, GetCmdRefitVeh(new_v)))) {
@@ -194,6 +205,7 @@
 			new_v->profit_this_year = old_v->profit_this_year;
 			new_v->profit_last_year = old_v->profit_last_year;
 			new_v->service_interval = old_v->service_interval;
+			new_v->group_id = old_v->group_id;
 			new_front = true;
 			new_v->unitnumber = old_v->unitnumber; // use the same unit number
 			new_v->dest_tile  = old_v->dest_tile;
@@ -211,6 +223,10 @@
 				if (temp_v != NULL) {
 					DoCommand(0, (new_v->index << 16) | temp_v->index, 1, DC_EXEC, CMD_MOVE_RAIL_VEHICLE);
 				}
+			} else if (!IsDefaultGroupID(old_v->group_id) && IsValidGroupID(old_v->group_id)) {
+				/* Increase the new num engines of the group for the ships, aircraft, and road vehicles
+					The old new num engine is decrease in the destroyvehicle function */
+				GetGroup(old_v->group_id)->num_engines[new_v->engine_type]++;
 			}
 		}
 		/* We are done setting up the new vehicle. Now we move the cargo from the old one to the new one */
@@ -305,8 +321,17 @@
 			if (!p->engine_renew ||
 					w->age - w->max_age < (p->engine_renew_months * 30) || // replace if engine is too old
 					w->max_age == 0) { // rail cars got a max age of 0
-				if (!EngineHasReplacementForPlayer(p, w->engine_type)) // updates to a new model
+				/* If the vehicle belongs to a group, check if the group is protected from the global autoreplace.
+				   If not, chek if an global auto remplacement is defined */
+				if (IsValidGroupID(w->group_id)) {
+					if (!EngineHasReplacementForPlayer(p, w->engine_type, w->group_id) && (
+							GetGroup(w->group_id)->replace_protection ||
+							!EngineHasReplacementForPlayer(p, w->engine_type, DEFAULT_GROUP))) {
+						continue;
+					}
+				} else if (!EngineHasReplacementForPlayer(p, w->engine_type, DEFAULT_GROUP)) {
 					continue;
+				}
 			}
 
 			/* Now replace the vehicle */
--- a/src/autoreplace_gui.cpp
+++ b/src/autoreplace_gui.cpp
@@ -14,6 +14,7 @@
 #include "variables.h"
 #include "vehicle_gui.h"
 #include "newgrf_engine.h"
+#include "group.h"
 
 
 static RailType _railtype_selected_in_replace_gui;
@@ -150,8 +151,11 @@
 		if (type == VEH_TRAIN && !GenerateReplaceRailList(e, draw_left, WP(w, replaceveh_d).wagon_btnstate)) continue; // special rules for trains
 
 		if (draw_left) {
+			const GroupID selected_group = WP(w, replaceveh_d).sel_group;
+			const uint num_engines = IsDefaultGroupID(selected_group) ? p->num_engines[e] : GetGroup(selected_group)->num_engines[e];
+
 			/* Skip drawing the engines we don't have any of and haven't set for replacement */
-			if (p->num_engines[e] == 0 && EngineReplacementForPlayer(GetPlayer(_local_player), e) == INVALID_ENGINE) continue;
+			if (num_engines == 0 && EngineReplacementForPlayer(GetPlayer(_local_player), e, selected_group) == INVALID_ENGINE) continue;
 		} else {
 			/* This is for engines we can replace to and they should depend on what we selected to replace from */
 			if (!IsEngineBuildable(e, type, _local_player)) continue; // we need to be able to build the engine
@@ -202,7 +206,7 @@
 }
 
 
-void DrawEngineList(VehicleType type, int x, int y, const EngineList eng_list, uint16 min, uint16 max, EngineID selected_id, bool show_count);
+void DrawEngineList(VehicleType type, int x, int y, const EngineList eng_list, uint16 min, uint16 max, EngineID selected_id, bool show_count, GroupID selected_group);
 
 static void ReplaceVehicleWndProc(Window *w, WindowEvent *e)
 {
@@ -231,6 +235,7 @@
 
 			Player *p = GetPlayer(_local_player);
 			EngineID selected_id[2];
+			const GroupID selected_group = WP(w,replaceveh_d).sel_group;
 
 			selected_id[0] = WP(w, replaceveh_d).sel_engine[0];
 			selected_id[1] = WP(w, replaceveh_d).sel_engine[1];
@@ -242,15 +247,15 @@
 			SetWindowWidgetDisabledState(w, 4,
 										 selected_id[0] == INVALID_ENGINE ||
 										 selected_id[1] == INVALID_ENGINE ||
-										 EngineReplacementForPlayer(p, selected_id[1]) != INVALID_ENGINE ||
-										 EngineReplacementForPlayer(p, selected_id[0]) == selected_id[1]);
+										 EngineReplacementForPlayer(p, selected_id[1], selected_group) != INVALID_ENGINE ||
+										 EngineReplacementForPlayer(p, selected_id[0], selected_group) == selected_id[1]);
 
 			/* Disable the "Stop Replacing" button if:
 			 *   The left list (existing vehicle) is empty
 			 *   or The selected vehicle has no replacement set up */
 			SetWindowWidgetDisabledState(w, 6,
 										 selected_id[0] == INVALID_ENGINE ||
-										 !EngineHasReplacementForPlayer(p, selected_id[0]));
+										 !EngineHasReplacementForPlayer(p, selected_id[0], selected_group));
 
 			/* now the actual drawing of the window itself takes place */
 			SetDParam(0, _vehicle_type_names[w->window_number]);
@@ -277,10 +282,10 @@
 
 			/* sets up the string for the vehicle that is being replaced to */
 			if (selected_id[0] != INVALID_ENGINE) {
-				if (!EngineHasReplacementForPlayer(p, selected_id[0])) {
+				if (!EngineHasReplacementForPlayer(p, selected_id[0], selected_group)) {
 					SetDParam(0, STR_NOT_REPLACING);
 				} else {
-					SetDParam(0, GetCustomEngineName(EngineReplacementForPlayer(p, selected_id[0])));
+					SetDParam(0, GetCustomEngineName(EngineReplacementForPlayer(p, selected_id[0], selected_group)));
 				}
 			} else {
 				SetDParam(0, STR_NOT_REPLACING_VEHICLE_SELECTED);
@@ -296,7 +301,7 @@
 				EngineID end    = min((i == 0 ? w->vscroll.cap : w->vscroll2.cap) + start, EngList_Count(&list));
 
 				/* Do the actual drawing */
-				DrawEngineList((VehicleType)w->window_number, x, 15, list, start, end, WP(w, replaceveh_d).sel_engine[i], i == 0);
+				DrawEngineList((VehicleType)w->window_number, x, 15, list, start, end, WP(w, replaceveh_d).sel_engine[i], i == 0, selected_group);
 
 				/* Also draw the details if an engine is selected */
 				if (WP(w, replaceveh_d).sel_engine[i] != INVALID_ENGINE) {
@@ -328,12 +333,12 @@
 				case 4: { /* Start replacing */
 					EngineID veh_from = WP(w, replaceveh_d).sel_engine[0];
 					EngineID veh_to = WP(w, replaceveh_d).sel_engine[1];
-					DoCommandP(0, 3, veh_from + (veh_to << 16), NULL, CMD_SET_AUTOREPLACE);
+					DoCommandP(0, 3 + (WP(w, replaceveh_d).sel_group << 16) , veh_from + (veh_to << 16), NULL, CMD_SET_AUTOREPLACE);
 				} break;
 
 				case 6: { /* Stop replacing */
 					EngineID veh_from = WP(w, replaceveh_d).sel_engine[0];
-					DoCommandP(0, 3, veh_from + (INVALID_ENGINE << 16), NULL, CMD_SET_AUTOREPLACE);
+					DoCommandP(0, 3 + (WP(w, replaceveh_d).sel_group << 16), veh_from + (INVALID_ENGINE << 16), NULL, CMD_SET_AUTOREPLACE);
 				} break;
 
 				case 7:
@@ -509,4 +514,37 @@
 
 	w->caption_color = _local_player;
 	w->vscroll2.cap = w->vscroll.cap;   // these two are always the same
+	WP(w, replaceveh_d).sel_group = DEFAULT_GROUP;
+ }
+
+void ShowReplaceGroupVehicleWindow(GroupID id_g, VehicleType vehicletype)
+{
+	Window *w;
+
+	DeleteWindowById(WC_REPLACE_VEHICLE, vehicletype);
+
+	switch (vehicletype) {
+		default: NOT_REACHED();
+		case VEH_TRAIN:
+			w = AllocateWindowDescFront(&_replace_rail_vehicle_desc, vehicletype);
+			w->vscroll.cap  = 8;
+			w->resize.step_height = 14;
+			WP(w, replaceveh_d).wagon_btnstate = true;
+			break;
+		case VEH_ROAD:
+			w = AllocateWindowDescFront(&_replace_road_vehicle_desc, vehicletype);
+			w->vscroll.cap  = 8;
+			w->resize.step_height = 14;
+			break;
+		case VEH_SHIP:
+		case VEH_AIRCRAFT:
+			w = AllocateWindowDescFront(&_replace_ship_aircraft_vehicle_desc, vehicletype);
+			w->vscroll.cap  = 4;
+			w->resize.step_height = 24;
+			break;
+	}
+
+	w->caption_color = _local_player;
+	WP(w, replaceveh_d).sel_group = id_g;
+	w->vscroll2.cap = w->vscroll.cap;   // these two are always the same
 }
--- a/src/build_vehicle_gui.cpp
+++ b/src/build_vehicle_gui.cpp
@@ -27,6 +27,7 @@
 #include "date.h"
 #include "strings.h"
 #include "cargotype.h"
+#include "group.h"
 
 
 enum BuildVehicleWidgets {
@@ -759,7 +760,7 @@
  * @param selected_id what engine to highlight as selected, if any
  * @param show_count Display the number of vehicles (used by autoreplace)
  */
-void DrawEngineList(VehicleType type, int x, int y, const EngineList eng_list, uint16 min, uint16 max, EngineID selected_id, bool show_count)
+void DrawEngineList(VehicleType type, int x, int y, const EngineList eng_list, uint16 min, uint16 max, EngineID selected_id, bool show_count, GroupID selected_group)
 {
 	byte step_size = GetVehicleListHeight(type);
 	byte x_offset = 0;
@@ -795,11 +796,12 @@
 
 	for (; min < max; min++, y += step_size) {
 		const EngineID engine = eng_list[min];
+		const uint num_engines = IsDefaultGroupID(selected_group) ? p->num_engines[engine] : GetGroup(selected_group)->num_engines[engine];
 
 		DrawString(x + x_offset, y, GetCustomEngineName(engine), engine == selected_id ? 0xC : 0x10);
-		DrawVehicleEngine(type, x, y + y_offset, engine, (show_count && p->num_engines[engine] == 0) ? PALETTE_CRASH : GetEnginePalette(engine, _local_player));
+		DrawVehicleEngine(type, x, y + y_offset, engine, (show_count && num_engines == 0) ? PALETTE_CRASH : GetEnginePalette(engine, _local_player));
 		if (show_count) {
-			SetDParam(0, p->num_engines[engine]);
+			SetDParam(0, num_engines);
 			DrawStringRightAligned(213, y + (GetVehicleListHeight(type) == 14 ? 3 : 8), STR_TINY_BLACK, 0);
 		}
 	}
@@ -833,7 +835,7 @@
 	SetDParam(0, bv->filter.railtype + STR_881C_NEW_RAIL_VEHICLES); // This should only affect rail vehicles
 	DrawWindowWidgets(w);
 
-	DrawEngineList(bv->vehicle_type, 2, 27, bv->eng_list, w->vscroll.pos, max, bv->sel_engine, false);
+	DrawEngineList(bv->vehicle_type, 2, 27, bv->eng_list, w->vscroll.pos, max, bv->sel_engine, false, DEFAULT_GROUP);
 
 	if (bv->sel_engine != INVALID_ENGINE) {
 		const Widget *wi = &w->widget[BUILD_VEHICLE_WIDGET_PANEL];
--- a/src/command.cpp
+++ b/src/command.cpp
@@ -168,6 +168,12 @@
 DEF_COMMAND(CmdDepotSellAllVehicles);
 DEF_COMMAND(CmdDepotMassAutoReplace);
 
+DEF_COMMAND(CmdCreateGroup);
+DEF_COMMAND(CmdRenameGroup);
+DEF_COMMAND(CmdDeleteGroup);
+DEF_COMMAND(CmdAddVehicleGroup);
+DEF_COMMAND(CmdAddSharedVehicleGroup);
+DEF_COMMAND(CmdRemoveAllVehiclesGroup);
 /* The master command table */
 static const Command _command_proc_table[] = {
 	{CmdBuildRailroadTrack,                  0}, /*   0 */
@@ -313,6 +319,12 @@
 	{CmdMassStartStopVehicle,                0}, /* 117 */
 	{CmdDepotSellAllVehicles,                0}, /* 118 */
 	{CmdDepotMassAutoReplace,                0}, /* 119 */
+	{CmdCreateGroup,                         0}, /* 120 */
+	{CmdDeleteGroup,                         0}, /* 121 */
+	{CmdRenameGroup,                         0}, /* 122 */
+	{CmdAddVehicleGroup,                     0}, /* 123 */
+	{CmdAddSharedVehicleGroup,               0}, /* 124 */
+	{CmdRemoveAllVehiclesGroup,              0}, /* 125 */
 };
 
 /* This function range-checks a cmd, and checks if the cmd is not NULL */
--- a/src/command.h
+++ b/src/command.h
@@ -143,6 +143,12 @@
 	CMD_MASS_START_STOP              = 117,
 	CMD_DEPOT_SELL_ALL_VEHICLES      = 118,
 	CMD_DEPOT_MASS_AUTOREPLACE       = 119,
+	CMD_CREATE_GROUP                 = 120,
+	CMD_DELETE_GROUP                 = 121,
+	CMD_RENAME_GROUP                 = 122,
+	CMD_ADD_VEHICLE_GROUP            = 123,
+	CMD_ADD_SHARED_VEHICLE_GROUP     = 124,
+	CMD_REMOVE_ALL_VEHICLES_GROUP    = 125,
 };
 
 enum {
--- a/src/economy.cpp
+++ b/src/economy.cpp
@@ -38,6 +38,7 @@
 #include "date.h"
 #include "cargotype.h"
 #include "player_face.h"
+#include "group.h"
 
 /* Score info */
 const ScoreInfo _score_info[] = {
@@ -359,6 +360,7 @@
 					DeleteVehicle(v);
 				} else {
 					v->owner = new_player;
+					v->group_id = DEFAULT_GROUP;
 					if (IsEngineCountable(v)) GetPlayer(new_player)->num_engines[v->engine_type]++;
 					switch (v->type) {
 						case VEH_TRAIN:    if (IsFrontEngine(v)) v->unitnumber = ++num_train; break;
--- a/src/engine.cpp
+++ b/src/engine.cpp
@@ -20,6 +20,7 @@
 #include "newgrf_cargo.h"
 #include "date.h"
 #include "table/engines.h"
+#include "group.h"
 
 EngineInfo _engine_info[TOTAL_NUM_ENGINES];
 RailVehicleInfo _rail_vehicle_info[NUM_TRAIN_ENGINES];
@@ -481,6 +482,7 @@
 
 		er->to = INVALID_ENGINE;
 		er->next = NULL;
+		er->group_id = DEFAULT_GROUP;
 		return er;
 	}
 
@@ -493,12 +495,12 @@
 /**
  * Retrieves the EngineRenew that specifies the replacement of the given
  * engine type from the given renewlist */
-static EngineRenew *GetEngineReplacement(EngineRenewList erl, EngineID engine)
+static EngineRenew *GetEngineReplacement(EngineRenewList erl, EngineID engine, GroupID group)
 {
 	EngineRenew *er = (EngineRenew *)erl;
 
 	while (er) {
-		if (er->from == engine) return er;
+		if (er->from == engine && er->group_id == group) return er;
 		er = er->next;
 	}
 	return NULL;
@@ -517,18 +519,18 @@
 	*erl = NULL; // Empty list
 }
 
-EngineID EngineReplacement(EngineRenewList erl, EngineID engine)
+EngineID EngineReplacement(EngineRenewList erl, EngineID engine, GroupID group)
 {
-	const EngineRenew *er = GetEngineReplacement(erl, engine);
+	const EngineRenew *er = GetEngineReplacement(erl, engine, group);
 	return er == NULL ? INVALID_ENGINE : er->to;
 }
 
-int32 AddEngineReplacement(EngineRenewList *erl, EngineID old_engine, EngineID new_engine, uint32 flags)
+int32 AddEngineReplacement(EngineRenewList *erl, EngineID old_engine, EngineID new_engine, GroupID group, uint32 flags)
 {
 	EngineRenew *er;
 
 	/* Check if the old vehicle is already in the list */
-	er = GetEngineReplacement(*erl, old_engine);
+	er = GetEngineReplacement(*erl, old_engine, group);
 	if (er != NULL) {
 		if (flags & DC_EXEC) er->to = new_engine;
 		return 0;
@@ -540,6 +542,7 @@
 	if (flags & DC_EXEC) {
 		er->from = old_engine;
 		er->to = new_engine;
+		er->group_id = group;
 
 		/* Insert before the first element */
 		er->next = (EngineRenew *)(*erl);
@@ -549,14 +552,14 @@
 	return 0;
 }
 
-int32 RemoveEngineReplacement(EngineRenewList *erl, EngineID engine, uint32 flags)
+int32 RemoveEngineReplacement(EngineRenewList *erl, EngineID engine, GroupID group, uint32 flags)
 {
 	EngineRenew *er = (EngineRenew *)(*erl);
 	EngineRenew *prev = NULL;
 
 	while (er)
 	{
-		if (er->from == engine) {
+		if (er->from == engine && er->group_id == group) {
 			if (flags & DC_EXEC) {
 				if (prev == NULL) { // First element
 					/* The second becomes the new first element */
@@ -577,11 +580,11 @@
 }
 
 static const SaveLoad _engine_renew_desc[] = {
-	SLE_VAR(EngineRenew, from, SLE_UINT16),
-	SLE_VAR(EngineRenew, to,   SLE_UINT16),
+	    SLE_VAR(EngineRenew, from,     SLE_UINT16),
+	    SLE_VAR(EngineRenew, to,       SLE_UINT16),
 
-	SLE_REF(EngineRenew, next, REF_ENGINE_RENEWS),
-
+	    SLE_REF(EngineRenew, next,     REF_ENGINE_RENEWS),
+	SLE_CONDVAR(EngineRenew, group_id, SLE_UINT16, 60, SL_MAX_VERSION),
 	SLE_END()
 };
 
@@ -607,6 +610,9 @@
 
 		er = GetEngineRenew(index);
 		SlObject(er, _engine_renew_desc);
+
+		/* Advanced vehicle lists got added */
+		if (CheckSavegameVersion(60)) er->group_id = DEFAULT_GROUP;
 	}
 }
 
--- a/src/engine.h
+++ b/src/engine.h
@@ -272,6 +272,7 @@
 	EngineID from;
 	EngineID to;
 	EngineRenew *next;
+	GroupID group_id;
 };
 
 /**
@@ -317,7 +318,7 @@
  * @return The engine type to replace with, or INVALID_ENGINE if no
  * replacement is in the list.
  */
-EngineID EngineReplacement(EngineRenewList erl, EngineID engine);
+EngineID EngineReplacement(EngineRenewList erl, EngineID engine, GroupID group);
 
 /**
  * Add an engine replacement to the given renewlist.
@@ -327,7 +328,7 @@
  * @param flags The calling command flags.
  * @return 0 on success, CMD_ERROR on failure.
  */
-int32 AddEngineReplacement(EngineRenewList* erl, EngineID old_engine, EngineID new_engine, uint32 flags);
+int32 AddEngineReplacement(EngineRenewList* erl, EngineID old_engine, EngineID new_engine, GroupID group, uint32 flags);
 
 /**
  * Remove an engine replacement from a given renewlist.
@@ -336,7 +337,7 @@
  * @param flags The calling command flags.
  * @return 0 on success, CMD_ERROR on failure.
  */
-int32 RemoveEngineReplacement(EngineRenewList* erl, EngineID engine, uint32 flags);
+int32 RemoveEngineReplacement(EngineRenewList* erl, EngineID engine, GroupID group, uint32 flags);
 
 /** When an engine is made buildable or is removed from being buildable, add/remove it from the build/autoreplace lists
  * @param type The type of engine
--- a/src/gfxinit.cpp
+++ b/src/gfxinit.cpp
@@ -393,6 +393,9 @@
 	assert(load_index == SPR_ROADSTOP_BASE);
 	load_index += LoadGrfFile("roadstops.grf", load_index, i++);
 
+	assert(load_index == SPR_GROUP_BASE);
+	load_index += LoadGrfFile("group.grf", load_index, i++);
+
 	/* Initialize the unicode to sprite mapping table */
 	InitializeUnicodeGlyphMap();
 
new file mode 100644
--- /dev/null
+++ b/src/group.h
@@ -0,0 +1,97 @@
+/* $Id$ */
+
+/** @file group.h */
+
+#ifndef GROUP_H
+#define GROUP_H
+
+#include "oldpool.h"
+
+enum {
+	DEFAULT_GROUP = 0xFFFE,
+	INVALID_GROUP = 0xFFFF,
+};
+
+struct Group {
+	StringID string_id;                     ///< Group Name
+
+	uint16 num_vehicle;                     ///< Number of vehicles wich belong to the group
+	PlayerID owner;                         ///< Group Owner
+	GroupID index;                          ///< Array index
+	VehicleTypeByte vehicle_type;           ///< Vehicle type of the group
+
+	bool replace_protection;                ///< If set to true, the global autoreplace have no effect on the group
+	uint16 num_engines[TOTAL_NUM_ENGINES];  ///< Caches the number of engines of each type the player owns (no need to save this)
+};
+
+DECLARE_OLD_POOL(Group, Group, 5, 2047)
+
+
+static inline bool IsValidGroup(const Group *g)
+{
+	return g->string_id != STR_NULL;
+}
+
+static inline void DestroyGroup(Group *g)
+{
+	DeleteName(g->string_id);
+}
+
+static inline void DeleteGroup(Group *g)
+{
+	DestroyGroup(g);
+	g->string_id = STR_NULL;
+}
+
+static inline bool IsValidGroupID(GroupID index)
+{
+	return index < GetGroupPoolSize() && IsValidGroup(GetGroup(index));
+}
+
+static inline bool IsDefaultGroupID(GroupID index)
+{
+	return (index == DEFAULT_GROUP);
+}
+
+static inline StringID GetGroupName(GroupID index)
+{
+	if (!IsValidGroupID(index)) return STR_NULL;
+
+	return GetGroup(index)->string_id;
+}
+
+
+#define FOR_ALL_GROUPS_FROM(g, start) for (g = GetGroup(start); g != NULL; g = (g->index + 1U < GetGroupPoolSize()) ? GetGroup(g->index + 1) : NULL) if (IsValidGroup(g))
+#define FOR_ALL_GROUPS(g) FOR_ALL_GROUPS_FROM(g, 0)
+
+/**
+ * Get the current size of the GroupPool
+ */
+static inline uint GetGroupArraySize(void)
+{
+	const Group *g;
+	uint num = 0;
+
+	FOR_ALL_GROUPS(g) num++;
+
+	return num;
+}
+
+static inline void IncreaseGroupNumVehicle(GroupID id_g)
+{
+	if (IsValidGroupID(id_g)) GetGroup(id_g)->num_vehicle++;
+}
+
+static inline void DecreaseGroupNumVehicle(GroupID id_g)
+{
+	if (IsValidGroupID(id_g)) GetGroup(id_g)->num_vehicle--;
+}
+
+
+void InitializeGroup();
+void SetTrainGroupID(Vehicle *v, GroupID grp);
+void UpdateTrainGroupID(Vehicle *v);
+void RemoveVehicleFromGroup(const Vehicle *v);
+void RemoveAllGroupsForPlayer(const Player *p);
+
+#endif /* GROUP_H */
new file mode 100644
--- /dev/null
+++ b/src/group_cmd.cpp
@@ -0,0 +1,390 @@
+/* $Id$ */
+
+/** @file group_cmd.cpp Handling of the engine groups */
+
+#include "stdafx.h"
+#include "openttd.h"
+#include "functions.h"
+#include "player.h"
+#include "table/strings.h"
+#include "command.h"
+#include "vehicle.h"
+#include "saveload.h"
+#include "debug.h"
+#include "group.h"
+#include "train.h"
+#include "aircraft.h"
+#include "string.h"
+
+/**
+ * Update the num engines of a groupID. Decrease the old one and increase the new one
+ * @note called in SetTrainGroupID and UpdateTrainGroupID
+ * @param i     EngineID we have to update
+ * @param old_g index of the old group
+ * @param new_g index of the new group
+ */
+static inline void UpdateNumEngineGroup(EngineID i, GroupID old_g, GroupID new_g)
+{
+	if (old_g != new_g) {
+		/* Decrease the num engines of EngineID i of the old group if it's not the default one */
+		if (!IsDefaultGroupID(old_g) && IsValidGroupID(old_g)) GetGroup(old_g)->num_engines[i]--;
+
+		/* Increase the num engines of EngineID i of the new group if it's not the new one */
+		if (!IsDefaultGroupID(new_g) && IsValidGroupID(new_g)) GetGroup(new_g)->num_engines[i]++;
+	}
+}
+
+
+/**
+ * Called if a new block is added to the group-pool
+ */
+static void GroupPoolNewBlock(uint start_item)
+{
+	/* We don't use FOR_ALL here, because FOR_ALL skips invalid items.
+	 * TODO - This is just a temporary stage, this will be removed. */
+	for (Group *g = GetGroup(start_item); g != NULL; g = (g->index + 1U < GetGroupPoolSize()) ? GetGroup(g->index + 1) : NULL) g->index = start_item++;
+}
+
+DEFINE_OLD_POOL(Group, Group, GroupPoolNewBlock, NULL)
+
+static Group *AllocateGroup(void)
+{
+	/* We don't use FOR_ALL here, because FOR_ALL skips invalid items.
+	 * TODO - This is just a temporary stage, this will be removed. */
+	for (Group *g = GetGroup(0); g != NULL; g = (g->index + 1U < GetGroupPoolSize()) ? GetGroup(g->index + 1) : NULL) {
+		if (!IsValidGroup(g)) {
+			const GroupID index = g->index;
+
+			memset(g, 0, sizeof(*g));
+			g->index = index;
+
+			return g;
+		}
+	}
+
+	/* Check if we can add a block to the pool */
+	return (AddBlockToPool(&_Group_pool)) ? AllocateGroup() : NULL;
+}
+
+void InitializeGroup(void)
+{
+	CleanPool(&_Group_pool);
+	AddBlockToPool(&_Group_pool);
+}
+
+
+/**
+ * Add a vehicle to a group
+ * @param tile unused
+ * @param p1   vehicle type
+ * @param p2   unused
+ */
+int32 CmdCreateGroup(TileIndex tile, uint32 flags, uint32 p1, uint32 p2)
+{
+	VehicleType vt = (VehicleType)p1;
+	if (!IsPlayerBuildableVehicleType(vt)) return CMD_ERROR;
+
+	Group *g = AllocateGroup();
+	if (g == NULL) return CMD_ERROR;
+
+	if (flags & DC_EXEC) {
+		g->owner = _current_player;
+		g->string_id = STR_SV_GROUP_NAME;
+		g->replace_protection = false;
+		g->vehicle_type = vt;
+	}
+
+	return 0;
+}
+
+
+/**
+ * Add a vehicle to a group
+ * @param tile unused
+ * @param p1   index of array group
+ *      - p1 bit 0-15 : GroupID
+ * @param p2   unused
+ */
+int32 CmdDeleteGroup(TileIndex tile, uint32 flags, uint32 p1, uint32 p2)
+{
+	if (!IsValidGroupID(p1)) return CMD_ERROR;
+
+	Group *g = GetGroup(p1);
+	if (g->owner != _current_player) return CMD_ERROR;
+
+	if (flags & DC_EXEC) {
+		Vehicle *v;
+
+		/* Add all vehicles belong to the group to the default group */
+		FOR_ALL_VEHICLES(v) {
+			if (v->group_id == g->index && v->type == g->vehicle_type) v->group_id = DEFAULT_GROUP;
+		}
+
+		/* If we set an autoreplace for the group we delete, remove it. */
+		if (_current_player < MAX_PLAYERS) {
+			Player *p;
+			EngineRenew *er;
+
+			p = GetPlayer(_current_player);
+			FOR_ALL_ENGINE_RENEWS(er) {
+				if (er->group_id == g->index) RemoveEngineReplacementForPlayer(p, er->from, g->index, flags);
+			}
+		}
+
+		/* Delete the Replace Vehicle Windows */
+		DeleteWindowById(WC_REPLACE_VEHICLE, g->vehicle_type);
+		DeleteGroup(g);
+	}
+
+	return 0;
+}
+
+
+/**
+ * Rename a group
+ * @param tile unused
+ * @param p1   index of array group
+ *   - p1 bit 0-15 : GroupID
+ * @param p2   unused
+ */
+int32 CmdRenameGroup(TileIndex tile, uint32 flags, uint32 p1, uint32 p2)
+{
+	if (!IsValidGroupID(p1) || StrEmpty(_cmd_text)) return CMD_ERROR;
+
+	/* Create the name */
+	StringID str = AllocateName(_cmd_text, 0);
+	if (str == STR_NULL) return CMD_ERROR;
+
+	if (flags & DC_EXEC) {
+		Group *g = GetGroup(p1);
+
+		/* Delete the old name */
+		DeleteName(g->string_id);
+		/* Assign the new one */
+		g->string_id = str;
+		g->owner = _current_player;
+	}
+
+	return 0;
+}
+
+
+/**
+ * Add a vehicle to a group
+ * @param tile unused
+ * @param p1   index of array group
+ *   - p1 bit 0-15 : GroupID
+ * @param p2   vehicle to add to a group
+ *   - p2 bit 0-15 : VehicleID
+ */
+int32 CmdAddVehicleGroup(TileIndex tile, uint32 flags, uint32 p1, uint32 p2)
+{
+	GroupID new_g = p1;
+
+	if (!IsValidVehicleID(p2) || !IsValidGroupID(new_g)) return CMD_ERROR;
+
+	Vehicle *v = GetVehicle(p2);
+	if (v->owner != _current_player || (v->type == VEH_TRAIN && !IsFrontEngine(v))) return CMD_ERROR;
+
+	if (flags & DC_EXEC) {
+		DecreaseGroupNumVehicle(v->group_id);
+		IncreaseGroupNumVehicle(new_g);
+
+		switch (v->type) {
+			default: NOT_REACHED();
+			case VEH_TRAIN:
+				SetTrainGroupID(v, new_g);
+				break;
+			case VEH_ROAD:
+			case VEH_SHIP:
+			case VEH_AIRCRAFT:
+				if (IsEngineCountable(v)) UpdateNumEngineGroup(v->engine_type, v->group_id, new_g);
+				v->group_id = new_g;
+				break;
+		}
+
+		/* Update the Replace Vehicle Windows */
+		InvalidateWindow(WC_REPLACE_VEHICLE, v->type);
+	}
+
+	return 0;
+}
+
+/**
+ * Add all shared vehicles of all vehicles from a group
+ * @param tile unused
+ * @param p1   index of group array
+ *  - p1 bit 0-15 : GroupID
+ * @param p2   type of vehicles
+ */
+int32 CmdAddSharedVehicleGroup(TileIndex tile, uint32 flags, uint32 p1, uint32 p2)
+{
+	VehicleType type = (VehicleType)p2;
+	if (!IsValidGroupID(p1) || !IsPlayerBuildableVehicleType(type)) return CMD_ERROR;
+
+	if (flags & DC_EXEC) {
+		Vehicle *v;
+		VehicleType type = (VehicleType)p2;
+		GroupID id_g = p1;
+		uint subtype = (type == VEH_AIRCRAFT) ? AIR_AIRCRAFT : 0;
+
+		/* Find the first front engine which belong to the group id_g
+		 * then add all shared vehicles of this front engine to the group id_g */
+		FOR_ALL_VEHICLES(v) {
+			if ((v->type == type) && (
+					(type == VEH_TRAIN && IsFrontEngine(v)) ||
+					(type != VEH_TRAIN && v->subtype <= subtype))) {
+				if (v->group_id != id_g) continue;
+
+				/* For each shared vehicles add it to the group */
+				for (Vehicle *v2 = GetFirstVehicleFromSharedList(v); v2 != NULL; v2 = v2->next_shared) {
+					if (v2->group_id != id_g) CmdAddVehicleGroup(tile, flags, id_g, v2->index);
+				}
+			}
+		}
+	}
+
+	return 0;
+}
+
+
+/**
+ * Remove all vehicles from a group
+ * @param tile unused
+ * @param p1   index of group array
+ * - p1 bit 0-15 : GroupID
+ * @param p2   type of vehicles
+ */
+int32 CmdRemoveAllVehiclesGroup(TileIndex tile, uint32 flags, uint32 p1, uint32 p2)
+{
+	VehicleType type = (VehicleType)p2;
+	if (!IsValidGroupID(p1) || !IsPlayerBuildableVehicleType(type)) return CMD_ERROR;
+
+	if (flags & DC_EXEC) {
+		GroupID old_g = p1;
+		uint subtype = (type == VEH_AIRCRAFT) ? AIR_AIRCRAFT : 0;
+		Vehicle *v;
+
+		/* Find each Vehicle that belongs to the group old_g and add it to the default group */
+		FOR_ALL_VEHICLES(v) {
+			if ((v->type == type) && (
+					(type == VEH_TRAIN && IsFrontEngine(v)) ||
+					(type != VEH_TRAIN && v->subtype <= subtype))) {
+				if (v->group_id != old_g) continue;
+
+				/* Add The Vehicle to the default group */
+				CmdAddVehicleGroup(tile, flags, DEFAULT_GROUP, v->index);
+			}
+		}
+	}
+
+	return 0;
+}
+
+/**
+ * Decrease the num_vehicle variable before delete an front engine from a group
+ * @note Called in CmdSellRailWagon and DeleteLasWagon,
+ * @param v     FrontEngine of the train we want to remove.
+ */
+void RemoveVehicleFromGroup(const Vehicle *v)
+{
+	if (!IsValidVehicle(v) || v->type != VEH_TRAIN || !IsFrontEngine(v)) return;
+
+	if (!IsDefaultGroupID(v->group_id)) DecreaseGroupNumVehicle(v->group_id);
+}
+
+
+/**
+ * Affect the groupID of a train to new_g.
+ * @note called in CmdAddVehicleGroup and CmdMoveRailVehicle
+ * @param v     First vehicle of the chain.
+ * @param new_g index of array group
+ */
+void SetTrainGroupID(Vehicle *v, GroupID new_g)
+{
+	if (!IsValidGroupID(new_g) && !IsDefaultGroupID(new_g)) return;
+
+	assert(IsValidVehicle(v) && v->type == VEH_TRAIN && IsFrontEngine(v));
+
+	for (Vehicle *u = v; u != NULL; u = u->next) {
+		if (IsEngineCountable(u)) UpdateNumEngineGroup(u->engine_type, u->group_id, new_g);
+
+		u->group_id = new_g;
+	}
+
+	/* Update the Replace Vehicle Windows */
+	InvalidateWindow(WC_REPLACE_VEHICLE, VEH_TRAIN);
+}
+
+
+/**
+ * Recalculates the groupID of a train. Should be called each time a vehicle is added
+ * to/removed from the chain,.
+ * @note this needs to be called too for 'wagon chains' (in the depot, without an engine)
+ * @note Called in CmdBuildRailVehicle, CmdBuildRailWagon, CmdMoveRailVehicle, CmdSellRailWagon
+ * @param v First vehicle of the chain.
+ */
+void UpdateTrainGroupID(Vehicle *v)
+{
+	assert(IsValidVehicle(v) && v->type == VEH_TRAIN && (IsFrontEngine(v) || IsFreeWagon(v)));
+
+	GroupID new_g = IsFrontEngine(v) ? v->group_id : (GroupID)DEFAULT_GROUP;
+	for (Vehicle *u = v; u != NULL; u = u->next) {
+		if (IsEngineCountable(u)) UpdateNumEngineGroup(u->engine_type, u->group_id, new_g);
+
+		u->group_id = new_g;
+	}
+
+	/* Update the Replace Vehicle Windows */
+	InvalidateWindow(WC_REPLACE_VEHICLE, VEH_TRAIN);
+}
+
+
+void RemoveAllGroupsForPlayer(const Player *p)
+{
+	Group *g;
+
+	FOR_ALL_GROUPS(g) {
+		if (p->index == g->owner) DeleteGroup(g);
+	}
+}
+
+
+static const SaveLoad _group_desc[] = {
+  SLE_VAR(Group, string_id,          SLE_UINT16),
+  SLE_VAR(Group, num_vehicle,        SLE_UINT16),
+  SLE_VAR(Group, owner,              SLE_UINT8),
+  SLE_VAR(Group, vehicle_type,       SLE_UINT8),
+  SLE_VAR(Group, replace_protection, SLE_BOOL),
+  SLE_END()
+};
+
+
+static void Save_GROUP(void)
+{
+	Group *g;
+
+	FOR_ALL_GROUPS(g) {
+		SlSetArrayIndex(g->index);
+		SlObject(g, _group_desc);
+	}
+}
+
+
+static void Load_GROUP(void)
+{
+	int index;
+
+	while ((index = SlIterateArray()) != -1) {
+		if (!AddBlockIfNeeded(&_Group_pool, index)) {
+			error("Groups: failed loading savegame: too many groups");
+		}
+
+		Group *g = GetGroup(index);
+		SlObject(g, _group_desc);
+	}
+}
+
+extern const ChunkHandler _group_chunk_handlers[] = {
+	{ 'GRPS', Save_GROUP, Load_GROUP, CH_ARRAY | CH_LAST},
+};
new file mode 100644
--- /dev/null
+++ b/src/group_gui.cpp
@@ -0,0 +1,795 @@
+/* $Id$ */
+
+/** @file group_gui.cpp */
+
+#include "stdafx.h"
+#include "openttd.h"
+#include "functions.h"
+#include "table/strings.h"
+#include "table/sprites.h"
+#include "window.h"
+#include "gui.h"
+#include "gfx.h"
+#include "vehicle.h"
+#include "command.h"
+#include "engine.h"
+#include "vehicle_gui.h"
+#include "depot.h"
+#include "train.h"
+#include "date.h"
+#include "group.h"
+#include "helpers.hpp"
+#include "viewport.h"
+#include "strings.h"
+#include "debug.h"
+
+
+struct Sorting {
+	Listing aircraft;
+	Listing roadveh;
+	Listing ship;
+	Listing train;
+};
+
+static Sorting _sorting;
+
+
+static void BuildGroupList(grouplist_d* gl, PlayerID owner, VehicleType vehicle_type)
+{
+	const Group** list;
+	const Group *g;
+	uint n = 0;
+
+	if (!(gl->l.flags & VL_REBUILD)) return;
+
+	list = MallocT<const Group*>(GetGroupArraySize());
+	if (list == NULL) {
+		error("Could not allocate memory for the group-sorting-list");
+	}
+
+	FOR_ALL_GROUPS(g) {
+		if (g->owner == owner && g->vehicle_type == vehicle_type) list[n++] = g;
+	}
+
+	free((void*)gl->sort_list);
+	gl->sort_list = MallocT<const Group *>(n);
+	if (n != 0 && gl->sort_list == NULL) {
+		error("Could not allocate memory for the group-sorting-list");
+	}
+	gl->l.list_length = n;
+
+	for (uint i = 0; i < n; ++i) gl->sort_list[i] = list[i];
+	free((void*)list);
+
+	gl->l.flags &= ~VL_REBUILD;
+	gl->l.flags |= VL_RESORT;
+}
+
+
+static int CDECL GroupNameSorter(const void *a, const void *b)
+{
+	static const Group *last_group[2] = { NULL, NULL };
+	static char         last_name[2][64] = { "", "" };
+
+	const Group *ga = *(const Group**)a;
+	const Group *gb = *(const Group**)b;
+	int r;
+
+	if (ga != last_group[0]) {
+		last_group[0] = ga;
+		SetDParam(0, ga->index);
+		GetString(last_name[0], ga->string_id, lastof(last_name[0]));
+	}
+
+	if (gb != last_group[1]) {
+		last_group[1] = gb;
+		SetDParam(0, gb->index);
+		GetString(last_name[1], gb->string_id, lastof(last_name[1]));
+	}
+
+	r = strcmp(last_name[0], last_name[1]); // sort by name
+
+	if (r == 0) return ga->index - gb->index;
+
+	return r;
+}
+
+
+static void SortGroupList(grouplist_d *gl)
+{
+	if (!(gl->l.flags & VL_RESORT)) return;
+
+	qsort((void*)gl->sort_list, gl->l.list_length, sizeof(gl->sort_list[0]), GroupNameSorter);
+
+	gl->l.resort_timer = DAY_TICKS * PERIODIC_RESORT_DAYS;
+	gl->l.flags &= ~VL_RESORT;
+}
+
+
+enum GroupListWidgets {
+	GRP_WIDGET_CLOSEBOX = 0,
+	GRP_WIDGET_CAPTION,
+	GRP_WIDGET_STICKY,
+	GRP_WIDGET_EMPTY_TOP_LEFT,
+	GRP_WIDGET_ALL_VEHICLES,
+	GRP_WIDGET_LIST_GROUP,
+	GRP_WIDGET_LIST_GROUP_SCROLLBAR,
+	GRP_WIDGET_SORT_BY_ORDER,
+	GRP_WIDGET_SORT_BY_TEXT,
+	GRP_WIDGET_SORT_BY_DROPDOWN,
+	GRP_WIDGET_EMPTY_TOP_RIGHT,
+	GRP_WIDGET_LIST_VEHICLE,
+	GRP_WIDGET_LIST_VEHICLE_SCROLLBAR,
+	GRP_WIDGET_CREATE_GROUP,
+	GRP_WIDGET_DELETE_GROUP,
+	GRP_WIDGET_RENAME_GROUP,
+	GRP_WIDGET_EMPTY1,
+	GRP_WIDGET_REPLACE_PROTECTION,
+	GRP_WIDGET_EMPTY2,
+	GRP_WIDGET_AVAILABLE_VEHICLES,
+	GRP_WIDGET_MANAGE_VEHICLES,
+	GRP_WIDGET_MANAGE_VEHICLES_DROPDOWN,
+	GRP_WIDGET_STOP_ALL,
+	GRP_WIDGET_START_ALL,
+	GRP_WIDGET_EMPTY_BOTTOM_RIGHT,
+	GRP_WIDGET_RESIZE,
+};
+
+
+static const Widget _group_widgets[] = {
+{   WWT_CLOSEBOX,   RESIZE_NONE,    14,     0,    10,     0,    13, STR_00C5,             STR_018B_CLOSE_WINDOW},
+{    WWT_CAPTION,  RESIZE_RIGHT,    14,    11,   513,     0,    13, 0x0,                  STR_018C_WINDOW_TITLE_DRAG_THIS},
+{  WWT_STICKYBOX,     RESIZE_LR,    14,   514,   525,     0,    13, 0x0,                  STR_STICKY_BUTTON},
+{      WWT_PANEL,   RESIZE_NONE,    14,     0,   200,    14,    25, 0x0,                  STR_NULL},
+{      WWT_PANEL,   RESIZE_NONE,    14,     0,   200,    26,    39, 0x0,                  STR_NULL},
+{     WWT_MATRIX, RESIZE_BOTTOM,    14,     0,   188,    39,   220, 0x701,                STR_GROUPS_CLICK_ON_GROUP_FOR_TIP},
+{  WWT_SCROLLBAR, RESIZE_BOTTOM,    14,   189,   200,    26,   220, 0x0,                  STR_0190_SCROLL_BAR_SCROLLS_LIST},
+{ WWT_PUSHTXTBTN,   RESIZE_NONE,    14,   201,   281,    14,    25, STR_SORT_BY,          STR_SORT_ORDER_TIP},
+{      WWT_PANEL,   RESIZE_NONE,    14,   282,   435,    14,    25, 0x0,                  STR_SORT_CRITERIA_TIP},
+{    WWT_TEXTBTN,   RESIZE_NONE,    14,   436,   447,    14,    25, STR_0225,             STR_SORT_CRITERIA_TIP},
+{      WWT_PANEL,  RESIZE_RIGHT,    14,   448,   525,    14,    25, 0x0,                  STR_NULL},
+{     WWT_MATRIX,     RESIZE_RB,    14,   201,   513,    26,   233, 0x701,                STR_NULL},
+{ WWT_SCROLL2BAR,    RESIZE_LRB,    14,   514,   525,    26,   233, 0x0,                  STR_0190_SCROLL_BAR_SCROLLS_LIST},
+{ WWT_PUSHIMGBTN,     RESIZE_TB,    14,     0,    23,   221,   245, 0x0,                  STR_GROUP_CREATE_TIP},
+{ WWT_PUSHIMGBTN,     RESIZE_TB,    14,    24,    47,   221,   245, 0x0,                  STR_GROUP_DELETE_TIP},
+{ WWT_PUSHIMGBTN,     RESIZE_TB,    14,    48,    71,   221,   245, 0x0,                  STR_GROUP_RENAME_TIP},
+{      WWT_PANEL,     RESIZE_TB,    14,    72,   164,   221,   245, 0x0,                  STR_NULL},
+{ WWT_PUSHIMGBTN,     RESIZE_TB,    14,   165,   188,   221,   245, 0x0,                  STR_GROUP_REPLACE_PROTECTION_TIP},
+{      WWT_PANEL,     RESIZE_TB,    14,   189,   200,   221,   245, 0x0,                  STR_NULL},
+{ WWT_PUSHTXTBTN,     RESIZE_TB,    14,   201,   306,   234,   245, 0x0,                  STR_AVAILABLE_ENGINES_TIP},
+{    WWT_TEXTBTN,     RESIZE_TB,    14,   307,   411,   234,   245, STR_MANAGE_LIST,      STR_MANAGE_LIST_TIP},
+{    WWT_TEXTBTN,     RESIZE_TB,    14,   412,   423,   234,   245, STR_0225,             STR_MANAGE_LIST_TIP},
+{ WWT_PUSHIMGBTN,     RESIZE_TB,    14,   424,   435,   234,   245, SPR_FLAG_VEH_STOPPED, STR_MASS_STOP_LIST_TIP},
+{ WWT_PUSHIMGBTN,     RESIZE_TB,    14,   436,   447,   234,   245, SPR_FLAG_VEH_RUNNING, STR_MASS_START_LIST_TIP},
+{      WWT_PANEL,    RESIZE_RTB,    14,   448,   513,   234,   245, 0x0,                  STR_NULL},
+{  WWT_RESIZEBOX,   RESIZE_LRTB,    14,   514,   525,   234,   245, 0x0,                  STR_RESIZE_BUTTON},
+{   WIDGETS_END},
+};
+
+
+static void CreateVehicleGroupWindow(Window *w)
+{
+	const PlayerID owner = (PlayerID)GB(w->window_number, 0, 8);
+	groupveh_d *gv = &WP(w, groupveh_d);
+	grouplist_d *gl = &WP(w, groupveh_d).gl;
+
+	w->caption_color = owner;
+	w->hscroll.cap = 10 * 29;
+	w->resize.step_width = 1;
+
+	switch (gv->vehicle_type) {
+		default: NOT_REACHED();
+		case VEH_TRAIN:
+		case VEH_ROAD:
+			w->vscroll.cap = 14;
+			w->vscroll2.cap = 8;
+			w->resize.step_height = PLY_WND_PRC__SIZE_OF_ROW_SMALL;
+			break;
+		case VEH_SHIP:
+		case VEH_AIRCRAFT:
+			w->vscroll.cap = 10;
+			w->vscroll2.cap = 4;
+			w->resize.step_height = PLY_WND_PRC__SIZE_OF_ROW_BIG2;
+			break;
+	}
+
+	w->widget[GRP_WIDGET_LIST_GROUP].data = (w->vscroll.cap << 8) + 1;
+	w->widget[GRP_WIDGET_LIST_VEHICLE].data = (w->vscroll2.cap << 8) + 1;
+
+	switch (gv->vehicle_type) {
+		default: NOT_REACHED(); break;
+		case VEH_TRAIN:    gv->_sorting = &_sorting.train;    break;
+		case VEH_ROAD:     gv->_sorting = &_sorting.roadveh;  break;
+		case VEH_SHIP:     gv->_sorting = &_sorting.ship;     break;
+		case VEH_AIRCRAFT: gv->_sorting = &_sorting.aircraft; break;
+	}
+
+	gv->sort_list = NULL;
+	gv->vehicle_type = (VehicleType)GB(w->window_number, 11, 5);
+	gv->l.sort_type = gv->_sorting->criteria;
+	gv->l.flags = VL_REBUILD | (gv->_sorting->order ? VL_DESC : VL_NONE);
+	gv->l.resort_timer = DAY_TICKS * PERIODIC_RESORT_DAYS;	// Set up resort timer
+
+	gl->sort_list = NULL;
+	gl->l.flags = VL_REBUILD | VL_NONE;
+	gl->l.resort_timer = DAY_TICKS * PERIODIC_RESORT_DAYS;	// Set up resort timer
+
+	gv->group_sel = DEFAULT_GROUP;
+
+	switch (gv->vehicle_type) {
+		case VEH_TRAIN:
+			w->widget[GRP_WIDGET_LIST_VEHICLE].tooltips = STR_883D_TRAINS_CLICK_ON_TRAIN_FOR;
+			w->widget[GRP_WIDGET_AVAILABLE_VEHICLES].data = STR_AVAILABLE_TRAINS;
+
+			w->widget[GRP_WIDGET_CREATE_GROUP].data = SPR_GROUP_CREATE_TRAIN;
+			w->widget[GRP_WIDGET_RENAME_GROUP].data = SPR_GROUP_RENAME_TRAIN;
+			w->widget[GRP_WIDGET_DELETE_GROUP].data = SPR_GROUP_DELETE_TRAIN;
+			break;
+
+		case VEH_ROAD:
+			w->widget[GRP_WIDGET_LIST_VEHICLE].tooltips = STR_901A_ROAD_VEHICLES_CLICK_ON;
+			w->widget[GRP_WIDGET_AVAILABLE_VEHICLES].data = STR_AVAILABLE_ROAD_VEHICLES;
+
+			w->widget[GRP_WIDGET_CREATE_GROUP].data = SPR_GROUP_CREATE_ROADVEH;
+			w->widget[GRP_WIDGET_RENAME_GROUP].data = SPR_GROUP_RENAME_ROADVEH;
+			w->widget[GRP_WIDGET_DELETE_GROUP].data = SPR_GROUP_DELETE_ROADVEH;
+			break;
+
+		case VEH_SHIP:
+			w->widget[GRP_WIDGET_LIST_VEHICLE].tooltips = STR_9823_SHIPS_CLICK_ON_SHIP_FOR;
+			w->widget[GRP_WIDGET_AVAILABLE_VEHICLES].data = STR_AVAILABLE_SHIPS;
+
+			w->widget[GRP_WIDGET_CREATE_GROUP].data = SPR_GROUP_CREATE_SHIP;
+			w->widget[GRP_WIDGET_RENAME_GROUP].data = SPR_GROUP_RENAME_SHIP;
+			w->widget[GRP_WIDGET_DELETE_GROUP].data = SPR_GROUP_DELETE_SHIP;
+			break;
+
+		case VEH_AIRCRAFT:
+			w->widget[GRP_WIDGET_LIST_VEHICLE].tooltips = STR_A01F_AIRCRAFT_CLICK_ON_AIRCRAFT;
+			w->widget[GRP_WIDGET_AVAILABLE_VEHICLES].data = STR_AVAILABLE_AIRCRAFT;
+
+			w->widget[GRP_WIDGET_CREATE_GROUP].data = SPR_GROUP_CREATE_AIRCRAFT;
+			w->widget[GRP_WIDGET_RENAME_GROUP].data = SPR_GROUP_RENAME_AIRCRAFT;
+			w->widget[GRP_WIDGET_DELETE_GROUP].data = SPR_GROUP_DELETE_AIRCRAFT;
+			break;
+
+		default: NOT_REACHED();
+	}
+}
+
+/**
+ * bitmask for w->window_number
+ * 0-7   PlayerID (owner)
+ * 11-15 vehicle type
+ **/
+static void GroupWndProc(Window *w, WindowEvent *e)
+{
+	const PlayerID owner = (PlayerID)GB(w->window_number, 0, 8);
+	const Player *p = GetPlayer(owner);
+	groupveh_d *gv = &WP(w, groupveh_d);
+	grouplist_d *gl = &WP(w, groupveh_d).gl;
+
+	gv->vehicle_type = (VehicleType)GB(w->window_number, 11, 5);
+
+	switch(e->event) {
+		case WE_CREATE:
+			CreateVehicleGroupWindow(w);
+			break;
+
+		case WE_PAINT: {
+			int x = 203;
+			int y2 = PLY_WND_PRC__OFFSET_TOP_WIDGET;
+			int y1 = PLY_WND_PRC__OFFSET_TOP_WIDGET + 2;
+			int max;
+			int i;
+
+			/* If we select the default group, gv->list will contain all vehicles of the player
+			 * else gv->list will contain all vehicles which belong to the selected group */
+			BuildVehicleList(gv, owner, gv->group_sel, IsDefaultGroupID(gv->group_sel) ? VLW_STANDARD : VLW_GROUP_LIST);
+			SortVehicleList(gv);
+
+
+			BuildGroupList(gl, owner, gv->vehicle_type);
+			SortGroupList(gl);
+
+			SetVScrollCount(w, gl->l.list_length);
+			SetVScroll2Count(w, gv->l.list_length);
+
+			/* Disable all lists management button when the list is empty */
+			SetWindowWidgetsDisabledState(w, gv->l.list_length == 0,
+					GRP_WIDGET_STOP_ALL,
+					GRP_WIDGET_START_ALL,
+					GRP_WIDGET_MANAGE_VEHICLES,
+					GRP_WIDGET_MANAGE_VEHICLES_DROPDOWN,
+					WIDGET_LIST_END);
+
+			/* Disable the group specific function when we select the default group */
+			SetWindowWidgetsDisabledState(w, IsDefaultGroupID(gv->group_sel),
+					GRP_WIDGET_DELETE_GROUP,
+					GRP_WIDGET_RENAME_GROUP,
+					GRP_WIDGET_REPLACE_PROTECTION,
+					WIDGET_LIST_END);
+
+			/* If selected_group == DEFAULT_GROUP, draw the standard caption
+			   We list all vehicles */
+			if (IsDefaultGroupID(gv->group_sel)) {
+				SetDParam(0, p->name_1);
+				SetDParam(1, p->name_2);
+				SetDParam(2, gv->l.list_length);
+
+				switch (gv->vehicle_type) {
+					case VEH_TRAIN:
+						w->widget[GRP_WIDGET_CAPTION].data = STR_881B_TRAINS;
+						w->widget[GRP_WIDGET_REPLACE_PROTECTION].data = SPR_GROUP_REPLACE_OFF_TRAIN;
+						break;
+					case VEH_ROAD:
+						w->widget[GRP_WIDGET_CAPTION].data = STR_9001_ROAD_VEHICLES;
+						w->widget[GRP_WIDGET_REPLACE_PROTECTION].data = SPR_GROUP_REPLACE_OFF_ROADVEH;
+						break;
+					case VEH_SHIP:
+						w->widget[GRP_WIDGET_CAPTION].data = STR_9805_SHIPS;
+						w->widget[GRP_WIDGET_REPLACE_PROTECTION].data = SPR_GROUP_REPLACE_OFF_SHIP;
+						break;
+					case VEH_AIRCRAFT:
+						w->widget[GRP_WIDGET_CAPTION].data =  STR_A009_AIRCRAFT;
+						w->widget[GRP_WIDGET_REPLACE_PROTECTION].data = SPR_GROUP_REPLACE_OFF_AIRCRAFT;
+						break;
+					default: NOT_REACHED(); break;
+				}
+			} else {
+				const Group *g = GetGroup(gv->group_sel);
+
+				SetDParam(0, g->index);
+				SetDParam(1, g->num_vehicle);
+
+				switch (gv->vehicle_type) {
+					case VEH_TRAIN:
+						w->widget[GRP_WIDGET_CAPTION].data = STR_GROUP_TRAINS_CAPTION;
+						w->widget[GRP_WIDGET_REPLACE_PROTECTION].data = (g->replace_protection) ? SPR_GROUP_REPLACE_ON_TRAIN : SPR_GROUP_REPLACE_OFF_TRAIN;
+						break;
+					case VEH_ROAD:
+						w->widget[GRP_WIDGET_CAPTION].data = STR_GROUP_ROADVEH_CAPTION;
+						w->widget[GRP_WIDGET_REPLACE_PROTECTION].data = (g->replace_protection) ? SPR_GROUP_REPLACE_ON_ROADVEH : SPR_GROUP_REPLACE_OFF_ROADVEH;
+						break;
+					case VEH_SHIP:
+						w->widget[GRP_WIDGET_CAPTION].data = STR_GROUP_SHIPS_CAPTION;
+						w->widget[GRP_WIDGET_REPLACE_PROTECTION].data = (g->replace_protection) ? SPR_GROUP_REPLACE_ON_SHIP : SPR_GROUP_REPLACE_OFF_SHIP;
+						break;
+					case VEH_AIRCRAFT:
+						w->widget[GRP_WIDGET_CAPTION].data = STR_GROUP_AIRCRAFTS_CAPTION;
+						w->widget[GRP_WIDGET_REPLACE_PROTECTION].data = (g->replace_protection) ? SPR_GROUP_REPLACE_ON_AIRCRAFT : SPR_GROUP_REPLACE_OFF_AIRCRAFT;
+						break;
+					default: NOT_REACHED(); break;
+				}
+			}
+
+
+			DrawWindowWidgets(w);
+
+			/* Draw Matrix Group
+			 * The selected group is drawn in white */
+			StringID str;
+
+			switch (gv->vehicle_type) {
+				case VEH_TRAIN:    str = STR_GROUP_ALL_TRAINS;    break;
+				case VEH_ROAD:     str = STR_GROUP_ALL_ROADS;     break;
+				case VEH_SHIP:     str = STR_GROUP_ALL_SHIPS;     break;
+				case VEH_AIRCRAFT: str = STR_GROUP_ALL_AIRCRAFTS; break;
+				default: NOT_REACHED(); break;
+			}
+			DrawString(10, y1, str, IsDefaultGroupID(gv->group_sel) ? 12 : 16);
+
+			max = min(w->vscroll.pos + w->vscroll.cap, gl->l.list_length);
+			for (i = w->vscroll.pos ; i < max ; ++i) {
+				const Group *g = gl->sort_list[i];
+
+				assert(g->owner == owner);
+
+				y1 += PLY_WND_PRC__SIZE_OF_ROW_TINY;
+
+				/* draw the selected group in white, else we draw it in black */
+				SetDParam(0, g->index);
+				DrawString(10, y1, STR_SV_GROUP_NAME, (gv->group_sel == g->index) ? 12 : 16);
+
+				/* draw the number of vehicles of the group */
+				SetDParam(0, g->num_vehicle);
+				DrawStringRightAligned(187, y1 + 1, STR_GROUP_TINY_NUM, (gv->group_sel == g->index) ? 12 : 16);
+			}
+
+			/* Draw Matrix Vehicle according to the vehicle list built before */
+			DrawString(285, 15, _vehicle_sort_listing[gv->l.sort_type], 0x10);
+			DoDrawString(gv->l.flags & VL_DESC ? DOWNARROW : UPARROW, 269, 15, 0x10);
+
+			max = min(w->vscroll2.pos + w->vscroll2.cap, gv->l.list_length);
+			for (i = w->vscroll2.pos ; i < max ; ++i) {
+				const Vehicle* v = gv->sort_list[i];
+				StringID str;
+
+				assert(v->type == gv->vehicle_type && v->owner == owner);
+
+				DrawVehicleImage(v, x + 19, y2 + 6, w->hscroll.cap, 0, gv->vehicle_sel);
+				DrawVehicleProfitButton(v, x, y2 + 13);
+
+				if (IsVehicleInDepot(v)) {
+					str = STR_021F;
+				} else {
+					str = v->age > v->max_age - 366 ? STR_00E3 : STR_00E2;
+				}
+				SetDParam(0, v->unitnumber);
+				DrawString(x, y2 + 2, str, 0);
+
+				if (w->resize.step_height == PLY_WND_PRC__SIZE_OF_ROW_BIG2) DrawSmallOrderList(v, x + 138, y2);
+
+				if (v->profit_this_year < 0) {
+					str = v->profit_last_year < 0 ?
+							STR_PROFIT_BAD_THIS_YEAR_BAD_LAST_YEAR :
+							STR_PROFIT_BAD_THIS_YEAR_GOOD_LAST_YEAR;
+				} else {
+					str = v->profit_last_year < 0 ?
+							STR_PROFIT_GOOD_THIS_YEAR_BAD_LAST_YEAR :
+							STR_PROFIT_GOOD_THIS_YEAR_GOOD_LAST_YEAR;
+				}
+
+				SetDParam(0, v->profit_this_year);
+				SetDParam(1, v->profit_last_year);
+				DrawString(x + 19, y2 + w->resize.step_height - 8, str, 0);
+
+				if (IsValidGroupID(v->group_id)) {
+					SetDParam(0, v->group_id);
+					DrawString(x + 19, y2, STR_GROUP_TINY_NAME, 16);
+				}
+
+				y2 += w->resize.step_height;
+			}
+
+			break;
+		}
+
+		case WE_CLICK:
+			switch(e->we.click.widget) {
+				case GRP_WIDGET_SORT_BY_ORDER: // Flip sorting method ascending/descending
+					gv->l.flags ^= VL_DESC;
+					gv->l.flags |= VL_RESORT;
+
+					gv->_sorting->order = !!(gv->l.flags & VL_DESC);
+					SetWindowDirty(w);
+					break;
+
+				case GRP_WIDGET_SORT_BY_TEXT:
+				case GRP_WIDGET_SORT_BY_DROPDOWN: // Select sorting criteria dropdown menu
+					ShowDropDownMenu(w, _vehicle_sort_listing, gv->l.sort_type,  GRP_WIDGET_SORT_BY_DROPDOWN, 0, 0);
+					return;
+
+				case GRP_WIDGET_ALL_VEHICLES: // All vehicles button
+					if (!IsDefaultGroupID(gv->group_sel)) {
+						gv->group_sel = DEFAULT_GROUP;
+						gv->l.flags |= VL_REBUILD;
+						SetWindowDirty(w);
+					}
+					break;
+
+				case GRP_WIDGET_LIST_GROUP: { // Matrix Group
+					uint16 id_g = (e->we.click.pt.y - PLY_WND_PRC__OFFSET_TOP_WIDGET - 13) / PLY_WND_PRC__SIZE_OF_ROW_TINY;
+
+					if (id_g >= w->vscroll.cap) return;
+
+					id_g += w->vscroll.pos;
+
+					if (id_g >= gl->l.list_length) return;
+
+					gv->group_sel = gl->sort_list[id_g]->index;;
+
+					gv->l.flags |= VL_REBUILD;
+					SetWindowDirty(w);
+					break;
+				}
+
+				case GRP_WIDGET_LIST_VEHICLE: { // Matrix Vehicle
+					uint32 id_v = (e->we.click.pt.y - PLY_WND_PRC__OFFSET_TOP_WIDGET) / (int)w->resize.step_height;
+					const Vehicle *v;
+
+					if (id_v >= w->vscroll2.cap) return; // click out of bounds
+
+					id_v += w->vscroll2.pos;
+
+					if (id_v >= gv->l.list_length) return; // click out of list bound
+
+					v = gv->sort_list[id_v];
+
+					gv->vehicle_sel = v->index;
+
+					if (IsValidVehicle(v)) {
+						CursorID image;
+
+						switch (gv->vehicle_type) {
+							case VEH_TRAIN:    image = GetTrainImage(v, DIR_W);    break;
+							case VEH_ROAD:     image = GetRoadVehImage(v, DIR_W);  break;
+							case VEH_SHIP:     image = GetShipImage(v, DIR_W);     break;
+							case VEH_AIRCRAFT: image = GetAircraftImage(v, DIR_W); break;
+							default: NOT_REACHED(); break;
+						}
+
+						SetObjectToPlaceWnd(image, GetVehiclePalette(v), 4, w);
+					}
+
+					SetWindowDirty(w);
+					break;
+				}
+
+				case GRP_WIDGET_CREATE_GROUP: // Create a new group
+					if (!CmdFailed(DoCommandP(0, gv->vehicle_type, 0, NULL, CMD_CREATE_GROUP | CMD_MSG(STR_GROUP_CAN_T_CREATE)))) {
+						SetWindowDirty(w);
+						gl->l.flags |= VL_REBUILD;
+					}
+					break;
+
+				case GRP_WIDGET_DELETE_GROUP: // Delete the selected group
+					if (!CmdFailed(DoCommandP(0, gv->group_sel, 0, NULL, CMD_DELETE_GROUP | CMD_MSG(STR_GROUP_CAN_T_DELETE)))) {
+						gv->group_sel = DEFAULT_GROUP;
+						gv->l.flags |= VL_REBUILD;
+						gl->l.flags |= VL_REBUILD;
+						SetWindowDirty(w);
+					}
+					break;
+
+				case GRP_WIDGET_RENAME_GROUP: { // Rename the selected roup
+					assert(!IsDefaultGroupID(gv->group_sel));
+
+					const Group *g = GetGroup(gv->group_sel);
+
+					SetDParam(0, g->index);
+					ShowQueryString(g->string_id, STR_GROUP_RENAME_CAPTION, 31, 150, w, CS_ALPHANUMERAL);
+				}	break;
+
+
+				case GRP_WIDGET_AVAILABLE_VEHICLES:
+					ShowBuildVehicleWindow(0, gv->vehicle_type);
+					break;
+
+				case GRP_WIDGET_MANAGE_VEHICLES:
+				case GRP_WIDGET_MANAGE_VEHICLES_DROPDOWN:  {
+					static StringID action_str[] = {
+						STR_REPLACE_VEHICLES,
+						STR_SEND_FOR_SERVICING,
+						STR_SEND_TRAIN_TO_DEPOT,
+						STR_NULL,
+						STR_NULL,
+						INVALID_STRING_ID
+					};
+
+					action_str[3] = IsDefaultGroupID(gv->group_sel) ? INVALID_STRING_ID : STR_GROUP_ADD_SHARED_VEHICLE;
+					action_str[4] = IsDefaultGroupID(gv->group_sel) ? INVALID_STRING_ID : STR_GROUP_REMOVE_ALL_VEHICLES;
+
+					ShowDropDownMenu(w, action_str, 0, GRP_WIDGET_MANAGE_VEHICLES_DROPDOWN, 0, 0);
+					break;
+				}
+
+
+				case GRP_WIDGET_START_ALL:
+				case GRP_WIDGET_STOP_ALL: { // Start/stop all vehicles of the list
+					DoCommandP(0, gv->group_sel, ((IsDefaultGroupID(gv->group_sel) ? VLW_STANDARD : VLW_GROUP_LIST) & VLW_MASK)
+														| (1 << 6)
+														| (e->we.click.widget == GRP_WIDGET_START_ALL ? (1 << 5) : 0)
+														| gv->vehicle_type, NULL, CMD_MASS_START_STOP);
+
+					break;
+				}
+
+				case GRP_WIDGET_REPLACE_PROTECTION:
+					if (!IsDefaultGroupID(gv->group_sel)) {
+						Group *g = GetGroup(gv->group_sel);
+
+						g->replace_protection = !g->replace_protection;
+					}
+					break;
+			}
+
+			break;
+
+		case WE_DRAGDROP: {
+			switch (e->we.click.widget) {
+				case GRP_WIDGET_ALL_VEHICLES: // All trains
+					if (!CmdFailed(DoCommandP(0, DEFAULT_GROUP , gv->vehicle_sel, NULL, CMD_ADD_VEHICLE_GROUP | CMD_MSG(STR_GROUP_CAN_T_ADD_VEHICLE)))) {
+						gv->l.flags |= VL_REBUILD;
+					}
+
+					gv->vehicle_sel = INVALID_VEHICLE;
+
+					SetWindowDirty(w);
+
+					break;
+
+				case GRP_WIDGET_LIST_GROUP: { // Maxtrix group
+					uint16 id_g = (e->we.click.pt.y - PLY_WND_PRC__OFFSET_TOP_WIDGET - 13) / PLY_WND_PRC__SIZE_OF_ROW_TINY;
+					const VehicleID vindex = gv->vehicle_sel;
+
+					gv->vehicle_sel = INVALID_VEHICLE;
+
+					SetWindowDirty(w);
+
+					if (id_g >= w->vscroll.cap) return;
+
+					id_g += w->vscroll.pos;
+
+					if (id_g >= gl->l.list_length) return;
+
+					if (!CmdFailed(DoCommandP(0,  gl->sort_list[id_g]->index , vindex, NULL, CMD_ADD_VEHICLE_GROUP | CMD_MSG(STR_GROUP_CAN_T_ADD_VEHICLE)))) {
+						gv->l.flags |= VL_REBUILD;
+					}
+
+					break;
+				}
+
+				case GRP_WIDGET_LIST_VEHICLE: { // Maxtrix vehicle
+					uint32 id_v = (e->we.click.pt.y - PLY_WND_PRC__OFFSET_TOP_WIDGET) / (int)w->resize.step_height;
+					const Vehicle *v;
+					const VehicleID vindex = gv->vehicle_sel;
+
+					gv->vehicle_sel = INVALID_VEHICLE;
+
+					SetWindowDirty(w);
+
+					if (id_v >= w->vscroll2.cap) return; // click out of bounds
+
+					id_v += w->vscroll2.pos;
+
+					if (id_v >= gv->l.list_length) return; // click out of list bound
+
+					v = gv->sort_list[id_v];
+
+					if (vindex == v->index) {
+						switch (gv->vehicle_type) {
+							default: NOT_REACHED(); break;
+							case VEH_TRAIN:    ShowTrainViewWindow(v);    break;
+							case VEH_ROAD:     ShowRoadVehViewWindow(v);  break;
+							case VEH_SHIP:     ShowShipViewWindow(v);     break;
+							case VEH_AIRCRAFT: ShowAircraftViewWindow(v); break;
+						}
+					}
+
+					break;
+				}
+			}
+			break;
+		}
+
+		case WE_ON_EDIT_TEXT:
+			if (!StrEmpty(e->we.edittext.str)) {
+				_cmd_text = e->we.edittext.str;
+
+				if (!CmdFailed(DoCommandP(0, gv->group_sel, 0, NULL, CMD_RENAME_GROUP | CMD_MSG(STR_GROUP_CAN_T_RENAME)))) {
+					SetWindowDirty(w);
+					gl->l.flags |= VL_REBUILD;
+				}
+			}
+			break;
+
+		case WE_RESIZE:
+			w->hscroll.cap += e->we.sizing.diff.x;
+			w->vscroll.cap += e->we.sizing.diff.y / PLY_WND_PRC__SIZE_OF_ROW_TINY;
+			w->vscroll2.cap += e->we.sizing.diff.y / (int)w->resize.step_height;
+
+			w->widget[GRP_WIDGET_LIST_GROUP].data = (w->vscroll.cap << 8) + 1;
+			w->widget[GRP_WIDGET_LIST_VEHICLE].data = (w->vscroll2.cap << 8) + 1;
+			break;
+
+
+		case WE_DROPDOWN_SELECT: // we have selected a dropdown item in the list
+			switch (e->we.dropdown.button) {
+				case GRP_WIDGET_SORT_BY_DROPDOWN:
+					if (gv->l.sort_type != e->we.dropdown.index) {
+						gv->l.flags |= VL_RESORT;
+						gv->l.sort_type = e->we.dropdown.index;
+						gv->_sorting->criteria = gv->l.sort_type;
+					}
+					break;
+
+				case GRP_WIDGET_MANAGE_VEHICLES_DROPDOWN:
+					assert(gv->l.list_length != 0);
+
+					switch (e->we.dropdown.index) {
+						case 0: // Replace window
+							ShowReplaceGroupVehicleWindow(gv->group_sel, gv->vehicle_type);
+							break;
+						case 1: // Send for servicing
+							DoCommandP(0, gv->group_sel, ((IsDefaultGroupID(gv->group_sel) ? VLW_STANDARD : VLW_GROUP_LIST) & VLW_MASK)
+										| DEPOT_MASS_SEND
+										| DEPOT_SERVICE, NULL, GetCmdSendToDepot(gv->vehicle_type));
+							break;
+						case 2: // Send to Depots
+							DoCommandP(0, gv->group_sel, ((IsDefaultGroupID(gv->group_sel) ? VLW_STANDARD : VLW_GROUP_LIST) & VLW_MASK)
+										| DEPOT_MASS_SEND, NULL, GetCmdSendToDepot(gv->vehicle_type));
+							break;
+						case 3: // Add shared Vehicles
+							assert(!IsDefaultGroupID(gv->group_sel));
+
+							if (!CmdFailed(DoCommandP(0, gv->group_sel, gv->vehicle_type, NULL, CMD_ADD_SHARED_VEHICLE_GROUP | CMD_MSG(STR_GROUP_CAN_T_ADD_SHARED_VEHICLE)))) {
+								gv->l.flags |= VL_REBUILD;
+							}
+							break;
+						case 4: // Remove all Vehicles from the selected group
+							assert(!IsDefaultGroupID(gv->group_sel));
+
+							if (!CmdFailed(DoCommandP(0, gv->group_sel, gv->vehicle_type, NULL, CMD_REMOVE_ALL_VEHICLES_GROUP | CMD_MSG(STR_GROUP_CAN_T_REMOVE_ALL_VEHICLES)))) {
+								gv->l.flags |= VL_REBUILD;
+							}
+							break;
+						default: NOT_REACHED();
+					}
+					break;
+
+				default: NOT_REACHED();
+			}
+
+			SetWindowDirty(w);
+			break;
+
+
+		case WE_DESTROY:
+			free((void*)gv->sort_list);
+			free((void*)gl->sort_list);
+			break;
+
+
+		case WE_TICK: // resort the lists every 20 seconds orso (10 days)
+			if (--gv->l.resort_timer == 0) {
+				gv->l.resort_timer = DAY_TICKS * PERIODIC_RESORT_DAYS;
+				gv->l.flags |= VL_RESORT;
+				SetWindowDirty(w);
+			}
+			if (--gl->l.resort_timer == 0) {
+				gl->l.resort_timer = DAY_TICKS * PERIODIC_RESORT_DAYS;
+				gl->l.flags |= VL_RESORT;
+				SetWindowDirty(w);
+			}
+			break;
+		}
+}
+
+
+static const WindowDesc _group_desc = {
+	WDP_AUTO, WDP_AUTO, 526, 246,
+	WC_TRAINS_LIST, WC_NONE,
+	WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET | WDF_UNCLICK_BUTTONS | WDF_STICKY_BUTTON | WDF_RESIZABLE,
+	_group_widgets,
+	GroupWndProc
+};
+
+void ShowPlayerGroup(PlayerID player, VehicleType vehicle_type)
+{
+	WindowClass wc;
+
+	switch (vehicle_type) {
+		default: NOT_REACHED();
+		case VEH_TRAIN:    wc = WC_TRAINS_LIST;   break;
+		case VEH_ROAD:     wc = WC_ROADVEH_LIST;  break;
+		case VEH_SHIP:     wc = WC_SHIPS_LIST;    break;
+		case VEH_AIRCRAFT: wc = WC_AIRCRAFT_LIST; break;
+	}
+
+	WindowNumber num = (vehicle_type << 11) | VLW_GROUP_LIST | player;
+	DeleteWindowById(wc, num);
+	Window *w = AllocateWindowDescFront(&_group_desc, num);
+	if (w == NULL) return;
+
+	w->window_class = wc;
+
+	switch (vehicle_type) {
+		default: NOT_REACHED();
+		case VEH_ROAD:
+			ResizeWindow(w, -66,   0);
+			/* FALL THROUGH */
+		case VEH_TRAIN:
+			w->resize.height = w->height - (PLY_WND_PRC__SIZE_OF_ROW_SMALL * 4); // Minimum of 4 vehicles
+			break;
+
+		case VEH_SHIP:
+		case VEH_AIRCRAFT:
+			ResizeWindow(w, -66, -52);
+			w->resize.height = w->height;  // Minimum of 4 vehicles
+			break;
+	}
+
+	/* Set the minimum window size to the current window size */
+	w->resize.width = w->width;
+}
--- a/src/gui.h
+++ b/src/gui.h
@@ -139,4 +139,6 @@
 /* vehicle_gui.cpp */
 void InitializeGUI();
 
+void ShowPlayerGroup(PlayerID player, VehicleType veh);
+
 #endif /* GUI_H */
--- a/src/lang/english.txt
+++ b/src/lang/english.txt
@@ -1098,6 +1098,7 @@
 STR_CONFIG_PATCHES_SCROLLWHEEL_OFF                              :Off
 STR_CONFIG_PATCHES_SCROLLWHEEL_MULTIPLIER                       :{LTBLUE}Map scrollwheel speed: {ORANGE}{STRING1}
 STR_CONFIG_PATCHES_PAUSE_ON_NEW_GAME                            :{LTBLUE}Automatically pause when starting a new game: {ORANGE}{STRING1}
+STR_CONFIG_PATCHES_ADVANCED_VEHICLE_LISTS                       :{LTBLUE}Use the advanced vehicle list: {ORANGE}{STRING1}
 
 STR_CONFIG_PATCHES_MAX_TRAINS                                   :{LTBLUE}Max trains per player: {ORANGE}{STRING1}
 STR_CONFIG_PATCHES_MAX_ROADVEH                                  :{LTBLUE}Max road vehicles per player: {ORANGE}{STRING1}
@@ -2012,6 +2013,8 @@
 STR_SV_STNAME_HELIPORT                                          :{STRING1} Heliport
 STR_SV_STNAME_FOREST                                            :{STRING1} Forest
 
+STR_SV_GROUP_NAME                                               :{GROUP}
+
 ############ end of savegame specific region!
 
 ##id 0x6800
@@ -3187,3 +3190,41 @@
 STR_TRANSPARENT_BUILDINGS_DESC                                  :{BLACK}Toggle transparency for buildables like stations, depots, waypoints and catenary
 STR_TRANSPARENT_BRIDGES_DESC                                    :{BLACK}Toggle transparency for bridges
 STR_TRANSPARENT_STRUCTURES_DESC                                 :{BLACK}Toggle transparency for structures like lighthouses and antennas, maybe in future for eyecandy
+
+##### Mass Order
+STR_GROUP_NAME_FORMAT                                           :Group {COMMA}
+STR_GROUP_TINY_NAME                                             :{TINYFONT}{GROUP}
+STR_GROUP_ALL_TRAINS                                            :All trains
+STR_GROUP_ALL_ROADS                                             :All road vehicles
+STR_GROUP_ALL_SHIPS                                             :All ships
+STR_GROUP_ALL_AIRCRAFTS                                         :All aircraft
+STR_GROUP_TINY_NUM                                              :{TINYFONT}{COMMA}
+STR_GROUP_ADD_SHARED_VEHICLE                                    :Add shared vehicles
+STR_GROUP_REMOVE_ALL_VEHICLES                                   :Remove all vehicles
+
+STR_GROUP_TRAINS_CAPTION                                        :{WHITE}{GROUP} - {COMMA} Train{P "" s}
+STR_GROUP_ROADVEH_CAPTION                                       :{WHITE}{GROUP} - {COMMA} Road Vehicle{P "" s}
+STR_GROUP_SHIPS_CAPTION                                         :{WHITE}{GROUP} - {COMMA} Ship{P "" s}
+STR_GROUP_AIRCRAFTS_CAPTION                                     :{WHITE}{GROUP} - {COMMA} Aircraft
+STR_GROUP_RENAME_CAPTION                                        :{BLACK}Rename a group
+STR_GROUP_REPLACE_CAPTION                                       :{WHITE}Replace Vehicles of "{GROUP}"
+
+STR_GROUP_CAN_T_CREATE                                          :{WHITE}Can't create group...
+STR_GROUP_CAN_T_DELETE                                          :{WHITE}Can't delete this group...
+STR_GROUP_CAN_T_RENAME                                          :{WHITE}Can't rename group...
+STR_GROUP_CAN_T_REMOVE_ALL_VEHICLES                             :{WHITE}Can't remove all vehicles from this group...
+STR_GROUP_CAN_T_ADD_VEHICLE                                     :{WHITE}Can't add the vehicle to this group...
+STR_GROUP_CAN_T_ADD_SHARED_VEHICLE                              :{WHITE}Can't add shared vehicles to group...
+
+STR_GROUPS_CLICK_ON_GROUP_FOR_TIP                               :{BLACK}Groups - Click on a group to list all vehicles of this group
+STR_GROUP_CREATE_TIP                                            :{BLACK}Click to create a group
+STR_GROUP_DELETE_TIP                                            :{BLACK}Delete the selected group
+STR_GROUP_RENAME_TIP                                            :{BLACK}Rename the selected group
+STR_GROUP_REPLACE_PROTECTION_TIP                                :{BLACK}Click to protect this group from global autoreplace
+
+STR_PROFIT_GOOD_THIS_YEAR_GOOD_LAST_YEAR                        :{TINYFONT}{BLACK}Profit this year: {GREEN}{CURRENCY} {BLACK}(last year: {GREEN}{CURRENCY}{BLACK})
+STR_PROFIT_BAD_THIS_YEAR_GOOD_LAST_YEAR                         :{TINYFONT}{BLACK}Profit this year: {RED}{CURRENCY} {BLACK}(last year: {GREEN}{CURRENCY}{BLACK})
+STR_PROFIT_GOOD_THIS_YEAR_BAD_LAST_YEAR                         :{TINYFONT}{BLACK}Profit this year: {GREEN}{CURRENCY} {BLACK}(last year: {RED}{CURRENCY}{BLACK})
+STR_PROFIT_BAD_THIS_YEAR_BAD_LAST_YEAR                          :{TINYFONT}{BLACK}Profit this year: {RED}{CURRENCY} {BLACK}(last year: {RED}{CURRENCY}{BLACK})
+
+########
--- a/src/misc.cpp
+++ b/src/misc.cpp
@@ -22,6 +22,7 @@
 #include "newgrf_house.h"
 #include "date.h"
 #include "cargotype.h"
+#include "group.h"
 
 char _name_array[512][32];
 
@@ -120,6 +121,7 @@
 	InitializeWaypoints();
 	InitializeDepots();
 	InitializeOrders();
+	InitializeGroup();
 
 	InitNewsItemStructs();
 	InitializeLandscape();
--- a/src/openttd.cpp
+++ b/src/openttd.cpp
@@ -63,6 +63,7 @@
 #include "newgrf_house.h"
 #include "newgrf_commons.h"
 #include "player_face.h"
+#include "group.h"
 
 #include "bridge_map.h"
 #include "clear_map.h"
@@ -294,6 +295,7 @@
 	CleanPool(&_Vehicle_pool);
 	CleanPool(&_Sign_pool);
 	CleanPool(&_Order_pool);
+	CleanPool(&_Group_pool);
 
 	free((void*)_town_sort);
 	free((void*)_industry_sort);
@@ -1954,6 +1956,20 @@
 		_opt.diff.number_towns++;
 	}
 
+	/* Recalculate */
+	Group *g;
+	FOR_ALL_GROUPS(g) {
+		const Vehicle *v;
+		FOR_ALL_VEHICLES(v) {
+			if (!IsEngineCountable(v)) continue;
+
+			if (v->group_id != g->index || v->type != g->vehicle_type || v->owner != g->owner) continue;
+
+			g->num_engines[v->engine_type]++;
+		}
+	}
+
+
 	return true;
 }
 
--- a/src/openttd.h
+++ b/src/openttd.h
@@ -40,6 +40,7 @@
 struct NewsItem;
 struct Industry;
 struct DrawPixelInfo;
+struct Group;
 typedef byte VehicleOrderID;  ///< The index of an order within its current vehicle (not pool related)
 typedef byte CargoID;
 typedef byte LandscapeID;
@@ -63,6 +64,7 @@
 typedef uint16 WaypointID;
 typedef uint16 OrderID;
 typedef uint16 SignID;
+typedef uint16 GroupID;
 typedef uint16 EngineRenewID;
 typedef uint16 DestinationID;
 
--- a/src/player.h
+++ b/src/player.h
@@ -311,7 +311,7 @@
  * @return The engine type to replace with, or INVALID_ENGINE if no
  * replacement is in the list.
  */
-static inline EngineID EngineReplacementForPlayer(const Player *p, EngineID engine) { return EngineReplacement(p->engine_renew_list, engine); }
+static inline EngineID EngineReplacementForPlayer(const Player *p, EngineID engine, GroupID group) { return EngineReplacement(p->engine_renew_list, engine, group); }
 
 /**
  * Check if a player has a replacement set up for the given engine.
@@ -319,7 +319,7 @@
  * @param  engine Engine type to be replaced.
  * @return true if a replacement was set up, false otherwise.
  */
-static inline bool EngineHasReplacementForPlayer(const Player *p, EngineID engine) { return EngineReplacementForPlayer(p, engine) != INVALID_ENGINE; }
+static inline bool EngineHasReplacementForPlayer(const Player *p, EngineID engine, GroupID group) { return EngineReplacementForPlayer(p, engine, group) != INVALID_ENGINE; }
 
 /**
  * Add an engine replacement for the player.
@@ -329,7 +329,7 @@
  * @param flags The calling command flags.
  * @return 0 on success, CMD_ERROR on failure.
  */
-static inline int32 AddEngineReplacementForPlayer(Player *p, EngineID old_engine, EngineID new_engine, uint32 flags) { return AddEngineReplacement(&p->engine_renew_list, old_engine, new_engine, flags); }
+static inline int32 AddEngineReplacementForPlayer(Player *p, EngineID old_engine, EngineID new_engine, GroupID group, uint32 flags) { return AddEngineReplacement(&p->engine_renew_list, old_engine, new_engine, group, flags); }
 
 /**
  * Remove an engine replacement for the player.
@@ -338,7 +338,7 @@
  * @param flags The calling command flags.
  * @return 0 on success, CMD_ERROR on failure.
  */
-static inline int32 RemoveEngineReplacementForPlayer(Player *p, EngineID engine, uint32 flags) {return RemoveEngineReplacement(&p->engine_renew_list, engine, flags); }
+static inline int32 RemoveEngineReplacementForPlayer(Player *p, EngineID engine, GroupID group, uint32 flags) {return RemoveEngineReplacement(&p->engine_renew_list, engine, group, flags); }
 
 /**
  * Reset the livery schemes to the player's primary colour.
--- a/src/players.cpp
+++ b/src/players.cpp
@@ -27,6 +27,7 @@
 #include "date.h"
 #include "window.h"
 #include "player_face.h"
+#include "group.h"
 
 /**
  * Sets the local player and updates the patch settings that are set on a
@@ -638,6 +639,7 @@
  * if p1 = 2, then
  * - p2 = minimum amount of money available
  * if p1 = 3, then:
+ * - p1 bits  8-15 = engine group
  * - p2 bits  0-15 = old engine type
  * - p2 bits 16-31 = new engine type
  * if p1 = 4, then:
@@ -693,8 +695,11 @@
 		case 3: {
 			EngineID old_engine_type = GB(p2, 0, 16);
 			EngineID new_engine_type = GB(p2, 16, 16);
+			GroupID id_g = GB(p1, 16, 8);
 			int32 cost;
 
+			if (!IsValidGroupID(id_g)) return CMD_ERROR;
+
 			if (new_engine_type != INVALID_ENGINE) {
 				/* First we make sure that it's a valid type the user requested
 				 * check that it's an engine that is in the engine array */
@@ -714,9 +719,9 @@
 				if (!HASBIT(GetEngine(new_engine_type)->player_avail, _current_player))
 					return CMD_ERROR;
 
-				cost = AddEngineReplacementForPlayer(p, old_engine_type, new_engine_type, flags);
+				cost = AddEngineReplacementForPlayer(p, old_engine_type, new_engine_type, id_g, flags);
 			} else {
-				cost = RemoveEngineReplacementForPlayer(p, old_engine_type, flags);
+				cost = RemoveEngineReplacementForPlayer(p, old_engine_type,id_g, flags);
 			}
 
 			if (IsLocalPlayer()) InvalidateAutoreplaceWindow(old_engine_type);
@@ -901,6 +906,7 @@
 			p->is_active = false;
 		}
 		RemoveAllEngineReplacementForPlayer(p);
+		RemoveAllGroupsForPlayer(p);
 
 	} break;
 
--- a/src/saveload.cpp
+++ b/src/saveload.cpp
@@ -29,7 +29,7 @@
 #include <setjmp.h>
 #include <list>
 
-extern const uint16 SAVEGAME_VERSION = 59;
+extern const uint16 SAVEGAME_VERSION = 60;
 uint16 _sl_version;       ///< the major savegame version identifier
 byte   _sl_minor_version; ///< the minor savegame version, DO NOT USE!
 
@@ -1257,6 +1257,7 @@
 extern const ChunkHandler _economy_chunk_handlers[];
 extern const ChunkHandler _animated_tile_chunk_handlers[];
 extern const ChunkHandler _newgrf_chunk_handlers[];
+extern const ChunkHandler _group_chunk_handlers[];
 
 static const ChunkHandler * const _chunk_handlers[] = {
 	_misc_chunk_handlers,
@@ -1274,6 +1275,7 @@
 	_player_chunk_handlers,
 	_animated_tile_chunk_handlers,
 	_newgrf_chunk_handlers,
+	_group_chunk_handlers,
 	NULL,
 };
 
--- a/src/settings.cpp
+++ b/src/settings.cpp
@@ -1342,6 +1342,7 @@
 	SDT_VAR(Patches, scrollwheel_scrolling,SLE_UINT8,S,MS, 0,  0,  2, 0, STR_CONFIG_PATCHES_SCROLLWHEEL_SCROLLING, NULL),
 	SDT_VAR(Patches,scrollwheel_multiplier,SLE_UINT8,S, 0, 5,  1, 15, 1, STR_CONFIG_PATCHES_SCROLLWHEEL_MULTIPLIER,NULL),
 	SDT_BOOL(Patches, pause_on_newgame,              S, 0, false,        STR_CONFIG_PATCHES_PAUSE_ON_NEW_GAME,     NULL),
+	SDT_BOOL(Patches, advanced_vehicle_list,         S, 0, true,        STR_CONFIG_PATCHES_ADVANCED_VEHICLE_LISTS,     NULL),
 
 	/***************************************************************************/
 	/* Construction section of the GUI-configure patches window */
--- a/src/settings_gui.cpp
+++ b/src/settings_gui.cpp
@@ -598,6 +598,7 @@
 	"scrollwheel_scrolling",
 	"scrollwheel_multiplier",
 	"pause_on_newgame",
+	"advanced_vehicle_list",
 };
 
 static const char *_patches_construction[] = {
--- a/src/strgen/strgen.cpp
+++ b/src/strgen/strgen.cpp
@@ -506,6 +506,7 @@
 	{"WAYPOINT", EmitSingleChar, SCC_WAYPOINT_NAME, 1, 0}, // waypoint name
 	{"STATION",  EmitSingleChar, SCC_STATION_NAME,  1, 0},
 	{"TOWN",     EmitSingleChar, SCC_TOWN_NAME,     1, 0},
+	{"GROUP",    EmitSingleChar, SCC_GROUP_NAME,    1, 0},
 
 	// 0x9D is used for the pseudo command SETCASE
 	// 0x9E is used for case switching
--- a/src/strings.cpp
+++ b/src/strings.cpp
@@ -25,6 +25,8 @@
 #include "industry.h"
 #include "helpers.hpp"
 #include "cargotype.h"
+#include "group.h"
+#include "debug.h"
 
 /* for opendir/readdir/closedir */
 # include "fios.h"
@@ -840,6 +842,18 @@
 				break;
 			}
 
+			case SCC_GROUP_NAME: { // {GROUP}
+				const Group *g = GetGroup(GetInt32(&argv));
+				int32 args[1];
+
+				assert(IsValidGroup(g));
+
+				args[0] = g->index;
+				buff = GetStringWithArgs(buff, (g->string_id == STR_SV_GROUP_NAME) ? (StringID)STR_GROUP_NAME_FORMAT : g->string_id, args, last);
+
+				break;
+			}
+
 			case SCC_CURRENCY_64: { // {CURRENCY64}
 				buff = FormatGenericCurrency(buff, _currency, GetInt64(&argv), false, last);
 				break;
--- a/src/table/control_codes.h
+++ b/src/table/control_codes.h
@@ -26,6 +26,7 @@
 	SCC_WAYPOINT_NAME,
 	SCC_STATION_NAME,
 	SCC_TOWN_NAME,
+	SCC_GROUP_NAME,
 
 	SCC_CURRENCY_COMPACT,
 	SCC_CURRENCY_COMPACT_64,
--- a/src/table/files.h
+++ b/src/table/files.h
@@ -62,4 +62,5 @@
 	{ "openttd.grf",   { 0x85, 0x4f, 0xf6, 0xb5, 0xd2, 0xf7, 0xbc, 0x1e, 0xb9, 0xdc, 0x44, 0xef, 0x35, 0x5f, 0x64, 0x9b } },
 	{ "trkfoundw.grf", { 0x12, 0x33, 0x3f, 0xa3, 0xd1, 0x86, 0x8b, 0x04, 0x53, 0x18, 0x9c, 0xee, 0xf9, 0x2d, 0xf5, 0x95 } },
 	{ "roadstops.grf", { 0x8c, 0xd9, 0x45, 0x21, 0x28, 0x82, 0x96, 0x45, 0x33, 0x22, 0x7a, 0xb9, 0x0d, 0xf3, 0x67, 0x4a } },
+	{ "group.grf",     { 0xe8, 0x52, 0x5f, 0x1c, 0x3e, 0xf9, 0x91, 0x9d, 0x0f, 0x70, 0x8c, 0x8a, 0x21, 0xa4, 0xc7, 0x02 } },
 };
--- a/src/table/sprites.h
+++ b/src/table/sprites.h
@@ -128,6 +128,28 @@
 	SPR_TRUCK_STOP_DT_X_W = SPR_ROADSTOP_BASE + 6,
 	SPR_TRUCK_STOP_DT_X_E = SPR_ROADSTOP_BASE + 7,
 
+	SPR_GROUP_BASE                 = SPR_ROADSTOP_BASE + 8, // The sprites used for the group interface
+	SPR_GROUP_CREATE_TRAIN         = SPR_GROUP_BASE,
+	SPR_GROUP_CREATE_ROADVEH       = SPR_GROUP_BASE + 1,
+	SPR_GROUP_CREATE_SHIP          = SPR_GROUP_BASE + 2,
+	SPR_GROUP_CREATE_AIRCRAFT      = SPR_GROUP_BASE + 3,
+	SPR_GROUP_DELETE_TRAIN         = SPR_GROUP_BASE + 4,
+	SPR_GROUP_DELETE_ROADVEH       = SPR_GROUP_BASE + 5,
+	SPR_GROUP_DELETE_SHIP          = SPR_GROUP_BASE + 6,
+	SPR_GROUP_DELETE_AIRCRAFT      = SPR_GROUP_BASE + 7,
+	SPR_GROUP_RENAME_TRAIN         = SPR_GROUP_BASE + 8,
+	SPR_GROUP_RENAME_ROADVEH       = SPR_GROUP_BASE + 9,
+	SPR_GROUP_RENAME_SHIP          = SPR_GROUP_BASE + 10,
+	SPR_GROUP_RENAME_AIRCRAFT      = SPR_GROUP_BASE + 11,
+	SPR_GROUP_REPLACE_ON_TRAIN     = SPR_GROUP_BASE + 12,
+	SPR_GROUP_REPLACE_ON_ROADVEH   = SPR_GROUP_BASE + 13,
+	SPR_GROUP_REPLACE_ON_SHIP      = SPR_GROUP_BASE + 14,
+	SPR_GROUP_REPLACE_ON_AIRCRAFT  = SPR_GROUP_BASE + 15,
+	SPR_GROUP_REPLACE_OFF_TRAIN    = SPR_GROUP_BASE + 16,
+	SPR_GROUP_REPLACE_OFF_ROADVEH  = SPR_GROUP_BASE + 17,
+	SPR_GROUP_REPLACE_OFF_SHIP     = SPR_GROUP_BASE + 18,
+	SPR_GROUP_REPLACE_OFF_AIRCRAFT = SPR_GROUP_BASE + 19,
+
 	/* Manager face sprites */
 	SPR_GRADIENT = 874, // background gradient behind manager face
 
--- a/src/train_cmd.cpp
+++ b/src/train_cmd.cpp
@@ -37,6 +37,7 @@
 #include "yapf/yapf.h"
 #include "date.h"
 #include "cargotype.h"
+#include "group.h"
 
 static bool TrainCheckIfLineEnds(Vehicle *v);
 static void TrainController(Vehicle *v, bool update_image);
@@ -637,12 +638,15 @@
 			v->cur_image = 0xAC2;
 			v->random_bits = VehicleRandomBits();
 
+			v->group_id = DEFAULT_GROUP;
+
 			AddArticulatedParts(vl);
 
 			_new_vehicle_id = v->index;
 
 			VehiclePositionChanged(v);
 			TrainConsistChanged(GetFirstVehicleInChain(v));
+			UpdateTrainGroupID(GetFirstVehicleInChain(v));
 
 			InvalidateWindow(WC_VEHICLE_DEPOT, v->tile);
 			if (IsLocalPlayer()) {
@@ -797,6 +801,8 @@
 			v->vehicle_flags = 0;
 			if (e->flags & ENGINE_EXCLUSIVE_PREVIEW) SETBIT(v->vehicle_flags, VF_BUILT_AS_PROTOTYPE);
 
+			v->group_id = DEFAULT_GROUP;
+
 			v->subtype = 0;
 			SetFrontEngine(v);
 			SetTrainEngine(v);
@@ -818,6 +824,7 @@
 
 			TrainConsistChanged(v);
 			UpdateTrainAcceleration(v);
+			UpdateTrainGroupID(v);
 
 			if (!HASBIT(p2, 1)) { // check if the cars should be added to the new vehicle
 				NormalizeTrainVehInDepot(v);
@@ -1113,6 +1120,16 @@
 		for (Vehicle *u = src_head; u != NULL; u = u->next) u->first = NULL;
 		for (Vehicle *u = dst_head; u != NULL; u = u->next) u->first = NULL;
 
+		/* If we move the front Engine and if the second vehicle is not an engine
+		   add the whole vehicle to the DEFAULT_GROUP */
+		if (IsFrontEngine(src) && !IsDefaultGroupID(src->group_id)) {
+			const Vehicle *v = GetNextVehicle(src);
+
+			if (v != NULL && !IsTrainEngine(v)) {
+				DoCommand(tile, DEFAULT_GROUP, v->index, flags, CMD_ADD_VEHICLE_GROUP);
+			}
+		}
+
 		if (HASBIT(p2, 0)) {
 			/* unlink ALL wagons */
 			if (src != src_head) {
@@ -1142,6 +1159,14 @@
 					SetFrontEngine(src);
 					assert(src->orders == NULL);
 					src->num_orders = 0;
+
+					// Decrease the engines number of the src engine_type
+					if (!IsDefaultGroupID(src->group_id) && IsValidGroupID(src->group_id)) {
+						GetGroup(src->group_id)->num_engines[src->engine_type]--;
+					}
+
+					// If we move an engine to a new line affect it to the DEFAULT_GROUP
+					src->group_id = DEFAULT_GROUP;
 				}
 			} else {
 				SetFreeWagon(src);
@@ -1203,13 +1228,18 @@
 		 * To do this, CmdMoveRailVehicle must be called once more
 		 * we can't loop forever here because next time we reach this line we will have a front engine */
 		if (src_head != NULL && !IsFrontEngine(src_head) && IsTrainEngine(src_head)) {
+			/* As in CmdMoveRailVehicle src_head->group_id will be equal to DEFAULT_GROUP
+			 * we need to save the group and reaffect it to src_head */
+			const GroupID tmp_g = src_head->group_id;
 			CmdMoveRailVehicle(0, flags, src_head->index | (INVALID_VEHICLE << 16), 1);
+			SetTrainGroupID(src_head, tmp_g);
 			src_head = NULL; // don't do anything more to this train since the new call will do it
 		}
 
 		if (src_head != NULL) {
 			NormaliseTrainConsist(src_head);
 			TrainConsistChanged(src_head);
+			UpdateTrainGroupID(src_head);
 			if (IsFrontEngine(src_head)) {
 				UpdateTrainAcceleration(src_head);
 				InvalidateWindow(WC_VEHICLE_DETAILS, src_head->index);
@@ -1224,6 +1254,7 @@
 		if (dst_head != NULL) {
 			NormaliseTrainConsist(dst_head);
 			TrainConsistChanged(dst_head);
+			UpdateTrainGroupID(dst_head);
 			if (IsFrontEngine(dst_head)) {
 				UpdateTrainAcceleration(dst_head);
 				InvalidateWindow(WC_VEHICLE_DETAILS, dst_head->index);
@@ -1364,6 +1395,8 @@
 					if (first->next_shared != NULL) {
 						first->next_shared->prev_shared = new_f;
 						new_f->next_shared = first->next_shared;
+					} else {
+						RemoveVehicleFromGroup(v);
 					}
 
 					/*
@@ -1394,6 +1427,7 @@
 				if (first != NULL) {
 					NormaliseTrainConsist(first);
 					TrainConsistChanged(first);
+					UpdateTrainGroupID(first);
 					if (IsFrontEngine(first)) {
 						InvalidateWindow(WC_VEHICLE_DETAILS, first->index);
 						InvalidateWindow(WC_VEHICLE_REFIT, first->index);
@@ -1447,6 +1481,7 @@
 					first = UnlinkWagon(v, first);
 					DeleteDepotHighlightOfVehicle(v);
 					DeleteVehicle(v);
+					RemoveVehicleFromGroup(v);
 				}
 			}
 
@@ -1454,6 +1489,7 @@
 			if (flags & DC_EXEC && first != NULL) {
 				NormaliseTrainConsist(first);
 				TrainConsistChanged(first);
+				UpdateTrainGroupID(first);
 				if (IsFrontEngine(first)) UpdateTrainAcceleration(first);
 				InvalidateWindow(WC_VEHICLE_DETAILS, first->index);
 				InvalidateWindow(WC_VEHICLE_REFIT, first->index);
@@ -3063,6 +3099,9 @@
 
 	BeginVehicleMove(v);
 	EndVehicleMove(v);
+
+	if (IsFrontEngine(v)) RemoveVehicleFromGroup(v);
+
 	DeleteVehicle(v);
 
 	if (v->u.rail.track != TRACK_BIT_DEPOT && v->u.rail.track != TRACK_BIT_WORMHOLE)
@@ -3148,6 +3187,7 @@
 
 	if (state >= 4440 && !(v->tick_counter&0x1F)) {
 		DeleteLastWagon(v);
+		InvalidateWindow(WC_REPLACE_VEHICLE, (v->group_id << 16) | VEH_TRAIN);
 	}
 }
 
--- a/src/variables.h
+++ b/src/variables.h
@@ -130,6 +130,7 @@
 	bool measure_tooltip;               // Show a permanent tooltip when dragging tools
 	byte liveries;                      // Options for displaying company liveries, 0=none, 1=self, 2=all
 	bool prefer_teamchat;               // Choose the chat message target with <ENTER>, true=all players, false=your team
+	bool advanced_vehicle_list;         // Use the "advanced" vehicle list
 
 	uint8 toolbar_pos;                  // position of toolbars, 0=left, 1=center, 2=right
 	uint8 window_snap_radius;           // Windows snap at each other if closer than this
--- a/src/vehicle.cpp
+++ b/src/vehicle.cpp
@@ -40,6 +40,7 @@
 #include "newgrf_engine.h"
 #include "newgrf_sound.h"
 #include "helpers.hpp"
+#include "group.h"
 #include "economy.h"
 
 #define INVALID_COORD (-0x8000)
@@ -111,7 +112,7 @@
 		return false; // Crashed vehicles don't need service anymore
 
 	if (_patches.no_servicing_if_no_breakdowns && _opt.diff.vehicle_breakdowns == 0) {
-		return EngineHasReplacementForPlayer(GetPlayer(v->owner), v->engine_type);  /* Vehicles set for autoreplacing needs to go to a depot even if breakdowns are turned off */
+		return EngineHasReplacementForPlayer(GetPlayer(v->owner), v->engine_type, v->group_id);  /* Vehicles set for autoreplacing needs to go to a depot even if breakdowns are turned off */
 	}
 
 	return _patches.servint_ispercent ?
@@ -284,6 +285,8 @@
 	v->prev_shared = NULL;
 	v->depot_list  = NULL;
 	v->random_bits = 0;
+	v->group_id = DEFAULT_GROUP;
+
 	return v;
 }
 
@@ -580,6 +583,8 @@
 	if (IsEngineCountable(v)) {
 		GetPlayer(v->owner)->num_engines[v->engine_type]--;
 		if (v->owner == _local_player) InvalidateAutoreplaceWindow(v->engine_type);
+
+		if (!IsDefaultGroupID(v->group_id) && IsValidGroupID(v->group_id)) GetGroup(v->group_id)->num_engines[v->engine_type]--;
 	}
 
 	DeleteVehicleNews(v->index, INVALID_STRING_ID);
@@ -1821,6 +1826,12 @@
 		_new_vehicle_id = w_front->index;
 	}
 
+	if (flags & DC_EXEC) {
+		/* Cloned vehicles belong to the same group */
+		DoCommand(0, v_front->group_id, w_front->index, flags, CMD_ADD_VEHICLE_GROUP);
+	}
+
+
 	/* Take care of refitting. */
 	w = w_front;
 	v = v_front;
@@ -1973,6 +1984,7 @@
       <li>VLW_SHARED_ORDERS: index of order to generate a list for<li>
       <li>VLW_STANDARD: not used<li>
       <li>VLW_DEPOT_LIST: TileIndex of the depot/hangar to make the list for</li>
+      <li>VLW_GROUP_LIST: index of group to generate a list for</li>
     </ul>
 * @param window_type tells what kind of window the list is for. Use the VLW flags in vehicle_gui.h
 * @return the number of vehicles added to the list
@@ -2051,6 +2063,19 @@
 			break;
 		}
 
+ 		case VLW_GROUP_LIST:
+			FOR_ALL_VEHICLES(v) {
+				if (v->type == type && (
+							(type == VEH_TRAIN && IsFrontEngine(v)) ||
+							(type != VEH_TRAIN && v->subtype <= subtype)
+						) && v->owner == owner && v->group_id == index) {
+					if (n == *length_of_array) ExtendVehicleListSize(sort_list, length_of_array, GetNumVehicles() / 4);
+
+					(*sort_list)[n++] = v;
+				}
+			}
+			break;
+
 		default: NOT_REACHED(); break;
 	}
 
@@ -2667,6 +2692,8 @@
 	    SLE_REF(Vehicle, next_shared,          REF_VEHICLE),
 	    SLE_REF(Vehicle, prev_shared,          REF_VEHICLE),
 
+	SLE_CONDVAR(Vehicle, group_id,             SLE_UINT16,                60, SL_MAX_VERSION),
+
 	/* reserve extra space in savegame here. (currently 10 bytes) */
 	SLE_CONDNULL(10,                                                       2, SL_MAX_VERSION),
 
@@ -2865,6 +2892,9 @@
 			v->current_order.flags = (v->current_order.type & 0xF0) >> 4;
 			v->current_order.type.m_val &= 0x0F;
 		}
+
+		/* Advanced vehicle lists got added */
+		if (CheckSavegameVersion(60)) v->group_id = DEFAULT_GROUP;
 	}
 
 	/* Check for shared order-lists (we now use pointers for that) */
--- a/src/vehicle.h
+++ b/src/vehicle.h
@@ -310,6 +310,8 @@
 	TileIndex cargo_loaded_at_xy;  ///< tile index where feeder cargo was loaded
 	uint32 value;
 
+	GroupID group_id;              ///< Index of group Pool array
+
 	union {
 		VehicleRail rail;
 		VehicleAir air;
--- a/src/vehicle_gui.cpp
+++ b/src/vehicle_gui.cpp
@@ -31,6 +31,7 @@
 #include "depot.h"
 #include "helpers.hpp"
 #include "cargotype.h"
+#include "group.h"
 
 struct Sorting {
 	Listing aircraft;
@@ -41,15 +42,6 @@
 
 static Sorting _sorting;
 
-struct vehiclelist_d {
-	const Vehicle** sort_list;  // List of vehicles (sorted)
-	Listing *_sorting;          // pointer to the appropiate subcategory of _sorting
-	uint16 length_of_sort_list; // Keeps track of how many vehicle pointers sort list got space for
-	VehicleType vehicle_type;   // The vehicle type that is sorted
-	list_d l;                   // General list struct
-};
-assert_compile(WINDOW_CUSTOM_SIZE >= sizeof(vehiclelist_d));
-
 static bool   _internal_sort_order;     // descending/ascending
 
 typedef int CDECL VehicleSortListingTypeFunction(const void*, const void*);
@@ -78,7 +70,7 @@
 	&VehicleValueSorter,
 };
 
-static const StringID _vehicle_sort_listing[] = {
+const StringID _vehicle_sort_listing[] = {
 	STR_SORT_BY_NUMBER,
 	STR_SORT_BY_DROPDOWN_NAME,
 	STR_SORT_BY_AGE,
@@ -134,7 +126,7 @@
 	}
 }
 
-static void BuildVehicleList(vehiclelist_d* vl, PlayerID owner, uint16 index, uint16 window_type)
+void BuildVehicleList(vehiclelist_d *vl, PlayerID owner, uint16 index, uint16 window_type)
 {
 	if (!(vl->l.flags & VL_REBUILD)) return;
 
@@ -146,7 +138,7 @@
 	vl->l.flags |= VL_RESORT;
 }
 
-static void SortVehicleList(vehiclelist_d *vl)
+void SortVehicleList(vehiclelist_d *vl)
 {
 	if (!(vl->l.flags & VL_RESORT)) return;
 
@@ -749,16 +741,6 @@
 	}
 }
 
-/*
- * Start of functions regarding vehicle list windows
- */
-
-enum {
-	PLY_WND_PRC__OFFSET_TOP_WIDGET = 26,
-	PLY_WND_PRC__SIZE_OF_ROW_SMALL = 26,
-	PLY_WND_PRC__SIZE_OF_ROW_BIG   = 36,
-};
-
 enum VehicleListWindowWidgets {
 	VLW_WIDGET_CLOSEBOX = 0,
 	VLW_WIDGET_CAPTION,
@@ -926,7 +908,7 @@
 	vl->l.resort_timer = DAY_TICKS * PERIODIC_RESORT_DAYS; // Set up resort timer
 }
 
-static void DrawSmallOrderList(const Vehicle *v, int x, int y)
+void DrawSmallOrderList(const Vehicle *v, int x, int y)
 {
 	const Order *order;
 	int sel, i = 0;
@@ -1275,7 +1257,11 @@
 
 void ShowVehicleListWindow(PlayerID player, VehicleType vehicle_type)
 {
-	ShowVehicleListWindowLocal(player, VLW_STANDARD, vehicle_type, 0);
+	if (player == _local_player && _patches.advanced_vehicle_list) {
+		ShowPlayerGroup(player, vehicle_type);
+	} else {
+		ShowVehicleListWindowLocal(player, VLW_STANDARD, vehicle_type, 0);
+	}
 }
 
 void ShowVehicleListWindow(const Vehicle *v)
--- a/src/vehicle_gui.h
+++ b/src/vehicle_gui.h
@@ -15,21 +15,35 @@
 /* sorter stuff */
 void RebuildVehicleLists();
 void ResortVehicleLists();
+void SortVehicleList(vehiclelist_d *vl);
+void BuildVehicleList(vehiclelist_d *vl, PlayerID owner, uint16 index, uint16 window_type);
 
 #define PERIODIC_RESORT_DAYS 10
 
+extern const StringID _vehicle_sort_listing[];
+
+/* Start of functions regarding vehicle list windows */
+enum {
+	PLY_WND_PRC__OFFSET_TOP_WIDGET = 26,
+	PLY_WND_PRC__SIZE_OF_ROW_TINY  = 13,
+	PLY_WND_PRC__SIZE_OF_ROW_SMALL = 26,
+	PLY_WND_PRC__SIZE_OF_ROW_BIG   = 36,
+	PLY_WND_PRC__SIZE_OF_ROW_BIG2  = 39,
+};
+
 /* Vehicle List Window type flags */
 enum {
 	VLW_STANDARD      = 0 << 8,
 	VLW_SHARED_ORDERS = 1 << 8,
 	VLW_STATION_LIST  = 2 << 8,
 	VLW_DEPOT_LIST    = 3 << 8,
+	VLW_GROUP_LIST    = 4 << 8,
 	VLW_MASK          = 0x700,
 };
 
 static inline bool ValidVLWFlags(uint16 flags)
 {
-	return (flags == VLW_STANDARD || flags == VLW_SHARED_ORDERS || flags == VLW_STATION_LIST || flags == VLW_DEPOT_LIST);
+	return (flags == VLW_STANDARD || flags == VLW_SHARED_ORDERS || flags == VLW_STATION_LIST || flags == VLW_DEPOT_LIST || flags == VLW_GROUP_LIST);
 }
 
 void PlayerVehWndProc(Window *w, WindowEvent *e);
@@ -54,6 +68,8 @@
 void ShowVehicleListWindow(PlayerID player, VehicleType vehicle_type, TileIndex depot_tile);
 
 void ShowReplaceVehicleWindow(VehicleType vehicletype);
+void DrawSmallOrderList(const Vehicle *v, int x, int y);
+void ShowReplaceGroupVehicleWindow(GroupID group, VehicleType veh);
 
 static inline void DrawVehicleImage(const Vehicle *v, int x, int y, int count, int skip, VehicleID selection)
 {
--- a/src/window.h
+++ b/src/window.h
@@ -348,6 +348,7 @@
 	bool update_left;
 	bool update_right;
 	bool init_lists;
+	GroupID sel_group;
 };
 assert_compile(WINDOW_CUSTOM_SIZE >= sizeof(replaceveh_d));
 
@@ -478,6 +479,28 @@
 };
 assert_compile(WINDOW_CUSTOM_SIZE >= sizeof(dropdown_d));
 
+struct vehiclelist_d {
+	const Vehicle** sort_list;  // List of vehicles (sorted)
+	Listing *_sorting;          // pointer to the appropiate subcategory of _sorting
+	uint16 length_of_sort_list; // Keeps track of how many vehicle pointers sort list got space for
+	VehicleType vehicle_type;   // The vehicle type that is sorted
+	list_d l;                   // General list struct
+};
+assert_compile(WINDOW_CUSTOM_SIZE >= sizeof(vehiclelist_d));
+
+struct grouplist_d {
+	const Group **sort_list;
+	list_d l;                   // General list struct
+};
+assert_compile(WINDOW_CUSTOM_SIZE >= sizeof(grouplist_d));
+
+struct groupveh_d : vehiclelist_d {
+	GroupID group_sel;
+	VehicleID vehicle_sel;
+
+	grouplist_d gl;
+};
+assert_compile(WINDOW_CUSTOM_SIZE >= sizeof(groupveh_d));
 
 /****************** THESE ARE NOT WIDGET TYPES!!!!! *******************/
 enum WindowWidgetBehaviours {