Example #1
0
GlobalResourceMap::GlobalResourceMap(IAICallback* cb, cLogFile* l, GlobalTerrainMap* TM)
	: l(l)
{
//	l = logfile;
	*l<<"\n Loading the Resource-Map ...";

	const int RAI_MaxMetalSites = 1500;
	const int RAI_MaxGeoSites = 500;

	for( int iT=0; iT<2; iT++ )
		RSize[iT] = 0;
	R[0] = new ResourceSite*[RAI_MaxMetalSites];
	R[1] = new ResourceSite*[RAI_MaxGeoSites];
	sector = 0;

	bool UDMetalResources = false;
	bool UDGeoResource = false;
	int udSize=cb->GetNumUnitDefs();
	const UnitDef **udList = new const UnitDef*[udSize];
	cb->GetUnitDefList(udList);
	for( int iud=udSize-1; iud>=0; iud-- )
	{
		if( udList[iud] == 0 ) // Work-Around(Several Mods):  Spring-Version(v0.72b1-0.76b1)
		{
			cb->SendTextMsg("WARNING: Mod Unit Definition Invalid.",0);
			*l<<"\n  WARNING: (unitdef->id="<<iud+1<<") Mod UnitDefList["<<iud<<"] = 0";
			udSize--;
			udList[iud] = udList[udSize];
		}
		else if( udList[iud]->extractsMetal > 0 )
			UDMetalResources = true;
		else if( udList[iud]->needGeo )
			UDGeoResource = true;
		else
		{
			udSize--;
			udList[iud] = udList[udSize];
		}
	}

	if( !UDGeoResource && !UDMetalResources )
	{
		saveResourceFile = false;
		delete [] udList;
		*l<<"\n  No resource units were detected for this mod.";
		return;
	}

	int *fList = NULL;
	int fSize = 0;
	if( UDGeoResource )
	{
		int maxFeatures = cb->GetMaxUnits();
		fList = new int[maxFeatures]; // Feature List
//		int fSize = cb->GetFeatures(fList,maxFeatures); // Crashes:  Spring-Version(v0.75b2-v0.76b1)
		fSize = cb->GetFeatures(fList,maxFeatures,float3(1,1,1),999999); // Crash Work-Around
		if( fSize == maxFeatures )
		{
			cb->SendTextMsg("WARNING: not all features will be searched for Geo Build Options",0);
			*l<<"\nWARNING: not all features will be searched for Geo Build Options";
		}
		for(int i=0; i<fSize; i++)
			if( !cb->GetFeatureDef(fList[i])->geoThermal )
				fList[i] = fList[--fSize];
	}

	relResourceFileName = "cache/" + cRAI::MakeFileSystemCompatible(cb->GetModHumanName());
	relResourceFileName += "-" + IntToString(cb->GetModHash(), "%x");
	relResourceFileName += "-" + cRAI::MakeFileSystemCompatible(cb->GetMapName());
	relResourceFileName += "-" + IntToString(cb->GetMapHash(), "%x");
	relResourceFileName += ".res";

	string resourceFileName_r;
	FILE* resourceFile_r = NULL;
	// get absolute file name
	if (cRAI::LocateFile(cb, relResourceFileName, resourceFileName_r, false)) {
		resourceFile_r = fopen(resourceFileName_r.c_str(), "rb");
	}

	bool useResourceFile = false;
	if( resourceFile_r )
	{
		try
		{
			useResourceFile = true;
			*l<<"\n  Loading Resource-Site Data ...";
			int udL2Size;
			file_read(&udL2Size, resourceFile_r);
			if( udSize != udL2Size )
				useResourceFile = false;
			else
			{	// Checks if the unit-Def list have changed
				int ID;
				for( int i=0; i<udSize; i++ )
				{
					file_read(&ID, resourceFile_r);
					if( udList[i]->id != ID )
					{	// The order or types of definitions have changed
						useResourceFile = false;
						break;
					}
				}
				if( useResourceFile )
				{
					int featureSites;
					file_read(&featureSites, resourceFile_r);
					if( fSize != featureSites )
						useResourceFile = false;
					else
					{	// Checks if the feature resource list has changed
						for( int i=0; i<fSize; i++ )
						{
							file_read(&ID, resourceFile_r);
							if( fList[i] != ID )
							{	// The order or types of features have changed
								useResourceFile = false;
								break;
							}
						}
						if( useResourceFile )
						{	// The actual loading starts here
							typedef pair<ResourceSite*,ResourceSiteDistance> rrPair;
							typedef pair<int,float> ifPair;
							float3 position;
							float distance;
							int featureID;
							int size,dSize;
							int iT2,iR2;
							int optionID;
							for( int iT=0; iT<2; iT++ )
							{
								file_read(&RSize[iT], resourceFile_r);
								for( int iR=0; iR<RSize[iT]; iR++ )
								{
									file_read(&featureID, resourceFile_r);
									file_read(&position, resourceFile_r);
									if( featureID >= 0 )
										R[iT][iR] = new ResourceSite(position,featureID,cb->GetFeatureDef(featureID));
									else
										R[iT][iR] = new ResourceSite(position);
									file_read(&R[iT][iR]->amount, resourceFile_r);
									file_read(&size, resourceFile_r);
									for( int i=0; i<size; i++ )
									{
										file_read(&optionID, resourceFile_r);
										R[iT][iR]->options.insert(optionID);
									}
								}
							}
							for( int iT=0; iT<2; iT++ )
								for( int iR=0; iR<RSize[iT]; iR++ )
								{
									file_read(&size, resourceFile_r);
									for( int i=0; i<size; i++ )
									{
										file_read(&iT2, resourceFile_r);
										file_read(&iR2, resourceFile_r);
										R[iT][iR]->siteDistance.insert(rrPair(R[iT2][iR2],ResourceSiteDistance(0.0)));
										ResourceSiteDistance* RSD = &R[iT][iR]->siteDistance.find(R[iT2][iR2])->second;
										file_read(&RSD->minDistance, resourceFile_r);
										file_read(&RSD->bestPathType, resourceFile_r);
										file_read(&dSize, resourceFile_r);
										for( int i=0; i<dSize; i++ )
										{
											file_read(&optionID, resourceFile_r);
											file_read(&distance, resourceFile_r);
											RSD->distance.insert(ifPair(optionID,distance));
										}
										file_read(&dSize, resourceFile_r);
										for( int i=0; i<dSize; i++ )
										{
											file_read(&position, resourceFile_r);
											RSD->pathDebug.push_back(position);
										}
										if( RSD->bestPathType == -2 )
											RSD->bestDistance = &RSD->minDistance;
										else if( RSD->bestPathType >= 0 )
											RSD->bestDistance = &RSD->distance.find(RSD->bestPathType)->second;
									}
								}
							file_read(&averageMetalSite, resourceFile_r);
							file_read(&isMetalMap, resourceFile_r);
							if( isMetalMap )
							{
								sector = new MetalMapSector[TM->sectorXSize*TM->sectorZSize];
								for( int iS=0; iS<TM->sectorXSize*TM->sectorZSize; iS++ )
								{
									file_read(&sector[iS].isMetalSector, resourceFile_r);
									sector[iS].S = &TM->sector[iS];
								}
							}
						}
					}
				}
			}
			fclose(resourceFile_r);
		}
		catch (const std::exception& ex)
		{
			*l<<"\nERROR: failed reading the resource map: " << ex.what();
		}
		if( !useResourceFile )
			*l<<"\n  A change has been detected in the map/mod, the resource data will be reloaded.";
	}

	// get absolute file name
	if (!cRAI::LocateFile(cb, relResourceFileName, resourceFileName_w, true)) {
		resourceFileName_w = "";
	}

	if( useResourceFile || cb->GetCurrentFrame() == 0 )
	{	// only save the data if it was created at the beginning of a game
		saveResourceFile = true;
		for( int i=0; i<udSize; i++ )
			saveUD.push_back(udList[i]->id);
		for( int i=0; i<fSize; i++ )
			saveF.push_back(fList[i]);
		saveSectorSize = TM->sectorXSize*TM->sectorZSize;
	}
	else
		saveResourceFile = false;

	if( !UDGeoResource )
		*l<<"\n  No geo-resource units were detected for this mod.";
	else if( !useResourceFile )
	{
		*l<<"\n  Finding Geo-Sites ...";
		float3 position;
		float3 buildPosition;
		for(int i=0; i<fSize; i++)
		{
			const FeatureDef *fd = cb->GetFeatureDef(fList[i]);
			if( fd->geoThermal )
			{
				position = cb->GetFeaturePos(fList[i]);
				if( !TM->waterIsHarmful || cb->GetElevation(position.x,position.z) >= 0 )
				{
					ResourceSite *RS = new ResourceSite(position,fList[i],fd);
					for( int iud=0; iud<udSize; iud++ )
					{
						const UnitDef* ud = udList[iud];
						if( ud->needGeo )
						{
							buildPosition = cb->ClosestBuildSite(ud,RS->position,48.0f,0);
							if( cb->CanBuildAt(ud,buildPosition) )
								RS->options.insert(ud->id);
						}
					}
					if( RS->options.empty() )
					{
						*l<<"\n  Energy Resource located at (x"<<RS->position.x<<" z"<<RS->position.z<<" y"<<RS->position.y<<") is unusable in this mod.";
						delete RS;
					}
					else
						R[1][RSize[1]++] = RS;
				}
			}
		}
		delete [] fList;
		*l<<"\n   Geo-Sites Found: "<<RSize[1];
	}
	else
	{
		delete [] fList;
		*l<<"\n   Geo-Sites Loaded: "<<RSize[1];
	}

	if( !UDMetalResources )
	{
		*l<<"\n  No metal-resource units were detected for this mod.";
		isMetalMap = false;
		averageMetalSite = 0.0;
	}
	else if( !useResourceFile )
	{
		*l<<"\n  Determining the available amount of metal ...";
		*l<<"\n   GetMaxMetal(): "<<cb->GetMaxMetal();
		// Calculate the radius distances & ranges
		const float MMExtractorRadius = cb->GetExtractorRadius()/16.0;
		MMExtractorRadiusI = MMExtractorRadius;
		*l<<"\n   Metal-Map Extractor Radius: "<<MMExtractorRadius;
		const int MMRSSize = MMExtractorRadiusI*2+1;
		MMRS = new sMMRadiusSquare*[MMRSSize];
		for( int x=0; x<MMRSSize; x++ )
		{
			MMRS[x] = new sMMRadiusSquare[MMRSSize];
			for(int z=0; z<MMRSSize; z++)
			{
				MMRS[x][z].distance = float3(x,0,z).distance2D(float3(MMExtractorRadiusI,0,MMExtractorRadiusI));
				if( MMRS[x][z].distance <= MMExtractorRadius )
					MMRS[x][z].inRange = true;
				else
					MMRS[x][z].inRange = false;
			}
		}

		if( cb->GetExtractorRadius() <= 70.0 )
		{
			int inRange = 0;
			for(int z=0; z<MMRSSize; z++)
				for(int x=0; x<MMRSSize; x++)
					if( MMRS[x][z].inRange )
						inRange++;
			const float minimalMetalSquare = RAI_MinimalMetalSite/(inRange*cb->GetMaxMetal());
			*l<<"\n   Metal-Squares per Site: "<<inRange;
			*l<<"\n   Minimal Metal-Square Value: "<<minimalMetalSquare;

			sector = new MetalMapSector[TM->sectorXSize*TM->sectorZSize];
			const unsigned char *standardMetalMap = cb->GetMetalMap();
			const int convertStoMM = TM->convertStoP/16; // * for conversion, / for reverse conversion
			const int metalMapXSize = TM->sectorXSize*convertStoMM;
			int uselessSectors = 0;
			int metalSectors = 0;
			float totalMetal = 0.0;
//			*l<<"\n   Sector to Metal-Map Conversion: "<<convertStoMM;
			for(int z=0; z < TM->sectorZSize; z++)
				for(int x=0; x < TM->sectorXSize; x++)
				{
//if( x == 0 ) *l<<"\n ";
					int i=(z*TM->sectorXSize)+x;
					sector[i].S = &TM->sector[i];
					if( sector[i].S->maxElevation < 0 && TM->waterIsHarmful )
						uselessSectors++;
					else
					{
						float sectorMetal = 0.0;
						int iMap = ((z*convertStoMM)*metalMapXSize)+x*convertStoMM;
//*l<<"\t"<<standardMetalMap[iMap];
						for(int zM=0; zM<convertStoMM; zM++)
							for(int xM=0,iM=iMap+zM*metalMapXSize+xM; xM<convertStoMM; xM++,iM=iMap+zM*metalMapXSize+xM )
								if( (float)standardMetalMap[iM] > 2.0*minimalMetalSquare )
								{
									sector[i].percentMetal++;
									sectorMetal += standardMetalMap[iM];
								}
						sector[i].percentMetal *= 100.0/(convertStoMM*convertStoMM);
						if( sector[i].percentMetal >= 75.0 )//&& sector[i].totalMetal > 2*RAI_MinimalMetalSite )
						{
							sector[i].isMetalSector = true;
							metalSectors++;
							totalMetal += sectorMetal;
						}
//*l<<"\t("<<sector[i].totalMetal<<"/"<<sector[i].percentMetal<<"%)";
					}
				}
			*l<<"\n   Metal-Sector Percent: "<<(100.0*metalSectors)/(TM->sectorXSize*TM->sectorZSize-uselessSectors)<<"%";
			if( (100.0*metalSectors)/(TM->sectorXSize*TM->sectorZSize-uselessSectors) > 40.0 ) // 40% of the sectors are metal
			{
				isMetalMap = true;
				*l<<" (Metal-Map Detected)";
				averageMetalSite = (inRange*cb->GetMaxMetal()*totalMetal)/(metalSectors*convertStoMM*convertStoMM);
			}
			else
			{
				isMetalMap = false;
				delete [] sector;
				sector = 0;
			}
		}
		else
			isMetalMap = false;

		if( !isMetalMap )
		{
			*l<<"\n  Finding Metal-Sites ...";
			averageMetalSite = 0.0;
			MMZSize = cb->GetMapHeight()/2;
			MMXSize = cb->GetMapWidth()/2;
			*l<<"\n   Metal-Map Size: "<<MMXSize*MMZSize<<" (x"<<MMXSize<<",z"<<MMZSize<<")";
			const float MBtoBB = 2.0; // Metal-Block to Build-Block, * for Conversion, / for the reverse
			const float MMMinExtractorRadius = sqrt(pow(float(udList[0]->xsize)/MBtoBB,2)+pow(float(udList[0]->zsize)/MBtoBB,2)); // If less then this value then sites could overlap
			*l<<"\n   Minimal Metal-Map Extractor Radius: "<<MMMinExtractorRadius;

			// sorts the list so that the most unique size extractors are first
			int uniqueExtractors = 0;
			for( int iudU=0; iudU<=uniqueExtractors; iudU++ )
			{
//*l<<" "<<iudU;
				int uniqueExtractorIndex=-1;
				for( int iud=iudU; iud<udSize && uniqueExtractorIndex==-1; iud++ )
					if( udList[iud]->extractsMetal > 0 )
					{
						uniqueExtractorIndex = iud;
						for( int iud2=0; iud2<uniqueExtractors; iud2++ )
							if( udList[iud]->xsize == udList[iud2]->xsize && udList[iud]->zsize == udList[iud2]->zsize &&
								udList[iud]->minWaterDepth == udList[iud2]->minWaterDepth && udList[iud]->maxWaterDepth == udList[iud2]->maxWaterDepth )
							{
								uniqueExtractorIndex = -1;
								break;
							}
					}
				if( uniqueExtractorIndex >=0 )
				{
//*l<<" +"<<uniqueExtractorIndex;
					const UnitDef* ud = udList[uniqueExtractors];
					udList[uniqueExtractors] = udList[uniqueExtractorIndex];
					udList[uniqueExtractorIndex] = ud;
					uniqueExtractors++;

					// make udList[0] = the smallest extractor
					for( int i=uniqueExtractors-1; i>0 && udList[i]->xsize*udList[i]->zsize < udList[i-1]->xsize*udList[i-1]->zsize; i-- )
					{
						const UnitDef* ud = udList[i-1];
						udList[i-1] = udList[i];
						udList[i] = ud;
					}
				}
			}
			*l<<"\n   Minimal Unit-Definition Size: (x"<<udList[0]->xsize<<",z"<<udList[0]->zsize<<")";
			*l<<"\n   Unique Extractor Unit-Definitions: "<<uniqueExtractors;

			// Calculate the offsets
			edgeOffset = new int[MMRSSize];
			for( int x=0; x<MMRSSize; x++ )
				for(int z=0; z<MMRSSize; z++)
					if( MMRS[x][z].inRange )
					{
						edgeOffset[x] = MMExtractorRadiusI-z;
						break;
					}

			// Initializing MMS(.metal .assessing .x .z)
			int SMindex;	// temp variable
			float percentMetal = 0.0;
			const unsigned char *StandardMetalMap = cb->GetMetalMap();
			MMS = new sMetalMapSquare*[MMXSize];
			for( int x=0; x<MMXSize; x++ )
			{
//*l<<"\n";
				MMS[x] = new sMetalMapSquare[MMZSize];
				for(int z=0; z<MMZSize; z++)
				{
					SMindex = z*MMXSize + x;
					if( (int)StandardMetalMap[SMindex] > 0 )
					{
						MMS[x][z].metal = (float)StandardMetalMap[SMindex]*cb->GetMaxMetal();
						percentMetal++;
					}
					else
						MMS[x][z].metal = 0.0;
//*l<<MMS[x][z].metal<<"\t";
					MMS[x][z].x = x;
					MMS[x][z].z = z;
					MMS[x][z].assessing = true;
					MMS[x][z].inaccuracy = -1.0;
				}
			}
			percentMetal *= 100.0/(MMXSize*MMZSize);
			*l<<"\n   Percent Metal: "<<percentMetal<<"%";
			bool valueAccuracy = false;
			if( percentMetal < 1.0 )
			{
				valueAccuracy = true;
				*l<<" (metal-site accuracy will be considered important)";
			}

//			double MSTimerTemp = clock();
			// Updates MMS(.totalMetal .assessing)
			FindMMSTotalMetal(0,MMXSize-1,0,MMZSize-1);
//			*l<<"\n    Metal-Site Init FindMMSTotalMetal Loading:\t"<<(clock()-MSTimerTemp)/(double)CLOCKS_PER_SEC<<" seconds";

			// Updates MMS(.assessing), checks for area occupied by Geo-Sites
			if( UDGeoResource )
			{
				// Find the smallest Geo UnitDef
				const UnitDef *ud = 0;
				for( int iud=0; iud<udSize; iud++ )
					if( udList[iud]->needGeo )
						if( ud == 0 || udList[iud]->xsize*udList[iud]->zsize < ud->xsize*ud->zsize )
							ud = udList[iud];

				const int MMtoMP = 16; // Metal-Map to Map-Position, * for conversion, / for the reverse
				int xMin,xMax,zMin,zMax; // temp variables
				for( int iR=0; iR<RSize[1]; iR++ )
				{
					xMin = int(R[1][iR]->position.x)/MMtoMP;
					zMin = int(R[1][iR]->position.z)/MMtoMP;
					SetLimitBoundary(xMin,xMax,ud->xsize/MBtoBB-1,zMin,zMax,ud->zsize/MBtoBB-1);
					for(int z=zMin; z<=zMax; z++)
						for(int x=xMin; x<=xMax; x++)
							if( MMS[x][z].assessing )
								MMS[x][z].assessing = false;
				}
			}

			// Updates MMS(.assessing), checks the elevation and water type
			const float *StandardHeightMap = cb->GetHeightMap();
			const int HeightMapXSize = cb->GetMapWidth();
			const int MMapToHMap = HeightMapXSize/MMXSize;
			MMSAssessingSize = 0;
			float3 position;// temp variable
			for( int x=0; x<MMXSize; x++ ) {
				for(int z=0; z<MMZSize; z++) {
					if( MMS[x][z].assessing ) {
						if( TM->waterIsHarmful && StandardHeightMap[(z*MMapToHMap+MMapToHMap/2)*HeightMapXSize+(x*MMapToHMap+MMapToHMap/2)] < 0 ) {
							MMS[x][z].assessing = false;
						} else {
							MMSAssessingSize++;
						}
					}
				}
			}

			// try cutting it down a little more, if needed
			if( MMSAssessingSize > 250000 )
			{
				*l<<"\n   Assessing "<<MMSAssessingSize<<" possible metal-sites.";
				*l<<"\n    Reducing Assessment ... ";
				for( int x=0; x<MMXSize; x++ ) {
					for(int z=0; z<MMZSize; z++) {
						if( MMS[x][z].assessing && MMS[x][z].totalMetal < 1.75*RAI_MinimalMetalSite ) {
							MMS[x][z].assessing = false;
						}
					}
				}
			}

//			MSTimerTemp = clock();
			// Updates MMS(.assessing), checks if the positions can be built at
			MMSAssessingSize = 0;
			MMSAssessing = new sMetalMapSquare*[MMXSize*MMZSize];
			for( int x=0; x<MMXSize; x++ )
				for(int z=0; z<MMZSize; z++)
					if( MMS[x][z].assessing )
					{	// Long Calculation: (cause: cb->CanBuildAt - probably can't be improved)
						MMS[x][z].assessing = false;
						position = float3(x*16.0+8.0, 0.0, z*16.0+8.0);
						for( int iud=0; iud<uniqueExtractors; iud++ )
							if( udList[iud]->extractsMetal > 0 && cb->CanBuildAt(udList[iud],position) )
							{
								MMS[x][z].assessing = true;
								MMSAssessing[MMSAssessingSize++] = &MMS[x][z];
								break;
							}
					}
//			*l<<"\n    Metal-Site Init CanBuildAt Loading:\t"<<(clock()-MSTimerTemp)/(double)CLOCKS_PER_SEC<<" seconds";
			*l<<"\n   Assessing "<<MMSAssessingSize<<" possible metal-sites.";
//			double MSTimer1 = 0;
//			double MSTimer2 = 0;
//			double MSTimer3 = 0;
			float searchDis = cb->GetExtractorRadius()/2.0;
			if( searchDis < 16.0 )
				searchDis = 16.0; // Needs to be at least this high to work with ClosestBuildSite
			sMetalMapSquare *mms;	// temp variable
			int xMin,xMax,zMin,zMax,xOffset,zOffset; // temp variables
			while( MMSAssessingSize > 0 && RSize[0] < RAI_MaxMetalSites )
			{
//*l<<"\nMMSRemaining.size()="<<MMSRemaining.size();
//				MSTimerTemp = clock();
				// Setting mms as the best metal-site available
				// Sorted by high totalMetal and then by low inaccuracy
				mms = MMSAssessing[0]; // [0] always has assessing = true
				if( valueAccuracy )
				{
					float bestMetal = 0;
					for( int i=0; i<MMSAssessingSize; i++ )
						if( !MMSAssessing[i]->assessing )
							MMSAssessing[i--] = MMSAssessing[--MMSAssessingSize];
						else if( MMSAssessing[i]->totalMetal < 0.5*bestMetal )
						{}	// nothing
						else
						{	// slow calculations
							if( MMSAssessing[i]->totalMetal > bestMetal )
							{
								bestMetal = MMSAssessing[i]->totalMetal;
								if( mms->totalMetal < 0.51*bestMetal )
									mms = MMSAssessing[i];
							}
							if( MMSAssessing[i]->inaccuracy <= 0.0 )
								FindMMSInaccuracy(MMSAssessing[i]->x,MMSAssessing[i]->z);
							if( mms->inaccuracy <= 0.0 )
								FindMMSInaccuracy(mms->x,mms->z);
//							*l<<"\n m="<<MMSAssessing[i]->totalMetal<<" ia="<<MMSAssessing[i]->inaccuracy<<" r="<<MMSAssessing[i]->totalMetal*(MMSAssessing[i]->totalMetal/MMSAssessing[i]->inaccuracy);
							if( MMSAssessing[i]->totalMetal*(MMSAssessing[i]->totalMetal/MMSAssessing[i]->inaccuracy) > mms->totalMetal*(mms->totalMetal/mms->inaccuracy) )
								mms = MMSAssessing[i];
						}
				}
				else
				{
					for( int i=0; i<MMSAssessingSize; i++ )
						if( !MMSAssessing[i]->assessing )
							MMSAssessing[i--] = MMSAssessing[--MMSAssessingSize];
						else if( MMSAssessing[i]->totalMetal > 1.001*mms->totalMetal )
							mms = MMSAssessing[i];
						else if( MMSAssessing[i]->totalMetal < 0.999*mms->totalMetal )
						{}	// nothing
						else
						{	// 0.999-1.001: fixes float rounding errors, cb->GetMaxMetal() was not a whole number
							// inaccuracy can take a long time to calculate so it won't be until it's needed
							if( MMSAssessing[i]->inaccuracy <= 0.0 )
								FindMMSInaccuracy(MMSAssessing[i]->x,MMSAssessing[i]->z);
							if( mms->inaccuracy <= 0.0 )
								FindMMSInaccuracy(mms->x,mms->z);
							if( MMSAssessing[i]->inaccuracy < mms->inaccuracy )//0.999*mms->inaccuracy )
								mms = MMSAssessing[i];
						}
				}
				if( mms->totalMetal < RAI_MinimalMetalSite )
					break;
//				MSTimer1 += clock()-MSTimerTemp;
//				MSTimerTemp = clock();

				// Create the metal-site
				position = float3(mms->x*16.0+8.0, 0.0, mms->z*16.0+8.0);
				ResourceSite *RS = new ResourceSite(position);
				RS->position.y = StandardHeightMap[(mms->z*MMapToHMap+MMapToHMap/2)*HeightMapXSize+(mms->x*MMapToHMap+MMapToHMap/2)];
				RS->amount = mms->totalMetal;
				averageMetalSite += RS->amount;
				for( int iud=0; iud<udSize; iud++ )
					if( udList[iud]->extractsMetal > 0.0 )
					{
						position = cb->ClosestBuildSite(udList[iud],RS->position,searchDis,0);
						if( cb->CanBuildAt(udList[iud],position) && (!TM->waterIsHarmful || position.y >= 0) )
							RS->options.insert(udList[iud]->id);
					}
				R[0][RSize[0]++] = RS;
//				MSTimer2 += clock()-MSTimerTemp;
//				*l<<"\n mms->(x"<<mms->x<<",z"<<mms->z<<")";

				// The Extractor Radius is small enough that Sites will overlap, remove the nearby indexes being assessed
//				if( MMExtractorRadius < MMMinExtractorRadius )
//				{
				SetLimitBoundary((xMin=mms->x),xMax,udList[0]->xsize/MBtoBB -1,(zMin=mms->z),zMax,udList[0]->zsize/MBtoBB -1);
				for(int z=zMin; z<=zMax; z++)
					for(int x=xMin; x<=xMax; x++)
						if( MMS[x][z].assessing )
							MMS[x][z].assessing = false;
//				}

				// Update nearby Metal-Map positions
				SetLimitBoundary((xMin=mms->x),xMax,xOffset,(zMin=mms->z),zMax,zOffset,MMExtractorRadiusI);
				for(int z=zMin,zMMRS=zOffset; z<=zMax; z++,zMMRS++)
					for(int x=xMin,xMMRS=xOffset; x<=xMax; x++,xMMRS++)
						if( MMRS[xMMRS][zMMRS].inRange )
						{
//							*l<<"\n  (x"<<x<<",z"<<z<<") i="<<index<<" MMSRsize="<<MMSRemaining.size();
							MMS[x][z].metal = 0.0;
						}

//				MSTimerTemp = clock();
				// Recalculate the affected MetalMapSquares
				SetLimitBoundary((xMin=mms->x),xMax,xOffset,(zMin=mms->z),zMax,zOffset,2*MMExtractorRadiusI);
				FindMMSTotalMetal(xMin,xMax,zMin,zMax);
//				MSTimer3 += clock()-MSTimerTemp;

				// Ensures that the first element is being assessed
				while( MMSAssessingSize > 0 && !MMSAssessing[0]->assessing )
					MMSAssessing[0] = MMSAssessing[--MMSAssessingSize];
			}
//			*l<<"\n    Metal-Site Search Loading:            "<<MSTimer1/CLOCKS_PER_SEC<<" seconds";
//			*l<<"\n    Metal-Site CanBuildAt Loading:        "<<MSTimer2/CLOCKS_PER_SEC<<" seconds";
//			*l<<"\n    Metal-Site FindMMSTotalMetal Loading: "<<MSTimer3/CLOCKS_PER_SEC<<" seconds";
			delete [] edgeOffset;
			delete [] MMSAssessing;
			for( int x=0; x<MMXSize; x++ )
				delete [] MMS[x];
			delete [] MMS;
			if( RSize[0] > 0 )
			{
				averageMetalSite /= RSize[0];
				*l<<"\n    Minimal Metal-Site Value: "<<RAI_MinimalMetalSite;
			}
		}
		for( int x=0; x<MMRSSize; x++ )
			delete [] MMRS[x];
		delete [] MMRS;
		*l<<"\n    Average Metal-Site Value: "<<averageMetalSite;
		*l<<"\n    Metal-Sites Found: "<<RSize[0];
	}
	else
		*l<<"\n    Metal-Sites Loaded: "<<RSize[0];
	delete [] udList;
/*
	// debugging
	for( int iT=0; iT<2; iT++ )
		for( int iR=0; iR<RSize[iT]; iR++ )
		{
			*l<<"\n    R["<<iT<<"]["<<iR<<"] \tamount: "<<R[iT][iR]->amount<<" \tlocation(x"<<R[iT][iR]->position.x<<",z"<<R[iT][iR]->position.z<<"):";
			*l<<"\t B("<<R[iT][iR]->options.size()<<"):";
			for( set<int>::iterator RS=R[iT][iR]->options.begin(); RS!=R[iT][iR]->options.end(); RS++ )
				*l<<" "<<*RS;
		}
*/
}
Example #2
0
cBuilderPlacement::cBuilderPlacement(IAICallback* callback, cRAI* global)
{
	G=global;
	l=global->l;
	cb=callback;
	*l<<"\nLoading Build-Placement ...";
	const double SecondAlgorithmTimeLimit = 2.5; // seconds per RAI

	//double clockStart = clock();

	if( int(G->Units.size()) == 0 )
		*l<<"\nERROR: G->Units";

	int unitLimit;
	cb->GetValue(AIVAL_UNIT_LIMIT,&unitLimit);
	if( unitLimit > 500 ) // Can't think of a reason to ever have this higher, but resource linking could take really long
		unitLimit = 500;
	float3 StartPosition=cb->GetUnitPos(G->Units.begin()->first);
	int MetalSiteLimit = G->RM->RSize[0];
	if( MetalSiteLimit > unitLimit/6 )
		MetalSiteLimit = unitLimit/6;
	int GeoSiteLimit = G->RM->RSize[1];
	if( GeoSiteLimit > unitLimit/6 )
		GeoSiteLimit = unitLimit/6;
	Resources = new ResourceSiteExt*[MetalSiteLimit+GeoSiteLimit];
	ResourceSize = 0;

	*l<<"\n Metal-Site limit : "<<MetalSiteLimit;
	*l<<"\n Geo-Site limit : "<<GeoSiteLimit;

	// Finding and using the Nearest Metal-Sites
	set<int> Seleted;
	for( int iM=0; iM<MetalSiteLimit && iM<G->RM->RSize[0]; iM++ )
	{
		int iBest=-1;
		float fBest=0.0f;
		for( int iR=0; iR<G->RM->RSize[0]; iR++ )
		{	// Cycles through only metal-sites
			if( Seleted.find(iR) == Seleted.end() )
				if( iBest == -1 || StartPosition.distance2D(G->RM->R[0][iR]->position) < fBest )
				{
					iBest=iR;
					fBest=StartPosition.distance2D(G->RM->R[0][iR]->position);
				}
		}
		Seleted.insert(iBest);
		Resources[ResourceSize++] = new ResourceSiteExt(G->RM->R[0][iBest],cb);
	}

	// Finding and using the Nearest Geo-Sites
	Seleted.clear();
	for( int iG=0; iG<GeoSiteLimit && iG<G->RM->RSize[1]; iG++ )
	{
		int iBest=-1;
		float fBest=0.0f;
		for( int iR=0; iR<G->RM->RSize[1]; iR++ )
		{	// Cycles through only geo-sites
			if( Seleted.find(iR) == Seleted.end() )
				if( iBest == -1 || StartPosition.distance2D(G->RM->R[1][iR]->position) < fBest )
				{
					iBest=iR;
					fBest=StartPosition.distance2D(G->RM->R[1][iR]->position);
				}
		}
		Seleted.insert(iBest);
		Resources[ResourceSize++] = new ResourceSiteExt(G->RM->R[1][iBest],cb);
	}

	// Updating BuildOptions and Resources, some units may have been disabled
	typedef pair<int,ResourceSiteExtBO> irbPair;
	typedef pair<ResourceSite*,ResourceSiteDistance> rrPair;
	for( int iR=0; iR<ResourceSize; iR++ )
	{
		if( Resources[iR]->S->type == 0 )
		{
			sRAIBuildList *BL = G->UDH->BLMetalL;
			for( int i=0; i<BL->UDefSize; i++ )
			{
				sRAIUnitDef* udr=BL->UDef[i]->RUD;
				if( Resources[iR]->S->options.find(udr->ud->id) != Resources[iR]->S->options.end() )
					Resources[iR]->BuildOptions.insert(irbPair(udr->ud->id,ResourceSiteExtBO(udr)));
			}
		}
		else if( Resources[iR]->S->type == 1 )
		{
			// Units using Geo-Sites can be found in any number of build lists
			for( map<int,sRAIUnitDef>::iterator iUD=G->UDH->UDR.begin(); iUD!=G->UDH->UDR.end(); ++iUD )
			{
				if( iUD->second.ud->needGeo && !iUD->second.Disabled && int(iUD->second.PrerequisiteOptions.size()) > 0 )
				{
					if( Resources[iR]->S->options.find(iUD->second.ud->id) != Resources[iR]->S->options.end() )
						Resources[iR]->BuildOptions.insert(irbPair(iUD->second.ud->id,ResourceSiteExtBO(&iUD->second)));
				}
			}
		}

		if( int(Resources[iR]->S->siteDistance.size()) == 0 || int(Resources[iR]->S->siteDistance.size()) < G->RM->RSize[0]+G->RM->RSize[1] )
		{
			// Cheap distance calculations, although this is still too much to calculate for all map resources
			for( int iR2=0; iR2<ResourceSize; iR2++ )
				if( Resources[iR]->S->siteDistance.find(Resources[iR2]->S) == Resources[iR]->S->siteDistance.end() )
					Resources[iR]->S->siteDistance.insert(rrPair(Resources[iR2]->S,ResourceSiteDistance(Resources[iR]->S->position.distance2D(Resources[iR2]->S->position))));
		}

		// Old Code, Unneeded?  Caused by: the old RAI style's handling of a Metal-Map
		if( int(Resources[iR]->BuildOptions.size()) == 0 )
		{
			float3* p = &Resources[iR]->S->position;
			*l<<"\nERROR: "<<(Resources[iR]->S->type==0?"Metal":"Energy")<<" Resource located at (x"<<p->x;
			*l<<" z"<<p->z<<" y"<<p->y<<") can not be built at.";
			*l<<" size="<<Resources[iR]->S->options.size();
			delete Resources[iR];
			Resources[iR] = Resources[ResourceSize-1];
			ResourceSize--;
			iR--;
		}
		else
			RSRemaining.insert(irPair(iR,Resources[iR]));
	}
	*l<<"\n Resources in use : "<<ResourceSize;

	if( ResourceSize == 0 )
		return;

	// Read all movetypes
	int arraySize = G->TM->mobileType.size();
	basicArray<TerrainMapMobileType*> MobileTypes(arraySize);
	for( list<TerrainMapMobileType>::iterator iM=G->TM->mobileType.begin(); iM!=G->TM->mobileType.end(); ++iM )
		MobileTypes.push_back(&*iM);

	// Sort them by worst to best, remove unusable movetypes
	for( int iM=0; iM<MobileTypes.size()-1; iM++)
	{
		if( (*MobileTypes[iM])->areaLargest == 0 )
		{
			MobileTypes.removeE(iM);
			iM--;
		}
		else if( (*MobileTypes[iM+1])->areaLargest == 0 )
		{
			MobileTypes.removeE(iM+1);
			iM--;
		}
		else if( (*MobileTypes[iM])->areaLargest->percentOfMap > (*MobileTypes[iM+1])->areaLargest->percentOfMap ||
				((*MobileTypes[iM])->areaLargest->percentOfMap == (*MobileTypes[iM+1])->areaLargest->percentOfMap && (*MobileTypes[iM])->maxSlope > (*MobileTypes[iM+1])->maxSlope) )
		{
//*l<<"\n  MT("<<(*MobileTypes[iM])->MD->name<<" "<<iM<<"<->"<<iM+1<<" "<<(*MobileTypes[iM+1])->MD->name<<")";
			MobileTypes.swap(iM,iM+1);
			iM--;
			if( iM != -1 )
				iM --;
		}
	}

	TerrainMapMobileType* MT=NULL;
//	for( MobileTypes.begin(); MobileTypes.nextE(MT); )
//		*l<<"\n  MoveType="<<MT->MD->name<<" pathType="<<MT->MD->pathType;

	// First Link Algorithm
	double linkStartClock = clock();
	TerrainMapArea* Area;
	for( int iR1=0; iR1<ResourceSize; iR1++ )
		for( int iR2=iR1+1; iR2<ResourceSize; iR2++ )
			for( MobileTypes.begin(); MobileTypes.nextE(MT); )
				if( (Area = MT->sector[G->TM->GetSectorIndex(Resources[iR1]->S->position)].area) == 0 ||
					Area != MT->sector[G->TM->GetSectorIndex(Resources[iR2]->S->position)].area )
				{
					Resources[iR1]->S->siteDistance.find(Resources[iR2]->S)->second.distance.insert(ifPair(MT->MD->pathType,-2.0));
					Resources[iR2]->S->siteDistance.find(Resources[iR1]->S)->second.distance.insert(ifPair(MT->MD->pathType,-2.0));
				}
	set<int> RSList1; // sites that are so far connected
	set<int> RSList2; // sites that have yet to be connected
	RSList1.insert(0);
	for( int i=1; i<ResourceSize; i++ )
		RSList2.insert(i);
	int iM = 0; // current moveType index
	while( int(RSList2.size()) > 0 )
	{
		if( iM >= MobileTypes.size() )
			iM = -1; // None of the moveTypes worked, just ignore terrain this time around
		int bestI1;
		int bestI2;
		int pathType;
		float dis; // temp
		bestI1 = -1;
		if( iM == -1 )
			pathType = -1;
		else
		{
			MT = (*MobileTypes[iM]);
			pathType = MT->MD->pathType;
		}
//*l<<"\n RSL2size="<<RSList2.size()<<" iM="<<iM;
		for( set<int>::iterator iR1=RSList1.begin(); iR1!=RSList1.end(); ++iR1 )
			if( iM == -1 || MT->sector[G->TM->GetSectorIndex(Resources[*iR1]->S->position)].area != 0 )
			{
//*l<<" r1="<<*iR1;
				for( set<int>::iterator iR2=RSList2.begin(); iR2!=RSList2.end(); ++iR2 )
				{
//*l<<" r2="<<*iR2;
					dis = Resources[*iR1]->S->GetResourceDistance(Resources[*iR2]->S,pathType);
					if( dis > 0 && (bestI1 == -1 || Resources[bestI1]->S->GetResourceDistance(Resources[bestI2]->S,pathType) > dis) )
					{
						bestI1=*iR1;
						bestI2=*iR2;
					}
				}
			}
		if( bestI1 == -1 )
		{	// Nothing selected, try again with a different moveType
//*l<<"m";
			iM++;
		}
		else if( iM >= 0 && Resources[bestI1]->S->siteDistance.find(Resources[bestI2]->S)->second.bestDistance == 0 )
		{	// The best choice distance has yet to be calculated
//*l<<"r";
			FindResourceDistance(Resources[bestI1]->S,Resources[bestI2]->S,MT->MD->pathType);
		}
		else
		{
//*l<<"s";
			RSList1.insert(bestI2);
			RSList2.erase(bestI2);
			Resources[bestI1]->Linked.insert(irPair(bestI2,Resources[bestI2]));
			Resources[bestI2]->Linked.insert(irPair(bestI1,Resources[bestI1]));
			if( iM == -1 )
			{
				Resources[bestI1]->S->siteDistance.find(Resources[bestI2]->S)->second.bestDistance = &Resources[bestI1]->S->siteDistance.find(Resources[bestI2]->S)->second.minDistance;
				Resources[bestI2]->S->siteDistance.find(Resources[bestI1]->S)->second.bestDistance = &Resources[bestI2]->S->siteDistance.find(Resources[bestI1]->S)->second.minDistance;
			}
			iM = 0;
		}
	}
	*l<<"\n First Link Algorithm Loading Time: "<<(clock()-linkStartClock)/CLOCKS_PER_SEC<<"s";

	// Second Link Algorithm
	linkStartClock = clock();
	float *MTPenalty; // the moveData*->pathType is the index to MTPenalty
	int MTPenaltySize = 0;
	for( MobileTypes.begin(); MobileTypes.nextE(MT); )
	{
		if( MTPenaltySize < MT->MD->pathType+1 )
			MTPenaltySize = MT->MD->pathType+1;
	}
	MTPenalty = new float[MTPenaltySize+1]; // the last index is for the flying movetype
	for( int iMP=0; iMP<MTPenaltySize; iMP++)
		MTPenalty[iMP] = -1.0;
	int linkCount = 0;
	for( MobileTypes.begin(); MobileTypes.nextE(MT); )
//	for( vector<TerrainMapMobileType*>::iterator iMT=MobileTypes.begin(); iMT!=MobileTypes.end(); iMT++ )
	{
		MTPenalty[MT->MD->pathType] = linkCount;

		for( int iR=0; iR<ResourceSize; iR++ )
			for(map<int,ResourceSiteExt*>::iterator iRL = Resources[iR]->Linked.begin(); iRL != Resources[iR]->Linked.end(); ++iRL )
				if( iRL->second->S->siteDistance.find(Resources[iR]->S)->second.bestPathType == MT->MD->pathType )
					linkCount++;
	}
	for( int iMP=0; iMP<MTPenaltySize; iMP++)
		if( MTPenalty[iMP] >= 0.0 )
		{
//*l<<"\n "<<MTPenalty[iMP]<<"/"<<linkCount;
			MTPenalty[iMP] = linkCount/(1+linkCount*1.10-MTPenalty[iMP]);
			if( MTPenalty[iMP] < 1.0 )
				MTPenalty[iMP] = 1.0;
			else if( MTPenalty[iMP] > 5.0 )
				MTPenalty[iMP] = 5.0;
//*l<<" MTPenalty["<<iMP<<"]="<<MTPenalty[iMP];
		}
	MTPenalty[MTPenaltySize] = 20.0;

	int **RMT = new int*[ResourceSize]; // Resource MoveType, 2 dimensional array
	float **RD = new float*[ResourceSize]; // Resource Distances, Local Copy, 2 dimensional array
	ResourceSiteDistance* RSD;
	for( int iR1=0; iR1<ResourceSize; iR1++ )
	{
		RMT[iR1] = new int[ResourceSize];
		RD[iR1] = new float[ResourceSize];
		for( int iR2=0; iR2<ResourceSize; iR2++ )
			RD[iR1][iR2] = -1.0;
	}
	for( int iR1=0; iR1<ResourceSize; iR1++ )
		for( int iR2=iR1+1; iR2<ResourceSize; iR2++ )
		{
			RMT[iR1][iR2] = MTPenaltySize;
			RMT[iR2][iR1] = MTPenaltySize;
			RSD = &Resources[iR1]->S->siteDistance.find(Resources[iR2]->S)->second;
			if( RSD->bestDistance != 0 )
			{
				if( RSD->bestPathType >= 0 )
				{
					RMT[iR1][iR2] = RSD->bestPathType;
					RMT[iR2][iR1] = RSD->bestPathType;
				}
			}
			else
				for( MobileTypes.begin(); MobileTypes.nextE(MT); )
					if( RSD->distance.find(MT->MD->pathType) == RSD->distance.end() )
					{
						RMT[iR1][iR2] = MT->MD->pathType;
						RMT[iR2][iR1] = MT->MD->pathType;
						break;
					}
		}

	basicArray<ResourceLinkInfo> RLIList(ResourceSize);
	ResourceLinkInfo *RLI;
	for( int iR=0; iR<ResourceSize; iR++ )
		if( Resources[iR]->Linked.size() < 3 )
		{
			RLI = RLIList.push_back();
			RLI->index = iR;
			RLI->bestI = -1;
			RLI->restrictedR.insert(iR);
			for(map<int,ResourceSiteExt*>::iterator iRL = Resources[iR]->Linked.begin(); iRL != Resources[iR]->Linked.end(); ++iRL )
				RLI->restrictedR.insert(iRL->first);
		}
//	*l<<"\n   Second Link Loading Time 0: "<<(clock()-linkStartClock)/(double)CLOCKS_PER_SEC<<"s";

//	double linkClock1 = 0;
//	double linkClock2 = 0;
//	double linkClock3 = 0;
//	double linkClock4 = 0;
//	double linkClockTemp;
	while( RLIList.elementSize > 0 && (clock()-linkStartClock)/(double)CLOCKS_PER_SEC <= SecondAlgorithmTimeLimit )
	{
//*l<<"\n";
		// Cycle through RLIList to find the closest resource pair available (with consideration to other factors)
		bool resetLink,acceptLink;
		ResourceLinkInfo *bestRLI = 0;
//		linkClockTemp = clock();
		for( RLIList.begin(); RLIList.nextE(RLI); )
		{
			if( RLI->bestI == -2 ) // The best link was found & rejected in latter code
				RLIList.removeE();
			else
			{
				if( RLI->bestI == -1 ) // has not been found yet
				{
					for( int iR2=0; iR2<ResourceSize; iR2++ )
						if( RLI->restrictedR.find(iR2) == RLI->restrictedR.end() )
						{
//*l<<"\n  r1="<<RLI->index<<" r2="<<iR2;
							if( RD[RLI->index][iR2] < 0.0 )
							{
								RSD = &Resources[RLI->index]->S->siteDistance.find(Resources[iR2]->S)->second;
//*l<<" BPT:"<<RSD->bestPathType;
								RD[RLI->index][iR2] = Resources[RLI->index]->S->GetResourceDistance(Resources[iR2]->S,RSD->bestPathType);
//*l<<" *:"<<RD[RLI->index][iR2];
								RD[RLI->index][iR2] *= MTPenalty[RMT[RLI->index][iR2]];
								RD[iR2][RLI->index] = RD[RLI->index][iR2];
							}
//*l<<" d(1-2):"<<RD[RLI->index][iR2];

							if( RLI->bestI < 0 || RD[RLI->index][iR2] < RD[RLI->index][RLI->bestI] )
							{
								acceptLink = true;
								for(map<int,ResourceSiteExt*>::iterator iRL1 = Resources[RLI->index]->Linked.begin(); iRL1 != Resources[RLI->index]->Linked.end(); ++iRL1 )
								{
									RSD = &Resources[iR2]->S->siteDistance.find(iRL1->second->S)->second;
									if( RSD->bestDistance != 0 )
									{
										if( RD[iR2][iRL1->first] < 0.0 )
										{
											RD[iR2][iRL1->first] = Resources[iR2]->S->GetResourceDistance(iRL1->second->S,RSD->bestPathType) * MTPenalty[RMT[iR2][iRL1->first]];
											RD[iRL1->first][iR2] = RD[iR2][iRL1->first];
										}
										if( RD[RLI->index][iRL1->first] < 0.0 )
										{
											RD[RLI->index][iRL1->first] = Resources[RLI->index]->S->GetResourceDistance(iRL1->second->S,Resources[RLI->index]->S->siteDistance.find(iRL1->second->S)->second.bestPathType) * MTPenalty[RMT[RLI->index][iRL1->first]];
											RD[iRL1->first][RLI->index] = RD[RLI->index][iRL1->first];
										}
//*l<<" d(L1-2):"<<RD[iR2][iRL1->first];
//*l<<"+"<<0.35*RD[RLI->index][iRL1->first];
										if( RD[iR2][iRL1->first]+0.35*RD[RLI->index][iRL1->first] < RD[RLI->index][iR2] )
										{
//*l<<" rejected("<<iRL1->first<<")";
											acceptLink = false;
											break;
										}
									}
								}

								if( acceptLink )
								{
//*l<<" *acceptLink*";
									RLI->bestI = iR2;
								}
							}
						}
				}
//if( RLI->bestI >= 0 ) *l<<"\n  dis["<<RLI->index<<"]["<<RLI->bestI<<"]="<<RD[RLI->index][RLI->bestI];
				if( RLI->bestI == -1 )
					RLIList.removeE();
				else if( bestRLI == 0 || RD[RLI->index][RLI->bestI] < RD[bestRLI->index][bestRLI->bestI] )
					bestRLI = RLI;
			}
		}
//		linkClock1 += clock()-linkClockTemp;

		if( bestRLI == 0 )
			break; // the loop would have ended anyway, this just jumps over the next area of code

//		linkClockTemp = clock();
		resetLink = false;
		RSD = &Resources[bestRLI->index]->S->siteDistance.find(Resources[bestRLI->bestI]->S)->second;
		if( RSD->bestDistance == 0 )
		{	// The best choice distance has yet to be calculated
//*l<<" r-1-2";
			for( MobileTypes.begin(); RSD->bestDistance == 0 && MobileTypes.nextE(MT); )
				if( RSD->distance.find(MT->MD->pathType) == RSD->distance.end() )
				{
					FindResourceDistance(Resources[bestRLI->index]->S,Resources[bestRLI->bestI]->S,MT->MD->pathType);
					RMT[bestRLI->index][bestRLI->bestI] = RSD->bestPathType;
					RMT[bestRLI->bestI][bestRLI->index] = RSD->bestPathType;
				}
			if( RSD->bestDistance == 0 )
			{
				RSD->bestDistance = &RSD->minDistance;
				RSD = &Resources[bestRLI->bestI]->S->siteDistance.find(Resources[bestRLI->index]->S)->second;
				RSD->bestDistance = &RSD->minDistance;
				RMT[bestRLI->index][bestRLI->bestI] = MTPenaltySize;
				RMT[bestRLI->bestI][bestRLI->index] = MTPenaltySize;
			}
			RD[bestRLI->index][bestRLI->bestI] = -1.0;
			RD[bestRLI->bestI][bestRLI->index] = -1.0;
			resetLink = true;
		}
//		linkClock2 += clock()-linkClockTemp;
//		linkClockTemp = clock();
		for(map<int,ResourceSiteExt*>::iterator iRL1 = Resources[bestRLI->index]->Linked.begin(); iRL1 != Resources[bestRLI->index]->Linked.end(); ++iRL1 )
		{
			RSD = &iRL1->second->S->siteDistance.find(Resources[bestRLI->bestI]->S)->second;
			if( RSD->bestDistance == 0 )
			{	// An additionally needed set of calculations
//*l<<" r-L1-2";
				for( MobileTypes.begin(); RSD->bestDistance == 0 && MobileTypes.nextE(MT); )
					if( RSD->distance.find(MT->MD->pathType) == RSD->distance.end() )
					{
						FindResourceDistance(iRL1->second->S,Resources[bestRLI->bestI]->S,MT->MD->pathType);
						RMT[iRL1->first][bestRLI->bestI] = RSD->bestPathType;
						RMT[bestRLI->bestI][iRL1->first] = RSD->bestPathType;
					}
				if( RSD->bestDistance == 0 )
				{
					RSD->bestDistance = &RSD->minDistance;
					RSD = &Resources[bestRLI->bestI]->S->siteDistance.find(iRL1->second->S)->second;
					RSD->bestDistance = &RSD->minDistance;
					RMT[iRL1->first][bestRLI->bestI] = MTPenaltySize;
					RMT[bestRLI->bestI][iRL1->first] = MTPenaltySize;
				}
				RD[iRL1->first][bestRLI->bestI] = -1.0;
				RD[bestRLI->bestI][iRL1->first] = -1.0;
				resetLink = true;
			}
		}
//		linkClock3 += clock()-linkClockTemp;
//		linkClockTemp = clock();
		if( resetLink )
		{	// New calculations have been made, distances should be reconsidered
			for( RLIList.begin(); RLIList.nextE(RLI); )
				if( (RLI->index == bestRLI->bestI && bestRLI->restrictedR.find(RLI->bestI) != bestRLI->restrictedR.end() ) ||
					(Resources[bestRLI->index]->Linked.find(RLI->index) != Resources[bestRLI->index]->Linked.end() && RLI->bestI == bestRLI->bestI) )
					RLI->bestI = -1;
			bestRLI->bestI = -1;
			bestRLI = 0;
		}
		else
		{
//*l<<" size1:"<<Resources[bestRLI->index]->Linked.size();
			// These are an additional sets of conditions that are not checked until the best has been found
			for(map<int,ResourceSiteExt*>::iterator iRL1 = Resources[bestRLI->index]->Linked.begin(); iRL1 != Resources[bestRLI->index]->Linked.end(); ++iRL1 )
			{
//*l<<" d(L1-B):"<<RD[bestRLI->bestI][iRL1->first];
				if( RD[bestRLI->bestI][iRL1->first] < RD[bestRLI->bestI][bestRLI->index] )
				{	// The best choose didn't work out
//*l<<" *no good*";
					bestRLI->bestI = -2; // this will end the search for this paticular resource
					bestRLI = 0;
					break;
				}
			}
			if( bestRLI != 0 )
			{
//*l<<" size2:"<<Resources[bestRLI->bestI]->Linked.size();
				for(map<int,ResourceSiteExt*>::iterator iRLB = Resources[bestRLI->bestI]->Linked.begin(); iRLB != Resources[bestRLI->bestI]->Linked.end(); ++iRLB )
				{
					RSD = &Resources[bestRLI->index]->S->siteDistance.find(iRLB->second->S)->second;
					if( RSD->bestDistance == 0 )
					{	// An additionally needed set of calculations
						for( MobileTypes.begin(); RSD->bestDistance == 0 && MobileTypes.nextE(MT); )
							if( RSD->distance.find(MT->MD->pathType) == RSD->distance.end() )
							{
								FindResourceDistance(Resources[bestRLI->index]->S,iRLB->second->S,MT->MD->pathType);
								RMT[bestRLI->index][iRLB->first] = RSD->bestPathType;
								RMT[iRLB->first][bestRLI->index] = RSD->bestPathType;
							}
						if( RSD->bestDistance == 0 )
						{
							RSD->bestDistance = &RSD->minDistance;
							RSD = &iRLB->second->S->siteDistance.find(Resources[bestRLI->index]->S)->second;
							RSD->bestDistance = &RSD->minDistance;
							RMT[bestRLI->index][iRLB->first] = MTPenaltySize;
							RMT[iRLB->first][bestRLI->index] = MTPenaltySize;
						}
						RD[bestRLI->index][iRLB->first] = -1.0;
						RD[iRLB->first][bestRLI->index] = -1.0;
					}
					if( RD[bestRLI->index][iRLB->first] < 0.0 )
					{
						RD[bestRLI->index][iRLB->first] = Resources[bestRLI->index]->S->GetResourceDistance(iRLB->second->S,RSD->bestPathType) * MTPenalty[RMT[bestRLI->index][iRLB->first]];
						RD[iRLB->first][bestRLI->index] = RD[bestRLI->index][iRLB->first];
					}
//*l<<" d(LB-1):"<<RD[bestRLI->index][iRLB->first];
					if( RD[bestRLI->index][iRLB->first] < RD[bestRLI->index][bestRLI->bestI] )
					{
//						float3 Pos1=Resources[bestRLI->index]->S->position;
//						float3 Pos2=Resources[bestRLI->bestI]->S->position;
//						Pos1.y+=515.0;
//						Pos2.y+=755.0;
//						cb->CreateLineFigure(Pos1,Pos2,30,0,900000,0);
//*l<<" *no good*";
						// The best choose didn't work out
						bestRLI->bestI = -2; // this will end the search for this paticular resource
						bestRLI = 0;
						break;
					}
				}
			}
			if( bestRLI != 0 )
			{
				RSD = &Resources[bestRLI->index]->S->siteDistance.find(Resources[bestRLI->bestI]->S)->second;
//*l<<"\n*Best* R("<<bestRLI->index<<"<->"<<bestRLI->bestI<<") dis=";
//*l<<Resources[bestRLI->index]->S->GetResourceDistance(Resources[bestRLI->bestI]->S,RSD->bestPathType);
//*l<<" p="<<MTPenalty[RMT[bestRLI->index][bestRLI->bestI]];
//*l<<" d="<<RD[bestRLI->bestI][bestRLI->index];
//*l<<" d="<<RD[bestRLI->index][bestRLI->bestI];
				if( RAIDEBUGGING && cb->GetMyTeam() == 0 ) // Debug Lines
				{
					vector<float3> *path = &RSD->pathDebug;
					if( path->empty() )
					{
						path->push_back(Resources[bestRLI->index]->S->position);
						path->push_back(Resources[bestRLI->bestI]->S->position);
					}
					for( size_t i=0; i+1 < path->size(); i++ )
					{
						float3 Pos1=path->at(i);
						float3 Pos2=path->at(i+1);
						Pos1.y+=45.0;
						if( i == 0 )
							Pos1.y+=35.0;
						Pos2.y+=45.0;
						if( i == path->size()-2 )
							Pos2.y-=75.0;
						cb->CreateLineFigure(Pos1,Pos2,30,0,900000,0);
					}
				}

				Resources[bestRLI->index]->Linked.insert(irPair(bestRLI->bestI,Resources[bestRLI->bestI]));
				Resources[bestRLI->bestI]->Linked.insert(irPair(bestRLI->index,Resources[bestRLI->index]));
				for( RLIList.begin(); RLIList.nextE(RLI); )
					if( RLI->restrictedR.find(bestRLI->bestI) != RLI->restrictedR.end() )
					{
						RLI->bestI = -1;
						if( RLI->index == bestRLI->bestI )
							RLI->restrictedR.insert(bestRLI->index);
					}
				bestRLI->restrictedR.insert(bestRLI->bestI);
				bestRLI->bestI = -1;
				for( RLIList.begin(); RLIList.nextE(RLI); )
					if( Resources[RLI->index]->Linked.size() >= 3 )
						RLIList.removeE();
				bestRLI = 0;
			}
		}
//		linkClock4 += clock()-linkClockTemp;
	}
	if( RLIList.elementSize > 0 )
		*l<<"\n Second Linking Algorithm Aborted (was running for more than "<<SecondAlgorithmTimeLimit<<" seconds)";

	for( int iR=0; iR<ResourceSize; iR++ )
	{
		delete [] RMT[iR];
		delete [] RD[iR];
	}
	delete [] RMT;
	delete [] RD;
	delete [] MTPenalty;
//	*l<<"\n   Second Link Loading Time 1: "<<linkClock1/(double)CLOCKS_PER_SEC<<"s";
//	*l<<"\n   Second Link Loading Time 2: "<<linkClock2/(double)CLOCKS_PER_SEC<<"s";
//	*l<<"\n   Second Link Loading Time 3: "<<linkClock3/(double)CLOCKS_PER_SEC<<"s";
//	*l<<"\n   Second Link Loading Time 4: "<<linkClock4/(double)CLOCKS_PER_SEC<<"s";
	*l<<"\n   Second Link Algorithm Loading Time: "<<(clock()-linkStartClock)/CLOCKS_PER_SEC<<"s";

	// Setting Linked Distance 2
	for( int iR=0; iR<ResourceSize; iR++ )
	{
		for( map<int,ResourceSiteExt*>::iterator iRL=Resources[iR]->Linked.begin(); iRL!=Resources[iR]->Linked.end(); ++iRL )
		{
			if( Resources[iR]->LinkedD2.find(iRL->first) == Resources[iR]->LinkedD2.end() )
				Resources[iR]->LinkedD2.insert(irPair(iRL->first,Resources[iRL->first]));
			for( map<int,ResourceSiteExt*>::iterator iRL2=Resources[iRL->first]->Linked.begin(); iRL2!=Resources[iRL->first]->Linked.end(); ++iRL2 )
			{
				if( iRL2->first != iR && Resources[iR]->LinkedD2.find(iRL2->first) == Resources[iR]->LinkedD2.end() )
					Resources[iR]->LinkedD2.insert(irPair(iRL2->first,Resources[iRL2->first]));
			}
		}
	}

	for( int iR=0; iR<ResourceSize; iR++ )
		Resources[iR]->SetRanged();

	// Debug Lines
	if( RAIDEBUGGING && cb->GetMyTeam() == 0 )
		for( int iR1=0; iR1<ResourceSize; iR1++ )
		{
			for( int iR2=iR1+1; iR2<ResourceSize; iR2++ )
			{
				if( Resources[iR1]->Linked.find(iR2) != Resources[iR1]->Linked.end() )
				{
					vector<float3> *path = &Resources[iR1]->S->siteDistance.find(Resources[iR2]->S)->second.pathDebug;
					if( path->empty() )
					{
						path->push_back(Resources[iR1]->S->position);
						path->push_back(Resources[iR2]->S->position);
					}
					for( int i=0; i<int(path->size())-1; i++ )
					{
						float3 Pos1=path->at(i);
						float3 Pos2=path->at(i+1);
						Pos1.y+=15.0;
						Pos2.y+=15.0;
						cb->CreateLineFigure(Pos1,Pos2,10,0,900000,0);
					}
				}
			}

			*l<<"\n R("<<iR1<<") type="<<Resources[iR1]->S->type<<" Pos(x"<<Resources[iR1]->S->position.x<<",z"<<Resources[iR1]->S->position.z<<") L("<<Resources[iR1]->Linked.size()<<"):";
			for( map<int,ResourceSiteExt*>::iterator RL=Resources[iR1]->Linked.begin(); RL!=Resources[iR1]->Linked.end(); ++RL )
				*l<<" "<<RL->first;
			*l<<"\t B("<<Resources[iR1]->BuildOptions.size()<<"):";
			for( map<int,ResourceSiteExtBO>::iterator RS=Resources[iR1]->BuildOptions.begin(); RS!=Resources[iR1]->BuildOptions.end(); ++RS )
				*l<<" "<<RS->first;
		}
}