Ejemplo n.º 1
0
/**
 * @brief Debug function to print a player's inventory
 */
void G_InvList_f (const Player& player)
{
	Edict* ent = nullptr;

	gi.DPrintf("Print inventory for '%s'\n", player.pers.netname);
	while ((ent = G_EdictsGetNextLivingActorOfTeam(ent, player.getTeam()))) {
		gi.DPrintf("actor: '%s'\n", ent->chr.name);

		const Container* cont = nullptr;
		while ((cont = ent->chr.inv.getNextCont(cont, true))) {
			Com_Printf("Container: %i\n", cont->id);
			Item* item = nullptr;
			while ((item = cont->getNextItem(item))) {
				Com_Printf(".. item.def(): %i, item.ammo: %i, item.ammoLeft: %i, x: %i, y: %i\n",
						(item->def() ? item->def()->idx : NONE), (item->ammoDef() ? item->ammoDef()->idx : NONE),
						item->getAmmoLeft(), item->getX(), item->getY());
				if (item->def())
					Com_Printf(".... weapon: %s\n", item->def()->id);
				if (item->ammoDef())
					Com_Printf(".... ammo:   %s (%i)\n", item->ammoDef()->id, item->getAmmoLeft());
			}
		}
		const float invWeight = ent->chr.inv.getWeight();
		const int maxWeight = ent->chr.score.skills[ABILITY_POWER];
		const float penalty = GET_ENCUMBRANCE_PENALTY(invWeight, maxWeight);
		const int normalTU = GET_TU(ent->chr.score.skills[ABILITY_SPEED], 1.0f - WEIGHT_NORMAL_PENALTY);
		const int tus = GET_TU(ent->chr.score.skills[ABILITY_SPEED], penalty);
		const int tuPenalty = tus - normalTU;
		const char* penaltyStr = 1.0f - penalty < WEIGHT_NORMAL_PENALTY ? "'Light weight'" : (1.0f - penalty < WEIGHT_HEAVY_PENALTY ? "'Normal weight'" : "'Encumbered'");
		Com_Printf("Weight: %g/%i, Encumbrance: %s (%.0f%%), TU's: %i (normal: %i, penalty/bonus: %+i)\n", invWeight, maxWeight, penaltyStr, invWeight / maxWeight * 100.0f, tus, normalTU, tuPenalty);
	}
}
Ejemplo n.º 2
0
int G_ActorCalculateMaxTU (const Edict* ent)
{
	const int invWeight = ent->chr.inv.getWeight();
	const int currentMaxTU = GET_TU(ent->chr.score.skills[ABILITY_SPEED], GET_ENCUMBRANCE_PENALTY(invWeight,
			ent->chr.score.skills[ABILITY_POWER])) * G_ActorGetInjuryPenalty(ent, MODIFIER_TU);

	return std::min(currentMaxTU, MAX_TU);
}
Ejemplo n.º 3
0
/**
 * @brief Determines the amount of XP earned by a given soldier for a given skill, based on the soldier's performance in the last mission.
 * @param[in] skill The skill for which to fetch the maximum amount of XP.
 * @param[in] ent Pointer to the character you want to get the earned experience for
 * @sa G_UpdateCharacterExperience
 * @sa G_GetMaxExperiencePerMission
 */
static int G_GetEarnedExperience (abilityskills_t skill, edict_t *ent)
{
	character_t *chr = &ent->chr;

	int experience = 0;
	int i;

	switch (skill) {
	case ABILITY_POWER: {
		const float weight = chr->scoreMission->carriedWeight / level.actualRound;
		const float penalty = GET_ENCUMBRANCE_PENALTY(weight, chr->score.skills[ABILITY_POWER]);
		experience = 150 * (weight / chr->score.skills[ABILITY_POWER]) / penalty;
		break;
	}
	case ABILITY_SPEED:
		experience += chr->scoreMission->movedNormal / 2 + chr->scoreMission->movedCrouched;
		/* skip skills < ABILITY_NUM_TYPES, they are abilities not real skills */
		for (i = ABILITY_NUM_TYPES; i < SKILL_NUM_TYPES; i++)
			experience += (chr->scoreMission->firedTUs[i] + chr->scoreMission->firedSplashTUs[i]) / 10;
		break;
	case ABILITY_ACCURACY:
		/* skip skills < ABILITY_NUM_TYPES, they are abilities not real skills */
		for (i = ABILITY_NUM_TYPES; i < SKILL_NUM_TYPES; i++)
			if (i == SKILL_SNIPER)
				experience += 30 * (chr->scoreMission->hits[i][KILLED_ENEMIES] + chr->scoreMission->hitsSplash[i][KILLED_ENEMIES]);
			else
				experience += 20 * (chr->scoreMission->hits[i][KILLED_ENEMIES] + chr->scoreMission->hitsSplash[i][KILLED_ENEMIES]);
		break;
	case ABILITY_MIND:
		experience = 50 + 200 * chr->scoreMission->kills[KILLED_ENEMIES];
		break;
	case SKILL_CLOSE:
		experience = 150 * (chr->scoreMission->hits[skill][KILLED_ENEMIES] + chr->scoreMission->hitsSplash[skill][KILLED_ENEMIES]);
		break;
	case SKILL_HEAVY:
		experience = 200 * (chr->scoreMission->hits[skill][KILLED_ENEMIES] + chr->scoreMission->hitsSplash[skill][KILLED_ENEMIES]);
		break;
	case SKILL_ASSAULT:
		experience = 100 * (chr->scoreMission->hits[skill][KILLED_ENEMIES] + chr->scoreMission->hitsSplash[skill][KILLED_ENEMIES]);
		break;
	case SKILL_SNIPER:
		experience = 200 * (chr->scoreMission->hits[skill][KILLED_ENEMIES] + chr->scoreMission->hitsSplash[skill][KILLED_ENEMIES]);
		break;
	case SKILL_EXPLOSIVE:
		experience = 200 * (chr->scoreMission->hits[skill][KILLED_ENEMIES] + chr->scoreMission->hitsSplash[skill][KILLED_ENEMIES]);
		break;
	default:
		break;
	}

	return experience;
}
Ejemplo n.º 4
0
/**
 * @brief Update the equipment weight for the selected actor.
 */
static void INV_UpdateActorLoad_f (void)
{
	if (Cmd_Argc() < 2) {
		Com_Printf("Usage: %s <callback>\n", Cmd_Argv(0));
		return;
	}

	const character_t* chr = GAME_GetSelectedChr();
	if (chr == nullptr)
		return;

	const float invWeight = chr->inv.getWeight();
	const int maxWeight = GAME_GetChrMaxLoad(chr);
	const float penalty = GET_ENCUMBRANCE_PENALTY(invWeight, maxWeight);
	const int normalTU = GET_TU(chr->score.skills[ABILITY_SPEED], 1.0f - WEIGHT_NORMAL_PENALTY);
	const int tus = GET_TU(chr->score.skills[ABILITY_SPEED], penalty);
	const int tuPenalty = tus - normalTU;
	int count = 0;

	const Container* cont = nullptr;
	while ((cont = chr->inv.getNextCont(cont))) {
		if (cont->def()->temp)
			continue;
		for (Item* invList = cont->_invList, *next; invList; invList = next) {
			next = invList->getNext();
			const fireDef_t* fireDef = invList->getFiredefs();
			if (fireDef == nullptr)
				continue;
			for (int i = 0; i < MAX_FIREDEFS_PER_WEAPON; i++) {
				if (fireDef[i].time <= 0)
					continue;
				if (fireDef[i].time <= tus)
					continue;
				if (count <= 0)
					Com_sprintf(popupText, sizeof(popupText), _("This soldier no longer has enough TUs to use the following items:\n\n"));
				Q_strcat(popupText, sizeof(popupText), "%s: %s (%i)\n", _(invList->def()->name), _(fireDef[i].name), fireDef[i].time);
				++count;
			}
		}
	}

	if ((Cmd_Argc() < 3 || atoi(Cmd_Argv(2)) == 0) && count > 0)
		UI_Popup(_("Warning"), popupText);

	char label[MAX_VAR];
	char tooltip[MAX_VAR];
	Com_sprintf(label, sizeof(label), "%g/%i %s %s", invWeight / WEIGHT_FACTOR, maxWeight, _("Kg"),
			(count > 0 ? _("Warning!") : ""));
	Com_sprintf(tooltip, sizeof(tooltip), "%s %i (%+i)", _("TU:"), tus, tuPenalty);
	UI_ExecuteConfunc("%s \"%s\" \"%s\" %f %i", Cmd_Argv(1), label, tooltip, WEIGHT_NORMAL_PENALTY - (1.0f - penalty), count);
}
Ejemplo n.º 5
0
/**
 * @brief Determines the amount of XP earned by a given soldier for a given skill, based on the soldier's performance in the last mission.
 * @param[in] skill The skill for which to fetch the maximum amount of XP.
 * @param[in] ent Pointer to the character you want to get the earned experience for
 * @sa G_UpdateCharacterExperience
 * @sa G_GetMaxExperiencePerMission
 */
static int G_GetEarnedExperience (abilityskills_t skill, Edict* ent)
{
	character_t* chr = &ent->chr;

	int experience = 0;

	switch (skill) {
	case ABILITY_POWER: {
		const float weight = chr->scoreMission->carriedWeight / WEIGHT_FACTOR / level.actualRound;
		const float penalty = GET_ENCUMBRANCE_PENALTY(weight, chr->score.skills[ABILITY_POWER]);
		experience = 50 * (weight / chr->score.skills[ABILITY_POWER]) / penalty;
		break;
	}
	case ABILITY_ACCURACY:
		/* skip skills < ABILITY_NUM_TYPES, they are abilities not real skills */
		for (int i = ABILITY_NUM_TYPES; i < SKILL_NUM_TYPES; i++)
			if (i == SKILL_SNIPER)
				experience += 60 * (chr->scoreMission->hits[i][KILLED_ENEMIES] + chr->scoreMission->hitsSplash[i][KILLED_ENEMIES]);
			else
				experience += 40 * (chr->scoreMission->hits[i][KILLED_ENEMIES] + chr->scoreMission->hitsSplash[i][KILLED_ENEMIES]);
		break;
	case ABILITY_MIND:
		experience = 50 + 100 * chr->scoreMission->kills[KILLED_ENEMIES];
		break;
	case SKILL_CLOSE:
		experience = 180 * (chr->scoreMission->hits[skill][KILLED_ENEMIES] + chr->scoreMission->hitsSplash[skill][KILLED_ENEMIES]);
		break;
#if 0
	case SKILL_HEAVY:
		experience = 180 * (chr->scoreMission->hits[skill][KILLED_ENEMIES] + chr->scoreMission->hitsSplash[skill][KILLED_ENEMIES]);
		break;
#endif
	case SKILL_ASSAULT:
		experience = 180 * (chr->scoreMission->hits[skill][KILLED_ENEMIES] + chr->scoreMission->hitsSplash[skill][KILLED_ENEMIES]);
		break;
	case SKILL_SNIPER:
		experience = 180 * (chr->scoreMission->hits[skill][KILLED_ENEMIES] + chr->scoreMission->hitsSplash[skill][KILLED_ENEMIES]);
		break;
	case SKILL_EXPLOSIVE:
		experience = 180 * (chr->scoreMission->hits[skill][KILLED_ENEMIES] + chr->scoreMission->hitsSplash[skill][KILLED_ENEMIES]);
		break;
	default:
		break;
	}

	return experience;
}
Ejemplo n.º 6
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] chr The character that will get the weapon.
 * @param[in] ed The equipment that is added from to the actors inventory
 * @param[in] maxWeight The max weight this actor is allowed to carry.
 * @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.
 */
void InventoryInterface::EquipActorNormal (character_t* const chr, const equipDef_t* ed, int maxWeight)
{
	const teamDef_t* td = chr->teamDef;
	const int numEquip = lengthof(ed->numItems);
	int repeat = 0;

	if (td->weapons) {
		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 = nullptr;
		/* Primary weapons */
		const int maxWeaponIdx = std::min(this->csi->numODs - 1, numEquip - 1);
		int randNumber = rand() % 100;
		for (int 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. */
		equipPrimaryWeaponType_t primary = WEAPON_NO_PRIMARY;
		int hasWeapon = 0;
		if (primaryWeapon) {
			hasWeapon += PackAmmoAndWeapon(chr, primaryWeapon, 0, ed, maxWeight);
			if (hasWeapon) {
				int ammo;

				/* Find the first possible ammo to check damage type. */
				for (ammo = 0; ammo < this->csi->numODs; ammo++)
					if (ed->numItems[ammo] && this->csi->ods[ammo].isLoadableInWeapon(primaryWeapon))
						break;
				if (ammo < this->csi->numODs) {
					if (/* To avoid two particle weapons. */
						!(this->csi->ods[ammo].dmgtype == this->csi->damParticle)
						/* To avoid SMG + Assault Rifle */
						&& !(this->csi->ods[ammo].dmgtype == this->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, invName);
				repeat = WEAPONLESS_BONUS > frand();
			}
		}

		/* Sidearms (secondary weapons with reload). */
		do {
			int randNumber = rand() % 100;
			const objDef_t* secondaryWeapon = nullptr;
			for (int i = 0; i < this->csi->numODs; i++) {
				const objDef_t* obj = INVSH_GetItemByIDX(i);
				if (ed->numItems[i] && obj->weapon && obj->isReloadable() && !obj->deplete && obj->isSecondary) {
					randNumber -= ed->numItems[i] / (primary == WEAPON_PARTICLE_OR_NORMAL ? 2 : 1);
					if (randNumber < 0) {
						secondaryWeapon = obj;
						break;
					}
				}
			}

			if (secondaryWeapon) {
				hasWeapon += PackAmmoAndWeapon(chr, secondaryWeapon, missedPrimary, ed, maxWeight);
			}
		} 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 */
		int sum = 0;
		for (int i = 0; i < this->csi->numODs; i++) {
			const objDef_t* obj = INVSH_GetItemByIDX(i);
			if (ed->numItems[i] && ((obj->weapon && obj->isSecondary
			 && (!obj->isReloadable() || 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] ? std::max(ed->numItems[i] % 100, 1) : 0;
			}
		}
		if (sum) {
			do {
				int randNumber = rand() % sum;
				const objDef_t* secondaryWeapon = nullptr;
				for (int i = 0; i < this->csi->numODs; i++) {
					const objDef_t* obj = INVSH_GetItemByIDX(i);
					if (ed->numItems[i] && ((obj->weapon && obj->isSecondary
					 && (!obj->isReloadable() || obj->deplete)) || obj->isMisc)) {
						randNumber -= ed->numItems[i] ? std::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 += PackAmmoAndWeapon(chr, secondaryWeapon, 0, ed, maxWeight);
					}
				}
			} 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 = nullptr;
			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, invName);
			for (int i = 0; i < this->csi->numODs; i++) {
				const objDef_t* obj = INVSH_GetItemByIDX(i);
				if (ed->numItems[i] && obj->weapon && obj->isSecondary && !obj->isReloadable()) {
					if (obj->price > maxPrice) {
						maxPrice = obj->price;
						blade = obj;
					}
				}
			}
			if (maxPrice)
				hasWeapon += PackAmmoAndWeapon(chr, blade, 0, ed, maxWeight);
		}
		/* 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, invName);

		/* Armour; especially for those without primary weapons. */
		repeat = (float) missedPrimary > frand() * 100.0;
	} else {
		return;
	}

	Inventory* const inv = &chr->inv;
	const int speed = chr->score.skills[ABILITY_SPEED];
	if (td->armour) {
		do {
			int randNumber = rand() % 100;
			for (int i = 0; i < this->csi->numODs; i++) {
				const objDef_t* armour = INVSH_GetItemByIDX(i);
				if (ed->numItems[i] && armour->isArmour()) {
					randNumber -= ed->numItems[i];
					if (randNumber < 0) {
						const Item item(armour);
						int tuNeed = 0;
						const int weight = GetInventoryState(inv, tuNeed) + item.getWeight();
						const int maxTU = GET_TU(speed, GET_ENCUMBRANCE_PENALTY(weight, chr->score.skills[ABILITY_POWER]));
						if (weight > maxWeight * WEIGHT_FACTOR || tuNeed > maxTU)
							continue;
						if (tryAddToInventory(inv, &item, &this->csi->ids[CID_ARMOUR])) {
							repeat = 0;
							break;
						}
					}
				}
			}
		} while (repeat-- > 0);
	} else {
		Com_DPrintf(DEBUG_SHARED, "INVSH_EquipActor: teamdef '%s' may not carry armour (%s)\n",
				td->name, invName);
	}

	{
		int randNumber = rand() % 10;
		for (int i = 0; i < this->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 bool oneShot = miscItem->oneshot;
						const Item item(miscItem, oneShot ? miscItem : nullptr, oneShot ? miscItem->ammo : NONE_AMMO);
						containerIndex_t container;
						int tuNeed;
						const fireDef_t* itemFd = item.getSlowestFireDef();
						const float weight = GetInventoryState(inv, tuNeed) + item.getWeight();
						const int maxTU = GET_TU(speed, GET_ENCUMBRANCE_PENALTY(weight, chr->score.skills[ABILITY_POWER]));

						if (miscItem->headgear)
							container = CID_HEADGEAR;
						else if (miscItem->implant)
							container = CID_IMPLANT;
						else
							container = CID_BACKPACK;
						if (weight > maxWeight * WEIGHT_FACTOR || tuNeed > maxTU || (itemFd && itemFd->time > maxTU))
							continue;
						tryAddToInventory(inv, &item, &this->csi->ids[container]);
					}
				}
			}
		}
	}
}
Ejemplo n.º 7
0
/**
 * @brief Pack a weapon, possibly with some ammo
 * @param[in] chr The character 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.
 * @param[in] maxWeight The max weight this actor is allowed to carry.
 * @sa isLoadableInWeapon()
 */
int InventoryInterface::PackAmmoAndWeapon (character_t* const chr, const objDef_t* weapon, int missedPrimary, const equipDef_t* ed, int maxWeight)
{
	assert(!weapon->isArmour());
	Item item(weapon);
	const objDef_t* ammo = nullptr;

	if (weapon->oneshot) {
		/* The weapon provides its own ammo (i.e. it is charged or loaded in the base.) */
		item.setAmmoLeft(weapon->ammo);
		item.setAmmoDef(weapon);
		Com_DPrintf(DEBUG_SHARED, "PackAmmoAndWeapon: oneshot weapon '%s' in equipment '%s' (%s).\n",
				weapon->id, ed->id, invName);
	} else if (!weapon->isReloadable()) {
		item.setAmmoDef(weapon);	/* no ammo needed, so fire definitions are in item */
	} 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;
		for (int i = 0; i < csi->numODs; i++) {
			const objDef_t* obj = INVSH_GetItemByIDX(i);
			if (ed->numItems[i] && obj->isLoadableInWeapon(weapon)) {
				totalAvailableAmmo++;
			}
		}
		if (totalAvailableAmmo) {
			int randNumber = rand() % totalAvailableAmmo;
			for (int i = 0; i < csi->numODs; i++) {
				const objDef_t* obj = INVSH_GetItemByIDX(i);
				if (ed->numItems[i] && obj->isLoadableInWeapon(weapon)) {
					randNumber--;
					if (randNumber < 0) {
						ammo = obj;
						break;
					}
				}
			}
		}

		if (!ammo) {
			Com_DPrintf(DEBUG_SHARED, "PackAmmoAndWeapon: no ammo for sidearm or primary weapon '%s' in equipment '%s' (%s).\n",
					weapon->id, ed->id, invName);
			return 0;
		}
		/* load ammo */
		item.setAmmoLeft(weapon->ammo);
		item.setAmmoDef(ammo);
	}

	if (!item.ammoDef()) {
		Com_Printf("PackAmmoAndWeapon: no ammo for sidearm or primary weapon '%s' in equipment '%s' (%s).\n",
				weapon->id, ed->id, invName);
		return 0;
	}

	Inventory* const inv = &chr->inv;
	const int speed = chr->score.skills[ABILITY_SPEED];
	int tuNeed = 0;
	float weight = GetInventoryState(inv, tuNeed) + item.getWeight();
	int maxTU = GET_TU(speed, GET_ENCUMBRANCE_PENALTY(weight, chr->score.skills[ABILITY_POWER]));
	if (weight > maxWeight * WEIGHT_FACTOR || tuNeed > maxTU) {
		Com_DPrintf(DEBUG_SHARED, "PackAmmoAndWeapon: weapon too heavy: '%s' in equipment '%s' (%s).\n",
				weapon->id, ed->id, invName);
		return 0;
	}

	/* are we going to allow trying the left hand? */
	const bool allowLeft = !(inv->getContainer2(CID_RIGHT) && inv->getContainer2(CID_RIGHT)->def()->fireTwoHanded);
	int ammoMult = 1;
	/* now try to pack the weapon */
	bool packed = tryAddToInventory(inv, &item, &csi->ids[CID_RIGHT]);
	if (packed)
		ammoMult = 3;
	if (!packed && allowLeft)
		packed = tryAddToInventory(inv, &item, &csi->ids[CID_LEFT]);
	if (!packed)
		packed = tryAddToInventory(inv, &item, &csi->ids[CID_BELT]);
	if (!packed)
		packed = tryAddToInventory(inv, &item, &csi->ids[CID_HOLSTER]);
	if (!packed)
		packed = tryAddToInventory(inv, &item, &csi->ids[CID_BACKPACK]);
	if (!packed)
		return 0;

	/* pack some more ammo in the backpack */
	if (ammo) {
		int numpacked = 0;

		/* how many clips? */
		int num = (1 + ed->numItems[ammo->idx])
			* (float) (1.0f + missedPrimary / 100.0);

		/* pack some ammo */
		while (num--) {
			weight = GetInventoryState(inv, tuNeed) + item.getWeight();
			maxTU = GET_TU(speed, GET_ENCUMBRANCE_PENALTY(weight, chr->score.skills[ABILITY_POWER]));

			Item mun(ammo);
			/* ammo to backpack; belt is for knives and grenades */
			if (weight <= maxWeight * WEIGHT_FACTOR && tuNeed <= maxTU)
					numpacked += tryAddToInventory(inv, &mun, &csi->ids[CID_BACKPACK]);
			/* no problem if no space left; one ammo already loaded */
			if (numpacked > ammoMult || numpacked * weapon->ammo > 11)
				break;
		}
	}

	return true;
}