void CTransportCAI::ExecuteUnloadUnits(Command &c) { 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; CUnit* unitToUnload=((CTransportUnit*)owner)->transported.front().unit; bool canUnload=FindEmptySpot(pos,max(16.0f,radius),unitToUnload->radius,found,unitToUnload); 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(); } return; }
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; }
void CTransportCAI::UnloadUnits_LandFlood(Command& c, CTransportUnit* transport) { 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; //((CTransportUnit*)owner)->transported bool canUnload=FindEmptySpot(pos,max(16.0f,radius),((CTransportUnit*)owner)->transported.front().unit->radius * ((CTransportUnit*)owner)->unitDef->unloadSpread, found,((CTransportUnit*)owner)->transported.front().unit); 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); if (isFirstIteration ) { Command c1; c1.id=CMD_MOVE; c1.params.push_back(pos.x); c1.params.push_back(pos.y); c1.params.push_back(pos.z); c1.options=c.options | INTERNAL_ORDER; commandQue.push_front(c1); startingDropPos = pos; } SlowUpdate(); return; } else { FinishCommand(); } return; }
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; 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_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(); } }
void CTransportCAI::UnloadUnits_Land(Command& c, CTransportUnit* transport) { if (lastCall == gs->frameNum) { // avoid infinite loops return; } lastCall = gs->frameNum; if (((CTransportUnit*) owner)->transported.empty()) { FinishCommand(); return; } CUnit* u = ((CTransportUnit*) owner)->transported.front().unit; float3 pos(c.params[0],c.params[1],c.params[2]); float radius = c.params[3]; float spread = u->radius * ((CTransportUnit*) owner)->unitDef->unloadSpread; float3 found; bool canUnload = FindEmptySpot(pos, max(16.0f, radius), spread, found, u); 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(); } return; }
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::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 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); } } } }