diff --git a/.github/workflows/preview_build.yml b/.github/workflows/preview_build.yml index 68ca5f08..f0fc11a9 100644 --- a/.github/workflows/preview_build.yml +++ b/.github/workflows/preview_build.yml @@ -31,7 +31,7 @@ jobs: GITHUB_TOKEN: ${{ secrets.PREVIEW_GITHUB_TOKEN }} - name: Checkout - uses: actions/checkout@v3 + uses: actions/checkout@v4 with: ref: ${{ github.event.client_payload.sha }} @@ -41,7 +41,7 @@ jobs: git checkout -b ${name} - name: Setup cache - uses: actions/cache@v3 + uses: actions/cache@v4 with: path: /emsdk/upstream/emscripten/cache key: 3.1.28-${{ runner.os }} diff --git a/.github/workflows/preview_label.yml b/.github/workflows/preview_label.yml index 503d4e53..e21a9178 100644 --- a/.github/workflows/preview_label.yml +++ b/.github/workflows/preview_label.yml @@ -59,7 +59,7 @@ jobs: - if: steps.core_developer.outcome == 'success' name: Trigger 'preview build' - uses: peter-evans/repository-dispatch@v2 + uses: peter-evans/repository-dispatch@v3 with: token: ${{ secrets.PREVIEW_GITHUB_TOKEN }} event-type: "Preview build #${{ github.event.number }}" diff --git a/.github/workflows/preview_push.yml b/.github/workflows/preview_push.yml index 3868c16f..f01069f7 100644 --- a/.github/workflows/preview_push.yml +++ b/.github/workflows/preview_push.yml @@ -59,7 +59,7 @@ jobs: - if: toJson(fromJson(steps.earlier_preview.outputs.data)) != '[]' && contains(fromJson(steps.preview_label.outputs.data).*.name, 'preview') name: Trigger 'preview build' - uses: peter-evans/repository-dispatch@v2 + uses: peter-evans/repository-dispatch@v3 with: token: ${{ secrets.PREVIEW_GITHUB_TOKEN }} event-type: "Preview build #${{ github.event.number }}" diff --git a/.github/workflows/release-macos.yml b/.github/workflows/release-macos.yml index 4c65d579..10cabfab 100644 --- a/.github/workflows/release-macos.yml +++ b/.github/workflows/release-macos.yml @@ -86,7 +86,7 @@ jobs: echo "::endgroup::" - name: Import code signing certificates - uses: Apple-Actions/import-codesign-certs@v2 + uses: Apple-Actions/import-codesign-certs@v3 with: # The certificates in a PKCS12 file encoded as a base64 string p12-file-base64: ${{ secrets.APPLE_DEVELOPER_CERTIFICATE_P12_BASE64 }} diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 67a6444b..0f4c2208 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -176,7 +176,7 @@ jobs: uses: actions/download-artifact@v4 - name: Release GitHub assets - uses: softprops/action-gh-release@v0.1.15 + uses: softprops/action-gh-release@v2.0.5 # if: startsWith(github.ref, 'refs/tags/') with: diff --git a/.github/workflows/upload-aws.yml b/.github/workflows/upload-aws.yml index 72319ad5..754b1e9a 100644 --- a/.github/workflows/upload-aws.yml +++ b/.github/workflows/upload-aws.yml @@ -21,7 +21,7 @@ jobs: steps: - name: Download all bundles - uses: actions/download-artifact@v3 + uses: actions/download-artifact@v4 - name: Calculate checksums run: | @@ -68,7 +68,7 @@ jobs: AWS_DEFAULT_REGION: ${{ secrets.AWS_REGION }} - name: Trigger 'New OpenTTD release' - uses: peter-evans/repository-dispatch@v2 + uses: peter-evans/repository-dispatch@v3 with: token: ${{ secrets.DEPLOYMENT_TOKEN }} repository: OpenTTD/workflows diff --git a/src/citymania/cm_blueprint.cpp b/src/citymania/cm_blueprint.cpp index fe45f30d..6d9352ac 100644 --- a/src/citymania/cm_blueprint.cpp +++ b/src/citymania/cm_blueprint.cpp @@ -4,6 +4,7 @@ #include "cm_commands.hpp" #include "cm_highlight.hpp" +#include "cm_station_gui.hpp" #include "../console_func.h" #include "../command_func.h" @@ -24,17 +25,7 @@ extern TileHighlightData _thd; extern RailType _cur_railtype; extern void GetStationLayout(byte *layout, uint numtracks, uint plat_len, const StationSpec *statspec); - -// from rail_gui.cpp -struct RailStationGUISettings { - Axis orientation; ///< Currently selected rail station orientation - - bool newstations; ///< Are custom station definitions available? - StationClassID station_class; ///< Currently selected custom station class (if newstations is \c true ) - byte station_type; ///< %Station type within the currently selected custom station class (if newstations is \c true ) - byte station_count; ///< Number of custom stations (if newstations is \c true ) -}; -extern RailStationGUISettings _railstation; ///< Settings of the station builder GUI +extern citymania::RailStationGUISettings _railstation; ///< Settings of the station builder GUI namespace citymania { diff --git a/src/citymania/cm_commands_gui.cpp b/src/citymania/cm_commands_gui.cpp index 849ecef9..3784311e 100644 --- a/src/citymania/cm_commands_gui.cpp +++ b/src/citymania/cm_commands_gui.cpp @@ -6,6 +6,7 @@ #include "cm_main.hpp" #include "../network/core/http.h" //HttpCallback +#include "../network/network_client.h" //for servername #include "core/geometry_func.hpp" //maxdim #include "settings_type.h" #include "settings_func.h" //saveconfig @@ -23,26 +24,48 @@ #include - namespace citymania { bool _novahost = true; IniFile *_inilogin = NULL; +/* admin company buttons */ +bool _admin = false; +int ACB_left = 0; +int ACB_top = 0; +int ACB_width = 0; +int ACB_Location[3][15]; + +/* server buttons */ +std::string _server_list_text; // list from http +std::string _cc_address; // current adddress +std::string _cc_name; // current name +int _cc_porti; // current port +std::int8_t _fromlast = 0; +int _left, _top, _height; +int ls_left, ls_top, ls_height; // for last server +int _servercount; // how many servers + static const int HTTPBUFLEN = 1024; static const int MAX_COMMUNITY_STRING_LEN = 128; static constexpr std::string_view NICE_HTTP_LOGIN = "http://n-ice.org/openttd/gettoken_md5salt.php?user={}&password={}"; -static constexpr std::string_view BTPRO_HTTP_LOGIN = "http://openttd.btpro.nl/gettoken-enc.php?user={}&password={}"; +static constexpr std::string_view BTPRO_HTTP_LOGIN = "https://openttd.btpro.nl/gettoken-enc.php?user={}&password={}"; -static const std::string CFG_LOGIN_FILE = "citymania.cfg"; +static const std::string CFG_FILE = "citymania.cfg"; static const std::string CFG_LOGIN_KEY = "login"; +static const std::string CFG_SERVER_KEY = "server"; static const char * const NOVAPOLIS_LOGIN = "citymania_login"; static const char * const NOVAPOLIS_PW = "citymania_pw"; static const char * const NICE_LOGIN = "nice_login"; static const char * const NICE_PW = "nice_pw"; static const char * const BTPRO_LOGIN = "btpro_login"; static const char * const BTPRO_PW = "btpro_pw"; +static const char * const NICE_ADMIN_PW = "nice_admin_pw"; +static const char * const BTPRO_ADMIN_PW = "btpro_admin_pw"; +static const char * const ADMIN = "admin"; + +static const char * const COMMUNITY = "community"; static const char * const INI_LOGIN_KEYS[] = { NOVAPOLIS_LOGIN, @@ -51,8 +74,17 @@ static const char * const INI_LOGIN_KEYS[] = { NICE_PW, BTPRO_LOGIN, BTPRO_PW, + NICE_ADMIN_PW, + BTPRO_ADMIN_PW, + ADMIN, +}; + + +static const char *const INI_SERVER_KEYS[] = { + COMMUNITY, }; + /** Widget number of the commands window. */ enum CommandsToolbarWidgets { CTW_BACKGROUND, @@ -122,31 +154,71 @@ enum CommandsToolbarCargoOption { }; enum LoginWindowWidgets { - LWW_NOVAPOLIS, - LWW_NICE, - LWW_BTPRO, - LWW_NOVAPOLIS_LOGIN, - LWW_NOVAPOLIS_PW, - LWW_NICE_LOGIN, - LWW_NICE_PW, - LWW_BTPRO_LOGIN, - LWW_BTPRO_PW, - LWW_USERNAME, - LWW_PASSWORD, + LWW_USER_NAME, + LWW_USER_PW, + LWW_ADMIN_PW, + LWW_USER_LOGIN, + LWW_USER_LOGOUT, + LWW_ADMIN_LOGIN, + LWW_ADMIN_LOGOUT, + LWW_COMMUNITY, + LWW_USERNAME, + LWW_PASSWORD, }; enum LoginWindowQueryWidgets { - LQW_NOVAPOLIS, - LQW_NICE, - LQW_BTPRO, - LQW_NOVAPOLIS_LOGIN, - LQW_NOVAPOLIS_PW, - LQW_NICE_LOGIN, - LQW_NICE_PW, - LQW_BTPRO_LOGIN, - LQW_BTPRO_PW, + LQW_USER_NAME, + LQW_USER_PW, + LQW_ADMIN_PW +}; + +enum AdminCompanyButtonsWidgets { + ACB_COMPANY_EMPTY, + ACB_COMPANY_LOCK, + ACB_COMPANY_UNLOCK, + ACB_COMPANY_NEWSTICKET, + ACB_COMPANY_NEWSTICKET_COMP, + ACB_COMPANY_RESET, + ACB_COMPANY_RESET_SPEC, + ACB_COMPANY_RESET_KICK, + ACB_COMPANY_KNOWN, + ACB_COMPANY_RESET_KNOWN, + ACB_COMPANY_MOVE_PLAYER, + ACB_RESET_COMPANY_TIMER_120, + ACB_RESET_COMPANY_TIMER, + ACB_RESET_COMPANY_TIMER_CANCEL, + ACB_COMPANY_SUSPEND, + ACB_COMPANY_UNSUSPEND, + ACB_COMPANY_AWARNING, + ACB_COMPANY_JOIN2, + ACB_COMPANY_LEAVE, + ACB_COMPANY_CANCEL, + ACB_COMPANY_CAPTION, +}; + +enum AdminCompanyButtonsQueryWidgets { + ACBQ_RESET_COMPANY_TIMER, + ACBQ_COMPANY_NEWSTICKET, + ACBQ_COMPANY_NEWSTICKET_COMP, + ACBQ_COMPANY_MOVE_PLAYER, +}; + +enum LastServerWidgets { + LSW_CAPTION, + LSW_BUTTON, +}; + +enum ServerButtonsWidgets { + WID_SB_SELECT_NICE, + WID_SB_SELECT_BTPRO, + WID_SB_SELECT_CITYMANIA, + WID_SB_SELECT_NONE, + AC_SERVERS, }; +enum ServerButtonsQueryWidgets {}; + + enum CommunityName { CITYMANIA, NICE, @@ -160,17 +232,26 @@ enum IniLoginKeys { NICEPW, BTPROUSER, BTPROPW, + NICEADMINPW, + BTPROADMINPW, + IS_ADMIN, }; -char _inilogindata[6][MAX_COMMUNITY_STRING_LEN]; +char _inilogindata[9][MAX_COMMUNITY_STRING_LEN]; void AccountLogin(CommunityName community); void IniReloadLogin(); +void ShowServerButtons(int left,int top, int height); +void ReloadServerButtons(); + bool novahost() { return _novahost; } +/* for company_gui */ +bool GetAdmin() { return _admin; } + // void strtomd5(char * buf, char * bufend, int length){ // MD5Hash digest; // Md5 checksum; @@ -196,11 +277,12 @@ bool novahost() { //ini login hadling -void IniLoginInitiate(){ +void IniInitiate(){ if(_inilogin != NULL) return; //it was already set _inilogin = new IniFile({CFG_LOGIN_KEY}); - _inilogin->LoadFromDisk(CFG_LOGIN_FILE, BASE_DIR); - IniReloadLogin(); + _inilogin->LoadFromDisk(CFG_FILE, BASE_DIR); + if (FileExists(_personal_dir + "/citymania.cfg")) + IniReloadLogin(); } std::string GetLoginItem(const std::string& itemname){ @@ -209,6 +291,12 @@ std::string GetLoginItem(const std::string& itemname){ return item.value.value_or(""); } +std::string GetServerItem(const std::string &itemname) { + IniGroup &group = _inilogin->GetOrCreateGroup(CFG_SERVER_KEY); + IniItem &item = group.GetOrCreateItem(itemname); + return item.value.value_or(""); +} + void IniReloadLogin(){ for(int i = 0, len = lengthof(INI_LOGIN_KEYS); i < len; i++){ auto str = GetLoginItem(INI_LOGIN_KEYS[i]); @@ -223,10 +311,22 @@ void SetLoginItem(const std::string& itemname, const std::string& value){ IniGroup &group = _inilogin->GetOrCreateGroup(CFG_LOGIN_KEY); IniItem &item = group.GetOrCreateItem(itemname); item.SetValue(value); - _inilogin->SaveToDisk(fmt::format("{}{}", _personal_dir, CFG_LOGIN_FILE)); - IniReloadLogin(); + _inilogin->SaveToDisk(fmt::format("{}{}", _personal_dir, CFG_FILE)); + IniReloadLogin(); +} + +void SetServerItem(const std::string &itemname, const std::string &value) { + IniGroup &group = _inilogin->GetOrCreateGroup(CFG_SERVER_KEY); + IniItem &item = group.GetOrCreateItem(itemname); + item.SetValue(value); + _inilogin->SaveToDisk(fmt::format("{}{}", _personal_dir, CFG_FILE)); } + + + + + /** Commands toolbar window handler. */ /* struct CommandsToolbarWindow : Window { @@ -563,20 +663,138 @@ void ShowCommandsToolbar() // AllocateWindowDescFront(&_commands_toolbar_desc, 0); } -// login window +/* for server buttons */ +/** To handle Community Server list */ +class CommunityServerManager: public HTTPCallback { + public: + CommunityServerManager() {} + + void initiateServerSequence(const std::string &uri) { + this->cursor = this->buf; + this->buf_last = lastof(buf); + NetworkHTTPSocketHandler::Connect(uri, this); + } + + void SaveServerString() const { + _server_list_text += this->buf; + //NetworkClientSendChatToServer("*** data has been saved."); + if (FindWindowByClass(CM_WC_LOGIN_WINDOW)) ReloadServerButtons(); + if (FindWindowByClass(WC_SELECT_GAME)) ReloadServerButtons(); + } + + void inspectServerData() { + if (this->cursor - this->buf >= 4) this->SaveServerString(); + else { + //NetworkClientSendChatToServer("*** no data has been received."); + } + } + + void OnFailure() override { + ShowErrorMessage(CM_STR_SB_SERVER_LIST_UNREACHABLE, INVALID_STRING_ID,WL_ERROR); + //NetworkClientSendChatToServer("*** connection failed."); + } + + bool IsCancelled() const override { return false; } + + void OnReceiveData(std::unique_ptr data, size_t length) override + { + if (data.get() == nullptr) + { + this->inspectServerData(); + this->cursor = nullptr; + } else { + for (size_t i = 0; i < length && this->cursor < this->buf_last; i++, this->cursor++) { + *(this->cursor) = data.get()[i]; + } + *(this->cursor) = '\0'; + //NetworkClientSendChatToServer("*** data received"); + } + + } + + private: + char buf[4096]{}; + char *buf_last{}; + char *cursor{}; +}; + +void CommunityServerManagerSend() +{ + std::int8_t _community = stoi(GetServerItem(COMMUNITY)); + std::string uri; + + if (_community == 1) { + uri = fmt::format("http://n-ice.org/openttd/serverlist.txt"); + } else if (_community == 2) { + uri = fmt::format("https://openttd.btpro.nl/btproservers.txt"); + } else if (_community == 3) { + uri = fmt::format("http://altseehof.de/openttd/citymaniaservers.txt"); + } + + static CommunityServerManager servermgr{}; + servermgr.initiateServerSequence(uri); +} + +void GetCommunityServerListText(){ + std::int8_t _community = stoi(GetServerItem(COMMUNITY)); + if(_fromlast == _community || _community == 0) return; + _fromlast = _community; + _server_list_text.clear(); + CommunityServerManagerSend(); +} + +bool GetCommunityServer(int number, bool findonly) { + + if(_server_list_text.empty()) return false; + _cc_address = ""; + _cc_name = ""; + + std::string server; + std::string port; + + if(number < 10) { + server = fmt::format("SERVER0{}", number); + port = fmt::format("PORT0{}", number); + } else { + server = fmt::format("SERVER{}", number); + port = fmt::format("PORT{}", number); + } + size_t posaddress = _server_list_text.find(server); + size_t posport = _server_list_text.find(port); + + if(posaddress != std::string::npos && posport != std::string::npos){ + std::string saddress = _server_list_text.substr(posaddress + 10, _server_list_text.find(";", posaddress + 10) - posaddress - 10); + std::string sport = _server_list_text.substr(posport + 8, _server_list_text.find(";", posport + 8) - posport - 8); + + if(saddress.compare("DISABLED") == 0) return false; + else if(findonly) return true; + + _cc_address = saddress; + _cc_porti = std::stoi(sport); + + return true; + } + else if(findonly) return false; + + ShowErrorMessage(CM_STR_SB_SERVER_LIST_ERROR_FILE, INVALID_STRING_ID,WL_ERROR); + return false; +} +/* end of serverbuttons */ + +/* for login window */ class GetHTTPContent: public HTTPCallback { public: - GetHTTPContent(const std::string &uri): uri{uri} { + GetHTTPContent() { this->proccessing = false; this->buf_last = lastof(buf); } - bool proccessing; + bool proccessing = false; - void InitiateLoginSequence() { + void InitiateLoginSequence(const std::string& uri) { if(this->proccessing) return; this->proccessing = true; this->cursor = this->buf; - NetworkHTTPSocketHandler::Connect(this->uri, this); + NetworkHTTPSocketHandler::Connect(uri, this); } void OnReceiveData(std::unique_ptr data, size_t length) override { @@ -587,7 +805,7 @@ class GetHTTPContent: public HTTPCallback { for(size_t i = 0; i < length && this->cursor < this->buf_last; i++, this->cursor++) { *(this->cursor) = data.get()[i]; } - *(this->cursor) = '\0'; + *(this->cursor) = '\0'; } } @@ -602,7 +820,7 @@ class GetHTTPContent: public HTTPCallback { void LoginAlready(){ if(strlen(this->buf) == 4 && _networking){ NetworkClientSendChatToServer(fmt::format("!login {}", this->buf)); - } else{ + } else { ShowErrorMessage(CM_STR_LOGIN_ERROR_BAD_INPUT, INVALID_STRING_ID, WL_ERROR); } this->proccessing = false; @@ -611,16 +829,15 @@ class GetHTTPContent: public HTTPCallback { virtual ~GetHTTPContent() { } private: - char buf[HTTPBUFLEN]; - char *buf_last; - char *cursor; - std::string uri; + char buf[HTTPBUFLEN]{}; + char *buf_last{}; + char *cursor{}; }; std::string urlencode(const std::string &s) { static const char lookup[]= "0123456789abcdef"; std::stringstream e; - for(int i=0, ix=s.length(); iInitNested(window_number); - // if(_novahost || !_networking){ - // this->DisableWidget(LWW_NICE); - // this->DisableWidget(LWW_BTPRO); - // } - // if(!_novahost || !_networking) this->DisableWidget(LWW_NOVAPOLIS); + if ((_community == 1) || (_community == 2) || (_community == 3)) this->InitNested(window_number); + + //no need for citymania + if (_community == 3) { + this->DisableWidget(LWW_ADMIN_LOGIN); + this->DisableWidget(LWW_ADMIN_LOGOUT); + this->DisableWidget(LWW_ADMIN_PW); + } + } + void Close([[maybe_unused]] int data) override { + CloseWindowByClass(CM_WC_SERVER_BUTTONS); + this->Window::Close(); + } + + void DrawWidget(const Rect &r, WidgetID widget) const override + { + r; widget; + ShowServerButtons(this->left, this->top, this->height); + } + + void SetStringParameters(int widget) const override { switch(widget){ - case LWW_NOVAPOLIS_LOGIN: - SetDParamStr(0, _inilogindata[NOVAPOLISUSER]); + //username + case LWW_USER_NAME: { + switch (_community) { + case 1: SetDParamStr(0, _inilogindata[NICEUSER]); break; + case 2: SetDParamStr(0, _inilogindata[BTPROUSER]); break; + case 3: SetDParamStr(0, _inilogindata[NOVAPOLISUSER]); break; + } + break; + } + //password + case LWW_USER_PW: { + switch (_community) { + case 1: SetDParam(0, (GetLoginItem(NICE_PW).empty() ? CM_STR_LOGIN_WINDOW_NOT_SET : CM_STR_LOGIN_WINDOW_SET)); break; + case 2: SetDParam(0, (GetLoginItem(BTPRO_PW).empty() ? CM_STR_LOGIN_WINDOW_NOT_SET : CM_STR_LOGIN_WINDOW_SET)); break; + case 3: SetDParam(0, (GetLoginItem(NOVAPOLIS_PW).empty() ? CM_STR_LOGIN_WINDOW_NOT_SET : CM_STR_LOGIN_WINDOW_SET)); break; + } + break; + } + //admin password + case LWW_ADMIN_PW: { + switch (_community) { + case 1: SetDParam(0, (GetLoginItem(NICE_ADMIN_PW).empty() ? CM_STR_LOGIN_WINDOW_NOT_SET : CM_STR_LOGIN_WINDOW_SET)); break; + case 2: SetDParam(0, (GetLoginItem(BTPRO_ADMIN_PW).empty() ? CM_STR_LOGIN_WINDOW_NOT_SET : CM_STR_LOGIN_WINDOW_SET)); break; + //case 3: SetDParam(0, (GetLoginItem(NOVAPOLIS_PW).empty() ? CM_STR_LOGIN_WINDOW_NOT_SET : CM_STR_LOGIN_WINDOW_SET)); break; + } + break; + } + //community name + case LWW_COMMUNITY: { + switch (_community) { + case 1: SetDParam(0, CM_STR_LOGIN_WINDOW_NICE); break; + case 2: SetDParam(0, CM_STR_LOGIN_WINDOW_BTPRO); break; + case 3: SetDParam(0, CM_STR_LOGIN_WINDOW_CITYMANIA); break; + } + break; + } + } + } + + void OnClick([[maybe_unused]] Point pt, int widget, [[maybe_unused]] int click_count) override + { + switch (widget) { + case LWW_USER_NAME: + this->query_widget = (LoginWindowQueryWidgets)widget; + ShowQueryString(STR_EMPTY, CM_STR_LOGIN_WINDOW_CHANGE_USERNAME, MAX_COMMUNITY_STRING_LEN, this, CS_ALPHANUMERAL, QSF_NONE); break; - case LWW_NOVAPOLIS_PW: - SetDParam(0, (GetLoginItem(NOVAPOLIS_PW).empty() ? CM_STR_LOGIN_WINDOW_NOT_SET : CM_STR_LOGIN_WINDOW_SET)); + case LWW_USER_PW: + this->query_widget = (LoginWindowQueryWidgets)widget; + ShowQueryString(STR_EMPTY, CM_STR_LOGIN_WINDOW_CHANGE_PASSWORD, MAX_COMMUNITY_STRING_LEN, this, CS_ALPHANUMERAL, QSF_NONE); break; - case LWW_NICE_LOGIN: - SetDParamStr(0, _inilogindata[NICEUSER]); + case LWW_ADMIN_PW: + this->query_widget = (LoginWindowQueryWidgets)widget; + ShowQueryString(STR_EMPTY, CM_STR_LOGIN_WINDOW_CHANGE_PASSWORD, MAX_COMMUNITY_STRING_LEN, this, CS_ALPHANUMERAL, QSF_NONE); break; - case LWW_NICE_PW: - SetDParam(0, (GetLoginItem(NICE_PW).empty() ? CM_STR_LOGIN_WINDOW_NOT_SET : CM_STR_LOGIN_WINDOW_SET)); + case LWW_USER_LOGIN: { + switch (_community) { + case 1: if(_networking) AccountLogin(NICE); break; + case 2: if(_networking) AccountLogin(BTPRO); break; + case 3: if(/*_novahost && */_networking) AccountLogin(CITYMANIA); break; + } break; - case LWW_BTPRO_LOGIN: - SetDParamStr(0, _inilogindata[BTPROUSER]); + } + case LWW_USER_LOGOUT: + NetworkClientSendChatToServer("!logout"); break; + case LWW_ADMIN_LOGIN: { + switch (_community) { + case 1: { + if(_networking) NetworkClientSendChatToServer(fmt::format("!alogin {} {}", _inilogindata[NICEUSER], base64_decode(_inilogindata[NICEADMINPW]))); + break; + } + case 2: { + if(_networking) NetworkClientSendChatToServer(fmt::format("!alogin {} {}", _inilogindata[BTPROUSER], base64_decode(_inilogindata[BTPROADMINPW]))); + break; + } + } break; - case LWW_BTPRO_PW: - SetDParam(0, (GetLoginItem(BTPRO_PW).empty() ? CM_STR_LOGIN_WINDOW_NOT_SET : CM_STR_LOGIN_WINDOW_SET)); + } + case LWW_ADMIN_LOGOUT: + NetworkClientSendChatToServer("!alogout"); break; + } + } + + void OnQueryTextFinished(char * str) override + { + if (str == NULL) return; + switch(this->query_widget){ + case LQW_USER_NAME: { + switch (_community) { + case 1: SetLoginItem(NICE_LOGIN, str); break; + case 2: SetLoginItem(BTPRO_LOGIN, str); break; + case 3: SetLoginItem(NOVAPOLIS_LOGIN, str); break; + } break; + } + case LQW_USER_PW: { + switch (_community) { + case 1: { + Md5 password, salted_password; + MD5Hash digest; + + password.Append(str, strlen(str)); + password.Finish(digest); + auto first_pass = fmt::format("{:02x}", fmt::join(digest, "")); + + auto tobe_salted = fmt::format("nice{}client", first_pass); + salted_password.Append(tobe_salted.c_str(),tobe_salted.length()); + salted_password.Finish(digest); + auto second_pass = fmt::format("{:02x}", fmt::join(digest, "")); + + // Save the result to citymania.cfg + SetLoginItem(NICE_PW, second_pass); + break; + } + case 2: SetLoginItem(BTPRO_PW, str); break; + case 3: SetLoginItem(NOVAPOLIS_PW, str); break; + } + break; + } + case LQW_ADMIN_PW: { + switch (_community) { + case 1: SetLoginItem(NICE_ADMIN_PW, base64_encode((const unsigned char *)str, (int)strlen(str))); break; + case 2: SetLoginItem(BTPRO_ADMIN_PW, base64_encode((const unsigned char *)str, (int)strlen(str))); break; + } + break; + } + default: return; } + this->SetDirty(); } +}; - void OnClick([[maybe_unused]] Point pt, int widget, [[maybe_unused]] int click_count) override +struct AdminCompanyButtonsWindow : Window { + AdminCompanyButtonsQueryWidgets query_widget{}; + + AdminCompanyButtonsWindow(WindowDesc *desc, WindowNumber window_number) : Window(desc) { + + this->InitNested(window_number); + + /* disable not supported buttons for n-ice */ + if (GetServerItem(COMMUNITY) == "1") { + this->DisableWidget(ACB_COMPANY_SUSPEND); + this->DisableWidget(ACB_COMPANY_UNSUSPEND); + this->DisableWidget(ACB_COMPANY_AWARNING); + } + } + + static void CWCompanyResetCallback(Window* w, bool confirmed) + { + if (confirmed) NetworkClientSendChatToServer(fmt::format("!resetcompany {}",w->window_number)); + } + + static void CWCompanyResetSpecCallback(Window* w, bool confirmed) + { + if (confirmed) NetworkClientSendChatToServer(fmt::format("!resetcompanyspec {}",w->window_number)); + } + + static void CWCompanyResetKickCallback(Window* w, bool confirmed) + { + if (confirmed) NetworkClientSendChatToServer(fmt::format("!resetcompanykick {}",w->window_number)); + } + + + virtual void OnClick([[maybe_unused]] Point pt, int widget,[[maybe_unused]] int click_count) { + + int _company = this->window_number; switch (widget) { - case LWW_NOVAPOLIS: - if(/*_novahost && */_networking) AccountLogin(CITYMANIA); + case ACB_COMPANY_EMPTY: + NetworkClientSendChatToServer(fmt::format("!emptycompany {}",_company)); + MarkWholeScreenDirty(); break; - case LWW_NICE: - if(_networking) AccountLogin(NICE); + case ACB_COMPANY_RESET_KICK: + ShowQuery(CM_STR_ACB_RESET_COMP, CM_STR_ACB_RESET_KICK_SURE, this, CWCompanyResetKickCallback); + MarkWholeScreenDirty(); break; - case LWW_BTPRO: - if(_networking) AccountLogin(BTPRO); + case ACB_COMPANY_RESET_SPEC: + ShowQuery(CM_STR_ACB_RESET_COMP, CM_STR_ACB_RESET_SPEC_SURE, this, CWCompanyResetSpecCallback); + MarkWholeScreenDirty(); break; - case LWW_NOVAPOLIS_LOGIN: - case LWW_NICE_LOGIN: - case LWW_BTPRO_LOGIN: - this->query_widget = (LoginWindowQueryWidgets)widget; - ShowQueryString(STR_EMPTY, CM_STR_LOGIN_WINDOW_CHANGE_USERNAME, MAX_COMMUNITY_STRING_LEN, this, CS_ALPHANUMERAL, QSF_NONE); + case ACB_COMPANY_RESET: + ShowQuery(CM_STR_ACB_RESET_COMP, CM_STR_ACB_RESET_COMP_SURE, this, CWCompanyResetCallback); + MarkWholeScreenDirty(); break; - case LWW_NOVAPOLIS_PW: - case LWW_NICE_PW: - case LWW_BTPRO_PW: - this->query_widget = (LoginWindowQueryWidgets)widget; - ShowQueryString(STR_EMPTY, CM_STR_LOGIN_WINDOW_CHANGE_PASSWORD, MAX_COMMUNITY_STRING_LEN, this, CS_ALPHANUMERAL, QSF_NONE); + case ACB_RESET_COMPANY_TIMER: + this->query_widget = ACBQ_RESET_COMPANY_TIMER; + SetDParam(0, _company); + ShowQueryString(STR_EMPTY, CM_STR_ACB_RESET_TIMER_VALUE, 25, this, CS_NUMERAL, QSF_NONE); + break; + case ACB_RESET_COMPANY_TIMER_120: + NetworkClientSendChatToServer(fmt::format("!resetcompanytimer {} 120",_company)); + MarkWholeScreenDirty(); + break; + case ACB_RESET_COMPANY_TIMER_CANCEL: + NetworkClientSendChatToServer(fmt::format("!cancelresetcompany {}",_company)); + break; + case ACB_COMPANY_LOCK: + NetworkClientSendChatToServer(fmt::format("!lockcompany {}",_company)); + MarkWholeScreenDirty(); + break; + case ACB_COMPANY_UNLOCK: + NetworkClientSendChatToServer(fmt::format("!unlockcompany {}",_company)); + MarkWholeScreenDirty(); + break; + case ACB_COMPANY_KNOWN: + NetworkClientSendChatToServer(fmt::format("!known {}",_company)); + MarkWholeScreenDirty(); + break; + case ACB_COMPANY_RESET_KNOWN: + NetworkClientSendChatToServer(fmt::format("!resetknown {}",_company)); + MarkWholeScreenDirty(); + break; + case ACB_COMPANY_MOVE_PLAYER: + this->query_widget = ACBQ_COMPANY_MOVE_PLAYER; + SetDParam(0, _company); + ShowQueryString(STR_EMPTY, STR_NETWORK_SERVER_LIST_PLAYER_NAME, 250, this, CS_ALPHANUMERAL, QSF_NONE); + break; + case ACB_COMPANY_NEWSTICKET: + this->query_widget = ACBQ_COMPANY_NEWSTICKET; + SetDParam(0, _company); + ShowQueryString(STR_EMPTY, CM_STR_ACB_PLAYER_NEWSTICKET, 250, this, CS_ALPHANUMERAL, QSF_NONE); + break; + case ACB_COMPANY_NEWSTICKET_COMP: + this->query_widget = ACBQ_COMPANY_NEWSTICKET_COMP; + SetDParam(0, _company); + ShowQueryString(STR_EMPTY, CM_STR_ACB_PLAYER_NEWSTICKET, 250, this, CS_ALPHANUMERAL, QSF_NONE); + break; + case ACB_COMPANY_SUSPEND: + NetworkClientSendChatToServer(fmt::format("!suspend {}",_company)); + MarkWholeScreenDirty(); + break; + case ACB_COMPANY_UNSUSPEND: + NetworkClientSendChatToServer(fmt::format("!unsuspend {}",_company)); + MarkWholeScreenDirty(); + break; + case ACB_COMPANY_AWARNING: + NetworkClientSendChatToServer(fmt::format("!awarning {}",_company)); + MarkWholeScreenDirty(); + break; + case ACB_COMPANY_JOIN2: + NetworkClientSendChatToServer(fmt::format("!move #{} {}",_network_own_client_id, _company)); + MarkWholeScreenDirty(); + break; + case ACB_COMPANY_LEAVE: + NetworkClientRequestMove(COMPANY_SPECTATOR); + break; + case ACB_COMPANY_CANCEL: + this->Close(); break; } } - void OnQueryTextFinished(char * str) override + void SetStringParameters(WidgetID widget) const override + { + switch (widget) { + case ACB_COMPANY_CAPTION: + SetDParam(0, this->window_number); + break; + } + } + + void OnQueryTextFinished(char *str) { if (str == NULL) return; - switch(this->query_widget){ - case LQW_NOVAPOLIS_LOGIN: { - SetLoginItem(NOVAPOLIS_LOGIN, str); + switch (this->query_widget) { + default: NOT_REACHED(); + + case ACBQ_RESET_COMPANY_TIMER: + NetworkClientSendChatToServer(fmt::format("!resetcompanytimer {} {}",this->window_number,str)); + MarkWholeScreenDirty(); + break; + case ACBQ_COMPANY_NEWSTICKET: { + std::string buffer = GetString(STR_COMPANY_NAME); + NetworkClientSendChatToServer(fmt::format("!news {}: {}", buffer, str)); + MarkWholeScreenDirty(); break; } - case LQW_NOVAPOLIS_PW: { - SetLoginItem(NOVAPOLIS_PW, str); + case ACBQ_COMPANY_NEWSTICKET_COMP: { + std::string buffer = GetString(STR_COMPANY_NAME); + NetworkClientSendChatToServer(fmt::format("!news {} {}", this->window_number, str)); + MarkWholeScreenDirty(); break; } - case LQW_NICE_LOGIN: - case LQW_BTPRO_LOGIN: - case LQW_BTPRO_PW: { - auto item = urlencode(str); - SetLoginItem(INI_LOGIN_KEYS[this->query_widget - 3], item); // - LWW_NICE_LOGIN + NICE_LOGIN + case ACBQ_COMPANY_MOVE_PLAYER: { + NetworkClientSendChatToServer(fmt::format("!move #{} {}",str, this->window_number)); + MarkWholeScreenDirty(); break; } - case LQW_NICE_PW: { - Md5 password, salted_password; - MD5Hash digest; + } + } - password.Append(str, strlen(str)); - password.Finish(digest); - auto first_pass = fmt::format("{:02x}", fmt::join(digest, "")); +}; - auto tobe_salted = fmt::format("nice{}client", first_pass); - salted_password.Append(tobe_salted.c_str(),tobe_salted.length()); - salted_password.Finish(digest); - auto second_pass = fmt::format("{:02x}", fmt::join(digest, "")); +struct JoinLastServerWindow : Window { - // Save the result to citymania.cfg - SetLoginItem(NICE_PW, second_pass); - break; + JoinLastServerWindow(WindowDesc *desc, WindowNumber window_number) + : Window(desc) { + + this->InitNested(window_number); + + if (_settings_client.network.last_joined == "") + this->DisableWidget(LSW_BUTTON); + + //call for intro menue + ShowServerButtons(ls_left, ls_top, ls_height); + } + + virtual void OnClick([[maybe_unused]] Point pt, int widget,[[maybe_unused]] int click_count) + { + switch (widget) { + case LSW_BUTTON: { + std::string _server = _settings_client.network.last_joined; + if (_ctrl_pressed) + NetworkClientConnectGame(_server, COMPANY_NEW_COMPANY); + else + NetworkClientConnectGame(_server, COMPANY_SPECTATOR); } - default: return; + break; + } + }; + +}; + +struct ServerButtonsWindow : Window { + ServerButtonsQueryWidgets query_widget{}; + + std::int8_t _community = stoi(GetServerItem(COMMUNITY)); + + ServerButtonsWindow(WindowDesc *desc, WindowNumber window_number) : Window(desc) { + + this->InitNested(window_number); + + if(_community == 1){ + this->GetWidget(WID_SB_SELECT_NICE)->colour = COLOUR_ORANGE; + } + else if(_community == 2){ + this->GetWidget(WID_SB_SELECT_BTPRO)->colour = COLOUR_ORANGE; + } + else if(_community == 3){ + this->GetWidget(WID_SB_SELECT_CITYMANIA)->colour = COLOUR_ORANGE; + } + } + + virtual void OnClick([[maybe_unused]] Point pt, int widget, [[maybe_unused]] int click_count) + { + switch (widget) { + case WID_SB_SELECT_NICE: + SetServerItem(COMMUNITY, "1"); + GetCommunityServerListText(); + break; + case WID_SB_SELECT_BTPRO: + SetServerItem(COMMUNITY, "2"); + GetCommunityServerListText(); + break; + case WID_SB_SELECT_CITYMANIA: + SetServerItem(COMMUNITY, "3"); + GetCommunityServerListText(); + break; + default: + if (widget >= AC_SERVERS) { + if (GetCommunityServer(widget - AC_SERVERS + 1,false)) { + if (_ctrl_pressed) { + NetworkClientConnectGame(fmt::format("{}:{}", _cc_address, _cc_porti), COMPANY_NEW_COMPANY); + } else { + NetworkClientConnectGame(fmt::format("{}:{}", _cc_address, _cc_porti), COMPANY_SPECTATOR); + } + } + } else ShowErrorMessage(CM_STR_SB_SERVER_DISABLED, INVALID_STRING_ID, WL_ERROR); + break; + } + } + + static void GetServerName(int number) + { + size_t poscount = _server_list_text.find("servercount"); + std::string scount = _server_list_text.substr(poscount + 12, 3); + _servercount = std::stoi(scount); + + std::string name; + if (number < 10){ + name = fmt::format("NAME0{}",number); + } else { + name = fmt::format("NAME{}",number); + } + std::string server; + if (number < 10){ + server = fmt::format("SERVER0{}",number); + } else { + server = fmt::format("SERVER{}",number); + } + size_t posname = _server_list_text.find(name); + std::string sname = _server_list_text.substr(posname + 8, _server_list_text.find(";", posname + 8) - posname - 8); + + if (number > _servercount) + _cc_name = "-"; + else + _cc_name = sname; + }; + + virtual void DrawWidget(const Rect &r, int widget) const override + { + std::string name; + + switch (widget) { + case AC_SERVERS: + default: + if(widget >= AC_SERVERS){ + if(widget - AC_SERVERS + 1 < 10){ + name = fmt::format("NAME0{}",widget - AC_SERVERS +1); + } + else { + name = fmt::format("NAME{}",widget - AC_SERVERS +1); + } + size_t posname = _server_list_text.find(name); + std::string sname = _server_list_text.substr(posname + 8, _server_list_text.find(";", posname + 8) - posname - 8); + _cc_name = sname; + + SetDParamStr(0, _cc_name); + DrawString(r.left, r.right, r.top + 3, CM_STR_SB_NETWORK_DIRECT_JOIN_GAME, TC_FROMSTRING, SA_CENTER); + } + break; } - this->SetDirty(); } }; +std::unique_ptr MakeServerButtons() +{ + std::int8_t _community = stoi(GetServerItem(COMMUNITY)); + auto ver = std::make_unique(); + + if(_community == 0 || _server_list_text.empty()){ + auto leaf = std::make_unique(WWT_PANEL, COLOUR_GREY, AC_SERVERS); + ver->Add(std::move(leaf)); + return ver; + } + + /* check for disabled server from serverlist file */ + int active = 0, aactive[50]{}, s_max = 0; + if (_community == 1) s_max = 50; //for n-ice + if (_community == 2) s_max = 30; //for btpro + if (_community == 3) s_max = 10; //for citymania + for (int i = 0; i < s_max; i++) { + aactive[i] = GetCommunityServer(i + 1, true) ? (i + 1) : 0; //server disabled? + active++; + } + + auto hor = std::make_unique(); + int i1 = 0, i2 = 0; + for (int i = 0; i < s_max; i++) { + if ((aactive[i] == 0) && (_community == 1)) continue; //hide button if disabled - for n-ice only + i2++; + if ((i1 == 5) || (i1 == 10) || (i1 == 15) || (i1 == 20) || (i1 == 25) || (i1 == 30) || (i1 == 35) || (i1 == 40) || (i1 == 45) || (i1 == 50)) { + i2=0; + auto spce = std::make_unique(3, 0); + spce->SetFill(1, 0); + hor->Add(std::move(spce)); + ver->Add(std::move(hor)); + auto spc = std::make_unique(0, 4); + spc->SetFill(1, 0); + ver->Add(std::move(spc)); + hor = std::make_unique(); + } + auto spce = std::make_unique(4, 0); + spce->SetFill(1, 0); + hor->Add(std::move(spce)); + auto leaf = std::make_unique(WWT_PANEL, COLOUR_ORANGE, AC_SERVERS + i); + if(aactive[i] == 0) leaf->SetDisabled(true); + leaf->SetDataTip(CM_STR_SB_NETWORK_DIRECT_JOIN_GAME, CM_STR_SB_NETWORK_DIRECT_JOIN_TOOLTIP); + leaf->SetMinimalSize(79, 15); + hor->Add(std::move(leaf)); + i1++; + } + + /* arrange buttons @ last line */ + if (i2==0) i2=375; + if (i2==1) i2=282; + if (i2==2) i2=189; + if (i2==3) i2=96; + if (i2==4) i2=3; + auto spce = std::make_unique(i2, 0); + spce->SetFill(1, 0); + hor->Add(std::move(spce)); + ver->Add(std::move(hor)); + return ver; +} + + static const NWidgetPart _nested_login_window_widgets[] = { NWidget(NWID_HORIZONTAL), - NWidget(WWT_CLOSEBOX, COLOUR_GREY), - NWidget(WWT_CAPTION, COLOUR_GREY), SetDataTip(CM_STR_LOGIN_WINDOW_CAPTION, 0), - NWidget(WWT_STICKYBOX, COLOUR_GREY), + NWidget(WWT_CLOSEBOX, COLOUR_ORANGE), + NWidget(WWT_CAPTION, COLOUR_ORANGE), SetDataTip(CM_STR_LOGIN_WINDOW_CAPTION, 0), EndContainer(), - NWidget(WWT_PANEL, COLOUR_GREY), SetResize(1, 0), - NWidget(NWID_VERTICAL, NC_EQUALSIZE), SetPadding(10), - //novapolis - NWidget(NWID_HORIZONTAL, NC_EQUALSIZE), + NWidget(WWT_PANEL, COLOUR_BROWN), SetFill(0,1), + NWidget(NWID_VERTICAL, NC_EQUALSIZE), + //welcome + NWidget(NWID_HORIZONTAL), + NWidget(NWID_SPACER), SetMinimalSize(112, 0), SetFill(1, 0), + NWidget(WWT_TEXT, COLOUR_BROWN, LWW_COMMUNITY), SetMinimalSize(200, 40), SetAlignment(SA_CENTER), SetDataTip(CM_STR_LOGIN_WINDOW_WELCOME, 0), SetFill(1, 1), + NWidget(NWID_SPACER), SetMinimalSize(112, 0), SetFill(1, 0), + EndContainer(), + NWidget(NWID_SPACER), SetMinimalSize(0, 5), + //username and pw + NWidget(NWID_HORIZONTAL), + NWidget(NWID_SPACER), SetMinimalSize(45, 0), NWidget(WWT_TEXT, COLOUR_BROWN, LWW_USERNAME), SetDataTip(CM_STR_LOGIN_WINDOW_USERNAME, 0), - NWidget(NWID_SPACER), SetMinimalSize(20, 0), + NWidget(NWID_SPACER), SetMinimalSize(5, 0), + NWidget(WWT_PUSHTXTBTN, COLOUR_BROWN, LWW_USER_NAME), SetMinimalSize(100, 15), SetFill(1, 1), + SetDataTip(CM_STR_LOGIN_WINDOW_USERNAME_DISPLAY, CM_STR_LOGIN_WINDOW_CHANGE_USERNAME_TOOLTIP), + NWidget(NWID_SPACER), SetMinimalSize(20, 0), NWidget(WWT_TEXT, COLOUR_BROWN, LWW_PASSWORD), SetDataTip(CM_STR_LOGIN_WINDOW_PASSWORD, 0), + NWidget(NWID_SPACER), SetMinimalSize(5, 0), + NWidget(WWT_PUSHTXTBTN, COLOUR_BROWN, LWW_USER_PW), SetMinimalSize(50, 15), SetFill(1, 1), + SetDataTip(CM_STR_LOGIN_WINDOW_PASSWORD_DISPLAY, CM_STR_LOGIN_WINDOW_CHANGE_PASSWORD_TOOLTIP), + NWidget(NWID_SPACER), SetMinimalSize(45, 0), EndContainer(), - NWidget(NWID_SPACER), SetMinimalSize(0, 5), - NWidget(NWID_HORIZONTAL, NC_EQUALSIZE), - NWidget(WWT_PUSHTXTBTN, COLOUR_BROWN, LWW_NOVAPOLIS_LOGIN), SetMinimalSize(60, 20), SetFill(1, 0), SetDataTip(CM_STR_LOGIN_WINDOW_USERNAME_DISPLAY, CM_STR_LOGIN_WINDOW_CHANGE_USERNAME_HELPTEXT), - NWidget(NWID_SPACER), SetMinimalSize(20, 0), - NWidget(WWT_PUSHTXTBTN, COLOUR_BROWN, LWW_NOVAPOLIS_PW), SetMinimalSize(30, 20), SetFill(1, 0), SetDataTip(CM_STR_LOGIN_WINDOW_PASSWORD_DISPLAY, CM_STR_LOGIN_WINDOW_CHANGE_PASSWORD_HELPTEXT), - EndContainer(), + NWidget(NWID_SPACER), SetMinimalSize(0, 20), + //login and logout + NWidget(NWID_HORIZONTAL), + NWidget(NWID_SPACER), SetMinimalSize(105, 0), + NWidget(WWT_PUSHTXTBTN, COLOUR_ORANGE, LWW_USER_LOGIN), SetMinimalSize(40, 20), SetAlignment(SA_CENTER), SetFill(1, 1), + SetDataTip(CM_STR_LOGIN_WINDOW_LOGIN, CM_STR_LOGIN_WINDOW_LOGIN_TOOLTIP), + NWidget(NWID_SPACER), SetMinimalSize(10, 0), + NWidget(WWT_PUSHTXTBTN, COLOUR_ORANGE, LWW_USER_LOGOUT), SetMinimalSize(40, 20), SetAlignment(SA_CENTER), SetFill(1, 1), + SetDataTip(CM_STR_LOGIN_WINDOW_LOGOUT, CM_STR_LOGIN_WINDOW_LOGOUT_TOOLTIP), + NWidget(NWID_SPACER), SetMinimalSize(105, 0), + EndContainer(), NWidget(NWID_SPACER), SetMinimalSize(0, 10), - NWidget(WWT_PUSHTXTBTN, COLOUR_PURPLE, LWW_NOVAPOLIS), SetMinimalSize(100, 30), SetFill(1, 0), SetDataTip(CM_STR_LOGIN_WINDOW_CITYMANIA, CM_STR_LOGIN_WINDOW_SIGN_IN_HELPTEXT), + EndContainer(), + EndContainer(), +}; + +static const NWidgetPart _nested_admin_window_widgets[] = { + NWidget(NWID_HORIZONTAL), + NWidget(WWT_CLOSEBOX, COLOUR_ORANGE), + NWidget(WWT_CAPTION, COLOUR_ORANGE), SetDataTip(CM_STR_LOGIN_WINDOW_CAPTION, 0), + EndContainer(), + NWidget(WWT_PANEL, COLOUR_BROWN), SetFill(0, 1), + NWidget(NWID_VERTICAL), + //welcome + NWidget(NWID_HORIZONTAL), + NWidget(NWID_SPACER), SetMinimalSize(112, 0), SetFill(1, 0), + NWidget(WWT_TEXT, COLOUR_BROWN, LWW_COMMUNITY), SetMinimalSize(200, 40), SetAlignment(SA_CENTER), SetDataTip(CM_STR_LOGIN_WINDOW_WELCOME, 0), SetFill(1, 1), + NWidget(NWID_SPACER), SetMinimalSize(112, 0), SetFill(1, 0), + EndContainer(), NWidget(NWID_SPACER), SetMinimalSize(0, 10), - //n-ice - NWidget(NWID_HORIZONTAL, NC_EQUALSIZE), + //username and pw + NWidget(NWID_HORIZONTAL), + NWidget(NWID_SPACER), SetMinimalSize(15, 0), NWidget(WWT_TEXT, COLOUR_BROWN, LWW_USERNAME), SetDataTip(CM_STR_LOGIN_WINDOW_USERNAME, 0), - NWidget(NWID_SPACER), SetMinimalSize(20, 0), + NWidget(NWID_SPACER), SetMinimalSize(5, 0), + NWidget(WWT_PUSHTXTBTN, COLOUR_BROWN, LWW_USER_NAME), SetMinimalSize(100, 15), SetFill(1, 1), + SetDataTip(CM_STR_LOGIN_WINDOW_USERNAME_DISPLAY, CM_STR_LOGIN_WINDOW_CHANGE_USERNAME_TOOLTIP), + NWidget(NWID_SPACER), SetMinimalSize(10, 0), NWidget(WWT_TEXT, COLOUR_BROWN, LWW_PASSWORD), SetDataTip(CM_STR_LOGIN_WINDOW_PASSWORD, 0), + NWidget(NWID_SPACER), SetMinimalSize(5, 0), + NWidget(WWT_PUSHTXTBTN, COLOUR_BROWN, LWW_USER_PW), SetMinimalSize(50, 15), SetFill(1, 1), + SetDataTip(CM_STR_LOGIN_WINDOW_PASSWORD_DISPLAY, CM_STR_LOGIN_WINDOW_CHANGE_PASSWORD_TOOLTIP), + NWidget(NWID_SPACER), SetMinimalSize(10, 0), + NWidget(WWT_TEXT, COLOUR_BROWN, LWW_ADMIN_PW), SetDataTip(CM_STR_LOGIN_WINDOW_ADMIN_PASSWORD, 0), + NWidget(NWID_SPACER), SetMinimalSize(5, 0), + NWidget(WWT_PUSHTXTBTN, COLOUR_BROWN, LWW_ADMIN_PW), SetMinimalSize(50, 15), SetFill(1, 1), + SetDataTip(CM_STR_LOGIN_WINDOW_PASSWORD_DISPLAY, CM_STR_LOGIN_WINDOW_CHANGE_PASSWORD_TOOLTIP), + NWidget(NWID_SPACER), SetMinimalSize(15, 0), EndContainer(), - NWidget(NWID_SPACER), SetMinimalSize(0, 5), - NWidget(NWID_HORIZONTAL, NC_EQUALSIZE), - NWidget(WWT_PUSHTXTBTN, COLOUR_BROWN, LWW_NICE_LOGIN), SetMinimalSize(60, 20), SetFill(1, 0), SetDataTip(CM_STR_LOGIN_WINDOW_USERNAME_DISPLAY, CM_STR_LOGIN_WINDOW_CHANGE_USERNAME_HELPTEXT), - NWidget(NWID_SPACER), SetMinimalSize(20, 0), - NWidget(WWT_PUSHTXTBTN, COLOUR_BROWN, LWW_NICE_PW), SetMinimalSize(30, 20), SetFill(1, 0), SetDataTip(CM_STR_LOGIN_WINDOW_PASSWORD_DISPLAY, CM_STR_LOGIN_WINDOW_CHANGE_PASSWORD_HELPTEXT), - EndContainer(), + NWidget(NWID_SPACER), SetMinimalSize(0, 20), + //login and logout + NWidget(NWID_HORIZONTAL), + NWidget(NWID_SPACER), SetMinimalSize(15, 0), + NWidget(WWT_PUSHTXTBTN, COLOUR_ORANGE, LWW_USER_LOGIN), SetMinimalSize(40, 20), SetAlignment(SA_CENTER), SetFill(1, 1), + SetDataTip(CM_STR_LOGIN_WINDOW_LOGIN, CM_STR_LOGIN_WINDOW_LOGIN_TOOLTIP), + NWidget(NWID_SPACER), SetMinimalSize(10, 0), + NWidget(WWT_PUSHTXTBTN, COLOUR_ORANGE, LWW_USER_LOGOUT), SetMinimalSize(40, 20), SetAlignment(SA_CENTER), SetFill(1, 1), + SetDataTip(CM_STR_LOGIN_WINDOW_LOGOUT, CM_STR_LOGIN_WINDOW_LOGOUT_TOOLTIP), + NWidget(NWID_SPACER), SetMinimalSize(10, 0), + NWidget(WWT_PUSHTXTBTN, COLOUR_ORANGE, LWW_ADMIN_LOGIN), SetMinimalSize(40, 20), SetAlignment(SA_CENTER), SetFill(1, 1), + SetDataTip(CM_STR_LOGIN_WINDOW_ADMIN_LOGIN, CM_STR_LOGIN_WINDOW_ADMIN_LOGIN_TOOLTIP), + NWidget(NWID_SPACER), SetMinimalSize(10, 0), + NWidget(WWT_PUSHTXTBTN, COLOUR_ORANGE, LWW_ADMIN_LOGOUT), SetMinimalSize(40, 20), SetAlignment(SA_CENTER), SetFill(1, 1), + SetDataTip(CM_STR_LOGIN_WINDOW_ADMIN_LOGOUT, CM_STR_LOGIN_WINDOW_ADMIN_LOGOUT_TOOLTIP), + NWidget(NWID_SPACER), SetMinimalSize(15, 0), + EndContainer(), NWidget(NWID_SPACER), SetMinimalSize(0, 10), - NWidget(WWT_PUSHTXTBTN, COLOUR_LIGHT_BLUE, LWW_NICE), SetMinimalSize(100, 30), SetFill(1, 0), SetDataTip(CM_STR_LOGIN_WINDOW_NICE, CM_STR_LOGIN_WINDOW_SIGN_IN_HELPTEXT), - NWidget(NWID_SPACER), SetMinimalSize(0, 10), - //btpro - NWidget(NWID_HORIZONTAL, NC_EQUALSIZE), - NWidget(WWT_TEXT, COLOUR_BROWN, LWW_USERNAME), SetDataTip(CM_STR_LOGIN_WINDOW_USERNAME, 0), - NWidget(NWID_SPACER), SetMinimalSize(20, 0), - NWidget(WWT_TEXT, COLOUR_BROWN, LWW_PASSWORD), SetDataTip(CM_STR_LOGIN_WINDOW_PASSWORD, 0), + EndContainer(), + EndContainer(), +}; + +static const NWidgetPart _nested_admin_company_window_widgets[] = { + NWidget(NWID_HORIZONTAL), + NWidget(WWT_CAPTION,COLOUR_END, ACB_COMPANY_CAPTION), + SetDataTip(CM_STR_ACB_COMPANY_ADMIN_CAPTION, 0), + SetMinimalSize(10,17), + EndContainer(), + NWidget(WWT_PANEL, COLOUR_GREY), SetFill(0, 1), + NWidget(NWID_HORIZONTAL), + NWidget(NWID_SPACER), SetMinimalSize(5, 0), SetFill(1, 0), + NWidget(NWID_VERTICAL, NC_EQUALSIZE), SetPIP(5, 3, 5), + NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, ACB_COMPANY_LOCK), SetMinimalSize(10, 13), SetFill(1, 0), SetDataTip(CM_STR_ACB_LOCK, CM_STR_ACB_LOCK_TOOLTIP), + NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, ACB_COMPANY_UNLOCK), SetMinimalSize(10, 13), SetFill(1, 0), SetDataTip(CM_STR_ACB_UNLOCK, CM_STR_ACB_UNLOCK_TOOLTIP), + NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, ACB_COMPANY_KNOWN), SetMinimalSize(10, 13), SetFill(1, 0), SetDataTip(CM_STR_ACB_KNOWN, CM_STR_ACB_KNOWN_TOOLTIP), + NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, ACB_COMPANY_RESET_KNOWN), SetMinimalSize(10, 13), SetFill(1, 0), SetDataTip(CM_STR_ACB_RESET_KNOWN, CM_STR_ACB_RESET_KNOWN_TOOLTIP), + NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, ACB_COMPANY_MOVE_PLAYER), SetMinimalSize(10, 13), SetFill(1, 0), SetDataTip(CM_STR_ACB_MOVE_PLAYER_TO, CM_STR_ACB_MOVE_PLAYER_TO_TOOLTIP), + NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, ACB_COMPANY_NEWSTICKET_COMP), SetMinimalSize(10, 13), SetFill(1, 0), SetDataTip(CM_STR_ACB_COMPANY_NEWSTICKET_BUTTON_COMP, CM_STR_ACB_COMPANY_NEWSTICKET_BUTTON_COMP_TOOLTIP), + NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, ACB_COMPANY_SUSPEND), SetMinimalSize(10, 13), SetFill(1, 0), SetDataTip(CM_STR_ACB_SUSPEND, CM_STR_ACB_SUSPEND_TOOLTIP), + NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, ACB_COMPANY_UNSUSPEND), SetMinimalSize(10, 13), SetFill(1, 0), SetDataTip(CM_STR_ACB_UNSUSPEND, CM_STR_ACB_UNSUSPEND_TOOLTIP), + NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, ACB_COMPANY_JOIN2),SetMinimalSize(10, 13), SetFill(1, 0),SetDataTip(CM_STR_ACB_COMPANY_JOIN2, CM_STR_ACB_COMPANY_JOIN2_TOOLTIP), EndContainer(), - NWidget(NWID_SPACER), SetMinimalSize(0, 5), - NWidget(NWID_HORIZONTAL, NC_EQUALSIZE), - NWidget(WWT_PUSHTXTBTN, COLOUR_BROWN, LWW_BTPRO_LOGIN), SetMinimalSize(60, 20), SetFill(1, 0), SetDataTip(CM_STR_LOGIN_WINDOW_USERNAME_DISPLAY, CM_STR_LOGIN_WINDOW_CHANGE_USERNAME_HELPTEXT), - NWidget(NWID_SPACER), SetMinimalSize(20, 0), - NWidget(WWT_PUSHTXTBTN, COLOUR_BROWN, LWW_BTPRO_PW), SetMinimalSize(30, 20), SetFill(1, 0), SetDataTip(CM_STR_LOGIN_WINDOW_PASSWORD_DISPLAY, CM_STR_LOGIN_WINDOW_CHANGE_PASSWORD_HELPTEXT), + NWidget(NWID_SPACER), SetMinimalSize(5, 0), SetFill(1, 0), + NWidget(NWID_VERTICAL, NC_EQUALSIZE), SetPIP(5, 3, 5), + NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, ACB_COMPANY_EMPTY), SetMinimalSize(10, 13), SetFill(1, 0), SetDataTip(CM_STR_ACB_EMPTY, CM_STR_ACB_EMPTY_TOOLTIP), + NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, ACB_COMPANY_RESET), SetMinimalSize(10, 13), SetFill(1, 0), SetDataTip(CM_STR_ACB_RESET, CM_STR_ACB_RESET_TOOLTIP), + NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, ACB_COMPANY_RESET_SPEC), SetMinimalSize(10, 13), SetFill(1, 0), SetDataTip(CM_STR_ACB_RESET_SPEC, CM_STR_ACB_RESET_SPEC_TOOLTIP), + NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, ACB_COMPANY_RESET_KICK), SetMinimalSize(10, 13), SetFill(1, 0), SetDataTip(CM_STR_ACB_RESET_KICK, CM_STR_ACB_RESET_KICK_TOOLTIP), + NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, ACB_RESET_COMPANY_TIMER_120), SetMinimalSize(10, 13), SetFill(1, 0), SetDataTip(CM_STR_ACB_RESET_TIMER_120, CM_STR_ACB_RESET_TIMER_120_TOOLTIP), + NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, ACB_RESET_COMPANY_TIMER), SetMinimalSize(10, 13), SetFill(1, 0), SetDataTip(CM_STR_ACB_RESET_TIMER, CM_STR_ACB_RESET_TIMER_TOOLTIP), + NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, ACB_RESET_COMPANY_TIMER_CANCEL), SetMinimalSize(10, 13), SetFill(1, 0), SetDataTip(CM_STR_ACB_RESET_TIMER_CANCEL, CM_STR_ACB_RESET_TIMER_CANCEL_TOOLTIP), + NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, ACB_COMPANY_AWARNING), SetMinimalSize(10, 13), SetFill(1, 0), SetDataTip(CM_STR_ACB_AWARNING, CM_STR_ACB_AWARNING_TOOLTIP), + NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, ACB_COMPANY_LEAVE),SetMinimalSize(10, 13), SetFill(1, 0),SetDataTip(STR_NETWORK_COMPANY_LIST_SPECTATE, STR_NETWORK_COMPANY_LIST_SPECTATE), EndContainer(), - NWidget(NWID_SPACER), SetMinimalSize(0, 10), - NWidget(WWT_PUSHTXTBTN, COLOUR_ORANGE, LWW_BTPRO), SetMinimalSize(100, 30), SetFill(1, 0), SetDataTip(CM_STR_LOGIN_WINDOW_BTPRO, CM_STR_LOGIN_WINDOW_SIGN_IN_HELPTEXT), + NWidget(NWID_SPACER), SetMinimalSize(5, 0), SetFill(1, 0), EndContainer(), + NWidget(NWID_SPACER), SetMinimalSize(0, 5), SetFill(0, 1), EndContainer(), }; +static const NWidgetPart _nested_last_server_widgets[] = { + NWidget(WWT_PANEL, COLOUR_BROWN), SetFill(0, 1), + NWidget(NWID_HORIZONTAL), SetPadding(WidgetDimensions::unscaled.sparse), + NWidget(NWID_SPACER), SetMinimalSize(81, 0), SetFill(1, 0), + NWidget(WWT_PUSHTXTBTN, COLOUR_ORANGE, LSW_BUTTON), SetMinimalSize(242, 15), SetAlignment(SA_CENTER), SetDataTip(CM_STR_SB_LAST_SERVER, CM_STR_SB_NETWORK_DIRECT_JOIN_TOOLTIP), SetFill(1, 1), + NWidget(NWID_SPACER), SetMinimalSize(81, 0), SetFill(1, 0), + EndContainer(), + EndContainer(), +}; + +static const NWidgetPart _nested_server_buttons_window_widgets[] = { + NWidget(WWT_PANEL, COLOUR_BROWN), SetFill(0, 1), + NWidget(NWID_HORIZONTAL, NC_EQUALSIZE), SetPadding(4), SetPIP(0, 7, 0), + NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_SB_SELECT_NICE), SetMinimalSize(134, 13), SetDataTip(CM_STR_SB_SELECT_NICE, 0), + NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_SB_SELECT_BTPRO), SetMinimalSize(134, 13), SetDataTip(CM_STR_SB_SELECT_BTPRO, 0), + NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_SB_SELECT_CITYMANIA), SetMinimalSize(134, 13), SetDataTip(CM_STR_SB_SELECT_CITYMANIA, 0), + EndContainer(), + NWidget(NWID_SPACER), SetMinimalSize(0, 3), + NWidget(NWID_HORIZONTAL, NC_EQUALSIZE), + NWidgetFunction(MakeServerButtons), + EndContainer(), + NWidget(NWID_SPACER), SetMinimalSize(0, 3), + EndContainer(), +}; + static WindowDesc _login_window_desc(__FILE__, __LINE__, WDP_CENTER, "cm_login", 0, 0, CM_WC_LOGIN_WINDOW, WC_NONE, @@ -838,11 +1573,153 @@ static WindowDesc _login_window_desc(__FILE__, __LINE__, std::begin(_nested_login_window_widgets), std::end(_nested_login_window_widgets) ); -void ShowLoginWindow() -{ - IniLoginInitiate(); - CloseWindowByClass(CM_WC_LOGIN_WINDOW); - AllocateWindowDescFront(&_login_window_desc, 0); +static WindowDesc _admin_window_desc(__FILE__, __LINE__, + WDP_CENTER, "cm_login", 0, 0, + CM_WC_LOGIN_WINDOW, WC_NONE, + WDF_CONSTRUCTION, + std::begin(_nested_admin_window_widgets), std::end(_nested_admin_window_widgets) +); + +static WindowDesc _admin_company_buttons_desc(__FILE__, __LINE__, + WDP_AUTO, NULL, 0, 0, + CM_WC_ADMIN_COMPANY_BUTTONS, WC_NONE, + WDF_CONSTRUCTION, + std::begin(_nested_admin_company_window_widgets), std::end(_nested_admin_company_window_widgets) +); + +static WindowDesc _last_server_desc(__FILE__, __LINE__, + WDP_AUTO, NULL, 0, 0, + CM_LAST_SERVER, + WC_NONE, WDF_CONSTRUCTION, + std::begin(_nested_last_server_widgets), std::end(_nested_last_server_widgets)); + +static WindowDesc _server_buttons_desc(__FILE__, __LINE__, + WDP_AUTO, NULL, 0, 0, + CM_WC_SERVER_BUTTONS, WC_NONE, + WDF_CONSTRUCTION, + std::begin(_nested_server_buttons_window_widgets), std::end(_nested_server_buttons_window_widgets) +); + +/* Identify the current community */ +void CheckCommunity() { + if (_networking) { + if (_network_server_name.find("n-ice.org") != std::string::npos) { + if (GetServerItem(COMMUNITY) != "1") { + SetServerItem(COMMUNITY, "1"); + GetCommunityServerListText(); + } + } else if (_network_server_name.find("BTPro.nl") != std::string::npos) { + if (GetServerItem(COMMUNITY) != "2") { + SetServerItem(COMMUNITY, "2"); + GetCommunityServerListText(); + } + } else if (_network_server_name.find("CityMania.org") != + std::string::npos) { + if (GetServerItem(COMMUNITY) != "3") { + SetServerItem(COMMUNITY, "3"); + GetCommunityServerListText(); + } + } else if (_network_server_name.find("reddit.com") != + std::string::npos) { + if (GetServerItem(COMMUNITY) != "4") { + SetServerItem(COMMUNITY, "4"); + // GetCommunityServerListText(); + } + } else { // Unknown Server + SetServerItem(COMMUNITY, "0"); + } + } + /* for intro menue */ + if ((GetServerItem(COMMUNITY) == "1") || (GetServerItem(COMMUNITY) == "2") || (GetServerItem(COMMUNITY) == "3")) + GetCommunityServerListText(); +}; + +void CheckAdmin() { + IniInitiate(); + if (GetLoginItem(ADMIN) == "1") + _admin = true; +}; + +void ShowLoginWindow() { + IniInitiate(); + CheckCommunity(); + CheckAdmin(); + CloseWindowByClass(CM_WC_LOGIN_WINDOW); + if (!_admin) AllocateWindowDescFront(&_login_window_desc, 0); + else AllocateWindowDescFront(&_admin_window_desc, 0); +}; + +void ShowServerButtons(int left, int top, int height) { + + _left = left; + _top = top; + _height = height; + + IniInitiate(); + if (!FileExists(_personal_dir + "/citymania.cfg")) + return; + + /* create window at coordinates */ + Window *b; + if (FindWindowByClass(CM_WC_SERVER_BUTTONS)) + CloseWindowByClass(CM_WC_SERVER_BUTTONS); + b = new ServerButtonsWindow(&_server_buttons_desc, 0); + b->top = top+height; + b->left = left; + b->SetDirty(); +}; + +void ReloadServerButtons() { + ShowServerButtons(_left, _top, _height); } +void CreateCommunityServerList() { + IniInitiate(); + CheckCommunity(); +}; + +void ShowAdminCompanyButtons(int left, int top, int width, int company2, bool draw, bool redraw) { + if (!draw) { + if (FindWindowById(CM_WC_ADMIN_COMPANY_BUTTONS,company2)) + CloseWindowById(CM_WC_ADMIN_COMPANY_BUTTONS, company2); + /* clear for company */ + ACB_Location[company2 - 1][0]=0; + ACB_Location[company2 - 1][1]=0; + ACB_Location[company2 - 1][2]=0; + return; + } + if (!Company::IsValidID((CompanyID)(company2-1))) return; + if ((left == ACB_Location[company2 - 1][0]) && + (top == ACB_Location[company2 - 1][1]) && + (width == ACB_Location[company2 - 1][2]) && + (!redraw)) + return; + + /* save position of company window */ + ACB_Location[company2 - 1][0] = left; + ACB_Location[company2 - 1][1] = top; + ACB_Location[company2 - 1][2] = width; + if (FindWindowById(CM_WC_ADMIN_COMPANY_BUTTONS,company2)) + CloseWindowById(CM_WC_ADMIN_COMPANY_BUTTONS, company2); + Window *w; + w = new AdminCompanyButtonsWindow(&_admin_company_buttons_desc, company2); + w->top = top; + w->left = left + width; + w->SetDirty(); +}; + +/* last server widget */ +void JoinLastServer(int left, int top, int height) { + if (FindWindowByClass (CM_LAST_SERVER)) + CloseWindowByClass(CM_LAST_SERVER); + Window *d; + d = new JoinLastServerWindow(&_last_server_desc, 0); + d->top = top + height; + d->left = left; + ls_left = left; + ls_top = d->top; + ls_height = d->height; +} + + } // namespace citymania diff --git a/src/citymania/cm_commands_gui.hpp b/src/citymania/cm_commands_gui.hpp index 8dc4b17f..080ef12b 100644 --- a/src/citymania/cm_commands_gui.hpp +++ b/src/citymania/cm_commands_gui.hpp @@ -5,6 +5,13 @@ namespace citymania { void ShowCommandsToolbar(); void ShowLoginWindow(); +void CheckAdmin(); +void ShowAdminCompanyButtons(int left, int top, int width, int company2, bool draw, bool redraw); +void JoinLastServer(int left, int top, int height); +void CreateCommunityServerList(); +void ShowServerButtons(int left, int top, int height); + +bool GetAdmin(); } // namespace citymania diff --git a/src/citymania/cm_highlight.cpp b/src/citymania/cm_highlight.cpp index 3eb87646..e812d577 100644 --- a/src/citymania/cm_highlight.cpp +++ b/src/citymania/cm_highlight.cpp @@ -4,9 +4,11 @@ #include "cm_blueprint.hpp" #include "cm_commands.hpp" +#include "cm_highlight_type.hpp" #include "cm_main.hpp" #include "cm_overlays.hpp" #include "cm_station_gui.hpp" +#include "cm_type.hpp" #include "cm_zoning.hpp" #include "../core/math_func.hpp" @@ -20,6 +22,7 @@ #include "../newgrf_roadtype.h" #include "../newgrf_station.h" #include "../newgrf_industrytiles.h" +#include "../sound_func.h" #include "../spritecache.h" #include "../strings_func.h" #include "../town.h" @@ -29,22 +32,27 @@ #include "../tilehighlight_func.h" #include "../viewport_func.h" #include "../window_gui.h" +#include "../window_func.h" #include "../zoom_func.h" // #include "../zoning.h" #include "../table/airporttile_ids.h" +#include "../table/animcursors.h" #include "../table/track_land.h" #include "../table/autorail.h" #include "../table/industry_land.h" #include "../debug.h" +#include "generated/cm_gen_commands.hpp" #include "station_gui.h" #include "station_type.h" #include "table/sprites.h" #include "table/strings.h" #include "tile_type.h" +#include +#include +#include #include - /** Enumeration of multi-part foundations */ enum FoundationPart { FOUNDATION_PART_NONE = 0xFF, ///< Neither foundation nor groundsprite drawn yet. @@ -68,16 +76,8 @@ extern uint32 _cm_funding_layout; extern IndustryType _cm_funding_type; extern void GetStationLayout(byte *layout, uint numtracks, uint plat_len, const StationSpec *statspec); extern void IndustryDrawTileLayout(const TileInfo *ti, const TileLayoutSpriteGroup *group, byte rnd_colour, byte stage); - -struct RailStationGUISettings { - Axis orientation; ///< Currently selected rail station orientation - - bool newstations; ///< Are custom station definitions available? - StationClassID station_class; ///< Currently selected custom station class (if newstations is \c true ) - byte station_type; ///< %Station type within the currently selected custom station class (if newstations is \c true ) - byte station_count; ///< Number of custom stations (if newstations is \c true ) -}; -extern RailStationGUISettings _railstation; ///< Settings of the station builder GUI +extern void SetSelectionTilesDirty(); +extern citymania::RailStationGUISettings _railstation; ///< Settings of the station builder GUI struct RoadStopGUISettings { DiagDirection orientation; @@ -89,6 +89,78 @@ struct RoadStopGUISettings { extern RoadStopGUISettings _roadstop_gui_settings; +template <> +struct std::hash { + std::size_t operator()(const citymania::ObjectTileHighlight &oh) const { + std::size_t h = std::hash()(oh.palette); + h ^= hash()(oh.type); + switch (oh.type) { + case citymania::ObjectTileHighlight::Type::RAIL_DEPOT: + h ^= std::hash()(oh.u.rail.depot.ddir); + break; + case citymania::ObjectTileHighlight::Type::RAIL_TRACK: + h ^= std::hash()(oh.u.rail.track); + break; + case citymania::ObjectTileHighlight::Type::RAIL_STATION: + h ^= hash()(oh.u.rail.station.axis); + h ^= oh.u.rail.station.section; + break; + case citymania::ObjectTileHighlight::Type::RAIL_SIGNAL: + h ^= hash()(oh.u.rail.signal.pos); + h ^= hash()(oh.u.rail.signal.type); + h ^= hash()(oh.u.rail.signal.variant); + break; + case citymania::ObjectTileHighlight::Type::RAIL_BRIDGE_HEAD: + h ^= hash()(oh.u.rail.bridge_head.ddir); + h ^= hash()(oh.u.rail.bridge_head.other_end); + h ^= hash()(oh.u.rail.bridge_head.type); + break; + case citymania::ObjectTileHighlight::Type::RAIL_TUNNEL_HEAD: + h ^= hash()(oh.u.rail.tunnel_head.ddir); + break; + case citymania::ObjectTileHighlight::Type::ROAD_STOP: + h ^= hash()(oh.u.road.stop.ddir); + h ^= hash()(oh.u.road.stop.roadtype); + h ^= hash()(oh.u.road.stop.is_truck); + h ^= hash()(oh.u.road.stop.spec_class); + h ^= hash()(oh.u.road.stop.spec_index); + break; + case citymania::ObjectTileHighlight::Type::ROAD_DEPOT: + h ^= hash()(oh.u.road.depot.ddir); + h ^= hash()(oh.u.road.depot.roadtype); + break; + case citymania::ObjectTileHighlight::Type::DOCK_SLOPE: + h ^= hash()(oh.u.dock_slope.ddir); + break; + case citymania::ObjectTileHighlight::Type::DOCK_FLAT: + h ^= hash()(oh.u.dock_flat.axis); + break; + case citymania::ObjectTileHighlight::Type::AIRPORT_TILE: + h ^= hash()(oh.u.airport_tile.gfx); + break; + case citymania::ObjectTileHighlight::Type::INDUSTRY_TILE: + h ^= hash()(oh.u.industry_tile.gfx); + h ^= hash()(oh.u.industry_tile.ind_type); + h ^= oh.u.industry_tile.ind_layout; + h ^= hash()(oh.u.industry_tile.tile_diff); + break; + case citymania::ObjectTileHighlight::Type::NUMBERED_RECT: + h ^= hash()(oh.u.numbered_rect.number); + break; + case citymania::ObjectTileHighlight::Type::BORDER: + h ^= hash()(oh.u.border); + break; + case citymania::ObjectTileHighlight::Type::POINT: + case citymania::ObjectTileHighlight::Type::RECT: + case citymania::ObjectTileHighlight::Type::END: + case citymania::ObjectTileHighlight::Type::TINT: + case citymania::ObjectTileHighlight::Type::STRUCT_TINT: + break; + } + return h; + } +}; + namespace citymania { extern CargoArray GetProductionAroundTiles(TileIndex tile, int w, int h, int rad); @@ -111,7 +183,7 @@ extern StationBuildingStatus _station_building_status; extern const Station *_station_to_join; extern const Station *_highlight_station_to_join; extern TileArea _highlight_join_area; - +extern bool _fn_mod; std::set, std::greater>> _town_cache; // struct { @@ -192,6 +264,18 @@ ObjectTileHighlight ObjectTileHighlight::make_road_depot(SpriteID palette, RoadT return oh; } +ObjectTileHighlight ObjectTileHighlight::make_dock_slope(SpriteID palette, DiagDirection ddir) { + auto oh = ObjectTileHighlight(Type::DOCK_SLOPE, palette); + oh.u.dock_slope.ddir = ddir; + return oh; +} + +ObjectTileHighlight ObjectTileHighlight::make_dock_flat(SpriteID palette, Axis axis) { + auto oh = ObjectTileHighlight(Type::DOCK_FLAT, palette); + oh.u.dock_flat.axis = axis; + return oh; +} + ObjectTileHighlight ObjectTileHighlight::make_airport_tile(SpriteID palette, StationGfx gfx) { auto oh = ObjectTileHighlight(Type::AIRPORT_TILE, palette); oh.u.airport_tile.gfx = gfx; @@ -222,6 +306,107 @@ ObjectTileHighlight ObjectTileHighlight::make_numbered_rect(SpriteID palette, ui return oh; } +ObjectTileHighlight ObjectTileHighlight::make_border(SpriteID palette, ZoningBorder border) { + auto oh = ObjectTileHighlight(Type::BORDER, palette); + oh.u.border = border; + return oh; +} + +ObjectTileHighlight ObjectTileHighlight::make_tint(SpriteID palette) { + auto oh = ObjectTileHighlight(Type::TINT, palette); + return oh; +} + +ObjectTileHighlight ObjectTileHighlight::make_struct_tint(SpriteID palette) { + auto oh = ObjectTileHighlight(Type::STRUCT_TINT, palette); + return oh; +} + +bool ObjectTileHighlight::operator==(const ObjectTileHighlight &oh) const { + if (this->type != oh.type) return false; + if (this->palette != oh.palette) return false; + switch (this->type) { + case ObjectTileHighlight::Type::RAIL_DEPOT: + return this->u.rail.depot.ddir == oh.u.rail.depot.ddir; + case ObjectTileHighlight::Type::RAIL_TRACK: + return this->u.rail.track == oh.u.rail.track; + case ObjectTileHighlight::Type::RAIL_STATION: + return this->u.rail.station.axis == oh.u.rail.station.axis + && this->u.rail.station.section == oh.u.rail.station.section; + case ObjectTileHighlight::Type::RAIL_SIGNAL: + return this->u.rail.signal.pos == oh.u.rail.signal.pos + && this->u.rail.signal.type == oh.u.rail.signal.type + && this->u.rail.signal.variant == oh.u.rail.signal.variant; + case ObjectTileHighlight::Type::RAIL_BRIDGE_HEAD: + return this->u.rail.bridge_head.ddir == oh.u.rail.bridge_head.ddir + && this->u.rail.bridge_head.other_end == oh.u.rail.bridge_head.other_end + && this->u.rail.bridge_head.type == oh.u.rail.bridge_head.type; + case ObjectTileHighlight::Type::RAIL_TUNNEL_HEAD: + return this->u.rail.tunnel_head.ddir == oh.u.rail.tunnel_head.ddir; + case ObjectTileHighlight::Type::ROAD_STOP: + return this->u.road.stop.ddir == oh.u.road.stop.ddir + && this->u.road.stop.roadtype == oh.u.road.stop.roadtype + && this->u.road.stop.is_truck == oh.u.road.stop.is_truck + && this->u.road.stop.spec_class == oh.u.road.stop.spec_class + && this->u.road.stop.spec_index == oh.u.road.stop.spec_index; + case ObjectTileHighlight::Type::ROAD_DEPOT: + return this->u.road.depot.ddir == oh.u.road.depot.ddir + && this->u.road.depot.roadtype == oh.u.road.depot.roadtype; + case ObjectTileHighlight::Type::AIRPORT_TILE: + return this->u.airport_tile.gfx == oh.u.airport_tile.gfx; + case ObjectTileHighlight::Type::INDUSTRY_TILE: + return this->u.industry_tile.gfx == oh.u.industry_tile.gfx + && this->u.industry_tile.ind_type == oh.u.industry_tile.ind_type + && this->u.industry_tile.ind_layout == oh.u.industry_tile.ind_layout + && this->u.industry_tile.tile_diff == oh.u.industry_tile.tile_diff; + case ObjectTileHighlight::Type::NUMBERED_RECT: + return this->u.numbered_rect.number == oh.u.numbered_rect.number; + case Type::BORDER: + return this->u.border == oh.u.border; + case ObjectTileHighlight::Type::DOCK_SLOPE: + return this->u.dock_slope.ddir == oh.u.dock_slope.ddir; + case ObjectTileHighlight::Type::DOCK_FLAT: + return this->u.dock_flat.axis == oh.u.dock_flat.axis; + case Type::POINT: + case Type::RECT: + case Type::END: + case Type::TINT: + case Type::STRUCT_TINT: + return true; + } + return true; +} + +bool ObjectTileHighlight::SetTileHighlight(TileHighlight &th, const TileInfo *) const { + switch (this->type) { + case ObjectTileHighlight::Type::RAIL_DEPOT: + // case ObjectTileHighlight::Type::RAIL_TRACK: Depot track shouldn't remove foundation + case ObjectTileHighlight::Type::RAIL_STATION: + case ObjectTileHighlight::Type::RAIL_SIGNAL: + case ObjectTileHighlight::Type::RAIL_BRIDGE_HEAD: + case ObjectTileHighlight::Type::RAIL_TUNNEL_HEAD: + case ObjectTileHighlight::Type::ROAD_STOP: + case ObjectTileHighlight::Type::ROAD_DEPOT: + case ObjectTileHighlight::Type::AIRPORT_TILE: + case ObjectTileHighlight::Type::INDUSTRY_TILE: + case ObjectTileHighlight::Type::DOCK_SLOPE: + case ObjectTileHighlight::Type::DOCK_FLAT: + th.structure_pal = CM_PALETTE_HIDE_SPRITE; + th.highlight_ground_pal = th.highlight_structure_pal = this->palette; + return true; + case ObjectTileHighlight::Type::BORDER: + case ObjectTileHighlight::Type::TINT: + th.ground_pal = th.structure_pal = this->palette; + return true; + case ObjectTileHighlight::Type::STRUCT_TINT: + th.structure_pal = this->palette; + return true; + + default: + break; + } + return false; +} bool ObjectHighlight::operator==(const ObjectHighlight& oh) const { if (this->type != oh.type) return false; @@ -240,6 +425,7 @@ bool ObjectHighlight::operator==(const ObjectHighlight& oh) const { && this->blueprint == oh.blueprint); } + bool ObjectHighlight::operator!=(const ObjectHighlight& oh) const { return !(*this == oh); } @@ -822,6 +1008,18 @@ void DrawRoadStop(SpriteID palette, const TileInfo *ti, RoadType roadtype, DiagD // DrawRoadCatenary(ti); } +void DrawDockSlope(SpriteID palette, const TileInfo *ti, DiagDirection ddir) { + uint image = (uint)ddir; + const DrawTileSprites *t = GetStationTileLayout(STATION_DOCK, image); + DrawRailTileSeq(ti, t, TO_INVALID, 0, 0, palette); +} + +void DrawDockFlat(SpriteID palette, const TileInfo *ti, Axis axis) { + uint image = GFX_DOCK_BASE_WATER_PART + (uint)axis; + const DrawTileSprites *t = GetStationTileLayout(STATION_DOCK, image); + DrawRailTileSeq(ti, t, TO_INVALID, 0, 0, palette); +} + struct DrawRoadTileStruct { uint16 image; @@ -1268,95 +1466,106 @@ void DrawSelectionPoint(SpriteID palette, const TileInfo *ti) { DrawSelectionSprite(SPR_DOT, palette, ti, z, foundation_part); } +void DrawBorderSprites(const TileInfo *ti, ZoningBorder border, SpriteID color) { + auto b = (uint8)border & 15; + auto tile_sprite = CM_SPR_BORDER_HIGHLIGHT_BASE + _tileh_to_sprite[ti->tileh] * 19; + if (b) { + DrawSelectionSprite(tile_sprite + b - 1, color, ti, 7, FOUNDATION_PART_NORMAL); + } + if (border & ZoningBorder::TOP_CORNER) + DrawSelectionSprite(tile_sprite + 15, color, ti, 7, FOUNDATION_PART_NORMAL); + if (border & ZoningBorder::RIGHT_CORNER) + DrawSelectionSprite(tile_sprite + 16, color, ti, 7, FOUNDATION_PART_NORMAL); + if (border & ZoningBorder::BOTTOM_CORNER) + DrawSelectionSprite(tile_sprite + 17, color, ti, 7, FOUNDATION_PART_NORMAL); + if (border & ZoningBorder::LEFT_CORNER) + DrawSelectionSprite(tile_sprite + 18, color, ti, 7, FOUNDATION_PART_NORMAL); +} + TileHighlight ObjectHighlight::GetTileHighlight(const TileInfo *ti) { TileHighlight th; auto range = this->tiles.equal_range(ti->tile); for (auto t = range.first; t != range.second; t++) { - auto &oth = t->second; - switch (oth.type) { - case ObjectTileHighlight::Type::RAIL_DEPOT: - // case ObjectTileHighlight::Type::RAIL_TRACK: Depot track shouldn't remove foundation - case ObjectTileHighlight::Type::RAIL_STATION: - case ObjectTileHighlight::Type::RAIL_SIGNAL: - case ObjectTileHighlight::Type::RAIL_BRIDGE_HEAD: - case ObjectTileHighlight::Type::RAIL_TUNNEL_HEAD: - case ObjectTileHighlight::Type::ROAD_STOP: - case ObjectTileHighlight::Type::ROAD_DEPOT: - case ObjectTileHighlight::Type::AIRPORT_TILE: - case ObjectTileHighlight::Type::INDUSTRY_TILE: - th.structure_pal = CM_PALETTE_HIDE_SPRITE; - th.highlight_ground_pal = th.highlight_structure_pal = oth.palette; - break; + t->second.SetTileHighlight(th, ti); + } + return th; +} - default: - break; +static void DrawObjectTileHighlight(const TileInfo *ti, const ObjectTileHighlight &oth) { + switch (oth.type) { + case ObjectTileHighlight::Type::RAIL_DEPOT: + DrawTrainDepotSprite(oth.palette, ti, _cur_railtype, oth.u.rail.depot.ddir); + break; + case ObjectTileHighlight::Type::RAIL_TRACK: { + DrawAutorailSelection(ti, (HighLightStyle)oth.u.rail.track, GetSelectionColourByTint(oth.palette)); + break; } + case ObjectTileHighlight::Type::RAIL_STATION: + DrawTrainStationSprite(oth.palette, ti, _cur_railtype, oth.u.rail.station.axis, oth.u.rail.station.section); + break; + case ObjectTileHighlight::Type::RAIL_SIGNAL: + DrawSignal(oth.palette, ti, _cur_railtype, oth.u.rail.signal.pos, oth.u.rail.signal.type, oth.u.rail.signal.variant); + break; + case ObjectTileHighlight::Type::RAIL_BRIDGE_HEAD: + DrawBridgeHead(oth.palette, ti, _cur_railtype, oth.u.rail.bridge_head.ddir, oth.u.rail.bridge_head.type); + break; + case ObjectTileHighlight::Type::RAIL_TUNNEL_HEAD: + DrawTunnelHead(oth.palette, ti, _cur_railtype, oth.u.rail.tunnel_head.ddir); + break; + case ObjectTileHighlight::Type::ROAD_STOP: + DrawRoadStop(oth.palette, ti, oth.u.road.stop.roadtype, oth.u.road.stop.ddir, oth.u.road.stop.is_truck); + break; + case ObjectTileHighlight::Type::ROAD_DEPOT: + DrawRoadDepot(oth.palette, ti, oth.u.road.depot.roadtype, oth.u.road.depot.ddir); + break; + case ObjectTileHighlight::Type::DOCK_SLOPE: + DrawDockSlope(oth.palette, ti, oth.u.dock_slope.ddir); + break; + case ObjectTileHighlight::Type::DOCK_FLAT: + DrawDockFlat(oth.palette, ti, oth.u.dock_flat.axis); + break; + case ObjectTileHighlight::Type::AIRPORT_TILE: + DrawAirportTile(oth.palette, ti, oth.u.airport_tile.gfx); + break; + case ObjectTileHighlight::Type::INDUSTRY_TILE: + DrawIndustryTile(oth.palette, ti, oth.u.industry_tile.ind_type, oth.u.industry_tile.ind_layout, oth.u.industry_tile.gfx, oth.u.industry_tile.tile_diff); + break; + case ObjectTileHighlight::Type::POINT: + DrawSelectionPoint(oth.palette, ti); + break; + case ObjectTileHighlight::Type::RECT: + DrawTileSelectionRect(ti, oth.palette); + break; + case ObjectTileHighlight::Type::NUMBERED_RECT: { + // TODO NUMBERED_RECT should not be used atm anyway + // DrawTileSelectionRect(ti, oth.palette); + // auto string_id = oth.u.numbered_rect.number ? CM_STR_LAYOUT_NUM : CM_STR_LAYOUT_RANDOM; + // SetDParam(0, oth.u.numbered_rect.number); + // std::string buffer = GetString(string_id); + // auto bb = GetStringBoundingBox(buffer); + // ViewportSign sign; + // sign.width_normal = WidgetDimensions::scaled.fullbevel.left + Align(bb.width, 2) + WidgetDimensions::scaled.fullbevel.right; + // Point pt = RemapCoords2(TileX(ti->tile) * TILE_SIZE + TILE_SIZE / 2, TileY(ti->tile) * TILE_SIZE + TILE_SIZE / 2); + // sign.center = pt.x; + // sign.top = pt.y - bb.height / 2; + + // ViewportAddString(_cur_dpi, ZOOM_LVL_OUT_8X, &sign, + // string_id, STR_NULL, STR_NULL, oth.u.numbered_rect.number, 0, COLOUR_WHITE); + break; + } + case ObjectTileHighlight::Type::BORDER: { + DrawBorderSprites(ti, oth.u.border, oth.palette); + break; + } + default: + break; } - return th; } void ObjectHighlight::Draw(const TileInfo *ti) { auto range = this->tiles.equal_range(ti->tile); for (auto t = range.first; t != range.second; t++) { - auto &oth = t->second; - switch (oth.type) { - case ObjectTileHighlight::Type::RAIL_DEPOT: - DrawTrainDepotSprite(oth.palette, ti, _cur_railtype, oth.u.rail.depot.ddir); - break; - case ObjectTileHighlight::Type::RAIL_TRACK: { - DrawAutorailSelection(ti, (HighLightStyle)oth.u.rail.track, GetSelectionColourByTint(oth.palette)); - break; - } - case ObjectTileHighlight::Type::RAIL_STATION: - DrawTrainStationSprite(oth.palette, ti, _cur_railtype, oth.u.rail.station.axis, oth.u.rail.station.section); - break; - case ObjectTileHighlight::Type::RAIL_SIGNAL: - DrawSignal(oth.palette, ti, _cur_railtype, oth.u.rail.signal.pos, oth.u.rail.signal.type, oth.u.rail.signal.variant); - break; - case ObjectTileHighlight::Type::RAIL_BRIDGE_HEAD: - DrawBridgeHead(oth.palette, ti, _cur_railtype, oth.u.rail.bridge_head.ddir, oth.u.rail.bridge_head.type); - break; - case ObjectTileHighlight::Type::RAIL_TUNNEL_HEAD: - DrawTunnelHead(oth.palette, ti, _cur_railtype, oth.u.rail.tunnel_head.ddir); - break; - case ObjectTileHighlight::Type::ROAD_STOP: - DrawRoadStop(oth.palette, ti, oth.u.road.stop.roadtype, oth.u.road.stop.ddir, oth.u.road.stop.is_truck); - break; - case ObjectTileHighlight::Type::ROAD_DEPOT: - DrawRoadDepot(oth.palette, ti, oth.u.road.depot.roadtype, oth.u.road.depot.ddir); - break; - case ObjectTileHighlight::Type::AIRPORT_TILE: - DrawAirportTile(oth.palette, ti, oth.u.airport_tile.gfx); - break; - case ObjectTileHighlight::Type::INDUSTRY_TILE: - DrawIndustryTile(oth.palette, ti, oth.u.industry_tile.ind_type, oth.u.industry_tile.ind_layout, oth.u.industry_tile.gfx, oth.u.industry_tile.tile_diff); - break; - case ObjectTileHighlight::Type::POINT: - DrawSelectionPoint(oth.palette, ti); - break; - case ObjectTileHighlight::Type::RECT: - DrawTileSelectionRect(ti, oth.palette); - break; - case ObjectTileHighlight::Type::NUMBERED_RECT: { - // TODO NUMBERED_RECT should be used atm anyway - // DrawTileSelectionRect(ti, oth.palette); - // auto string_id = oth.u.numbered_rect.number ? CM_STR_LAYOUT_NUM : CM_STR_LAYOUT_RANDOM; - // SetDParam(0, oth.u.numbered_rect.number); - // std::string buffer = GetString(string_id); - // auto bb = GetStringBoundingBox(buffer); - // ViewportSign sign; - // sign.width_normal = WidgetDimensions::scaled.fullbevel.left + Align(bb.width, 2) + WidgetDimensions::scaled.fullbevel.right; - // Point pt = RemapCoords2(TileX(ti->tile) * TILE_SIZE + TILE_SIZE / 2, TileY(ti->tile) * TILE_SIZE + TILE_SIZE / 2); - // sign.center = pt.x; - // sign.top = pt.y - bb.height / 2; - - // ViewportAddString(_cur_dpi, ZOOM_LVL_OUT_8X, &sign, - // string_id, STR_NULL, STR_NULL, oth.u.numbered_rect.number, 0, COLOUR_WHITE); - break; - } - default: - break; - } + DrawObjectTileHighlight(ti, t->second); } // fprintf(stderr, "TILEH DRAW %d %d %d\n", ti->tile, (int)i, (int)this->tiles.size()); } @@ -1456,22 +1665,6 @@ bool CanBuildIndustryOnTileCached(IndustryType type, TileIndex tile) { return (_mz[tile.base()].industry_fund_result == 2); } -void DrawBorderSprites(const TileInfo *ti, ZoningBorder border, SpriteID color) { - auto b = (uint8)border & 15; - auto tile_sprite = CM_SPR_BORDER_HIGHLIGHT_BASE + _tileh_to_sprite[ti->tileh] * 19; - if (b) { - DrawSelectionSprite(tile_sprite + b - 1, color, ti, 7, FOUNDATION_PART_NORMAL); - } - if (border & ZoningBorder::TOP_CORNER) - DrawSelectionSprite(tile_sprite + 15, color, ti, 7, FOUNDATION_PART_NORMAL); - if (border & ZoningBorder::RIGHT_CORNER) - DrawSelectionSprite(tile_sprite + 16, color, ti, 7, FOUNDATION_PART_NORMAL); - if (border & ZoningBorder::BOTTOM_CORNER) - DrawSelectionSprite(tile_sprite + 17, color, ti, 7, FOUNDATION_PART_NORMAL); - if (border & ZoningBorder::LEFT_CORNER) - DrawSelectionSprite(tile_sprite + 18, color, ti, 7, FOUNDATION_PART_NORMAL); -} - SpriteID GetIndustryZoningPalette(TileIndex tile) { if (!IsTileType(tile, MP_INDUSTRY)) return PAL_NONE; Industry *ind = Industry::GetByTile(tile); @@ -1622,7 +1815,16 @@ void CalcCBTownLimitBorder(TileHighlight &th, TileIndex tile, SpriteID border_pa } TileHighlight GetTileHighlight(const TileInfo *ti, TileType tile_type) { - TileHighlight th = _thd.cm.GetTileHighlight(ti);; + TileHighlight th; + auto it = _ap.tiles.find(ti->tile); + if (it != _ap.tiles.end()) { + for (auto &oth : it->second) { + oth.SetTileHighlight(th, ti); + } + return th; + } + + th = _thd.cm.GetTileHighlight(ti);; if (ti->tile == INVALID_TILE || tile_type == MP_VOID) return th; if (_zoning.outer == CHECKTOWNZONES) { auto p = GetTownZoneBorder(ti->tile); @@ -1742,6 +1944,15 @@ void DrawTileZoning(const TileInfo *ti, const TileHighlight &th, TileType tile_t bool DrawTileSelection(const TileInfo *ti, [[maybe_unused]] const TileHighlightType &tht) { if (ti->tile == INVALID_TILE || IsTileType(ti->tile, MP_VOID)) return false; + + auto it = _ap.tiles.find(ti->tile); + if (it != _ap.tiles.end()) { + for (auto oth : it->second) { + DrawObjectTileHighlight(ti, oth); + } + return true; + } + _thd.cm.Draw(ti); if (_thd.drawstyle == CM_HT_BLUEPRINT_PLACE) return true; @@ -2064,4 +2275,132 @@ PaletteID GetTreeShadePal(TileIndex tile) { } } +ActivePreview _ap; + +static void ResetVanillaHighlight() { + if (_thd.window_class != WC_INVALID) { + /* Undo clicking on button and drag & drop */ + Window *w = _thd.GetCallbackWnd(); + /* Call the abort function, but set the window class to something + * that will never be used to avoid infinite loops. Setting it to + * the 'next' window class must not be done because recursion into + * this function might in some cases reset the newly set object to + * place or not properly reset the original selection. */ + _thd.window_class = WC_INVALID; + if (w != nullptr) { + w->OnPlaceObjectAbort(); + CloseWindowById(WC_TOOLTIPS, 0); + } + } + + /* Mark the old selection dirty, in case the selection shape or colour changes */ + if ((_thd.drawstyle & HT_DRAG_MASK) != HT_NONE) SetSelectionTilesDirty(); + + SetTileSelectSize(1, 1); + + _thd.make_square_red = false; +} + +void SetActivePreview(up &&preview) { + ResetVanillaHighlight(); + ResetActivePreview(); + _ap.preview = std::move(preview); +} + +void ResetActivePreview() { + for (auto &[t, l] : _ap.tiles) { + MarkTileDirtyByTile(t); + } + _ap.preview = nullptr; + _ap.tiles = {}; +} + +void UpdateActivePreview() { + if (_ap.preview == nullptr) return; + Point pt = GetTileBelowCursor(); + auto tile = pt.x == -1 ? INVALID_TILE : TileVirtXY(pt.x, pt.y); + _ap.preview->Update(pt, tile); + + auto tiles = _ap.preview->GetTiles(); + for (auto it = _ap.tiles.begin(); it != _ap.tiles.end();) { + MarkTileDirtyByTile(it->first); + it = (tiles.find(it->first) == tiles.end() ? _ap.tiles.erase(it) : std::next(it)); + } + for (auto &[t, l] : tiles) { + auto it = _ap.tiles.find(t); + if (it != _ap.tiles.end() && it->second == l) + continue; + _ap.tiles.insert_or_assign(it, t, l); + MarkTileDirtyByTile(t); + } +} + +bool _prev_left_button_down = false; +// const Window *_click_window = nullptr; +bool _keep_mouse_click = false; +// const Window *_keep_mouse_window; + +bool HandleMouseMove() { + bool changed = _left_button_down != _prev_left_button_down; + _prev_left_button_down = _left_button_down; + // Window *w = FindWindowFromPt(_cursor.pos.x, _cursor.pos.y); + bool released = !_left_button_down && changed && _keep_mouse_click; + if (!_left_button_down) _keep_mouse_click = false; + + if (_ap.preview == nullptr) return false; + // Viewport *vp = IsPtInWindowViewport(w, ); + + auto pt = GetTileBelowCursor(); + if (pt.x == -1) return false; + auto tile = pt.x == -1 ? INVALID_TILE : TileVirtXY(pt.x, pt.y); + _ap.preview->Update(pt, tile); + _ap.preview->HandleMouseMove(); + if (_left_button_down) { + if (changed && _ap.preview->HandleMousePress()) { + _keep_mouse_click = true; + } + if (_keep_mouse_click) return true; + } + if (released) { + _ap.preview->HandleMouseRelease(); + } + return false; +} + +bool HandleMouseClick(Viewport *vp, bool double_click) { + if (_ap.preview == nullptr) return false; + auto pt = GetTileBelowCursor(); + auto tile = pt.x == -1 ? INVALID_TILE : TileVirtXY(pt.x, pt.y); + _ap.preview->Update(pt, tile); + return _ap.preview->HandleMouseClick(vp, pt, tile, double_click); +} + +bool HandlePlacePushButton(Window *w, WidgetID widget, up preview) { + if (w->IsWidgetDisabled(widget)) return false; + + if (_settings_client.sound.click_beep) SndPlayFx(SND_15_BEEP); + w->SetDirty(); + + if (w->IsWidgetLowered(widget)) { + ResetObjectToPlace(); + return false; + } + + w->LowerWidget(widget); + + auto icon = preview->GetCursor(); + if ((icon & ANIMCURSOR_FLAG) != 0) { + SetAnimatedMouseCursor(_animcursors[icon & ~ANIMCURSOR_FLAG]); + } else { + SetMouseCursor(icon, PAL_NONE); + } + citymania::SetActivePreview(std::move(preview)); + _thd.window_class = w->window_class; + _thd.window_number = w->window_number; + + return true; + +} + + } // namespace citymania diff --git a/src/citymania/cm_highlight.hpp b/src/citymania/cm_highlight.hpp index 9c0b7faa..1cf709c8 100644 --- a/src/citymania/cm_highlight.hpp +++ b/src/citymania/cm_highlight.hpp @@ -1,6 +1,7 @@ #ifndef CITYMANIA_HIGHLIGHT_HPP #define CITYMANIA_HIGHLIGHT_HPP +#include "cm_commands.hpp" #include "cm_highlight_type.hpp" #include "../core/enum_type.hpp" @@ -56,6 +57,14 @@ PaletteID GetTreeShadePal(TileIndex tile); void RotateAutodetection(); void ResetRotateAutodetection(); +void ResetActivePreview(); +void SetActivePreview(up &&preview); +void UpdateActivePreview(); + +bool HandlePlacePushButton(Window *w, WidgetID widget, up preview); +bool HandleMouseMove(); +bool HandleMouseClick(Viewport *vp, bool double_click); + } // namespace citymania #endif diff --git a/src/citymania/cm_highlight_type.hpp b/src/citymania/cm_highlight_type.hpp index 0655bb92..65884e28 100644 --- a/src/citymania/cm_highlight_type.hpp +++ b/src/citymania/cm_highlight_type.hpp @@ -16,11 +16,18 @@ #include #include +#include +#include #include +#include "cm_command_type.hpp" + namespace citymania { +typedef std::function(TileIndex start_tile, TileIndex end_tile)> HighlightGenerator; + + enum ZoningBorder: uint8 { NONE = 0, TOP_LEFT = 1, @@ -81,7 +88,7 @@ class TileIndexWrapper { class ObjectTileHighlight { public: - enum class Type { + enum class Type : uint8_t { BEGIN = 0, RAIL_DEPOT = BEGIN, RAIL_TRACK, @@ -91,12 +98,17 @@ class ObjectTileHighlight { RAIL_TUNNEL_HEAD, ROAD_STOP, ROAD_DEPOT, + DOCK_SLOPE, + DOCK_FLAT, AIRPORT_TILE, INDUSTRY_TILE, POINT, RECT, NUMBERED_RECT, + BORDER, + TINT, + STRUCT_TINT, END, }; @@ -152,9 +164,17 @@ class ObjectTileHighlight { struct { uint32 number; } numbered_rect; + struct { + DiagDirection ddir; + } dock_slope; + struct { + Axis axis; + } dock_flat; + ZoningBorder border; } u; ObjectTileHighlight(Type type, SpriteID palette): type{type}, palette{palette} {} + static ObjectTileHighlight make_rail_depot(SpriteID palette, DiagDirection ddir); static ObjectTileHighlight make_rail_track(SpriteID palette, Track track); static ObjectTileHighlight make_rail_station(SpriteID palette, Axis axis, byte section); @@ -164,11 +184,22 @@ class ObjectTileHighlight { static ObjectTileHighlight make_road_stop(SpriteID palette, RoadType roadtype, DiagDirection ddir, bool is_truck, RoadStopClassID spec_class, uint16_t spec_index); static ObjectTileHighlight make_road_depot(SpriteID palette, RoadType roadtype, DiagDirection ddir); + + static ObjectTileHighlight make_dock_slope(SpriteID palette, DiagDirection ddir); + static ObjectTileHighlight make_dock_flat(SpriteID palette, Axis axis); + static ObjectTileHighlight make_airport_tile(SpriteID palette, StationGfx gfx); + static ObjectTileHighlight make_industry_tile(SpriteID palette, IndustryType ind_type, byte ind_layout, TileIndexDiff tile_diff, IndustryGfx gfx); static ObjectTileHighlight make_point(SpriteID palette); static ObjectTileHighlight make_rect(SpriteID palette); static ObjectTileHighlight make_numbered_rect(SpriteID palette, uint32 number); + static ObjectTileHighlight make_border(SpriteID palette, ZoningBorder border); + static ObjectTileHighlight make_tint(SpriteID palette); + static ObjectTileHighlight make_struct_tint(SpriteID palette); + + bool operator==(const ObjectTileHighlight &oh) const; + bool SetTileHighlight(TileHighlight &th, const TileInfo *ti) const; }; @@ -282,7 +313,7 @@ class Blueprint { class ObjectHighlight { public: - enum class Type { + enum class Type : byte { NONE = 0, RAIL_DEPOT = 1, RAIL_STATION = 2, @@ -351,6 +382,33 @@ class ObjectHighlight { void MarkDirty(); }; +class Preview { +public: + typedef std::map> TileMap; + virtual ~Preview() {} + virtual void Update(Point pt, TileIndex tile) = 0; + virtual void HandleMouseMove() {}; + virtual bool HandleMousePress() { return false; }; + virtual void HandleMouseRelease() {}; + virtual bool HandleMouseClick(Viewport* /* vp */, Point /* pt */, TileIndex /* tile */, bool /* double_click */) { return false; }; + virtual TileMap GetTiles() = 0; + virtual CursorID GetCursor() = 0; + virtual void OnStationRemoved(const Station* /* station */) {}; +}; + +// enum class ActiveHighlightState { +// None, +// Place, +// DragStart, +// DragStop, +// }; + +struct ActivePreview { + up preview; + Preview::TileMap tiles; +}; + +extern ActivePreview _ap; } // namespace citymania diff --git a/src/citymania/cm_hotkeys.cpp b/src/citymania/cm_hotkeys.cpp index ffdd5c9a..562ac72c 100644 --- a/src/citymania/cm_hotkeys.cpp +++ b/src/citymania/cm_hotkeys.cpp @@ -2,6 +2,7 @@ #include "cm_hotkeys.hpp" #include "cm_settings.hpp" +#include "cm_station_gui.hpp" #include "../newgrf_station.h" #include "../settings_type.h" @@ -19,15 +20,7 @@ #include "../safeguards.h" -struct RailStationGUISettings { - Axis orientation; ///< Currently selected rail station orientation - - bool newstations; ///< Are custom station definitions available? - StationClassID station_class; ///< Currently selected custom station class (if newstations is \c true ) - byte station_type; ///< %Station type within the currently selected custom station class (if newstations is \c true ) - byte station_count; ///< Number of custom stations (if newstations is \c true ) -}; -extern RailStationGUISettings _railstation; ///< Settings of the station builder GUI +extern citymania::RailStationGUISettings _railstation; ///< Settings of the station builder GUI namespace citymania { diff --git a/src/citymania/cm_station_gui.cpp b/src/citymania/cm_station_gui.cpp index a2aff580..91f0accc 100644 --- a/src/citymania/cm_station_gui.cpp +++ b/src/citymania/cm_station_gui.cpp @@ -2,6 +2,8 @@ #include "cm_station_gui.hpp" +#include "cm_highlight.hpp" +#include "cm_highlight_type.hpp" #include "cm_hotkeys.hpp" #include "cm_commands.hpp" @@ -28,8 +30,13 @@ #include "../window_gui.h" #include "../zoom_type.h" #include "../zoom_func.h" +#include "cm_type.hpp" +#include "generated/cm_gen_commands.hpp" #include +#include + +bool _remove_button_clicked; // replace vanilla static vars extern const Station *_viewport_highlight_station; extern TileHighlightData _thd; @@ -42,16 +49,9 @@ extern AirportClassID _selected_airport_class; extern int _selected_airport_index; extern byte _selected_airport_layout; extern RailType _cur_railtype; // rail_gui.cpp - -struct RailStationGUISettings { - Axis orientation; ///< Currently selected rail station orientation - - bool newstations; ///< Are custom station definitions available? - StationClassID station_class; ///< Currently selected custom station class (if newstations is \c true ) - byte station_type; ///< %Station type within the currently selected custom station class (if newstations is \c true ) - byte station_count; ///< Number of custom stations (if newstations is \c true ) -}; -extern RailStationGUISettings _railstation; //rail_gui.cpp +extern RoadType _cur_roadtype; // road_gui.cpp +extern void GetStationLayout(byte *layout, uint numtracks, uint plat_len, const StationSpec *statspec); +extern citymania::RailStationGUISettings _railstation; ///< Settings of the station builder GUI struct RoadStopGUISettings { DiagDirection orientation; @@ -65,18 +65,11 @@ extern RoadStopGUISettings _roadstop_gui_settings; namespace citymania { StationBuildingStatus _station_building_status = StationBuildingStatus::NEW; -const Station *_station_to_join = nullptr; const Station *_highlight_station_to_join = nullptr; TileArea _highlight_join_area; -// void SetStationTileSelectSize(int w, int h, int catchment) { -// _station_select.w = w; -// _station_select.h = h; -// _station_select.catchment = catchment; -// } - bool UseImprovedStationJoin() { - return _settings_client.gui.cm_use_improved_station_join && _settings_game.station.distant_join_stations; + return _settings_client.gui.cm_use_improved_station_join && _settings_game.station.distant_join_stations && _settings_game.station.adjacent_stations; } void SetStationBiildingStatus(StationBuildingStatus status) { @@ -109,19 +102,19 @@ static void UpdateHiglightJoinArea(const Station *station) { _highlight_join_area.tile = INVALID_TILE; return; } - auto &r = _station_to_join->rect; - auto d = (int)_settings_game.station.station_spread - 1; - TileArea ta( - TileXY(std::max(r.right - d, 0), - std::max(r.bottom - d, 0)), - TileXY(std::min(r.left + d, Map::SizeX() - 1), - std::min(r.top + d, Map::SizeY() - 1)) - ); - if (_highlight_join_area.tile == ta.tile && - _highlight_join_area.w == ta.w && - _highlight_join_area.h == ta.h) return; + // auto &r = _station_to_join->rect; + // auto d = (int)_settings_game.station.station_spread - 1; + // TileArea ta( + // TileXY(std::max(r.right - d, 0), + // std::max(r.bottom - d, 0)), + // TileXY(std::min(r.left + d, Map::SizeX() - 1), + // std::min(r.top + d, Map::SizeY() - 1)) + // ); + // if (_highlight_join_area.tile == ta.tile && + // _highlight_join_area.w == ta.w && + // _highlight_join_area.h == ta.h) return; MarkTileAreaDirty(_highlight_join_area); - _highlight_join_area = ta; + // _highlight_join_area = ta; MarkTileAreaDirty(_highlight_join_area); } @@ -147,8 +140,8 @@ void SetHighlightStationToJoin(const Station *station, bool with_area) { void OnStationTileSetChange(const Station *station, bool /* adding */, StationType /* type */) { if (station == _highlight_station_to_join) { - if (_highlight_join_area.tile != INVALID_TILE) - UpdateHiglightJoinArea(_station_to_join); + // if (_highlight_join_area.tile != INVALID_TILE) + // UpdateHiglightJoinArea(_station_to_join); if (_settings_client.gui.station_show_coverage) MarkCoverageAreaDirty(_highlight_station_to_join); } @@ -168,15 +161,6 @@ void OnStationPartBuilt(const Station *station) { CheckRedrawStationCoverage(); } -void OnStationRemoved(const Station *station) { - if (_last_built_station == station) _last_built_station = nullptr; - if (_station_to_join == station) { - // TODO MarkJoinHighlight - MarkCoverageAreaDirty(station); - _station_to_join = nullptr; - } -} - const Station *CheckClickOnDeadStationSign() { int x = _cursor.pos.x; @@ -231,7 +215,7 @@ void JoinAndBuild(Tcommand command, Tcallback *callback) { command.with_callback([] (bool res)->bool { if (!res) return false; - _station_to_join = _last_built_station; + // _station_to_join = _last_built_station; return true; }).post(callback); } @@ -318,7 +302,7 @@ DiagDirection AutodetectRoadObjectDirection(TileIndex tile, Point pt, RoadType r if (HasExactlyOneBit(bits)) { return RoadBitsToDiagDir(bits); } - if (bits == ROAD_NONE){ + if (bits == ROAD_NONE) { bits = ROAD_ALL; } RoadBits frac_bits = DiagDirToRoadBits(TileFractCoordsToDiagDir(pt)); @@ -462,35 +446,6 @@ void PlaceAirport(TileIndex tile) { JoinAndBuild(c, CcBuildAirport); } -static void FindStationsAroundSelection(const TileArea &location) -{ - /* Extended area by one tile */ - int x = TileX(location.tile); - int y = TileY(location.tile); - - TileArea ta(TileXY(std::max(0, x - 1), std::max(0, y - 1)), TileXY(std::min(Map::MaxX() - 1, x + location.w + 1), std::min(Map::MaxY() - 1, y + location.h + 1))); - - Station *adjacent = nullptr; - - /* Direct loop instead of FindStationsAroundTiles as we are not interested in catchment area */ - for (auto tile : ta) { - if (IsTileType(tile, MP_STATION) && GetTileOwner(tile) == _local_company) { - Station *st = Station::GetByTile(tile); - if (st == nullptr) continue; - - int tx = TileX(tile); - int ty = TileY(tile); - bool is_corner = ((tx == x - 1 || tx == x + location.w + 1) && (ty == y - 1 || ty == y + location.h + 1)); - - if (adjacent && is_corner) continue; - adjacent = st; - if (!is_corner) break; - } - } - SetHighlightStationToJoin(adjacent, false); - _station_building_status = (adjacent == nullptr ? StationBuildingStatus::NEW : StationBuildingStatus::JOIN); -} - bool CheckRedrawStationCoverage() { // static bool last_ctrl_pressed = false; static TileArea last_location; @@ -512,30 +467,30 @@ bool CheckRedrawStationCoverage() { if (IsTileType(location.tile, MP_STATION) && GetTileOwner(location.tile) == _local_company) st = Station::GetByTile(location.tile); - SetHighlightStationToJoin(st, _station_to_join && st == _station_to_join); + // SetHighlightStationToJoin(st, _station_to_join && st == _station_to_join); _station_building_status = (st == nullptr ? StationBuildingStatus::NEW : StationBuildingStatus::JOIN); } else { - if (_station_to_join) { - SetHighlightStationToJoin(_station_to_join, true); - _station_building_status = StationBuildingStatus::JOIN; - } else { - FindStationsAroundSelection(location); - } + // if (_station_to_join) { + // SetHighlightStationToJoin(_station_to_join, true); + // _station_building_status = StationBuildingStatus::JOIN; + // } else { + // FindStationsAroundSelection(location); + // } } return true; } -void SelectStationToJoin(const Station *station) { - if (_station_to_join == station) - _station_to_join = nullptr; - else - _station_to_join = station; +void SelectStationToJoin(const Station *) { + // if (_station_to_join == station) + // _station_to_join = nullptr; + // else + // _station_to_join = station; CheckRedrawStationCoverage(); } void AbortStationPlacement() { - _station_to_join = nullptr; + // _station_to_join = nullptr; SetHighlightStationToJoin(nullptr, false); } @@ -678,4 +633,637 @@ std::string GetStationCoverageProductionText(TileIndex tile, int w, int h, int r return s.str(); } +/* enable/disable catchment area with ctrl+click on a station */ +void ShowCatchmentByClick(StationID station) +{ + if (_viewport_highlight_station != nullptr) { + if (_viewport_highlight_station == Station::Get(station)) + SetViewportCatchmentStation(Station::Get(station), false); + else SetViewportCatchmentStation(Station::Get(station), true); + } + else SetViewportCatchmentStation(Station::Get(station), true); +} + +// ---- NEw code + +StationID _station_to_join = INVALID_STATION; +std::chrono::time_point _station_to_join_selected; + +void OnStationRemoved(const Station *station) { + if (_last_built_station == station) _last_built_station = nullptr; + if (_station_to_join == station->index) { + _station_to_join = INVALID_STATION; + } + if (_ap.preview != nullptr) _ap.preview->OnStationRemoved(station); +} + +static void AddAreaRectTiles(Preview::TileMap &tiles, TileArea area, SpriteID palette) { + if (area.w == 0 || area.h == 0) return; + + if (area.w == 1 && area.h == 1) { + tiles[area.tile].push_back(ObjectTileHighlight::make_border(palette, ZoningBorder::FULL)); + return; + } + auto sx = TileX(area.tile), sy = TileY(area.tile); + auto ex = sx + area.w - 1, ey = sy + area.h - 1; + // NOTE: Doesn't handle one-tile width/height separately but relies on border overlapping + tiles[TileXY(sx, sy)].push_back(ObjectTileHighlight::make_border(palette, ZoningBorder::TOP_LEFT | ZoningBorder::TOP_RIGHT)); + for (auto x = sx + 1; x < ex; x++) + tiles[TileXY(x, sy)].push_back(ObjectTileHighlight::make_border(palette, ZoningBorder::TOP_LEFT)); + tiles[TileXY(ex, sy)].push_back(ObjectTileHighlight::make_border(palette, ZoningBorder::TOP_LEFT | ZoningBorder::BOTTOM_LEFT)); + for (auto y = sy + 1; y < ey; y++) { + tiles[TileXY(sx, y)].push_back(ObjectTileHighlight::make_border(palette, ZoningBorder::TOP_RIGHT)); + for (auto x = sx + 1; x < ex; x++) { + tiles[TileXY(x, y)].push_back(ObjectTileHighlight::make_border(palette, ZoningBorder::NONE)); + } + tiles[TileXY(ex, y)].push_back(ObjectTileHighlight::make_border(palette, ZoningBorder::BOTTOM_LEFT)); + } + tiles[TileXY(sx, ey)].push_back(ObjectTileHighlight::make_border(palette, ZoningBorder::TOP_RIGHT | ZoningBorder::BOTTOM_RIGHT)); + for (auto x = sx + 1; x < ex; x++) + tiles[TileXY(x, ey)].push_back(ObjectTileHighlight::make_border(palette, ZoningBorder::BOTTOM_RIGHT)); + tiles[TileXY(ex, ey)].push_back(ObjectTileHighlight::make_border(palette, ZoningBorder::BOTTOM_LEFT | ZoningBorder::BOTTOM_RIGHT)); +} + +// copied from cm_blueprint.cpp +template +void IterateStation(TileIndex start_tile, Axis axis, byte numtracks, byte plat_len, Func visitor) { + auto plat_delta = (axis == AXIS_X ? TileDiffXY(1, 0) : TileDiffXY(0, 1)); + auto track_delta = (axis == AXIS_Y ? TileDiffXY(1, 0) : TileDiffXY(0, 1)); + TileIndex tile_track = start_tile; + do { + TileIndex tile = tile_track; + int w = plat_len; + do { + visitor(tile); + tile += plat_delta; + } while (--w); + tile_track += track_delta; + } while (--numtracks); +} + +void AddJoinAreaTiles(Preview::TileMap &tiles, StationID station_id) { + auto station = Station::GetIfValid(station_id); + if (station == nullptr) return; + + auto &r = station->rect; + auto d = (int)_settings_game.station.station_spread - 1; + TileArea ta( + TileXY(std::max(r.right - d, 0), + std::max(r.bottom - d, 0)), + TileXY(std::min(r.left + d, Map::SizeX() - 1), + std::min(r.top + d, Map::SizeY() - 1)) + ); + + AddAreaRectTiles(tiles, ta, CM_PALETTE_TINT_CYAN); +} + +bool RailStationPreview::IsDragDrop() const { + return _settings_client.gui.station_dragdrop; +} + +CursorID RailStationPreview::GetCursor() const { + return SPR_CURSOR_RAIL_STATION; +} + +TileArea RailStationPreview::GetArea(bool remove_mode) const { + if (this->IsDragDrop() || remove_mode) return {this->GetStartTile(), this->cur_tile}; + + if (_railstation.orientation == AXIS_X) return {this->cur_tile, _settings_client.gui.station_platlength, _settings_client.gui.station_numtracks}; + return {this->cur_tile, _settings_client.gui.station_numtracks, _settings_client.gui.station_platlength}; +} + +up RailStationPreview::GetCommand(bool adjacent, StationID join_to) const { + auto ta = this->GetArea(false); + auto start_tile = ta.tile; + auto numtracks = ta.w; + auto platlength = ta.h; + if (_railstation.orientation == AXIS_X) Swap(numtracks, platlength); + + auto res = make_up( + start_tile, + _cur_railtype, + _railstation.orientation, + numtracks, + platlength, + _railstation.station_class, + _railstation.station_type, + join_to, + adjacent + ); + res->with_error(STR_ERROR_CAN_T_BUILD_RAILROAD_STATION); + return res; +} + +up RailStationPreview::GetRemoveCommand() const { + auto res = make_up( + this->GetStartTile(), + this->cur_tile, + !citymania::_fn_mod + ); + res->with_error(STR_ERROR_CAN_T_REMOVE_PART_OF_STATION); + return res; +} + +bool RailStationPreview::Execute(up cmd, bool remove_mode) const { + if (remove_mode) return cmd->post(&CcPlaySound_CONSTRUCTION_RAIL); + else return cmd->post(&CcStation); +} + +void RailStationPreview::AddPreviewTiles(Preview::TileMap &tiles, SpriteID palette) const { + auto cmd = this->GetCommand(true, NEW_STATION); + auto cmdt = dynamic_cast(cmd.get()); + if (cmdt == nullptr) return; + + if (palette == PAL_NONE) palette = cmd->test().Succeeded() ? CM_PALETTE_TINT_WHITE : CM_PALETTE_TINT_RED_DEEP; + + std::vector layouts(cmdt->numtracks * cmdt->plat_len); + byte *layout_ptr = layouts.data(); + GetStationLayout(layout_ptr, cmdt->numtracks, cmdt->plat_len, nullptr); + IterateStation(cmdt->tile_org, cmdt->axis, cmdt->numtracks, cmdt->plat_len, + [&](TileIndex t) { + byte layout = *layout_ptr++; + tiles[t].push_back(ObjectTileHighlight::make_rail_station(palette, cmdt->axis, layout & ~1)); + } + ); +} + +OverlayParams RailStationPreview::GetOverlayParams() const { + return {this->GetArea(false), CA_TRAIN, SCT_ALL}; +} + +bool RoadStationPreview::IsDragDrop() const { + return true; +} + +CursorID RoadStationPreview::GetCursor() const { + return SPR_CURSOR_BUS_STATION; + // return SPR_CURSOR_TRUCK_STATION; +} + +TileArea RoadStationPreview::GetArea(bool /* remove_mode */) const { + return {this->GetStartTile(), this->cur_tile}; +} + +extern DiagDirection AddAutodetectionRotation(DiagDirection ddir); // cm_highlight.cpp + +void RoadStationPreview::Update(Point pt, TileIndex tile) { + if (pt.x == -1) return; + + auto ddir = _roadstop_gui_settings.orientation; + auto area = this->GetArea(false); + if (ddir >= DIAGDIR_END && ddir < STATIONDIR_AUTO) { + // When placed on road autorotate anyway + if (ddir == STATIONDIR_X) { + if (!CheckDriveThroughRoadStopDirection(area, ROAD_X)) + ddir = STATIONDIR_Y; + } else { + if (!CheckDriveThroughRoadStopDirection(area, ROAD_Y)) + ddir = STATIONDIR_X; + } + } else if (ddir == STATIONDIR_AUTO) { + ddir = AddAutodetectionRotation(AutodetectRoadObjectDirection(tile, pt, _cur_roadtype)); + } else if (ddir == STATIONDIR_AUTO_XY) { + ddir = AddAutodetectionRotation(AutodetectDriveThroughRoadStopDirection(area, pt, _cur_roadtype)); + } + this->ddir = ddir; +} + +up RoadStationPreview::GetCommand(bool adjacent, StationID join_to) const { + auto area = this->GetArea(false); + DiagDirection ddir = this->ddir; + bool drive_through = this->ddir >= DIAGDIR_END; + if (drive_through) ddir = static_cast(this->ddir - DIAGDIR_END); // Adjust picker result to actual direction. + RoadStopClassID spec_class = _roadstop_gui_settings.roadstop_class; + uint16_t spec_index = _roadstop_gui_settings.roadstop_type; + + auto res = make_up( + area.tile, + area.w, + area.h, + this->stop_type, + drive_through, + ddir, + _cur_roadtype, + spec_class, + spec_index, + join_to, + adjacent + ); + + return res; +} + +up RoadStationPreview::GetRemoveCommand() const { + auto area = this->GetArea(false); + auto res = make_up( + area.tile, + area.w, + area.h, + this->stop_type, + citymania::_fn_mod + ); + auto rti = GetRoadTypeInfo(_cur_roadtype); + res->with_error(rti->strings.err_remove_station[this->stop_type]); + return res; +} + +bool RoadStationPreview::Execute(up cmd, bool remove_mode) const { + if (remove_mode) return cmd->post(&CcPlaySound_CONSTRUCTION_OTHER); + else return cmd->post(&CcRoadStop); +} + +void RoadStationPreview::AddPreviewTiles(Preview::TileMap &tiles, SpriteID palette) const { + auto cmd = this->GetCommand(true, NEW_STATION); + auto cmdt = dynamic_cast(cmd.get()); + if (cmdt == nullptr) return; + + if (palette == PAL_NONE) palette = cmd->test().Succeeded() ? CM_PALETTE_TINT_WHITE : CM_PALETTE_TINT_RED_DEEP; + + for (TileIndex t : this->GetArea(false)) { + auto ddir = cmdt->ddir; + if (cmdt->is_drive_through) ddir = ddir + DIAGDIR_END; + tiles[t].push_back(ObjectTileHighlight::make_road_stop( + palette, + cmdt->rt, + ddir, + cmdt->stop_type == ROADSTOP_TRUCK, + cmdt->spec_class, + cmdt->spec_index + )); + } +} + +OverlayParams RoadStationPreview::GetOverlayParams() const { + return { + this->GetArea(false), + this->stop_type == ROADSTOP_TRUCK ? CA_TRUCK : CA_BUS, + this->stop_type == ROADSTOP_TRUCK ? SCT_NON_PASSENGERS_ONLY : SCT_PASSENGERS_ONLY + }; +} + +bool DockPreview::IsDragDrop() const { + return false; +} + +CursorID DockPreview::GetCursor() const { + return SPR_CURSOR_DOCK; +} + +TileArea DockPreview::GetArea(bool /* remove_mode */) const { + auto tile = this->GetStartTile(); + TileIndex tile_to = (this->ddir != INVALID_DIAGDIR ? TileAddByDiagDir(tile, ReverseDiagDir(this->ddir)) : tile); + return {tile, tile_to}; +} + +void DockPreview::Update(Point pt, TileIndex tile) { + if (pt.x == -1) return; + this->ddir = GetInclinedSlopeDirection(GetTileSlope(tile)); + if (this->ddir == INVALID_DIAGDIR) this->ddir = DIAGDIR_NE; + else this->ddir = ReverseDiagDir(this->ddir); +} + +up DockPreview::GetCommand(bool adjacent, StationID join_to) const { + // STR_ERROR_CAN_T_BUILD_DOCK_HERE + return make_up( + this->GetStartTile(), + join_to, + adjacent + ); +} + +up DockPreview::GetRemoveCommand() const { + return nullptr; +} + +bool DockPreview::Execute(up cmd, bool remove_mode) const { + return cmd->post(&CcBuildDocks); +} + +void DockPreview::AddPreviewTiles(Preview::TileMap &tiles, SpriteID palette) const { + auto t = this->GetStartTile(); + tiles[t].push_back(ObjectTileHighlight::make_dock_slope(CM_PALETTE_TINT_WHITE, this->ddir)); + t += TileOffsByDiagDir(this->ddir); + tiles[t].push_back(ObjectTileHighlight::make_dock_flat(CM_PALETTE_TINT_WHITE, DiagDirToAxis(this->ddir))); + // TODO + // auto cmd = this->GetCommand(true, NEW_STATION); + // auto cmdt = dynamic_cast(cmd.get()); + // if (cmdt == nullptr) return; + + // if (palette == PAL_NONE) palette = cmd->test().Succeeded() ? CM_PALETTE_TINT_WHITE : CM_PALETTE_TINT_RED_DEEP; + + // for (TileIndex t : this->GetArea(false)) { + // auto ddir = cmdt->ddir; + // if (cmdt->is_drive_through) ddir = ddir + DIAGDIR_END; + // tiles[t].push_back(ObjectTileHighlight::make_road_stop( + // palette, + // cmdt->rt, + // ddir, + // cmdt->stop_type == ROADSTOP_TRUCK, + // cmdt->spec_class, + // cmdt->spec_index + // )); + // } +} + +OverlayParams DockPreview::GetOverlayParams() const { + return { + this->GetArea(false), + CA_DOCK, + SCT_ALL + }; +} + + +void StationPreviewBase::AddAreaTiles(Preview::TileMap &tiles, bool add_current, bool show_join_area) { + Station *st_join = Station::GetIfValid(this->station_to_join); + std::set join_area; + + if (show_join_area && st_join != nullptr) { + AddJoinAreaTiles(tiles, st_join->index); + for (auto t : tiles) join_area.insert(t.first); + } + + if (this->show_coverage && st_join != nullptr) { + // Add joining station coverage + for (auto t : st_join->catchment_tiles) { + auto pal = join_area.find(t) != join_area.end() ? CM_PALETTE_TINT_CYAN_WHITE : CM_PALETTE_TINT_WHITE; + tiles[t].push_back(ObjectTileHighlight::make_tint(pal)); + } + } + + if (this->show_coverage && add_current) { + // Add current station coverage + auto rad = CA_UNMODIFIED; + if (_settings_game.station.modified_catchment) rad = CA_TRAIN; + auto area = this->type->GetArea(false); + area.Expand(rad); + area.ClampToMap(); + for (auto t : area) { + auto pal = join_area.find(t) != join_area.end() ? CM_PALETTE_TINT_CYAN_WHITE : CM_PALETTE_TINT_WHITE; + tiles[t].push_back(ObjectTileHighlight::make_tint(pal)); + } + } + + if (st_join != nullptr) { + TileArea ta(TileXY(st_join->rect.left, st_join->rect.top), TileXY(st_join->rect.right, st_join->rect.bottom)); + for (TileIndex t : ta) { + if (!IsTileType(t, MP_STATION) || GetStationIndex(t) != st_join->index) continue; + tiles[t].push_back(ObjectTileHighlight::make_struct_tint(CM_PALETTE_TINT_BLUE)); + } + } +} + +void StationPreviewBase::Update(Point pt, TileIndex tile) { + if (tile != INVALID_TILE) this->type->cur_tile = tile; + this->show_coverage = _settings_client.gui.station_show_coverage; + this->adjacent_stations = _settings_game.station.adjacent_stations; + this->remove_mode = false; + if (_remove_button_clicked) { + this->remove_mode = true; + this->keep_rail = !_fn_mod; + } else if (!this->type->IsDragDrop()) { + this->type->start_tile = INVALID_TILE; + } + this->type->Update(pt, tile); +} + +bool StationPreviewBase::HandleMousePress() { + if (!IsValidTile(this->type->cur_tile)) return false; + + if (this->remove_mode || this->type->IsDragDrop()) { + this->type->start_tile = this->type->cur_tile; + return true; + } + + this->Execute(); + return true; +} + +void StationPreviewBase::HandleMouseRelease() { + if (!IsValidTile(this->type->cur_tile)) return; + + if (this->type->start_tile != INVALID_TILE) { + this->Execute(); + this->type->start_tile = INVALID_TILE; + } +} + +std::vector> StationPreviewBase::GetOverlayData() { + if (this->remove_mode) return {}; + + std::vector> res; + auto params = this->type->GetOverlayParams(); + + if (!_settings_game.station.modified_catchment) params.radius = CA_UNMODIFIED; + auto production = citymania::GetProductionAroundTiles(params.area.tile, params.area.w, params.area.h, params.radius); + bool has_header = false; + for (CargoID i = 0; i < NUM_CARGO; i++) { + if (production[i] == 0) continue; + + switch (params.coverage_type) { + case SCT_PASSENGERS_ONLY: if (!IsCargoInClass(i, CC_PASSENGERS)) continue; break; + case SCT_NON_PASSENGERS_ONLY: if (IsCargoInClass(i, CC_PASSENGERS)) continue; break; + case SCT_ALL: break; + default: NOT_REACHED(); + } + + const CargoSpec *cs = CargoSpec::Get(i); + if (cs == nullptr) continue; + + if (!has_header) { + res.emplace_back(PAL_NONE, GetString(CM_STR_BUILD_INFO_OVERLAY_STATION_SUPPLIES)); + has_header = true; + } + SetDParam(0, i); + SetDParam(1, production[i] >> 8); + res.emplace_back(cs->GetCargoIcon(), GetString(CM_STR_BUILD_INFO_OVERLAY_STATION_CARGO)); + } + return res; +} + +up StationPreviewBase::GetCommand(bool adjacent, StationID join_to) { + if (this->remove_mode) return this->type->GetRemoveCommand(); + return this->type->GetCommand(adjacent, join_to); +} + +Preview::TileMap VanillaStationPreview::GetTiles() { + Preview::TileMap tiles; + + if (!IsValidTile(this->type->cur_tile)) return tiles; + + if (this->remove_mode) { + AddAreaRectTiles(tiles, this->type->GetArea(true), CM_PALETTE_TINT_RED_DEEP); + return tiles; + } + + this->AddAreaTiles(tiles, true, false); + this->type->AddPreviewTiles(tiles, this->palette); + + return tiles; +} + +void VanillaStationPreview::Update(Point pt, TileIndex tile) { + StationPreviewBase::Update(pt, tile); + this->palette = CM_PALETTE_TINT_WHITE; + + if (this->remove_mode) return; + if (this->selected_station_to_join != INVALID_STATION) { + this->station_to_join = this->selected_station_to_join; + return; + } + + if (!IsValidTile(this->type->cur_tile)) return; + this->station_to_join = INVALID_STATION; + auto area = this->type->GetArea(false); + area.Expand(1); + area.ClampToMap(); + for (auto tile : area) { + if (IsTileType(tile, MP_STATION) && GetTileOwner(tile) == _local_company) { + Station *st = Station::GetByTile(tile); + if (st == nullptr || st->index == this->station_to_join) continue; + if (this->station_to_join != INVALID_STATION) { + this->station_to_join = INVALID_STATION; + this->palette = CM_PALETTE_TINT_YELLOW; + break; + } + this->station_to_join = st->index; + // TODO check for command to return multiple? but also check each to + // see if they can be built + // if (this->GetCommand(true, st->index)->test().Succeeded()) { + // if (this->station_to_join != INVALID_STATION) { + // this->station_to_join = INVALID_STATION; + // this->palette = CM_PALETTE_TINT_YELLOW; + // break; + // } else this->station_to_join = st->index; + // } + } + } + if (this->station_to_join == INVALID_STATION && !this->GetCommand(true, NEW_STATION)->test().Succeeded()) + this->palette = CM_PALETTE_TINT_RED_DEEP; +} + +void VanillaStationPreview::Execute() { + if (this->remove_mode) { + this->type->Execute(this->type->GetRemoveCommand(), true); + return; + } + auto proc = [type=this->type](bool test, StationID to_join) -> bool { + auto cmd = type->GetCommand(_fn_mod, to_join); + if (test) return cmd->test().Succeeded(); + return type->Execute(std::move(cmd), false); + }; + ShowSelectStationIfNeeded(this->type->GetArea(false), proc); +} + +void VanillaStationPreview::OnStationRemoved(const Station *station) { + if (this->station_to_join == station->index) this->station_to_join = INVALID_STATION; + if (this->selected_station_to_join == station->index) this->station_to_join = INVALID_STATION; +} + +StationPreview::StationPreview(sp type) + :StationPreviewBase{type} +{ + auto seconds_since_selected = std::chrono::duration_cast(std::chrono::system_clock::now() - _station_to_join_selected).count(); + if (seconds_since_selected < 30) this->station_to_join = _station_to_join; + else this->station_to_join = INVALID_STATION; +} + +StationPreview::~StationPreview() { + _station_to_join_selected = std::chrono::system_clock::now(); +} + +up StationPreview::GetCommand() { + if (this->select_mode) return nullptr; + + auto res = StationPreviewBase::GetCommand(true, this->station_to_join); + if (this->remove_mode) return res; + + res->with_callback([] (bool res) -> bool { + if (!res) return false; + if (_last_built_station == nullptr) return false; + _station_to_join = _last_built_station->index; + _station_to_join_selected = std::chrono::system_clock::now(); + auto p = dynamic_cast(_ap.preview.get()); + if (p == nullptr) return false; + p->station_to_join = _last_built_station->index; + return true; + }); + return res; +} + +Preview::TileMap StationPreview::GetTiles() { + Preview::TileMap tiles; + + if (!IsValidTile(this->type->cur_tile)) return tiles; + + if (this->remove_mode) { + AddAreaRectTiles(tiles, this->type->GetArea(true), CM_PALETTE_TINT_RED_DEEP); + return tiles; + } + + this->AddAreaTiles(tiles, !this->select_mode, true); + + if (this->select_mode) { + tiles[this->type->cur_tile].push_back(ObjectTileHighlight::make_border(CM_PALETTE_TINT_BLUE, ZoningBorder::FULL)); + return tiles; + } + + this->type->AddPreviewTiles(tiles, PAL_NONE); + + return tiles; +} + +void StationPreview::Update(Point pt, TileIndex tile) { + this->select_mode = false; + StationPreviewBase::Update(pt, tile); + if (!this->remove_mode && _fn_mod) { + this->select_mode = true; + this->type->start_tile = INVALID_TILE; + } +} + +bool StationPreview::HandleMousePress() { + if (!IsValidTile(this->type->cur_tile)) return false; + + if (this->select_mode) { + if (IsTileType(this->type->cur_tile, MP_STATION)) { + auto st = Station::GetByTile(this->type->cur_tile); + this->station_to_join = st->index; + _station_to_join = this->station_to_join; + _station_to_join_selected = std::chrono::system_clock::now(); + } else { + this->station_to_join = INVALID_STATION; + _station_to_join = INVALID_STATION; + } + return true; + } + + return StationPreviewBase::HandleMousePress(); +} + +void StationPreview::Execute() { + this->type->Execute(std::move(this->GetCommand()), this->remove_mode); +} + +void StationPreview::OnStationRemoved(const Station *station) { + if (this->station_to_join == station->index) this->station_to_join = INVALID_STATION; +} + +void SetSelectedStationToJoin(StationID station_id) { + auto p = dynamic_cast(_ap.preview.get()); + if (p == nullptr) return; + p->selected_station_to_join = station_id; + UpdateActivePreview(); +} + +bool HandleStationPlacePushButton(Window *w, WidgetID widget, sp type) { + up preview; + if (citymania::UseImprovedStationJoin()) { + preview = make_up(type); + } else { + preview = make_up(type); + } + return citymania::HandlePlacePushButton(w, widget, std::move(preview)); +} + } // namespace citymania diff --git a/src/citymania/cm_station_gui.hpp b/src/citymania/cm_station_gui.hpp index b1739e55..2443905e 100644 --- a/src/citymania/cm_station_gui.hpp +++ b/src/citymania/cm_station_gui.hpp @@ -1,6 +1,8 @@ #ifndef CM_STATION_GUI_HPP #define CM_STATION_GUI_HPP +#include "cm_highlight_type.hpp" + #include "../core/geometry_type.hpp" #include "../command_type.h" #include "../road_type.h" @@ -15,6 +17,15 @@ const DiagDirection STATIONDIR_Y = (DiagDirection)((uint)DIAGDIR_END + 1); const DiagDirection STATIONDIR_AUTO = (DiagDirection)((uint)DIAGDIR_END + 2); const DiagDirection STATIONDIR_AUTO_XY = (DiagDirection)((uint)DIAGDIR_END + 3); +struct RailStationGUISettings { + Axis orientation; ///< Currently selected rail station orientation + + bool newstations; ///< Are custom station definitions available? + StationClassID station_class; ///< Currently selected custom station class (if newstations is \c true ) + uint16_t station_type; ///< %Station type within the currently selected custom station class (if newstations is \c true ) + uint16_t station_count; ///< Number of custom stations (if newstations is \c true ) +}; + enum class StationBuildingStatus { IMPOSSIBLE = 0, QUERY = 1, @@ -39,6 +50,7 @@ void SelectStationToJoin(const Station *station); void MarkCoverageHighlightDirty(); bool CheckRedrawStationCoverage(); void AbortStationPlacement(); +void ShowCatchmentByClick(StationID station); std::string GetStationCoverageProductionText(TileIndex tile, int w, int h, int rad, StationCoverageType sct); @@ -46,6 +58,152 @@ bool CheckDriveThroughRoadStopDirection(TileArea area, RoadBits r); DiagDirection AutodetectRoadObjectDirection(TileIndex tile, Point pt, RoadType roadtype); DiagDirection AutodetectDriveThroughRoadStopDirection(TileArea area, Point pt, RoadType roadtype); DiagDirection AutodetectRailObjectDirection(TileIndex tile, Point pt); +void SetSelectedStationToJoin(StationID station_id); + + +struct OverlayParams { + TileArea area; + CatchmentArea radius; + StationCoverageType coverage_type; +}; + + +class PreviewStationType { +public: + TileIndex start_tile = INVALID_TILE; + TileIndex cur_tile = INVALID_TILE; + + virtual ~PreviewStationType() {}; + + TileIndex GetStartTile() const { return start_tile == INVALID_TILE ? cur_tile : start_tile; } + virtual bool IsDragDrop() const { return true; }; + virtual CursorID GetCursor() const =0; + virtual TileArea GetArea(bool remove_mode) const =0; + virtual void Update(Point /* pt */, TileIndex /* tile */) {}; + virtual up GetCommand(bool adjacent, StationID join_to) const =0; + virtual up GetRemoveCommand() const =0; + virtual void AddPreviewTiles(Preview::TileMap &tiles, SpriteID palette) const =0; + virtual bool Execute(up cmd, bool remove_mode) const =0; + virtual OverlayParams GetOverlayParams() const =0; +}; + +class RailStationPreview : public PreviewStationType { +public: + virtual ~RailStationPreview() {}; + + // RailPreviewStation(RailStationGUISettings &settings) :settings{settings} {} + bool IsDragDrop() const override; + CursorID GetCursor() const override; + TileArea GetArea(bool remove_mode) const override; + up GetCommand(bool adjacent, StationID join_to) const override; + up GetRemoveCommand() const override; + void AddPreviewTiles(Preview::TileMap &tiles, SpriteID palette) const override; + bool Execute(up cmd, bool remove_mode) const override; + OverlayParams GetOverlayParams() const override; +}; + +class RoadStationPreview : public PreviewStationType { +protected: + DiagDirection ddir; + RoadStopType stop_type; + +public: + RoadStationPreview(RoadStopType stop_type) :stop_type{stop_type} {} + virtual ~RoadStationPreview() {}; + + bool IsDragDrop() const override; + CursorID GetCursor() const override; + TileArea GetArea(bool remove_mode) const override; + void Update(Point pt, TileIndex tile) override; + up GetCommand(bool adjacent, StationID join_to) const override; + up GetRemoveCommand() const override; + void AddPreviewTiles(Preview::TileMap &tiles, SpriteID palette) const override; + bool Execute(up cmd, bool remove_mode) const override; + OverlayParams GetOverlayParams() const override; +}; + +class DockPreview : public PreviewStationType { +protected: + DiagDirection ddir; + +public: + DockPreview() {} + virtual ~DockPreview() {}; + + bool IsDragDrop() const override; + CursorID GetCursor() const override; + TileArea GetArea(bool remove_mode) const override; + void Update(Point pt, TileIndex tile) override; + up GetCommand(bool adjacent, StationID join_to) const override; + up GetRemoveCommand() const override; + void AddPreviewTiles(Preview::TileMap &tiles, SpriteID palette) const override; + bool Execute(up cmd, bool remove_mode) const override; + OverlayParams GetOverlayParams() const override; +}; + +class StationPreviewBase : public Preview { +protected: + sp type; + bool remove_mode = false; + bool keep_rail = true; // whether to keep rail in remove mode + StationID station_to_join = INVALID_STATION; + bool adjacent_stations = false; + bool show_coverage = true; + + void AddAreaTiles(Preview::TileMap &tiles, bool add_current, bool show_join_area); + virtual void Execute() = 0; + up GetCommand(bool adjacent, StationID join_to); + void AddStationPreview(Preview::TileMap &tiles, SpriteID palette); + +public: + StationPreviewBase(sp type) :type{type} {}; + CursorID GetCursor() override { return this->type->GetCursor(); }; + void Update(Point pt, TileIndex tile) override; + bool HandleMousePress() override; + void HandleMouseRelease() override; + std::vector> GetOverlayData(); +}; + + +class VanillaStationPreview : public StationPreviewBase { +protected: + SpriteID palette; + + void Execute() override; + +public: + StationID selected_station_to_join = INVALID_STATION; + + VanillaStationPreview(sp type) :StationPreviewBase{type} {}; + virtual ~VanillaStationPreview() {}; + void Update(Point pt, TileIndex tile) override; + + Preview::TileMap GetTiles() override; + void OnStationRemoved(const Station *station) override; +}; + + +class StationPreview : public StationPreviewBase { +protected: + bool select_mode = false; + + void Execute() override; + up GetCommand(); + +public: + StationPreview(sp type); + virtual ~StationPreview(); + void Update(Point pt, TileIndex tile) override; + bool HandleMousePress() override; + + Preview::TileMap GetTiles() override; + void OnStationRemoved(const Station *station) override; +}; + +// SPR_CURSOR_BUS_STATION SPR_CURSOR_TRUCK_STATION + +bool HandleStationPlacePushButton(Window *w, WidgetID widget, sp type); + } // namespace citymania diff --git a/src/citymania/cm_type.hpp b/src/citymania/cm_type.hpp index 0c636af7..85a4ecae 100644 --- a/src/citymania/cm_type.hpp +++ b/src/citymania/cm_type.hpp @@ -1,6 +1,9 @@ #ifndef CMEXT_TYPE_HPP #define CMEXT_TYPE_HPP +#include "../core/geometry_type.hpp" +#include "../core/overflowsafe_type.hpp" + #include #include #include @@ -71,4 +74,26 @@ enum class ControllerType: uint8_t { } // namespace citymania + +template +struct fmt::formatter> { + constexpr auto parse(format_parse_context& ctx) { return ctx.begin(); } + + template + auto format(const OverflowSafeInt& i, FormatContext& ctx) { + return fmt::format_to(ctx.out(), "{}", static_cast(i)); + } +}; + +template <> +struct fmt::formatter { + constexpr auto parse(format_parse_context& ctx) { return ctx.begin(); } + + template + auto format(const Point& pt, FormatContext& ctx) { + return fmt::format_to(ctx.out(), "({},{})", pt.x, pt.y); + } +}; + + #endif diff --git a/src/company_gui.cpp b/src/company_gui.cpp index 2842f7e7..e42fccd0 100644 --- a/src/company_gui.cpp +++ b/src/company_gui.cpp @@ -49,9 +49,12 @@ #include "citymania/cm_hotkeys.hpp" #include "citymania/cm_main.hpp" +#include "citymania/cm_commands_gui.hpp" #include "safeguards.h" +/* Admin company buttons */ +bool Show_ACB[15]; /** Company GUI constants. */ static void DoSelectCompanyManagerFace(Window *parent); @@ -2196,6 +2199,10 @@ static constexpr NWidgetPart _nested_company_widgets[] = { NWidget(WWT_TEXTBTN, COLOUR_GREY, WID_C_RELOCATE_HQ), SetDataTip(STR_COMPANY_VIEW_RELOCATE_HQ, STR_COMPANY_VIEW_RELOCATE_COMPANY_HEADQUARTERS), NWidget(NWID_SPACER), EndContainer(), + /* Admin company buttons */ + NWidget(NWID_SELECTION, INVALID_COLOUR, CM_WID_C_SELECT_ADMINBUTTONS), + NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, CM_WID_C_ADMINBUTTONS), SetDataTip(CM_STR_ACB_COMPANY_ADMIN_BUTTON, CM_STR_ACB_COMPANY_ADMIN_BUTTON_TOOLTIP), + EndContainer(), EndContainer(), EndContainer(), @@ -2225,15 +2232,7 @@ static constexpr NWidgetPart _nested_company_widgets[] = { NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_C_COMPANY_PASSWORD), SetDataTip(STR_COMPANY_VIEW_PASSWORD, STR_COMPANY_VIEW_PASSWORD_TOOLTIP), NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_C_COMPANY_JOIN), SetDataTip(STR_COMPANY_VIEW_JOIN, STR_COMPANY_VIEW_JOIN_TOOLTIP), EndContainer(), - // NWidget(NWID_SELECTION, INVALID_COLOUR, WID_C_SELECT_MOD), - // NWidget(NWID_SPACER), SetMinimalSize(0, 0), SetFill(0, 1), - // NWidget(NWID_VERTICAL), SetPIP(4, 2, 4), - // NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_C_MOD_COMPANY_JOIN), SetFill(1, 0), SetDataTip(STR_MOD_COMPANY_JOIN_BUTTON, STR_NULL), - // NWidget(WWT_TEXTBTN, COLOUR_GREY, WID_C_MOD_COMPANY_TOGGLE_LOCK), SetFill(1, 0), SetDataTip(STR_MOD_TOGGLE_LOCK_BUTTON, STR_NULL), - // NWidget(WWT_TEXTBTN, COLOUR_GREY, WID_C_MOD_COMPANY_RESET), SetFill(1, 0), SetDataTip(STR_MOD_COMPANY_RESET_BUTTON, STR_NULL), - // EndContainer(), - // EndContainer(), - EndContainer(), + EndContainer(), EndContainer(), EndContainer(), EndContainer(), @@ -2257,14 +2256,6 @@ static const StringID _company_view_vehicle_count_strings[] = { /** * Window with general information about a company */ -static void ResetCallback(Window *w, bool confirmed) -{ - if (confirmed) { - CompanyID company2 = (CompanyID)w->window_number; - citymania::NetworkClientSendChatToServer(fmt::format("!reset {}", company2 + 1)); - } -} - struct CompanyWindow : Window { CompanyWidgets query_widget; @@ -2302,6 +2293,10 @@ struct CompanyWindow : Window /* Button bar selection. */ reinit |= this->GetWidget(WID_C_SELECT_BUTTONS)->SetDisplayedPlane(local ? 0 : SZSP_NONE); + /* Admin company buttons: show/hide admin menue button from param of citymania.cfg */ + citymania::CheckAdmin(); + reinit |= this->GetWidget(CM_WID_C_SELECT_ADMINBUTTONS)->SetDisplayedPlane(citymania::GetAdmin() ? 0 : SZSP_NONE); + /* Build HQ button handling. */ reinit |= this->GetWidget(WID_C_SELECT_VIEW_BUILD_HQ)->SetDisplayedPlane((local && c->location_of_HQ == INVALID_TILE) ? CWP_VB_BUILD : CWP_VB_VIEW); @@ -2321,17 +2316,13 @@ struct CompanyWindow : Window if (reinit) { this->ReInit(); + /* Admin company buttons: recall buttons while join/leave company */ + if (Show_ACB[this->window_number]) citymania::ShowAdminCompanyButtons(this->left, this->top, this->width, this->window_number + 1, Show_ACB[this->window_number], true); return; } } - - // if(!_networking) { - // this->SetWidgetDisabledState(CW_WIDGET_COMPANY_RESUME, true); - // this->SetWidgetDisabledState(CW_WIDGET_COMPANY_SUSPEND, true); - // this->SetWidgetDisabledState(CW_WIDGET_COMPANY_RESET, true); - // this->SetWidgetDisabledState(CW_WIDGET_COMPANY_JOIN2, true); - // } - + /* Admin company buttons: recall buttons while moving company window */ + if (Show_ACB[this->window_number]) citymania::ShowAdminCompanyButtons(this->left, this->top, this->width, this->window_number + 1, Show_ACB[this->window_number], false); this->DrawWidgets(); } @@ -2389,19 +2380,14 @@ struct CompanyWindow : Window size->width = std::max(size->width, GetStringBoundingBox(STR_COMPANY_VIEW_GIVE_MONEY_BUTTON).width); size->width = std::max(size->width, GetStringBoundingBox(STR_COMPANY_VIEW_HOSTILE_TAKEOVER_BUTTON).width); size->width = std::max(size->width, GetStringBoundingBox(STR_COMPANY_VIEW_PASSWORD).width); - size->width = std::max(size->width, GetStringBoundingBox(STR_COMPANY_VIEW_JOIN).width); + size->width += padding.width; break; - // case CW_WIDGET_COMPANY_RESUME: - // case CW_WIDGET_COMPANY_SUSPEND: - // case CW_WIDGET_COMPANY_RESET: - // case CW_WIDGET_COMPANY_JOIN2: - // if(!_novarole){ - // size->width = 0; - // size->height = 0; - // } - // break; + /* Admin company buttons */ + case CM_WID_C_ADMINBUTTONS: + size->width = std::max(size->width, GetStringBoundingBox(CM_STR_ACB_COMPANY_ADMIN_BUTTON).width); + case WID_C_HAS_PASSWORD: if (_networking) *size = maxdim(*size, GetSpriteSize(SPR_LOCK)); break; @@ -2624,28 +2610,13 @@ struct CompanyWindow : Window MarkWholeScreenDirty(); break; } - - case WID_C_MOD_COMPANY_JOIN: { - if (!_novarole) return; + /* Admin company buttons: show/hide buttons */ + case CM_WID_C_ADMINBUTTONS: { CompanyID company2 = (CompanyID)this->window_number; - // this->query_widget = WID_C_MOD_COMPANY_JOIN; - citymania::NetworkClientSendChatToServer(fmt::format("!move {}", company2 + 1)); - MarkWholeScreenDirty(); - break; - } - case WID_C_MOD_COMPANY_RESET: { - if (!_networking) return; - this->query_widget = WID_C_MOD_COMPANY_RESET; - ShowQuery(CM_STR_XI_RESET_CAPTION, CM_STR_XI_REALY_RESET, this, ResetCallback); - MarkWholeScreenDirty(); - break; - } - case WID_C_MOD_COMPANY_TOGGLE_LOCK: { - if (!_novarole) return; - CompanyID company2 = (CompanyID)this->window_number; - citymania::NetworkClientSendChatToServer(fmt::format("!lockp {}", company2 + 1)); - MarkWholeScreenDirty(); - break; + if (!Show_ACB[company2]) Show_ACB[company2] = true; + else Show_ACB[company2] = false; + citymania::ShowAdminCompanyButtons(this->left, this->top, this->width,company2 + 1, Show_ACB[company2], false); + break; } } } @@ -2702,6 +2673,14 @@ struct CompanyWindow : Window OnResize(); } } + + /* Admin company buttons: close window */ + void Close([[maybe_unused]] int data) override + { + if (FindWindowById(CM_WC_ADMIN_COMPANY_BUTTONS,this->window_number+1)) + CloseWindowById(CM_WC_ADMIN_COMPANY_BUTTONS,this->window_number+1); + this->Window::Close(); + } }; static WindowDesc _company_desc(__FILE__, __LINE__, diff --git a/src/dock_gui.cpp b/src/dock_gui.cpp index f8552e5e..428c62e7 100644 --- a/src/dock_gui.cpp +++ b/src/dock_gui.cpp @@ -171,7 +171,11 @@ struct BuildDocksToolbarWindow : Window { break; case WID_DT_STATION: // Build station button - if (HandlePlacePushButton(this, WID_DT_STATION, SPR_CURSOR_DOCK, HT_SPECIAL, CM_DDSP_BUILD_DOCK)) ShowBuildDockStationPicker(this); + // if (HandlePlacePushButton(this, WID_DT_STATION, SPR_CURSOR_DOCK, HT_SPECIAL, CM_DDSP_BUILD_DOCK)) ShowBuildDockStationPicker(this); + if (citymania::HandleStationPlacePushButton(this, WID_DT_STATION, std::make_shared())) { + ShowBuildDockStationPicker(this); + } + break; case WID_DT_BUOY: // Build buoy button diff --git a/src/graph_gui.cpp b/src/graph_gui.cpp index 8488359b..8a170aeb 100644 --- a/src/graph_gui.cpp +++ b/src/graph_gui.cpp @@ -1299,7 +1299,7 @@ struct PaymentRatesGraphWindow : ExcludingCargoBaseGraphWindow { for (const CargoSpec *cs : _sorted_standard_cargo_specs) { this->colours[i] = cs->legend_colour; for (uint j = 0; j != this->num_on_x_axis; j++) { - this->cost[i][j] = GetTransportedGoodsIncome(10, 20, j * 4 + 4, cs->Index()); + this->cost[i][j] = GetTransportedGoodsIncome(10, 20, 2 * (j * 4 + 4), cs->Index()); } i++; } diff --git a/src/intro_gui.cpp b/src/intro_gui.cpp index a4fa957b..3e81ed16 100644 --- a/src/intro_gui.cpp +++ b/src/intro_gui.cpp @@ -41,6 +41,7 @@ #include "table/sprites.h" #include "citymania/cm_hotkeys.hpp" +#include "citymania/cm_commands_gui.hpp" #include "safeguards.h" @@ -297,6 +298,9 @@ struct SelectGameWindow : public Window { DrawStringMultiLine(r.left, r.right, r.top, r.bottom, STR_INTRO_TRANSLATION, TC_FROMSTRING, SA_CENTER); break; } + /* last server widget & serverbuttons */ + citymania::CreateCommunityServerList(); + citymania::JoinLastServer(this->left, this->top, this->height); } void UpdateWidgetSize(WidgetID widget, Dimension *size, [[maybe_unused]] const Dimension &padding, [[maybe_unused]] Dimension *fill, [[maybe_unused]] Dimension *resize) override @@ -452,7 +456,9 @@ static constexpr NWidgetPart _nested_select_game_widgets[] = { /* 'Exit' button */ NWidget(NWID_HORIZONTAL), SetPIPRatio(1, 0, 1), + NWidget(NWID_SPACER), SetMinimalSize(138, 0), SetFill(1, 0), NWidget(WWT_PUSHTXTBTN, COLOUR_ORANGE, WID_SGI_EXIT), SetMinimalSize(128, 0), SetDataTip(STR_INTRO_QUIT, STR_INTRO_TOOLTIP_QUIT), + NWidget(NWID_SPACER), SetMinimalSize(138, 0), SetFill(1, 0), EndContainer(), EndContainer(), EndContainer(), diff --git a/src/lang/english.txt b/src/lang/english.txt index c9379d9b..a4596c95 100644 --- a/src/lang/english.txt +++ b/src/lang/english.txt @@ -5951,6 +5951,8 @@ CM_STR_TOOLBAR_COMMANDS_HINT_CAPTION :{BLACK}CB Hint CM_STR_TOOLBAR_COMMANDS_HINT_TOOLTIP :{BLACK}Send !hint command to server(Shows town demand in CB mode). CM_STR_TOOLBAR_COMMANDS_LOGIN_CAPTION :{BLACK}Login CM_STR_TOOLBAR_COMMANDS_LOGIN_TOOLTIP :{BLACK}Send !login command to server. +CM_STR_TOOLBAR_COMMANDS_LOGOUT_CAPTION :{BLACK}Logout +CM_STR_TOOLBAR_COMMANDS_LOGOUT_TOOLTIP :{BLACK}Send !logout command to server. CM_STR_TOOLBAR_COMMANDS_TIMELEFT_CAPTION :{BLACK}Timeleft CM_STR_TOOLBAR_COMMANDS_TIMELEFT_TOOLTIP :{BLACK}Send !timeleft command to server. CM_STR_TOOLBAR_COMMANDS_NS_CAPTION :{BLACK}CM{NUM} @@ -6197,25 +6199,6 @@ CM_STR_CONFIG_SETTING_GRAPH_BACKGROUND_HELPTEXT :Main background CM_STR_CONFIG_SETTING_GRAPH_BACKGROUND_BLACK :Black CM_STR_CONFIG_SETTING_GRAPH_BACKGROUND_GREY :Light grey -# Community login window -CM_STR_LOGIN_WINDOW_CAPTION :{WHITE}Login Window -CM_STR_LOGIN_WINDOW_CITYMANIA :{BLACK}CityMania -CM_STR_LOGIN_WINDOW_NICE :{BLACK}N-ice -CM_STR_LOGIN_WINDOW_BTPRO :{BLACK}BTPro -CM_STR_LOGIN_ERROR_SIGN_IN_FAILED :{WHITE}Failed to connect -CM_STR_LOGIN_ERROR_BAD_INPUT :{WHITE}Bad username or password -CM_STR_LOGIN_WINDOW_USERNAME :{WHITE}Username -CM_STR_LOGIN_WINDOW_PASSWORD :{WHITE}Password -CM_STR_LOGIN_WINDOW_SET :{WHITE}Set -CM_STR_LOGIN_WINDOW_NOT_SET :{WHITE}Not Set -CM_STR_LOGIN_WINDOW_CHANGE_USERNAME :{WHITE}Change username -CM_STR_LOGIN_WINDOW_CHANGE_PASSWORD :{WHITE}Change password -CM_STR_LOGIN_WINDOW_CHANGE_USERNAME_HELPTEXT :{BLACK}Change username -CM_STR_LOGIN_WINDOW_CHANGE_PASSWORD_HELPTEXT :{BLACK}Change password -CM_STR_LOGIN_WINDOW_SIGN_IN_HELPTEXT :{BLACK}Sign in (you must be on the correct community server) -CM_STR_LOGIN_WINDOW_USERNAME_DISPLAY :{WHITE}{RAW_STRING} -CM_STR_LOGIN_WINDOW_PASSWORD_DISPLAY :{WHITE}{STRING} - CM_STR_NETWORK_COMPANY_LIST_NEW_COMPANY :New company CM_STR_NETWORK_COMPANY_LIST_SPECTATE :Spectate @@ -6297,7 +6280,6 @@ CM_STR_CONFIG_SETTING_TYPE_DROPDOWN_CITYMANIA :CityMania patch CM_BUILDING_PREVIEW_COST_ENOUGH :Cost CM_STR_NO_BLUEPRINT_IN_SLOT :{WHITE}No blueprint in slot {NUM} -CM_STR_ABOUT_MENU_LOGIN_WINDOW :CityMania server login CM_STR_BUILD_INFO_OVERLAY_COST_OK :{WHITE}Cost: {CURRENCY_LONG} CM_STR_BUILD_INFO_OVERLAY_COST_NO_MONEY :{RED}Cost: {CURRENCY_LONG} CM_STR_BUILD_INFO_OVERLAY_STATION_ACCEPTS :{WHITE}Accepts: @@ -6312,7 +6294,94 @@ CM_STR_CONFIG_SETTING_TOOLBAR_DROPDOWN_CLOSE :Toolbar dropdow CM_STR_CONFIG_SETTING_TOOLBAR_DROPDOWN_CLOSE_HELPTEXT :Control whether the main toolbar dropdowns activate their default function when the mouse button is released. If not enabled then the dropdown menu will stay open so a selection can be made. ###length VEHICLE_TYPES -CM_STR_SHOW_HIDDEN_ENGINES_VEHICLE_TRAIN :{BLACK}Show hidden ({NUM}) -CM_STR_SHOW_HIDDEN_ENGINES_VEHICLE_ROAD_VEHICLE :{BLACK}Show hidden ({NUM}) -CM_STR_SHOW_HIDDEN_ENGINES_VEHICLE_SHIP :{BLACK}Show hidden ({NUM}) -CM_STR_SHOW_HIDDEN_ENGINES_VEHICLE_AIRCRAFT :{BLACK}Show hidden ({NUM}) +CM_STR_SHOW_HIDDEN_ENGINES_VEHICLE_TRAIN :{BLACK}Show hidden ({NUM}) +CM_STR_SHOW_HIDDEN_ENGINES_VEHICLE_ROAD_VEHICLE :{BLACK}Show hidden ({NUM}) +CM_STR_SHOW_HIDDEN_ENGINES_VEHICLE_SHIP :{BLACK}Show hidden ({NUM}) +CM_STR_SHOW_HIDDEN_ENGINES_VEHICLE_AIRCRAFT :{BLACK}Show hidden ({NUM}) + +### Community login window +CM_STR_LOGIN_WINDOW_CAPTION :Login Window +CM_STR_LOGIN_WINDOW_CITYMANIA :{BLACK}CityMania +CM_STR_LOGIN_WINDOW_NICE :{BLACK}N-ice +CM_STR_LOGIN_WINDOW_BTPRO :{BLACK}BTPro +CM_STR_LOGIN_ERROR_SIGN_IN_FAILED :{WHITE}Failed to connect +CM_STR_LOGIN_ERROR_BAD_INPUT :{WHITE}Bad username or password +CM_STR_LOGIN_WINDOW_USERNAME :{WHITE}Username: +CM_STR_LOGIN_WINDOW_PASSWORD :{WHITE}Password: +CM_STR_LOGIN_WINDOW_LOGIN :{BLACK}Login +CM_STR_LOGIN_WINDOW_LOGIN_TOOLTIP :Send login to server +CM_STR_LOGIN_WINDOW_LOGOUT :{BLACK}Logout +CM_STR_LOGIN_WINDOW_LOGOUT_TOOLTIP :Send logout to server +CM_STR_LOGIN_WINDOW_ADMIN_LOGIN :{BLACK}A-Login +CM_STR_LOGIN_WINDOW_ADMIN_LOGIN_TOOLTIP :Send admin login to server +CM_STR_LOGIN_WINDOW_ADMIN_LOGOUT :{BLACK}A-Logout +CM_STR_LOGIN_WINDOW_ADMIN_LOGOUT_TOOLTIP :Send admin logout to server +CM_STR_LOGIN_WINDOW_ADMIN_PASSWORD :{WHITE}Admin: +CM_STR_LOGIN_WINDOW_SET :{WHITE}Set +CM_STR_LOGIN_WINDOW_NOT_SET :{WHITE}Not Set +CM_STR_LOGIN_WINDOW_CHANGE_USERNAME :{WHITE}Change username +CM_STR_LOGIN_WINDOW_CHANGE_PASSWORD :{WHITE}Change password +CM_STR_LOGIN_WINDOW_CHANGE_USERNAME_TOOLTIP :Enter your username here +CM_STR_LOGIN_WINDOW_CHANGE_PASSWORD_TOOLTIP :Enter your password here +CM_STR_LOGIN_WINDOW_USERNAME_DISPLAY :{WHITE}{RAW_STRING} +CM_STR_LOGIN_WINDOW_PASSWORD_DISPLAY :{WHITE}{STRING} +CM_STR_LOGIN_WINDOW_WELCOME :{BLACK}Welcome to {STRING} server ! + +### Admin company buttons +CM_STR_ACB_COMPANY_ADMIN_CAPTION :{WHITE}Admin commands #{NUM} +CM_STR_ACB_COMPANY_ADMIN_BUTTON :{WHITE}Admin commands +CM_STR_ACB_COMPANY_ADMIN_BUTTON_TOOLTIP :{BLACK}Show / hide the admin commands. +CM_STR_ACB_LOCK :{BLACK}Lock +CM_STR_ACB_LOCK_TOOLTIP :{BLACK}Lock Company. All players on it will be sent to spectators. +CM_STR_ACB_UNLOCK :{BLACK}Unlock +CM_STR_ACB_UNLOCK_TOOLTIP :{BLACK}Unlock Company making possible player(s) joining it again. +CM_STR_ACB_KNOWN :{BLACK}Known +CM_STR_ACB_KNOWN_TOOLTIP :{BLACK}Show the known player(s) of company. +CM_STR_ACB_RESET_KNOWN :{BLACK}Reset Known +CM_STR_ACB_RESET_KNOWN_TOOLTIP :{BLACK}Reset list of known players of company. +CM_STR_ACB_MOVE_PLAYER_TO :{BLACK}Move to ... +CM_STR_ACB_MOVE_PLAYER_TO_TOOLTIP :{BLACK}Move a player (number #.. or name) to company +CM_STR_ACB_COMPANY_NEWSTICKET_BUTTON :{BLACK}News +CM_STR_ACB_COMPANY_NEWSTICKET_BUTTON_TOOLTIP :{BLACK}Open a Newspapper message directed to Company +CM_STR_ACB_COMPANY_NEWSTICKET_BUTTON_COMP :{BLACK}News (P. C.) +CM_STR_ACB_COMPANY_NEWSTICKET_BUTTON_COMP_TOOLTIP :{BLACK}Open a Newspapper message only showed to Company +CM_STR_ACB_SUSPEND :{BLACK}Suspend +CM_STR_ACB_SUSPEND_TOOLTIP :{BLACK}Suspend Company actions keeping player in the company. +CM_STR_ACB_UNSUSPEND :{BLACK}Unsuspend +CM_STR_ACB_UNSUSPEND_TOOLTIP :{BLACK}Unsuspend Company actions. +CM_STR_ACB_EMPTY :{BLACK}Empty +CM_STR_ACB_EMPTY_TOOLTIP :{BLACK}Send players in company to Spectators. +CM_STR_ACB_RESET :{BLACK}Reset +CM_STR_ACB_RESET_TOOLTIP :{BLACK}Reset the company. The company must be empty. +CM_STR_ACB_RESET_SPEC :{BLACK}Reset Spec +CM_STR_ACB_RESET_SPEC_TOOLTIP :{BLACK}Reset company and send player(s) to spectators. +CM_STR_ACB_RESET_KICK :{BLACK}Reset Kick +CM_STR_ACB_RESET_KICK_TOOLTIP :{BLACK}Reset company and Kick out of the game player(s) on it. +CM_STR_ACB_RESET_TIMER_120 :{BLACK}Reset T. 2m +CM_STR_ACB_RESET_TIMER_120_TOOLTIP :{BLACK}Starts a timer to reset company when after 2 minutes countdown. +CM_STR_ACB_RESET_TIMER :{BLACK}Reset T.... +CM_STR_ACB_RESET_TIMER_TOOLTIP :{BLACK}Starts a timer to reset company when time expire (time is Admin input). +CM_STR_ACB_RESET_TIMER_VALUE :{BLACK}Time to Reset (seconds): +CM_STR_ACB_RESET_TIMER_CANCEL :{BLACK}Cancel Reset +CM_STR_ACB_RESET_TIMER_CANCEL_TOOLTIP :{BLACK}Cancel the Reset timer. +CM_STR_ACB_AWARNING :{BLACK}Warning +CM_STR_ACB_AWARNING_TOOLTIP :{BLACK}Sends a Warning to company that an admin is trying to contact. +CM_STR_ACB_RESET_COMP :{BLACK}RESET Company +CM_STR_ACB_RESET_COMP_SURE :{WHITE}Sure to RESET this company? +CM_STR_ACB_RESET_SPEC_SURE :{WHITE}Sure to RESET this company, send players to SPECTATORS? +CM_STR_ACB_RESET_KICK_SURE :{WHITE}Sure to RESET this company, and KICK players on it? +CM_STR_ACB_PLAYER_NEWSTICKET :{BLACK}Type message as newspapper ticket to this Player +CM_STR_ACB_COMPANY_NEWSTICKET :{BLACK}Type message as newspapper ticket to this Company +CM_STR_ACB_COMPANY_JOIN2 :{BLACK}A. Join +CM_STR_ACB_COMPANY_JOIN2_TOOLTIP :{BLACK}Join as an admin + +### Server buttons +CM_STR_SB_SELECT_NICE :{BLACK}N-ice +CM_STR_SB_SELECT_BTPRO :{BLACK}BTPro +CM_STR_SB_SELECT_CITYMANIA :{BLACK}Citymania +CM_STR_SB_SERVER_LIST_UNREACHABLE :{WHITE}Couldn't get server details +CM_STR_SB_SERVER_DISABLED :{WHITE}Server is Disabled / Offline +CM_STR_SB_SERVER_LIST_ERROR_FILE :{WHITE}Error on getting servers file +CM_STR_SB_NETWORK_DIRECT_JOIN_GAME :{BLACK}{RAW_STRING} +CM_STR_SB_NETWORK_DIRECT_JOIN_TOOLTIP :{BLACK}Hold CTRL and click for joining with new company. +CM_STR_SB_LAST_SERVER :{BLACK}Join the server you played last time diff --git a/src/lang/german.txt b/src/lang/german.txt index 53e1eea9..e1108931 100644 --- a/src/lang/german.txt +++ b/src/lang/german.txt @@ -6193,25 +6193,6 @@ CM_STR_CONFIG_SETTING_GRAPH_BACKGROUND_HELPTEXT :Haupt-Hintergru CM_STR_CONFIG_SETTING_GRAPH_BACKGROUND_BLACK :Schwarz CM_STR_CONFIG_SETTING_GRAPH_BACKGROUND_GREY :Hellgrau -# Community login window -CM_STR_LOGIN_WINDOW_CAPTION :{WHITE}Login-Fenster -CM_STR_LOGIN_WINDOW_CITYMANIA :{BLACK}CityMania -CM_STR_LOGIN_WINDOW_NICE :{BLACK}N-ice -CM_STR_LOGIN_WINDOW_BTPRO :{BLACK}BTPro -CM_STR_LOGIN_ERROR_SIGN_IN_FAILED :{WHITE}Verbindung fehlgeschlagen -CM_STR_LOGIN_ERROR_BAD_INPUT :{WHITE}Falscher Benutzername oder Passwort -CM_STR_LOGIN_WINDOW_USERNAME :{WHITE}Benutzername -CM_STR_LOGIN_WINDOW_PASSWORD :{WHITE}Passwort -CM_STR_LOGIN_WINDOW_SET :{WHITE}Gesetzt -CM_STR_LOGIN_WINDOW_NOT_SET :{WHITE}Nicht gesetzt -CM_STR_LOGIN_WINDOW_CHANGE_USERNAME :{WHITE}Benutzernamen ändern -CM_STR_LOGIN_WINDOW_CHANGE_PASSWORD :{WHITE}Passwort ändern -CM_STR_LOGIN_WINDOW_CHANGE_USERNAME_HELPTEXT :{BLACK}Benutzernamen ändern -CM_STR_LOGIN_WINDOW_CHANGE_PASSWORD_HELPTEXT :{BLACK}Passwort ändern -CM_STR_LOGIN_WINDOW_SIGN_IN_HELPTEXT :{BLACK}Login abschicken (Sie müssen auf dem richtigen Community-Server sein) -CM_STR_LOGIN_WINDOW_USERNAME_DISPLAY :{WHITE}{STRING} -CM_STR_LOGIN_WINDOW_PASSWORD_DISPLAY :{WHITE}{STRING} - CM_STR_NETWORK_COMPANY_LIST_NEW_COMPANY :Neue Firma CM_STR_NETWORK_COMPANY_LIST_SPECTATE :Zuschauen @@ -6289,4 +6270,90 @@ CM_STR_INDUSTRY_TYPE_WITH_ID :{STRING} {SILVE CM_STR_CONFIG_SETTING_TYPE_DROPDOWN_CITYMANIA :CityMania-Patchpack-Einstellungen CM_BUILDING_PREVIEW_COST_ENOUGH :Kosten -CM_STR_ABOUT_MENU_LOGIN_WINDOW :CityMania-Serverlogin + +### Community login window +CM_STR_LOGIN_WINDOW_CAPTION :Serveranmeldung +CM_STR_LOGIN_WINDOW_CITYMANIA :{BLACK}CityMania +CM_STR_LOGIN_WINDOW_NICE :{BLACK}N-ice +CM_STR_LOGIN_WINDOW_BTPRO :{BLACK}BTPro +CM_STR_LOGIN_ERROR_SIGN_IN_FAILED :{WHITE}Verbindung fehlgeschlagen +CM_STR_LOGIN_ERROR_BAD_INPUT :{WHITE}Falscher Benutzername oder Passwort +CM_STR_LOGIN_WINDOW_USERNAME :{WHITE}Benutzername +CM_STR_LOGIN_WINDOW_PASSWORD :{WHITE}Passwort +CM_STR_LOGIN_WINDOW_LOGIN :{BLACK}Anmelden +CM_STR_LOGIN_WINDOW_LOGIN_TOOLTIP :beim Server anmelden +CM_STR_LOGIN_WINDOW_LOGOUT :{BLACK}Abmelden +CM_STR_LOGIN_WINDOW_LOGOUT_TOOLTIP :beim Server abmelden +CM_STR_LOGIN_WINDOW_ADMIN_LOGIN :{BLACK}A-Anmelden +CM_STR_LOGIN_WINDOW_ADMIN_LOGIN_TOOLTIP :als Admin beim Server anmelden +CM_STR_LOGIN_WINDOW_ADMIN_LOGOUT :{BLACK}A-Abmelden +CM_STR_LOGIN_WINDOW_ADMIN_LOGOUT_TOOLTIP :als Admin beim Server abmelden +CM_STR_LOGIN_WINDOW_ADMIN_PASSWORD :{WHITE}Admin: +CM_STR_LOGIN_WINDOW_SET :{WHITE}Gesetzt +CM_STR_LOGIN_WINDOW_NOT_SET :{WHITE}Nicht gesetzt +CM_STR_LOGIN_WINDOW_CHANGE_USERNAME :{WHITE}Benutzernamen ändern +CM_STR_LOGIN_WINDOW_CHANGE_PASSWORD :{WHITE}Passwort ändern +CM_STR_LOGIN_WINDOW_CHANGE_USERNAME_TOOLTIP :{BLACK}Benutzernamen hier eingeben +CM_STR_LOGIN_WINDOW_CHANGE_PASSWORD_TOOLTIP :{BLACK}Passwort hier eingeben +CM_STR_LOGIN_WINDOW_USERNAME_DISPLAY :{WHITE}{STRING} +CM_STR_LOGIN_WINDOW_PASSWORD_DISPLAY :{WHITE}{STRING} +CM_STR_LOGIN_WINDOW_WELCOME :{BLACK}Willkommen beim {STRING} Server ! + +### Admin company buttons +CM_STR_ACB_COMPANY_ADMIN_CAPTION :{WHITE}Adminbefehle #{NUM} +CM_STR_ACB_COMPANY_ADMIN_BUTTON :{WHITE}Adminbefehle +CM_STR_ACB_COMPANY_ADMIN_BUTTON_TOOLTIP :{BLACK}Zeige / verstecke die Adminbefehle. +CM_STR_ACB_LOCK :{BLACK}Lock +CM_STR_ACB_LOCK_TOOLTIP :{BLACK}Sperre eine Firma, alle Spiele werden Zuschauer. +CM_STR_ACB_UNLOCK :{BLACK}Unlock +CM_STR_ACB_UNLOCK_TOOLTIP :{BLACK}Entsperre eine Firma, Spieler können wieder beitreten. +CM_STR_ACB_KNOWN :{BLACK}Known +CM_STR_ACB_KNOWN_TOOLTIP :{BLACK}Zeige die Liste der bekannten Spieler der Firma. +CM_STR_ACB_RESET_KNOWN :{BLACK}Reset Known +CM_STR_ACB_RESET_KNOWN_TOOLTIP :{BLACK}Lösche die List der bekannten Spieler der Firma. +CM_STR_ACB_MOVE_PLAYER_TO :{BLACK}Move to ... +CM_STR_ACB_MOVE_PLAYER_TO_TOOLTIP :{BLACK}Verschiebe Spieler (#123 oder Name) zur Firma +CM_STR_ACB_COMPANY_NEWSTICKET_BUTTON :{BLACK}News +CM_STR_ACB_COMPANY_NEWSTICKET_BUTTON_TOOLTIP :{BLACK}Erstelle eine Zeitungsnachricht mit den Namen der Firma. +CM_STR_ACB_COMPANY_NEWSTICKET_BUTTON_COMP :{BLACK}News (P. C.) +CM_STR_ACB_COMPANY_NEWSTICKET_BUTTON_COMP_TOOLTIP :{BLACK}Erstelle eine Zeitungsnachricht nur für die Firma. +CM_STR_ACB_SUSPEND :{BLACK}Suspend +CM_STR_ACB_SUSPEND_TOOLTIP :{BLACK}Deaktiviere alle Handlungen der Firma. +CM_STR_ACB_UNSUSPEND :{BLACK}Unsuspend +CM_STR_ACB_UNSUSPEND_TOOLTIP :{BLACK}Aktiviere alle Handlungen der Firma. +CM_STR_ACB_EMPTY :{BLACK}Empty +CM_STR_ACB_EMPTY_TOOLTIP :{BLACK}Mache alle Spieler der Firma zu Zuschauer +CM_STR_ACB_RESET :{BLACK}Reset +CM_STR_ACB_RESET_TOOLTIP :{BLACK}Lösche die Firma. Es dürfen keine Spieler in der Firma sein. +CM_STR_ACB_RESET_SPEC :{BLACK}Reset Spec +CM_STR_ACB_RESET_SPEC_TOOLTIP :{BLACK}Lösche die Firma und mache alle Spieler zu Zuschauer. +CM_STR_ACB_RESET_KICK :{BLACK}Reset Kick +CM_STR_ACB_RESET_KICK_TOOLTIP :{BLACK}Lösche die Firma und kicke die Spieler aus den Server. +CM_STR_ACB_RESET_TIMER_120 :{BLACK}Reset T. 2m +CM_STR_ACB_RESET_TIMER_120_TOOLTIP :{BLACK}Starte einen Countdown über 2 Minuten für die Löschung der Firma. +CM_STR_ACB_RESET_TIMER :{BLACK}Reset T.... +CM_STR_ACB_RESET_TIMER_TOOLTIP :{BLACK}Starte einen Countdown der eingebenen Minuten für die Löschung der Firma. +CM_STR_ACB_RESET_TIMER_VALUE :{BLACK}Time to Reset (seconds): +CM_STR_ACB_RESET_TIMER_CANCEL :{BLACK}Cancel Reset +CM_STR_ACB_RESET_TIMER_CANCEL_TOOLTIP :{BLACK}Abbruch des Countdowns. +CM_STR_ACB_AWARNING :{BLACK}Warning +CM_STR_ACB_AWARNING_TOOLTIP :{BLACK}Sende eine Nachricht zum Spieler, dass ein Admin eine Antwort erwartet. +CM_STR_ACB_RESET_COMP :{BLACK}RESET Company +CM_STR_ACB_RESET_COMP_SURE :{WHITE}Die Firma wirklich löschen? +CM_STR_ACB_RESET_SPEC_SURE :{WHITE}Die Firma wirklich löschen und alle Spieler zu Zuschauer machen? +CM_STR_ACB_RESET_KICK_SURE :{WHITE}Die Firma wirklich löschen und alle Spieler rauswerfen? +CM_STR_ACB_PLAYER_NEWSTICKET :{BLACK}Tippe die Nachricht an den Spieler ein +CM_STR_ACB_COMPANY_NEWSTICKET :{BLACK}Tippe die Nachricht an die Firma ein +CM_STR_ACB_COMPANY_JOIN2 :{BLACK}A. Join +CM_STR_ACB_COMPANY_JOIN2_TOOLTIP :{BLACK}als Admin anmelden + +### Server buttons +CM_STR_SB_SELECT_NICE :{BLACK}N-ice +CM_STR_SB_SELECT_BTPRO :{BLACK}BTPro +CM_STR_SB_SELECT_CITYMANIA :{BLACK}Citymania +CM_STR_SB_SERVER_LIST_UNREACHABLE :{WHITE}Serverliste nicht erreichbar +CM_STR_SB_SERVER_DISABLED :{WHITE}Server ist nicht erreichbar +CM_STR_SB_SERVER_LIST_ERROR_FILE :{WHITE}Fehler in der Serverliste +CM_STR_SB_NETWORK_DIRECT_JOIN_GAME :{BLACK}{RAW_STRING} +CM_STR_SB_NETWORK_DIRECT_JOIN_TOOLTIP :{BLACK}Halte STRG und klicke, um mit neuer Firma zu beginnen +CM_STR_SB_LAST_SERVER :{BLACK}Den zuletzt gespielten Server beitreten diff --git a/src/lang/russian.txt b/src/lang/russian.txt index 667b01ce..e5c406a4 100644 --- a/src/lang/russian.txt +++ b/src/lang/russian.txt @@ -6420,25 +6420,6 @@ CM_STR_CONFIG_SETTING_GRAPH_BACKGROUND_HELPTEXT :Основно CM_STR_CONFIG_SETTING_GRAPH_BACKGROUND_BLACK :Черный CM_STR_CONFIG_SETTING_GRAPH_BACKGROUND_GREY :Светло-серый -# Community login window -CM_STR_LOGIN_WINDOW_CAPTION :{WHITE}Login Window -CM_STR_LOGIN_WINDOW_CITYMANIA :{BLACK}CityMania -CM_STR_LOGIN_WINDOW_NICE :{BLACK}N-ice -CM_STR_LOGIN_WINDOW_BTPRO :{BLACK}BTPro -CM_STR_LOGIN_ERROR_SIGN_IN_FAILED :{WHITE}Failed to connect -CM_STR_LOGIN_ERROR_BAD_INPUT :{WHITE}Bad username or password -CM_STR_LOGIN_WINDOW_USERNAME :{WHITE}Username -CM_STR_LOGIN_WINDOW_PASSWORD :{WHITE}Password -CM_STR_LOGIN_WINDOW_SET :{WHITE}Set -CM_STR_LOGIN_WINDOW_NOT_SET :{WHITE}Not Set -CM_STR_LOGIN_WINDOW_CHANGE_USERNAME :{WHITE}Change username -CM_STR_LOGIN_WINDOW_CHANGE_PASSWORD :{WHITE}Change password -CM_STR_LOGIN_WINDOW_CHANGE_USERNAME_HELPTEXT :{BLACK}Change username -CM_STR_LOGIN_WINDOW_CHANGE_PASSWORD_HELPTEXT :{BLACK}Change password -CM_STR_LOGIN_WINDOW_SIGN_IN_HELPTEXT :{BLACK}Sign in (you must be on the correct community server) -CM_STR_LOGIN_WINDOW_USERNAME_DISPLAY :{WHITE}{STRING} -CM_STR_LOGIN_WINDOW_PASSWORD_DISPLAY :{WHITE}{STRING} - CM_STR_NETWORK_COMPANY_LIST_NEW_COMPANY :Новая компания CM_STR_NETWORK_COMPANY_LIST_SPECTATE :Стать зрителем @@ -6520,7 +6501,6 @@ CM_STR_CONFIG_SETTING_TYPE_DROPDOWN_CITYMANIA :Настрой CM_BUILDING_PREVIEW_COST_ENOUGH :Стоимость CM_STR_NO_BLUEPRINT_IN_SLOT :{WHITE}Нет чертежа в слоте {NUM} -CM_STR_ABOUT_MENU_LOGIN_WINDOW :Аутентификация на сервере CityMania CM_STR_BUILD_INFO_OVERLAY_COST_OK :{WHITE}Стоимость: {CURRENCY_LONG} CM_STR_BUILD_INFO_OVERLAY_COST_NO_MONEY :{RED}Стоимость: {CURRENCY_LONG} CM_STR_BUILD_INFO_OVERLAY_STATION_ACCEPTS :{WHITE}Принимается: @@ -6531,11 +6511,14 @@ CM_STR_BUILD_INFO_OVERLAY_ERROR_UNKNOWN :{RED}Неизв CM_STR_VEHICLE_INFO_BUILT_VALUE_WITH_ID :{LTBLUE}{ENGINE} {BLACK}Построен в {LTBLUE}{NUM} г.{BLACK} Стоимость: {LTBLUE}{CURRENCY_LONG}{BLACK} ID: {LTBLUE}{NUM} -CM_STR_CONFIG_SETTING_TOOLBAR_DROPDOWN_CLOSE :Активировать выпадающие меню при отпускании клавиши: {STRING} -CM_STR_CONFIG_SETTING_TOOLBAR_DROPDOWN_CLOSE_HELPTEXT :Контролирует, активируют ли выпадающие меню основной панели инструментов свою стандартную функцию при отпускании кнопки мыши. Если выключено, то выпадающее меню останется открытым, чтобы можно было сделать выбор. +CM_STR_CONFIG_SETTING_TOOLBAR_DROPDOWN_CLOSE :Активировать выпадающие меню при отпускании клавиши: {STRING} +CM_STR_CONFIG_SETTING_TOOLBAR_DROPDOWN_CLOSE_HELPTEXT :Контролирует, активируют ли выпадающие меню основной панели инструментов свою стандартную функцию при отпускании кнопки мыши. Если выключено, то выпадающее меню останется открытым, чтобы можно было сделать выбор. ###length VEHICLE_TYPES -CM_STR_SHOW_HIDDEN_ENGINES_VEHICLE_TRAIN :{BLACK}Показать скрытые ({NUM}) -CM_STR_SHOW_HIDDEN_ENGINES_VEHICLE_ROAD_VEHICLE :{BLACK}Показать скрытые ({NUM}) -CM_STR_SHOW_HIDDEN_ENGINES_VEHICLE_SHIP :{BLACK}Показать скрытые ({NUM}) -CM_STR_SHOW_HIDDEN_ENGINES_VEHICLE_AIRCRAFT :{BLACK}Показать скрытые ({NUM}) +CM_STR_SHOW_HIDDEN_ENGINES_VEHICLE_TRAIN :{BLACK}Показать скрытые ({NUM}) +CM_STR_SHOW_HIDDEN_ENGINES_VEHICLE_ROAD_VEHICLE :{BLACK}Показать скрытые ({NUM}) +CM_STR_SHOW_HIDDEN_ENGINES_VEHICLE_SHIP :{BLACK}Показать скрытые ({NUM}) +CM_STR_SHOW_HIDDEN_ENGINES_VEHICLE_AIRCRAFT :{BLACK}Показать скрытые ({NUM}) + +### Community login window +CM_STR_LOGIN_WINDOW_CAPTION :Аутентификация на сервере diff --git a/src/main_gui.cpp b/src/main_gui.cpp index 1fb554b1..24d948a6 100644 --- a/src/main_gui.cpp +++ b/src/main_gui.cpp @@ -7,9 +7,12 @@ /** @file main_gui.cpp Handling of the main viewport. */ +#include "3rdparty/fmt/core.h" +#include "citymania/cm_highlight.hpp" #include "stdafx.h" #include "currency.h" #include "spritecache.h" +#include "viewport_type.h" #include "window_gui.h" #include "window_func.h" #include "textbuf_gui.h" @@ -50,6 +53,8 @@ #include "debug.h" #include "citymania/cm_commands.hpp" +#include "citymania/cm_highlight.hpp" +#include "citymania/cm_station_gui.hpp" #include "safeguards.h" @@ -76,6 +81,13 @@ bool HandlePlacePushButton(Window *w, WidgetID widget, CursorID cursor, HighLigh } SetObjectToPlace(cursor, PAL_NONE, mode, w->window_class, w->window_number, cm_process); + // if (cm_process == DDSP_BUILD_STATION) { + // if (citymania::UseImprovedStationJoin()) { + // citymania::SetActivePreview(std::make_unique()); + // } else { + // citymania::SetActivePreview(std::make_unique()); + // } + // } w->LowerWidget(widget); return true; } diff --git a/src/network/network_client.cpp b/src/network/network_client.cpp index 560a78f0..1081b9fd 100644 --- a/src/network/network_client.cpp +++ b/src/network/network_client.cpp @@ -40,6 +40,7 @@ #include "../citymania/cm_client_list_gui.hpp" #include "../citymania/cm_commands.hpp" #include "../citymania/cm_newgrf_revisions.hpp" +#include "../citymania/cm_commands_gui.hpp" /* Admin company buttons */ #include "../safeguards.h" @@ -1227,7 +1228,9 @@ 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); - + citymania::CreateCommunityServerList(); //@cm_commands_gui.cpp + CSleep(3 * MILLISECONDS_PER_TICK); + citymania::ShowLoginWindow(); //@cm_commands_gui.cpp Debug(net, 9, "Client::Receive_SERVER_CONFIG_UPDATE(): max_companies={}", _network_server_max_companies); return NETWORK_RECV_STATUS_OKAY; @@ -1325,7 +1328,8 @@ void NetworkClientsToSpectators(CompanyID cid) NetworkTextMessage(NETWORK_ACTION_COMPANY_SPECTATOR, CC_DEFAULT, false, ci->client_name); ci->client_playas = COMPANY_SPECTATOR; } - + /* Admin company buttons: delete window */ + citymania::ShowAdminCompanyButtons(0, 0, 0, cid+1, false, false); cur_company.Restore(); } diff --git a/src/rail_cmd.cpp b/src/rail_cmd.cpp index e8dcd5a5..c03f2f31 100644 --- a/src/rail_cmd.cpp +++ b/src/rail_cmd.cpp @@ -902,9 +902,8 @@ static CommandCost CmdRailTrackHelper(DoCommandFlag flags, TileIndex tile, TileI bool had_success = false; CommandCost last_error = CMD_ERROR; for (;;) { - ret = remove ? Command::Do(flags, tile, TrackdirToTrack(trackdir)) : Command::Do(flags, tile, railtype, TrackdirToTrack(trackdir), auto_remove_signals); - TileIndex last_endtile = _rail_track_endtile; // CM + ret = remove ? Command::Do(flags, tile, TrackdirToTrack(trackdir)) : Command::Do(flags, tile, railtype, TrackdirToTrack(trackdir), auto_remove_signals); if (ret.Failed()) { last_error = ret; if (_rail_track_endtile == INVALID_TILE) _rail_track_endtile = last_endtile; // CM diff --git a/src/rail_gui.cpp b/src/rail_gui.cpp index 240b31a5..0951cf33 100644 --- a/src/rail_gui.cpp +++ b/src/rail_gui.cpp @@ -7,6 +7,7 @@ /** @file rail_gui.cpp %File for dealing with rail construction user interface */ +#include "citymania/cm_highlight_type.hpp" #include "stdafx.h" #include "gui.h" #include "window_gui.h" @@ -58,10 +59,11 @@ /* CityMania code end */ #include "safeguards.h" +#include RailType _cur_railtype; ///< Rail type of the current build-rail toolbar. -static bool _remove_button_clicked; ///< Flag whether 'remove' toggle-button is currently enabled +extern bool _remove_button_clicked; ///< Flag whether 'remove' toggle-button is currently enabled static bool _cm_invert_remove; ///< Invert remove mode on tools (when fn-clicked) /* CM static */ DiagDirection _build_depot_direction; ///< Currently selected depot direction static uint16_t _cur_waypoint_type; ///< Currently selected waypoint type @@ -79,15 +81,7 @@ static const int HOTKEY_BLUEPRINT_LOAD_END = 0x1030; static const int HOTKEY_BLUEPRINT_SAVE = 0x1030; static const int HOTKEY_BLUEPRINT_SAVE_END = 0x1040; -struct RailStationGUISettings { - Axis orientation; ///< Currently selected rail station orientation - - bool newstations; ///< Are custom station definitions available? - StationClassID station_class; ///< Currently selected custom station class (if newstations is \c true ) - uint16_t station_type; ///< %Station type within the currently selected custom station class (if newstations is \c true ) - uint16_t station_count; ///< Number of custom stations (if newstations is \c true ) -}; -RailStationGUISettings _railstation; ///< Settings of the station builder GUI +citymania::RailStationGUISettings _railstation; ///< Settings of the station builder GUI static void HandleStationPlacement(TileIndex start, TileIndex end); @@ -214,7 +208,7 @@ static void PlaceRail_Station(TileIndex tile) int h = _settings_client.gui.station_platlength; if (!_railstation.orientation) Swap(w, h); - RailStationGUISettings params = _railstation; + citymania::RailStationGUISettings params = _railstation; RailType rt = _cur_railtype; byte numtracks = _settings_client.gui.station_numtracks; byte platlength = _settings_client.gui.station_platlength; @@ -677,12 +671,12 @@ struct BuildRailToolbarWindow : Window { if (was_open) ResetObjectToPlace(); if (!was_open || dragdrop != _settings_client.gui.station_dragdrop) { _settings_client.gui.station_dragdrop = dragdrop; - if (HandlePlacePushButton(this, WID_RAT_BUILD_STATION, SPR_CURSOR_RAIL_STATION, HT_RECT, DDSP_BUILD_STATION)) + if (citymania::HandleStationPlacePushButton(this, WID_RAT_BUILD_STATION, std::make_shared())) ShowStationBuilder(this); } this->last_user_action = WID_RAT_BUILD_STATION; } else { /* button */ - if (HandlePlacePushButton(this, WID_RAT_BUILD_STATION, SPR_CURSOR_RAIL_STATION, HT_RECT, DDSP_BUILD_STATION)) { + if (citymania::HandleStationPlacePushButton(this, WID_RAT_BUILD_STATION, std::make_shared())) { ShowStationBuilder(this); this->last_user_action = WID_RAT_BUILD_STATION; } @@ -1038,7 +1032,7 @@ struct BuildRailToolbarWindow : Window { Hotkey((uint16)0, "cm_blueprint_save_7", HOTKEY_BLUEPRINT_SAVE + 7), Hotkey((uint16)0, "cm_blueprint_save_8", HOTKEY_BLUEPRINT_SAVE + 8), Hotkey((uint16)0, "cm_blueprint_save_9", HOTKEY_BLUEPRINT_SAVE + 9), - Hotkey(CM_WKC_MOUSE_MIDDLE, "cm_blueprint_rotate", HOTKEY_BLUEPRINT_ROTATE), + Hotkey(CM_WKC_MOUSE_MIDDLE, "cm_blueprint_rotate", HOTKEY_BLUEPRINT_ROTATE), }, RailToolbarGlobalHotkeys}; }; @@ -1131,7 +1125,7 @@ static void HandleStationPlacement(TileIndex start, TileIndex end) if (_railstation.orientation == AXIS_X) Swap(numtracks, platlength); - RailStationGUISettings params = _railstation; + citymania::RailStationGUISettings params = _railstation; RailType rt = _cur_railtype; bool adjacent = citymania::_fn_mod; diff --git a/src/road_gui.cpp b/src/road_gui.cpp index a880b5b7..df6c0c65 100644 --- a/src/road_gui.cpp +++ b/src/road_gui.cpp @@ -56,7 +56,7 @@ static void ShowRVStationPicker(Window *parent, RoadStopType rs); static void ShowRoadDepotPicker(Window *parent); -static bool _remove_button_clicked; +extern bool _remove_button_clicked; static bool _one_way_button_clicked; static Axis _place_road_dir; @@ -517,15 +517,16 @@ struct BuildRoadToolbarWindow : Window { } break; - case WID_ROT_BUS_STATION: - if (HandlePlacePushButton(this, WID_ROT_BUS_STATION, SPR_CURSOR_BUS_STATION, HT_RECT, DDSP_BUILD_BUSSTOP)) { + case WID_ROT_BUS_STATION: { + if (citymania::HandleStationPlacePushButton(this, WID_ROT_BUS_STATION, std::make_shared(ROADSTOP_BUS))) { ShowRVStationPicker(this, ROADSTOP_BUS); this->last_started_action = widget; } break; + } case WID_ROT_TRUCK_STATION: - if (HandlePlacePushButton(this, WID_ROT_TRUCK_STATION, SPR_CURSOR_TRUCK_STATION, HT_RECT, DDSP_BUILD_TRUCKSTOP)) { + if (citymania::HandleStationPlacePushButton(this, WID_ROT_BUS_STATION, std::make_shared(ROADSTOP_TRUCK))) { ShowRVStationPicker(this, ROADSTOP_TRUCK); this->last_started_action = widget; } diff --git a/src/station_gui.cpp b/src/station_gui.cpp index df74f5fa..62281c58 100644 --- a/src/station_gui.cpp +++ b/src/station_gui.cpp @@ -7,6 +7,7 @@ /** @file station_gui.cpp The GUI for stations. */ +#include "station_type.h" #include "stdafx.h" #include "debug.h" #include "gui.h" @@ -42,6 +43,7 @@ #include "citymania/cm_tooltips.hpp" #include "safeguards.h" +#include /** * Calculates and draws the accepted or supplied cargo around the selected tile(s) @@ -2202,6 +2204,7 @@ static WindowDesc _station_view_desc(__FILE__, __LINE__, */ void ShowStationViewWindow(StationID station) { + if (_ctrl_pressed) citymania::ShowCatchmentByClick(station); else AllocateWindowDescFront(&_station_view_desc, station); } @@ -2347,7 +2350,8 @@ struct SelectStationWindow : WindowPopup { void Close([[maybe_unused]] int data = 0) override { - SetViewportCatchmentSpecializedStation(nullptr, true); + if constexpr (std::is_same_v) SetViewportCatchmentSpecializedStation(nullptr, true); + else citymania::SetSelectedStationToJoin(INVALID_STATION); _thd.freeze = false; this->Window::Close(); @@ -2435,14 +2439,16 @@ struct SelectStationWindow : WindowPopup { void OnMouseOver([[maybe_unused]] Point pt, WidgetID widget) override { if (widget != WID_JS_PANEL) { - SetViewportCatchmentSpecializedStation(nullptr, true); + if constexpr (std::is_same_v) SetViewportCatchmentSpecializedStation(nullptr, true); + else citymania::SetSelectedStationToJoin(INVALID_STATION); return; } /* Show coverage area of station under cursor */ auto it = this->vscroll->GetScrolledItemFromWidget(_stations_nearby_list, pt.y, this, WID_JS_PANEL, WidgetDimensions::scaled.framerect.top); const T *st = it == _stations_nearby_list.end() || *it == NEW_STATION ? nullptr : T::Get(*it); - SetViewportCatchmentSpecializedStation(st, true); + if constexpr (std::is_same_v) SetViewportCatchmentSpecializedStation(st, true); + else citymania::SetSelectedStationToJoin(*it); } }; @@ -2478,7 +2484,6 @@ static bool StationJoinerNeeded(TileArea ta, const StationPickerCmdProc &proc) /* only show the popup, if we press ctrl */ if (!citymania::_fn_mod) return false; - /* Now check if we could build there */ if (!proc(true, INVALID_STATION)) return false; diff --git a/src/statusbar_gui.cpp b/src/statusbar_gui.cpp index d9e586dc..799f068a 100644 --- a/src/statusbar_gui.cpp +++ b/src/statusbar_gui.cpp @@ -282,6 +282,11 @@ void ShowStatusBar() } void CM_RedrawStatusBar() { - StatusBarWindow *w = dynamic_cast(FindWindowById(WC_STATUS_BAR, 0)); - w->ReInit(); + Window *w = FindWindowById(WC_STATUS_BAR, 0); + + if (w == nullptr) return; + + StatusBarWindow *st = static_cast(w); + + st->ReInit(); } diff --git a/src/tilehighlight_type.h b/src/tilehighlight_type.h index adcfdbd2..249ff634 100644 --- a/src/tilehighlight_type.h +++ b/src/tilehighlight_type.h @@ -10,6 +10,7 @@ #ifndef TILEHIGHLIGHT_TYPE_H #define TILEHIGHLIGHT_TYPE_H +#include "citymania/cm_command_type.hpp" #include "citymania/cm_highlight_type.hpp" #include "core/geometry_type.hpp" @@ -89,6 +90,7 @@ struct TileHighlightData { ViewportPlaceMethod select_method; ///< The method which governs how tiles are selected. ViewportDragDropSelectionProcess select_proc; ///< The procedure that has to be called when the selection is done. + // Old CM highilight citymania::ObjectHighlight cm; citymania::ObjectHighlight cm_new; bool cm_poly_terra; diff --git a/src/toolbar_gui.cpp b/src/toolbar_gui.cpp index c83a8235..92cde70e 100644 --- a/src/toolbar_gui.cpp +++ b/src/toolbar_gui.cpp @@ -1112,13 +1112,13 @@ static CallBackFunction ToolbarHelpClick(Window *w) { if (_settings_client.gui.newgrf_developer_tools) { PopupMainToolbarMenu(w, _game_mode == GM_EDITOR ? (WidgetID)WID_TE_HELP : (WidgetID)WID_TN_HELP, {STR_ABOUT_MENU_LAND_BLOCK_INFO, - CM_STR_ABOUT_MENU_LOGIN_WINDOW, STR_ABOUT_MENU_HELP, STR_NULL, STR_ABOUT_MENU_TOGGLE_CONSOLE, STR_ABOUT_MENU_AI_DEBUG, + CM_STR_LOGIN_WINDOW_CAPTION, STR_ABOUT_MENU_HELP, STR_NULL, STR_ABOUT_MENU_TOGGLE_CONSOLE, STR_ABOUT_MENU_AI_DEBUG, STR_ABOUT_MENU_SCREENSHOT, STR_ABOUT_MENU_SHOW_FRAMERATE, STR_ABOUT_MENU_ABOUT_OPENTTD, STR_ABOUT_MENU_SPRITE_ALIGNER, STR_ABOUT_MENU_TOGGLE_BOUNDING_BOXES, STR_ABOUT_MENU_TOGGLE_DIRTY_BLOCKS, STR_ABOUT_MENU_TOGGLE_WIDGET_OUTLINES}); } else { PopupMainToolbarMenu(w, _game_mode == GM_EDITOR ? (WidgetID)WID_TE_HELP : (WidgetID)WID_TN_HELP, {STR_ABOUT_MENU_LAND_BLOCK_INFO, - CM_STR_ABOUT_MENU_LOGIN_WINDOW, STR_ABOUT_MENU_HELP, STR_NULL, STR_ABOUT_MENU_TOGGLE_CONSOLE, STR_ABOUT_MENU_AI_DEBUG, + CM_STR_LOGIN_WINDOW_CAPTION, STR_ABOUT_MENU_HELP, STR_NULL, STR_ABOUT_MENU_TOGGLE_CONSOLE, STR_ABOUT_MENU_AI_DEBUG, STR_ABOUT_MENU_SCREENSHOT, STR_ABOUT_MENU_SHOW_FRAMERATE, STR_ABOUT_MENU_ABOUT_OPENTTD}); } return CBF_NONE; diff --git a/src/viewport.cpp b/src/viewport.cpp index dff44c7f..d356165e 100644 --- a/src/viewport.cpp +++ b/src/viewport.cpp @@ -2200,7 +2200,7 @@ void MarkTileDirtyByTile(TileIndex tile, int bridge_level_offset, int tile_heigh * * @ingroup dirty */ -static void SetSelectionTilesDirty() +/* CM static */ void SetSelectionTilesDirty() { int x_size = _thd.size.x; int y_size = _thd.size.y; @@ -2915,6 +2915,8 @@ void UpdateTileSelection() /* Draw the new tile selection? */ if ((new_drawstyle & HT_DRAG_MASK) != HT_NONE) SetSelectionTilesDirty(); } + + citymania::UpdateActivePreview(); } /** @@ -3993,6 +3995,7 @@ void SetObjectToPlaceWnd(CursorID icon, PaletteID pal, HighLightStyle mode, Wind */ void SetObjectToPlace(CursorID icon, PaletteID pal, HighLightStyle mode, WindowClass window_class, WindowNumber window_num, ViewportDragDropSelectionProcess cm_process) { + citymania::ResetActivePreview(); if (_thd.window_class != WC_INVALID) { /* Undo clicking on button and drag & drop */ Window *w = _thd.GetCallbackWnd(); @@ -4043,6 +4046,7 @@ void SetObjectToPlace(CursorID icon, PaletteID pal, HighLightStyle mode, WindowC void ResetObjectToPlace() { SetObjectToPlace(SPR_CURSOR_MOUSE, PAL_NONE, HT_NONE, WC_MAIN_WINDOW, 0, CM_DDSP_NONE); + citymania::ResetActivePreview(); } Point GetViewportStationMiddle(const Viewport *vp, const Station *st) diff --git a/src/widgets/company_widget.h b/src/widgets/company_widget.h index 45295b63..ceec34c8 100644 --- a/src/widgets/company_widget.h +++ b/src/widgets/company_widget.h @@ -52,11 +52,8 @@ enum CompanyWidgets : WidgetID { WID_C_COMPANY_PASSWORD, ///< Button to set company password. WID_C_COMPANY_JOIN, ///< Button to join company. - WID_C_SELECT_MOD, - WID_C_SELECT_MOD_JOIN, - WID_C_MOD_COMPANY_JOIN, - WID_C_MOD_COMPANY_RESET, - WID_C_MOD_COMPANY_TOGGLE_LOCK, + CM_WID_C_SELECT_ADMINBUTTONS, + CM_WID_C_ADMINBUTTONS, }; /** Widgets of the #CompanyFinancesWindow class. */ diff --git a/src/window.cpp b/src/window.cpp index 9576736f..ba2143ac 100644 --- a/src/window.cpp +++ b/src/window.cpp @@ -42,6 +42,7 @@ /* CityMania code start */ #include #include "window_gui.h" +#include "citymania/cm_highlight.hpp" #include "citymania/cm_hotkeys.hpp" #include "citymania/cm_overlays.hpp" #include "citymania/cm_tooltips.hpp" @@ -2811,6 +2812,7 @@ static void MouseLoop(MouseClick click, int mousewheel) if (HandleActiveWidget() == ES_HANDLED) return; if (HandleViewportScroll() == ES_HANDLED) return; + if (citymania::HandleMouseMove()) return; HandleMouseOver(); bool scrollwheel_scrolling = _settings_client.gui.scrollwheel_scrolling == 1 && (_cursor.v_wheel != 0 || _cursor.h_wheel != 0); @@ -2845,6 +2847,7 @@ static void MouseLoop(MouseClick click, int mousewheel) switch (click) { case MC_DOUBLE_LEFT: case MC_LEFT: + if (citymania::HandleMouseClick(vp, click == MC_DOUBLE_LEFT)) return; if (HandleViewportClicked(vp, x, y, click == MC_DOUBLE_LEFT)) return; if (!(w->flags & WF_DISABLE_VP_SCROLL) && _settings_client.gui.scroll_mode == VSM_MAP_LMB) { diff --git a/src/window_type.h b/src/window_type.h index 8e1269c5..d0250a26 100644 --- a/src/window_type.h +++ b/src/window_type.h @@ -732,6 +732,9 @@ enum WindowClass { CM_WC_COMMAND_TOOLBAR, CM_WC_LOGIN_WINDOW, CM_WC_CB_TOWN, + CM_WC_ADMIN_COMPANY_BUTTONS, + CM_WC_SERVER_BUTTONS, + CM_LAST_SERVER, WC_INVALID = 0xFFFF, ///< Invalid window. };