Crossfire Client, Branches  R11627
stats.c
Go to the documentation of this file.
1 const char * const rcsid_gtk2_stats_c =
2  "$Id: stats.c 9201 2008-06-01 17:32:45Z anmaster $";
3 /*
4  Crossfire client, a client program for the crossfire program.
5 
6  Copyright (C) 2005-2007 Mark Wedel & Crossfire Development Team
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 via e-mail to crossfire@metalforge.org
23 */
24 
30 #ifdef HAVE_CONFIG_H
31 # include <config.h>
32 #endif
33 
34 #include <assert.h>
35 #include <gtk/gtk.h>
36 #include <glade/glade.h>
37 
38 #include "client.h"
39 
40 #include "main.h"
41 
42 #define STAT_BAR_HP 0
43 #define STAT_BAR_SP 1
44 #define STAT_BAR_GRACE 2
45 #define STAT_BAR_FOOD 3
46 #define STAT_BAR_EXP 4
47 #define MAX_STAT_BARS 5
48 static const char * const stat_bar_names[MAX_STAT_BARS] = {
49  "hp", "sp", "grace", "food", "exp"
50 };
51 
53 
54 #define STYLE_NORMAL 0
55 #define STYLE_LOW 1
56 #define STYLE_SUPER 2
57 #define STYLE_GRAD_NORMAL 3
58 #define STYLE_GRAD_LOW 4
59 #define STYLE_GRAD_SUPER 5
60 #define NUM_STYLES 6
61 
62 /* The name of the symbolic widget names we try to look up
63  * the styles of (these will be prefixed with hp_, sp_, etc).
64  * This should always match NUM_STYLES.
65  */
66 static const char * const stat_style_names[NUM_STYLES] = {
67  "bar_normal", "bar_low", "bar_super",
68  "gradual_bar_normal", "gradual_bar_low", "gradual_bar_super"
69 };
70 
71 /* We really only care about the colors, as there isn't
72  * anything else we can change about the progressbar widget
73  * itself.
74  */
76 
77 
78 /* The table for showing skill exp is an x & y grid. Note
79  * for proper formatting, SKILL_BOXES_X must be even.
80  * Hmmm - perhaps these should instead be dynamically
81  * generated?
82  */
83 #define SKILL_BOXES_X 6
84 #define SKILL_BOXES_Y 17
85 
86 #define PROTECTION_BOXES_X 6
87 #define PROTECTION_BOXES_Y 6
88 
89 typedef struct {
90  GtkWidget *playername;
91  GtkWidget *Str;
92  GtkWidget *Dex;
93  GtkWidget *Con;
94  GtkWidget *Int;
95  GtkWidget *Wis;
96  GtkWidget *Cha;
97  GtkWidget *Pow;
98  GtkWidget *wc;
99  GtkWidget *dam;
100  GtkWidget *ac;
101  GtkWidget *armor;
102  GtkWidget *speed;
103  GtkWidget *weapon_speed;
104  GtkWidget *range;
105  GtkWidget *exp;
106  GtkWidget *level;
107  GtkWidget *table_skills_exp;
108  GtkWidget *table_protections;
109  GtkWidget *skill_exp[SKILL_BOXES_X * SKILL_BOXES_Y];
111 
112 } StatWindow;
113 
115 
116 /* The basic idea of this structure is there are some lists of
117  * names we get from the server (like say skill names) -
118  * internally, these are all referred to number, and the numbers
119  * are not in any order. The idea here is we can store away the
120  * names, and then can display the items in the window in
121  * nice alphabetical order instead of the random order they
122  * normally show up in.
123  */
124 typedef struct {
125  const char *name;
126  int value;
127 } NameMapping;
128 
130 
132 
134 
140 {
141  static int has_init=0;
142  int stat_bar, sub_style;
143  char buf[MAX_BUF];
144  GtkStyle *tmp_style;
145 
146  if (!has_init) {
147  memset(bar_colors, 0, sizeof(GdkColor*) * MAX_STAT_BARS * NUM_STYLES);
148  }
149 
150  for (stat_bar=0; stat_bar < MAX_STAT_BARS; stat_bar++) {
151  for (sub_style=0; sub_style < NUM_STYLES; sub_style++) {
152  snprintf(buf, sizeof(buf), "%s_%s", stat_bar_names[stat_bar], stat_style_names[sub_style]);
153 
154  tmp_style = gtk_rc_get_style_by_paths(gtk_settings_get_default(), NULL, buf, G_TYPE_NONE);
155 
156  if (!tmp_style) {
157  if (bar_colors[stat_bar][sub_style]) {
158  free(bar_colors[stat_bar][sub_style]);
159  bar_colors[stat_bar][sub_style] = NULL;
160  }
161  LOG(LOG_INFO, "stats.c::stats_get_styles()", "Unable to find style '%s'", buf);
162  } else {
163  if (!bar_colors[stat_bar][sub_style])
164  bar_colors[stat_bar][sub_style] = calloc(1, sizeof(GdkColor));
165  memcpy(bar_colors[stat_bar][sub_style],
166  &tmp_style->base[GTK_STATE_SELECTED], sizeof(GdkColor));
167  }
168  }
169  }
170 }
171 
176 void stats_init(GtkWidget *window_root)
177 {
178  int i, x, y;
179  char buf[MAX_BUF];
180  GladeXML *xml_tree;
181 
182  xml_tree = glade_get_widget_tree(GTK_WIDGET(window_root));
183  for (i=0; i<MAX_STAT_BARS; i++) {
184  snprintf(buf, sizeof(buf), "label_stats_%s", stat_bar_names[i]);
185  stat_label[i] = glade_xml_get_widget(xml_tree, buf);
186 
187  snprintf(buf, sizeof(buf), "progressbar_%s", stat_bar_names[i]);
188  stat_bar[i] = glade_xml_get_widget(xml_tree, buf);
189 
190  lastval[i] = -1;
191  lastmax[i] = -1;
192  }
193 
194  statwindow.playername =
195  glade_xml_get_widget(xml_tree, "label_playername");
196  statwindow.Str =
197  glade_xml_get_widget(xml_tree, "label_str");
198  statwindow.Dex =
199  glade_xml_get_widget(xml_tree, "label_dex");
200  statwindow.Con =
201  glade_xml_get_widget(xml_tree, "label_con");
202  statwindow.Int =
203  glade_xml_get_widget(xml_tree, "label_int");
204  statwindow.Wis =
205  glade_xml_get_widget(xml_tree, "label_wis");
206  statwindow.Pow =
207  glade_xml_get_widget(xml_tree, "label_pow");
208  statwindow.Cha =
209  glade_xml_get_widget(xml_tree, "label_cha");
210  statwindow.wc =
211  glade_xml_get_widget(xml_tree, "label_wc");
212  statwindow.dam =
213  glade_xml_get_widget(xml_tree, "label_dam");
214  statwindow.ac =
215  glade_xml_get_widget(xml_tree, "label_ac");
216  statwindow.armor =
217  glade_xml_get_widget(xml_tree, "label_armor");
218  statwindow.speed =
219  glade_xml_get_widget(xml_tree, "label_speed");
220  statwindow.weapon_speed =
221  glade_xml_get_widget(xml_tree, "label_weapon_speed");
222  statwindow.range =
223  glade_xml_get_widget(xml_tree, "label_range");
224  statwindow.exp =
225  glade_xml_get_widget(xml_tree, "label_exp");
226  statwindow.level =
227  glade_xml_get_widget(xml_tree, "label_level");
228 
229  /* Note that the order the labels are attached to the tables determines
230  * the order of display. The order as right now is left to right,
231  * then top to bottom, which means that is the order if displaying
232  * skills & protections.
233  */
234 
235  statwindow.table_skills_exp =
236  glade_xml_get_widget(xml_tree,"table_skills_exp");
237 
238  for (i=0, x=0, y=0; i < SKILL_BOXES_X * SKILL_BOXES_Y; i++) {
239  statwindow.skill_exp[i] = gtk_label_new("");
240  gtk_table_attach(GTK_TABLE(statwindow.table_skills_exp), statwindow.skill_exp[i],
241  x, x+1, y, y+1, GTK_EXPAND, 0, 0, 0);
242  gtk_widget_show(statwindow.skill_exp[i]);
243  x++;
244  if (x == SKILL_BOXES_X) {
245  x=0;
246  y++;
247  }
248  }
249 
250  statwindow.table_protections =
251  glade_xml_get_widget(xml_tree,"table_protections");
252 
253  for (i=0, x=0, y=0; i < PROTECTION_BOXES_X * PROTECTION_BOXES_Y; i++) {
254  statwindow.resists[i] = gtk_label_new("");
255  gtk_table_attach(GTK_TABLE(statwindow.table_protections), statwindow.resists[i],
256  x, x+1, y, y+1, GTK_EXPAND, 0, 0, 0);
257  gtk_widget_show(statwindow.resists[i]);
258  x++;
259  if (x == PROTECTION_BOXES_X) {
260  x=0;
261  y++;
262  }
263  }
265 }
266 
267 
292 void update_stat(int stat_no, sint64 max_stat, sint64 current_stat,
293  sint64 statbar_max, sint64 statbar_stat, const char *name, int can_alert)
294 {
295  float bar;
296  int is_alert;
297  char buf[256];
298  GdkColor ncolor, *set_color=NULL;
299 
300  /* If nothing changed, don't need to do anything */
301  if (lastval[stat_no] == current_stat && lastmax[stat_no] == max_stat)
302  return;
303 
304  lastval[stat_no] = current_stat;
305  lastmax[stat_no] = max_stat;
306 
307  if (statbar_max > 0) bar = (float) statbar_stat / (float) statbar_max;
308  else bar = 0.0;
309 
310  /* Simple check to see if current stat is less than 25% */
311  if (can_alert && current_stat * 4 < max_stat) is_alert=1;
312  else is_alert = 0;
313 
315  /* In this mode, the color of the stat bar were go between red and green
316  * in a gradual style. Color is blended from low to high
317  */
318 
319  GdkColor *hcolor, *lcolor;
320  float diff;
321 
322  /* First thing we do is figure out current values, and thus
323  * what color bases we use (based on super charged or
324  * normal value). We also set up diff as where we are between
325  * those two points. In this way, the blending logic
326  * below is the same regardless of actual value.
327  */
328  if (bar > 1.0) {
329  if (bar>2.0) bar=2.0; /* Doesn't affect display, just our calculations */
330  hcolor = bar_colors[stat_no][STYLE_GRAD_SUPER];
331  lcolor = bar_colors[stat_no][STYLE_GRAD_NORMAL];
332  diff = bar - 1.0;
333  } else {
334  if (bar < 0.0) bar=0.0; /* Like above, doesn't affect display */
335  hcolor = bar_colors[stat_no][STYLE_GRAD_NORMAL];
336  lcolor = bar_colors[stat_no][STYLE_GRAD_LOW];
337  diff = bar;
338  }
339  /* Now time to blend. First, make sure colors are set.
340  * then, we use the lcolor as the base, making adjustments
341  * based on hcolor. Values in hcolor may be lower than
342  * lcolor, but that just means we substract from lcolor, not
343  * add.
344  */
345 
346  if (lcolor && hcolor) {
347 #if 1
348  memcpy(&ncolor, lcolor, sizeof(GdkColor));
349 
350  ncolor.red += (hcolor->red - lcolor->red) * diff;
351  ncolor.green += (hcolor->green - lcolor->green) * diff;
352  ncolor.blue += (hcolor->blue - lcolor->blue) * diff;
353 #else
354  /* This is an alternate coloring method that works
355  * when using saturated colors for the base points.
356  * This mimics the old code, and works good
357  * when using such saturated colors (eg, one
358  * of the RGB triplets being 255, others 0, like
359  * red, green, or blue). However, this doesn't produce
360  * very good results when not using those colors - if
361  * say magenta and yellow are chosen as the two colors,
362  * this code results in the colors basically getting near
363  * white in the middle values. For saturated colors, the
364  * code below would produce nice bright yellow for the middle
365  * values, where as the code above produces more a dark yellow,
366  * since it only takes half the red and half the green.
367  * However, the code above still produces useful results
368  * even with that limitation, and it works for all colors,
369  * so it is the code enabled. It perhaps be
370  * interesting to have some detection logic on how the colors
371  * are actually set - if only a single r/g/b value is set for
372  * the two colors, then use this logic here, otherwise
373  * the above logic or something.
374  * MSW 2007-01-24
375  */
376  if (diff > 0.5) {
377  memcpy(&ncolor, hcolor, sizeof(GdkColor));
378 
379  if (lcolor->red > hcolor->red)
380  ncolor.red = 2 * lcolor->red * (1.0 - diff);
381 
382  if (lcolor->green > hcolor->green)
383  ncolor.green = 2 * lcolor->green * (1.0 - diff);
384 
385  if (lcolor->blue > hcolor->blue)
386  ncolor.blue = 2 * lcolor->blue * (1.0 - diff);
387 
388  } else {
389  memcpy(&ncolor, lcolor, sizeof(GdkColor));
390 
391  if (hcolor->red > lcolor->red)
392  ncolor.red = 2 * hcolor->red * diff;
393 
394  if (hcolor->green > lcolor->green)
395  ncolor.green = 2 * hcolor->green * diff;
396 
397  if (hcolor->blue > lcolor->blue)
398  ncolor.blue = 2 * hcolor->blue * diff;
399  }
400 #endif
401 #if 0
402  fprintf(stderr,"stat %d, val %d, r/g/b=%d/%d/%d\n",
403  stat_no, current_stat, ncolor.red, ncolor.green, ncolor.blue);
404 #endif
405  set_color = &ncolor;
406  }
407  } else {
408  if (statbar_stat * 4 < statbar_max)
409  set_color = bar_colors[stat_no][STYLE_LOW];
410  else if (statbar_stat > statbar_max)
411  set_color = bar_colors[stat_no][STYLE_SUPER];
412  else
413  set_color = bar_colors[stat_no][STYLE_NORMAL];
414  }
415  if (bar > 1.0) bar = 1.0;
416  if (bar < 0.0) bar = 0.0;
417 
418  /* It may be a waste, but we set the color everytime here - it
419  * isn't very costly, and keeps us from tracing the last
420  * color we set.
421  * Note that set_color could be null, which means it reverts
422  * back to normal color.
423  */
424  gtk_widget_modify_base(stat_bar[stat_no], GTK_STATE_SELECTED, set_color);
425 
426  gtk_progress_set_percentage(GTK_PROGRESS(stat_bar[stat_no]), bar);
427  snprintf(buf, sizeof(buf), "%s %"FMT64"/%"FMT64, name, current_stat, max_stat);
428  gtk_label_set(GTK_LABEL(stat_label[stat_no]), buf);
429 
430 }
431 
437 void draw_message_window(int redraw) {
438  static int lastbeep=0;
439  static sint64 level_diff;
440 
442  cpl.stats.maxhp, cpl.stats.hp, "HP:", TRUE);
444  cpl.stats.maxsp, cpl.stats.sp, "Spell Points:", TRUE);
446  cpl.stats.maxgrace, cpl.stats.grace, "Grace:", TRUE);
447  update_stat(3, 999, cpl.stats.food, 999, cpl.stats.food, "Food:", TRUE);
448 
449  /* We may or may not have an exp table from the server. If we don't, just
450  * use current exp value so it will always appear maxed out.
451  */
452  /* We calculate level_diff here just so it makes the update_stat()
453  * call below less messy.
454  */
455  if ((cpl.stats.level+1) < exp_table_max)
456  level_diff = exp_table[cpl.stats.level+1] - exp_table[cpl.stats.level];
457  else
458  level_diff=cpl.stats.exp;
459 
460  update_stat(4,
461  (cpl.stats.level+1) < exp_table_max ? exp_table[cpl.stats.level+1]:cpl.stats.exp,
462  cpl.stats.exp,
463  level_diff,
464  (cpl.stats.level+1) < exp_table_max ?
465  (cpl.stats.exp - exp_table[cpl.stats.level]):cpl.stats.exp,
466  "Exp:", FALSE);
467 
468  if (use_config[CONFIG_FOODBEEP] && (cpl.stats.food%4==3) && (cpl.stats.food < 200)) {
469  gdk_beep( );
470  } else if (use_config[CONFIG_FOODBEEP] && cpl.stats.food == 0 && ++lastbeep == 5) {
471  lastbeep = 0;
472  gdk_beep( );
473  }
474 }
475 
481 {
482  if (!a->name && !b->name) return 0;
483  if (!a->name) return 1;
484  if (!b->name) return -1;
485  else return strcasecmp(a->name, b->name);
486 }
487 
491 static void update_stat_mapping(void)
492 {
493  int i;
494 
495  for (i=0; i < MAX_SKILL; i++) {
496  skill_mapping[i].value=i;
497  if (skill_names[i])
498  skill_mapping[i].name = skill_names[i];
499  else
500  skill_mapping[i].name = NULL;
501  }
502  qsort(skill_mapping, MAX_SKILL, sizeof(NameMapping),
503  (int (*)(const void*, const void*))mapping_sort);
504 
505  for (i=0; i < NUM_RESISTS; i++) {
506  resist_mapping[i].value=i;
507  if (resists_name[i])
508  resist_mapping[i].name = resists_name[i];
509  else
510  resist_mapping[i].name = NULL;
511  }
512  qsort(resist_mapping, NUM_RESISTS, sizeof(NameMapping),
513  (int (*)(const void*, const void*))mapping_sort);
514 
516 }
517 
522 void draw_stats(int redraw) {
523  static Stats last_stats;
524  static char last_name[MAX_BUF]="", last_range[MAX_BUF]="";
525  static int init_before=0, max_drawn_skill=0, max_drawn_resists=0;
526 
527  float weap_sp;
528  char buff[MAX_BUF];
529  int i, on_skill, sk;
530 
531  if (!init_before) {
532  init_before=1;
533  memset(&last_stats, 0, sizeof(Stats));
534  }
535 
536  /* skill_names gets set as part of the initialization with the
537  * client - however, right now, there is no callback when
538  * it is set, so instead, just track that wee need to update
539  * and see if it changes.
540  */
541  if (need_mapping_update && skill_names[1] != NULL) {
543  }
544 
545  if (strcmp(cpl.title, last_name) || redraw) {
546  strcpy(last_name,cpl.title);
547  gtk_label_set (GTK_LABEL(statwindow.playername), cpl.title);
548  }
549 
550  if(redraw || cpl.stats.exp!=last_stats.exp) {
551  last_stats.exp = cpl.stats.exp;
552  snprintf(buff, sizeof(buff), "Experience: %5" FMT64 ,cpl.stats.exp);
553  gtk_label_set (GTK_LABEL(statwindow.exp), buff);
554  }
555 
556  if(redraw || cpl.stats.level!=last_stats.level) {
557  last_stats.level = cpl.stats.level;
558  snprintf(buff, sizeof(buff), "Level: %d",cpl.stats.level);
559  gtk_label_set (GTK_LABEL(statwindow.level), buff);
560  }
561 
562  if(redraw || cpl.stats.Str!=last_stats.Str) {
563  last_stats.Str=cpl.stats.Str;
564  snprintf(buff, sizeof(buff), "%2d",cpl.stats.Str);
565  gtk_label_set (GTK_LABEL(statwindow.Str), buff);
566  }
567 
568  if(redraw || cpl.stats.Dex!=last_stats.Dex) {
569  last_stats.Dex=cpl.stats.Dex;
570  snprintf(buff, sizeof(buff), "%2d",cpl.stats.Dex);
571  gtk_label_set (GTK_LABEL(statwindow.Dex), buff);
572  }
573 
574  if(redraw || cpl.stats.Con!=last_stats.Con) {
575  last_stats.Con=cpl.stats.Con;
576  snprintf(buff, sizeof(buff), "%2d",cpl.stats.Con);
577  gtk_label_set (GTK_LABEL(statwindow.Con), buff);
578  }
579 
580  if(redraw || cpl.stats.Int!=last_stats.Int) {
581  last_stats.Int=cpl.stats.Int;
582  snprintf(buff, sizeof(buff), "%2d",cpl.stats.Int);
583  gtk_label_set (GTK_LABEL(statwindow.Int), buff);
584  }
585 
586  if(redraw || cpl.stats.Wis!=last_stats.Wis) {
587  last_stats.Wis=cpl.stats.Wis;
588  snprintf(buff, sizeof(buff), "%2d",cpl.stats.Wis);
589  gtk_label_set (GTK_LABEL(statwindow.Wis), buff);
590  }
591 
592  if(redraw || cpl.stats.Pow!=last_stats.Pow) {
593  last_stats.Pow=cpl.stats.Pow;
594  snprintf(buff, sizeof(buff), "%2d",cpl.stats.Pow);
595  gtk_label_set (GTK_LABEL(statwindow.Pow), buff);
596  }
597 
598  if(redraw || cpl.stats.Cha!=last_stats.Cha) {
599  last_stats.Cha=cpl.stats.Cha;
600  snprintf(buff, sizeof(buff), "%2d",cpl.stats.Cha);
601  gtk_label_set (GTK_LABEL(statwindow.Cha), buff);
602  }
603 
604  if(redraw || cpl.stats.wc!=last_stats.wc) {
605  last_stats.wc=cpl.stats.wc;
606  snprintf(buff, sizeof(buff), "%3d",cpl.stats.wc);
607  gtk_label_set (GTK_LABEL(statwindow.wc), buff);
608  }
609 
610  if(redraw || cpl.stats.dam!=last_stats.dam) {
611  last_stats.dam=cpl.stats.dam;
612  snprintf(buff, sizeof(buff), "%d",cpl.stats.dam);
613  gtk_label_set (GTK_LABEL(statwindow.dam), buff);
614  }
615 
616  if(redraw || cpl.stats.ac!=last_stats.ac) {
617  last_stats.ac=cpl.stats.ac;
618  snprintf(buff, sizeof(buff), "%d",cpl.stats.ac);
619  gtk_label_set (GTK_LABEL(statwindow.ac), buff);
620  }
621 
622  if(redraw || cpl.stats.resists[0]!=last_stats.resists[0]) {
623  last_stats.resists[0]=cpl.stats.resists[0];
624  snprintf(buff, sizeof(buff), "%d",cpl.stats.resists[0]);
625  gtk_label_set (GTK_LABEL(statwindow.armor), buff);
626  }
627 
628  if (redraw || cpl.stats.speed!=last_stats.speed) {
629  last_stats.speed=cpl.stats.speed;
630  snprintf(buff, sizeof(buff), "%3.2f",(float)cpl.stats.speed/FLOAT_MULTF);
631  gtk_label_set (GTK_LABEL(statwindow.speed), buff);
632  }
633  /* sc_version >= 1029 reports real value of weapon speed -
634  * not as a factor of player speed. Handle accordingly.
635  */
636  if (csocket.sc_version >= 1029)
637  weap_sp = (float) cpl.stats.weapon_sp / FLOAT_MULTF;
638  else
639  weap_sp = (float) cpl.stats.speed/((float)cpl.stats.weapon_sp);
640 
641  if (redraw || weap_sp !=last_stats.weapon_sp) {
642  last_stats.weapon_sp=weap_sp;
643  snprintf(buff, sizeof(buff), "%3.2f",weap_sp);
644  gtk_label_set (GTK_LABEL(statwindow.weapon_speed), buff);
645  }
646 
647  if(redraw || strcmp(cpl.range, last_range)) {
648  strcpy(last_range, cpl.range);
649  snprintf(buff, sizeof(buff), "Range: %s",cpl.range);
650  gtk_label_set (GTK_LABEL(statwindow.range), cpl.range);
651  }
652 
653  on_skill=0;
654  assert(sizeof(statwindow.skill_exp)/sizeof(*statwindow.skill_exp) >= 2*MAX_SKILL);
655  for (i=0; i<MAX_SKILL; i++) {
656  /* Drawing a particular skill entry is tricky - only draw if
657  * different, and only draw if we have a name for the skill
658  * and the player has some exp in the skill - don't draw
659  * all 30 skills for no reason.
660  */
661  sk = skill_mapping[i].value;
662 
663  if ((redraw || cpl.stats.skill_exp[sk] != last_stats.skill_exp[sk]) &&
664  skill_mapping[i].name && cpl.stats.skill_exp[sk]){
665  gtk_label_set(GTK_LABEL(statwindow.skill_exp[on_skill++]), skill_mapping[i].name);
666  snprintf(buff, sizeof(buff), "%" FMT64 " (%d)", cpl.stats.skill_exp[sk], cpl.stats.skill_level[sk]);
667  gtk_label_set(GTK_LABEL(statwindow.skill_exp[on_skill++]), buff);
668  last_stats.skill_level[sk] = cpl.stats.skill_level[sk];
669  last_stats.skill_exp[sk] = cpl.stats.skill_exp[sk];
670  } else if (cpl.stats.skill_exp[sk]) {
671  /* don't need to draw the skill, but need to update the position
672  * of where to draw the next one.
673  */
674  on_skill+=2;
675  }
676  }
677 
678  /* Since the number of skills we draw come and go, basically we want
679  * to erase any extra. This shows up when switching characters, eg, character
680  * #1 knows 10 skills, #2 knows 5 - need to erase those 5 extra.
681  */
682  if (on_skill < max_drawn_skill) {
683  int k;
684 
685  for (k = on_skill; k <= max_drawn_skill; k++)
686  gtk_label_set(GTK_LABEL(statwindow.skill_exp[k]), "");
687  }
688  max_drawn_skill = on_skill;
689 
690  /* Now do the resistance table */
691  if (redraw || cpl.stats.resist_change) {
692  int i,j=0;
693 
695  for (i=0; i<NUM_RESISTS; i++) {
696  sk = resist_mapping[i].value;
697  if (cpl.stats.resists[sk]) {
698  gtk_label_set(GTK_LABEL(statwindow.resists[j]), resist_mapping[i].name);
699  j++;
700  snprintf(buff, sizeof(buff), "%+4d", cpl.stats.resists[sk]);
701  gtk_label_set(GTK_LABEL(statwindow.resists[j]), buff);
702  j++;
703  if (j >= PROTECTION_BOXES_X * PROTECTION_BOXES_Y) break;
704  }
705  }
706  /* Erase old/unused resistances */
707  if (j < max_drawn_resists) {
708  for (i=j; i <= max_drawn_resists; i++) {
709  gtk_label_set(GTK_LABEL(statwindow.resists[i]), "");
710  }
711  }
712  max_drawn_resists = j;
713  } /* if we draw the resists */
714 
715 
716  /* Don't need to worry about hp, sp, grace, food - update_stat()
717  * deals with that as part of the stat bar logic.
718  */
719 
720 }
721 
726 {
728 }
void draw_stats(int redraw)
Definition: stats.c:522
GtkWidget * level
Definition: gx11.c:165
void draw_message_window(int redraw)
Definition: stats.c:437
int need_mapping_update
Definition: stats.c:131
#define STYLE_LOW
Definition: stats.c:55
sint8 level
Definition: client.h:215
GtkWidget * playername
Definition: gx11.c:163
int value
Definition: stats.c:126
static const char *const stat_style_names[NUM_STYLES]
Definition: stats.c:66
uint16 exp_table_max
Definition: client.c:72
sint16 hp
Definition: client.h:216
GtkWidget * dam
Definition: gx11.c:177
sint8 Int
Definition: client.h:211
GtkWidget * weapon_speed
Definition: stats.c:103
sint32 speed
Definition: client.h:229
int sc_version
Definition: client.h:99
sint8 Str
Definition: client.h:206
#define MAX_SKILL
Definition: client.h:61
#define NUM_STYLES
Definition: stats.c:60
sint16 food
Definition: client.h:223
#define STYLE_NORMAL
Definition: stats.c:54
ClientSocket csocket
Definition: client.c:78
sint16 maxhp
Definition: client.h:217
sint8 ac
Definition: client.h:214
Stats stats
Definition: client.h:285
uint32 resist_change
Definition: client.h:242
const char *const resists_name[NUM_RESISTS]
Definition: client.c:80
GtkWidget * armor
Definition: gx11.c:179
sint64 skill_exp[MAX_SKILL]
Definition: client.h:244
sint8 Con
Definition: client.h:208
sint8 Cha
Definition: client.h:210
static GtkWidget * resists[NUM_RESISTS]
Definition: gx11.c:234
#define SKILL_BOXES_X
Definition: stats.c:83
#define PROTECTION_BOXES_Y
Definition: stats.c:87
GtkWidget * speed
Definition: gx11.c:180
char * skill_names[MAX_SKILL]
Definition: client.c:63
GtkWidget * Pow
Definition: gx11.c:175
static int lastmax[MAX_STAT_BARS]
Definition: stats.c:133
GtkWidget * table_protections
Definition: stats.c:108
sint8 Pow
Definition: client.h:212
void stats_get_styles(void)
Definition: stats.c:139
#define STYLE_SUPER
Definition: stats.c:56
void stats_init(GtkWidget *window_root)
Definition: stats.c:176
static Stats last_stats
Definition: x11.c:264
#define STYLE_GRAD_LOW
Definition: stats.c:58
void LOG(LogLevel level, const char *origin, const char *format,...)
Definition: misc.c:178
static int mapping_sort(NameMapping *a, NameMapping *b)
Definition: stats.c:480
#define TRUE
Definition: client-types.h:71
GtkWidget * Dex
Definition: gx11.c:170
sint16 grace
Definition: client.h:220
#define NUM_RESISTS
Definition: client.h:432
GdkColor * bar_colors[MAX_STAT_BARS][NUM_STYLES]
Definition: stats.c:75
#define CONFIG_FOODBEEP
Definition: client.h:172
sint16 resists[30]
Definition: client.h:241
GtkWidget * Con
Definition: gx11.c:171
GtkWidget * window_root
Definition: main.c:56
void clear_stat_mapping(void)
Definition: stats.c:725
#define STYLE_GRAD_NORMAL
Definition: stats.c:57
sint8 Dex
Definition: client.h:207
GtkWidget * Wis
Definition: gx11.c:173
#define SKILL_BOXES_Y
Definition: stats.c:84
sint16 use_config[CONFIG_NUMS]
Definition: init.c:50
NameMapping skill_mapping[MAX_SKILL]
Definition: stats.c:129
#define PROTECTION_BOXES_X
Definition: stats.c:86
Client_Player cpl
Definition: client.c:77
#define CONFIG_GRAD_COLOR
Definition: client.h:175
GtkWidget * Cha
Definition: gx11.c:174
char * name
Definition: image.c:61
#define STYLE_GRAD_SUPER
Definition: stats.c:59
sint16 maxgrace
Definition: client.h:221
GtkWidget * exp
Definition: stats.c:105
GtkWidget * range
Definition: stats.c:104
void update_stat(int stat_no, sint64 max_stat, sint64 current_stat, sint64 statbar_max, sint64 statbar_stat, const char *name, int can_alert)
Definition: stats.c:292
sint16 maxsp
Definition: client.h:219
#define MAX_BUF
Definition: client-types.h:128
const char * name
Definition: stats.c:125
GtkWidget * stat_bar[MAX_STAT_BARS]
Definition: stats.c:52
sint16 dam
Definition: client.h:226
static const char *const stat_bar_names[MAX_STAT_BARS]
Definition: stats.c:48
sint32 weapon_sp
Definition: client.h:230
NameMapping resist_mapping[NUM_RESISTS]
Definition: stats.c:129
GtkWidget * stat_label[MAX_STAT_BARS]
Definition: stats.c:52
static int lastval[MAX_STAT_BARS]
Definition: stats.c:133
GtkWidget * skill_exp[MAX_SKILL *2]
Definition: gx11.c:183
static int has_init
Definition: spells.c:63
const char *const rcsid_gtk2_stats_c
Definition: stats.c:1
sint16 sp
Definition: client.h:218
char range[MAX_BUF]
Definition: client.h:288
GtkWidget * resists[PROTECTION_BOXES_X *PROTECTION_BOXES_Y]
Definition: stats.c:110
char title[MAX_BUF]
Definition: client.h:287
GtkWidget * wc
Definition: gx11.c:176
sint8 wc
Definition: client.h:213
sint16 skill_level[MAX_SKILL]
Definition: client.h:243
#define FALSE
Definition: client-types.h:68
static StatWindow statwindow
Definition: stats.c:114
sint8 Wis
Definition: client.h:209
static void update_stat_mapping(void)
Definition: stats.c:491
#define FLOAT_MULTF
Definition: newclient.h:102
#define MAX_STAT_BARS
Definition: stats.c:47
sint64 exp
Definition: client.h:222
GtkWidget * Str
Definition: gx11.c:169
GtkWidget * ac
Definition: gx11.c:178
GtkWidget * Int
Definition: gx11.c:172
uint64 * exp_table
Definition: client.c:73
GtkWidget * table_skills_exp
Definition: stats.c:107