Example #1
0
/**
 * @brief Draw an item node
 */
void uiItemNode::draw (uiNode_t *node)
{
	const objDef_t *od;
	const char* ref = UI_GetReferenceString(node, EXTRADATA(node).model);
	vec2_t pos;

	if (Q_strnull(ref))
		return;

	UI_GetNodeAbsPos(node, pos);
	R_CleanupDepthBuffer(pos[0], pos[1], node->box.size[0], node->box.size[1]);

	od = INVSH_GetItemByIDSilent(ref);
	if (od) {
		item_t item = {1, NULL, NULL, 0, 0}; /* 1 so it's not reddish; fake item anyway */
		item.t = INVSH_GetItemByIDX(od->idx);

		if (EXTRADATA(node).containerLike || INV_IsArmour(item.t)) {
			const vec4_t color = {1, 1, 1, 1};
			vec3_t itemNodePos;
			/* We position the model of the item ourself (in the middle of the item
			 * node). See the "-1, -1" parameter of UI_DrawItem. */
			UI_GetNodeAbsPos(node, itemNodePos);
			itemNodePos[0] += node->box.size[0] / 2.0;
			itemNodePos[1] += node->box.size[1] / 2.0;
			itemNodePos[2] = 0;
			/** @todo we should not use DrawItem but draw the image with render function (remove dependency with container) */
			UI_DrawItem(node, itemNodePos, &item, -1, -1, EXTRADATA(node).scale, color);
		} else {
			UI_DrawModelNode(node, GAME_GetModelForItem(item.t, NULL));
		}
	} else {
		GAME_DisplayItemInfo(node, ref);
	}
}
Example #2
0
bool CHRSH_IsArmourUseableForTeam (const objDef_t *od, const teamDef_t *teamDef)
{
	assert(teamDef);
	assert(INV_IsArmour(od));

	if (!teamDef->armour)
		return false;

	return od->useable == teamDef->team;
}
Example #3
0
static void testTeamDefsModelScriptData (void)
{
	int i;
	linkedList_t *armourPaths = NULL;

	for (i = 0; i < csi.numTeamDefs; i++) {
		int j;
		const teamDef_t *teamDef = &csi.teamDef[i];
		if (!teamDef->armour)
			continue;

		for (j = 0; j < csi.numODs; j++) {
			const objDef_t *od = INVSH_GetItemByIDX(j);
			if (!INV_IsArmour(od))
				continue;

			/* not for this team */
			if (!CHRSH_IsArmourUseableForTeam(od, teamDef))
				continue;

			if (!LIST_ContainsString(armourPaths, od->armourPath))
				LIST_AddString(&armourPaths, od->armourPath);
		}

		UFO_CU_ASSERT_TRUE_MSG(!LIST_IsEmpty(armourPaths), va("no armour definitions found for team %s - but armour is set to true",
				teamDef->id));

		LIST_Foreach(armourPaths, char const, armourPath) {
			int l;

			for (l = NAME_NEUTRAL; l < NAME_LAST; l++) {
				/* no models for this gender */
				if (!teamDef->numModels[l])
					continue;

				CU_ASSERT_PTR_NOT_NULL_FATAL(teamDef->models[l]);

				for (linkedList_t const* list = teamDef->models[l]; list; list = list->next) {
					teamDef_t::model_t const& m = *static_cast<teamDef_t::model_t const*>(list->data);

					UFO_CU_ASSERT_TRUE_MSG(TEST_CheckModel(va("%s/%s", m.path, m.body)), va("%s does not exist in models/%s (teamDef: %s)",
							m.body, m.path, teamDef->id));
					UFO_CU_ASSERT_TRUE_MSG(TEST_CheckModel(va("%s%s/%s", m.path, armourPath, m.body)), va("%s does not exist in models/%s%s (teamDef: %s)",
							m.body, m.path, armourPath, teamDef->id));

					UFO_CU_ASSERT_TRUE_MSG(TEST_CheckModel(va("%s/%s", m.path, m.head)), va("%s does not exist in models/%s (teamDef: %s)",
							m.head, m.path, teamDef->id));
					UFO_CU_ASSERT_TRUE_MSG(TEST_CheckModel(va("%s%s/%s", m.path, armourPath, m.head)), va("%s does not exist in models/%s%s (teamDef: %s)",
							m.head, m.path, armourPath, teamDef->id));
				}
			}
		}

		LIST_Delete(&armourPaths);
	}
Example #4
0
/**
 * @brief Draw a preview of the DND item dropped into the node
 */
static void UI_ContainerNodeDrawDropPreview (uiNode_t *target)
{
	item_t previewItem;
	int checkedTo;
	vec3_t origine;

	/* no preview into scrollable list */
	if (UI_IsScrollContainerNode(target))
		return;

	/* copy the DND item to not change the original one */
	previewItem = *UI_DNDGetItem();
	previewItem.rotated = false;
	checkedTo = INVSH_CheckToInventory(ui_inventory, previewItem.item, EXTRADATA(target).container, dragInfoToX, dragInfoToY, dragInfoIC);
	switch (checkedTo) {
	case INV_DOES_NOT_FIT:
		return;
	case INV_FITS:
		break;
	case INV_FITS_BOTH:
		/** @todo enable Shift-rotate for battlescape too when issues are solved */
		if (!Key_IsDown(K_SHIFT) || CL_BattlescapeRunning())
			break;
	case INV_FITS_ONLY_ROTATED:
		previewItem.rotated = true;
	}

	/* Hack, no preview for armour, we don't want it out of the armour container (and armour container is not visible) */
	if (INV_IsArmour(previewItem.item))
		return;

	UI_GetNodeAbsPos(target, origine);
	origine[2] = -40;

	/* Get center of single container for placement of preview item */
	if (EXTRADATA(target).container->single) {
		origine[0] += target->box.size[0] / 2.0;
		origine[1] += target->box.size[1] / 2.0;
	/* This is a "grid" container - we need to calculate the item-position
	 * (on the screen) from stored placement in the container and the
	 * calculated rotation info. */
	} else {
		if (previewItem.rotated) {
			origine[0] += (dragInfoToX + previewItem.item->sy / 2.0) * C_UNIT;
			origine[1] += (dragInfoToY + previewItem.item->sx / 2.0) * C_UNIT;
		} else {
			origine[0] += (dragInfoToX + previewItem.item->sx / 2.0) * C_UNIT;
			origine[1] += (dragInfoToY + previewItem.item->sy / 2.0) * C_UNIT;
		}
	}

	UI_DrawItem(NULL, origine, &previewItem, -1, -1, scale, colorPreview);
}
Example #5
0
static qboolean GAME_IsArmourUseableForTeam (const objDef_t *od, const teamDef_t *teamDef)
{
	if (teamDef != NULL && teamDef->armour && INV_IsArmour(od)) {
		if (CHRSH_IsTeamDefAlien(teamDef))
			return od->useable == TEAM_ALIEN;
		else if (teamDef->race == RACE_PHALANX_HUMAN)
			return od->useable == TEAM_PHALANX;
		else if (teamDef->race == RACE_CIVILIAN)
			return od->useable == TEAM_CIVILIAN;
		else
			return qfalse;
	}

	return qtrue;
}
Example #6
0
/**
 * @brief Returns the head model for the soldiers for armoured and non armoured soldiers
 * @param[in] chr Pointer to character struct
 * @sa CHRSH_CharGetBody
 */
const char *CHRSH_CharGetHead (const character_t * const chr)
{
	static char returnModel[MAX_VAR];

	/* models of UGVs don't change - because they are already armoured */
	if (INVSH_HasArmour(&chr->i) && chr->teamDef->race != RACE_ROBOT) {
		const objDef_t *od = INVSH_HasArmour(&chr->i)->item.t;
		const char *id = od->armourPath;
		if (!INV_IsArmour(od))
			Sys_Error("CHRSH_CharGetBody: Item is no armour");

		Com_sprintf(returnModel, sizeof(returnModel), "%s%s/%s", chr->path, id, chr->head);
	} else
		Com_sprintf(returnModel, sizeof(returnModel), "%s/%s", chr->path, chr->head);
	return returnModel;
}
Example #7
0
bool CHRSH_IsArmourUseableForTeam (const objDef_t *od, const teamDef_t *teamDef)
{
	assert(teamDef);
	assert(INV_IsArmour(od));

	if (!teamDef->armour)
		return false;

	if (CHRSH_IsTeamDefAlien(teamDef))
		return od->useable == TEAM_ALIEN;
	else if (teamDef->race == RACE_PHALANX_HUMAN)
		return od->useable == TEAM_PHALANX;
	else if (teamDef->race == RACE_CIVILIAN)
		return od->useable == TEAM_CIVILIAN;

	return false;
}
Example #8
0
qboolean GAME_ItemIsUseable (const objDef_t *od)
{
	const cgame_export_t *list = GAME_GetCurrentType();

	if (INV_IsArmour(od)) {
		const char *teamDefID = GAME_GetTeamDef();
		const teamDef_t *teamDef = Com_GetTeamDefinitionByID((teamDefID));

		/* Don't allow armour for other teams */
		if (!GAME_IsArmourUseableForTeam(od, teamDef))
			return qfalse;
	}

	if (list && list->IsItemUseable)
		return list->IsItemUseable(od);

	return qtrue;
}
Example #9
0
itemFilterTypes_t INV_GetFilterFromItem (const objDef_t *obj)
{
	assert(obj);

	/* heavy weapons may be primary too. check heavy first */
	if (obj->isHeavy)
		return FILTER_S_HEAVY;
	else if (obj->isPrimary)
		return FILTER_S_PRIMARY;
	else if (obj->isSecondary)
		return FILTER_S_SECONDARY;
	else if (obj->isMisc)
		return FILTER_S_MISC;
	else if (INV_IsArmour(obj))
		return FILTER_S_ARMOUR;

	/** @todo need to implement everything */
	Sys_Error("INV_GetFilterFromItem: unknown filter category for item '%s'", obj->id);
}
Example #10
0
/**
 * @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]);
					}
				}
			}
		}
	}
}
Example #11
0
/**
 * @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;
}
Example #12
0
/**
 * @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;
}
static void testTeamDefsModelScriptData (void)
{
	int i;
	linkedList_t *armourPaths = NULL;

	for (i = 0; i < csi.numTeamDefs; i++) {
		int j;
		const teamDef_t *teamDef = &csi.teamDef[i];
		if (!teamDef->armour)
			continue;

		for (j = 0; j < csi.numODs; j++) {
			const objDef_t *od = INVSH_GetItemByIDX(j);
			if (!INV_IsArmour(od))
				continue;

			/* not for this team */
			if (!CHRSH_IsArmourUseableForTeam(od, teamDef))
				continue;

			if (!LIST_ContainsString(armourPaths, od->armourPath))
				LIST_AddString(&armourPaths, od->armourPath);
		}

		UFO_CU_ASSERT_TRUE_MSG(!LIST_IsEmpty(armourPaths), va("no armour definitions found for team %s - but armour is set to true",
				teamDef->id));

		LIST_Foreach(armourPaths, char const, armourPath) {
			nametypes_t l;

			for (l = NAME_NEUTRAL; l < NAME_LAST; l++) {
				linkedList_t *list = teamDef->models[l];
				int k;

				/* no models for this gender */
				if (!teamDef->numModels[l])
					continue;

				CU_ASSERT_PTR_NOT_NULL(list);
				for (k = 0; k < teamDef->numModels[l]; k++) {
					const char *path;

					CU_ASSERT_PTR_NOT_NULL_FATAL(list);
					path = (const char*)list->data;
					/* body */
					list = list->next;
					CU_ASSERT_PTR_NOT_NULL_FATAL(list);
					UFO_CU_ASSERT_TRUE_MSG(TEST_CheckModel(va("%s/%s", path, list->data)), va("%s does not exist in models/%s (teamDef: %s)",
							list->data, path, teamDef->id));
					UFO_CU_ASSERT_TRUE_MSG(TEST_CheckModel(va("%s%s/%s", path, armourPath, list->data)), va("%s does not exist in models/%s%s (teamDef: %s)",
							list->data, path, armourPath, teamDef->id));

					list = list->next;
					CU_ASSERT_PTR_NOT_NULL_FATAL(list);
					/* head */
					UFO_CU_ASSERT_TRUE_MSG(TEST_CheckModel(va("%s/%s", path, list->data)), va("%s does not exist in models/%s (teamDef: %s)",
							list->data, path, teamDef->id));
					UFO_CU_ASSERT_TRUE_MSG(TEST_CheckModel(va("%s%s/%s", path, armourPath, list->data)), va("%s does not exist in models/%s%s (teamDef: %s)",
							list->data, path, armourPath, teamDef->id));

					/* skip skin */
					/** @todo check that the skin is valid for the given model */
					list = list->next;
					CU_ASSERT_PTR_NOT_NULL_FATAL(list);

					/* new path */
					list = list->next;
				}
			}
		}

		LIST_Delete(&armourPaths);
	}
/**
 * @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;");
	}
}
Example #15
0
/**
 * @brief Checks if the given object/item matched the given filter type.
 * @param[in] obj A pointer to an objDef_t item.
 * @param[in] filterType Filter type to check against.
 * @return @c true if obj is in filterType
 */
bool INV_ItemMatchesFilter (const objDef_t *obj, const itemFilterTypes_t filterType)
{
	int i;

	if (!obj)
		return false;

	switch (filterType) {
	case FILTER_S_PRIMARY:
		if (obj->isPrimary && !obj->isHeavy)
			return true;

		/* Check if one of the items that uses this ammo matches this filter type. */
		for (i = 0; i < obj->numWeapons; i++) {
			const objDef_t *weapon = obj->weapons[i];
			if (weapon && weapon != obj && INV_ItemMatchesFilter(weapon, filterType))
				return true;
		}
		break;

	case FILTER_S_SECONDARY:
		if (obj->isSecondary && !obj->isHeavy)
			return true;

		/* Check if one of the items that uses this ammo matches this filter type. */
		for (i = 0; i < obj->numWeapons; i++) {
			const objDef_t *weapon = obj->weapons[i];
			if (weapon && weapon != obj && INV_ItemMatchesFilter(weapon, filterType))
				return true;
		}
		break;

	case FILTER_S_HEAVY:
		if (obj->isHeavy)
			return true;

		/* Check if one of the items that uses this ammo matches this filter type. */
		for (i = 0; i < obj->numWeapons; i++) {
			const objDef_t *weapon = obj->weapons[i];
			if (weapon && weapon != obj && INV_ItemMatchesFilter(weapon, filterType))
				return true;
		}
		break;

	case FILTER_S_ARMOUR:
		return INV_IsArmour(obj);

	case FILTER_S_MISC:
		return obj->isMisc;

	case FILTER_CRAFTITEM:
		/** @todo Should we handle FILTER_AIRCRAFT here as well? */
		return INV_IsCraftItem(obj);

	case FILTER_UGVITEM:
		return obj->isUGVitem;

	case FILTER_DUMMY:
		return obj->isDummy;

	case FILTER_AIRCRAFT:
		return Q_streq(obj->type, "aircraft");

	case FILTER_DISASSEMBLY:
		/** @todo I guess we should search for components matching this item here. */
		break;

	case MAX_SOLDIER_FILTERTYPES:
	case MAX_FILTERTYPES:
	case FILTER_ENSURE_32BIT:
		Com_Printf("INV_ItemMatchesFilter: Unknown filter type for items: %i\n", filterType);
		break;
	}

	/* The given filter type is unknown. */
	return false;
}
Example #16
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);
	}
}