ui_node_baseinventory.c

Go to the documentation of this file.
00001 
00015 /*
00016 Copyright (C) 2002-2010 UFO: Alien Invasion.
00017 
00018 This program is free software; you can redistribute it and/or
00019 modify it under the terms of the GNU General Public License
00020 as published by the Free Software Foundation; either version 2
00021 of the License, or (at your option) any later version.
00022 
00023 This program is distributed in the hope that it will be useful,
00024 but WITHOUT ANY WARRANTY; without even the implied warranty of
00025 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
00026 
00027 See the GNU General Public License for more details.
00028 
00029 You should have received a copy of the GNU General Public License
00030 along with this program; if not, write to the Free Software
00031 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
00032 
00033 */
00034 
00035 #include "../ui_main.h"
00036 #include "../ui_parse.h"
00037 #include "../ui_actions.h"
00038 #include "../ui_dragndrop.h"
00039 #include "../ui_tooltip.h"
00040 #include "../ui_nodes.h"
00041 #include "../ui_input.h"
00042 #include "../ui_render.h"
00043 #include "ui_node_baseinventory.h"
00044 #include "ui_node_model.h"
00045 #include "ui_node_container.h"
00046 #include "ui_node_abstractnode.h"
00047 
00048 #include "../../client.h"
00049 #include "../../renderer/r_draw.h"
00050 #include "../../renderer/r_mesh.h"
00051 #include "../../cl_game.h"
00052 #include "../../cl_team.h"
00053 #include "../../battlescape/cl_actor.h"
00054 #include "../../cl_inventory.h"
00055 
00056 #define EXTRADATA_TYPE containerExtraData_t
00057 #define EXTRADATA(node) UI_EXTRADATA(node, EXTRADATA_TYPE)
00058 #define EXTRADATACONST(node) UI_EXTRADATACONST(node, EXTRADATA_TYPE)
00059 
00064 static int dragInfoFromX = -1;
00065 static int dragInfoFromY = -1;
00066 
00071 static const invList_t *dragInfoIC;
00072 
00083 static invList_t *UI_ContainerNodeGetExistingItem (const uiNode_t *node, objDef_t *item, const itemFilterTypes_t filterType)
00084 {
00085     return INVSH_SearchInInventoryWithFilter(ui_inventory, EXTRADATACONST(node).container, NONE, NONE, item, filterType);
00086 }
00087 
00091 #define CII_AMMOONLY 0x01
00092 #define CII_WEAPONONLY 0x02     
00093 #define CII_AVAILABLEONLY 0x04
00094 #define CII_NOTAVAILABLEONLY 0x08
00095 #define CII_END 0x80
00096 
00097 typedef struct {
00098     const uiNode_t* node;
00099     byte groupSteps[6];
00100     int groupID;
00101     itemFilterTypes_t filterEquipType;
00102 
00103     int itemID;             
00104     invList_t *itemFound;   
00105 } containerItemIterator_t;
00106 
00113 static void UI_ContainerItemIteratorNext (containerItemIterator_t *iterator)
00114 {
00115     assert(iterator->groupSteps[iterator->groupID] != CII_END);
00116 
00117     /* iterate each groups */
00118     for (; iterator->groupSteps[iterator->groupID] != CII_END; iterator->groupID++) {
00119         int filter = iterator->groupSteps[iterator->groupID];
00120         /* next */
00121         iterator->itemID++;
00122 
00123         /* iterate all item type*/
00124         for (;iterator->itemID < csi.numODs; iterator->itemID++) {
00125             qboolean isAmmo;
00126             qboolean isWeapon;
00127             qboolean isArmour;
00128             objDef_t *obj = INVSH_GetItemByIDX(iterator->itemID);
00129 
00130             /* gameplay filter */
00131             if (!GAME_ItemIsUseable(obj))
00132                 continue;
00133 
00134             /* type filter */
00136             isArmour = INV_IsArmour(obj);
00137             isAmmo = obj->numWeapons != 0 && INV_IsAmmo(obj);
00138             isWeapon = obj->weapon || obj->isMisc || isArmour;
00139 
00140             if ((filter & CII_WEAPONONLY) && !isWeapon)
00141                 continue;
00142             if ((filter & CII_AMMOONLY) && !isAmmo)
00143                 continue;
00144             if (!INV_ItemMatchesFilter(obj, iterator->filterEquipType))
00145                 continue;
00146 
00147             /* exists in inventory filter */
00148             iterator->itemFound = UI_ContainerNodeGetExistingItem(iterator->node, obj, iterator->filterEquipType);
00149             if ((filter & CII_AVAILABLEONLY) && iterator->itemFound == NULL)
00150                 continue;
00151             if ((filter & CII_NOTAVAILABLEONLY) && iterator->itemFound != NULL)
00152                 continue;
00153 
00154             /* we found something */
00155             return;
00156         }
00157 
00158         /* can we search into another group? */
00159         if (iterator->groupSteps[iterator->groupID + 1] != CII_END)
00160             iterator->itemID = -1;
00161     }
00162 
00163     /* clean up */
00164     iterator->itemFound = NULL;
00165 }
00166 
00170 static void UI_ContainerItemIteratorInit (containerItemIterator_t *iterator, const uiNode_t* const node)
00171 {
00172     int groupID = 0;
00173     iterator->itemID = -1;
00174     iterator->groupID = 0;
00175     iterator->node = node;
00176     iterator->filterEquipType = EXTRADATACONST(node).filterEquipType;
00177 
00178     if (EXTRADATACONST(node).displayAvailableOnTop) {
00179         /* available items */
00180         if (EXTRADATACONST(node).displayWeapon)
00181             iterator->groupSteps[groupID++] = CII_WEAPONONLY | CII_AVAILABLEONLY;
00182         if (EXTRADATACONST(node).displayAmmo)
00183             iterator->groupSteps[groupID++] = CII_AMMOONLY | CII_AVAILABLEONLY;
00184         /* unavailable items */
00185         if (EXTRADATACONST(node).displayUnavailableItem) {
00186             if (EXTRADATACONST(node).displayWeapon)
00187                 iterator->groupSteps[groupID++] = CII_WEAPONONLY | CII_NOTAVAILABLEONLY;
00188             if (EXTRADATACONST(node).displayAmmo)
00189                 iterator->groupSteps[groupID++] = CII_AMMOONLY | CII_NOTAVAILABLEONLY;
00190         }
00191     } else {
00192         const int filter = (EXTRADATACONST(node).displayUnavailableItem) ? 0 : CII_AVAILABLEONLY;
00193         if (EXTRADATACONST(node).displayWeapon)
00194             iterator->groupSteps[groupID++] = CII_WEAPONONLY | filter;
00195         if (EXTRADATACONST(node).displayAmmo)
00196             iterator->groupSteps[groupID++] = CII_AMMOONLY | filter;
00197     }
00198     iterator->groupSteps[groupID++] = CII_END;
00199 
00200     /* find the first item */
00201     UI_ContainerItemIteratorNext(iterator);
00202 }
00203 
00210 static void UI_BaseInventoryNodeUpdateScroll (uiNode_t* node)
00211 {
00212     if (EXTRADATA(node).onViewChange) {
00213         UI_ExecuteEventActions(node, EXTRADATA(node).onViewChange);
00214     }
00215 }
00216 
00224 static void UI_GetItemTooltip (item_t item, char *tooltipText, size_t stringMaxLength)
00225 {
00226     objDef_t *weapon;
00227 
00228     assert(item.t);
00229 
00230     if (item.amount > 1)
00231         Com_sprintf(tooltipText, stringMaxLength, "%i x %s\n", item.amount, _(item.t->name));
00232     else
00233         Com_sprintf(tooltipText, stringMaxLength, "%s\n", _(item.t->name));
00234 
00235     /* Only display further info if item.t is researched */
00236     if (GAME_ItemIsUseable(item.t)) {
00237         if (item.t->weapon) {
00238             /* Get info about used ammo (if there is any) */
00239             if (item.t == item.m) {
00240                 /* Item has no ammo but might have shot-count */
00241                 if (item.a) {
00242                     Q_strcat(tooltipText, va(_("Ammo: %i\n"), item.a), stringMaxLength);
00243                 }
00244             } else if (item.m) {
00245                 /* Search for used ammo and display name + ammo count */
00246                 Q_strcat(tooltipText, va(_("%s loaded\n"), _(item.m->name)), stringMaxLength);
00247                 Q_strcat(tooltipText, va(_("Ammo: %i\n"),  item.a), stringMaxLength);
00248             }
00249         } else if (item.t->numWeapons) {
00250             /* Check if this is a non-weapon and non-ammo item */
00251             if (!(item.t->numWeapons == 1 && item.t->weapons[0] == item.t)) {
00252                 int i;
00253                 /* If it's ammo get the weapon names it can be used in */
00254                 Q_strcat(tooltipText, _("Usable in:\n"), stringMaxLength);
00255                 for (i = 0; i < item.t->numWeapons; i++) {
00256                     weapon = item.t->weapons[i];
00257                     if (GAME_ItemIsUseable(weapon)) {
00258                         Q_strcat(tooltipText, va("* %s\n", _(weapon->name)), stringMaxLength);
00259                     }
00260                 }
00261             }
00262         }
00263     }
00264 }
00265 
00271 static void UI_BaseInventoryNodeLoaded (uiNode_t* const node)
00272 {
00273     EXTRADATA(node).container = INVSH_GetInventoryDefinitionByID("equip");
00274 }
00275 
00276 static const vec3_t scale = {3.5, 3.5, 3.5};
00278 static const vec4_t colorDefault = {1, 1, 1, 1};
00279 static const vec4_t colorLoadable = {0.5, 1, 0.5, 1};
00280 static const vec4_t colorDisabled = {0.5, 0.5, 0.5, 1};
00281 static const vec4_t colorDisabledHiden = {0.5, 0.5, 0.5, 0.5};
00282 static const vec4_t colorDisabledLoadable = {0.5, 0.25, 0.25, 1.0};
00283 static const vec4_t colorPreview = { 0.5, 0.5, 1, 1 };  
00289 static int UI_BaseInventoryNodeDrawItems (uiNode_t *node, objDef_t *highlightType)
00290 {
00291     qboolean outOfNode = qfalse;
00292     vec2_t nodepos;
00293     int items = 0;
00294     int rowHeight = 0;
00295     const int cellWidth = node->size[0] / EXTRADATA(node).columns;
00296     containerItemIterator_t iterator;
00297     int currentHeight = 0;
00298     UI_GetNodeAbsPos(node, nodepos);
00299 
00300     UI_ContainerItemIteratorInit(&iterator, node);
00301     for (; iterator.itemID < csi.numODs; UI_ContainerItemIteratorNext(&iterator)) {
00302         const int id = iterator.itemID;
00303         objDef_t *obj = INVSH_GetItemByIDX(id);
00304         item_t tempItem = {1, NULL, obj, 0, 0};
00305         vec3_t pos;
00306         vec3_t ammopos;
00307         const float *color;
00308         qboolean isHighlight = qfalse;
00309         int amount;
00310         const int col = items % EXTRADATA(node).columns;
00311         int cellHeight = 0;
00312         invList_t *icItem = iterator.itemFound;
00313 
00314         /* skip items over and bellow the node view */
00315         if (outOfNode || currentHeight < EXTRADATA(node).scrollCur) {
00316             int height;
00317             R_FontTextSize("f_verysmall", _(obj->name),
00318                 cellWidth - 5, LONGLINES_WRAP, NULL, &height, NULL, NULL);
00319             height += obj->sy * C_UNIT + 10;
00320             if (height > rowHeight)
00321                 rowHeight = height;
00322 
00323             if (outOfNode || currentHeight + rowHeight < EXTRADATA(node).scrollCur) {
00324                 if (col == EXTRADATA(node).columns - 1) {
00325                     currentHeight += rowHeight;
00326                     rowHeight = 0;
00327                 }
00328                 items++;
00329                 continue;
00330             }
00331         }
00332 
00333         Vector2Copy(nodepos, pos);
00334         pos[0] += cellWidth * col;
00335         pos[1] += currentHeight - EXTRADATA(node).scrollCur;
00336         pos[2] = 0;
00337 
00338         if (highlightType) {
00339             if (INV_IsAmmo(obj))
00340                 isHighlight = INVSH_LoadableInWeapon(obj, highlightType);
00341             else
00342                 isHighlight = INVSH_LoadableInWeapon(highlightType, obj);
00343         }
00344 
00345         if (icItem != NULL) {
00346             if (isHighlight)
00347                 color = colorLoadable;
00348             else
00349                 color = colorDefault;
00350         } else {
00351             if (isHighlight)
00352                 color = colorDisabledLoadable;
00353             else
00354                 color = colorDisabledHiden;
00355         }
00356 
00357         if (icItem)
00358             amount = icItem->item.amount;
00359         else
00360             amount = 0;
00361 
00362         /* draw item */
00363         pos[0] += obj->sx * C_UNIT / 2.0;
00364         pos[1] += obj->sy * C_UNIT / 2.0;
00365         UI_DrawItem(node, pos, &tempItem, -1, -1, scale, color);
00366         UI_DrawString("f_verysmall", ALIGN_LC,
00367             pos[0] + obj->sx * C_UNIT / 2.0, pos[1] + obj->sy * C_UNIT / 2.0,
00368             pos[0] + obj->sx * C_UNIT / 2.0, cellWidth - 5, /* maxWidth */
00369             0, va("x%i", amount), 0, 0, NULL, qfalse, 0);
00370         pos[0] -= obj->sx * C_UNIT / 2.0;
00371         pos[1] += obj->sy * C_UNIT / 2.0;
00372         cellHeight += obj->sy * C_UNIT;
00373 
00374         /* save position for ammo */
00375         Vector2Copy(pos, ammopos);
00376         ammopos[2] = 0;
00377         ammopos[0] += obj->sx * C_UNIT + 10;
00378 
00379         /* draw the item name. */
00380         cellHeight += UI_DrawString("f_verysmall", ALIGN_UL,
00381             pos[0], pos[1],
00382             pos[0], cellWidth - 5, /* max width */
00383             0, _(obj->name), 0, 0, NULL, qfalse, LONGLINES_WRAP);
00384 
00385         /* draw ammos of weapon */
00386         if (obj->weapon && EXTRADATA(node).displayAmmoOfWeapon) {
00387             int ammoIdx;
00388             for (ammoIdx = 0; ammoIdx < obj->numAmmos; ammoIdx++) {
00389                 tempItem.t = obj->ammos[ammoIdx];
00390 
00391                 /* skip unusable ammo */
00392                 if (!GAME_ItemIsUseable(tempItem.t))
00393                     continue;
00394 
00395                 /* find and skip none existing ammo */
00396                 icItem = UI_ContainerNodeGetExistingItem(node, tempItem.t, EXTRADATA(node).filterEquipType);
00397                 if (!icItem)
00398                     continue;
00399 
00400                 /* Calculate the center of the item model/image. */
00401                 ammopos[0] += icItem->item.t->sx * C_UNIT / 2.0;
00402                 ammopos[1] -= icItem->item.t->sy * C_UNIT / 2.0;
00403                 UI_DrawItem(node, ammopos, &tempItem, -1, -1, scale, colorDefault);
00404                 UI_DrawString("f_verysmall", ALIGN_LC,
00405                     ammopos[0] + icItem->item.t->sx * C_UNIT / 2.0, ammopos[1] + icItem->item.t->sy * C_UNIT / 2.0,
00406                     ammopos[0] + icItem->item.t->sx * C_UNIT / 2.0, cellWidth - 5 - ammopos[0], /* maxWidth */
00407                     0, va("x%i", icItem->item.amount), 0, 0, NULL, qfalse, 0);
00408                 ammopos[0] += icItem->item.t->sx * C_UNIT / 2.0;
00409                 ammopos[1] += icItem->item.t->sy * C_UNIT / 2.0;
00410             }
00411         }
00412         cellHeight += 10;
00413 
00414         if (cellHeight > rowHeight) {
00415             rowHeight = cellHeight;
00416         }
00417 
00418         /* add a marge between rows */
00419         if (col == EXTRADATA(node).columns - 1) {
00420             currentHeight += rowHeight;
00421             rowHeight = 0;
00422             if (currentHeight - EXTRADATA(node).scrollCur >= node->size[1])
00423                 outOfNode = qtrue;
00424         }
00425 
00426         /* count items */
00427         items++;
00428     }
00429 
00430     if (rowHeight != 0) {
00431         currentHeight += rowHeight;
00432     }
00433     return currentHeight;
00434 }
00435 
00439 static void UI_BaseInventoryNodeDraw2 (uiNode_t *node, objDef_t *highlightType)
00440 {
00441     qboolean updateScroll = qfalse;
00442     int visibleHeight = 0;
00443     int needHeight = 0;
00444     vec2_t pos;
00445 
00446     UI_GetNodeAbsPos(node, pos);
00447     R_PushClipRect(pos[0], pos[1], node->size[0], node->size[1]);
00448 
00449     needHeight = UI_BaseInventoryNodeDrawItems(node, highlightType);
00450 
00451     R_PopClipRect();
00452     visibleHeight = node->size[1];
00453 
00454 #if 0
00455     R_FontDrawString("f_verysmall", ALIGN_UL,
00456         node->pos[0], node->pos[1], node->pos[0], node->pos[1],
00457         0,  0,  /* maxWidth/maxHeight */
00458         0, va("%i %i/%i", EXTRADATA(node).scrollCur, visibleRows, totalRows), 0, 0, NULL, qfalse, 0);
00459 #endif
00460 
00461     /* Update display of scroll buttons if something changed. */
00462     if (visibleHeight != EXTRADATA(node).scrollNum || needHeight != EXTRADATA(node).scrollTotalNum) {
00463         EXTRADATA(node).scrollTotalNum = needHeight;
00464         EXTRADATA(node).scrollNum = visibleHeight;
00465         updateScroll = qtrue;
00466     }
00467     if (EXTRADATA(node).scrollCur > needHeight - visibleHeight) {
00468         EXTRADATA(node).scrollCur = needHeight - visibleHeight;
00469         updateScroll = qtrue;
00470     }
00471     if (EXTRADATA(node).scrollCur < 0) {
00472         EXTRADATA(node).scrollCur = 0;
00473         updateScroll = qtrue;
00474     }
00475 
00476     if (updateScroll)
00477         UI_BaseInventoryNodeUpdateScroll(node);
00478 }
00479 
00483 static void UI_BaseInventoryNodeDraw (uiNode_t *node)
00484 {
00485     objDef_t *highlightType = NULL;
00486 
00487     if (!EXTRADATA(node).container)
00488         return;
00489     if (!ui_inventory)
00490         return;
00491     /* is container invisible */
00492     if (node->color[3] < 0.001)
00493         return;
00494 
00495     /* Highlight weapons that the dragged ammo (if it is one) can be loaded into. */
00496     if (UI_DNDIsDragging() && UI_DNDGetType() == DND_ITEM) {
00497         highlightType = UI_DNDGetItem()->t;
00498     }
00499 
00500     UI_BaseInventoryNodeDraw2(node, highlightType);
00501 }
00502 
00506 static invList_t *UI_BaseInventoryNodeGetItem (const uiNode_t* const node,
00507     int mouseX, int mouseY, int* contX, int* contY)
00508 {
00509     qboolean outOfNode = qfalse;
00510     vec2_t nodepos;
00511     int items = 0;
00512     int rowHeight = 0;
00513     const int cellWidth = node->size[0] / EXTRADATACONST(node).columns;
00514     int tempX, tempY;
00515     containerItemIterator_t iterator;
00516     int currentHeight = 0;
00517 
00518     if (!contX)
00519         contX = &tempX;
00520     if (!contY)
00521         contY = &tempY;
00522 
00523     UI_GetNodeAbsPos(node, nodepos);
00524 
00525     UI_ContainerItemIteratorInit(&iterator, node);
00526     for (; iterator.itemID < csi.numODs; UI_ContainerItemIteratorNext(&iterator)) {
00527         const int id = iterator.itemID;
00528         objDef_t *obj = INVSH_GetItemByIDX(id);
00529         vec2_t pos;
00530         vec2_t ammopos;
00531         const int col = items % EXTRADATACONST(node).columns;
00532         int cellHeight = 0;
00533         invList_t *icItem = iterator.itemFound;
00534         int height;
00535 
00536         /* skip items over and bellow the node view */
00537         if (outOfNode || currentHeight < EXTRADATACONST(node).scrollCur) {
00538             int height;
00539             R_FontTextSize("f_verysmall", _(obj->name),
00540                 cellWidth - 5, LONGLINES_WRAP, NULL, &height, NULL, NULL);
00541             height += obj->sy * C_UNIT + 10;
00542             if (height > rowHeight)
00543                 rowHeight = height;
00544 
00545             if (outOfNode || currentHeight + rowHeight < EXTRADATACONST(node).scrollCur) {
00546                 if (col == EXTRADATACONST(node).columns - 1) {
00547                     currentHeight += rowHeight;
00548                     rowHeight = 0;
00549                 }
00550                 items++;
00551                 continue;
00552             }
00553         }
00554 
00555         Vector2Copy(nodepos, pos);
00556         pos[0] += cellWidth * col;
00557         pos[1] += currentHeight - EXTRADATACONST(node).scrollCur;
00558 
00559         /* check item */
00560         if (mouseY < pos[1])
00561             break;
00562         if (mouseX >= pos[0] && mouseX < pos[0] + obj->sx * C_UNIT
00563          && mouseY >= pos[1] && mouseY < pos[1] + obj->sy * C_UNIT) {
00564             if (icItem) {
00565                 *contX = icItem->x;
00566                 *contY = icItem->y;
00567                 return icItem;
00568             } else {
00569                 return NULL;
00570             }
00571         }
00572         pos[1] += obj->sy * C_UNIT;
00573         cellHeight += obj->sy * C_UNIT;
00574 
00575         /* save position for ammo */
00576         Vector2Copy(pos, ammopos);
00577         ammopos[0] += obj->sx * C_UNIT + 10;
00578 
00579         /* draw the item name. */
00580         R_FontTextSize("f_verysmall", _(obj->name),
00581             cellWidth - 5, LONGLINES_WRAP, NULL, &height, NULL, NULL);
00582         cellHeight += height;
00583 
00584         /* draw ammos of weapon */
00585         if (obj->weapon && EXTRADATACONST(node).displayAmmoOfWeapon) {
00586             int ammoIdx;
00587             for (ammoIdx = 0; ammoIdx < obj->numAmmos; ammoIdx++) {
00588                 objDef_t *objammo = obj->ammos[ammoIdx];
00589 
00590                 /* skip unusable ammo */
00591                 if (!GAME_ItemIsUseable(objammo))
00592                     continue;
00593 
00594                 /* find and skip none existing ammo */
00595                 icItem = UI_ContainerNodeGetExistingItem(node, objammo, EXTRADATACONST(node).filterEquipType);
00596                 if (!icItem)
00597                     continue;
00598 
00599                 /* check ammo (ammopos in on the left-lower corner) */
00600                 if (mouseX < ammopos[0] || mouseY >= ammopos[1])
00601                     break;
00602                 if (mouseX >= ammopos[0] && mouseX < ammopos[0] + objammo->sx * C_UNIT
00603                  && mouseY >= ammopos[1] - objammo->sy * C_UNIT && mouseY < ammopos[1]) {
00604                     *contX = icItem->x;
00605                     *contY = icItem->y;
00606                     return icItem;
00607                 }
00608                 ammopos[0] += objammo->sx * C_UNIT;
00609             }
00610         }
00611         cellHeight += 10;
00612 
00613         if (cellHeight > rowHeight) {
00614             rowHeight = cellHeight;
00615         }
00616 
00617         /* add a margin between rows */
00618         if (col == EXTRADATACONST(node).columns - 1) {
00619             currentHeight += rowHeight;
00620             rowHeight = 0;
00621             if (currentHeight - EXTRADATACONST(node).scrollCur >= node->size[1])
00622                 return NULL;
00623         }
00624 
00625         /* count items */
00626         items++;
00627     }
00628 
00629     *contX = NONE;
00630     *contY = NONE;
00631     return NULL;
00632 }
00633 
00640 static void UI_BaseInventoryNodeDrawTooltip (uiNode_t *node, int x, int y)
00641 {
00642     static char tooltiptext[MAX_VAR * 2];
00643     const invList_t *itemHover;
00644     vec2_t nodepos;
00645 
00646     UI_GetNodeAbsPos(node, nodepos);
00647 
00648     /* Find out where the mouse is. */
00649     itemHover = UI_BaseInventoryNodeGetItem(node, x, y, NULL, NULL);
00650 
00651     if (itemHover) {
00652         const int itemToolTipWidth = 250;
00653 
00654         /* Get name and info about item */
00655         UI_GetItemTooltip(itemHover->item, tooltiptext, sizeof(tooltiptext));
00656 #ifdef DEBUG
00657         /* Display stored container-coordinates of the item. */
00658         Q_strcat(tooltiptext, va("\n%i/%i", itemHover->x, itemHover->y), sizeof(tooltiptext));
00659 #endif
00660         UI_DrawTooltip(tooltiptext, x, y, itemToolTipWidth);
00661     }
00662 }
00663 
00673 static void UI_ContainerNodeAutoPlace (uiNode_t* node, int mouseX, int mouseY)
00674 {
00675     int sel;
00676 #if 0   /* see bellow #1 */
00677     int id;
00678 #endif
00679     invList_t *ic;
00680     int fromX, fromY;
00681 
00682     if (!ui_inventory)
00683         return;
00684 
00685     /* don't allow this in tactical missions */
00686     if (CL_BattlescapeRunning())
00687         return;
00688 
00689     sel = cl_selected->integer;
00690     if (sel < 0)
00691         return;
00692 
00693     assert(EXTRADATA(node).container);
00694 
00695     ic = UI_BaseInventoryNodeGetItem(node, mouseX, mouseY, &fromX, &fromY);
00696     Com_DPrintf(DEBUG_CLIENT, "UI_ContainerNodeAutoPlace: item %i/%i selected from scrollable container.\n", fromX, fromY);
00697     if (!ic)
00698         return;
00699 #if 0   /* see bellow #1 */
00700     id = ic->item.t->idx;
00701 #endif
00702 
00703     /* Right click: automatic item assignment/removal. */
00704     if (EXTRADATA(node).container->id != csi.idEquip) {
00705         if (ic->item.m && ic->item.m != ic->item.t && ic->item.a) {
00706             /* Remove ammo on removing weapon from a soldier */
00707             INV_UnloadWeapon(ic, ui_inventory, INVDEF(csi.idEquip));
00708         } else {
00709             /* Move back to idEquip (ground, floor) container. */
00710             INV_MoveItem(ui_inventory, INVDEF(csi.idEquip), NONE, NONE, EXTRADATA(node).container, ic);
00711         }
00712     } else {
00713         qboolean packed = qfalse;
00714         int px, py;
00715         assert(ic->item.t);
00716         /* armour can only have one target */
00717         if (INV_IsArmour(ic->item.t)) {
00718             packed = INV_MoveItem(ui_inventory, INVDEF(csi.idArmour), 0, 0, EXTRADATA(node).container, ic);
00719         /* ammo or item */
00720         } else if (INV_IsAmmo(ic->item.t)) {
00721             INVSH_FindSpace(ui_inventory, &ic->item, INVDEF(csi.idBelt), &px, &py, NULL);
00722             packed = INV_MoveItem(ui_inventory, INVDEF(csi.idBelt), px, py, EXTRADATA(node).container, ic);
00723             if (!packed) {
00724                 INVSH_FindSpace(ui_inventory, &ic->item, INVDEF(csi.idHolster), &px, &py, NULL);
00725                 packed = INV_MoveItem(ui_inventory, INVDEF(csi.idHolster), px, py, EXTRADATA(node).container, ic);
00726             }
00727             if (!packed) {
00728                 INVSH_FindSpace(ui_inventory, &ic->item, INVDEF(csi.idBackpack), &px, &py, NULL);
00729                 packed = INV_MoveItem( ui_inventory, INVDEF(csi.idBackpack), px, py, EXTRADATA(node).container, ic);
00730             }
00731             /* Finally try left and right hand. There is no other place to put it now. */
00732             if (!packed) {
00733                 const invList_t *rightHand = INVSH_SearchInInventory(ui_inventory, INVDEF(csi.idRight), 0, 0);
00734 
00735                 /* Only try left hand if right hand is empty or no twohanded weapon/item is in it. */
00736                 if (!rightHand || !rightHand->item.t->fireTwoHanded) {
00737                     INVSH_FindSpace(ui_inventory, &ic->item, INVDEF(csi.idLeft), &px, &py, NULL);
00738                     packed = INV_MoveItem(ui_inventory, INVDEF(csi.idLeft), px, py, EXTRADATA(node).container, ic);
00739                 }
00740             }
00741             if (!packed) {
00742                 INVSH_FindSpace(ui_inventory, &ic->item, INVDEF(csi.idRight), &px, &py, NULL);
00743                 packed = INV_MoveItem(ui_inventory, INVDEF(csi.idRight), px, py, EXTRADATA(node).container, ic);
00744             }
00745         } else {
00746             if (ic->item.t->headgear) {
00747                 INVSH_FindSpace(ui_inventory, &ic->item, INVDEF(csi.idHeadgear), &px, &py, NULL);
00748                 packed = INV_MoveItem(ui_inventory, INVDEF(csi.idHeadgear), px, py, EXTRADATA(node).container, ic);
00749             } else {
00750                 /* left and right are single containers, but this might change - it's cleaner to check
00751                  * for available space here, too */
00752                 INVSH_FindSpace(ui_inventory, &ic->item, INVDEF(csi.idRight), &px, &py, NULL);
00753                 packed = INV_MoveItem(ui_inventory, INVDEF(csi.idRight), px, py, EXTRADATA(node).container, ic);
00754                 if (ic->item.t->weapon && !ic->item.a && packed)
00755                     INV_LoadWeapon(ic, ui_inventory, EXTRADATA(node).container, INVDEF(csi.idRight));
00756                 if (!packed) {
00757                     const invList_t *rightHand = INVSH_SearchInInventory(ui_inventory, INVDEF(csi.idRight), 0, 0);
00758 
00759                     /* Only try left hand if right hand is empty or no twohanded weapon/item is in it. */
00760                     if (!rightHand || (rightHand && !rightHand->item.t->fireTwoHanded)) {
00761                         INVSH_FindSpace(ui_inventory, &ic->item, INVDEF(csi.idLeft), &px, &py, NULL);
00762                         packed = INV_MoveItem(ui_inventory, INVDEF(csi.idLeft), px, py, EXTRADATA(node).container, ic);
00763                         if (ic->item.t->weapon && !ic->item.a && packed)
00764                             INV_LoadWeapon(ic, ui_inventory, EXTRADATA(node).container, INVDEF(csi.idLeft));
00765                     }
00766                 }
00767                 if (!packed) {
00768                     INVSH_FindSpace(ui_inventory, &ic->item, INVDEF(csi.idBelt), &px, &py, NULL);
00769                     packed = INV_MoveItem(ui_inventory, INVDEF(csi.idBelt), px, py, EXTRADATA(node).container, ic);
00770                     if (ic->item.t->weapon && !ic->item.a && packed)
00771                         INV_LoadWeapon(ic, ui_inventory, EXTRADATA(node).container, INVDEF(csi.idBelt));
00772                 }
00773                 if (!packed) {
00774                     INVSH_FindSpace(ui_inventory, &ic->item, INVDEF(csi.idHolster), &px, &py, NULL);
00775                     packed = INV_MoveItem(ui_inventory, INVDEF(csi.idHolster), px, py, EXTRADATA(node).container, ic);
00776                     if (ic->item.t->weapon && !ic->item.a && packed)
00777                         INV_LoadWeapon(ic, ui_inventory, EXTRADATA(node).container, INVDEF(csi.idHolster));
00778                 }
00779                 if (!packed) {
00780                     INVSH_FindSpace(ui_inventory, &ic->item, INVDEF(csi.idBackpack), &px, &py, NULL);
00781                     packed = INV_MoveItem(ui_inventory, INVDEF(csi.idBackpack), px, py, EXTRADATA(node).container, ic);
00782                     if (ic->item.t->weapon && !ic->item.a && packed)
00783                         INV_LoadWeapon(ic, ui_inventory, EXTRADATA(node).container, INVDEF(csi.idBackpack));
00784                 }
00785             }
00786         }
00787         /* no need to continue here - placement wasn't successful at all */
00788         if (!packed)
00789             return;
00790     }
00791 
00797     if (INV_IsArmour(ic->item.t)) {
00798         uiNode_t *armour = UI_GetNode(node->root, "armour");
00799         if (armour && armour->onChange)
00800             UI_ExecuteEventActions(armour, armour->onChange);
00801     }
00802 
00803     /* Update display of scroll buttons. */
00804     UI_BaseInventoryNodeUpdateScroll(node);
00805 }
00806 
00807 static int oldMouseX = 0;
00808 static int oldMouseY = 0;
00809 
00810 static void UI_BaseInventoryNodeCapturedMouseMove (uiNode_t *node, int x, int y)
00811 {
00812     const int delta = abs(oldMouseX - x) + abs(oldMouseY - y);
00813     if (delta > 15) {
00814         UI_DNDDragItem(node, &(dragInfoIC->item));
00815         UI_MouseRelease();
00816     }
00817 }
00818 
00819 static void UI_BaseInventoryNodeMouseDown (uiNode_t *node, int x, int y, int button)
00820 {
00821     switch (button) {
00822     case K_MOUSE1:
00823     {
00824         /* start drag and drop */
00825         int fromX, fromY;
00826         dragInfoIC = UI_BaseInventoryNodeGetItem(node, x, y, &fromX, &fromY);
00827         if (dragInfoIC) {
00828             dragInfoFromX = fromX;
00829             dragInfoFromY = fromY;
00830             oldMouseX = x;
00831             oldMouseY = y;
00832             UI_SetMouseCapture(node);
00833             EXTRADATA(node).lastSelectedId = dragInfoIC->item.t->idx;
00834             if (EXTRADATA(node).onSelect) {
00835                 UI_ExecuteEventActions(node, EXTRADATA(node).onSelect);
00836             }
00837         }
00838         break;
00839     }
00840     case K_MOUSE2:
00841         if (UI_DNDIsDragging()) {
00842             UI_DNDAbort();
00843         } else {
00844             /* auto place */
00845             UI_ContainerNodeAutoPlace(node, x, y);
00846         }
00847         break;
00848     default:
00849         break;
00850     }
00851 }
00852 
00853 static void UI_BaseInventoryNodeMouseUp (uiNode_t *node, int x, int y, int button)
00854 {
00855     if (button != K_MOUSE1)
00856         return;
00857     if (UI_GetMouseCapture() == node) {
00858         UI_MouseRelease();
00859     }
00860     if (UI_DNDIsDragging()) {
00861         UI_DNDDrop();
00862     }
00863 }
00864 static void UI_BaseInventoryNodeWheel (uiNode_t *node, qboolean down, int x, int y)
00865 {
00866     const int delta = 20;
00867     if (down) {
00868         const int lenght = EXTRADATA(node).scrollTotalNum - EXTRADATA(node).scrollNum;
00869         if (EXTRADATA(node).scrollCur < lenght) {
00870             EXTRADATA(node).scrollCur += delta;
00871             if (EXTRADATA(node).scrollCur > lenght)
00872                 EXTRADATA(node).scrollCur = lenght;
00873             UI_BaseInventoryNodeUpdateScroll(node);
00874         }
00875     } else {
00876         if (EXTRADATA(node).scrollCur > 0) {
00877             EXTRADATA(node).scrollCur -= delta;
00878             if (EXTRADATA(node).scrollCur < 0)
00879                 EXTRADATA(node).scrollCur = 0;
00880             UI_BaseInventoryNodeUpdateScroll(node);
00881         }
00882     }
00883 }
00884 
00885 static void UI_BaseInventoryNodeLoading (uiNode_t *node)
00886 {
00887     EXTRADATA(node).container = NULL;
00888     EXTRADATA(node).columns = 1;
00889     node->color[3] = 1.0;
00890 }
00891 
00895 static qboolean UI_BaseInventoryNodeDNDEnter (uiNode_t *target)
00896 {
00897     /* The node is invalide */
00898     if (EXTRADATA(target).container == NULL)
00899         return qfalse;
00900     /* accept items only, if we have a container */
00901     return UI_DNDGetType() == DND_ITEM && UI_DNDGetSourceNode() != target;
00902 }
00903 
00908 static qboolean UI_BaseInventoryNodeDNDMove (uiNode_t *target, int x, int y)
00909 {
00910     return qtrue;
00911 }
00912 
00916 static void UI_BaseInventoryNodeDNDLeave (uiNode_t *node)
00917 {
00918 }
00919 
00923 static void UI_BaseInventoryNodeInit (uiNode_t *node)
00924 {
00926     assert(!CL_BattlescapeRunning());
00927 }
00928 
00929 void UI_RegisterBaseInventoryNode (uiBehaviour_t* behaviour)
00930 {
00931     behaviour->name = "baseinventory";
00932     behaviour->extends = "container";
00933     behaviour->draw = UI_BaseInventoryNodeDraw;
00934     behaviour->drawTooltip = UI_BaseInventoryNodeDrawTooltip;
00935     behaviour->mouseDown = UI_BaseInventoryNodeMouseDown;
00936     behaviour->mouseUp = UI_BaseInventoryNodeMouseUp;
00937     behaviour->mouseWheel = UI_BaseInventoryNodeWheel;
00938     behaviour->capturedMouseMove = UI_BaseInventoryNodeCapturedMouseMove;
00939     behaviour->init = UI_BaseInventoryNodeInit;
00940     behaviour->loading = UI_BaseInventoryNodeLoading;
00941     behaviour->loaded = UI_BaseInventoryNodeLoaded;
00942 
00943     behaviour->dndEnter = UI_BaseInventoryNodeDNDEnter;
00944     behaviour->dndMove = UI_BaseInventoryNodeDNDMove;
00945     behaviour->dndLeave = UI_BaseInventoryNodeDNDLeave;
00946 
00947     Com_RegisterConstInt("FILTER_S_PRIMARY", FILTER_S_PRIMARY);
00948     Com_RegisterConstInt("FILTER_S_SECONDARY", FILTER_S_SECONDARY);
00949     Com_RegisterConstInt("FILTER_S_HEAVY", FILTER_S_HEAVY);
00950     Com_RegisterConstInt("FILTER_S_MISC", FILTER_S_MISC);
00951     Com_RegisterConstInt("FILTER_S_ARMOUR", FILTER_S_ARMOUR);
00952     Com_RegisterConstInt("FILTER_CRAFTITEM", FILTER_CRAFTITEM);
00953     Com_RegisterConstInt("FILTER_UGVITEM", FILTER_UGVITEM);
00954     Com_RegisterConstInt("FILTER_AIRCRAFT", FILTER_AIRCRAFT);
00955     Com_RegisterConstInt("FILTER_DUMMY", FILTER_DUMMY);
00956     Com_RegisterConstInt("FILTER_DISASSEMBLY", FILTER_DISASSEMBLY);
00957 }

Generated by  doxygen 1.6.2