From 187d3301777daa0bd569f38b04deb5fc9e7415d2 Mon Sep 17 00:00:00 2001 From: translators Date: Fri, 24 Feb 2023 18:48:34 +0000 Subject: [PATCH 01/47] Update: Translations from eints spanish (mexican): 1 change by GNosii --- src/lang/spanish_MX.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/src/lang/spanish_MX.txt b/src/lang/spanish_MX.txt index d0e9bc0f26..d09e8e3ab4 100644 --- a/src/lang/spanish_MX.txt +++ b/src/lang/spanish_MX.txt @@ -3353,6 +3353,7 @@ STR_NEWGRF_ERROR_MSG_INFO :{SILVER}{STRING STR_NEWGRF_ERROR_MSG_WARNING :{RED}Atención: {SILVER}{STRING} STR_NEWGRF_ERROR_MSG_ERROR :{RED}Error: {SILVER}{STRING} STR_NEWGRF_ERROR_MSG_FATAL :{RED}Error fatal: {SILVER}{STRING} +STR_NEWGRF_ERROR_POPUP :{WHITE}Ha ocurrido un error en el NewGRF "{STRING}":{}{STRING} STR_NEWGRF_ERROR_VERSION_NUMBER :{1:STRING} no funcionará con la con la versión de TTDPatch reportada por OpenTTD STR_NEWGRF_ERROR_DOS_OR_WINDOWS :{1:STRING} es la para la versión {2:STRING} de TTD STR_NEWGRF_ERROR_UNSET_SWITCH :{1:STRING} está diseñado para usarse con {2:STRING} From ca3cb4d2ef3926ea28a4d81524680c247cadd8a4 Mon Sep 17 00:00:00 2001 From: Michael Lutz Date: Mon, 17 Jan 2022 00:54:59 +0100 Subject: [PATCH 02/47] Codechange: [Script] Use a class template instead of a function template for squirrel type conversion. Class templates allow using partial template specialization, which is useful in case one wants to have a type conversion on a type that is itself templated. --- cmake/scripts/SquirrelExport.cmake | 34 ++--- src/script/api/script_priorityqueue.cpp | 8 +- src/script/squirrel_helper.hpp | 170 ++++++++++++------------ 3 files changed, 108 insertions(+), 104 deletions(-) diff --git a/cmake/scripts/SquirrelExport.cmake b/cmake/scripts/SquirrelExport.cmake index e5d83714b9..5c41618d6c 100644 --- a/cmake/scripts/SquirrelExport.cmake +++ b/cmake/scripts/SquirrelExport.cmake @@ -28,25 +28,27 @@ endmacro() macro(dump_class_templates NAME) string(REGEX REPLACE "^Script" "" REALNAME ${NAME}) - string(APPEND SQUIRREL_EXPORT "\n template <> inline ${NAME} *GetParam(ForceType<${NAME} *>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, nullptr); return (${NAME} *)instance; }") - string(APPEND SQUIRREL_EXPORT "\n template <> inline ${NAME} &GetParam(ForceType<${NAME} &>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, nullptr); return *(${NAME} *)instance; }") - string(APPEND SQUIRREL_EXPORT "\n template <> inline const ${NAME} *GetParam(ForceType, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, nullptr); return (${NAME} *)instance; }") - string(APPEND SQUIRREL_EXPORT "\n template <> inline const ${NAME} &GetParam(ForceType, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, nullptr); return *(${NAME} *)instance; }") + string(APPEND SQUIRREL_EXPORT "\n template <> struct Param<${NAME} *> { static inline ${NAME} *Get(HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, nullptr); return (${NAME} *)instance; } };") + string(APPEND SQUIRREL_EXPORT "\n template <> struct Param<${NAME} &> { static inline ${NAME} &Get(HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, nullptr); return *(${NAME} *)instance; } };") + string(APPEND SQUIRREL_EXPORT "\n template <> struct Param { static inline const ${NAME} *Get(HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, nullptr); return (${NAME} *)instance; } };") + string(APPEND SQUIRREL_EXPORT "\n template <> struct Param { static inline const ${NAME} &Get(HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, nullptr); return *(${NAME} *)instance; } };") if("${NAME}" STREQUAL "ScriptEvent") - string(APPEND SQUIRREL_EXPORT "\n template <> inline int Return<${NAME} *>(HSQUIRRELVM vm, ${NAME} *res) { if (res == nullptr) { sq_pushnull(vm); return 1; } Squirrel::CreateClassInstanceVM(vm, \"${REALNAME}\", res, nullptr, DefSQDestructorCallback<${NAME}>, true); return 1; }") + string(APPEND SQUIRREL_EXPORT "\n template <> struct Return<${NAME} *> { static inline int Set(HSQUIRRELVM vm, ${NAME} *res) { if (res == nullptr) { sq_pushnull(vm); return 1; } Squirrel::CreateClassInstanceVM(vm, \"${REALNAME}\", res, nullptr, DefSQDestructorCallback<${NAME}>, true); return 1; } };") elseif("${NAME}" STREQUAL "ScriptText") string(APPEND SQUIRREL_EXPORT "\n") - string(APPEND SQUIRREL_EXPORT "\n template <> inline Text *GetParam(ForceType, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) {") - string(APPEND SQUIRREL_EXPORT "\n if (sq_gettype(vm, index) == OT_INSTANCE) {") - string(APPEND SQUIRREL_EXPORT "\n return GetParam(ForceType(), vm, index, ptr);") + string(APPEND SQUIRREL_EXPORT "\n template <> struct Param {") + string(APPEND SQUIRREL_EXPORT "\n static inline Text *Get(HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) {") + string(APPEND SQUIRREL_EXPORT "\n if (sq_gettype(vm, index) == OT_INSTANCE) {") + string(APPEND SQUIRREL_EXPORT "\n return Param::Get(vm, index, ptr);") + string(APPEND SQUIRREL_EXPORT "\n }") + string(APPEND SQUIRREL_EXPORT "\n if (sq_gettype(vm, index) == OT_STRING) {") + string(APPEND SQUIRREL_EXPORT "\n return new RawText(Param::Get(vm, index, ptr));") + string(APPEND SQUIRREL_EXPORT "\n }") + string(APPEND SQUIRREL_EXPORT "\n return nullptr;") string(APPEND SQUIRREL_EXPORT "\n }") - string(APPEND SQUIRREL_EXPORT "\n if (sq_gettype(vm, index) == OT_STRING) {") - string(APPEND SQUIRREL_EXPORT "\n return new RawText(GetParam(ForceType(), vm, index, ptr));") - string(APPEND SQUIRREL_EXPORT "\n }") - string(APPEND SQUIRREL_EXPORT "\n return nullptr;") - string(APPEND SQUIRREL_EXPORT "\n }") + string(APPEND SQUIRREL_EXPORT "\n };") else() - string(APPEND SQUIRREL_EXPORT "\n template <> inline int Return<${NAME} *>(HSQUIRRELVM vm, ${NAME} *res) { if (res == nullptr) { sq_pushnull(vm); return 1; } res->AddRef(); Squirrel::CreateClassInstanceVM(vm, \"${REALNAME}\", res, nullptr, DefSQDestructorCallback<${NAME}>, true); return 1; }") + string(APPEND SQUIRREL_EXPORT "\n template <> struct Return<${NAME} *> { static inline int Set(HSQUIRRELVM vm, ${NAME} *res) { if (res == nullptr) { sq_pushnull(vm); return 1; } res->AddRef(); Squirrel::CreateClassInstanceVM(vm, \"${REALNAME}\", res, nullptr, DefSQDestructorCallback<${NAME}>, true); return 1; } };") endif() endmacro() @@ -297,8 +299,8 @@ foreach(LINE IN LISTS SOURCE_LINES) endif() string(APPEND SQUIRREL_EXPORT "\n /* Allow enums to be used as Squirrel parameters */") foreach(ENUM IN LISTS ENUMS) - string(APPEND SQUIRREL_EXPORT "\n template <> inline ${ENUM} GetParam(ForceType<${ENUM}>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQInteger tmp; sq_getinteger(vm, index, &tmp); return (${ENUM})tmp; }") - string(APPEND SQUIRREL_EXPORT "\n template <> inline int Return<${ENUM}>(HSQUIRRELVM vm, ${ENUM} res) { sq_pushinteger(vm, res); return 1; }") + string(APPEND SQUIRREL_EXPORT "\n template <> struct Param<${ENUM}> { static inline ${ENUM} Get(HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQInteger tmp; sq_getinteger(vm, index, &tmp); return (${ENUM})tmp; } };") + string(APPEND SQUIRREL_EXPORT "\n template <> struct Return<${ENUM}> { static inline int Set(HSQUIRRELVM vm, ${ENUM} res) { sq_pushinteger(vm, res); return 1; } };") endforeach() endif() diff --git a/src/script/api/script_priorityqueue.cpp b/src/script/api/script_priorityqueue.cpp index fda452096a..96f36e10ee 100644 --- a/src/script/api/script_priorityqueue.cpp +++ b/src/script/api/script_priorityqueue.cpp @@ -45,7 +45,7 @@ SQInteger ScriptPriorityQueue::Insert(HSQUIRRELVM vm) this->queue.emplace_back(priority, item); std::push_heap(this->queue.begin(), this->queue.end(), this->comp); - return SQConvert::Return(vm, true); + return SQConvert::Return::Set(vm, true); } SQInteger ScriptPriorityQueue::Pop(HSQUIRRELVM vm) @@ -61,7 +61,7 @@ SQInteger ScriptPriorityQueue::Pop(HSQUIRRELVM vm) this->queue.pop_back(); /* Store the object on the Squirrel stack before releasing it to make sure the ref count can't drop to zero. */ - auto ret = SQConvert::Return(vm, item); + auto ret = SQConvert::Return::Set(vm, item); sq_release(vm, &item); return ret; } @@ -74,7 +74,7 @@ SQInteger ScriptPriorityQueue::Peek(HSQUIRRELVM vm) return 1; } - return SQConvert::Return(vm, this->queue.front().second); + return SQConvert::Return::Set(vm, this->queue.front().second); } SQInteger ScriptPriorityQueue::Exists(HSQUIRRELVM vm) @@ -83,7 +83,7 @@ SQInteger ScriptPriorityQueue::Exists(HSQUIRRELVM vm) sq_resetobject(&item); sq_getstackobj(vm, 2, &item); - return SQConvert::Return(vm, std::find(this->queue.cbegin(), this->queue.cend(), item) != this->queue.cend()); + return SQConvert::Return::Set(vm, std::find(this->queue.cbegin(), this->queue.cend(), item) != this->queue.cend()); } SQInteger ScriptPriorityQueue::Clear(HSQUIRRELVM vm) diff --git a/src/script/squirrel_helper.hpp b/src/script/squirrel_helper.hpp index e6ce02786c..a717fbdad5 100644 --- a/src/script/squirrel_helper.hpp +++ b/src/script/squirrel_helper.hpp @@ -37,92 +37,94 @@ namespace SQConvert { /** - * Special class to make it possible for the compiler to pick the correct GetParam(). + * To return a value to squirrel, we use this helper class. It converts to the right format. + * We use a class instead of a plain function to allow us to use partial template specializations. */ - template class ForceType { }; + template struct Return; + + template <> struct Return { static inline int Set(HSQUIRRELVM vm, uint8 res) { sq_pushinteger(vm, (int32)res); return 1; } }; + template <> struct Return { static inline int Set(HSQUIRRELVM vm, uint16 res) { sq_pushinteger(vm, (int32)res); return 1; } }; + template <> struct Return { static inline int Set(HSQUIRRELVM vm, uint32 res) { sq_pushinteger(vm, (int32)res); return 1; } }; + template <> struct Return { static inline int Set(HSQUIRRELVM vm, int8 res) { sq_pushinteger(vm, res); return 1; } }; + template <> struct Return { static inline int Set(HSQUIRRELVM vm, int16 res) { sq_pushinteger(vm, res); return 1; } }; + template <> struct Return { static inline int Set(HSQUIRRELVM vm, int32 res) { sq_pushinteger(vm, res); return 1; } }; + template <> struct Return { static inline int Set(HSQUIRRELVM vm, int64 res) { sq_pushinteger(vm, res); return 1; } }; + template <> struct Return { static inline int Set(HSQUIRRELVM vm, Money res) { sq_pushinteger(vm, res); return 1; } }; + template <> struct Return { static inline int Set(HSQUIRRELVM vm, TileIndex res) { sq_pushinteger(vm, (int32)res.value); return 1; } }; + template <> struct Return { static inline int Set(HSQUIRRELVM vm, bool res) { sq_pushbool (vm, res); return 1; } }; + template <> struct Return { static inline int Set(HSQUIRRELVM vm, char *res) { if (res == nullptr) sq_pushnull(vm); else { sq_pushstring(vm, res, -1); free(res); } return 1; } }; + template <> struct Return { static inline int Set(HSQUIRRELVM vm, const char *res) { if (res == nullptr) sq_pushnull(vm); else { sq_pushstring(vm, res, -1); } return 1; } }; + template <> struct Return { static inline int Set(HSQUIRRELVM vm, void *res) { sq_pushuserpointer(vm, res); return 1; } }; + template <> struct Return { static inline int Set(HSQUIRRELVM vm, HSQOBJECT res) { sq_pushobject(vm, res); return 1; } }; /** - * To return a value to squirrel, we call this function. It converts to the right format. + * To get a param from squirrel, we use this helper class. It converts to the right format. + * We use a class instead of a plain function to allow us to use partial template specializations. */ - template static int Return(HSQUIRRELVM vm, T t); - - template <> inline int Return (HSQUIRRELVM vm, uint8 res) { sq_pushinteger(vm, (int32)res); return 1; } - template <> inline int Return (HSQUIRRELVM vm, uint16 res) { sq_pushinteger(vm, (int32)res); return 1; } - template <> inline int Return (HSQUIRRELVM vm, uint32 res) { sq_pushinteger(vm, (int32)res); return 1; } - template <> inline int Return (HSQUIRRELVM vm, int8 res) { sq_pushinteger(vm, res); return 1; } - template <> inline int Return (HSQUIRRELVM vm, int16 res) { sq_pushinteger(vm, res); return 1; } - template <> inline int Return (HSQUIRRELVM vm, int32 res) { sq_pushinteger(vm, res); return 1; } - template <> inline int Return (HSQUIRRELVM vm, int64 res) { sq_pushinteger(vm, res); return 1; } - template <> inline int Return (HSQUIRRELVM vm, Money res) { sq_pushinteger(vm, res); return 1; } - template <> inline int Return (HSQUIRRELVM vm, TileIndex res) { sq_pushinteger(vm, (int32)res.value); return 1; } - template <> inline int Return (HSQUIRRELVM vm, bool res) { sq_pushbool (vm, res); return 1; } - template <> inline int Return (HSQUIRRELVM vm, char *res) { if (res == nullptr) sq_pushnull(vm); else { sq_pushstring(vm, res, -1); free(res); } return 1; } - template <> inline int Return(HSQUIRRELVM vm, const char *res) { if (res == nullptr) sq_pushnull(vm); else { sq_pushstring(vm, res, -1); } return 1; } - template <> inline int Return (HSQUIRRELVM vm, void *res) { sq_pushuserpointer(vm, res); return 1; } - template <> inline int Return (HSQUIRRELVM vm, HSQOBJECT res) { sq_pushobject(vm, res); return 1; } - - /** - * To get a param from squirrel, we call this function. It converts to the right format. - */ - template static T GetParam(ForceType, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr); - - template <> inline uint8 GetParam(ForceType , HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQInteger tmp; sq_getinteger (vm, index, &tmp); return tmp; } - template <> inline uint16 GetParam(ForceType , HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQInteger tmp; sq_getinteger (vm, index, &tmp); return tmp; } - template <> inline uint32 GetParam(ForceType , HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQInteger tmp; sq_getinteger (vm, index, &tmp); return tmp; } - template <> inline int8 GetParam(ForceType , HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQInteger tmp; sq_getinteger (vm, index, &tmp); return tmp; } - template <> inline int16 GetParam(ForceType , HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQInteger tmp; sq_getinteger (vm, index, &tmp); return tmp; } - template <> inline int32 GetParam(ForceType , HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQInteger tmp; sq_getinteger (vm, index, &tmp); return tmp; } - template <> inline int64 GetParam(ForceType , HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQInteger tmp; sq_getinteger (vm, index, &tmp); return tmp; } - template <> inline TileIndex GetParam(ForceType , HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQInteger tmp; sq_getinteger (vm, index, &tmp); return TileIndex((uint32)(int32)tmp); } - template <> inline Money GetParam(ForceType , HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQInteger tmp; sq_getinteger (vm, index, &tmp); return tmp; } - template <> inline bool GetParam(ForceType , HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQBool tmp; sq_getbool (vm, index, &tmp); return tmp != 0; } - template <> inline void *GetParam(ForceType , HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer tmp; sq_getuserpointer(vm, index, &tmp); return tmp; } - template <> inline const char *GetParam(ForceType, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) - { - /* Convert what-ever there is as parameter to a string */ - sq_tostring(vm, index); - - const SQChar *tmp; - sq_getstring(vm, -1, &tmp); - char *tmp_str = stredup(tmp); - sq_poptop(vm); - ptr->push_back((void *)tmp_str); - StrMakeValidInPlace(tmp_str); - return tmp_str; - } - - template <> inline Array *GetParam(ForceType, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) - { - /* Sanity check of the size. */ - if (sq_getsize(vm, index) > UINT16_MAX) throw sq_throwerror(vm, "an array used as parameter to a function is too large"); - - SQObject obj; - sq_getstackobj(vm, index, &obj); - sq_pushobject(vm, obj); - sq_pushnull(vm); - - std::vector data; + template struct Param; + + template <> struct Param { static inline uint8 Get(HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQInteger tmp; sq_getinteger (vm, index, &tmp); return tmp; } }; + template <> struct Param { static inline uint16 Get(HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQInteger tmp; sq_getinteger (vm, index, &tmp); return tmp; } }; + template <> struct Param { static inline uint32 Get(HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQInteger tmp; sq_getinteger (vm, index, &tmp); return tmp; } }; + template <> struct Param { static inline int8 Get(HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQInteger tmp; sq_getinteger (vm, index, &tmp); return tmp; } }; + template <> struct Param { static inline int16 Get(HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQInteger tmp; sq_getinteger (vm, index, &tmp); return tmp; } }; + template <> struct Param { static inline int32 Get(HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQInteger tmp; sq_getinteger (vm, index, &tmp); return tmp; } }; + template <> struct Param { static inline int64 Get(HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQInteger tmp; sq_getinteger (vm, index, &tmp); return tmp; } }; + template <> struct Param { static inline TileIndex Get(HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQInteger tmp; sq_getinteger (vm, index, &tmp); return TileIndex((uint32)(int32)tmp); } }; + template <> struct Param { static inline Money Get(HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQInteger tmp; sq_getinteger (vm, index, &tmp); return tmp; } }; + template <> struct Param { static inline bool Get(HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQBool tmp; sq_getbool (vm, index, &tmp); return tmp != 0; } }; + template <> struct Param { static inline void *Get(HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer tmp; sq_getuserpointer(vm, index, &tmp); return tmp; } }; + + template <> struct Param { + static inline const char *Get(HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) + { + /* Convert what-ever there is as parameter to a string */ + sq_tostring(vm, index); + + const SQChar *tmp; + sq_getstring(vm, -1, &tmp); + char *tmp_str = stredup(tmp); + sq_poptop(vm); + ptr->push_back((void *)tmp_str); + StrMakeValidInPlace(tmp_str); + return tmp_str; + } + }; - while (SQ_SUCCEEDED(sq_next(vm, -2))) { - SQInteger tmp; - if (SQ_SUCCEEDED(sq_getinteger(vm, -1, &tmp))) { - data.push_back((int32)tmp); - } else { - sq_pop(vm, 4); - throw sq_throwerror(vm, "a member of an array used as parameter to a function is not numeric"); + template <> struct Param { + static inline Array *Get(HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) + { + /* Sanity check of the size. */ + if (sq_getsize(vm, index) > UINT16_MAX) throw sq_throwerror(vm, "an array used as parameter to a function is too large"); + + SQObject obj; + sq_getstackobj(vm, index, &obj); + sq_pushobject(vm, obj); + sq_pushnull(vm); + + std::vector data; + + while (SQ_SUCCEEDED(sq_next(vm, -2))) { + SQInteger tmp; + if (SQ_SUCCEEDED(sq_getinteger(vm, -1, &tmp))) { + data.push_back((int32)tmp); + } else { + sq_pop(vm, 4); + throw sq_throwerror(vm, "a member of an array used as parameter to a function is not numeric"); + } + + sq_pop(vm, 2); } - sq_pop(vm, 2); - } - sq_pop(vm, 2); - Array *arr = (Array*)MallocT(sizeof(Array) + sizeof(int32) * data.size()); - arr->size = data.size(); - memcpy(arr->array, data.data(), sizeof(int32) * data.size()); + Array *arr = (Array *)MallocT(sizeof(Array) + sizeof(int32) * data.size()); + arr->size = data.size(); + memcpy(arr->array, data.data(), sizeof(int32) * data.size()); - ptr->push_back(arr); - return arr; - } + ptr->push_back(arr); + return arr; + } + }; /** * Helper class to recognize the function type (retval type, args) and use the proper specialization @@ -148,14 +150,14 @@ namespace SQConvert { [[maybe_unused]] SQAutoFreePointers ptr; if constexpr (std::is_void_v) { (*func)( - GetParam(ForceType(), vm, 2 + i, &ptr)... + Param::Get(vm, 2 + i, &ptr)... ); return 0; } else { Tretval ret = (*func)( - GetParam(ForceType(), vm, 2 + i, &ptr)... + Param::Get(vm, 2 + i, &ptr)... ); - return Return(vm, ret); + return Return::Set(vm, ret); } } }; @@ -182,14 +184,14 @@ namespace SQConvert { [[maybe_unused]] SQAutoFreePointers ptr; if constexpr (std::is_void_v) { (instance->*func)( - GetParam(ForceType(), vm, 2 + i, &ptr)... + Param::Get(vm, 2 + i, &ptr)... ); return 0; } else { Tretval ret = (instance->*func)( - GetParam(ForceType(), vm, 2 + i, &ptr)... + Param::Get(vm, 2 + i, &ptr)... ); - return Return(vm, ret); + return Return::Set(vm, ret); } } @@ -198,7 +200,7 @@ namespace SQConvert { { [[maybe_unused]] SQAutoFreePointers ptr; Tcls *inst = new Tcls( - GetParam(ForceType(), vm, 2 + i, &ptr)... + Param::Get(vm, 2 + i, &ptr)... ); return inst; From 9b3326e3fdf5d90a2ab763869d0c678c8bd18c33 Mon Sep 17 00:00:00 2001 From: Michael Lutz Date: Fri, 24 Feb 2023 00:49:57 +0100 Subject: [PATCH 03/47] Codechange: [Script] Use std::vector instead of a hand-rolled alternative. --- cmake/scripts/SquirrelExport.cmake | 2 +- src/script/api/script_road.cpp | 34 ++++++++++++++--------------- src/script/api/script_road.hpp | 3 ++- src/script/squirrel_helper.hpp | 23 +++++-------------- src/script/squirrel_helper_type.hpp | 8 +++---- 5 files changed, 30 insertions(+), 40 deletions(-) diff --git a/cmake/scripts/SquirrelExport.cmake b/cmake/scripts/SquirrelExport.cmake index 5c41618d6c..d13bd8d4d0 100644 --- a/cmake/scripts/SquirrelExport.cmake +++ b/cmake/scripts/SquirrelExport.cmake @@ -627,7 +627,7 @@ foreach(LINE IN LISTS SOURCE_LINES) string(APPEND TYPES "p") elseif("${PARAM}" MATCHES "^Array") string(APPEND TYPES "a") - elseif("${PARAM}" MATCHES "^struct Array") + elseif("${PARAM}" MATCHES "^const Array") string(APPEND TYPES "a") elseif("${PARAM}" MATCHES "^Text") string(APPEND TYPES ".") diff --git a/src/script/api/script_road.cpp b/src/script/api/script_road.cpp index 20e38398de..1cb01327c4 100644 --- a/src/script/api/script_road.cpp +++ b/src/script/api/script_road.cpp @@ -148,9 +148,9 @@ * @param end The part that will be build second. * @return True if and only if the road bits can be build. */ -static bool CheckAutoExpandedRoadBits(const Array *existing, int32 start, int32 end) +static bool CheckAutoExpandedRoadBits(const Array<> &existing, int32 start, int32 end) { - return (start + end == 0) && (existing->size == 0 || existing->array[0] == start || existing->array[0] == end); + return (start + end == 0) && (existing.empty() || existing[0] == start || existing[0] == end); } /** @@ -163,7 +163,7 @@ static bool CheckAutoExpandedRoadBits(const Array *existing, int32 start, int32 * they are build or 2 when building the first part automatically * builds the second part. */ -static int32 LookupWithoutBuildOnSlopes(::Slope slope, const Array *existing, int32 start, int32 end) +static int32 LookupWithoutBuildOnSlopes(::Slope slope, const Array<> &existing, int32 start, int32 end) { switch (slope) { /* Flat slopes can always be build. */ @@ -175,9 +175,9 @@ static int32 LookupWithoutBuildOnSlopes(::Slope slope, const Array *existing, in * in the game have been changed. */ case SLOPE_NE: case SLOPE_SW: - return (CheckAutoExpandedRoadBits(existing, start, end) && (start == 1 || end == 1)) ? (existing->size == 0 ? 2 : 1) : 0; + return (CheckAutoExpandedRoadBits(existing, start, end) && (start == 1 || end == 1)) ? (existing.empty() ? 2 : 1) : 0; case SLOPE_SE: case SLOPE_NW: - return (CheckAutoExpandedRoadBits(existing, start, end) && (start != 1 && end != 1)) ? (existing->size == 0 ? 2 : 1) : 0; + return (CheckAutoExpandedRoadBits(existing, start, end) && (start != 1 && end != 1)) ? (existing.empty() ? 2 : 1) : 0; /* Any other tile cannot be built on. */ default: @@ -227,7 +227,7 @@ static RoadBits NeighbourToRoadBits(int32 neighbour) * they are build or 2 when building the first part automatically * builds the second part. */ -static int32 LookupWithBuildOnSlopes(::Slope slope, Array *existing, int32 start, int32 end) +static int32 LookupWithBuildOnSlopes(::Slope slope, const Array<> &existing, int32 start, int32 end) { /* Steep slopes behave the same as slopes with one corner raised. */ if (IsSteepSlope(slope)) { @@ -277,9 +277,6 @@ static int32 LookupWithBuildOnSlopes(::Slope slope, Array *existing, int32 start /* Now perform the actual rotation. */ for (int j = 0; j < base_rotate; j++) { - for (size_t i = 0; i < existing->size; i++) { - existing->array[i] = RotateNeighbour(existing->array[i]); - } start = RotateNeighbour(start); end = RotateNeighbour(end); } @@ -288,8 +285,11 @@ static int32 LookupWithBuildOnSlopes(::Slope slope, Array *existing, int32 start RoadBits start_roadbits = NeighbourToRoadBits(start); RoadBits new_roadbits = start_roadbits | NeighbourToRoadBits(end); RoadBits existing_roadbits = ROAD_NONE; - for (size_t i = 0; i < existing->size; i++) { - existing_roadbits |= NeighbourToRoadBits(existing->array[i]); + for (int32 neighbour : existing) { + for (int j = 0; j < base_rotate; j++) { + neighbour = RotateNeighbour(neighbour); + } + existing_roadbits |= NeighbourToRoadBits(neighbour); } switch (slope) { @@ -377,7 +377,7 @@ static bool NormaliseTileOffset(int32 *tile) return false; } -/* static */ int32 ScriptRoad::CanBuildConnectedRoadParts(ScriptTile::Slope slope_, Array *existing, TileIndex start_, TileIndex end_) +/* static */ int32 ScriptRoad::CanBuildConnectedRoadParts(ScriptTile::Slope slope_, Array<> existing, TileIndex start_, TileIndex end_) { ::Slope slope = (::Slope)slope_; int32 start = start_; @@ -386,8 +386,8 @@ static bool NormaliseTileOffset(int32 *tile) /* The start tile and end tile cannot be the same tile either. */ if (start == end) return -1; - for (size_t i = 0; i < existing->size; i++) { - if (!NormaliseTileOffset(&existing->array[i])) return -1; + for (size_t i = 0; i < existing.size(); i++) { + if (!NormaliseTileOffset(&existing[i])) return -1; } if (!NormaliseTileOffset(&start)) return -1; @@ -405,8 +405,6 @@ static bool NormaliseTileOffset(int32 *tile) /* ROAD_NW ROAD_SW ROAD_SE ROAD_NE */ const TileIndexDiff neighbours[] = {::TileDiffXY(0, -1), ::TileDiffXY(1, 0), ::TileDiffXY(0, 1), ::TileDiffXY(-1, 0)}; - Array *existing = (Array*)alloca(sizeof(Array) + lengthof(neighbours) * sizeof(int32)); - existing->size = 0; ::RoadBits rb = ::ROAD_NONE; if (::IsNormalRoadTile(tile)) { @@ -414,8 +412,10 @@ static bool NormaliseTileOffset(int32 *tile) } else { rb = ::GetAnyRoadBits(tile, RTT_ROAD) | ::GetAnyRoadBits(tile, RTT_TRAM); } + + Array<> existing; for (uint i = 0; i < lengthof(neighbours); i++) { - if (HasBit(rb, i)) existing->array[existing->size++] = neighbours[i]; + if (HasBit(rb, i)) existing.emplace_back(neighbours[i]); } return ScriptRoad::CanBuildConnectedRoadParts(ScriptTile::GetSlope(tile), existing, start - tile, end - tile); diff --git a/src/script/api/script_road.hpp b/src/script/api/script_road.hpp index 9631309ebc..a042644078 100644 --- a/src/script/api/script_road.hpp +++ b/src/script/api/script_road.hpp @@ -11,6 +11,7 @@ #define SCRIPT_ROAD_HPP #include "script_tile.hpp" +#include "../squirrel_helper_type.hpp" #include "../../../road.h" /** @@ -245,7 +246,7 @@ public: * they are build or 2 when building the first part automatically * builds the second part. -1 means the preconditions are not met. */ - static int32 CanBuildConnectedRoadParts(ScriptTile::Slope slope, struct Array *existing, TileIndex start, TileIndex end); + static int32 CanBuildConnectedRoadParts(ScriptTile::Slope slope, Array<> existing, TileIndex start, TileIndex end); /** * Lookup function for building road parts independent of whether the diff --git a/src/script/squirrel_helper.hpp b/src/script/squirrel_helper.hpp index a717fbdad5..47c5eb62a0 100644 --- a/src/script/squirrel_helper.hpp +++ b/src/script/squirrel_helper.hpp @@ -91,8 +91,9 @@ namespace SQConvert { } }; - template <> struct Param { - static inline Array *Get(HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) + template + struct Param> { + static inline Array Get(HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { /* Sanity check of the size. */ if (sq_getsize(vm, index) > UINT16_MAX) throw sq_throwerror(vm, "an array used as parameter to a function is too large"); @@ -102,27 +103,15 @@ namespace SQConvert { sq_pushobject(vm, obj); sq_pushnull(vm); - std::vector data; + Array data; while (SQ_SUCCEEDED(sq_next(vm, -2))) { - SQInteger tmp; - if (SQ_SUCCEEDED(sq_getinteger(vm, -1, &tmp))) { - data.push_back((int32)tmp); - } else { - sq_pop(vm, 4); - throw sq_throwerror(vm, "a member of an array used as parameter to a function is not numeric"); - } - + data.emplace_back(Param::Get(vm, -1, ptr)); sq_pop(vm, 2); } sq_pop(vm, 2); - Array *arr = (Array *)MallocT(sizeof(Array) + sizeof(int32) * data.size()); - arr->size = data.size(); - memcpy(arr->array, data.data(), sizeof(int32) * data.size()); - - ptr->push_back(arr); - return arr; + return data; } }; diff --git a/src/script/squirrel_helper_type.hpp b/src/script/squirrel_helper_type.hpp index c20655ca19..05f3824fd9 100644 --- a/src/script/squirrel_helper_type.hpp +++ b/src/script/squirrel_helper_type.hpp @@ -10,10 +10,10 @@ #ifndef SQUIRREL_HELPER_TYPE_HPP #define SQUIRREL_HELPER_TYPE_HPP +#include + /** Definition of a simple array. */ -struct Array { - size_t size; ///< The size of the array. - int32 array[]; ///< The data of the array. -}; +template +using Array = std::vector; #endif /* SQUIRREL_HELPER_TYPE_HPP */ From c73b88ddcaeb13025aa62a39e5e8f7e35c6872eb Mon Sep 17 00:00:00 2001 From: dP Date: Sat, 25 Feb 2023 01:50:11 +0400 Subject: [PATCH 04/47] Fix: Don't send unused tile field over the network (#10507) --- src/ai/ai_instance.cpp | 5 ++--- src/command_func.h | 38 +++++++++++++------------------- src/command_type.h | 3 ++- src/game/game_instance.cpp | 5 ++--- src/group_gui.cpp | 2 +- src/network/network_command.cpp | 8 ++----- src/network/network_internal.h | 3 +-- src/order_cmd.h | 14 ++++++------ src/roadveh_cmd.h | 2 +- src/script/api/script_object.cpp | 10 ++++----- src/script/api/script_object.hpp | 6 ++--- src/script/script_instance.cpp | 6 ++--- src/script/script_instance.hpp | 2 +- src/script/script_storage.hpp | 2 -- src/town_cmd.h | 2 +- src/train_cmd.h | 6 ++--- src/train_gui.cpp | 2 +- src/vehicle_cmd.h | 22 +++++++++--------- 18 files changed, 60 insertions(+), 78 deletions(-) diff --git a/src/ai/ai_instance.cpp b/src/ai/ai_instance.cpp index 57528f2877..411afe3aa0 100644 --- a/src/ai/ai_instance.cpp +++ b/src/ai/ai_instance.cpp @@ -97,11 +97,10 @@ ScriptInfo *AIInstance::FindLibrary(const char *library, int version) * DoCommand callback function for all commands executed by AIs. * @param cmd cmd as given to DoCommandPInternal. * @param result The result of the command. - * @param tile The tile on which the command was executed. * @param data Command data as given to Command<>::Post. * @param result_data Additional returned data from the command. */ -void CcAI(Commands cmd, const CommandCost &result, TileIndex tile, const CommandDataBuffer &data, CommandDataBuffer result_data) +void CcAI(Commands cmd, const CommandCost &result, const CommandDataBuffer &data, CommandDataBuffer result_data) { /* * The company might not exist anymore. Check for this. @@ -112,7 +111,7 @@ void CcAI(Commands cmd, const CommandCost &result, TileIndex tile, const Command const Company *c = Company::GetIfValid(_current_company); if (c == nullptr || c->ai_instance == nullptr) return; - if (c->ai_instance->DoCommandCallback(result, tile, data, std::move(result_data), cmd)) { + if (c->ai_instance->DoCommandCallback(result, data, std::move(result_data), cmd)) { c->ai_instance->Continue(); } } diff --git a/src/command_func.h b/src/command_func.h index 45eaabfe15..de7cae2569 100644 --- a/src/command_func.h +++ b/src/command_func.h @@ -37,7 +37,7 @@ static const CommandCost CMD_ERROR = CommandCost(INVALID_STRING_ID); */ #define return_cmd_error(errcode) return CommandCost(errcode); -void NetworkSendCommand(Commands cmd, StringID err_message, CommandCallback *callback, CompanyID company, TileIndex location, const CommandDataBuffer &cmd_data); +void NetworkSendCommand(Commands cmd, StringID err_message, CommandCallback *callback, CompanyID company, const CommandDataBuffer &cmd_data); bool IsValidCommand(Commands cmd); CommandFlags GetCommandFlags(Commands cmd); @@ -215,20 +215,13 @@ public: * @param err_message Message prefix to show on error * @param callback A callback function to call after the command is finished * @param my_cmd indicator if the command is from a company or server (to display error messages for a user) - * @param location Tile location for user feedback. * @param args Parameters for the command * @return \c true if the command succeeded, else \c false. */ template - static bool PostFromNet(StringID err_message, Tcallback *callback, bool my_cmd, TileIndex location, std::tuple args) + static bool PostFromNet(StringID err_message, Tcallback *callback, bool my_cmd, std::tuple args) { - if constexpr (std::is_same_v>) { - /* Do not even think about executing out-of-bounds tile-commands. */ - TileIndex tile = std::get<0>(args); - if (tile != 0 && (tile >= Map::Size() || (!IsValidTile(tile) && (GetCommandFlags() & CMD_ALL_TILES) == 0))) return false; - } - - return InternalPost(err_message, callback, my_cmd, true, location, std::move(args)); + return InternalPost(err_message, callback, my_cmd, true, std::move(args)); } /** @@ -242,12 +235,7 @@ public: { auto args_tuple = std::forward_as_tuple(args...); - TileIndex tile{}; - if constexpr (std::is_same_v>) { - tile = std::get<0>(args_tuple); - } - - ::NetworkSendCommand(Tcmd, err_message, nullptr, _current_company, tile, EndianBufferWriter::FromValue(args_tuple)); + ::NetworkSendCommand(Tcmd, err_message, nullptr, _current_company, EndianBufferWriter::FromValue(args_tuple)); } /** @@ -324,9 +312,9 @@ protected: } else if constexpr (std::is_same_v) { /* Generic callback that takes packed arguments as a buffer. */ if constexpr (std::is_same_v) { - callback(Tcmd, ExtractCommandCost(res), tile, EndianBufferWriter::FromValue(args), {}); + callback(Tcmd, ExtractCommandCost(res), EndianBufferWriter::FromValue(args), {}); } else { - callback(Tcmd, ExtractCommandCost(res), tile, EndianBufferWriter::FromValue(args), EndianBufferWriter::FromValue(RemoveFirstTupleElement(res))); + callback(Tcmd, ExtractCommandCost(res), EndianBufferWriter::FromValue(args), EndianBufferWriter::FromValue(RemoveFirstTupleElement(res))); } } else if constexpr (!std::is_same_v && std::is_same_v::RetCallbackProc>) { std::apply(callback, std::tuple_cat(std::make_tuple(Tcmd), res)); @@ -405,7 +393,7 @@ protected: /* If we are in network, and the command is not from the network * send it to the command-queue and abort execution. */ if (send_net) { - ::NetworkSendCommand(Tcmd, err_message, callback, _current_company, tile, EndianBufferWriter::FromValue(args)); + ::NetworkSendCommand(Tcmd, err_message, callback, _current_company, EndianBufferWriter::FromValue(args)); cur_company.Restore(); /* Don't return anything special here; no error, no costs. @@ -446,8 +434,13 @@ protected: template struct CommandHelper : CommandHelper { - /* Import Post overloads from our base class. */ - using CommandHelper::Post; + /* Do not allow Post without explicit location. */ + static inline bool Post(StringID err_message, Targs... args) = delete; + template + static inline bool Post(Tcallback *callback, Targs... args) = delete; + static inline bool Post(Targs... args) = delete; + template + static bool Post(StringID err_message, Tcallback *callback, Targs... args) = delete; /** * Shortcut for Post when not using a callback. @@ -476,7 +469,6 @@ struct CommandHelper : CommandHel * commands that don't take a TileIndex by themselves. * @param err_message Message prefix to show on error * @param callback A callback function to call after the command is finished - * @param location Tile location for user feedback. * @param args Parameters for the command * @return \c true if the command succeeded, else \c false. */ @@ -492,6 +484,6 @@ struct CommandHelper : CommandHel #endif template -using Command = CommandHelper::ProcType, std::is_same_v::Args>>>; +using Command = CommandHelper::ProcType, (GetCommandFlags() & CMD_LOCATION) == 0>; #endif /* COMMAND_FUNC_H */ diff --git a/src/command_type.h b/src/command_type.h index 80caa8440f..90c59c6773 100644 --- a/src/command_type.h +++ b/src/command_type.h @@ -388,6 +388,7 @@ enum CommandFlags { CMD_DEITY = 0x100, ///< the command may be executed by COMPANY_DEITY CMD_STR_CTRL = 0x200, ///< the command's string may contain control strings CMD_NO_EST = 0x400, ///< the command is never estimated. + CMD_LOCATION = 0x800, ///< the command has implicit location argument. }; DECLARE_ENUM_AS_BIT_SET(CommandFlags) @@ -479,6 +480,6 @@ typedef void CommandCallback(Commands cmd, const CommandCost &result, TileIndex * @param result_data Additional returned data from the command * @see CommandProc */ -typedef void CommandCallbackData(Commands cmd, const CommandCost &result, TileIndex tile, const CommandDataBuffer &data, CommandDataBuffer result_data); +typedef void CommandCallbackData(Commands cmd, const CommandCost &result, const CommandDataBuffer &data, CommandDataBuffer result_data); #endif /* COMMAND_TYPE_H */ diff --git a/src/game/game_instance.cpp b/src/game/game_instance.cpp index facb875b43..8fcc6252de 100644 --- a/src/game/game_instance.cpp +++ b/src/game/game_instance.cpp @@ -84,13 +84,12 @@ void GameInstance::Died() * DoCommand callback function for all commands executed by Game Scripts. * @param cmd cmd as given to DoCommandPInternal. * @param result The result of the command. - * @param tile The tile on which the command was executed. * @param data Command data as given to Command<>::Post. * @param result_data Additional returned data from the command. */ -void CcGame(Commands cmd, const CommandCost &result, TileIndex tile, const CommandDataBuffer &data, CommandDataBuffer result_data) +void CcGame(Commands cmd, const CommandCost &result, const CommandDataBuffer &data, CommandDataBuffer result_data) { - if (Game::GetGameInstance()->DoCommandCallback(result, tile, data, std::move(result_data), cmd)) { + if (Game::GetGameInstance()->DoCommandCallback(result, data, std::move(result_data), cmd)) { Game::GetGameInstance()->Continue(); } } diff --git a/src/group_gui.cpp b/src/group_gui.cpp index db000aba8e..5766ce0380 100644 --- a/src/group_gui.cpp +++ b/src/group_gui.cpp @@ -881,7 +881,7 @@ public: uint id_g = this->group_sb->GetScrolledRowFromWidget(pt.y, this, WID_GL_LIST_GROUP); GroupID new_g = id_g >= this->groups.size() ? NEW_GROUP : this->groups[id_g]->index; - Command::Post(STR_ERROR_GROUP_CAN_T_ADD_VEHICLE, new_g == NEW_GROUP ? CcAddVehicleNewGroup : nullptr, 0, new_g, vindex, _ctrl_pressed || this->grouping == GB_SHARED_ORDERS); + Command::Post(STR_ERROR_GROUP_CAN_T_ADD_VEHICLE, new_g == NEW_GROUP ? CcAddVehicleNewGroup : nullptr, new_g, vindex, _ctrl_pressed || this->grouping == GB_SHARED_ORDERS); break; } diff --git a/src/network/network_command.cpp b/src/network/network_command.cpp index 2285b53360..ceb1df20da 100644 --- a/src/network/network_command.cpp +++ b/src/network/network_command.cpp @@ -261,17 +261,15 @@ static size_t FindCallbackIndex(CommandCallback *callback) * @param err_message Message prefix to show on error * @param callback A callback function to call after the command is finished * @param company The company that wants to send the command - * @param location Location of the command (e.g. for error message position) * @param cmd_data The command proc arguments. */ -void NetworkSendCommand(Commands cmd, StringID err_message, CommandCallback *callback, CompanyID company, TileIndex location, const CommandDataBuffer &cmd_data) +void NetworkSendCommand(Commands cmd, StringID err_message, CommandCallback *callback, CompanyID company, const CommandDataBuffer &cmd_data) { CommandPacket c; c.company = company; c.cmd = cmd; c.err_msg = err_message; c.callback = callback; - c.tile = location; c.data = cmd_data; if (_network_server) { @@ -429,7 +427,6 @@ const char *NetworkGameSocketHandler::ReceiveCommand(Packet *p, CommandPacket *c if (!IsValidCommand(cp->cmd)) return "invalid command"; if (GetCommandFlags(cp->cmd) & CMD_OFFLINE) return "single-player only command"; cp->err_msg = p->Recv_uint16(); - cp->tile = p->Recv_uint32(); cp->data = _cmd_dispatch[cp->cmd].Sanitize(p->Recv_buffer()); byte callback = p->Recv_uint8(); @@ -449,7 +446,6 @@ void NetworkGameSocketHandler::SendCommand(Packet *p, const CommandPacket *cp) p->Send_uint8(cp->company); p->Send_uint16(cp->cmd); p->Send_uint16(cp->err_msg); - p->Send_uint32(cp->tile); p->Send_buffer(cp->data); size_t callback = FindCallbackIndex(cp->callback); @@ -540,5 +536,5 @@ template void UnpackNetworkCommand(const CommandPacket* cp) { auto args = EndianBufferReader::ToValue::Args>(cp->data); - Command::PostFromNet(cp->err_msg, std::get(_callback_tuple), cp->my_cmd, cp->tile, args); + Command::PostFromNet(cp->err_msg, std::get(_callback_tuple), cp->my_cmd, args); } diff --git a/src/network/network_internal.h b/src/network/network_internal.h index 23c1224d17..3ea63570f3 100644 --- a/src/network/network_internal.h +++ b/src/network/network_internal.h @@ -108,7 +108,7 @@ void UpdateNetworkGameWindow(); */ struct CommandPacket { /** Make sure the pointer is nullptr. */ - CommandPacket() : next(nullptr), company(INVALID_COMPANY), frame(0), my_cmd(false), tile(0) {} + CommandPacket() : next(nullptr), company(INVALID_COMPANY), frame(0), my_cmd(false) {} CommandPacket *next; ///< the next command packet (if in queue) CompanyID company; ///< company that is executing the command uint32 frame; ///< the frame in which this packet is executed @@ -117,7 +117,6 @@ struct CommandPacket { Commands cmd; ///< command being executed. StringID err_msg; ///< string ID of error message to use. CommandCallback *callback; ///< any callback function executed upon successful completion of the command. - TileIndex tile; ///< location of the command (for e.g. error message or effect display). CommandDataBuffer data; ///< command parameters. }; diff --git a/src/order_cmd.h b/src/order_cmd.h index aeb42b6240..5faf871056 100644 --- a/src/order_cmd.h +++ b/src/order_cmd.h @@ -23,13 +23,13 @@ CommandCost CmdCloneOrder(DoCommandFlag flags, CloneOptions action, VehicleID ve CommandCost CmdMoveOrder(DoCommandFlag flags, VehicleID veh, VehicleOrderID moving_order, VehicleOrderID target_order); CommandCost CmdClearOrderBackup(DoCommandFlag flags, TileIndex tile, ClientID user_id); -DEF_CMD_TRAIT(CMD_MODIFY_ORDER, CmdModifyOrder, 0, CMDT_ROUTE_MANAGEMENT) -DEF_CMD_TRAIT(CMD_SKIP_TO_ORDER, CmdSkipToOrder, 0, CMDT_ROUTE_MANAGEMENT) -DEF_CMD_TRAIT(CMD_DELETE_ORDER, CmdDeleteOrder, 0, CMDT_ROUTE_MANAGEMENT) -DEF_CMD_TRAIT(CMD_INSERT_ORDER, CmdInsertOrder, 0, CMDT_ROUTE_MANAGEMENT) -DEF_CMD_TRAIT(CMD_ORDER_REFIT, CmdOrderRefit, 0, CMDT_ROUTE_MANAGEMENT) -DEF_CMD_TRAIT(CMD_CLONE_ORDER, CmdCloneOrder, 0, CMDT_ROUTE_MANAGEMENT) -DEF_CMD_TRAIT(CMD_MOVE_ORDER, CmdMoveOrder, 0, CMDT_ROUTE_MANAGEMENT) +DEF_CMD_TRAIT(CMD_MODIFY_ORDER, CmdModifyOrder, CMD_LOCATION, CMDT_ROUTE_MANAGEMENT) +DEF_CMD_TRAIT(CMD_SKIP_TO_ORDER, CmdSkipToOrder, CMD_LOCATION, CMDT_ROUTE_MANAGEMENT) +DEF_CMD_TRAIT(CMD_DELETE_ORDER, CmdDeleteOrder, CMD_LOCATION, CMDT_ROUTE_MANAGEMENT) +DEF_CMD_TRAIT(CMD_INSERT_ORDER, CmdInsertOrder, CMD_LOCATION, CMDT_ROUTE_MANAGEMENT) +DEF_CMD_TRAIT(CMD_ORDER_REFIT, CmdOrderRefit, CMD_LOCATION, CMDT_ROUTE_MANAGEMENT) +DEF_CMD_TRAIT(CMD_CLONE_ORDER, CmdCloneOrder, CMD_LOCATION, CMDT_ROUTE_MANAGEMENT) +DEF_CMD_TRAIT(CMD_MOVE_ORDER, CmdMoveOrder, CMD_LOCATION, CMDT_ROUTE_MANAGEMENT) DEF_CMD_TRAIT(CMD_CLEAR_ORDER_BACKUP, CmdClearOrderBackup, CMD_CLIENT_ID, CMDT_SERVER_SETTING) template diff --git a/src/roadveh_cmd.h b/src/roadveh_cmd.h index 67fe08bb79..357c8799ed 100644 --- a/src/roadveh_cmd.h +++ b/src/roadveh_cmd.h @@ -22,6 +22,6 @@ CommandCost CmdBuildRoadVehicle(DoCommandFlag flags, TileIndex tile, const Engin CommandCost CmdTurnRoadVeh(DoCommandFlag flags, VehicleID veh_id); -DEF_CMD_TRAIT(CMD_TURN_ROADVEH, CmdTurnRoadVeh, 0, CMDT_VEHICLE_MANAGEMENT) +DEF_CMD_TRAIT(CMD_TURN_ROADVEH, CmdTurnRoadVeh, CMD_LOCATION, CMDT_VEHICLE_MANAGEMENT) #endif /* ROADVEH_CMD_H */ diff --git a/src/script/api/script_object.cpp b/src/script/api/script_object.cpp index 1d02088376..1ae185b809 100644 --- a/src/script/api/script_object.cpp +++ b/src/script/api/script_object.cpp @@ -82,20 +82,18 @@ ScriptObject::ActiveInstance::~ActiveInstance() return GetStorage()->mode_instance; } -/* static */ void ScriptObject::SetLastCommand(TileIndex tile, const CommandDataBuffer &data, Commands cmd) +/* static */ void ScriptObject::SetLastCommand(const CommandDataBuffer &data, Commands cmd) { ScriptStorage *s = GetStorage(); - Debug(script, 6, "SetLastCommand company={:02d} tile={:06x} cmd={} data={}", s->root_company, tile, cmd, FormatArrayAsHex(data)); - s->last_tile = tile; + Debug(script, 6, "SetLastCommand company={:02d} cmd={} data={}", s->root_company, cmd, FormatArrayAsHex(data)); s->last_data = data; s->last_cmd = cmd; } -/* static */ bool ScriptObject::CheckLastCommand(TileIndex tile, const CommandDataBuffer &data, Commands cmd) +/* static */ bool ScriptObject::CheckLastCommand(const CommandDataBuffer &data, Commands cmd) { ScriptStorage *s = GetStorage(); - Debug(script, 6, "CheckLastCommand company={:02d} tile={:06x} cmd={} data={}", s->root_company, tile, cmd, FormatArrayAsHex(data)); - if (s->last_tile != tile) return false; + Debug(script, 6, "CheckLastCommand company={:02d} cmd={} data={}", s->root_company, cmd, FormatArrayAsHex(data)); if (s->last_cmd != cmd) return false; if (s->last_data != data) return false; return true; diff --git a/src/script/api/script_object.hpp b/src/script/api/script_object.hpp index 5c22a5c140..779381ca80 100644 --- a/src/script/api/script_object.hpp +++ b/src/script/api/script_object.hpp @@ -118,12 +118,12 @@ protected: /** * Store the latest command executed by the script. */ - static void SetLastCommand(TileIndex tile, const CommandDataBuffer &data, Commands cmd); + static void SetLastCommand(const CommandDataBuffer &data, Commands cmd); /** * Check if it's the latest command executed by the script. */ - static bool CheckLastCommand(TileIndex tile, const CommandDataBuffer &data, Commands cmd); + static bool CheckLastCommand(const CommandDataBuffer &data, Commands cmd); /** * Sets the DoCommand costs counter to a value. @@ -354,7 +354,7 @@ bool ScriptObject::ScriptDoCommandHelper if constexpr ((::GetCommandFlags() & CMD_CLIENT_ID) != 0) ScriptObjectInternal::SetClientIds(args, std::index_sequence_for{}); /* Store the command for command callback validation. */ - if (!estimate_only && networking) ScriptObject::SetLastCommand(tile, EndianBufferWriter::FromValue(args), Tcmd); + if (!estimate_only && networking) ScriptObject::SetLastCommand(EndianBufferWriter::FromValue(args), Tcmd); /* Try to perform the command. */ Tret res = ::Command::Unsafe((StringID)0, networking ? ScriptObject::GetDoCommandCallback() : nullptr, false, estimate_only, tile, args); diff --git a/src/script/script_instance.cpp b/src/script/script_instance.cpp index c02003a8a7..18f8ef39b6 100644 --- a/src/script/script_instance.cpp +++ b/src/script/script_instance.cpp @@ -744,11 +744,11 @@ SQInteger ScriptInstance::GetOpsTillSuspend() return this->engine->GetOpsTillSuspend(); } -bool ScriptInstance::DoCommandCallback(const CommandCost &result, TileIndex tile, const CommandDataBuffer &data, CommandDataBuffer result_data, Commands cmd) +bool ScriptInstance::DoCommandCallback(const CommandCost &result, const CommandDataBuffer &data, CommandDataBuffer result_data, Commands cmd) { ScriptObject::ActiveInstance active(this); - if (!ScriptObject::CheckLastCommand(tile, data, cmd)) { + if (!ScriptObject::CheckLastCommand(data, cmd)) { Debug(script, 1, "DoCommandCallback terminating a script, last command does not match expected command"); return false; } @@ -763,7 +763,7 @@ bool ScriptInstance::DoCommandCallback(const CommandCost &result, TileIndex tile ScriptObject::SetLastCost(result.GetCost()); } - ScriptObject::SetLastCommand(INVALID_TILE, {}, CMD_END); + ScriptObject::SetLastCommand({}, CMD_END); return true; } diff --git a/src/script/script_instance.hpp b/src/script/script_instance.hpp index ea7fcf1ed8..65a6414bb5 100644 --- a/src/script/script_instance.hpp +++ b/src/script/script_instance.hpp @@ -217,7 +217,7 @@ public: * @param cmd cmd as given to DoCommandPInternal. * @return true if we handled result. */ - bool DoCommandCallback(const CommandCost &result, TileIndex tile, const CommandDataBuffer &data, CommandDataBuffer result_data, Commands cmd); + bool DoCommandCallback(const CommandCost &result, const CommandDataBuffer &data, CommandDataBuffer result_data, Commands cmd); /** * Insert an event for this script. diff --git a/src/script/script_storage.hpp b/src/script/script_storage.hpp index 2afb03827d..44d17af837 100644 --- a/src/script/script_storage.hpp +++ b/src/script/script_storage.hpp @@ -44,7 +44,6 @@ private: uint last_error; ///< The last error of the command. bool last_command_res; ///< The last result of the command. - TileIndex last_tile; ///< The last tile passed to a command. CommandDataBuffer last_data; ///< The last data passed to a command. Commands last_cmd; ///< The last cmd passed to a command. CommandDataBuffer last_cmd_ret; ///< The extra data returned by the last command. @@ -69,7 +68,6 @@ public: last_cost (0), last_error (STR_NULL), last_command_res (true), - last_tile (INVALID_TILE), last_cmd (CMD_END), /* calback_value (can't be set) */ road_type (INVALID_ROADTYPE), diff --git a/src/town_cmd.h b/src/town_cmd.h index 74af1e10e1..52ea486c93 100644 --- a/src/town_cmd.h +++ b/src/town_cmd.h @@ -28,7 +28,7 @@ CommandCost CmdDeleteTown(DoCommandFlag flags, TownID town_id); DEF_CMD_TRAIT(CMD_FOUND_TOWN, CmdFoundTown, CMD_DEITY | CMD_NO_TEST, CMDT_LANDSCAPE_CONSTRUCTION) // founding random town can fail only in exec run DEF_CMD_TRAIT(CMD_RENAME_TOWN, CmdRenameTown, CMD_DEITY | CMD_SERVER, CMDT_OTHER_MANAGEMENT) -DEF_CMD_TRAIT(CMD_DO_TOWN_ACTION, CmdDoTownAction, 0, CMDT_LANDSCAPE_CONSTRUCTION) +DEF_CMD_TRAIT(CMD_DO_TOWN_ACTION, CmdDoTownAction, CMD_LOCATION, CMDT_LANDSCAPE_CONSTRUCTION) DEF_CMD_TRAIT(CMD_TOWN_CARGO_GOAL, CmdTownCargoGoal, CMD_DEITY, CMDT_OTHER_MANAGEMENT) DEF_CMD_TRAIT(CMD_TOWN_GROWTH_RATE, CmdTownGrowthRate, CMD_DEITY, CMDT_OTHER_MANAGEMENT) DEF_CMD_TRAIT(CMD_TOWN_RATING, CmdTownRating, CMD_DEITY, CMDT_OTHER_MANAGEMENT) diff --git a/src/train_cmd.h b/src/train_cmd.h index cb9e683682..3d224717a5 100644 --- a/src/train_cmd.h +++ b/src/train_cmd.h @@ -21,9 +21,9 @@ CommandCost CmdMoveRailVehicle(DoCommandFlag flags, VehicleID src_veh, VehicleID CommandCost CmdForceTrainProceed(DoCommandFlag flags, VehicleID veh_id); CommandCost CmdReverseTrainDirection(DoCommandFlag flags, VehicleID veh_id, bool reverse_single_veh); -DEF_CMD_TRAIT(CMD_MOVE_RAIL_VEHICLE, CmdMoveRailVehicle, 0, CMDT_VEHICLE_CONSTRUCTION) -DEF_CMD_TRAIT(CMD_FORCE_TRAIN_PROCEED, CmdForceTrainProceed, 0, CMDT_VEHICLE_MANAGEMENT) -DEF_CMD_TRAIT(CMD_REVERSE_TRAIN_DIRECTION, CmdReverseTrainDirection, 0, CMDT_VEHICLE_MANAGEMENT) +DEF_CMD_TRAIT(CMD_MOVE_RAIL_VEHICLE, CmdMoveRailVehicle, CMD_LOCATION, CMDT_VEHICLE_CONSTRUCTION) +DEF_CMD_TRAIT(CMD_FORCE_TRAIN_PROCEED, CmdForceTrainProceed, CMD_LOCATION, CMDT_VEHICLE_MANAGEMENT) +DEF_CMD_TRAIT(CMD_REVERSE_TRAIN_DIRECTION, CmdReverseTrainDirection, CMD_LOCATION, CMDT_VEHICLE_MANAGEMENT) void CcBuildWagon(Commands cmd, const CommandCost &result, VehicleID new_veh_id, uint, uint16, CargoArray, TileIndex tile, EngineID, bool, CargoID, ClientID); diff --git a/src/train_gui.cpp b/src/train_gui.cpp index 0745a08b2d..e6a3d0f135 100644 --- a/src/train_gui.cpp +++ b/src/train_gui.cpp @@ -44,7 +44,7 @@ void CcBuildWagon(Commands cmd, const CommandCost &result, VehicleID new_veh_id, if (found != nullptr) { found = found->Last(); /* put the new wagon at the end of the loco. */ - Command::Post(new_veh_id, found->index, false); + Command::Post(found->tile, new_veh_id, found->index, false); InvalidateWindowClassesData(WC_TRAINS_LIST, 0); } } diff --git a/src/vehicle_cmd.h b/src/vehicle_cmd.h index 0b9318d6b5..3f8faf5d7d 100644 --- a/src/vehicle_cmd.h +++ b/src/vehicle_cmd.h @@ -28,17 +28,17 @@ CommandCost CmdMassStartStopVehicle(DoCommandFlag flags, TileIndex tile, bool do CommandCost CmdDepotSellAllVehicles(DoCommandFlag flags, TileIndex tile, VehicleType vehicle_type); CommandCost CmdDepotMassAutoReplace(DoCommandFlag flags, TileIndex tile, VehicleType vehicle_type); -DEF_CMD_TRAIT(CMD_BUILD_VEHICLE, CmdBuildVehicle, CMD_CLIENT_ID, CMDT_VEHICLE_CONSTRUCTION) -DEF_CMD_TRAIT(CMD_SELL_VEHICLE, CmdSellVehicle, CMD_CLIENT_ID, CMDT_VEHICLE_CONSTRUCTION) -DEF_CMD_TRAIT(CMD_REFIT_VEHICLE, CmdRefitVehicle, 0, CMDT_VEHICLE_CONSTRUCTION) -DEF_CMD_TRAIT(CMD_SEND_VEHICLE_TO_DEPOT, CmdSendVehicleToDepot, 0, CMDT_VEHICLE_MANAGEMENT) -DEF_CMD_TRAIT(CMD_CHANGE_SERVICE_INT, CmdChangeServiceInt, 0, CMDT_VEHICLE_MANAGEMENT) -DEF_CMD_TRAIT(CMD_RENAME_VEHICLE, CmdRenameVehicle, 0, CMDT_OTHER_MANAGEMENT) -DEF_CMD_TRAIT(CMD_CLONE_VEHICLE, CmdCloneVehicle, CMD_NO_TEST, CMDT_VEHICLE_CONSTRUCTION) // NewGRF callbacks influence building and refitting making it impossible to correctly estimate the cost -DEF_CMD_TRAIT(CMD_START_STOP_VEHICLE, CmdStartStopVehicle, 0, CMDT_VEHICLE_MANAGEMENT) -DEF_CMD_TRAIT(CMD_MASS_START_STOP, CmdMassStartStopVehicle, 0, CMDT_VEHICLE_MANAGEMENT) -DEF_CMD_TRAIT(CMD_DEPOT_SELL_ALL_VEHICLES, CmdDepotSellAllVehicles, 0, CMDT_VEHICLE_CONSTRUCTION) -DEF_CMD_TRAIT(CMD_DEPOT_MASS_AUTOREPLACE, CmdDepotMassAutoReplace, 0, CMDT_VEHICLE_CONSTRUCTION) +DEF_CMD_TRAIT(CMD_BUILD_VEHICLE, CmdBuildVehicle, CMD_CLIENT_ID, CMDT_VEHICLE_CONSTRUCTION) +DEF_CMD_TRAIT(CMD_SELL_VEHICLE, CmdSellVehicle, CMD_CLIENT_ID | CMD_LOCATION, CMDT_VEHICLE_CONSTRUCTION) +DEF_CMD_TRAIT(CMD_REFIT_VEHICLE, CmdRefitVehicle, CMD_LOCATION, CMDT_VEHICLE_CONSTRUCTION) +DEF_CMD_TRAIT(CMD_SEND_VEHICLE_TO_DEPOT, CmdSendVehicleToDepot, 0, CMDT_VEHICLE_MANAGEMENT) +DEF_CMD_TRAIT(CMD_CHANGE_SERVICE_INT, CmdChangeServiceInt, 0, CMDT_VEHICLE_MANAGEMENT) +DEF_CMD_TRAIT(CMD_RENAME_VEHICLE, CmdRenameVehicle, 0, CMDT_OTHER_MANAGEMENT) +DEF_CMD_TRAIT(CMD_CLONE_VEHICLE, CmdCloneVehicle, CMD_NO_TEST, CMDT_VEHICLE_CONSTRUCTION) // NewGRF callbacks influence building and refitting making it impossible to correctly estimate the cost +DEF_CMD_TRAIT(CMD_START_STOP_VEHICLE, CmdStartStopVehicle, CMD_LOCATION, CMDT_VEHICLE_MANAGEMENT) +DEF_CMD_TRAIT(CMD_MASS_START_STOP, CmdMassStartStopVehicle, 0, CMDT_VEHICLE_MANAGEMENT) +DEF_CMD_TRAIT(CMD_DEPOT_SELL_ALL_VEHICLES, CmdDepotSellAllVehicles, 0, CMDT_VEHICLE_CONSTRUCTION) +DEF_CMD_TRAIT(CMD_DEPOT_MASS_AUTOREPLACE, CmdDepotMassAutoReplace, 0, CMDT_VEHICLE_CONSTRUCTION) void CcBuildPrimaryVehicle(Commands cmd, const CommandCost &result, VehicleID new_veh_id, uint, uint16, CargoArray); void CcStartStopVehicle(Commands cmd, const CommandCost &result, VehicleID veh_id, bool); From 2561fad5d4fc718e96364cc5ab4dfedbe4f0615b Mon Sep 17 00:00:00 2001 From: translators Date: Sat, 25 Feb 2023 18:45:15 +0000 Subject: [PATCH 05/47] Update: Translations from eints romanian: 1 change by bnegrut --- src/lang/romanian.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lang/romanian.txt b/src/lang/romanian.txt index 1ace894e34..65b342791c 100644 --- a/src/lang/romanian.txt +++ b/src/lang/romanian.txt @@ -4529,7 +4529,7 @@ STR_TIMETABLE_CLEAR_SPEED :{BLACK}Şterge STR_TIMETABLE_CLEAR_SPEED_TOOLTIP :{BLACK}Ștergeți viteza maximă de deplasare a comenzii evidențiate. Ctrl+Click șterge viteza pentru toate comenzile STR_TIMETABLE_RESET_LATENESS :{BLACK}Reinitializeaza contorul de intarziere -STR_TIMETABLE_RESET_LATENESS_TOOLTIP :{BLACK}Reiniţializează contorul de întârziere, astfel ca vehiculul să ajungă la timp +STR_TIMETABLE_RESET_LATENESS_TOOLTIP :{BLACK}Reiniţializează contorul de întârziere, astfel ca vehiculul să ajungă la timp. Ctrl+clic va reseta tot grupul astfel încât cel mai întârziat vehicul va ajunge la timp și toate celelalte vor ajunge mai devreme STR_TIMETABLE_AUTOFILL :{BLACK}Auto-completare STR_TIMETABLE_AUTOFILL_TOOLTIP :{BLACK}Completați automat orarul cu valorile din următoarea călătorie. Ctrl+Click pentru a încerca să păstrați timpii de așteptare From a409e4b026ae174d0dfa1f7121f6feed1131cbdb Mon Sep 17 00:00:00 2001 From: Rubidium Date: Sat, 25 Feb 2023 16:59:43 +0100 Subject: [PATCH 06/47] Add: documention to large functions that have too little documentation according to CodeQL --- src/gamelog.cpp | 12 ++++++++++++ src/landscape.cpp | 12 +++++++++++- src/newgrf_text.cpp | 9 ++++++++- src/order_gui.cpp | 8 ++++++++ src/spritecache.cpp | 2 +- src/table/sprites.h | 4 ++++ 6 files changed, 44 insertions(+), 3 deletions(-) diff --git a/src/gamelog.cpp b/src/gamelog.cpp index 5a6e4b477b..0e251aae00 100644 --- a/src/gamelog.cpp +++ b/src/gamelog.cpp @@ -212,11 +212,13 @@ void GamelogPrint(std::function proc) switch (lc->ct) { default: NOT_REACHED(); case GLCT_MODE: + /* Changing landscape, or going from scenario editor to game or back. */ buf += seprintf(buf, lastof(buffer), "New game mode: %u landscape: %u", (uint)lc->mode.mode, (uint)lc->mode.landscape); break; case GLCT_REVISION: + /* The game was loaded in a diffferent version than before. */ buf += seprintf(buf, lastof(buffer), "Revision text changed to %s, savegame version %u, ", lc->revision.text, lc->revision.slver); @@ -230,6 +232,7 @@ void GamelogPrint(std::function proc) break; case GLCT_OLDVER: + /* The game was loaded from before 0.7.0-beta1. */ buf += seprintf(buf, lastof(buffer), "Conversion from "); switch (lc->oldver.type) { default: NOT_REACHED(); @@ -260,10 +263,12 @@ void GamelogPrint(std::function proc) break; case GLCT_SETTING: + /* A setting with the SF_NO_NETWORK flag got changed; these settings usually affect NewGRFs, such as road side or wagon speed limits. */ buf += seprintf(buf, lastof(buffer), "Setting changed: %s : %d -> %d", lc->setting.name, lc->setting.oldval, lc->setting.newval); break; case GLCT_GRFADD: { + /* A NewGRF got added to the game, either at the start of the game (never an issue), or later on when it could be an issue. */ const GRFConfig *gc = FindGRFConfig(lc->grfadd.grfid, FGCM_EXACT, lc->grfadd.md5sum); buf += seprintf(buf, lastof(buffer), "Added NewGRF: "); buf = PrintGrfInfo(buf, lastof(buffer), lc->grfadd.grfid, lc->grfadd.md5sum, gc); @@ -274,6 +279,7 @@ void GamelogPrint(std::function proc) } case GLCT_GRFREM: { + /* A NewGRF got removed from the game, either manually or by it missing when loading the game. */ GrfIDMapping::Pair *gm = grf_names.Find(lc->grfrem.grfid); buf += seprintf(buf, lastof(buffer), la->at == GLAT_LOAD ? "Missing NewGRF: " : "Removed NewGRF: "); buf = PrintGrfInfo(buf, lastof(buffer), lc->grfrem.grfid, nullptr, gm != grf_names.End() ? gm->second.gc : nullptr); @@ -291,6 +297,7 @@ void GamelogPrint(std::function proc) } case GLCT_GRFCOMPAT: { + /* Another version of the same NewGRF got loaded. */ const GRFConfig *gc = FindGRFConfig(lc->grfadd.grfid, FGCM_EXACT, lc->grfadd.md5sum); buf += seprintf(buf, lastof(buffer), "Compatible NewGRF loaded: "); buf = PrintGrfInfo(buf, lastof(buffer), lc->grfcompat.grfid, lc->grfcompat.md5sum, gc); @@ -300,6 +307,7 @@ void GamelogPrint(std::function proc) } case GLCT_GRFPARAM: { + /* A parameter of a NewGRF got changed after the game was started. */ GrfIDMapping::Pair *gm = grf_names.Find(lc->grfrem.grfid); buf += seprintf(buf, lastof(buffer), "GRF parameter changed: "); buf = PrintGrfInfo(buf, lastof(buffer), lc->grfparam.grfid, nullptr, gm != grf_names.End() ? gm->second.gc : nullptr); @@ -308,6 +316,7 @@ void GamelogPrint(std::function proc) } case GLCT_GRFMOVE: { + /* The order of NewGRFs got changed, which might cause some other NewGRFs to behave differently. */ GrfIDMapping::Pair *gm = grf_names.Find(lc->grfrem.grfid); buf += seprintf(buf, lastof(buffer), "GRF order changed: %08X moved %d places %s", BSWAP32(lc->grfmove.grfid), abs(lc->grfmove.offset), lc->grfmove.offset >= 0 ? "down" : "up" ); @@ -317,6 +326,7 @@ void GamelogPrint(std::function proc) } case GLCT_GRFBUG: { + /* A specific bug in a NewGRF, that could cause wide spread problems, has been noted during the execution of the game. */ GrfIDMapping::Pair *gm = grf_names.Find(lc->grfrem.grfid); switch (lc->grfbug.bug) { default: NOT_REACHED(); @@ -330,6 +340,8 @@ void GamelogPrint(std::function proc) } case GLCT_EMERGENCY: + /* At one point the savegame was made during the handling of a game crash. + * The generic code already mentioned the emergency savegame, and there is no extra information to log. */ break; } diff --git a/src/landscape.cpp b/src/landscape.cpp index eacac6be24..af70e74982 100644 --- a/src/landscape.cpp +++ b/src/landscape.cpp @@ -863,12 +863,15 @@ static void GenerateTerrain(int type, uint flag) { uint32 r = Random(); - const Sprite *templ = GetSprite((((r >> 24) * _genterrain_tbl_1[type]) >> 8) + _genterrain_tbl_2[type] + 4845, ST_MAPGEN); + /* Choose one of the templates from the graphics file. */ + const Sprite *templ = GetSprite((((r >> 24) * _genterrain_tbl_1[type]) >> 8) + _genterrain_tbl_2[type] + SPR_MAPGEN_BEGIN, ST_MAPGEN); if (templ == nullptr) usererror("Map generator sprites could not be loaded"); + /* Chose a random location to apply the template to. */ uint x = r & Map::MaxX(); uint y = (r >> Map::LogX()) & Map::MaxY(); + /* Make sure the template is not too close to the upper edges; bottom edges are checked later. */ uint edge_distance = 1 + (_settings_game.construction.freeform_edges ? 1 : 0); if (x <= edge_distance || y <= edge_distance) return; @@ -881,6 +884,9 @@ static void GenerateTerrain(int type, uint flag) const byte *p = templ->data; if ((flag & 4) != 0) { + /* This is only executed in secondary/tertiary loops to generate the terrain for arctic and tropic. + * It prevents the templates to be applied to certain parts of the map based on the flags, thus + * creating regions with different elevations/topography. */ uint xw = x * Map::SizeY(); uint yw = y * Map::SizeX(); uint bias = (Map::SizeX() + Map::SizeY()) * 16; @@ -905,11 +911,15 @@ static void GenerateTerrain(int type, uint flag) } } + /* Ensure the template does not overflow at the bottom edges of the map; upper edges were checked before. */ if (x + w >= Map::MaxX()) return; if (y + h >= Map::MaxY()) return; TileIndex tile = TileXY(x, y); + /* Get the template and overlay in a particular direction over the map's height from the given + * origin point (tile), and update the map's height everywhere where the height from the template + * is higher than the height of the map. In other words, this only raises the tile heights. */ switch (direction) { default: NOT_REACHED(); case DIAGDIR_NE: diff --git a/src/newgrf_text.cpp b/src/newgrf_text.cpp index 6617365f9c..3c2d229468 100644 --- a/src/newgrf_text.cpp +++ b/src/newgrf_text.cpp @@ -860,6 +860,7 @@ uint RemapNewGRFStringControlCode(uint scc, char *buf_start, char **buff, const switch (scc) { default: break; + /* These control codes take one string parameter, check there are at least that many available. */ case SCC_NEWGRF_PRINT_DWORD_SIGNED: case SCC_NEWGRF_PRINT_WORD_SIGNED: case SCC_NEWGRF_PRINT_BYTE_SIGNED: @@ -889,6 +890,7 @@ uint RemapNewGRFStringControlCode(uint scc, char *buf_start, char **buff, const } break; + /* These string code take two string parameters, check there are at least that many available. */ case SCC_NEWGRF_PRINT_WORD_CARGO_LONG: case SCC_NEWGRF_PRINT_WORD_CARGO_SHORT: case SCC_NEWGRF_PRINT_WORD_CARGO_TINY: @@ -900,6 +902,8 @@ uint RemapNewGRFStringControlCode(uint scc, char *buf_start, char **buff, const } if (_newgrf_textrefstack.used && modify_argv) { + /* There is data on the NewGRF text stack, and we want to move them to OpenTTD's string stack. + * After this call, a new call is made with `modify_argv` set to false when the string is finally formatted. */ switch (scc) { default: NOT_REACHED(); case SCC_NEWGRF_PRINT_BYTE_SIGNED: *argv = _newgrf_textrefstack.PopSignedByte(); break; @@ -927,6 +931,7 @@ uint RemapNewGRFStringControlCode(uint scc, char *buf_start, char **buff, const case SCC_NEWGRF_PRINT_DWORD_DATE_SHORT: case SCC_NEWGRF_PRINT_DWORD_HEX: *argv = _newgrf_textrefstack.PopUnsignedDWord(); break; + /* Dates from NewGRFs have 1920-01-01 as their zero point, convert it to OpenTTD's epoch. */ case SCC_NEWGRF_PRINT_WORD_DATE_LONG: case SCC_NEWGRF_PRINT_WORD_DATE_SHORT: *argv = _newgrf_textrefstack.PopUnsignedWord() + DAYS_TILL_ORIGINAL_BASE_YEAR; break; @@ -954,7 +959,7 @@ uint RemapNewGRFStringControlCode(uint scc, char *buf_start, char **buff, const } } } else { - /* Consume additional parameter characters */ + /* Consume additional parameter characters that follow the NewGRF string code. */ switch (scc) { default: break; @@ -965,6 +970,7 @@ uint RemapNewGRFStringControlCode(uint scc, char *buf_start, char **buff, const } } + /* Emit OpenTTD's internal string code for the different NewGRF variants. */ switch (scc) { default: NOT_REACHED(); case SCC_NEWGRF_PRINT_DWORD_SIGNED: @@ -1027,6 +1033,7 @@ uint RemapNewGRFStringControlCode(uint scc, char *buf_start, char **buff, const case SCC_NEWGRF_PRINT_WORD_STATION_NAME: return SCC_STATION_NAME; + /* These NewGRF string codes modify the NewGRF stack or otherwise do not map to OpenTTD string codes. */ case SCC_NEWGRF_DISCARD_WORD: case SCC_NEWGRF_ROTATE_TOP_4_WORDS: case SCC_NEWGRF_PUSH_WORD: diff --git a/src/order_gui.cpp b/src/order_gui.cpp index 134b488960..db9074784b 100644 --- a/src/order_gui.cpp +++ b/src/order_gui.cpp @@ -223,9 +223,11 @@ void DrawOrderString(const Vehicle *v, const Order *order, int order_index, int SpriteID sprite = rtl ? SPR_ARROW_LEFT : SPR_ARROW_RIGHT; Dimension sprite_size = GetSpriteSize(sprite); if (v->cur_real_order_index == order_index) { + /* Draw two arrows before the next real order. */ DrawSprite(sprite, PAL_NONE, rtl ? right - sprite_size.width : left, y + ((int)FONT_HEIGHT_NORMAL - (int)sprite_size.height) / 2); DrawSprite(sprite, PAL_NONE, rtl ? right - 2 * sprite_size.width : left + sprite_size.width, y + ((int)FONT_HEIGHT_NORMAL - (int)sprite_size.height) / 2); } else if (v->cur_implicit_order_index == order_index) { + /* Draw one arrow before the next implicit order; the next real order will still get two arrows. */ DrawSprite(sprite, PAL_NONE, rtl ? right - sprite_size.width : left, y + ((int)FONT_HEIGHT_NORMAL - (int)sprite_size.height) / 2); } @@ -271,6 +273,7 @@ void DrawOrderString(const Vehicle *v, const Order *order, int order_index, int SetDParam(2, order->GetDestination()); if (timetable) { + /* Show only wait time in the timetable window. */ SetDParam(3, STR_EMPTY); if (order->GetWaitTime() > 0) { @@ -278,6 +281,7 @@ void DrawOrderString(const Vehicle *v, const Order *order, int order_index, int SetTimetableParams(6, 7, order->GetWaitTime()); } } else { + /* Show non-stop, refit and stop location only in the order window. */ SetDParam(3, (order->GetNonStopType() & ONSF_NO_STOP_AT_DESTINATION_STATION) ? STR_EMPTY : _station_load_types[order->IsRefit()][unload][load]); if (order->IsRefit()) { SetDParam(4, order->IsAutoRefit() ? STR_ORDER_AUTO_REFIT_ANY : CargoSpec::Get(order->GetRefitCargo())->name); @@ -291,6 +295,7 @@ void DrawOrderString(const Vehicle *v, const Order *order, int order_index, int case OT_GOTO_DEPOT: if (order->GetDepotActionType() & ODATFB_NEAREST_DEPOT) { + /* Going to the nearest depot. */ SetDParam(0, STR_ORDER_GO_TO_NEAREST_DEPOT_FORMAT); if (v->type == VEH_AIRCRAFT) { SetDParam(2, STR_ORDER_NEAREST_HANGAR); @@ -300,6 +305,7 @@ void DrawOrderString(const Vehicle *v, const Order *order, int order_index, int SetDParam(3, STR_ORDER_TRAIN_DEPOT + v->type); } } else { + /* Going to a specific depot. */ SetDParam(0, STR_ORDER_GO_TO_DEPOT_FORMAT); SetDParam(2, v->type); SetDParam(3, order->GetDestination()); @@ -311,10 +317,12 @@ void DrawOrderString(const Vehicle *v, const Order *order, int order_index, int SetDParam(1, (order->GetNonStopType() & ONSF_NO_STOP_AT_INTERMEDIATE_STATIONS) ? STR_ORDER_GO_NON_STOP_TO : STR_ORDER_GO_TO); } + /* Do not show stopping in the depot in the timetable window. */ if (!timetable && (order->GetDepotActionType() & ODATFB_HALT)) { SetDParam(5, STR_ORDER_STOP_ORDER); } + /* Do not show refitting in the depot in the timetable window. */ if (!timetable && order->IsRefit()) { SetDParam(5, (order->GetDepotActionType() & ODATFB_HALT) ? STR_ORDER_REFIT_STOP_ORDER : STR_ORDER_REFIT_ORDER); SetDParam(6, CargoSpec::Get(order->GetRefitCargo())->name); diff --git a/src/spritecache.cpp b/src/spritecache.cpp index 08b1ad85a9..aee8661c4d 100644 --- a/src/spritecache.cpp +++ b/src/spritecache.cpp @@ -51,7 +51,7 @@ static inline SpriteCache *GetSpriteCache(uint index) static inline bool IsMapgenSpriteID(SpriteID sprite) { - return IsInsideMM(sprite, 4845, 4882); + return IsInsideMM(sprite, SPR_MAPGEN_BEGIN, SPR_MAPGEN_END); } static SpriteCache *AllocateSpriteCache(uint index) diff --git a/src/table/sprites.h b/src/table/sprites.h index 407a7a2d87..907d37fdcc 100644 --- a/src/table/sprites.h +++ b/src/table/sprites.h @@ -1126,6 +1126,10 @@ static const SpriteID SPR_OTTD_N = 4839; static const SpriteID SPR_OTTD_T = 4836; /* Letters not used: R,A,S,Y,C (4837, 4838, 4840, 4843, 4844) */ +/* Range of "special" sprites that are used by the old map generation algorithm. */ +static const SpriteID SPR_MAPGEN_BEGIN = 4845; +static const SpriteID SPR_MAPGEN_END = 4882; + static const SpriteID SPR_HIGHSCORE_CHART_BEGIN = 4804; static const SpriteID SPR_TYCOON_IMG1_BEGIN = 4814; static const SpriteID SPR_TYCOON_IMG2_BEGIN = 4824; From 8cbf1be9d68daa8c5e01a901ea006422e53a0179 Mon Sep 17 00:00:00 2001 From: Rubidium Date: Sat, 25 Feb 2023 17:08:43 +0100 Subject: [PATCH 07/47] Codechange: split large function into smaller functions with self-explanatory names --- src/industry_cmd.cpp | 275 +++++++++++++++++++++++-------------------- src/station_cmd.cpp | 134 +++++++++++---------- 2 files changed, 219 insertions(+), 190 deletions(-) diff --git a/src/industry_cmd.cpp b/src/industry_cmd.cpp index 2bfee78afa..5870be9d2d 100644 --- a/src/industry_cmd.cpp +++ b/src/industry_cmd.cpp @@ -554,171 +554,194 @@ static bool TransportIndustryGoods(TileIndex tile) return moved_cargo; } +static void AnimateSugarSieve(TileIndex tile) +{ + byte m = GetAnimationFrame(tile) + 1; -static void AnimateTile_Industry(TileIndex tile) + if (_settings_client.sound.ambient) { + switch (m & 7) { + case 2: SndPlayTileFx(SND_2D_SUGAR_MINE_1, tile); break; + case 6: SndPlayTileFx(SND_29_SUGAR_MINE_2, tile); break; + } + } + + if (m >= 96) { + m = 0; + DeleteAnimatedTile(tile); + } + SetAnimationFrame(tile, m); + + MarkTileDirtyByTile(tile); +} + +static void AnimateToffeeQuarry(TileIndex tile) { - IndustryGfx gfx = GetIndustryGfx(tile); + byte m = GetAnimationFrame(tile); - if (GetIndustryTileSpec(gfx)->animation.status != ANIM_STATUS_NO_ANIMATION) { - AnimateNewIndustryTile(tile); - return; + if (_industry_anim_offs_toffee[m] == 0xFF && _settings_client.sound.ambient) { + SndPlayTileFx(SND_30_TOFFEE_QUARRY, tile); } - switch (gfx) { - case GFX_SUGAR_MINE_SIEVE: - if ((_tick_counter & 1) == 0) { - byte m = GetAnimationFrame(tile) + 1; + if (++m >= 70) { + m = 0; + DeleteAnimatedTile(tile); + } + SetAnimationFrame(tile, m); - if (_settings_client.sound.ambient) { - switch (m & 7) { - case 2: SndPlayTileFx(SND_2D_SUGAR_MINE_1, tile); break; - case 6: SndPlayTileFx(SND_29_SUGAR_MINE_2, tile); break; - } - } + MarkTileDirtyByTile(tile); +} - if (m >= 96) { - m = 0; - DeleteAnimatedTile(tile); - } - SetAnimationFrame(tile, m); +static void AnimateBubbleCatcher(TileIndex tile) +{ + byte m = GetAnimationFrame(tile); - MarkTileDirtyByTile(tile); - } - break; + if (++m >= 40) { + m = 0; + DeleteAnimatedTile(tile); + } + SetAnimationFrame(tile, m); - case GFX_TOFFEE_QUARY: - if ((_tick_counter & 3) == 0) { - byte m = GetAnimationFrame(tile); + MarkTileDirtyByTile(tile); +} - if (_industry_anim_offs_toffee[m] == 0xFF && _settings_client.sound.ambient) { - SndPlayTileFx(SND_30_TOFFEE_QUARRY, tile); - } +static void AnimatePowerPlantSparks(TileIndex tile) +{ + byte m = GetAnimationFrame(tile); + if (m == 6) { + SetAnimationFrame(tile, 0); + DeleteAnimatedTile(tile); + } else { + SetAnimationFrame(tile, m + 1); + MarkTileDirtyByTile(tile); + } +} + +static void AnimateToyFactory(TileIndex tile) +{ + byte m = GetAnimationFrame(tile) + 1; - if (++m >= 70) { + switch (m) { + case 1: if (_settings_client.sound.ambient) SndPlayTileFx(SND_2C_TOY_FACTORY_1, tile); break; + case 23: if (_settings_client.sound.ambient) SndPlayTileFx(SND_2B_TOY_FACTORY_2, tile); break; + case 28: if (_settings_client.sound.ambient) SndPlayTileFx(SND_2A_TOY_FACTORY_3, tile); break; + default: + if (m >= 50) { + int n = GetIndustryAnimationLoop(tile) + 1; m = 0; - DeleteAnimatedTile(tile); + if (n >= 8) { + n = 0; + DeleteAnimatedTile(tile); + } + SetIndustryAnimationLoop(tile, n); } - SetAnimationFrame(tile, m); + } - MarkTileDirtyByTile(tile); - } - break; + SetAnimationFrame(tile, m); + MarkTileDirtyByTile(tile); +} - case GFX_BUBBLE_CATCHER: - if ((_tick_counter & 1) == 0) { - byte m = GetAnimationFrame(tile); +static void AnimatePlasticFountain(TileIndex tile, IndustryGfx gfx) +{ + gfx = (gfx < GFX_PLASTIC_FOUNTAIN_ANIMATED_8) ? gfx + 1 : GFX_PLASTIC_FOUNTAIN_ANIMATED_1; + SetIndustryGfx(tile, gfx); + MarkTileDirtyByTile(tile); +} - if (++m >= 40) { - m = 0; - DeleteAnimatedTile(tile); - } - SetAnimationFrame(tile, m); +static void AnimateOilWell(TileIndex tile, IndustryGfx gfx) +{ + bool b = Chance16(1, 7); + byte m = GetAnimationFrame(tile) + 1; + if (m == 4 && (m = 0, ++gfx) == GFX_OILWELL_ANIMATED_3 + 1 && (gfx = GFX_OILWELL_ANIMATED_1, b)) { + SetIndustryGfx(tile, GFX_OILWELL_NOT_ANIMATED); + SetIndustryConstructionStage(tile, 3); + DeleteAnimatedTile(tile); + } else { + SetAnimationFrame(tile, m); + SetIndustryGfx(tile, gfx); + MarkTileDirtyByTile(tile); + } +} - MarkTileDirtyByTile(tile); - } - break; +static void AnimateMineTower(TileIndex tile) +{ + int state = _tick_counter & 0x7FF; - /* Sparks on a coal plant */ - case GFX_POWERPLANT_SPARKS: - if ((_tick_counter & 3) == 0) { + if ((state -= 0x400) < 0) return; + + if (state < 0x1A0) { + if (state < 0x20 || state >= 0x180) { byte m = GetAnimationFrame(tile); - if (m == 6) { - SetAnimationFrame(tile, 0); - DeleteAnimatedTile(tile); - } else { - SetAnimationFrame(tile, m + 1); - MarkTileDirtyByTile(tile); + if (!(m & 0x40)) { + SetAnimationFrame(tile, m | 0x40); + if (_settings_client.sound.ambient) SndPlayTileFx(SND_0B_MINE, tile); } + if (state & 7) return; + } else { + if (state & 3) return; } + byte m = (GetAnimationFrame(tile) + 1) | 0x40; + if (m > 0xC2) m = 0xC0; + SetAnimationFrame(tile, m); + MarkTileDirtyByTile(tile); + } else if (state >= 0x200 && state < 0x3A0) { + int i = (state < 0x220 || state >= 0x380) ? 7 : 3; + if (state & i) return; + + byte m = (GetAnimationFrame(tile) & 0xBF) - 1; + if (m < 0x80) m = 0x82; + SetAnimationFrame(tile, m); + MarkTileDirtyByTile(tile); + } +} + +static void AnimateTile_Industry(TileIndex tile) +{ + IndustryGfx gfx = GetIndustryGfx(tile); + + if (GetIndustryTileSpec(gfx)->animation.status != ANIM_STATUS_NO_ANIMATION) { + AnimateNewIndustryTile(tile); + return; + } + + switch (gfx) { + case GFX_SUGAR_MINE_SIEVE: + if ((_tick_counter & 1) == 0) AnimateSugarSieve(tile); break; - case GFX_TOY_FACTORY: - if ((_tick_counter & 1) == 0) { - byte m = GetAnimationFrame(tile) + 1; - - switch (m) { - case 1: if (_settings_client.sound.ambient) SndPlayTileFx(SND_2C_TOY_FACTORY_1, tile); break; - case 23: if (_settings_client.sound.ambient) SndPlayTileFx(SND_2B_TOY_FACTORY_2, tile); break; - case 28: if (_settings_client.sound.ambient) SndPlayTileFx(SND_2A_TOY_FACTORY_3, tile); break; - default: - if (m >= 50) { - int n = GetIndustryAnimationLoop(tile) + 1; - m = 0; - if (n >= 8) { - n = 0; - DeleteAnimatedTile(tile); - } - SetIndustryAnimationLoop(tile, n); - } - } + case GFX_TOFFEE_QUARY: + if ((_tick_counter & 3) == 0) AnimateToffeeQuarry(tile); + break; - SetAnimationFrame(tile, m); - MarkTileDirtyByTile(tile); - } + case GFX_BUBBLE_CATCHER: + if ((_tick_counter & 1) == 0) AnimateBubbleCatcher(tile); + break; + + case GFX_POWERPLANT_SPARKS: + if ((_tick_counter & 3) == 0) AnimatePowerPlantSparks(tile); + break; + + case GFX_TOY_FACTORY: + if ((_tick_counter & 1) == 0) AnimateToyFactory(tile); break; case GFX_PLASTIC_FOUNTAIN_ANIMATED_1: case GFX_PLASTIC_FOUNTAIN_ANIMATED_2: case GFX_PLASTIC_FOUNTAIN_ANIMATED_3: case GFX_PLASTIC_FOUNTAIN_ANIMATED_4: case GFX_PLASTIC_FOUNTAIN_ANIMATED_5: case GFX_PLASTIC_FOUNTAIN_ANIMATED_6: case GFX_PLASTIC_FOUNTAIN_ANIMATED_7: case GFX_PLASTIC_FOUNTAIN_ANIMATED_8: - if ((_tick_counter & 3) == 0) { - gfx = (gfx < 155) ? gfx + 1 : 148; - SetIndustryGfx(tile, gfx); - MarkTileDirtyByTile(tile); - } + if ((_tick_counter & 3) == 0) AnimatePlasticFountain(tile, gfx); break; case GFX_OILWELL_ANIMATED_1: case GFX_OILWELL_ANIMATED_2: case GFX_OILWELL_ANIMATED_3: - if ((_tick_counter & 7) == 0) { - bool b = Chance16(1, 7); - byte m = GetAnimationFrame(tile) + 1; - if (m == 4 && (m = 0, ++gfx) == GFX_OILWELL_ANIMATED_3 + 1 && (gfx = GFX_OILWELL_ANIMATED_1, b)) { - SetIndustryGfx(tile, GFX_OILWELL_NOT_ANIMATED); - SetIndustryConstructionStage(tile, 3); - DeleteAnimatedTile(tile); - } else { - SetAnimationFrame(tile, m); - SetIndustryGfx(tile, gfx); - MarkTileDirtyByTile(tile); - } - } + if ((_tick_counter & 7) == 0) AnimateOilWell(tile, gfx); break; case GFX_COAL_MINE_TOWER_ANIMATED: case GFX_COPPER_MINE_TOWER_ANIMATED: - case GFX_GOLD_MINE_TOWER_ANIMATED: { - int state = _tick_counter & 0x7FF; - - if ((state -= 0x400) < 0) return; - - if (state < 0x1A0) { - if (state < 0x20 || state >= 0x180) { - byte m = GetAnimationFrame(tile); - if (!(m & 0x40)) { - SetAnimationFrame(tile, m | 0x40); - if (_settings_client.sound.ambient) SndPlayTileFx(SND_0B_MINE, tile); - } - if (state & 7) return; - } else { - if (state & 3) return; - } - byte m = (GetAnimationFrame(tile) + 1) | 0x40; - if (m > 0xC2) m = 0xC0; - SetAnimationFrame(tile, m); - MarkTileDirtyByTile(tile); - } else if (state >= 0x200 && state < 0x3A0) { - int i = (state < 0x220 || state >= 0x380) ? 7 : 3; - if (state & i) return; - - byte m = (GetAnimationFrame(tile) & 0xBF) - 1; - if (m < 0x80) m = 0x82; - SetAnimationFrame(tile, m); - MarkTileDirtyByTile(tile); - } - break; - } + case GFX_GOLD_MINE_TOWER_ANIMATED: + AnimateMineTower(tile); + break; } } diff --git a/src/station_cmd.cpp b/src/station_cmd.cpp index 92a5faae4e..a4479deca4 100644 --- a/src/station_cmd.cpp +++ b/src/station_cmd.cpp @@ -3091,83 +3091,89 @@ static Foundation GetFoundation_Station(TileIndex tile, Slope tileh) return FlatteningFoundation(tileh); } -static void GetTileDesc_Station(TileIndex tile, TileDesc *td) +static void FillTileDescRoadStop(TileIndex tile, TileDesc *td) { - td->owner[0] = GetTileOwner(tile); - - if (IsRoadStopTile(tile)) { - RoadType road_rt = GetRoadTypeRoad(tile); - RoadType tram_rt = GetRoadTypeTram(tile); - Owner road_owner = INVALID_OWNER; - Owner tram_owner = INVALID_OWNER; - if (road_rt != INVALID_ROADTYPE) { - const RoadTypeInfo *rti = GetRoadTypeInfo(road_rt); - td->roadtype = rti->strings.name; - td->road_speed = rti->max_speed / 2; - road_owner = GetRoadOwner(tile, RTT_ROAD); - } - - if (tram_rt != INVALID_ROADTYPE) { - const RoadTypeInfo *rti = GetRoadTypeInfo(tram_rt); - td->tramtype = rti->strings.name; - td->tram_speed = rti->max_speed / 2; - tram_owner = GetRoadOwner(tile, RTT_TRAM); - } - - if (IsDriveThroughStopTile(tile)) { - /* Is there a mix of owners? */ - if ((tram_owner != INVALID_OWNER && tram_owner != td->owner[0]) || - (road_owner != INVALID_OWNER && road_owner != td->owner[0])) { - uint i = 1; - if (road_owner != INVALID_OWNER) { - td->owner_type[i] = STR_LAND_AREA_INFORMATION_ROAD_OWNER; - td->owner[i] = road_owner; - i++; - } - if (tram_owner != INVALID_OWNER) { - td->owner_type[i] = STR_LAND_AREA_INFORMATION_TRAM_OWNER; - td->owner[i] = tram_owner; - } + RoadType road_rt = GetRoadTypeRoad(tile); + RoadType tram_rt = GetRoadTypeTram(tile); + Owner road_owner = INVALID_OWNER; + Owner tram_owner = INVALID_OWNER; + if (road_rt != INVALID_ROADTYPE) { + const RoadTypeInfo *rti = GetRoadTypeInfo(road_rt); + td->roadtype = rti->strings.name; + td->road_speed = rti->max_speed / 2; + road_owner = GetRoadOwner(tile, RTT_ROAD); + } + + if (tram_rt != INVALID_ROADTYPE) { + const RoadTypeInfo *rti = GetRoadTypeInfo(tram_rt); + td->tramtype = rti->strings.name; + td->tram_speed = rti->max_speed / 2; + tram_owner = GetRoadOwner(tile, RTT_TRAM); + } + + if (IsDriveThroughStopTile(tile)) { + /* Is there a mix of owners? */ + if ((tram_owner != INVALID_OWNER && tram_owner != td->owner[0]) || + (road_owner != INVALID_OWNER && road_owner != td->owner[0])) { + uint i = 1; + if (road_owner != INVALID_OWNER) { + td->owner_type[i] = STR_LAND_AREA_INFORMATION_ROAD_OWNER; + td->owner[i] = road_owner; + i++; + } + if (tram_owner != INVALID_OWNER) { + td->owner_type[i] = STR_LAND_AREA_INFORMATION_TRAM_OWNER; + td->owner[i] = tram_owner; } } } +} - td->build_date = BaseStation::GetByTile(tile)->build_date; - - if (HasStationTileRail(tile)) { - const StationSpec *spec = GetStationSpec(tile); +void FillTileDescRailStation(TileIndex tile, TileDesc *td) +{ + const StationSpec *spec = GetStationSpec(tile); - if (spec != nullptr) { - td->station_class = StationClass::Get(spec->cls_id)->name; - td->station_name = spec->name; + if (spec != nullptr) { + td->station_class = StationClass::Get(spec->cls_id)->name; + td->station_name = spec->name; - if (spec->grf_prop.grffile != nullptr) { - const GRFConfig *gc = GetGRFConfig(spec->grf_prop.grffile->grfid); - td->grf = gc->GetName(); - } + if (spec->grf_prop.grffile != nullptr) { + const GRFConfig *gc = GetGRFConfig(spec->grf_prop.grffile->grfid); + td->grf = gc->GetName(); } - - const RailtypeInfo *rti = GetRailTypeInfo(GetRailType(tile)); - td->rail_speed = rti->max_speed; - td->railtype = rti->strings.name; } - if (IsAirport(tile)) { - const AirportSpec *as = Station::GetByTile(tile)->airport.GetSpec(); - td->airport_class = AirportClass::Get(as->cls_id)->name; - td->airport_name = as->name; + const RailtypeInfo *rti = GetRailTypeInfo(GetRailType(tile)); + td->rail_speed = rti->max_speed; + td->railtype = rti->strings.name; +} - const AirportTileSpec *ats = AirportTileSpec::GetByTile(tile); - td->airport_tile_name = ats->name; +void FillTileDescAirport(TileIndex tile, TileDesc *td) +{ + const AirportSpec *as = Station::GetByTile(tile)->airport.GetSpec(); + td->airport_class = AirportClass::Get(as->cls_id)->name; + td->airport_name = as->name; - if (as->grf_prop.grffile != nullptr) { - const GRFConfig *gc = GetGRFConfig(as->grf_prop.grffile->grfid); - td->grf = gc->GetName(); - } else if (ats->grf_prop.grffile != nullptr) { - const GRFConfig *gc = GetGRFConfig(ats->grf_prop.grffile->grfid); - td->grf = gc->GetName(); - } + const AirportTileSpec *ats = AirportTileSpec::GetByTile(tile); + td->airport_tile_name = ats->name; + + if (as->grf_prop.grffile != nullptr) { + const GRFConfig *gc = GetGRFConfig(as->grf_prop.grffile->grfid); + td->grf = gc->GetName(); + } else if (ats->grf_prop.grffile != nullptr) { + const GRFConfig *gc = GetGRFConfig(ats->grf_prop.grffile->grfid); + td->grf = gc->GetName(); } +} + +static void GetTileDesc_Station(TileIndex tile, TileDesc *td) +{ + td->owner[0] = GetTileOwner(tile); + td->build_date = BaseStation::GetByTile(tile)->build_date; + + if (IsRoadStop(tile)) FillTileDescRoadStop(tile, td); + if (HasStationRail(tile)) FillTileDescRailStation(tile, td); + if (IsAirport(tile)) FillTileDescAirport(tile, td); StringID str; switch (GetStationType(tile)) { From 947a789bf684c8a3adae6a75b0f6076cc241c7e5 Mon Sep 17 00:00:00 2001 From: Rubidium Date: Sat, 25 Feb 2023 19:02:22 +0100 Subject: [PATCH 08/47] Codechange: split large function into smaller steps and simplify logic --- src/company_gui.cpp | 129 +++++++++++++++++++++++--------------------- 1 file changed, 68 insertions(+), 61 deletions(-) diff --git a/src/company_gui.cpp b/src/company_gui.cpp index 821abd9f97..79cede9573 100644 --- a/src/company_gui.cpp +++ b/src/company_gui.cpp @@ -2444,6 +2444,70 @@ struct CompanyWindow : Window } } + void DrawVehicleCountsWidget(const Rect &r, const Company *c) const + { + static_assert(VEH_COMPANY_END == lengthof(_company_view_vehicle_count_strings)); + + int y = r.top; + for (VehicleType type = VEH_BEGIN; type < VEH_COMPANY_END; type++) { + uint amount = c->group_all[type].num_vehicle; + if (amount != 0) { + SetDParam(0, amount); + DrawString(r.left, r.right, y, _company_view_vehicle_count_strings[type]); + y += FONT_HEIGHT_NORMAL; + } + } + + if (y == r.top) { + /* No String was emited before, so there must be no vehicles at all. */ + DrawString(r.left, r.right, y, STR_COMPANY_VIEW_VEHICLES_NONE); + } + } + + void DrawInfrastructureCountsWidget(const Rect &r, const Company *c) const + { + int y = r.top; + + uint rail_pieces = c->infrastructure.signal; + for (uint i = 0; i < lengthof(c->infrastructure.rail); i++) rail_pieces += c->infrastructure.rail[i]; + if (rail_pieces != 0) { + SetDParam(0, rail_pieces); + DrawString(r.left, r.right, y, STR_COMPANY_VIEW_INFRASTRUCTURE_RAIL); + y += FONT_HEIGHT_NORMAL; + } + + uint road_pieces = 0; + for (uint i = 0; i < lengthof(c->infrastructure.road); i++) road_pieces += c->infrastructure.road[i]; + if (road_pieces != 0) { + SetDParam(0, road_pieces); + DrawString(r.left, r.right, y, STR_COMPANY_VIEW_INFRASTRUCTURE_ROAD); + y += FONT_HEIGHT_NORMAL; + } + + if (c->infrastructure.water != 0) { + SetDParam(0, c->infrastructure.water); + DrawString(r.left, r.right, y, STR_COMPANY_VIEW_INFRASTRUCTURE_WATER); + y += FONT_HEIGHT_NORMAL; + } + + if (c->infrastructure.station != 0) { + SetDParam(0, c->infrastructure.station); + DrawString(r.left, r.right, y, STR_COMPANY_VIEW_INFRASTRUCTURE_STATION); + y += FONT_HEIGHT_NORMAL; + } + + if (c->infrastructure.airport != 0) { + SetDParam(0, c->infrastructure.airport); + DrawString(r.left, r.right, y, STR_COMPANY_VIEW_INFRASTRUCTURE_AIRPORT); + y += FONT_HEIGHT_NORMAL; + } + + if (y == r.top) { + /* No String was emited before, so there must be no infrastructure at all. */ + DrawString(r.left, r.right, y, STR_COMPANY_VIEW_INFRASTRUCTURE_NONE); + } + } + void DrawWidget(const Rect &r, int widget) const override { const Company *c = Company::Get((CompanyID)this->window_number); @@ -2465,70 +2529,13 @@ struct CompanyWindow : Window break; } - case WID_C_DESC_VEHICLE_COUNTS: { - uint amounts[4]; - amounts[0] = c->group_all[VEH_TRAIN].num_vehicle; - amounts[1] = c->group_all[VEH_ROAD].num_vehicle; - amounts[2] = c->group_all[VEH_SHIP].num_vehicle; - amounts[3] = c->group_all[VEH_AIRCRAFT].num_vehicle; - - int y = r.top; - if (amounts[0] + amounts[1] + amounts[2] + amounts[3] == 0) { - DrawString(r.left, r.right, y, STR_COMPANY_VIEW_VEHICLES_NONE); - } else { - static_assert(lengthof(amounts) == lengthof(_company_view_vehicle_count_strings)); - - for (uint i = 0; i < lengthof(amounts); i++) { - if (amounts[i] != 0) { - SetDParam(0, amounts[i]); - DrawString(r.left, r.right, y, _company_view_vehicle_count_strings[i]); - y += FONT_HEIGHT_NORMAL; - } - } - } + case WID_C_DESC_VEHICLE_COUNTS: + DrawVehicleCountsWidget(r, c); break; - } - - case WID_C_DESC_INFRASTRUCTURE_COUNTS: { - uint y = r.top; - - /* Collect rail and road counts. */ - uint rail_pieces = c->infrastructure.signal; - uint road_pieces = 0; - for (uint i = 0; i < lengthof(c->infrastructure.rail); i++) rail_pieces += c->infrastructure.rail[i]; - for (uint i = 0; i < lengthof(c->infrastructure.road); i++) road_pieces += c->infrastructure.road[i]; - - if (rail_pieces == 0 && road_pieces == 0 && c->infrastructure.water == 0 && c->infrastructure.station == 0 && c->infrastructure.airport == 0) { - DrawString(r.left, r.right, y, STR_COMPANY_VIEW_INFRASTRUCTURE_NONE); - } else { - if (rail_pieces != 0) { - SetDParam(0, rail_pieces); - DrawString(r.left, r.right, y, STR_COMPANY_VIEW_INFRASTRUCTURE_RAIL); - y += FONT_HEIGHT_NORMAL; - } - if (road_pieces != 0) { - SetDParam(0, road_pieces); - DrawString(r.left, r.right, y, STR_COMPANY_VIEW_INFRASTRUCTURE_ROAD); - y += FONT_HEIGHT_NORMAL; - } - if (c->infrastructure.water != 0) { - SetDParam(0, c->infrastructure.water); - DrawString(r.left, r.right, y, STR_COMPANY_VIEW_INFRASTRUCTURE_WATER); - y += FONT_HEIGHT_NORMAL; - } - if (c->infrastructure.station != 0) { - SetDParam(0, c->infrastructure.station); - DrawString(r.left, r.right, y, STR_COMPANY_VIEW_INFRASTRUCTURE_STATION); - y += FONT_HEIGHT_NORMAL; - } - if (c->infrastructure.airport != 0) { - SetDParam(0, c->infrastructure.airport); - DrawString(r.left, r.right, y, STR_COMPANY_VIEW_INFRASTRUCTURE_AIRPORT); - } - } + case WID_C_DESC_INFRASTRUCTURE_COUNTS: + DrawInfrastructureCountsWidget(r, c); break; - } case WID_C_DESC_OWNERS: { uint y = r.top; From 07fbd19da3161bda86e9e074d6330737b53e0896 Mon Sep 17 00:00:00 2001 From: translators Date: Sun, 26 Feb 2023 18:45:57 +0000 Subject: [PATCH 09/47] Update: Translations from eints luxembourgish: 8 changes by phreeze83 --- src/lang/luxembourgish.txt | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/src/lang/luxembourgish.txt b/src/lang/luxembourgish.txt index 0e5c6e56df..a80e438777 100644 --- a/src/lang/luxembourgish.txt +++ b/src/lang/luxembourgish.txt @@ -1591,7 +1591,7 @@ STR_CONFIG_SETTING_RIGHT_MOUSE_WND_CLOSE_HELPTEXT :Mécht eng Fens STR_CONFIG_SETTING_AUTOSAVE :Autospäicheren: {STRING} STR_CONFIG_SETTING_AUTOSAVE_HELPTEXT :Setz den Interval tëschend automateschen Späicherstänn -STR_CONFIG_SETTING_DATE_FORMAT_IN_SAVE_NAMES :Benotz {STRING} Datumsformat fir Späichernimm +STR_CONFIG_SETTING_DATE_FORMAT_IN_SAVE_NAMES :Datumsformat fir Späichernimm: {STRING} STR_CONFIG_SETTING_DATE_FORMAT_IN_SAVE_NAMES_HELPTEXT :Format vum Datum am Numm vum Späicherstand ###length 3 STR_CONFIG_SETTING_DATE_FORMAT_IN_SAVE_NAMES_LONG :laang (31ten Dez 2008) @@ -1886,7 +1886,7 @@ STR_CONFIG_SETTING_SOFT_LIMIT_VALUE :{COMMA} ###setting-zero-is-special STR_CONFIG_SETTING_SOFT_LIMIT_DISABLED :ausgeschalt -STR_CONFIG_SETTING_ZOOM_MIN :Maximalen Ranzoom Level: {STRING} +STR_CONFIG_SETTING_ZOOM_MIN :Maximale Razoom Level: {STRING} STR_CONFIG_SETTING_ZOOM_MIN_HELPTEXT :Maximal Razoomstuf fir Usiichtsfënsteren. Et gëtt méi Späicher gebraucht wann d'Stufen ze grouss ginn STR_CONFIG_SETTING_ZOOM_MAX :Maximalen Rauszoom Level: {STRING} STR_CONFIG_SETTING_ZOOM_MAX_HELPTEXT :Maximal Rauszoom-Stuf fir Usiichtsfënsteren. Méi grouss Rauszoom-Stufen kënnen Ruckeler verursaachen @@ -2046,7 +2046,7 @@ STR_CONFIG_ERROR_INVALID_GRF_SYSTEM :System NewGRF STR_CONFIG_ERROR_INVALID_GRF_INCOMPATIBLE :net mat dëser Versioun vun OpenTTD kompatibel STR_CONFIG_ERROR_INVALID_GRF_UNKNOWN :onbekannt STR_CONFIG_ERROR_INVALID_SAVEGAME_COMPRESSION_LEVEL :{WHITE}... Kompressiounslevel '{STRING}' ass net gültëg. -STR_CONFIG_ERROR_INVALID_SAVEGAME_COMPRESSION_ALGORITHM :{WHITE}... Spillstand format '{STRING}' gëtt et net. Revertéiren zu '{STRING}' +STR_CONFIG_ERROR_INVALID_SAVEGAME_COMPRESSION_ALGORITHM :{WHITE}... Späicherstand format '{STRING}' gëtt et net. Revertéiren zu '{STRING}' STR_CONFIG_ERROR_INVALID_BASE_GRAPHICS_NOT_FOUND :{WHITE}... ignoréiren Basis Grafik Set '{STRING}': net fonnt STR_CONFIG_ERROR_INVALID_BASE_SOUNDS_NOT_FOUND :{WHITE}... ignoréiren Basis Sound Set '{STRING}': net fonnt STR_CONFIG_ERROR_INVALID_BASE_MUSIC_NOT_FOUND :{WHITE}... ignoréiren Basis Musik Set '{STRING}': net fonnt @@ -2114,7 +2114,7 @@ STR_ABANDON_SCENARIO_QUERY :{YELLOW}Bass du # Cheat window STR_CHEATS :{WHITE}Cheater STR_CHEATS_TOOLTIP :{BLACK}Checkboxen weisen un dass du den Cheat schon eng Kéier benotzt hues -STR_CHEATS_NOTE :{BLACK}Notiz: jeglech Benotzung vun dësen Astellungen wäert am Spillstand opgeholl ginn +STR_CHEATS_NOTE :{BLACK}Notiz: jeglech Benotzung vun dësen Astellungen wäert am Späicherstand opgeholl ginn STR_CHEAT_MONEY :{LTBLUE}Suen ëm {CURRENCY_LONG} erhéijen STR_CHEAT_CHANGE_COMPANY :{LTBLUE}Als Firma {ORANGE}{COMMA} spillen STR_CHEAT_EXTRA_DYNAMITE :{LTBLUE}Magësche Bulldozer (Industrien ofrappen, onzerstéierbar Objeten): {ORANGE}{STRING} @@ -3352,6 +3352,8 @@ STR_NEWGRF_ERROR_MSG_INFO :{SILVER}{STRING STR_NEWGRF_ERROR_MSG_WARNING :{RED}Warnung: {SILVER}{STRING} STR_NEWGRF_ERROR_MSG_ERROR :{RED}Fehler: {SILVER}{STRING} STR_NEWGRF_ERROR_MSG_FATAL :{RED}Fatal: {SILVER}{STRING} +STR_NEWGRF_ERROR_FATAL_POPUP :{WHITE}Den NewGRF "{STRING}" huet e fatale Fehler gemet:{}{STRING} +STR_NEWGRF_ERROR_POPUP :{WHITE}Den NewGRF "{STRING}" huet e Fehler gemet:{}{STRING} STR_NEWGRF_ERROR_VERSION_NUMBER :{1:STRING} wärt net mat der TTDPatch Versioun déi vun OpenTTD erkannt gouf funktionéiren STR_NEWGRF_ERROR_DOS_OR_WINDOWS :{1:STRING} ass fir {2:STRING} Versioun vun TTD STR_NEWGRF_ERROR_UNSET_SWITCH :{1:STRING} ass designed fir mat {2:STRING} benotzt ze ginn @@ -4527,7 +4529,7 @@ STR_TIMETABLE_CLEAR_SPEED :{BLACK}Geschwin STR_TIMETABLE_CLEAR_SPEED_TOOLTIP :{BLACK}Maximal Reesgeschwindegkeet vum gewielten Optrag läschen. Ctrl+Klick läscht d'Geschwindegkeet fir all Opträg STR_TIMETABLE_RESET_LATENESS :{BLACK}Verspeidungszieler zerécksetzen -STR_TIMETABLE_RESET_LATENESS_TOOLTIP :{BLACK}Setzt de Verspéidungszieler zréck, sou dass d'Gefier mat Zäit ukënnt +STR_TIMETABLE_RESET_LATENESS_TOOLTIP :{BLACK}Setzt de Verspéidungszieler zréck, sou dass d'Gefier mat Zäit ukënnt. Ctrl+klick setzt déi ganz Grupp zeréck, sou das dat lescht Gefiert pünktlech ass an all déi aner méi fréi. STR_TIMETABLE_AUTOFILL :{BLACK}Autofëllen STR_TIMETABLE_AUTOFILL_TOOLTIP :{BLACK}Fëll den Zäitplang automatesch mat de Wäerter vum nächsten Trajet Ctrl+Klick fir Wardzäiten probéiren bäizehalen @@ -4675,7 +4677,7 @@ STR_ERROR_GAME_SAVE_FAILED :{WHITE}Fehler b STR_ERROR_UNABLE_TO_DELETE_FILE :{WHITE}Kann d'Datei net läschen STR_ERROR_GAME_LOAD_FAILED :{WHITE}Fehler beim Lueden{}{STRING} STR_GAME_SAVELOAD_ERROR_BROKEN_INTERNAL_ERROR :Interne Fehler: {STRING} -STR_GAME_SAVELOAD_ERROR_BROKEN_SAVEGAME :Futtissen Spillstand - {STRING} +STR_GAME_SAVELOAD_ERROR_BROKEN_SAVEGAME :Futtissen Späicherstand - {STRING} STR_GAME_SAVELOAD_ERROR_TOO_NEW_SAVEGAME :Spillstand ass mat enger méi neier Versioun gemaach STR_GAME_SAVELOAD_ERROR_FILE_NOT_READABLE :Datei net liesbar STR_GAME_SAVELOAD_ERROR_FILE_NOT_WRITEABLE :Datei net beschreiwbar From a18182e24b1c35b73c12e7d7d9726b29c4db1c01 Mon Sep 17 00:00:00 2001 From: Tyler Trahan Date: Sun, 26 Feb 2023 14:23:31 -0500 Subject: [PATCH 10/47] Revert 92c7551: Line drawing algorithm fix broke other cases (#10497) This reverts commit 92c755161d6a467d33477bd0041fc720d7d9ba41. --- src/blitter/common.hpp | 22 ++++++++++------------ 1 file changed, 10 insertions(+), 12 deletions(-) diff --git a/src/blitter/common.hpp b/src/blitter/common.hpp index 1ecd09a01e..cd3b6276a4 100644 --- a/src/blitter/common.hpp +++ b/src/blitter/common.hpp @@ -109,6 +109,11 @@ void Blitter::DrawLineGeneric(int x1, int y1, int x2, int y2, int screen_width, } while (x1 != x2) { + if (dash_count < dash) { + for (int y = y_low; y != y_high; y += stepy) { + if (y >= 0 && y < screen_height) set_pixel(x1, y); + } + } if (frac_low >= 0) { y_low += stepy; frac_low -= dx; @@ -117,12 +122,6 @@ void Blitter::DrawLineGeneric(int x1, int y1, int x2, int y2, int screen_width, y_high += stepy; frac_high -= dx; } - if (dash_count < dash) { - for (int y = y_low; y != y_high; y += stepy) { - if (y >= 0 && y < screen_height) set_pixel(x1, y); - } - } - x1++; frac_low += dy; frac_high += dy; @@ -172,6 +171,11 @@ void Blitter::DrawLineGeneric(int x1, int y1, int x2, int y2, int screen_width, } while (y1 != y2) { + if (dash_count < dash) { + for (int x = x_low; x != x_high; x += stepx) { + if (x >= 0 && x < screen_width) set_pixel(x, y1); + } + } if (frac_low >= 0) { x_low += stepx; frac_low -= dy; @@ -180,12 +184,6 @@ void Blitter::DrawLineGeneric(int x1, int y1, int x2, int y2, int screen_width, x_high += stepx; frac_high -= dy; } - if (dash_count < dash) { - for (int x = x_low; x != x_high; x += stepx) { - if (x >= 0 && x < screen_width) set_pixel(x, y1); - } - } - y1++; frac_low += dx; frac_high += dx; From 4c1406a4b5421632df2b5faadd56d5d965c108a8 Mon Sep 17 00:00:00 2001 From: Jonathan G Rennison Date: Sun, 6 Nov 2022 15:01:27 +0000 Subject: [PATCH 11/47] Add: NewGRF road stops --- docs/landscape.html | 1 + docs/landscape_grid.html | 2 +- src/CMakeLists.txt | 2 + src/base_station_base.h | 46 +- src/cheat_gui.cpp | 2 + src/date.cpp | 2 + src/economy.cpp | 6 + src/newgrf.cpp | 209 ++++++- src/newgrf.h | 2 + src/newgrf_airporttiles.cpp | 2 +- src/newgrf_animation_base.h | 25 +- src/newgrf_callbacks.h | 9 + src/newgrf_debug_gui.cpp | 2 + src/newgrf_house.cpp | 2 +- src/newgrf_industrytiles.cpp | 2 +- src/newgrf_object.cpp | 2 +- src/newgrf_roadstop.cpp | 614 +++++++++++++++++++++ src/newgrf_roadstop.h | 189 +++++++ src/newgrf_station.cpp | 2 +- src/road_cmd.h | 4 +- src/road_gui.cpp | 688 +++++++++++++++++++++--- src/saveload/compat/station_sl_compat.h | 6 + src/saveload/saveload.h | 1 + src/saveload/station_sl.cpp | 65 +++ src/script/api/script_road.cpp | 3 +- src/station.cpp | 30 ++ src/station_base.h | 5 + src/station_cmd.cpp | 128 ++++- src/station_cmd.h | 3 +- src/station_map.h | 36 ++ src/table/newgrf_debug_data.h | 60 +++ src/vehicle.cpp | 9 + src/widgets/road_widget.h | 37 +- 33 files changed, 2075 insertions(+), 121 deletions(-) create mode 100644 src/newgrf_roadstop.cpp create mode 100644 src/newgrf_roadstop.h diff --git a/docs/landscape.html b/docs/landscape.html index ab4ad989e3..0f9a18bbf5 100644 --- a/docs/landscape.html +++ b/docs/landscape.html @@ -998,6 +998,7 @@
  • m7: animation frame (railway stations/waypoints, airports)
  • m8 bits 11..6: Tramtype
  • m8 bits 5..0: track type for railway stations/waypoints
  • +
  • m8 bits 5..0: custom road stop id; 0 means standard graphics
  • diff --git a/docs/landscape_grid.html b/docs/landscape_grid.html index fc0c6e0cfc..e7e55aabf3 100644 --- a/docs/landscape_grid.html +++ b/docs/landscape_grid.html @@ -203,7 +203,7 @@ the array so you can quickly see what is used and what is not. OOOO OXXX OOXX XOOO OOOX XXXX - OOOO XXXX XXOO OOOO + OOOO XXXX XX XXXXXX airport diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index b27a4ca080..5b49deba25 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -274,6 +274,8 @@ add_files( newgrf_properties.h newgrf_railtype.cpp newgrf_railtype.h + newgrf_roadstop.cpp + newgrf_roadstop.h newgrf_roadtype.cpp newgrf_roadtype.h newgrf_sound.cpp diff --git a/src/base_station_base.h b/src/base_station_base.h index 2ad09ca21c..8550d3ec49 100644 --- a/src/base_station_base.h +++ b/src/base_station_base.h @@ -24,6 +24,17 @@ struct StationSpecList { uint8 localidx; ///< Station ID within GRF of station }; +struct RoadStopSpecList { + const RoadStopSpec *spec; + uint32 grfid; ///< GRF ID of this custom road stop + uint8 localidx; ///< Station ID within GRF of road stop +}; + +struct RoadStopTileData { + TileIndex tile; + uint8 random_bits; + uint8 animation_frame; +}; /** StationRect - used to track station spread out rectangle - cheaper than scanning whole map */ struct StationRect : public Rect { @@ -62,18 +73,23 @@ struct BaseStation : StationPool::PoolItem<&_station_pool> { Owner owner; ///< The owner of this station StationFacility facilities; ///< The facilities that this station has - std::vector speclist; ///< List of rail station specs of this station. + std::vector speclist; ///< List of rail station specs of this station. + std::vector roadstop_speclist; ///< List of road stop specs of this station Date build_date; ///< Date of construction uint16 random_bits; ///< Random bits assigned to this station byte waiting_triggers; ///< Waiting triggers (NewGRF) for this station - uint8 cached_anim_triggers; ///< NOSAVE: Combined animation trigger bitmask, used to determine if trigger processing should happen. - CargoTypes cached_cargo_triggers; ///< NOSAVE: Combined cargo trigger bitmask + uint8 cached_anim_triggers; ///< NOSAVE: Combined animation trigger bitmask, used to determine if trigger processing should happen. + uint8 cached_roadstop_anim_triggers; ///< NOSAVE: Combined animation trigger bitmask for road stops, used to determine if trigger processing should happen. + CargoTypes cached_cargo_triggers; ///< NOSAVE: Combined cargo trigger bitmask + CargoTypes cached_roadstop_cargo_triggers; ///< NOSAVE: Combined cargo trigger bitmask for road stops TileArea train_station; ///< Tile area the train 'station' part covers StationRect rect; ///< NOSAVE: Station spread out rectangle maintained by StationRect::xxx() functions + std::vector custom_roadstop_tile_data; ///< List of custom road stop tile data + /** * Initialize the base station. * @param tile The location of the station sign @@ -167,6 +183,30 @@ struct BaseStation : StationPool::PoolItem<&_station_pool> { return (this->facilities & ~FACIL_WAYPOINT) != 0; } + inline byte GetRoadStopRandomBits(TileIndex tile) const + { + for (const RoadStopTileData &tile_data : this->custom_roadstop_tile_data) { + if (tile_data.tile == tile) return tile_data.random_bits; + } + return 0; + } + + inline byte GetRoadStopAnimationFrame(TileIndex tile) const + { + for (const RoadStopTileData &tile_data : this->custom_roadstop_tile_data) { + if (tile_data.tile == tile) return tile_data.animation_frame; + } + return 0; + } + +private: + void SetRoadStopTileData(TileIndex tile, byte data, bool animation); + +public: + inline void SetRoadStopRandomBits(TileIndex tile, byte random_bits) { this->SetRoadStopTileData(tile, random_bits, false); } + inline void SetRoadStopAnimationFrame(TileIndex tile, byte frame) { this->SetRoadStopTileData(tile, frame, true); } + void RemoveRoadStopTileData(TileIndex tile); + static void PostDestructor(size_t index); private: diff --git a/src/cheat_gui.cpp b/src/cheat_gui.cpp index ba70bdb8e1..34418c9840 100644 --- a/src/cheat_gui.cpp +++ b/src/cheat_gui.cpp @@ -115,6 +115,8 @@ static int32 ClickChangeDateCheat(int32 p1, int32 p2) EnginesMonthlyLoop(); SetWindowDirty(WC_STATUS_BAR, 0); InvalidateWindowClassesData(WC_BUILD_STATION, 0); + InvalidateWindowClassesData(WC_BUS_STATION, 0); + InvalidateWindowClassesData(WC_TRUCK_STATION, 0); InvalidateWindowClassesData(WC_BUILD_OBJECT, 0); ResetSignalVariant(); return _cur_year; diff --git a/src/date.cpp b/src/date.cpp index 66ccc1444c..198dd89585 100644 --- a/src/date.cpp +++ b/src/date.cpp @@ -195,6 +195,8 @@ static void OnNewYear() VehiclesYearlyLoop(); TownsYearlyLoop(); InvalidateWindowClassesData(WC_BUILD_STATION); + InvalidateWindowClassesData(WC_BUS_STATION); + InvalidateWindowClassesData(WC_TRUCK_STATION); if (_network_server) NetworkServerYearlyLoop(); if (_cur_year == _settings_client.gui.semaphore_build_before) ResetSignalVariant(); diff --git a/src/economy.cpp b/src/economy.cpp index ab23288c5f..6d22e69ff3 100644 --- a/src/economy.cpp +++ b/src/economy.cpp @@ -26,6 +26,7 @@ #include "newgrf_industrytiles.h" #include "newgrf_station.h" #include "newgrf_airporttiles.h" +#include "newgrf_roadstop.h" #include "object.h" #include "strings_func.h" #include "date_func.h" @@ -1813,6 +1814,8 @@ static void LoadUnloadVehicle(Vehicle *front) TriggerStationRandomisation(st, st->xy, SRT_CARGO_TAKEN, v->cargo_type); TriggerStationAnimation(st, st->xy, SAT_CARGO_TAKEN, v->cargo_type); AirportAnimationTrigger(st, AAT_STATION_CARGO_TAKEN, v->cargo_type); + TriggerRoadStopRandomisation(st, st->xy, RSRT_CARGO_TAKEN, v->cargo_type); + TriggerRoadStopAnimation(st, st->xy, SAT_CARGO_TAKEN, v->cargo_type); } new_load_unload_ticks += loaded; @@ -1833,6 +1836,9 @@ static void LoadUnloadVehicle(Vehicle *front) if (front->type == VEH_TRAIN) { TriggerStationRandomisation(st, front->tile, SRT_TRAIN_LOADS); TriggerStationAnimation(st, front->tile, SAT_TRAIN_LOADS); + } else if (front->type == VEH_ROAD) { + TriggerRoadStopRandomisation(st, front->tile, RSRT_VEH_LOADS); + TriggerRoadStopAnimation(st, front->tile, SAT_TRAIN_LOADS); } } diff --git a/src/newgrf.cpp b/src/newgrf.cpp index 84c8cab467..611ce367c2 100644 --- a/src/newgrf.cpp +++ b/src/newgrf.cpp @@ -48,6 +48,7 @@ #include "language.h" #include "vehicle_base.h" #include "road.h" +#include "newgrf_roadstop.h" #include "table/strings.h" #include "table/build_industry.h" @@ -4747,6 +4748,131 @@ static ChangeInfoResult AirportTilesChangeInfo(uint airtid, int numinfo, int pro return ret; } +/** + * Ignore properties for roadstops + * @param prop The property to ignore. + * @param buf The property value. + * @return ChangeInfoResult. + */ +static ChangeInfoResult IgnoreRoadStopProperty(uint prop, ByteReader *buf) +{ + ChangeInfoResult ret = CIR_SUCCESS; + + switch (prop) { + case 0x09: + case 0x0C: + buf->ReadByte(); + break; + + case 0x0A: + case 0x0B: + buf->ReadWord(); + break; + + case 0x08: + case 0x0D: + buf->ReadDWord(); + break; + + default: + ret = CIR_UNKNOWN; + break; + } + + return ret; +} + +static ChangeInfoResult RoadStopChangeInfo(uint id, int numinfo, int prop, ByteReader *buf) +{ + ChangeInfoResult ret = CIR_SUCCESS; + + if (id + numinfo > 255) { + grfmsg(1, "RoadStopChangeInfo: RoadStop %u is invalid, max %u, ignoring", id + numinfo, 255); + return CIR_INVALID_ID; + } + + if (_cur.grffile->roadstops == nullptr) _cur.grffile->roadstops = CallocT(255); + + for (int i = 0; i < numinfo; i++) { + RoadStopSpec *rs = _cur.grffile->roadstops[id + i]; + + if (rs == nullptr && prop != 0x08) { + grfmsg(1, "RoadStopChangeInfo: Attempt to modify undefined road stop %u, ignoring", id + i); + ChangeInfoResult cir = IgnoreRoadStopProperty(prop, buf); + if (cir > ret) ret = cir; + continue; + } + + switch (prop) { + case 0x08: { // Road Stop Class ID + RoadStopSpec **spec = &_cur.grffile->roadstops[id + i]; + + if (*spec == nullptr) { + *spec = CallocT(1); + new (*spec) RoadStopSpec(); + } + + uint32 classid = buf->ReadDWord(); + (*spec)->cls_id = RoadStopClass::Allocate(BSWAP32(classid)); + (*spec)->spec_id = id + i; + break; + } + + case 0x09: // Road stop type + rs->stop_type = (RoadStopAvailabilityType)buf->ReadByte(); + break; + + case 0x0A: // Road Stop Name + AddStringForMapping(buf->ReadWord(), &rs->name); + break; + + case 0x0B: // Road Stop Class name + AddStringForMapping(buf->ReadWord(), &RoadStopClass::Get(rs->cls_id)->name); + break; + + case 0x0C: // The draw mode + rs->draw_mode = (RoadStopDrawMode)buf->ReadByte(); + break; + + case 0x0D: // Cargo types for random triggers + rs->cargo_triggers = TranslateRefitMask(buf->ReadDWord()); + break; + + case 0x0E: // Animation info + rs->animation.frames = buf->ReadByte(); + rs->animation.status = buf->ReadByte(); + break; + + case 0x0F: // Animation speed + rs->animation.speed = buf->ReadByte(); + break; + + case 0x10: // Animation triggers + rs->animation.triggers = buf->ReadWord(); + break; + + case 0x11: // Callback mask + rs->callback_mask = buf->ReadByte(); + break; + + case 0x12: // General flags + rs->flags = (uint8)buf->ReadDWord(); // Future-proofing, size this as 4 bytes, but we only need one byte's worth of flags at present + break; + + case 0x15: // Cost multipliers + rs->build_cost_multiplier = buf->ReadByte(); + rs->clear_cost_multiplier = buf->ReadByte(); + break; + + default: + ret = CIR_UNKNOWN; + break; + } + } + + return ret; +} + static bool HandleChangeInfoResult(const char *caller, ChangeInfoResult cir, uint8 feature, uint8 property) { switch (cir) { @@ -4811,6 +4937,7 @@ static void FeatureChangeInfo(ByteReader *buf) /* GSF_AIRPORTTILES */ AirportTilesChangeInfo, /* GSF_ROADTYPES */ RoadTypeChangeInfo, /* GSF_TRAMTYPES */ TramTypeChangeInfo, + /* GSF_ROADSTOPS */ RoadStopChangeInfo, }; static_assert(GSF_END == lengthof(handler)); @@ -5289,7 +5416,8 @@ static void NewSpriteGroup(ByteReader *buf) case GSF_HOUSES: case GSF_AIRPORTTILES: case GSF_OBJECTS: - case GSF_INDUSTRYTILES: { + case GSF_INDUSTRYTILES: + case GSF_ROADSTOPS: { byte num_building_sprites = std::max((uint8)1, type); assert(TileLayoutSpriteGroup::CanAllocateItem()); @@ -5404,7 +5532,7 @@ static CargoID TranslateCargo(uint8 feature, uint8 ctype) } } /* Special cargo types for purchase list and stations */ - if (feature == GSF_STATIONS && ctype == 0xFE) return CT_DEFAULT_NA; + if ((feature == GSF_STATIONS || feature == GSF_ROADSTOPS) && ctype == 0xFE) return CT_DEFAULT_NA; if (ctype == 0xFF) return CT_PURCHASE; if (_cur.grffile->cargo_list.size() == 0) { @@ -5923,6 +6051,61 @@ static void AirportTileMapSpriteGroup(ByteReader *buf, uint8 idcount) } } +static void RoadStopMapSpriteGroup(ByteReader *buf, uint8 idcount) +{ + uint8 *roadstops = AllocaM(uint8, idcount); + for (uint i = 0; i < idcount; i++) { + roadstops[i] = buf->ReadByte(); + } + + uint8 cidcount = buf->ReadByte(); + for (uint c = 0; c < cidcount; c++) { + uint8 ctype = buf->ReadByte(); + uint16 groupid = buf->ReadWord(); + if (!IsValidGroupID(groupid, "RoadStopMapSpriteGroup")) continue; + + ctype = TranslateCargo(GSF_ROADSTOPS, ctype); + if (ctype == CT_INVALID) continue; + + for (uint i = 0; i < idcount; i++) { + RoadStopSpec *roadstopspec = _cur.grffile->roadstops == nullptr ? nullptr : _cur.grffile->roadstops[roadstops[i]]; + + if (roadstopspec == nullptr) { + grfmsg(1, "RoadStopMapSpriteGroup: Road stop with ID 0x%02X does not exist, skipping", roadstops[i]); + continue; + } + + roadstopspec->grf_prop.spritegroup[ctype] = _cur.spritegroups[groupid]; + } + } + + uint16 groupid = buf->ReadWord(); + if (!IsValidGroupID(groupid, "RoadStopMapSpriteGroup")) return; + + if (_cur.grffile->roadstops == nullptr) { + grfmsg(0, "RoadStopMapSpriteGroup: No roadstops defined, skipping."); + return; + } + + for (uint i = 0; i < idcount; i++) { + RoadStopSpec *roadstopspec = _cur.grffile->roadstops == nullptr ? nullptr : _cur.grffile->roadstops[roadstops[i]]; + + if (roadstopspec == nullptr) { + grfmsg(1, "RoadStopMapSpriteGroup: Road stop with ID 0x%02X does not exist, skipping.", roadstops[i]); + continue; + } + + if (roadstopspec->grf_prop.grffile != nullptr) { + grfmsg(1, "RoadStopMapSpriteGroup: Road stop with ID 0x%02X mapped multiple times, skipping", roadstops[i]); + continue; + } + + roadstopspec->grf_prop.spritegroup[CT_DEFAULT] = _cur.spritegroups[groupid]; + roadstopspec->grf_prop.grffile = _cur.grffile; + roadstopspec->grf_prop.local_id = roadstops[i]; + RoadStopClass::Assign(roadstopspec); + } +} /* Action 0x03 */ static void FeatureMapSpriteGroup(ByteReader *buf) @@ -6023,6 +6206,10 @@ static void FeatureMapSpriteGroup(ByteReader *buf) AirportTileMapSpriteGroup(buf, idcount); return; + case GSF_ROADSTOPS: + RoadStopMapSpriteGroup(buf, idcount); + return; + default: grfmsg(1, "FeatureMapSpriteGroup: Unsupported feature 0x%02X, skipping", feature); return; @@ -8601,6 +8788,20 @@ static void ResetCustomObjects() } } +static void ResetCustomRoadStops() +{ + for (auto file : _grf_files) { + RoadStopSpec **&roadstopspec = file->roadstops; + if (roadstopspec == nullptr) continue; + for (uint i = 0; i < NUM_ROADSTOPS_PER_GRF; i++) { + free(roadstopspec[i]); + } + + free(roadstopspec); + roadstopspec = nullptr; + } +} + /** Reset and clear all NewGRFs */ static void ResetNewGRF() { @@ -8687,6 +8888,10 @@ void ResetNewGRFData() AirportSpec::ResetAirports(); AirportTileSpec::ResetAirportTiles(); + /* Reset road stop classes */ + RoadStopClass::Reset(); + ResetCustomRoadStops(); + /* Reset canal sprite groups and flags */ memset(_water_feature, 0, sizeof(_water_feature)); diff --git a/src/newgrf.h b/src/newgrf.h index 9e6a4f1c25..7561fb145b 100644 --- a/src/newgrf.h +++ b/src/newgrf.h @@ -84,6 +84,7 @@ enum GrfSpecFeature { GSF_AIRPORTTILES, GSF_ROADTYPES, GSF_TRAMTYPES, + GSF_ROADSTOPS, GSF_END, GSF_FAKE_TOWNS = GSF_END, ///< Fake town GrfSpecFeature for NewGRF debugging (parent scope) @@ -118,6 +119,7 @@ struct GRFFile : ZeroedMemoryAllocator { struct ObjectSpec **objectspec; struct AirportSpec **airportspec; struct AirportTileSpec **airtspec; + struct RoadStopSpec **roadstops; uint32 param[0x80]; uint param_end; ///< one more than the highest set parameter diff --git a/src/newgrf_airporttiles.cpp b/src/newgrf_airporttiles.cpp index b34992fcf0..ebdf226dd0 100644 --- a/src/newgrf_airporttiles.cpp +++ b/src/newgrf_airporttiles.cpp @@ -278,7 +278,7 @@ bool DrawNewAirportTile(TileInfo *ti, Station *st, StationGfx gfx, const Airport } /** Helper class for animation control. */ -struct AirportTileAnimationBase : public AnimationBase { +struct AirportTileAnimationBase : public AnimationBase > { static const CallbackID cb_animation_speed = CBID_AIRPTILE_ANIMATION_SPEED; static const CallbackID cb_animation_next_frame = CBID_AIRPTILE_ANIM_NEXT_FRAME; diff --git a/src/newgrf_animation_base.h b/src/newgrf_animation_base.h index faffe95e4f..8df0228408 100644 --- a/src/newgrf_animation_base.h +++ b/src/newgrf_animation_base.h @@ -17,15 +17,22 @@ #include "newgrf_callbacks.h" #include "tile_map.h" +template +struct TileAnimationFrameAnimationHelper { + static byte Get(Tobj *obj, TileIndex tile) { return GetAnimationFrame(tile); } + static void Set(Tobj *obj, TileIndex tile, byte frame) { SetAnimationFrame(tile, frame); } +}; + /** * Helper class for a unified approach to NewGRF animation. - * @tparam Tbase Instantiation of this class. - * @tparam Tspec NewGRF specification related to the animated tile. - * @tparam Tobj Object related to the animated tile. - * @tparam Textra Custom extra callback data. - * @tparam GetCallback The callback function pointer. + * @tparam Tbase Instantiation of this class. + * @tparam Tspec NewGRF specification related to the animated tile. + * @tparam Tobj Object related to the animated tile. + * @tparam Textra Custom extra callback data. + * @tparam GetCallback The callback function pointer. + * @tparam Tframehelper The animation frame get/set helper. */ -template +template struct AnimationBase { /** * Animate a single tile. @@ -55,7 +62,7 @@ struct AnimationBase { * maximum, corresponding to around 33 minutes. */ if (_tick_counter % (1ULL << animation_speed) != 0) return; - uint8 frame = GetAnimationFrame(tile); + uint8 frame = Tframehelper::Get(obj, tile); uint8 num_frames = spec->animation.frames; bool frame_set_by_callback = false; @@ -98,7 +105,7 @@ struct AnimationBase { } } - SetAnimationFrame(tile, frame); + Tframehelper::Set(obj, tile, frame); MarkTileDirtyByTile(tile); } @@ -124,7 +131,7 @@ struct AnimationBase { case 0xFE: AddAnimatedTile(tile); break; case 0xFF: DeleteAnimatedTile(tile); break; default: - SetAnimationFrame(tile, callback); + Tframehelper::Set(obj, tile, callback); AddAnimatedTile(tile); break; } diff --git a/src/newgrf_callbacks.h b/src/newgrf_callbacks.h index ea20105cb8..482031cec0 100644 --- a/src/newgrf_callbacks.h +++ b/src/newgrf_callbacks.h @@ -311,6 +311,15 @@ enum StationCallbackMask { CBM_STATION_SLOPE_CHECK = 4, ///< Check slope of new station tiles }; +/** + * Callback masks for road stops. + */ +enum RoadStopCallbackMask { + CBM_ROAD_STOP_AVAIL = 0, ///< Availability of road stop in construction window + CBM_ROAD_STOP_ANIMATION_NEXT_FRAME = 1, ///< Use a custom next frame callback + CBM_ROAD_STOP_ANIMATION_SPEED = 2, ///< Customize the animation speed of the road stop +}; + /** * Callback masks for houses. */ diff --git a/src/newgrf_debug_gui.cpp b/src/newgrf_debug_gui.cpp index 7c619e78c8..764fd995cf 100644 --- a/src/newgrf_debug_gui.cpp +++ b/src/newgrf_debug_gui.cpp @@ -776,6 +776,8 @@ GrfSpecFeature GetGrfSpecFeature(TileIndex tile) switch (GetStationType(tile)) { case STATION_RAIL: return GSF_STATIONS; case STATION_AIRPORT: return GSF_AIRPORTTILES; + case STATION_BUS: return GSF_ROADSTOPS; + case STATION_TRUCK: return GSF_ROADSTOPS; default: return GSF_INVALID; } } diff --git a/src/newgrf_house.cpp b/src/newgrf_house.cpp index 21a4413fa2..53ff4b8f72 100644 --- a/src/newgrf_house.cpp +++ b/src/newgrf_house.cpp @@ -480,7 +480,7 @@ uint16 GetSimpleHouseCallback(CallbackID callback, uint32 param1, uint32 param2, } /** Helper class for animation control. */ -struct HouseAnimationBase : public AnimationBase { +struct HouseAnimationBase : public AnimationBase > { static const CallbackID cb_animation_speed = CBID_HOUSE_ANIMATION_SPEED; static const CallbackID cb_animation_next_frame = CBID_HOUSE_ANIMATION_NEXT_FRAME; diff --git a/src/newgrf_industrytiles.cpp b/src/newgrf_industrytiles.cpp index 6d8601192f..72241fb120 100644 --- a/src/newgrf_industrytiles.cpp +++ b/src/newgrf_industrytiles.cpp @@ -257,7 +257,7 @@ uint16 GetSimpleIndustryCallback(CallbackID callback, uint32 param1, uint32 para } /** Helper class for animation control. */ -struct IndustryAnimationBase : public AnimationBase { +struct IndustryAnimationBase : public AnimationBase > { static const CallbackID cb_animation_speed = CBID_INDTILE_ANIMATION_SPEED; static const CallbackID cb_animation_next_frame = CBID_INDTILE_ANIM_NEXT_FRAME; diff --git a/src/newgrf_object.cpp b/src/newgrf_object.cpp index 730fa7afd0..ccd46e2e92 100644 --- a/src/newgrf_object.cpp +++ b/src/newgrf_object.cpp @@ -510,7 +510,7 @@ uint16 StubGetObjectCallback(CallbackID callback, uint32 param1, uint32 param2, } /** Helper class for animation control. */ -struct ObjectAnimationBase : public AnimationBase { +struct ObjectAnimationBase : public AnimationBase > { static const CallbackID cb_animation_speed = CBID_OBJECT_ANIMATION_SPEED; static const CallbackID cb_animation_next_frame = CBID_OBJECT_ANIMATION_NEXT_FRAME; diff --git a/src/newgrf_roadstop.cpp b/src/newgrf_roadstop.cpp new file mode 100644 index 0000000000..76b901d8f7 --- /dev/null +++ b/src/newgrf_roadstop.cpp @@ -0,0 +1,614 @@ +/* + * This file is part of OpenTTD. + * OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2. + * OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see . + */ + +/** @file command.cpp Handling of NewGRF road stops. */ + +#include "stdafx.h" +#include "debug.h" +#include "station_base.h" +#include "roadstop_base.h" +#include "newgrf_roadstop.h" +#include "newgrf_class_func.h" +#include "newgrf_cargo.h" +#include "newgrf_roadtype.h" +#include "gfx_type.h" +#include "company_func.h" +#include "road.h" +#include "window_type.h" +#include "date_func.h" +#include "town.h" +#include "viewport_func.h" +#include "newgrf_animation_base.h" +#include "newgrf_sound.h" + +#include "safeguards.h" + +template +void NewGRFClass::InsertDefaults() +{ + /* Set up initial data */ + classes[0].global_id = 'DFLT'; + classes[0].name = STR_STATION_CLASS_DFLT; + classes[0].Insert(nullptr); + + classes[1].global_id = 'WAYP'; + classes[1].name = STR_STATION_CLASS_WAYP; + classes[1].Insert(nullptr); +} + +template +bool NewGRFClass::IsUIAvailable(uint index) const +{ + return true; +} + +INSTANTIATE_NEWGRF_CLASS_METHODS(RoadStopClass, RoadStopSpec, RoadStopClassID, ROADSTOP_CLASS_MAX) + +static const uint NUM_ROADSTOPSPECS_PER_STATION = 63; ///< Maximum number of parts per station. + +uint32 RoadStopScopeResolver::GetRandomBits() const +{ + if (this->st == nullptr) return 0; + + uint32 bits = this->st->random_bits; + if (this->tile != INVALID_TILE && Station::IsExpected(this->st)) { + bits |= Station::From(this->st)->GetRoadStopRandomBits(this->tile) << 16; + } + return bits; +} + +uint32 RoadStopScopeResolver::GetTriggers() const +{ + return this->st == nullptr ? 0 : this->st->waiting_triggers; +} + +uint32 RoadStopScopeResolver::GetVariable(byte variable, uint32 parameter, bool *available) const +{ + auto get_road_type_variable = [&](RoadTramType rtt) -> uint32 { + RoadType rt; + if (this->tile == INVALID_TILE) { + rt = (GetRoadTramType(this->roadtype) == rtt) ? this->roadtype : INVALID_ROADTYPE; + } else { + rt = GetRoadType(this->tile, rtt); + } + if (rt == INVALID_ROADTYPE) { + return 0xFFFFFFFF; + } else { + return GetReverseRoadTypeTranslation(rt, this->roadstopspec->grf_prop.grffile); + } + }; + + switch (variable) { + /* View/rotation */ + case 0x40: return this->view; + + /* Stop type: 0: bus, 1: truck, 2: waypoint */ + case 0x41: + if (this->type == STATION_BUS) return 0; + if (this->type == STATION_TRUCK) return 1; + return 2; + + /* Terrain type */ + case 0x42: return this->tile == INVALID_TILE ? 0 : GetTerrainType(this->tile, TCX_NORMAL); // terrain_type + + /* Road type */ + case 0x43: return get_road_type_variable(RTT_ROAD); + + /* Tram type */ + case 0x44: return get_road_type_variable(RTT_TRAM); + + /* Town zone and Manhattan distance of closest town */ + case 0x45: { + if (this->tile == INVALID_TILE) return HZB_TOWN_EDGE << 16; + const Town *t = (this->st == nullptr) ? ClosestTownFromTile(this->tile, UINT_MAX) : this->st->town; + return t != nullptr ? (GetTownRadiusGroup(t, this->tile) << 16 | std::min(DistanceManhattan(this->tile, t->xy), 0xFFFFu)) : HZB_TOWN_EDGE << 16; + } + + /* Get square of Euclidian distance of closest town */ + case 0x46: { + if (this->tile == INVALID_TILE) return 0; + const Town *t = (this->st == nullptr) ? ClosestTownFromTile(this->tile, UINT_MAX) : this->st->town; + return t != nullptr ? DistanceSquare(this->tile, t->xy) : 0; + } + + /* Company information */ + case 0x47: return GetCompanyInfo(this->st == nullptr ? _current_company : this->st->owner); + + /* Animation frame */ + case 0x49: return this->tile == INVALID_TILE ? 0 : this->st->GetRoadStopAnimationFrame(this->tile); + + /* Variables which use the parameter */ + /* Variables 0x60 to 0x65 and 0x69 are handled separately below */ + + /* Animation frame of nearby tile */ + case 0x66: { + if (this->tile == INVALID_TILE) return UINT_MAX; + TileIndex tile = this->tile; + if (parameter != 0) tile = GetNearbyTile(parameter, tile); + return (IsRoadStopTile(tile) && GetStationIndex(tile) == this->st->index) ? this->st->GetRoadStopAnimationFrame(tile) : UINT_MAX; + } + + /* Land info of nearby tile */ + case 0x67: { + if (this->tile == INVALID_TILE) return 0; + TileIndex tile = this->tile; + if (parameter != 0) tile = GetNearbyTile(parameter, tile); // only perform if it is required + return GetNearbyTileInformation(tile, this->ro.grffile->grf_version >= 8); + } + + /* Road stop info of nearby tiles */ + case 0x68: { + if (this->tile == INVALID_TILE) return 0xFFFFFFFF; + TileIndex nearby_tile = GetNearbyTile(parameter, this->tile); + + if (!IsRoadStopTile(nearby_tile)) return 0xFFFFFFFF; + + uint32 grfid = this->st->roadstop_speclist[GetCustomRoadStopSpecIndex(this->tile)].grfid; + bool same_orientation = GetStationGfx(this->tile) == GetStationGfx(nearby_tile); + bool same_station = GetStationIndex(nearby_tile) == this->st->index; + uint32 res = GetStationGfx(nearby_tile) << 12 | !same_orientation << 11 | !!same_station << 10; + StationType type = GetStationType(nearby_tile); + if (type == STATION_TRUCK) res |= (1 << 16); + if (type == this->type) SetBit(res, 20); + + if (IsCustomRoadStopSpecIndex(nearby_tile)) { + const RoadStopSpecList ssl = BaseStation::GetByTile(nearby_tile)->roadstop_speclist[GetCustomRoadStopSpecIndex(nearby_tile)]; + res |= 1 << (ssl.grfid != grfid ? 9 : 8) | ssl.localidx; + } + return res; + } + + /* GRFID of nearby road stop tiles */ + case 0x6A: { + if (this->tile == INVALID_TILE) return 0xFFFFFFFF; + TileIndex nearby_tile = GetNearbyTile(parameter, this->tile); + + if (!IsRoadStopTile(nearby_tile)) return 0xFFFFFFFF; + if (!IsCustomRoadStopSpecIndex(nearby_tile)) return 0; + + const RoadStopSpecList ssl = BaseStation::GetByTile(nearby_tile)->roadstop_speclist[GetCustomRoadStopSpecIndex(nearby_tile)]; + return ssl.grfid; + } + + case 0xF0: return this->st == nullptr ? 0 : this->st->facilities; // facilities + + case 0xFA: return Clamp((this->st == nullptr ? _date : this->st->build_date) - DAYS_TILL_ORIGINAL_BASE_YEAR, 0, 65535); // build date + } + + if (this->st != nullptr) return this->st->GetNewGRFVariable(this->ro, variable, parameter, available); + + *available = false; + return UINT_MAX; +} + +const SpriteGroup *RoadStopResolverObject::ResolveReal(const RealSpriteGroup *group) const +{ + if (group == nullptr) return nullptr; + + return group->loading[0]; +} + +RoadStopResolverObject::RoadStopResolverObject(const RoadStopSpec *roadstopspec, BaseStation *st, TileIndex tile, RoadType roadtype, StationType type, uint8 view, + CallbackID callback, uint32 param1, uint32 param2) + : ResolverObject(roadstopspec->grf_prop.grffile, callback, param1, param2), roadstop_scope(*this, st, roadstopspec, tile, roadtype, type, view) + { + + this->town_scope = nullptr; + + CargoID ctype = CT_DEFAULT_NA; + + if (st == nullptr) { + /* No station, so we are in a purchase list */ + ctype = CT_PURCHASE; + } else if (Station::IsExpected(st)) { + const Station *station = Station::From(st); + /* Pick the first cargo that we have waiting */ + for (const CargoSpec *cs : CargoSpec::Iterate()) { + if (roadstopspec->grf_prop.spritegroup[cs->Index()] != nullptr && + station->goods[cs->Index()].cargo.TotalCount() > 0) { + ctype = cs->Index(); + break; + } + } + } + + if (roadstopspec->grf_prop.spritegroup[ctype] == nullptr) { + ctype = CT_DEFAULT; + } + + /* Remember the cargo type we've picked */ + this->roadstop_scope.cargo_type = ctype; + this->root_spritegroup = roadstopspec->grf_prop.spritegroup[ctype]; +} + +RoadStopResolverObject::~RoadStopResolverObject() +{ + delete this->town_scope; +} + +TownScopeResolver* RoadStopResolverObject::GetTown() +{ + if (this->town_scope == nullptr) { + Town *t; + if (this->roadstop_scope.st != nullptr) { + t = this->roadstop_scope.st->town; + } else { + t = ClosestTownFromTile(this->roadstop_scope.tile, UINT_MAX); + } + if (t == nullptr) return nullptr; + this->town_scope = new TownScopeResolver(*this, t, this->roadstop_scope.st == nullptr); + } + return this->town_scope; +} + +uint16 GetRoadStopCallback(CallbackID callback, uint32 param1, uint32 param2, const RoadStopSpec *roadstopspec, BaseStation *st, TileIndex tile, RoadType roadtype, StationType type, uint8 view) +{ + RoadStopResolverObject object(roadstopspec, st, tile, roadtype, type, view, callback, param1, param2); + return object.ResolveCallback(); +} + +/** + * Draw representation of a road stop tile for GUI purposes. + * @param x position x of image. + * @param y position y of image. + * @param image an int offset for the sprite. + * @param roadtype the RoadType of the underlying road. + * @param spec the RoadStop's spec. + * @return true of the tile was drawn (allows for fallback to default graphics) + */ +void DrawRoadStopTile(int x, int y, RoadType roadtype, const RoadStopSpec *spec, StationType type, int view) +{ + assert(roadtype != INVALID_ROADTYPE); + assert(spec != nullptr); + + const RoadTypeInfo *rti = GetRoadTypeInfo(roadtype); + RoadStopResolverObject object(spec, nullptr, INVALID_TILE, roadtype, type, view); + const SpriteGroup *group = object.Resolve(); + if (group == nullptr || group->type != SGT_TILELAYOUT) return; + const DrawTileSprites *dts = ((const TileLayoutSpriteGroup *)group)->ProcessRegisters(nullptr); + + PaletteID palette = COMPANY_SPRITE_COLOUR(_local_company); + + SpriteID image = dts->ground.sprite; + PaletteID pal = dts->ground.pal; + + if (GB(image, 0, SPRITE_WIDTH) != 0) { + DrawSprite(image, GroundSpritePaletteTransform(image, pal, palette), x, y); + } + + if (view >= 4) { + /* Drive-through stop */ + uint sprite_offset = 5 - view; + + /* Road underlay takes precedence over tram */ + if (spec->draw_mode & ROADSTOP_DRAW_MODE_OVERLAY) { + if (rti->UsesOverlay()) { + SpriteID ground = GetCustomRoadSprite(rti, INVALID_TILE, ROTSG_GROUND); + DrawSprite(ground + sprite_offset, PAL_NONE, x, y); + + SpriteID overlay = GetCustomRoadSprite(rti, INVALID_TILE, ROTSG_OVERLAY); + if (overlay) DrawSprite(overlay + sprite_offset, PAL_NONE, x, y); + } else if (RoadTypeIsTram(roadtype)) { + DrawSprite(SPR_TRAMWAY_TRAM + sprite_offset, PAL_NONE, x, y); + } + } + } else { + /* Drive-in stop */ + if ((spec->draw_mode & ROADSTOP_DRAW_MODE_ROAD) && rti->UsesOverlay()) { + SpriteID ground = GetCustomRoadSprite(rti, INVALID_TILE, ROTSG_ROADSTOP); + DrawSprite(ground + view, PAL_NONE, x, y); + } + } + + DrawCommonTileSeqInGUI(x, y, dts, 0, 0, palette, true); +} + +/** Wrapper for animation control, see GetRoadStopCallback. */ +uint16 GetAnimRoadStopCallback(CallbackID callback, uint32 param1, uint32 param2, const RoadStopSpec *roadstopspec, BaseStation *st, TileIndex tile, int extra_data) +{ + return GetRoadStopCallback(callback, param1, param2, roadstopspec, st, tile, INVALID_ROADTYPE, GetStationType(tile), GetStationGfx(tile)); +} + +struct RoadStopAnimationFrameAnimationHelper { + static byte Get(BaseStation *st, TileIndex tile) { return st->GetRoadStopAnimationFrame(tile); } + static void Set(BaseStation *st, TileIndex tile, byte frame) { st->SetRoadStopAnimationFrame(tile, frame); } +}; + +/** Helper class for animation control. */ +struct RoadStopAnimationBase : public AnimationBase { + static const CallbackID cb_animation_speed = CBID_STATION_ANIMATION_SPEED; + static const CallbackID cb_animation_next_frame = CBID_STATION_ANIM_NEXT_FRAME; + + static const RoadStopCallbackMask cbm_animation_speed = CBM_ROAD_STOP_ANIMATION_SPEED; + static const RoadStopCallbackMask cbm_animation_next_frame = CBM_ROAD_STOP_ANIMATION_NEXT_FRAME; +}; + +void AnimateRoadStopTile(TileIndex tile) +{ + const RoadStopSpec *ss = GetRoadStopSpec(tile); + if (ss == nullptr) return; + + RoadStopAnimationBase::AnimateTile(ss, BaseStation::GetByTile(tile), tile, HasBit(ss->flags, RSF_CB141_RANDOM_BITS)); +} + +void TriggerRoadStopAnimation(BaseStation *st, TileIndex trigger_tile, StationAnimationTrigger trigger, CargoID cargo_type) +{ + /* Get Station if it wasn't supplied */ + if (st == nullptr) st = BaseStation::GetByTile(trigger_tile); + + /* Check the cached animation trigger bitmask to see if we need + * to bother with any further processing. */ + if (!HasBit(st->cached_roadstop_anim_triggers, trigger)) return; + + uint16 random_bits = Random(); + auto process_tile = [&](TileIndex cur_tile) { + const RoadStopSpec *ss = GetRoadStopSpec(cur_tile); + if (ss != nullptr && HasBit(ss->animation.triggers, trigger)) { + CargoID cargo; + if (cargo_type == CT_INVALID) { + cargo = CT_INVALID; + } else { + cargo = ss->grf_prop.grffile->cargo_map[cargo_type]; + } + RoadStopAnimationBase::ChangeAnimationFrame(CBID_STATION_ANIM_START_STOP, ss, st, cur_tile, (random_bits << 16) | Random(), (uint8)trigger | (cargo << 8)); + } + }; + + if (trigger == SAT_NEW_CARGO || trigger == SAT_CARGO_TAKEN || trigger == SAT_250_TICKS) { + for (const RoadStopTileData &tile_data : st->custom_roadstop_tile_data) { + process_tile(tile_data.tile); + } + } else { + process_tile(trigger_tile); + } +} + +/** + * Trigger road stop randomisation + * + * @param st the station being triggered + * @param tile the exact tile of the station that should be triggered + * @param trigger trigger type + * @param cargo_type cargo type causing the trigger + */ +void TriggerRoadStopRandomisation(Station *st, TileIndex tile, RoadStopRandomTrigger trigger, CargoID cargo_type) +{ + if (st == nullptr) st = Station::GetByTile(tile); + + /* Check the cached cargo trigger bitmask to see if we need + * to bother with any further processing. */ + if (st->cached_roadstop_cargo_triggers == 0) return; + if (cargo_type != CT_INVALID && !HasBit(st->cached_roadstop_cargo_triggers, cargo_type)) return; + + SetBit(st->waiting_triggers, trigger); + + uint32 whole_reseed = 0; + + CargoTypes empty_mask = 0; + if (trigger == RSRT_CARGO_TAKEN) { + /* Create a bitmask of completely empty cargo types to be matched */ + for (CargoID i = 0; i < NUM_CARGO; i++) { + if (st->goods[i].cargo.TotalCount() == 0) { + SetBit(empty_mask, i); + } + } + } + + uint32 used_triggers = 0; + auto process_tile = [&](TileIndex cur_tile) { + const RoadStopSpec *ss = GetRoadStopSpec(cur_tile); + if (ss == nullptr) return; + + /* Cargo taken "will only be triggered if all of those + * cargo types have no more cargo waiting." */ + if (trigger == RSRT_CARGO_TAKEN) { + if ((ss->cargo_triggers & ~empty_mask) != 0) return; + } + + if (cargo_type == CT_INVALID || HasBit(ss->cargo_triggers, cargo_type)) { + RoadStopResolverObject object(ss, st, cur_tile, INVALID_ROADTYPE, GetStationType(cur_tile), GetStationGfx(cur_tile)); + object.waiting_triggers = st->waiting_triggers; + + const SpriteGroup *group = object.Resolve(); + if (group == nullptr) return; + + used_triggers |= object.used_triggers; + + uint32 reseed = object.GetReseedSum(); + if (reseed != 0) { + whole_reseed |= reseed; + reseed >>= 16; + + /* Set individual tile random bits */ + uint8 random_bits = st->GetRoadStopRandomBits(cur_tile); + random_bits &= ~reseed; + random_bits |= Random() & reseed; + st->SetRoadStopRandomBits(cur_tile, random_bits); + + MarkTileDirtyByTile(cur_tile); + } + } + }; + if (trigger == RSRT_NEW_CARGO || trigger == RSRT_CARGO_TAKEN) { + for (const RoadStopTileData &tile_data : st->custom_roadstop_tile_data) { + process_tile(tile_data.tile); + } + } else { + process_tile(tile); + } + + /* Update whole station random bits */ + st->waiting_triggers &= ~used_triggers; + if ((whole_reseed & 0xFFFF) != 0) { + st->random_bits &= ~whole_reseed; + st->random_bits |= Random() & whole_reseed; + } +} + +/** + * Checks if there's any new stations by a specific RoadStopType + * @param rs the RoadStopType to check. + * @param roadtype the RoadType to check. + * @return true if there was any new RoadStopSpec's found for the given RoadStopType and RoadType, else false. + */ +bool GetIfNewStopsByType(RoadStopType rs, RoadType roadtype) +{ + if (!(RoadStopClass::GetClassCount() > 1 || RoadStopClass::Get(ROADSTOP_CLASS_DFLT)->GetSpecCount() > 1)) return false; + for (uint i = 0; i < RoadStopClass::GetClassCount(); i++) { + // We don't want to check the default or waypoint classes. These classes are always available. + if (i == ROADSTOP_CLASS_DFLT || i == ROADSTOP_CLASS_WAYP) continue; + RoadStopClass *roadstopclass = RoadStopClass::Get((RoadStopClassID)i); + if (GetIfClassHasNewStopsByType(roadstopclass, rs, roadtype)) return true; + } + return false; +} + +/** + * Checks if the given RoadStopClass has any specs assigned to it, compatible with the given RoadStopType. + * @param roadstopclass the RoadStopClass to check. + * @param rs the RoadStopType to check. + * @param roadtype the RoadType to check. + * @return true if the RoadStopSpec has any specs compatible with the given RoadStopType and RoadType. + */ +bool GetIfClassHasNewStopsByType(RoadStopClass *roadstopclass, RoadStopType rs, RoadType roadtype) +{ + for (uint j = 0; j < roadstopclass->GetSpecCount(); j++) { + if (GetIfStopIsForType(roadstopclass->GetSpec(j), rs, roadtype)) return true; + } + return false; +} + +/** + * Checks if the given RoadStopSpec is compatible with the given RoadStopType. + * @param roadstopspec the RoadStopSpec to check. + * @param rs the RoadStopType to check. + * @param roadtype the RoadType to check. + * @return true if the RoadStopSpec is compatible with the given RoadStopType and RoadType. + */ +bool GetIfStopIsForType(const RoadStopSpec *roadstopspec, RoadStopType rs, RoadType roadtype) +{ + // The roadstopspec is nullptr, must be the default station, always return true. + if (roadstopspec == nullptr) return true; + + if (HasBit(roadstopspec->flags, RSF_BUILD_MENU_ROAD_ONLY) && !RoadTypeIsRoad(roadtype)) return false; + if (HasBit(roadstopspec->flags, RSF_BUILD_MENU_TRAM_ONLY) && !RoadTypeIsTram(roadtype)) return false; + + if (roadstopspec->stop_type == ROADSTOPTYPE_ALL) return true; + + switch (rs) { + case ROADSTOP_BUS: + if (roadstopspec->stop_type == ROADSTOPTYPE_PASSENGER) return true; + break; + + case ROADSTOP_TRUCK: + if (roadstopspec->stop_type == ROADSTOPTYPE_FREIGHT) return true; + break; + + default: + NOT_REACHED(); + } + return false; +} + +const RoadStopSpec *GetRoadStopSpec(TileIndex t) +{ + if (!IsCustomRoadStopSpecIndex(t)) return nullptr; + + const BaseStation *st = BaseStation::GetByTile(t); + uint specindex = GetCustomRoadStopSpecIndex(t); + return specindex < st->roadstop_speclist.size() ? st->roadstop_speclist[specindex].spec : nullptr; +} + +int AllocateSpecToRoadStop(const RoadStopSpec *statspec, BaseStation *st, bool exec) +{ + uint i; + + if (statspec == nullptr || st == nullptr) return 0; + + /* Try to find the same spec and return that one */ + for (i = 1; i < st->roadstop_speclist.size() && i < NUM_ROADSTOPSPECS_PER_STATION; i++) { + if (st->roadstop_speclist[i].spec == statspec) return i; + } + + /* Try to find an unused spec slot */ + for (i = 1; i < st->roadstop_speclist.size() && i < NUM_ROADSTOPSPECS_PER_STATION; i++) { + if (st->roadstop_speclist[i].spec == nullptr && st->roadstop_speclist[i].grfid == 0) break; + } + + if (i == NUM_ROADSTOPSPECS_PER_STATION) { + /* Full, give up */ + return -1; + } + + if (exec) { + if (i >= st->roadstop_speclist.size()) st->roadstop_speclist.resize(i + 1); + st->roadstop_speclist[i].spec = statspec; + st->roadstop_speclist[i].grfid = statspec->grf_prop.grffile->grfid; + st->roadstop_speclist[i].localidx = statspec->grf_prop.local_id; + + RoadStopUpdateCachedTriggers(st); + } + + return i; +} + +void DeallocateSpecFromRoadStop(BaseStation *st, byte specindex) +{ + /* specindex of 0 (default) is never freeable */ + if (specindex == 0) return; + + /* Check custom road stop tiles if the specindex is still in use */ + for (const RoadStopTileData &tile_data : st->custom_roadstop_tile_data) { + if (GetCustomRoadStopSpecIndex(tile_data.tile) == specindex) { + return; + } + } + + /* This specindex is no longer in use, so deallocate it */ + st->roadstop_speclist[specindex].spec = nullptr; + st->roadstop_speclist[specindex].grfid = 0; + st->roadstop_speclist[specindex].localidx = 0; + + /* If this was the highest spec index, reallocate */ + if (specindex == st->roadstop_speclist.size() - 1) { + size_t num_specs; + for (num_specs = st->roadstop_speclist.size() - 1; num_specs > 0; num_specs--) { + if (st->roadstop_speclist[num_specs].grfid != 0) break; + } + + if (num_specs > 0) { + st->roadstop_speclist.resize(num_specs + 1); + } else { + st->roadstop_speclist.clear(); + st->cached_roadstop_anim_triggers = 0; + st->cached_roadstop_cargo_triggers = 0; + return; + } + } + + RoadStopUpdateCachedTriggers(st); +} + +/** + * Update the cached animation trigger bitmask for a station. + * @param st Station to update. + */ +void RoadStopUpdateCachedTriggers(BaseStation *st) +{ + st->cached_roadstop_anim_triggers = 0; + st->cached_roadstop_cargo_triggers = 0; + + /* Combine animation trigger bitmask for all road stop specs + * of this station. */ + for (uint i = 0; i < st->roadstop_speclist.size(); i++) { + const RoadStopSpec *ss = st->roadstop_speclist[i].spec; + if (ss != nullptr) { + st->cached_roadstop_anim_triggers |= ss->animation.triggers; + st->cached_roadstop_cargo_triggers |= ss->cargo_triggers; + } + } +} diff --git a/src/newgrf_roadstop.h b/src/newgrf_roadstop.h new file mode 100644 index 0000000000..fa05c30ba1 --- /dev/null +++ b/src/newgrf_roadstop.h @@ -0,0 +1,189 @@ +/* + * This file is part of OpenTTD. + * OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2. + * OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see . + */ + +/** + * @file newgrf_roadstop.h NewGRF definitions and structures for road stops. + */ + +#ifndef NEWGRF_ROADSTATION_H +#define NEWGRF_ROADSTATION_H + +#include "newgrf_animation_type.h" +#include "newgrf_spritegroup.h" +#include "newgrf_class.h" +#include "newgrf_commons.h" +#include "newgrf_town.h" +#include "road.h" + +/** The maximum amount of roadstops a single GRF is allowed to add */ +static const int NUM_ROADSTOPS_PER_GRF = 255; + +enum RoadStopClassID : byte { + ROADSTOP_CLASS_BEGIN = 0, ///< The lowest valid value + ROADSTOP_CLASS_DFLT = 0, ///< Default road stop class. + ROADSTOP_CLASS_WAYP, ///< Waypoint class (unimplemented: this is reserved for future use with road waypoints). + ROADSTOP_CLASS_MAX = 255, ///< Maximum number of classes. +}; +DECLARE_POSTFIX_INCREMENT(RoadStopClassID) + +/* Some Triggers etc. */ +enum RoadStopRandomTrigger { + RSRT_NEW_CARGO, ///< Trigger roadstop on arrival of new cargo. + RSRT_CARGO_TAKEN, ///< Trigger roadstop when cargo is completely taken. + RSRT_VEH_ARRIVES, ///< Trigger roadstop when road vehicle arrives. + RSRT_VEH_DEPARTS, ///< Trigger roadstop when road vehicle leaves. + RSRT_VEH_LOADS, ///< Trigger roadstop when road vehicle loads. +}; + +/** + * Various different options for availability, restricting + * the roadstop to be only for busses or for trucks. + */ +enum RoadStopAvailabilityType : byte { + ROADSTOPTYPE_PASSENGER, ///< This RoadStop is for passenger (bus) stops. + ROADSTOPTYPE_FREIGHT, ///< This RoadStop is for freight (truck) stops. + ROADSTOPTYPE_ALL, ///< This RoadStop is for both types of station road stops. + + ROADSTOPTYPE_END, +}; + +/** + * Different draw modes to disallow rendering of some parts of the stop + * or road. + */ +enum RoadStopDrawMode : byte { + ROADSTOP_DRAW_MODE_NONE = 0, + ROADSTOP_DRAW_MODE_ROAD = 1 << 0, ///< Bay stops: Draw the road itself + ROADSTOP_DRAW_MODE_OVERLAY = 1 << 1, ///< Drive-through stops: Draw the road overlay, e.g. pavement +}; +DECLARE_ENUM_AS_BIT_SET(RoadStopDrawMode) + +enum RoadStopSpecFlags { + RSF_CB141_RANDOM_BITS = 0, ///< Callback 141 needs random bits. + RSF_NO_CATENARY = 2, ///< Do not show catenary. + RSF_DRIVE_THROUGH_ONLY = 3, ///< Stop is drive-through only. + RSF_NO_AUTO_ROAD_CONNECTION = 4, ///< No auto road connection. + RSF_BUILD_MENU_ROAD_ONLY = 5, ///< Only show in the road build menu (not tram). + RSF_BUILD_MENU_TRAM_ONLY = 6, ///< Only show in the tram build menu (not road). +}; + +/** Scope resolver for road stops. */ +struct RoadStopScopeResolver : public ScopeResolver { + TileIndex tile; ///< %Tile of the station. + struct BaseStation *st; ///< Instance of the station. + const struct RoadStopSpec *roadstopspec; ///< Station (type) specification. + CargoID cargo_type; ///< Type of cargo of the station. + StationType type; ///< Station type. + uint8 view; ///< Station axis. + RoadType roadtype; ///< Road type (used when no tile) + + RoadStopScopeResolver(ResolverObject& ro, BaseStation* st, const RoadStopSpec *roadstopspec, TileIndex tile, RoadType roadtype, StationType type, uint8 view = 0) + : ScopeResolver(ro), tile(tile), st(st), roadstopspec(roadstopspec), type(type), view(view), roadtype(roadtype) + { + } + + uint32 GetRandomBits() const override; + uint32 GetTriggers() const override; + + uint32 GetVariable(byte variable, uint32 parameter, bool *available) const override; +}; + +/** Road stop resolver. */ +struct RoadStopResolverObject : public ResolverObject { + RoadStopScopeResolver roadstop_scope; ///< The stop scope resolver. + TownScopeResolver *town_scope; ///< The town scope resolver (created on the first call). + + RoadStopResolverObject(const RoadStopSpec* roadstopspec, BaseStation* st, TileIndex tile, RoadType roadtype, StationType type, uint8 view, CallbackID callback = CBID_NO_CALLBACK, uint32 param1 = 0, uint32 param2 = 0); + ~RoadStopResolverObject(); + + ScopeResolver* GetScope(VarSpriteGroupScope scope = VSG_SCOPE_SELF, byte relative = 0) override + { + switch (scope) { + case VSG_SCOPE_SELF: return &this->roadstop_scope; + case VSG_SCOPE_PARENT: { + TownScopeResolver *tsr = this->GetTown(); + if (tsr != nullptr) return tsr; + FALLTHROUGH; + } + default: return ResolverObject::GetScope(scope, relative); + } + } + + TownScopeResolver *GetTown(); + + const SpriteGroup *ResolveReal(const RealSpriteGroup *group) const override; +}; + +/** Road stop specification. */ +struct RoadStopSpec { + /** + * Properties related the the grf file. + * NUM_CARGO real cargo plus three pseudo cargo sprite groups. + * Used for obtaining the sprite offset of custom sprites, and for + * evaluating callbacks. + */ + GRFFilePropsBase grf_prop; + RoadStopClassID cls_id; ///< The class to which this spec belongs. + int spec_id; ///< The ID of this spec inside the class. + StringID name; ///< Name of this stop + + RoadStopAvailabilityType stop_type = ROADSTOPTYPE_ALL; + RoadStopDrawMode draw_mode = ROADSTOP_DRAW_MODE_ROAD | ROADSTOP_DRAW_MODE_OVERLAY; + uint8 callback_mask = 0; + uint8 flags = 0; + + CargoTypes cargo_triggers = 0; ///< Bitmask of cargo types which cause trigger re-randomizing + + AnimationInfo animation; + + byte bridge_height[6]; ///< Minimum height for a bridge above, 0 for none + byte bridge_disallowed_pillars[6]; ///< Disallowed pillar flags for a bridge above + + uint8 build_cost_multiplier = 16; ///< Build cost multiplier per tile. + uint8 clear_cost_multiplier = 16; ///< Clear cost multiplier per tile. + + /** + * Get the cost for building a road stop of this type. + * @return The cost for building. + */ + Money GetBuildCost(Price category) const { return GetPrice(category, this->build_cost_multiplier, this->grf_prop.grffile, -4); } + + /** + * Get the cost for clearing a road stop of this type. + * @return The cost for clearing. + */ + Money GetClearCost(Price category) const { return GetPrice(category, this->clear_cost_multiplier, this->grf_prop.grffile, -4); } + + static const RoadStopSpec *Get(uint16 index); +}; + +template <> +struct EnumPropsT : MakeEnumPropsT {}; + +typedef NewGRFClass RoadStopClass; + +void DrawRoadStopTile(int x, int y, RoadType roadtype, const RoadStopSpec *spec, StationType type, int view); + +uint16 GetRoadStopCallback(CallbackID callback, uint32 param1, uint32 param2, const RoadStopSpec *roadstopspec, BaseStation *st, TileIndex tile, RoadType roadtype, StationType type, uint8 view); + +void AnimateRoadStopTile(TileIndex tile); +uint8 GetRoadStopTileAnimationSpeed(TileIndex tile); +void TriggerRoadStopAnimation(BaseStation *st, TileIndex tile, StationAnimationTrigger trigger, CargoID cargo_type = CT_INVALID); +void TriggerRoadStopRandomisation(Station *st, TileIndex tile, RoadStopRandomTrigger trigger, CargoID cargo_type = CT_INVALID); + +bool GetIfNewStopsByType(RoadStopType rs, RoadType roadtype); +bool GetIfClassHasNewStopsByType(RoadStopClass *roadstopclass, RoadStopType rs, RoadType roadtype); +bool GetIfStopIsForType(const RoadStopSpec *roadstopspec, RoadStopType rs, RoadType roadtype); + +uint GetCountOfCompatibleStopsByType(RoadStopClass *roadstopclass, RoadStopType rs); + +const RoadStopSpec *GetRoadStopSpec(TileIndex t); +int AllocateSpecToRoadStop(const RoadStopSpec *statspec, BaseStation *st, bool exec); +void DeallocateSpecFromRoadStop(BaseStation *st, byte specindex); +void RoadStopUpdateCachedTriggers(BaseStation *st); + +#endif /* NEWGRF_ROADSTATION_H */ diff --git a/src/newgrf_station.cpp b/src/newgrf_station.cpp index bf938eab0f..671104fe23 100644 --- a/src/newgrf_station.cpp +++ b/src/newgrf_station.cpp @@ -894,7 +894,7 @@ uint16 GetAnimStationCallback(CallbackID callback, uint32 param1, uint32 param2, } /** Helper class for animation control. */ -struct StationAnimationBase : public AnimationBase { +struct StationAnimationBase : public AnimationBase > { static const CallbackID cb_animation_speed = CBID_STATION_ANIMATION_SPEED; static const CallbackID cb_animation_next_frame = CBID_STATION_ANIM_NEXT_FRAME; diff --git a/src/road_cmd.h b/src/road_cmd.h index 21539e6cb8..fbe05ef33d 100644 --- a/src/road_cmd.h +++ b/src/road_cmd.h @@ -14,6 +14,8 @@ #include "road_type.h" #include "command_type.h" +enum RoadStopClassID : byte; + void DrawRoadDepotSprite(int x, int y, DiagDirection dir, RoadType rt); void UpdateNearestTownForRoadTiles(bool invalidate); @@ -32,6 +34,6 @@ DEF_CMD_TRAIT(CMD_CONVERT_ROAD, CmdConvertRoad, 0, CommandCallback CcPlaySound_CONSTRUCTION_OTHER; CommandCallback CcBuildRoadTunnel; void CcRoadDepot(Commands cmd, const CommandCost &result, TileIndex tile, RoadType rt, DiagDirection dir); -void CcRoadStop(Commands cmd, const CommandCost &result, TileIndex tile, uint8 width, uint8 length, RoadStopType, bool is_drive_through, DiagDirection dir, RoadType, StationID, bool); +void CcRoadStop(Commands cmd, const CommandCost &result, TileIndex tile, uint8 width, uint8 length, RoadStopType, bool is_drive_through, DiagDirection dir, RoadType, RoadStopClassID spec_class, byte spec_index, StationID, bool); #endif /* ROAD_CMD_H */ diff --git a/src/road_gui.cpp b/src/road_gui.cpp index b933db981d..0c9ee9d298 100644 --- a/src/road_gui.cpp +++ b/src/road_gui.cpp @@ -34,6 +34,11 @@ #include "station_cmd.h" #include "road_cmd.h" #include "tunnelbridge_cmd.h" +#include "newgrf_roadstop.h" +#include "querystring_gui.h" +#include "sortlist_type.h" +#include "stringfilter_type.h" +#include "string_func.h" #include "widgets/road_widget.h" @@ -55,7 +60,42 @@ static bool _place_road_end_half; static RoadType _cur_roadtype; static DiagDirection _road_depot_orientation; -static DiagDirection _road_station_picker_orientation; + +struct RoadStopGUISettings { + DiagDirection orientation; + + RoadStopClassID roadstop_class; + byte roadstop_type; + byte roadstop_count; +}; +static RoadStopGUISettings _roadstop_gui_settings; + +/** + * Check whether a road stop type can be built. + * @return true if building is allowed. + */ +static bool IsRoadStopAvailable(const RoadStopSpec *roadstopspec, StationType type) +{ + if (roadstopspec == nullptr) return true; + + if (HasBit(roadstopspec->flags, RSF_BUILD_MENU_ROAD_ONLY) && !RoadTypeIsRoad(_cur_roadtype)) return false; + if (HasBit(roadstopspec->flags, RSF_BUILD_MENU_TRAM_ONLY) && !RoadTypeIsTram(_cur_roadtype)) return false; + + if (roadstopspec->stop_type != ROADSTOPTYPE_ALL) { + switch (type) { + case STATION_BUS: if (roadstopspec->stop_type != ROADSTOPTYPE_PASSENGER) return false; break; + case STATION_TRUCK: if (roadstopspec->stop_type != ROADSTOPTYPE_FREIGHT) return false; break; + default: break; + } + } + + if (!HasBit(roadstopspec->callback_mask, CBM_ROAD_STOP_AVAIL)) return true; + + uint16 cb_res = GetRoadStopCallback(CBID_STATION_AVAILABILITY, 0, 0, roadstopspec, nullptr, INVALID_TILE, _cur_roadtype, type, 0); + if (cb_res == CALLBACK_FAILED) return true; + + return Convert8bitBooleanCallback(roadstopspec->grf_prop.grffile, CBID_STATION_AVAILABILITY, cb_res); +} void CcPlaySound_CONSTRUCTION_OTHER(Commands cmd, const CommandCost &result, TileIndex tile) { @@ -135,19 +175,31 @@ void CcRoadDepot(Commands cmd, const CommandCost &result, TileIndex tile, RoadTy * @param length Length of the road stop. * @param is_drive_through False for normal stops, true for drive-through. * @param dir Entrance direction (#DiagDirection) for normal stops. Converted to the axis for drive-through stops. + * @param spec_class Road stop spec class. + * @param spec_index Road stop spec index. * @see CmdBuildRoadStop */ -void CcRoadStop(Commands cmd, const CommandCost &result, TileIndex tile, uint8 width, uint8 length, RoadStopType, bool is_drive_through, DiagDirection dir, RoadType, StationID, bool) +void CcRoadStop(Commands cmd, const CommandCost &result, TileIndex tile, uint8 width, uint8 length, RoadStopType, bool is_drive_through, + DiagDirection dir, RoadType, RoadStopClassID spec_class, byte spec_index, StationID, bool) { if (result.Failed()) return; if (_settings_client.sound.confirm) SndPlayTileFx(SND_1F_CONSTRUCTION_OTHER, tile); if (!_settings_client.gui.persistent_buildingtools) ResetObjectToPlace(); - TileArea roadstop_area(tile, width, length); - for (TileIndex cur_tile : roadstop_area) { - ConnectRoadToStructure(cur_tile, dir); - /* For a drive-through road stop build connecting road for other entrance. */ - if (is_drive_through) ConnectRoadToStructure(cur_tile, ReverseDiagDir(dir)); + + bool connect_to_road = true; + if ((uint)spec_class < RoadStopClass::GetClassCount() && spec_index < RoadStopClass::Get(spec_class)->GetSpecCount()) { + const RoadStopSpec *roadstopspec = RoadStopClass::Get(spec_class)->GetSpec(spec_index); + if (roadstopspec != nullptr && HasBit(roadstopspec->flags, RSF_NO_AUTO_ROAD_CONNECTION)) connect_to_road = false; + } + + if (connect_to_road) { + TileArea roadstop_area(tile, width, length); + for (TileIndex cur_tile : roadstop_area) { + ConnectRoadToStructure(cur_tile, dir); + /* For a drive-through road stop build connecting road for other entrance. */ + if (is_drive_through) ConnectRoadToStructure(cur_tile, ReverseDiagDir(dir)); + } } } @@ -164,15 +216,19 @@ void CcRoadStop(Commands cmd, const CommandCost &result, TileIndex tile, uint8 w static void PlaceRoadStop(TileIndex start_tile, TileIndex end_tile, RoadStopType stop_type, bool adjacent, RoadType rt, StringID err_msg) { TileArea ta(start_tile, end_tile); - DiagDirection ddir = _road_station_picker_orientation; + DiagDirection ddir = _roadstop_gui_settings.orientation; bool drive_through = ddir >= DIAGDIR_END; if (drive_through) ddir = static_cast(ddir - DIAGDIR_END); // Adjust picker result to actual direction. + RoadStopClassID spec_class = _roadstop_gui_settings.roadstop_class; + byte spec_index = _roadstop_gui_settings.roadstop_type; auto proc = [=](bool test, StationID to_join) -> bool { if (test) { - return Command::Do(CommandFlagsToDCFlags(GetCommandFlags()), ta.tile, ta.w, ta.h, stop_type, drive_through, ddir, rt, INVALID_STATION, adjacent).Succeeded(); + return Command::Do(CommandFlagsToDCFlags(GetCommandFlags()), ta.tile, ta.w, ta.h, stop_type, drive_through, + ddir, rt, spec_class, spec_index, INVALID_STATION, adjacent).Succeeded(); } else { - return Command::Post(err_msg, CcRoadStop, ta.tile, ta.w, ta.h, stop_type, drive_through, ddir, rt, to_join, adjacent); + return Command::Post(err_msg, CcRoadStop, ta.tile, ta.w, ta.h, stop_type, drive_through, + ddir, rt, spec_class, spec_index, to_join, adjacent); } }; @@ -188,8 +244,8 @@ static void PlaceRoad_BusStation(TileIndex tile) if (_remove_button_clicked) { VpStartPlaceSizing(tile, VPM_X_AND_Y, DDSP_REMOVE_BUSSTOP); } else { - if (_road_station_picker_orientation < DIAGDIR_END) { // Not a drive-through stop. - VpStartPlaceSizing(tile, (DiagDirToAxis(_road_station_picker_orientation) == AXIS_X) ? VPM_X_LIMITED : VPM_Y_LIMITED, DDSP_BUILD_BUSSTOP); + if (_roadstop_gui_settings.orientation < DIAGDIR_END) { // Not a drive-through stop. + VpStartPlaceSizing(tile, (DiagDirToAxis(_roadstop_gui_settings.orientation) == AXIS_X) ? VPM_X_LIMITED : VPM_Y_LIMITED, DDSP_BUILD_BUSSTOP); } else { VpStartPlaceSizing(tile, VPM_X_AND_Y_LIMITED, DDSP_BUILD_BUSSTOP); } @@ -206,8 +262,8 @@ static void PlaceRoad_TruckStation(TileIndex tile) if (_remove_button_clicked) { VpStartPlaceSizing(tile, VPM_X_AND_Y, DDSP_REMOVE_TRUCKSTOP); } else { - if (_road_station_picker_orientation < DIAGDIR_END) { // Not a drive-through stop. - VpStartPlaceSizing(tile, (DiagDirToAxis(_road_station_picker_orientation) == AXIS_X) ? VPM_X_LIMITED : VPM_Y_LIMITED, DDSP_BUILD_TRUCKSTOP); + if (_roadstop_gui_settings.orientation < DIAGDIR_END) { // Not a drive-through stop. + VpStartPlaceSizing(tile, (DiagDirToAxis(_roadstop_gui_settings.orientation) == AXIS_X) ? VPM_X_LIMITED : VPM_Y_LIMITED, DDSP_BUILD_TRUCKSTOP); } else { VpStartPlaceSizing(tile, VPM_X_AND_Y_LIMITED, DDSP_BUILD_TRUCKSTOP); } @@ -652,7 +708,7 @@ struct BuildRoadToolbarWindow : Window { case DDSP_BUILD_BUSSTOP: case DDSP_REMOVE_BUSSTOP: - if (this->IsWidgetLowered(WID_ROT_BUS_STATION)) { + if (this->IsWidgetLowered(WID_ROT_BUS_STATION) && GetIfClassHasNewStopsByType(RoadStopClass::Get(_roadstop_gui_settings.roadstop_class), ROADSTOP_BUS, _cur_roadtype)) { if (_remove_button_clicked) { TileArea ta(start_tile, end_tile); Command::Post(this->rti->strings.err_remove_station[ROADSTOP_BUS], CcPlaySound_CONSTRUCTION_OTHER, ta.tile, ta.w, ta.h, ROADSTOP_BUS, _ctrl_pressed); @@ -664,7 +720,7 @@ struct BuildRoadToolbarWindow : Window { case DDSP_BUILD_TRUCKSTOP: case DDSP_REMOVE_TRUCKSTOP: - if (this->IsWidgetLowered(WID_ROT_TRUCK_STATION)) { + if (this->IsWidgetLowered(WID_ROT_TRUCK_STATION) && GetIfClassHasNewStopsByType(RoadStopClass::Get(_roadstop_gui_settings.roadstop_class), ROADSTOP_TRUCK, _cur_roadtype)) { if (_remove_button_clicked) { TileArea ta(start_tile, end_tile); Command::Post(this->rti->strings.err_remove_station[ROADSTOP_TRUCK], CcPlaySound_CONSTRUCTION_OTHER, ta.tile, ta.w, ta.h, ROADSTOP_TRUCK, _ctrl_pressed); @@ -1046,14 +1102,91 @@ static void ShowRoadDepotPicker(Window *parent) new BuildRoadDepotWindow(&_build_road_depot_desc, parent); } +/** Enum referring to the Hotkeys in the build road stop window */ +enum BuildRoadStopHotkeys { + BROSHK_FOCUS_FILTER_BOX, ///< Focus the edit box for editing the filter string +}; + struct BuildRoadStationWindow : public PickerWindowBase { - BuildRoadStationWindow(WindowDesc *desc, Window *parent, RoadStopType rs) : PickerWindowBase(desc, parent) +private: + RoadStopType roadStopType; ///< The RoadStopType for this Window. + uint line_height; ///< Height of a single line in the newstation selection matrix. + uint coverage_height; ///< Height of the coverage texts. + Scrollbar *vscrollList; ///< Vertical scrollbar of the new station list. + Scrollbar *vscrollMatrix; ///< Vertical scrollbar of the station picker matrix. + + typedef GUIList GUIRoadStopClassList; ///< Type definition for the list to hold available road stop classes. + + static const uint EDITBOX_MAX_SIZE = 16; ///< The maximum number of characters for the filter edit box. + + static Listing last_sorting; ///< Default sorting of #GUIRoadStopClassList. + static Filtering last_filtering; ///< Default filtering of #GUIRoadStopClassList. + static GUIRoadStopClassList::SortFunction * const sorter_funcs[]; ///< Sort functions of the #GUIRoadStopClassList. + static GUIRoadStopClassList::FilterFunction * const filter_funcs[]; ///< Filter functions of the #GUIRoadStopClassList. + GUIRoadStopClassList roadstop_classes; ///< Available road stop classes. + StringFilter string_filter; ///< Filter for available road stop classes. + QueryString filter_editbox; ///< Filter editbox. + + void EnsureSelectedClassIsVisible() { + uint pos = 0; + for (auto rs_class : this->roadstop_classes) { + if (rs_class == _roadstop_gui_settings.roadstop_class) break; + pos++; + } + this->vscrollList->SetCount((int)this->roadstop_classes.size()); + this->vscrollList->ScrollTowards(pos); + } + + void CheckOrientationValid() + { + if (_roadstop_gui_settings.orientation >= DIAGDIR_END) return; + const RoadStopSpec *spec = RoadStopClass::Get(_roadstop_gui_settings.roadstop_class)->GetSpec(_roadstop_gui_settings.roadstop_type); + if (spec != nullptr && HasBit(spec->flags, RSF_DRIVE_THROUGH_ONLY)) { + this->RaiseWidget(_roadstop_gui_settings.orientation + WID_BROS_STATION_NE); + _roadstop_gui_settings.orientation = DIAGDIR_END; + this->LowerWidget(_roadstop_gui_settings.orientation + WID_BROS_STATION_NE); + this->SetDirty(); + CloseWindowById(WC_SELECT_STATION, 0); + } + } + +public: + BuildRoadStationWindow(WindowDesc *desc, Window *parent, RoadStopType rs) : PickerWindowBase(desc, parent), filter_editbox(EDITBOX_MAX_SIZE * MAX_CHAR_LENGTH, EDITBOX_MAX_SIZE) + { + this->coverage_height = 2 * FONT_HEIGHT_NORMAL + 3 * WidgetDimensions::scaled.vsep_normal; + this->vscrollList = nullptr; + this->vscrollMatrix = nullptr; + this->roadStopType = rs; + bool newstops = GetIfNewStopsByType(rs, _cur_roadtype); + this->CreateNestedTree(); + /* Hide the station class filter if no stations other than the default one are available. */ + this->GetWidget(WID_BROS_SHOW_NEWST_DEFSIZE)->SetDisplayedPlane(newstops ? 0 : SZSP_NONE); + this->GetWidget(WID_BROS_FILTER_CONTAINER)->SetDisplayedPlane(newstops ? 0 : SZSP_HORIZONTAL); + this->GetWidget(WID_BROS_SHOW_NEWST_ADDITIONS)->SetDisplayedPlane(newstops ? 0 : SZSP_HORIZONTAL); + this->GetWidget(WID_BROS_SHOW_NEWST_ORIENTATION)->SetDisplayedPlane(newstops ? 0 : SZSP_HORIZONTAL); + this->GetWidget(WID_BROS_SHOW_NEWST_TYPE_SEL)->SetDisplayedPlane(newstops ? 0 : SZSP_HORIZONTAL); + this->GetWidget(WID_BROS_SHOW_NEWST_MATRIX)->SetDisplayedPlane(newstops ? 0 : SZSP_NONE); + this->GetWidget(WID_BROS_SHOW_NEWST_RESIZE)->SetDisplayedPlane(newstops ? 0 : SZSP_NONE); + if (newstops) { + this->vscrollList = this->GetScrollbar(WID_BROS_NEWST_SCROLL); + this->vscrollMatrix = this->GetScrollbar(WID_BROS_MATRIX_SCROLL); + + this->querystrings[WID_BROS_FILTER_EDITBOX] = &this->filter_editbox; + this->roadstop_classes.SetListing(this->last_sorting); + this->roadstop_classes.SetFiltering(this->last_filtering); + this->roadstop_classes.SetSortFuncs(this->sorter_funcs); + this->roadstop_classes.SetFilterFuncs(this->filter_funcs); + } + + this->roadstop_classes.ForceRebuild(); + BuildRoadStopClassesAvailable(); + /* Trams don't have non-drivethrough stations */ - if (RoadTypeIsTram(_cur_roadtype) && _road_station_picker_orientation < DIAGDIR_END) { - _road_station_picker_orientation = DIAGDIR_END; + if (RoadTypeIsTram(_cur_roadtype) && _roadstop_gui_settings.orientation < DIAGDIR_END) { + _roadstop_gui_settings.orientation = DIAGDIR_END; } const RoadTypeInfo *rti = GetRoadTypeInfo(_cur_roadtype); this->GetWidget(WID_BROS_CAPTION)->widget_data = rti->strings.picker_title[rs]; @@ -1062,12 +1195,42 @@ struct BuildRoadStationWindow : public PickerWindowBase { this->GetWidget(i)->tool_tip = rti->strings.picker_tooltip[rs]; } - this->LowerWidget(_road_station_picker_orientation + WID_BROS_STATION_NE); + this->LowerWidget(_roadstop_gui_settings.orientation + WID_BROS_STATION_NE); this->LowerWidget(_settings_client.gui.station_show_coverage + WID_BROS_LT_OFF); this->FinishInitNested(TRANSPORT_ROAD); this->window_class = (rs == ROADSTOP_BUS) ? WC_BUS_STATION : WC_TRUCK_STATION; + if (!newstops || _roadstop_gui_settings.roadstop_class >= (int)RoadStopClass::GetClassCount()) { + /* There's no new stops available or the list has reduced in size. + * Now, set the default road stops as selected. */ + _roadstop_gui_settings.roadstop_class = ROADSTOP_CLASS_DFLT; + _roadstop_gui_settings.roadstop_type = 0; + } + if (newstops) { + /* The currently selected class doesn't have any stops for this RoadStopType, reset the selection. */ + if (!GetIfClassHasNewStopsByType(RoadStopClass::Get(_roadstop_gui_settings.roadstop_class), rs, _cur_roadtype)) { + _roadstop_gui_settings.roadstop_class = ROADSTOP_CLASS_DFLT; + _roadstop_gui_settings.roadstop_type = 0; + } + _roadstop_gui_settings.roadstop_count = RoadStopClass::Get(_roadstop_gui_settings.roadstop_class)->GetSpecCount(); + _roadstop_gui_settings.roadstop_type = std::min((int)_roadstop_gui_settings.roadstop_type, _roadstop_gui_settings.roadstop_count - 1); + + /* Reset back to default class if the previously selected class is not available for this road stop type. */ + if (!GetIfClassHasNewStopsByType(RoadStopClass::Get(_roadstop_gui_settings.roadstop_class), roadStopType, _cur_roadtype)) { + _roadstop_gui_settings.roadstop_class = ROADSTOP_CLASS_DFLT; + } + + this->SelectFirstAvailableTypeIfUnavailable(); + + NWidgetMatrix *matrix = this->GetWidget(WID_BROS_MATRIX); + matrix->SetScrollbar(this->vscrollMatrix); + matrix->SetCount(_roadstop_gui_settings.roadstop_count); + matrix->SetClicked(_roadstop_gui_settings.roadstop_type); + + this->EnsureSelectedClassIsVisible(); + this->CheckOrientationValid(); + } } void Close() override @@ -1076,6 +1239,94 @@ struct BuildRoadStationWindow : public PickerWindowBase { this->PickerWindowBase::Close(); } + /** Sort classes by RoadStopClassID. */ + static bool RoadStopClassIDSorter(RoadStopClassID const &a, RoadStopClassID const &b) + { + return a < b; + } + + /** Filter classes by class name. */ + static bool CDECL TagNameFilter(RoadStopClassID const *sc, StringFilter &filter) + { + char buffer[DRAW_STRING_BUFFER]; + GetString(buffer, RoadStopClass::Get(*sc)->name, lastof(buffer)); + + filter.ResetState(); + filter.AddLine(buffer); + return filter.GetState(); + } + + inline bool ShowNewStops() const + { + return this->vscrollList != nullptr; + } + + void BuildRoadStopClassesAvailable() + { + if (!this->roadstop_classes.NeedRebuild()) return; + + this->roadstop_classes.clear(); + + for (uint i = 0; i < RoadStopClass::GetClassCount(); i++) { + RoadStopClassID rs_id = (RoadStopClassID)i; + if (rs_id == ROADSTOP_CLASS_WAYP) { + // Skip waypoints. + continue; + } + RoadStopClass *rs_class = RoadStopClass::Get(rs_id); + if (GetIfClassHasNewStopsByType(rs_class, this->roadStopType, _cur_roadtype)) this->roadstop_classes.push_back(rs_id); + } + + if (this->ShowNewStops()) { + this->roadstop_classes.Filter(this->string_filter); + this->roadstop_classes.shrink_to_fit(); + this->roadstop_classes.RebuildDone(); + this->roadstop_classes.Sort(); + + this->vscrollList->SetCount((uint)this->roadstop_classes.size()); + } + } + + void SelectFirstAvailableTypeIfUnavailable() + { + const RoadStopClass *rs_class = RoadStopClass::Get(_roadstop_gui_settings.roadstop_class); + StationType st = GetRoadStationTypeByWindowClass(this->window_class); + + if (IsRoadStopAvailable(rs_class->GetSpec(_roadstop_gui_settings.roadstop_type), st)) return; + for (uint i = 0; i < _roadstop_gui_settings.roadstop_count; i++) { + if (IsRoadStopAvailable(rs_class->GetSpec(i), st)) { + _roadstop_gui_settings.roadstop_type = i; + break; + } + } + } + + void OnInvalidateData(int data = 0, bool gui_scope = true) override + { + if (!gui_scope) return; + + this->BuildRoadStopClassesAvailable(); + } + + EventState OnHotkey(int hotkey) override + { + if (hotkey == BROSHK_FOCUS_FILTER_BOX) { + this->SetFocusedWidget(WID_BROS_FILTER_EDITBOX); + SetFocusedWindow(this); // The user has asked to give focus to the text box, so make sure this window is focused. + return ES_HANDLED; + } + + return ES_NOT_HANDLED; + } + + void OnEditboxChanged(int wid) override + { + string_filter.SetFilterTerm(this->filter_editbox.text.buf); + this->roadstop_classes.SetFilterState(!string_filter.IsEmpty()); + this->roadstop_classes.ForceRebuild(); + this->InvalidateData(); + } + void OnPaint() override { this->DrawWidgets(); @@ -1087,6 +1338,8 @@ struct BuildRoadStationWindow : public PickerWindowBase { SetTileSelectSize(1, 1); } + if (this->IsShaded()) return; + /* 'Accepts' and 'Supplies' texts. */ StationCoverageType sct = (this->window_class == WC_BUS_STATION) ? SCT_PASSENGERS_ONLY : SCT_NON_PASSENGERS_ONLY; Rect r = this->GetWidget(WID_BROS_ACCEPTANCE)->GetCurrentRect(); @@ -1097,45 +1350,175 @@ struct BuildRoadStationWindow : public PickerWindowBase { * Never make the window smaller to avoid oscillating if the size change affects the acceptance. * (This is the case, if making the window bigger moves the mouse into the window.) */ if (top > r.bottom) { - ResizeWindow(this, 0, top - r.bottom, false); + this->coverage_height += top - r.bottom; + this->ReInit(); } } void UpdateWidgetSize(int widget, Dimension *size, const Dimension &padding, Dimension *fill, Dimension *resize) override { - if (!IsInsideMM(widget, WID_BROS_STATION_NE, WID_BROS_STATION_Y + 1)) return; + switch (widget) { + case WID_BROS_NEWST_LIST: { + Dimension d = { 0, 0 }; + for (auto rs_class : this->roadstop_classes) { + d = maxdim(d, GetStringBoundingBox(RoadStopClass::Get(rs_class)->name)); + } + size->width = std::max(size->width, d.width + padding.width); + this->line_height = FONT_HEIGHT_NORMAL + WidgetDimensions::scaled.matrix.Vertical(); + size->height = 5 * this->line_height; + resize->height = this->line_height; + break; + } - size->width = ScaleGUITrad(64) + WidgetDimensions::scaled.fullbevel.Horizontal(); - size->height = ScaleGUITrad(48) + WidgetDimensions::scaled.fullbevel.Vertical(); + case WID_BROS_SHOW_NEWST_TYPE: { + Dimension d = {0, 0}; + StringID str = this->GetWidget(widget)->widget_data; + for (auto roadstop_class : this->roadstop_classes) { + RoadStopClass *rs_class = RoadStopClass::Get(roadstop_class); + for (uint j = 0; j < rs_class->GetSpecCount(); j++) { + const RoadStopSpec *roadstopspec = rs_class->GetSpec(j); + SetDParam(0, (roadstopspec != nullptr && roadstopspec->name != 0) ? roadstopspec->name : STR_STATION_CLASS_DFLT); + d = maxdim(d, GetStringBoundingBox(str)); + } + } + size->width = std::max(size->width, d.width + padding.width); + break; + } + + case WID_BROS_STATION_NE: + case WID_BROS_STATION_SE: + case WID_BROS_STATION_SW: + case WID_BROS_STATION_NW: + case WID_BROS_STATION_X: + case WID_BROS_STATION_Y: + case WID_BROS_IMAGE: + size->width = ScaleGUITrad(64) + WidgetDimensions::scaled.fullbevel.Horizontal(); + size->height = ScaleGUITrad(48) + WidgetDimensions::scaled.fullbevel.Vertical(); + break; + + case WID_BROS_MATRIX: + fill->height = 1; + resize->height = 1; + break; + + case WID_BROS_ACCEPTANCE: + size->height = this->coverage_height; + break; + } + } + + /** + * Simply to have a easier way to get the StationType for bus, truck and trams from the WindowClass. + */ + StationType GetRoadStationTypeByWindowClass(WindowClass window_class) const { + switch (window_class) { + case WC_BUS_STATION: return STATION_BUS; + case WC_TRUCK_STATION: return STATION_TRUCK; + default: NOT_REACHED(); + } } void DrawWidget(const Rect &r, int widget) const override { - if (!IsInsideMM(widget, WID_BROS_STATION_NE, WID_BROS_STATION_Y + 1)) return; + switch (GB(widget, 0, 16)) { + case WID_BROS_STATION_NE: + case WID_BROS_STATION_SE: + case WID_BROS_STATION_SW: + case WID_BROS_STATION_NW: + case WID_BROS_STATION_X: + case WID_BROS_STATION_Y: { + StationType st = GetRoadStationTypeByWindowClass(this->window_class); + const RoadStopSpec *spec = RoadStopClass::Get(_roadstop_gui_settings.roadstop_class)->GetSpec(_roadstop_gui_settings.roadstop_type); + bool disabled = (spec != nullptr && widget < WID_BROS_STATION_X && HasBit(spec->flags, RSF_DRIVE_THROUGH_ONLY)); + DrawPixelInfo tmp_dpi; + if (FillDrawPixelInfo(&tmp_dpi, r.left, r.top, r.Width(), r.Height())) { + AutoRestoreBackup dpi_backup(_cur_dpi, &tmp_dpi); + int x = (r.Width() - ScaleSpriteTrad(64)) / 2 + ScaleSpriteTrad(31); + int y = (r.Height() + ScaleSpriteTrad(48)) / 2 - ScaleSpriteTrad(31); + if (spec == nullptr || disabled) { + StationPickerDrawSprite(x, y, st, INVALID_RAILTYPE, _cur_roadtype, widget - WID_BROS_STATION_NE); + if (disabled) GfxFillRect(1, 1, r.Width() - 1, r.Height() - 1, PC_BLACK, FILLRECT_CHECKER); + } else { + DrawRoadStopTile(x, y, _cur_roadtype, spec, st, widget - WID_BROS_STATION_NE); + } + } + break; + } - StationType st = (this->window_class == WC_BUS_STATION) ? STATION_BUS : STATION_TRUCK; + case WID_BROS_NEWST_LIST: { + uint statclass = 0; + uint row = 0; + for (auto rs_class : this->roadstop_classes) { + if (this->vscrollList->IsVisible(statclass)) { + DrawString(r.left + WidgetDimensions::scaled.matrix.left, r.right, row * this->line_height + r.top + WidgetDimensions::scaled.matrix.top, + RoadStopClass::Get(rs_class)->name, + rs_class == _roadstop_gui_settings.roadstop_class ? TC_WHITE : TC_BLACK); + row++; + } + statclass++; + } + break; + } - DrawPixelInfo tmp_dpi; - if (FillDrawPixelInfo(&tmp_dpi, r.left, r.top, r.Width(), r.Height())) { - AutoRestoreBackup dpi_backup(_cur_dpi, &tmp_dpi); - int x = (r.Width() - ScaleSpriteTrad(64)) / 2 + ScaleSpriteTrad(31); - int y = (r.Height() + ScaleSpriteTrad(48)) / 2 - ScaleSpriteTrad(31); - StationPickerDrawSprite(x, y, st, INVALID_RAILTYPE, _cur_roadtype, widget - WID_BROS_STATION_NE); + case WID_BROS_IMAGE: { + byte type = GB(widget, 16, 16); + assert(type < _roadstop_gui_settings.roadstop_count); + + const RoadStopSpec *spec = RoadStopClass::Get(_roadstop_gui_settings.roadstop_class)->GetSpec(type); + StationType st = GetRoadStationTypeByWindowClass(this->window_class); + + if (!IsRoadStopAvailable(spec, st)) { + GfxFillRect(r.Shrink(WidgetDimensions::scaled.bevel), PC_BLACK, FILLRECT_CHECKER); + } + + /* Set up a clipping area for the sprite preview. */ + DrawPixelInfo tmp_dpi; + if (FillDrawPixelInfo(&tmp_dpi, r.left, r.top, r.Width(), r.Height())) { + AutoRestoreBackup dpi_backup(_cur_dpi, &tmp_dpi); + int x = (r.Width() - ScaleSpriteTrad(64)) / 2 + ScaleSpriteTrad(31); + int y = (r.Height() + ScaleSpriteTrad(48)) / 2 - ScaleSpriteTrad(31); + if (spec == nullptr) { + StationPickerDrawSprite(x, y, st, INVALID_RAILTYPE, _cur_roadtype, _roadstop_gui_settings.orientation); + } else { + DiagDirection orientation = _roadstop_gui_settings.orientation; + if (orientation < DIAGDIR_END && HasBit(spec->flags, RSF_DRIVE_THROUGH_ONLY)) orientation = DIAGDIR_END; + DrawRoadStopTile(x, y, _cur_roadtype, spec, st, (uint8)orientation); + } + } + break; + } + } + } + + void OnResize() override { + if (this->vscrollList != nullptr) { + this->vscrollList->SetCapacityFromWidget(this, WID_BROS_NEWST_LIST); + } + } + + void SetStringParameters(int widget) const override { + if (widget == WID_BROS_SHOW_NEWST_TYPE) { + const RoadStopSpec *roadstopspec = RoadStopClass::Get(_roadstop_gui_settings.roadstop_class)->GetSpec(_roadstop_gui_settings.roadstop_type); + SetDParam(0, (roadstopspec != nullptr && roadstopspec->name != 0) ? roadstopspec->name : STR_STATION_CLASS_DFLT); } } void OnClick(Point pt, int widget, int click_count) override { - switch (widget) { + switch (GB(widget, 0, 16)) { case WID_BROS_STATION_NE: case WID_BROS_STATION_SE: case WID_BROS_STATION_SW: case WID_BROS_STATION_NW: case WID_BROS_STATION_X: case WID_BROS_STATION_Y: - this->RaiseWidget(_road_station_picker_orientation + WID_BROS_STATION_NE); - _road_station_picker_orientation = (DiagDirection)(widget - WID_BROS_STATION_NE); - this->LowerWidget(_road_station_picker_orientation + WID_BROS_STATION_NE); + if (widget < WID_BROS_STATION_X) { + const RoadStopSpec *spec = RoadStopClass::Get(_roadstop_gui_settings.roadstop_class)->GetSpec(_roadstop_gui_settings.roadstop_type); + if (spec != nullptr && HasBit(spec->flags, RSF_DRIVE_THROUGH_ONLY)) return; + } + this->RaiseWidget(_roadstop_gui_settings.orientation + WID_BROS_STATION_NE); + _roadstop_gui_settings.orientation = (DiagDirection)(widget - WID_BROS_STATION_NE); + this->LowerWidget(_roadstop_gui_settings.orientation + WID_BROS_STATION_NE); if (_settings_client.sound.click_beep) SndPlayFx(SND_15_BEEP); this->SetDirty(); CloseWindowById(WC_SELECT_STATION, 0); @@ -1151,6 +1534,48 @@ struct BuildRoadStationWindow : public PickerWindowBase { SetViewportCatchmentStation(nullptr, true); break; + case WID_BROS_NEWST_LIST: { + int y = this->vscrollList->GetScrolledRowFromWidget(pt.y, this, WID_BROS_NEWST_LIST); + if (y >= (int)this->roadstop_classes.size()) return; + RoadStopClassID class_id = this->roadstop_classes[y]; + if (_roadstop_gui_settings.roadstop_class != class_id && GetIfClassHasNewStopsByType(RoadStopClass::Get(class_id), roadStopType, _cur_roadtype)) { + _roadstop_gui_settings.roadstop_class = class_id; + RoadStopClass *rsclass = RoadStopClass::Get(_roadstop_gui_settings.roadstop_class); + _roadstop_gui_settings.roadstop_count = rsclass->GetSpecCount(); + _roadstop_gui_settings.roadstop_type = std::min((int)_roadstop_gui_settings.roadstop_type, std::max(0, (int)_roadstop_gui_settings.roadstop_count - 1)); + this->SelectFirstAvailableTypeIfUnavailable(); + + NWidgetMatrix *matrix = this->GetWidget(WID_BROS_MATRIX); + matrix->SetCount(_roadstop_gui_settings.roadstop_count); + matrix->SetClicked(_roadstop_gui_settings.roadstop_type); + this->CheckOrientationValid(); + } + if (_settings_client.sound.click_beep) SndPlayFx(SND_15_BEEP); + this->SetDirty(); + CloseWindowById(WC_SELECT_STATION, 0); + break; + } + + case WID_BROS_IMAGE: { + int y = GB(widget, 16, 16); + if (y >= _roadstop_gui_settings.roadstop_count) return; + + const RoadStopSpec *spec = RoadStopClass::Get(_roadstop_gui_settings.roadstop_class)->GetSpec(y); + StationType st = GetRoadStationTypeByWindowClass(this->window_class); + + if (!IsRoadStopAvailable(spec, st)) return; + + _roadstop_gui_settings.roadstop_type = y; + + this->GetWidget(WID_BROS_MATRIX)->SetClicked(_roadstop_gui_settings.roadstop_type); + + if (_settings_client.sound.click_beep) SndPlayFx(SND_15_BEEP); + this->SetDirty(); + CloseWindowById(WC_SELECT_STATION, 0); + this->CheckOrientationValid(); + break; + } + default: break; } @@ -1160,6 +1585,25 @@ struct BuildRoadStationWindow : public PickerWindowBase { { CheckRedrawStationCoverage(this); } + + static HotkeyList hotkeys; +}; + +static Hotkey buildroadstop_hotkeys[] = { + Hotkey('F', "focus_filter_box", BROSHK_FOCUS_FILTER_BOX), + HOTKEY_LIST_END +}; +HotkeyList BuildRoadStationWindow::hotkeys("buildroadstop", buildroadstop_hotkeys); + +Listing BuildRoadStationWindow::last_sorting = { false, 0 }; +Filtering BuildRoadStationWindow::last_filtering = { false, 0 }; + +BuildRoadStationWindow::GUIRoadStopClassList::SortFunction * const BuildRoadStationWindow::sorter_funcs[] = { + &RoadStopClassIDSorter, +}; + +BuildRoadStationWindow::GUIRoadStopClassList::FilterFunction * const BuildRoadStationWindow::filter_funcs[] = { + &TagNameFilter, }; /** Widget definition of the build road station window */ @@ -1167,34 +1611,82 @@ static const NWidgetPart _nested_road_station_picker_widgets[] = { NWidget(NWID_HORIZONTAL), NWidget(WWT_CLOSEBOX, COLOUR_DARK_GREEN), NWidget(WWT_CAPTION, COLOUR_DARK_GREEN, WID_BROS_CAPTION), + NWidget(WWT_SHADEBOX, COLOUR_DARK_GREEN), + NWidget(NWID_SELECTION, INVALID_COLOUR, WID_BROS_SHOW_NEWST_DEFSIZE), + NWidget(WWT_DEFSIZEBOX, COLOUR_DARK_GREEN), + EndContainer(), EndContainer(), NWidget(WWT_PANEL, COLOUR_DARK_GREEN, WID_BROS_BACKGROUND), - NWidget(NWID_HORIZONTAL), SetPadding(3), - NWidget(NWID_SPACER), SetFill(1, 0), - NWidget(NWID_VERTICAL), SetPIP(0, 2, 0), - NWidget(NWID_HORIZONTAL), SetPIP(0, 2, 0), - NWidget(WWT_PANEL, COLOUR_GREY, WID_BROS_STATION_NW), SetMinimalSize(66, 50), SetFill(0, 0), EndContainer(), - NWidget(WWT_PANEL, COLOUR_GREY, WID_BROS_STATION_NE), SetMinimalSize(66, 50), SetFill(0, 0), EndContainer(), - NWidget(WWT_PANEL, COLOUR_GREY, WID_BROS_STATION_X), SetMinimalSize(66, 50), SetFill(0, 0), EndContainer(), + NWidget(NWID_HORIZONTAL), SetPadding(2, 0, 0, 2), + NWidget(NWID_VERTICAL), + NWidget(NWID_SELECTION, INVALID_COLOUR, WID_BROS_FILTER_CONTAINER), + NWidget(NWID_HORIZONTAL), SetPadding(0, 5, 2, 0), + NWidget(WWT_TEXT, COLOUR_DARK_GREEN), SetFill(0, 1), SetDataTip(STR_LIST_FILTER_TITLE, STR_NULL), + NWidget(WWT_EDITBOX, COLOUR_GREY, WID_BROS_FILTER_EDITBOX), SetFill(1, 0), SetResize(1, 0), + SetDataTip(STR_LIST_FILTER_OSKTITLE, STR_LIST_FILTER_TOOLTIP), + EndContainer(), + EndContainer(), + NWidget(NWID_SELECTION, INVALID_COLOUR, WID_BROS_SHOW_NEWST_ADDITIONS), + NWidget(NWID_HORIZONTAL), SetPadding(0, 5, 2, 0), + NWidget(WWT_MATRIX, COLOUR_GREY, WID_BROS_NEWST_LIST), SetMinimalSize(122, 71), SetFill(1, 0), + SetMatrixDataTip(1, 0, STR_STATION_BUILD_STATION_CLASS_TOOLTIP), SetScrollbar(WID_BROS_NEWST_SCROLL), + NWidget(NWID_VSCROLLBAR, COLOUR_GREY, WID_BROS_NEWST_SCROLL), + EndContainer(), + EndContainer(), + NWidget(NWID_SELECTION, INVALID_COLOUR, WID_BROS_SHOW_NEWST_ORIENTATION), + NWidget(WWT_LABEL, COLOUR_DARK_GREEN), SetMinimalSize(144, 11), SetDataTip(STR_STATION_BUILD_ORIENTATION, STR_NULL), SetPadding(4, 2, 1, 2), SetFill(1, 0), + EndContainer(), + NWidget(NWID_HORIZONTAL), SetPadding(3), + NWidget(NWID_SPACER), SetFill(1, 0), + NWidget(NWID_VERTICAL), SetPIP(0, 2, 0), + NWidget(NWID_HORIZONTAL), SetPIP(0, 2, 0), + NWidget(WWT_PANEL, COLOUR_GREY, WID_BROS_STATION_NW), SetMinimalSize(66, 50), SetFill(0, 0), EndContainer(), + NWidget(WWT_PANEL, COLOUR_GREY, WID_BROS_STATION_NE), SetMinimalSize(66, 50), SetFill(0, 0), EndContainer(), + NWidget(WWT_PANEL, COLOUR_GREY, WID_BROS_STATION_X), SetMinimalSize(66, 50), SetFill(0, 0), EndContainer(), + EndContainer(), + NWidget(NWID_HORIZONTAL), SetPIP(0, 2, 0), + NWidget(WWT_PANEL, COLOUR_GREY, WID_BROS_STATION_SW), SetMinimalSize(66, 50), SetFill(0, 0), EndContainer(), + NWidget(WWT_PANEL, COLOUR_GREY, WID_BROS_STATION_SE), SetMinimalSize(66, 50), SetFill(0, 0), EndContainer(), + NWidget(WWT_PANEL, COLOUR_GREY, WID_BROS_STATION_Y), SetMinimalSize(66, 50), SetFill(0, 0), EndContainer(), + EndContainer(), + EndContainer(), + NWidget(NWID_SPACER), SetFill(1, 0), EndContainer(), - NWidget(NWID_HORIZONTAL), SetPIP(0, 2, 0), - NWidget(WWT_PANEL, COLOUR_GREY, WID_BROS_STATION_SW), SetMinimalSize(66, 50), SetFill(0, 0), EndContainer(), - NWidget(WWT_PANEL, COLOUR_GREY, WID_BROS_STATION_SE), SetMinimalSize(66, 50), SetFill(0, 0), EndContainer(), - NWidget(WWT_PANEL, COLOUR_GREY, WID_BROS_STATION_Y), SetMinimalSize(66, 50), SetFill(0, 0), EndContainer(), + NWidget(NWID_SELECTION, INVALID_COLOUR, WID_BROS_SHOW_NEWST_TYPE_SEL), + NWidget(WWT_LABEL, COLOUR_DARK_GREEN, WID_BROS_SHOW_NEWST_TYPE), SetMinimalSize(144, 8), SetDataTip(STR_ORANGE_STRING, STR_NULL), SetPadding(4, 2, 4, 2), SetFill(1, 0), + EndContainer(), + NWidget(WWT_LABEL, COLOUR_DARK_GREEN), SetDataTip(STR_STATION_BUILD_COVERAGE_AREA_TITLE, STR_NULL), SetPadding(WidgetDimensions::unscaled.framerect), SetFill(1, 0), + NWidget(NWID_HORIZONTAL), + NWidget(NWID_SPACER), SetMinimalSize(2, 0), SetFill(1, 0), + NWidget(WWT_TEXTBTN, COLOUR_GREY, WID_BROS_LT_OFF), SetMinimalSize(60, 12), + SetDataTip(STR_STATION_BUILD_COVERAGE_OFF, STR_STATION_BUILD_COVERAGE_AREA_OFF_TOOLTIP), + NWidget(WWT_TEXTBTN, COLOUR_GREY, WID_BROS_LT_ON), SetMinimalSize(60, 12), + SetDataTip(STR_STATION_BUILD_COVERAGE_ON, STR_STATION_BUILD_COVERAGE_AREA_ON_TOOLTIP), + NWidget(NWID_SPACER), SetMinimalSize(2, 0), SetFill(1, 0), + EndContainer(), + EndContainer(), + NWidget(NWID_SELECTION, INVALID_COLOUR, WID_BROS_SHOW_NEWST_MATRIX), + NWidget(WWT_PANEL, COLOUR_DARK_GREEN), SetScrollbar(WID_BROS_MATRIX_SCROLL), + NWidget(NWID_HORIZONTAL), + NWidget(NWID_MATRIX, COLOUR_DARK_GREEN, WID_BROS_MATRIX), SetScrollbar(WID_BROS_MATRIX_SCROLL), SetPIP(0, 2, 0), + NWidget(WWT_PANEL, COLOUR_DARK_GREEN, WID_BROS_IMAGE), SetMinimalSize(66, 60), + SetFill(0, 0), SetResize(0, 0), SetDataTip(0x0, STR_STATION_BUILD_STATION_TYPE_TOOLTIP), SetScrollbar(WID_BROS_MATRIX_SCROLL), + EndContainer(), + EndContainer(), + NWidget(NWID_VSCROLLBAR, COLOUR_DARK_GREEN, WID_BROS_MATRIX_SCROLL), + EndContainer(), EndContainer(), EndContainer(), - NWidget(NWID_SPACER), SetFill(1, 0), EndContainer(), - NWidget(WWT_LABEL, COLOUR_DARK_GREEN, WID_BROS_INFO), SetPadding(WidgetDimensions::unscaled.framerect), SetDataTip(STR_STATION_BUILD_COVERAGE_AREA_TITLE, STR_NULL), SetFill(1, 0), - NWidget(NWID_HORIZONTAL), SetPadding(3), - NWidget(NWID_SPACER), SetFill(1, 0), - NWidget(WWT_TEXTBTN, COLOUR_GREY, WID_BROS_LT_OFF), SetMinimalSize(60, 12), - SetDataTip(STR_STATION_BUILD_COVERAGE_OFF, STR_STATION_BUILD_COVERAGE_AREA_OFF_TOOLTIP), - NWidget(WWT_TEXTBTN, COLOUR_GREY, WID_BROS_LT_ON), SetMinimalSize(60, 12), - SetDataTip(STR_STATION_BUILD_COVERAGE_ON, STR_STATION_BUILD_COVERAGE_AREA_ON_TOOLTIP), - NWidget(NWID_SPACER), SetFill(1, 0), + NWidget(NWID_HORIZONTAL), + NWidget(WWT_EMPTY, INVALID_COLOUR, WID_BROS_ACCEPTANCE), SetPadding(WidgetDimensions::unscaled.framerect), SetFill(1, 1), SetResize(1, 0), + NWidget(NWID_SELECTION, INVALID_COLOUR, WID_BROS_SHOW_NEWST_RESIZE), + NWidget(NWID_VERTICAL), + NWidget(WWT_PANEL, COLOUR_DARK_GREEN), SetFill(0, 1), EndContainer(), + NWidget(WWT_RESIZEBOX, COLOUR_DARK_GREEN), + EndContainer(), + EndContainer(), EndContainer(), - NWidget(WWT_EMPTY, COLOUR_DARK_GREEN, WID_BROS_ACCEPTANCE), SetPadding(WidgetDimensions::unscaled.framerect), SetResize(0, 1), EndContainer(), }; @@ -1210,28 +1702,76 @@ static const NWidgetPart _nested_tram_station_picker_widgets[] = { NWidget(NWID_HORIZONTAL), NWidget(WWT_CLOSEBOX, COLOUR_DARK_GREEN), NWidget(WWT_CAPTION, COLOUR_DARK_GREEN, WID_BROS_CAPTION), + NWidget(WWT_SHADEBOX, COLOUR_DARK_GREEN), + NWidget(NWID_SELECTION, INVALID_COLOUR, WID_BROS_SHOW_NEWST_DEFSIZE), + NWidget(WWT_DEFSIZEBOX, COLOUR_DARK_GREEN), + EndContainer(), EndContainer(), NWidget(WWT_PANEL, COLOUR_DARK_GREEN, WID_BROS_BACKGROUND), - NWidget(NWID_HORIZONTAL), SetPadding(3), - NWidget(NWID_SPACER), SetFill(1, 0), - NWidget(NWID_VERTICAL), SetPIP(0, 2, 0), - NWidget(NWID_HORIZONTAL), SetPIP(0, 2, 0), - NWidget(WWT_PANEL, COLOUR_GREY, WID_BROS_STATION_X), SetMinimalSize(66, 50), SetFill(0, 0), EndContainer(), - NWidget(WWT_PANEL, COLOUR_GREY, WID_BROS_STATION_Y), SetMinimalSize(66, 50), SetFill(0, 0), EndContainer(), + NWidget(NWID_HORIZONTAL), SetPadding(2, 0, 0, 2), + NWidget(NWID_VERTICAL), + NWidget(NWID_SELECTION, INVALID_COLOUR, WID_BROS_FILTER_CONTAINER), + NWidget(NWID_HORIZONTAL), SetPadding(0, 5, 2, 0), + NWidget(WWT_TEXT, COLOUR_DARK_GREEN), SetFill(0, 1), SetDataTip(STR_LIST_FILTER_TITLE, STR_NULL), + NWidget(WWT_EDITBOX, COLOUR_GREY, WID_BROS_FILTER_EDITBOX), SetFill(1, 0), SetResize(1, 0), + SetDataTip(STR_LIST_FILTER_OSKTITLE, STR_LIST_FILTER_TOOLTIP), + EndContainer(), + EndContainer(), + NWidget(NWID_SELECTION, INVALID_COLOUR, WID_BROS_SHOW_NEWST_ADDITIONS), + NWidget(NWID_HORIZONTAL), SetPadding(0, 5, 2, 0), + NWidget(WWT_MATRIX, COLOUR_GREY, WID_BROS_NEWST_LIST), SetMinimalSize(122, 71), SetFill(1, 0), + SetMatrixDataTip(1, 0, STR_STATION_BUILD_STATION_CLASS_TOOLTIP), SetScrollbar(WID_BROS_NEWST_SCROLL), + NWidget(NWID_VSCROLLBAR, COLOUR_GREY, WID_BROS_NEWST_SCROLL), + EndContainer(), + EndContainer(), + NWidget(NWID_SELECTION, INVALID_COLOUR, WID_BROS_SHOW_NEWST_ORIENTATION), + NWidget(WWT_LABEL, COLOUR_DARK_GREEN), SetMinimalSize(144, 11), SetDataTip(STR_STATION_BUILD_ORIENTATION, STR_NULL), SetPadding(4, 2, 1, 2), SetFill(1, 0), + EndContainer(), + NWidget(NWID_HORIZONTAL), SetPadding(3), + NWidget(NWID_SPACER), SetFill(1, 0), + NWidget(NWID_VERTICAL), SetPIP(0, 2, 0), + NWidget(NWID_HORIZONTAL), SetPIP(0, 2, 0), + NWidget(WWT_PANEL, COLOUR_GREY, WID_BROS_STATION_X), SetMinimalSize(66, 50), SetFill(0, 0), EndContainer(), + NWidget(WWT_PANEL, COLOUR_GREY, WID_BROS_STATION_Y), SetMinimalSize(66, 50), SetFill(0, 0), EndContainer(), + EndContainer(), + EndContainer(), + NWidget(NWID_SPACER), SetFill(1, 0), + EndContainer(), + NWidget(NWID_SELECTION, INVALID_COLOUR, WID_BROS_SHOW_NEWST_TYPE_SEL), + NWidget(WWT_LABEL, COLOUR_DARK_GREEN, WID_BROS_SHOW_NEWST_TYPE), SetMinimalSize(144, 8), SetDataTip(STR_ORANGE_STRING, STR_NULL), SetPadding(4, 2, 4, 2), SetFill(1, 0), + EndContainer(), + NWidget(WWT_LABEL, COLOUR_DARK_GREEN), SetDataTip(STR_STATION_BUILD_COVERAGE_AREA_TITLE, STR_NULL), SetPadding(WidgetDimensions::unscaled.framerect), SetFill(1, 0), + NWidget(NWID_HORIZONTAL), + NWidget(NWID_SPACER), SetMinimalSize(2, 0), SetFill(1, 0), + NWidget(WWT_TEXTBTN, COLOUR_GREY, WID_BROS_LT_OFF), SetMinimalSize(60, 12), + SetDataTip(STR_STATION_BUILD_COVERAGE_OFF, STR_STATION_BUILD_COVERAGE_AREA_OFF_TOOLTIP), + NWidget(WWT_TEXTBTN, COLOUR_GREY, WID_BROS_LT_ON), SetMinimalSize(60, 12), + SetDataTip(STR_STATION_BUILD_COVERAGE_ON, STR_STATION_BUILD_COVERAGE_AREA_ON_TOOLTIP), + NWidget(NWID_SPACER), SetMinimalSize(2, 0), SetFill(1, 0), + EndContainer(), + EndContainer(), + NWidget(NWID_SELECTION, INVALID_COLOUR, WID_BROS_SHOW_NEWST_MATRIX), + NWidget(WWT_PANEL, COLOUR_DARK_GREEN), SetScrollbar(WID_BROS_MATRIX_SCROLL), + NWidget(NWID_HORIZONTAL), + NWidget(NWID_MATRIX, COLOUR_DARK_GREEN, WID_BROS_MATRIX), SetScrollbar(WID_BROS_MATRIX_SCROLL), SetPIP(0, 2, 0), SetPadding(2, 0, 0, 0), + NWidget(WWT_PANEL, COLOUR_DARK_GREEN, WID_BROS_IMAGE), SetMinimalSize(66, 60), + SetFill(0, 0), SetResize(0, 0), SetDataTip(0x0, STR_STATION_BUILD_STATION_TYPE_TOOLTIP), SetScrollbar(WID_BROS_MATRIX_SCROLL), + EndContainer(), + EndContainer(), + NWidget(NWID_VSCROLLBAR, COLOUR_DARK_GREEN, WID_BROS_MATRIX_SCROLL), + EndContainer(), EndContainer(), EndContainer(), - NWidget(NWID_SPACER), SetFill(1, 0), EndContainer(), - NWidget(WWT_LABEL, COLOUR_DARK_GREEN, WID_BROS_INFO), SetPadding(WidgetDimensions::unscaled.framerect), SetMinimalSize(140, 14), SetDataTip(STR_STATION_BUILD_COVERAGE_AREA_TITLE, STR_NULL), SetFill(1, 0), - NWidget(NWID_HORIZONTAL), SetPadding(3), - NWidget(NWID_SPACER), SetFill(1, 0), - NWidget(WWT_TEXTBTN, COLOUR_GREY, WID_BROS_LT_OFF), SetMinimalSize(60, 12), - SetDataTip(STR_STATION_BUILD_COVERAGE_OFF, STR_STATION_BUILD_COVERAGE_AREA_OFF_TOOLTIP), - NWidget(WWT_TEXTBTN, COLOUR_GREY, WID_BROS_LT_ON), SetMinimalSize(60, 12), - SetDataTip(STR_STATION_BUILD_COVERAGE_ON, STR_STATION_BUILD_COVERAGE_AREA_ON_TOOLTIP), - NWidget(NWID_SPACER), SetFill(1, 0), + NWidget(NWID_HORIZONTAL), + NWidget(WWT_EMPTY, INVALID_COLOUR, WID_BROS_ACCEPTANCE), SetPadding(WidgetDimensions::unscaled.framerect), SetFill(1, 1), SetResize(1, 0), + NWidget(NWID_SELECTION, INVALID_COLOUR, WID_BROS_SHOW_NEWST_RESIZE), + NWidget(NWID_VERTICAL), + NWidget(WWT_PANEL, COLOUR_DARK_GREEN), SetFill(0, 1), EndContainer(), + NWidget(WWT_RESIZEBOX, COLOUR_DARK_GREEN), + EndContainer(), + EndContainer(), EndContainer(), - NWidget(WWT_EMPTY, COLOUR_DARK_GREEN, WID_BROS_ACCEPTANCE), SetPadding(WidgetDimensions::unscaled.framerect), SetResize(0, 1), EndContainer(), }; @@ -1250,7 +1790,7 @@ static void ShowRVStationPicker(Window *parent, RoadStopType rs) void InitializeRoadGui() { _road_depot_orientation = DIAGDIR_NW; - _road_station_picker_orientation = DIAGDIR_NW; + _roadstop_gui_settings.orientation = DIAGDIR_NW; } /** diff --git a/src/saveload/compat/station_sl_compat.h b/src/saveload/compat/station_sl_compat.h index 1c24a8d5d9..d94761506e 100644 --- a/src/saveload/compat/station_sl_compat.h +++ b/src/saveload/compat/station_sl_compat.h @@ -32,6 +32,12 @@ const SaveLoadCompat _station_spec_list_sl_compat[] = { SLC_VAR("localidx"), }; +/** Nominal field order for SlRoadStopSpecList. */ +const SaveLoadCompat _station_road_stop_spec_list_sl_compat[] = { + SLC_VAR("grfid"), + SLC_VAR("localidx"), +}; + /** Original field order for SlStationCargo. */ const SaveLoadCompat _station_cargo_sl_compat[] = { SLC_VAR("first"), diff --git a/src/saveload/saveload.h b/src/saveload/saveload.h index ad082ef590..de27caa8de 100644 --- a/src/saveload/saveload.h +++ b/src/saveload/saveload.h @@ -343,6 +343,7 @@ enum SaveLoadVersion : uint16 { SLV_U64_TICK_COUNTER, ///< 300 PR#10035 Make _tick_counter 64bit to avoid wrapping. SLV_LAST_LOADING_TICK, ///< 301 PR#9693 Store tick of last loading for vehicles. SLV_MULTITRACK_LEVEL_CROSSINGS, ///< 302 PR#9931 v13.0 Multi-track level crossings. + SLV_NEWGRF_ROAD_STOPS, ///< 303 PR#10144 NewGRF road stops. SL_MAX_VERSION, ///< Highest possible saveload version }; diff --git a/src/saveload/station_sl.cpp b/src/saveload/station_sl.cpp index 7a0b57d67d..f964a8b8cd 100644 --- a/src/saveload/station_sl.cpp +++ b/src/saveload/station_sl.cpp @@ -17,6 +17,7 @@ #include "../roadstop_base.h" #include "../vehicle_base.h" #include "../newgrf_station.h" +#include "../newgrf_roadstop.h" #include "table/strings.h" @@ -114,6 +115,11 @@ void AfterLoadStations() st->speclist[i].spec = StationClass::GetByGrf(st->speclist[i].grfid, st->speclist[i].localidx, nullptr); } + for (uint i = 0; i < st->roadstop_speclist.size(); i++) { + if (st->roadstop_speclist[i].grfid == 0) continue; + + st->roadstop_speclist[i].spec = RoadStopClass::GetByGrf(st->roadstop_speclist[i].grfid, st->roadstop_speclist[i].localidx, nullptr); + } if (Station::IsExpected(st)) { Station *sta = Station::From(st); @@ -122,6 +128,7 @@ void AfterLoadStations() } StationUpdateCachedTriggers(st); + RoadStopUpdateCachedTriggers(st); } } @@ -224,6 +231,33 @@ public: uint8 SlStationSpecList::last_num_specs; +class SlRoadStopSpecList : public DefaultSaveLoadHandler { +public: + inline static const SaveLoad description[] = { + SLE_VAR(RoadStopSpecList, grfid, SLE_UINT32), + SLE_VAR(RoadStopSpecList, localidx, SLE_UINT8), + }; + inline const static SaveLoadCompatTable compat_description = _station_road_stop_spec_list_sl_compat; + + void Save(BaseStation *bst) const override + { + SlSetStructListLength(bst->roadstop_speclist.size()); + for (uint i = 0; i < bst->roadstop_speclist.size(); i++) { + SlObject(&bst->roadstop_speclist[i], this->GetDescription()); + } + } + + void Load(BaseStation *bst) const override + { + uint8 num_specs = (uint8)SlGetStructListLength(UINT8_MAX); + + bst->roadstop_speclist.resize(num_specs); + for (uint i = 0; i < num_specs; i++) { + SlObject(&bst->roadstop_speclist[i], this->GetLoadDescription()); + } + } +}; + class SlStationCargo : public DefaultSaveLoadHandler { public: inline static const SaveLoad description[] = { @@ -516,6 +550,35 @@ struct STNSChunkHandler : ChunkHandler { } }; +class SlRoadStopTileData : public DefaultSaveLoadHandler { +public: + inline static const SaveLoad description[] = { + SLE_VAR(RoadStopTileData, tile, SLE_UINT32), + SLE_VAR(RoadStopTileData, random_bits, SLE_UINT8), + SLE_VAR(RoadStopTileData, animation_frame, SLE_UINT8), + }; + inline const static SaveLoadCompatTable compat_description = {}; + + static uint8 last_num_specs; ///< Number of specs of the last loaded station. + + void Save(BaseStation *bst) const override + { + SlSetStructListLength(bst->custom_roadstop_tile_data.size()); + for (uint i = 0; i < bst->custom_roadstop_tile_data.size(); i++) { + SlObject(&bst->custom_roadstop_tile_data[i], this->GetDescription()); + } + } + + void Load(BaseStation *bst) const override + { + uint32 num_tiles = (uint32)SlGetStructListLength(UINT32_MAX); + bst->custom_roadstop_tile_data.resize(num_tiles); + for (uint i = 0; i < num_tiles; i++) { + SlObject(&bst->custom_roadstop_tile_data[i], this->GetLoadDescription()); + } + } +}; + /** * SaveLoad handler for the BaseStation, which all other stations / waypoints * make use of. @@ -593,6 +656,7 @@ public: SLE_REFLIST(Station, loading_vehicles, REF_VEHICLE), SLE_CONDVAR(Station, always_accepted, SLE_FILE_U32 | SLE_VAR_U64, SLV_127, SLV_EXTEND_CARGOTYPES), SLE_CONDVAR(Station, always_accepted, SLE_UINT64, SLV_EXTEND_CARGOTYPES, SL_MAX_VERSION), + SLEG_CONDSTRUCTLIST("speclist", SlRoadStopTileData, SLV_NEWGRF_ROAD_STOPS, SL_MAX_VERSION), SLEG_STRUCTLIST("goods", SlStationGoods), }; inline const static SaveLoadCompatTable compat_description = _station_normal_sl_compat; @@ -652,6 +716,7 @@ static const SaveLoad _station_desc[] = { SLEG_STRUCT("normal", SlStationNormal), SLEG_STRUCT("waypoint", SlStationWaypoint), SLEG_CONDSTRUCTLIST("speclist", SlStationSpecList, SLV_27, SL_MAX_VERSION), + SLEG_CONDSTRUCTLIST("roadstopspeclist", SlRoadStopSpecList, SLV_NEWGRF_ROAD_STOPS, SL_MAX_VERSION), }; struct STNNChunkHandler : ChunkHandler { diff --git a/src/script/api/script_road.cpp b/src/script/api/script_road.cpp index 1cb01327c4..6ee2a41158 100644 --- a/src/script/api/script_road.cpp +++ b/src/script/api/script_road.cpp @@ -15,6 +15,7 @@ #include "../../landscape_cmd.h" #include "../../road_cmd.h" #include "../../station_cmd.h" +#include "../../newgrf_roadstop.h" #include "../../script/squirrel_helper_type.hpp" #include "../../safeguards.h" @@ -549,7 +550,7 @@ static bool NeighbourHasReachableRoad(::RoadType rt, TileIndex start_tile, DiagD DiagDirection entrance_dir = DiagdirBetweenTiles(tile, front); RoadStopType stop_type = road_veh_type == ROADVEHTYPE_TRUCK ? ROADSTOP_TRUCK : ROADSTOP_BUS; StationID to_join = ScriptStation::IsValidStation(station_id) ? station_id : INVALID_STATION; - return ScriptObject::Command::Do(tile, 1, 1, stop_type, drive_through, entrance_dir, ScriptObject::GetRoadType(), to_join, station_id != ScriptStation::STATION_JOIN_ADJACENT); + return ScriptObject::Command::Do(tile, 1, 1, stop_type, drive_through, entrance_dir, ScriptObject::GetRoadType(), ROADSTOP_CLASS_DFLT, 0, to_join, station_id != ScriptStation::STATION_JOIN_ADJACENT); } /* static */ bool ScriptRoad::BuildRoadStation(TileIndex tile, TileIndex front, RoadVehicleType road_veh_type, StationID station_id) diff --git a/src/station.cpp b/src/station.cpp index 5230eeeaf5..aecd019901 100644 --- a/src/station.cpp +++ b/src/station.cpp @@ -170,6 +170,36 @@ void BaseStation::PostDestructor(size_t index) InvalidateWindowData(WC_SELECT_STATION, 0, 0); } +void BaseStation::SetRoadStopTileData(TileIndex tile, byte data, bool animation) +{ + for (RoadStopTileData &tile_data : this->custom_roadstop_tile_data) { + if (tile_data.tile == tile) { + if (animation) { + tile_data.animation_frame = data; + } else { + tile_data.random_bits = data; + } + return; + } + } + RoadStopTileData tile_data; + tile_data.tile = tile; + tile_data.animation_frame = animation ? data : 0; + tile_data.random_bits = animation ? 0 : data; + this->custom_roadstop_tile_data.push_back(tile_data); +} + +void BaseStation::RemoveRoadStopTileData(TileIndex tile) +{ + for (RoadStopTileData &tile_data : this->custom_roadstop_tile_data) { + if (tile_data.tile == tile) { + tile_data = this->custom_roadstop_tile_data.back(); + this->custom_roadstop_tile_data.pop_back(); + return; + } + } +} + /** * Get the primary road stop (the first road stop) that the given vehicle can load/unload. * @param v the vehicle to get the first road stop for diff --git a/src/station_base.h b/src/station_base.h index e8eb40d4b8..b2e31a29de 100644 --- a/src/station_base.h +++ b/src/station_base.h @@ -521,6 +521,11 @@ public: return IsRailStationTile(tile) && GetStationIndex(tile) == this->index; } + inline bool TileBelongsToRoadStop(TileIndex tile) const + { + return IsRoadStopTile(tile) && GetStationIndex(tile) == this->index; + } + inline bool TileBelongsToAirport(TileIndex tile) const { return IsAirportTile(tile) && GetStationIndex(tile) == this->index; diff --git a/src/station_cmd.cpp b/src/station_cmd.cpp index a4479deca4..414c09812d 100644 --- a/src/station_cmd.cpp +++ b/src/station_cmd.cpp @@ -59,6 +59,7 @@ #include "waypoint_cmd.h" #include "landscape_cmd.h" #include "rail_cmd.h" +#include "newgrf_roadstop.h" #include "table/strings.h" @@ -1794,7 +1795,7 @@ static RoadStop **FindRoadStopSpot(bool truck_station, Station *st) } } -static CommandCost RemoveRoadStop(TileIndex tile, DoCommandFlag flags); +static CommandCost RemoveRoadStop(TileIndex tile, DoCommandFlag flags, int replacement_spec_index = -1); /** * Find a nearby station that joins this road stop. @@ -1820,17 +1821,31 @@ static CommandCost FindJoiningRoadStop(StationID existing_stop, StationID statio * @param is_drive_through False for normal stops, true for drive-through. * @param ddir Entrance direction (#DiagDirection) for normal stops. Converted to the axis for drive-through stops. * @param rt The roadtype. + * @param spec_class Road stop spec class. + * @param spec_index Road stop spec index. * @param station_to_join Station ID to join (NEW_STATION if build new one). * @param adjacent Allow stations directly adjacent to other stations. * @return The cost of this operation or an error. */ -CommandCost CmdBuildRoadStop(DoCommandFlag flags, TileIndex tile, uint8 width, uint8 length, RoadStopType stop_type, bool is_drive_through, DiagDirection ddir, RoadType rt, StationID station_to_join, bool adjacent) +CommandCost CmdBuildRoadStop(DoCommandFlag flags, TileIndex tile, uint8 width, uint8 length, RoadStopType stop_type, bool is_drive_through, + DiagDirection ddir, RoadType rt, RoadStopClassID spec_class, byte spec_index, StationID station_to_join, bool adjacent) { if (!ValParamRoadType(rt) || !IsValidDiagDirection(ddir) || stop_type >= ROADSTOP_END) return CMD_ERROR; bool reuse = (station_to_join != NEW_STATION); if (!reuse) station_to_join = INVALID_STATION; bool distant_join = (station_to_join != INVALID_STATION); + /* Check if the given station class is valid */ + if ((uint)spec_class >= RoadStopClass::GetClassCount() || spec_class == ROADSTOP_CLASS_WAYP) return CMD_ERROR; + if (spec_index >= RoadStopClass::Get(spec_class)->GetSpecCount()) return CMD_ERROR; + + const RoadStopSpec *roadstopspec = RoadStopClass::Get(spec_class)->GetSpec(spec_index); + if (roadstopspec != nullptr) { + if (stop_type == ROADSTOP_TRUCK && roadstopspec->stop_type != ROADSTOPTYPE_FREIGHT && roadstopspec->stop_type != ROADSTOPTYPE_ALL) return CMD_ERROR; + if (stop_type == ROADSTOP_BUS && roadstopspec->stop_type != ROADSTOPTYPE_PASSENGER && roadstopspec->stop_type != ROADSTOPTYPE_ALL) return CMD_ERROR; + if (!is_drive_through && HasBit(roadstopspec->flags, RSF_DRIVE_THROUGH_ONLY)) return CMD_ERROR; + } + /* Check if the requested road stop is too big */ if (width > _settings_game.station.station_spread || length > _settings_game.station.station_spread) return_cmd_error(STR_ERROR_STATION_TOO_SPREAD_OUT); /* Check for incorrect width / length. */ @@ -1853,7 +1868,13 @@ CommandCost CmdBuildRoadStop(DoCommandFlag flags, TileIndex tile, uint8 width, u bool is_truck_stop = stop_type != ROADSTOP_BUS; /* Total road stop cost. */ - CommandCost cost(EXPENSES_CONSTRUCTION, roadstop_area.w * roadstop_area.h * _price[is_truck_stop ? PR_BUILD_STATION_TRUCK : PR_BUILD_STATION_BUS]); + Money unit_cost; + if (roadstopspec != nullptr) { + unit_cost = roadstopspec->GetBuildCost(is_truck_stop ? PR_BUILD_STATION_TRUCK : PR_BUILD_STATION_BUS); + } else { + unit_cost = _price[is_truck_stop ? PR_BUILD_STATION_TRUCK : PR_BUILD_STATION_BUS]; + } + CommandCost cost(EXPENSES_CONSTRUCTION, roadstop_area.w * roadstop_area.h * unit_cost); StationID est = INVALID_STATION; ret = CheckFlatLandRoadStop(roadstop_area, flags, is_drive_through ? 5 << axis : 1 << ddir, is_drive_through, is_truck_stop, axis, &est, rt); if (ret.Failed()) return ret; @@ -1869,6 +1890,20 @@ CommandCost CmdBuildRoadStop(DoCommandFlag flags, TileIndex tile, uint8 width, u ret = BuildStationPart(&st, flags, reuse, roadstop_area, STATIONNAMING_ROAD); if (ret.Failed()) return ret; + /* Check if we can allocate a custom stationspec to this station */ + int specindex = AllocateSpecToRoadStop(roadstopspec, st, (flags & DC_EXEC) != 0); + if (specindex == -1) return_cmd_error(STR_ERROR_TOO_MANY_STATION_SPECS); + + if (roadstopspec != nullptr) { + /* Perform NewGRF checks */ + + /* Check if the road stop is buildable */ + if (HasBit(roadstopspec->callback_mask, CBM_ROAD_STOP_AVAIL)) { + uint16 cb_res = GetRoadStopCallback(CBID_STATION_AVAILABILITY, 0, 0, roadstopspec, nullptr, INVALID_TILE, rt, is_truck_stop ? STATION_TRUCK : STATION_BUS, 0); + if (cb_res != CALLBACK_FAILED && !Convert8bitBooleanCallback(roadstopspec->grf_prop.grffile, CBID_STATION_AVAILABILITY, cb_res)) return CMD_ERROR; + } + } + if (flags & DC_EXEC) { /* Check every tile in the area. */ for (TileIndex cur_tile : roadstop_area) { @@ -1879,7 +1914,13 @@ CommandCost CmdBuildRoadStop(DoCommandFlag flags, TileIndex tile, uint8 width, u Owner tram_owner = tram_rt != INVALID_ROADTYPE ? GetRoadOwner(cur_tile, RTT_TRAM) : _current_company; if (IsTileType(cur_tile, MP_STATION) && IsRoadStop(cur_tile)) { - RemoveRoadStop(cur_tile, flags); + RemoveRoadStop(cur_tile, flags, specindex); + } + + if (roadstopspec != nullptr) { + /* Include this road stop spec's animation trigger bitmask + * in the station's cached copy. */ + st->cached_roadstop_anim_triggers |= roadstopspec->animation.triggers; } RoadStop *road_stop = new RoadStop(cur_tile); @@ -1921,6 +1962,15 @@ CommandCost CmdBuildRoadStop(DoCommandFlag flags, TileIndex tile, uint8 width, u UpdateCompanyRoadInfrastructure(tram_rt, tram_owner, ROAD_STOP_TRACKBIT_FACTOR); Company::Get(st->owner)->infrastructure.station++; + UpdateCompanyRoadInfrastructure(road_rt, road_owner, ROAD_STOP_TRACKBIT_FACTOR); + UpdateCompanyRoadInfrastructure(tram_rt, tram_owner, ROAD_STOP_TRACKBIT_FACTOR); + + SetCustomRoadStopSpecIndex(cur_tile, specindex); + if (roadstopspec != nullptr) { + st->SetRoadStopRandomBits(cur_tile, GB(Random(), 0, 8)); + TriggerRoadStopAnimation(st, cur_tile, SAT_BUILT); + } + MarkTileDirtyByTile(cur_tile); } @@ -1953,9 +2003,10 @@ static Vehicle *ClearRoadStopStatusEnum(Vehicle *v, void *) * Remove a bus station/truck stop * @param tile TileIndex been queried * @param flags operation to perform + * @param replacement_spec_index replacement spec index to avoid deallocating, if < 0, tile is not being replaced * @return cost or failure of operation */ -static CommandCost RemoveRoadStop(TileIndex tile, DoCommandFlag flags) +static CommandCost RemoveRoadStop(TileIndex tile, DoCommandFlag flags, int replacement_spec_index) { Station *st = Station::GetByTile(tile); @@ -1987,6 +2038,8 @@ static CommandCost RemoveRoadStop(TileIndex tile, DoCommandFlag flags) if (ret.Failed()) return ret; } + const RoadStopSpec *spec = GetRoadStopSpec(tile); + if (flags & DC_EXEC) { if (*primary_stop == cur_stop) { /* removed the first stop in the list */ @@ -2011,6 +2064,12 @@ static CommandCost RemoveRoadStop(TileIndex tile, DoCommandFlag flags) Company::Get(st->owner)->infrastructure.station--; DirtyCompanyInfrastructureWindows(st->owner); + DeleteAnimatedTile(tile); + + uint specindex = GetCustomRoadStopSpecIndex(tile); + + DeleteNewGRFInspectWindow(GSF_ROADSTOPS, tile); + if (IsDriveThroughStopTile(tile)) { /* Clears the tile for us */ cur_stop->ClearDriveThrough(); @@ -2030,7 +2089,10 @@ static CommandCost RemoveRoadStop(TileIndex tile, DoCommandFlag flags) st->rect.AfterRemoveTile(st, tile); - st->AfterStationTileSetChange(false, is_truck ? STATION_TRUCK: STATION_BUS); + if (replacement_spec_index < 0) st->AfterStationTileSetChange(false, is_truck ? STATION_TRUCK: STATION_BUS); + + st->RemoveRoadStopTileData(tile); + if ((int)specindex != replacement_spec_index) DeallocateSpecFromRoadStop(st, specindex); /* Update the tile area of the truck/bus stop */ if (is_truck) { @@ -2042,7 +2104,8 @@ static CommandCost RemoveRoadStop(TileIndex tile, DoCommandFlag flags) } } - return CommandCost(EXPENSES_CONSTRUCTION, _price[is_truck ? PR_CLEAR_STATION_TRUCK : PR_CLEAR_STATION_BUS]); + Price category = is_truck ? PR_CLEAR_STATION_TRUCK : PR_CLEAR_STATION_BUS; + return CommandCost(EXPENSES_CONSTRUCTION, spec != nullptr ? spec->GetClearCost(category) : _price[category]); } /** @@ -2932,6 +2995,8 @@ draw_default_foundation: } } + bool draw_ground = false; + if (IsBuoy(ti->tile)) { DrawWaterClassGround(ti); SpriteID sprite = GetCanalSprite(CF_BUOY, ti->tile); @@ -2970,6 +3035,10 @@ draw_default_foundation: ground_relocation += rti->fallback_railtype; } + draw_ground = true; + } + + if (draw_ground && !IsRoadStop(ti->tile)) { SpriteID image = t->ground.sprite; PaletteID pal = t->ground.pal; RailTrackOffset overlay_offset; @@ -3002,8 +3071,32 @@ draw_default_foundation: const RoadTypeInfo* road_rti = road_rt == INVALID_ROADTYPE ? nullptr : GetRoadTypeInfo(road_rt); const RoadTypeInfo* tram_rti = tram_rt == INVALID_ROADTYPE ? nullptr : GetRoadTypeInfo(tram_rt); + Axis axis = GetRoadStopDir(ti->tile) == DIAGDIR_NE ? AXIS_X : AXIS_Y; + DiagDirection dir = GetRoadStopDir(ti->tile); + StationType type = GetStationType(ti->tile); + + const RoadStopSpec *stopspec = GetRoadStopSpec(ti->tile); + if (stopspec != nullptr) { + int view = dir; + if (IsDriveThroughStopTile(ti->tile)) view += 4; + st = BaseStation::GetByTile(ti->tile); + RoadStopResolverObject object(stopspec, st, ti->tile, INVALID_ROADTYPE, type, view); + const SpriteGroup *group = object.Resolve(); + if (group != nullptr && group->type == SGT_TILELAYOUT) { + t = ((const TileLayoutSpriteGroup *)group)->ProcessRegisters(nullptr); + } + } + + /* Draw ground sprite */ + if (draw_ground) { + SpriteID image = t->ground.sprite; + PaletteID pal = t->ground.pal; + image += HasBit(image, SPRITE_MODIFIER_CUSTOM_SPRITE) ? ground_relocation : total_offset; + if (HasBit(pal, SPRITE_MODIFIER_CUSTOM_SPRITE)) pal += ground_relocation; + DrawGroundSprite(image, GroundSpritePaletteTransform(image, pal, palette)); + } + if (IsDriveThroughStopTile(ti->tile)) { - Axis axis = GetRoadStopDir(ti->tile) == DIAGDIR_NE ? AXIS_X : AXIS_Y; uint sprite_offset = axis == AXIS_X ? 1 : 0; DrawRoadOverlays(ti, PAL_NONE, road_rti, tram_rti, sprite_offset, sprite_offset); @@ -3011,15 +3104,16 @@ draw_default_foundation: /* Non-drivethrough road stops are only valid for roads. */ assert(road_rt != INVALID_ROADTYPE && tram_rt == INVALID_ROADTYPE); - if (road_rti->UsesOverlay()) { - DiagDirection dir = GetRoadStopDir(ti->tile); + if ((stopspec == nullptr || (stopspec->draw_mode & ROADSTOP_DRAW_MODE_ROAD) != 0) && road_rti->UsesOverlay()) { SpriteID ground = GetCustomRoadSprite(road_rti, ti->tile, ROTSG_ROADSTOP); DrawGroundSprite(ground + dir, PAL_NONE); } } - /* Draw road, tram catenary */ - DrawRoadCatenary(ti); + if (stopspec == nullptr || !HasBit(stopspec->flags, RSF_NO_CATENARY)) { + /* Draw road, tram catenary */ + DrawRoadCatenary(ti); + } } if (IsRailWaypoint(ti->tile)) { @@ -3278,6 +3372,12 @@ static void AnimateTile_Station(TileIndex tile) if (IsAirport(tile)) { AnimateAirportTile(tile); + return; + } + + if (IsRoadStopTile(tile)) { + AnimateRoadStopTile(tile); + return; } } @@ -3805,6 +3905,7 @@ void OnTick_Station() /* Stop processing this station if it was deleted */ if (!StationHandleBigTick(st)) continue; TriggerStationAnimation(st, st->xy, SAT_250_TICKS); + TriggerRoadStopAnimation(st, st->xy, SAT_250_TICKS); if (Station::IsExpected(st)) AirportAnimationTrigger(Station::From(st), AAT_STATION_250_TICKS); } } @@ -3877,6 +3978,9 @@ static uint UpdateStationWaiting(Station *st, CargoID type, uint amount, SourceT TriggerStationRandomisation(st, st->xy, SRT_NEW_CARGO, type); TriggerStationAnimation(st, st->xy, SAT_NEW_CARGO, type); AirportAnimationTrigger(st, AAT_STATION_NEW_CARGO, type); + TriggerRoadStopRandomisation(st, st->xy, RSRT_NEW_CARGO, type); + TriggerRoadStopAnimation(st, st->xy, SAT_NEW_CARGO, type); + SetWindowDirty(WC_STATION_VIEW, st->index); st->MarkTilesDirty(true); diff --git a/src/station_cmd.h b/src/station_cmd.h index 84e17ed59f..50a08bbd24 100644 --- a/src/station_cmd.h +++ b/src/station_cmd.h @@ -14,6 +14,7 @@ #include "station_type.h" enum StationClassID : byte; +enum RoadStopClassID : byte; extern Town *AirportGetNearestTown(const struct AirportSpec *as, const TileIterator &it, uint &mindist); extern uint8 GetAirportNoiseLevelForDistance(const struct AirportSpec *as, uint distance); @@ -22,7 +23,7 @@ CommandCost CmdBuildAirport(DoCommandFlag flags, TileIndex tile, byte airport_ty CommandCost CmdBuildDock(DoCommandFlag flags, TileIndex tile, StationID station_to_join, bool adjacent); CommandCost CmdBuildRailStation(DoCommandFlag flags, TileIndex tile_org, RailType rt, Axis axis, byte numtracks, byte plat_len, StationClassID spec_class, byte spec_index, StationID station_to_join, bool adjacent); CommandCost CmdRemoveFromRailStation(DoCommandFlag flags, TileIndex start, TileIndex end, bool keep_rail); -CommandCost CmdBuildRoadStop(DoCommandFlag flags, TileIndex tile, uint8 width, uint8 length, RoadStopType stop_type, bool is_drive_through, DiagDirection ddir, RoadType rt, StationID station_to_join, bool adjacent); +CommandCost CmdBuildRoadStop(DoCommandFlag flags, TileIndex tile, uint8 width, uint8 length, RoadStopType stop_type, bool is_drive_through, DiagDirection ddir, RoadType rt, RoadStopClassID spec_class, byte spec_index, StationID station_to_join, bool adjacent); CommandCost CmdRemoveRoadStop(DoCommandFlag flags, TileIndex tile, uint8 width, uint8 height, RoadStopType stop_type, bool remove_road); CommandCost CmdRenameStation(DoCommandFlag flags, StationID station_id, const std::string &text); CommandCost CmdOpenCloseAirport(DoCommandFlag flags, StationID station_id); diff --git a/src/station_map.h b/src/station_map.h index d38ffe337f..201d3554aa 100644 --- a/src/station_map.h +++ b/src/station_map.h @@ -497,6 +497,42 @@ static inline uint GetCustomStationSpecIndex(TileIndex t) return _m[t].m4; } +/** + * Is there a custom road stop spec on this tile? + * @param t Tile to query + * @pre IsRoadStopTile(t) + * @return True if this station is part of a newgrf station. + */ +static inline bool IsCustomRoadStopSpecIndex(TileIndex t) +{ + assert(IsRoadStopTile(t)); + return GB(_me[t].m8, 0, 6) != 0; +} + +/** + * Set the custom road stop spec for this tile. + * @param t Tile to set the stationspec of. + * @param specindex The new spec. + * @pre IsRoadStopTile(t) + */ +static inline void SetCustomRoadStopSpecIndex(TileIndex t, byte specindex) +{ + assert(IsRoadStopTile(t)); + SB(_me[t].m8, 0, 6, specindex); +} + +/** + * Get the custom road stop spec for this tile. + * @param t Tile to query + * @pre IsRoadStopTile(t) + * @return The custom station spec of this tile. + */ +static inline uint GetCustomRoadStopSpecIndex(TileIndex t) +{ + assert(IsRoadStopTile(t)); + return GB(_me[t].m8, 0, 6); +} + /** * Set the random bits for a station tile. * @param t Tile to set random bits for. diff --git a/src/table/newgrf_debug_data.h b/src/table/newgrf_debug_data.h index f21d2df4d4..b99a11c4df 100644 --- a/src/table/newgrf_debug_data.h +++ b/src/table/newgrf_debug_data.h @@ -10,6 +10,7 @@ #include "../newgrf_house.h" #include "../newgrf_engine.h" #include "../newgrf_roadtype.h" +#include "../newgrf_roadstop.h" /* Helper for filling property tables */ #define NIP(prop, base, variable, type, name) { name, [] (const void *b) -> const void * { return std::addressof(static_cast(b)->variable); }, cpp_sizeof(base, variable), prop, type } @@ -607,6 +608,64 @@ static const NIFeature _nif_tramtype = { new NIHRoadType(), }; +#define NICRS(cb_id, bit) NIC(cb_id, RoadStopSpec, callback_mask, bit) +static const NICallback _nic_roadstops[] = { + NICRS(CBID_STATION_AVAILABILITY, CBM_ROAD_STOP_AVAIL), + NICRS(CBID_STATION_ANIM_START_STOP, CBM_NO_BIT), + NICRS(CBID_STATION_ANIM_NEXT_FRAME, CBM_ROAD_STOP_ANIMATION_NEXT_FRAME), + NICRS(CBID_STATION_ANIMATION_SPEED, CBM_ROAD_STOP_ANIMATION_SPEED), + NIC_END() +}; + +static const NIVariable _nif_roadstops[] = { + NIV(0x40, "view/rotation"), + NIV(0x41, "stop type"), + NIV(0x42, "terrain type"), + NIV(0x43, "road type"), + NIV(0x44, "tram type"), + NIV(0x45, "town zone and Manhattan distance of town"), + NIV(0x46, "square of Euclidean distance of town"), + NIV(0x47, "player info"), + NIV(0x48, "bitmask of accepted cargoes"), + NIV(0x49, "current animation frame"), + NIV(0x60, "amount of cargo waiting"), + NIV(0x61, "time since last cargo pickup"), + NIV(0x62, "rating of cargo"), + NIV(0x63, "time spent on route"), + NIV(0x64, "information about last vehicle picking cargo up"), + NIV(0x65, "amount of cargo acceptance"), + NIV(0x66, "animation frame of nearby tile"), + NIV(0x67, "land info of nearby tiles"), + NIV(0x68, "road stop info of nearby tiles"), + NIV(0x69, "information about cargo accepted in the past"), + NIV(0x6A, "GRFID of nearby road stop tiles"), + NIV_END(), +}; + +class NIHRoadStop : public NIHelper { + bool IsInspectable(uint index) const override { return GetRoadStopSpec(index) != nullptr; } + uint GetParent(uint index) const override { return GetInspectWindowNumber(GSF_FAKE_TOWNS, BaseStation::GetByTile(index)->town->index); } + const void *GetInstance(uint index)const override { return nullptr; } + const void *GetSpec(uint index) const override { return GetRoadStopSpec(index); } + void SetStringParameters(uint index) const override { this->SetObjectAtStringParameters(STR_STATION_NAME, GetStationIndex(index), index); } + uint32 GetGRFID(uint index) const override { return (this->IsInspectable(index)) ? GetRoadStopSpec(index)->grf_prop.grffile->grfid : 0; } + + uint Resolve(uint index, uint var, uint32 param, bool *avail) const override + { + int view = GetRoadStopDir(index); + if (IsDriveThroughStopTile(index)) view += 4; + RoadStopResolverObject ro(GetRoadStopSpec(index), BaseStation::GetByTile(index), index, INVALID_ROADTYPE, GetStationType(index), view); + return ro.GetScope(VSG_SCOPE_SELF)->GetVariable(var, param, avail); + } +}; + +static const NIFeature _nif_roadstop = { + nullptr, + _nic_roadstops, + _nif_roadstops, + new NIHRoadStop(), +}; + /** Table with all NIFeatures. */ static const NIFeature * const _nifeatures[] = { &_nif_vehicle, // GSF_TRAINS @@ -629,6 +688,7 @@ static const NIFeature * const _nifeatures[] = { &_nif_airporttile, // GSF_AIRPORTTILES &_nif_roadtype, // GSF_ROADTYPES &_nif_tramtype, // GSF_TRAMTYPES + &_nif_roadstop, // GSF_ROADSTOPS &_nif_town, // GSF_FAKE_TOWNS }; static_assert(lengthof(_nifeatures) == GSF_FAKE_END); diff --git a/src/vehicle.cpp b/src/vehicle.cpp index 937625e884..678b7db0ac 100644 --- a/src/vehicle.cpp +++ b/src/vehicle.cpp @@ -55,6 +55,7 @@ #include "misc_cmd.h" #include "train_cmd.h" #include "vehicle_cmd.h" +#include "newgrf_roadstop.h" #include "table/strings.h" @@ -2303,6 +2304,14 @@ void Vehicle::LeaveStation() SetBit(Train::From(this)->flags, VRF_LEAVING_STATION); } + if (this->type == VEH_ROAD && !(this->vehstatus & VS_CRASHED)) { + /* Trigger road stop animation */ + if (IsRoadStopTile(this->tile)) { + TriggerRoadStopRandomisation(st, this->tile, RSRT_VEH_DEPARTS); + TriggerRoadStopAnimation(st, this->tile, SAT_TRAIN_DEPARTS); + } + } + this->MarkDirty(); } diff --git a/src/widgets/road_widget.h b/src/widgets/road_widget.h index 425031e05b..fa23631ba1 100644 --- a/src/widgets/road_widget.h +++ b/src/widgets/road_widget.h @@ -41,18 +41,31 @@ enum BuildRoadDepotWidgets { /** Widgets of the #BuildRoadStationWindow class. */ enum BuildRoadStationWidgets { /* Name starts with BRO instead of BR, because of collision with BuildRailStationWidgets */ - WID_BROS_CAPTION, ///< Caption of the window. - WID_BROS_BACKGROUND, ///< Background of the window. - WID_BROS_STATION_NE, ///< Terminal station with NE entry. - WID_BROS_STATION_SE, ///< Terminal station with SE entry. - WID_BROS_STATION_SW, ///< Terminal station with SW entry. - WID_BROS_STATION_NW, ///< Terminal station with NW entry. - WID_BROS_STATION_X, ///< Drive-through station in x-direction. - WID_BROS_STATION_Y, ///< Drive-through station in y-direction. - WID_BROS_LT_OFF, ///< Turn off area highlight. - WID_BROS_LT_ON, ///< Turn on area highlight. - WID_BROS_INFO, ///< Station acceptance toggle. - WID_BROS_ACCEPTANCE, ///< Station acceptance. + WID_BROS_CAPTION, ///< Caption of the window. + WID_BROS_BACKGROUND, ///< Background of the window. + WID_BROS_STATION_NE, ///< Terminal station with NE entry. + WID_BROS_STATION_SE, ///< Terminal station with SE entry. + WID_BROS_STATION_SW, ///< Terminal station with SW entry. + WID_BROS_STATION_NW, ///< Terminal station with NW entry. + WID_BROS_STATION_X, ///< Drive-through station in x-direction. + WID_BROS_STATION_Y, ///< Drive-through station in y-direction. + WID_BROS_LT_OFF, ///< Turn off area highlight. + WID_BROS_LT_ON, ///< Turn on area highlight. + WID_BROS_ACCEPTANCE, ///< Station acceptance info. + WID_BROS_MATRIX, ///< Matrix widget displaying all available road stops. + WID_BROS_IMAGE, ///< Panel used for each image of the matrix. + WID_BROS_MATRIX_SCROLL, ///< Scrollbar of the #WID_BROS_SHOW_NEWST_ADDITIONS. + WID_BROS_FILTER_CONTAINER, ///< Container for the filter text box for the road stop class list. + WID_BROS_FILTER_EDITBOX, ///< Filter text box for the road stop class list. + WID_BROS_SHOW_NEWST_DEFSIZE, ///< Selection for default-size button for new road stops. + WID_BROS_SHOW_NEWST_ADDITIONS, ///< Selection for new class selection list. + WID_BROS_SHOW_NEWST_MATRIX, ///< Selection for new stop image matrix. + WID_BROS_SHOW_NEWST_RESIZE, ///< Selection for panel and resize at bottom right for new stops. + WID_BROS_SHOW_NEWST_ORIENTATION, ///< Selection for the orientation string for new stops. + WID_BROS_SHOW_NEWST_TYPE_SEL, ///< Selection for the type name. + WID_BROS_SHOW_NEWST_TYPE, ///< Display of selected stop type. + WID_BROS_NEWST_LIST, ///< List with new road stops. + WID_BROS_NEWST_SCROLL, ///< Scrollbar of the #WID_BROS_NEWST_LIST. }; #endif /* WIDGETS_ROAD_WIDGET_H */ From 2fee8ecdda20a08e9f3039132f7458b0debfe74a Mon Sep 17 00:00:00 2001 From: Niels Martin Hansen Date: Sun, 8 Sep 2019 10:54:46 +0200 Subject: [PATCH 12/47] Add #7525: Allow autoreplace with same model vehicle --- src/autoreplace.cpp | 13 ++++++++++++- src/autoreplace_cmd.cpp | 3 --- 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/src/autoreplace.cpp b/src/autoreplace.cpp index e1b73448dd..977b5e674a 100644 --- a/src/autoreplace.cpp +++ b/src/autoreplace.cpp @@ -69,7 +69,18 @@ EngineID EngineReplacement(EngineRenewList erl, EngineID engine, GroupID group, /* We didn't find anything useful in the vehicle's own group so we will try ALL_GROUP */ er = GetEngineReplacement(erl, engine, ALL_GROUP); } - if (replace_when_old != nullptr) *replace_when_old = er == nullptr ? false : er->replace_when_old; + if (replace_when_old != nullptr) { + if (er == nullptr) { + /* Not replacing */ + *replace_when_old = false; + } else if (er->to == engine) { + /* When replacing with same model, only ever do it when old */ + *replace_when_old = true; + } else { + /* Use player setting */ + *replace_when_old = er->replace_when_old; + } + } return er == nullptr ? INVALID_ENGINE : er->to; } diff --git a/src/autoreplace_cmd.cpp b/src/autoreplace_cmd.cpp index 2d3943c863..ca3883f8d4 100644 --- a/src/autoreplace_cmd.cpp +++ b/src/autoreplace_cmd.cpp @@ -60,9 +60,6 @@ bool CheckAutoreplaceValidity(EngineID from, EngineID to, CompanyID company) { assert(Engine::IsValidID(from) && Engine::IsValidID(to)); - /* we can't replace an engine into itself (that would be autorenew) */ - if (from == to) return false; - const Engine *e_from = Engine::Get(from); const Engine *e_to = Engine::Get(to); VehicleType type = e_from->type; From fe27db3dfdaa1e61dd50d526939d35ebcd5cc1c1 Mon Sep 17 00:00:00 2001 From: Michael Lutz Date: Sun, 1 Jan 2023 17:12:56 +0100 Subject: [PATCH 13/47] Codechange: [Linkgraph] Store edges in each node and not in a global matrix. --- src/core/span_type.hpp | 2 + src/linkgraph/linkgraph.cpp | 68 +++++++++++-------------- src/linkgraph/linkgraph.h | 91 ++++++++++++++++------------------ src/linkgraph/linkgraphjob.cpp | 31 +----------- src/linkgraph/linkgraphjob.h | 31 +++++++----- src/linkgraph/mcf.cpp | 4 +- src/saveload/linkgraph_sl.cpp | 13 ++--- 7 files changed, 101 insertions(+), 139 deletions(-) diff --git a/src/core/span_type.hpp b/src/core/span_type.hpp index 0df528816e..1cee60288b 100644 --- a/src/core/span_type.hpp +++ b/src/core/span_type.hpp @@ -94,6 +94,8 @@ public: constexpr reference operator[](size_type idx) const { return first[idx]; } + constexpr pointer data() const noexcept { return first; } + private: pointer first; pointer last; diff --git a/src/linkgraph/linkgraph.cpp b/src/linkgraph/linkgraph.cpp index b0aa549e2b..d72b45d046 100644 --- a/src/linkgraph/linkgraph.cpp +++ b/src/linkgraph/linkgraph.cpp @@ -23,7 +23,7 @@ INSTANTIATE_POOL_METHODS(LinkGraph) * @param st ID of the associated station. * @param demand Demand for cargo at the station. */ -inline void LinkGraph::BaseNode::Init(TileIndex xy, StationID st, uint demand) +LinkGraph::BaseNode::BaseNode(TileIndex xy, StationID st, uint demand) { this->xy = xy; this->supply = 0; @@ -35,7 +35,7 @@ inline void LinkGraph::BaseNode::Init(TileIndex xy, StationID st, uint demand) /** * Create an edge. */ -inline void LinkGraph::BaseEdge::Init() +LinkGraph::BaseEdge::BaseEdge() { this->capacity = 0; this->usage = 0; @@ -57,7 +57,7 @@ void LinkGraph::ShiftDates(int interval) BaseNode &source = this->nodes[node1]; if (source.last_update != INVALID_DATE) source.last_update += interval; for (NodeID node2 = 0; node2 < this->Size(); ++node2) { - BaseEdge &edge = this->edges[node1][node2]; + BaseEdge &edge = this->nodes[node1].edges[node2]; if (edge.last_unrestricted_update != INVALID_DATE) edge.last_unrestricted_update += interval; if (edge.last_restricted_update != INVALID_DATE) edge.last_restricted_update += interval; } @@ -70,7 +70,7 @@ void LinkGraph::Compress() for (NodeID node1 = 0; node1 < this->Size(); ++node1) { this->nodes[node1].supply /= 2; for (NodeID node2 = 0; node2 < this->Size(); ++node2) { - BaseEdge &edge = this->edges[node1][node2]; + BaseEdge &edge = this->nodes[node1].edges[node2]; if (edge.capacity > 0) { uint new_capacity = std::max(1U, edge.capacity / 2); if (edge.capacity < (1 << 16)) { @@ -101,10 +101,10 @@ void LinkGraph::Merge(LinkGraph *other) st->goods[this->cargo].link_graph = this->index; st->goods[this->cargo].node = new_node; for (NodeID node2 = 0; node2 < node1; ++node2) { - BaseEdge &forward = this->edges[new_node][first + node2]; - BaseEdge &backward = this->edges[first + node2][new_node]; - forward = other->edges[node1][node2]; - backward = other->edges[node2][node1]; + BaseEdge &forward = this->nodes[new_node].edges[first + node2]; + BaseEdge &backward = this->nodes[first + node2].edges[new_node]; + forward = other->nodes[node1].edges[node2]; + backward = other->nodes[node2].edges[node1]; forward.capacity = LinkGraph::Scale(forward.capacity, age, other_age); forward.usage = LinkGraph::Scale(forward.usage, age, other_age); forward.travel_time_sum = LinkGraph::Scale(forward.travel_time_sum, age, other_age); @@ -114,8 +114,8 @@ void LinkGraph::Merge(LinkGraph *other) backward.travel_time_sum = LinkGraph::Scale(backward.travel_time_sum, age, other_age); if (backward.next_edge != INVALID_NODE) backward.next_edge += first; } - BaseEdge &new_start = this->edges[new_node][new_node]; - new_start = other->edges[node1][node1]; + BaseEdge &new_start = this->nodes[new_node].edges[new_node]; + new_start = other->nodes[node1].edges[node1]; if (new_start.next_edge != INVALID_NODE) new_start.next_edge += first; } delete other; @@ -132,7 +132,7 @@ void LinkGraph::RemoveNode(NodeID id) NodeID last_node = this->Size() - 1; for (NodeID i = 0; i <= last_node; ++i) { (*this)[i].RemoveEdge(id); - BaseEdge *node_edges = this->edges[i]; + auto node_edges = this->nodes[i].edges; NodeID prev = i; NodeID next = node_edges[i].next_edge; while (next != INVALID_NODE) { @@ -150,10 +150,9 @@ void LinkGraph::RemoveNode(NodeID id) * directly from station goods entries so the order and position must remain. */ this->nodes[id] = this->nodes.back(); this->nodes.pop_back(); - this->edges.EraseColumn(id); - /* Not doing EraseRow here, as having the extra invalid row doesn't hurt - * and removing it would trigger a lot of memmove. The data has already - * been copied around in the loop above. */ + for (auto &n : this->nodes) { + n.edges.pop_back(); + } } /** @@ -169,23 +168,15 @@ NodeID LinkGraph::AddNode(const Station *st) const GoodsEntry &good = st->goods[this->cargo]; NodeID new_node = this->Size(); - this->nodes.emplace_back(); - /* Avoid reducing the height of the matrix as that is expensive and we - * most likely will increase it again later which is again expensive. */ - this->edges.Resize(new_node + 1U, std::max(new_node + 1U, this->edges.Height())); - - this->nodes[new_node].Init(st->xy, st->index, - HasBit(good.status, GoodsEntry::GES_ACCEPTANCE)); + this->nodes.emplace_back(st->xy, st->index, HasBit(good.status, GoodsEntry::GES_ACCEPTANCE)); - BaseEdge *new_edges = this->edges[new_node]; + for (auto &n : this->nodes) { + n.edges.resize(this->Size()); + } /* Reset the first edge starting at the new node */ - new_edges[new_node].next_edge = INVALID_NODE; + this->nodes[new_node].edges[new_node].next_edge = INVALID_NODE; - for (NodeID i = 0; i <= new_node; ++i) { - new_edges[i].Init(); - this->edges[i][new_node].Init(); - } return new_node; } @@ -200,8 +191,8 @@ NodeID LinkGraph::AddNode(const Station *st) void LinkGraph::Node::AddEdge(NodeID to, uint capacity, uint usage, uint32 travel_time, EdgeUpdateMode mode) { assert(this->index != to); - BaseEdge &edge = this->edges[to]; - BaseEdge &first = this->edges[this->index]; + BaseEdge &edge = this->node.edges[to]; + BaseEdge &first = this->node.edges[this->index]; edge.capacity = capacity; edge.usage = usage; edge.travel_time_sum = static_cast(travel_time) * capacity; @@ -222,7 +213,7 @@ void LinkGraph::Node::UpdateEdge(NodeID to, uint capacity, uint usage, uint32 tr { assert(capacity > 0); assert(usage <= capacity); - if (this->edges[to].capacity == 0) { + if (this->node.edges[to].capacity == 0) { this->AddEdge(to, capacity, usage, travel_time, mode); } else { (*this)[to].Update(capacity, usage, travel_time, mode); @@ -236,7 +227,7 @@ void LinkGraph::Node::UpdateEdge(NodeID to, uint capacity, uint usage, uint32 tr void LinkGraph::Node::RemoveEdge(NodeID to) { if (this->index == to) return; - BaseEdge &edge = this->edges[to]; + BaseEdge &edge = this->node.edges[to]; edge.capacity = 0; edge.last_unrestricted_update = INVALID_DATE; edge.last_restricted_update = INVALID_DATE; @@ -244,16 +235,16 @@ void LinkGraph::Node::RemoveEdge(NodeID to) edge.travel_time_sum = 0; NodeID prev = this->index; - NodeID next = this->edges[this->index].next_edge; + NodeID next = this->node.edges[this->index].next_edge; while (next != INVALID_NODE) { if (next == to) { /* Will be removed, skip it. */ - this->edges[prev].next_edge = edge.next_edge; + this->node.edges[prev].next_edge = edge.next_edge; edge.next_edge = INVALID_NODE; break; } else { prev = next; - next = this->edges[next].next_edge; + next = this->node.edges[next].next_edge; } } } @@ -305,12 +296,9 @@ void LinkGraph::Edge::Update(uint capacity, uint usage, uint32 travel_time, Edge void LinkGraph::Init(uint size) { assert(this->Size() == 0); - this->edges.Resize(size, size); this->nodes.resize(size); - for (uint i = 0; i < size; ++i) { - this->nodes[i].Init(); - BaseEdge *column = this->edges[i]; - for (uint j = 0; j < size; ++j) column[j].Init(); + for (auto &n : this->nodes) { + n.edges.resize(size); } } diff --git a/src/linkgraph/linkgraph.h b/src/linkgraph/linkgraph.h index 3c5aee8720..67ac483465 100644 --- a/src/linkgraph/linkgraph.h +++ b/src/linkgraph/linkgraph.h @@ -12,7 +12,6 @@ #include "../core/pool_type.hpp" #include "../core/smallmap_type.hpp" -#include "../core/smallmatrix_type.hpp" #include "../station_base.h" #include "../cargotype.h" #include "../date_func.h" @@ -38,21 +37,6 @@ extern LinkGraphPool _link_graph_pool; */ class LinkGraph : public LinkGraphPool::PoolItem<&_link_graph_pool> { public: - - /** - * Node of the link graph. contains all relevant information from the associated - * station. It's copied so that the link graph job can work on its own data set - * in a separate thread. - */ - struct BaseNode { - uint supply; ///< Supply at the station. - uint demand; ///< Acceptance at the station. - StationID station; ///< Station ID. - TileIndex xy; ///< Location of the station referred to by the node. - Date last_update; ///< When the supply was last updated. - void Init(TileIndex xy = INVALID_TILE, StationID st = INVALID_STATION, uint demand = 0); - }; - /** * An edge in the link graph. Corresponds to a link between two stations or at * least the distance between them. Edges from one node to itself contain the @@ -66,7 +50,25 @@ public: Date last_unrestricted_update; ///< When the unrestricted part of the link was last updated. Date last_restricted_update; ///< When the restricted part of the link was last updated. NodeID next_edge; ///< Destination of next valid edge starting at the same source node. - void Init(); + + BaseEdge(); + }; + + /** + * Node of the link graph. contains all relevant information from the associated + * station. It's copied so that the link graph job can work on its own data set + * in a separate thread. + */ + struct BaseNode { + uint supply; ///< Supply at the station. + uint demand; ///< Acceptance at the station. + StationID station; ///< Station ID. + TileIndex xy; ///< Location of the station referred to by the node. + Date last_update; ///< When the supply was last updated. + + std::vector edges; + + BaseNode(TileIndex xy = INVALID_TILE, StationID st = INVALID_STATION, uint demand = 0); }; /** @@ -125,26 +127,22 @@ public: /** * Wrapper for a node (const or not) allowing retrieval, but no modification. - * @tparam Tedge Actual node class, may be "const BaseNode" or just "BaseNode". - * @tparam Tedge Actual edge class, may be "const BaseEdge" or just "BaseEdge". + * @tparam Tnode Actual node class, may be "const BaseNode" or just "BaseNode". */ - template + template class NodeWrapper { protected: - Tnode &node; ///< Node being wrapped. - Tedge *edges; ///< Outgoing edges for wrapped node. - NodeID index; ///< ID of wrapped node. + Tnode &node; ///< Node being wrapped. + NodeID index; ///< ID of wrapped node. public: /** * Wrap a node. * @param node Node to be wrapped. - * @param edges Outgoing edges for node to be wrapped. * @param index ID of node to be wrapped. */ - NodeWrapper(Tnode &node, Tedge *edges, NodeID index) : node(node), - edges(edges), index(index) {} + NodeWrapper(Tnode &node, NodeID index) : node(node), index(index) {} /** * Get supply of wrapped node. @@ -187,8 +185,8 @@ public: template class BaseEdgeIterator { protected: - Tedge *base; ///< Array of edges being iterated. - NodeID current; ///< Current offset in edges array. + span base; ///< Array of edges being iterated. + NodeID current; ///< Current offset in edges array. /** * A "fake" pointer to enable operator-> on temporaries. As the objects @@ -218,7 +216,7 @@ public: * @param base Array of edges to be iterated. * @param current ID of current node (to locate the first edge). */ - BaseEdgeIterator (Tedge *base, NodeID current) : + BaseEdgeIterator (span base, NodeID current) : base(base), current(current == INVALID_NODE ? current : base[current].next_edge) {} @@ -254,7 +252,7 @@ public: template bool operator==(const Tother &other) { - return this->base == other.base && this->current == other.current; + return this->base.data() == other.base.data() && this->current == other.current; } /** @@ -267,7 +265,7 @@ public: template bool operator!=(const Tother &other) { - return this->base != other.base || this->current != other.current; + return this->base.data() != other.base.data() || this->current != other.current; } /** @@ -319,7 +317,7 @@ public: * @param edges Array of edges to be iterated over. * @param current ID of current edge's end node. */ - ConstEdgeIterator(const BaseEdge *edges, NodeID current) : + ConstEdgeIterator(span edges, NodeID current) : BaseEdgeIterator(edges, current) {} }; @@ -334,7 +332,7 @@ public: * @param edges Array of edges to be iterated over. * @param current ID of current edge's end node. */ - EdgeIterator(BaseEdge *edges, NodeID current) : + EdgeIterator(span edges, NodeID current) : BaseEdgeIterator(edges, current) {} }; @@ -342,16 +340,14 @@ public: * Constant node class. Only retrieval operations are allowed on both the * node itself and its edges. */ - class ConstNode : public NodeWrapper { + class ConstNode : public NodeWrapper { public: /** * Constructor. * @param lg LinkGraph to get the node from. * @param node ID of the node. */ - ConstNode(const LinkGraph *lg, NodeID node) : - NodeWrapper(lg->nodes[node], lg->edges[node], node) - {} + ConstNode(const LinkGraph *lg, NodeID node) : NodeWrapper(lg->nodes[node], node) {} /** * Get a ConstEdge. This is not a reference as the wrapper objects are @@ -359,34 +355,32 @@ public: * @param to ID of end node of edge. * @return Constant edge wrapper. */ - ConstEdge operator[](NodeID to) const { return ConstEdge(this->edges[to]); } + ConstEdge operator[](NodeID to) const { return ConstEdge(this->node.edges[to]); } /** * Get an iterator pointing to the start of the edges array. * @return Constant edge iterator. */ - ConstEdgeIterator Begin() const { return ConstEdgeIterator(this->edges, this->index); } + ConstEdgeIterator Begin() const { return ConstEdgeIterator(this->node.edges, this->index); } /** * Get an iterator pointing beyond the end of the edges array. * @return Constant edge iterator. */ - ConstEdgeIterator End() const { return ConstEdgeIterator(this->edges, INVALID_NODE); } + ConstEdgeIterator End() const { return ConstEdgeIterator(this->node.edges, INVALID_NODE); } }; /** * Updatable node class. The node itself as well as its edges can be modified. */ - class Node : public NodeWrapper { + class Node : public NodeWrapper { public: /** * Constructor. * @param lg LinkGraph to get the node from. * @param node ID of the node. */ - Node(LinkGraph *lg, NodeID node) : - NodeWrapper(lg->nodes[node], lg->edges[node], node) - {} + Node(LinkGraph *lg, NodeID node) : NodeWrapper(lg->nodes[node], node) {} /** * Get an Edge. This is not a reference as the wrapper objects are not @@ -394,19 +388,19 @@ public: * @param to ID of end node of edge. * @return Edge wrapper. */ - Edge operator[](NodeID to) { return Edge(this->edges[to]); } + Edge operator[](NodeID to) { return Edge(this->node.edges[to]); } /** * Get an iterator pointing to the start of the edges array. * @return Edge iterator. */ - EdgeIterator Begin() { return EdgeIterator(this->edges, this->index); } + EdgeIterator Begin() { return EdgeIterator(this->node.edges, this->index); } /** * Get an iterator pointing beyond the end of the edges array. * @return Constant edge iterator. */ - EdgeIterator End() { return EdgeIterator(this->edges, INVALID_NODE); } + EdgeIterator End() { return EdgeIterator(this->node.edges, INVALID_NODE); } /** * Update the node's supply and set last_update to the current date. @@ -442,7 +436,6 @@ public: }; typedef std::vector NodeVector; - typedef SmallMatrix EdgeMatrix; /** Minimum effective distance for timeout calculation. */ static const uint MIN_TIMEOUT_DISTANCE = 32; @@ -539,11 +532,11 @@ protected: friend SaveLoadTable GetLinkGraphJobDesc(); friend class SlLinkgraphNode; friend class SlLinkgraphEdge; + friend class LinkGraphJob; CargoID cargo; ///< Cargo of this component's link graph. Date last_compression; ///< Last time the capacities and supplies were compressed. NodeVector nodes; ///< Nodes in the component. - EdgeMatrix edges; ///< Edges in the component. }; #endif /* LINKGRAPH_H */ diff --git a/src/linkgraph/linkgraphjob.cpp b/src/linkgraph/linkgraphjob.cpp index 8e5ec32838..d5304ca56e 100644 --- a/src/linkgraph/linkgraphjob.cpp +++ b/src/linkgraph/linkgraphjob.cpp @@ -180,39 +180,12 @@ LinkGraphJob::~LinkGraphJob() void LinkGraphJob::Init() { uint size = this->Size(); - this->nodes.resize(size); - this->edges.Resize(size, size); + this->nodes.reserve(size); for (uint i = 0; i < size; ++i) { - this->nodes[i].Init(this->link_graph[i].Supply()); - EdgeAnnotation *node_edges = this->edges[i]; - for (uint j = 0; j < size; ++j) { - node_edges[j].Init(); - } + this->nodes.emplace_back(this->link_graph.nodes[i]); } } -/** - * Initialize a linkgraph job edge. - */ -void LinkGraphJob::EdgeAnnotation::Init() -{ - this->demand = 0; - this->flow = 0; - this->unsatisfied_demand = 0; -} - -/** - * Initialize a Linkgraph job node. The underlying memory is expected to be - * freshly allocated, without any constructors having been called. - * @param supply Initial undelivered supply. - */ -void LinkGraphJob::NodeAnnotation::Init(uint supply) -{ - this->undelivered_supply = supply; - new (&this->flows) FlowStatMap; - new (&this->paths) PathList; -} - /** * Add this path as a new child to the given base path, thus making this path * a "fork" of the base path. diff --git a/src/linkgraph/linkgraphjob.h b/src/linkgraph/linkgraphjob.h index 812afd1f71..9bcc59da7e 100644 --- a/src/linkgraph/linkgraphjob.h +++ b/src/linkgraph/linkgraphjob.h @@ -36,7 +36,6 @@ private: uint demand; ///< Transport demand between the nodes. uint unsatisfied_demand; ///< Demand over this edge that hasn't been satisfied yet. uint flow; ///< Planned flow over this edge. - void Init(); }; /** @@ -46,11 +45,16 @@ private: uint undelivered_supply; ///< Amount of supply that hasn't been distributed yet. PathList paths; ///< Paths through this node, sorted so that those with flow == 0 are in the back. FlowStatMap flows; ///< Planned flows to other nodes. - void Init(uint supply); + + std::vector edges; + + NodeAnnotation(const LinkGraph::BaseNode &node) : undelivered_supply(node.supply), paths(), flows() + { + this->edges.resize(node.edges.size()); + } }; typedef std::vector NodeAnnotationVector; - typedef SmallMatrix EdgeAnnotationMatrix; friend SaveLoadTable GetLinkGraphJobDesc(); friend class LinkGraphSchedule; @@ -61,7 +65,6 @@ protected: std::thread thread; ///< Thread the job is running in or a default-constructed thread if it's running in the main thread. Date join_date; ///< Date when the job is to be joined. NodeAnnotationVector nodes; ///< Extra node data necessary for link graph calculation. - EdgeAnnotationMatrix edges; ///< Extra edge data necessary for link graph calculation. std::atomic job_completed; ///< Is the job still running. This is accessed by multiple threads and reads may be stale. std::atomic job_aborted; ///< Has the job been aborted. This is accessed by multiple threads and reads may be stale. @@ -146,7 +149,7 @@ public: * Iterator for job edges. */ class EdgeIterator : public LinkGraph::BaseEdgeIterator { - EdgeAnnotation *base_anno; ///< Array of annotations to be (indirectly) iterated. + span base_anno; ///< Array of annotations to be (indirectly) iterated. public: /** * Constructor. @@ -154,10 +157,14 @@ public: * @param base_anno Array of annotations to be iterated. * @param current Start offset of iteration. */ - EdgeIterator(const LinkGraph::BaseEdge *base, EdgeAnnotation *base_anno, NodeID current) : + EdgeIterator(span base, span base_anno, NodeID current) : LinkGraph::BaseEdgeIterator(base, current), base_anno(base_anno) {} + EdgeIterator() : + LinkGraph::BaseEdgeIterator(span(), INVALID_NODE), + base_anno() {} + /** * Dereference. * @return Pair of the edge currently pointed to and the ID of its @@ -184,8 +191,8 @@ public: */ class Node : public LinkGraph::ConstNode { private: - NodeAnnotation &node_anno; ///< Annotation being wrapped. - EdgeAnnotation *edge_annos; ///< Edge annotations belonging to this node. + NodeAnnotation &node_anno; ///< Annotation being wrapped. + span edge_annos; ///< Edge annotations belonging to this node. public: /** @@ -195,7 +202,7 @@ public: */ Node (LinkGraphJob *lgj, NodeID node) : LinkGraph::ConstNode(&lgj->link_graph, node), - node_anno(lgj->nodes[node]), edge_annos(lgj->edges[node]) + node_anno(lgj->nodes[node]), edge_annos(lgj->nodes[node].edges) {} /** @@ -204,21 +211,21 @@ public: * @param to Remote end of the edge. * @return Edge between this node and "to". */ - Edge operator[](NodeID to) const { return Edge(this->edges[to], this->edge_annos[to]); } + Edge operator[](NodeID to) const { return Edge(this->node.edges[to], this->edge_annos[to]); } /** * Iterator for the "begin" of the edge array. Only edges with capacity * are iterated. The others are skipped. * @return Iterator pointing to the first edge. */ - EdgeIterator Begin() const { return EdgeIterator(this->edges, this->edge_annos, index); } + EdgeIterator Begin() const { return EdgeIterator(this->node.edges, this->edge_annos, index); } /** * Iterator for the "end" of the edge array. Only edges with capacity * are iterated. The others are skipped. * @return Iterator pointing beyond the last edge. */ - EdgeIterator End() const { return EdgeIterator(this->edges, this->edge_annos, INVALID_NODE); } + EdgeIterator End() const { return EdgeIterator(this->node.edges, this->edge_annos, INVALID_NODE); } /** * Get amount of supply that hasn't been delivered, yet. diff --git a/src/linkgraph/mcf.cpp b/src/linkgraph/mcf.cpp index 8cd73cb916..78ca36eeed 100644 --- a/src/linkgraph/mcf.cpp +++ b/src/linkgraph/mcf.cpp @@ -103,9 +103,7 @@ public: * Construct a GraphEdgeIterator. * @param job Job to iterate on. */ - GraphEdgeIterator(LinkGraphJob &job) : job(job), - i(nullptr, nullptr, INVALID_NODE), end(nullptr, nullptr, INVALID_NODE) - {} + GraphEdgeIterator(LinkGraphJob &job) : job(job), i(), end() {} /** * Setup the node to start iterating at. diff --git a/src/saveload/linkgraph_sl.cpp b/src/saveload/linkgraph_sl.cpp index 9ff7e130a5..ecd3fc0911 100644 --- a/src/saveload/linkgraph_sl.cpp +++ b/src/saveload/linkgraph_sl.cpp @@ -43,24 +43,25 @@ public: void Save(Node *bn) const override { uint16 size = 0; - for (NodeID to = _linkgraph_from; to != INVALID_NODE; to = _linkgraph->edges[_linkgraph_from][to].next_edge) { + for (NodeID to = _linkgraph_from; to != INVALID_NODE; to = _linkgraph->nodes[_linkgraph_from].edges[to].next_edge) { size++; } SlSetStructListLength(size); - for (NodeID to = _linkgraph_from; to != INVALID_NODE; to = _linkgraph->edges[_linkgraph_from][to].next_edge) { - SlObject(&_linkgraph->edges[_linkgraph_from][to], this->GetDescription()); + for (NodeID to = _linkgraph_from; to != INVALID_NODE; to = _linkgraph->nodes[_linkgraph_from].edges[to].next_edge) { + SlObject(&_linkgraph->nodes[_linkgraph_from].edges[to], this->GetDescription()); } } void Load(Node *bn) const override { uint16 max_size = _linkgraph->Size(); + _linkgraph->nodes[_linkgraph_from].edges.resize(max_size); if (IsSavegameVersionBefore(SLV_191)) { /* We used to save the full matrix ... */ for (NodeID to = 0; to < max_size; ++to) { - SlObject(&_linkgraph->edges[_linkgraph_from][to], this->GetLoadDescription()); + SlObject(&_linkgraph->nodes[_linkgraph_from].edges[to], this->GetLoadDescription()); } return; } @@ -68,12 +69,12 @@ public: size_t used_size = IsSavegameVersionBefore(SLV_SAVELOAD_LIST_LENGTH) ? max_size : SlGetStructListLength(UINT16_MAX); /* ... but as that wasted a lot of space we save a sparse matrix now. */ - for (NodeID to = _linkgraph_from; to != INVALID_NODE; to = _linkgraph->edges[_linkgraph_from][to].next_edge) { + for (NodeID to = _linkgraph_from; to != INVALID_NODE; to = _linkgraph->nodes[_linkgraph_from].edges[to].next_edge) { if (used_size == 0) SlErrorCorrupt("Link graph structure overflow"); used_size--; if (to >= max_size) SlErrorCorrupt("Link graph structure overflow"); - SlObject(&_linkgraph->edges[_linkgraph_from][to], this->GetLoadDescription()); + SlObject(&_linkgraph->nodes[_linkgraph_from].edges[to], this->GetLoadDescription()); } if (!IsSavegameVersionBefore(SLV_SAVELOAD_LIST_LENGTH) && used_size > 0) SlErrorCorrupt("Corrupted link graph"); From 564de01d66d1b2bcc14e339d8cfe352f1d24d747 Mon Sep 17 00:00:00 2001 From: Michael Lutz Date: Sun, 1 Jan 2023 17:19:41 +0100 Subject: [PATCH 14/47] Remove: SmallMatrix be gone. --- src/core/CMakeLists.txt | 1 - src/core/smallmatrix_type.hpp | 320 ---------------------------------- 2 files changed, 321 deletions(-) delete mode 100644 src/core/smallmatrix_type.hpp diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index 1c14067ae4..d0a4cc3313 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -23,7 +23,6 @@ add_files( random_func.cpp random_func.hpp smallmap_type.hpp - smallmatrix_type.hpp smallstack_type.hpp smallvec_type.hpp span_type.hpp diff --git a/src/core/smallmatrix_type.hpp b/src/core/smallmatrix_type.hpp deleted file mode 100644 index 03fd1b7908..0000000000 --- a/src/core/smallmatrix_type.hpp +++ /dev/null @@ -1,320 +0,0 @@ -/* - * This file is part of OpenTTD. - * OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2. - * OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see . - */ - -/** @file smallmatrix_type.hpp Simple matrix class that allows allocating an item without the need to copy this->data needlessly. */ - -#ifndef SMALLMATRIX_TYPE_HPP -#define SMALLMATRIX_TYPE_HPP - -#include "alloc_func.hpp" -#include "mem_func.hpp" - -/** - * Simple matrix template class. - * - * Allocating a matrix in one piece reduces overhead in allocations compared - * with allocating a vector of vectors and saves some pointer dereferencing. - * However, you can only get rectangular matrixes like this and if you're - * changing their height very often performance will probably be worse than - * with a vector of vectors, due to more frequent copying of memory blocks. - * - * No iterators are provided as iterating the columns would require persistent - * column objects. Those do not exist. Providing iterators with transient - * column objects would tie each iterator to a column object, thus replacing - * previously retrieved columns when iterating and defeating the point of - * iteration. - * - * It's expected that the items don't need to be constructed or deleted by the - * container. Only memory allocation and deallocation is performed. This is the - * same for all openttd "SmallContainer" classes. - * - * @tparam T The type of the items stored - */ -template -class SmallMatrix { -protected: - T *data; ///< The pointer to the first item - uint width; ///< Number of items over first axis - uint height; ///< Number of items over second axis - uint capacity; ///< The available space for storing items - -public: - - SmallMatrix() : data(nullptr), width(0), height(0), capacity(0) {} - - /** - * Copy constructor. - * @param other The other matrix to copy. - */ - SmallMatrix(const SmallMatrix &other) : data(nullptr), width(0), height(0), capacity(0) - { - this->Assign(other); - } - - ~SmallMatrix() - { - free(this->data); - } - - /** - * Assignment. - * @param other The other matrix to assign. - */ - SmallMatrix &operator=(const SmallMatrix &other) - { - this->Assign(other); - return *this; - } - - /** - * Assign items from other vector. - */ - inline void Assign(const SmallMatrix &other) - { - if (&other == this) return; - - this->height = other.Height(); - this->width = other.Width(); - uint num_items = this->width * this->height; - if (num_items > this->capacity) { - this->capacity = num_items; - free(this->data); - this->data = MallocT(num_items); - MemCpyT(this->data, other[0], num_items); - } else if (num_items > 0) { - MemCpyT(this->data, other[0], num_items); - } - } - - /** - * Remove all rows from the matrix. - */ - inline void Clear() - { - /* In fact we just reset the width avoiding the need to - * probably reallocate the same amount of memory the matrix was - * previously using. */ - this->width = 0; - } - - /** - * Remove all items from the list and free allocated memory. - */ - inline void Reset() - { - this->height = 0; - this->width = 0; - this->capacity = 0; - free(this->data); - this->data = nullptr; - } - - /** - * Compact the matrix down to the smallest possible size. - */ - inline void Compact() - { - uint capacity = this->height * this->width; - if (capacity >= this->capacity) return; - this->capacity = capacity; - this->data = ReallocT(this->data, this->capacity); - } - - /** - * Erase a column, replacing it with the last one. - * @param x Position of the column. - */ - void EraseColumn(uint x) - { - if (x < --this->width) { - MemCpyT(this->data + x * this->height, - this->data + this->width * this->height, - this->height); - } - } - - /** - * Remove columns from the matrix while preserving the order of other columns. - * @param x First column to remove. - * @param count Number of consecutive columns to remove. - */ - void EraseColumnPreservingOrder(uint x, uint count = 1) - { - if (count == 0) return; - assert(x < this->width); - assert(x + count <= this->width); - this->width -= count; - uint to_move = (this->width - x) * this->height; - if (to_move > 0) { - MemMoveT(this->data + x * this->height, - this->data + (x + count) * this->height, to_move); - } - } - - /** - * Erase a row, replacing it with the last one. - * @param y Position of the row. - */ - void EraseRow(uint y) - { - if (y < this->height - 1) { - for (uint x = 0; x < this->width; ++x) { - this->data[x * this->height + y] = - this->data[(x + 1) * this->height - 1]; - } - } - this->Resize(this->width, this->height - 1); - } - - /** - * Remove columns from the matrix while preserving the order of other columns. - * @param y First column to remove. - * @param count Number of consecutive columns to remove. - */ - void EraseRowPreservingOrder(uint y, uint count = 1) - { - if (this->height > count + y) { - for (uint x = 0; x < this->width; ++x) { - MemMoveT(this->data + x * this->height + y, - this->data + x * this->height + y + count, - this->height - count - y); - } - } - this->Resize(this->width, this->height - count); - } - - /** - * Append rows. - * @param to_add Number of rows to append. - */ - inline void AppendRow(uint to_add = 1) - { - this->Resize(this->width, to_add + this->height); - } - - /** - * Append rows. - * @param to_add Number of rows to append. - */ - inline void AppendColumn(uint to_add = 1) - { - this->Resize(to_add + this->width, this->height); - } - - /** - * Set the size to a specific width and height, preserving item positions - * as far as possible in the process. - * @param new_width Target width. - * @param new_height Target height. - */ - inline void Resize(uint new_width, uint new_height) - { - uint new_capacity = new_width * new_height; - T *new_data = nullptr; - void (*copy)(T *dest, const T *src, size_t count) = nullptr; - if (new_capacity > this->capacity) { - /* If the data doesn't fit into current capacity, resize and copy ... */ - new_data = MallocT(new_capacity); - copy = &MemCpyT; - } else { - /* ... otherwise just move the columns around, if necessary. */ - new_data = this->data; - copy = &MemMoveT; - } - if (this->height != new_height || new_data != this->data) { - if (this->height > 0) { - if (new_height > this->height) { - /* If matrix is growing, copy from the back to avoid - * overwriting uncopied data. */ - for (uint x = this->width; x > 0; --x) { - if (x * new_height > new_capacity) continue; - (*copy)(new_data + (x - 1) * new_height, - this->data + (x - 1) * this->height, - std::min(this->height, new_height)); - } - } else { - /* If matrix is shrinking copy from the front. */ - for (uint x = 0; x < this->width; ++x) { - if ((x + 1) * new_height > new_capacity) break; - (*copy)(new_data + x * new_height, - this->data + x * this->height, - std::min(this->height, new_height)); - } - } - } - this->height = new_height; - if (new_data != this->data) { - free(this->data); - this->data = new_data; - this->capacity = new_capacity; - } - } - this->width = new_width; - } - - inline uint Height() const - { - return this->height; - } - - inline uint Width() const - { - return this->width; - } - - /** - * Get item x/y (const). - * - * @param x X-position of the item. - * @param y Y-position of the item. - * @return Item at specified position. - */ - inline const T &Get(uint x, uint y) const - { - assert(x < this->width && y < this->height); - return this->data[x * this->height + y]; - } - - /** - * Get item x/y. - * - * @param x X-position of the item. - * @param y Y-position of the item. - * @return Item at specified position. - */ - inline T &Get(uint x, uint y) - { - assert(x < this->width && y < this->height); - return this->data[x * this->height + y]; - } - - /** - * Get column "number" (const) - * - * @param x Position of the column. - * @return Column at "number". - */ - inline const T *operator[](uint x) const - { - assert(x < this->width); - return this->data + x * this->height; - } - - /** - * Get column "number" (const) - * - * @param x Position of the column. - * @return Column at "number". - */ - inline T *operator[](uint x) - { - assert(x < this->width); - return this->data + x * this->height; - } -}; - -#endif /* SMALLMATRIX_TYPE_HPP */ From 8f851ead7058f69e43a9bcaa863231fe543d0145 Mon Sep 17 00:00:00 2001 From: Michael Lutz Date: Mon, 2 Jan 2023 01:06:31 +0100 Subject: [PATCH 15/47] Codechange: [Linkgraph] Split annotation for demand between nodes from the edge annotations. --- src/linkgraph/linkgraphjob.cpp | 2 +- src/linkgraph/linkgraphjob.h | 73 +++++++++++++++++----------------- src/linkgraph/mcf.cpp | 33 +++++++-------- src/linkgraph/mcf.h | 2 +- 4 files changed, 55 insertions(+), 55 deletions(-) diff --git a/src/linkgraph/linkgraphjob.cpp b/src/linkgraph/linkgraphjob.cpp index d5304ca56e..047bfc7f67 100644 --- a/src/linkgraph/linkgraphjob.cpp +++ b/src/linkgraph/linkgraphjob.cpp @@ -182,7 +182,7 @@ void LinkGraphJob::Init() uint size = this->Size(); this->nodes.reserve(size); for (uint i = 0; i < size; ++i) { - this->nodes.emplace_back(this->link_graph.nodes[i]); + this->nodes.emplace_back(this->link_graph.nodes[i], this->link_graph.Size()); } } diff --git a/src/linkgraph/linkgraphjob.h b/src/linkgraph/linkgraphjob.h index 9bcc59da7e..265131ef24 100644 --- a/src/linkgraph/linkgraphjob.h +++ b/src/linkgraph/linkgraphjob.h @@ -30,11 +30,17 @@ extern LinkGraphJobPool _link_graph_job_pool; class LinkGraphJob : public LinkGraphJobPool::PoolItem<&_link_graph_job_pool>{ private: /** - * Annotation for a link graph edge. + * Demand between two nodes. */ - struct EdgeAnnotation { + struct DemandAnnotation { uint demand; ///< Transport demand between the nodes. uint unsatisfied_demand; ///< Demand over this edge that hasn't been satisfied yet. + }; + + /** + * Annotation for a link graph edge. + */ + struct EdgeAnnotation { uint flow; ///< Planned flow over this edge. }; @@ -46,11 +52,13 @@ private: PathList paths; ///< Paths through this node, sorted so that those with flow == 0 are in the back. FlowStatMap flows; ///< Planned flows to other nodes. - std::vector edges; + std::vector edges; ///< Annotations for all edges originating at this node. + std::vector demands; ///< Annotations for the demand to all other nodes. - NodeAnnotation(const LinkGraph::BaseNode &node) : undelivered_supply(node.supply), paths(), flows() + NodeAnnotation(const LinkGraph::BaseNode &node, size_t size) : undelivered_supply(node.supply), paths(), flows() { this->edges.resize(node.edges.size()); + this->demands.resize(size); } }; @@ -90,18 +98,6 @@ public: Edge(const LinkGraph::BaseEdge &edge, EdgeAnnotation &anno) : LinkGraph::ConstEdge(edge), anno(anno) {} - /** - * Get the transport demand between end the points of the edge. - * @return Demand. - */ - uint Demand() const { return this->anno.demand; } - - /** - * Get the transport demand that hasn't been satisfied by flows, yet. - * @return Unsatisfied demand. - */ - uint UnsatisfiedDemand() const { return this->anno.unsatisfied_demand; } - /** * Get the total flow on the edge. * @return Flow. @@ -123,26 +119,6 @@ public: assert(flow <= this->anno.flow); this->anno.flow -= flow; } - - /** - * Add some (not yet satisfied) demand. - * @param demand Demand to be added. - */ - void AddDemand(uint demand) - { - this->anno.demand += demand; - this->anno.unsatisfied_demand += demand; - } - - /** - * Satisfy some demand. - * @param demand Demand to be satisfied. - */ - void SatisfyDemand(uint demand) - { - assert(demand <= this->anno.unsatisfied_demand); - this->anno.unsatisfied_demand -= demand; - } }; /** @@ -258,6 +234,28 @@ public: */ const PathList &Paths() const { return this->node_anno.paths; } + /** + * Get the transport demand between end the points of the edge. + * @return Demand. + */ + uint DemandTo(NodeID to) const { return this->node_anno.demands[to].demand; } + + /** + * Get the transport demand that hasn't been satisfied by flows, yet. + * @return Unsatisfied demand. + */ + uint UnsatisfiedDemandTo(NodeID to) const { return this->node_anno.demands[to].unsatisfied_demand; } + + /** + * Satisfy some demand. + * @param demand Demand to be satisfied. + */ + void SatisfyDemandTo(NodeID to, uint demand) + { + assert(demand <= this->node_anno.demands[to].unsatisfied_demand); + this->node_anno.demands[to].unsatisfied_demand -= demand; + } + /** * Deliver some supply, adding demand to the respective edge. * @param to Destination for supply. @@ -266,7 +264,8 @@ public: void DeliverSupply(NodeID to, uint amount) { this->node_anno.undelivered_supply -= amount; - (*this)[to].AddDemand(amount); + this->node_anno.demands[to].demand += amount; + this->node_anno.demands[to].unsatisfied_demand += amount; } }; diff --git a/src/linkgraph/mcf.cpp b/src/linkgraph/mcf.cpp index 78ca36eeed..599bac3d52 100644 --- a/src/linkgraph/mcf.cpp +++ b/src/linkgraph/mcf.cpp @@ -334,19 +334,20 @@ void MultiCommodityFlow::CleanupPaths(NodeID source_id, PathVector &paths) /** * Push flow along a path and update the unsatisfied_demand of the associated * edge. - * @param edge Edge whose ends the path connects. + * @param node Node where the path starts. + * @param to Node where the path ends. * @param path End of the path the flow should be pushed on. * @param accuracy Accuracy of the calculation. * @param max_saturation If < UINT_MAX only push flow up to the given * saturation, otherwise the path can be "overloaded". */ -uint MultiCommodityFlow::PushFlow(Edge &edge, Path *path, uint accuracy, +uint MultiCommodityFlow::PushFlow(Node &node, NodeID to, Path *path, uint accuracy, uint max_saturation) { - assert(edge.UnsatisfiedDemand() > 0); - uint flow = Clamp(edge.Demand() / accuracy, 1, edge.UnsatisfiedDemand()); + assert(node.UnsatisfiedDemandTo(to) > 0); + uint flow = Clamp(node.DemandTo(to) / accuracy, 1, node.UnsatisfiedDemandTo(to)); flow = path->AddFlow(flow, this->job, max_saturation); - edge.SatisfyDemand(flow); + node.SatisfyDemandTo(to, flow); return flow; } @@ -511,25 +512,25 @@ MCF1stPass::MCF1stPass(LinkGraphJob &job) : MultiCommodityFlow(job) /* First saturate the shortest paths. */ this->Dijkstra(source, paths); + Node src_node = job[source]; bool source_demand_left = false; for (NodeID dest = 0; dest < size; ++dest) { - Edge edge = job[source][dest]; - if (edge.UnsatisfiedDemand() > 0) { + if (src_node.UnsatisfiedDemandTo(dest) > 0) { Path *path = paths[dest]; assert(path != nullptr); /* Generally only allow paths that don't exceed the * available capacity. But if no demand has been assigned * yet, make an exception and allow any valid path *once*. */ - if (path->GetFreeCapacity() > 0 && this->PushFlow(edge, path, + if (path->GetFreeCapacity() > 0 && this->PushFlow(src_node, dest, path, accuracy, this->max_saturation) > 0) { /* If a path has been found there is a chance we can * find more. */ - more_loops = more_loops || (edge.UnsatisfiedDemand() > 0); - } else if (edge.UnsatisfiedDemand() == edge.Demand() && + more_loops = more_loops || (src_node.UnsatisfiedDemandTo(dest) > 0); + } else if (src_node.UnsatisfiedDemandTo(dest) == src_node.DemandTo(dest) && path->GetFreeCapacity() > INT_MIN) { - this->PushFlow(edge, path, accuracy, UINT_MAX); + this->PushFlow(src_node, dest, path, accuracy, UINT_MAX); } - if (edge.UnsatisfiedDemand() > 0) source_demand_left = true; + if (src_node.UnsatisfiedDemandTo(dest) > 0) source_demand_left = true; } } finished_sources[source] = !source_demand_left; @@ -558,13 +559,13 @@ MCF2ndPass::MCF2ndPass(LinkGraphJob &job) : MultiCommodityFlow(job) this->Dijkstra(source, paths); + Node src_node = job[source]; bool source_demand_left = false; for (NodeID dest = 0; dest < size; ++dest) { - Edge edge = this->job[source][dest]; Path *path = paths[dest]; - if (edge.UnsatisfiedDemand() > 0 && path->GetFreeCapacity() > INT_MIN) { - this->PushFlow(edge, path, accuracy, UINT_MAX); - if (edge.UnsatisfiedDemand() > 0) { + if (src_node.UnsatisfiedDemandTo(dest) > 0 && path->GetFreeCapacity() > INT_MIN) { + this->PushFlow(src_node, dest, path, accuracy, UINT_MAX); + if (src_node.UnsatisfiedDemandTo(dest) > 0) { demand_left = true; source_demand_left = true; } diff --git a/src/linkgraph/mcf.h b/src/linkgraph/mcf.h index 4ad598c574..e1e83e3898 100644 --- a/src/linkgraph/mcf.h +++ b/src/linkgraph/mcf.h @@ -24,7 +24,7 @@ protected: template void Dijkstra(NodeID from, PathVector &paths); - uint PushFlow(Edge &edge, Path *path, uint accuracy, uint max_saturation); + uint PushFlow(Node &node, NodeID to, Path *path, uint accuracy, uint max_saturation); void CleanupPaths(NodeID source, PathVector &paths); From 178249e7cc415f94efe07b9c14b1c0a700b91884 Mon Sep 17 00:00:00 2001 From: Michael Lutz Date: Thu, 22 Dec 2022 00:38:30 +0100 Subject: [PATCH 16/47] Codechange: Saveload macros for entries with a custom table name. --- src/saveload/saveload.h | 27 ++++++++++++++++++++++++++- src/table/settings.h.preamble | 3 +++ 2 files changed, 29 insertions(+), 1 deletion(-) diff --git a/src/saveload/saveload.h b/src/saveload/saveload.h index de27caa8de..f6672a9ad3 100644 --- a/src/saveload/saveload.h +++ b/src/saveload/saveload.h @@ -687,6 +687,7 @@ struct SaveLoadCompat { /** * Storage of simple variables, references (pointers), and arrays. * @param cmd Load/save type. @see SaveLoadType + * @param name Field name for table chunks. * @param base Name of the class or struct containing the variable. * @param variable Name of the variable in the class or struct referenced by \a base. * @param type Storage of the data in memory and in the savegame. @@ -695,7 +696,20 @@ struct SaveLoadCompat { * @param extra Extra data to pass to the address callback function. * @note In general, it is better to use one of the SLE_* macros below. */ -#define SLE_GENERAL(cmd, base, variable, type, length, from, to, extra) SaveLoad {#variable, cmd, type, length, from, to, cpp_sizeof(base, variable), [] (void *b, size_t) -> void * { assert(b != nullptr); return const_cast(static_cast(std::addressof(static_cast(b)->variable))); }, extra, nullptr} +#define SLE_GENERAL_NAME(cmd, name, base, variable, type, length, from, to, extra) SaveLoad {name, cmd, type, length, from, to, cpp_sizeof(base, variable), [] (void *b, size_t) -> void * { assert(b != nullptr); return const_cast(static_cast(std::addressof(static_cast(b)->variable))); }, extra, nullptr} + +/** + * Storage of simple variables, references (pointers), and arrays with a custom name. + * @param cmd Load/save type. @see SaveLoadType + * @param base Name of the class or struct containing the variable. + * @param variable Name of the variable in the class or struct referenced by \a base. + * @param type Storage of the data in memory and in the savegame. + * @param from First savegame version that has the field. + * @param to Last savegame version that has the field. + * @param extra Extra data to pass to the address callback function. + * @note In general, it is better to use one of the SLE_* macros below. + */ +#define SLE_GENERAL(cmd, base, variable, type, length, from, to, extra) SLE_GENERAL_NAME(cmd, #variable, base, variable, type, length, from, to, extra) /** * Storage of a variable in some savegame versions. @@ -707,6 +721,17 @@ struct SaveLoadCompat { */ #define SLE_CONDVAR(base, variable, type, from, to) SLE_GENERAL(SL_VAR, base, variable, type, 0, from, to, 0) +/** + * Storage of a variable in some savegame versions. + * @param base Name of the class or struct containing the variable. + * @param variable Name of the variable in the class or struct referenced by \a base. + * @param name Field name for table chunks. + * @param type Storage of the data in memory and in the savegame. + * @param from First savegame version that has the field. + * @param to Last savegame version that has the field. + */ +#define SLE_CONDVARNAME(base, variable, name, type, from, to) SLE_GENERAL_NAME(SL_VAR, name, base, variable, type, 0, from, to, 0) + /** * Storage of a reference in some savegame versions. * @param base Name of the class or struct containing the variable. diff --git a/src/table/settings.h.preamble b/src/table/settings.h.preamble index dca29cf88c..dd978727ed 100644 --- a/src/table/settings.h.preamble +++ b/src/table/settings.h.preamble @@ -79,6 +79,9 @@ static size_t ConvertLandscape(const char *value); #define SDT_VAR(base, var, type, flags, def, min, max, interval, str, strhelp, strval, pre_check, post_callback, from, to, cat, extra, startup)\ NSD(Int, SLE_GENERAL(SL_VAR, base, var, type, 1, from, to, extra), flags, startup, def, min, max, interval, str, strhelp, strval, cat, pre_check, post_callback) +#define SDT_VAR_NAME(base, var, type, flags, def, min, max, interval, str, strhelp, strval, pre_check, post_callback, from, to, cat, extra, startup, name)\ + NSD(Int, SLE_GENERAL_NAME(SL_VAR, name, base, var, type, 1, from, to, extra), flags, startup, def, min, max, interval, str, strhelp, strval, cat, pre_check, post_callback) + #define SDT_BOOL(base, var, flags, def, str, strhelp, strval, pre_check, post_callback, from, to, cat, extra, startup)\ NSD(Bool, SLE_GENERAL(SL_VAR, base, var, SLE_BOOL, 1, from, to, extra), flags, startup, def, str, strhelp, strval, cat, pre_check, post_callback) From 7352f812e631f3e47e4e974b3d0bd0fcafc73b8a Mon Sep 17 00:00:00 2001 From: Michael Lutz Date: Mon, 2 Jan 2023 21:46:49 +0100 Subject: [PATCH 17/47] Codechange: [Linkgraph] Only store present link graph edges and not all possible edges. --- src/linkgraph/linkgraph.cpp | 102 +++++++++----------------------- src/linkgraph/linkgraph.h | 88 ++++++++++++++++++--------- src/linkgraph/linkgraph_gui.cpp | 4 +- src/linkgraph/linkgraphjob.cpp | 3 +- src/linkgraph/linkgraphjob.h | 19 +++--- src/saveload/linkgraph_sl.cpp | 70 +++++++++++++--------- src/saveload/saveload.h | 2 + src/station.cpp | 2 +- src/station_cmd.cpp | 9 ++- 9 files changed, 153 insertions(+), 146 deletions(-) diff --git a/src/linkgraph/linkgraph.cpp b/src/linkgraph/linkgraph.cpp index d72b45d046..1230204542 100644 --- a/src/linkgraph/linkgraph.cpp +++ b/src/linkgraph/linkgraph.cpp @@ -35,14 +35,14 @@ LinkGraph::BaseNode::BaseNode(TileIndex xy, StationID st, uint demand) /** * Create an edge. */ -LinkGraph::BaseEdge::BaseEdge() +LinkGraph::BaseEdge::BaseEdge(NodeID dest_node) { this->capacity = 0; this->usage = 0; this->travel_time_sum = 0; this->last_unrestricted_update = INVALID_DATE; this->last_restricted_update = INVALID_DATE; - this->next_edge = INVALID_NODE; + this->dest_node = dest_node; } /** @@ -56,8 +56,7 @@ void LinkGraph::ShiftDates(int interval) for (NodeID node1 = 0; node1 < this->Size(); ++node1) { BaseNode &source = this->nodes[node1]; if (source.last_update != INVALID_DATE) source.last_update += interval; - for (NodeID node2 = 0; node2 < this->Size(); ++node2) { - BaseEdge &edge = this->nodes[node1].edges[node2]; + for (BaseEdge &edge : this->nodes[node1].edges) { if (edge.last_unrestricted_update != INVALID_DATE) edge.last_unrestricted_update += interval; if (edge.last_restricted_update != INVALID_DATE) edge.last_restricted_update += interval; } @@ -69,8 +68,7 @@ void LinkGraph::Compress() this->last_compression = (_date + this->last_compression) / 2; for (NodeID node1 = 0; node1 < this->Size(); ++node1) { this->nodes[node1].supply /= 2; - for (NodeID node2 = 0; node2 < this->Size(); ++node2) { - BaseEdge &edge = this->nodes[node1].edges[node2]; + for (BaseEdge &edge : this->nodes[node1].edges) { if (edge.capacity > 0) { uint new_capacity = std::max(1U, edge.capacity / 2); if (edge.capacity < (1 << 16)) { @@ -100,23 +98,13 @@ void LinkGraph::Merge(LinkGraph *other) this->nodes[new_node].supply = LinkGraph::Scale(other->nodes[node1].supply, age, other_age); st->goods[this->cargo].link_graph = this->index; st->goods[this->cargo].node = new_node; - for (NodeID node2 = 0; node2 < node1; ++node2) { - BaseEdge &forward = this->nodes[new_node].edges[first + node2]; - BaseEdge &backward = this->nodes[first + node2].edges[new_node]; - forward = other->nodes[node1].edges[node2]; - backward = other->nodes[node2].edges[node1]; - forward.capacity = LinkGraph::Scale(forward.capacity, age, other_age); - forward.usage = LinkGraph::Scale(forward.usage, age, other_age); - forward.travel_time_sum = LinkGraph::Scale(forward.travel_time_sum, age, other_age); - if (forward.next_edge != INVALID_NODE) forward.next_edge += first; - backward.capacity = LinkGraph::Scale(backward.capacity, age, other_age); - backward.usage = LinkGraph::Scale(backward.usage, age, other_age); - backward.travel_time_sum = LinkGraph::Scale(backward.travel_time_sum, age, other_age); - if (backward.next_edge != INVALID_NODE) backward.next_edge += first; + + for (BaseEdge &e : other->nodes[node1].edges) { + BaseEdge &new_edge = this->nodes[new_node].edges.emplace_back(first + e.dest_node); + new_edge.capacity = LinkGraph::Scale(e.capacity, age, other_age); + new_edge.usage = LinkGraph::Scale(e.usage, age, other_age); + new_edge.travel_time_sum = LinkGraph::Scale(e.travel_time_sum, age, other_age); } - BaseEdge &new_start = this->nodes[new_node].edges[new_node]; - new_start = other->nodes[node1].edges[node1]; - if (new_start.next_edge != INVALID_NODE) new_start.next_edge += first; } delete other; } @@ -130,28 +118,23 @@ void LinkGraph::RemoveNode(NodeID id) assert(id < this->Size()); NodeID last_node = this->Size() - 1; - for (NodeID i = 0; i <= last_node; ++i) { - (*this)[i].RemoveEdge(id); - auto node_edges = this->nodes[i].edges; - NodeID prev = i; - NodeID next = node_edges[i].next_edge; - while (next != INVALID_NODE) { - if (next == last_node) { - node_edges[prev].next_edge = id; - break; - } - prev = next; - next = node_edges[prev].next_edge; - } - node_edges[id] = node_edges[last_node]; - } Station::Get(this->nodes[last_node].station)->goods[this->cargo].node = id; /* Erase node by swapping with the last element. Node index is referenced * directly from station goods entries so the order and position must remain. */ this->nodes[id] = this->nodes.back(); this->nodes.pop_back(); for (auto &n : this->nodes) { - n.edges.pop_back(); + /* Find iterator position where an edge to id would be. */ + auto [first, last] = std::equal_range(n.edges.begin(), n.edges.end(), id); + /* Remove potential node (erasing an empty range is safe). */ + auto insert = n.edges.erase(first, last); + /* As the edge list is sorted, a potential edge to last_node will always be the last edge. */ + if (!n.edges.empty() && n.edges.back().dest_node == last_node) { + /* Change dest ID and move into the spot of the deleted edge. */ + n.edges.back().dest_node = id; + n.edges.insert(insert, n.edges.back()); + n.edges.pop_back(); + } } } @@ -170,13 +153,6 @@ NodeID LinkGraph::AddNode(const Station *st) NodeID new_node = this->Size(); this->nodes.emplace_back(st->xy, st->index, HasBit(good.status, GoodsEntry::GES_ACCEPTANCE)); - for (auto &n : this->nodes) { - n.edges.resize(this->Size()); - } - - /* Reset the first edge starting at the new node */ - this->nodes[new_node].edges[new_node].next_edge = INVALID_NODE; - return new_node; } @@ -191,13 +167,12 @@ NodeID LinkGraph::AddNode(const Station *st) void LinkGraph::Node::AddEdge(NodeID to, uint capacity, uint usage, uint32 travel_time, EdgeUpdateMode mode) { assert(this->index != to); - BaseEdge &edge = this->node.edges[to]; - BaseEdge &first = this->node.edges[this->index]; + assert(!this->HasEdgeTo(to)); + + BaseEdge &edge = *this->node.edges.emplace(std::upper_bound(this->node.edges.begin(), this->node.edges.end(), to), to); edge.capacity = capacity; edge.usage = usage; edge.travel_time_sum = static_cast(travel_time) * capacity; - edge.next_edge = first.next_edge; - first.next_edge = to; if (mode & EUM_UNRESTRICTED) edge.last_unrestricted_update = _date; if (mode & EUM_RESTRICTED) edge.last_restricted_update = _date; } @@ -213,7 +188,7 @@ void LinkGraph::Node::UpdateEdge(NodeID to, uint capacity, uint usage, uint32 tr { assert(capacity > 0); assert(usage <= capacity); - if (this->node.edges[to].capacity == 0) { + if (!this->HasEdgeTo(to)) { this->AddEdge(to, capacity, usage, travel_time, mode); } else { (*this)[to].Update(capacity, usage, travel_time, mode); @@ -226,27 +201,8 @@ void LinkGraph::Node::UpdateEdge(NodeID to, uint capacity, uint usage, uint32 tr */ void LinkGraph::Node::RemoveEdge(NodeID to) { - if (this->index == to) return; - BaseEdge &edge = this->node.edges[to]; - edge.capacity = 0; - edge.last_unrestricted_update = INVALID_DATE; - edge.last_restricted_update = INVALID_DATE; - edge.usage = 0; - edge.travel_time_sum = 0; - - NodeID prev = this->index; - NodeID next = this->node.edges[this->index].next_edge; - while (next != INVALID_NODE) { - if (next == to) { - /* Will be removed, skip it. */ - this->node.edges[prev].next_edge = edge.next_edge; - edge.next_edge = INVALID_NODE; - break; - } else { - prev = next; - next = this->node.edges[next].next_edge; - } - } + auto [first, last] = std::equal_range(this->node.edges.begin(), this->node.edges.end(), to); + this->node.edges.erase(first, last); } /** @@ -297,8 +253,4 @@ void LinkGraph::Init(uint size) { assert(this->Size() == 0); this->nodes.resize(size); - - for (auto &n : this->nodes) { - n.edges.resize(size); - } } diff --git a/src/linkgraph/linkgraph.h b/src/linkgraph/linkgraph.h index 67ac483465..6403168144 100644 --- a/src/linkgraph/linkgraph.h +++ b/src/linkgraph/linkgraph.h @@ -38,10 +38,7 @@ extern LinkGraphPool _link_graph_pool; class LinkGraph : public LinkGraphPool::PoolItem<&_link_graph_pool> { public: /** - * An edge in the link graph. Corresponds to a link between two stations or at - * least the distance between them. Edges from one node to itself contain the - * ID of the opposite Node of the first active edge (i.e. not just distance) in - * the column as next_edge. + * An edge in the link graph. Corresponds to a link between two stations. */ struct BaseEdge { uint capacity; ///< Capacity of the link. @@ -49,9 +46,25 @@ public: uint64 travel_time_sum; ///< Sum of the travel times of the link, in ticks. Date last_unrestricted_update; ///< When the unrestricted part of the link was last updated. Date last_restricted_update; ///< When the restricted part of the link was last updated. - NodeID next_edge; ///< Destination of next valid edge starting at the same source node. + NodeID dest_node; ///< Destination of the edge. - BaseEdge(); + BaseEdge(NodeID dest_node = INVALID_NODE); + + /** Comparison operator based on \c dest_node. */ + bool operator <(const BaseEdge &rhs) const + { + return this->dest_node < rhs.dest_node; + } + + bool operator <(NodeID rhs) const + { + return this->dest_node < rhs; + } + + friend inline bool operator <(NodeID lhs, const LinkGraph::BaseEdge &rhs) + { + return lhs < rhs.dest_node; + } }; /** @@ -66,7 +79,7 @@ public: TileIndex xy; ///< Location of the station referred to by the node. Date last_update; ///< When the supply was last updated. - std::vector edges; + std::vector edges; ///< Sorted list of outgoing edges from this node. BaseNode(TileIndex xy = INVALID_TILE, StationID st = INVALID_STATION, uint demand = 0); }; @@ -135,6 +148,10 @@ public: Tnode &node; ///< Node being wrapped. NodeID index; ///< ID of wrapped node. + auto GetEdge(NodeID dest) const + { + return std::lower_bound(this->node.edges.begin(), this->node.edges.end(), dest); + } public: /** @@ -173,6 +190,16 @@ public: * @return Location of the station. */ TileIndex XY() const { return this->node.xy; } + + /** + * Check if an edge to a destination is present. + * @param dest Wanted edge destination. + * @return True if an edge is present. + */ + bool HasEdgeTo(NodeID dest) const + { + return std::binary_search(this->node.edges.begin(), this->node.edges.end(), dest); + } }; /** @@ -186,7 +213,7 @@ public: class BaseEdgeIterator { protected: span base; ///< Array of edges being iterated. - NodeID current; ///< Current offset in edges array. + size_t current; ///< Current offset in edges array. /** * A "fake" pointer to enable operator-> on temporaries. As the objects @@ -214,12 +241,9 @@ public: /** * Constructor. * @param base Array of edges to be iterated. - * @param current ID of current node (to locate the first edge). + * @param end Make the iterator the end sentinel? */ - BaseEdgeIterator (span base, NodeID current) : - base(base), - current(current == INVALID_NODE ? current : base[current].next_edge) - {} + BaseEdgeIterator(span base, bool end) : base(base), current(end ? base.size() : 0) {} /** * Prefix-increment. @@ -227,7 +251,7 @@ public: */ Titer &operator++() { - this->current = this->base[this->current].next_edge; + if (this->current < this->base.size()) this->current++; return static_cast(*this); } @@ -238,7 +262,7 @@ public: Titer operator++(int) { Titer ret(static_cast(*this)); - this->current = this->base[this->current].next_edge; + ++(*this); return ret; } @@ -274,7 +298,7 @@ public: */ std::pair operator*() const { - return std::pair(this->current, Tedge_wrapper(this->base[this->current])); + return std::pair(this->base[this->current].dest_node, Tedge_wrapper(this->base[this->current])); } /** @@ -315,10 +339,10 @@ public: /** * Constructor. * @param edges Array of edges to be iterated over. - * @param current ID of current edge's end node. + * @param end Make the iterator the end sentinel? */ - ConstEdgeIterator(span edges, NodeID current) : - BaseEdgeIterator(edges, current) {} + ConstEdgeIterator(span edges, bool end) : + BaseEdgeIterator(edges, end) {} }; /** @@ -330,10 +354,10 @@ public: /** * Constructor. * @param edges Array of edges to be iterated over. - * @param current ID of current edge's end node. + * @param end Make the iterator the end sentinel? */ - EdgeIterator(span edges, NodeID current) : - BaseEdgeIterator(edges, current) {} + EdgeIterator(span edges, bool end) : + BaseEdgeIterator(edges, end) {} }; /** @@ -355,19 +379,23 @@ public: * @param to ID of end node of edge. * @return Constant edge wrapper. */ - ConstEdge operator[](NodeID to) const { return ConstEdge(this->node.edges[to]); } + ConstEdge operator[](NodeID to) const + { + assert(this->HasEdgeTo(to)); + return ConstEdge(*this->GetEdge(to)); + } /** * Get an iterator pointing to the start of the edges array. * @return Constant edge iterator. */ - ConstEdgeIterator Begin() const { return ConstEdgeIterator(this->node.edges, this->index); } + ConstEdgeIterator Begin() const { return ConstEdgeIterator(this->node.edges, false); } /** * Get an iterator pointing beyond the end of the edges array. * @return Constant edge iterator. */ - ConstEdgeIterator End() const { return ConstEdgeIterator(this->node.edges, INVALID_NODE); } + ConstEdgeIterator End() const { return ConstEdgeIterator(this->node.edges, true); } }; /** @@ -388,19 +416,23 @@ public: * @param to ID of end node of edge. * @return Edge wrapper. */ - Edge operator[](NodeID to) { return Edge(this->node.edges[to]); } + Edge operator[](NodeID to) + { + assert(this->HasEdgeTo(to)); + return Edge(*this->GetEdge(to)); + } /** * Get an iterator pointing to the start of the edges array. * @return Edge iterator. */ - EdgeIterator Begin() { return EdgeIterator(this->node.edges, this->index); } + EdgeIterator Begin() { return EdgeIterator(this->node.edges, false); } /** * Get an iterator pointing beyond the end of the edges array. * @return Constant edge iterator. */ - EdgeIterator End() { return EdgeIterator(this->node.edges, INVALID_NODE); } + EdgeIterator End() { return EdgeIterator(this->node.edges, true); } /** * Update the node's supply and set last_update to the current date. diff --git a/src/linkgraph/linkgraph_gui.cpp b/src/linkgraph/linkgraph_gui.cpp index ee7cdc05dd..2f0c941553 100644 --- a/src/linkgraph/linkgraph_gui.cpp +++ b/src/linkgraph/linkgraph_gui.cpp @@ -217,8 +217,8 @@ void LinkGraphOverlay::AddLinks(const Station *from, const Station *to) continue; } const LinkGraph &lg = *LinkGraph::Get(ge.link_graph); - ConstEdge edge = lg[ge.node][to->goods[c].node]; - if (edge.Capacity() > 0) { + if (lg[ge.node].HasEdgeTo(to->goods[c].node)) { + ConstEdge edge = lg[ge.node][to->goods[c].node]; this->AddStats(c, lg.Monthly(edge.Capacity()), lg.Monthly(edge.Usage()), ge.flows.GetFlowVia(to->index), edge.TravelTime() / DAY_TICKS, diff --git a/src/linkgraph/linkgraphjob.cpp b/src/linkgraph/linkgraphjob.cpp index 047bfc7f67..763485ce07 100644 --- a/src/linkgraph/linkgraphjob.cpp +++ b/src/linkgraph/linkgraphjob.cpp @@ -124,11 +124,12 @@ LinkGraphJob::~LinkGraphJob() FlowStatMap &flows = from.Flows(); for (EdgeIterator it(from.Begin()); it != from.End(); ++it) { - if (from[it->first].Flow() == 0) continue; + if (it->second.Flow() == 0) continue; StationID to = (*this)[it->first].Station(); Station *st2 = Station::GetIfValid(to); if (st2 == nullptr || st2->goods[this->Cargo()].link_graph != this->link_graph.index || st2->goods[this->Cargo()].node != it->first || + !(*lg)[node_id].HasEdgeTo(it->first) || (*lg)[node_id][it->first].LastUpdate() == INVALID_DATE) { /* Edge has been removed. Delete flows. */ StationIDStack erased = flows.DeleteFlows(to); diff --git a/src/linkgraph/linkgraphjob.h b/src/linkgraph/linkgraphjob.h index 265131ef24..980c27b067 100644 --- a/src/linkgraph/linkgraphjob.h +++ b/src/linkgraph/linkgraphjob.h @@ -133,12 +133,12 @@ public: * @param base_anno Array of annotations to be iterated. * @param current Start offset of iteration. */ - EdgeIterator(span base, span base_anno, NodeID current) : - LinkGraph::BaseEdgeIterator(base, current), + EdgeIterator(span base, span base_anno, bool end) : + LinkGraph::BaseEdgeIterator(base, end), base_anno(base_anno) {} EdgeIterator() : - LinkGraph::BaseEdgeIterator(span(), INVALID_NODE), + LinkGraph::BaseEdgeIterator(span(), true), base_anno() {} /** @@ -148,7 +148,7 @@ public: */ std::pair operator*() const { - return std::pair(this->current, Edge(this->base[this->current], this->base_anno[this->current])); + return std::pair(this->base[this->current].dest_node, Edge(this->base[this->current], this->base_anno[this->current])); } /** @@ -187,21 +187,26 @@ public: * @param to Remote end of the edge. * @return Edge between this node and "to". */ - Edge operator[](NodeID to) const { return Edge(this->node.edges[to], this->edge_annos[to]); } + Edge operator[](NodeID to) const + { + assert(this->HasEdgeTo(to)); + auto index = std::distance(this->node.edges.begin(), this->GetEdge(to)); + return Edge(this->node.edges[index], this->edge_annos[index]); + } /** * Iterator for the "begin" of the edge array. Only edges with capacity * are iterated. The others are skipped. * @return Iterator pointing to the first edge. */ - EdgeIterator Begin() const { return EdgeIterator(this->node.edges, this->edge_annos, index); } + EdgeIterator Begin() const { return EdgeIterator(this->node.edges, this->edge_annos, false); } /** * Iterator for the "end" of the edge array. Only edges with capacity * are iterated. The others are skipped. * @return Iterator pointing beyond the last edge. */ - EdgeIterator End() const { return EdgeIterator(this->node.edges, this->edge_annos, INVALID_NODE); } + EdgeIterator End() const { return EdgeIterator(this->node.edges, this->edge_annos, true); } /** * Get amount of supply that hasn't been delivered, yet. diff --git a/src/saveload/linkgraph_sl.cpp b/src/saveload/linkgraph_sl.cpp index ecd3fc0911..c3abf88c32 100644 --- a/src/saveload/linkgraph_sl.cpp +++ b/src/saveload/linkgraph_sl.cpp @@ -36,48 +36,60 @@ public: SLE_CONDVAR(Edge, travel_time_sum, SLE_UINT64, SLV_LINKGRAPH_TRAVEL_TIME, SL_MAX_VERSION), SLE_VAR(Edge, last_unrestricted_update, SLE_INT32), SLE_CONDVAR(Edge, last_restricted_update, SLE_INT32, SLV_187, SL_MAX_VERSION), - SLE_VAR(Edge, next_edge, SLE_UINT16), + SLE_VAR(Edge, dest_node, SLE_UINT16), + SLE_CONDVARNAME(Edge, dest_node, "next_edge", SLE_UINT16, SL_MIN_VERSION, SLV_LINKGRAPH_EDGES), }; inline const static SaveLoadCompatTable compat_description = _linkgraph_edge_sl_compat; void Save(Node *bn) const override { - uint16 size = 0; - for (NodeID to = _linkgraph_from; to != INVALID_NODE; to = _linkgraph->nodes[_linkgraph_from].edges[to].next_edge) { - size++; - } - - SlSetStructListLength(size); - for (NodeID to = _linkgraph_from; to != INVALID_NODE; to = _linkgraph->nodes[_linkgraph_from].edges[to].next_edge) { - SlObject(&_linkgraph->nodes[_linkgraph_from].edges[to], this->GetDescription()); + SlSetStructListLength(bn->edges.size()); + for (Edge &e : bn->edges) { + SlObject(&e, this->GetDescription()); } } void Load(Node *bn) const override { - uint16 max_size = _linkgraph->Size(); - _linkgraph->nodes[_linkgraph_from].edges.resize(max_size); - - if (IsSavegameVersionBefore(SLV_191)) { - /* We used to save the full matrix ... */ - for (NodeID to = 0; to < max_size; ++to) { - SlObject(&_linkgraph->nodes[_linkgraph_from].edges[to], this->GetLoadDescription()); + if (IsSavegameVersionBefore(SLV_LINKGRAPH_EDGES)) { + uint16 max_size = _linkgraph->Size(); + std::vector edges(max_size); + + if (IsSavegameVersionBefore(SLV_191)) { + /* We used to save the full matrix ... */ + for (NodeID to = 0; to < max_size; ++to) { + SlObject(&_linkgraph->nodes[_linkgraph_from].edges[to], this->GetLoadDescription()); + } + } else { + size_t used_size = IsSavegameVersionBefore(SLV_SAVELOAD_LIST_LENGTH) ? max_size : SlGetStructListLength(UINT16_MAX); + + /* ... but as that wasted a lot of space we save a sparse matrix now. */ + for (NodeID to = _linkgraph_from; to != INVALID_NODE; to = edges[to].dest_node) { + if (used_size == 0) SlErrorCorrupt("Link graph structure overflow"); + used_size--; + + if (to >= max_size) SlErrorCorrupt("Link graph structure overflow"); + SlObject(&edges[to], this->GetLoadDescription()); + } + + if (!IsSavegameVersionBefore(SLV_SAVELOAD_LIST_LENGTH) && used_size > 0) SlErrorCorrupt("Corrupted link graph"); } - return; - } - - size_t used_size = IsSavegameVersionBefore(SLV_SAVELOAD_LIST_LENGTH) ? max_size : SlGetStructListLength(UINT16_MAX); - /* ... but as that wasted a lot of space we save a sparse matrix now. */ - for (NodeID to = _linkgraph_from; to != INVALID_NODE; to = _linkgraph->nodes[_linkgraph_from].edges[to].next_edge) { - if (used_size == 0) SlErrorCorrupt("Link graph structure overflow"); - used_size--; - - if (to >= max_size) SlErrorCorrupt("Link graph structure overflow"); - SlObject(&_linkgraph->nodes[_linkgraph_from].edges[to], this->GetLoadDescription()); + /* Build edge list from edge matrix. */ + for (NodeID to = edges[_linkgraph_from].dest_node; to != INVALID_NODE; to = edges[to].dest_node) { + bn->edges.push_back(edges[to]); + bn->edges.back().dest_node = to; + } + /* Sort by destination. */ + std::sort(bn->edges.begin(), bn->edges.end()); + } else { + /* Edge data is now a simple vector and not any kind of matrix. */ + size_t size = SlGetStructListLength(UINT16_MAX); + for (size_t i = 0; i < size; i++) { + bn->edges.emplace_back(); + SlObject(&bn->edges.back(), this->GetLoadDescription()); + } } - - if (!IsSavegameVersionBefore(SLV_SAVELOAD_LIST_LENGTH) && used_size > 0) SlErrorCorrupt("Corrupted link graph"); } }; diff --git a/src/saveload/saveload.h b/src/saveload/saveload.h index f6672a9ad3..8e51962561 100644 --- a/src/saveload/saveload.h +++ b/src/saveload/saveload.h @@ -345,6 +345,8 @@ enum SaveLoadVersion : uint16 { SLV_MULTITRACK_LEVEL_CROSSINGS, ///< 302 PR#9931 v13.0 Multi-track level crossings. SLV_NEWGRF_ROAD_STOPS, ///< 303 PR#10144 NewGRF road stops. + SLV_LINKGRAPH_EDGES, ///< 303 PR#10314 Explicitly store link graph edges destination. + SL_MAX_VERSION, ///< Highest possible saveload version }; diff --git a/src/station.cpp b/src/station.cpp index aecd019901..3922af085d 100644 --- a/src/station.cpp +++ b/src/station.cpp @@ -106,7 +106,7 @@ Station::~Station() for (NodeID node = 0; node < lg->Size(); ++node) { Station *st = Station::Get((*lg)[node].Station()); st->goods[c].flows.erase(this->index); - if ((*lg)[node][this->goods[c].node].LastUpdate() != INVALID_DATE) { + if ((*lg)[node].HasEdgeTo(this->goods[c].node) && (*lg)[node][this->goods[c].node].LastUpdate() != INVALID_DATE) { st->goods[c].flows.DeleteFlows(this->index); RerouteCargo(st, c, this->index, st->index); } diff --git a/src/station_cmd.cpp b/src/station_cmd.cpp index 414c09812d..986e0f6f52 100644 --- a/src/station_cmd.cpp +++ b/src/station_cmd.cpp @@ -3720,11 +3720,11 @@ void DeleteStaleLinks(Station *from) LinkGraph *lg = LinkGraph::GetIfValid(ge.link_graph); if (lg == nullptr) continue; Node node = (*lg)[ge.node]; - for (EdgeIterator it(node.Begin()); it != node.End();) { + std::vector to_remove{}; + for (EdgeIterator it(node.Begin()); it != node.End(); ++it) { Edge edge = it->second; Station *to = Station::Get((*lg)[it->first].Station()); assert(to->goods[c].node == it->first); - ++it; // Do that before removing the edge. Anything else may crash. assert(_date >= edge.LastUpdate()); uint timeout = LinkGraph::MIN_TIMEOUT_DISTANCE + (DistanceManhattan(from->xy, to->xy) >> 3); if ((uint)(_date - edge.LastUpdate()) > timeout) { @@ -3778,7 +3778,7 @@ void DeleteStaleLinks(Station *from) if (!updated) { /* If it's still considered dead remove it. */ - node.RemoveEdge(to->goods[c].node); + to_remove.emplace_back(to->goods[c].node); ge.flows.DeleteFlows(to->index); RerouteCargo(from, c, to->index, from->index); } @@ -3790,6 +3790,9 @@ void DeleteStaleLinks(Station *from) edge.Release(); } } + /* Remove dead edges. */ + for (NodeID r : to_remove) node.RemoveEdge(r); + assert(_date >= lg->LastCompression()); if ((uint)(_date - lg->LastCompression()) > LinkGraph::COMPRESSION_INTERVAL) { lg->Compress(); From 4d3da0cf14ed8bdb31a0bd2923d1f05c99c92e26 Mon Sep 17 00:00:00 2001 From: Michael Lutz Date: Tue, 3 Jan 2023 01:27:16 +0100 Subject: [PATCH 18/47] Codechange: [Linkgraph] Drop node/edge wrappers from LinkGraphJob. --- src/linkgraph/demands.cpp | 26 ++-- src/linkgraph/flowmapper.cpp | 33 ++--- src/linkgraph/linkgraph.h | 8 +- src/linkgraph/linkgraphjob.cpp | 29 ++-- src/linkgraph/linkgraphjob.h | 234 +++++++++--------------------- src/linkgraph/linkgraphjob_base.h | 5 +- src/linkgraph/mcf.cpp | 35 ++--- 7 files changed, 136 insertions(+), 234 deletions(-) diff --git a/src/linkgraph/demands.cpp b/src/linkgraph/demands.cpp index 6fc529c497..7d7e182132 100644 --- a/src/linkgraph/demands.cpp +++ b/src/linkgraph/demands.cpp @@ -36,7 +36,7 @@ public: */ inline void AddNode(const Node &node) { - this->supply_sum += node.Supply(); + this->supply_sum += node.base.supply; } /** @@ -57,7 +57,7 @@ public: */ inline uint EffectiveSupply(const Node &from, const Node &to) { - return std::max(from.Supply() * std::max(1U, to.Supply()) * this->mod_size / 100 / this->demand_per_node, 1U); + return std::max(from.base.supply * std::max(1U, to.base.supply) * this->mod_size / 100 / this->demand_per_node, 1U); } /** @@ -69,7 +69,7 @@ public: */ inline bool HasDemandLeft(const Node &to) { - return (to.Supply() == 0 || to.UndeliveredSupply() > 0) && to.Demand() > 0; + return (to.base.supply == 0 || to.undelivered_supply > 0) && to.base.demand > 0; } void SetDemands(LinkGraphJob &job, NodeID from, NodeID to, uint demand_forw); @@ -108,7 +108,7 @@ public: */ inline uint EffectiveSupply(const Node &from, const Node &) { - return from.Supply(); + return from.base.supply; } /** @@ -116,7 +116,7 @@ public: * nodes always accept as long as their demand > 0. * @param to The node to be checked. */ - inline bool HasDemandLeft(const Node &to) { return to.Demand() > 0; } + inline bool HasDemandLeft(const Node &to) { return to.base.demand > 0; } }; /** @@ -129,9 +129,9 @@ public: */ void SymmetricScaler::SetDemands(LinkGraphJob &job, NodeID from_id, NodeID to_id, uint demand_forw) { - if (job[from_id].Demand() > 0) { + if (job[from_id].base.demand > 0) { uint demand_back = demand_forw * this->mod_size / 100; - uint undelivered = job[to_id].UndeliveredSupply(); + uint undelivered = job[to_id].undelivered_supply; if (demand_back > undelivered) { demand_back = undelivered; demand_forw = std::max(1U, demand_back * 100 / this->mod_size); @@ -170,11 +170,11 @@ void DemandCalculator::CalcDemand(LinkGraphJob &job, Tscaler scaler) for (NodeID node = 0; node < job.Size(); node++) { scaler.AddNode(job[node]); - if (job[node].Supply() > 0) { + if (job[node].base.supply > 0) { supplies.push(node); num_supplies++; } - if (job[node].Demand() > 0) { + if (job[node].base.demand > 0) { demands.push(node); num_demands++; } @@ -209,7 +209,7 @@ void DemandCalculator::CalcDemand(LinkGraphJob &job, Tscaler scaler) /* Scale the distance by mod_dist around max_distance */ int32 distance = this->max_distance - (this->max_distance - - (int32)DistanceMaxPlusManhattan(job[from_id].XY(), job[to_id].XY())) * + (int32)DistanceMaxPlusManhattan(job[from_id].base.xy, job[to_id].base.xy)) * this->mod_dist / 100; /* Scale the accuracy by distance around accuracy / 2 */ @@ -230,7 +230,7 @@ void DemandCalculator::CalcDemand(LinkGraphJob &job, Tscaler scaler) demand_forw = 1; } - demand_forw = std::min(demand_forw, job[from_id].UndeliveredSupply()); + demand_forw = std::min(demand_forw, job[from_id].undelivered_supply); scaler.SetDemands(job, from_id, to_id, demand_forw); @@ -240,10 +240,10 @@ void DemandCalculator::CalcDemand(LinkGraphJob &job, Tscaler scaler) num_demands--; } - if (job[from_id].UndeliveredSupply() == 0) break; + if (job[from_id].undelivered_supply == 0) break; } - if (job[from_id].UndeliveredSupply() != 0) { + if (job[from_id].undelivered_supply != 0) { supplies.push(from_id); } else { num_supplies--; diff --git a/src/linkgraph/flowmapper.cpp b/src/linkgraph/flowmapper.cpp index 2e0257fc1b..eb86d5e32e 100644 --- a/src/linkgraph/flowmapper.cpp +++ b/src/linkgraph/flowmapper.cpp @@ -19,35 +19,33 @@ void FlowMapper::Run(LinkGraphJob &job) const { for (NodeID node_id = 0; node_id < job.Size(); ++node_id) { - Node prev_node = job[node_id]; - StationID prev = prev_node.Station(); - PathList &paths = prev_node.Paths(); - for (PathList::iterator i = paths.begin(); i != paths.end(); ++i) { - Path *path = *i; + Node &prev_node = job[node_id]; + StationID prev = prev_node.base.station; + for (const Path *path : prev_node.paths) { uint flow = path->GetFlow(); if (flow == 0) break; - Node node = job[path->GetNode()]; - StationID via = node.Station(); - StationID origin = job[path->GetOrigin()].Station(); + Node &node = job[path->GetNode()]; + StationID via = node.base.station; + StationID origin = job[path->GetOrigin()].base.station; assert(prev != via && via != origin); /* Mark all of the flow for local consumption at "first". */ - node.Flows().AddFlow(origin, via, flow); + node.flows.AddFlow(origin, via, flow); if (prev != origin) { /* Pass some of the flow marked for local consumption at "prev" on * to this node. */ - prev_node.Flows().PassOnFlow(origin, via, flow); + prev_node.flows.PassOnFlow(origin, via, flow); } else { /* Prev node is origin. Simply add flow. */ - prev_node.Flows().AddFlow(origin, via, flow); + prev_node.flows.AddFlow(origin, via, flow); } } } for (NodeID node_id = 0; node_id < job.Size(); ++node_id) { /* Remove local consumption shares marked as invalid. */ - Node node = job[node_id]; - FlowStatMap &flows = node.Flows(); - flows.FinalizeLocalConsumption(node.Station()); + Node &node = job[node_id]; + FlowStatMap &flows = node.flows; + flows.FinalizeLocalConsumption(node.base.station); if (this->scale) { /* Scale by time the graph has been running without being compressed. Add 1 to avoid * division by 0 if spawn date == last compression date. This matches @@ -58,10 +56,7 @@ void FlowMapper::Run(LinkGraphJob &job) const } } /* Clear paths. */ - PathList &paths = node.Paths(); - for (PathList::iterator i = paths.begin(); i != paths.end(); ++i) { - delete *i; - } - paths.clear(); + for (Path *i : node.paths) delete i; + node.paths.clear(); } } diff --git a/src/linkgraph/linkgraph.h b/src/linkgraph/linkgraph.h index 6403168144..cd9c176930 100644 --- a/src/linkgraph/linkgraph.h +++ b/src/linkgraph/linkgraph.h @@ -50,6 +50,12 @@ public: BaseEdge(NodeID dest_node = INVALID_NODE); + /** + * Get edge's average travel time. + * @return Travel time, in ticks. + */ + uint32 TravelTime() const { return this->travel_time_sum / this->capacity; } + /** Comparison operator based on \c dest_node. */ bool operator <(const BaseEdge &rhs) const { @@ -117,7 +123,7 @@ public: * Get edge's average travel time. * @return Travel time, in ticks. */ - uint32 TravelTime() const { return this->edge.travel_time_sum / this->edge.capacity; } + uint32 TravelTime() const { return this->edge.TravelTime(); } /** * Get the date of the last update to the edge's unrestricted capacity. diff --git a/src/linkgraph/linkgraphjob.cpp b/src/linkgraph/linkgraphjob.cpp index 763485ce07..2a59ea9948 100644 --- a/src/linkgraph/linkgraphjob.cpp +++ b/src/linkgraph/linkgraphjob.cpp @@ -50,7 +50,7 @@ LinkGraphJob::LinkGraphJob(const LinkGraph &orig) : void LinkGraphJob::EraseFlows(NodeID from) { for (NodeID node_id = 0; node_id < this->Size(); ++node_id) { - (*this)[node_id].Flows().erase(from); + (*this)[node_id].flows.erase(from); } } @@ -103,10 +103,10 @@ LinkGraphJob::~LinkGraphJob() uint16 size = this->Size(); for (NodeID node_id = 0; node_id < size; ++node_id) { - Node from = (*this)[node_id]; + NodeAnnotation &from = this->nodes[node_id]; /* The station can have been deleted. Remove all flows originating from it then. */ - Station *st = Station::GetIfValid(from.Station()); + Station *st = Station::GetIfValid(from.base.station); if (st == nullptr) { this->EraseFlows(node_id); continue; @@ -121,23 +121,24 @@ LinkGraphJob::~LinkGraphJob() } LinkGraph *lg = LinkGraph::Get(ge.link_graph); - FlowStatMap &flows = from.Flows(); + FlowStatMap &flows = from.flows; - for (EdgeIterator it(from.Begin()); it != from.End(); ++it) { - if (it->second.Flow() == 0) continue; - StationID to = (*this)[it->first].Station(); + for (const auto &edge : from.edges) { + if (edge.Flow() == 0) continue; + NodeID dest_id = edge.base.dest_node; + StationID to = this->nodes[dest_id].base.station; Station *st2 = Station::GetIfValid(to); if (st2 == nullptr || st2->goods[this->Cargo()].link_graph != this->link_graph.index || - st2->goods[this->Cargo()].node != it->first || - !(*lg)[node_id].HasEdgeTo(it->first) || - (*lg)[node_id][it->first].LastUpdate() == INVALID_DATE) { + st2->goods[this->Cargo()].node != dest_id || + !(*lg)[node_id].HasEdgeTo(dest_id) || + (*lg)[node_id][dest_id].LastUpdate() == INVALID_DATE) { /* Edge has been removed. Delete flows. */ StationIDStack erased = flows.DeleteFlows(to); /* Delete old flows for source stations which have been deleted * from the new flows. This avoids flow cycles between old and * new flows. */ while (!erased.IsEmpty()) ge.flows.erase(erased.Pop()); - } else if ((*lg)[node_id][it->first].LastUnrestrictedUpdate() == INVALID_DATE) { + } else if ((*lg)[node_id][dest_id].LastUnrestrictedUpdate() == INVALID_DATE) { /* Edge is fully restricted. */ flows.RestrictFlows(to); } @@ -220,9 +221,9 @@ void Path::Fork(Path *base, uint cap, int free_cap, uint dist) uint Path::AddFlow(uint new_flow, LinkGraphJob &job, uint max_saturation) { if (this->parent != nullptr) { - LinkGraphJob::Edge edge = job[this->parent->node][this->node]; + LinkGraphJob::EdgeAnnotation edge = job[this->parent->node][this->node]; if (max_saturation != UINT_MAX) { - uint usable_cap = edge.Capacity() * max_saturation / 100; + uint usable_cap = edge.base.capacity * max_saturation / 100; if (usable_cap > edge.Flow()) { new_flow = std::min(new_flow, usable_cap - edge.Flow()); } else { @@ -231,7 +232,7 @@ uint Path::AddFlow(uint new_flow, LinkGraphJob &job, uint max_saturation) } new_flow = this->parent->AddFlow(new_flow, job, max_saturation); if (this->flow == 0 && new_flow > 0) { - job[this->parent->node].Paths().push_front(this); + job[this->parent->node].paths.push_front(this); } edge.AddFlow(new_flow); } diff --git a/src/linkgraph/linkgraphjob.h b/src/linkgraph/linkgraphjob.h index 980c27b067..5a25664ad2 100644 --- a/src/linkgraph/linkgraphjob.h +++ b/src/linkgraph/linkgraphjob.h @@ -28,7 +28,7 @@ extern LinkGraphJobPool _link_graph_job_pool; * Class for calculation jobs to be run on link graphs. */ class LinkGraphJob : public LinkGraphJobPool::PoolItem<&_link_graph_job_pool>{ -private: +public: /** * Demand between two nodes. */ @@ -41,74 +41,23 @@ private: * Annotation for a link graph edge. */ struct EdgeAnnotation { - uint flow; ///< Planned flow over this edge. - }; - - /** - * Annotation for a link graph node. - */ - struct NodeAnnotation { - uint undelivered_supply; ///< Amount of supply that hasn't been distributed yet. - PathList paths; ///< Paths through this node, sorted so that those with flow == 0 are in the back. - FlowStatMap flows; ///< Planned flows to other nodes. - - std::vector edges; ///< Annotations for all edges originating at this node. - std::vector demands; ///< Annotations for the demand to all other nodes. - - NodeAnnotation(const LinkGraph::BaseNode &node, size_t size) : undelivered_supply(node.supply), paths(), flows() - { - this->edges.resize(node.edges.size()); - this->demands.resize(size); - } - }; - - typedef std::vector NodeAnnotationVector; - - friend SaveLoadTable GetLinkGraphJobDesc(); - friend class LinkGraphSchedule; - -protected: - const LinkGraph link_graph; ///< Link graph to by analyzed. Is copied when job is started and mustn't be modified later. - const LinkGraphSettings settings; ///< Copy of _settings_game.linkgraph at spawn time. - std::thread thread; ///< Thread the job is running in or a default-constructed thread if it's running in the main thread. - Date join_date; ///< Date when the job is to be joined. - NodeAnnotationVector nodes; ///< Extra node data necessary for link graph calculation. - std::atomic job_completed; ///< Is the job still running. This is accessed by multiple threads and reads may be stale. - std::atomic job_aborted; ///< Has the job been aborted. This is accessed by multiple threads and reads may be stale. - - void EraseFlows(NodeID from); - void JoinThread(); - void SpawnThread(); + const LinkGraph::BaseEdge &base; ///< Reference to the edge that is annotated. -public: + uint flow; ///< Planned flow over this edge. - /** - * A job edge. Wraps a link graph edge and an edge annotation. The - * annotation can be modified, the edge is constant. - */ - class Edge : public LinkGraph::ConstEdge { - private: - EdgeAnnotation &anno; ///< Annotation being wrapped. - public: - /** - * Constructor. - * @param edge Link graph edge to be wrapped. - * @param anno Annotation to be wrapped. - */ - Edge(const LinkGraph::BaseEdge &edge, EdgeAnnotation &anno) : - LinkGraph::ConstEdge(edge), anno(anno) {} + EdgeAnnotation(const LinkGraph::BaseEdge &base) : base(base), flow(0) {} /** * Get the total flow on the edge. * @return Flow. */ - uint Flow() const { return this->anno.flow; } + uint Flow() const { return this->flow; } /** * Add some flow. * @param flow Flow to be added. */ - void AddFlow(uint flow) { this->anno.flow += flow; } + void AddFlow(uint flow) { this->flow += flow; } /** * Remove some flow. @@ -116,140 +65,71 @@ public: */ void RemoveFlow(uint flow) { - assert(flow <= this->anno.flow); - this->anno.flow -= flow; + assert(flow <= this->flow); + this->flow -= flow; + } + + friend inline bool operator <(NodeID dest, const EdgeAnnotation &rhs) + { + return dest < rhs.base.dest_node; } }; /** - * Iterator for job edges. + * Annotation for a link graph node. */ - class EdgeIterator : public LinkGraph::BaseEdgeIterator { - span base_anno; ///< Array of annotations to be (indirectly) iterated. - public: - /** - * Constructor. - * @param base Array of edges to be iterated. - * @param base_anno Array of annotations to be iterated. - * @param current Start offset of iteration. - */ - EdgeIterator(span base, span base_anno, bool end) : - LinkGraph::BaseEdgeIterator(base, end), - base_anno(base_anno) {} + struct NodeAnnotation { + const LinkGraph::BaseNode &base; ///< Reference to the node that is annotated. - EdgeIterator() : - LinkGraph::BaseEdgeIterator(span(), true), - base_anno() {} + uint undelivered_supply; ///< Amount of supply that hasn't been distributed yet. + PathList paths; ///< Paths through this node, sorted so that those with flow == 0 are in the back. + FlowStatMap flows; ///< Planned flows to other nodes. - /** - * Dereference. - * @return Pair of the edge currently pointed to and the ID of its - * other end. - */ - std::pair operator*() const + std::vector edges; ///< Annotations for all edges originating at this node. + std::vector demands; ///< Annotations for the demand to all other nodes. + + NodeAnnotation(const LinkGraph::BaseNode &node, size_t size) : base(node), undelivered_supply(node.supply), paths(), flows() { - return std::pair(this->base[this->current].dest_node, Edge(this->base[this->current], this->base_anno[this->current])); + this->edges.reserve(node.edges.size()); + for (auto &e : node.edges) this->edges.emplace_back(e); + this->demands.resize(size); } /** - * Dereference. Has to be repeated here as operator* is different than - * in LinkGraph::EdgeWrapper. - * @return Fake pointer to pair of NodeID/Edge. + * Retrieve an edge starting at this node. + * @param to Remote end of the edge. + * @return Edge between this node and "to". */ - FakePointer operator->() const { - return FakePointer(this->operator*()); + EdgeAnnotation &operator[](NodeID to) + { + auto it = std::find_if(this->edges.begin(), this->edges.end(), [=] (const EdgeAnnotation &e) { return e.base.dest_node == to; }); + assert(it != this->edges.end()); + return *it; } - }; - - /** - * Link graph job node. Wraps a constant link graph node and a modifiable - * node annotation. - */ - class Node : public LinkGraph::ConstNode { - private: - NodeAnnotation &node_anno; ///< Annotation being wrapped. - span edge_annos; ///< Edge annotations belonging to this node. - public: - - /** - * Constructor. - * @param lgj Job to take the node from. - * @param node ID of the node. - */ - Node (LinkGraphJob *lgj, NodeID node) : - LinkGraph::ConstNode(&lgj->link_graph, node), - node_anno(lgj->nodes[node]), edge_annos(lgj->nodes[node].edges) - {} /** - * Retrieve an edge starting at this node. Mind that this returns an - * object, not a reference. + * Retrieve an edge starting at this node. * @param to Remote end of the edge. * @return Edge between this node and "to". */ - Edge operator[](NodeID to) const + const EdgeAnnotation &operator[](NodeID to) const { - assert(this->HasEdgeTo(to)); - auto index = std::distance(this->node.edges.begin(), this->GetEdge(to)); - return Edge(this->node.edges[index], this->edge_annos[index]); + auto it = std::find_if(this->edges.begin(), this->edges.end(), [=] (const EdgeAnnotation &e) { return e.base.dest_node == to; }); + assert(it != this->edges.end()); + return *it; } - /** - * Iterator for the "begin" of the edge array. Only edges with capacity - * are iterated. The others are skipped. - * @return Iterator pointing to the first edge. - */ - EdgeIterator Begin() const { return EdgeIterator(this->node.edges, this->edge_annos, false); } - - /** - * Iterator for the "end" of the edge array. Only edges with capacity - * are iterated. The others are skipped. - * @return Iterator pointing beyond the last edge. - */ - EdgeIterator End() const { return EdgeIterator(this->node.edges, this->edge_annos, true); } - - /** - * Get amount of supply that hasn't been delivered, yet. - * @return Undelivered supply. - */ - uint UndeliveredSupply() const { return this->node_anno.undelivered_supply; } - - /** - * Get the flows running through this node. - * @return Flows. - */ - FlowStatMap &Flows() { return this->node_anno.flows; } - - /** - * Get a constant version of the flows running through this node. - * @return Flows. - */ - const FlowStatMap &Flows() const { return this->node_anno.flows; } - - /** - * Get the paths this node is part of. Paths are always expected to be - * sorted so that those with flow == 0 are in the back of the list. - * @return Paths. - */ - PathList &Paths() { return this->node_anno.paths; } - - /** - * Get a constant version of the paths this node is part of. - * @return Paths. - */ - const PathList &Paths() const { return this->node_anno.paths; } - /** * Get the transport demand between end the points of the edge. * @return Demand. */ - uint DemandTo(NodeID to) const { return this->node_anno.demands[to].demand; } + uint DemandTo(NodeID to) const { return this->demands[to].demand; } /** * Get the transport demand that hasn't been satisfied by flows, yet. * @return Unsatisfied demand. */ - uint UnsatisfiedDemandTo(NodeID to) const { return this->node_anno.demands[to].unsatisfied_demand; } + uint UnsatisfiedDemandTo(NodeID to) const { return this->demands[to].unsatisfied_demand; } /** * Satisfy some demand. @@ -257,8 +137,8 @@ public: */ void SatisfyDemandTo(NodeID to, uint demand) { - assert(demand <= this->node_anno.demands[to].unsatisfied_demand); - this->node_anno.demands[to].unsatisfied_demand -= demand; + assert(demand <= this->demands[to].unsatisfied_demand); + this->demands[to].unsatisfied_demand -= demand; } /** @@ -268,12 +148,32 @@ public: */ void DeliverSupply(NodeID to, uint amount) { - this->node_anno.undelivered_supply -= amount; - this->node_anno.demands[to].demand += amount; - this->node_anno.demands[to].unsatisfied_demand += amount; + this->undelivered_supply -= amount; + this->demands[to].demand += amount; + this->demands[to].unsatisfied_demand += amount; } }; +private: + typedef std::vector NodeAnnotationVector; + + friend SaveLoadTable GetLinkGraphJobDesc(); + friend class LinkGraphSchedule; + +protected: + const LinkGraph link_graph; ///< Link graph to by analyzed. Is copied when job is started and mustn't be modified later. + const LinkGraphSettings settings; ///< Copy of _settings_game.linkgraph at spawn time. + std::thread thread; ///< Thread the job is running in or a default-constructed thread if it's running in the main thread. + Date join_date; ///< Date when the job is to be joined. + NodeAnnotationVector nodes; ///< Extra node data necessary for link graph calculation. + std::atomic job_completed; ///< Is the job still running. This is accessed by multiple threads and reads may be stale. + std::atomic job_aborted; ///< Has the job been aborted. This is accessed by multiple threads and reads may be stale. + + void EraseFlows(NodeID from); + void JoinThread(); + void SpawnThread(); + +public: /** * Bare constructor, only for save/load. link_graph, join_date and actually * settings have to be brutally const-casted in order to populate them. @@ -337,7 +237,7 @@ public: * @param num ID of the node. * @return the Requested node. */ - inline Node operator[](NodeID num) { return Node(this, num); } + inline NodeAnnotation &operator[](NodeID num) { return this->nodes[num]; } /** * Get the size of the underlying link graph. diff --git a/src/linkgraph/linkgraphjob_base.h b/src/linkgraph/linkgraphjob_base.h index a05d6ef8d6..1a0c3667b2 100644 --- a/src/linkgraph/linkgraphjob_base.h +++ b/src/linkgraph/linkgraphjob_base.h @@ -14,8 +14,7 @@ #include "linkgraphjob.h" #include "linkgraphschedule.h" -typedef LinkGraphJob::Node Node; -typedef LinkGraphJob::Edge Edge; -typedef LinkGraphJob::EdgeIterator EdgeIterator; +typedef LinkGraphJob::NodeAnnotation Node; +typedef LinkGraphJob::EdgeAnnotation Edge; #endif /* LINKGRAPHJOB_BASE_H */ diff --git a/src/linkgraph/mcf.cpp b/src/linkgraph/mcf.cpp index 599bac3d52..c7b5d6d075 100644 --- a/src/linkgraph/mcf.cpp +++ b/src/linkgraph/mcf.cpp @@ -94,8 +94,9 @@ public: class GraphEdgeIterator { private: LinkGraphJob &job; ///< Job being executed - EdgeIterator i; ///< Iterator pointing to current edge. - EdgeIterator end; ///< Iterator pointing beyond last edge. + + std::vector::const_iterator i; ///< Iterator pointing to current edge. + std::vector::const_iterator end; ///< Iterator pointing beyond last edge. public: @@ -112,8 +113,8 @@ public: */ void SetNode(NodeID source, NodeID node) { - this->i = this->job[node].Begin(); - this->end = this->job[node].End(); + this->i = this->job[node].edges.cbegin(); + this->end = this->job[node].edges.cend(); } /** @@ -122,7 +123,7 @@ public: */ NodeID Next() { - return this->i != this->end ? (this->i++)->first : INVALID_NODE; + return this->i != this->end ? (this->i++)->base.dest_node : INVALID_NODE; } }; @@ -150,7 +151,7 @@ public: FlowEdgeIterator(LinkGraphJob &job) : job(job) { for (NodeID i = 0; i < job.Size(); ++i) { - StationID st = job[i].Station(); + StationID st = job[i].base.station; if (st >= this->station_to_node.size()) { this->station_to_node.resize(st + 1); } @@ -165,8 +166,8 @@ public: */ void SetNode(NodeID source, NodeID node) { - const FlowStatMap &flows = this->job[node].Flows(); - FlowStatMap::const_iterator it = flows.find(this->job[source].Station()); + const FlowStatMap &flows = this->job[node].flows; + FlowStatMap::const_iterator it = flows.find(this->job[source].base.station); if (it != flows.end()) { this->it = it->second.GetShares()->begin(); this->end = it->second.GetShares()->end(); @@ -275,8 +276,8 @@ void MultiCommodityFlow::Dijkstra(NodeID source_node, PathVector &paths) iter.SetNode(source_node, from); for (NodeID to = iter.Next(); to != INVALID_NODE; to = iter.Next()) { if (to == from) continue; // Not a real edge but a consumption sign. - Edge edge = this->job[from][to]; - uint capacity = edge.Capacity(); + const Edge &edge = this->job[from][to]; + uint capacity = edge.base.capacity; if (this->max_saturation != UINT_MAX) { capacity *= this->max_saturation; capacity /= 100; @@ -288,9 +289,9 @@ void MultiCommodityFlow::Dijkstra(NodeID source_node, PathVector &paths) bool express = IsCargoInClass(this->job.Cargo(), CC_PASSENGERS) || IsCargoInClass(this->job.Cargo(), CC_MAIL) || IsCargoInClass(this->job.Cargo(), CC_EXPRESS); - uint distance = DistanceMaxPlusManhattan(this->job[from].XY(), this->job[to].XY()) + 1; + uint distance = DistanceMaxPlusManhattan(this->job[from].base.xy, this->job[to].base.xy) + 1; /* Compute a default travel time from the distance and an average speed of 1 tile/day. */ - uint time = (edge.TravelTime() != 0) ? edge.TravelTime() + DAY_TICKS : distance * DAY_TICKS; + uint time = (edge.base.TravelTime() != 0) ? edge.base.TravelTime() + DAY_TICKS : distance * DAY_TICKS; uint distance_anno = express ? time : distance; Tannotation *dest = static_cast(paths[to]); @@ -381,7 +382,7 @@ void MCF1stPass::EliminateCycle(PathVector &path, Path *cycle_begin, uint flow) NodeID prev = cycle_begin->GetNode(); cycle_begin->ReduceFlow(flow); if (cycle_begin->GetFlow() == 0) { - PathList &node_paths = this->job[cycle_begin->GetParent()->GetNode()].Paths(); + PathList &node_paths = this->job[cycle_begin->GetParent()->GetNode()].paths; for (PathList::iterator i = node_paths.begin(); i != node_paths.end(); ++i) { if (*i == cycle_begin) { node_paths.erase(i); @@ -391,7 +392,7 @@ void MCF1stPass::EliminateCycle(PathVector &path, Path *cycle_begin, uint flow) } } cycle_begin = path[prev]; - Edge edge = this->job[prev][cycle_begin->GetNode()]; + Edge &edge = this->job[prev][cycle_begin->GetNode()]; edge.RemoveFlow(flow); } while (cycle_begin != cycle_end); } @@ -415,7 +416,7 @@ bool MCF1stPass::EliminateCycles(PathVector &path, NodeID origin_id, NodeID next if (at_next_pos == nullptr) { /* Summarize paths; add up the paths with the same source and next hop * in one path each. */ - PathList &paths = this->job[next_id].Paths(); + PathList &paths = this->job[next_id].paths; PathViaMap next_hops; for (PathList::iterator i = paths.begin(); i != paths.end();) { Path *new_child = *i; @@ -512,7 +513,7 @@ MCF1stPass::MCF1stPass(LinkGraphJob &job) : MultiCommodityFlow(job) /* First saturate the shortest paths. */ this->Dijkstra(source, paths); - Node src_node = job[source]; + Node &src_node = job[source]; bool source_demand_left = false; for (NodeID dest = 0; dest < size; ++dest) { if (src_node.UnsatisfiedDemandTo(dest) > 0) { @@ -559,7 +560,7 @@ MCF2ndPass::MCF2ndPass(LinkGraphJob &job) : MultiCommodityFlow(job) this->Dijkstra(source, paths); - Node src_node = job[source]; + Node &src_node = job[source]; bool source_demand_left = false; for (NodeID dest = 0; dest < size; ++dest) { Path *path = paths[dest]; From aab580e0ac52f89861b42bcde1367fb5e654953e Mon Sep 17 00:00:00 2001 From: Michael Lutz Date: Tue, 3 Jan 2023 13:48:42 +0100 Subject: [PATCH 19/47] Codechange: [Linkgraph] Drop node/edge wrappers from LinkGraph. --- src/linkgraph/linkgraph.cpp | 49 ++-- src/linkgraph/linkgraph.h | 394 ++++---------------------------- src/linkgraph/linkgraph_base.h | 10 +- src/linkgraph/linkgraph_gui.cpp | 12 +- src/linkgraph/linkgraphjob.cpp | 2 +- src/saveload/linkgraph_sl.cpp | 4 +- src/station.cpp | 2 +- src/station_cmd.cpp | 14 +- src/station_gui.cpp | 2 +- 9 files changed, 87 insertions(+), 402 deletions(-) diff --git a/src/linkgraph/linkgraph.cpp b/src/linkgraph/linkgraph.cpp index 1230204542..c84c62b7e6 100644 --- a/src/linkgraph/linkgraph.cpp +++ b/src/linkgraph/linkgraph.cpp @@ -164,12 +164,11 @@ NodeID LinkGraph::AddNode(const Station *st) * @param usage Usage to be added. * @param mode Update mode to be used. */ -void LinkGraph::Node::AddEdge(NodeID to, uint capacity, uint usage, uint32 travel_time, EdgeUpdateMode mode) +void LinkGraph::BaseNode::AddEdge(NodeID to, uint capacity, uint usage, uint32 travel_time, EdgeUpdateMode mode) { - assert(this->index != to); assert(!this->HasEdgeTo(to)); - BaseEdge &edge = *this->node.edges.emplace(std::upper_bound(this->node.edges.begin(), this->node.edges.end(), to), to); + BaseEdge &edge = *this->edges.emplace(std::upper_bound(this->edges.begin(), this->edges.end(), to), to); edge.capacity = capacity; edge.usage = usage; edge.travel_time_sum = static_cast(travel_time) * capacity; @@ -184,14 +183,14 @@ void LinkGraph::Node::AddEdge(NodeID to, uint capacity, uint usage, uint32 trave * @param usage Usage to be added. * @param mode Update mode to be used. */ -void LinkGraph::Node::UpdateEdge(NodeID to, uint capacity, uint usage, uint32 travel_time, EdgeUpdateMode mode) +void LinkGraph::BaseNode::UpdateEdge(NodeID to, uint capacity, uint usage, uint32 travel_time, EdgeUpdateMode mode) { assert(capacity > 0); assert(usage <= capacity); if (!this->HasEdgeTo(to)) { this->AddEdge(to, capacity, usage, travel_time, mode); } else { - (*this)[to].Update(capacity, usage, travel_time, mode); + this->GetEdge(to)->Update(capacity, usage, travel_time, mode); } } @@ -199,10 +198,10 @@ void LinkGraph::Node::UpdateEdge(NodeID to, uint capacity, uint usage, uint32 tr * Remove an outgoing edge from this node. * @param to ID of destination node. */ -void LinkGraph::Node::RemoveEdge(NodeID to) +void LinkGraph::BaseNode::RemoveEdge(NodeID to) { - auto [first, last] = std::equal_range(this->node.edges.begin(), this->node.edges.end(), to); - this->node.edges.erase(first, last); + auto [first, last] = std::equal_range(this->edges.begin(), this->edges.end(), to); + this->edges.erase(first, last); } /** @@ -215,33 +214,33 @@ void LinkGraph::Node::RemoveEdge(NodeID to) * @param travel_time Travel time to be added, in ticks. * @param mode Update mode to be applied. */ -void LinkGraph::Edge::Update(uint capacity, uint usage, uint32 travel_time, EdgeUpdateMode mode) +void LinkGraph::BaseEdge::Update(uint capacity, uint usage, uint32 travel_time, EdgeUpdateMode mode) { - assert(this->edge.capacity > 0); + assert(this->capacity > 0); assert(capacity >= usage); if (mode & EUM_INCREASE) { - if (this->edge.travel_time_sum == 0) { - this->edge.travel_time_sum = static_cast(this->edge.capacity + capacity) * travel_time; + if (this->travel_time_sum == 0) { + this->travel_time_sum = static_cast(this->capacity + capacity) * travel_time; } else if (travel_time == 0) { - this->edge.travel_time_sum += this->edge.travel_time_sum / this->edge.capacity * capacity; + this->travel_time_sum += this->travel_time_sum / this->capacity * capacity; } else { - this->edge.travel_time_sum += static_cast(travel_time) * capacity; + this->travel_time_sum += static_cast(travel_time) * capacity; } - this->edge.capacity += capacity; - this->edge.usage += usage; + this->capacity += capacity; + this->usage += usage; } else if (mode & EUM_REFRESH) { - if (this->edge.travel_time_sum == 0) { - this->edge.capacity = std::max(this->edge.capacity, capacity); - this->edge.travel_time_sum = static_cast(travel_time) * this->edge.capacity; - } else if (capacity > this->edge.capacity) { - this->edge.travel_time_sum = this->edge.travel_time_sum / this->edge.capacity * capacity; - this->edge.capacity = capacity; + if (this->travel_time_sum == 0) { + this->capacity = std::max(this->capacity, capacity); + this->travel_time_sum = static_cast(travel_time) * this->capacity; + } else if (capacity > this->capacity) { + this->travel_time_sum = this->travel_time_sum / this->capacity * capacity; + this->capacity = capacity; } - this->edge.usage = std::max(this->edge.usage, usage); + this->usage = std::max(this->usage, usage); } - if (mode & EUM_UNRESTRICTED) this->edge.last_unrestricted_update = _date; - if (mode & EUM_RESTRICTED) this->edge.last_restricted_update = _date; + if (mode & EUM_UNRESTRICTED) this->last_unrestricted_update = _date; + if (mode & EUM_RESTRICTED) this->last_restricted_update = _date; } /** diff --git a/src/linkgraph/linkgraph.h b/src/linkgraph/linkgraph.h index cd9c176930..d9f8f9d9a7 100644 --- a/src/linkgraph/linkgraph.h +++ b/src/linkgraph/linkgraph.h @@ -56,6 +56,16 @@ public: */ uint32 TravelTime() const { return this->travel_time_sum / this->capacity; } + /** + * Get the date of the last update to any part of the edge's capacity. + * @return Last update. + */ + Date LastUpdate() const { return std::max(this->last_unrestricted_update, this->last_restricted_update); } + + void Update(uint capacity, uint usage, uint32 time, EdgeUpdateMode mode); + void Restrict() { this->last_unrestricted_update = INVALID_DATE; } + void Release() { this->last_restricted_update = INVALID_DATE; } + /** Comparison operator based on \c dest_node. */ bool operator <(const BaseEdge &rhs) const { @@ -88,389 +98,71 @@ public: std::vector edges; ///< Sorted list of outgoing edges from this node. BaseNode(TileIndex xy = INVALID_TILE, StationID st = INVALID_STATION, uint demand = 0); - }; - - /** - * Wrapper for an edge (const or not) allowing retrieval, but no modification. - * @tparam Tedge Actual edge class, may be "const BaseEdge" or just "BaseEdge". - */ - template - class EdgeWrapper { - protected: - Tedge &edge; ///< Actual edge to be used. - - public: - - /** - * Wrap a an edge. - * @param edge Edge to be wrapped. - */ - EdgeWrapper (Tedge &edge) : edge(edge) {} - - /** - * Get edge's capacity. - * @return Capacity. - */ - uint Capacity() const { return this->edge.capacity; } - - /** - * Get edge's usage. - * @return Usage. - */ - uint Usage() const { return this->edge.usage; } - - /** - * Get edge's average travel time. - * @return Travel time, in ticks. - */ - uint32 TravelTime() const { return this->edge.TravelTime(); } - - /** - * Get the date of the last update to the edge's unrestricted capacity. - * @return Last update. - */ - Date LastUnrestrictedUpdate() const { return this->edge.last_unrestricted_update; } - - /** - * Get the date of the last update to the edge's restricted capacity. - * @return Last update. - */ - Date LastRestrictedUpdate() const { return this->edge.last_restricted_update; } - - /** - * Get the date of the last update to any part of the edge's capacity. - * @return Last update. - */ - Date LastUpdate() const { return std::max(this->edge.last_unrestricted_update, this->edge.last_restricted_update); } - }; - - /** - * Wrapper for a node (const or not) allowing retrieval, but no modification. - * @tparam Tnode Actual node class, may be "const BaseNode" or just "BaseNode". - */ - template - class NodeWrapper { - protected: - Tnode &node; ///< Node being wrapped. - NodeID index; ///< ID of wrapped node. - - auto GetEdge(NodeID dest) const - { - return std::lower_bound(this->node.edges.begin(), this->node.edges.end(), dest); - } - public: - - /** - * Wrap a node. - * @param node Node to be wrapped. - * @param index ID of node to be wrapped. - */ - NodeWrapper(Tnode &node, NodeID index) : node(node), index(index) {} - - /** - * Get supply of wrapped node. - * @return Supply. - */ - uint Supply() const { return this->node.supply; } - - /** - * Get demand of wrapped node. - * @return Demand. - */ - uint Demand() const { return this->node.demand; } /** - * Get ID of station belonging to wrapped node. - * @return ID of node's station. - */ - StationID Station() const { return this->node.station; } - - /** - * Get node's last update. - * @return Last update. - */ - Date LastUpdate() const { return this->node.last_update; } - - /** - * Get the location of the station associated with the node. - * @return Location of the station. - */ - TileIndex XY() const { return this->node.xy; } - - /** - * Check if an edge to a destination is present. - * @param dest Wanted edge destination. - * @return True if an edge is present. - */ - bool HasEdgeTo(NodeID dest) const - { - return std::binary_search(this->node.edges.begin(), this->node.edges.end(), dest); - } - }; - - /** - * Base class for iterating across outgoing edges of a node. Only the real - * edges (those with capacity) are iterated. The ones with only distance - * information are skipped. - * @tparam Tedge Actual edge class. May be "BaseEdge" or "const BaseEdge". - * @tparam Titer Actual iterator class. - */ - template - class BaseEdgeIterator { - protected: - span base; ///< Array of edges being iterated. - size_t current; ///< Current offset in edges array. - - /** - * A "fake" pointer to enable operator-> on temporaries. As the objects - * returned from operator* aren't references but real objects, we have - * to return something that implements operator->, but isn't a pointer - * from operator->. A fake pointer. - */ - class FakePointer : public std::pair { - public: - - /** - * Construct a fake pointer from a pair of NodeID and edge. - * @param pair Pair to be "pointed" to (in fact shallow-copied). - */ - FakePointer(const std::pair &pair) : std::pair(pair) {} - - /** - * Retrieve the pair by operator->. - * @return Pair being "pointed" to. - */ - std::pair *operator->() { return this; } - }; - - public: - /** - * Constructor. - * @param base Array of edges to be iterated. - * @param end Make the iterator the end sentinel? - */ - BaseEdgeIterator(span base, bool end) : base(base), current(end ? base.size() : 0) {} - - /** - * Prefix-increment. - * @return This. + * Update the node's supply and set last_update to the current date. + * @param supply Supply to be added. */ - Titer &operator++() + void UpdateSupply(uint supply) { - if (this->current < this->base.size()) this->current++; - return static_cast(*this); + this->supply += supply; + this->last_update = _date; } /** - * Postfix-increment. - * @return Version of this before increment. + * Update the node's location on the map. + * @param xy New location. */ - Titer operator++(int) + void UpdateLocation(TileIndex xy) { - Titer ret(static_cast(*this)); - ++(*this); - return ret; + this->xy = xy; } /** - * Compare with some other edge iterator. The other one may be of a - * child class. - * @tparam Tother Class of other iterator. - * @param other Instance of other iterator. - * @return If the iterators have the same edge array and current node. + * Set the node's demand. + * @param demand New demand for the node. */ - template - bool operator==(const Tother &other) + void SetDemand(uint demand) { - return this->base.data() == other.base.data() && this->current == other.current; + this->demand = demand; } - /** - * Compare for inequality with some other edge iterator. The other one - * may be of a child class. - * @tparam Tother Class of other iterator. - * @param other Instance of other iterator. - * @return If either the edge arrays or the current nodes differ. - */ - template - bool operator!=(const Tother &other) - { - return this->base.data() != other.base.data() || this->current != other.current; - } + void AddEdge(NodeID to, uint capacity, uint usage, uint32 time, EdgeUpdateMode mode); + void UpdateEdge(NodeID to, uint capacity, uint usage, uint32 time, EdgeUpdateMode mode); + void RemoveEdge(NodeID to); /** - * Dereference with operator*. - * @return Pair of current target NodeID and edge object. + * Check if an edge to a destination is present. + * @param dest Wanted edge destination. + * @return True if an edge is present. */ - std::pair operator*() const + bool HasEdgeTo(NodeID dest) const { - return std::pair(this->base[this->current].dest_node, Tedge_wrapper(this->base[this->current])); - } - - /** - * Dereference with operator->. - * @return Fake pointer to Pair of current target NodeID and edge object. - */ - FakePointer operator->() const { - return FakePointer(this->operator*()); + return std::binary_search(this->edges.begin(), this->edges.end(), dest); } - }; - - /** - * A constant edge class. - */ - typedef EdgeWrapper ConstEdge; - - /** - * An updatable edge class. - */ - class Edge : public EdgeWrapper { - public: - /** - * Constructor - * @param edge Edge to be wrapped. - */ - Edge(BaseEdge &edge) : EdgeWrapper(edge) {} - void Update(uint capacity, uint usage, uint32 time, EdgeUpdateMode mode); - void Restrict() { this->edge.last_unrestricted_update = INVALID_DATE; } - void Release() { this->edge.last_restricted_update = INVALID_DATE; } - }; - - /** - * An iterator for const edges. Cannot be typedef'ed because of - * template-reference to ConstEdgeIterator itself. - */ - class ConstEdgeIterator : public BaseEdgeIterator { - public: - /** - * Constructor. - * @param edges Array of edges to be iterated over. - * @param end Make the iterator the end sentinel? - */ - ConstEdgeIterator(span edges, bool end) : - BaseEdgeIterator(edges, end) {} - }; - - /** - * An iterator for non-const edges. Cannot be typedef'ed because of - * template-reference to EdgeIterator itself. - */ - class EdgeIterator : public BaseEdgeIterator { - public: - /** - * Constructor. - * @param edges Array of edges to be iterated over. - * @param end Make the iterator the end sentinel? - */ - EdgeIterator(span edges, bool end) : - BaseEdgeIterator(edges, end) {} - }; - - /** - * Constant node class. Only retrieval operations are allowed on both the - * node itself and its edges. - */ - class ConstNode : public NodeWrapper { - public: - /** - * Constructor. - * @param lg LinkGraph to get the node from. - * @param node ID of the node. - */ - ConstNode(const LinkGraph *lg, NodeID node) : NodeWrapper(lg->nodes[node], node) {} - /** - * Get a ConstEdge. This is not a reference as the wrapper objects are - * not actually persistent. - * @param to ID of end node of edge. - * @return Constant edge wrapper. - */ - ConstEdge operator[](NodeID to) const + BaseEdge &operator[](NodeID to) { assert(this->HasEdgeTo(to)); - return ConstEdge(*this->GetEdge(to)); + return *GetEdge(to); } - /** - * Get an iterator pointing to the start of the edges array. - * @return Constant edge iterator. - */ - ConstEdgeIterator Begin() const { return ConstEdgeIterator(this->node.edges, false); } - - /** - * Get an iterator pointing beyond the end of the edges array. - * @return Constant edge iterator. - */ - ConstEdgeIterator End() const { return ConstEdgeIterator(this->node.edges, true); } - }; - - /** - * Updatable node class. The node itself as well as its edges can be modified. - */ - class Node : public NodeWrapper { - public: - /** - * Constructor. - * @param lg LinkGraph to get the node from. - * @param node ID of the node. - */ - Node(LinkGraph *lg, NodeID node) : NodeWrapper(lg->nodes[node], node) {} - - /** - * Get an Edge. This is not a reference as the wrapper objects are not - * actually persistent. - * @param to ID of end node of edge. - * @return Edge wrapper. - */ - Edge operator[](NodeID to) + const BaseEdge &operator[](NodeID to) const { assert(this->HasEdgeTo(to)); - return Edge(*this->GetEdge(to)); - } - - /** - * Get an iterator pointing to the start of the edges array. - * @return Edge iterator. - */ - EdgeIterator Begin() { return EdgeIterator(this->node.edges, false); } - - /** - * Get an iterator pointing beyond the end of the edges array. - * @return Constant edge iterator. - */ - EdgeIterator End() { return EdgeIterator(this->node.edges, true); } - - /** - * Update the node's supply and set last_update to the current date. - * @param supply Supply to be added. - */ - void UpdateSupply(uint supply) - { - this->node.supply += supply; - this->node.last_update = _date; + return *GetEdge(to); } - /** - * Update the node's location on the map. - * @param xy New location. - */ - void UpdateLocation(TileIndex xy) + private: + std::vector::iterator GetEdge(NodeID dest) { - this->node.xy = xy; + return std::lower_bound(this->edges.begin(), this->edges.end(), dest); } - /** - * Set the node's demand. - * @param demand New demand for the node. - */ - void SetDemand(uint demand) + std::vector::const_iterator GetEdge(NodeID dest) const { - this->node.demand = demand; + return std::lower_bound(this->edges.begin(), this->edges.end(), dest); } - - void AddEdge(NodeID to, uint capacity, uint usage, uint32 time, EdgeUpdateMode mode); - void UpdateEdge(NodeID to, uint capacity, uint usage, uint32 time, EdgeUpdateMode mode); - void RemoveEdge(NodeID to); }; typedef std::vector NodeVector; @@ -523,14 +215,14 @@ public: * @param num ID of the node. * @return the Requested node. */ - inline Node operator[](NodeID num) { return Node(this, num); } + inline BaseNode &operator[](NodeID num) { return this->nodes[num]; } /** * Get a const reference to a node with the specified id. * @param num ID of the node. * @return the Requested node. */ - inline ConstNode operator[](NodeID num) const { return ConstNode(this, num); } + inline const BaseNode &operator[](NodeID num) const { return this->nodes[num]; } /** * Get the current size of the component. @@ -564,8 +256,6 @@ public: void RemoveNode(NodeID id); protected: - friend class LinkGraph::ConstNode; - friend class LinkGraph::Node; friend SaveLoadTable GetLinkGraphDesc(); friend SaveLoadTable GetLinkGraphJobDesc(); friend class SlLinkgraphNode; diff --git a/src/linkgraph/linkgraph_base.h b/src/linkgraph/linkgraph_base.h index eb86c0c64e..0eed6063af 100644 --- a/src/linkgraph/linkgraph_base.h +++ b/src/linkgraph/linkgraph_base.h @@ -13,13 +13,11 @@ #include "linkgraph.h" #include "linkgraphschedule.h" -typedef LinkGraph::Node Node; -typedef LinkGraph::Edge Edge; -typedef LinkGraph::EdgeIterator EdgeIterator; +typedef LinkGraph::BaseNode Node; +typedef LinkGraph::BaseEdge Edge; -typedef LinkGraph::ConstNode ConstNode; -typedef LinkGraph::ConstEdge ConstEdge; -typedef LinkGraph::ConstEdgeIterator ConstEdgeIterator; +typedef const LinkGraph::BaseNode ConstNode; +typedef const LinkGraph::BaseEdge ConstEdge; #endif /* LINKGRAPH_BASE_H */ diff --git a/src/linkgraph/linkgraph_gui.cpp b/src/linkgraph/linkgraph_gui.cpp index 2f0c941553..73cdf9ba48 100644 --- a/src/linkgraph/linkgraph_gui.cpp +++ b/src/linkgraph/linkgraph_gui.cpp @@ -88,10 +88,10 @@ void LinkGraphOverlay::RebuildCache() if (!LinkGraph::IsValidID(sta->goods[c].link_graph)) continue; const LinkGraph &lg = *LinkGraph::Get(sta->goods[c].link_graph); - ConstNode from_node = lg[sta->goods[c].node]; - supply += lg.Monthly(from_node.Supply()); - for (ConstEdgeIterator i = from_node.Begin(); i != from_node.End(); ++i) { - StationID to = lg[i->first].Station(); + ConstNode &from_node = lg[sta->goods[c].node]; + supply += lg.Monthly(from_node.supply); + for (const Edge &edge : from_node.edges) { + StationID to = lg[edge.dest_node].station; assert(from != to); if (!Station::IsValidID(to) || seen_links.find(to) != seen_links.end()) { continue; @@ -218,8 +218,8 @@ void LinkGraphOverlay::AddLinks(const Station *from, const Station *to) } const LinkGraph &lg = *LinkGraph::Get(ge.link_graph); if (lg[ge.node].HasEdgeTo(to->goods[c].node)) { - ConstEdge edge = lg[ge.node][to->goods[c].node]; - this->AddStats(c, lg.Monthly(edge.Capacity()), lg.Monthly(edge.Usage()), + ConstEdge &edge = lg[ge.node][to->goods[c].node]; + this->AddStats(c, lg.Monthly(edge.capacity), lg.Monthly(edge.usage), ge.flows.GetFlowVia(to->index), edge.TravelTime() / DAY_TICKS, from->owner == OWNER_NONE || to->owner == OWNER_NONE, diff --git a/src/linkgraph/linkgraphjob.cpp b/src/linkgraph/linkgraphjob.cpp index 2a59ea9948..5902e01e73 100644 --- a/src/linkgraph/linkgraphjob.cpp +++ b/src/linkgraph/linkgraphjob.cpp @@ -138,7 +138,7 @@ LinkGraphJob::~LinkGraphJob() * from the new flows. This avoids flow cycles between old and * new flows. */ while (!erased.IsEmpty()) ge.flows.erase(erased.Pop()); - } else if ((*lg)[node_id][dest_id].LastUnrestrictedUpdate() == INVALID_DATE) { + } else if ((*lg)[node_id][dest_id].last_restricted_update == INVALID_DATE) { /* Edge is fully restricted. */ flows.RestrictFlows(to); } diff --git a/src/saveload/linkgraph_sl.cpp b/src/saveload/linkgraph_sl.cpp index c3abf88c32..fc6059f97a 100644 --- a/src/saveload/linkgraph_sl.cpp +++ b/src/saveload/linkgraph_sl.cpp @@ -234,7 +234,7 @@ void AfterLoadLinkGraphs() if (IsSavegameVersionBefore(SLV_191)) { for (LinkGraph *lg : LinkGraph::Iterate()) { for (NodeID node_id = 0; node_id < lg->Size(); ++node_id) { - const Station *st = Station::GetIfValid((*lg)[node_id].Station()); + const Station *st = Station::GetIfValid((*lg)[node_id].station); if (st != nullptr) (*lg)[node_id].UpdateLocation(st->xy); } } @@ -242,7 +242,7 @@ void AfterLoadLinkGraphs() for (LinkGraphJob *lgj : LinkGraphJob::Iterate()) { LinkGraph *lg = &(const_cast(lgj->Graph())); for (NodeID node_id = 0; node_id < lg->Size(); ++node_id) { - const Station *st = Station::GetIfValid((*lg)[node_id].Station()); + const Station *st = Station::GetIfValid((*lg)[node_id].station); if (st != nullptr) (*lg)[node_id].UpdateLocation(st->xy); } } diff --git a/src/station.cpp b/src/station.cpp index 3922af085d..a9ccfc4329 100644 --- a/src/station.cpp +++ b/src/station.cpp @@ -104,7 +104,7 @@ Station::~Station() if (lg == nullptr) continue; for (NodeID node = 0; node < lg->Size(); ++node) { - Station *st = Station::Get((*lg)[node].Station()); + Station *st = Station::Get((*lg)[node].station); st->goods[c].flows.erase(this->index); if ((*lg)[node].HasEdgeTo(this->goods[c].node) && (*lg)[node][this->goods[c].node].LastUpdate() != INVALID_DATE) { st->goods[c].flows.DeleteFlows(this->index); diff --git a/src/station_cmd.cpp b/src/station_cmd.cpp index 986e0f6f52..526bbd6c8c 100644 --- a/src/station_cmd.cpp +++ b/src/station_cmd.cpp @@ -3719,12 +3719,10 @@ void DeleteStaleLinks(Station *from) GoodsEntry &ge = from->goods[c]; LinkGraph *lg = LinkGraph::GetIfValid(ge.link_graph); if (lg == nullptr) continue; - Node node = (*lg)[ge.node]; std::vector to_remove{}; - for (EdgeIterator it(node.Begin()); it != node.End(); ++it) { - Edge edge = it->second; - Station *to = Station::Get((*lg)[it->first].Station()); - assert(to->goods[c].node == it->first); + for (Edge &edge : (*lg)[ge.node].edges) { + Station *to = Station::Get((*lg)[edge.dest_node].station); + assert(to->goods[c].node == edge.dest_node); assert(_date >= edge.LastUpdate()); uint timeout = LinkGraph::MIN_TIMEOUT_DISTANCE + (DistanceManhattan(from->xy, to->xy) >> 3); if ((uint)(_date - edge.LastUpdate()) > timeout) { @@ -3782,16 +3780,16 @@ void DeleteStaleLinks(Station *from) ge.flows.DeleteFlows(to->index); RerouteCargo(from, c, to->index, from->index); } - } else if (edge.LastUnrestrictedUpdate() != INVALID_DATE && (uint)(_date - edge.LastUnrestrictedUpdate()) > timeout) { + } else if (edge.last_unrestricted_update != INVALID_DATE && (uint)(_date - edge.last_unrestricted_update) > timeout) { edge.Restrict(); ge.flows.RestrictFlows(to->index); RerouteCargo(from, c, to->index, from->index); - } else if (edge.LastRestrictedUpdate() != INVALID_DATE && (uint)(_date - edge.LastRestrictedUpdate()) > timeout) { + } else if (edge.last_restricted_update != INVALID_DATE && (uint)(_date - edge.last_restricted_update) > timeout) { edge.Release(); } } /* Remove dead edges. */ - for (NodeID r : to_remove) node.RemoveEdge(r); + for (NodeID r : to_remove) (*lg)[ge.node].RemoveEdge(r); assert(_date >= lg->LastCompression()); if ((uint)(_date - lg->LastCompression()) > LinkGraph::COMPRESSION_INTERVAL) { diff --git a/src/station_gui.cpp b/src/station_gui.cpp index 6f3cf7076f..99a55e48f8 100644 --- a/src/station_gui.cpp +++ b/src/station_gui.cpp @@ -1837,7 +1837,7 @@ struct StationViewWindow : public Window { const LinkGraph *lg = LinkGraph::GetIfValid(ge->link_graph); SetDParam(0, cs->name); - SetDParam(1, lg != nullptr ? lg->Monthly((*lg)[ge->node].Supply()) : 0); + SetDParam(1, lg != nullptr ? lg->Monthly((*lg)[ge->node].supply) : 0); SetDParam(2, STR_CARGO_RATING_APPALLING + (ge->rating >> 5)); SetDParam(3, ToPercent8(ge->rating)); DrawString(tr.Indent(WidgetDimensions::scaled.hsep_indent, rtl), STR_STATION_VIEW_CARGO_SUPPLY_RATING); From 6eabbaa751c24c447668576c42331318d7a0c880 Mon Sep 17 00:00:00 2001 From: SamuXarick <43006711+SamuXarick@users.noreply.github.com> Date: Sun, 12 Feb 2023 14:29:18 +0000 Subject: [PATCH 20/47] Fix #10469, 5e14a20: [Script] League Table rating element is a int64 everywhere else --- src/league_base.h | 2 +- src/saveload/league_sl.cpp | 15 ++++++++------- src/saveload/saveload.h | 3 +-- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/src/league_base.h b/src/league_base.h index 8a70db4ca2..6195d2c3ee 100644 --- a/src/league_base.h +++ b/src/league_base.h @@ -30,7 +30,7 @@ extern LeagueTablePool _league_table_pool; **/ struct LeagueTableElement : LeagueTableElementPool::PoolItem<&_league_table_element_pool> { LeagueTableID table; ///< Id of the table which this element belongs to - uint64 rating; ///< Value that determines ordering of elements in the table (higher=better) + int64 rating; ///< Value that determines ordering of elements in the table (higher=better) CompanyID company; ///< Company Id to show the color blob for or INVALID_COMPANY std::string text; ///< Text of the element std::string score; ///< String representation of the score associated with the element diff --git a/src/saveload/league_sl.cpp b/src/saveload/league_sl.cpp index 122d7ccee5..9945b6c298 100644 --- a/src/saveload/league_sl.cpp +++ b/src/saveload/league_sl.cpp @@ -16,13 +16,14 @@ #include "../safeguards.h" static const SaveLoad _league_table_elements_desc[] = { - SLE_VAR(LeagueTableElement, table, SLE_UINT8), - SLE_VAR(LeagueTableElement, rating, SLE_UINT64), - SLE_VAR(LeagueTableElement, company, SLE_UINT8), - SLE_SSTR(LeagueTableElement, text, SLE_STR | SLF_ALLOW_CONTROL), - SLE_SSTR(LeagueTableElement, score, SLE_STR | SLF_ALLOW_CONTROL), - SLE_VAR(LeagueTableElement, link.type, SLE_UINT8), - SLE_VAR(LeagueTableElement, link.target, SLE_UINT32), + SLE_VAR(LeagueTableElement, table, SLE_UINT8), + SLE_CONDVAR(LeagueTableElement, rating, SLE_FILE_U64 | SLE_VAR_I64, SL_MIN_VERSION, SLV_LINKGRAPH_EDGES), + SLE_CONDVAR(LeagueTableElement, rating, SLE_INT64, SLV_LINKGRAPH_EDGES, SL_MAX_VERSION), + SLE_VAR(LeagueTableElement, company, SLE_UINT8), + SLE_SSTR(LeagueTableElement, text, SLE_STR | SLF_ALLOW_CONTROL), + SLE_SSTR(LeagueTableElement, score, SLE_STR | SLF_ALLOW_CONTROL), + SLE_VAR(LeagueTableElement, link.type, SLE_UINT8), + SLE_VAR(LeagueTableElement, link.target, SLE_UINT32), }; struct LEAEChunkHandler : ChunkHandler { diff --git a/src/saveload/saveload.h b/src/saveload/saveload.h index 8e51962561..61d4047b15 100644 --- a/src/saveload/saveload.h +++ b/src/saveload/saveload.h @@ -344,8 +344,7 @@ enum SaveLoadVersion : uint16 { SLV_LAST_LOADING_TICK, ///< 301 PR#9693 Store tick of last loading for vehicles. SLV_MULTITRACK_LEVEL_CROSSINGS, ///< 302 PR#9931 v13.0 Multi-track level crossings. SLV_NEWGRF_ROAD_STOPS, ///< 303 PR#10144 NewGRF road stops. - - SLV_LINKGRAPH_EDGES, ///< 303 PR#10314 Explicitly store link graph edges destination. + SLV_LINKGRAPH_EDGES, ///< 304 PR#10314 Explicitly store link graph edges destination, PR#10471 int64 instead of uint64 league rating SL_MAX_VERSION, ///< Highest possible saveload version }; From b0542c8c498eb6012d87e8c3f0f0f4b7ae8a7b29 Mon Sep 17 00:00:00 2001 From: dP Date: Mon, 27 Feb 2023 01:39:44 +0400 Subject: [PATCH 21/47] Codechange: Don't store tree counter in the map array (#10018) --- docs/landscape.html | 2 -- regression/regression/result.txt | 2 +- src/saveload/afterload.cpp | 12 +++++++-- src/tree_cmd.cpp | 17 +++++------- src/tree_map.h | 44 -------------------------------- 5 files changed, 18 insertions(+), 59 deletions(-) diff --git a/docs/landscape.html b/docs/landscape.html index 0f9a18bbf5..99243101e6 100644 --- a/docs/landscape.html +++ b/docs/landscape.html @@ -807,8 +807,6 @@
  • m2 bits 5..4: ground density
  • -
  • m2 bits 3..0: update counter, incremented on every periodic processing.
    - on wraparound the growth status is updated (or, if it's 3, a random action is taken)
  • m3 bits 7..0: type of trees: diff --git a/regression/regression/result.txt b/regression/regression/result.txt index 13a770a8aa..9942857cfb 100644 --- a/regression/regression/result.txt +++ b/regression/regression/result.txt @@ -588,7 +588,7 @@ ERROR: IsEnd() is invalid as Begin() is never called SetName(): false GetLastErrorString(): ERR_NAME_IS_NOT_UNIQUE GetName(): Regression - GetPresidentName(): E. McAlpine + GetPresidentName(): J. Green SetPresidentName(): true GetPresidentName(): Regression AI GetBankBalance(): 100000 diff --git a/src/saveload/afterload.cpp b/src/saveload/afterload.cpp index 854bb5b56f..4c3e942255 100644 --- a/src/saveload/afterload.cpp +++ b/src/saveload/afterload.cpp @@ -2334,8 +2334,7 @@ bool AfterLoadGame() if (IsTileType(t, MP_TREES)) { uint density = GB(_m[t].m2, 6, 2); uint ground = GB(_m[t].m2, 4, 2); - uint counter = GB(_m[t].m2, 0, 4); - _m[t].m2 = ground << 6 | density << 4 | counter; + _m[t].m2 = ground << 6 | density << 4; } } } @@ -3190,6 +3189,15 @@ bool AfterLoadGame() } } + if (IsSavegameVersionBeforeOrAt(SLV_MULTITRACK_LEVEL_CROSSINGS)) { + /* Reset unused tree counters to reduce the savegame size. */ + for (TileIndex t = 0; t < map_size; t++) { + if (IsTileType(t, MP_TREES)) { + SB(_m[t].m2, 0, 4, 0); + } + } + } + /* Refresh all level crossings to bar adjacent crossing tiles, if needed. */ for (TileIndex tile = 0; tile < Map::Size(); tile++) { if (IsLevelCrossingTile(tile)) UpdateLevelCrossing(tile, false); diff --git a/src/tree_cmd.cpp b/src/tree_cmd.cpp index 7ed2f163c6..c42a7ff003 100644 --- a/src/tree_cmd.cpp +++ b/src/tree_cmd.cpp @@ -174,9 +174,6 @@ static void PlaceTree(TileIndex tile, uint32 r) if (ground != TREE_GROUND_SNOW_DESERT && ground != TREE_GROUND_ROUGH_SNOW && ground != TREE_GROUND_SHORE) { SetTreeGroundDensity(tile, (TreeGround)GB(r, 28, 1), 3); } - - /* Set the counter to a random start value */ - SetTreeCounter(tile, (TreeGround)GB(r, 24, 4)); } } @@ -710,10 +707,14 @@ static void TileLoop_Trees(TileIndex tile) AmbientSoundEffect(tile); - uint treeCounter = GetTreeCounter(tile); + /* _tick_counter is incremented by 256 between each call, so ignore lower 8 bits. + * Also, we use a simple hash to spread the updates evenly over the map. + * 11 and 9 are just some co-prime numbers for better spread. + */ + uint32 cycle = 11 * TileX(tile) + 9 * TileY(tile) + (_tick_counter >> 8); /* Handle growth of grass (under trees/on MP_TREES tiles) at every 8th processings, like it's done for grass on MP_CLEAR tiles. */ - if ((treeCounter & 7) == 7 && GetTreeGround(tile) == TREE_GROUND_GRASS) { + if ((cycle & 7) == 7 && GetTreeGround(tile) == TREE_GROUND_GRASS) { uint density = GetTreeDensity(tile); if (density < 3) { SetTreeGroundDensity(tile, TREE_GROUND_GRASS, density + 1); @@ -723,11 +724,7 @@ static void TileLoop_Trees(TileIndex tile) if (_settings_game.construction.extra_tree_placement == ETP_NO_GROWTH_NO_SPREAD) return; - if (GetTreeCounter(tile) < 15) { - AddTreeCounter(tile, 1); - return; - } - SetTreeCounter(tile, 0); + if ((cycle & 15) != 15) return; switch (GetTreeGrowth(tile)) { case 3: // regular sized tree diff --git a/src/tree_map.h b/src/tree_map.h index e8f68d825d..a01968f08f 100644 --- a/src/tree_map.h +++ b/src/tree_map.h @@ -215,50 +215,6 @@ static inline void SetTreeGrowth(TileIndex t, uint g) SB(_m[t].m5, 0, 3, g); } -/** - * Get the tick counter of a tree tile. - * - * Returns the saved tick counter of a given tile. - * - * @param t The tile to get the counter value from - * @pre Tile must be of type MP_TREES - */ -static inline uint GetTreeCounter(TileIndex t) -{ - assert(IsTileType(t, MP_TREES)); - return GB(_m[t].m2, 0, 4); -} - -/** - * Add a value on the tick counter of a tree-tile - * - * This function adds a value on the tick counter of a tree-tile. - * - * @param t The tile to add the value on - * @param a The value to add on the tick counter - * @pre Tile must be of type MP_TREES - */ -static inline void AddTreeCounter(TileIndex t, int a) -{ - assert(IsTileType(t, MP_TREES)); // XXX incomplete - _m[t].m2 += a; -} - -/** - * Set the tick counter for a tree-tile - * - * This function sets directly the tick counter for a tree-tile. - * - * @param t The tile to set the tick counter - * @param c The new tick counter value - * @pre Tile must be of type MP_TREES - */ -static inline void SetTreeCounter(TileIndex t, uint c) -{ - assert(IsTileType(t, MP_TREES)); // XXX incomplete - SB(_m[t].m2, 0, 4, c); -} - /** * Make a tree-tile. * From 6fcc8727f5c14c28b988b4755f6ee0ebbbefd18d Mon Sep 17 00:00:00 2001 From: Patric Stout Date: Sun, 26 Feb 2023 22:36:33 +0100 Subject: [PATCH 22/47] Change: [Actions] cancel previous run if pushing new PR Lately we had a few times that people pushed to their PR branch a few times to make small changes. Sadly, this triggers all CIs every time, which takes ~20 minutes. As we are limited in the amount of runners we get assigned to us, this means all other CI, even for other repositories within OpenTTD, are delayed too. We can avoid this by simply cancelling old runs when a new PR is pushed. There is a downside: sometimes people already push a new commit, but still want to know if the old one passed. That will no longer be possible with this change. --- .github/workflows/ci-build.yml | 4 ++++ .github/workflows/codeql.yml | 4 ++++ .github/workflows/commit-checker.yml | 4 ++++ .github/workflows/unused-strings.yml | 4 ++++ 4 files changed, 16 insertions(+) diff --git a/.github/workflows/ci-build.yml b/.github/workflows/ci-build.yml index e5b0947315..a9c868242e 100644 --- a/.github/workflows/ci-build.yml +++ b/.github/workflows/ci-build.yml @@ -9,6 +9,10 @@ on: env: CTEST_OUTPUT_ON_FAILURE: 1 +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: ${{ github.ref != 'refs/heads/master' }} + jobs: emscripten: name: Emscripten diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index 153a4706c7..936c4c92d7 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -9,6 +9,10 @@ on: branches: - master +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: ${{ github.ref != 'refs/heads/master' }} + jobs: analyze: name: Analyze diff --git a/.github/workflows/commit-checker.yml b/.github/workflows/commit-checker.yml index 91fc54b104..1407757fc5 100644 --- a/.github/workflows/commit-checker.yml +++ b/.github/workflows/commit-checker.yml @@ -3,6 +3,10 @@ name: Commit checker on: pull_request: +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: ${{ github.ref != 'refs/heads/master' }} + jobs: commit-checker: name: Commit checker diff --git a/.github/workflows/unused-strings.yml b/.github/workflows/unused-strings.yml index 6026671307..08a0cb26e7 100644 --- a/.github/workflows/unused-strings.yml +++ b/.github/workflows/unused-strings.yml @@ -3,6 +3,10 @@ name: Unused strings on: pull_request: +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: ${{ github.ref != 'refs/heads/master' }} + jobs: unused-strings: name: Unused strings From eabb9db0bb67df758da008ef59aeefb81b15a194 Mon Sep 17 00:00:00 2001 From: Chenshu Yu <36859142+wholepuncher@users.noreply.github.com> Date: Mon, 27 Feb 2023 03:59:42 -0500 Subject: [PATCH 23/47] Fix #9810: 'Rebuilding' a through road stop costs money (#9852) --- src/station_cmd.cpp | 114 +++++++++++++++++++++++++++++++++++++++---- src/waypoint_cmd.cpp | 18 ++++++- 2 files changed, 121 insertions(+), 11 deletions(-) diff --git a/src/station_cmd.cpp b/src/station_cmd.cpp index 526bbd6c8c..802c0fb8cb 100644 --- a/src/station_cmd.cpp +++ b/src/station_cmd.cpp @@ -1244,6 +1244,59 @@ static void RestoreTrainReservation(Train *v) if (IsRailStationTile(v->tile)) SetRailStationPlatformReservation(v->tile, TrackdirToExitdir(ReverseTrackdir(v->GetVehicleTrackdir())), true); } +/** + * Calculates cost of new rail stations within the area. + * @param tile_area Area to check. + * @param flags Operation to perform. + * @param axis Rail station axis. + * @param station StationID to be queried and returned if available. + * @param rt The rail type to check for (overbuilding rail stations over rail). + * @param affected_vehicles List of trains with PBS reservations on the tiles + * @param spec_class Station class. + * @param spec_index Index into the station class. + * @param plat_len Platform length. + * @param numtracks Number of platforms. + * @return The cost in case of success, or an error code if it failed. + */ +static CommandCost CalculateRailStationCost(TileArea tile_area, DoCommandFlag flags, Axis axis, StationID *station, RailType rt, std::vector &affected_vehicles, StationClassID spec_class, byte spec_index, byte plat_len, byte numtracks) +{ + CommandCost cost(EXPENSES_CONSTRUCTION); + bool success = false; + bool length_price_ready = true; + byte tracknum = 0; + for (TileIndex cur_tile : tile_area) { + /* Clear the land below the station. */ + CommandCost ret = CheckFlatLandRailStation(TileArea(cur_tile, 1, 1), flags, axis, station, rt, affected_vehicles, spec_class, spec_index, plat_len, numtracks); + if (ret.Failed()) return ret; + + /* Only add _price[PR_BUILD_STATION_RAIL_LENGTH] once for each valid plat_len. */ + if (tracknum == numtracks) { + length_price_ready = true; + tracknum = 0; + } else { + tracknum++; + } + + /* AddCost for new or rotated rail stations. */ + if (!IsRailStationTile(cur_tile) || (IsRailStationTile(cur_tile) && GetRailStationAxis(cur_tile) != axis)) { + + cost.AddCost(ret); + cost.AddCost(_price[PR_BUILD_STATION_RAIL]); + cost.AddCost(RailBuildCost(rt)); + + if (length_price_ready) { + cost.AddCost(_price[PR_BUILD_STATION_RAIL_LENGTH]); + length_price_ready = false; + } + success = true; + } + } + + if (!success) return_cmd_error(STR_ERROR_ALREADY_BUILT); + + return cost; +} + /** * Build rail station * @param flags operation to perform @@ -1294,12 +1347,9 @@ CommandCost CmdBuildRailStation(DoCommandFlag flags, TileIndex tile_org, RailTyp /* Make sure the area below consists of clear tiles. (OR tiles belonging to a certain rail station) */ StationID est = INVALID_STATION; std::vector affected_vehicles; - /* Clear the land below the station. */ - CommandCost cost = CheckFlatLandRailStation(new_location, flags, axis, &est, rt, affected_vehicles, spec_class, spec_index, plat_len, numtracks); + /* Add construction and clearing expenses. */ + CommandCost cost = CalculateRailStationCost(new_location, flags, axis, &est, rt, affected_vehicles, spec_class, spec_index, plat_len, numtracks); if (cost.Failed()) return cost; - /* Add construction expenses. */ - cost.AddCost((numtracks * _price[PR_BUILD_STATION_RAIL] + _price[PR_BUILD_STATION_RAIL_LENGTH]) * plat_len); - cost.AddCost(numtracks * plat_len * RailBuildCost(rt)); Station *st = nullptr; ret = FindJoiningStation(est, station_to_join, adjacent, new_location, &st); @@ -1811,6 +1861,54 @@ static CommandCost FindJoiningRoadStop(StationID existing_stop, StationID statio return FindJoiningBaseStation(existing_stop, station_to_join, adjacent, ta, st); } +/** + * Calculates cost of new road stops within the area. + * @param tile_area Area to check. + * @param flags Operation to perform. + * @param is_drive_through True if trying to build a drive-through station. + * @param is_truck_stop True when building a truck stop, false otherwise. + * @param axis Axis of a drive-through road stop. + * @param ddir Entrance direction (#DiagDirection) for normal stops. Converted to the axis for drive-through stops. + * @param station StationID to be queried and returned if available. + * @param rt Road type to build. + * @param unit_cost The cost to build one road stop of the current type. + * @return The cost in case of success, or an error code if it failed. + */ +static CommandCost CalculateRoadStopCost(TileArea tile_area, DoCommandFlag flags, bool is_drive_through, bool is_truck_stop, Axis axis, DiagDirection ddir, StationID *est, RoadType rt, Money unit_cost) +{ + CommandCost cost(EXPENSES_CONSTRUCTION); + bool success = false; + /* Check every tile in the area. */ + for (TileIndex cur_tile : tile_area) { + uint invalid_dirs = 0; + if (is_drive_through) { + SetBit(invalid_dirs, AxisToDiagDir(axis)); + SetBit(invalid_dirs, ReverseDiagDir(AxisToDiagDir(axis))); + } else { + SetBit(invalid_dirs, ddir); + } + CommandCost ret = CheckFlatLandRoadStop(TileArea(cur_tile, cur_tile), flags, invalid_dirs, is_drive_through, is_truck_stop, axis, est, rt); + if (ret.Failed()) return ret; + + bool is_preexisting_roadstop = IsTileType(cur_tile, MP_STATION) && IsRoadStop(cur_tile); + + /* Only add costs if a stop doesn't already exist in the location */ + if (!is_preexisting_roadstop) { + cost.AddCost(ret); + cost.AddCost(unit_cost); + success = true; + } else if (is_preexisting_roadstop && !is_drive_through) { + /* Allow rotating non-drive through stops for free */ + success = true; + } + + } + + if (!success) return_cmd_error(STR_ERROR_ALREADY_BUILT); + + return cost; +} + /** * Build a bus or truck stop. * @param flags Operation to perform. @@ -1874,11 +1972,9 @@ CommandCost CmdBuildRoadStop(DoCommandFlag flags, TileIndex tile, uint8 width, u } else { unit_cost = _price[is_truck_stop ? PR_BUILD_STATION_TRUCK : PR_BUILD_STATION_BUS]; } - CommandCost cost(EXPENSES_CONSTRUCTION, roadstop_area.w * roadstop_area.h * unit_cost); StationID est = INVALID_STATION; - ret = CheckFlatLandRoadStop(roadstop_area, flags, is_drive_through ? 5 << axis : 1 << ddir, is_drive_through, is_truck_stop, axis, &est, rt); - if (ret.Failed()) return ret; - cost.AddCost(ret); + CommandCost cost = CalculateRoadStopCost(roadstop_area, flags, is_drive_through, is_truck_stop, axis, ddir, &est, rt, unit_cost); + if (cost.Failed()) return cost; Station *st = nullptr; ret = FindJoiningRoadStop(est, station_to_join, adjacent, roadstop_area, &st); diff --git a/src/waypoint_cmd.cpp b/src/waypoint_cmd.cpp index f8c3415260..b04a570d2b 100644 --- a/src/waypoint_cmd.cpp +++ b/src/waypoint_cmd.cpp @@ -191,6 +191,21 @@ CommandCost CmdBuildRailWaypoint(DoCommandFlag flags, TileIndex start_tile, Axis if (distant_join && (!_settings_game.station.distant_join_stations || !Waypoint::IsValidID(station_to_join))) return CMD_ERROR; + TileArea new_location(start_tile, width, height); + + /* only AddCost for non-existing waypoints */ + CommandCost cost(EXPENSES_CONSTRUCTION); + bool success = false; + for (TileIndex cur_tile : new_location) { + if (!IsRailWaypointTile(cur_tile)) { + cost.AddCost(_price[PR_BUILD_WAYPOINT_RAIL]); + success = true; + } + } + if (!success) { + return_cmd_error(STR_ERROR_ALREADY_BUILT); + } + /* Make sure the area below consists of clear tiles. (OR tiles belonging to a certain rail station) */ StationID est = INVALID_STATION; @@ -203,7 +218,6 @@ CommandCost CmdBuildRailWaypoint(DoCommandFlag flags, TileIndex start_tile, Axis } Waypoint *wp = nullptr; - TileArea new_location(start_tile, width, height); CommandCost ret = FindJoiningWaypoint(est, station_to_join, adjacent, new_location, &wp); if (ret.Failed()) return ret; @@ -279,7 +293,7 @@ CommandCost CmdBuildRailWaypoint(DoCommandFlag flags, TileIndex start_tile, Axis DirtyCompanyInfrastructureWindows(wp->owner); } - return CommandCost(EXPENSES_CONSTRUCTION, count * _price[PR_BUILD_WAYPOINT_RAIL]); + return cost; } /** From 20a1b24b451e1fb1ceac08cfc74c82d6bb643f54 Mon Sep 17 00:00:00 2001 From: Eric Long Date: Sun, 26 Feb 2023 19:39:09 +0800 Subject: [PATCH 24/47] Fix: build on platforms without native atomic --- CMakeLists.txt | 2 + cmake/CheckAtomic.cmake | 87 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 89 insertions(+) create mode 100644 cmake/CheckAtomic.cmake diff --git a/CMakeLists.txt b/CMakeLists.txt index e91ecf613b..19ebc760d6 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -292,6 +292,8 @@ if(NOT OPTION_DEDICATED) endif() endif() +include(CheckAtomic) + if(APPLE) link_package(Iconv TARGET Iconv::Iconv) diff --git a/cmake/CheckAtomic.cmake b/cmake/CheckAtomic.cmake new file mode 100644 index 0000000000..52d93f4f5b --- /dev/null +++ b/cmake/CheckAtomic.cmake @@ -0,0 +1,87 @@ +# atomic builtins are required for threading support. + +INCLUDE(CheckCXXSourceCompiles) +INCLUDE(CheckLibraryExists) + +# Sometimes linking against libatomic is required for atomic ops, if +# the platform doesn't support lock-free atomics. + +function(check_working_cxx_atomics varname) + set(OLD_CMAKE_REQUIRED_FLAGS ${CMAKE_REQUIRED_FLAGS}) + set(CMAKE_REQUIRED_FLAGS "${CMAKE_REQUIRED_FLAGS} -std=c++17") + check_cxx_source_compiles(" +#include +std::atomic x; +std::atomic y; +std::atomic z; +int main() { + ++z; + ++y; + return ++x; +} +" ${varname}) + set(CMAKE_REQUIRED_FLAGS ${OLD_CMAKE_REQUIRED_FLAGS}) +endfunction(check_working_cxx_atomics) + +function(check_working_cxx_atomics64 varname) + set(OLD_CMAKE_REQUIRED_FLAGS ${CMAKE_REQUIRED_FLAGS}) + set(CMAKE_REQUIRED_FLAGS "-std=c++17 ${CMAKE_REQUIRED_FLAGS}") + check_cxx_source_compiles(" +#include +#include +std::atomic x (0); +int main() { + uint64_t i = x.load(std::memory_order_relaxed); + (void)i; + return 0; +} +" ${varname}) + set(CMAKE_REQUIRED_FLAGS ${OLD_CMAKE_REQUIRED_FLAGS}) +endfunction(check_working_cxx_atomics64) + + +# Check for (non-64-bit) atomic operations. +if(MSVC) + set(HAVE_CXX_ATOMICS_WITHOUT_LIB True) +else() + # First check if atomics work without the library. + check_working_cxx_atomics(HAVE_CXX_ATOMICS_WITHOUT_LIB) + # If not, check if the library exists, and atomics work with it. + if(NOT HAVE_CXX_ATOMICS_WITHOUT_LIB) + check_library_exists(atomic __atomic_fetch_add_4 "" HAVE_LIBATOMIC) + if(HAVE_LIBATOMIC) + list(APPEND CMAKE_REQUIRED_LIBRARIES "atomic") + check_working_cxx_atomics(HAVE_CXX_ATOMICS_WITH_LIB) + if (NOT HAVE_CXX_ATOMICS_WITH_LIB) + message(FATAL_ERROR "Host compiler must support std::atomic!") + endif() + else() + message(FATAL_ERROR "Host compiler appears to require libatomic, but cannot find it.") + endif() + endif() +endif() + +# Check for 64 bit atomic operations. +if(MSVC) + set(HAVE_CXX_ATOMICS64_WITHOUT_LIB True) +else() + # First check if atomics work without the library. + check_working_cxx_atomics64(HAVE_CXX_ATOMICS64_WITHOUT_LIB) + # If not, check if the library exists, and atomics work with it. + if(NOT HAVE_CXX_ATOMICS64_WITHOUT_LIB) + check_library_exists(atomic __atomic_load_8 "" HAVE_CXX_LIBATOMICS64) + if(HAVE_CXX_LIBATOMICS64) + list(APPEND CMAKE_REQUIRED_LIBRARIES "atomic") + check_working_cxx_atomics64(HAVE_CXX_ATOMICS64_WITH_LIB) + if (NOT HAVE_CXX_ATOMICS64_WITH_LIB) + message(FATAL_ERROR "Host compiler must support 64-bit std::atomic!") + endif() + else() + message(FATAL_ERROR "Host compiler appears to require libatomic for 64-bit operations, but cannot find it.") + endif() + endif() +endif() + +if(HAVE_CXX_ATOMICS_WITH_LIB OR HAVE_CXX_ATOMICS64_WITH_LIB) + target_link_libraries(openttd atomic) +endif() From 0fb0469e4761b403f0eaf1603eed5639640887ec Mon Sep 17 00:00:00 2001 From: Rubidium Date: Sat, 21 Jan 2023 17:07:06 +0100 Subject: [PATCH 25/47] Codechange: introduce iterator to iterate over the Tiles of a Map Note: this version is using TileIndex, but that changes later --- src/map_func.h | 34 ++++++++++++++ src/saveload/afterload.cpp | 96 +++++++++++++++++++------------------- 2 files changed, 81 insertions(+), 49 deletions(-) diff --git a/src/map_func.h b/src/map_func.h index 3ebfb349c6..399f84a943 100644 --- a/src/map_func.h +++ b/src/map_func.h @@ -36,6 +36,34 @@ extern TileExtended *_me; */ struct Map { private: + /** + * Iterator to iterate all Tiles + */ + struct Iterator { + typedef TileIndex value_type; + typedef TileIndex *pointer; + typedef TileIndex &reference; + typedef size_t difference_type; + typedef std::forward_iterator_tag iterator_category; + + explicit Iterator(TileIndex index) : index(index) {} + bool operator==(const Iterator &other) const { return this->index == other.index; } + bool operator!=(const Iterator &other) const { return !(*this == other); } + TileIndex operator*() const { return this->index; } + Iterator & operator++() { this->index++; return *this; } + private: + TileIndex index; + }; + + /* + * Iterable ensemble of all Tiles + */ + struct IterateWrapper { + Iterator begin() { return Iterator(0); } + Iterator end() { return Iterator(Map::Size()); } + bool empty() { return false; } + }; + static uint log_x; ///< 2^_map_log_x == _map_size_x static uint log_y; ///< 2^_map_log_y == _map_size_y static uint size_x; ///< Size of the map along the X @@ -158,6 +186,12 @@ public: { return _m != nullptr; } + + /** + * Returns an iterable ensemble of all Tiles + * @return an iterable ensemble of all Tiles + */ + static IterateWrapper Iterate() { return IterateWrapper(); } }; /** diff --git a/src/saveload/afterload.cpp b/src/saveload/afterload.cpp index 4c3e942255..28dad77a29 100644 --- a/src/saveload/afterload.cpp +++ b/src/saveload/afterload.cpp @@ -150,7 +150,7 @@ void SetWaterClassDependingOnSurroundings(TileIndex t, bool include_invalid_wate static void ConvertTownOwner() { - for (TileIndex tile = 0; tile != Map::Size(); tile++) { + for (auto tile : Map::Iterate()) { switch (GetTileType(tile)) { case MP_ROAD: if (GB(_m[tile].m5, 4, 2) == ROAD_TILE_CROSSING && HasBit(_m[tile].m3, 7)) { @@ -566,8 +566,6 @@ bool AfterLoadGame() { SetSignalHandlers(); - TileIndex map_size = Map::Size(); - extern TileIndex _cur_tileloop_tile; // From landscape.cpp. /* The LFSR used in RunTileLoop iteration cannot have a zeroed state, make it non-zeroed. */ if (_cur_tileloop_tile == 0) _cur_tileloop_tile = 1; @@ -614,7 +612,7 @@ bool AfterLoadGame() for (Station *st : Station::Iterate()) { st->train_station.w = st->train_station.h = 0; } - for (TileIndex t = 0; t < map_size; t++) { + for (auto t : Map::Iterate()) { if (!IsTileType(t, MP_STATION)) continue; if (_m[t].m5 > 7) continue; // is it a rail station tile? Station *st = Station::Get(_m[t].m2); @@ -631,7 +629,7 @@ bool AfterLoadGame() _settings_game.construction.map_height_limit = 15; /* In old savegame versions, the heightlevel was coded in bits 0..3 of the type field */ - for (TileIndex t = 0; t < map_size; t++) { + for (auto t : Map::Iterate()) { _m[t].height = GB(_m[t].type, 0, 4); SB(_m[t].type, 0, 2, GB(_me[t].m6, 0, 2)); SB(_me[t].m6, 0, 2, 0); @@ -658,7 +656,7 @@ bool AfterLoadGame() * (4.3) version, so I just check when versions are older, and then * walk through the whole map.. */ if (IsSavegameVersionBefore(SLV_4, 3)) { - for (TileIndex t = 0; t < map_size; t++) { + for (auto t : Map::Iterate()) { if (IsTileType(t, MP_WATER) && GetTileOwner(t) >= MAX_COMPANIES) { SetTileOwner(t, OWNER_WATER); } @@ -840,7 +838,7 @@ bool AfterLoadGame() if (IsSavegameVersionBefore(SLV_72)) { /* Locks in very old savegames had OWNER_WATER as owner */ - for (TileIndex t = 0; t < Map::Size(); t++) { + for (auto t : Map::Iterate()) { switch (GetTileType(t)) { default: break; @@ -894,7 +892,7 @@ bool AfterLoadGame() } } - for (TileIndex t = 0; t < map_size; t++) { + for (auto t : Map::Iterate()) { switch (GetTileType(t)) { case MP_STATION: { BaseStation *bst = BaseStation::GetByTile(t); @@ -964,7 +962,7 @@ bool AfterLoadGame() * to use m2 (16bit big), so we need to clean m2, and that is where this is * all about ;) */ if (IsSavegameVersionBefore(SLV_6, 1)) { - for (TileIndex t = 0; t < map_size; t++) { + for (auto t : Map::Iterate()) { switch (GetTileType(t)) { case MP_HOUSE: _m[t].m4 = _m[t].m2; @@ -1019,7 +1017,7 @@ bool AfterLoadGame() } if (IsSavegameVersionBefore(SLV_48)) { - for (TileIndex t = 0; t < map_size; t++) { + for (auto t : Map::Iterate()) { switch (GetTileType(t)) { case MP_RAILWAY: if (IsPlainRail(t)) { @@ -1049,7 +1047,7 @@ bool AfterLoadGame() if (IsSavegameVersionBefore(SLV_61)) { /* Added the RoadType */ bool old_bridge = IsSavegameVersionBefore(SLV_42); - for (TileIndex t = 0; t < map_size; t++) { + for (auto t : Map::Iterate()) { switch (GetTileType(t)) { case MP_ROAD: SB(_m[t].m5, 6, 2, GB(_m[t].m5, 4, 2)); @@ -1089,7 +1087,7 @@ bool AfterLoadGame() bool fix_roadtypes = !IsSavegameVersionBefore(SLV_61); bool old_bridge = IsSavegameVersionBefore(SLV_42); - for (TileIndex t = 0; t < map_size; t++) { + for (auto t : Map::Iterate()) { switch (GetTileType(t)) { case MP_ROAD: if (fix_roadtypes) SB(_me[t].m7, 6, 2, (RoadTypes)GB(_me[t].m7, 5, 3)); @@ -1154,7 +1152,7 @@ bool AfterLoadGame() /* Railtype moved from m3 to m8 in version SLV_EXTEND_RAILTYPES. */ if (IsSavegameVersionBefore(SLV_EXTEND_RAILTYPES)) { - for (TileIndex t = 0; t < map_size; t++) { + for (auto t : Map::Iterate()) { switch (GetTileType(t)) { case MP_RAILWAY: SetRailType(t, (RailType)GB(_m[t].m3, 0, 4)); @@ -1185,7 +1183,7 @@ bool AfterLoadGame() } if (IsSavegameVersionBefore(SLV_42)) { - for (TileIndex t = 0; t < map_size; t++) { + for (auto t : Map::Iterate()) { if (MayHaveBridgeAbove(t)) ClearBridgeMiddle(t); if (IsBridgeTile(t)) { if (HasBit(_m[t].m5, 6)) { // middle part @@ -1267,7 +1265,7 @@ bool AfterLoadGame() if (IsSavegameVersionBefore(SLV_ROAD_TYPES)) { /* Add road subtypes */ - for (TileIndex t = 0; t < map_size; t++) { + for (auto t : Map::Iterate()) { bool has_road = false; switch (GetTileType(t)) { case MP_ROAD: @@ -1306,7 +1304,7 @@ bool AfterLoadGame() } /* .. so we convert the entire map from normal to elrail (so maintain "fairness") */ - for (TileIndex t = 0; t < map_size; t++) { + for (auto t : Map::Iterate()) { switch (GetTileType(t)) { case MP_RAILWAY: SetRailType(t, UpdateRailType(GetRailType(t), min_rail)); @@ -1358,7 +1356,7 @@ bool AfterLoadGame() /* From version 15, we moved a semaphore bit from bit 2 to bit 3 in m4, making * room for PBS. Now in version 21 move it back :P. */ if (IsSavegameVersionBefore(SLV_21) && !IsSavegameVersionBefore(SLV_15)) { - for (TileIndex t = 0; t < map_size; t++) { + for (auto t : Map::Iterate()) { switch (GetTileType(t)) { case MP_RAILWAY: if (HasSignals(t)) { @@ -1438,7 +1436,7 @@ bool AfterLoadGame() * To give this prettiness to old savegames, we remove all farmfields and * plant new ones. */ if (IsSavegameVersionBefore(SLV_32)) { - for (TileIndex t = 0; t < map_size; t++) { + for (auto t : Map::Iterate()) { if (IsTileType(t, MP_CLEAR) && IsClearGround(t, CLEAR_FIELDS)) { /* remove fields */ MakeClear(t, CLEAR_GRASS, 3); @@ -1475,7 +1473,7 @@ bool AfterLoadGame() /* From version 53, the map array was changed for house tiles to allow * space for newhouses grf features. A new byte, m7, was also added. */ if (IsSavegameVersionBefore(SLV_53)) { - for (TileIndex t = 0; t < map_size; t++) { + for (auto t : Map::Iterate()) { if (IsTileType(t, MP_HOUSE)) { if (GB(_m[t].m3, 6, 2) != TOWN_HOUSE_COMPLETED) { /* Move the construction stage from m3[7..6] to m5[5..4]. @@ -1512,7 +1510,7 @@ bool AfterLoadGame() UpdateHousesAndTowns(); if (IsSavegameVersionBefore(SLV_43)) { - for (TileIndex t = 0; t < map_size; t++) { + for (auto t : Map::Iterate()) { if (IsTileType(t, MP_INDUSTRY)) { switch (GetIndustryGfx(t)) { case GFX_POWERPLANT_SPARKS: @@ -1572,7 +1570,7 @@ bool AfterLoadGame() if (IsSavegameVersionBefore(SLV_49)) for (Company *c : Company::Iterate()) c->face = ConvertFromOldCompanyManagerFace(c->face); if (IsSavegameVersionBefore(SLV_52)) { - for (TileIndex t = 0; t < map_size; t++) { + for (auto t : Map::Iterate()) { if (IsTileType(t, MP_OBJECT) && _m[t].m5 == OBJECT_STATUE) { _m[t].m2 = CalcClosestTownFromTile(t)->index; } @@ -1630,7 +1628,7 @@ bool AfterLoadGame() if (IsSavegameVersionBefore(SLV_64)) { /* Since now we allow different signal types and variants on a single tile. * Move signal states to m4 to make room and clone the signal type/variant. */ - for (TileIndex t = 0; t < map_size; t++) { + for (auto t : Map::Iterate()) { if (IsTileType(t, MP_RAILWAY) && HasSignals(t)) { /* move signal states */ SetSignalStates(t, GB(_m[t].m2, 4, 4)); @@ -1658,7 +1656,7 @@ bool AfterLoadGame() /* From version 82, old style canals (above sealevel (0), WATER owner) are no longer supported. Replace the owner for those by OWNER_NONE. */ if (IsSavegameVersionBefore(SLV_82)) { - for (TileIndex t = 0; t < map_size; t++) { + for (auto t : Map::Iterate()) { if (IsTileType(t, MP_WATER) && GetWaterTileType(t) == WATER_TILE_CLEAR && GetTileOwner(t) == OWNER_WATER && @@ -1675,7 +1673,7 @@ bool AfterLoadGame() * making floods using the removal of ship depots. */ if (IsSavegameVersionBefore(SLV_83)) { - for (TileIndex t = 0; t < map_size; t++) { + for (auto t : Map::Iterate()) { if (IsShipDepotTile(t)) { _m[t].m4 = (TileHeight(t) == 0) ? OWNER_WATER : OWNER_NONE; } @@ -1709,7 +1707,7 @@ bool AfterLoadGame() * land used to have zero density, now they have full density. Therefore, * make all grassy/rough land trees have a density of 3. */ if (IsSavegameVersionBefore(SLV_81)) { - for (TileIndex t = 0; t < map_size; t++) { + for (auto t : Map::Iterate()) { if (GetTileType(t) == MP_TREES) { TreeGround groundType = (TreeGround)GB(_m[t].m2, 4, 2); if (groundType != TREE_GROUND_SNOW_DESERT) SB(_m[t].m2, 6, 2, 3); @@ -1767,7 +1765,7 @@ bool AfterLoadGame() /* The water class was moved/unified. */ if (IsSavegameVersionBefore(SLV_146)) { - for (TileIndex t = 0; t < map_size; t++) { + for (auto t : Map::Iterate()) { switch (GetTileType(t)) { case MP_STATION: switch (GetStationType(t)) { @@ -1801,7 +1799,7 @@ bool AfterLoadGame() } if (IsSavegameVersionBefore(SLV_86)) { - for (TileIndex t = 0; t < map_size; t++) { + for (auto t : Map::Iterate()) { /* Move river flag and update canals to use water class */ if (IsTileType(t, MP_WATER)) { if (GetWaterClass(t) != WATER_CLASS_RIVER) { @@ -1823,7 +1821,7 @@ bool AfterLoadGame() /* Update locks, depots, docks and buoys to have a water class based * on its neighbouring tiles. Done after river and canal updates to * ensure neighbours are correct. */ - for (TileIndex t = 0; t < map_size; t++) { + for (auto t : Map::Iterate()) { if (!IsTileFlat(t)) continue; if (IsTileType(t, MP_WATER) && IsLock(t)) SetWaterClassDependingOnSurroundings(t, false); @@ -1832,7 +1830,7 @@ bool AfterLoadGame() } if (IsSavegameVersionBefore(SLV_87)) { - for (TileIndex t = 0; t < map_size; t++) { + for (auto t : Map::Iterate()) { /* skip oil rigs at borders! */ if ((IsTileType(t, MP_WATER) || IsBuoyTile(t)) && (TileX(t) == 0 || TileY(t) == 0 || TileX(t) == Map::MaxX() - 1 || TileY(t) == Map::MaxY() - 1)) { @@ -1899,7 +1897,7 @@ bool AfterLoadGame() if (IsSavegameVersionBefore(SLV_91)) { /* Increase HouseAnimationFrame from 5 to 7 bits */ - for (TileIndex t = 0; t < map_size; t++) { + for (auto t : Map::Iterate()) { if (IsTileType(t, MP_HOUSE) && GetHouseType(t) >= NEW_HOUSE_OFFSET) { SB(_me[t].m6, 2, 6, GB(_me[t].m6, 3, 5)); SB(_m[t].m3, 5, 1, 0); @@ -1920,7 +1918,7 @@ bool AfterLoadGame() } if (IsSavegameVersionBefore(SLV_99)) { - for (TileIndex t = 0; t < map_size; t++) { + for (auto t : Map::Iterate()) { /* Set newly introduced WaterClass of industry tiles */ if (IsTileType(t, MP_STATION) && IsOilRig(t)) { SetWaterClassDependingOnSurroundings(t, true); @@ -1944,7 +1942,7 @@ bool AfterLoadGame() * format here, as an old layout wouldn't work properly anyway. To be safe, we * clear any possible PBS reservations as well. */ if (IsSavegameVersionBefore(SLV_100)) { - for (TileIndex t = 0; t < map_size; t++) { + for (auto t : Map::Iterate()) { switch (GetTileType(t)) { case MP_RAILWAY: if (HasSignals(t)) { @@ -2027,7 +2025,7 @@ bool AfterLoadGame() } if (IsSavegameVersionBefore(SLV_112)) { - for (TileIndex t = 0; t < map_size; t++) { + for (auto t : Map::Iterate()) { /* Check for HQ bit being set, instead of using map accessor, * since we've already changed it code-wise */ if (IsTileType(t, MP_OBJECT) && HasBit(_m[t].m5, 7)) { @@ -2039,7 +2037,7 @@ bool AfterLoadGame() } } if (IsSavegameVersionBefore(SLV_144)) { - for (TileIndex t = 0; t < map_size; t++) { + for (auto t : Map::Iterate()) { if (!IsTileType(t, MP_OBJECT)) continue; /* Reordering/generalisation of the object bits. */ @@ -2055,7 +2053,7 @@ bool AfterLoadGame() if (IsSavegameVersionBefore(SLV_147) && Object::GetNumItems() == 0) { /* Make real objects for object tiles. */ - for (TileIndex t = 0; t < map_size; t++) { + for (auto t : Map::Iterate()) { if (!IsTileType(t, MP_OBJECT)) continue; if (Town::GetNumItems() == 0) { @@ -2322,7 +2320,7 @@ bool AfterLoadGame() /* The bits for the tree ground and tree density have * been swapped (m2 bits 7..6 and 5..4. */ if (IsSavegameVersionBefore(SLV_135)) { - for (TileIndex t = 0; t < map_size; t++) { + for (auto t : Map::Iterate()) { if (IsTileType(t, MP_CLEAR)) { if (GetRawClearGround(t) == CLEAR_SNOW) { SetClearGroundDensity(t, CLEAR_GRASS, GetClearDensity(t)); @@ -2367,7 +2365,7 @@ bool AfterLoadGame() {119, 15}, // 14 unused tiles (radar) {140, 4}, // APT_GRASS_FENCE_NE_FLAG_2 }; - for (TileIndex t = 0; t < map_size; t++) { + for (auto t : Map::Iterate()) { if (IsAirportTile(t)) { StationGfx old_gfx = GetStationGfx(t); byte offset = 0; @@ -2397,7 +2395,7 @@ bool AfterLoadGame() } if (IsSavegameVersionBefore(SLV_141)) { - for (TileIndex t = 0; t < map_size; t++) { + for (auto t : Map::Iterate()) { /* Reset tropic zone for VOID tiles, they shall not have any. */ if (IsTileType(t, MP_VOID)) SetTropicZone(t, TROPICZONE_NORMAL); } @@ -2438,7 +2436,7 @@ bool AfterLoadGame() /* Move the animation frame to the same location (m7) for all objects. */ if (IsSavegameVersionBefore(SLV_147)) { - for (TileIndex t = 0; t < map_size; t++) { + for (auto t : Map::Iterate()) { switch (GetTileType(t)) { case MP_HOUSE: if (GetHouseType(t) >= NEW_HOUSE_OFFSET) { @@ -2477,7 +2475,7 @@ bool AfterLoadGame() } if (IsSavegameVersionBefore(SLV_149)) { - for (TileIndex t = 0; t < map_size; t++) { + for (auto t : Map::Iterate()) { if (!IsTileType(t, MP_STATION)) continue; if (!IsBuoy(t) && !IsOilRig(t) && !(IsDock(t) && IsTileFlat(t))) { SetWaterClass(t, WATER_CLASS_INVALID); @@ -2770,7 +2768,7 @@ bool AfterLoadGame() if (IsSavegameVersionBefore(SLV_164) && !IsSavegameVersionBefore(SLV_32)) { /* We store 4 fences in the field tiles instead of only SE and SW. */ - for (TileIndex t = 0; t < map_size; t++) { + for (auto t : Map::Iterate()) { if (!IsTileType(t, MP_CLEAR) && !IsTileType(t, MP_TREES)) continue; if (IsTileType(t, MP_CLEAR) && IsClearGround(t, CLEAR_FIELDS)) continue; uint fence = GB(_m[t].m4, 5, 3); @@ -2819,7 +2817,7 @@ bool AfterLoadGame() /* The road owner of standard road stops was not properly accounted for. */ if (IsSavegameVersionBefore(SLV_172)) { - for (TileIndex t = 0; t < map_size; t++) { + for (auto t : Map::Iterate()) { if (!IsStandardRoadStopTile(t)) continue; Owner o = GetTileOwner(t); SetRoadOwner(t, RTT_ROAD, o); @@ -2860,7 +2858,7 @@ bool AfterLoadGame() /* Blocked tiles could be reserved due to a bug, which causes * other places to assert upon e.g. station reconstruction. */ - for (TileIndex t = 0; t < map_size; t++) { + for (auto t : Map::Iterate()) { if (HasStationTileRail(t) && IsStationTileBlocked(t)) { SetRailStationReservation(t, false); } @@ -2880,7 +2878,7 @@ bool AfterLoadGame() if (IsSavegameVersionBefore(SLV_186)) { /* Move ObjectType from map to pool */ - for (TileIndex t = 0; t < map_size; t++) { + for (auto t : Map::Iterate()) { if (IsTileType(t, MP_OBJECT)) { Object *o = Object::Get(_m[t].m2); o->type = _m[t].m5; @@ -3079,14 +3077,14 @@ bool AfterLoadGame() if (IsSavegameVersionBefore(SLV_TREES_WATER_CLASS)) { /* Update water class for trees. */ - for (TileIndex t = 0; t < map_size; t++) { + for (auto t : Map::Iterate()) { if (IsTileType(t, MP_TREES)) SetWaterClass(t, GetTreeGround(t) == TREE_GROUND_SHORE ? WATER_CLASS_SEA : WATER_CLASS_INVALID); } } /* Update structures for multitile docks */ if (IsSavegameVersionBefore(SLV_MULTITILE_DOCKS)) { - for (TileIndex t = 0; t < map_size; t++) { + for (auto t : Map::Iterate()) { /* Clear docking tile flag from relevant tiles as it * was not previously cleared. */ if (IsTileType(t, MP_WATER) || IsTileType(t, MP_RAILWAY) || IsTileType(t, MP_STATION) || IsTileType(t, MP_TUNNELBRIDGE)) { @@ -3108,7 +3106,7 @@ bool AfterLoadGame() if (IsSavegameVersionBeforeOrAt(SLV_ENDING_YEAR)) { /* Reset roadtype/streetcartype info for non-road bridges. */ - for (TileIndex t = 0; t < map_size; t++) { + for (auto t : Map::Iterate()) { if (IsTileType(t, MP_TUNNELBRIDGE) && GetTunnelBridgeTransportType(t) != TRANSPORT_ROAD) { SetRoadTypes(t, INVALID_ROADTYPE, INVALID_ROADTYPE); } @@ -3191,7 +3189,7 @@ bool AfterLoadGame() if (IsSavegameVersionBeforeOrAt(SLV_MULTITRACK_LEVEL_CROSSINGS)) { /* Reset unused tree counters to reduce the savegame size. */ - for (TileIndex t = 0; t < map_size; t++) { + for (auto t : Map::Iterate()) { if (IsTileType(t, MP_TREES)) { SB(_m[t].m2, 0, 4, 0); } @@ -3199,7 +3197,7 @@ bool AfterLoadGame() } /* Refresh all level crossings to bar adjacent crossing tiles, if needed. */ - for (TileIndex tile = 0; tile < Map::Size(); tile++) { + for (auto tile : Map::Iterate()) { if (IsLevelCrossingTile(tile)) UpdateLevelCrossing(tile, false); } } From 7a1816a6cd64f3f4091faf1e7a4e5810a7ec54be Mon Sep 17 00:00:00 2001 From: Rubidium Date: Sat, 21 Jan 2023 15:16:28 +0100 Subject: [PATCH 26/47] Codechange: rename Tile to TileBase to be able to introduce a new Tile class --- src/map.cpp | 4 ++-- src/map_func.h | 2 +- src/map_type.h | 6 +++--- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/map.cpp b/src/map.cpp index 6400f15ab4..41f579b899 100644 --- a/src/map.cpp +++ b/src/map.cpp @@ -27,7 +27,7 @@ extern "C" _CRTIMP void __cdecl _assert(void *, void *, unsigned); /* static */ uint Map::size; ///< The number of tiles on the map /* static */ uint Map::tile_mask; ///< _map_size - 1 (to mask the mapsize) -Tile *_m = nullptr; ///< Tiles of the map +TileBase *_m = nullptr; ///< Tiles of the map TileExtended *_me = nullptr; ///< Extended Tiles of the map @@ -59,7 +59,7 @@ TileExtended *_me = nullptr; ///< Extended Tiles of the map free(_m); free(_me); - _m = CallocT(Map::size); + _m = CallocT(Map::size); _me = CallocT(Map::size); } diff --git a/src/map_func.h b/src/map_func.h index 399f84a943..8a85c9de27 100644 --- a/src/map_func.h +++ b/src/map_func.h @@ -21,7 +21,7 @@ * This variable points to the tile-array which contains the tiles of * the map. */ -extern Tile *_m; +extern TileBase *_m; /** * Pointer to the extended tile-array. diff --git a/src/map_type.h b/src/map_type.h index 89012d6753..238647d792 100644 --- a/src/map_type.h +++ b/src/map_type.h @@ -14,7 +14,7 @@ * Data that is stored per tile. Also used TileExtended for this. * Look at docs/landscape.html for the exact meaning of the members. */ -struct Tile { +struct TileBase { byte type; ///< The type (bits 4..7), bridges (2..3), rainforest/desert (0..1) byte height; ///< The height of the northern corner. uint16 m2; ///< Primarily used for indices to towns, industries and stations @@ -24,10 +24,10 @@ struct Tile { byte m5; ///< General purpose }; -static_assert(sizeof(Tile) == 8); +static_assert(sizeof(TileBase) == 8); /** - * Data that is stored per tile. Also used Tile for this. + * Data that is stored per tile. Also used TileBase for this. * Look at docs/landscape.html for the exact meaning of the members. */ struct TileExtended { From 7a6452d3ef83d22b6ffe31b1b53dd2e21385d575 Mon Sep 17 00:00:00 2001 From: Rubidium Date: Sat, 21 Jan 2023 15:39:20 +0100 Subject: [PATCH 27/47] Codechange: introduce Tile class for accessing map data of a single tile --- src/map_func.h | 149 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 149 insertions(+) diff --git a/src/map_func.h b/src/map_func.h index 8a85c9de27..0bcf064302 100644 --- a/src/map_func.h +++ b/src/map_func.h @@ -31,6 +31,155 @@ extern TileBase *_m; */ extern TileExtended *_me; +/** + * Wrapper class to abstract away the way the tiles are stored. It is + * intended to be used to access the "map" data of a single tile. + * + * The wrapper is expected to be fully optimized away by the compiler, even + * with low optimization levels except when completely disabling it. + */ +class Tile { +private: + TileIndex tile; ///< The tile to access the map data for. + +public: + /** + * Create the tile wrapper for the given tile. + * @param tile The tile to access the map for. + */ + debug_inline Tile(TileIndex tile) : tile(tile) {} + + /** + * Implicit conversion to the TileIndex. + */ + debug_inline constexpr operator TileIndex() const { return tile; } + + /** + * Implicit conversion to the uint for bounds checking. + */ + debug_inline constexpr operator uint() const { return tile; } + + /** + * The type (bits 4..7), bridges (2..3), rainforest/desert (0..1) + * + * Look at docs/landscape.html for the exact meaning of the data. + * @param tile The tile to get the data for. + * @return reference to the byte holding the data. + */ + debug_inline byte &type() + { + return _m[tile].type; + } + + /** + * The height of the northern corner + * + * Look at docs/landscape.html for the exact meaning of the data. + * @param tile The tile to get the height for. + * @return reference to the byte holding the height. + */ + debug_inline byte &height() + { + return _m[tile].height; + } + + /** + * Primarily used for ownership information + * + * Look at docs/landscape.html for the exact meaning of the data. + * @param tile The tile to get the data for. + * @return reference to the byte holding the data. + */ + debug_inline byte &m1() + { + return _m[tile].m1; + } + + /** + * Primarily used for indices to towns, industries and stations + * + * Look at docs/landscape.html for the exact meaning of the data. + * @param tile The tile to get the data for. + * @return reference to the uint16 holding the data. + */ + debug_inline uint16 &m2() + { + return _m[tile].m2; + } + + /** + * General purpose + * + * Look at docs/landscape.html for the exact meaning of the data. + * @param tile The tile to get the data for. + * @return reference to the byte holding the data. + */ + debug_inline byte &m3() + { + return _m[tile].m3; + } + + /** + * General purpose + * + * Look at docs/landscape.html for the exact meaning of the data. + * @param tile The tile to get the data for. + * @return reference to the byte holding the data. + */ + debug_inline byte &m4() + { + return _m[tile].m4; + } + + /** + * General purpose + * + * Look at docs/landscape.html for the exact meaning of the data. + * @param tile The tile to get the data for. + * @return reference to the byte holding the data. + */ + debug_inline byte &m5() + { + return _m[tile].m5; + } + + /** + * General purpose + * + * Look at docs/landscape.html for the exact meaning of the data. + * @param tile The tile to get the data for. + * @return reference to the byte holding the data. + */ + debug_inline byte &m6() + { + return _me[tile].m6; + } + + /** + * Primarily used for newgrf support + * + * Look at docs/landscape.html for the exact meaning of the data. + * @param tile The tile to get the data for. + * @return reference to the byte holding the data. + */ + debug_inline byte &m7() + { + return _me[tile].m7; + } + + /** + * General purpose + * + * Look at docs/landscape.html for the exact meaning of the data. + * @param tile The tile to get the data for. + * @return reference to the uint16 holding the data. + */ + debug_inline uint16 &m8() + { + return _me[tile].m8; + } +}; + /** * Size related data of the map. */ From 580d0a6343549f7706a7dac4509e8e2f1c9f66f9 Mon Sep 17 00:00:00 2001 From: Rubidium Date: Sat, 21 Jan 2023 16:40:28 +0100 Subject: [PATCH 28/47] Codechange: make use of Tile in for all direct map accesses --- src/bridge_map.h | 50 ++-- src/clear_map.h | 116 ++++----- src/depot_map.h | 10 +- src/industry_cmd.cpp | 2 +- src/industry_map.h | 90 +++---- src/map_func.h | 14 +- src/misc_gui.cpp | 22 +- src/object_cmd.cpp | 2 +- src/object_map.h | 28 +-- src/pathfinder/npf/npf.cpp | 2 +- src/rail.cpp | 2 +- src/rail_map.h | 176 +++++++------- src/road_map.cpp | 2 +- src/road_map.h | 202 ++++++++-------- src/saveload/afterload.cpp | 343 ++++++++++++++------------- src/saveload/map_sl.cpp | 48 ++-- src/saveload/oldloader_sl.cpp | 148 ++++++------ src/saveload/station_sl.cpp | 5 +- src/saveload/waypoint_sl.cpp | 24 +- src/script/api/script_story_page.cpp | 4 +- src/station_cmd.cpp | 11 +- src/station_map.h | 154 ++++++------ src/tile_map.h | 50 ++-- src/town_map.h | 118 ++++----- src/tree_map.h | 52 ++-- src/tunnel_map.h | 38 +-- src/tunnelbridge_map.h | 28 +-- src/void_map.h | 16 +- src/water_cmd.cpp | 2 +- src/water_map.h | 150 ++++++------ 30 files changed, 963 insertions(+), 946 deletions(-) diff --git a/src/bridge_map.h b/src/bridge_map.h index 575af60470..317ebdd6f0 100644 --- a/src/bridge_map.h +++ b/src/bridge_map.h @@ -21,10 +21,10 @@ * @pre IsTileType(t, MP_TUNNELBRIDGE) * @return true if the structure is a bridge one */ -static inline bool IsBridge(TileIndex t) +static inline bool IsBridge(Tile t) { assert(IsTileType(t, MP_TUNNELBRIDGE)); - return HasBit(_m[t].m5, 7); + return HasBit(t.m5(), 7); } /** @@ -32,7 +32,7 @@ static inline bool IsBridge(TileIndex t) * @param t The tile to analyze * @return true if a bridge is present */ -static inline bool IsBridgeTile(TileIndex t) +static inline bool IsBridgeTile(Tile t) { return IsTileType(t, MP_TUNNELBRIDGE) && IsBridge(t); } @@ -42,9 +42,9 @@ static inline bool IsBridgeTile(TileIndex t) * @param t The tile to analyze * @return true if a bridge is detected above */ -static inline bool IsBridgeAbove(TileIndex t) +static inline bool IsBridgeAbove(Tile t) { - return GB(_m[t].type, 2, 2) != 0; + return GB(t.type(), 2, 2) != 0; } /** @@ -53,10 +53,10 @@ static inline bool IsBridgeAbove(TileIndex t) * @pre IsBridgeTile(t) * @return The bridge type */ -static inline BridgeType GetBridgeType(TileIndex t) +static inline BridgeType GetBridgeType(Tile t) { assert(IsBridgeTile(t)); - return GB(_me[t].m6, 2, 4); + return GB(t.m6(), 2, 4); } /** @@ -65,10 +65,10 @@ static inline BridgeType GetBridgeType(TileIndex t) * @pre IsBridgeAbove(t) * @return the above mentioned axis */ -static inline Axis GetBridgeAxis(TileIndex t) +static inline Axis GetBridgeAxis(Tile t) { assert(IsBridgeAbove(t)); - return (Axis)(GB(_m[t].type, 2, 2) - 1); + return (Axis)(GB(t.type(), 2, 2) - 1); } TileIndex GetNorthernBridgeEnd(TileIndex t); @@ -91,16 +91,16 @@ static inline int GetBridgePixelHeight(TileIndex tile) * @param t the tile to remove the bridge from * @param a the axis of the bridge to remove */ -static inline void ClearSingleBridgeMiddle(TileIndex t, Axis a) +static inline void ClearSingleBridgeMiddle(Tile t, Axis a) { - ClrBit(_m[t].type, 2 + a); + ClrBit(t.type(), 2 + a); } /** * Removes bridges from the given, that is bridges along the X and Y axis. * @param t the tile to remove the bridge from */ -static inline void ClearBridgeMiddle(TileIndex t) +static inline void ClearBridgeMiddle(Tile t) { ClearSingleBridgeMiddle(t, AXIS_X); ClearSingleBridgeMiddle(t, AXIS_Y); @@ -111,9 +111,9 @@ static inline void ClearBridgeMiddle(TileIndex t) * @param t the tile to add the bridge to * @param a the axis of the bridge to add */ -static inline void SetBridgeMiddle(TileIndex t, Axis a) +static inline void SetBridgeMiddle(Tile t, Axis a) { - SetBit(_m[t].type, 2 + a); + SetBit(t.type(), 2 + a); } /** @@ -125,18 +125,18 @@ static inline void SetBridgeMiddle(TileIndex t, Axis a) * @param tt the transport type of the bridge * @note this function should not be called directly. */ -static inline void MakeBridgeRamp(TileIndex t, Owner o, BridgeType bridgetype, DiagDirection d, TransportType tt) +static inline void MakeBridgeRamp(Tile t, Owner o, BridgeType bridgetype, DiagDirection d, TransportType tt) { SetTileType(t, MP_TUNNELBRIDGE); SetTileOwner(t, o); SetDockingTile(t, false); - _m[t].m2 = 0; - _m[t].m3 = 0; - _m[t].m4 = INVALID_ROADTYPE; - _m[t].m5 = 1 << 7 | tt << 2 | d; - SB(_me[t].m6, 2, 4, bridgetype); - _me[t].m7 = 0; - _me[t].m8 = INVALID_ROADTYPE << 6; + t.m2() = 0; + t.m3() = 0; + t.m4() = INVALID_ROADTYPE; + t.m5() = 1 << 7 | tt << 2 | d; + SB(t.m6(), 2, 4, bridgetype); + t.m7() = 0; + t.m8() = INVALID_ROADTYPE << 6; } /** @@ -150,7 +150,7 @@ static inline void MakeBridgeRamp(TileIndex t, Owner o, BridgeType bridgetype, D * @param road_rt the road type of the bridge * @param tram_rt the tram type of the bridge */ -static inline void MakeRoadBridgeRamp(TileIndex t, Owner o, Owner owner_road, Owner owner_tram, BridgeType bridgetype, DiagDirection d, RoadType road_rt, RoadType tram_rt) +static inline void MakeRoadBridgeRamp(Tile t, Owner o, Owner owner_road, Owner owner_tram, BridgeType bridgetype, DiagDirection d, RoadType road_rt, RoadType tram_rt) { MakeBridgeRamp(t, o, bridgetype, d, TRANSPORT_ROAD); SetRoadOwner(t, RTT_ROAD, owner_road); @@ -166,7 +166,7 @@ static inline void MakeRoadBridgeRamp(TileIndex t, Owner o, Owner owner_road, Ow * @param d the direction this ramp must be facing * @param rt the rail type of the bridge */ -static inline void MakeRailBridgeRamp(TileIndex t, Owner o, BridgeType bridgetype, DiagDirection d, RailType rt) +static inline void MakeRailBridgeRamp(Tile t, Owner o, BridgeType bridgetype, DiagDirection d, RailType rt) { MakeBridgeRamp(t, o, bridgetype, d, TRANSPORT_RAIL); SetRailType(t, rt); @@ -178,7 +178,7 @@ static inline void MakeRailBridgeRamp(TileIndex t, Owner o, BridgeType bridgetyp * @param o the new owner of the bridge ramp * @param d the direction this ramp must be facing */ -static inline void MakeAqueductBridgeRamp(TileIndex t, Owner o, DiagDirection d) +static inline void MakeAqueductBridgeRamp(Tile t, Owner o, DiagDirection d) { MakeBridgeRamp(t, o, 0, d, TRANSPORT_WATER); } diff --git a/src/clear_map.h b/src/clear_map.h index 159f5c6a40..cf2fb9ad60 100644 --- a/src/clear_map.h +++ b/src/clear_map.h @@ -32,10 +32,10 @@ enum ClearGround { * @pre IsTileType(t, MP_CLEAR) * @return whether the tile is covered with snow. */ -static inline bool IsSnowTile(TileIndex t) +static inline bool IsSnowTile(Tile t) { assert(IsTileType(t, MP_CLEAR)); - return HasBit(_m[t].m3, 4); + return HasBit(t.m3(), 4); } /** @@ -44,10 +44,10 @@ static inline bool IsSnowTile(TileIndex t) * @pre IsTileType(t, MP_CLEAR) * @return the ground type */ -static inline ClearGround GetRawClearGround(TileIndex t) +static inline ClearGround GetRawClearGround(Tile t) { assert(IsTileType(t, MP_CLEAR)); - return (ClearGround)GB(_m[t].m5, 2, 3); + return (ClearGround)GB(t.m5(), 2, 3); } /** @@ -56,7 +56,7 @@ static inline ClearGround GetRawClearGround(TileIndex t) * @pre IsTileType(t, MP_CLEAR) * @return the ground type */ -static inline ClearGround GetClearGround(TileIndex t) +static inline ClearGround GetClearGround(Tile t) { if (IsSnowTile(t)) return CLEAR_SNOW; return GetRawClearGround(t); @@ -68,7 +68,7 @@ static inline ClearGround GetClearGround(TileIndex t) * @param ct the ground type * @pre IsTileType(t, MP_CLEAR) */ -static inline bool IsClearGround(TileIndex t, ClearGround ct) +static inline bool IsClearGround(Tile t, ClearGround ct) { return GetClearGround(t) == ct; } @@ -80,10 +80,10 @@ static inline bool IsClearGround(TileIndex t, ClearGround ct) * @pre IsTileType(t, MP_CLEAR) * @return the density */ -static inline uint GetClearDensity(TileIndex t) +static inline uint GetClearDensity(Tile t) { assert(IsTileType(t, MP_CLEAR)); - return GB(_m[t].m5, 0, 2); + return GB(t.m5(), 0, 2); } /** @@ -92,10 +92,10 @@ static inline uint GetClearDensity(TileIndex t) * @param d the amount to increment the density with * @pre IsTileType(t, MP_CLEAR) */ -static inline void AddClearDensity(TileIndex t, int d) +static inline void AddClearDensity(Tile t, int d) { assert(IsTileType(t, MP_CLEAR)); // XXX incomplete - _m[t].m5 += d; + t.m5() += d; } /** @@ -104,10 +104,10 @@ static inline void AddClearDensity(TileIndex t, int d) * @param d the new density * @pre IsTileType(t, MP_CLEAR) */ -static inline void SetClearDensity(TileIndex t, uint d) +static inline void SetClearDensity(Tile t, uint d) { assert(IsTileType(t, MP_CLEAR)); - SB(_m[t].m5, 0, 2, d); + SB(t.m5(), 0, 2, d); } @@ -117,10 +117,10 @@ static inline void SetClearDensity(TileIndex t, uint d) * @pre IsTileType(t, MP_CLEAR) * @return the value of the counter */ -static inline uint GetClearCounter(TileIndex t) +static inline uint GetClearCounter(Tile t) { assert(IsTileType(t, MP_CLEAR)); - return GB(_m[t].m5, 5, 3); + return GB(t.m5(), 5, 3); } /** @@ -129,10 +129,10 @@ static inline uint GetClearCounter(TileIndex t) * @param c the amount to increment the counter with * @pre IsTileType(t, MP_CLEAR) */ -static inline void AddClearCounter(TileIndex t, int c) +static inline void AddClearCounter(Tile t, int c) { assert(IsTileType(t, MP_CLEAR)); // XXX incomplete - _m[t].m5 += c << 5; + t.m5() += c << 5; } /** @@ -141,10 +141,10 @@ static inline void AddClearCounter(TileIndex t, int c) * @param c the amount to set the counter to * @pre IsTileType(t, MP_CLEAR) */ -static inline void SetClearCounter(TileIndex t, uint c) +static inline void SetClearCounter(Tile t, uint c) { assert(IsTileType(t, MP_CLEAR)); // XXX incomplete - SB(_m[t].m5, 5, 3, c); + SB(t.m5(), 5, 3, c); } @@ -155,10 +155,10 @@ static inline void SetClearCounter(TileIndex t, uint c) * @param density the density of the ground tile * @pre IsTileType(t, MP_CLEAR) */ -static inline void SetClearGroundDensity(TileIndex t, ClearGround type, uint density) +static inline void SetClearGroundDensity(Tile t, ClearGround type, uint density) { assert(IsTileType(t, MP_CLEAR)); // XXX incomplete - _m[t].m5 = 0 << 5 | type << 2 | density; + t.m5() = 0 << 5 | type << 2 | density; } @@ -168,10 +168,10 @@ static inline void SetClearGroundDensity(TileIndex t, ClearGround type, uint den * @pre GetClearGround(t) == CLEAR_FIELDS * @return the field type */ -static inline uint GetFieldType(TileIndex t) +static inline uint GetFieldType(Tile t) { assert(GetClearGround(t) == CLEAR_FIELDS); - return GB(_m[t].m3, 0, 4); + return GB(t.m3(), 0, 4); } /** @@ -180,10 +180,10 @@ static inline uint GetFieldType(TileIndex t) * @param f the field type * @pre GetClearGround(t) == CLEAR_FIELDS */ -static inline void SetFieldType(TileIndex t, uint f) +static inline void SetFieldType(Tile t, uint f) { assert(GetClearGround(t) == CLEAR_FIELDS); // XXX incomplete - SB(_m[t].m3, 0, 4, f); + SB(t.m3(), 0, 4, f); } /** @@ -192,10 +192,10 @@ static inline void SetFieldType(TileIndex t, uint f) * @pre GetClearGround(t) == CLEAR_FIELDS * @return the industry that made the field */ -static inline IndustryID GetIndustryIndexOfField(TileIndex t) +static inline IndustryID GetIndustryIndexOfField(Tile t) { assert(GetClearGround(t) == CLEAR_FIELDS); - return(IndustryID) _m[t].m2; + return(IndustryID) t.m2(); } /** @@ -204,10 +204,10 @@ static inline IndustryID GetIndustryIndexOfField(TileIndex t) * @param i the industry that made the field * @pre GetClearGround(t) == CLEAR_FIELDS */ -static inline void SetIndustryIndexOfField(TileIndex t, IndustryID i) +static inline void SetIndustryIndexOfField(Tile t, IndustryID i) { assert(GetClearGround(t) == CLEAR_FIELDS); - _m[t].m2 = i; + t.m2() = i; } @@ -218,15 +218,15 @@ static inline void SetIndustryIndexOfField(TileIndex t, IndustryID i) * @pre IsClearGround(t, CLEAR_FIELDS) * @return 0 if there is no fence, otherwise the fence type */ -static inline uint GetFence(TileIndex t, DiagDirection side) +static inline uint GetFence(Tile t, DiagDirection side) { assert(IsClearGround(t, CLEAR_FIELDS)); switch (side) { default: NOT_REACHED(); - case DIAGDIR_SE: return GB(_m[t].m4, 2, 3); - case DIAGDIR_SW: return GB(_m[t].m4, 5, 3); - case DIAGDIR_NE: return GB(_m[t].m3, 5, 3); - case DIAGDIR_NW: return GB(_me[t].m6, 2, 3); + case DIAGDIR_SE: return GB(t.m4(), 2, 3); + case DIAGDIR_SW: return GB(t.m4(), 5, 3); + case DIAGDIR_NE: return GB(t.m3(), 5, 3); + case DIAGDIR_NW: return GB(t.m6(), 2, 3); } } @@ -237,15 +237,15 @@ static inline uint GetFence(TileIndex t, DiagDirection side) * @param h 0 if there is no fence, otherwise the fence type * @pre IsClearGround(t, CLEAR_FIELDS) */ -static inline void SetFence(TileIndex t, DiagDirection side, uint h) +static inline void SetFence(Tile t, DiagDirection side, uint h) { assert(IsClearGround(t, CLEAR_FIELDS)); switch (side) { default: NOT_REACHED(); - case DIAGDIR_SE: SB(_m[t].m4, 2, 3, h); break; - case DIAGDIR_SW: SB(_m[t].m4, 5, 3, h); break; - case DIAGDIR_NE: SB(_m[t].m3, 5, 3, h); break; - case DIAGDIR_NW: SB(_me[t].m6, 2, 3, h); break; + case DIAGDIR_SE: SB(t.m4(), 2, 3, h); break; + case DIAGDIR_SW: SB(t.m4(), 5, 3, h); break; + case DIAGDIR_NE: SB(t.m3(), 5, 3, h); break; + case DIAGDIR_NW: SB(t.m6(), 2, 3, h); break; } } @@ -256,18 +256,18 @@ static inline void SetFence(TileIndex t, DiagDirection side, uint h) * @param g the type of ground * @param density the density of the grass/snow/desert etc */ -static inline void MakeClear(TileIndex t, ClearGround g, uint density) +static inline void MakeClear(Tile t, ClearGround g, uint density) { SetTileType(t, MP_CLEAR); - _m[t].m1 = 0; + t.m1() = 0; SetTileOwner(t, OWNER_NONE); - _m[t].m2 = 0; - _m[t].m3 = 0; - _m[t].m4 = 0 << 5 | 0 << 2; + t.m2() = 0; + t.m3() = 0; + t.m4() = 0 << 5 | 0 << 2; SetClearGroundDensity(t, g, density); // Sets m5 - _me[t].m6 = 0; - _me[t].m7 = 0; - _me[t].m8 = 0; + t.m6() = 0; + t.m7() = 0; + t.m8() = 0; } @@ -277,18 +277,18 @@ static inline void MakeClear(TileIndex t, ClearGround g, uint density) * @param field_type the 'growth' level of the field * @param industry the industry this tile belongs to */ -static inline void MakeField(TileIndex t, uint field_type, IndustryID industry) +static inline void MakeField(Tile t, uint field_type, IndustryID industry) { SetTileType(t, MP_CLEAR); - _m[t].m1 = 0; + t.m1() = 0; SetTileOwner(t, OWNER_NONE); - _m[t].m2 = industry; - _m[t].m3 = field_type; - _m[t].m4 = 0 << 5 | 0 << 2; + t.m2() = industry; + t.m3() = field_type; + t.m4() = 0 << 5 | 0 << 2; SetClearGroundDensity(t, CLEAR_FIELDS, 3); - SB(_me[t].m6, 2, 4, 0); - _me[t].m7 = 0; - _me[t].m8 = 0; + SB(t.m6(), 2, 4, 0); + t.m7() = 0; + t.m8() = 0; } /** @@ -297,10 +297,10 @@ static inline void MakeField(TileIndex t, uint field_type, IndustryID industry) * @param density The density of snowiness. * @pre GetClearGround(t) != CLEAR_SNOW */ -static inline void MakeSnow(TileIndex t, uint density = 0) +static inline void MakeSnow(Tile t, uint density = 0) { assert(GetClearGround(t) != CLEAR_SNOW); - SetBit(_m[t].m3, 4); + SetBit(t.m3(), 4); if (GetRawClearGround(t) == CLEAR_FIELDS) { SetClearGroundDensity(t, CLEAR_GRASS, density); } else { @@ -313,10 +313,10 @@ static inline void MakeSnow(TileIndex t, uint density = 0) * @param t the tile to clear of snow * @pre GetClearGround(t) == CLEAR_SNOW */ -static inline void ClearSnow(TileIndex t) +static inline void ClearSnow(Tile t) { assert(GetClearGround(t) == CLEAR_SNOW); - ClrBit(_m[t].m3, 4); + ClrBit(t.m3(), 4); SetClearDensity(t, 3); } diff --git a/src/depot_map.h b/src/depot_map.h index f92ca9f6e4..939569bd85 100644 --- a/src/depot_map.h +++ b/src/depot_map.h @@ -15,7 +15,7 @@ /** * Check if a tile is a depot and it is a depot of the given type. */ -static inline bool IsDepotTypeTile(TileIndex tile, TransportType type) +static inline bool IsDepotTypeTile(Tile tile, TransportType type) { switch (type) { default: NOT_REACHED(); @@ -38,7 +38,7 @@ static inline bool IsDepotTypeTile(TileIndex tile, TransportType type) * @param tile the tile to check * @return true if and only if there is a depot on the tile. */ -static inline bool IsDepotTile(TileIndex tile) +static inline bool IsDepotTile(Tile tile) { return IsRailDepotTile(tile) || IsRoadDepotTile(tile) || IsShipDepotTile(tile) || IsHangarTile(tile); } @@ -49,11 +49,11 @@ static inline bool IsDepotTile(TileIndex tile) * @pre IsRailDepotTile(t) || IsRoadDepotTile(t) || IsShipDepotTile(t) * @return DepotID */ -static inline DepotID GetDepotIndex(TileIndex t) +static inline DepotID GetDepotIndex(Tile t) { /* Hangars don't have a Depot class, thus store no DepotID. */ assert(IsRailDepotTile(t) || IsRoadDepotTile(t) || IsShipDepotTile(t)); - return _m[t].m2; + return t.m2(); } /** @@ -62,7 +62,7 @@ static inline DepotID GetDepotIndex(TileIndex t) * @pre IsDepotTile(t) * @return the type of vehicles that can use the depot */ -static inline VehicleType GetDepotVehicleType(TileIndex t) +static inline VehicleType GetDepotVehicleType(Tile t) { switch (GetTileType(t)) { default: NOT_REACHED(); diff --git a/src/industry_cmd.cpp b/src/industry_cmd.cpp index 5870be9d2d..68ad920851 100644 --- a/src/industry_cmd.cpp +++ b/src/industry_cmd.cpp @@ -103,7 +103,7 @@ void ResetIndustries() * @pre IsTileType(tile, MP_INDUSTRY) * @return general type for this industry, as defined in industry.h */ -IndustryType GetIndustryType(TileIndex tile) +IndustryType GetIndustryType(Tile tile) { assert(IsTileType(tile, MP_INDUSTRY)); diff --git a/src/industry_map.h b/src/industry_map.h index 1cdf90e59a..f1e6182755 100644 --- a/src/industry_map.h +++ b/src/industry_map.h @@ -60,10 +60,10 @@ enum IndustryGraphics { * @pre IsTileType(t, MP_INDUSTRY) * @return the industry ID */ -static inline IndustryID GetIndustryIndex(TileIndex t) +static inline IndustryID GetIndustryIndex(Tile t) { assert(IsTileType(t, MP_INDUSTRY)); - return _m[t].m2; + return t.m2(); } /** @@ -72,23 +72,23 @@ static inline IndustryID GetIndustryIndex(TileIndex t) * @pre IsTileType(t, MP_INDUSTRY) * @return true if and only if the industry tile is fully built */ -static inline bool IsIndustryCompleted(TileIndex t) +static inline bool IsIndustryCompleted(Tile t) { assert(IsTileType(t, MP_INDUSTRY)); - return HasBit(_m[t].m1, 7); + return HasBit(t.m1(), 7); } -IndustryType GetIndustryType(TileIndex tile); +IndustryType GetIndustryType(Tile tile); /** * Set if the industry that owns the tile as under construction or not * @param tile the tile to query * @pre IsTileType(tile, MP_INDUSTRY) */ -static inline void SetIndustryCompleted(TileIndex tile) +static inline void SetIndustryCompleted(Tile tile) { assert(IsTileType(tile, MP_INDUSTRY)); - SB(_m[tile].m1, 7, 1, 1); + SB(tile.m1(), 7, 1, 1); } /** @@ -97,10 +97,10 @@ static inline void SetIndustryCompleted(TileIndex tile) * @pre IsTileType(tile, MP_INDUSTRY) * @return the construction stage */ -static inline byte GetIndustryConstructionStage(TileIndex tile) +static inline byte GetIndustryConstructionStage(Tile tile) { assert(IsTileType(tile, MP_INDUSTRY)); - return IsIndustryCompleted(tile) ? (byte)INDUSTRY_COMPLETED : GB(_m[tile].m1, 0, 2); + return IsIndustryCompleted(tile) ? (byte)INDUSTRY_COMPLETED : GB(tile.m1(), 0, 2); } /** @@ -109,10 +109,10 @@ static inline byte GetIndustryConstructionStage(TileIndex tile) * @param value the new construction stage * @pre IsTileType(tile, MP_INDUSTRY) */ -static inline void SetIndustryConstructionStage(TileIndex tile, byte value) +static inline void SetIndustryConstructionStage(Tile tile, byte value) { assert(IsTileType(tile, MP_INDUSTRY)); - SB(_m[tile].m1, 0, 2, value); + SB(tile.m1(), 0, 2, value); } /** @@ -122,10 +122,10 @@ static inline void SetIndustryConstructionStage(TileIndex tile, byte value) * @pre IsTileType(t, MP_INDUSTRY) * @return the gfx ID */ -static inline IndustryGfx GetCleanIndustryGfx(TileIndex t) +static inline IndustryGfx GetCleanIndustryGfx(Tile t) { assert(IsTileType(t, MP_INDUSTRY)); - return _m[t].m5 | (GB(_me[t].m6, 2, 1) << 8); + return t.m5() | (GB(t.m6(), 2, 1) << 8); } /** @@ -134,7 +134,7 @@ static inline IndustryGfx GetCleanIndustryGfx(TileIndex t) * @pre IsTileType(t, MP_INDUSTRY) * @return the gfx ID */ -static inline IndustryGfx GetIndustryGfx(TileIndex t) +static inline IndustryGfx GetIndustryGfx(Tile t) { assert(IsTileType(t, MP_INDUSTRY)); return GetTranslatedIndustryTileID(GetCleanIndustryGfx(t)); @@ -146,11 +146,11 @@ static inline IndustryGfx GetIndustryGfx(TileIndex t) * @pre IsTileType(t, MP_INDUSTRY) * @param gfx the graphics ID */ -static inline void SetIndustryGfx(TileIndex t, IndustryGfx gfx) +static inline void SetIndustryGfx(Tile t, IndustryGfx gfx) { assert(IsTileType(t, MP_INDUSTRY)); - _m[t].m5 = GB(gfx, 0, 8); - SB(_me[t].m6, 2, 1, GB(gfx, 8, 1)); + t.m5() = GB(gfx, 0, 8); + SB(t.m6(), 2, 1, GB(gfx, 8, 1)); } /** @@ -159,10 +159,10 @@ static inline void SetIndustryGfx(TileIndex t, IndustryGfx gfx) * @pre IsTileType(tile, MP_INDUSTRY) * @return the construction counter */ -static inline byte GetIndustryConstructionCounter(TileIndex tile) +static inline byte GetIndustryConstructionCounter(Tile tile) { assert(IsTileType(tile, MP_INDUSTRY)); - return GB(_m[tile].m1, 2, 2); + return GB(tile.m1(), 2, 2); } /** @@ -171,10 +171,10 @@ static inline byte GetIndustryConstructionCounter(TileIndex tile) * @param value the new value for the construction counter * @pre IsTileType(tile, MP_INDUSTRY) */ -static inline void SetIndustryConstructionCounter(TileIndex tile, byte value) +static inline void SetIndustryConstructionCounter(Tile tile, byte value) { assert(IsTileType(tile, MP_INDUSTRY)); - SB(_m[tile].m1, 2, 2, value); + SB(tile.m1(), 2, 2, value); } /** @@ -184,11 +184,11 @@ static inline void SetIndustryConstructionCounter(TileIndex tile, byte value) * @param tile the tile to query * @pre IsTileType(tile, MP_INDUSTRY) */ -static inline void ResetIndustryConstructionStage(TileIndex tile) +static inline void ResetIndustryConstructionStage(Tile tile) { assert(IsTileType(tile, MP_INDUSTRY)); - SB(_m[tile].m1, 0, 4, 0); - SB(_m[tile].m1, 7, 1, 0); + SB(tile.m1(), 0, 4, 0); + SB(tile.m1(), 7, 1, 0); } /** @@ -196,10 +196,10 @@ static inline void ResetIndustryConstructionStage(TileIndex tile) * @param tile the tile to get the animation loop number of * @pre IsTileType(tile, MP_INDUSTRY) */ -static inline byte GetIndustryAnimationLoop(TileIndex tile) +static inline byte GetIndustryAnimationLoop(Tile tile) { assert(IsTileType(tile, MP_INDUSTRY)); - return _m[tile].m4; + return tile.m4(); } /** @@ -208,63 +208,63 @@ static inline byte GetIndustryAnimationLoop(TileIndex tile) * @param count the new animation frame number * @pre IsTileType(tile, MP_INDUSTRY) */ -static inline void SetIndustryAnimationLoop(TileIndex tile, byte count) +static inline void SetIndustryAnimationLoop(Tile tile, byte count) { assert(IsTileType(tile, MP_INDUSTRY)); - _m[tile].m4 = count; + tile.m4() = count; } /** * Get the random bits for this tile. * Used for grf callbacks - * @param tile TileIndex of the tile to query + * @param tile the tile to query * @pre IsTileType(tile, MP_INDUSTRY) * @return requested bits */ -static inline byte GetIndustryRandomBits(TileIndex tile) +static inline byte GetIndustryRandomBits(Tile tile) { assert(IsTileType(tile, MP_INDUSTRY)); - return _m[tile].m3; + return tile.m3(); } /** * Set the random bits for this tile. * Used for grf callbacks - * @param tile TileIndex of the tile to query + * @param tile the tile to query * @param bits the random bits * @pre IsTileType(tile, MP_INDUSTRY) */ -static inline void SetIndustryRandomBits(TileIndex tile, byte bits) +static inline void SetIndustryRandomBits(Tile tile, byte bits) { assert(IsTileType(tile, MP_INDUSTRY)); - _m[tile].m3 = bits; + tile.m3() = bits; } /** * Get the activated triggers bits for this industry tile * Used for grf callbacks - * @param tile TileIndex of the tile to query + * @param tile the tile to query * @pre IsTileType(tile, MP_INDUSTRY) * @return requested triggers */ -static inline byte GetIndustryTriggers(TileIndex tile) +static inline byte GetIndustryTriggers(Tile tile) { assert(IsTileType(tile, MP_INDUSTRY)); - return GB(_me[tile].m6, 3, 3); + return GB(tile.m6(), 3, 3); } /** * Set the activated triggers bits for this industry tile * Used for grf callbacks - * @param tile TileIndex of the tile to query + * @param tile the tile to query * @param triggers the triggers to set * @pre IsTileType(tile, MP_INDUSTRY) */ -static inline void SetIndustryTriggers(TileIndex tile, byte triggers) +static inline void SetIndustryTriggers(Tile tile, byte triggers) { assert(IsTileType(tile, MP_INDUSTRY)); - SB(_me[tile].m6, 3, 3, triggers); + SB(tile.m6(), 3, 3, triggers); } /** @@ -275,17 +275,17 @@ static inline void SetIndustryTriggers(TileIndex tile, byte triggers) * @param random the random value * @param wc the water class for this industry; only useful when build on water */ -static inline void MakeIndustry(TileIndex t, IndustryID index, IndustryGfx gfx, uint8 random, WaterClass wc) +static inline void MakeIndustry(Tile t, IndustryID index, IndustryGfx gfx, uint8 random, WaterClass wc) { SetTileType(t, MP_INDUSTRY); - _m[t].m1 = 0; - _m[t].m2 = index; + t.m1() = 0; + t.m2() = index; SetIndustryRandomBits(t, random); // m3 - _m[t].m4 = 0; + t.m4() = 0; SetIndustryGfx(t, gfx); // m5, part of m6 SetIndustryTriggers(t, 0); // rest of m6 SetWaterClass(t, wc); - _me[t].m7 = 0; + t.m7() = 0; } #endif /* INDUSTRY_MAP_H */ diff --git a/src/map_func.h b/src/map_func.h index 0bcf064302..507fb4914f 100644 --- a/src/map_func.h +++ b/src/map_func.h @@ -49,6 +49,12 @@ public: */ debug_inline Tile(TileIndex tile) : tile(tile) {} + /** + * Create the tile wrapper for the given tile. + * @param tile The tile to access the map for. + */ + Tile(uint tile) : tile(tile) {} + /** * Implicit conversion to the TileIndex. */ @@ -189,16 +195,16 @@ private: * Iterator to iterate all Tiles */ struct Iterator { - typedef TileIndex value_type; - typedef TileIndex *pointer; - typedef TileIndex &reference; + typedef Tile value_type; + typedef Tile *pointer; + typedef Tile &reference; typedef size_t difference_type; typedef std::forward_iterator_tag iterator_category; explicit Iterator(TileIndex index) : index(index) {} bool operator==(const Iterator &other) const { return this->index == other.index; } bool operator!=(const Iterator &other) const { return !(*this == other); } - TileIndex operator*() const { return this->index; } + Tile operator*() const { return this->index; } Iterator & operator++() { this->index++; return *this; } private: TileIndex index; diff --git a/src/misc_gui.cpp b/src/misc_gui.cpp index d1bca7d1b2..896708b77a 100644 --- a/src/misc_gui.cpp +++ b/src/misc_gui.cpp @@ -107,7 +107,7 @@ public: } } - LandInfoWindow(TileIndex tile) : Window(&_land_info_desc), tile(tile) + LandInfoWindow(Tile tile) : Window(&_land_info_desc), tile(tile) { this->InitNested(); @@ -117,16 +117,16 @@ public: # define LANDINFOD_LEVEL 1 #endif Debug(misc, LANDINFOD_LEVEL, "TILE: {:#x} ({},{})", tile, TileX(tile), TileY(tile)); - Debug(misc, LANDINFOD_LEVEL, "type = {:#x}", _m[tile].type); - Debug(misc, LANDINFOD_LEVEL, "height = {:#x}", _m[tile].height); - Debug(misc, LANDINFOD_LEVEL, "m1 = {:#x}", _m[tile].m1); - Debug(misc, LANDINFOD_LEVEL, "m2 = {:#x}", _m[tile].m2); - Debug(misc, LANDINFOD_LEVEL, "m3 = {:#x}", _m[tile].m3); - Debug(misc, LANDINFOD_LEVEL, "m4 = {:#x}", _m[tile].m4); - Debug(misc, LANDINFOD_LEVEL, "m5 = {:#x}", _m[tile].m5); - Debug(misc, LANDINFOD_LEVEL, "m6 = {:#x}", _me[tile].m6); - Debug(misc, LANDINFOD_LEVEL, "m7 = {:#x}", _me[tile].m7); - Debug(misc, LANDINFOD_LEVEL, "m8 = {:#x}", _me[tile].m8); + Debug(misc, LANDINFOD_LEVEL, "type = {:#x}", tile.type()); + Debug(misc, LANDINFOD_LEVEL, "height = {:#x}", tile.height()); + Debug(misc, LANDINFOD_LEVEL, "m1 = {:#x}", tile.m1()); + Debug(misc, LANDINFOD_LEVEL, "m2 = {:#x}", tile.m2()); + Debug(misc, LANDINFOD_LEVEL, "m3 = {:#x}", tile.m3()); + Debug(misc, LANDINFOD_LEVEL, "m4 = {:#x}", tile.m4()); + Debug(misc, LANDINFOD_LEVEL, "m5 = {:#x}", tile.m5()); + Debug(misc, LANDINFOD_LEVEL, "m6 = {:#x}", tile.m6()); + Debug(misc, LANDINFOD_LEVEL, "m7 = {:#x}", tile.m7()); + Debug(misc, LANDINFOD_LEVEL, "m8 = {:#x}", tile.m8()); #undef LANDINFOD_LEVEL } diff --git a/src/object_cmd.cpp b/src/object_cmd.cpp index e27b4a600a..5f37b6d1c7 100644 --- a/src/object_cmd.cpp +++ b/src/object_cmd.cpp @@ -61,7 +61,7 @@ uint16 Object::counts[NUM_OBJECTS]; * @pre IsTileType(t, MP_OBJECT) * @return the type. */ -ObjectType GetObjectType(TileIndex t) +ObjectType GetObjectType(Tile t) { assert(IsTileType(t, MP_OBJECT)); return Object::GetByTile(t)->type; diff --git a/src/object_map.h b/src/object_map.h index d86bf0690e..1a1af93444 100644 --- a/src/object_map.h +++ b/src/object_map.h @@ -13,7 +13,7 @@ #include "water_map.h" #include "object_type.h" -ObjectType GetObjectType(TileIndex t); +ObjectType GetObjectType(Tile t); /** * Check whether the object on a tile is of a specific type. @@ -22,7 +22,7 @@ ObjectType GetObjectType(TileIndex t); * @pre IsTileType(t, MP_OBJECT) * @return True if type matches. */ -static inline bool IsObjectType(TileIndex t, ObjectType type) +static inline bool IsObjectType(Tile t, ObjectType type) { return GetObjectType(t) == type; } @@ -33,7 +33,7 @@ static inline bool IsObjectType(TileIndex t, ObjectType type) * @param type Type to test. * @return True if type matches. */ -static inline bool IsObjectTypeTile(TileIndex t, ObjectType type) +static inline bool IsObjectTypeTile(Tile t, ObjectType type) { return IsTileType(t, MP_OBJECT) && GetObjectType(t) == type; } @@ -44,10 +44,10 @@ static inline bool IsObjectTypeTile(TileIndex t, ObjectType type) * @pre IsTileType(t, MP_OBJECT) * @return The ObjectID of the object. */ -static inline ObjectID GetObjectIndex(TileIndex t) +static inline ObjectID GetObjectIndex(Tile t) { assert(IsTileType(t, MP_OBJECT)); - return _m[t].m2 | _m[t].m5 << 16; + return t.m2() | t.m5() << 16; } /** @@ -56,10 +56,10 @@ static inline ObjectID GetObjectIndex(TileIndex t) * @pre IsTileType(t, MP_OBJECT) * @return The random bits. */ -static inline byte GetObjectRandomBits(TileIndex t) +static inline byte GetObjectRandomBits(Tile t) { assert(IsTileType(t, MP_OBJECT)); - return _m[t].m3; + return t.m3(); } @@ -71,17 +71,17 @@ static inline byte GetObjectRandomBits(TileIndex t) * @param wc Water class for this object. * @param random Random data to store on the tile */ -static inline void MakeObject(TileIndex t, Owner o, ObjectID index, WaterClass wc, byte random) +static inline void MakeObject(Tile t, Owner o, ObjectID index, WaterClass wc, byte random) { SetTileType(t, MP_OBJECT); SetTileOwner(t, o); SetWaterClass(t, wc); - _m[t].m2 = index; - _m[t].m3 = random; - _m[t].m4 = 0; - _m[t].m5 = index >> 16; - SB(_me[t].m6, 2, 4, 0); - _me[t].m7 = 0; + t.m2() = index; + t.m3() = random; + t.m4() = 0; + t.m5() = index >> 16; + SB(t.m6(), 2, 4, 0); + t.m7() = 0; } #endif /* OBJECT_MAP_H */ diff --git a/src/pathfinder/npf/npf.cpp b/src/pathfinder/npf/npf.cpp index c880124dd9..90112a5fc3 100644 --- a/src/pathfinder/npf/npf.cpp +++ b/src/pathfinder/npf/npf.cpp @@ -144,7 +144,7 @@ static uint NPFHash(uint key1, uint key2) uint part2 = TileY(key1) & NPF_HASH_HALFMASK; assert(IsValidTrackdir((Trackdir)key2)); - assert(IsValidTile(key1)); + assert(IsValidTile((TileIndex)key1)); return ((part1 << NPF_HASH_HALFBITS | part2) + (NPF_HASH_SIZE * key2 / TRACKDIR_END)) % NPF_HASH_SIZE; } diff --git a/src/rail.cpp b/src/rail.cpp index 63afa379e0..dd7550d7d3 100644 --- a/src/rail.cpp +++ b/src/rail.cpp @@ -152,7 +152,7 @@ extern const TrackdirBits _uphill_trackdirs[] = { /** * Return the rail type of tile, or INVALID_RAILTYPE if this is no rail tile. */ -RailType GetTileRailType(TileIndex tile) +RailType GetTileRailType(Tile tile) { switch (GetTileType(tile)) { case MP_RAILWAY: diff --git a/src/rail_map.h b/src/rail_map.h index 5717da3613..25f61339eb 100644 --- a/src/rail_map.h +++ b/src/rail_map.h @@ -33,10 +33,10 @@ enum RailTileType { * @pre IsTileType(t, MP_RAILWAY) * @return the RailTileType */ -debug_inline static RailTileType GetRailTileType(TileIndex t) +debug_inline static RailTileType GetRailTileType(Tile t) { assert(IsTileType(t, MP_RAILWAY)); - return (RailTileType)GB(_m[t].m5, 6, 2); + return (RailTileType)GB(t.m5(), 6, 2); } /** @@ -46,7 +46,7 @@ debug_inline static RailTileType GetRailTileType(TileIndex t) * @pre IsTileType(t, MP_RAILWAY) * @return true if and only if the tile is normal rail (with or without signals) */ -debug_inline static bool IsPlainRail(TileIndex t) +debug_inline static bool IsPlainRail(Tile t) { RailTileType rtt = GetRailTileType(t); return rtt == RAIL_TILE_NORMAL || rtt == RAIL_TILE_SIGNALS; @@ -57,7 +57,7 @@ debug_inline static bool IsPlainRail(TileIndex t) * @param t the tile to get the information from * @return true if and only if the tile is normal rail (with or without signals) */ -debug_inline static bool IsPlainRailTile(TileIndex t) +debug_inline static bool IsPlainRailTile(Tile t) { return IsTileType(t, MP_RAILWAY) && IsPlainRail(t); } @@ -69,7 +69,7 @@ debug_inline static bool IsPlainRailTile(TileIndex t) * @pre IsTileType(t, MP_RAILWAY) * @return true if and only if the tile has signals */ -static inline bool HasSignals(TileIndex t) +static inline bool HasSignals(Tile t) { return GetRailTileType(t) == RAIL_TILE_SIGNALS; } @@ -80,10 +80,10 @@ static inline bool HasSignals(TileIndex t) * @param signals whether the rail tile should have signals or not * @pre IsPlainRailTile(tile) */ -static inline void SetHasSignals(TileIndex tile, bool signals) +static inline void SetHasSignals(Tile tile, bool signals) { assert(IsPlainRailTile(tile)); - SB(_m[tile].m5, 6, 1, signals); + SB(tile.m5(), 6, 1, signals); } /** @@ -92,7 +92,7 @@ static inline void SetHasSignals(TileIndex tile, bool signals) * @pre IsTileType(t, MP_RAILWAY) * @return true if and only if the tile is a rail depot */ -debug_inline static bool IsRailDepot(TileIndex t) +debug_inline static bool IsRailDepot(Tile t) { return GetRailTileType(t) == RAIL_TILE_DEPOT; } @@ -102,7 +102,7 @@ debug_inline static bool IsRailDepot(TileIndex t) * @param t the tile to get the information from * @return true if and only if the tile is a rail depot */ -debug_inline static bool IsRailDepotTile(TileIndex t) +debug_inline static bool IsRailDepotTile(Tile t) { return IsTileType(t, MP_RAILWAY) && IsRailDepot(t); } @@ -112,9 +112,9 @@ debug_inline static bool IsRailDepotTile(TileIndex t) * @param t the tile to get the rail type from * @return the rail type of the tile */ -static inline RailType GetRailType(TileIndex t) +static inline RailType GetRailType(Tile t) { - return (RailType)GB(_me[t].m8, 0, 6); + return (RailType)GB(t.m8(), 0, 6); } /** @@ -122,9 +122,9 @@ static inline RailType GetRailType(TileIndex t) * @param t the tile to set the rail type of * @param r the new rail type for the tile */ -static inline void SetRailType(TileIndex t, RailType r) +static inline void SetRailType(Tile t, RailType r) { - SB(_me[t].m8, 0, 6, r); + SB(t.m8(), 0, 6, r); } @@ -133,10 +133,10 @@ static inline void SetRailType(TileIndex t, RailType r) * @param tile the tile to get the track bits from * @return the track bits of the tile */ -static inline TrackBits GetTrackBits(TileIndex tile) +static inline TrackBits GetTrackBits(Tile tile) { assert(IsPlainRailTile(tile)); - return (TrackBits)GB(_m[tile].m5, 0, 6); + return (TrackBits)GB(tile.m5(), 0, 6); } /** @@ -144,10 +144,10 @@ static inline TrackBits GetTrackBits(TileIndex tile) * @param t the tile to set the track bits of * @param b the new track bits for the tile */ -static inline void SetTrackBits(TileIndex t, TrackBits b) +static inline void SetTrackBits(Tile t, TrackBits b) { assert(IsPlainRailTile(t)); - SB(_m[t].m5, 0, 6, b); + SB(t.m5(), 0, 6, b); } /** @@ -157,7 +157,7 @@ static inline void SetTrackBits(TileIndex t, TrackBits b) * @pre IsPlainRailTile(tile) * @return true if and only if the given track exists on the tile */ -static inline bool HasTrack(TileIndex tile, Track track) +static inline bool HasTrack(Tile tile, Track track) { return HasBit(GetTrackBits(tile), track); } @@ -168,9 +168,9 @@ static inline bool HasTrack(TileIndex tile, Track track) * @pre IsRailDepotTile(t) * @return the direction the depot is facing */ -static inline DiagDirection GetRailDepotDirection(TileIndex t) +static inline DiagDirection GetRailDepotDirection(Tile t) { - return (DiagDirection)GB(_m[t].m5, 0, 2); + return (DiagDirection)GB(t.m5(), 0, 2); } /** @@ -179,7 +179,7 @@ static inline DiagDirection GetRailDepotDirection(TileIndex t) * @param t the tile to get the depot track from * @return the track of the depot */ -static inline Track GetRailDepotTrack(TileIndex t) +static inline Track GetRailDepotTrack(Tile t) { return DiagDirToDiagTrack(GetRailDepotDirection(t)); } @@ -191,13 +191,13 @@ static inline Track GetRailDepotTrack(TileIndex t) * @param t the tile to query * @return the track bits */ -static inline TrackBits GetRailReservationTrackBits(TileIndex t) +static inline TrackBits GetRailReservationTrackBits(Tile t) { assert(IsPlainRailTile(t)); - byte track_b = GB(_m[t].m2, 8, 3); + byte track_b = GB(t.m2(), 8, 3); Track track = (Track)(track_b - 1); // map array saves Track+1 if (track_b == 0) return TRACK_BIT_NONE; - return (TrackBits)(TrackToTrackBits(track) | (HasBit(_m[t].m2, 11) ? TrackToTrackBits(TrackToOppositeTrack(track)) : 0)); + return (TrackBits)(TrackToTrackBits(track) | (HasBit(t.m2(), 11) ? TrackToTrackBits(TrackToOppositeTrack(track)) : 0)); } /** @@ -206,14 +206,14 @@ static inline TrackBits GetRailReservationTrackBits(TileIndex t) * @param t the tile to change * @param b the track bits */ -static inline void SetTrackReservation(TileIndex t, TrackBits b) +static inline void SetTrackReservation(Tile t, TrackBits b) { assert(IsPlainRailTile(t)); assert(b != INVALID_TRACK_BIT); assert(!TracksOverlap(b)); Track track = RemoveFirstTrack(&b); - SB(_m[t].m2, 8, 3, track == INVALID_TRACK ? 0 : track + 1); - SB(_m[t].m2, 11, 1, (byte)(b != TRACK_BIT_NONE)); + SB(t.m2(), 8, 3, track == INVALID_TRACK ? 0 : track + 1); + SB(t.m2(), 11, 1, (byte)(b != TRACK_BIT_NONE)); } /** @@ -223,7 +223,7 @@ static inline void SetTrackReservation(TileIndex t, TrackBits b) * @param t the rack to reserve * @return true if successful */ -static inline bool TryReserveTrack(TileIndex tile, Track t) +static inline bool TryReserveTrack(Tile tile, Track t) { assert(HasTrack(tile, t)); TrackBits bits = TrackToTrackBits(t); @@ -241,7 +241,7 @@ static inline bool TryReserveTrack(TileIndex tile, Track t) * @param tile the tile * @param t the track to free */ -static inline void UnreserveTrack(TileIndex tile, Track t) +static inline void UnreserveTrack(Tile tile, Track t) { assert(HasTrack(tile, t)); TrackBits res = GetRailReservationTrackBits(tile); @@ -255,10 +255,10 @@ static inline void UnreserveTrack(TileIndex tile, Track t) * @param t the depot tile * @return reservation state */ -static inline bool HasDepotReservation(TileIndex t) +static inline bool HasDepotReservation(Tile t) { assert(IsRailDepot(t)); - return HasBit(_m[t].m5, 4); + return HasBit(t.m5(), 4); } /** @@ -267,10 +267,10 @@ static inline bool HasDepotReservation(TileIndex t) * @param t the depot tile * @param b the reservation state */ -static inline void SetDepotReservation(TileIndex t, bool b) +static inline void SetDepotReservation(Tile t, bool b) { assert(IsRailDepot(t)); - SB(_m[t].m5, 4, 1, (byte)b); + SB(t.m5(), 4, 1, (byte)b); } /** @@ -279,7 +279,7 @@ static inline void SetDepotReservation(TileIndex t, bool b) * @param t the tile * @return reserved track bits */ -static inline TrackBits GetDepotReservationTrackBits(TileIndex t) +static inline TrackBits GetDepotReservationTrackBits(Tile t) { return HasDepotReservation(t) ? TrackToTrackBits(GetRailDepotTrack(t)) : TRACK_BIT_NONE; } @@ -290,58 +290,58 @@ static inline bool IsPbsSignal(SignalType s) return s == SIGTYPE_PBS || s == SIGTYPE_PBS_ONEWAY; } -static inline SignalType GetSignalType(TileIndex t, Track track) +static inline SignalType GetSignalType(Tile t, Track track) { assert(GetRailTileType(t) == RAIL_TILE_SIGNALS); byte pos = (track == TRACK_LOWER || track == TRACK_RIGHT) ? 4 : 0; - return (SignalType)GB(_m[t].m2, pos, 3); + return (SignalType)GB(t.m2(), pos, 3); } -static inline void SetSignalType(TileIndex t, Track track, SignalType s) +static inline void SetSignalType(Tile t, Track track, SignalType s) { assert(GetRailTileType(t) == RAIL_TILE_SIGNALS); byte pos = (track == TRACK_LOWER || track == TRACK_RIGHT) ? 4 : 0; - SB(_m[t].m2, pos, 3, s); - if (track == INVALID_TRACK) SB(_m[t].m2, 4, 3, s); + SB(t.m2(), pos, 3, s); + if (track == INVALID_TRACK) SB(t.m2(), 4, 3, s); } -static inline bool IsPresignalEntry(TileIndex t, Track track) +static inline bool IsPresignalEntry(Tile t, Track track) { return GetSignalType(t, track) == SIGTYPE_ENTRY || GetSignalType(t, track) == SIGTYPE_COMBO; } -static inline bool IsPresignalExit(TileIndex t, Track track) +static inline bool IsPresignalExit(Tile t, Track track) { return GetSignalType(t, track) == SIGTYPE_EXIT || GetSignalType(t, track) == SIGTYPE_COMBO; } /** One-way signals can't be passed the 'wrong' way. */ -static inline bool IsOnewaySignal(TileIndex t, Track track) +static inline bool IsOnewaySignal(Tile t, Track track) { return GetSignalType(t, track) != SIGTYPE_PBS; } -static inline void CycleSignalSide(TileIndex t, Track track) +static inline void CycleSignalSide(Tile t, Track track) { byte sig; byte pos = (track == TRACK_LOWER || track == TRACK_RIGHT) ? 4 : 6; - sig = GB(_m[t].m3, pos, 2); + sig = GB(t.m3(), pos, 2); if (--sig == 0) sig = IsPbsSignal(GetSignalType(t, track)) ? 2 : 3; - SB(_m[t].m3, pos, 2, sig); + SB(t.m3(), pos, 2, sig); } -static inline SignalVariant GetSignalVariant(TileIndex t, Track track) +static inline SignalVariant GetSignalVariant(Tile t, Track track) { byte pos = (track == TRACK_LOWER || track == TRACK_RIGHT) ? 7 : 3; - return (SignalVariant)GB(_m[t].m2, pos, 1); + return (SignalVariant)GB(t.m2(), pos, 1); } -static inline void SetSignalVariant(TileIndex t, Track track, SignalVariant v) +static inline void SetSignalVariant(Tile t, Track track, SignalVariant v) { byte pos = (track == TRACK_LOWER || track == TRACK_RIGHT) ? 7 : 3; - SB(_m[t].m2, pos, 1, v); - if (track == INVALID_TRACK) SB(_m[t].m2, 7, 1, v); + SB(t.m2(), pos, 1, v); + if (track == INVALID_TRACK) SB(t.m2(), 7, 1, v); } /** @@ -349,9 +349,9 @@ static inline void SetSignalVariant(TileIndex t, Track track, SignalVariant v) * @param tile the tile to set the states for * @param state the new state */ -static inline void SetSignalStates(TileIndex tile, uint state) +static inline void SetSignalStates(Tile tile, uint state) { - SB(_m[tile].m4, 4, 4, state); + SB(tile.m4(), 4, 4, state); } /** @@ -359,9 +359,9 @@ static inline void SetSignalStates(TileIndex tile, uint state) * @param tile the tile to set the states for * @return the state of the signals */ -static inline uint GetSignalStates(TileIndex tile) +static inline uint GetSignalStates(Tile tile) { - return GB(_m[tile].m4, 4, 4); + return GB(tile.m4(), 4, 4); } /** @@ -370,7 +370,7 @@ static inline uint GetSignalStates(TileIndex tile) * @param signalbit the signal * @return the state of the signal */ -static inline SignalState GetSingleSignalState(TileIndex t, byte signalbit) +static inline SignalState GetSingleSignalState(Tile t, byte signalbit) { return (SignalState)HasBit(GetSignalStates(t), signalbit); } @@ -380,9 +380,9 @@ static inline SignalState GetSingleSignalState(TileIndex t, byte signalbit) * @param tile the tile to set the present signals for * @param signals the signals that have to be present */ -static inline void SetPresentSignals(TileIndex tile, uint signals) +static inline void SetPresentSignals(Tile tile, uint signals) { - SB(_m[tile].m3, 4, 4, signals); + SB(tile.m3(), 4, 4, signals); } /** @@ -390,9 +390,9 @@ static inline void SetPresentSignals(TileIndex tile, uint signals) * @param tile the tile to get the present signals for * @return the signals that are present */ -static inline uint GetPresentSignals(TileIndex tile) +static inline uint GetPresentSignals(Tile tile) { - return GB(_m[tile].m3, 4, 4); + return GB(tile.m3(), 4, 4); } /** @@ -401,7 +401,7 @@ static inline uint GetPresentSignals(TileIndex tile) * @param signalbit the signal * @return true if and only if the signal is present */ -static inline bool IsSignalPresent(TileIndex t, byte signalbit) +static inline bool IsSignalPresent(Tile t, byte signalbit) { return HasBit(GetPresentSignals(t), signalbit); } @@ -410,7 +410,7 @@ static inline bool IsSignalPresent(TileIndex t, byte signalbit) * Checks for the presence of signals (either way) on the given track on the * given rail tile. */ -static inline bool HasSignalOnTrack(TileIndex tile, Track track) +static inline bool HasSignalOnTrack(Tile tile, Track track) { assert(IsValidTrack(track)); return GetRailTileType(tile) == RAIL_TILE_SIGNALS && (GetPresentSignals(tile) & SignalOnTrack(track)) != 0; @@ -423,7 +423,7 @@ static inline bool HasSignalOnTrack(TileIndex tile, Track track) * Along meaning if you are currently driving on the given trackdir, this is * the signal that is facing us (for which we stop when it's red). */ -static inline bool HasSignalOnTrackdir(TileIndex tile, Trackdir trackdir) +static inline bool HasSignalOnTrackdir(Tile tile, Trackdir trackdir) { assert (IsValidTrackdir(trackdir)); return GetRailTileType(tile) == RAIL_TILE_SIGNALS && GetPresentSignals(tile) & SignalAlongTrackdir(trackdir); @@ -435,7 +435,7 @@ static inline bool HasSignalOnTrackdir(TileIndex tile, Trackdir trackdir) * Along meaning if you are currently driving on the given trackdir, this is * the signal that is facing us (for which we stop when it's red). */ -static inline SignalState GetSignalStateByTrackdir(TileIndex tile, Trackdir trackdir) +static inline SignalState GetSignalStateByTrackdir(Tile tile, Trackdir trackdir) { assert(IsValidTrackdir(trackdir)); assert(HasSignalOnTrack(tile, TrackdirToTrack(trackdir))); @@ -446,7 +446,7 @@ static inline SignalState GetSignalStateByTrackdir(TileIndex tile, Trackdir trac /** * Sets the state of the signal along the given trackdir. */ -static inline void SetSignalStateByTrackdir(TileIndex tile, Trackdir trackdir, SignalState state) +static inline void SetSignalStateByTrackdir(Tile tile, Trackdir trackdir, SignalState state) { if (state == SIGNAL_STATE_GREEN) { // set 1 SetSignalStates(tile, GetSignalStates(tile) | SignalAlongTrackdir(trackdir)); @@ -460,7 +460,7 @@ static inline void SetSignalStateByTrackdir(TileIndex tile, Trackdir trackdir, S * @param tile the tile to check * @param td the trackdir to check */ -static inline bool HasPbsSignalOnTrackdir(TileIndex tile, Trackdir td) +static inline bool HasPbsSignalOnTrackdir(Tile tile, Trackdir td) { return IsTileType(tile, MP_RAILWAY) && HasSignalOnTrackdir(tile, td) && IsPbsSignal(GetSignalType(tile, TrackdirToTrack(td))); @@ -472,14 +472,14 @@ static inline bool HasPbsSignalOnTrackdir(TileIndex tile, Trackdir td) * @param tile the tile to check * @param td the trackdir to check */ -static inline bool HasOnewaySignalBlockingTrackdir(TileIndex tile, Trackdir td) +static inline bool HasOnewaySignalBlockingTrackdir(Tile tile, Trackdir td) { return IsTileType(tile, MP_RAILWAY) && HasSignalOnTrackdir(tile, ReverseTrackdir(td)) && !HasSignalOnTrackdir(tile, td) && IsOnewaySignal(tile, TrackdirToTrack(td)); } -RailType GetTileRailType(TileIndex tile); +RailType GetTileRailType(Tile tile); /** The ground 'under' the rail */ enum RailGroundType { @@ -500,49 +500,49 @@ enum RailGroundType { RAIL_GROUND_HALF_SNOW = 14, ///< Snow only on higher part of slope (steep or one corner raised) }; -static inline void SetRailGroundType(TileIndex t, RailGroundType rgt) +static inline void SetRailGroundType(Tile t, RailGroundType rgt) { - SB(_m[t].m4, 0, 4, rgt); + SB(t.m4(), 0, 4, rgt); } -static inline RailGroundType GetRailGroundType(TileIndex t) +static inline RailGroundType GetRailGroundType(Tile t) { - return (RailGroundType)GB(_m[t].m4, 0, 4); + return (RailGroundType)GB(t.m4(), 0, 4); } -static inline bool IsSnowRailGround(TileIndex t) +static inline bool IsSnowRailGround(Tile t) { return GetRailGroundType(t) == RAIL_GROUND_ICE_DESERT; } -static inline void MakeRailNormal(TileIndex t, Owner o, TrackBits b, RailType r) +static inline void MakeRailNormal(Tile t, Owner o, TrackBits b, RailType r) { SetTileType(t, MP_RAILWAY); SetTileOwner(t, o); SetDockingTile(t, false); - _m[t].m2 = 0; - _m[t].m3 = 0; - _m[t].m4 = 0; - _m[t].m5 = RAIL_TILE_NORMAL << 6 | b; - SB(_me[t].m6, 2, 4, 0); - _me[t].m7 = 0; - _me[t].m8 = r; + t.m2() = 0; + t.m3() = 0; + t.m4() = 0; + t.m5() = RAIL_TILE_NORMAL << 6 | b; + SB(t.m6(), 2, 4, 0); + t.m7() = 0; + t.m8() = r; } -static inline void MakeRailDepot(TileIndex t, Owner o, DepotID did, DiagDirection d, RailType r) +static inline void MakeRailDepot(Tile t, Owner o, DepotID did, DiagDirection d, RailType r) { SetTileType(t, MP_RAILWAY); SetTileOwner(t, o); SetDockingTile(t, false); - _m[t].m2 = did; - _m[t].m3 = 0; - _m[t].m4 = 0; - _m[t].m5 = RAIL_TILE_DEPOT << 6 | d; - SB(_me[t].m6, 2, 4, 0); - _me[t].m7 = 0; - _me[t].m8 = r; + t.m2() = did; + t.m3() = 0; + t.m4() = 0; + t.m5() = RAIL_TILE_DEPOT << 6 | d; + SB(t.m6(), 2, 4, 0); + t.m7() = 0; + t.m8() = r; } #endif /* RAIL_MAP_H */ diff --git a/src/road_map.cpp b/src/road_map.cpp index 9954e42993..d5642ff48c 100644 --- a/src/road_map.cpp +++ b/src/road_map.cpp @@ -30,7 +30,7 @@ * @param straight_tunnel_bridge_entrance whether to return straight road bits for tunnels/bridges. * @return the road bits of the given tile */ -RoadBits GetAnyRoadBits(TileIndex tile, RoadTramType rtt, bool straight_tunnel_bridge_entrance) +RoadBits GetAnyRoadBits(Tile tile, RoadTramType rtt, bool straight_tunnel_bridge_entrance) { if (!MayHaveRoad(tile) || !HasTileRoadType(tile, rtt)) return ROAD_NONE; diff --git a/src/road_map.h b/src/road_map.h index 3114bcc18a..2b95993aa7 100644 --- a/src/road_map.h +++ b/src/road_map.h @@ -30,7 +30,7 @@ enum RoadTileType { * @param t Tile to query. * @return true if tile can be queried about road/tram types. */ -static inline bool MayHaveRoad(TileIndex t) +static inline bool MayHaveRoad(Tile t) { switch (GetTileType(t)) { case MP_ROAD: @@ -49,10 +49,10 @@ static inline bool MayHaveRoad(TileIndex t) * @pre IsTileType(t, MP_ROAD) * @return The road tile type. */ -debug_inline static RoadTileType GetRoadTileType(TileIndex t) +debug_inline static RoadTileType GetRoadTileType(Tile t) { assert(IsTileType(t, MP_ROAD)); - return (RoadTileType)GB(_m[t].m5, 6, 2); + return (RoadTileType)GB(t.m5(), 6, 2); } /** @@ -61,7 +61,7 @@ debug_inline static RoadTileType GetRoadTileType(TileIndex t) * @pre IsTileType(t, MP_ROAD) * @return True if normal road. */ -debug_inline static bool IsNormalRoad(TileIndex t) +debug_inline static bool IsNormalRoad(Tile t) { return GetRoadTileType(t) == ROAD_TILE_NORMAL; } @@ -71,7 +71,7 @@ debug_inline static bool IsNormalRoad(TileIndex t) * @param t Tile to query. * @return True if normal road tile. */ -debug_inline static bool IsNormalRoadTile(TileIndex t) +debug_inline static bool IsNormalRoadTile(Tile t) { return IsTileType(t, MP_ROAD) && IsNormalRoad(t); } @@ -82,7 +82,7 @@ debug_inline static bool IsNormalRoadTile(TileIndex t) * @pre IsTileType(t, MP_ROAD) * @return True if level crossing. */ -static inline bool IsLevelCrossing(TileIndex t) +static inline bool IsLevelCrossing(Tile t) { return GetRoadTileType(t) == ROAD_TILE_CROSSING; } @@ -92,7 +92,7 @@ static inline bool IsLevelCrossing(TileIndex t) * @param t Tile to query. * @return True if level crossing tile. */ -static inline bool IsLevelCrossingTile(TileIndex t) +static inline bool IsLevelCrossingTile(Tile t) { return IsTileType(t, MP_ROAD) && IsLevelCrossing(t); } @@ -103,7 +103,7 @@ static inline bool IsLevelCrossingTile(TileIndex t) * @pre IsTileType(t, MP_ROAD) * @return True if road depot. */ -debug_inline static bool IsRoadDepot(TileIndex t) +debug_inline static bool IsRoadDepot(Tile t) { return GetRoadTileType(t) == ROAD_TILE_DEPOT; } @@ -113,7 +113,7 @@ debug_inline static bool IsRoadDepot(TileIndex t) * @param t Tile to query. * @return True if road depot tile. */ -debug_inline static bool IsRoadDepotTile(TileIndex t) +debug_inline static bool IsRoadDepotTile(Tile t) { return IsTileType(t, MP_ROAD) && IsRoadDepot(t); } @@ -125,11 +125,11 @@ debug_inline static bool IsRoadDepotTile(TileIndex t) * @pre IsNormalRoad(t) * @return The present road bits for the road type. */ -static inline RoadBits GetRoadBits(TileIndex t, RoadTramType rtt) +static inline RoadBits GetRoadBits(Tile t, RoadTramType rtt) { assert(IsNormalRoad(t)); - if (rtt == RTT_TRAM) return (RoadBits)GB(_m[t].m3, 0, 4); - return (RoadBits)GB(_m[t].m5, 0, 4); + if (rtt == RTT_TRAM) return (RoadBits)GB(t.m3(), 0, 4); + return (RoadBits)GB(t.m5(), 0, 4); } /** @@ -138,7 +138,7 @@ static inline RoadBits GetRoadBits(TileIndex t, RoadTramType rtt) * @param tile The tile from which we want to get the RoadBits * @return all set RoadBits of the tile */ -static inline RoadBits GetAllRoadBits(TileIndex tile) +static inline RoadBits GetAllRoadBits(Tile tile) { return GetRoadBits(tile, RTT_ROAD) | GetRoadBits(tile, RTT_TRAM); } @@ -150,29 +150,29 @@ static inline RoadBits GetAllRoadBits(TileIndex tile) * @param rt Road type. * @pre IsNormalRoad(t) */ -static inline void SetRoadBits(TileIndex t, RoadBits r, RoadTramType rtt) +static inline void SetRoadBits(Tile t, RoadBits r, RoadTramType rtt) { assert(IsNormalRoad(t)); // XXX incomplete if (rtt == RTT_TRAM) { - SB(_m[t].m3, 0, 4, r); + SB(t.m3(), 0, 4, r); } else { - SB(_m[t].m5, 0, 4, r); + SB(t.m5(), 0, 4, r); } } -static inline RoadType GetRoadTypeRoad(TileIndex t) +static inline RoadType GetRoadTypeRoad(Tile t) { assert(MayHaveRoad(t)); - return (RoadType)GB(_m[t].m4, 0, 6); + return (RoadType)GB(t.m4(), 0, 6); } -static inline RoadType GetRoadTypeTram(TileIndex t) +static inline RoadType GetRoadTypeTram(Tile t) { assert(MayHaveRoad(t)); - return (RoadType)GB(_me[t].m8, 6, 6); + return (RoadType)GB(t.m8(), 6, 6); } -static inline RoadType GetRoadType(TileIndex t, RoadTramType rtt) +static inline RoadType GetRoadType(Tile t, RoadTramType rtt) { return (rtt == RTT_TRAM) ? GetRoadTypeTram(t) : GetRoadTypeRoad(t); } @@ -182,7 +182,7 @@ static inline RoadType GetRoadType(TileIndex t, RoadTramType rtt) * @param t The tile to query. * @return Present road types. */ -static inline RoadTypes GetPresentRoadTypes(TileIndex t) +static inline RoadTypes GetPresentRoadTypes(Tile t) { RoadTypes result = ROADTYPES_NONE; if (MayHaveRoad(t)) { @@ -192,12 +192,12 @@ static inline RoadTypes GetPresentRoadTypes(TileIndex t) return result; } -static inline bool HasRoadTypeRoad(TileIndex t) +static inline bool HasRoadTypeRoad(Tile t) { return GetRoadTypeRoad(t) != INVALID_ROADTYPE; } -static inline bool HasRoadTypeTram(TileIndex t) +static inline bool HasRoadTypeTram(Tile t) { return GetRoadTypeTram(t) != INVALID_ROADTYPE; } @@ -208,7 +208,7 @@ static inline bool HasRoadTypeTram(TileIndex t) * @param tram True to check tram, false to check road. * @return True if the tile has the specified road type. */ -static inline bool HasTileRoadType(TileIndex t, RoadTramType rtt) +static inline bool HasTileRoadType(Tile t, RoadTramType rtt) { return GetRoadType(t, rtt) != INVALID_ROADTYPE; } @@ -219,7 +219,7 @@ static inline bool HasTileRoadType(TileIndex t, RoadTramType rtt) * @param rts Allowed road types. * @return True if the tile has one of the specified road types. */ -static inline bool HasTileAnyRoadType(TileIndex t, RoadTypes rts) +static inline bool HasTileAnyRoadType(Tile t, RoadTypes rts) { if (!MayHaveRoad(t)) return false; return (GetPresentRoadTypes(t) & rts); @@ -231,14 +231,14 @@ static inline bool HasTileAnyRoadType(TileIndex t, RoadTypes rts) * @param rtt RoadTramType. * @return Owner of the given road type. */ -static inline Owner GetRoadOwner(TileIndex t, RoadTramType rtt) +static inline Owner GetRoadOwner(Tile t, RoadTramType rtt) { assert(MayHaveRoad(t)); - if (rtt == RTT_ROAD) return (Owner)GB(IsNormalRoadTile(t) ? _m[t].m1 : _me[t].m7, 0, 5); + if (rtt == RTT_ROAD) return (Owner)GB(IsNormalRoadTile(t) ? t.m1() : t.m7(), 0, 5); /* Trams don't need OWNER_TOWN, and remapping OWNER_NONE * to OWNER_TOWN makes it use one bit less */ - Owner o = (Owner)GB(_m[t].m3, 4, 4); + Owner o = (Owner)GB(t.m3(), 4, 4); return o == OWNER_TOWN ? OWNER_NONE : o; } @@ -248,12 +248,12 @@ static inline Owner GetRoadOwner(TileIndex t, RoadTramType rtt) * @param rtt RoadTramType. * @param o New owner of the given road type. */ -static inline void SetRoadOwner(TileIndex t, RoadTramType rtt, Owner o) +static inline void SetRoadOwner(Tile t, RoadTramType rtt, Owner o) { if (rtt == RTT_ROAD) { - SB(IsNormalRoadTile(t) ? _m[t].m1 : _me[t].m7, 0, 5, o); + SB(IsNormalRoadTile(t) ? t.m1() : t.m7(), 0, 5, o); } else { - SB(_m[t].m3, 4, 4, o == OWNER_NONE ? OWNER_TOWN : o); + SB(t.m3(), 4, 4, o == OWNER_NONE ? OWNER_TOWN : o); } } @@ -265,7 +265,7 @@ static inline void SetRoadOwner(TileIndex t, RoadTramType rtt, Owner o) * @pre HasTileRoadType(t, rt) * @return True if the road type is owned by the given owner. */ -static inline bool IsRoadOwner(TileIndex t, RoadTramType rtt, Owner o) +static inline bool IsRoadOwner(Tile t, RoadTramType rtt, Owner o) { assert(HasTileRoadType(t, rtt)); return (GetRoadOwner(t, rtt) == o); @@ -277,7 +277,7 @@ static inline bool IsRoadOwner(TileIndex t, RoadTramType rtt, Owner o) * @pre IsTileType(t, MP_ROAD) * @return true iff tile has road and the road is owned by a town */ -static inline bool HasTownOwnedRoad(TileIndex t) +static inline bool HasTownOwnedRoad(Tile t) { return HasTileRoadType(t, RTT_ROAD) && IsRoadOwner(t, RTT_ROAD, OWNER_TOWN); } @@ -298,10 +298,10 @@ static inline bool IsValidDisallowedRoadDirections(DisallowedRoadDirections drt) * @param t the tile to get the directions from * @return the disallowed directions */ -static inline DisallowedRoadDirections GetDisallowedRoadDirections(TileIndex t) +static inline DisallowedRoadDirections GetDisallowedRoadDirections(Tile t) { assert(IsNormalRoad(t)); - return (DisallowedRoadDirections)GB(_m[t].m5, 4, 2); + return (DisallowedRoadDirections)GB(t.m5(), 4, 2); } /** @@ -309,11 +309,11 @@ static inline DisallowedRoadDirections GetDisallowedRoadDirections(TileIndex t) * @param t the tile to set the directions for * @param drd the disallowed directions */ -static inline void SetDisallowedRoadDirections(TileIndex t, DisallowedRoadDirections drd) +static inline void SetDisallowedRoadDirections(Tile t, DisallowedRoadDirections drd) { assert(IsNormalRoad(t)); assert(drd < DRD_END); - SB(_m[t].m5, 4, 2, drd); + SB(t.m5(), 4, 2, drd); } /** @@ -322,10 +322,10 @@ static inline void SetDisallowedRoadDirections(TileIndex t, DisallowedRoadDirect * @pre IsLevelCrossing(t) * @return The axis of the road. */ -static inline Axis GetCrossingRoadAxis(TileIndex t) +static inline Axis GetCrossingRoadAxis(Tile t) { assert(IsLevelCrossing(t)); - return (Axis)GB(_m[t].m5, 0, 1); + return (Axis)GB(t.m5(), 0, 1); } /** @@ -334,7 +334,7 @@ static inline Axis GetCrossingRoadAxis(TileIndex t) * @pre IsLevelCrossing(t) * @return The axis of the rail. */ -static inline Axis GetCrossingRailAxis(TileIndex t) +static inline Axis GetCrossingRailAxis(Tile t) { assert(IsLevelCrossing(t)); return OtherAxis((Axis)GetCrossingRoadAxis(t)); @@ -345,7 +345,7 @@ static inline Axis GetCrossingRailAxis(TileIndex t) * @param tile The tile to query. * @return The present road bits. */ -static inline RoadBits GetCrossingRoadBits(TileIndex tile) +static inline RoadBits GetCrossingRoadBits(Tile tile) { return GetCrossingRoadAxis(tile) == AXIS_X ? ROAD_X : ROAD_Y; } @@ -355,7 +355,7 @@ static inline RoadBits GetCrossingRoadBits(TileIndex tile) * @param tile The tile to query. * @return The rail track. */ -static inline Track GetCrossingRailTrack(TileIndex tile) +static inline Track GetCrossingRailTrack(Tile tile) { return AxisToTrack(GetCrossingRailAxis(tile)); } @@ -365,7 +365,7 @@ static inline Track GetCrossingRailTrack(TileIndex tile) * @param tile The tile to query. * @return The rail track bits. */ -static inline TrackBits GetCrossingRailBits(TileIndex tile) +static inline TrackBits GetCrossingRailBits(Tile tile) { return AxisToTrackBits(GetCrossingRailAxis(tile)); } @@ -377,10 +377,10 @@ static inline TrackBits GetCrossingRailBits(TileIndex tile) * @return reservation state * @pre IsLevelCrossingTile(t) */ -static inline bool HasCrossingReservation(TileIndex t) +static inline bool HasCrossingReservation(Tile t) { assert(IsLevelCrossingTile(t)); - return HasBit(_m[t].m5, 4); + return HasBit(t.m5(), 4); } /** @@ -390,10 +390,10 @@ static inline bool HasCrossingReservation(TileIndex t) * @param b the reservation state * @pre IsLevelCrossingTile(t) */ -static inline void SetCrossingReservation(TileIndex t, bool b) +static inline void SetCrossingReservation(Tile t, bool b) { assert(IsLevelCrossingTile(t)); - SB(_m[t].m5, 4, 1, b ? 1 : 0); + SB(t.m5(), 4, 1, b ? 1 : 0); } /** @@ -402,7 +402,7 @@ static inline void SetCrossingReservation(TileIndex t, bool b) * @pre IsLevelCrossingTile(t) * @return reserved track bits */ -static inline TrackBits GetCrossingReservationTrackBits(TileIndex t) +static inline TrackBits GetCrossingReservationTrackBits(Tile t) { return HasCrossingReservation(t) ? GetCrossingRailBits(t) : TRACK_BIT_NONE; } @@ -413,10 +413,10 @@ static inline TrackBits GetCrossingReservationTrackBits(TileIndex t) * @pre IsLevelCrossing(t) * @return True if the level crossing is barred. */ -static inline bool IsCrossingBarred(TileIndex t) +static inline bool IsCrossingBarred(Tile t) { assert(IsLevelCrossing(t)); - return HasBit(_m[t].m5, 5); + return HasBit(t.m5(), 5); } /** @@ -425,17 +425,17 @@ static inline bool IsCrossingBarred(TileIndex t) * @param barred True if the crossing should be barred, false otherwise. * @pre IsLevelCrossing(t) */ -static inline void SetCrossingBarred(TileIndex t, bool barred) +static inline void SetCrossingBarred(Tile t, bool barred) { assert(IsLevelCrossing(t)); - SB(_m[t].m5, 5, 1, barred ? 1 : 0); + SB(t.m5(), 5, 1, barred ? 1 : 0); } /** * Unbar a level crossing. * @param t The tile to change. */ -static inline void UnbarCrossing(TileIndex t) +static inline void UnbarCrossing(Tile t) { SetCrossingBarred(t, false); } @@ -444,7 +444,7 @@ static inline void UnbarCrossing(TileIndex t) * Bar a level crossing. * @param t The tile to change. */ -static inline void BarCrossing(TileIndex t) +static inline void BarCrossing(Tile t) { SetCrossingBarred(t, true); } @@ -456,9 +456,9 @@ static inline void BarCrossing(TileIndex t) * @param t The tile to query. * @return True if the tile has snow/desert. */ -static inline bool IsOnSnow(TileIndex t) +static inline bool IsOnSnow(Tile t) { - return HasBit(_me[t].m7, 5); + return HasBit(t.m7(), 5); } /** Toggle the snow/desert state of a road tile. */ @@ -467,9 +467,9 @@ static inline bool IsOnSnow(TileIndex t) * Toggle the snow/desert state of a road tile. * @param t The tile to change. */ -static inline void ToggleSnow(TileIndex t) +static inline void ToggleSnow(Tile t) { - ToggleBit(_me[t].m7, 5); + ToggleBit(t.m7(), 5); } @@ -490,9 +490,9 @@ enum Roadside { * @param tile The tile to query. * @return The road decoration of the tile. */ -static inline Roadside GetRoadside(TileIndex tile) +static inline Roadside GetRoadside(Tile tile) { - return (Roadside)GB(_me[tile].m6, 3, 3); + return (Roadside)GB(tile.m6(), 3, 3); } /** @@ -500,9 +500,9 @@ static inline Roadside GetRoadside(TileIndex tile) * @param tile The tile to change. * @param s The new road decoration of the tile. */ -static inline void SetRoadside(TileIndex tile, Roadside s) +static inline void SetRoadside(Tile tile, Roadside s) { - SB(_me[tile].m6, 3, 3, s); + SB(tile.m6(), 3, 3, s); } /** @@ -510,7 +510,7 @@ static inline void SetRoadside(TileIndex tile, Roadside s) * @param t The tile to check. * @return True if the tile has road works in progress. */ -static inline bool HasRoadWorks(TileIndex t) +static inline bool HasRoadWorks(Tile t) { return GetRoadside(t) >= ROADSIDE_GRASS_ROAD_WORKS; } @@ -520,11 +520,11 @@ static inline bool HasRoadWorks(TileIndex t) * @param t The tile to modify. * @return True if the road works are in the last stage. */ -static inline bool IncreaseRoadWorksCounter(TileIndex t) +static inline bool IncreaseRoadWorksCounter(Tile t) { - AB(_me[t].m7, 0, 4, 1); + AB(t.m7(), 0, 4, 1); - return GB(_me[t].m7, 0, 4) == 15; + return GB(t.m7(), 0, 4) == 15; } /** @@ -532,7 +532,7 @@ static inline bool IncreaseRoadWorksCounter(TileIndex t) * @param t The tile to start the work on. * @pre !HasRoadWorks(t) */ -static inline void StartRoadWorks(TileIndex t) +static inline void StartRoadWorks(Tile t) { assert(!HasRoadWorks(t)); /* Remove any trees or lamps in case or roadwork */ @@ -548,12 +548,12 @@ static inline void StartRoadWorks(TileIndex t) * @param t Tile to stop the road works on. * @pre HasRoadWorks(t) */ -static inline void TerminateRoadWorks(TileIndex t) +static inline void TerminateRoadWorks(Tile t) { assert(HasRoadWorks(t)); SetRoadside(t, (Roadside)(GetRoadside(t) - ROADSIDE_GRASS_ROAD_WORKS + ROADSIDE_GRASS)); /* Stop the counter */ - SB(_me[t].m7, 0, 4, 0); + SB(t.m7(), 0, 4, 0); } @@ -562,25 +562,25 @@ static inline void TerminateRoadWorks(TileIndex t) * @param t The tile to query. * @return Diagonal direction of the depot exit. */ -static inline DiagDirection GetRoadDepotDirection(TileIndex t) +static inline DiagDirection GetRoadDepotDirection(Tile t) { assert(IsRoadDepot(t)); - return (DiagDirection)GB(_m[t].m5, 0, 2); + return (DiagDirection)GB(t.m5(), 0, 2); } -RoadBits GetAnyRoadBits(TileIndex tile, RoadTramType rtt, bool straight_tunnel_bridge_entrance = false); +RoadBits GetAnyRoadBits(Tile tile, RoadTramType rtt, bool straight_tunnel_bridge_entrance = false); /** * Set the road road type of a tile. * @param t The tile to change. * @param rt The road type to set. */ -static inline void SetRoadTypeRoad(TileIndex t, RoadType rt) +static inline void SetRoadTypeRoad(Tile t, RoadType rt) { assert(MayHaveRoad(t)); assert(rt == INVALID_ROADTYPE || RoadTypeIsRoad(rt)); - SB(_m[t].m4, 0, 6, rt); + SB(t.m4(), 0, 6, rt); } /** @@ -588,11 +588,11 @@ static inline void SetRoadTypeRoad(TileIndex t, RoadType rt) * @param t The tile to change. * @param rt The road type to set. */ -static inline void SetRoadTypeTram(TileIndex t, RoadType rt) +static inline void SetRoadTypeTram(Tile t, RoadType rt) { assert(MayHaveRoad(t)); assert(rt == INVALID_ROADTYPE || RoadTypeIsTram(rt)); - SB(_me[t].m8, 6, 6, rt); + SB(t.m8(), 6, 6, rt); } /** @@ -601,7 +601,7 @@ static inline void SetRoadTypeTram(TileIndex t, RoadType rt) * @param rtt Set road or tram type. * @param rt The road type to set. */ -static inline void SetRoadType(TileIndex t, RoadTramType rtt, RoadType rt) +static inline void SetRoadType(Tile t, RoadTramType rtt, RoadType rt) { if (rtt == RTT_TRAM) { SetRoadTypeTram(t, rt); @@ -616,7 +616,7 @@ static inline void SetRoadType(TileIndex t, RoadTramType rtt, RoadType rt) * @param road_rt The road roadtype to set for the tile. * @param tram_rt The tram roadtype to set for the tile. */ -static inline void SetRoadTypes(TileIndex t, RoadType road_rt, RoadType tram_rt) +static inline void SetRoadTypes(Tile t, RoadType road_rt, RoadType tram_rt) { SetRoadTypeRoad(t, road_rt); SetRoadTypeTram(t, tram_rt); @@ -632,15 +632,15 @@ static inline void SetRoadTypes(TileIndex t, RoadType road_rt, RoadType tram_rt) * @param road New owner of road. * @param tram New owner of tram tracks. */ -static inline void MakeRoadNormal(TileIndex t, RoadBits bits, RoadType road_rt, RoadType tram_rt, TownID town, Owner road, Owner tram) +static inline void MakeRoadNormal(Tile t, RoadBits bits, RoadType road_rt, RoadType tram_rt, TownID town, Owner road, Owner tram) { SetTileType(t, MP_ROAD); SetTileOwner(t, road); - _m[t].m2 = town; - _m[t].m3 = (tram_rt != INVALID_ROADTYPE ? bits : 0); - _m[t].m5 = (road_rt != INVALID_ROADTYPE ? bits : 0) | ROAD_TILE_NORMAL << 6; - SB(_me[t].m6, 2, 4, 0); - _me[t].m7 = 0; + t.m2() = town; + t.m3() = (tram_rt != INVALID_ROADTYPE ? bits : 0); + t.m5() = (road_rt != INVALID_ROADTYPE ? bits : 0) | ROAD_TILE_NORMAL << 6; + SB(t.m6(), 2, 4, 0); + t.m7() = 0; SetRoadTypes(t, road_rt, tram_rt); SetRoadOwner(t, RTT_TRAM, tram); } @@ -657,17 +657,17 @@ static inline void MakeRoadNormal(TileIndex t, RoadBits bits, RoadType road_rt, * @param tram_rt The tram roadtype to set for the tile. * @param town Town ID if the road is a town-owned road. */ -static inline void MakeRoadCrossing(TileIndex t, Owner road, Owner tram, Owner rail, Axis roaddir, RailType rat, RoadType road_rt, RoadType tram_rt, uint town) +static inline void MakeRoadCrossing(Tile t, Owner road, Owner tram, Owner rail, Axis roaddir, RailType rat, RoadType road_rt, RoadType tram_rt, uint town) { SetTileType(t, MP_ROAD); SetTileOwner(t, rail); - _m[t].m2 = town; - _m[t].m3 = 0; - _m[t].m4 = INVALID_ROADTYPE; - _m[t].m5 = ROAD_TILE_CROSSING << 6 | roaddir; - SB(_me[t].m6, 2, 4, 0); - _me[t].m7 = road; - _me[t].m8 = INVALID_ROADTYPE << 6 | rat; + t.m2() = town; + t.m3() = 0; + t.m4() = INVALID_ROADTYPE; + t.m5() = ROAD_TILE_CROSSING << 6 | roaddir; + SB(t.m6(), 2, 4, 0); + t.m7() = road; + t.m8() = INVALID_ROADTYPE << 6 | rat; SetRoadTypes(t, road_rt, tram_rt); SetRoadOwner(t, RTT_TRAM, tram); } @@ -680,17 +680,17 @@ static inline void MakeRoadCrossing(TileIndex t, Owner road, Owner tram, Owner r * @param dir Direction of the depot exit.* * @param rt Road type of the depot. */ -static inline void MakeRoadDepot(TileIndex t, Owner owner, DepotID did, DiagDirection dir, RoadType rt) +static inline void MakeRoadDepot(Tile t, Owner owner, DepotID did, DiagDirection dir, RoadType rt) { SetTileType(t, MP_ROAD); SetTileOwner(t, owner); - _m[t].m2 = did; - _m[t].m3 = 0; - _m[t].m4 = INVALID_ROADTYPE; - _m[t].m5 = ROAD_TILE_DEPOT << 6 | dir; - SB(_me[t].m6, 2, 4, 0); - _me[t].m7 = owner; - _me[t].m8 = INVALID_ROADTYPE << 6; + t.m2() = did; + t.m3() = 0; + t.m4() = INVALID_ROADTYPE; + t.m5() = ROAD_TILE_DEPOT << 6 | dir; + SB(t.m6(), 2, 4, 0); + t.m7() = owner; + t.m8() = INVALID_ROADTYPE << 6; SetRoadType(t, GetRoadTramType(rt), rt); SetRoadOwner(t, RTT_TRAM, owner); } diff --git a/src/saveload/afterload.cpp b/src/saveload/afterload.cpp index 28dad77a29..6ebf4ccff1 100644 --- a/src/saveload/afterload.cpp +++ b/src/saveload/afterload.cpp @@ -77,7 +77,7 @@ extern Company *DoStartupNewCompany(bool is_ai, CompanyID company = INVALID_COMP * @param t the tile to change. * @param include_invalid_water_class Also consider WATER_CLASS_INVALID, i.e. industry tiles on land */ -void SetWaterClassDependingOnSurroundings(TileIndex t, bool include_invalid_water_class) +void SetWaterClassDependingOnSurroundings(Tile t, bool include_invalid_water_class) { /* If the slope is not flat, we always assume 'land' (if allowed). Also for one-corner-raised-shores. * Note: Wrt. autosloping under industry tiles this is the most fool-proof behaviour. */ @@ -104,7 +104,7 @@ void SetWaterClassDependingOnSurroundings(TileIndex t, bool include_invalid_wate bool has_river = false; for (DiagDirection dir = DIAGDIR_BEGIN; dir < DIAGDIR_END; dir++) { - TileIndex neighbour = TileAddByDiagDir(t, dir); + Tile neighbour = TileAddByDiagDir(t, dir); switch (GetTileType(neighbour)) { case MP_WATER: /* clear water and shipdepots have already a WaterClass associated */ @@ -127,7 +127,7 @@ void SetWaterClassDependingOnSurroundings(TileIndex t, bool include_invalid_wate case MP_TREES: /* trees on shore */ - has_water |= (GB(_m[neighbour].m2, 4, 2) == TREE_GROUND_SHORE); + has_water |= (GB(neighbour.m2(), 4, 2) == TREE_GROUND_SHORE); break; default: break; @@ -153,13 +153,13 @@ static void ConvertTownOwner() for (auto tile : Map::Iterate()) { switch (GetTileType(tile)) { case MP_ROAD: - if (GB(_m[tile].m5, 4, 2) == ROAD_TILE_CROSSING && HasBit(_m[tile].m3, 7)) { - _m[tile].m3 = OWNER_TOWN; + if (GB(tile.m5(), 4, 2) == ROAD_TILE_CROSSING && HasBit(tile.m3(), 7)) { + tile.m3() = OWNER_TOWN; } FALLTHROUGH; case MP_TUNNELBRIDGE: - if (_m[tile].m1 & 0x80) SetTileOwner(tile, OWNER_TOWN); + if (tile.m1() & 0x80) SetTileOwner(tile, OWNER_TOWN); break; default: break; @@ -439,14 +439,14 @@ static void CDECL HandleSavegameLoadCrash(int signum) * a rail track had an invalid owner. When conversion isn't possible, track is removed. * @param t tile to update */ -static void FixOwnerOfRailTrack(TileIndex t) +static void FixOwnerOfRailTrack(Tile t) { assert(!Company::IsValidID(GetTileOwner(t)) && (IsLevelCrossingTile(t) || IsPlainRailTile(t))); /* remove leftover rail piece from crossing (from very old savegames) */ Train *v = nullptr; for (Train *w : Train::Iterate()) { - if (w->tile == t) { + if (w->tile == TileIndex(t)) { v = w; break; } @@ -474,15 +474,15 @@ static void FixOwnerOfRailTrack(TileIndex t) Owner road = GetRoadOwner(t, RTT_ROAD); Owner tram = GetRoadOwner(t, RTT_TRAM); RoadBits bits = GetCrossingRoadBits(t); - bool hasroad = HasBit(_me[t].m7, 6); - bool hastram = HasBit(_me[t].m7, 7); + bool hasroad = HasBit(t.m7(), 6); + bool hastram = HasBit(t.m7(), 7); /* MakeRoadNormal */ SetTileType(t, MP_ROAD); SetTileOwner(t, road); - _m[t].m3 = (hasroad ? bits : 0); - _m[t].m5 = (hastram ? bits : 0) | ROAD_TILE_NORMAL << 6; - SB(_me[t].m6, 2, 4, 0); + t.m3() = (hasroad ? bits : 0); + t.m5() = (hastram ? bits : 0) | ROAD_TILE_NORMAL << 6; + SB(t.m6(), 2, 4, 0); SetRoadOwner(t, RTT_TRAM, tram); return; } @@ -533,7 +533,7 @@ static uint FixVehicleInclination(Vehicle *v, Direction dir) * @param t The tile to analyze * @return True if a bridge might have been present prior to savegame 194. */ -static inline bool MayHaveBridgeAbove(TileIndex t) +static inline bool MayHaveBridgeAbove(Tile t) { return IsTileType(t, MP_CLEAR) || IsTileType(t, MP_RAILWAY) || IsTileType(t, MP_ROAD) || IsTileType(t, MP_WATER) || IsTileType(t, MP_TUNNELBRIDGE) || IsTileType(t, MP_OBJECT); @@ -614,8 +614,8 @@ bool AfterLoadGame() } for (auto t : Map::Iterate()) { if (!IsTileType(t, MP_STATION)) continue; - if (_m[t].m5 > 7) continue; // is it a rail station tile? - Station *st = Station::Get(_m[t].m2); + if (t.m5() > 7) continue; // is it a rail station tile? + Station *st = Station::Get(t.m2()); assert(st->train_station.tile != 0); int dx = TileX(t) - TileX(st->train_station.tile); int dy = TileY(t) - TileY(st->train_station.tile); @@ -630,14 +630,14 @@ bool AfterLoadGame() /* In old savegame versions, the heightlevel was coded in bits 0..3 of the type field */ for (auto t : Map::Iterate()) { - _m[t].height = GB(_m[t].type, 0, 4); - SB(_m[t].type, 0, 2, GB(_me[t].m6, 0, 2)); - SB(_me[t].m6, 0, 2, 0); + t.height() = GB(t.type(), 0, 4); + SB(t.type(), 0, 2, GB(t.m6(), 0, 2)); + SB(t.m6(), 0, 2, 0); if (MayHaveBridgeAbove(t)) { - SB(_m[t].type, 2, 2, GB(_me[t].m6, 6, 2)); - SB(_me[t].m6, 6, 2, 0); + SB(t.type(), 2, 2, GB(t.m6(), 6, 2)); + SB(t.m6(), 6, 2, 0); } else { - SB(_m[t].type, 2, 2, 0); + SB(t.type(), 2, 2, 0); } } } @@ -847,7 +847,7 @@ bool AfterLoadGame() break; case MP_STATION: { - if (HasBit(_me[t].m6, 3)) SetBit(_me[t].m6, 2); + if (HasBit(t.m6(), 3)) SetBit(t.m6(), 2); StationGfx gfx = GetStationGfx(t); StationType st; if ( IsInsideMM(gfx, 0, 8)) { // Rail station @@ -885,7 +885,7 @@ bool AfterLoadGame() ResetSignalHandlers(); return false; } - SB(_me[t].m6, 3, 3, st); + SB(t.m6(), 3, 3, st); break; } } @@ -965,13 +965,13 @@ bool AfterLoadGame() for (auto t : Map::Iterate()) { switch (GetTileType(t)) { case MP_HOUSE: - _m[t].m4 = _m[t].m2; + t.m4() = t.m2(); SetTownIndex(t, CalcClosestTownFromTile(t)->index); break; case MP_ROAD: - _m[t].m4 |= (_m[t].m2 << 4); - if ((GB(_m[t].m5, 4, 2) == ROAD_TILE_CROSSING ? (Owner)_m[t].m3 : GetTileOwner(t)) == OWNER_TOWN) { + t.m4() |= (t.m2() << 4); + if ((GB(t.m5(), 4, 2) == ROAD_TILE_CROSSING ? (Owner)t.m3() : GetTileOwner(t)) == OWNER_TOWN) { SetTownIndex(t, CalcClosestTownFromTile(t)->index); } else { SetTownIndex(t, 0); @@ -1023,20 +1023,20 @@ bool AfterLoadGame() if (IsPlainRail(t)) { /* Swap ground type and signal type for plain rail tiles, so the * ground type uses the same bits as for depots and waypoints. */ - uint tmp = GB(_m[t].m4, 0, 4); - SB(_m[t].m4, 0, 4, GB(_m[t].m2, 0, 4)); - SB(_m[t].m2, 0, 4, tmp); - } else if (HasBit(_m[t].m5, 2)) { + uint tmp = GB(t.m4(), 0, 4); + SB(t.m4(), 0, 4, GB(t.m2(), 0, 4)); + SB(t.m2(), 0, 4, tmp); + } else if (HasBit(t.m5(), 2)) { /* Split waypoint and depot rail type and remove the subtype. */ - ClrBit(_m[t].m5, 2); - ClrBit(_m[t].m5, 6); + ClrBit(t.m5(), 2); + ClrBit(t.m5(), 6); } break; case MP_ROAD: /* Swap m3 and m4, so the track type for rail crossings is the * same as for normal rail. */ - Swap(_m[t].m3, _m[t].m4); + Swap(t.m3(), t.m4()); break; default: break; @@ -1050,31 +1050,31 @@ bool AfterLoadGame() for (auto t : Map::Iterate()) { switch (GetTileType(t)) { case MP_ROAD: - SB(_m[t].m5, 6, 2, GB(_m[t].m5, 4, 2)); + SB(t.m5(), 6, 2, GB(t.m5(), 4, 2)); switch (GetRoadTileType(t)) { default: SlErrorCorrupt("Invalid road tile type"); case ROAD_TILE_NORMAL: - SB(_m[t].m4, 0, 4, GB(_m[t].m5, 0, 4)); - SB(_m[t].m4, 4, 4, 0); - SB(_me[t].m6, 2, 4, 0); + SB(t.m4(), 0, 4, GB(t.m5(), 0, 4)); + SB(t.m4(), 4, 4, 0); + SB(t.m6(), 2, 4, 0); break; case ROAD_TILE_CROSSING: - SB(_m[t].m4, 5, 2, GB(_m[t].m5, 2, 2)); + SB(t.m4(), 5, 2, GB(t.m5(), 2, 2)); break; case ROAD_TILE_DEPOT: break; } - SB(_me[t].m7, 6, 2, 1); // Set pre-NRT road type bits for conversion later. + SB(t.m7(), 6, 2, 1); // Set pre-NRT road type bits for conversion later. break; case MP_STATION: - if (IsRoadStop(t)) SB(_me[t].m7, 6, 2, 1); + if (IsRoadStop(t)) SB(t.m7(), 6, 2, 1); break; case MP_TUNNELBRIDGE: /* Middle part of "old" bridges */ - if (old_bridge && IsBridge(t) && HasBit(_m[t].m5, 6)) break; - if (((old_bridge && IsBridge(t)) ? (TransportType)GB(_m[t].m5, 1, 2) : GetTunnelBridgeTransportType(t)) == TRANSPORT_ROAD) { - SB(_me[t].m7, 6, 2, 1); // Set pre-NRT road type bits for conversion later. + if (old_bridge && IsBridge(t) && HasBit(t.m5(), 6)) break; + if (((old_bridge && IsBridge(t)) ? (TransportType)GB(t.m5(), 1, 2) : GetTunnelBridgeTransportType(t)) == TRANSPORT_ROAD) { + SB(t.m7(), 6, 2, 1); // Set pre-NRT road type bits for conversion later. } break; @@ -1090,24 +1090,24 @@ bool AfterLoadGame() for (auto t : Map::Iterate()) { switch (GetTileType(t)) { case MP_ROAD: - if (fix_roadtypes) SB(_me[t].m7, 6, 2, (RoadTypes)GB(_me[t].m7, 5, 3)); - SB(_me[t].m7, 5, 1, GB(_m[t].m3, 7, 1)); // snow/desert + if (fix_roadtypes) SB(t.m7(), 6, 2, (RoadTypes)GB(t.m7(), 5, 3)); + SB(t.m7(), 5, 1, GB(t.m3(), 7, 1)); // snow/desert switch (GetRoadTileType(t)) { default: SlErrorCorrupt("Invalid road tile type"); case ROAD_TILE_NORMAL: - SB(_me[t].m7, 0, 4, GB(_m[t].m3, 0, 4)); // road works - SB(_me[t].m6, 3, 3, GB(_m[t].m3, 4, 3)); // ground - SB(_m[t].m3, 0, 4, GB(_m[t].m4, 4, 4)); // tram bits - SB(_m[t].m3, 4, 4, GB(_m[t].m5, 0, 4)); // tram owner - SB(_m[t].m5, 0, 4, GB(_m[t].m4, 0, 4)); // road bits + SB(t.m7(), 0, 4, GB(t.m3(), 0, 4)); // road works + SB(t.m6(), 3, 3, GB(t.m3(), 4, 3)); // ground + SB(t.m3(), 0, 4, GB(t.m4(), 4, 4)); // tram bits + SB(t.m3(), 4, 4, GB(t.m5(), 0, 4)); // tram owner + SB(t.m5(), 0, 4, GB(t.m4(), 0, 4)); // road bits break; case ROAD_TILE_CROSSING: - SB(_me[t].m7, 0, 5, GB(_m[t].m4, 0, 5)); // road owner - SB(_me[t].m6, 3, 3, GB(_m[t].m3, 4, 3)); // ground - SB(_m[t].m3, 4, 4, GB(_m[t].m5, 0, 4)); // tram owner - SB(_m[t].m5, 0, 1, GB(_m[t].m4, 6, 1)); // road axis - SB(_m[t].m5, 5, 1, GB(_m[t].m4, 5, 1)); // crossing state + SB(t.m7(), 0, 5, GB(t.m4(), 0, 5)); // road owner + SB(t.m6(), 3, 3, GB(t.m3(), 4, 3)); // ground + SB(t.m3(), 4, 4, GB(t.m5(), 0, 4)); // tram owner + SB(t.m5(), 0, 1, GB(t.m4(), 6, 1)); // road axis + SB(t.m5(), 5, 1, GB(t.m4(), 5, 1)); // crossing state break; case ROAD_TILE_DEPOT: @@ -1117,32 +1117,32 @@ bool AfterLoadGame() const Town *town = CalcClosestTownFromTile(t); if (town != nullptr) SetTownIndex(t, town->index); } - _m[t].m4 = 0; + t.m4() = 0; break; case MP_STATION: if (!IsRoadStop(t)) break; - if (fix_roadtypes) SB(_me[t].m7, 6, 2, (RoadTypes)GB(_m[t].m3, 0, 3)); - SB(_me[t].m7, 0, 5, HasBit(_me[t].m6, 2) ? OWNER_TOWN : GetTileOwner(t)); - SB(_m[t].m3, 4, 4, _m[t].m1); - _m[t].m4 = 0; + if (fix_roadtypes) SB(t.m7(), 6, 2, (RoadTypes)GB(t.m3(), 0, 3)); + SB(t.m7(), 0, 5, HasBit(t.m6(), 2) ? OWNER_TOWN : GetTileOwner(t)); + SB(t.m3(), 4, 4, t.m1()); + t.m4() = 0; break; case MP_TUNNELBRIDGE: - if (old_bridge && IsBridge(t) && HasBit(_m[t].m5, 6)) break; - if (((old_bridge && IsBridge(t)) ? (TransportType)GB(_m[t].m5, 1, 2) : GetTunnelBridgeTransportType(t)) == TRANSPORT_ROAD) { - if (fix_roadtypes) SB(_me[t].m7, 6, 2, (RoadTypes)GB(_m[t].m3, 0, 3)); + if (old_bridge && IsBridge(t) && HasBit(t.m5(), 6)) break; + if (((old_bridge && IsBridge(t)) ? (TransportType)GB(t.m5(), 1, 2) : GetTunnelBridgeTransportType(t)) == TRANSPORT_ROAD) { + if (fix_roadtypes) SB(t.m7(), 6, 2, (RoadTypes)GB(t.m3(), 0, 3)); Owner o = GetTileOwner(t); - SB(_me[t].m7, 0, 5, o); // road owner - SB(_m[t].m3, 4, 4, o == OWNER_NONE ? OWNER_TOWN : o); // tram owner + SB(t.m7(), 0, 5, o); // road owner + SB(t.m3(), 4, 4, o == OWNER_NONE ? OWNER_TOWN : o); // tram owner } - SB(_me[t].m6, 2, 4, GB(_m[t].m2, 4, 4)); // bridge type - SB(_me[t].m7, 5, 1, GB(_m[t].m4, 7, 1)); // snow/desert + SB(t.m6(), 2, 4, GB(t.m2(), 4, 4)); // bridge type + SB(t.m7(), 5, 1, GB(t.m4(), 7, 1)); // snow/desert - _m[t].m2 = 0; - _m[t].m4 = 0; + t.m2() = 0; + t.m4() = 0; break; default: break; @@ -1155,24 +1155,24 @@ bool AfterLoadGame() for (auto t : Map::Iterate()) { switch (GetTileType(t)) { case MP_RAILWAY: - SetRailType(t, (RailType)GB(_m[t].m3, 0, 4)); + SetRailType(t, (RailType)GB(t.m3(), 0, 4)); break; case MP_ROAD: if (IsLevelCrossing(t)) { - SetRailType(t, (RailType)GB(_m[t].m3, 0, 4)); + SetRailType(t, (RailType)GB(t.m3(), 0, 4)); } break; case MP_STATION: if (HasStationRail(t)) { - SetRailType(t, (RailType)GB(_m[t].m3, 0, 4)); + SetRailType(t, (RailType)GB(t.m3(), 0, 4)); } break; case MP_TUNNELBRIDGE: if (GetTunnelBridgeTransportType(t) == TRANSPORT_RAIL) { - SetRailType(t, (RailType)GB(_m[t].m3, 0, 4)); + SetRailType(t, (RailType)GB(t.m3(), 0, 4)); } break; @@ -1186,11 +1186,11 @@ bool AfterLoadGame() for (auto t : Map::Iterate()) { if (MayHaveBridgeAbove(t)) ClearBridgeMiddle(t); if (IsBridgeTile(t)) { - if (HasBit(_m[t].m5, 6)) { // middle part - Axis axis = (Axis)GB(_m[t].m5, 0, 1); + if (HasBit(t.m5(), 6)) { // middle part + Axis axis = (Axis)GB(t.m5(), 0, 1); - if (HasBit(_m[t].m5, 5)) { // transport route under bridge? - if (GB(_m[t].m5, 3, 2) == TRANSPORT_RAIL) { + if (HasBit(t.m5(), 5)) { // transport route under bridge? + if (GB(t.m5(), 3, 2) == TRANSPORT_RAIL) { MakeRailNormal( t, GetTileOwner(t), @@ -1202,15 +1202,15 @@ bool AfterLoadGame() /* MakeRoadNormal */ SetTileType(t, MP_ROAD); - _m[t].m2 = town; - _m[t].m3 = 0; - _m[t].m5 = (axis == AXIS_X ? ROAD_Y : ROAD_X) | ROAD_TILE_NORMAL << 6; - SB(_me[t].m6, 2, 4, 0); - _me[t].m7 = 1 << 6; + t.m2() = town; + t.m3() = 0; + t.m5() = (axis == AXIS_X ? ROAD_Y : ROAD_X) | ROAD_TILE_NORMAL << 6; + SB(t.m6(), 2, 4, 0); + t.m7() = 1 << 6; SetRoadOwner(t, RTT_TRAM, OWNER_NONE); } } else { - if (GB(_m[t].m5, 3, 2) == 0) { + if (GB(t.m5(), 3, 2) == 0) { MakeClear(t, CLEAR_GRASS, 3); } else { if (!IsTileFlat(t)) { @@ -1226,12 +1226,12 @@ bool AfterLoadGame() } SetBridgeMiddle(t, axis); } else { // ramp - Axis axis = (Axis)GB(_m[t].m5, 0, 1); - uint north_south = GB(_m[t].m5, 5, 1); + Axis axis = (Axis)GB(t.m5(), 0, 1); + uint north_south = GB(t.m5(), 5, 1); DiagDirection dir = ReverseDiagDir(XYNSToDiagDir(axis, north_south)); - TransportType type = (TransportType)GB(_m[t].m5, 1, 2); + TransportType type = (TransportType)GB(t.m5(), 1, 2); - _m[t].m5 = 1 << 7 | type << 2 | dir; + t.m5() = 1 << 7 | type << 2 | dir; } } } @@ -1282,12 +1282,12 @@ bool AfterLoadGame() } if (has_road) { - RoadType road_rt = HasBit(_me[t].m7, 6) ? ROADTYPE_ROAD : INVALID_ROADTYPE; - RoadType tram_rt = HasBit(_me[t].m7, 7) ? ROADTYPE_TRAM : INVALID_ROADTYPE; + RoadType road_rt = HasBit(t.m7(), 6) ? ROADTYPE_ROAD : INVALID_ROADTYPE; + RoadType tram_rt = HasBit(t.m7(), 7) ? ROADTYPE_TRAM : INVALID_ROADTYPE; assert(road_rt != INVALID_ROADTYPE || tram_rt != INVALID_ROADTYPE); SetRoadTypes(t, road_rt, tram_rt); - SB(_me[t].m7, 6, 2, 0); // Clear pre-NRT road type bits. + SB(t.m7(), 6, 2, 0); // Clear pre-NRT road type bits. } } } @@ -1365,23 +1365,23 @@ bool AfterLoadGame() * (see the code somewhere above) so don't use m4, use m2 instead. */ /* convert PBS signals to combo-signals */ - if (HasBit(_m[t].m2, 2)) SB(_m[t].m2, 0, 2, SIGTYPE_COMBO); + if (HasBit(t.m2(), 2)) SB(t.m2(), 0, 2, SIGTYPE_COMBO); /* move the signal variant back */ - SB(_m[t].m2, 2, 1, HasBit(_m[t].m2, 3) ? SIG_SEMAPHORE : SIG_ELECTRIC); - ClrBit(_m[t].m2, 3); + SB(t.m2(), 2, 1, HasBit(t.m2(), 3) ? SIG_SEMAPHORE : SIG_ELECTRIC); + ClrBit(t.m2(), 3); } /* Clear PBS reservation on track */ if (!IsRailDepotTile(t)) { - SB(_m[t].m4, 4, 4, 0); + SB(t.m4(), 4, 4, 0); } else { - ClrBit(_m[t].m3, 6); + ClrBit(t.m3(), 6); } break; case MP_STATION: // Clear PBS reservation on station - ClrBit(_m[t].m3, 6); + ClrBit(t.m3(), 6); break; default: break; @@ -1475,31 +1475,31 @@ bool AfterLoadGame() if (IsSavegameVersionBefore(SLV_53)) { for (auto t : Map::Iterate()) { if (IsTileType(t, MP_HOUSE)) { - if (GB(_m[t].m3, 6, 2) != TOWN_HOUSE_COMPLETED) { + if (GB(t.m3(), 6, 2) != TOWN_HOUSE_COMPLETED) { /* Move the construction stage from m3[7..6] to m5[5..4]. * The construction counter does not have to move. */ - SB(_m[t].m5, 3, 2, GB(_m[t].m3, 6, 2)); - SB(_m[t].m3, 6, 2, 0); + SB(t.m5(), 3, 2, GB(t.m3(), 6, 2)); + SB(t.m3(), 6, 2, 0); /* The "house is completed" bit is now in m6[2]. */ SetHouseCompleted(t, false); } else { /* The "lift has destination" bit has been moved from * m5[7] to m7[0]. */ - SB(_me[t].m7, 0, 1, HasBit(_m[t].m5, 7)); - ClrBit(_m[t].m5, 7); + SB(t.m7(), 0, 1, HasBit(t.m5(), 7)); + ClrBit(t.m5(), 7); /* The "lift is moving" bit has been removed, as it does * the same job as the "lift has destination" bit. */ - ClrBit(_m[t].m1, 7); + ClrBit(t.m1(), 7); /* The position of the lift goes from m1[7..0] to m6[7..2], * making m1 totally free, now. The lift position does not * have to be a full byte since the maximum value is 36. */ - SetLiftPosition(t, GB(_m[t].m1, 0, 6 )); + SetLiftPosition(t, GB(t.m1(), 0, 6 )); - _m[t].m1 = 0; - _m[t].m3 = 0; + t.m1() = 0; + t.m3() = 0; SetHouseCompleted(t, true); } } @@ -1514,19 +1514,19 @@ bool AfterLoadGame() if (IsTileType(t, MP_INDUSTRY)) { switch (GetIndustryGfx(t)) { case GFX_POWERPLANT_SPARKS: - _m[t].m3 = GB(_m[t].m1, 2, 5); + t.m3() = GB(t.m1(), 2, 5); break; case GFX_OILWELL_ANIMATED_1: case GFX_OILWELL_ANIMATED_2: case GFX_OILWELL_ANIMATED_3: - _m[t].m3 = GB(_m[t].m1, 0, 2); + t.m3() = GB(t.m1(), 0, 2); break; case GFX_COAL_MINE_TOWER_ANIMATED: case GFX_COPPER_MINE_TOWER_ANIMATED: case GFX_GOLD_MINE_TOWER_ANIMATED: - _m[t].m3 = _m[t].m1; + t.m3() = t.m1(); break; default: // No animation states to change @@ -1571,8 +1571,8 @@ bool AfterLoadGame() if (IsSavegameVersionBefore(SLV_52)) { for (auto t : Map::Iterate()) { - if (IsTileType(t, MP_OBJECT) && _m[t].m5 == OBJECT_STATUE) { - _m[t].m2 = CalcClosestTownFromTile(t)->index; + if (IsTileType(t, MP_OBJECT) && t.m5() == OBJECT_STATUE) { + t.m2() = CalcClosestTownFromTile(t)->index; } } } @@ -1631,10 +1631,10 @@ bool AfterLoadGame() for (auto t : Map::Iterate()) { if (IsTileType(t, MP_RAILWAY) && HasSignals(t)) { /* move signal states */ - SetSignalStates(t, GB(_m[t].m2, 4, 4)); - SB(_m[t].m2, 4, 4, 0); + SetSignalStates(t, GB(t.m2(), 4, 4)); + SB(t.m2(), 4, 4, 0); /* clone signal type and variant */ - SB(_m[t].m2, 4, 3, GB(_m[t].m2, 0, 3)); + SB(t.m2(), 4, 3, GB(t.m2(), 0, 3)); } } } @@ -1675,7 +1675,7 @@ bool AfterLoadGame() if (IsSavegameVersionBefore(SLV_83)) { for (auto t : Map::Iterate()) { if (IsShipDepotTile(t)) { - _m[t].m4 = (TileHeight(t) == 0) ? OWNER_WATER : OWNER_NONE; + t.m4() = (TileHeight(t) == 0) ? OWNER_WATER : OWNER_NONE; } } } @@ -1709,8 +1709,8 @@ bool AfterLoadGame() if (IsSavegameVersionBefore(SLV_81)) { for (auto t : Map::Iterate()) { if (GetTileType(t) == MP_TREES) { - TreeGround groundType = (TreeGround)GB(_m[t].m2, 4, 2); - if (groundType != TREE_GROUND_SNOW_DESERT) SB(_m[t].m2, 6, 2, 3); + TreeGround groundType = (TreeGround)GB(t.m2(), 4, 2); + if (groundType != TREE_GROUND_SNOW_DESERT) SB(t.m2(), 6, 2, 3); } } } @@ -1772,8 +1772,8 @@ bool AfterLoadGame() case STATION_OILRIG: case STATION_DOCK: case STATION_BUOY: - SetWaterClass(t, (WaterClass)GB(_m[t].m3, 0, 2)); - SB(_m[t].m3, 0, 2, 0); + SetWaterClass(t, (WaterClass)GB(t.m3(), 0, 2)); + SB(t.m3(), 0, 2, 0); break; default: @@ -1783,8 +1783,8 @@ bool AfterLoadGame() break; case MP_WATER: - SetWaterClass(t, (WaterClass)GB(_m[t].m3, 0, 2)); - SB(_m[t].m3, 0, 2, 0); + SetWaterClass(t, (WaterClass)GB(t.m3(), 0, 2)); + SB(t.m3(), 0, 2, 0); break; case MP_OBJECT: @@ -1811,7 +1811,7 @@ bool AfterLoadGame() MakeCanal(t, o, Random()); } } else if (IsShipDepot(t)) { - Owner o = (Owner)_m[t].m4; // Original water owner + Owner o = (Owner)t.m4(); // Original water owner SetWaterClass(t, o == OWNER_WATER ? WATER_CLASS_SEA : WATER_CLASS_CANAL); } } @@ -1899,8 +1899,8 @@ bool AfterLoadGame() /* Increase HouseAnimationFrame from 5 to 7 bits */ for (auto t : Map::Iterate()) { if (IsTileType(t, MP_HOUSE) && GetHouseType(t) >= NEW_HOUSE_OFFSET) { - SB(_me[t].m6, 2, 6, GB(_me[t].m6, 3, 5)); - SB(_m[t].m3, 5, 1, 0); + SB(t.m6(), 2, 6, GB(t.m6(), 3, 5)); + SB(t.m3(), 5, 1, 0); } } } @@ -1933,7 +1933,7 @@ bool AfterLoadGame() /* Replace "house construction year" with "house age" */ if (IsTileType(t, MP_HOUSE) && IsHouseCompleted(t)) { - _m[t].m5 = Clamp(_cur_year - (_m[t].m5 + ORIGINAL_BASE_YEAR), 0, 0xFF); + t.m5() = Clamp(_cur_year - (t.m5() + ORIGINAL_BASE_YEAR), 0, 0xFF); } } } @@ -1947,10 +1947,10 @@ bool AfterLoadGame() case MP_RAILWAY: if (HasSignals(t)) { /* move the signal variant */ - SetSignalVariant(t, TRACK_UPPER, HasBit(_m[t].m2, 2) ? SIG_SEMAPHORE : SIG_ELECTRIC); - SetSignalVariant(t, TRACK_LOWER, HasBit(_m[t].m2, 6) ? SIG_SEMAPHORE : SIG_ELECTRIC); - ClrBit(_m[t].m2, 2); - ClrBit(_m[t].m2, 6); + SetSignalVariant(t, TRACK_UPPER, HasBit(t.m2(), 2) ? SIG_SEMAPHORE : SIG_ELECTRIC); + SetSignalVariant(t, TRACK_LOWER, HasBit(t.m2(), 6) ? SIG_SEMAPHORE : SIG_ELECTRIC); + ClrBit(t.m2(), 2); + ClrBit(t.m2(), 6); } /* Clear PBS reservation on track */ @@ -2028,11 +2028,11 @@ bool AfterLoadGame() for (auto t : Map::Iterate()) { /* Check for HQ bit being set, instead of using map accessor, * since we've already changed it code-wise */ - if (IsTileType(t, MP_OBJECT) && HasBit(_m[t].m5, 7)) { + if (IsTileType(t, MP_OBJECT) && HasBit(t.m5(), 7)) { /* Move size and part identification of HQ out of the m5 attribute, * on new locations */ - _m[t].m3 = GB(_m[t].m5, 0, 5); - _m[t].m5 = OBJECT_HQ; + t.m3() = GB(t.m5(), 0, 5); + t.m5() = OBJECT_HQ; } } } @@ -2041,13 +2041,13 @@ bool AfterLoadGame() if (!IsTileType(t, MP_OBJECT)) continue; /* Reordering/generalisation of the object bits. */ - ObjectType type = _m[t].m5; - SB(_me[t].m6, 2, 4, type == OBJECT_HQ ? GB(_m[t].m3, 2, 3) : 0); - _m[t].m3 = type == OBJECT_HQ ? GB(_m[t].m3, 1, 1) | GB(_m[t].m3, 0, 1) << 4 : 0; + ObjectType type = t.m5(); + SB(t.m6(), 2, 4, type == OBJECT_HQ ? GB(t.m3(), 2, 3) : 0); + t.m3() = type == OBJECT_HQ ? GB(t.m3(), 1, 1) | GB(t.m3(), 0, 1) << 4 : 0; /* Make sure those bits are clear as well! */ - _m[t].m4 = 0; - _me[t].m7 = 0; + t.m4() = 0; + t.m7() = 0; } } @@ -2060,15 +2060,15 @@ bool AfterLoadGame() /* No towns, so remove all objects! */ DoClearSquare(t); } else { - uint offset = _m[t].m3; + uint offset = t.m3(); /* Also move the animation state. */ - _m[t].m3 = GB(_me[t].m6, 2, 4); - SB(_me[t].m6, 2, 4, 0); + t.m3() = GB(t.m6(), 2, 4); + SB(t.m6(), 2, 4, 0); if (offset == 0) { /* No offset, so make the object. */ - ObjectType type = _m[t].m5; + ObjectType type = t.m5(); int size = type == OBJECT_HQ ? 2 : 1; if (!Object::CanAllocateItem()) { @@ -2078,18 +2078,18 @@ bool AfterLoadGame() } Object *o = new Object(); - o->location.tile = t; + o->location.tile = (TileIndex)t; o->location.w = size; o->location.h = size; o->build_date = _date; - o->town = type == OBJECT_STATUE ? Town::Get(_m[t].m2) : CalcClosestTownFromTile(t, UINT_MAX); - _m[t].m2 = o->index; + o->town = type == OBJECT_STATUE ? Town::Get(t.m2()) : CalcClosestTownFromTile(t, UINT_MAX); + t.m2() = o->index; Object::IncTypeCount(type); } else { /* We're at an offset, so get the ID from our "root". */ - TileIndex northern_tile = t - TileXY(GB(offset, 0, 4), GB(offset, 4, 4)); + Tile northern_tile = t - TileXY(GB(offset, 0, 4), GB(offset, 4, 4)); assert(IsTileType(northern_tile, MP_OBJECT)); - _m[t].m2 = _m[northern_tile].m2; + t.m2() = northern_tile.m2(); } } } @@ -2294,16 +2294,17 @@ bool AfterLoadGame() if (IsSavegameVersionBefore(SLV_128)) { for (const Depot *d : Depot::Iterate()) { + Tile tile = d->xy; /* At some point, invalid depots were saved into the game (possibly those removed in the past?) * Remove them here, so they don't cause issues further down the line */ - if (!IsDepotTile(d->xy)) { + if (!IsDepotTile(tile)) { Debug(sl, 0, "Removing invalid depot {} at {}, {}", d->index, TileX(d->xy), TileY(d->xy)); delete d; d = nullptr; continue; } - _m[d->xy].m2 = d->index; - if (IsTileType(d->xy, MP_WATER)) _m[GetOtherShipDepotTile(d->xy)].m2 = d->index; + tile.m2() = d->index; + if (IsTileType(tile, MP_WATER)) Tile(GetOtherShipDepotTile(tile)).m2() = d->index; } } @@ -2324,15 +2325,15 @@ bool AfterLoadGame() if (IsTileType(t, MP_CLEAR)) { if (GetRawClearGround(t) == CLEAR_SNOW) { SetClearGroundDensity(t, CLEAR_GRASS, GetClearDensity(t)); - SetBit(_m[t].m3, 4); + SetBit(t.m3(), 4); } else { - ClrBit(_m[t].m3, 4); + ClrBit(t.m3(), 4); } } if (IsTileType(t, MP_TREES)) { - uint density = GB(_m[t].m2, 6, 2); - uint ground = GB(_m[t].m2, 4, 2); - _m[t].m2 = ground << 6 | density << 4; + uint density = GB(t.m2(), 6, 2); + uint ground = GB(t.m2(), 4, 2); + t.m2() = ground << 6 | density << 4; } } } @@ -2440,23 +2441,23 @@ bool AfterLoadGame() switch (GetTileType(t)) { case MP_HOUSE: if (GetHouseType(t) >= NEW_HOUSE_OFFSET) { - uint per_proc = _me[t].m7; - _me[t].m7 = GB(_me[t].m6, 2, 6) | (GB(_m[t].m3, 5, 1) << 6); - SB(_m[t].m3, 5, 1, 0); - SB(_me[t].m6, 2, 6, std::min(per_proc, 63U)); + uint per_proc = t.m7(); + t.m7() = GB(t.m6(), 2, 6) | (GB(t.m3(), 5, 1) << 6); + SB(t.m3(), 5, 1, 0); + SB(t.m6(), 2, 6, std::min(per_proc, 63U)); } break; case MP_INDUSTRY: { - uint rand = _me[t].m7; - _me[t].m7 = _m[t].m3; - _m[t].m3 = rand; + uint rand = t.m7(); + t.m7() = t.m3(); + t.m3() = rand; break; } case MP_OBJECT: - _me[t].m7 = _m[t].m3; - _m[t].m3 = 0; + t.m7() = t.m3(); + t.m3() = 0; break; default: @@ -2771,16 +2772,16 @@ bool AfterLoadGame() for (auto t : Map::Iterate()) { if (!IsTileType(t, MP_CLEAR) && !IsTileType(t, MP_TREES)) continue; if (IsTileType(t, MP_CLEAR) && IsClearGround(t, CLEAR_FIELDS)) continue; - uint fence = GB(_m[t].m4, 5, 3); + uint fence = GB(t.m4(), 5, 3); if (fence != 0 && IsTileType(TILE_ADDXY(t, 1, 0), MP_CLEAR) && IsClearGround(TILE_ADDXY(t, 1, 0), CLEAR_FIELDS)) { SetFence(TILE_ADDXY(t, 1, 0), DIAGDIR_NE, fence); } - fence = GB(_m[t].m4, 2, 3); + fence = GB(t.m4(), 2, 3); if (fence != 0 && IsTileType(TILE_ADDXY(t, 0, 1), MP_CLEAR) && IsClearGround(TILE_ADDXY(t, 0, 1), CLEAR_FIELDS)) { SetFence(TILE_ADDXY(t, 0, 1), DIAGDIR_NW, fence); } - SB(_m[t].m4, 2, 3, 0); - SB(_m[t].m4, 5, 3, 0); + SB(t.m4(), 2, 3, 0); + SB(t.m4(), 5, 3, 0); } } @@ -2880,9 +2881,9 @@ bool AfterLoadGame() /* Move ObjectType from map to pool */ for (auto t : Map::Iterate()) { if (IsTileType(t, MP_OBJECT)) { - Object *o = Object::Get(_m[t].m2); - o->type = _m[t].m5; - _m[t].m5 = 0; // zero upper bits of (now bigger) ObjectID + Object *o = Object::Get(t.m2()); + o->type = t.m5(); + t.m5() = 0; // zero upper bits of (now bigger) ObjectID } } } @@ -3191,7 +3192,7 @@ bool AfterLoadGame() /* Reset unused tree counters to reduce the savegame size. */ for (auto t : Map::Iterate()) { if (IsTileType(t, MP_TREES)) { - SB(_m[t].m2, 0, 4, 0); + SB(t.m2(), 0, 4, 0); } } } diff --git a/src/saveload/map_sl.cpp b/src/saveload/map_sl.cpp index 363abfb9d5..2809ad9cfd 100644 --- a/src/saveload/map_sl.cpp +++ b/src/saveload/map_sl.cpp @@ -77,7 +77,7 @@ struct MAPTChunkHandler : ChunkHandler { for (TileIndex i = 0; i != size;) { SlCopy(buf.data(), MAP_SL_BUF_SIZE, SLE_UINT8); - for (uint j = 0; j != MAP_SL_BUF_SIZE; j++) _m[i++].type = buf[j]; + for (uint j = 0; j != MAP_SL_BUF_SIZE; j++) Tile(i++).type() = buf[j]; } } @@ -88,7 +88,7 @@ struct MAPTChunkHandler : ChunkHandler { SlSetLength(size); for (TileIndex i = 0; i != size;) { - for (uint j = 0; j != MAP_SL_BUF_SIZE; j++) buf[j] = _m[i++].type; + for (uint j = 0; j != MAP_SL_BUF_SIZE; j++) buf[j] = Tile(i++).type(); SlCopy(buf.data(), MAP_SL_BUF_SIZE, SLE_UINT8); } } @@ -104,7 +104,7 @@ struct MAPHChunkHandler : ChunkHandler { for (TileIndex i = 0; i != size;) { SlCopy(buf.data(), MAP_SL_BUF_SIZE, SLE_UINT8); - for (uint j = 0; j != MAP_SL_BUF_SIZE; j++) _m[i++].height = buf[j]; + for (uint j = 0; j != MAP_SL_BUF_SIZE; j++) Tile(i++).height() = buf[j]; } } @@ -115,7 +115,7 @@ struct MAPHChunkHandler : ChunkHandler { SlSetLength(size); for (TileIndex i = 0; i != size;) { - for (uint j = 0; j != MAP_SL_BUF_SIZE; j++) buf[j] = _m[i++].height; + for (uint j = 0; j != MAP_SL_BUF_SIZE; j++) buf[j] = Tile(i++).height(); SlCopy(buf.data(), MAP_SL_BUF_SIZE, SLE_UINT8); } } @@ -131,7 +131,7 @@ struct MAPOChunkHandler : ChunkHandler { for (TileIndex i = 0; i != size;) { SlCopy(buf.data(), MAP_SL_BUF_SIZE, SLE_UINT8); - for (uint j = 0; j != MAP_SL_BUF_SIZE; j++) _m[i++].m1 = buf[j]; + for (uint j = 0; j != MAP_SL_BUF_SIZE; j++) Tile(i++).m1() = buf[j]; } } @@ -142,7 +142,7 @@ struct MAPOChunkHandler : ChunkHandler { SlSetLength(size); for (TileIndex i = 0; i != size;) { - for (uint j = 0; j != MAP_SL_BUF_SIZE; j++) buf[j] = _m[i++].m1; + for (uint j = 0; j != MAP_SL_BUF_SIZE; j++) buf[j] = Tile(i++).m1(); SlCopy(buf.data(), MAP_SL_BUF_SIZE, SLE_UINT8); } } @@ -161,7 +161,7 @@ struct MAP2ChunkHandler : ChunkHandler { /* In those versions the m2 was 8 bits */ IsSavegameVersionBefore(SLV_5) ? SLE_FILE_U8 | SLE_VAR_U16 : SLE_UINT16 ); - for (uint j = 0; j != MAP_SL_BUF_SIZE; j++) _m[i++].m2 = buf[j]; + for (uint j = 0; j != MAP_SL_BUF_SIZE; j++) Tile(i++).m2() = buf[j]; } } @@ -172,7 +172,7 @@ struct MAP2ChunkHandler : ChunkHandler { SlSetLength(size * sizeof(uint16)); for (TileIndex i = 0; i != size;) { - for (uint j = 0; j != MAP_SL_BUF_SIZE; j++) buf[j] = _m[i++].m2; + for (uint j = 0; j != MAP_SL_BUF_SIZE; j++) buf[j] = Tile(i++).m2(); SlCopy(buf.data(), MAP_SL_BUF_SIZE, SLE_UINT16); } } @@ -188,7 +188,7 @@ struct M3LOChunkHandler : ChunkHandler { for (TileIndex i = 0; i != size;) { SlCopy(buf.data(), MAP_SL_BUF_SIZE, SLE_UINT8); - for (uint j = 0; j != MAP_SL_BUF_SIZE; j++) _m[i++].m3 = buf[j]; + for (uint j = 0; j != MAP_SL_BUF_SIZE; j++) Tile(i++).m3() = buf[j]; } } @@ -199,7 +199,7 @@ struct M3LOChunkHandler : ChunkHandler { SlSetLength(size); for (TileIndex i = 0; i != size;) { - for (uint j = 0; j != MAP_SL_BUF_SIZE; j++) buf[j] = _m[i++].m3; + for (uint j = 0; j != MAP_SL_BUF_SIZE; j++) buf[j] = Tile(i++).m3(); SlCopy(buf.data(), MAP_SL_BUF_SIZE, SLE_UINT8); } } @@ -215,7 +215,7 @@ struct M3HIChunkHandler : ChunkHandler { for (TileIndex i = 0; i != size;) { SlCopy(buf.data(), MAP_SL_BUF_SIZE, SLE_UINT8); - for (uint j = 0; j != MAP_SL_BUF_SIZE; j++) _m[i++].m4 = buf[j]; + for (uint j = 0; j != MAP_SL_BUF_SIZE; j++) Tile(i++).m4() = buf[j]; } } @@ -226,7 +226,7 @@ struct M3HIChunkHandler : ChunkHandler { SlSetLength(size); for (TileIndex i = 0; i != size;) { - for (uint j = 0; j != MAP_SL_BUF_SIZE; j++) buf[j] = _m[i++].m4; + for (uint j = 0; j != MAP_SL_BUF_SIZE; j++) buf[j] = Tile(i++).m4(); SlCopy(buf.data(), MAP_SL_BUF_SIZE, SLE_UINT8); } } @@ -242,7 +242,7 @@ struct MAP5ChunkHandler : ChunkHandler { for (TileIndex i = 0; i != size;) { SlCopy(buf.data(), MAP_SL_BUF_SIZE, SLE_UINT8); - for (uint j = 0; j != MAP_SL_BUF_SIZE; j++) _m[i++].m5 = buf[j]; + for (uint j = 0; j != MAP_SL_BUF_SIZE; j++) Tile(i++).m5() = buf[j]; } } @@ -253,7 +253,7 @@ struct MAP5ChunkHandler : ChunkHandler { SlSetLength(size); for (TileIndex i = 0; i != size;) { - for (uint j = 0; j != MAP_SL_BUF_SIZE; j++) buf[j] = _m[i++].m5; + for (uint j = 0; j != MAP_SL_BUF_SIZE; j++) buf[j] = Tile(i++).m5(); SlCopy(buf.data(), MAP_SL_BUF_SIZE, SLE_UINT8); } } @@ -272,16 +272,16 @@ struct MAPEChunkHandler : ChunkHandler { /* 1024, otherwise we overflow on 64x64 maps! */ SlCopy(buf.data(), 1024, SLE_UINT8); for (uint j = 0; j != 1024; j++) { - _me[i++].m6 = GB(buf[j], 0, 2); - _me[i++].m6 = GB(buf[j], 2, 2); - _me[i++].m6 = GB(buf[j], 4, 2); - _me[i++].m6 = GB(buf[j], 6, 2); + Tile(i++).m6() = GB(buf[j], 0, 2); + Tile(i++).m6() = GB(buf[j], 2, 2); + Tile(i++).m6() = GB(buf[j], 4, 2); + Tile(i++).m6() = GB(buf[j], 6, 2); } } } else { for (TileIndex i = 0; i != size;) { SlCopy(buf.data(), MAP_SL_BUF_SIZE, SLE_UINT8); - for (uint j = 0; j != MAP_SL_BUF_SIZE; j++) _me[i++].m6 = buf[j]; + for (uint j = 0; j != MAP_SL_BUF_SIZE; j++) Tile(i++).m6() = buf[j]; } } } @@ -293,7 +293,7 @@ struct MAPEChunkHandler : ChunkHandler { SlSetLength(size); for (TileIndex i = 0; i != size;) { - for (uint j = 0; j != MAP_SL_BUF_SIZE; j++) buf[j] = _me[i++].m6; + for (uint j = 0; j != MAP_SL_BUF_SIZE; j++) buf[j] = Tile(i++).m6(); SlCopy(buf.data(), MAP_SL_BUF_SIZE, SLE_UINT8); } } @@ -309,7 +309,7 @@ struct MAP7ChunkHandler : ChunkHandler { for (TileIndex i = 0; i != size;) { SlCopy(buf.data(), MAP_SL_BUF_SIZE, SLE_UINT8); - for (uint j = 0; j != MAP_SL_BUF_SIZE; j++) _me[i++].m7 = buf[j]; + for (uint j = 0; j != MAP_SL_BUF_SIZE; j++) Tile(i++).m7() = buf[j]; } } @@ -320,7 +320,7 @@ struct MAP7ChunkHandler : ChunkHandler { SlSetLength(size); for (TileIndex i = 0; i != size;) { - for (uint j = 0; j != MAP_SL_BUF_SIZE; j++) buf[j] = _me[i++].m7; + for (uint j = 0; j != MAP_SL_BUF_SIZE; j++) buf[j] = Tile(i++).m7(); SlCopy(buf.data(), MAP_SL_BUF_SIZE, SLE_UINT8); } } @@ -336,7 +336,7 @@ struct MAP8ChunkHandler : ChunkHandler { for (TileIndex i = 0; i != size;) { SlCopy(buf.data(), MAP_SL_BUF_SIZE, SLE_UINT16); - for (uint j = 0; j != MAP_SL_BUF_SIZE; j++) _me[i++].m8 = buf[j]; + for (uint j = 0; j != MAP_SL_BUF_SIZE; j++) Tile(i++).m8() = buf[j]; } } @@ -347,7 +347,7 @@ struct MAP8ChunkHandler : ChunkHandler { SlSetLength(size * sizeof(uint16)); for (TileIndex i = 0; i != size;) { - for (uint j = 0; j != MAP_SL_BUF_SIZE; j++) buf[j] = _me[i++].m8; + for (uint j = 0; j != MAP_SL_BUF_SIZE; j++) buf[j] = Tile(i++).m8(); SlCopy(buf.data(), MAP_SL_BUF_SIZE, SLE_UINT16); } } diff --git a/src/saveload/oldloader_sl.cpp b/src/saveload/oldloader_sl.cpp index f6b64ede7a..fa87681d8b 100644 --- a/src/saveload/oldloader_sl.cpp +++ b/src/saveload/oldloader_sl.cpp @@ -54,46 +54,48 @@ static void FixTTDMapArray() { /* _old_map3 is moved to _m::m3 and _m::m4 */ for (TileIndex t = 0; t < OLD_MAP_SIZE; t++) { - _m[t].m3 = _old_map3[t * 2]; - _m[t].m4 = _old_map3[t * 2 + 1]; + Tile tile(t); + tile.m3() = _old_map3[t * 2]; + tile.m4() = _old_map3[t * 2 + 1]; } for (TileIndex t = 0; t < OLD_MAP_SIZE; t++) { - switch (GetTileType(t)) { + Tile tile(t); + switch (GetTileType(tile)) { case MP_STATION: - _m[t].m4 = 0; // We do not understand this TTDP station mapping (yet) - switch (_m[t].m5) { + tile.m4() = 0; // We do not understand this TTDP station mapping (yet) + switch (tile.m5()) { /* We have drive through stops at a totally different place */ - case 0x53: case 0x54: _m[t].m5 += 170 - 0x53; break; // Bus drive through - case 0x57: case 0x58: _m[t].m5 += 168 - 0x57; break; // Truck drive through - case 0x55: case 0x56: _m[t].m5 += 170 - 0x55; break; // Bus tram stop - case 0x59: case 0x5A: _m[t].m5 += 168 - 0x59; break; // Truck tram stop + case 0x53: case 0x54: tile.m5() += 170 - 0x53; break; // Bus drive through + case 0x57: case 0x58: tile.m5() += 168 - 0x57; break; // Truck drive through + case 0x55: case 0x56: tile.m5() += 170 - 0x55; break; // Bus tram stop + case 0x59: case 0x5A: tile.m5() += 168 - 0x59; break; // Truck tram stop default: break; } break; case MP_RAILWAY: /* We save presignals different from TTDPatch, convert them */ - if (GB(_m[t].m5, 6, 2) == 1) { // RAIL_TILE_SIGNALS + if (GB(tile.m5(), 6, 2) == 1) { // RAIL_TILE_SIGNALS /* This byte is always zero in TTD for this type of tile */ - if (_m[t].m4) { // Convert the presignals to our own format - _m[t].m4 = (_m[t].m4 >> 1) & 7; + if (tile.m4()) { // Convert the presignals to our own format + tile.m4() = (tile.m4() >> 1) & 7; } } /* TTDPatch stores PBS things in L6 and all elsewhere; so we'll just * clear it for ourselves and let OTTD's rebuild PBS itself */ - _m[t].m4 &= 0xF; // Only keep the lower four bits; upper four is PBS + tile.m4() &= 0xF; // Only keep the lower four bits; upper four is PBS break; case MP_WATER: /* if water class == 3, make river there */ - if (GB(_m[t].m3, 0, 2) == 3) { - SetTileType(t, MP_WATER); - SetTileOwner(t, OWNER_WATER); - _m[t].m2 = 0; - _m[t].m3 = 2; // WATER_CLASS_RIVER - _m[t].m4 = Random(); - _m[t].m5 = 0; + if (GB(tile.m3(), 0, 2) == 3) { + SetTileType(tile, MP_WATER); + SetTileOwner(tile, OWNER_WATER); + tile.m2() = 0; + tile.m3() = 2; // WATER_CLASS_RIVER + tile.m4() = Random(); + tile.m5() = 0; } break; @@ -192,7 +194,8 @@ void FixOldVehicles() RoadVehicle *rv = RoadVehicle::From(v); if (rv->state != RVSB_IN_DEPOT && rv->state != RVSB_WORMHOLE) { ClrBit(rv->state, 2); - if (IsTileType(rv->tile, MP_STATION) && _m[rv->tile].m5 >= 168) { + Tile tile(rv->tile); + if (IsTileType(tile, MP_STATION) && tile.m5() >= 168) { /* Update the vehicle's road state to show we're in a drive through road stop. */ SetBit(rv->state, RVS_IN_DT_ROAD_STOP); } @@ -218,13 +221,14 @@ void FixOldVehicles() static bool FixTTOMapArray() { for (TileIndex t = 0; t < OLD_MAP_SIZE; t++) { - TileType tt = GetTileType(t); + Tile tile(t); + TileType tt = GetTileType(tile); if (tt == 11) { /* TTO has a different way of storing monorail. * Instead of using bits in m3 it uses a different tile type. */ - _m[t].m3 = 1; // rail type = monorail (in TTD) - SetTileType(t, MP_RAILWAY); - _m[t].m2 = 1; // set monorail ground to RAIL_GROUND_GRASS + tile.m3() = 1; // rail type = monorail (in TTD) + SetTileType(tile, MP_RAILWAY); + tile.m2() = 1; // set monorail ground to RAIL_GROUND_GRASS tt = MP_RAILWAY; } @@ -233,18 +237,18 @@ static bool FixTTOMapArray() break; case MP_RAILWAY: - switch (GB(_m[t].m5, 6, 2)) { + switch (GB(tile.m5(), 6, 2)) { case 0: // RAIL_TILE_NORMAL break; case 1: // RAIL_TILE_SIGNALS - _m[t].m4 = (~_m[t].m5 & 1) << 2; // signal variant (present only in OTTD) - SB(_m[t].m2, 6, 2, GB(_m[t].m5, 3, 2)); // signal status - _m[t].m3 |= 0xC0; // both signals are present - _m[t].m5 = HasBit(_m[t].m5, 5) ? 2 : 1; // track direction (only X or Y) - _m[t].m5 |= 0x40; // RAIL_TILE_SIGNALS + tile.m4() = (~tile.m5() & 1) << 2; // signal variant (present only in OTTD) + SB(tile.m2(), 6, 2, GB(tile.m5(), 3, 2)); // signal status + tile.m3() |= 0xC0; // both signals are present + tile.m5() = HasBit(tile.m5(), 5) ? 2 : 1; // track direction (only X or Y) + tile.m5() |= 0x40; // RAIL_TILE_SIGNALS break; case 3: // RAIL_TILE_DEPOT - _m[t].m2 = 0; + tile.m2() = 0; break; default: return false; @@ -252,12 +256,12 @@ static bool FixTTOMapArray() break; case MP_ROAD: // road (depot) or level crossing - switch (GB(_m[t].m5, 4, 4)) { + switch (GB(tile.m5(), 4, 4)) { case 0: // ROAD_TILE_NORMAL - if (_m[t].m2 == 4) _m[t].m2 = 5; // 'small trees' -> ROADSIDE_TREES + if (tile.m2() == 4) tile.m2() = 5; // 'small trees' -> ROADSIDE_TREES break; case 1: // ROAD_TILE_CROSSING (there aren't monorail crossings in TTO) - _m[t].m3 = _m[t].m1; // set owner of road = owner of rail + tile.m3() = tile.m1(); // set owner of road = owner of rail break; case 2: // ROAD_TILE_DEPOT break; @@ -267,69 +271,69 @@ static bool FixTTOMapArray() break; case MP_HOUSE: - _m[t].m3 = _m[t].m2 & 0xC0; // construction stage - _m[t].m2 &= 0x3F; // building type - if (_m[t].m2 >= 5) _m[t].m2++; // skip "large office block on snow" + tile.m3() = tile.m2() & 0xC0; // construction stage + tile.m2() &= 0x3F; // building type + if (tile.m2() >= 5) tile.m2()++; // skip "large office block on snow" break; case MP_TREES: - _m[t].m3 = GB(_m[t].m5, 3, 3); // type of trees - _m[t].m5 &= 0xC7; // number of trees and growth status + tile.m3() = GB(tile.m5(), 3, 3); // type of trees + tile.m5() &= 0xC7; // number of trees and growth status break; case MP_STATION: - _m[t].m3 = (_m[t].m5 >= 0x08 && _m[t].m5 <= 0x0F) ? 1 : 0; // monorail -> 1, others 0 (rail, road, airport, dock) - if (_m[t].m5 >= 8) _m[t].m5 -= 8; // shift for monorail - if (_m[t].m5 >= 0x42) _m[t].m5++; // skip heliport + tile.m3() = (tile.m5() >= 0x08 && tile.m5() <= 0x0F) ? 1 : 0; // monorail -> 1, others 0 (rail, road, airport, dock) + if (tile.m5() >= 8) tile.m5() -= 8; // shift for monorail + if (tile.m5() >= 0x42) tile.m5()++; // skip heliport break; case MP_WATER: - _m[t].m3 = _m[t].m2 = 0; + tile.m3() = tile.m2() = 0; break; case MP_VOID: - _m[t].m2 = _m[t].m3 = _m[t].m5 = 0; + tile.m2() = tile.m3() = tile.m5() = 0; break; case MP_INDUSTRY: - _m[t].m3 = 0; - switch (_m[t].m5) { + tile.m3() = 0; + switch (tile.m5()) { case 0x24: // farm silo - _m[t].m5 = 0x25; + tile.m5() = 0x25; break; case 0x25: case 0x27: // farm case 0x28: case 0x29: case 0x2A: case 0x2B: // factory - _m[t].m5--; + tile.m5()--; break; default: - if (_m[t].m5 >= 0x2C) _m[t].m5 += 3; // iron ore mine, steel mill or bank + if (tile.m5() >= 0x2C) tile.m5() += 3; // iron ore mine, steel mill or bank break; } break; case MP_TUNNELBRIDGE: - if (HasBit(_m[t].m5, 7)) { // bridge - byte m5 = _m[t].m5; - _m[t].m5 = m5 & 0xE1; // copy bits 7..5, 1 - if (GB(m5, 1, 2) == 1) _m[t].m5 |= 0x02; // road bridge - if (GB(m5, 1, 2) == 3) _m[t].m2 |= 0xA0; // monorail bridge -> tubular, steel bridge + if (HasBit(tile.m5(), 7)) { // bridge + byte m5 = tile.m5(); + tile.m5() = m5 & 0xE1; // copy bits 7..5, 1 + if (GB(m5, 1, 2) == 1) tile.m5() |= 0x02; // road bridge + if (GB(m5, 1, 2) == 3) tile.m2() |= 0xA0; // monorail bridge -> tubular, steel bridge if (!HasBit(m5, 6)) { // bridge head - _m[t].m3 = (GB(m5, 1, 2) == 3) ? 1 : 0; // track subtype (1 for monorail, 0 for others) + tile.m3() = (GB(m5, 1, 2) == 3) ? 1 : 0; // track subtype (1 for monorail, 0 for others) } else { // middle bridge part - _m[t].m3 = HasBit(m5, 2) ? 0x10 : 0; // track subtype on bridge - if (GB(m5, 3, 2) == 3) _m[t].m3 |= 1; // track subtype under bridge - if (GB(m5, 3, 2) == 1) _m[t].m5 |= 0x08; // set for road/water under (0 for rail/clear) + tile.m3() = HasBit(m5, 2) ? 0x10 : 0; // track subtype on bridge + if (GB(m5, 3, 2) == 3) tile.m3() |= 1; // track subtype under bridge + if (GB(m5, 3, 2) == 1) tile.m5() |= 0x08; // set for road/water under (0 for rail/clear) } } else { // tunnel entrance/exit - _m[t].m2 = 0; - _m[t].m3 = HasBit(_m[t].m5, 3); // monorail - _m[t].m5 &= HasBit(_m[t].m5, 3) ? 0x03 : 0x07 ; // direction, transport type (== 0 for rail) + tile.m2() = 0; + tile.m3() = HasBit(tile.m5(), 3); // monorail + tile.m5() &= HasBit(tile.m5(), 3) ? 0x03 : 0x07 ; // direction, transport type (== 0 for rail) } break; case MP_OBJECT: - _m[t].m2 = 0; - _m[t].m3 = 0; + tile.m2() = 0; + tile.m3() = 0; break; default: @@ -1471,10 +1475,10 @@ static bool LoadOldMapPart1(LoadgameState *ls, int num) } for (uint i = 0; i < OLD_MAP_SIZE; i++) { - _m[i].m1 = ReadByte(ls); + Tile(i).m1() = ReadByte(ls); } for (uint i = 0; i < OLD_MAP_SIZE; i++) { - _m[i].m2 = ReadByte(ls); + Tile(i).m2() = ReadByte(ls); } if (_savegame_type != SGT_TTO) { @@ -1484,10 +1488,10 @@ static bool LoadOldMapPart1(LoadgameState *ls, int num) } for (uint i = 0; i < OLD_MAP_SIZE / 4; i++) { byte b = ReadByte(ls); - _me[i * 4 + 0].m6 = GB(b, 0, 2); - _me[i * 4 + 1].m6 = GB(b, 2, 2); - _me[i * 4 + 2].m6 = GB(b, 4, 2); - _me[i * 4 + 3].m6 = GB(b, 6, 2); + Tile(i * 4 + 0).m6() = GB(b, 0, 2); + Tile(i * 4 + 1).m6() = GB(b, 2, 2); + Tile(i * 4 + 2).m6() = GB(b, 4, 2); + Tile(i * 4 + 3).m6() = GB(b, 6, 2); } } @@ -1499,10 +1503,10 @@ static bool LoadOldMapPart2(LoadgameState *ls, int num) uint i; for (i = 0; i < OLD_MAP_SIZE; i++) { - _m[i].type = ReadByte(ls); + Tile(i).type() = ReadByte(ls); } for (i = 0; i < OLD_MAP_SIZE; i++) { - _m[i].m5 = ReadByte(ls); + Tile(i).m5() = ReadByte(ls); } return true; diff --git a/src/saveload/station_sl.cpp b/src/saveload/station_sl.cpp index f964a8b8cd..e72231bb8a 100644 --- a/src/saveload/station_sl.cpp +++ b/src/saveload/station_sl.cpp @@ -91,9 +91,10 @@ void MoveBuoysToWaypoints() if (train) { /* When we make a rail waypoint of the station, convert the map as well. */ for (TileIndex t : train_st) { - if (!IsTileType(t, MP_STATION) || GetStationIndex(t) != index) continue; + Tile tile(t); + if (!IsTileType(tile, MP_STATION) || GetStationIndex(tile) != index) continue; - SB(_me[t].m6, 3, 3, STATION_WAYPOINT); + SB(tile.m6(), 3, 3, STATION_WAYPOINT); wp->rect.BeforeAddTile(t, StationRect::ADD_FORCE); } diff --git a/src/saveload/waypoint_sl.cpp b/src/saveload/waypoint_sl.cpp index bc765a9511..e8f66fb5d0 100644 --- a/src/saveload/waypoint_sl.cpp +++ b/src/saveload/waypoint_sl.cpp @@ -74,10 +74,11 @@ void MoveWaypointsToBaseStations() if (wp.delete_ctr != 0) continue; // The waypoint was deleted /* Waypoint indices were not added to the map prior to this. */ - _m[wp.xy].m2 = (StationID)wp.index; + Tile tile = wp.xy; + tile.m2() = (StationID)wp.index; - if (HasBit(_m[wp.xy].m3, 4)) { - wp.spec = StationClass::Get(STAT_CLASS_WAYP)->GetSpec(_m[wp.xy].m4 + 1); + if (HasBit(tile.m3(), 4)) { + wp.spec = StationClass::Get(STAT_CLASS_WAYP)->GetSpec(tile.m4() + 1); } } } else { @@ -102,10 +103,10 @@ void MoveWaypointsToBaseStations() TileIndex t = wp.xy; /* Sometimes waypoint (sign) locations became disconnected from their actual location in * the map array. If this is the case, try to locate the actual location in the map array */ - if (!IsTileType(t, MP_RAILWAY) || GetRailTileType(t) != 2 /* RAIL_TILE_WAYPOINT */ || _m[t].m2 != wp.index) { + if (!IsTileType(t, MP_RAILWAY) || GetRailTileType(t) != 2 /* RAIL_TILE_WAYPOINT */ || Tile(t).m2() != wp.index) { Debug(sl, 0, "Found waypoint tile {} with invalid position", t); for (t = 0; t < Map::Size(); t++) { - if (IsTileType(t, MP_RAILWAY) && GetRailTileType(t) == 2 /* RAIL_TILE_WAYPOINT */ && _m[t].m2 == wp.index) { + if (IsTileType(t, MP_RAILWAY) && GetRailTileType(t) == 2 /* RAIL_TILE_WAYPOINT */ && Tile(t).m2() == wp.index) { Debug(sl, 0, "Found actual waypoint position at {}", t); break; } @@ -125,19 +126,20 @@ void MoveWaypointsToBaseStations() new_wp->string_id = STR_SV_STNAME_WAYPOINT; /* The tile might've been reserved! */ - bool reserved = !IsSavegameVersionBefore(SLV_100) && HasBit(_m[t].m5, 4); + Tile tile(t); + bool reserved = !IsSavegameVersionBefore(SLV_100) && HasBit(tile.m5(), 4); /* The tile really has our waypoint, so reassign the map array */ - MakeRailWaypoint(t, GetTileOwner(t), new_wp->index, (Axis)GB(_m[t].m5, 0, 1), 0, GetRailType(t)); + MakeRailWaypoint(tile, GetTileOwner(tile), new_wp->index, (Axis)GB(tile.m5(), 0, 1), 0, GetRailType(tile)); new_wp->facilities |= FACIL_TRAIN; - new_wp->owner = GetTileOwner(t); + new_wp->owner = GetTileOwner(tile); - SetRailStationReservation(t, reserved); + SetRailStationReservation(tile, reserved); if (wp.spec != nullptr) { - SetCustomStationSpecIndex(t, AllocateSpecToStation(wp.spec, new_wp, true)); + SetCustomStationSpecIndex(tile, AllocateSpecToStation(wp.spec, new_wp, true)); } - new_wp->rect.BeforeAddTile(t, StationRect::ADD_FORCE); + new_wp->rect.BeforeAddTile(tile, StationRect::ADD_FORCE); wp.new_index = new_wp->index; } diff --git a/src/script/api/script_story_page.cpp b/src/script/api/script_story_page.cpp index 970e983a70..0aab8da805 100644 --- a/src/script/api/script_story_page.cpp +++ b/src/script/api/script_story_page.cpp @@ -69,7 +69,7 @@ static inline bool StoryPageElementTypeRequiresText(StoryPageElementType type) encoded_text = text->GetEncodedText(); EnforcePreconditionEncodedText(STORY_PAGE_ELEMENT_INVALID, encoded_text); } - EnforcePrecondition(STORY_PAGE_ELEMENT_INVALID, type != SPET_LOCATION || ::IsValidTile(reference)); + EnforcePrecondition(STORY_PAGE_ELEMENT_INVALID, type != SPET_LOCATION || ::IsValidTile((::TileIndex)reference)); EnforcePrecondition(STORY_PAGE_ELEMENT_INVALID, type != SPET_GOAL || ScriptGoal::IsValidGoal((ScriptGoal::GoalID)reference)); EnforcePrecondition(STORY_PAGE_ELEMENT_INVALID, type != SPET_GOAL || !(StoryPage::Get(story_page_id)->company == INVALID_COMPANY && Goal::Get(reference)->company != INVALID_COMPANY)); @@ -118,7 +118,7 @@ static inline bool StoryPageElementTypeRequiresText(StoryPageElementType type) encoded_text = text->GetEncodedText(); EnforcePreconditionEncodedText(false, encoded_text); } - EnforcePrecondition(false, type != ::SPET_LOCATION || ::IsValidTile(reference)); + EnforcePrecondition(false, type != ::SPET_LOCATION || ::IsValidTile((::TileIndex)reference)); EnforcePrecondition(false, type != ::SPET_GOAL || ScriptGoal::IsValidGoal((ScriptGoal::GoalID)reference)); EnforcePrecondition(false, type != ::SPET_GOAL || !(p->company == INVALID_COMPANY && Goal::Get(reference)->company != INVALID_COMPANY)); diff --git a/src/station_cmd.cpp b/src/station_cmd.cpp index 802c0fb8cb..53b3ef168e 100644 --- a/src/station_cmd.cpp +++ b/src/station_cmd.cpp @@ -78,7 +78,7 @@ * @pre IsTileType(t, MP_STATION) * @return true if and only if the tile is a hangar. */ -bool IsHangar(TileIndex t) +bool IsHangar(Tile t) { assert(IsTileType(t, MP_STATION)); @@ -89,7 +89,7 @@ bool IsHangar(TileIndex t) const AirportSpec *as = st->airport.GetSpec(); for (uint i = 0; i < as->nof_depots; i++) { - if (st->airport.GetHangarTile(i) == t) return true; + if (st->airport.GetHangarTile(i) == TileIndex(t)) return true; } return false; @@ -2462,11 +2462,12 @@ CommandCost CmdBuildAirport(DoCommandFlag flags, TileIndex tile, byte airport_ty st->rect.BeforeAddRect(tile, w, h, StationRect::ADD_TRY); for (AirportTileTableIterator iter(as->table[layout], tile); iter != INVALID_TILE; ++iter) { - MakeAirport(iter, st->owner, st->index, iter.GetStationGfx(), WATER_CLASS_INVALID); - SetStationTileRandomBits(iter, GB(Random(), 0, 4)); + Tile t(iter); + MakeAirport(t, st->owner, st->index, iter.GetStationGfx(), WATER_CLASS_INVALID); + SetStationTileRandomBits(t, GB(Random(), 0, 4)); st->airport.Add(iter); - if (AirportTileSpec::Get(GetTranslatedAirportTileID(iter.GetStationGfx()))->animation.status != ANIM_STATUS_NO_ANIMATION) AddAnimatedTile(iter); + if (AirportTileSpec::Get(GetTranslatedAirportTileID(iter.GetStationGfx()))->animation.status != ANIM_STATUS_NO_ANIMATION) AddAnimatedTile(t); } /* Only call the animation trigger after all tiles have been built */ diff --git a/src/station_map.h b/src/station_map.h index 201d3554aa..f525cb48ba 100644 --- a/src/station_map.h +++ b/src/station_map.h @@ -25,10 +25,10 @@ typedef byte StationGfx; ///< Index of station graphics. @see _station_display_d * @pre IsTileType(t, MP_STATION) * @return Station ID of the station at \a t */ -static inline StationID GetStationIndex(TileIndex t) +static inline StationID GetStationIndex(Tile t) { assert(IsTileType(t, MP_STATION)); - return (StationID)_m[t].m2; + return (StationID)t.m2(); } @@ -41,10 +41,10 @@ static const int GFX_TRUCK_BUS_DRIVETHROUGH_OFFSET = 4; ///< The offset for the * @pre IsTileType(t, MP_STATION) * @return the station type */ -static inline StationType GetStationType(TileIndex t) +static inline StationType GetStationType(Tile t) { assert(IsTileType(t, MP_STATION)); - return (StationType)GB(_me[t].m6, 3, 3); + return (StationType)GB(t.m6(), 3, 3); } /** @@ -53,7 +53,7 @@ static inline StationType GetStationType(TileIndex t) * @pre GetStationType(t) == STATION_TRUCK || GetStationType(t) == STATION_BUS * @return the road stop type */ -static inline RoadStopType GetRoadStopType(TileIndex t) +static inline RoadStopType GetRoadStopType(Tile t) { assert(GetStationType(t) == STATION_TRUCK || GetStationType(t) == STATION_BUS); return GetStationType(t) == STATION_TRUCK ? ROADSTOP_TRUCK : ROADSTOP_BUS; @@ -65,10 +65,10 @@ static inline RoadStopType GetRoadStopType(TileIndex t) * @pre IsTileType(t, MP_STATION) * @return the station graphics */ -static inline StationGfx GetStationGfx(TileIndex t) +static inline StationGfx GetStationGfx(Tile t) { assert(IsTileType(t, MP_STATION)); - return _m[t].m5; + return t.m5(); } /** @@ -77,10 +77,10 @@ static inline StationGfx GetStationGfx(TileIndex t) * @param gfx the new graphics * @pre IsTileType(t, MP_STATION) */ -static inline void SetStationGfx(TileIndex t, StationGfx gfx) +static inline void SetStationGfx(Tile t, StationGfx gfx) { assert(IsTileType(t, MP_STATION)); - _m[t].m5 = gfx; + t.m5() = gfx; } /** @@ -89,7 +89,7 @@ static inline void SetStationGfx(TileIndex t, StationGfx gfx) * @pre IsTileType(t, MP_STATION) * @return true if and only if the tile is a rail station */ -static inline bool IsRailStation(TileIndex t) +static inline bool IsRailStation(Tile t) { return GetStationType(t) == STATION_RAIL; } @@ -99,7 +99,7 @@ static inline bool IsRailStation(TileIndex t) * @param t the tile to get the information from * @return true if and only if the tile is a rail station */ -static inline bool IsRailStationTile(TileIndex t) +static inline bool IsRailStationTile(Tile t) { return IsTileType(t, MP_STATION) && IsRailStation(t); } @@ -110,7 +110,7 @@ static inline bool IsRailStationTile(TileIndex t) * @pre IsTileType(t, MP_STATION) * @return true if and only if the tile is a rail waypoint */ -static inline bool IsRailWaypoint(TileIndex t) +static inline bool IsRailWaypoint(Tile t) { return GetStationType(t) == STATION_WAYPOINT; } @@ -120,7 +120,7 @@ static inline bool IsRailWaypoint(TileIndex t) * @param t the tile to get the information from * @return true if and only if the tile is a rail waypoint */ -static inline bool IsRailWaypointTile(TileIndex t) +static inline bool IsRailWaypointTile(Tile t) { return IsTileType(t, MP_STATION) && IsRailWaypoint(t); } @@ -132,7 +132,7 @@ static inline bool IsRailWaypointTile(TileIndex t) * @pre IsTileType(t, MP_STATION) * @return true if and only if the tile has rail */ -static inline bool HasStationRail(TileIndex t) +static inline bool HasStationRail(Tile t) { return IsRailStation(t) || IsRailWaypoint(t); } @@ -143,7 +143,7 @@ static inline bool HasStationRail(TileIndex t) * @param t the tile to check * @return true if and only if the tile is a station tile and has rail */ -static inline bool HasStationTileRail(TileIndex t) +static inline bool HasStationTileRail(Tile t) { return IsTileType(t, MP_STATION) && HasStationRail(t); } @@ -154,7 +154,7 @@ static inline bool HasStationTileRail(TileIndex t) * @pre IsTileType(t, MP_STATION) * @return true if and only if the tile is an airport */ -static inline bool IsAirport(TileIndex t) +static inline bool IsAirport(Tile t) { return GetStationType(t) == STATION_AIRPORT; } @@ -164,12 +164,12 @@ static inline bool IsAirport(TileIndex t) * @param t the tile to get the information from * @return true if and only if the tile is an airport */ -static inline bool IsAirportTile(TileIndex t) +static inline bool IsAirportTile(Tile t) { return IsTileType(t, MP_STATION) && IsAirport(t); } -bool IsHangar(TileIndex t); +bool IsHangar(Tile t); /** * Is the station at \a t a truck stop? @@ -177,7 +177,7 @@ bool IsHangar(TileIndex t); * @pre IsTileType(t, MP_STATION) * @return \c true if station is a truck stop, \c false otherwise */ -static inline bool IsTruckStop(TileIndex t) +static inline bool IsTruckStop(Tile t) { return GetStationType(t) == STATION_TRUCK; } @@ -188,7 +188,7 @@ static inline bool IsTruckStop(TileIndex t) * @pre IsTileType(t, MP_STATION) * @return \c true if station is a bus stop, \c false otherwise */ -static inline bool IsBusStop(TileIndex t) +static inline bool IsBusStop(Tile t) { return GetStationType(t) == STATION_BUS; } @@ -199,7 +199,7 @@ static inline bool IsBusStop(TileIndex t) * @pre IsTileType(t, MP_STATION) * @return \c true if station at the tile is a bus top or a truck stop, \c false otherwise */ -static inline bool IsRoadStop(TileIndex t) +static inline bool IsRoadStop(Tile t) { assert(IsTileType(t, MP_STATION)); return IsTruckStop(t) || IsBusStop(t); @@ -210,7 +210,7 @@ static inline bool IsRoadStop(TileIndex t) * @param t Tile to check * @return \c true if the tile is a station tile and a road stop */ -static inline bool IsRoadStopTile(TileIndex t) +static inline bool IsRoadStopTile(Tile t) { return IsTileType(t, MP_STATION) && IsRoadStop(t); } @@ -220,7 +220,7 @@ static inline bool IsRoadStopTile(TileIndex t) * @param t Tile to check * @return \c true if the tile is a station tile and a standard road stop */ -static inline bool IsStandardRoadStopTile(TileIndex t) +static inline bool IsStandardRoadStopTile(Tile t) { return IsRoadStopTile(t) && GetStationGfx(t) < GFX_TRUCK_BUS_DRIVETHROUGH_OFFSET; } @@ -230,7 +230,7 @@ static inline bool IsStandardRoadStopTile(TileIndex t) * @param t Tile to check * @return \c true if the tile is a station tile and a drive through road stop */ -static inline bool IsDriveThroughStopTile(TileIndex t) +static inline bool IsDriveThroughStopTile(Tile t) { return IsRoadStopTile(t) && GetStationGfx(t) >= GFX_TRUCK_BUS_DRIVETHROUGH_OFFSET; } @@ -243,7 +243,7 @@ StationGfx GetTranslatedAirportTileID(StationGfx gfx); * @pre IsAirport(t) * @return the station graphics */ -static inline StationGfx GetAirportGfx(TileIndex t) +static inline StationGfx GetAirportGfx(Tile t) { assert(IsAirport(t)); return GetTranslatedAirportTileID(GetStationGfx(t)); @@ -255,7 +255,7 @@ static inline StationGfx GetAirportGfx(TileIndex t) * @pre IsRoadStopTile(t) * @return the direction of the entrance */ -static inline DiagDirection GetRoadStopDir(TileIndex t) +static inline DiagDirection GetRoadStopDir(Tile t) { StationGfx gfx = GetStationGfx(t); assert(IsRoadStopTile(t)); @@ -272,7 +272,7 @@ static inline DiagDirection GetRoadStopDir(TileIndex t) * @pre IsTileType(t, MP_STATION) * @return \c true if the tile is an oilrig tile */ -static inline bool IsOilRig(TileIndex t) +static inline bool IsOilRig(Tile t) { return GetStationType(t) == STATION_OILRIG; } @@ -283,7 +283,7 @@ static inline bool IsOilRig(TileIndex t) * @pre IsTileType(t, MP_STATION) * @return \c true if the tile is a dock */ -static inline bool IsDock(TileIndex t) +static inline bool IsDock(Tile t) { return GetStationType(t) == STATION_DOCK; } @@ -293,7 +293,7 @@ static inline bool IsDock(TileIndex t) * @param t Tile to check * @return \c true if the tile is a dock */ -static inline bool IsDockTile(TileIndex t) +static inline bool IsDockTile(Tile t) { return IsTileType(t, MP_STATION) && GetStationType(t) == STATION_DOCK; } @@ -304,7 +304,7 @@ static inline bool IsDockTile(TileIndex t) * @pre IsTileType(t, MP_STATION) * @return \c true if the tile is a buoy */ -static inline bool IsBuoy(TileIndex t) +static inline bool IsBuoy(Tile t) { return GetStationType(t) == STATION_BUOY; } @@ -314,7 +314,7 @@ static inline bool IsBuoy(TileIndex t) * @param t Tile to check * @return \c true if the tile is a buoy */ -static inline bool IsBuoyTile(TileIndex t) +static inline bool IsBuoyTile(Tile t) { return IsTileType(t, MP_STATION) && IsBuoy(t); } @@ -324,7 +324,7 @@ static inline bool IsBuoyTile(TileIndex t) * @param t Tile to check * @return \c true if the tile is an hangar */ -static inline bool IsHangarTile(TileIndex t) +static inline bool IsHangarTile(Tile t) { return IsTileType(t, MP_STATION) && IsHangar(t); } @@ -335,7 +335,7 @@ static inline bool IsHangarTile(TileIndex t) * @pre HasStationRail(t) * @return The direction of the rails on tile \a t. */ -static inline Axis GetRailStationAxis(TileIndex t) +static inline Axis GetRailStationAxis(Tile t) { assert(HasStationRail(t)); return HasBit(GetStationGfx(t), 0) ? AXIS_Y : AXIS_X; @@ -347,7 +347,7 @@ static inline Axis GetRailStationAxis(TileIndex t) * @pre HasStationRail(t) * @return The rail track of the rails on tile \a t. */ -static inline Track GetRailStationTrack(TileIndex t) +static inline Track GetRailStationTrack(Tile t) { return AxisToTrack(GetRailStationAxis(t)); } @@ -358,7 +358,7 @@ static inline Track GetRailStationTrack(TileIndex t) * @pre HasStationRail(t) * @return The trackbits of the rails on tile \a t. */ -static inline TrackBits GetRailStationTrackBits(TileIndex t) +static inline TrackBits GetRailStationTrackBits(Tile t) { return AxisToTrackBits(GetRailStationAxis(t)); } @@ -376,7 +376,7 @@ static inline TrackBits GetRailStationTrackBits(TileIndex t) * @pre IsRailStationTile(station_tile) * @return true if the two tiles are compatible */ -static inline bool IsCompatibleTrainStationTile(TileIndex test_tile, TileIndex station_tile) +static inline bool IsCompatibleTrainStationTile(Tile test_tile, Tile station_tile) { assert(IsRailStationTile(station_tile)); return IsRailStationTile(test_tile) && IsCompatibleRail(GetRailType(test_tile), GetRailType(station_tile)) && @@ -391,10 +391,10 @@ static inline bool IsCompatibleTrainStationTile(TileIndex test_tile, TileIndex s * @param t the station tile * @return reservation state */ -static inline bool HasStationReservation(TileIndex t) +static inline bool HasStationReservation(Tile t) { assert(HasStationRail(t)); - return HasBit(_me[t].m6, 2); + return HasBit(t.m6(), 2); } /** @@ -403,10 +403,10 @@ static inline bool HasStationReservation(TileIndex t) * @param t the station tile * @param b the reservation state */ -static inline void SetRailStationReservation(TileIndex t, bool b) +static inline void SetRailStationReservation(Tile t, bool b) { assert(HasStationRail(t)); - SB(_me[t].m6, 2, 1, b ? 1 : 0); + SB(t.m6(), 2, 1, b ? 1 : 0); } /** @@ -415,7 +415,7 @@ static inline void SetRailStationReservation(TileIndex t, bool b) * @param t the tile * @return reserved track bits */ -static inline TrackBits GetStationReservationTrackBits(TileIndex t) +static inline TrackBits GetStationReservationTrackBits(Tile t) { return HasStationReservation(t) ? GetRailStationTrackBits(t) : TRACK_BIT_NONE; } @@ -427,7 +427,7 @@ static inline TrackBits GetStationReservationTrackBits(TileIndex t) * @pre \a t is the land part of the dock * @return The direction of the dock on tile \a t. */ -static inline DiagDirection GetDockDirection(TileIndex t) +static inline DiagDirection GetDockDirection(Tile t) { StationGfx gfx = GetStationGfx(t); assert(IsDock(t) && gfx < GFX_DOCK_BASE_WATER_PART); @@ -441,7 +441,7 @@ static inline DiagDirection GetDockDirection(TileIndex t) * @pre IsBuoy(t) || IsOilRig(t) || IsDock(t) * @return The offset from this tile that should be used as destination for ships. */ -static inline TileIndexDiffC GetDockOffset(TileIndex t) +static inline TileIndexDiffC GetDockOffset(Tile t) { static const TileIndexDiffC buoy_offset = {0, 0}; static const TileIndexDiffC oilrig_offset = {2, 0}; @@ -467,10 +467,10 @@ static inline TileIndexDiffC GetDockOffset(TileIndex t) * @pre HasStationTileRail(t) * @return True if this station is part of a newgrf station. */ -static inline bool IsCustomStationSpecIndex(TileIndex t) +static inline bool IsCustomStationSpecIndex(Tile t) { assert(HasStationTileRail(t)); - return _m[t].m4 != 0; + return t.m4() != 0; } /** @@ -479,10 +479,10 @@ static inline bool IsCustomStationSpecIndex(TileIndex t) * @param specindex The new spec. * @pre HasStationTileRail(t) */ -static inline void SetCustomStationSpecIndex(TileIndex t, byte specindex) +static inline void SetCustomStationSpecIndex(Tile t, byte specindex) { assert(HasStationTileRail(t)); - _m[t].m4 = specindex; + t.m4() = specindex; } /** @@ -491,10 +491,10 @@ static inline void SetCustomStationSpecIndex(TileIndex t, byte specindex) * @pre HasStationTileRail(t) * @return The custom station spec of this tile. */ -static inline uint GetCustomStationSpecIndex(TileIndex t) +static inline uint GetCustomStationSpecIndex(Tile t) { assert(HasStationTileRail(t)); - return _m[t].m4; + return t.m4(); } /** @@ -503,10 +503,10 @@ static inline uint GetCustomStationSpecIndex(TileIndex t) * @pre IsRoadStopTile(t) * @return True if this station is part of a newgrf station. */ -static inline bool IsCustomRoadStopSpecIndex(TileIndex t) +static inline bool IsCustomRoadStopSpecIndex(Tile t) { assert(IsRoadStopTile(t)); - return GB(_me[t].m8, 0, 6) != 0; + return GB(t.m8(), 0, 6) != 0; } /** @@ -515,10 +515,10 @@ static inline bool IsCustomRoadStopSpecIndex(TileIndex t) * @param specindex The new spec. * @pre IsRoadStopTile(t) */ -static inline void SetCustomRoadStopSpecIndex(TileIndex t, byte specindex) +static inline void SetCustomRoadStopSpecIndex(Tile t, byte specindex) { assert(IsRoadStopTile(t)); - SB(_me[t].m8, 0, 6, specindex); + SB(t.m8(), 0, 6, specindex); } /** @@ -527,10 +527,10 @@ static inline void SetCustomRoadStopSpecIndex(TileIndex t, byte specindex) * @pre IsRoadStopTile(t) * @return The custom station spec of this tile. */ -static inline uint GetCustomRoadStopSpecIndex(TileIndex t) +static inline uint GetCustomRoadStopSpecIndex(Tile t) { assert(IsRoadStopTile(t)); - return GB(_me[t].m8, 0, 6); + return GB(t.m8(), 0, 6); } /** @@ -539,10 +539,10 @@ static inline uint GetCustomRoadStopSpecIndex(TileIndex t) * @param random_bits The random bits. * @pre IsTileType(t, MP_STATION) */ -static inline void SetStationTileRandomBits(TileIndex t, byte random_bits) +static inline void SetStationTileRandomBits(Tile t, byte random_bits) { assert(IsTileType(t, MP_STATION)); - SB(_m[t].m3, 4, 4, random_bits); + SB(t.m3(), 4, 4, random_bits); } /** @@ -551,10 +551,10 @@ static inline void SetStationTileRandomBits(TileIndex t, byte random_bits) * @pre IsTileType(t, MP_STATION) * @return The random bits for this station tile. */ -static inline byte GetStationTileRandomBits(TileIndex t) +static inline byte GetStationTileRandomBits(Tile t) { assert(IsTileType(t, MP_STATION)); - return GB(_m[t].m3, 4, 4); + return GB(t.m3(), 4, 4); } /** @@ -566,20 +566,20 @@ static inline byte GetStationTileRandomBits(TileIndex t) * @param section the StationGfx to be used for this tile * @param wc The water class of the station */ -static inline void MakeStation(TileIndex t, Owner o, StationID sid, StationType st, byte section, WaterClass wc = WATER_CLASS_INVALID) +static inline void MakeStation(Tile t, Owner o, StationID sid, StationType st, byte section, WaterClass wc = WATER_CLASS_INVALID) { SetTileType(t, MP_STATION); SetTileOwner(t, o); SetWaterClass(t, wc); SetDockingTile(t, false); - _m[t].m2 = sid; - _m[t].m3 = 0; - _m[t].m4 = 0; - _m[t].m5 = section; - SB(_me[t].m6, 2, 1, 0); - SB(_me[t].m6, 3, 3, st); - _me[t].m7 = 0; - _me[t].m8 = 0; + t.m2() = sid; + t.m3() = 0; + t.m4() = 0; + t.m5() = section; + SB(t.m6(), 2, 1, 0); + SB(t.m6(), 3, 3, st); + t.m7() = 0; + t.m8() = 0; } /** @@ -591,7 +591,7 @@ static inline void MakeStation(TileIndex t, Owner o, StationID sid, StationType * @param section the StationGfx to be used for this tile * @param rt the railtype of this tile */ -static inline void MakeRailStation(TileIndex t, Owner o, StationID sid, Axis a, byte section, RailType rt) +static inline void MakeRailStation(Tile t, Owner o, StationID sid, Axis a, byte section, RailType rt) { MakeStation(t, o, sid, STATION_RAIL, section + a); SetRailType(t, rt); @@ -607,7 +607,7 @@ static inline void MakeRailStation(TileIndex t, Owner o, StationID sid, Axis a, * @param section the StationGfx to be used for this tile * @param rt the railtype of this tile */ -static inline void MakeRailWaypoint(TileIndex t, Owner o, StationID sid, Axis a, byte section, RailType rt) +static inline void MakeRailWaypoint(Tile t, Owner o, StationID sid, Axis a, byte section, RailType rt) { MakeStation(t, o, sid, STATION_WAYPOINT, section + a); SetRailType(t, rt); @@ -624,7 +624,7 @@ static inline void MakeRailWaypoint(TileIndex t, Owner o, StationID sid, Axis a, * @param tram_rt the tram roadtype on this tile * @param d the direction of the roadstop */ -static inline void MakeRoadStop(TileIndex t, Owner o, StationID sid, RoadStopType rst, RoadType road_rt, RoadType tram_rt, DiagDirection d) +static inline void MakeRoadStop(Tile t, Owner o, StationID sid, RoadStopType rst, RoadType road_rt, RoadType tram_rt, DiagDirection d) { MakeStation(t, o, sid, (rst == ROADSTOP_BUS ? STATION_BUS : STATION_TRUCK), d); SetRoadTypes(t, road_rt, tram_rt); @@ -644,7 +644,7 @@ static inline void MakeRoadStop(TileIndex t, Owner o, StationID sid, RoadStopTyp * @param tram_rt the tram roadtype on this tile * @param a the direction of the roadstop */ -static inline void MakeDriveThroughRoadStop(TileIndex t, Owner station, Owner road, Owner tram, StationID sid, RoadStopType rst, RoadType road_rt, RoadType tram_rt, Axis a) +static inline void MakeDriveThroughRoadStop(Tile t, Owner station, Owner road, Owner tram, StationID sid, RoadStopType rst, RoadType road_rt, RoadType tram_rt, Axis a) { MakeStation(t, station, sid, (rst == ROADSTOP_BUS ? STATION_BUS : STATION_TRUCK), GFX_TRUCK_BUS_DRIVETHROUGH_OFFSET + a); SetRoadTypes(t, road_rt, tram_rt); @@ -660,7 +660,7 @@ static inline void MakeDriveThroughRoadStop(TileIndex t, Owner station, Owner ro * @param section the StationGfx to be used for this tile * @param wc the type of water on this tile */ -static inline void MakeAirport(TileIndex t, Owner o, StationID sid, byte section, WaterClass wc) +static inline void MakeAirport(Tile t, Owner o, StationID sid, byte section, WaterClass wc) { MakeStation(t, o, sid, STATION_AIRPORT, section, wc); } @@ -671,7 +671,7 @@ static inline void MakeAirport(TileIndex t, Owner o, StationID sid, byte section * @param sid the station to which this tile belongs * @param wc the type of water on this tile */ -static inline void MakeBuoy(TileIndex t, StationID sid, WaterClass wc) +static inline void MakeBuoy(Tile t, StationID sid, WaterClass wc) { /* Make the owner of the buoy tile the same as the current owner of the * water tile. In this way, we can reset the owner of the water to its @@ -687,10 +687,10 @@ static inline void MakeBuoy(TileIndex t, StationID sid, WaterClass wc) * @param d the direction of the dock * @param wc the type of water on this tile */ -static inline void MakeDock(TileIndex t, Owner o, StationID sid, DiagDirection d, WaterClass wc) +static inline void MakeDock(Tile t, Owner o, StationID sid, DiagDirection d, WaterClass wc) { MakeStation(t, o, sid, STATION_DOCK, d); - MakeStation(t + TileOffsByDiagDir(d), o, sid, STATION_DOCK, GFX_DOCK_BASE_WATER_PART + DiagDirToAxis(d), wc); + MakeStation(TileIndex(t) + TileOffsByDiagDir(d), o, sid, STATION_DOCK, GFX_DOCK_BASE_WATER_PART + DiagDirToAxis(d), wc); } /** @@ -699,7 +699,7 @@ static inline void MakeDock(TileIndex t, Owner o, StationID sid, DiagDirection d * @param sid the station to which this tile belongs * @param wc the type of water on this tile */ -static inline void MakeOilrig(TileIndex t, StationID sid, WaterClass wc) +static inline void MakeOilrig(Tile t, StationID sid, WaterClass wc) { MakeStation(t, OWNER_NONE, sid, STATION_OILRIG, 0, wc); } diff --git a/src/tile_map.h b/src/tile_map.h index fb27154ab6..4111bfbd6b 100644 --- a/src/tile_map.h +++ b/src/tile_map.h @@ -26,10 +26,10 @@ * @return the height of the tile * @pre tile < Map::Size() */ -debug_inline static uint TileHeight(TileIndex tile) +debug_inline static uint TileHeight(Tile tile) { assert(tile < Map::Size()); - return _m[tile].height; + return tile.height(); } /** @@ -54,11 +54,11 @@ static inline uint TileHeightOutsideMap(int x, int y) * @pre tile < Map::Size() * @pre height <= MAX_TILE_HEIGHT */ -static inline void SetTileHeight(TileIndex tile, uint height) +static inline void SetTileHeight(Tile tile, uint height) { assert(tile < Map::Size()); assert(height <= MAX_TILE_HEIGHT); - _m[tile].height = height; + tile.height() = height; } /** @@ -69,7 +69,7 @@ static inline void SetTileHeight(TileIndex tile, uint height) * @param tile The tile to get the height * @return The height of the tile in pixel */ -static inline uint TilePixelHeight(TileIndex tile) +static inline uint TilePixelHeight(Tile tile) { return TileHeight(tile) * TILE_HEIGHT; } @@ -93,10 +93,10 @@ static inline uint TilePixelHeightOutsideMap(int x, int y) * @return The tiletype of the tile * @pre tile < Map::Size() */ -debug_inline static TileType GetTileType(TileIndex tile) +debug_inline static TileType GetTileType(Tile tile) { assert(tile < Map::Size()); - return (TileType)GB(_m[tile].type, 4, 4); + return (TileType)GB(tile.type(), 4, 4); } /** @@ -106,7 +106,7 @@ debug_inline static TileType GetTileType(TileIndex tile) * @return Whether the tile is in the interior of the map * @pre tile < Map::Size() */ -static inline bool IsInnerTile(TileIndex tile) +static inline bool IsInnerTile(Tile tile) { assert(tile < Map::Size()); @@ -128,14 +128,14 @@ static inline bool IsInnerTile(TileIndex tile) * @pre tile < Map::Size() * @pre type MP_VOID <=> tile is on the south-east or south-west edge. */ -static inline void SetTileType(TileIndex tile, TileType type) +static inline void SetTileType(Tile tile, TileType type) { assert(tile < Map::Size()); /* VOID tiles (and no others) are exactly allowed at the lower left and right * edges of the map. If _settings_game.construction.freeform_edges is true, * the upper edges of the map are also VOID tiles. */ assert(IsInnerTile(tile) == (type != MP_VOID)); - SB(_m[tile].type, 4, 4, type); + SB(tile.type(), 4, 4, type); } /** @@ -147,7 +147,7 @@ static inline void SetTileType(TileIndex tile, TileType type) * @param type The type to check against * @return true If the type matches against the type of the tile */ -debug_inline static bool IsTileType(TileIndex tile, TileType type) +debug_inline static bool IsTileType(Tile tile, TileType type) { return GetTileType(tile) == type; } @@ -158,7 +158,7 @@ debug_inline static bool IsTileType(TileIndex tile, TileType type) * @param tile The tile to check * @return True if the tile is on the map and not one of MP_VOID. */ -static inline bool IsValidTile(TileIndex tile) +static inline bool IsValidTile(Tile tile) { return tile < Map::Size() && !IsTileType(tile, MP_VOID); } @@ -175,13 +175,13 @@ static inline bool IsValidTile(TileIndex tile) * @pre IsValidTile(tile) * @pre The type of the tile must not be MP_HOUSE and MP_INDUSTRY */ -static inline Owner GetTileOwner(TileIndex tile) +static inline Owner GetTileOwner(Tile tile) { assert(IsValidTile(tile)); assert(!IsTileType(tile, MP_HOUSE)); assert(!IsTileType(tile, MP_INDUSTRY)); - return (Owner)GB(_m[tile].m1, 0, 5); + return (Owner)GB(tile.m1(), 0, 5); } /** @@ -195,13 +195,13 @@ static inline Owner GetTileOwner(TileIndex tile) * @pre IsValidTile(tile) * @pre The type of the tile must not be MP_HOUSE and MP_INDUSTRY */ -static inline void SetTileOwner(TileIndex tile, Owner owner) +static inline void SetTileOwner(Tile tile, Owner owner) { assert(IsValidTile(tile)); assert(!IsTileType(tile, MP_HOUSE)); assert(!IsTileType(tile, MP_INDUSTRY)); - SB(_m[tile].m1, 0, 5, owner); + SB(tile.m1(), 0, 5, owner); } /** @@ -211,7 +211,7 @@ static inline void SetTileOwner(TileIndex tile, Owner owner) * @param owner The owner to check against * @return True if a tile belongs the the given owner */ -static inline bool IsTileOwner(TileIndex tile, Owner owner) +static inline bool IsTileOwner(Tile tile, Owner owner) { return GetTileOwner(tile) == owner; } @@ -222,11 +222,11 @@ static inline bool IsTileOwner(TileIndex tile, Owner owner) * @param type the new type * @pre tile < Map::Size() */ -static inline void SetTropicZone(TileIndex tile, TropicZone type) +static inline void SetTropicZone(Tile tile, TropicZone type) { assert(tile < Map::Size()); assert(!IsTileType(tile, MP_VOID) || type == TROPICZONE_NORMAL); - SB(_m[tile].type, 0, 2, type); + SB(tile.type(), 0, 2, type); } /** @@ -235,10 +235,10 @@ static inline void SetTropicZone(TileIndex tile, TropicZone type) * @pre tile < Map::Size() * @return the zone type */ -static inline TropicZone GetTropicZone(TileIndex tile) +static inline TropicZone GetTropicZone(Tile tile) { assert(tile < Map::Size()); - return (TropicZone)GB(_m[tile].type, 0, 2); + return (TropicZone)GB(tile.type(), 0, 2); } /** @@ -247,10 +247,10 @@ static inline TropicZone GetTropicZone(TileIndex tile) * @pre IsTileType(t, MP_HOUSE) || IsTileType(t, MP_OBJECT) || IsTileType(t, MP_INDUSTRY) ||IsTileType(t, MP_STATION) * @return frame number */ -static inline byte GetAnimationFrame(TileIndex t) +static inline byte GetAnimationFrame(Tile t) { assert(IsTileType(t, MP_HOUSE) || IsTileType(t, MP_OBJECT) || IsTileType(t, MP_INDUSTRY) ||IsTileType(t, MP_STATION)); - return _me[t].m7; + return t.m7(); } /** @@ -259,10 +259,10 @@ static inline byte GetAnimationFrame(TileIndex t) * @param frame the new frame number * @pre IsTileType(t, MP_HOUSE) || IsTileType(t, MP_OBJECT) || IsTileType(t, MP_INDUSTRY) ||IsTileType(t, MP_STATION) */ -static inline void SetAnimationFrame(TileIndex t, byte frame) +static inline void SetAnimationFrame(Tile t, byte frame) { assert(IsTileType(t, MP_HOUSE) || IsTileType(t, MP_OBJECT) || IsTileType(t, MP_INDUSTRY) ||IsTileType(t, MP_STATION)); - _me[t].m7 = frame; + t.m7() = frame; } Slope GetTileSlope(TileIndex tile, int *h = nullptr); diff --git a/src/town_map.h b/src/town_map.h index 248b1bd323..42a2b5d049 100644 --- a/src/town_map.h +++ b/src/town_map.h @@ -19,10 +19,10 @@ * @pre IsTileType(t, MP_HOUSE) or IsTileType(t, MP_ROAD) but not a road depot * @return TownID */ -static inline TownID GetTownIndex(TileIndex t) +static inline TownID GetTownIndex(Tile t) { assert(IsTileType(t, MP_HOUSE) || (IsTileType(t, MP_ROAD) && !IsRoadDepot(t))); - return _m[t].m2; + return t.m2(); } /** @@ -31,10 +31,10 @@ static inline TownID GetTownIndex(TileIndex t) * @param index the index of the town * @pre IsTileType(t, MP_HOUSE) or IsTileType(t, MP_ROAD) but not a road depot */ -static inline void SetTownIndex(TileIndex t, TownID index) +static inline void SetTownIndex(Tile t, TownID index) { assert(IsTileType(t, MP_HOUSE) || (IsTileType(t, MP_ROAD) && !IsRoadDepot(t))); - _m[t].m2 = index; + t.m2() = index; } /** @@ -44,10 +44,10 @@ static inline void SetTownIndex(TileIndex t, TownID index) * @pre IsTileType(t, MP_HOUSE) * @return house type */ -static inline HouseID GetCleanHouseType(TileIndex t) +static inline HouseID GetCleanHouseType(Tile t) { assert(IsTileType(t, MP_HOUSE)); - return _m[t].m4 | (GB(_m[t].m3, 6, 1) << 8); + return t.m4() | (GB(t.m3(), 6, 1) << 8); } /** @@ -56,7 +56,7 @@ static inline HouseID GetCleanHouseType(TileIndex t) * @pre IsTileType(t, MP_HOUSE) * @return house type */ -static inline HouseID GetHouseType(TileIndex t) +static inline HouseID GetHouseType(Tile t) { return GetTranslatedHouseID(GetCleanHouseType(t)); } @@ -67,11 +67,11 @@ static inline HouseID GetHouseType(TileIndex t) * @param house_id the new house type * @pre IsTileType(t, MP_HOUSE) */ -static inline void SetHouseType(TileIndex t, HouseID house_id) +static inline void SetHouseType(Tile t, HouseID house_id) { assert(IsTileType(t, MP_HOUSE)); - _m[t].m4 = GB(house_id, 0, 8); - SB(_m[t].m3, 6, 1, GB(house_id, 8, 1)); + t.m4() = GB(house_id, 0, 8); + SB(t.m3(), 6, 1, GB(house_id, 8, 1)); } /** @@ -79,9 +79,9 @@ static inline void SetHouseType(TileIndex t, HouseID house_id) * @param t the tile * @return has destination */ -static inline bool LiftHasDestination(TileIndex t) +static inline bool LiftHasDestination(Tile t) { - return HasBit(_me[t].m7, 0); + return HasBit(t.m7(), 0); } /** @@ -90,10 +90,10 @@ static inline bool LiftHasDestination(TileIndex t) * @param t the tile * @param dest new destination */ -static inline void SetLiftDestination(TileIndex t, byte dest) +static inline void SetLiftDestination(Tile t, byte dest) { - SetBit(_me[t].m7, 0); - SB(_me[t].m7, 1, 3, dest); + SetBit(t.m7(), 0); + SB(t.m7(), 1, 3, dest); } /** @@ -101,9 +101,9 @@ static inline void SetLiftDestination(TileIndex t, byte dest) * @param t the tile * @return destination */ -static inline byte GetLiftDestination(TileIndex t) +static inline byte GetLiftDestination(Tile t) { - return GB(_me[t].m7, 1, 3); + return GB(t.m7(), 1, 3); } /** @@ -112,9 +112,9 @@ static inline byte GetLiftDestination(TileIndex t) * and the destination. * @param t the tile */ -static inline void HaltLift(TileIndex t) +static inline void HaltLift(Tile t) { - SB(_me[t].m7, 0, 4, 0); + SB(t.m7(), 0, 4, 0); } /** @@ -122,9 +122,9 @@ static inline void HaltLift(TileIndex t) * @param t the tile * @return position, from 0 to 36 */ -static inline byte GetLiftPosition(TileIndex t) +static inline byte GetLiftPosition(Tile t) { - return GB(_me[t].m6, 2, 6); + return GB(t.m6(), 2, 6); } /** @@ -132,9 +132,9 @@ static inline byte GetLiftPosition(TileIndex t) * @param t the tile * @param pos position, from 0 to 36 */ -static inline void SetLiftPosition(TileIndex t, byte pos) +static inline void SetLiftPosition(Tile t, byte pos) { - SB(_me[t].m6, 2, 6, pos); + SB(t.m6(), 2, 6, pos); } /** @@ -142,10 +142,10 @@ static inline void SetLiftPosition(TileIndex t, byte pos) * @param t the tile * @return true if it is, false if it is not */ -static inline bool IsHouseCompleted(TileIndex t) +static inline bool IsHouseCompleted(Tile t) { assert(IsTileType(t, MP_HOUSE)); - return HasBit(_m[t].m3, 7); + return HasBit(t.m3(), 7); } /** @@ -153,10 +153,10 @@ static inline bool IsHouseCompleted(TileIndex t) * @param t the tile * @param status */ -static inline void SetHouseCompleted(TileIndex t, bool status) +static inline void SetHouseCompleted(Tile t, bool status) { assert(IsTileType(t, MP_HOUSE)); - SB(_m[t].m3, 7, 1, !!status); + SB(t.m3(), 7, 1, !!status); } /** @@ -180,10 +180,10 @@ static inline void SetHouseCompleted(TileIndex t, bool status) * @pre IsTileType(t, MP_HOUSE) * @return the building stage of the house */ -static inline byte GetHouseBuildingStage(TileIndex t) +static inline byte GetHouseBuildingStage(Tile t) { assert(IsTileType(t, MP_HOUSE)); - return IsHouseCompleted(t) ? (byte)TOWN_HOUSE_COMPLETED : GB(_m[t].m5, 3, 2); + return IsHouseCompleted(t) ? (byte)TOWN_HOUSE_COMPLETED : GB(t.m5(), 3, 2); } /** @@ -192,10 +192,10 @@ static inline byte GetHouseBuildingStage(TileIndex t) * @pre IsTileType(t, MP_HOUSE) * @return the construction stage of the house */ -static inline byte GetHouseConstructionTick(TileIndex t) +static inline byte GetHouseConstructionTick(Tile t) { assert(IsTileType(t, MP_HOUSE)); - return IsHouseCompleted(t) ? 0 : GB(_m[t].m5, 0, 3); + return IsHouseCompleted(t) ? 0 : GB(t.m5(), 0, 3); } /** @@ -205,12 +205,12 @@ static inline byte GetHouseConstructionTick(TileIndex t) * @param t the tile of the house to increment the construction stage of * @pre IsTileType(t, MP_HOUSE) */ -static inline void IncHouseConstructionTick(TileIndex t) +static inline void IncHouseConstructionTick(Tile t) { assert(IsTileType(t, MP_HOUSE)); - AB(_m[t].m5, 0, 5, 1); + AB(t.m5(), 0, 5, 1); - if (GB(_m[t].m5, 3, 2) == TOWN_HOUSE_COMPLETED) { + if (GB(t.m5(), 3, 2) == TOWN_HOUSE_COMPLETED) { /* House is now completed. * Store the year of construction as well, for newgrf house purpose */ SetHouseCompleted(t, true); @@ -223,10 +223,10 @@ static inline void IncHouseConstructionTick(TileIndex t) * @param t the tile of this house * @pre IsTileType(t, MP_HOUSE) && IsHouseCompleted(t) */ -static inline void ResetHouseAge(TileIndex t) +static inline void ResetHouseAge(Tile t) { assert(IsTileType(t, MP_HOUSE) && IsHouseCompleted(t)); - _m[t].m5 = 0; + t.m5() = 0; } /** @@ -234,10 +234,10 @@ static inline void ResetHouseAge(TileIndex t) * @param t the tile of this house * @pre IsTileType(t, MP_HOUSE) */ -static inline void IncrementHouseAge(TileIndex t) +static inline void IncrementHouseAge(Tile t) { assert(IsTileType(t, MP_HOUSE)); - if (IsHouseCompleted(t) && _m[t].m5 < 0xFF) _m[t].m5++; + if (IsHouseCompleted(t) && t.m5() < 0xFF) t.m5()++; } /** @@ -246,10 +246,10 @@ static inline void IncrementHouseAge(TileIndex t) * @pre IsTileType(t, MP_HOUSE) * @return year */ -static inline Year GetHouseAge(TileIndex t) +static inline Year GetHouseAge(Tile t) { assert(IsTileType(t, MP_HOUSE)); - return IsHouseCompleted(t) ? _m[t].m5 : 0; + return IsHouseCompleted(t) ? t.m5() : 0; } /** @@ -259,10 +259,10 @@ static inline Year GetHouseAge(TileIndex t) * @param random the new random bits * @pre IsTileType(t, MP_HOUSE) */ -static inline void SetHouseRandomBits(TileIndex t, byte random) +static inline void SetHouseRandomBits(Tile t, byte random) { assert(IsTileType(t, MP_HOUSE)); - _m[t].m1 = random; + t.m1() = random; } /** @@ -272,10 +272,10 @@ static inline void SetHouseRandomBits(TileIndex t, byte random) * @pre IsTileType(t, MP_HOUSE) * @return random bits */ -static inline byte GetHouseRandomBits(TileIndex t) +static inline byte GetHouseRandomBits(Tile t) { assert(IsTileType(t, MP_HOUSE)); - return _m[t].m1; + return t.m1(); } /** @@ -285,10 +285,10 @@ static inline byte GetHouseRandomBits(TileIndex t) * @param triggers the activated triggers * @pre IsTileType(t, MP_HOUSE) */ -static inline void SetHouseTriggers(TileIndex t, byte triggers) +static inline void SetHouseTriggers(Tile t, byte triggers) { assert(IsTileType(t, MP_HOUSE)); - SB(_m[t].m3, 0, 5, triggers); + SB(t.m3(), 0, 5, triggers); } /** @@ -298,10 +298,10 @@ static inline void SetHouseTriggers(TileIndex t, byte triggers) * @pre IsTileType(t, MP_HOUSE) * @return triggers */ -static inline byte GetHouseTriggers(TileIndex t) +static inline byte GetHouseTriggers(Tile t) { assert(IsTileType(t, MP_HOUSE)); - return GB(_m[t].m3, 0, 5); + return GB(t.m3(), 0, 5); } /** @@ -310,10 +310,10 @@ static inline byte GetHouseTriggers(TileIndex t) * @pre IsTileType(t, MP_HOUSE) * @return time remaining */ -static inline byte GetHouseProcessingTime(TileIndex t) +static inline byte GetHouseProcessingTime(Tile t) { assert(IsTileType(t, MP_HOUSE)); - return GB(_me[t].m6, 2, 6); + return GB(t.m6(), 2, 6); } /** @@ -322,10 +322,10 @@ static inline byte GetHouseProcessingTime(TileIndex t) * @param time the time to be set * @pre IsTileType(t, MP_HOUSE) */ -static inline void SetHouseProcessingTime(TileIndex t, byte time) +static inline void SetHouseProcessingTime(Tile t, byte time) { assert(IsTileType(t, MP_HOUSE)); - SB(_me[t].m6, 2, 6, time); + SB(t.m6(), 2, 6, time); } /** @@ -333,10 +333,10 @@ static inline void SetHouseProcessingTime(TileIndex t, byte time) * @param t the house tile * @pre IsTileType(t, MP_HOUSE) */ -static inline void DecHouseProcessingTime(TileIndex t) +static inline void DecHouseProcessingTime(Tile t) { assert(IsTileType(t, MP_HOUSE)); - _me[t].m6 -= 1 << 2; + t.m6() -= 1 << 2; } /** @@ -349,17 +349,17 @@ static inline void DecHouseProcessingTime(TileIndex t) * @param random_bits required for newgrf houses * @pre IsTileType(t, MP_CLEAR) */ -static inline void MakeHouseTile(TileIndex t, TownID tid, byte counter, byte stage, HouseID type, byte random_bits) +static inline void MakeHouseTile(Tile t, TownID tid, byte counter, byte stage, HouseID type, byte random_bits) { assert(IsTileType(t, MP_CLEAR)); SetTileType(t, MP_HOUSE); - _m[t].m1 = random_bits; - _m[t].m2 = tid; - _m[t].m3 = 0; + t.m1() = random_bits; + t.m2() = tid; + t.m3() = 0; SetHouseType(t, type); SetHouseCompleted(t, stage == TOWN_HOUSE_COMPLETED); - _m[t].m5 = IsHouseCompleted(t) ? 0 : (stage << 3 | counter); + t.m5() = IsHouseCompleted(t) ? 0 : (stage << 3 | counter); SetAnimationFrame(t, 0); SetHouseProcessingTime(t, HouseSpec::Get(type)->processing_time); } diff --git a/src/tree_map.h b/src/tree_map.h index a01968f08f..9bed461a51 100644 --- a/src/tree_map.h +++ b/src/tree_map.h @@ -70,10 +70,10 @@ enum TreeGround { * @return The treetype of the given tile with trees * @pre Tile t must be of type MP_TREES */ -static inline TreeType GetTreeType(TileIndex t) +static inline TreeType GetTreeType(Tile t) { assert(IsTileType(t, MP_TREES)); - return (TreeType)_m[t].m3; + return (TreeType)t.m3(); } /** @@ -85,10 +85,10 @@ static inline TreeType GetTreeType(TileIndex t) * @return The groundtype of the tile * @pre Tile must be of type MP_TREES */ -static inline TreeGround GetTreeGround(TileIndex t) +static inline TreeGround GetTreeGround(Tile t) { assert(IsTileType(t, MP_TREES)); - return (TreeGround)GB(_m[t].m2, 6, 3); + return (TreeGround)GB(t.m2(), 6, 3); } /** @@ -110,10 +110,10 @@ static inline TreeGround GetTreeGround(TileIndex t) * @pre Tile must be of type MP_TREES * @see GetTreeCount */ -static inline uint GetTreeDensity(TileIndex t) +static inline uint GetTreeDensity(Tile t) { assert(IsTileType(t, MP_TREES)); - return GB(_m[t].m2, 4, 2); + return GB(t.m2(), 4, 2); } /** @@ -127,11 +127,11 @@ static inline uint GetTreeDensity(TileIndex t) * @param d The density to save with * @pre Tile must be of type MP_TREES */ -static inline void SetTreeGroundDensity(TileIndex t, TreeGround g, uint d) +static inline void SetTreeGroundDensity(Tile t, TreeGround g, uint d) { assert(IsTileType(t, MP_TREES)); // XXX incomplete - SB(_m[t].m2, 4, 2, d); - SB(_m[t].m2, 6, 3, g); + SB(t.m2(), 4, 2, d); + SB(t.m2(), 6, 3, g); SetWaterClass(t, g == TREE_GROUND_SHORE ? WATER_CLASS_SEA : WATER_CLASS_INVALID); } @@ -146,10 +146,10 @@ static inline void SetTreeGroundDensity(TileIndex t, TreeGround g, uint d) * @return The number of trees (1-4) * @pre Tile must be of type MP_TREES */ -static inline uint GetTreeCount(TileIndex t) +static inline uint GetTreeCount(Tile t) { assert(IsTileType(t, MP_TREES)); - return GB(_m[t].m5, 6, 2) + 1; + return GB(t.m5(), 6, 2) + 1; } /** @@ -163,10 +163,10 @@ static inline uint GetTreeCount(TileIndex t) * @param c The value to add (or reduce) on the tree-count value * @pre Tile must be of type MP_TREES */ -static inline void AddTreeCount(TileIndex t, int c) +static inline void AddTreeCount(Tile t, int c) { assert(IsTileType(t, MP_TREES)); // XXX incomplete - _m[t].m5 += c << 6; + t.m5() += c << 6; } /** @@ -178,10 +178,10 @@ static inline void AddTreeCount(TileIndex t, int c) * @return The tree growth status * @pre Tile must be of type MP_TREES */ -static inline uint GetTreeGrowth(TileIndex t) +static inline uint GetTreeGrowth(Tile t) { assert(IsTileType(t, MP_TREES)); - return GB(_m[t].m5, 0, 3); + return GB(t.m5(), 0, 3); } /** @@ -193,10 +193,10 @@ static inline uint GetTreeGrowth(TileIndex t) * @param a The value to add on the tree growth status * @pre Tile must be of type MP_TREES */ -static inline void AddTreeGrowth(TileIndex t, int a) +static inline void AddTreeGrowth(Tile t, int a) { assert(IsTileType(t, MP_TREES)); // XXX incomplete - _m[t].m5 += a; + t.m5() += a; } /** @@ -209,10 +209,10 @@ static inline void AddTreeGrowth(TileIndex t, int a) * @param g The new value * @pre Tile must be of type MP_TREES */ -static inline void SetTreeGrowth(TileIndex t, uint g) +static inline void SetTreeGrowth(Tile t, uint g) { assert(IsTileType(t, MP_TREES)); // XXX incomplete - SB(_m[t].m5, 0, 3, g); + SB(t.m5(), 0, 3, g); } /** @@ -227,17 +227,17 @@ static inline void SetTreeGrowth(TileIndex t, uint g) * @param ground the ground type * @param density the density (not the number of trees) */ -static inline void MakeTree(TileIndex t, TreeType type, uint count, uint growth, TreeGround ground, uint density) +static inline void MakeTree(Tile t, TreeType type, uint count, uint growth, TreeGround ground, uint density) { SetTileType(t, MP_TREES); SetTileOwner(t, OWNER_NONE); SetWaterClass(t, ground == TREE_GROUND_SHORE ? WATER_CLASS_SEA : WATER_CLASS_INVALID); - _m[t].m2 = ground << 6 | density << 4 | 0; - _m[t].m3 = type; - _m[t].m4 = 0 << 5 | 0 << 2; - _m[t].m5 = count << 6 | growth; - SB(_me[t].m6, 2, 4, 0); - _me[t].m7 = 0; + t.m2() = ground << 6 | density << 4 | 0; + t.m3() = type; + t.m4() = 0 << 5 | 0 << 2; + t.m5() = count << 6 | growth; + SB(t.m6(), 2, 4, 0); + t.m7() = 0; } #endif /* TREE_MAP_H */ diff --git a/src/tunnel_map.h b/src/tunnel_map.h index c347626d55..757d988ac7 100644 --- a/src/tunnel_map.h +++ b/src/tunnel_map.h @@ -20,10 +20,10 @@ * @pre IsTileType(t, MP_TUNNELBRIDGE) * @return true if and only if this tile is a tunnel (entrance) */ -static inline bool IsTunnel(TileIndex t) +static inline bool IsTunnel(Tile t) { assert(IsTileType(t, MP_TUNNELBRIDGE)); - return !HasBit(_m[t].m5, 7); + return !HasBit(t.m5(), 7); } /** @@ -31,7 +31,7 @@ static inline bool IsTunnel(TileIndex t) * @param t the tile that might be a tunnel * @return true if and only if this tile is a tunnel (entrance) */ -static inline bool IsTunnelTile(TileIndex t) +static inline bool IsTunnelTile(Tile t) { return IsTileType(t, MP_TUNNELBRIDGE) && IsTunnel(t); } @@ -47,17 +47,17 @@ bool IsTunnelInWayDir(TileIndex tile, int z, DiagDirection dir); * @param d the direction facing out of the tunnel * @param r the road type used in the tunnel */ -static inline void MakeRoadTunnel(TileIndex t, Owner o, DiagDirection d, RoadType road_rt, RoadType tram_rt) +static inline void MakeRoadTunnel(Tile t, Owner o, DiagDirection d, RoadType road_rt, RoadType tram_rt) { SetTileType(t, MP_TUNNELBRIDGE); SetTileOwner(t, o); - _m[t].m2 = 0; - _m[t].m3 = 0; - _m[t].m4 = 0; - _m[t].m5 = TRANSPORT_ROAD << 2 | d; - SB(_me[t].m6, 2, 4, 0); - _me[t].m7 = 0; - _me[t].m8 = 0; + t.m2() = 0; + t.m3() = 0; + t.m4() = 0; + t.m5() = TRANSPORT_ROAD << 2 | d; + SB(t.m6(), 2, 4, 0); + t.m7() = 0; + t.m8() = 0; SetRoadOwner(t, RTT_ROAD, o); if (o != OWNER_TOWN) SetRoadOwner(t, RTT_TRAM, o); SetRoadTypes(t, road_rt, tram_rt); @@ -70,17 +70,17 @@ static inline void MakeRoadTunnel(TileIndex t, Owner o, DiagDirection d, RoadTyp * @param d the direction facing out of the tunnel * @param r the rail type used in the tunnel */ -static inline void MakeRailTunnel(TileIndex t, Owner o, DiagDirection d, RailType r) +static inline void MakeRailTunnel(Tile t, Owner o, DiagDirection d, RailType r) { SetTileType(t, MP_TUNNELBRIDGE); SetTileOwner(t, o); - _m[t].m2 = 0; - _m[t].m3 = 0; - _m[t].m4 = 0; - _m[t].m5 = TRANSPORT_RAIL << 2 | d; - SB(_me[t].m6, 2, 4, 0); - _me[t].m7 = 0; - _me[t].m8 = 0; + t.m2() = 0; + t.m3() = 0; + t.m4() = 0; + t.m5() = TRANSPORT_RAIL << 2 | d; + SB(t.m6(), 2, 4, 0); + t.m7() = 0; + t.m8() = 0; SetRailType(t, r); SetRoadTypes(t, INVALID_ROADTYPE, INVALID_ROADTYPE); } diff --git a/src/tunnelbridge_map.h b/src/tunnelbridge_map.h index 62d3c14b2d..3f1779ffa6 100644 --- a/src/tunnelbridge_map.h +++ b/src/tunnelbridge_map.h @@ -23,10 +23,10 @@ * @pre IsTileType(t, MP_TUNNELBRIDGE) * @return the above mentioned direction */ -static inline DiagDirection GetTunnelBridgeDirection(TileIndex t) +static inline DiagDirection GetTunnelBridgeDirection(Tile t) { assert(IsTileType(t, MP_TUNNELBRIDGE)); - return (DiagDirection)GB(_m[t].m5, 0, 2); + return (DiagDirection)GB(t.m5(), 0, 2); } /** @@ -36,10 +36,10 @@ static inline DiagDirection GetTunnelBridgeDirection(TileIndex t) * @pre IsTileType(t, MP_TUNNELBRIDGE) * @return the transport type in the tunnel/bridge */ -static inline TransportType GetTunnelBridgeTransportType(TileIndex t) +static inline TransportType GetTunnelBridgeTransportType(Tile t) { assert(IsTileType(t, MP_TUNNELBRIDGE)); - return (TransportType)GB(_m[t].m5, 2, 2); + return (TransportType)GB(t.m5(), 2, 2); } /** @@ -49,10 +49,10 @@ static inline TransportType GetTunnelBridgeTransportType(TileIndex t) * @pre IsTileType(t, MP_TUNNELBRIDGE) * @return true if and only if the tile is in a snowy/desert area */ -static inline bool HasTunnelBridgeSnowOrDesert(TileIndex t) +static inline bool HasTunnelBridgeSnowOrDesert(Tile t) { assert(IsTileType(t, MP_TUNNELBRIDGE)); - return HasBit(_me[t].m7, 5); + return HasBit(t.m7(), 5); } /** @@ -63,10 +63,10 @@ static inline bool HasTunnelBridgeSnowOrDesert(TileIndex t) * not in snow and not in desert false * @pre IsTileType(t, MP_TUNNELBRIDGE) */ -static inline void SetTunnelBridgeSnowOrDesert(TileIndex t, bool snow_or_desert) +static inline void SetTunnelBridgeSnowOrDesert(Tile t, bool snow_or_desert) { assert(IsTileType(t, MP_TUNNELBRIDGE)); - SB(_me[t].m7, 5, 1, snow_or_desert); + SB(t.m7(), 5, 1, snow_or_desert); } /** @@ -75,7 +75,7 @@ static inline void SetTunnelBridgeSnowOrDesert(TileIndex t, bool snow_or_desert) * @pre IsTileType(t, MP_TUNNELBRIDGE) * @return other end */ -static inline TileIndex GetOtherTunnelBridgeEnd(TileIndex t) +static inline TileIndex GetOtherTunnelBridgeEnd(Tile t) { assert(IsTileType(t, MP_TUNNELBRIDGE)); return IsTunnel(t) ? GetOtherTunnelEnd(t) : GetOtherBridgeEnd(t); @@ -88,11 +88,11 @@ static inline TileIndex GetOtherTunnelBridgeEnd(TileIndex t) * @param t the tile * @return reservation state */ -static inline bool HasTunnelBridgeReservation(TileIndex t) +static inline bool HasTunnelBridgeReservation(Tile t) { assert(IsTileType(t, MP_TUNNELBRIDGE)); assert(GetTunnelBridgeTransportType(t) == TRANSPORT_RAIL); - return HasBit(_m[t].m5, 4); + return HasBit(t.m5(), 4); } /** @@ -101,11 +101,11 @@ static inline bool HasTunnelBridgeReservation(TileIndex t) * @param t the tile * @param b the reservation state */ -static inline void SetTunnelBridgeReservation(TileIndex t, bool b) +static inline void SetTunnelBridgeReservation(Tile t, bool b) { assert(IsTileType(t, MP_TUNNELBRIDGE)); assert(GetTunnelBridgeTransportType(t) == TRANSPORT_RAIL); - SB(_m[t].m5, 4, 1, b ? 1 : 0); + SB(t.m5(), 4, 1, b ? 1 : 0); } /** @@ -114,7 +114,7 @@ static inline void SetTunnelBridgeReservation(TileIndex t, bool b) * @param t the tile * @return reserved track bits */ -static inline TrackBits GetTunnelBridgeReservationTrackBits(TileIndex t) +static inline TrackBits GetTunnelBridgeReservationTrackBits(Tile t) { return HasTunnelBridgeReservation(t) ? DiagDirToDiagTrackBits(GetTunnelBridgeDirection(t)) : TRACK_BIT_NONE; } diff --git a/src/void_map.h b/src/void_map.h index 415e0a5803..de658585c1 100644 --- a/src/void_map.h +++ b/src/void_map.h @@ -16,17 +16,17 @@ * Make a nice void tile ;) * @param t the tile to make void */ -static inline void MakeVoid(TileIndex t) +static inline void MakeVoid(Tile t) { SetTileType(t, MP_VOID); SetTileHeight(t, 0); - _m[t].m1 = 0; - _m[t].m2 = 0; - _m[t].m3 = 0; - _m[t].m4 = 0; - _m[t].m5 = 0; - _me[t].m6 = 0; - _me[t].m7 = 0; + t.m1() = 0; + t.m2() = 0; + t.m3() = 0; + t.m4() = 0; + t.m5() = 0; + t.m6() = 0; + t.m7() = 0; } #endif /* VOID_MAP_H */ diff --git a/src/water_cmd.cpp b/src/water_cmd.cpp index 6db1c62e06..759afbdc4f 100644 --- a/src/water_cmd.cpp +++ b/src/water_cmd.cpp @@ -158,7 +158,7 @@ CommandCost CmdBuildShipDepot(DoCommandFlag flags, TileIndex tile, Axis axis) return cost; } -bool IsPossibleDockingTile(TileIndex t) +bool IsPossibleDockingTile(Tile t) { assert(IsValidTile(t)); switch (GetTileType(t)) { diff --git a/src/water_map.h b/src/water_map.h index ff2c38a7bc..97808d176b 100644 --- a/src/water_map.h +++ b/src/water_map.h @@ -78,19 +78,19 @@ enum LockPart { LOCK_PART_UPPER = 2, ///< Upper part of a lock. }; -bool IsPossibleDockingTile(TileIndex t); +bool IsPossibleDockingTile(Tile t); /** * Get the water tile type at a tile. * @param t Water tile to query. * @return Water tile type at the tile. */ -static inline WaterTileType GetWaterTileType(TileIndex t) +static inline WaterTileType GetWaterTileType(Tile t) { assert(IsTileType(t, MP_WATER)); - switch (GB(_m[t].m5, WBL_TYPE_BEGIN, WBL_TYPE_COUNT)) { - case WBL_TYPE_NORMAL: return HasBit(_m[t].m5, WBL_COAST_FLAG) ? WATER_TILE_COAST : WATER_TILE_CLEAR; + switch (GB(t.m5(), WBL_TYPE_BEGIN, WBL_TYPE_COUNT)) { + case WBL_TYPE_NORMAL: return HasBit(t.m5(), WBL_COAST_FLAG) ? WATER_TILE_COAST : WATER_TILE_CLEAR; case WBL_TYPE_LOCK: return WATER_TILE_LOCK; case WBL_TYPE_DEPOT: return WATER_TILE_DEPOT; default: NOT_REACHED(); @@ -103,7 +103,7 @@ static inline WaterTileType GetWaterTileType(TileIndex t) * @param t Tile to query. * @return True if the tiletype has a waterclass. */ -static inline bool HasTileWaterClass(TileIndex t) +static inline bool HasTileWaterClass(Tile t) { return IsTileType(t, MP_WATER) || IsTileType(t, MP_STATION) || IsTileType(t, MP_INDUSTRY) || IsTileType(t, MP_OBJECT) || IsTileType(t, MP_TREES); } @@ -114,10 +114,10 @@ static inline bool HasTileWaterClass(TileIndex t) * @pre IsTileType(t, MP_WATER) || IsTileType(t, MP_STATION) || IsTileType(t, MP_INDUSTRY) || IsTileType(t, MP_OBJECT) * @return Water class at the tile. */ -static inline WaterClass GetWaterClass(TileIndex t) +static inline WaterClass GetWaterClass(Tile t) { assert(HasTileWaterClass(t)); - return (WaterClass)GB(_m[t].m1, 5, 2); + return (WaterClass)GB(t.m1(), 5, 2); } /** @@ -126,10 +126,10 @@ static inline WaterClass GetWaterClass(TileIndex t) * @param wc New water class. * @pre IsTileType(t, MP_WATER) || IsTileType(t, MP_STATION) || IsTileType(t, MP_INDUSTRY) || IsTileType(t, MP_OBJECT) */ -static inline void SetWaterClass(TileIndex t, WaterClass wc) +static inline void SetWaterClass(Tile t, WaterClass wc) { assert(HasTileWaterClass(t)); - SB(_m[t].m1, 5, 2, wc); + SB(t.m1(), 5, 2, wc); } /** @@ -138,7 +138,7 @@ static inline void SetWaterClass(TileIndex t, WaterClass wc) * @pre IsTileType(t, MP_WATER) || IsTileType(t, MP_STATION) || IsTileType(t, MP_INDUSTRY) || IsTileType(t, MP_OBJECT) * @return true iff on water */ -static inline bool IsTileOnWater(TileIndex t) +static inline bool IsTileOnWater(Tile t) { return (GetWaterClass(t) != WATER_CLASS_INVALID); } @@ -149,7 +149,7 @@ static inline bool IsTileOnWater(TileIndex t) * @return \c true if any type of clear water like ocean, river, or canal. * @pre IsTileType(t, MP_WATER) */ -static inline bool IsWater(TileIndex t) +static inline bool IsWater(Tile t) { return GetWaterTileType(t) == WATER_TILE_CLEAR; } @@ -160,7 +160,7 @@ static inline bool IsWater(TileIndex t) * @return \c true if it is a sea water tile. * @pre IsTileType(t, MP_WATER) */ -static inline bool IsSea(TileIndex t) +static inline bool IsSea(Tile t) { return IsWater(t) && GetWaterClass(t) == WATER_CLASS_SEA; } @@ -171,7 +171,7 @@ static inline bool IsSea(TileIndex t) * @return \c true if it is a canal tile. * @pre IsTileType(t, MP_WATER) */ -static inline bool IsCanal(TileIndex t) +static inline bool IsCanal(Tile t) { return IsWater(t) && GetWaterClass(t) == WATER_CLASS_CANAL; } @@ -182,7 +182,7 @@ static inline bool IsCanal(TileIndex t) * @return \c true if it is a river water tile. * @pre IsTileType(t, MP_WATER) */ -static inline bool IsRiver(TileIndex t) +static inline bool IsRiver(Tile t) { return IsWater(t) && GetWaterClass(t) == WATER_CLASS_RIVER; } @@ -192,7 +192,7 @@ static inline bool IsRiver(TileIndex t) * @param t Tile to query. * @return \c true if it is a plain water tile. */ -static inline bool IsWaterTile(TileIndex t) +static inline bool IsWaterTile(Tile t) { return IsTileType(t, MP_WATER) && IsWater(t); } @@ -203,7 +203,7 @@ static inline bool IsWaterTile(TileIndex t) * @return \c true if it is a sea water tile. * @pre IsTileType(t, MP_WATER) */ -static inline bool IsCoast(TileIndex t) +static inline bool IsCoast(Tile t) { return GetWaterTileType(t) == WATER_TILE_COAST; } @@ -213,7 +213,7 @@ static inline bool IsCoast(TileIndex t) * @param t Tile to query. * @return \c true if it is a coast. */ -static inline bool IsCoastTile(TileIndex t) +static inline bool IsCoastTile(Tile t) { return (IsTileType(t, MP_WATER) && IsCoast(t)) || (IsTileType(t, MP_TREES) && GetWaterClass(t) != WATER_CLASS_INVALID); } @@ -224,7 +224,7 @@ static inline bool IsCoastTile(TileIndex t) * @return \c true if it is a ship depot tile. * @pre IsTileType(t, MP_WATER) */ -static inline bool IsShipDepot(TileIndex t) +static inline bool IsShipDepot(Tile t) { return GetWaterTileType(t) == WATER_TILE_DEPOT; } @@ -234,7 +234,7 @@ static inline bool IsShipDepot(TileIndex t) * @param t Tile to query. * @return \c true if it is a ship depot tile. */ -static inline bool IsShipDepotTile(TileIndex t) +static inline bool IsShipDepotTile(Tile t) { return IsTileType(t, MP_WATER) && IsShipDepot(t); } @@ -245,10 +245,10 @@ static inline bool IsShipDepotTile(TileIndex t) * @return Axis of the depot. * @pre IsShipDepotTile(t) */ -static inline Axis GetShipDepotAxis(TileIndex t) +static inline Axis GetShipDepotAxis(Tile t) { assert(IsShipDepotTile(t)); - return (Axis)GB(_m[t].m5, WBL_DEPOT_AXIS, 1); + return (Axis)GB(t.m5(), WBL_DEPOT_AXIS, 1); } /** @@ -257,10 +257,10 @@ static inline Axis GetShipDepotAxis(TileIndex t) * @return Part of the depot. * @pre IsShipDepotTile(t) */ -static inline DepotPart GetShipDepotPart(TileIndex t) +static inline DepotPart GetShipDepotPart(Tile t) { assert(IsShipDepotTile(t)); - return (DepotPart)GB(_m[t].m5, WBL_DEPOT_PART, 1); + return (DepotPart)GB(t.m5(), WBL_DEPOT_PART, 1); } /** @@ -269,7 +269,7 @@ static inline DepotPart GetShipDepotPart(TileIndex t) * @return Direction of the depot. * @pre IsShipDepotTile(t) */ -static inline DiagDirection GetShipDepotDirection(TileIndex t) +static inline DiagDirection GetShipDepotDirection(Tile t) { return XYNSToDiagDir(GetShipDepotAxis(t), GetShipDepotPart(t)); } @@ -280,9 +280,9 @@ static inline DiagDirection GetShipDepotDirection(TileIndex t) * @return Tile containing the other section of the depot. * @pre IsShipDepotTile(t) */ -static inline TileIndex GetOtherShipDepotTile(TileIndex t) +static inline TileIndex GetOtherShipDepotTile(Tile t) { - return t + (GetShipDepotPart(t) != DEPOT_PART_NORTH ? -1 : 1) * (GetShipDepotAxis(t) != AXIS_X ? TileDiffXY(0, 1) : TileDiffXY(1, 0)); + return TileIndex(t) + (GetShipDepotPart(t) != DEPOT_PART_NORTH ? -1 : 1) * (GetShipDepotAxis(t) != AXIS_X ? TileDiffXY(0, 1) : TileDiffXY(1, 0)); } /** @@ -291,12 +291,12 @@ static inline TileIndex GetOtherShipDepotTile(TileIndex t) * @return The northern tile of the depot. * @pre IsShipDepotTile(t) */ -static inline TileIndex GetShipDepotNorthTile(TileIndex t) +static inline TileIndex GetShipDepotNorthTile(Tile t) { assert(IsShipDepot(t)); TileIndex tile2 = GetOtherShipDepotTile(t); - return t < tile2 ? t : tile2; + return t < tile2 ? TileIndex(t) : tile2; } /** @@ -305,7 +305,7 @@ static inline TileIndex GetShipDepotNorthTile(TileIndex t) * @return \c true if it is a water lock tile. * @pre IsTileType(t, MP_WATER) */ -static inline bool IsLock(TileIndex t) +static inline bool IsLock(Tile t) { return GetWaterTileType(t) == WATER_TILE_LOCK; } @@ -316,10 +316,10 @@ static inline bool IsLock(TileIndex t) * @return Direction of the lock. * @pre IsTileType(t, MP_WATER) && IsLock(t) */ -static inline DiagDirection GetLockDirection(TileIndex t) +static inline DiagDirection GetLockDirection(Tile t) { assert(IsLock(t)); - return (DiagDirection)GB(_m[t].m5, WBL_LOCK_ORIENT_BEGIN, WBL_LOCK_ORIENT_COUNT); + return (DiagDirection)GB(t.m5(), WBL_LOCK_ORIENT_BEGIN, WBL_LOCK_ORIENT_COUNT); } /** @@ -328,10 +328,10 @@ static inline DiagDirection GetLockDirection(TileIndex t) * @return The part. * @pre IsTileType(t, MP_WATER) && IsLock(t) */ -static inline byte GetLockPart(TileIndex t) +static inline byte GetLockPart(Tile t) { assert(IsLock(t)); - return GB(_m[t].m5, WBL_LOCK_PART_BEGIN, WBL_LOCK_PART_COUNT); + return GB(t.m5(), WBL_LOCK_PART_BEGIN, WBL_LOCK_PART_COUNT); } /** @@ -340,10 +340,10 @@ static inline byte GetLockPart(TileIndex t) * @return Random bits of the tile. * @pre IsTileType(t, MP_WATER) */ -static inline byte GetWaterTileRandomBits(TileIndex t) +static inline byte GetWaterTileRandomBits(Tile t) { assert(IsTileType(t, MP_WATER)); - return _m[t].m4; + return t.m4(); } /** @@ -352,7 +352,7 @@ static inline byte GetWaterTileRandomBits(TileIndex t) * @return true iff the tile has water at the ground. * @note Coast tiles are not considered waterish, even if there is water on a halftile. */ -static inline bool HasTileWaterGround(TileIndex t) +static inline bool HasTileWaterGround(Tile t) { return HasTileWaterClass(t) && IsTileOnWater(t) && !IsCoastTile(t); } @@ -363,19 +363,19 @@ static inline bool HasTileWaterGround(TileIndex t) * @param t the tile * @param b the docking tile state */ -static inline void SetDockingTile(TileIndex t, bool b) +static inline void SetDockingTile(Tile t, bool b) { assert(IsTileType(t, MP_WATER) || IsTileType(t, MP_RAILWAY) || IsTileType(t, MP_STATION) || IsTileType(t, MP_TUNNELBRIDGE)); - SB(_m[t].m1, 7, 1, b ? 1 : 0); + SB(t.m1(), 7, 1, b ? 1 : 0); } /** * Checks whether the tile is marked as a dockling tile. * @return true iff the tile is marked as a docking tile. */ -static inline bool IsDockingTile(TileIndex t) +static inline bool IsDockingTile(Tile t) { - return (IsTileType(t, MP_WATER) || IsTileType(t, MP_RAILWAY) || IsTileType(t, MP_STATION) || IsTileType(t, MP_TUNNELBRIDGE)) && HasBit(_m[t].m1, 7); + return (IsTileType(t, MP_WATER) || IsTileType(t, MP_RAILWAY) || IsTileType(t, MP_STATION) || IsTileType(t, MP_TUNNELBRIDGE)) && HasBit(t.m1(), 7); } @@ -383,18 +383,18 @@ static inline bool IsDockingTile(TileIndex t) * Helper function to make a coast tile. * @param t The tile to change into water */ -static inline void MakeShore(TileIndex t) +static inline void MakeShore(Tile t) { SetTileType(t, MP_WATER); SetTileOwner(t, OWNER_WATER); SetWaterClass(t, WATER_CLASS_SEA); SetDockingTile(t, false); - _m[t].m2 = 0; - _m[t].m3 = 0; - _m[t].m4 = 0; - _m[t].m5 = WBL_TYPE_NORMAL << WBL_TYPE_BEGIN | 1 << WBL_COAST_FLAG; - SB(_me[t].m6, 2, 4, 0); - _me[t].m7 = 0; + t.m2() = 0; + t.m3() = 0; + t.m4() = 0; + t.m5() = WBL_TYPE_NORMAL << WBL_TYPE_BEGIN | 1 << WBL_COAST_FLAG; + SB(t.m6(), 2, 4, 0); + t.m7() = 0; } /** @@ -404,25 +404,25 @@ static inline void MakeShore(TileIndex t) * @param wc The class of water the tile has to be * @param random_bits Eventual random bits to be set for this tile */ -static inline void MakeWater(TileIndex t, Owner o, WaterClass wc, uint8 random_bits) +static inline void MakeWater(Tile t, Owner o, WaterClass wc, uint8 random_bits) { SetTileType(t, MP_WATER); SetTileOwner(t, o); SetWaterClass(t, wc); SetDockingTile(t, false); - _m[t].m2 = 0; - _m[t].m3 = 0; - _m[t].m4 = random_bits; - _m[t].m5 = WBL_TYPE_NORMAL << WBL_TYPE_BEGIN; - SB(_me[t].m6, 2, 4, 0); - _me[t].m7 = 0; + t.m2() = 0; + t.m3() = 0; + t.m4() = random_bits; + t.m5() = WBL_TYPE_NORMAL << WBL_TYPE_BEGIN; + SB(t.m6(), 2, 4, 0); + t.m7() = 0; } /** * Make a sea tile. * @param t The tile to change into sea */ -static inline void MakeSea(TileIndex t) +static inline void MakeSea(Tile t) { MakeWater(t, OWNER_WATER, WATER_CLASS_SEA, 0); } @@ -432,7 +432,7 @@ static inline void MakeSea(TileIndex t) * @param t The tile to change into river * @param random_bits Random bits to be set for this tile */ -static inline void MakeRiver(TileIndex t, uint8 random_bits) +static inline void MakeRiver(Tile t, uint8 random_bits) { MakeWater(t, OWNER_WATER, WATER_CLASS_RIVER, random_bits); } @@ -443,7 +443,7 @@ static inline void MakeRiver(TileIndex t, uint8 random_bits) * @param o The owner of the canal * @param random_bits Random bits to be set for this tile */ -static inline void MakeCanal(TileIndex t, Owner o, uint8 random_bits) +static inline void MakeCanal(Tile t, Owner o, uint8 random_bits) { assert(o != OWNER_WATER); MakeWater(t, o, WATER_CLASS_CANAL, random_bits); @@ -458,18 +458,18 @@ static inline void MakeCanal(TileIndex t, Owner o, uint8 random_bits) * @param a Axis of the depot. * @param original_water_class Original water class. */ -static inline void MakeShipDepot(TileIndex t, Owner o, DepotID did, DepotPart part, Axis a, WaterClass original_water_class) +static inline void MakeShipDepot(Tile t, Owner o, DepotID did, DepotPart part, Axis a, WaterClass original_water_class) { SetTileType(t, MP_WATER); SetTileOwner(t, o); SetWaterClass(t, original_water_class); SetDockingTile(t, false); - _m[t].m2 = did; - _m[t].m3 = 0; - _m[t].m4 = 0; - _m[t].m5 = WBL_TYPE_DEPOT << WBL_TYPE_BEGIN | part << WBL_DEPOT_PART | a << WBL_DEPOT_AXIS; - SB(_me[t].m6, 2, 4, 0); - _me[t].m7 = 0; + t.m2() = did; + t.m3() = 0; + t.m4() = 0; + t.m5() = WBL_TYPE_DEPOT << WBL_TYPE_BEGIN | part << WBL_DEPOT_PART | a << WBL_DEPOT_AXIS; + SB(t.m6(), 2, 4, 0); + t.m7() = 0; } /** @@ -481,18 +481,18 @@ static inline void MakeShipDepot(TileIndex t, Owner o, DepotID did, DepotPart pa * @param original_water_class Original water class. * @see MakeLock */ -static inline void MakeLockTile(TileIndex t, Owner o, LockPart part, DiagDirection dir, WaterClass original_water_class) +static inline void MakeLockTile(Tile t, Owner o, LockPart part, DiagDirection dir, WaterClass original_water_class) { SetTileType(t, MP_WATER); SetTileOwner(t, o); SetWaterClass(t, original_water_class); SetDockingTile(t, false); - _m[t].m2 = 0; - _m[t].m3 = 0; - _m[t].m4 = 0; - _m[t].m5 = WBL_TYPE_LOCK << WBL_TYPE_BEGIN | part << WBL_LOCK_PART_BEGIN | dir << WBL_LOCK_ORIENT_BEGIN; - SB(_me[t].m6, 2, 4, 0); - _me[t].m7 = 0; + t.m2() = 0; + t.m3() = 0; + t.m4() = 0; + t.m5() = WBL_TYPE_LOCK << WBL_TYPE_BEGIN | part << WBL_LOCK_PART_BEGIN | dir << WBL_LOCK_ORIENT_BEGIN; + SB(t.m6(), 2, 4, 0); + t.m7() = 0; } /** @@ -504,15 +504,17 @@ static inline void MakeLockTile(TileIndex t, Owner o, LockPart part, DiagDirecti * @param wc_upper Original water class of the upper part. * @param wc_middle Original water class of the middle part. */ -static inline void MakeLock(TileIndex t, Owner o, DiagDirection d, WaterClass wc_lower, WaterClass wc_upper, WaterClass wc_middle) +static inline void MakeLock(Tile t, Owner o, DiagDirection d, WaterClass wc_lower, WaterClass wc_upper, WaterClass wc_middle) { TileIndexDiff delta = TileOffsByDiagDir(d); + Tile lower_tile = TileIndex(t) - delta; + Tile upper_tile = TileIndex(t) + delta; /* Keep the current waterclass and owner for the tiles. * It allows to restore them after the lock is deleted */ MakeLockTile(t, o, LOCK_PART_MIDDLE, d, wc_middle); - MakeLockTile(t - delta, IsWaterTile(t - delta) ? GetTileOwner(t - delta) : o, LOCK_PART_LOWER, d, wc_lower); - MakeLockTile(t + delta, IsWaterTile(t + delta) ? GetTileOwner(t + delta) : o, LOCK_PART_UPPER, d, wc_upper); + MakeLockTile(lower_tile, IsWaterTile(lower_tile) ? GetTileOwner(lower_tile) : o, LOCK_PART_LOWER, d, wc_lower); + MakeLockTile(upper_tile, IsWaterTile(upper_tile) ? GetTileOwner(upper_tile) : o, LOCK_PART_UPPER, d, wc_upper); } #endif /* WATER_MAP_H */ From 1eefe1c3a0147c5a926af0a7afbf67e13f29d383 Mon Sep 17 00:00:00 2001 From: Rubidium Date: Sat, 21 Jan 2023 17:32:28 +0100 Subject: [PATCH 29/47] Codechange: hide Tile(Extended) into the Tile structure --- src/map.cpp | 12 ++++----- src/map_func.h | 68 ++++++++++++++++++++++++++++++-------------------- src/map_type.h | 26 ------------------- 3 files changed, 47 insertions(+), 59 deletions(-) diff --git a/src/map.cpp b/src/map.cpp index 41f579b899..e5bbfac250 100644 --- a/src/map.cpp +++ b/src/map.cpp @@ -27,8 +27,8 @@ extern "C" _CRTIMP void __cdecl _assert(void *, void *, unsigned); /* static */ uint Map::size; ///< The number of tiles on the map /* static */ uint Map::tile_mask; ///< _map_size - 1 (to mask the mapsize) -TileBase *_m = nullptr; ///< Tiles of the map -TileExtended *_me = nullptr; ///< Extended Tiles of the map +/* static */ Tile::TileBase *Tile::base_tiles = nullptr; ///< Base tiles of the map +/* static */ Tile::TileExtended *Tile::extended_tiles = nullptr; ///< Extended tiles of the map /** @@ -56,11 +56,11 @@ TileExtended *_me = nullptr; ///< Extended Tiles of the map Map::size = size_x * size_y; Map::tile_mask = Map::size - 1; - free(_m); - free(_me); + free(Tile::base_tiles); + free(Tile::extended_tiles); - _m = CallocT(Map::size); - _me = CallocT(Map::size); + Tile::base_tiles = CallocT(Map::size); + Tile::extended_tiles = CallocT(Map::size); } diff --git a/src/map_func.h b/src/map_func.h index 507fb4914f..c04796ff8b 100644 --- a/src/map_func.h +++ b/src/map_func.h @@ -15,22 +15,6 @@ #include "map_type.h" #include "direction_func.h" -/** - * Pointer to the tile-array. - * - * This variable points to the tile-array which contains the tiles of - * the map. - */ -extern TileBase *_m; - -/** - * Pointer to the extended tile-array. - * - * This variable points to the extended tile-array which contains the tiles - * of the map. - */ -extern TileExtended *_me; - /** * Wrapper class to abstract away the way the tiles are stored. It is * intended to be used to access the "map" data of a single tile. @@ -40,6 +24,36 @@ extern TileExtended *_me; */ class Tile { private: + friend struct Map; + /** + * Data that is stored per tile. Also used TileExtended for this. + * Look at docs/landscape.html for the exact meaning of the members. + */ + struct TileBase { + byte type; ///< The type (bits 4..7), bridges (2..3), rainforest/desert (0..1) + byte height; ///< The height of the northern corner. + uint16 m2; ///< Primarily used for indices to towns, industries and stations + byte m1; ///< Primarily used for ownership information + byte m3; ///< General purpose + byte m4; ///< General purpose + byte m5; ///< General purpose + }; + + static_assert(sizeof(TileBase) == 8); + + /** + * Data that is stored per tile. Also used TileBase for this. + * Look at docs/landscape.html for the exact meaning of the members. + */ + struct TileExtended { + byte m6; ///< General purpose + byte m7; ///< Primarily used for newgrf support + uint16 m8; ///< General purpose + }; + + static TileBase *base_tiles; ///< Pointer to the tile-array. + static TileExtended *extended_tiles; ///< Pointer to the extended tile-array. + TileIndex tile; ///< The tile to access the map data for. public: @@ -74,7 +88,7 @@ public: */ debug_inline byte &type() { - return _m[tile].type; + return base_tiles[tile].type; } /** @@ -86,7 +100,7 @@ public: */ debug_inline byte &height() { - return _m[tile].height; + return base_tiles[tile].height; } /** @@ -98,7 +112,7 @@ public: */ debug_inline byte &m1() { - return _m[tile].m1; + return base_tiles[tile].m1; } /** @@ -110,7 +124,7 @@ public: */ debug_inline uint16 &m2() { - return _m[tile].m2; + return base_tiles[tile].m2; } /** @@ -122,7 +136,7 @@ public: */ debug_inline byte &m3() { - return _m[tile].m3; + return base_tiles[tile].m3; } /** @@ -134,7 +148,7 @@ public: */ debug_inline byte &m4() { - return _m[tile].m4; + return base_tiles[tile].m4; } /** @@ -146,7 +160,7 @@ public: */ debug_inline byte &m5() { - return _m[tile].m5; + return base_tiles[tile].m5; } /** @@ -158,7 +172,7 @@ public: */ debug_inline byte &m6() { - return _me[tile].m6; + return extended_tiles[tile].m6; } /** @@ -170,7 +184,7 @@ public: */ debug_inline byte &m7() { - return _me[tile].m7; + return extended_tiles[tile].m7; } /** @@ -182,7 +196,7 @@ public: */ debug_inline uint16 &m8() { - return _me[tile].m8; + return extended_tiles[tile].m8; } }; @@ -339,7 +353,7 @@ public: */ static bool IsInitialized() { - return _m != nullptr; + return Tile::base_tiles != nullptr; } /** diff --git a/src/map_type.h b/src/map_type.h index 238647d792..1b5c0fc975 100644 --- a/src/map_type.h +++ b/src/map_type.h @@ -10,32 +10,6 @@ #ifndef MAP_TYPE_H #define MAP_TYPE_H -/** - * Data that is stored per tile. Also used TileExtended for this. - * Look at docs/landscape.html for the exact meaning of the members. - */ -struct TileBase { - byte type; ///< The type (bits 4..7), bridges (2..3), rainforest/desert (0..1) - byte height; ///< The height of the northern corner. - uint16 m2; ///< Primarily used for indices to towns, industries and stations - byte m1; ///< Primarily used for ownership information - byte m3; ///< General purpose - byte m4; ///< General purpose - byte m5; ///< General purpose -}; - -static_assert(sizeof(TileBase) == 8); - -/** - * Data that is stored per tile. Also used TileBase for this. - * Look at docs/landscape.html for the exact meaning of the members. - */ -struct TileExtended { - byte m6; ///< General purpose - byte m7; ///< Primarily used for newgrf support - uint16 m8; ///< General purpose -}; - /** * An offset value between two tiles. * From 6e52ceab96d91b3766cca093d0b397b7e4ff3e9d Mon Sep 17 00:00:00 2001 From: Tyler Trahan Date: Tue, 28 Feb 2023 12:20:41 -0500 Subject: [PATCH 30/47] Feature: Industry directory text filter (#10518) --- src/industry_gui.cpp | 44 ++++++++++++++++++++++++++++------- src/widgets/industry_widget.h | 1 + 2 files changed, 37 insertions(+), 8 deletions(-) diff --git a/src/industry_gui.cpp b/src/industry_gui.cpp index 8bec1d669f..0508acd5a7 100644 --- a/src/industry_gui.cpp +++ b/src/industry_gui.cpp @@ -40,6 +40,8 @@ #include "clear_map.h" #include "zoom_func.h" #include "industry_cmd.h" +#include "querystring_gui.h" +#include "stringfilter_type.h" #include "table/strings.h" @@ -1225,12 +1227,17 @@ static const NWidgetPart _nested_industry_directory_widgets[] = { EndContainer(), NWidget(NWID_HORIZONTAL), NWidget(NWID_VERTICAL), - NWidget(NWID_HORIZONTAL), - NWidget(WWT_TEXTBTN, COLOUR_BROWN, WID_ID_DROPDOWN_ORDER), SetDataTip(STR_BUTTON_SORT_BY, STR_TOOLTIP_SORT_ORDER), - NWidget(WWT_DROPDOWN, COLOUR_BROWN, WID_ID_DROPDOWN_CRITERIA), SetDataTip(STR_JUST_STRING, STR_TOOLTIP_SORT_CRITERIA), - NWidget(WWT_DROPDOWN, COLOUR_BROWN, WID_ID_FILTER_BY_ACC_CARGO), SetMinimalSize(225, 12), SetFill(0, 1), SetDataTip(STR_INDUSTRY_DIRECTORY_ACCEPTED_CARGO_FILTER, STR_TOOLTIP_FILTER_CRITERIA), - NWidget(WWT_DROPDOWN, COLOUR_BROWN, WID_ID_FILTER_BY_PROD_CARGO), SetMinimalSize(225, 12), SetFill(0, 1), SetDataTip(STR_INDUSTRY_DIRECTORY_PRODUCED_CARGO_FILTER, STR_TOOLTIP_FILTER_CRITERIA), - NWidget(WWT_PANEL, COLOUR_BROWN), SetResize(1, 0), EndContainer(), + NWidget(NWID_VERTICAL), + NWidget(NWID_HORIZONTAL), + NWidget(WWT_TEXTBTN, COLOUR_BROWN, WID_ID_DROPDOWN_ORDER), SetDataTip(STR_BUTTON_SORT_BY, STR_TOOLTIP_SORT_ORDER), + NWidget(WWT_DROPDOWN, COLOUR_BROWN, WID_ID_DROPDOWN_CRITERIA), SetDataTip(STR_JUST_STRING, STR_TOOLTIP_SORT_CRITERIA), + NWidget(WWT_EDITBOX, COLOUR_BROWN, WID_ID_FILTER), SetFill(1, 0), SetResize(1, 0), SetDataTip(STR_LIST_FILTER_OSKTITLE, STR_LIST_FILTER_TOOLTIP), + EndContainer(), + NWidget(NWID_HORIZONTAL), + NWidget(WWT_DROPDOWN, COLOUR_BROWN, WID_ID_FILTER_BY_ACC_CARGO), SetMinimalSize(225, 12), SetFill(0, 1), SetDataTip(STR_INDUSTRY_DIRECTORY_ACCEPTED_CARGO_FILTER, STR_TOOLTIP_FILTER_CRITERIA), + NWidget(WWT_DROPDOWN, COLOUR_BROWN, WID_ID_FILTER_BY_PROD_CARGO), SetMinimalSize(225, 12), SetFill(0, 1), SetDataTip(STR_INDUSTRY_DIRECTORY_PRODUCED_CARGO_FILTER, STR_TOOLTIP_FILTER_CRITERIA), + NWidget(WWT_PANEL, COLOUR_BROWN), SetResize(1, 0), EndContainer(), + EndContainer(), EndContainer(), NWidget(WWT_PANEL, COLOUR_BROWN, WID_ID_INDUSTRY_LIST), SetDataTip(0x0, STR_INDUSTRY_DIRECTORY_LIST_CAPTION), SetResize(1, 1), SetScrollbar(WID_ID_SCROLLBAR), EndContainer(), EndContainer(), @@ -1326,6 +1333,10 @@ protected: byte accepted_cargo_filter_criteria; ///< Selected accepted cargo filter index static CargoID produced_cargo_filter; + const int MAX_FILTER_LENGTH = 16; ///< The max length of the filter, in chars + StringFilter string_filter; ///< Filter for industries + QueryString industry_editbox; ///< Filter editbox + enum class SorterType : uint8 { ByName, ///< Sorter type to sort by name ByType, ///< Sorter type to sort by type @@ -1410,7 +1421,13 @@ protected: this->industries.clear(); for (const Industry *i : Industry::Iterate()) { - this->industries.push_back(i); + if (this->string_filter.IsEmpty()) { + this->industries.push_back(i); + continue; + } + this->string_filter.ResetState(); + this->string_filter.AddLine(i->GetCachedName()); + if (this->string_filter.GetState()) this->industries.push_back(i); } this->industries.shrink_to_fit(); @@ -1610,7 +1627,7 @@ protected: } public: - IndustryDirectoryWindow(WindowDesc *desc, WindowNumber number) : Window(desc) + IndustryDirectoryWindow(WindowDesc *desc, WindowNumber number) : Window(desc), industry_editbox(MAX_FILTER_LENGTH * MAX_CHAR_LENGTH, MAX_FILTER_LENGTH) { this->CreateNestedTree(); this->vscroll = this->GetScrollbar(WID_ID_SCROLLBAR); @@ -1621,6 +1638,9 @@ public: this->BuildSortIndustriesList(); this->FinishInitNested(0); + + this->querystrings[WID_ID_FILTER] = &this->industry_editbox; + this->industry_editbox.cancel_button = QueryString::ACTION_CLEAR; } ~IndustryDirectoryWindow() @@ -1786,6 +1806,14 @@ public: this->vscroll->SetCapacityFromWidget(this, WID_ID_INDUSTRY_LIST); } + void OnEditboxChanged(int wid) override + { + if (wid == WID_ID_FILTER) { + this->string_filter.SetFilterTerm(this->industry_editbox.text.buf); + this->InvalidateData(IDIWD_FORCE_REBUILD); + } + } + void OnPaint() override { if (this->industries.NeedRebuild()) this->BuildSortIndustriesList(); diff --git a/src/widgets/industry_widget.h b/src/widgets/industry_widget.h index 5e3a656c1f..9f5762a534 100644 --- a/src/widgets/industry_widget.h +++ b/src/widgets/industry_widget.h @@ -37,6 +37,7 @@ enum IndustryDirectoryWidgets { WID_ID_DROPDOWN_CRITERIA, ///< Dropdown for the criteria of the sort. WID_ID_FILTER_BY_ACC_CARGO, ///< Accepted cargo filter dropdown list. WID_ID_FILTER_BY_PROD_CARGO, ///< Produced cargo filter dropdown list. + WID_ID_FILTER, ///< Textbox to filter industry name. WID_ID_INDUSTRY_LIST, ///< Industry list. WID_ID_SCROLLBAR, ///< Scrollbar of the list. }; From af15dca3160f7b59ba4f13e7dea8592862e930ea Mon Sep 17 00:00:00 2001 From: glx22 Date: Sat, 18 Feb 2023 00:52:22 +0100 Subject: [PATCH 31/47] Change: [Script] Extract params info from GS strings --- src/game/game_text.cpp | 42 ++++++++++++++++++++++++++++++++++++++ src/game/game_text.hpp | 17 +++++++++++++++ src/strgen/strgen.h | 16 +++++++++++++++ src/strgen/strgen_base.cpp | 15 ++------------ 4 files changed, 77 insertions(+), 13 deletions(-) diff --git a/src/game/game_text.cpp b/src/game/game_text.cpp index 85d9b03ced..57ef1f97ec 100644 --- a/src/game/game_text.cpp +++ b/src/game/game_text.cpp @@ -19,6 +19,7 @@ #include "game_info.hpp" #include "table/strings.h" +#include "table/strgen_tables.h" #include #include @@ -273,6 +274,31 @@ GameStrings *LoadTranslations() } } +static StringParam::ParamType GetParamType(const CmdStruct *cs) +{ + if (cs->value == SCC_RAW_STRING_POINTER) return StringParam::RAW_STRING; + if (cs->value == SCC_STRING || cs != TranslateCmdForCompare(cs)) return StringParam::STRING; + return StringParam::OTHER; +} + +static void ExtractStringParams(const StringData &data, StringParamsList ¶ms) +{ + for (size_t i = 0; i < data.max_strings; i++) { + const LangString *ls = data.strings[i]; + + if (ls != nullptr) { + StringParams ¶m = params.emplace_back(); + ParsedCommandStruct pcs; + ExtractCommandString(&pcs, ls->english, false); + + for (const CmdStruct *cs : pcs.cmd) { + if (cs == nullptr) break; + param.emplace_back(GetParamType(cs), cs->consumes); + } + } + } +} + /** Compile the language. */ void GameStrings::Compile() { @@ -283,6 +309,8 @@ void GameStrings::Compile() this->version = data.Version(); + ExtractStringParams(data, this->string_params); + StringNameWriter id_writer(this->string_names); id_writer.WriteHeader(data); @@ -312,6 +340,20 @@ const char *GetGameStringPtr(uint id) return _current_data->cur_language->lines[id].c_str(); } +/** + * Get the string parameters of a particular game string. + * @param id The ID of the game string. + * @return The string parameters. + */ +const StringParams &GetGameStringParams(uint id) +{ + /* An empty result for STR_UNDEFINED. */ + static StringParams empty; + + if (id >= _current_data->string_params.size()) return empty; + return _current_data->string_params[id]; +} + /** * Register the current translation to the Squirrel engine. * @param engine The engine to update/ diff --git a/src/game/game_text.hpp b/src/game/game_text.hpp index 91d85847df..7aebf885e3 100644 --- a/src/game/game_text.hpp +++ b/src/game/game_text.hpp @@ -12,7 +12,23 @@ #include "../core/smallvec_type.hpp" +struct StringParam { + enum ParamType { + RAW_STRING, + STRING, + OTHER + }; + + ParamType type; + uint8 consumes; + + StringParam(ParamType type, uint8 consumes) : type(type), consumes(consumes) {} +}; +using StringParams = std::vector; +using StringParamsList = std::vector; + const char *GetGameStringPtr(uint id); +const StringParams &GetGameStringParams(uint id); void RegisterGameTranslation(class Squirrel *engine); void ReconsiderGameScriptLanguage(); @@ -37,6 +53,7 @@ struct GameStrings { std::vector raw_strings; ///< The raw strings per language, first must be English/the master language!. std::vector compiled_strings; ///< The compiled strings per language, first must be English/the master language!. StringList string_names; ///< The names of the compiled strings. + StringParamsList string_params; ///< The parameters for the strings. void Compile(); diff --git a/src/strgen/strgen.h b/src/strgen/strgen.h index 2110d30876..809b475143 100644 --- a/src/strgen/strgen.h +++ b/src/strgen/strgen.h @@ -136,6 +136,22 @@ struct LanguageWriter { virtual void WriteLang(const StringData &data); }; +struct CmdStruct; + +struct CmdPair { + const CmdStruct *a; + const char *v; +}; + +struct ParsedCommandStruct { + uint np; + CmdPair pairs[32]; + const CmdStruct *cmd[32]; // ordered by param # +}; + +const CmdStruct *TranslateCmdForCompare(const CmdStruct *a); +void ExtractCommandString(ParsedCommandStruct *p, const char *s, bool warnings); + void CDECL strgen_warning(const char *s, ...) WARN_FORMAT(1, 2); void CDECL strgen_error(const char *s, ...) WARN_FORMAT(1, 2); void NORETURN CDECL strgen_fatal(const char *s, ...) WARN_FORMAT(1, 2); diff --git a/src/strgen/strgen_base.cpp b/src/strgen/strgen_base.cpp index bf3a25e3ad..ccd043adfa 100644 --- a/src/strgen/strgen_base.cpp +++ b/src/strgen/strgen_base.cpp @@ -217,17 +217,6 @@ uint StringData::CountInUse(uint tab) const static const char *_cur_ident; -struct CmdPair { - const CmdStruct *a; - const char *v; -}; - -struct ParsedCommandStruct { - uint np; - CmdPair pairs[32]; - const CmdStruct *cmd[32]; // ordered by param # -}; - /* Used when generating some advanced commands. */ static ParsedCommandStruct _cur_pcs; static int _cur_argidx; @@ -594,7 +583,7 @@ StringReader::~StringReader() free(file); } -static void ExtractCommandString(ParsedCommandStruct *p, const char *s, bool warnings) +void ExtractCommandString(ParsedCommandStruct *p, const char *s, bool warnings) { char param[MAX_COMMAND_PARAM_SIZE]; int argno; @@ -628,7 +617,7 @@ static void ExtractCommandString(ParsedCommandStruct *p, const char *s, bool war } -static const CmdStruct *TranslateCmdForCompare(const CmdStruct *a) +const CmdStruct *TranslateCmdForCompare(const CmdStruct *a) { if (a == nullptr) return nullptr; From a1fc4d5c0e2b9f6665aa14014b0326378d2b02a3 Mon Sep 17 00:00:00 2001 From: glx22 Date: Sat, 18 Feb 2023 02:29:50 +0100 Subject: [PATCH 32/47] Codechange: [Script] Merge the 3 ScriptText param arrays --- src/script/api/script_text.cpp | 27 ++++++++++----------------- src/script/api/script_text.hpp | 6 +++--- 2 files changed, 13 insertions(+), 20 deletions(-) diff --git a/src/script/api/script_text.cpp b/src/script/api/script_text.cpp index d01f3b93f1..ea5079eb82 100644 --- a/src/script/api/script_text.cpp +++ b/src/script/api/script_text.cpp @@ -56,8 +56,7 @@ ScriptText::ScriptText(HSQUIRRELVM vm) : ScriptText::~ScriptText() { for (int i = 0; i < SCRIPT_TEXT_MAX_PARAMETERS; i++) { - free(this->params[i]); - if (this->paramt[i] != nullptr) this->paramt[i]->Release(); + if (std::holds_alternative(this->param[i])) std::get(this->param[i])->Release(); } } @@ -65,20 +64,14 @@ SQInteger ScriptText::_SetParam(int parameter, HSQUIRRELVM vm) { if (parameter >= SCRIPT_TEXT_MAX_PARAMETERS) return SQ_ERROR; - free(this->params[parameter]); - if (this->paramt[parameter] != nullptr) this->paramt[parameter]->Release(); - - this->parami[parameter] = 0; - this->params[parameter] = nullptr; - this->paramt[parameter] = nullptr; + if (std::holds_alternative(this->param[parameter])) std::get(this->param[parameter])->Release(); switch (sq_gettype(vm, -1)) { case OT_STRING: { const SQChar *value; sq_getstring(vm, -1, &value); - this->params[parameter] = stredup(value); - StrMakeValidInPlace(this->params[parameter]); + this->param[parameter] = StrMakeValid(value); break; } @@ -86,7 +79,7 @@ SQInteger ScriptText::_SetParam(int parameter, HSQUIRRELVM vm) SQInteger value; sq_getinteger(vm, -1, &value); - this->parami[parameter] = value; + this->param[parameter] = value; break; } @@ -110,7 +103,7 @@ SQInteger ScriptText::_SetParam(int parameter, HSQUIRRELVM vm) ScriptText *value = static_cast(real_instance); value->AddRef(); - this->paramt[parameter] = value; + this->param[parameter] = value; break; } @@ -186,17 +179,17 @@ char *ScriptText::_GetEncodedText(char *p, char *lastofp, int ¶m_count) p += Utf8Encode(p, SCC_ENCODED); p += seprintf(p, lastofp, "%X", this->string); for (int i = 0; i < this->paramc; i++) { - if (this->params[i] != nullptr) { - p += seprintf(p, lastofp, ":\"%s\"", this->params[i]); + if (std::holds_alternative(this->param[i])) { + p += seprintf(p, lastofp, ":\"%s\"", std::get(this->param[i]).c_str()); param_count++; continue; } - if (this->paramt[i] != nullptr) { + if (std::holds_alternative(this->param[i])) { p += seprintf(p, lastofp, ":"); - p = this->paramt[i]->_GetEncodedText(p, lastofp, param_count); + p = std::get(this->param[i])->_GetEncodedText(p, lastofp, param_count); continue; } - p += seprintf(p, lastofp,":" OTTD_PRINTFHEX64, this->parami[i]); + p += seprintf(p, lastofp, ":" OTTD_PRINTFHEX64, std::get(this->param[i])); param_count++; } diff --git a/src/script/api/script_text.hpp b/src/script/api/script_text.hpp index 66ab2bd47d..4694407879 100644 --- a/src/script/api/script_text.hpp +++ b/src/script/api/script_text.hpp @@ -13,6 +13,8 @@ #include "script_object.hpp" #include "../../core/alloc_type.hpp" +#include + /** * Internal parent object of all Text-like objects. * @api -all @@ -128,9 +130,7 @@ public: private: StringID string; - char *params[SCRIPT_TEXT_MAX_PARAMETERS]; - int64 parami[SCRIPT_TEXT_MAX_PARAMETERS]; - ScriptText *paramt[SCRIPT_TEXT_MAX_PARAMETERS]; + std::variant param[SCRIPT_TEXT_MAX_PARAMETERS]; int paramc; /** From 728973859d5b97b2c862718b111cb09fc1efa769 Mon Sep 17 00:00:00 2001 From: Rubidium Date: Tue, 28 Feb 2023 17:13:38 +0100 Subject: [PATCH 33/47] Change: [Script] Automate the ScriptObject reference counting --- src/script/api/script_object.hpp | 66 ++++++++++++++++++++++++++++++++ src/script/api/script_text.cpp | 16 ++------ src/script/api/script_text.hpp | 5 ++- 3 files changed, 72 insertions(+), 15 deletions(-) diff --git a/src/script/api/script_object.hpp b/src/script/api/script_object.hpp index 779381ca80..a8d01d5c0d 100644 --- a/src/script/api/script_object.hpp +++ b/src/script/api/script_object.hpp @@ -21,6 +21,8 @@ #include "../script_suspend.hpp" #include "../squirrel.hpp" +#include + /** * The callback function for Mode-classes. */ @@ -367,4 +369,68 @@ bool ScriptObject::ScriptDoCommandHelper } } +/** + * Internally used class to automate the ScriptObject reference counting. + * @api -all + */ +template +class ScriptObjectRef { +private: + T *data; ///< The reference counted object. +public: + /** + * Create the reference counter for the given ScriptObject instance. + * @param data The underlying object. + */ + ScriptObjectRef(T *data) : data(data) + { + this->data->AddRef(); + } + + /* No copy constructor. */ + ScriptObjectRef(const ScriptObjectRef &ref) = delete; + + /* Move constructor. */ + ScriptObjectRef(ScriptObjectRef &&ref) noexcept : data(std::exchange(ref.data, nullptr)) + { + } + + /* No copy assignment. */ + ScriptObjectRef& operator=(const ScriptObjectRef &other) = delete; + + /* Move assignment. */ + ScriptObjectRef& operator=(ScriptObjectRef &&other) noexcept + { + std::swap(this->data, other.data); + return *this; + } + + /** + * Release the reference counted object. + */ + ~ScriptObjectRef() + { + if (this->data != nullptr) this->data->Release(); + } + + /** + * Dereferencing this reference returns a reference to the reference + * counted object + * @return Reference to the underlying object. + */ + T &operator*() + { + return *this->data; + } + + /** + * The arrow operator on this reference returns the reference counted object. + * @return Pointer to the underlying object. + */ + T *operator->() + { + return this->data; + } +}; + #endif /* SCRIPT_OBJECT_HPP */ diff --git a/src/script/api/script_text.cpp b/src/script/api/script_text.cpp index ea5079eb82..1c3216c3af 100644 --- a/src/script/api/script_text.cpp +++ b/src/script/api/script_text.cpp @@ -53,19 +53,10 @@ ScriptText::ScriptText(HSQUIRRELVM vm) : } } -ScriptText::~ScriptText() -{ - for (int i = 0; i < SCRIPT_TEXT_MAX_PARAMETERS; i++) { - if (std::holds_alternative(this->param[i])) std::get(this->param[i])->Release(); - } -} - SQInteger ScriptText::_SetParam(int parameter, HSQUIRRELVM vm) { if (parameter >= SCRIPT_TEXT_MAX_PARAMETERS) return SQ_ERROR; - if (std::holds_alternative(this->param[parameter])) std::get(this->param[parameter])->Release(); - switch (sq_gettype(vm, -1)) { case OT_STRING: { const SQChar *value; @@ -102,8 +93,7 @@ SQInteger ScriptText::_SetParam(int parameter, HSQUIRRELVM vm) if (real_instance == nullptr) return SQ_ERROR; ScriptText *value = static_cast(real_instance); - value->AddRef(); - this->param[parameter] = value; + this->param[parameter] = ScriptTextRef(value); break; } @@ -184,9 +174,9 @@ char *ScriptText::_GetEncodedText(char *p, char *lastofp, int ¶m_count) param_count++; continue; } - if (std::holds_alternative(this->param[i])) { + if (std::holds_alternative(this->param[i])) { p += seprintf(p, lastofp, ":"); - p = std::get(this->param[i])->_GetEncodedText(p, lastofp, param_count); + p = std::get(this->param[i])->_GetEncodedText(p, lastofp, param_count); continue; } p += seprintf(p, lastofp, ":" OTTD_PRINTFHEX64, std::get(this->param[i])); diff --git a/src/script/api/script_text.hpp b/src/script/api/script_text.hpp index 4694407879..e64dda6b6c 100644 --- a/src/script/api/script_text.hpp +++ b/src/script/api/script_text.hpp @@ -90,7 +90,6 @@ public: */ ScriptText(StringID string, ...); #endif /* DOXYGEN_API */ - ~ScriptText(); #ifndef DOXYGEN_API /** @@ -129,8 +128,10 @@ public: virtual const std::string GetEncodedText(); private: + using ScriptTextRef = ScriptObjectRef; + StringID string; - std::variant param[SCRIPT_TEXT_MAX_PARAMETERS]; + std::variant param[SCRIPT_TEXT_MAX_PARAMETERS]; int paramc; /** From 991a797e098e22b8b3a0adaead757d691d80fca4 Mon Sep 17 00:00:00 2001 From: glx22 Date: Sat, 18 Feb 2023 03:21:25 +0100 Subject: [PATCH 34/47] Change: [Script] Validate ScriptText parameters type and amount --- src/script/api/script_text.cpp | 43 ++++++++++++++++++++++++---------- 1 file changed, 31 insertions(+), 12 deletions(-) diff --git a/src/script/api/script_text.cpp b/src/script/api/script_text.cpp index 1c3216c3af..e634e14d43 100644 --- a/src/script/api/script_text.cpp +++ b/src/script/api/script_text.cpp @@ -10,6 +10,7 @@ #include "../../stdafx.h" #include "../../string_func.h" #include "../../strings_func.h" +#include "../../game/game_text.hpp" #include "script_text.hpp" #include "../script_fatalerror.hpp" #include "../../table/control_codes.h" @@ -168,19 +169,37 @@ char *ScriptText::_GetEncodedText(char *p, char *lastofp, int ¶m_count) { p += Utf8Encode(p, SCC_ENCODED); p += seprintf(p, lastofp, "%X", this->string); - for (int i = 0; i < this->paramc; i++) { - if (std::holds_alternative(this->param[i])) { - p += seprintf(p, lastofp, ":\"%s\"", std::get(this->param[i]).c_str()); - param_count++; - continue; - } - if (std::holds_alternative(this->param[i])) { - p += seprintf(p, lastofp, ":"); - p = std::get(this->param[i])->_GetEncodedText(p, lastofp, param_count); - continue; + + const StringParams ¶ms = GetGameStringParams(this->string); + int cur_idx = 0; + + for (const StringParam &cur_param : params) { + if (cur_idx >= this->paramc) throw Script_FatalError("Not enough string parameters"); + + switch (cur_param.type) { + case StringParam::RAW_STRING: + if (!std::holds_alternative(this->param[cur_idx])) throw Script_FatalError("Wrong string parameter type"); + p += seprintf(p, lastofp, ":\"%s\"", std::get(this->param[cur_idx++]).c_str()); + break; + + case StringParam::STRING: { + if (!std::holds_alternative(this->param[cur_idx])) throw Script_FatalError("Wrong string parameter type"); + int count = 1; // 1 because the string id is included in consumed parameters + p += seprintf(p, lastofp, ":"); + p = std::get(this->param[cur_idx++])->_GetEncodedText(p, lastofp, count); + if (count != cur_param.consumes) throw Script_FatalError("Substring doesn't consume the expected amount of parameters."); + break; + } + + default: + if (cur_idx + cur_param.consumes > this->paramc) throw Script_FatalError("Not enough string parameters"); + for (int i = 0; i < cur_param.consumes; i++) { + if (!std::holds_alternative(this->param[cur_idx])) throw Script_FatalError("Wrong string parameter type"); + p += seprintf(p, lastofp,":" OTTD_PRINTFHEX64, std::get(this->param[cur_idx++])); + } } - p += seprintf(p, lastofp, ":" OTTD_PRINTFHEX64, std::get(this->param[i])); - param_count++; + + param_count += cur_param.consumes; } return p; From 41b414bc1c45d46ab6c227ab0a67a876f0faa3c8 Mon Sep 17 00:00:00 2001 From: glx22 Date: Sat, 18 Feb 2023 14:37:52 +0100 Subject: [PATCH 35/47] Change: [Script] Restore support of {RAW_STRING} in ScriptText --- src/strings.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/strings.cpp b/src/strings.cpp index f888770714..2f39a39991 100644 --- a/src/strings.cpp +++ b/src/strings.cpp @@ -926,8 +926,9 @@ static char *FormatString(char *buff, const char *str_arg, StringParameters *arg sub_args.SetParam(i++, param); } else { + s++; // skip the leading \" char *g = stredup(s); - g[p - s] = '\0'; + g[p - s - 1] = '\0'; // skip the trailing \" sub_args_need_free[i] = true; sub_args.SetParam(i++, (uint64)(size_t)g); @@ -1044,7 +1045,6 @@ static char *FormatString(char *buff, const char *str_arg, StringParameters *arg break; case SCC_RAW_STRING_POINTER: { // {RAW_STRING} - if (game_script) break; const char *raw_string = (const char *)(size_t)args->GetInt64(SCC_RAW_STRING_POINTER); buff = FormatString(buff, raw_string, args, last); break; From 6f9b3fa9641ab83290820bc796a3ca5b2459e120 Mon Sep 17 00:00:00 2001 From: translators Date: Tue, 28 Feb 2023 18:45:57 +0000 Subject: [PATCH 36/47] Update: Translations from eints chinese (traditional): 7 changes by inAndyPao --- src/lang/traditional_chinese.txt | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/lang/traditional_chinese.txt b/src/lang/traditional_chinese.txt index 73d9aeb11f..1ab1387228 100644 --- a/src/lang/traditional_chinese.txt +++ b/src/lang/traditional_chinese.txt @@ -1206,7 +1206,9 @@ STR_CONFIG_SETTING_HORIZONTAL_POS_RIGHT :靠右 STR_CONFIG_SETTING_MAXIMUM_INITIAL_LOAN :最大貸款額:{STRING} STR_CONFIG_SETTING_MAXIMUM_INITIAL_LOAN_HELPTEXT :公司最多可借多少錢(此設定的值排除了通貨膨脹因素)。 +STR_CONFIG_SETTING_MAXIMUM_INITIAL_LOAN_VALUE :{CURRENCY_LONG} ###setting-zero-is-special +STR_CONFIG_SETTING_MAXIMUM_INITIAL_LOAN_DISABLED :無貸款 {RED} 需要有遊戲腳本以提供初始資金 STR_CONFIG_SETTING_INTEREST_RATE :息率:{STRING} STR_CONFIG_SETTING_INTEREST_RATE_HELPTEXT :設定貸款息率。如啟用了通貨膨脹的設定,則此設定同時決定通脹率。 @@ -3350,6 +3352,8 @@ STR_NEWGRF_ERROR_MSG_INFO :{SILVER}{STRING STR_NEWGRF_ERROR_MSG_WARNING :{RED}警告:{SILVER}{STRING} STR_NEWGRF_ERROR_MSG_ERROR :{RED}錯誤:{SILVER}{STRING} STR_NEWGRF_ERROR_MSG_FATAL :{RED}嚴重錯誤:{SILVER}{STRING} +STR_NEWGRF_ERROR_FATAL_POPUP :{WHITE}發生嚴重 NewGRF "{STRING}" 錯誤的處理方式:{}{STRING} +STR_NEWGRF_ERROR_POPUP :{WHITE}發生NewGRF "{STRING}" 錯誤:{}{STRING} STR_NEWGRF_ERROR_VERSION_NUMBER :{1:STRING} 跟由 OpenTTD 回報的 TTDPatch 版本不合 STR_NEWGRF_ERROR_DOS_OR_WINDOWS :{1:STRING} 只適合用於 TTD 版本 {2:STRING}。 STR_NEWGRF_ERROR_UNSET_SWITCH :{1:STRING} 必須跟 {2:STRING} 一起使用 @@ -4620,7 +4624,9 @@ STR_SCREENSHOT_HEIGHTMAP_SCREENSHOT :{BLACK}高度 STR_SCREENSHOT_MINIMAP_SCREENSHOT :{BLACK}小地圖截圖 # Script Parameters -STR_AI_SETTINGS_CAPTION_AI :{WHITE}AI參數 +STR_AI_SETTINGS_CAPTION :{WHITE}{STRING} 參數 +STR_AI_SETTINGS_CAPTION_AI :AI 參數 +STR_AI_SETTINGS_CAPTION_GAMESCRIPT :遊戲腳本 STR_AI_SETTINGS_CLOSE :{BLACK}關閉 STR_AI_SETTINGS_RESET :{BLACK}重設 STR_AI_SETTINGS_SETTING :{STRING}:{ORANGE}{STRING} From e17c82e32b74c44dd89cfe0aa77d191e60fd9b98 Mon Sep 17 00:00:00 2001 From: Zachary Date: Mon, 3 Oct 2022 10:52:38 -0400 Subject: [PATCH 37/47] Add: maximum number of companies allowed to the client list --- src/lang/english.txt | 3 ++- src/network/network_client.cpp | 12 +++++++++++- src/network/network_func.h | 1 + src/network/network_gui.cpp | 3 ++- src/settings_table.cpp | 6 +++++- 5 files changed, 21 insertions(+), 4 deletions(-) diff --git a/src/lang/english.txt b/src/lang/english.txt index ef80b11ac5..0a8a84a3d6 100644 --- a/src/lang/english.txt +++ b/src/lang/english.txt @@ -2358,7 +2358,8 @@ STR_NETWORK_CLIENT_LIST_NEW_COMPANY :(New company) STR_NETWORK_CLIENT_LIST_NEW_COMPANY_TOOLTIP :{BLACK}Create a new company and join it STR_NETWORK_CLIENT_LIST_PLAYER_ICON_SELF_TOOLTIP :{BLACK}This is you STR_NETWORK_CLIENT_LIST_PLAYER_ICON_HOST_TOOLTIP :{BLACK}This is the host of the game -STR_NETWORK_CLIENT_LIST_CLIENT_COMPANY_COUNT :{BLACK}{NUM} client{P "" s} / {NUM} compan{P y ies} +STR_NETWORK_CLIENT_LIST_CLIENT_COMPANY_COUNT :{BLACK}{NUM} client{P "" s} - {NUM}/{NUM} compan{P y ies} +STR_NETWORK_CLIENT_LIST_CLIENT_COMPANY_COUNT_TOOLTIP :{BLACK}The number of currently connected clients, number of companies and maximum number of companies allowed by the server administrator # Matches ConnectionType ###length 5 diff --git a/src/network/network_client.cpp b/src/network/network_client.cpp index 7abd18d6a1..5fab9a655d 100644 --- a/src/network/network_client.cpp +++ b/src/network/network_client.cpp @@ -1111,6 +1111,7 @@ NetworkRecvStatus ClientNetworkGameSocketHandler::Receive_SERVER_CONFIG_UPDATE(P _network_server_max_companies = p->Recv_uint8(); _network_server_name = p->Recv_string(NETWORK_NAME_LENGTH); + SetWindowClassesDirty(WC_CLIENT_LIST); return NETWORK_RECV_STATUS_OKAY; } @@ -1319,11 +1320,20 @@ bool NetworkClientPreferTeamChat(const NetworkClientInfo *cio) return false; } +/** + * Get the maximum number of companies that are allowed by the server. + * @return The number of companies allowed. + */ +uint NetworkMaxCompaniesAllowed() +{ + return _network_server ? _settings_client.network.max_companies : _network_server_max_companies; +} + /** * Check if max_companies has been reached on the server (local check only). * @return true if the max value has been reached or exceeded, false otherwise. */ bool NetworkMaxCompaniesReached() { - return Company::GetNumItems() >= (_network_server ? _settings_client.network.max_companies : _network_server_max_companies); + return Company::GetNumItems() >= NetworkMaxCompaniesAllowed(); } diff --git a/src/network/network_func.h b/src/network/network_func.h index 52c508a75b..63d3d0cdc0 100644 --- a/src/network/network_func.h +++ b/src/network/network_func.h @@ -59,6 +59,7 @@ void NetworkClientSendRcon(const std::string &password, const std::string &comma void NetworkClientSendChat(NetworkAction action, DestType type, int dest, const std::string &msg, int64 data = 0); bool NetworkClientPreferTeamChat(const NetworkClientInfo *cio); bool NetworkCompanyIsPassworded(CompanyID company_id); +uint NetworkMaxCompaniesAllowed(); bool NetworkMaxCompaniesReached(); void NetworkPrintClients(); void NetworkHandlePauseChange(PauseMode prev_mode, PauseMode changed_mode); diff --git a/src/network/network_gui.cpp b/src/network/network_gui.cpp index 6591cde3bd..da89de8c25 100644 --- a/src/network/network_gui.cpp +++ b/src/network/network_gui.cpp @@ -1352,7 +1352,7 @@ static const NWidgetPart _nested_client_list_widgets[] = { NWidget(NWID_VERTICAL), NWidget(WWT_MATRIX, COLOUR_GREY, WID_CL_MATRIX), SetMinimalSize(180, 0), SetResize(1, 1), SetFill(1, 1), SetMatrixDataTip(1, 0, STR_NULL), SetScrollbar(WID_CL_SCROLLBAR), NWidget(WWT_PANEL, COLOUR_GREY), - NWidget(WWT_TEXT, COLOUR_GREY, WID_CL_CLIENT_COMPANY_COUNT), SetFill(1, 0), SetMinimalTextLines(1, 0), SetResize(1, 0), SetPadding(2, 1, 2, 1), SetAlignment(SA_CENTER), SetDataTip(STR_NETWORK_CLIENT_LIST_CLIENT_COMPANY_COUNT, STR_NULL), + NWidget(WWT_TEXT, COLOUR_GREY, WID_CL_CLIENT_COMPANY_COUNT), SetFill(1, 0), SetMinimalTextLines(1, 0), SetResize(1, 0), SetPadding(2, 1, 2, 1), SetAlignment(SA_CENTER), SetDataTip(STR_NETWORK_CLIENT_LIST_CLIENT_COMPANY_COUNT, STR_NETWORK_CLIENT_LIST_CLIENT_COMPANY_COUNT_TOOLTIP), EndContainer(), EndContainer(), NWidget(NWID_VERTICAL), @@ -1797,6 +1797,7 @@ public: case WID_CL_CLIENT_COMPANY_COUNT: SetDParam(0, NetworkClientInfo::GetNumItems()); SetDParam(1, Company::GetNumItems()); + SetDParam(2, NetworkMaxCompaniesAllowed()); break; } } diff --git a/src/settings_table.cpp b/src/settings_table.cpp index ef910a3600..9fda41720f 100644 --- a/src/settings_table.cpp +++ b/src/settings_table.cpp @@ -477,7 +477,11 @@ static bool ReplaceAsteriskWithEmptyPassword(std::string &newval) static void UpdateClientConfigValues() { NetworkServerUpdateGameInfo(); - if (_network_server) NetworkServerSendConfigUpdate(); + + if (_network_server) { + NetworkServerSendConfigUpdate(); + SetWindowClassesDirty(WC_CLIENT_LIST); + } } /* End - Callback Functions */ From 5d0ad5625bb3df5839154f292af184c591abcf97 Mon Sep 17 00:00:00 2001 From: Jonathan G Rennison Date: Wed, 1 Mar 2023 00:19:39 +0000 Subject: [PATCH 38/47] Fix : [NewGRF] Object and road stop ignore property handlers (#10525) * Fix: IgnoreObjectProperty did not handle object property 0x18 * Fix: IgnoreRoadStopProperty did not handle properties 0x0E - 0x12, 0x15 --- src/newgrf.cpp | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/newgrf.cpp b/src/newgrf.cpp index 611ce367c2..045e2e9940 100644 --- a/src/newgrf.cpp +++ b/src/newgrf.cpp @@ -4059,6 +4059,7 @@ static ChangeInfoResult IgnoreObjectProperty(uint prop, ByteReader *buf) case 0x14: case 0x16: case 0x17: + case 0x18: buf->ReadByte(); break; @@ -4761,16 +4762,22 @@ static ChangeInfoResult IgnoreRoadStopProperty(uint prop, ByteReader *buf) switch (prop) { case 0x09: case 0x0C: + case 0x0F: + case 0x11: buf->ReadByte(); break; case 0x0A: case 0x0B: + case 0x0E: + case 0x10: + case 0x15: buf->ReadWord(); break; case 0x08: case 0x0D: + case 0x12: buf->ReadDWord(); break; From 0e4b716815f43effe39968a7cac80cf112e7659c Mon Sep 17 00:00:00 2001 From: Jonathan G Rennison Date: Mon, 23 Sep 2019 18:45:37 +0100 Subject: [PATCH 39/47] Fix: O(N^2) cost of Station::RecomputeCatchmentForAll Station::RemoveFromAllNearbyLists does not need to be called when all station nearby lists have been cleared and are being regenerated. --- src/station.cpp | 9 ++++++--- src/station_base.h | 2 +- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/src/station.cpp b/src/station.cpp index a9ccfc4329..700ca7241d 100644 --- a/src/station.cpp +++ b/src/station.cpp @@ -455,11 +455,12 @@ bool Station::CatchmentCoversTown(TownID t) const /** * Recompute tiles covered in our catchment area. * This will additionally recompute nearby towns and industries. + * @param no_clear_nearby_lists If Station::RemoveFromAllNearbyLists does not need to be called. */ -void Station::RecomputeCatchment() +void Station::RecomputeCatchment(bool no_clear_nearby_lists) { this->industries_near.clear(); - this->RemoveFromAllNearbyLists(); + if (!no_clear_nearby_lists) this->RemoveFromAllNearbyLists(); if (this->rect.IsEmpty()) { this->catchment_tiles.Reset(); @@ -526,7 +527,9 @@ void Station::RecomputeCatchment() */ /* static */ void Station::RecomputeCatchmentForAll() { - for (Station *st : Station::Iterate()) { st->RecomputeCatchment(); } + for (Town *t : Town::Iterate()) { t->stations_near.clear(); } + for (Industry *i : Industry::Iterate()) { i->stations_near.clear(); } + for (Station *st : Station::Iterate()) { st->RecomputeCatchment(true); } } /************************************************************************/ diff --git a/src/station_base.h b/src/station_base.h index b2e31a29de..bd7f00b9f9 100644 --- a/src/station_base.h +++ b/src/station_base.h @@ -501,7 +501,7 @@ public: uint GetPlatformLength(TileIndex tile, DiagDirection dir) const override; uint GetPlatformLength(TileIndex tile) const override; - void RecomputeCatchment(); + void RecomputeCatchment(bool no_clear_nearby_lists = false); static void RecomputeCatchmentForAll(); uint GetCatchmentRadius() const; From 9bdcbc5af7ea0901bebff0319626d3dcdd239dd0 Mon Sep 17 00:00:00 2001 From: translators Date: Wed, 1 Mar 2023 18:47:27 +0000 Subject: [PATCH 40/47] Update: Translations from eints french: 3 changes by glx22 --- src/lang/brazilian_portuguese.txt | 1 - src/lang/catalan.txt | 1 - src/lang/czech.txt | 1 - src/lang/danish.txt | 1 - src/lang/dutch.txt | 1 - src/lang/english_AU.txt | 1 - src/lang/english_US.txt | 1 - src/lang/estonian.txt | 1 - src/lang/finnish.txt | 1 - src/lang/french.txt | 5 +++-- src/lang/frisian.txt | 1 - src/lang/galician.txt | 1 - src/lang/german.txt | 1 - src/lang/greek.txt | 1 - src/lang/hungarian.txt | 1 - src/lang/indonesian.txt | 1 - src/lang/irish.txt | 1 - src/lang/italian.txt | 1 - src/lang/japanese.txt | 1 - src/lang/korean.txt | 1 - src/lang/latvian.txt | 1 - src/lang/luxembourgish.txt | 1 - src/lang/norwegian_bokmal.txt | 1 - src/lang/polish.txt | 1 - src/lang/portuguese.txt | 1 - src/lang/romanian.txt | 1 - src/lang/russian.txt | 1 - src/lang/serbian.txt | 1 - src/lang/simplified_chinese.txt | 1 - src/lang/slovak.txt | 1 - src/lang/spanish.txt | 1 - src/lang/spanish_MX.txt | 1 - src/lang/swedish.txt | 1 - src/lang/tamil.txt | 1 - src/lang/traditional_chinese.txt | 1 - src/lang/turkish.txt | 1 - src/lang/ukrainian.txt | 1 - src/lang/vietnamese.txt | 1 - 38 files changed, 3 insertions(+), 39 deletions(-) diff --git a/src/lang/brazilian_portuguese.txt b/src/lang/brazilian_portuguese.txt index a5c075c828..0bb7b3fc61 100644 --- a/src/lang/brazilian_portuguese.txt +++ b/src/lang/brazilian_portuguese.txt @@ -2359,7 +2359,6 @@ STR_NETWORK_CLIENT_LIST_NEW_COMPANY :(Nova empresa) STR_NETWORK_CLIENT_LIST_NEW_COMPANY_TOOLTIP :{BLACK}Cria uma nova empresa e se une a ela STR_NETWORK_CLIENT_LIST_PLAYER_ICON_SELF_TOOLTIP :{BLACK}Esse é você STR_NETWORK_CLIENT_LIST_PLAYER_ICON_HOST_TOOLTIP :{BLACK}Esse é o hospedeiro do jogo -STR_NETWORK_CLIENT_LIST_CLIENT_COMPANY_COUNT :{BLACK}{NUM} cliente{P "" s} / {NUM} empresa{P "" s} # Matches ConnectionType ###length 5 diff --git a/src/lang/catalan.txt b/src/lang/catalan.txt index 7bf877fafa..0343b42e20 100644 --- a/src/lang/catalan.txt +++ b/src/lang/catalan.txt @@ -2359,7 +2359,6 @@ STR_NETWORK_CLIENT_LIST_NEW_COMPANY :(Companyia nova STR_NETWORK_CLIENT_LIST_NEW_COMPANY_TOOLTIP :{BLACK}Crea una companyia nova i uniu-vos. STR_NETWORK_CLIENT_LIST_PLAYER_ICON_SELF_TOOLTIP :{BLACK}Aquest ets tu. STR_NETWORK_CLIENT_LIST_PLAYER_ICON_HOST_TOOLTIP :{BLACK}Aquest és l'hoste de la partida. -STR_NETWORK_CLIENT_LIST_CLIENT_COMPANY_COUNT :{BLACK}{NUM} client{P "" s} / {NUM} companyi{P a es} # Matches ConnectionType ###length 5 diff --git a/src/lang/czech.txt b/src/lang/czech.txt index eb4828360f..ff56ed24ba 100644 --- a/src/lang/czech.txt +++ b/src/lang/czech.txt @@ -2445,7 +2445,6 @@ STR_NETWORK_CLIENT_LIST_NEW_COMPANY :(Nová společn STR_NETWORK_CLIENT_LIST_NEW_COMPANY_TOOLTIP :{BLACK}Vytvořit novou společnost a připojit se do ní STR_NETWORK_CLIENT_LIST_PLAYER_ICON_SELF_TOOLTIP :{BLACK}Tohle jsi ty STR_NETWORK_CLIENT_LIST_PLAYER_ICON_HOST_TOOLTIP :{BLACK}Toto je hostitel hry -STR_NETWORK_CLIENT_LIST_CLIENT_COMPANY_COUNT :{BLACK}{NUM} klient{P "" i i} / {NUM} společnost{P “” i i} # Matches ConnectionType ###length 5 diff --git a/src/lang/danish.txt b/src/lang/danish.txt index a9452a12e8..d8f87ec570 100644 --- a/src/lang/danish.txt +++ b/src/lang/danish.txt @@ -2320,7 +2320,6 @@ STR_NETWORK_CLIENT_LIST_NEW_COMPANY :(Nyt selskab) STR_NETWORK_CLIENT_LIST_NEW_COMPANY_TOOLTIP :{BLACK}Etabler et nyt selskab og tilslut dig det STR_NETWORK_CLIENT_LIST_PLAYER_ICON_SELF_TOOLTIP :{BLACK}Dette er dig STR_NETWORK_CLIENT_LIST_PLAYER_ICON_HOST_TOOLTIP :{BLACK}Dette er værten for dette spil -STR_NETWORK_CLIENT_LIST_CLIENT_COMPANY_COUNT :{BLACK}{NUM} klient{P "" er} / {NUM} selskab{P "" er} # Matches ConnectionType ###length 5 diff --git a/src/lang/dutch.txt b/src/lang/dutch.txt index aa5b30eb0a..3033568242 100644 --- a/src/lang/dutch.txt +++ b/src/lang/dutch.txt @@ -2358,7 +2358,6 @@ STR_NETWORK_CLIENT_LIST_NEW_COMPANY :(Nieuw bedrijf) STR_NETWORK_CLIENT_LIST_NEW_COMPANY_TOOLTIP :{BLACK}Een nieuw bedrijf maken en meedoen STR_NETWORK_CLIENT_LIST_PLAYER_ICON_SELF_TOOLTIP :{BLACK}Dit ben jij STR_NETWORK_CLIENT_LIST_PLAYER_ICON_HOST_TOOLTIP :{BLACK}Dit is de host van het spel -STR_NETWORK_CLIENT_LIST_CLIENT_COMPANY_COUNT :{BLACK}{NUM} speler{P "" s} / {NUM} bedrij{P f ven} # Matches ConnectionType ###length 5 diff --git a/src/lang/english_AU.txt b/src/lang/english_AU.txt index b0cc185897..d42768a09c 100644 --- a/src/lang/english_AU.txt +++ b/src/lang/english_AU.txt @@ -2358,7 +2358,6 @@ STR_NETWORK_CLIENT_LIST_NEW_COMPANY :(New company) STR_NETWORK_CLIENT_LIST_NEW_COMPANY_TOOLTIP :{BLACK}Create a new company and join it STR_NETWORK_CLIENT_LIST_PLAYER_ICON_SELF_TOOLTIP :{BLACK}This is you STR_NETWORK_CLIENT_LIST_PLAYER_ICON_HOST_TOOLTIP :{BLACK}This is the host of the game -STR_NETWORK_CLIENT_LIST_CLIENT_COMPANY_COUNT :{BLACK}{NUM} client{P "" s} / {NUM} compan{P y ies} # Matches ConnectionType ###length 5 diff --git a/src/lang/english_US.txt b/src/lang/english_US.txt index c28c3c5494..5635986ec6 100644 --- a/src/lang/english_US.txt +++ b/src/lang/english_US.txt @@ -2358,7 +2358,6 @@ STR_NETWORK_CLIENT_LIST_NEW_COMPANY :(New company) STR_NETWORK_CLIENT_LIST_NEW_COMPANY_TOOLTIP :{BLACK}Create a new company and join it STR_NETWORK_CLIENT_LIST_PLAYER_ICON_SELF_TOOLTIP :{BLACK}This is you STR_NETWORK_CLIENT_LIST_PLAYER_ICON_HOST_TOOLTIP :{BLACK}This is the host of the game -STR_NETWORK_CLIENT_LIST_CLIENT_COMPANY_COUNT :{BLACK}{NUM} client{P "" s} / {NUM} compan{P y ies} # Matches ConnectionType ###length 5 diff --git a/src/lang/estonian.txt b/src/lang/estonian.txt index 1c6d3aaa46..37f5ec9daa 100644 --- a/src/lang/estonian.txt +++ b/src/lang/estonian.txt @@ -2415,7 +2415,6 @@ STR_NETWORK_CLIENT_LIST_NEW_COMPANY :(Uus ettevõte) STR_NETWORK_CLIENT_LIST_NEW_COMPANY_TOOLTIP :{BLACK}Loo uus ettevõte ja liitu sellega STR_NETWORK_CLIENT_LIST_PLAYER_ICON_SELF_TOOLTIP :{BLACK}Tema oled sina STR_NETWORK_CLIENT_LIST_PLAYER_ICON_HOST_TOOLTIP :{BLACK}Tema on mängu korraldaja -STR_NETWORK_CLIENT_LIST_CLIENT_COMPANY_COUNT :{BLACK}{NUM} klient{P "" i} / {NUM} ettevõt{P e teid} # Matches ConnectionType ###length 5 diff --git a/src/lang/finnish.txt b/src/lang/finnish.txt index 534b7e546e..f53b4583e3 100644 --- a/src/lang/finnish.txt +++ b/src/lang/finnish.txt @@ -2358,7 +2358,6 @@ STR_NETWORK_CLIENT_LIST_NEW_COMPANY :(Uusi yhtiö) STR_NETWORK_CLIENT_LIST_NEW_COMPANY_TOOLTIP :{BLACK}Perusta uusi yhtiö ja liity siihen STR_NETWORK_CLIENT_LIST_PLAYER_ICON_SELF_TOOLTIP :{BLACK}Tämä olet sinä STR_NETWORK_CLIENT_LIST_PLAYER_ICON_HOST_TOOLTIP :{BLACK}Tämä on pelin ylläpitäjä -STR_NETWORK_CLIENT_LIST_CLIENT_COMPANY_COUNT :{BLACK}{NUM} pelaaja{P "" a} / {NUM} yhtiö{P "" tä} # Matches ConnectionType ###length 5 diff --git a/src/lang/french.txt b/src/lang/french.txt index f8845f85c3..a46201e88d 100644 --- a/src/lang/french.txt +++ b/src/lang/french.txt @@ -2359,7 +2359,6 @@ STR_NETWORK_CLIENT_LIST_NEW_COMPANY :(Nouvelle compa STR_NETWORK_CLIENT_LIST_NEW_COMPANY_TOOLTIP :{BLACK}Créer une nouvelle compagnie et la rejoindre STR_NETWORK_CLIENT_LIST_PLAYER_ICON_SELF_TOOLTIP :{BLACK}C'est vous STR_NETWORK_CLIENT_LIST_PLAYER_ICON_HOST_TOOLTIP :{BLACK}C'est l'hôte du jeu -STR_NETWORK_CLIENT_LIST_CLIENT_COMPANY_COUNT :{BLACK}{NUM} client{P "" s} / {NUM} compagnie{P "" s} # Matches ConnectionType ###length 5 @@ -3353,6 +3352,8 @@ STR_NEWGRF_ERROR_MSG_INFO :{SILVER}{STRING STR_NEWGRF_ERROR_MSG_WARNING :{RED}Attention{NBSP}: {SILVER}{STRING} STR_NEWGRF_ERROR_MSG_ERROR :{RED}Erreur{NBSP}: {SILVER}{STRING} STR_NEWGRF_ERROR_MSG_FATAL :{RED}Erreur fatale{NBSP}: {SILVER}{STRING} +STR_NEWGRF_ERROR_FATAL_POPUP :{WHITE}Le NewGRF "{STRING}" a retourné une erreur fatale{NBSP}:{}{STRING} +STR_NEWGRF_ERROR_POPUP :{WHITE}Le NewGRF "{STRING}" a retourné une erreur{NBSP}:{}{STRING} STR_NEWGRF_ERROR_VERSION_NUMBER :{1:STRING} ne fonctionnera pas avec la version de TTDPatch rapportée par OpenTTD STR_NEWGRF_ERROR_DOS_OR_WINDOWS :{1:STRING} est conçu pour la version {2:STRING} de TTD STR_NEWGRF_ERROR_UNSET_SWITCH :{1:STRING} est conçu pour être utilisé avec {2:STRING} @@ -4528,7 +4529,7 @@ STR_TIMETABLE_CLEAR_SPEED :{BLACK}Annuler STR_TIMETABLE_CLEAR_SPEED_TOOLTIP :{BLACK}Annuler la vitesse maximale de déplacement de l'ordre sélectionné. Ctrl-clic pour annuler la vitesse de tous les ordres STR_TIMETABLE_RESET_LATENESS :{BLACK}RAZ compteur de retard -STR_TIMETABLE_RESET_LATENESS_TOOLTIP :{BLACK}Remettre à zéro le compteur de retard (le véhicule sera donc à l'heure) +STR_TIMETABLE_RESET_LATENESS_TOOLTIP :{BLACK}Remettre à zéro le compteur de retard, le véhicule sera donc à l'heure. Ctrl-clic pour remettre à zéro le groupe entier, le véhicule le plus en retard sera donc à l'heure et les autres en avance STR_TIMETABLE_AUTOFILL :{BLACK}Autoremplir STR_TIMETABLE_AUTOFILL_TOOLTIP :{BLACK}Remplir l'horaire automatiquement avec les valeurs du prochain trajet. Ctrl-clic pour essayer de préserver les temps d'attente diff --git a/src/lang/frisian.txt b/src/lang/frisian.txt index 0c97d4bbf9..e47258ed59 100644 --- a/src/lang/frisian.txt +++ b/src/lang/frisian.txt @@ -2103,7 +2103,6 @@ STR_NETWORK_CLIENT_LIST_SERVER_NAME_TOOLTIP :{BLACK}Namme fa STR_NETWORK_CLIENT_LIST_SERVER_VISIBILITY :{BLACK}Sichtberheid STR_NETWORK_CLIENT_LIST_PLAYER_NAME_QUERY_CAPTION :Dyn namme STR_NETWORK_CLIENT_LIST_PLAYER_ICON_SELF_TOOLTIP :{BLACK}Dit bisto sels -STR_NETWORK_CLIENT_LIST_CLIENT_COMPANY_COUNT :{BLACK}{NUM} klïïnt{P "" en} / {NUM} bedriuw{P "" en} # Matches ConnectionType ###length 5 diff --git a/src/lang/galician.txt b/src/lang/galician.txt index a7a340030c..bc0b0835cb 100644 --- a/src/lang/galician.txt +++ b/src/lang/galician.txt @@ -2359,7 +2359,6 @@ STR_NETWORK_CLIENT_LIST_NEW_COMPANY :(Nova compañí STR_NETWORK_CLIENT_LIST_NEW_COMPANY_TOOLTIP :{BLACK}Crear unha nova compañía e unirte a ela STR_NETWORK_CLIENT_LIST_PLAYER_ICON_SELF_TOOLTIP :{BLACK}Este es ti STR_NETWORK_CLIENT_LIST_PLAYER_ICON_HOST_TOOLTIP :{BLACK}Este é o anfitrión da partida -STR_NETWORK_CLIENT_LIST_CLIENT_COMPANY_COUNT :{BLACK}{NUM} cliente{P "" s} / {NUM} compañía{P "" "s"} # Matches ConnectionType ###length 5 diff --git a/src/lang/german.txt b/src/lang/german.txt index e7229ec163..73feefe433 100644 --- a/src/lang/german.txt +++ b/src/lang/german.txt @@ -2357,7 +2357,6 @@ STR_NETWORK_CLIENT_LIST_NEW_COMPANY :(Neue Firma) STR_NETWORK_CLIENT_LIST_NEW_COMPANY_TOOLTIP :{BLACK}Eine neue Firma gründen und beitreten STR_NETWORK_CLIENT_LIST_PLAYER_ICON_SELF_TOOLTIP :{BLACK}Das sind Sie STR_NETWORK_CLIENT_LIST_PLAYER_ICON_HOST_TOOLTIP :{BLACK}Dies ist der Host des Spiels -STR_NETWORK_CLIENT_LIST_CLIENT_COMPANY_COUNT :{BLACK}{NUM} Client{P "" s} / {NUM} Firm{P a en} # Matches ConnectionType ###length 5 diff --git a/src/lang/greek.txt b/src/lang/greek.txt index 11b3a63f64..b9e88f06cb 100644 --- a/src/lang/greek.txt +++ b/src/lang/greek.txt @@ -2462,7 +2462,6 @@ STR_NETWORK_CLIENT_LIST_NEW_COMPANY :(Νέα ετα STR_NETWORK_CLIENT_LIST_NEW_COMPANY_TOOLTIP :{BLACK}Δημιουργήστε μία νέα εταιρία και συμμετέχετε σε αυτήν STR_NETWORK_CLIENT_LIST_PLAYER_ICON_SELF_TOOLTIP :{BLACK}Αυτός/η είστε εσείς STR_NETWORK_CLIENT_LIST_PLAYER_ICON_HOST_TOOLTIP :{BLACK}Αυτός/η είναι ο/η οικοδεσπότης του παιχνιδιού -STR_NETWORK_CLIENT_LIST_CLIENT_COMPANY_COUNT :{BLACK}{NUM} πελάτης/ες / {NUM} εταιρία/ες # Matches ConnectionType ###length 5 diff --git a/src/lang/hungarian.txt b/src/lang/hungarian.txt index c1e84eda9a..79d8c390cc 100644 --- a/src/lang/hungarian.txt +++ b/src/lang/hungarian.txt @@ -2420,7 +2420,6 @@ STR_NETWORK_CLIENT_LIST_NEW_COMPANY :(Új vállalat) STR_NETWORK_CLIENT_LIST_NEW_COMPANY_TOOLTIP :{BLACK}Hozz létre egy új vállalatot és csatlakozz STR_NETWORK_CLIENT_LIST_PLAYER_ICON_SELF_TOOLTIP :{BLACK}Ez vagy te STR_NETWORK_CLIENT_LIST_PLAYER_ICON_HOST_TOOLTIP :{BLACK}Ez a játék elindítója -STR_NETWORK_CLIENT_LIST_CLIENT_COMPANY_COUNT :{BLACK}{NUM} kliens / {NUM} vállalat # Matches ConnectionType ###length 5 diff --git a/src/lang/indonesian.txt b/src/lang/indonesian.txt index 6bdcea6e62..9f4e22dcbd 100644 --- a/src/lang/indonesian.txt +++ b/src/lang/indonesian.txt @@ -2356,7 +2356,6 @@ STR_NETWORK_CLIENT_LIST_NEW_COMPANY :(Perusahaan bar STR_NETWORK_CLIENT_LIST_NEW_COMPANY_TOOLTIP :{BLACK}Ciptakan perusahaan baru dan bergabung STR_NETWORK_CLIENT_LIST_PLAYER_ICON_SELF_TOOLTIP :{BLACK}Ini adalah Anda STR_NETWORK_CLIENT_LIST_PLAYER_ICON_HOST_TOOLTIP :{BLACK}Ini adalah hos permainan -STR_NETWORK_CLIENT_LIST_CLIENT_COMPANY_COUNT :{BLACK}{NUM} klien / {NUM} Perusahaan # Matches ConnectionType ###length 5 diff --git a/src/lang/irish.txt b/src/lang/irish.txt index e6e990baa5..3adc714f79 100644 --- a/src/lang/irish.txt +++ b/src/lang/irish.txt @@ -2313,7 +2313,6 @@ STR_NETWORK_CLIENT_LIST_NEW_COMPANY :(Cuideachta nua STR_NETWORK_CLIENT_LIST_NEW_COMPANY_TOOLTIP :{BLACK}Cruthaigh cuideachta nua agus téigh isteach ann STR_NETWORK_CLIENT_LIST_PLAYER_ICON_SELF_TOOLTIP :{BLACK}Seo tusa STR_NETWORK_CLIENT_LIST_PLAYER_ICON_HOST_TOOLTIP :{BLACK}Is é seo óstach an chluiche -STR_NETWORK_CLIENT_LIST_CLIENT_COMPANY_COUNT :{BLACK}{NUM} {P ch ch ch gc c}liant / {NUM} {P ch ch ch gc c}uideachta # Matches ConnectionType ###length 5 diff --git a/src/lang/italian.txt b/src/lang/italian.txt index e6adfb495b..fd6765b562 100644 --- a/src/lang/italian.txt +++ b/src/lang/italian.txt @@ -2393,7 +2393,6 @@ STR_NETWORK_CLIENT_LIST_NEW_COMPANY :(Nuova compagni STR_NETWORK_CLIENT_LIST_NEW_COMPANY_TOOLTIP :{BLACK}Crea una nuova compagnia e controllala STR_NETWORK_CLIENT_LIST_PLAYER_ICON_SELF_TOOLTIP :{BLACK}Questo sei tu STR_NETWORK_CLIENT_LIST_PLAYER_ICON_HOST_TOOLTIP :{BLACK}Questo è l'host del gioco -STR_NETWORK_CLIENT_LIST_CLIENT_COMPANY_COUNT :{BLACK}{NUM} client{P "" s} / {NUM} compan{P y ies} # Matches ConnectionType ###length 5 diff --git a/src/lang/japanese.txt b/src/lang/japanese.txt index 7b7e754adb..169611e3d4 100644 --- a/src/lang/japanese.txt +++ b/src/lang/japanese.txt @@ -2342,7 +2342,6 @@ STR_NETWORK_CLIENT_LIST_NEW_COMPANY :(新規会社 STR_NETWORK_CLIENT_LIST_NEW_COMPANY_TOOLTIP :{BLACK}新しく会社を設立し参画します STR_NETWORK_CLIENT_LIST_PLAYER_ICON_SELF_TOOLTIP :{BLACK}これはあなたです STR_NETWORK_CLIENT_LIST_PLAYER_ICON_HOST_TOOLTIP :{BLACK}ゲームのホストです -STR_NETWORK_CLIENT_LIST_CLIENT_COMPANY_COUNT :{BLACK}{NUM} 個のクライアント / {NUM} 個の企業 # Matches ConnectionType ###length 5 diff --git a/src/lang/korean.txt b/src/lang/korean.txt index 3c54ce74f2..dd0d53f5c6 100644 --- a/src/lang/korean.txt +++ b/src/lang/korean.txt @@ -2359,7 +2359,6 @@ STR_NETWORK_CLIENT_LIST_NEW_COMPANY :(새 회사) STR_NETWORK_CLIENT_LIST_NEW_COMPANY_TOOLTIP :{BLACK}새 회사를 만듭니다 STR_NETWORK_CLIENT_LIST_PLAYER_ICON_SELF_TOOLTIP :{BLACK}당신입니다 STR_NETWORK_CLIENT_LIST_PLAYER_ICON_HOST_TOOLTIP :{BLACK}이 게임을 연 사람입니다 -STR_NETWORK_CLIENT_LIST_CLIENT_COMPANY_COUNT :{BLACK}접속자 {NUM}명 / 회사 {NUM}개 # Matches ConnectionType ###length 5 diff --git a/src/lang/latvian.txt b/src/lang/latvian.txt index 2237f672a0..0001724c0a 100644 --- a/src/lang/latvian.txt +++ b/src/lang/latvian.txt @@ -2362,7 +2362,6 @@ STR_NETWORK_CLIENT_LIST_NEW_COMPANY :(Jauna kompāni STR_NETWORK_CLIENT_LIST_NEW_COMPANY_TOOLTIP :{BLACK}Izveidot jaunu kompāniju un pievienoties tai STR_NETWORK_CLIENT_LIST_PLAYER_ICON_SELF_TOOLTIP :{BLACK}Tas esat jūs STR_NETWORK_CLIENT_LIST_PLAYER_ICON_HOST_TOOLTIP :{BLACK}Šis ir spēles īpašnieks -STR_NETWORK_CLIENT_LIST_CLIENT_COMPANY_COUNT :{BLACK}{NUM} klient{P s а u} / {NUM} kompānij{P a u as} # Matches ConnectionType ###length 5 diff --git a/src/lang/luxembourgish.txt b/src/lang/luxembourgish.txt index a80e438777..7cedd8e167 100644 --- a/src/lang/luxembourgish.txt +++ b/src/lang/luxembourgish.txt @@ -2358,7 +2358,6 @@ STR_NETWORK_CLIENT_LIST_NEW_COMPANY :(Nei Firma) STR_NETWORK_CLIENT_LIST_NEW_COMPANY_TOOLTIP :{BLACK}Eng nei Firma erstellen an hier bäitrieden STR_NETWORK_CLIENT_LIST_PLAYER_ICON_SELF_TOOLTIP :{BLACK}Dat bass du STR_NETWORK_CLIENT_LIST_PLAYER_ICON_HOST_TOOLTIP :{BLACK}Dest ass den Host vun der Partie -STR_NETWORK_CLIENT_LIST_CLIENT_COMPANY_COUNT :{BLACK}{NUM} Client{P "" en} / {NUM} Firm{P a en} # Matches ConnectionType ###length 5 diff --git a/src/lang/norwegian_bokmal.txt b/src/lang/norwegian_bokmal.txt index e2679130f6..2b287921ed 100644 --- a/src/lang/norwegian_bokmal.txt +++ b/src/lang/norwegian_bokmal.txt @@ -2330,7 +2330,6 @@ STR_NETWORK_CLIENT_LIST_NEW_COMPANY :(Nytt firma) STR_NETWORK_CLIENT_LIST_NEW_COMPANY_TOOLTIP :{BLACK}Opprett et nytt firma og bli med i det STR_NETWORK_CLIENT_LIST_PLAYER_ICON_SELF_TOOLTIP :{BLACK}Dette er deg STR_NETWORK_CLIENT_LIST_PLAYER_ICON_HOST_TOOLTIP :{BLACK}Dette er verten for spillet -STR_NETWORK_CLIENT_LIST_CLIENT_COMPANY_COUNT :{BLACK}{NUM} klient{P "" s} / {NUM} firma{P et er} # Matches ConnectionType ###length 5 diff --git a/src/lang/polish.txt b/src/lang/polish.txt index 2ce044765e..fc7e897559 100644 --- a/src/lang/polish.txt +++ b/src/lang/polish.txt @@ -2738,7 +2738,6 @@ STR_NETWORK_CLIENT_LIST_NEW_COMPANY :(Nowa firma) STR_NETWORK_CLIENT_LIST_NEW_COMPANY_TOOLTIP :{BLACK}Utwórz nową firmę i dołącz do niej STR_NETWORK_CLIENT_LIST_PLAYER_ICON_SELF_TOOLTIP :{BLACK}To jesteś ty STR_NETWORK_CLIENT_LIST_PLAYER_ICON_HOST_TOOLTIP :{BLACK}To jest gospodarz gry -STR_NETWORK_CLIENT_LIST_CLIENT_COMPANY_COUNT :{BLACK}{NUM} klient{P "" ów ów} / {NUM} firm{P a y ""} # Matches ConnectionType ###length 5 diff --git a/src/lang/portuguese.txt b/src/lang/portuguese.txt index 75346f169f..80ccb57ec7 100644 --- a/src/lang/portuguese.txt +++ b/src/lang/portuguese.txt @@ -2359,7 +2359,6 @@ STR_NETWORK_CLIENT_LIST_NEW_COMPANY :(Nova empresa) STR_NETWORK_CLIENT_LIST_NEW_COMPANY_TOOLTIP :{BLACK}Criar uma nova empresa e juntar-se a ela STR_NETWORK_CLIENT_LIST_PLAYER_ICON_SELF_TOOLTIP :{BLACK}Este é você STR_NETWORK_CLIENT_LIST_PLAYER_ICON_HOST_TOOLTIP :{BLACK}Este é o anfitrião do jogo -STR_NETWORK_CLIENT_LIST_CLIENT_COMPANY_COUNT :{BLACK}{NUM} cliente{P "" s} / {NUM} companhi{P a as} # Matches ConnectionType ###length 5 diff --git a/src/lang/romanian.txt b/src/lang/romanian.txt index 65b342791c..bbe7938fb5 100644 --- a/src/lang/romanian.txt +++ b/src/lang/romanian.txt @@ -2358,7 +2358,6 @@ STR_NETWORK_CLIENT_LIST_NEW_COMPANY :(Companie nouă STR_NETWORK_CLIENT_LIST_NEW_COMPANY_TOOLTIP :{BLACK}Creează o nouă companie și intră în ea STR_NETWORK_CLIENT_LIST_PLAYER_ICON_SELF_TOOLTIP :{BLACK}Aici ești tu STR_NETWORK_CLIENT_LIST_PLAYER_ICON_HOST_TOOLTIP :{BLACK}Aceasta este gazda jocului -STR_NETWORK_CLIENT_LIST_CLIENT_COMPANY_COUNT :{BLACK}{NUM} {P client clienți "de clienți"} / {NUM}{P companie companii "de companii"} # Matches ConnectionType ###length 5 diff --git a/src/lang/russian.txt b/src/lang/russian.txt index e8185b1da6..df55bf69a2 100644 --- a/src/lang/russian.txt +++ b/src/lang/russian.txt @@ -2509,7 +2509,6 @@ STR_NETWORK_CLIENT_LIST_NEW_COMPANY :(Новая к STR_NETWORK_CLIENT_LIST_NEW_COMPANY_TOOLTIP :{BLACK}Основать новую транспортную компанию и присоединиться к ней STR_NETWORK_CLIENT_LIST_PLAYER_ICON_SELF_TOOLTIP :{BLACK}Это вы! STR_NETWORK_CLIENT_LIST_PLAYER_ICON_HOST_TOOLTIP :{BLACK}Это организатор игры -STR_NETWORK_CLIENT_LIST_CLIENT_COMPANY_COUNT :{BLACK}{NUM} клиент{P "" а ов} / {NUM} компани{P я и й} # Matches ConnectionType ###length 5 diff --git a/src/lang/serbian.txt b/src/lang/serbian.txt index fcc09de47f..24c8352577 100644 --- a/src/lang/serbian.txt +++ b/src/lang/serbian.txt @@ -2551,7 +2551,6 @@ STR_NETWORK_CLIENT_LIST_NEW_COMPANY :(novo preduzeć STR_NETWORK_CLIENT_LIST_NEW_COMPANY_TOOLTIP :{BLACK}Napravi novo preduzeće i pridruži mu se STR_NETWORK_CLIENT_LIST_PLAYER_ICON_SELF_TOOLTIP :{BLACK}Ovo si ti STR_NETWORK_CLIENT_LIST_PLAYER_ICON_HOST_TOOLTIP :{BLACK}Ovo je domaćin igre -STR_NETWORK_CLIENT_LIST_CLIENT_COMPANY_COUNT :{BLACK}{NUM} klijen{P t ata ata} / {NUM} preduzeć{P e a a} # Matches ConnectionType ###length 5 diff --git a/src/lang/simplified_chinese.txt b/src/lang/simplified_chinese.txt index e241e3708b..38ab3b03e9 100644 --- a/src/lang/simplified_chinese.txt +++ b/src/lang/simplified_chinese.txt @@ -2358,7 +2358,6 @@ STR_NETWORK_CLIENT_LIST_NEW_COMPANY :(新公司) STR_NETWORK_CLIENT_LIST_NEW_COMPANY_TOOLTIP :{BLACK}新建并加入公司 STR_NETWORK_CLIENT_LIST_PLAYER_ICON_SELF_TOOLTIP :{BLACK}这是你 STR_NETWORK_CLIENT_LIST_PLAYER_ICON_HOST_TOOLTIP :{BLACK}这里是游戏的主机 -STR_NETWORK_CLIENT_LIST_CLIENT_COMPANY_COUNT :{BLACK}{NUM}个客户端 / {NUM}个公司 # Matches ConnectionType ###length 5 diff --git a/src/lang/slovak.txt b/src/lang/slovak.txt index b7b531ddcd..a154237e0d 100644 --- a/src/lang/slovak.txt +++ b/src/lang/slovak.txt @@ -2426,7 +2426,6 @@ STR_NETWORK_CLIENT_LIST_NEW_COMPANY :(Nová spoločn STR_NETWORK_CLIENT_LIST_NEW_COMPANY_TOOLTIP :{BLACK}Založiť novú spoločnosť a pripojiť sa k nej STR_NETWORK_CLIENT_LIST_PLAYER_ICON_SELF_TOOLTIP :{BLACK}Toto ste vy STR_NETWORK_CLIENT_LIST_PLAYER_ICON_HOST_TOOLTIP :{BLACK}Toto je hosť hry -STR_NETWORK_CLIENT_LIST_CLIENT_COMPANY_COUNT :{BLACK}{NUM} klient{P "" "i" "ov"} / {NUM} spoločnos{P "ť" "ti" "tí"} # Matches ConnectionType ###length 5 diff --git a/src/lang/spanish.txt b/src/lang/spanish.txt index 8e551b3453..653d00d7ea 100644 --- a/src/lang/spanish.txt +++ b/src/lang/spanish.txt @@ -2359,7 +2359,6 @@ STR_NETWORK_CLIENT_LIST_NEW_COMPANY :(Nueva empresa) STR_NETWORK_CLIENT_LIST_NEW_COMPANY_TOOLTIP :{BLACK}Crea una nueva empresa y te une a ella STR_NETWORK_CLIENT_LIST_PLAYER_ICON_SELF_TOOLTIP :{BLACK}Éste eres tú STR_NETWORK_CLIENT_LIST_PLAYER_ICON_HOST_TOOLTIP :{BLACK}Éste es el servidor de la partida -STR_NETWORK_CLIENT_LIST_CLIENT_COMPANY_COUNT :{BLACK}{NUM} cliente{P "" s} / {NUM} empresa{P "" s} # Matches ConnectionType ###length 5 diff --git a/src/lang/spanish_MX.txt b/src/lang/spanish_MX.txt index d09e8e3ab4..cfbfb84c1b 100644 --- a/src/lang/spanish_MX.txt +++ b/src/lang/spanish_MX.txt @@ -2359,7 +2359,6 @@ STR_NETWORK_CLIENT_LIST_NEW_COMPANY :(Nueva empresa) STR_NETWORK_CLIENT_LIST_NEW_COMPANY_TOOLTIP :{BLACK}Crear nueva empresa y unirse a ella STR_NETWORK_CLIENT_LIST_PLAYER_ICON_SELF_TOOLTIP :{BLACK}Este eres tú STR_NETWORK_CLIENT_LIST_PLAYER_ICON_HOST_TOOLTIP :{BLACK}Este es el host del juego -STR_NETWORK_CLIENT_LIST_CLIENT_COMPANY_COUNT :{BLACK}{NUM} cliente{P "" s}/{NUM} empresa{P "" s} # Matches ConnectionType ###length 5 diff --git a/src/lang/swedish.txt b/src/lang/swedish.txt index 243ebc4bca..319670cd7d 100644 --- a/src/lang/swedish.txt +++ b/src/lang/swedish.txt @@ -2358,7 +2358,6 @@ STR_NETWORK_CLIENT_LIST_NEW_COMPANY :(Nytt företag) STR_NETWORK_CLIENT_LIST_NEW_COMPANY_TOOLTIP :{BLACK}Skapa ett nytt företag och gå med i det STR_NETWORK_CLIENT_LIST_PLAYER_ICON_SELF_TOOLTIP :{BLACK}Det här är du STR_NETWORK_CLIENT_LIST_PLAYER_ICON_HOST_TOOLTIP :{BLACK}Det här är spelets värd -STR_NETWORK_CLIENT_LIST_CLIENT_COMPANY_COUNT :{BLACK}{NUM} klient{P "" er} / {NUM} företag # Matches ConnectionType ###length 5 diff --git a/src/lang/tamil.txt b/src/lang/tamil.txt index 1a710967cb..d56de395a6 100644 --- a/src/lang/tamil.txt +++ b/src/lang/tamil.txt @@ -2098,7 +2098,6 @@ STR_NETWORK_CLIENT_LIST_NEW_COMPANY :(புதி STR_NETWORK_CLIENT_LIST_NEW_COMPANY_TOOLTIP :{BLACK}புதிய நிறுவனத்தினை நிறுவி விளையாடு STR_NETWORK_CLIENT_LIST_PLAYER_ICON_SELF_TOOLTIP :இது நீன்கள் STR_NETWORK_CLIENT_LIST_PLAYER_ICON_HOST_TOOLTIP :{BLACK}இது விலையாட்டின் வழங்குபவர் -STR_NETWORK_CLIENT_LIST_CLIENT_COMPANY_COUNT :{BLACK}{NUM} வாடிக்கையாளர்{P "" கள்} / {NUM} நிறுவன{P ம் ங்கள்} # Matches ConnectionType ###length 5 diff --git a/src/lang/traditional_chinese.txt b/src/lang/traditional_chinese.txt index 1ab1387228..64f841dc00 100644 --- a/src/lang/traditional_chinese.txt +++ b/src/lang/traditional_chinese.txt @@ -2358,7 +2358,6 @@ STR_NETWORK_CLIENT_LIST_NEW_COMPANY :(新公司) STR_NETWORK_CLIENT_LIST_NEW_COMPANY_TOOLTIP :{BLACK}創立並加入一個新公司 STR_NETWORK_CLIENT_LIST_PLAYER_ICON_SELF_TOOLTIP :{BLACK}這是你 STR_NETWORK_CLIENT_LIST_PLAYER_ICON_HOST_TOOLTIP :{BLACK}這是遊戲的主持端 -STR_NETWORK_CLIENT_LIST_CLIENT_COMPANY_COUNT :{BLACK}{NUM}個用戶端 / {NUM}所公司 # Matches ConnectionType ###length 5 diff --git a/src/lang/turkish.txt b/src/lang/turkish.txt index 2e081c3e9b..a4d8ea5e1c 100644 --- a/src/lang/turkish.txt +++ b/src/lang/turkish.txt @@ -2359,7 +2359,6 @@ STR_NETWORK_CLIENT_LIST_NEW_COMPANY :(Yeni şirket) STR_NETWORK_CLIENT_LIST_NEW_COMPANY_TOOLTIP :{BLACK}Yeni bir şirket oluştur ve ona katıl STR_NETWORK_CLIENT_LIST_PLAYER_ICON_SELF_TOOLTIP :{BLACK}Bu sensin STR_NETWORK_CLIENT_LIST_PLAYER_ICON_HOST_TOOLTIP :{BLACK}Bu, oyunun ev sahibi -STR_NETWORK_CLIENT_LIST_CLIENT_COMPANY_COUNT :{BLACK}{NUM} oyuncu / {NUM} şirket # Matches ConnectionType ###length 5 diff --git a/src/lang/ukrainian.txt b/src/lang/ukrainian.txt index ead9afb663..e82fca0b27 100644 --- a/src/lang/ukrainian.txt +++ b/src/lang/ukrainian.txt @@ -2484,7 +2484,6 @@ STR_NETWORK_CLIENT_LIST_NEW_COMPANY :(Нова ко STR_NETWORK_CLIENT_LIST_NEW_COMPANY_TOOLTIP :{BLACK}Створити нову компанію і приєднатись до неї STR_NETWORK_CLIENT_LIST_PLAYER_ICON_SELF_TOOLTIP :{BLACK}Це ти STR_NETWORK_CLIENT_LIST_PLAYER_ICON_HOST_TOOLTIP :{BLACK}Організатор гри -STR_NETWORK_CLIENT_LIST_CLIENT_COMPANY_COUNT :{BLACK}{NUM} клієнт{P "" и ів} / {NUM} компан{P ія ії ій} # Matches ConnectionType ###length 5 diff --git a/src/lang/vietnamese.txt b/src/lang/vietnamese.txt index c74363d550..fe3d19ff7f 100644 --- a/src/lang/vietnamese.txt +++ b/src/lang/vietnamese.txt @@ -2358,7 +2358,6 @@ STR_NETWORK_CLIENT_LIST_NEW_COMPANY :(Công ty mới STR_NETWORK_CLIENT_LIST_NEW_COMPANY_TOOLTIP :{BLACK}Khai sinh một công ty và gia nhập STR_NETWORK_CLIENT_LIST_PLAYER_ICON_SELF_TOOLTIP :{BLACK}Đây là bạn STR_NETWORK_CLIENT_LIST_PLAYER_ICON_HOST_TOOLTIP :{BLACK}Đây là người tổ chức ván chơi -STR_NETWORK_CLIENT_LIST_CLIENT_COMPANY_COUNT :{BLACK}{NUM} người chơi / {NUM} công ty # Matches ConnectionType ###length 5 From 2a2e51765a073dfc6280ad3ee2bb9fc73c6c7947 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lo=C3=AFc=20Guilloux?= Date: Wed, 1 Mar 2023 23:53:50 +0100 Subject: [PATCH 41/47] Cleanup 54db96b: Left-over function declaration (#10528) --- src/order_base.h | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/order_base.h b/src/order_base.h index 836ceb4b04..78b4b1ff60 100644 --- a/src/order_base.h +++ b/src/order_base.h @@ -262,8 +262,6 @@ private: friend void AfterLoadVehicles(bool part_of_load); ///< For instantiating the shared vehicle chain friend SaveLoadTable GetOrderListDescription(); ///< Saving and loading of order lists. - StationID GetBestLoadableNext(const Vehicle *v, const Order *o1, const Order *o2) const; - Order *first; ///< First order of the order list. VehicleOrderID num_orders; ///< NOSAVE: How many orders there are in the list. VehicleOrderID num_manual_orders; ///< NOSAVE: How many manually added orders are there in the list. From 58aa7692820733f20ae6911147c8806df3f08e17 Mon Sep 17 00:00:00 2001 From: translators Date: Thu, 2 Mar 2023 18:50:41 +0000 Subject: [PATCH 42/47] Update: Translations from eints vietnamese: 2 changes by KhoiCanDev italian: 2 changes by Rivarossi russian: 2 changes by Ln-Wolf turkish: 3 changes by EndChapter portuguese: 2 changes by azulcosta --- src/lang/italian.txt | 2 ++ src/lang/portuguese.txt | 2 ++ src/lang/russian.txt | 2 ++ src/lang/turkish.txt | 4 +++- src/lang/vietnamese.txt | 2 ++ 5 files changed, 11 insertions(+), 1 deletion(-) diff --git a/src/lang/italian.txt b/src/lang/italian.txt index fd6765b562..4c3c11dc09 100644 --- a/src/lang/italian.txt +++ b/src/lang/italian.txt @@ -2393,6 +2393,8 @@ STR_NETWORK_CLIENT_LIST_NEW_COMPANY :(Nuova compagni STR_NETWORK_CLIENT_LIST_NEW_COMPANY_TOOLTIP :{BLACK}Crea una nuova compagnia e controllala STR_NETWORK_CLIENT_LIST_PLAYER_ICON_SELF_TOOLTIP :{BLACK}Questo sei tu STR_NETWORK_CLIENT_LIST_PLAYER_ICON_HOST_TOOLTIP :{BLACK}Questo è l'host del gioco +STR_NETWORK_CLIENT_LIST_CLIENT_COMPANY_COUNT :{BLACK}{NUM} client{P "" s} - {NUM}/{NUM} compagni{P a e} +STR_NETWORK_CLIENT_LIST_CLIENT_COMPANY_COUNT_TOOLTIP :{BLACK}Il numero di client attualmente connessi, il numero di compagnie e il numero massimo di compagnie consentito dall'amministratore del server. # Matches ConnectionType ###length 5 diff --git a/src/lang/portuguese.txt b/src/lang/portuguese.txt index 80ccb57ec7..74dda44ed8 100644 --- a/src/lang/portuguese.txt +++ b/src/lang/portuguese.txt @@ -2359,6 +2359,8 @@ STR_NETWORK_CLIENT_LIST_NEW_COMPANY :(Nova empresa) STR_NETWORK_CLIENT_LIST_NEW_COMPANY_TOOLTIP :{BLACK}Criar uma nova empresa e juntar-se a ela STR_NETWORK_CLIENT_LIST_PLAYER_ICON_SELF_TOOLTIP :{BLACK}Este é você STR_NETWORK_CLIENT_LIST_PLAYER_ICON_HOST_TOOLTIP :{BLACK}Este é o anfitrião do jogo +STR_NETWORK_CLIENT_LIST_CLIENT_COMPANY_COUNT :{BLACK}{NUM} cliente{P "" s} - {NUM}/{NUM} companhi{P a as} +STR_NETWORK_CLIENT_LIST_CLIENT_COMPANY_COUNT_TOOLTIP :{BLACK}O número de clientes ligados atualmente, número de empresas e número máximo de empresas permitido pelo administrador do servidor # Matches ConnectionType ###length 5 diff --git a/src/lang/russian.txt b/src/lang/russian.txt index df55bf69a2..cb8c9434b4 100644 --- a/src/lang/russian.txt +++ b/src/lang/russian.txt @@ -2509,6 +2509,8 @@ STR_NETWORK_CLIENT_LIST_NEW_COMPANY :(Новая к STR_NETWORK_CLIENT_LIST_NEW_COMPANY_TOOLTIP :{BLACK}Основать новую транспортную компанию и присоединиться к ней STR_NETWORK_CLIENT_LIST_PLAYER_ICON_SELF_TOOLTIP :{BLACK}Это вы! STR_NETWORK_CLIENT_LIST_PLAYER_ICON_HOST_TOOLTIP :{BLACK}Это организатор игры +STR_NETWORK_CLIENT_LIST_CLIENT_COMPANY_COUNT :{BLACK}Клиентов: {NUM}; компаний: {NUM}/{NUM} +STR_NETWORK_CLIENT_LIST_CLIENT_COMPANY_COUNT_TOOLTIP :{BLACK}Количество подключённых в данный момент клиентов, существующих компаний и максимально допустимое количество компаний # Matches ConnectionType ###length 5 diff --git a/src/lang/turkish.txt b/src/lang/turkish.txt index a4d8ea5e1c..007c60a238 100644 --- a/src/lang/turkish.txt +++ b/src/lang/turkish.txt @@ -2359,6 +2359,8 @@ STR_NETWORK_CLIENT_LIST_NEW_COMPANY :(Yeni şirket) STR_NETWORK_CLIENT_LIST_NEW_COMPANY_TOOLTIP :{BLACK}Yeni bir şirket oluştur ve ona katıl STR_NETWORK_CLIENT_LIST_PLAYER_ICON_SELF_TOOLTIP :{BLACK}Bu sensin STR_NETWORK_CLIENT_LIST_PLAYER_ICON_HOST_TOOLTIP :{BLACK}Bu, oyunun ev sahibi +STR_NETWORK_CLIENT_LIST_CLIENT_COMPANY_COUNT :{BLACK}{NUM} oyuncu - {NUM}/{NUM} şirket +STR_NETWORK_CLIENT_LIST_CLIENT_COMPANY_COUNT_TOOLTIP :{BLACK}Şu anda bağlı olan istemci sayısı, şirket sayısı ve sunucu sahibi tarafından izin verilen maksimum şirket sayısı # Matches ConnectionType ###length 5 @@ -4625,7 +4627,7 @@ STR_SCREENSHOT_MINIMAP_SCREENSHOT :{BLACK}Küçük # Script Parameters STR_AI_SETTINGS_CAPTION :{WHITE}{STRING} Parametreler -STR_AI_SETTINGS_CAPTION_AI :{WHITE}YZ Parametreleri +STR_AI_SETTINGS_CAPTION_AI :YZ STR_AI_SETTINGS_CAPTION_GAMESCRIPT :Oyun Betiği STR_AI_SETTINGS_CLOSE :{BLACK}Kapat STR_AI_SETTINGS_RESET :{BLACK}Yeniden başlat diff --git a/src/lang/vietnamese.txt b/src/lang/vietnamese.txt index fe3d19ff7f..81fb0e7fdb 100644 --- a/src/lang/vietnamese.txt +++ b/src/lang/vietnamese.txt @@ -2358,6 +2358,8 @@ STR_NETWORK_CLIENT_LIST_NEW_COMPANY :(Công ty mới STR_NETWORK_CLIENT_LIST_NEW_COMPANY_TOOLTIP :{BLACK}Khai sinh một công ty và gia nhập STR_NETWORK_CLIENT_LIST_PLAYER_ICON_SELF_TOOLTIP :{BLACK}Đây là bạn STR_NETWORK_CLIENT_LIST_PLAYER_ICON_HOST_TOOLTIP :{BLACK}Đây là người tổ chức ván chơi +STR_NETWORK_CLIENT_LIST_CLIENT_COMPANY_COUNT :{BLACK}{NUM} nguời chơi - {NUM}/{NUM} công ty +STR_NETWORK_CLIENT_LIST_CLIENT_COMPANY_COUNT_TOOLTIP :{BLACK}Số lượng máy trạm, số lượng công ty và số lượng công ty tối đa mà quản lý máy chủ cho phép # Matches ConnectionType ###length 5 From ec8b2836dfdf04dd978b1dc265eeacf5f9d851bf Mon Sep 17 00:00:00 2001 From: translators Date: Fri, 3 Mar 2023 18:44:33 +0000 Subject: [PATCH 43/47] Update: Translations from eints english (us): 2 changes by 2TallTyler dutch: 2 changes by Afoklala polish: 3 changes by pAter-exe --- src/lang/dutch.txt | 2 ++ src/lang/english_US.txt | 2 ++ src/lang/polish.txt | 4 +++- 3 files changed, 7 insertions(+), 1 deletion(-) diff --git a/src/lang/dutch.txt b/src/lang/dutch.txt index 3033568242..ec1cf81bd1 100644 --- a/src/lang/dutch.txt +++ b/src/lang/dutch.txt @@ -2358,6 +2358,8 @@ STR_NETWORK_CLIENT_LIST_NEW_COMPANY :(Nieuw bedrijf) STR_NETWORK_CLIENT_LIST_NEW_COMPANY_TOOLTIP :{BLACK}Een nieuw bedrijf maken en meedoen STR_NETWORK_CLIENT_LIST_PLAYER_ICON_SELF_TOOLTIP :{BLACK}Dit ben jij STR_NETWORK_CLIENT_LIST_PLAYER_ICON_HOST_TOOLTIP :{BLACK}Dit is de host van het spel +STR_NETWORK_CLIENT_LIST_CLIENT_COMPANY_COUNT :{BLACK}{NUM} speler{P "" s} - {NUM}/{NUM} bedrij{P f ven} +STR_NETWORK_CLIENT_LIST_CLIENT_COMPANY_COUNT_TOOLTIP :{BLACK}Het aantal momenteel verbonden clients, het aantal bedrijven en het maximale aantal toegestane bedrijven door de serverbeheerder # Matches ConnectionType ###length 5 diff --git a/src/lang/english_US.txt b/src/lang/english_US.txt index 5635986ec6..0afe772262 100644 --- a/src/lang/english_US.txt +++ b/src/lang/english_US.txt @@ -2358,6 +2358,8 @@ STR_NETWORK_CLIENT_LIST_NEW_COMPANY :(New company) STR_NETWORK_CLIENT_LIST_NEW_COMPANY_TOOLTIP :{BLACK}Create a new company and join it STR_NETWORK_CLIENT_LIST_PLAYER_ICON_SELF_TOOLTIP :{BLACK}This is you STR_NETWORK_CLIENT_LIST_PLAYER_ICON_HOST_TOOLTIP :{BLACK}This is the host of the game +STR_NETWORK_CLIENT_LIST_CLIENT_COMPANY_COUNT :{BLACK}{NUM} client{P "" s} - {NUM}/{NUM} compan{P y ies} +STR_NETWORK_CLIENT_LIST_CLIENT_COMPANY_COUNT_TOOLTIP :{BLACK}The number of currently connected clients, number of companies and maximum number of companies allowed by the server administrator # Matches ConnectionType ###length 5 diff --git a/src/lang/polish.txt b/src/lang/polish.txt index fc7e897559..7416412022 100644 --- a/src/lang/polish.txt +++ b/src/lang/polish.txt @@ -2679,7 +2679,7 @@ STR_NETWORK_START_SERVER_NUMBER_OF_CLIENTS :{BLACK}Dopuszcz STR_NETWORK_START_SERVER_NUMBER_OF_CLIENTS_TOOLTIP :{BLACK}Ustaw maksymalną liczbę klientów. Nie wszystkie miejsca muszą być zapełnione. STR_NETWORK_START_SERVER_COMPANIES_SELECT :{BLACK}{NUM} firm{P a y ""} STR_NETWORK_START_SERVER_NUMBER_OF_COMPANIES :{BLACK}Maksymalna liczba firm: -STR_NETWORK_START_SERVER_NUMBER_OF_COMPANIES_TOOLTIP :{BLACK}Ogranicz serwer do określonej ilości firm +STR_NETWORK_START_SERVER_NUMBER_OF_COMPANIES_TOOLTIP :{BLACK}Ogranicz serwer do określonej liczby firm STR_NETWORK_START_SERVER_NEW_GAME_NAME_OSKTITLE :{BLACK}Wpisz nazwę dla gry sieciowej @@ -2738,6 +2738,8 @@ STR_NETWORK_CLIENT_LIST_NEW_COMPANY :(Nowa firma) STR_NETWORK_CLIENT_LIST_NEW_COMPANY_TOOLTIP :{BLACK}Utwórz nową firmę i dołącz do niej STR_NETWORK_CLIENT_LIST_PLAYER_ICON_SELF_TOOLTIP :{BLACK}To jesteś ty STR_NETWORK_CLIENT_LIST_PLAYER_ICON_HOST_TOOLTIP :{BLACK}To jest gospodarz gry +STR_NETWORK_CLIENT_LIST_CLIENT_COMPANY_COUNT :{BLACK}{NUM} klient{P "" ów ów} - {NUM}/{NUM} firm{P ę y ""} +STR_NETWORK_CLIENT_LIST_CLIENT_COMPANY_COUNT_TOOLTIP :{BLACK}Liczba aktualnie podłączonych klientów, liczba firm oraz maksymalna liczba firm dozwolona przez administratora serwera # Matches ConnectionType ###length 5 From e3169c73bdb8fd645b1910c5a3a282ee75fff8be Mon Sep 17 00:00:00 2001 From: dP Date: Sat, 4 Mar 2023 01:04:02 +0400 Subject: [PATCH 44/47] Fix: Restore using founder client name as company manager name (#10535) --- src/command_func.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/command_func.h b/src/command_func.h index de7cae2569..d10340146d 100644 --- a/src/command_func.h +++ b/src/command_func.h @@ -235,7 +235,7 @@ public: { auto args_tuple = std::forward_as_tuple(args...); - ::NetworkSendCommand(Tcmd, err_message, nullptr, _current_company, EndianBufferWriter::FromValue(args_tuple)); + ::NetworkSendCommand(Tcmd, err_message, nullptr, company, EndianBufferWriter::FromValue(args_tuple)); } /** From 44848f4edf449ae30aeb101e4afddecb5d439542 Mon Sep 17 00:00:00 2001 From: Tyler Trahan Date: Mon, 19 Nov 2018 18:59:25 +0000 Subject: [PATCH 45/47] Add: CommandCost supports an optional second error string --- src/autoreplace_cmd.cpp | 6 +++--- src/command.cpp | 2 +- src/command_type.h | 30 +++++++++++++++++++--------- src/error.h | 7 +++++-- src/error_gui.cpp | 44 ++++++++++++++++++++++++++++++++++++----- src/misc_cmd.cpp | 2 +- 6 files changed, 70 insertions(+), 21 deletions(-) diff --git a/src/autoreplace_cmd.cpp b/src/autoreplace_cmd.cpp index ca3883f8d4..c9ba7c36b8 100644 --- a/src/autoreplace_cmd.cpp +++ b/src/autoreplace_cmd.cpp @@ -442,7 +442,7 @@ static CommandCost ReplaceFreeUnit(Vehicle **single_unit, DoCommandFlag flags, b Train *old_v = Train::From(*single_unit); assert(!old_v->IsArticulatedPart() && !old_v->IsRearDualheaded()); - CommandCost cost = CommandCost(EXPENSES_NEW_VEHICLES, 0); + CommandCost cost = CommandCost(EXPENSES_NEW_VEHICLES, (Money)0); /* Build and refit replacement vehicle */ Vehicle *new_v = nullptr; @@ -494,7 +494,7 @@ static CommandCost ReplaceChain(Vehicle **chain, DoCommandFlag flags, bool wagon Vehicle *old_head = *chain; assert(old_head->IsPrimaryVehicle()); - CommandCost cost = CommandCost(EXPENSES_NEW_VEHICLES, 0); + CommandCost cost = CommandCost(EXPENSES_NEW_VEHICLES, (Money)0); if (old_head->type == VEH_TRAIN) { /* Store the length of the old vehicle chain, rounded up to whole tiles */ @@ -753,7 +753,7 @@ CommandCost CmdAutoreplaceVehicle(DoCommandFlag flags, VehicleID veh_id) w = (!free_wagon && w->type == VEH_TRAIN ? Train::From(w)->GetNextUnit() : nullptr); } - CommandCost cost = CommandCost(EXPENSES_NEW_VEHICLES, 0); + CommandCost cost = CommandCost(EXPENSES_NEW_VEHICLES, (Money)0); bool nothing_to_do = true; if (any_replacements) { diff --git a/src/command.cpp b/src/command.cpp index bd85690e2d..410e4fd4a8 100644 --- a/src/command.cpp +++ b/src/command.cpp @@ -259,7 +259,7 @@ void CommandHelperBase::InternalPostResult(const CommandCost &res, TileIndex til if (res.Failed()) { /* Only show the error when it's for us. */ if (estimate_only || (IsLocalCompany() && err_message != 0 && my_cmd)) { - ShowErrorMessage(err_message, res.GetErrorMessage(), WL_INFO, x, y, res.GetTextRefStackGRF(), res.GetTextRefStackSize(), res.GetTextRefStack()); + ShowErrorMessage(err_message, x, y, res); } } else if (estimate_only) { ShowEstimatedCostOrIncome(res.GetCost(), x, y); diff --git a/src/command_type.h b/src/command_type.h index 90c59c6773..3ade17ed7b 100644 --- a/src/command_type.h +++ b/src/command_type.h @@ -22,12 +22,13 @@ struct GRFFile; * a possible error message/state together. */ class CommandCost { - ExpensesType expense_type; ///< the type of expence as shown on the finances view - Money cost; ///< The cost of this action - StringID message; ///< Warning message for when success is unset - bool success; ///< Whether the command went fine up to this moment - const GRFFile *textref_stack_grffile; ///< NewGRF providing the #TextRefStack content. - uint textref_stack_size; ///< Number of uint32 values to put on the #TextRefStack for the error message. + ExpensesType expense_type; ///< the type of expence as shown on the finances view + Money cost; ///< The cost of this action + StringID message; ///< Warning message for when success is unset + bool success; ///< Whether the command went fine up to this moment + const GRFFile *textref_stack_grffile; ///< NewGRF providing the #TextRefStack content. + uint textref_stack_size; ///< Number of uint32 values to put on the #TextRefStack for the error message. + StringID extra_message = INVALID_STRING_ID; ///< Additional warning message for when success is unset static uint32 textref_stack[16]; @@ -38,9 +39,9 @@ public: CommandCost() : expense_type(INVALID_EXPENSES), cost(0), message(INVALID_STRING_ID), success(true), textref_stack_grffile(nullptr), textref_stack_size(0) {} /** - * Creates a command return value the is failed with the given message + * Creates a command return value with one, or optionally two, error message strings. */ - explicit CommandCost(StringID msg) : expense_type(INVALID_EXPENSES), cost(0), message(msg), success(false), textref_stack_grffile(nullptr), textref_stack_size(0) {} + explicit CommandCost(StringID msg, StringID extra_msg = INVALID_STRING_ID) : expense_type(INVALID_EXPENSES), cost(0), message(msg), success(false), textref_stack_grffile(nullptr), textref_stack_size(0), extra_message(extra_msg) {} /** * Creates a command cost with given expense type and start cost of 0 @@ -98,11 +99,12 @@ public: * Makes this #CommandCost behave like an error command. * @param message The error message. */ - void MakeError(StringID message) + void MakeError(StringID message, StringID extra_message = INVALID_STRING_ID) { assert(message != INVALID_STRING_ID); this->success = false; this->message = message; + this->extra_message = extra_message; } void UseTextRefStack(const GRFFile *grffile, uint num_registers); @@ -144,6 +146,16 @@ public: return this->message; } + /** + * Returns the extra error message of a command + * @return the extra error message, if succeeded #INVALID_STRING_ID + */ + StringID GetExtraErrorMessage() const + { + if (this->success) return INVALID_STRING_ID; + return this->extra_message; + } + /** * Did this command succeed? * @return true if and only if it succeeded diff --git a/src/error.h b/src/error.h index 21ccc93386..51b164c432 100644 --- a/src/error.h +++ b/src/error.h @@ -13,6 +13,7 @@ #include #include "strings_type.h" #include "company_type.h" +#include "command_type.h" #include "core/geometry_type.hpp" #include "guitimer_func.h" @@ -37,13 +38,14 @@ protected: uint32 textref_stack[16]; ///< Values to put on the #TextRefStack for the error message. StringID summary_msg; ///< General error message showed in first line. Must be valid. StringID detailed_msg; ///< Detailed error message showed in second line. Can be #INVALID_STRING_ID. + StringID extra_msg; ///< Extra error message shown in third line. Can be #INVALID_STRING_ID. Point position; ///< Position of the error message window. CompanyID face; ///< Company belonging to the face being shown. #INVALID_COMPANY if no face present. public: ErrorMessageData(const ErrorMessageData &data); ~ErrorMessageData(); - ErrorMessageData(StringID summary_msg, StringID detailed_msg, uint duration = 0, int x = 0, int y = 0, const GRFFile *textref_stack_grffile = nullptr, uint textref_stack_size = 0, const uint32 *textref_stack = nullptr); + ErrorMessageData(StringID summary_msg, StringID detailed_msg, uint duration = 0, int x = 0, int y = 0, const GRFFile *textref_stack_grffile = nullptr, uint textref_stack_size = 0, const uint32 *textref_stack = nullptr, StringID extra_msg = INVALID_STRING_ID); /* Remove the copy assignment, as the default implementation will not do the right thing. */ ErrorMessageData &operator=(ErrorMessageData &rhs) = delete; @@ -64,7 +66,8 @@ typedef std::list ErrorList; void ScheduleErrorMessage(ErrorList &datas); void ScheduleErrorMessage(const ErrorMessageData &data); -void ShowErrorMessage(StringID summary_msg, StringID detailed_msg, WarningLevel wl, int x = 0, int y = 0, const GRFFile *textref_stack_grffile = nullptr, uint textref_stack_size = 0, const uint32 *textref_stack = nullptr); +void ShowErrorMessage(StringID summary_msg, int x, int y, CommandCost cc); +void ShowErrorMessage(StringID summary_msg, StringID detailed_msg, WarningLevel wl, int x = 0, int y = 0, const GRFFile *textref_stack_grffile = nullptr, uint textref_stack_size = 0, const uint32 *textref_stack = nullptr, StringID extra_msg = INVALID_STRING_ID); bool HideActiveErrorMessage(); void ClearErrorMessages(); diff --git a/src/error_gui.cpp b/src/error_gui.cpp index 3e6d519b49..6d05d24395 100644 --- a/src/error_gui.cpp +++ b/src/error_gui.cpp @@ -72,7 +72,7 @@ static WindowDesc _errmsg_face_desc( */ ErrorMessageData::ErrorMessageData(const ErrorMessageData &data) : display_timer(data.display_timer), textref_stack_grffile(data.textref_stack_grffile), textref_stack_size(data.textref_stack_size), - summary_msg(data.summary_msg), detailed_msg(data.detailed_msg), position(data.position), face(data.face) + summary_msg(data.summary_msg), detailed_msg(data.detailed_msg), extra_msg(data.extra_msg), position(data.position), face(data.face) { memcpy(this->textref_stack, data.textref_stack, sizeof(this->textref_stack)); memcpy(this->decode_params, data.decode_params, sizeof(this->decode_params)); @@ -101,12 +101,14 @@ ErrorMessageData::~ErrorMessageData() * @param textref_stack_grffile NewGRF that provides the #TextRefStack for the error message. * @param textref_stack_size Number of uint32 values to put on the #TextRefStack for the error message; 0 if the #TextRefStack shall not be used. * @param textref_stack Values to put on the #TextRefStack. + * @param extra_msg Extra error message showed in third line. Can be INVALID_STRING_ID. */ -ErrorMessageData::ErrorMessageData(StringID summary_msg, StringID detailed_msg, uint duration, int x, int y, const GRFFile *textref_stack_grffile, uint textref_stack_size, const uint32 *textref_stack) : +ErrorMessageData::ErrorMessageData(StringID summary_msg, StringID detailed_msg, uint duration, int x, int y, const GRFFile *textref_stack_grffile, uint textref_stack_size, const uint32 *textref_stack, StringID extra_msg) : textref_stack_grffile(textref_stack_grffile), textref_stack_size(textref_stack_size), summary_msg(summary_msg), detailed_msg(detailed_msg), + extra_msg(extra_msg), face(INVALID_COMPANY) { this->position.x = x; @@ -184,6 +186,7 @@ struct ErrmsgWindow : public Window, ErrorMessageData { private: uint height_summary; ///< Height of the #summary_msg string in pixels in the #WID_EM_MESSAGE widget. uint height_detailed; ///< Height of the #detailed_msg string in pixels in the #WID_EM_MESSAGE widget. + uint height_extra; ///< Height of the #extra_msg string in pixels in the #WID_EM_MESSAGE widget. public: ErrmsgWindow(const ErrorMessageData &data) : Window(data.HasFace() ? &_errmsg_face_desc : &_errmsg_desc), ErrorMessageData(data) @@ -200,11 +203,13 @@ public: this->height_summary = GetStringHeight(this->summary_msg, size->width); this->height_detailed = (this->detailed_msg == INVALID_STRING_ID) ? 0 : GetStringHeight(this->detailed_msg, size->width); + this->height_extra = (this->extra_msg == INVALID_STRING_ID) ? 0 : GetStringHeight(this->extra_msg, size->width); if (this->textref_stack_size > 0) StopTextRefStackUsage(); uint panel_height = this->height_summary; if (this->detailed_msg != INVALID_STRING_ID) panel_height += this->height_detailed + WidgetDimensions::scaled.vsep_wide; + if (this->extra_msg != INVALID_STRING_ID) panel_height += this->height_extra + WidgetDimensions::scaled.vsep_wide; size->height = std::max(size->height, panel_height); break; @@ -280,13 +285,24 @@ public: if (this->detailed_msg == INVALID_STRING_ID) { DrawStringMultiLine(r, this->summary_msg, TC_FROMSTRING, SA_CENTER); - } else { + } else if (this->extra_msg == INVALID_STRING_ID) { /* Extra space when message is shorter than company face window */ int extra = (r.Height() - this->height_summary - this->height_detailed - WidgetDimensions::scaled.vsep_wide) / 2; /* Note: NewGRF supplied error message often do not start with a colour code, so default to white. */ DrawStringMultiLine(r.WithHeight(this->height_summary + extra, false), this->summary_msg, TC_WHITE, SA_CENTER); DrawStringMultiLine(r.WithHeight(this->height_detailed + extra, true), this->detailed_msg, TC_WHITE, SA_CENTER); + } else { + /* Extra space when message is shorter than company face window */ + int extra = (r.Height() - this->height_summary - this->height_detailed - this->height_extra - (WidgetDimensions::scaled.vsep_wide * 2)) / 3; + + /* Note: NewGRF supplied error message often do not start with a colour code, so default to white. */ + Rect top_section = r.WithHeight(this->height_summary + extra, false); + Rect bottom_section = r.WithHeight(this->height_extra + extra, true); + Rect middle_section = { top_section.left, top_section.bottom, top_section.right, bottom_section.top }; + DrawStringMultiLine(top_section, this->summary_msg, TC_WHITE, SA_CENTER); + DrawStringMultiLine(middle_section, this->detailed_msg, TC_WHITE, SA_CENTER); + DrawStringMultiLine(bottom_section, this->extra_msg, TC_WHITE, SA_CENTER); } if (this->textref_stack_size > 0) StopTextRefStackUsage(); @@ -361,6 +377,19 @@ void UnshowCriticalError() } } +/** + * Display an error message in a window. + * Note: CommandCost errors are always severity level WL_INFO. + * @param summary_msg General error message showed in first line. Must be valid. + * @param x World X position (TileVirtX) of the error location. Set both x and y to 0 to just center the message when there is no related error tile. + * @param y World Y position (TileVirtY) of the error location. Set both x and y to 0 to just center the message when there is no related error tile. + * @param cc CommandCost containing the optional detailed and extra error messages shown in the second and third lines (can be INVALID_STRING_ID) and TextRefStack info. + */ +void ShowErrorMessage(StringID summary_msg, int x, int y, CommandCost cc) +{ + ShowErrorMessage(summary_msg, cc.GetErrorMessage(), WL_INFO, x, y, cc.GetTextRefStackGRF(), cc.GetTextRefStackSize(), cc.GetTextRefStack(), cc.GetExtraErrorMessage()); +} + /** * Display an error message in a window. * @param summary_msg General error message showed in first line. Must be valid. @@ -371,8 +400,9 @@ void UnshowCriticalError() * @param textref_stack_grffile NewGRF providing the #TextRefStack for the error message. * @param textref_stack_size Number of uint32 values to put on the #TextRefStack for the error message; 0 if the #TextRefStack shall not be used. * @param textref_stack Values to put on the #TextRefStack. + * @param extra_msg Extra error message shown in third line. Can be INVALID_STRING_ID. */ -void ShowErrorMessage(StringID summary_msg, StringID detailed_msg, WarningLevel wl, int x, int y, const GRFFile *textref_stack_grffile, uint textref_stack_size, const uint32 *textref_stack) +void ShowErrorMessage(StringID summary_msg, StringID detailed_msg, WarningLevel wl, int x, int y, const GRFFile *textref_stack_grffile, uint textref_stack_size, const uint32 *textref_stack, StringID extra_msg) { assert(textref_stack_size == 0 || (textref_stack_grffile != nullptr && textref_stack != nullptr)); if (summary_msg == STR_NULL) summary_msg = STR_EMPTY; @@ -388,6 +418,10 @@ void ShowErrorMessage(StringID summary_msg, StringID detailed_msg, WarningLevel b += seprintf(b, lastof(buf), " "); GetString(b, detailed_msg, lastof(buf)); } + if (extra_msg != INVALID_STRING_ID) { + b += seprintf(b, lastof(buf), " "); + GetString(b, extra_msg, lastof(buf)); + } if (textref_stack_size > 0) StopTextRefStackUsage(); @@ -399,7 +433,7 @@ void ShowErrorMessage(StringID summary_msg, StringID detailed_msg, WarningLevel if (_game_mode == GM_BOOTSTRAP) return; if (_settings_client.gui.errmsg_duration == 0 && !no_timeout) return; - ErrorMessageData data(summary_msg, detailed_msg, no_timeout ? 0 : _settings_client.gui.errmsg_duration, x, y, textref_stack_grffile, textref_stack_size, textref_stack); + ErrorMessageData data(summary_msg, detailed_msg, no_timeout ? 0 : _settings_client.gui.errmsg_duration, x, y, textref_stack_grffile, textref_stack_size, textref_stack, extra_msg); data.CopyOutDParams(); ErrmsgWindow *w = (ErrmsgWindow*)FindWindowById(WC_ERRMSG, 0); diff --git a/src/misc_cmd.cpp b/src/misc_cmd.cpp index a18504c1ef..25fc827f67 100644 --- a/src/misc_cmd.cpp +++ b/src/misc_cmd.cpp @@ -222,6 +222,6 @@ CommandCost CmdChangeBankBalance(DoCommandFlag flags, TileIndex tile, Money delt } /* This command doesn't cost anything for deity. */ - CommandCost zero_cost(expenses_type, 0); + CommandCost zero_cost(expenses_type, (Money)0); return zero_cost; } From 3719f60de0c6378364809e243fec8c324c8db9a6 Mon Sep 17 00:00:00 2001 From: Tyler Trahan Date: Tue, 29 Mar 2022 22:14:12 +0100 Subject: [PATCH 46/47] Add: Use specific error message when vehicle cannot go to station/waypoint --- src/lang/english.txt | 14 ++++++++++++ src/order_cmd.cpp | 16 +++++++------- src/vehicle.cpp | 52 ++++++++++++++++++++++++++++++++++++++++++++ src/vehicle_func.h | 1 + 4 files changed, 75 insertions(+), 8 deletions(-) diff --git a/src/lang/english.txt b/src/lang/english.txt index 0a8a84a3d6..80c9dd9e3f 100644 --- a/src/lang/english.txt +++ b/src/lang/english.txt @@ -5085,6 +5085,20 @@ STR_ERROR_CAN_T_COPY_ORDER_LIST :{WHITE}Can't co STR_ERROR_TOO_FAR_FROM_PREVIOUS_DESTINATION :{WHITE}... too far from previous destination STR_ERROR_AIRCRAFT_NOT_ENOUGH_RANGE :{WHITE}... aircraft has not enough range +# Extra messages which go on the third line of errors, explaining why orders failed +STR_ERROR_NO_RAIL_STATION :{WHITE}There is no railway station +STR_ERROR_NO_BUS_STATION :{WHITE}There is no bus station +STR_ERROR_NO_TRUCK_STATION :{WHITE}There is no lorry station +STR_ERROR_NO_DOCK :{WHITE}There is no dock +STR_ERROR_NO_AIRPORT :{WHITE}There is no airport/heliport +STR_ERROR_NO_STOP_COMPATIBLE_ROAD_TYPE :{WHITE}There are no stops with a compatible road type +STR_ERROR_NO_STOP_COMPATIBLE_TRAM_TYPE :{WHITE}There are no stops with a compatible tram type +STR_ERROR_NO_STOP_ARTICULATED_VEHICLE :{WHITE}There are no stops which are suitable for articulated road vehicles.{}Articulated road vehicles require a drive-through stop, not a bay stop +STR_ERROR_AIRPORT_NO_PLANES :{WHITE}This plane cannot land at this heliport +STR_ERROR_AIRPORT_NO_HELICOPTERS :{WHITE}This helicopter cannot land at this airport +STR_ERROR_NO_RAIL_WAYPOINT :{WHITE}There is no railway waypoint +STR_ERROR_NO_BUOY :{WHITE}There is no buoy + # Timetable related errors STR_ERROR_CAN_T_TIMETABLE_VEHICLE :{WHITE}Can't timetable vehicle... STR_ERROR_TIMETABLE_ONLY_WAIT_AT_STATIONS :{WHITE}Vehicles can only wait at stations diff --git a/src/order_cmd.cpp b/src/order_cmd.cpp index 5ce15f93e0..56c306856f 100644 --- a/src/order_cmd.cpp +++ b/src/order_cmd.cpp @@ -758,9 +758,9 @@ CommandCost CmdInsertOrder(DoCommandFlag flags, VehicleID veh, VehicleOrderID se if (ret.Failed()) return ret; } - if (!CanVehicleUseStation(v, st)) return_cmd_error(STR_ERROR_CAN_T_ADD_ORDER); + if (!CanVehicleUseStation(v, st)) return CommandCost(STR_ERROR_CAN_T_ADD_ORDER, GetVehicleCannotUseStationReason(v, st)); for (Vehicle *u = v->FirstShared(); u != nullptr; u = u->NextShared()) { - if (!CanVehicleUseStation(u, st)) return_cmd_error(STR_ERROR_CAN_T_ADD_ORDER_SHARED); + if (!CanVehicleUseStation(u, st)) return CommandCost(STR_ERROR_CAN_T_ADD_ORDER_SHARED, GetVehicleCannotUseStationReason(u, st)); } /* Non stop only allowed for ground vehicles. */ @@ -847,7 +847,7 @@ CommandCost CmdInsertOrder(DoCommandFlag flags, VehicleID veh, VehicleOrderID se default: return CMD_ERROR; case VEH_TRAIN: { - if (!(wp->facilities & FACIL_TRAIN)) return_cmd_error(STR_ERROR_CAN_T_ADD_ORDER); + if (!(wp->facilities & FACIL_TRAIN)) return CommandCost(STR_ERROR_CAN_T_ADD_ORDER, STR_ERROR_NO_RAIL_WAYPOINT); ret = CheckOwnership(wp->owner); if (ret.Failed()) return ret; @@ -855,7 +855,7 @@ CommandCost CmdInsertOrder(DoCommandFlag flags, VehicleID veh, VehicleOrderID se } case VEH_SHIP: - if (!(wp->facilities & FACIL_DOCK)) return_cmd_error(STR_ERROR_CAN_T_ADD_ORDER); + if (!(wp->facilities & FACIL_DOCK)) return CommandCost(STR_ERROR_CAN_T_ADD_ORDER, STR_ERROR_NO_BUOY); if (wp->owner != OWNER_NONE) { ret = CheckOwnership(wp->owner); if (ret.Failed()) return ret; @@ -1533,7 +1533,7 @@ CommandCost CmdCloneOrder(DoCommandFlag flags, CloneOptions action, VehicleID ve * are temporarily invalid due to reconstruction. */ const Station *st = Station::Get(order->GetDestination()); if (CanVehicleUseStation(src, st) && !CanVehicleUseStation(dst, st)) { - return_cmd_error(STR_ERROR_CAN_T_COPY_SHARE_ORDER); + return CommandCost(STR_ERROR_CAN_T_COPY_SHARE_ORDER, GetVehicleCannotUseStationReason(dst, st)); } } @@ -1577,9 +1577,9 @@ CommandCost CmdCloneOrder(DoCommandFlag flags, CloneOptions action, VehicleID ve /* Trucks can't copy all the orders from busses (and visa versa), * and neither can helicopters and aircraft. */ for (const Order *order : src->Orders()) { - if (OrderGoesToStation(dst, order) && - !CanVehicleUseStation(dst, Station::Get(order->GetDestination()))) { - return_cmd_error(STR_ERROR_CAN_T_COPY_SHARE_ORDER); + Station *st = Station::Get(order->GetDestination()); + if (OrderGoesToStation(dst, order) && !CanVehicleUseStation(dst, st)) { + return CommandCost(STR_ERROR_CAN_T_COPY_SHARE_ORDER, GetVehicleCannotUseStationReason(dst, st)); } } diff --git a/src/vehicle.cpp b/src/vehicle.cpp index 678b7db0ac..bf35856f88 100644 --- a/src/vehicle.cpp +++ b/src/vehicle.cpp @@ -2903,6 +2903,58 @@ bool CanVehicleUseStation(const Vehicle *v, const Station *st) return CanVehicleUseStation(v->engine_type, st); } +/** + * Get reason string why this station can't be used by the given vehicle. + * @param v The vehicle to test. + * @param st The station to test for. + * @return The string explaining why the vehicle cannot use the station. + */ +StringID GetVehicleCannotUseStationReason(const Vehicle *v, const Station *st) +{ + switch (v->type) { + case VEH_TRAIN: + return STR_ERROR_NO_RAIL_STATION; + + case VEH_ROAD: { + const RoadVehicle *rv = RoadVehicle::From(v); + RoadStop *rs = st->GetPrimaryRoadStop(rv->IsBus() ? ROADSTOP_BUS : ROADSTOP_TRUCK); + + StringID err = rv->IsBus() ? STR_ERROR_NO_BUS_STATION : STR_ERROR_NO_TRUCK_STATION; + + for (; rs != nullptr; rs = rs->next) { + /* Articulated vehicles cannot use bay road stops, only drive-through. Make sure the vehicle can actually use this bay stop */ + if (HasTileAnyRoadType(rs->xy, rv->compatible_roadtypes) && IsStandardRoadStopTile(rs->xy) && rv->HasArticulatedPart()) { + err = STR_ERROR_NO_STOP_ARTICULATED_VEHICLE; + continue; + } + + /* Bay stop errors take precedence, but otherwise the vehicle may not be compatible with the roadtype/tramtype of this station tile. + * We give bay stop errors precedence because they are usually a bus sent to a tram station or vice versa. */ + if (!HasTileAnyRoadType(rs->xy, rv->compatible_roadtypes) && err != STR_ERROR_NO_STOP_ARTICULATED_VEHICLE) { + err = RoadTypeIsRoad(rv->roadtype) ? STR_ERROR_NO_STOP_COMPATIBLE_ROAD_TYPE : STR_ERROR_NO_STOP_COMPATIBLE_TRAM_TYPE; + continue; + } + } + + return err; + } + + case VEH_SHIP: + return STR_ERROR_NO_DOCK; + + case VEH_AIRCRAFT: + if ((st->facilities & FACIL_AIRPORT) == 0) return STR_ERROR_NO_AIRPORT; + if (v->GetEngine()->u.air.subtype & AIR_CTOL) { + return STR_ERROR_AIRPORT_NO_PLANES; + } else { + return STR_ERROR_AIRPORT_NO_HELICOPTERS; + } + + default: + return INVALID_STRING_ID; + } +} + /** * Access the ground vehicle cache of the vehicle. * @pre The vehicle is a #GroundVehicle. diff --git a/src/vehicle_func.h b/src/vehicle_func.h index 13fc352e46..0efd4fd878 100644 --- a/src/vehicle_func.h +++ b/src/vehicle_func.h @@ -166,6 +166,7 @@ CommandCost EnsureNoTrainOnTrackBits(TileIndex tile, TrackBits track_bits); bool CanVehicleUseStation(EngineID engine_type, const struct Station *st); bool CanVehicleUseStation(const Vehicle *v, const struct Station *st); +StringID GetVehicleCannotUseStationReason(const Vehicle *v, const Station *st); void ReleaseDisastersTargetingVehicle(VehicleID vehicle); From 87f4d37de379da391fc4478161fcec66824a48c8 Mon Sep 17 00:00:00 2001 From: Jonathan G Rennison Date: Fri, 3 Mar 2023 22:37:17 +0000 Subject: [PATCH 47/47] Fix #10522: Link graph line tooltip test for vertical lines (#10524) --- src/linkgraph/linkgraph_gui.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/linkgraph/linkgraph_gui.cpp b/src/linkgraph/linkgraph_gui.cpp index 73cdf9ba48..026f85b853 100644 --- a/src/linkgraph/linkgraph_gui.cpp +++ b/src/linkgraph/linkgraph_gui.cpp @@ -373,8 +373,10 @@ bool LinkGraphOverlay::ShowTooltip(Point pt, TooltipCloseCondition close_cond) std::sqrt((ptb.x - pta.x) * (ptb.x - pta.x) + (ptb.y - pta.y) * (ptb.y - pta.y)); const auto &link = j->second; if (dist <= 4 && link.Usage() > 0 && - pt.x >= std::min(pta.x, ptb.x) && - pt.x <= std::max(pta.x, ptb.x)) { + pt.x + 2 >= std::min(pta.x, ptb.x) && + pt.x - 2 <= std::max(pta.x, ptb.x) && + pt.y + 2 >= std::min(pta.y, ptb.y) && + pt.y - 2 <= std::max(pta.y, ptb.y)) { static char buf[1024]; char *buf_end = buf; buf[0] = 0;