(svn r9278) [NoAI] -Add: added AISettings which adds the function to control the Delay-value on DoCommands noai
authortruelight
Sun, 18 Mar 2007 16:02:23 +0000
branchnoai
changeset 9450 d675836e865c
parent 9449 8b688e6517d0
child 9451 7629656423cc
(svn r9278) [NoAI] -Add: added AISettings which adds the function to control the Delay-value on DoCommands
-Add: added AIExecMode and AITestMode which allows to switch the DoCommand mode
source.list
src/ai/ai_squirrel.cpp
src/ai/ai_threads.cpp
src/ai/api/ai_execmode.cpp
src/ai/api/ai_execmode.hpp
src/ai/api/ai_object.cpp
src/ai/api/ai_object.hpp
src/ai/api/ai_settings.cpp
src/ai/api/ai_settings.hpp
src/ai/api/ai_testmode.cpp
src/ai/api/ai_testmode.hpp
--- a/source.list	Sun Mar 18 14:23:26 2007 +0000
+++ b/source.list	Sun Mar 18 16:02:23 2007 +0000
@@ -312,20 +312,26 @@
 ai/api/ai_cargo.hpp
 ai/api/ai_company.hpp
 ai/api/ai_controller.hpp
+ai/api/ai_execmode.hpp
 ai/api/ai_industry.hpp
 ai/api/ai_map.hpp
 ai/api/ai_object.hpp
+ai/api/ai_settings.hpp
 ai/api/ai_town.hpp
+ai/api/ai_testmode.hpp
 
 # AI API Implementation
 ai/api/ai_base.cpp
 ai/api/ai_cargo.cpp
 ai/api/ai_company.cpp
 ai/api/ai_controller.cpp
+ai/api/ai_execmode.cpp
 ai/api/ai_industry.cpp
 ai/api/ai_map.cpp
 ai/api/ai_object.cpp
+ai/api/ai_settings.cpp
 ai/api/ai_town.cpp
+ai/api/ai_testmode.cpp
 
 # NewGRF
 newgrf.cpp
--- a/src/ai/ai_squirrel.cpp	Sun Mar 18 14:23:26 2007 +0000
+++ b/src/ai/ai_squirrel.cpp	Sun Mar 18 16:02:23 2007 +0000
@@ -24,8 +24,11 @@
 #include "api/ai_cargo.hpp"
 #include "api/ai_controller.hpp"
 #include "api/ai_company.hpp"
+#include "api/ai_execmode.hpp"
 #include "api/ai_industry.hpp"
 #include "api/ai_map.hpp"
+#include "api/ai_settings.hpp"
+#include "api/ai_testmode.hpp"
 #include "api/ai_town.hpp"
 #undef DEFINE_SQUIRREL_CLASS
 
@@ -183,8 +186,11 @@
 	SQAICargoRegister(this->engine);
 	SQAICompanyRegister(this->engine);
 	SQAIControllerRegister(this->engine);
+	SQAIExecModeRegister(this->engine);
 	SQAIIndustryRegister(this->engine);
 	SQAIMapRegister(this->engine);
+	SQAISettingsRegister(this->engine);
+	SQAITestModeRegister(this->engine);
 	SQAITownRegister(this->engine);
 
 	this->engine->SetGlobalPointer(this->engine);
--- a/src/ai/ai_threads.cpp	Sun Mar 18 14:23:26 2007 +0000
+++ b/src/ai/ai_threads.cpp	Sun Mar 18 16:02:23 2007 +0000
@@ -145,7 +145,12 @@
 	void Resume()
 	{
 		assert(this->state == SUSPENDED);
-		this->ticks_to_sleep = 0;
+		/* Normally the ticks_to_sleep hangs at -1 for MP. Possible the MP is
+		 *  faster then the delay requested by the user. In this case the value
+		 *  is lower. To let the normal delay system kick in, we reverse the value
+		 *  of ticks_to_sleep. But now it doesn't directly continue when the value
+		 *  was 'hanging', so we substract 1 and it all works fine. */
+		this->ticks_to_sleep = -this->ticks_to_sleep - 1;
 	}
 
 	/**
@@ -160,6 +165,12 @@
 		assert(this->state == SUSPENDED);
 		this->controller->IncreaseTick();
 
+		/* If the value is < -1, the user wants a delay which might exceed the delay
+		 *  of the MP. Therefor we keep adding value to ticks_to_sleep till it
+		 *  reaches -1, and we 'hang' it there infinitely, until the MP commands
+		 *  comes in. In the case it happens sooner, the Resume() codes handles it
+		 *  nicely and makes the value positive with the remaining ticks to wait. */
+		if (this->ticks_to_sleep < -1) this->ticks_to_sleep++;
 		if (this->ticks_to_sleep < 0) return;   // We have to wait infinitely
 		if (--this->ticks_to_sleep > 0) return; // We have to wait a little longer
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ai/api/ai_execmode.cpp	Sun Mar 18 16:02:23 2007 +0000
@@ -0,0 +1,23 @@
+/* $Id$ */
+
+/** @file ai_execmode.cpp class to switch the AI to Execute mode */
+
+#include "ai_execmode.hpp"
+
+bool AIExecMode::ModeProc(TileIndex tile, uint32 p1, uint32 p2, uint32 flags, uint procc)
+{
+	/* In execution mode we only return 'true', telling the DoCommand it
+	 *  should continue with the real execution of the command. */
+	return true;
+}
+
+AIExecMode::AIExecMode()
+{
+	this->last_mode = this->GetDoCommandMode();
+	this->SetDoCommandMode(&AIExecMode::ModeProc);
+}
+
+AIExecMode::~AIExecMode()
+{
+	this->SetDoCommandMode(this->last_mode);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ai/api/ai_execmode.hpp	Sun Mar 18 16:02:23 2007 +0000
@@ -0,0 +1,48 @@
+/* $Id$ */
+
+/** @file ai_execmode.hpp class to switch the AI to Execute mode */
+
+#ifndef AI_EXECMODE_HPP
+#define AI_EXECMODE_HPP
+
+#include "ai_object.hpp"
+
+/**
+ * Class to switch current mode to Execute mode.
+ * If you create an instance of this class, the mode will be switched to
+ *   Execute. The original mode is stored and recovered from when ever the
+ *   instance is destroyed.
+ * In Execute mode all commands you do are executed for real.
+ */
+class AIExecMode : public AIObject {
+private:
+	AIModeProc *last_mode;
+
+protected:
+	static bool ModeProc(TileIndex tile, uint32 p1, uint32 p2, uint32 flags, uint procc);
+
+public:
+	/**
+	 * Creating instance of this class switches the build mode to Execute.
+	 * @note when the instance is destroyed, he restores the mode that was
+	 *   current when the instance was created!
+	 */
+	AIExecMode();
+
+	/**
+	 * Destroying this instance reset the building mode to the mode it was
+	 *   in when the instance was created.
+	 */
+	~AIExecMode();
+};
+
+#ifdef DEFINE_SQUIRREL_CLASS
+void SQAIExecModeRegister(Squirrel *engine) {
+	DefSQClass <AIExecMode> SQAIExecMode("AIExecMode");
+	SQAIExecMode.PreRegister(engine);
+	SQAIExecMode.AddConstructor(engine);
+	SQAIExecMode.PostRegister(engine);
+}
+#endif /* SQUIRREL_CLASS */
+
+#endif /* AI_EXECMODE_HPP */
--- a/src/ai/api/ai_object.cpp	Sun Mar 18 14:23:26 2007 +0000
+++ b/src/ai/api/ai_object.cpp	Sun Mar 18 16:02:23 2007 +0000
@@ -8,6 +8,27 @@
 #include "../ai.h"
 #include "../ai_threads.h"
 
+void AIObject::SetDoCommandDelay(uint ticks)
+{
+	assert(ticks > 0);
+	AIObject::GetDoCommandStruct(_current_player)->delay = ticks;
+}
+
+uint AIObject::GetDoCommandDelay()
+{
+	return AIObject::GetDoCommandStruct(_current_player)->delay;
+}
+
+void AIObject::SetDoCommandMode(AIModeProc *proc)
+{
+	AIObject::GetDoCommandStruct(_current_player)->mode = proc;
+}
+
+AIModeProc *AIObject::GetDoCommandMode()
+{
+	return AIObject::GetDoCommandStruct(_current_player)->mode;
+}
+
 bool AIObject::CmdFailed(int32 res)
 {
 	return ::CmdFailed(res);
@@ -18,6 +39,24 @@
 	return !::CmdFailed(res);
 }
 
+AIObject::AIDoCommandStruct *AIObject::GetDoCommandStruct(PlayerID player)
+{
+	static bool initialized = false;
+	/* Storage for data on per-AI level */
+	static AIObject::AIDoCommandStruct command_struct[MAX_PLAYERS];
+
+	if (!initialized) {
+		initialized = true;
+		/* Set default values */
+		for (int i = 0; i < MAX_PLAYERS; i++) {
+			command_struct[i].mode = NULL;
+			command_struct[i].delay = 1;
+		}
+	}
+
+	return &command_struct[player];
+}
+
 int32 AIObject::DoCommand(TileIndex tile, uint32 p1, uint32 p2, uint32 flags, uint procc)
 {
 	PlayerID old_lp;
@@ -29,8 +68,11 @@
 
 	/* First, do a test-run to see if we can do this */
 	res = ::DoCommand(tile, p1, p2, flags & ~DC_EXEC, procc);
-	/* The command failed, or you didn't want to execute, or you are quering, return */
-	if (this->CmdFailed(res) || !(flags & DC_EXEC) || (flags & DC_QUERY_COST)) return res;
+	/* The command failed, so return */
+	if (this->CmdFailed(res)) return res;
+
+	/* Check what the callback wants us to do */
+	if (this->GetDoCommandMode() != NULL && !this->GetDoCommandMode()(tile, p1, p2, flags, procc)) return res;
 
 	/* Restore _cmd_text */
 	_cmd_text = tmp_cmdtext;
@@ -51,7 +93,7 @@
 		_local_player = old_lp;
 
 		/* Suspend the AI till the command is really executed */
-		AI_SuspendPlayer(_current_player, -1);
+		AI_SuspendPlayer(_current_player, -this->GetDoCommandDelay());
 		/* Check if the callback still agrees with us, else return error */
 		if (!AI_GetCallbackResult(_current_player)) res = CMD_ERROR;
 	} else {
@@ -61,7 +103,7 @@
 		/* For SinglePlayer we execute the command immediatly */
 		::DoCommandP(tile, p1, p2, NULL, procc);
 		/* Suspend the AI player for 1 tick, so it simulates MultiPlayer */
-		AI_SuspendPlayer(_current_player, 1);
+		AI_SuspendPlayer(_current_player, this->GetDoCommandDelay());
 	}
 
 	return res;
--- a/src/ai/api/ai_object.hpp	Sun Mar 18 14:23:26 2007 +0000
+++ b/src/ai/api/ai_object.hpp	Sun Mar 18 16:02:23 2007 +0000
@@ -8,6 +8,8 @@
 #include "../../stdafx.h"
 #include "../../functions.h"
 
+typedef bool (AIModeProc)(TileIndex tile, uint32 p1, uint32 p2, uint32 flags, uint procc);
+
 /**
  * Uper-parent object of all API classes. You should never use this class in
  *   your AI, as it doesn't publish any public functions. It is used
@@ -15,6 +17,17 @@
  *   command processing, and command-validation checks.
  */
 class AIObject {
+private:
+	struct AIDoCommandStruct {
+		AIModeProc *mode;
+		uint delay;
+	};
+
+	/**
+	 * The current mode of the AI players.
+	 */
+	static AIDoCommandStruct *GetDoCommandStruct(PlayerID player);
+
 protected:
 	/**
 	 * Executes a raw DoCommand for the AI.
@@ -30,6 +43,26 @@
 	 * Checks if the result of a DoCommand went okay.
 	 */
 	bool CmdSucceeded(int32 res);
+
+	/**
+	 * Set the current mode of your AI to this proc.
+	 */
+	void SetDoCommandMode(AIModeProc *proc);
+
+	/**
+	 * Get the current mode your AI is currently under.
+	 */
+	AIModeProc *GetDoCommandMode();
+
+	/**
+	 * Set the delay of the DoCommand.
+	 */
+	void SetDoCommandDelay(uint ticks);
+
+	/**
+	 * Get the delay of the DoCommand.
+	 */
+	uint GetDoCommandDelay();
 };
 
 #endif /* AI_OBJECT_HPP */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ai/api/ai_settings.cpp	Sun Mar 18 16:02:23 2007 +0000
@@ -0,0 +1,11 @@
+/* $Id$ */
+
+/** @file ai_settings.cpp everything to change AI settings */
+
+#include "ai_settings.hpp"
+
+void AISettings::SetCommandDelay(uint ticks)
+{
+	if (ticks == 0) return;
+	this->SetDoCommandDelay(ticks);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ai/api/ai_settings.hpp	Sun Mar 18 16:02:23 2007 +0000
@@ -0,0 +1,39 @@
+/* $Id$ */
+
+/** @file ai_settings.hpp everything to change AI settings */
+
+#ifndef AI_SETTINGS_HPP
+#define AI_SETTINGS_HPP
+
+#include "ai_object.hpp"
+
+/**
+ * Class that handles all AI settings related functions.
+ * @note This isn't available from your AI.
+ */
+class AISettings : public AIObject {
+public:
+	/**
+	 * Change the minimum amount of time the AI should be put in suspend mode
+	 *   when you execute a command. Normally in SP this is 1, and in MP it is
+	 *   what ever delay the server has been programmed to delay commands
+	 *   (normally between 1 and 5). To give a more 'real' effect to your AI,
+	 *   you can control that number here.
+	 * @param ticks the minimum amount of ticks to wait.
+	 * @pre ticks should be positive. Too big values will influence performance of the AI.
+	 * @note If the number is lower then the MP setting, the MP setting wins.
+	 */
+	void SetCommandDelay(uint ticks);
+};
+
+#ifdef DEFINE_SQUIRREL_CLASS
+void SQAISettingsRegister(Squirrel *engine) {
+	DefSQClass <AISettings> SQAISettings("AISettings");
+	SQAISettings.PreRegister(engine);
+	SQAISettings.AddConstructor(engine);
+	SQAISettings.DefSQFunction(engine, &AISettings::SetCommandDelay, "SetCommandDelay");
+	SQAISettings.PostRegister(engine);
+}
+#endif /* SQUIRREL_CLASS */
+
+#endif /* AI_SETTINGS_HPP */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ai/api/ai_testmode.cpp	Sun Mar 18 16:02:23 2007 +0000
@@ -0,0 +1,23 @@
+/* $Id$ */
+
+/** @file ai_testmode.cpp class to switch the AI to Testing mode */
+
+#include "ai_testmode.hpp"
+
+bool AITestMode::ModeProc(TileIndex tile, uint32 p1, uint32 p2, uint32 flags, uint procc)
+{
+	/* In test mode we only return 'false', telling the DoCommand it
+		*  should stop after testing the command and return with that result. */
+	return false;
+}
+
+AITestMode::AITestMode()
+{
+	this->last_mode = this->GetDoCommandMode();
+	this->SetDoCommandMode(&AITestMode::ModeProc);
+}
+
+AITestMode::~AITestMode()
+{
+	this->SetDoCommandMode(this->last_mode);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ai/api/ai_testmode.hpp	Sun Mar 18 16:02:23 2007 +0000
@@ -0,0 +1,50 @@
+/* $Id$ */
+
+/** @file ai_testmode.hpp class to switch the AI to Testing mode */
+
+#ifndef AI_TESTMODE_HPP
+#define AI_TESTMODE_HPP
+
+#include "ai_object.hpp"
+
+/**
+ * Class to switch current mode to Testing mode.
+ * If you create an instance of this class, the mode will be switched to
+ *   Testing. The original mode is stored and recovered from when ever the
+ *   instance is destroyed.
+ * In Test mode all the commands you execute aren't really executed. The
+ *   system only checks if it would be able to execute your requests, and what
+ *   the cost would be.
+ */
+class AITestMode : public AIObject {
+private:
+	AIModeProc *last_mode;
+
+protected:
+	static bool ModeProc(TileIndex tile, uint32 p1, uint32 p2, uint32 flags, uint procc);
+
+public:
+	/**
+	 * Creating instance of this class switches the build mode to Testing.
+	 * @note when the instance is destroyed, he restores the mode that was
+	 *   current when the instance was created!
+	 */
+	AITestMode();
+
+	/**
+	 * Destroying this instance reset the building mode to the mode it was
+	 *   in when the instance was created.
+	 */
+	~AITestMode();
+};
+
+#ifdef DEFINE_SQUIRREL_CLASS
+void SQAITestModeRegister(Squirrel *engine) {
+	DefSQClass <AITestMode> SQAITestMode("AITestMode");
+	SQAITestMode.PreRegister(engine);
+	SQAITestMode.AddConstructor(engine);
+	SQAITestMode.PostRegister(engine);
+}
+#endif /* SQUIRREL_CLASS */
+
+#endif /* AI_TESTMODE_HPP */