(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
--- 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 */