(svn r12510) [NoAI] -Add: added AIError, which allows you to catch errors triggered by commands (Morloth)
--- a/bin/ai/regression/regression.nut Mon Mar 31 09:51:47 2008 +0000
+++ b/bin/ai/regression/regression.nut Mon Mar 31 10:55:13 2008 +0000
@@ -680,6 +680,9 @@
print(" BuildRoadDepot(): " + AIRoad.BuildRoadDepot(33411, 33411));
print(" BuildRoadDepot(): " + AIRoad.BuildRoadDepot(33411, 33414));
print(" BuildRoadDepot(): " + AIRoad.BuildRoadDepot(33411, 33412));
+ print(" GetLastError(): " + AIError.GetLastError());
+ print(" GetLastErrorString(): " + AIError.GetLastErrorString());
+ print(" GetErrorCategory(): " + AIError.GetErrorCategory());
print(" IsRoadTile(): " + AIRoad.IsRoadTile(33411));
print(" GetRoadDepotFrontTile(): " + AIRoad.GetRoadDepotFrontTile(33411));
print(" IsRoadDepotTile(): " + AIRoad.IsRoadDepotTile(33411));
--- a/bin/ai/regression/regression.txt Mon Mar 31 09:51:47 2008 +0000
+++ b/bin/ai/regression/regression.txt Mon Mar 31 10:55:13 2008 +0000
@@ -5336,6 +5336,9 @@
BuildRoadDepot(): false
BuildRoadDepot(): true
BuildRoadDepot(): false
+ GetLastError(): 260
+ GetLastErrorString(): ERR_AREA_NOT_CLEAR
+ GetErrorCategory(): 1
IsRoadTile(): false
GetRoadDepotFrontTile(): 33412
IsRoadDepotTile(): true
--- a/projects/openttd_vs80.vcproj Mon Mar 31 09:51:47 2008 +0000
+++ b/projects/openttd_vs80.vcproj Mon Mar 31 10:55:13 2008 +0000
@@ -2196,6 +2196,10 @@
>
</File>
<File
+ RelativePath=".\..\src\ai\api\ai_error.hpp"
+ >
+ </File>
+ <File
RelativePath=".\..\src\ai\api\ai_event.hpp"
>
</File>
@@ -2348,6 +2352,10 @@
>
</File>
<File
+ RelativePath=".\..\src\ai\api\ai_error.cpp"
+ >
+ </File>
+ <File
RelativePath=".\..\src\ai\api\ai_event.cpp"
>
</File>
--- a/projects/openttd_vs90.vcproj Mon Mar 31 09:51:47 2008 +0000
+++ b/projects/openttd_vs90.vcproj Mon Mar 31 10:55:13 2008 +0000
@@ -2193,6 +2193,10 @@
>
</File>
<File
+ RelativePath=".\..\src\ai\api\ai_error.hpp"
+ >
+ </File>
+ <File
RelativePath=".\..\src\ai\api\ai_event.hpp"
>
</File>
@@ -2345,6 +2349,10 @@
>
</File>
<File
+ RelativePath=".\..\src\ai\api\ai_error.cpp"
+ >
+ </File>
+ <File
RelativePath=".\..\src\ai\api\ai_event.cpp"
>
</File>
--- a/source.list Mon Mar 31 09:51:47 2008 +0000
+++ b/source.list Mon Mar 31 10:55:13 2008 +0000
@@ -479,6 +479,7 @@
ai/api/ai_date.hpp
ai/api/ai_engine.hpp
ai/api/ai_enginelist.hpp
+ai/api/ai_error.hpp
ai/api/ai_event.hpp
ai/api/ai_event_types.hpp
ai/api/ai_execmode.hpp
@@ -518,6 +519,7 @@
ai/api/ai_date.cpp
ai/api/ai_engine.cpp
ai/api/ai_enginelist.cpp
+ai/api/ai_error.cpp
ai/api/ai_event.cpp
ai/api/ai_event_types.cpp
ai/api/ai_execmode.cpp
--- a/src/ai/ai_squirrel.cpp Mon Mar 31 09:51:47 2008 +0000
+++ b/src/ai/ai_squirrel.cpp Mon Mar 31 10:55:13 2008 +0000
@@ -8,6 +8,7 @@
#include "../string_func.h"
#include "../fileio.h"
#include "../fios.h"
+#include "table/strings.h"
#include <sys/types.h>
#include <sys/stat.h>
@@ -35,6 +36,7 @@
#include "api/ai_date.hpp.sq"
#include "api/ai_engine.hpp.sq"
#include "api/ai_enginelist.hpp.sq"
+#include "api/ai_error.hpp.sq"
#include "api/ai_event.hpp.sq"
#include "api/ai_event_types.hpp.sq"
#include "api/ai_execmode.hpp.sq"
@@ -224,6 +226,7 @@
SQAIDate_Register(this->engine);
SQAIEngine_Register(this->engine);
SQAIEngineList_Register(this->engine);
+ SQAIError_Register(this->engine);
SQAIEvent_Register(this->engine);
SQAIEventController_Register(this->engine);
SQAIEventSubsidiaryOffer_Register(this->engine);
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ai/api/ai_error.cpp Mon Mar 31 10:55:13 2008 +0000
@@ -0,0 +1,41 @@
+/* $Id$ */
+
+/** @file ai_error.cpp handles the commands-related functions of the AIError class */
+
+#include "ai_error.hpp"
+#include "table/strings.h"
+
+AIError::AIErrorMap AIError::error_map = AIError::AIErrorMap();
+AIError::AIErrorMapString AIError::error_map_string = AIError::AIErrorMapString();
+
+/* static */ uint AIError::GetLastError()
+{
+ if (AIObject::GetLastError() == STR_NULL) return ERR_NONE;
+
+ AIErrorMap::iterator it = error_map.find(AIObject::GetLastError());
+ if (it == error_map.end()) return ERR_UNKNOWN;
+ return (*it).second;
+}
+
+/* static */ const char *AIError::GetLastErrorString()
+{
+ if (AIObject::GetLastError() == STR_NULL) return strdup("ERR_NONE");
+
+ AIErrorMapString::iterator it = error_map_string.find(AIObject::GetLastError());
+ if (it == error_map_string.end()) return strdup("ERR_UNKNOWN");
+ return (*it).second;
+}
+
+/* static */ void AIError::RegisterErrorMap(uint internal_string_id, uint ai_error_msg)
+{
+ error_map[internal_string_id] = ai_error_msg;
+}
+
+/* static */ void AIError::RegisterErrorMapString(uint internal_string_id, const char *message)
+{
+ error_map_string[internal_string_id] = message;
+}
+
+/* static */ AIError::ErrorCategories AIError::GetErrorCategory() {
+ return (AIError::ErrorCategories)(GetLastError() >> (uint)ERR_CAT_BIT_SIZE);
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ai/api/ai_error.hpp Mon Mar 31 10:55:13 2008 +0000
@@ -0,0 +1,102 @@
+/* $Id$ */
+
+/** @file ai_error.hpp Everything to query errors. */
+
+#ifndef AI_ERROR_HPP
+#define AI_ERROR_HPP
+
+#include "ai_object.hpp"
+#include <map>
+
+/**
+ * Class that handles all error related functions.
+ */
+class AIError : public AIObject {
+public:
+ static const char *GetClassName() { return "AIError"; }
+
+ /**
+ * All categories errors can be divided in.
+ */
+ enum ErrorCategories {
+ ERR_CAT_NONE = 0, //!< Error messages not related to any category.
+ ERR_CAT_GENERAL, //!< Error messages related to general things.
+ ERR_CAT_VEHICLE, //!< Error messages related to building / maintaining vehicles.
+
+ /**
+ * DO NOT USE! The error bitsize determines how many errors can be stored in
+ * a category and what the offsets are of all categories.
+ */
+ ERR_CAT_BIT_SIZE = 8,
+ };
+
+ /**
+ * General error messages (0x0100 - 0x0200).
+ */
+ enum ErrorMessages {
+ ERR_NONE = ERR_CAT_NONE << ERR_CAT_BIT_SIZE, //!< Initial error value
+ ERR_UNKNOWN, //!< If an error occured and the error wasn't mapped
+
+ /** Base for general errors */
+ ERR_GENERAL_BASE = ERR_CAT_GENERAL << ERR_CAT_BIT_SIZE,
+
+ /** Not enough cash to perform the previous action */
+ ERR_NOT_ENOUGH_CASH, // [STR_0003_NOT_ENOUGH_CASH_REQUIRES]
+
+ /** Local authority won't allow the previous action */
+ ERR_LOCAL_AUTHORITY_REFUSES, // [STR_2009_LOCAL_AUTHORITY_REFUSES]
+
+ /** The piece of infrastructure you tried to build is already in place */
+ ERR_ALREADY_BUILT, // [STR_1007_ALREADY_BUILT]
+
+ /** Area isn't clear, try to demolish the building on it */
+ ERR_AREA_NOT_CLEAR, // [STR_2004_BUILDING_MUST_BE_DEMOLISHED]
+
+ /** Area is owned by another company */
+ ERR_AREA_IS_OWNED_BY_ANOTHER_COMPANY, // [STR_1024_AREA_IS_OWNED_BY_ANOTHER]
+ };
+
+ /**
+ * Check the membership of the last thrown error.
+ * @return The category the error belongs to.
+ * @note The last throw error can be aquired by calling GetLastError().
+ */
+ static ErrorCategories GetErrorCategory();
+
+ /**
+ * Get the last error.
+ * @return An ErrorMessages enum value.
+ */
+ static uint GetLastError();
+
+ /**
+ * Get the last error in string format (for human readability).
+ * @return An ErrorMessage enum item, as string.
+ */
+ static const char *GetLastErrorString();
+
+ /**
+ * Map an internal OpenTTD error message to it's NoAI equivalent.
+ * @note DO NOT INVOKE THIS METHOD YOURSELF! The calls are autogenerated.
+ * @param internal_string_id The OpenTTD StringID used for an error.
+ * @param ai_error_msg The NoAI equivalent error message.
+ */
+ static void RegisterErrorMap(uint internal_string_id, uint ai_error_msg);
+
+ /**
+ * Map an internal OpenTTD error message to it's NoAI equivalent.
+ * @note DO NOT INVOKE THIS METHOD YOURSELF! The calls are autogenerated.
+ * @param internal_string_id The OpenTTD StringID used for an error.
+ * @param message The string representation of this error message, used for debug purposes.
+ */
+ static void RegisterErrorMapString(uint internal_string_id, const char *message);
+
+private:
+ typedef std::map<uint, uint> AIErrorMap;
+ typedef std::map<uint, const char *> AIErrorMapString;
+
+ static AIErrorMap error_map;
+ static AIErrorMapString error_map_string;
+};
+
+#endif /* AI_ERROR_HPP */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ai/api/ai_error.hpp.sq Mon Mar 31 10:55:13 2008 +0000
@@ -0,0 +1,55 @@
+#include "ai_error.hpp"
+
+namespace SQConvert {
+ /* Allow enums to be used as Squirrel parameters */
+ template <> AIError::ErrorCategories GetParam(ForceType<AIError::ErrorCategories>, HSQUIRRELVM vm, int index) { SQInteger tmp; sq_getinteger(vm, index, &tmp); return (AIError::ErrorCategories)tmp; }
+ template <> int Return<AIError::ErrorCategories>(HSQUIRRELVM vm, AIError::ErrorCategories res) { sq_pushinteger(vm, (int32)res); return 1; }
+ template <> AIError::ErrorMessages GetParam(ForceType<AIError::ErrorMessages>, HSQUIRRELVM vm, int index) { SQInteger tmp; sq_getinteger(vm, index, &tmp); return (AIError::ErrorMessages)tmp; }
+ template <> int Return<AIError::ErrorMessages>(HSQUIRRELVM vm, AIError::ErrorMessages res) { sq_pushinteger(vm, (int32)res); return 1; }
+
+ /* Allow AIError to be used as Squirrel parameter */
+ template <> AIError *GetParam(ForceType<AIError *>, HSQUIRRELVM vm, int index) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return (AIError *)instance; }
+ template <> AIError &GetParam(ForceType<AIError &>, HSQUIRRELVM vm, int index) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return *(AIError *)instance; }
+ template <> const AIError *GetParam(ForceType<const AIError *>, HSQUIRRELVM vm, int index) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return (AIError *)instance; }
+ template <> const AIError &GetParam(ForceType<const AIError &>, HSQUIRRELVM vm, int index) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return *(AIError *)instance; }
+ template <> int Return<AIError *>(HSQUIRRELVM vm, AIError *res) { if (res == NULL) { sq_pushnull(vm); return 1; } res->AddRef(); Squirrel::CreateClassInstanceVM(vm, "AIError", res, NULL, DefSQDestructorCallback<AIError>); return 1; }
+}; // namespace SQConvert
+
+void SQAIError_Register(Squirrel *engine) {
+ DefSQClass <AIError> SQAIError("AIError");
+ SQAIError.PreRegister(engine);
+ SQAIError.AddConstructor<void (AIError::*)(), 1>(engine, "x");
+
+ SQAIError.DefSQConst(engine, AIError::ERR_CAT_NONE, "ERR_CAT_NONE");
+ SQAIError.DefSQConst(engine, AIError::ERR_CAT_GENERAL, "ERR_CAT_GENERAL");
+ SQAIError.DefSQConst(engine, AIError::ERR_CAT_VEHICLE, "ERR_CAT_VEHICLE");
+ SQAIError.DefSQConst(engine, AIError::ERR_CAT_BIT_SIZE, "ERR_CAT_BIT_SIZE");
+ SQAIError.DefSQConst(engine, AIError::ERR_NONE, "ERR_NONE");
+ SQAIError.DefSQConst(engine, AIError::ERR_UNKNOWN, "ERR_UNKNOWN");
+ SQAIError.DefSQConst(engine, AIError::ERR_GENERAL_BASE, "ERR_GENERAL_BASE");
+ SQAIError.DefSQConst(engine, AIError::ERR_NOT_ENOUGH_CASH, "ERR_NOT_ENOUGH_CASH");
+ SQAIError.DefSQConst(engine, AIError::ERR_LOCAL_AUTHORITY_REFUSES, "ERR_LOCAL_AUTHORITY_REFUSES");
+ SQAIError.DefSQConst(engine, AIError::ERR_ALREADY_BUILT, "ERR_ALREADY_BUILT");
+ SQAIError.DefSQConst(engine, AIError::ERR_AREA_NOT_CLEAR, "ERR_AREA_NOT_CLEAR");
+ SQAIError.DefSQConst(engine, AIError::ERR_AREA_IS_OWNED_BY_ANOTHER_COMPANY, "ERR_AREA_IS_OWNED_BY_ANOTHER_COMPANY");
+
+ AIError::RegisterErrorMap (STR_0003_NOT_ENOUGH_CASH_REQUIRES, AIError::ERR_NOT_ENOUGH_CASH);
+ AIError::RegisterErrorMapString(STR_0003_NOT_ENOUGH_CASH_REQUIRES, "ERR_NOT_ENOUGH_CASH");
+ AIError::RegisterErrorMap (STR_2009_LOCAL_AUTHORITY_REFUSES, AIError::ERR_LOCAL_AUTHORITY_REFUSES);
+ AIError::RegisterErrorMapString(STR_2009_LOCAL_AUTHORITY_REFUSES, "ERR_LOCAL_AUTHORITY_REFUSES");
+ AIError::RegisterErrorMap (STR_1007_ALREADY_BUILT, AIError::ERR_ALREADY_BUILT);
+ AIError::RegisterErrorMapString(STR_1007_ALREADY_BUILT, "ERR_ALREADY_BUILT");
+ AIError::RegisterErrorMap (STR_2004_BUILDING_MUST_BE_DEMOLISHED, AIError::ERR_AREA_NOT_CLEAR);
+ AIError::RegisterErrorMapString(STR_2004_BUILDING_MUST_BE_DEMOLISHED, "ERR_AREA_NOT_CLEAR");
+ AIError::RegisterErrorMap (STR_1024_AREA_IS_OWNED_BY_ANOTHER, AIError::ERR_AREA_IS_OWNED_BY_ANOTHER_COMPANY);
+ AIError::RegisterErrorMapString(STR_1024_AREA_IS_OWNED_BY_ANOTHER, "ERR_AREA_IS_OWNED_BY_ANOTHER_COMPANY");
+
+ SQAIError.DefSQStaticMethod(engine, &AIError::GetClassName, "GetClassName", 1, "x");
+ SQAIError.DefSQStaticMethod(engine, &AIError::GetErrorCategory, "GetErrorCategory", 1, "x");
+ SQAIError.DefSQStaticMethod(engine, &AIError::GetLastError, "GetLastError", 1, "x");
+ SQAIError.DefSQStaticMethod(engine, &AIError::GetLastErrorString, "GetLastErrorString", 1, "x");
+ SQAIError.DefSQStaticMethod(engine, &AIError::RegisterErrorMap, "RegisterErrorMap", 3, "xii");
+ SQAIError.DefSQStaticMethod(engine, &AIError::RegisterErrorMapString, "RegisterErrorMapString", 3, "xis");
+
+ SQAIError.PostRegister(engine);
+}
--- a/src/ai/api/ai_object.cpp Mon Mar 31 09:51:47 2008 +0000
+++ b/src/ai/api/ai_object.cpp Mon Mar 31 10:55:13 2008 +0000
@@ -3,6 +3,7 @@
/** @file ai_object.cpp Implementation of AIObject. */
#include "ai_object.hpp"
+#include "table/strings.h"
#include "../../command_func.h"
#include "../../network/network.h"
#include "../../player_func.h"
@@ -53,6 +54,16 @@
return AIObject::GetDoCommandStruct(_current_player)->costs.GetCost();
}
+void AIObject::SetLastError(const StringID last_error)
+{
+ AIObject::GetDoCommandStruct(_current_player)->last_error = last_error;
+}
+
+const StringID AIObject::GetLastError()
+{
+ return AIObject::GetDoCommandStruct(_current_player)->last_error;
+}
+
void AIObject::SetLastCommandRes(bool res)
{
AIObject::GetDoCommandStruct(_current_player)->last_command_res = res;
@@ -120,6 +131,9 @@
CommandCost res;
const char *tmp_cmdtext;
+ /* Make sure the last error is reset, so we don't give faulty warnings */
+ SetLastError(STR_NULL);
+
if (procc != CMD_LANDSCAPE_CLEAR) flags |= DC_AUTO;
if (water_protection) flags |= DC_NO_WATER;
@@ -129,7 +143,10 @@
/* First, do a test-run to see if we can do this */
res = ::DoCommand(tile, p1, p2, flags, procc);
/* The command failed, so return */
- if (::CmdFailed(res)) return false;
+ if (::CmdFailed(res)) {
+ SetLastError(_error_message);
+ return false;
+ }
/* Restore _cmd_text */
_cmd_text = tmp_cmdtext;
@@ -172,7 +189,10 @@
AI_SuspendPlayer(_current_player, AIObject::GetDoCommandDelay());
}
- if (::CmdFailed(res)) return false;
+ if (::CmdFailed(res)) {
+ SetLastError(_error_message);
+ return false;
+ }
AIObject::IncreaseDoCommandCosts(res.GetCost());
return true;
--- a/src/ai/api/ai_object.hpp Mon Mar 31 09:51:47 2008 +0000
+++ b/src/ai/api/ai_object.hpp Mon Mar 31 10:55:13 2008 +0000
@@ -9,6 +9,7 @@
#include "../../openttd.h"
#include "../../misc/countedptr.hpp"
#include "../../signs_type.h"
+#include "../../strings_type.h"
#include "../../command_type.h"
#include "../../vehicle_type.h"
#include "../../tile_type.h"
@@ -39,6 +40,7 @@
AIObject *mode_instance;
uint delay;
CommandCost costs;
+ StringID last_error;
bool last_command_res;
VehicleID new_vehicle_id;
SignID new_sign_id;
@@ -72,6 +74,16 @@
static Money GetDoCommandCosts();
/**
+ * Set the DoCommand last error.
+ */
+ static void SetLastError(const StringID last_error);
+
+ /**
+ * Get the DoCommand last error.
+ */
+ static const StringID GetLastError();
+
+ /**
* Set the current mode of your AI to this proc.
*/
static void SetDoCommandMode(AIModeProc *proc, AIObject *instance);
--- a/src/ai/api/squirrel_export.awk Mon Mar 31 09:51:47 2008 +0000
+++ b/src/ai/api/squirrel_export.awk Mon Mar 31 10:55:13 2008 +0000
@@ -28,6 +28,7 @@
BEGIN {
enum_size = 0
enum_value_size = 0
+ enum_error_size = 0
struct_size = 0
method_size = 0
static_method_size = 0
@@ -186,6 +187,19 @@
}
if (enum_value_size != 0) print ""
+ mlen = 0
+ for (i = 1; i <= enum_error_size; i++) {
+ if (mlen <= length(enum_error_string_mapping[i])) mlen = length(enum_error_string_mapping[i])
+ }
+ for (i = 1; i <= enum_error_size; i++) {
+ print " AIError::RegisterErrorMap (" enum_error_string_mapping[i] ", " substr(spaces, 1, mlen - length(enum_error_string_mapping[i])) cls "::" enum_error_value_mapping[i] ");"
+ print " AIError::RegisterErrorMapString(" enum_error_string_mapping[i] ", " substr(spaces, 1, mlen - length(enum_error_string_mapping[i]) + length(cls) + 1) "\"" enum_error_value_mapping[i] "\");"
+
+ delete enum_error_string_mapping[i]
+ delete enum_error_value_mapping[i]
+ }
+ if (enum_error_size != 0) print ""
+
# Static methods
mlen = 0
for (i = 1; i <= static_method_size; i++) {
@@ -218,6 +232,7 @@
enum_size = 0
enum_value_size = 0
+ enum_error_size = 0
struct_size = 0
method_size = 0
static_method_size = 0
@@ -236,6 +251,30 @@
enum_value_size++
sub(",", "", $1)
enum_value[enum_value_size] = $1
+
+ # Check if this a special error enum
+ if (match(enums[enum_size], ".*::ErrorMessages") != 0) {
+ # syntax:
+ # enum ErrorMessages {
+ # ERR_SOME_ERROR, // [STR_ITEM1, STR_ITEM2, ...]
+ # }
+
+ # Set the mappings
+ if (match($0, "\\[.+\\]") != 0) {
+ mappings = substr($0, RSTART, RLENGTH);
+ gsub("([\\[[:space:]\\]])", "", mappings);
+
+ split(mappings, mapitems, ",");
+ for (i = 1; i <= length(mapitems); i++) {
+ enum_error_size++
+ enum_error_string_mapping[enum_error_size] = mapitems[i]
+ enum_error_value_mapping[enum_error_size] = $1
+ }
+
+ # Mark this index as an error
+ enum_is_error_mapping[enum_value_size] = 1
+ }
+ }
next
}
}