int CTransportCAI::GetDefaultCmd(CUnit* pointed, CFeature* feature) { if (pointed) { if (!gs->Ally(gu->myAllyTeam, pointed->allyteam)) { if (owner->unitDef->canAttack) { return CMD_ATTACK; } else if (CanTransport(pointed)) { return CMD_LOAD_UNITS; // comm napping? } } else { if (CanTransport(pointed)) { return CMD_LOAD_UNITS; } else if (owner->unitDef->canGuard) { return CMD_GUARD; } } } // if(((CTransportUnit*)owner)->transported.empty()) if (owner->unitDef->canmove) { return CMD_MOVE; } else { return CMD_STOP; } // else // return CMD_UNLOAD_UNITS; }
CUnit* CTransportCAI::FindUnitToTransport(float3 center, float radius) { CUnit* bestUnit = NULL; float bestDist = std::numeric_limits<float>::max(); const std::vector<CUnit*>& units = quadField->GetUnitsExact(center, radius); for (std::vector<CUnit*>::const_iterator ui = units.begin(); ui != units.end(); ++ui) { CUnit* unit = (*ui); float dist = unit->pos.SqDistance2D(owner->pos); if (unit->loadingTransportId != -1 && unit->loadingTransportId != owner->id) { CUnit* trans = unitHandler->GetUnitUnsafe(unit->loadingTransportId); if ((trans != NULL) && teamHandler->AlliedTeams(owner->team, trans->team)) { continue; // don't refuse to load comm only because the enemy is trying to nap it at the same time } } if (dist >= bestDist) continue; if (!CanTransport(unit)) continue; if (unit->losStatus[owner->allyteam] & (LOS_INRADAR | LOS_INLOS)) { bestDist = dist; bestUnit = unit; } } return bestUnit; }
void CTransportUnit::AttachUnit(CUnit* unit, int piece) { DetachUnit(unit); if (!CanTransport(unit)) return; AddDeathDependence(unit); unit->AddDeathDependence (this); unit->transporter = this; unit->toBeTransported=false; if (!unitDef->isfireplatform) { unit->stunned=true; //make sure unit doesnt fire etc in transport selectedUnits.RemoveUnit(unit); } unit->UnBlock(); loshandler->FreeInstance(unit->los); unit->los=0; if (dynamic_cast<CTAAirMoveType*>(moveType)) { unit->moveType->useHeading=false; } TransportedUnit tu; tu.unit=unit; tu.piece=piece; tu.size=unit->xsize/2; tu.mass=unit->mass; transportCapacityUsed+=tu.size; transportMassUsed+=tu.mass; transported.push_back(tu); unit->CalculateTerrainType(); unit->UpdateTerrainType(); luaCallIns.UnitLoaded(unit, this); }
int CTransportCAI::GetDefaultCmd(const CUnit* pointed, const CFeature* feature) { if (pointed) { if (!teamHandler->Ally(gu->myAllyTeam, pointed->allyteam)) { if (owner->unitDef->canAttack) { return CMD_ATTACK; } else if (CanTransport(pointed)) { return CMD_LOAD_UNITS; // comm napping? } } else { if (CanTransport(pointed)) { return CMD_LOAD_UNITS; } else if (owner->unitDef->canGuard) { return CMD_GUARD; } } } if (owner->unitDef->canmove) { return CMD_MOVE; } return CMD_STOP; }
CUnit* CTransportCAI::FindUnitToTransport(float3 center, float radius) { CUnit* best=0; float bestDist=100000000; std::vector<CUnit*> units=qf->GetUnitsExact(center,radius); for(std::vector<CUnit*>::iterator ui=units.begin();ui!=units.end();++ui){ CUnit* unit=(*ui); float dist=unit->pos.distance2D(owner->pos); if(CanTransport(unit) && dist<bestDist && !unit->toBeTransported){ bestDist=dist; best=unit; } } return best; }
int CTransportCAI::GetDefaultCmd(CUnit* pointed,CFeature* feature) { if(pointed){ if(!gs->Ally(gu->myAllyTeam,pointed->allyteam)){ return CMD_ATTACK; } else { if(CanTransport(pointed)) return CMD_LOAD_UNITS; else return CMD_GUARD; } } // if(((CTransportUnit*)owner)->transported.empty()) return CMD_MOVE; // else // return CMD_UNLOAD_UNITS; }
/** ** Wait for transporter. ** ** @param unit Pointer to unit. ** ** @return True if ship arrived/present, False otherwise. */ static int WaitForTransporter(CUnit *unit) { CUnit *trans; if (unit->Wait) { unit->Wait--; return 0; } trans = unit->Orders[0]->Goal; if (!trans || !CanTransport(trans, unit)) { // FIXME: destination destroyed?? unit->Wait = 6; return 0; } if (!trans->IsVisibleAsGoal(unit->Player)) { DebugPrint("Transporter Gone\n"); trans->RefsDecrease(); unit->Orders[0]->Goal = NoUnitP; unit->Wait = 6; return 0; } if (MapDistanceBetweenUnits(unit, trans) == 1) { // enter transporter return 1; } // // FIXME: any enemies in range attack them, while waiting. // // n0b0dy: This means we have to search with a smaller range. // It happens only when you reach the shore,and the transporter // is not there. The unit searches with a big range, so it thinks // it's there. This is why we reset the search. The transporter // should be a lot closer now, so it's not as bad as it seems. unit->SubAction = 0; unit->Orders[0]->Range = 1; // Uhh wait a bit. unit->Wait = 10; return 0; }
/** ** Wait for transporter. ** ** @param unit Pointer to unit. ** ** @return True if ship arrived/present, False otherwise. */ bool COrder_Board::WaitForTransporter(CUnit &unit) { if (unit.Wait) { unit.Wait--; return false; } const CUnit *trans = this->GetGoal(); if (!trans || !CanTransport(*trans, unit)) { // FIXME: destination destroyed?? unit.Wait = 6; return false; } if (!trans->IsVisibleAsGoal(*unit.Player)) { DebugPrint("Transporter Gone\n"); this->ClearGoal(); unit.Wait = 6; return false; } if (unit.MapDistanceTo(*trans) == 1) { // enter transporter return true; } // FIXME: any enemies in range attack them, while waiting. // n0b0dy: This means we have to search with a smaller range. // It happens only when you reach the shore,and the transporter // is not there. The unit searches with a big range, so it thinks // it's there. This is why we reset the search. The transporter // should be a lot closer now, so it's not as bad as it seems. this->State = State_Init; this->Range = 1; // Uhh wait a bit. unit.Wait = 10; return false; }
/** ** Load all unit before attack. ** ** @param aiForce force to group. */ static void AiGroupAttackerForTransport(AiForce &aiForce) { Assert(aiForce.State == AiForceAttackingState_Boarding); unsigned int nbToTransport = 0; unsigned int transporterIndex = 0; bool forceIsReady = true; for (; transporterIndex < aiForce.Size(); ++transporterIndex) { const CUnit &unit = *aiForce.Units[transporterIndex]; if (unit.Type->CanTransport() && unit.Type->MaxOnBoard - unit.BoardCount > 0) { nbToTransport = unit.Type->MaxOnBoard - unit.BoardCount; break; } } if (transporterIndex == aiForce.Size()) { aiForce.State = AiForceAttackingState_AttackingWithTransporter; return ; } for (unsigned int i = 0; i < aiForce.Size(); ++i) { const CUnit &unit = *aiForce.Units[i]; const CUnit &transporter = *aiForce.Units[transporterIndex]; if (CanTransport(transporter, unit) && unit.Container == NULL) { forceIsReady = false; break; } } if (forceIsReady == true) { aiForce.State = AiForceAttackingState_AttackingWithTransporter; return ; } for (unsigned int i = 0; i < aiForce.Size(); ++i) { CUnit &unit = *aiForce.Units[i]; CUnit &transporter = *aiForce.Units[transporterIndex]; if (unit.CurrentAction() == UnitActionBoard && static_cast<COrder_Board *>(unit.CurrentOrder())->GetGoal() == &transporter) { CommandFollow(transporter, unit, 0); } if (CanTransport(transporter, unit) && (unit.IsIdle() || (unit.CurrentAction() == UnitActionBoard && !unit.Moving && static_cast<COrder_Board *>(unit.CurrentOrder())->GetGoal() != &transporter)) && unit.Container == NULL) { CommandBoard(unit, transporter, FlushCommands); CommandFollow(transporter, unit, 0); if (--nbToTransport == 0) { // full : next transporter. for (++transporterIndex; transporterIndex < aiForce.Size(); ++transporterIndex) { const CUnit &nextTransporter = *aiForce.Units[transporterIndex]; if (nextTransporter.Type->CanTransport()) { nbToTransport = nextTransporter.Type->MaxOnBoard - nextTransporter.BoardCount; break ; } } if (transporterIndex == aiForce.Size()) { // No more transporter. break ; } } } } }
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::SlowUpdate(void) { if(commandQue.empty()){ CMobileCAI::SlowUpdate(); return; } CTransportUnit* transport=(CTransportUnit*)owner; Command& c=commandQue.front(); switch(c.id){ case CMD_LOAD_UNITS: if(c.params.size()==1){ //load single unit if(transport->transportCapacityUsed >= owner->unitDef->transportCapacity){ FinishCommand(); return; } if(inCommand){ if(!owner->cob->busy) FinishCommand(); return; } CUnit* unit=uh->units[(int)c.params[0]]; if(unit && CanTransport(unit)){ toBeTransportedUnitId=unit->id; unit->toBeTransported=true; if(unit->mass+transport->transportMassUsed > owner->unitDef->transportMass){ FinishCommand(); return; } if(goalPos.distance2D(unit->pos)>10){ float3 fix = unit->pos; SetGoal(fix,owner->pos,64); } if(unit->pos.distance2D(owner->pos)<owner->unitDef->loadingRadius*0.9){ if(CTAAirMoveType* am=dynamic_cast<CTAAirMoveType*>(owner->moveType)){ //handle air transports differently float3 wantedPos=unit->pos+UpVector*unit->model->height; SetGoal(wantedPos,owner->pos); am->dontCheckCol=true; am->ForceHeading(unit->heading); am->SetWantedAltitude(unit->model->height); am->maxDrift=1; //info->AddLine("cai dist %f %f %f",owner->pos.distance(wantedPos),owner->pos.distance2D(wantedPos),owner->pos.y-wantedPos.y); if(owner->pos.distance(wantedPos)<4 && abs(owner->heading-unit->heading)<50 && owner->updir.dot(UpVector)>0.995){ am->dontCheckCol=false; am->dontLand=true; std::vector<int> args; args.push_back((int)(unit->model->height*65536)); owner->cob->Call("BeginTransport",args); std::vector<int> args2; args2.push_back(0); args2.push_back((int)(unit->model->height*65536)); owner->cob->Call("QueryTransport",args2); ((CTransportUnit*)owner)->AttachUnit(unit,args2[0]); am->SetWantedAltitude(0); FinishCommand(); return; } } else { inCommand=true; scriptReady=false; StopMove(); std::vector<int> args; args.push_back(unit->id); owner->cob->Call("TransportPickup",args,ScriptCallback,this,0); } } } else { FinishCommand(); } } else if(c.params.size()==4){ //load in radius if(lastCall==gs->frameNum) //avoid infinite loops return; lastCall=gs->frameNum; float3 pos(c.params[0],c.params[1],c.params[2]); float radius=c.params[3]; CUnit* unit=FindUnitToTransport(pos,radius); if(unit && ((CTransportUnit*)owner)->transportCapacityUsed < owner->unitDef->transportCapacity){ Command c2; c2.id=CMD_LOAD_UNITS; c2.params.push_back(unit->id); c2.options=c.options | INTERNAL_ORDER; commandQue.push_front(c2); inCommand=false; SlowUpdate(); return; } else { FinishCommand(); return; } } break; case CMD_UNLOAD_UNITS:{ if(lastCall==gs->frameNum) //avoid infinite loops return; lastCall=gs->frameNum; if(((CTransportUnit*)owner)->transported.empty()){ FinishCommand(); return; } float3 pos(c.params[0],c.params[1],c.params[2]); float radius=c.params[3]; float3 found; bool canUnload=FindEmptySpot(pos,max(16.0f,radius),((CTransportUnit*)owner)->transported.front().unit->radius,found); if(canUnload){ Command c2; c2.id=CMD_UNLOAD_UNIT; c2.params.push_back(found.x); c2.params.push_back(found.y); c2.params.push_back(found.z); c2.options=c.options | INTERNAL_ORDER; commandQue.push_front(c2); SlowUpdate(); return; } else { FinishCommand(); } break;} case CMD_UNLOAD_UNIT: if(inCommand){ if(!owner->cob->busy) // if(scriptReady) FinishCommand(); } else { if(((CTransportUnit*)owner)->transported.empty()){ FinishCommand(); return; } float3 pos(c.params[0],c.params[1],c.params[2]); if(goalPos.distance2D(pos)>20){ SetGoal(pos,owner->pos); } if(pos.distance2D(owner->pos)<owner->unitDef->loadingRadius*0.9){ if(CTAAirMoveType* am=dynamic_cast<CTAAirMoveType*>(owner->moveType)){ //handle air transports differently pos.y=ground->GetHeight(pos.x,pos.z); CUnit* unit=((CTransportUnit*)owner)->transported.front().unit; 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.99){ am->dontLand=false; owner->cob->Call("EndTransport"); ((CTransportUnit*)owner)->DetachUnit(unit); 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(((CTransportUnit*)owner)->transported.front().unit->id); args.push_back(PACKXZ(pos.x, pos.z)); owner->cob->Call("TransportDrop",args,ScriptCallback,this,0); } } } break; default: CMobileCAI::SlowUpdate(); break; } }
void CTransportUnit::AttachUnit(CUnit* unit, int piece) { assert(unit != this); if (unit->transporter == this) { // assume we are already transporting this unit, // and just want to move it to a different piece // with script logic (this means the UnitLoaded // event is only sent once) std::list<TransportedUnit>::iterator transporteesIt; for (transporteesIt = transportedUnits.begin(); transporteesIt != transportedUnits.end(); ++transporteesIt) { TransportedUnit& tu = *transporteesIt; if (tu.unit == unit) { tu.piece = piece; break; } } return; } else { // handle transfers from another transport to us // (can still fail depending on CanTransport()) if (unit->transporter != NULL) { unit->transporter->DetachUnit(unit); } } // covers the case where unit->transporter != NULL if (!CanTransport(unit)) { return; } AddDeathDependence(unit, DEPENDENCE_TRANSPORTEE); unit->AddDeathDependence(this, DEPENDENCE_TRANSPORTER); unit->transporter = this; unit->toBeTransported = false; unit->stunned = !unitDef->isFirePlatform; if (unit->stunned) { // make sure unit does not fire etc in transport selectedUnits.RemoveUnit(unit); } unit->UnBlock(); loshandler->FreeInstance(unit->los); radarhandler->RemoveUnit(unit); // do not remove unit from QF, otherwise projectiles // will not be able to connect with (ie. damage) it // // for NON-stunned transportees, QF position is kept // up-to-date by MoveType::SlowUpdate, otherwise by // ::Update // // qf->RemoveUnit(unit); unit->los = NULL; if (CBuilding* building = dynamic_cast<CBuilding*>(unit)) { unitLoader->RestoreGround(unit); groundDecals->RemoveBuilding(building, NULL); } if (dynamic_cast<CHoverAirMoveType*>(moveType)) { unit->moveType->useHeading = false; } TransportedUnit tu; tu.unit = unit; tu.piece = piece; tu.size = unit->xsize / 2; tu.mass = unit->mass; transportCapacityUsed += tu.size; transportMassUsed += tu.mass; transportedUnits.push_back(tu); unit->CalculateTerrainType(); unit->UpdateTerrainType(); eventHandler.UnitLoaded(unit, this); commandAI->BuggerOff(pos, -1.0f); // make sure not to get buggered off :) by transportee }
void CTransportCAI::ExecuteLoadUnits(Command &c) { CTransportUnit* transport=(CTransportUnit*)owner; if(c.params.size()==1){ //load single unit if(transport->transportCapacityUsed >= owner->unitDef->transportCapacity){ FinishCommand(); return; } CUnit* unit=uh->units[(int)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.id == CMD_LOAD_ONTO && currentUnitCommand.params.size() == 1 && int(currentUnitCommand.params[0]) == owner->id){ if((unit->moveType->progressState == CMoveType::Failed) && (owner->moveType->progressState == CMoveType::Failed)){ unit->commandAI->FinishCommand(); FinishCommand(); return; } } else if(!LoadStillValid(unit)) { FinishCommand(); return; } } } if(inCommand){ if(!owner->cob->busy) FinishCommand(); return; } if(unit && CanTransport(unit) && UpdateTargetLostTimer(int(c.params[0]))){ toBeTransportedUnitId=unit->id; unit->toBeTransported=true; if(unit->mass+transport->transportMassUsed > owner->unitDef->transportMass){ FinishCommand(); return; } if(goalPos.distance2D(unit->pos)>10){ float3 fix = unit->pos; SetGoal(fix,owner->pos,64); } if(unit->pos.distance2D(owner->pos)<owner->unitDef->loadingRadius*0.9f){ if(CTAAirMoveType* am=dynamic_cast<CTAAirMoveType*>(owner->moveType)){ //handle air transports differently float3 wantedPos=unit->pos+UpVector*unit->model->height; SetGoal(wantedPos,owner->pos); am->dontCheckCol=true; am->ForceHeading(unit->heading); am->SetWantedAltitude(unit->model->height); am->maxDrift=1; //logOutput.Print("cai dist %f %f %f",owner->pos.distance(wantedPos),owner->pos.distance2D(wantedPos),owner->pos.y-wantedPos.y); if(owner->pos.distance(wantedPos)<4 && abs(owner->heading-unit->heading)<50 && owner->updir.dot(UpVector)>0.995f){ am->dontCheckCol=false; am->dontLand=true; std::vector<int> args; args.push_back((int)(unit->model->height*65536)); owner->cob->Call("BeginTransport",args); std::vector<int> args2; args2.push_back(0); args2.push_back((int)(unit->model->height*65536)); owner->cob->Call("QueryTransport",args2); ((CTransportUnit*)owner)->AttachUnit(unit,args2[0]); am->SetWantedAltitude(0); FinishCommand(); return; } } else { inCommand=true; scriptReady=false; StopMove(); std::vector<int> args; args.push_back(unit->id); owner->cob->Call("TransportPickup",args,ScriptCallback,this,0); } } } else { FinishCommand(); } } else if(c.params.size()==4){ //load in radius if(lastCall==gs->frameNum) //avoid infinite loops return; lastCall=gs->frameNum; float3 pos(c.params[0],c.params[1],c.params[2]); float radius=c.params[3]; CUnit* unit=FindUnitToTransport(pos,radius); if(unit && ((CTransportUnit*)owner)->transportCapacityUsed < owner->unitDef->transportCapacity){ Command c2; c2.id=CMD_LOAD_UNITS; c2.params.push_back(unit->id); c2.options=c.options | INTERNAL_ORDER; commandQue.push_front(c2); inCommand=false; SlowUpdate(); return; } else { FinishCommand(); return; } } return; }
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); }