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; } } }
void CBuilderCAI::ExecuteRepair(Command& c) { CBuilder* fac=(CBuilder*)owner; assert(owner->unitDef->canRepair || owner->unitDef->canAssist); if (c.params.size() == 1) { //repair unit CUnit* unit = uh->units[(int)c.params[0]]; if (tempOrder && owner->moveState == 1) { //limit how far away we go if (unit && LinePointDist(commandPos1, commandPos2, unit->pos) > 500) { StopMove(); FinishCommand(); return; } } if (unit && (unit->health < unit->maxHealth) && ((unit != owner) || owner->unitDef->canSelfRepair) && (!unit->soloBuilder || (unit->soloBuilder == owner)) && UpdateTargetLostTimer((int)c.params[0])) { if (f3SqDist(unit->pos, fac->pos) < Square(fac->buildDistance+unit->radius-8)) { StopMove(); fac->SetRepairTarget(unit); owner->moveType->KeepPointingTo(unit->pos, fac->buildDistance*0.9f+unit->radius, false); } else { if (f3SqDist(goalPos, unit->pos) > 1) { SetGoal(unit->pos,owner->pos, fac->buildDistance*0.9f+unit->radius); } } } else { StopMove(); FinishCommand(); } } else { // repair area float3 pos(c.params[0], c.params[1], c.params[2]); float radius=c.params[3]; if (FindRepairTargetAndRepair(pos, radius, c.options, false)) { inCommand=false; SlowUpdate(); return; } if (!(c.options & ALT_KEY)) { FinishCommand(); } } return; }
/** * @breif Causes this CMoblieCAI to execute the attack order c */ void CMobileCAI::ExecuteAttack(Command &c){ assert(owner->unitDef->canAttack); if(tempOrder && owner->moveState < 2){ //limit how far away we fly if(orderTarget && LinePointDist(commandPos1,commandPos2,orderTarget->pos) > 500 + owner->maxRange){ StopMove(); return FinishCommand(); } } if(!inCommand){ if(c.params.size()==1){ if(uh->units[int(c.params[0])] != 0 && uh->units[int(c.params[0])] != owner){ float3 fix=uh->units[int(c.params[0])]->pos+owner->posErrorVector*128; SetGoal(fix, owner->pos); orderTarget=uh->units[int(c.params[0])]; AddDeathDependence(orderTarget); inCommand=true; } else { StopMove(); //cancel keeppointingto return FinishCommand(); } } else { float3 pos(c.params[0],c.params[1],c.params[2]); SetGoal(pos, owner->pos); inCommand=true; } return; } if(targetDied || (c.params.size() == 1 && UpdateTargetLostTimer(int(c.params[0])) == 0)){ StopMove(); //cancel keeppointingto return FinishCommand(); } if(orderTarget){ //note that we handle aircrafts slightly differently if((((owner->AttackUnit(orderTarget, c.id==CMD_DGUN) && owner->weapons.size() > 0 && owner->weapons.front()->range - owner->weapons.front()->relWeaponPos.Length() > orderTarget->pos.distance(owner->pos)) || dynamic_cast<CTAAirMoveType*>(owner->moveType)) && (owner->pos-orderTarget->pos).Length2D() < owner->maxRange*0.9f) || (owner->pos-orderTarget->pos).SqLength2D()<1024){ StopMove(); owner->moveType->KeepPointingTo(orderTarget, min((float)(owner->losRadius*SQUARE_SIZE*2), owner->maxRange*0.9f), true); } else if((orderTarget->pos+owner->posErrorVector*128).distance2D(goalPos) > 10+orderTarget->pos.distance2D(owner->pos)*0.2f){ float3 fix=orderTarget->pos+owner->posErrorVector*128; SetGoal(fix, owner->pos); } } else { float3 pos(c.params[0],c.params[1],c.params[2]); if((owner->AttackGround(pos,c.id==CMD_DGUN) && owner->weapons.size() > 0 && (owner->pos-pos).Length() < owner->weapons.front()->range - owner->weapons.front()->relWeaponPos.Length()) || (owner->pos-pos).SqLength2D()<1024){ StopMove(); owner->moveType->KeepPointingTo(pos, owner->maxRange*0.9f, true); } else if(pos.distance2D(goalPos)>10){ SetGoal(pos, owner->pos); } } }
void CAirCAI::SlowUpdate() { if(!commandQue.empty() && commandQue.front().timeOut<gs->frameNum){ FinishCommand(); return; } CAirMoveType* myPlane=(CAirMoveType*)owner->moveType; if(commandQue.empty()){ if(myPlane->aircraftState==CAirMoveType::AIRCRAFT_FLYING && !owner->unitDef->DontLand ()) myPlane->SetState(CAirMoveType::AIRCRAFT_LANDING); if(owner->fireState==2 && owner->moveState!=0 && owner->maxRange>0){ if(myPlane->isFighter){ float testRad=1000*owner->moveState; CUnit* enemy=helper->GetClosestEnemyAircraft(owner->pos+owner->speed*10,testRad,owner->allyteam); if(enemy && !enemy->crashing){ Command nc; nc.id=CMD_ATTACK; nc.params.push_back(enemy->id); nc.options=0; commandQue.push_front(nc); inCommand=false; return; } } if(owner->moveState && owner->fireState==2 && owner->maxRange>0){ float testRad=500*owner->moveState; CUnit* enemy=helper->GetClosestEnemyUnit(owner->pos+owner->speed*20,testRad,owner->allyteam); if(enemy && (owner->hasUWWeapons || !enemy->isUnderWater) && !enemy->crashing && (myPlane->isFighter || !enemy->unitDef->canfly)){ Command nc; nc.id=CMD_ATTACK; nc.params.push_back(enemy->id); nc.options=0; commandQue.push_front(nc); inCommand=false; return; } } } return; } if(myPlane->aircraftState==CAirMoveType::AIRCRAFT_LANDED && commandQue.front().id!=CMD_STOP){ myPlane->SetState(CAirMoveType::AIRCRAFT_TAKEOFF); } if(myPlane->aircraftState==CAirMoveType::AIRCRAFT_LANDING && commandQue.front().id!=CMD_STOP){ myPlane->SetState(CAirMoveType::AIRCRAFT_FLYING); } float3 curPos=owner->pos; Command& c=commandQue.front(); switch(c.id){ case CMD_STOP:{ CCommandAI::SlowUpdate(); break;} case CMD_MOVE:{ if(tempOrder){ tempOrder=false; inCommand=true; } if(!inCommand){ inCommand=true; commandPos1=myPlane->owner->pos; } float3 pos(c.params[0],c.params[1],c.params[2]); commandPos2=pos; myPlane->goalPos=pos; if(!(c.options&CONTROL_KEY)){ if(owner->fireState==2 && owner->moveState!=0 && owner->maxRange>0){ if(myPlane->isFighter){ float testRad=500*owner->moveState; CUnit* enemy=helper->GetClosestEnemyAircraft(owner->pos+owner->speed*20,testRad,owner->allyteam); if(enemy && ((enemy->unitDef->canfly && !enemy->crashing && myPlane->isFighter) || (!enemy->unitDef->canfly && (!myPlane->isFighter || owner->moveState==2)))){ if(owner->moveState!=1 || LinePointDist(commandPos1,commandPos2,enemy->pos) < 1000){ Command nc; nc.id=CMD_ATTACK; nc.params.push_back(enemy->id); nc.options=c.options; commandQue.push_front(nc); tempOrder=true; inCommand=false; SlowUpdate(); return; } } } if((!myPlane->isFighter || owner->moveState==2) && owner->maxRange>0){ float testRad=325*owner->moveState; CUnit* enemy=helper->GetClosestEnemyUnit(owner->pos+owner->speed*30,testRad,owner->allyteam); if(enemy && (owner->hasUWWeapons || !enemy->isUnderWater) && ((enemy->unitDef->canfly && !enemy->crashing && myPlane->isFighter) || (!enemy->unitDef->canfly && (!myPlane->isFighter || owner->moveState==2)))){ if(owner->moveState!=1 || LinePointDist(commandPos1,commandPos2,enemy->pos) < 1000){ Command nc; nc.id=CMD_ATTACK; nc.params.push_back(enemy->id); nc.options=c.options; commandQue.push_front(nc); tempOrder=true; inCommand=false; SlowUpdate(); return; } } } } } if((curPos-pos).SqLength2D()<16000){ FinishCommand(); } break;} case CMD_PATROL:{ if(tempOrder){ tempOrder=false; inCommand=true; } if(!inCommand){ float3 pos(c.params[0],c.params[1],c.params[2]); inCommand=true; Command co; co.id=CMD_PATROL; co.params.push_back(curPos.x); co.params.push_back(curPos.y); co.params.push_back(curPos.z); commandQue.push_front(co); commandPos1=curPos; activeCommand=1; patrolTime=3; } Command& c=commandQue[activeCommand]; if(c.params.size()<3){ info->AddLine("Patrol command with insufficient parameters ?"); return; } goalPos=float3(c.params[0],c.params[1],c.params[2]); commandPos2=goalPos; patrolTime++; if(owner->fireState==2 && owner->moveState!=0 && owner->maxRange>0){ if(myPlane->isFighter){ float testRad=1000*owner->moveState; CUnit* enemy=helper->GetClosestEnemyAircraft(owner->pos+owner->speed*10,testRad,owner->allyteam); if(enemy && !enemy->crashing){ if(owner->moveState!=1 || LinePointDist(commandPos1,commandPos2,enemy->pos) < 1000){ Command nc; nc.id=CMD_ATTACK; nc.params.push_back(enemy->id); nc.options=c.options; commandQue.push_front(nc); tempOrder=true; inCommand=false; SlowUpdate(); return; } } } if((!myPlane->isFighter || owner->moveState==2) && owner->maxRange>0){ float testRad=500*owner->moveState; CUnit* enemy=helper->GetClosestEnemyUnit(owner->pos+owner->speed*20,testRad,owner->allyteam); if(enemy && (owner->hasUWWeapons || !enemy->isUnderWater) && !enemy->crashing && (myPlane->isFighter || !enemy->unitDef->canfly)){ if(owner->moveState!=1 || LinePointDist(commandPos1,commandPos2,enemy->pos) < 1000){ Command nc; nc.id=CMD_ATTACK; nc.params.push_back(enemy->id); nc.options=c.options; commandQue.push_front(nc); tempOrder=true; inCommand=false; SlowUpdate(); return; } } } } myPlane->goalPos=goalPos; if((owner->pos-goalPos).SqLength2D()<16000 || (owner->pos+owner->speed*8-goalPos).SqLength2D()<16000){ if(owner->group) owner->group->CommandFinished(owner->id,CMD_PATROL); commandPos1=commandPos2; if((int)commandQue.size()<=activeCommand+1) activeCommand=0; else { if(commandQue[activeCommand+1].id!=CMD_PATROL) activeCommand=0; else activeCommand++; } } return;} case CMD_ATTACK: targetAge++; if(tempOrder && owner->moveState==1){ //limit how far away we fly if(orderTarget && LinePointDist(commandPos1,commandPos2,orderTarget->pos) > 1500){ owner->SetUserTarget(0); FinishCommand(); break; } } if(tempOrder && myPlane->isFighter && orderTarget){ if(owner->fireState==2 && owner->moveState!=0){ CUnit* enemy=helper->GetClosestEnemyAircraft(owner->pos+owner->speed*50,800,owner->allyteam); if(enemy && (!orderTarget->unitDef->canfly || (orderTarget->pos-owner->pos+owner->speed*50).Length()+100*gs->randFloat()*40-targetAge < (enemy->pos-owner->pos+owner->speed*50).Length())){ c.params.clear(); c.params.push_back(enemy->id); inCommand=false; } } } if(inCommand){ if(targetDied){ FinishCommand(); break; } if(orderTarget && orderTarget->unitDef->canfly && orderTarget->crashing){ owner->SetUserTarget(0); FinishCommand(); break; } if(orderTarget){ // myPlane->goalPos=orderTarget->pos; } else { // float3 pos(c.params[0],c.params[1],c.params[2]); // myPlane->goalPos=pos; } } else { targetAge=0; if(c.params.size()==1){ if(uh->units[int(c.params[0])]!=0 && uh->units[int(c.params[0])]!=owner){ orderTarget=uh->units[int(c.params[0])]; owner->AttackUnit(orderTarget,false); AddDeathDependence(orderTarget); inCommand=true; } else { FinishCommand(); break; } } else { float3 pos(c.params[0],c.params[1],c.params[2]); owner->AttackGround(pos,false); inCommand=true; } } break; case CMD_AREA_ATTACK:{ if(targetDied){ targetDied=false; inCommand=false; } float3 pos(c.params[0],c.params[1],c.params[2]); float radius=c.params[3]; if(inCommand){ if(myPlane->aircraftState==CAirMoveType::AIRCRAFT_LANDED) inCommand=false; if(orderTarget && orderTarget->pos.distance2D(pos)>radius){ inCommand=false; DeleteDeathDependence(orderTarget); orderTarget=0; } } else { if(myPlane->aircraftState!=CAirMoveType::AIRCRAFT_LANDED){ inCommand=true; std::vector<int> eu; helper->GetEnemyUnits(pos,radius,owner->allyteam,eu); if(eu.empty()){ float3 attackPos=pos+gs->randVector()*radius; attackPos.y=ground->GetHeight(attackPos.x,attackPos.z); owner->AttackGround(attackPos,false); } else { int num=(int) (gs->randFloat()*eu.size()); orderTarget=uh->units[eu[num]]; owner->AttackUnit(orderTarget,false); AddDeathDependence(orderTarget); } } } break;} case CMD_GUARD: if(uh->units[int(c.params[0])]!=0){ CUnit* guarded=uh->units[int(c.params[0])]; if(guarded->lastAttacker && guarded->lastAttack+40<gs->frameNum && owner->maxRange>0 && (owner->hasUWWeapons || !guarded->lastAttacker->isUnderWater)){ Command nc; nc.id=CMD_ATTACK; nc.params.push_back(guarded->lastAttacker->id); nc.options=c.options; commandQue.push_front(nc); SlowUpdate(); return; } else { Command c2; c2.id=CMD_MOVE; c2.options=c.options; c2.params.push_back(guarded->pos.x); c2.params.push_back(guarded->pos.y); c2.params.push_back(guarded->pos.z); c2.timeOut=gs->frameNum+60; commandQue.push_front(c2); return; } } else { FinishCommand(); } break; default: CCommandAI::SlowUpdate(); break; } }
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; }
void CMobileCAI::SlowUpdate() { if(owner->unitDef->maxFuel>0 && dynamic_cast<CTAAirMoveType*>(owner->moveType)){ CTAAirMoveType* myPlane=(CTAAirMoveType*)owner->moveType; if(myPlane->reservedPad){ return; } else { if(owner->currentFuel <= 0){ StopMove(); owner->userAttackGround=false; owner->userTarget=0; inCommand=false; CAirBaseHandler::LandingPad* lp=airBaseHandler->FindAirBase(owner,8000,owner->unitDef->minAirBasePower); if(lp){ myPlane->AddDeathDependence(lp); myPlane->reservedPad=lp; myPlane->padStatus=0; myPlane->oldGoalPos=myPlane->goalPos; return; } float3 landingPos = airBaseHandler->FindClosestAirBasePos(owner,8000,owner->unitDef->minAirBasePower); if(landingPos != ZeroVector && owner->pos.distance2D(landingPos) > 300){ inCommand=false; StopMove(); SetGoal(landingPos,owner->pos); } else { if(myPlane->aircraftState == CTAAirMoveType::AIRCRAFT_FLYING) myPlane->SetState(CTAAirMoveType::AIRCRAFT_LANDING); } return; } if(owner->currentFuel < myPlane->repairBelowHealth*owner->unitDef->maxFuel){ if(commandQue.empty() || commandQue.front().id==CMD_PATROL){ CAirBaseHandler::LandingPad* lp=airBaseHandler->FindAirBase(owner,8000,owner->unitDef->minAirBasePower); if(lp){ StopMove(); owner->userAttackGround=false; owner->userTarget=0; inCommand=false; myPlane->AddDeathDependence(lp); myPlane->reservedPad=lp; myPlane->padStatus=0; myPlane->oldGoalPos=myPlane->goalPos; if(myPlane->aircraftState==CTAAirMoveType::AIRCRAFT_LANDED){ myPlane->SetState(CTAAirMoveType::AIRCRAFT_TAKEOFF); } return; } } } } } if(!commandQue.empty() && commandQue.front().timeOut<gs->frameNum){ StopMove(); FinishCommand(); return; } if(commandQue.empty()) { // if(!owner->ai || owner->ai->State() != CHasState::Active) { IdleCheck(); // } if(commandQue.empty() || commandQue.front().id==CMD_ATTACK) //the attack order could terminate directly and thus cause a loop return; } float3 curPos=owner->pos; // treat any following CMD_SET_WANTED_MAX_SPEED commands as options // to the current command (and ignore them when it's their turn) if (commandQue.size() >= 2) { deque<Command>::iterator it = commandQue.begin(); it++; const Command& c = *it; if ((c.id == CMD_SET_WANTED_MAX_SPEED) && (c.params.size() >= 1)) { const float defMaxSpeed = owner->maxSpeed; const float newMaxSpeed = min(c.params[0], defMaxSpeed); owner->moveType->SetMaxSpeed(newMaxSpeed); } } Command& c=commandQue.front(); switch(c.id){ case CMD_WAIT:{ break; } case CMD_STOP:{ StopMove(); CCommandAI::SlowUpdate(); break; } case CMD_SET_WANTED_MAX_SPEED:{ if (repeatOrders && (commandQue.size() >= 2) && (commandQue.back().id != CMD_SET_WANTED_MAX_SPEED)) { commandQue.push_back(commandQue.front()); } commandQue.pop_front(); SlowUpdate(); break; } case CMD_MOVE:{ float3 pos(c.params[0],c.params[1],c.params[2]); if(!(pos==goalPos)){ SetGoal(pos,curPos); } if((curPos-goalPos).SqLength2D()<1024 || owner->moveType->progressState==CMoveType::Failed){ FinishCommand(); } break; } case CMD_PATROL:{ assert(owner->unitDef->canPatrol); if(c.params.size()<3){ //this shouldnt happen but anyway ... logOutput.Print("Error: got patrol cmd with less than 3 params on %s in mobilecai", owner->unitDef->humanName.c_str()); return; } Command temp; temp.id = CMD_FIGHT; temp.params.push_back(c.params[0]); temp.params.push_back(c.params[1]); temp.params.push_back(c.params[2]); temp.options = c.options|INTERNAL_ORDER; commandQue.push_back(c); commandQue.pop_front(); commandQue.push_front(temp); if(owner->group){ owner->group->CommandFinished(owner->id,CMD_PATROL); } SlowUpdate(); return; } case CMD_FIGHT:{ assert((c.options & INTERNAL_ORDER) || owner->unitDef->canFight); if(tempOrder){ inCommand = true; tempOrder = false; } if(c.params.size()<3){ //this shouldnt happen but anyway ... logOutput.Print("Error: got fight cmd with less than 3 params on %s in mobilecai", 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, curPos); if ((curPos-commandPos1).SqLength2D()>(96*96)) { commandPos1 = curPos; } } float3 pos(c.params[0],c.params[1],c.params[2]); if(!inCommand){ inCommand = true; commandPos2 = pos; } if(pos!=goalPos){ SetGoal(pos,curPos); } if(owner->unitDef->canAttack && owner->fireState==2){ float3 curPosOnLine = ClosestPointOnLine(commandPos1, commandPos2, curPos); CUnit* enemy=helper->GetClosestEnemyUnit( curPosOnLine, owner->maxRange+100*owner->moveState*owner->moveState, owner->allyteam); if(enemy && (owner->hasUWWeapons || !enemy->isUnderWater) && !(owner->unitDef->noChaseCategory & enemy->category) && !owner->weapons.empty()){ Command c2; c2.id=CMD_ATTACK; c2.options=c.options|INTERNAL_ORDER; c2.params.push_back(enemy->id); PushOrUpdateReturnFight(); commandQue.push_front(c2); inCommand=false; tempOrder=true; if(lastPC!=gs->frameNum){ //avoid infinite loops lastPC=gs->frameNum; SlowUpdate(); } return; } } if((curPos-goalPos).SqLength2D()<(64*64) || owner->moveType->progressState==CMoveType::Failed){ FinishCommand(); } return; } case CMD_DGUN: if(uh->limitDgun && owner->unitDef->isCommander && owner->pos.distance(gs->Team(owner->team)->startPos)>uh->dgunRadius){ StopMove(); FinishCommand(); return; } //no break case CMD_ATTACK: assert(owner->unitDef->canAttack); if(tempOrder && owner->moveState<2){ //limit how far away we fly if(orderTarget && LinePointDist(commandPos1,commandPos2,orderTarget->pos) > 500+owner->maxRange){ StopMove(); FinishCommand(); break; } } if(!inCommand){ if(c.params.size()==1){ if(uh->units[int(c.params[0])]!=0 && uh->units[int(c.params[0])]!=owner){ float3 fix=uh->units[int(c.params[0])]->pos+owner->posErrorVector*128; SetGoal(fix,curPos); orderTarget=uh->units[int(c.params[0])]; AddDeathDependence(orderTarget); inCommand=true; } else { StopMove(); //cancel keeppointingto FinishCommand(); break; } } else { float3 pos(c.params[0],c.params[1],c.params[2]); SetGoal(pos,curPos); inCommand=true; } } else if ((c.params.size() == 3) && (owner->commandShotCount > 0) && (commandQue.size() > 1)) { // the trailing CMD_SET_WANTED_MAX_SPEED in a command pair does not count if ((commandQue.size() != 2) || (commandQue.back().id != CMD_SET_WANTED_MAX_SPEED)) { StopMove(); FinishCommand(); break; } } if(targetDied || (c.params.size() == 1 && UpdateTargetLostTimer(int(c.params[0])) == 0)){ StopMove(); //cancel keeppointingto FinishCommand(); break; } if(orderTarget){ //note that we handle aircrafts slightly differently if((((owner->AttackUnit(orderTarget, c.id==CMD_DGUN) && owner->weapons.size() > 0 && owner->weapons.front()->range - owner->weapons.front()->relWeaponPos.Length() > orderTarget->pos.distance(owner->pos)) || dynamic_cast<CTAAirMoveType*>(owner->moveType)) && (owner->pos-orderTarget->pos).Length2D() < owner->maxRange*0.9f) || (owner->pos-orderTarget->pos).SqLength2D()<1024){ StopMove(); owner->moveType->KeepPointingTo(orderTarget, min((float)(owner->losRadius*SQUARE_SIZE*2), owner->maxRange*0.9f), true); } else if((orderTarget->pos+owner->posErrorVector*128).distance2D(goalPos) > 10+orderTarget->pos.distance2D(owner->pos)*0.2f){ float3 fix=orderTarget->pos+owner->posErrorVector*128; SetGoal(fix,curPos); } } else { float3 pos(c.params[0],c.params[1],c.params[2]); if((owner->AttackGround(pos,c.id==CMD_DGUN) && owner->weapons.size() > 0 && (owner->pos-pos).Length() < owner->weapons.front()->range - owner->weapons.front()->relWeaponPos.Length()) || (owner->pos-pos).SqLength2D()<1024){ StopMove(); owner->moveType->KeepPointingTo(pos, owner->maxRange*0.9f, true); } else if(pos.distance2D(goalPos)>10){ SetGoal(pos,curPos); } } break; case CMD_GUARD: assert(owner->unitDef->canGuard); if(int(c.params[0]) >= 0 && uh->units[int(c.params[0])] != NULL && UpdateTargetLostTimer(int(c.params[0]))){ CUnit* guarded=uh->units[int(c.params[0])]; if(owner->unitDef->canAttack && guarded->lastAttacker && guarded->lastAttack+40<gs->frameNum && (owner->hasUWWeapons || !guarded->lastAttacker->isUnderWater)){ Command nc; nc.id=CMD_ATTACK; nc.params.push_back(guarded->lastAttacker->id); nc.options=c.options; commandQue.push_front(nc); SlowUpdate(); return; } else { float3 dif=guarded->pos-curPos; dif.Normalize(); float3 goal=guarded->pos-dif*(guarded->radius+owner->radius+64); if((goal-curPos).SqLength2D()<8000){ StopMove(); NonMoving(); }else{ if((goalPos-goal).SqLength2D()>3600) SetGoal(goal,curPos); } } } else { FinishCommand(); } break; default: CCommandAI::SlowUpdate(); break; } }
/** * @brief Causes this CMobileCAI to execute the attack order c */ void CMobileCAI::ExecuteAttack(Command &c) { assert(owner->unitDef->canAttack); // limit how far away we fly if (tempOrder && (owner->moveState < 2) && orderTarget && LinePointDist(ClosestPointOnLine(commandPos1, commandPos2, owner->pos), commandPos2, orderTarget->pos) > (500 * owner->moveState + owner->maxRange)) { StopMove(); FinishCommand(); return; } // check if we are in direct command of attacker if (!inCommand) { // don't start counting until the owner->AttackGround() order is given owner->commandShotCount = -1; if (c.params.size() == 1) { CUnit* targetUnit = uh->GetUnit(c.params[0]); // check if we have valid target parameter and that we aren't attacking ourselves if (targetUnit != NULL && targetUnit != owner) { float3 fix = targetUnit->pos + owner->posErrorVector * 128; float3 diff = float3(fix - owner->pos).Normalize(); SetGoal(fix - diff * targetUnit->radius, owner->pos); orderTarget = targetUnit; AddDeathDependence(orderTarget); inCommand = true; } else { // unit may not fire on itself, cancel order StopMove(); FinishCommand(); return; } } else if (c.params.size() >= 3) { // user gave force-fire attack command float3 pos(c.params[0], c.params[1], c.params[2]); SetGoal(pos, owner->pos); inCommand = true; } } else if ((c.params.size() == 3) && (owner->commandShotCount > 0) && (commandQue.size() > 1)) { // the trailing CMD_SET_WANTED_MAX_SPEED in a command pair does not count if ((commandQue.size() > 2) || (commandQue.back().id != CMD_SET_WANTED_MAX_SPEED)) { StopMove(); FinishCommand(); return; } } // 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) { //bool b1 = owner->AttackUnit(orderTarget, c.id == CMD_DGUN); bool b2 = false; bool b3 = false; bool b4 = false; float edgeFactor = 0.f; // percent offset to target center float3 diff = owner->pos - orderTarget->midPos; if (owner->weapons.size() > 0) { if (!(c.options & ALT_KEY) && SkipParalyzeTarget(orderTarget)) { StopMove(); FinishCommand(); return; } CWeapon* w = owner->weapons.front(); // if we have at least one weapon then check if we // can hit target with our first (meanest) one b2 = w->TryTargetRotate(orderTarget, c.id == CMD_DGUN); b3 = Square(w->range - (w->relWeaponPos).Length()) > (orderTarget->pos.SqDistance(owner->pos)); b4 = w->TryTargetHeading(GetHeadingFromVector(-diff.x, -diff.z), orderTarget->pos, orderTarget != NULL, orderTarget); edgeFactor = fabs(w->targetBorder); } float diffLength2d = diff.Length2D(); // 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 (b2) { if (!(tempOrder && owner->moveState == 0) && (diffLength2d * 1.4f > owner->maxRange - orderTarget->speed.SqLength() / owner->unitDef->maxAcc) && b4 && diff.dot(orderTarget->speed) < 0) { SetGoal(owner->pos + (orderTarget->speed * 80), owner->pos, SQUARE_SIZE, orderTarget->speed.Length() * 1.1f); } else { StopMove(); // FIXME kill magic frame number if (gs->frameNum > lastCloseInTry + MAX_CLOSE_IN_RETRY_TICKS) { owner->moveType->KeepPointingTo(orderTarget->midPos, std::min((float) owner->losRadius * loshandler->losDiv, owner->maxRange * 0.9f), true); } } owner->AttackUnit(orderTarget, c.id == CMD_DGUN); } // 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 == 0) { StopMove(); FinishCommand(); return; } // if ((our movetype has type TAAirMoveType 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(diffLength2d < (owner->maxRange * 0.9f)){ if (dynamic_cast<CTAAirMoveType*>(owner->moveType) || (diff.SqLength2D() < 1024)) { StopMove(); owner->moveType->KeepPointingTo(orderTarget->midPos, std::min((float) owner->losRadius * loshandler->losDiv, owner->maxRange * 0.9f), 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 && b3 && diffLength2d < (owner->maxRange * 0.9f)) { moveDir ^= (owner->moveType->progressState == AMoveType::Failed); float sin = moveDir ? 3.0/5 : -3.0/5; float cos = 4.0/5; float3 goalDiff(0, 0, 0); goalDiff.x = diff.dot(float3(cos, 0, -sin)); goalDiff.z = diff.dot(float3(sin, 0, cos)); goalDiff *= (diffLength2d < (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 ((orderTarget->pos + owner->posErrorVector * 128).SqDistance2D(goalPos) > Square(10 + orderTarget->pos.distance2D(owner->pos) * 0.2f)) { // 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) float3 fix = orderTarget->pos + (orderTarget->losStatus[owner->allyteam] & LOS_INLOS ? float3(0.f,0.f,0.f) : owner->posErrorVector * 128); float3 norm = float3(fix - owner->pos).Normalize(); float3 goal = fix - norm*(orderTarget->radius*edgeFactor*0.8f); SetGoal(goal, owner->pos); if (lastCloseInTry < gs->frameNum + MAX_CLOSE_IN_RETRY_TICKS) lastCloseInTry = gs->frameNum; } } // user is attacking ground else if (c.params.size() >= 3) { const float3 pos(c.params[0], c.params[1], c.params[2]); const float3 diff = owner->pos - pos; if (owner->weapons.size() > 0) { // if we have at least one weapon then check if // we can hit position with our first (assumed // to be meanest) one CWeapon* w = owner->weapons.front(); // XXX hack - dgun overrides any checks if (c.id == CMD_DGUN) { float rr = owner->maxRange * owner->maxRange; for (vector<CWeapon*>::iterator it = owner->weapons.begin(); it != owner->weapons.end(); ++it) { if (dynamic_cast<CDGunWeapon*>(*it)) rr = (*it)->range * (*it)->range; } if (diff.SqLength() < rr) { StopMove(); owner->AttackGround(pos, c.id == CMD_DGUN); owner->moveType->KeepPointingTo(pos, owner->maxRange * 0.9f, true); } } else { const bool inAngle = w->TryTargetRotate(pos, c.id == CMD_DGUN); const bool inRange = diff.SqLength2D() < Square(w->range - (w->relWeaponPos).Length2D()); if (inAngle || inRange) { StopMove(); owner->AttackGround(pos, c.id == CMD_DGUN); owner->moveType->KeepPointingTo(pos, owner->maxRange * 0.9f, true); } } } else if (diff.SqLength2D() < 1024) { StopMove(); owner->moveType->KeepPointingTo(pos, owner->maxRange * 0.9f, true); } // if we are more than 10 units distant from target position then keeping moving closer else if (pos.SqDistance2D(goalPos) > 100) { SetGoal(pos, owner->pos); } } }
void CAirCAI::ExecuteAttack(Command &c) { assert(owner->unitDef->canAttack); targetAge++; if (tempOrder && owner->moveState == 1) { // limit how far away we fly if (orderTarget && LinePointDist(commandPos1, commandPos2, orderTarget->pos) > 1500) { owner->SetUserTarget(0); FinishCommand(); return; } } if (inCommand) { if (targetDied || (c.params.size() == 1 && UpdateTargetLostTimer(int(c.params[0])) == 0)) { FinishCommand(); return; } if ((c.params.size() == 3) && (owner->commandShotCount < 0) && (commandQue.size() > 1)) { owner->AttackGround(float3(c.params[0], c.params[1], c.params[2]), true); FinishCommand(); return; } if (orderTarget) { if (orderTarget->unitDef->canfly && orderTarget->crashing) { owner->SetUserTarget(0); FinishCommand(); return; } if (!(c.options & ALT_KEY) && SkipParalyzeTarget(orderTarget)) { owner->SetUserTarget(0); FinishCommand(); return; } } } else { targetAge = 0; owner->commandShotCount = -1; if (c.params.size() == 1) { const int targetID = int(c.params[0]); const bool legalTarget = (targetID >= 0 && targetID < MAX_UNITS); CUnit* targetUnit = (legalTarget)? uh->units[targetID]: 0x0; if (legalTarget && targetUnit != 0x0 && targetUnit != owner) { orderTarget = targetUnit; owner->AttackUnit(orderTarget, false); AddDeathDependence(orderTarget); inCommand = true; SetGoal(orderTarget->pos, owner->pos, cancelDistance); } else { FinishCommand(); return; } } else { float3 pos(c.params[0], c.params[1], c.params[2]); owner->AttackGround(pos, false); inCommand = true; } } return; }
void CAirCAI::ExecuteFight(Command &c) { assert((c.options & INTERNAL_ORDER) || owner->unitDef->canFight); AAirMoveType* myPlane = (AAirMoveType*) owner->moveType; if(tempOrder){ tempOrder=false; inCommand=true; } if(c.params.size()<3){ //this shouldnt happen but anyway ... logOutput.Print("Error: got fight cmd with less than 3 params on %s in AirCAI", 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()<(127*127)){' // 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() > (150 * 150)) { commandPos1 = owner->pos; } } goalPos = float3(c.params[0], c.params[1], c.params[2]); if(!inCommand){ inCommand = true; commandPos2=goalPos; } if(c.params.size() >= 6){ goalPos = ClosestPointOnLine(commandPos1, commandPos2, owner->pos); } // CMD_FIGHT is pretty useless if !canAttack but we try to honour the modders wishes anyway... if (owner->unitDef->canAttack && owner->fireState >= 2 && owner->moveState != 0 && owner->maxRange > 0) { float3 curPosOnLine = ClosestPointOnLine(commandPos1, commandPos2, owner->pos + owner->speed*10); float testRad = 1000 * owner->moveState; CUnit* enemy = NULL; if(myPlane->IsFighter()) { enemy = helper->GetClosestEnemyAircraft(curPosOnLine, testRad, owner->allyteam); } if(IsValidTarget(enemy) && (owner->moveState!=1 || LinePointDist(commandPos1, commandPos2, enemy->pos) < 1000)) { Command nc; nc.id=CMD_ATTACK; nc.params.push_back(enemy->id); nc.options=c.options|INTERNAL_ORDER; commandQue.push_front(nc); tempOrder=true; inCommand=false; if(lastPC1!=gs->frameNum){ //avoid infinite loops lastPC1=gs->frameNum; SlowUpdate(); } return; } else { float3 curPosOnLine = ClosestPointOnLine( commandPos1, commandPos2, owner->pos + owner->speed * 20); float testRad = 500 * owner->moveState; enemy = helper->GetClosestEnemyUnit(curPosOnLine, testRad, owner->allyteam); if(IsValidTarget(enemy)) { Command nc; nc.id = CMD_ATTACK; nc.params.push_back(enemy->id); nc.options = c.options | INTERNAL_ORDER; PushOrUpdateReturnFight(); commandQue.push_front(nc); tempOrder = true; inCommand = false; if(lastPC2 != gs->frameNum){ //avoid infinite loops lastPC2 = gs->frameNum; SlowUpdate(); } return; } } } myPlane->goalPos = goalPos; if((owner->pos - goalPos).SqLength2D() < (127 * 127) || (owner->pos + owner->speed*8 - goalPos).SqLength2D() < (127 * 127)) { FinishCommand(); } return; }
void CBuilderCAI::SlowUpdate() { if(commandQue.empty()){ CMobileCAI::SlowUpdate(); return; } Command& c=commandQue.front(); CBuilder* fac=(CBuilder*)owner; map<int,string>::iterator boi; if((boi=buildOptions.find(c.id))!=buildOptions.end()){ float radius; if(cachedRadiusId==c.id){ radius=cachedRadius; } else { radius=modelParser->Load3DO(unitDefHandler->GetUnitByName(boi->second)->model.modelpath,1,owner->team)->radius; cachedRadiusId=c.id; cachedRadius=radius; } if(inCommand){ if(building){ if(buildPos.distance2D(fac->pos)>fac->buildDistance+radius-8){ owner->moveType->StartMoving(buildPos, fac->buildDistance*0.5+radius); } else { StopMove(); owner->moveType->KeepPointingTo(buildPos, (fac->buildDistance+radius)*0.6, false); //needed since above startmoving cancels this } if(!fac->curBuild && !fac->terraforming){ building=false; StopMove(); //cancel the effect of KeepPointingTo FinishCommand(); } } else { buildPos.x=c.params[0]; buildPos.y=c.params[1]; buildPos.z=c.params[2]; if(buildPos.distance2D(fac->pos)<fac->buildDistance*0.6+radius){ StopMove(); if(uh->maxUnits>(int)gs->Team(owner->team)->units.size()){ buildRetries++; owner->moveType->KeepPointingTo(buildPos, fac->buildDistance*0.7+radius, false); if(fac->StartBuild(boi->second,buildPos) || buildRetries>20){ building=true; } else { ENTER_MIXED; if(owner->team==gu->myTeam && !(buildRetries&7)){ info->AddLine("%s: Build pos blocked",owner->unitDef->humanName.c_str()); info->SetLastMsgPos(owner->pos); } ENTER_SYNCED; helper->BuggerOff(buildPos,radius); NonMoving(); } } } else { if(owner->moveType->progressState==CMoveType::Failed){ if(++buildRetries>5){ StopMove(); FinishCommand(); } } SetGoal(buildPos,owner->pos, fac->buildDistance*0.4+radius); } } } else { //!inCommand float3 pos; pos.x=floor(c.params[0]/SQUARE_SIZE+0.5)*SQUARE_SIZE; pos.z=floor(c.params[2]/SQUARE_SIZE+0.5)*SQUARE_SIZE; pos.y=c.params[1]; CFeature* f=0; uh->TestUnitBuildSquare(pos,boi->second,f); if(f){ Command c2; c2.id=CMD_RECLAIM; c2.options=0; c2.params.push_back(f->id+MAX_UNITS); commandQue.push_front(c2); SlowUpdate(); //this assumes that the reclaim command can never return directly without having reclaimed the target } else { inCommand=true; SlowUpdate(); } } return; } switch(c.id){ case CMD_STOP: building=false; fac->StopBuild(); break; case CMD_REPAIR:{ if(c.params.size()==1){ //repair unit CUnit* unit=uh->units[(int)c.params[0]]; if(commandFromPatrol && owner->moveState==1){ //limit how far away we go if(unit && LinePointDist(commandPos1,commandPos2,unit->pos) > 500){ StopMove(); FinishCommand(); break; } } if(unit && unit->health<unit->maxHealth && unit!=owner){ if(unit->pos.distance2D(fac->pos)<fac->buildDistance+unit->radius-8){ StopMove(); fac->SetRepairTarget(unit); owner->moveType->KeepPointingTo(unit->pos, fac->buildDistance*0.9+unit->radius, false); } else { if(goalPos.distance2D(unit->pos)>1) SetGoal(unit->pos,owner->pos, fac->buildDistance*0.9+unit->radius); } } else { StopMove(); FinishCommand(); } } else { //repair area float3 pos(c.params[0],c.params[1],c.params[2]); float radius=c.params[3]; if(FindRepairTargetAndRepair(pos,radius,c.options,false)){ inCommand=false; SlowUpdate(); return; } if(!(c.options & ALT_KEY)){ FinishCommand(); } } return;} case CMD_CAPTURE:{ if(c.params.size()==1){ //capture unit CUnit* unit=uh->units[(int)c.params[0]]; if(unit && unit->team!=owner->team){ if(unit->pos.distance2D(fac->pos)<fac->buildDistance+unit->radius-8){ StopMove(); fac->SetCaptureTarget(unit); owner->moveType->KeepPointingTo(unit->pos, fac->buildDistance*0.9+unit->radius, false); } else { if(goalPos.distance2D(unit->pos)>1) SetGoal(unit->pos,owner->pos, fac->buildDistance*0.9+unit->radius); } } else { StopMove(); FinishCommand(); } } else { //capture area float3 pos(c.params[0],c.params[1],c.params[2]); float radius=c.params[3]; if(FindCaptureTargetAndCapture(pos,radius,c.options)){ inCommand=false; SlowUpdate(); return; } if(!(c.options & ALT_KEY)){ FinishCommand(); } } return;} case CMD_GUARD:{ CUnit* guarded=uh->units[(int)c.params[0]]; if(guarded && guarded!=owner){ if(CBuilder* b=dynamic_cast<CBuilder*>(guarded)){ if(b->terraforming){ if(fac->pos.distance2D(b->terraformCenter)<fac->buildDistance*0.8+b->terraformRadius*0.7){ StopMove(); owner->moveType->KeepPointingTo(b->terraformCenter, fac->buildDistance*0.9, false); fac->HelpTerraform(b); } else { SetGoal(b->terraformCenter,fac->pos,fac->buildDistance*0.7+b->terraformRadius*0.6); } return; } if(b->curBuild){ Command nc; nc.id=CMD_REPAIR; nc.options=c.options; nc.params.push_back(b->curBuild->id); commandQue.push_front(nc); inCommand=false; // SlowUpdate(); return; } } if(CFactory* f=dynamic_cast<CFactory*>(guarded)){ if(f->curBuild){ Command nc; nc.id=CMD_REPAIR; nc.options=c.options; nc.params.push_back(f->curBuild->id); commandQue.push_front(nc); inCommand=false; // SlowUpdate(); return; } } float3 curPos=owner->pos; float3 dif=guarded->pos-curPos; dif.Normalize(); float3 goal=guarded->pos-dif*(fac->buildDistance-5); if((guarded->pos-curPos).SqLength2D()<(fac->buildDistance*0.9+guarded->radius)*(fac->buildDistance*0.9+guarded->radius)){ StopMove(); // info->AddLine("should point with type 3?"); owner->moveType->KeepPointingTo(guarded->pos, fac->buildDistance*0.9+guarded->radius, false); if(guarded->health<guarded->maxHealth) fac->SetRepairTarget(guarded); else NonMoving(); }else{ if((goalPos-goal).SqLength2D()>4000) SetGoal(goal,curPos); } } else { FinishCommand(); } return;} case CMD_RECLAIM:{ if(c.params.size()==1){ int id=(int) c.params[0]; if(id>=MAX_UNITS){ //reclaim feature CFeature* feature=featureHandler->features[id-MAX_UNITS]; if(feature){ if(feature->pos.distance2D(fac->pos)<fac->buildDistance*0.9+feature->radius){ StopMove(); owner->moveType->KeepPointingTo(feature->pos, fac->buildDistance*0.9+feature->radius, false); fac->SetReclaimTarget(feature); } else { if(goalPos.distance2D(feature->pos)>1){ SetGoal(feature->pos,owner->pos, fac->buildDistance*0.8+feature->radius); } else { if(owner->moveType->progressState==CMoveType::Failed){ StopMove(); FinishCommand(); } } } } else { StopMove(); FinishCommand(); } } else { //reclaim unit CUnit* unit=uh->units[id]; if(unit && unit!=owner){ if(unit->pos.distance2D(fac->pos)<fac->buildDistance-1+unit->radius){ StopMove(); owner->moveType->KeepPointingTo(unit->pos, fac->buildDistance*0.9+unit->radius, false); fac->SetReclaimTarget(unit); } else { if(goalPos.distance2D(unit->pos)>1){ SetGoal(unit->pos,owner->pos); }else{ if(owner->moveType->progressState==CMoveType::Failed){ StopMove(); FinishCommand(); } } } } else { FinishCommand(); } } } else if(c.params.size()==4){//area reclaim float3 pos(c.params[0],c.params[1],c.params[2]); float radius=c.params[3]; if(FindReclaimableFeatureAndReclaim(pos,radius,c.options)){ inCommand=false; SlowUpdate(); return; } if(!(c.options & ALT_KEY)){ FinishCommand(); } } else { //wrong number of parameters FinishCommand(); } return;} case CMD_RESURRECT:{ if(c.params.size()==1){ int id=(int)c.params[0]; if(id>=MAX_UNITS){ //resurrect feature CFeature* feature=featureHandler->features[id-MAX_UNITS]; if(feature && feature->createdFromUnit!=""){ if(feature->pos.distance2D(fac->pos)<fac->buildDistance*0.9+feature->radius){ StopMove(); owner->moveType->KeepPointingTo(feature->pos, fac->buildDistance*0.9+feature->radius, false); fac->SetResurrectTarget(feature); } else { if(goalPos.distance2D(feature->pos)>1){ SetGoal(feature->pos,owner->pos, fac->buildDistance*0.8+feature->radius); } else { if(owner->moveType->progressState==CMoveType::Failed){ StopMove(); FinishCommand(); } } } } else { if(fac->lastResurrected && uh->units[fac->lastResurrected]){ //resurrection finished, start repair c.id=CMD_REPAIR; //kind of hackery to overwrite the current order i suppose c.params.clear(); c.params.push_back(fac->lastResurrected); c.options|=INTERNAL_ORDER; fac->lastResurrected=0; inCommand=false; SlowUpdate(); return; } StopMove(); FinishCommand(); } } else { //resurrect unit FinishCommand(); } } else if(c.params.size()==4){//area resurect float3 pos(c.params[0],c.params[1],c.params[2]); float radius=c.params[3]; if(FindResurrectableFeatureAndResurrect(pos,radius,c.options)){ inCommand=false; SlowUpdate(); return; } if(!(c.options & ALT_KEY)){ FinishCommand(); } } else { //wrong number of parameters FinishCommand(); } return;} case CMD_PATROL:{ float3 curPos=owner->pos; if(commandFromPatrol){ commandFromPatrol=false; inCommand=true; --patrolTime; //prevent searching reclaimtargets etc directly if one of those call returned directly (infinite loop) } if(!inCommand){ float3 pos(c.params[0],c.params[1],c.params[2]); inCommand=true; Command co; co.id=CMD_PATROL; co.params.push_back(curPos.x); co.params.push_back(curPos.y); co.params.push_back(curPos.z); commandQue.push_front(co); activeCommand=1; commandPos1=curPos; patrolTime=0; } Command& c=commandQue[activeCommand]; if(c.params.size()<3){ //this shouldnt happen but anyway ... info->AddLine("Error: got patrol cmd with less than 3 params on %s in BuilderCAI",owner->unitDef->humanName.c_str()); return; } patrolGoal=float3(c.params[0],c.params[1],c.params[2]); commandPos2=patrolGoal; if(!(patrolTime&3) && owner->fireState==2){ if(FindRepairTargetAndRepair(owner->pos,300*owner->moveState,c.options,true)){ CUnit* target=uh->units[(int)commandQue.front().params[0]]; if(owner->moveState!=1 || LinePointDist(commandPos1,commandPos2,target->pos) < 300){ commandFromPatrol=true; inCommand=false; SlowUpdate(); return; } else { commandQue.pop_front(); } } if(FindReclaimableFeatureAndReclaim(owner->pos,300*owner->moveState,c.options)){ commandFromPatrol=true; inCommand=false; SlowUpdate(); return; } } patrolTime++; if(!(patrolGoal==goalPos)){ SetGoal(patrolGoal,curPos); } if((curPos-patrolGoal).SqLength2D()<4096){ if(owner->group) owner->group->CommandFinished(owner->id,CMD_PATROL); if((int)commandQue.size()<=activeCommand+1) activeCommand=0; else { if(commandQue[activeCommand+1].id!=CMD_PATROL) activeCommand=0; else activeCommand++; } commandPos1=commandPos2; } if(owner->haveTarget && owner->moveType->progressState!=CMoveType::Done) StopMove(); else if(owner->moveType->progressState!=CMoveType::Active) owner->moveType->StartMoving(goalPos, 8); return;} case CMD_RESTORE:{ if(inCommand){ if(!fac->terraforming){ FinishCommand(); } } else { float3 pos(c.params[0],c.params[1],c.params[2]); float radius(c.params[3]); if(fac->pos.distance2D(pos)<fac->buildDistance-1){ StopMove(); fac->StartRestore(pos,radius); owner->moveType->KeepPointingTo(pos, fac->buildDistance*0.9, false); inCommand=true; } else { SetGoal(pos,owner->pos, fac->buildDistance*0.9); } } return;} default: break; } CMobileCAI::SlowUpdate(); }
void CMobileCAI::SlowUpdate() { if(owner->unitDef->maxFuel>0 && dynamic_cast<CTAAirMoveType*>(owner->moveType)) { CTAAirMoveType* myPlane=(CTAAirMoveType*)owner->moveType; if(myPlane->reservedPad) { return; } else { if(owner->currentFuel <= 0) { StopMove(); owner->userAttackGround=false; owner->userTarget=0; inCommand=false; CAirBaseHandler::LandingPad* lp=airBaseHandler->FindAirBase(owner,8000,owner->unitDef->minAirBasePower); if(lp) { myPlane->AddDeathDependence(lp); myPlane->reservedPad=lp; myPlane->padStatus=0; myPlane->oldGoalPos=myPlane->goalPos; return; } float3 landingPos = airBaseHandler->FindClosestAirBasePos(owner,8000,owner->unitDef->minAirBasePower); if(landingPos != ZeroVector && owner->pos.distance2D(landingPos) > 300) { inCommand=false; StopMove(); SetGoal(landingPos,owner->pos); } else { if(myPlane->aircraftState == CTAAirMoveType::AIRCRAFT_FLYING) myPlane->SetState(CTAAirMoveType::AIRCRAFT_LANDING); } return; } if(owner->currentFuel < myPlane->repairBelowHealth*owner->unitDef->maxFuel) { if(commandQue.empty() || commandQue.front().id==CMD_PATROL) { CAirBaseHandler::LandingPad* lp=airBaseHandler->FindAirBase(owner,8000,owner->unitDef->minAirBasePower); if(lp) { StopMove(); owner->userAttackGround=false; owner->userTarget=0; inCommand=false; myPlane->AddDeathDependence(lp); myPlane->reservedPad=lp; myPlane->padStatus=0; myPlane->oldGoalPos=myPlane->goalPos; if(myPlane->aircraftState==CTAAirMoveType::AIRCRAFT_LANDED) { myPlane->SetState(CTAAirMoveType::AIRCRAFT_TAKEOFF); } return; } } } } } if(!commandQue.empty() && commandQue.front().timeOut<gs->frameNum) { StopMove(); FinishCommand(); return; } if(commandQue.empty()) { // if(!owner->ai || owner->ai->State() != CHasState::Active) { IdleCheck(); // } if(commandQue.empty() || commandQue.front().id==CMD_ATTACK) //the attack order could terminate directly and thus cause a loop return; } float3 curPos=owner->pos; Command& c=commandQue.front(); switch(c.id) { case CMD_STOP: { StopMove(); CCommandAI::SlowUpdate(); break; } case CMD_MOVE: { float3 pos(c.params[0],c.params[1],c.params[2]); if(!(pos==goalPos)) { SetGoal(pos,curPos); } if((curPos-goalPos).SqLength2D()<1024 || owner->moveType->progressState==CMoveType::Failed) { FinishCommand(); } break; } case CMD_PATROL: { if(c.params.size()<3) { //this shouldnt happen but anyway ... info->AddLine("Error: got patrol cmd with less than 3 params on %s in mobilecai", owner->unitDef->humanName.c_str()); return; } Command temp; temp.id = CMD_FIGHT; temp.params.push_back(c.params[0]); temp.params.push_back(c.params[1]); temp.params.push_back(c.params[2]); temp.options = c.options|INTERNAL_ORDER; commandQue.push_back(c); commandQue.pop_front(); commandQue.push_front(temp); if(owner->group) { owner->group->CommandFinished(owner->id,CMD_PATROL); } SlowUpdate(); return; } case CMD_FIGHT: { if(tempOrder) { inCommand = true; tempOrder = false; } if(c.params.size()<3) { //this shouldnt happen but anyway ... info->AddLine("Error: got fight cmd with less than 3 params on %s in mobilecai", 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 { commandPos1 = curPos; } float3 pos(c.params[0],c.params[1],c.params[2]); if(!inCommand) { inCommand = true; commandPos2 = pos; } if(pos!=goalPos) { SetGoal(pos,curPos); } if(owner->fireState==2) { CUnit* enemy=helper->GetClosestEnemyUnit( curPos, owner->maxRange+100*owner->moveState*owner->moveState, owner->allyteam); if(enemy && (owner->hasUWWeapons || !enemy->isUnderWater) && !(owner->unitDef->noChaseCategory & enemy->category) && !owner->weapons.empty()) { //todo: make sure they dont stray to far from path if(owner->moveState!=1 || LinePointDist(commandPos1,commandPos2,enemy->pos) < 300+max(owner->maxRange, (float)owner->losRadius)) { Command c2,c3; c3 = this->GetReturnFight(c); c2.id=CMD_ATTACK; c2.options=c.options|INTERNAL_ORDER; c2.params.push_back(enemy->id); commandQue.push_front(c3); commandQue.push_front(c2); inCommand=false; tempOrder=true; if(lastPC!=gs->frameNum) { //avoid infinite loops lastPC=gs->frameNum; SlowUpdate(); } return; } } } if((curPos-goalPos).SqLength2D()<4096 || owner->moveType->progressState==CMoveType::Failed) { FinishCommand(); } return; } case CMD_DGUN: if(uh->limitDgun && owner->unitDef->isCommander && owner->pos.distance(gs->Team(owner->team)->startPos)>uh->dgunRadius) { StopMove(); FinishCommand(); return; } //no break case CMD_ATTACK: if(tempOrder && owner->moveState<2) { //limit how far away we fly if(orderTarget && LinePointDist(commandPos1,commandPos2,orderTarget->pos) > 500+owner->maxRange) { StopMove(); FinishCommand(); break; } } if(!inCommand) { if(c.params.size()==1) { if(uh->units[int(c.params[0])]!=0 && uh->units[int(c.params[0])]!=owner) { float3 fix=uh->units[int(c.params[0])]->pos+owner->posErrorVector*128; SetGoal(fix,curPos); orderTarget=uh->units[int(c.params[0])]; AddDeathDependence(orderTarget); inCommand=true; } else { StopMove(); //cancel keeppointingto FinishCommand(); break; } } else { float3 pos(c.params[0],c.params[1],c.params[2]); SetGoal(pos,curPos); inCommand=true; } } else if ((c.params.size() == 3) && (owner->commandShotCount > 0) && (commandQue.size() > 1)) { StopMove(); FinishCommand(); break; } if(targetDied || (c.params.size() == 1 && uh->units[int(c.params[0])] && !(uh->units[int(c.params[0])]->losStatus[owner->allyteam] & LOS_INRADAR))) { StopMove(); //cancel keeppointingto FinishCommand(); break; } if(orderTarget) { //note that we handle aircrafts slightly differently if((((owner->AttackUnit(orderTarget, c.id==CMD_DGUN) && owner->weapons.size() > 0 && owner->weapons.front()->range > orderTarget->pos.distance(owner->pos)) || dynamic_cast<CTAAirMoveType*>(owner->moveType)) && (owner->pos-orderTarget->pos).Length2D()<owner->maxRange*0.9) || (owner->pos-orderTarget->pos).SqLength2D()<1024) { StopMove(); owner->moveType->KeepPointingTo(orderTarget->pos, min((double)(owner->losRadius*SQUARE_SIZE*2), owner->maxRange*0.9), true); } else if((orderTarget->pos+owner->posErrorVector*128).distance2D(goalPos) > 10+orderTarget->pos.distance2D(owner->pos)*0.2) { float3 fix=orderTarget->pos+owner->posErrorVector*128; SetGoal(fix,curPos); } } else { float3 pos(c.params[0],c.params[1],c.params[2]); if((owner->AttackGround(pos,c.id==CMD_DGUN) && owner->weapons.size() > 0 && (owner->pos-pos).Length()< owner->weapons.front()->range) || (owner->pos-pos).SqLength2D()<1024) { StopMove(); owner->moveType->KeepPointingTo(pos, owner->maxRange*0.9, true); } else if(pos.distance2D(goalPos)>10) { SetGoal(pos,curPos); } } break; case CMD_GUARD: if(uh->units[int(c.params[0])]!=0) { CUnit* guarded=uh->units[int(c.params[0])]; if(guarded->lastAttacker && guarded->lastAttack+40<gs->frameNum && (owner->hasUWWeapons || !guarded->lastAttacker->isUnderWater)) { Command nc; nc.id=CMD_ATTACK; nc.params.push_back(guarded->lastAttacker->id); nc.options=c.options; commandQue.push_front(nc); SlowUpdate(); return; } else { float3 dif=guarded->pos-curPos; dif.Normalize(); float3 goal=guarded->pos-dif*(guarded->radius+owner->radius+64); if((goal-curPos).SqLength2D()<8000) { StopMove(); NonMoving(); } else { if((goalPos-goal).SqLength2D()>3600) SetGoal(goal,curPos); } } } else { FinishCommand(); } break; default: CCommandAI::SlowUpdate(); break; } }
void CAirCAI::ExecuteAttack(Command &c) { assert(owner->unitDef->canAttack); targetAge++; if(tempOrder && owner->moveState == 1){ //limit how far away we fly if(orderTarget && LinePointDist(commandPos1, commandPos2, orderTarget->pos) > 1500){ owner->SetUserTarget(0); FinishCommand(); return; } }/* why was this block here? - ILMTitan if(tempOrder && myPlane->isFighter && orderTarget){ if(owner->fireState == 2 && owner->moveState != 0){ CUnit* enemy = helper->GetClosestEnemyAircraft( owner->pos + (owner->speed * 50), 800, owner->allyteam); if(enemy && (!orderTarget->unitDef->canfly || (orderTarget->pos-owner->pos + owner->speed * 50). Length() + (100 * gs->randFloat() * 40) - targetAge < (enemy->pos-owner->pos + owner->speed * 50).Length())){ c.params.clear(); c.params.push_back(enemy->id); inCommand=false; } } }*/ if(inCommand){ if(targetDied || (c.params.size() == 1 && UpdateTargetLostTimer(int(c.params[0])) == 0)){ FinishCommand(); return; } if ((c.params.size() == 3) && (owner->commandShotCount > 0) && (commandQue.size() > 1)) { owner->AttackUnit(0,true); FinishCommand(); return; } if (orderTarget) { if (orderTarget->unitDef->canfly && orderTarget->crashing) { owner->SetUserTarget(0); FinishCommand(); return; } if (!(c.options & ALT_KEY) && SkipParalyzeTarget(orderTarget)) { owner->SetUserTarget(0); FinishCommand(); return; } } if(orderTarget){ // myPlane->goalPos=orderTarget->pos; } else { // float3 pos(c.params[0],c.params[1],c.params[2]); // myPlane->goalPos=pos; } } else { targetAge=0; owner->commandShotCount = -1; if(c.params.size() == 1){ if(uh->units[int(c.params[0])] != 0 && uh->units[int(c.params[0])] != owner){ orderTarget = uh->units[int(c.params[0])]; owner->AttackUnit(orderTarget, false); AddDeathDependence(orderTarget); inCommand = true; } else { FinishCommand(); return; } } else { float3 pos(c.params[0],c.params[1],c.params[2]); owner->AttackGround(pos,false); inCommand = true; } } return; }
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); } }
void CAirCAI::SlowUpdate() { if(!commandQue.empty() && commandQue.front().timeOut<gs->frameNum){ FinishCommand(); return; } CAirMoveType* myPlane=(CAirMoveType*)owner->moveType; if(owner->unitDef->maxFuel>0){ if(myPlane->reservedPad){ return; }else{ if(owner->currentFuel <= 0){ owner->userAttackGround=false; owner->userTarget=0; inCommand=false; CAirBaseHandler::LandingPad* lp=airBaseHandler->FindAirBase(owner,8000,owner->unitDef->minAirBasePower); if(lp){ myPlane->AddDeathDependence(lp); myPlane->reservedPad=lp; myPlane->padStatus=0; myPlane->oldGoalPos=myPlane->goalPos; return; } float3 landingPos = airBaseHandler->FindClosestAirBasePos(owner,8000,owner->unitDef->minAirBasePower); if(landingPos != ZeroVector && owner->pos.distance2D(landingPos) > 300){ if(myPlane->aircraftState == CAirMoveType::AIRCRAFT_LANDED && owner->pos.distance2D(landingPos) > 800) myPlane->SetState(CAirMoveType::AIRCRAFT_TAKEOFF); myPlane->goalPos=landingPos; } else { if(myPlane->aircraftState == CAirMoveType::AIRCRAFT_FLYING) myPlane->SetState(CAirMoveType::AIRCRAFT_LANDING); } return; } if(owner->currentFuel < myPlane->repairBelowHealth*owner->unitDef->maxFuel){ if(commandQue.empty() || commandQue.front().id==CMD_PATROL){ CAirBaseHandler::LandingPad* lp=airBaseHandler->FindAirBase(owner,8000,owner->unitDef->minAirBasePower); if(lp){ owner->userAttackGround=false; owner->userTarget=0; inCommand=false; myPlane->AddDeathDependence(lp); myPlane->reservedPad=lp; myPlane->padStatus=0; myPlane->oldGoalPos=myPlane->goalPos; if(myPlane->aircraftState==CAirMoveType::AIRCRAFT_LANDED){ myPlane->SetState(CAirMoveType::AIRCRAFT_TAKEOFF); } return; } } } } } if(commandQue.empty()){ if(myPlane->aircraftState==CAirMoveType::AIRCRAFT_FLYING && !owner->unitDef->DontLand ()) myPlane->SetState(CAirMoveType::AIRCRAFT_LANDING); if(owner->fireState==2 && owner->moveState!=0 && owner->maxRange>0){ if(myPlane->isFighter){ float testRad=1000*owner->moveState; CUnit* enemy=helper->GetClosestEnemyAircraft(owner->pos+owner->speed*10,testRad,owner->allyteam); if(enemy && !enemy->crashing){ Command nc; nc.id=CMD_ATTACK; nc.params.push_back(enemy->id); nc.options=0; commandQue.push_front(nc); inCommand=false; return; } } if(owner->moveState && owner->fireState==2 && owner->maxRange>0){ float testRad=500*owner->moveState; CUnit* enemy=helper->GetClosestEnemyUnit(owner->pos+owner->speed*20,testRad,owner->allyteam); if(enemy && (owner->hasUWWeapons || !enemy->isUnderWater) && !enemy->crashing && (myPlane->isFighter || !enemy->unitDef->canfly)){ Command nc; nc.id=CMD_ATTACK; nc.params.push_back(enemy->id); nc.options=0; commandQue.push_front(nc); inCommand=false; return; } } } return; } if(myPlane->aircraftState==CAirMoveType::AIRCRAFT_LANDED && commandQue.front().id!=CMD_STOP){ myPlane->SetState(CAirMoveType::AIRCRAFT_TAKEOFF); } if(myPlane->aircraftState==CAirMoveType::AIRCRAFT_LANDING && commandQue.front().id!=CMD_STOP){ myPlane->SetState(CAirMoveType::AIRCRAFT_FLYING); } float3 curPos=owner->pos; Command& c=commandQue.front(); switch(c.id){ case CMD_STOP:{ CCommandAI::SlowUpdate(); break;} case CMD_MOVE:{ if(tempOrder){ tempOrder=false; inCommand=true; } if(!inCommand){ inCommand=true; commandPos1=myPlane->owner->pos; } float3 pos(c.params[0],c.params[1],c.params[2]); commandPos2=pos; myPlane->goalPos=pos; if(!(c.options&CONTROL_KEY)){ if(owner->fireState==2 && owner->moveState!=0 && owner->maxRange>0){ if(myPlane->isFighter){ float testRad=500*owner->moveState; CUnit* enemy=helper->GetClosestEnemyAircraft(owner->pos+owner->speed*20,testRad,owner->allyteam); if(enemy && ((enemy->unitDef->canfly && !enemy->crashing && myPlane->isFighter) || (!enemy->unitDef->canfly && (!myPlane->isFighter || owner->moveState==2)))){ if(owner->moveState!=1 || LinePointDist(commandPos1,commandPos2,enemy->pos) < 1000){ Command nc; nc.id=CMD_ATTACK; nc.params.push_back(enemy->id); nc.options=c.options; commandQue.push_front(nc); tempOrder=true; inCommand=false; SlowUpdate(); return; } } } if((!myPlane->isFighter || owner->moveState==2) && owner->maxRange>0){ float testRad=325*owner->moveState; CUnit* enemy=helper->GetClosestEnemyUnit(owner->pos+owner->speed*30,testRad,owner->allyteam); if(enemy && (owner->hasUWWeapons || !enemy->isUnderWater) && ((enemy->unitDef->canfly && !enemy->crashing && myPlane->isFighter) || (!enemy->unitDef->canfly && (!myPlane->isFighter || owner->moveState==2)))){ if(owner->moveState!=1 || LinePointDist(commandPos1,commandPos2,enemy->pos) < 1000){ Command nc; nc.id=CMD_ATTACK; nc.params.push_back(enemy->id); nc.options=c.options; commandQue.push_front(nc); tempOrder=true; inCommand=false; SlowUpdate(); return; } } } } } if((curPos-pos).SqLength2D()<16000){ FinishCommand(); } break;} case CMD_PATROL:{ float3 curPos=owner->pos; if(c.params.size()<3){ //this shouldnt happen but anyway ... info->AddLine("Error: got patrol cmd with less than 3 params on %s in aircai", owner->unitDef->humanName.c_str()); return; } Command temp; temp.id = CMD_FIGHT; temp.params.push_back(c.params[0]); temp.params.push_back(c.params[1]); temp.params.push_back(c.params[2]); temp.options = c.options|INTERNAL_ORDER; commandQue.push_back(c); commandQue.pop_front(); commandQue.push_front(temp); if(owner->group){ owner->group->CommandFinished(owner->id,CMD_PATROL); } SlowUpdate(); break;} case CMD_FIGHT:{ if(tempOrder){ tempOrder=false; inCommand=true; } goalPos=float3(c.params[0],c.params[1],c.params[2]); if(!inCommand){ commandPos2=goalPos; inCommand=true; } commandPos1=curPos; if(c.params.size()<3){ //this shouldnt happen but anyway ... info->AddLine("Error: got fight cmd with less than 3 params on %s in aircai", owner->unitDef->humanName.c_str()); return; } float testRad=1000*owner->moveState; CUnit* enemy = helper->GetClosestEnemyAircraft(owner->pos+owner->speed*10,testRad,owner->allyteam); if(owner->fireState==2 && owner->moveState!=0 && owner->maxRange>0){ if(myPlane->isFighter && enemy && !enemy->crashing && (owner->moveState!=1 || LinePointDist(commandPos1,commandPos2,enemy->pos) < 1000)) { Command nc,c3; c3.id = CMD_MOVE; //keep it from being pulled too far off the path float3 dir = goalPos-curPos; dir.Normalize(); dir*=sqrtf(1024+owner->xsize*owner->xsize+owner->ysize*owner->ysize); dir+=curPos; c3.params.push_back(dir.x); c3.params.push_back(dir.y); c3.params.push_back(dir.z); c3.options = c.options|INTERNAL_ORDER; nc.id=CMD_ATTACK; nc.params.push_back(enemy->id); nc.options=c.options|INTERNAL_ORDER; commandQue.push_front(nc); tempOrder=true; inCommand=false; if(lastPC1!=gs->frameNum){ //avoid infinite loops lastPC1=gs->frameNum; SlowUpdate(); } return; } else if(owner->maxRange>0){ float testRad=500*owner->moveState; enemy=helper->GetClosestEnemyUnit(owner->pos+owner->speed*20,testRad,owner->allyteam); if(enemy && (owner->hasUWWeapons || !enemy->isUnderWater) && !enemy->crashing && (myPlane->isFighter || !enemy->unitDef->canfly)) { if(owner->moveState!=1 || LinePointDist(commandPos1,commandPos2,enemy->pos) < 1000){ Command nc,c3; c3.id = CMD_MOVE; //keep it from being pulled too far off the path float3 dir = goalPos-curPos; dir.Normalize(); dir*=sqrtf(1024+owner->xsize*owner->xsize+owner->ysize*owner->ysize); dir+=curPos; c3.params.push_back(dir.x); c3.params.push_back(dir.y); c3.params.push_back(dir.z); c3.options = c.options|INTERNAL_ORDER; nc.id=CMD_ATTACK; nc.params.push_back(enemy->id); nc.options=c.options|INTERNAL_ORDER; commandQue.push_front(c3); commandQue.push_front(nc); tempOrder=true; inCommand=false; if(lastPC2!=gs->frameNum){ //avoid infinite loops lastPC2=gs->frameNum; SlowUpdate(); } return; } } } } myPlane->goalPos=goalPos; if((owner->pos-goalPos).SqLength2D()<16000 || (owner->pos+owner->speed*8-goalPos).SqLength2D()<16000){ FinishCommand(); } return;} case CMD_ATTACK: targetAge++; if(tempOrder && owner->moveState==1){ //limit how far away we fly if(orderTarget && LinePointDist(commandPos1,commandPos2,orderTarget->pos) > 1500){ owner->SetUserTarget(0); FinishCommand(); break; } } if(tempOrder && myPlane->isFighter && orderTarget){ if(owner->fireState==2 && owner->moveState!=0){ CUnit* enemy=helper->GetClosestEnemyAircraft(owner->pos+owner->speed*50,800,owner->allyteam); if(enemy && (!orderTarget->unitDef->canfly || (orderTarget->pos-owner->pos+owner->speed*50).Length()+100*gs->randFloat()*40-targetAge < (enemy->pos-owner->pos+owner->speed*50).Length())){ c.params.clear(); c.params.push_back(enemy->id); inCommand=false; } } } if(inCommand){ if(targetDied || (c.params.size() == 1 && uh->units[int(c.params[0])] && !(uh->units[int(c.params[0])]->losStatus[owner->allyteam] & LOS_INRADAR))){ FinishCommand(); break; } if ((c.params.size() == 3) && (owner->commandShotCount > 0) && (commandQue.size() > 1)) { owner->AttackUnit(0,true); FinishCommand(); break; } if(orderTarget && orderTarget->unitDef->canfly && orderTarget->crashing){ owner->SetUserTarget(0); FinishCommand(); break; } if(orderTarget){ // myPlane->goalPos=orderTarget->pos; } else { // float3 pos(c.params[0],c.params[1],c.params[2]); // myPlane->goalPos=pos; } } else { targetAge=0; if(c.params.size()==1){ if(uh->units[int(c.params[0])]!=0 && uh->units[int(c.params[0])]!=owner){ orderTarget=uh->units[int(c.params[0])]; owner->AttackUnit(orderTarget,false); AddDeathDependence(orderTarget); inCommand=true; } else { FinishCommand(); break; } } else { float3 pos(c.params[0],c.params[1],c.params[2]); owner->AttackGround(pos,false); inCommand=true; } } break; case CMD_AREA_ATTACK:{ if(targetDied){ targetDied=false; inCommand=false; } float3 pos(c.params[0],c.params[1],c.params[2]); float radius=c.params[3]; if(inCommand){ if(myPlane->aircraftState==CAirMoveType::AIRCRAFT_LANDED) inCommand=false; if(orderTarget && orderTarget->pos.distance2D(pos)>radius){ inCommand=false; DeleteDeathDependence(orderTarget); orderTarget=0; } if ((c.params.size() == 4) && (owner->commandShotCount > 0) && (commandQue.size() > 1)) { owner->AttackUnit(0,true); FinishCommand(); } } else { if(myPlane->aircraftState!=CAirMoveType::AIRCRAFT_LANDED){ inCommand=true; std::vector<int> eu; helper->GetEnemyUnits(pos,radius,owner->allyteam,eu); if(eu.empty()){ float3 attackPos=pos+gs->randVector()*radius; attackPos.y=ground->GetHeight(attackPos.x,attackPos.z); owner->AttackGround(attackPos,false); } else { int num=(int) (gs->randFloat()*eu.size()); orderTarget=uh->units[eu[num]]; owner->AttackUnit(orderTarget,false); AddDeathDependence(orderTarget); } } } break;} case CMD_GUARD: if(uh->units[int(c.params[0])]!=0){ CUnit* guarded=uh->units[int(c.params[0])]; if(guarded->lastAttacker && guarded->lastAttack+40<gs->frameNum && owner->maxRange>0 && (owner->hasUWWeapons || !guarded->lastAttacker->isUnderWater)){ Command nc; nc.id=CMD_ATTACK; nc.params.push_back(guarded->lastAttacker->id); nc.options=c.options; commandQue.push_front(nc); SlowUpdate(); return; } else { Command c2; c2.id=CMD_MOVE; c2.options=c.options; c2.params.push_back(guarded->pos.x); c2.params.push_back(guarded->pos.y); c2.params.push_back(guarded->pos.z); c2.timeOut=gs->frameNum+60; commandQue.push_front(c2); return; } } else { FinishCommand(); } break; default: CCommandAI::SlowUpdate(); break; } }
void CAirCAI::ExecuteFight(Command& c) { assert((c.options & INTERNAL_ORDER) || owner->unitDef->canFight); // FIXME: check owner->UsingScriptMoveType() and skip rest if true? AAirMoveType* myPlane = GetStrafeAirMoveType(owner); assert(owner == myPlane->owner); if (tempOrder) { tempOrder = false; inCommand = true; } if (c.params.size() < 3) { LOG_L(L_ERROR, "[ACAI::%s][f=%d][id=%d] CMD_FIGHT #params < 3", __FUNCTION__, gs->frameNum, owner->id); return; } if (c.params.size() >= 6) { if (!inCommand) { commandPos1 = c.GetPos(3); } } else { // HACK 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()<(127*127)){' // 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() > (150 * 150)) { commandPos1 = owner->pos; } } goalPos = c.GetPos(0); if (!inCommand) { inCommand = true; commandPos2 = goalPos; } if (c.params.size() >= 6) { goalPos = ClosestPointOnLine(commandPos1, commandPos2, owner->pos); } // CMD_FIGHT is pretty useless if !canAttack, but we try to honour the modders wishes anyway... if (owner->unitDef->canAttack && (owner->fireState >= FIRESTATE_FIREATWILL) && (owner->moveState != MOVESTATE_HOLDPOS) && (owner->maxRange > 0)) { CUnit* enemy = NULL; if (owner->unitDef->IsFighterAirUnit()) { const float3 P = ClosestPointOnLine(commandPos1, commandPos2, owner->pos + owner->speed*10); const float R = 1000.0f * owner->moveState; enemy = CGameHelper::GetClosestEnemyAircraft(NULL, P, R, owner->allyteam); } if (IsValidTarget(enemy) && (owner->moveState != MOVESTATE_MANEUVER || LinePointDist(commandPos1, commandPos2, enemy->pos) < 1000)) { // make the attack-command inherit <c>'s options // (if <c> is internal, then so are the attacks) // // this is needed because CWeapon code will not // fire on "internal" targets if the weapon has // noAutoTarget set (although the <enemy> CUnit* // is technically not a user-target, we treat it // as such) even when explicitly told to fight Command nc(CMD_ATTACK, c.options, enemy->id); commandQue.push_front(nc); tempOrder = true; inCommand = false; if (lastPC1 != gs->frameNum) { // avoid infinite loops lastPC1 = gs->frameNum; SlowUpdate(); } return; } else { const float3 P = ClosestPointOnLine(commandPos1, commandPos2, owner->pos + owner->speed * 20); const float R = 500.0f * owner->moveState; enemy = CGameHelper::GetClosestValidTarget(P, R, owner->allyteam, this); if (enemy != NULL) { PushOrUpdateReturnFight(); // make the attack-command inherit <c>'s options Command nc(CMD_ATTACK, c.options, enemy->id); commandQue.push_front(nc); tempOrder = true; inCommand = false; // avoid infinite loops (?) if (lastPC2 != gs->frameNum) { lastPC2 = gs->frameNum; SlowUpdate(); } return; } } } myPlane->goalPos = goalPos; const CStrafeAirMoveType* airMT = (!owner->UsingScriptMoveType())? static_cast<const CStrafeAirMoveType*>(myPlane): NULL; const float radius = (airMT != NULL)? std::max(airMT->turnRadius + 2*SQUARE_SIZE, 128.f) : 127.f; // we're either circling or will get to the target in 8 frames if ((owner->pos - goalPos).SqLength2D() < (radius * radius) || (owner->pos + owner->speed*8 - goalPos).SqLength2D() < 127*127) { FinishCommand(); } }
void CAirCAI::SlowUpdate() { if(!commandQue.empty() && commandQue.front().timeOut<gs->frameNum){ FinishCommand(); return; } CAirMoveType* myPlane=(CAirMoveType*)owner->moveType; if(owner->unitDef->maxFuel>0){ if(myPlane->reservedPad){ return; }else{ if(owner->currentFuel <= 0){ owner->userAttackGround=false; owner->userTarget=0; inCommand=false; CAirBaseHandler::LandingPad* lp=airBaseHandler->FindAirBase(owner,8000,owner->unitDef->minAirBasePower); if(lp){ myPlane->AddDeathDependence(lp); myPlane->reservedPad=lp; myPlane->padStatus=0; myPlane->oldGoalPos=myPlane->goalPos; return; } float3 landingPos = airBaseHandler->FindClosestAirBasePos(owner,8000,owner->unitDef->minAirBasePower); if(landingPos != ZeroVector && owner->pos.distance2D(landingPos) > 300){ if(myPlane->aircraftState == CAirMoveType::AIRCRAFT_LANDED && owner->pos.distance2D(landingPos) > 800) myPlane->SetState(CAirMoveType::AIRCRAFT_TAKEOFF); myPlane->goalPos=landingPos; } else { if(myPlane->aircraftState == CAirMoveType::AIRCRAFT_FLYING) myPlane->SetState(CAirMoveType::AIRCRAFT_LANDING); } return; } if(owner->currentFuel < myPlane->repairBelowHealth*owner->unitDef->maxFuel){ if(commandQue.empty() || commandQue.front().id==CMD_PATROL){ CAirBaseHandler::LandingPad* lp=airBaseHandler->FindAirBase(owner,8000,owner->unitDef->minAirBasePower); if(lp){ owner->userAttackGround=false; owner->userTarget=0; inCommand=false; myPlane->AddDeathDependence(lp); myPlane->reservedPad=lp; myPlane->padStatus=0; myPlane->oldGoalPos=myPlane->goalPos; if(myPlane->aircraftState==CAirMoveType::AIRCRAFT_LANDED){ myPlane->SetState(CAirMoveType::AIRCRAFT_TAKEOFF); } return; } } } } } if(commandQue.empty()){ if(myPlane->aircraftState==CAirMoveType::AIRCRAFT_FLYING && !owner->unitDef->DontLand ()) myPlane->SetState(CAirMoveType::AIRCRAFT_LANDING); if(owner->unitDef->canAttack && owner->fireState==2 && owner->moveState!=0 && owner->maxRange>0){ if(myPlane->isFighter){ float testRad=1000*owner->moveState; CUnit* enemy=helper->GetClosestEnemyAircraft(owner->pos+owner->speed*10,testRad,owner->allyteam); if(enemy && !enemy->crashing){ Command nc; nc.id=CMD_ATTACK; nc.params.push_back(enemy->id); nc.options=0; commandQue.push_front(nc); inCommand=false; return; } } float testRad=500*owner->moveState; CUnit* enemy=helper->GetClosestEnemyUnit(owner->pos+owner->speed*20,testRad,owner->allyteam); if(enemy && (owner->hasUWWeapons || !enemy->isUnderWater) && !enemy->crashing && (myPlane->isFighter || !enemy->unitDef->canfly)){ Command nc; nc.id=CMD_ATTACK; nc.params.push_back(enemy->id); nc.options=0; commandQue.push_front(nc); inCommand=false; return; } } return; } Command& c = commandQue.front(); if (c.id == CMD_WAIT) { if ((myPlane->aircraftState == CAirMoveType::AIRCRAFT_FLYING) && !owner->unitDef->DontLand()) { myPlane->SetState(CAirMoveType::AIRCRAFT_LANDING); } return; } if (c.id != CMD_STOP) { if (myPlane->aircraftState == CAirMoveType::AIRCRAFT_LANDED) { myPlane->SetState(CAirMoveType::AIRCRAFT_TAKEOFF); } if (myPlane->aircraftState == CAirMoveType::AIRCRAFT_LANDING) { myPlane->SetState(CAirMoveType::AIRCRAFT_FLYING); } } float3 curPos=owner->pos; switch(c.id){ case CMD_STOP:{ CCommandAI::SlowUpdate(); break; } case CMD_MOVE:{ if(tempOrder){ tempOrder=false; inCommand=true; } if(!inCommand){ inCommand=true; commandPos1=myPlane->owner->pos; } float3 pos(c.params[0],c.params[1],c.params[2]); commandPos2=pos; myPlane->goalPos=pos; if(owner->unitDef->canAttack && !(c.options&CONTROL_KEY)){ if(owner->fireState==2 && owner->moveState!=0 && owner->maxRange>0){ if(myPlane->isFighter){ float testRad=500*owner->moveState; CUnit* enemy=helper->GetClosestEnemyAircraft(owner->pos+owner->speed*20,testRad,owner->allyteam); if(enemy && ((enemy->unitDef->canfly && !enemy->crashing && myPlane->isFighter) || (!enemy->unitDef->canfly && (!myPlane->isFighter || owner->moveState==2)))){ if(owner->moveState!=1 || LinePointDist(commandPos1,commandPos2,enemy->pos) < 1000){ Command nc; nc.id=CMD_ATTACK; nc.params.push_back(enemy->id); nc.options=c.options; commandQue.push_front(nc); tempOrder=true; inCommand=false; SlowUpdate(); return; } } } if((!myPlane->isFighter || owner->moveState==2) && owner->maxRange>0){ float testRad=325*owner->moveState; CUnit* enemy=helper->GetClosestEnemyUnit(owner->pos+owner->speed*30,testRad,owner->allyteam); if(enemy && (owner->hasUWWeapons || !enemy->isUnderWater) && ((enemy->unitDef->canfly && !enemy->crashing && myPlane->isFighter) || (!enemy->unitDef->canfly && (!myPlane->isFighter || owner->moveState==2)))){ if(owner->moveState!=1 || LinePointDist(commandPos1,commandPos2,enemy->pos) < 1000){ Command nc; nc.id=CMD_ATTACK; nc.params.push_back(enemy->id); nc.options=c.options; commandQue.push_front(nc); tempOrder=true; inCommand=false; SlowUpdate(); return; } } } } } if((curPos-pos).SqLength2D()<16000){ FinishCommand(); } break;} case CMD_PATROL:{ assert(owner->unitDef->canPatrol); float3 curPos=owner->pos; if(c.params.size()<3){ //this shouldnt happen but anyway ... logOutput.Print("Error: got patrol cmd with less than 3 params on %s in aircai", owner->unitDef->humanName.c_str()); return; } Command temp; temp.id = CMD_FIGHT; temp.params.push_back(c.params[0]); temp.params.push_back(c.params[1]); temp.params.push_back(c.params[2]); temp.options = c.options|INTERNAL_ORDER; commandQue.push_back(c); commandQue.pop_front(); commandQue.push_front(temp); if(owner->group){ owner->group->CommandFinished(owner->id,CMD_PATROL); } SlowUpdate(); break;} case CMD_FIGHT:{ assert((c.options & INTERNAL_ORDER) || owner->unitDef->canFight); if(tempOrder){ tempOrder=false; inCommand=true; } if(c.params.size()<3){ //this shouldnt happen but anyway ... logOutput.Print("Error: got fight cmd with less than 3 params on %s in AirCAI", 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()<(127*127)){' // 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, curPos); if ((curPos-commandPos1).SqLength2D()>(150*150)) { commandPos1 = curPos; } } goalPos=float3(c.params[0],c.params[1],c.params[2]); if(!inCommand){ inCommand = true; commandPos2=goalPos; } // CMD_FIGHT is pretty useless if !canAttack but we try to honour the modders wishes anyway... if (owner->unitDef->canAttack && owner->fireState == 2 && owner->moveState != 0 && owner->maxRange > 0) { float3 curPosOnLine = ClosestPointOnLine(commandPos1, commandPos2, owner->pos+owner->speed*10); float testRad=1000*owner->moveState; CUnit* enemy = helper->GetClosestEnemyAircraft(curPosOnLine,testRad,owner->allyteam); if(myPlane->isFighter && enemy && !enemy->crashing && (owner->moveState!=1 || LinePointDist(commandPos1,commandPos2,enemy->pos) < 1000)) { Command nc; nc.id=CMD_ATTACK; nc.params.push_back(enemy->id); nc.options=c.options|INTERNAL_ORDER; commandQue.push_front(nc); tempOrder=true; inCommand=false; if(lastPC1!=gs->frameNum){ //avoid infinite loops lastPC1=gs->frameNum; SlowUpdate(); } return; } else { float3 curPosOnLine = ClosestPointOnLine(commandPos1, commandPos2, owner->pos+owner->speed*20); float testRad=500*owner->moveState; enemy=helper->GetClosestEnemyUnit(curPosOnLine,testRad,owner->allyteam); if(enemy && (owner->hasUWWeapons || !enemy->isUnderWater) && !enemy->crashing && (myPlane->isFighter || !enemy->unitDef->canfly)) { Command nc; nc.id=CMD_ATTACK; nc.params.push_back(enemy->id); nc.options=c.options|INTERNAL_ORDER; PushOrUpdateReturnFight(); commandQue.push_front(nc); tempOrder=true; inCommand=false; if(lastPC2!=gs->frameNum){ //avoid infinite loops lastPC2=gs->frameNum; SlowUpdate(); } return; } } } myPlane->goalPos=goalPos; if((owner->pos-goalPos).SqLength2D()<(127*127) || (owner->pos+owner->speed*8-goalPos).SqLength2D()<(127*127)){ FinishCommand(); } return;} case CMD_ATTACK: assert(owner->unitDef->canAttack); targetAge++; if(tempOrder && owner->moveState==1){ //limit how far away we fly if(orderTarget && LinePointDist(commandPos1,commandPos2,orderTarget->pos) > 1500){ owner->SetUserTarget(0); FinishCommand(); break; } } if(tempOrder && myPlane->isFighter && orderTarget){ if(owner->fireState==2 && owner->moveState!=0){ CUnit* enemy=helper->GetClosestEnemyAircraft(owner->pos+owner->speed*50,800,owner->allyteam); if(enemy && (!orderTarget->unitDef->canfly || (orderTarget->pos-owner->pos+owner->speed*50).Length()+100*gs->randFloat()*40-targetAge < (enemy->pos-owner->pos+owner->speed*50).Length())){ c.params.clear(); c.params.push_back(enemy->id); inCommand=false; } } } if(inCommand){ if(targetDied || (c.params.size() == 1 && UpdateTargetLostTimer(int(c.params[0])) == 0)){ FinishCommand(); break; } if ((c.params.size() == 3) && (owner->commandShotCount > 0) && (commandQue.size() > 1)) { owner->AttackUnit(0,true); FinishCommand(); break; } if(orderTarget && orderTarget->unitDef->canfly && orderTarget->crashing){ owner->SetUserTarget(0); FinishCommand(); break; } if(orderTarget){ // myPlane->goalPos=orderTarget->pos; } else { // float3 pos(c.params[0],c.params[1],c.params[2]); // myPlane->goalPos=pos; } } else { targetAge=0; if(c.params.size()==1){ if(uh->units[int(c.params[0])]!=0 && uh->units[int(c.params[0])]!=owner){ orderTarget=uh->units[int(c.params[0])]; owner->AttackUnit(orderTarget,false); AddDeathDependence(orderTarget); inCommand=true; } else { FinishCommand(); break; } } else { float3 pos(c.params[0],c.params[1],c.params[2]); owner->AttackGround(pos,false); inCommand=true; } } break; case CMD_AREA_ATTACK:{ assert(owner->unitDef->canAttack); if(targetDied){ targetDied=false; inCommand=false; } float3 pos(c.params[0],c.params[1],c.params[2]); float radius=c.params[3]; if(inCommand){ if(myPlane->aircraftState==CAirMoveType::AIRCRAFT_LANDED) inCommand=false; if(orderTarget && orderTarget->pos.distance2D(pos)>radius){ inCommand=false; DeleteDeathDependence(orderTarget); orderTarget=0; } if ((c.params.size() == 4) && (owner->commandShotCount > 0) && (commandQue.size() > 1)) { owner->AttackUnit(0,true); FinishCommand(); } } else { if(myPlane->aircraftState!=CAirMoveType::AIRCRAFT_LANDED){ inCommand=true; std::vector<int> eu; helper->GetEnemyUnits(pos,radius,owner->allyteam,eu); if(eu.empty()){ float3 attackPos=pos+gs->randVector()*radius; attackPos.y=ground->GetHeight(attackPos.x,attackPos.z); owner->AttackGround(attackPos,false); } else { int num=(int) (gs->randFloat()*eu.size()); orderTarget=uh->units[eu[num]]; owner->AttackUnit(orderTarget,false); AddDeathDependence(orderTarget); } } } break;} case CMD_GUARD: assert(owner->unitDef->canGuard); if (int(c.params[0]) >= 0 && uh->units[int(c.params[0])] != NULL && UpdateTargetLostTimer(int(c.params[0]))) { CUnit* guarded=uh->units[int(c.params[0])]; if(owner->unitDef->canAttack && guarded->lastAttacker && guarded->lastAttack+40<gs->frameNum && owner->maxRange>0 && (owner->hasUWWeapons || !guarded->lastAttacker->isUnderWater)){ Command nc; nc.id=CMD_ATTACK; nc.params.push_back(guarded->lastAttacker->id); nc.options=c.options; commandQue.push_front(nc); SlowUpdate(); return; } else { Command c2; c2.id=CMD_MOVE; c2.options=c.options; c2.params.push_back(guarded->pos.x); c2.params.push_back(guarded->pos.y); c2.params.push_back(guarded->pos.z); c2.timeOut=gs->frameNum+60; commandQue.push_front(c2); return; } } else { FinishCommand(); } break; default: CCommandAI::SlowUpdate(); break; } }