Example #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);
	}
}
Example #2
0
TEST_F(GameTest, InventoryForDiedAlien)
{
	const char* mapName = "test_game";
	ASSERT_NE(-1, FS_CheckFile("maps/%s.bsp", mapName)) << "Map resource '" << mapName << ".bsp' for test is missing.";
	Actor* diedEnt;
	Actor* actor;
	Edict* floorItems;
	Item* invlist;
	int count;
	SV_Map(true, mapName, nullptr);
	level.activeTeam = TEAM_ALIEN;

	/* first alien that should die and drop its inventory */
	diedEnt = G_EdictsGetNextLivingActorOfTeam(nullptr, TEAM_ALIEN);
	ASSERT_TRUE(nullptr != diedEnt);
	diedEnt->HP = 0;
	ASSERT_TRUE(G_ActorDieOrStun(diedEnt, nullptr));
	ASSERT_TRUE(diedEnt->isDead());

	/* now try to collect the inventory with a second alien */
	actor = G_EdictsGetNextLivingActorOfTeam(nullptr, TEAM_ALIEN);
	ASSERT_TRUE(nullptr != actor);

	/* move to the location of the first died alien to drop the inventory into the same floor container */
	Player& player = actor->getPlayer();
	ASSERT_TRUE(G_IsAIPlayer(&player));
	G_ClientMove(player, 0, actor, diedEnt->pos);
	ASSERT_TRUE(VectorCompare(actor->pos, diedEnt->pos));

	floorItems = G_GetFloorItems(actor);
	ASSERT_TRUE(nullptr != floorItems);
	ASSERT_EQ(floorItems->getFloor(), actor->getFloor());

	/* drop everything to floor to make sure we have space in the backpack */
	G_InventoryToFloor(actor);
	ASSERT_EQ(0, GAMETEST_GetItemCount(actor, CID_BACKPACK));

	invlist = actor->getContainer(CID_BACKPACK);
	ASSERT_TRUE(nullptr == invlist);
	count = GAMETEST_GetItemCount(actor, CID_FLOOR);
	if (count > 0) {
		Item* entryToMove = actor->getFloor();
		int tx, ty;
		actor->chr.inv.findSpace(INVDEF(CID_BACKPACK), entryToMove, &tx, &ty, entryToMove);
		if (tx == NONE)
			return;
		Com_Printf("trying to move item %s from floor into backpack to pos %i:%i\n", entryToMove->def()->name, tx, ty);
		ASSERT_TRUE(G_ActorInvMove(actor, INVDEF(CID_FLOOR), entryToMove, INVDEF(CID_BACKPACK), tx, ty, false));
		ASSERT_EQ(count - 1, GAMETEST_GetItemCount(actor, CID_FLOOR)) << "item " << entryToMove->def()->name << " could not get moved successfully from floor into backpack";
		Com_Printf("item %s was removed from floor\n", entryToMove->def()->name);
		ASSERT_EQ(1, GAMETEST_GetItemCount(actor, CID_BACKPACK)) << "item " << entryToMove->def()->name << " could not get moved successfully from floor into backpack";
		Com_Printf("item %s was moved successfully into the backpack\n", entryToMove->def()->name);
		invlist = actor->getContainer(CID_BACKPACK);
		ASSERT_TRUE(nullptr != invlist);
	}
}
Example #3
0
/**
 * @sa CL_InvDel
 * @sa G_SendInventory
 * @sa EV_INV_ADD
 */
void CL_InvAdd (const eventRegister_t* self, dbuffer* msg)
{
	const int number = NET_ReadShort(msg);
	le_t* le = LE_Get(number);
	int nr = NET_ReadShort(msg);

	if (!le)
		LE_NotFoundError(number);

	le->flags &= ~LE_REMOVE_NEXT_FRAME;

	for (; nr-- > 0;) {
		Item item;
		containerIndex_t container;
		int x, y;
		CL_NetReceiveItem(msg, &item, &container, &x, &y);

		if (LE_IsItem(le)) {
			if (container != CID_FLOOR)
				Com_Error(ERR_DROP, "InvAdd for ET_ITEM but target container is not the floor but %i", container);
		} else if (INVDEF(container)->temp) {
			Com_Error(ERR_DROP, "InvAdd for %i to temp container %i", le->type, container);
		}

		if (cls.i.addToInventory(&le->inv, &item, INVDEF(container), x, y, item.getAmount()) == nullptr)
			Com_Error(ERR_DROP, "InvAdd failed - could not add %i item(s) of %s to container %i",
					item.getAmount(), item.def()->id, container);

		if (container == CID_RIGHT)
			le->right = item.def()->idx;
		else if (container == CID_LEFT)
			le->left = item.def()->idx;
		else if (container == CID_HEADGEAR)
			le->headgear = item.def()->idx;
	}

	switch (le->type) {
	case ET_ACTOR:
	case ET_ACTOR2x2:
		if (LE_IsSelected(le))
			Cmd_ExecuteString("hud_updateactorload");
		LE_SetThink(le, LET_StartIdle);
		break;
	case ET_ITEM:
		LE_PlaceItem(le);
		break;
	default:
		break;
	}
}
Example #4
0
/**
 * @brief Updates the character cvars for the given character.
 *
 * The models and stats that are displayed in the menu are stored in cvars.
 * These cvars are updated here when you select another character.
 *
 * @param[in] chr Pointer to character_t (may not be null)
 * @sa CL_UGVCvars
 * @sa CL_ActorSelect
 */
static void CL_ActorCvars (const character_t* chr)
{
	Item* weapon;
	assert(chr);

	/* visible equipment */
	weapon = chr->inv.getRightHandContainer();
	if (weapon)
		Cvar_Set("mn_rweapon", "%s", weapon->def()->model);
	else
		Cvar_Set("mn_rweapon", "");
	weapon = chr->inv.getLeftHandContainer();
	if (weapon)
		Cvar_Set("mn_lweapon", "%s", weapon->def()->model);
	else
		Cvar_Set("mn_lweapon", "");
}
Example #5
0
/**
 * @brief Mission trigger
 * @todo use level.nextmap to spawn another map when every living actor has touched the mission trigger
 * @todo use level.actualRound to determine the 'King of the Hill' time
 * @note Don't set a client action here - otherwise the movement event might
 * be corrupted
 */
bool G_MissionTouch (Edict* self, Edict* activator)
{
	if (!self->owner())
		return false;

	switch (self->owner()->getTeam()) {
	case TEAM_ALIEN:
		if (G_IsAlien(activator)) {
			if (!self->count) {
				self->count = level.actualRound;
				gi.BroadcastPrintf(PRINT_HUD, _("Aliens entered target zone!"));
			}
			return true;
		} else {
			/* reset king of the hill counter */
			self->count = 0;
		}
	/* general case that also works for multiplayer teams */
	default:
		if (!activator->isSameTeamAs(self->owner())) {
			/* reset king of the hill counter */
			self->count = 0;
			return false;
		}
		if (self->owner()->count)
			return false;

		self->owner()->count = level.actualRound;
		if (!self->owner()->item) {
			gi.BroadcastPrintf(PRINT_HUD, _("Target zone is occupied!"));
			return true;
		}

		/* search the item in the activator's inventory */
		/* ignore items linked from any temp container the actor must have this in his hands */
		const Container* cont = nullptr;
		while ((cont = activator->chr.inv.getNextCont(cont))) {
			Item* item = nullptr;
			while ((item = cont->getNextItem(item))) {
				const objDef_t* od = item->def();
				/* check whether we found the searched item in the actor's inventory */
				if (!Q_streq(od->id, self->owner()->item))
					continue;

				/* drop the weapon - even if out of TUs */
				G_ActorInvMove(makeActor(activator), cont->def(), item, INVDEF(CID_FLOOR), NONE, NONE, false);
				gi.BroadcastPrintf(PRINT_HUD, _("Item was placed."));
				self->owner()->count = level.actualRound;
				return true;
			}
		}
		break;
	}
	return true;
}
Example #6
0
/**
 * @brief Add an item to a specified container in a given inventory.
 * @note Set x and y to NONE if the item should get added to an automatically chosen free spot in the container.
 * @param[in,out] inv Pointer to inventory definition, to which we will add item.
 * @param[in] item Item to add to given container (needs to have "rotated" tag already set/checked, this is NOT checked here!)
 * @param[in] container Container in given inventory definition, where the new item will be stored.
 * @param[in] x,y The location in the container.
 * @param[in] amount How many items of this type should be added. (this will overwrite the amount as defined in "item.amount")
 * @sa removeFromInventory
 * @return the @c Item pointer the item was added to, or @c nullptr in case of an error (item wasn't added)
 */
Item* InventoryInterface::addToInventory (Inventory* const inv, const Item* const item, const invDef_t* container, int x, int y, int amount)
{
	if (!item->def())
		return nullptr;

	if (amount <= 0)
		return nullptr;

	assert(inv);
	assert(container);

	if (container->single && inv->getContainer2(container->id))
		return nullptr;

	Item* ic;
	/* CID_EQUIP and CID_FLOOR */
	if (container->temp) {
		for (ic = inv->getContainer2(container->id); ic; ic = ic->getNext())
			if (ic->isSameAs(item)) {
				ic->addAmount(amount);
				Com_DPrintf(DEBUG_SHARED, "addToInventory: Amount of '%s': %i (%s)\n",
					ic->def()->name, ic->getAmount(), invName);
				return ic;
			}
	}

	if (x < 0 || y < 0 || x >= SHAPE_BIG_MAX_WIDTH || y >= SHAPE_BIG_MAX_HEIGHT) {
		/* No (sane) position in container given as parameter - find free space on our own. */
		inv->findSpace(container, item, &x, &y, nullptr);
		if (x == NONE)
			return nullptr;
	}

	const int checkedTo = inv->canHoldItem(container, item->def(), x, y, nullptr);
	assert(checkedTo);

	/* not found - add a new one */
	ic = addInvList(inv, container);

	/* Set the data in the new entry to the data we got via function-parameters.*/
	*ic = *item;
	ic->setNext(nullptr);
	ic->setAmount(amount);

	/* don't reset an already applied rotation */
	if (checkedTo == INV_FITS_ONLY_ROTATED)
		ic->rotated = true;
	ic->setX(x);
	ic->setY(y);

	return ic;
}
Example #7
0
void CL_InvReload (const eventRegister_t* self, dbuffer* msg)
{
	int		number;
	int		ammo, type, x, y;
	containerIndex_t container;

	NET_ReadFormat(msg, self->formatString, &number, &ammo, &type, &container, &x, &y);

	le_t* le = LE_Get(number);
	if (!le)
		return;

	if (le->team != cls.team)
		return;

	assert(container >= 0);
	assert(container < MAX_INVDEFS);
	Item* ic = le->inv.getItemAtPos(INVDEF(container), x, y);
	if (!ic)
		return;

	S_LoadAndPlaySample(ic->def()->reloadSound, le->origin, ic->def()->reloadAttenuation, SND_VOLUME_WEAPONS);

	/* if the displaced clip had any remaining bullets
	 * store them as loose, unless the removed clip was full */
	equipDef_t* ed = GAME_GetEquipmentDefinition();
	if (ed && ic->getAmmoLeft() > 0 && ic->getAmmoLeft() != ic->def()->ammo) {
		assert(ammo == ic->def()->ammo);
		/* Accumulate loose ammo into clips (only accessible post-mission) */
		ed->addClip(ic);
	}

	/* set new ammo */
	ic->setAmmoLeft(ammo);
	ic->setAmmoDef(INVSH_GetItemByIDX(type));

	if (le == selActor)
		Cmd_ExecuteString("hud_updateactorload");
}
Example #8
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);
}
Example #9
0
/**
 * @brief Reload weapon with actor.
 * @param[in] ent Pointer to an actor reloading weapon.
 * @param[in] invDef Reloading weapon in right or left hand.
 * @sa AI_ActorThink
 */
void G_ActorReload (Edict* ent, const invDef_t* invDef)
{
	const objDef_t* weapon;

	if (ent->getContainer(invDef->id)) {
		weapon = ent->getContainer(invDef->id)->def();
	} else if (invDef->isLeftDef() && ent->getRightHandItem()->isHeldTwoHanded()) {
		/* Check for two-handed weapon */
		invDef = INVDEF(CID_RIGHT);
		weapon = ent->getRightHandItem()->def();
	} else
		return;

	assert(weapon);

	/* LordHavoc: Check if item is researched when in singleplayer? I don't think this is really a
	 * cheat issue as in singleplayer there is no way to inject fake client commands in the virtual
	 * network buffer, and in multiplayer everything is researched */

	/* search for clips and select the one that is available easily */
	/* also try the temp containers */
	const invDef_t* bestContainer = nullptr;
	Item* ammoItem = nullptr;
	int tu = 100;
	const Container* cont = nullptr;
	while ((cont = ent->chr.inv.getNextCont(cont, true))) {
		if (cont->def()->out >= tu)
			continue;
		/* Once we've found at least one clip, there's no point
		 * searching other containers if it would take longer
		 * to retrieve the ammo from them than the one
		 * we've already found. */
		Item* item = nullptr;
		while ((item = cont->getNextItem(item))) {
			if (item->def()->isLoadableInWeapon(weapon)) {
				ammoItem = item;
				bestContainer = INVDEF(cont->id);
				tu = bestContainer->out;
				break;
			}
		}
	}

	/* send request */
	if (bestContainer)
		G_ActorInvMove(ent, bestContainer, ammoItem, invDef, 0, 0, true);
}
Example #10
0
/**
 * @brief Mission trigger
 * @todo use level.nextmap to spawn another map when every living actor has touched the mission trigger
 * @note Don't set a client action here - otherwise the movement event might
 * be corrupted
 */
bool G_MissionTouch (Edict* self, Edict* activator)
{
	if (!G_IsLivingActor(activator))
		return false;

	Actor* actor = makeActor(activator);
	const char* const actorTeam = G_MissionGetTeamString(actor->getTeam());
	if (!G_IsCivilian(actor) && self->isOpponent(actor)) {
		if (!self->item && self->count) {
			if (self->targetname) {
				gi.BroadcastPrintf(PRINT_HUD, _("%s forces are attacking the %s!"), actorTeam, self->targetname);
			} else {
				const char* const teamName = G_MissionGetTeamString(self->getTeam());
				gi.BroadcastPrintf(PRINT_HUD, _("%s forces are attacking %s target zone!"),
						actorTeam, teamName);
			}

			/* reset king of the hill counter */
			self->count = 0;
		}
		return false;
	}
	if (self->count)
		return false;

	if (self->isSameTeamAs(actor)) {
		self->count = level.actualRound;
		if (!self->item) {
			linkedList_t* touched = self->touchedList;
			while (touched) {
				const Edict* const ent = static_cast<const Edict* const>(touched->data);
				if (!self->isSameTeamAs(ent) && !G_IsDead(ent)) {
					return true;
				}
				touched = touched->next;
			}
			if (self->targetname) {
				gi.BroadcastPrintf(PRINT_HUD, _("%s forces have occupied the %s!"), actorTeam, self->targetname);
			} else {
				gi.BroadcastPrintf(PRINT_HUD, _("%s forces have occupied their target zone!"), actorTeam);
			}
			return true;
		}
	}

	/* search the item in the activator's inventory */
	/* ignore items linked from any temp container the actor must have this in his hands */
	const Container* cont = nullptr;
	while ((cont = actor->chr.inv.getNextCont(cont))) {
		Item* item = nullptr;
		while ((item = cont->getNextItem(item))) {
			const objDef_t* od = item->def();
			/* check whether we found the searched item in the actor's inventory */
			if (!Q_streq(od->id, self->item))
				continue;

			/* drop the weapon - even if out of TUs */
			G_ActorInvMove(actor, cont->def(), item, INVDEF(CID_FLOOR), NONE, NONE, false);
			if (self->targetname) {
				gi.BroadcastPrintf(PRINT_HUD, _("The %s was placed at the %s."), item->def()->name, self->targetname);
			} else {
				gi.BroadcastPrintf(PRINT_HUD, _("The %s was placed."), item->def()->name);
			}
			self->count = level.actualRound;
			return true;
		}
	}

	return false;
}
Example #11
0
/**
 * @brief Conditions for moving items between containers.
 * @param[in] inv The 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 nullptr if TU doesn't matter (outside battlescape)
 * @param[out] uponItem The item fItem is evetually dropped upon
 * @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.
 */
inventory_action_t InventoryInterface::moveInInventory (Inventory* const inv, const invDef_t* from, Item* fItem, const invDef_t* to, int tx, int ty, int* TU, Item**  uponItem)
{
	assert(to);
	assert(from);

	if (uponItem)
		*uponItem = nullptr;

	if (from == to && fItem->getX() == tx && fItem->getY() == ty)
		return IA_NONE;

	int time = from->out + to->in;
	if (from == to) {
		if (from->isFloorDef())
			time = 0;
		else
			time /= 2;
	}

	if (TU && *TU < time)
		return IA_NOTIME;

	assert(inv);

	int checkedTo = INV_DOES_NOT_FIT;
	/* 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;

		const Container& cont = inv->getContainer(from->id);
		Item* item = nullptr;
		while ((item = cont.getNextItem(item))) {
			if (item != fItem)
				continue;

			if (item->getAmount() <= 1)
				continue;
			checkedTo = inv->canHoldItem(to, item->def(), tx, ty, fItem);
			if (!(checkedTo & INV_FITS))
				return IA_NONE;

			item->setX(tx);
			item->setY(ty);
			if (uponItem)
				*uponItem = item;
			return IA_MOVE;
		}
	}

	/* If weapon is twohanded and is moved from hand to hand do nothing. */
	/* Twohanded weapon are only in CID_RIGHT. */
	if (fItem->def()->fireTwoHanded && to->isLeftDef() && from->isRightDef()) {
		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 && !fItem->isArmour())
	 || (to->implant && !fItem->def()->implant)
	 || (to->headgear && !fItem->def()->headgear)) {
		return IA_NONE;
	}

	/* Check if the target is a blocked inv-armour and source!=dest. */
	if (to->single)
		checkedTo = inv->canHoldItem(to, fItem->def(), 0, 0, fItem);
	else {
		if (tx == NONE || ty == NONE)
			inv->findSpace(to, fItem, &tx, &ty, fItem);
		/* still no valid location found */
		if (tx == NONE || ty == NONE)
			return IA_NONE;

		checkedTo = inv->canHoldItem(to, fItem->def(), tx, ty, fItem);
	}

	Item* ic;
	bool alreadyRemovedSource = false;
	if (to->armour && from != to && !checkedTo) {
		/* Store x/y origin coordinates of removed (source) item.
		 * When we re-add it we can use this. */
		const int cacheFromX = fItem->getX();
		const int cacheFromY = fItem->getY();

		/* Check if destination/blocking item is the same as source/from item.
		 * In that case the move is not needed -> abort. */
		Item* icTo = inv->getItemAtPos(to, tx, ty);
		if (fItem->def() == icTo->def())
			return IA_NONE;

		/* Actually remove the ammo from the 'from' container. */
		if (!removeFromInventory(inv, from, fItem))
			return IA_NONE;
		else
			/* Removal successful - store this info. */
			alreadyRemovedSource = true;

		Item cacheItem2 = this->cacheItem; /* Save/cache (source) item. The cacheItem is modified in MoveInInventory. */

		/* Move the destination item to the source. */
		moveInInventory(inv, to, icTo, from, cacheFromX, cacheFromY, TU, uponItem);

		/* Reset the cached item (source) (It'll be move to container emptied by destination item later.) */
		this->cacheItem = cacheItem2;
		checkedTo = inv->canHoldItem(to, this->cacheItem.def(), 0, 0, fItem);
	} 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 = inv->getItemAtPos(to, tx, ty);

		if (ic && !to->isEquipDef() && fItem->def()->isLoadableInWeapon(ic->def())) {
			/* A target-item was found and the dragged item (implicitly ammo)
			 * can be loaded in it (implicitly weapon). */
			if (ic->getAmmoLeft() >= ic->def()->ammo && ic->ammoDef() == fItem->def()) {
				/* Weapon already fully loaded with the same ammunition -> abort */
				return IA_NORELOAD;
			}
			time += ic->def()->getReloadTime();
			if (!TU || *TU >= time) {
				if (TU)
					*TU -= time;
				if (ic->getAmmoLeft() >= ic->def()->ammo) {
					/* exchange ammo */
					const Item item(ic->ammoDef());
					/* Put current ammo in place of the new ammo unless floor - there can be more than 1 item */
					const int cacheFromX = from->isFloorDef() ? NONE : fItem->getX();
					const int cacheFromY = from->isFloorDef() ? NONE : fItem->getY();

					/* Actually remove the ammo from the 'from' container. */
					if (!removeFromInventory(inv, from, fItem))
						return IA_NONE;

					/* Add the currently used ammo in place of the new ammo in the "from" container. */
					if (addToInventory(inv, &item, from, cacheFromX, cacheFromY, 1) == nullptr)
						Sys_Error("Could not reload the weapon - add to inventory failed (%s)", invName);

					ic->setAmmoDef(this->cacheItem.def());
					if (uponItem)
						*uponItem = ic;
					return IA_RELOAD_SWAP;
				} else {
					/* Actually remove the ammo from the 'from' container. */
					if (!removeFromInventory(inv, from, fItem))
						return IA_NONE;

					ic->setAmmoDef(this->cacheItem.def());
					/* loose ammo of type ic->m saved on server side */
					ic->setAmmoLeft(ic->def()->ammo);
					if (uponItem)
						*uponItem = ic;
					return IA_RELOAD;
				}
			}
			/* Not enough time -> abort. */
			return IA_NOTIME;
		}

		/* temp container like CID_EQUIP and CID_FLOOR */
		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 addToInventory below.*/
			/** @todo change the other code to browse through these things. */
			inv->findSpace(to, fItem, &tx, &ty, fItem);
			if (tx == NONE || ty == NONE) {
				Com_DPrintf(DEBUG_SHARED, "MoveInInventory - item will be added non-visible (%s)\n", invName);
			}
		} else {
			/* Impossible move -> abort. */
			return IA_NONE;
		}
	}

	/* twohanded exception - only CID_RIGHT is allowed for fireTwoHanded weapons */
	if (fItem->def()->fireTwoHanded && to->isLeftDef())
		to = &this->csi->ids[CID_RIGHT];

	switch (checkedTo) {
	case INV_DOES_NOT_FIT:
		/* Impossible move - should be handled above, but add an abort just in case */
		Com_Printf("MoveInInventory: Item doesn't fit into container.");
		return IA_NONE;
	case INV_FITS:
		/* Remove rotated tag */
		fItem->rotated = false;
		break;
	case INV_FITS_ONLY_ROTATED:
		/* Set rotated tag */
		fItem->rotated = true;
		break;
	case INV_FITS_BOTH:
		/* Leave rotated tag as-is */
		break;
	}

	/* Actually remove the item from the 'from' container (if it wasn't already removed). */
	if (!alreadyRemovedSource)
		if (!removeFromInventory(inv, from, fItem))
			return IA_NONE;

	/* successful */
	if (TU)
		*TU -= time;

	assert(this->cacheItem.def());
	ic = addToInventory(inv, &this->cacheItem, to, tx, ty, 1);

	/* return data */
	if (uponItem) {
		assert(ic);
		*uponItem = ic;
	}

	if (to->isArmourDef()) {
		assert(this->cacheItem.isArmour());
		return IA_ARMOUR;
	}

	return IA_MOVE;
}
Example #12
0
/**
 * @param[in] inv The inventory the container is in.
 * @param[in] container The container where the item should be removed.
 * @param[in] fItem The item to be removed.
 * @return true If removal was successful.
 * @return false If nothing was removed or an error occurred.
 * @sa addToInventory
 */
bool InventoryInterface::removeFromInventory (Inventory* const inv, const invDef_t* container, Item* fItem)
{
	assert(inv);
	assert(container);
	assert(fItem);

	Item* ic = inv->getContainer2(container->id);
	if (!ic)
		return false;

	/** @todo the problem here is, that in case of a move inside the same container
	 * the item don't just get updated x and y values but it is tried to remove
	 * one of the items => crap - maybe we have to change the inventory move function
	 * to check for this case of move and only update the x and y coordinates instead
	 * of calling the add and remove functions */
	if (container->single || ic == fItem) {
		this->cacheItem = *ic;
		/* temp container like CID_EQUIP and CID_FLOOR */
		if (container->temp && ic->getAmount() > 1) {
			ic->addAmount(-1);
			Com_DPrintf(DEBUG_SHARED, "removeFromInventory: Amount of '%s': %i (%s)\n",
				ic->def()->name, ic->getAmount(), invName);
			return true;
		}

		if (container->single && ic->getNext())
			Com_Printf("removeFromInventory: Error: single container %s has many items. (%s)\n", container->name, invName);

		/* An item in other containers than CID_FLOOR or CID_EQUIP should
		 * always have an amount value of 1.
		 * The other container types do not support stacking.*/
		assert(ic->getAmount() == 1);

		inv->setContainer(container->id, ic->getNext());

		/* updated invUnused to be able to reuse this space later again */
		removeInvList(ic);

		return true;
	}

	for (Item* previous = inv->getContainer2(container->id); ic; ic = ic->getNext()) {
		if (ic != fItem) {
			previous = ic;
			continue;
		}

		this->cacheItem = *ic;
		/* temp container like CID_EQUIP and CID_FLOOR */
		if (ic->getAmount() > 1 && container->temp) {
			ic->addAmount(-1);
			Com_DPrintf(DEBUG_SHARED, "removeFromInventory: Amount of '%s': %i (%s)\n",
				ic->def()->name, ic->getAmount(), invName);
			return true;
		}

		if (ic == inv->getContainer2(container->id))
			inv->setContainer(container->id, inv->getContainer2(container->id)->getNext());
		else
			previous->setNext(ic->getNext());

		removeInvList(ic);

		return true;
	}
	return false;
}