(svn r10631) [NoAI] -Add: AIEvent, to take care of events; for now it only reports when vehicles are crashed noai
authortruelight
Thu, 19 Jul 2007 22:39:43 +0000
branchnoai
changeset 9682 d031eb183733
parent 9681 3997f1ce203a
child 9683 a4683b021073
(svn r10631) [NoAI] -Add: AIEvent, to take care of events; for now it only reports when vehicles are crashed
bin/ai/regression/regression.nut
bin/ai/regression/regression.txt
bin/ai/wrightai/main.nut
projects/openttd.vcproj
projects/openttd_vs80.vcproj
source.list
src/ai/ai.cpp
src/ai/ai.h
src/ai/ai_squirrel.cpp
src/ai/api/ai_event.cpp
src/ai/api/ai_event.hpp
src/ai/api/ai_event.hpp.sq
src/ai/api/ai_event_types.cpp
src/ai/api/ai_event_types.hpp
src/ai/api/ai_event_types.hpp.sq
src/ai/api/ai_object.cpp
src/ai/api/ai_object.hpp
src/aircraft_cmd.cpp
src/roadveh_cmd.cpp
src/train_cmd.cpp
--- a/bin/ai/regression/regression.nut	Thu Jul 19 22:26:26 2007 +0000
+++ b/bin/ai/regression/regression.nut	Thu Jul 19 22:39:43 2007 +0000
@@ -149,6 +149,45 @@
 	print("  GetLoanAmount():             " + company.GetLoanAmount());
 }
 
+function Regression::Event()
+{
+	print("");
+	print("--Event--");
+	AIEventController.Test();
+	local e = AIEventController.GetNextEvent();
+	print("  GetNextEvent:      " + e);
+	print("    GetEventType:    " + e.GetEventType());
+	local c = AIEventTest.Convert(e);
+	print("    Convert:         " + c);
+	print("      GetTest:       " + c.GetTest());
+
+	print("  DisableEvent(1):   done");
+	AIEventController.DisableEvent(1);
+	AIEventController.Test();
+	e = AIEventController.GetNextEvent();
+	print("  GetNextEvent:      " + e);
+
+	print("  EnableEvent(1):    done");
+	AIEventController.EnableEvent(1);
+	AIEventController.Test();
+	e = AIEventController.GetNextEvent();
+	print("  GetNextEvent:      " + e);
+
+	{
+		print("  DisableAllEvents():done");
+		AIEventController.DisableAllEvents();
+		AIEventController.Test();
+		e = AIEventController.GetNextEvent();
+		print("  GetNextEvent:      " + e);
+	}
+
+	print("  EnableEvent(1):    done");
+	AIEventController.EnableEvent(1);
+	AIEventController.Test();
+	e = AIEventController.GetNextEvent();
+	print("  GetNextEvent:      " + e);
+}
+
 function Regression::Industry()
 {
 	local industry = AIIndustry();
@@ -796,6 +835,7 @@
 	this.Airport();
 	this.Cargo();
 	this.Company();
+	this.Event();
 	this.Industry();
 	this.IndustryList();
 	this.Map();
--- a/bin/ai/regression/regression.txt	Thu Jul 19 22:26:26 2007 +0000
+++ b/bin/ai/regression/regression.txt	Thu Jul 19 22:39:43 2007 +0000
@@ -246,6 +246,20 @@
   GetBankBalance():            284436
   GetLoanAmount():             300000
 
+--Event--
+  GetNextEvent:      (instance : 0x0xb431e0)
+    GetEventType:    1
+    Convert:         (instance : 0x0xb41280)
+      GetTest:       42
+  DisableEvent(1):   done
+  GetNextEvent:      (null : 0x00000000)
+  EnableEvent(1):    done
+  GetNextEvent:      (instance : 0x0xb431e0)
+  DisableAllEvents():done
+  GetNextEvent:      (null : 0x00000000)
+  EnableEvent(1):    done
+  GetNextEvent:      (instance : 0x0xb431e0)
+
 --Industry--
   GetMaxIndustryID():  71
   GetIndustryCount():  69
--- a/bin/ai/wrightai/main.nut	Thu Jul 19 22:26:26 2007 +0000
+++ b/bin/ai/wrightai/main.nut	Thu Jul 19 22:39:43 2007 +0000
@@ -13,7 +13,6 @@
 	route_2 = null;
 	distance_of_route = {};
 	vehicle_to_depot = {};
-	vehicle_depot_tile = {};
 	delay_build_airport_route = 1000;
 
 	function Start();
@@ -52,7 +51,7 @@
 
 	local loan = money - this.company.GetBankBalance(AICompany.MY_COMPANY) + this.company.GetLoanInterval() + this.company.GetLoanAmount();
 	loan = loan - loan % this.company.GetLoanInterval();
-	print(this.name + ": Need a loan to get " + money + ": " + loan);
+	print(this.name + ": [INFO] Need a loan to get " + money + ": " + loan);
 	this.company.SetLoanAmount(loan);
 }
 
@@ -237,8 +236,8 @@
 					print(this.name + ": [INFO] Removing airports as nobody serves them anymore.");
 					this.airport.RemoveAirport(this.route_1.GetValue(i));
 					this.airport.RemoveAirport(this.route_2.GetValue(i));
-					this.route_1.RemoteItem(i);
-					this.route_2.RemoteItem(i);
+					this.route_1.RemoveItem(i);
+					this.route_2.RemoveItem(i);
 					/* XXX -- We should free the towns_used entries too */
 				}
 				this.vehicle_to_depot.rawdelete(i);
@@ -277,6 +276,26 @@
 	}
 }
 
+function WrightAI::HandleEvents()
+{
+	while (AIEventController.IsEventWaiting()) {
+		local e = AIEventController.GetNextEvent();
+		switch (e.GetEventType()) {
+			case AIEvent.AI_ET_CRASHED_VEHICLE:
+				local ec = AIEventVehicleCrash.Convert(e);
+				local v = ec.GetVehicleID();
+				print(this.name + ": [INFO] We have a crashed vehicle (" + v + "), buying a new one as replacement");
+				this.BuildAircraft(this.route_1.GetValue(v), this.route_2.GetValue(v));
+				this.route_1.RemoveItem(v);
+				this.route_2.RemoveItem(v);
+				break;
+
+			default:
+				break;
+		}
+	}
+}
+
 function WrightAI::Start()
 {
 	/* Sleep 1 tick, as we can't execute anything in tick 0 */
@@ -316,6 +335,10 @@
 		if (ticker % 5000 == 0) {
 			this.company.SetLoanAmount(0);
 		}
+		/* Check for events once in a while */
+		if (ticker % 100 == 0) {
+			this.HandleEvents();
+		}
 		/* Make sure we do not create infinite loops */
 		Sleep(1);
 	}
--- a/projects/openttd.vcproj	Thu Jul 19 22:26:26 2007 +0000
+++ b/projects/openttd.vcproj	Thu Jul 19 22:39:43 2007 +0000
@@ -1096,6 +1096,12 @@
 				RelativePath=".\..\src\ai\api\ai_controller.hpp">
 			</File>
 			<File
+				RelativePath=".\..\src\ai\api\ai_event.hpp">
+			</File>
+			<File
+				RelativePath=".\..\src\ai\api\ai_event_types.hpp">
+			</File>
+			<File
 				RelativePath=".\..\src\ai\api\ai_execmode.hpp">
 			</File>
 			<File
@@ -1205,6 +1211,12 @@
 				RelativePath=".\..\src\ai\api\ai_controller.cpp">
 			</File>
 			<File
+				RelativePath=".\..\src\ai\api\ai_event.cpp">
+			</File>
+			<File
+				RelativePath=".\..\src\ai\api\ai_event_types.cpp">
+			</File>
+			<File
 				RelativePath=".\..\src\ai\api\ai_execmode.cpp">
 			</File>
 			<File
--- a/projects/openttd_vs80.vcproj	Thu Jul 19 22:26:26 2007 +0000
+++ b/projects/openttd_vs80.vcproj	Thu Jul 19 22:39:43 2007 +0000
@@ -1680,6 +1680,14 @@
 				>
 			</File>
 			<File
+				RelativePath=".\..\src\ai\api\ai_event.hpp"
+				>
+			</File>
+			<File
+				RelativePath=".\..\src\ai\api\ai_event_types.hpp"
+				>
+			</File>
+			<File
 				RelativePath=".\..\src\ai\api\ai_execmode.hpp"
 				>
 			</File>
@@ -1824,6 +1832,14 @@
 				>
 			</File>
 			<File
+				RelativePath=".\..\src\ai\api\ai_event.cpp"
+				>
+			</File>
+			<File
+				RelativePath=".\..\src\ai\api\ai_event_types.cpp"
+				>
+			</File>
+			<File
 				RelativePath=".\..\src\ai\api\ai_execmode.cpp"
 				>
 			</File>
--- a/source.list	Thu Jul 19 22:26:26 2007 +0000
+++ b/source.list	Thu Jul 19 22:39:43 2007 +0000
@@ -338,6 +338,8 @@
 ai/api/ai_cargo.hpp
 ai/api/ai_company.hpp
 ai/api/ai_controller.hpp
+ai/api/ai_event.hpp
+ai/api/ai_event_types.hpp
 ai/api/ai_execmode.hpp
 ai/api/ai_industry.hpp
 ai/api/ai_industrylist.hpp
@@ -375,6 +377,8 @@
 ai/api/ai_cargo.cpp
 ai/api/ai_company.cpp
 ai/api/ai_controller.cpp
+ai/api/ai_event.cpp
+ai/api/ai_event_types.cpp
 ai/api/ai_execmode.cpp
 ai/api/ai_industry.cpp
 ai/api/ai_industrylist.cpp
--- a/src/ai/ai.cpp	Thu Jul 19 22:26:26 2007 +0000
+++ b/src/ai/ai.cpp	Thu Jul 19 22:39:43 2007 +0000
@@ -59,6 +59,16 @@
 	_current_player = OWNER_NONE;
 }
 
+void AI_Event(PlayerID player, AIEvent *event)
+{
+	DEBUG(ai, 4, "Event (%d) for player %d\n", event->GetEventType(), player);
+
+	PlayerID old_player = _current_player;
+	_current_player = player;
+	AIEventController::InsertEvent(event);
+	_current_player = old_player;
+}
+
 /**
  * A new AI sees the day of light. You can do here what ever you think is needed.
  */
@@ -167,6 +177,7 @@
 void AI_StartNewAI(PlayerID player) { DEBUG(ai, 0, "Threading is disabled and therefor the AI too."); }
 void AI_PlayerDied(PlayerID player) {}
 void AI_RunGameLoop() {}
+void AI_Event(PlayerID player, AIEvent *event) {}
 void AI_Initialize() {}
 void AI_Uninitialize() {}
 bool AI_AllowNewAI() { return false; }
--- a/src/ai/ai.h	Thu Jul 19 22:26:26 2007 +0000
+++ b/src/ai/ai.h	Thu Jul 19 22:39:43 2007 +0000
@@ -5,6 +5,8 @@
 #ifndef AI_H
 #define AI_H
 
+#include "api/ai_event_types.hpp"
+
 void AI_StartNewAI(PlayerID player);
 void AI_PlayerDied(PlayerID player);
 void AI_RunGameLoop();
@@ -12,6 +14,7 @@
 void AI_Uninitialize();
 bool AI_AllowNewAI();
 void AI_ForceAI(const char *forced_ai);
+void AI_Event(PlayerID player, AIEvent *event);
 
 void CcAI(bool success, TileIndex tile, uint32 p1, uint32 p2);
 
--- a/src/ai/ai_squirrel.cpp	Thu Jul 19 22:26:26 2007 +0000
+++ b/src/ai/ai_squirrel.cpp	Thu Jul 19 22:39:43 2007 +0000
@@ -28,6 +28,8 @@
 #include "api/ai_cargo.hpp.sq"
 #include "api/ai_company.hpp.sq"
 #include "api/ai_controller.hpp.sq"
+#include "api/ai_event.hpp.sq"
+#include "api/ai_event_types.hpp.sq"
 #include "api/ai_execmode.hpp.sq"
 #include "api/ai_industry.hpp.sq"
 #include "api/ai_industrylist.hpp.sq"
@@ -214,6 +216,10 @@
 	SQAICargoRegister(this->engine);
 	SQAICompanyRegister(this->engine);
 	SQAIControllerRegister(this->engine);
+	SQAIEventControllerRegister(this->engine);
+	SQAIEventRegister(this->engine);
+	SQAIEventTestRegister(this->engine);
+	SQAIEventVehicleCrashRegister(this->engine);
 	SQAIExecModeRegister(this->engine);
 	SQAIIndustryListDistanceManhattanToTileRegister(this->engine);
 	SQAIIndustryListDistanceSquareToTileRegister(this->engine);
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ai/api/ai_event.cpp	Thu Jul 19 22:39:43 2007 +0000
@@ -0,0 +1,115 @@
+#include "ai_event.hpp"
+/* TODO -- This include has to go */
+#include "ai_event_types.hpp"
+
+#include <queue>
+#include <set>
+
+struct AIEventData {
+	std::queue<AIEvent *> stack;
+	std::set<AIEvent::AIEventType> events;
+	bool events_disabled;
+
+	AIEventData() :
+		events_disabled(false)
+	{}
+};
+
+/* static */ void AIEventController::CreateEventPointer()
+{
+	assert(AIObject::GetEventPointer() == NULL);
+
+	AIObject::GetEventPointer() = new AIEventData();
+}
+
+/* static */ void AIEventController::FreeEventPointer()
+{
+	AIEventData *data = (AIEventData *)AIObject::GetEventPointer();
+
+	/* Free up the event-list */
+	data->events.clear();
+
+	/* Free all waiting events (if any) */
+	while (!data->stack.empty()) {
+		AIEvent *e = data->stack.front();
+		data->stack.pop();
+		delete e;
+	}
+
+	/* Now kill our data pointer */
+	delete data;
+}
+
+/* static */ bool AIEventController::IsEventWaiting()
+{
+	if (AIObject::GetEventPointer() == NULL) AIEventController::CreateEventPointer();
+	AIEventData *data = (AIEventData *)AIObject::GetEventPointer();
+
+	return !data->stack.empty();
+}
+
+/* static */ AIEvent *AIEventController::GetNextEvent()
+{
+	if (AIObject::GetEventPointer() == NULL) AIEventController::CreateEventPointer();
+	AIEventData *data = (AIEventData *)AIObject::GetEventPointer();
+
+	if (data->stack.empty()) return NULL;
+
+	AIEvent *e = data->stack.front();
+	data->stack.pop();
+	return e;
+}
+
+/* static */ void AIEventController::DisableEvent(AIEvent::AIEventType event)
+{
+	if (AIObject::GetEventPointer() == NULL) AIEventController::CreateEventPointer();
+	AIEventData *data = (AIEventData *)AIObject::GetEventPointer();
+
+	if (data->events_disabled) data->events.erase(event);
+	else data->events.insert(event);
+}
+
+/* static */ void AIEventController::EnableEvent(AIEvent::AIEventType event)
+{
+	if (AIObject::GetEventPointer() == NULL) AIEventController::CreateEventPointer();
+	AIEventData *data = (AIEventData *)AIObject::GetEventPointer();
+
+	if (!data->events_disabled) data->events.erase(event);
+	else data->events.insert(event);
+}
+
+/* static */ void AIEventController::DisableAllEvents()
+{
+	if (AIObject::GetEventPointer() == NULL) AIEventController::CreateEventPointer();
+	AIEventData *data = (AIEventData *)AIObject::GetEventPointer();
+
+	data->events_disabled = true;
+	data->events.clear();
+}
+
+/* static */ void AIEventController::EnableAllEvents()
+{
+	if (AIObject::GetEventPointer() == NULL) AIEventController::CreateEventPointer();
+	AIEventData *data = (AIEventData *)AIObject::GetEventPointer();
+
+	data->events_disabled = false;
+	data->events.clear();
+}
+
+/* static */ void AIEventController::InsertEvent(AIEvent *event)
+{
+	if (AIObject::GetEventPointer() == NULL) AIEventController::CreateEventPointer();
+	AIEventData *data = (AIEventData *)AIObject::GetEventPointer();
+
+	/* If events_disabled is true, the list shows only the enabled events.
+	 *  If events_disabled is false, the list shows only the disabled events. */
+	if (data->events_disabled  && data->events.count(event->GetEventType()) == 0) return;
+	if (!data->events_disabled && data->events.count(event->GetEventType()) != 0) return;
+
+	data->stack.push(event);
+}
+
+/* static */ void AIEventController::Test()
+{
+	AIEventController::InsertEvent(new AIEventTest(42));
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ai/api/ai_event.hpp	Thu Jul 19 22:39:43 2007 +0000
@@ -0,0 +1,116 @@
+/* $Id$ */
+
+/** @file ai_event.hpp Everything to handle events from the game */
+
+#ifndef AI_EVENT_HPP
+#define AI_EVENT_HPP
+
+#include "ai_object.hpp"
+
+/**
+ * A single event that can be triggered by the game.
+ * You can lookup the type, and than convert it to the real event-class.
+ * That way you can request more detailed information about the event.
+ */
+class AIEvent : public AIObject {
+public:
+	/**
+	 * The type of event. Needed to lookup the detailed class.
+	 */
+	enum AIEventType {
+		AI_ET_INVALID = 0,
+		AI_ET_TEST,
+		AI_ET_CRASHED_VEHICLE,
+	};
+
+	AIEvent(AIEvent::AIEventType type) :
+		type(type)
+	{}
+
+	/**
+	 * Get the event-type.
+	 */
+	AIEventType GetEventType() { return this->type; }
+
+	/**
+	 * The name of the class, needed by several sub-processes.
+	 */
+	static const char *GetClassName() { return "AIEvent"; }
+
+protected:
+	/**
+	 * The type of this event.
+	 */
+	AIEventType type;
+};
+
+/**
+ * Class that handles all event related functions.
+ * @note it is not needed to create an instance of AIEvent to access it, as
+ *  all members are static, and all data is stored AI-wide.
+ */
+class AIEventController : public AIObject {
+public:
+	/**
+	 * The name of the class, needed by several sub-processes.
+	 */
+	static const char *GetClassName() { return "AIEventController"; }
+
+	/**
+	 * Check if there is an event waiting.
+	 * @return true if there is an event on the stack.
+	 */
+	static bool IsEventWaiting();
+
+	/**
+	 * Get the next event.
+	 * @return a class of the event-child issues.
+	 */
+	static AIEvent *GetNextEvent();
+
+	/**
+	 * No longer report an event of this type.
+	 * @param event the event to no longer report.
+	 */
+	static void DisableEvent(AIEvent::AIEventType event);
+
+	/**
+	 * Report events of this type.
+	 * @param event the event to report again.
+	 */
+	static void EnableEvent(AIEvent::AIEventType event);
+
+	/**
+	 * Disable all events.
+	 */
+	static void DisableAllEvents();
+
+	/**
+	 * Enable all events.
+	 */
+	static void EnableAllEvents();
+
+	/**
+	 * Insert an event to the queue for the player.
+	 */
+	static void InsertEvent(AIEvent *event);
+
+	/**
+	 * Give a test event to the system.
+	 */
+	static void Test();
+
+	/**
+	 * Free the event pointer.
+	 * @note DO NOT CALL YOURSELF; leave it to the internal AI programming.
+	 */
+	static void FreeEventPointer();
+
+private:
+	/**
+	 * Create the event pointer.
+	 */
+	static void CreateEventPointer();
+};
+
+#endif /* AI_EVENT_HPP */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ai/api/ai_event.hpp.sq	Thu Jul 19 22:39:43 2007 +0000
@@ -0,0 +1,58 @@
+#include "ai_event.hpp"
+
+namespace SQConvert {
+	/* Allow enums to be used as Squirrel parameters */
+	template <> AIEvent::AIEventType GetParam(ForceType<AIEvent::AIEventType>, HSQUIRRELVM vm, int index) { SQInteger tmp; sq_getinteger(vm, index, &tmp); return (AIEvent::AIEventType)tmp; }
+	template <> int Return<AIEvent::AIEventType>(HSQUIRRELVM vm, AIEvent::AIEventType res) { sq_pushinteger(vm, (int32)res); return 1; }
+
+	/* Allow AIEvent to be used as Squirrel parameter */
+	template <> AIEvent *GetParam(ForceType<AIEvent *>, HSQUIRRELVM vm, int index) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return  (AIEvent *)instance; }
+	template <> AIEvent &GetParam(ForceType<AIEvent &>, HSQUIRRELVM vm, int index) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return *(AIEvent *)instance; }
+	template <> const AIEvent *GetParam(ForceType<const AIEvent *>, HSQUIRRELVM vm, int index) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return  (AIEvent *)instance; }
+	template <> const AIEvent &GetParam(ForceType<const AIEvent &>, HSQUIRRELVM vm, int index) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return *(AIEvent *)instance; }
+	template <> int Return<AIEvent *>(HSQUIRRELVM vm, AIEvent *res) { if (res == NULL) { sq_pushnull(vm); return 1; } res->AddRef(); Squirrel::CreateClassInstanceVM(vm, "AIEvent", res, NULL, DefSQDestructorCallback<AIEvent>); return 1; }
+}; // namespace SQConvert
+
+void SQAIEventRegister(Squirrel *engine) {
+	DefSQClass <AIEvent> SQAIEvent("AIEvent");
+	SQAIEvent.PreRegister(engine);
+	SQAIEvent.AddConstructor<void (AIEvent::*)(AIEvent::AIEventType type), 2>(engine, "xi");
+
+	SQAIEvent.DefSQConst(engine, AIEvent::AI_ET_INVALID,         "AI_ET_INVALID");
+	SQAIEvent.DefSQConst(engine, AIEvent::AI_ET_TEST,            "AI_ET_TEST");
+	SQAIEvent.DefSQConst(engine, AIEvent::AI_ET_CRASHED_VEHICLE, "AI_ET_CRASHED_VEHICLE");
+
+	SQAIEvent.DefSQStaticMethod(engine, &AIEvent::GetClassName, "GetClassName", 1, "x");
+
+	SQAIEvent.DefSQMethod(engine, &AIEvent::GetEventType, "GetEventType", 1, "x");
+
+	SQAIEvent.PostRegister(engine);
+}
+
+namespace SQConvert {
+	/* Allow AIEventController to be used as Squirrel parameter */
+	template <> AIEventController *GetParam(ForceType<AIEventController *>, HSQUIRRELVM vm, int index) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return  (AIEventController *)instance; }
+	template <> AIEventController &GetParam(ForceType<AIEventController &>, HSQUIRRELVM vm, int index) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return *(AIEventController *)instance; }
+	template <> const AIEventController *GetParam(ForceType<const AIEventController *>, HSQUIRRELVM vm, int index) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return  (AIEventController *)instance; }
+	template <> const AIEventController &GetParam(ForceType<const AIEventController &>, HSQUIRRELVM vm, int index) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return *(AIEventController *)instance; }
+	template <> int Return<AIEventController *>(HSQUIRRELVM vm, AIEventController *res) { if (res == NULL) { sq_pushnull(vm); return 1; } res->AddRef(); Squirrel::CreateClassInstanceVM(vm, "AIEventController", res, NULL, DefSQDestructorCallback<AIEventController>); return 1; }
+}; // namespace SQConvert
+
+void SQAIEventControllerRegister(Squirrel *engine) {
+	DefSQClass <AIEventController> SQAIEventController("AIEventController");
+	SQAIEventController.PreRegister(engine);
+	SQAIEventController.AddConstructor<void (AIEventController::*)(), 1>(engine, "x");
+
+	SQAIEventController.DefSQStaticMethod(engine, &AIEventController::GetClassName,     "GetClassName",     1, "x");
+	SQAIEventController.DefSQStaticMethod(engine, &AIEventController::IsEventWaiting,   "IsEventWaiting",   1, "x");
+	SQAIEventController.DefSQStaticMethod(engine, &AIEventController::GetNextEvent,     "GetNextEvent",     1, "x");
+	SQAIEventController.DefSQStaticMethod(engine, &AIEventController::DisableEvent,     "DisableEvent",     2, "xi");
+	SQAIEventController.DefSQStaticMethod(engine, &AIEventController::EnableEvent,      "EnableEvent",      2, "xi");
+	SQAIEventController.DefSQStaticMethod(engine, &AIEventController::DisableAllEvents, "DisableAllEvents", 1, "x");
+	SQAIEventController.DefSQStaticMethod(engine, &AIEventController::EnableAllEvents,  "EnableAllEvents",  1, "x");
+	SQAIEventController.DefSQStaticMethod(engine, &AIEventController::InsertEvent,      "InsertEvent",      2, "xx");
+	SQAIEventController.DefSQStaticMethod(engine, &AIEventController::Test,             "Test",             1, "x");
+	SQAIEventController.DefSQStaticMethod(engine, &AIEventController::FreeEventPointer, "FreeEventPointer", 1, "x");
+
+	SQAIEventController.PostRegister(engine);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ai/api/ai_event_types.cpp	Thu Jul 19 22:39:43 2007 +0000
@@ -0,0 +1,6 @@
+#include "ai_event_types.hpp"
+
+bool AIEventVehicleCrash::CloneCrashedVehicle(TileIndex depot)
+{
+	return false;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ai/api/ai_event_types.hpp	Thu Jul 19 22:39:43 2007 +0000
@@ -0,0 +1,93 @@
+/* $Id$ */
+
+/** @file ai_event_types.hpp The detailed types of all events */
+
+#ifndef AI_EVENT_TYPES_HPP
+#define AI_EVENT_TYPES_HPP
+
+#include "ai_object.hpp"
+#include "ai_event.hpp"
+
+/**
+ * A simple test event, to see if the event system is working. Triggered via
+ *  AIEventController::Test();
+ */
+class AIEventTest : public AIEvent {
+public:
+	/**
+	 * The name of the class, needed by several sub-processes.
+	 */
+	static const char *GetClassName() { return "AIEventTest"; }
+
+	/**
+	 * Constructor for this event class.
+	 */
+	AIEventTest(uint test) :
+		AIEvent(AI_ET_TEST),
+		test(test)
+	{}
+
+	/**
+	 * Convert an AIEvent to the real instance.
+	 */
+	static AIEventTest *Convert(AIEvent *instance) { return (AIEventTest *)instance; }
+
+	/**
+	 * Return the test value.
+	 */
+	uint GetTest() { return this->test; }
+
+private:
+	uint test;
+};
+
+/**
+ * A vehicle crashed, and because of that this event is triggered.
+ *  It contains both the crash site as the vehicle crashed. It has a nice
+ *  helper that creates a new vehicle in a depot with the same type
+ *  and orders as the crashed one. In case the vehicle type isn't available
+ *  anymore, it will find the next best.
+ */
+class AIEventVehicleCrash : public AIEvent {
+public:
+	/**
+	 * The name of the class, needed by several sub-processes.
+	 */
+	static const char *GetClassName() { return "AIEventVehicleCrash"; }
+
+	/**
+	 * Constructor for this event class.
+	 */
+	AIEventVehicleCrash(VehicleID vehicle, TileIndex crash_site) :
+		AIEvent(AI_ET_CRASHED_VEHICLE),
+		crash_site(crash_site),
+		vehicle(vehicle)
+	{}
+
+	/**
+	 * Convert an AIEvent to the real instance.
+	 */
+	static AIEventVehicleCrash *Convert(AIEvent *instance) { return (AIEventVehicleCrash *)instance; }
+
+	/**
+	 * Get the vehicleID of the crashed vehicle.
+	 */
+	VehicleID GetVehicleID() { return vehicle; }
+
+	/**
+	 * Find the tile the vehicle crashed.
+	 */
+	TileIndex GetCrashSite() { return crash_site; }
+
+	/**
+	 * Clone the crashed vehicle and send it on its way again/
+	 * @param depot the depot to build the vehicle in.
+	 */
+	bool CloneCrashedVehicle(TileIndex depot);
+
+private:
+	TileIndex crash_site;
+	VehicleID vehicle;
+};
+
+#endif /* AI_EVENT_TYPES_HPP */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ai/api/ai_event_types.hpp.sq	Thu Jul 19 22:39:43 2007 +0000
@@ -0,0 +1,47 @@
+#include "ai_event_types.hpp"
+
+namespace SQConvert {
+	/* Allow AIEventTest to be used as Squirrel parameter */
+	template <> AIEventTest *GetParam(ForceType<AIEventTest *>, HSQUIRRELVM vm, int index) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return  (AIEventTest *)instance; }
+	template <> AIEventTest &GetParam(ForceType<AIEventTest &>, HSQUIRRELVM vm, int index) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return *(AIEventTest *)instance; }
+	template <> const AIEventTest *GetParam(ForceType<const AIEventTest *>, HSQUIRRELVM vm, int index) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return  (AIEventTest *)instance; }
+	template <> const AIEventTest &GetParam(ForceType<const AIEventTest &>, HSQUIRRELVM vm, int index) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return *(AIEventTest *)instance; }
+	template <> int Return<AIEventTest *>(HSQUIRRELVM vm, AIEventTest *res) { if (res == NULL) { sq_pushnull(vm); return 1; } res->AddRef(); Squirrel::CreateClassInstanceVM(vm, "AIEventTest", res, NULL, DefSQDestructorCallback<AIEventTest>); return 1; }
+}; // namespace SQConvert
+
+void SQAIEventTestRegister(Squirrel *engine) {
+	DefSQClass <AIEventTest> SQAIEventTest("AIEventTest");
+	SQAIEventTest.PreRegister(engine, "AIEvent");
+	SQAIEventTest.AddConstructor<void (AIEventTest::*)(uint test), 2>(engine, "xi");
+
+	SQAIEventTest.DefSQStaticMethod(engine, &AIEventTest::GetClassName, "GetClassName", 1, "x");
+	SQAIEventTest.DefSQStaticMethod(engine, &AIEventTest::Convert,      "Convert",      2, "xx");
+
+	SQAIEventTest.DefSQMethod(engine, &AIEventTest::GetTest, "GetTest", 1, "x");
+
+	SQAIEventTest.PostRegister(engine);
+}
+
+namespace SQConvert {
+	/* Allow AIEventVehicleCrash to be used as Squirrel parameter */
+	template <> AIEventVehicleCrash *GetParam(ForceType<AIEventVehicleCrash *>, HSQUIRRELVM vm, int index) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return  (AIEventVehicleCrash *)instance; }
+	template <> AIEventVehicleCrash &GetParam(ForceType<AIEventVehicleCrash &>, HSQUIRRELVM vm, int index) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return *(AIEventVehicleCrash *)instance; }
+	template <> const AIEventVehicleCrash *GetParam(ForceType<const AIEventVehicleCrash *>, HSQUIRRELVM vm, int index) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return  (AIEventVehicleCrash *)instance; }
+	template <> const AIEventVehicleCrash &GetParam(ForceType<const AIEventVehicleCrash &>, HSQUIRRELVM vm, int index) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return *(AIEventVehicleCrash *)instance; }
+	template <> int Return<AIEventVehicleCrash *>(HSQUIRRELVM vm, AIEventVehicleCrash *res) { if (res == NULL) { sq_pushnull(vm); return 1; } res->AddRef(); Squirrel::CreateClassInstanceVM(vm, "AIEventVehicleCrash", res, NULL, DefSQDestructorCallback<AIEventVehicleCrash>); return 1; }
+}; // namespace SQConvert
+
+void SQAIEventVehicleCrashRegister(Squirrel *engine) {
+	DefSQClass <AIEventVehicleCrash> SQAIEventVehicleCrash("AIEventVehicleCrash");
+	SQAIEventVehicleCrash.PreRegister(engine, "AIEvent");
+	SQAIEventVehicleCrash.AddConstructor<void (AIEventVehicleCrash::*)(VehicleID vehicle, TileIndex crash_site), 3>(engine, "xii");
+
+	SQAIEventVehicleCrash.DefSQStaticMethod(engine, &AIEventVehicleCrash::GetClassName, "GetClassName", 1, "x");
+	SQAIEventVehicleCrash.DefSQStaticMethod(engine, &AIEventVehicleCrash::Convert,      "Convert",      2, "xx");
+
+	SQAIEventVehicleCrash.DefSQMethod(engine, &AIEventVehicleCrash::GetVehicleID,        "GetVehicleID",        1, "x");
+	SQAIEventVehicleCrash.DefSQMethod(engine, &AIEventVehicleCrash::GetCrashSite,        "GetCrashSite",        1, "x");
+	SQAIEventVehicleCrash.DefSQMethod(engine, &AIEventVehicleCrash::CloneCrashedVehicle, "CloneCrashedVehicle", 2, "xi");
+
+	SQAIEventVehicleCrash.PostRegister(engine);
+}
--- a/src/ai/api/ai_object.cpp	Thu Jul 19 22:26:26 2007 +0000
+++ b/src/ai/api/ai_object.cpp	Thu Jul 19 22:39:43 2007 +0000
@@ -72,15 +72,22 @@
 	return AIObject::GetDoCommandStruct(_current_player)->new_sign_id;
 }
 
-void **AIObject::GetEventPointer()
+void *&AIObject::GetEventPointer()
 {
-	return &AIObject::GetDoCommandStruct(_current_player)->event_data;
+	return AIObject::GetDoCommandStruct(_current_player)->event_data;
 }
 
 AIObject::AIDoCommandStruct *AIObject::GetDoCommandStruct(PlayerID player)
 {
 	/* Storage for data on per-AI level */
 	static AIObject::AIDoCommandStruct command_struct[MAX_PLAYERS];
+	static bool initialized = false;
+
+	/* Make sure all memory is NULL when we start */
+	if (!initialized) {
+		initialized = true;
+		memset(&command_struct, 0, sizeof(command_struct));
+	}
 
 	return &command_struct[player];
 }
@@ -91,6 +98,8 @@
 	command_struct->mode = NULL;
 	command_struct->delay = 1;
 	command_struct->costs = CommandCost();
+	if (command_struct->event_data != NULL) AIEventController::FreeEventPointer();
+	command_struct->event_data = NULL;
 }
 
 bool AIObject::DoCommand(TileIndex tile, uint32 p1, uint32 p2, uint procc)
--- a/src/ai/api/ai_object.hpp	Thu Jul 19 22:26:26 2007 +0000
+++ b/src/ai/api/ai_object.hpp	Thu Jul 19 22:39:43 2007 +0000
@@ -96,7 +96,7 @@
 	/**
 	 * Get the pointer to store event data in.
 	 */
-	static void **GetEventPointer();
+	static void *&GetEventPointer();
 
 public:
 	/**
--- a/src/aircraft_cmd.cpp	Thu Jul 19 22:26:26 2007 +0000
+++ b/src/aircraft_cmd.cpp	Thu Jul 19 22:39:43 2007 +0000
@@ -33,6 +33,7 @@
 #include "date.h"
 #include "spritecache.h"
 #include "cargotype.h"
+#include "ai/ai.h"
 
 void Aircraft::UpdateDeltaXY(Direction direction)
 {
@@ -1438,6 +1439,8 @@
 		newsitem = STR_A034_PLANE_CRASH_DIE_IN_FIREBALL;
 	}
 
+	AI_Event(v->owner, new AIEventVehicleCrash(v->index, v->tile));
+
 	SetDParam(1, st->index);
 	AddNewsItem(newsitem,
 		NEWS_FLAGS(NM_THIN, NF_VIEWPORT|NF_VEHICLE, NT_ACCIDENT, 0),
--- a/src/roadveh_cmd.cpp	Thu Jul 19 22:26:26 2007 +0000
+++ b/src/roadveh_cmd.cpp	Thu Jul 19 22:39:43 2007 +0000
@@ -36,6 +36,7 @@
 #include "yapf/yapf.h"
 #include "date.h"
 #include "cargotype.h"
+#include "ai/ai.h"
 
 static const uint16 _roadveh_images[63] = {
 	0xCD4, 0xCDC, 0xCE4, 0xCEC, 0xCF4, 0xCFC, 0xD0C, 0xD14,
@@ -688,6 +689,8 @@
 
 	InvalidateWindowWidget(WC_VEHICLE_VIEW, v->index, STATUS_BAR);
 
+	AI_Event(v->owner, new AIEventVehicleCrash(v->index, v->tile));
+
 	SetDParam(0, pass);
 	AddNewsItem(
 		(pass == 1) ?
--- a/src/train_cmd.cpp	Thu Jul 19 22:26:26 2007 +0000
+++ b/src/train_cmd.cpp	Thu Jul 19 22:39:43 2007 +0000
@@ -40,6 +40,7 @@
 #include "date.h"
 #include "cargotype.h"
 #include "group.h"
+#include "ai/ai.h"
 
 static bool TrainCheckIfLineEnds(Vehicle *v);
 static void TrainController(Vehicle *v, bool update_image);
@@ -2764,6 +2765,7 @@
 	/* any dead -> no crash */
 	if (tcc.num == 0) return;
 
+	AI_Event(v->owner, new AIEventVehicleCrash(v->index, v->tile));
 	SetDParam(0, tcc.num);
 	AddNewsItem(STR_8868_TRAIN_CRASH_DIE_IN_FIREBALL,
 		NEWS_FLAGS(NM_THIN, NF_VIEWPORT | NF_VEHICLE, NT_ACCIDENT, 0),