| 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 "menus.h" |
| 4 | |
| 5 | #include <base/str.h> |
| 6 | |
| 7 | #include <engine/shared/config.h> |
| 8 | #include <engine/storage.h> |
| 9 | |
| 10 | #include <game/client/components/menu_background.h> |
| 11 | #include <game/client/components/tooltips.h> |
| 12 | #include <game/client/gameclient.h> |
| 13 | #include <game/client/ui.h> |
| 14 | #include <game/client/ui_listbox.h> |
| 15 | #include <game/localization.h> |
| 16 | |
| 17 | void CMenus::(CUIRect MainView) |
| 18 | { |
| 19 | char aBuf[128 + IO_MAX_PATH_LENGTH]; |
| 20 | CUIRect Label, Button, Left, Right, Game, ClientSettings; |
| 21 | MainView.HSplitTop(Cut: 150.0f, pTop: &Game, pBottom: &ClientSettings); |
| 22 | |
| 23 | // game |
| 24 | { |
| 25 | // headline |
| 26 | Game.HSplitTop(Cut: 30.0f, pTop: &Label, pBottom: &Game); |
| 27 | Ui()->DoLabel(pRect: &Label, pText: Localize(pStr: "Game" ), Size: 20.0f, Align: TEXTALIGN_ML); |
| 28 | Game.HSplitTop(Cut: 5.0f, pTop: nullptr, pBottom: &Game); |
| 29 | Game.VSplitMid(pLeft: &Left, pRight: nullptr, Spacing: 20.0f); |
| 30 | |
| 31 | // dynamic camera |
| 32 | Left.HSplitTop(Cut: 20.0f, pTop: &Button, pBottom: &Left); |
| 33 | const bool IsDyncam = g_Config.m_ClDyncam || g_Config.m_ClMouseFollowfactor > 0; |
| 34 | if(DoButton_CheckBox(pId: &g_Config.m_ClDyncam, pText: Localize(pStr: "Dynamic Camera" ), Checked: IsDyncam, pRect: &Button)) |
| 35 | { |
| 36 | if(IsDyncam) |
| 37 | { |
| 38 | g_Config.m_ClDyncam = 0; |
| 39 | g_Config.m_ClMouseFollowfactor = 0; |
| 40 | } |
| 41 | else |
| 42 | { |
| 43 | g_Config.m_ClDyncam = 1; |
| 44 | } |
| 45 | } |
| 46 | |
| 47 | // smooth dynamic camera |
| 48 | Left.HSplitTop(Cut: 5.0f, pTop: nullptr, pBottom: &Left); |
| 49 | Left.HSplitTop(Cut: 20.0f, pTop: &Button, pBottom: &Left); |
| 50 | if(g_Config.m_ClDyncam) |
| 51 | { |
| 52 | if(DoButton_CheckBox(pId: &g_Config.m_ClDyncamSmoothness, pText: Localize(pStr: "Smooth Dynamic Camera" ), Checked: g_Config.m_ClDyncamSmoothness, pRect: &Button)) |
| 53 | { |
| 54 | if(g_Config.m_ClDyncamSmoothness) |
| 55 | { |
| 56 | g_Config.m_ClDyncamSmoothness = 0; |
| 57 | } |
| 58 | else |
| 59 | { |
| 60 | g_Config.m_ClDyncamSmoothness = 50; |
| 61 | g_Config.m_ClDyncamStabilizing = 50; |
| 62 | } |
| 63 | } |
| 64 | } |
| 65 | |
| 66 | // weapon pickup |
| 67 | Left.HSplitTop(Cut: 5.0f, pTop: nullptr, pBottom: &Left); |
| 68 | Left.HSplitTop(Cut: 20.0f, pTop: &Button, pBottom: &Left); |
| 69 | if(DoButton_CheckBox(pId: &g_Config.m_ClAutoswitchWeapons, pText: Localize(pStr: "Switch weapon on pickup" ), Checked: g_Config.m_ClAutoswitchWeapons, pRect: &Button)) |
| 70 | g_Config.m_ClAutoswitchWeapons ^= 1; |
| 71 | |
| 72 | // weapon out of ammo autoswitch |
| 73 | Left.HSplitTop(Cut: 5.0f, pTop: nullptr, pBottom: &Left); |
| 74 | Left.HSplitTop(Cut: 20.0f, pTop: &Button, pBottom: &Left); |
| 75 | if(DoButton_CheckBox(pId: &g_Config.m_ClAutoswitchWeaponsOutOfAmmo, pText: Localize(pStr: "Switch weapon when out of ammo" ), Checked: g_Config.m_ClAutoswitchWeaponsOutOfAmmo, pRect: &Button)) |
| 76 | g_Config.m_ClAutoswitchWeaponsOutOfAmmo ^= 1; |
| 77 | } |
| 78 | |
| 79 | // client |
| 80 | { |
| 81 | // headline |
| 82 | ClientSettings.HSplitTop(Cut: 30.0f, pTop: &Label, pBottom: &ClientSettings); |
| 83 | Ui()->DoLabel(pRect: &Label, pText: Localize(pStr: "Client" ), Size: 20.0f, Align: TEXTALIGN_ML); |
| 84 | ClientSettings.HSplitTop(Cut: 5.0f, pTop: nullptr, pBottom: &ClientSettings); |
| 85 | ClientSettings.VSplitMid(pLeft: &Left, pRight: &Right, Spacing: 20.0f); |
| 86 | |
| 87 | // skip main menu |
| 88 | Left.HSplitTop(Cut: 20.0f, pTop: &Button, pBottom: &Left); |
| 89 | if(DoButton_CheckBox(pId: &g_Config.m_ClSkipStartMenu, pText: Localize(pStr: "Skip the main menu" ), Checked: g_Config.m_ClSkipStartMenu, pRect: &Button)) |
| 90 | g_Config.m_ClSkipStartMenu ^= 1; |
| 91 | |
| 92 | Left.HSplitTop(Cut: 10.0f, pTop: nullptr, pBottom: &Left); |
| 93 | Left.HSplitTop(Cut: 20.0f, pTop: &Button, pBottom: &Left); |
| 94 | str_copy(dst&: aBuf, src: " " ); |
| 95 | str_append(dst&: aBuf, src: Localize(pStr: "Hz" , pContext: "Hertz" )); |
| 96 | Ui()->DoScrollbarOption(pId: &g_Config.m_ClRefreshRate, pOption: &g_Config.m_ClRefreshRate, pRect: &Button, pStr: Localize(pStr: "Update Rate" ), Min: 10, Max: 1000, pScale: &CUi::ms_LinearScrollbarScale, Flags: CUi::SCROLLBAR_OPTION_INFINITE | CUi::SCROLLBAR_OPTION_NOCLAMPVALUE | CUi::SCROLLBAR_OPTION_DELAYUPDATE, pSuffix: aBuf); |
| 97 | Left.HSplitTop(Cut: 5.0f, pTop: nullptr, pBottom: &Left); |
| 98 | Left.HSplitTop(Cut: 20.0f, pTop: &Button, pBottom: &Left); |
| 99 | static int s_LowerRefreshRate; |
| 100 | if(DoButton_CheckBox(pId: &s_LowerRefreshRate, pText: Localize(pStr: "Save power by lowering update rate (higher input latency)" ), Checked: g_Config.m_ClRefreshRate <= 480 && g_Config.m_ClRefreshRate != 0, pRect: &Button)) |
| 101 | g_Config.m_ClRefreshRate = g_Config.m_ClRefreshRate > 480 || g_Config.m_ClRefreshRate == 0 ? 480 : 0; |
| 102 | |
| 103 | CUIRect SettingsButton; |
| 104 | Left.HSplitBottom(Cut: 20.0f, pTop: &Left, pBottom: &SettingsButton); |
| 105 | Left.HSplitBottom(Cut: 5.0f, pTop: &Left, pBottom: nullptr); |
| 106 | static CButtonContainer s_SettingsButtonId; |
| 107 | if(DoButton_Menu(pButtonContainer: &s_SettingsButtonId, pText: Localize(pStr: "Settings file" ), Checked: 0, pRect: &SettingsButton)) |
| 108 | { |
| 109 | Storage()->GetCompletePath(Type: IStorage::TYPE_SAVE, CONFIG_FILE, pBuffer: aBuf, BufferSize: sizeof(aBuf)); |
| 110 | Client()->ViewFile(pFilename: aBuf); |
| 111 | } |
| 112 | GameClient()->m_Tooltips.DoToolTip(pId: &s_SettingsButtonId, pNearRect: &SettingsButton, pText: Localize(pStr: "Open the settings file" )); |
| 113 | |
| 114 | CUIRect SavesButton; |
| 115 | Left.HSplitBottom(Cut: 20.0f, pTop: &Left, pBottom: &SavesButton); |
| 116 | Left.HSplitBottom(Cut: 5.0f, pTop: &Left, pBottom: nullptr); |
| 117 | static CButtonContainer s_SavesButtonId; |
| 118 | if(DoButton_Menu(pButtonContainer: &s_SavesButtonId, pText: Localize(pStr: "Saves file" ), Checked: 0, pRect: &SavesButton)) |
| 119 | { |
| 120 | Storage()->GetCompletePath(Type: IStorage::TYPE_SAVE, pDir: SAVES_FILE, pBuffer: aBuf, BufferSize: sizeof(aBuf)); |
| 121 | Client()->ViewFile(pFilename: aBuf); |
| 122 | } |
| 123 | GameClient()->m_Tooltips.DoToolTip(pId: &s_SavesButtonId, pNearRect: &SavesButton, pText: Localize(pStr: "Open the saves file" )); |
| 124 | |
| 125 | CUIRect ConfigButton; |
| 126 | Left.HSplitBottom(Cut: 20.0f, pTop: &Left, pBottom: &ConfigButton); |
| 127 | Left.HSplitBottom(Cut: 5.0f, pTop: &Left, pBottom: nullptr); |
| 128 | static CButtonContainer s_ConfigButtonId; |
| 129 | if(DoButton_Menu(pButtonContainer: &s_ConfigButtonId, pText: Localize(pStr: "Config directory" ), Checked: 0, pRect: &ConfigButton)) |
| 130 | { |
| 131 | Storage()->GetCompletePath(Type: IStorage::TYPE_SAVE, pDir: "" , pBuffer: aBuf, BufferSize: sizeof(aBuf)); |
| 132 | Client()->ViewFile(pFilename: aBuf); |
| 133 | } |
| 134 | GameClient()->m_Tooltips.DoToolTip(pId: &s_ConfigButtonId, pNearRect: &ConfigButton, pText: Localize(pStr: "Open the directory that contains the configuration and user files" )); |
| 135 | |
| 136 | CUIRect DirectoryButton; |
| 137 | Left.HSplitBottom(Cut: 20.0f, pTop: &Left, pBottom: &DirectoryButton); |
| 138 | Left.HSplitBottom(Cut: 5.0f, pTop: &Left, pBottom: nullptr); |
| 139 | static CButtonContainer s_ThemesButtonId; |
| 140 | if(DoButton_Menu(pButtonContainer: &s_ThemesButtonId, pText: Localize(pStr: "Themes directory" ), Checked: 0, pRect: &DirectoryButton)) |
| 141 | { |
| 142 | Storage()->GetCompletePath(Type: IStorage::TYPE_SAVE, pDir: "themes" , pBuffer: aBuf, BufferSize: sizeof(aBuf)); |
| 143 | Storage()->CreateFolder(pFoldername: "themes" , Type: IStorage::TYPE_SAVE); |
| 144 | Client()->ViewFile(pFilename: aBuf); |
| 145 | } |
| 146 | GameClient()->m_Tooltips.DoToolTip(pId: &s_ThemesButtonId, pNearRect: &DirectoryButton, pText: Localize(pStr: "Open the directory to add custom themes" )); |
| 147 | |
| 148 | Left.HSplitTop(Cut: 20.0f, pTop: nullptr, pBottom: &Left); |
| 149 | RenderThemeSelection(MainView: Left); |
| 150 | |
| 151 | // auto demo settings |
| 152 | { |
| 153 | Right.HSplitTop(Cut: 40.0f, pTop: nullptr, pBottom: &Right); |
| 154 | Right.HSplitTop(Cut: 20.0f, pTop: &Button, pBottom: &Right); |
| 155 | if(DoButton_CheckBox(pId: &g_Config.m_ClAutoDemoRecord, pText: Localize(pStr: "Automatically record demos" ), Checked: g_Config.m_ClAutoDemoRecord, pRect: &Button)) |
| 156 | g_Config.m_ClAutoDemoRecord ^= 1; |
| 157 | |
| 158 | Right.HSplitTop(Cut: 2 * 20.0f, pTop: &Button, pBottom: &Right); |
| 159 | if(g_Config.m_ClAutoDemoRecord) |
| 160 | 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); |
| 161 | |
| 162 | Right.HSplitTop(Cut: 10.0f, pTop: nullptr, pBottom: &Right); |
| 163 | Right.HSplitTop(Cut: 20.0f, pTop: &Button, pBottom: &Right); |
| 164 | if(DoButton_CheckBox(pId: &g_Config.m_ClAutoScreenshot, pText: Localize(pStr: "Automatically take game over screenshot" ), Checked: g_Config.m_ClAutoScreenshot, pRect: &Button)) |
| 165 | g_Config.m_ClAutoScreenshot ^= 1; |
| 166 | |
| 167 | Right.HSplitTop(Cut: 2 * 20.0f, pTop: &Button, pBottom: &Right); |
| 168 | if(g_Config.m_ClAutoScreenshot) |
| 169 | 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); |
| 170 | } |
| 171 | |
| 172 | // auto statboard screenshot |
| 173 | { |
| 174 | Right.HSplitTop(Cut: 10.0f, pTop: nullptr, pBottom: &Right); |
| 175 | Right.HSplitTop(Cut: 20.0f, pTop: &Button, pBottom: &Right); |
| 176 | if(DoButton_CheckBox(pId: &g_Config.m_ClAutoStatboardScreenshot, pText: Localize(pStr: "Automatically take statboard screenshot" ), Checked: g_Config.m_ClAutoStatboardScreenshot, pRect: &Button)) |
| 177 | { |
| 178 | g_Config.m_ClAutoStatboardScreenshot ^= 1; |
| 179 | } |
| 180 | |
| 181 | Right.HSplitTop(Cut: 2 * 20.0f, pTop: &Button, pBottom: &Right); |
| 182 | if(g_Config.m_ClAutoStatboardScreenshot) |
| 183 | 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); |
| 184 | } |
| 185 | |
| 186 | // auto statboard csv |
| 187 | { |
| 188 | Right.HSplitTop(Cut: 10.0f, pTop: nullptr, pBottom: &Right); |
| 189 | Right.HSplitTop(Cut: 20.0f, pTop: &Button, pBottom: &Right); |
| 190 | if(DoButton_CheckBox(pId: &g_Config.m_ClAutoCSV, pText: Localize(pStr: "Automatically create statboard csv" ), Checked: g_Config.m_ClAutoCSV, pRect: &Button)) |
| 191 | { |
| 192 | g_Config.m_ClAutoCSV ^= 1; |
| 193 | } |
| 194 | |
| 195 | Right.HSplitTop(Cut: 2 * 20.0f, pTop: &Button, pBottom: &Right); |
| 196 | if(g_Config.m_ClAutoCSV) |
| 197 | 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); |
| 198 | } |
| 199 | } |
| 200 | } |
| 201 | |
| 202 | void CMenus::(CUIRect MainView) |
| 203 | { |
| 204 | const std::vector<CTheme> &vThemes = GameClient()->m_MenuBackground.GetThemes(); |
| 205 | |
| 206 | int SelectedTheme = -1; |
| 207 | for(int i = 0; i < (int)vThemes.size(); i++) |
| 208 | { |
| 209 | if(str_comp(a: vThemes[i].m_Name.c_str(), b: g_Config.m_ClMenuMap) == 0) |
| 210 | { |
| 211 | SelectedTheme = i; |
| 212 | break; |
| 213 | } |
| 214 | } |
| 215 | const int OldSelected = SelectedTheme; |
| 216 | |
| 217 | static CListBox s_ListBox; |
| 218 | s_ListBox.DoHeader(pRect: &MainView, pTitle: Localize(pStr: "Theme" ), HeaderHeight: 20.0f); |
| 219 | s_ListBox.DoStart(RowHeight: 20.0f, NumItems: vThemes.size(), ItemsPerRow: 1, RowsPerScroll: 3, SelectedIndex: SelectedTheme); |
| 220 | |
| 221 | for(int i = 0; i < (int)vThemes.size(); i++) |
| 222 | { |
| 223 | const CTheme &Theme = vThemes[i]; |
| 224 | const CListboxItem Item = s_ListBox.DoNextItem(pId: &Theme.m_Name, Selected: i == SelectedTheme); |
| 225 | |
| 226 | if(!Item.m_Visible) |
| 227 | continue; |
| 228 | |
| 229 | CUIRect Icon, Label; |
| 230 | Item.m_Rect.VSplitLeft(Cut: Item.m_Rect.h * 2.0f, pLeft: &Icon, pRight: &Label); |
| 231 | |
| 232 | // draw icon if it exists |
| 233 | if(Theme.m_IconTexture.IsValid()) |
| 234 | { |
| 235 | Icon.VMargin(Cut: 6.0f, pOtherRect: &Icon); |
| 236 | Icon.HMargin(Cut: 3.0f, pOtherRect: &Icon); |
| 237 | Graphics()->TextureSet(Texture: Theme.m_IconTexture); |
| 238 | Graphics()->QuadsBegin(); |
| 239 | Graphics()->SetColor(r: 1.0f, g: 1.0f, b: 1.0f, a: 1.0f); |
| 240 | IGraphics::CQuadItem QuadItem(Icon.x, Icon.y, Icon.w, Icon.h); |
| 241 | Graphics()->QuadsDrawTL(pArray: &QuadItem, Num: 1); |
| 242 | Graphics()->QuadsEnd(); |
| 243 | } |
| 244 | |
| 245 | char aName[128]; |
| 246 | if(Theme.m_Name.empty()) |
| 247 | str_copy(dst&: aName, src: "(none)" ); |
| 248 | else if(str_comp(a: Theme.m_Name.c_str(), b: "auto" ) == 0) |
| 249 | str_copy(dst&: aName, src: "(seasons)" ); |
| 250 | else if(str_comp(a: Theme.m_Name.c_str(), b: "rand" ) == 0) |
| 251 | str_copy(dst&: aName, src: "(random)" ); |
| 252 | else if(Theme.m_HasDay && Theme.m_HasNight) |
| 253 | str_copy(dst&: aName, src: Theme.m_Name.c_str()); |
| 254 | else if(Theme.m_HasDay && !Theme.m_HasNight) |
| 255 | str_format(buffer: aName, buffer_size: sizeof(aName), format: "%s (day)" , Theme.m_Name.c_str()); |
| 256 | else if(!Theme.m_HasDay && Theme.m_HasNight) |
| 257 | str_format(buffer: aName, buffer_size: sizeof(aName), format: "%s (night)" , Theme.m_Name.c_str()); |
| 258 | else // generic |
| 259 | str_copy(dst&: aName, src: Theme.m_Name.c_str()); |
| 260 | |
| 261 | Ui()->DoLabel(pRect: &Label, pText: aName, Size: 16.0f * CUi::ms_FontmodHeight, Align: TEXTALIGN_ML); |
| 262 | } |
| 263 | |
| 264 | SelectedTheme = s_ListBox.DoEnd(); |
| 265 | |
| 266 | if(OldSelected != SelectedTheme) |
| 267 | { |
| 268 | const CTheme &Theme = vThemes[SelectedTheme]; |
| 269 | str_copy(dst&: g_Config.m_ClMenuMap, src: Theme.m_Name.c_str()); |
| 270 | GameClient()->m_MenuBackground.LoadMenuBackground(HasDayHint: Theme.m_HasDay, HasNightHint: Theme.m_HasNight); |
| 271 | } |
| 272 | } |
| 273 | |