~popey/+junk/openspades-stable

« back to all changes in this revision

Viewing changes to Resources/Scripts/Gui/MainScreen.as

  • Committer: Alan Pope
  • Date: 2017-04-30 20:42:53 UTC
  • Revision ID: alan@popey.com-20170430204253-1vpvxb47ygller2c
openspades stable 0.1.1c

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
 Copyright (c) 2013 yvt
 
3
 
 
4
 This file is part of OpenSpades.
 
5
 
 
6
 OpenSpades is free software: you can redistribute it and/or modify
 
7
 it under the terms of the GNU General Public License as published by
 
8
 the Free Software Foundation, either version 3 of the License, or
 
9
 (at your option) any later version.
 
10
 
 
11
 OpenSpades is distributed in the hope that it will be useful,
 
12
 but WITHOUT ANY WARRANTY; without even the implied warranty of
 
13
 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
14
 GNU General Public License for more details.
 
15
 
 
16
 You should have received a copy of the GNU General Public License
 
17
 along with OpenSpades.  If not, see <http://www.gnu.org/licenses/>.
 
18
 
 
19
 */
 
20
 
 
21
#include "Flags.as"
 
22
#include "CreateProfileScreen.as"
 
23
 
 
24
namespace spades {
 
25
 
 
26
 
 
27
        class MainScreenUI {
 
28
                private Renderer@ renderer;
 
29
                private AudioDevice@ audioDevice;
 
30
                FontManager@ fontManager;
 
31
                MainScreenHelper@ helper;
 
32
 
 
33
                spades::ui::UIManager@ manager;
 
34
 
 
35
                MainScreenMainMenu@ mainMenu;
 
36
 
 
37
                bool shouldExit = false;
 
38
 
 
39
                private float time = -1.f;
 
40
 
 
41
        private ConfigItem cg_playerName("cg_playerName");
 
42
        private ConfigItem cg_playerNameIsSet("cg_playerNameIsSet", "0");
 
43
 
 
44
                MainScreenUI(Renderer@ renderer, AudioDevice@ audioDevice, FontManager@ fontManager, MainScreenHelper@ helper) {
 
45
                        @this.renderer = renderer;
 
46
                        @this.audioDevice = audioDevice;
 
47
                        @this.fontManager = fontManager;
 
48
                        @this.helper = helper;
 
49
 
 
50
                        SetupRenderer();
 
51
 
 
52
                        @manager = spades::ui::UIManager(renderer, audioDevice);
 
53
                        @manager.RootElement.Font = fontManager.GuiFont;
 
54
 
 
55
                        @mainMenu = MainScreenMainMenu(this);
 
56
                        mainMenu.Bounds = manager.RootElement.Bounds;
 
57
                        manager.RootElement.AddChild(mainMenu);
 
58
 
 
59
                        // Let the new player choose their IGN
 
60
                        if (cg_playerName.StringValue != "" &&
 
61
                                cg_playerName.StringValue != "Deuce") {
 
62
                                cg_playerNameIsSet.IntValue = 1;
 
63
                        }
 
64
                        if (cg_playerNameIsSet.IntValue == 0) {
 
65
                                CreateProfileScreen al(mainMenu);
 
66
                                al.Run();
 
67
                        }
 
68
 
 
69
                }
 
70
 
 
71
                void SetupRenderer() {
 
72
                        // load map
 
73
                        @renderer.GameMap = GameMap("Maps/Title.vxl");
 
74
                        renderer.FogColor = Vector3(0.1f, 0.10f, 0.1f);
 
75
                        renderer.FogDistance = 128.f;
 
76
                        time = -1.f;
 
77
 
 
78
                        // returned from the client game, so reload the server list.
 
79
                        if(mainMenu !is null)
 
80
                                mainMenu.LoadServerList();
 
81
 
 
82
                        if(manager !is null)
 
83
                                manager.KeyPanic();
 
84
                }
 
85
 
 
86
                void MouseEvent(float x, float y) {
 
87
                        manager.MouseEvent(x, y);
 
88
                }
 
89
 
 
90
                void WheelEvent(float x, float y) {
 
91
                        manager.WheelEvent(x, y);
 
92
                }
 
93
 
 
94
                void KeyEvent(string key, bool down) {
 
95
                        manager.KeyEvent(key, down);
 
96
                }
 
97
 
 
98
                void TextInputEvent(string text) {
 
99
                        manager.TextInputEvent(text);
 
100
                }
 
101
 
 
102
                void TextEditingEvent(string text, int start, int len) {
 
103
                        manager.TextEditingEvent(text, start, len);
 
104
                }
 
105
 
 
106
                bool AcceptsTextInput() {
 
107
                        return manager.AcceptsTextInput;
 
108
                }
 
109
 
 
110
                AABB2 GetTextInputRect() {
 
111
                        return manager.TextInputRect;
 
112
                }
 
113
 
 
114
                private SceneDefinition SetupCamera(SceneDefinition sceneDef,
 
115
                        Vector3 eye, Vector3 at, Vector3 up, float fov) {
 
116
                        Vector3 dir = (at - eye).Normalized;
 
117
                        Vector3 side = Cross(dir, up).Normalized;
 
118
                        up = -Cross(dir, side);
 
119
                        sceneDef.viewOrigin = eye;
 
120
                        sceneDef.viewAxisX = side;
 
121
                        sceneDef.viewAxisY = up;
 
122
                        sceneDef.viewAxisZ = dir;
 
123
                        sceneDef.fovY = fov * 3.141592654f / 180.f;
 
124
                        sceneDef.fovX = atan(tan(sceneDef.fovY * 0.5f) * renderer.ScreenWidth / renderer.ScreenHeight) * 2.f;
 
125
                        return sceneDef;
 
126
                }
 
127
 
 
128
                void RunFrame(float dt) {
 
129
                        if(time < 0.f) {
 
130
                                time = 0.f;
 
131
                        }
 
132
 
 
133
                        SceneDefinition sceneDef;
 
134
                        float cameraX = time;
 
135
                        cameraX -= floor(cameraX / 512.f) * 512.f;
 
136
                        cameraX = 512.f - cameraX;
 
137
                        sceneDef = SetupCamera(sceneDef,
 
138
                                Vector3(cameraX, 256.f, 12.f), Vector3(cameraX + .1f, 257.f, 12.5f), Vector3(0.f, 0.f, -1.f),
 
139
                                30.f);
 
140
                        sceneDef.zNear = 0.1f;
 
141
                        sceneDef.zFar = 222.f;
 
142
                        sceneDef.time = int(time * 1000.f);
 
143
                        sceneDef.viewportWidth = int(renderer.ScreenWidth);
 
144
                        sceneDef.viewportHeight = int(renderer.ScreenHeight);
 
145
                        sceneDef.denyCameraBlur = true;
 
146
                        sceneDef.depthOfFieldFocalLength = 100.f;
 
147
                        sceneDef.skipWorld = false;
 
148
 
 
149
                        // fade the map
 
150
                        float fade = Clamp((time - 1.f) / 2.2f, 0.f, 1.f);
 
151
                        sceneDef.globalBlur = Clamp((1.f - (time - 1.f) / 2.5f), 0.f, 1.f);
 
152
                        if(!mainMenu.IsEnabled) {
 
153
                                sceneDef.globalBlur = Max(sceneDef.globalBlur, 0.5f);
 
154
                        }
 
155
 
 
156
                        renderer.StartScene(sceneDef);
 
157
                        renderer.EndScene();
 
158
 
 
159
                        // fade the map (draw)
 
160
                        if(fade < 1.f) {
 
161
                                renderer.ColorNP = Vector4(0.f, 0.f, 0.f, 1.f - fade);
 
162
                                renderer.DrawImage(renderer.RegisterImage("Gfx/White.tga"),
 
163
                                        AABB2(0.f, 0.f, renderer.ScreenWidth, renderer.ScreenHeight));
 
164
                        }
 
165
 
 
166
                        // draw title logo
 
167
                        Image@ img = renderer.RegisterImage("Gfx/Title/Logo.png");
 
168
                        renderer.ColorNP = Vector4(1.f, 1.f, 1.f, 1.f);
 
169
                        renderer.DrawImage(img, Vector2((renderer.ScreenWidth - img.Width) * 0.5f, 64.f));
 
170
 
 
171
                        manager.RunFrame(dt);
 
172
                        manager.Render();
 
173
 
 
174
                        renderer.FrameDone();
 
175
                        renderer.Flip();
 
176
                        time += Min(dt, 0.05f);
 
177
                }
 
178
 
 
179
                void Closing() {
 
180
                        shouldExit = true;
 
181
                }
 
182
 
 
183
                bool WantsToBeClosed() {
 
184
                        return shouldExit;
 
185
                }
 
186
        }
 
187
 
 
188
        class ServerListItem: spades::ui::ButtonBase {
 
189
                MainScreenServerItem@ item;
 
190
                FlagIconRenderer@ flagIconRenderer;
 
191
                ServerListItem(spades::ui::UIManager@ manager, MainScreenServerItem@ item){
 
192
                        super(manager);
 
193
                        @this.item = item;
 
194
                        @flagIconRenderer = FlagIconRenderer(manager.Renderer);
 
195
                }
 
196
                void Render() {
 
197
                        Renderer@ renderer = Manager.Renderer;
 
198
                        Vector2 pos = ScreenPosition;
 
199
                        Vector2 size = Size;
 
200
                        Image@ img = renderer.RegisterImage("Gfx/White.tga");
 
201
 
 
202
                        Vector4 bgcolor = Vector4(1.f, 1.f, 1.f, 0.0f);
 
203
                        Vector4 fgcolor = Vector4(1.f, 1.f, 1.f, 1.f);
 
204
                        if(item.Favorite) {
 
205
                                bgcolor = Vector4(0.3f, 0.3f, 1.f, 0.1f);
 
206
                                fgcolor = Vector4(220.f/255.f,220.f/255.f,0,1);
 
207
                        }
 
208
                        if(Pressed && Hover) {
 
209
                                bgcolor.w += 0.3;
 
210
                        } else if(Hover) {
 
211
                                bgcolor.w += 0.15;
 
212
                        }
 
213
                        renderer.ColorNP = bgcolor;
 
214
                        renderer.DrawImage(img, AABB2(pos.x, pos.y, size.x, size.y));
 
215
 
 
216
                        Font.Draw(item.Name, ScreenPosition + Vector2(4.f, 2.f), 1.f, fgcolor);
 
217
                        string playersStr = ToString(item.NumPlayers) + "/" + ToString(item.MaxPlayers);
 
218
                        Vector4 col(1,1,1,1);
 
219
                        if(item.NumPlayers >= item.MaxPlayers) col = Vector4(1,0.7f,0.7f,1);
 
220
                        else if(item.NumPlayers >= item.MaxPlayers * 3 / 4) col = Vector4(1,1,0.7f,1);
 
221
                        else if(item.NumPlayers == 0) col = Vector4(0.7f,0.7f,1,1);
 
222
                        Font.Draw(playersStr, ScreenPosition + Vector2(340.f-Font.Measure(playersStr).x * 0.5f, 2.f), 1.f, col);
 
223
                        Font.Draw(item.MapName, ScreenPosition + Vector2(400.f, 2.f), 1.f, Vector4(1,1,1,1));
 
224
                        Font.Draw(item.GameMode, ScreenPosition + Vector2(550.f, 2.f), 1.f, Vector4(1,1,1,1));
 
225
                        Font.Draw(item.Protocol, ScreenPosition + Vector2(630.f, 2.f), 1.f, Vector4(1,1,1,1));
 
226
                        if(not flagIconRenderer.DrawIcon(item.Country, ScreenPosition + Vector2(700.f, size.y * 0.5f))) {
 
227
                                Font.Draw(item.Country, ScreenPosition + Vector2(680.f, 2.f), 1.f, Vector4(1,1,1,1));
 
228
                        }
 
229
                }
 
230
        }
 
231
 
 
232
        funcdef void ServerListItemEventHandler(ServerListModel@ sender, MainScreenServerItem@ item);
 
233
 
 
234
        class ServerListModel: spades::ui::ListViewModel {
 
235
                spades::ui::UIManager@ manager;
 
236
                MainScreenServerItem@[]@ list;
 
237
 
 
238
                ServerListItemEventHandler@ ItemActivated;
 
239
                ServerListItemEventHandler@ ItemDoubleClicked;
 
240
                ServerListItemEventHandler@ ItemRightClicked;
 
241
 
 
242
                ServerListModel(spades::ui::UIManager@ manager, MainScreenServerItem@[]@ list) {
 
243
                        @this.manager = manager;
 
244
                        @this.list = list;
 
245
                }
 
246
                int NumRows {
 
247
                        get { return int(list.length); }
 
248
                }
 
249
                private void OnItemClicked(spades::ui::UIElement@ sender){
 
250
                        ServerListItem@ item = cast<ServerListItem>(sender);
 
251
                        if(ItemActivated !is null) {
 
252
                                ItemActivated(this, item.item);
 
253
                        }
 
254
                }
 
255
                private void OnItemDoubleClicked(spades::ui::UIElement@ sender){
 
256
                        ServerListItem@ item = cast<ServerListItem>(sender);
 
257
                        if(ItemDoubleClicked !is null) {
 
258
                                ItemDoubleClicked(this, item.item);
 
259
                        }
 
260
                }
 
261
                private void OnItemRightClicked(spades::ui::UIElement@ sender){
 
262
                        ServerListItem@ item = cast<ServerListItem>(sender);
 
263
                        if(ItemRightClicked !is null) {
 
264
                                ItemRightClicked(this, item.item);
 
265
                        }
 
266
                }
 
267
                spades::ui::UIElement@ CreateElement(int row) {
 
268
                        ServerListItem i(manager, list[row]);
 
269
                        @i.Activated = spades::ui::EventHandler(this.OnItemClicked);
 
270
                        @i.DoubleClicked = spades::ui::EventHandler(this.OnItemDoubleClicked);
 
271
                        @i.RightClicked = spades::ui::EventHandler(this.OnItemRightClicked);
 
272
                        return i;
 
273
                }
 
274
                void RecycleElement(spades::ui::UIElement@ elem) {}
 
275
        }
 
276
 
 
277
 
 
278
        class ServerListHeader: spades::ui::ButtonBase {
 
279
                string Text;
 
280
                ServerListHeader(spades::ui::UIManager@ manager){
 
281
                        super(manager);
 
282
                }
 
283
                void OnActivated() {
 
284
                        ButtonBase::OnActivated();
 
285
                }
 
286
                void Render() {
 
287
                        Renderer@ renderer = Manager.Renderer;
 
288
                        Vector2 pos = ScreenPosition;
 
289
                        Vector2 size = Size;
 
290
                        Image@ img = renderer.RegisterImage("Gfx/White.tga");
 
291
                        if(Pressed && Hover) {
 
292
                                renderer.ColorNP = Vector4(1.f, 1.f, 1.f, 0.3f);
 
293
                        } else if(Hover) {
 
294
                                renderer.ColorNP = Vector4(1.f, 1.f, 1.f, 0.15f);
 
295
                        } else {
 
296
                                renderer.ColorNP = Vector4(1.f, 1.f, 1.f, 0.0f);
 
297
                        }
 
298
                        renderer.DrawImage(img, AABB2(pos.x - 2.f, pos.y, size.x, size.y));
 
299
 
 
300
                        Font.Draw(Text, ScreenPosition + Vector2(0.f, 2.f), 1.f, Vector4(1,1,1,1));
 
301
                }
 
302
        }
 
303
 
 
304
 
 
305
        class RefreshButton: spades::ui::SimpleButton {
 
306
                RefreshButton(spades::ui::UIManager@ manager){
 
307
                        super(manager);
 
308
                }
 
309
                void Render() {
 
310
                        SimpleButton::Render();
 
311
 
 
312
                        Renderer@ renderer = Manager.Renderer;
 
313
                        Vector2 pos = ScreenPosition;
 
314
                        Vector2 size = Size;
 
315
                        Image@ img = renderer.RegisterImage("Gfx/UI/Refresh.png");
 
316
                        renderer.DrawImage(img, pos + (size - Vector2(16.f, 16.f)) * 0.5f);
 
317
                }
 
318
        }
 
319
 
 
320
        class ProtocolButton: spades::ui::SimpleButton {
 
321
                ProtocolButton(spades::ui::UIManager@ manager){
 
322
                        super(manager);
 
323
                        Toggle = true;
 
324
                }
 
325
        }
 
326
 
 
327
        uint8 ToLower(uint8 c) {
 
328
                if(c >= uint8(0x41) and c <= uint8(0x5a)) {
 
329
                        return uint8(c - 0x41 + 0x61);
 
330
                } else {
 
331
                        return c;
 
332
                }
 
333
        }
 
334
        bool StringContainsCaseInsensitive(string text, string pattern) {
 
335
                for(int i = text.length - 1; i >= 0; i--)
 
336
                        text[i] = ToLower(text[i]);
 
337
                for(int i = pattern.length - 1; i >= 0; i--)
 
338
                        pattern[i] = ToLower(pattern[i]);
 
339
                return text.findFirst(pattern) >= 0;
 
340
        }
 
341
 
 
342
        class MainScreenMainMenu: spades::ui::UIElement {
 
343
 
 
344
                MainScreenUI@ ui;
 
345
                MainScreenHelper@ helper;
 
346
                spades::ui::Field@ addressField;
 
347
 
 
348
                spades::ui::Button@ protocol3Button;
 
349
                spades::ui::Button@ protocol4Button;
 
350
 
 
351
                spades::ui::Button@ filterProtocol3Button;
 
352
                spades::ui::Button@ filterProtocol4Button;
 
353
                spades::ui::Button@ filterEmptyButton;
 
354
                spades::ui::Button@ filterFullButton;
 
355
                spades::ui::Field@ filterField;
 
356
 
 
357
                spades::ui::ListView@ serverList;
 
358
                MainScreenServerListLoadingView@ loadingView;
 
359
                MainScreenServerListErrorView@ errorView;
 
360
                bool loading = false, loaded = false;
 
361
 
 
362
                private ConfigItem cg_protocolVersion("cg_protocolVersion", "3");
 
363
                private ConfigItem cg_lastQuickConnectHost("cg_lastQuickConnectHost");
 
364
                private ConfigItem cg_serverlistSort("cg_serverlistSort", "16385");
 
365
 
 
366
                MainScreenMainMenu(MainScreenUI@ ui) {
 
367
                        super(ui.manager);
 
368
                        @this.ui = ui;
 
369
                        @this.helper = ui.helper;
 
370
 
 
371
                        float contentsWidth = 750.f;
 
372
                        float contentsLeft = (Manager.Renderer.ScreenWidth - contentsWidth) * 0.5f;
 
373
                        float footerPos = Manager.Renderer.ScreenHeight - 50.f;
 
374
                        {
 
375
                                spades::ui::Button button(Manager);
 
376
                                button.Caption = _Tr("MainScreen", "Connect");
 
377
                                button.Bounds = AABB2(contentsLeft + contentsWidth - 150.f, 200.f, 150.f, 30.f);
 
378
                                @button.Activated = spades::ui::EventHandler(this.OnConnectPressed);
 
379
                                AddChild(button);
 
380
                        }
 
381
                        {
 
382
                                @addressField = spades::ui::Field(Manager);
 
383
                                addressField.Bounds = AABB2(contentsLeft, 200, contentsWidth - 240.f, 30.f);
 
384
                                addressField.Placeholder = _Tr("MainScreen", "Quick Connect");
 
385
                                addressField.Text = cg_lastQuickConnectHost.StringValue;
 
386
                                @addressField.Changed = spades::ui::EventHandler(this.OnAddressChanged);
 
387
                                AddChild(addressField);
 
388
                        }
 
389
                        {
 
390
                                @protocol3Button = ProtocolButton(Manager);
 
391
                                protocol3Button.Bounds = AABB2(contentsLeft + contentsWidth - 240.f + 6.f, 200,
 
392
                                        40.f, 30.f);
 
393
                                protocol3Button.Caption = _Tr("MainScreen", "0.75");
 
394
                                @protocol3Button.Activated = spades::ui::EventHandler(this.OnProtocol3Pressed);
 
395
                                protocol3Button.Toggle = true;
 
396
                                protocol3Button.Toggled = cg_protocolVersion.IntValue == 3;
 
397
                                AddChild(protocol3Button);
 
398
                        }
 
399
                        {
 
400
                                @protocol4Button = ProtocolButton(Manager);
 
401
                                protocol4Button.Bounds = AABB2(contentsLeft + contentsWidth - 200.f + 6.f, 200,
 
402
                                        40.f, 30.f);
 
403
                                protocol4Button.Caption = _Tr("MainScreen", "0.76");
 
404
                                @protocol4Button.Activated = spades::ui::EventHandler(this.OnProtocol4Pressed);
 
405
                                protocol4Button.Toggle = true;
 
406
                                protocol4Button.Toggled = cg_protocolVersion.IntValue == 4;
 
407
                                AddChild(protocol4Button);
 
408
                        }
 
409
                        {
 
410
                                spades::ui::Button button(Manager);
 
411
                                button.Caption = _Tr("MainScreen", "Quit");
 
412
                                button.Bounds = AABB2(contentsLeft + contentsWidth - 100.f, footerPos, 100.f, 30.f);
 
413
                                @button.Activated = spades::ui::EventHandler(this.OnQuitPressed);
 
414
                                AddChild(button);
 
415
                        }
 
416
                        {
 
417
                                spades::ui::Button button(Manager);
 
418
                                button.Caption = _Tr("MainScreen", "Credits");
 
419
                                button.Bounds = AABB2(contentsLeft + contentsWidth - 202.f, footerPos, 100.f, 30.f);
 
420
                                @button.Activated = spades::ui::EventHandler(this.OnCreditsPressed);
 
421
                                AddChild(button);
 
422
                        }
 
423
                        {
 
424
                                spades::ui::Button button(Manager);
 
425
                                button.Caption = _Tr("MainScreen", "Setup");
 
426
                                button.Bounds = AABB2(contentsLeft + contentsWidth - 304.f, footerPos, 100.f, 30.f);
 
427
                                @button.Activated = spades::ui::EventHandler(this.OnSetupPressed);
 
428
                                AddChild(button);
 
429
                        }
 
430
                        {
 
431
                                RefreshButton button(Manager);
 
432
                                button.Bounds = AABB2(contentsLeft + contentsWidth - 364.f, footerPos, 30.f, 30.f);
 
433
                                @button.Activated = spades::ui::EventHandler(this.OnRefreshServerListPressed);
 
434
                                AddChild(button);
 
435
                        }
 
436
                        {
 
437
                                spades::ui::Label label(Manager);
 
438
                                label.Text = _Tr("MainScreen", "Filter");
 
439
                                label.Bounds = AABB2(contentsLeft, footerPos, 50.f, 30.f);
 
440
                                label.Alignment = Vector2(0.f, 0.5f);
 
441
                                AddChild(label);
 
442
                        }
 
443
                        {
 
444
                                @filterProtocol3Button = ProtocolButton(Manager);
 
445
                                filterProtocol3Button.Bounds = AABB2(contentsLeft + 50.f, footerPos,
 
446
                                        40.f, 30.f);
 
447
                                filterProtocol3Button.Caption = _Tr("MainScreen", "0.75");
 
448
                                @filterProtocol3Button.Activated = spades::ui::EventHandler(this.OnFilterProtocol3Pressed);
 
449
                                filterProtocol3Button.Toggle = true;
 
450
                                AddChild(filterProtocol3Button);
 
451
                        }
 
452
                        {
 
453
                                @filterProtocol4Button = ProtocolButton(Manager);
 
454
                                filterProtocol4Button.Bounds = AABB2(contentsLeft + 90.f, footerPos,
 
455
                                        40.f, 30.f);
 
456
                                filterProtocol4Button.Caption = _Tr("MainScreen", "0.76");
 
457
                                @filterProtocol4Button.Activated = spades::ui::EventHandler(this.OnFilterProtocol4Pressed);
 
458
                                filterProtocol4Button.Toggle = true;
 
459
                                AddChild(filterProtocol4Button);
 
460
                        }
 
461
                        {
 
462
                                @filterEmptyButton = ProtocolButton(Manager);
 
463
                                filterEmptyButton.Bounds = AABB2(contentsLeft + 135.f, footerPos,
 
464
                                        50.f, 30.f);
 
465
                                filterEmptyButton.Caption = _Tr("MainScreen", "Empty");
 
466
                                @filterEmptyButton.Activated = spades::ui::EventHandler(this.OnFilterEmptyPressed);
 
467
                                filterEmptyButton.Toggle = true;
 
468
                                AddChild(filterEmptyButton);
 
469
                        }
 
470
                        {
 
471
                                @filterFullButton = ProtocolButton(Manager);
 
472
                                filterFullButton.Bounds = AABB2(contentsLeft + 185.f, footerPos,
 
473
                                        70.f, 30.f);
 
474
                                filterFullButton.Caption = _Tr("MainScreen", "Not Full");
 
475
                                @filterFullButton.Activated = spades::ui::EventHandler(this.OnFilterFullPressed);
 
476
                                filterFullButton.Toggle = true;
 
477
                                AddChild(filterFullButton);
 
478
                        }
 
479
                        {
 
480
                                @filterField = spades::ui::Field(Manager);
 
481
                                filterField.Bounds = AABB2(contentsLeft + 260.f, footerPos, 120.f, 30.f);
 
482
                                filterField.Placeholder = _Tr("MainScreen", "Filter");
 
483
                                @filterField.Changed = spades::ui::EventHandler(this.OnFilterTextChanged);
 
484
                                AddChild(filterField);
 
485
                        }
 
486
                        {
 
487
                                @serverList = spades::ui::ListView(Manager);
 
488
                                serverList.Bounds = AABB2(contentsLeft, 270.f, contentsWidth, footerPos - 280.f);
 
489
                                AddChild(serverList);
 
490
                        }
 
491
                        {
 
492
                                ServerListHeader header(Manager);
 
493
                                header.Bounds = AABB2(contentsLeft + 2.f, 240.f, 300.f - 2.f, 30.f);
 
494
                                header.Text = _Tr("MainScreen", "Server Name");
 
495
                                @header.Activated = spades::ui::EventHandler(this.SortServerListByName);
 
496
                                AddChild(header);
 
497
                        }
 
498
                        {
 
499
                                ServerListHeader header(Manager);
 
500
                                header.Bounds = AABB2(contentsLeft + 300.f, 240.f, 100.f, 30.f);
 
501
                                header.Text = _Tr("MainScreen", "Players");
 
502
                                @header.Activated = spades::ui::EventHandler(this.SortServerListByNumPlayers);
 
503
                                AddChild(header);
 
504
                        }
 
505
                        {
 
506
                                ServerListHeader header(Manager);
 
507
                                header.Bounds = AABB2(contentsLeft + 400.f, 240.f, 150.f, 30.f);
 
508
                                header.Text = _Tr("MainScreen", "Map Name");
 
509
                                @header.Activated = spades::ui::EventHandler(this.SortServerListByMapName);
 
510
                                AddChild(header);
 
511
                        }
 
512
                        {
 
513
                                ServerListHeader header(Manager);
 
514
                                header.Bounds = AABB2(contentsLeft + 550.f, 240.f, 80.f, 30.f);
 
515
                                header.Text = _Tr("MainScreen", "Game Mode");
 
516
                                @header.Activated = spades::ui::EventHandler(this.SortServerListByGameMode);
 
517
                                AddChild(header);
 
518
                        }
 
519
                        {
 
520
                                ServerListHeader header(Manager);
 
521
                                header.Bounds = AABB2(contentsLeft + 630.f, 240.f, 50.f, 30.f);
 
522
                                header.Text = _Tr("MainScreen", "Ver.");
 
523
                                @header.Activated = spades::ui::EventHandler(this.SortServerListByProtocol);
 
524
                                AddChild(header);
 
525
                        }
 
526
                        {
 
527
                                ServerListHeader header(Manager);
 
528
                                header.Bounds = AABB2(contentsLeft + 680.f, 240.f, 50.f, 30.f);
 
529
                                header.Text = _Tr("MainScreen", "Loc.");
 
530
                                @header.Activated = spades::ui::EventHandler(this.SortServerListByCountry);
 
531
                                AddChild(header);
 
532
                        }
 
533
                        {
 
534
                                @loadingView = MainScreenServerListLoadingView(Manager);
 
535
                                loadingView.Bounds = AABB2(contentsLeft, 240.f, contentsWidth, 100.f);
 
536
                                loadingView.Visible = false;
 
537
                                AddChild(loadingView);
 
538
                        }
 
539
                        {
 
540
                                @errorView = MainScreenServerListErrorView(Manager);
 
541
                                errorView.Bounds = AABB2(contentsLeft, 240.f, contentsWidth, 100.f);
 
542
                                errorView.Visible = false;
 
543
                                AddChild(errorView);
 
544
                        }
 
545
                        LoadServerList();
 
546
                }
 
547
 
 
548
                void LoadServerList() {
 
549
                        if(loading) {
 
550
                                return;
 
551
                        }
 
552
                        loaded = false;
 
553
                        loading = true;
 
554
                        @serverList.Model = spades::ui::ListViewModel(); // empty
 
555
                        errorView.Visible = false;
 
556
                        loadingView.Visible = true;
 
557
                        helper.StartQuery();
 
558
                }
 
559
 
 
560
                void ServerListItemActivated(ServerListModel@ sender, MainScreenServerItem@ item) {
 
561
                        addressField.Text = item.Address;
 
562
                        cg_lastQuickConnectHost = addressField.Text;
 
563
                        if(item.Protocol == "0.75") {
 
564
                                SetProtocolVersion(3);
 
565
                        }else if(item.Protocol == "0.76") {
 
566
                                SetProtocolVersion(4);
 
567
                        }
 
568
                        addressField.SelectAll();
 
569
                }
 
570
 
 
571
                void ServerListItemDoubleClicked(ServerListModel@ sender, MainScreenServerItem@ item) {
 
572
                        ServerListItemActivated(sender, item);
 
573
 
 
574
                        // Double-click to connect
 
575
                        Connect();
 
576
                }
 
577
 
 
578
                void ServerListItemRightClicked(ServerListModel@ sender, MainScreenServerItem@ item) {
 
579
                        helper.SetServerFavorite(item.Address, !item.Favorite);
 
580
                        UpdateServerList();
 
581
                }
 
582
 
 
583
                private void SortServerListByPing(spades::ui::UIElement@ sender) {
 
584
                        SortServerList(0);
 
585
                }
 
586
                private void SortServerListByNumPlayers(spades::ui::UIElement@ sender) {
 
587
                        SortServerList(1);
 
588
                }
 
589
                private void SortServerListByName(spades::ui::UIElement@ sender) {
 
590
                        SortServerList(2);
 
591
                }
 
592
                private void SortServerListByMapName(spades::ui::UIElement@ sender) {
 
593
                        SortServerList(3);
 
594
                }
 
595
                private void SortServerListByGameMode(spades::ui::UIElement@ sender) {
 
596
                        SortServerList(4);
 
597
                }
 
598
                private void SortServerListByProtocol(spades::ui::UIElement@ sender) {
 
599
                        SortServerList(5);
 
600
                }
 
601
                private void SortServerListByCountry(spades::ui::UIElement@ sender) {
 
602
                        SortServerList(6);
 
603
                }
 
604
 
 
605
                private void SortServerList(int keyId) {
 
606
                        int sort = cg_serverlistSort.IntValue;
 
607
                        if(int(sort & 0xfff) == keyId) {
 
608
                                sort ^= int(0x4000);
 
609
                        } else {
 
610
                                sort = keyId;
 
611
                        }
 
612
                        cg_serverlistSort = sort;
 
613
                        UpdateServerList();
 
614
                }
 
615
 
 
616
                private void UpdateServerList() {
 
617
                        string key = "";
 
618
                        switch(cg_serverlistSort.IntValue & 0xfff) {
 
619
                                case 0: key = "Ping"; break;
 
620
                                case 1: key = "NumPlayers"; break;
 
621
                                case 2: key = "Name"; break;
 
622
                                case 3: key = "MapName"; break;
 
623
                                case 4: key = "GameMode"; break;
 
624
                                case 5: key = "Protocol"; break;
 
625
                                case 6: key = "Country"; break;
 
626
                        }
 
627
                        MainScreenServerItem@[]@ list = helper.GetServerList(key,
 
628
                                (cg_serverlistSort.IntValue & 0x4000) != 0);
 
629
                        if((list is null) or (loading)){
 
630
                                @serverList.Model = spades::ui::ListViewModel(); // empty
 
631
                                return;
 
632
                        }
 
633
 
 
634
                        // filter the server list
 
635
                        bool filterProtocol3 = filterProtocol3Button.Toggled;
 
636
                        bool filterProtocol4 = filterProtocol4Button.Toggled;
 
637
                        bool filterEmpty = filterEmptyButton.Toggled;
 
638
                        bool filterFull = filterFullButton.Toggled;
 
639
                        string filterText = filterField.Text;
 
640
                        MainScreenServerItem@[]@ list2 = array<spades::MainScreenServerItem@>();
 
641
                        for(int i = 0, count = list.length; i < count; i++) {
 
642
                                MainScreenServerItem@ item = list[i];
 
643
                                if(filterProtocol3 and (item.Protocol != "0.75")) {
 
644
                                        continue;
 
645
                                }
 
646
                                if(filterProtocol4 and (item.Protocol != "0.76")) {
 
647
                                        continue;
 
648
                                }
 
649
                                if(filterEmpty and (item.NumPlayers > 0)) {
 
650
                                        continue;
 
651
                                }
 
652
                                if(filterFull and (item.NumPlayers >= item.MaxPlayers)) {
 
653
                                        continue;
 
654
                                }
 
655
                                if(filterText.length > 0) {
 
656
                                        if(not (StringContainsCaseInsensitive(item.Name, filterText) or
 
657
                                                StringContainsCaseInsensitive(item.MapName, filterText) or
 
658
                                                StringContainsCaseInsensitive(item.GameMode, filterText))) {
 
659
                                                continue;
 
660
                                        }
 
661
                                }
 
662
                                list2.insertLast(item);
 
663
                        }
 
664
 
 
665
                        ServerListModel model(Manager, list2);
 
666
                        @serverList.Model = model;
 
667
                        @model.ItemActivated = ServerListItemEventHandler(this.ServerListItemActivated);
 
668
                        @model.ItemDoubleClicked = ServerListItemEventHandler(this.ServerListItemDoubleClicked);
 
669
                        @model.ItemRightClicked = ServerListItemEventHandler(this.ServerListItemRightClicked);
 
670
                        serverList.ScrollToTop();
 
671
                }
 
672
 
 
673
                private void CheckServerList() {
 
674
                        if(helper.PollServerListState()) {
 
675
                                MainScreenServerItem@[]@ list = helper.GetServerList("", false);
 
676
                                if(list is null or list.length == 0) {
 
677
                                        // failed.
 
678
                                        // FIXME: show error message?
 
679
                                        loaded = false; loading = false;
 
680
                                        errorView.Visible = true;
 
681
                                        loadingView.Visible = false;
 
682
                                        @serverList.Model = spades::ui::ListViewModel(); // empty
 
683
                                        return;
 
684
                                }
 
685
                                loading = false;
 
686
                                loaded = true;
 
687
                                errorView.Visible = false;
 
688
                                loadingView.Visible = false;
 
689
                                UpdateServerList();
 
690
                        }
 
691
                }
 
692
 
 
693
                private void OnAddressChanged(spades::ui::UIElement@ sender) {
 
694
                        cg_lastQuickConnectHost = addressField.Text;
 
695
                }
 
696
 
 
697
                private void SetProtocolVersion(int ver) {
 
698
                        protocol3Button.Toggled = (ver == 3);
 
699
                        protocol4Button.Toggled = (ver == 4);
 
700
                        cg_protocolVersion = ver;
 
701
                }
 
702
 
 
703
                private void OnProtocol3Pressed(spades::ui::UIElement@ sender) {
 
704
                        SetProtocolVersion(3);
 
705
                }
 
706
 
 
707
                private void OnProtocol4Pressed(spades::ui::UIElement@ sender) {
 
708
                        SetProtocolVersion(4);
 
709
                }
 
710
 
 
711
                private void OnFilterProtocol3Pressed(spades::ui::UIElement@ sender) {
 
712
                        filterProtocol4Button.Toggled = false;
 
713
                        UpdateServerList();
 
714
                }
 
715
                private void OnFilterProtocol4Pressed(spades::ui::UIElement@ sender) {
 
716
                        filterProtocol3Button.Toggled = false;
 
717
                        UpdateServerList();
 
718
                }
 
719
                private void OnFilterFullPressed(spades::ui::UIElement@ sender) {
 
720
                        filterEmptyButton.Toggled = false;
 
721
                        UpdateServerList();
 
722
                }
 
723
                private void OnFilterEmptyPressed(spades::ui::UIElement@ sender) {
 
724
                        filterFullButton.Toggled = false;
 
725
                        UpdateServerList();
 
726
                }
 
727
                private void OnFilterTextChanged(spades::ui::UIElement@ sender) {
 
728
                        UpdateServerList();
 
729
                }
 
730
 
 
731
                private void OnRefreshServerListPressed(spades::ui::UIElement@ sender) {
 
732
                        LoadServerList();
 
733
                }
 
734
 
 
735
                private void OnQuitPressed(spades::ui::UIElement@ sender) {
 
736
                        ui.shouldExit = true;
 
737
                }
 
738
 
 
739
                private void OnCreditsPressed(spades::ui::UIElement@ sender) {
 
740
                        AlertScreen al(this, ui.helper.Credits, Min(500.f, Manager.Renderer.ScreenHeight - 100.f));
 
741
                        al.Run();
 
742
                }
 
743
 
 
744
                private void OnSetupPressed(spades::ui::UIElement@ sender) {
 
745
                        PreferenceView al(this, PreferenceViewOptions(), ui.fontManager);
 
746
                        al.Run();
 
747
                }
 
748
 
 
749
                private void Connect() {
 
750
                        string msg = helper.ConnectServer(addressField.Text, cg_protocolVersion.IntValue);
 
751
                        if(msg.length > 0) {
 
752
                                // failde to initialize client.
 
753
                                AlertScreen al(this, msg);
 
754
                                al.Run();
 
755
                        }
 
756
                }
 
757
 
 
758
                private void OnConnectPressed(spades::ui::UIElement@ sender) {
 
759
                        Connect();
 
760
                }
 
761
 
 
762
                void HotKey(string key) {
 
763
                        if(IsEnabled and key == "Enter") {
 
764
                                Connect();
 
765
                        } else if(IsEnabled and key == "Escape") {
 
766
                                ui.shouldExit = true;
 
767
                        } else {
 
768
                                UIElement::HotKey(key);
 
769
                        }
 
770
                }
 
771
 
 
772
                void Render() {
 
773
                        CheckServerList();
 
774
                        UIElement::Render();
 
775
 
 
776
                        // check for client error message.
 
777
                        if(IsEnabled) {
 
778
                                string msg = helper.GetPendingErrorMessage();
 
779
                                if(msg.length > 0) {
 
780
                                        // try to maek the "disconnected" message more friendly.
 
781
                                        if(msg.findFirst("Disconnected:") >= 0) {
 
782
                                                int ind1 = msg.findFirst("Disconnected:");
 
783
                                                int ind2 = msg.findFirst("\n", ind1);
 
784
                                                if(ind2 < 0) ind2 = msg.length;
 
785
                                                ind1 += "Disconnected:".length;
 
786
                                                msg = msg.substr(ind1, ind2 - ind1);
 
787
                                                msg = _Tr("MainScreen", "You were disconnected from the server because of the following reason:\n\n{0}", msg);
 
788
                                        }
 
789
 
 
790
                                        // failed to connect.
 
791
                                        AlertScreen al(this, msg);
 
792
                                        al.Run();
 
793
                                }
 
794
                        }
 
795
                }
 
796
        }
 
797
 
 
798
        class MainScreenServerListLoadingView: spades::ui::UIElement {
 
799
                MainScreenServerListLoadingView(spades::ui::UIManager@ manager) {
 
800
                        super(manager);
 
801
                }
 
802
                void Render() {
 
803
                        Renderer@ renderer = Manager.Renderer;
 
804
                        Vector2 pos = ScreenPosition;
 
805
                        Vector2 size = Size;
 
806
                        Font@ font = this.Font;
 
807
                        string text = _Tr("MainScreen", "Loading...");
 
808
                        Vector2 txtSize = font.Measure(text);
 
809
                        Vector2 txtPos;
 
810
                        txtPos = pos + (size - txtSize) * 0.5f;
 
811
 
 
812
                        font.Draw(text, txtPos, 1.f, Vector4(1,1,1,0.8));
 
813
                }
 
814
        }
 
815
 
 
816
        class MainScreenServerListErrorView: spades::ui::UIElement {
 
817
                MainScreenServerListErrorView(spades::ui::UIManager@ manager) {
 
818
                        super(manager);
 
819
                }
 
820
                void Render() {
 
821
                        Renderer@ renderer = Manager.Renderer;
 
822
                        Vector2 pos = ScreenPosition;
 
823
                        Vector2 size = Size;
 
824
                        Font@ font = this.Font;
 
825
                        string text = _Tr("MainScreen", "Failed to fetch the server list.");
 
826
                        Vector2 txtSize = font.Measure(text);
 
827
                        Vector2 txtPos;
 
828
                        txtPos = pos + (size - txtSize) * 0.5f;
 
829
 
 
830
                        font.Draw(text, txtPos, 1.f, Vector4(1,1,1,0.8));
 
831
                }
 
832
        }
 
833
 
 
834
        MainScreenUI@ CreateMainScreenUI(Renderer@ renderer, AudioDevice@ audioDevice,
 
835
                FontManager@ fontManager, MainScreenHelper@ helper) {
 
836
                return MainScreenUI(renderer, audioDevice, fontManager, helper);
 
837
        }
 
838
}