Exemple #1
0
/**
* @brief Executes the guard command c
*/
void CMobileCAI::ExecuteGuard(Command &c)
{
	assert(owner->unitDef->canGuard);
	assert(!c.params.empty());

	CUnit* guarded = uh->GetUnit(c.params[0]);

	if (guarded != NULL && UpdateTargetLostTimer(guarded->id)) {
		if (owner->unitDef->canAttack && guarded->lastAttack + 40 < gs->frameNum
				&& IsValidTarget(guarded->lastAttacker))
		{
			StopSlowGuard();
			Command nc;
			nc.id = CMD_ATTACK;
			nc.params.push_back(guarded->lastAttacker->id);
			nc.options = c.options;
			commandQue.push_front(nc);
			SlowUpdate();
			return;
		} else {
			//float3 dif = guarded->speed * guarded->frontdir;
			float3 dif = guarded->pos - owner->pos;
			dif.Normalize();
			float3 goal = guarded->pos - dif * (guarded->radius + owner->radius + 64);
			if((goalPos - goal).SqLength2D() > 1600
					|| (goalPos - owner->pos).SqLength2D()
						< (owner->maxSpeed*30 + 1 + SQUARE_SIZE*2)
						 * (owner->maxSpeed*30 + 1 + SQUARE_SIZE*2)){
				SetGoal(goal, owner->pos);
			}
			if((goal - owner->pos).SqLength2D() < 6400) {
				StartSlowGuard(guarded->maxSpeed);
				if((goal-owner->pos).SqLength2D() < 1800){
					StopMove();
					NonMoving();
				}
			} else {
				StopSlowGuard();
			}
		}
	} else {
		FinishCommand();
	}
	return;
}
void CMobileCAI::IdleCheck(void)
{
	if(owner->unitDef->canAttack && owner->moveState && owner->fireState && !owner->weapons.empty() && (!owner->haveTarget || owner->weapons[0]->onlyForward)){
		if(owner->lastAttacker && owner->lastAttack+100>gs->frameNum && !(owner->unitDef->noChaseCategory & owner->lastAttacker->category)){
			float3 apos=owner->lastAttacker->pos;
			float dist=apos.distance2D(owner->pos);
			if(dist<owner->maxRange+200*owner->moveState*owner->moveState){
				Command c;
				c.id=CMD_ATTACK;
				c.options=INTERNAL_ORDER;
				c.params.push_back(owner->lastAttacker->id);
				c.timeOut=gs->frameNum+140;
				commandQue.push_front(c);
				return;
			}
		}
	}
	if(owner->unitDef->canAttack && (gs->frameNum!=lastIdleCheck+16) && owner->moveState && owner->fireState==2 && !owner->weapons.empty() && (!owner->haveTarget || owner->weapons[0]->onlyForward)){
		if(!owner->unitDef->noAutoFire){
			CUnit* u=helper->GetClosestEnemyUnit(owner->pos,owner->maxRange+150*owner->moveState*owner->moveState,owner->allyteam);
			if(u && !(owner->unitDef->noChaseCategory & u->category)){
				Command c;
				c.id=CMD_ATTACK;
				c.options=INTERNAL_ORDER;
				c.params.push_back(u->id);
				c.timeOut=gs->frameNum+140;
				commandQue.push_front(c);
				return;
			}
		}
	}
	lastIdleCheck=gs->frameNum;
	if((owner->pos-lastUserGoal).SqLength2D()>10000 && !owner->haveTarget && !dynamic_cast<CTAAirMoveType*>(owner->moveType)){
		Command c;
		c.id=CMD_MOVE;
		c.options=0;		//note that this is not internal order so that we dont keep generating new orders if we cant get to that pos
		c.params.push_back(lastUserGoal.x);
		c.params.push_back(lastUserGoal.y);
		c.params.push_back(lastUserGoal.z);
		commandQue.push_front(c);
		unimportantMove=true;
	} else {
		NonMoving();
	}
}
Exemple #3
0
/**
* @brief Executes the guard command c
*/
void CMobileCAI::ExecuteGuard(Command &c)
{
	assert(owner->unitDef->canGuard);
	assert(!c.params.empty());

	const CUnit* guardee = unitHandler->GetUnit(c.params[0]);

	if (guardee == NULL) { FinishCommand(); return; }
	if (UpdateTargetLostTimer(guardee->id) == 0) { FinishCommand(); return; }
	if (guardee->outOfMapTime > (GAME_SPEED * 5)) { FinishCommand(); return; }

	const bool pushAttackCommand =
		owner->unitDef->canAttack &&
		(guardee->lastAttackFrame + 40 < gs->frameNum) &&
		IsValidTarget(guardee->lastAttacker);

	if (pushAttackCommand) {
		Command nc(CMD_ATTACK, c.options, guardee->lastAttacker->id);
		commandQue.push_front(nc);

		StopSlowGuard();
		SlowUpdate();
	} else {
		const float3 dif = (guardee->pos - owner->pos).SafeNormalize();
		const float3 goal = guardee->pos - dif * (guardee->radius + owner->radius + 64.0f);
		const bool resetGoal =
			((goalPos - goal).SqLength2D() > 1600.0f) ||
			(goalPos - owner->pos).SqLength2D() < Square(owner->moveType->GetMaxSpeed() * GAME_SPEED + 1 + SQUARE_SIZE * 2);

		if (resetGoal) {
			SetGoal(goal, owner->pos);
		}

		if ((goal - owner->pos).SqLength2D() < 6400.0f) {
			StartSlowGuard(guardee->moveType->GetMaxSpeed());

			if ((goal - owner->pos).SqLength2D() < 1800.0f) {
				StopMove();
				NonMoving();
			}
		} else {
			StopSlowGuard();
		}
	}
}
void CBuilderCAI::ExecuteGuard(Command &c)
{
	assert(owner->unitDef->canGuard);
	CBuilder* fac=(CBuilder*)owner;
	CUnit* guarded=uh->units[(int)c.params[0]];
	if(guarded && guarded!=owner && UpdateTargetLostTimer((int)c.params[0])){
		if(CBuilder* b=dynamic_cast<CBuilder*>(guarded)){
			if(b->terraforming){
				if(fac->pos.distance2D(b->terraformCenter)<fac->buildDistance*0.8f+b->terraformRadius*0.7f){
					StopMove();
					owner->moveType->KeepPointingTo(b->terraformCenter, fac->buildDistance*0.9f, false);
					fac->HelpTerraform(b);
				} else {
					SetGoal(b->terraformCenter,fac->pos,fac->buildDistance*0.7f+b->terraformRadius*0.6f);
				}
				return;
			}
			if (b->curBuild &&
			    (( b->curBuild->beingBuilt && owner->unitDef->canAssist) ||
			     (!b->curBuild->beingBuilt && owner->unitDef->canRepair))) {
				Command nc;
				nc.id=CMD_REPAIR;
				nc.options=c.options;
				nc.params.push_back(b->curBuild->id);
				commandQue.push_front(nc);
				inCommand=false;
//					SlowUpdate();
				return;
			}
		}
		if(CFactory* f=dynamic_cast<CFactory*>(guarded)){
			if (f->curBuild &&
					(( f->curBuild->beingBuilt && owner->unitDef->canAssist) ||
					 (!f->curBuild->beingBuilt && owner->unitDef->canRepair))) {
				Command nc;
				nc.id=CMD_REPAIR;
				nc.options=c.options;
				nc.params.push_back(f->curBuild->id);
				commandQue.push_front(nc);
				inCommand=false;
//					SlowUpdate();
				return;
			}
		}
		float3 curPos=owner->pos;
		float3 dif=guarded->pos-curPos;
		dif.Normalize();
		float3 goal=guarded->pos-dif*(fac->buildDistance*.5);
		if((guarded->pos-curPos).SqLength2D()<
				(fac->buildDistance*1.9f + guarded->radius)
				*(fac->buildDistance*1.9f + guarded->radius)){
			StartSlowGuard(guarded->maxSpeed);
		} else {
			StopSlowGuard();
		}
		if((guarded->pos-curPos).SqLength2D()<
				(fac->buildDistance*0.9f + guarded->radius)
				*(fac->buildDistance*0.9f + guarded->radius)){
			StopMove();
//				logOutput.Print("should point with type 3?");
			owner->moveType->KeepPointingTo(guarded->pos,
				fac->buildDistance*0.9f+guarded->radius, false);
			if(guarded->health<guarded->maxHealth
					&& ((guarded->beingBuilt && owner->unitDef->canAssist)
					|| (!guarded->beingBuilt && owner->unitDef->canRepair)))
				fac->SetRepairTarget(guarded);
			else
				NonMoving();
		}else{
			if((goalPos-goal).SqLength2D()>4000
					|| (goalPos - owner->pos).SqLength2D() <
						(owner->maxSpeed*30 + 1 + SQUARE_SIZE*2)
						* (owner->maxSpeed*30 + 1 + SQUARE_SIZE*2)){
				SetGoal(goal,curPos);
			}
		}
	} else {
		FinishCommand();
	}
	return;
}
void CBuilderCAI::SlowUpdate()
{
	if(commandQue.empty()){
		CMobileCAI::SlowUpdate();
		return;
	}

	Command& c=commandQue.front();
	CBuilder* fac=(CBuilder*)owner;
	float3 curPos=owner->pos;

	map<int,string>::iterator boi;
	if((boi=buildOptions.find(c.id))!=buildOptions.end()){
		const UnitDef* ud = unitDefHandler->GetUnitByName(boi->second);
		const float radius = GetUnitRadius(ud, c.id);
		if (inCommand) {
			if (building) {
				if (build.pos.distance2D(fac->pos) > fac->buildDistance+radius-8.0f) {
					owner->moveType->StartMoving(build.pos, fac->buildDistance*0.5f+radius);
				} else {
					StopMove();
					owner->moveType->KeepPointingTo(build.pos, (fac->buildDistance+radius)*0.6f, false);	//needed since above startmoving cancels this
				}
				if(!fac->curBuild && !fac->terraforming){
					building=false;
					StopMove();				//cancel the effect of KeepPointingTo
					FinishCommand();
				}
			} else {
				build.Parse(c);
				const float dist = build.pos.distance2D(fac->pos);
				if ((dist < (fac->buildDistance * 0.6f + radius)) ||
				    (!owner->unitDef->canmove && (dist <= (fac->buildDistance+radius-8.0f)))) {
					StopMove();
					if(uh->unitsType[owner->team][build.def->id]>=build.def->maxThisUnit){ //unit restricted
						ENTER_MIXED;
						if(owner->team == gu->myTeam){
							logOutput.Print("%s: Build failed, unit type limit reached",owner->unitDef->humanName.c_str());
							logOutput.SetLastMsgPos(owner->pos);
						}
						ENTER_SYNCED;
						FinishCommand();
					}
					else if(uh->maxUnits>(int)gs->Team(owner->team)->units.size()){ //max unitlimit reached
						buildRetries++;
						owner->moveType->KeepPointingTo(build.pos, fac->buildDistance*0.7f+radius, false);
						if(fac->StartBuild(build) || buildRetries>20){
							building=true;
						} else {
							ENTER_MIXED;
							if(owner->team==gu->myTeam && !(buildRetries&7)){
								logOutput.Print("%s: Build pos blocked",owner->unitDef->humanName.c_str());
								logOutput.SetLastMsgPos(owner->pos);
							}
							ENTER_SYNCED;
							helper->BuggerOff(build.pos,radius);
							NonMoving();
						}
					}
				} else {
					if(owner->moveType->progressState==CMoveType::Failed){
						if(++buildRetries>5){
							StopMove();
							FinishCommand();
						}
					}
					SetGoal(build.pos,owner->pos, fac->buildDistance*0.4f+radius);
				}
			}
		} else {		//!inCommand
			BuildInfo bi;
			bi.pos.x=floor(c.params[0]/SQUARE_SIZE+0.5f)*SQUARE_SIZE;
			bi.pos.z=floor(c.params[2]/SQUARE_SIZE+0.5f)*SQUARE_SIZE;
			bi.pos.y=c.params[1];
			CFeature* f=0;
			if (c.params.size()==4)
				bi.buildFacing = int(c.params[3]);
			bi.def = unitDefHandler->GetUnitByName(boi->second);

			uh->TestUnitBuildSquare(bi,f,owner->allyteam);
			if(f){
				if (!owner->unitDef->canReclaim || !f->def->destructable) {
					// FIXME user shouldn't be able to queue buildings on top of features
					// in the first place (in this case).
					StopMove();
					FinishCommand();
				} else {
					Command c2;
					c2.id=CMD_RECLAIM;
					c2.options=0;
					c2.params.push_back(f->id+MAX_UNITS);
					commandQue.push_front(c2);
					SlowUpdate(); //this assumes that the reclaim command can never return directly without having reclaimed the target
				}
			} else {
				inCommand=true;
				SlowUpdate();
			}
		}
		return;
	}

	switch (c.id) {
		case CMD_STOP:      { ExecuteStop(c);      return; }
		case CMD_REPAIR:    { ExecuteRepair(c);    return; }
		case CMD_CAPTURE:   { ExecuteCapture(c);   return; }
		case CMD_GUARD:     { ExecuteGuard(c);     return; }
		case CMD_RECLAIM:   { ExecuteReclaim(c);   return; }
		case CMD_RESURRECT: { ExecuteResurrect(c); return; }
		case CMD_PATROL:    { ExecutePatrol(c);    return; }
		case CMD_FIGHT:     { ExecuteFight(c);     return; }
		case CMD_RESTORE:   { ExecuteRestore(c);   return; }
		default: {
			CMobileCAI::SlowUpdate();
			return;
		}
	}
}
Exemple #6
0
void CBuilderCAI::ExecuteGuard(Command& c)
{
	if (!owner->unitDef->canGuard)
		return;

	CBuilder* builder = (CBuilder*) owner;
	CUnit* guardee = uh->GetUnit(c.params[0]);

	if (guardee == NULL) { FinishCommand(); return; }
	if (guardee == owner) { FinishCommand(); return; }
	if (UpdateTargetLostTimer(guardee->id) == 0) { FinishCommand(); return; }
	if (guardee->outOfMapTime > (GAME_SPEED * 5)) { FinishCommand(); return; }


	if (CBuilder* b = dynamic_cast<CBuilder*>(guardee)) {
		if (b->terraforming) {
			if (MoveInBuildRange(b->terraformCenter, b->terraformRadius * 0.7f)) {
				builder->HelpTerraform(b);
			} else {
				StopSlowGuard();
			}
			return;
		} else if (b->curReclaim && owner->unitDef->canReclaim) {
			StopSlowGuard();
			if (!ReclaimObject(b->curReclaim)) {
				StopMove();
			}
			return;
		} else if (b->curResurrect && owner->unitDef->canResurrect) {
			StopSlowGuard();
			if (!ResurrectObject(b->curResurrect)) {
				StopMove();
			}
			return;
		} else {
			builder->StopBuild();
		}

		const bool pushRepairCommand =
			(  b->curBuild != NULL) &&
			(  b->curBuild->soloBuilder == NULL || b->curBuild->soloBuilder == owner) &&
			(( b->curBuild->beingBuilt && owner->unitDef->canAssist) ||
			( !b->curBuild->beingBuilt && owner->unitDef->canRepair));

		if (pushRepairCommand) {
			StopSlowGuard();

			Command nc(CMD_REPAIR, c.options);
			nc.params.push_back(b->curBuild->id);

			commandQue.push_front(nc);
			inCommand = false;
			SlowUpdate();
			return;
		}
	}

	if (CFactory* fac = dynamic_cast<CFactory*>(guardee)) {
		const bool pushRepairCommand =
			(  fac->curBuild != NULL) &&
			(  fac->curBuild->soloBuilder == NULL || fac->curBuild->soloBuilder == owner) &&
			(( fac->curBuild->beingBuilt && owner->unitDef->canAssist) ||
			 (!fac->curBuild->beingBuilt && owner->unitDef->canRepair));

		if (pushRepairCommand) {
			StopSlowGuard();

			Command nc(CMD_REPAIR, c.options);
			nc.params.push_back(fac->curBuild->id);

			commandQue.push_front(nc);
			inCommand = false;
			// SlowUpdate();
			return;
		}
	}

	if (!(c.options & CONTROL_KEY) && IsUnitBeingReclaimed(guardee, owner))
		return;

	const float3 pos    = guardee->pos;
	const float  radius = (guardee->immobile) ? guardee->radius : guardee->radius * 0.8f; // in case of mobile units reduce radius a bit
	
	if (MoveInBuildRange(pos, radius)) {
		StartSlowGuard(guardee->moveType->GetMaxSpeed());

		const bool pushRepairCommand =
			(  guardee->health < guardee->maxHealth) &&
			(  guardee->soloBuilder == NULL || guardee->soloBuilder == owner) &&
			(( guardee->beingBuilt && owner->unitDef->canAssist) ||
			 (!guardee->beingBuilt && owner->unitDef->canRepair));

		if (pushRepairCommand) {
			StopSlowGuard();

			Command nc(CMD_REPAIR, c.options);
			nc.params.push_back(guardee->id);

			commandQue.push_front(nc);
			inCommand = false;
			return;
		} else {
			NonMoving();
		}
	} else {
		StopSlowGuard();
	}
}
Exemple #7
0
void CBuilderCAI::ExecuteBuildCmd(Command& c)
{
	CBuilder* builder = (CBuilder*) owner;

	map<int, string>::iterator boi = buildOptions.find(c.GetID());
	if (boi == buildOptions.end())
		return;

	if (!inCommand) {
		BuildInfo bi;
		bi.pos.x = floor(c.params[0] / SQUARE_SIZE + 0.5f) * SQUARE_SIZE;
		bi.pos.z = floor(c.params[2] / SQUARE_SIZE + 0.5f) * SQUARE_SIZE;
		bi.pos.y = c.params[1];

		if (c.params.size() == 4)
			bi.buildFacing = abs((int)c.params[3]) % NUM_FACINGS;

		bi.def = unitDefHandler->GetUnitDefByName(boi->second);

		CFeature* f = NULL;
		uh->TestUnitBuildSquare(bi, f, owner->allyteam, true);

		if (f != NULL) {
			if (!bi.def->isFeature || bi.def->wreckName != f->def->name) {
				ReclaimFeature(f);
			} else {
				FinishCommand();
			}
		} else {
			inCommand = true;
			build.Parse(c);
			ExecuteBuildCmd(c);
		}
		return;
	}

	assert(build.def);
	const float radius = GetBuildOptionRadius(build.def, c.GetID());
	if (building) {
		MoveInBuildRange(build.pos, radius);

		if (!builder->curBuild && !builder->terraforming) {
			building = false;
			StopMove(); // cancel the effect of KeepPointingTo
			FinishCommand();
		}
		// This can only be true if two builders started building
		// the restricted unit in the same simulation frame
		else if (uh->unitsByDefs[owner->team][build.def->id].size() > build.def->maxThisUnit) {
			// unit restricted
			building = false;
			builder->StopBuild();
			CancelRestrictedUnit(boi->second);
		}
	} else {
		if (uh->unitsByDefs[owner->team][build.def->id].size() >= build.def->maxThisUnit) {
			// unit restricted, don't bother moving all the way
			// to the construction site first before telling us
			// (since greyed-out icons can still be clicked etc,
			// would be better to prevent that but doesn't cover
			// case where limit reached while builder en-route)
			CancelRestrictedUnit(boi->second);
			StopMove();
			return;
		}

		build.pos = helper->Pos2BuildPos(build, true);

		// we are on the way to the buildpos, meanwhile it can happen
		// that another builder already finished our buildcmd or blocked
		// the buildpos with another building (skip our buildcmd then)
		const bool checkBuildPos = (gs->frameNum % (5 * UNIT_SLOWUPDATE_RATE)) < UNIT_SLOWUPDATE_RATE; //FIXME add a per-unit solution to better balance the load?
		CFeature* feature = NULL;
		if (checkBuildPos && !uh->TestUnitBuildSquare(build, feature, owner->allyteam, true)) {
			if (!feature) {
				const int yardxpos = int(build.pos.x + 4) / SQUARE_SIZE;
				const int yardypos = int(build.pos.z + 4) / SQUARE_SIZE;
				const CSolidObject* s = groundBlockingObjectMap->GroundBlocked(yardxpos, yardypos);
				const CUnit* u = dynamic_cast<const CUnit*>(s);
				if (u != NULL) {
					const bool canAssist =
						   u->beingBuilt
						&& owner->unitDef->canAssist
						&& (!u->soloBuilder || (u->soloBuilder == owner));

					if ((u->unitDef != build.def) || !canAssist) {
						StopMove();
						FinishCommand();
						return;
					}
				}
			}
		}

		if (MoveInBuildRange(build.pos, radius, true)) {
			if (luaRules && !luaRules->AllowUnitCreation(build.def, owner, &build)) {
				StopMove(); // cancel KeepPointingTo
				FinishCommand();
			}
			else if (!teamHandler->Team(owner->team)->AtUnitLimit()) {
				// unit-limit not yet reached
				CFeature* f = NULL;

				bool waitstance = false;
				if (builder->StartBuild(build, f, waitstance) || (++buildRetries > 30)) {
					building = true;
				}
				else if (f != NULL && (!build.def->isFeature || build.def->wreckName != f->def->name)) {
					inCommand = false;
					ReclaimFeature(f);
				}
				else if (!waitstance) {
					const float fpSqRadius = (build.def->xsize * build.def->xsize + build.def->zsize * build.def->zsize);
					const float fpRadius = (math::sqrt(fpSqRadius) * 0.5f) * SQUARE_SIZE;

					// tell everything within the radius of the soon-to-be buildee
					// to get out of the way; using the model radius is not correct
					// because this can be shorter than half the footprint diagonal
					helper->BuggerOff(build.pos, std::max(radius, fpRadius), false, true, owner->team, NULL);
					NonMoving();
				}
			}
		} else {
			if (owner->moveType->progressState == AMoveType::Failed) {
				if (++buildRetries > 5) {
					StopMove();
					FinishCommand();
				}
			}
		}
	}
}
void CBuilderCAI::SlowUpdate()
{
	if (commandQue.empty()) {
		CMobileCAI::SlowUpdate();
		return;
	}

	if (owner->stunned) {
		return;
	}

	CBuilder* fac = (CBuilder*)owner;
	Command& c = commandQue.front();

	if (OutOfImmobileRange(c)) {
		FinishCommand();
		return;
	}

	map<int, string>::iterator boi = buildOptions.find(c.id);
	if (!owner->beingBuilt && boi != buildOptions.end()) {
		const UnitDef* ud = unitDefHandler->GetUnitByName(boi->second);
		const float radius = GetUnitDefRadius(ud, c.id);
		if (inCommand) {
			if (building) {
				if (f3SqDist(build.pos, fac->pos) > Square(fac->buildDistance + radius - 8.0f)) {
					owner->moveType->StartMoving(build.pos, fac->buildDistance * 0.5f + radius);
				} else {
					StopMove();
					// needed since above startmoving cancels this
					owner->moveType->KeepPointingTo(build.pos, (fac->buildDistance + radius) * 0.6f, false);
				}
				if (!fac->curBuild && !fac->terraforming) {
					building = false;
					StopMove();				//cancel the effect of KeepPointingTo
					FinishCommand();
				}
				// This can only be true if two builders started building
				// the restricted unit in the same simulation frame
				else if (uh->unitsByDefs[owner->team][build.def->id].size() > build.def->maxThisUnit) {
					// unit restricted
					building = false;
					fac->StopBuild();
					CancelRestrictedUnit(boi->second);
				}
			} else {
				build.Parse(c);

				if (uh->unitsByDefs[owner->team][build.def->id].size() >= build.def->maxThisUnit) {
					// unit restricted, don't bother moving all the way
					// to the construction site first before telling us
					// (since greyed-out icons can still be clicked etc,
					// would be better to prevent that but doesn't cover
					// case where limit reached while builder en-route)
					CancelRestrictedUnit(boi->second);
					StopMove();
				} else {
					build.pos = helper->Pos2BuildPos(build);
					const float sqdist = f3SqDist(build.pos, fac->pos);

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

						if (luaRules && !luaRules->AllowUnitCreation(build.def, owner, &build.pos)) {
							FinishCommand();
						}
						else if (uh->maxUnits > (int) teamHandler->Team(owner->team)->units.size()) {
							// max unitlimit reached
							buildRetries++;
							owner->moveType->KeepPointingTo(build.pos, fac->buildDistance * 0.7f + radius, false);

							if (fac->StartBuild(build) || (buildRetries > 20)) {
								building = true;
							} else {
								ENTER_MIXED;
								if ((owner->team == gu->myTeam) && !(buildRetries & 7)) {
									logOutput.Print("%s: Build pos blocked", owner->unitDef->humanName.c_str());
									logOutput.SetLastMsgPos(owner->pos);
								}
								ENTER_SYNCED;
								helper->BuggerOff(build.pos, radius);
								NonMoving();
							}
						}
					} else {
						if (owner->moveType->progressState == AMoveType::Failed) {
							if (++buildRetries > 5) {
								StopMove();
								FinishCommand();
							}
						}
						SetGoal(build.pos, owner->pos, fac->buildDistance * 0.4f + radius);
					}
				}
			}
		} else {		//!inCommand
			BuildInfo bi;
			bi.pos.x=floor(c.params[0]/SQUARE_SIZE+0.5f)*SQUARE_SIZE;
			bi.pos.z=floor(c.params[2]/SQUARE_SIZE+0.5f)*SQUARE_SIZE;
			bi.pos.y=c.params[1];
			CFeature* f=0;
			if (c.params.size()==4)
				bi.buildFacing = int(c.params[3]);
			bi.def = unitDefHandler->GetUnitByName(boi->second);

			uh->TestUnitBuildSquare(bi,f,owner->allyteam);
			if (f) {
				if (!owner->unitDef->canReclaim || !f->def->reclaimable) {
					// FIXME user shouldn't be able to queue buildings on top of features
					// in the first place (in this case).
					StopMove();
					FinishCommand();
				} else {
					Command c2;
					c2.id=CMD_RECLAIM;
					c2.options=0;
					c2.params.push_back(f->id+MAX_UNITS);
					commandQue.push_front(c2);
					SlowUpdate(); //this assumes that the reclaim command can never return directly without having reclaimed the target
				}
			} else {
				inCommand=true;
				SlowUpdate();
			}
		}
		return;
	}

	switch (c.id) {
		case CMD_STOP:      { ExecuteStop(c);      return; }
		case CMD_REPAIR:    { ExecuteRepair(c);    return; }
		case CMD_CAPTURE:   { ExecuteCapture(c);   return; }
		case CMD_GUARD:     { ExecuteGuard(c);     return; }
		case CMD_RECLAIM:   { ExecuteReclaim(c);   return; }
		case CMD_RESURRECT: { ExecuteResurrect(c); return; }
		case CMD_PATROL:    { ExecutePatrol(c);    return; }
		case CMD_FIGHT:     { ExecuteFight(c);     return; }
		case CMD_RESTORE:   { ExecuteRestore(c);   return; }
		default: {
			CMobileCAI::SlowUpdate();
			return;
		}
	}
}
void CMobileCAI::SlowUpdate()
{
	if(owner->unitDef->maxFuel>0 && dynamic_cast<CTAAirMoveType*>(owner->moveType)){
		CTAAirMoveType* myPlane=(CTAAirMoveType*)owner->moveType;
		if(myPlane->reservedPad){
			return;
		} else {
			if(owner->currentFuel <= 0){
				StopMove();
				owner->userAttackGround=false;
				owner->userTarget=0;
				inCommand=false;
				CAirBaseHandler::LandingPad* lp=airBaseHandler->FindAirBase(owner,8000,owner->unitDef->minAirBasePower);
				if(lp){
					myPlane->AddDeathDependence(lp);
					myPlane->reservedPad=lp;
					myPlane->padStatus=0;
					myPlane->oldGoalPos=myPlane->goalPos;
					return;
				}
				float3 landingPos = airBaseHandler->FindClosestAirBasePos(owner,8000,owner->unitDef->minAirBasePower);
				if(landingPos != ZeroVector && owner->pos.distance2D(landingPos) > 300){
					inCommand=false;
					StopMove();
					SetGoal(landingPos,owner->pos);
				} else {
					if(myPlane->aircraftState == CTAAirMoveType::AIRCRAFT_FLYING)
						myPlane->SetState(CTAAirMoveType::AIRCRAFT_LANDING);	
				}
				return;
			}
			if(owner->currentFuel < myPlane->repairBelowHealth*owner->unitDef->maxFuel){
				if(commandQue.empty() || commandQue.front().id==CMD_PATROL){
					CAirBaseHandler::LandingPad* lp=airBaseHandler->FindAirBase(owner,8000,owner->unitDef->minAirBasePower);
					if(lp){
						StopMove();
						owner->userAttackGround=false;
						owner->userTarget=0;
						inCommand=false;
						myPlane->AddDeathDependence(lp);
						myPlane->reservedPad=lp;
						myPlane->padStatus=0;
						myPlane->oldGoalPos=myPlane->goalPos;
						if(myPlane->aircraftState==CTAAirMoveType::AIRCRAFT_LANDED){
							myPlane->SetState(CTAAirMoveType::AIRCRAFT_TAKEOFF);
						}
						return;
					}		
				}
			}
		}
	}

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

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

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

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

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

		if(targetDied || (c.params.size() == 1 && UpdateTargetLostTimer(int(c.params[0])) == 0)){
			StopMove();		//cancel keeppointingto
			FinishCommand();
			break;
		}
		if(orderTarget){
			//note that we handle aircrafts slightly differently
			if((((owner->AttackUnit(orderTarget, c.id==CMD_DGUN)
					&& owner->weapons.size() > 0 
					&& owner->weapons.front()->range -
						owner->weapons.front()->relWeaponPos.Length() >
						orderTarget->pos.distance(owner->pos))
					|| dynamic_cast<CTAAirMoveType*>(owner->moveType))
					&& (owner->pos-orderTarget->pos).Length2D() <
						owner->maxRange*0.9f)
					|| (owner->pos-orderTarget->pos).SqLength2D()<1024){
				StopMove();
				owner->moveType->KeepPointingTo(orderTarget,
					min((float)(owner->losRadius*SQUARE_SIZE*2),
					owner->maxRange*0.9f), true);
			} else if((orderTarget->pos+owner->posErrorVector*128).distance2D(goalPos) > 10+orderTarget->pos.distance2D(owner->pos)*0.2f){
				float3 fix=orderTarget->pos+owner->posErrorVector*128;
				SetGoal(fix,curPos);
			}
		} else {
			float3 pos(c.params[0],c.params[1],c.params[2]);
			if((owner->AttackGround(pos,c.id==CMD_DGUN) && owner->weapons.size() > 0
					&& (owner->pos-pos).Length() < 
						owner->weapons.front()->range -
						owner->weapons.front()->relWeaponPos.Length())
					|| (owner->pos-pos).SqLength2D()<1024){
				StopMove();
				owner->moveType->KeepPointingTo(pos, owner->maxRange*0.9f, true);
			} else if(pos.distance2D(goalPos)>10){
				SetGoal(pos,curPos);
			}
		}
		break;
	case CMD_GUARD:
		assert(owner->unitDef->canGuard);
		if(int(c.params[0]) >= 0 && uh->units[int(c.params[0])] != NULL && UpdateTargetLostTimer(int(c.params[0]))){
			CUnit* guarded=uh->units[int(c.params[0])];
			if(owner->unitDef->canAttack && guarded->lastAttacker && guarded->lastAttack+40<gs->frameNum
			  && (owner->hasUWWeapons || !guarded->lastAttacker->isUnderWater)){
				Command nc;
				nc.id=CMD_ATTACK;
				nc.params.push_back(guarded->lastAttacker->id);
				nc.options=c.options;
				commandQue.push_front(nc);
				SlowUpdate();
				return;
			} else {
				float3 dif=guarded->pos-curPos;
				dif.Normalize();
				float3 goal=guarded->pos-dif*(guarded->radius+owner->radius+64);
				if((goal-curPos).SqLength2D()<8000){
					StopMove();
					NonMoving();
				}else{
					if((goalPos-goal).SqLength2D()>3600)
						SetGoal(goal,curPos);
				}
			}
		} else {
			FinishCommand();
		}
		break;
	default:
		CCommandAI::SlowUpdate();
		break;
	}
}
Exemple #10
0
void CMobileCAI::IdleCheck(void)
{
	if(owner->unitDef->canAttack && owner->fireState
			&& !owner->weapons.empty() && owner->haveTarget) {
		if(!owner->userTarget) {
			owner->haveTarget = false;
		} else if(owner->pos.SqDistance2D(owner->userTarget->pos) <
				Square(owner->maxRange + 200*owner->moveState*owner->moveState)) {
			Command c;
			c.id = CMD_ATTACK;
			c.options=INTERNAL_ORDER;
			c.params.push_back(owner->userTarget->id);
			c.timeOut = gs->frameNum + 140;
			commandQue.push_front(c);
			tempOrder = true;
			commandPos1 = owner->pos;
			commandPos2 = owner->pos;
			return;
		}
	}
	if(owner->unitDef->canAttack && owner->fireState
				&& !owner->weapons.empty() && !owner->haveTarget) {
		if(owner->lastAttacker && owner->lastAttack + 200 > gs->frameNum
				&& !(owner->unitDef->noChaseCategory & owner->lastAttacker->category)){
			float3 apos=owner->lastAttacker->pos;
			float dist=apos.SqDistance2D(owner->pos);
			if(dist<Square(owner->maxRange+200*owner->moveState*owner->moveState)){
				Command c;
				c.id=CMD_ATTACK;
				c.options=INTERNAL_ORDER;
				c.params.push_back(owner->lastAttacker->id);
				c.timeOut=gs->frameNum+140;
				commandQue.push_front(c);
				tempOrder = true;
				commandPos1 = owner->pos;
				commandPos2 = owner->pos;
				return;
			}
		}
	}
	if (owner->unitDef->canAttack && (gs->frameNum >= lastIdleCheck + 10)
			&& owner->fireState >= 2 && !owner->weapons.empty() && !owner->haveTarget)
	{
		const float searchRadius = owner->maxRange + 150 * owner->moveState * owner->moveState;
		CUnit* enemy = helper->GetClosestValidTarget(owner->pos, searchRadius, owner->allyteam, this);
		if (enemy != NULL) {
			Command c;
			c.id=CMD_ATTACK;
			c.options=INTERNAL_ORDER;
			c.params.push_back(enemy->id);
			c.timeOut=gs->frameNum+140;
			commandQue.push_front(c);
			tempOrder = true;
			commandPos1 = owner->pos;
			commandPos2 = owner->pos;
			return;
		}
	}
	if (owner->usingScriptMoveType) {
		return;
	}
	lastIdleCheck = gs->frameNum;
	if (((owner->pos - lastUserGoal).SqLength2D() > 10000.0f) &&
	    !owner->haveTarget && !dynamic_cast<CTAAirMoveType*>(owner->moveType)) {
		//note that this is not internal order so that we dont keep generating
		//new orders if we cant get to that pos
		Command c;
		c.id=CMD_MOVE;
		c.options=0;
		c.params.push_back(lastUserGoal.x);
		c.params.push_back(lastUserGoal.y);
		c.params.push_back(lastUserGoal.z);
		commandQue.push_front(c);
		unimportantMove=true;
	} else {
		NonMoving();
	}
}
void CBuilderCAI::SlowUpdate()
{
	if(commandQue.empty()){
		CMobileCAI::SlowUpdate();
		return;
	}

	Command& c=commandQue.front();
	CBuilder* fac=(CBuilder*)owner;

	map<int,string>::iterator boi;
	if((boi=buildOptions.find(c.id))!=buildOptions.end()){
		float radius;
		if(cachedRadiusId==c.id){
			radius=cachedRadius;
		} else {
			radius=modelParser->Load3DO(unitDefHandler->GetUnitByName(boi->second)->model.modelpath,1,owner->team)->radius;
			cachedRadiusId=c.id;
			cachedRadius=radius;
		}
		if(inCommand){
			if(building){
				if(buildPos.distance2D(fac->pos)>fac->buildDistance+radius-8){
					owner->moveType->StartMoving(buildPos, fac->buildDistance*0.5+radius);
				} else {
					StopMove();
					owner->moveType->KeepPointingTo(buildPos, (fac->buildDistance+radius)*0.6, false);	//needed since above startmoving cancels this
				}
				if(!fac->curBuild && !fac->terraforming){
					building=false;
					StopMove();				//cancel the effect of KeepPointingTo
					FinishCommand();
				}
			} else {
				buildPos.x=c.params[0];
				buildPos.y=c.params[1];
				buildPos.z=c.params[2];
				if(buildPos.distance2D(fac->pos)<fac->buildDistance*0.6+radius){
					StopMove();
					if(uh->maxUnits>(int)gs->Team(owner->team)->units.size()){
						buildRetries++;
						owner->moveType->KeepPointingTo(buildPos, fac->buildDistance*0.7+radius, false);
						if(fac->StartBuild(boi->second,buildPos) || buildRetries>20){
							building=true;
						} else {
							ENTER_MIXED;
							if(owner->team==gu->myTeam && !(buildRetries&7)){
								info->AddLine("%s: Build pos blocked",owner->unitDef->humanName.c_str());
								info->SetLastMsgPos(owner->pos);
							}
							ENTER_SYNCED;
							helper->BuggerOff(buildPos,radius);
							NonMoving();
						}
					}
				} else {
					if(owner->moveType->progressState==CMoveType::Failed){
						if(++buildRetries>5){
							StopMove();
							FinishCommand();			
						}
					}
					SetGoal(buildPos,owner->pos, fac->buildDistance*0.4+radius);
				}
			}
		} else {		//!inCommand
			float3 pos;
			pos.x=floor(c.params[0]/SQUARE_SIZE+0.5)*SQUARE_SIZE;
			pos.z=floor(c.params[2]/SQUARE_SIZE+0.5)*SQUARE_SIZE;
			pos.y=c.params[1];
			CFeature* f=0;
			uh->TestUnitBuildSquare(pos,boi->second,f);
			if(f){
				Command c2;
				c2.id=CMD_RECLAIM;
				c2.options=0;
				c2.params.push_back(f->id+MAX_UNITS);
				commandQue.push_front(c2);
				SlowUpdate();										//this assumes that the reclaim command can never return directly without having reclaimed the target
			} else {
				inCommand=true;
				SlowUpdate();
			}
		}
		return;
	}
	switch(c.id){
	case CMD_STOP:
		building=false;
		fac->StopBuild();
		break;
	case CMD_REPAIR:{
		if(c.params.size()==1){		//repair unit
			CUnit* unit=uh->units[(int)c.params[0]];
			if(commandFromPatrol && owner->moveState==1){		//limit how far away we go
				if(unit && LinePointDist(commandPos1,commandPos2,unit->pos) > 500){
					StopMove();
					FinishCommand();
					break;
				}
			}
			if(unit && unit->health<unit->maxHealth && unit!=owner){
				if(unit->pos.distance2D(fac->pos)<fac->buildDistance+unit->radius-8){
					StopMove();
					fac->SetRepairTarget(unit);
					owner->moveType->KeepPointingTo(unit->pos, fac->buildDistance*0.9+unit->radius, false);
				} else {
					if(goalPos.distance2D(unit->pos)>1)
						SetGoal(unit->pos,owner->pos, fac->buildDistance*0.9+unit->radius);
				}
			} else {
				StopMove();
				FinishCommand();
			}
		} else {			//repair area
			float3 pos(c.params[0],c.params[1],c.params[2]);
			float radius=c.params[3];
			if(FindRepairTargetAndRepair(pos,radius,c.options,false)){
				inCommand=false;
				SlowUpdate();
				return;
			}
			if(!(c.options & ALT_KEY)){
				FinishCommand();
			}
		}
		return;}
	case CMD_CAPTURE:{
		if(c.params.size()==1){		//capture unit
			CUnit* unit=uh->units[(int)c.params[0]];
			if(unit && unit->team!=owner->team){
				if(unit->pos.distance2D(fac->pos)<fac->buildDistance+unit->radius-8){
					StopMove();
					fac->SetCaptureTarget(unit);
					owner->moveType->KeepPointingTo(unit->pos, fac->buildDistance*0.9+unit->radius, false);
				} else {
					if(goalPos.distance2D(unit->pos)>1)
						SetGoal(unit->pos,owner->pos, fac->buildDistance*0.9+unit->radius);
				}
			} else {
				StopMove();
				FinishCommand();
			}
		} else {			//capture area
			float3 pos(c.params[0],c.params[1],c.params[2]);
			float radius=c.params[3];
			if(FindCaptureTargetAndCapture(pos,radius,c.options)){
				inCommand=false;
				SlowUpdate();
				return;
			}
			if(!(c.options & ALT_KEY)){
				FinishCommand();
			}
		}
		return;}
	case CMD_GUARD:{
		CUnit* guarded=uh->units[(int)c.params[0]];
		if(guarded && guarded!=owner){
			if(CBuilder* b=dynamic_cast<CBuilder*>(guarded)){
				if(b->terraforming){
					if(fac->pos.distance2D(b->terraformCenter)<fac->buildDistance*0.8+b->terraformRadius*0.7){
						StopMove();
						owner->moveType->KeepPointingTo(b->terraformCenter, fac->buildDistance*0.9, false);
						fac->HelpTerraform(b);
					} else {
						SetGoal(b->terraformCenter,fac->pos,fac->buildDistance*0.7+b->terraformRadius*0.6);
					}
					return;
				}
				if(b->curBuild){
					Command nc;
					nc.id=CMD_REPAIR;
					nc.options=c.options;
					nc.params.push_back(b->curBuild->id);
					commandQue.push_front(nc);
					inCommand=false;
//					SlowUpdate();
					return;
				}
			}
			if(CFactory* f=dynamic_cast<CFactory*>(guarded)){
				if(f->curBuild){
					Command nc;
					nc.id=CMD_REPAIR;
					nc.options=c.options;
					nc.params.push_back(f->curBuild->id);
					commandQue.push_front(nc);
					inCommand=false;
//					SlowUpdate();
					return;
				}
			}
			float3 curPos=owner->pos;
			float3 dif=guarded->pos-curPos;
			dif.Normalize();
			float3 goal=guarded->pos-dif*(fac->buildDistance-5);
			if((guarded->pos-curPos).SqLength2D()<(fac->buildDistance*0.9+guarded->radius)*(fac->buildDistance*0.9+guarded->radius)){
				StopMove();
//				info->AddLine("should point with type 3?");
				owner->moveType->KeepPointingTo(guarded->pos, fac->buildDistance*0.9+guarded->radius, false);
				if(guarded->health<guarded->maxHealth)
					fac->SetRepairTarget(guarded);
				else
					NonMoving();
			}else{
				if((goalPos-goal).SqLength2D()>4000)
					SetGoal(goal,curPos);
			}
		} else {
			FinishCommand();
		}
		return;}
	case CMD_RECLAIM:{
		if(c.params.size()==1){
			int id=(int) c.params[0];
			if(id>=MAX_UNITS){		//reclaim feature
				CFeature* feature=featureHandler->features[id-MAX_UNITS];
				if(feature){
					if(feature->pos.distance2D(fac->pos)<fac->buildDistance*0.9+feature->radius){
						StopMove();
						owner->moveType->KeepPointingTo(feature->pos, fac->buildDistance*0.9+feature->radius, false);
						fac->SetReclaimTarget(feature);
					} else {
						if(goalPos.distance2D(feature->pos)>1){
							SetGoal(feature->pos,owner->pos, fac->buildDistance*0.8+feature->radius);
						} else {
							if(owner->moveType->progressState==CMoveType::Failed){
								StopMove();
								FinishCommand();
							}
						}
					}
				} else {
					StopMove();
					FinishCommand();
				}

			} else {							//reclaim unit
				CUnit* unit=uh->units[id];
				if(unit && unit!=owner){
					if(unit->pos.distance2D(fac->pos)<fac->buildDistance-1+unit->radius){
						StopMove();
						owner->moveType->KeepPointingTo(unit->pos, fac->buildDistance*0.9+unit->radius, false);
						fac->SetReclaimTarget(unit);
					} else {
						if(goalPos.distance2D(unit->pos)>1){
							SetGoal(unit->pos,owner->pos);
						}else{
							if(owner->moveType->progressState==CMoveType::Failed){
								StopMove();
								FinishCommand();			
							}
						}
					}
				} else {
					FinishCommand();
				}
			}
		} else if(c.params.size()==4){//area reclaim
			float3 pos(c.params[0],c.params[1],c.params[2]);
			float radius=c.params[3];
			if(FindReclaimableFeatureAndReclaim(pos,radius,c.options)){
				inCommand=false;
				SlowUpdate();
				return;
			}
			if(!(c.options & ALT_KEY)){
				FinishCommand();
			}
		} else {	//wrong number of parameters
			FinishCommand();
		}
		return;}
	case CMD_RESURRECT:{
		if(c.params.size()==1){
			int id=(int)c.params[0];
			if(id>=MAX_UNITS){		//resurrect feature
				CFeature* feature=featureHandler->features[id-MAX_UNITS];
				if(feature && feature->createdFromUnit!=""){
					if(feature->pos.distance2D(fac->pos)<fac->buildDistance*0.9+feature->radius){
						StopMove();
						owner->moveType->KeepPointingTo(feature->pos, fac->buildDistance*0.9+feature->radius, false);
						fac->SetResurrectTarget(feature);
					} else {
						if(goalPos.distance2D(feature->pos)>1){
							SetGoal(feature->pos,owner->pos, fac->buildDistance*0.8+feature->radius);
						} else {
							if(owner->moveType->progressState==CMoveType::Failed){
								StopMove();
								FinishCommand();			
							}
						}
					}
				} else {
					if(fac->lastResurrected && uh->units[fac->lastResurrected]){	//resurrection finished, start repair
						c.id=CMD_REPAIR;		//kind of hackery to overwrite the current order i suppose
						c.params.clear();
						c.params.push_back(fac->lastResurrected);
						c.options|=INTERNAL_ORDER;
						fac->lastResurrected=0;
						inCommand=false;
						SlowUpdate();
						return;
					}
					StopMove();
					FinishCommand();
				}
			} else {							//resurrect unit
				FinishCommand();
			}
		} else if(c.params.size()==4){//area resurect
			float3 pos(c.params[0],c.params[1],c.params[2]);
			float radius=c.params[3];
			if(FindResurrectableFeatureAndResurrect(pos,radius,c.options)){
				inCommand=false;
				SlowUpdate();
				return;
			}
			if(!(c.options & ALT_KEY)){
				FinishCommand();
			}
		} else {	//wrong number of parameters
			FinishCommand();
		}
		return;}
	case CMD_PATROL:{
		float3 curPos=owner->pos;
		if(commandFromPatrol){
			commandFromPatrol=false;
			inCommand=true;
			--patrolTime;		//prevent searching reclaimtargets etc directly if one of those call returned directly (infinite loop)
		}
		if(!inCommand){
			float3 pos(c.params[0],c.params[1],c.params[2]);
			inCommand=true;
			Command co;
			co.id=CMD_PATROL;
			co.params.push_back(curPos.x);
			co.params.push_back(curPos.y);
			co.params.push_back(curPos.z);
			commandQue.push_front(co);
			activeCommand=1;
			commandPos1=curPos;
			patrolTime=0;
		}
		Command& c=commandQue[activeCommand];

		if(c.params.size()<3){		//this shouldnt happen but anyway ...
			info->AddLine("Error: got patrol cmd with less than 3 params on %s in BuilderCAI",owner->unitDef->humanName.c_str());
			return;
		}
		patrolGoal=float3(c.params[0],c.params[1],c.params[2]);
		commandPos2=patrolGoal;

		if(!(patrolTime&3) && owner->fireState==2){
			if(FindRepairTargetAndRepair(owner->pos,300*owner->moveState,c.options,true)){
				CUnit* target=uh->units[(int)commandQue.front().params[0]];
				if(owner->moveState!=1 || LinePointDist(commandPos1,commandPos2,target->pos) < 300){
					commandFromPatrol=true;
					inCommand=false;
					SlowUpdate();
					return;
				} else {
					commandQue.pop_front();
				}
			}
			if(FindReclaimableFeatureAndReclaim(owner->pos,300*owner->moveState,c.options)){
				commandFromPatrol=true;
				inCommand=false;
				SlowUpdate();
				return;
			}
		}
		patrolTime++;
		if(!(patrolGoal==goalPos)){
			SetGoal(patrolGoal,curPos);
		}
		if((curPos-patrolGoal).SqLength2D()<4096){
			if(owner->group)
				owner->group->CommandFinished(owner->id,CMD_PATROL);

			if((int)commandQue.size()<=activeCommand+1)
				activeCommand=0;
			else {
				if(commandQue[activeCommand+1].id!=CMD_PATROL)
					activeCommand=0;
				else
					activeCommand++;
			}
			commandPos1=commandPos2;
		}
		if(owner->haveTarget && owner->moveType->progressState!=CMoveType::Done)
			StopMove();
		else if(owner->moveType->progressState!=CMoveType::Active)
			owner->moveType->StartMoving(goalPos, 8);

		return;}
	case CMD_RESTORE:{
		if(inCommand){
			if(!fac->terraforming){
				FinishCommand();
			}
		} else {
			float3 pos(c.params[0],c.params[1],c.params[2]);
			float radius(c.params[3]);
			if(fac->pos.distance2D(pos)<fac->buildDistance-1){
				StopMove();
				fac->StartRestore(pos,radius);
				owner->moveType->KeepPointingTo(pos, fac->buildDistance*0.9, false);
				inCommand=true;
			} else {
				SetGoal(pos,owner->pos, fac->buildDistance*0.9);
			}
		}
		return;}
	default:
		break;
	}
	CMobileCAI::SlowUpdate();
}
void CMobileCAI::SlowUpdate()
{
    if(owner->unitDef->maxFuel>0 && dynamic_cast<CTAAirMoveType*>(owner->moveType)) {
        CTAAirMoveType* myPlane=(CTAAirMoveType*)owner->moveType;
        if(myPlane->reservedPad) {
            return;
        } else {
            if(owner->currentFuel <= 0) {
                StopMove();
                owner->userAttackGround=false;
                owner->userTarget=0;
                inCommand=false;
                CAirBaseHandler::LandingPad* lp=airBaseHandler->FindAirBase(owner,8000,owner->unitDef->minAirBasePower);
                if(lp) {
                    myPlane->AddDeathDependence(lp);
                    myPlane->reservedPad=lp;
                    myPlane->padStatus=0;
                    myPlane->oldGoalPos=myPlane->goalPos;
                    return;
                }
                float3 landingPos = airBaseHandler->FindClosestAirBasePos(owner,8000,owner->unitDef->minAirBasePower);
                if(landingPos != ZeroVector && owner->pos.distance2D(landingPos) > 300) {
                    inCommand=false;
                    StopMove();
                    SetGoal(landingPos,owner->pos);
                } else {
                    if(myPlane->aircraftState == CTAAirMoveType::AIRCRAFT_FLYING)
                        myPlane->SetState(CTAAirMoveType::AIRCRAFT_LANDING);
                }
                return;
            }
            if(owner->currentFuel < myPlane->repairBelowHealth*owner->unitDef->maxFuel) {
                if(commandQue.empty() || commandQue.front().id==CMD_PATROL) {
                    CAirBaseHandler::LandingPad* lp=airBaseHandler->FindAirBase(owner,8000,owner->unitDef->minAirBasePower);
                    if(lp) {
                        StopMove();
                        owner->userAttackGround=false;
                        owner->userTarget=0;
                        inCommand=false;
                        myPlane->AddDeathDependence(lp);
                        myPlane->reservedPad=lp;
                        myPlane->padStatus=0;
                        myPlane->oldGoalPos=myPlane->goalPos;
                        if(myPlane->aircraftState==CTAAirMoveType::AIRCRAFT_LANDED) {
                            myPlane->SetState(CTAAirMoveType::AIRCRAFT_TAKEOFF);
                        }
                        return;
                    }
                }
            }
        }
    }

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

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

    float3 curPos=owner->pos;

    Command& c=commandQue.front();
    switch(c.id) {
    case CMD_STOP: {
        StopMove();
        CCommandAI::SlowUpdate();
        break;
    }
    case CMD_MOVE: {
        float3 pos(c.params[0],c.params[1],c.params[2]);
        if(!(pos==goalPos)) {
            SetGoal(pos,curPos);
        }
        if((curPos-goalPos).SqLength2D()<1024 || owner->moveType->progressState==CMoveType::Failed) {
            FinishCommand();
        }
        break;
    }
    case CMD_PATROL: {
        if(c.params.size()<3) {		//this shouldnt happen but anyway ...
            info->AddLine("Error: got patrol cmd with less than 3 params on %s in mobilecai",
                          owner->unitDef->humanName.c_str());
            return;
        }
        Command temp;
        temp.id = CMD_FIGHT;
        temp.params.push_back(c.params[0]);
        temp.params.push_back(c.params[1]);
        temp.params.push_back(c.params[2]);
        temp.options = c.options|INTERNAL_ORDER;
        commandQue.push_back(c);
        commandQue.pop_front();
        commandQue.push_front(temp);
        if(owner->group) {
            owner->group->CommandFinished(owner->id,CMD_PATROL);
        }
        SlowUpdate();
        return;
    }
    case CMD_FIGHT: {
        if(tempOrder) {
            inCommand = true;
            tempOrder = false;
        }
        if(c.params.size()<3) {		//this shouldnt happen but anyway ...
            info->AddLine("Error: got fight cmd with less than 3 params on %s in mobilecai",
                          owner->unitDef->humanName.c_str());
            return;
        }
        if(c.params.size() >= 6) {
            if(!inCommand) {
                commandPos1 = float3(c.params[3],c.params[4],c.params[5]);
            }
        } else {
            commandPos1 = curPos;
        }
        float3 pos(c.params[0],c.params[1],c.params[2]);
        if(!inCommand) {
            inCommand = true;
            commandPos2 = pos;
        }
        if(pos!=goalPos) {
            SetGoal(pos,curPos);
        }

        if(owner->fireState==2) {
            CUnit* enemy=helper->GetClosestEnemyUnit(
                             curPos, owner->maxRange+100*owner->moveState*owner->moveState, owner->allyteam);
            if(enemy && (owner->hasUWWeapons || !enemy->isUnderWater)
                    && !(owner->unitDef->noChaseCategory & enemy->category) && !owner->weapons.empty()) {	//todo: make sure they dont stray to far from path
                if(owner->moveState!=1
                        || LinePointDist(commandPos1,commandPos2,enemy->pos)
                        < 300+max(owner->maxRange, (float)owner->losRadius)) {
                    Command c2,c3;
                    c3 = this->GetReturnFight(c);
                    c2.id=CMD_ATTACK;
                    c2.options=c.options|INTERNAL_ORDER;
                    c2.params.push_back(enemy->id);
                    commandQue.push_front(c3);
                    commandQue.push_front(c2);
                    inCommand=false;
                    tempOrder=true;
                    if(lastPC!=gs->frameNum) {	//avoid infinite loops
                        lastPC=gs->frameNum;
                        SlowUpdate();
                    }
                    return;
                }
            }
        }
        if((curPos-goalPos).SqLength2D()<4096 || owner->moveType->progressState==CMoveType::Failed) {
            FinishCommand();
        }
        return;
    }
    case CMD_DGUN:
        if(uh->limitDgun && owner->unitDef->isCommander
                && owner->pos.distance(gs->Team(owner->team)->startPos)>uh->dgunRadius) {
            StopMove();
            FinishCommand();
            return;
        }
    //no break
    case CMD_ATTACK:
        if(tempOrder && owner->moveState<2) {		//limit how far away we fly
            if(orderTarget && LinePointDist(commandPos1,commandPos2,orderTarget->pos) > 500+owner->maxRange) {
                StopMove();
                FinishCommand();
                break;
            }
        }
        if(!inCommand) {
            if(c.params.size()==1) {
                if(uh->units[int(c.params[0])]!=0 && uh->units[int(c.params[0])]!=owner) {
                    float3 fix=uh->units[int(c.params[0])]->pos+owner->posErrorVector*128;
                    SetGoal(fix,curPos);
                    orderTarget=uh->units[int(c.params[0])];
                    AddDeathDependence(orderTarget);
                    inCommand=true;
                } else {
                    StopMove();		//cancel keeppointingto
                    FinishCommand();
                    break;
                }
            } else {
                float3 pos(c.params[0],c.params[1],c.params[2]);
                SetGoal(pos,curPos);
                inCommand=true;
            }
        }
        else if ((c.params.size() == 3) && (owner->commandShotCount > 0) && (commandQue.size() > 1)) {
            StopMove();
            FinishCommand();
            break;
        }

        if(targetDied || (c.params.size() == 1 && uh->units[int(c.params[0])] && !(uh->units[int(c.params[0])]->losStatus[owner->allyteam] & LOS_INRADAR))) {
            StopMove();		//cancel keeppointingto
            FinishCommand();
            break;
        }
        if(orderTarget) {
            //note that we handle aircrafts slightly differently
            if((((owner->AttackUnit(orderTarget, c.id==CMD_DGUN) && owner->weapons.size() > 0
                    && owner->weapons.front()->range > orderTarget->pos.distance(owner->pos))
                    || dynamic_cast<CTAAirMoveType*>(owner->moveType))
                    && (owner->pos-orderTarget->pos).Length2D()<owner->maxRange*0.9)
                    || (owner->pos-orderTarget->pos).SqLength2D()<1024) {
                StopMove();
                owner->moveType->KeepPointingTo(orderTarget->pos, min((double)(owner->losRadius*SQUARE_SIZE*2), owner->maxRange*0.9), true);
            } else if((orderTarget->pos+owner->posErrorVector*128).distance2D(goalPos) > 10+orderTarget->pos.distance2D(owner->pos)*0.2) {
                float3 fix=orderTarget->pos+owner->posErrorVector*128;
                SetGoal(fix,curPos);
            }
        } else {
            float3 pos(c.params[0],c.params[1],c.params[2]);
            if((owner->AttackGround(pos,c.id==CMD_DGUN) && owner->weapons.size() > 0
                    && (owner->pos-pos).Length()< owner->weapons.front()->range) || (owner->pos-pos).SqLength2D()<1024) {
                StopMove();
                owner->moveType->KeepPointingTo(pos, owner->maxRange*0.9, true);
            } else if(pos.distance2D(goalPos)>10) {
                SetGoal(pos,curPos);
            }
        }
        break;
    case CMD_GUARD:
        if(uh->units[int(c.params[0])]!=0) {
            CUnit* guarded=uh->units[int(c.params[0])];
            if(guarded->lastAttacker && guarded->lastAttack+40<gs->frameNum
                    && (owner->hasUWWeapons || !guarded->lastAttacker->isUnderWater)) {
                Command nc;
                nc.id=CMD_ATTACK;
                nc.params.push_back(guarded->lastAttacker->id);
                nc.options=c.options;
                commandQue.push_front(nc);
                SlowUpdate();
                return;
            } else {
                float3 dif=guarded->pos-curPos;
                dif.Normalize();
                float3 goal=guarded->pos-dif*(guarded->radius+owner->radius+64);
                if((goal-curPos).SqLength2D()<8000) {
                    StopMove();
                    NonMoving();
                } else {
                    if((goalPos-goal).SqLength2D()>3600)
                        SetGoal(goal,curPos);
                }
            }
        } else {
            FinishCommand();
        }
        break;
    default:
        CCommandAI::SlowUpdate();
        break;
    }
}
Exemple #13
0
void CBuilderCAI::ExecuteBuildCmd(Command& c)
{
	const map<int, string>::const_iterator boi = buildOptions.find(c.GetID());

	if (boi == buildOptions.end())
		return;

	if (!inCommand) {
		BuildInfo bi;
		bi.pos.x = math::floor(c.params[0] / SQUARE_SIZE + 0.5f) * SQUARE_SIZE;
		bi.pos.z = math::floor(c.params[2] / SQUARE_SIZE + 0.5f) * SQUARE_SIZE;
		bi.pos.y = c.params[1];

		if (c.params.size() == 4)
			bi.buildFacing = abs((int)c.params[3]) % NUM_FACINGS;

		bi.def = unitDefHandler->GetUnitDefByName(boi->second);

		CFeature* f = NULL;
		CGameHelper::TestUnitBuildSquare(bi, f, owner->allyteam, true);

		if (f != NULL) {
			if (!bi.def->isFeature || bi.def->wreckName != f->def->name) {
				ReclaimFeature(f);
			} else {
				FinishCommand();
			}
			return;
		}

		inCommand = true;
		build.Parse(c);
	}

	assert(build.def->id == -c.GetID() && build.def->id != 0);
	const float buildeeRadius = GetBuildOptionRadius(build.def, c.GetID());

	if (building) {
		// keep moving until 3D distance to buildPos is LEQ our buildDistance
		MoveInBuildRange(build.pos, 0.0f);

		if (!ownerBuilder->curBuild && !ownerBuilder->terraforming) {
			building = false;
			StopMove(); // cancel the effect of KeepPointingTo
			FinishCommand();
		}
		// This can only be true if two builders started building
		// the restricted unit in the same simulation frame
		else if (unitHandler->unitsByDefs[owner->team][build.def->id].size() > build.def->maxThisUnit) {
			// unit restricted
			building = false;
			ownerBuilder->StopBuild();
			CancelRestrictedUnit(boi->second);
		}
	} else {
		if (unitHandler->unitsByDefs[owner->team][build.def->id].size() >= build.def->maxThisUnit) {
			// unit restricted, don't bother moving all the way
			// to the construction site first before telling us
			// (since greyed-out icons can still be clicked etc,
			// would be better to prevent that but doesn't cover
			// case where limit reached while builder en-route)
			CancelRestrictedUnit(boi->second);
			StopMove();
			return;
		}

		build.pos = CGameHelper::Pos2BuildPos(build, true);

		// keep moving until until 3D distance to buildPos is LEQ our buildDistance
		if (MoveInBuildRange(build.pos, 0.0f, true)) {
			if (IsBuildPosBlocked(build)) {
				StopMove();
				FinishCommand();
				return;
			}

			if (!eventHandler.AllowUnitCreation(build.def, owner, &build)) {
				StopMove(); // cancel KeepPointingTo
				FinishCommand();
				return;
			}

			if (teamHandler->Team(owner->team)->AtUnitLimit()) {
				return;
			}

			CFeature* f = NULL;

			bool waitstance = false;
			if (ownerBuilder->StartBuild(build, f, waitstance) || (++buildRetries > 30)) {
				building = true;
			}
			else if (f != NULL && (!build.def->isFeature || build.def->wreckName != f->def->name)) {
				inCommand = false;
				ReclaimFeature(f);
			}
			else if (!waitstance) {
				const float fpSqRadius = (build.def->xsize * build.def->xsize + build.def->zsize * build.def->zsize);
				const float fpRadius = (math::sqrt(fpSqRadius) * 0.5f) * SQUARE_SIZE;

				// tell everything within the radius of the soon-to-be buildee
				// to get out of the way; using the model radius is not correct
				// because this can be shorter than half the footprint diagonal
				CGameHelper::BuggerOff(build.pos, std::max(buildeeRadius, fpRadius), false, true, owner->team, NULL);
				NonMoving();
			}
		} else {
			if (owner->moveType->progressState == AMoveType::Failed) {
				if (++buildRetries > 5) {
					StopMove();
					FinishCommand();
					return;
				}
			}

			// we are on the way to the buildpos, meanwhile it can happen
			// that another builder already finished our buildcmd or blocked
			// the buildpos with another building (skip our buildcmd then)
			if ((++randomCounter % 5) == 0) {
				if (IsBuildPosBlocked(build)) {
					StopMove();
					FinishCommand();
					return;
				}
			}
		}
	}
}