| 1 | /* (c) Magnus Auvinen. See licence.txt in the root of the distribution for more information. */ |
| 2 | /* If you are missing that file, acquire a complete release at teeworlds.com. */ |
| 3 | #include "countryflags.h" |
| 4 | #include "menus.h" |
| 5 | #include "skins.h" |
| 6 | |
| 7 | #include <base/log.h> |
| 8 | #include <base/math.h> |
| 9 | #include <base/system.h> |
| 10 | |
| 11 | #include <engine/graphics.h> |
| 12 | #include <engine/shared/config.h> |
| 13 | #include <engine/shared/localization.h> |
| 14 | #include <engine/shared/protocol7.h> |
| 15 | #include <engine/storage.h> |
| 16 | #include <engine/textrender.h> |
| 17 | #include <engine/updater.h> |
| 18 | |
| 19 | #include <generated/protocol.h> |
| 20 | |
| 21 | #include <game/client/animstate.h> |
| 22 | #include <game/client/components/chat.h> |
| 23 | #include <game/client/components/menu_background.h> |
| 24 | #include <game/client/components/sounds.h> |
| 25 | #include <game/client/gameclient.h> |
| 26 | #include <game/client/skin.h> |
| 27 | #include <game/client/ui.h> |
| 28 | #include <game/client/ui_listbox.h> |
| 29 | #include <game/client/ui_scrollregion.h> |
| 30 | #include <game/localization.h> |
| 31 | |
| 32 | #include <array> |
| 33 | #include <chrono> |
| 34 | #include <memory> |
| 35 | #include <numeric> |
| 36 | #include <string> |
| 37 | #include <vector> |
| 38 | |
| 39 | using namespace FontIcons; |
| 40 | using namespace std::chrono_literals; |
| 41 | |
| 42 | void CMenus::(CUIRect MainView) |
| 43 | { |
| 44 | char aBuf[128 + IO_MAX_PATH_LENGTH]; |
| 45 | CUIRect Label, Button, Left, Right, Game, ClientSettings; |
| 46 | MainView.HSplitTop(Cut: 150.0f, pTop: &Game, pBottom: &ClientSettings); |
| 47 | |
| 48 | // game |
| 49 | { |
| 50 | // headline |
| 51 | Game.HSplitTop(Cut: 30.0f, pTop: &Label, pBottom: &Game); |
| 52 | Ui()->DoLabel(pRect: &Label, pText: Localize(pStr: "Game" ), Size: 20.0f, Align: TEXTALIGN_ML); |
| 53 | Game.HSplitTop(Cut: 5.0f, pTop: nullptr, pBottom: &Game); |
| 54 | Game.VSplitMid(pLeft: &Left, pRight: nullptr, Spacing: 20.0f); |
| 55 | |
| 56 | // dynamic camera |
| 57 | Left.HSplitTop(Cut: 20.0f, pTop: &Button, pBottom: &Left); |
| 58 | const bool IsDyncam = g_Config.m_ClDyncam || g_Config.m_ClMouseFollowfactor > 0; |
| 59 | if(DoButton_CheckBox(pId: &g_Config.m_ClDyncam, pText: Localize(pStr: "Dynamic Camera" ), Checked: IsDyncam, pRect: &Button)) |
| 60 | { |
| 61 | if(IsDyncam) |
| 62 | { |
| 63 | g_Config.m_ClDyncam = 0; |
| 64 | g_Config.m_ClMouseFollowfactor = 0; |
| 65 | } |
| 66 | else |
| 67 | { |
| 68 | g_Config.m_ClDyncam = 1; |
| 69 | } |
| 70 | } |
| 71 | |
| 72 | // smooth dynamic camera |
| 73 | Left.HSplitTop(Cut: 5.0f, pTop: nullptr, pBottom: &Left); |
| 74 | Left.HSplitTop(Cut: 20.0f, pTop: &Button, pBottom: &Left); |
| 75 | if(g_Config.m_ClDyncam) |
| 76 | { |
| 77 | if(DoButton_CheckBox(pId: &g_Config.m_ClDyncamSmoothness, pText: Localize(pStr: "Smooth Dynamic Camera" ), Checked: g_Config.m_ClDyncamSmoothness, pRect: &Button)) |
| 78 | { |
| 79 | if(g_Config.m_ClDyncamSmoothness) |
| 80 | { |
| 81 | g_Config.m_ClDyncamSmoothness = 0; |
| 82 | } |
| 83 | else |
| 84 | { |
| 85 | g_Config.m_ClDyncamSmoothness = 50; |
| 86 | g_Config.m_ClDyncamStabilizing = 50; |
| 87 | } |
| 88 | } |
| 89 | } |
| 90 | |
| 91 | // weapon pickup |
| 92 | Left.HSplitTop(Cut: 5.0f, pTop: nullptr, pBottom: &Left); |
| 93 | Left.HSplitTop(Cut: 20.0f, pTop: &Button, pBottom: &Left); |
| 94 | if(DoButton_CheckBox(pId: &g_Config.m_ClAutoswitchWeapons, pText: Localize(pStr: "Switch weapon on pickup" ), Checked: g_Config.m_ClAutoswitchWeapons, pRect: &Button)) |
| 95 | g_Config.m_ClAutoswitchWeapons ^= 1; |
| 96 | |
| 97 | // weapon out of ammo autoswitch |
| 98 | Left.HSplitTop(Cut: 5.0f, pTop: nullptr, pBottom: &Left); |
| 99 | Left.HSplitTop(Cut: 20.0f, pTop: &Button, pBottom: &Left); |
| 100 | if(DoButton_CheckBox(pId: &g_Config.m_ClAutoswitchWeaponsOutOfAmmo, pText: Localize(pStr: "Switch weapon when out of ammo" ), Checked: g_Config.m_ClAutoswitchWeaponsOutOfAmmo, pRect: &Button)) |
| 101 | g_Config.m_ClAutoswitchWeaponsOutOfAmmo ^= 1; |
| 102 | } |
| 103 | |
| 104 | // client |
| 105 | { |
| 106 | // headline |
| 107 | ClientSettings.HSplitTop(Cut: 30.0f, pTop: &Label, pBottom: &ClientSettings); |
| 108 | Ui()->DoLabel(pRect: &Label, pText: Localize(pStr: "Client" ), Size: 20.0f, Align: TEXTALIGN_ML); |
| 109 | ClientSettings.HSplitTop(Cut: 5.0f, pTop: nullptr, pBottom: &ClientSettings); |
| 110 | ClientSettings.VSplitMid(pLeft: &Left, pRight: &Right, Spacing: 20.0f); |
| 111 | |
| 112 | // skip main menu |
| 113 | Left.HSplitTop(Cut: 20.0f, pTop: &Button, pBottom: &Left); |
| 114 | if(DoButton_CheckBox(pId: &g_Config.m_ClSkipStartMenu, pText: Localize(pStr: "Skip the main menu" ), Checked: g_Config.m_ClSkipStartMenu, pRect: &Button)) |
| 115 | g_Config.m_ClSkipStartMenu ^= 1; |
| 116 | |
| 117 | Left.HSplitTop(Cut: 10.0f, pTop: nullptr, pBottom: &Left); |
| 118 | Left.HSplitTop(Cut: 20.0f, pTop: &Button, pBottom: &Left); |
| 119 | str_copy(dst&: aBuf, src: " " ); |
| 120 | str_append(dst&: aBuf, src: Localize(pStr: "Hz" , pContext: "Hertz" )); |
| 121 | Ui()->DoScrollbarOption(pId: &g_Config.m_ClRefreshRate, pOption: &g_Config.m_ClRefreshRate, pRect: &Button, pStr: Localize(pStr: "Refresh Rate" ), Min: 10, Max: 1000, pScale: &CUi::ms_LinearScrollbarScale, Flags: CUi::SCROLLBAR_OPTION_INFINITE | CUi::SCROLLBAR_OPTION_NOCLAMPVALUE | CUi::SCROLLBAR_OPTION_DELAYUPDATE, pSuffix: aBuf); |
| 122 | Left.HSplitTop(Cut: 5.0f, pTop: nullptr, pBottom: &Left); |
| 123 | Left.HSplitTop(Cut: 20.0f, pTop: &Button, pBottom: &Left); |
| 124 | static int s_LowerRefreshRate; |
| 125 | if(DoButton_CheckBox(pId: &s_LowerRefreshRate, pText: Localize(pStr: "Save power by lowering refresh rate (higher input latency)" ), Checked: g_Config.m_ClRefreshRate <= 480 && g_Config.m_ClRefreshRate != 0, pRect: &Button)) |
| 126 | g_Config.m_ClRefreshRate = g_Config.m_ClRefreshRate > 480 || g_Config.m_ClRefreshRate == 0 ? 480 : 0; |
| 127 | |
| 128 | CUIRect SettingsButton; |
| 129 | Left.HSplitBottom(Cut: 20.0f, pTop: &Left, pBottom: &SettingsButton); |
| 130 | Left.HSplitBottom(Cut: 5.0f, pTop: &Left, pBottom: nullptr); |
| 131 | static CButtonContainer s_SettingsButtonId; |
| 132 | if(DoButton_Menu(pButtonContainer: &s_SettingsButtonId, pText: Localize(pStr: "Settings file" ), Checked: 0, pRect: &SettingsButton)) |
| 133 | { |
| 134 | Storage()->GetCompletePath(Type: IStorage::TYPE_SAVE, CONFIG_FILE, pBuffer: aBuf, BufferSize: sizeof(aBuf)); |
| 135 | Client()->ViewFile(pFilename: aBuf); |
| 136 | } |
| 137 | GameClient()->m_Tooltips.DoToolTip(pId: &s_SettingsButtonId, pNearRect: &SettingsButton, pText: Localize(pStr: "Open the settings file" )); |
| 138 | |
| 139 | CUIRect SavesButton; |
| 140 | Left.HSplitBottom(Cut: 20.0f, pTop: &Left, pBottom: &SavesButton); |
| 141 | Left.HSplitBottom(Cut: 5.0f, pTop: &Left, pBottom: nullptr); |
| 142 | static CButtonContainer s_SavesButtonId; |
| 143 | if(DoButton_Menu(pButtonContainer: &s_SavesButtonId, pText: Localize(pStr: "Saves file" ), Checked: 0, pRect: &SavesButton)) |
| 144 | { |
| 145 | Storage()->GetCompletePath(Type: IStorage::TYPE_SAVE, pDir: SAVES_FILE, pBuffer: aBuf, BufferSize: sizeof(aBuf)); |
| 146 | Client()->ViewFile(pFilename: aBuf); |
| 147 | } |
| 148 | GameClient()->m_Tooltips.DoToolTip(pId: &s_SavesButtonId, pNearRect: &SavesButton, pText: Localize(pStr: "Open the saves file" )); |
| 149 | |
| 150 | CUIRect ConfigButton; |
| 151 | Left.HSplitBottom(Cut: 20.0f, pTop: &Left, pBottom: &ConfigButton); |
| 152 | Left.HSplitBottom(Cut: 5.0f, pTop: &Left, pBottom: nullptr); |
| 153 | static CButtonContainer s_ConfigButtonId; |
| 154 | if(DoButton_Menu(pButtonContainer: &s_ConfigButtonId, pText: Localize(pStr: "Config directory" ), Checked: 0, pRect: &ConfigButton)) |
| 155 | { |
| 156 | Storage()->GetCompletePath(Type: IStorage::TYPE_SAVE, pDir: "" , pBuffer: aBuf, BufferSize: sizeof(aBuf)); |
| 157 | Client()->ViewFile(pFilename: aBuf); |
| 158 | } |
| 159 | GameClient()->m_Tooltips.DoToolTip(pId: &s_ConfigButtonId, pNearRect: &ConfigButton, pText: Localize(pStr: "Open the directory that contains the configuration and user files" )); |
| 160 | |
| 161 | CUIRect DirectoryButton; |
| 162 | Left.HSplitBottom(Cut: 20.0f, pTop: &Left, pBottom: &DirectoryButton); |
| 163 | Left.HSplitBottom(Cut: 5.0f, pTop: &Left, pBottom: nullptr); |
| 164 | static CButtonContainer s_ThemesButtonId; |
| 165 | if(DoButton_Menu(pButtonContainer: &s_ThemesButtonId, pText: Localize(pStr: "Themes directory" ), Checked: 0, pRect: &DirectoryButton)) |
| 166 | { |
| 167 | Storage()->GetCompletePath(Type: IStorage::TYPE_SAVE, pDir: "themes" , pBuffer: aBuf, BufferSize: sizeof(aBuf)); |
| 168 | Storage()->CreateFolder(pFoldername: "themes" , Type: IStorage::TYPE_SAVE); |
| 169 | Client()->ViewFile(pFilename: aBuf); |
| 170 | } |
| 171 | GameClient()->m_Tooltips.DoToolTip(pId: &s_ThemesButtonId, pNearRect: &DirectoryButton, pText: Localize(pStr: "Open the directory to add custom themes" )); |
| 172 | |
| 173 | Left.HSplitTop(Cut: 20.0f, pTop: nullptr, pBottom: &Left); |
| 174 | RenderThemeSelection(MainView: Left); |
| 175 | |
| 176 | // auto demo settings |
| 177 | { |
| 178 | Right.HSplitTop(Cut: 40.0f, pTop: nullptr, pBottom: &Right); |
| 179 | Right.HSplitTop(Cut: 20.0f, pTop: &Button, pBottom: &Right); |
| 180 | if(DoButton_CheckBox(pId: &g_Config.m_ClAutoDemoRecord, pText: Localize(pStr: "Automatically record demos" ), Checked: g_Config.m_ClAutoDemoRecord, pRect: &Button)) |
| 181 | g_Config.m_ClAutoDemoRecord ^= 1; |
| 182 | |
| 183 | Right.HSplitTop(Cut: 2 * 20.0f, pTop: &Button, pBottom: &Right); |
| 184 | if(g_Config.m_ClAutoDemoRecord) |
| 185 | Ui()->DoScrollbarOption(pId: &g_Config.m_ClAutoDemoMax, pOption: &g_Config.m_ClAutoDemoMax, pRect: &Button, pStr: Localize(pStr: "Max demos" ), Min: 1, Max: 1000, pScale: &CUi::ms_LinearScrollbarScale, Flags: CUi::SCROLLBAR_OPTION_INFINITE | CUi::SCROLLBAR_OPTION_MULTILINE); |
| 186 | |
| 187 | Right.HSplitTop(Cut: 10.0f, pTop: nullptr, pBottom: &Right); |
| 188 | Right.HSplitTop(Cut: 20.0f, pTop: &Button, pBottom: &Right); |
| 189 | if(DoButton_CheckBox(pId: &g_Config.m_ClAutoScreenshot, pText: Localize(pStr: "Automatically take game over screenshot" ), Checked: g_Config.m_ClAutoScreenshot, pRect: &Button)) |
| 190 | g_Config.m_ClAutoScreenshot ^= 1; |
| 191 | |
| 192 | Right.HSplitTop(Cut: 2 * 20.0f, pTop: &Button, pBottom: &Right); |
| 193 | if(g_Config.m_ClAutoScreenshot) |
| 194 | Ui()->DoScrollbarOption(pId: &g_Config.m_ClAutoScreenshotMax, pOption: &g_Config.m_ClAutoScreenshotMax, pRect: &Button, pStr: Localize(pStr: "Max Screenshots" ), Min: 1, Max: 1000, pScale: &CUi::ms_LinearScrollbarScale, Flags: CUi::SCROLLBAR_OPTION_INFINITE | CUi::SCROLLBAR_OPTION_MULTILINE); |
| 195 | } |
| 196 | |
| 197 | // auto statboard screenshot |
| 198 | { |
| 199 | Right.HSplitTop(Cut: 10.0f, pTop: nullptr, pBottom: &Right); |
| 200 | Right.HSplitTop(Cut: 20.0f, pTop: &Button, pBottom: &Right); |
| 201 | if(DoButton_CheckBox(pId: &g_Config.m_ClAutoStatboardScreenshot, pText: Localize(pStr: "Automatically take statboard screenshot" ), Checked: g_Config.m_ClAutoStatboardScreenshot, pRect: &Button)) |
| 202 | { |
| 203 | g_Config.m_ClAutoStatboardScreenshot ^= 1; |
| 204 | } |
| 205 | |
| 206 | Right.HSplitTop(Cut: 2 * 20.0f, pTop: &Button, pBottom: &Right); |
| 207 | if(g_Config.m_ClAutoStatboardScreenshot) |
| 208 | Ui()->DoScrollbarOption(pId: &g_Config.m_ClAutoStatboardScreenshotMax, pOption: &g_Config.m_ClAutoStatboardScreenshotMax, pRect: &Button, pStr: Localize(pStr: "Max Screenshots" ), Min: 1, Max: 1000, pScale: &CUi::ms_LinearScrollbarScale, Flags: CUi::SCROLLBAR_OPTION_INFINITE | CUi::SCROLLBAR_OPTION_MULTILINE); |
| 209 | } |
| 210 | |
| 211 | // auto statboard csv |
| 212 | { |
| 213 | Right.HSplitTop(Cut: 10.0f, pTop: nullptr, pBottom: &Right); |
| 214 | Right.HSplitTop(Cut: 20.0f, pTop: &Button, pBottom: &Right); |
| 215 | if(DoButton_CheckBox(pId: &g_Config.m_ClAutoCSV, pText: Localize(pStr: "Automatically create statboard csv" ), Checked: g_Config.m_ClAutoCSV, pRect: &Button)) |
| 216 | { |
| 217 | g_Config.m_ClAutoCSV ^= 1; |
| 218 | } |
| 219 | |
| 220 | Right.HSplitTop(Cut: 2 * 20.0f, pTop: &Button, pBottom: &Right); |
| 221 | if(g_Config.m_ClAutoCSV) |
| 222 | Ui()->DoScrollbarOption(pId: &g_Config.m_ClAutoCSVMax, pOption: &g_Config.m_ClAutoCSVMax, pRect: &Button, pStr: Localize(pStr: "Max CSVs" ), Min: 1, Max: 1000, pScale: &CUi::ms_LinearScrollbarScale, Flags: CUi::SCROLLBAR_OPTION_INFINITE | CUi::SCROLLBAR_OPTION_MULTILINE); |
| 223 | } |
| 224 | } |
| 225 | } |
| 226 | |
| 227 | void CMenus::() |
| 228 | { |
| 229 | if(m_Dummy) |
| 230 | m_NeedSendDummyinfo = true; |
| 231 | else |
| 232 | m_NeedSendinfo = true; |
| 233 | } |
| 234 | |
| 235 | void CMenus::(CUIRect MainView) |
| 236 | { |
| 237 | CUIRect TabBar, PlayerTab, DummyTab, ChangeInfo, QuickSearch; |
| 238 | MainView.HSplitTop(Cut: 20.0f, pTop: &TabBar, pBottom: &MainView); |
| 239 | TabBar.VSplitMid(pLeft: &TabBar, pRight: &ChangeInfo, Spacing: 20.f); |
| 240 | TabBar.VSplitMid(pLeft: &PlayerTab, pRight: &DummyTab); |
| 241 | MainView.HSplitTop(Cut: 10.0f, pTop: nullptr, pBottom: &MainView); |
| 242 | |
| 243 | static CButtonContainer s_PlayerTabButton; |
| 244 | if(DoButton_MenuTab(pButtonContainer: &s_PlayerTabButton, pText: Localize(pStr: "Player" ), Checked: !m_Dummy, pRect: &PlayerTab, Corners: IGraphics::CORNER_L, pAnimator: nullptr, pDefaultColor: nullptr, pActiveColor: nullptr, pHoverColor: nullptr, EdgeRounding: 4.0f)) |
| 245 | { |
| 246 | m_Dummy = false; |
| 247 | } |
| 248 | |
| 249 | static CButtonContainer s_DummyTabButton; |
| 250 | if(DoButton_MenuTab(pButtonContainer: &s_DummyTabButton, pText: Localize(pStr: "Dummy" ), Checked: m_Dummy, pRect: &DummyTab, Corners: IGraphics::CORNER_R, pAnimator: nullptr, pDefaultColor: nullptr, pActiveColor: nullptr, pHoverColor: nullptr, EdgeRounding: 4.0f)) |
| 251 | { |
| 252 | m_Dummy = true; |
| 253 | } |
| 254 | |
| 255 | if(Client()->State() == IClient::STATE_ONLINE && |
| 256 | GameClient()->m_aNextChangeInfo[m_Dummy] > Client()->GameTick(Conn: m_Dummy)) |
| 257 | { |
| 258 | char aChangeInfo[128], aTimeLeft[32]; |
| 259 | str_format(buffer: aTimeLeft, buffer_size: sizeof(aTimeLeft), format: Localize(pStr: "%ds left" ), (GameClient()->m_aNextChangeInfo[m_Dummy] - Client()->GameTick(Conn: m_Dummy) + Client()->GameTickSpeed() - 1) / Client()->GameTickSpeed()); |
| 260 | str_format(buffer: aChangeInfo, buffer_size: sizeof(aChangeInfo), format: "%s: %s" , Localize(pStr: "Player info change cooldown" ), aTimeLeft); |
| 261 | Ui()->DoLabel(pRect: &ChangeInfo, pText: aChangeInfo, Size: 10.f, Align: TEXTALIGN_ML); |
| 262 | } |
| 263 | |
| 264 | static CLineInput s_NameInput; |
| 265 | static CLineInput s_ClanInput; |
| 266 | |
| 267 | int *pCountry; |
| 268 | if(!m_Dummy) |
| 269 | { |
| 270 | pCountry = &g_Config.m_PlayerCountry; |
| 271 | s_NameInput.SetBuffer(pStr: g_Config.m_PlayerName, MaxSize: sizeof(g_Config.m_PlayerName)); |
| 272 | s_NameInput.SetEmptyText(Client()->PlayerName()); |
| 273 | s_ClanInput.SetBuffer(pStr: g_Config.m_PlayerClan, MaxSize: sizeof(g_Config.m_PlayerClan)); |
| 274 | } |
| 275 | else |
| 276 | { |
| 277 | pCountry = &g_Config.m_ClDummyCountry; |
| 278 | s_NameInput.SetBuffer(pStr: g_Config.m_ClDummyName, MaxSize: sizeof(g_Config.m_ClDummyName)); |
| 279 | s_NameInput.SetEmptyText(Client()->DummyName()); |
| 280 | s_ClanInput.SetBuffer(pStr: g_Config.m_ClDummyClan, MaxSize: sizeof(g_Config.m_ClDummyClan)); |
| 281 | } |
| 282 | |
| 283 | // player name |
| 284 | CUIRect Button, Label; |
| 285 | MainView.HSplitTop(Cut: 20.0f, pTop: &Button, pBottom: &MainView); |
| 286 | Button.VSplitLeft(Cut: 80.0f, pLeft: &Label, pRight: &Button); |
| 287 | Button.VSplitLeft(Cut: 150.0f, pLeft: &Button, pRight: nullptr); |
| 288 | char aBuf[128]; |
| 289 | str_format(buffer: aBuf, buffer_size: sizeof(aBuf), format: "%s:" , Localize(pStr: "Name" )); |
| 290 | Ui()->DoLabel(pRect: &Label, pText: aBuf, Size: 14.0f, Align: TEXTALIGN_ML); |
| 291 | if(Ui()->DoEditBox(pLineInput: &s_NameInput, pRect: &Button, FontSize: 14.0f)) |
| 292 | { |
| 293 | SetNeedSendInfo(); |
| 294 | } |
| 295 | |
| 296 | // player clan |
| 297 | MainView.HSplitTop(Cut: 5.0f, pTop: nullptr, pBottom: &MainView); |
| 298 | MainView.HSplitTop(Cut: 20.0f, pTop: &Button, pBottom: &MainView); |
| 299 | Button.VSplitLeft(Cut: 80.0f, pLeft: &Label, pRight: &Button); |
| 300 | Button.VSplitLeft(Cut: 150.0f, pLeft: &Button, pRight: nullptr); |
| 301 | str_format(buffer: aBuf, buffer_size: sizeof(aBuf), format: "%s:" , Localize(pStr: "Clan" )); |
| 302 | Ui()->DoLabel(pRect: &Label, pText: aBuf, Size: 14.0f, Align: TEXTALIGN_ML); |
| 303 | if(Ui()->DoEditBox(pLineInput: &s_ClanInput, pRect: &Button, FontSize: 14.0f)) |
| 304 | { |
| 305 | SetNeedSendInfo(); |
| 306 | } |
| 307 | |
| 308 | // country flag selector |
| 309 | static CLineInputBuffered<25> s_FlagFilterInput; |
| 310 | |
| 311 | std::vector<const CCountryFlags::CCountryFlag *> vpFilteredFlags; |
| 312 | for(size_t i = 0; i < GameClient()->m_CountryFlags.Num(); ++i) |
| 313 | { |
| 314 | const CCountryFlags::CCountryFlag &Entry = GameClient()->m_CountryFlags.GetByIndex(Index: i); |
| 315 | if(!str_find_nocase(haystack: Entry.m_aCountryCodeString, needle: s_FlagFilterInput.GetString())) |
| 316 | continue; |
| 317 | vpFilteredFlags.push_back(x: &Entry); |
| 318 | } |
| 319 | |
| 320 | MainView.HSplitTop(Cut: 10.0f, pTop: nullptr, pBottom: &MainView); |
| 321 | MainView.HSplitBottom(Cut: 20.0f, pTop: &MainView, pBottom: &QuickSearch); |
| 322 | MainView.HSplitBottom(Cut: 5.0f, pTop: &MainView, pBottom: nullptr); |
| 323 | QuickSearch.VSplitLeft(Cut: 220.0f, pLeft: &QuickSearch, pRight: nullptr); |
| 324 | |
| 325 | int OldSelected = -1; |
| 326 | static CListBox s_ListBox; |
| 327 | s_ListBox.DoStart(RowHeight: 48.0f, NumItems: vpFilteredFlags.size(), ItemsPerRow: 10, RowsPerScroll: 3, SelectedIndex: OldSelected, pRect: &MainView); |
| 328 | |
| 329 | for(size_t i = 0; i < vpFilteredFlags.size(); i++) |
| 330 | { |
| 331 | const CCountryFlags::CCountryFlag *pEntry = vpFilteredFlags[i]; |
| 332 | |
| 333 | if(pEntry->m_CountryCode == *pCountry) |
| 334 | OldSelected = i; |
| 335 | |
| 336 | const CListboxItem Item = s_ListBox.DoNextItem(pId: &pEntry->m_CountryCode, Selected: OldSelected >= 0 && (size_t)OldSelected == i); |
| 337 | if(!Item.m_Visible) |
| 338 | continue; |
| 339 | |
| 340 | CUIRect FlagRect; |
| 341 | Item.m_Rect.Margin(Cut: 5.0f, pOtherRect: &FlagRect); |
| 342 | FlagRect.HSplitBottom(Cut: 12.0f, pTop: &FlagRect, pBottom: &Label); |
| 343 | Label.HSplitTop(Cut: 2.0f, pTop: nullptr, pBottom: &Label); |
| 344 | const float OldWidth = FlagRect.w; |
| 345 | FlagRect.w = FlagRect.h * 2; |
| 346 | FlagRect.x += (OldWidth - FlagRect.w) / 2.0f; |
| 347 | GameClient()->m_CountryFlags.Render(CountryCode: pEntry->m_CountryCode, Color: ColorRGBA(1.0f, 1.0f, 1.0f, 1.0f), x: FlagRect.x, y: FlagRect.y, w: FlagRect.w, h: FlagRect.h); |
| 348 | |
| 349 | if(pEntry->m_Texture.IsValid()) |
| 350 | { |
| 351 | Ui()->DoLabel(pRect: &Label, pText: pEntry->m_aCountryCodeString, Size: 10.0f, Align: TEXTALIGN_MC); |
| 352 | } |
| 353 | } |
| 354 | |
| 355 | const int NewSelected = s_ListBox.DoEnd(); |
| 356 | if(OldSelected != NewSelected) |
| 357 | { |
| 358 | *pCountry = vpFilteredFlags[NewSelected]->m_CountryCode; |
| 359 | SetNeedSendInfo(); |
| 360 | } |
| 361 | |
| 362 | Ui()->DoEditBox_Search(pLineInput: &s_FlagFilterInput, pRect: &QuickSearch, FontSize: 14.0f, HotkeyEnabled: !Ui()->IsPopupOpen() && !GameClient()->m_GameConsole.IsActive()); |
| 363 | } |
| 364 | |
| 365 | void CMenus::(CUIRect MainView) |
| 366 | { |
| 367 | CUIRect TabBar, PlayerTab, DummyTab, ChangeInfo; |
| 368 | MainView.HSplitTop(Cut: 20.0f, pTop: &TabBar, pBottom: &MainView); |
| 369 | TabBar.VSplitMid(pLeft: &TabBar, pRight: &ChangeInfo, Spacing: 20.f); |
| 370 | TabBar.VSplitMid(pLeft: &PlayerTab, pRight: &DummyTab); |
| 371 | MainView.HSplitTop(Cut: 10.0f, pTop: nullptr, pBottom: &MainView); |
| 372 | |
| 373 | static CButtonContainer s_PlayerTabButton; |
| 374 | if(DoButton_MenuTab(pButtonContainer: &s_PlayerTabButton, pText: Localize(pStr: "Player" ), Checked: !m_Dummy, pRect: &PlayerTab, Corners: IGraphics::CORNER_L, pAnimator: nullptr, pDefaultColor: nullptr, pActiveColor: nullptr, pHoverColor: nullptr, EdgeRounding: 4.0f)) |
| 375 | { |
| 376 | m_Dummy = false; |
| 377 | m_SkinListScrollToSelected = true; |
| 378 | } |
| 379 | |
| 380 | static CButtonContainer s_DummyTabButton; |
| 381 | if(DoButton_MenuTab(pButtonContainer: &s_DummyTabButton, pText: Localize(pStr: "Dummy" ), Checked: m_Dummy, pRect: &DummyTab, Corners: IGraphics::CORNER_R, pAnimator: nullptr, pDefaultColor: nullptr, pActiveColor: nullptr, pHoverColor: nullptr, EdgeRounding: 4.0f)) |
| 382 | { |
| 383 | m_Dummy = true; |
| 384 | m_SkinListScrollToSelected = true; |
| 385 | } |
| 386 | |
| 387 | if(Client()->State() == IClient::STATE_ONLINE && |
| 388 | GameClient()->m_aNextChangeInfo[m_Dummy] > Client()->GameTick(Conn: m_Dummy)) |
| 389 | { |
| 390 | char aChangeInfo[128], aTimeLeft[32]; |
| 391 | str_format(buffer: aTimeLeft, buffer_size: sizeof(aTimeLeft), format: Localize(pStr: "%ds left" ), (GameClient()->m_aNextChangeInfo[m_Dummy] - Client()->GameTick(Conn: m_Dummy) + Client()->GameTickSpeed() - 1) / Client()->GameTickSpeed()); |
| 392 | str_format(buffer: aChangeInfo, buffer_size: sizeof(aChangeInfo), format: "%s: %s" , Localize(pStr: "Player info change cooldown" ), aTimeLeft); |
| 393 | Ui()->DoLabel(pRect: &ChangeInfo, pText: aChangeInfo, Size: 10.f, Align: TEXTALIGN_ML); |
| 394 | } |
| 395 | |
| 396 | if(g_Config.m_Debug) |
| 397 | { |
| 398 | const CSkins::CSkinLoadingStats Stats = GameClient()->m_Skins.LoadingStats(); |
| 399 | char aStats[256]; |
| 400 | str_format(buffer: aStats, buffer_size: sizeof(aStats), format: "unloaded: %" PRIzu ", pending: %" PRIzu ", loading: %" PRIzu ",\nloaded: %" PRIzu ", error: %" PRIzu ", notfound: %" PRIzu, |
| 401 | Stats.m_NumUnloaded, Stats.m_NumPending, Stats.m_NumLoading, Stats.m_NumLoaded, Stats.m_NumError, Stats.m_NumNotFound); |
| 402 | Ui()->DoLabel(pRect: &ChangeInfo, pText: aStats, Size: 9.0f, Align: TEXTALIGN_MR); |
| 403 | } |
| 404 | |
| 405 | char *pSkinName; |
| 406 | size_t SkinNameSize; |
| 407 | int *pUseCustomColor; |
| 408 | unsigned *pColorBody; |
| 409 | unsigned *pColorFeet; |
| 410 | int *pEmote; |
| 411 | if(!m_Dummy) |
| 412 | { |
| 413 | pSkinName = g_Config.m_ClPlayerSkin; |
| 414 | SkinNameSize = sizeof(g_Config.m_ClPlayerSkin); |
| 415 | pUseCustomColor = &g_Config.m_ClPlayerUseCustomColor; |
| 416 | pColorBody = &g_Config.m_ClPlayerColorBody; |
| 417 | pColorFeet = &g_Config.m_ClPlayerColorFeet; |
| 418 | pEmote = &g_Config.m_ClPlayerDefaultEyes; |
| 419 | } |
| 420 | else |
| 421 | { |
| 422 | pSkinName = g_Config.m_ClDummySkin; |
| 423 | SkinNameSize = sizeof(g_Config.m_ClDummySkin); |
| 424 | pUseCustomColor = &g_Config.m_ClDummyUseCustomColor; |
| 425 | pColorBody = &g_Config.m_ClDummyColorBody; |
| 426 | pColorFeet = &g_Config.m_ClDummyColorFeet; |
| 427 | pEmote = &g_Config.m_ClDummyDefaultEyes; |
| 428 | } |
| 429 | |
| 430 | const float EyeButtonSize = 40.0f; |
| 431 | const bool RenderEyesBelow = MainView.w < 750.0f; |
| 432 | CUIRect YourSkin, Checkboxes, SkinPrefix, Eyes, Button, Label; |
| 433 | MainView.HSplitTop(Cut: 90.0f, pTop: &YourSkin, pBottom: &MainView); |
| 434 | if(RenderEyesBelow) |
| 435 | { |
| 436 | YourSkin.VSplitLeft(Cut: MainView.w * 0.45f, pLeft: &YourSkin, pRight: &Checkboxes); |
| 437 | Checkboxes.VSplitLeft(Cut: MainView.w * 0.35f, pLeft: &Checkboxes, pRight: &SkinPrefix); |
| 438 | MainView.HSplitTop(Cut: 5.0f, pTop: nullptr, pBottom: &MainView); |
| 439 | MainView.HSplitTop(Cut: EyeButtonSize, pTop: &Eyes, pBottom: &MainView); |
| 440 | Eyes.VSplitRight(Cut: EyeButtonSize * NUM_EMOTES + 5.0f * (NUM_EMOTES - 1), pLeft: nullptr, pRight: &Eyes); |
| 441 | } |
| 442 | else |
| 443 | { |
| 444 | YourSkin.VSplitRight(Cut: 3 * EyeButtonSize + 2 * 5.0f, pLeft: &YourSkin, pRight: &Eyes); |
| 445 | const float RemainderWidth = YourSkin.w; |
| 446 | YourSkin.VSplitLeft(Cut: RemainderWidth * 0.4f, pLeft: &YourSkin, pRight: &Checkboxes); |
| 447 | Checkboxes.VSplitLeft(Cut: RemainderWidth * 0.35f, pLeft: &Checkboxes, pRight: &SkinPrefix); |
| 448 | SkinPrefix.VSplitRight(Cut: 20.0f, pLeft: &SkinPrefix, pRight: nullptr); |
| 449 | } |
| 450 | YourSkin.VSplitRight(Cut: 20.0f, pLeft: &YourSkin, pRight: nullptr); |
| 451 | Checkboxes.VSplitRight(Cut: 20.0f, pLeft: &Checkboxes, pRight: nullptr); |
| 452 | |
| 453 | // Checkboxes |
| 454 | bool ShouldRefresh = false; |
| 455 | Checkboxes.HSplitTop(Cut: 20.0f, pTop: &Button, pBottom: &Checkboxes); |
| 456 | if(DoButton_CheckBox(pId: &g_Config.m_ClDownloadSkins, pText: Localize(pStr: "Download skins" ), Checked: g_Config.m_ClDownloadSkins, pRect: &Button)) |
| 457 | { |
| 458 | g_Config.m_ClDownloadSkins ^= 1; |
| 459 | ShouldRefresh = true; |
| 460 | } |
| 461 | |
| 462 | Checkboxes.HSplitTop(Cut: 20.0f, pTop: &Button, pBottom: &Checkboxes); |
| 463 | if(DoButton_CheckBox(pId: &g_Config.m_ClDownloadCommunitySkins, pText: Localize(pStr: "Download community skins" ), Checked: g_Config.m_ClDownloadCommunitySkins, pRect: &Button)) |
| 464 | { |
| 465 | g_Config.m_ClDownloadCommunitySkins ^= 1; |
| 466 | ShouldRefresh = true; |
| 467 | } |
| 468 | |
| 469 | Checkboxes.HSplitTop(Cut: 20.0f, pTop: &Button, pBottom: &Checkboxes); |
| 470 | if(DoButton_CheckBox(pId: &g_Config.m_ClVanillaSkinsOnly, pText: Localize(pStr: "Vanilla skins only" ), Checked: g_Config.m_ClVanillaSkinsOnly, pRect: &Button)) |
| 471 | { |
| 472 | g_Config.m_ClVanillaSkinsOnly ^= 1; |
| 473 | ShouldRefresh = true; |
| 474 | } |
| 475 | |
| 476 | Checkboxes.HSplitTop(Cut: 20.0f, pTop: &Button, pBottom: &Checkboxes); |
| 477 | if(DoButton_CheckBox(pId: &g_Config.m_ClFatSkins, pText: Localize(pStr: "Fat skins (DDFat)" ), Checked: g_Config.m_ClFatSkins, pRect: &Button)) |
| 478 | { |
| 479 | g_Config.m_ClFatSkins ^= 1; |
| 480 | } |
| 481 | |
| 482 | // Skin prefix |
| 483 | { |
| 484 | SkinPrefix.HSplitTop(Cut: 20.0f, pTop: &Label, pBottom: &SkinPrefix); |
| 485 | Ui()->DoLabel(pRect: &Label, pText: Localize(pStr: "Skin prefix" ), Size: 14.0f, Align: TEXTALIGN_ML); |
| 486 | |
| 487 | SkinPrefix.HSplitTop(Cut: 20.0f, pTop: &Button, pBottom: &SkinPrefix); |
| 488 | static CLineInput s_SkinPrefixInput(g_Config.m_ClSkinPrefix, sizeof(g_Config.m_ClSkinPrefix)); |
| 489 | if(Ui()->DoClearableEditBox(pLineInput: &s_SkinPrefixInput, pRect: &Button, FontSize: 14.0f)) |
| 490 | { |
| 491 | ShouldRefresh = true; |
| 492 | } |
| 493 | |
| 494 | SkinPrefix.HSplitTop(Cut: 2.0f, pTop: nullptr, pBottom: &SkinPrefix); |
| 495 | |
| 496 | static const char *s_apSkinPrefixes[] = {"kitty" , "santa" }; |
| 497 | static CButtonContainer s_aPrefixButtons[std::size(s_apSkinPrefixes)]; |
| 498 | for(size_t i = 0; i < std::size(s_apSkinPrefixes); i++) |
| 499 | { |
| 500 | SkinPrefix.HSplitTop(Cut: 20.0f, pTop: &Button, pBottom: &SkinPrefix); |
| 501 | Button.HMargin(Cut: 2.0f, pOtherRect: &Button); |
| 502 | if(DoButton_Menu(pButtonContainer: &s_aPrefixButtons[i], pText: s_apSkinPrefixes[i], Checked: 0, pRect: &Button)) |
| 503 | { |
| 504 | str_copy(dst&: g_Config.m_ClSkinPrefix, src: s_apSkinPrefixes[i]); |
| 505 | ShouldRefresh = true; |
| 506 | } |
| 507 | } |
| 508 | } |
| 509 | |
| 510 | // Player skin area |
| 511 | CUIRect CustomColorsButton, RandomSkinButton; |
| 512 | YourSkin.HSplitTop(Cut: 20.0f, pTop: &Label, pBottom: &YourSkin); |
| 513 | YourSkin.HSplitBottom(Cut: 20.0f, pTop: &YourSkin, pBottom: &CustomColorsButton); |
| 514 | CustomColorsButton.VSplitRight(Cut: 30.0f, pLeft: &CustomColorsButton, pRight: &RandomSkinButton); |
| 515 | CustomColorsButton.VSplitRight(Cut: 20.0f, pLeft: &CustomColorsButton, pRight: nullptr); |
| 516 | YourSkin.VSplitLeft(Cut: 65.0f, pLeft: &YourSkin, pRight: &Button); |
| 517 | Button.VSplitLeft(Cut: 5.0f, pLeft: nullptr, pRight: &Button); |
| 518 | Button.HMargin(Cut: (Button.h - 20.0f) / 2.0f, pOtherRect: &Button); |
| 519 | |
| 520 | char aBuf[128 + IO_MAX_PATH_LENGTH]; |
| 521 | str_format(buffer: aBuf, buffer_size: sizeof(aBuf), format: "%s:" , Localize(pStr: "Your skin" )); |
| 522 | Ui()->DoLabel(pRect: &Label, pText: aBuf, Size: 14.0f, Align: TEXTALIGN_ML); |
| 523 | |
| 524 | CSkins::CSkinList &SkinList = GameClient()->m_Skins.SkinList(); |
| 525 | const CSkin *pDefaultSkin = GameClient()->m_Skins.Find(pName: "default" ); |
| 526 | const CSkins::CSkinContainer *pOwnSkinContainer = GameClient()->m_Skins.FindContainerOrNullptr(pName: pSkinName[0] == '\0' ? "default" : pSkinName); |
| 527 | if(pOwnSkinContainer != nullptr && pOwnSkinContainer->IsSpecial()) |
| 528 | { |
| 529 | pOwnSkinContainer = nullptr; // Special skins cannot be selected, show as missing due to invalid name |
| 530 | } |
| 531 | |
| 532 | CTeeRenderInfo OwnSkinInfo; |
| 533 | OwnSkinInfo.Apply(pSkin: pOwnSkinContainer == nullptr || pOwnSkinContainer->Skin() == nullptr ? pDefaultSkin : pOwnSkinContainer->Skin().get()); |
| 534 | OwnSkinInfo.ApplyColors(CustomColoredSkin: *pUseCustomColor, ColorBody: *pColorBody, ColorFeet: *pColorFeet); |
| 535 | OwnSkinInfo.m_Size = 50.0f; |
| 536 | |
| 537 | // Tee |
| 538 | { |
| 539 | vec2 OffsetToMid; |
| 540 | CRenderTools::GetRenderTeeOffsetToRenderedTee(pAnim: CAnimState::GetIdle(), pInfo: &OwnSkinInfo, TeeOffsetToMid&: OffsetToMid); |
| 541 | const vec2 TeeRenderPos = vec2(YourSkin.x + YourSkin.w / 2.0f, YourSkin.y + YourSkin.h / 2.0f + OffsetToMid.y); |
| 542 | // tee looking towards cursor, and it is happy when you touch it |
| 543 | const vec2 DeltaPosition = Ui()->MousePos() - TeeRenderPos; |
| 544 | const float Distance = length(a: DeltaPosition); |
| 545 | const float InteractionDistance = 20.0f; |
| 546 | const vec2 TeeDirection = Distance < InteractionDistance ? normalize(v: vec2(DeltaPosition.x, maximum(a: DeltaPosition.y, b: 0.5f))) : normalize(v: DeltaPosition); |
| 547 | const int TeeEmote = Distance < InteractionDistance ? EMOTE_HAPPY : *pEmote; |
| 548 | RenderTools()->RenderTee(pAnim: CAnimState::GetIdle(), pInfo: &OwnSkinInfo, Emote: TeeEmote, Dir: TeeDirection, Pos: TeeRenderPos); |
| 549 | } |
| 550 | |
| 551 | // Skin loading status |
| 552 | const auto &&RenderSkinStatus = [&](CUIRect Parent, const CSkins::CSkinContainer *pSkinContainer, const void *pStatusTooltipId) { |
| 553 | if(pSkinContainer != nullptr && pSkinContainer->State() == CSkins::CSkinContainer::EState::LOADED) |
| 554 | { |
| 555 | return; |
| 556 | } |
| 557 | |
| 558 | CUIRect StatusIcon; |
| 559 | Parent.HSplitTop(Cut: 20.0f, pTop: &StatusIcon, pBottom: nullptr); |
| 560 | StatusIcon.VSplitLeft(Cut: 20.0f, pLeft: &StatusIcon, pRight: nullptr); |
| 561 | |
| 562 | if(pSkinContainer != nullptr && |
| 563 | (pSkinContainer->State() == CSkins::CSkinContainer::EState::UNLOADED || |
| 564 | pSkinContainer->State() == CSkins::CSkinContainer::EState::PENDING || |
| 565 | pSkinContainer->State() == CSkins::CSkinContainer::EState::LOADING)) |
| 566 | { |
| 567 | Ui()->RenderProgressSpinner(Center: StatusIcon.Center(), OuterRadius: 5.0f); |
| 568 | } |
| 569 | else |
| 570 | { |
| 571 | TextRender()->TextColor(Color: ColorRGBA(1.0f, 0.0f, 0.0f, 1.0f)); |
| 572 | TextRender()->SetFontPreset(EFontPreset::ICON_FONT); |
| 573 | TextRender()->SetRenderFlags(ETextRenderFlags::TEXT_RENDER_FLAG_ONLY_ADVANCE_WIDTH | ETextRenderFlags::TEXT_RENDER_FLAG_NO_X_BEARING | ETextRenderFlags::TEXT_RENDER_FLAG_NO_Y_BEARING | ETextRenderFlags::TEXT_RENDER_FLAG_NO_PIXEL_ALIGNMENT | ETextRenderFlags::TEXT_RENDER_FLAG_NO_OVERSIZE); |
| 574 | Ui()->DoLabel(pRect: &StatusIcon, pText: pSkinContainer == nullptr || pSkinContainer->State() == CSkins::CSkinContainer::EState::ERROR ? FONT_ICON_TRIANGLE_EXCLAMATION : FONT_ICON_QUESTION, Size: 12.0f, Align: TEXTALIGN_MC); |
| 575 | TextRender()->SetRenderFlags(0); |
| 576 | TextRender()->SetFontPreset(EFontPreset::DEFAULT_FONT); |
| 577 | TextRender()->TextColor(Color: TextRender()->DefaultTextColor()); |
| 578 | Ui()->DoButtonLogic(pId: pStatusTooltipId, Checked: 0, pRect: &StatusIcon, Flags: BUTTONFLAG_NONE); |
| 579 | const char *pErrorTooltip; |
| 580 | if(pSkinContainer == nullptr) |
| 581 | { |
| 582 | pErrorTooltip = Localize(pStr: "This skin name cannot be used." ); |
| 583 | } |
| 584 | else if(pSkinContainer->State() == CSkins::CSkinContainer::EState::ERROR) |
| 585 | { |
| 586 | pErrorTooltip = Localize(pStr: "Skin could not be loaded due to an error. Check the local console for details." ); |
| 587 | } |
| 588 | else |
| 589 | { |
| 590 | pErrorTooltip = Localize(pStr: "Skin could not be found." ); |
| 591 | } |
| 592 | GameClient()->m_Tooltips.DoToolTip(pId: pStatusTooltipId, pNearRect: &StatusIcon, pText: pErrorTooltip); |
| 593 | } |
| 594 | }; |
| 595 | static char s_StatusTooltipId; |
| 596 | RenderSkinStatus(YourSkin, pOwnSkinContainer, &s_StatusTooltipId); |
| 597 | |
| 598 | // Skin name |
| 599 | static CLineInput s_SkinInput; |
| 600 | s_SkinInput.SetBuffer(pStr: pSkinName, MaxSize: SkinNameSize); |
| 601 | s_SkinInput.SetEmptyText("default" ); |
| 602 | if(Ui()->DoClearableEditBox(pLineInput: &s_SkinInput, pRect: &Button, FontSize: 14.0f)) |
| 603 | { |
| 604 | SetNeedSendInfo(); |
| 605 | m_SkinListScrollToSelected = true; |
| 606 | SkinList.ForceRefresh(); |
| 607 | } |
| 608 | |
| 609 | // Random skin button |
| 610 | static CButtonContainer s_RandomSkinButton; |
| 611 | static const char *s_apDice[] = {FONT_ICON_DICE_ONE, FONT_ICON_DICE_TWO, FONT_ICON_DICE_THREE, FONT_ICON_DICE_FOUR, FONT_ICON_DICE_FIVE, FONT_ICON_DICE_SIX}; |
| 612 | static int s_CurrentDie = rand() % std::size(s_apDice); |
| 613 | TextRender()->SetFontPreset(EFontPreset::ICON_FONT); |
| 614 | TextRender()->SetRenderFlags(ETextRenderFlags::TEXT_RENDER_FLAG_ONLY_ADVANCE_WIDTH | ETextRenderFlags::TEXT_RENDER_FLAG_NO_X_BEARING | ETextRenderFlags::TEXT_RENDER_FLAG_NO_Y_BEARING | ETextRenderFlags::TEXT_RENDER_FLAG_NO_PIXEL_ALIGNMENT | ETextRenderFlags::TEXT_RENDER_FLAG_NO_OVERSIZE); |
| 615 | if(DoButton_Menu(pButtonContainer: &s_RandomSkinButton, pText: s_apDice[s_CurrentDie], Checked: 0, pRect: &RandomSkinButton, Flags: BUTTONFLAG_LEFT, pImageName: nullptr, Corners: IGraphics::CORNER_ALL, Rounding: 5.0f, FontFactor: -0.2f)) |
| 616 | { |
| 617 | GameClient()->m_Skins.RandomizeSkin(Dummy: m_Dummy); |
| 618 | SetNeedSendInfo(); |
| 619 | m_SkinListScrollToSelected = true; |
| 620 | s_CurrentDie = rand() % std::size(s_apDice); |
| 621 | } |
| 622 | TextRender()->SetRenderFlags(0); |
| 623 | TextRender()->SetFontPreset(EFontPreset::DEFAULT_FONT); |
| 624 | GameClient()->m_Tooltips.DoToolTip(pId: &s_RandomSkinButton, pNearRect: &RandomSkinButton, pText: Localize(pStr: "Create a random skin" )); |
| 625 | |
| 626 | // Custom colors button |
| 627 | if(DoButton_CheckBox(pId: pUseCustomColor, pText: Localize(pStr: "Custom colors" ), Checked: *pUseCustomColor, pRect: &CustomColorsButton)) |
| 628 | { |
| 629 | *pUseCustomColor = *pUseCustomColor ? 0 : 1; |
| 630 | SetNeedSendInfo(); |
| 631 | } |
| 632 | |
| 633 | // Default eyes |
| 634 | { |
| 635 | CTeeRenderInfo EyeSkinInfo = OwnSkinInfo; |
| 636 | EyeSkinInfo.m_Size = EyeButtonSize; |
| 637 | vec2 OffsetToMid; |
| 638 | CRenderTools::GetRenderTeeOffsetToRenderedTee(pAnim: CAnimState::GetIdle(), pInfo: &EyeSkinInfo, TeeOffsetToMid&: OffsetToMid); |
| 639 | |
| 640 | CUIRect EyesRow; |
| 641 | Eyes.HSplitTop(Cut: EyeButtonSize, pTop: &EyesRow, pBottom: &Eyes); |
| 642 | static CButtonContainer s_aEyeButtons[NUM_EMOTES]; |
| 643 | for(int CurrentEyeEmote = 0; CurrentEyeEmote < NUM_EMOTES; CurrentEyeEmote++) |
| 644 | { |
| 645 | EyesRow.VSplitLeft(Cut: EyeButtonSize, pLeft: &Button, pRight: &EyesRow); |
| 646 | EyesRow.VSplitLeft(Cut: 5.0f, pLeft: nullptr, pRight: &EyesRow); |
| 647 | if(!RenderEyesBelow && (CurrentEyeEmote + 1) % 3 == 0) |
| 648 | { |
| 649 | Eyes.HSplitTop(Cut: 5.0f, pTop: nullptr, pBottom: &Eyes); |
| 650 | Eyes.HSplitTop(Cut: EyeButtonSize, pTop: &EyesRow, pBottom: &Eyes); |
| 651 | } |
| 652 | |
| 653 | const ColorRGBA EyeButtonColor = ColorRGBA(1.0f, 1.0f, 1.0f, 0.25f + (*pEmote == CurrentEyeEmote ? 0.25f : 0.0f)); |
| 654 | if(DoButton_Menu(pButtonContainer: &s_aEyeButtons[CurrentEyeEmote], pText: "" , Checked: 0, pRect: &Button, Flags: BUTTONFLAG_LEFT, pImageName: nullptr, Corners: IGraphics::CORNER_ALL, Rounding: 5.0f, FontFactor: 0.0f, Color: EyeButtonColor)) |
| 655 | { |
| 656 | *pEmote = CurrentEyeEmote; |
| 657 | if((int)m_Dummy == g_Config.m_ClDummy) |
| 658 | GameClient()->m_Emoticon.EyeEmote(EyeEmote: CurrentEyeEmote); |
| 659 | } |
| 660 | GameClient()->m_Tooltips.DoToolTip(pId: &s_aEyeButtons[CurrentEyeEmote], pNearRect: &Button, pText: Localize(pStr: "Choose default eyes when joining a server" )); |
| 661 | RenderTools()->RenderTee(pAnim: CAnimState::GetIdle(), pInfo: &EyeSkinInfo, Emote: CurrentEyeEmote, Dir: vec2(1.0f, 0.0f), Pos: vec2(Button.x + Button.w / 2.0f, Button.y + Button.h / 2.0f + OffsetToMid.y)); |
| 662 | } |
| 663 | } |
| 664 | |
| 665 | // Custom color pickers |
| 666 | MainView.HSplitTop(Cut: 5.0f, pTop: nullptr, pBottom: &MainView); |
| 667 | if(*pUseCustomColor) |
| 668 | { |
| 669 | CUIRect CustomColors; |
| 670 | MainView.HSplitTop(Cut: 95.0f, pTop: &CustomColors, pBottom: &MainView); |
| 671 | CUIRect aRects[2]; |
| 672 | CustomColors.VSplitMid(pLeft: &aRects[0], pRight: &aRects[1], Spacing: 20.0f); |
| 673 | |
| 674 | unsigned *apColors[] = {pColorBody, pColorFeet}; |
| 675 | const char *apParts[] = {Localize(pStr: "Body" ), Localize(pStr: "Feet" )}; |
| 676 | |
| 677 | for(int i = 0; i < 2; i++) |
| 678 | { |
| 679 | aRects[i].HSplitTop(Cut: 20.0f, pTop: &Label, pBottom: &aRects[i]); |
| 680 | Ui()->DoLabel(pRect: &Label, pText: apParts[i], Size: 14.0f, Align: TEXTALIGN_ML); |
| 681 | if(RenderHslaScrollbars(pRect: &aRects[i], pColor: apColors[i], Alpha: false, DarkestLight: ColorHSLA::DARKEST_LGT)) |
| 682 | { |
| 683 | SetNeedSendInfo(); |
| 684 | } |
| 685 | } |
| 686 | } |
| 687 | MainView.HSplitTop(Cut: 5.0f, pTop: nullptr, pBottom: &MainView); |
| 688 | |
| 689 | // Layout bottom controls and use remainder for skin selector |
| 690 | CUIRect QuickSearch, DatabaseButton, DirectoryButton, RefreshButton; |
| 691 | MainView.HSplitBottom(Cut: 20.0f, pTop: &MainView, pBottom: &QuickSearch); |
| 692 | MainView.HSplitBottom(Cut: 5.0f, pTop: &MainView, pBottom: nullptr); |
| 693 | QuickSearch.VSplitLeft(Cut: 220.0f, pLeft: &QuickSearch, pRight: &DatabaseButton); |
| 694 | DatabaseButton.VSplitLeft(Cut: 10.0f, pLeft: nullptr, pRight: &DatabaseButton); |
| 695 | DatabaseButton.VSplitLeft(Cut: 150.0f, pLeft: &DatabaseButton, pRight: &DirectoryButton); |
| 696 | DirectoryButton.VSplitRight(Cut: 175.0f, pLeft: nullptr, pRight: &DirectoryButton); |
| 697 | DirectoryButton.VSplitRight(Cut: 25.0f, pLeft: &DirectoryButton, pRight: &RefreshButton); |
| 698 | DirectoryButton.VSplitRight(Cut: 10.0f, pLeft: &DirectoryButton, pRight: nullptr); |
| 699 | |
| 700 | // Skin selector |
| 701 | static CListBox s_ListBox; |
| 702 | std::vector<CSkins::CSkinListEntry> &vSkinList = SkinList.Skins(); |
| 703 | int OldSelected = -1; |
| 704 | s_ListBox.DoStart(RowHeight: 50.0f, NumItems: vSkinList.size(), ItemsPerRow: 4, RowsPerScroll: 2, SelectedIndex: OldSelected, pRect: &MainView); |
| 705 | for(size_t i = 0; i < vSkinList.size(); ++i) |
| 706 | { |
| 707 | CSkins::CSkinListEntry &SkinListEntry = vSkinList[i]; |
| 708 | const CSkins::CSkinContainer *pSkinContainer = vSkinList[i].SkinContainer(); |
| 709 | |
| 710 | if(!m_Dummy ? SkinListEntry.IsSelectedMain() : SkinListEntry.IsSelectedDummy()) |
| 711 | { |
| 712 | OldSelected = i; |
| 713 | if(m_SkinListScrollToSelected) |
| 714 | { |
| 715 | s_ListBox.ScrollToSelected(); |
| 716 | m_SkinListScrollToSelected = false; |
| 717 | } |
| 718 | } |
| 719 | |
| 720 | const CListboxItem Item = s_ListBox.DoNextItem(pId: SkinListEntry.ListItemId(), Selected: OldSelected >= 0 && (size_t)OldSelected == i); |
| 721 | if(!Item.m_Visible) |
| 722 | { |
| 723 | continue; |
| 724 | } |
| 725 | |
| 726 | SkinListEntry.RequestLoad(); |
| 727 | const CSkin *pSkin = pSkinContainer->State() == CSkins::CSkinContainer::EState::LOADED ? pSkinContainer->Skin().get() : pDefaultSkin; |
| 728 | |
| 729 | Item.m_Rect.VSplitLeft(Cut: 60.0f, pLeft: &Button, pRight: &Label); |
| 730 | |
| 731 | { |
| 732 | CTeeRenderInfo Info = OwnSkinInfo; |
| 733 | Info.Apply(pSkin); |
| 734 | vec2 OffsetToMid; |
| 735 | CRenderTools::GetRenderTeeOffsetToRenderedTee(pAnim: CAnimState::GetIdle(), pInfo: &Info, TeeOffsetToMid&: OffsetToMid); |
| 736 | const vec2 TeeRenderPos = vec2(Button.x + Button.w / 2.0f, Button.y + Button.h / 2 + OffsetToMid.y); |
| 737 | RenderTools()->RenderTee(pAnim: CAnimState::GetIdle(), pInfo: &Info, Emote: *pEmote, Dir: vec2(1.0f, 0.0f), Pos: TeeRenderPos); |
| 738 | } |
| 739 | |
| 740 | { |
| 741 | SLabelProperties Props; |
| 742 | Props.m_MaxWidth = Label.w - 5.0f; |
| 743 | const auto &NameMatch = SkinListEntry.NameMatch(); |
| 744 | if(NameMatch.has_value()) |
| 745 | { |
| 746 | const auto [MatchStart, MatchLength] = NameMatch.value(); |
| 747 | Props.m_vColorSplits.emplace_back(args: MatchStart, args: MatchLength, args: ColorRGBA(0.4f, 0.4f, 1.0f, 1.0f)); |
| 748 | } |
| 749 | Ui()->DoLabel(pRect: &Label, pText: pSkinContainer->Name(), Size: 12.0f, Align: TEXTALIGN_ML, LabelProps: Props); |
| 750 | } |
| 751 | |
| 752 | if(g_Config.m_Debug) |
| 753 | { |
| 754 | Graphics()->TextureClear(); |
| 755 | Graphics()->QuadsBegin(); |
| 756 | Graphics()->SetColor(*pUseCustomColor ? color_cast<ColorRGBA>(hsl: ColorHSLA(*pColorBody).UnclampLighting(Darkest: ColorHSLA::DARKEST_LGT)) : pSkin->m_BloodColor); |
| 757 | IGraphics::CQuadItem QuadItem(Label.x, Label.y, 12.0f, 12.0f); |
| 758 | Graphics()->QuadsDrawTL(pArray: &QuadItem, Num: 1); |
| 759 | Graphics()->QuadsEnd(); |
| 760 | } |
| 761 | |
| 762 | // render skin favorite icon |
| 763 | { |
| 764 | CUIRect FavIcon; |
| 765 | Item.m_Rect.HSplitTop(Cut: 20.0f, pTop: &FavIcon, pBottom: nullptr); |
| 766 | FavIcon.VSplitRight(Cut: 20.0f, pLeft: nullptr, pRight: &FavIcon); |
| 767 | if(DoButton_Favorite(pButtonId: SkinListEntry.FavoriteButtonId(), pParentId: SkinListEntry.ListItemId(), Checked: SkinListEntry.IsFavorite(), pRect: &FavIcon)) |
| 768 | { |
| 769 | if(SkinListEntry.IsFavorite()) |
| 770 | { |
| 771 | GameClient()->m_Skins.RemoveFavorite(pName: pSkinContainer->Name()); |
| 772 | } |
| 773 | else |
| 774 | { |
| 775 | GameClient()->m_Skins.AddFavorite(pName: pSkinContainer->Name()); |
| 776 | } |
| 777 | } |
| 778 | } |
| 779 | |
| 780 | RenderSkinStatus(Item.m_Rect, pSkinContainer, SkinListEntry.ErrorTooltipId()); |
| 781 | } |
| 782 | |
| 783 | const int NewSelected = s_ListBox.DoEnd(); |
| 784 | if(OldSelected != NewSelected) |
| 785 | { |
| 786 | str_copy(dst: pSkinName, src: vSkinList[NewSelected].SkinContainer()->Name(), dst_size: SkinNameSize); |
| 787 | SkinList.ForceRefresh(); |
| 788 | SetNeedSendInfo(); |
| 789 | } |
| 790 | |
| 791 | static CLineInput s_SkinFilterInput(g_Config.m_ClSkinFilterString, sizeof(g_Config.m_ClSkinFilterString)); |
| 792 | if(SkinList.UnfilteredCount() > 0 && vSkinList.empty()) |
| 793 | { |
| 794 | CUIRect FilterLabel, ResetButton; |
| 795 | MainView.HMargin(Cut: (MainView.h - (16.0f + 18.0f + 8.0f)) / 2.0f, pOtherRect: &FilterLabel); |
| 796 | FilterLabel.HSplitTop(Cut: 16.0f, pTop: &FilterLabel, pBottom: &ResetButton); |
| 797 | ResetButton.HSplitTop(Cut: 8.0f, pTop: nullptr, pBottom: &ResetButton); |
| 798 | ResetButton.VMargin(Cut: (ResetButton.w - 200.0f) / 2.0f, pOtherRect: &ResetButton); |
| 799 | Ui()->DoLabel(pRect: &FilterLabel, pText: Localize(pStr: "No skins match your filter criteria" ), Size: 16.0f, Align: TEXTALIGN_MC); |
| 800 | static CButtonContainer s_ResetButton; |
| 801 | if(DoButton_Menu(pButtonContainer: &s_ResetButton, pText: Localize(pStr: "Reset filter" ), Checked: 0, pRect: &ResetButton)) |
| 802 | { |
| 803 | s_SkinFilterInput.Clear(); |
| 804 | SkinList.ForceRefresh(); |
| 805 | } |
| 806 | } |
| 807 | |
| 808 | if(Ui()->DoEditBox_Search(pLineInput: &s_SkinFilterInput, pRect: &QuickSearch, FontSize: 14.0f, HotkeyEnabled: !Ui()->IsPopupOpen() && !GameClient()->m_GameConsole.IsActive())) |
| 809 | { |
| 810 | SkinList.ForceRefresh(); |
| 811 | } |
| 812 | |
| 813 | static CButtonContainer s_SkinDatabaseButton; |
| 814 | if(DoButton_Menu(pButtonContainer: &s_SkinDatabaseButton, pText: Localize(pStr: "Skin Database" ), Checked: 0, pRect: &DatabaseButton)) |
| 815 | { |
| 816 | Client()->ViewLink(pLink: "https://ddnet.org/skins/" ); |
| 817 | } |
| 818 | |
| 819 | static CButtonContainer s_DirectoryButton; |
| 820 | if(DoButton_Menu(pButtonContainer: &s_DirectoryButton, pText: Localize(pStr: "Skins directory" ), Checked: 0, pRect: &DirectoryButton)) |
| 821 | { |
| 822 | Storage()->GetCompletePath(Type: IStorage::TYPE_SAVE, pDir: "skins" , pBuffer: aBuf, BufferSize: sizeof(aBuf)); |
| 823 | Storage()->CreateFolder(pFoldername: "skins" , Type: IStorage::TYPE_SAVE); |
| 824 | Client()->ViewFile(pFilename: aBuf); |
| 825 | } |
| 826 | GameClient()->m_Tooltips.DoToolTip(pId: &s_DirectoryButton, pNearRect: &DirectoryButton, pText: Localize(pStr: "Open the directory to add custom skins" )); |
| 827 | |
| 828 | TextRender()->SetFontPreset(EFontPreset::ICON_FONT); |
| 829 | TextRender()->SetRenderFlags(ETextRenderFlags::TEXT_RENDER_FLAG_ONLY_ADVANCE_WIDTH | ETextRenderFlags::TEXT_RENDER_FLAG_NO_X_BEARING | ETextRenderFlags::TEXT_RENDER_FLAG_NO_Y_BEARING | ETextRenderFlags::TEXT_RENDER_FLAG_NO_PIXEL_ALIGNMENT | ETextRenderFlags::TEXT_RENDER_FLAG_NO_OVERSIZE); |
| 830 | static CButtonContainer s_SkinRefreshButton; |
| 831 | if(DoButton_Menu(pButtonContainer: &s_SkinRefreshButton, pText: FONT_ICON_ARROW_ROTATE_RIGHT, Checked: 0, pRect: &RefreshButton) || Input()->KeyPress(Key: KEY_F5) || (Input()->KeyPress(Key: KEY_R) && Input()->ModifierIsPressed())) |
| 832 | { |
| 833 | ShouldRefresh = true; |
| 834 | } |
| 835 | TextRender()->SetRenderFlags(0); |
| 836 | TextRender()->SetFontPreset(EFontPreset::DEFAULT_FONT); |
| 837 | |
| 838 | if(ShouldRefresh) |
| 839 | { |
| 840 | GameClient()->RefreshSkins(SkinDescriptorFlags: CSkinDescriptor::FLAG_SIX); |
| 841 | } |
| 842 | } |
| 843 | |
| 844 | void CMenus::(CUIRect MainView) |
| 845 | { |
| 846 | CUIRect Button; |
| 847 | char aBuf[128]; |
| 848 | bool CheckSettings = false; |
| 849 | |
| 850 | static const int MAX_RESOLUTIONS = 256; |
| 851 | static CVideoMode s_aModes[MAX_RESOLUTIONS]; |
| 852 | static int s_NumNodes = Graphics()->GetVideoModes(pModes: s_aModes, MaxModes: MAX_RESOLUTIONS, Screen: g_Config.m_GfxScreen); |
| 853 | static int s_GfxFsaaSamples = g_Config.m_GfxFsaaSamples; |
| 854 | static bool s_GfxBackendChanged = false; |
| 855 | static bool s_GfxGpuChanged = false; |
| 856 | |
| 857 | static int s_InitDisplayAllVideoModes = g_Config.m_GfxDisplayAllVideoModes; |
| 858 | |
| 859 | static bool s_WasInit = false; |
| 860 | static bool s_ModesReload = false; |
| 861 | if(!s_WasInit) |
| 862 | { |
| 863 | s_WasInit = true; |
| 864 | |
| 865 | Graphics()->AddWindowPropChangeListener(pFunc: []() { |
| 866 | s_ModesReload = true; |
| 867 | }); |
| 868 | } |
| 869 | |
| 870 | if(s_ModesReload || g_Config.m_GfxDisplayAllVideoModes != s_InitDisplayAllVideoModes) |
| 871 | { |
| 872 | s_NumNodes = Graphics()->GetVideoModes(pModes: s_aModes, MaxModes: MAX_RESOLUTIONS, Screen: g_Config.m_GfxScreen); |
| 873 | s_ModesReload = false; |
| 874 | s_InitDisplayAllVideoModes = g_Config.m_GfxDisplayAllVideoModes; |
| 875 | } |
| 876 | |
| 877 | CUIRect ModeList, ModeLabel; |
| 878 | MainView.VSplitLeft(Cut: 350.0f, pLeft: &MainView, pRight: &ModeList); |
| 879 | ModeList.HSplitTop(Cut: 24.0f, pTop: &ModeLabel, pBottom: &ModeList); |
| 880 | MainView.VSplitLeft(Cut: 340.0f, pLeft: &MainView, pRight: nullptr); |
| 881 | |
| 882 | // display mode list |
| 883 | static CListBox s_ListBox; |
| 884 | static const float sc_RowHeightResList = 22.0f; |
| 885 | static const float = 12.0f; |
| 886 | static const float sc_FontSizeResList = 10.0f; |
| 887 | |
| 888 | { |
| 889 | int G = std::gcd(m: g_Config.m_GfxScreenWidth, n: g_Config.m_GfxScreenHeight); |
| 890 | str_format(buffer: aBuf, buffer_size: sizeof(aBuf), format: "%s: %dx%d @%dhz %d bit (%d:%d)" , Localize(pStr: "Current" ), (int)(g_Config.m_GfxScreenWidth * Graphics()->ScreenHiDPIScale()), (int)(g_Config.m_GfxScreenHeight * Graphics()->ScreenHiDPIScale()), g_Config.m_GfxScreenRefreshRate, g_Config.m_GfxColorDepth, g_Config.m_GfxScreenWidth / G, g_Config.m_GfxScreenHeight / G); |
| 891 | Ui()->DoLabel(pRect: &ModeLabel, pText: aBuf, Size: sc_FontSizeResListHeader, Align: TEXTALIGN_MC); |
| 892 | } |
| 893 | |
| 894 | int OldSelected = -1; |
| 895 | s_ListBox.SetActive(!Ui()->IsPopupOpen()); |
| 896 | s_ListBox.DoStart(RowHeight: sc_RowHeightResList, NumItems: s_NumNodes, ItemsPerRow: 1, RowsPerScroll: 3, SelectedIndex: OldSelected, pRect: &ModeList); |
| 897 | |
| 898 | for(int i = 0; i < s_NumNodes; ++i) |
| 899 | { |
| 900 | const int Depth = s_aModes[i].m_Red + s_aModes[i].m_Green + s_aModes[i].m_Blue > 16 ? 24 : 16; |
| 901 | if(g_Config.m_GfxColorDepth == Depth && |
| 902 | g_Config.m_GfxScreenWidth == s_aModes[i].m_WindowWidth && |
| 903 | g_Config.m_GfxScreenHeight == s_aModes[i].m_WindowHeight && |
| 904 | g_Config.m_GfxScreenRefreshRate == s_aModes[i].m_RefreshRate) |
| 905 | { |
| 906 | OldSelected = i; |
| 907 | } |
| 908 | |
| 909 | const CListboxItem Item = s_ListBox.DoNextItem(pId: &s_aModes[i], Selected: OldSelected == i); |
| 910 | if(!Item.m_Visible) |
| 911 | continue; |
| 912 | |
| 913 | int G = std::gcd(m: s_aModes[i].m_WindowWidth, n: s_aModes[i].m_WindowHeight); |
| 914 | str_format(buffer: aBuf, buffer_size: sizeof(aBuf), format: " %dx%d @%dhz %d bit (%d:%d)" , s_aModes[i].m_CanvasWidth, s_aModes[i].m_CanvasHeight, s_aModes[i].m_RefreshRate, Depth, s_aModes[i].m_WindowWidth / G, s_aModes[i].m_WindowHeight / G); |
| 915 | Ui()->DoLabel(pRect: &Item.m_Rect, pText: aBuf, Size: sc_FontSizeResList, Align: TEXTALIGN_ML); |
| 916 | } |
| 917 | |
| 918 | const int NewSelected = s_ListBox.DoEnd(); |
| 919 | if(OldSelected != NewSelected) |
| 920 | { |
| 921 | const int Depth = s_aModes[NewSelected].m_Red + s_aModes[NewSelected].m_Green + s_aModes[NewSelected].m_Blue > 16 ? 24 : 16; |
| 922 | g_Config.m_GfxColorDepth = Depth; |
| 923 | g_Config.m_GfxScreenWidth = s_aModes[NewSelected].m_WindowWidth; |
| 924 | g_Config.m_GfxScreenHeight = s_aModes[NewSelected].m_WindowHeight; |
| 925 | g_Config.m_GfxScreenRefreshRate = s_aModes[NewSelected].m_RefreshRate; |
| 926 | Graphics()->ResizeToScreen(); |
| 927 | } |
| 928 | |
| 929 | // switches |
| 930 | CUIRect WindowModeDropDown; |
| 931 | MainView.HSplitTop(Cut: 20.0f, pTop: &WindowModeDropDown, pBottom: &MainView); |
| 932 | |
| 933 | const char *apWindowModes[] = {Localize(pStr: "Windowed" ), Localize(pStr: "Windowed borderless" ), Localize(pStr: "Windowed fullscreen" ), Localize(pStr: "Desktop fullscreen" ), Localize(pStr: "Fullscreen" )}; |
| 934 | static const int s_NumWindowMode = std::size(apWindowModes); |
| 935 | |
| 936 | const int OldWindowMode = (g_Config.m_GfxFullscreen ? (g_Config.m_GfxFullscreen == 1 ? 4 : (g_Config.m_GfxFullscreen == 2 ? 3 : 2)) : (g_Config.m_GfxBorderless ? 1 : 0)); |
| 937 | |
| 938 | static CUi::SDropDownState s_WindowModeDropDownState; |
| 939 | static CScrollRegion s_WindowModeDropDownScrollRegion; |
| 940 | s_WindowModeDropDownState.m_SelectionPopupContext.m_pScrollRegion = &s_WindowModeDropDownScrollRegion; |
| 941 | const int NewWindowMode = Ui()->DoDropDown(pRect: &WindowModeDropDown, CurSelection: OldWindowMode, pStrs: apWindowModes, Num: s_NumWindowMode, State&: s_WindowModeDropDownState); |
| 942 | if(OldWindowMode != NewWindowMode) |
| 943 | { |
| 944 | if(NewWindowMode == 0) |
| 945 | Graphics()->SetWindowParams(FullscreenMode: 0, IsBorderless: false); |
| 946 | else if(NewWindowMode == 1) |
| 947 | Graphics()->SetWindowParams(FullscreenMode: 0, IsBorderless: true); |
| 948 | else if(NewWindowMode == 2) |
| 949 | Graphics()->SetWindowParams(FullscreenMode: 3, IsBorderless: false); |
| 950 | else if(NewWindowMode == 3) |
| 951 | Graphics()->SetWindowParams(FullscreenMode: 2, IsBorderless: false); |
| 952 | else if(NewWindowMode == 4) |
| 953 | Graphics()->SetWindowParams(FullscreenMode: 1, IsBorderless: false); |
| 954 | } |
| 955 | |
| 956 | if(Graphics()->GetNumScreens() > 1) |
| 957 | { |
| 958 | CUIRect ScreenDropDown; |
| 959 | MainView.HSplitTop(Cut: 2.0f, pTop: nullptr, pBottom: &MainView); |
| 960 | MainView.HSplitTop(Cut: 20.0f, pTop: &ScreenDropDown, pBottom: &MainView); |
| 961 | |
| 962 | const int NumScreens = Graphics()->GetNumScreens(); |
| 963 | static std::vector<std::string> s_vScreenNames; |
| 964 | static std::vector<const char *> s_vpScreenNames; |
| 965 | s_vScreenNames.resize(new_size: NumScreens); |
| 966 | s_vpScreenNames.resize(new_size: NumScreens); |
| 967 | |
| 968 | for(int i = 0; i < NumScreens; ++i) |
| 969 | { |
| 970 | str_format(buffer: aBuf, buffer_size: sizeof(aBuf), format: "%s %d: %s" , Localize(pStr: "Screen" ), i, Graphics()->GetScreenName(Screen: i)); |
| 971 | s_vScreenNames[i] = aBuf; |
| 972 | s_vpScreenNames[i] = s_vScreenNames[i].c_str(); |
| 973 | } |
| 974 | |
| 975 | static CUi::SDropDownState s_ScreenDropDownState; |
| 976 | static CScrollRegion s_ScreenDropDownScrollRegion; |
| 977 | s_ScreenDropDownState.m_SelectionPopupContext.m_pScrollRegion = &s_ScreenDropDownScrollRegion; |
| 978 | const int NewScreen = Ui()->DoDropDown(pRect: &ScreenDropDown, CurSelection: g_Config.m_GfxScreen, pStrs: s_vpScreenNames.data(), Num: s_vpScreenNames.size(), State&: s_ScreenDropDownState); |
| 979 | if(NewScreen != g_Config.m_GfxScreen) |
| 980 | Graphics()->SwitchWindowScreen(Index: NewScreen, MoveToCenter: true); |
| 981 | } |
| 982 | |
| 983 | MainView.HSplitTop(Cut: 2.0f, pTop: nullptr, pBottom: &MainView); |
| 984 | MainView.HSplitTop(Cut: 20.0f, pTop: &Button, pBottom: &MainView); |
| 985 | str_format(buffer: aBuf, buffer_size: sizeof(aBuf), format: "%s (%s)" , Localize(pStr: "V-Sync" ), Localize(pStr: "may cause delay" )); |
| 986 | if(DoButton_CheckBox(pId: &g_Config.m_GfxVsync, pText: aBuf, Checked: g_Config.m_GfxVsync, pRect: &Button)) |
| 987 | { |
| 988 | Graphics()->SetVSync(!g_Config.m_GfxVsync); |
| 989 | } |
| 990 | |
| 991 | bool MultiSamplingChanged = false; |
| 992 | MainView.HSplitTop(Cut: 20.0f, pTop: &Button, pBottom: &MainView); |
| 993 | str_format(buffer: aBuf, buffer_size: sizeof(aBuf), format: "%s (%s)" , Localize(pStr: "FSAA samples" ), Localize(pStr: "may cause delay" )); |
| 994 | int GfxFsaaSamplesMouseButton = DoButton_CheckBox_Number(pId: &g_Config.m_GfxFsaaSamples, pText: aBuf, Checked: g_Config.m_GfxFsaaSamples, pRect: &Button); |
| 995 | int CurFSAA = g_Config.m_GfxFsaaSamples == 0 ? 1 : g_Config.m_GfxFsaaSamples; |
| 996 | if(GfxFsaaSamplesMouseButton == 1) // inc |
| 997 | { |
| 998 | g_Config.m_GfxFsaaSamples = std::pow(x: 2, y: (int)std::log2(x: CurFSAA) + 1); |
| 999 | if(g_Config.m_GfxFsaaSamples > 64) |
| 1000 | g_Config.m_GfxFsaaSamples = 0; |
| 1001 | MultiSamplingChanged = true; |
| 1002 | } |
| 1003 | else if(GfxFsaaSamplesMouseButton == 2) // dec |
| 1004 | { |
| 1005 | if(CurFSAA == 1) |
| 1006 | g_Config.m_GfxFsaaSamples = 64; |
| 1007 | else if(CurFSAA == 2) |
| 1008 | g_Config.m_GfxFsaaSamples = 0; |
| 1009 | else |
| 1010 | g_Config.m_GfxFsaaSamples = std::pow(x: 2, y: (int)std::log2(x: CurFSAA) - 1); |
| 1011 | MultiSamplingChanged = true; |
| 1012 | } |
| 1013 | |
| 1014 | uint32_t MultiSamplingCountBackend = 0; |
| 1015 | if(MultiSamplingChanged) |
| 1016 | { |
| 1017 | if(Graphics()->SetMultiSampling(ReqMultiSamplingCount: g_Config.m_GfxFsaaSamples, MultiSamplingCountBackend)) |
| 1018 | { |
| 1019 | // try again with 0 if mouse click was increasing multi sampling |
| 1020 | // else just accept the current value as is |
| 1021 | if((uint32_t)g_Config.m_GfxFsaaSamples > MultiSamplingCountBackend && GfxFsaaSamplesMouseButton == 1) |
| 1022 | Graphics()->SetMultiSampling(ReqMultiSamplingCount: 0, MultiSamplingCountBackend); |
| 1023 | g_Config.m_GfxFsaaSamples = (int)MultiSamplingCountBackend; |
| 1024 | } |
| 1025 | else |
| 1026 | { |
| 1027 | CheckSettings = true; |
| 1028 | } |
| 1029 | } |
| 1030 | |
| 1031 | MainView.HSplitTop(Cut: 20.0f, pTop: &Button, pBottom: &MainView); |
| 1032 | if(DoButton_CheckBox(pId: &g_Config.m_GfxHighDetail, pText: Localize(pStr: "High Detail" ), Checked: g_Config.m_GfxHighDetail, pRect: &Button)) |
| 1033 | g_Config.m_GfxHighDetail ^= 1; |
| 1034 | GameClient()->m_Tooltips.DoToolTip(pId: &g_Config.m_GfxHighDetail, pNearRect: &Button, pText: Localize(pStr: "Allows maps to render with more detail" )); |
| 1035 | |
| 1036 | MainView.HSplitTop(Cut: 20.0f, pTop: &Button, pBottom: &MainView); |
| 1037 | if(DoButton_CheckBox(pId: &g_Config.m_ClShowfps, pText: Localize(pStr: "Show FPS" ), Checked: g_Config.m_ClShowfps, pRect: &Button)) |
| 1038 | g_Config.m_ClShowfps ^= 1; |
| 1039 | GameClient()->m_Tooltips.DoToolTip(pId: &g_Config.m_ClShowfps, pNearRect: &Button, pText: Localize(pStr: "Renders your frame rate in the top right" )); |
| 1040 | |
| 1041 | MainView.HSplitTop(Cut: 20.0f, pTop: &Button, pBottom: &MainView); |
| 1042 | str_copy(dst&: aBuf, src: " " ); |
| 1043 | str_append(dst&: aBuf, src: Localize(pStr: "Hz" , pContext: "Hertz" )); |
| 1044 | Ui()->DoScrollbarOption(pId: &g_Config.m_GfxRefreshRate, pOption: &g_Config.m_GfxRefreshRate, pRect: &Button, pStr: Localize(pStr: "Refresh Rate" ), Min: 10, Max: 1000, pScale: &CUi::ms_LinearScrollbarScale, Flags: CUi::SCROLLBAR_OPTION_INFINITE | CUi::SCROLLBAR_OPTION_NOCLAMPVALUE | CUi::SCROLLBAR_OPTION_DELAYUPDATE, pSuffix: aBuf); |
| 1045 | |
| 1046 | MainView.HSplitTop(Cut: 2.0f, pTop: nullptr, pBottom: &MainView); |
| 1047 | static CButtonContainer s_UiColorResetId; |
| 1048 | DoLine_ColorPicker(pResetId: &s_UiColorResetId, LineSize: 25.0f, LabelSize: 13.0f, BottomMargin: 2.0f, pMainRect: &MainView, pText: Localize(pStr: "UI Color" ), pColorValue: &g_Config.m_UiColor, DefaultColor: color_cast<ColorRGBA>(hsl: ColorHSLA(0xE4A046AFU, true)), CheckBoxSpacing: false, pCheckBoxValue: nullptr, Alpha: true); |
| 1049 | |
| 1050 | // Backend list |
| 1051 | struct |
| 1052 | { |
| 1053 | int m_Major = 0; |
| 1054 | int m_Minor = 0; |
| 1055 | int m_Patch = 0; |
| 1056 | const char *m_pBackendName = "" ; |
| 1057 | bool m_Found = false; |
| 1058 | }; |
| 1059 | std::array<std::array<SMenuBackendInfo, EGraphicsDriverAgeType::GRAPHICS_DRIVER_AGE_TYPE_COUNT>, EBackendType::BACKEND_TYPE_COUNT> aaSupportedBackends{}; |
| 1060 | uint32_t FoundBackendCount = 0; |
| 1061 | for(uint32_t i = 0; i < BACKEND_TYPE_COUNT; ++i) |
| 1062 | { |
| 1063 | if(EBackendType(i) == BACKEND_TYPE_AUTO) |
| 1064 | continue; |
| 1065 | for(uint32_t n = 0; n < GRAPHICS_DRIVER_AGE_TYPE_COUNT; ++n) |
| 1066 | { |
| 1067 | auto &Info = aaSupportedBackends[i][n]; |
| 1068 | if(Graphics()->GetDriverVersion(DriverAgeType: EGraphicsDriverAgeType(n), Major&: Info.m_Major, Minor&: Info.m_Minor, Patch&: Info.m_Patch, pName&: Info.m_pBackendName, BackendType: EBackendType(i))) |
| 1069 | { |
| 1070 | // don't count blocked opengl drivers |
| 1071 | if(EBackendType(i) != BACKEND_TYPE_OPENGL || EGraphicsDriverAgeType(n) == GRAPHICS_DRIVER_AGE_TYPE_LEGACY || g_Config.m_GfxDriverIsBlocked == 0) |
| 1072 | { |
| 1073 | Info.m_Found = true; |
| 1074 | ++FoundBackendCount; |
| 1075 | } |
| 1076 | } |
| 1077 | } |
| 1078 | } |
| 1079 | |
| 1080 | if(FoundBackendCount > 1) |
| 1081 | { |
| 1082 | CUIRect Text, BackendDropDown; |
| 1083 | MainView.HSplitTop(Cut: 10.0f, pTop: nullptr, pBottom: &MainView); |
| 1084 | MainView.HSplitTop(Cut: 20.0f, pTop: &Text, pBottom: &MainView); |
| 1085 | MainView.HSplitTop(Cut: 2.0f, pTop: nullptr, pBottom: &MainView); |
| 1086 | MainView.HSplitTop(Cut: 20.0f, pTop: &BackendDropDown, pBottom: &MainView); |
| 1087 | Ui()->DoLabel(pRect: &Text, pText: Localize(pStr: "Renderer" ), Size: 16.0f, Align: TEXTALIGN_MC); |
| 1088 | |
| 1089 | static std::vector<std::string> s_vBackendIdNames; |
| 1090 | static std::vector<const char *> s_vpBackendIdNamesCStr; |
| 1091 | static std::vector<SMenuBackendInfo> s_vBackendInfos; |
| 1092 | |
| 1093 | size_t BackendCount = FoundBackendCount + 1; |
| 1094 | s_vBackendIdNames.resize(new_size: BackendCount); |
| 1095 | s_vpBackendIdNamesCStr.resize(new_size: BackendCount); |
| 1096 | s_vBackendInfos.resize(new_size: BackendCount); |
| 1097 | |
| 1098 | char aTmpBackendName[256]; |
| 1099 | |
| 1100 | auto IsInfoDefault = [](const SMenuBackendInfo &CheckInfo) { |
| 1101 | return str_comp_nocase(a: CheckInfo.m_pBackendName, b: CConfig::ms_pGfxBackend) == 0 && CheckInfo.m_Major == CConfig::ms_GfxGLMajor && CheckInfo.m_Minor == CConfig::ms_GfxGLMinor && CheckInfo.m_Patch == CConfig::ms_GfxGLPatch; |
| 1102 | }; |
| 1103 | |
| 1104 | int OldSelectedBackend = -1; |
| 1105 | uint32_t CurCounter = 0; |
| 1106 | for(uint32_t i = 0; i < BACKEND_TYPE_COUNT; ++i) |
| 1107 | { |
| 1108 | for(uint32_t n = 0; n < GRAPHICS_DRIVER_AGE_TYPE_COUNT; ++n) |
| 1109 | { |
| 1110 | auto &Info = aaSupportedBackends[i][n]; |
| 1111 | if(Info.m_Found) |
| 1112 | { |
| 1113 | bool IsDefault = IsInfoDefault(Info); |
| 1114 | str_format(buffer: aTmpBackendName, buffer_size: sizeof(aTmpBackendName), format: "%s (%d.%d.%d)%s%s" , Info.m_pBackendName, Info.m_Major, Info.m_Minor, Info.m_Patch, IsDefault ? " - " : "" , IsDefault ? Localize(pStr: "default" ) : "" ); |
| 1115 | s_vBackendIdNames[CurCounter] = aTmpBackendName; |
| 1116 | s_vpBackendIdNamesCStr[CurCounter] = s_vBackendIdNames[CurCounter].c_str(); |
| 1117 | if(str_comp_nocase(a: Info.m_pBackendName, b: g_Config.m_GfxBackend) == 0 && g_Config.m_GfxGLMajor == Info.m_Major && g_Config.m_GfxGLMinor == Info.m_Minor && g_Config.m_GfxGLPatch == Info.m_Patch) |
| 1118 | { |
| 1119 | OldSelectedBackend = CurCounter; |
| 1120 | } |
| 1121 | |
| 1122 | s_vBackendInfos[CurCounter] = Info; |
| 1123 | ++CurCounter; |
| 1124 | } |
| 1125 | } |
| 1126 | } |
| 1127 | |
| 1128 | if(OldSelectedBackend != -1) |
| 1129 | { |
| 1130 | // no custom selected |
| 1131 | BackendCount -= 1; |
| 1132 | } |
| 1133 | else |
| 1134 | { |
| 1135 | // custom selected one |
| 1136 | str_format(buffer: aTmpBackendName, buffer_size: sizeof(aTmpBackendName), format: "%s (%s %d.%d.%d)" , Localize(pStr: "custom" ), g_Config.m_GfxBackend, g_Config.m_GfxGLMajor, g_Config.m_GfxGLMinor, g_Config.m_GfxGLPatch); |
| 1137 | s_vBackendIdNames[CurCounter] = aTmpBackendName; |
| 1138 | s_vpBackendIdNamesCStr[CurCounter] = s_vBackendIdNames[CurCounter].c_str(); |
| 1139 | OldSelectedBackend = CurCounter; |
| 1140 | |
| 1141 | s_vBackendInfos[CurCounter].m_pBackendName = "custom" ; |
| 1142 | s_vBackendInfos[CurCounter].m_Major = g_Config.m_GfxGLMajor; |
| 1143 | s_vBackendInfos[CurCounter].m_Minor = g_Config.m_GfxGLMinor; |
| 1144 | s_vBackendInfos[CurCounter].m_Patch = g_Config.m_GfxGLPatch; |
| 1145 | } |
| 1146 | |
| 1147 | static int s_OldSelectedBackend = -1; |
| 1148 | if(s_OldSelectedBackend == -1) |
| 1149 | s_OldSelectedBackend = OldSelectedBackend; |
| 1150 | |
| 1151 | static CUi::SDropDownState s_BackendDropDownState; |
| 1152 | static CScrollRegion s_BackendDropDownScrollRegion; |
| 1153 | s_BackendDropDownState.m_SelectionPopupContext.m_pScrollRegion = &s_BackendDropDownScrollRegion; |
| 1154 | const int NewBackend = Ui()->DoDropDown(pRect: &BackendDropDown, CurSelection: OldSelectedBackend, pStrs: s_vpBackendIdNamesCStr.data(), Num: BackendCount, State&: s_BackendDropDownState); |
| 1155 | if(OldSelectedBackend != NewBackend) |
| 1156 | { |
| 1157 | str_copy(dst&: g_Config.m_GfxBackend, src: s_vBackendInfos[NewBackend].m_pBackendName); |
| 1158 | g_Config.m_GfxGLMajor = s_vBackendInfos[NewBackend].m_Major; |
| 1159 | g_Config.m_GfxGLMinor = s_vBackendInfos[NewBackend].m_Minor; |
| 1160 | g_Config.m_GfxGLPatch = s_vBackendInfos[NewBackend].m_Patch; |
| 1161 | |
| 1162 | CheckSettings = true; |
| 1163 | s_GfxBackendChanged = s_OldSelectedBackend != NewBackend; |
| 1164 | } |
| 1165 | } |
| 1166 | |
| 1167 | // GPU list |
| 1168 | const auto &GpuList = Graphics()->GetGpus(); |
| 1169 | if(GpuList.m_vGpus.size() > 1) |
| 1170 | { |
| 1171 | CUIRect Text, GpuDropDown; |
| 1172 | MainView.HSplitTop(Cut: 10.0f, pTop: nullptr, pBottom: &MainView); |
| 1173 | MainView.HSplitTop(Cut: 20.0f, pTop: &Text, pBottom: &MainView); |
| 1174 | MainView.HSplitTop(Cut: 2.0f, pTop: nullptr, pBottom: &MainView); |
| 1175 | MainView.HSplitTop(Cut: 20.0f, pTop: &GpuDropDown, pBottom: &MainView); |
| 1176 | Ui()->DoLabel(pRect: &Text, pText: Localize(pStr: "Graphics card" ), Size: 16.0f, Align: TEXTALIGN_MC); |
| 1177 | |
| 1178 | static std::vector<const char *> s_vpGpuIdNames; |
| 1179 | |
| 1180 | size_t GpuCount = GpuList.m_vGpus.size() + 1; |
| 1181 | s_vpGpuIdNames.resize(new_size: GpuCount); |
| 1182 | |
| 1183 | char aCurDeviceName[256 + 4]; |
| 1184 | |
| 1185 | int OldSelectedGpu = -1; |
| 1186 | for(size_t i = 0; i < GpuCount; ++i) |
| 1187 | { |
| 1188 | if(i == 0) |
| 1189 | { |
| 1190 | str_format(buffer: aCurDeviceName, buffer_size: sizeof(aCurDeviceName), format: "%s (%s)" , Localize(pStr: "auto" ), GpuList.m_AutoGpu.m_aName); |
| 1191 | s_vpGpuIdNames[i] = aCurDeviceName; |
| 1192 | if(str_comp(a: "auto" , b: g_Config.m_GfxGpuName) == 0) |
| 1193 | { |
| 1194 | OldSelectedGpu = 0; |
| 1195 | } |
| 1196 | } |
| 1197 | else |
| 1198 | { |
| 1199 | s_vpGpuIdNames[i] = GpuList.m_vGpus[i - 1].m_aName; |
| 1200 | if(str_comp(a: GpuList.m_vGpus[i - 1].m_aName, b: g_Config.m_GfxGpuName) == 0) |
| 1201 | { |
| 1202 | OldSelectedGpu = i; |
| 1203 | } |
| 1204 | } |
| 1205 | } |
| 1206 | |
| 1207 | static int s_OldSelectedGpu = -1; |
| 1208 | if(s_OldSelectedGpu == -1) |
| 1209 | s_OldSelectedGpu = OldSelectedGpu; |
| 1210 | |
| 1211 | static CUi::SDropDownState s_GpuDropDownState; |
| 1212 | static CScrollRegion s_GpuDropDownScrollRegion; |
| 1213 | s_GpuDropDownState.m_SelectionPopupContext.m_pScrollRegion = &s_GpuDropDownScrollRegion; |
| 1214 | const int NewGpu = Ui()->DoDropDown(pRect: &GpuDropDown, CurSelection: OldSelectedGpu, pStrs: s_vpGpuIdNames.data(), Num: GpuCount, State&: s_GpuDropDownState); |
| 1215 | if(OldSelectedGpu != NewGpu) |
| 1216 | { |
| 1217 | if(NewGpu == 0) |
| 1218 | str_copy(dst&: g_Config.m_GfxGpuName, src: "auto" ); |
| 1219 | else |
| 1220 | str_copy(dst&: g_Config.m_GfxGpuName, src: GpuList.m_vGpus[NewGpu - 1].m_aName); |
| 1221 | CheckSettings = true; |
| 1222 | s_GfxGpuChanged = NewGpu != s_OldSelectedGpu; |
| 1223 | } |
| 1224 | } |
| 1225 | |
| 1226 | // check if the new settings require a restart |
| 1227 | if(CheckSettings) |
| 1228 | { |
| 1229 | m_NeedRestartGraphics = !(s_GfxFsaaSamples == g_Config.m_GfxFsaaSamples && |
| 1230 | !s_GfxBackendChanged && |
| 1231 | !s_GfxGpuChanged); |
| 1232 | } |
| 1233 | } |
| 1234 | |
| 1235 | void CMenus::(CUIRect MainView) |
| 1236 | { |
| 1237 | static int s_SndEnable = g_Config.m_SndEnable; |
| 1238 | |
| 1239 | CUIRect Button; |
| 1240 | MainView.HSplitTop(Cut: 20.0f, pTop: &Button, pBottom: &MainView); |
| 1241 | if(DoButton_CheckBox(pId: &g_Config.m_SndEnable, pText: Localize(pStr: "Use sounds" ), Checked: g_Config.m_SndEnable, pRect: &Button)) |
| 1242 | { |
| 1243 | g_Config.m_SndEnable ^= 1; |
| 1244 | UpdateMusicState(); |
| 1245 | m_NeedRestartSound = g_Config.m_SndEnable && !s_SndEnable; |
| 1246 | } |
| 1247 | |
| 1248 | if(!g_Config.m_SndEnable) |
| 1249 | return; |
| 1250 | |
| 1251 | MainView.HSplitTop(Cut: 20.0f, pTop: &Button, pBottom: &MainView); |
| 1252 | if(DoButton_CheckBox(pId: &g_Config.m_SndMusic, pText: Localize(pStr: "Play background music" ), Checked: g_Config.m_SndMusic, pRect: &Button)) |
| 1253 | { |
| 1254 | g_Config.m_SndMusic ^= 1; |
| 1255 | UpdateMusicState(); |
| 1256 | } |
| 1257 | |
| 1258 | MainView.HSplitTop(Cut: 20.0f, pTop: &Button, pBottom: &MainView); |
| 1259 | if(DoButton_CheckBox(pId: &g_Config.m_SndNonactiveMute, pText: Localize(pStr: "Mute when not active" ), Checked: g_Config.m_SndNonactiveMute, pRect: &Button)) |
| 1260 | g_Config.m_SndNonactiveMute ^= 1; |
| 1261 | |
| 1262 | MainView.HSplitTop(Cut: 20.0f, pTop: &Button, pBottom: &MainView); |
| 1263 | if(DoButton_CheckBox(pId: &g_Config.m_SndGame, pText: Localize(pStr: "Enable game sounds" ), Checked: g_Config.m_SndGame, pRect: &Button)) |
| 1264 | g_Config.m_SndGame ^= 1; |
| 1265 | |
| 1266 | MainView.HSplitTop(Cut: 20.0f, pTop: &Button, pBottom: &MainView); |
| 1267 | if(DoButton_CheckBox(pId: &g_Config.m_SndGun, pText: Localize(pStr: "Enable gun sound" ), Checked: g_Config.m_SndGun, pRect: &Button)) |
| 1268 | g_Config.m_SndGun ^= 1; |
| 1269 | |
| 1270 | MainView.HSplitTop(Cut: 20.0f, pTop: &Button, pBottom: &MainView); |
| 1271 | if(DoButton_CheckBox(pId: &g_Config.m_SndLongPain, pText: Localize(pStr: "Enable long pain sound (used when shooting in freeze)" ), Checked: g_Config.m_SndLongPain, pRect: &Button)) |
| 1272 | g_Config.m_SndLongPain ^= 1; |
| 1273 | |
| 1274 | MainView.HSplitTop(Cut: 20.0f, pTop: &Button, pBottom: &MainView); |
| 1275 | if(DoButton_CheckBox(pId: &g_Config.m_SndServerMessage, pText: Localize(pStr: "Enable server message sound" ), Checked: g_Config.m_SndServerMessage, pRect: &Button)) |
| 1276 | g_Config.m_SndServerMessage ^= 1; |
| 1277 | |
| 1278 | MainView.HSplitTop(Cut: 20.0f, pTop: &Button, pBottom: &MainView); |
| 1279 | if(DoButton_CheckBox(pId: &g_Config.m_SndChat, pText: Localize(pStr: "Enable regular chat sound" ), Checked: g_Config.m_SndChat, pRect: &Button)) |
| 1280 | g_Config.m_SndChat ^= 1; |
| 1281 | |
| 1282 | MainView.HSplitTop(Cut: 20.0f, pTop: &Button, pBottom: &MainView); |
| 1283 | if(DoButton_CheckBox(pId: &g_Config.m_SndTeamChat, pText: Localize(pStr: "Enable team chat sound" ), Checked: g_Config.m_SndTeamChat, pRect: &Button)) |
| 1284 | g_Config.m_SndTeamChat ^= 1; |
| 1285 | |
| 1286 | MainView.HSplitTop(Cut: 20.0f, pTop: &Button, pBottom: &MainView); |
| 1287 | if(DoButton_CheckBox(pId: &g_Config.m_SndHighlight, pText: Localize(pStr: "Enable highlighted chat sound" ), Checked: g_Config.m_SndHighlight, pRect: &Button)) |
| 1288 | g_Config.m_SndHighlight ^= 1; |
| 1289 | |
| 1290 | // volume slider |
| 1291 | { |
| 1292 | MainView.HSplitTop(Cut: 5.0f, pTop: nullptr, pBottom: &MainView); |
| 1293 | MainView.HSplitTop(Cut: 20.0f, pTop: &Button, pBottom: &MainView); |
| 1294 | Ui()->DoScrollbarOption(pId: &g_Config.m_SndVolume, pOption: &g_Config.m_SndVolume, pRect: &Button, pStr: Localize(pStr: "Sound volume" ), Min: 0, Max: 100, pScale: &CUi::ms_LogarithmicScrollbarScale, Flags: 0u, pSuffix: "%" ); |
| 1295 | } |
| 1296 | |
| 1297 | // volume slider game sounds |
| 1298 | { |
| 1299 | MainView.HSplitTop(Cut: 5.0f, pTop: nullptr, pBottom: &MainView); |
| 1300 | MainView.HSplitTop(Cut: 20.0f, pTop: &Button, pBottom: &MainView); |
| 1301 | Ui()->DoScrollbarOption(pId: &g_Config.m_SndGameVolume, pOption: &g_Config.m_SndGameVolume, pRect: &Button, pStr: Localize(pStr: "Game sound volume" ), Min: 0, Max: 100, pScale: &CUi::ms_LogarithmicScrollbarScale, Flags: 0u, pSuffix: "%" ); |
| 1302 | } |
| 1303 | |
| 1304 | // volume slider gui sounds |
| 1305 | { |
| 1306 | MainView.HSplitTop(Cut: 5.0f, pTop: nullptr, pBottom: &MainView); |
| 1307 | MainView.HSplitTop(Cut: 20.0f, pTop: &Button, pBottom: &MainView); |
| 1308 | Ui()->DoScrollbarOption(pId: &g_Config.m_SndChatVolume, pOption: &g_Config.m_SndChatVolume, pRect: &Button, pStr: Localize(pStr: "Chat sound volume" ), Min: 0, Max: 100, pScale: &CUi::ms_LogarithmicScrollbarScale, Flags: 0u, pSuffix: "%" ); |
| 1309 | } |
| 1310 | |
| 1311 | // volume slider map sounds |
| 1312 | { |
| 1313 | MainView.HSplitTop(Cut: 5.0f, pTop: nullptr, pBottom: &MainView); |
| 1314 | MainView.HSplitTop(Cut: 20.0f, pTop: &Button, pBottom: &MainView); |
| 1315 | Ui()->DoScrollbarOption(pId: &g_Config.m_SndMapVolume, pOption: &g_Config.m_SndMapVolume, pRect: &Button, pStr: Localize(pStr: "Map sound volume" ), Min: 0, Max: 100, pScale: &CUi::ms_LogarithmicScrollbarScale, Flags: 0u, pSuffix: "%" ); |
| 1316 | } |
| 1317 | |
| 1318 | // volume slider background music |
| 1319 | { |
| 1320 | MainView.HSplitTop(Cut: 5.0f, pTop: nullptr, pBottom: &MainView); |
| 1321 | MainView.HSplitTop(Cut: 20.0f, pTop: &Button, pBottom: &MainView); |
| 1322 | Ui()->DoScrollbarOption(pId: &g_Config.m_SndBackgroundMusicVolume, pOption: &g_Config.m_SndBackgroundMusicVolume, pRect: &Button, pStr: Localize(pStr: "Background music volume" ), Min: 0, Max: 100, pScale: &CUi::ms_LogarithmicScrollbarScale, Flags: 0u, pSuffix: "%" ); |
| 1323 | } |
| 1324 | } |
| 1325 | |
| 1326 | void CMenus::(CUIRect MainView) |
| 1327 | { |
| 1328 | const float CreditsFontSize = 14.0f; |
| 1329 | const float CreditsMargin = 10.0f; |
| 1330 | |
| 1331 | CUIRect List, CreditsScroll; |
| 1332 | MainView.HSplitBottom(Cut: 4.0f * CreditsFontSize + 2.0f * CreditsMargin + CScrollRegion::HEIGHT_MAGIC_FIX, pTop: &List, pBottom: &CreditsScroll); |
| 1333 | List.HSplitBottom(Cut: 5.0f, pTop: &List, pBottom: nullptr); |
| 1334 | |
| 1335 | RenderLanguageSelection(MainView: List); |
| 1336 | |
| 1337 | CreditsScroll.Draw(Color: ColorRGBA(0.0f, 0.0f, 0.0f, 0.25f), Corners: IGraphics::CORNER_ALL, Rounding: 5.0f); |
| 1338 | |
| 1339 | static CScrollRegion s_CreditsScrollRegion; |
| 1340 | vec2 ScrollOffset(0.0f, 0.0f); |
| 1341 | CScrollRegionParams ScrollParams; |
| 1342 | ScrollParams.m_ScrollUnit = CreditsFontSize; |
| 1343 | s_CreditsScrollRegion.Begin(pClipRect: &CreditsScroll, pOutOffset: &ScrollOffset, pParams: &ScrollParams); |
| 1344 | CreditsScroll.y += ScrollOffset.y; |
| 1345 | |
| 1346 | CTextCursor Cursor; |
| 1347 | Cursor.m_FontSize = CreditsFontSize; |
| 1348 | Cursor.m_LineWidth = CreditsScroll.w - 2.0f * CreditsMargin; |
| 1349 | |
| 1350 | const unsigned OldRenderFlags = TextRender()->GetRenderFlags(); |
| 1351 | TextRender()->SetRenderFlags(OldRenderFlags | TEXT_RENDER_FLAG_ONE_TIME_USE); |
| 1352 | STextContainerIndex CreditsTextContainer; |
| 1353 | TextRender()->CreateTextContainer(TextContainerIndex&: CreditsTextContainer, pCursor: &Cursor, pText: Localize(pStr: "English translation by the DDNet Team" , pContext: "Translation credits: Add your own name here when you update translations" )); |
| 1354 | TextRender()->SetRenderFlags(OldRenderFlags); |
| 1355 | if(CreditsTextContainer.Valid()) |
| 1356 | { |
| 1357 | CUIRect CreditsLabel; |
| 1358 | CreditsScroll.HSplitTop(Cut: TextRender()->GetBoundingBoxTextContainer(TextContainerIndex: CreditsTextContainer).m_H + 2.0f * CreditsMargin, pTop: &CreditsLabel, pBottom: &CreditsScroll); |
| 1359 | s_CreditsScrollRegion.AddRect(Rect: CreditsLabel); |
| 1360 | CreditsLabel.Margin(Cut: CreditsMargin, pOtherRect: &CreditsLabel); |
| 1361 | TextRender()->RenderTextContainer(TextContainerIndex: CreditsTextContainer, TextColor: TextRender()->DefaultTextColor(), TextOutlineColor: TextRender()->DefaultTextOutlineColor(), X: CreditsLabel.x, Y: CreditsLabel.y); |
| 1362 | TextRender()->DeleteTextContainer(TextContainerIndex&: CreditsTextContainer); |
| 1363 | } |
| 1364 | |
| 1365 | s_CreditsScrollRegion.End(); |
| 1366 | } |
| 1367 | |
| 1368 | bool CMenus::(CUIRect MainView) |
| 1369 | { |
| 1370 | static int s_SelectedLanguage = -2; // -2 = unloaded, -1 = unset |
| 1371 | static CListBox s_ListBox; |
| 1372 | |
| 1373 | if(s_SelectedLanguage == -2) |
| 1374 | { |
| 1375 | s_SelectedLanguage = -1; |
| 1376 | for(size_t i = 0; i < g_Localization.Languages().size(); i++) |
| 1377 | { |
| 1378 | if(str_comp(a: g_Localization.Languages()[i].m_Filename.c_str(), b: g_Config.m_ClLanguagefile) == 0) |
| 1379 | { |
| 1380 | s_SelectedLanguage = i; |
| 1381 | s_ListBox.ScrollToSelected(); |
| 1382 | break; |
| 1383 | } |
| 1384 | } |
| 1385 | } |
| 1386 | |
| 1387 | const int OldSelected = s_SelectedLanguage; |
| 1388 | |
| 1389 | s_ListBox.DoStart(RowHeight: 24.0f, NumItems: g_Localization.Languages().size(), ItemsPerRow: 1, RowsPerScroll: 3, SelectedIndex: s_SelectedLanguage, pRect: &MainView); |
| 1390 | |
| 1391 | for(const auto &Language : g_Localization.Languages()) |
| 1392 | { |
| 1393 | const CListboxItem Item = s_ListBox.DoNextItem(pId: &Language.m_Name, Selected: s_SelectedLanguage != -1 && !str_comp(a: g_Localization.Languages()[s_SelectedLanguage].m_Name.c_str(), b: Language.m_Name.c_str())); |
| 1394 | if(!Item.m_Visible) |
| 1395 | continue; |
| 1396 | |
| 1397 | CUIRect FlagRect, Label; |
| 1398 | Item.m_Rect.VSplitLeft(Cut: Item.m_Rect.h * 2.0f, pLeft: &FlagRect, pRight: &Label); |
| 1399 | FlagRect.VMargin(Cut: 6.0f, pOtherRect: &FlagRect); |
| 1400 | FlagRect.HMargin(Cut: 3.0f, pOtherRect: &FlagRect); |
| 1401 | GameClient()->m_CountryFlags.Render(CountryCode: Language.m_CountryCode, Color: ColorRGBA(1.0f, 1.0f, 1.0f, 1.0f), x: FlagRect.x, y: FlagRect.y, w: FlagRect.w, h: FlagRect.h); |
| 1402 | |
| 1403 | Ui()->DoLabel(pRect: &Label, pText: Language.m_Name.c_str(), Size: 16.0f, Align: TEXTALIGN_ML); |
| 1404 | } |
| 1405 | |
| 1406 | s_SelectedLanguage = s_ListBox.DoEnd(); |
| 1407 | |
| 1408 | if(OldSelected != s_SelectedLanguage) |
| 1409 | { |
| 1410 | str_copy(dst&: g_Config.m_ClLanguagefile, src: g_Localization.Languages()[s_SelectedLanguage].m_Filename.c_str()); |
| 1411 | GameClient()->OnLanguageChange(); |
| 1412 | } |
| 1413 | |
| 1414 | return s_ListBox.WasItemActivated(); |
| 1415 | } |
| 1416 | |
| 1417 | void CMenus::(CUIRect MainView) |
| 1418 | { |
| 1419 | // render background |
| 1420 | CUIRect Button, TabBar, RestartBar; |
| 1421 | MainView.VSplitRight(Cut: 120.0f, pLeft: &MainView, pRight: &TabBar); |
| 1422 | MainView.Draw(Color: ms_ColorTabbarActive, Corners: IGraphics::CORNER_B, Rounding: 10.0f); |
| 1423 | MainView.Margin(Cut: 20.0f, pOtherRect: &MainView); |
| 1424 | |
| 1425 | const bool NeedRestart = m_NeedRestartGraphics || m_NeedRestartSound || m_NeedRestartUpdate; |
| 1426 | if(NeedRestart) |
| 1427 | { |
| 1428 | MainView.HSplitBottom(Cut: 20.0f, pTop: &MainView, pBottom: &RestartBar); |
| 1429 | MainView.HSplitBottom(Cut: 10.0f, pTop: &MainView, pBottom: nullptr); |
| 1430 | } |
| 1431 | |
| 1432 | TabBar.HSplitTop(Cut: 50.0f, pTop: &Button, pBottom: &TabBar); |
| 1433 | Button.Draw(Color: ms_ColorTabbarActive, Corners: IGraphics::CORNER_BR, Rounding: 10.0f); |
| 1434 | |
| 1435 | const char *apTabs[SETTINGS_LENGTH] = { |
| 1436 | Localize(pStr: "Language" ), |
| 1437 | Localize(pStr: "General" ), |
| 1438 | Localize(pStr: "Player" ), |
| 1439 | Client()->IsSixup() ? "Tee 0.7" : Localize(pStr: "Tee" ), |
| 1440 | Localize(pStr: "Appearance" ), |
| 1441 | Localize(pStr: "Controls" ), |
| 1442 | Localize(pStr: "Graphics" ), |
| 1443 | Localize(pStr: "Sound" ), |
| 1444 | Localize(pStr: "DDNet" ), |
| 1445 | Localize(pStr: "Assets" )}; |
| 1446 | static CButtonContainer s_aTabButtons[SETTINGS_LENGTH]; |
| 1447 | |
| 1448 | for(int i = 0; i < SETTINGS_LENGTH; i++) |
| 1449 | { |
| 1450 | TabBar.HSplitTop(Cut: 10.0f, pTop: nullptr, pBottom: &TabBar); |
| 1451 | TabBar.HSplitTop(Cut: 26.0f, pTop: &Button, pBottom: &TabBar); |
| 1452 | if(DoButton_MenuTab(pButtonContainer: &s_aTabButtons[i], pText: apTabs[i], Checked: g_Config.m_UiSettingsPage == i, pRect: &Button, Corners: IGraphics::CORNER_R, pAnimator: &m_aAnimatorsSettingsTab[i])) |
| 1453 | g_Config.m_UiSettingsPage = i; |
| 1454 | } |
| 1455 | |
| 1456 | if(g_Config.m_UiSettingsPage == SETTINGS_LANGUAGE) |
| 1457 | { |
| 1458 | GameClient()->m_MenuBackground.ChangePosition(PositionNumber: CMenuBackground::POS_SETTINGS_LANGUAGE); |
| 1459 | RenderLanguageSettings(MainView); |
| 1460 | } |
| 1461 | else if(g_Config.m_UiSettingsPage == SETTINGS_GENERAL) |
| 1462 | { |
| 1463 | GameClient()->m_MenuBackground.ChangePosition(PositionNumber: CMenuBackground::POS_SETTINGS_GENERAL); |
| 1464 | RenderSettingsGeneral(MainView); |
| 1465 | } |
| 1466 | else if(g_Config.m_UiSettingsPage == SETTINGS_PLAYER) |
| 1467 | { |
| 1468 | GameClient()->m_MenuBackground.ChangePosition(PositionNumber: CMenuBackground::POS_SETTINGS_PLAYER); |
| 1469 | RenderSettingsPlayer(MainView); |
| 1470 | } |
| 1471 | else if(g_Config.m_UiSettingsPage == SETTINGS_TEE) |
| 1472 | { |
| 1473 | GameClient()->m_MenuBackground.ChangePosition(PositionNumber: CMenuBackground::POS_SETTINGS_TEE); |
| 1474 | if(Client()->IsSixup()) |
| 1475 | RenderSettingsTee7(MainView); |
| 1476 | else |
| 1477 | RenderSettingsTee(MainView); |
| 1478 | } |
| 1479 | else if(g_Config.m_UiSettingsPage == SETTINGS_APPEARANCE) |
| 1480 | { |
| 1481 | GameClient()->m_MenuBackground.ChangePosition(PositionNumber: CMenuBackground::POS_SETTINGS_APPEARANCE); |
| 1482 | RenderSettingsAppearance(MainView); |
| 1483 | } |
| 1484 | else if(g_Config.m_UiSettingsPage == SETTINGS_CONTROLS) |
| 1485 | { |
| 1486 | GameClient()->m_MenuBackground.ChangePosition(PositionNumber: CMenuBackground::POS_SETTINGS_CONTROLS); |
| 1487 | m_MenusSettingsControls.Render(MainView); |
| 1488 | } |
| 1489 | else if(g_Config.m_UiSettingsPage == SETTINGS_GRAPHICS) |
| 1490 | { |
| 1491 | GameClient()->m_MenuBackground.ChangePosition(PositionNumber: CMenuBackground::POS_SETTINGS_GRAPHICS); |
| 1492 | RenderSettingsGraphics(MainView); |
| 1493 | } |
| 1494 | else if(g_Config.m_UiSettingsPage == SETTINGS_SOUND) |
| 1495 | { |
| 1496 | GameClient()->m_MenuBackground.ChangePosition(PositionNumber: CMenuBackground::POS_SETTINGS_SOUND); |
| 1497 | RenderSettingsSound(MainView); |
| 1498 | } |
| 1499 | else if(g_Config.m_UiSettingsPage == SETTINGS_DDNET) |
| 1500 | { |
| 1501 | GameClient()->m_MenuBackground.ChangePosition(PositionNumber: CMenuBackground::POS_SETTINGS_DDNET); |
| 1502 | RenderSettingsDDNet(MainView); |
| 1503 | } |
| 1504 | else if(g_Config.m_UiSettingsPage == SETTINGS_ASSETS) |
| 1505 | { |
| 1506 | GameClient()->m_MenuBackground.ChangePosition(PositionNumber: CMenuBackground::POS_SETTINGS_ASSETS); |
| 1507 | RenderSettingsCustom(MainView); |
| 1508 | } |
| 1509 | else |
| 1510 | { |
| 1511 | dbg_assert_failed("ui_settings_page invalid" ); |
| 1512 | } |
| 1513 | |
| 1514 | if(NeedRestart) |
| 1515 | { |
| 1516 | CUIRect RestartWarning, RestartButton; |
| 1517 | RestartBar.VSplitRight(Cut: 125.0f, pLeft: &RestartWarning, pRight: &RestartButton); |
| 1518 | RestartWarning.VSplitRight(Cut: 10.0f, pLeft: &RestartWarning, pRight: nullptr); |
| 1519 | if(m_NeedRestartUpdate) |
| 1520 | { |
| 1521 | TextRender()->TextColor(r: 1.0f, g: 0.4f, b: 0.4f, a: 1.0f); |
| 1522 | Ui()->DoLabel(pRect: &RestartWarning, pText: Localize(pStr: "DDNet Client needs to be restarted to complete update!" ), Size: 14.0f, Align: TEXTALIGN_ML); |
| 1523 | TextRender()->TextColor(r: 1.0f, g: 1.0f, b: 1.0f, a: 1.0f); |
| 1524 | } |
| 1525 | else |
| 1526 | { |
| 1527 | Ui()->DoLabel(pRect: &RestartWarning, pText: Localize(pStr: "You must restart the game for all settings to take effect." ), Size: 14.0f, Align: TEXTALIGN_ML); |
| 1528 | } |
| 1529 | |
| 1530 | static CButtonContainer s_RestartButton; |
| 1531 | if(DoButton_Menu(pButtonContainer: &s_RestartButton, pText: Localize(pStr: "Restart" ), Checked: 0, pRect: &RestartButton)) |
| 1532 | { |
| 1533 | if(Client()->State() == IClient::STATE_ONLINE || GameClient()->Editor()->HasUnsavedData()) |
| 1534 | { |
| 1535 | m_Popup = POPUP_RESTART; |
| 1536 | } |
| 1537 | else |
| 1538 | { |
| 1539 | Client()->Restart(); |
| 1540 | } |
| 1541 | } |
| 1542 | } |
| 1543 | } |
| 1544 | |
| 1545 | bool CMenus::(CUIRect *pRect, unsigned int *pColor, bool Alpha, float DarkestLight) |
| 1546 | { |
| 1547 | const unsigned PrevPackedColor = *pColor; |
| 1548 | ColorHSLA Color(*pColor, Alpha); |
| 1549 | const ColorHSLA OriginalColor = Color; |
| 1550 | const char *apLabels[] = {Localize(pStr: "Hue" ), Localize(pStr: "Sat." ), Localize(pStr: "Lht." ), Localize(pStr: "Alpha" )}; |
| 1551 | const float SizePerEntry = 20.0f; |
| 1552 | const float MarginPerEntry = 5.0f; |
| 1553 | const float PreviewMargin = 2.5f; |
| 1554 | const float PreviewHeight = 40.0f + 2 * PreviewMargin; |
| 1555 | const float OffY = (SizePerEntry + MarginPerEntry) * (3 + (Alpha ? 1 : 0)) - PreviewHeight; |
| 1556 | |
| 1557 | CUIRect Preview; |
| 1558 | pRect->VSplitLeft(Cut: PreviewHeight, pLeft: &Preview, pRight: pRect); |
| 1559 | Preview.HSplitTop(Cut: OffY / 2.0f, pTop: nullptr, pBottom: &Preview); |
| 1560 | Preview.HSplitTop(Cut: PreviewHeight, pTop: &Preview, pBottom: nullptr); |
| 1561 | |
| 1562 | Preview.Draw(Color: ColorRGBA(0.15f, 0.15f, 0.15f, 1.0f), Corners: IGraphics::CORNER_ALL, Rounding: 4.0f + PreviewMargin); |
| 1563 | Preview.Margin(Cut: PreviewMargin, pOtherRect: &Preview); |
| 1564 | Preview.Draw(Color: color_cast<ColorRGBA>(hsl: Color.UnclampLighting(Darkest: DarkestLight)), Corners: IGraphics::CORNER_ALL, Rounding: 4.0f + PreviewMargin); |
| 1565 | |
| 1566 | auto &&RenderHueRect = [&](CUIRect *pColorRect) { |
| 1567 | float CurXOff = pColorRect->x; |
| 1568 | const float SizeColor = pColorRect->w / 6; |
| 1569 | |
| 1570 | // red to yellow |
| 1571 | { |
| 1572 | IGraphics::CColorVertex aColorVertices[] = { |
| 1573 | IGraphics::CColorVertex(0, 1, 0, 0, 1), |
| 1574 | IGraphics::CColorVertex(1, 1, 1, 0, 1), |
| 1575 | IGraphics::CColorVertex(2, 1, 0, 0, 1), |
| 1576 | IGraphics::CColorVertex(3, 1, 1, 0, 1)}; |
| 1577 | Graphics()->SetColorVertex(pArray: aColorVertices, Num: std::size(aColorVertices)); |
| 1578 | |
| 1579 | IGraphics::CFreeformItem Freeform( |
| 1580 | CurXOff, pColorRect->y, |
| 1581 | CurXOff + SizeColor, pColorRect->y, |
| 1582 | CurXOff, pColorRect->y + pColorRect->h, |
| 1583 | CurXOff + SizeColor, pColorRect->y + pColorRect->h); |
| 1584 | Graphics()->QuadsDrawFreeform(pArray: &Freeform, Num: 1); |
| 1585 | } |
| 1586 | |
| 1587 | // yellow to green |
| 1588 | CurXOff += SizeColor; |
| 1589 | { |
| 1590 | IGraphics::CColorVertex aColorVertices[] = { |
| 1591 | IGraphics::CColorVertex(0, 1, 1, 0, 1), |
| 1592 | IGraphics::CColorVertex(1, 0, 1, 0, 1), |
| 1593 | IGraphics::CColorVertex(2, 1, 1, 0, 1), |
| 1594 | IGraphics::CColorVertex(3, 0, 1, 0, 1)}; |
| 1595 | Graphics()->SetColorVertex(pArray: aColorVertices, Num: std::size(aColorVertices)); |
| 1596 | |
| 1597 | IGraphics::CFreeformItem Freeform( |
| 1598 | CurXOff, pColorRect->y, |
| 1599 | CurXOff + SizeColor, pColorRect->y, |
| 1600 | CurXOff, pColorRect->y + pColorRect->h, |
| 1601 | CurXOff + SizeColor, pColorRect->y + pColorRect->h); |
| 1602 | Graphics()->QuadsDrawFreeform(pArray: &Freeform, Num: 1); |
| 1603 | } |
| 1604 | |
| 1605 | CurXOff += SizeColor; |
| 1606 | // green to turquoise |
| 1607 | { |
| 1608 | IGraphics::CColorVertex aColorVertices[] = { |
| 1609 | IGraphics::CColorVertex(0, 0, 1, 0, 1), |
| 1610 | IGraphics::CColorVertex(1, 0, 1, 1, 1), |
| 1611 | IGraphics::CColorVertex(2, 0, 1, 0, 1), |
| 1612 | IGraphics::CColorVertex(3, 0, 1, 1, 1)}; |
| 1613 | Graphics()->SetColorVertex(pArray: aColorVertices, Num: std::size(aColorVertices)); |
| 1614 | |
| 1615 | IGraphics::CFreeformItem Freeform( |
| 1616 | CurXOff, pColorRect->y, |
| 1617 | CurXOff + SizeColor, pColorRect->y, |
| 1618 | CurXOff, pColorRect->y + pColorRect->h, |
| 1619 | CurXOff + SizeColor, pColorRect->y + pColorRect->h); |
| 1620 | Graphics()->QuadsDrawFreeform(pArray: &Freeform, Num: 1); |
| 1621 | } |
| 1622 | |
| 1623 | CurXOff += SizeColor; |
| 1624 | // turquoise to blue |
| 1625 | { |
| 1626 | IGraphics::CColorVertex aColorVertices[] = { |
| 1627 | IGraphics::CColorVertex(0, 0, 1, 1, 1), |
| 1628 | IGraphics::CColorVertex(1, 0, 0, 1, 1), |
| 1629 | IGraphics::CColorVertex(2, 0, 1, 1, 1), |
| 1630 | IGraphics::CColorVertex(3, 0, 0, 1, 1)}; |
| 1631 | Graphics()->SetColorVertex(pArray: aColorVertices, Num: std::size(aColorVertices)); |
| 1632 | |
| 1633 | IGraphics::CFreeformItem Freeform( |
| 1634 | CurXOff, pColorRect->y, |
| 1635 | CurXOff + SizeColor, pColorRect->y, |
| 1636 | CurXOff, pColorRect->y + pColorRect->h, |
| 1637 | CurXOff + SizeColor, pColorRect->y + pColorRect->h); |
| 1638 | Graphics()->QuadsDrawFreeform(pArray: &Freeform, Num: 1); |
| 1639 | } |
| 1640 | |
| 1641 | CurXOff += SizeColor; |
| 1642 | // blue to purple |
| 1643 | { |
| 1644 | IGraphics::CColorVertex aColorVertices[] = { |
| 1645 | IGraphics::CColorVertex(0, 0, 0, 1, 1), |
| 1646 | IGraphics::CColorVertex(1, 1, 0, 1, 1), |
| 1647 | IGraphics::CColorVertex(2, 0, 0, 1, 1), |
| 1648 | IGraphics::CColorVertex(3, 1, 0, 1, 1)}; |
| 1649 | Graphics()->SetColorVertex(pArray: aColorVertices, Num: std::size(aColorVertices)); |
| 1650 | |
| 1651 | IGraphics::CFreeformItem Freeform( |
| 1652 | CurXOff, pColorRect->y, |
| 1653 | CurXOff + SizeColor, pColorRect->y, |
| 1654 | CurXOff, pColorRect->y + pColorRect->h, |
| 1655 | CurXOff + SizeColor, pColorRect->y + pColorRect->h); |
| 1656 | Graphics()->QuadsDrawFreeform(pArray: &Freeform, Num: 1); |
| 1657 | } |
| 1658 | |
| 1659 | CurXOff += SizeColor; |
| 1660 | // purple to red |
| 1661 | { |
| 1662 | IGraphics::CColorVertex aColorVertices[] = { |
| 1663 | IGraphics::CColorVertex(0, 1, 0, 1, 1), |
| 1664 | IGraphics::CColorVertex(1, 1, 0, 0, 1), |
| 1665 | IGraphics::CColorVertex(2, 1, 0, 1, 1), |
| 1666 | IGraphics::CColorVertex(3, 1, 0, 0, 1)}; |
| 1667 | Graphics()->SetColorVertex(pArray: aColorVertices, Num: std::size(aColorVertices)); |
| 1668 | |
| 1669 | IGraphics::CFreeformItem Freeform( |
| 1670 | CurXOff, pColorRect->y, |
| 1671 | CurXOff + SizeColor, pColorRect->y, |
| 1672 | CurXOff, pColorRect->y + pColorRect->h, |
| 1673 | CurXOff + SizeColor, pColorRect->y + pColorRect->h); |
| 1674 | Graphics()->QuadsDrawFreeform(pArray: &Freeform, Num: 1); |
| 1675 | } |
| 1676 | }; |
| 1677 | |
| 1678 | auto &&RenderSaturationRect = [&](CUIRect *pColorRect, const ColorRGBA &CurColor) { |
| 1679 | ColorHSLA LeftColor = color_cast<ColorHSLA>(rgb: CurColor); |
| 1680 | ColorHSLA RightColor = color_cast<ColorHSLA>(rgb: CurColor); |
| 1681 | |
| 1682 | LeftColor.s = 0.0f; |
| 1683 | RightColor.s = 1.0f; |
| 1684 | |
| 1685 | const ColorRGBA LeftColorRGBA = color_cast<ColorRGBA>(hsl: LeftColor); |
| 1686 | const ColorRGBA RightColorRGBA = color_cast<ColorRGBA>(hsl: RightColor); |
| 1687 | |
| 1688 | Graphics()->SetColor4(TopLeft: LeftColorRGBA, TopRight: RightColorRGBA, BottomLeft: RightColorRGBA, BottomRight: LeftColorRGBA); |
| 1689 | |
| 1690 | IGraphics::CFreeformItem Freeform( |
| 1691 | pColorRect->x, pColorRect->y, |
| 1692 | pColorRect->x + pColorRect->w, pColorRect->y, |
| 1693 | pColorRect->x, pColorRect->y + pColorRect->h, |
| 1694 | pColorRect->x + pColorRect->w, pColorRect->y + pColorRect->h); |
| 1695 | Graphics()->QuadsDrawFreeform(pArray: &Freeform, Num: 1); |
| 1696 | }; |
| 1697 | |
| 1698 | auto &&RenderLightingRect = [&](CUIRect *pColorRect, const ColorRGBA &CurColor) { |
| 1699 | ColorHSLA LeftColor = color_cast<ColorHSLA>(rgb: CurColor); |
| 1700 | ColorHSLA RightColor = color_cast<ColorHSLA>(rgb: CurColor); |
| 1701 | |
| 1702 | LeftColor.l = DarkestLight; |
| 1703 | RightColor.l = 1.0f; |
| 1704 | |
| 1705 | const ColorRGBA LeftColorRGBA = color_cast<ColorRGBA>(hsl: LeftColor); |
| 1706 | const ColorRGBA RightColorRGBA = color_cast<ColorRGBA>(hsl: RightColor); |
| 1707 | |
| 1708 | Graphics()->SetColor4(TopLeft: LeftColorRGBA, TopRight: RightColorRGBA, BottomLeft: RightColorRGBA, BottomRight: LeftColorRGBA); |
| 1709 | |
| 1710 | IGraphics::CFreeformItem Freeform( |
| 1711 | pColorRect->x, pColorRect->y, |
| 1712 | pColorRect->x + pColorRect->w, pColorRect->y, |
| 1713 | pColorRect->x, pColorRect->y + pColorRect->h, |
| 1714 | pColorRect->x + pColorRect->w, pColorRect->y + pColorRect->h); |
| 1715 | Graphics()->QuadsDrawFreeform(pArray: &Freeform, Num: 1); |
| 1716 | }; |
| 1717 | |
| 1718 | auto &&RenderAlphaRect = [&](CUIRect *pColorRect, const ColorRGBA &CurColorFull) { |
| 1719 | const ColorRGBA LeftColorRGBA = color_cast<ColorRGBA>(hsl: color_cast<ColorHSLA>(rgb: CurColorFull).WithAlpha(alpha: 0.0f)); |
| 1720 | const ColorRGBA RightColorRGBA = color_cast<ColorRGBA>(hsl: color_cast<ColorHSLA>(rgb: CurColorFull).WithAlpha(alpha: 1.0f)); |
| 1721 | |
| 1722 | Graphics()->SetColor4(TopLeft: LeftColorRGBA, TopRight: RightColorRGBA, BottomLeft: RightColorRGBA, BottomRight: LeftColorRGBA); |
| 1723 | |
| 1724 | IGraphics::CFreeformItem Freeform( |
| 1725 | pColorRect->x, pColorRect->y, |
| 1726 | pColorRect->x + pColorRect->w, pColorRect->y, |
| 1727 | pColorRect->x, pColorRect->y + pColorRect->h, |
| 1728 | pColorRect->x + pColorRect->w, pColorRect->y + pColorRect->h); |
| 1729 | Graphics()->QuadsDrawFreeform(pArray: &Freeform, Num: 1); |
| 1730 | }; |
| 1731 | |
| 1732 | for(int i = 0; i < 3 + Alpha; i++) |
| 1733 | { |
| 1734 | CUIRect Button, Label; |
| 1735 | pRect->HSplitTop(Cut: SizePerEntry, pTop: &Button, pBottom: pRect); |
| 1736 | pRect->HSplitTop(Cut: MarginPerEntry, pTop: nullptr, pBottom: pRect); |
| 1737 | Button.VSplitLeft(Cut: 10.0f, pLeft: nullptr, pRight: &Button); |
| 1738 | Button.VSplitLeft(Cut: 100.0f, pLeft: &Label, pRight: &Button); |
| 1739 | |
| 1740 | Button.Draw(Color: ColorRGBA(0.15f, 0.15f, 0.15f, 1.0f), Corners: IGraphics::CORNER_ALL, Rounding: 1.0f); |
| 1741 | |
| 1742 | CUIRect Rail; |
| 1743 | Button.Margin(Cut: 2.0f, pOtherRect: &Rail); |
| 1744 | |
| 1745 | char aBuf[32]; |
| 1746 | str_format(buffer: aBuf, buffer_size: sizeof(aBuf), format: "%s: %03d" , apLabels[i], round_to_int(f: Color[i] * 255.0f)); |
| 1747 | Ui()->DoLabel(pRect: &Label, pText: aBuf, Size: 14.0f, Align: TEXTALIGN_ML); |
| 1748 | |
| 1749 | ColorRGBA HandleColor; |
| 1750 | Graphics()->TextureClear(); |
| 1751 | Graphics()->TrianglesBegin(); |
| 1752 | if(i == 0) |
| 1753 | { |
| 1754 | RenderHueRect(&Rail); |
| 1755 | HandleColor = color_cast<ColorRGBA>(hsl: ColorHSLA(Color.h, 1.0f, 0.5f, 1.0f)); |
| 1756 | } |
| 1757 | else if(i == 1) |
| 1758 | { |
| 1759 | RenderSaturationRect(&Rail, color_cast<ColorRGBA>(hsl: ColorHSLA(Color.h, 1.0f, 0.5f, 1.0f))); |
| 1760 | HandleColor = color_cast<ColorRGBA>(hsl: ColorHSLA(Color.h, Color.s, 0.5f, 1.0f)); |
| 1761 | } |
| 1762 | else if(i == 2) |
| 1763 | { |
| 1764 | RenderLightingRect(&Rail, color_cast<ColorRGBA>(hsl: ColorHSLA(Color.h, Color.s, 0.5f, 1.0f))); |
| 1765 | HandleColor = color_cast<ColorRGBA>(hsl: ColorHSLA(Color.h, Color.s, Color.l, 1.0f).UnclampLighting(Darkest: DarkestLight)); |
| 1766 | } |
| 1767 | else if(i == 3) |
| 1768 | { |
| 1769 | RenderAlphaRect(&Rail, color_cast<ColorRGBA>(hsl: ColorHSLA(Color.h, Color.s, Color.l, 1.0f).UnclampLighting(Darkest: DarkestLight))); |
| 1770 | HandleColor = color_cast<ColorRGBA>(hsl: Color.UnclampLighting(Darkest: DarkestLight)); |
| 1771 | } |
| 1772 | Graphics()->TrianglesEnd(); |
| 1773 | |
| 1774 | Color[i] = Ui()->DoScrollbarH(pId: &((char *)pColor)[i], pRect: &Button, Current: Color[i], pColorInner: &HandleColor); |
| 1775 | } |
| 1776 | |
| 1777 | if(OriginalColor != Color) |
| 1778 | { |
| 1779 | *pColor = Color.Pack(Alpha); |
| 1780 | } |
| 1781 | return PrevPackedColor != *pColor; |
| 1782 | } |
| 1783 | |
| 1784 | enum |
| 1785 | { |
| 1786 | APPEARANCE_TAB_HUD = 0, |
| 1787 | APPEARANCE_TAB_CHAT = 1, |
| 1788 | APPEARANCE_TAB_NAME_PLATE = 2, |
| 1789 | APPEARANCE_TAB_HOOK_COLLISION = 3, |
| 1790 | APPEARANCE_TAB_INFO_MESSAGES = 4, |
| 1791 | APPEARANCE_TAB_LASER = 5, |
| 1792 | NUMBER_OF_APPEARANCE_TABS = 6, |
| 1793 | }; |
| 1794 | |
| 1795 | void CMenus::(CUIRect MainView) |
| 1796 | { |
| 1797 | char aBuf[128]; |
| 1798 | static int s_CurTab = 0; |
| 1799 | |
| 1800 | CUIRect TabBar, LeftView, RightView, Button; |
| 1801 | |
| 1802 | MainView.HSplitTop(Cut: 20.0f, pTop: &TabBar, pBottom: &MainView); |
| 1803 | const float TabWidth = TabBar.w / NUMBER_OF_APPEARANCE_TABS; |
| 1804 | static CButtonContainer s_aPageTabs[NUMBER_OF_APPEARANCE_TABS] = {}; |
| 1805 | const char *apTabNames[NUMBER_OF_APPEARANCE_TABS] = { |
| 1806 | Localize(pStr: "HUD" ), |
| 1807 | Localize(pStr: "Chat" ), |
| 1808 | Localize(pStr: "Name Plate" ), |
| 1809 | Localize(pStr: "Hook Collisions" ), |
| 1810 | Localize(pStr: "Info Messages" ), |
| 1811 | Localize(pStr: "Laser" )}; |
| 1812 | |
| 1813 | for(int Tab = APPEARANCE_TAB_HUD; Tab < NUMBER_OF_APPEARANCE_TABS; ++Tab) |
| 1814 | { |
| 1815 | TabBar.VSplitLeft(Cut: TabWidth, pLeft: &Button, pRight: &TabBar); |
| 1816 | const int Corners = Tab == APPEARANCE_TAB_HUD ? IGraphics::CORNER_L : (Tab == NUMBER_OF_APPEARANCE_TABS - 1 ? IGraphics::CORNER_R : IGraphics::CORNER_NONE); |
| 1817 | if(DoButton_MenuTab(pButtonContainer: &s_aPageTabs[Tab], pText: apTabNames[Tab], Checked: s_CurTab == Tab, pRect: &Button, Corners, pAnimator: nullptr, pDefaultColor: nullptr, pActiveColor: nullptr, pHoverColor: nullptr, EdgeRounding: 4.0f)) |
| 1818 | { |
| 1819 | s_CurTab = Tab; |
| 1820 | } |
| 1821 | } |
| 1822 | |
| 1823 | MainView.HSplitTop(Cut: 10.0f, pTop: nullptr, pBottom: &MainView); |
| 1824 | |
| 1825 | const float LineSize = 20.0f; |
| 1826 | const float ColorPickerLineSize = 25.0f; |
| 1827 | const float HeadlineFontSize = 20.0f; |
| 1828 | const float HeadlineHeight = 30.0f; |
| 1829 | const float MarginSmall = 5.0f; |
| 1830 | const float MarginBetweenViews = 20.0f; |
| 1831 | |
| 1832 | const float ColorPickerLabelSize = 13.0f; |
| 1833 | const float ColorPickerLineSpacing = 5.0f; |
| 1834 | |
| 1835 | if(s_CurTab == APPEARANCE_TAB_HUD) |
| 1836 | { |
| 1837 | MainView.VSplitMid(pLeft: &LeftView, pRight: &RightView, Spacing: MarginBetweenViews); |
| 1838 | |
| 1839 | // ***** HUD ***** // |
| 1840 | Ui()->DoLabel_AutoLineSize(pText: Localize(pStr: "HUD" ), FontSize: HeadlineFontSize, |
| 1841 | Align: TEXTALIGN_ML, pRect: &LeftView, LineSize: HeadlineHeight); |
| 1842 | LeftView.HSplitTop(Cut: MarginSmall, pTop: nullptr, pBottom: &LeftView); |
| 1843 | |
| 1844 | // Switch of the entire HUD |
| 1845 | DoButton_CheckBoxAutoVMarginAndSet(pId: &g_Config.m_ClShowhud, pText: Localize(pStr: "Show ingame HUD" ), pValue: &g_Config.m_ClShowhud, pRect: &LeftView, VMargin: LineSize); |
| 1846 | |
| 1847 | // Switches of the various normal HUD elements |
| 1848 | DoButton_CheckBoxAutoVMarginAndSet(pId: &g_Config.m_ClShowhudHealthAmmo, pText: Localize(pStr: "Show health, shields and ammo" ), pValue: &g_Config.m_ClShowhudHealthAmmo, pRect: &LeftView, VMargin: LineSize); |
| 1849 | DoButton_CheckBoxAutoVMarginAndSet(pId: &g_Config.m_ClShowhudScore, pText: Localize(pStr: "Show score" ), pValue: &g_Config.m_ClShowhudScore, pRect: &LeftView, VMargin: LineSize); |
| 1850 | DoButton_CheckBoxAutoVMarginAndSet(pId: &g_Config.m_ClShowLocalTimeAlways, pText: Localize(pStr: "Show local time always" ), pValue: &g_Config.m_ClShowLocalTimeAlways, pRect: &LeftView, VMargin: LineSize); |
| 1851 | DoButton_CheckBoxAutoVMarginAndSet(pId: &g_Config.m_ClSpecCursor, pText: Localize(pStr: "Show spectator cursor" ), pValue: &g_Config.m_ClSpecCursor, pRect: &LeftView, VMargin: LineSize); |
| 1852 | |
| 1853 | // Settings of the HUD element for votes |
| 1854 | DoButton_CheckBoxAutoVMarginAndSet(pId: &g_Config.m_ClShowVotesAfterVoting, pText: Localize(pStr: "Show votes window after voting" ), pValue: &g_Config.m_ClShowVotesAfterVoting, pRect: &LeftView, VMargin: LineSize); |
| 1855 | |
| 1856 | // ***** Scoreboard ***** // |
| 1857 | LeftView.HSplitTop(Cut: MarginBetweenViews, pTop: nullptr, pBottom: &LeftView); |
| 1858 | Ui()->DoLabel_AutoLineSize(pText: Localize(pStr: "Scoreboard" ), FontSize: HeadlineFontSize, |
| 1859 | Align: TEXTALIGN_ML, pRect: &LeftView, LineSize: HeadlineHeight); |
| 1860 | LeftView.HSplitTop(Cut: MarginSmall, pTop: nullptr, pBottom: &LeftView); |
| 1861 | |
| 1862 | ColorRGBA GreenDefault(0.78f, 1.0f, 0.8f, 1.0f); |
| 1863 | static CButtonContainer s_AuthedColor, s_SameClanColor; |
| 1864 | DoLine_ColorPicker(pResetId: &s_AuthedColor, LineSize: ColorPickerLineSize, LabelSize: ColorPickerLabelSize, BottomMargin: ColorPickerLineSpacing, pMainRect: &LeftView, pText: Localize(pStr: "Authed name color in scoreboard" ), pColorValue: &g_Config.m_ClAuthedPlayerColor, DefaultColor: GreenDefault, CheckBoxSpacing: false); |
| 1865 | DoLine_ColorPicker(pResetId: &s_SameClanColor, LineSize: ColorPickerLineSize, LabelSize: ColorPickerLabelSize, BottomMargin: ColorPickerLineSpacing, pMainRect: &LeftView, pText: Localize(pStr: "Same clan color in scoreboard" ), pColorValue: &g_Config.m_ClSameClanColor, DefaultColor: GreenDefault, CheckBoxSpacing: false); |
| 1866 | |
| 1867 | // ***** DDRace HUD ***** // |
| 1868 | Ui()->DoLabel_AutoLineSize(pText: Localize(pStr: "DDRace HUD" ), FontSize: HeadlineFontSize, |
| 1869 | Align: TEXTALIGN_ML, pRect: &RightView, LineSize: HeadlineHeight); |
| 1870 | RightView.HSplitTop(Cut: MarginSmall, pTop: nullptr, pBottom: &RightView); |
| 1871 | |
| 1872 | // Switches of various DDRace HUD elements |
| 1873 | DoButton_CheckBoxAutoVMarginAndSet(pId: &g_Config.m_ClShowIds, pText: Localize(pStr: "Show client IDs (scoreboard, chat, spectator)" ), pValue: &g_Config.m_ClShowIds, pRect: &RightView, VMargin: LineSize); |
| 1874 | DoButton_CheckBoxAutoVMarginAndSet(pId: &g_Config.m_ClShowhudDDRace, pText: Localize(pStr: "Show DDRace HUD" ), pValue: &g_Config.m_ClShowhudDDRace, pRect: &RightView, VMargin: LineSize); |
| 1875 | if(g_Config.m_ClShowhudDDRace) |
| 1876 | { |
| 1877 | DoButton_CheckBoxAutoVMarginAndSet(pId: &g_Config.m_ClShowhudJumpsIndicator, pText: Localize(pStr: "Show jumps indicator" ), pValue: &g_Config.m_ClShowhudJumpsIndicator, pRect: &RightView, VMargin: LineSize); |
| 1878 | } |
| 1879 | else |
| 1880 | { |
| 1881 | RightView.HSplitTop(Cut: LineSize, pTop: nullptr, pBottom: &RightView); // Create empty space for hidden option |
| 1882 | } |
| 1883 | |
| 1884 | // Eye with a number of spectators |
| 1885 | DoButton_CheckBoxAutoVMarginAndSet(pId: &g_Config.m_ClShowhudSpectatorCount, pText: Localize(pStr: "Show number of spectators" ), pValue: &g_Config.m_ClShowhudSpectatorCount, pRect: &RightView, VMargin: LineSize); |
| 1886 | |
| 1887 | // Switch for dummy actions display |
| 1888 | DoButton_CheckBoxAutoVMarginAndSet(pId: &g_Config.m_ClShowhudDummyActions, pText: Localize(pStr: "Show dummy actions" ), pValue: &g_Config.m_ClShowhudDummyActions, pRect: &RightView, VMargin: LineSize); |
| 1889 | |
| 1890 | // Player movement information display settings |
| 1891 | DoButton_CheckBoxAutoVMarginAndSet(pId: &g_Config.m_ClShowhudPlayerPosition, pText: Localize(pStr: "Show player position" ), pValue: &g_Config.m_ClShowhudPlayerPosition, pRect: &RightView, VMargin: LineSize); |
| 1892 | DoButton_CheckBoxAutoVMarginAndSet(pId: &g_Config.m_ClShowhudPlayerSpeed, pText: Localize(pStr: "Show player speed" ), pValue: &g_Config.m_ClShowhudPlayerSpeed, pRect: &RightView, VMargin: LineSize); |
| 1893 | DoButton_CheckBoxAutoVMarginAndSet(pId: &g_Config.m_ClShowhudPlayerAngle, pText: Localize(pStr: "Show player target angle" ), pValue: &g_Config.m_ClShowhudPlayerAngle, pRect: &RightView, VMargin: LineSize); |
| 1894 | |
| 1895 | // Freeze bar settings |
| 1896 | DoButton_CheckBoxAutoVMarginAndSet(pId: &g_Config.m_ClShowFreezeBars, pText: Localize(pStr: "Show freeze bars" ), pValue: &g_Config.m_ClShowFreezeBars, pRect: &RightView, VMargin: LineSize); |
| 1897 | RightView.HSplitTop(Cut: LineSize * 2.0f, pTop: &Button, pBottom: &RightView); |
| 1898 | if(g_Config.m_ClShowFreezeBars) |
| 1899 | { |
| 1900 | Ui()->DoScrollbarOption(pId: &g_Config.m_ClFreezeBarsAlphaInsideFreeze, pOption: &g_Config.m_ClFreezeBarsAlphaInsideFreeze, pRect: &Button, pStr: Localize(pStr: "Opacity of freeze bars inside freeze" ), Min: 0, Max: 100, pScale: &CUi::ms_LinearScrollbarScale, Flags: CUi::SCROLLBAR_OPTION_MULTILINE, pSuffix: "%" ); |
| 1901 | } |
| 1902 | } |
| 1903 | else if(s_CurTab == APPEARANCE_TAB_CHAT) |
| 1904 | { |
| 1905 | CChat &Chat = GameClient()->m_Chat; |
| 1906 | CUIRect TopView, PreviewView; |
| 1907 | MainView.HSplitBottom(Cut: 220.0f, pTop: &TopView, pBottom: &PreviewView); |
| 1908 | TopView.HSplitBottom(Cut: MarginBetweenViews, pTop: &TopView, pBottom: nullptr); |
| 1909 | TopView.VSplitMid(pLeft: &LeftView, pRight: &RightView, Spacing: MarginBetweenViews); |
| 1910 | |
| 1911 | // ***** Chat ***** // |
| 1912 | Ui()->DoLabel_AutoLineSize(pText: Localize(pStr: "Chat" ), FontSize: HeadlineFontSize, |
| 1913 | Align: TEXTALIGN_ML, pRect: &LeftView, LineSize: HeadlineHeight); |
| 1914 | LeftView.HSplitTop(Cut: MarginSmall, pTop: nullptr, pBottom: &LeftView); |
| 1915 | |
| 1916 | // General chat settings |
| 1917 | LeftView.HSplitTop(Cut: LineSize, pTop: &Button, pBottom: &LeftView); |
| 1918 | if(DoButton_CheckBox(pId: &g_Config.m_ClShowChat, pText: Localize(pStr: "Show chat" ), Checked: g_Config.m_ClShowChat, pRect: &Button)) |
| 1919 | { |
| 1920 | g_Config.m_ClShowChat = g_Config.m_ClShowChat ? 0 : 1; |
| 1921 | } |
| 1922 | LeftView.HSplitTop(Cut: LineSize, pTop: &Button, pBottom: &LeftView); |
| 1923 | if(g_Config.m_ClShowChat) |
| 1924 | { |
| 1925 | static int s_ShowChat = 0; |
| 1926 | if(DoButton_CheckBox(pId: &s_ShowChat, pText: Localize(pStr: "Always show chat" ), Checked: g_Config.m_ClShowChat == 2, pRect: &Button)) |
| 1927 | g_Config.m_ClShowChat = g_Config.m_ClShowChat != 2 ? 2 : 1; |
| 1928 | } |
| 1929 | |
| 1930 | DoButton_CheckBoxAutoVMarginAndSet(pId: &g_Config.m_ClChatTeamColors, pText: Localize(pStr: "Show names in chat in team colors" ), pValue: &g_Config.m_ClChatTeamColors, pRect: &LeftView, VMargin: LineSize); |
| 1931 | DoButton_CheckBoxAutoVMarginAndSet(pId: &g_Config.m_ClShowChatFriends, pText: Localize(pStr: "Show only chat messages from friends" ), pValue: &g_Config.m_ClShowChatFriends, pRect: &LeftView, VMargin: LineSize); |
| 1932 | DoButton_CheckBoxAutoVMarginAndSet(pId: &g_Config.m_ClShowChatTeamMembersOnly, pText: Localize(pStr: "Show only chat messages from team members" ), pValue: &g_Config.m_ClShowChatTeamMembersOnly, pRect: &LeftView, VMargin: LineSize); |
| 1933 | |
| 1934 | if(DoButton_CheckBoxAutoVMarginAndSet(pId: &g_Config.m_ClChatOld, pText: Localize(pStr: "Use old chat style" ), pValue: &g_Config.m_ClChatOld, pRect: &LeftView, VMargin: LineSize)) |
| 1935 | GameClient()->m_Chat.RebuildChat(); |
| 1936 | |
| 1937 | // DoButton_CheckBoxAutoVMarginAndSet(&g_Config.m_ClCensorChat, Localize("Censor profanity"), &g_Config.m_ClCensorChat, &LeftView, LineSize); |
| 1938 | |
| 1939 | LeftView.HSplitTop(Cut: LineSize, pTop: &Button, pBottom: &LeftView); |
| 1940 | if(Ui()->DoScrollbarOption(pId: &g_Config.m_ClChatFontSize, pOption: &g_Config.m_ClChatFontSize, pRect: &Button, pStr: Localize(pStr: "Chat font size" ), Min: 10, Max: 100)) |
| 1941 | { |
| 1942 | Chat.EnsureCoherentWidth(); |
| 1943 | Chat.RebuildChat(); |
| 1944 | } |
| 1945 | |
| 1946 | LeftView.HSplitTop(Cut: LineSize, pTop: &Button, pBottom: &LeftView); |
| 1947 | if(Ui()->DoScrollbarOption(pId: &g_Config.m_ClChatWidth, pOption: &g_Config.m_ClChatWidth, pRect: &Button, pStr: Localize(pStr: "Chat width" ), Min: 120, Max: 400)) |
| 1948 | { |
| 1949 | Chat.EnsureCoherentFontSize(); |
| 1950 | Chat.RebuildChat(); |
| 1951 | } |
| 1952 | |
| 1953 | static CButtonContainer s_BackgroundColor; |
| 1954 | DoLine_ColorPicker(pResetId: &s_BackgroundColor, LineSize: ColorPickerLineSize, LabelSize: ColorPickerLabelSize, BottomMargin: ColorPickerLineSpacing, pMainRect: &LeftView, pText: Localize(pStr: "Chat background color" ), pColorValue: &g_Config.m_ClChatBackgroundColor, DefaultColor: color_cast<ColorRGBA>(hsl: ColorHSLA(CConfig::ms_ClChatBackgroundColor, true)), CheckBoxSpacing: false, pCheckBoxValue: nullptr, Alpha: true); |
| 1955 | |
| 1956 | // ***** Messages ***** // |
| 1957 | Ui()->DoLabel_AutoLineSize(pText: Localize(pStr: "Messages" ), FontSize: HeadlineFontSize, |
| 1958 | Align: TEXTALIGN_ML, pRect: &RightView, LineSize: HeadlineHeight); |
| 1959 | RightView.HSplitTop(Cut: MarginSmall, pTop: nullptr, pBottom: &RightView); |
| 1960 | |
| 1961 | // Message Colors and extra settings |
| 1962 | static CButtonContainer s_SystemMessageColor; |
| 1963 | DoLine_ColorPicker(pResetId: &s_SystemMessageColor, LineSize: ColorPickerLineSize, LabelSize: ColorPickerLabelSize, BottomMargin: ColorPickerLineSpacing, pMainRect: &RightView, pText: Localize(pStr: "System message" ), pColorValue: &g_Config.m_ClMessageSystemColor, DefaultColor: ColorRGBA(1.0f, 1.0f, 0.5f), CheckBoxSpacing: true, pCheckBoxValue: &g_Config.m_ClShowChatSystem); |
| 1964 | static CButtonContainer s_HighlightedMessageColor; |
| 1965 | DoLine_ColorPicker(pResetId: &s_HighlightedMessageColor, LineSize: ColorPickerLineSize, LabelSize: ColorPickerLabelSize, BottomMargin: ColorPickerLineSpacing, pMainRect: &RightView, pText: Localize(pStr: "Highlighted message" ), pColorValue: &g_Config.m_ClMessageHighlightColor, DefaultColor: ColorRGBA(1.0f, 0.5f, 0.5f)); |
| 1966 | static CButtonContainer s_TeamMessageColor; |
| 1967 | DoLine_ColorPicker(pResetId: &s_TeamMessageColor, LineSize: ColorPickerLineSize, LabelSize: ColorPickerLabelSize, BottomMargin: ColorPickerLineSpacing, pMainRect: &RightView, pText: Localize(pStr: "Team message" ), pColorValue: &g_Config.m_ClMessageTeamColor, DefaultColor: ColorRGBA(0.65f, 1.0f, 0.65f)); |
| 1968 | static CButtonContainer s_FriendMessageColor; |
| 1969 | DoLine_ColorPicker(pResetId: &s_FriendMessageColor, LineSize: ColorPickerLineSize, LabelSize: ColorPickerLabelSize, BottomMargin: ColorPickerLineSpacing, pMainRect: &RightView, pText: Localize(pStr: "Friend message" ), pColorValue: &g_Config.m_ClMessageFriendColor, DefaultColor: ColorRGBA(1.0f, 0.137f, 0.137f), CheckBoxSpacing: true, pCheckBoxValue: &g_Config.m_ClMessageFriend); |
| 1970 | static CButtonContainer s_NormalMessageColor; |
| 1971 | DoLine_ColorPicker(pResetId: &s_NormalMessageColor, LineSize: ColorPickerLineSize, LabelSize: ColorPickerLabelSize, BottomMargin: ColorPickerLineSpacing, pMainRect: &RightView, pText: Localize(pStr: "Normal message" ), pColorValue: &g_Config.m_ClMessageColor, DefaultColor: ColorRGBA(1.0f, 1.0f, 1.0f)); |
| 1972 | |
| 1973 | str_format(buffer: aBuf, buffer_size: sizeof(aBuf), format: "%s (echo)" , Localize(pStr: "Client message" )); |
| 1974 | static CButtonContainer s_ClientMessageColor; |
| 1975 | DoLine_ColorPicker(pResetId: &s_ClientMessageColor, LineSize: ColorPickerLineSize, LabelSize: ColorPickerLabelSize, BottomMargin: ColorPickerLineSpacing, pMainRect: &RightView, pText: aBuf, pColorValue: &g_Config.m_ClMessageClientColor, DefaultColor: ColorRGBA(0.5f, 0.78f, 1.0f)); |
| 1976 | |
| 1977 | // ***** Chat Preview ***** // |
| 1978 | Ui()->DoLabel_AutoLineSize(pText: Localize(pStr: "Preview" ), FontSize: HeadlineFontSize, |
| 1979 | Align: TEXTALIGN_ML, pRect: &PreviewView, LineSize: HeadlineHeight); |
| 1980 | PreviewView.HSplitTop(Cut: MarginSmall, pTop: nullptr, pBottom: &PreviewView); |
| 1981 | |
| 1982 | // Use the rest of the view for preview |
| 1983 | PreviewView.Draw(Color: ColorRGBA(1, 1, 1, 0.1f), Corners: IGraphics::CORNER_ALL, Rounding: 5.0f); |
| 1984 | PreviewView.Margin(Cut: MarginSmall, pOtherRect: &PreviewView); |
| 1985 | |
| 1986 | ColorRGBA SystemColor = color_cast<ColorRGBA, ColorHSLA>(hsl: ColorHSLA(g_Config.m_ClMessageSystemColor)); |
| 1987 | ColorRGBA HighlightedColor = color_cast<ColorRGBA, ColorHSLA>(hsl: ColorHSLA(g_Config.m_ClMessageHighlightColor)); |
| 1988 | ColorRGBA TeamColor = color_cast<ColorRGBA, ColorHSLA>(hsl: ColorHSLA(g_Config.m_ClMessageTeamColor)); |
| 1989 | ColorRGBA FriendColor = color_cast<ColorRGBA, ColorHSLA>(hsl: ColorHSLA(g_Config.m_ClMessageFriendColor)); |
| 1990 | ColorRGBA NormalColor = color_cast<ColorRGBA, ColorHSLA>(hsl: ColorHSLA(g_Config.m_ClMessageColor)); |
| 1991 | ColorRGBA ClientColor = color_cast<ColorRGBA, ColorHSLA>(hsl: ColorHSLA(g_Config.m_ClMessageClientColor)); |
| 1992 | ColorRGBA DefaultNameColor(0.8f, 0.8f, 0.8f, 1.0f); |
| 1993 | |
| 1994 | const float RealFontSize = Chat.FontSize() * 2; |
| 1995 | const float RealMsgPaddingX = (!g_Config.m_ClChatOld ? Chat.MessagePaddingX() : 0) * 2; |
| 1996 | const float RealMsgPaddingY = (!g_Config.m_ClChatOld ? Chat.MessagePaddingY() : 0) * 2; |
| 1997 | const float RealMsgPaddingTee = (!g_Config.m_ClChatOld ? Chat.MessageTeeSize() + CChat::MESSAGE_TEE_PADDING_RIGHT : 0) * 2; |
| 1998 | const float RealOffsetY = RealFontSize + RealMsgPaddingY; |
| 1999 | |
| 2000 | const float X = RealMsgPaddingX / 2.0f + PreviewView.x; |
| 2001 | float Y = PreviewView.y; |
| 2002 | float LineWidth = g_Config.m_ClChatWidth * 2 - (RealMsgPaddingX * 1.5f) - RealMsgPaddingTee; |
| 2003 | |
| 2004 | str_copy(dst&: aBuf, src: Client()->PlayerName()); |
| 2005 | |
| 2006 | const CAnimState *pIdleState = CAnimState::GetIdle(); |
| 2007 | const float RealTeeSize = Chat.MessageTeeSize() * 2; |
| 2008 | const float RealTeeSizeHalved = Chat.MessageTeeSize(); |
| 2009 | constexpr float TWSkinUnreliableOffset = -0.25f; |
| 2010 | const float OffsetTeeY = RealTeeSizeHalved; |
| 2011 | const float FullHeightMinusTee = RealOffsetY - RealTeeSize; |
| 2012 | |
| 2013 | struct SPreviewLine |
| 2014 | { |
| 2015 | int m_ClientId; |
| 2016 | bool m_Team; |
| 2017 | char m_aName[64]; |
| 2018 | char m_aText[256]; |
| 2019 | bool m_Friend; |
| 2020 | bool m_Player; |
| 2021 | bool m_Client; |
| 2022 | bool m_Highlighted; |
| 2023 | int m_TimesRepeated; |
| 2024 | |
| 2025 | CTeeRenderInfo m_RenderInfo; |
| 2026 | }; |
| 2027 | |
| 2028 | static std::vector<SPreviewLine> s_vLines; |
| 2029 | |
| 2030 | enum ELineFlag |
| 2031 | { |
| 2032 | FLAG_TEAM = 1 << 0, |
| 2033 | FLAG_FRIEND = 1 << 1, |
| 2034 | FLAG_HIGHLIGHT = 1 << 2, |
| 2035 | FLAG_CLIENT = 1 << 3 |
| 2036 | }; |
| 2037 | enum |
| 2038 | { |
| 2039 | PREVIEW_SYS, |
| 2040 | PREVIEW_HIGHLIGHT, |
| 2041 | PREVIEW_TEAM, |
| 2042 | PREVIEW_FRIEND, |
| 2043 | PREVIEW_SPAMMER, |
| 2044 | PREVIEW_CLIENT |
| 2045 | }; |
| 2046 | auto &&SetPreviewLine = [](int Index, int ClientId, const char *pName, const char *pText, int Flag, int Repeats) { |
| 2047 | SPreviewLine *pLine; |
| 2048 | if((int)s_vLines.size() <= Index) |
| 2049 | { |
| 2050 | s_vLines.emplace_back(); |
| 2051 | pLine = &s_vLines.back(); |
| 2052 | } |
| 2053 | else |
| 2054 | { |
| 2055 | pLine = &s_vLines[Index]; |
| 2056 | } |
| 2057 | pLine->m_ClientId = ClientId; |
| 2058 | pLine->m_Team = Flag & FLAG_TEAM; |
| 2059 | pLine->m_Friend = Flag & FLAG_FRIEND; |
| 2060 | pLine->m_Player = ClientId >= 0; |
| 2061 | pLine->m_Highlighted = Flag & FLAG_HIGHLIGHT; |
| 2062 | pLine->m_Client = Flag & FLAG_CLIENT; |
| 2063 | pLine->m_TimesRepeated = Repeats; |
| 2064 | str_copy(dst&: pLine->m_aName, src: pName); |
| 2065 | str_copy(dst&: pLine->m_aText, src: pText); |
| 2066 | }; |
| 2067 | auto &&SetLineSkin = [RealTeeSize](int Index, const CSkin *pSkin) { |
| 2068 | if(Index >= (int)s_vLines.size()) |
| 2069 | return; |
| 2070 | s_vLines[Index].m_RenderInfo.m_Size = RealTeeSize; |
| 2071 | s_vLines[Index].m_RenderInfo.Apply(pSkin); |
| 2072 | }; |
| 2073 | |
| 2074 | auto &&RenderPreview = [&](int LineIndex, int x, int y, bool Render = true) { |
| 2075 | if(LineIndex >= (int)s_vLines.size()) |
| 2076 | return vec2(0, 0); |
| 2077 | CTextCursor LocalCursor; |
| 2078 | LocalCursor.SetPosition(vec2(x, y)); |
| 2079 | LocalCursor.m_FontSize = RealFontSize; |
| 2080 | LocalCursor.m_Flags = Render ? TEXTFLAG_RENDER : 0; |
| 2081 | LocalCursor.m_LineWidth = LineWidth; |
| 2082 | const auto &Line = s_vLines[LineIndex]; |
| 2083 | |
| 2084 | char aClientId[16] = "" ; |
| 2085 | if(g_Config.m_ClShowIds && Line.m_ClientId >= 0 && Line.m_aName[0] != '\0') |
| 2086 | { |
| 2087 | GameClient()->FormatClientId(ClientId: Line.m_ClientId, aClientId, Format: EClientIdFormat::INDENT_FORCE); |
| 2088 | } |
| 2089 | |
| 2090 | char aCount[12]; |
| 2091 | if(Line.m_ClientId < 0) |
| 2092 | str_format(buffer: aCount, buffer_size: sizeof(aCount), format: "[%d] " , Line.m_TimesRepeated + 1); |
| 2093 | else |
| 2094 | str_format(buffer: aCount, buffer_size: sizeof(aCount), format: " [%d]" , Line.m_TimesRepeated + 1); |
| 2095 | |
| 2096 | if(Line.m_Player) |
| 2097 | { |
| 2098 | LocalCursor.m_X += RealMsgPaddingTee; |
| 2099 | |
| 2100 | if(Line.m_Friend && g_Config.m_ClMessageFriend) |
| 2101 | { |
| 2102 | if(Render) |
| 2103 | TextRender()->TextColor(Color: FriendColor); |
| 2104 | TextRender()->TextEx(pCursor: &LocalCursor, pText: "♥ " , Length: -1); |
| 2105 | } |
| 2106 | } |
| 2107 | |
| 2108 | ColorRGBA NameColor; |
| 2109 | if(Line.m_Team) |
| 2110 | NameColor = CalculateNameColor(TextColorHSL: color_cast<ColorHSLA>(rgb: TeamColor)); |
| 2111 | else if(Line.m_Player) |
| 2112 | NameColor = DefaultNameColor; |
| 2113 | else if(Line.m_Client) |
| 2114 | NameColor = ClientColor; |
| 2115 | else |
| 2116 | NameColor = SystemColor; |
| 2117 | |
| 2118 | if(Render) |
| 2119 | TextRender()->TextColor(Color: NameColor); |
| 2120 | |
| 2121 | TextRender()->TextEx(pCursor: &LocalCursor, pText: aClientId); |
| 2122 | TextRender()->TextEx(pCursor: &LocalCursor, pText: Line.m_aName); |
| 2123 | |
| 2124 | if(Line.m_TimesRepeated > 0) |
| 2125 | { |
| 2126 | if(Render) |
| 2127 | TextRender()->TextColor(r: 1.0f, g: 1.0f, b: 1.0f, a: 0.3f); |
| 2128 | TextRender()->TextEx(pCursor: &LocalCursor, pText: aCount, Length: -1); |
| 2129 | } |
| 2130 | |
| 2131 | if(Line.m_ClientId >= 0 && Line.m_aName[0] != '\0') |
| 2132 | { |
| 2133 | if(Render) |
| 2134 | TextRender()->TextColor(Color: NameColor); |
| 2135 | TextRender()->TextEx(pCursor: &LocalCursor, pText: ": " , Length: -1); |
| 2136 | } |
| 2137 | |
| 2138 | CTextCursor AppendCursor = LocalCursor; |
| 2139 | AppendCursor.m_LongestLineWidth = 0.0f; |
| 2140 | if(!g_Config.m_ClChatOld) |
| 2141 | { |
| 2142 | AppendCursor.m_StartX = LocalCursor.m_X; |
| 2143 | AppendCursor.m_LineWidth -= LocalCursor.m_LongestLineWidth; |
| 2144 | } |
| 2145 | |
| 2146 | if(Render) |
| 2147 | { |
| 2148 | if(Line.m_Highlighted) |
| 2149 | TextRender()->TextColor(Color: HighlightedColor); |
| 2150 | else if(Line.m_Team) |
| 2151 | TextRender()->TextColor(Color: TeamColor); |
| 2152 | else if(Line.m_Player) |
| 2153 | TextRender()->TextColor(Color: NormalColor); |
| 2154 | } |
| 2155 | |
| 2156 | TextRender()->TextEx(pCursor: &AppendCursor, pText: Line.m_aText, Length: -1); |
| 2157 | if(Render) |
| 2158 | TextRender()->TextColor(Color: TextRender()->DefaultTextColor()); |
| 2159 | |
| 2160 | return vec2{LocalCursor.m_LongestLineWidth + AppendCursor.m_LongestLineWidth, AppendCursor.Height() + RealMsgPaddingY}; |
| 2161 | }; |
| 2162 | |
| 2163 | // Set preview lines |
| 2164 | { |
| 2165 | char aLineBuilder[128]; |
| 2166 | |
| 2167 | str_format(buffer: aLineBuilder, buffer_size: sizeof(aLineBuilder), format: "'%s' entered and joined the game" , aBuf); |
| 2168 | SetPreviewLine(PREVIEW_SYS, -1, "*** " , aLineBuilder, 0, 0); |
| 2169 | |
| 2170 | str_format(buffer: aLineBuilder, buffer_size: sizeof(aLineBuilder), format: "Hey, how are you %s?" , aBuf); |
| 2171 | SetPreviewLine(PREVIEW_HIGHLIGHT, 7, "Random Tee" , aLineBuilder, FLAG_HIGHLIGHT, 0); |
| 2172 | |
| 2173 | SetPreviewLine(PREVIEW_TEAM, 11, "Your Teammate" , "Let's speedrun this!" , FLAG_TEAM, 0); |
| 2174 | SetPreviewLine(PREVIEW_FRIEND, 8, "Friend" , "Hello there" , FLAG_FRIEND, 0); |
| 2175 | SetPreviewLine(PREVIEW_SPAMMER, 9, "Spammer" , "Hey fools, I'm spamming here!" , 0, 5); |
| 2176 | SetPreviewLine(PREVIEW_CLIENT, -1, "— " , "Echo command executed" , FLAG_CLIENT, 0); |
| 2177 | } |
| 2178 | |
| 2179 | SetLineSkin(1, GameClient()->m_Skins.Find(pName: "pinky" )); |
| 2180 | SetLineSkin(2, GameClient()->m_Skins.Find(pName: "default" )); |
| 2181 | SetLineSkin(3, GameClient()->m_Skins.Find(pName: "cammostripes" )); |
| 2182 | SetLineSkin(4, GameClient()->m_Skins.Find(pName: "beast" )); |
| 2183 | |
| 2184 | // Backgrounds first |
| 2185 | if(!g_Config.m_ClChatOld) |
| 2186 | { |
| 2187 | Graphics()->TextureClear(); |
| 2188 | Graphics()->QuadsBegin(); |
| 2189 | Graphics()->SetColor(color_cast<ColorRGBA>(hsl: ColorHSLA(g_Config.m_ClChatBackgroundColor, true))); |
| 2190 | |
| 2191 | float TempY = Y; |
| 2192 | const float RealBackgroundRounding = Chat.MessageRounding() * 2.0f; |
| 2193 | |
| 2194 | auto &&RenderMessageBackground = [&](int LineIndex) { |
| 2195 | auto Size = RenderPreview(LineIndex, 0, 0, false); |
| 2196 | Graphics()->DrawRectExt(x: X - RealMsgPaddingX / 2.0f, y: TempY - RealMsgPaddingY / 2.0f, w: Size.x + RealMsgPaddingX * 1.5f, h: Size.y, r: RealBackgroundRounding, Corners: IGraphics::CORNER_ALL); |
| 2197 | return Size.y; |
| 2198 | }; |
| 2199 | |
| 2200 | if(g_Config.m_ClShowChatSystem) |
| 2201 | { |
| 2202 | TempY += RenderMessageBackground(PREVIEW_SYS); |
| 2203 | } |
| 2204 | |
| 2205 | if(!g_Config.m_ClShowChatFriends) |
| 2206 | { |
| 2207 | if(!g_Config.m_ClShowChatTeamMembersOnly) |
| 2208 | TempY += RenderMessageBackground(PREVIEW_HIGHLIGHT); |
| 2209 | TempY += RenderMessageBackground(PREVIEW_TEAM); |
| 2210 | } |
| 2211 | |
| 2212 | if(!g_Config.m_ClShowChatTeamMembersOnly) |
| 2213 | TempY += RenderMessageBackground(PREVIEW_FRIEND); |
| 2214 | |
| 2215 | if(!g_Config.m_ClShowChatFriends && !g_Config.m_ClShowChatTeamMembersOnly) |
| 2216 | { |
| 2217 | TempY += RenderMessageBackground(PREVIEW_SPAMMER); |
| 2218 | } |
| 2219 | |
| 2220 | TempY += RenderMessageBackground(PREVIEW_CLIENT); |
| 2221 | |
| 2222 | Graphics()->QuadsEnd(); |
| 2223 | } |
| 2224 | |
| 2225 | // System |
| 2226 | if(g_Config.m_ClShowChatSystem) |
| 2227 | { |
| 2228 | Y += RenderPreview(PREVIEW_SYS, X, Y).y; |
| 2229 | } |
| 2230 | |
| 2231 | if(!g_Config.m_ClShowChatFriends) |
| 2232 | { |
| 2233 | // Highlighted |
| 2234 | if(!g_Config.m_ClChatOld && !g_Config.m_ClShowChatTeamMembersOnly) |
| 2235 | RenderTools()->RenderTee(pAnim: pIdleState, pInfo: &s_vLines[PREVIEW_HIGHLIGHT].m_RenderInfo, Emote: EMOTE_NORMAL, Dir: vec2(1, 0.1f), Pos: vec2(X + RealTeeSizeHalved, Y + OffsetTeeY + FullHeightMinusTee / 2.0f + TWSkinUnreliableOffset)); |
| 2236 | if(!g_Config.m_ClShowChatTeamMembersOnly) |
| 2237 | Y += RenderPreview(PREVIEW_HIGHLIGHT, X, Y).y; |
| 2238 | |
| 2239 | // Team |
| 2240 | if(!g_Config.m_ClChatOld) |
| 2241 | RenderTools()->RenderTee(pAnim: pIdleState, pInfo: &s_vLines[PREVIEW_TEAM].m_RenderInfo, Emote: EMOTE_NORMAL, Dir: vec2(1, 0.1f), Pos: vec2(X + RealTeeSizeHalved, Y + OffsetTeeY + FullHeightMinusTee / 2.0f + TWSkinUnreliableOffset)); |
| 2242 | Y += RenderPreview(PREVIEW_TEAM, X, Y).y; |
| 2243 | } |
| 2244 | |
| 2245 | // Friend |
| 2246 | if(!g_Config.m_ClChatOld && !g_Config.m_ClShowChatTeamMembersOnly) |
| 2247 | RenderTools()->RenderTee(pAnim: pIdleState, pInfo: &s_vLines[PREVIEW_FRIEND].m_RenderInfo, Emote: EMOTE_NORMAL, Dir: vec2(1, 0.1f), Pos: vec2(X + RealTeeSizeHalved, Y + OffsetTeeY + FullHeightMinusTee / 2.0f + TWSkinUnreliableOffset)); |
| 2248 | if(!g_Config.m_ClShowChatTeamMembersOnly) |
| 2249 | Y += RenderPreview(PREVIEW_FRIEND, X, Y).y; |
| 2250 | |
| 2251 | // Normal |
| 2252 | if(!g_Config.m_ClShowChatFriends && !g_Config.m_ClShowChatTeamMembersOnly) |
| 2253 | { |
| 2254 | if(!g_Config.m_ClChatOld) |
| 2255 | RenderTools()->RenderTee(pAnim: pIdleState, pInfo: &s_vLines[PREVIEW_SPAMMER].m_RenderInfo, Emote: EMOTE_NORMAL, Dir: vec2(1, 0.1f), Pos: vec2(X + RealTeeSizeHalved, Y + OffsetTeeY + FullHeightMinusTee / 2.0f + TWSkinUnreliableOffset)); |
| 2256 | Y += RenderPreview(PREVIEW_SPAMMER, X, Y).y; |
| 2257 | } |
| 2258 | // Client |
| 2259 | RenderPreview(PREVIEW_CLIENT, X, Y); |
| 2260 | |
| 2261 | TextRender()->TextColor(Color: TextRender()->DefaultTextColor()); |
| 2262 | } |
| 2263 | else if(s_CurTab == APPEARANCE_TAB_NAME_PLATE) |
| 2264 | { |
| 2265 | MainView.VSplitMid(pLeft: &LeftView, pRight: &RightView, Spacing: MarginBetweenViews); |
| 2266 | |
| 2267 | // ***** Name Plate ***** // |
| 2268 | Ui()->DoLabel_AutoLineSize(pText: Localize(pStr: "Name Plate" ), FontSize: HeadlineFontSize, |
| 2269 | Align: TEXTALIGN_ML, pRect: &LeftView, LineSize: HeadlineHeight); |
| 2270 | LeftView.HSplitTop(Cut: MarginSmall, pTop: nullptr, pBottom: &LeftView); |
| 2271 | |
| 2272 | // General name plate settings |
| 2273 | { |
| 2274 | int Pressed = (g_Config.m_ClNamePlates ? 2 : 0) + (g_Config.m_ClNamePlatesOwn ? 1 : 0); |
| 2275 | if(DoLine_RadioMenu(View&: LeftView, pLabel: Localize(pStr: "Show name plates" ), |
| 2276 | vButtonContainers&: m_vButtonContainersNamePlateShow, |
| 2277 | vLabels: {Localize(pStr: "None" , pContext: "Show name plates" ), Localize(pStr: "Own" , pContext: "Show name plates" ), Localize(pStr: "Others" , pContext: "Show name plates" ), Localize(pStr: "All" , pContext: "Show name plates" )}, |
| 2278 | vValues: {0, 1, 2, 3}, |
| 2279 | Value&: Pressed)) |
| 2280 | { |
| 2281 | g_Config.m_ClNamePlates = Pressed & 2 ? 1 : 0; |
| 2282 | g_Config.m_ClNamePlatesOwn = Pressed & 1 ? 1 : 0; |
| 2283 | } |
| 2284 | } |
| 2285 | LeftView.HSplitTop(Cut: LineSize, pTop: &Button, pBottom: &LeftView); |
| 2286 | Ui()->DoScrollbarOption(pId: &g_Config.m_ClNamePlatesSize, pOption: &g_Config.m_ClNamePlatesSize, pRect: &Button, pStr: Localize(pStr: "Name plates size" ), Min: -50, Max: 100); |
| 2287 | LeftView.HSplitTop(Cut: LineSize, pTop: &Button, pBottom: &LeftView); |
| 2288 | Ui()->DoScrollbarOption(pId: &g_Config.m_ClNamePlatesOffset, pOption: &g_Config.m_ClNamePlatesOffset, pRect: &Button, pStr: Localize(pStr: "Name plates offset" ), Min: 10, Max: 50); |
| 2289 | |
| 2290 | DoButton_CheckBoxAutoVMarginAndSet(pId: &g_Config.m_ClNamePlatesClan, pText: Localize(pStr: "Show clan above name plates" ), pValue: &g_Config.m_ClNamePlatesClan, pRect: &LeftView, VMargin: LineSize); |
| 2291 | LeftView.HSplitTop(Cut: LineSize, pTop: &Button, pBottom: &LeftView); |
| 2292 | if(g_Config.m_ClNamePlatesClan) |
| 2293 | Ui()->DoScrollbarOption(pId: &g_Config.m_ClNamePlatesClanSize, pOption: &g_Config.m_ClNamePlatesClanSize, pRect: &Button, pStr: Localize(pStr: "Clan plates size" ), Min: -50, Max: 100); |
| 2294 | |
| 2295 | DoButton_CheckBoxAutoVMarginAndSet(pId: &g_Config.m_ClNamePlatesTeamcolors, pText: Localize(pStr: "Use team colors for name plates" ), pValue: &g_Config.m_ClNamePlatesTeamcolors, pRect: &LeftView, VMargin: LineSize); |
| 2296 | DoButton_CheckBoxAutoVMarginAndSet(pId: &g_Config.m_ClNamePlatesFriendMark, pText: Localize(pStr: "Show friend icon in name plates" ), pValue: &g_Config.m_ClNamePlatesFriendMark, pRect: &LeftView, VMargin: LineSize); |
| 2297 | |
| 2298 | DoButton_CheckBoxAutoVMarginAndSet(pId: &g_Config.m_ClNamePlatesIds, pText: Localize(pStr: "Show client IDs in name plates" ), pValue: &g_Config.m_ClNamePlatesIds, pRect: &LeftView, VMargin: LineSize); |
| 2299 | if(g_Config.m_ClNamePlatesIds > 0) |
| 2300 | DoButton_CheckBoxAutoVMarginAndSet(pId: &g_Config.m_ClNamePlatesIdsSeparateLine, pText: Localize(pStr: "Show client IDs on a separate line" ), pValue: &g_Config.m_ClNamePlatesIdsSeparateLine, pRect: &LeftView, VMargin: LineSize); |
| 2301 | else |
| 2302 | LeftView.HSplitTop(Cut: LineSize, pTop: nullptr, pBottom: &LeftView); |
| 2303 | LeftView.HSplitTop(Cut: LineSize, pTop: &Button, pBottom: &LeftView); |
| 2304 | if(g_Config.m_ClNamePlatesIds > 0 && g_Config.m_ClNamePlatesIdsSeparateLine > 0) |
| 2305 | Ui()->DoScrollbarOption(pId: &g_Config.m_ClNamePlatesIdsSize, pOption: &g_Config.m_ClNamePlatesIdsSize, pRect: &Button, pStr: Localize(pStr: "Client IDs size" ), Min: -50, Max: 100); |
| 2306 | |
| 2307 | // ***** Hook Strength ***** // |
| 2308 | LeftView.HSplitTop(Cut: MarginBetweenViews, pTop: nullptr, pBottom: &LeftView); |
| 2309 | Ui()->DoLabel_AutoLineSize(pText: Localize(pStr: "Hook Strength" ), FontSize: HeadlineFontSize, |
| 2310 | Align: TEXTALIGN_ML, pRect: &LeftView, LineSize: HeadlineHeight); |
| 2311 | LeftView.HSplitTop(Cut: MarginSmall, pTop: nullptr, pBottom: &LeftView); |
| 2312 | |
| 2313 | LeftView.HSplitTop(Cut: LineSize, pTop: &Button, pBottom: &LeftView); |
| 2314 | if(DoButton_CheckBox(pId: &g_Config.m_ClNamePlatesStrong, pText: Localize(pStr: "Show hook strength icon indicator" ), Checked: g_Config.m_ClNamePlatesStrong, pRect: &Button)) |
| 2315 | { |
| 2316 | g_Config.m_ClNamePlatesStrong = g_Config.m_ClNamePlatesStrong ? 0 : 1; |
| 2317 | } |
| 2318 | LeftView.HSplitTop(Cut: LineSize, pTop: &Button, pBottom: &LeftView); |
| 2319 | if(g_Config.m_ClNamePlatesStrong) |
| 2320 | { |
| 2321 | static int s_NamePlatesStrong = 0; |
| 2322 | if(DoButton_CheckBox(pId: &s_NamePlatesStrong, pText: Localize(pStr: "Show hook strength number indicator" ), Checked: g_Config.m_ClNamePlatesStrong == 2, pRect: &Button)) |
| 2323 | g_Config.m_ClNamePlatesStrong = g_Config.m_ClNamePlatesStrong != 2 ? 2 : 1; |
| 2324 | } |
| 2325 | |
| 2326 | LeftView.HSplitTop(Cut: LineSize * 2.0f, pTop: &Button, pBottom: &LeftView); |
| 2327 | if(g_Config.m_ClNamePlatesStrong) |
| 2328 | { |
| 2329 | Ui()->DoScrollbarOption(pId: &g_Config.m_ClNamePlatesStrongSize, pOption: &g_Config.m_ClNamePlatesStrongSize, pRect: &Button, pStr: Localize(pStr: "Size of hook strength icon and number indicator" ), Min: -50, Max: 100, pScale: &CUi::ms_LinearScrollbarScale, Flags: CUi::SCROLLBAR_OPTION_MULTILINE); |
| 2330 | } |
| 2331 | |
| 2332 | // ***** Key Presses ***** // |
| 2333 | LeftView.HSplitTop(Cut: MarginBetweenViews, pTop: nullptr, pBottom: &LeftView); |
| 2334 | Ui()->DoLabel_AutoLineSize(pText: Localize(pStr: "Key Presses" ), FontSize: HeadlineFontSize, |
| 2335 | Align: TEXTALIGN_ML, pRect: &LeftView, LineSize: HeadlineHeight); |
| 2336 | LeftView.HSplitTop(Cut: MarginSmall, pTop: nullptr, pBottom: &LeftView); |
| 2337 | |
| 2338 | DoLine_RadioMenu(View&: LeftView, pLabel: Localize(pStr: "Show players' key presses" ), |
| 2339 | vButtonContainers&: m_vButtonContainersNamePlateKeyPresses, |
| 2340 | vLabels: {Localize(pStr: "None" , pContext: "Show players' key presses" ), Localize(pStr: "Own" , pContext: "Show players' key presses" ), Localize(pStr: "Others" , pContext: "Show players' key presses" ), Localize(pStr: "All" , pContext: "Show players' key presses" )}, |
| 2341 | vValues: {0, 3, 1, 2}, |
| 2342 | Value&: g_Config.m_ClShowDirection); |
| 2343 | |
| 2344 | LeftView.HSplitTop(Cut: LineSize, pTop: &Button, pBottom: &LeftView); |
| 2345 | if(g_Config.m_ClShowDirection > 0) |
| 2346 | Ui()->DoScrollbarOption(pId: &g_Config.m_ClDirectionSize, pOption: &g_Config.m_ClDirectionSize, pRect: &Button, pStr: Localize(pStr: "Size of key press icons" ), Min: -50, Max: 100); |
| 2347 | |
| 2348 | // ***** Name Plate Preview ***** // |
| 2349 | Ui()->DoLabel_AutoLineSize(pText: Localize(pStr: "Preview" ), FontSize: HeadlineFontSize, |
| 2350 | Align: TEXTALIGN_ML, pRect: &RightView, LineSize: HeadlineHeight); |
| 2351 | RightView.HSplitTop(Cut: 2.0f * MarginSmall, pTop: nullptr, pBottom: &RightView); |
| 2352 | |
| 2353 | // ***** Name Plate Dummy Preview ***** // |
| 2354 | RightView.HSplitBottom(Cut: LineSize, pTop: &RightView, pBottom: &Button); |
| 2355 | if(DoButton_CheckBox(pId: &m_DummyNamePlatePreview, pText: g_Config.m_ClDummy ? Localize(pStr: "Preview player's name plate" ) : Localize(pStr: "Preview dummy's name plate" ), Checked: m_DummyNamePlatePreview, pRect: &Button)) |
| 2356 | m_DummyNamePlatePreview = !m_DummyNamePlatePreview; |
| 2357 | |
| 2358 | int Dummy = g_Config.m_ClDummy != (m_DummyNamePlatePreview ? 1 : 0); |
| 2359 | |
| 2360 | const vec2 Position = RightView.Center(); |
| 2361 | |
| 2362 | GameClient()->m_NamePlates.RenderNamePlatePreview(Position, Dummy); |
| 2363 | } |
| 2364 | else if(s_CurTab == APPEARANCE_TAB_HOOK_COLLISION) |
| 2365 | { |
| 2366 | MainView.VSplitMid(pLeft: &LeftView, pRight: &RightView, Spacing: MarginBetweenViews); |
| 2367 | |
| 2368 | // ***** Hookline ***** // |
| 2369 | Ui()->DoLabel_AutoLineSize(pText: Localize(pStr: "Hook collision line" ), FontSize: HeadlineFontSize, |
| 2370 | Align: TEXTALIGN_ML, pRect: &LeftView, LineSize: HeadlineHeight); |
| 2371 | LeftView.HSplitTop(Cut: MarginSmall, pTop: nullptr, pBottom: &LeftView); |
| 2372 | |
| 2373 | // General hookline settings |
| 2374 | LeftView.HSplitTop(Cut: LineSize, pTop: &Button, pBottom: &LeftView); |
| 2375 | if(DoButton_CheckBox(pId: &g_Config.m_ClShowHookCollOwn, pText: Localize(pStr: "Show own player's hook collision line" ), Checked: g_Config.m_ClShowHookCollOwn, pRect: &Button)) |
| 2376 | { |
| 2377 | g_Config.m_ClShowHookCollOwn = g_Config.m_ClShowHookCollOwn ? 0 : 1; |
| 2378 | } |
| 2379 | LeftView.HSplitTop(Cut: LineSize, pTop: &Button, pBottom: &LeftView); |
| 2380 | if(g_Config.m_ClShowHookCollOwn) |
| 2381 | { |
| 2382 | static int s_ShowHookCollOwn = 0; |
| 2383 | if(DoButton_CheckBox(pId: &s_ShowHookCollOwn, pText: Localize(pStr: "Always show own player's hook collision line" ), Checked: g_Config.m_ClShowHookCollOwn == 2, pRect: &Button)) |
| 2384 | g_Config.m_ClShowHookCollOwn = g_Config.m_ClShowHookCollOwn != 2 ? 2 : 1; |
| 2385 | } |
| 2386 | |
| 2387 | LeftView.HSplitTop(Cut: LineSize, pTop: &Button, pBottom: &LeftView); |
| 2388 | if(DoButton_CheckBox(pId: &g_Config.m_ClShowHookCollOther, pText: Localize(pStr: "Show other players' hook collision lines" ), Checked: g_Config.m_ClShowHookCollOther, pRect: &Button)) |
| 2389 | { |
| 2390 | g_Config.m_ClShowHookCollOther = g_Config.m_ClShowHookCollOther >= 1 ? 0 : 1; |
| 2391 | } |
| 2392 | LeftView.HSplitTop(Cut: LineSize, pTop: &Button, pBottom: &LeftView); |
| 2393 | if(g_Config.m_ClShowHookCollOther) |
| 2394 | { |
| 2395 | static int s_ShowHookCollOther = 0; |
| 2396 | if(DoButton_CheckBox(pId: &s_ShowHookCollOther, pText: Localize(pStr: "Always show other players' hook collision lines" ), Checked: g_Config.m_ClShowHookCollOther == 2, pRect: &Button)) |
| 2397 | g_Config.m_ClShowHookCollOther = g_Config.m_ClShowHookCollOther != 2 ? 2 : 1; |
| 2398 | } |
| 2399 | |
| 2400 | LeftView.HSplitTop(Cut: LineSize * 2.0f, pTop: &Button, pBottom: &LeftView); |
| 2401 | Ui()->DoScrollbarOption(pId: &g_Config.m_ClHookCollSize, pOption: &g_Config.m_ClHookCollSize, pRect: &Button, pStr: Localize(pStr: "Width of your own hook collision line" ), Min: 0, Max: 20, pScale: &CUi::ms_LinearScrollbarScale, Flags: CUi::SCROLLBAR_OPTION_MULTILINE); |
| 2402 | |
| 2403 | LeftView.HSplitTop(Cut: LineSize * 2.0f, pTop: &Button, pBottom: &LeftView); |
| 2404 | Ui()->DoScrollbarOption(pId: &g_Config.m_ClHookCollSizeOther, pOption: &g_Config.m_ClHookCollSizeOther, pRect: &Button, pStr: Localize(pStr: "Width of others' hook collision line" ), Min: 0, Max: 20, pScale: &CUi::ms_LinearScrollbarScale, Flags: CUi::SCROLLBAR_OPTION_MULTILINE); |
| 2405 | |
| 2406 | LeftView.HSplitTop(Cut: LineSize * 2.0f, pTop: &Button, pBottom: &LeftView); |
| 2407 | Ui()->DoScrollbarOption(pId: &g_Config.m_ClHookCollAlpha, pOption: &g_Config.m_ClHookCollAlpha, pRect: &Button, pStr: Localize(pStr: "Hook collision line opacity" ), Min: 0, Max: 100, pScale: &CUi::ms_LinearScrollbarScale, Flags: CUi::SCROLLBAR_OPTION_MULTILINE, pSuffix: "%" ); |
| 2408 | |
| 2409 | static CButtonContainer s_HookCollNoCollResetId, s_HookCollHookableCollResetId, s_HookCollTeeCollResetId; |
| 2410 | static int s_HookCollToolTip; |
| 2411 | |
| 2412 | Ui()->DoLabel_AutoLineSize(pText: Localize(pStr: "Colors of the hook collision line, in case of a possible collision with:" ), FontSize: 13.0f, |
| 2413 | Align: TEXTALIGN_ML, pRect: &LeftView, LineSize: HeadlineHeight); |
| 2414 | |
| 2415 | Ui()->DoButtonLogic(pId: &s_HookCollToolTip, Checked: 0, pRect: &LeftView, Flags: BUTTONFLAG_NONE); // Just for the tooltip, result ignored |
| 2416 | GameClient()->m_Tooltips.DoToolTip(pId: &s_HookCollToolTip, pNearRect: &LeftView, pText: Localize(pStr: "Your movements are not taken into account when calculating the line colors" )); |
| 2417 | DoLine_ColorPicker(pResetId: &s_HookCollNoCollResetId, LineSize: ColorPickerLineSize, LabelSize: ColorPickerLabelSize, BottomMargin: ColorPickerLineSpacing, pMainRect: &LeftView, pText: Localize(pStr: "Nothing hookable" ), pColorValue: &g_Config.m_ClHookCollColorNoColl, DefaultColor: ColorRGBA(1.0f, 0.0f, 0.0f, 1.0f), CheckBoxSpacing: false); |
| 2418 | DoLine_ColorPicker(pResetId: &s_HookCollHookableCollResetId, LineSize: ColorPickerLineSize, LabelSize: ColorPickerLabelSize, BottomMargin: ColorPickerLineSpacing, pMainRect: &LeftView, pText: Localize(pStr: "Something hookable" ), pColorValue: &g_Config.m_ClHookCollColorHookableColl, DefaultColor: ColorRGBA(130.0f / 255.0f, 232.0f / 255.0f, 160.0f / 255.0f, 1.0f), CheckBoxSpacing: false); |
| 2419 | DoLine_ColorPicker(pResetId: &s_HookCollTeeCollResetId, LineSize: ColorPickerLineSize, LabelSize: ColorPickerLabelSize, BottomMargin: ColorPickerLineSpacing, pMainRect: &LeftView, pText: Localize(pStr: "A Tee" ), pColorValue: &g_Config.m_ClHookCollColorTeeColl, DefaultColor: ColorRGBA(1.0f, 1.0f, 0.0f, 1.0f), CheckBoxSpacing: false); |
| 2420 | |
| 2421 | // ***** Hook collisions preview ***** // |
| 2422 | Ui()->DoLabel_AutoLineSize(pText: Localize(pStr: "Preview" ), FontSize: HeadlineFontSize, |
| 2423 | Align: TEXTALIGN_ML, pRect: &RightView, LineSize: HeadlineHeight); |
| 2424 | RightView.HSplitTop(Cut: 2 * MarginSmall, pTop: nullptr, pBottom: &RightView); |
| 2425 | |
| 2426 | auto DoHookCollision = [this](const vec2 &Pos, const float &Length, const int &Size, const ColorRGBA &Color, const bool &Invert) { |
| 2427 | ColorRGBA ColorModified = Color; |
| 2428 | if(Invert) |
| 2429 | ColorModified = color_invert(col: ColorModified); |
| 2430 | ColorModified = ColorModified.WithAlpha(alpha: (float)g_Config.m_ClHookCollAlpha / 100); |
| 2431 | Graphics()->TextureClear(); |
| 2432 | if(Size > 0) |
| 2433 | { |
| 2434 | Graphics()->QuadsBegin(); |
| 2435 | Graphics()->SetColor(ColorModified); |
| 2436 | float LineWidth = 0.5f + (float)(Size - 1) * 0.25f; |
| 2437 | IGraphics::CQuadItem QuadItem(Pos.x, Pos.y - LineWidth, Length, LineWidth * 2.f); |
| 2438 | Graphics()->QuadsDrawTL(pArray: &QuadItem, Num: 1); |
| 2439 | Graphics()->QuadsEnd(); |
| 2440 | } |
| 2441 | else |
| 2442 | { |
| 2443 | Graphics()->LinesBegin(); |
| 2444 | Graphics()->SetColor(ColorModified); |
| 2445 | IGraphics::CLineItem LineItem(Pos.x, Pos.y, Pos.x + Length, Pos.y); |
| 2446 | Graphics()->LinesDraw(pArray: &LineItem, Num: 1); |
| 2447 | Graphics()->LinesEnd(); |
| 2448 | } |
| 2449 | }; |
| 2450 | |
| 2451 | CTeeRenderInfo OwnSkinInfo; |
| 2452 | OwnSkinInfo.Apply(pSkin: GameClient()->m_Skins.Find(pName: g_Config.m_ClPlayerSkin)); |
| 2453 | OwnSkinInfo.ApplyColors(CustomColoredSkin: g_Config.m_ClPlayerUseCustomColor, ColorBody: g_Config.m_ClPlayerColorBody, ColorFeet: g_Config.m_ClPlayerColorFeet); |
| 2454 | OwnSkinInfo.m_Size = 50.0f; |
| 2455 | |
| 2456 | CTeeRenderInfo DummySkinInfo; |
| 2457 | DummySkinInfo.Apply(pSkin: GameClient()->m_Skins.Find(pName: g_Config.m_ClDummySkin)); |
| 2458 | DummySkinInfo.ApplyColors(CustomColoredSkin: g_Config.m_ClDummyUseCustomColor, ColorBody: g_Config.m_ClDummyColorBody, ColorFeet: g_Config.m_ClDummyColorFeet); |
| 2459 | DummySkinInfo.m_Size = 50.0f; |
| 2460 | |
| 2461 | vec2 TeeRenderPos, DummyRenderPos; |
| 2462 | |
| 2463 | const float LineLength = 150.f; |
| 2464 | const float LeftMargin = 30.f; |
| 2465 | |
| 2466 | const int TileScale = 32.0f; |
| 2467 | |
| 2468 | // Toggled via checkbox later, inverts some previews |
| 2469 | static bool s_HookCollPressed = false; |
| 2470 | |
| 2471 | CUIRect PreviewColl; |
| 2472 | |
| 2473 | // ***** Unhookable Tile Preview ***** |
| 2474 | CUIRect PreviewNoColl; |
| 2475 | RightView.HSplitTop(Cut: 50.0f, pTop: &PreviewNoColl, pBottom: &RightView); |
| 2476 | RightView.HSplitTop(Cut: 4 * MarginSmall, pTop: nullptr, pBottom: &RightView); |
| 2477 | TeeRenderPos = vec2(PreviewNoColl.x + LeftMargin, PreviewNoColl.y + PreviewNoColl.h / 2.0f); |
| 2478 | DoHookCollision(TeeRenderPos, PreviewNoColl.w - LineLength, g_Config.m_ClHookCollSize, color_cast<ColorRGBA>(hsl: ColorHSLA(g_Config.m_ClHookCollColorNoColl)), s_HookCollPressed); |
| 2479 | RenderTools()->RenderTee(pAnim: CAnimState::GetIdle(), pInfo: &OwnSkinInfo, Emote: 0, Dir: vec2(1.0f, 0.0f), Pos: TeeRenderPos); |
| 2480 | |
| 2481 | CUIRect NoHookTileRect; |
| 2482 | PreviewNoColl.VSplitRight(Cut: LineLength, pLeft: &PreviewNoColl, pRight: &NoHookTileRect); |
| 2483 | NoHookTileRect.VSplitLeft(Cut: 50.0f, pLeft: &NoHookTileRect, pRight: nullptr); |
| 2484 | NoHookTileRect.Margin(Cut: 10.0f, pOtherRect: &NoHookTileRect); |
| 2485 | |
| 2486 | // Render unhookable tile |
| 2487 | Graphics()->TextureClear(); |
| 2488 | Graphics()->TextureSet(Texture: GameClient()->m_MapImages.GetEntities(EntityLayerType: MAP_IMAGE_ENTITY_LAYER_TYPE_ALL_EXCEPT_SWITCH)); |
| 2489 | Graphics()->BlendNormal(); |
| 2490 | Graphics()->SetColor(r: 1.0f, g: 1.0f, b: 1.0f, a: 1.0f); |
| 2491 | RenderMap()->RenderTile(x: NoHookTileRect.x, y: NoHookTileRect.y, Index: TILE_NOHOOK, Scale: TileScale, Color: ColorRGBA(1.0f, 1.0f, 1.0f, 1.0f)); |
| 2492 | |
| 2493 | // ***** Hookable Tile Preview ***** |
| 2494 | RightView.HSplitTop(Cut: 50.0f, pTop: &PreviewColl, pBottom: &RightView); |
| 2495 | RightView.HSplitTop(Cut: 4 * MarginSmall, pTop: nullptr, pBottom: &RightView); |
| 2496 | TeeRenderPos = vec2(PreviewColl.x + LeftMargin, PreviewColl.y + PreviewColl.h / 2.0f); |
| 2497 | DoHookCollision(TeeRenderPos, PreviewColl.w - LineLength, g_Config.m_ClHookCollSize, color_cast<ColorRGBA>(hsl: ColorHSLA(g_Config.m_ClHookCollColorHookableColl)), s_HookCollPressed); |
| 2498 | RenderTools()->RenderTee(pAnim: CAnimState::GetIdle(), pInfo: &OwnSkinInfo, Emote: 0, Dir: vec2(1.0f, 0.0f), Pos: TeeRenderPos); |
| 2499 | |
| 2500 | CUIRect HookTileRect; |
| 2501 | PreviewColl.VSplitRight(Cut: LineLength, pLeft: &PreviewColl, pRight: &HookTileRect); |
| 2502 | HookTileRect.VSplitLeft(Cut: 50.0f, pLeft: &HookTileRect, pRight: nullptr); |
| 2503 | HookTileRect.Margin(Cut: 10.0f, pOtherRect: &HookTileRect); |
| 2504 | |
| 2505 | // Render hookable tile |
| 2506 | Graphics()->TextureClear(); |
| 2507 | Graphics()->TextureSet(Texture: GameClient()->m_MapImages.GetEntities(EntityLayerType: MAP_IMAGE_ENTITY_LAYER_TYPE_ALL_EXCEPT_SWITCH)); |
| 2508 | Graphics()->BlendNormal(); |
| 2509 | Graphics()->SetColor(r: 1.0f, g: 1.0f, b: 1.0f, a: 1.0f); |
| 2510 | RenderMap()->RenderTile(x: HookTileRect.x, y: HookTileRect.y, Index: TILE_SOLID, Scale: TileScale, Color: ColorRGBA(1.0f, 1.0f, 1.0f, 1.0f)); |
| 2511 | |
| 2512 | // ***** Hook Dummy Preview ***** |
| 2513 | RightView.HSplitTop(Cut: 50.0f, pTop: &PreviewColl, pBottom: &RightView); |
| 2514 | RightView.HSplitTop(Cut: 4 * MarginSmall, pTop: nullptr, pBottom: &RightView); |
| 2515 | TeeRenderPos = vec2(PreviewColl.x + LeftMargin, PreviewColl.y + PreviewColl.h / 2.0f); |
| 2516 | DummyRenderPos = vec2(PreviewColl.x + PreviewColl.w - LineLength - 5.f + LeftMargin, PreviewColl.y + PreviewColl.h / 2.0f); |
| 2517 | DoHookCollision(TeeRenderPos, PreviewColl.w - LineLength - 15.f, g_Config.m_ClHookCollSize, color_cast<ColorRGBA>(hsl: ColorHSLA(g_Config.m_ClHookCollColorTeeColl)), s_HookCollPressed); |
| 2518 | RenderTools()->RenderTee(pAnim: CAnimState::GetIdle(), pInfo: &DummySkinInfo, Emote: 0, Dir: vec2(1.0f, 0.0f), Pos: DummyRenderPos); |
| 2519 | RenderTools()->RenderTee(pAnim: CAnimState::GetIdle(), pInfo: &OwnSkinInfo, Emote: 0, Dir: vec2(1.0f, 0.0f), Pos: TeeRenderPos); |
| 2520 | |
| 2521 | // ***** Hook Dummy Reverse Preview ***** |
| 2522 | RightView.HSplitTop(Cut: 50.0f, pTop: &PreviewColl, pBottom: &RightView); |
| 2523 | RightView.HSplitTop(Cut: 4 * MarginSmall, pTop: nullptr, pBottom: &RightView); |
| 2524 | TeeRenderPos = vec2(PreviewColl.x + LeftMargin, PreviewColl.y + PreviewColl.h / 2.0f); |
| 2525 | DummyRenderPos = vec2(PreviewColl.x + PreviewColl.w - LineLength - 5.f + LeftMargin, PreviewColl.y + PreviewColl.h / 2.0f); |
| 2526 | DoHookCollision(TeeRenderPos, PreviewColl.w - LineLength - 15.f, g_Config.m_ClHookCollSizeOther, color_cast<ColorRGBA>(hsl: ColorHSLA(g_Config.m_ClHookCollColorTeeColl)), false); |
| 2527 | RenderTools()->RenderTee(pAnim: CAnimState::GetIdle(), pInfo: &OwnSkinInfo, Emote: 0, Dir: vec2(1.0f, 0.0f), Pos: DummyRenderPos); |
| 2528 | RenderTools()->RenderTee(pAnim: CAnimState::GetIdle(), pInfo: &DummySkinInfo, Emote: 0, Dir: vec2(1.0f, 0.0f), Pos: TeeRenderPos); |
| 2529 | |
| 2530 | // ***** Preview +hookcoll pressed toggle ***** |
| 2531 | RightView.HSplitTop(Cut: LineSize, pTop: &Button, pBottom: &RightView); |
| 2532 | if(DoButton_CheckBox(pId: &s_HookCollPressed, pText: Localize(pStr: "Preview 'Hook collisions' being pressed" ), Checked: s_HookCollPressed, pRect: &Button)) |
| 2533 | s_HookCollPressed = !s_HookCollPressed; |
| 2534 | } |
| 2535 | else if(s_CurTab == APPEARANCE_TAB_INFO_MESSAGES) |
| 2536 | { |
| 2537 | MainView.VSplitMid(pLeft: &LeftView, pRight: &RightView, Spacing: MarginBetweenViews); |
| 2538 | |
| 2539 | // ***** Info Messages ***** // |
| 2540 | Ui()->DoLabel_AutoLineSize(pText: Localize(pStr: "Info Messages" ), FontSize: HeadlineFontSize, |
| 2541 | Align: TEXTALIGN_ML, pRect: &LeftView, LineSize: HeadlineHeight); |
| 2542 | LeftView.HSplitTop(Cut: MarginSmall, pTop: nullptr, pBottom: &LeftView); |
| 2543 | |
| 2544 | // General info messages settings |
| 2545 | LeftView.HSplitTop(Cut: LineSize, pTop: &Button, pBottom: &LeftView); |
| 2546 | if(DoButton_CheckBox(pId: &g_Config.m_ClShowKillMessages, pText: Localize(pStr: "Show kill messages" ), Checked: g_Config.m_ClShowKillMessages, pRect: &Button)) |
| 2547 | { |
| 2548 | g_Config.m_ClShowKillMessages ^= 1; |
| 2549 | } |
| 2550 | |
| 2551 | LeftView.HSplitTop(Cut: LineSize, pTop: &Button, pBottom: &LeftView); |
| 2552 | if(DoButton_CheckBox(pId: &g_Config.m_ClShowFinishMessages, pText: Localize(pStr: "Show finish messages" ), Checked: g_Config.m_ClShowFinishMessages, pRect: &Button)) |
| 2553 | { |
| 2554 | g_Config.m_ClShowFinishMessages ^= 1; |
| 2555 | } |
| 2556 | |
| 2557 | static CButtonContainer s_KillMessageNormalColorId, s_KillMessageHighlightColorId; |
| 2558 | DoLine_ColorPicker(pResetId: &s_KillMessageNormalColorId, LineSize: ColorPickerLineSize, LabelSize: ColorPickerLabelSize, BottomMargin: ColorPickerLineSpacing, pMainRect: &LeftView, pText: Localize(pStr: "Normal Color" ), pColorValue: &g_Config.m_ClKillMessageNormalColor, DefaultColor: ColorRGBA(1.0f, 1.0f, 1.0f), CheckBoxSpacing: false); |
| 2559 | DoLine_ColorPicker(pResetId: &s_KillMessageHighlightColorId, LineSize: ColorPickerLineSize, LabelSize: ColorPickerLabelSize, BottomMargin: ColorPickerLineSpacing, pMainRect: &LeftView, pText: Localize(pStr: "Highlight Color" ), pColorValue: &g_Config.m_ClKillMessageHighlightColor, DefaultColor: ColorRGBA(1.0f, 1.0f, 1.0f), CheckBoxSpacing: false); |
| 2560 | } |
| 2561 | else if(s_CurTab == APPEARANCE_TAB_LASER) |
| 2562 | { |
| 2563 | MainView.VSplitMid(pLeft: &LeftView, pRight: &RightView, Spacing: MarginBetweenViews); |
| 2564 | |
| 2565 | // ***** Weapons ***** // |
| 2566 | Ui()->DoLabel_AutoLineSize(pText: Localize(pStr: "Weapons" ), FontSize: HeadlineFontSize, |
| 2567 | Align: TEXTALIGN_ML, pRect: &LeftView, LineSize: HeadlineHeight); |
| 2568 | LeftView.HSplitTop(Cut: MarginSmall, pTop: nullptr, pBottom: &LeftView); |
| 2569 | |
| 2570 | // General weapon laser settings |
| 2571 | static CButtonContainer s_LaserRifleOutResetId, s_LaserRifleInResetId, s_LaserShotgunOutResetId, s_LaserShotgunInResetId; |
| 2572 | |
| 2573 | ColorHSLA LaserRifleOutlineColor = DoLine_ColorPicker(pResetId: &s_LaserRifleOutResetId, LineSize: ColorPickerLineSize, LabelSize: ColorPickerLabelSize, BottomMargin: ColorPickerLineSpacing, pMainRect: &LeftView, pText: Localize(pStr: "Rifle Laser Outline Color" ), pColorValue: &g_Config.m_ClLaserRifleOutlineColor, DefaultColor: ColorRGBA(0.074402f, 0.074402f, 0.247166f, 1.0f), CheckBoxSpacing: false); |
| 2574 | ColorHSLA LaserRifleInnerColor = DoLine_ColorPicker(pResetId: &s_LaserRifleInResetId, LineSize: ColorPickerLineSize, LabelSize: ColorPickerLabelSize, BottomMargin: ColorPickerLineSpacing, pMainRect: &LeftView, pText: Localize(pStr: "Rifle Laser Inner Color" ), pColorValue: &g_Config.m_ClLaserRifleInnerColor, DefaultColor: ColorRGBA(0.498039f, 0.498039f, 1.0f, 1.0f), CheckBoxSpacing: false); |
| 2575 | ColorHSLA LaserShotgunOutlineColor = DoLine_ColorPicker(pResetId: &s_LaserShotgunOutResetId, LineSize: ColorPickerLineSize, LabelSize: ColorPickerLabelSize, BottomMargin: ColorPickerLineSpacing, pMainRect: &LeftView, pText: Localize(pStr: "Shotgun Laser Outline Color" ), pColorValue: &g_Config.m_ClLaserShotgunOutlineColor, DefaultColor: ColorRGBA(0.125490f, 0.098039f, 0.043137f, 1.0f), CheckBoxSpacing: false); |
| 2576 | ColorHSLA LaserShotgunInnerColor = DoLine_ColorPicker(pResetId: &s_LaserShotgunInResetId, LineSize: ColorPickerLineSize, LabelSize: ColorPickerLabelSize, BottomMargin: ColorPickerLineSpacing, pMainRect: &LeftView, pText: Localize(pStr: "Shotgun Laser Inner Color" ), pColorValue: &g_Config.m_ClLaserShotgunInnerColor, DefaultColor: ColorRGBA(0.570588f, 0.417647f, 0.252941f, 1.0f), CheckBoxSpacing: false); |
| 2577 | |
| 2578 | // ***** Entities ***** // |
| 2579 | LeftView.HSplitTop(Cut: 10.0f, pTop: nullptr, pBottom: &LeftView); |
| 2580 | Ui()->DoLabel_AutoLineSize(pText: Localize(pStr: "Entities" ), FontSize: HeadlineFontSize, |
| 2581 | Align: TEXTALIGN_ML, pRect: &LeftView, LineSize: HeadlineHeight); |
| 2582 | LeftView.HSplitTop(Cut: MarginSmall, pTop: nullptr, pBottom: &LeftView); |
| 2583 | |
| 2584 | // General entity laser settings |
| 2585 | static CButtonContainer s_LaserDoorOutResetId, s_LaserDoorInResetId, s_LaserFreezeOutResetId, s_LaserFreezeInResetId, s_LaserDraggerOutResetId, s_LaserDraggerInResetId; |
| 2586 | |
| 2587 | ColorHSLA LaserDoorOutlineColor = DoLine_ColorPicker(pResetId: &s_LaserDoorOutResetId, LineSize: ColorPickerLineSize, LabelSize: ColorPickerLabelSize, BottomMargin: ColorPickerLineSpacing, pMainRect: &LeftView, pText: Localize(pStr: "Door Laser Outline Color" ), pColorValue: &g_Config.m_ClLaserDoorOutlineColor, DefaultColor: ColorRGBA(0.0f, 0.131372f, 0.096078f, 1.0f), CheckBoxSpacing: false); |
| 2588 | ColorHSLA LaserDoorInnerColor = DoLine_ColorPicker(pResetId: &s_LaserDoorInResetId, LineSize: ColorPickerLineSize, LabelSize: ColorPickerLabelSize, BottomMargin: ColorPickerLineSpacing, pMainRect: &LeftView, pText: Localize(pStr: "Door Laser Inner Color" ), pColorValue: &g_Config.m_ClLaserDoorInnerColor, DefaultColor: ColorRGBA(0.262745f, 0.760784f, 0.639215f, 1.0f), CheckBoxSpacing: false); |
| 2589 | ColorHSLA LaserFreezeOutlineColor = DoLine_ColorPicker(pResetId: &s_LaserFreezeOutResetId, LineSize: ColorPickerLineSize, LabelSize: ColorPickerLabelSize, BottomMargin: ColorPickerLineSpacing, pMainRect: &LeftView, pText: Localize(pStr: "Freeze Laser Outline Color" ), pColorValue: &g_Config.m_ClLaserFreezeOutlineColor, DefaultColor: ColorRGBA(0.131372f, 0.123529f, 0.182352f, 1.0f), CheckBoxSpacing: false); |
| 2590 | ColorHSLA LaserFreezeInnerColor = DoLine_ColorPicker(pResetId: &s_LaserFreezeInResetId, LineSize: ColorPickerLineSize, LabelSize: ColorPickerLabelSize, BottomMargin: ColorPickerLineSpacing, pMainRect: &LeftView, pText: Localize(pStr: "Freeze Laser Inner Color" ), pColorValue: &g_Config.m_ClLaserFreezeInnerColor, DefaultColor: ColorRGBA(0.482352f, 0.443137f, 0.564705f, 1.0f), CheckBoxSpacing: false); |
| 2591 | ColorHSLA LaserDraggerOutlineColor = DoLine_ColorPicker(pResetId: &s_LaserDraggerOutResetId, LineSize: ColorPickerLineSize, LabelSize: ColorPickerLabelSize, BottomMargin: ColorPickerLineSpacing, pMainRect: &LeftView, pText: Localize(pStr: "Dragger Outline Color" ), pColorValue: &g_Config.m_ClLaserDraggerOutlineColor, DefaultColor: ColorRGBA(0.1640625f, 0.015625f, 0.015625f, 1.0f), CheckBoxSpacing: false); |
| 2592 | ColorHSLA LaserDraggerInnerColor = DoLine_ColorPicker(pResetId: &s_LaserDraggerInResetId, LineSize: ColorPickerLineSize, LabelSize: ColorPickerLabelSize, BottomMargin: ColorPickerLineSpacing, pMainRect: &LeftView, pText: Localize(pStr: "Dragger Inner Color" ), pColorValue: &g_Config.m_ClLaserDraggerInnerColor, DefaultColor: ColorRGBA(.8666666f, .3725490f, .3725490f, 1.0f), CheckBoxSpacing: false); |
| 2593 | |
| 2594 | static CButtonContainer s_AllToRifleResetId, s_AllToDefaultResetId; |
| 2595 | |
| 2596 | LeftView.HSplitTop(Cut: 4 * MarginSmall, pTop: nullptr, pBottom: &LeftView); |
| 2597 | LeftView.HSplitTop(Cut: LineSize, pTop: &Button, pBottom: &LeftView); |
| 2598 | if(DoButton_Menu(pButtonContainer: &s_AllToRifleResetId, pText: Localize(pStr: "Set all to Rifle" ), Checked: 0, pRect: &Button)) |
| 2599 | { |
| 2600 | g_Config.m_ClLaserShotgunOutlineColor = g_Config.m_ClLaserRifleOutlineColor; |
| 2601 | g_Config.m_ClLaserShotgunInnerColor = g_Config.m_ClLaserRifleInnerColor; |
| 2602 | g_Config.m_ClLaserDoorOutlineColor = g_Config.m_ClLaserRifleOutlineColor; |
| 2603 | g_Config.m_ClLaserDoorInnerColor = g_Config.m_ClLaserRifleInnerColor; |
| 2604 | g_Config.m_ClLaserFreezeOutlineColor = g_Config.m_ClLaserRifleOutlineColor; |
| 2605 | g_Config.m_ClLaserFreezeInnerColor = g_Config.m_ClLaserRifleInnerColor; |
| 2606 | g_Config.m_ClLaserDraggerOutlineColor = g_Config.m_ClLaserRifleOutlineColor; |
| 2607 | g_Config.m_ClLaserDraggerInnerColor = g_Config.m_ClLaserRifleInnerColor; |
| 2608 | } |
| 2609 | |
| 2610 | // values taken from the CL commands |
| 2611 | LeftView.HSplitTop(Cut: 2 * MarginSmall, pTop: nullptr, pBottom: &LeftView); |
| 2612 | LeftView.HSplitTop(Cut: LineSize, pTop: &Button, pBottom: &LeftView); |
| 2613 | if(DoButton_Menu(pButtonContainer: &s_AllToDefaultResetId, pText: Localize(pStr: "Reset to defaults" ), Checked: 0, pRect: &Button)) |
| 2614 | { |
| 2615 | g_Config.m_ClLaserRifleOutlineColor = 11176233; |
| 2616 | g_Config.m_ClLaserRifleInnerColor = 11206591; |
| 2617 | g_Config.m_ClLaserShotgunOutlineColor = 1866773; |
| 2618 | g_Config.m_ClLaserShotgunInnerColor = 1467241; |
| 2619 | g_Config.m_ClLaserDoorOutlineColor = 7667473; |
| 2620 | g_Config.m_ClLaserDoorInnerColor = 7701379; |
| 2621 | g_Config.m_ClLaserFreezeOutlineColor = 11613223; |
| 2622 | g_Config.m_ClLaserFreezeInnerColor = 12001153; |
| 2623 | g_Config.m_ClLaserDraggerOutlineColor = 57618; |
| 2624 | g_Config.m_ClLaserDraggerInnerColor = 42398; |
| 2625 | } |
| 2626 | |
| 2627 | // ***** Laser Preview ***** // |
| 2628 | Ui()->DoLabel_AutoLineSize(pText: Localize(pStr: "Preview" ), FontSize: HeadlineFontSize, |
| 2629 | Align: TEXTALIGN_ML, pRect: &RightView, LineSize: HeadlineHeight); |
| 2630 | RightView.HSplitTop(Cut: MarginSmall, pTop: nullptr, pBottom: &RightView); |
| 2631 | |
| 2632 | const float LaserPreviewHeight = 60.0f; |
| 2633 | CUIRect LaserPreview; |
| 2634 | RightView.HSplitTop(Cut: LaserPreviewHeight, pTop: &LaserPreview, pBottom: &RightView); |
| 2635 | RightView.HSplitTop(Cut: 2 * MarginSmall, pTop: nullptr, pBottom: &RightView); |
| 2636 | DoLaserPreview(pRect: &LaserPreview, OutlineColor: LaserRifleOutlineColor, InnerColor: LaserRifleInnerColor, LaserType: LASERTYPE_RIFLE); |
| 2637 | |
| 2638 | RightView.HSplitTop(Cut: LaserPreviewHeight, pTop: &LaserPreview, pBottom: &RightView); |
| 2639 | RightView.HSplitTop(Cut: 2 * MarginSmall, pTop: nullptr, pBottom: &RightView); |
| 2640 | DoLaserPreview(pRect: &LaserPreview, OutlineColor: LaserShotgunOutlineColor, InnerColor: LaserShotgunInnerColor, LaserType: LASERTYPE_SHOTGUN); |
| 2641 | |
| 2642 | RightView.HSplitTop(Cut: LaserPreviewHeight, pTop: &LaserPreview, pBottom: &RightView); |
| 2643 | RightView.HSplitTop(Cut: 2 * MarginSmall, pTop: nullptr, pBottom: &RightView); |
| 2644 | DoLaserPreview(pRect: &LaserPreview, OutlineColor: LaserDoorOutlineColor, InnerColor: LaserDoorInnerColor, LaserType: LASERTYPE_DOOR); |
| 2645 | |
| 2646 | RightView.HSplitTop(Cut: LaserPreviewHeight, pTop: &LaserPreview, pBottom: &RightView); |
| 2647 | RightView.HSplitTop(Cut: 2 * MarginSmall, pTop: nullptr, pBottom: &RightView); |
| 2648 | DoLaserPreview(pRect: &LaserPreview, OutlineColor: LaserFreezeOutlineColor, InnerColor: LaserFreezeInnerColor, LaserType: LASERTYPE_FREEZE); |
| 2649 | |
| 2650 | RightView.HSplitTop(Cut: LaserPreviewHeight, pTop: &LaserPreview, pBottom: &RightView); |
| 2651 | RightView.HSplitTop(Cut: 2 * MarginSmall, pTop: nullptr, pBottom: &RightView); |
| 2652 | DoLaserPreview(pRect: &LaserPreview, OutlineColor: LaserDraggerOutlineColor, InnerColor: LaserDraggerInnerColor, LaserType: LASERTYPE_DRAGGER); |
| 2653 | } |
| 2654 | } |
| 2655 | |
| 2656 | void CMenus::(CUIRect MainView) |
| 2657 | { |
| 2658 | CUIRect Button, Left, Right, LeftLeft, Label; |
| 2659 | |
| 2660 | #if defined(CONF_AUTOUPDATE) |
| 2661 | CUIRect UpdaterRect; |
| 2662 | MainView.HSplitBottom(Cut: 20.0f, pTop: &MainView, pBottom: &UpdaterRect); |
| 2663 | MainView.HSplitBottom(Cut: 5.0f, pTop: &MainView, pBottom: nullptr); |
| 2664 | #endif |
| 2665 | |
| 2666 | // demo |
| 2667 | CUIRect Demo; |
| 2668 | MainView.HSplitTop(Cut: 110.0f, pTop: &Demo, pBottom: &MainView); |
| 2669 | Demo.HSplitTop(Cut: 30.0f, pTop: &Label, pBottom: &Demo); |
| 2670 | Ui()->DoLabel(pRect: &Label, pText: Localize(pStr: "Demo" ), Size: 20.0f, Align: TEXTALIGN_ML); |
| 2671 | Label.VSplitMid(pLeft: nullptr, pRight: &Label, Spacing: 20.0f); |
| 2672 | Ui()->DoLabel(pRect: &Label, pText: Localize(pStr: "Ghost" ), Size: 20.0f, Align: TEXTALIGN_ML); |
| 2673 | |
| 2674 | Demo.HSplitTop(Cut: 5.0f, pTop: nullptr, pBottom: &Demo); |
| 2675 | Demo.VSplitMid(pLeft: &Left, pRight: &Right, Spacing: 20.0f); |
| 2676 | |
| 2677 | Left.HSplitTop(Cut: 20.0f, pTop: &Button, pBottom: &Left); |
| 2678 | if(DoButton_CheckBox(pId: &g_Config.m_ClAutoRaceRecord, pText: Localize(pStr: "Save the best demo of each race" ), Checked: g_Config.m_ClAutoRaceRecord, pRect: &Button)) |
| 2679 | { |
| 2680 | g_Config.m_ClAutoRaceRecord ^= 1; |
| 2681 | } |
| 2682 | |
| 2683 | Left.HSplitTop(Cut: 20.0f, pTop: &Button, pBottom: &Left); |
| 2684 | if(DoButton_CheckBox(pId: &g_Config.m_ClReplays, pText: Localize(pStr: "Enable replays" ), Checked: g_Config.m_ClReplays, pRect: &Button)) |
| 2685 | { |
| 2686 | g_Config.m_ClReplays ^= 1; |
| 2687 | Client()->DemoRecorder_UpdateReplayRecorder(); |
| 2688 | } |
| 2689 | |
| 2690 | Left.HSplitTop(Cut: 20.0f, pTop: &Button, pBottom: &Left); |
| 2691 | if(g_Config.m_ClReplays) |
| 2692 | Ui()->DoScrollbarOption(pId: &g_Config.m_ClReplayLength, pOption: &g_Config.m_ClReplayLength, pRect: &Button, pStr: Localize(pStr: "Default length" ), Min: 10, Max: 600, pScale: &CUi::ms_LinearScrollbarScale, Flags: CUi::SCROLLBAR_OPTION_NOCLAMPVALUE); |
| 2693 | |
| 2694 | Right.HSplitTop(Cut: 20.0f, pTop: &Button, pBottom: &Right); |
| 2695 | if(DoButton_CheckBox(pId: &g_Config.m_ClRaceGhost, pText: Localize(pStr: "Enable ghost" ), Checked: g_Config.m_ClRaceGhost, pRect: &Button)) |
| 2696 | { |
| 2697 | g_Config.m_ClRaceGhost ^= 1; |
| 2698 | } |
| 2699 | GameClient()->m_Tooltips.DoToolTip(pId: &g_Config.m_ClRaceGhost, pNearRect: &Button, pText: Localize(pStr: "When you cross the start line, show a ghost tee replicating the movements of your best time" )); |
| 2700 | |
| 2701 | if(g_Config.m_ClRaceGhost) |
| 2702 | { |
| 2703 | Right.HSplitTop(Cut: 20.0f, pTop: &Button, pBottom: &Right); |
| 2704 | Button.VSplitMid(pLeft: &LeftLeft, pRight: &Button); |
| 2705 | if(DoButton_CheckBox(pId: &g_Config.m_ClRaceShowGhost, pText: Localize(pStr: "Show ghost" ), Checked: g_Config.m_ClRaceShowGhost, pRect: &LeftLeft)) |
| 2706 | { |
| 2707 | g_Config.m_ClRaceShowGhost ^= 1; |
| 2708 | } |
| 2709 | Ui()->DoScrollbarOption(pId: &g_Config.m_ClRaceGhostAlpha, pOption: &g_Config.m_ClRaceGhostAlpha, pRect: &Button, pStr: Localize(pStr: "Opacity" ), Min: 0, Max: 100, pScale: &CUi::ms_LinearScrollbarScale, Flags: 0u, pSuffix: "%" ); |
| 2710 | |
| 2711 | Right.HSplitTop(Cut: 20.0f, pTop: &Button, pBottom: &Right); |
| 2712 | if(DoButton_CheckBox(pId: &g_Config.m_ClRaceSaveGhost, pText: Localize(pStr: "Save ghost" ), Checked: g_Config.m_ClRaceSaveGhost, pRect: &Button)) |
| 2713 | { |
| 2714 | g_Config.m_ClRaceSaveGhost ^= 1; |
| 2715 | } |
| 2716 | |
| 2717 | if(g_Config.m_ClRaceSaveGhost) |
| 2718 | { |
| 2719 | Right.HSplitTop(Cut: 20.0f, pTop: &Button, pBottom: &Right); |
| 2720 | if(DoButton_CheckBox(pId: &g_Config.m_ClRaceGhostSaveBest, pText: Localize(pStr: "Only save improvements" ), Checked: g_Config.m_ClRaceGhostSaveBest, pRect: &Button)) |
| 2721 | { |
| 2722 | g_Config.m_ClRaceGhostSaveBest ^= 1; |
| 2723 | } |
| 2724 | } |
| 2725 | } |
| 2726 | |
| 2727 | // gameplay |
| 2728 | CUIRect Gameplay; |
| 2729 | MainView.HSplitTop(Cut: 150.0f, pTop: &Gameplay, pBottom: &MainView); |
| 2730 | Gameplay.HSplitTop(Cut: 30.0f, pTop: &Label, pBottom: &Gameplay); |
| 2731 | Ui()->DoLabel(pRect: &Label, pText: Localize(pStr: "Gameplay" ), Size: 20.0f, Align: TEXTALIGN_ML); |
| 2732 | Gameplay.HSplitTop(Cut: 5.0f, pTop: nullptr, pBottom: &Gameplay); |
| 2733 | Gameplay.VSplitMid(pLeft: &Left, pRight: &Right, Spacing: 20.0f); |
| 2734 | |
| 2735 | Left.HSplitTop(Cut: 20.0f, pTop: &Button, pBottom: &Left); |
| 2736 | Ui()->DoScrollbarOption(pId: &g_Config.m_ClOverlayEntities, pOption: &g_Config.m_ClOverlayEntities, pRect: &Button, pStr: Localize(pStr: "Overlay entities" ), Min: 0, Max: 100); |
| 2737 | |
| 2738 | Left.HSplitTop(Cut: 20.0f, pTop: &Button, pBottom: &Left); |
| 2739 | Button.VSplitMid(pLeft: &LeftLeft, pRight: &Button); |
| 2740 | |
| 2741 | if(DoButton_CheckBox(pId: &g_Config.m_ClTextEntities, pText: Localize(pStr: "Show text entities" ), Checked: g_Config.m_ClTextEntities, pRect: &LeftLeft)) |
| 2742 | g_Config.m_ClTextEntities ^= 1; |
| 2743 | |
| 2744 | if(g_Config.m_ClTextEntities) |
| 2745 | { |
| 2746 | if(Ui()->DoScrollbarOption(pId: &g_Config.m_ClTextEntitiesSize, pOption: &g_Config.m_ClTextEntitiesSize, pRect: &Button, pStr: Localize(pStr: "Size" ), Min: 20, Max: 100, pScale: &CUi::ms_LinearScrollbarScale, Flags: CUi::SCROLLBAR_OPTION_DELAYUPDATE)) |
| 2747 | GameClient()->m_MapImages.SetTextureScale(g_Config.m_ClTextEntitiesSize); |
| 2748 | } |
| 2749 | |
| 2750 | Left.HSplitTop(Cut: 20.0f, pTop: &Button, pBottom: &Left); |
| 2751 | Button.VSplitMid(pLeft: &LeftLeft, pRight: &Button); |
| 2752 | |
| 2753 | if(DoButton_CheckBox(pId: &g_Config.m_ClShowOthers, pText: Localize(pStr: "Show others" ), Checked: g_Config.m_ClShowOthers == SHOW_OTHERS_ON, pRect: &LeftLeft)) |
| 2754 | g_Config.m_ClShowOthers = g_Config.m_ClShowOthers != SHOW_OTHERS_ON ? SHOW_OTHERS_ON : SHOW_OTHERS_OFF; |
| 2755 | |
| 2756 | Ui()->DoScrollbarOption(pId: &g_Config.m_ClShowOthersAlpha, pOption: &g_Config.m_ClShowOthersAlpha, pRect: &Button, pStr: Localize(pStr: "Opacity" ), Min: 0, Max: 100, pScale: &CUi::ms_LinearScrollbarScale, Flags: 0u, pSuffix: "%" ); |
| 2757 | |
| 2758 | GameClient()->m_Tooltips.DoToolTip(pId: &g_Config.m_ClShowOthersAlpha, pNearRect: &Button, pText: Localize(pStr: "Adjust the opacity of entities belonging to other teams, such as tees and name plates" )); |
| 2759 | |
| 2760 | Left.HSplitTop(Cut: 20.0f, pTop: &Button, pBottom: &Left); |
| 2761 | static int s_ShowOwnTeamId = 0; |
| 2762 | if(DoButton_CheckBox(pId: &s_ShowOwnTeamId, pText: Localize(pStr: "Show others (own team only)" ), Checked: g_Config.m_ClShowOthers == SHOW_OTHERS_ONLY_TEAM, pRect: &Button)) |
| 2763 | { |
| 2764 | g_Config.m_ClShowOthers = g_Config.m_ClShowOthers != SHOW_OTHERS_ONLY_TEAM ? SHOW_OTHERS_ONLY_TEAM : SHOW_OTHERS_OFF; |
| 2765 | } |
| 2766 | |
| 2767 | Left.HSplitTop(Cut: 20.0f, pTop: &Button, pBottom: &Left); |
| 2768 | if(DoButton_CheckBox(pId: &g_Config.m_ClShowQuads, pText: Localize(pStr: "Show background quads" ), Checked: g_Config.m_ClShowQuads, pRect: &Button)) |
| 2769 | { |
| 2770 | g_Config.m_ClShowQuads ^= 1; |
| 2771 | } |
| 2772 | GameClient()->m_Tooltips.DoToolTip(pId: &g_Config.m_ClShowQuads, pNearRect: &Button, pText: Localize(pStr: "Quads are used for background decoration" )); |
| 2773 | |
| 2774 | Right.HSplitTop(Cut: 20.0f, pTop: &Button, pBottom: &Right); |
| 2775 | if(Ui()->DoScrollbarOption(pId: &g_Config.m_ClDefaultZoom, pOption: &g_Config.m_ClDefaultZoom, pRect: &Button, pStr: Localize(pStr: "Default zoom" ), Min: 0, Max: 20)) |
| 2776 | GameClient()->m_Camera.SetZoom(Target: CCamera::ZoomStepsToValue(Steps: g_Config.m_ClDefaultZoom - 10), Smoothness: g_Config.m_ClSmoothZoomTime, IsUser: true); |
| 2777 | |
| 2778 | Right.HSplitTop(Cut: 20.0f, pTop: &Button, pBottom: &Right); |
| 2779 | if(DoButton_CheckBox(pId: &g_Config.m_ClAntiPing, pText: Localize(pStr: "AntiPing" ), Checked: g_Config.m_ClAntiPing, pRect: &Button)) |
| 2780 | { |
| 2781 | g_Config.m_ClAntiPing ^= 1; |
| 2782 | } |
| 2783 | GameClient()->m_Tooltips.DoToolTip(pId: &g_Config.m_ClAntiPing, pNearRect: &Button, pText: Localize(pStr: "Tries to predict other entities to give a feel of low latency" )); |
| 2784 | |
| 2785 | if(g_Config.m_ClAntiPing) |
| 2786 | { |
| 2787 | Right.HSplitTop(Cut: 20.0f, pTop: &Button, pBottom: &Right); |
| 2788 | if(DoButton_CheckBox(pId: &g_Config.m_ClAntiPingPlayers, pText: Localize(pStr: "AntiPing: predict other players" ), Checked: g_Config.m_ClAntiPingPlayers, pRect: &Button)) |
| 2789 | { |
| 2790 | g_Config.m_ClAntiPingPlayers ^= 1; |
| 2791 | } |
| 2792 | |
| 2793 | Right.HSplitTop(Cut: 20.0f, pTop: &Button, pBottom: &Right); |
| 2794 | if(DoButton_CheckBox(pId: &g_Config.m_ClAntiPingWeapons, pText: Localize(pStr: "AntiPing: predict weapons" ), Checked: g_Config.m_ClAntiPingWeapons, pRect: &Button)) |
| 2795 | { |
| 2796 | g_Config.m_ClAntiPingWeapons ^= 1; |
| 2797 | } |
| 2798 | |
| 2799 | Right.HSplitTop(Cut: 20.0f, pTop: &Button, pBottom: &Right); |
| 2800 | if(DoButton_CheckBox(pId: &g_Config.m_ClAntiPingGrenade, pText: Localize(pStr: "AntiPing: predict grenade paths" ), Checked: g_Config.m_ClAntiPingGrenade, pRect: &Button)) |
| 2801 | { |
| 2802 | g_Config.m_ClAntiPingGrenade ^= 1; |
| 2803 | } |
| 2804 | Right.HSplitTop(Cut: 20.0f, pTop: &Button, pBottom: &Right); |
| 2805 | Ui()->DoScrollbarOption(pId: &g_Config.m_ClPredictionMargin, pOption: &g_Config.m_ClPredictionMargin, pRect: &Button, pStr: Localize(pStr: "AntiPing: prediction margin" ), Min: 1, Max: 300); |
| 2806 | } |
| 2807 | |
| 2808 | CUIRect Background, Miscellaneous; |
| 2809 | MainView.VSplitMid(pLeft: &Background, pRight: &Miscellaneous, Spacing: 20.0f); |
| 2810 | |
| 2811 | // background |
| 2812 | Background.HSplitTop(Cut: 30.0f, pTop: &Label, pBottom: &Background); |
| 2813 | Background.HSplitTop(Cut: 5.0f, pTop: nullptr, pBottom: &Background); |
| 2814 | Ui()->DoLabel(pRect: &Label, pText: Localize(pStr: "Background" ), Size: 20.0f, Align: TEXTALIGN_ML); |
| 2815 | |
| 2816 | ColorRGBA GreyDefault(0.5f, 0.5f, 0.5f, 1); |
| 2817 | |
| 2818 | static CButtonContainer s_ResetId1; |
| 2819 | DoLine_ColorPicker(pResetId: &s_ResetId1, LineSize: 25.0f, LabelSize: 13.0f, BottomMargin: 5.0f, pMainRect: &Background, pText: Localize(pStr: "Regular background color" ), pColorValue: &g_Config.m_ClBackgroundColor, DefaultColor: GreyDefault, CheckBoxSpacing: false); |
| 2820 | |
| 2821 | static CButtonContainer s_ResetId2; |
| 2822 | DoLine_ColorPicker(pResetId: &s_ResetId2, LineSize: 25.0f, LabelSize: 13.0f, BottomMargin: 5.0f, pMainRect: &Background, pText: Localize(pStr: "Entities background color" ), pColorValue: &g_Config.m_ClBackgroundEntitiesColor, DefaultColor: GreyDefault, CheckBoxSpacing: false); |
| 2823 | |
| 2824 | CUIRect EditBox, ReloadButton; |
| 2825 | Background.HSplitTop(Cut: 20.0f, pTop: &Label, pBottom: &Background); |
| 2826 | Background.HSplitTop(Cut: 2.0f, pTop: nullptr, pBottom: &Background); |
| 2827 | Label.VSplitLeft(Cut: 100.0f, pLeft: &Label, pRight: &EditBox); |
| 2828 | EditBox.VSplitRight(Cut: 60.0f, pLeft: &EditBox, pRight: &Button); |
| 2829 | Button.VSplitMid(pLeft: &ReloadButton, pRight: &Button, Spacing: 5.0f); |
| 2830 | EditBox.VSplitRight(Cut: 5.0f, pLeft: &EditBox, pRight: nullptr); |
| 2831 | |
| 2832 | Ui()->DoLabel(pRect: &Label, pText: Localize(pStr: "Map" ), Size: 14.0f, Align: TEXTALIGN_ML); |
| 2833 | |
| 2834 | static CLineInput s_BackgroundEntitiesInput(g_Config.m_ClBackgroundEntities, sizeof(g_Config.m_ClBackgroundEntities)); |
| 2835 | Ui()->DoEditBox(pLineInput: &s_BackgroundEntitiesInput, pRect: &EditBox, FontSize: 14.0f); |
| 2836 | |
| 2837 | static CButtonContainer s_BackgroundEntitiesMapPicker, s_BackgroundEntitiesReload; |
| 2838 | |
| 2839 | if(Ui()->DoButton_FontIcon(pButtonContainer: &s_BackgroundEntitiesReload, pText: FONT_ICON_ARROW_ROTATE_RIGHT, Checked: 0, pRect: &ReloadButton, Flags: BUTTONFLAG_LEFT)) |
| 2840 | { |
| 2841 | GameClient()->m_Background.LoadBackground(); |
| 2842 | } |
| 2843 | |
| 2844 | if(Ui()->DoButton_FontIcon(pButtonContainer: &s_BackgroundEntitiesMapPicker, pText: FONT_ICON_FOLDER, Checked: 0, pRect: &Button, Flags: BUTTONFLAG_LEFT)) |
| 2845 | { |
| 2846 | static SPopupMenuId ; |
| 2847 | static CPopupMapPickerContext ; |
| 2848 | s_PopupMapPickerContext.m_pMenus = this; |
| 2849 | s_PopupMapPickerContext.MapListPopulate(); |
| 2850 | Ui()->DoPopupMenu(pId: &s_PopupMapPickerId, X: Ui()->MouseX(), Y: Ui()->MouseY(), Width: 300.0f, Height: 250.0f, pContext: &s_PopupMapPickerContext, pfnFunc: PopupMapPicker); |
| 2851 | } |
| 2852 | |
| 2853 | Background.HSplitTop(Cut: 20.0f, pTop: &Button, pBottom: &Background); |
| 2854 | const bool UseCurrentMap = str_comp(a: g_Config.m_ClBackgroundEntities, CURRENT_MAP) == 0; |
| 2855 | static int s_UseCurrentMapId = 0; |
| 2856 | if(DoButton_CheckBox(pId: &s_UseCurrentMapId, pText: Localize(pStr: "Use current map as background" ), Checked: UseCurrentMap, pRect: &Button)) |
| 2857 | { |
| 2858 | if(UseCurrentMap) |
| 2859 | g_Config.m_ClBackgroundEntities[0] = '\0'; |
| 2860 | else |
| 2861 | str_copy(dst&: g_Config.m_ClBackgroundEntities, CURRENT_MAP); |
| 2862 | GameClient()->m_Background.LoadBackground(); |
| 2863 | } |
| 2864 | |
| 2865 | Background.HSplitTop(Cut: 20.0f, pTop: &Button, pBottom: &Background); |
| 2866 | if(DoButton_CheckBox(pId: &g_Config.m_ClBackgroundShowTilesLayers, pText: Localize(pStr: "Show tiles layers from BG map" ), Checked: g_Config.m_ClBackgroundShowTilesLayers, pRect: &Button)) |
| 2867 | g_Config.m_ClBackgroundShowTilesLayers ^= 1; |
| 2868 | |
| 2869 | // miscellaneous |
| 2870 | Miscellaneous.HSplitTop(Cut: 30.0f, pTop: &Label, pBottom: &Miscellaneous); |
| 2871 | Miscellaneous.HSplitTop(Cut: 5.0f, pTop: nullptr, pBottom: &Miscellaneous); |
| 2872 | |
| 2873 | Ui()->DoLabel(pRect: &Label, pText: Localize(pStr: "Miscellaneous" ), Size: 20.0f, Align: TEXTALIGN_ML); |
| 2874 | |
| 2875 | static CButtonContainer s_ButtonTimeout; |
| 2876 | Miscellaneous.HSplitTop(Cut: 20.0f, pTop: &Button, pBottom: &Miscellaneous); |
| 2877 | if(DoButton_Menu(pButtonContainer: &s_ButtonTimeout, pText: Localize(pStr: "New random timeout code" ), Checked: 0, pRect: &Button)) |
| 2878 | { |
| 2879 | Client()->GenerateTimeoutSeed(); |
| 2880 | } |
| 2881 | |
| 2882 | Miscellaneous.HSplitTop(Cut: 5.0f, pTop: nullptr, pBottom: &Miscellaneous); |
| 2883 | Miscellaneous.HSplitTop(Cut: 20.0f, pTop: &Label, pBottom: &Miscellaneous); |
| 2884 | Miscellaneous.HSplitTop(Cut: 2.0f, pTop: nullptr, pBottom: &Miscellaneous); |
| 2885 | Ui()->DoLabel(pRect: &Label, pText: Localize(pStr: "Run on join" ), Size: 14.0f, Align: TEXTALIGN_ML); |
| 2886 | Miscellaneous.HSplitTop(Cut: 20.0f, pTop: &Button, pBottom: &Miscellaneous); |
| 2887 | static CLineInput s_RunOnJoinInput(g_Config.m_ClRunOnJoin, sizeof(g_Config.m_ClRunOnJoin)); |
| 2888 | s_RunOnJoinInput.SetEmptyText(Localize(pStr: "Chat command (e.g. showall 1)" )); |
| 2889 | Ui()->DoEditBox(pLineInput: &s_RunOnJoinInput, pRect: &Button, FontSize: 14.0f); |
| 2890 | |
| 2891 | #if defined(CONF_FAMILY_WINDOWS) |
| 2892 | static CButtonContainer s_ButtonUnregisterShell; |
| 2893 | Miscellaneous.HSplitTop(10.0f, nullptr, &Miscellaneous); |
| 2894 | Miscellaneous.HSplitTop(20.0f, &Button, &Miscellaneous); |
| 2895 | if(DoButton_Menu(&s_ButtonUnregisterShell, Localize("Unregister protocol and file extensions" ), 0, &Button)) |
| 2896 | { |
| 2897 | Client()->ShellUnregister(); |
| 2898 | } |
| 2899 | #endif |
| 2900 | |
| 2901 | // Updater |
| 2902 | #if defined(CONF_AUTOUPDATE) |
| 2903 | { |
| 2904 | bool NeedUpdate = str_comp(a: Client()->LatestVersion(), b: "0" ); |
| 2905 | IUpdater::EUpdaterState State = Updater()->GetCurrentState(); |
| 2906 | |
| 2907 | // Update Button |
| 2908 | char aBuf[256]; |
| 2909 | if(NeedUpdate && State <= IUpdater::CLEAN) |
| 2910 | { |
| 2911 | str_format(buffer: aBuf, buffer_size: sizeof(aBuf), format: Localize(pStr: "DDNet %s is available:" ), Client()->LatestVersion()); |
| 2912 | UpdaterRect.VSplitLeft(Cut: TextRender()->TextWidth(Size: 14.0f, pText: aBuf, StrLength: -1, LineWidth: -1.0f) + 10.0f, pLeft: &UpdaterRect, pRight: &Button); |
| 2913 | Button.VSplitLeft(Cut: 100.0f, pLeft: &Button, pRight: nullptr); |
| 2914 | static CButtonContainer s_ButtonUpdate; |
| 2915 | if(DoButton_Menu(pButtonContainer: &s_ButtonUpdate, pText: Localize(pStr: "Update now" ), Checked: 0, pRect: &Button)) |
| 2916 | { |
| 2917 | Updater()->InitiateUpdate(); |
| 2918 | } |
| 2919 | } |
| 2920 | else if(State >= IUpdater::GETTING_MANIFEST && State < IUpdater::NEED_RESTART) |
| 2921 | str_copy(dst&: aBuf, src: Localize(pStr: "Updating…" )); |
| 2922 | else if(State == IUpdater::NEED_RESTART) |
| 2923 | { |
| 2924 | str_copy(dst&: aBuf, src: Localize(pStr: "DDNet Client updated!" )); |
| 2925 | m_NeedRestartUpdate = true; |
| 2926 | } |
| 2927 | else |
| 2928 | { |
| 2929 | str_copy(dst&: aBuf, src: Localize(pStr: "No updates available" )); |
| 2930 | UpdaterRect.VSplitLeft(Cut: TextRender()->TextWidth(Size: 14.0f, pText: aBuf, StrLength: -1, LineWidth: -1.0f) + 10.0f, pLeft: &UpdaterRect, pRight: &Button); |
| 2931 | Button.VSplitLeft(Cut: 100.0f, pLeft: &Button, pRight: nullptr); |
| 2932 | static CButtonContainer s_ButtonUpdate; |
| 2933 | if(DoButton_Menu(pButtonContainer: &s_ButtonUpdate, pText: Localize(pStr: "Check now" ), Checked: 0, pRect: &Button)) |
| 2934 | { |
| 2935 | Client()->RequestDDNetInfo(); |
| 2936 | } |
| 2937 | } |
| 2938 | Ui()->DoLabel(pRect: &UpdaterRect, pText: aBuf, Size: 14.0f, Align: TEXTALIGN_ML); |
| 2939 | TextRender()->TextColor(r: 1.0f, g: 1.0f, b: 1.0f, a: 1.0f); |
| 2940 | } |
| 2941 | #endif |
| 2942 | } |
| 2943 | |
| 2944 | CUi::EPopupMenuFunctionResult CMenus::(void *pContext, CUIRect View, bool Active) |
| 2945 | { |
| 2946 | CPopupMapPickerContext * = static_cast<CPopupMapPickerContext *>(pContext); |
| 2947 | CMenus * = pPopupContext->m_pMenus; |
| 2948 | |
| 2949 | static CListBox s_ListBox; |
| 2950 | s_ListBox.SetActive(Active); |
| 2951 | s_ListBox.DoStart(RowHeight: 20.0f, NumItems: pPopupContext->m_vMaps.size(), ItemsPerRow: 1, RowsPerScroll: 3, SelectedIndex: -1, pRect: &View, Background: false); |
| 2952 | |
| 2953 | int MapIndex = 0; |
| 2954 | for(auto &Map : pPopupContext->m_vMaps) |
| 2955 | { |
| 2956 | MapIndex++; |
| 2957 | const CListboxItem Item = s_ListBox.DoNextItem(pId: &Map, Selected: MapIndex == pPopupContext->m_Selection); |
| 2958 | if(!Item.m_Visible) |
| 2959 | continue; |
| 2960 | |
| 2961 | CUIRect Label, Icon; |
| 2962 | Item.m_Rect.VSplitLeft(Cut: 20.0f, pLeft: &Icon, pRight: &Label); |
| 2963 | |
| 2964 | char aLabelText[IO_MAX_PATH_LENGTH]; |
| 2965 | str_copy(dst&: aLabelText, src: Map.m_aFilename); |
| 2966 | if(Map.m_IsDirectory) |
| 2967 | str_append(dst: aLabelText, src: "/" , dst_size: sizeof(aLabelText)); |
| 2968 | |
| 2969 | const char *pIconType; |
| 2970 | if(!Map.m_IsDirectory) |
| 2971 | { |
| 2972 | pIconType = FONT_ICON_MAP; |
| 2973 | } |
| 2974 | else |
| 2975 | { |
| 2976 | if(!str_comp(a: Map.m_aFilename, b: ".." )) |
| 2977 | pIconType = FONT_ICON_FOLDER_TREE; |
| 2978 | else |
| 2979 | pIconType = FONT_ICON_FOLDER; |
| 2980 | } |
| 2981 | |
| 2982 | pMenus->TextRender()->SetFontPreset(EFontPreset::ICON_FONT); |
| 2983 | pMenus->TextRender()->SetRenderFlags(ETextRenderFlags::TEXT_RENDER_FLAG_ONLY_ADVANCE_WIDTH | ETextRenderFlags::TEXT_RENDER_FLAG_NO_X_BEARING | ETextRenderFlags::TEXT_RENDER_FLAG_NO_Y_BEARING); |
| 2984 | pMenus->Ui()->DoLabel(pRect: &Icon, pText: pIconType, Size: 12.0f, Align: TEXTALIGN_ML); |
| 2985 | pMenus->TextRender()->SetRenderFlags(0); |
| 2986 | pMenus->TextRender()->SetFontPreset(EFontPreset::DEFAULT_FONT); |
| 2987 | |
| 2988 | pMenus->Ui()->DoLabel(pRect: &Label, pText: aLabelText, Size: 10.0f, Align: TEXTALIGN_ML); |
| 2989 | } |
| 2990 | |
| 2991 | const int NewSelected = s_ListBox.DoEnd(); |
| 2992 | pPopupContext->m_Selection = NewSelected >= 0 ? NewSelected : -1; |
| 2993 | if(s_ListBox.WasItemSelected() || s_ListBox.WasItemActivated()) |
| 2994 | { |
| 2995 | const CMapListItem &SelectedItem = pPopupContext->m_vMaps[pPopupContext->m_Selection]; |
| 2996 | |
| 2997 | if(SelectedItem.m_IsDirectory) |
| 2998 | { |
| 2999 | if(!str_comp(a: SelectedItem.m_aFilename, b: ".." )) |
| 3000 | { |
| 3001 | fs_parent_dir(path: pPopupContext->m_aCurrentMapFolder); |
| 3002 | } |
| 3003 | else |
| 3004 | { |
| 3005 | str_append(dst: pPopupContext->m_aCurrentMapFolder, src: "/" , dst_size: sizeof(pPopupContext->m_aCurrentMapFolder)); |
| 3006 | str_append(dst: pPopupContext->m_aCurrentMapFolder, src: SelectedItem.m_aFilename, dst_size: sizeof(pPopupContext->m_aCurrentMapFolder)); |
| 3007 | } |
| 3008 | pPopupContext->MapListPopulate(); |
| 3009 | } |
| 3010 | else |
| 3011 | { |
| 3012 | str_format(buffer: g_Config.m_ClBackgroundEntities, buffer_size: sizeof(g_Config.m_ClBackgroundEntities), format: "%s/%s" , pPopupContext->m_aCurrentMapFolder, SelectedItem.m_aFilename); |
| 3013 | pMenus->GameClient()->m_Background.LoadBackground(); |
| 3014 | return CUi::POPUP_CLOSE_CURRENT; |
| 3015 | } |
| 3016 | } |
| 3017 | |
| 3018 | return CUi::POPUP_KEEP_OPEN; |
| 3019 | } |
| 3020 | |
| 3021 | void CMenus::CPopupMapPickerContext::() |
| 3022 | { |
| 3023 | m_vMaps.clear(); |
| 3024 | char aTemp[IO_MAX_PATH_LENGTH]; |
| 3025 | str_format(buffer: aTemp, buffer_size: sizeof(aTemp), format: "maps/%s" , m_aCurrentMapFolder); |
| 3026 | m_pMenus->Storage()->ListDirectoryInfo(Type: IStorage::TYPE_ALL, pPath: aTemp, pfnCallback: MapListFetchCallback, pUser: this); |
| 3027 | std::stable_sort(first: m_vMaps.begin(), last: m_vMaps.end(), comp: CompareFilenameAscending); |
| 3028 | m_Selection = -1; |
| 3029 | } |
| 3030 | |
| 3031 | int CMenus::CPopupMapPickerContext::(const CFsFileInfo *pInfo, int IsDir, int StorageType, void *pUser) |
| 3032 | { |
| 3033 | CPopupMapPickerContext *pRealUser = (CPopupMapPickerContext *)pUser; |
| 3034 | if((!IsDir && !str_endswith(str: pInfo->m_pName, suffix: ".map" )) || !str_comp(a: pInfo->m_pName, b: "." ) || (!str_comp(a: pInfo->m_pName, b: ".." ) && (!str_comp(a: pRealUser->m_aCurrentMapFolder, b: "" )))) |
| 3035 | return 0; |
| 3036 | |
| 3037 | CMapListItem Item; |
| 3038 | str_copy(dst&: Item.m_aFilename, src: pInfo->m_pName); |
| 3039 | Item.m_IsDirectory = IsDir; |
| 3040 | |
| 3041 | pRealUser->m_vMaps.emplace_back(args&: Item); |
| 3042 | |
| 3043 | return 0; |
| 3044 | } |
| 3045 | |