(svn r9179) [NoAI] -Add: added templates that makes adding classes to squirrel very easy noai
authortruelight
Wed, 14 Mar 2007 20:22:07 +0000
branchnoai
changeset 9387 4255a0a2d272
parent 9386 c8aa2ae117f5
child 9388 032008c3f6e3
(svn r9179) [NoAI] -Add: added templates that makes adding classes to squirrel very easy
-Update: SQNoAI does now exactly what NoAI does
bin/ai/SQNoAI/main.nut
projects/openttd.vcproj
projects/openttd_vs80.vcproj
source.list
src/ai/core/ai_base.hpp
src/ai/core/ai_company.hpp
src/ai/core/ai_map.hpp
src/ai/core/ai_town.hpp
src/ai/squirrel/convert.cpp
src/ai/squirrel/convert.hpp
src/ai/squirrel/squirrel.cpp
src/ai/squirrel/squirrel.hpp
src/ai/squirrel/squirrel_class.hpp
--- a/bin/ai/SQNoAI/main.nut	Wed Mar 14 12:42:37 2007 +0000
+++ b/bin/ai/SQNoAI/main.nut	Wed Mar 14 20:22:07 2007 +0000
@@ -1,12 +1,45 @@
 /* Extend our own AI from AIController so we can access basic things. */
 class SQNoAI extends AIController {
+	base = AIBase();
+	company = AICompany();
+	map = AIMap();
+	town = AITown();
+
 	function GameLoop();
 }
 
 /* Define the GameLoop. This is called every tick. */
 function SQNoAI::GameLoop()
 {
-	print("GameLoop " + this.GetTick());
+	if (this.GetTick() == 1) {
+		this.company.SetCompanyName("SQNoAI");
+		print("Map size: " + this.map.GetMapSizeX() + " by " +
+				this.map.GetMapSizeY() + ", " + this.map.GetMapSize() + " tiles");
+	}
+
+	if (this.GetTick() % 10 == 0) {
+		local company_name = this.company.GetCompanyName();
+		print(company_name + " has loaned " + this.company.GetLoanAmount());
+	}
+
+	if (this.GetTick() % 14 == 0) {
+		local level = (this.company.GetMaxLoanAmount() / this.company.GetLoanInterval()) + 1;
+		this.company.SetLoanAmount(this.base.RandomRange(level) * this.company.GetLoanInterval());
+	}
+
+	if (this.GetTick() % 13 == 0) {
+		local town_id = this.base.RandomRange(this.town.GetMaxTownID());
+		if (!this.town.IsValidTown(town_id)) {
+			print("He, an invalid town ID... could happen :(");
+		} else {
+			local town_name = this.town.GetName(town_id);
+			local t = this.town.GetLocation(town_id);
+
+			print("Town " + town_name + " [" + town_id + " of " + this.town.GetTownCount() +
+					"] at (" + this.map.GetTileX(t) + ", " + this.map.GetTileY(t) + ") with " +
+					this.town.GetPopulation(town_id) + " inhabitants");
+		}
+	}
 }
 
 /* Tell OpenTTD we have an AI */
--- a/projects/openttd.vcproj	Wed Mar 14 12:42:37 2007 +0000
+++ b/projects/openttd.vcproj	Wed Mar 14 20:22:07 2007 +0000
@@ -954,11 +954,20 @@
 				RelativePath=".\..\src\ai\NoAI\NoAI.hpp">
 			</File>
 			<File
+				RelativePath=".\..\src\ai\squirrel\convert.cpp">
+			</File>
+			<File
+				RelativePath=".\..\src\ai\squirrel\convert.hpp">
+			</File>
+			<File
 				RelativePath=".\..\src\ai\squirrel\squirrel.cpp">
 			</File>
 			<File
 				RelativePath=".\..\src\ai\squirrel\squirrel.hpp">
 			</File>
+			<File
+				RelativePath=".\..\src\ai\squirrel\squirrel_class.hpp">
+			</File>
 		</Filter>
 		<Filter
 			Name="NewGRF"
--- a/projects/openttd_vs80.vcproj	Wed Mar 14 12:42:37 2007 +0000
+++ b/projects/openttd_vs80.vcproj	Wed Mar 14 20:22:07 2007 +0000
@@ -1492,6 +1492,14 @@
 				>
 			</File>
 			<File
+				RelativePath=".\..\src\ai\squirrel\convert.cpp"
+				>
+			</File>
+			<File
+				RelativePath=".\..\src\ai\squirrel\convert.hpp"
+				>
+			</File>
+			<File
 				RelativePath=".\..\src\ai\squirrel\squirrel.cpp"
 				>
 			</File>
@@ -1499,6 +1507,10 @@
 				RelativePath=".\..\src\ai\squirrel\squirrel.hpp"
 				>
 			</File>
+			<File
+				RelativePath=".\..\src\ai\squirrel\squirrel_class.hpp"
+				>
+			</File>
 		</Filter>
 		<Filter
 			Name="NewGRF"
--- a/source.list	Wed Mar 14 12:42:37 2007 +0000
+++ b/source.list	Wed Mar 14 20:22:07 2007 +0000
@@ -289,8 +289,11 @@
 # AIs
 ai/NoAI/NoAI.cpp
 ai/NoAI/NoAI.hpp
+ai/squirrel/convert.cpp
+ai/squirrel/convert.hpp
 ai/squirrel/squirrel.cpp
 ai/squirrel/squirrel.hpp
+ai/squirrel/squirrel_class.hpp
 
 # NewGRF
 newgrf.cpp
--- a/src/ai/core/ai_base.hpp	Wed Mar 14 12:42:37 2007 +0000
+++ b/src/ai/core/ai_base.hpp	Wed Mar 14 20:22:07 2007 +0000
@@ -39,4 +39,15 @@
 	bool Chance(uint out, uint max);
 };
 
+#ifdef SQUIRREL_CLASS
+void SQAIBaseRegister(HSQUIRRELVM vm) {
+	DefSQClass <AIBase> SQAIBase("AIBase");
+	SQAIBase.PreRegister(vm);
+	SQAIBase.DefSQFunction(vm, &AIBase::Random,      "Random");
+	SQAIBase.DefSQFunction(vm, &AIBase::RandomRange, "RandomRange");
+	SQAIBase.DefSQFunction(vm, &AIBase::Chance,      "Chance");
+	SQAIBase.PostRegister(vm);
+}
+#endif /* SQUIRREL_CLASS */
+
 #endif /* AI_BASE_HPP */
--- a/src/ai/core/ai_company.hpp	Wed Mar 14 12:42:37 2007 +0000
+++ b/src/ai/core/ai_company.hpp	Wed Mar 14 20:22:07 2007 +0000
@@ -71,4 +71,20 @@
 	bool SetLoanAmount(int32 loan);
 };
 
+#ifdef SQUIRREL_CLASS
+void SQAICompanyRegister(HSQUIRRELVM vm) {
+	DefSQClass <AICompany> SQAICompany("AICompany");
+	SQAICompany.PreRegister(vm);
+	SQAICompany.DefSQFunction(vm, &AICompany::SetCompanyName,   "SetCompanyName");
+	SQAICompany.DefSQFunction(vm, &AICompany::GetCompanyName,   "GetCompanyName");
+	SQAICompany.DefSQFunction(vm, &AICompany::GetCompanyValue,  "GetCompanyValue");
+	SQAICompany.DefSQFunction(vm, &AICompany::GetBankBalance,   "GetBankBalance");
+	SQAICompany.DefSQFunction(vm, &AICompany::GetLoanAmount,    "GetLoanAmount");
+	SQAICompany.DefSQFunction(vm, &AICompany::GetMaxLoanAmount, "GetMaxLoanAmount");
+	SQAICompany.DefSQFunction(vm, &AICompany::GetLoanInterval,  "GetLoanInterval");
+	SQAICompany.DefSQFunction(vm, &AICompany::SetLoanAmount,    "SetLoanAmount");
+	SQAICompany.PostRegister(vm);
+}
+#endif /* SQUIRREL_CLASS */
+
 #endif /* AI_COMPANY_HPP */
--- a/src/ai/core/ai_map.hpp	Wed Mar 14 12:42:37 2007 +0000
+++ b/src/ai/core/ai_map.hpp	Wed Mar 14 20:22:07 2007 +0000
@@ -56,4 +56,18 @@
 	uint32 GetTileY(TileIndex t);
 };
 
+#ifdef SQUIRREL_CLASS
+void SQAIMapRegister(HSQUIRRELVM vm) {
+	DefSQClass <AIMap> SQAIMap("AIMap");
+	SQAIMap.PreRegister(vm);
+	SQAIMap.DefSQFunction(vm, &AIMap::IsValidTile, "IsValidTile");
+	SQAIMap.DefSQFunction(vm, &AIMap::GetMapSize,  "GetMapSize");
+	SQAIMap.DefSQFunction(vm, &AIMap::GetMapSizeX, "GetMapSizeX");
+	SQAIMap.DefSQFunction(vm, &AIMap::GetMapSizeY, "GetMapSizeY");
+	SQAIMap.DefSQFunction(vm, &AIMap::GetTileX,    "GetTileX");
+	SQAIMap.DefSQFunction(vm, &AIMap::GetTileY,    "GetTileY");
+	SQAIMap.PostRegister(vm);
+}
+#endif /* SQUIRREL_CLASS */
+
 #endif /* AI_MAP_HPP */
--- a/src/ai/core/ai_town.hpp	Wed Mar 14 12:42:37 2007 +0000
+++ b/src/ai/core/ai_town.hpp	Wed Mar 14 20:22:07 2007 +0000
@@ -58,4 +58,18 @@
 	TileIndex GetLocation(TownID town_id);
 };
 
+#ifdef SQUIRREL_CLASS
+void SQAITownRegister(HSQUIRRELVM vm) {
+	DefSQClass <AITown> SQAITown("AITown");
+	SQAITown.PreRegister(vm);
+	SQAITown.DefSQFunction(vm, &AITown::GetMaxTownID,  "GetMaxTownID");
+	SQAITown.DefSQFunction(vm, &AITown::GetTownCount,  "GetTownCount");
+	SQAITown.DefSQFunction(vm, &AITown::IsValidTown,   "IsValidTown");
+	SQAITown.DefSQFunction(vm, &AITown::GetName,       "GetName");
+	SQAITown.DefSQFunction(vm, &AITown::GetPopulation, "GetPopulation");
+	SQAITown.DefSQFunction(vm, &AITown::GetLocation,   "GetLocation");
+	SQAITown.PostRegister(vm);
+}
+#endif /* SQUIRREL_CLASS */
+
 #endif /* AI_TOWN_HPP */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ai/squirrel/convert.cpp	Wed Mar 14 20:22:07 2007 +0000
@@ -0,0 +1,24 @@
+/* $Id$ */
+
+/** @file convert.cpp parts of the implementation of the class for convert code */
+
+#include "squirrel.hpp"
+#include "convert.hpp"
+
+template <> int SQConvert::Return<uint8> (HSQUIRRELVM vm, uint8 res)  { sq_pushinteger(vm, res); return 1; }
+template <> int SQConvert::Return<uint16>(HSQUIRRELVM vm, uint16 res) { sq_pushinteger(vm, res); return 1; }
+template <> int SQConvert::Return<uint32>(HSQUIRRELVM vm, uint32 res) { sq_pushinteger(vm, res); return 1; }
+template <> int SQConvert::Return<int8>  (HSQUIRRELVM vm, int8 res)   { sq_pushinteger(vm, res); return 1; }
+template <> int SQConvert::Return<int16> (HSQUIRRELVM vm, int16 res)  { sq_pushinteger(vm, res); return 1; }
+template <> int SQConvert::Return<int32> (HSQUIRRELVM vm, int32 res)  { sq_pushinteger(vm, res); return 1; }
+template <> int SQConvert::Return<bool>  (HSQUIRRELVM vm, bool res)   { sq_pushbool   (vm, res); return 1; }
+template <> int SQConvert::Return<char *>(HSQUIRRELVM vm, char *res)  { sq_pushstring (vm, res, strlen(res)); free(res); return 1; }
+
+template <> uint8       SQConvert::GetParam(ForceType<uint8>       , HSQUIRRELVM vm, int index) { SQInteger     tmp; sq_getinteger(vm, index, &tmp); return tmp; }
+template <> uint16      SQConvert::GetParam(ForceType<uint16>      , HSQUIRRELVM vm, int index) { SQInteger     tmp; sq_getinteger(vm, index, &tmp); return tmp; }
+template <> uint32      SQConvert::GetParam(ForceType<uint32>      , HSQUIRRELVM vm, int index) { SQInteger     tmp; sq_getinteger(vm, index, &tmp); return tmp; }
+template <> int8        SQConvert::GetParam(ForceType<int8>        , HSQUIRRELVM vm, int index) { SQInteger     tmp; sq_getinteger(vm, index, &tmp); return tmp; }
+template <> int16       SQConvert::GetParam(ForceType<int16>       , HSQUIRRELVM vm, int index) { SQInteger     tmp; sq_getinteger(vm, index, &tmp); return tmp; }
+template <> int32       SQConvert::GetParam(ForceType<int32>       , HSQUIRRELVM vm, int index) { SQInteger     tmp; sq_getinteger(vm, index, &tmp); return tmp; }
+template <> bool        SQConvert::GetParam(ForceType<bool>        , HSQUIRRELVM vm, int index) { SQBool        tmp; sq_getbool   (vm, index, &tmp); return tmp; }
+template <> const char *SQConvert::GetParam(ForceType<const char *>, HSQUIRRELVM vm, int index) { const SQChar *tmp; sq_getstring (vm, index, &tmp); return tmp; }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ai/squirrel/convert.hpp	Wed Mar 14 20:22:07 2007 +0000
@@ -0,0 +1,95 @@
+/* $Id$ */
+
+/** @file convert.hpp declarations and parts of the implementation of the class for convert code */
+
+#ifndef SQUIRREL_CONVERT_HPP
+#define SQUIRREL_CONVERT_HPP
+
+/**
+ * The Squirrel convert class. It doesn't have an instance, the methods only are used.
+ */
+class SQConvert {
+public:
+	/**
+	 * Special class to make it possible for the compiler to pick the correct GetParam().
+	 */
+	template <typename T> class ForceType { };
+
+	/**
+	 * To return a value to squirrel, we call this function. It converts to the right format.
+	 */
+	template <typename T> static int Return(HSQUIRRELVM vm, T t);
+
+	/**
+	 * To get a param from squirrel, we call this function. It converts to the right format.
+	 */
+	template <typename T> static T GetParam(ForceType<T>, HSQUIRRELVM vm, int index);
+
+	/**
+	 * The real C++ caller for function with a return value and 0 params.
+	 */
+	template <typename CL, typename RT>
+	static int SQCall(CL *instance, RT (CL::*func)(), HSQUIRRELVM vm)
+	{
+		return Return(vm, (instance->*func)());
+	}
+
+	/**
+	 * The real C++ caller for function with a return value and 1 param.
+	 */
+	template <typename CL, typename RT, typename P1>
+	static int SQCall(CL *instance, RT (CL::*func)(P1), HSQUIRRELVM vm)
+	{
+		return Return(vm, (instance->*func)(
+						GetParam(ForceType<P1>(), vm, 2)
+					));
+	}
+
+	/**
+	 * The real C++ caller for function with a return value and 2 params.
+	 */
+	template <typename CL, typename RT, typename P1, typename P2>
+	static int SQCall(CL *instance, RT (CL::*func)(P1, P2), HSQUIRRELVM vm)
+	{
+		return Return(vm, (instance->*func)(
+						GetParam(ForceType<P1>(), vm, 2),
+						GetParam(ForceType<P2>(), vm, 3)
+					));
+	}
+
+	/**
+	 * The real C++ caller for function with a return value and 3 params.
+	 */
+	template <typename CL, typename RT, typename P1, typename P2, typename P3>
+	static int SQCall(CL *instance, RT (CL::*func)(P1, P2, P3), HSQUIRRELVM vm)
+	{
+		return Return(vm, (instance->*func)(
+						GetParam(ForceType<P1>(), vm, 2),
+						GetParam(ForceType<P2>(), vm, 3),
+						GetParam(ForceType<P3>(), vm, 4)
+					));
+	}
+
+	/**
+	 * A general template for all callback functions from Squirrel.
+	 *  In here the function_proc is recovered, and the SQCall is called that
+	 *  can handle this exact amount of params.
+	 */
+	template <typename CL, typename Func>
+	static SQInteger DefSQCallback(HSQUIRRELVM vm)
+	{
+		/* Find the amount of params we got */
+		int nparam = sq_gettop(vm);
+		SQUserPointer ptr = NULL;
+		SQUserPointer instance = NULL;
+
+		/* Get the 'real' instance of this class */
+		sq_getinstanceup(vm, 1, &instance, 0);
+		/* Get the real function pointer */
+		sq_getuserdata(vm, nparam, &ptr, 0);
+		/* Deligate it to a template that can handle this specific function */
+		return SQCall((CL *)instance, *(Func *)ptr, vm);
+	}
+};
+
+#endif /* SQUIRREL_CONVERT_HPP */
--- a/src/ai/squirrel/squirrel.cpp	Wed Mar 14 12:42:37 2007 +0000
+++ b/src/ai/squirrel/squirrel.cpp	Wed Mar 14 20:22:07 2007 +0000
@@ -8,6 +8,14 @@
 #include <stdarg.h>
 #include "../../debug.h"
 
+#define SQUIRREL_CLASS
+#include "convert.hpp"
+#include "squirrel_class.hpp"
+#include "../core/ai_base.hpp"
+#include "../core/ai_company.hpp"
+#include "../core/ai_map.hpp"
+#include "../core/ai_town.hpp"
+
 /**
  * Here we handle errors that come from squirrel.
  */
@@ -63,16 +71,16 @@
 
 SQInteger Squirrel::SQGetTick(HSQUIRRELVM vm)
 {
-	Squirrel *self;
+	SQUserPointer ptr;
 
 	/* Find the 'self' pointer */
-	if (SQ_FAILED(sq_getinstanceup(vm, 1, (SQUserPointer *)&self, 0))) {
+	if (SQ_FAILED(sq_getinstanceup(vm, 1, &ptr, 0))) {
 		DEBUG(ai, 0, "[squirrel] Failed to find the instance-pointer");
 		return SQ_ERROR;
 	}
 
 	/* Return the value */
-	sq_pushinteger(vm, self->GetTick());
+	sq_pushinteger(vm, ((Squirrel *)ptr)->GetTick());
 	return 1;
 }
 
@@ -174,6 +182,11 @@
 	sq_pushroottable(this->vm);
 	this->SQAddMethod("RegisterAI", &Squirrel::SQRegisterAI, 2, "ts");
 
+	SQAIBaseRegister(this->vm);
+	SQAICompanyRegister(this->vm);
+	SQAIMapRegister(this->vm);
+	SQAITownRegister(this->vm);
+
 	/* Reset top */
 	sq_settop(this->vm, top);
 }
--- a/src/ai/squirrel/squirrel.hpp	Wed Mar 14 12:42:37 2007 +0000
+++ b/src/ai/squirrel/squirrel.hpp	Wed Mar 14 20:22:07 2007 +0000
@@ -6,7 +6,6 @@
 #define SQUIRREL_HPP
 
 #include "../core/ai_controller.hpp"
-#include "../core/ai_base.hpp"
 #include <squirrel.h>
 
 class Squirrel: public AIController {
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ai/squirrel/squirrel_class.hpp	Wed Mar 14 20:22:07 2007 +0000
@@ -0,0 +1,81 @@
+#ifndef SQUIRREL_CLASS_HPP
+#define SQUIRREL_CLASS_HPP
+
+/**
+ * The template to define classes in Squirrel. It takes care of the creation
+ *  and calling of such classes, to make the AI Layer cleaner while having a
+ *  powerful script as possible AI language.
+ */
+template <class CL>
+class DefSQClass {
+private:
+	const char *classname;
+
+public:
+	DefSQClass(const char *_classname) :
+		classname(_classname)
+	{}
+
+	/**
+	 * The destructor, to delete the real instance.
+	 */
+	static SQInteger Destructor(SQUserPointer p, SQInteger size)
+	{
+		/* Remove the real instance too */
+		if (p != NULL) delete (CL *)p;
+		return 0;
+	}
+
+	/**
+	 * The constructor, to create the real instance.
+	 */
+	static SQInteger Constructor(HSQUIRRELVM vm)
+	{
+		/* Create the real instance */
+		CL *instance = new CL();
+		sq_setinstanceup(vm, -1, instance);
+		sq_setreleasehook(vm, -1, &DefSQClass::Destructor);
+		return 0;
+	}
+
+	/**
+	 * This defines a method inside a class for Squirrel.
+	 */
+	template <typename Func>
+	void DefSQFunction(HSQUIRRELVM vm, Func function_proc, const char *function_name)
+	{
+//		int nparam = 1;
+//		const char *params = "x";
+		void *ptr;
+
+		sq_pushstring(vm, function_name, -1);
+		/* Store the real function-pointer in the user-data */
+		ptr = sq_newuserdata(vm, sizeof(function_proc));
+		memcpy(ptr, &function_proc, sizeof(function_proc));
+		sq_newclosure(vm, SQConvert::DefSQCallback<CL, Func>, 1);
+//		sq_setparamscheck(vm, nparam, params);
+		sq_createslot(vm, -3);
+	}
+
+	void PreRegister(HSQUIRRELVM vm)
+	{
+		/* Prepare the new class */
+		sq_pushroottable(vm);
+		sq_pushstring(vm, this->classname, -1);
+		sq_newclass(vm, SQFalse);
+
+		/* Register a constructor */
+		sq_pushstring(vm, "constructor", -1);
+		sq_newclosure(vm, &DefSQClass::Constructor, 0);
+//		sq_setparamscheck(vm, nparam, params);
+		sq_createslot(vm, -3);
+	}
+
+	void PostRegister(HSQUIRRELVM vm)
+	{
+		/* Create the class and all his methods */
+		sq_createslot(vm, -3);
+	}
+};
+
+#endif /* SQUIRREL_CLASS_HPP */