/** * @brief Main function to draw a container node */ void uiContainerNode::draw (uiNode_t *node) { const objDef_t *highlightType = NULL; if (!EXTRADATA(node).container) return; if (!ui_inventory) return; /* is container invisible */ if (node->color[3] < 0.001) return; /* Highlight weapons that the dragged ammo (if it is one) can be loaded into. */ if (UI_DNDIsDragging() && UI_DNDGetType() == DND_ITEM) { highlightType = UI_DNDGetItem()->item; } if (EXTRADATA(node).container->single) { UI_ContainerNodeDrawSingle(node, highlightType); } else { if (UI_IsScrollContainerNode(node)) { assert(false); } else { UI_ContainerNodeDrawGrid(node, highlightType); } } /* Draw free space if dragging - but not for idEquip */ if (UI_DNDIsDragging() && EXTRADATA(node).container->id != csi.idEquip) UI_ContainerNodeDrawFreeSpace(node, ui_inventory); if (UI_DNDIsTargetNode(node)) UI_ContainerNodeDrawDropPreview(node); }
/** * @brief Draw a preview of the DND item dropped into the node */ static void UI_ContainerNodeDrawDropPreview (uiNode_t *target) { item_t previewItem; int checkedTo; vec3_t origine; /* no preview into scrollable list */ if (UI_IsScrollContainerNode(target)) return; /* copy the DND item to not change the original one */ previewItem = *UI_DNDGetItem(); previewItem.rotated = false; checkedTo = INVSH_CheckToInventory(ui_inventory, previewItem.item, EXTRADATA(target).container, dragInfoToX, dragInfoToY, dragInfoIC); switch (checkedTo) { case INV_DOES_NOT_FIT: return; case INV_FITS: break; case INV_FITS_BOTH: /** @todo enable Shift-rotate for battlescape too when issues are solved */ if (!Key_IsDown(K_SHIFT) || CL_BattlescapeRunning()) break; case INV_FITS_ONLY_ROTATED: previewItem.rotated = true; } /* Hack, no preview for armour, we don't want it out of the armour container (and armour container is not visible) */ if (INV_IsArmour(previewItem.item)) return; UI_GetNodeAbsPos(target, origine); origine[2] = -40; /* Get center of single container for placement of preview item */ if (EXTRADATA(target).container->single) { origine[0] += target->box.size[0] / 2.0; origine[1] += target->box.size[1] / 2.0; /* This is a "grid" container - we need to calculate the item-position * (on the screen) from stored placement in the container and the * calculated rotation info. */ } else { if (previewItem.rotated) { origine[0] += (dragInfoToX + previewItem.item->sy / 2.0) * C_UNIT; origine[1] += (dragInfoToY + previewItem.item->sx / 2.0) * C_UNIT; } else { origine[0] += (dragInfoToX + previewItem.item->sx / 2.0) * C_UNIT; origine[1] += (dragInfoToY + previewItem.item->sy / 2.0) * C_UNIT; } } UI_DrawItem(NULL, origine, &previewItem, -1, -1, scale, colorPreview); }
/** * @brief Draws the free and usable inventory positions when dragging an item. * @note Only call this function in dragging mode */ static void UI_ContainerNodeDrawFreeSpace (uiNode_t *node, inventory_t *inv) { const objDef_t *od = UI_DNDGetItem()->item; /**< Get the 'type' of the dragged item. */ vec2_t nodepos; /* Draw only in dragging-mode and not for the equip-floor */ assert(UI_DNDIsDragging()); assert(inv); UI_GetNodeAbsPos(node, nodepos); /* if single container (hands, extension, headgear) */ if (EXTRADATA(node).container->single) { /* if container is free or the dragged-item is in it */ if (UI_DNDIsSourceNode(node) || INVSH_CheckToInventory(inv, od, EXTRADATA(node).container, 0, 0, dragInfoIC)) UI_DrawFree(EXTRADATA(node).container->id, node, nodepos[0], nodepos[1], node->box.size[0], node->box.size[1], true); } else { /* The shape of the free positions. */ uint32_t free[SHAPE_BIG_MAX_HEIGHT]; bool showTUs = true; int x, y; OBJZERO(free); for (y = 0; y < SHAPE_BIG_MAX_HEIGHT; y++) { for (x = 0; x < SHAPE_BIG_MAX_WIDTH; x++) { /* Check if the current position is usable (topleft of the item). */ /* Add '1's to each position the item is 'blocking'. */ const int checkedTo = INVSH_CheckToInventory(inv, od, EXTRADATA(node).container, x, y, dragInfoIC); if (checkedTo & INV_FITS) /* Item can be placed normally. */ INVSH_MergeShapes(free, (uint32_t)od->shape, x, y); if (checkedTo & INV_FITS_ONLY_ROTATED) /* Item can be placed rotated. */ INVSH_MergeShapes(free, INVSH_ShapeRotate((uint32_t)od->shape), x, y); /* Only draw on existing positions. */ if (INVSH_CheckShape(EXTRADATA(node).container->shape, x, y)) { if (INVSH_CheckShape(free, x, y)) { UI_DrawFree(EXTRADATA(node).container->id, node, nodepos[0] + x * C_UNIT, nodepos[1] + y * C_UNIT, C_UNIT, C_UNIT, showTUs); showTUs = false; } } } } } }
/** * @brief Main function to draw a container node */ void uiBaseInventoryNode::draw (uiNode_t* node) { const objDef_t* highlightType = nullptr; if (!EXTRADATA(node).super.container) return; if (!ui_inventory) return; /* is container invisible */ if (node->color[3] < 0.001) return; /* Highlight weapons that the dragged ammo (if it is one) can be loaded into. */ if (UI_DNDIsDragging() && UI_DNDGetType() == DND_ITEM) { highlightType = UI_DNDGetItem()->def(); } UI_BaseInventoryNodeDraw2(node, highlightType); }
/** * @brief Call into the source when the DND end */ bool uiContainerNode::onDndFinished (uiNode_t *source, bool isDropped) { item_t *dragItem = UI_DNDGetItem(); const invDef_t *sourceContainer = EXTRADATACONST(source).container; /* if the target can't finalize the DND we stop */ if (!isDropped) { return false; } assert(sourceContainer); /* on tactical mission */ if (CL_BattlescapeRunning()) { const uiNode_t *target = UI_DNDGetTargetNode(); const invDef_t *targetContainer = EXTRADATACONST(target).container; assert(targetContainer); CL_ActorInvMove(selActor, sourceContainer->id, dragInfoFromX, dragInfoFromY, targetContainer->id, dragInfoToX, dragInfoToY); } else { uiNode_t *target = UI_DNDGetTargetNode(); if (target) { invList_t *fItem; invList_t *tItem; bool moved = false; const invDef_t *targetContainer = EXTRADATACONST(target).container; assert(targetContainer); if (UI_IsScrollContainerNode(source)) { fItem = UI_ContainerNodeGetExistingItem(sourceContainer, dragItem->item, MAX_FILTERTYPES); } else fItem = INVSH_SearchInInventory(ui_inventory, sourceContainer, dragInfoFromX, dragInfoFromY); assert(fItem); /** @todo We must split the move in two. Here, we should not know how to add the item to the target (see dndDrop) */ /* Remove ammo on removing weapon from a soldier */ if (UI_IsScrollContainerNode(target) && fItem->item.ammo && fItem->item.ammo != fItem->item.item && fItem->item.ammoLeft) INV_UnloadWeapon(fItem, ui_inventory, targetContainer); /* rotate on Shift */ /** @todo enable Shift-rotate for battlescape too when issues are solved */ fItem->item.rotated = Key_IsDown(K_SHIFT) && !CL_BattlescapeRunning(); /* move the item */ moved = INV_MoveItem(ui_inventory, targetContainer, dragInfoToX, dragInfoToY, sourceContainer, fItem, &tItem); /* No need to continue move wasn't successful */ if (!moved) return false; /* Add ammo on adding weapon to a soldier */ if (UI_IsScrollContainerNode(source) && ((fItem->item.item->weapon && !fItem->item.ammoLeft) || fItem->item.item->oneshot)) INV_LoadWeapon(tItem, ui_inventory, sourceContainer, targetContainer); /* Run onChange events */ if (source->onChange) UI_ExecuteEventActions(source, source->onChange); if (source != target && target->onChange) UI_ExecuteEventActions(target, target->onChange); } } dragInfoFromX = -1; dragInfoFromY = -1; return true; }
/** * @brief Call into the target when the DND hover it * @return True if the DND is accepted */ bool uiContainerNode::onDndMove (uiNode_t *target, int x, int y) { vec2_t nodepos; bool exists; int itemX = 0; int itemY = 0; item_t *dragItem = UI_DNDGetItem(); /* we already check it when the node accept the DND */ assert(EXTRADATA(target).container); UI_GetNodeAbsPos(target, nodepos); /** We calculate the position of the top-left corner of the dragged * item in oder to compensate for the centered-drawn cursor-item. * Or to be more exact, we calculate the relative offset from the cursor * location to the middle of the top-left square of the item. * @sa UI_LeftClick */ if (dragItem->item) { itemX = C_UNIT * dragItem->item->sx / 2; /* Half item-width. */ itemY = C_UNIT * dragItem->item->sy / 2; /* Half item-height. */ /* Place relative center in the middle of the square. */ itemX -= C_UNIT / 2; itemY -= C_UNIT / 2; } dragInfoToX = (mousePosX - nodepos[0] - itemX) / C_UNIT; dragInfoToY = (mousePosY - nodepos[1] - itemY) / C_UNIT; /* Check if the items already exists in the container. i.e. there is already at least one item. */ exists = false; if ((INV_IsFloorDef(EXTRADATA(target).container) || INV_IsEquipDef(EXTRADATA(target).container)) && (dragInfoToX < 0 || dragInfoToY < 0 || dragInfoToX >= SHAPE_BIG_MAX_WIDTH || dragInfoToY >= SHAPE_BIG_MAX_HEIGHT) && INVSH_ExistsInInventory(ui_inventory, EXTRADATA(target).container, dragItem)) { exists = true; } /* Search for a suitable position to render the item at if * the container is "single", the cursor is out of bound of the container. */ if (!exists && dragItem->item && (EXTRADATA(target).container->single || dragInfoToX < 0 || dragInfoToY < 0 || dragInfoToX >= SHAPE_BIG_MAX_WIDTH || dragInfoToY >= SHAPE_BIG_MAX_HEIGHT)) { #if 0 /* ... or there is something in the way. */ /* 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. */ || (INVSH_CheckToInventory(ui_inventory, dragItem->item, EXTRADATA(target).container, dragInfoToX, dragInfoToY) == INV_DOES_NOT_FIT)) { #endif INVSH_FindSpace(ui_inventory, dragItem, EXTRADATA(target).container, &dragInfoToX, &dragInfoToY, dragInfoIC); } /* we can drag every thing */ if (UI_IsScrollContainerNode(target)) { return true; } { invList_t *fItem; /* is there empty slot? */ const int checkedTo = INVSH_CheckToInventory(ui_inventory, dragItem->item, EXTRADATA(target).container, dragInfoToX, dragInfoToY, dragInfoIC); if (checkedTo != INV_DOES_NOT_FIT) return true; /* can we equip dragging item into the target item? */ fItem = INVSH_SearchInInventory(ui_inventory, EXTRADATA(target).container, dragInfoToX, dragInfoToY); if (!fItem) return false; if (EXTRADATA(target).container->single) return true; return INVSH_LoadableInWeapon(dragItem->item, fItem->item.item); } } /** * @brief Call when a DND enter into the node */ void uiContainerNode::onDndLeave (uiNode_t *node) { dragInfoToX = -1; dragInfoToY = -1; }