Atrinik Client 2.5
client/player.c
Go to the documentation of this file.
00001 /************************************************************************
00002 *            Atrinik, a Multiplayer Online Role Playing Game            *
00003 *                                                                       *
00004 *    Copyright (C) 2009-2011 Alex Tokar and Atrinik Development Team    *
00005 *                                                                       *
00006 * Fork from Daimonin (Massive Multiplayer Online Role Playing Game)     *
00007 * and Crossfire (Multiplayer game for X-windows).                       *
00008 *                                                                       *
00009 * This program is free software; you can redistribute it and/or modify  *
00010 * it under the terms of the GNU General Public License as published by  *
00011 * the Free Software Foundation; either version 2 of the License, or     *
00012 * (at your option) any later version.                                   *
00013 *                                                                       *
00014 * This program is distributed in the hope that it will be useful,       *
00015 * but WITHOUT ANY WARRANTY; without even the implied warranty of        *
00016 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
00017 * GNU General Public License for more details.                          *
00018 *                                                                       *
00019 * You should have received a copy of the GNU General Public License     *
00020 * along with this program; if not, write to the Free Software           *
00021 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.             *
00022 *                                                                       *
00023 * The author can be reached at admin@atrinik.org                        *
00024 ************************************************************************/
00025 
00037 #include <global.h>
00038 
00043 static _player_doll_pos player_doll[PDOLL_INIT] =
00044 {
00045     {93,    55},
00046     {93,    8},
00047     {93,    100},
00048     {93,    158},
00049     {135,   95},
00050     {50,    95},
00051     {50,    134},
00052     {135,   134},
00053     {54,    51},
00054     {141,   10},
00055     {5,     148},
00056     {180,   148},
00057     {5,     108},
00058     {180,   108},
00059     {43,    10},
00060     {4,     10}
00061 };
00062 
00064 static float weapon_speed_table[19] =
00065 {
00066     20.0f,  18.0f,  10.0f,  8.0f,   5.5f,
00067     4.25f,  3.50f,  3.05f,  2.70f,  2.35f,
00068     2.15f,  1.95f,  1.80f,  1.60f,  1.52f,
00069     1.44f,  1.32f,  1.25f,  1.20f
00070 };
00071 
00074 const char *gender_noun[GENDER_MAX] =
00075 {
00076     "neuter", "male", "female", "hermaphrodite"
00077 };
00078 
00081 void clear_player()
00082 {
00083     memset(&cpl, 0, sizeof(cpl));
00084     quickslots_init();
00085     objects_init();
00086     init_player_data();
00087     WIDGET_REDRAW_ALL(SKILL_EXP_ID);
00088 }
00089 
00096 void new_player(long tag, long weight, short face)
00097 {
00098     cpl.ob->tag = tag;
00099     cpl.ob->weight = (float) weight / 1000;
00100     cpl.ob->face = face;
00101 }
00102 
00106 void client_send_apply(int tag)
00107 {
00108     char buf[MAX_BUF];
00109 
00110     snprintf(buf, sizeof(buf), "ap %d", tag);
00111     cs_write_string(buf, strlen(buf));
00112 }
00113 
00117 void client_send_examine(int tag)
00118 {
00119     char buf[MAX_BUF];
00120 
00121     snprintf(buf, sizeof(buf), "ex %d", tag);
00122     cs_write_string(buf, strlen(buf));
00123 }
00124 
00130 void client_send_move(int loc, int tag, int nrof)
00131 {
00132     char buf[MAX_BUF];
00133 
00134     snprintf(buf, sizeof(buf), "mv %d %d %d", loc, tag, nrof);
00135     cs_write_string(buf, strlen(buf));
00136 }
00137 
00143 void send_command(const char *command)
00144 {
00145     char buf[MAX_BUF];
00146     SockList sl;
00147 
00148     sl.buf = (unsigned char *) buf;
00149     strcpy((char *) sl.buf, "cm ");
00150     sl.len = 3;
00151     strncpy((char *) sl.buf + sl.len, command, MAX_BUF - sl.len);
00152     sl.buf[MAX_BUF - 1] = '\0';
00153     sl.len += (int) strlen(command);
00154     send_socklist(sl);
00155 }
00156 
00161 void CompleteCmd(unsigned char *data, int len)
00162 {
00163     (void) data;
00164     (void) len;
00165 }
00166 
00170 void set_weight_limit(uint32 wlim)
00171 {
00172     cpl.weight_limit = wlim;
00173 }
00174 
00177 void init_player_data()
00178 {
00179     new_player(0, 0, 0);
00180 
00181     cpl.dm = 0;
00182     cpl.fire_on = cpl.firekey_on = 0;
00183     cpl.run_on = cpl.runkey_on = 0;
00184     cpl.inventory_win = IWIN_BELOW;
00185 
00186     cpl.container_tag = -996;
00187     cpl.container = NULL;
00188 
00189     memset(&cpl.stats, 0, sizeof(Stats));
00190 
00191     cpl.stats.maxsp = 1;
00192     cpl.stats.maxhp = 1;
00193     cpl.gen_hp = 0.0f;
00194     cpl.gen_sp = 0.0f;
00195     cpl.gen_grace = 0.0f;
00196     cpl.target_hp = 0;
00197 
00198     cpl.stats.maxgrace = 1;
00199     cpl.stats.speed = 1;
00200 
00201     cpl.range[0] = '\0';
00202 
00203     cpl.ob->nrof = 1;
00204     cpl.partyname[0] = cpl.partyjoin[0] = '\0';
00205 
00206     cpl.menustatus = MENU_NO;
00207     cpl.menustatus = MENU_NO;
00208 
00209     /* Avoid division by 0 errors */
00210     cpl.stats.maxsp = 1;
00211     cpl.stats.maxhp = 1;
00212     cpl.stats.maxgrace = 1;
00213 
00214     /* Displayed weapon speed is weapon speed/speed */
00215     cpl.stats.speed = 0;
00216     cpl.stats.weapon_sp = 0;
00217     cpl.action_timer = 0.0f;
00218 
00219     cpl.container_tag = -997;
00220     cpl.container = NULL;
00221 
00222     RangeFireMode = 0;
00223 }
00224 
00230 void widget_player_data_event(widgetdata *widget, int x, int y)
00231 {
00232     int mx = x - widget->x1, my = y - widget->y1;
00233 
00234     if (mx >= 184 && mx <= 210 && my >= 5 && my <= 35)
00235     {
00236         if (!client_command_check("/pray"))
00237         {
00238             send_command("/pray");
00239         }
00240     }
00241 }
00242 
00246 void widget_show_player_data(widgetdata *widget)
00247 {
00248     SDL_Rect box;
00249 
00250     sprite_blt(Bitmaps[BITMAP_PLAYER_INFO], widget->x1, widget->y1, NULL, NULL);
00251 
00252     box.w = widget->wd - 12;
00253     box.h = 36;
00254     string_blt(ScreenSurface, FONT_ARIAL10, cpl.ext_title, widget->x1 + 6, widget->y1 + 2, COLOR_HGOLD, TEXT_MARKUP | TEXT_WORD_WRAP, &box);
00255 
00256     /* Prayer button */
00257     sprite_blt(Bitmaps[BITMAP_PRAY], widget->x1 + 184, widget->y1 + 5, NULL, NULL);
00258 }
00259 
00263 void widget_player_stats(widgetdata *widget)
00264 {
00265     double temp;
00266     SDL_Rect box;
00267     int tmp;
00268 
00269     /* Let's look if we have a backbuffer SF, if not create one from the background */
00270     if (!widget->widgetSF)
00271     {
00272         widget->widgetSF = SDL_ConvertSurface(Bitmaps[BITMAP_STATS_BG]->bitmap, Bitmaps[BITMAP_STATS_BG]->bitmap->format, Bitmaps[BITMAP_STATS_BG]->bitmap->flags);
00273     }
00274 
00275     /* We have a backbuffer SF, test for the redrawing flag and do the redrawing */
00276     if (widget->redraw)
00277     {
00278         char buf[MAX_BUF];
00279         _BLTFX bltfx;
00280 
00281         widget->redraw = 0;
00282 
00283         /* We redraw here only all halfway static stuff *
00284          * We simply don't need to redraw that stuff every frame, how often the stats change? */
00285         bltfx.surface = widget->widgetSF;
00286         bltfx.flags = 0;
00287         bltfx.dark_level = 0;
00288         bltfx.alpha = 0;
00289 
00290         sprite_blt(Bitmaps[BITMAP_STATS_BG], 0, 0, NULL, &bltfx);
00291 
00292         string_blt(widget->widgetSF, FONT_MONO9, "Stats", 8, 4, COLOR_HGOLD, TEXT_OUTLINE, NULL);
00293 
00294         /* Strength */
00295         string_blt(widget->widgetSF, FONT_ARIAL10, "Str", 8, 17, COLOR_WHITE, 0, NULL);
00296         string_blt_format(widget->widgetSF, FONT_ARIAL10, 33, 17, COLOR_GREEN, 0, NULL, "%02d", cpl.stats.Str);
00297 
00298         /* Dexterity */
00299         string_blt(widget->widgetSF, FONT_ARIAL10, "Dex", 8, 28, COLOR_WHITE, 0, NULL);
00300         string_blt_format(widget->widgetSF, FONT_ARIAL10, 33, 28, COLOR_GREEN, 0, NULL, "%02d", cpl.stats.Dex);
00301 
00302         /* Constitution */
00303         string_blt(widget->widgetSF, FONT_ARIAL10, "Con", 8, 39, COLOR_WHITE, 0, NULL);
00304         string_blt_format(widget->widgetSF, FONT_ARIAL10, 33, 39, COLOR_GREEN, 0, NULL, "%02d", cpl.stats.Con);
00305 
00306         /* Intelligence */
00307         string_blt(widget->widgetSF, FONT_ARIAL10, "Int", 8, 50, COLOR_WHITE, 0, NULL);
00308         string_blt_format(widget->widgetSF, FONT_ARIAL10, 33, 50, COLOR_GREEN, 0, NULL, "%02d", cpl.stats.Int);
00309 
00310         /* Wisdom */
00311         string_blt(widget->widgetSF, FONT_ARIAL10, "Wis", 8, 61, COLOR_WHITE, 0, NULL);
00312         string_blt_format(widget->widgetSF, FONT_ARIAL10, 33, 61, COLOR_GREEN, 0, NULL, "%02d", cpl.stats.Wis);
00313 
00314         /* Power */
00315         string_blt(widget->widgetSF, FONT_ARIAL10, "Pow", 8, 72, COLOR_WHITE, 0, NULL);
00316         string_blt_format(widget->widgetSF, FONT_ARIAL10, 33, 72, COLOR_GREEN, 0, NULL, "%02d", cpl.stats.Pow);
00317 
00318         /* Charisma */
00319         string_blt(widget->widgetSF, FONT_ARIAL10, "Cha", 8, 83, COLOR_WHITE, 0, NULL);
00320         string_blt_format(widget->widgetSF, FONT_ARIAL10, 33, 83, COLOR_GREEN, 0, NULL, "%02d", cpl.stats.Cha);
00321 
00322         /* Health */
00323         string_blt(widget->widgetSF, FONT_ARIAL10, "HP", 58, 10, COLOR_WHITE, 0, NULL);
00324         snprintf(buf, sizeof(buf), "%d/%d", cpl.stats.hp, cpl.stats.maxhp);
00325         string_truncate_overflow(FONT_ARIAL10, buf, 90);
00326         string_blt(widget->widgetSF, FONT_ARIAL10, buf, 160 - string_get_width(FONT_ARIAL10, buf, 0), 10, COLOR_GREEN, 0, NULL);
00327 
00328         /* Mana */
00329         string_blt(widget->widgetSF, FONT_ARIAL10, "Mana", 58, 34, COLOR_WHITE, 0, NULL);
00330         snprintf(buf, sizeof(buf), "%d/%d", cpl.stats.sp, cpl.stats.maxsp);
00331         string_truncate_overflow(FONT_ARIAL10, buf, 75);
00332         string_blt(widget->widgetSF, FONT_ARIAL10, buf, 160 - string_get_width(FONT_ARIAL10, buf, 0), 34, COLOR_GREEN, 0, NULL);
00333 
00334         /* Grace */
00335         string_blt(widget->widgetSF, FONT_ARIAL10, "Grace", 58, 58, COLOR_WHITE, 0, NULL);
00336         snprintf(buf, sizeof(buf), "%d/%d", cpl.stats.grace, cpl.stats.maxgrace);
00337         string_truncate_overflow(FONT_ARIAL10, buf, 75);
00338         string_blt(widget->widgetSF, FONT_ARIAL10, buf, 160 - string_get_width(FONT_ARIAL10, buf, 0), 58, COLOR_GREEN, 0, NULL);
00339 
00340         /* Food */
00341         string_blt(widget->widgetSF, FONT_ARIAL10, "Food", 58, 83, COLOR_WHITE, 0, NULL);
00342     }
00343 
00344     /* Now we blit our backbuffer SF */
00345     box.x = widget->x1;
00346     box.y = widget->y1;
00347     SDL_BlitSurface(widget->widgetSF, NULL, ScreenSurface, &box);
00348 
00349     /* Health bar */
00350     if (cpl.stats.maxhp)
00351     {
00352         tmp = cpl.stats.hp;
00353 
00354         if (tmp < 0)
00355         {
00356             tmp = 0;
00357         }
00358 
00359         temp = (double) tmp / (double) cpl.stats.maxhp;
00360         box.x = 0;
00361         box.y = 0;
00362         box.h = Bitmaps[BITMAP_HP]->bitmap->h;
00363         box.w = (int) (Bitmaps[BITMAP_HP]->bitmap->w * temp);
00364 
00365         if (tmp && !box.w)
00366         {
00367             box.w = 1;
00368         }
00369 
00370         if (box.w > Bitmaps[BITMAP_HP]->bitmap->w)
00371         {
00372             box.w = Bitmaps[BITMAP_HP]->bitmap->w;
00373         }
00374 
00375         sprite_blt(Bitmaps[BITMAP_HP_BACK], widget->x1 + 57, widget->y1 + 23, NULL, NULL);
00376         sprite_blt(Bitmaps[BITMAP_HP], widget->x1 + 57, widget->y1 + 23, &box, NULL);
00377     }
00378 
00379     /* Mana bar */
00380     if (cpl.stats.maxsp)
00381     {
00382         tmp = cpl.stats.sp;
00383 
00384         if (tmp < 0)
00385         {
00386             tmp = 0;
00387         }
00388 
00389         temp = (double) tmp / (double) cpl.stats.maxsp;
00390         box.x = 0;
00391         box.y = 0;
00392         box.h = Bitmaps[BITMAP_SP]->bitmap->h;
00393         box.w = (int) (Bitmaps[BITMAP_SP]->bitmap->w * temp);
00394 
00395         if (tmp && !box.w)
00396         {
00397             box.w = 1;
00398         }
00399 
00400         if (box.w > Bitmaps[BITMAP_SP]->bitmap->w)
00401         {
00402             box.w = Bitmaps[BITMAP_SP]->bitmap->w;
00403         }
00404 
00405         sprite_blt(Bitmaps[BITMAP_SP_BACK], widget->x1 + 57, widget->y1 + 47, NULL, NULL);
00406         sprite_blt(Bitmaps[BITMAP_SP], widget->x1 + 57, widget->y1 + 47, &box, NULL);
00407     }
00408 
00409     /* Grace bar */
00410     if (cpl.stats.maxgrace)
00411     {
00412         tmp = cpl.stats.grace;
00413 
00414         if (tmp < 0)
00415         {
00416             tmp = 0;
00417         }
00418 
00419         temp = (double) tmp / (double) cpl.stats.maxgrace;
00420 
00421         box.x = 0;
00422         box.y = 0;
00423         box.h = Bitmaps[BITMAP_GRACE]->bitmap->h;
00424         box.w = (int) (Bitmaps[BITMAP_GRACE]->bitmap->w * temp);
00425 
00426         if (tmp && !box.w)
00427         {
00428             box.w = 1;
00429         }
00430 
00431         if (box.w > Bitmaps[BITMAP_GRACE]->bitmap->w)
00432         {
00433             box.w = Bitmaps[BITMAP_GRACE]->bitmap->w;
00434         }
00435 
00436         sprite_blt(Bitmaps[BITMAP_GRACE_BACK], widget->x1 + 57, widget->y1 + 71, NULL, NULL);
00437         sprite_blt(Bitmaps[BITMAP_GRACE], widget->x1 + 57, widget->y1 + 71, &box, NULL);
00438     }
00439 
00440     /* Food bar */
00441     tmp = cpl.stats.food;
00442 
00443     if (tmp < 0)
00444     {
00445         tmp = 0;
00446     }
00447 
00448     temp = (double) tmp / 1000;
00449     box.x = 0;
00450     box.y = 0;
00451     box.h = Bitmaps[BITMAP_FOOD]->bitmap->h;
00452     box.w = (int) (Bitmaps[BITMAP_FOOD]->bitmap->w * temp);
00453 
00454     if (tmp && !box.w)
00455     {
00456         box.w = 1;
00457     }
00458 
00459     if (box.w > Bitmaps[BITMAP_FOOD]->bitmap->w)
00460     {
00461         box.w = Bitmaps[BITMAP_FOOD]->bitmap->w;
00462     }
00463 
00464     sprite_blt(Bitmaps[BITMAP_FOOD_BACK], widget->x1 + 87, widget->y1 + 88, NULL, NULL);
00465     sprite_blt(Bitmaps[BITMAP_FOOD], widget->x1 + 87, widget->y1 + 88, &box, NULL);
00466 }
00467 
00471 void widget_skillgroups(widgetdata *widget)
00472 {
00473     SDL_Rect box;
00474 
00475     if (!widget->widgetSF)
00476     {
00477         widget->widgetSF = SDL_ConvertSurface(Bitmaps[BITMAP_SKILL_LVL_BG]->bitmap, Bitmaps[BITMAP_SKILL_LVL_BG]->bitmap->format, Bitmaps[BITMAP_SKILL_LVL_BG]->bitmap->flags);
00478     }
00479 
00480     if (widget->redraw)
00481     {
00482         char buf[MAX_BUF];
00483         _BLTFX bltfx;
00484 
00485         widget->redraw = 0;
00486         bltfx.surface = widget->widgetSF;
00487         bltfx.flags = 0;
00488         bltfx.alpha = 0;
00489         sprite_blt(Bitmaps[BITMAP_SKILL_LVL_BG], 0, 0, NULL, &bltfx);
00490 
00491         string_blt(widget->widgetSF, FONT_MONO10, "Skill", 12, 3, COLOR_HGOLD, TEXT_OUTLINE, NULL);
00492         string_blt(widget->widgetSF, FONT_MONO10, "Groups", 8, 14, COLOR_HGOLD, TEXT_OUTLINE, NULL);
00493 
00494         /* Agility */
00495         string_blt(widget->widgetSF, FONT_ARIAL10, "Ag:", 6, 26, COLOR_HGOLD, 0, NULL);
00496         snprintf(buf, sizeof(buf), "%d", cpl.stats.skill_level[0]);
00497         string_blt(widget->widgetSF, FONT_ARIAL10, buf, 47 - string_get_width(FONT_ARIAL10, buf, 0), 26, COLOR_WHITE, 0, NULL);
00498 
00499         /* Mental */
00500         string_blt(widget->widgetSF, FONT_ARIAL10, "Me:", 6, 38, COLOR_HGOLD, 0, NULL);
00501         snprintf(buf, sizeof(buf), " %d", cpl.stats.skill_level[2]);
00502         string_blt(widget->widgetSF, FONT_ARIAL10, buf, 47 - string_get_width(FONT_ARIAL10, buf, 0), 38, COLOR_WHITE, 0, NULL);
00503 
00504         /* Magic */
00505         string_blt(widget->widgetSF, FONT_ARIAL10, "Ma:", 6, 49, COLOR_HGOLD, 0, NULL);
00506         snprintf(buf, sizeof(buf), " %d", cpl.stats.skill_level[4]);
00507         string_blt(widget->widgetSF, FONT_ARIAL10, buf, 47 - string_get_width(FONT_ARIAL10, buf, 0), 49, COLOR_WHITE, 0, NULL);
00508 
00509         /* Personality */
00510         string_blt(widget->widgetSF, FONT_ARIAL10, "Pe:", 6, 62, COLOR_HGOLD, 0, NULL);
00511         snprintf(buf, sizeof(buf), " %d", cpl.stats.skill_level[1]);
00512         string_blt(widget->widgetSF, FONT_ARIAL10, buf, 47 - string_get_width(FONT_ARIAL10, buf, 0), 62, COLOR_WHITE, 0, NULL);
00513 
00514         /* Physique */
00515         string_blt(widget->widgetSF, FONT_ARIAL10, "Ph:", 6, 74, COLOR_HGOLD, 0, NULL);
00516         snprintf(buf, sizeof(buf), " %d", cpl.stats.skill_level[3]);
00517         string_blt(widget->widgetSF, FONT_ARIAL10, buf, 47 - string_get_width(FONT_ARIAL10, buf, 0), 74, COLOR_WHITE, 0, NULL);
00518 
00519         /* Wisdom */
00520         string_blt(widget->widgetSF, FONT_ARIAL10, "Wi:", 6, 86, COLOR_HGOLD, 0, NULL);
00521         snprintf(buf, sizeof(buf), " %d", cpl.stats.skill_level[5]);
00522         string_blt(widget->widgetSF, FONT_ARIAL10, buf, 47 - string_get_width(FONT_ARIAL10, buf, 0), 86, COLOR_WHITE, 0, NULL);
00523     }
00524 
00525     box.x = widget->x1;
00526     box.y = widget->y1;
00527     SDL_BlitSurface(widget->widgetSF, NULL, ScreenSurface, &box);
00528 }
00529 
00532 void widget_show_player_doll_event()
00533 {
00534     int old_inv_win = cpl.inventory_win, old_inv_tag = cpl.win_inv_tag;
00535     cpl.inventory_win = IWIN_INV;
00536 
00537     if (draggingInvItem(DRAG_GET_STATUS) == DRAG_QUICKSLOT)
00538     {
00539         cpl.win_inv_tag = cpl.dragging.tag;
00540 
00541         /* Drop to player doll */
00542         if (!(object_find(cpl.win_inv_tag)->flags & F_APPLIED))
00543         {
00544             keybind_process_command("?APPLY");
00545         }
00546     }
00547 
00548     if (draggingInvItem(DRAG_GET_STATUS) == DRAG_IWIN_INV)
00549     {
00550         if (object_find(cpl.win_inv_tag)->flags & F_APPLIED)
00551         {
00552             draw_info(COLOR_WHITE, "This is applied already!");
00553         }
00554         /* Drop to player doll */
00555         else
00556         {
00557             keybind_process_command("?APPLY");
00558         }
00559     }
00560 
00561     cpl.inventory_win = old_inv_win;
00562     cpl.win_inv_tag = old_inv_tag;
00563 
00564     draggingInvItem(DRAG_NONE);
00565 }
00566 
00570 void widget_show_player_doll(widgetdata *widget)
00571 {
00572     object *tmp;
00573     char *tooltip_text = NULL;
00574     int idx, tooltip_index = -1, ring_flag = 0;
00575     int mx, my;
00576 
00577     /* This is ugly to calculate because it's a curve which increases heavily
00578      * with lower weapon_speed... So, we use a table */
00579     int ws_temp = cpl.stats.weapon_sp;
00580 
00581     if (ws_temp < 0)
00582     {
00583         ws_temp = 0;
00584     }
00585     else if (ws_temp > 18)
00586     {
00587         ws_temp = 18;
00588     }
00589 
00590     sprite_blt(Bitmaps[BITMAP_DOLL], widget->x1, widget->y1, NULL, NULL);
00591 
00592     if (!cpl.ob)
00593     {
00594         return;
00595     }
00596 
00597     string_blt(ScreenSurface, FONT_SANS12, "<b>Ranged</b>", widget->x1 + 20, widget->y1 + 188, COLOR_HGOLD, TEXT_MARKUP, NULL);
00598     string_blt(ScreenSurface, FONT_ARIAL10, "DMG", widget->x1 + 9, widget->y1 + 205, COLOR_HGOLD, 0, NULL);
00599     string_blt_format(ScreenSurface, FONT_MONO10, widget->x1 + 40, widget->y1 + 205, COLOR_WHITE, 0, NULL, "%02d", cpl.stats.ranged_dam);
00600     string_blt(ScreenSurface, FONT_ARIAL10, "WC", widget->x1 + 10, widget->y1 + 215, COLOR_HGOLD, 0, NULL);
00601     string_blt_format(ScreenSurface, FONT_MONO10, widget->x1 + 40, widget->y1 + 215, COLOR_WHITE, 0, NULL, "%02d", cpl.stats.ranged_wc);
00602     string_blt(ScreenSurface, FONT_ARIAL10, "WS", widget->x1 + 10, widget->y1 + 225, COLOR_HGOLD, 0, NULL);
00603     string_blt_format(ScreenSurface, FONT_MONO10, widget->x1 + 40, widget->y1 + 225, COLOR_WHITE, 0, NULL, "%3.2fs", cpl.stats.ranged_ws / 1000.0);
00604 
00605     string_blt(ScreenSurface, FONT_SANS12, "<b>Melee</b>", widget->x1 + 155, widget->y1 + 188, COLOR_HGOLD, TEXT_MARKUP, NULL);
00606     string_blt(ScreenSurface, FONT_ARIAL10, "DMG", widget->x1 + 139, widget->y1 + 205, COLOR_HGOLD, 0, NULL);
00607     string_blt_format(ScreenSurface, FONT_MONO10, widget->x1 + 170, widget->y1 + 205, COLOR_WHITE, 0, NULL, "%02d", cpl.stats.dam);
00608     string_blt(ScreenSurface, FONT_ARIAL10, "WC", widget->x1 + 140, widget->y1 + 215, COLOR_HGOLD, 0, NULL);
00609     string_blt_format(ScreenSurface, FONT_MONO10, widget->x1 + 170, widget->y1 + 215, COLOR_WHITE, 0, NULL, "%02d", cpl.stats.wc);
00610     string_blt(ScreenSurface, FONT_ARIAL10, "WS", widget->x1 + 140, widget->y1 + 225, COLOR_HGOLD, 0, NULL);
00611     string_blt_format(ScreenSurface, FONT_MONO10, widget->x1 + 170, widget->y1 + 225, COLOR_WHITE, 0, NULL, "%3.2fs", weapon_speed_table[ws_temp]);
00612 
00613     string_blt(ScreenSurface, FONT_ARIAL10, "Speed", widget->x1 + 92, widget->y1 + 193, COLOR_HGOLD, 0, NULL);
00614     string_blt_format(ScreenSurface, FONT_MONO10, widget->x1 + 93, widget->y1 + 205, COLOR_WHITE, 0, NULL, "%3.2f", (float) cpl.stats.speed / FLOAT_MULTF);
00615     string_blt(ScreenSurface, FONT_ARIAL10, "AC", widget->x1 + 92, widget->y1 + 215, COLOR_HGOLD, 0, NULL);
00616     string_blt_format(ScreenSurface, FONT_MONO10, widget->x1 + 92, widget->y1 + 225, COLOR_WHITE, 0, NULL, "%02d", cpl.stats.ac);
00617 
00618     /* Show items applied */
00619     for (tmp = cpl.ob->inv; tmp; tmp = tmp->next)
00620     {
00621         if (tmp->flags & F_APPLIED)
00622         {
00623             idx = -1;
00624 
00625             switch (tmp->itype)
00626             {
00627                 case TYPE_ARMOUR:
00628                     idx = PDOLL_ARMOUR;
00629                     break;
00630 
00631                 case TYPE_HELMET:
00632                     idx = PDOLL_HELM;
00633                     break;
00634 
00635                 case TYPE_GIRDLE:
00636                     idx = PDOLL_GIRDLE;
00637                     break;
00638 
00639                 case TYPE_BOOTS:
00640                     idx = PDOLL_BOOT;
00641                     break;
00642 
00643                 case TYPE_WEAPON:
00644                     idx = PDOLL_RHAND;
00645                     break;
00646 
00647                 case TYPE_SHIELD:
00648                     idx = PDOLL_LHAND;
00649                     break;
00650 
00651                 case TYPE_RING:
00652                     idx = PDOLL_RRING;
00653                     break;
00654 
00655                 case TYPE_BRACERS:
00656                     idx = PDOLL_BRACER;
00657                     break;
00658 
00659                 case TYPE_AMULET:
00660                     idx = PDOLL_AMULET;
00661                     break;
00662 
00663                 case TYPE_SKILL_ITEM:
00664                     idx = PDOLL_SKILL_ITEM;
00665                     break;
00666 
00667                 case TYPE_BOW:
00668                     idx = PDOLL_BOW;
00669                     break;
00670 
00671                 case TYPE_GLOVES:
00672                     idx = PDOLL_GAUNTLET;
00673                     break;
00674 
00675                 case TYPE_CLOAK:
00676                     idx = PDOLL_ROBE;
00677                     break;
00678 
00679                 case TYPE_LIGHT_APPLY:
00680                     idx = PDOLL_LIGHT;
00681                     break;
00682 
00683                 case TYPE_WAND:
00684                 case TYPE_ROD:
00685                 case TYPE_HORN:
00686                     idx = PDOLL_WAND;
00687                     break;
00688             }
00689 
00690             if (idx == PDOLL_RRING)
00691             {
00692                 idx += ++ring_flag & 1;
00693             }
00694 
00695             if (idx != -1)
00696             {
00697                 int mb;
00698                 blt_inv_item_centered(tmp, player_doll[idx].xpos + widget->x1, player_doll[idx].ypos + widget->y1);
00699                 mb = SDL_GetMouseState(&mx, &my);
00700 
00701                 /* Prepare item name tooltip */
00702                 if (mx >= widget->x1 + player_doll[idx].xpos && mx < widget->x1 + player_doll[idx].xpos + 33 && my >= widget->y1 + player_doll[idx].ypos && my < widget->y1 + player_doll[idx].ypos + 33)
00703                 {
00704                     tooltip_index = idx;
00705                     tooltip_text = tmp->s_name;
00706 
00707                     if ((mb & SDL_BUTTON(SDL_BUTTON_LEFT)) && !draggingInvItem(DRAG_GET_STATUS))
00708                     {
00709                         cpl.win_pdoll_tag = tmp->tag;
00710                         draggingInvItem(DRAG_PDOLL);
00711                     }
00712                 }
00713             }
00714         }
00715     }
00716 
00717     /* Draw item name tooltip */
00718     if (tooltip_index != -1)
00719     {
00720         tooltip_create(mx, my, FONT_ARIAL10, tooltip_text);
00721     }
00722 }
00723 
00730 void widget_show_main_lvl(widgetdata *widget)
00731 {
00732     SDL_Rect box;
00733 
00734     if (!widget->widgetSF)
00735     {
00736         widget->widgetSF = SDL_ConvertSurface(Bitmaps[BITMAP_MAIN_LVL_BG]->bitmap, Bitmaps[BITMAP_MAIN_LVL_BG]->bitmap->format, Bitmaps[BITMAP_MAIN_LVL_BG]->bitmap->flags);
00737     }
00738 
00739     if (widget->redraw)
00740     {
00741         char buf[MAX_BUF];
00742         double multi, line;
00743         int s;
00744         sint64 level_exp;
00745         _BLTFX bltfx;
00746 
00747         widget->redraw = 0;
00748         bltfx.surface = widget->widgetSF;
00749         bltfx.flags = 0;
00750         bltfx.alpha = 0;
00751 
00752         sprite_blt(Bitmaps[BITMAP_MAIN_LVL_BG], 0, 0, NULL, &bltfx);
00753 
00754         string_blt(widget->widgetSF, FONT_ARIAL10, "Level / Exp", 5, 5, COLOR_HGOLD, TEXT_OUTLINE, NULL);
00755 
00756         snprintf(buf, sizeof(buf), "<b>%d</b>", cpl.stats.level);
00757         string_blt(widget->widgetSF, FONT_SERIF14, buf, widget->wd - 4 - string_get_width(FONT_SERIF14, buf, TEXT_MARKUP), 4, cpl.stats.level == s_settings->max_level ? COLOR_HGOLD : COLOR_WHITE, TEXT_MARKUP, NULL);
00758 
00759         string_blt_format(widget->widgetSF, FONT_ARIAL10, 5, 20, COLOR_WHITE, 0, NULL, "%"FMT64, cpl.stats.exp);
00760 
00761         /* Calculate the exp bubbles */
00762         level_exp = cpl.stats.exp - s_settings->level_exp[cpl.stats.level];
00763         multi = modf(((double) level_exp / (double) (s_settings->level_exp[cpl.stats.level + 1] - s_settings->level_exp[cpl.stats.level]) * 10.0), &line);
00764 
00765         sprite_blt(Bitmaps[BITMAP_EXP_BORDER], 9, 49, NULL, &bltfx);
00766 
00767         if (multi)
00768         {
00769             box.x = 0;
00770             box.y = 0;
00771             box.h = Bitmaps[BITMAP_EXP_SLIDER]->bitmap->h;
00772             box.w = (int) (Bitmaps[BITMAP_EXP_SLIDER]->bitmap->w * multi);
00773 
00774             if (!box.w)
00775             {
00776                 box.w = 1;
00777             }
00778 
00779             if (box.w > Bitmaps[BITMAP_EXP_SLIDER]->bitmap->w)
00780             {
00781                 box.w = Bitmaps[BITMAP_EXP_SLIDER]->bitmap->w;
00782             }
00783 
00784             sprite_blt(Bitmaps[BITMAP_EXP_SLIDER], 9, 49, &box, &bltfx);
00785         }
00786 
00787         for (s = 0; s < 10; s++)
00788         {
00789             sprite_blt(Bitmaps[BITMAP_EXP_BUBBLE2], 10 + s * 8, 40, NULL, &bltfx);
00790         }
00791 
00792         for (s = 0; s < (int) line; s++)
00793         {
00794             sprite_blt(Bitmaps[BITMAP_EXP_BUBBLE1], 10 + s * 8, 40, NULL, &bltfx);
00795         }
00796     }
00797 
00798     box.x = widget->x1;
00799     box.y = widget->y1;
00800     SDL_BlitSurface(widget->widgetSF, NULL, ScreenSurface, &box);
00801 }
00802 
00806 void widget_show_skill_exp(widgetdata *widget)
00807 {
00808     SDL_Rect box;
00809     static uint32 action_tick = 0;
00810 
00811     /* Pre-emptively tick down the skill delay timer */
00812     if (cpl.action_timer > 0)
00813     {
00814         if (LastTick - action_tick > 125)
00815         {
00816             cpl.action_timer -= (float) (LastTick - action_tick) / 1000.0f;
00817 
00818             if (cpl.action_timer < 0)
00819             {
00820                 cpl.action_timer = 0;
00821             }
00822 
00823             action_tick = LastTick;
00824             WIDGET_REDRAW(widget);
00825         }
00826     }
00827     else
00828     {
00829         action_tick = LastTick;
00830     }
00831 
00832     if (!widget->widgetSF)
00833     {
00834         widget->widgetSF = SDL_ConvertSurface(Bitmaps[BITMAP_SKILL_EXP_BG]->bitmap, Bitmaps[BITMAP_SKILL_EXP_BG]->bitmap->format, Bitmaps[BITMAP_SKILL_EXP_BG]->bitmap->flags);
00835     }
00836 
00837     if (widget->redraw)
00838     {
00839         int s;
00840         sint64 level_exp;
00841         _BLTFX bltfx;
00842         char buf[MAX_BUF];
00843         sint64 liLExp = 0;
00844         sint64 liLExpTNL = 0;
00845         sint64 liTExp = 0;
00846         sint64 liTExpTNL = 0;
00847         double fLExpPercent = 0;
00848         double multi = 0, line = 0;
00849 
00850         widget->redraw = 0;
00851         bltfx.surface = widget->widgetSF;
00852         bltfx.flags = 0;
00853         bltfx.alpha = 0;
00854 
00855         sprite_blt(Bitmaps[BITMAP_SKILL_EXP_BG], 0, 0, NULL, &bltfx);
00856 
00857         string_blt(widget->widgetSF, FONT_ARIAL10, "Used", 4, 0, COLOR_HGOLD, TEXT_OUTLINE, NULL);
00858         string_blt(widget->widgetSF, FONT_ARIAL10, "Skill", 5, 9, COLOR_HGOLD, TEXT_OUTLINE, NULL);
00859 
00860         if (cpl.skill_name[0] != '\0')
00861         {
00862             switch (setting_get_int(OPT_CAT_GENERAL, OPT_EXP_DISPLAY))
00863             {
00864                 /* Default */
00865                 default:
00866                 case 0:
00867                     snprintf(buf, sizeof(buf), "%s", cpl.skill_name);
00868                     break;
00869 
00870                 /* LExp% || LExp/LExp tnl || TExp/TExp tnl || (LExp%) LExp/LExp tnl */
00871                 case 1:
00872                 case 2:
00873                 case 3:
00874                 case 4:
00875                     if (cpl.skill && (cpl.skill->exp >= 0 || cpl.skill->exp == -2))
00876                     {
00877                         snprintf(buf, sizeof(buf), "%s - level: %d", cpl.skill_name, cpl.skill->level);
00878                     }
00879                     else
00880                     {
00881                         snprintf(buf, sizeof(buf), "%s - level: **", cpl.skill_name);
00882                     }
00883 
00884                     break;
00885             }
00886 
00887             string_blt(widget->widgetSF, FONT_ARIAL10, buf, 28, 0, COLOR_WHITE, 0, NULL);
00888 
00889             if (cpl.skill && cpl.skill->exp >= 0)
00890             {
00891                 level_exp = cpl.skill->exp - s_settings->level_exp[cpl.skill->level];
00892                 multi = modf(((double) level_exp / (double) (s_settings->level_exp[cpl.skill->level + 1] - s_settings->level_exp[cpl.skill->level]) * 10.0), &line);
00893 
00894                 liTExp = cpl.skill->exp;
00895                 liTExpTNL = s_settings->level_exp[cpl.skill->level + 1];
00896 
00897                 liLExp = liTExp - s_settings->level_exp[cpl.skill->level];
00898                 liLExpTNL = liTExpTNL - s_settings->level_exp[cpl.skill->level];
00899 
00900                 fLExpPercent = ((float) liLExp / (float) (liLExpTNL)) * 100.0f;
00901             }
00902 
00903             switch (setting_get_int(OPT_CAT_GENERAL, OPT_EXP_DISPLAY))
00904             {
00905                 /* Default */
00906                 default:
00907                 case 0:
00908                     if (cpl.skill && cpl.skill->exp >= 0)
00909                     {
00910                         snprintf(buf, sizeof(buf), "%d / %-9"FMT64, cpl.skill->level, cpl.skill->exp);
00911                     }
00912                     else if (cpl.skill && cpl.skill->exp == -2)
00913                     {
00914                         snprintf(buf, sizeof(buf), "%d / **", cpl.skill->level);
00915                     }
00916                     else
00917                     {
00918                         snprintf(buf, sizeof(buf), "** / **");
00919                     }
00920 
00921                     break;
00922 
00923                 /* LExp% */
00924                 case 1:
00925                     if (cpl.skill && cpl.skill->exp >= 0)
00926                     {
00927                         snprintf(buf, sizeof(buf), "%#05.2f%%", fLExpPercent);
00928                     }
00929                     else
00930                     {
00931                         snprintf(buf, sizeof(buf), "**.**%%");
00932                     }
00933 
00934                     break;
00935 
00936                 /* LExp/LExp tnl */
00937                 case 2:
00938                     if (cpl.skill && cpl.skill->exp >= 0)
00939                     {
00940                         snprintf(buf, sizeof(buf), "%"FMT64" / %"FMT64, liLExp, liLExpTNL);
00941                     }
00942                     else
00943                     {
00944                         snprintf(buf, sizeof(buf), "** / **");
00945                     }
00946 
00947                     break;
00948 
00949                 /* TExp/TExp tnl */
00950                 case 3:
00951                     if (cpl.skill && cpl.skill->exp >= 0)
00952                     {
00953                         snprintf(buf, sizeof(buf), "%"FMT64" / %"FMT64, liTExp, liTExpTNL);
00954                     }
00955                     else
00956                     {
00957                         snprintf(buf, sizeof(buf), "** / **");
00958                     }
00959 
00960                     break;
00961 
00962                 /* (LExp%) LExp/LExp tnl */
00963                 case 4:
00964                     if (cpl.skill && cpl.skill->exp >= 0)
00965                     {
00966                         snprintf(buf, sizeof(buf), "%#05.2f%% - %"FMT64, fLExpPercent, liLExpTNL - liLExp);
00967                     }
00968                     else
00969                     {
00970                         snprintf(buf, sizeof(buf), "(**.**%%) **");
00971                     }
00972 
00973                     break;
00974             }
00975 
00976             if (cpl.skill && (uint32) cpl.skill->level == s_settings->max_level)
00977             {
00978                 strncpy(buf, "Maximum level reached", sizeof(buf) - 1);
00979             }
00980 
00981             string_blt(widget->widgetSF, FONT_ARIAL10, buf, 28, 9, COLOR_WHITE, 0, NULL);
00982 
00983             snprintf(buf, sizeof(buf), "%1.2f sec", cpl.action_timer);
00984             string_blt(widget->widgetSF, FONT_ARIAL10, buf, widget->wd - 3 - string_get_width(FONT_ARIAL10, buf, 0), 0, COLOR_WHITE, 0, NULL);
00985         }
00986 
00987         sprite_blt(Bitmaps[BITMAP_EXP_SKILL_BORDER], 142, 11, NULL, &bltfx);
00988 
00989         if (multi)
00990         {
00991             box.x = 0;
00992             box.y = 0;
00993             box.h = Bitmaps[BITMAP_EXP_SKILL_LINE]->bitmap->h;
00994             box.w = (int) (Bitmaps[BITMAP_EXP_SKILL_LINE]->bitmap->w * multi);
00995 
00996             if (!box.w)
00997             {
00998                 box.w = 1;
00999             }
01000 
01001             if (box.w > Bitmaps[BITMAP_EXP_SKILL_LINE]->bitmap->w)
01002             {
01003                 box.w = Bitmaps[BITMAP_EXP_SKILL_LINE]->bitmap->w;
01004             }
01005 
01006             sprite_blt(Bitmaps[BITMAP_EXP_SKILL_LINE], 145, 18, &box, &bltfx);
01007         }
01008 
01009         if (line > 0)
01010         {
01011             for (s = 0; s < (int) line; s++)
01012             {
01013                 sprite_blt(Bitmaps[BITMAP_EXP_SKILL_BUBBLE], 145 + s * 5, 13, NULL, &bltfx);
01014             }
01015         }
01016     }
01017 
01018     box.x = widget->x1;
01019     box.y = widget->y1;
01020     SDL_BlitSurface(widget->widgetSF, NULL, ScreenSurface, &box);
01021 }
01022 
01026 void widget_show_regeneration(widgetdata *widget)
01027 {
01028     SDL_Rect box;
01029 
01030     if (!widget->widgetSF)
01031     {
01032         widget->widgetSF = SDL_ConvertSurface(Bitmaps[BITMAP_REGEN_BG]->bitmap, Bitmaps[BITMAP_REGEN_BG]->bitmap->format, Bitmaps[BITMAP_REGEN_BG]->bitmap->flags);
01033     }
01034 
01035     if (widget->redraw)
01036     {
01037         char buf[MAX_BUF];
01038         _BLTFX bltfx;
01039 
01040         widget->redraw = 0;
01041         bltfx.surface = widget->widgetSF;
01042         bltfx.flags = 0;
01043         bltfx.alpha = 0;
01044 
01045         sprite_blt(Bitmaps[BITMAP_REGEN_BG], 0, 0, NULL, &bltfx);
01046 
01047         string_blt(widget->widgetSF, FONT_SANS8, "R", 4, 1, COLOR_HGOLD, TEXT_OUTLINE, NULL);
01048         string_blt(widget->widgetSF, FONT_SANS8, "e", 4, 7, COLOR_HGOLD, TEXT_OUTLINE, NULL);
01049         string_blt(widget->widgetSF, FONT_SANS8, "g", 4, 13, COLOR_HGOLD, TEXT_OUTLINE, NULL);
01050         string_blt(widget->widgetSF, FONT_SANS8, "e", 4, 21, COLOR_HGOLD, TEXT_OUTLINE, NULL);
01051         string_blt(widget->widgetSF, FONT_SANS8, "n", 4, 27, COLOR_HGOLD, TEXT_OUTLINE, NULL);
01052 
01053         /* Health */
01054         string_blt(widget->widgetSF, FONT_ARIAL10, "HP:", 13, 3, COLOR_HGOLD, 0, NULL);
01055         snprintf(buf, sizeof(buf), "%2.1f/s", cpl.gen_hp);
01056         string_truncate_overflow(FONT_ARIAL10, buf, 45);
01057         string_blt(widget->widgetSF, FONT_ARIAL10, buf, widget->wd - 5 - string_get_width(FONT_ARIAL10, buf, 0), 3, COLOR_WHITE, 0, NULL);
01058 
01059         /* Mana */
01060         string_blt(widget->widgetSF, FONT_ARIAL10, "Mana:", 13, 13, COLOR_HGOLD, 0, NULL);
01061         snprintf(buf, sizeof(buf), "%2.1f/s", cpl.gen_sp);
01062         string_truncate_overflow(FONT_ARIAL10, buf, 45);
01063         string_blt(widget->widgetSF, FONT_ARIAL10, buf, widget->wd - 5 - string_get_width(FONT_ARIAL10, buf, 0), 13, COLOR_WHITE, 0, NULL);
01064 
01065         /* Grace */
01066         string_blt(widget->widgetSF, FONT_ARIAL10, "Grace:", 13, 23, COLOR_HGOLD, 0, NULL);
01067         snprintf(buf, sizeof(buf), "%2.1f/s", cpl.gen_grace);
01068         string_truncate_overflow(FONT_ARIAL10, buf, 45);
01069         string_blt(widget->widgetSF, FONT_ARIAL10, buf, widget->wd - 5 - string_get_width(FONT_ARIAL10, buf, 0), 23, COLOR_WHITE, 0, NULL);
01070     }
01071 
01072     box.x = widget->x1;
01073     box.y = widget->y1;
01074     SDL_BlitSurface(widget->widgetSF, NULL, ScreenSurface, &box);
01075 }
01076 
01080 void widget_show_container(widgetdata *widget)
01081 {
01082     SDL_Rect box, box2;
01083     int x = widget->x1;
01084     int y = widget->y1;
01085 
01086     /* special case, menuitem is highlighted when mouse is moved over it */
01087     if (widget->WidgetSubtypeID == MENU_ID)
01088     {
01089         widget_highlight_menu(widget);
01090     }
01091 
01092     box.x = box.y = 0;
01093     box.w = widget->wd;
01094     box.h = widget->ht;
01095 
01096     /* if we don't have a backbuffer, create it */
01097     if (!widget->widgetSF)
01098     {
01099         widget->widgetSF = SDL_ConvertSurface(Bitmaps[BITMAP_TEXTWIN_MASK]->bitmap, Bitmaps[BITMAP_TEXTWIN_MASK]->bitmap->format, Bitmaps[BITMAP_TEXTWIN_MASK]->bitmap->flags);
01100     }
01101 
01102     if (widget->redraw)
01103     {
01104         widget->redraw = 0;
01105 
01106         SDL_FillRect(widget->widgetSF, NULL, 0);
01107 
01108         box.x = 0;
01109         box.y = 0;
01110         box.h = 1;
01111         box.w = widget->wd;
01112         SDL_FillRect(widget->widgetSF, &box, SDL_MapRGBA(widget->widgetSF->format, 0x60, 0x60, 0x60, 255));
01113         box.y = widget->ht;
01114         box.h = 1;
01115         box.x = 0;
01116         box.w = widget->wd;
01117         SDL_FillRect(widget->widgetSF, &box, SDL_MapRGBA(widget->widgetSF->format, 0x60, 0x60, 0x60, 255));
01118         box.w = widget->wd;
01119         box.x = box.w - 1;
01120         box.w = 1;
01121         box.y = 0;
01122         box.h = widget->ht;
01123         SDL_FillRect(widget->widgetSF, &box, SDL_MapRGBA(widget->widgetSF->format, 0x60, 0x60, 0x60, 255));
01124         box.x = 0;
01125         box.y = 0;
01126         box.h = widget->ht;
01127         box.w = 1;
01128         SDL_FillRect(widget->widgetSF, &box, SDL_MapRGBA(widget->widgetSF->format, 0x60, 0x60, 0x60, 255));
01129     }
01130 
01131     box.x = x;
01132     box.y = y;
01133     box2.x = 0;
01134     box2.y = 0;
01135     box2.w = widget->wd;
01136     box2.h = widget->ht + 1;
01137 
01138     SDL_BlitSurface(widget->widgetSF, &box2, ScreenSurface, &box);
01139 }
01140 
01141 /* Handles highlighting of menuitems when the cursor is hovering over them. */
01142 void widget_highlight_menu(widgetdata *widget)
01143 {
01144     widgetdata *tmp, *tmp2, *tmp3;
01145     _menu *menu = NULL, *tmp_menu = NULL;
01146     _menuitem *menuitem = NULL;
01147     int visible, create_submenu = 0, x = 0, y = 0;
01148 
01149     /* Sanity check. Make sure widget is a menu. */
01150     if (!widget || widget->WidgetSubtypeID != MENU_ID)
01151     {
01152         return;
01153     }
01154 
01155     /* Check to see if the cursor is hovering over a menuitem or a widget inside it.
01156      * We don't need to go recursive here, just scan the immediate children. */
01157     for (tmp = widget->inv; tmp; tmp = tmp->next)
01158     {
01159         visible = 0;
01160 
01161         /* Menuitem is being directly hovered over. Make the background visible for visual feedback. */
01162         if (tmp == widget_mouse_event.owner)
01163         {
01164             visible = 1;
01165         }
01166         /* The cursor is hovering over something the menuitem contains. This only needs to search the direct children,
01167          * as there should be nothing contained within the children. */
01168         else if (tmp->inv)
01169         {
01170             for (tmp2 = tmp->inv; tmp2; tmp2 = tmp2->next)
01171             {
01172                 if (tmp2 == widget_mouse_event.owner)
01173                 {
01174                     /* The cursor was hovering over something inside the menuitem. */
01175                     visible = 1;
01176                     break;
01177                 }
01178             }
01179         }
01180 
01181         /* Only do any real working if the state of the menuitem changed. */
01182         if (tmp->visible == visible)
01183         {
01184             continue;
01185         }
01186 
01187         menu = MENU(widget);
01188         menuitem = MENUITEM(tmp);
01189 
01190         /* Cursor has just started to hover over the menuitem. */
01191         if (visible)
01192         {
01193             tmp->visible = 1;
01194 
01195             /* If the highlighted menuitem is a submenu, we need to create a submenu next to the menuitem. */
01196             if (menuitem->menu_type == MENU_SUBMENU)
01197             {
01198                 create_submenu = 1;
01199                 x = tmp->x1 + widget->wd - 4;
01200                 y = tmp->y1 - (CONTAINER(widget))->outer_padding_top;
01201             }
01202         }
01203         /* Cursor no longer hovers over the menuitem. */
01204         else
01205         {
01206             tmp->visible = 0;
01207 
01208             /* Let's check if we need to remove the submenu.
01209              * Don't remove it if the cursor is hovering over the submenu itself,
01210              * or a submenu of the submenu, etc. */
01211             if (menuitem->menu_type == MENU_SUBMENU && menu->submenu)
01212             {
01213                 /* This will for sure get the menu that the cursor is hovering over. */
01214                 tmp2 = get_outermost_container(widget_mouse_event.owner);
01215 
01216                 /* Just in case the 'for sure' part of the last comment turns out to be incorrect... */
01217                 if (tmp2 && tmp2->WidgetSubtypeID == MENU_ID)
01218                 {
01219                     /* Loop through the submenus to see if we find a match for the menu the cursor is hovering over. */
01220                     for (tmp_menu = menu; tmp_menu->submenu && tmp_menu->submenu != tmp2; tmp_menu = MENU(tmp_menu->submenu))
01221                     {
01222                     }
01223 
01224                     /* Remove any submenus related to menu if the menu underneath the cursor is not a submenu of menu. */
01225                     if (!tmp_menu->submenu)
01226                     {
01227                         tmp2 = menu->submenu;
01228 
01229                         while (tmp2)
01230                         {
01231                             tmp3 = (MENU(tmp2))->submenu;
01232                             remove_widget_object(tmp2);
01233                             tmp2 = tmp3;
01234                         }
01235 
01236                         menu->submenu = NULL;
01237                     }
01238                     else
01239                     {
01240                         /* Cursor is hovering over the submenu, so leave this menuitem highlighted. */
01241                         tmp->visible = 1;
01242                     }
01243                 }
01244                 /* Cursor is not over a menu, so leave the menuitem containing the submenu highlighted.
01245                  * We want to keep the submenu open, which should reduce annoyance if the user is not precise with the mouse. */
01246                 else
01247                 {
01248                     tmp->visible = 1;
01249                 }
01250             }
01251         }
01252     }
01253 
01254     /* If a submenu needs to be created, create it now. Make sure there can be only one submenu open here. */
01255     if (create_submenu && !menu->submenu)
01256     {
01257         tmp_menu = MENU(widget);
01258 
01259         tmp_menu->submenu = create_menu(x, y, tmp_menu->owner);
01260 
01261         if (tmp_menu->owner->WidgetTypeID == MAIN_INV_ID)
01262         {
01263             add_menuitem(tmp_menu->submenu, "All", &menu_inv_filter_all, MENU_CHECKBOX, inventory_filter == INVENTORY_FILTER_ALL);
01264             add_menuitem(tmp_menu->submenu, "Applied", &menu_inv_filter_applied, MENU_CHECKBOX, inventory_filter & INVENTORY_FILTER_APPLIED);
01265             add_menuitem(tmp_menu->submenu, "Unapplied", &menu_inv_filter_unapplied, MENU_CHECKBOX, inventory_filter & INVENTORY_FILTER_UNAPPLIED);
01266             add_menuitem(tmp_menu->submenu, "Containers", &menu_inv_filter_containers, MENU_CHECKBOX, inventory_filter & INVENTORY_FILTER_CONTAINER);
01267             add_menuitem(tmp_menu->submenu, "Magical", &menu_inv_filter_magical, MENU_CHECKBOX, inventory_filter & INVENTORY_FILTER_MAGICAL);
01268             add_menuitem(tmp_menu->submenu, "Cursed", &menu_inv_filter_cursed, MENU_CHECKBOX, inventory_filter & INVENTORY_FILTER_CURSED);
01269             add_menuitem(tmp_menu->submenu, "Unidentified", &menu_inv_filter_unidentified, MENU_CHECKBOX, inventory_filter & INVENTORY_FILTER_UNIDENTIFIED);
01270             add_menuitem(tmp_menu->submenu, "Locked", &menu_inv_filter_locked, MENU_CHECKBOX, inventory_filter & INVENTORY_FILTER_LOCKED);
01271         }
01272         else
01273         {
01274             /* TODO: Remove this later. It's hardcoded here for testing. */
01275             submenu_chatwindow_filters(tmp_menu->submenu, 0, 0);
01276             add_menuitem(tmp_menu->submenu, "Test", &menu_detach_widget, MENU_SUBMENU, 0);
01277             add_menuitem(tmp_menu->submenu, "Test2", &menu_detach_widget, MENU_SUBMENU, 0);
01278         }
01279 
01280         menu_finalize(tmp_menu->submenu);
01281     }
01282 }
01283 
01285 void widget_menu_event(widgetdata *widget, int x, int y)
01286 {
01287     widgetdata *tmp;
01288     _menuitem *menuitem;
01289 
01290     /* Bypass this code when unnecessary, such as when a menu doesn't exist. */
01291     if (!cur_widget[MENU_ID])
01292     {
01293         return;
01294     }
01295 
01296     tmp = widget;
01297 
01298     /* If the widget isn't a menuitem, the user probably clicked on a widget it contains.
01299      * In that case, the parent is probably the menuitem, so grab that instead. */
01300     if (tmp->WidgetSubtypeID != MENUITEM_ID && tmp->env && tmp->env->WidgetSubtypeID == MENUITEM_ID)
01301     {
01302         tmp = tmp->env;
01303     }
01304 
01305     /* Make sure the menuitem is in a menu first as a sanity check. If so, we found the menu. */
01306     if (tmp->env && tmp->env->WidgetSubtypeID == MENU_ID)
01307     {
01308         menuitem = MENUITEM(tmp);
01309         /* Get the owner of the menu to do the operations on,
01310          * (this is what the user would have right clicked on).
01311          * Make it the mouse owner as this is what the user is interacting with. */
01312         widget_mouse_event.owner = (MENU(tmp->env))->owner;
01313 
01314         if (widget_mouse_event.owner)
01315         {
01316             widget_menuitem_event(widget_mouse_event.owner, x, y, menuitem->menu_func_ptr);
01317         }
01318     }
01319 }
01320 
01322 void widget_menuitem_event(widgetdata *widget, int x, int y, void (*menu_func_ptr)(widgetdata *, int, int))
01323 {
01324     /* sanity check */
01325     if (!menu_func_ptr)
01326     {
01327         return;
01328     }
01329 
01330     menu_func_ptr(widget, x, y);
01331 }
01332 
01333 void widget_show_label(widgetdata *widget)
01334 {
01335     _widget_label *label = LABEL(widget);
01336 
01337     string_blt(ScreenSurface, label->font, label->text, widget->x1, widget->y1, label->color, 0, NULL);
01338 }
01339 
01340 void widget_show_bitmap(widgetdata *widget)
01341 {
01342     _widget_bitmap *bitmap = BITMAP(widget);
01343 
01344     sprite_blt(Bitmaps[bitmap->bitmap_id], widget->x1, widget->y1, NULL, NULL);
01345 }
01346 
01352 int gender_to_id(const char *gender)
01353 {
01354     size_t i;
01355 
01356     for (i = 0; i < GENDER_MAX; i++)
01357     {
01358         if (!strcmp(gender_noun[i], gender))
01359         {
01360             return i;
01361         }
01362     }
01363 
01364     return -1;
01365 }