/**
 * @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;");
	}
}
Esempio n. 2
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);
	}
}