/**
 * @file
 * @brief Actor related routines.
 */

/*
Copyright (C) 2002-2013 UFO: Alien Invasion.

This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

See the GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.

*/

#include "../client.h"
#include "cl_actor.h"
#include "../cgame/cl_game.h"
#include "cl_hud.h"
#include "cl_parse.h"
#include "cl_particle.h"
#include "cl_view.h"
#include "../cl_screen.h"
#include "../ui/ui_main.h"
#include "../ui/ui_popup.h"
#include "../ui/node/ui_node_container.h"
#include "../renderer/r_entity.h"
#include "../renderer/r_mesh.h"
#include "../../common/routing.h"
#include "../../common/grid.h"

/** @brief Confirm actions in tactical mode - valid values are 0, 1 and 2 */
static cvar_t* confirm_actions;
/** @brief Player preference: should the server make guys stand for long walks, to save TU. */
static cvar_t* cl_autostand;
static cvar_t* cl_showactors;

/* public */
le_t* selActor;
pos3_t truePos; /**< The cell at the current worldlevel under the mouse cursor. */
pos3_t mousePos; /**< The cell that an actor will move to when directed to move. */
static vec3_t mouseDraggingPos; /**< The world pos, which we "grab" to scroll the world in touchscreen mode. */

/**
 * @brief If you want to change the z level of targeting and shooting,
 * use this value. Negative and positive offsets are possible
 * @sa CL_ActorTargetAlign_f
 * @sa G_ClientShoot
 * @sa G_ShootGrenade
 * @sa G_ShootSingle
 */
static int mousePosTargettingAlign = 0;

static le_t* mouseActor;
static le_t* interactEntity;
static pos3_t mouseLastPos;

/**
 * @brief Writes player action with its data.
 * @param[in] playerAction Type of action.
 * @param[in] entnum The server side edict number of the actor
 */
void MSG_Write_PA (player_action_t playerAction, int entnum, ...)
{
	va_list ap;
	dbuffer msg;

	va_start(ap, entnum);
	NET_WriteFormat(&msg, "bbs", clc_action, playerAction, entnum);
	NET_vWriteFormat(&msg, pa_format[playerAction], ap);
	va_end(ap);
	NET_WriteMsg(cls.netStream, msg);
}

/*
==============================================================
ACTOR MENU UPDATING
==============================================================
*/

void CL_ActorSetFireDef (le_t* actor, const fireDef_t* fd)
{
	if (actor->fd != fd)
		mousePosTargettingAlign = 0;
	actor->fd = fd;
}

/**
 * @brief Decide how the actor will walk, taking into account autostanding.
 * @param[in] le Pointer to an actor for which we set the moving mode.
 */
int CL_ActorMoveMode (const le_t* le)
{
	assert(le);
	if (!LE_IsCrouched(le))
		return WALKTYPE_WALKING;

	/* Is the player using autostand? */
	if (!cl_autostand->integer)
		return WALKTYPE_CROUCH_WALKING;

	/* ...and if this is a long walk... */
	if (RT_CanActorStandHere(cl.mapData->routing, le->fieldSize, le->pos)
		&& Grid_ShouldUseAutostand(&cl.pathMap, mousePos))
		return WALKTYPE_AUTOSTAND_BEING_USED;

	return WALKTYPE_AUTOSTAND_BUT_NOT_FAR_ENOUGH;
}

/**
 * @brief Returns the number of the actor in the teamlist.
 * @param[in] le The actor to search.
 * @return The number of the actor in the teamlist. Or @c -1 if the given entity is not in the team list.
 */
int CL_ActorGetNumber (const le_t* le)
{
	int actorIdx;

	assert(le);

	for (actorIdx = 0; actorIdx < cl.numTeamList; actorIdx++) {
		if (cl.teamList[actorIdx] == le)
			return actorIdx;
	}
	return -1;
}

/**
 * @brief Returns the local entity information for a character in the team list
 * @param[in] chr The character to search the local entity for.
 * @return A pointer to a le_t struct.
 */
le_t* CL_ActorGetFromCharacter (const character_t* chr)
{
	for (int i = 0; i < cl.numTeamList; ++i) {
		if (cl.teamList[i] && cl.teamList[i]->ucn == chr->ucn)
			return cl.teamList[i];
	}
	return nullptr;
}

/**
 * @brief Returns the character information for an actor in the teamlist.
 * @param[in] le The actor to search.
 * @return A pointer to a character struct.
 */
character_t* CL_ActorGetChr (const le_t* le)
{
	const linkedList_t* chrList = cl.chrList;

	LIST_Foreach(chrList, character_t, chr) {
		if (chr->ucn == le->ucn)
			return chr;
	}

	return nullptr;
}

/**
 * @param[in] shooter The local entity to get the reaction fire firedef from
 * @return The current selected firedef for reaction fire or @c nullptr if there is none
 */
const fireDef_t* CL_ActorGetReactionFireFireDef (const le_t* shooter)
{
	const character_t* chr = CL_ActorGetChr(shooter);
	if (chr == nullptr)
		return nullptr;

	const FiremodeSettings &fmSetting = chr->RFmode;
	const Item* weapon = shooter->getHandItem(fmSetting.getHand());
	if (weapon == nullptr)
		return nullptr;

	const fireDef_t* fdArray = weapon->getFiredefs();
	if (fdArray == nullptr)
		return nullptr;

	const int fmIdx = fmSetting.getFmIdx();
	if (fmIdx < 0 || fmIdx >= MAX_FIREDEFS_PER_WEAPON)
		return nullptr;

	const fireDef_t* fd = &fdArray[fmIdx];
	return fd;
}

/**
 * @param[in] shooter The local entity to get the reaction fire definition for the range check for
 * @param[in] target The target to calculate the distance to
 * @return @c true if the given @c target is out of range for the @c shooter with the current selected fire mode
 */
bool CL_ActorIsReactionFireOutOfRange (const le_t* shooter, const le_t* target)
{
	const float distance = VectorDist(shooter->origin, target->origin);
	const fireDef_t* fd = CL_ActorGetReactionFireFireDef(shooter);
	const bool outOfRange = fd->range < distance;
	return outOfRange;
}

/**
 * @brief Returns the amount of reserved TUs for a certain type.
 * @param[in] le The actor to check.
 * @param[in] type The type to check. Use RES_ALL_ACTIVE to get all reserved TUs that are not "active" (e.g. RF is skipped if disabled). RES_ALL returns ALL of them, no matter what. See reservation_types_t for a list of options.
 * @return The reserved TUs for the given type.
 * @return -1 on error.
 */
int CL_ActorReservedTUs (const le_t* le, const reservation_types_t type)
{
	character_t* chr;
	int reservedReaction, reservedCrouch, reservedShot;

	if (!le)
		return -1;

	chr = CL_ActorGetChr(le);
	if (!chr) {
		Com_DPrintf(DEBUG_CLIENT, "CL_ActorReservedTUs: No character found for le.\n");
		return -1;
	}

	reservedReaction = std::max(0, chr->reservedTus.reaction);
	reservedCrouch = std::max(0, chr->reservedTus.crouch);
	reservedShot = std::max(0, chr->reservedTus.shot);

	switch (type) {
	case RES_ALL:
		/* A summary of ALL TUs that are reserved. */
		return reservedReaction + reservedCrouch + reservedShot;
	case RES_ALL_ACTIVE: {
		/* A summary of ALL TUs that are reserved depending on their "status". */
		/* Only use reaction-value if we have RF activated. */
		if ((le->state & STATE_REACTION))
			return reservedReaction + reservedShot + reservedCrouch;
		else
			return reservedShot + reservedCrouch;
	}
	case RES_REACTION:
		return reservedReaction;
	case RES_CROUCH:
		return reservedCrouch;
	case RES_SHOT:
		return reservedShot;
	default:
		Com_DPrintf(DEBUG_CLIENT, "CL_ActorReservedTUs: Bad type given: %i\n", type);
		return -1;
	}
}

/**
 * @brief Returns the amount of usable (overall-reserved) TUs for an actor.
 * @param[in] le The actor to check.
 * @return The remaining/usable TUs for this actor
 * @return -1 on error (this includes bad [very large] numbers stored in the struct).
 */
int CL_ActorUsableTUs (const le_t* le)
{
	if (!le)
		return -1;

	return le->TU - CL_ActorReservedTUs(le, RES_ALL_ACTIVE);
}

/**
 * @brief Replace the reserved TUs for a certain type.
 * @param[in] le The actor to change it for.
 * @param[in] type The reservation type to be changed (i.e be replaced).
 * @param[in] tus How many TUs to set.
 */
void CL_ActorReserveTUs (const le_t* le, const reservation_types_t type, const int tus)
{
	character_t* chr;

	assert(type != RES_REACTION);

	if (!le || tus < 0)
		return;

	chr = CL_ActorGetChr(le);
	if (chr) {
		chrReservations_t res = chr->reservedTus;

		if (type == RES_CROUCH)
			res.crouch = tus;
		else if (type == RES_SHOT)
			res.shot = tus;

		MSG_Write_PA(PA_RESERVE_STATE, le->entnum, res.shot, res.crouch);
	}
}

/**
 * @brief Returns the actor injury modifier of the specified type.
 * @param[in] le The actor.
 * @param[in] type The injury modifier type.
 * @return The injury modifier for this actor.
 */
float CL_ActorInjuryModifier (const le_t* le, const modifier_types_t type)
{
	float mod = 0;

	if (le) {
		const character_t* chr = CL_ActorGetChr(le);
		int bodyPart;
		if (!chr)
			return 0;
		const BodyData* bodyTemplate = chr->teamDef->bodyTemplate;
		for (bodyPart = 0; bodyPart < bodyTemplate->numBodyParts(); ++bodyPart) {
			const int threshold = le->maxHP * bodyTemplate->woundThreshold(bodyPart);
			const int injury = (le->wounds.woundLevel[bodyPart] + le->wounds.treatmentLevel[bodyPart] * 0.5);
			if (injury > threshold)
				mod += 2 * bodyTemplate->penalty(bodyPart, type) * injury / le->maxHP;
		}

		switch (type) {
		case MODIFIER_REACTION:
			mod += CL_ActorInjuryModifier(le, MODIFIER_SHOOTING);
			break;
		case MODIFIER_ACCURACY:
		case MODIFIER_SHOOTING:
			++mod;
			break;
		case MODIFIER_MOVEMENT:
			mod = ceil(mod);
			break;
		default:
			Com_Printf("CL_ActorInjuryPenalty: Unused modifier type %i\n", type);
			mod = 0;
			break;
		}
	}
	return mod;
}

/**
 * @brief Find the TUs needed for the given fireDef taking into account the actor wound penalties.
 * @param[in] le The actor.
 * @param[in] fd The fire definition.
 * @param[in] reaction Whether this is a normal or reaction fire shot.
 * @return The TUs needed for the fireDef for this actor.
 */
int CL_ActorTimeForFireDef (const le_t* le, const fireDef_t* fd, bool reaction)
{
	if (!fd)
		return -1;

	return fd->time * CL_ActorInjuryModifier(le, reaction ? MODIFIER_REACTION : MODIFIER_SHOOTING);
}

/*
==============================================================
ACTOR SELECTION AND TEAM LIST
==============================================================
*/

/**
 * @brief Adds the actor to the team list.
 * @sa CL_ActorAppear
 * @sa CL_ActorRemoveFromTeamList
 * @param le Pointer to local entity struct
 */
void CL_ActorAddToTeamList (le_t* le)
{
	int actorIdx;
	const size_t size = lengthof(cl.teamList);

	/* test team */
	if (!le || le->team != cls.team || le->pnum != cl.pnum || LE_IsDead(le))
		return;

	/* check list for that actor */
	actorIdx = CL_ActorGetNumber(le);

	/* add it */
	if (actorIdx == -1) {
		/* check list length */
		if (cl.numTeamList >= size) {
			Com_Printf("Too many actors on the teamlist!\n");
			return;
		}
		cl.teamList[cl.numTeamList] = le;
		UI_ExecuteConfunc("hudenable %i", cl.numTeamList);
		cl.numTeamList++;
		if (cl.numTeamList == 1)
			CL_ActorSelectList(0);
	} else {
		UI_ExecuteConfunc("hudenable %i", actorIdx);
	}
}

void CL_ActorCleanup (le_t* le)
{
	cls.i.destroyInventory(&le->inv);
}

/**
 * @brief Removes an actor (from your team) from the team list.
 * @sa CL_ActorStateChange
 * @sa CL_ActorAddToTeamList
 * @param[in,out] le Pointer to local entity struct of the actor of your team
 */
void CL_ActorRemoveFromTeamList (le_t* le)
{
	int i;

	if (!le)
		return;

	for (i = 0; i < cl.numTeamList; i++) {
		if (cl.teamList[i] == le) {
			if (!LE_IsStunned(le)) {
				CL_ActorCleanup(le);
				/* remove from list */
				cl.teamList[i] = nullptr;
			}

			/* disable hud button */
			UI_ExecuteConfunc("huddisable %i", i);

			break;
		}
	}

	/* check selection */
	if (LE_IsSelected(le)) {
		for (i = 0; i < cl.numTeamList; i++) {
			le_t* tl = cl.teamList[i];
			if (tl && CL_ActorSelect(tl))
				break;
		}

		if (i == cl.numTeamList)
			CL_ActorSelect(nullptr);
	}
}

/**
 * @brief Selects an actor.
 * @param le Pointer to local entity struct. If this is @c nullptr the ui_inventory that is linked from the actors
 * @sa CL_UGVCvars
 * @sa CL_ActorCvars
 */
bool CL_ActorSelect (le_t* le)
{
	int actorIdx;
	character_t* chr;

	/* test team */
	if (!le) {
		if (selActor)
			selActor->flags &= ~LE_SELECTED;
		selActor = nullptr;
		ui_inventory = nullptr;
		return false;
	}

	if (le->pnum != cl.pnum || LE_IsDead(le) || !le->inuse)
		return false;

	if (LE_IsSelected(le)) {
		mousePosTargettingAlign = 0;
		return true;
	}

	if (selActor)
		selActor->flags &= ~LE_SELECTED;

	mousePosTargettingAlign = 0;
	selActor = le;
	selActor->flags |= LE_SELECTED;
	ui_inventory = &selActor->inv;

	if (le->state & RF_IRGOGGLESSHOT)
		refdef.rendererFlags |= RDF_IRGOGGLES;
	else
		refdef.rendererFlags &= ~RDF_IRGOGGLES;

	if (le->clientAction != nullptr)
		UI_ExecuteConfunc("enable_clientaction");
	else
		UI_ExecuteConfunc("disable_clientaction");

	actorIdx = CL_ActorGetNumber(le);
	if (actorIdx == -1)
		return false;

	/* console commands, update cvars */
	Cvar_ForceSet("cl_selected", va("%i", actorIdx));

	chr = CL_ActorGetChr(le);
	if (!chr)
		Com_Error(ERR_DROP, "No character given for local entity!");

	CL_UpdateCharacterValues(chr);

	CL_ActorConditionalMoveCalc(le);

	return true;
}

/**
 * @brief Selects an actor from a list.
 *
 * This function is used to select an actor from the lists that are
 * used in equipment and team assemble screens
 *
 * @param num The index value from the list of actors
 *
 * @sa CL_ActorSelect
 * @return true if selection was possible otherwise false
 */
bool CL_ActorSelectList (int num)
{
	le_t* le;

	/* check if actor exists */
	if (num >= cl.numTeamList || num < 0)
		return false;

	/* select actor */
	le = cl.teamList[num];
	if (!le || !CL_ActorSelect(le))
		return false;

	/* center view (if wanted) */
	LE_CenterView(le);
	Cvar_SetValue("cl_worldlevel", le->pos[2]);

	return true;
}

/**
 * @brief selects the next actor
 */
bool CL_ActorSelectNext (void)
{
	int selIndex = -1;
	const int num = cl.numTeamList;
	int i;

	/* find index of currently selected actor */
	for (i = 0; i < num; i++) {
		const le_t* le = cl.teamList[i];
		if (le && le->inuse && LE_IsSelected(le) && !LE_IsDead(le)) {
			selIndex = i;
			break;
		}
	}
	if (selIndex < 0)
		return false;			/* no one selected? */

	/* cycle round */
	i = selIndex;
	while (true) {
		i = (i + 1) % num;
		if (i == selIndex)
			break;
		if (CL_ActorSelectList(i))
			return true;
	}
	return false;
}

/**
 * @brief selects the previous actor
 */
bool CL_ActorSelectPrev (void)
{
	int selIndex = -1;
	const int num = cl.numTeamList;
	int i;

	/* find index of currently selected actor */
	for (i = 0; i < num; i++) {
		const le_t* le = cl.teamList[i];
		if (le && le->inuse && LE_IsSelected(le) && !LE_IsDead(le)) {
			selIndex = i;
			break;
		}
	}
	if (selIndex < 0)
		return false;			/* no one selected? */

	/* cycle round */
	i = selIndex;
	while (true) {
	/*	i = (i - 1) % num; */
		i--; if (i < 0) i = num - 1;
		if (i == selIndex)
			break;
		if (CL_ActorSelectList(i))
			return true;
	}
	return false;
}


/*
==============================================================
ACTOR MOVEMENT AND SHOOTING
==============================================================
*/

/**
 * @brief A list of locations that cannot be moved to.
 * @note Pointer to le->pos or edict->pos followed by le->fieldSize or edict->fieldSize
 * @see CL_BuildForbiddenList
 */
static pos_t* forbiddenList[MAX_FORBIDDENLIST];
/**
 * @brief Current length of fb_list.
 * @note all byte pointers in the fb_list list (pos + fieldSize)
 * @see fb_list
 */
static int forbiddenListLength;

/**
 * @brief Builds a list of locations that cannot be moved to (client side).
 * @sa G_MoveCalc
 * @sa G_BuildForbiddenList <- server side
 * @sa Grid_CheckForbidden
 * @note This is used for pathfinding.
 * It is a list of where the selected unit can not move to because others are standing there already.
 */
static void CL_BuildForbiddenList (void)
{
	le_t* le = nullptr;

	forbiddenListLength = 0;

	while ((le = LE_GetNextInUse(le))) {
		if (LE_IsInvisible(le))
			continue;
		/* Dead ugv will stop walking, too. */
		if (le->type == ET_ACTOR2x2 || (!LE_IsStunned(le) && LE_IsLivingAndVisibleActor(le))) {
			forbiddenList[forbiddenListLength++] = le->pos;
			forbiddenList[forbiddenListLength++] = (byte*)&le->fieldSize;
		}
	}

#ifdef PARANOID
	if (forbiddenListLength > MAX_FORBIDDENLIST)
		Com_Error(ERR_DROP, "CL_BuildForbiddenList: list too long!");
#endif
}

#ifdef DEBUG
/**
 * @brief Draws a marker for all blocked map-positions.
 * @note currently uses basically the same code as CL_BuildForbiddenList
 * @note usage in console: "debug_drawblocked"
 * @todo currently the particles stay a _very_ long time ... so everybody has to stand still in order for the display to be correct.
 * @sa CL_BuildForbiddenList
 */
static void CL_DisplayBlockedPaths_f (void)
{
	le_t* le = nullptr;
	int j;
	ptl_t* ptl;
	vec3_t s;

	while ((le = LE_GetNextInUse(le))) {
		switch (le->type) {
		case ET_ACTOR:
		case ET_ACTOR2x2:
			/* draw blocking cursor at le->pos */
			if (!LE_IsDead(le))
				Grid_PosToVec(cl.mapData->routing, le->fieldSize, le->pos, s);
			break;
		case ET_DOOR:
		case ET_BREAKABLE:
		case ET_ROTATING:
			VectorCopy(le->origin, s);
			break;
		default:
			continue;
		}

		ptl = CL_ParticleSpawn("blocked_field", 0, s, nullptr, nullptr);
		ptl->rounds = 2;
		ptl->roundsCnt = 2;
		ptl->life = 10000;
		ptl->t = 0;
		if (le->fieldSize == ACTOR_SIZE_2x2) {
			/* If this actor blocks 4 fields draw them as well. */
			for (j = 0; j < 3; j++) {
				ptl_t* ptl2 = CL_ParticleSpawn("blocked_field", 0, s, nullptr, nullptr);
				ptl2->rounds = ptl->rounds;
				ptl2->roundsCnt = ptl->roundsCnt;
				ptl2->life = ptl->life;
				ptl2->t = ptl->t;
			}
		}
	}
}
#endif

/**
 * @brief Recalculate forbidden list, available moves and actor's move length
 * for the current selected actor.
 * @note An attempt to do this with le->TU to save time ended up with the first actor not being able to move at gamestart.
 * @todo seems like this function is called *before* the TUs are set
 */
void CL_ActorConditionalMoveCalc (le_t* le)
{
	CL_BuildForbiddenList();
	if (le && LE_IsSelected(le)) {
		Grid_CalcPathing(cl.mapData->routing, le->fieldSize, &cl.pathMap, le->pos, MAX_ROUTE_TUS, forbiddenList, forbiddenListLength);
		CL_ActorResetMoveLength(le);
	}
}

/**
 * @brief Returns the actor that is closest to the given origin
 */
le_t* CL_ActorGetClosest (const vec3_t origin, int team)
{
	le_t* closest = nullptr;
	le_t* le = nullptr;
	while ((le = LE_GetNextInUse(le))) {
		if (le->team != team || !LE_IsLivingAndVisibleActor(le))
			continue;

		if (closest == nullptr || VectorDist(le->origin, origin) < VectorDist(closest->origin, origin))
			closest = le;
	}
	return closest;
}

/**
 * @brief Checks that an action is valid.
 * @param[in] le Pointer to actor for which we check an action.
 * @return true if action is valid.
 */
int CL_ActorCheckAction (const le_t* le)
{
	if (!le)
		return false;

	if (le->isMoving())
		return false;

	if (!cls.isOurRound()) {
		HUD_DisplayMessage(_("It is not your turn!"));
		return false;
	}

	return true;
}

/**
 * @brief Get the real move length (depends on crouch-state of the current actor).
 * @note The part of the line that is not reachable in this turn (i.e. not enough
 * @note TUs left) will be drawn differently.
 * @param[in] to The position in the map to calculate the move-length for.
 * @param[in] le Pointer to actor for which we calculate move lenght.
 * @return The amount of TUs that are needed to walk to the given grid position
 */
static byte CL_ActorMoveLength (const le_t* le, const pos3_t to)
{
	const bool useAutostand = LE_IsCrouched(le) && cl_autostand->integer
				&& Grid_ShouldUseAutostand(&cl.pathMap, to);
	const int autostandTU = useAutostand ? 2 * TU_CROUCH : 0;
	byte crouchingState = LE_IsCrouched(le) && !useAutostand ? 1 : 0;
	const int length = Grid_MoveLength(&cl.pathMap, to, crouchingState, false);
	int dvec, numSteps = 0;
	pos3_t pos;

	if (!length || length == ROUTING_NOT_REACHABLE)
		return length;

	VectorCopy(to, pos);
	while ((dvec = Grid_MoveNext(&cl.pathMap, pos, crouchingState)) != ROUTING_UNREACHABLE) {
		++numSteps;
		PosSubDV(pos, crouchingState, dvec); /* We are going backwards to the origin. */
	}

	return std::min(ROUTING_NOT_REACHABLE, length + static_cast<int>(numSteps
			* CL_ActorInjuryModifier(le, MODIFIER_MOVEMENT)) + autostandTU);
}

/**
 * @brief Recalculates the currently selected Actor's move length.
 * @param[in,out] le Pointer to actor for which we reset move lenght.
 */
void CL_ActorResetMoveLength (le_t* le)
{
	le->actorMoveLength = CL_ActorMoveLength(le, mousePos);
}

/**
 * @brief Draws the way to walk when confirm actions is activated.
 * @param[in] to The location we draw the line to (starting with the location of selActor)
 * @return true if everything went ok, otherwise false.
 * @sa CL_MaximumMove (similar algo.)
 * @sa CL_AddTargetingBox
 */
static bool CL_ActorTraceMove (const pos3_t to)
{
	byte length;
	vec3_t vec, oldVec;
	pos3_t pos;
	int dvec;
	byte crouchingState;

	if (!selActor)
		return false;

	length = CL_ActorMoveLength(selActor, to);
	if (!length || length >= ROUTING_NOT_REACHABLE)
		return false;

	crouchingState = LE_IsCrouched(selActor) ? 1 : 0;

	Grid_PosToVec(cl.mapData->routing, selActor->fieldSize, to, oldVec);
	VectorCopy(to, pos);

	while ((dvec = Grid_MoveNext(&cl.pathMap, pos, crouchingState)) != ROUTING_UNREACHABLE) {
		length = CL_ActorMoveLength(selActor, pos);
		PosSubDV(pos, crouchingState, dvec); /* We are going backwards to the origin. */
		Grid_PosToVec(cl.mapData->routing, selActor->fieldSize, pos, vec);
		if (length > CL_ActorUsableTUs(selActor))
			CL_ParticleSpawn("longRangeTracer", 0, vec, oldVec);
		else if (crouchingState)
			CL_ParticleSpawn("crawlTracer", 0, vec, oldVec);
		else
			CL_ParticleSpawn("moveTracer", 0, vec, oldVec);
		VectorCopy(vec, oldVec);
	}
	return true;
}

/**
 * @brief Return the last position we can walk to with a defined amount of TUs.
 * @param[in] to The location we want to reach.
 * @param[in] le Pointer to an actor for which we check maximum move.
 * @param[in,out] pos The location we can reach with the given amount of TUs.
 * @sa CL_TraceMove (similar algo.)
 */
static void CL_ActorMaximumMove (const pos3_t to, const le_t* le, pos3_t pos)
{
	int dvec;
	byte crouchingState = le && LE_IsCrouched(le) ? 1 : 0;
	const int tus = CL_ActorUsableTUs(le);
	const byte length = CL_ActorMoveLength(le, to);
	if (!length || length >= ROUTING_NOT_REACHABLE)
		return;

	VectorCopy(to, pos);

	while ((dvec = Grid_MoveNext(&cl.pathMap, pos, crouchingState)) != ROUTING_UNREACHABLE) {
		const byte length2 = CL_ActorMoveLength(le, pos);
		if (length2 <= tus)
			return;
		PosSubDV(pos, crouchingState, dvec); /* We are going backwards to the origin. */
	}
}

void CL_ActorSetMode (le_t* actor, actorModes_t actorMode)
{
	actor->actorMode = actorMode;
}

/**
 * @brief Starts moving actor.
 * @param[in] le
 * @param[in] to
 * @sa CL_ActorActionMouse
 * @sa CL_ActorSelectMouse
 */
void CL_ActorStartMove (le_t* le, const pos3_t to)
{
	byte length;
	pos3_t toReal;

	if (IN_GetMouseSpace() != MS_WORLD)
		return;

	if (!CL_ActorCheckAction(le))
		return;

	length = CL_ActorMoveLength(le, to);

	if (!length || length >= ROUTING_NOT_REACHABLE) {
		/* move not valid, don't even care to send */
		return;
	}

	/* Get the last position we can walk to with the usable TUs. */
	CL_ActorMaximumMove(to, le, toReal);

	/* Get the cost of the new position just in case. */
	length = CL_ActorMoveLength(le, toReal);

	if (CL_ActorUsableTUs(le) < length) {
		/* We do not have enough _usable_ TUs to move so don't even try to send. */
		/* This includes a check for reserved TUs (which isn't done on the server!) */
		return;
	}

	/* change mode to move now */
	CL_ActorSetMode(le, M_MOVE);

	/* move seems to be possible; send request to server */
	MSG_Write_PA(PA_MOVE, le->entnum, toReal);
}


/**
 * @brief Shoot with actor.
 * @param[in] le Who is shooting
 * @param[in] at Position you are targeting to
 */
void CL_ActorShoot (const le_t* le, const pos3_t at)
{
	int type;

	if (IN_GetMouseSpace() != MS_WORLD)
		return;

	if (!CL_ActorCheckAction(le))
		return;

	if (IS_MODE_FIRE_RIGHT(le->actorMode)) {
		type = ST_RIGHT;
	} else if (IS_MODE_FIRE_LEFT(le->actorMode)) {
		type = ST_LEFT;
	} else if (IS_MODE_FIRE_HEADGEAR(le->actorMode)) {
		type = ST_HEADGEAR;
	} else
		return;

	MSG_Write_PA(PA_SHOOT, le->entnum, at, type, le->currentSelectedFiremode, mousePosTargettingAlign);
}

/**
 * @brief Searches the clip with the least TU usage to put it into the weapon
 * @param invList The inventory list that can be used outside of this function for the found ammo
 * @param inv The inventory to do the search in
 * @param weapon The weapon to reload
 * @return @c NONE if no container was found, the container id otherwise.
 */
int CL_ActorGetContainerForReload (Item** invList, const Inventory* inv, const objDef_t* weapon)
{
	containerIndex_t container;
	int tu = 100;
	containerIndex_t bestContainer = NONE;

	/* also search the linked ground floor tile (temp container) */
	for (container = 0; container < CID_MAX; ++container) {
		if (INVDEF(container)->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. */
		for (Item* ic = inv->getContainer2(container); ic; ic = ic->getNext()) {
			const objDef_t* od = ic->def();
			if (!od->isLoadableInWeapon(weapon) || !GAME_ItemIsUseable(od))
				continue;
			tu = INVDEF(container)->out;
			bestContainer = container;
			*invList = ic;
			break;
		}
	}
	return bestContainer;
}

/**
 * @brief Reload weapon with actor.
 * @param[in,out] le The actor to reload the weapon for
 * @param[in] containerID The container to reload
 * @sa CL_ActorCheckAction
 */
void CL_ActorReload (le_t* le, containerIndex_t containerID)
{
	Inventory* inv;
	Item* ic;
	const objDef_t* weapon;
	containerIndex_t bestContainer;

	if (!CL_ActorCheckAction(le))
		return;

	/* check weapon */
	inv = &le->inv;

	if (inv->getContainer2(containerID)) {
		weapon = inv->getContainer2(containerID)->def();
	} else if (containerID == CID_LEFT && inv->getContainer2(CID_RIGHT)->isHeldTwoHanded()) {
		/* Check for two-handed weapon */
		containerID = CID_RIGHT;
		weapon = inv->getContainer2(containerID)->def();
	} else {
		/* no weapon in the reloadable containers found */
		return;
	}

	if (!weapon)
		return;

	/* return if the weapon is not reloadable */
	if (!weapon->isReloadable())
		return;

	if (!GAME_ItemIsUseable(weapon)) {
		HUD_DisplayMessage(_("You cannot reload this unknown item."));
		return;
	}

	bestContainer = CL_ActorGetContainerForReload(&ic, inv, weapon);
	/* send request */
	if (bestContainer != NONE) {
		int x, y;

		ic->getFirstShapePosition(&x, &y);
		x += ic->getX();
		y += ic->getY();

		CL_ActorInvMove(le, bestContainer, x, y, containerID, 0, 0);
	}
}

/**
 * @brief Sends an inventory move event to the server
 * @param le The le that is doing the inventory move (an actor)
 * @param fromContainer The container to fetch the item from
 * @param fromX The x position in the container to get the item from
 * @param fromY The y position in the container to get the item from
 * @param toContainer The container to store the item in
 * @param toX The x position in the container to move the item to
 * @param toY The y position in the container to move the item to
 */
void CL_ActorInvMove (const le_t* le, containerIndex_t fromContainer, int fromX, int fromY, containerIndex_t toContainer, int toX, int toY)
{
	const invDef_t* fromPtr = INVDEF(fromContainer);

	assert(CL_BattlescapeRunning());
	assert(le);
	assert(LE_IsActor(le));

	const Item* item = le->inv.getItemAtPos(fromPtr, fromX, fromY);

	if (item != nullptr) {
		const character_t* chr = CL_ActorGetChr(le);
		if (!le->inv.canHoldItemWeight(fromContainer, toContainer, *item, GAME_GetChrMaxLoad(chr))) {
			UI_Popup(_("Warning"), _("This soldier can not carry anything else."));
			return;
		}
		MSG_Write_PA(PA_INVMOVE, le->entnum, fromContainer, fromX, fromY, toContainer, toX, toY);
	}
}

/**
 * @brief Uses the current selected entity in the battlescape. Can e.g. open the selected door.
 * @sa G_ClientUseEdict
 */
static void CL_ActorUse (const le_t* le)
{
	if (!CL_ActorCheckAction(le))
		return;

	assert(le->clientAction);

	MSG_Write_PA(PA_USE, le->entnum, le->clientAction->entnum);
	Com_DPrintf(DEBUG_CLIENT, "CL_ActorUse: Use door number: %i (actor %i).\n", le->clientAction->entnum, le->entnum);
}

/**
 * @brief Hud callback to use the current selected entity
 */
static void CL_ActorUse_f (void)
{
	le_t* actor = selActor;

	if (!CL_ActorCheckAction(actor))
		return;

	/* no client action */
	if (actor->clientAction == nullptr) {
		Com_DPrintf(DEBUG_CLIENT, "CL_ActorUse_f: No client_action set for actor with entnum %i.\n", actor->entnum);
		return;
	}

	if (LE_IsDoor(actor->clientAction)) {
		/* Check if we should even try to send this command (no TUs left or). */
		if (CL_ActorUsableTUs(actor) >= TU_DOOR_ACTION)
			CL_ActorUse(actor);
	}
}

/**
 * @brief Checks whether we are in fire mode or node
 * @param mode The actor mode
 * @return @c true if we are in fire mode, @c false otherwise
 */
bool CL_ActorFireModeActivated (const actorModes_t mode)
{
	return IS_MODE_FIRE_RIGHT(mode) || IS_MODE_FIRE_LEFT(mode) || IS_MODE_FIRE_HEADGEAR(mode);
}

/**
 * @brief Turns the actor around without moving
 */
void CL_ActorTurnMouse (void)
{
	vec3_t directionVector;
	dvec_t dvec;

	if (IN_GetMouseSpace() != MS_WORLD)
		return;

	if (!CL_ActorCheckAction(selActor))
		return;

	if (CL_ActorUsableTUs(selActor) < TU_TURN) {
		/* Cannot turn because of not enough usable TUs. */
		return;
	}

	/* check for fire-modes, and cancel them */
	if (CL_ActorFireModeActivated(selActor->actorMode)) {
		CL_ActorActionMouse();
		return; /* and return without turning */
	}

	/* calculate dvec */
	VectorSubtract(mousePos, selActor->pos, directionVector);
	dvec = AngleToDV((int) (atan2(directionVector[1], directionVector[0]) * todeg));

	/* send message to server */
	MSG_Write_PA(PA_TURN, selActor->entnum, dvec);
}

/**
 * @brief Stands or crouches actor.
 */
static void CL_ActorStandCrouch_f (void)
{
	if (!CL_ActorCheckAction(selActor))
		return;

	/* In case of standing up also check the headroom */
	if (LE_IsCrouched(selActor) && !RT_CanActorStandHere(cl.mapData->routing, selActor->fieldSize, selActor->pos))
		return;

	/* Check if we should even try to send this command (no TUs left or). */
	if (CL_ActorUsableTUs(selActor) >= TU_CROUCH || CL_ActorReservedTUs(selActor, RES_CROUCH) >= TU_CROUCH) {
		/* send a request to toggle crouch to the server */
		MSG_Write_PA(PA_STATE, selActor->entnum, STATE_CROUCHED);
	}
}

/**
 * @brief Toggles the headgear for the current selected player
 */
static void CL_ActorUseHeadgear_f (void)
{
	const mouseSpace_t tmpMouseSpace = IN_GetMouseSpace();

	/* this can be executed by a click on a hud button
	 * but we need MS_WORLD mouse space to let the shooting
	 * function work */
	IN_SetMouseSpace(MS_WORLD);

	if (!CL_ActorCheckAction(selActor))
		return;

	Item* headgear = selActor->inv.getHeadgear();
	if (!headgear)
		return;

	CL_ActorSetMode(selActor, M_FIRE_HEADGEAR);
	/** @todo make this a variable somewhere? */
	selActor->currentSelectedFiremode = 0;
	CL_ActorShoot(selActor, selActor->pos);
	CL_ActorSetMode(selActor, M_MOVE);

	/* restore old mouse space */
	IN_SetMouseSpace(tmpMouseSpace);
}

/*
==============================================================
MOUSE INPUT
==============================================================
*/

/**
 * @brief handle select or action clicking in either move mode
 * @sa CL_ActorSelectMouse
 * @sa CL_ActorActionMouse
 */
static void CL_ActorMoveMouse (void)
{
	/* Don't display the cursor if it's above the currently selected level.
	 * The 2nd part of the if is an attempt to display it anyway when we eg. climb a hill.
	 * But there are too many situations inside buildings that match the criteria (eg. actorclip around chair).
	 * So disabled for now.*/
	if (mousePos[2] > cl_worldlevel->integer/* && !RT_AllCellsBelowAreFilled(cl.mapData->map, fieldSize, pos)*/)
		return;

	if (selActor->actorMode == M_PEND_MOVE) {
		if (VectorCompare(mousePos, selActor->mousePendPos)) {
			/* Pending move and clicked the same spot (i.e. 2 clicks on the same place) */
			CL_ActorStartMove(selActor, mousePos);
		} else {
			/* Clicked different spot. */
			VectorCopy(mousePos, selActor->mousePendPos);
		}
	} else {
		/* either we want to confirm every move, or it's not our round and we prepare the
		 * movement for the next round */
		if (confirm_actions->integer || !cls.isOurRound()) {
			/* Set our mode to pending move. */
			VectorCopy(mousePos, selActor->mousePendPos);

			CL_ActorSetMode(selActor, M_PEND_MOVE);
		} else {
			/* Just move there */
			CL_ActorStartMove(selActor, mousePos);
		}
	}
}

/**
 * @brief Selects an actor using the mouse.
 * @sa CL_ActorStartMove
 */
void CL_ActorSelectMouse (void)
{
	if (IN_GetMouseSpace() != MS_WORLD || !selActor)
		return;

	switch (selActor->actorMode) {
	case M_MOVE:
	case M_PEND_MOVE:
		/* Try and select another team member */
		if (mouseActor && !LE_IsSelected(mouseActor) && CL_ActorSelect(mouseActor)) {
			/* Succeeded so go back into move mode. */
			CL_ActorSetMode(selActor, M_MOVE);
		} else if (interactEntity) {
			CL_ActorUse(selActor);
		} else {
			CL_ActorMoveMouse();
		}
		break;
	case M_PEND_FIRE_R:
	case M_PEND_FIRE_L:
		if (VectorCompare(mousePos, selActor->mousePendPos)) {
			/* Pending shot and clicked the same spot (i.e. 2 clicks on the same place) */
			CL_ActorShoot(selActor, mousePos);

			/* We switch back to aiming mode. */
			if (selActor->actorMode == M_PEND_FIRE_R)
				CL_ActorSetMode(selActor, M_FIRE_R);
			else
				CL_ActorSetMode(selActor, M_FIRE_L);
		} else {
			/* Clicked different spot. */
			VectorCopy(mousePos, selActor->mousePendPos);
		}
		break;
	case M_FIRE_R:
		if (mouseActor && LE_IsSelected(mouseActor))
			break;

		/* We either switch to "pending" fire-mode or fire the gun. */
		if (confirm_actions->integer == 1) {
			CL_ActorSetMode(selActor, M_PEND_FIRE_R);
			VectorCopy(mousePos, selActor->mousePendPos);
		} else {
			CL_ActorShoot(selActor, mousePos);
		}
		break;
	case M_FIRE_L:
		if (mouseActor && LE_IsSelected(mouseActor))
			break;

		/* We either switch to "pending" fire-mode or fire the gun. */
		if (confirm_actions->integer == 1) {
			CL_ActorSetMode(selActor, M_PEND_FIRE_L);
			VectorCopy(mousePos, selActor->mousePendPos);
		} else {
			CL_ActorShoot(selActor, mousePos);
		}
		break;
	default:
		break;
	}
}


/**
 * @brief initiates action with mouse.
 * @sa CL_ActionDown
 * @sa CL_ActorStartMove
 */
void CL_ActorActionMouse (void)
{
	if (!selActor || IN_GetMouseSpace() != MS_WORLD)
		return;

	if (CL_ActorFireModeActivated(selActor->actorMode)) {
		CL_ActorSetMode(selActor, M_MOVE);
	}
}

/*
==============================================================
MOUSE SCANNING
==============================================================
*/

/**
 * @brief Get battlescape cell position under mouse cursor.
 * @note The returned position might be out of world boundaries, or under the ground etc.
 * @param[out] groundIntersection Point on the ground under the mouse cursor, in the world coordinates
 * @param[out] upperTracePoint Point in the sky under the mouse cursor, in the world coordinates
 * @param[out] lowerTracePoint Point below the ground under the mouse cursor, at the world boundary
 * @sa CL_ActorMouseTrace
 */
void CL_GetWorldCoordsUnderMouse (vec3_t groundIntersection, vec3_t upperTracePoint, vec3_t lowerTracePoint)
{
	/* TODO: Move this to cl_battlescape.cpp? This functino is not directly related to actors. */
	float cur[2], frustumSlope[2];
	const float projectionDistance = 2048.0f;
	float nDotP2minusP1;
	vec3_t forward, right, up, stop;
	vec3_t from, end;
	vec3_t mapNormal, P3, P2minusP1;

	/* get cursor position as a -1 to +1 range for projection */
	cur[0] = (mousePosX * viddef.rx - viddef.viewWidth * 0.5 - viddef.x) / (viddef.viewWidth * 0.5);
	cur[1] = (mousePosY * viddef.ry - viddef.viewHeight * 0.5 - viddef.y) / (viddef.viewHeight * 0.5);

	/* get trace vectors */
	VectorCopy(cl.cam.camorg, from);
	VectorCopy(cl.cam.axis[0], forward);
	VectorCopy(cl.cam.axis[1], right);
	VectorCopy(cl.cam.axis[2], up);

	if (cl_isometric->integer)
		frustumSlope[0] = 10.0 * refdef.fieldOfViewX;
	else
		frustumSlope[0] = tan(refdef.fieldOfViewX * (M_PI / 360.0)) * projectionDistance;
	frustumSlope[1] = frustumSlope[0] * ((float)viddef.viewHeight / (float)viddef.viewWidth);

	/* transform cursor position into perspective space */
	VectorMA(from, projectionDistance, forward, stop);
	VectorMA(stop, cur[0] * frustumSlope[0], right, stop);
	VectorMA(stop, cur[1] * -frustumSlope[1], up, stop);

	/* in isometric mode the camera position has to be calculated from the cursor position so that the trace goes in the right direction */
	if (cl_isometric->integer)
		VectorMA(stop, -projectionDistance * 2, forward, from);

	/* set stop point to the intersection of the trace line with the desired plane */
	/* description of maths used:
	 *   The equation for the plane can be written:
	 *     mapNormal dot (end - P3) = 0
	 *     where mapNormal is the vector normal to the plane,
	 *         P3 is any point on the plane and
	 *         end is the point where the line intersects the plane
	 *   All points on the line can be calculated using:
	 *     P1 + u*(P2 - P1)
	 *     where P1 and P2 are points that define the line and
	 *           u is some scalar
	 *   The intersection of the line and plane occurs when:
	 *     mapNormal dot (P1 + u*(P2 - P1)) == mapNormal dot P3
	 *   The intersection therefore occurs when:
	 *     u = (mapNormal dot (P3 - P1))/(mapNormal dot (P2 - P1))
	 * Note: in the code below from & stop represent P1 and P2 respectively
	 */
	VectorSet(P3, 0., 0., cl_worldlevel->integer * UNIT_HEIGHT + CURSOR_OFFSET);
	VectorSet(mapNormal, 0., 0., 1.);
	VectorSubtract(stop, from, P2minusP1);
	nDotP2minusP1 = DotProduct(mapNormal, P2minusP1);

	/* calculate intersection directly if angle is not parallel to the map plane */
	if (nDotP2minusP1 > 0.01 || nDotP2minusP1 < -0.01) {
		float u;
		vec3_t dir, P3minusP1;

		VectorSubtract(P3, from, P3minusP1);
		u = DotProduct(mapNormal, P3minusP1) / nDotP2minusP1;
		VectorScale(P2minusP1, (vec_t)u, dir);
		VectorAdd(from, dir, end);
	} else { /* otherwise do a full trace */
		CM_EntTestLineDM(cl.mapTiles, from, stop, end, TL_FLAG_ACTORCLIP, cl.leInlineModelList);
	}

	if (groundIntersection)
		VectorCopy(end, groundIntersection);
	if (upperTracePoint)
		VectorCopy(from, upperTracePoint);
	if (lowerTracePoint)
		VectorCopy(stop, lowerTracePoint);
}

/**
 * @brief Battlescape cursor positioning.
 * @note Sets global var mouseActor to current selected le
 * @sa IN_Parse CL_GetWorldCoordsUnderMouse
 */
bool CL_ActorMouseTrace (void)
{
	vec3_t from, stop, end;
	vec3_t pA, pB, pC;
	pos3_t testPos;
	le_t* interactLe;

	CL_GetWorldCoordsUnderMouse(end, from, stop);
	VecToPos(end, testPos);
	/* hack to prevent cursor from getting stuck on the top of an invisible
	 * playerclip surface (in most cases anyway) */
	PosToVec(testPos, pA);
	/* ensure that the cursor is in the world, if this is not done, the tracer box is
	 * drawn in the void on the first level and the menu key bindings might get executed
	 * this could result in different problems like the zooming issue (where you can't zoom
	 * in again, because in_zoomout->state is not reseted). */
	if (CL_OutsideMap(pA, MAP_SIZE_OFFSET))
		return false;

	VectorCopy(pA, pB);
	pA[2] += UNIT_HEIGHT;
	pB[2] -= UNIT_HEIGHT;
	/** @todo Shouldn't we check the return value of CM_TestLineDM here - maybe
	 * we don't have to do the second Grid_Fall call at all and can save a lot
	 * of traces */
	pos_t restingLevel = Grid_Fall(cl.mapData->routing, ACTOR_GET_FIELDSIZE(selActor), testPos);
	CM_EntTestLineDM(cl.mapTiles, pA, pB, pC, TL_FLAG_ACTORCLIP, cl.leInlineModelList);
	VecToPos(pC, testPos);
	/* VecToPos strictly rounds the values down, while routing will round floors up to the next QUANT.
	 * This makes a huge diffence when calculating the z-level:
	 * without compensation, z of 61-63 will belong to the level below. */
	testPos[2] = ModelFloorToQuant(pC[2] / CELL_HEIGHT);

	restingLevel = std::min(restingLevel, Grid_Fall(cl.mapData->routing, ACTOR_GET_FIELDSIZE(selActor), testPos));

	/* if grid below intersection level, start a trace from the intersection */
	if (restingLevel < cl_worldlevel->integer) {
		VectorCopy(end, from);
		from[2] -= CURSOR_OFFSET;
		CM_EntTestLineDM(cl.mapTiles, from, stop, end, TL_FLAG_ACTORCLIP, cl.leInlineModelList);
		VecToPos(end, testPos);
		restingLevel = Grid_Fall(cl.mapData->routing, ACTOR_GET_FIELDSIZE(selActor), testPos);
	}

	/* test if the selected grid is out of the world */
	if (restingLevel >= PATHFINDING_HEIGHT)
		return false;

	/* Set truePos- test pos is under the cursor. */
	VectorCopy(testPos, truePos);
	truePos[2] = cl_worldlevel->integer;

	/* Set mousePos to the position that the actor will move to. */
	testPos[2] = restingLevel;
	VectorCopy(testPos, mousePos);

	interactLe = CL_BattlescapeSearchAtGridPos(mousePos, false, selActor);
	if (interactLe != nullptr && LE_IsActor(interactLe)) {
		mouseActor = interactLe;
		interactEntity = nullptr;
	} else if (selActor != nullptr && selActor->clientAction == interactLe) {
		interactEntity = interactLe;
		mouseActor = nullptr;
	} else {
		interactEntity = nullptr;
		mouseActor = nullptr;
	}

	if (interactEntity != nullptr) {
		SCR_ChangeCursor(2);
	} else {
		SCR_ChangeCursor(1);
	}

	/* calculate move length */
	if (selActor && !VectorCompare(mousePos, mouseLastPos)) {
		VectorCopy(mousePos, mouseLastPos);
		CL_ActorResetMoveLength(selActor);
	}

	return true;
}

/**
 * @brief Scroll battlescape touchscreen-style, by clicking and dragging away
 */
void CL_InitBattlescapeMouseDragging (void)
{
	CL_GetWorldCoordsUnderMouse(mouseDraggingPos, nullptr, nullptr);
}

/**
 * @brief Scroll battlescape touchscreen-style, by clicking and dragging away
 */
void CL_BattlescapeMouseDragging (void)
{
	/* TODO: the movement is snapping to the cell center, and is clunky - make it smooth */
	/* Difference between last and currently selected cell, we'll move camera by that difference */
	vec3_t currentMousePos, mousePosDiff;

	CL_GetWorldCoordsUnderMouse(currentMousePos, nullptr, nullptr);
	if (fabs(currentMousePos[0] - mouseDraggingPos[0]) + fabs(currentMousePos[1] - mouseDraggingPos[1]) < 0.5f)
		return;
	VectorSubtract(mouseDraggingPos, currentMousePos, mousePosDiff);
	VectorMA(cl.cam.origin, 0.2f, mousePosDiff, cl.cam.origin); /* Move camera slowly to the dest point, to prevent shaking */
	Cvar_SetValue("cl_worldlevel", truePos[2]); /* Do not change world level */
}

/*
==============================================================
ACTOR GRAPHICS
==============================================================
*/

/**
 * @brief Checks whether a weapon should be added to the entity's hand
 * @param[in] objID The item id that the actor is holding in his hand (@c le->left or @c le->right)
 * @return true if the weapon is a valid item and false if it's a dummy item or the actor has nothing
 * in the given hand
 */
static inline bool CL_AddActorWeapon (int objID)
{
	if (objID != NONE) {
		const objDef_t* od = INVSH_GetItemByIDX(objID);
		if (od->isVirtual)
			return false;
		return true;
	}
	return false;
}

/**
 * @brief Adds an actor to the render entities with all it's models and items.
 * @param[in] le The local entity to get the values from
 * @param[in] ent The body entity used in the renderer
 * @sa CL_AddUGV
 * @sa LE_AddToScene
 * @sa CL_ActorAppear
 * @note Called via addfunc for each local entity in every frame
 */
bool CL_AddActor (le_t* le, entity_t* ent)
{
	entity_t add(RF_NONE);

	if (!cl_showactors->integer)
		return false;

	const bool hasTagHead = R_GetTagIndexByName(le->model1, "tag_head") != -1;
	const int delta = hasTagHead ? 2 : 1;

	if (LE_IsStunned(le)) {
		if (!le->ptl)
			le->ptl = CL_ParticleSpawn("stunnedactor", 0, le->origin);
	} else if (!LE_IsDead(le)) {
		/* add the weapons to the actor's hands */
		const bool addLeftHandWeapon = CL_AddActorWeapon(le->left);
		const bool addRightHandWeapon = CL_AddActorWeapon(le->right);
		/* add left hand weapon */
		if (addLeftHandWeapon) {
			add.init();

			add.model = cls.modelPool[le->left];
			if (!add.model)
				Com_Error(ERR_DROP, "Actor model for left hand weapon wasn't found!");

			/* point to the body ent which will be added last */
			add.tagent = R_GetFreeEntity() + delta + addRightHandWeapon;
			add.tagname = "tag_lweapon";

			R_AddEntity(&add);
		}

		/* add right hand weapon */
		if (addRightHandWeapon) {
			add.init();

			add.alpha = le->alpha;
			add.model = cls.modelPool[le->right];
			if (!add.model)
				Com_Error(ERR_DROP, "Actor model for right hand weapon wasn't found!");

			/* point to the body ent which will be added last */
			add.tagent = R_GetFreeEntity() + delta;
			add.tagname = "tag_rweapon";

			R_AddEntity(&add);
		}
	}

	if (hasTagHead) {
		/* add head */
		add.init();

		add.alpha = le->alpha;
		add.model = le->model2;
		if (!add.model)
			Com_Error(ERR_DROP, "Actor model wasn't found!");
		add.skinnum = le->headSkin;

		/* point to the body ent which will be added last */
		add.tagent = R_GetFreeEntity() + 1;
		add.tagname = "tag_head";

		if (le->team != cls.team)
			add.flags |= RF_IRGOGGLES;

		R_AddEntity(&add);
	}

	/** Add actor special effects.
	 * Only draw blood if the actor is dead or (if stunned) was damaged more than half its maximum HPs. */
	/** @todo Better value for this?	*/
	if (LE_IsStunned(le) && le->HP <= le->maxHP / 2)
		ent->flags |= RF_BLOOD;
	else if (LE_IsDead(le))
		ent->flags |= RF_BLOOD;
	else
		ent->flags |= RF_SHADOW;

	ent->flags |= RF_ACTOR;
	/* actors are highlighted if some other actor uses ir goggles */
	if (le->team != cls.team)
		ent->flags |= RF_IRGOGGLES;

	if (!LE_IsDead(le) && !LE_IsStunned(le)) {
		if (LE_IsSelected(le))
			ent->flags |= RF_SELECTED;
		if (le->team == cls.team) {
			if (le->pnum == cl.pnum)
				ent->flags |= RF_MEMBER;
			if (le->pnum != cl.pnum)
				ent->flags |= RF_ALLIED;
		} else {
			ent->flags |= RF_OPPONENT;
		}
		if (le->team == TEAM_CIVILIAN)
			ent->flags |= RF_NEUTRAL;
	}

	if (ent->flags & RF_BLOOD) {
		const char* deathTextureName;
		assert(le->teamDef != nullptr);
		deathTextureName = le->teamDef->deathTextureName;
		ent->texture = R_FindImage(deathTextureName, it_effect);
	}

	return true;
}

/*
==============================================================
TARGETING GRAPHICS
==============================================================
*/

/**
 * @brief Show weapon radius
 * @param[in] center The center of the circle
 * @param[in] radius The radius of the damage circle
 */
static void CL_TargetingRadius (const vec3_t center, const float radius)
{
	ptl_t* particle = CL_ParticleSpawn("circle", 0, center);
	if (particle != nullptr)
		particle->size[0] = radius;
}

/**
 * @brief Draws line to target.
 * @param[in] fromPos The (grid-) position of the aiming actor.
 * @param[in] fromActorSize The size of the aiming actor (1 for 1x1 or 2 for 2x2).
 * @param[in] toPos The (grid-) position of the target.
 * @sa CL_TargetingGrenade
 * @sa CL_AddTargeting
 * @sa CL_Trace
 * @sa G_ShootSingle
 */
static void CL_TargetingStraight (const pos3_t fromPos, actorSizeEnum_t fromActorSize, const pos3_t toPos)
{
	vec3_t start, end;
	vec3_t dir, mid, temp;
	bool crossNo;
	le_t* target = nullptr;
	actorSizeEnum_t toActorSize;

	if (!selActor || !selActor->fd)
		return;

	/* search for an actor at target */
	target = CL_BattlescapeSearchAtGridPos(toPos, true, nullptr);

	/* Determine the target's size. */
	toActorSize = target
		? target->fieldSize
		: ACTOR_SIZE_NORMAL;

	Grid_PosToVec(cl.mapData->routing, fromActorSize, fromPos, start);
	Grid_PosToVec(cl.mapData->routing, toActorSize, toPos, end);
	if (mousePosTargettingAlign)
		end[2] -= mousePosTargettingAlign;

	/* calculate direction */
	VectorSubtract(end, start, dir);
	VectorNormalize(dir);

	/* calculate 'out of range point' if there is one */
	if (VectorDistSqr(start, end) > selActor->fd->range * selActor->fd->range) {
		VectorMA(start, selActor->fd->range, dir, mid);
		crossNo = true;
	} else {
		VectorCopy(end, mid);
		crossNo = false;
	}

	VectorMA(start, UNIT_SIZE * 1.4, dir, temp);
	/* switch up to top level, this is needed to make sure our trace doesn't go through ceilings ... */
	/** @todo is this really needed for straight targetting? - for grenades, yes, but not for straight no?
	 * cl_worldlevel->integer should be enough here */
	trace_t tr = CL_Trace(start, temp, AABB(), selActor, nullptr, MASK_SHOT, cl.mapMaxLevel - 1);
	if (tr.le && (tr.le->team == cls.team || LE_IsCivilian(tr.le)) && LE_IsCrouched(tr.le))
		VectorMA(start, UNIT_SIZE * 1.4, dir, temp);
	else
		VectorCopy(start, temp);

	/** @todo is this really needed for straight targetting? - for grenades, yes, but not for straight no?
	 * cl_worldlevel->integer should be enough here */
	tr = CL_Trace(temp, mid, AABB(), selActor, target, MASK_SHOT, cl.mapMaxLevel - 1);

	if (tr.fraction < 1.0 && (!tr.le || (!LE_IsInvisible(tr.le) && !VectorCompare(tr.le->pos, toPos)))) {
		const float d = VectorDist(temp, mid);
		VectorMA(start, tr.fraction * d, dir, mid);
		crossNo = true;
	}

	/* spawn particles */
	CL_ParticleSpawn("inRangeTracer", 0, start, mid);
	if (crossNo) {
		CL_ParticleSpawn("longRangeTracer", 0, mid, end);
		CL_ParticleSpawn("cross_no", 0, end);
	} else {
		CL_ParticleSpawn("cross", 0, end);
	}

	if (selActor->fd->splrad > 0.0) {
		Grid_PosToVec(cl.mapData->routing, toActorSize, toPos, end);
		CL_TargetingRadius(end, selActor->fd->splrad);
	}
}

#define GRENADE_PARTITIONS	20

/**
 * @brief Shows targeting for a grenade.
 * @param[in] fromPos The (grid-) position of the aiming actor.
 * @param[in] fromActorSize The size of the aiming actor (1 for 1x1 or 2 for 2x2).
 * @param[in] toPos The (grid-) position of the target (mousePos or mousePendPos).
 * @sa CL_TargetingStraight
 */
static void CL_TargetingGrenade (const pos3_t fromPos, actorSizeEnum_t fromActorSize, const pos3_t toPos)
{
	vec3_t from, at, cross;
	float vz, dt;
	vec3_t v0, ds, next;
	bool obstructed = false;
	int i;
	le_t* target = nullptr;
	actorSizeEnum_t toActorSize;

	if (!selActor || !selActor->fd || Vector2Compare(fromPos, toPos))
		return;

	/* search for an actor at target */
	target = CL_BattlescapeSearchAtGridPos(toPos, true, nullptr);

	/* Determine the target's size. */
	toActorSize = target
		? target->fieldSize
		: ACTOR_SIZE_NORMAL;

	/* get vectors, paint cross */
	Grid_PosToVec(cl.mapData->routing, fromActorSize, fromPos, from);
	Grid_PosToVec(cl.mapData->routing, toActorSize, toPos, at);
	from[2] += selActor->fd->shotOrg[1];

	/* prefer to aim grenades at the ground */
	at[2] -= GROUND_DELTA;
	if (mousePosTargettingAlign)
		at[2] -= mousePosTargettingAlign;
	VectorCopy(at, cross);

	/* calculate parabola */
	dt = Com_GrenadeTarget(from, at, selActor->fd->range, selActor->fd->launched, selActor->fd->rolled, v0);
	if (!dt) {
		CL_ParticleSpawn("cross_no", 0, cross);
		return;
	}

	dt /= GRENADE_PARTITIONS;
	VectorSubtract(at, from, ds);
	VectorScale(ds, 1.0 / GRENADE_PARTITIONS, ds);
	ds[2] = 0;

	/* paint */
	vz = v0[2];

	for (i = 0; i < GRENADE_PARTITIONS; i++) {
		VectorAdd(from, ds, next);
		next[2] += dt * (vz - 0.5 * GRAVITY * dt);
		vz -= GRAVITY * dt;
		VectorScale(ds, i + 1.0, at);

		/* trace for obstacles. Switch up to top level, to make sure our trace
		 * doesn't go through ceilings ... */
		const trace_t tr = CL_Trace(from, next, AABB(), selActor, target, MASK_SHOT, cl.mapMaxLevel - 1);

		/* something was hit */
		if (tr.fraction < 1.0 && (!tr.le || (!LE_IsInvisible(tr.le) && !VectorCompare(tr.le->pos, toPos)))) {
			obstructed = true;
		}

		/* draw particles */
		/** @todo character strength should be used here, too
		 * the stronger the character, the further the throw */
		if (obstructed || VectorLength(at) > selActor->fd->range)
			CL_ParticleSpawn("longRangeTracer", 0, from, next);
		else
			CL_ParticleSpawn("inRangeTracer", 0, from, next);
		VectorCopy(next, from);
	}
	/* draw targeting cross */
	if (obstructed || VectorLength(at) > selActor->fd->range)
		CL_ParticleSpawn("cross_no", 0, cross);
	else
		CL_ParticleSpawn("cross", 0, cross);

	if (selActor->fd->splrad > 0.0) {
		Grid_PosToVec(cl.mapData->routing, toActorSize, toPos, at);
		CL_TargetingRadius(at, selActor->fd->splrad);
	}
}

/**
 * @brief field marker box
 * @sa ModelOffset
 */
static const vec3_t halfBoxSize = { BOX_DELTA_WIDTH, BOX_DELTA_LENGTH, BOX_DELTA_HEIGHT };
#define BoxOffset(aSize, target) (target[0]=(aSize-1)*(UNIT_SIZE+BOX_DELTA_WIDTH), target[1]=(aSize-1)*(UNIT_SIZE+BOX_DELTA_LENGTH), target[2]=0)

/**
 * @brief create a targeting box at the given position
 * @sa CL_ParseClientinfo
 * @sa CL_TraceMove
 */
static void CL_AddTargetingBox (pos3_t pos, bool pendBox)
{
	if (!cl_showactors->integer)
		return;

	entity_t cursor(RF_BOX);

	/* Paint the green box if move is possible ...
	 * OR paint a dark blue one if move is impossible or the
	 * soldier does not have enough TimeUnits left. */
	if (selActor && selActor->actorMoveLength < ROUTING_NOT_REACHABLE
	 && selActor->actorMoveLength <= CL_ActorUsableTUs(selActor))
		VectorSet(cursor.color, 0, 1, 0); /* Green */
	else
		VectorSet(cursor.color, 0.6, 0.68, 1); /* Light Blue */

	/* color */
	/* if the mouse is over an actor, but not the selected one */
	actorSizeEnum_t actorSize = ACTOR_SIZE_NORMAL;
	if (mouseActor && !LE_IsSelected(mouseActor)) {
		actorSize = mouseActor->fieldSize;
		cursor.alpha = 0.6 + 0.2 * sin((float) cl.time / 80);
		/* Paint the box red if the soldiers under the cursor is
		 * not in our team and no civilian either. */
		if (mouseActor->team != cls.team) {
			switch (mouseActor->team) {
			case TEAM_CIVILIAN:
				/* Civilians are yellow */
				VectorSet(cursor.color, 1, 1, 0); /* Yellow */
				break;
			default:
				if (LE_IsAlien(mouseActor)) {
					if (GAME_TeamIsKnown(mouseActor->teamDef))
						UI_RegisterText(TEXT_MOUSECURSOR_PLAYERNAMES, _(mouseActor->teamDef->name));
					else
						UI_RegisterText(TEXT_MOUSECURSOR_PLAYERNAMES, _("Unknown alien race"));
				} else {
					/* multiplayer names */
					/* see CL_ParseClientinfo */
					UI_RegisterText(TEXT_MOUSECURSOR_PLAYERNAMES, CL_PlayerGetName(mouseActor->pnum));
				}
				/* Aliens (and players not in our team [multiplayer]) are red */
				VectorSet(cursor.color, 1, 0, 0); /* Red */
				break;
			}
		} else {
			/* coop multiplayer games */
			if (mouseActor->pnum != cl.pnum) {
				UI_RegisterText(TEXT_MOUSECURSOR_PLAYERNAMES, CL_PlayerGetName(mouseActor->pnum));
			} else {
				/* we know the names of our own actors */
				character_t* chr = CL_ActorGetChr(mouseActor);
				assert(chr);
				UI_RegisterText(TEXT_MOUSECURSOR_PLAYERNAMES, chr->name);
			}
			/* Paint a light blue box if on our team */
			VectorSet(cursor.color, 0.2, 0.3, 1); /* Light Blue */
		}
	} else {
		/* either no actor under the cursor or the selected one */
		cursor.alpha = 0.3;
		if (selActor) { /* there should always be an actor selected, but who knows */
			actorSize = selActor->fieldSize;
		}
	}

	/* Now calculate the size of the cursor box, depending on the actor. */
	/* For some strange reason we use origin and oldorigin instead of the ent's min/max, respectively */
	Grid_PosToVec(cl.mapData->routing, ACTOR_SIZE_NORMAL, pos, cursor.origin);	/* center of the (lower left) cell */
	VectorAdd(cursor.origin, halfBoxSize, cursor.oldorigin);
	VectorSubtract(cursor.origin, halfBoxSize, cursor.origin);
	if (actorSize > ACTOR_SIZE_NORMAL) {
		vec_t inc = UNIT_SIZE * (actorSize - 1);
		vec3_t increase = { inc, inc, 0};
		VectorAdd(cursor.oldorigin, increase, cursor.oldorigin);
	}

	/* if pendBox is true then ignore all the previous color considerations and use cyan */
	if (pendBox) {
		VectorSet(cursor.color, 0, 1, 1); /* Cyan */
		cursor.alpha = 0.15;
	}

	/* add it */
	R_AddEntity(&cursor);
}

/**
 * @brief Targets to the ground when holding the assigned button
 * @sa mousePosTargettingAlign
 */
void CL_ActorTargetAlign_f (void)
{
	int align = GROUND_DELTA;

	/* no firedef selected */
	if (!selActor || !selActor->fd)
		return;
	if (!CL_ActorFireModeActivated(selActor->actorMode))
		return;

	/* user defined height align */
	if (Cmd_Argc() == 2) {
		align = atoi(Cmd_Argv(1));
	} else {
		static int currentPos = 0;
		switch (currentPos) {
		case 0:
			if (selActor->fd->gravity)
				align = -align;
			currentPos = 1; /* next level */
			break;
		case 1:
			/* only allow to align to lower z level if the actor is
			 * standing at a higher z-level */
			if (selActor->fd->gravity)
				align = -(2 * align);
			else
				align = -align;
			currentPos = 2;
			break;
		case 2:
			/* the static var is not reseted on weaponswitch or actorswitch */
			if (selActor->fd->gravity) {
				align = 0;
				currentPos = 0; /* next level */
			} else {
				align = -(2 * align);
				currentPos = 3; /* next level */
			}
			break;
		case 3:
			align = 0;
			currentPos = 0; /* back to start */
			break;
		}
	}
	mousePosTargettingAlign = align;
}

/**
 * @brief Adds a target cursor when we render the world.
 * @sa CL_TargetingStraight
 * @sa CL_TargetingGrenade
 * @sa CL_AddTargetingBox
 * @sa CL_TraceMove
 * @sa CL_ViewRender
 * Draws the tracer (red, yellow, green box) on the grid
 */
void CL_AddTargeting (void)
{
	if (IN_GetMouseSpace() != MS_WORLD || !selActor)
		return;

	switch (selActor->actorMode) {
	case M_MOVE:
	case M_PEND_MOVE:
		/* Don't display the cursor if it's above the currently selected level.
		 * The 2nd part of the if is an attempt to display it anyway when we eg. climb a hill.
		 * But there are too many situations inside buildings that match the criteria (eg. actorclip around chair).
		 * So disabled for now.*/
		if (mousePos[2] > cl_worldlevel->integer/* && !RT_AllCellsBelowAreFilled(cl.mapData->map, fieldSize, pos)*/)
			return;

		/* Display Move-cursor. */
		CL_AddTargetingBox(mousePos, false);

		if (selActor->actorMode == M_PEND_MOVE) {
			/* Also display a box for the pending move if we have one. */
			CL_AddTargetingBox(selActor->mousePendPos, true);
			if (!CL_ActorTraceMove(selActor->mousePendPos))
				CL_ActorSetMode(selActor, M_MOVE);
		}
		break;
	case M_FIRE_R:
	case M_FIRE_L:
		if (!selActor->fd)
			return;

		if (!selActor->fd->gravity)
			CL_TargetingStraight(selActor->pos, selActor->fieldSize, mousePos);
		else
			CL_TargetingGrenade(selActor->pos, selActor->fieldSize, mousePos);
		break;
	case M_PEND_FIRE_R:
	case M_PEND_FIRE_L:
		if (!selActor->fd)
			return;

		/* Draw cursor at mousepointer */
		CL_AddTargetingBox(mousePos, false);

		/* Draw (pending) Cursor at target */
		CL_AddTargetingBox(selActor->mousePendPos, true);

		if (!selActor->fd->gravity)
			CL_TargetingStraight(selActor->pos, selActor->fieldSize, selActor->mousePendPos);
		else
			CL_TargetingGrenade(selActor->pos, selActor->fieldSize, selActor->mousePendPos);
		break;
	default:
		break;
	}
}

static const vec3_t boxShift = { PLAYER_WIDTH, PLAYER_WIDTH, UNIT_HEIGHT / 2 - DIST_EPSILON };

/**
 * @brief create a targeting box at the given position
 * @sa CL_ParseClientinfo
 */
static bool CL_AddPathingBox (pos3_t pos, bool addUnreachableCells)
{
	const int TUneed = CL_ActorMoveLength(selActor, pos);
	const int TUhave = CL_ActorUsableTUs(selActor);
	if (!addUnreachableCells && TUhave < TUneed)
		return false;

	entity_t ent(RF_PATH);

	Grid_PosToVec(cl.mapData->routing, ACTOR_GET_FIELDSIZE(selActor), pos, ent.origin);
	VectorSubtract(ent.origin, boxShift, ent.origin);

	int base; /* The floor relative to this cell */
	base = Grid_Floor(cl.mapData->routing, ACTOR_GET_FIELDSIZE(selActor), pos);

	/* Paint the box green if it is reachable,
	 * yellow if it can be entered but is too far,
	 * or red if it cannot be entered ever. */
	if (base < -QuantToModel(PATHFINDING_MAX_FALL)) {
		VectorSet(ent.color, 0.0, 0.0, 0.0); /* Can't enter - black */
	} else {
		/* Can reach - green
		 * Passable but unreachable - yellow
		 * Not passable - red */
		VectorSet(ent.color, (TUneed > TUhave), (TUneed != ROUTING_NOT_REACHABLE), 0);
	}

	/* Set the box height to the ceiling value of the cell. */
	int height; /* The total opening size */
	height = 2 + std::min(TUneed * (UNIT_HEIGHT - 2) / ROUTING_NOT_REACHABLE, 16);
	ent.oldorigin[2] = height;
	ent.oldorigin[0] = TUneed;
	ent.oldorigin[1] = TUhave;

	ent.alpha = 0.25;

	/* add it */
	R_AddEntity(&ent);
	return true;
}

/**
 * @brief Adds a pathing marker to the current floor when we render the world.
 * @sa CL_ViewRender
 * Draws the tracer (red, yellow, green box) on the grid
 */
void CL_AddPathing (void)
{
	pos3_t pos;

	if (selActor == nullptr) {
		return;
	}

	pos[2] = cl_worldlevel->integer;
	for (pos[1] = std::max(mousePos[1] - 8, 0); pos[1] <= std::min(mousePos[1] + 8, PATHFINDING_WIDTH - 1); pos[1]++) {
		for (pos[0] = std::max(mousePos[0] - 8, 0); pos[0] <= std::min(mousePos[0] + 8, PATHFINDING_WIDTH - 1); pos[0]++) {
			CL_AddPathingBox(pos, true);
		}
	}
}

/**
 * @brief Adds an actor pathing marker to the current floor when we render the world.
 * @sa CL_ViewRender
 * Draws the tracer (red, yellow, green box) on the grid
 */
void CL_AddActorPathing (void)
{
	int x, y;
	pos3_t pos;
	int i = 0;

	if (selActor == nullptr) {
		return;
	}

	pos[2] = cl_worldlevel->integer;
	for (y = 0; y <= PATHFINDING_WIDTH; y++) {
		for (x = 0; x <= PATHFINDING_WIDTH; x++) {
			pos[0] = (pos_t)x;
			pos[1] = (pos_t)y;
			i += CL_AddPathingBox(pos, false);
			if (i > 1024)
				return;
		}
	}
}

/**
 * @brief Plays various sounds on actor action.
 * @param[in] le The actor
 * @param[in] soundType Type of action (among actorSound_t) for which we need a sound.
 */
void CL_ActorPlaySound (const le_t* le, actorSound_t soundType)
{
	const char* actorSound = Com_GetActorSound(le->teamDef, le->gender, soundType);
	if (actorSound) {
		if (S_LoadAndPlaySample(actorSound, le->origin, SOUND_ATTN_IDLE, SND_VOLUME_DEFAULT)) {
			Com_DPrintf(DEBUG_SOUND|DEBUG_CLIENT, "CL_PlayActorSound: ActorSound: '%s'\n", actorSound);
		}
	}
}

/**
 * @brief create an arrow between from and to with the specified color ratios
 */
static void CL_AddArrow (vec3_t from, vec3_t to, float red, float green, float blue)
{
	/* Com_Printf("Adding arrow (%f, %f, %f) to (%f, %f, %f).\n", from[0], from[1], from[2], to[0], to[1], to[2]); */

	entity_t ent(RF_ARROW);

	VectorCopy(from, ent.origin);
	VectorCopy(to, ent.oldorigin);
	VectorSet(ent.color, red, green, blue);

	ent.alpha = 0.25;

	/* add it */
	R_AddEntity(&ent);
}

/**
 * @brief Useful for debugging pathfinding
 */
void CL_DisplayFloorArrows (void)
{
	vec3_t base, start;

	Grid_PosToVec(cl.mapData->routing, ACTOR_GET_FIELDSIZE(selActor), truePos, base);
	VectorCopy(base, start);
	base[2] -= QUANT;
	start[2] += QUANT;
	CL_AddArrow(base, start, 0.0, 0.0, 0.0);
}

/**
 * @brief Useful for debugging pathfinding
 */
void CL_DisplayObstructionArrows (void)
{
	vec3_t base, start;

	Grid_PosToVec(cl.mapData->routing, ACTOR_GET_FIELDSIZE(selActor), truePos, base);
	VectorCopy(base, start);
	CL_AddArrow(base, start, 0.0, 0.0, 0.0);
}

#ifdef DEBUG
/**
 * @brief Triggers @c Step::isPossible in every direction at the current truePos.
 */
static void CL_DumpMoveMark_f (void)
{
	const int temp = developer->integer;

	if (!selActor)
		return;

	CL_BuildForbiddenList();
	Grid_CalcPathing(cl.mapData->routing, ACTOR_GET_FIELDSIZE(selActor), &cl.pathMap, truePos, MAX_ROUTE_TUS, forbiddenList, forbiddenListLength);

	CL_ActorConditionalMoveCalc(selActor);
	developer->integer = temp;
}

/**
 * @brief Shows a table of the TUs that would be used by the current actor to move
 * relative to its current location
 */
static void CL_DumpTUs_f (void)
{
	int x, y, crouchingState;
	pos3_t pos, loc;

	if (!selActor)
		return;

	crouchingState = LE_IsCrouched(selActor) ? 1 : 0;
	VectorCopy(selActor->pos, pos);

	Com_Printf("TUs around (%i, %i, %i).\n", pos[0], pos[1], pos[2]);

	for (y = std::max(0, pos[1] - 8); y <= std::min(PATHFINDING_WIDTH, pos[1] + 8); y++) {
		for (x = std::max(0, pos[0] - 8); x <= std::min(PATHFINDING_WIDTH, pos[0] + 8); x++) {
			VectorSet(loc, x, y, pos[2]);
			Com_Printf("%3i ", Grid_MoveLength(&cl.pathMap, loc, crouchingState, false));
		}
		Com_Printf("\n");
	}
	Com_Printf("TUs at (%i, %i, %i) = %i\n", pos[0], pos[1], pos[2], Grid_MoveLength(&cl.pathMap, pos, crouchingState, false));
}

static void CL_DebugPath_f (void)
{
	const actorSizeEnum_t actorSize = ACTOR_SIZE_NORMAL;
	const pos_t x = mousePos[0];
	const pos_t y = mousePos[1];
	const pos_t z = mousePos[2];

	if (IN_GetMouseSpace() != MS_WORLD)
		return;

#if 0
	int dir = 3;
	RT_DebugSpecial(cl.mapTiles, cl.mapData->routing, actorSize, x, y, dir, cl.leInlineModelList);

	bool found = Grid_FindPath(cl.mapData->routing, actorSize, &cl.pathMap, selActor->pos, mousePos, 0, 600, nullptr, nullptr);
	if (found)
		Com_Printf("found the path !\n");
	{
//	pos3_t boxmin = {134,128,0};
//	pos3_t boxmax = {136,130,1};
//	GridBox myBox(boxmin, boxmax);
//	Grid_RecalcBoxRouting(cl.mapTiles, cl.mapData->routing, myBox, cl.leInlineModelList);
	}
#endif

	RT_DebugPathDisplay(cl.mapData->routing, actorSize, x, y, z);

	GridBox mbox(cl.mapData->mapBox);
	int xW = mbox.maxs[0] - mbox.mins[0];
	int yW = mbox.maxs[1] - mbox.mins[1];
	int zW = mbox.maxs[2] - mbox.mins[2];
	Com_Printf("Statistics:\nWorldsize(x/y/z) %i/%i/%i\n", xW, yW, zW);
	int numCells = xW * yW * zW;
	Com_Printf("number of Cells: %i\n", numCells);
	Com_Printf("Base Coords (x/y/z) %i/%i/%i\n", mbox.mins[0], mbox.mins[1], mbox.mins[2]);
}
#endif


/**
 * @brief Switch to the next living soldier
 */
static void CL_ActorNext_f (void)
{
	if (CL_BattlescapeRunning()) {
		CL_ActorSelectNext();
	}
}

/**
 * @brief Switch to the previous living soldier
 */
static void CL_ActorPrev_f (void)
{
	if (CL_BattlescapeRunning()) {
		CL_ActorSelectPrev();
	}
}

/**
 * @brief Selects a soldier while we are on battlescape
 */
static void CL_ActorSelect_f (void)
{
	/* check syntax */
	if (Cmd_Argc() < 2) {
		Com_Printf("Usage: %s <num>\n", Cmd_Argv(0));
		return;
	}

	/* check whether we are connected (tactical mission) */
	if (CL_BattlescapeRunning()) {
		const int num = atoi(Cmd_Argv(1));
		CL_ActorSelectList(num);
	}
}

/**
 * @brief Update the skin of the current soldier
 */
static void CL_ActorUpdate_f (void)
{
	const int num = cl_selected->integer;

	/* We are in the base or multiplayer inventory */
	int i = 0;
	LIST_Foreach(chrDisplayList, character_t, chr) {
		if (i++ != num)
			continue;
		CL_UpdateCharacterValues(chr);
	}
}

/**
 * @sa G_ActorVis
 * @param[in] le The local entity to do the check for
 * @param[in] check The local entity to check the visibility for
 * @return @c true if the given edict is visible from the given world coordinate, @c false otherwise.
 */
static bool CL_ActorVis (const le_t* le, const le_t* check)
{
	vec3_t test, dir;
	float delta;
	int i;
	vec3_t from;

	VectorCopy(le->origin, from);

	/* start on eye height */
	VectorCopy(check->origin, test);
	if (LE_IsDead(check)) {
		test[2] += PLAYER_DEAD;
		delta = 0;
	} else if (LE_IsCrouched(check)) {
		test[2] += PLAYER_CROUCH - 2;
		delta = (PLAYER_CROUCH - PLAYER_MIN) / 2 - 2;
	} else {
		test[2] += PLAYER_STAND;
		delta = (PLAYER_STAND - PLAYER_MIN) / 2 - 2;
	}

	/* side shifting -> better checks */
	dir[0] = from[1] - check->origin[1];
	dir[1] = check->origin[0] - from[0];
	dir[2] = 0;
	VectorNormalize(dir);
	VectorMA(test, -7, dir, test);

	/* do 3 tests */
	for (i = 0; i < 3; i++) {
		const trace_t tr = CL_Trace(from, test, AABB(), le, nullptr, MASK_SOLID, cl_worldlevel->integer);
		/* trace didn't reach the target - something was hit before */
		if (tr.fraction < 1.0) {
			/* look further down or stop */
			if (!delta)
				return false;
			VectorMA(test, 7, dir, test);
			test[2] -= delta;
			continue;
		}

		return true;
	}

	return false;
}

/**
 * @brief Cycles between visible (to selected actor) aliens.
 * @sa CL_NextAlien_f
 */
static void CL_NextAlienVisibleFromActor_f (void)
{
	static int lastAlien = 0;
	int i;

	if (!selActor)
		return;

	if (lastAlien >= cl.numLEs)
		lastAlien = 0;

	i = lastAlien;
	do {
		const le_t* le;
		if (++i >= cl.numLEs)
			i = 0;
		le = &cl.LEs[i];
		if (le->inuse && LE_IsLivingAndVisibleActor(le) && le->team != cls.team
		 && !LE_IsCivilian(le)) {
			if (CL_ActorVis(selActor, le)) {
				lastAlien = i;
				CL_ViewCenterAtGridPosition(le->pos);
				CL_ParticleSpawn("fadeTracer", 0, selActor->origin, le->origin);
				return;
			}
		}
	} while (i != lastAlien);
}

/**
 * @brief Cycles between visible aliens
 * @sa CL_NextAlienVisibleFromActor_f
 */
static void CL_NextAlien_f (void)
{
	int lastAlien;
	int i;

	if (cl.numLEs <= 0)
		return;

	lastAlien = Cvar_GetInteger("ui_lastalien");
	lastAlien = std::max(0, std::min(cl.numLEs - 1, lastAlien));

	i = lastAlien;
	do {
		const le_t* le;
		if (++i >= cl.numLEs)
			i = 0;
		le = &cl.LEs[i];
		if (le->inuse && LE_IsLivingAndVisibleActor(le) && le->team != cls.team
		 && le->team != TEAM_CIVILIAN) {
			lastAlien = i;
			CL_ViewCenterAtGridPosition(le->pos);
			Cvar_SetValue("ui_lastalien", lastAlien);
			return;
		}
	} while (i != lastAlien);
}

/**
 * @brief Cycles between visible aliens in reverse direction
 * @sa CL_NextAlienVisibleFromActor_f
 */
static void CL_PrevAlien_f (void)
{
	int lastAlien;
	int i;

	if (cl.numLEs <= 0)
		return;

	lastAlien = Cvar_GetInteger("ui_lastalien");
	lastAlien = std::max(0, std::min(cl.numLEs - 1, lastAlien));

	i = lastAlien;
	do {
		const le_t* le;
		if (--i < 0)
			i = cl.numLEs - 1;
		le = &cl.LEs[i];
		if (le->inuse && LE_IsLivingAndVisibleActor(le) && le->team != cls.team
		 && le->team != TEAM_CIVILIAN) {
			lastAlien = i;
			CL_ViewCenterAtGridPosition(le->pos);
			Cvar_SetValue("ui_lastalien", lastAlien);
			return;
		}
	} while (i != lastAlien);
}

/**
 * Performs pending actions for the given actor
 * @param le The actor that should perform the pending actions
 */
static void CL_ActorConfirmAction (le_t* le)
{
	if (le->team != cl.actTeam)
		return;

	/* might be a friendly player controlled actor */
	if (le->pnum != cl.pnum)
		return;

	switch (le->actorMode) {
	case M_PEND_MOVE:
		CL_ActorStartMove(le, le->mousePendPos);
		break;
	case M_PEND_FIRE_R:
	case M_PEND_FIRE_L:
		CL_ActorShoot(le, le->mousePendPos);
		break;
	default:
		break;
	}
}

/**
 * @brief Executes "pending" actions such as walking and firing.
 * @note Manually triggered by the player when hitting the "confirm" button.
 * @note When triggering this twice in 1000ms all pending actions are performed, otherwise only
 * the current selected actor is handled.
 */
static void CL_ActorConfirmAction_f (void)
{
	static int time = 0;

	if (time - cl.time < 1000) {
		le_t* le = nullptr;
		while ((le = LE_GetNextInUse(le))) {
			if (LE_IsLivingActor(le) && !LE_IsStunned(le) && le->team == cls.team)
				CL_ActorConfirmAction(le);
		}
	} else {
		time = cl.time;
		if (!selActor)
			return;
		CL_ActorConfirmAction(selActor);
	}
}

void ACTOR_InitStartup (void)
{
	cl_autostand = Cvar_Get("cl_autostand","1", CVAR_USERINFO | CVAR_ARCHIVE, "Prevent accidental wasting of TUs by allowing the actor to automatically stand up before starting long walks.");
	confirm_actions = Cvar_Get("confirm_actions", "0", CVAR_ARCHIVE, "Confirm all actions in tactical mode");
	cl_showactors = Cvar_Get("cl_showactors", "1", 0, "Show actors on the battlefield");
	Cmd_AddCommand("actor_next", CL_ActorNext_f, N_("Toggle to next living actor"));
	Cmd_AddCommand("actor_prev", CL_ActorPrev_f, N_("Toggle to previous living actor"));
	Cmd_AddCommand("actor_select", CL_ActorSelect_f, N_("Select an actor from list"));
	Cmd_AddCommand("actor_updatecurrent", CL_ActorUpdate_f, N_("Update an actor"));
	Cmd_AddCommand("actor_standcrouch", CL_ActorStandCrouch_f, N_("Toggle stand/crouch."));
	Cmd_AddCommand("actor_useheadgear", CL_ActorUseHeadgear_f, N_("Toggle the headgear"));
	Cmd_AddCommand("actor_use", CL_ActorUse_f, N_("Use"));
	Cmd_AddCommand("actor_confirmaction", CL_ActorConfirmAction_f, N_("Confirm the current action"));
	Cmd_AddCommand("actor_nextalien", CL_NextAlienVisibleFromActor_f, N_("Toggle to the next alien in sight of the selected actor."));

	Cmd_AddCommand("nextalien", CL_NextAlien_f, N_("Toggle camera to the next visible alien."));
	Cmd_AddCommand("prevalien", CL_PrevAlien_f, N_("Toggle camera to the previous visible alien."));

#ifdef DEBUG
	Cmd_AddCommand("debug_path", CL_DebugPath_f, "Display routing data for current mouse position.");
	Cmd_AddCommand("debug_drawblocked", CL_DisplayBlockedPaths_f, "Draw a marker for all blocked map-positions.");
	Cmd_AddCommand("debug_movemark", CL_DumpMoveMark_f, "Trigger Step::isPossible in every direction at the current truePos.");
	Cmd_AddCommand("debug_tus", CL_DumpTUs_f, "Show a table of the TUs that would be used by the current actor to move relative to his current location.");
	Cmd_AddCommand("debug_actorinvlist", nullptr, "Show the inventory list of all actors.");
#endif /* DEBUG */
}
