Пример #1
0
void CCommandAI::ExecuteAttack(Command& c)
{
	assert(owner->unitDef->canAttack);

	if (inCommand) {
		if (targetDied || (c.params.size() == 1 && UpdateTargetLostTimer(int(c.params[0])) == 0)) {
			FinishCommand();
			return;
		}
		if (!(c.options & ALT_KEY) && SkipParalyzeTarget(orderTarget)) {
			FinishCommand();
			return;
		}
	} else {
		if (c.params.size() == 1) {
			CUnit* targetUnit = unitHandler->GetUnit(c.params[0]);

			if (targetUnit == NULL) { FinishCommand(); return; }
			if (targetUnit == owner) { FinishCommand(); return; }
			if (targetUnit->GetTransporter() != NULL && !modInfo.targetableTransportedUnits) {
				FinishCommand(); return;
			}

			SetOrderTarget(targetUnit);
			owner->AttackUnit(targetUnit, (c.options & INTERNAL_ORDER) == 0, c.GetID() == CMD_MANUALFIRE);
			inCommand = true;
		} else {
			owner->AttackGround(c.GetPos(0), (c.options & INTERNAL_ORDER) == 0, c.GetID() == CMD_MANUALFIRE);
			inCommand = true;
		}
	}
}
Пример #2
0
bool CAirCAI::SelectNewAreaAttackTargetOrPos(const Command& ac) {
    assert(ac.GetID() == CMD_AREA_ATTACK || (ac.GetID() == CMD_ATTACK && ac.GetParamsCount() >= 3));

    if (ac.GetID() == CMD_ATTACK) {
        FinishCommand();
        return false;
    }

    const float3& pos = ac.GetPos(0);
    const float radius = ac.params[3];

    std::vector<int> enemyUnitIDs;
    CGameHelper::GetEnemyUnits(pos, radius, owner->allyteam, enemyUnitIDs);

    if (enemyUnitIDs.empty()) {
        float3 attackPos = pos + (gs->randVector() * radius);
        attackPos.y = CGround::GetHeightAboveWater(attackPos.x, attackPos.z);

        owner->AttackGround(attackPos, (ac.options & INTERNAL_ORDER) == 0, false);
        SetGoal(attackPos, owner->pos);
    } else {
        // note: the range of randFloat() is inclusive of 1.0f
        const unsigned int unitIdx = std::min<int>(gs->randFloat() * enemyUnitIDs.size(), enemyUnitIDs.size() - 1);
        const unsigned int unitID = enemyUnitIDs[unitIdx];

        CUnit* targetUnit = unitHandler->GetUnitUnsafe(unitID);

        SetOrderTarget(targetUnit);
        owner->AttackUnit(targetUnit, (ac.options & INTERNAL_ORDER) == 0, false);
        SetGoal(targetUnit->pos, owner->pos);
    }

    return true;
}
Пример #3
0
void CAirCAI::ExecuteAreaAttack(Command& c)
{
    assert(owner->unitDef->canAttack);

    // FIXME: check owner->UsingScriptMoveType() and skip rest if true?
    AAirMoveType* myPlane = GetStrafeAirMoveType(owner);

    if (targetDied) {
        targetDied = false;
        inCommand = false;
    }

    const float3& pos = c.GetPos(0);
    const float radius = c.params[3];

    if (inCommand) {
        if (myPlane->aircraftState == AAirMoveType::AIRCRAFT_LANDED)
            inCommand = false;

        if (orderTarget && orderTarget->pos.SqDistance2D(pos) > Square(radius)) {
            inCommand = false;

            // target wandered out of the attack-area
            SetOrderTarget(NULL);
            SelectNewAreaAttackTargetOrPos(c);
        }
    } else {
        if (myPlane->aircraftState != AAirMoveType::AIRCRAFT_LANDED) {
            inCommand = true;

            SelectNewAreaAttackTargetOrPos(c);
        }
    }
}
Пример #4
0
void CAirCAI::ExecuteAttack(Command& c)
{
	assert(owner->unitDef->canAttack);
	targetAge++;

	if (tempOrder && owner->moveState == MOVESTATE_MANEUVER) {
		// limit how far away we fly
		if (orderTarget && LinePointDist(commandPos1, commandPos2, orderTarget->pos) > 1500) {
			owner->AttackUnit(NULL, false, false);
			FinishCommand();
			return;
		}
	}

	if (inCommand) {
		if (targetDied || (c.params.size() == 1 && UpdateTargetLostTimer(int(c.params[0])) == 0)) {
			FinishCommand();
			return;
		}
		if (orderTarget != NULL) {
			if (orderTarget->unitDef->canfly && orderTarget->IsCrashing()) {
				owner->AttackUnit(NULL, false, false);
				FinishCommand();
				return;
			}
			if (!(c.options & ALT_KEY) && SkipParalyzeTarget(orderTarget)) {
				owner->AttackUnit(NULL, false, false);
				FinishCommand();
				return;
			}
		}
	} else {
		targetAge = 0;

		if (c.params.size() == 1) {
			CUnit* targetUnit = unitHandler->GetUnit(c.params[0]);

			if (targetUnit == NULL) { FinishCommand(); return; }
			if (targetUnit == owner) { FinishCommand(); return; }
			if (targetUnit->GetTransporter() != NULL && !modInfo.targetableTransportedUnits) {
				FinishCommand(); return;
			}

			SetGoal(targetUnit->pos, owner->pos, cancelDistance);
			SetOrderTarget(targetUnit);
			owner->AttackUnit(targetUnit, (c.options & INTERNAL_ORDER) == 0, false);

			inCommand = true;
		} else {
			SetGoal(c.GetPos(0), owner->pos, cancelDistance);
			owner->AttackGround(c.GetPos(0), (c.options & INTERNAL_ORDER) == 0, false);

			inCommand = true;
		}
	}
}
Пример #5
0
void CCommandAI::FinishCommand()
{
	assert(!commandQue.empty());

	const Command cmd = commandQue.front();
	const int cmdID  = cmd.GetID();
	const int cmdTag = cmd.tag;
	const bool dontRepeat = (cmd.options & INTERNAL_ORDER);

	if (repeatOrders
	    && !dontRepeat
	    && (cmdID != CMD_STOP)
	    && (cmdID != CMD_PATROL)
	    && (cmdID != CMD_SET_WANTED_MAX_SPEED)){
		commandQue.push_back(commandQue.front());
	}

	commandQue.pop_front();
	inCommand = false;
	targetDied = false;
	unimportantMove = false;
	SetOrderTarget(NULL);
	eoh->CommandFinished(*owner, cmd);
	eventHandler.UnitCmdDone(owner, cmdID, cmdTag);
	ClearTargetLock(cmd);

	if (commandQue.empty()) {
		if (!owner->group) {
			eoh->UnitIdle(*owner);
		}
		eventHandler.UnitIdle(owner);
	}

	// avoid infinite loops
	if (lastFinishCommand != gs->frameNum) {
		lastFinishCommand = gs->frameNum;
		if (!owner->IsStunned()) {
			SlowUpdate();
		}
	}
}
Пример #6
0
void CCommandAI::ExecuteAttack(Command& c)
{
	assert(owner->unitDef->canAttack);

	if (inCommand) {
		if (targetDied || (c.params.size() == 1 && UpdateTargetLostTimer(int(c.params[0])) == 0)) {
			FinishCommand();
			return;
		}
		if ((c.params.size() == 3) && (owner->commandShotCount > 0) && (commandQue.size() > 1)) {
			FinishCommand();
			return;
		}
		if (!(c.options & ALT_KEY) && SkipParalyzeTarget(orderTarget)) {
			FinishCommand();
			return;
		}
	}
	else {
		owner->commandShotCount = -1;

		if (c.params.size() == 1) {
			CUnit* targetUnit = uh->GetUnit(c.params[0]);

			if (targetUnit != NULL && targetUnit != owner) {
				owner->AttackUnit(targetUnit, c.GetID() == CMD_MANUALFIRE);

				SetOrderTarget(targetUnit);
				inCommand = true;
			} else {
				FinishCommand();
				return;
			}
		} else {
			float3 pos(c.params[0], c.params[1], c.params[2]);
			owner->AttackGround(pos, c.GetID() == CMD_MANUALFIRE);
			inCommand = true;
		}
	}
}
Пример #7
0
void CCommandAI::ExecuteInsert(const Command& c, bool fromSynced)
{
	if (c.params.size() < 3) {
		return;
	}

	// make the command
	Command newCmd((int)c.params[1], (unsigned char)c.params[2]);
	for (int p = 3; p < (int)c.params.size(); p++) {
		newCmd.params.push_back(c.params[p]);
	}

	// validate the command
	if (!AllowedCommand(newCmd, fromSynced)) {
		return;
	}

	CCommandQueue* queue = &commandQue;

	bool facBuildQueue = false;
	CFactoryCAI* facCAI = dynamic_cast<CFactoryCAI*>(this);
	if (facCAI) {
		if (c.options & CONTROL_KEY) {
			// check the build order
			const map<int, CFactoryCAI::BuildOption>& bOpts = facCAI->buildOptions;
			if ((newCmd.GetID() != CMD_STOP) && (newCmd.GetID() != CMD_WAIT) &&
			    ((newCmd.GetID() >= 0) || (bOpts.find(newCmd.GetID()) == bOpts.end()))) {
				return;
			}
			facBuildQueue = true;
		} else {
			// use the new commands
			queue = &facCAI->newUnitCommands;
		}
	}

	// FIXME: handle CMD_LOOPBACKATTACK, etc...

	CCommandQueue::iterator insertIt = queue->begin();

	if (c.options & ALT_KEY) {
		// treat param0 as a position
		int pos = (int)c.params[0];
		const unsigned int qsize = queue->size();
		if (pos < 0) {
			pos = qsize + pos + 1; // convert the negative index
			if (pos < 0) {
				pos = 0;
			}
		}
		if (pos > qsize) {
			pos = qsize;
		}
		std::advance(insertIt, pos);
	}
	else {
		// treat param0 as a command tag
		const unsigned int tag = (unsigned int)c.params[0];
		CCommandQueue::iterator ci;
		bool found = false;
		for (ci = queue->begin(); ci != queue->end(); ++ci) {
			const Command& qc = *ci;
			if (qc.tag == tag) {
				insertIt = ci;
				found = true;
				break;
			}
		}
		if (!found) {
			return;
		}
		if ((c.options & RIGHT_MOUSE_KEY) && (insertIt != queue->end())) {
			++insertIt; // insert after the tagged command
		}
	}

	if (facBuildQueue) {
		facCAI->InsertBuildCommand(insertIt, newCmd);
		if (!owner->stunned) {
			SlowUpdate();
		}
		return;
	}

	// shutdown the current order if the insertion is at the beginning
	if (!queue->empty() && (insertIt == queue->begin())) {
		inCommand = false;
		targetDied = false;
		unimportantMove = false;
		SetOrderTarget(NULL);
		const Command& cmd = queue->front();
		eoh->CommandFinished(*owner, cmd);
		eventHandler.UnitCmdDone(owner, cmd.GetID(), cmd.tag);
	}

	queue->insert(insertIt, newCmd);

	if (!owner->stunned) {
		SlowUpdate();
	}
}
Пример #8
0
void CCommandAI::GiveAllowedCommand(const Command& c, bool fromSynced)
{
	if (ExecuteStateCommand(c)) {
		return;
	}

	switch (c.GetID()) {
		case CMD_SELFD: {
			if (owner->unitDef->canSelfD) {
				if (!(c.options & SHIFT_KEY) || commandQue.empty()) {
					if (owner->selfDCountdown != 0) {
						owner->selfDCountdown = 0;
					} else {
						owner->selfDCountdown = owner->unitDef->selfDCountdown*2+1;
					}
				}
				else if (commandQue.back().GetID() == CMD_SELFD) {
					commandQue.pop_back();
				} else {
					commandQue.push_back(c);
				}
			}
			return;
		}
		case CMD_SET_WANTED_MAX_SPEED: {
			if (CanSetMaxSpeed() &&
			    (commandQue.empty() ||
			     (commandQue.back().GetID() != CMD_SET_WANTED_MAX_SPEED))) {
				// bail early, do not check for overlaps or queue cancelling
				commandQue.push_back(c);
				if (commandQue.size()==1 && !owner->beingBuilt) {
					SlowUpdate();
				}
			}
			return;
		}
		case CMD_WAIT: {
			GiveWaitCommand(c);
			return;
		}
		case CMD_INSERT: {
			ExecuteInsert(c, fromSynced);
			return;
		}
		case CMD_REMOVE: {
			ExecuteRemove(c);
			return;
		}
	}

	// flush the queue for immediate commands
	if (!(c.options & SHIFT_KEY)) {
		if (!commandQue.empty()) {
			const int& cmd_id = commandQue.front().GetID();
			if ((cmd_id == CMD_MANUALFIRE) ||
			    (cmd_id == CMD_ATTACK)     ||
			    (cmd_id == CMD_AREA_ATTACK)) {
				owner->AttackUnit(0,true);
			}
			waitCommandsAI.ClearUnitQueue(owner, commandQue);
			ClearCommandDependencies();
			commandQue.clear();
		}
		inCommand=false;
		SetOrderTarget(NULL);
	}

	AddCommandDependency(c);

	if (c.GetID() == CMD_PATROL) {
		CCommandQueue::iterator ci = commandQue.begin();
		for (; ci != commandQue.end() && ci->GetID() != CMD_PATROL; ++ci) {
			// just increment
		}
		if (ci == commandQue.end()) {
			if (commandQue.empty()) {
				Command c2(CMD_PATROL, c.options);
				c2.params.push_back(owner->pos.x);
				c2.params.push_back(owner->pos.y);
				c2.params.push_back(owner->pos.z);
				commandQue.push_back(c2);
			}
			else {
				do {
					--ci;
					if (ci->params.size() >= 3) {
						Command c2(CMD_PATROL, c.options);
						c2.params = ci->params;
						commandQue.push_back(c2);
						break;
					} else if (ci == commandQue.begin()) {
						Command c2(CMD_PATROL, c.options);
						c2.params.push_back(owner->pos.x);
						c2.params.push_back(owner->pos.y);
						c2.params.push_back(owner->pos.z);
						commandQue.push_back(c2);
						break;
					}
				}
				while (ci != commandQue.begin());
			}
		}
	}

	// cancel duplicated commands
	bool first;
	if (CancelCommands(c, commandQue, first) > 0) {
		if (first) {
			Command stopCommand(CMD_STOP);
			commandQue.push_front(stopCommand);
			SlowUpdate();
		}
		return;
	}

	// do not allow overlapping commands
	if (!GetOverlapQueued(c).empty()) {
		return;
	}

	if (c.GetID() == CMD_ATTACK) {
		// avoid weaponless units moving to 0 distance when given attack order
		if (owner->weapons.empty() && (owner->unitDef->canKamikaze == false)) {
			Command c2(CMD_STOP);
			commandQue.push_back(c2);
			return;
		}
	}

	commandQue.push_back(c);

	if (commandQue.size() == 1 && !owner->beingBuilt && !owner->stunned) {
		SlowUpdate();
	}
}
Пример #9
0
CCommandAI::~CCommandAI()
{
	SetOrderTarget(NULL);
	ClearCommandDependencies();
}
Пример #10
0
void CMobileCAI::ExecuteAttack(Command &c)
{
	assert(owner->unitDef->canAttack);

	// limit how far away we fly based on our movestate
	if (tempOrder && orderTarget) {
		const float3& closestPos = ClosestPointOnLine(commandPos1, commandPos2, owner->pos);
		const float curTargetDist = LinePointDist(closestPos, commandPos2, orderTarget->pos);
		const float maxTargetDist = (500 * owner->moveState + owner->maxRange);

		if (owner->moveState < MOVESTATE_ROAM && curTargetDist > maxTargetDist) {
			StopMove();
			FinishCommand();
			return;
		}
	}

	// check if we are in direct command of attacker
	if (!inCommand) {
		if (c.params.size() == 1) {
			CUnit* targetUnit = unitHandler->GetUnit(c.params[0]);

			// check if we have valid target parameter and that we aren't attacking ourselves
			if (targetUnit == NULL) { StopMove(); FinishCommand(); return; }
			if (targetUnit == owner) { StopMove(); FinishCommand(); return; }
			if (targetUnit->GetTransporter() != NULL && !modInfo.targetableTransportedUnits) {
				StopMove(); FinishCommand(); return;
			}

			const float3 tgtErrPos = targetUnit->pos + owner->posErrorVector * 128;
			const float3 tgtPosDir = (tgtErrPos - owner->pos).Normalize();

			SetGoal(tgtErrPos - tgtPosDir * targetUnit->radius, owner->pos);
			SetOrderTarget(targetUnit);
			owner->AttackUnit(targetUnit, (c.options & INTERNAL_ORDER) == 0, c.GetID() == CMD_MANUALFIRE);

			inCommand = true;
		}
		else if (c.params.size() >= 3) {
			// user gave force-fire attack command
			SetGoal(c.GetPos(0), owner->pos);

			inCommand = true;
		}
	}

	// if our target is dead or we lost it then stop attacking
	// NOTE: unit should actually just continue to target area!
	if (targetDied || (c.params.size() == 1 && UpdateTargetLostTimer(int(c.params[0])) == 0)) {
		// cancel keeppointingto
		StopMove();
		FinishCommand();
		return;
	}


	// user clicked on enemy unit (note that we handle aircrafts slightly differently)
	if (orderTarget != NULL) {
		bool tryTargetRotate  = false;
		bool tryTargetHeading = false;

		float edgeFactor = 0.0f; // percent offset to target center
		const float3 targetMidPosVec = owner->midPos - orderTarget->midPos;

		const float targetGoalDist = (orderTarget->pos + owner->posErrorVector * 128.0f).SqDistance2D(goalPos);
		const float targetPosDist = Square(10.0f + orderTarget->pos.distance2D(owner->pos) * 0.2f);
		const float minPointingDist = std::min(1.0f * owner->losRadius * loshandler->losDiv, owner->maxRange * 0.9f);

		// FIXME? targetMidPosMaxDist is 3D, but compared with a 2D value
		const float targetMidPosDist2D = targetMidPosVec.Length2D();
		//const float targetMidPosMaxDist = owner->maxRange - (orderTarget->speed.SqLength() / owner->unitDef->maxAcc);

		if (!owner->weapons.empty()) {
			if (!(c.options & ALT_KEY) && SkipParalyzeTarget(orderTarget)) {
				StopMove();
				FinishCommand();
				return;
			}
		}

		for (unsigned int wNum = 0; wNum < owner->weapons.size(); wNum++) {
			CWeapon* w = owner->weapons[wNum];

			if (c.GetID() == CMD_MANUALFIRE) {
				assert(owner->unitDef->canManualFire);

				if (!w->weaponDef->manualfire) {
					continue;
				}
			}

			tryTargetRotate  = w->TryTargetRotate(orderTarget, (c.options & INTERNAL_ORDER) == 0);
			tryTargetHeading = w->TryTargetHeading(GetHeadingFromVector(-targetMidPosVec.x, -targetMidPosVec.z), orderTarget->pos, orderTarget != NULL, orderTarget);

			if (tryTargetRotate || tryTargetHeading)
				break;

			edgeFactor = math::fabs(w->targetBorder);
		}


		// if w->AttackUnit() returned true then we are already
		// in range with our biggest (?) weapon, so stop moving
		// also make sure that we're not locked in close-in/in-range state loop
		// due to rotates invoked by in-range or out-of-range states
		if (tryTargetRotate) {
			const bool canChaseTarget = (!tempOrder || owner->moveState != MOVESTATE_HOLDPOS);
			const bool targetBehind = (targetMidPosVec.dot(orderTarget->speed) < 0.0f);

			if (canChaseTarget && tryTargetHeading && targetBehind) {
				SetGoal(owner->pos + (orderTarget->speed * 80), owner->pos, SQUARE_SIZE, orderTarget->speed.Length() * 1.1f);
			} else {
				StopMove();

				if (gs->frameNum > lastCloseInTry + MAX_CLOSE_IN_RETRY_TICKS) {
					owner->moveType->KeepPointingTo(orderTarget->midPos, minPointingDist, true);
				}
			}

			owner->AttackUnit(orderTarget, (c.options & INTERNAL_ORDER) == 0, c.GetID() == CMD_MANUALFIRE);
		}

		// if we're on hold pos in a temporary order, then none of the close-in
		// code below should run, and the attack command is cancelled.
		else if (tempOrder && owner->moveState == MOVESTATE_HOLDPOS) {
			StopMove();
			FinishCommand();
			return;
		}

		// if ((our movetype has type HoverAirMoveType and length of 2D vector from us to target
		// less than 90% of our maximum range) OR squared length of 2D vector from us to target
		// less than 1024) then we are close enough
		else if (targetMidPosDist2D < (owner->maxRange * 0.9f)) {
			if (dynamic_cast<CHoverAirMoveType*>(owner->moveType) != NULL || (targetMidPosVec.SqLength2D() < 1024)) {
				StopMove();
				owner->moveType->KeepPointingTo(orderTarget->midPos, minPointingDist, true);
			}

			// if (((first weapon range minus first weapon length greater than distance to target)
			// and length of 2D vector from us to target less than 90% of our maximum range)
			// then we are close enough, but need to move sideways to get a shot.
			//assumption is flawed: The unit may be aiming or otherwise unable to shoot
			else if (owner->unitDef->strafeToAttack && targetMidPosDist2D < (owner->maxRange * 0.9f)) {
				moveDir ^= (owner->moveType->progressState == AMoveType::Failed);

				const float sin = moveDir ? 3.0/5 : -3.0/5;
				const float cos = 4.0 / 5;

				float3 goalDiff;
				goalDiff.x = targetMidPosVec.dot(float3(cos, 0, -sin));
				goalDiff.z = targetMidPosVec.dot(float3(sin, 0,  cos));
				goalDiff *= (targetMidPosDist2D < (owner->maxRange * 0.3f)) ? 1/cos : cos;
				goalDiff += orderTarget->pos;
				SetGoal(goalDiff, owner->pos);
			}
		}

		// if 2D distance of (target position plus attacker error vector times 128)
		// to goal position greater than
		// (10 plus 20% of 2D distance between attacker and target) then we need to close
		// in on target more
		else if (targetGoalDist > targetPosDist) {
			// if the target isn't in LOS, go to its approximate position
			// otherwise try to go precisely to the target
			// this should fix issues with low range weapons (mainly melee)
			const float3 errPos = ((orderTarget->losStatus[owner->allyteam] & LOS_INLOS)? ZeroVector: owner->posErrorVector * 128.0f);
			const float3 tgtPos = orderTarget->pos + errPos;

			const float3 norm = (tgtPos - owner->pos).Normalize();
			const float3 goal = tgtPos - norm * (orderTarget->radius * edgeFactor * 0.8f);

			SetGoal(goal, owner->pos);

			if (lastCloseInTry < gs->frameNum + MAX_CLOSE_IN_RETRY_TICKS)
				lastCloseInTry = gs->frameNum;
		}
	}

	// user wants to attack the ground; cycle through our
	// weapons until we find one that can accomodate him
	else if (c.params.size() >= 3) {
		const float3 attackPos = c.GetPos(0);
		const float3 attackVec = attackPos - owner->pos;

		bool foundWeapon = false;

		for (unsigned int wNum = 0; wNum < owner->weapons.size(); wNum++) {
			CWeapon* w = owner->weapons[wNum];

			if (foundWeapon)
				break;

			// XXX HACK - special weapon overrides any checks
			if (c.GetID() == CMD_MANUALFIRE) {
				assert(owner->unitDef->canManualFire);

				if (!w->weaponDef->manualfire)
					continue;
				if (attackVec.SqLength() >= (w->range * w->range))
					continue;

				StopMove();
				owner->AttackGround(attackPos, (c.options & INTERNAL_ORDER) == 0, c.GetID() == CMD_MANUALFIRE);
				owner->moveType->KeepPointingTo(attackPos, owner->maxRange * 0.9f, true);

				foundWeapon = true;
			} else {
				// NOTE:
				//   we call TryTargetHeading which is less restrictive than TryTarget
				//   (eg. the former succeeds even if the unit has not already aligned
				//   itself with <attackVec>)
				if (w->TryTargetHeading(GetHeadingFromVector(attackVec.x, attackVec.z), attackPos, (c.options & INTERNAL_ORDER) == 0, NULL)) {
					if (w->TryTargetRotate(attackPos, (c.options & INTERNAL_ORDER) == 0)) {
						StopMove();
						owner->AttackGround(attackPos, (c.options & INTERNAL_ORDER) == 0, c.GetID() == CMD_MANUALFIRE);

						foundWeapon = true;
					}

					// for gunships, this pitches the nose down such that
					// TryTargetRotate (which also checks range for itself)
					// has a bigger chance of succeeding
					//
					// hence it must be called as soon as we get in range
					// and may not depend on what TryTargetRotate returns
					// (otherwise we might never get a firing solution)
					owner->moveType->KeepPointingTo(attackPos, owner->maxRange * 0.9f, true);
				}
			}
		}

		#if 0
		// no weapons --> no need to stop at an arbitrary distance?
		else if (diff.SqLength2D() < 1024) {
			StopMove();
			owner->moveType->KeepPointingTo(attackPos, owner->maxRange * 0.9f, true);
		}
		#endif

		// if we are unarmed and more than 10 elmos distant
		// from target position, then keeping moving closer
		if (owner->weapons.empty() && attackPos.SqDistance2D(goalPos) > 100) {
			SetGoal(attackPos, owner->pos);
		}
	}