Atrinik Client  4.0
characters.c
Go to the documentation of this file.
1 /*************************************************************************
2  * Atrinik, a Multiplayer Online Role Playing Game *
3  * *
4  * Copyright (C) 2009-2014 Alex Tokar and Atrinik Development Team *
5  * *
6  * Fork from Crossfire (Multiplayer game for X-windows). *
7  * *
8  * This program is free software; you can redistribute it and/or modify *
9  * it under the terms of the GNU General Public License as published by *
10  * the Free Software Foundation; either version 2 of the License, or *
11  * (at your option) any later version. *
12  * *
13  * This program is distributed in the hope that it will be useful, *
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of *
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
16  * GNU General Public License for more details. *
17  * *
18  * You should have received a copy of the GNU General Public License *
19  * along with this program; if not, write to the Free Software *
20  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. *
21  * *
22  * The author can be reached at admin@atrinik.org *
23  ************************************************************************/
24 
32 #include <global.h>
33 #include <toolkit/packet.h>
34 #include <toolkit/string.h>
35 #include <toolkit/datetime.h>
36 
37 enum {
38  TEXT_INPUT_CHARNAME,
39  TEXT_INPUT_PASSWORD,
40  TEXT_INPUT_PASSWORD_NEW,
41  TEXT_INPUT_PASSWORD_NEW2,
42 
43  TEXT_INPUT_NUM
44 } ;
45 
53 static button_struct button_tab_characters, button_tab_new, button_tab_password, button_character_male, button_character_female, button_character_left, button_character_right, button_login, button_done;
57 static text_input_struct text_inputs[TEXT_INPUT_NUM];
65 static size_t text_input_current;
69 static size_t character_race;
73 static size_t character_gender;
74 
80 static void button_tab_switch(button_struct *button)
81 {
82  if (button == &button_tab_characters) {
83  button_tab_new.pressed_forced = button_tab_password.pressed_forced = 0;
84  button_tab_characters.pressed_forced = 1;
85  } else if (button == &button_tab_new) {
86  button_tab_characters.pressed_forced = button_tab_password.pressed_forced = 0;
87  button_tab_new.pressed_forced = 1;
88 
89  text_input_reset(&text_inputs[TEXT_INPUT_CHARNAME]);
90  text_inputs[TEXT_INPUT_CHARNAME].focus = 1;
91 
92  character_race = 0;
94  } else if (button == &button_tab_password) {
95  size_t i;
96 
97  button_tab_characters.pressed_forced = button_tab_new.pressed_forced = 0;
98  button_tab_password.pressed_forced = 1;
99 
100  for (i = TEXT_INPUT_PASSWORD; i < TEXT_INPUT_NUM; i++) {
101  text_input_reset(&text_inputs[i]);
102  }
103 
104  text_input_current = TEXT_INPUT_PASSWORD;
105  text_inputs[text_input_current].focus = 1;
106  }
107 }
108 
111 {
112  if (text_input == &text_inputs[TEXT_INPUT_CHARNAME] && !char_contains(c, s_settings->text[SERVER_TEXT_ALLOWED_CHARS_CHARNAME])) {
113  return 0;
114  } else if (!char_contains(c, s_settings->text[SERVER_TEXT_ALLOWED_CHARS_PASSWORD])) {
115  return 0;
116  }
117 
118  return 1;
119 }
120 
122 static void list_text_color(list_struct *list, uint32_t row, uint32_t col, const char **color, const char **color_shadow)
123 {
124  if (col == 0) {
125  *color_shadow = NULL;
126  *color = NULL;
127  }
128 }
129 
131 static void list_post_column(list_struct *list, uint32_t row, uint32_t col)
132 {
133  if (col == 0) {
134  static uint32_t ticks[2] = {0, 0};
135  static uint8_t state[2] = {0, 0};
136  uint16_t anim_id, face;
137  size_t idx;
138 
139  anim_id = atoi(list->text[row][col]);
140  check_animation_status(anim_id);
141  idx = list->row_selected - 1 == row ? 1 : 0;
142 
143  if (SDL_GetTicks() - ticks[idx] > 500) {
144  ticks[idx] = SDL_GetTicks();
145  state[idx]++;
146  }
147 
148  if (state[idx] >= (animations[anim_id].num_animations / animations[anim_id].facings)) {
149  state[idx] = 0;
150  }
151 
152  if (list->row_selected - 1 == row) {
153  face = animations[anim_id].faces[(animations[anim_id].num_animations / animations[anim_id].facings) * (5 + 8) + state[idx]];
154  } else {
155  face = animations[anim_id].faces[(animations[anim_id].num_animations / animations[anim_id].facings) * 5 + state[idx]];
156  }
157 
158  image_request_face(face);
159 
160  if (FaceList[face].name) {
161  char *facename;
162 
163  facename = string_sub(FaceList[face].name, 0, -4);
164  text_show_format(list->surface, FONT_ARIAL10, list->x, LIST_ROWS_START(list) + (LIST_ROW_OFFSET(row, list) * LIST_ROW_HEIGHT(list)), COLOR_WHITE, TEXT_MARKUP, NULL, "[img=%s 0 10 0 0 0 0 0 0 0 0 0 50 45]", facename);
165  efree(facename);
166  }
167  }
168 }
169 
171 static int text_anchor_handle(const char *anchor_action, const char *buf, size_t len, void *custom_data)
172 {
173  if (strcmp(anchor_action, "charname") == 0) {
174  packet_struct *packet;
175 
176  packet = packet_new(SERVER_CMD_ACCOUNT, 64, 64);
177  packet_append_uint8(packet, CMD_ACCOUNT_LOGIN_CHAR);
178  packet_append_string_terminated(packet, buf);
179  socket_send_packet(packet);
180 
181  cpl.state = ST_WAITFORPLAY;
182 
183  return 1;
184  }
185 
186  return 0;
187 }
188 
190 static void list_handle_enter(list_struct *list, SDL_Event *event)
191 {
192  if (list->row_selected) {
193  text_info_struct info;
194 
195  text_anchor_parse(&info, list->text[list->row_selected - 1][1]);
197  text_anchor_execute(&info, NULL);
199  }
200 }
201 
203 static int popup_draw(popup_struct *popup)
204 {
205  SDL_Rect box;
206  char timebuf[MAX_BUF];
207  size_t i;
208 
209  box.w = popup->surface->w;
210  box.h = 38;
211  text_show_shadow_format(popup->surface, FONT_SERIF14, 0, 0, COLOR_HGOLD, COLOR_BLACK, TEXT_ALIGN_CENTER | TEXT_VALIGN_CENTER, &box, "Welcome, %s", cpl.account);
212 
213  /* Waiting to log in. */
214  if (cpl.state == ST_WAITFORPLAY) {
215  box.w = popup->surface->w;
216  box.h = popup->surface->h;
217  text_show_shadow(popup->surface, FONT_SERIF12, "Logging in, please wait...", 0, 0, COLOR_HGOLD, COLOR_BLACK, TEXT_ALIGN_CENTER | TEXT_VALIGN_CENTER, &box);
218  return 1;
219  } else if (cpl.state == ST_PLAY || cpl.state < ST_STARTCONNECT) {
220  return 0;
221  }
222 
223  textwin_show(popup->surface, 265, 45, 220, 132);
224 
225  box.w = 220;
226  box.h = 80;
227  strftime(timebuf, sizeof(timebuf), "%a %b %d %H:%M:%S %Y", localtime(&cpl.last_time));
228  text_show_shadow_format(popup->surface, FONT_ARIAL11, 265, 190, COLOR_WHITE, COLOR_BLACK, TEXT_MARKUP | TEXT_WORD_WRAP, &box, "Your IP: [b]%s[/b]\nYou last logged in from [b]%s[/b] at %s.", cpl.host, cpl.last_host, timebuf);
229 
230  button_set_parent(&button_tab_characters, popup->x, popup->y);
231  button_set_parent(&button_tab_new, popup->x, popup->y);
232  button_set_parent(&button_tab_password, popup->x, popup->y);
233  button_set_parent(&button_character_male, popup->x, popup->y);
234  button_set_parent(&button_character_female, popup->x, popup->y);
235  button_set_parent(&button_character_left, popup->x, popup->y);
236  button_set_parent(&button_character_right, popup->x, popup->y);
237  button_set_parent(&button_login, popup->x, popup->y);
238  button_set_parent(&button_done, popup->x, popup->y);
239 
240  button_tab_characters.x = 38;
241  button_tab_characters.y = 38;
242  button_show(&button_tab_characters, "Characters");
243  button_tab_new.x = button_tab_characters.x + texture_surface(button_tab_characters.texture)->w + 1;
244  button_tab_new.y = button_tab_characters.y;
245  button_show(&button_tab_new, "New");
246  button_tab_password.x = button_tab_new.x + texture_surface(button_tab_new.texture)->w + 1;
247  button_tab_password.y = button_tab_new.y;
248  button_show(&button_tab_password, "Password");
249 
250  for (i = 0; i < TEXT_INPUT_NUM; i++) {
251  text_input_set_parent(&text_inputs[i], popup->x, popup->y);
252  }
253 
254  if (button_tab_characters.pressed_forced) {
255  list_set_parent(list_characters, popup->x, popup->y);
256  list_show(list_characters, 36, 50);
257 
258  button_login.x = list_characters->x + LIST_WIDTH_FULL(list_characters) / 2 - texture_surface(button_login.texture)->w / 2;
259  button_login.y = list_characters->y + LIST_HEIGHT_FULL(list_characters) + 8;
260  button_show(&button_login, "[b]Login[/b]");
261  } else if (button_tab_new.pressed_forced) {
262  int max_width, width;
263 
264  max_width = 0;
265 
266  for (i = 0; i < s_settings->num_characters; i++) {
267  width = text_get_width(FONT_SERIF12, s_settings->characters[i].name, 0);
268 
269  if (width > max_width) {
270  max_width = width;
271  }
272  }
273 
274  text_show_format(popup->surface, FONT_ARIAL10, 38, 90, COLOR_WHITE, TEXT_MARKUP, NULL, "[bar=#202020 50 60][icon=%s 50 60][border=#909090 50 60]", s_settings->characters[character_race].gender_faces[character_gender]);
275 
276  button_character_left.x = 100;
277  button_character_left.y = 90;
278  button_show(&button_character_left, "<");
279 
280  box.w = max_width;
281  box.h = 0;
282  text_show_shadow(popup->surface, FONT_SERIF12, s_settings->characters[character_race].name, button_character_left.x + texture_surface(button_character_left.texture)->w + 5, button_character_left.y, COLOR_HGOLD, COLOR_BLACK, TEXT_ALIGN_CENTER, &box);
283 
284  button_character_right.x = button_character_left.x + texture_surface(button_character_left.texture)->w + 5 + max_width + 5;
285  button_character_right.y = 90;
286  button_show(&button_character_right, ">");
287 
288  button_character_male.x = button_character_left.x;
289  button_character_male.y = button_character_left.y + 30;
290  button_show(&button_character_male, "Male");
291 
292  button_character_female.x = button_character_male.x + texture_surface(button_character_female.texture)->w + 5;
293  button_character_female.y = button_character_male.y;
294  button_show(&button_character_female, "Female");
295 
296  box.w = text_inputs[TEXT_INPUT_CHARNAME].coords.w;
297  text_show(popup->surface, FONT_ARIAL12, "Character name &lsqb;[tooltip=Enter your character's name.][h=#"COLOR_HGOLD "]?[/h][/tooltip]&rsqb;", 50, 172, COLOR_WHITE, TEXT_MARKUP | TEXT_ALIGN_CENTER, &box);
298  text_input_show(&text_inputs[TEXT_INPUT_CHARNAME], popup->surface, 50, 190);
299 
300  button_done.x = text_inputs[TEXT_INPUT_CHARNAME].coords.x + text_inputs[TEXT_INPUT_CHARNAME].coords.w - texture_surface(button_done.texture)->w;
301  button_done.y = 210;
302  button_show(&button_done, "Done");
303  } else if (button_tab_password.pressed_forced) {
304  box.w = text_inputs[TEXT_INPUT_PASSWORD].coords.w;
305  text_show(popup->surface, FONT_ARIAL12, "Current password &lsqb;[tooltip=Enter your current password.][h=#"COLOR_HGOLD "]?[/h][/tooltip]&rsqb;", 50, 92, COLOR_WHITE, TEXT_MARKUP | TEXT_ALIGN_CENTER, &box);
306  text_show(popup->surface, FONT_ARIAL12, "New password &lsqb;[tooltip=Enter your new password.][h=#"COLOR_HGOLD "]?[/h][/tooltip]&rsqb;", 50, 132, COLOR_WHITE, TEXT_MARKUP | TEXT_ALIGN_CENTER, &box);
307  text_show(popup->surface, FONT_ARIAL12, "Verify new password &lsqb;[tooltip=Enter your new password again.][h=#"COLOR_HGOLD "]?[/h][/tooltip]&rsqb;", 50, 172, COLOR_WHITE, TEXT_MARKUP | TEXT_ALIGN_CENTER, &box);
308 
309  text_input_show(&text_inputs[TEXT_INPUT_PASSWORD], popup->surface, 50, 110);
310  text_input_show(&text_inputs[TEXT_INPUT_PASSWORD_NEW], popup->surface, 50, 150);
311  text_input_show(&text_inputs[TEXT_INPUT_PASSWORD_NEW2], popup->surface, 50, 190);
312 
313  button_done.x = text_inputs[TEXT_INPUT_PASSWORD_NEW2].coords.x + text_inputs[TEXT_INPUT_CHARNAME].coords.w - texture_surface(button_done.texture)->w;
314  button_done.y = 210;
315  button_show(&button_done, "Done");
316  }
317 
318  return 1;
319 }
320 
322 static int popup_event(popup_struct *popup, SDL_Event *event)
323 {
324  size_t i;
325 
326  if (event->type == SDL_KEYDOWN) {
327  if (event->key.keysym.sym == SDLK_ESCAPE) {
328  popup_destroy(popup);
329  return 1;
330  }
331  }
332 
333  if (button_event(&button_tab_characters, event)) {
334  button_tab_switch(&button_tab_characters);
335  return 1;
336  } else if (button_event(&button_tab_new, event)) {
337  button_tab_switch(&button_tab_new);
338  return 1;
339  } else if (button_event(&button_tab_password, event)) {
340  button_tab_switch(&button_tab_password);
341  return 1;
342  } else if (button_event(button_tab_characters.pressed_forced ? &button_login : &button_done, event)) {
343  event_push_key_once(SDLK_RETURN, 0);
344  return 1;
345  }
346 
347  if (button_tab_characters.pressed_forced) {
348  if (list_handle_keyboard(list_characters, event) || list_handle_mouse(list_characters, event)) {
349  return 1;
350  }
351  } else if (button_tab_new.pressed_forced) {
352  if (event->type == SDL_KEYDOWN) {
353  if (IS_ENTER(event->key.keysym.sym)) {
354  uint32_t lower, upper;
355  packet_struct *packet;
356 
357  if (*text_inputs[TEXT_INPUT_CHARNAME].str == '\0') {
358  draw_info(COLOR_RED, "You must enter a character name.");
359  return 1;
360  } else if (sscanf(s_settings->text[SERVER_TEXT_ALLOWED_CHARS_CHARNAME_MAX], "%u-%u", &lower, &upper) == 2 && (text_inputs[TEXT_INPUT_CHARNAME].num < lower || text_inputs[TEXT_INPUT_CHARNAME].num > upper)) {
361  draw_info_format(COLOR_RED, "Character name must be between %d and %d characters long.", lower, upper);
362  return 1;
363  }
364 
365  packet = packet_new(SERVER_CMD_ACCOUNT, 64, 64);
366  packet_append_uint8(packet, CMD_ACCOUNT_NEW_CHAR);
367  packet_append_string_terminated(packet, text_inputs[TEXT_INPUT_CHARNAME].str);
368  packet_append_string_terminated(packet, s_settings->characters[character_race].gender_archetypes[character_gender]);
369  socket_send_packet(packet);
370 
371  text_input_reset(&text_inputs[TEXT_INPUT_CHARNAME]);
372 
373  return 1;
374  }
375  }
376 
377  if (text_input_event(&text_inputs[TEXT_INPUT_CHARNAME], event)) {
378  return 1;
379  } else if (button_event(&button_character_male, event)) {
381  return 1;
382  } else if (button_event(&button_character_female, event)) {
384  return 1;
385  } else if (button_event(&button_character_left, event)) {
386  if (character_race == 0) {
388  } else {
389  character_race--;
390  }
391  } else if (button_event(&button_character_right, event)) {
393  character_race = 0;
394  } else {
395  character_race++;
396  }
397  }
398  } else if (button_tab_password.pressed_forced) {
399  if (event->type == SDL_KEYDOWN) {
400  if (IS_NEXT(event->key.keysym.sym)) {
401  if (IS_ENTER(event->key.keysym.sym) && text_input_current == TEXT_INPUT_PASSWORD_NEW2) {
402  packet_struct *packet;
403  uint32_t lower, upper;
404 
405  if (strcmp(text_inputs[TEXT_INPUT_PASSWORD_NEW].str, text_inputs[TEXT_INPUT_PASSWORD_NEW2].str) != 0) {
406  draw_info(COLOR_RED, "The new passwords do not match.");
407  return 1;
408  }
409 
410  packet = packet_new(SERVER_CMD_ACCOUNT, 64, 64);
411  packet_append_uint8(packet, CMD_ACCOUNT_PSWD);
412 
413  for (i = TEXT_INPUT_PASSWORD; i < TEXT_INPUT_NUM; i++) {
414  if (*text_inputs[i].str == '\0') {
415  draw_info(COLOR_RED, "You must enter a valid value for all text inputs.");
416  packet_free(packet);
417  return 1;
418  } else if (sscanf(s_settings->text[SERVER_TEXT_ALLOWED_CHARS_PASSWORD_MAX], "%u-%u", &lower, &upper) == 2 && (text_inputs[i].num < lower || text_inputs[i].num > upper)) {
419  draw_info_format(COLOR_RED, "Password must be between %d and %d characters long.", lower, upper);
420  packet_free(packet);
421  return 1;
422  }
423 
424  packet_append_string_terminated(packet, text_inputs[i].str);
425  }
426 
427  socket_send_packet(packet);
428 
429  for (i = TEXT_INPUT_PASSWORD; i < TEXT_INPUT_NUM; i++) {
430  text_input_reset(&text_inputs[i]);
431  text_inputs[i].focus = 0;
432  }
433 
434  text_input_current = TEXT_INPUT_PASSWORD;
435  text_inputs[text_input_current].focus = 1;
436 
437  return 1;
438  }
439 
440  text_inputs[text_input_current].focus = 0;
442 
443  if (text_input_current > TEXT_INPUT_PASSWORD_NEW2) {
444  text_input_current = TEXT_INPUT_PASSWORD;
445  }
446 
447  text_inputs[text_input_current].focus = 1;
448 
449  return 1;
450  }
451  } else if (event->type == SDL_MOUSEBUTTONDOWN) {
452  if (event->button.button == SDL_BUTTON_LEFT) {
453  for (i = TEXT_INPUT_PASSWORD; i < TEXT_INPUT_NUM; i++) {
454  if (text_input_mouse_over(&text_inputs[i], event->motion.x, event->motion.y)) {
455  text_inputs[text_input_current].focus = 0;
456  text_input_current = i;
457  text_inputs[text_input_current].focus = 1;
458  return 1;
459  }
460  }
461  }
462  }
463 
464  if (text_input_event(&text_inputs[text_input_current], event)) {
465  return 1;
466  }
467  }
468 
469  return -1;
470 }
471 
474 {
475  list_remove(list_characters);
476 
477  if (cpl.state != ST_PLAY) {
478  cpl.state = ST_START;
479  }
480 
481  button_destroy(&button_tab_characters);
482  button_destroy(&button_tab_new);
483  button_destroy(&button_tab_password);
484  button_destroy(&button_character_male);
485  button_destroy(&button_character_female);
486  button_destroy(&button_character_left);
487  button_destroy(&button_character_right);
488  button_destroy(&button_done);
489  button_destroy(&button_login);
490 
491  for (size_t i = 0; i < TEXT_INPUT_NUM; i++) {
492  text_input_destroy(&text_inputs[i]);
493  }
494 
495  return 1;
496 }
497 
501 void characters_open(void)
502 {
503  popup_struct *popup;
504  size_t i;
505 
506  progress_dots_create(&progress);
507 
508  popup = popup_create(texture_get(TEXTURE_TYPE_CLIENT, "popup"));
509  popup->draw_func = popup_draw;
510  popup->event_func = popup_event;
512 
513  button_create(&button_tab_characters);
514  button_create(&button_tab_new);
515  button_create(&button_tab_password);
516  button_create(&button_character_male);
517  button_create(&button_character_female);
518  button_create(&button_character_left);
519  button_create(&button_character_right);
520  button_create(&button_done);
521  button_create(&button_login);
522  button_tab_characters.pressed_forced = 1;
523  button_tab_characters.surface = button_tab_new.surface = button_tab_password.surface = button_character_male.surface = button_character_female.surface = button_character_left.surface = button_character_right.surface = button_login.surface = button_done.surface = popup->surface;
524  button_tab_characters.texture = button_tab_new.texture = button_tab_password.texture = texture_get(TEXTURE_TYPE_CLIENT, "button_tab");
525  button_tab_characters.texture_over = button_tab_new.texture_over = button_tab_password.texture_over = texture_get(TEXTURE_TYPE_CLIENT, "button_tab_over");
526  button_tab_characters.texture_pressed = button_tab_new.texture_pressed = button_tab_password.texture_pressed = texture_get(TEXTURE_TYPE_CLIENT, "button_tab_down");
527  button_character_left.texture = button_character_right.texture = texture_get(TEXTURE_TYPE_CLIENT, "button_round");
528  button_character_left.texture_over = button_character_right.texture_over = texture_get(TEXTURE_TYPE_CLIENT, "button_round_over");
529  button_character_left.texture_pressed = button_character_right.texture_pressed = texture_get(TEXTURE_TYPE_CLIENT, "button_round_down");
530  button_login.texture = texture_get(TEXTURE_TYPE_CLIENT, "button_large");
531  button_login.texture_over = texture_get(TEXTURE_TYPE_CLIENT, "button_large_over");
532  button_login.texture_pressed = texture_get(TEXTURE_TYPE_CLIENT, "button_large_down");
533  button_login.flags = TEXT_MARKUP;
534  button_set_font(&button_login, FONT_SERIF14);
535 
536  for (i = 0; i < TEXT_INPUT_NUM; i++) {
537  text_input_create(&text_inputs[i]);
538  text_inputs[i].character_check_func = text_input_character_check;
539  text_inputs[i].coords.w = 150;
540  text_inputs[i].focus = 0;
541 
542  if (i != TEXT_INPUT_CHARNAME) {
543  text_inputs[i].show_edit_func = text_input_show_edit_password;
544  }
545  }
546 
547  list_characters = list_create(3, 2, 8);
548  list_characters->handle_enter_func = list_handle_enter;
549  list_characters->text_color_hook = list_text_color;
550  list_characters->post_column_func = list_post_column;
551  list_characters->surface = popup->surface;
552  list_characters->row_height_adjust = 45;
553  list_characters->text_flags = TEXT_MARKUP;
554  list_set_column(list_characters, 0, 55, 0, NULL, -1);
555  list_set_column(list_characters, 1, 150, 0, NULL, -1);
556  list_scrollbar_enable(list_characters);
557 }
558 
569 static int archname_to_character(const char *archname, size_t *race, size_t *gender)
570 {
571  for (*race = 0; *race < s_settings->num_characters; (*race)++) {
572  for (*gender = 0; *gender < GENDER_MAX; (*gender)++) {
573  if (s_settings->characters[*race].gender_archetypes[*gender] && strcmp(s_settings->characters[*race].gender_archetypes[*gender], archname) == 0) {
574  return 1;
575  }
576  }
577  }
578 
579  return 0;
580 }
581 
583 void socket_command_characters(uint8_t *data, size_t len, size_t pos)
584 {
585  char archname[MAX_BUF], name[MAX_BUF], region_name[MAX_BUF], buf[MAX_BUF], race_gender[MAX_BUF];
586  uint16_t anim_id;
587  uint8_t level;
588  size_t race, gender;
589 
590  if (len == pos) {
591  cpl.state = ST_LOGIN;
592  clioption_settings.reconnect = 0;
593  return;
594  }
595 
596  if (cpl.state != ST_CHARACTERS) {
597  characters_open();
598  cpl.state = ST_CHARACTERS;
599  }
600 
601  list_clear(list_characters);
602 
603  packet_to_string(data, len, &pos, cpl.account, sizeof(cpl.account));
604  packet_to_string(data, len, &pos, cpl.host, sizeof(cpl.host));
605  packet_to_string(data, len, &pos, cpl.last_host, sizeof(cpl.last_host));
606  cpl.last_time = datetime_utctolocal(packet_to_uint64(data, len, &pos));
607 
608  while (pos < len) {
609  packet_to_string(data, len, &pos, archname, sizeof(archname));
610  packet_to_string(data, len, &pos, name, sizeof(name));
611  packet_to_string(data, len, &pos, region_name, sizeof(region_name));
612  anim_id = packet_to_uint16(data, len, &pos);
613  level = packet_to_uint8(data, len, &pos);
614 
615  /* If it's a valid player arch, add race and gender information. */
616  if (archname_to_character(archname, &race, &gender)) {
617  snprintf(race_gender, sizeof(race_gender), "%s %s\n", s_settings->characters[race].name, gender_noun[gender]);
618  } else {
619  *race_gender = '\0';
620  }
621 
622  /* If we have specified a character in '--connect' command line
623  * option, update the selected row and create an enter event. */
624  if ((string_isempty(clioption_settings.connect[0]) || strcasecmp(selected_server->name, clioption_settings.connect[0]) == 0) &&
625  clioption_settings.connect[3] && (strcasecmp(clioption_settings.connect[3], name) == 0 ||
626  (string_isdigit(clioption_settings.connect[3]) && (uint32_t) atoi(clioption_settings.connect[3]) == list_characters->rows + 1))) {
627  list_characters->row_selected = list_characters->rows + 1;
628 
629  if (!clioption_settings.reconnect) {
630  efree(clioption_settings.connect[3]);
631  clioption_settings.connect[3] = NULL;
632  }
633 
634  event_push_key_once(SDLK_RETURN, 0);
635  }
636 
637  snprintf(buf, sizeof(buf), "%d", anim_id);
638  list_add(list_characters, list_characters->rows, 0, buf);
639  snprintf(buf, sizeof(buf), "[y=3][a=charname][c=#"COLOR_HGOLD "][font=serif 12]%s[/font][/c][/a]\n[y=2]%sLevel: %d\n%s", name, race_gender, level, region_name);
640  list_add(list_characters, list_characters->rows - 1, 1, buf);
641  }
642 
643  /* No characters yet, so switch to the character creation tab. */
644  if (list_characters->rows == 0) {
645  button_tab_switch(&button_tab_new);
646  }/* Characters tab otherwise. */
647  else {
648  button_tab_switch(&button_tab_characters);
649  }
650 }
#define GENDER_MALE
Definition: player.h:40
void characters_open(void)
Definition: characters.c:501
list_struct * list_create(uint32_t max_rows, uint32_t cols, int spacing)
Definition: list.c:113
server_struct * selected_server
Definition: main.c:54
#define COLOR_BLACK
Definition: text.h:323
void textwin_show(SDL_Surface *surface, int x, int y, int w, int h)
Definition: textwin.c:523
#define TEXT_WORD_WRAP
Definition: text.h:226
Definition: main.h:332
static uint32_t ticks
Definition: range_buttons.c:38
int(* event_func)(struct popup_struct *popup, SDL_Event *event)
Definition: popup.h:148
int(* draw_func)(struct popup_struct *popup)
Definition: popup.h:127
static void button_tab_switch(button_struct *button)
Definition: characters.c:80
Definition: main.h:252
char last_host[MAX_BUF]
Definition: player.h:242
void draw_info(const char *color, const char *str)
Definition: textwin.c:448
static int popup_event(popup_struct *popup, SDL_Event *event)
Definition: characters.c:322
const char * gender_noun[GENDER_MAX]
Definition: player.c:43
static size_t text_input_current
Definition: characters.c:65
void list_remove(list_struct *list)
Definition: list.c:522
SDL_Surface * texture_surface(texture_struct *texture)
Definition: texture.c:303
int x
Definition: list.h:36
char * text[SERVER_TEXT_MAX]
static button_struct button_tab_characters
Definition: characters.c:53
int button_event(button_struct *button, SDL_Event *event)
Definition: button.c:222
void check_animation_status(int anum)
Definition: client.c:150
void list_clear(list_struct *list)
Definition: list.c:486
void list_set_column(list_struct *list, uint32_t col, int width, int spacing, const char *name, int centered)
Definition: list.c:242
int(* destroy_callback_func)(struct popup_struct *popup)
Definition: popup.h:159
SDL_Rect coords
Definition: text_input.h:96
void button_destroy(button_struct *button)
Definition: button.c:94
int list_handle_mouse(list_struct *list, SDL_Event *event)
Definition: list.c:698
void list_add(list_struct *list, uint32_t row, uint32_t col, const char *str)
Definition: list.c:163
void text_show(SDL_Surface *surface, font_struct *font, const char *text, int x, int y, const char *color_notation, uint64_t flags, SDL_Rect *box)
Definition: text.c:1983
static void list_handle_enter(list_struct *list, SDL_Event *event)
Definition: characters.c:190
Definition: main.h:312
#define LIST_ROWS_START(list)
Definition: list.h:258
static void list_post_column(list_struct *list, uint32_t row, uint32_t col)
Definition: characters.c:131
#define GENDER_MAX
Definition: player.h:46
server_settings * s_settings
int x
Definition: popup.h:87
#define COLOR_HGOLD
Definition: text.h:319
int y
Definition: list.h:39
static text_input_struct text_input
Definition: interface.c:59
static int text_input_character_check(text_input_struct *text_input, char c)
Definition: characters.c:110
uint64_t text_flags
Definition: list.h:141
texture_struct * texture_get(texture_type_t type, const char *name)
Definition: texture.c:279
char *** text
Definition: list.h:85
void progress_dots_create(progress_dots *progress)
Definition: progress.c:39
#define TEXT_MARKUP
Definition: text.h:224
#define LIST_ROW_HEIGHT(list)
Definition: list.h:256
#define TEXT_ALIGN_CENTER
Definition: text.h:230
static size_t character_race
Definition: characters.c:69
#define COLOR_RED
Definition: text.h:295
char account[MAX_BUF]
Definition: player.h:227
#define LIST_ROW_OFFSET(row, list)
Definition: list.h:267
void socket_command_characters(uint8_t *data, size_t len, size_t pos)
Definition: characters.c:583
void image_request_face(int pnum)
Definition: image.c:402
void text_show_shadow_format(SDL_Surface *surface, font_struct *font, int x, int y, const char *color_notation, const char *color_shadow_notation, uint64_t flags, SDL_Rect *box, const char *format,...)
Definition: text.c:2305
char * name
Definition: main.h:65
static text_input_struct text_inputs[TEXT_INPUT_NUM]
Definition: characters.c:57
Client_Player cpl
Definition: client.c:50
_face_struct FaceList[MAX_FACE_TILES]
Definition: main.c:77
void text_set_anchor_handle(text_anchor_handle_func func)
Definition: text.c:437
void button_set_font(button_struct *button, font_struct *font)
Definition: button.c:114
texture_struct * texture_over
Definition: button.h:67
#define LIST_WIDTH_FULL(list)
Definition: list.h:276
void button_create(button_struct *button)
Definition: button.c:65
char * gender_archetypes[GENDER_MAX]
void(* text_color_hook)(struct list_struct *list, uint32_t row, uint32_t col, const char **color, const char **color_shadow)
Definition: list.h:228
static int text_anchor_handle(const char *anchor_action, const char *buf, size_t len, void *custom_data)
Definition: characters.c:171
void text_show_shadow(SDL_Surface *surface, font_struct *font, const char *text, int x, int y, const char *color_notation, const char *color_shadow_notation, uint64_t flags, SDL_Rect *box)
Definition: text.c:2278
static void list_text_color(list_struct *list, uint32_t row, uint32_t col, const char **color, const char **color_shadow)
Definition: characters.c:122
void text_anchor_execute(text_info_struct *info, void *custom_data)
Definition: text.c:632
time_t last_time
Definition: player.h:247
static int popup_draw(popup_struct *popup)
Definition: characters.c:203
char host[MAX_BUF]
Definition: player.h:237
static list_struct * list_characters
Definition: characters.c:61
texture_struct * texture
Definition: button.h:61
#define GENDER_FEMALE
Definition: player.h:42
void(* post_column_func)(struct list_struct *list, uint32_t row, uint32_t col)
Definition: list.h:239
void text_anchor_parse(text_info_struct *info, const char *text)
Definition: text.c:2471
static int archname_to_character(const char *archname, size_t *race, size_t *gender)
Definition: characters.c:569
char * gender_faces[GENDER_MAX]
#define LIST_HEIGHT_FULL(list)
Definition: list.h:271
int list_handle_keyboard(list_struct *list, SDL_Event *event)
Definition: list.c:622
SDL_Surface * surface
Definition: list.h:138
int y
Definition: popup.h:90
SDL_Surface * surface
Definition: button.h:46
texture_struct * texture_pressed
Definition: button.h:73
int text_get_width(font_struct *font, const char *text, uint64_t flags)
Definition: text.c:2328
void list_show(list_struct *list, int x, int y)
Definition: list.c:337
clioption_settings_struct clioption_settings
Definition: main.c:87
int16_t row_height_adjust
Definition: list.h:88
void list_set_parent(list_struct *list, int px, int py)
Definition: list.c:96
char_struct * characters
SDL_Surface * surface
Definition: popup.h:65
static size_t character_gender
Definition: characters.c:73
void text_show_format(SDL_Surface *surface, font_struct *font, int x, int y, const char *color_notation, uint64_t flags, SDL_Rect *box, const char *format,...)
Definition: text.c:2289
void draw_info_format(const char *color, const char *format,...)
Definition: textwin.c:429
void list_scrollbar_enable(list_struct *list)
Definition: list.c:302
void button_show(button_struct *button, const char *text)
Definition: button.c:161
#define TEXT_VALIGN_CENTER
Definition: text.h:246
static progress_dots progress
Definition: characters.c:49
uint64_t flags
Definition: button.h:79
uint32_t rows
Definition: list.h:60
void(* handle_enter_func)(struct list_struct *list, SDL_Event *event)
Definition: list.h:201
static int popup_destroy_callback(popup_struct *popup)
Definition: characters.c:473
void text_input_destroy(text_input_struct *text_input)
Definition: text_input.c:96
uint32_t row_selected
Definition: list.h:109
#define COLOR_WHITE
Definition: text.h:289
int pressed_forced
Definition: button.h:108