/** * @brief Draw a grip container */ static void UI_ContainerNodeDrawGrid (uiNode_t *node, const objDef_t *highlightType) { const invList_t *ic; vec3_t pos; UI_GetNodeAbsPos(node, pos); pos[2] = 0; for (ic = ui_inventory->c[EXTRADATA(node).container->id]; ic; ic = ic->next) { assert(ic->item.item); if (highlightType && INVSH_LoadableInWeapon(highlightType, ic->item.item)) UI_DrawItem(node, pos, &ic->item, ic->x, ic->y, scale, colorLoadable); else UI_DrawItem(node, pos, &ic->item, ic->x, ic->y, scale, colorDefault); } }
/** * @brief Returns true if actor can reload weapon * @sa AI_ActorThink */ bool G_ClientCanReload (edict_t *ent, containerIndex_t containerID) { invList_t *ic; containerIndex_t container; const objDef_t *weapon; if (CONTAINER(ent, containerID)) { weapon = CONTAINER(ent, containerID)->item.item; } else if (containerID == gi.csi->idLeft && RIGHT(ent)->item.item->holdTwoHanded) { /* Check for two-handed weapon */ containerID = gi.csi->idRight; weapon = CONTAINER(ent, containerID)->item.item; } else return false; assert(weapon); /* also try the temp containers */ for (container = 0; container < gi.csi->numIDs; container++) for (ic = CONTAINER(ent, container); ic; ic = ic->next) if (INVSH_LoadableInWeapon(ic->item.item, weapon)) return true; return false; }
/** * @brief Fully equip one actor. The equipment that is added to the inventory of the given actor * is taken from the equipment script definition. * @param[in] self The inventory interface pointer * @param[in] inv The inventory that will get the weapon. * @param[in] ed The equipment that is added from to the actors inventory * @param[in] td Pointer to teamdef data - to get the weapon and armour bools. * @note The code below is a complete implementation * of the scheme sketched at the beginning of equipment_missions.ufo. * Beware: If two weapons in the same category have the same price, * only one will be considered for inventory. */ static void I_EquipActor (inventoryInterface_t* self, inventory_t* const inv, const equipDef_t *ed, const teamDef_t* td) { int i; const int numEquip = lengthof(ed->numItems); int repeat = 0; const float AKIMBO_CHANCE = 0.3; /**< if you got a one-handed secondary weapon (and no primary weapon), this is the chance to get another one (between 0 and 1) */ if (td->weapons) { equipPrimaryWeaponType_t primary = WEAPON_NO_PRIMARY; int sum; int missedPrimary = 0; /**< If actor has a primary weapon, this is zero. Otherwise, this is the probability * 100 * that the actor had to get a primary weapon (used to compensate the lack of primary weapon) */ const objDef_t *primaryWeapon = NULL; int hasWeapon = 0; /* Primary weapons */ const int maxWeaponIdx = min(self->csi->numODs - 1, numEquip - 1); int randNumber = rand() % 100; for (i = 0; i < maxWeaponIdx; i++) { const objDef_t *obj = INVSH_GetItemByIDX(i); if (ed->numItems[i] && obj->weapon && obj->fireTwoHanded && obj->isPrimary) { randNumber -= ed->numItems[i]; missedPrimary += ed->numItems[i]; if (!primaryWeapon && randNumber < 0) primaryWeapon = obj; } } /* See if a weapon has been selected. */ if (primaryWeapon) { hasWeapon += I_PackAmmoAndWeapon(self, inv, primaryWeapon, 0, ed); if (hasWeapon) { int ammo; /* Find the first possible ammo to check damage type. */ for (ammo = 0; ammo < self->csi->numODs; ammo++) if (ed->numItems[ammo] && INVSH_LoadableInWeapon(&self->csi->ods[ammo], primaryWeapon)) break; if (ammo < self->csi->numODs) { if (/* To avoid two particle weapons. */ !(self->csi->ods[ammo].dmgtype == self->csi->damParticle) /* To avoid SMG + Assault Rifle */ && !(self->csi->ods[ammo].dmgtype == self->csi->damNormal)) { primary = WEAPON_OTHER; } else { primary = WEAPON_PARTICLE_OR_NORMAL; } } /* reset missedPrimary: we got a primary weapon */ missedPrimary = 0; } else { Com_DPrintf(DEBUG_SHARED, "INVSH_EquipActor: primary weapon '%s' couldn't be equipped in equipment '%s' (%s).\n", primaryWeapon->id, ed->id, self->name); repeat = WEAPONLESS_BONUS > frand(); } } /* Sidearms (secondary weapons with reload). */ do { int randNumber = rand() % 100; const objDef_t *secondaryWeapon = NULL; for (i = 0; i < self->csi->numODs; i++) { const objDef_t *obj = INVSH_GetItemByIDX(i); if (ed->numItems[i] && obj->weapon && obj->reload && !obj->deplete && obj->isSecondary) { randNumber -= ed->numItems[i] / (primary == WEAPON_PARTICLE_OR_NORMAL ? 2 : 1); if (randNumber < 0) { secondaryWeapon = obj; break; } } } if (secondaryWeapon) { hasWeapon += I_PackAmmoAndWeapon(self, inv, secondaryWeapon, missedPrimary, ed); if (hasWeapon) { /* Try to get the second akimbo pistol if no primary weapon. */ if (primary == WEAPON_NO_PRIMARY && !secondaryWeapon->fireTwoHanded && frand() < AKIMBO_CHANCE) { I_PackAmmoAndWeapon(self, inv, secondaryWeapon, 0, ed); } } } } while (!hasWeapon && repeat--); /* Misc items and secondary weapons without reload. */ if (!hasWeapon) repeat = WEAPONLESS_BONUS > frand(); else repeat = 0; /* Misc object probability can be bigger than 100 -- you're sure to * have one misc if it fits your backpack */ sum = 0; for (i = 0; i < self->csi->numODs; i++) { const objDef_t *obj = INVSH_GetItemByIDX(i); if (ed->numItems[i] && ((obj->weapon && obj->isSecondary && (!obj->reload || obj->deplete)) || obj->isMisc)) { /* if ed->num[i] is greater than 100, the first number is the number of items you'll get: * don't take it into account for probability * Make sure that the probability is at least one if an item can be selected */ sum += ed->numItems[i] ? max(ed->numItems[i] % 100, 1) : 0; } } if (sum) { do { int randNumber = rand() % sum; const objDef_t *secondaryWeapon = NULL; for (i = 0; i < self->csi->numODs; i++) { const objDef_t *obj = INVSH_GetItemByIDX(i); if (ed->numItems[i] && ((obj->weapon && obj->isSecondary && (!obj->reload || obj->deplete)) || obj->isMisc)) { randNumber -= ed->numItems[i] ? max(ed->numItems[i] % 100, 1) : 0; if (randNumber < 0) { secondaryWeapon = obj; break; } } } if (secondaryWeapon) { int num = ed->numItems[secondaryWeapon->idx] / 100 + (ed->numItems[secondaryWeapon->idx] % 100 >= 100 * frand()); while (num--) { hasWeapon += I_PackAmmoAndWeapon(self, inv, secondaryWeapon, 0, ed); } } } while (repeat--); /* Gives more if no serious weapons. */ } /* If no weapon at all, bad guys will always find a blade to wield. */ if (!hasWeapon) { int maxPrice = 0; const objDef_t *blade = NULL; Com_DPrintf(DEBUG_SHARED, "INVSH_EquipActor: no weapon picked in equipment '%s', defaulting to the most expensive secondary weapon without reload. (%s)\n", ed->id, self->name); for (i = 0; i < self->csi->numODs; i++) { const objDef_t *obj = INVSH_GetItemByIDX(i); if (ed->numItems[i] && obj->weapon && obj->isSecondary && !obj->reload) { if (obj->price > maxPrice) { maxPrice = obj->price; blade = obj; } } } if (maxPrice) hasWeapon += I_PackAmmoAndWeapon(self, inv, blade, 0, ed); } /* If still no weapon, something is broken, or no blades in equipment. */ if (!hasWeapon) Com_DPrintf(DEBUG_SHARED, "INVSH_EquipActor: cannot add any weapon; no secondary weapon without reload detected for equipment '%s' (%s).\n", ed->id, self->name); /* Armour; especially for those without primary weapons. */ repeat = (float) missedPrimary > frand() * 100.0; } else { return; } if (td->armour) { do { int randNumber = rand() % 100; for (i = 0; i < self->csi->numODs; i++) { const objDef_t *armour = INVSH_GetItemByIDX(i); if (ed->numItems[i] && INV_IsArmour(armour)) { randNumber -= ed->numItems[i]; if (randNumber < 0) { const item_t item = {NONE_AMMO, NULL, armour, 0, 0}; if (self->TryAddToInventory(self, inv, &item, &self->csi->ids[self->csi->idArmour])) { repeat = 0; break; } } } } } while (repeat-- > 0); } else { Com_DPrintf(DEBUG_SHARED, "INVSH_EquipActor: teamdef '%s' may not carry armour (%s)\n", td->name, self->name); } { int randNumber = rand() % 10; for (i = 0; i < self->csi->numODs; i++) { if (ed->numItems[i]) { const objDef_t *miscItem = INVSH_GetItemByIDX(i); if (miscItem->isMisc && !miscItem->weapon) { randNumber -= ed->numItems[i]; if (randNumber < 0) { const qboolean oneShot = miscItem->oneshot; const item_t item = {oneShot ? miscItem->ammo : NONE_AMMO, oneShot ? miscItem : NULL, miscItem, 0, 0}; containerIndex_t container; if (miscItem->headgear) container = self->csi->idHeadgear; else if (miscItem->extension) container = self->csi->idExtension; else container = self->csi->idBackpack; self->TryAddToInventory(self, inv, &item, &self->csi->ids[container]); } } } } } }
/** * @brief Pack a weapon, possibly with some ammo * @param[in] self The inventory interface pointer * @param[in] inv The inventory that will get the weapon * @param[in] weapon The weapon type index in gi.csi->ods * @param[in] ed The equipment for debug messages * @param[in] missedPrimary if actor didn't get primary weapon, this is 0-100 number to increase ammo number. * @sa INVSH_LoadableInWeapon */ static int I_PackAmmoAndWeapon (inventoryInterface_t *self, inventory_t* const inv, const objDef_t* weapon, int missedPrimary, const equipDef_t *ed) { const objDef_t *ammo = NULL; item_t item = {NONE_AMMO, NULL, NULL, 0, 0}; qboolean allowLeft; qboolean packed; int ammoMult = 1; assert(!INV_IsArmour(weapon)); item.t = weapon; /* are we going to allow trying the left hand */ allowLeft = !(inv->c[self->csi->idRight] && inv->c[self->csi->idRight]->item.t->fireTwoHanded); if (weapon->oneshot) { /* The weapon provides its own ammo (i.e. it is charged or loaded in the base.) */ item.a = weapon->ammo; item.m = weapon; Com_DPrintf(DEBUG_SHARED, "I_PackAmmoAndWeapon: oneshot weapon '%s' in equipment '%s' (%s).\n", weapon->id, ed->id, self->name); } else if (!weapon->reload) { item.m = item.t; /* no ammo needed, so fire definitions are in t */ } else { /* find some suitable ammo for the weapon (we will have at least one if there are ammos for this * weapon in equipment definition) */ int totalAvailableAmmo = 0; int i; for (i = 0; i < self->csi->numODs; i++) { const objDef_t *obj = INVSH_GetItemByIDX(i); if (ed->numItems[i] && INVSH_LoadableInWeapon(obj, weapon)) { totalAvailableAmmo++; } } if (totalAvailableAmmo) { int randNumber = rand() % totalAvailableAmmo; for (i = 0; i < self->csi->numODs; i++) { const objDef_t *obj = INVSH_GetItemByIDX(i); if (ed->numItems[i] && INVSH_LoadableInWeapon(obj, weapon)) { randNumber--; if (randNumber < 0) { ammo = obj; break; } } } } if (!ammo) { Com_DPrintf(DEBUG_SHARED, "I_PackAmmoAndWeapon: no ammo for sidearm or primary weapon '%s' in equipment '%s' (%s).\n", weapon->id, ed->id, self->name); return 0; } /* load ammo */ item.a = weapon->ammo; item.m = ammo; } if (!item.m) { Com_Printf("I_PackAmmoAndWeapon: no ammo for sidearm or primary weapon '%s' in equipment '%s' (%s).\n", weapon->id, ed->id, self->name); return 0; } /* now try to pack the weapon */ packed = self->TryAddToInventory(self, inv, &item, &self->csi->ids[self->csi->idRight]); if (packed) ammoMult = 3; if (!packed && allowLeft) packed = self->TryAddToInventory(self, inv, &item, &self->csi->ids[self->csi->idLeft]); if (!packed) packed = self->TryAddToInventory(self, inv, &item, &self->csi->ids[self->csi->idBelt]); if (!packed) packed = self->TryAddToInventory(self, inv, &item, &self->csi->ids[self->csi->idHolster]); if (!packed) return 0; /* pack some more ammo in the backpack */ if (ammo) { int num; int numpacked = 0; /* how many clips? */ num = (1 + ed->numItems[ammo->idx]) * (float) (1.0f + missedPrimary / 100.0); /* pack some ammo */ while (num--) { item_t mun = {NONE_AMMO, NULL, NULL, 0, 0}; mun.t = ammo; /* ammo to backpack; belt is for knives and grenades */ numpacked += self->TryAddToInventory(self, inv, &mun, &self->csi->ids[self->csi->idBackpack]); /* no problem if no space left; one ammo already loaded */ if (numpacked > ammoMult || numpacked * weapon->ammo > 11) break; } } return qtrue; }
/** * @brief Conditions for moving items between containers. * @param[in] self The inventory interface pointer * @param[in] inv Inventory to move in. * @param[in] from Source container. * @param[in] fItem The item to be moved. * @param[in] to Destination container. * @param[in] tx X coordinate in destination container. * @param[in] ty Y coordinate in destination container. * @param[in,out] TU pointer to entity available TU at this moment * or @c NULL if TU doesn't matter (outside battlescape) * @param[out] icp * @return IA_NOTIME when not enough TU. * @return IA_NONE if no action possible. * @return IA_NORELOAD if you cannot reload a weapon. * @return IA_RELOAD_SWAP in case of exchange of ammo in a weapon. * @return IA_RELOAD when reloading. * @return IA_ARMOUR when placing an armour on the actor. * @return IA_MOVE when just moving an item. */ static int I_MoveInInventory (inventoryInterface_t* self, inventory_t* const inv, const invDef_t * from, invList_t *fItem, const invDef_t * to, int tx, int ty, int *TU, invList_t ** icp) { invList_t *ic; int time; int checkedTo = INV_DOES_NOT_FIT; qboolean alreadyRemovedSource = qfalse; assert(to); assert(from); if (icp) *icp = NULL; if (from == to && fItem->x == tx && fItem->y == ty) return IA_NONE; time = from->out + to->in; if (from == to) { if (INV_IsFloorDef(from)) time = 0; else time /= 2; } if (TU && *TU < time) return IA_NOTIME; assert(inv); /* Special case for moving an item within the same container. */ if (from == to) { /* Do nothing if we move inside a scroll container. */ if (from->scroll) return IA_NONE; ic = inv->c[from->id]; for (; ic; ic = ic->next) { if (ic == fItem) { if (ic->item.amount > 1) { checkedTo = INVSH_CheckToInventory(inv, ic->item.t, to, tx, ty, fItem); if (checkedTo & INV_FITS) { ic->x = tx; ic->y = ty; if (icp) *icp = ic; return IA_MOVE; } return IA_NONE; } } } } /* If weapon is twohanded and is moved from hand to hand do nothing. */ /* Twohanded weapon are only in CSI->idRight. */ if (fItem->item.t->fireTwoHanded && INV_IsLeftDef(to) && INV_IsRightDef(from)) { return IA_NONE; } /* If non-armour moved to an armour slot then abort. * Same for non extension items when moved to an extension slot. */ if ((to->armour && !INV_IsArmour(fItem->item.t)) || (to->extension && !fItem->item.t->extension) || (to->headgear && !fItem->item.t->headgear)) { return IA_NONE; } /* Check if the target is a blocked inv-armour and source!=dest. */ if (to->single) checkedTo = INVSH_CheckToInventory(inv, fItem->item.t, to, 0, 0, fItem); else { if (tx == NONE || ty == NONE) INVSH_FindSpace(inv, &fItem->item, to, &tx, &ty, fItem); /* still no valid location found */ if (tx == NONE || ty == NONE) return IA_NONE; checkedTo = INVSH_CheckToInventory(inv, fItem->item.t, to, tx, ty, fItem); } if (to->armour && from != to && !checkedTo) { item_t cacheItem2; invList_t *icTo; /* Store x/y origin coordinates of removed (source) item. * When we re-add it we can use this. */ const int cacheFromX = fItem->x; const int cacheFromY = fItem->y; /* Check if destination/blocking item is the same as source/from item. * In that case the move is not needed -> abort. */ icTo = INVSH_SearchInInventory(inv, to, tx, ty); if (fItem->item.t == icTo->item.t) return IA_NONE; /* Actually remove the ammo from the 'from' container. */ if (!self->RemoveFromInventory(self, inv, from, fItem)) return IA_NONE; else /* Removal successful - store this info. */ alreadyRemovedSource = qtrue; cacheItem2 = self->cacheItem; /* Save/cache (source) item. The cacheItem is modified in I_MoveInInventory. */ /* Move the destination item to the source. */ self->MoveInInventory(self, inv, to, icTo, from, cacheFromX, cacheFromY, TU, icp); /* Reset the cached item (source) (It'll be move to container emptied by destination item later.) */ self->cacheItem = cacheItem2; } else if (!checkedTo) { /* Get the target-invlist (e.g. a weapon). We don't need to check for * scroll because checkedTo is always true here. */ ic = INVSH_SearchInInventory(inv, to, tx, ty); if (ic && !INV_IsEquipDef(to) && INVSH_LoadableInWeapon(fItem->item.t, ic->item.t)) { /* A target-item was found and the dragged item (implicitly ammo) * can be loaded in it (implicitly weapon). */ if (ic->item.a >= ic->item.t->ammo && ic->item.m == fItem->item.t) { /* Weapon already fully loaded with the same ammunition -> abort */ return IA_NORELOAD; } time += ic->item.t->reload; if (!TU || *TU >= time) { if (TU) *TU -= time; if (ic->item.a >= ic->item.t->ammo) { /* exchange ammo */ const item_t item = {NONE_AMMO, NULL, ic->item.m, 0, 0}; /* Put current ammo in place of the new ammo unless floor - there can be more than 1 item */ const int cacheFromX = INV_IsFloorDef(from) ? NONE : fItem->x; const int cacheFromY = INV_IsFloorDef(from) ? NONE : fItem->y; /* Actually remove the ammo from the 'from' container. */ if (!self->RemoveFromInventory(self, inv, from, fItem)) return IA_NONE; /* Add the currently used ammo in place of the new ammo in the "from" container. */ if (self->AddToInventory(self, inv, &item, from, cacheFromX, cacheFromY, 1) == NULL) Sys_Error("Could not reload the weapon - add to inventory failed (%s)", self->name); ic->item.m = self->cacheItem.t; if (icp) *icp = ic; return IA_RELOAD_SWAP; } else { /* Actually remove the ammo from the 'from' container. */ if (!self->RemoveFromInventory(self, inv, from, fItem)) return IA_NONE; ic->item.m = self->cacheItem.t; /* loose ammo of type ic->item.m saved on server side */ ic->item.a = ic->item.t->ammo; if (icp) *icp = ic; return IA_RELOAD; } } /* Not enough time -> abort. */ return IA_NOTIME; } /* temp container like idEquip and idFloor */ if (ic && to->temp) { /* We are moving to a blocked location container but it's the base-equipment floor or a battlescape floor. * We add the item anyway but it'll not be displayed (yet) * This is then used in I_AddToInventory below.*/ /** @todo change the other code to browse trough these things. */ INVSH_FindSpace(inv, &fItem->item, to, &tx, &ty, fItem); if (tx == NONE || ty == NONE) { Com_DPrintf(DEBUG_SHARED, "I_MoveInInventory - item will be added non-visible (%s)\n", self->name); } } else { /* Impossible move -> abort. */ return IA_NONE; } } /* twohanded exception - only CSI->idRight is allowed for fireTwoHanded weapons */ if (fItem->item.t->fireTwoHanded && INV_IsLeftDef(to)) to = &self->csi->ids[self->csi->idRight]; if (checkedTo == INV_FITS_ONLY_ROTATED) { /* Set rotated tag */ fItem->item.rotated = qtrue; } else if (fItem->item.rotated) { /* Remove rotated tag */ fItem->item.rotated = qfalse; } /* Actually remove the item from the 'from' container (if it wasn't already removed). */ if (!alreadyRemovedSource) if (!self->RemoveFromInventory(self, inv, from, fItem)) return IA_NONE; /* successful */ if (TU) *TU -= time; assert(self->cacheItem.t); ic = self->AddToInventory(self, inv, &self->cacheItem, to, tx, ty, 1); /* return data */ if (icp) { assert(ic); *icp = ic; } if (INV_IsArmourDef(to)) { assert(INV_IsArmour(self->cacheItem.t)); return IA_ARMOUR; } else return IA_MOVE; }
/** * @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; }
/** * @brief Draw a container which only contains one item * @param node Context node * @param highlightType Current selected object */ static void UI_ContainerNodeDrawSingle (uiNode_t *node, const objDef_t *highlightType) { vec4_t color; vec3_t pos; UI_GetNodeAbsPos(node, pos); pos[0] += node->box.size[0] / 2.0; pos[1] += node->box.size[1] / 2.0; pos[2] = 0; /* Single item container (special case for left hand). */ if (INV_IsLeftDef(EXTRADATA(node).container) && !ui_inventory->c[csi.idLeft]) { if (ui_inventory->c[csi.idRight]) { const item_t *item = &ui_inventory->c[csi.idRight]->item; assert(item); assert(item->item); if (item->item->holdTwoHanded) { if (highlightType && INVSH_LoadableInWeapon(highlightType, item->item)) memcpy(color, colorLoadable, sizeof(vec4_t)); else memcpy(color, colorDefault, sizeof(vec4_t)); color[3] = 0.5; UI_DrawItem(node, pos, item, -1, -1, scale, color); } } } else if (ui_inventory->c[EXTRADATA(node).container->id]) { bool disabled = false; const item_t *item; if (ui_inventory->c[csi.idRight]) { item = &ui_inventory->c[csi.idRight]->item; /* If there is a weapon in the right hand that needs two hands to shoot it * and there is a weapon in the left, then draw a disabled marker for the * fireTwoHanded weapon. */ assert(item); assert(item->item); if (INV_IsRightDef(EXTRADATA(node).container) && item->item->fireTwoHanded && ui_inventory->c[csi.idLeft]) { disabled = true; UI_DrawDisabled(node); } } item = &ui_inventory->c[EXTRADATA(node).container->id]->item; assert(item); assert(item->item); if (highlightType && INVSH_LoadableInWeapon(highlightType, item->item)) { if (disabled) Vector4Copy(colorDisabledLoadable, color); else Vector4Copy(colorLoadable, color); } else { if (disabled) Vector4Copy(colorDisabled, color); else Vector4Copy(colorDefault, color); } if (disabled) color[3] = 0.5; UI_DrawItem(node, pos, item, -1, -1, scale, color); } }