Пример #1
0
/**
 * @brief Load a weapon with ammo.
 * @param[in] weaponList Pointer to weapon to load.
 * @param[in] inv Pointer to inventory where the change happen.
 * @param[in] srcContainer Pointer to inventorydef where to search ammo.
 * @param[in] destContainer Pointer to inventorydef where the weapon is.
 */
bool INV_LoadWeapon (const invList_t *weaponList, inventory_t *inv, const invDef_t *srcContainer, const invDef_t *destContainer)
{
	invList_t *ic = NULL;
	const objDef_t *weapon;
	int x, y;

	assert(weaponList);

	weapon = weaponList->item.item;
	INVSH_GetFirstShapePosition(weaponList, &x, &y);
	x += weaponList->x;
	y += weaponList->y;
	if (weapon->oneshot) {
		ic = INVSH_SearchInInventory(inv, destContainer, x, y);
		if (ic) {
			ic->item.ammoLeft = weapon->ammo;
			ic->item.ammo = weapon;
			return true;
		}
	} else if (weapon->reload) {
		const itemFilterTypes_t equipType = INV_GetFilterFromItem(weapon);
		int i = 0;
		/* search an ammo */
		while (i < weapon->numAmmos && !ic) {
			const objDef_t *ammo = weapon->ammos[i];
			ic = INV_SearchInInventoryWithFilter(inv, srcContainer, ammo, equipType);
			i++;
		}
		if (ic) {
			return INV_MoveItem(inv, destContainer, x, y, srcContainer, ic, NULL);
		}
	}

	return false;
}
Пример #2
0
/**
 * @brief Gets location of the item the mouse is over
 * @param[in] node The container-node.
 * @param[in] mouseX X location of the mouse.
 * @param[in] mouseY Y location of the mouse.
 * @param[out] contX X location in the container (index of item in row).
 * @param[out] contY Y location in the container (row).
 * @sa UI_ContainerNodeSearchInScrollableContainer
 */
static invList_t *UI_ContainerNodeGetItemAtPosition (const uiNode_t* const node, int mouseX, int mouseY, int* contX = NULL, int* contY = NULL)
{
	invList_t *result = NULL;

	if (!ui_inventory)
		return NULL;

	/* Get coordinates inside a scrollable container (if it is one). */
	if (UI_IsScrollContainerNode(node)) {
		Sys_Error("UI_ContainerNodeGetItemAtPosition is not usable for scrollable containers!");
	} else {
		vec2_t nodepos;
		int fromX, fromY;

		UI_GetNodeAbsPos(node, nodepos);
		/* Normalize screen coordinates to container coordinates. */
		fromX = (int) (mouseX - nodepos[0]) / C_UNIT;
		fromY = (int) (mouseY - nodepos[1]) / C_UNIT;
		if (contX)
			*contX = fromX;
		if (contY)
			*contY = fromY;

		result = INVSH_SearchInInventory(ui_inventory, EXTRADATACONST(node).container, fromX, fromY);
	}
	return result;
}
Пример #3
0
void CL_InvAmmo (const eventRegister_t *self, dbuffer *msg)
{
    invList_t	*ic;
    le_t	*le;
    int		number;
    int		ammo, type, x, y;
    containerIndex_t container;

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

    le = LE_Get(number);
    if (!le) {
        Com_DPrintf(DEBUG_CLIENT, "InvAmmo message ignored... LE not found\n");
        return;
    }

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

    assert(container >= 0);
    assert(container < MAX_INVDEFS);
    ic = INVSH_SearchInInventory(&le->i, INVDEF(container), x, y);
    if (!ic)
        return;

    /* set new ammo */
    ic->item.ammoLeft = ammo;
    ic->item.ammo = INVSH_GetItemByIDX(type);
}
Пример #4
0
/**
 * @sa CL_InvAdd
 */
void CL_InvDel (const eventRegister_t *self, dbuffer *msg)
{
	le_t	*le;
	int		number;
	int		x, y;
	containerIndex_t container;
	invList_t	*ic;

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

	le = LE_Get(number);
	if (!le)
		Com_Error(ERR_DROP, "InvDel message ignored... LE not found\n");

	/* update the local entity to ensure that the correct weapon/item is rendered in the battlescape */
	if (container == csi.idRight)
		le->right = NONE;
	else if (container == csi.idLeft)
		le->left = NONE;
	else if (container == csi.idExtension)
		le->extension = NONE;
	else if (container == csi.idHeadgear)
		le->headgear = NONE;

	if (le->type == ET_ACTOR || le->type == ET_ACTOR2x2)
		LE_SetThink(le, LET_StartIdle);

	ic = INVSH_SearchInInventory(&le->i, INVDEF(container), x, y);
	/* ic can be null for other team actors - we don't the full inventory of them, only
	 * the object index */
	if (!ic)
		return;

	if (!cls.i.RemoveFromInventory(&cls.i, &le->i, INVDEF(container), ic))
		Com_Error(ERR_DROP, "CL_InvDel: No item was removed from container %i", container);

	if (le == selActor)
		Cmd_ExecuteString("hud_updateactorload");

	/* update the rendered item after it was removed from the floor container */
	if (LE_IsItem(le))
		LE_PlaceItem(le);
}
Пример #5
0
/**
 * @brief The client sent us a message that he did something. We now execute the related function(s) and notify him if necessary.
 * @param[in] player The player to execute the action for (the actor belongs to this player)
 * @note a client action will also send the server side edict number to determine the actor
 */
int G_ClientAction (player_t * player)
{
	player_action_t action;
	int num;
	pos3_t pos;
	int i;
	fireDefIndex_t firemode;
	int from, fx, fy, to, tx, ty;
	actorHands_t hand;
	int fmIdx, objIdx;
	int resCrouch, resShot;
	edict_t *ent;
	const char *format;

	/* read the header */
	action = (player_action_t)gi.ReadByte();
	num = gi.ReadShort();

	ent = G_EdictsGetByNum(num);
	if (ent == NULL)
		return action;

	format = pa_format[action];

	switch (action) {
	case PA_NULL:
		/* do nothing on a null action */
		break;

	case PA_TURN:
		gi.ReadFormat(format, &i);
		G_ClientTurn(player, ent, (dvec_t) i);
		break;

	case PA_MOVE:
		gi.ReadFormat(format, &pos);
		G_ClientMove(player, player->pers.team, ent, pos);
		break;

	case PA_STATE:
		gi.ReadFormat(format, &i);
		G_ClientStateChange(player, ent, i, true);
		break;

	case PA_SHOOT:
		gi.ReadFormat(format, &pos, &i, &firemode, &from);
		G_ClientShoot(player, ent, pos, i, firemode, NULL, true, from);
		break;

	case PA_INVMOVE:
		gi.ReadFormat(format, &from, &fx, &fy, &to, &tx, &ty);

		if (from < 0 || from >= gi.csi->numIDs || to < 0 || to >= gi.csi->numIDs) {
			gi.DPrintf("G_ClientAction: PA_INVMOVE Container index out of range. (from: %i, to: %i)\n", from, to);
		} else {
			const invDef_t *fromPtr = INVDEF(from);
			const invDef_t *toPtr = INVDEF(to);
			invList_t *fromItem = INVSH_SearchInInventory(&ent->chr.i, fromPtr, fx, fy);
			if (fromItem)
				G_ActorInvMove(ent, fromPtr, fromItem, toPtr, tx, ty, true);
		}
		break;

	case PA_USE:
		if (ent->clientAction) {
			edict_t *actionEnt;

			/* read the door the client wants to open */
			gi.ReadFormat(format, &i);

			/* get the door edict */
			actionEnt = G_EdictsGetByNum(i);

			/* maybe the door is no longer 'alive' because it was destroyed */
			if (actionEnt && ent->clientAction == actionEnt) {
				if (G_IsDoor(actionEnt)) {
					G_ActorUseDoor(ent, actionEnt);
				}
			}
		}
		break;

	case PA_REACT_SELECT:
		gi.ReadFormat(format, &hand, &fmIdx, &objIdx);
		G_ReactionFireSettingsUpdate(ent, fmIdx, hand, INVSH_GetItemByIDX(objIdx));
		break;

	case PA_RESERVE_STATE:
		gi.ReadFormat(format, &resShot, &resCrouch);

		G_ActorReserveTUs(ent, ent->chr.reservedTus.reaction, resShot, resCrouch);
		break;

	default:
		gi.Error("G_ClientAction: Unknown action!\n");
	}
	return action;
}
Пример #6
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;
}
Пример #7
0
/**
 * @brief Call into the source when the DND end
 */
bool uiContainerNode::onDndFinished (uiNode_t *source, bool isDropped)
{
	item_t *dragItem = UI_DNDGetItem();
	const invDef_t *sourceContainer = EXTRADATACONST(source).container;

	/* if the target can't finalize the DND we stop */
	if (!isDropped) {
		return false;
	}

	assert(sourceContainer);

	/* on tactical mission */
	if (CL_BattlescapeRunning()) {
		const uiNode_t *target = UI_DNDGetTargetNode();
		const invDef_t *targetContainer = EXTRADATACONST(target).container;
		assert(targetContainer);
		CL_ActorInvMove(selActor, sourceContainer->id, dragInfoFromX, dragInfoFromY,
				targetContainer->id, dragInfoToX, dragInfoToY);
	} else {
		uiNode_t *target = UI_DNDGetTargetNode();
		if (target) {
			invList_t *fItem;
			invList_t *tItem;
			bool moved = false;
			const invDef_t *targetContainer = EXTRADATACONST(target).container;
			assert(targetContainer);
			if (UI_IsScrollContainerNode(source)) {
				fItem = UI_ContainerNodeGetExistingItem(sourceContainer, dragItem->item, MAX_FILTERTYPES);
			} else
				fItem = INVSH_SearchInInventory(ui_inventory, sourceContainer, dragInfoFromX, dragInfoFromY);
			assert(fItem);

			/** @todo We must split the move in two. Here, we should not know how to add the item to the target (see dndDrop) */
			/* Remove ammo on removing weapon from a soldier */
			if (UI_IsScrollContainerNode(target) && fItem->item.ammo && fItem->item.ammo != fItem->item.item && fItem->item.ammoLeft)
				INV_UnloadWeapon(fItem, ui_inventory, targetContainer);

			/* rotate on Shift */
			/** @todo enable Shift-rotate for battlescape too when issues are solved */
			fItem->item.rotated = Key_IsDown(K_SHIFT) && !CL_BattlescapeRunning();

			/* move the item */
			moved = INV_MoveItem(ui_inventory, targetContainer, dragInfoToX, dragInfoToY, sourceContainer, fItem, &tItem);

			/* No need to continue move wasn't successful */
			if (!moved)
				return false;

			/* Add ammo on adding weapon to a soldier */
			if (UI_IsScrollContainerNode(source) && ((fItem->item.item->weapon && !fItem->item.ammoLeft) || fItem->item.item->oneshot))
				INV_LoadWeapon(tItem, ui_inventory, sourceContainer, targetContainer);

			/* Run onChange events */
			if (source->onChange)
				UI_ExecuteEventActions(source, source->onChange);
			if (source != target && target->onChange)
				UI_ExecuteEventActions(target, target->onChange);
		}
	}

	dragInfoFromX = -1;
	dragInfoFromY = -1;
	return true;
}
Пример #8
0
/**
 * @brief Call into the target when the DND hover it
 * @return True if the DND is accepted
 */
bool uiContainerNode::onDndMove (uiNode_t *target, int x, int y)
{
	vec2_t nodepos;
	bool exists;
	int itemX = 0;
	int itemY = 0;
	item_t *dragItem = UI_DNDGetItem();

	/* we already check it when the node accept the DND */
	assert(EXTRADATA(target).container);

	UI_GetNodeAbsPos(target, nodepos);

	/** We calculate the position of the top-left corner of the dragged
	 * item in oder to compensate for the centered-drawn cursor-item.
	 * Or to be more exact, we calculate the relative offset from the cursor
	 * location to the middle of the top-left square of the item.
	 * @sa UI_LeftClick */
	if (dragItem->item) {
		itemX = C_UNIT * dragItem->item->sx / 2;	/* Half item-width. */
		itemY = C_UNIT * dragItem->item->sy / 2;	/* Half item-height. */

		/* Place relative center in the middle of the square. */
		itemX -= C_UNIT / 2;
		itemY -= C_UNIT / 2;
	}

	dragInfoToX = (mousePosX - nodepos[0] - itemX) / C_UNIT;
	dragInfoToY = (mousePosY - nodepos[1] - itemY) / C_UNIT;

	/* Check if the items already exists in the container. i.e. there is already at least one item. */
	exists = false;
	if ((INV_IsFloorDef(EXTRADATA(target).container) || INV_IsEquipDef(EXTRADATA(target).container))
		&& (dragInfoToX < 0 || dragInfoToY < 0 || dragInfoToX >= SHAPE_BIG_MAX_WIDTH || dragInfoToY >= SHAPE_BIG_MAX_HEIGHT)
		&& INVSH_ExistsInInventory(ui_inventory, EXTRADATA(target).container, dragItem)) {
		exists = true;
	}

	/* Search for a suitable position to render the item at if
	 * the container is "single", the cursor is out of bound of the container. */
	if (!exists && dragItem->item && (EXTRADATA(target).container->single
		|| dragInfoToX < 0 || dragInfoToY < 0
		|| dragInfoToX >= SHAPE_BIG_MAX_WIDTH || dragInfoToY >= SHAPE_BIG_MAX_HEIGHT)) {
#if 0
/* ... or there is something in the way. */
/* We would need to check for weapon/ammo as well here, otherwise a preview would be drawn as well when hovering over the correct weapon to reload. */
		|| (INVSH_CheckToInventory(ui_inventory, dragItem->item, EXTRADATA(target).container, dragInfoToX, dragInfoToY) == INV_DOES_NOT_FIT)) {
#endif
		INVSH_FindSpace(ui_inventory, dragItem, EXTRADATA(target).container, &dragInfoToX, &dragInfoToY, dragInfoIC);
	}

	/* we can drag every thing */
	if (UI_IsScrollContainerNode(target)) {
		return true;
	}

	{
		invList_t *fItem;

		/* is there empty slot? */
		const int checkedTo = INVSH_CheckToInventory(ui_inventory, dragItem->item, EXTRADATA(target).container, dragInfoToX, dragInfoToY, dragInfoIC);
		if (checkedTo != INV_DOES_NOT_FIT)
			return true;

		/* can we equip dragging item into the target item? */
		fItem = INVSH_SearchInInventory(ui_inventory, EXTRADATA(target).container, dragInfoToX, dragInfoToY);
		if (!fItem)
			return false;
		if (EXTRADATA(target).container->single)
			return true;
		return INVSH_LoadableInWeapon(dragItem->item, fItem->item.item);
	}
}

/**
 * @brief Call when a DND enter into the node
 */
void uiContainerNode::onDndLeave (uiNode_t *node)
{
	dragInfoToX = -1;
	dragInfoToY = -1;
}