Exemplo n.º 1
0
// give move orders to units along previously generated pathToTarget
void CAttackGroup::MoveAlongPath(float3& groupPosition, int numUnits) {
	const int maxStepsAhead = 8;
	int pathMaxIndex = (int) pathToTarget.size() - 1;
	int step1 = std::min(pathIterator + maxStepsAhead / 2, pathMaxIndex);
	int step2 = std::min(pathIterator + maxStepsAhead, pathMaxIndex);

	const float3& moveToHereFirst = pathToTarget[step1];
	const float3& moveToHere = pathToTarget[step2];

	// if we aren't there yet
	if (groupPosition.distance2D(pathToTarget[pathMaxIndex]) > GROUP_DESTINATION_SLACK) {
		// TODO: give a group the order instead of each unit
		assert(numUnits >= 0);

		for (unsigned int i = 0; i < (unsigned int)numUnits; i++) {
			CUNIT* unit = ai->GetUnit(units[i]);

			if (ai->cb->GetUnitDef(unit->uid) != NULL) {
				// TODO: when they are near target, change this so they eg. line up
				// while some are here and some aren't, there's also something that
				// should be done with the units in front that are given the same
				// order+shiftorder and skittle around back and forth meanwhile if
				// the single unit isn't there yet
				if ((unit->pos()).distance2D(pathToTarget[pathMaxIndex]) > UNIT_DESTINATION_SLACK) {
					unit->Move(moveToHereFirst);

					if (moveToHere != moveToHereFirst) {
						unit->MoveShift(moveToHere);
					}
				}
			}
		}

		// if group is as close as the pathiterator-indicated target
		// is to the end of the path, increase pathIterator

		pathIterator = 0;
		float3 endOfPathPos = pathToTarget[pathMaxIndex];
		float groupDistanceToEnemy = groupPosition.distance2D(endOfPathPos);
		float pathIteratorTargetDistanceToEnemy = pathToTarget[pathIterator].distance2D(endOfPathPos);
		int increment = maxStepsAhead / 2;

		while (groupDistanceToEnemy <= pathIteratorTargetDistanceToEnemy && pathIterator < pathMaxIndex) {
			pathIterator = std::min(pathIterator + increment, pathMaxIndex);
			pathIteratorTargetDistanceToEnemy = pathToTarget[pathIterator].distance2D(endOfPathPos);
		}

		pathIterator = std::min(pathIterator, pathMaxIndex);
	}
	else {
		// group thinks it has arrived at the destination
		this->ClearTarget();
	}
}
Exemplo n.º 2
0
void CKAIK::Serialize(creg::ISerializer* s) {
	if (ai->Initialized()) {
		for (int i = 0; i < MAX_UNITS; i++) {
			CUNIT* u = ai->GetUnit(i);

			if (ai->ccb->GetUnitDef(i) != NULL) {
				// do not save non-existing units
				s->SerializeObjectInstance(u, u->GetClass());

				if (!s->IsWriting()) {
					u->uid = i;
				}
			} else if (!s->IsWriting()) {
				u->uid = i;
			}
		}

		s->SerializeObjectInstance(ai, ai->GetClass());
	}
}
Exemplo n.º 3
0
bool CUnitHandler::FactoryBuilderAdd(BuilderTracker* builderTracker) {
	assert(builderTracker->buildTaskId == 0);
	assert(builderTracker->taskPlanId == 0);
	assert(builderTracker->factoryId == 0);

	for (std::list<Factory>::iterator i = Factories.begin(); i != Factories.end(); i++) {
		CUNIT* u = ai->GetUnit(i->id);

		// don't assist hubs (or factories that cannot be assisted)
		if ((u->def())->canBeAssisted && !u->isHub()) {
			float totalBuilderCost = 0.0f;

			// HACK: get the sum of the heuristic costs of every
			// builder that is already assisting this factory
			for (std::list<int>::iterator j = i->supportbuilders.begin(); j != i->supportbuilders.end(); j++) {
				if ((ai->GetUnit(*j)->def())->isCommander) {
					continue;
				}

				totalBuilderCost += ai->math->GetUnitCost(*j);
			}

			// if this sum is less than the heuristic cost of the
			// factory itself, add the builder to this factory
			//
			// this is based purely on the metal and energy costs
			// of all involved parties, and silently expects that
			// building _another_ factory would always be better
			// than assisting it further
			if (totalBuilderCost < (ai->math->GetUnitCost(i->id) * BUILDERFACTORYCOSTRATIO * 2.5f)) {
				builderTracker->factoryId = i->id;
				i->supportbuilders.push_back(builderTracker->builderID);
				i->supportBuilderTrackers.push_back(builderTracker);
				ai->GetUnit(builderTracker->builderID)->Guard(i->id);
				return true;
			}
		}
	}

	return false;
}
Exemplo n.º 4
0
bool CUnitHandler::BuildTaskAddBuilder(int builderID, UnitCategory category) {
	assert(category < CAT_LAST);
	assert(builderID >= 0);
	assert(ai->GetUnit(builderID) != NULL);

	CUNIT* u = ai->GetUnit(builderID);
	BuilderTracker* builderTracker = GetBuilderTracker(builderID);

	const UnitDef* builderDef = ai->cb->GetUnitDef(builderID);
	const int frame = ai->cb->GetCurrentFrame();

	// make sure this builder is free
	const bool b1 = (builderTracker->taskPlanId == 0);
	const bool b2 = (builderTracker->buildTaskId == 0);
	const bool b3 = (builderTracker->factoryId == 0);
	const bool b4 = builderDef->canAssist;
	const bool b5 = (category == CAT_FACTORY && frame >= 18000);

	if (!b1 || !b2 || !b3 || !b4) {
		if (b5) {
			// note that FactoryBuilderAdd() asserts b1 through b4
			// immediately after BuildTaskAddBuilder() is tried and
			// fails in BuildUp(), so at least those must be true
			// (and so should b5 in most of the *A mods)

			std::stringstream msg;
				msg << "[CUnitHandler::BuildTaskAddBuilder()][frame=" << frame << "]\n";
				msg << "\tbuilder " << builderID << " not able to be added to CAT_FACTORY build-task\n";
				msg << "\tb1: " << b1 << ", b2: " << b2 << ", b3: " << b3;
				msg << ", b4: " << b4 << ", b5: " << b5;
			ai->GetLogger()->Log(msg.str());
		}

		return false;
	}

	// see if there are any BuildTasks that it can join
	if (BuildTasks[category].size() > 0) {
		float largestTime = 0.0f;
		std::list<BuildTask>::iterator task;
		std::list<BuildTask>::iterator bestTask;

		for (task = BuildTasks[category].begin(); task != BuildTasks[category].end(); task++) {
			float buildTime = ai->math->ETT(*task) - ai->math->ETA(builderID, ai->cb->GetUnitPos(task->id));

			if (buildTime > largestTime) {
				largestTime = buildTime;
				bestTask = task;
			}
		}

		if (largestTime > 0.0f) {
			BuildTaskAddBuilder(&*bestTask, builderTracker);
			u->Repair(bestTask->id);
			return true;
		}
	}

	// see if there any joinable TaskPlans
	if (TaskPlans[category].size() > 0) {
		float largestTime = 0.0f;
		std::list<TaskPlan>::iterator plan;
		std::list<TaskPlan>::iterator bestPlan;

		for (plan = TaskPlans[category].begin(); plan != TaskPlans[category].end(); plan++) {
			float buildTime = (plan->def->buildTime / plan->currentBuildPower) - ai->math->ETA(builderID, plan->pos);

			// must test if this builder can make this unit/building too
			if (buildTime > largestTime) {
				const std::vector<int>* canBuildList = &ai->ut->unitTypes[builderDef->id].canBuildList;
				const int buildListSize = canBuildList->size();

				for (int j = 0; j < buildListSize; j++) {
					if (canBuildList->at(j) == plan->def->id) {
						largestTime = buildTime;
						bestPlan = plan;
						break;
					}
				}
			}
		}

		if (largestTime > 10.0f) {
			assert(builderID >= 0);

			// bad, CUNIT::Build() uses TaskPlanCreate()
			// should we really give build orders here?
			// return u->Build(bestPlan->pos, bestPlan->def, -1);
			// TaskPlanCreate(builderID, bestPlan->pos, bestPlan->def);
			return true;
		}
	}

	if (b5) {
		std::stringstream msg;
			msg << "[CUnitHandler::BuildTaskAddBuilder()][frame=" << frame << "]\n";
			msg << "\tno joinable CAT_FACTORY build-tasks or task-plans for builder " << builderID;
		ai->GetLogger()->Log(msg.str());
	}

	return false;
}
Exemplo n.º 5
0
bool CUnitHandler::FactoryBuilderAdd(int builderID) {
	CUNIT* unit = ai->GetUnit(builderID);
	BuilderTracker* builderTracker = GetBuilderTracker(builderID);
	return ((unit->def()->canAssist) && FactoryBuilderAdd(builderTracker));
}
void CAttackHandler::UpdateAir() {
	if (airUnits.size() == 0) return;
	/*
	if enemy is dead, attacking = false
	every blue moon, do an air raid on most valuable thingy
	*/
	assert(!(airIsAttacking && airTarget == -1));
	if (airIsAttacking &&  (airUnits.size() == 0 || ai->cheat->GetUnitDef(airTarget) == NULL)) {
		airTarget = -1;
		airIsAttacking = false;
	}
	if (ai->cb->GetCurrentFrame() % (60*30*5) == 0  //5 mins
			|| (ai->cb->GetCurrentFrame() % (30*30) == 0 && airUnits.size() > 8)) { //30 secs && 8+ units
		//L("AH: trying to attack with air units.");
		int numOfEnemies = ai->cheat->GetEnemyUnits(unitArray);
		int bestID = -1;
		float bestFound = -1.0;
		for (int i = 0; i < numOfEnemies; i++) {
			int enemy = unitArray[i];
			if (enemy != -1 && ai->cheat->GetUnitDef(enemy) != NULL && ai->cheat->GetUnitDef(enemy)->metalCost > bestFound) {
				bestID = enemy;
				bestFound = ai->cheat->GetUnitDef(enemy)->metalCost;
			}
		}
		//L("AH: selected the enemy: " << bestID);
		if (bestID != -1 && ai->cheat->GetUnitDef(bestID)) {
			//give the order
			for (list<int>::iterator it = airUnits.begin(); it != airUnits.end(); it++) {
				CUNIT* u = ai->MyUnits[*it];
				u->Attack(bestID);
			}
			airIsAttacking = true;
			airTarget = bestID;
			ai->cb->SendTextMsg("AH: air group is attacking", 0);
		}
	}

	//TODO: units currently being built. (while the others are off attacking)

	if (ai->cb->GetCurrentFrame() % 1800 == 0) {
		airPatrolOrdersGiven = false;
	}

	if (!airPatrolOrdersGiven && !airIsAttacking) {
		//L("AH: updating air patrol routes");
//		assert(false);
		//get / make up some outer base perimeter points
		vector<float3> outerMeans;
		const int num = 3;
		outerMeans.reserve(num);
		if (kMeansK > 1) {
			int counter = 0;
			counter += kMeansK / 8; //offsetting the outermost one
			for (int i = 0; i < num; i++) {
				outerMeans.push_back(kMeansBase[counter]);
				if (counter < kMeansK-1) counter++;
			}
		} else {
			//theres just 1 kmeans and we need three
			for (int i = 0; i < num; i++) {
				outerMeans.push_back(kMeansBase[0] + float3(250*i, 0, 0));
			}
		}
		assert(outerMeans.size() == num);
		//give the patrol orders to the outer means
		for (list<int>::iterator it = airUnits.begin(); it != airUnits.end(); it++) {
			CUNIT* u = ai->MyUnits[*it];
			u->Move(outerMeans[0] + float3(0,50,0)); //do this first in case theyre in the enemy base
			for (int i = 0; i < num; i++) {
				u->PatrolShift(outerMeans[i]);
			}
		}
		airPatrolOrdersGiven = true;
	}
}
bool CAttackHandler::UnitReadyFilter(int unit) {
	CUNIT u = *ai->MyUnits[unit];
	bool result = u.def() != NULL && !ai->cb->UnitBeingBuilt(unit) && ai->cb->GetUnitHealth(unit) > ai->cb->GetUnitMaxHealth(unit)*0.8f;
	return result;
}
bool CAttackHandler::UnitGroundAttackFilter(int unit) {
	CUNIT u = *ai->MyUnits[unit];
	bool result = u.def() != NULL && u.def()->canmove && u.category() == CAT_G_ATTACK;
	return result;
}
void CAttackHandler::UpdateKMeans() {
	const int arrowDuration = 300;
	{//want local variable definitions
		//get positions of all friendly units and put them in a vector (completed buildings only)
		int numFriendlies = 0;
		vector<float3> friendlyPositions;
		int friendlies[MAXUNITS];
		numFriendlies = ai->cb->GetFriendlyUnits(friendlies);
		for (int i = 0; i < numFriendlies; i++) {
			int unit = friendlies[i];
			CUNIT* u = ai->MyUnits[unit];
			//its a building, it has hp, and its mine (0)
			if (this->UnitBuildingFilter(u->def()) && this->UnitReadyFilter(unit) && u->owner() == 0) {
				friendlyPositions.push_back(u->pos());
			}
		}
		//hack to make it at least 1 unit, should only happen when you have no base:
		if (friendlyPositions.size() < 1) {
			//it has to be a proper position, unless there are no proper positions.
			if (numFriendlies > 0 && ai->cb->GetUnitDef(friendlies[0]) && ai->MyUnits[friendlies[0]]->owner() == 0) friendlyPositions.push_back(ai->cb->GetUnitPos(friendlies[0]));
			else friendlyPositions.push_back(float3(RANDINT % (ai->cb->GetMapWidth()*8), 1000, RANDINT % (ai->cb->GetMapHeight()*8))); //when everything is dead
		}
		//calculate a new K. change the formula to adjust max K, needs to be 1 minimum.
		this->kMeansK = int(min((float)(KMEANS_BASE_MAX_K), 1.0f + sqrtf((float)numFriendlies+0.01f)));
		//iterate k-means algo over these positions and move the means
		this->kMeansBase = KMeansIteration(this->kMeansBase, friendlyPositions, this->kMeansK);
		//now, draw these means on the map
		int lineWidth = 25;
		int lineWidth2 = 3;
//		for (int i = 0; i < kMeansK; i++) {
//			//ai->cb->CreateLineFigure(kMeansBase[i]+float3(0,400,0), kMeansBase[i], lineWidth, 1, arrowDuration, RANDINT % 59549847);
//		}
	}
	
	//update enemy position k-means
	//get positions of all enemy units and put them in a vector (completed buildings only)
	int numEnemies = 0;
	vector<float3> enemyPositions;
	int enemies[MAXUNITS];
	numEnemies = ai->cheat->GetEnemyUnits(enemies);
	for (int i = 0; i < numEnemies; i++) {
		const UnitDef *ud = ai->cheat->GetUnitDef(enemies[i]);
		if (this->UnitBuildingFilter(ud)) { // && this->UnitReadyFilter(unit)) {
			enemyPositions.push_back(ai->cheat->GetUnitPos(enemies[i]));
//			//L("AttackHandler debug: added enemy building position for k-means " << i);
		}
	}
	//hack to make it at least 1 unit, should only happen when you have no base:
	if (enemyPositions.size() < 1) {
		//it has to be a proper position, unless there are no proper positions.
		if (numEnemies > 0 && ai->cheat->GetUnitDef(enemies[0])) enemyPositions.push_back(ai->cheat->GetUnitPos(enemies[0]));
		else enemyPositions.push_back(float3(RANDINT % (ai->cb->GetMapWidth()*8), 1000, RANDINT % (ai->cb->GetMapHeight()*8))); //when everything is dead
	}
	//calculate a new K. change the formula to adjust max K, needs to be 1 minimum.
	this->kMeansEnemyK = int(min(float(KMEANS_ENEMY_MAX_K), 1.0f + sqrtf((float)numEnemies+0.01f)));
//		//L("AttackHandler: doing k-means k:" << kMeansK << " numPositions=" << numFriendlies);
	//iterate k-means algo over these positions and move the means
	this->kMeansEnemyBase = KMeansIteration(this->kMeansEnemyBase, enemyPositions, this->kMeansEnemyK);
	//now, draw these means on the map
	int lineWidth = 25;
//	for (int i = 0; i < kMeansEnemyK; i++) {
//		//L("AttackHandler debug: painting k-means for enemy nr " << i);
//		//ai->cb->CreateLineFigure(kMeansEnemyBase[i]+float3(300,300,0), kMeansEnemyBase[i], lineWidth, 1, arrowDuration, (RANDINT % 49584985));
//	}

	//base k-means and enemy base k-means are updated.	
	//approach: add up (max - distance) to enemies
	vector<float> proximity;
	proximity.resize(kMeansK, 0.0000001f);
	const float mapDiagonal = sqrt(pow((float)ai->cb->GetMapHeight()*8,2) + pow((float)ai->cb->GetMapWidth()*8,2) + 1.0f);

	for (int f = 0; f < kMeansK; f++) {
		for (int e = 0; e < kMeansEnemyK; e++) {
			proximity[f] += mapDiagonal - kMeansBase[f].distance2D(kMeansEnemyBase[e]);
		}
	}

	//sort kMeans by the proximity score
	float3 tempPos;
	float temp;
	for (int i = 1; i < kMeansK; i++) { //how many are completed
		for (int j = 0; j < i; j++) { //compare to / switch with
			if (proximity[i] > proximity[j]) { //switch
				tempPos = kMeansBase[i];
				kMeansBase[i] = kMeansBase[j];
				kMeansBase[j] = tempPos;
				temp = proximity[i];
				proximity[i] = proximity[j];
				proximity[j] = temp;
			}
		}
	}
	//okay, so now we have a kMeans list sorted by distance to enemies, 0 being risky and k being safest.

	//now, draw these means on the map
	for (int i = 1; i < kMeansK; i++) {
		//ai->cb->CreateLineFigure(kMeansBase[i-1]+float3(0,100,0), kMeansBase[i]+float3(0,100,0), lineWidth, 1, arrowDuration, (RANDINT % 59549847));
	}
}
Exemplo n.º 10
0
void CAttackGroup::AttackEnemy(int enemySelected, int numUnits, float range, int frameSpread) {
	const float3& enemyPos = ai->ccb->GetUnitPos(ai->unitIDs[enemySelected]);

	assert(CloakedFix(ai->unitIDs[enemySelected]));
	isShooting = true;

	assert(numUnits >= 0);

	for (unsigned int i = 0; i < (unsigned int)numUnits; i++) {
		CUNIT* unit = ai->GetUnit(units[i]);
		const UnitDef* udef = ai->cb->GetUnitDef(unit->uid);

		if (udef == NULL || unit->maneuverCounter-- > 0) {
			// our unit does not exist (?) or is it currently maneuvering
			continue;
		}

		// TODO: add a routine finding best (not just closest) target
		// TODO: in some cases, force-fire on position
		// TODO: add canAttack
		unit->Attack(ai->unitIDs[enemySelected]);

		// TODO: this should be the max-range of the lowest-ranged weapon
		// the unit has assuming you want to rush in with the heavy stuff
		assert(range >= ai->cb->GetUnitMaxRange(unit->uid));

		// SINGLE UNIT MANEUVERING: testing the possibility of retreating to max
		// range if target is too close, EXCEPT FOR FLAMETHROWER-EQUIPPED units
		float3 myPos = ai->cb->GetUnitPos(unit->uid);

		const float maxRange = ai->ut->GetMaxRange(udef);
		const float losDiff = (maxRange - udef->losRadius);
	//	const float myRange = (losDiff > 0.0f)? (maxRange + udef->losRadius) * 0.5f: maxRange;
		const float myRange = (losDiff > 0.0f)? maxRange * 0.75f: maxRange;

		const bool b6 = (myPos.y < (ai->cb->GetElevation(myPos.x, myPos.z) + 25.0f));
		const bool b7 = (myRange - UNIT_MIN_MANEUVER_RANGE_DELTA) > myPos.distance2D(enemyPos);

		if (!udef->canfly || (b6 && b7)) {
			std::vector<float3> tempPath;

			// note 1: we don't need a path, just a position
			// note 2: should avoid other immediate friendly units and/or immediate enemy units + radius
			// maybe include the height parameter in the search? probably not possible
			// doesn't this mean pathing might happen every second? outer limit should harsher than inner
			const float3 unitPos = ai->ccb->GetUnitPos(ai->unitIDs[enemySelected]);


			//!! TODO:
			//!!     for deterministic reproduction, should be able to
			//!!     set the random seed for the RNG's to a fixed value
			ai->pather->FindBestPathToRadius(tempPath, myPos, myRange, unitPos);

			if (!tempPath.empty()) {
				const float3& moveHere = tempPath.back();
				const float dist = myPos.distance2D(moveHere);

				// TODO: Penetrators are now broken
				// is the position between the proposed destination and the
				// enemy higher than the average of mine and his height?
				const float v1 = ((moveHere.y + enemyPos.y) * 0.5f) + UNIT_MAX_MANEUVER_HEIGHT_DIFFERENCE_UP;
				const float v2 = ai->cb->GetElevation((moveHere.x + enemyPos.x) * 0.5f, (moveHere.z + enemyPos.z) * 0.5f);

				if (v1 <= v2) {
					// nope
					continue;
				}

				const float a = float(UNIT_MIN_MANEUVER_TIME) / frameSpread;
				const float b = (dist / unit->def()->speed);
				const float c = ceilf(std::max(a, b));

				// assume the pathfinder returns correct Y values
				// REMEMBER that this will suck for planes
				if (dist > std::max((UNIT_MIN_MANEUVER_RANGE_PERCENTAGE * myRange), float(UNIT_MIN_MANEUVER_DISTANCE))) {
					unit->maneuverCounter = int(c);
					unit->Move(moveHere);
				}
			}
		}
	}
}
Exemplo n.º 11
0
void CAttackGroup::Update()
{
	int frameNr = ai->cb->GetCurrentFrame();

	////L("AG: Update-start. isShooting:" << isShooting << " isMoving:" << isMoving << " frame:" << frameNr << " groupID:" << groupID << " path size:" << pathToTarget.size());

	int numUnits = (int)units.size();
	if(!numUnits) {
		//L("updated an empty AttackGroup!");
		return;
	}
	int frameSpread; // variable used as a varying "constant" for determining how often a part of the update scheme is done

	float3 groupPosition = this->GetGroupPos();
	if(groupPosition == ERRORVECTOR) return;

	//debug line:
	//	//ai->cb->CreateLineFigure(groupPosition + float3(0,100,50), groupPosition + float3(5+(unitCounter*20),100,50),20,0,2,groupID+1500);

	//sets the isShooting variable. - this part of the code checks for nearby enemies and does focus fire/maneuvering
	frameSpread = 30; 
	if (/*myEnemy == -1 && */frameNr % frameSpread == (groupID*4) % frameSpread) {
//		//L("AG: isShooting start");
		this->isShooting = false;
//		int enemies[MAXUNITS];
		//get all enemies within attack range:
		float range = this->highestAttackRange + 100.0f;//(this->highestAttackRange+200.0f)*2.0f;
		int numEnemies = ai->cheat->GetEnemyUnits(unitArray, groupPosition, range);
//		//L("this is the isShooting setting procedure, numEnemies=" << numEnemies << " range checked: " << range << " and the position of the group is (" << groupPosition.x << " " << groupPosition.y << " " << groupPosition.z << ") numUnits:" << numUnits);
		//assert(numEnemies > 0);
		if(numEnemies > 0) {
			////L("this is the isShooting setting procedure, numEnemies=" << numEnemies << " range checked: " << range << " and the position of the group is (" << groupPosition.x << " " << groupPosition.y << " " << groupPosition.z << ") numUnits:" << numUnits);
//			//L("this is the isShooting setting and numEnemies was more than 0 : " << numEnemies);
			//ai->cb->SendTextMsg("attackgroup : Im not sure that this happens, ever", 0);
			//selecting one of the enemies
			int enemySelected = -1;
			float shortestDistanceFound = FLT_MAX;
			float temp;
			//bool closestSoFarIsBuilding = false; shoot units first
			//float3 enemyPos;// = ai->cheat->GetUnitPos(enemies[enemySelected]);
			int xSize = 40;
			int lineGroupID = groupID+5000;
//			int heightOffset = 10;
			for (int i = 0; i < numEnemies; i++) {
				//my range not considered in picking the closest one
				//TODO: is it air? is it cloaked? 
				if (((temp = groupPosition.distance2D(ai->cheat->GetUnitPos(unitArray[i]))) < shortestDistanceFound)
						&& ai->cheat->GetUnitDef(unitArray[i]) != NULL 
						&& CloakedFix(unitArray[i])
						&& !ai->cheat->GetUnitDef(unitArray[i])->canfly) {
					enemySelected = i;
					shortestDistanceFound = temp;
				}
//				enemyPos = ai->cheat->GetUnitPos(unitArray[i]);
//				//ai->cb->CreateLineFigure(enemyPos + float3(-xSize,heightOffset,0), enemyPos + float3(xSize,heightOffset,0), 5, 0, frameSpread/2, 6000+(groupID*6000 + i));
//				//ai->cb->CreateLineFigure(enemyPos + float3(0,heightOffset,-xSize), enemyPos + float3(0,heightOffset,xSize), 5, 0, frameSpread/2, 6000+(groupID*6000 + numEnemies + i));
			}
//			enemyPos = ai->cheat->GetUnitPos(unitArray[enemySelected]);
			//X marks the target
//			//ai->cb->CreateLineFigure(enemyPos + float3(-xSize,heightOffset,-xSize), enemyPos + float3(xSize,heightOffset,xSize), 5, 0, frameSpread, lineGroupID);
//			//ai->cb->CreateLineFigure(enemyPos + float3(-xSize,heightOffset,xSize), enemyPos + float3(xSize,heightOffset,-xSize), 5, 0, frameSpread, lineGroupID);
			//tiny line from there to groupPosition
//			//ai->cb->CreateLineFigure(enemyPos, groupPosition, 5, 0, frameSpread, lineGroupID);

			//for every unit, pathfind to enemy perifery(with personal range-10) then if distance to that last point in the path is < 10 or cost is 0, attack
			// hack: just attack it
			////L("index of the best enemy: " << enemySelected << " its distance: " << shortestDistanceFound << " its id:" << enemies[enemySelected] << " humanName:" << ai->cheat->GetUnitDef(enemies[enemySelected])->humanName);
			//TODO: can enemy id be 0
			//GIVING ATTACK ORDER:
			if (enemySelected != -1) {
				//L("AG:isShooting: YES");
				float3 enemyPos = ai->cheat->GetUnitPos(unitArray[enemySelected]);
				assert (CloakedFix(unitArray[enemySelected]));
				this->isShooting = true;
				for (int i = 0; i < numUnits; i++) {
					int unit = units[i];
					//does our unit exist and its not currently maneuvering	
					if (ai->cb->GetUnitDef(unit) != NULL && ai->MyUnits[unit]->maneuverCounter-- <= 0) {
						//add a routine finding the best(not just closest) target, but for now just fire away
						//TODO: add canattack
						ai->MyUnits[unit]->Attack(unitArray[enemySelected]); //TODO: some cases, force fire on position
						
						//assert(!ai->cheat->GetUnitDef(unitArray[enemySelected])->canfly);

						//TODO: this should be the max range of the weapon the unit has with the lowest range
						//assuming you want to rush in with the heavy stuff, that is
						assert(range >= ai->cb->GetUnitMaxRange(unit));

						//SINGLE UNIT MANEUVERING:
						//testing the possibility of retreating to max range if target is too close
						float3 myPos = ai->cb->GetUnitPos(unit);
						float myRange = this->ai->ut->GetMaxRange(ai->cb->GetUnitDef(unit));
						//is it air, or air thats landed
						if(!ai->cb->GetUnitDef(unit)->canfly || myPos.y < ai->cb->GetElevation(myPos.x, myPos.z) + 25 && (myRange - UNIT_MIN_MANEUVER_RANGE_DELTA) > myPos.distance2D(enemyPos)) {
							bool debug1 = true;
							bool debug2 = false;
//							ai->cb->SendTextMsg("range diff makes it good to maneuver && im not air", 0);
							vector<float3> tempPath;
							//two things: 1. dont need a path, just a position and 2. it should avoid other immediate friendly units and/or immediate enemy units+radius 
							//needs to be a point ON the perifery circle, not INSIDE it - fixed
							//maybe include the height parameter in the search? probably not possible
							//TODO: OBS doesnt this mean pathing might happen every second? outer limit should be more harsh than inner
							float3 unitPos = ai->cheat->GetUnitPos(unitArray[enemySelected]);
							float dist = ai->pather->FindBestPathToRadius(&tempPath, &myPos, myRange, &unitPos);
							if (tempPath.size() > 0) {
								float3 moveHere = tempPath.back();
								dist = myPos.distance2D(moveHere);

								//TODO: penetrators are now broken. kind of.

								//is the position between the proposed destination and the enemy higher than the average of mine and his height?
								bool losHack = ((moveHere.y + enemyPos.y)/2.0f) + UNIT_MAX_MANEUVER_HEIGHT_DIFFERENCE_UP > ai->cb->GetElevation((moveHere.x+enemyPos.x)/2, (moveHere.z+enemyPos.z)/2);
								//im here assuming the pathfinder returns correct Y values
								if (dist > max((UNIT_MIN_MANEUVER_RANGE_PERCENTAGE*myRange), float(UNIT_MIN_MANEUVER_DISTANCE))
									&& losHack) { //(enemyPos.y - moveHere.y) < UNIT_MAX_MANEUVER_HEIGHT_DIFFERENCE_UP) {
									debug2 = true;
									//ai->cb->SendTextMsg("AG: maneuvering", 0);
									//Draw where it would move:
									//ai->cb->CreateLineFigure(myPos, moveHere, 80, 1, frameSpread, RANDINT%1000000);
									ai->MyUnits[unit]->maneuverCounter = int(ceilf(max((float)UNIT_MIN_MANEUVER_TIME/frameSpread, (dist/ai->MyUnits[unit]->def()->speed))));
									//L("AG maneuver debug, maneuverCounter set to " << ai->MyUnits[unit]->maneuverCounter << " and dist:" << dist << " speed:" << ai->MyUnits[unit]->def()->speed << " frameSpread:" << frameSpread << " dist/speed:" << (dist/ai->MyUnits[unit]->def()->speed) << " its a " << ai->MyUnits[unit]->def()->humanName);
									ai->MyUnits[unit]->Move(moveHere);
									//REMEMBER that this will suck for planes (which is why i wont add them to this group)
								}
							}
							if (debug1 && !debug2) {
								//L("AG: maneuver: pathfinder run but path not used");
							}
						} else if (!ai->cb->GetUnitDef(unit)->canfly || myPos.y < ai->cb->GetElevation(myPos.x, myPos.z) + 25){
							//L("AttackGroup: this unit is an air unit: " << ai->cb->GetUnitDef(unit)->humanName);
						}//endif our unit exists && we arent currently maneuvering
					} else {
						////L("isShooting: OUR unit is dead. or something. Shouldnt happen ever. or maneuvering.");
					}
					//if cant attack, stop with the others, assuming someone was able to attack something
				}
			}
		}
//		//L("AG: isShooting end");
	}

	if (frameNr % 30 == 0 && !defending) {
		//drawing the target region:
		//L("AG update: drawing target region (attacking)");
		int heightOffset = 50;
		vector<float3> circleHack;
		float diagonalHack = attackRadius * (2.0f/3.0f);
		circleHack.resize(8, attackPosition);
		circleHack[0] += float3(-diagonalHack,heightOffset,-diagonalHack);
		circleHack[1] += float3(0,heightOffset,-attackRadius);
		circleHack[2] += float3(diagonalHack,heightOffset,-diagonalHack);
		circleHack[3] += float3(attackRadius,heightOffset,0);
		circleHack[4] += float3(diagonalHack,heightOffset,diagonalHack);
		circleHack[5] += float3(0,heightOffset,attackRadius);
		circleHack[6] += float3(-diagonalHack,heightOffset,diagonalHack);
		circleHack[7] += float3(-attackRadius,heightOffset,0);
		for(int i = 0; i < 8; i++) {
			//ai->cb->CreateLineFigure(circleHack[i], circleHack[(i+1)%8], 5, 0, 300, GetGroupID()+6000);
		}
		//from pos to circle center
		//ai->cb->CreateLineFigure(groupPosition+float3(0,heightOffset,0), attackPosition+float3(0,heightOffset,0), 5, 1, 30, GetGroupID()+6000);
		ai->cb->SetFigureColor(GetGroupID() + 6000, 0, 0, 0, 1.0f);
		//L("AG update: done drawing stuff");
	}

	//TODO: if arrived and not isShooting, set defending? already done in ismoving-end

	if (defending && !isShooting && !isMoving && frameNr % 60 == groupID % 60) {
		FindDefenseTarget(groupPosition);
	}

	// GIVE MOVE ORDERS TO UNITS ALONG PATHTOTARGET
	frameSpread = 60;
	if (!isShooting && isMoving && frameNr % frameSpread == (groupID*5) % frameSpread) {
		//L("AG: start of move order part before loop");
		//do the moving
		const int maxStepsAhead = 8;
		int pathMaxIndex = (int)pathToTarget.size()-1;
		int step1 = min(this->pathIterator + maxStepsAhead/2, pathMaxIndex);
		int step2 = min(this->pathIterator + maxStepsAhead, pathMaxIndex);
		//L("AG: picking stuff out of pathToTarget");
		//L("AG: pathtotarget size: " << pathToTarget.size());
		float3 moveToHereFirst = this->pathToTarget[step1];
		float3 moveToHere = this->pathToTarget[step2];
		
		//drawing destination
//		//ai->cb->CreateLineFigure(groupPosition + float3(0,50,0), pathToTarget[pathMaxIndex] + float3(0,50,0),8,1,frameSpread,groupID+1200);
		
		//L("AG: move order still before loop");
		
        //if we aint there yet
		if (groupPosition.distance2D(pathToTarget[pathMaxIndex]) > GROUP_DESTINATION_SLACK) {
			for(int i = 0; i < numUnits;i++) {
				int unit = units[i];
				if (ai->cb->GetUnitDef(unit) != NULL) {
					//TODO: when they are near target, change this so they line up or something while some are here and some arent. attack checker will take over soon.
					//theres also something that should be done with the units in front that are given the same order+shiftorder and skittle around back and forth meanwhile
	//				if (groupPosition.distance2D(ai->cheat->GetUnitPos(myEnemy)) > this->highestAttackRange) {
					//if the single unit aint there yet
					if (ai->cb->GetUnitPos(unit).distance2D(pathToTarget[pathMaxIndex]) > UNIT_DESTINATION_SLACK) {
						ai->MyUnits[unit]->Move(moveToHereFirst);
//						//ai->cb->CreateLineFigure(ai->cb->GetUnitPos(unit) + float3(0,50,0), moveToHereFirst + float3(0,50,0),15,1,10,groupID+50000);
						if (moveToHere != moveToHereFirst) {
							ai->MyUnits[unit]->MoveShift(moveToHere);
//							//ai->cb->CreateLineFigure(moveToHereFirst + float3(0,50,0), moveToHere + float3(0,50,0),15,1,10,groupID+50000);
						}
						//TODO: give a spring group the order instead of each unit
						//draw thin line from unit to groupPos
						//ai->cb->CreateLineFigure(ai->cb->GetUnitPos(unit) + float3(0,50,0), groupPosition + float3(0,50,0),4,0,frameSpread,groupID+500);
					} else {
						AIHCAddMapPoint amp;
						amp.label = "case-sua";
						amp.pos = groupPosition;
//						//ai->cb->HandleCommand(&amp);
					}
				}
			}
			//if group is as close as the pathiterator-indicated target is to the end of the path, increase pathIterator
			
			//L("AG: after move order, about to adjust stuff");

/*			
			float3 endOfPathPos = pathToTarget[pathMaxIndex];
			float groupDistanceToEnemy = groupPosition.distance2D(endOfPathPos);
			float pathIteratorTargetDistanceToEnemy = pathToTarget[pathIterator].distance2D(endOfPathPos);
			if (groupDistanceToEnemy <= pathIteratorTargetDistanceToEnemy) {
				this->pathIterator = min(pathIterator + maxStepsAhead/2, pathMaxIndex);
			}
			
			if (groupDistanceToEnemy > (pathIteratorTargetDistanceToEnemy*3.0f)) {
				//L("AG: path iterator reset bug thing");
				//this->pathIterator = min(pathIterator + maxStepsAhead/2, pathMaxIndex);
				this->pathIterator = 1;
			}
*/
			pathIterator = 0;
			float3 endOfPathPos = pathToTarget[pathMaxIndex];
			float groupDistanceToEnemy = groupPosition.distance2D(endOfPathPos);
			float pathIteratorTargetDistanceToEnemy = pathToTarget[pathIterator].distance2D(endOfPathPos);
			int increment = maxStepsAhead/2;
			while (groupDistanceToEnemy <= pathIteratorTargetDistanceToEnemy && pathIterator < pathMaxIndex) {
				pathIterator = min(pathIterator + increment, pathMaxIndex);
				pathIteratorTargetDistanceToEnemy = pathToTarget[pathIterator].distance2D(endOfPathPos);
			}
			pathIterator = min(pathIterator, pathMaxIndex);
			
		} else {
			//L("AG: group thinks it has arrived at the destination:" << groupID);
//			AIHCAddMapPoint amp;
//			amp.label = "group thinks it has arrived at the destination.";
//			amp.pos = groupPosition;
//			//ai->cb->HandleCommand(&amp);
			this->ClearTarget();
		}
		//L("AG: done updating move stuff (yay)");

	}//endif move
	
	//stuck fix stuff. disabled because the spring one works now and thats not taken into consideration yet.
	frameSpread = 60;
	if (false && isMoving && !isShooting && frameNr % frameSpread == 0) {
		//L("AG: unit stuck checking start");
		//stuck unit counter update. (at a 60 frame modulo)
		//TODO: if one unit has completed the path, it wont be given any new orders, but this will still happen
		//so there might be false positives here :<
		for (vector<int>::iterator it = units.begin(); it != units.end(); it++) {
			int unit = *it;
			if (ai->cb->GetUnitDef(unit)){
				CUNIT *u = ai->MyUnits[unit];
				float distanceMovedSinceLastUpdate = ai->cb->GetUnitPos(unit).distance2D(u->earlierPosition);
				if (distanceMovedSinceLastUpdate < UNIT_STUCK_MOVE_DISTANCE) {
					u->stuckCounter++;
				} else { //it moved so it isnt stuck
					u->stuckCounter = 0;
				}
				u->earlierPosition = u->pos();
				//hack for slight maneuvering around buildings, spring fails at this sometimes
				if (u->stuckCounter == UNIT_STUCK_COUNTER_MANEUVER_LIMIT) {
					//L("AG: attempting unit unstuck manuevering for " << unit << " at pos " << u->pos().x << " " << u->pos().y << " " << u->pos().z);
					AIHCAddMapPoint amp;
					amp.label = "stuck-fix";
					amp.pos = u->pos();
					//ai->cb->HandleCommand(&amp);
					//findbestpath to perifery of circle around mypos, distance 2x resolution or somesuch
					//dont reset counter, that would make it loop manuever attempts.
					vector<float3> tempPath;
					float3 upos = u->pos();
					float dist = ai->pather->FindBestPathToRadius(&tempPath, &upos, UNIT_STUCK_MANEUVER_DISTANCE, &upos); //500 = hack
					//float dist = ai->pather->FindBestPathToRadius(&tempPath, &ai->cb->GetUnitPos(unit), myRange, &ai->cheat->GetUnitPos(enemies[enemySelected]));
					if (tempPath.size() > 0) {
						float3 moveHere = tempPath.back();
						//ai->cb->CreateLineFigure(u->pos(), moveHere, 14, 1, frameSpread, RANDINT%1000000);
						u->Move(moveHere);
					}
				}
			}
		}//endfor unstuck
	}
}