ui_node_container.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_model.h"
00044 #include "ui_node_container.h"
00045 #include "ui_node_abstractnode.h"
00046 
00047 #include "../../client.h"
00048 #include "../../renderer/r_draw.h"
00049 #include "../../renderer/r_mesh.h"
00050 #include "../../cl_game.h"
00051 #include "../../cl_team.h"
00052 #include "../../battlescape/cl_actor.h"
00053 #include "../../cl_inventory.h"
00054 
00059 inventory_t *ui_inventory = NULL;
00060 
00061 #define EXTRADATA_TYPE containerExtraData_t
00062 #define EXTRADATA(node) UI_EXTRADATA(node, EXTRADATA_TYPE)
00063 #define EXTRADATACONST(node) UI_EXTRADATACONST(node, EXTRADATA_TYPE)
00064 
00069 static int dragInfoFromX = -1;
00070 static int dragInfoFromY = -1;
00071 
00076 static int dragInfoToX = -1;
00077 static int dragInfoToY = -1;
00078 
00083 static const invList_t *dragInfoIC;
00084 
00095 static invList_t *UI_ContainerNodeGetExistingItem (const uiNode_t *node, objDef_t *item, const itemFilterTypes_t filterType)
00096 {
00097     return INVSH_SearchInInventoryWithFilter(ui_inventory, EXTRADATACONST(node).container, NONE, NONE, item, filterType);
00098 }
00099 
00106 static void UI_ContainerNodeUpdateScroll (uiNode_t* node)
00107 {
00108     if (EXTRADATA(node).onViewChange) {
00109         UI_ExecuteEventActions(node, EXTRADATA(node).onViewChange);
00110     }
00111 }
00112 
00113 static inline qboolean UI_IsScrollContainerNode (const uiNode_t* const node)
00114 {
00115     return EXTRADATACONST(node).container && EXTRADATACONST(node).container->scroll;
00116 }
00117 
00130 void UI_ContainerNodeUpdateEquipment (inventory_t *inv, equipDef_t *ed)
00131 {
00132     int i;
00133 
00134     /* a 'tiny hack' to add the remaining equipment (not carried)
00135      * correctly into buy categories, reloading at the same time;
00136      * it is valid only due to the following property: */
00137     assert(MAX_CONTAINERS >= FILTER_AIRCRAFT);
00138 
00139     for (i = 0; i < csi.numODs; i++) {
00140         objDef_t *od = INVSH_GetItemByIDX(i);
00141         /* Don't allow to show unuseable items. */
00142         if (!GAME_ItemIsUseable(od))
00143             continue;
00144 
00145         while (ed->numItems[i]) {
00146             const item_t item = {NONE_AMMO, NULL, od, 0, 0};
00147             if (!cls.i.AddToInventory(&cls.i, inv, item, INVDEF(csi.idEquip), NONE, NONE, 1)) {
00148                 /* no space left in the inventory */
00149                 break;
00150             }
00151             ed->numItems[item.t->idx]--;
00152         }
00153     }
00154 
00155     /* First-time linking of ui_inventory. */
00156     if (ui_inventory && !ui_inventory->c[csi.idEquip]) {
00157         ui_inventory->c[csi.idEquip] = inv->c[csi.idEquip];
00158     }
00159 }
00160 
00174 void UI_DrawItem (uiNode_t *node, const vec3_t org, const item_t *item, int x, int y, const vec3_t scale, const vec4_t color)
00175 {
00176     objDef_t *od = item->t;
00177     vec4_t col;
00178     vec3_t origin;
00179 
00180     assert(od);
00181     assert(org[2] > -1000 && org[2] < 1000);    /*< prevent use of vec2_t for org */
00182 
00183     Vector4Copy(color, col);
00184     /* no ammo in this weapon - highlight this item */
00185     if (od->weapon && od->reload && !item->a) {
00186         col[1] *= 0.5;
00187         col[2] *= 0.5;
00188     }
00189 
00190     VectorCopy(org, origin);
00191 
00192     /* Calculate correct location of the image or the model (depends on rotation) */
00195     if (x >= 0 || y >= 0) {
00196         /* Add offset of location in container. */
00197         origin[0] += x * C_UNIT;
00198         origin[1] += y * C_UNIT;
00199 
00200         /* Add offset for item-center (depends on rotation). */
00201         if (item->rotated) {
00202             origin[0] += od->sy * C_UNIT / 2.0;
00203             origin[1] += od->sx * C_UNIT / 2.0;
00208         } else {
00209             origin[0] += od->sx * C_UNIT / 2.0;
00210             origin[1] += od->sy * C_UNIT / 2.0;
00211         }
00212     }
00213 
00214     /* don't handle the od->tech->image here - it's very ufopedia specific in most cases */
00215     if (od->image[0] != '\0') {
00216         const int imgWidth = od->sx * C_UNIT;
00217         const int imgHeight = od->sy * C_UNIT;
00218         origin[0] -= od->sx * C_UNIT / 2.0;
00219         origin[1] -= od->sy * C_UNIT / 2.0;
00220 
00221         /* Draw the image. */
00222         R_Color(color);
00223         UI_DrawNormImageByName(origin[0], origin[1], imgWidth, imgHeight, 0, 0, 0, 0, od->image);
00224         R_Color(NULL);
00225     } else {
00226         uiModel_t *model = NULL;
00227         const char *modelName = GAME_GetModelForItem(od, &model);
00228 
00229         /* no model definition in the tech struct, not in the fallback object definition */
00230         if (modelName == NULL || modelName[0] == '\0') {
00231             Com_Printf("UI_DrawItem: No model given for item: '%s'\n", od->id);
00232             return;
00233         }
00234 
00235         if (model && node) {
00236             UI_DrawModelNode(node, modelName);
00237         } else {
00238             modelInfo_t mi;
00239             vec3_t angles = {-10, 160, 70};
00240             vec3_t size = {scale[0], scale[1], scale[2]};
00241             vec3_t center;
00242 
00243             if (item->rotated)
00244                 angles[0] -= 90;
00245 
00246             if (od->scale)
00247                 VectorScale(size, od->scale, size);
00248 
00249             VectorNegate(od->center, center);
00250 
00251             memset(&mi, 0, sizeof(mi));
00252             mi.origin = origin;
00253             mi.angles = angles;
00254             mi.center = center;
00255             mi.scale = size;
00256             mi.color = col;
00257             mi.name = modelName;
00258 
00259             /* draw the model */
00260             R_DrawModelDirect(&mi, NULL, NULL);
00261         }
00262     }
00263 }
00264 
00272 static void UI_GetItemTooltip (item_t item, char *tooltipText, size_t stringMaxLength)
00273 {
00274     objDef_t *weapon;
00275 
00276     assert(item.t);
00277 
00278     if (item.amount > 1)
00279         Com_sprintf(tooltipText, stringMaxLength, "%i x %s\n", item.amount, _(item.t->name));
00280     else
00281         Com_sprintf(tooltipText, stringMaxLength, "%s\n", _(item.t->name));
00282 
00283     /* Only display further info if item.t is researched */
00284     if (GAME_ItemIsUseable(item.t)) {
00285         if (item.t->weapon) {
00286             /* Get info about used ammo (if there is any) */
00287             if (item.t == item.m) {
00288                 /* Item has no ammo but might have shot-count */
00289                 if (item.a) {
00290                     Q_strcat(tooltipText, va(_("Ammo: %i\n"), item.a), stringMaxLength);
00291                 }
00292             } else if (item.m) {
00293                 /* Search for used ammo and display name + ammo count */
00294                 Q_strcat(tooltipText, va(_("%s loaded\n"), _(item.m->name)), stringMaxLength);
00295                 Q_strcat(tooltipText, va(_("Ammo: %i\n"),  item.a), stringMaxLength);
00296             }
00297         } else if (item.t->numWeapons) {
00298             /* Check if this is a non-weapon and non-ammo item */
00299             if (!(item.t->numWeapons == 1 && item.t->weapons[0] == item.t)) {
00300                 int i;
00301                 /* If it's ammo get the weapon names it can be used in */
00302                 Q_strcat(tooltipText, _("Usable in:\n"), stringMaxLength);
00303                 for (i = 0; i < item.t->numWeapons; i++) {
00304                     weapon = item.t->weapons[i];
00305                     if (GAME_ItemIsUseable(weapon)) {
00306                         Q_strcat(tooltipText, va("* %s\n", _(weapon->name)), stringMaxLength);
00307                     }
00308                 }
00309             }
00310         }
00311     }
00312 }
00313 
00317 static void UI_DrawDisabled (const uiNode_t* node)
00318 {
00319     const vec4_t color = { 0.3f, 0.3f, 0.3f, 0.7f };
00320     vec2_t nodepos;
00321 
00322     UI_GetNodeAbsPos(node, nodepos);
00323     UI_DrawFill(nodepos[0], nodepos[1], node->size[0], node->size[1], color);
00324 }
00325 
00329 static void UI_DrawFree (containerIndex_t container, const uiNode_t *node, int posx, int posy, int sizex, int sizey, qboolean showTUs)
00330 {
00331     const vec4_t color = { 0.0f, 1.0f, 0.0f, 0.7f };
00332     invDef_t* inv = INVDEF(container);
00333     vec2_t nodepos;
00334 
00335     UI_GetNodeAbsPos(node, nodepos);
00336     UI_DrawFill(posx, posy, sizex, sizey, color);
00337 
00338     /* if showTUs is true (only the first time in none single containers)
00339      * and we are connected to a game */
00340     if (showTUs && CL_BattlescapeRunning()) {
00341         UI_DrawString("f_verysmall", ALIGN_UL, nodepos[0] + 3, nodepos[1] + 3,
00342             nodepos[0] + 3, node->size[0] - 6, 0,
00343             va(_("In: %i Out: %i"), inv->in, inv->out), 0, 0, NULL, qfalse, 0);
00344     }
00345 }
00346 
00351 static void UI_ContainerNodeDrawFreeSpace (uiNode_t *node, inventory_t *inv)
00352 {
00353     const objDef_t *od = UI_DNDGetItem()->t;    
00354     vec2_t nodepos;
00355 
00356     /* Draw only in dragging-mode and not for the equip-floor */
00357     assert(UI_DNDIsDragging());
00358     assert(inv);
00359 
00360     UI_GetNodeAbsPos(node, nodepos);
00361     /* if single container (hands, extension, headgear) */
00362     if (EXTRADATA(node).container->single) {
00363         /* if container is free or the dragged-item is in it */
00364         if (UI_DNDIsSourceNode(node) || INVSH_CheckToInventory(inv, od, EXTRADATA(node).container, 0, 0, dragInfoIC))
00365             UI_DrawFree(EXTRADATA(node).container->id, node, nodepos[0], nodepos[1], node->size[0], node->size[1], qtrue);
00366     } else {
00367         /* The shape of the free positions. */
00368         uint32_t free[SHAPE_BIG_MAX_HEIGHT];
00369         qboolean showTUs = qtrue;
00370         int x, y;
00371 
00372         memset(free, 0, sizeof(free));
00373 
00374         for (y = 0; y < SHAPE_BIG_MAX_HEIGHT; y++) {
00375             for (x = 0; x < SHAPE_BIG_MAX_WIDTH; x++) {
00376                 /* Check if the current position is usable (topleft of the item). */
00377 
00378                 /* Add '1's to each position the item is 'blocking'. */
00379                 const int checkedTo = INVSH_CheckToInventory(inv, od, EXTRADATA(node).container, x, y, dragInfoIC);
00380                 if (checkedTo & INV_FITS)               /* Item can be placed normally. */
00381                     INVSH_MergeShapes(free, (uint32_t)od->shape, x, y);
00382                 if (checkedTo & INV_FITS_ONLY_ROTATED)  /* Item can be placed rotated. */
00383                     INVSH_MergeShapes(free, INVSH_ShapeRotate((uint32_t)od->shape), x, y);
00384 
00385                 /* Only draw on existing positions. */
00386                 if (INVSH_CheckShape(EXTRADATA(node).container->shape, x, y)) {
00387                     if (INVSH_CheckShape(free, x, y)) {
00388                         UI_DrawFree(EXTRADATA(node).container->id, node, nodepos[0] + x * C_UNIT, nodepos[1] + y * C_UNIT, C_UNIT, C_UNIT, showTUs);
00389                         showTUs = qfalse;
00390                     }
00391                 }
00392             }
00393         }
00394     }
00395 }
00396 
00402 static void UI_ContainerNodeLoaded (uiNode_t* const node)
00403 {
00404     const char *name;
00405     invDef_t *container;
00406 
00408     name = node->name;
00409     if (!strncmp(node->name, "equip_", 6))
00410         name = "equip";
00411 
00412     container = INVSH_GetInventoryDefinitionByID(name);
00413     if (container == NULL)
00414         return;
00415 
00416     EXTRADATA(node).container = container;
00417 
00418     if (UI_IsScrollContainerNode(node)) {
00419         /* No need to compute the size, the script provide it */
00420     } else {
00421         int i, j;
00422         /* Start on the last bit of the shape mask. */
00423         for (i = SHAPE_BIG_MAX_WIDTH - 1; i >= 0; i--) {
00424             for (j = 0; j < SHAPE_BIG_MAX_HEIGHT; j++)
00425                 if (container->shape[j] & (1 << i))
00426                     break;
00427             if (j < SHAPE_BIG_MAX_HEIGHT)
00428                 break;
00429         }
00430         node->size[0] = C_UNIT * (i + 1) + 0.01;
00431 
00432         /* start on the lower row of the shape mask */
00433         for (i = SHAPE_BIG_MAX_HEIGHT - 1; i >= 0; i--)
00434             if (container->shape[i] & ~0x0)
00435                 break;
00436         node->size[1] = C_UNIT * (i + 1) + 0.01;
00437     }
00438 }
00439 
00440 static const vec3_t scale = {3.5, 3.5, 3.5};
00442 static const vec4_t colorDefault = {1, 1, 1, 1};
00443 static const vec4_t colorLoadable = {0.5, 1, 0.5, 1};
00444 static const vec4_t colorDisabled = {0.5, 0.5, 0.5, 1};
00445 static const vec4_t colorDisabledHiden = {0.5, 0.5, 0.5, 0.5};
00446 static const vec4_t colorDisabledLoadable = {0.5, 0.25, 0.25, 1.0};
00447 static const vec4_t colorPreview = { 0.5, 0.5, 1, 1 };  
00454 static void UI_ContainerNodeDrawSingle (uiNode_t *node, objDef_t *highlightType)
00455 {
00456     vec4_t color;
00457     vec3_t pos;
00458 
00459     UI_GetNodeAbsPos(node, pos);
00460     pos[0] += node->size[0] / 2.0;
00461     pos[1] += node->size[1] / 2.0;
00462     pos[2] = 0;
00463 
00464     /* Single item container (special case for left hand). */
00465     if (INV_IsLeftDef(EXTRADATA(node).container) && !ui_inventory->c[csi.idLeft]) {
00466         if (ui_inventory->c[csi.idRight]) {
00467             const item_t *item = &ui_inventory->c[csi.idRight]->item;
00468             assert(item);
00469             assert(item->t);
00470 
00471             if (item->t->holdTwoHanded) {
00472                 if (highlightType && INVSH_LoadableInWeapon(highlightType, item->t))
00473                     memcpy(color, colorLoadable, sizeof(vec4_t));
00474                 else
00475                     memcpy(color, colorDefault, sizeof(vec4_t));
00476                 color[3] = 0.5;
00477                 UI_DrawItem(node, pos, item, -1, -1, scale, color);
00478             }
00479         }
00480     } else if (ui_inventory->c[EXTRADATA(node).container->id]) {
00481         qboolean disabled = qfalse;
00482         const item_t *item;
00483 
00484         if (ui_inventory->c[csi.idRight]) {
00485             item = &ui_inventory->c[csi.idRight]->item;
00486             /* If there is a weapon in the right hand that needs two hands to shoot it
00487              * and there is a weapon in the left, then draw a disabled marker for the
00488              * fireTwoHanded weapon. */
00489             assert(item);
00490             assert(item->t);
00491             if (INV_IsRightDef(EXTRADATA(node).container) && item->t->fireTwoHanded && ui_inventory->c[csi.idLeft]) {
00492                 disabled = qtrue;
00493                 UI_DrawDisabled(node);
00494             }
00495         }
00496 
00497         item = &ui_inventory->c[EXTRADATA(node).container->id]->item;
00498         assert(item);
00499         assert(item->t);
00500         if (highlightType && INVSH_LoadableInWeapon(highlightType, item->t)) {
00501             if (disabled)
00502                 Vector4Copy(colorDisabledLoadable, color);
00503             else
00504                 Vector4Copy(colorLoadable, color);
00505         } else {
00506             if (disabled)
00507                 Vector4Copy(colorDisabled, color);
00508             else
00509                 Vector4Copy(colorDefault, color);
00510         }
00511         if (disabled)
00512             color[3] = 0.5;
00513         UI_DrawItem(node, pos, item, -1, -1, scale, color);
00514     }
00515 }
00516 
00520 static void UI_ContainerNodeDrawGrid (uiNode_t *node, objDef_t *highlightType)
00521 {
00522     const invList_t *ic;
00523     vec3_t pos;
00524 
00525     UI_GetNodeAbsPos(node, pos);
00526     pos[2] = 0;
00527 
00528     for (ic = ui_inventory->c[EXTRADATA(node).container->id]; ic; ic = ic->next) {
00529         assert(ic->item.t);
00530         if (highlightType && INVSH_LoadableInWeapon(highlightType, ic->item.t))
00531             UI_DrawItem(node, pos, &ic->item, ic->x, ic->y, scale, colorLoadable);
00532         else
00533             UI_DrawItem(node, pos, &ic->item, ic->x, ic->y, scale, colorDefault);
00534     }
00535 }
00536 
00540 static void UI_ContainerNodeDrawDropPreview (uiNode_t *target)
00541 {
00542     item_t previewItem;
00543     int checkedTo;
00544     vec3_t origine;
00545 
00546     /* no preview into scrollable list */
00547     if (UI_IsScrollContainerNode(target))
00548         return;
00549 
00550     /* copy the DND item to not change the original one */
00551     previewItem = *UI_DNDGetItem();
00552     previewItem.rotated = qfalse;
00553     checkedTo = INVSH_CheckToInventory(ui_inventory, previewItem.t, EXTRADATA(target).container, dragInfoToX, dragInfoToY, dragInfoIC);
00554     if (checkedTo == INV_FITS_ONLY_ROTATED)
00555         previewItem.rotated = qtrue;
00556 
00557     /* no place found */
00558     if (!checkedTo)
00559         return;
00560 
00561     /* Hack, no preview for armour, we don't want it out of the armour container (and armour container is not visible) */
00562     if (INV_IsArmour(previewItem.t))
00563         return;
00564 
00565     UI_GetNodeAbsPos(target, origine);
00566     origine[2] = -40;
00567 
00568     /* Get center of single container for placement of preview item */
00569     if (EXTRADATA(target).container->single) {
00570         origine[0] += target->size[0] / 2.0;
00571         origine[1] += target->size[1] / 2.0;
00572     /* This is a "grid" container - we need to calculate the item-position
00573      * (on the screen) from stored placement in the container and the
00574      * calculated rotation info. */
00575     } else {
00576         if (previewItem.rotated) {
00577             origine[0] += (dragInfoToX + previewItem.t->sy / 2.0) * C_UNIT;
00578             origine[1] += (dragInfoToY + previewItem.t->sx / 2.0) * C_UNIT;
00579         } else {
00580             origine[0] += (dragInfoToX + previewItem.t->sx / 2.0) * C_UNIT;
00581             origine[1] += (dragInfoToY + previewItem.t->sy / 2.0) * C_UNIT;
00582         }
00583     }
00584 
00585     UI_DrawItem(NULL, origine, &previewItem, -1, -1, scale, colorPreview);
00586 }
00587 
00591 static void UI_ContainerNodeDraw (uiNode_t *node)
00592 {
00593     objDef_t *highlightType = NULL;
00594 
00595     if (!EXTRADATA(node).container)
00596         return;
00597     if (!ui_inventory)
00598         return;
00599     /* is container invisible */
00600     if (node->color[3] < 0.001)
00601         return;
00602 
00603     /* Highlight weapons that the dragged ammo (if it is one) can be loaded into. */
00604     if (UI_DNDIsDragging() && UI_DNDGetType() == DND_ITEM) {
00605         highlightType = UI_DNDGetItem()->t;
00606     }
00607 
00608     if (EXTRADATA(node).container->single) {
00609         UI_ContainerNodeDrawSingle(node, highlightType);
00610     } else {
00611         if (UI_IsScrollContainerNode(node)) {
00612             assert(qfalse);
00613         } else {
00614             UI_ContainerNodeDrawGrid(node, highlightType);
00615         }
00616     }
00617 
00618     /* Draw free space if dragging - but not for idEquip */
00619     if (UI_DNDIsDragging() && EXTRADATA(node).container->id != csi.idEquip)
00620         UI_ContainerNodeDrawFreeSpace(node, ui_inventory);
00621 
00622     if (UI_DNDIsTargetNode(node))
00623         UI_ContainerNodeDrawDropPreview(node);
00624 }
00625 
00635 static invList_t *UI_ContainerNodeGetItemAtPosition (const uiNode_t* const node, int mouseX, int mouseY, int* contX, int* contY)
00636 {
00637     invList_t *result = NULL;
00638     /* Get coordinates inside a scrollable container (if it is one). */
00639     if (UI_IsScrollContainerNode(node)) {
00640         assert(qfalse);
00641     } else {
00642         vec2_t nodepos;
00643         int fromX, fromY;
00644 
00645         UI_GetNodeAbsPos(node, nodepos);
00646         /* Normalize screen coordinates to container coordinates. */
00647         fromX = (int) (mouseX - nodepos[0]) / C_UNIT;
00648         fromY = (int) (mouseY - nodepos[1]) / C_UNIT;
00649         if (contX)
00650             *contX = fromX;
00651         if (contY)
00652             *contY = fromY;
00653 
00654         result = INVSH_SearchInInventory(ui_inventory, EXTRADATACONST(node).container, fromX, fromY);
00655     }
00656     return result;
00657 }
00658 
00665 static void UI_ContainerNodeDrawTooltip (uiNode_t *node, int x, int y)
00666 {
00667     static char tooltiptext[MAX_VAR * 2];
00668     const invList_t *itemHover;
00669     vec2_t nodepos;
00670 
00671     UI_GetNodeAbsPos(node, nodepos);
00672 
00673     /* Find out where the mouse is. */
00674     itemHover = UI_ContainerNodeGetItemAtPosition(node, x, y, NULL, NULL);
00675 
00676     if (itemHover) {
00677         const int itemToolTipWidth = 250;
00678 
00679         /* Get name and info about item */
00680         UI_GetItemTooltip(itemHover->item, tooltiptext, sizeof(tooltiptext));
00681 #ifdef DEBUG
00682         /* Display stored container-coordinates of the item. */
00683         Q_strcat(tooltiptext, va("\n%i/%i", itemHover->x, itemHover->y), sizeof(tooltiptext));
00684 #endif
00685         UI_DrawTooltip(tooltiptext, x, y, itemToolTipWidth);
00686     }
00687 }
00688 
00697 static void UI_ContainerNodeAutoPlace (uiNode_t* node, int mouseX, int mouseY)
00698 {
00699     int sel;
00700 #if 0   /* see bellow #1 */
00701     int id;
00702 #endif
00703     invList_t *ic;
00704     int fromX, fromY;
00705 
00706     if (!ui_inventory)
00707         return;
00708 
00709     /* don't allow this in tactical missions */
00710     if (CL_BattlescapeRunning())
00711         return;
00712 
00713     sel = cl_selected->integer;
00714     if (sel < 0)
00715         return;
00716 
00717     assert(EXTRADATA(node).container);
00718 
00719     ic = UI_ContainerNodeGetItemAtPosition(node, mouseX, mouseY, &fromX, &fromY);
00720     Com_DPrintf(DEBUG_CLIENT, "UI_ContainerNodeAutoPlace: item %i/%i selected from scrollable container.\n", fromX, fromY);
00721     if (!ic)
00722         return;
00723 #if 0   /* see bellow #1 */
00724     id = ic->item.t->idx;
00725 #endif
00726 
00727     /* Right click: automatic item assignment/removal. */
00728     if (EXTRADATA(node).container->id != csi.idEquip) {
00729         if (ic->item.m && ic->item.m != ic->item.t && ic->item.a) {
00730             /* Remove ammo on removing weapon from a soldier */
00731             INV_UnloadWeapon(ic, ui_inventory, INVDEF(csi.idEquip));
00732         } else {
00733             /* Move back to idEquip (ground, floor) container. */
00734             INV_MoveItem(ui_inventory, INVDEF(csi.idEquip), NONE, NONE, EXTRADATA(node).container, ic);
00735         }
00736     } else {
00737         qboolean packed = qfalse;
00738         int px, py;
00739         assert(ic->item.t);
00740         /* armour can only have one target */
00741         if (INV_IsArmour(ic->item.t)) {
00742             packed = INV_MoveItem(ui_inventory, INVDEF(csi.idArmour), 0, 0, EXTRADATA(node).container, ic);
00743         /* ammo or item */
00744         } else if (INV_IsAmmo(ic->item.t)) {
00745             INVSH_FindSpace(ui_inventory, &ic->item, INVDEF(csi.idBelt), &px, &py, NULL);
00746             packed = INV_MoveItem(ui_inventory, INVDEF(csi.idBelt), px, py, EXTRADATA(node).container, ic);
00747             if (!packed) {
00748                 INVSH_FindSpace(ui_inventory, &ic->item, INVDEF(csi.idHolster), &px, &py, NULL);
00749                 packed = INV_MoveItem(ui_inventory, INVDEF(csi.idHolster), px, py, EXTRADATA(node).container, ic);
00750             }
00751             if (!packed) {
00752                 INVSH_FindSpace(ui_inventory, &ic->item, INVDEF(csi.idBackpack), &px, &py, NULL);
00753                 packed = INV_MoveItem( ui_inventory, INVDEF(csi.idBackpack), px, py, EXTRADATA(node).container, ic);
00754             }
00755             /* Finally try left and right hand. There is no other place to put it now. */
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->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                 }
00764             }
00765             if (!packed) {
00766                 INVSH_FindSpace(ui_inventory, &ic->item, INVDEF(csi.idRight), &px, &py, NULL);
00767                 packed = INV_MoveItem(ui_inventory, INVDEF(csi.idRight), px, py, EXTRADATA(node).container, ic);
00768             }
00769         } else {
00770             if (ic->item.t->headgear) {
00771                 INVSH_FindSpace(ui_inventory, &ic->item, INVDEF(csi.idHeadgear), &px, &py, NULL);
00772                 packed = INV_MoveItem(ui_inventory, INVDEF(csi.idHeadgear), px, py, EXTRADATA(node).container, ic);
00773             } else {
00774                 /* left and right are single containers, but this might change - it's cleaner to check
00775                  * for available space here, too */
00776                 INVSH_FindSpace(ui_inventory, &ic->item, INVDEF(csi.idRight), &px, &py, NULL);
00777                 packed = INV_MoveItem(ui_inventory, INVDEF(csi.idRight), px, py, EXTRADATA(node).container, ic);
00778                 if (ic->item.t->weapon && !ic->item.a && packed)
00779                     INV_LoadWeapon(ic, ui_inventory, EXTRADATA(node).container, INVDEF(csi.idRight));
00780                 if (!packed) {
00781                     const invList_t *rightHand = INVSH_SearchInInventory(ui_inventory, INVDEF(csi.idRight), 0, 0);
00782 
00783                     /* Only try left hand if right hand is empty or no twohanded weapon/item is in it. */
00784                     if (!rightHand || (rightHand && !rightHand->item.t->fireTwoHanded)) {
00785                         INVSH_FindSpace(ui_inventory, &ic->item, INVDEF(csi.idLeft), &px, &py, NULL);
00786                         packed = INV_MoveItem(ui_inventory, INVDEF(csi.idLeft), px, py, EXTRADATA(node).container, ic);
00787                         if (ic->item.t->weapon && !ic->item.a && packed)
00788                             INV_LoadWeapon(ic, ui_inventory, EXTRADATA(node).container, INVDEF(csi.idLeft));
00789                     }
00790                 }
00791                 if (!packed) {
00792                     INVSH_FindSpace(ui_inventory, &ic->item, INVDEF(csi.idBelt), &px, &py, NULL);
00793                     packed = INV_MoveItem(ui_inventory, INVDEF(csi.idBelt), px, py, EXTRADATA(node).container, ic);
00794                     if (ic->item.t->weapon && !ic->item.a && packed)
00795                         INV_LoadWeapon(ic, ui_inventory, EXTRADATA(node).container, INVDEF(csi.idBelt));
00796                 }
00797                 if (!packed) {
00798                     INVSH_FindSpace(ui_inventory, &ic->item, INVDEF(csi.idHolster), &px, &py, NULL);
00799                     packed = INV_MoveItem(ui_inventory, INVDEF(csi.idHolster), px, py, EXTRADATA(node).container, ic);
00800                     if (ic->item.t->weapon && !ic->item.a && packed)
00801                         INV_LoadWeapon(ic, ui_inventory, EXTRADATA(node).container, INVDEF(csi.idHolster));
00802                 }
00803                 if (!packed) {
00804                     INVSH_FindSpace(ui_inventory, &ic->item, INVDEF(csi.idBackpack), &px, &py, NULL);
00805                     packed = INV_MoveItem(ui_inventory, INVDEF(csi.idBackpack), px, py, EXTRADATA(node).container, ic);
00806                     if (ic->item.t->weapon && !ic->item.a && packed)
00807                         INV_LoadWeapon(ic, ui_inventory, EXTRADATA(node).container, INVDEF(csi.idBackpack));
00808                 }
00809             }
00810         }
00811         /* no need to continue here - placement wasn't successful at all */
00812         if (!packed)
00813             return;
00814     }
00815 
00821     if (INV_IsArmour(ic->item.t)) {
00822         uiNode_t *armour = UI_GetNode(node->root, "armour");
00823         if (armour && armour->onChange)
00824             UI_ExecuteEventActions(armour, armour->onChange);
00825     }
00826 
00827     /* Update display of scroll buttons. */
00828     if (UI_IsScrollContainerNode(node))
00829         UI_ContainerNodeUpdateScroll(node);
00830 }
00831 
00832 static int oldMouseX = 0;
00833 static int oldMouseY = 0;
00834 
00835 static void UI_ContainerNodeCapturedMouseMove (uiNode_t *node, int x, int y)
00836 {
00837     const int delta = abs(oldMouseX - x) + abs(oldMouseY - y);
00838     if (delta > 15) {
00839         UI_DNDDragItem(node, &(dragInfoIC->item));
00840         UI_MouseRelease();
00841     }
00842 }
00843 
00844 static void UI_ContainerNodeMouseDown (uiNode_t *node, int x, int y, int button)
00845 {
00846     switch (button) {
00847     case K_MOUSE1:
00848     {
00849         /* start drag and drop */
00850         int fromX, fromY;
00851         dragInfoIC = UI_ContainerNodeGetItemAtPosition(node, x, y, &fromX, &fromY);
00852         if (dragInfoIC) {
00853             dragInfoFromX = fromX;
00854             dragInfoFromY = fromY;
00855             oldMouseX = x;
00856             oldMouseY = y;
00857             UI_SetMouseCapture(node);
00858             EXTRADATA(node).lastSelectedId = dragInfoIC->item.t->idx;
00859             if (EXTRADATA(node).onSelect) {
00860                 UI_ExecuteEventActions(node, EXTRADATA(node).onSelect);
00861             }
00862         }
00863         break;
00864     }
00865     case K_MOUSE2:
00866         if (UI_DNDIsDragging()) {
00867             UI_DNDAbort();
00868         } else {
00869             /* auto place */
00870             UI_ContainerNodeAutoPlace(node, x, y);
00871         }
00872         break;
00873     default:
00874         break;
00875     }
00876 }
00877 
00878 static void UI_ContainerNodeMouseUp (uiNode_t *node, int x, int y, int button)
00879 {
00880     if (button != K_MOUSE1)
00881         return;
00882     if (UI_GetMouseCapture() == node) {
00883         UI_MouseRelease();
00884     }
00885     if (UI_DNDIsDragging()) {
00886         UI_DNDDrop();
00887     }
00888 }
00889 static void UI_ContainerNodeWheel (uiNode_t *node, qboolean down, int x, int y)
00890 {
00891     if (UI_IsScrollContainerNode(node)) {
00892         const int delta = 20;
00893         if (down) {
00894             const int lenght = EXTRADATA(node).scrollTotalNum - EXTRADATA(node).scrollNum;
00895             if (EXTRADATA(node).scrollCur < lenght) {
00896                 EXTRADATA(node).scrollCur += delta;
00897                 if (EXTRADATA(node).scrollCur > lenght)
00898                     EXTRADATA(node).scrollCur = lenght;
00899                 UI_ContainerNodeUpdateScroll(node);
00900             }
00901         } else {
00902             if (EXTRADATA(node).scrollCur > 0) {
00903                 EXTRADATA(node).scrollCur -= delta;
00904                 if (EXTRADATA(node).scrollCur < 0)
00905                     EXTRADATA(node).scrollCur = 0;
00906                 UI_ContainerNodeUpdateScroll(node);
00907             }
00908         }
00909     }
00910 }
00911 
00912 static void UI_ContainerNodeLoading (uiNode_t *node)
00913 {
00914     EXTRADATA(node).container = NULL;
00915     EXTRADATA(node).columns = 1;
00916     node->color[3] = 1.0;
00917 }
00918 
00922 static qboolean UI_ContainerNodeDNDEnter (uiNode_t *target)
00923 {
00924     /* accept items only, if we have a container */
00925     return UI_DNDGetType() == DND_ITEM && EXTRADATA(target).container && (!UI_IsScrollContainerNode(target) || UI_DNDGetSourceNode() !=  target);
00926 }
00927 
00932 static qboolean UI_ContainerNodeDNDMove (uiNode_t *target, int x, int y)
00933 {
00934     vec2_t nodepos;
00935     qboolean exists;
00936     int itemX = 0;
00937     int itemY = 0;
00938     item_t *dragItem = UI_DNDGetItem();
00939 
00940     /* we already check it when the node accept the DND */
00941     assert(EXTRADATA(target).container);
00942 
00943     UI_GetNodeAbsPos(target, nodepos);
00944 
00950     if (dragItem->t) {
00951         itemX = C_UNIT * dragItem->t->sx / 2;   /* Half item-width. */
00952         itemY = C_UNIT * dragItem->t->sy / 2;   /* Half item-height. */
00953 
00954         /* Place relative center in the middle of the square. */
00955         itemX -= C_UNIT / 2;
00956         itemY -= C_UNIT / 2;
00957     }
00958 
00959     dragInfoToX = (mousePosX - nodepos[0] - itemX) / C_UNIT;
00960     dragInfoToY = (mousePosY - nodepos[1] - itemY) / C_UNIT;
00961 
00962     /* Check if the items already exists in the container. i.e. there is already at least one item. */
00963     exists = qfalse;
00964     if ((INV_IsFloorDef(EXTRADATA(target).container) || INV_IsEquipDef(EXTRADATA(target).container))
00965         && (dragInfoToX < 0 || dragInfoToY < 0 || dragInfoToX >= SHAPE_BIG_MAX_WIDTH || dragInfoToY >= SHAPE_BIG_MAX_HEIGHT)
00966         && INVSH_ExistsInInventory(ui_inventory, EXTRADATA(target).container, *dragItem)) {
00967         exists = qtrue;
00968     }
00969 
00970     /* Search for a suitable position to render the item at if
00971      * the container is "single", the cursor is out of bound of the container. */
00972     if (!exists && dragItem->t && (EXTRADATA(target).container->single
00973         || dragInfoToX < 0 || dragInfoToY < 0
00974         || dragInfoToX >= SHAPE_BIG_MAX_WIDTH || dragInfoToY >= SHAPE_BIG_MAX_HEIGHT)) {
00975 #if 0
00976 /* ... or there is something in the way. */
00977 /* We would need to check for weapon/ammo as well here, otherwise a preview would be drawn as well when hovering over the correct weapon to reload. */
00978         || (INVSH_CheckToInventory(ui_inventory, dragItem->t, EXTRADATA(target).container, dragInfoToX, dragInfoToY) == INV_DOES_NOT_FIT)) {
00979 #endif
00980         INVSH_FindSpace(ui_inventory, dragItem, EXTRADATA(target).container, &dragInfoToX, &dragInfoToY, dragInfoIC);
00981     }
00982 
00983     /* we can drag every thing */
00984     if (UI_IsScrollContainerNode(target)) {
00985         return qtrue;
00986     }
00987 
00988     {
00989         invList_t *fItem;
00990 
00991         /* is there empty slot? */
00992         const int checkedTo = INVSH_CheckToInventory(ui_inventory, dragItem->t, EXTRADATA(target).container, dragInfoToX, dragInfoToY, dragInfoIC);
00993         if (checkedTo != INV_DOES_NOT_FIT)
00994             return qtrue;
00995 
00996         /* can we equip dragging item into the target item? */
00997         fItem = INVSH_SearchInInventory(ui_inventory, EXTRADATA(target).container, dragInfoToX, dragInfoToY);
00998         if (!fItem)
00999             return qfalse;
01000         if (EXTRADATA(target).container->single)
01001             return qtrue;
01002         return INVSH_LoadableInWeapon(dragItem->t, fItem->item.t);
01003     }
01004 }
01005 
01009 static void UI_ContainerNodeDNDLeave (uiNode_t *node)
01010 {
01011     dragInfoToX = -1;
01012     dragInfoToY = -1;
01013 }
01014 
01018 static qboolean UI_ContainerNodeDNDFinished (uiNode_t *source, qboolean isDropped)
01019 {
01020     item_t *dragItem = UI_DNDGetItem();
01021 
01022     /* if the target can't finalize the DND we stop */
01023     if (!isDropped) {
01024         return qfalse;
01025     }
01026 
01027     /* on tactical mission */
01028     if (CL_BattlescapeRunning()) {
01029         const uiNode_t *target = UI_DNDGetTargetNode();
01030         assert(EXTRADATA(source).container);
01031         assert(target);
01032         assert(EXTRADATACONST(target).container);
01033         assert(selActor);
01034         CL_ActorInvMove(selActor, EXTRADATA(source).container->id, dragInfoFromX, dragInfoFromY,
01035             EXTRADATACONST(target).container->id, dragInfoToX, dragInfoToY);
01036     } else {
01037         uiNode_t *target = UI_DNDGetTargetNode();
01038         if (target) {
01039             invList_t *fItem;
01041             if (UI_IsScrollContainerNode(source)) {
01042                 const int equipType = EXTRADATA(source).filterEquipType;
01043                 fItem = UI_ContainerNodeGetExistingItem(source, dragItem->t, equipType);
01044             } else
01045                 fItem = INVSH_SearchInInventory(ui_inventory, EXTRADATA(source).container, dragInfoFromX, dragInfoFromY);
01046 
01048             assert(EXTRADATA(target).container);
01049             assert(fItem);
01050 
01051             /* Remove ammo on removing weapon from a soldier */
01052             if (UI_IsScrollContainerNode(target) && fItem->item.m && fItem->item.m != fItem->item.t)
01053                 INV_UnloadWeapon(fItem, ui_inventory, EXTRADATA(target).container);
01054             /* move the item */
01055             INV_MoveItem(ui_inventory,
01056                 EXTRADATA(target).container, dragInfoToX, dragInfoToY,
01057                 EXTRADATA(source).container, fItem);
01058             /* Add ammo on adding weapon to a soldier  */
01059             if (UI_IsScrollContainerNode(source) && fItem->item.t->weapon && !fItem->item.a)
01060                 INV_LoadWeapon(fItem, ui_inventory, EXTRADATA(source).container, EXTRADATA(target).container);
01061             /* Run onChange events */
01062             if (source->onChange)
01063                 UI_ExecuteEventActions(source, source->onChange);
01064             if (source != target && target->onChange)
01065                 UI_ExecuteEventActions(target, target->onChange);
01066         }
01067     }
01068 
01069     dragInfoFromX = -1;
01070     dragInfoFromY = -1;
01071     return qtrue;
01072 }
01073 
01074 static const value_t properties[] = {
01075     /* Base container only. Display/hide weapons. */
01076     {"displayweapon", V_BOOL, UI_EXTRADATA_OFFSETOF(containerExtraData_t, displayWeapon),  MEMBER_SIZEOF(containerExtraData_t, displayWeapon)},
01077     /* Base container only. Display/hide ammo. */
01078     {"displayammo", V_BOOL, UI_EXTRADATA_OFFSETOF(containerExtraData_t, displayAmmo),  MEMBER_SIZEOF(containerExtraData_t, displayAmmo)},
01079     /* Base container only. Display/hide out of stock items. */
01080     {"displayunavailableitem", V_BOOL, UI_EXTRADATA_OFFSETOF(containerExtraData_t, displayUnavailableItem),  MEMBER_SIZEOF(containerExtraData_t, displayUnavailableItem)},
01081     /* Base container only. Sort the list to display in stock items on top of the list. */
01082     {"displayavailableontop", V_BOOL, UI_EXTRADATA_OFFSETOF(containerExtraData_t, displayAvailableOnTop),  MEMBER_SIZEOF(containerExtraData_t, displayAvailableOnTop)},
01083     /* Base container only. Display/hide ammo near weapons. */
01084     {"displayammoofweapon", V_BOOL, UI_EXTRADATA_OFFSETOF(containerExtraData_t, displayAmmoOfWeapon),  MEMBER_SIZEOF(containerExtraData_t, displayAmmoOfWeapon)},
01085     /* Base container only. Display/hide out of stock ammo near weapons. <code>displayammoofweapon</code> must be activated first. */
01086     {"displayunavailableammoofweapon", V_BOOL, UI_EXTRADATA_OFFSETOF(containerExtraData_t, displayUnavailableAmmoOfWeapon),  MEMBER_SIZEOF(containerExtraData_t, displayUnavailableAmmoOfWeapon)},
01087     /* Base container only. Custom the number of column we must use to display items. */
01088     {"columns", V_INT, UI_EXTRADATA_OFFSETOF(containerExtraData_t, columns),  MEMBER_SIZEOF(containerExtraData_t, columns)},
01089     /* Base container only. Filter items by a category. */
01090     {"filter", V_INT, UI_EXTRADATA_OFFSETOF(containerExtraData_t, filterEquipType),  MEMBER_SIZEOF(containerExtraData_t, filterEquipType)},
01091 
01092     /* Callback value set before calling onSelect. It is used to know the item selected */
01093     {"lastselectedid", V_INT, UI_EXTRADATA_OFFSETOF(containerExtraData_t, lastSelectedId),  MEMBER_SIZEOF(containerExtraData_t, lastSelectedId)},
01094     /* Callback event called when the user select an item */
01095     {"onselect", V_UI_ACTION, UI_EXTRADATA_OFFSETOF(containerExtraData_t, onSelect),  MEMBER_SIZEOF(containerExtraData_t, onSelect)},
01096 
01097     /* Base container only. Position of the vertical view (into the full number of elements the node contain)
01098      * @todo Rename it viewpos (like scrollable node)
01099      */
01100     {"scrollpos", V_INT, UI_EXTRADATA_OFFSETOF(containerExtraData_t, scrollCur),  MEMBER_SIZEOF(containerExtraData_t, scrollCur)},
01101     /* Base container only. Size of the vertical view (proportional to the number of elements the node can display without moving) */
01102     {"viewsize", V_INT, UI_EXTRADATA_OFFSETOF(containerExtraData_t, scrollNum),  MEMBER_SIZEOF(containerExtraData_t, scrollNum)},
01103     /* Base container only. Full vertical size (proportional to the number of elements the node contain) */
01104     {"fullsize", V_INT, UI_EXTRADATA_OFFSETOF(containerExtraData_t, scrollTotalNum),  MEMBER_SIZEOF(containerExtraData_t, scrollTotalNum)},
01105     /* Base container only. Called when one of the properties viewpos/viewsize/fullsize change */
01106     {"onviewchange", V_UI_ACTION, UI_EXTRADATA_OFFSETOF(containerExtraData_t, onViewChange), MEMBER_SIZEOF(containerExtraData_t, onViewChange)},
01107 
01108     {NULL, V_NULL, 0, 0}
01109 };
01110 
01111 void UI_RegisterContainerNode (uiBehaviour_t* behaviour)
01112 {
01113     behaviour->name = "container";
01114     behaviour->draw = UI_ContainerNodeDraw;
01115     behaviour->drawTooltip = UI_ContainerNodeDrawTooltip;
01116     behaviour->mouseDown = UI_ContainerNodeMouseDown;
01117     behaviour->mouseUp = UI_ContainerNodeMouseUp;
01118     behaviour->capturedMouseMove = UI_ContainerNodeCapturedMouseMove;
01119     behaviour->loading = UI_ContainerNodeLoading;
01120     behaviour->loaded = UI_ContainerNodeLoaded;
01121     behaviour->dndEnter = UI_ContainerNodeDNDEnter;
01122     behaviour->dndFinished = UI_ContainerNodeDNDFinished;
01123     behaviour->dndMove = UI_ContainerNodeDNDMove;
01124     behaviour->dndLeave = UI_ContainerNodeDNDLeave;
01125     behaviour->mouseWheel = UI_ContainerNodeWheel;
01126     behaviour->properties = properties;
01127     behaviour->extraDataSize = sizeof(EXTRADATA_TYPE);
01128 }

Generated by  doxygen 1.6.2