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->LoadModel(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(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;
			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",owner->unitDef->humanName.c_str());
			return;
		}

		if(!(patrolTime&3) && owner->fireState==2){
			if(FindRepairTargetAndRepair(owner->pos,300*owner->moveState,c.options,true)){
				commandFromPatrol=true;
				inCommand=false;
				SlowUpdate();
				return;
			}
			if(FindReclaimableFeatureAndReclaim(owner->pos,300*owner->moveState,c.options)){
				commandFromPatrol=true;
				inCommand=false;
				SlowUpdate();
				return;
			}
			patrolGoal=float3(c.params[0],c.params[1],c.params[2]);
		}
		patrolTime++;
		if(!(patrolGoal==goalPos)){
			SetGoal(patrolGoal,curPos);
		}
		if((curPos-float3(c.params[0],c.params[1],c.params[2])).SqLength2D()<64){
			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++;
			}
		}
		if(owner->haveTarget)
			StopMove();
		else
			owner->moveType->StartMoving(patrolGoal, 1000);					
		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();
}
Exemple #2
0
void CFactoryCAI::GiveCommandReal(const Command& c, bool fromSynced)
{
	const int cmdID = c.GetID();

	// move is always allowed for factories (passed to units it produces)
	if (cmdID == CMD_SET_WANTED_MAX_SPEED)
		return;
	if ((cmdID != CMD_MOVE) && !AllowedCommand(c, fromSynced))
		return;

	auto boi = buildOptions.find(cmdID);

	// not a build order (or a build order we do not support, eg. if multiple
	// factories of different types were selected) so queue it to built units
	if (boi == buildOptions.end()) {
		if (cmdID < 0)
			return;

		if (nonQueingCommands.find(cmdID) != nonQueingCommands.end()) {
			CCommandAI::GiveAllowedCommand(c);
			return;
		}

		if (cmdID == CMD_INSERT || cmdID == CMD_REMOVE) {
			CCommandAI::GiveAllowedCommand(c);
			return;
		}

		if (!(c.options & SHIFT_KEY) && (cmdID == CMD_WAIT || cmdID == CMD_SELFD)) {
			CCommandAI::GiveAllowedCommand(c);
			return;
		}

		if (!(c.options & SHIFT_KEY)) {
 			waitCommandsAI.ClearUnitQueue(owner, newUnitCommands);
			CCommandAI::ClearCommandDependencies();
			newUnitCommands.clear();
		}

		CCommandAI::AddCommandDependency(c);

		if (cmdID != CMD_STOP) {
			if ((cmdID == CMD_WAIT) || (cmdID == CMD_SELFD)) {
				if (!newUnitCommands.empty() && (newUnitCommands.back().GetID() == cmdID)) {
					if (cmdID == CMD_WAIT) {
						waitCommandsAI.RemoveWaitCommand(owner, c);
					}
					newUnitCommands.pop_back();
				} else {
					newUnitCommands.push_back(c);
				}
			} else {
				bool dummy;
				if (CancelCommands(c, newUnitCommands, dummy) > 0) {
					return;
				} else {
					if (GetOverlapQueued(c, newUnitCommands).empty()) {
						newUnitCommands.push_back(c);
					} else {
						return;
					}
				}
			}
		}

		// the first new-unit build order can not be WAIT or SELFD
		while (!newUnitCommands.empty()) {
			const Command& newUnitCommand = newUnitCommands.front();
			const int id = newUnitCommand.GetID();

			if ((id == CMD_WAIT) || (id == CMD_SELFD)) {
				if (cmdID == CMD_WAIT) {
					waitCommandsAI.RemoveWaitCommand(owner, c);
				}
				newUnitCommands.pop_front();
			} else {
				break;
			}
		}

		return;
	}

	int& numQueued = boi->second;
	int numItems = 1;

	if (c.options & SHIFT_KEY)   { numItems *= 5; }
	if (c.options & CONTROL_KEY) { numItems *= 20; }

	if (c.options & RIGHT_MOUSE_KEY) {
		numQueued -= numItems;
		numQueued  = std::max(numQueued, 0);

		int numToErase = numItems;
		if (c.options & ALT_KEY) {
			for (unsigned int cmdNum = 0; cmdNum < commandQue.size() && numToErase; ++cmdNum) {
				if (commandQue[cmdNum].GetID() == cmdID) {
					commandQue[cmdNum] = Command(CMD_STOP);
					numToErase--;
				}
			}
		} else {
			for (int cmdNum = commandQue.size() - 1; cmdNum != -1 && numToErase; --cmdNum) {
				if (commandQue[cmdNum].GetID() == cmdID) {
					commandQue[cmdNum] = Command(CMD_STOP);
					numToErase--;
				}
			}
		}
		UpdateIconName(cmdID, numQueued);
		SlowUpdate();
	} else {
		if (c.options & ALT_KEY) {
			for (int a = 0; a < numItems; ++a) {
				if (repeatOrders) {
					Command nc(c);
					nc.options |= INTERNAL_ORDER;
					if (commandQue.empty()) {
						commandQue.push_front(nc);
					} else {
						commandQue.insert(commandQue.begin()+1, nc);
					}
				} else {
					commandQue.push_front(c);
				}
			}
			if (!repeatOrders) {
				CFactory* fac = static_cast<CFactory*>(owner);
				fac->StopBuild();
			}
		} else {
			for (int a = 0; a < numItems; ++a) {
				commandQue.push_back(c);
			}
		}
		numQueued += numItems;
		UpdateIconName(cmdID, numQueued);

		SlowUpdate();
	}
}
Exemple #3
0
void CBuilderCAI::ExecuteReclaim(Command& c)
{
	// not all builders are reclaim-capable by default
	if (!owner->unitDef->canReclaim)
		return;

	if (c.params.size() == 1 || c.params.size() == 5) {
		const int signedId = (int) c.params[0];

		if (signedId < 0) {
			LOG_L(L_WARNING, "Trying to reclaim unit or feature with id < 0 (%i), aborting.", signedId);
			return;
		}

		const unsigned int uid = signedId;

		const bool checkForBetterTarget = ((++randomCounter % 5) == 0);
		if (checkForBetterTarget && (c.options & INTERNAL_ORDER) && (c.params.size() >= 5)) {
			// regular check if there is a closer reclaim target
			CSolidObject* obj;
			if (uid >= unitHandler->MaxUnits()) {
				obj = featureHandler->GetFeature(uid - unitHandler->MaxUnits());
			} else {
				obj = unitHandler->GetUnit(uid);
			}
			if (obj) {
				const float3& pos = c.GetPos(1);
				const float radius = c.params[4];
				const float curdist = pos.SqDistance2D(obj->pos);

				const bool recUnits = !!(c.options & META_KEY);
				const bool recEnemyOnly = (c.options & META_KEY) && (c.options & CONTROL_KEY);
				const bool recSpecial = !!(c.options & CONTROL_KEY);

				ReclaimOption recopt = REC_NORESCHECK;
				if (recUnits)     recopt |= REC_UNITS;
				if (recEnemyOnly) recopt |= REC_ENEMYONLY;
				if (recSpecial)   recopt |= REC_SPECIAL;

				const int rid = FindReclaimTarget(pos, radius, c.options, recopt, curdist);
				if ((rid > 0) && (rid != uid)) {
					FinishCommand();
					RemoveUnitFromReclaimers(owner);
					RemoveUnitFromFeatureReclaimers(owner);
					return;
				}
			}
		}

		if (uid >= unitHandler->MaxUnits()) { // reclaim feature
			CFeature* feature = featureHandler->GetFeature(uid - unitHandler->MaxUnits());

			if (feature != NULL) {
				bool featureBeingResurrected = IsFeatureBeingResurrected(feature->id, owner);
				featureBeingResurrected &= (c.options & INTERNAL_ORDER) && !(c.options & CONTROL_KEY);

				if (featureBeingResurrected || !ReclaimObject(feature)) {
					StopMove();
					FinishCommand();
					RemoveUnitFromFeatureReclaimers(owner);
				} else {
					AddUnitToFeatureReclaimers(owner);
				}
			} else {
				StopMove();
				FinishCommand();
				RemoveUnitFromFeatureReclaimers(owner);
			}

			RemoveUnitFromReclaimers(owner);
		} else { // reclaim unit
			CUnit* unit = unitHandler->GetUnit(uid);

			if (unit != NULL && c.params.size() == 5) {
				const float3& pos = c.GetPos(1);
				const float radius = c.params[4] + 100.0f; // do not walk too far outside reclaim area

				const bool outOfReclaimRange =
					(pos.SqDistance2D(unit->pos) > radius * radius) ||
					(ownerBuilder->curReclaim == unit && unit->IsMoving() && !IsInBuildRange(unit));
				const bool busyAlliedBuilder =
					unit->unitDef->builder &&
					!unit->commandAI->commandQue.empty() &&
					teamHandler->Ally(owner->allyteam, unit->allyteam);

				if (outOfReclaimRange || busyAlliedBuilder) {
					StopMove();
					RemoveUnitFromReclaimers(owner);
					FinishCommand();
					RemoveUnitFromFeatureReclaimers(owner);
					return;
				}
			}

			if (unit != NULL && unit != owner && unit->unitDef->reclaimable && UpdateTargetLostTimer(unit->id) && unit->AllowedReclaim(owner)) {
				if (!ReclaimObject(unit)) {
					StopMove();
					FinishCommand();
				} else {
					AddUnitToReclaimers(owner);
				}
			} else {
				RemoveUnitFromReclaimers(owner);
				FinishCommand();
			}

			RemoveUnitFromFeatureReclaimers(owner);
		}
	} else if (c.params.size() == 4) {
		// area reclaim
		const float3 pos = c.GetPos(0);
		const float radius = c.params[3];
		const bool recUnits = !!(c.options & META_KEY);
		const bool recEnemyOnly = (c.options & META_KEY) && (c.options & CONTROL_KEY);
		const bool recSpecial = !!(c.options & CONTROL_KEY);

		RemoveUnitFromReclaimers(owner);
		RemoveUnitFromFeatureReclaimers(owner);
		ownerBuilder->StopBuild();

		ReclaimOption recopt = REC_NORESCHECK;
		if (recUnits)     recopt |= REC_UNITS;
		if (recEnemyOnly) recopt |= REC_ENEMYONLY;
		if (recSpecial)   recopt |= REC_SPECIAL;

		if (FindReclaimTargetAndReclaim(pos, radius, c.options, recopt)) {
			inCommand = false;
			SlowUpdate();
			return;
		}
		if(!(c.options & ALT_KEY)){
			FinishCommand();
		}
	} else {
		// wrong number of parameters
		RemoveUnitFromReclaimers(owner);
		RemoveUnitFromFeatureReclaimers(owner);
		FinishCommand();
	}
}
void CAirCAI::SlowUpdate()
{
	if(!commandQue.empty() && commandQue.front().timeOut<gs->frameNum){
		FinishCommand();
		return;
	}

	CAirMoveType* myPlane=(CAirMoveType*)owner->moveType;

	if(owner->unitDef->maxFuel>0){
		if(myPlane->reservedPad){
			return;
		}else{
			if(owner->currentFuel <= 0){
				owner->userAttackGround=false;
				owner->userTarget=0;
				inCommand=false;

				CAirBaseHandler::LandingPad* lp=airBaseHandler->FindAirBase(owner,8000,owner->unitDef->minAirBasePower);
				if(lp){
					myPlane->AddDeathDependence(lp);
					myPlane->reservedPad=lp;
					myPlane->padStatus=0;
					myPlane->oldGoalPos=myPlane->goalPos;
					return;
				}
				float3 landingPos = airBaseHandler->FindClosestAirBasePos(owner,8000,owner->unitDef->minAirBasePower);
				if(landingPos != ZeroVector && owner->pos.distance2D(landingPos) > 300){
					if(myPlane->aircraftState == CAirMoveType::AIRCRAFT_LANDED && owner->pos.distance2D(landingPos) > 800)
						myPlane->SetState(CAirMoveType::AIRCRAFT_TAKEOFF);	
					myPlane->goalPos=landingPos;		
				} else {
					if(myPlane->aircraftState == CAirMoveType::AIRCRAFT_FLYING)
						myPlane->SetState(CAirMoveType::AIRCRAFT_LANDING);	
				}
				return;
			}
			if(owner->currentFuel < myPlane->repairBelowHealth*owner->unitDef->maxFuel){
				if(commandQue.empty() || commandQue.front().id==CMD_PATROL){
					CAirBaseHandler::LandingPad* lp=airBaseHandler->FindAirBase(owner,8000,owner->unitDef->minAirBasePower);
					if(lp){
						owner->userAttackGround=false;
						owner->userTarget=0;
						inCommand=false;
						myPlane->AddDeathDependence(lp);
						myPlane->reservedPad=lp;
						myPlane->padStatus=0;
						myPlane->oldGoalPos=myPlane->goalPos;
						if(myPlane->aircraftState==CAirMoveType::AIRCRAFT_LANDED){
							myPlane->SetState(CAirMoveType::AIRCRAFT_TAKEOFF);
						}
						return;
					}		
				}
			}
		}
	}
	
	if(commandQue.empty()){
		if(myPlane->aircraftState==CAirMoveType::AIRCRAFT_FLYING && !owner->unitDef->DontLand ())
			myPlane->SetState(CAirMoveType::AIRCRAFT_LANDING);

		if(owner->unitDef->canAttack && owner->fireState==2 && owner->moveState!=0 && owner->maxRange>0){
			if(myPlane->isFighter){
				float testRad=1000*owner->moveState;
				CUnit* enemy=helper->GetClosestEnemyAircraft(owner->pos+owner->speed*10,testRad,owner->allyteam);
				if(enemy && !enemy->crashing){
					Command nc;
					nc.id=CMD_ATTACK;
					nc.params.push_back(enemy->id);
					nc.options=0;
					commandQue.push_front(nc);
					inCommand=false;
					return;
				}
			}
			float testRad=500*owner->moveState;
			CUnit* enemy=helper->GetClosestEnemyUnit(owner->pos+owner->speed*20,testRad,owner->allyteam);
			if(enemy && (owner->hasUWWeapons || !enemy->isUnderWater)  && !enemy->crashing && (myPlane->isFighter || !enemy->unitDef->canfly)){
				Command nc;
				nc.id=CMD_ATTACK;
				nc.params.push_back(enemy->id);
				nc.options=0;
				commandQue.push_front(nc);
				inCommand=false;
				return;
			}
		}
		return;
	}

	Command& c = commandQue.front();

	if (c.id == CMD_WAIT) {
		if ((myPlane->aircraftState == CAirMoveType::AIRCRAFT_FLYING)
		    && !owner->unitDef->DontLand()) {
			myPlane->SetState(CAirMoveType::AIRCRAFT_LANDING);
		}
		return;
	}

	if (c.id != CMD_STOP) {
		if (myPlane->aircraftState == CAirMoveType::AIRCRAFT_LANDED) {
			myPlane->SetState(CAirMoveType::AIRCRAFT_TAKEOFF);
		}
		if (myPlane->aircraftState == CAirMoveType::AIRCRAFT_LANDING) {
			myPlane->SetState(CAirMoveType::AIRCRAFT_FLYING);
		}
	}

	float3 curPos=owner->pos;

	switch(c.id){
	case CMD_STOP:{
		CCommandAI::SlowUpdate();
		break;
	}
	case CMD_MOVE:{
		if(tempOrder){
			tempOrder=false;
			inCommand=true;
		}
		if(!inCommand){
			inCommand=true;
			commandPos1=myPlane->owner->pos;
		}
		float3 pos(c.params[0],c.params[1],c.params[2]);
		commandPos2=pos;
		myPlane->goalPos=pos;
		if(owner->unitDef->canAttack && !(c.options&CONTROL_KEY)){
			if(owner->fireState==2 && owner->moveState!=0 && owner->maxRange>0){
				if(myPlane->isFighter){
					float testRad=500*owner->moveState;
					CUnit* enemy=helper->GetClosestEnemyAircraft(owner->pos+owner->speed*20,testRad,owner->allyteam);
					if(enemy && ((enemy->unitDef->canfly && !enemy->crashing && myPlane->isFighter) || (!enemy->unitDef->canfly && (!myPlane->isFighter || owner->moveState==2)))){
						if(owner->moveState!=1 || LinePointDist(commandPos1,commandPos2,enemy->pos) < 1000){
							Command nc;
							nc.id=CMD_ATTACK;
							nc.params.push_back(enemy->id);
							nc.options=c.options;
							commandQue.push_front(nc);
							tempOrder=true;
							inCommand=false;
							SlowUpdate();
							return;
						}
					}
				}
				if((!myPlane->isFighter || owner->moveState==2) && owner->maxRange>0){
					float testRad=325*owner->moveState;
					CUnit* enemy=helper->GetClosestEnemyUnit(owner->pos+owner->speed*30,testRad,owner->allyteam);
					if(enemy && (owner->hasUWWeapons || !enemy->isUnderWater) && ((enemy->unitDef->canfly && !enemy->crashing && myPlane->isFighter) || (!enemy->unitDef->canfly && (!myPlane->isFighter || owner->moveState==2)))){
						if(owner->moveState!=1 || LinePointDist(commandPos1,commandPos2,enemy->pos) < 1000){
							Command nc;
							nc.id=CMD_ATTACK;
							nc.params.push_back(enemy->id);
							nc.options=c.options;
							commandQue.push_front(nc);
							tempOrder=true;
							inCommand=false;
							SlowUpdate();
							return;
						}
					}
				}
			}
		}
		if((curPos-pos).SqLength2D()<16000){
			FinishCommand();
		}
		break;}
	case CMD_PATROL:{
		assert(owner->unitDef->canPatrol);
		float3 curPos=owner->pos;
		if(c.params.size()<3){		//this shouldnt happen but anyway ...
			logOutput.Print("Error: got patrol cmd with less than 3 params on %s in aircai",
				owner->unitDef->humanName.c_str());
			return;
		}
		Command temp;
		temp.id = CMD_FIGHT;
		temp.params.push_back(c.params[0]);
		temp.params.push_back(c.params[1]);
		temp.params.push_back(c.params[2]);
		temp.options = c.options|INTERNAL_ORDER;
		commandQue.push_back(c);
		commandQue.pop_front();
		commandQue.push_front(temp);
		if(owner->group){
			owner->group->CommandFinished(owner->id,CMD_PATROL);
		}
		SlowUpdate();
		break;}
	case CMD_FIGHT:{
		assert((c.options & INTERNAL_ORDER) || owner->unitDef->canFight);
		if(tempOrder){
			tempOrder=false;
			inCommand=true;
		}
		if(c.params.size()<3){		//this shouldnt happen but anyway ...
			logOutput.Print("Error: got fight cmd with less than 3 params on %s in AirCAI", owner->unitDef->humanName.c_str());
			return;
		}
		if(c.params.size() >= 6){
			if(!inCommand){
				commandPos1 = float3(c.params[3],c.params[4],c.params[5]);
			}
		} else {
			// Some hackery to make sure the line (commandPos1,commandPos2) is NOT
			// rotated (only shortened) if we reach this because the previous return
			// fight command finished by the 'if((curPos-pos).SqLength2D()<(127*127)){'
			// condition, but is actually updated correctly if you click somewhere
			// outside the area close to the line (for a new command).
			commandPos1 = ClosestPointOnLine(commandPos1, commandPos2, curPos);
			if ((curPos-commandPos1).SqLength2D()>(150*150)) {
				commandPos1 = curPos;
			}
		}
		goalPos=float3(c.params[0],c.params[1],c.params[2]);
		if(!inCommand){
			inCommand = true;
			commandPos2=goalPos;
		}

		// CMD_FIGHT is pretty useless if !canAttack but we try to honour the modders wishes anyway...
		if (owner->unitDef->canAttack && owner->fireState == 2 && owner->moveState != 0 && owner->maxRange > 0) {
			float3 curPosOnLine = ClosestPointOnLine(commandPos1, commandPos2, owner->pos+owner->speed*10);
			float testRad=1000*owner->moveState;
			CUnit* enemy = helper->GetClosestEnemyAircraft(curPosOnLine,testRad,owner->allyteam);
			if(myPlane->isFighter
				&& enemy && !enemy->crashing
				&& (owner->moveState!=1 || LinePointDist(commandPos1,commandPos2,enemy->pos) < 1000))
			{
				Command nc;
				nc.id=CMD_ATTACK;
				nc.params.push_back(enemy->id);
				nc.options=c.options|INTERNAL_ORDER;
				commandQue.push_front(nc);
				tempOrder=true;
				inCommand=false;
				if(lastPC1!=gs->frameNum){	//avoid infinite loops
					lastPC1=gs->frameNum;
					SlowUpdate();
				}
				return;
			} else {
				float3 curPosOnLine = ClosestPointOnLine(commandPos1, commandPos2, owner->pos+owner->speed*20);
				float testRad=500*owner->moveState;
				enemy=helper->GetClosestEnemyUnit(curPosOnLine,testRad,owner->allyteam);
				if(enemy && (owner->hasUWWeapons || !enemy->isUnderWater)  && !enemy->crashing
					&& (myPlane->isFighter || !enemy->unitDef->canfly))
				{
					Command nc;
					nc.id=CMD_ATTACK;
					nc.params.push_back(enemy->id);
					nc.options=c.options|INTERNAL_ORDER;
					PushOrUpdateReturnFight();
					commandQue.push_front(nc);
					tempOrder=true;
					inCommand=false;
					if(lastPC2!=gs->frameNum){	//avoid infinite loops
						lastPC2=gs->frameNum;
						SlowUpdate();
					}
					return;
				}
			}
		}
		myPlane->goalPos=goalPos;

		if((owner->pos-goalPos).SqLength2D()<(127*127) || (owner->pos+owner->speed*8-goalPos).SqLength2D()<(127*127)){
			FinishCommand();
		}
		return;}
	case CMD_ATTACK:
		assert(owner->unitDef->canAttack);
		targetAge++;
		if(tempOrder && owner->moveState==1){		//limit how far away we fly
			if(orderTarget && LinePointDist(commandPos1,commandPos2,orderTarget->pos) > 1500){
				owner->SetUserTarget(0);
				FinishCommand();
				break;
			}
		}
		if(tempOrder && myPlane->isFighter && orderTarget){
			if(owner->fireState==2 && owner->moveState!=0){
				CUnit* enemy=helper->GetClosestEnemyAircraft(owner->pos+owner->speed*50,800,owner->allyteam);
				if(enemy && (!orderTarget->unitDef->canfly || (orderTarget->pos-owner->pos+owner->speed*50).Length()+100*gs->randFloat()*40-targetAge < (enemy->pos-owner->pos+owner->speed*50).Length())){
					c.params.clear();
					c.params.push_back(enemy->id);
					inCommand=false;
				}
			}
		}
		if(inCommand){
			if(targetDied || (c.params.size() == 1 && UpdateTargetLostTimer(int(c.params[0])) == 0)){
				FinishCommand();
				break;
			}
			if ((c.params.size() == 3) && (owner->commandShotCount > 0) && (commandQue.size() > 1)) {
				owner->AttackUnit(0,true); 
				FinishCommand();
			  break;
			}
			if(orderTarget && orderTarget->unitDef->canfly && orderTarget->crashing){
				owner->SetUserTarget(0);
				FinishCommand();
				break;
			}
			if(orderTarget){
//				myPlane->goalPos=orderTarget->pos;
			} else {
//				float3 pos(c.params[0],c.params[1],c.params[2]);
//				myPlane->goalPos=pos;
			}
		} else {
			targetAge=0;
			if(c.params.size()==1){
				if(uh->units[int(c.params[0])]!=0 && uh->units[int(c.params[0])]!=owner){
					orderTarget=uh->units[int(c.params[0])];
					owner->AttackUnit(orderTarget,false);
					AddDeathDependence(orderTarget);
					inCommand=true;
				} else {
					FinishCommand();
					break;
				}
			} else {
				float3 pos(c.params[0],c.params[1],c.params[2]);
				owner->AttackGround(pos,false);
				inCommand=true;
			}
		}
		break;
	case CMD_AREA_ATTACK:{
		assert(owner->unitDef->canAttack);
		if(targetDied){
			targetDied=false;
			inCommand=false;
		}
		float3 pos(c.params[0],c.params[1],c.params[2]);
		float radius=c.params[3];
		if(inCommand){
			if(myPlane->aircraftState==CAirMoveType::AIRCRAFT_LANDED)
				inCommand=false;
			if(orderTarget && orderTarget->pos.distance2D(pos)>radius){
				inCommand=false;
				DeleteDeathDependence(orderTarget);
				orderTarget=0;
			}
			if ((c.params.size() == 4) && (owner->commandShotCount > 0) && (commandQue.size() > 1)) {
				owner->AttackUnit(0,true); 
				FinishCommand();
			}
		} else {
			if(myPlane->aircraftState!=CAirMoveType::AIRCRAFT_LANDED){
				inCommand=true;
				std::vector<int> eu;
				helper->GetEnemyUnits(pos,radius,owner->allyteam,eu);
				if(eu.empty()){
					float3 attackPos=pos+gs->randVector()*radius;
					attackPos.y=ground->GetHeight(attackPos.x,attackPos.z);
					owner->AttackGround(attackPos,false);
				} else {
					int num=(int) (gs->randFloat()*eu.size());
					orderTarget=uh->units[eu[num]];
					owner->AttackUnit(orderTarget,false);
					AddDeathDependence(orderTarget);
				}
			}
		}
		break;}
	case CMD_GUARD:
		assert(owner->unitDef->canGuard);
		if (int(c.params[0]) >= 0 && uh->units[int(c.params[0])] != NULL && UpdateTargetLostTimer(int(c.params[0]))) {
			CUnit* guarded=uh->units[int(c.params[0])];
			if(owner->unitDef->canAttack && guarded->lastAttacker && guarded->lastAttack+40<gs->frameNum && owner->maxRange>0 && (owner->hasUWWeapons || !guarded->lastAttacker->isUnderWater)){
				Command nc;
				nc.id=CMD_ATTACK;
				nc.params.push_back(guarded->lastAttacker->id);
				nc.options=c.options;
				commandQue.push_front(nc);
				SlowUpdate();
				return;
			} else {
				Command c2;
				c2.id=CMD_MOVE;
				c2.options=c.options;
				c2.params.push_back(guarded->pos.x);
				c2.params.push_back(guarded->pos.y);
				c2.params.push_back(guarded->pos.z);
				c2.timeOut=gs->frameNum+60;
				commandQue.push_front(c2);
				return;
			}
		} else {
			FinishCommand();
		}
		break;
	default:
		CCommandAI::SlowUpdate();
		break;
	}
}
Exemple #5
0
void CCommandAI::ExecuteInsert(const Command& c, bool fromSynced)
{
	if (c.params.size() < 3) {
		return;
	}

	// make the command
	Command newCmd((int)c.params[1], (unsigned char)c.params[2]);
	for (int p = 3; p < (int)c.params.size(); p++) {
		newCmd.params.push_back(c.params[p]);
	}

	// validate the command
	if (!AllowedCommand(newCmd, fromSynced)) {
		return;
	}

	CCommandQueue* queue = &commandQue;

	bool facBuildQueue = false;
	CFactoryCAI* facCAI = dynamic_cast<CFactoryCAI*>(this);
	if (facCAI) {
		if (c.options & CONTROL_KEY) {
			// check the build order
			const map<int, CFactoryCAI::BuildOption>& bOpts = facCAI->buildOptions;
			if ((newCmd.GetID() != CMD_STOP) && (newCmd.GetID() != CMD_WAIT) &&
			    ((newCmd.GetID() >= 0) || (bOpts.find(newCmd.GetID()) == bOpts.end()))) {
				return;
			}
			facBuildQueue = true;
		} else {
			// use the new commands
			queue = &facCAI->newUnitCommands;
		}
	}

	// FIXME: handle CMD_LOOPBACKATTACK, etc...

	CCommandQueue::iterator insertIt = queue->begin();

	if (c.options & ALT_KEY) {
		// treat param0 as a position
		int pos = (int)c.params[0];
		const unsigned int qsize = queue->size();
		if (pos < 0) {
			pos = qsize + pos + 1; // convert the negative index
			if (pos < 0) {
				pos = 0;
			}
		}
		if (pos > qsize) {
			pos = qsize;
		}
		std::advance(insertIt, pos);
	}
	else {
		// treat param0 as a command tag
		const unsigned int tag = (unsigned int)c.params[0];
		CCommandQueue::iterator ci;
		bool found = false;
		for (ci = queue->begin(); ci != queue->end(); ++ci) {
			const Command& qc = *ci;
			if (qc.tag == tag) {
				insertIt = ci;
				found = true;
				break;
			}
		}
		if (!found) {
			return;
		}
		if ((c.options & RIGHT_MOUSE_KEY) && (insertIt != queue->end())) {
			++insertIt; // insert after the tagged command
		}
	}

	if (facBuildQueue) {
		facCAI->InsertBuildCommand(insertIt, newCmd);
		if (!owner->stunned) {
			SlowUpdate();
		}
		return;
	}

	// shutdown the current order if the insertion is at the beginning
	if (!queue->empty() && (insertIt == queue->begin())) {
		inCommand = false;
		targetDied = false;
		unimportantMove = false;
		SetOrderTarget(NULL);
		const Command& cmd = queue->front();
		eoh->CommandFinished(*owner, cmd);
		eventHandler.UnitCmdDone(owner, cmd.GetID(), cmd.tag);
	}

	queue->insert(insertIt, newCmd);

	if (!owner->stunned) {
		SlowUpdate();
	}
}
Exemple #6
0
void CBuilderCAI::ExecuteRepair(Command& c)
{
	// not all builders are repair-capable by default
	if (!owner->unitDef->canRepair)
		return;

	if (c.params.size() == 1 || c.params.size() == 5) {
		// repair unit
		CUnit* unit = unitHandler->GetUnit(c.params[0]);

		if (unit == NULL) {
			FinishCommand();
			return;
		}

		if (tempOrder && owner->moveState <= MOVESTATE_MANEUVER) {
			// limit how far away we go when not roaming
			if (LinePointDist(commandPos1, commandPos2, unit->pos) > std::max(500.0f, GetBuildRange(unit->radius))) {
				StopMove();
				FinishCommand();
				return;
			}
		}

		if (c.params.size() == 5) {
			const float3& pos = c.GetPos(1);
			const float radius = c.params[4] + 100.0f; // do not walk too far outside repair area

			if ((pos - unit->pos).SqLength2D() > radius * radius ||
				(unit->IsMoving() && (((c.options & INTERNAL_ORDER) && !TargetInterceptable(unit, unit->speed.Length2D())) || ownerBuilder->curBuild == unit)
				&& !IsInBuildRange(unit))) {
				StopMove();
				FinishCommand();
				return;
			}
		}

		// do not consider units under construction irreparable
		// even if they can be repaired
		bool canRepairUnit = true;
		canRepairUnit &= ((unit->beingBuilt) || (unit->unitDef->repairable && (unit->health < unit->maxHealth)));
		canRepairUnit &= ((unit != owner) || owner->unitDef->canSelfRepair);
		canRepairUnit &= (!unit->soloBuilder || (unit->soloBuilder == owner));
		canRepairUnit &= (!(c.options & INTERNAL_ORDER) || (c.options & CONTROL_KEY) || !IsUnitBeingReclaimed(unit, owner));
		canRepairUnit &= (UpdateTargetLostTimer(unit->id) != 0);

		if (canRepairUnit) {
			if (MoveInBuildRange(unit)) {
				ownerBuilder->SetRepairTarget(unit);
			}
		} else {
			StopMove();
			FinishCommand();
		}
	} else if (c.params.size() == 4) {
		// area repair
		const float3 pos = c.GetPos(0);
		const float radius = c.params[3];

		ownerBuilder->StopBuild();
		if (FindRepairTargetAndRepair(pos, radius, c.options, false, (c.options & META_KEY))) {
			inCommand = false;
			SlowUpdate();
			return;
		}
		if (!(c.options & ALT_KEY)) {
			FinishCommand();
		}
	} else {
		FinishCommand();
	}
}
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 (f3Dist(fac->pos, 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 {
					StopSlowGuard();
					SetGoal(b->terraformCenter,fac->pos,fac->buildDistance*0.7f+b->terraformRadius*0.6f);
				}
				return;
			} else if (b->curReclaim && owner->unitDef->canReclaim){
				StopSlowGuard();
				if(!ReclaimObject(b->curReclaim)){
					StopMove();
				}
				return;
			} else {
				fac->StopBuild();
			}
			if (b->curBuild &&
			    (!b->curBuild->soloBuilder || (b->curBuild->soloBuilder == owner)) &&
			    (( b->curBuild->beingBuilt && owner->unitDef->canAssist) ||
			     (!b->curBuild->beingBuilt && owner->unitDef->canRepair))) {
				StopSlowGuard();
				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->soloBuilder || (f->curBuild->soloBuilder == owner)) &&
			    (( f->curBuild->beingBuilt && owner->unitDef->canAssist) ||
			     (!f->curBuild->beingBuilt && owner->unitDef->canRepair))) {
				StopSlowGuard();
				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;
			}
		}
		if (IsUnitBeingReclaimedByFriend(guarded))
			return;
		float3 curPos = owner->pos;
		float3 dif = guarded->pos - curPos;
		dif.Normalize();
		float3 goal = guarded->pos - dif * (fac->buildDistance * 0.5f);
		if (f3SqLen(guarded->pos - curPos) >
				(fac->buildDistance*1.1f + guarded->radius) *
				(fac->buildDistance*1.1f + guarded->radius)) {
			StopSlowGuard();
		}
		if (f3SqLen(guarded->pos - curPos) <
				(fac->buildDistance*0.9f + guarded->radius) *
				(fac->buildDistance*0.9f + guarded->radius)) {
			StartSlowGuard(guarded->maxSpeed);
			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->soloBuilder || (guarded->soloBuilder == owner)) &&
			    (( guarded->beingBuilt && owner->unitDef->canAssist) ||
			     (!guarded->beingBuilt && owner->unitDef->canRepair))) {
				StopSlowGuard();

				Command nc;
				nc.id=CMD_REPAIR;
				nc.options=c.options;
				nc.params.push_back(guarded->id);
				commandQue.push_front(nc);
				inCommand=false;
				return;
			} else {
				NonMoving();
			}
		} else {
			if (f3SqLen(goalPos - goal) > 4000
					|| f3SqLen(goalPos - owner->pos) <
					   (owner->maxSpeed*30 + 1 + SQUARE_SIZE*2) *
					   (owner->maxSpeed*30 + 1 + SQUARE_SIZE*2)){
				SetGoal(goal,curPos);
			}
		}
	} else {
		FinishCommand();
	}
	return;
}
void CAirCAI::SlowUpdate()
{
	if(!commandQue.empty() && commandQue.front().timeOut<gs->frameNum){
		FinishCommand();
		return;
	}

	CAirMoveType* myPlane=(CAirMoveType*)owner->moveType;

	if(commandQue.empty()){
		if(myPlane->aircraftState==CAirMoveType::AIRCRAFT_FLYING)
			myPlane->SetState(CAirMoveType::AIRCRAFT_LANDING);

		if(owner->fireState==2 && owner->moveState!=0 && owner->maxRange>0){
			if(myPlane->isFighter){
				float testRad=1000*owner->moveState;
				CUnit* enemy=helper->GetClosestEnemyAircraft(owner->pos+owner->speed*10,testRad,owner->allyteam);
				if(enemy && !enemy->crashing){
					Command nc;
					nc.id=CMD_ATTACK;
					nc.params.push_back(enemy->id);
					nc.options=0;
					commandQue.push_front(nc);
					inCommand=false;
					return;
				}
			}
			if(owner->moveState && owner->fireState==2 && owner->maxRange>0){
				float testRad=500*owner->moveState;
				CUnit* enemy=helper->GetClosestEnemyUnit(owner->pos+owner->speed*20,testRad,owner->allyteam);
				if(enemy && (owner->hasUWWeapons || !enemy->isUnderWater)  && !enemy->crashing && (myPlane->isFighter || !enemy->unitDef->canfly)){
					Command nc;
					nc.id=CMD_ATTACK;
					nc.params.push_back(enemy->id);
					nc.options=0;
					commandQue.push_front(nc);
					inCommand=false;
					return;
				}
			}
		}
		return;
	}

	if(myPlane->aircraftState==CAirMoveType::AIRCRAFT_LANDED && commandQue.front().id!=CMD_STOP){
		myPlane->SetState(CAirMoveType::AIRCRAFT_TAKEOFF);
	}

	if(myPlane->aircraftState==CAirMoveType::AIRCRAFT_LANDING && commandQue.front().id!=CMD_STOP){
		myPlane->SetState(CAirMoveType::AIRCRAFT_FLYING);
	}

	float3 curPos=owner->pos;

	Command& c=commandQue.front();
	switch(c.id){
	case CMD_STOP:{
		CCommandAI::SlowUpdate();
		break;}
	case CMD_MOVE:{
		if(tempOrder)
			tempOrder=false;
		float3 pos(c.params[0],c.params[1],c.params[2]);
		myPlane->goalPos=pos;
		if(!(c.options&CONTROL_KEY)){
			if(owner->fireState==2 && owner->moveState!=0 && owner->maxRange>0){
				if(myPlane->isFighter){
					float testRad=500*owner->moveState;
					CUnit* enemy=helper->GetClosestEnemyAircraft(owner->pos+owner->speed*20,testRad,owner->allyteam);
					if(enemy && ((enemy->unitDef->canfly && !enemy->crashing && myPlane->isFighter) || (!enemy->unitDef->canfly && (!myPlane->isFighter || owner->moveState==2)))){
						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)))){
						Command nc;
						nc.id=CMD_ATTACK;
						nc.params.push_back(enemy->id);
						nc.options=c.options;
						commandQue.push_front(nc);
						tempOrder=true;
						inCommand=false;
						SlowUpdate();
						return;
					}
				}
			}
		}
		if((curPos-pos).SqLength2D()<16000){
			FinishCommand();
		}
		break;}
	case CMD_PATROL:{
		if(tempOrder){
			tempOrder=false;
			inCommand=true;
		}
		if(!inCommand){
			float3 pos(c.params[0],c.params[1],c.params[2]);
			inCommand=true;
			Command co;
			co.id=CMD_PATROL;
			co.params.push_back(curPos.x);
			co.params.push_back(curPos.y);
			co.params.push_back(curPos.z);
			commandQue.push_front(co);
			activeCommand=1;
			patrolTime=3;
		}
		Command& c=commandQue[activeCommand];
		if(c.params.size()<3){
			info->AddLine("Patrol command with insufficient parameters ?");
			return;
		}
		goalPos=float3(c.params[0],c.params[1],c.params[2]);
		patrolTime++;

		if(owner->fireState==2 && owner->moveState!=0 && owner->maxRange>0){
			if(myPlane->isFighter){
				float testRad=1000*owner->moveState;
				CUnit* enemy=helper->GetClosestEnemyAircraft(owner->pos+owner->speed*10,testRad,owner->allyteam);
				if(enemy && !enemy->crashing){
					Command nc;
					nc.id=CMD_ATTACK;
					nc.params.push_back(enemy->id);
					nc.options=c.options;
					commandQue.push_front(nc);
					tempOrder=true;
					inCommand=false;
					SlowUpdate();
					return;
				}
			}
			if((!myPlane->isFighter || owner->moveState==2) && owner->maxRange>0){
				float testRad=500*owner->moveState;
				CUnit* enemy=helper->GetClosestEnemyUnit(owner->pos+owner->speed*20,testRad,owner->allyteam);
				if(enemy && (owner->hasUWWeapons || !enemy->isUnderWater)  && !enemy->crashing && (myPlane->isFighter || !enemy->unitDef->canfly)){
					Command nc;
					nc.id=CMD_ATTACK;
					nc.params.push_back(enemy->id);
					nc.options=c.options;
					commandQue.push_front(nc);
					tempOrder=true;
					inCommand=false;
					SlowUpdate();
					return;
				}
			}
		}
		myPlane->goalPos=goalPos;

		if((owner->pos-goalPos).SqLength2D()<16000 || (owner->pos+owner->speed*8-goalPos).SqLength2D()<16000){
			if(owner->group)
				owner->group->CommandFinished(owner->id,CMD_PATROL);

			if((int)commandQue.size()<=activeCommand+1)
				activeCommand=0;
			else {
				if(commandQue[activeCommand+1].id!=CMD_PATROL)
					activeCommand=0;
				else
					activeCommand++;
			}
		}
		return;}
	case CMD_ATTACK:
		targetAge++;
		if(tempOrder && myPlane->isFighter && orderTarget){
			if(owner->fireState==2 && owner->moveState!=0){
				CUnit* enemy=helper->GetClosestEnemyAircraft(owner->pos+owner->speed*50,800,owner->allyteam);
				if(enemy && (!orderTarget->unitDef->canfly || (orderTarget->pos-owner->pos+owner->speed*50).Length()+100*gs->randFloat()*40-targetAge < (enemy->pos-owner->pos+owner->speed*50).Length())){
					c.params.clear();
					c.params.push_back(enemy->id);
					inCommand=false;
				}
			}
		}
		if(inCommand){
			if(targetDied){
				FinishCommand();
				break;
			}
			if(orderTarget && orderTarget->unitDef->canfly && orderTarget->crashing){
				owner->SetUserTarget(0);
				FinishCommand();
				break;
			}
			if(orderTarget){
//				myPlane->goalPos=orderTarget->pos;
			} else {
//				float3 pos(c.params[0],c.params[1],c.params[2]);
//				myPlane->goalPos=pos;
			}
		} else {
			targetAge=0;
			if(c.params.size()==1){
				if(uh->units[int(c.params[0])]!=0 && uh->units[int(c.params[0])]!=owner){
					orderTarget=uh->units[int(c.params[0])];
					owner->AttackUnit(orderTarget,false);
					AddDeathDependence(orderTarget);
					inCommand=true;
				} else {
					FinishCommand();
					break;
				}
			} else {
				float3 pos(c.params[0],c.params[1],c.params[2]);
				owner->AttackGround(pos,false);
				inCommand=true;
			}
		}
		break;
	case CMD_AREA_ATTACK:{
		if(targetDied){
			targetDied=false;
			inCommand=false;
		}
		float3 pos(c.params[0],c.params[1],c.params[2]);
		float radius=c.params[3];
		if(inCommand){
			if(myPlane->aircraftState==CAirMoveType::AIRCRAFT_LANDED)
				inCommand=false;
			if(orderTarget && orderTarget->pos.distance2D(pos)>radius){
				inCommand=false;
				DeleteDeathDependence(orderTarget);
				orderTarget=0;
			}
		} else {
			if(myPlane->aircraftState!=CAirMoveType::AIRCRAFT_LANDED){
				inCommand=true;
				std::vector<int> eu;
				helper->GetEnemyUnits(pos,radius,owner->allyteam,eu);
				if(eu.empty()){
					float3 attackPos=pos+gs->randVector()*radius;
					attackPos.y=ground->GetHeight(attackPos.x,attackPos.z);
					owner->AttackGround(attackPos,false);
				} else {
					int num=gs->randFloat()*eu.size();
					orderTarget=uh->units[eu[num]];
					owner->AttackUnit(orderTarget,false);
					AddDeathDependence(orderTarget);
				}
			}
		}
		break;}
	case CMD_GUARD:
		if(uh->units[int(c.params[0])]!=0){
			CUnit* guarded=uh->units[int(c.params[0])];
			if(guarded->lastAttacker && guarded->lastAttack+40<gs->frameNum && owner->maxRange>0 && (owner->hasUWWeapons || !guarded->lastAttacker->isUnderWater)){
				Command nc;
				nc.id=CMD_ATTACK;
				nc.params.push_back(guarded->lastAttacker->id);
				nc.options=c.options;
				commandQue.push_front(nc);
				SlowUpdate();
				return;
			} else {
				Command c2;
				c2.id=CMD_MOVE;
				c2.options=c.options;
				c2.params.push_back(guarded->pos.x);
				c2.params.push_back(guarded->pos.y);
				c2.params.push_back(guarded->pos.z);
				c2.timeOut=gs->frameNum+60;
				commandQue.push_front(c2);
				return;
			}
		} else {
			FinishCommand();
		}
		break;
	default:
		CCommandAI::SlowUpdate();
		break;
	}
}
void 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((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.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::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 (f3Dist(build.pos, fac->pos) > 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 dist = f3Dist(build.pos, fac->pos);

					if ((dist < (fac->buildDistance * 0.6f + radius)) ||
						(!owner->unitDef->canmove && (dist <= (fac->buildDistance + radius - 8.0f)))) {
						StopMove();

						if (luaRules && !luaRules->AllowUnitCreation(build.def, owner, &build.pos)) {
							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 == 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;
		}
	}
}
Exemple #11
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();
	}
}
Exemple #12
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) {
		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;
}
Exemple #13
0
void CTransportCAI::ExecuteLoadUnits(Command& c)
{
	CTransportUnit* transport = reinterpret_cast<CTransportUnit*>(owner);

	if (c.params.size() == 1) {
		// load single unit
		CUnit* unit = unitHandler->GetUnit(c.params[0]);

		if (!unit) {
			FinishCommand();
			return;
		}
		if (c.options & INTERNAL_ORDER) {
			if (unit->commandAI->commandQue.empty()) {
				if (!LoadStillValid(unit)) {
					FinishCommand();
					return;
				}
			} else {
				Command& currentUnitCommand = unit->commandAI->commandQue[0];
				if ((currentUnitCommand.GetID() == CMD_LOAD_ONTO) && (currentUnitCommand.params.size() == 1) && (int(currentUnitCommand.params[0]) == owner->id)) {
					if ((unit->moveType->progressState == AMoveType::Failed) && (owner->moveType->progressState == AMoveType::Failed)) {
						unit->commandAI->FinishCommand();
						FinishCommand();
						return;
					}
				} else if (!LoadStillValid(unit)) {
					FinishCommand();
					return;
				}
			}
		}
		if (inCommand) {
			if (!owner->script->IsBusy()) {
				FinishCommand();
			}
			return;
		}
		if (unit && CanTransport(unit) && UpdateTargetLostTimer(int(c.params[0]))) {
			SetTransportee(unit);

			const float sqDist = unit->pos.SqDistance2D(owner->pos);
			const bool inLoadingRadius = (sqDist <= Square(owner->unitDef->loadingRadius));
			
			CHoverAirMoveType* am = dynamic_cast<CHoverAirMoveType*>(owner->moveType);

			// subtracting 1 square to account for pathfinder/groundmovetype inaccuracy
			if (goalPos.SqDistance2D(unit->pos) > Square(owner->unitDef->loadingRadius - SQUARE_SIZE) || 
				(!inLoadingRadius && (!owner->isMoving || (am && am->aircraftState != AAirMoveType::AIRCRAFT_FLYING)))) {
				SetGoal(unit->pos, owner->pos, std::min(64.0f, owner->unitDef->loadingRadius));
			}

			if (inLoadingRadius) {
				if (am) { // handle air transports differently
					float3 wantedPos = unit->pos;
					wantedPos.y = static_cast<CTransportUnit*>(owner)->GetLoadUnloadHeight(wantedPos, unit);
					SetGoal(wantedPos, owner->pos);

					am->loadingUnits = true;
					am->ForceHeading(static_cast<CTransportUnit*>(owner)->GetLoadUnloadHeading(unit));
					am->SetWantedAltitude(wantedPos.y - ground->GetHeightAboveWater(wantedPos.x, wantedPos.z));
					am->maxDrift = 1;

					if ((owner->pos.SqDistance(wantedPos) < Square(AIRTRANSPORT_DOCKING_RADIUS)) &&
						(abs(owner->heading-unit->heading) < AIRTRANSPORT_DOCKING_ANGLE) &&
						(owner->updir.dot(UpVector) > 0.995f))
					{
						am->loadingUnits = false;
						am->dontLand = true;

						owner->script->BeginTransport(unit);
						SetTransportee(NULL);
						transport->AttachUnit(unit, owner->script->QueryTransport(unit));
						am->SetWantedAltitude(0);

						FinishCommand();
						return;
					}
				} else {
					inCommand = true;

					StopMove();
					owner->script->TransportPickup(unit);
				}
			} else if (owner->moveType->progressState == AMoveType::Failed && sqDist < (200 * 200)) {
				// if we're pretty close already but CGroundMoveType fails because it considers
				// the goal clogged (with the future passenger...), just try to move to the
				// point halfway between the transport and the passenger.
				SetGoal((unit->pos + owner->pos) * 0.5f, owner->pos);
			}
		} else {
			FinishCommand();
		}
	} else if (c.params.size() == 4) { // area-load
		if (lastCall == gs->frameNum) { // avoid infinite loops
			return;
		}

		lastCall = gs->frameNum;

		const float3 pos = c.GetPos(0);
		const float radius = c.params[3];

		CUnit* unit = FindUnitToTransport(pos, radius);

		if (unit && CanTransport(unit)) {
			Command c2(CMD_LOAD_UNITS, c.options|INTERNAL_ORDER, unit->id);
			commandQue.push_front(c2);
			inCommand = false;

			SlowUpdate();
			return;
		} else {
			FinishCommand();
			return;
		}
	}

	isFirstIteration = true;
	startingDropPos = float3(-1.0f, -1.0f, -1.0f);
}
Exemple #14
0
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::ExecuteReclaim(Command& c)
{
	assert(owner->unitDef->canReclaim);
	if(c.params.size()==1){
		int id=(int) c.params[0];
		if (id >= MAX_UNITS) {     //reclaim feature
			const CFeatureSet& fset = featureHandler->GetActiveFeatures();
			CFeatureSet::const_iterator it = fset.find(id - MAX_UNITS);
			if (it != fset.end()) {
				CFeature* feature = *it;
				if(!ReclaimObject(feature)){
					StopMove();
					FinishCommand();
					RemoveUnitFromFeatureReclaimers(owner);
				} else {
					AddUnitToFeatureReclaimers(owner);
				}
			} else {
				StopMove();
				FinishCommand();
				RemoveUnitFromFeatureReclaimers(owner);
			}
			RemoveUnitFromReclaimers(owner);

		} else {                   //reclaim unit
			CUnit* unit=uh->units[id];

			if(unit && unit!=owner && unit->unitDef->reclaimable && UpdateTargetLostTimer(id) && unit->AllowedReclaim(owner) ){
				if(!ReclaimObject(unit)){
					StopMove();
					FinishCommand();
				} else {
					AddUnitToReclaimers(owner);
				}
			} else {
				RemoveUnitFromReclaimers(owner);
				FinishCommand();
			}
			RemoveUnitFromFeatureReclaimers(owner);
		}
	} else if(c.params.size()==4){//area reclaim
		float3 pos(c.params[0],c.params[1],c.params[2]);
		float radius=c.params[3];
		const bool recAnyTeam = ((c.options & CONTROL_KEY) != 0);
		RemoveUnitFromReclaimers(owner);
		RemoveUnitFromFeatureReclaimers(owner);
		if (FindReclaimableFeatureAndReclaim(pos, radius, c.options, true, recAnyTeam)) {
			inCommand=false;
			SlowUpdate();
			return;
		}
		if(!(c.options & ALT_KEY)){
			FinishCommand();
		}
	} else {	//wrong number of parameters
		RemoveUnitFromReclaimers(owner);
		RemoveUnitFromFeatureReclaimers(owner);
		FinishCommand();
	}
	return;
}
void CAirCAI::ExecuteFight(Command &c)
{
	assert((c.options & INTERNAL_ORDER) || owner->unitDef->canFight);
	AAirMoveType* myPlane = (AAirMoveType*) owner->moveType;
	if(tempOrder){
		tempOrder=false;
		inCommand=true;
	}
	if(c.params.size()<3){		//this shouldnt happen but anyway ...
		logOutput.Print("Error: got fight cmd with less than 3 params on %s in AirCAI",
			owner->unitDef->humanName.c_str());
		return;
	}
	if(c.params.size() >= 6){
		if(!inCommand){
			commandPos1 = float3(c.params[3],c.params[4],c.params[5]);
		}
	} else {
		// Some hackery to make sure the line (commandPos1,commandPos2) is NOT
		// rotated (only shortened) if we reach this because the previous return
		// fight command finished by the 'if((curPos-pos).SqLength2D()<(127*127)){'
		// condition, but is actually updated correctly if you click somewhere
		// outside the area close to the line (for a new command).
		commandPos1 = ClosestPointOnLine(commandPos1, commandPos2, owner->pos);
		if ((owner->pos - commandPos1).SqLength2D() > (150 * 150)) {
			commandPos1 = owner->pos;
		}
	}
	goalPos = float3(c.params[0], c.params[1], c.params[2]);
	if(!inCommand){
		inCommand = true;
		commandPos2=goalPos;
	}
	if(c.params.size() >= 6){
		goalPos = ClosestPointOnLine(commandPos1, commandPos2, owner->pos);
	}

	// CMD_FIGHT is pretty useless if !canAttack but we try to honour the modders wishes anyway...
	if (owner->unitDef->canAttack && owner->fireState >= 2
			&& owner->moveState != 0 && owner->maxRange > 0) {
		float3 curPosOnLine = ClosestPointOnLine(commandPos1, commandPos2,
				owner->pos + owner->speed*10);
		float testRad = 1000 * owner->moveState;
		CUnit* enemy = NULL;
		if(myPlane->IsFighter()) {
			enemy = helper->GetClosestEnemyAircraft(curPosOnLine,
					testRad, owner->allyteam);
		}
		if(IsValidTarget(enemy) && (owner->moveState!=1
				|| LinePointDist(commandPos1, commandPos2, enemy->pos) < 1000))
		{
			Command nc;
			nc.id=CMD_ATTACK;
			nc.params.push_back(enemy->id);
			nc.options=c.options|INTERNAL_ORDER;
			commandQue.push_front(nc);
			tempOrder=true;
			inCommand=false;
			if(lastPC1!=gs->frameNum){	//avoid infinite loops
				lastPC1=gs->frameNum;
				SlowUpdate();
			}
			return;
		} else {
			float3 curPosOnLine = ClosestPointOnLine(
				commandPos1, commandPos2, owner->pos + owner->speed * 20);
			float testRad = 500 * owner->moveState;
			enemy = helper->GetClosestEnemyUnit(curPosOnLine, testRad, owner->allyteam);
			if(IsValidTarget(enemy)) {
				Command nc;
				nc.id = CMD_ATTACK;
				nc.params.push_back(enemy->id);
				nc.options = c.options | INTERNAL_ORDER;
				PushOrUpdateReturnFight();
				commandQue.push_front(nc);
				tempOrder = true;
				inCommand = false;
				if(lastPC2 != gs->frameNum){	//avoid infinite loops
					lastPC2 = gs->frameNum;
					SlowUpdate();
				}
				return;
			}
		}
	}
	myPlane->goalPos = goalPos;

	if((owner->pos - goalPos).SqLength2D() < (127 * 127)
			|| (owner->pos + owner->speed*8 - goalPos).SqLength2D() < (127 * 127)) {
		FinishCommand();
	}
	return;
}
void CBuilderCAI::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 (f3SqLen(owner->pos - commandPos1) > (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, false)) {
		tempOrder = true;
		inCommand = false;
		if (lastPC2 != gs->frameNum) {  //avoid infinite loops
			lastPC2 = gs->frameNum;
			SlowUpdate();
		}
		return;
	}
	if (f3SqLen(owner->pos - pos) < (64*64)) {
		FinishCommand();
		return;
	}
	if(owner->haveTarget && owner->moveType->progressState!=AMoveType::Done){
		StopMove();
	} else if(owner->moveType->progressState!=AMoveType::Active){
		owner->moveType->StartMoving(goalPos, 8);
	}
	return;
}
Exemple #18
0
void CCommandAI::GiveAllowedCommand(const Command& c, bool fromSynced)
{
	if (ExecuteStateCommand(c)) {
		return;
	}

	switch (c.GetID()) {
		case CMD_SELFD: {
			if (owner->unitDef->canSelfD) {
				if (!(c.options & SHIFT_KEY) || commandQue.empty()) {
					if (owner->selfDCountdown != 0) {
						owner->selfDCountdown = 0;
					} else {
						owner->selfDCountdown = owner->unitDef->selfDCountdown*2+1;
					}
				}
				else if (commandQue.back().GetID() == CMD_SELFD) {
					commandQue.pop_back();
				} else {
					commandQue.push_back(c);
				}
			}
			return;
		}
		case CMD_SET_WANTED_MAX_SPEED: {
			if (CanSetMaxSpeed() &&
			    (commandQue.empty() ||
			     (commandQue.back().GetID() != CMD_SET_WANTED_MAX_SPEED))) {
				// bail early, do not check for overlaps or queue cancelling
				commandQue.push_back(c);
				if (commandQue.size()==1 && !owner->beingBuilt) {
					SlowUpdate();
				}
			}
			return;
		}
		case CMD_WAIT: {
			GiveWaitCommand(c);
			return;
		}
		case CMD_INSERT: {
			ExecuteInsert(c, fromSynced);
			return;
		}
		case CMD_REMOVE: {
			ExecuteRemove(c);
			return;
		}
	}

	// flush the queue for immediate commands
	if (!(c.options & SHIFT_KEY)) {
		if (!commandQue.empty()) {
			const int& cmd_id = commandQue.front().GetID();
			if ((cmd_id == CMD_MANUALFIRE) ||
			    (cmd_id == CMD_ATTACK)     ||
			    (cmd_id == CMD_AREA_ATTACK)) {
				owner->AttackUnit(0,true);
			}
			waitCommandsAI.ClearUnitQueue(owner, commandQue);
			ClearCommandDependencies();
			commandQue.clear();
		}
		inCommand=false;
		SetOrderTarget(NULL);
	}

	AddCommandDependency(c);

	if (c.GetID() == CMD_PATROL) {
		CCommandQueue::iterator ci = commandQue.begin();
		for (; ci != commandQue.end() && ci->GetID() != CMD_PATROL; ++ci) {
			// just increment
		}
		if (ci == commandQue.end()) {
			if (commandQue.empty()) {
				Command c2(CMD_PATROL, c.options);
				c2.params.push_back(owner->pos.x);
				c2.params.push_back(owner->pos.y);
				c2.params.push_back(owner->pos.z);
				commandQue.push_back(c2);
			}
			else {
				do {
					--ci;
					if (ci->params.size() >= 3) {
						Command c2(CMD_PATROL, c.options);
						c2.params = ci->params;
						commandQue.push_back(c2);
						break;
					} else if (ci == commandQue.begin()) {
						Command c2(CMD_PATROL, c.options);
						c2.params.push_back(owner->pos.x);
						c2.params.push_back(owner->pos.y);
						c2.params.push_back(owner->pos.z);
						commandQue.push_back(c2);
						break;
					}
				}
				while (ci != commandQue.begin());
			}
		}
	}

	// cancel duplicated commands
	bool first;
	if (CancelCommands(c, commandQue, first) > 0) {
		if (first) {
			Command stopCommand(CMD_STOP);
			commandQue.push_front(stopCommand);
			SlowUpdate();
		}
		return;
	}

	// do not allow overlapping commands
	if (!GetOverlapQueued(c).empty()) {
		return;
	}

	if (c.GetID() == CMD_ATTACK) {
		// avoid weaponless units moving to 0 distance when given attack order
		if (owner->weapons.empty() && (owner->unitDef->canKamikaze == false)) {
			Command c2(CMD_STOP);
			commandQue.push_back(c2);
			return;
		}
	}

	commandQue.push_back(c);

	if (commandQue.size() == 1 && !owner->beingBuilt && !owner->stunned) {
		SlowUpdate();
	}
}
void CTransportCAI::ExecuteLoadUnits(Command &c)
{
	CTransportUnit* transport=(CTransportUnit*)owner;
	if(c.params.size()==1){		//load single unit
		if(transport->transportCapacityUsed >= owner->unitDef->transportCapacity){
			FinishCommand();
			return;
		}
		CUnit* unit=uh->units[(int)c.params[0]];
		if (!unit) {
			FinishCommand();
			return;
		}
		if(c.options & INTERNAL_ORDER) {
			if(unit->commandAI->commandQue.empty()){
				if(!LoadStillValid(unit)){
					FinishCommand();
					return;
				}
			} else {
				Command & currentUnitCommand = unit->commandAI->commandQue[0];
				if(currentUnitCommand.id == CMD_LOAD_ONTO && currentUnitCommand.params.size() == 1 && int(currentUnitCommand.params[0]) == owner->id){
					if((unit->moveType->progressState == CMoveType::Failed) && (owner->moveType->progressState == CMoveType::Failed)){
						unit->commandAI->FinishCommand();
						FinishCommand();
						return;
					}
				} else if(!LoadStillValid(unit)) {
					FinishCommand();
					return;
				}
			}
		}
		if(inCommand){
			if(!owner->cob->busy)
				FinishCommand();
			return;
		}
		if(unit && CanTransport(unit) && UpdateTargetLostTimer(int(c.params[0]))){
			toBeTransportedUnitId=unit->id;
			unit->toBeTransported=true;
			if(unit->mass+transport->transportMassUsed > owner->unitDef->transportMass){
				FinishCommand();
				return;
			}
			if(goalPos.distance2D(unit->pos)>10){
				float3 fix = unit->pos;
				SetGoal(fix,owner->pos,64);
			}
			if(unit->pos.distance2D(owner->pos)<owner->unitDef->loadingRadius*0.9f){
				if(CTAAirMoveType* am=dynamic_cast<CTAAirMoveType*>(owner->moveType)){		//handle air transports differently
					float3 wantedPos=unit->pos+UpVector*unit->model->height;
					SetGoal(wantedPos,owner->pos);
					am->dontCheckCol=true;
					am->ForceHeading(unit->heading);
					am->SetWantedAltitude(unit->model->height);
					am->maxDrift=1;
					//logOutput.Print("cai dist %f %f %f",owner->pos.distance(wantedPos),owner->pos.distance2D(wantedPos),owner->pos.y-wantedPos.y);
					if(owner->pos.distance(wantedPos)<4 && abs(owner->heading-unit->heading)<50 && owner->updir.dot(UpVector)>0.995f){
						am->dontCheckCol=false;
						am->dontLand=true;
						std::vector<int> args;
						args.push_back((int)(unit->model->height*65536));
						owner->cob->Call("BeginTransport",args);
						std::vector<int> args2;
						args2.push_back(0);
						args2.push_back((int)(unit->model->height*65536));
						owner->cob->Call("QueryTransport",args2);
						((CTransportUnit*)owner)->AttachUnit(unit,args2[0]);
						am->SetWantedAltitude(0);
						FinishCommand();
						return;
					}
				} else {
					inCommand=true;
					scriptReady=false;
					StopMove();
					std::vector<int> args;
					args.push_back(unit->id);
					owner->cob->Call("TransportPickup",args,ScriptCallback,this,0);
				}
			}
		} else {
			FinishCommand();
		}
	} else if(c.params.size()==4){		//load in radius
		if(lastCall==gs->frameNum)	//avoid infinite loops
			return;
		lastCall=gs->frameNum;
		float3 pos(c.params[0],c.params[1],c.params[2]);
		float radius=c.params[3];
		CUnit* unit=FindUnitToTransport(pos,radius);
		if(unit && ((CTransportUnit*)owner)->transportCapacityUsed < owner->unitDef->transportCapacity){
			Command c2;
			c2.id=CMD_LOAD_UNITS;
			c2.params.push_back(unit->id);
			c2.options=c.options | INTERNAL_ORDER;
			commandQue.push_front(c2);
			inCommand=false;
			SlowUpdate();
			return;
		} else {
			FinishCommand();
			return;
		}
	}
	return;
}
Exemple #20
0
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 != 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)) {
		FinishCommand();
		return;
	}

	if (owner->haveTarget && owner->moveType->progressState != AMoveType::Done) {
		StopMove();
	} else if (owner->moveType->progressState != AMoveType::Active) {
		owner->moveType->StartMoving(goalPos, 8);
	}
}
void CWeapon::SlowUpdate()
{
	SlowUpdate(false);
}
Exemple #22
0
void CBuilderCAI::ExecuteGuard(Command& c)
{
	if (!owner->unitDef->canGuard)
		return;

	CUnit* guardee = unitHandler->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)) {
				ownerBuilder->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 {
			ownerBuilder->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, 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, 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, guardee->id);

			commandQue.push_front(nc);
			inCommand = false;
			return;
		} else {
			NonMoving();
		}
	} else {
		StopSlowGuard();
	}
}
void CCommandAI::GiveCommand(Command& c)
{
	switch(c.id)
	{
	case CMD_FIRE_STATE:
		{
			if(c.params.empty())
				return;
			owner->fireState=(int)c.params[0];
			for(vector<CommandDescription>::iterator cdi=possibleCommands.begin();cdi!=possibleCommands.end();++cdi){
				if(cdi->id==CMD_FIRE_STATE){
					char t[10];
					SNPRINTF(t, 10, "%d", (int)c.params[0]);
					cdi->params[0]=t;
					break;
				}
			}
			return;
		}
	case CMD_MOVE_STATE:
		{
			if(c.params.empty())
				return;
			owner->moveState=(int)c.params[0];
			for(vector<CommandDescription>::iterator cdi=possibleCommands.begin();cdi!=possibleCommands.end();++cdi){
				if(cdi->id==CMD_MOVE_STATE){
					char t[10];
					SNPRINTF(t, 10, "%d", (int)c.params[0]);
					cdi->params[0]=t;
					break;
				}
			}
			return;
		}
	case CMD_REPEAT:
		{
			if(c.params.empty())
				return;
			repeatOrders=!!c.params[0];
			for(vector<CommandDescription>::iterator cdi=possibleCommands.begin();cdi!=possibleCommands.end();++cdi){
				if(cdi->id==CMD_REPEAT){
					char t[10];
					SNPRINTF(t, 10, "%d", (int)c.params[0]);
					cdi->params[0]=t;
					break;
				}
			}
			return;
		}
	case CMD_TRAJECTORY:
		{
			if(c.params.empty() || owner->unitDef->highTrajectoryType<2)
				return;
			owner->useHighTrajectory=!!c.params[0];
			for(vector<CommandDescription>::iterator cdi=possibleCommands.begin();cdi!=possibleCommands.end();++cdi){
				if(cdi->id==CMD_TRAJECTORY){
					char t[10];
					SNPRINTF(t, 10, "%d", (int)c.params[0]);
					cdi->params[0]=t;
					break;
				}
			}
			return;
		}
	case CMD_ONOFF:{
		if(c.params.empty() || !owner->unitDef->onoffable || owner->beingBuilt)
			return;
		if(c.params[0]==1){
			owner->Activate();
		} else if(c.params[0]==0) {
			owner->Deactivate();
		}
		for(vector<CommandDescription>::iterator cdi=possibleCommands.begin();cdi!=possibleCommands.end();++cdi){
			if(cdi->id==CMD_ONOFF){
				char t[10];
				SNPRINTF(t, 10, "%d", (int)c.params[0]);
				cdi->params[0]=t;
				break;
			}
		}
		return; }
	case CMD_CLOAK:{
		if(c.params.empty() || !owner->unitDef->canCloak)
			return;
		if(c.params[0]==1){
			owner->wantCloak=true;
		} else if(c.params[0]==0) {
			owner->wantCloak=false;
			owner->curCloakTimeout=gs->frameNum+owner->cloakTimeout;
		}
		for(vector<CommandDescription>::iterator cdi=possibleCommands.begin();cdi!=possibleCommands.end();++cdi){
			if(cdi->id==CMD_CLOAK){
				char t[10];
				SNPRINTF(t, 10, "%d", (int)c.params[0]);
				cdi->params[0]=t;
				break;
			}
		}
		return; }
	case CMD_STOCKPILE:{
		if(!stockpileWeapon)
			return;
		int change=1;
		if(c.options & RIGHT_MOUSE_KEY)
			change*=-1;
		if(c.options & CONTROL_KEY)
			change*=20;
		if(c.options & SHIFT_KEY)
			change*=5;
		stockpileWeapon->numStockpileQued+=change;
		if(stockpileWeapon->numStockpileQued<0)
			stockpileWeapon->numStockpileQued=0;
		UpdateStockpileIcon();
		return; }
	case CMD_SELFD:{
		if(owner->selfDCountdown){
			owner->selfDCountdown=0;
		} else {
			owner->selfDCountdown = owner->unitDef->selfDCountdown*2+1;
		}
		return;}
	}
    
	if(!(c.options & SHIFT_KEY)){
		if(!commandQue.empty()){
			if(commandQue.front().id==CMD_ATTACK || commandQue.front().id==CMD_AREA_ATTACK
				|| commandQue.front().id==CMD_DGUN)
			{
				owner->AttackUnit(0,true);
			}

			if((c.id==CMD_STOP || c.id==CMD_WAIT) && commandQue.front().id==CMD_WAIT)
 				commandQue.pop_front();
 			else
				commandQue.clear();
 		}
		inCommand=false;
		if(orderTarget){
			DeleteDeathDependence(orderTarget);
			orderTarget=0;
		}
	}
	if(c.id == CMD_PATROL){
		std::deque<Command>::iterator ci = commandQue.begin();
		for(; ci != commandQue.end() && ci->id!=CMD_PATROL; ci++);
		if(ci==commandQue.end()){
			if(commandQue.empty()){
				Command c2;
				c2.id = CMD_PATROL;
				c2.params.push_back(owner->pos.x);
				c2.params.push_back(owner->pos.y);
				c2.params.push_back(owner->pos.z);
				c2.options = c.options;
				commandQue.push_back(c2);
			} else {
				do{
					ci--;
					if(ci->params.size() >=3){
						Command c2;
						c2.id = CMD_PATROL;
						c2.params = ci->params;
						c2.options = c.options;
						commandQue.push_back(c2);
						ci=commandQue.begin();
					} else if(ci==commandQue.begin()){
						Command c2;
						c2.id = CMD_PATROL;
						c2.params.push_back(owner->pos.x);
						c2.params.push_back(owner->pos.y);
						c2.params.push_back(owner->pos.z);
						c2.options = c.options;
						commandQue.push_back(c2);
					}
				}while(ci!=commandQue.begin());
			}
		}
	}
	std::deque<Command>::iterator ci = CCommandAI::GetCancelQueued(c);
	if(c.id<0 && ci != commandQue.end()){
		do{
			if(ci == commandQue.begin()){
				commandQue.erase(ci);
				Command c2;
				c2.id = CMD_STOP;
				commandQue.push_front(c2);
				SlowUpdate();
			} else {
				commandQue.erase(ci);
			}
			ci = CCommandAI::GetCancelQueued(c);
		}while(ci != commandQue.end());
		return;
	} else if(ci != commandQue.end()){
		if(ci == commandQue.begin()){
			commandQue.erase(ci);
			Command c2;
			c2.id = CMD_STOP;
			commandQue.push_front(c2);
			SlowUpdate();
		} else {
			commandQue.erase(ci);
		}
		ci = CCommandAI::GetCancelQueued(c);
		return;
	}
	if(!this->GetOverlapQueued(c).empty()){
		return;
	}
	if(c.id==CMD_ATTACK && owner->weapons.empty() && owner->unitDef->canKamikaze==false){		//avoid weaponless units moving to 0 distance when given attack order
		Command c2;
		c2.id=CMD_STOP;
		commandQue.push_back(c2);
		return;
	}

	commandQue.push_back(c);
	if(commandQue.size()==1 && !owner->beingBuilt)
		SlowUpdate();
}
Exemple #24
0
void CFactoryCAI::GiveCommandReal(const Command& c, bool fromSynced)
{
	// move is always allowed for factories (passed to units it produces)
	if ((c.id == CMD_SET_WANTED_MAX_SPEED) ||
	    ((c.id != CMD_MOVE) && !AllowedCommand(c))) {
		return;
	}

	map<int, BuildOption>::iterator boi = buildOptions.find(c.id);

	// not a build order so queue it to built units
	if (boi == buildOptions.end()) {
		if ((nonQueingCommands.find(c.id) != nonQueingCommands.end()) ||
		    (c.id == CMD_INSERT) || (c.id == CMD_REMOVE) ||
		    (!(c.options & SHIFT_KEY) && ((c.id == CMD_WAIT) || (c.id == CMD_SELFD)))) {
			CCommandAI::GiveAllowedCommand(c);
			return;
		}

		if (!(c.options & SHIFT_KEY)) {
 			waitCommandsAI.ClearUnitQueue(owner, newUnitCommands);
			newUnitCommands.clear();
		}

		if (c.id != CMD_STOP) {
			if ((c.id == CMD_WAIT) || (c.id == CMD_SELFD)) {
				if (!newUnitCommands.empty() && (newUnitCommands.back().id == c.id)) {
					if (c.id == CMD_WAIT) {
						waitCommandsAI.RemoveWaitCommand(owner, c);
					}
					newUnitCommands.pop_back();
				} else {
					newUnitCommands.push_back(c);
				}
			}
			else {
				bool dummy;
				if (CancelCommands(c, newUnitCommands, dummy) > 0) {
					return;
				} else {
					if (GetOverlapQueued(c, newUnitCommands).empty()) {
						newUnitCommands.push_back(c);
					} else {
						return;
					}
				}
			}
		}

		// the first new-unit build order can not be WAIT or SELFD
		while (!newUnitCommands.empty()) {
			const int id = newUnitCommands.front().id;
			if ((id == CMD_WAIT) || (id == CMD_SELFD)) {
				if (c.id == CMD_WAIT) {
					waitCommandsAI.RemoveWaitCommand(owner, c);
				}
				newUnitCommands.pop_front();
			} else {
				break;
			}
		}

		return;
	}

	BuildOption &bo = boi->second;

	int numItems = 1;
	if (c.options & SHIFT_KEY)   { numItems *= 5; }
	if (c.options & CONTROL_KEY) { numItems *= 20; }

	if (c.options & RIGHT_MOUSE_KEY) {
		bo.numQued -= numItems;
		if (bo.numQued < 0) {
			bo.numQued = 0;
		}

		int numToErase = numItems;
		if (c.options & ALT_KEY) {
			for (unsigned int cmdNum = 0; cmdNum < commandQue.size() && numToErase; ++cmdNum) {
				if (commandQue[cmdNum].id == c.id) {
					commandQue[cmdNum].id = CMD_STOP;
					numToErase--;
				}
			}
		}
		else {
			for (int cmdNum = commandQue.size() - 1; cmdNum != -1 && numToErase; --cmdNum) {
				if (commandQue[cmdNum].id == c.id) {
					commandQue[cmdNum].id = CMD_STOP;
					numToErase--;
				}
			}
		}
		UpdateIconName(c.id,bo);
		SlowUpdate();
	}
	else {
		if (c.options & ALT_KEY) {
			for (int a = 0; a < numItems; ++a) {
				if (repeatOrders) {
					Command nc(c);
					nc.options |= DONT_REPEAT;
					if (commandQue.empty()) {
						commandQue.push_front(nc);
					} else {
						commandQue.insert(commandQue.begin()+1, nc);
					}
				} else {
					commandQue.push_front(c);
				}
			}
			if (!repeatOrders) {
				building=false;
				CFactory* fac = (CFactory*)owner;
				fac->StopBuild();
			}
		} else {
			for (int a = 0; a < numItems; ++a) {
				commandQue.push_back(c);
			}
		}
		bo.numQued += numItems;
		UpdateIconName(c.id, bo);

		SlowUpdate();
	}
}
	void CGridManager::Update(int GameTime) {
		GameFrame=GameTime;
		if(GameFrame%UpdateModifierInterval==0){
			SlowUpdate();
		}
	}