Crossfire Client, Branch
R11627
|
00001 const char * const rcsid_gtk2_stats_c = 00002 "$Id: stats.c 9201 2008-06-01 17:32:45Z anmaster $"; 00003 /* 00004 Crossfire client, a client program for the crossfire program. 00005 00006 Copyright (C) 2005-2007 Mark Wedel & Crossfire Development Team 00007 00008 This program is free software; you can redistribute it and/or modify 00009 it under the terms of the GNU General Public License as published by 00010 the Free Software Foundation; either version 2 of the License, or 00011 (at your option) any later version. 00012 00013 This program is distributed in the hope that it will be useful, 00014 but WITHOUT ANY WARRANTY; without even the implied warranty of 00015 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 00016 GNU General Public License for more details. 00017 00018 You should have received a copy of the GNU General Public License 00019 along with this program; if not, write to the Free Software 00020 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 00021 00022 The author can be reached via e-mail to crossfire@metalforge.org 00023 */ 00024 00030 #ifdef HAVE_CONFIG_H 00031 # include <config.h> 00032 #endif 00033 00034 #include <assert.h> 00035 #include <gtk/gtk.h> 00036 #include <glade/glade.h> 00037 00038 #include "client.h" 00039 00040 #include "main.h" 00041 00042 #define STAT_BAR_HP 0 00043 #define STAT_BAR_SP 1 00044 #define STAT_BAR_GRACE 2 00045 #define STAT_BAR_FOOD 3 00046 #define STAT_BAR_EXP 4 00047 #define MAX_STAT_BARS 5 00048 static const char * const stat_bar_names[MAX_STAT_BARS] = { 00049 "hp", "sp", "grace", "food", "exp" 00050 }; 00051 00052 GtkWidget *stat_label[MAX_STAT_BARS], *stat_bar[MAX_STAT_BARS]; 00053 00054 #define STYLE_NORMAL 0 00055 #define STYLE_LOW 1 00056 #define STYLE_SUPER 2 00057 #define STYLE_GRAD_NORMAL 3 00058 #define STYLE_GRAD_LOW 4 00059 #define STYLE_GRAD_SUPER 5 00060 #define NUM_STYLES 6 00061 00062 /* The name of the symbolic widget names we try to look up 00063 * the styles of (these will be prefixed with hp_, sp_, etc). 00064 * This should always match NUM_STYLES. 00065 */ 00066 static const char * const stat_style_names[NUM_STYLES] = { 00067 "bar_normal", "bar_low", "bar_super", 00068 "gradual_bar_normal", "gradual_bar_low", "gradual_bar_super" 00069 }; 00070 00071 /* We really only care about the colors, as there isn't 00072 * anything else we can change about the progressbar widget 00073 * itself. 00074 */ 00075 GdkColor *bar_colors[MAX_STAT_BARS][NUM_STYLES]; 00076 00077 00078 /* The table for showing skill exp is an x & y grid. Note 00079 * for proper formatting, SKILL_BOXES_X must be even. 00080 * Hmmm - perhaps these should instead be dynamically 00081 * generated? 00082 */ 00083 #define SKILL_BOXES_X 6 00084 #define SKILL_BOXES_Y 17 00085 00086 #define PROTECTION_BOXES_X 6 00087 #define PROTECTION_BOXES_Y 6 00088 00089 typedef struct { 00090 GtkWidget *playername; 00091 GtkWidget *Str; 00092 GtkWidget *Dex; 00093 GtkWidget *Con; 00094 GtkWidget *Int; 00095 GtkWidget *Wis; 00096 GtkWidget *Cha; 00097 GtkWidget *Pow; 00098 GtkWidget *wc; 00099 GtkWidget *dam; 00100 GtkWidget *ac; 00101 GtkWidget *armor; 00102 GtkWidget *speed; 00103 GtkWidget *weapon_speed; 00104 GtkWidget *range; 00105 GtkWidget *exp; 00106 GtkWidget *level; 00107 GtkWidget *table_skills_exp; 00108 GtkWidget *table_protections; 00109 GtkWidget *skill_exp[SKILL_BOXES_X * SKILL_BOXES_Y]; 00110 GtkWidget *resists[PROTECTION_BOXES_X * PROTECTION_BOXES_Y]; 00111 00112 } StatWindow; 00113 00114 static StatWindow statwindow; 00115 00116 /* The basic idea of this structure is there are some lists of 00117 * names we get from the server (like say skill names) - 00118 * internally, these are all referred to number, and the numbers 00119 * are not in any order. The idea here is we can store away the 00120 * names, and then can display the items in the window in 00121 * nice alphabetical order instead of the random order they 00122 * normally show up in. 00123 */ 00124 typedef struct { 00125 const char *name; 00126 int value; 00127 } NameMapping; 00128 00129 NameMapping skill_mapping[MAX_SKILL], resist_mapping[NUM_RESISTS]; 00130 00131 int need_mapping_update=1; 00132 00133 static int lastval[MAX_STAT_BARS], lastmax[MAX_STAT_BARS]; 00134 00139 void stats_get_styles(void) 00140 { 00141 static int has_init=0; 00142 int stat_bar, sub_style; 00143 char buf[MAX_BUF]; 00144 GtkStyle *tmp_style; 00145 00146 if (!has_init) { 00147 memset(bar_colors, 0, sizeof(GdkColor*) * MAX_STAT_BARS * NUM_STYLES); 00148 } 00149 00150 for (stat_bar=0; stat_bar < MAX_STAT_BARS; stat_bar++) { 00151 for (sub_style=0; sub_style < NUM_STYLES; sub_style++) { 00152 snprintf(buf, sizeof(buf), "%s_%s", stat_bar_names[stat_bar], stat_style_names[sub_style]); 00153 00154 tmp_style = gtk_rc_get_style_by_paths(gtk_settings_get_default(), NULL, buf, G_TYPE_NONE); 00155 00156 if (!tmp_style) { 00157 if (bar_colors[stat_bar][sub_style]) { 00158 free(bar_colors[stat_bar][sub_style]); 00159 bar_colors[stat_bar][sub_style] = NULL; 00160 } 00161 LOG(LOG_INFO, "stats.c::stats_get_styles()", "Unable to find style '%s'", buf); 00162 } else { 00163 if (!bar_colors[stat_bar][sub_style]) 00164 bar_colors[stat_bar][sub_style] = calloc(1, sizeof(GdkColor)); 00165 memcpy(bar_colors[stat_bar][sub_style], 00166 &tmp_style->base[GTK_STATE_SELECTED], sizeof(GdkColor)); 00167 } 00168 } 00169 } 00170 } 00171 00176 void stats_init(GtkWidget *window_root) 00177 { 00178 int i, x, y; 00179 char buf[MAX_BUF]; 00180 GladeXML *xml_tree; 00181 00182 xml_tree = glade_get_widget_tree(GTK_WIDGET(window_root)); 00183 for (i=0; i<MAX_STAT_BARS; i++) { 00184 snprintf(buf, sizeof(buf), "label_stats_%s", stat_bar_names[i]); 00185 stat_label[i] = glade_xml_get_widget(xml_tree, buf); 00186 00187 snprintf(buf, sizeof(buf), "progressbar_%s", stat_bar_names[i]); 00188 stat_bar[i] = glade_xml_get_widget(xml_tree, buf); 00189 00190 lastval[i] = -1; 00191 lastmax[i] = -1; 00192 } 00193 00194 statwindow.playername = 00195 glade_xml_get_widget(xml_tree, "label_playername"); 00196 statwindow.Str = 00197 glade_xml_get_widget(xml_tree, "label_str"); 00198 statwindow.Dex = 00199 glade_xml_get_widget(xml_tree, "label_dex"); 00200 statwindow.Con = 00201 glade_xml_get_widget(xml_tree, "label_con"); 00202 statwindow.Int = 00203 glade_xml_get_widget(xml_tree, "label_int"); 00204 statwindow.Wis = 00205 glade_xml_get_widget(xml_tree, "label_wis"); 00206 statwindow.Pow = 00207 glade_xml_get_widget(xml_tree, "label_pow"); 00208 statwindow.Cha = 00209 glade_xml_get_widget(xml_tree, "label_cha"); 00210 statwindow.wc = 00211 glade_xml_get_widget(xml_tree, "label_wc"); 00212 statwindow.dam = 00213 glade_xml_get_widget(xml_tree, "label_dam"); 00214 statwindow.ac = 00215 glade_xml_get_widget(xml_tree, "label_ac"); 00216 statwindow.armor = 00217 glade_xml_get_widget(xml_tree, "label_armor"); 00218 statwindow.speed = 00219 glade_xml_get_widget(xml_tree, "label_speed"); 00220 statwindow.weapon_speed = 00221 glade_xml_get_widget(xml_tree, "label_weapon_speed"); 00222 statwindow.range = 00223 glade_xml_get_widget(xml_tree, "label_range"); 00224 statwindow.exp = 00225 glade_xml_get_widget(xml_tree, "label_exp"); 00226 statwindow.level = 00227 glade_xml_get_widget(xml_tree, "label_level"); 00228 00229 /* Note that the order the labels are attached to the tables determines 00230 * the order of display. The order as right now is left to right, 00231 * then top to bottom, which means that is the order if displaying 00232 * skills & protections. 00233 */ 00234 00235 statwindow.table_skills_exp = 00236 glade_xml_get_widget(xml_tree,"table_skills_exp"); 00237 00238 for (i=0, x=0, y=0; i < SKILL_BOXES_X * SKILL_BOXES_Y; i++) { 00239 statwindow.skill_exp[i] = gtk_label_new(""); 00240 gtk_table_attach(GTK_TABLE(statwindow.table_skills_exp), statwindow.skill_exp[i], 00241 x, x+1, y, y+1, GTK_EXPAND, 0, 0, 0); 00242 gtk_widget_show(statwindow.skill_exp[i]); 00243 x++; 00244 if (x == SKILL_BOXES_X) { 00245 x=0; 00246 y++; 00247 } 00248 } 00249 00250 statwindow.table_protections = 00251 glade_xml_get_widget(xml_tree,"table_protections"); 00252 00253 for (i=0, x=0, y=0; i < PROTECTION_BOXES_X * PROTECTION_BOXES_Y; i++) { 00254 statwindow.resists[i] = gtk_label_new(""); 00255 gtk_table_attach(GTK_TABLE(statwindow.table_protections), statwindow.resists[i], 00256 x, x+1, y, y+1, GTK_EXPAND, 0, 0, 0); 00257 gtk_widget_show(statwindow.resists[i]); 00258 x++; 00259 if (x == PROTECTION_BOXES_X) { 00260 x=0; 00261 y++; 00262 } 00263 } 00264 stats_get_styles(); 00265 } 00266 00267 00292 void update_stat(int stat_no, sint64 max_stat, sint64 current_stat, 00293 sint64 statbar_max, sint64 statbar_stat, const char *name, int can_alert) 00294 { 00295 float bar; 00296 int is_alert; 00297 char buf[256]; 00298 GdkColor ncolor, *set_color=NULL; 00299 00300 /* If nothing changed, don't need to do anything */ 00301 if (lastval[stat_no] == current_stat && lastmax[stat_no] == max_stat) 00302 return; 00303 00304 lastval[stat_no] = current_stat; 00305 lastmax[stat_no] = max_stat; 00306 00307 if (statbar_max > 0) bar = (float) statbar_stat / (float) statbar_max; 00308 else bar = 0.0; 00309 00310 /* Simple check to see if current stat is less than 25% */ 00311 if (can_alert && current_stat * 4 < max_stat) is_alert=1; 00312 else is_alert = 0; 00313 00314 if (use_config[CONFIG_GRAD_COLOR]) { 00315 /* In this mode, the color of the stat bar were go between red and green 00316 * in a gradual style. Color is blended from low to high 00317 */ 00318 00319 GdkColor *hcolor, *lcolor; 00320 float diff; 00321 00322 /* First thing we do is figure out current values, and thus 00323 * what color bases we use (based on super charged or 00324 * normal value). We also set up diff as where we are between 00325 * those two points. In this way, the blending logic 00326 * below is the same regardless of actual value. 00327 */ 00328 if (bar > 1.0) { 00329 if (bar>2.0) bar=2.0; /* Doesn't affect display, just our calculations */ 00330 hcolor = bar_colors[stat_no][STYLE_GRAD_SUPER]; 00331 lcolor = bar_colors[stat_no][STYLE_GRAD_NORMAL]; 00332 diff = bar - 1.0; 00333 } else { 00334 if (bar < 0.0) bar=0.0; /* Like above, doesn't affect display */ 00335 hcolor = bar_colors[stat_no][STYLE_GRAD_NORMAL]; 00336 lcolor = bar_colors[stat_no][STYLE_GRAD_LOW]; 00337 diff = bar; 00338 } 00339 /* Now time to blend. First, make sure colors are set. 00340 * then, we use the lcolor as the base, making adjustments 00341 * based on hcolor. Values in hcolor may be lower than 00342 * lcolor, but that just means we substract from lcolor, not 00343 * add. 00344 */ 00345 00346 if (lcolor && hcolor) { 00347 #if 1 00348 memcpy(&ncolor, lcolor, sizeof(GdkColor)); 00349 00350 ncolor.red += (hcolor->red - lcolor->red) * diff; 00351 ncolor.green += (hcolor->green - lcolor->green) * diff; 00352 ncolor.blue += (hcolor->blue - lcolor->blue) * diff; 00353 #else 00354 /* This is an alternate coloring method that works 00355 * when using saturated colors for the base points. 00356 * This mimics the old code, and works good 00357 * when using such saturated colors (eg, one 00358 * of the RGB triplets being 255, others 0, like 00359 * red, green, or blue). However, this doesn't produce 00360 * very good results when not using those colors - if 00361 * say magenta and yellow are chosen as the two colors, 00362 * this code results in the colors basically getting near 00363 * white in the middle values. For saturated colors, the 00364 * code below would produce nice bright yellow for the middle 00365 * values, where as the code above produces more a dark yellow, 00366 * since it only takes half the red and half the green. 00367 * However, the code above still produces useful results 00368 * even with that limitation, and it works for all colors, 00369 * so it is the code enabled. It perhaps be 00370 * interesting to have some detection logic on how the colors 00371 * are actually set - if only a single r/g/b value is set for 00372 * the two colors, then use this logic here, otherwise 00373 * the above logic or something. 00374 * MSW 2007-01-24 00375 */ 00376 if (diff > 0.5) { 00377 memcpy(&ncolor, hcolor, sizeof(GdkColor)); 00378 00379 if (lcolor->red > hcolor->red) 00380 ncolor.red = 2 * lcolor->red * (1.0 - diff); 00381 00382 if (lcolor->green > hcolor->green) 00383 ncolor.green = 2 * lcolor->green * (1.0 - diff); 00384 00385 if (lcolor->blue > hcolor->blue) 00386 ncolor.blue = 2 * lcolor->blue * (1.0 - diff); 00387 00388 } else { 00389 memcpy(&ncolor, lcolor, sizeof(GdkColor)); 00390 00391 if (hcolor->red > lcolor->red) 00392 ncolor.red = 2 * hcolor->red * diff; 00393 00394 if (hcolor->green > lcolor->green) 00395 ncolor.green = 2 * hcolor->green * diff; 00396 00397 if (hcolor->blue > lcolor->blue) 00398 ncolor.blue = 2 * hcolor->blue * diff; 00399 } 00400 #endif 00401 #if 0 00402 fprintf(stderr,"stat %d, val %d, r/g/b=%d/%d/%d\n", 00403 stat_no, current_stat, ncolor.red, ncolor.green, ncolor.blue); 00404 #endif 00405 set_color = &ncolor; 00406 } 00407 } else { 00408 if (statbar_stat * 4 < statbar_max) 00409 set_color = bar_colors[stat_no][STYLE_LOW]; 00410 else if (statbar_stat > statbar_max) 00411 set_color = bar_colors[stat_no][STYLE_SUPER]; 00412 else 00413 set_color = bar_colors[stat_no][STYLE_NORMAL]; 00414 } 00415 if (bar > 1.0) bar = 1.0; 00416 if (bar < 0.0) bar = 0.0; 00417 00418 /* It may be a waste, but we set the color everytime here - it 00419 * isn't very costly, and keeps us from tracing the last 00420 * color we set. 00421 * Note that set_color could be null, which means it reverts 00422 * back to normal color. 00423 */ 00424 gtk_widget_modify_base(stat_bar[stat_no], GTK_STATE_SELECTED, set_color); 00425 00426 gtk_progress_set_percentage(GTK_PROGRESS(stat_bar[stat_no]), bar); 00427 snprintf(buf, sizeof(buf), "%s %"FMT64"/%"FMT64, name, current_stat, max_stat); 00428 gtk_label_set(GTK_LABEL(stat_label[stat_no]), buf); 00429 00430 } 00431 00437 void draw_message_window(int redraw) { 00438 static int lastbeep=0; 00439 static sint64 level_diff; 00440 00441 update_stat(0, cpl.stats.maxhp, cpl.stats.hp, 00442 cpl.stats.maxhp, cpl.stats.hp, "HP:", TRUE); 00443 update_stat(1, cpl.stats.maxsp, cpl.stats.sp, 00444 cpl.stats.maxsp, cpl.stats.sp, "Spell Points:", TRUE); 00445 update_stat(2, cpl.stats.maxgrace, cpl.stats.grace, 00446 cpl.stats.maxgrace, cpl.stats.grace, "Grace:", TRUE); 00447 update_stat(3, 999, cpl.stats.food, 999, cpl.stats.food, "Food:", TRUE); 00448 00449 /* We may or may not have an exp table from the server. If we don't, just 00450 * use current exp value so it will always appear maxed out. 00451 */ 00452 /* We calculate level_diff here just so it makes the update_stat() 00453 * call below less messy. 00454 */ 00455 if ((cpl.stats.level+1) < exp_table_max) 00456 level_diff = exp_table[cpl.stats.level+1] - exp_table[cpl.stats.level]; 00457 else 00458 level_diff=cpl.stats.exp; 00459 00460 update_stat(4, 00461 (cpl.stats.level+1) < exp_table_max ? exp_table[cpl.stats.level+1]:cpl.stats.exp, 00462 cpl.stats.exp, 00463 level_diff, 00464 (cpl.stats.level+1) < exp_table_max ? 00465 (cpl.stats.exp - exp_table[cpl.stats.level]):cpl.stats.exp, 00466 "Exp:", FALSE); 00467 00468 if (use_config[CONFIG_FOODBEEP] && (cpl.stats.food%4==3) && (cpl.stats.food < 200)) { 00469 gdk_beep( ); 00470 } else if (use_config[CONFIG_FOODBEEP] && cpl.stats.food == 0 && ++lastbeep == 5) { 00471 lastbeep = 0; 00472 gdk_beep( ); 00473 } 00474 } 00475 00480 static int mapping_sort(NameMapping *a, NameMapping *b) 00481 { 00482 if (!a->name && !b->name) return 0; 00483 if (!a->name) return 1; 00484 if (!b->name) return -1; 00485 else return strcasecmp(a->name, b->name); 00486 } 00487 00491 static void update_stat_mapping(void) 00492 { 00493 int i; 00494 00495 for (i=0; i < MAX_SKILL; i++) { 00496 skill_mapping[i].value=i; 00497 if (skill_names[i]) 00498 skill_mapping[i].name = skill_names[i]; 00499 else 00500 skill_mapping[i].name = NULL; 00501 } 00502 qsort(skill_mapping, MAX_SKILL, sizeof(NameMapping), 00503 (int (*)(const void*, const void*))mapping_sort); 00504 00505 for (i=0; i < NUM_RESISTS; i++) { 00506 resist_mapping[i].value=i; 00507 if (resists_name[i]) 00508 resist_mapping[i].name = resists_name[i]; 00509 else 00510 resist_mapping[i].name = NULL; 00511 } 00512 qsort(resist_mapping, NUM_RESISTS, sizeof(NameMapping), 00513 (int (*)(const void*, const void*))mapping_sort); 00514 00515 need_mapping_update=0; 00516 } 00517 00522 void draw_stats(int redraw) { 00523 static Stats last_stats; 00524 static char last_name[MAX_BUF]="", last_range[MAX_BUF]=""; 00525 static int init_before=0, max_drawn_skill=0, max_drawn_resists=0; 00526 00527 float weap_sp; 00528 char buff[MAX_BUF]; 00529 int i, on_skill, sk; 00530 00531 if (!init_before) { 00532 init_before=1; 00533 memset(&last_stats, 0, sizeof(Stats)); 00534 } 00535 00536 /* skill_names gets set as part of the initialization with the 00537 * client - however, right now, there is no callback when 00538 * it is set, so instead, just track that wee need to update 00539 * and see if it changes. 00540 */ 00541 if (need_mapping_update && skill_names[1] != NULL) { 00542 update_stat_mapping(); 00543 } 00544 00545 if (strcmp(cpl.title, last_name) || redraw) { 00546 strcpy(last_name,cpl.title); 00547 gtk_label_set (GTK_LABEL(statwindow.playername), cpl.title); 00548 } 00549 00550 if(redraw || cpl.stats.exp!=last_stats.exp) { 00551 last_stats.exp = cpl.stats.exp; 00552 snprintf(buff, sizeof(buff), "Experience: %5" FMT64 ,cpl.stats.exp); 00553 gtk_label_set (GTK_LABEL(statwindow.exp), buff); 00554 } 00555 00556 if(redraw || cpl.stats.level!=last_stats.level) { 00557 last_stats.level = cpl.stats.level; 00558 snprintf(buff, sizeof(buff), "Level: %d",cpl.stats.level); 00559 gtk_label_set (GTK_LABEL(statwindow.level), buff); 00560 } 00561 00562 if(redraw || cpl.stats.Str!=last_stats.Str) { 00563 last_stats.Str=cpl.stats.Str; 00564 snprintf(buff, sizeof(buff), "%2d",cpl.stats.Str); 00565 gtk_label_set (GTK_LABEL(statwindow.Str), buff); 00566 } 00567 00568 if(redraw || cpl.stats.Dex!=last_stats.Dex) { 00569 last_stats.Dex=cpl.stats.Dex; 00570 snprintf(buff, sizeof(buff), "%2d",cpl.stats.Dex); 00571 gtk_label_set (GTK_LABEL(statwindow.Dex), buff); 00572 } 00573 00574 if(redraw || cpl.stats.Con!=last_stats.Con) { 00575 last_stats.Con=cpl.stats.Con; 00576 snprintf(buff, sizeof(buff), "%2d",cpl.stats.Con); 00577 gtk_label_set (GTK_LABEL(statwindow.Con), buff); 00578 } 00579 00580 if(redraw || cpl.stats.Int!=last_stats.Int) { 00581 last_stats.Int=cpl.stats.Int; 00582 snprintf(buff, sizeof(buff), "%2d",cpl.stats.Int); 00583 gtk_label_set (GTK_LABEL(statwindow.Int), buff); 00584 } 00585 00586 if(redraw || cpl.stats.Wis!=last_stats.Wis) { 00587 last_stats.Wis=cpl.stats.Wis; 00588 snprintf(buff, sizeof(buff), "%2d",cpl.stats.Wis); 00589 gtk_label_set (GTK_LABEL(statwindow.Wis), buff); 00590 } 00591 00592 if(redraw || cpl.stats.Pow!=last_stats.Pow) { 00593 last_stats.Pow=cpl.stats.Pow; 00594 snprintf(buff, sizeof(buff), "%2d",cpl.stats.Pow); 00595 gtk_label_set (GTK_LABEL(statwindow.Pow), buff); 00596 } 00597 00598 if(redraw || cpl.stats.Cha!=last_stats.Cha) { 00599 last_stats.Cha=cpl.stats.Cha; 00600 snprintf(buff, sizeof(buff), "%2d",cpl.stats.Cha); 00601 gtk_label_set (GTK_LABEL(statwindow.Cha), buff); 00602 } 00603 00604 if(redraw || cpl.stats.wc!=last_stats.wc) { 00605 last_stats.wc=cpl.stats.wc; 00606 snprintf(buff, sizeof(buff), "%3d",cpl.stats.wc); 00607 gtk_label_set (GTK_LABEL(statwindow.wc), buff); 00608 } 00609 00610 if(redraw || cpl.stats.dam!=last_stats.dam) { 00611 last_stats.dam=cpl.stats.dam; 00612 snprintf(buff, sizeof(buff), "%d",cpl.stats.dam); 00613 gtk_label_set (GTK_LABEL(statwindow.dam), buff); 00614 } 00615 00616 if(redraw || cpl.stats.ac!=last_stats.ac) { 00617 last_stats.ac=cpl.stats.ac; 00618 snprintf(buff, sizeof(buff), "%d",cpl.stats.ac); 00619 gtk_label_set (GTK_LABEL(statwindow.ac), buff); 00620 } 00621 00622 if(redraw || cpl.stats.resists[0]!=last_stats.resists[0]) { 00623 last_stats.resists[0]=cpl.stats.resists[0]; 00624 snprintf(buff, sizeof(buff), "%d",cpl.stats.resists[0]); 00625 gtk_label_set (GTK_LABEL(statwindow.armor), buff); 00626 } 00627 00628 if (redraw || cpl.stats.speed!=last_stats.speed) { 00629 last_stats.speed=cpl.stats.speed; 00630 snprintf(buff, sizeof(buff), "%3.2f",(float)cpl.stats.speed/FLOAT_MULTF); 00631 gtk_label_set (GTK_LABEL(statwindow.speed), buff); 00632 } 00633 /* sc_version >= 1029 reports real value of weapon speed - 00634 * not as a factor of player speed. Handle accordingly. 00635 */ 00636 if (csocket.sc_version >= 1029) 00637 weap_sp = (float) cpl.stats.weapon_sp / FLOAT_MULTF; 00638 else 00639 weap_sp = (float) cpl.stats.speed/((float)cpl.stats.weapon_sp); 00640 00641 if (redraw || weap_sp !=last_stats.weapon_sp) { 00642 last_stats.weapon_sp=weap_sp; 00643 snprintf(buff, sizeof(buff), "%3.2f",weap_sp); 00644 gtk_label_set (GTK_LABEL(statwindow.weapon_speed), buff); 00645 } 00646 00647 if(redraw || strcmp(cpl.range, last_range)) { 00648 strcpy(last_range, cpl.range); 00649 snprintf(buff, sizeof(buff), "Range: %s",cpl.range); 00650 gtk_label_set (GTK_LABEL(statwindow.range), cpl.range); 00651 } 00652 00653 on_skill=0; 00654 assert(sizeof(statwindow.skill_exp)/sizeof(*statwindow.skill_exp) >= 2*MAX_SKILL); 00655 for (i=0; i<MAX_SKILL; i++) { 00656 /* Drawing a particular skill entry is tricky - only draw if 00657 * different, and only draw if we have a name for the skill 00658 * and the player has some exp in the skill - don't draw 00659 * all 30 skills for no reason. 00660 */ 00661 sk = skill_mapping[i].value; 00662 00663 if ((redraw || cpl.stats.skill_exp[sk] != last_stats.skill_exp[sk]) && 00664 skill_mapping[i].name && cpl.stats.skill_exp[sk]){ 00665 gtk_label_set(GTK_LABEL(statwindow.skill_exp[on_skill++]), skill_mapping[i].name); 00666 snprintf(buff, sizeof(buff), "%" FMT64 " (%d)", cpl.stats.skill_exp[sk], cpl.stats.skill_level[sk]); 00667 gtk_label_set(GTK_LABEL(statwindow.skill_exp[on_skill++]), buff); 00668 last_stats.skill_level[sk] = cpl.stats.skill_level[sk]; 00669 last_stats.skill_exp[sk] = cpl.stats.skill_exp[sk]; 00670 } else if (cpl.stats.skill_exp[sk]) { 00671 /* don't need to draw the skill, but need to update the position 00672 * of where to draw the next one. 00673 */ 00674 on_skill+=2; 00675 } 00676 } 00677 00678 /* Since the number of skills we draw come and go, basically we want 00679 * to erase any extra. This shows up when switching characters, eg, character 00680 * #1 knows 10 skills, #2 knows 5 - need to erase those 5 extra. 00681 */ 00682 if (on_skill < max_drawn_skill) { 00683 int k; 00684 00685 for (k = on_skill; k <= max_drawn_skill; k++) 00686 gtk_label_set(GTK_LABEL(statwindow.skill_exp[k]), ""); 00687 } 00688 max_drawn_skill = on_skill; 00689 00690 /* Now do the resistance table */ 00691 if (redraw || cpl.stats.resist_change) { 00692 int i,j=0; 00693 00694 cpl.stats.resist_change=0; 00695 for (i=0; i<NUM_RESISTS; i++) { 00696 sk = resist_mapping[i].value; 00697 if (cpl.stats.resists[sk]) { 00698 gtk_label_set(GTK_LABEL(statwindow.resists[j]), resist_mapping[i].name); 00699 j++; 00700 snprintf(buff, sizeof(buff), "%+4d", cpl.stats.resists[sk]); 00701 gtk_label_set(GTK_LABEL(statwindow.resists[j]), buff); 00702 j++; 00703 if (j >= PROTECTION_BOXES_X * PROTECTION_BOXES_Y) break; 00704 } 00705 } 00706 /* Erase old/unused resistances */ 00707 if (j < max_drawn_resists) { 00708 for (i=j; i <= max_drawn_resists; i++) { 00709 gtk_label_set(GTK_LABEL(statwindow.resists[i]), ""); 00710 } 00711 } 00712 max_drawn_resists = j; 00713 } /* if we draw the resists */ 00714 00715 00716 /* Don't need to worry about hp, sp, grace, food - update_stat() 00717 * deals with that as part of the stat bar logic. 00718 */ 00719 00720 } 00721 00725 void clear_stat_mapping(void) 00726 { 00727 need_mapping_update=1; 00728 }