/** * @brief Prints the description for items (weapons, armour, ...) * @param[in] od The object definition of the item * @note Not only called from UFOpaedia but also from other places to display * weapon and ammo statistics * @todo Do we need to add checks for @c od->isDummy here somewhere? */ void INV_ItemDescription (const objDef_t *od) { static char itemText[UI_MAX_SMALLTEXTLEN]; int i; int count; currentDisplayedObject = od; if (!od) { /* If nothing selected return */ Cvar_Set("mn_itemname", ""); Cvar_Set("mn_item", ""); UI_ResetData(TEXT_ITEMDESCRIPTION); itemIndex = fireModeIndex = 0; UI_ExecuteConfunc("itemdesc_view 0 0;"); return; } /* select item */ Cvar_Set("mn_itemname", _(od->name)); Cvar_Set("mn_item", od->id); count = 0; if (INV_IsAmmo(od)) { /* We display the pre/next buttons for changing weapon only if there are at least 2 researched weapons * we are counting the number of weapons that are usable with this ammo */ for (i = 0; i < od->numWeapons; i++) if (GAME_ItemIsUseable(od->weapons[i])) count++; if (itemIndex >= od->numWeapons || itemIndex < 0) itemIndex = 0; if (count > 0) { while (!GAME_ItemIsUseable(od->weapons[itemIndex])) { itemIndex++; if (itemIndex >= od->numWeapons) itemIndex = 0; } Cvar_ForceSet("mn_linkname", _(od->weapons[itemIndex]->name)); } } else if (od->weapon && od->reload) { /* We display the pre/next buttons for changing ammo only if there are at least 2 researched ammo * we are counting the number of ammo that is usable with this weapon */ for (i = 0; i < od->numAmmos; i++) if (GAME_ItemIsUseable(od->ammos[i])) count++; if (itemIndex >= od->numAmmos || itemIndex < 0) itemIndex = 0; /* Only display ammos if at least one has been researched */ if (count > 0) { /* We have a weapon that uses ammos */ while (!GAME_ItemIsUseable(od->ammos[itemIndex])) { itemIndex++; if (itemIndex >= od->numAmmos) itemIndex = 0; } Cvar_ForceSet("mn_linkname", _(od->ammos[itemIndex]->name)); } } /* set description text if item has been researched or one of its ammo/weapon has been researched */ if (count > 0 || GAME_ItemIsUseable(od)) { int numFiredefs = 0; *itemText = '\0'; if (INV_IsArmour(od)) { Com_sprintf(itemText, sizeof(itemText), _("Size:\t%i\n"), od->size); Q_strcat(itemText, "\n", sizeof(itemText)); Q_strcat(itemText, _("^BDamage type:\tProtection:\n"), sizeof(itemText)); for (i = 0; i < csi.numDTs; i++) { const damageType_t *dt = &csi.dts[i]; if (!dt->showInMenu) continue; Q_strcat(itemText, va(_("%s\t%i\n"), _(dt->id), od->ratings[i]), sizeof(itemText)); } } else if ((od->weapon && od->numAmmos) || INV_IsAmmo(od)) { const objDef_t *odAmmo; if (count > 0) { int weaponIndex; if (od->weapon) { Com_sprintf(itemText, sizeof(itemText), _("%s weapon\n"), (od->fireTwoHanded ? _("Two-handed") : _("One-handed"))); if (od->ammo > 0) Q_strcat(itemText, va(_("Max ammo:\t%i\n"), od->ammo), sizeof(itemText)); odAmmo = (od->numAmmos) ? od->ammos[itemIndex] : od; assert(odAmmo); for (weaponIndex = 0; (weaponIndex < odAmmo->numWeapons) && (odAmmo->weapons[weaponIndex] != od); weaponIndex++) {} } else { odAmmo = od; weaponIndex = itemIndex; } /** @todo is there ammo with no firedefs? */ if (GAME_ItemIsUseable(odAmmo) && odAmmo->numFiredefs[weaponIndex] > 0) { const fireDef_t *fd; numFiredefs = odAmmo->numFiredefs[weaponIndex]; /* This contains everything common for weapons and ammos */ /* We check if the wanted firemode to display exists. */ if (fireModeIndex > numFiredefs - 1) fireModeIndex = 0; if (fireModeIndex < 0) fireModeIndex = numFiredefs - 1; fd = &odAmmo->fd[weaponIndex][fireModeIndex]; /* We always display the name of the firemode for an ammo */ Cvar_Set("mn_firemodename", _(fd->name)); /* We display the characteristics of this firemode */ Q_strcat(itemText, va(_("Skill:\t%s\n"), CL_WeaponSkillToName(fd->weaponSkill)), sizeof(itemText)); Q_strcat(itemText, va(_("Damage:\t%i\n"), (int) (fd->damage[0] + fd->spldmg[0]) * fd->shots), sizeof(itemText)); Q_strcat(itemText, va(_("Time units:\t%i\n"), fd->time), sizeof(itemText)); Q_strcat(itemText, va(_("Range:\t%g\n"), fd->range / UNIT_SIZE), sizeof(itemText)); Q_strcat(itemText, va(_("Spreads:\t%g\n"), (fd->spread[0] + fd->spread[1]) / 2), sizeof(itemText)); } } else { Com_sprintf(itemText, sizeof(itemText), _("%s. No detailed info available.\n"), INV_IsAmmo(od) ? _("Ammunition") : _("Weapon")); } } else if (od->weapon) { Com_sprintf(itemText, sizeof(itemText), _("%s ammo-less weapon\n"), (od->fireTwoHanded ? _("Two-handed") : _("One-handed"))); } else { /* just an item - only primary definition */ Com_sprintf(itemText, sizeof(itemText), _("%s auxiliary equipment\n"), (od->fireTwoHanded ? _("Two-handed") : _("One-handed"))); if (od->numWeapons > 0 && od->numFiredefs[0] > 0) { const fireDef_t *fd = &od->fd[0][0]; Q_strcat(itemText, va(_("Action:\t%s\n"), _(fd->name)), sizeof(itemText)); Q_strcat(itemText, va(_("Time units:\t%i\n"), fd->time), sizeof(itemText)); Q_strcat(itemText, va(_("Range:\t%g\n"), fd->range / UNIT_SIZE), sizeof(itemText)); } } UI_RegisterText(TEXT_ITEMDESCRIPTION, itemText); UI_ExecuteConfunc("itemdesc_view %i %i;", count, numFiredefs); } else { Com_sprintf(itemText, sizeof(itemText), _("Unknown - not useable")); UI_RegisterText(TEXT_ITEMDESCRIPTION, itemText); UI_ExecuteConfunc("itemdesc_view 0 0;"); } }
/** * @brief Try to autoplace an item from a container. * @param[in] node The context node * @param[in] ic An item from the node container * @todo We should use an item ID, and get the item inside the function, to avoid wrong uses. */ void UI_ContainerNodeAutoPlaceItem (uiNode_t* node, invList_t *ic) { containerIndex_t target; uiNode_t *targetNode; bool ammoChanged = false; const invDef_t *container = EXTRADATA(node).container; /* Right click: automatic item assignment/removal. */ if (container->id != csi.idEquip) { if (ic->item.ammo && ic->item.ammo != ic->item.item && ic->item.ammoLeft) { /* Remove ammo on removing weapon from a soldier */ target = csi.idEquip; ammoChanged = INV_UnloadWeapon(ic, ui_inventory, INVDEF(target)); } else { /* Move back to idEquip (ground, floor) container. */ target = csi.idEquip; INV_MoveItem(ui_inventory, INVDEF(target), NONE, NONE, container, ic, NULL); } } else { bool packed = false; assert(ic->item.item); /* armour can only have one target */ if (INV_IsArmour(ic->item.item)) { target = csi.idArmour; packed = INV_MoveItem(ui_inventory, INVDEF(target), 0, 0, container, ic, NULL); /* ammo or item */ } else if (INV_IsAmmo(ic->item.item)) { /* Finally try left and right hand. There is no other place to put it now. */ const containerIndex_t idxArray[] = { csi.idBelt, csi.idHolster, csi.idBackpack, csi.idLeft, csi.idRight }; const size_t size = lengthof(idxArray); unsigned int i; for (i = 0; i < size; i++) { target = idxArray[i]; packed = UI_ContainerNodeAddItem(container, ic, target, NULL); if (packed) break; } } else { if (ic->item.item->headgear) { target = csi.idHeadgear; packed = UI_ContainerNodeAddItem(container, ic, target, NULL); } else { /* left and right are single containers, but this might change - it's cleaner to check * for available space here, too */ const containerIndex_t idxArray[] = { csi.idRight, csi.idBelt, csi.idHolster, csi.idBackpack, csi.idLeft }; const size_t size = lengthof(idxArray); invList_t *tItem = NULL; unsigned int i; for (i = 0; i < size; i++) { target = idxArray[i]; packed = UI_ContainerNodeAddItem(container, ic, target, &tItem); if (packed) { if ((ic->item.item->weapon && !ic->item.ammoLeft) || ic->item.item->oneshot) ammoChanged = INV_LoadWeapon(tItem, ui_inventory, container, INVDEF(target)); break; } } } } /* no need to continue here - placement wasn't successful at all */ if (!packed) return; } /* Run onChange events */ targetNode = UI_GetContainerNodeByContainerIDX(node->parent, target); if (node->onChange) UI_ExecuteEventActions(node, node->onChange); if (targetNode != NULL && node != targetNode && targetNode->onChange) UI_ExecuteEventActions(targetNode, targetNode->onChange); /* Also call onChange for equip_ammo if ammo moved * Maybe there's a better way to do this? */ if (INV_IsAmmo(ic->item.item) || ammoChanged) { /** @todo hard coded node name, remove it when it is possible */ uiNode_t *ammoNode = UI_GetNode(node->root, "equip_ammo"); if (ammoNode != NULL && node != ammoNode && ammoNode->onChange) UI_ExecuteEventActions(ammoNode, ammoNode->onChange); } }