/** * @brief Executes the guard command c */ void CMobileCAI::ExecuteGuard(Command &c) { assert(owner->unitDef->canGuard); assert(!c.params.empty()); CUnit* guarded = uh->GetUnit(c.params[0]); if (guarded != NULL && UpdateTargetLostTimer(guarded->id)) { if (owner->unitDef->canAttack && guarded->lastAttack + 40 < gs->frameNum && IsValidTarget(guarded->lastAttacker)) { StopSlowGuard(); 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->speed * guarded->frontdir; float3 dif = guarded->pos - owner->pos; dif.Normalize(); float3 goal = guarded->pos - dif * (guarded->radius + owner->radius + 64); if((goalPos - goal).SqLength2D() > 1600 || (goalPos - owner->pos).SqLength2D() < (owner->maxSpeed*30 + 1 + SQUARE_SIZE*2) * (owner->maxSpeed*30 + 1 + SQUARE_SIZE*2)){ SetGoal(goal, owner->pos); } if((goal - owner->pos).SqLength2D() < 6400) { StartSlowGuard(guarded->maxSpeed); if((goal-owner->pos).SqLength2D() < 1800){ StopMove(); NonMoving(); } } else { StopSlowGuard(); } } } else { FinishCommand(); } return; }
void CMobileCAI::IdleCheck(void) { if(owner->unitDef->canAttack && owner->moveState && owner->fireState && !owner->weapons.empty() && (!owner->haveTarget || owner->weapons[0]->onlyForward)){ if(owner->lastAttacker && owner->lastAttack+100>gs->frameNum && !(owner->unitDef->noChaseCategory & owner->lastAttacker->category)){ float3 apos=owner->lastAttacker->pos; float dist=apos.distance2D(owner->pos); if(dist<owner->maxRange+200*owner->moveState*owner->moveState){ Command c; c.id=CMD_ATTACK; c.options=INTERNAL_ORDER; c.params.push_back(owner->lastAttacker->id); c.timeOut=gs->frameNum+140; commandQue.push_front(c); return; } } } if(owner->unitDef->canAttack && (gs->frameNum!=lastIdleCheck+16) && owner->moveState && owner->fireState==2 && !owner->weapons.empty() && (!owner->haveTarget || owner->weapons[0]->onlyForward)){ if(!owner->unitDef->noAutoFire){ CUnit* u=helper->GetClosestEnemyUnit(owner->pos,owner->maxRange+150*owner->moveState*owner->moveState,owner->allyteam); if(u && !(owner->unitDef->noChaseCategory & u->category)){ Command c; c.id=CMD_ATTACK; c.options=INTERNAL_ORDER; c.params.push_back(u->id); c.timeOut=gs->frameNum+140; commandQue.push_front(c); return; } } } lastIdleCheck=gs->frameNum; if((owner->pos-lastUserGoal).SqLength2D()>10000 && !owner->haveTarget && !dynamic_cast<CTAAirMoveType*>(owner->moveType)){ Command c; c.id=CMD_MOVE; c.options=0; //note that this is not internal order so that we dont keep generating new orders if we cant get to that pos c.params.push_back(lastUserGoal.x); c.params.push_back(lastUserGoal.y); c.params.push_back(lastUserGoal.z); commandQue.push_front(c); unimportantMove=true; } else { NonMoving(); } }
/** * @brief Executes the guard command c */ void CMobileCAI::ExecuteGuard(Command &c) { assert(owner->unitDef->canGuard); assert(!c.params.empty()); const CUnit* guardee = unitHandler->GetUnit(c.params[0]); if (guardee == NULL) { FinishCommand(); return; } if (UpdateTargetLostTimer(guardee->id) == 0) { FinishCommand(); return; } if (guardee->outOfMapTime > (GAME_SPEED * 5)) { FinishCommand(); return; } const bool pushAttackCommand = owner->unitDef->canAttack && (guardee->lastAttackFrame + 40 < gs->frameNum) && IsValidTarget(guardee->lastAttacker); if (pushAttackCommand) { Command nc(CMD_ATTACK, c.options, guardee->lastAttacker->id); commandQue.push_front(nc); StopSlowGuard(); SlowUpdate(); } else { const float3 dif = (guardee->pos - owner->pos).SafeNormalize(); const float3 goal = guardee->pos - dif * (guardee->radius + owner->radius + 64.0f); const bool resetGoal = ((goalPos - goal).SqLength2D() > 1600.0f) || (goalPos - owner->pos).SqLength2D() < Square(owner->moveType->GetMaxSpeed() * GAME_SPEED + 1 + SQUARE_SIZE * 2); if (resetGoal) { SetGoal(goal, owner->pos); } if ((goal - owner->pos).SqLength2D() < 6400.0f) { StartSlowGuard(guardee->moveType->GetMaxSpeed()); if ((goal - owner->pos).SqLength2D() < 1800.0f) { StopMove(); NonMoving(); } } else { StopSlowGuard(); } } }
void CBuilderCAI::ExecuteGuard(Command &c) { assert(owner->unitDef->canGuard); CBuilder* fac=(CBuilder*)owner; CUnit* guarded=uh->units[(int)c.params[0]]; if(guarded && guarded!=owner && UpdateTargetLostTimer((int)c.params[0])){ if(CBuilder* b=dynamic_cast<CBuilder*>(guarded)){ if(b->terraforming){ if(fac->pos.distance2D(b->terraformCenter)<fac->buildDistance*0.8f+b->terraformRadius*0.7f){ StopMove(); owner->moveType->KeepPointingTo(b->terraformCenter, fac->buildDistance*0.9f, false); fac->HelpTerraform(b); } else { SetGoal(b->terraformCenter,fac->pos,fac->buildDistance*0.7f+b->terraformRadius*0.6f); } return; } if (b->curBuild && (( b->curBuild->beingBuilt && owner->unitDef->canAssist) || (!b->curBuild->beingBuilt && owner->unitDef->canRepair))) { 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 && (( f->curBuild->beingBuilt && owner->unitDef->canAssist) || (!f->curBuild->beingBuilt && owner->unitDef->canRepair))) { 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*1.9f + guarded->radius) *(fac->buildDistance*1.9f + guarded->radius)){ StartSlowGuard(guarded->maxSpeed); } else { StopSlowGuard(); } if((guarded->pos-curPos).SqLength2D()< (fac->buildDistance*0.9f + guarded->radius) *(fac->buildDistance*0.9f + guarded->radius)){ StopMove(); // logOutput.Print("should point with type 3?"); owner->moveType->KeepPointingTo(guarded->pos, fac->buildDistance*0.9f+guarded->radius, false); if(guarded->health<guarded->maxHealth && ((guarded->beingBuilt && owner->unitDef->canAssist) || (!guarded->beingBuilt && owner->unitDef->canRepair))) fac->SetRepairTarget(guarded); else NonMoving(); }else{ if((goalPos-goal).SqLength2D()>4000 || (goalPos - owner->pos).SqLength2D() < (owner->maxSpeed*30 + 1 + SQUARE_SIZE*2) * (owner->maxSpeed*30 + 1 + SQUARE_SIZE*2)){ SetGoal(goal,curPos); } } } else { FinishCommand(); } return; }
void CBuilderCAI::SlowUpdate() { if(commandQue.empty()){ CMobileCAI::SlowUpdate(); return; } Command& c=commandQue.front(); CBuilder* fac=(CBuilder*)owner; float3 curPos=owner->pos; map<int,string>::iterator boi; if((boi=buildOptions.find(c.id))!=buildOptions.end()){ const UnitDef* ud = unitDefHandler->GetUnitByName(boi->second); const float radius = GetUnitRadius(ud, c.id); if (inCommand) { if (building) { if (build.pos.distance2D(fac->pos) > fac->buildDistance+radius-8.0f) { owner->moveType->StartMoving(build.pos, fac->buildDistance*0.5f+radius); } else { StopMove(); owner->moveType->KeepPointingTo(build.pos, (fac->buildDistance+radius)*0.6f, false); //needed since above startmoving cancels this } if(!fac->curBuild && !fac->terraforming){ building=false; StopMove(); //cancel the effect of KeepPointingTo FinishCommand(); } } else { build.Parse(c); const float dist = build.pos.distance2D(fac->pos); if ((dist < (fac->buildDistance * 0.6f + radius)) || (!owner->unitDef->canmove && (dist <= (fac->buildDistance+radius-8.0f)))) { StopMove(); if(uh->unitsType[owner->team][build.def->id]>=build.def->maxThisUnit){ //unit restricted ENTER_MIXED; if(owner->team == gu->myTeam){ logOutput.Print("%s: Build failed, unit type limit reached",owner->unitDef->humanName.c_str()); logOutput.SetLastMsgPos(owner->pos); } ENTER_SYNCED; FinishCommand(); } else if(uh->maxUnits>(int)gs->Team(owner->team)->units.size()){ //max unitlimit reached buildRetries++; owner->moveType->KeepPointingTo(build.pos, fac->buildDistance*0.7f+radius, false); if(fac->StartBuild(build) || buildRetries>20){ building=true; } else { ENTER_MIXED; if(owner->team==gu->myTeam && !(buildRetries&7)){ logOutput.Print("%s: Build pos blocked",owner->unitDef->humanName.c_str()); logOutput.SetLastMsgPos(owner->pos); } ENTER_SYNCED; helper->BuggerOff(build.pos,radius); NonMoving(); } } } else { if(owner->moveType->progressState==CMoveType::Failed){ if(++buildRetries>5){ StopMove(); FinishCommand(); } } SetGoal(build.pos,owner->pos, fac->buildDistance*0.4f+radius); } } } else { //!inCommand BuildInfo bi; bi.pos.x=floor(c.params[0]/SQUARE_SIZE+0.5f)*SQUARE_SIZE; bi.pos.z=floor(c.params[2]/SQUARE_SIZE+0.5f)*SQUARE_SIZE; bi.pos.y=c.params[1]; CFeature* f=0; if (c.params.size()==4) bi.buildFacing = int(c.params[3]); bi.def = unitDefHandler->GetUnitByName(boi->second); uh->TestUnitBuildSquare(bi,f,owner->allyteam); if(f){ if (!owner->unitDef->canReclaim || !f->def->destructable) { // FIXME user shouldn't be able to queue buildings on top of features // in the first place (in this case). StopMove(); FinishCommand(); } else { 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: { ExecuteStop(c); return; } case CMD_REPAIR: { ExecuteRepair(c); return; } case CMD_CAPTURE: { ExecuteCapture(c); return; } case CMD_GUARD: { ExecuteGuard(c); return; } case CMD_RECLAIM: { ExecuteReclaim(c); return; } case CMD_RESURRECT: { ExecuteResurrect(c); return; } case CMD_PATROL: { ExecutePatrol(c); return; } case CMD_FIGHT: { ExecuteFight(c); return; } case CMD_RESTORE: { ExecuteRestore(c); return; } default: { CMobileCAI::SlowUpdate(); return; } } }
void CBuilderCAI::ExecuteGuard(Command& c) { if (!owner->unitDef->canGuard) return; CBuilder* builder = (CBuilder*) owner; CUnit* guardee = uh->GetUnit(c.params[0]); if (guardee == NULL) { FinishCommand(); return; } if (guardee == owner) { FinishCommand(); return; } if (UpdateTargetLostTimer(guardee->id) == 0) { FinishCommand(); return; } if (guardee->outOfMapTime > (GAME_SPEED * 5)) { FinishCommand(); return; } if (CBuilder* b = dynamic_cast<CBuilder*>(guardee)) { if (b->terraforming) { if (MoveInBuildRange(b->terraformCenter, b->terraformRadius * 0.7f)) { builder->HelpTerraform(b); } else { StopSlowGuard(); } return; } else if (b->curReclaim && owner->unitDef->canReclaim) { StopSlowGuard(); if (!ReclaimObject(b->curReclaim)) { StopMove(); } return; } else if (b->curResurrect && owner->unitDef->canResurrect) { StopSlowGuard(); if (!ResurrectObject(b->curResurrect)) { StopMove(); } return; } else { builder->StopBuild(); } const bool pushRepairCommand = ( b->curBuild != NULL) && ( b->curBuild->soloBuilder == NULL || b->curBuild->soloBuilder == owner) && (( b->curBuild->beingBuilt && owner->unitDef->canAssist) || ( !b->curBuild->beingBuilt && owner->unitDef->canRepair)); if (pushRepairCommand) { StopSlowGuard(); Command nc(CMD_REPAIR, c.options); nc.params.push_back(b->curBuild->id); commandQue.push_front(nc); inCommand = false; SlowUpdate(); return; } } if (CFactory* fac = dynamic_cast<CFactory*>(guardee)) { const bool pushRepairCommand = ( fac->curBuild != NULL) && ( fac->curBuild->soloBuilder == NULL || fac->curBuild->soloBuilder == owner) && (( fac->curBuild->beingBuilt && owner->unitDef->canAssist) || (!fac->curBuild->beingBuilt && owner->unitDef->canRepair)); if (pushRepairCommand) { StopSlowGuard(); Command nc(CMD_REPAIR, c.options); nc.params.push_back(fac->curBuild->id); commandQue.push_front(nc); inCommand = false; // SlowUpdate(); return; } } if (!(c.options & CONTROL_KEY) && IsUnitBeingReclaimed(guardee, owner)) return; const float3 pos = guardee->pos; const float radius = (guardee->immobile) ? guardee->radius : guardee->radius * 0.8f; // in case of mobile units reduce radius a bit if (MoveInBuildRange(pos, radius)) { StartSlowGuard(guardee->moveType->GetMaxSpeed()); const bool pushRepairCommand = ( guardee->health < guardee->maxHealth) && ( guardee->soloBuilder == NULL || guardee->soloBuilder == owner) && (( guardee->beingBuilt && owner->unitDef->canAssist) || (!guardee->beingBuilt && owner->unitDef->canRepair)); if (pushRepairCommand) { StopSlowGuard(); Command nc(CMD_REPAIR, c.options); nc.params.push_back(guardee->id); commandQue.push_front(nc); inCommand = false; return; } else { NonMoving(); } } else { StopSlowGuard(); } }
void CBuilderCAI::ExecuteBuildCmd(Command& c) { CBuilder* builder = (CBuilder*) owner; map<int, string>::iterator boi = buildOptions.find(c.GetID()); if (boi == buildOptions.end()) return; if (!inCommand) { BuildInfo bi; bi.pos.x = floor(c.params[0] / SQUARE_SIZE + 0.5f) * SQUARE_SIZE; bi.pos.z = floor(c.params[2] / SQUARE_SIZE + 0.5f) * SQUARE_SIZE; bi.pos.y = c.params[1]; if (c.params.size() == 4) bi.buildFacing = abs((int)c.params[3]) % NUM_FACINGS; bi.def = unitDefHandler->GetUnitDefByName(boi->second); CFeature* f = NULL; uh->TestUnitBuildSquare(bi, f, owner->allyteam, true); if (f != NULL) { if (!bi.def->isFeature || bi.def->wreckName != f->def->name) { ReclaimFeature(f); } else { FinishCommand(); } } else { inCommand = true; build.Parse(c); ExecuteBuildCmd(c); } return; } assert(build.def); const float radius = GetBuildOptionRadius(build.def, c.GetID()); if (building) { MoveInBuildRange(build.pos, radius); if (!builder->curBuild && !builder->terraforming) { building = false; StopMove(); // cancel the effect of KeepPointingTo FinishCommand(); } // This can only be true if two builders started building // the restricted unit in the same simulation frame else if (uh->unitsByDefs[owner->team][build.def->id].size() > build.def->maxThisUnit) { // unit restricted building = false; builder->StopBuild(); CancelRestrictedUnit(boi->second); } } else { if (uh->unitsByDefs[owner->team][build.def->id].size() >= build.def->maxThisUnit) { // unit restricted, don't bother moving all the way // to the construction site first before telling us // (since greyed-out icons can still be clicked etc, // would be better to prevent that but doesn't cover // case where limit reached while builder en-route) CancelRestrictedUnit(boi->second); StopMove(); return; } build.pos = helper->Pos2BuildPos(build, true); // we are on the way to the buildpos, meanwhile it can happen // that another builder already finished our buildcmd or blocked // the buildpos with another building (skip our buildcmd then) const bool checkBuildPos = (gs->frameNum % (5 * UNIT_SLOWUPDATE_RATE)) < UNIT_SLOWUPDATE_RATE; //FIXME add a per-unit solution to better balance the load? CFeature* feature = NULL; if (checkBuildPos && !uh->TestUnitBuildSquare(build, feature, owner->allyteam, true)) { if (!feature) { const int yardxpos = int(build.pos.x + 4) / SQUARE_SIZE; const int yardypos = int(build.pos.z + 4) / SQUARE_SIZE; const CSolidObject* s = groundBlockingObjectMap->GroundBlocked(yardxpos, yardypos); const CUnit* u = dynamic_cast<const CUnit*>(s); if (u != NULL) { const bool canAssist = u->beingBuilt && owner->unitDef->canAssist && (!u->soloBuilder || (u->soloBuilder == owner)); if ((u->unitDef != build.def) || !canAssist) { StopMove(); FinishCommand(); return; } } } } if (MoveInBuildRange(build.pos, radius, true)) { if (luaRules && !luaRules->AllowUnitCreation(build.def, owner, &build)) { StopMove(); // cancel KeepPointingTo FinishCommand(); } else if (!teamHandler->Team(owner->team)->AtUnitLimit()) { // unit-limit not yet reached CFeature* f = NULL; bool waitstance = false; if (builder->StartBuild(build, f, waitstance) || (++buildRetries > 30)) { building = true; } else if (f != NULL && (!build.def->isFeature || build.def->wreckName != f->def->name)) { inCommand = false; ReclaimFeature(f); } else if (!waitstance) { const float fpSqRadius = (build.def->xsize * build.def->xsize + build.def->zsize * build.def->zsize); const float fpRadius = (math::sqrt(fpSqRadius) * 0.5f) * SQUARE_SIZE; // tell everything within the radius of the soon-to-be buildee // to get out of the way; using the model radius is not correct // because this can be shorter than half the footprint diagonal helper->BuggerOff(build.pos, std::max(radius, fpRadius), false, true, owner->team, NULL); NonMoving(); } } } else { if (owner->moveType->progressState == AMoveType::Failed) { if (++buildRetries > 5) { StopMove(); FinishCommand(); } } } } }
void CBuilderCAI::SlowUpdate() { if (commandQue.empty()) { CMobileCAI::SlowUpdate(); return; } if (owner->stunned) { return; } CBuilder* fac = (CBuilder*)owner; Command& c = commandQue.front(); if (OutOfImmobileRange(c)) { FinishCommand(); return; } map<int, string>::iterator boi = buildOptions.find(c.id); if (!owner->beingBuilt && boi != buildOptions.end()) { const UnitDef* ud = unitDefHandler->GetUnitByName(boi->second); const float radius = GetUnitDefRadius(ud, c.id); if (inCommand) { if (building) { if (f3SqDist(build.pos, fac->pos) > Square(fac->buildDistance + radius - 8.0f)) { owner->moveType->StartMoving(build.pos, fac->buildDistance * 0.5f + radius); } else { StopMove(); // needed since above startmoving cancels this owner->moveType->KeepPointingTo(build.pos, (fac->buildDistance + radius) * 0.6f, false); } if (!fac->curBuild && !fac->terraforming) { building = false; StopMove(); //cancel the effect of KeepPointingTo FinishCommand(); } // This can only be true if two builders started building // the restricted unit in the same simulation frame else if (uh->unitsByDefs[owner->team][build.def->id].size() > build.def->maxThisUnit) { // unit restricted building = false; fac->StopBuild(); CancelRestrictedUnit(boi->second); } } else { build.Parse(c); if (uh->unitsByDefs[owner->team][build.def->id].size() >= build.def->maxThisUnit) { // unit restricted, don't bother moving all the way // to the construction site first before telling us // (since greyed-out icons can still be clicked etc, // would be better to prevent that but doesn't cover // case where limit reached while builder en-route) CancelRestrictedUnit(boi->second); StopMove(); } else { build.pos = helper->Pos2BuildPos(build); const float sqdist = f3SqDist(build.pos, fac->pos); if ((sqdist < Square(fac->buildDistance * 0.6f + radius)) || (!owner->unitDef->canmove && (sqdist <= Square(fac->buildDistance + radius - 8.0f)))) { StopMove(); if (luaRules && !luaRules->AllowUnitCreation(build.def, owner, &build.pos)) { FinishCommand(); } else if (uh->maxUnits > (int) teamHandler->Team(owner->team)->units.size()) { // max unitlimit reached buildRetries++; owner->moveType->KeepPointingTo(build.pos, fac->buildDistance * 0.7f + radius, false); if (fac->StartBuild(build) || (buildRetries > 20)) { building = true; } else { ENTER_MIXED; if ((owner->team == gu->myTeam) && !(buildRetries & 7)) { logOutput.Print("%s: Build pos blocked", owner->unitDef->humanName.c_str()); logOutput.SetLastMsgPos(owner->pos); } ENTER_SYNCED; helper->BuggerOff(build.pos, radius); NonMoving(); } } } else { if (owner->moveType->progressState == AMoveType::Failed) { if (++buildRetries > 5) { StopMove(); FinishCommand(); } } SetGoal(build.pos, owner->pos, fac->buildDistance * 0.4f + radius); } } } } else { //!inCommand BuildInfo bi; bi.pos.x=floor(c.params[0]/SQUARE_SIZE+0.5f)*SQUARE_SIZE; bi.pos.z=floor(c.params[2]/SQUARE_SIZE+0.5f)*SQUARE_SIZE; bi.pos.y=c.params[1]; CFeature* f=0; if (c.params.size()==4) bi.buildFacing = int(c.params[3]); bi.def = unitDefHandler->GetUnitByName(boi->second); uh->TestUnitBuildSquare(bi,f,owner->allyteam); if (f) { if (!owner->unitDef->canReclaim || !f->def->reclaimable) { // FIXME user shouldn't be able to queue buildings on top of features // in the first place (in this case). StopMove(); FinishCommand(); } else { 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: { ExecuteStop(c); return; } case CMD_REPAIR: { ExecuteRepair(c); return; } case CMD_CAPTURE: { ExecuteCapture(c); return; } case CMD_GUARD: { ExecuteGuard(c); return; } case CMD_RECLAIM: { ExecuteReclaim(c); return; } case CMD_RESURRECT: { ExecuteResurrect(c); return; } case CMD_PATROL: { ExecutePatrol(c); return; } case CMD_FIGHT: { ExecuteFight(c); return; } case CMD_RESTORE: { ExecuteRestore(c); return; } default: { CMobileCAI::SlowUpdate(); 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; } }
void CMobileCAI::IdleCheck(void) { if(owner->unitDef->canAttack && owner->fireState && !owner->weapons.empty() && owner->haveTarget) { if(!owner->userTarget) { owner->haveTarget = false; } else if(owner->pos.SqDistance2D(owner->userTarget->pos) < Square(owner->maxRange + 200*owner->moveState*owner->moveState)) { Command c; c.id = CMD_ATTACK; c.options=INTERNAL_ORDER; c.params.push_back(owner->userTarget->id); c.timeOut = gs->frameNum + 140; commandQue.push_front(c); tempOrder = true; commandPos1 = owner->pos; commandPos2 = owner->pos; return; } } if(owner->unitDef->canAttack && owner->fireState && !owner->weapons.empty() && !owner->haveTarget) { if(owner->lastAttacker && owner->lastAttack + 200 > gs->frameNum && !(owner->unitDef->noChaseCategory & owner->lastAttacker->category)){ float3 apos=owner->lastAttacker->pos; float dist=apos.SqDistance2D(owner->pos); if(dist<Square(owner->maxRange+200*owner->moveState*owner->moveState)){ Command c; c.id=CMD_ATTACK; c.options=INTERNAL_ORDER; c.params.push_back(owner->lastAttacker->id); c.timeOut=gs->frameNum+140; commandQue.push_front(c); tempOrder = true; commandPos1 = owner->pos; commandPos2 = owner->pos; return; } } } if (owner->unitDef->canAttack && (gs->frameNum >= lastIdleCheck + 10) && owner->fireState >= 2 && !owner->weapons.empty() && !owner->haveTarget) { const float searchRadius = owner->maxRange + 150 * owner->moveState * owner->moveState; CUnit* enemy = helper->GetClosestValidTarget(owner->pos, searchRadius, owner->allyteam, this); if (enemy != NULL) { Command c; c.id=CMD_ATTACK; c.options=INTERNAL_ORDER; c.params.push_back(enemy->id); c.timeOut=gs->frameNum+140; commandQue.push_front(c); tempOrder = true; commandPos1 = owner->pos; commandPos2 = owner->pos; return; } } if (owner->usingScriptMoveType) { return; } lastIdleCheck = gs->frameNum; if (((owner->pos - lastUserGoal).SqLength2D() > 10000.0f) && !owner->haveTarget && !dynamic_cast<CTAAirMoveType*>(owner->moveType)) { //note that this is not internal order so that we dont keep generating //new orders if we cant get to that pos Command c; c.id=CMD_MOVE; c.options=0; c.params.push_back(lastUserGoal.x); c.params.push_back(lastUserGoal.y); c.params.push_back(lastUserGoal.z); commandQue.push_front(c); unimportantMove=true; } else { NonMoving(); } }
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 CBuilderCAI::ExecuteBuildCmd(Command& c) { const map<int, string>::const_iterator boi = buildOptions.find(c.GetID()); if (boi == buildOptions.end()) return; if (!inCommand) { BuildInfo bi; bi.pos.x = math::floor(c.params[0] / SQUARE_SIZE + 0.5f) * SQUARE_SIZE; bi.pos.z = math::floor(c.params[2] / SQUARE_SIZE + 0.5f) * SQUARE_SIZE; bi.pos.y = c.params[1]; if (c.params.size() == 4) bi.buildFacing = abs((int)c.params[3]) % NUM_FACINGS; bi.def = unitDefHandler->GetUnitDefByName(boi->second); CFeature* f = NULL; CGameHelper::TestUnitBuildSquare(bi, f, owner->allyteam, true); if (f != NULL) { if (!bi.def->isFeature || bi.def->wreckName != f->def->name) { ReclaimFeature(f); } else { FinishCommand(); } return; } inCommand = true; build.Parse(c); } assert(build.def->id == -c.GetID() && build.def->id != 0); const float buildeeRadius = GetBuildOptionRadius(build.def, c.GetID()); if (building) { // keep moving until 3D distance to buildPos is LEQ our buildDistance MoveInBuildRange(build.pos, 0.0f); if (!ownerBuilder->curBuild && !ownerBuilder->terraforming) { building = false; StopMove(); // cancel the effect of KeepPointingTo FinishCommand(); } // This can only be true if two builders started building // the restricted unit in the same simulation frame else if (unitHandler->unitsByDefs[owner->team][build.def->id].size() > build.def->maxThisUnit) { // unit restricted building = false; ownerBuilder->StopBuild(); CancelRestrictedUnit(boi->second); } } else { if (unitHandler->unitsByDefs[owner->team][build.def->id].size() >= build.def->maxThisUnit) { // unit restricted, don't bother moving all the way // to the construction site first before telling us // (since greyed-out icons can still be clicked etc, // would be better to prevent that but doesn't cover // case where limit reached while builder en-route) CancelRestrictedUnit(boi->second); StopMove(); return; } build.pos = CGameHelper::Pos2BuildPos(build, true); // keep moving until until 3D distance to buildPos is LEQ our buildDistance if (MoveInBuildRange(build.pos, 0.0f, true)) { if (IsBuildPosBlocked(build)) { StopMove(); FinishCommand(); return; } if (!eventHandler.AllowUnitCreation(build.def, owner, &build)) { StopMove(); // cancel KeepPointingTo FinishCommand(); return; } if (teamHandler->Team(owner->team)->AtUnitLimit()) { return; } CFeature* f = NULL; bool waitstance = false; if (ownerBuilder->StartBuild(build, f, waitstance) || (++buildRetries > 30)) { building = true; } else if (f != NULL && (!build.def->isFeature || build.def->wreckName != f->def->name)) { inCommand = false; ReclaimFeature(f); } else if (!waitstance) { const float fpSqRadius = (build.def->xsize * build.def->xsize + build.def->zsize * build.def->zsize); const float fpRadius = (math::sqrt(fpSqRadius) * 0.5f) * SQUARE_SIZE; // tell everything within the radius of the soon-to-be buildee // to get out of the way; using the model radius is not correct // because this can be shorter than half the footprint diagonal CGameHelper::BuggerOff(build.pos, std::max(buildeeRadius, fpRadius), false, true, owner->team, NULL); NonMoving(); } } else { if (owner->moveType->progressState == AMoveType::Failed) { if (++buildRetries > 5) { StopMove(); FinishCommand(); return; } } // we are on the way to the buildpos, meanwhile it can happen // that another builder already finished our buildcmd or blocked // the buildpos with another building (skip our buildcmd then) if ((++randomCounter % 5) == 0) { if (IsBuildPosBlocked(build)) { StopMove(); FinishCommand(); return; } } } } }