83 * found on the network. |
80 * found on the network. |
84 * @param unselect unselect the currently selected item */ |
81 * @param unselect unselect the currently selected item */ |
85 void UpdateNetworkGameWindow(bool unselect) |
82 void UpdateNetworkGameWindow(bool unselect) |
86 { |
83 { |
87 InvalidateWindowData(WC_NETWORK_WINDOW, 0, unselect); |
84 InvalidateWindowData(WC_NETWORK_WINDOW, 0, unselect); |
88 } |
|
89 |
|
90 static bool _internal_sort_order; // Used for Qsort order-flipping |
|
91 typedef int CDECL NGameNameSortFunction(const void*, const void*); |
|
92 |
|
93 /** Qsort function to sort by name. */ |
|
94 static int CDECL NGameNameSorter(const void *a, const void *b) |
|
95 { |
|
96 const NetworkGameList *cmp1 = *(const NetworkGameList**)a; |
|
97 const NetworkGameList *cmp2 = *(const NetworkGameList**)b; |
|
98 int r = strcasecmp(cmp1->info.server_name, cmp2->info.server_name); |
|
99 |
|
100 return _internal_sort_order ? -r : r; |
|
101 } |
|
102 |
|
103 /** Qsort function to sort by the amount of clients online on a |
|
104 * server. If the two servers have the same amount, the one with the |
|
105 * higher maximum is preferred. */ |
|
106 static int CDECL NGameClientSorter(const void *a, const void *b) |
|
107 { |
|
108 const NetworkGameList *cmp1 = *(const NetworkGameList**)a; |
|
109 const NetworkGameList *cmp2 = *(const NetworkGameList**)b; |
|
110 /* Reverse as per default we are interested in most-clients first */ |
|
111 int r = cmp1->info.clients_on - cmp2->info.clients_on; |
|
112 |
|
113 if (r == 0) r = cmp1->info.clients_max - cmp2->info.clients_max; |
|
114 if (r == 0) r = strcasecmp(cmp1->info.server_name, cmp2->info.server_name); |
|
115 |
|
116 return _internal_sort_order ? -r : r; |
|
117 } |
|
118 |
|
119 /** Qsort function to sort by joinability. If both servers are the |
|
120 * same, prefer the non-passworded server first. */ |
|
121 static int CDECL NGameAllowedSorter(const void *a, const void *b) |
|
122 { |
|
123 const NetworkGameList *cmp1 = *(const NetworkGameList**)a; |
|
124 const NetworkGameList *cmp2 = *(const NetworkGameList**)b; |
|
125 |
|
126 /* The servers we do not know anything about (the ones that did not reply) should be at the bottom) */ |
|
127 int r = StrEmpty(cmp1->info.server_revision) - StrEmpty(cmp2->info.server_revision); |
|
128 |
|
129 /* Reverse default as we are interested in version-compatible clients first */ |
|
130 if (r == 0) r = cmp2->info.version_compatible - cmp1->info.version_compatible; |
|
131 /* The version-compatible ones are then sorted with NewGRF compatible first, incompatible last */ |
|
132 if (r == 0) r = cmp2->info.compatible - cmp1->info.compatible; |
|
133 /* Passworded servers should be below unpassworded servers */ |
|
134 if (r == 0) r = cmp1->info.use_password - cmp2->info.use_password; |
|
135 /* Finally sort on the name of the server */ |
|
136 if (r == 0) r = strcasecmp(cmp1->info.server_name, cmp2->info.server_name); |
|
137 |
|
138 return _internal_sort_order ? -r : r; |
|
139 } |
85 } |
140 |
86 |
141 /** Enum for NetworkGameWindow, referring to _network_game_window_widgets */ |
87 /** Enum for NetworkGameWindow, referring to _network_game_window_widgets */ |
142 enum NetworkGameWindowWidgets { |
88 enum NetworkGameWindowWidgets { |
143 NGWW_CLOSE, ///< Close 'X' button |
89 NGWW_CLOSE, ///< Close 'X' button |
169 NGWW_CANCEL, ///< 'Cancel' button |
115 NGWW_CANCEL, ///< 'Cancel' button |
170 }; |
116 }; |
171 |
117 |
172 typedef GUIList<NetworkGameList*> GUIGameServerList; |
118 typedef GUIList<NetworkGameList*> GUIGameServerList; |
173 |
119 |
174 struct NetworkGameWindow : public QueryStringBaseWindow { |
120 class NetworkGameWindow : public QueryStringBaseWindow { |
|
121 protected: |
|
122 /* Runtime saved values */ |
|
123 static Listing last_sorting; |
|
124 |
|
125 /* Constants for sorting servers */ |
|
126 static GUIGameServerList::SortFunction *const sorter_funcs[]; |
|
127 |
175 byte field; ///< selected text-field |
128 byte field; ///< selected text-field |
176 NetworkGameList *server; ///< selected server |
129 NetworkGameList *server; ///< selected server |
177 GUIGameServerList servers; ///< list with game servers. |
130 GUIGameServerList servers; ///< list with game servers. |
178 |
|
179 NetworkGameWindow(const WindowDesc *desc) : QueryStringBaseWindow(desc) |
|
180 { |
|
181 ttd_strlcpy(this->edit_str_buf, _network_player_name, lengthof(this->edit_str_buf)); |
|
182 this->afilter = CS_ALPHANUMERAL; |
|
183 InitializeTextBuffer(&this->text, this->edit_str_buf, lengthof(this->edit_str_buf), 120); |
|
184 |
|
185 UpdateNetworkGameWindow(true); |
|
186 |
|
187 this->vscroll.cap = 11; |
|
188 this->resize.step_height = NET_PRC__SIZE_OF_ROW; |
|
189 |
|
190 this->field = NGWW_PLAYER; |
|
191 this->server = NULL; |
|
192 |
|
193 this->servers.flags = VL_REBUILD | (_ng_sorting.order ? VL_DESC : VL_NONE); |
|
194 this->servers.sort_type = _ng_sorting.criteria; |
|
195 |
|
196 this->FindWindowPlacementAndResize(desc); |
|
197 } |
|
198 |
|
199 ~NetworkGameWindow() |
|
200 { |
|
201 } |
|
202 |
131 |
203 /** |
132 /** |
204 * (Re)build the network game list as its amount has changed because |
133 * (Re)build the network game list as its amount has changed because |
205 * an item has been added or deleted for example |
134 * an item has been added or deleted for example |
206 */ |
135 */ |
207 void BuildNetworkGameList() |
136 void BuildNetworkGameList() |
208 { |
137 { |
209 if (!(this->servers.flags & VL_REBUILD)) return; |
138 if (!this->servers.NeedRebuild()) return; |
210 |
139 |
211 /* Create temporary array of games to use for listing */ |
140 /* Create temporary array of games to use for listing */ |
212 this->servers.Clear(); |
141 this->servers.Clear(); |
213 |
142 |
214 for (NetworkGameList *ngl = _network_game_list; ngl != NULL; ngl = ngl->next) { |
143 for (NetworkGameList *ngl = _network_game_list; ngl != NULL; ngl = ngl->next) { |
215 *this->servers.Append() = ngl; |
144 *this->servers.Append() = ngl; |
216 } |
145 } |
217 |
146 |
218 this->servers.Compact(); |
147 this->servers.Compact(); |
219 |
148 this->servers.RebuildDone(); |
220 /* Force resort */ |
149 } |
221 this->servers.flags &= ~VL_REBUILD; |
150 |
222 this->servers.flags |= VL_RESORT; |
151 /** Sort servers by name. */ |
223 } |
152 static int CDECL NGameNameSorter(NetworkGameList* const *a, NetworkGameList* const *b) |
224 |
153 { |
|
154 return strcasecmp((*a)->info.server_name, (*b)->info.server_name); |
|
155 } |
|
156 |
|
157 /** Sort servers by the amount of clients online on a |
|
158 * server. If the two servers have the same amount, the one with the |
|
159 * higher maximum is preferred. */ |
|
160 static int CDECL NGameClientSorter(NetworkGameList* const *a, NetworkGameList* const *b) |
|
161 { |
|
162 /* Reverse as per default we are interested in most-clients first */ |
|
163 int r = (*a)->info.clients_on - (*b)->info.clients_on; |
|
164 |
|
165 if (r == 0) r = (*a)->info.clients_max - (*b)->info.clients_max; |
|
166 if (r == 0) r = NGameNameSorter(a, b); |
|
167 |
|
168 return r; |
|
169 } |
|
170 |
|
171 /** Sort servers by joinability. If both servers are the |
|
172 * same, prefer the non-passworded server first. */ |
|
173 static int CDECL NGameAllowedSorter(NetworkGameList* const *a, NetworkGameList* const *b) |
|
174 { |
|
175 /* The servers we do not know anything about (the ones that did not reply) should be at the bottom) */ |
|
176 int r = StrEmpty((*a)->info.server_revision) - StrEmpty((*b)->info.server_revision); |
|
177 |
|
178 /* Reverse default as we are interested in version-compatible clients first */ |
|
179 if (r == 0) r = (*b)->info.version_compatible - (*a)->info.version_compatible; |
|
180 /* The version-compatible ones are then sorted with NewGRF compatible first, incompatible last */ |
|
181 if (r == 0) r = (*b)->info.compatible - (*a)->info.compatible; |
|
182 /* Passworded servers should be below unpassworded servers */ |
|
183 if (r == 0) r = (*a)->info.use_password - (*b)->info.use_password; |
|
184 /* Finally sort on the name of the server */ |
|
185 if (r == 0) r = NGameNameSorter(a, b); |
|
186 |
|
187 return r; |
|
188 } |
|
189 |
|
190 /** Sort the server list */ |
225 void SortNetworkGameList() |
191 void SortNetworkGameList() |
226 { |
192 { |
227 static NGameNameSortFunction * const ngame_sorter[] = { |
193 if (!this->servers.Sort()) return; |
228 &NGameNameSorter, |
|
229 &NGameClientSorter, |
|
230 &NGameAllowedSorter |
|
231 }; |
|
232 |
|
233 NetworkGameList *item; |
|
234 uint i; |
|
235 |
|
236 if (!(this->servers.flags & VL_RESORT)) return; |
|
237 if (this->servers.Length() == 0) return; |
|
238 |
|
239 _internal_sort_order = !!(this->servers.flags & VL_DESC); |
|
240 qsort(this->servers.Begin(), this->servers.Length(), sizeof(*this->servers.Begin()), ngame_sorter[this->servers.sort_type]); |
|
241 |
194 |
242 /* After sorting ngl->sort_list contains the sorted items. Put these back |
195 /* After sorting ngl->sort_list contains the sorted items. Put these back |
243 * into the original list. Basically nothing has changed, we are only |
196 * into the original list. Basically nothing has changed, we are only |
244 * shuffling the ->next pointers */ |
197 * shuffling the ->next pointers */ |
245 _network_game_list = this->servers[0]; |
198 _network_game_list = this->servers[0]; |
246 for (item = _network_game_list, i = 1; i != this->servers.Length(); i++) { |
199 NetworkGameList *item = _network_game_list; |
|
200 for (uint i = 1; i != this->servers.Length(); i++) { |
247 item->next = this->servers[i]; |
201 item->next = this->servers[i]; |
248 item = item->next; |
202 item = item->next; |
249 } |
203 } |
250 item->next = NULL; |
204 item->next = NULL; |
251 |
|
252 this->servers.flags &= ~VL_RESORT; |
|
253 } |
205 } |
254 |
206 |
255 /** |
207 /** |
256 * Draw a single server line. |
208 * Draw a single server line. |
257 * @param cur_item the server to draw. |
209 * @param cur_item the server to draw. |
283 /* draw flag according to server language */ |
235 /* draw flag according to server language */ |
284 DrawSprite(SPR_FLAGS_BASE + cur_item->info.server_lang, PAL_NONE, this->widget[NGWW_INFO].left + 25, y); |
236 DrawSprite(SPR_FLAGS_BASE + cur_item->info.server_lang, PAL_NONE, this->widget[NGWW_INFO].left + 25, y); |
285 } |
237 } |
286 } |
238 } |
287 |
239 |
|
240 public: |
|
241 NetworkGameWindow(const WindowDesc *desc) : QueryStringBaseWindow(desc) |
|
242 { |
|
243 ttd_strlcpy(this->edit_str_buf, _settings_client.network.player_name, lengthof(this->edit_str_buf)); |
|
244 this->afilter = CS_ALPHANUMERAL; |
|
245 InitializeTextBuffer(&this->text, this->edit_str_buf, lengthof(this->edit_str_buf), 120); |
|
246 |
|
247 UpdateNetworkGameWindow(true); |
|
248 |
|
249 this->vscroll.cap = 11; |
|
250 this->resize.step_height = NET_PRC__SIZE_OF_ROW; |
|
251 |
|
252 this->field = NGWW_PLAYER; |
|
253 this->server = NULL; |
|
254 |
|
255 this->servers.SetListing(this->last_sorting); |
|
256 this->servers.SetSortFuncs(this->sorter_funcs); |
|
257 this->servers.ForceRebuild(); |
|
258 this->SortNetworkGameList(); |
|
259 |
|
260 this->FindWindowPlacementAndResize(desc); |
|
261 } |
|
262 |
|
263 ~NetworkGameWindow() |
|
264 { |
|
265 this->last_sorting = this->servers.GetListing(); |
|
266 } |
|
267 |
288 virtual void OnPaint() |
268 virtual void OnPaint() |
289 { |
269 { |
290 const NetworkGameList *sel = this->server; |
270 const NetworkGameList *sel = this->server; |
291 const SortButtonState arrow = (this->servers.flags & VL_DESC) ? SBS_DOWN : SBS_UP; |
271 const SortButtonState arrow = this->servers.IsDescSortOrder() ? SBS_DOWN : SBS_UP; |
292 |
272 |
293 if (this->servers.flags & VL_REBUILD) { |
273 if (this->servers.NeedRebuild()) { |
294 this->BuildNetworkGameList(); |
274 this->BuildNetworkGameList(); |
295 SetVScrollCount(this, this->servers.Length()); |
275 SetVScrollCount(this, this->servers.Length()); |
296 } |
276 } |
297 if (this->servers.flags & VL_RESORT) this->SortNetworkGameList(); |
277 this->SortNetworkGameList(); |
298 |
278 |
299 /* 'Refresh' button invisible if no server selected */ |
279 /* 'Refresh' button invisible if no server selected */ |
300 this->SetWidgetDisabledState(NGWW_REFRESH, sel == NULL); |
280 this->SetWidgetDisabledState(NGWW_REFRESH, sel == NULL); |
301 /* 'Join' button disabling conditions */ |
281 /* 'Join' button disabling conditions */ |
302 this->SetWidgetDisabledState(NGWW_JOIN, sel == NULL || // no Selected Server |
282 this->SetWidgetDisabledState(NGWW_JOIN, sel == NULL || // no Selected Server |
308 this->SetWidgetHiddenState(NGWW_NEWGRF, sel == NULL || |
288 this->SetWidgetHiddenState(NGWW_NEWGRF, sel == NULL || |
309 !sel->online || |
289 !sel->online || |
310 sel->info.grfconfig == NULL); |
290 sel->info.grfconfig == NULL); |
311 |
291 |
312 SetDParam(0, 0x00); |
292 SetDParam(0, 0x00); |
313 SetDParam(1, _lan_internet_types_dropdown[_network_lan_internet]); |
293 SetDParam(1, _lan_internet_types_dropdown[_settings_client.network.lan_internet]); |
314 this->DrawWidgets(); |
294 this->DrawWidgets(); |
315 |
295 |
316 /* Edit box to set player name */ |
296 /* Edit box to set player name */ |
317 this->DrawEditBox(NGWW_PLAYER); |
297 this->DrawEditBox(NGWW_PLAYER); |
318 |
298 |
319 DrawString(this->widget[NGWW_PLAYER].left - 100, 23, STR_NETWORK_PLAYER_NAME, TC_GOLD); |
299 DrawString(this->widget[NGWW_PLAYER].left - 100, 23, STR_NETWORK_PLAYER_NAME, TC_GOLD); |
320 |
300 |
321 /* Sort based on widgets: name, clients, compatibility */ |
301 /* Sort based on widgets: name, clients, compatibility */ |
322 switch (this->servers.sort_type) { |
302 switch (this->servers.SortType()) { |
323 case NGWW_NAME - NGWW_NAME: this->DrawSortButtonState(NGWW_NAME, arrow); break; |
303 case NGWW_NAME - NGWW_NAME: this->DrawSortButtonState(NGWW_NAME, arrow); break; |
324 case NGWW_CLIENTS - NGWW_NAME: this->DrawSortButtonState(NGWW_CLIENTS, arrow); break; |
304 case NGWW_CLIENTS - NGWW_NAME: this->DrawSortButtonState(NGWW_CLIENTS, arrow); break; |
325 case NGWW_INFO - NGWW_NAME: this->DrawSortButtonState(NGWW_INFO, arrow); break; |
305 case NGWW_INFO - NGWW_NAME: this->DrawSortButtonState(NGWW_INFO, arrow); break; |
326 } |
306 } |
327 |
307 |
328 uint16 y = NET_PRC__OFFSET_TOP_WIDGET + 3; |
308 uint16 y = NET_PRC__OFFSET_TOP_WIDGET + 3; |
329 int32 n = 0; |
309 |
330 int32 pos = this->vscroll.pos; |
310 const int max = min(this->vscroll.pos + this->vscroll.cap, (int)this->servers.Length()); |
331 const NetworkGameList *cur_item = _network_game_list; |
311 |
332 |
312 for (int i = this->vscroll.pos; i < max; ++i) { |
333 while (pos > 0 && cur_item != NULL) { |
313 const NetworkGameList *ngl = this->servers[i]; |
334 pos--; |
314 this->DrawServerLine(ngl, y, ngl == sel); |
335 cur_item = cur_item->next; |
|
336 } |
|
337 |
|
338 while (cur_item != NULL) { |
|
339 this->DrawServerLine(cur_item, y, cur_item == sel); |
|
340 |
|
341 cur_item = cur_item->next; |
|
342 y += NET_PRC__SIZE_OF_ROW; |
315 y += NET_PRC__SIZE_OF_ROW; |
343 if (++n == this->vscroll.cap) break; // max number of games in the window |
316 } |
344 } |
317 |
345 |
318 const NetworkGameList *last_joined = NetworkGameListAddItem(inet_addr(_settings_client.network.last_host), _settings_client.network.last_port); |
346 const NetworkGameList *last_joined = NetworkGameListAddItem(inet_addr(_network_last_host), _network_last_port); |
|
347 /* Draw the last joined server, if any */ |
319 /* Draw the last joined server, if any */ |
348 if (last_joined != NULL) this->DrawServerLine(last_joined, y = this->widget[NGWW_LASTJOINED].top + 3, last_joined == sel); |
320 if (last_joined != NULL) this->DrawServerLine(last_joined, y = this->widget[NGWW_LASTJOINED].top + 3, last_joined == sel); |
349 |
321 |
350 /* Draw the right menu */ |
322 /* Draw the right menu */ |
351 GfxFillRect(this->widget[NGWW_DETAILS].left + 1, 43, this->widget[NGWW_DETAILS].right - 1, 92, 157); |
323 GfxFillRect(this->widget[NGWW_DETAILS].left + 1, 43, this->widget[NGWW_DETAILS].right - 1, 92, 157); |
409 y += 2; |
381 y += 2; |
410 |
382 |
411 if (!sel->info.compatible) { |
383 if (!sel->info.compatible) { |
412 DrawStringCentered(this->widget[NGWW_DETAILS].left + 115, y, sel->info.version_compatible ? STR_NETWORK_GRF_MISMATCH : STR_NETWORK_VERSION_MISMATCH, TC_FROMSTRING); // server mismatch |
384 DrawStringCentered(this->widget[NGWW_DETAILS].left + 115, y, sel->info.version_compatible ? STR_NETWORK_GRF_MISMATCH : STR_NETWORK_VERSION_MISMATCH, TC_FROMSTRING); // server mismatch |
413 } else if (sel->info.clients_on == sel->info.clients_max) { |
385 } else if (sel->info.clients_on == sel->info.clients_max) { |
414 /* Show: server full, when clients_on == clients_max */ |
386 /* Show: server full, when clients_on == max_clients */ |
415 DrawStringCentered(this->widget[NGWW_DETAILS].left + 115, y, STR_NETWORK_SERVER_FULL, TC_FROMSTRING); // server full |
387 DrawStringCentered(this->widget[NGWW_DETAILS].left + 115, y, STR_NETWORK_SERVER_FULL, TC_FROMSTRING); // server full |
416 } else if (sel->info.use_password) { |
388 } else if (sel->info.use_password) { |
417 DrawStringCentered(this->widget[NGWW_DETAILS].left + 115, y, STR_NETWORK_PASSWORD, TC_FROMSTRING); // password warning |
389 DrawStringCentered(this->widget[NGWW_DETAILS].left + 115, y, STR_NETWORK_PASSWORD, TC_FROMSTRING); // password warning |
418 } |
390 } |
419 |
391 |
432 case NGWW_CANCEL: // Cancel button |
404 case NGWW_CANCEL: // Cancel button |
433 DeleteWindowById(WC_NETWORK_WINDOW, 0); |
405 DeleteWindowById(WC_NETWORK_WINDOW, 0); |
434 break; |
406 break; |
435 |
407 |
436 case NGWW_CONN_BTN: // 'Connection' droplist |
408 case NGWW_CONN_BTN: // 'Connection' droplist |
437 ShowDropDownMenu(this, _lan_internet_types_dropdown, _network_lan_internet, NGWW_CONN_BTN, 0, 0); // do it for widget NSSW_CONN_BTN |
409 ShowDropDownMenu(this, _lan_internet_types_dropdown, _settings_client.network.lan_internet, NGWW_CONN_BTN, 0, 0); // do it for widget NSSW_CONN_BTN |
438 break; |
410 break; |
439 |
411 |
440 case NGWW_NAME: // Sort by name |
412 case NGWW_NAME: // Sort by name |
441 case NGWW_CLIENTS: // Sort by connected clients |
413 case NGWW_CLIENTS: // Sort by connected clients |
442 case NGWW_INFO: // Connectivity (green dot) |
414 case NGWW_INFO: // Connectivity (green dot) |
443 if (this->servers.sort_type == widget - NGWW_NAME) this->servers.flags ^= VL_DESC; |
415 if (this->servers.SortType() == widget - NGWW_NAME) { |
444 this->servers.flags |= VL_RESORT; |
416 this->servers.ToggleSortOrder(); |
445 this->servers.sort_type = widget - NGWW_NAME; |
417 } else { |
446 |
418 this->servers.SetSortType(widget - NGWW_NAME); |
447 _ng_sorting.order = !!(this->servers.flags & VL_DESC); |
419 this->servers.ForceResort(); |
448 _ng_sorting.criteria = this->servers.sort_type; |
420 } |
449 this->SetDirty(); |
421 this->SetDirty(); |
450 break; |
422 break; |
451 |
423 |
452 case NGWW_MATRIX: { // Matrix to show networkgames |
424 case NGWW_MATRIX: { // Matrix to show networkgames |
453 NetworkGameList *cur_item; |
|
454 uint32 id_v = (pt.y - NET_PRC__OFFSET_TOP_WIDGET) / NET_PRC__SIZE_OF_ROW; |
425 uint32 id_v = (pt.y - NET_PRC__OFFSET_TOP_WIDGET) / NET_PRC__SIZE_OF_ROW; |
455 |
426 |
456 if (id_v >= this->vscroll.cap) return; // click out of bounds |
427 if (id_v >= this->vscroll.cap) return; // click out of bounds |
457 id_v += this->vscroll.pos; |
428 id_v += this->vscroll.pos; |
458 |
429 |
459 cur_item = _network_game_list; |
430 this->server = (id_v < this->servers.Length()) ? this->servers[id_v] : NULL; |
460 for (; id_v > 0 && cur_item != NULL; id_v--) cur_item = cur_item->next; |
|
461 |
|
462 this->server = cur_item; |
|
463 this->SetDirty(); |
431 this->SetDirty(); |
464 } break; |
432 } break; |
465 |
433 |
466 case NGWW_LASTJOINED: { |
434 case NGWW_LASTJOINED: { |
467 NetworkGameList *last_joined = NetworkGameListAddItem(inet_addr(_network_last_host), _network_last_port); |
435 NetworkGameList *last_joined = NetworkGameListAddItem(inet_addr(_settings_client.network.last_host), _settings_client.network.last_port); |
468 if (last_joined != NULL) { |
436 if (last_joined != NULL) { |
469 this->server = last_joined; |
437 this->server = last_joined; |
470 this->SetDirty(); |
438 this->SetDirty(); |
471 } |
439 } |
472 } break; |
440 } break; |
473 |
441 |
474 case NGWW_FIND: // Find server automatically |
442 case NGWW_FIND: // Find server automatically |
475 switch (_network_lan_internet) { |
443 switch (_settings_client.network.lan_internet) { |
476 case 0: NetworkUDPSearchGame(); break; |
444 case 0: NetworkUDPSearchGame(); break; |
477 case 1: NetworkUDPQueryMasterServer(); break; |
445 case 1: NetworkUDPQueryMasterServer(); break; |
478 } |
446 } |
479 break; |
447 break; |
480 |
448 |
481 case NGWW_ADD: // Add a server |
449 case NGWW_ADD: // Add a server |
482 ShowQueryString( |
450 ShowQueryString( |
483 BindCString(_network_default_ip), |
451 BindCString(_settings_client.network.connect_to_ip), |
484 STR_NETWORK_ENTER_IP, |
452 STR_NETWORK_ENTER_IP, |
485 31 | 0x1000, // maximum number of characters OR |
453 31 | 0x1000, // maximum number of characters OR |
486 250, // characters up to this width pixels, whichever is satisfied first |
454 250, // characters up to this width pixels, whichever is satisfied first |
487 this, CS_ALPHANUMERAL); |
455 this, CS_ALPHANUMERAL); |
488 break; |
456 break; |
587 offset += space; |
555 offset += space; |
588 } |
556 } |
589 } |
557 } |
590 }; |
558 }; |
591 |
559 |
|
560 Listing NetworkGameWindow::last_sorting = {false, 2}; |
|
561 GUIGameServerList::SortFunction *const NetworkGameWindow::sorter_funcs[] = { |
|
562 &NGameNameSorter, |
|
563 &NGameClientSorter, |
|
564 &NGameAllowedSorter |
|
565 }; |
|
566 |
|
567 |
592 static const Widget _network_game_window_widgets[] = { |
568 static const Widget _network_game_window_widgets[] = { |
593 /* TOP */ |
569 /* TOP */ |
594 { WWT_CLOSEBOX, RESIZE_NONE, BGC, 0, 10, 0, 13, STR_00C5, STR_018B_CLOSE_WINDOW}, // NGWW_CLOSE |
570 { WWT_CLOSEBOX, RESIZE_NONE, BGC, 0, 10, 0, 13, STR_00C5, STR_018B_CLOSE_WINDOW}, // NGWW_CLOSE |
595 { WWT_CAPTION, RESIZE_RIGHT, BGC, 11, 449, 0, 13, STR_NETWORK_MULTIPLAYER, STR_NULL}, // NGWW_CAPTION |
571 { WWT_CAPTION, RESIZE_RIGHT, BGC, 11, 449, 0, 13, STR_NETWORK_MULTIPLAYER, STR_NULL}, // NGWW_CAPTION |
596 { WWT_PANEL, RESIZE_RB, BGC, 0, 449, 14, 263, 0x0, STR_NULL}, // NGWW_RESIZE |
572 { WWT_PANEL, RESIZE_RB, BGC, 0, 449, 14, 263, 0x0, STR_NULL}, // NGWW_RESIZE |
690 FiosItem *map; ///< Selected map |
663 FiosItem *map; ///< Selected map |
691 byte widget_id; ///< The widget that has the pop-up input menu |
664 byte widget_id; ///< The widget that has the pop-up input menu |
692 |
665 |
693 NetworkStartServerWindow(const WindowDesc *desc) : QueryStringBaseWindow(desc) |
666 NetworkStartServerWindow(const WindowDesc *desc) : QueryStringBaseWindow(desc) |
694 { |
667 { |
695 ttd_strlcpy(this->edit_str_buf, _network_server_name, lengthof(this->edit_str_buf)); |
668 ttd_strlcpy(this->edit_str_buf, _settings_client.network.server_name, lengthof(this->edit_str_buf)); |
696 |
669 |
697 _saveload_mode = SLD_NEW_GAME; |
670 _saveload_mode = SLD_NEW_GAME; |
698 BuildFileList(); |
671 BuildFileList(); |
699 this->vscroll.cap = 12; |
672 this->vscroll.cap = 12; |
700 this->vscroll.count = _fios_num + 1; |
673 this->vscroll.count = _fios_items.Length(); |
701 |
674 |
702 this->afilter = CS_ALPHANUMERAL; |
675 this->afilter = CS_ALPHANUMERAL; |
703 InitializeTextBuffer(&this->text, this->edit_str_buf, lengthof(this->edit_str_buf), 160); |
676 InitializeTextBuffer(&this->text, this->edit_str_buf, lengthof(this->edit_str_buf), 160); |
704 |
677 |
705 this->field = NSSW_GAMENAME; |
678 this->field = NSSW_GAMENAME; |
706 _network_game_info.use_password = !StrEmpty(_network_server_password); |
|
707 |
679 |
708 this->FindWindowPlacementAndResize(desc); |
680 this->FindWindowPlacementAndResize(desc); |
709 } |
681 } |
710 |
682 |
711 virtual void OnPaint() |
683 virtual void OnPaint() |
712 { |
684 { |
713 int y = NSSWND_START, pos; |
685 int y = NSSWND_START; |
714 const FiosItem *item; |
686 const FiosItem *item; |
715 |
687 |
716 /* draw basic widgets */ |
688 /* draw basic widgets */ |
717 SetDParam(1, _connection_types_dropdown[_network_advertise]); |
689 SetDParam(1, _connection_types_dropdown[_network_advertise]); |
718 SetDParam(2, _network_game_info.clients_max); |
690 SetDParam(2, _settings_client.network.max_clients); |
719 SetDParam(3, _network_game_info.companies_max); |
691 SetDParam(3, _settings_client.network.max_companies); |
720 SetDParam(4, _network_game_info.spectators_max); |
692 SetDParam(4, _settings_client.network.max_spectators); |
721 SetDParam(5, STR_NETWORK_LANG_ANY + _network_game_info.server_lang); |
693 SetDParam(5, STR_NETWORK_LANG_ANY + _settings_client.network.server_lang); |
722 this->DrawWidgets(); |
694 this->DrawWidgets(); |
723 |
695 |
724 /* editbox to set game name */ |
696 /* editbox to set game name */ |
725 this->DrawEditBox(NSSW_GAMENAME); |
697 this->DrawEditBox(NSSW_GAMENAME); |
726 |
698 |
727 /* if password is set, draw red '*' next to 'Set password' button */ |
699 /* if password is set, draw red '*' next to 'Set password' button */ |
728 if (_network_game_info.use_password) DoDrawString("*", 408, 23, TC_RED); |
700 if (!StrEmpty(_settings_client.network.server_password)) DoDrawString("*", 408, 23, TC_RED); |
729 |
701 |
730 /* draw list of maps */ |
702 /* draw list of maps */ |
731 GfxFillRect(11, 63, 258, 215, 0xD7); // black background of maps list |
703 GfxFillRect(11, 63, 258, 215, 0xD7); // black background of maps list |
732 |
704 |
733 pos = this->vscroll.pos; |
705 for (uint pos = this->vscroll.pos; pos < _fios_items.Length() + 1; pos++) { |
734 while (pos < _fios_num + 1) { |
706 item = _fios_items.Get(pos - 1); |
735 item = _fios_list + pos - 1; |
|
736 if (item == this->map || (pos == 0 && this->map == NULL)) |
707 if (item == this->map || (pos == 0 && this->map == NULL)) |
737 GfxFillRect(11, y - 1, 258, y + 10, 155); // show highlighted item with a different colour |
708 GfxFillRect(11, y - 1, 258, y + 10, 155); // show highlighted item with a different colour |
738 |
709 |
739 if (pos == 0) { |
710 if (pos == 0) { |
740 DrawString(14, y, STR_4010_GENERATE_RANDOM_NEW_GAME, TC_DARK_GREEN); |
711 DrawString(14, y, STR_4010_GENERATE_RANDOM_NEW_GAME, TC_DARK_GREEN); |
741 } else { |
712 } else { |
742 DoDrawString(item->title, 14, y, _fios_colors[item->type] ); |
713 DoDrawString(item->title, 14, y, _fios_colors[item->type] ); |
743 } |
714 } |
744 pos++; |
|
745 y += NSSWND_ROWSIZE; |
715 y += NSSWND_ROWSIZE; |
746 |
716 |
747 if (y >= this->vscroll.cap * NSSWND_ROWSIZE + NSSWND_START) break; |
717 if (y >= this->vscroll.cap * NSSWND_ROWSIZE + NSSWND_START) break; |
748 } |
718 } |
749 } |
719 } |
762 ShowOnScreenKeyboard(this, NSSW_GAMENAME, 0, 0); |
732 ShowOnScreenKeyboard(this, NSSW_GAMENAME, 0, 0); |
763 break; |
733 break; |
764 |
734 |
765 case NSSW_SETPWD: // Set password button |
735 case NSSW_SETPWD: // Set password button |
766 this->widget_id = NSSW_SETPWD; |
736 this->widget_id = NSSW_SETPWD; |
767 ShowQueryString(BindCString(_network_server_password), STR_NETWORK_SET_PASSWORD, 20, 250, this, CS_ALPHANUMERAL); |
737 ShowQueryString(BindCString(_settings_client.network.server_password), STR_NETWORK_SET_PASSWORD, 20, 250, this, CS_ALPHANUMERAL); |
768 break; |
738 break; |
769 |
739 |
770 case NSSW_SELMAP: { // Select map |
740 case NSSW_SELMAP: { // Select map |
771 int y = (pt.y - NSSWND_START) / NSSWND_ROWSIZE; |
741 int y = (pt.y - NSSWND_START) / NSSWND_ROWSIZE; |
772 |
742 |
773 y += this->vscroll.pos; |
743 y += this->vscroll.pos; |
774 if (y >= this->vscroll.count) return; |
744 if (y >= this->vscroll.count) return; |
775 |
745 |
776 this->map = (y == 0) ? NULL : _fios_list + y - 1; |
746 this->map = (y == 0) ? NULL : _fios_items.Get(y - 1); |
777 this->SetDirty(); |
747 this->SetDirty(); |
778 } break; |
748 } break; |
779 |
749 |
780 case NSSW_CONNTYPE_BTN: // Connection type |
750 case NSSW_CONNTYPE_BTN: // Connection type |
781 ShowDropDownMenu(this, _connection_types_dropdown, _network_advertise, NSSW_CONNTYPE_BTN, 0, 0); // do it for widget NSSW_CONNTYPE_BTN |
751 ShowDropDownMenu(this, _connection_types_dropdown, _network_advertise, NSSW_CONNTYPE_BTN, 0, 0); // do it for widget NSSW_CONNTYPE_BTN |
789 this->HandleButtonClick(widget); |
759 this->HandleButtonClick(widget); |
790 this->SetDirty(); |
760 this->SetDirty(); |
791 switch (widget) { |
761 switch (widget) { |
792 default: NOT_REACHED(); |
762 default: NOT_REACHED(); |
793 case NSSW_CLIENTS_BTND: case NSSW_CLIENTS_BTNU: |
763 case NSSW_CLIENTS_BTND: case NSSW_CLIENTS_BTNU: |
794 _network_game_info.clients_max = Clamp(_network_game_info.clients_max + widget - NSSW_CLIENTS_TXT, 2, MAX_CLIENTS); |
764 _settings_client.network.max_clients = Clamp(_settings_client.network.max_clients + widget - NSSW_CLIENTS_TXT, 2, MAX_CLIENTS); |
795 break; |
765 break; |
796 case NSSW_COMPANIES_BTND: case NSSW_COMPANIES_BTNU: |
766 case NSSW_COMPANIES_BTND: case NSSW_COMPANIES_BTNU: |
797 _network_game_info.companies_max = Clamp(_network_game_info.companies_max + widget - NSSW_COMPANIES_TXT, 1, MAX_PLAYERS); |
767 _settings_client.network.max_companies = Clamp(_settings_client.network.max_companies + widget - NSSW_COMPANIES_TXT, 1, MAX_PLAYERS); |
798 break; |
768 break; |
799 case NSSW_SPECTATORS_BTND: case NSSW_SPECTATORS_BTNU: |
769 case NSSW_SPECTATORS_BTND: case NSSW_SPECTATORS_BTNU: |
800 _network_game_info.spectators_max = Clamp(_network_game_info.spectators_max + widget - NSSW_SPECTATORS_TXT, 0, MAX_CLIENTS); |
770 _settings_client.network.max_spectators = Clamp(_settings_client.network.max_spectators + widget - NSSW_SPECTATORS_TXT, 0, MAX_CLIENTS); |
801 break; |
771 break; |
802 } |
772 } |
803 } |
773 } |
804 _left_button_clicked = false; |
774 _left_button_clicked = false; |
805 break; |
775 break; |
806 |
776 |
807 case NSSW_CLIENTS_TXT: // Click on number of players |
777 case NSSW_CLIENTS_TXT: // Click on number of players |
808 this->widget_id = NSSW_CLIENTS_TXT; |
778 this->widget_id = NSSW_CLIENTS_TXT; |
809 SetDParam(0, _network_game_info.clients_max); |
779 SetDParam(0, _settings_client.network.max_clients); |
810 ShowQueryString(STR_CONFIG_PATCHES_INT32, STR_NETWORK_NUMBER_OF_CLIENTS, 3, 50, this, CS_NUMERAL); |
780 ShowQueryString(STR_CONFIG_PATCHES_INT32, STR_NETWORK_NUMBER_OF_CLIENTS, 3, 50, this, CS_NUMERAL); |
811 break; |
781 break; |
812 |
782 |
813 case NSSW_COMPANIES_TXT: // Click on number of companies |
783 case NSSW_COMPANIES_TXT: // Click on number of companies |
814 this->widget_id = NSSW_COMPANIES_TXT; |
784 this->widget_id = NSSW_COMPANIES_TXT; |
815 SetDParam(0, _network_game_info.companies_max); |
785 SetDParam(0, _settings_client.network.max_companies); |
816 ShowQueryString(STR_CONFIG_PATCHES_INT32, STR_NETWORK_NUMBER_OF_COMPANIES, 3, 50, this, CS_NUMERAL); |
786 ShowQueryString(STR_CONFIG_PATCHES_INT32, STR_NETWORK_NUMBER_OF_COMPANIES, 3, 50, this, CS_NUMERAL); |
817 break; |
787 break; |
818 |
788 |
819 case NSSW_SPECTATORS_TXT: // Click on number of spectators |
789 case NSSW_SPECTATORS_TXT: // Click on number of spectators |
820 this->widget_id = NSSW_SPECTATORS_TXT; |
790 this->widget_id = NSSW_SPECTATORS_TXT; |
821 SetDParam(0, _network_game_info.spectators_max); |
791 SetDParam(0, _settings_client.network.max_spectators); |
822 ShowQueryString(STR_CONFIG_PATCHES_INT32, STR_NETWORK_NUMBER_OF_SPECTATORS, 3, 50, this, CS_NUMERAL); |
792 ShowQueryString(STR_CONFIG_PATCHES_INT32, STR_NETWORK_NUMBER_OF_SPECTATORS, 3, 50, this, CS_NUMERAL); |
823 break; |
793 break; |
824 |
794 |
825 case NSSW_LANGUAGE_BTN: { // Language |
795 case NSSW_LANGUAGE_BTN: { // Language |
826 uint sel = 0; |
796 uint sel = 0; |
827 for (uint i = 0; i < lengthof(_language_dropdown) - 1; i++) { |
797 for (uint i = 0; i < lengthof(_language_dropdown) - 1; i++) { |
828 if (_language_dropdown[i] == STR_NETWORK_LANG_ANY + _network_game_info.server_lang) { |
798 if (_language_dropdown[i] == STR_NETWORK_LANG_ANY + _settings_client.network.server_lang) { |
829 sel = i; |
799 sel = i; |
830 break; |
800 break; |
831 } |
801 } |
832 } |
802 } |
833 ShowDropDownMenu(this, _language_dropdown, sel, NSSW_LANGUAGE_BTN, 0, 0); |
803 ShowDropDownMenu(this, _language_dropdown, sel, NSSW_LANGUAGE_BTN, 0, 0); |
887 { |
857 { |
888 EventState state = ES_NOT_HANDLED; |
858 EventState state = ES_NOT_HANDLED; |
889 if (this->field == NSSW_GAMENAME) { |
859 if (this->field == NSSW_GAMENAME) { |
890 if (this->HandleEditBoxKey(NSSW_GAMENAME, key, keycode, state) == 1) return state; // enter pressed |
860 if (this->HandleEditBoxKey(NSSW_GAMENAME, key, keycode, state) == 1) return state; // enter pressed |
891 |
861 |
892 ttd_strlcpy(_network_server_name, this->text.buf, sizeof(_network_server_name)); |
862 ttd_strlcpy(_settings_client.network.server_name, this->text.buf, sizeof(_settings_client.network.server_name)); |
893 } |
863 } |
894 |
864 |
895 return state; |
865 return state; |
896 } |
866 } |
897 |
867 |
898 virtual void OnQueryTextFinished(char *str) |
868 virtual void OnQueryTextFinished(char *str) |
899 { |
869 { |
900 if (str == NULL) return; |
870 if (str == NULL) return; |
901 |
871 |
902 if (this->widget_id == NSSW_SETPWD) { |
872 if (this->widget_id == NSSW_SETPWD) { |
903 ttd_strlcpy(_network_server_password, str, lengthof(_network_server_password)); |
873 ttd_strlcpy(_settings_client.network.server_password, str, lengthof(_settings_client.network.server_password)); |
904 _network_game_info.use_password = !StrEmpty(_network_server_password); |
|
905 } else { |
874 } else { |
906 int32 value = atoi(str); |
875 int32 value = atoi(str); |
907 this->InvalidateWidget(this->widget_id); |
876 this->InvalidateWidget(this->widget_id); |
908 switch (this->widget_id) { |
877 switch (this->widget_id) { |
909 default: NOT_REACHED(); |
878 default: NOT_REACHED(); |
910 case NSSW_CLIENTS_TXT: _network_game_info.clients_max = Clamp(value, 2, MAX_CLIENTS); break; |
879 case NSSW_CLIENTS_TXT: _settings_client.network.max_clients = Clamp(value, 2, MAX_CLIENTS); break; |
911 case NSSW_COMPANIES_TXT: _network_game_info.companies_max = Clamp(value, 1, MAX_PLAYERS); break; |
880 case NSSW_COMPANIES_TXT: _settings_client.network.max_companies = Clamp(value, 1, MAX_PLAYERS); break; |
912 case NSSW_SPECTATORS_TXT: _network_game_info.spectators_max = Clamp(value, 0, MAX_CLIENTS); break; |
881 case NSSW_SPECTATORS_TXT: _settings_client.network.max_spectators = Clamp(value, 0, MAX_CLIENTS); break; |
913 } |
882 } |
914 } |
883 } |
915 |
884 |
916 this->SetDirty(); |
885 this->SetDirty(); |
917 } |
886 } |
1129 } break; |
1098 } break; |
1130 |
1099 |
1131 case NLWW_JOIN: // Join company |
1100 case NLWW_JOIN: // Join company |
1132 /* Button can be clicked only when it is enabled */ |
1101 /* Button can be clicked only when it is enabled */ |
1133 _network_playas = this->company; |
1102 _network_playas = this->company; |
1134 NetworkClientConnectGame(_network_last_host, _network_last_port); |
1103 NetworkClientConnectGame(_settings_client.network.last_host, _settings_client.network.last_port); |
1135 break; |
1104 break; |
1136 |
1105 |
1137 case NLWW_NEW: // New company |
1106 case NLWW_NEW: // New company |
1138 _network_playas = PLAYER_NEW_COMPANY; |
1107 _network_playas = PLAYER_NEW_COMPANY; |
1139 NetworkClientConnectGame(_network_last_host, _network_last_port); |
1108 NetworkClientConnectGame(_settings_client.network.last_host, _settings_client.network.last_port); |
1140 break; |
1109 break; |
1141 |
1110 |
1142 case NLWW_SPECTATE: // Spectate game |
1111 case NLWW_SPECTATE: // Spectate game |
1143 _network_playas = PLAYER_SPECTATOR; |
1112 _network_playas = PLAYER_SPECTATOR; |
1144 NetworkClientConnectGame(_network_last_host, _network_last_port); |
1113 NetworkClientConnectGame(_settings_client.network.last_host, _settings_client.network.last_port); |
1145 break; |
1114 break; |
1146 |
1115 |
1147 case NLWW_REFRESH: // Refresh |
1116 case NLWW_REFRESH: // Refresh |
1148 NetworkTCPQueryServer(_network_last_host, _network_last_port); // company info |
1117 NetworkTCPQueryServer(_settings_client.network.last_host, _settings_client.network.last_port); // company info |
1149 NetworkUDPQueryServer(_network_last_host, _network_last_port); // general data |
1118 NetworkUDPQueryServer(_settings_client.network.last_host, _settings_client.network.last_port); // general data |
1150 break; |
1119 break; |
1151 } |
1120 } |
1152 } |
1121 } |
1153 }; |
1122 }; |
1154 |
1123 |
1919 struct NetworkCompanyPasswordWindow : public QueryStringBaseWindow { |
1894 struct NetworkCompanyPasswordWindow : public QueryStringBaseWindow { |
1920 NetworkCompanyPasswordWindow(const WindowDesc *desc, Window *parent) : QueryStringBaseWindow(desc) |
1895 NetworkCompanyPasswordWindow(const WindowDesc *desc, Window *parent) : QueryStringBaseWindow(desc) |
1921 { |
1896 { |
1922 this->parent = parent; |
1897 this->parent = parent; |
1923 this->afilter = CS_ALPHANUMERAL; |
1898 this->afilter = CS_ALPHANUMERAL; |
1924 InitializeTextBuffer(&this->text, this->edit_str_buf, min(lengthof(_network_default_company_pass), lengthof(this->edit_str_buf)), 0); |
1899 InitializeTextBuffer(&this->text, this->edit_str_buf, min(lengthof(_settings_client.network.default_company_pass), lengthof(this->edit_str_buf)), 0); |
1925 |
1900 |
1926 this->FindWindowPlacementAndResize(desc); |
1901 this->FindWindowPlacementAndResize(desc); |
1927 } |
1902 } |
1928 |
1903 |
1929 void OnOk() |
1904 void OnOk() |
1930 { |
1905 { |
1931 if (this->IsWidgetLowered(NCPWW_SAVE_AS_DEFAULT_PASSWORD)) { |
1906 if (this->IsWidgetLowered(NCPWW_SAVE_AS_DEFAULT_PASSWORD)) { |
1932 snprintf(_network_default_company_pass, lengthof(_network_default_company_pass), "%s", this->edit_str_buf); |
1907 snprintf(_settings_client.network.default_company_pass, lengthof(_settings_client.network.default_company_pass), "%s", this->edit_str_buf); |
1933 } |
1908 } |
1934 |
1909 |
1935 /* empty password is a '*' because of console argument */ |
1910 /* empty password is a '*' because of console argument */ |
1936 if (StrEmpty(this->edit_str_buf)) snprintf(this->edit_str_buf, lengthof(this->edit_str_buf), "*"); |
1911 if (StrEmpty(this->edit_str_buf)) snprintf(this->edit_str_buf, lengthof(this->edit_str_buf), "*"); |
1937 char *password = this->edit_str_buf; |
1912 char *password = this->edit_str_buf; |