(svn r13614) [NoAI] -Add: AIGroup which allows an AI to manage its vehicle in the context of groups. noai
authorrubidium
Mon, 23 Jun 2008 12:46:38 +0000
branchnoai
changeset 11057 188a9ca6d8de
parent 11056 d91b79ffff9c
child 11058 3305a425f55b
(svn r13614) [NoAI] -Add: AIGroup which allows an AI to manage its vehicle in the context of groups.
bin/ai/regression/regression.nut
bin/ai/regression/regression.txt
projects/openttd_vs80.vcproj
projects/openttd_vs90.vcproj
source.list
src/ai/api/ai_controller.cpp
src/ai/api/ai_group.cpp
src/ai/api/ai_group.hpp
src/ai/api/ai_group.hpp.sq
src/ai/api/ai_object.cpp
src/ai/api/ai_object.hpp
src/ai/api/ai_types.hpp
src/ai/api/ai_vehicle.cpp
src/ai/api/ai_vehicle.hpp
src/ai/api/ai_vehicle.hpp.sq
src/group.h
src/group_cmd.cpp
--- a/bin/ai/regression/regression.nut	Mon Jun 23 12:33:38 2008 +0000
+++ b/bin/ai/regression/regression.nut	Mon Jun 23 12:46:38 2008 +0000
@@ -398,6 +398,29 @@
 	}
 }
 
+function Regression::Group()
+{
+	print ("");
+	print("--Group--");
+	print("  SetAutoReplace():         " + AIGroup.SetAutoReplace(AIGroup.ALL_GROUP, 116, 117));
+	print("  GetEngineReplacement():   " + AIGroup.GetEngineReplacement(AIGroup.ALL_GROUP, 116));
+	print("  GetNumEngines():          " + AIGroup.GetNumEngines(AIGroup.ALL_GROUP, 116));
+	print("  AIRoad.BuildRoadDepot():  " + AIRoad.BuildRoadDepot(10000, 10001));
+	local vehicle = AIVehicle.BuildVehicle(10000, 116);
+	print("  AIVehicle.BuildVehicle(): " + vehicle);
+	print("  GetNumEngines():          " + AIGroup.GetNumEngines(AIGroup.ALL_GROUP, 116));
+	local group = AIGroup.CreateGroup(AIVehicle.VEHICLE_ROAD);
+	print("  CreateGroup():            " + group);
+	print("  MoveVehicle():            " + AIGroup.MoveVehicle(group, vehicle));
+	print("  GetNumEngines():          " + AIGroup.GetNumEngines(group, 116));
+	print("  GetNumEngines():          " + AIGroup.GetNumEngines(AIGroup.ALL_GROUP, 116));
+	print("  GetNumEngines():          " + AIGroup.GetNumEngines(AIGroup.DEFAULT_GROUP, 116));
+	print("  GetName():                " + AIGroup.GetName(0));
+	print("  GetName():                " + AIGroup.GetName(1));
+	print("  AIVehicle.SellVehicle():  " + AIVehicle.SellVehicle(vehicle));
+	print("  AITile.DemolishTile():    " + AITile.DemolishTile(10000));
+}
+
 function Regression::Industry()
 {
 	local j = 0;
@@ -1387,6 +1410,7 @@
 	this.EngineList();
 	this.Event();
 	this.Graph();
+	this.Group();
 	this.Industry();
 	this.IndustryList();
 	this.Map();
--- a/bin/ai/regression/regression.txt	Mon Jun 23 12:33:38 2008 +0000
+++ b/bin/ai/regression/regression.txt	Mon Jun 23 12:46:38 2008 +0000
@@ -4831,6 +4831,23 @@
     Tile 2
     Tile 1
 
+--Group--
+  SetAutoReplace():         false
+  GetEngineReplacement():   65535
+  GetNumEngines():          0
+  AIRoad.BuildRoadDepot():  true
+  AIVehicle.BuildVehicle(): 11
+  GetNumEngines():          1
+  CreateGroup():            0
+  MoveVehicle():            true
+  GetNumEngines():          1
+  GetNumEngines():          1
+  GetNumEngines():          0
+  GetName():                Group 0
+  GetName():                (null : 0x00000000)
+  AIVehicle.SellVehicle():  true
+  AITile.DemolishTile():    true
+
 --Industry--
   GetMaxIndustryID():  71
   GetIndustryCount():  69
@@ -5797,7 +5814,7 @@
   IsBuoyTile():         false
   IsLockTile():         false
   IsCanalTile():        false
-  GetBankBalance():     665942
+  GetBankBalance():     680935
   BuildWaterDepot():    true
   BuildDock():          true
   BuildBuoy():          true
@@ -5810,7 +5827,7 @@
   IsBuoyTile():         true
   IsLockTile():         true
   IsCanalTile():        true
-  GetBankBalance():     701546
+  GetBankBalance():     716539
   RemoveWaterDepot():   true
   RemoveDock():         true
   RemoveBuoy():         true
@@ -5821,7 +5838,7 @@
   IsBuoyTile():         false
   IsLockTile():         false
   IsCanalTile():        false
-  GetBankBalance():     746546
+  GetBankBalance():     761539
   BuildWaterDepot():    true
   BuildDock():          true
 
@@ -7044,9 +7061,9 @@
     GetLocation():       33417
     GetEngineType():     153
     GetUnitNumber():     1
-    GetAge():            1
+    GetAge():            0
     GetMaxAge():         5490
-    GetAgeLeft():        5489
+    GetAgeLeft():        5490
     GetCurrentSpeed():   4
     GetRunningCost():    13
     GetProfitThisYear(): 0
@@ -7095,10 +7112,10 @@
     13 => 1
     11 => 1
   Age ListDump:
-    11 => 1
     15 => 0
     13 => 0
     12 => 0
+    11 => 0
   MaxAge ListDump:
     15 => 10980
     13 => 10980
@@ -7108,7 +7125,7 @@
     15 => 10980
     13 => 10980
     12 => 5490
-    11 => 5489
+    11 => 5490
   CurrentSpeed ListDump:
     11 => 7
     15 => 0
@@ -7221,9 +7238,9 @@
         GetAwardedTo():       -1
         GetExpireDate():      712619
         SourceIsTown():       true
-        GetSource():          25
+        GetSource():          16
         DestionationIsTown(): true
-        GetDestionation():    10
+        GetDestionation():    21
         GetCargoType():       0
   GetNextEvent:          instance
     GetEventType:        2
@@ -7232,11 +7249,24 @@
         IsValidSubsidy():     true
         IsAwarded():          false
         GetAwardedTo():       -1
-        GetExpireDate():      712708
+        GetExpireDate():      712647
         SourceIsTown():       true
-        GetSource():          20
+        GetSource():          27
         DestionationIsTown(): true
-        GetDestionation():    6
+        GetDestionation():    4
+        GetCargoType():       0
+  GetNextEvent:          instance
+    GetEventType:        2
+      EventName:         SubsidyOffer
+      --Subsidy (2) --
+        IsValidSubsidy():     true
+        IsAwarded():          false
+        GetAwardedTo():       -1
+        GetExpireDate():      712769
+        SourceIsTown():       true
+        GetSource():          26
+        DestionationIsTown(): true
+        GetDestionation():    15
         GetCargoType():       0
   IsEventWaiting:        false
 ERROR: We've got a suicidal AI for player 1
--- a/projects/openttd_vs80.vcproj	Mon Jun 23 12:33:38 2008 +0000
+++ b/projects/openttd_vs80.vcproj	Mon Jun 23 12:46:38 2008 +0000
@@ -2372,6 +2372,10 @@
 				>
 			</File>
 			<File
+				RelativePath=".\..\src\ai\api\ai_group.hpp"
+				>
+			</File>
+			<File
 				RelativePath=".\..\src\ai\api\ai_industry.hpp"
 				>
 			</File>
@@ -2552,6 +2556,10 @@
 				>
 			</File>
 			<File
+				RelativePath=".\..\src\ai\api\ai_group.cpp"
+				>
+			</File>
+			<File
 				RelativePath=".\..\src\ai\api\ai_industry.cpp"
 				>
 			</File>
--- a/projects/openttd_vs90.vcproj	Mon Jun 23 12:33:38 2008 +0000
+++ b/projects/openttd_vs90.vcproj	Mon Jun 23 12:46:38 2008 +0000
@@ -2369,6 +2369,10 @@
 				>
 			</File>
 			<File
+				RelativePath=".\..\src\ai\api\ai_group.hpp"
+				>
+			</File>
+			<File
 				RelativePath=".\..\src\ai\api\ai_industry.hpp"
 				>
 			</File>
@@ -2549,6 +2553,10 @@
 				>
 			</File>
 			<File
+				RelativePath=".\..\src\ai\api\ai_group.cpp"
+				>
+			</File>
+			<File
 				RelativePath=".\..\src\ai\api\ai_industry.cpp"
 				>
 			</File>
--- a/source.list	Mon Jun 23 12:33:38 2008 +0000
+++ b/source.list	Mon Jun 23 12:46:38 2008 +0000
@@ -541,6 +541,7 @@
 ai/api/ai_event_types.hpp
 ai/api/ai_execmode.hpp
 ai/api/ai_gamesettings.hpp
+ai/api/ai_group.hpp
 ai/api/ai_industry.hpp
 ai/api/ai_industrylist.hpp
 ai/api/ai_list.hpp
@@ -587,6 +588,7 @@
 ai/api/ai_event_types.cpp
 ai/api/ai_execmode.cpp
 ai/api/ai_gamesettings.cpp
+ai/api/ai_group.cpp
 ai/api/ai_industry.cpp
 ai/api/ai_industrylist.cpp
 ai/api/ai_list.cpp
--- a/src/ai/api/ai_controller.cpp	Mon Jun 23 12:33:38 2008 +0000
+++ b/src/ai/api/ai_controller.cpp	Mon Jun 23 12:46:38 2008 +0000
@@ -39,6 +39,7 @@
 #include "ai_event_types.hpp.sq"
 #include "ai_execmode.hpp.sq"
 #include "ai_gamesettings.hpp.sq"
+#include "ai_group.hpp.sq"
 #include "ai_industry.hpp.sq"
 #include "ai_industrylist.hpp.sq"
 #include "ai_list.hpp.sq"
@@ -121,6 +122,7 @@
 	SQAIEventVehicleWaitingInDepot_Register(this->engine);
 	SQAIExecMode_Register(this->engine);
 	SQAIGameSettings_Register(this->engine);
+	SQAIGroup_Register(this->engine);
 	SQAIIndustry_Register(this->engine);
 	SQAIIndustryList_Register(this->engine);
 	SQAIIndustryList_CargoAccepting_Register(this->engine);
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ai/api/ai_group.cpp	Mon Jun 23 12:46:38 2008 +0000
@@ -0,0 +1,116 @@
+/* $Id$ */
+
+/** @file ai_group.cpp Implementation of AIGroup. */
+
+#include "ai_group.hpp"
+#include "ai_vehicle.hpp"
+#include "ai_engine.hpp"
+#include "../../openttd.h"
+#include "../../player_func.h"
+#include "../../group.h"
+#include "../../string_func.h"
+#include "../../strings_func.h"
+#include "../../core/alloc_func.hpp"
+#include "../../command_func.h"
+#include "../../autoreplace_func.h"
+#include "table/strings.h"
+
+/* static */ bool AIGroup::IsValidGroup(GroupID group_id)
+{
+	return ::IsValidGroupID(group_id) && ::GetGroup(group_id)->owner == _current_player;
+}
+
+/* static */ AIGroup::GroupID AIGroup::CreateGroup(AIVehicle::VehicleType vehicle_type)
+{
+	/* Reset the internal NewGroupID in case we are in TestMode */
+	AIObject::SetNewGroupID(0);
+
+	bool ret = AIObject::DoCommand(0, (::VehicleType)vehicle_type, 0, CMD_CREATE_GROUP);
+	return ret ? (GroupID)AIObject::GetNewGroupID() : INVALID_GROUP;
+}
+
+/* static */ bool AIGroup::DeleteGroup(GroupID group_id)
+{
+	EnforcePrecondition(false, IsValidGroup(group_id));
+
+	return AIObject::DoCommand(0, group_id, 0, CMD_DELETE_GROUP);
+}
+
+/* static */ AIVehicle::VehicleType AIGroup::GetVehicleType(GroupID group_id)
+{
+	if (!IsValidGroup(group_id)) return AIVehicle::VEHICLE_INVALID;
+
+	return (AIVehicle::VehicleType)((::VehicleType)::GetGroup(group_id)->vehicle_type);
+}
+
+/* static */ bool AIGroup::SetName(GroupID group_id, const char *name)
+{
+	EnforcePrecondition(false, IsValidGroup(group_id));
+	EnforcePrecondition(false, !::StrEmpty(name));
+
+	_cmd_text = name;
+	return AIObject::DoCommand(0, group_id, 0, CMD_RENAME_GROUP);
+}
+
+/* static */ char *AIGroup::GetName(GroupID group_id)
+{
+	if (!IsValidGroup(group_id)) return NULL;
+
+	static const int len = 64;
+	char *group_name = MallocT<char>(len);
+
+	::SetDParam(0, group_id);
+	::GetString(group_name, STR_GROUP_NAME, &group_name[len - 1]);
+	return group_name;
+}
+
+/* static */ bool AIGroup::EnableAutoReplaceProtection(GroupID group_id, bool enable)
+{
+	EnforcePrecondition(false, IsValidGroup(group_id));
+
+	return AIObject::DoCommand(0, group_id, enable ? 1 : 0, CMD_SET_GROUP_REPLACE_PROTECTION);
+}
+
+/* static */ bool AIGroup::GetAutoReplaceProtection(GroupID group_id)
+{
+	if (!IsValidGroup(group_id)) return false;
+
+	return ::GetGroup(group_id)->replace_protection;
+}
+
+/* static */ int32 AIGroup::GetNumEngines(GroupID group_id, EngineID engine_id)
+{
+	if (!IsValidGroup(group_id) && group_id != DEFAULT_GROUP && group_id != ALL_GROUP) return -1;
+
+	return GetGroupNumEngines(_current_player, group_id, engine_id);
+}
+
+/* static */ bool AIGroup::MoveVehicle(GroupID group_id, VehicleID vehicle_id)
+{
+	EnforcePrecondition(false, IsValidGroup(group_id) || group_id == DEFAULT_GROUP);
+	EnforcePrecondition(false, AIVehicle::IsValidVehicle(vehicle_id));
+
+	return AIObject::DoCommand(0, group_id, vehicle_id, CMD_ADD_VEHICLE_GROUP);
+}
+
+/* static */ bool AIGroup::SetAutoReplace(GroupID group_id, EngineID engine_id_old, EngineID engine_id_new)
+{
+	EnforcePrecondition(false, IsValidGroup(group_id) || group_id == ALL_GROUP);
+	EnforcePrecondition(false, AIEngine::IsValidEngine(engine_id_new));
+
+	return AIObject::DoCommand(0, 3 | (group_id << 16), (engine_id_new << 16) | engine_id_old, CMD_SET_AUTOREPLACE);
+}
+
+/* static */ EngineID AIGroup::GetEngineReplacement(GroupID group_id, EngineID engine_id)
+{
+	if (!IsValidGroup(group_id) && group_id != ALL_GROUP) return ::INVALID_ENGINE;
+
+	return ::EngineReplacementForPlayer(GetPlayer(_current_player), engine_id, group_id);
+}
+
+/* static */ bool AIGroup::StopAutoReplace(GroupID group_id, EngineID engine_id)
+{
+	EnforcePrecondition(false, IsValidGroup(group_id) || group_id == ALL_GROUP);
+
+	return AIObject::DoCommand(0, 3 | (group_id << 16), (::INVALID_ENGINE << 16) | engine_id, CMD_SET_AUTOREPLACE);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ai/api/ai_group.hpp	Mon Jun 23 12:46:38 2008 +0000
@@ -0,0 +1,153 @@
+/* $Id$ */
+
+/** @file ai_group.hpp Everything to put vehicles into groups. */
+
+#ifndef AI_GROUP_HPP
+#define AI_GROUP_HPP
+
+#include "ai_object.hpp"
+#include "ai_vehicle.hpp"
+
+/**
+ * Class that handles all group related functions.
+ */
+class AIGroup : public AIObject {
+public:
+	static const char *GetClassName() { return "AIGroup"; }
+
+	/**
+	 * The group IDs of some special groups.
+	 */
+	enum GroupID {
+		/* Values are important, as they represent the internal state of the game (see group_type.h). */
+		ALL_GROUP = 0xFFFD,     //!< All vehicles are in this group.
+		DEFAULT_GROUP = 0xFFFE, //!< Vehicles not put in any other group are in this one.
+		INVALID_GROUP = 0xFFFF, //!< An invalid group id.
+	};
+
+	/**
+	 * Checks whether the given group is valid.
+	 * @param group_id The group to check.
+	 * @pre group_id != DEFAULT_GROUP && group_id != ALL_GROUP.
+	 * @return True if and only if the group is valid.
+	 */
+	static bool IsValidGroup(GroupID group_id);
+
+	/**
+	 * Create a new group.
+	 * @param vehicle_type The type of vehicle to create a group for.
+	 * @return The GroupID of the new group, or an invalid GroupID when
+	 *  it failed. Check the return value using IsValidGroup(). In test-mode
+	 *  0 is returned if it was successful; any other value indicates failure.
+	 */
+	static GroupID CreateGroup(AIVehicle::VehicleType vehicle_type);
+
+	/**
+	 * Delete the given group. When the deletion succeeds all vehicles in the
+	 *  given group will move to the DEFAULT_GROUP.
+	 * @param group_id The group to delete.
+	 * @pre IsValidGroup(group_id).
+	 * @return True if and only if the group was succesfully deleted.
+	 */
+	static bool DeleteGroup(GroupID group_id);
+
+	/**
+	 * Get the vehicle type of a group.
+	 * @param group_id The group to get the type from.
+	 * @pre IsValidGroup(group_id).
+	 * @return The vehicletype of the given group.
+	 */
+	static AIVehicle::VehicleType GetVehicleType(GroupID group_id);
+
+	/**
+	 * Set the name of a group.
+	 * @param group_id The group to set the name for.
+	 * @param name The name for the group.
+	 * @pre IsValidGroup(group_id).
+	 * @pre Name has to be unique.
+	 * @pre Name has to be at least one character long.
+	 * @return True if and only if the name was changed.
+	 */
+	static bool SetName(GroupID group_id, const char *name);
+
+	/**
+	 * Get the name of a group.
+	 * @param group_id The group to get the name of.
+	 * @pre IsValidGroup(group_id).
+	 * @return The name the group has.
+	 */
+	static char *GetName(GroupID group_id);
+
+	/**
+	 * Enable or disable autoreplace protected. If the protection is
+	 *  enabled, global autoreplace won't affect vehicles in this group.
+	 * @param group_id The group to change the protection for.
+	 * @param enable True if protection should be enabled.
+	 * @pre IsValidGroup(group_id).
+	 * @return True if and only if the protection was succesfully changed.
+	 */
+	static bool EnableAutoReplaceProtection(GroupID group_id, bool enable);
+
+	/**
+	 * Get the autoreplace protection status.
+	 * @param group_id The group to get the protection status for.
+	 * @pre IsValidGroup(group_id).
+	 * @return The autoreplace protection status for the given group.
+	 */
+	static bool GetAutoReplaceProtection(GroupID group_id);
+
+	/**
+	 * Get the number of engines in a given group.
+	 * @param group_id The group to get the number of engines in.
+	 * @param engine_id The engine id to count.
+	 * @pre IsValidGroup(group_id) || group_id == ALL_GROUP || group_id == DEFAULT_GROUP.
+	 * @return The number of engines with id engine_id in the group with id group_id.
+	 */
+	static int32 GetNumEngines(GroupID group_id, EngineID engine_id);
+
+	/**
+	 * Move a vehicle to a group.
+	 * @param group_id The group to move the vehicel to.
+	 * @param vehicle_id The vehicle to move to the group.
+	 * @pre IsValidGroup(group_id) || group_id == DEFAULT_GROUP.
+	 * @pre AIVehicle::IsValidVehicle(vehicle_id).
+	 * @return True if and only if the vehicle was succesfully moved to the group.
+	 * @note A vehicle can be in only one group at the same time. To remove it from
+	 *  a group, move it to another or to DEFAULT_GROUP. Moving the vehicle to the
+	 *  given group means removing it from another group.
+	 */
+	static bool MoveVehicle(GroupID group_id, VehicleID vehicle_id);
+
+	/**
+	 * Start replacing all vehicles with a specified engine with another engine.
+	 * @param group_id The group to replace vehicles from. Use ALL_GROUP to replace
+	 *  vehicles from all groups that haven't set autoreplace protection.
+	 * @param engine_id_old The engine id to start replacing.
+	 * @param engine_id_new The engine id to replace with.
+	 * @pre IsValidGroup(group_id) || group_id == ALL_GROUP.
+	 * @pre AIEngine.IsValidEngine(engine_id_new).
+	 * @note To stop autoreplacing engine_id_old, call StopAutoReplace(group_id, engine_id_old).
+	 */
+	static bool SetAutoReplace(GroupID group_id, EngineID engine_id_old, EngineID engine_id_new);
+
+	/**
+	 * Get the EngineID the given EngineID is replaced with.
+	 * @param group_id The group to get the replacement from.
+	 * @param engine_id The engine that is being replaced.
+	 * @pre IsValidGroup(group_id) || group_id == ALL_GROUP.
+	 * @return The EngineID that is replacing engine_id or an invalid EngineID
+	 *   in case engine_id is not begin replaced.
+	 */
+	static EngineID GetEngineReplacement(GroupID group_id, EngineID engine_id);
+
+	/**
+	 * Stop replacing a certain engine in the specified group.
+	 * @param group_id The group to stop replacing the engine in.
+	 * @param engine_id The engine id to stop replacing with another engine.
+	 * @pre IsValidGroup(group_id) || group_id == ALL_GROUP.
+	 * @return True if and if the replacing was succesfully stopped.
+	 */
+	static bool StopAutoReplace(GroupID group_id, EngineID engine_id);
+};
+
+#endif /* AI_GROUP_HPP */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ai/api/ai_group.hpp.sq	Mon Jun 23 12:46:38 2008 +0000
@@ -0,0 +1,44 @@
+/* $Id$ */
+/* THIS FILE IS AUTO-GENERATED; PLEASE DO NOT ALTER MANUALLY */
+
+#include "ai_group.hpp"
+
+namespace SQConvert {
+	/* Allow enums to be used as Squirrel parameters */
+	template <> AIGroup::GroupID GetParam(ForceType<AIGroup::GroupID>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQInteger tmp; sq_getinteger(vm, index, &tmp); return (AIGroup::GroupID)tmp; }
+	template <> int Return<AIGroup::GroupID>(HSQUIRRELVM vm, AIGroup::GroupID res) { sq_pushinteger(vm, (int32)res); return 1; }
+
+	/* Allow AIGroup to be used as Squirrel parameter */
+	template <> AIGroup *GetParam(ForceType<AIGroup *>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return  (AIGroup *)instance; }
+	template <> AIGroup &GetParam(ForceType<AIGroup &>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return *(AIGroup *)instance; }
+	template <> const AIGroup *GetParam(ForceType<const AIGroup *>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return  (AIGroup *)instance; }
+	template <> const AIGroup &GetParam(ForceType<const AIGroup &>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return *(AIGroup *)instance; }
+	template <> int Return<AIGroup *>(HSQUIRRELVM vm, AIGroup *res) { if (res == NULL) { sq_pushnull(vm); return 1; } res->AddRef(); Squirrel::CreateClassInstanceVM(vm, "AIGroup", res, NULL, DefSQDestructorCallback<AIGroup>); return 1; }
+}; // namespace SQConvert
+
+void SQAIGroup_Register(Squirrel *engine) {
+	DefSQClass <AIGroup> SQAIGroup("AIGroup");
+	SQAIGroup.PreRegister(engine);
+	SQAIGroup.AddConstructor<void (AIGroup::*)(), 1>(engine, "x");
+
+	SQAIGroup.DefSQConst(engine, AIGroup::ALL_GROUP,     "ALL_GROUP");
+	SQAIGroup.DefSQConst(engine, AIGroup::DEFAULT_GROUP, "DEFAULT_GROUP");
+	SQAIGroup.DefSQConst(engine, AIGroup::INVALID_GROUP, "INVALID_GROUP");
+
+	SQAIGroup.DefSQStaticMethod(engine, &AIGroup::GetClassName,                "GetClassName",                1, "x");
+	SQAIGroup.DefSQStaticMethod(engine, &AIGroup::IsValidGroup,                "IsValidGroup",                2, "xi");
+	SQAIGroup.DefSQStaticMethod(engine, &AIGroup::CreateGroup,                 "CreateGroup",                 2, "xi");
+	SQAIGroup.DefSQStaticMethod(engine, &AIGroup::DeleteGroup,                 "DeleteGroup",                 2, "xi");
+	SQAIGroup.DefSQStaticMethod(engine, &AIGroup::GetVehicleType,              "GetVehicleType",              2, "xi");
+	SQAIGroup.DefSQStaticMethod(engine, &AIGroup::SetName,                     "SetName",                     3, "xis");
+	SQAIGroup.DefSQStaticMethod(engine, &AIGroup::GetName,                     "GetName",                     2, "xi");
+	SQAIGroup.DefSQStaticMethod(engine, &AIGroup::EnableAutoReplaceProtection, "EnableAutoReplaceProtection", 3, "xib");
+	SQAIGroup.DefSQStaticMethod(engine, &AIGroup::GetAutoReplaceProtection,    "GetAutoReplaceProtection",    2, "xi");
+	SQAIGroup.DefSQStaticMethod(engine, &AIGroup::GetNumEngines,               "GetNumEngines",               3, "xii");
+	SQAIGroup.DefSQStaticMethod(engine, &AIGroup::MoveVehicle,                 "MoveVehicle",                 3, "xii");
+	SQAIGroup.DefSQStaticMethod(engine, &AIGroup::SetAutoReplace,              "SetAutoReplace",              4, "xiii");
+	SQAIGroup.DefSQStaticMethod(engine, &AIGroup::GetEngineReplacement,        "GetEngineReplacement",        3, "xii");
+	SQAIGroup.DefSQStaticMethod(engine, &AIGroup::StopAutoReplace,             "StopAutoReplace",             3, "xii");
+
+	SQAIGroup.PostRegister(engine);
+}
--- a/src/ai/api/ai_object.cpp	Mon Jun 23 12:33:38 2008 +0000
+++ b/src/ai/api/ai_object.cpp	Mon Jun 23 12:46:38 2008 +0000
@@ -14,6 +14,7 @@
 #include "../../signs_func.h"
 #include "../../tunnelbridge.h"
 #include "../../vehicle_func.h"
+#include "../../group.h"
 #include "../ai.h"
 #include "../ai_threads.h"
 
@@ -27,6 +28,7 @@
 	VehicleID new_vehicle_id;
 	SignID new_sign_id;
 	TileIndex new_tunnel_endtile;
+	GroupID new_group_id;
 	RoadType road_type;
 	void *event_data;
 	void *log_data;
@@ -101,6 +103,7 @@
 	SetNewVehicleID(_new_vehicle_id);
 	SetNewSignID(_new_sign_id);
 	SetNewTunnelEndtile(_build_tunnel_endtile);
+	SetNewGroupID(_new_group_id);
 }
 
 bool AIObject::GetLastCommandRes()
@@ -138,6 +141,16 @@
 	return GetDoCommandStruct(_current_player)->new_tunnel_endtile;
 }
 
+void AIObject::SetNewGroupID(GroupID group_id)
+{
+	GetDoCommandStruct(_current_player)->new_group_id = group_id;
+}
+
+GroupID AIObject::GetNewGroupID()
+{
+	return GetDoCommandStruct(_current_player)->new_group_id;
+}
+
 void *&AIObject::GetEventPointer()
 {
 	return GetDoCommandStruct(_current_player)->event_data;
--- a/src/ai/api/ai_object.hpp	Mon Jun 23 12:33:38 2008 +0000
+++ b/src/ai/api/ai_object.hpp	Mon Jun 23 12:46:38 2008 +0000
@@ -118,6 +118,11 @@
 	static TileIndex GetNewTunnelEndtile();
 
 	/**
+	 * Get the latest stored new_group_id.
+	 */
+	static GroupID GetNewGroupID();
+
+	/**
 	 * Get the pointer to store event data in.
 	 */
 	static void *&GetEventPointer();
@@ -152,6 +157,13 @@
 	static void SetNewTunnelEndtile(TileIndex tile);
 
 	/**
+	 * Store a new_group_id per player.
+	 * @note NEVER use this yourself in your AI!
+	 * @param group_id The new GroupID.
+	 */
+	static void SetNewGroupID(GroupID group_id);
+
+	/**
 	 * If an AI starts, some internals needs to be resetted. This function
 	 *   takes care of that.
 	 * @note NEVER use this yourself in your AI!
--- a/src/ai/api/ai_types.hpp	Mon Jun 23 12:33:38 2008 +0000
+++ b/src/ai/api/ai_types.hpp	Mon Jun 23 12:46:38 2008 +0000
@@ -13,6 +13,7 @@
 typedef byte CargoID;        //!< The ID of a cargo.
 class CommandCost;           //!< The cost of a command.
 typedef uint16 EngineID;     //!< The ID of an engine.
+typedef uint16 GroupID;      //!< The ID of a group.
 typedef uint16 IndustryID;   //!< The ID of an industry.
 typedef OverflowSafeInt64 Money; //!< Money, stored in a 32bit/64bit safe way. For AIs money is always in pounds.
 typedef uint16 SignID;       //!< The ID of a sign.
--- a/src/ai/api/ai_vehicle.cpp	Mon Jun 23 12:33:38 2008 +0000
+++ b/src/ai/api/ai_vehicle.cpp	Mon Jun 23 12:46:38 2008 +0000
@@ -276,3 +276,10 @@
 
 	return amount;
 }
+
+/* static */ GroupID AIVehicle::GetGroupID(VehicleID vehicle_id)
+{
+	if (!IsValidVehicle(vehicle_id)) return ::INVALID_GROUP;
+
+	return ::GetVehicle(vehicle_id)->group_id;
+}
--- a/src/ai/api/ai_vehicle.hpp	Mon Jun 23 12:33:38 2008 +0000
+++ b/src/ai/api/ai_vehicle.hpp	Mon Jun 23 12:46:38 2008 +0000
@@ -353,6 +353,13 @@
 	 * @return The amount of the given cargo the vehicle currently transports.
 	 */
 	static int32 GetCargoLoad(VehicleID vehicle_id, CargoID cargo);
+
+	/**
+	 * Get the group of a given vehicle.
+	 * @param vehicle_id The vehicle to get the group from.
+	 * @return The group of the given vehicle.
+	 */
+	static GroupID GetGroupID(VehicleID vehicle_id);
 };
 
 #endif /* AI_VEHICLE_HPP */
--- a/src/ai/api/ai_vehicle.hpp.sq	Mon Jun 23 12:33:38 2008 +0000
+++ b/src/ai/api/ai_vehicle.hpp.sq	Mon Jun 23 12:46:38 2008 +0000
@@ -125,6 +125,7 @@
 	SQAIVehicle.DefSQStaticMethod(engine, &AIVehicle::SkipToVehicleOrder, "SkipToVehicleOrder", 3, "xii");
 	SQAIVehicle.DefSQStaticMethod(engine, &AIVehicle::GetCapacity,        "GetCapacity",        3, "xii");
 	SQAIVehicle.DefSQStaticMethod(engine, &AIVehicle::GetCargoLoad,       "GetCargoLoad",       3, "xii");
+	SQAIVehicle.DefSQStaticMethod(engine, &AIVehicle::GetGroupID,         "GetGroupID",         2, "xi");
 
 	SQAIVehicle.PostRegister(engine);
 }
--- a/src/group.h	Mon Jun 23 12:33:38 2008 +0000
+++ b/src/group.h	Mon Jun 23 12:46:38 2008 +0000
@@ -92,4 +92,6 @@
 void RemoveVehicleFromGroup(const Vehicle *v);
 void RemoveAllGroupsForPlayer(const PlayerID p);
 
+extern GroupID _new_group_id;
+
 #endif /* GROUP_H */
--- a/src/group_cmd.cpp	Mon Jun 23 12:33:38 2008 +0000
+++ b/src/group_cmd.cpp	Mon Jun 23 12:46:38 2008 +0000
@@ -26,6 +26,8 @@
 
 #include "table/strings.h"
 
+GroupID _new_group_id;
+
 /**
  * Update the num engines of a groupID. Decrease the old one and increase the new one
  * @note called in SetTrainGroupID and UpdateTrainGroupID
@@ -91,6 +93,8 @@
 		g->replace_protection = false;
 		g->vehicle_type = vt;
 
+		_new_group_id = g->index;
+
 		InvalidateWindowData(GetWindowClassForVehicleType(vt), (vt << 11) | VLW_GROUP_LIST | _current_player);
 	}