/** * @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 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); } }