Пример #1
0
bool CBuilderCAI::FindRepairTargetAndRepair(float3 pos, float radius, unsigned char options, bool attackEnemy)
{
	std::vector<CUnit*> cu=qf->GetUnits(pos,radius);
	int myAllyteam = owner->allyteam;
	for (std::vector<CUnit*>::iterator ui = cu.begin(); ui != cu.end(); ++ui) {
		CUnit* unit = *ui;
		if (gs->Ally(owner->allyteam, unit->allyteam) &&
		    (unit->health < unit->maxHealth) &&
		    ((unit != owner) || owner->unitDef->canSelfRepair)) {
			// dont lock-on to units outside of our reach (for immobile builders)
			if (!owner->unitDef->canmove) {
				const CBuilder* builder = (CBuilder*)owner;
				const float distSqr = (owner->pos - unit->pos).SqLength2D();
				const float buildDist = builder->buildDistance + unit->radius - 9.0f;
				if (distSqr > (buildDist * buildDist)) {
					continue;
				}
			}
			// dont help factories produce units unless set on roam
			if (unit->mobility && unit->beingBuilt && owner->moveState<2) {
				continue;
			}
			if (!( unit->beingBuilt && owner->unitDef->canAssist) &&
			    !(!unit->beingBuilt && owner->unitDef->canRepair)) {
				continue;
			}
			if (unit->soloBuilder && (unit->soloBuilder != owner)) {
				continue;
			}
			Command nc;
			if(attackEnemy){
				PushOrUpdateReturnFight();
			}
			nc.id=CMD_REPAIR;
			nc.options=options | INTERNAL_ORDER;
			nc.params.push_back(unit->id);
			commandQue.push_front(nc);
			return true;
		} else if(attackEnemy && owner->unitDef->canAttack && owner->maxRange>0 &&
		          !gs->Ally(owner->allyteam,unit->allyteam)){
			Command nc;
			PushOrUpdateReturnFight();
			nc.id=CMD_ATTACK;
			nc.options=options | INTERNAL_ORDER;
			nc.params.push_back(unit->id);
			commandQue.push_front(nc);
			return true;
		}
	}
	return false;
}
Пример #2
0
bool CBuilderCAI::FindReclaimableFeatureAndReclaim(float3 pos, float radius,unsigned char options, bool recAny)
{
	CFeature* best=0;
	float bestDist=10000000;
	std::vector<CFeature*> features=qf->GetFeaturesExact(pos,radius);
	for(std::vector<CFeature*>::iterator fi=features.begin();fi!=features.end();++fi){
		if((*fi)->def->destructable && (*fi)->allyteam!=owner->allyteam){
			float dist=(*fi)->pos.distance2D(owner->pos);
			if(dist<bestDist && (recAny
			  ||((*fi)->def->energy > 0 && gs->Team(owner->team)->energy < gs->Team(owner->team)->energyStorage)
			  ||((*fi)->def->metal > 0 && gs->Team(owner->team)->metal < gs->Team(owner->team)->metalStorage))){
				bestDist=dist;
				best=*fi;
			}
		}
	}
	if(best){
		Command c2;
		if(!recAny){
			PushOrUpdateReturnFight();
		}
		c2.options=options | INTERNAL_ORDER;
		c2.id=CMD_RECLAIM;
		c2.params.push_back(MAX_UNITS+best->id);
		commandQue.push_front(c2);
		return true;
	}
	return false;
}
Пример #3
0
bool CBuilderCAI::FindReclaimableFeatureAndReclaim(const float3& pos,
                                                   float radius,
                                                   unsigned char options,
                                                   bool noResCheck,
                                                   bool recAnyTeam)
{
	const std::vector<CFeature*> features = qf->GetFeaturesExact(pos, radius);
	std::vector<CFeature*>::const_iterator fi;

	const CFeature* best = NULL;
	float bestDist = 1.0e30f;
	const CTeam* team = teamHandler->Team(owner->team);

	for (fi = features.begin(); fi != features.end(); ++fi) {
		const CFeature* f = *fi;
		if (f->def->reclaimable &&
		    (recAnyTeam || (f->allyteam != owner->allyteam))) {
			const float dist = f3SqLen(f->pos - owner->pos);
			if ((dist < bestDist) &&
			    (noResCheck ||
			     ((f->def->metal  > 0.0f) && (team->metal  < team->metalStorage)) ||
			     ((f->def->energy > 0.0f) && (team->energy < team->energyStorage)))) {
				if (!owner->unitDef->canmove && !ObjInBuildRange(f)) {
					continue;
				}
				bestDist = dist;
				best = f;
			}
		}
	}

	if (best) {
		Command cmd;
		if (!noResCheck) {
			// FIGHT commands always resource check
			PushOrUpdateReturnFight();
		}
		cmd.options = options | INTERNAL_ORDER;
		cmd.id = CMD_RECLAIM;
		cmd.params.push_back(MAX_UNITS + best->id);
		commandQue.push_front(cmd);
		return true;
	}

	return false;
}
Пример #4
0
bool CBuilderCAI::FindReclaimTargetAndReclaim(const float3& pos, float radius, unsigned char cmdopt, ReclaimOption recoptions)
{
	const int rid = FindReclaimTarget(pos, radius, cmdopt, recoptions);

	if (rid >= 0) {
		if (!(recoptions & REC_NORESCHECK)) {
			// FIGHT commands always resource check
			PushOrUpdateReturnFight();
		}

		Command cmd(CMD_RECLAIM, cmdopt | INTERNAL_ORDER, rid, pos);
			cmd.PushParam(radius);
		commandQue.push_front(cmd);
		return true;
	}

	return false;
}
Пример #5
0
/**
* @brief Executes the Fight command c
*/
void CMobileCAI::ExecuteFight(Command &c){
	assert((c.options & INTERNAL_ORDER) || owner->unitDef->canFight);
	if(tempOrder){
		inCommand = true;
		tempOrder = false;
	}
	if(c.params.size()<3){		//this shouldnt happen but anyway ...
		logOutput.Print("Error: got fight cmd with less than 3 params on %s in mobilecai",
			owner->unitDef->humanName.c_str());
		return;
	}
	if(c.params.size() >= 6){
		if(!inCommand){
			commandPos1 = float3(c.params[3],c.params[4],c.params[5]);
		}
	} else {
		// Some hackery to make sure the line (commandPos1,commandPos2) is NOT
		// rotated (only shortened) if we reach this because the previous return
		// fight command finished by the 'if((curPos-pos).SqLength2D()<(64*64)){'
		// condition, but is actually updated correctly if you click somewhere
		// outside the area close to the line (for a new command).
		commandPos1 = ClosestPointOnLine(commandPos1, commandPos2, owner->pos);
		if ((owner->pos - commandPos1).SqLength2D() > (96 * 96)) {
			commandPos1 = owner->pos;
		}
	}
	float3 pos(c.params[0],c.params[1],c.params[2]);
	if(!inCommand){
		inCommand = true;
		commandPos2 = pos;
	}
	if(c.params.size() >= 6){
		pos = ClosestPointOnLine(commandPos1, commandPos2, pos);
	}
	if(pos!=goalPos){
		SetGoal(pos, owner->pos);
	}

	if(owner->unitDef->canAttack && owner->fireState==2){
		float3 curPosOnLine = ClosestPointOnLine(commandPos1, commandPos2, owner->pos);
		CUnit* enemy=helper->GetClosestEnemyUnit(
			curPosOnLine, owner->maxRange + 100 * owner->moveState * owner->moveState,
			owner->allyteam);
		if(enemy && (owner->hasUWWeapons || !enemy->isUnderWater)
				&& !(owner->unitDef->noChaseCategory & enemy->category)
				&& !owner->weapons.empty()){
			Command c2;
			c2.id=CMD_ATTACK;
			c2.options=c.options|INTERNAL_ORDER;
			c2.params.push_back(enemy->id);
			PushOrUpdateReturnFight();
			commandQue.push_front(c2);
			inCommand=false;
			tempOrder=true;
			if(lastPC!=gs->frameNum){	//avoid infinite loops
				lastPC=gs->frameNum;
				SlowUpdate();
			}
			return;
		}
	}
	if((owner->pos - goalPos).SqLength2D() < (64 * 64)
			|| (owner->moveType->progressState == CMoveType::Failed)){
		FinishCommand();
	}
	return;
}
Пример #6
0
bool CBuilderCAI::FindRepairTargetAndRepair(const float3& pos, float radius,
                                            unsigned char options,
                                            bool attackEnemy,
											bool builtOnly)
{
	const std::vector<CUnit*> &cu = qf->GetUnitsExact(pos, radius);

	const CUnit* best = NULL;
	float bestDist = 1.0e30f;
	bool haveEnemy = false;
	bool trySelfRepair = false;
	bool stationary = false;
	const float& maxSpeed = owner->moveType->GetMaxSpeed();
	float uspeed = 0.0f;

	for (std::vector<CUnit*>::const_iterator ui = cu.begin(); ui != cu.end(); ++ui) {
		CUnit* unit = *ui;
		if (teamHandler->Ally(owner->allyteam, unit->allyteam)) {
			if (!haveEnemy && (unit->health < unit->maxHealth)) {
				// don't help allies build unless set on roam
				if (unit->beingBuilt && owner->team != unit->team && (owner->moveState != MOVESTATE_ROAM)) {
					continue;
				}
				// don't help factories produce units when set on hold pos                
				if (unit->beingBuilt && unit->moveDef && (owner->moveState == MOVESTATE_HOLDPOS)) {
					continue;
				}
				// don't repair stuff that can't be repaired
				if (!unit->beingBuilt && !unit->unitDef->repairable) {
					continue;
				}
				// don't assist or repair if can't assist or repair
				if (!( unit->beingBuilt && owner->unitDef->canAssist) &&
						!(!unit->beingBuilt && owner->unitDef->canRepair)) {
					continue;
				}
				if (unit->soloBuilder && (unit->soloBuilder != owner)) {
					continue;
				}
				if (unit == owner) {
					trySelfRepair = true;
					continue;
				}
				if(unit->isMoving && stationary) { // repair stationary targets first
					continue;
				}
				if(builtOnly && unit->beingBuilt) {
					continue;
				}

				float dist = f3SqDist(unit->pos, owner->pos);
				if (unit->isMoving) {
					uspeed = unit->speed.Length2D();
					dist *= 1.0f + std::max(uspeed - maxSpeed, 0.0f); // avoid targets that are faster than our max speed
				}
				if (dist < bestDist || (!stationary && !unit->isMoving)) {
					// dont lock-on to units outside of our reach (for immobile builders)
					if ((owner->immobile || (unit->isMoving && !TargetInterceptable(unit, uspeed))) && !IsInBuildRange(unit)) {
						continue;
					}
					// don't repair stuff that's being reclaimed
					if (!(options & CONTROL_KEY) && IsUnitBeingReclaimed(unit, owner)) {
						continue;
					}
					if(!stationary && !unit->isMoving) {
						stationary = true;
					}
					bestDist = dist;
					best = unit;
				}
			}
		}
		else {
			if (!attackEnemy || !owner->unitDef->canAttack || (owner->maxRange <= 0) )
				continue;

			if (!(unit->losStatus[owner->allyteam] & (LOS_INRADAR | LOS_INLOS)))
				continue;

			const float dist = f3SqDist(unit->pos, owner->pos);
			if ((dist < bestDist) || !haveEnemy) {
				if (owner->immobile &&
					((dist - unit->radius) > owner->maxRange)) {
					continue;
				}
				best = unit;
				bestDist = dist;
				haveEnemy = true;
			}
		}
	}

	if (best == NULL) {
		if (trySelfRepair &&
		    owner->unitDef->canSelfRepair &&
		    (owner->health < owner->maxHealth)) {
			best = owner;
		} else {
			return false;
		}
	}

	if (!haveEnemy) {
		if (attackEnemy) {
			PushOrUpdateReturnFight();
		}
		Command cmd(CMD_REPAIR, options | INTERNAL_ORDER);
			cmd.params.push_back(best->id);
			cmd.params.push_back(pos.x);
			cmd.params.push_back(pos.y);
			cmd.params.push_back(pos.z);
			cmd.params.push_back(radius);
		commandQue.push_front(cmd);
	}
	else {
		PushOrUpdateReturnFight(); // attackEnemy must be true
		Command cmd(CMD_ATTACK, options | INTERNAL_ORDER);
		cmd.params.push_back(best->id);
		commandQue.push_front(cmd);
	}

	return true;
}
Пример #7
0
bool CBuilderCAI::FindRepairTargetAndRepair(const float3& pos, float radius,
                                            unsigned char options,
                                            bool attackEnemy)
{
	const std::vector<CUnit*> cu = qf->GetUnits(pos, radius);
	std::vector<CUnit*>::const_iterator ui;

	const CUnit* best = NULL;
	float bestDist = 1.0e30f;
	bool haveEnemy = false;
	bool trySelfRepair = false;

	for (ui = cu.begin(); ui != cu.end(); ++ui) {
		CUnit* unit = *ui;
		if (teamHandler->Ally(owner->allyteam, unit->allyteam)) {
			if (!haveEnemy && (unit->health < unit->maxHealth)) {
				// dont lock-on to units outside of our reach (for immobile builders)
				if (!owner->unitDef->canmove && !ObjInBuildRange(unit)) {
					continue;
				}
				// dont help factories produce units unless set on roam
				if (unit->beingBuilt && unit->mobility && (owner->moveState < 2)) {
					continue;
				}
				if (!( unit->beingBuilt && owner->unitDef->canAssist) &&
						!(!unit->beingBuilt && owner->unitDef->canRepair)) {
					continue;
				}
				if (unit->soloBuilder && (unit->soloBuilder != owner)) {
					continue;
				}
				// don't repair stuff that's being reclaimed
				if (IsUnitBeingReclaimedByFriend(unit)) {
					continue;
				}
				if (unit == owner) {
					trySelfRepair = true;
					continue;
				}
				const float dist = f3SqLen(unit->pos - owner->pos);
				if (dist < bestDist) {
					bestDist = dist;
					best = unit;
				}
			}
		}
		else {
			if (attackEnemy && owner->unitDef->canAttack && (owner->maxRange > 0)) {
				const float dist = f3SqLen(unit->pos - owner->pos);
				if ((dist < bestDist) || !haveEnemy) {
					if (!owner->unitDef->canmove &&
					    ((dist - unit->radius) > owner->maxRange)) {
						continue;
					}
					best = unit;
					bestDist = dist;
					haveEnemy = true;
				}
			}
		}
	}

	if (best == NULL) {
		if (trySelfRepair &&
		    owner->unitDef->canSelfRepair &&
		    (owner->health < owner->maxHealth)) {
			best = owner;
		} else {
			return false;
		}
	}

	if (!haveEnemy) {
		Command cmd;
		if (attackEnemy) {
			PushOrUpdateReturnFight();
		}
		cmd.id = CMD_REPAIR;
		cmd.options = options | INTERNAL_ORDER;
		cmd.params.push_back(best->id);
		commandQue.push_front(cmd);
	}
	else {
		Command cmd;
		PushOrUpdateReturnFight(); // attackEnemy must be true
		cmd.id = CMD_ATTACK;
		cmd.options = options | INTERNAL_ORDER;
		cmd.params.push_back(best->id);
		commandQue.push_front(cmd);
	}

	return true;
}
Пример #8
0
void CMobileCAI::SlowUpdate()
{
	if(owner->unitDef->maxFuel>0 && dynamic_cast<CTAAirMoveType*>(owner->moveType)){
		CTAAirMoveType* myPlane=(CTAAirMoveType*)owner->moveType;
		if(myPlane->reservedPad){
			return;
		} else {
			if(owner->currentFuel <= 0){
				StopMove();
				owner->userAttackGround=false;
				owner->userTarget=0;
				inCommand=false;
				CAirBaseHandler::LandingPad* lp=airBaseHandler->FindAirBase(owner,8000,owner->unitDef->minAirBasePower);
				if(lp){
					myPlane->AddDeathDependence(lp);
					myPlane->reservedPad=lp;
					myPlane->padStatus=0;
					myPlane->oldGoalPos=myPlane->goalPos;
					return;
				}
				float3 landingPos = airBaseHandler->FindClosestAirBasePos(owner,8000,owner->unitDef->minAirBasePower);
				if(landingPos != ZeroVector && owner->pos.distance2D(landingPos) > 300){
					inCommand=false;
					StopMove();
					SetGoal(landingPos,owner->pos);
				} else {
					if(myPlane->aircraftState == CTAAirMoveType::AIRCRAFT_FLYING)
						myPlane->SetState(CTAAirMoveType::AIRCRAFT_LANDING);	
				}
				return;
			}
			if(owner->currentFuel < myPlane->repairBelowHealth*owner->unitDef->maxFuel){
				if(commandQue.empty() || commandQue.front().id==CMD_PATROL){
					CAirBaseHandler::LandingPad* lp=airBaseHandler->FindAirBase(owner,8000,owner->unitDef->minAirBasePower);
					if(lp){
						StopMove();
						owner->userAttackGround=false;
						owner->userTarget=0;
						inCommand=false;
						myPlane->AddDeathDependence(lp);
						myPlane->reservedPad=lp;
						myPlane->padStatus=0;
						myPlane->oldGoalPos=myPlane->goalPos;
						if(myPlane->aircraftState==CTAAirMoveType::AIRCRAFT_LANDED){
							myPlane->SetState(CTAAirMoveType::AIRCRAFT_TAKEOFF);
						}
						return;
					}		
				}
			}
		}
	}

	if(!commandQue.empty() && commandQue.front().timeOut<gs->frameNum){
		StopMove();
		FinishCommand();
		return;
	}

	if(commandQue.empty()) {
//		if(!owner->ai || owner->ai->State() != CHasState::Active) {
			IdleCheck();
//		}
		if(commandQue.empty() || commandQue.front().id==CMD_ATTACK)	//the attack order could terminate directly and thus cause a loop
			return;
	}

	float3 curPos=owner->pos;
	
	// treat any following CMD_SET_WANTED_MAX_SPEED commands as options
	// to the current command  (and ignore them when it's their turn)
	if (commandQue.size() >= 2) {
		deque<Command>::iterator it = commandQue.begin();
		it++;
		const Command& c = *it;
		if ((c.id == CMD_SET_WANTED_MAX_SPEED) && (c.params.size() >= 1)) {
			const float defMaxSpeed = owner->maxSpeed;
			const float newMaxSpeed = min(c.params[0], defMaxSpeed);
			owner->moveType->SetMaxSpeed(newMaxSpeed);
		}
	}	 

	Command& c=commandQue.front();
	switch(c.id){
	case CMD_WAIT:{
		break;
	}
	case CMD_STOP:{
		StopMove();
		CCommandAI::SlowUpdate();
		break;
	}
	case CMD_SET_WANTED_MAX_SPEED:{
	  if (repeatOrders && (commandQue.size() >= 2) &&
	      (commandQue.back().id != CMD_SET_WANTED_MAX_SPEED)) {
	  	commandQue.push_back(commandQue.front());
		}
		commandQue.pop_front();
		SlowUpdate();
		break;
	}
	case CMD_MOVE:{
		float3 pos(c.params[0],c.params[1],c.params[2]);
		if(!(pos==goalPos)){
			SetGoal(pos,curPos);
		}
		if((curPos-goalPos).SqLength2D()<1024 || owner->moveType->progressState==CMoveType::Failed){
			FinishCommand();
		}
		break;
	}
	case CMD_PATROL:{
		assert(owner->unitDef->canPatrol);
		if(c.params.size()<3){		//this shouldnt happen but anyway ...
			logOutput.Print("Error: got patrol cmd with less than 3 params on %s in mobilecai",
				owner->unitDef->humanName.c_str());
			return;
		}
		Command temp;
		temp.id = CMD_FIGHT;
		temp.params.push_back(c.params[0]);
		temp.params.push_back(c.params[1]);
		temp.params.push_back(c.params[2]);
		temp.options = c.options|INTERNAL_ORDER;
		commandQue.push_back(c);
		commandQue.pop_front();
		commandQue.push_front(temp);
		if(owner->group){
			owner->group->CommandFinished(owner->id,CMD_PATROL);
		}
		SlowUpdate();
		return;
	}
	case CMD_FIGHT:{
		assert((c.options & INTERNAL_ORDER) || owner->unitDef->canFight);
		if(tempOrder){
			inCommand = true;
			tempOrder = false;
		}
		if(c.params.size()<3){		//this shouldnt happen but anyway ...
			logOutput.Print("Error: got fight cmd with less than 3 params on %s in mobilecai",
				owner->unitDef->humanName.c_str());
			return;
		}
		if(c.params.size() >= 6){
			if(!inCommand){
				commandPos1 = float3(c.params[3],c.params[4],c.params[5]);
			}
		} else {
			// Some hackery to make sure the line (commandPos1,commandPos2) is NOT
			// rotated (only shortened) if we reach this because the previous return
			// fight command finished by the 'if((curPos-pos).SqLength2D()<(64*64)){'
			// condition, but is actually updated correctly if you click somewhere
			// outside the area close to the line (for a new command).
			commandPos1 = ClosestPointOnLine(commandPos1, commandPos2, curPos);
			if ((curPos-commandPos1).SqLength2D()>(96*96)) {
				commandPos1 = curPos;
			}
		}
		float3 pos(c.params[0],c.params[1],c.params[2]);
		if(!inCommand){
			inCommand = true;
			commandPos2 = pos;
		}
		if(pos!=goalPos){
			SetGoal(pos,curPos);
		}

		if(owner->unitDef->canAttack && owner->fireState==2){
			float3 curPosOnLine = ClosestPointOnLine(commandPos1, commandPos2, curPos);
			CUnit* enemy=helper->GetClosestEnemyUnit(
				curPosOnLine, owner->maxRange+100*owner->moveState*owner->moveState, owner->allyteam);
			if(enemy && (owner->hasUWWeapons || !enemy->isUnderWater)
						&& !(owner->unitDef->noChaseCategory & enemy->category) && !owner->weapons.empty()){
				Command c2;
				c2.id=CMD_ATTACK;
				c2.options=c.options|INTERNAL_ORDER;
				c2.params.push_back(enemy->id);
				PushOrUpdateReturnFight();
				commandQue.push_front(c2);
				inCommand=false;
				tempOrder=true;
				if(lastPC!=gs->frameNum){	//avoid infinite loops
					lastPC=gs->frameNum;
					SlowUpdate();
				}
				return;
			}
		}
		if((curPos-goalPos).SqLength2D()<(64*64) || owner->moveType->progressState==CMoveType::Failed){
			FinishCommand();
		}
		return;
	}
	case CMD_DGUN:
		if(uh->limitDgun && owner->unitDef->isCommander
		  && owner->pos.distance(gs->Team(owner->team)->startPos)>uh->dgunRadius){
			StopMove();
			FinishCommand();
			return;
		}
		//no break
	case CMD_ATTACK:
		assert(owner->unitDef->canAttack);
		if(tempOrder && owner->moveState<2){		//limit how far away we fly
			if(orderTarget && LinePointDist(commandPos1,commandPos2,orderTarget->pos) > 500+owner->maxRange){
				StopMove();
				FinishCommand();
				break;
			}
		}
		if(!inCommand){
			if(c.params.size()==1){
				if(uh->units[int(c.params[0])]!=0 && uh->units[int(c.params[0])]!=owner){
					float3 fix=uh->units[int(c.params[0])]->pos+owner->posErrorVector*128;
					SetGoal(fix,curPos);
					orderTarget=uh->units[int(c.params[0])];
					AddDeathDependence(orderTarget);
					inCommand=true;
				} else {
					StopMove();		//cancel keeppointingto
					FinishCommand();
					break;
				}
			} else {
				float3 pos(c.params[0],c.params[1],c.params[2]);
				SetGoal(pos,curPos);
				inCommand=true;
			}
		}
		else if ((c.params.size() == 3) && (owner->commandShotCount > 0) && (commandQue.size() > 1)) {
			// the trailing CMD_SET_WANTED_MAX_SPEED in a command pair does not count
			if ((commandQue.size() != 2) || (commandQue.back().id != CMD_SET_WANTED_MAX_SPEED)) {
				StopMove();
				FinishCommand();
				break;
			}
		}

		if(targetDied || (c.params.size() == 1 && UpdateTargetLostTimer(int(c.params[0])) == 0)){
			StopMove();		//cancel keeppointingto
			FinishCommand();
			break;
		}
		if(orderTarget){
			//note that we handle aircrafts slightly differently
			if((((owner->AttackUnit(orderTarget, c.id==CMD_DGUN)
					&& owner->weapons.size() > 0 
					&& owner->weapons.front()->range -
						owner->weapons.front()->relWeaponPos.Length() >
						orderTarget->pos.distance(owner->pos))
					|| dynamic_cast<CTAAirMoveType*>(owner->moveType))
					&& (owner->pos-orderTarget->pos).Length2D() <
						owner->maxRange*0.9f)
					|| (owner->pos-orderTarget->pos).SqLength2D()<1024){
				StopMove();
				owner->moveType->KeepPointingTo(orderTarget,
					min((float)(owner->losRadius*SQUARE_SIZE*2),
					owner->maxRange*0.9f), true);
			} else if((orderTarget->pos+owner->posErrorVector*128).distance2D(goalPos) > 10+orderTarget->pos.distance2D(owner->pos)*0.2f){
				float3 fix=orderTarget->pos+owner->posErrorVector*128;
				SetGoal(fix,curPos);
			}
		} else {
			float3 pos(c.params[0],c.params[1],c.params[2]);
			if((owner->AttackGround(pos,c.id==CMD_DGUN) && owner->weapons.size() > 0
					&& (owner->pos-pos).Length() < 
						owner->weapons.front()->range -
						owner->weapons.front()->relWeaponPos.Length())
					|| (owner->pos-pos).SqLength2D()<1024){
				StopMove();
				owner->moveType->KeepPointingTo(pos, owner->maxRange*0.9f, true);
			} else if(pos.distance2D(goalPos)>10){
				SetGoal(pos,curPos);
			}
		}
		break;
	case CMD_GUARD:
		assert(owner->unitDef->canGuard);
		if(int(c.params[0]) >= 0 && uh->units[int(c.params[0])] != NULL && UpdateTargetLostTimer(int(c.params[0]))){
			CUnit* guarded=uh->units[int(c.params[0])];
			if(owner->unitDef->canAttack && guarded->lastAttacker && guarded->lastAttack+40<gs->frameNum
			  && (owner->hasUWWeapons || !guarded->lastAttacker->isUnderWater)){
				Command nc;
				nc.id=CMD_ATTACK;
				nc.params.push_back(guarded->lastAttacker->id);
				nc.options=c.options;
				commandQue.push_front(nc);
				SlowUpdate();
				return;
			} else {
				float3 dif=guarded->pos-curPos;
				dif.Normalize();
				float3 goal=guarded->pos-dif*(guarded->radius+owner->radius+64);
				if((goal-curPos).SqLength2D()<8000){
					StopMove();
					NonMoving();
				}else{
					if((goalPos-goal).SqLength2D()>3600)
						SetGoal(goal,curPos);
				}
			}
		} else {
			FinishCommand();
		}
		break;
	default:
		CCommandAI::SlowUpdate();
		break;
	}
}
Пример #9
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;
}
Пример #10
0
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;
}
Пример #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 = CGameHelper::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 = CGameHelper::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();
	}
}
Пример #12
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();
    }
}
Пример #13
0
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;
	}
}