Exemple #1
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;
		}
	}
}
Exemple #2
0
void CBuilderCAI::ExecuteCapture(Command& c)
{
	// not all builders are capture-capable by default
	if (!owner->unitDef->canCapture)
		return;

	if (c.params.size() == 1 || c.params.size() == 5) {
		// capture unit
		CUnit* unit = unitHandler->GetUnit(c.params[0]);

		if (unit == NULL) {
			FinishCommand();
			return;
		}

		if (c.params.size() == 5) {
			const float3& pos = c.GetPos(1);
			const float radius = c.params[4] + 100; // do not walk too far outside capture area

			if (((pos - unit->pos).SqLength2D() > (radius * radius) ||
				(ownerBuilder->curCapture == unit && unit->IsMoving() && !IsInBuildRange(unit)))) {
				StopMove();
				FinishCommand();
				return;
			}
		}

		if (unit->unitDef->capturable && unit->team != owner->team && UpdateTargetLostTimer(unit->id)) {
			if (MoveInBuildRange(unit)) {
				ownerBuilder->SetCaptureTarget(unit);
			}
		} else {
			StopMove();
			FinishCommand();
		}
	} else if (c.params.size() == 4) {
		// area capture
		const float3 pos = c.GetPos(0);
		const float radius = c.params[3];

		ownerBuilder->StopBuild();

		if (FindCaptureTargetAndCapture(pos, radius, c.options, (c.options & META_KEY))) {
			inCommand = false;
			SlowUpdate();
			return;
		}
		if (!(c.options & ALT_KEY)) {
			FinishCommand();
		}
	} else {
		FinishCommand();
	}
}
Exemple #3
0
void CBuilderCAI::GiveCommandReal(const Command& c, bool fromSynced)
{
	if (!AllowedCommand(c, fromSynced))
		return;

	// don't guard yourself
	if ((c.GetID() == CMD_GUARD) &&
	    (c.params.size() == 1) && ((int)c.params[0] == owner->id)) {
		return;
	}

	// stop building/reclaiming/... if the new command is not queued, i.e. replaces our current activity
	// FIXME should happen just before CMobileCAI::GiveCommandReal? (the new cmd can still be skipped!)
	if ((c.GetID() != CMD_WAIT && c.GetID() != CMD_SET_WANTED_MAX_SPEED) && !(c.options & SHIFT_KEY)) {
		if (nonQueingCommands.find(c.GetID()) == nonQueingCommands.end()) {
			building = false;
			static_cast<CBuilder*>(owner)->StopBuild();
		}
	}

	if (buildOptions.find(c.GetID()) != buildOptions.end()) {
		if (c.params.size() < 3)
			return;

		BuildInfo bi;
		bi.pos = c.GetPos(0);

		if (c.params.size() == 4)
			bi.buildFacing = abs((int)c.params[3]) % NUM_FACINGS;

		bi.def = unitDefHandler->GetUnitDefByID(-c.GetID());
		bi.pos = CGameHelper::Pos2BuildPos(bi, true);

		// We are a static building, check if the buildcmd is in range
		if (!owner->unitDef->canmove) {
			if (!IsInBuildRange(bi.pos, GetBuildOptionRadius(bi.def, c.GetID()))) {
				return;
			}
		}

		const CUnit* nanoFrame = NULL;

		// check if the buildpos is blocked
		if (IsBuildPosBlocked(bi, &nanoFrame))
			return;

		// if it is a nanoframe help to finish it
		if (nanoFrame != NULL) {
			Command c2(CMD_REPAIR, c.options | INTERNAL_ORDER, nanoFrame->id);
			CMobileCAI::GiveCommandReal(c2, fromSynced);
			CMobileCAI::GiveCommandReal(c, fromSynced);
			return;
		}
	} else {
		if (c.GetID() < 0)
			return;
	}

	CMobileCAI::GiveCommandReal(c, fromSynced);
}
Exemple #4
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;
		}
	}
}
Exemple #5
0
void CBuilderCAI::ExecuteResurrect(Command& c)
{
	// not all builders are resurrect-capable by default
	if (!owner->unitDef->canResurrect)
		return;

	if (c.params.size() == 1) {
		unsigned int id = (unsigned int) c.params[0];

		if (id >= unitHandler->MaxUnits()) { // resurrect feature
			CFeature* feature = featureHandler->GetFeature(id - unitHandler->MaxUnits());

			if (feature && feature->udef != NULL) {
				if (((c.options & INTERNAL_ORDER) && !(c.options & CONTROL_KEY) && IsFeatureBeingReclaimed(feature->id, owner)) ||
					!ResurrectObject(feature)) {
					StopMove();
					RemoveUnitFromResurrecters(owner);
					FinishCommand();
				}
				else {
					AddUnitToResurrecters(owner);
				}
			} else {
				RemoveUnitFromResurrecters(owner);

				if (ownerBuilder->lastResurrected && unitHandler->GetUnitUnsafe(ownerBuilder->lastResurrected) != NULL && owner->unitDef->canRepair) {
					// resurrection finished, start repair (by overwriting the current order)
					c = Command(CMD_REPAIR, c.options | INTERNAL_ORDER, ownerBuilder->lastResurrected);

					ownerBuilder->lastResurrected = 0;
					inCommand = false;
					SlowUpdate();
					return;
				}
				StopMove();
				FinishCommand();
			}
		} else { // resurrect unit
			RemoveUnitFromResurrecters(owner);
			FinishCommand();
		}
	} else if (c.params.size() == 4) {
		// area resurrect
		const float3 pos = c.GetPos(0);
		const float radius = c.params[3];

		if (FindResurrectableFeatureAndResurrect(pos, radius, c.options, (c.options & META_KEY))) {
			inCommand = false;
			SlowUpdate();
			return;
		}
		if (!(c.options & ALT_KEY)) {
			FinishCommand();
		}
	} else {
		// wrong number of parameters
		RemoveUnitFromResurrecters(owner);
		FinishCommand();
	}
}
Exemple #6
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;
}
Exemple #7
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);
        }
    }
}
bool CTransportCAI::AllowedCommand(const Command& c, bool fromSynced)
{
	if (!CMobileCAI::AllowedCommand(c, fromSynced)) {
		return false;
	}

	switch (c.GetID()) {
		case CMD_UNLOAD_UNIT:
		case CMD_UNLOAD_UNITS: {
			const CTransportUnit* transport = static_cast<CTransportUnit*>(owner);
			const std::list<CTransportUnit::TransportedUnit>& transportees = transport->GetTransportedUnits();

			// allow unloading empty transports for easier setup of transport bridges
			if (transportees.empty())
				return true;

			if (c.GetParamsCount() == 5) {
				if (fromSynced) {
					// point transported buildings (...) in their wanted direction after unloading
					for (auto it = transportees.begin(); it != transportees.end(); ++it) {
						CBuilding* building = dynamic_cast<CBuilding*>(it->unit);

						if (building == NULL)
							continue;

						building->buildFacing = std::abs(int(c.GetParam(4))) % NUM_FACINGS;
					}
				}
			}

			if (c.GetParamsCount() >= 4) {
				// find unload positions for transportees (WHY can this run in unsynced context?)
				for (auto it = transportees.begin(); it != transportees.end(); ++it) {
					CUnit* u = it->unit;

					const float radius = (c.GetID() == CMD_UNLOAD_UNITS)? c.GetParam(3): 0.0f;
					const float spread = u->radius * transport->unitDef->unloadSpread;

					float3 foundPos;

					if (FindEmptySpot(c.GetPos(0), radius, spread, foundPos, u, fromSynced)) {
						return true;
					}
					 // FIXME: support arbitrary unloading order for other unload types also
					if (unloadType != UNLOAD_LAND) {
						return false;
					}
				}

				// no empty spot found for any transported unit
				return false;
			}

			break;
		}
	}

	return true;
}
static inline bool IsCommandInMap(const Command& c)
{
	// TODO:
	//   extend the check to commands for which
	//   position is not stored in params[0..2]
	if (c.params.size() < 3) {
		return true;
	}
	if ((c.GetPos(0)).IsInBounds()) {
		return true;
	}
	const float3 pos = c.GetPos(0);
	LOG_L(L_DEBUG, "Dropped command %d: outside of map (x:%f y:%f z:%f)", c.GetID(), pos.x, pos.y, pos.z);

	return false;

}
void CTransportCAI::UnloadDrop(Command& c)
{
	// fly over and drop unit
	if (inCommand) {
		if (!owner->script->IsBusy()) {
			FinishCommand();
		}
	} else {
		auto transport = static_cast<CTransportUnit*>(owner);
		const auto& transportees = transport->GetTransportedUnits();

		if (transportees.empty() || dropSpots.empty()) {
			FinishCommand();
			return;
		}

		float3 pos = c.GetPos(0);

		// head towards goal
		// note that HoverAirMoveType must be modified to allow
		// non-stop movement through goals for this to work well
		if (goalPos.SqDistance2D(pos) > Square(20.0f)) {
			SetGoal(lastDropPos = pos, owner->pos);
		}

		CHoverAirMoveType* am = dynamic_cast<CHoverAirMoveType*>(owner->moveType);
		CUnit* transportee = (transportees.front()).unit;

		if (am != NULL) {
			pos.y = CGround::GetHeightAboveWater(pos.x, pos.z);
			am->maxDrift = 1.0f;

			// if near target or passed it accidentally, drop unit
			if (owner->pos.SqDistance2D(pos) < Square(40.0f) || (((pos - owner->pos).Normalize()).SqDistance(owner->frontdir) > 0.25 && owner->pos.SqDistance2D(pos)< (205*205))) {
				am->SetAllowLanding(false);

				owner->script->EndTransport();
				transport->DetachUnitFromAir(transportee, pos);

				dropSpots.pop_back();

				if (dropSpots.empty()) {
					// move the transport away after last drop
					SetGoal(owner->pos + owner->frontdir * 200, owner->pos);
				}

				FinishCommand();
			}
		} else {
			inCommand = true;
			StopMove();
			owner->script->TransportDrop(transportee, pos);
		}
	}
}
Exemple #11
0
static inline bool IsCommandInMap(const Command& c)
{
	// TODO:
	//   extend the check to commands for which
	//   position is not stored in params[0..2]
	if (c.params.size() >= 3) {
		return ((c.GetPos(0)).IsInBounds());
	}

	return true;
}
void CTransportCAI::UnloadUnits_Drop(Command& c, CTransportUnit* transport)
{
	// called repeatedly for each unit till units are unloaded
	if (lastCall == gs->frameNum) {
		// avoid infinite loops
		return;
	}

	lastCall = gs->frameNum;

	const auto& transportees = transport->GetTransportedUnits();

	if (transportees.empty()) {
		FinishCommand();
		return;
	}

	bool canUnload = false;

	// at the start of each user command
	if (isFirstIteration)	{
		dropSpots.clear();

		startingDropPos = c.GetPos(0);
		approachVector = (startingDropPos - owner->pos).Normalize();

		canUnload = FindEmptyDropSpots(startingDropPos, startingDropPos + approachVector * std::max(16.0f, c.params[3]), dropSpots);
	} else if (!dropSpots.empty() ) {
		// make sure we check current spot in front of us each
		// unload, take last landing pos as new start spot
		// pos = dropSpots.back();
		canUnload = !dropSpots.empty();
	}

	if (canUnload) {
		if (SpotIsClear(dropSpots.back(), (transportees.front()).unit)) {
			Command c2(CMD_UNLOAD_UNIT, c.options | INTERNAL_ORDER, dropSpots.back());
			commandQue.push_front(c2);

			SlowUpdate();
			isFirstIteration = false;
			return;
		} else {
			dropSpots.pop_back();
		}
	} else {
		startingDropPos = -OnesVector;
		isFirstIteration = true;
		dropSpots.clear();
		FinishCommand();
	}
}
Exemple #13
0
CCommandQueue::iterator CCommandAI::GetCancelQueued(const Command& c, CCommandQueue& q)
{
	CCommandQueue::iterator ci = q.end();

	while (ci != q.begin()) {
		--ci; //iterate from the end and dont check the current order
		const Command& c2 = *ci;
		const int cmdID = c.GetID();
		const int cmd2ID = c2.GetID();

		const bool attackAndFight = (cmdID == CMD_ATTACK && cmd2ID == CMD_FIGHT && c2.params.size() == 1);

		if (c2.params.size() != c.params.size())
			continue;

		if ((cmdID == cmd2ID) || (cmdID < 0 && cmd2ID < 0) || attackAndFight) {
			if (c.params.size() == 1) {
				// assume the param is a unit-ID or feature-ID
				if ((c2.params[0] == c.params[0]) &&
				    (cmd2ID != CMD_SET_WANTED_MAX_SPEED)) {
					return ci;
				}
			}
			else if (c.params.size() >= 3) {
				if (cmdID < 0) {
					BuildInfo bc1(c);
					BuildInfo bc2(c2);

					if (bc1.def == NULL) continue;
					if (bc2.def == NULL) continue;

					if (math::fabs(bc1.pos.x - bc2.pos.x) * 2 <= std::max(bc1.GetXSize(), bc2.GetXSize()) * SQUARE_SIZE &&
					    math::fabs(bc1.pos.z - bc2.pos.z) * 2 <= std::max(bc1.GetZSize(), bc2.GetZSize()) * SQUARE_SIZE) {
						return ci;
					}
				} else {
					// assume c and c2 are positional commands
					const float3& c1p = c.GetPos(0);
					const float3& c2p = c2.GetPos(0);

					if ((c1p - c2p).SqLength2D() >= (COMMAND_CANCEL_DIST * COMMAND_CANCEL_DIST))
						continue;
					if ((c.options & SHIFT_KEY) != 0 && (c.options & INTERNAL_ORDER) != 0)
						continue;

					return ci;
				}
			}
		}
	}
	return q.end();
}
Exemple #14
0
/**
* @brief executes the move command
*/
void CMobileCAI::ExecuteMove(Command &c)
{
	const float3 cmdPos = c.GetPos(0);

	if (cmdPos != goalPos) {
		SetGoal(cmdPos, owner->pos);
	}

	if ((owner->pos - goalPos).SqLength2D() < cancelDistance || owner->moveType->progressState == AMoveType::Failed) {
		FinishCommand();
	}
	return;
}
Exemple #15
0
void CTransportCAI::UnloadUnits_Drop(Command& c, CTransportUnit* transport)
{
	// called repeatedly for each unit till units are unloaded
	if (lastCall == gs->frameNum) { // avoid infinite loops
		return;
	}
	lastCall = gs->frameNum;

	if (static_cast<CTransportUnit*>(owner)->GetTransportedUnits().empty()) {
		FinishCommand();
		return;
	}

	float3 pos = c.GetPos(0);
	float radius = c.params[3];
	bool canUnload = false;

	// at the start of each user command
	if (isFirstIteration)	{
		dropSpots.clear();
		startingDropPos = pos;

		approachVector = startingDropPos - owner->pos;
		approachVector.Normalize();
		canUnload = FindEmptyDropSpots(pos, pos + approachVector * std::max(16.0f,radius), dropSpots);
	} else if (!dropSpots.empty() ) {
		// make sure we check current spot infront of us each unload
		pos = dropSpots.back(); // take last landing pos as new start spot
		canUnload = !dropSpots.empty();
	}

	if (canUnload) {
		if (SpotIsClear(dropSpots.back(), static_cast<CTransportUnit*>(owner)->GetTransportedUnits().front().unit)) {
			const float3 pos = dropSpots.back();
			Command c2(CMD_UNLOAD_UNIT, c.options | INTERNAL_ORDER, pos);
			commandQue.push_front(c2);

			SlowUpdate();
			isFirstIteration = false;
			return;
		} else {
			dropSpots.pop_back();
		}
	} else {
		startingDropPos = float3(-1.0f, -1.0f, -1.0f);
		isFirstIteration = true;
		dropSpots.clear();
		FinishCommand();
	}
}
Exemple #16
0
void CBuilderCAI::ExecutePatrol(Command& c)
{
	if (!owner->unitDef->canPatrol)
		return;

	if (c.params.size() < 3) {
		return;
	}

	Command temp(CMD_FIGHT, c.options|INTERNAL_ORDER, c.GetPos(0));

	commandQue.push_back(c);
	commandQue.pop_front();
	commandQue.push_front(temp);
	Command tmpC(CMD_PATROL);
	eoh->CommandFinished(*owner, tmpC);
	SlowUpdate();
}
static inline bool AdjustGroundAttackCommand(const Command& c, bool fromSynced, bool aiOrder)
{
	if (c.params.size() < 3)
		return false;
	if (aiOrder)
		return false;

	const float3 cPos = c.GetPos(0);

	#if 0
	// check if attack-ground is really attack-ground
	//
	// NOTE:
	//     problematic if command contains value from UHM
	//     but is evaluated in synced context against SHM
	//     after roundtrip (when UHM and SHM differ a lot)
	//
	//     instead just clamp the elevation, which creates
	//     fewer issues overall (eg. artillery force-firing
	//     at positions outside LOS where UHM and SHM do not
	//     match will not be broken)
	//
	if (math::fabs(cPos.y - gHeight) > SQUARE_SIZE) {
		return false;
	}
	#else
	// FIXME: is fromSynced really sync-safe???
	// NOTE:
	//   uses gHeight = min(cPos.y, GetHeightAboveWater) instead
	//   of gHeight = GetHeightReal because GuiTraceRay can stop
	//   at water surface, so the attack-position would be moved
	//   UNDERWATER and cause ground-attack orders to fail (this
	//   SHOULD no longer happen, Weapon::AdjustTargetPosToWater
	//   always forcibly adjusts positions to respect waterWeapon
	//   now)
	//
	Command& cc = const_cast<Command&>(c);

	cc.params[1] = std::min(cPos.y, CGround::GetHeightAboveWater(cPos.x, cPos.z, true || fromSynced));
	// cc.params[1] = CGround::GetHeightReal(cPos.x, cPos.z, true || fromSynced);

	return true;
	#endif
}
Exemple #18
0
/**
* @brief Executes the Patrol command c
*/
void CMobileCAI::ExecutePatrol(Command &c)
{
	assert(owner->unitDef->canPatrol);
	if (c.params.size() < 3) {
		LOG_L(L_ERROR,
				"Received a Patrol command with less than 3 params on %s in MobileCAI",
				owner->unitDef->humanName.c_str());
		return;
	}
	Command temp(CMD_FIGHT, c.options | INTERNAL_ORDER, c.GetPos(0));

	commandQue.push_back(c);
	commandQue.pop_front();
	commandQue.push_front(temp);

	Command tmpC(CMD_PATROL);
	eoh->CommandFinished(*owner, tmpC);
	ExecuteFight(temp);
}
Exemple #19
0
void CTransportCAI::UnloadUnits_Land(Command& c, CTransportUnit* transport)
{
	if (lastCall == gs->frameNum) {
		// avoid infinite loops
		return;
	}

	lastCall = gs->frameNum;

	if (static_cast<CTransportUnit*>(owner)->GetTransportedUnits().empty()) {
		FinishCommand();
		return;
	}

	bool canUnload = false;
	float3 unloadPos;
	CUnit* u = NULL;
	const CTransportUnit* ownerTrans = static_cast<CTransportUnit*>(owner);
	const std::list<CTransportUnit::TransportedUnit>& transpunits = ownerTrans->GetTransportedUnits();

	for(std::list<CTransportUnit::TransportedUnit>::const_iterator it = transpunits.begin(); it != transpunits.end(); ++it) {
		u = it->unit;
		const float3 pos = c.GetPos(0);
		const float radius = c.params[3];
		const float spread = u->radius * ownerTrans->unitDef->unloadSpread;
		canUnload = FindEmptySpot(pos, radius, spread, unloadPos, u);
		if (canUnload) {
			break;
		}
	}

	if (canUnload) {
		Command c2(CMD_UNLOAD_UNIT, c.options | INTERNAL_ORDER, unloadPos);
		c2.PushParam(u->id);
		commandQue.push_front(c2);
		SlowUpdate();
	} else {
		FinishCommand();
	}
}
void CTransportCAI::UnloadUnits_LandFlood(Command& c, CTransportUnit* transport)
{
	if (lastCall == gs->frameNum) {
		// avoid infinite loops
		return;
	}

	lastCall = gs->frameNum;

	const auto& transportees = transport->GetTransportedUnits();

	if (transportees.empty()) {
		FinishCommand();
		return;
	}

	float3 pos = c.GetPos(0);
	float3 found;

	const CUnit* transportee = (transportees.front()).unit;
	const float radius = c.params[3];
	const float spread = transportee->radius * transport->unitDef->unloadSpread;
	const bool canUnload = FindEmptySpot(pos, radius, spread, found, transportee);

	if (canUnload) {
		Command c2(CMD_UNLOAD_UNIT, c.options | INTERNAL_ORDER, found);
		commandQue.push_front(c2);

		if (isFirstIteration)	{
			Command c1(CMD_MOVE, c.options | INTERNAL_ORDER, pos);
			commandQue.push_front(c1);
			startingDropPos = pos;
		}

		SlowUpdate();
		return;
	}

	FinishCommand();
}
void CTransportCAI::UnloadUnits_Land(Command& c, CTransportUnit* transport)
{
	if (lastCall == gs->frameNum) {
		// avoid infinite loops
		return;
	}

	lastCall = gs->frameNum;

	const std::list<CTransportUnit::TransportedUnit>& transportees = transport->GetTransportedUnits();

	if (transportees.empty()) {
		FinishCommand();
		return;
	}

	CUnit* transportee = NULL;
	float3 unloadPos;

	for (auto it = transportees.begin(); it != transportees.end(); ++it) {
		const float3 pos = c.GetPos(0);
		const float radius = c.params[3];
		const float spread = (it->unit)->radius * transport->unitDef->unloadSpread;

		if (FindEmptySpot(pos, radius, spread, unloadPos, it->unit)) {
			transportee = it->unit; break;
		}
	}

	if (transportee != NULL) {
		Command c2(CMD_UNLOAD_UNIT, c.options | INTERNAL_ORDER, unloadPos);
		c2.PushParam(transportee->id);
		commandQue.push_front(c2);
		SlowUpdate();
	} else {
		FinishCommand();
	}
}
Exemple #22
0
void CBuilderCAI::ExecuteRepair(Command& c)
{
	// not all builders are repair-capable by default
	if (!owner->unitDef->canRepair)
		return;

	CBuilder* builder = (CBuilder*) owner;

	if (c.params.size() == 1 || c.params.size() == 5) {
		// repair unit
		CUnit* unit = uh->GetUnit(c.params[0]);

		if (unit == NULL) {
			FinishCommand();
			return;
		}

		if (tempOrder && owner->moveState == MOVESTATE_MANEUVER) {
			// limit how far away we go
			if (LinePointDist(commandPos1, commandPos2, unit->pos) > 500) {
				StopMove();
				FinishCommand();
				return;
			}
		}

		if (c.params.size() == 5) {
			const float3 pos(c.params[1], c.params[2], c.params[3]);
			const float radius = c.params[4] + 100.0f; // do not walk too far outside repair area

			if ((pos - unit->pos).SqLength2D() > radius * radius ||
				(unit->isMoving && (((c.options & INTERNAL_ORDER) && !TargetInterceptable(unit, unit->speed.Length2D())) || builder->curBuild == unit)
				&& !IsInBuildRange(unit))) {
				StopMove();
				FinishCommand();
				return;
			}
		}

		// do not consider units under construction irreparable
		// even if they can be repaired
		bool canRepairUnit = true;
		canRepairUnit &= ((unit->beingBuilt) || (unit->unitDef->repairable && (unit->health < unit->maxHealth)));
		canRepairUnit &= ((unit != owner) || owner->unitDef->canSelfRepair);
		canRepairUnit &= (!unit->soloBuilder || (unit->soloBuilder == owner));
		canRepairUnit &= (!(c.options & INTERNAL_ORDER) || (c.options & CONTROL_KEY) || !IsUnitBeingReclaimed(unit, owner));
		canRepairUnit &= (UpdateTargetLostTimer(unit->id) != 0);

		if (canRepairUnit) {
			if (MoveInBuildRange(unit)) {
				builder->SetRepairTarget(unit);
			}
		} else {
			StopMove();
			FinishCommand();
		}
	} else if (c.params.size() == 4) {
		// area repair
		const float3 pos = c.GetPos(0);
		const float radius = c.params[3];

		builder->StopBuild();
		if (FindRepairTargetAndRepair(pos, radius, c.options, false, (c.options & META_KEY))) {
			inCommand = false;
			SlowUpdate();
			return;
		}
		if (!(c.options & ALT_KEY)) {
			FinishCommand();
		}
	} else {
		FinishCommand();
	}
	return;
}
Exemple #23
0
void CBuilderCAI::ExecuteReclaim(Command& c)
{
	CBuilder* builder = (CBuilder*) owner;

	// not all builders are reclaim-capable by default
	if (!owner->unitDef->canReclaim)
		return;

	if (c.params.size() == 1 || c.params.size() == 5) {
		const int signedId = (int) c.params[0];

		if (signedId < 0) {
			LOG_L(L_WARNING, "Trying to reclaim unit or feature with id < 0 (%i), aborting.", signedId);
			return;
		}

		const unsigned int uid = signedId;

		//FIXME add a per-unit solution to better balance the load?
		const bool checkForBetterTarget = (gs->frameNum % (5 * UNIT_SLOWUPDATE_RATE)) < UNIT_SLOWUPDATE_RATE;
		if (checkForBetterTarget && (c.options & INTERNAL_ORDER) && (c.params.size() >= 5)) {
			// regular check if there is a closer reclaim target
			CSolidObject* obj;
			if (uid >= uh->MaxUnits()) {
				obj = featureHandler->GetFeature(uid - uh->MaxUnits());
			} else {
				obj = uh->GetUnit(uid);
			}
			if (obj) {
				const float3 pos(c.params[1], c.params[2], c.params[3]);
				const float radius = c.params[4];
				const float curdist = pos.SqDistance2D(obj->pos);

				const bool recUnits = !!(c.options & META_KEY);
				const bool recEnemyOnly = (c.options & META_KEY) && (c.options & CONTROL_KEY);
				const bool recSpecial = !!(c.options & CONTROL_KEY);

				ReclaimOption recopt = REC_NORESCHECK;
				if (recUnits)     recopt |= REC_UNITS;
				if (recEnemyOnly) recopt |= REC_ENEMYONLY;
				if (recSpecial)   recopt |= REC_SPECIAL;
				
				const int rid = FindReclaimTarget(pos, radius, c.options, recopt, curdist);
				if ((rid > 0) && (rid != uid)) {
					FinishCommand();
					RemoveUnitFromReclaimers(owner);
					RemoveUnitFromFeatureReclaimers(owner);
					return;
				}
			}
		}

		if (uid >= uh->MaxUnits()) { // reclaim feature
			CFeature* feature = featureHandler->GetFeature(uid - uh->MaxUnits());

			if (feature != NULL) {
				bool featureBeingResurrected = IsFeatureBeingResurrected(feature->id, owner);
				featureBeingResurrected &= (c.options & INTERNAL_ORDER) && !(c.options & CONTROL_KEY);

				if (featureBeingResurrected || !ReclaimObject(feature)) {
					StopMove();
					FinishCommand();
					RemoveUnitFromFeatureReclaimers(owner);
				} else {
					AddUnitToFeatureReclaimers(owner);
				}
			} else {
				StopMove();
				FinishCommand();
				RemoveUnitFromFeatureReclaimers(owner);
			}

			RemoveUnitFromReclaimers(owner);
		} else { // reclaim unit
			CUnit* unit = uh->GetUnit(uid);

			if (unit != NULL && c.params.size() == 5) {
				const float3 pos(c.params[1], c.params[2], c.params[3]);
				const float radius = c.params[4] + 100.0f; // do not walk too far outside reclaim area

				const bool outOfReclaimRange =
					(pos.SqDistance2D(unit->pos) > radius * radius) ||
					(builder->curReclaim == unit && unit->isMoving && !IsInBuildRange(unit));
				const bool busyAlliedBuilder =
					unit->unitDef->builder &&
					!unit->commandAI->commandQue.empty() &&
					teamHandler->Ally(owner->allyteam, unit->allyteam);

				if (outOfReclaimRange || busyAlliedBuilder) {
					StopMove();
					RemoveUnitFromReclaimers(owner);
					FinishCommand();
					RemoveUnitFromFeatureReclaimers(owner);
					return;
				}
			}

			if (unit != NULL && unit != owner && unit->unitDef->reclaimable && UpdateTargetLostTimer(unit->id) && unit->AllowedReclaim(owner)) {
				if (!ReclaimObject(unit)) {
					StopMove();
					FinishCommand();
				} else {
					AddUnitToReclaimers(owner);
				}
			} else {
				RemoveUnitFromReclaimers(owner);
				FinishCommand();
			}

			RemoveUnitFromFeatureReclaimers(owner);
		}
	} else if (c.params.size() == 4) {
		// area reclaim
		const float3 pos = c.GetPos(0);
		const float radius = c.params[3];
		const bool recUnits = !!(c.options & META_KEY);
		const bool recEnemyOnly = (c.options & META_KEY) && (c.options & CONTROL_KEY);
		const bool recSpecial = !!(c.options & CONTROL_KEY);

		RemoveUnitFromReclaimers(owner);
		RemoveUnitFromFeatureReclaimers(owner);
		builder->StopBuild();

		ReclaimOption recopt = REC_NORESCHECK;
		if (recUnits)     recopt |= REC_UNITS;
		if (recEnemyOnly) recopt |= REC_ENEMYONLY;
		if (recSpecial)   recopt |= REC_SPECIAL;

		if (FindReclaimTargetAndReclaim(pos, radius, c.options, recopt)) {
			inCommand = false;
			SlowUpdate();
			return;
		}
		if(!(c.options & ALT_KEY)){
			FinishCommand();
		}
	} else {
		// wrong number of parameters
		RemoveUnitFromReclaimers(owner);
		RemoveUnitFromFeatureReclaimers(owner);
		FinishCommand();
	}
}
Exemple #24
0
void CBuilderCAI::ExecuteFight(Command& c)
{
	assert((c.options & INTERNAL_ORDER) || owner->unitDef->canFight);
	CBuilder* builder = (CBuilder*) owner;

	if (tempOrder) {
		tempOrder = false;
		inCommand = true;
	}
	if (c.params.size() < 3) { // this shouldnt happen but anyway ...
		LOG_L(L_ERROR,
				"Received a Fight command with less than 3 params on %s in BuilderCAI",
				owner->unitDef->humanName.c_str());
		return;
	}

	if (c.params.size() >= 6) {
		if (!inCommand) {
			commandPos1 = float3(c.params[3], c.params[4], c.params[5]);
		}
	} else {
		// Some hackery to make sure the line (commandPos1,commandPos2) is NOT
		// rotated (only shortened) if we reach this because the previous return
		// fight command finished by the 'if((curPos-pos).SqLength2D()<(64*64)){'
		// condition, but is actually updated correctly if you click somewhere
		// outside the area close to the line (for a new command).
		commandPos1 = ClosestPointOnLine(commandPos1, commandPos2, owner->pos);
		if (f3SqDist(owner->pos, commandPos1) > (96 * 96)) {
			commandPos1 = owner->pos;
		}
	}

	float3 pos = c.GetPos(0);
	if (!inCommand) {
		inCommand = true;
		commandPos2 = pos;
	}

	float3 curPosOnLine = ClosestPointOnLine(commandPos1, commandPos2, owner->pos);
	if (c.params.size() >= 6) {
		pos = curPosOnLine;
	}

	if (pos != goalPos) {
		SetGoal(pos, owner->pos);
	}

	const bool resurrectMode = !!(c.options & ALT_KEY);
	const bool reclaimEnemyMode = !!(c.options & META_KEY);
	const bool reclaimEnemyOnlyMode = (c.options & CONTROL_KEY) && (c.options & META_KEY);

	ReclaimOption recopt;
	if (resurrectMode)        recopt |= REC_NONREZ;
	if (reclaimEnemyMode)     recopt |= REC_ENEMY;
	if (reclaimEnemyOnlyMode) recopt |= REC_ENEMYONLY;

	const float searchRadius = (owner->immobile ? 0 : (300 * owner->moveState)) + builder->buildDistance;

	if (!reclaimEnemyOnlyMode && (owner->unitDef->canRepair || owner->unitDef->canAssist) && // Priority 1: Repair
	    FindRepairTargetAndRepair(curPosOnLine, searchRadius, c.options, true, resurrectMode)){
		tempOrder = true;
		inCommand = false;
		if (lastPC1 != gs->frameNum) {  //avoid infinite loops
			lastPC1 = gs->frameNum;
			SlowUpdate();
		}
		return;
	}
	if (!reclaimEnemyOnlyMode && resurrectMode && owner->unitDef->canResurrect && // Priority 2: Resurrect (optional)
	    FindResurrectableFeatureAndResurrect(curPosOnLine, searchRadius, c.options, false)) {
		tempOrder = true;
		inCommand = false;
		if (lastPC2 != gs->frameNum) {  //avoid infinite loops
			lastPC2 = gs->frameNum;
			SlowUpdate();
		}
		return;
	}
	if (owner->unitDef->canReclaim && // Priority 3: Reclaim / reclaim non resurrectable (optional) / reclaim enemy units (optional)
	    FindReclaimTargetAndReclaim(curPosOnLine, searchRadius, c.options, recopt)) {
		tempOrder = true;
		inCommand = false;
		if (lastPC3 != gs->frameNum) {  //avoid infinite loops
			lastPC3 = gs->frameNum;
			SlowUpdate();
		}
		return;
	}
	if (f3SqDist(owner->pos, pos) < (64*64)) {
		FinishCommand();
		return;
	}
	if(owner->haveTarget && owner->moveType->progressState!=AMoveType::Done){
		StopMove();
	} else if(owner->moveType->progressState!=AMoveType::Active){
		owner->moveType->StartMoving(goalPos, 8);
	}
	return;
}
Exemple #25
0
void CBuilderCAI::GiveCommandReal(const Command& c, bool fromSynced)
{
	if (!AllowedCommand(c, fromSynced))
		return;

	// don't guard yourself
	if ((c.GetID() == CMD_GUARD) &&
	    (c.params.size() == 1) && ((int)c.params[0] == owner->id)) {
		return;
	}

	// stop current build if the new command is a not queued and replaces the current buidcmd
	//FIXME should happen just before CMobileCAI::GiveCommandReal? (the new cmd can still be skipped!)
	if (!(c.options & SHIFT_KEY) && nonQueingCommands.find(c.GetID()) == nonQueingCommands.end()
			&& c.GetID() != CMD_WAIT) {
		building = false;
		((CBuilder*) owner)->StopBuild();
	}

	map<int,string>::iterator boi = buildOptions.find(c.GetID());
	if (boi != buildOptions.end()) {
		if (c.params.size() < 3) {
			return;
		}

		BuildInfo bi;
		bi.pos = c.GetPos(0);

		if (c.params.size() == 4)
			bi.buildFacing = abs((int)c.params[3]) % NUM_FACINGS;

		bi.def = unitDefHandler->GetUnitDefByName(boi->second);
		bi.pos = helper->Pos2BuildPos(bi, true);

		// We are a static building, check if the buildcmd is in range
		if (!owner->unitDef->canmove) {
			const float radius = GetBuildOptionRadius(bi.def, c.GetID());
			if (!IsInBuildRange(bi.pos, radius)) {
				return;
			}
		}

		// check if the buildpos is blocked if it is a nanoframe help to finish it
		//FIXME finish it just if it is of the same unitdef?
		CFeature* feature = NULL;
		if (!uh->TestUnitBuildSquare(bi, feature, owner->allyteam, true)) {
			if (!feature && owner->unitDef->canAssist) {
				const int yardxpos = int(bi.pos.x + 4) / SQUARE_SIZE;
				const int yardypos = int(bi.pos.z + 4) / SQUARE_SIZE;
				const CSolidObject* s = groundBlockingObjectMap->GroundBlocked(yardxpos, yardypos);
				const CUnit* u = dynamic_cast<const CUnit*>(s);
				if (
					   (u != NULL)
					&& u->beingBuilt && (u->buildProgress == 0.0f)
					&& (!u->soloBuilder || (u->soloBuilder == owner))
				) {
					Command c2(CMD_REPAIR, c.options | INTERNAL_ORDER);
					c2.params.push_back(u->id);
					CMobileCAI::GiveCommandReal(c2);
					CMobileCAI::GiveCommandReal(c);
				}
			}
			return;
		}
	}
	CMobileCAI::GiveCommandReal(c);
}
Exemple #26
0
void CSelectedUnitsHandlerAI::SelectAttack(const Command& cmd, int player)
{
	std::vector<int> targets;

	if (cmd.params.size() == 4) {
		SelectCircleUnits(cmd.GetPos(0), cmd.params[3], player, targets);
	} else {
		SelectRectangleUnits(cmd.GetPos(0), cmd.GetPos(3), player, targets);
	}

	if (targets.empty())
		return;

	const bool queueing = !!(cmd.options & SHIFT_KEY);
	const std::vector<int>& selected = selectedUnitsHandler.netSelected[player];

	const unsigned int targetsCount = targets.size();
	const unsigned int selectedCount = selected.size();

	if (selectedCount == 0)
		return;

	Command attackCmd(CMD_ATTACK, cmd.options, 0.0f);

	// delete the attack commands and bail for CONTROL_KEY
	if (cmd.options & CONTROL_KEY) {
		attackCmd.options |= SHIFT_KEY;

		for (unsigned int s = 0; s < selectedCount; s++) {
			const CUnit* unit = unitHandler->GetUnit( selected[s] );

			if (unit == nullptr)
				continue;

			CCommandAI* commandAI = unit->commandAI;

			for (unsigned int t = 0; t < targetsCount; t++) {
				attackCmd.params[0] = targets[t];

				if (commandAI->WillCancelQueued(attackCmd)) {
					commandAI->GiveCommand(attackCmd, false);
				}
			}
		}

		return;
	}

	// get the group center
	float3 midPos;
	unsigned int realCount = 0;
	for (unsigned int s = 0; s < selectedCount; s++) {
		CUnit* unit = unitHandler->GetUnit(selected[s]);

		if (unit == nullptr)
			continue;

		if (queueing) {
			midPos += LastQueuePosition(unit);
		} else {
			midPos += unit->midPos;
		}

		realCount++;
	}

	if (realCount == 0)
		return;

	midPos /= realCount;

	// sort the targets
	std::vector<DistInfo> distVec;

	for (unsigned int t = 0; t < targetsCount; t++) {
		const CUnit* unit = unitHandler->GetUnit( targets[t] );
		const float3 unitPos = float3(unit->midPos);

		DistInfo di;
		di.unitID = targets[t];
		di.dist = (unitPos - midPos).SqLength2D();
		distVec.push_back(di);
	}
	stable_sort(distVec.begin(), distVec.end());

	// give the commands
	for (unsigned int s = 0; s < selectedCount; s++) {
		if (!queueing) {
			// clear it for the first command
			attackCmd.options &= ~SHIFT_KEY;
		}

		CUnit* unit = unitHandler->GetUnit(selected[s]);

		if (unit == nullptr)
			continue;

		CCommandAI* commandAI = unit->commandAI;

		for (unsigned t = 0; t < targetsCount; t++) {
			attackCmd.params[0] = distVec[t].unitID;

			if (!queueing || !commandAI->WillCancelQueued(attackCmd)) {
				commandAI->GiveCommand(attackCmd, false);

				AddUnitSetMaxSpeedCommand(unit, attackCmd.options);
				// following commands are always queued
				attackCmd.options |= SHIFT_KEY;
			}
		}
	}
}
Exemple #27
0
void CSelectedUnitsAI::SelectAttack(const Command& cmd, int player)
{
    std::vector<int> targets;
    const float3& pos0 = cmd.GetPos(0);
    if (cmd.params.size() == 4) {
        SelectCircleUnits(pos0, cmd.params[3], targets, player);
    } else {
        const float3& pos1 = cmd.GetPos(3);
        SelectRectangleUnits(pos0, pos1, targets, player);
    }
    if (targets.empty()) {
        return;
    }
    const int targetsCount = (int)targets.size();

    const std::vector<int>& selected = selectedUnits.netSelected[player];
    const int selectedCount = (int)selected.size();
    if (selectedCount <= 0) {
        return;
    }

    Command attackCmd(CMD_ATTACK, cmd.options, 0.0f);

    // delete the attack commands and bail for CONTROL_KEY
    if (cmd.options & CONTROL_KEY) {
        attackCmd.options |= SHIFT_KEY;
        for (int s = 0; s < selectedCount; s++) {
            const CUnit* unit = uh->units[selected[s]];
            if (unit == NULL) {
                continue;
            }
            CCommandAI* commandAI = uh->units[selected[s]]->commandAI;
            for (int t = 0; t < targetsCount; t++) {
                attackCmd.params[0] = targets[t];
                if (commandAI->WillCancelQueued(attackCmd)) {
                    commandAI->GiveCommand(attackCmd, false);
                }
            }
        }
        return;
    }

    const bool queueing = !!(cmd.options & SHIFT_KEY);

    // get the group center
    float3 midPos(ZeroVector);
    int realCount = 0;
    for (int s = 0; s < selectedCount; s++) {
        CUnit* unit = uh->units[selected[s]];
        if (unit == NULL) {
            continue;
        }
        if (queueing) {
            midPos += LastQueuePosition(unit);
        } else {
            midPos += unit->midPos;
        }
        realCount++;
    }
    if (realCount <= 0) {
        return;
    }
    midPos /= (float)realCount;

    // sort the targets
    std::vector<DistInfo> distVec;
    int t;
    for (t = 0; t < targetsCount; t++) {
        DistInfo di;
        di.unitID = targets[t];
        const CUnit* unit = uh->units[di.unitID];
        const float3 unitPos = queueing ? LastQueuePosition(unit) : float3(unit->midPos);
        di.dist = (unitPos - midPos).SqLength2D();
        distVec.push_back(di);
    }
    sort(distVec.begin(), distVec.end());

    // give the commands
    for (int s = 0; s < selectedCount; s++) {
        if (!queueing) {
            // clear it for the first command
            attackCmd.options &= ~SHIFT_KEY;
        }
        CUnit* unit = uh->units[selected[s]];
        if (unit == NULL) {
            continue;
        }
        CCommandAI* commandAI = unit->commandAI;
        for (t = 0; t < targetsCount; t++) {
            attackCmd.params[0] = distVec[t].unitID;
            if (!queueing || !commandAI->WillCancelQueued(attackCmd)) {
                commandAI->GiveCommand(attackCmd, false);
                AddUnitSetMaxSpeedCommand(unit, attackCmd.options);
                // following commands are always queued
                attackCmd.options |= SHIFT_KEY;
            }
        }
    }
}
Exemple #28
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);
		}
	}
Exemple #29
0
/**
* @brief Executes the Fight command c
*/
void CMobileCAI::ExecuteFight(Command& c)
{
	assert((c.options & INTERNAL_ORDER) || owner->unitDef->canFight);

	if (c.params.size() == 1 && !owner->weapons.empty()) {
		CWeapon* w = owner->weapons.front();

		if ((orderTarget != NULL) && !w->AttackUnit(orderTarget, false)) {
			CUnit* newTarget = CGameHelper::GetClosestValidTarget(owner->pos, owner->maxRange, owner->allyteam, this);

			if ((newTarget != NULL) && w->AttackUnit(newTarget, false)) {
				c.params[0] = newTarget->id;

				inCommand = false;
			} else {
				w->AttackUnit(orderTarget, false);
			}
		}

		ExecuteAttack(c);
		return;
	}

	if (tempOrder) {
		inCommand = true;
		tempOrder = false;
	}
	if (c.params.size() < 3) {
		LOG_L(L_ERROR,
				"Received a Fight command with less than 3 params on %s in MobileCAI",
				owner->unitDef->humanName.c_str());
		return;
	}
	if (c.params.size() >= 6) {
		if (!inCommand) {
			commandPos1 = c.GetPos(3);
		}
	} else {
		// Some hackery to make sure the line (commandPos1,commandPos2) is NOT
		// rotated (only shortened) if we reach this because the previous return
		// fight command finished by the 'if((curPos-pos).SqLength2D()<(64*64)){'
		// condition, but is actually updated correctly if you click somewhere
		// outside the area close to the line (for a new command).
		commandPos1 = ClosestPointOnLine(commandPos1, commandPos2, owner->pos);
		if ((owner->pos - commandPos1).SqLength2D() > (96 * 96)) {
			commandPos1 = owner->pos;
		}
	}

	float3 pos = c.GetPos(0);

	if (!inCommand) {
		inCommand = true;
		commandPos2 = pos;
		lastUserGoal = commandPos2;
	}
	if (c.params.size() >= 6) {
		pos = ClosestPointOnLine(commandPos1, commandPos2, owner->pos);
	}
	if (pos != goalPos) {
		SetGoal(pos, owner->pos);
	}

	if (owner->unitDef->canAttack && owner->fireState >= FIRESTATE_FIREATWILL && !owner->weapons.empty()) {
		const float3 curPosOnLine = ClosestPointOnLine(commandPos1, commandPos2, owner->pos);
		const float searchRadius = owner->maxRange + 100 * owner->moveState * owner->moveState;
		CUnit* enemy = CGameHelper::GetClosestValidTarget(curPosOnLine, searchRadius, owner->allyteam, this);

		if (enemy != NULL) {
			PushOrUpdateReturnFight();

			// make the attack-command inherit <c>'s options
			// NOTE: see AirCAI::ExecuteFight why we do not set INTERNAL_ORDER
			Command c2(CMD_ATTACK, c.options, enemy->id);
			commandQue.push_front(c2);

			inCommand = false;
			tempOrder = true;

			// avoid infinite loops (?)
			if (lastPC != gs->frameNum) {
				lastPC = gs->frameNum;
				SlowUpdate();
			}
			return;
		}
	}

	if ((owner->pos - goalPos).SqLength2D() < (64 * 64)
			|| (owner->moveType->progressState == AMoveType::Failed)){
		FinishCommand();
	}
}
void CTransportCAI::UnloadLandFlood(Command& c)
{
	//land, then release all units at once
	CTransportUnit* transport = static_cast<CTransportUnit*>(owner);
	CUnit* transportee = NULL;

	if (inCommand) {
		if (!owner->script->IsBusy()) {
			FinishCommand();
		}
	} else {
		const std::list<CTransportUnit::TransportedUnit>& transportees = transport->GetTransportedUnits();

		if (transportees.empty()) {
			FinishCommand();
			return;
		}

		if (c.params.size() < 4) {
			transportee = (transportees.front()).unit;
		} else {
			const int unitID = c.params[3];

			for (auto it = transportees.begin(); it != transportees.end(); ++it) {
				CUnit* carried = it->unit;

				if (unitID == carried->id) {
					transportee = carried;
					break;
				}
			}
			if (transportee == NULL) {
				FinishCommand();
				return;
			}
		}

		// move to position
		float3 pos = c.GetPos(0);
		if (isFirstIteration) {
			if (goalPos.SqDistance2D(pos) > 400) {
				SetGoal(startingDropPos, owner->pos);
			}
		}

		if (startingDropPos.SqDistance2D(owner->pos) < Square(owner->unitDef->loadingRadius * 0.9f)) {
			CHoverAirMoveType* am = dynamic_cast<CHoverAirMoveType*>(owner->moveType);

			if (am != NULL) {
				// lower to ground
				startingDropPos.y = CGround::GetHeightAboveWater(startingDropPos.x, startingDropPos.z);
				const float3 wantedPos = startingDropPos + UpVector * transportee->model->height;
				SetGoal(wantedPos, owner->pos);

				am->SetWantedAltitude(1.0f);
				am->SetAllowLanding(true);
				am->maxDrift = 1.0f;

				// when on our way down start animations for unloading gear
				if (isFirstIteration) {
					owner->script->StartUnload();
				}
				isFirstIteration = false;

				// once at ground
				if (owner->pos.y - CGround::GetHeightAboveWater(wantedPos.x, wantedPos.z) < SQUARE_SIZE) {
					// nail it to the ground before it tries jumping up, only to land again...
					am->SetState(am->AIRCRAFT_LANDED);
					// call this so that other animations such as opening doors may be started
					owner->script->TransportDrop((transportees.front()).unit, pos);
					transport->DetachUnitFromAir(transportee, pos);

					FinishCommand();

					if (transportees.empty()) {
						am->SetAllowLanding(true);
						owner->script->EndTransport();
						am->UpdateLanded();
					}
				}
			} else {
				// land transports
				inCommand = true;
				isFirstIteration = false;

				StopMove();
				owner->script->TransportDrop((transportees.front()).unit, pos);
				transport->DetachUnitFromAir(transportee, pos);
				FinishCommand();

				if (transportees.empty()) {
					owner->script->EndTransport();
				}
			}
		}
	}
}