Exemple #1
0
bool CTransportCAI::SpotIsClearIgnoreSelf(float3 pos, CUnit* unitToUnload)
{
	if (!static_cast<CTransportUnit*>(owner)->CanLoadUnloadAtPos(pos, unitToUnload)) {
		return false;
	}

	if (unitToUnload->moveDef && ground->GetSlope(pos.x,pos.z) > unitToUnload->moveDef->maxSlope) {
		return false;
	}

	const std::vector<CUnit*>& units = quadField->GetUnitsExact(pos, std::max(1.0f, math::ceil(unitToUnload->radius / SQUARE_SIZE)) * SQUARE_SIZE);
	CTransportUnit* me = static_cast<CTransportUnit*>(owner);
	for (std::vector<CUnit*>::const_iterator it = units.begin(); it != units.end(); ++it) {
		// check if the units are in the transport
		bool found = false;
		for (std::list<CTransportUnit::TransportedUnit>::const_iterator it2 = me->GetTransportedUnits().begin();
				it2 != me->GetTransportedUnits().end(); ++it2) {
			if (it2->unit == *it) {
				found = true;
				break;
			}
		}
		if (!found && (*it != owner)) {
			return false;
		}
	}

	return true;
}
Exemple #2
0
void CUnitScript::DropUnit(int u)
{
#ifndef _CONSOLE
	CTransportUnit* tu = dynamic_cast<CTransportUnit*>(unit);

	if (tu && uh->units[u]) {
		tu->DetachUnit(uh->units[u]);
	}
#endif
}
Exemple #3
0
void CTransportCAI::UnloadDrop(Command& c)
{
	CTransportUnit* transport = static_cast<CTransportUnit*>(owner);

	// fly over and drop unit
	if (inCommand) {
		if (!owner->script->IsBusy()) {
			FinishCommand();
		}
	} else {
		const std::list<CTransportUnit::TransportedUnit>& 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) > 400) {
			SetGoal(pos, owner->pos);
			lastDropPos = pos;
		}

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

		if (am != NULL) {
			pos.y = ground->GetHeightAboveWater(pos.x, pos.z);
			am->maxDrift = 1;

			// if near target or have past it accidentally- drop unit
			if (owner->pos.SqDistance2D(pos) < 1600 || (((pos - owner->pos).Normalize()).SqDistance(owner->frontdir.Normalize()) > 0.25 && owner->pos.SqDistance2D(pos)< (205*205))) {
				am->dontLand = true;

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

				dropSpots.pop_back();

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

				FinishCommand();
			}
		} else {
			inCommand = true;
			StopMove();
			owner->script->TransportDrop(transportee, pos);
		}
	}
}
Exemple #4
0
void CUnitScript::AttachUnit(int piece, int u)
{
	// -1 is valid, indicates that the unit should be hidden
	if ((piece >= 0) && (!PieceExists(piece))) {
		ShowScriptError("Invalid piecenumber for attach");
		return;
	}

#ifndef _CONSOLE
	CTransportUnit* tu = dynamic_cast<CTransportUnit*>(unit);

	if (tu && uh->units[u]) {
		tu->AttachUnit(uh->units[u], piece);
	}
#endif
}
Exemple #5
0
bool CTransportCAI::FindEmptyDropSpots(float3 startpos, float3 endpos, std::list<float3>& dropSpots)
{
	//should only be used by air

	CTransportUnit* transport = static_cast<CTransportUnit*>(owner);
	//dropSpots.clear();
	float gap = 25.5; // TODO - set tag for this?
	float3 dir = endpos - startpos;
	dir.Normalize();

	float3 nextPos = startpos;
	float3 pos;

	std::list<CTransportUnit::TransportedUnit>::const_iterator ti = transport->GetTransportedUnits().begin();
	dropSpots.push_front(nextPos);

	// first spot
	if (ti != transport->GetTransportedUnits().end()) {
		nextPos += dir * (gap + ti->unit->radius);
		++ti;
	}

	// remaining spots
	if (dynamic_cast<CHoverAirMoveType*>(owner->moveType)) {
		while (ti != transport->GetTransportedUnits().end() && startpos.SqDistance(nextPos) < startpos.SqDistance(endpos)) {
			nextPos += dir * (ti->unit->radius);
			nextPos.y = ground->GetHeightAboveWater(nextPos.x, nextPos.z);

			// check landing spot is ok for landing on
			if (!SpotIsClear(nextPos,ti->unit))
				continue;

			dropSpots.push_front(nextPos);
			nextPos += dir * (gap + ti->unit->radius);
			++ti;
		}
		return true;
	}
	return false;
}
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();
				}
			}
		}
	}
}
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);
			}
		}
	}
}
void CTransportCAI::ExecuteLoadUnits(Command& c)
{
	CTransportUnit* transport = reinterpret_cast<CTransportUnit*>(owner);

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

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

		if (c.options & INTERNAL_ORDER) {
			if (unit->commandAI->commandQue.empty()) {
				if (!LoadStillValid(unit)) {
					FinishCommand();
					return;
				}
			} else {
				Command& currentUnitCommand = unit->commandAI->commandQue[0];

				if ((currentUnitCommand.GetID() == CMD_LOAD_ONTO) && (currentUnitCommand.params.size() == 1) && (int(currentUnitCommand.params[0]) == owner->id)) {
					if ((unit->moveType->progressState == AMoveType::Failed) && (owner->moveType->progressState == AMoveType::Failed)) {
						unit->commandAI->FinishCommand();
						FinishCommand();
						return;
					}
				} else if (!LoadStillValid(unit)) {
					FinishCommand();
					return;
				}
			}
		}

		if (inCommand) {
			if (!owner->script->IsBusy()) {
				FinishCommand();
			}
			return;
		}
		if (unit != NULL && CanTransport(unit) && UpdateTargetLostTimer(int(c.params[0]))) {
			SetTransportee(unit);

			const float sqDist = unit->pos.SqDistance2D(owner->pos);
			const bool inLoadingRadius = (sqDist <= Square(owner->unitDef->loadingRadius));

			CTransportUnit* trans = static_cast<CTransportUnit*>(owner);
			CHoverAirMoveType* am = dynamic_cast<CHoverAirMoveType*>(owner->moveType);

			// subtract 1 square to account for PFS/GMT inaccuracy
			const bool outOfRange = (goalPos.SqDistance2D(unit->pos) > Square(owner->unitDef->loadingRadius - SQUARE_SIZE));
			const bool moveCloser = (!inLoadingRadius && (!owner->IsMoving() || (am != NULL && am->aircraftState != AAirMoveType::AIRCRAFT_FLYING)));

			if (outOfRange || moveCloser) {
				SetGoal(unit->pos, owner->pos, std::min(64.0f, owner->unitDef->loadingRadius));
			}

			if (inLoadingRadius) {
				if (am != NULL) {
					// handle air transports differently
					float3 wantedPos = unit->pos;
					wantedPos.y = trans->GetTransporteeWantedHeight(wantedPos, unit);

					// calls am->StartMoving() which sets forceHeading to false (and also
					// changes aircraftState, possibly in mid-pickup) --> must check that
					// wantedPos == goalPos using some epsilon tolerance
					// we do not want the forceHeading change at point of pickup because
					// am->UpdateHeading() will suddenly notice a large deltaHeading and
					// break the DOCKING_ANGLE constraint so call am->ForceHeading() next
					SetGoal(wantedPos, owner->pos, 1.0f);

					am->ForceHeading(trans->GetTransporteeWantedHeading(unit));
					am->SetWantedAltitude(wantedPos.y - CGround::GetHeightAboveWater(wantedPos.x, wantedPos.z));
					am->maxDrift = 1.0f;

					// FIXME: kill the hardcoded constants, use the command's radius
					const bool b1 = (owner->pos.SqDistance(wantedPos) < Square(AIRTRANSPORT_DOCKING_RADIUS));
					const bool b2 = (std::abs(owner->heading - unit->heading) < AIRTRANSPORT_DOCKING_ANGLE);
					const bool b3 = (owner->updir.dot(UpVector) > 0.995f);

					if (b1 && b2 && b3) {
						am->SetAllowLanding(false);
						am->SetWantedAltitude(0.0f);

						owner->script->BeginTransport(unit);
						SetTransportee(NULL);
						transport->AttachUnit(unit, owner->script->QueryTransport(unit));

						FinishCommand();
						return;
					}
				} else {
					inCommand = true;

					StopMove();
					owner->script->TransportPickup(unit);
				}
			} else if (owner->moveType->progressState == AMoveType::Failed && sqDist < (200 * 200)) {
				// if we're pretty close already but CGroundMoveType fails because it considers
				// the goal clogged (with the future passenger...), just try to move to the
				// point halfway between the transport and the passenger.
				SetGoal((unit->pos + owner->pos) * 0.5f, owner->pos);
			}
		} else {
			FinishCommand();
		}
	} else if (c.params.size() == 4) { // area-load
		if (lastCall == gs->frameNum) { // avoid infinite loops
			return;
		}

		lastCall = gs->frameNum;

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

		CUnit* unit = FindUnitToTransport(pos, radius);

		if (unit && CanTransport(unit)) {
			Command c2(CMD_LOAD_UNITS, c.options|INTERNAL_ORDER, unit->id);
			commandQue.push_front(c2);
			inCommand = false;

			SlowUpdate();
			return;
		} else {
			FinishCommand();
			return;
		}
	}

	isFirstIteration = true;
	startingDropPos = -OnesVector;
}
void CTransportCAI::ExecuteUnloadUnit(Command &c)
{
	CTransportUnit* transport = (CTransportUnit*)owner;
	if (inCommand) {
		if (!owner->cob->busy) {
	//			if(scriptReady)
			FinishCommand();
		}
	}
	else {
		const std::list<CTransportUnit::TransportedUnit>& transList =
		  transport->transported;

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

		float3 pos(c.params[0], c.params[1], c.params[2]);
		if(goalPos.distance2D(pos) > 20){
			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.distance2D(owner->pos) < (owner->unitDef->loadingRadius * 0.9f)) {
			CTAAirMoveType* am = dynamic_cast<CTAAirMoveType*>(owner->moveType);
			if (am != NULL) {
				// handle air transports differently
				pos.y = ground->GetHeight(pos.x, pos.z);
				const float3 wantedPos = pos + UpVector * unit->model->height;
				SetGoal(wantedPos, owner->pos);
				am->SetWantedAltitude(unit->model->height);
				am->maxDrift = 1;
				if ((owner->pos.distance(wantedPos) < 8) &&
				    (owner->updir.dot(UpVector) > 0.99f)) {
					am->dontLand = false;
					owner->cob->Call("EndTransport");
					transport->DetachUnit(unit);
					const float3 fix = owner->pos + owner->frontdir * 20;
					SetGoal(fix, owner->pos);		//move the transport away slightly
					FinishCommand();
				}
			} else {
				inCommand = true;
				scriptReady = false;
				StopMove();
				std::vector<int> args;
				args.push_back(transList.front().unit->id);
				args.push_back(PACKXZ(pos.x, pos.z));
				owner->cob->Call("TransportDrop", args, ScriptCallback, this, 0);
			}
		}
	}
	return;
}
void CTransportCAI::UnloadLandFlood(Command& c) {
	//land, then release all units at once
	CTransportUnit* transport = (CTransportUnit*)owner;
	if (inCommand) {
			if (!owner->cob->busy) {
			  //if(scriptReady)
				FinishCommand();
			}
	} else {
		const std::list<CTransportUnit::TransportedUnit>& transList =
		  transport->transported;

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

		//check units are all carried
		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;
			}
		}

		//move to position
		float3 pos(c.params[0], c.params[1], c.params[2]);
		if (isFirstIteration) {
			if(goalPos.distance2D(pos) > 20)
				SetGoal(startingDropPos, owner->pos);
		}

		if (startingDropPos.distance2D(owner->pos) < (owner->unitDef->loadingRadius * 0.9f)) {
			//create aircraft movetype instance
			CTAAirMoveType* am = dynamic_cast<CTAAirMoveType*>(owner->moveType);

			if (am != NULL) {
				//lower to ground

				startingDropPos.y = ground->GetHeight(startingDropPos.x,startingDropPos.z);
				const float3 wantedPos = startingDropPos + UpVector * unit->model->height;
				SetGoal(wantedPos, owner->pos);
				am->SetWantedAltitude(1);
				am->maxDrift = 1;
				am->dontLand = false;

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

				//once at ground
				if (owner->pos.y - ground->GetHeight(wantedPos.x,wantedPos.z) < 8) {

					am->SetState(am->AIRCRAFT_LANDED);//nail it to the ground before it tries jumping up, only to land again...
					std::vector<int> args;
					args.push_back(transList.front().unit->id);
					args.push_back(PACKXZ(pos.x, pos.z));
					owner->cob->Call("TransportDrop", args, ScriptCallback, this, 0); //call this so that other animations such as opening doors may be started
					transport->DetachUnitFromAir(unit,pos);

					FinishCommand();
					if (transport->transported.empty()) {
						am->dontLand = false;
						owner->cob->Call("EndTransport");
						am->UpdateLanded();
					}
				}
			} else {

				//land transports
				inCommand = true;
				scriptReady = false;
				StopMove();
				std::vector<int> args;
				args.push_back(transList.front().unit->id);
				args.push_back(PACKXZ(pos.x, pos.z));
				owner->cob->Call("TransportDrop", args, ScriptCallback, this, 0);
				transport->DetachUnitFromAir(unit,pos);
				isFirstIteration = false;
				FinishCommand();
				if (transport->transported.empty())
					owner->cob->Call("EndTransport");
			}
		}
	}
	return;
}
Exemple #11
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);
			}
		}
	}
}
Exemple #12
0
void CTransportCAI::ExecuteLoadUnits(Command& c)
{
	CTransportUnit* transport = reinterpret_cast<CTransportUnit*>(owner);

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

		if (!unit) {
			FinishCommand();
			return;
		}
		if (c.options & INTERNAL_ORDER) {
			if (unit->commandAI->commandQue.empty()) {
				if (!LoadStillValid(unit)) {
					FinishCommand();
					return;
				}
			} else {
				Command& currentUnitCommand = unit->commandAI->commandQue[0];
				if ((currentUnitCommand.GetID() == CMD_LOAD_ONTO) && (currentUnitCommand.params.size() == 1) && (int(currentUnitCommand.params[0]) == owner->id)) {
					if ((unit->moveType->progressState == AMoveType::Failed) && (owner->moveType->progressState == AMoveType::Failed)) {
						unit->commandAI->FinishCommand();
						FinishCommand();
						return;
					}
				} else if (!LoadStillValid(unit)) {
					FinishCommand();
					return;
				}
			}
		}
		if (inCommand) {
			if (!owner->script->IsBusy()) {
				FinishCommand();
			}
			return;
		}
		if (unit && CanTransport(unit) && UpdateTargetLostTimer(int(c.params[0]))) {
			SetTransportee(unit);

			const float sqDist = unit->pos.SqDistance2D(owner->pos);
			const bool inLoadingRadius = (sqDist <= Square(owner->unitDef->loadingRadius));
			
			CHoverAirMoveType* am = dynamic_cast<CHoverAirMoveType*>(owner->moveType);

			// subtracting 1 square to account for pathfinder/groundmovetype inaccuracy
			if (goalPos.SqDistance2D(unit->pos) > Square(owner->unitDef->loadingRadius - SQUARE_SIZE) || 
				(!inLoadingRadius && (!owner->isMoving || (am && am->aircraftState != AAirMoveType::AIRCRAFT_FLYING)))) {
				SetGoal(unit->pos, owner->pos, std::min(64.0f, owner->unitDef->loadingRadius));
			}

			if (inLoadingRadius) {
				if (am) { // handle air transports differently
					float3 wantedPos = unit->pos;
					wantedPos.y = static_cast<CTransportUnit*>(owner)->GetLoadUnloadHeight(wantedPos, unit);
					SetGoal(wantedPos, owner->pos);

					am->loadingUnits = true;
					am->ForceHeading(static_cast<CTransportUnit*>(owner)->GetLoadUnloadHeading(unit));
					am->SetWantedAltitude(wantedPos.y - ground->GetHeightAboveWater(wantedPos.x, wantedPos.z));
					am->maxDrift = 1;

					if ((owner->pos.SqDistance(wantedPos) < Square(AIRTRANSPORT_DOCKING_RADIUS)) &&
						(abs(owner->heading-unit->heading) < AIRTRANSPORT_DOCKING_ANGLE) &&
						(owner->updir.dot(UpVector) > 0.995f))
					{
						am->loadingUnits = false;
						am->dontLand = true;

						owner->script->BeginTransport(unit);
						SetTransportee(NULL);
						transport->AttachUnit(unit, owner->script->QueryTransport(unit));
						am->SetWantedAltitude(0);

						FinishCommand();
						return;
					}
				} else {
					inCommand = true;

					StopMove();
					owner->script->TransportPickup(unit);
				}
			} else if (owner->moveType->progressState == AMoveType::Failed && sqDist < (200 * 200)) {
				// if we're pretty close already but CGroundMoveType fails because it considers
				// the goal clogged (with the future passenger...), just try to move to the
				// point halfway between the transport and the passenger.
				SetGoal((unit->pos + owner->pos) * 0.5f, owner->pos);
			}
		} else {
			FinishCommand();
		}
	} else if (c.params.size() == 4) { // area-load
		if (lastCall == gs->frameNum) { // avoid infinite loops
			return;
		}

		lastCall = gs->frameNum;

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

		CUnit* unit = FindUnitToTransport(pos, radius);

		if (unit && CanTransport(unit)) {
			Command c2(CMD_LOAD_UNITS, c.options|INTERNAL_ORDER, unit->id);
			commandQue.push_front(c2);
			inCommand = false;

			SlowUpdate();
			return;
		} else {
			FinishCommand();
			return;
		}
	}

	isFirstIteration = true;
	startingDropPos = float3(-1.0f, -1.0f, -1.0f);
}