# HG changeset patch # User truebrain # Date 1206960913 0 # Node ID 738b8f69675f939cd3480bd1cb3057f52d565823 # Parent 29f34a179daf994f1f8066f6863204ef1d9b077e (svn r12510) [NoAI] -Add: added AIError, which allows you to catch errors triggered by commands (Morloth) diff -r 29f34a179daf -r 738b8f69675f bin/ai/regression/regression.nut --- 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)); diff -r 29f34a179daf -r 738b8f69675f bin/ai/regression/regression.txt --- 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 diff -r 29f34a179daf -r 738b8f69675f projects/openttd_vs80.vcproj --- 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 @@ > + + @@ -2348,6 +2352,10 @@ > + + diff -r 29f34a179daf -r 738b8f69675f projects/openttd_vs90.vcproj --- 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 @@ > + + @@ -2345,6 +2349,10 @@ > + + diff -r 29f34a179daf -r 738b8f69675f source.list --- 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 diff -r 29f34a179daf -r 738b8f69675f src/ai/ai_squirrel.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 #include @@ -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); diff -r 29f34a179daf -r 738b8f69675f src/ai/api/ai_error.cpp --- /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); +} diff -r 29f34a179daf -r 738b8f69675f src/ai/api/ai_error.hpp --- /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 + +/** + * 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 AIErrorMap; + typedef std::map AIErrorMapString; + + static AIErrorMap error_map; + static AIErrorMapString error_map_string; +}; + +#endif /* AI_ERROR_HPP */ diff -r 29f34a179daf -r 738b8f69675f src/ai/api/ai_error.hpp.sq --- /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, HSQUIRRELVM vm, int index) { SQInteger tmp; sq_getinteger(vm, index, &tmp); return (AIError::ErrorCategories)tmp; } + template <> int Return(HSQUIRRELVM vm, AIError::ErrorCategories res) { sq_pushinteger(vm, (int32)res); return 1; } + template <> AIError::ErrorMessages GetParam(ForceType, HSQUIRRELVM vm, int index) { SQInteger tmp; sq_getinteger(vm, index, &tmp); return (AIError::ErrorMessages)tmp; } + template <> int Return(HSQUIRRELVM vm, AIError::ErrorMessages res) { sq_pushinteger(vm, (int32)res); return 1; } + + /* Allow AIError to be used as Squirrel parameter */ + template <> AIError *GetParam(ForceType, HSQUIRRELVM vm, int index) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return (AIError *)instance; } + template <> AIError &GetParam(ForceType, HSQUIRRELVM vm, int index) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return *(AIError *)instance; } + template <> const AIError *GetParam(ForceType, HSQUIRRELVM vm, int index) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return (AIError *)instance; } + template <> const AIError &GetParam(ForceType, HSQUIRRELVM vm, int index) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return *(AIError *)instance; } + template <> int Return(HSQUIRRELVM vm, AIError *res) { if (res == NULL) { sq_pushnull(vm); return 1; } res->AddRef(); Squirrel::CreateClassInstanceVM(vm, "AIError", res, NULL, DefSQDestructorCallback); return 1; } +}; // namespace SQConvert + +void SQAIError_Register(Squirrel *engine) { + DefSQClass SQAIError("AIError"); + SQAIError.PreRegister(engine); + SQAIError.AddConstructor(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); +} diff -r 29f34a179daf -r 738b8f69675f src/ai/api/ai_object.cpp --- 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; diff -r 29f34a179daf -r 738b8f69675f src/ai/api/ai_object.hpp --- 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); diff -r 29f34a179daf -r 738b8f69675f src/ai/api/squirrel_export.awk --- 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 } }