int CheckCollisionGround( vector<z_face> &collision, vec center, float radius, float angle, float mindist) { float ground_skew = (float)cos(angle*PI180)*radius; vec vpold_vp = mindist*vec(0,-1,0); for( int i=0; i<collision.size(); i++) { vec normal = collision[i].normal; float distance = PlaneDistance( normal, collision[i].a); // D = - (Ax+By+Cz) // --------------------------------------------------------- // najdeme kolidujuci bod na guli vec ClosestPointOnSphere; // najblizsi bod gule k rovine aktualneho trojuholnika // najdeme ho ako priesecnik priamky prechadzajucej stredom gule a smerovym vektorom = normalovemu vektoru roviny (trojuholnika) // vypocitame ho, ale jednodusie tak, ze pripocitame (opocitame) k stredu vektor normala*radius if( PlanePointDelta( normal, distance, center) > 0 ) { // center je na strane normaly, blizsi bod je v opacnom smere ako smer normaly ClosestPointOnSphere = -radius*normal+center; } else { // center je na opacnej strane ako normala, blizsi bod je v smere normaly ClosestPointOnSphere = radius*normal+center; } // --------------------------------------------------------- // najdeme kolidujuci bod na trojuholniku // najprv najdeme kolidujuci bod vzhladom na rovinu v ktorej lezi trojuholnik vec contactPointSphereToPlane; // kolidujuci bod na rovine trojuholnika s gulou float distanceCenterToPlane; // vzdialenost stredu gule k rovine // zistime ci rovina pretina gulu if( SpherePlaneCollision( center, 0.9999f*radius, normal, distance, &distanceCenterToPlane)==1 ) { // gula pretina rovinu // kolidujuci bod je bod na rovine najblizsi k stredu gule // je vzdialeny od roviny na distanceCenterToPlane, pretoze pocitame bod na rovine pouzijeme - contactPointSphereToPlane = center-distanceCenterToPlane*normal; } else { // nie sme v kolizii z gulov, ak sa pohybujeme v smere od roviny, nemoze nastat ziadna kolizia // ak sa pohybujeme v smere kolmom na normalovy vektor roviny, tak isto kolizia nehrozi // kvoli nepresnosti vypoctov umoznime pohyb aj ked velmi malou castou smeruje do roviny // if( DOT3( vpold_vp, center-ClosestPointOnSphere) >= 0) if( DOT3( vpold_vp, center-ClosestPointOnSphere) > -0.000001f) { continue; } // gula nepretina rovinu // kolidujuci bod je priesecnik roviny a priamky vedenej z bodu ClosestPointOnSphere // v smere pohybu t.j. z vpold do vp float t = LinePlaneIntersectionDirParameter( ClosestPointOnSphere, vpold_vp, normal, distance); // t > 1.f, priesecnik z rovinou je dalej ako vpold_vp od bodu ClosestPointOnSphere if(t>1.f) continue; // za cely krok vpold_vp sa s tymto trojuholnikom nestretneme else if( t<-radius/vpold_vp.Length()) // priesecnik je za gulou, v smere pohybu tuto rovinu nestretneme continue; else contactPointSphereToPlane = ClosestPointOnSphere+t*vpold_vp; } // najdeme kolidujuci bod na trojuholniku vec contactPointSphereToTriangle; // ak sa bod contactPointSphereToPlane nenachadza v trojuholniku // najdeme najblizsi bod trojuholnika k bodu contactPointSphereToTriangle if( !PointInsidePolygon( contactPointSphereToPlane, collision[i].a, collision[i].b, collision[i].c) ) { // najdeme najblizsi bod k contactPointSphereToPlane na hranach trojuholnika // z tychto vyberieme najblizi k contactPointSphereToPlane vec closest_ab = ClosestPointOnLine( collision[i].a, collision[i].b, contactPointSphereToPlane); vec closest_bc = ClosestPointOnLine( collision[i].b, collision[i].c, contactPointSphereToPlane); vec closest_ca = ClosestPointOnLine( collision[i].c, collision[i].a, contactPointSphereToPlane); float dist_ab = Distance2( closest_ab, contactPointSphereToPlane); float dist_bc = Distance2( closest_bc, contactPointSphereToPlane); float dist_ca = Distance2( closest_ca, contactPointSphereToPlane); if( dist_ab<dist_bc) { if(dist_ab<dist_ca) contactPointSphereToTriangle = closest_ab; else contactPointSphereToTriangle = closest_ca; } else { if(dist_bc<dist_ca) contactPointSphereToTriangle = closest_bc; else contactPointSphereToTriangle = closest_ca; } // kedze kolidujuci bod na trojuholniku je iny ako kolidujuci bod na rovine // zmeni sa aj kolidujuci bod na guli - ClosestPointOnSphere // vypocitame ho ako priesecnik gule a priamky z bodu contactPointSphereToTriangle // v smere pohybu t.j. z vpold do vp double t1,t2; if( LineSphereIntersectionDir( contactPointSphereToTriangle, vpold_vp, center, radius, &t1, &t2) ) { if( t1<=0 && t2<0) { // gula je pred trojuholnikom // berieme bod s t1, lebo ten je blizsie k stene (t1>t2) if( t1<-1.f)continue; // tento trojuholnik nas nezaujima lebo nekoliduje po cely tento krok ClosestPointOnSphere = t1*vpold_vp+contactPointSphereToTriangle; if( (center.y-ClosestPointOnSphere.y)>ground_skew )return 1; else continue; // mozeme sa pohnut iba tolko pokial sa colidujuci bod na guli nedotkne // kolidujuceho bodu na trojuholniku // vp_move = contactPointSphereToTriangle - ClosestPointOnSphere; } else if( t1>0 && t2<0) { // gula je v stene, vratime ju von zo steny // berieme bod, ktory je blizsie k rovine vec t1point = t1*vpold_vp+contactPointSphereToTriangle; vec t2point = t2*vpold_vp+contactPointSphereToTriangle; /* if(PlanePointDistance( normal, distance, t1point)<=PlanePointDistance( normal, distance, t2point) ) ClosestPointOnSphere = t1point; else ClosestPointOnSphere = t2point; */ if( ABS(t1) < ABS(t2) ) ClosestPointOnSphere = t1point; else ClosestPointOnSphere = t2point; if( (center.y-ClosestPointOnSphere.y)>ground_skew )return 1; else continue; // mozeme sa pohnut iba tolko pokial sa colidujuci bod na guli nedotkne // kolidujuceho bodu na trojuholniku // vp_move = contactPointSphereToTriangle - ClosestPointOnSphere; } else // if( t1>0 && t2>0) { // gula je za trojuholnikom, gula nekoliduje s trojuholnikom v tomto smere pohybu continue; } } else { // nie je priesecnik, gula je mimo trojuholnika continue; } } else { if( (center.y-ClosestPointOnSphere.y)>ground_skew )return 1; else continue; // bod je vnutri trojuholnika // contactPointSphereToTriangle = contactPointSphereToPlane; // mozeme sa pohnut iba tolko pokial sa colidujuci bod na guli nedotkne // kolidujuceho bodu na trojuholniku // vp_move = contactPointSphereToTriangle - ClosestPointOnSphere; } /* if( LineSphereIntersectionDir( contactPointSphereToTriangle, vpold_vp, center, radius, &t1, &t2) ) { if( t1<=0 && t2<0) { // gula je pred trojuholnikom // berieme bod s t1, lebo ten je blizsie k stene (t1>t2) if( t1<-1.f)continue; // tento trojuholnik nas nezaujima lebo nekoliduje po cely tento krok return 1; } else if( t1>0 && t2<0) { return 1; // gula je v stene } else // if( t1>0 && t2>0) { // gula je za trojuholnikom, gula nekoliduje s trojuholnikom v tomto smere pohybu continue; } } else { // nie je priesecnik, gula je mimo trojuholnika continue; } } else { return 1; // bod je vnutri trojuholnika }*/ } return 0; }
/** * @brief Executes the Fight command c */ void CMobileCAI::ExecuteFight(Command& c) { assert((c.options & INTERNAL_ORDER) || owner->unitDef->canFight); if (c.params.size() == 1 && !owner->weapons.empty()) { CWeapon* w = owner->weapons.front(); if ((orderTarget != NULL) && !w->AttackUnit(orderTarget, false)) { CUnit* newTarget = helper->GetClosestValidTarget(owner->pos, owner->maxRange, owner->allyteam, this); if ((newTarget != NULL) && w->AttackUnit(newTarget, false)) { c.params[0] = newTarget->id; inCommand = false; } else { w->AttackUnit(orderTarget, false); } } ExecuteAttack(c); return; } if (tempOrder) { inCommand = true; tempOrder = false; } if (c.params.size() < 3) { LOG_L(L_ERROR, "Received a Fight command with less than 3 params on %s in MobileCAI", owner->unitDef->humanName.c_str()); return; } if (c.params.size() >= 6) { if (!inCommand) { commandPos1 = c.GetPos(3); } } 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, owner->pos); if ((owner->pos - commandPos1).SqLength2D() > (96 * 96)) { commandPos1 = owner->pos; } } float3 pos = c.GetPos(0); if (!inCommand) { inCommand = true; commandPos2 = pos; lastUserGoal = commandPos2; } if (c.params.size() >= 6) { pos = ClosestPointOnLine(commandPos1, commandPos2, owner->pos); } if (pos != goalPos) { SetGoal(pos, owner->pos); } if (owner->unitDef->canAttack && owner->fireState >= FIRESTATE_FIREATWILL && !owner->weapons.empty()) { const float3 curPosOnLine = ClosestPointOnLine(commandPos1, commandPos2, owner->pos); const float searchRadius = owner->maxRange + 100 * owner->moveState * owner->moveState; CUnit* enemy = helper->GetClosestValidTarget(curPosOnLine, searchRadius, owner->allyteam, this); if (enemy != NULL) { PushOrUpdateReturnFight(); // make the attack-command inherit <c>'s options // NOTE: see AirCAI::ExecuteFight why we do not set INTERNAL_ORDER Command c2(CMD_ATTACK, c.options, enemy->id); commandQue.push_front(c2); inCommand = false; tempOrder = true; // avoid infinite loops (?) if (lastPC != gs->frameNum) { lastPC = gs->frameNum; SlowUpdate(); } return; } } if ((owner->pos - goalPos).SqLength2D() < (64 * 64) || (owner->moveType->progressState == AMoveType::Failed)){ FinishCommand(); } }
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) { CUnit* enemy = NULL; if(myPlane->IsFighter()) { const float3 curPosOnLine = ClosestPointOnLine( commandPos1, commandPos2, owner->pos + owner->speed*10); const float searchRadius = 1000 * owner->moveState; enemy = helper->GetClosestEnemyAircraft(curPosOnLine, searchRadius, 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 { const float3 curPosOnLine = ClosestPointOnLine( commandPos1, commandPos2, owner->pos + owner->speed * 20); const float searchRadius = 500 * owner->moveState; enemy = helper->GetClosestValidTarget(curPosOnLine, searchRadius, owner->allyteam, this); if (enemy != NULL) { 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; CAirMoveType* airmt = dynamic_cast<CAirMoveType*>(myPlane); const float radius = airmt ? 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(); } return; }
vec CheckCollision( vector<z_face> &collision, vec vp, vec vpold, float radius) { // if(vpold_vp.Length()==0)return vpold; vec vpold_vp = vp-vpold; // smer pohybu vec vpold_vp_povodny = vpold_vp;// smer pohybu int iter=0; float radius2 = radius*radius; vec newmove = vpold_vp; vec newClosestPointOnSphere; vec newContactPointSphereToTriangle; do { float distanceCenterPointOnTriangle=1.e+15f; int smykanie=0; for( int i=0; i<collision.size(); i++) { vec normal = collision[i].normal; vec vp_move; vec center = vpold; float distance = PlaneDistance( normal, collision[i].a); // D = - (Ax+By+Cz) // --------------------------------------------------------- // najdeme kolidujuci bod na guli vec ClosestPointOnSphere; // najblizsi bod gule k rovine aktualneho trojuholnika // najdeme ho ako priesecnik priamky prechadzajucej stredom gule a smerovym vektorom = normalovemu vektoru roviny (trojuholnika) // vypocitame ho, ale jednodusie tak, ze pripocitame (opocitame) k stredu vektor normala*radius if( PlanePointDelta( normal, distance, center) > 0 ) { // center je na strane normaly, blizsi bod je v opacnom smere ako smer normaly ClosestPointOnSphere = -radius*normal+center; } else { // center je na opacnej strane ako normala, blizsi bod je v smere normaly ClosestPointOnSphere = radius*normal+center; } // --------------------------------------------------------- // najdeme kolidujuci bod na trojuholniku // najprv najdeme kolidujuci bod vzhladom na rovinu v ktorej lezi trojuholnik vec contactPointSphereToPlane; // kolidujuci bod na rovine trojuholnika s gulou float distanceCenterToPlane; // vzdialenost stredu gule k rovine // zistime ci rovina pretina gulu if( SpherePlaneCollision( center, 0.9999f*radius, normal, distance, &distanceCenterToPlane)==1 ) { // gula pretina rovinu // kolidujuci bod je bod na rovine najblizsi k stredu gule // je vzdialeny od roviny na distanceCenterToPlane, pretoze pocitame bod na rovine pouzijeme - contactPointSphereToPlane = center-distanceCenterToPlane*normal; } else { // nie sme v kolizii z gulov, ak sa pohybujeme v smere od roviny, nemoze nastat ziadna kolizia // ak sa pohybujeme v smere kolmom na normalovy vektor roviny, tak isto kolizia nehrozi // kvoli nepresnosti vypoctov umoznime pohyb aj ked velmi malou castou smeruje do roviny // if( DOT3( vpold_vp, center-ClosestPointOnSphere) >= 0) if( DOT3( vpold_vp, center-ClosestPointOnSphere) > -0.000001f) { continue; } // gula nepretina rovinu // kolidujuci bod je priesecnik roviny a priamky vedenej z bodu ClosestPointOnSphere // v smere pohybu t.j. z vpold do vp float t = LinePlaneIntersectionDirParameter( ClosestPointOnSphere, vpold_vp, normal, distance); // t > 1.f, priesecnik z rovinou je dalej ako vpold_vp od bodu ClosestPointOnSphere if(t>1.f) continue; // za cely krok vpold_vp sa s tymto trojuholnikom nestretneme else if( t<-radius/vpold_vp.Length()) // priesecnik je za gulou, v smere pohybu tuto rovinu nestretneme continue; else contactPointSphereToPlane = ClosestPointOnSphere+t*vpold_vp; } // najdeme kolidujuci bod na trojuholniku vec contactPointSphereToTriangle; // ak sa bod contactPointSphereToPlane nenachadza v trojuholniku // najdeme najblizsi bod trojuholnika k bodu contactPointSphereToTriangle if( !PointInsidePolygon( contactPointSphereToPlane, collision[i].a, collision[i].b, collision[i].c) ) { // najdeme najblizsi bod k contactPointSphereToPlane na hranach trojuholnika // z tychto vyberieme najblizi k contactPointSphereToPlane vec closest_ab = ClosestPointOnLine( collision[i].a, collision[i].b, contactPointSphereToPlane); vec closest_bc = ClosestPointOnLine( collision[i].b, collision[i].c, contactPointSphereToPlane); vec closest_ca = ClosestPointOnLine( collision[i].c, collision[i].a, contactPointSphereToPlane); float dist_ab = Distance2( closest_ab, contactPointSphereToPlane); float dist_bc = Distance2( closest_bc, contactPointSphereToPlane); float dist_ca = Distance2( closest_ca, contactPointSphereToPlane); if( dist_ab<dist_bc) { if(dist_ab<dist_ca) contactPointSphereToTriangle = closest_ab; else contactPointSphereToTriangle = closest_ca; } else { if(dist_bc<dist_ca) contactPointSphereToTriangle = closest_bc; else contactPointSphereToTriangle = closest_ca; } // kedze kolidujuci bod na trojuholniku je iny ako kolidujuci bod na rovine // zmeni sa aj kolidujuci bod na guli - ClosestPointOnSphere // vypocitame ho ako priesecnik gule a priamky z bodu contactPointSphereToTriangle // v smere pohybu t.j. z vpold do vp double t1,t2; if( LineSphereIntersectionDir( contactPointSphereToTriangle, vpold_vp, center, radius, &t1, &t2) ) { if( t1<=0 && t2<0) { // gula je pred trojuholnikom // berieme bod s t1, lebo ten je blizsie k stene (t1>t2) if( t1<-1.f)continue; // tento trojuholnik nas nezaujima lebo nekoliduje po cely tento krok ClosestPointOnSphere = t1*vpold_vp+contactPointSphereToTriangle; // mozeme sa pohnut iba tolko pokial sa colidujuci bod na guli nedotkne // kolidujuceho bodu na trojuholniku vp_move = contactPointSphereToTriangle - ClosestPointOnSphere; } else if( t1>0 && t2<0) { // gula je v stene, vratime ju von zo steny // berieme bod, ktory je blizsie k rovine vec t1point = t1*vpold_vp+contactPointSphereToTriangle; vec t2point = t2*vpold_vp+contactPointSphereToTriangle; /* if(PlanePointDistance( normal, distance, t1point)<=PlanePointDistance( normal, distance, t2point) ) ClosestPointOnSphere = t1point; else ClosestPointOnSphere = t2point; */ if( ABS(t1) < ABS(t2) ) ClosestPointOnSphere = t1point; else ClosestPointOnSphere = t2point; // mozeme sa pohnut iba tolko pokial sa colidujuci bod na guli nedotkne // kolidujuceho bodu na trojuholniku vp_move = contactPointSphereToTriangle - ClosestPointOnSphere; } else // if( t1>0 && t2>0) { // gula je za trojuholnikom, gula nekoliduje s trojuholnikom v tomto smere pohybu continue; } } else { // nie je priesecnik, gula je mimo trojuholnika continue; } } else { // bod je vnutri trojuholnika contactPointSphereToTriangle = contactPointSphereToPlane; // mozeme sa pohnut iba tolko pokial sa colidujuci bod na guli nedotkne // kolidujuceho bodu na trojuholniku vp_move = contactPointSphereToTriangle - ClosestPointOnSphere; } // zistime vzdialenost kontaktneho bodu na trojuholniku ku stredu gule float dist = Distance2(contactPointSphereToTriangle,center); if(dist<radius2) // ak je mensi ako polomer, gula je v kolizii z polygonom { if(dist<distanceCenterPointOnTriangle) // ak vzdialenost je mensia ako ineho bodu v kolizii, nahradime ho { distanceCenterPointOnTriangle=dist; newmove = vp_move; newClosestPointOnSphere = ClosestPointOnSphere; newContactPointSphereToTriangle = contactPointSphereToTriangle; } } else { if(distanceCenterPointOnTriangle>5.e+14f) // nenasiel sa ziaden bod vnutri gule { if( vp_move.Length2() < newmove.Length2() ) // berieme kratsi { newmove = vp_move; newClosestPointOnSphere = ClosestPointOnSphere; newContactPointSphereToTriangle = contactPointSphereToTriangle; } } } smykanie=1; } if(smykanie) { vec normal=vpold-newClosestPointOnSphere; float distance = PlaneDistance( normal, newContactPointSphereToTriangle); vec delta = LinePlaneIntersectionDir( newClosestPointOnSphere+vpold_vp, normal, normal, distance)-newContactPointSphereToTriangle; // vec newvp = newClosestPointOnSphere+vpold_vp; // float distancepoint = PlanePointDelta( normal, distance, newvp); // vec intersec = -distancepoint*normal+newvp; // vec delta = intersec-newContactPointSphereToTriangle; // taky klzavy pohyb, ktory ide proti povodnemu pohybu zamietneme if( DOT3(vpold_vp_povodny, delta) < 0)delta.clear(); vpold += newmove; // posunieme sa po najblizi kolidujuci bod vpold += 0.000001f*normal; vp = vpold + delta; // cielovy bod posunieme o deltu klzanim vpold_vp = vp-vpold; // novy vektor pohybu newmove = vpold_vp; iter++; } else { vpold += newmove; vpold_vp.clear(); iter=1000; } } while( (vpold_vp.Length2()>1.e-8f)&&(iter<10) ); return vpold; }
/** * @brief Executes the Fight command c */ void CMobileCAI::ExecuteFight(Command &c) { assert((c.options & INTERNAL_ORDER) || owner->unitDef->canFight); if(c.params.size() == 1) { if(orderTarget && !owner->weapons.empty() && !owner->weapons.front()->AttackUnit(orderTarget, false)) { CUnit* newTarget = helper->GetClosestValidTarget( owner->pos, owner->maxRange, owner->allyteam, this); if ((newTarget != NULL) && owner->weapons.front()->AttackUnit(newTarget, false)) { c.params[0] = newTarget->id; inCommand = false; } else { owner->weapons.front()->AttackUnit(orderTarget, false); } } ExecuteAttack(c); return; } if(tempOrder){ inCommand = true; tempOrder = false; } if (c.params.size() < 3) { 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, owner->pos); if ((owner->pos - commandPos1).SqLength2D() > (96 * 96)) { commandPos1 = owner->pos; } } float3 pos(c.params[0],c.params[1],c.params[2]); if(!inCommand){ inCommand = true; commandPos2 = pos; lastUserGoal = commandPos2; } if(c.params.size() >= 6){ pos = ClosestPointOnLine(commandPos1, commandPos2, owner->pos); } if(pos!=goalPos){ SetGoal(pos, owner->pos); } if (owner->unitDef->canAttack && owner->fireState >= 2 && !owner->weapons.empty()) { const float3 curPosOnLine = ClosestPointOnLine(commandPos1, commandPos2, owner->pos); const float searchRadius = owner->maxRange + 100 * owner->moveState * owner->moveState; CUnit* enemy = helper->GetClosestValidTarget(curPosOnLine, searchRadius, owner->allyteam, this); if (enemy != NULL) { Command c2; c2.id=CMD_FIGHT; 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((owner->pos - goalPos).SqLength2D() < (64 * 64) || (owner->moveType->progressState == AMoveType::Failed)){ FinishCommand(); } return; }
/** * @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) { const unsigned int targetID = (unsigned int) c.params[0]; const bool legalTarget = (targetID < uh->MaxUnits()); CUnit* targetUnit = (legalTarget)? uh->units[targetID]: 0x0; // check if we have valid target parameter and that we aren't attacking ourselves if (legalTarget && 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 { // 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 { 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.Length2D() < (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::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 CBuilderCAI::ExecuteFight(Command& c) { assert((c.options & INTERNAL_ORDER) || owner->unitDef->canFight); if (tempOrder) { tempOrder = false; inCommand = true; } if (c.params.size() < 3) { // this shouldnt happen but anyway ... LOG_L(L_ERROR, "Received a Fight command with less than 3 params on %s in BuilderCAI", owner->unitDef->humanName.c_str()); return; } if (c.params.size() >= 6) { if (!inCommand) { commandPos1 = c.GetPos(3); } } 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, owner->pos); if (f3SqDist(owner->pos, commandPos1) > (96 * 96)) { commandPos1 = owner->pos; } } float3 pos = c.GetPos(0); if (!inCommand) { inCommand = true; commandPos2 = pos; } float3 curPosOnLine = ClosestPointOnLine(commandPos1, commandPos2, owner->pos); if (c.params.size() >= 6) { pos = curPosOnLine; } if (pos != owner->moveType->goalPos) { SetGoal(pos, owner->pos); } const bool resurrectMode = !!(c.options & ALT_KEY); const bool reclaimEnemyMode = !!(c.options & META_KEY); const bool reclaimEnemyOnlyMode = (c.options & CONTROL_KEY) && (c.options & META_KEY); ReclaimOption recopt; if (resurrectMode) recopt |= REC_NONREZ; if (reclaimEnemyMode) recopt |= REC_ENEMY; if (reclaimEnemyOnlyMode) recopt |= REC_ENEMYONLY; const float searchRadius = (owner->immobile ? 0 : (300 * owner->moveState)) + ownerBuilder->buildDistance; if (!reclaimEnemyOnlyMode && (owner->unitDef->canRepair || owner->unitDef->canAssist) && // Priority 1: Repair FindRepairTargetAndRepair(curPosOnLine, searchRadius, c.options, true, resurrectMode)){ tempOrder = true; inCommand = false; if (lastPC1 != gs->frameNum) { //avoid infinite loops lastPC1 = gs->frameNum; SlowUpdate(); } return; } if (!reclaimEnemyOnlyMode && resurrectMode && owner->unitDef->canResurrect && // Priority 2: Resurrect (optional) FindResurrectableFeatureAndResurrect(curPosOnLine, searchRadius, c.options, false)) { tempOrder = true; inCommand = false; if (lastPC2 != gs->frameNum) { //avoid infinite loops lastPC2 = gs->frameNum; SlowUpdate(); } return; } if (owner->unitDef->canReclaim && // Priority 3: Reclaim / reclaim non resurrectable (optional) / reclaim enemy units (optional) FindReclaimTargetAndReclaim(curPosOnLine, searchRadius, c.options, recopt)) { tempOrder = true; inCommand = false; if (lastPC3 != gs->frameNum) { //avoid infinite loops lastPC3 = gs->frameNum; SlowUpdate(); } return; } if (f3SqDist(owner->pos, pos) < (64*64)) { StopMoveAndFinishCommand(); return; } if (owner->HaveTarget() && owner->moveType->progressState != AMoveType::Done) { StopMove(); } else { SetGoal(owner->moveType->goalPos, owner->pos); } }
void CBuilderCAI::ExecuteFight(Command &c) { assert((c.options & INTERNAL_ORDER) || owner->unitDef->canFight); CBuilder* fac=(CBuilder*)owner; 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 BuilderCAI",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, owner->pos); if ((owner->pos - commandPos1).SqLength2D() > (96 * 96)) { commandPos1 = owner->pos; } } float3 pos(c.params[0],c.params[1],c.params[2]); if(!inCommand){ inCommand = true; commandPos2 = pos; } if(c.params.size() >= 6){ pos = ClosestPointOnLine(commandPos1, commandPos2, owner->pos); } if(pos!=goalPos){ SetGoal(pos, owner->pos); } float3 curPosOnLine = ClosestPointOnLine(commandPos1, commandPos2, owner->pos); if ((owner->unitDef->canRepair || owner->unitDef->canAssist) && FindRepairTargetAndRepair(curPosOnLine, 300*owner->moveState+fac->buildDistance-8,c.options,true)){ tempOrder=true; inCommand=false; if(lastPC1!=gs->frameNum){ //avoid infinite loops lastPC1=gs->frameNum; SlowUpdate(); } return; } if(owner->unitDef->canReclaim && FindReclaimableFeatureAndReclaim(curPosOnLine,300,c.options,false)){ tempOrder=true; inCommand=false; if(lastPC2!=gs->frameNum){ //avoid infinite loops lastPC2=gs->frameNum; SlowUpdate(); } return; } if((owner->pos - pos).SqLength2D()<(64*64)){ FinishCommand(); return; } if(owner->haveTarget && owner->moveType->progressState!=CMoveType::Done){ StopMove(); } else if(owner->moveType->progressState!=CMoveType::Active){ owner->moveType->StartMoving(goalPos, 8); } return; }
void RenderScene() { glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // Clear The Screen And The Depth Buffer glLoadIdentity(); // Reset The matrix /////// * /////////// * /////////// * NEW * /////// * /////////// * /////////// * // Set our camera to the left a bit for a nice side view of the left hand side // Position View Up Vector gluLookAt(0, 1, -2, 0, 0, 0, 0, 1, 0); // This determines where the camera's position and view is // Rotate the camera around our world for a 360 degree view of what is going on. // Yes I know... we are actually rotating the whole world, not the camera :) glRotatef(g_rotateY, 0, 1, 0); // We need a radius for our spheres, so let's create it here with 0.05f float radius = 0.05f; float radius2 = 0.01f; // This sets our line width to %250, since 1 is %100 for normal line width glLineWidth(2.5f); // Turn of lighting so our line isn't effected by it glDisable(GL_LIGHTING); // Draw the line segment glBegin (GL_LINES); // This is our BEGIN to draw glColor3ub(255, 0, 0); // Make the Left vertex RED glVertex3f(g_vLine[0].x, g_vLine[0].y, g_vLine[0].z); glColor3ub(0, 255, 0); // Make the Right vertex GREEN glVertex3f(g_vLine[1].x, g_vLine[1].y, g_vLine[1].z); glEnd(); // This is the END of drawing glEnable(GL_LIGHTING); // Turn lighting back on // Instead of calculating the sphere ourselves, we are going to use quadrics. // Check out the tutorial on quadrics if this is confusing for you. // Allocate a quadric object to use as a sphere GLUquadricObj *pObj = gluNewQuadric(); // Get a Quadric off the stack // Below we create a new matrix so that when we translate the first sphere, // it doesn't effect the other sphere that we need to translate. glPushMatrix(); // Push on a new matrix // Move the sphere to it's center position glTranslatef(g_vPosition.x, g_vPosition.y, g_vPosition.z); // Draw the quadric as a sphere with the radius of .05 and a 15 by 15 detail. // To increase the detail of the sphere, just increase the 2 last parameters. gluSphere(pObj, radius, 15, 15); glPopMatrix(); // Pop off the current matrix // Now we need to find the closest point on the line. We pass in the end points // of the line segment, and the point we want to use, which is the sphere's center position. // This returns the closest point to use for the center of the purple sphere. CVector3 vClosestPoint = ClosestPointOnLine(g_vLine[0], g_vLine[1], g_vPosition); // Move the closest point sphere to it's center position glTranslatef(vClosestPoint.x, vClosestPoint.y, vClosestPoint.z); // Turn the line segment sphere purple glColor3f(0.8f, 0, 1); // Draw the quadric as a sphere with the radius of .01 and a 15 by 15 detail. gluSphere(pObj, radius2, 15, 15); gluDeleteQuadric(pObj); // Free the Quadric /////// * /////////// * /////////// * NEW * /////// * /////////// * /////////// * SwapBuffers(g_hDC); // Swap the backbuffers to the foreground }
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; } }