/** * @brief Compute the next itemID * @note If something found, item type can be find with iterator->itemID * @note If item is available into the container iterator->itemFound point to this element * @note If nothing found (no next element) then iterator->itemID >= csi.numODs */ static void UI_ContainerItemIteratorNext (containerItemIterator_t* iterator) { assert(iterator->groupSteps[iterator->groupID] != CII_END); /* iterate each groups */ for (; iterator->groupSteps[iterator->groupID] != CII_END; iterator->groupID++) { int filter = iterator->groupSteps[iterator->groupID]; /* next */ iterator->itemID++; /* iterate all item type*/ for (;iterator->itemID < csi.numODs; iterator->itemID++) { const objDef_t* obj = INVSH_GetItemByIDX(iterator->itemID); /* gameplay filter */ if (!GAME_ItemIsUseable(obj)) continue; /* type filter */ /** @todo not sure its the right check */ const bool isArmour = obj->isArmour(); const bool isAmmo = obj->numWeapons != 0 && obj->isAmmo(); const bool isWeapon = obj->weapon || obj->isMisc || isArmour; const bool isImplant = obj->implant; if ((filter & CII_WEAPONONLY) && !isWeapon) continue; if ((filter & CII_AMMOONLY) && !isAmmo) continue; if ((filter & CII_IMPLANTONLY) && !isImplant) continue; if (!INV_ItemMatchesFilter(obj, iterator->filterEquipType)) continue; /* exists in inventory filter */ iterator->itemFound = UI_ContainerNodeGetExistingItem(iterator->node, obj, iterator->filterEquipType); if ((filter & CII_AVAILABLEONLY) && iterator->itemFound == nullptr) continue; if ((filter & CII_NOTAVAILABLEONLY) && iterator->itemFound != nullptr) continue; /* we found something */ return; } /* can we search into another group? */ if (iterator->groupSteps[iterator->groupID + 1] != CII_END) iterator->itemID = -1; } /* clean up */ iterator->itemFound = nullptr; }
/** * @note this function is a copy-paste of UI_ContainerNodeDrawItems (with remove of unneeded code) */ static Item* UI_BaseInventoryNodeGetItem (const uiNode_t* const node, int mouseX, int mouseY, int* contX, int* contY) { bool outOfNode = false; vec2_t nodepos; int items = 0; int rowHeight = 0; const int cellWidth = node->box.size[0] / EXTRADATACONST(node).columns; int tempX, tempY; containerItemIterator_t iterator; int currentHeight = 0; if (!contX) contX = &tempX; if (!contY) contY = &tempY; UI_GetNodeAbsPos(node, nodepos); UI_ContainerItemIteratorInit(&iterator, node); for (; iterator.itemID < csi.numODs; UI_ContainerItemIteratorNext(&iterator)) { const int id = iterator.itemID; const objDef_t* obj = INVSH_GetItemByIDX(id); vec2_t pos; vec2_t ammopos; const int col = items % EXTRADATACONST(node).columns; int cellHeight = 0; Item* icItem = iterator.itemFound; int height; /* skip items over and bellow the node view */ if (outOfNode || currentHeight < EXTRADATACONST(node).scrollY.viewPos) { int outHeight; R_FontTextSize("f_verysmall", _(obj->name), cellWidth - 5, LONGLINES_WRAP, nullptr, &outHeight, nullptr, nullptr); outHeight += obj->sy * C_UNIT + 10; if (outHeight > rowHeight) rowHeight = outHeight; if (outOfNode || currentHeight + rowHeight < EXTRADATACONST(node).scrollY.viewPos) { if (col == EXTRADATACONST(node).columns - 1) { currentHeight += rowHeight; rowHeight = 0; } items++; continue; } } Vector2Copy(nodepos, pos); pos[0] += cellWidth * col; pos[1] += currentHeight - EXTRADATACONST(node).scrollY.viewPos; /* check item */ if (mouseY < pos[1]) break; if (mouseX >= pos[0] && mouseX < pos[0] + obj->sx * C_UNIT && mouseY >= pos[1] && mouseY < pos[1] + obj->sy * C_UNIT) { if (icItem) { *contX = icItem->getX(); *contY = icItem->getY(); return icItem; } return nullptr; } pos[1] += obj->sy * C_UNIT; cellHeight += obj->sy * C_UNIT; /* save position for ammo */ Vector2Copy(pos, ammopos); ammopos[0] += obj->sx * C_UNIT + 10; /* draw the item name. */ R_FontTextSize("f_verysmall", _(obj->name), cellWidth - 5, LONGLINES_WRAP, nullptr, &height, nullptr, nullptr); cellHeight += height; /* draw ammos of weapon */ if (obj->weapon && EXTRADATACONST(node).displayAmmoOfWeapon) { for (int ammoIdx = 0; ammoIdx < obj->numAmmos; ammoIdx++) { const objDef_t* objammo = obj->ammos[ammoIdx]; /* skip unusable ammo */ if (!GAME_ItemIsUseable(objammo)) continue; /* find and skip none existing ammo */ icItem = UI_ContainerNodeGetExistingItem(node, objammo, (itemFilterTypes_t) EXTRADATACONST(node).filterEquipType); if (!icItem) continue; /* check ammo (ammopos in on the left-lower corner) */ if (mouseX < ammopos[0] || mouseY >= ammopos[1]) break; if (mouseX >= ammopos[0] && mouseX < ammopos[0] + objammo->sx * C_UNIT && mouseY >= ammopos[1] - objammo->sy * C_UNIT && mouseY < ammopos[1]) { *contX = icItem->getX(); *contY = icItem->getY(); return icItem; } ammopos[0] += objammo->sx * C_UNIT; } } cellHeight += 10; if (cellHeight > rowHeight) { rowHeight = cellHeight; } /* add a margin between rows */ if (col == EXTRADATACONST(node).columns - 1) { currentHeight += rowHeight; rowHeight = 0; if (currentHeight - EXTRADATACONST(node).scrollY.viewPos >= node->box.size[1]) return nullptr; } /* count items */ items++; } *contX = NONE; *contY = NONE; return nullptr; }
/** * @brief Draw the base inventory * @return The full height requested by the current view (not the node height) */ static int UI_BaseInventoryNodeDrawItems (uiNode_t* node, const objDef_t* highlightType) { bool outOfNode = false; vec2_t nodepos; int items = 0; int rowHeight = 0; const int cellWidth = node->box.size[0] / EXTRADATA(node).columns; containerItemIterator_t iterator; int currentHeight = 0; UI_GetNodeAbsPos(node, nodepos); UI_ContainerItemIteratorInit(&iterator, node); for (; iterator.itemID < csi.numODs; UI_ContainerItemIteratorNext(&iterator)) { const int id = iterator.itemID; const objDef_t* obj = INVSH_GetItemByIDX(id); Item tempItem(obj, nullptr, 1); vec3_t pos; vec3_t ammopos; const float* color; bool isHighlight = false; int amount; const int col = items % EXTRADATA(node).columns; int cellHeight = 0; const Item* icItem = iterator.itemFound; /* skip items over and bellow the node view */ if (outOfNode || currentHeight < EXTRADATA(node).scrollY.viewPos) { int height; R_FontTextSize("f_verysmall", _(obj->name), cellWidth - 5, LONGLINES_WRAP, nullptr, &height, nullptr, nullptr); height += obj->sy * C_UNIT + 10; if (height > rowHeight) rowHeight = height; if (outOfNode || currentHeight + rowHeight < EXTRADATA(node).scrollY.viewPos) { if (col == EXTRADATA(node).columns - 1) { currentHeight += rowHeight; rowHeight = 0; } items++; continue; } } Vector2Copy(nodepos, pos); pos[0] += cellWidth * col; pos[1] += currentHeight - EXTRADATA(node).scrollY.viewPos; pos[2] = 0; if (highlightType) { if (obj->isAmmo()) isHighlight = obj->isLoadableInWeapon(highlightType); else isHighlight = highlightType->isLoadableInWeapon(obj); } if (icItem != nullptr) { if (isHighlight) color = colorLoadable; else color = colorDefault; } else { if (isHighlight) color = colorDisabledLoadable; else color = colorDisabledHiden; } if (icItem) amount = icItem->getAmount(); else amount = 0; /* draw item */ pos[0] += obj->sx * C_UNIT / 2.0; pos[1] += obj->sy * C_UNIT / 2.0; UI_DrawItem(node, pos, &tempItem, -1, -1, scale, color); UI_DrawString("f_verysmall", ALIGN_LC, pos[0] + obj->sx * C_UNIT / 2.0, pos[1] + obj->sy * C_UNIT / 2.0, pos[0] + obj->sx * C_UNIT / 2.0, cellWidth - 5, /* maxWidth */ 0, va("x%i", amount)); pos[0] -= obj->sx * C_UNIT / 2.0; pos[1] += obj->sy * C_UNIT / 2.0; cellHeight += obj->sy * C_UNIT; /* save position for ammo */ Vector2Copy(pos, ammopos); ammopos[2] = 0; ammopos[0] += obj->sx * C_UNIT + 10; /* draw the item name. */ cellHeight += UI_DrawString("f_verysmall", ALIGN_UL, pos[0], pos[1], pos[0], cellWidth - 5, /* max width */ 0, _(obj->name)); /* draw ammos of weapon */ if (obj->weapon && EXTRADATA(node).displayAmmoOfWeapon) { for (int ammoIdx = 0; ammoIdx < obj->numAmmos; ammoIdx++) { tempItem.setDef(obj->ammos[ammoIdx]); /* skip weapos that are their own ammo -- oneshot and such */ if (obj == tempItem.def()) continue; /* skip unusable ammo */ if (!GAME_ItemIsUseable(tempItem.def())) continue; /* find and skip none existing ammo */ icItem = UI_ContainerNodeGetExistingItem(node, tempItem.def(), (itemFilterTypes_t) EXTRADATA(node).filterEquipType); if (!icItem) continue; /* Calculate the center of the item model/image. */ ammopos[0] += icItem->def()->sx * C_UNIT / 2.0; ammopos[1] -= icItem->def()->sy * C_UNIT / 2.0; UI_DrawItem(node, ammopos, &tempItem, -1, -1, scale, colorDefault); UI_DrawString("f_verysmall", ALIGN_LC, ammopos[0] + icItem->def()->sx * C_UNIT / 2.0, ammopos[1] + icItem->def()->sy * C_UNIT / 2.0, ammopos[0] + icItem->def()->sx * C_UNIT / 2.0, cellWidth - 5 - ammopos[0], /* maxWidth */ 0, va("x%i", icItem->getAmount())); ammopos[0] += icItem->def()->sx * C_UNIT / 2.0; ammopos[1] += icItem->def()->sy * C_UNIT / 2.0; } } cellHeight += 10; if (cellHeight > rowHeight) { rowHeight = cellHeight; } /* add a marge between rows */ if (col == EXTRADATA(node).columns - 1) { currentHeight += rowHeight; rowHeight = 0; if (currentHeight - EXTRADATA(node).scrollY.viewPos >= node->box.size[1]) outOfNode = true; } /* count items */ items++; } if (rowHeight != 0) { currentHeight += rowHeight; } return currentHeight; }
/** * @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; }