Exemple #1
0
void CEconomy::addUnitOnCreated(CUnit& unit) {
	unitCategory c = unit.type->cats;
	if ((c&MEXTRACTOR).any()) {
		CGroup *group = requestGroup();
		group->addUnit(unit);
		takenMexes[group->key] = group->pos();
		CUnit *builder = ai->unittable->getUnit(group->firstUnit()->builtBy);
		if (builder) {
			assert(group->key != builder->group->key);
			takenMexes.erase(builder->group->key);
		}
	}
	else if(unit.type->def->needGeo) {
		CGroup *group = requestGroup();
		group->addUnit(unit);
		takenGeo[group->key] = group->pos();	
		CUnit *builder = ai->unittable->getUnit(group->firstUnit()->builtBy);
		if (builder) {
			assert(group->key != builder->group->key);
			takenGeo.erase(builder->group->key);
		}
	}
}
Exemple #2
0
void CMilitary::update(int frame) {
	int busyScoutGroups = 0;
	std::vector<int> keys;
	std::vector<int> occupied;
	std::map<int, bool> isOccupied;
	std::map<int, ATask*>::iterator itTask;
	std::map<MilitaryGroupBehaviour, std::map<int, CGroup*>* >::iterator itGroup;
	TargetsFilter tf;

	// NOTE: we store occupied targets in two formats because vector is used
	// for list of targets (which can be used if no suitable primary
	// targets are found), second one is used for TargetFilter to filter out 
	// occupied targets when searching for primary targets
	for (itTask = ai->tasks->activeTasks[TASK_ATTACK].begin(); itTask != ai->tasks->activeTasks[TASK_ATTACK].end(); ++itTask) {
		AttackTask *task = (AttackTask*)itTask->second;
		occupied.push_back(task->target);
		isOccupied[task->target] = true;
	}
	
	for(itGroup = groups.begin(); itGroup != groups.end(); itGroup++) {
		int target = -2;
		MilitaryGroupBehaviour behaviour = itGroup->first;
		
		// setup common target filter params per behaviour...
		tf.reset();
		switch(behaviour) {
			case SCOUT:
				tf.threatRadius = 300.0f;
				tf.threatFactor = 1000.0f;
				break;
			case ENGAGE:
				tf.threatFactor = 0.001f;
				break;
			case BOMBER:
				tf.threatFactor = 100.0f;
				// TODO: replace constant with maneuvering radius of plane?
				tf.threatRadius = 1000.0f;
				break;
			case AIRFIGHTER:
				tf.threatFactor = 100.0f;
				break;
		}

		// NOTE: start with random group ID because some groups can't reach the 
		// target (e.g. Fleas); this helps to overcome the problem when there 
		// is a target, but first group can't reach it, and AI constantly 
		// trying to add same task again and again which leads to attack stall
		keys.clear();
		util::GetShuffledKeys<int, CGroup*>(keys, *(itGroup->second));
		
		const std::vector<CategoryMatcher>& targetBlocks = ai->intel->targets[behaviour];

		for (int i = 0; i < keys.size(); ++i) {
			CGroup *group = (*(itGroup->second))[keys[i]];

			// if group is busy, don't bother...
			if (group->busy || !group->canPerformTasks()) {
				if (group->busy) {
					if (behaviour == SCOUT)
						busyScoutGroups++;
				
    				if (drawTasks)
    					visualizeTasks(group);
				}

				continue;
			}

			// NOTE: each group can have different score on the same target
			// because of their disposition, strength etc.
			tf.scoreCeiling = std::numeric_limits<float>::max();
			tf.excludeId = &isOccupied;

			// setup custom target filter params per current group...
			switch(behaviour) {
				case SCOUT:
					if ((group->cats&AIR).any())
						tf.threatCeiling = 1.1f;
					else
						tf.threatCeiling = (std::max<float>((float)MAX_SCOUTS_IN_GROUP / group->units.size(), 1.0f)) * group->strength - EPS;
					break;
				case BOMBER:
					tf.threatCeiling = group->strength + group->firstUnit()->type->dps;
					break;
			}

			// basic target selection...
			if (target != -1) {
				for(int b = 0; b < targetBlocks.size(); b++) {
					target = group->selectTarget(ai->intel->enemies.getUnits(targetBlocks[b]), tf);
				}
			}

			bool isAssembling = isAssemblingGroup(group);
			
			if ((!isAssembling && behaviour == SCOUT) || target < 0) {
				// scan for better target among existing targets...
				tf.excludeId = NULL;
				int assistTarget = group->selectTarget(occupied, tf);
				if (assistTarget >= 0 && assistTarget != target) {
					ATask *task = ai->tasks->getTaskByTarget(assistTarget);
					if (task) {
						bool canAssist = false;
						int assisters = task->assisters.size();
						float cumulativeStrength = task->firstGroup()->strength;
						std::list<ATask*>::iterator itATask;
						for (itATask = task->assisters.begin(); itATask != task->assisters.end(); itATask++) {
							cumulativeStrength += (*itATask)->firstGroup()->strength;
						}

						switch(behaviour) {
							case SCOUT:
								canAssist = assisters == 0;
								break;
							case ENGAGE: 
								canAssist = cumulativeStrength < 2.0f * tf.threatValue;
								break;
							case BOMBER:
								canAssist = assisters < 9;
								break;
							case AIRFIGHTER:
								canAssist = assisters < 3;
								break;
						}
						
						if (canAssist) {
							mergeGroups.erase(group->key);
							
							if (!ai->tasks->addTask(new AssistTask(ai, *task, *group)))
								group->addBadTarget(assistTarget);
							break;
						}
					} 
				}
			}
			
			bool isStrongEnough = true;
			
			if (target >= 0) {
				isStrongEnough = group->strength >= (tf.threatValue - EPS);
				bool isSizeEnough = (behaviour == ENGAGE) ? group->units.size() >= ai->cfgparser->getMinGroupSize(group->techlvl) : true;

				if (behaviour != ENGAGE)
					isAssembling = false;
				
				if ((isAssembling && isSizeEnough) || (!isAssembling && isStrongEnough)) {
					ATask::NPriority taskPriority = (behaviour == BOMBER || behaviour == AIRFIGHTER) ? ATask::HIGH : ATask::NORMAL;
					mergeGroups.erase(group->key);
					if (ai->tasks->addTask(new AttackTask(ai, target, *group), taskPriority)) {
						occupied.push_back(target);
						isOccupied[target] = true;
					}
					else {
						group->addBadTarget(target);
					}
					break;
				}
			}

			bool bMerge = !(isStrongEnough || isAssembling);
			
			switch(behaviour) {
				case SCOUT:
					bMerge = bMerge && activeScoutGroups.size() > 1 && group->units.size() < MAX_SCOUTS_IN_GROUP;
					break;
				default:
					bMerge = bMerge && groups[behaviour]->size() > 1;
			}
				
			if (bMerge)
				mergeGroups[group->key] = group;
		}
	}

	/* Merge the groups that were not strong enough */
	if (mergeGroups.size() >= 2) {
		std::list<CGroup*> merge;
		for (std::map<int, CGroup*>::iterator base = mergeGroups.begin(); base != mergeGroups.end(); ++base) {
			if (!base->second->busy) {
				for (std::map<int,CGroup*>::iterator compare = mergeGroups.begin(); compare != mergeGroups.end(); ++compare) {
					if (!compare->second->busy && base->first != compare->first) {
						if (base->second->canMerge(compare->second)) {
							bool canMerge = false;
							
							if ((base->second->cats&SCOUTER).any())
								// TODO: replace MERGE_DISTANCE with ETA?
								canMerge = (base->second->pos().distance2D(compare->second->pos()) < MERGE_DISTANCE);
							else
								canMerge = true;

							if (canMerge) {
								if (merge.empty())
									merge.push_back(base->second);
								merge.push_back(compare->second);
								break;
							}
						}
					}
				}
				
				if (!merge.empty()) {
					ai->tasks->addTask(new MergeTask(ai, merge));
					merge.clear();
					break;
				}
			}
		}

		// remove busy (merging) groups...
		std::map<int, CGroup*>::iterator it = mergeGroups.begin();
		while(it != mergeGroups.end()) {
			int key = it->first; ++it;
			if (mergeGroups[key]->busy)
				mergeGroups.erase(key);
		}
	}

	//bool gotAirFactory = ai->unittable->gotFactory(AIRCRAFT);
	//bool gotSeaFactory = (ai->unittable->gotFactory(NAVAL) || ai->unittable->gotFactory(HOVER));
	
	if (ai->difficulty == DIFFICULTY_HARD) {
		// when all scouts are busy create some more...
		
		// FIXME: when scouts are stucked AI will not build them anymore,
		// while there are scout targets available
		
		if (busyScoutGroups == activeScoutGroups.size()) {
			//unitCategory baseType = ai->gamemap->IsWaterMap() && gotSeaFactory ? SEA|SUB : LAND;
			Wish::NPriority p = activeScoutGroups.size() < ai->cfgparser->getMinScouts() ? Wish::HIGH: Wish::NORMAL;

			//if(gotAirFactory && rng.RandFloat() > 0.66f)
			//	baseType = AIR;
			ai->wishlist->push(MOBILE | SCOUTER | allowedEnvCats, 0, p);
		} 
	}

	// TODO: build units on real need only, not always
	ai->wishlist->push(requestUnit(allowedEnvCats), 0, Wish::NORMAL);
	
	/*
	if (gotAirFactory && rng.RandFloat() > 0.66f) {
		ai->wishlist->push(requestUnit(AIR), forbiddenCats);
	}
	else {
		if (ai->gamemap->IsWaterMap() && gotSeaFactory)
			ai->wishlist->push(requestUnit(SEA|SUB), forbiddenCats, Wish::NORMAL);
		else
			ai->wishlist->push(requestUnit(LAND), forbiddenCats, Wish::NORMAL);
	}
	*/
}
Exemple #3
0
void CEconomy::update() {
	int buildersCount = 0;
	int assistersCount = 0;
	int maxTechLevel = ai->cfgparser->getMaxTechLevel();

	/* See if we can improve our eco by controlling metalmakers */
	controlMetalMakers();

	/* If we are stalling, do something about it */
	preventStalling();

	/* Update idle worker groups */
	std::map<int, CGroup*>::iterator i;
	for (i = activeGroups.begin(); i != activeGroups.end(); ++i) {
		CGroup *group = i->second;
		CUnit *unit = group->firstUnit();

		if ((group->cats&MOBILE).any() && (group->cats&BUILDER).any())
			buildersCount++;
		if ((group->cats&MOBILE).any() && (group->cats&ASSISTER).any() && (group->cats&BUILDER).none())
			assistersCount++;

		if (group->busy || !group->canPerformTasks())
			continue;

		if ((group->cats&FACTORY).any()) {
			ai->tasks->addTask(new FactoryTask(ai, *group));
			continue;
		}
		
		if ((group->cats&STATIC).any() && (group->cats&BUILDER).none()) {
			// we don't have a task for current unit type yet (these types are:
			// MEXTRACTOR & geoplant)
			continue;
		}

		float3 pos = group->pos();

		// NOTE: we're using special algo for commander to prevent
		// it walking outside the base
		if ((unit->type->cats&COMMANDER).any()) {
			tryFixingStall(group);
			if (group->busy) continue;
			
			/* If we don't have a factory, build one */
			if (ai->unittable->factories.empty() || mexceeding) {
				unitCategory factory = getNextTypeToBuild(unit, FACTORY, maxTechLevel);
				if (factory.any())
					buildOrAssist(*group, BUILD_FACTORY, factory);
				if (group->busy) continue;
			}

			/* If we are exceeding and don't have estorage yet, build estorage */
			if (eexceeding && !ai->unittable->factories.empty()) {
				if (ai->unittable->energyStorages.size() < ai->cfgparser->getMaxTechLevel())
					buildOrAssist(*group, BUILD_ESTORAGE, ESTORAGE);
				if (group->busy) continue;
			}
			
			// NOTE: in NOTA only static commanders can build TECH1 factories
			if ((unit->type->cats&STATIC).any()) {
				/* See if this unit can build desired factory */
				unitCategory factory = getNextTypeToBuild(unit, FACTORY, maxTechLevel);
				if (factory.any())
					buildOrAssist(*group, BUILD_FACTORY, factory);
				if (group->busy) continue;

				factory = getNextTypeToBuild(unit, BUILDER|STATIC, maxTechLevel);
				if (factory.any())
					// TODO: invoke BUILD_ASSISTER building algo instead of
					// BUILD_FACTORY to properly place static builders?
					buildOrAssist(*group, BUILD_FACTORY, factory);
				if (group->busy) continue;
			}
			
			// build nearby metal extractors if possible...
			if (mstall && !ai->gamemap->IsMetalMap()) {
				// NOTE: there is a special hack withing buildOrAssist() 
				// to prevent building unnecessary MMAKERS
				buildOrAssist(*group, BUILD_MPROVIDER, MEXTRACTOR);
				if (group->busy) continue;
			}
			
			// see if we can build defense
			tryBuildingDefense(group);
			if (group->busy) continue;
			
			tryAssistingFactory(group);
			if (group->busy) continue;
		}
		else if ((unit->type->cats&BUILDER).any()) {
			tryFixingStall(group);
			if (group->busy) continue;
    		
    		/* See if this unit can build desired factory */
    		unitCategory factory = getNextTypeToBuild(unit, FACTORY, maxTechLevel);
    		if (factory.any())
    			buildOrAssist(*group, BUILD_FACTORY, factory);
    		if (group->busy) continue;
    		
    		/* If we are overflowing energy build an estorage */
    		if (eexceeding && !mRequest) {
    			if (ai->unittable->energyStorages.size() < ai->cfgparser->getMaxTechLevel())
    				buildOrAssist(*group, BUILD_ESTORAGE, ESTORAGE);
    			if (group->busy) continue;
    		}

    		/* If we are overflowing metal build an mstorage */
    		if (mexceeding && !eRequest) {
    			buildOrAssist(*group, BUILD_MSTORAGE, MSTORAGE);
    			if (group->busy) continue;
    		}

    		/* If both requested, see what is required most */
    		if (eRequest && mRequest) {
    			if ((mNow / mStorage) > (eNow / eStorage))
    				buildOrAssist(*group, BUILD_EPROVIDER, EMAKER);
    			else
    				buildOrAssist(*group, BUILD_MPROVIDER, MEXTRACTOR);
    			if (group->busy) continue;
    		}
    		
    		/* See if we can build defense */
    		tryBuildingDefense(group);
    		if (group->busy) continue;
    		
    		tryBuildingAntiNuke(group);
    		if (group->busy) continue;
    		
			tryBuildingJammer(group);
			if (group->busy) continue;
    		
    		tryBuildingStaticAssister(group);
    		if (group->busy) continue;
    		
    		/* Else just provide what is requested */
    		if (eRequest) {
    			buildOrAssist(*group, BUILD_EPROVIDER, EMAKER);
    			if (group->busy) continue;
    		}
    		
    		if (mRequest) {
    			buildOrAssist(*group, BUILD_MPROVIDER, MEXTRACTOR);
    			if (group->busy) continue;
    		}

    		tryAssistingFactory(group);
    		if (group->busy) continue;
    		
    		tryBuildingShield(group);
    		if (group->busy) continue;

    		/* Otherwise just expand */
    		if (!ai->gamemap->IsMetalMap()) {
    			if ((mNow / mStorage) > (eNow / eStorage))
    				buildOrAssist(*group, BUILD_EPROVIDER, EMAKER);
    			else
    				buildOrAssist(*group, BUILD_MPROVIDER, MEXTRACTOR);
    		}
		}
		else if ((unit->type->cats&ASSISTER).any()) {
			// TODO: repair damaged buildings (& non-moving units?)
			// TODO: finish unfinished bulidings
			tryAssist(group, BUILD_IMP_DEFENSE);
			if (group->busy) continue;
    		tryAssistingFactory(group);
    		if (group->busy) continue;
			tryAssist(group, BUILD_AG_DEFENSE);
			if (group->busy) continue;
			tryAssist(group, BUILD_AA_DEFENSE);
			if (group->busy) continue;
			tryAssist(group, BUILD_UW_DEFENSE);
			if (group->busy) continue;
			tryAssist(group, BUILD_MISC_DEFENSE);
			if (group->busy) continue;
		}
	}

	// TODO: consider assistersCount & military groups count for
	// requesting assisters

	if (buildersCount < ai->cfgparser->getMaxWorkers()
	&& (buildersCount < ai->cfgparser->getMinWorkers()))
		ai->wishlist->push(BUILDER, 0, Wish::HIGH);
	else {
		if (buildersCount < ai->cfgparser->getMaxWorkers())
			ai->wishlist->push(BUILDER, 0, Wish::NORMAL);
	}
}
Exemple #4
0
void CEconomy::buildOrAssist(CGroup& group, buildType bt, unitCategory include, unitCategory exclude) {
	ATask* task = canAssist(bt, group);
	if (task != NULL) {
		ai->tasks->addTask(new AssistTask(ai, *task, group));
		return;
	}
	
	if ((group.cats&BUILDER).none())
		return;

	float3 pos = group.pos();
	float3 goal = pos;
	unitCategory catsWhere = canBuildWhere(group.cats);
	unitCategory catsWhereStrict = canBuildWhere(group.cats, true);

	if (bt == BUILD_EPROVIDER) {
		if (!windmap)
			exclude |= WIND;
		if (!worthBuildingTidal)
			exclude |= TIDAL;
	}
	else if (bt == BUILD_MPROVIDER) {
		if ((include&MEXTRACTOR).any()) {
			goal = getClosestOpenMetalSpot(group);
			if (goal != ERRORVECTOR)
				if (goal.y < 0.0f)
					exclude |= (LAND|AIR);
				else
					exclude |= (SEA|SUB);
		}
	}

	const CUnit* unit = group.firstUnit();
	bool isComm = (unit->type->cats&COMMANDER).any();
	std::multimap<float, UnitType*> candidates;
	
	// retrieve the allowed buildable units...
	if ((include&MEXTRACTOR).any())
		ai->unittable->getBuildables(unit->type, include|catsWhereStrict, exclude, candidates);
	else		
		ai->unittable->getBuildables(unit->type, include|catsWhere, exclude, candidates);
	
   	std::multimap<float, UnitType*>::iterator i = candidates.begin();
   	int iterations = candidates.size() / (ai->cfgparser->getTotalStates() - state + 1);
   	bool affordable = false;

	if (!candidates.empty()) {
    	/* Determine which of these we can afford */
    	while(iterations >= 0) {
    		if (canAffordToBuild(unit->type, i->second))
    			affordable = true;
    		else
    			break;

    		if (i == --candidates.end())
    			break;
    		iterations--;
    		i++;
    	}
	}
	else if (bt != BUILD_MPROVIDER) {
		return;
	}
	else {
		goal = ERRORVECTOR;
	}
	
	/* Perform the build */
	switch(bt) {
		case BUILD_EPROVIDER: {
			if (i->second->def->needGeo) {
				goal = getClosestOpenGeoSpot(group);
				if (goal != ERRORVECTOR) {
					// TODO: prevent commander walking?
					if (!eRequest && ai->pathfinder->getETA(group, goal) > 450.0f) {
						goal = ERRORVECTOR;
					}
				}
			}
			else if ((i->second->cats&EBOOSTER).any()) {
				goal = ai->coverage->getNextClosestBuildSite(unit, i->second);
			}

			if (goal == ERRORVECTOR) {
				goal = pos;
				if (i != candidates.begin())
					--i;
				else
					++i;
			}
			
			if (i != candidates.end())
				ai->tasks->addTask(new BuildTask(ai, bt, i->second, group, goal));

			break;
		}

		case BUILD_MPROVIDER: {
			bool canBuildMMaker = (eIncome - eUsage) >= METAL2ENERGY || eexceeding;
			
			if (goal != ERRORVECTOR) {
				bool allowDirectTask = true;
				
				// TODO: there is a flaw in logic because when spot is under
				// threat we anyway send builders there
				
				if (isComm) {
					int numOtherBuilders = ai->unittable->builders.size() - ai->unittable->factories.size();
					// if commander can build mmakers and there is enough
					// ordinary builders then prevent it walking for more than 
					// 20 sec...
					if (numOtherBuilders > 2 && ai->unittable->canBuild(unit->type, MMAKER))
						allowDirectTask = ai->pathfinder->getETA(group, goal) < 600.0f;
				}

				if (allowDirectTask) {
					ai->tasks->addTask(new BuildTask(ai, bt, i->second, group, goal));
				}
				else if (mstall && (isComm || areMMakersEnabled) && canBuildMMaker) {
					UnitType* mmaker = ai->unittable->canBuild(unit->type, MMAKER);
					if (mmaker != NULL)
						ai->tasks->addTask(new BuildTask(ai, bt, mmaker, group, pos));
				}
			}
			else if (mstall && areMMakersEnabled && canBuildMMaker) {
				UnitType* mmaker = ai->unittable->canBuild(unit->type, MMAKER);
				if (mmaker != NULL)
					ai->tasks->addTask(new BuildTask(ai, bt, mmaker, group, pos));
			}
			else if (!eexceeding) {
				buildOrAssist(group, BUILD_EPROVIDER, EMAKER);
			}
			break;
		}

		case BUILD_MSTORAGE: case BUILD_ESTORAGE: {
			/* Start building storage after enough ingame time */
			if (!taskInProgress(bt) && ai->cb->GetCurrentFrame() > 30*60*7) {
				pos = ai->defensematrix->getBestDefendedPos(0);
				ai->tasks->addTask(new BuildTask(ai, bt, i->second, group, pos));
			}
			break;
		}

		case BUILD_FACTORY: {
			if (!taskInProgress(bt)) {
				int numFactories = ai->unittable->factories.size();

				bool build = numFactories <= 0;
				
				// TODO: add some delay before building next factory

				if (!build && affordable && !eRequest) {
					float m = mNow / mStorage;
			
					switch(state) {
						case 0: {
							build = (m > 0.45f);
							break;
						}
						case 1: {
							build = (m > 0.40f);
							break;
						}
						case 2: {
							build = (m > 0.35f);
							break;
						}
						case 3: {
							build = (m > 0.3f);
							break;
						}
						case 4: {
							build = (m > 0.25f);
							break;
						}
						case 5: {
							build = (m > 0.2f);
							break;
						}
						case 6: {
							build = (m > 0.15f);
							break;
						}
						default: {
							build = (m > 0.1f);
						}
					}
				}

				if (build) {
					// TODO: fix getBestDefendedPos() for static builders
					if (numFactories > 1 && (unit->type->cats&MOBILE).any()) {
						goal = ai->coverage->getClosestDefendedPos(pos);
						if (goal == ERRORVECTOR) {
							goal = pos;
						}
					}
					ai->tasks->addTask(new BuildTask(ai, bt, i->second, group, goal));
				}
			}
			break;
		}
		
		case BUILD_IMP_DEFENSE: {
			// NOTE: important defense placement selects important place,
			// not closest one as other BUILD_XX_DEFENSE algoes do
			if (!taskInProgress(bt)) {
				bool allowTask = true;
				
				// TODO: implement in getNextXXXBuildSite() "radius" argument
				// and fill it for static builders
            	goal = ai->coverage->getNextImportantBuildSite(i->second);
				
				allowTask = (goal != ERRORVECTOR);

				if (allowTask && isComm)
					allowTask = ai->pathfinder->getETA(group, goal) < 300.0f;
				
				if (allowTask)
					ai->tasks->addTask(new BuildTask(ai, bt, i->second, group, goal));
			}
			break;
		}
		
		case BUILD_AG_DEFENSE: case BUILD_AA_DEFENSE: case BUILD_UW_DEFENSE: case BUILD_MISC_DEFENSE: {
			if (affordable && !taskInProgress(bt)) {
				bool allowTask = true;
				
				// TODO: implement in getNextBuildSite() "radius" argument
				// and fill it for static builders
				goal = ai->coverage->getNextClosestBuildSite(unit, i->second);
				
				allowTask = (goal != ERRORVECTOR);

				if (allowTask && isComm)
					allowTask = ai->pathfinder->getETA(group, goal) < 300.0f;
				
				if (allowTask)
					ai->tasks->addTask(new BuildTask(ai, bt, i->second, group, goal));
			}
			break;
		}

		default: {
			if (affordable && !taskInProgress(bt))
				ai->tasks->addTask(new BuildTask(ai, bt, i->second, group, goal));
			break;
		}
	}
}