void CTransportCAI::UnloadLand(Command& c)
{
	// default unload
	CTransportUnit* transport = static_cast<CTransportUnit*>(owner);
	CUnit* transportee = NULL;

	float3 wantedPos = c.GetPos(0);

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

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

		SetGoal(wantedPos, owner->pos);

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

			// unload a specific transportee
			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;
			}
		}

		if (wantedPos.SqDistance2D(owner->pos) < Square(owner->unitDef->loadingRadius * 0.9f)) {
			CHoverAirMoveType* am = dynamic_cast<CHoverAirMoveType*>(owner->moveType);
			wantedPos.y = static_cast<CTransportUnit*>(owner)->GetTransporteeWantedHeight(wantedPos, transportee);

			if (am != NULL) {
				// handle air transports differently
				SetGoal(wantedPos, owner->pos);

				am->SetWantedAltitude(wantedPos.y - CGround::GetHeightAboveWater(wantedPos.x, wantedPos.z));
				am->ForceHeading(static_cast<CTransportUnit*>(owner)->GetTransporteeWantedHeading(transportee));

				am->maxDrift = 1.0f;

				// FIXME: kill the hardcoded constants, use the command's radius
				// NOTE: 2D distance-check would mean units get dropped from air
				const bool b1 = (owner->pos.SqDistance(wantedPos) < Square(AIRTRANSPORT_DOCKING_RADIUS));
				const bool b2 = (std::abs(owner->heading - am->GetForcedHeading()) < AIRTRANSPORT_DOCKING_ANGLE);
				const bool b3 = (owner->updir.dot(UpVector) > 0.99f);

				if (b1 && b2 && b3) {
					wantedPos.y -= transportee->radius;

					if (!SpotIsClearIgnoreSelf(wantedPos, transportee)) {
						// chosen spot is no longer clear to land, choose a new one
						// if a new spot cannot be found, don't unload at all
						float3 newWantedPos;

						if (FindEmptySpot(wantedPos, std::max(16.0f * SQUARE_SIZE, transportee->radius * 4.0f), transportee->radius, newWantedPos, transportee)) {
							c.SetPos(0, newWantedPos);
							SetGoal(newWantedPos + UpVector * transportee->model->height, owner->pos);
							return;
						}
					} else {
						transport->DetachUnit(transportee);

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

					// move the transport away slightly
					SetGoal(owner->pos + owner->frontdir * 20.0f, owner->pos);
					FinishCommand();
				}
			} else {
				inCommand = true;
				StopMove();
				owner->script->TransportDrop(transportee, wantedPos);
			}
		}
	}
}
Exemple #2
0
void CTransportCAI::UnloadLand(Command& c)
{
	//default unload
	CTransportUnit* transport = static_cast<CTransportUnit*>(owner);
	if (inCommand) {
		if (!owner->script->IsBusy()) {
			FinishCommand();
		}
	} else {
		const std::list<CTransportUnit::TransportedUnit>& transList = transport->GetTransportedUnits();

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

		float3 pos = c.GetPos(0);
		if (goalPos.SqDistance2D(pos) > 400) {
			SetGoal(pos, owner->pos);
		}

		CUnit* unit = NULL;
		if (c.params.size() < 4) {
			unit = transList.front().unit;
		} else {
			const int unitID = (int)c.params[3];
			std::list<CTransportUnit::TransportedUnit>::const_iterator it;
			for (it = transList.begin(); it != transList.end(); ++it) {
				CUnit* carried = it->unit;
				if (unitID == carried->id) {
					unit = carried;
					break;
				}
			}
			if (unit == NULL) {
				FinishCommand();
				return;
			}
		}

		if (pos.SqDistance2D(owner->pos) < Square(owner->unitDef->loadingRadius * 0.9f)) {
			CHoverAirMoveType* am = dynamic_cast<CHoverAirMoveType*>(owner->moveType);
			pos.y = static_cast<CTransportUnit*>(owner)->GetLoadUnloadHeight(pos, unit);
			if (am != NULL) {
				// handle air transports differently
				SetGoal(pos, owner->pos);
				am->SetWantedAltitude(pos.y - ground->GetHeightAboveWater(pos.x, pos.z));
				float unloadHeading = static_cast<CTransportUnit*>(owner)->GetLoadUnloadHeading(unit);
				am->ForceHeading(unloadHeading);
				am->maxDrift = 1;
				if ((owner->pos.SqDistance(pos) < 64) &&
						(owner->updir.dot(UpVector) > 0.99f) && math::fabs(owner->heading - unloadHeading) < AIRTRANSPORT_DOCKING_ANGLE) {
					pos.y -= unit->radius;
					if (!SpotIsClearIgnoreSelf(pos, unit)) {
						// chosen spot is no longer clear to land, choose a new one
						// if a new spot cannot be found, don't unload at all
						float3 newpos;
						if (FindEmptySpot(pos, std::max(16.0f * SQUARE_SIZE, unit->radius * 4.0f), unit->radius, newpos, unit)) {
							c.SetPos(0, newpos);
							SetGoal(newpos + UpVector * unit->model->height, owner->pos);
							return;
						}
					} else {
						transport->DetachUnit(unit);
						if (transport->GetTransportedUnits().empty()) {
							am->dontLand = false;
							owner->script->EndTransport();
						}
					}
					const float3 fix = owner->pos + owner->frontdir * 20;
					SetGoal(fix, owner->pos); // move the transport away slightly
					FinishCommand();
				}
			} else {
				inCommand = true;
				StopMove();
				owner->script->TransportDrop(unit, pos);
			}
		}
	}
}