Teach a noob how to do things with SDK.

Yup, but it seems like something you could calculate once for each turn and store on the CvPlot. Each AI who's at war with the plot's owner will end up valuing its destruction equally, but that's probably acceptable as a first pass.
 
Actually the current AI doesn't care about what improvement is on a plot when deciding where to airbomb, it only cares about what bonus is on the plot. It doesn't check to ensure that the improvement on the tile even connects the bonus, or that the owner of the plot can even see that bonus, or that there are roads to connect it.

But adding in a check to see if removing the road would break a traderoute wouldn't be terribly difficult actually. And may be a worthwhile addition.
 
But adding in a check to see if removing the road would break a traderoute wouldn't be terribly difficult actually. And may be a worthwhile addition.

How exactly could that be done? Where is it defined what plot the AI will bomb?
This is just a guess but could the bool CvUnitAI::AI_airBombPlots() in CvUnitAI.cpp do it?
Spoiler :
Code:
bool CvUnitAI::AI_airBombPlots()
{
	//PROFILE_FUNC();

	CvUnit* pInterceptor;
	CvPlot* pLoopPlot;
	CvPlot* pBestPlot;
	int iSearchRange;
	int iInterceptProb;
	int iValue;
	int iBestValue;
	int iDX, iDY;

	iSearchRange = airRange();

	iBestValue = 0;
	pBestPlot = NULL;

	for (iDX = -(iSearchRange); iDX <= iSearchRange; iDX++)
	{
		for (iDY = -(iSearchRange); iDY <= iSearchRange; iDY++)
		{
			pLoopPlot	= plotXY(getX_INLINE(), getY_INLINE(), iDX, iDY);

			if (pLoopPlot != NULL)
			{
				if (!pLoopPlot->isCity() && pLoopPlot->isOwned() && pLoopPlot != plot())
				{
					if (canAirBombAt(plot(), pLoopPlot->getX_INLINE(), pLoopPlot->getY_INLINE()))
					{
						iValue = 0;

						if (pLoopPlot->getBonusType(pLoopPlot->getTeam()) != NO_BONUS)
						{
							iValue += AI_pillageValue(pLoopPlot, 15);

							iValue += GC.getGameINLINE().getSorenRandNum(10, "AI Air Bomb");
						}
						else if (isSuicide())
						{
							//This should only be reached when the unit is desperate to die
							iValue += AI_pillageValue(pLoopPlot);
							// Guided missiles lean towards destroying resource-producing tiles as opposed to improvements like Towns
							if (pLoopPlot->getBonusType(pLoopPlot->getTeam()) != NO_BONUS)
							{
								//and even more so if it's a resource
								iValue += GET_PLAYER(pLoopPlot->getOwnerINLINE()).AI_bonusVal(pLoopPlot->getBonusType(pLoopPlot->getTeam()));
							}
						}

						if (iValue > 0)
						{

							pInterceptor = bestInterceptor(pLoopPlot);

							if (pInterceptor != NULL)
							{
								iInterceptProb = isSuicide() ? 100 : pInterceptor->currInterceptionProbability();

								iInterceptProb *= std::max(0, (100 - evasionProbability()));
								iInterceptProb /= 100;

								iValue *= std::max(0, 100 - iInterceptProb / 2);
								iValue /= 100;
							}

							if (iValue > iBestValue)
							{
								iBestValue = iValue;
								pBestPlot = pLoopPlot;
								FAssert(!atPlot(pBestPlot));
							}
						}
					}
				}
			}
		}
	}

	if (pBestPlot != NULL)
	{
		getGroup()->pushMission(MISSION_AIRBOMB, pBestPlot->getX_INLINE(), pBestPlot->getY_INLINE());
		return true;
	}

	return false;
}
I have an idea how to check if the plot has a route, but how can I check if removing the road would break the traderoute?
 
You want to search in CvUnitAI for all instances of "canAirBombAt" there are 3 as I recall.

Basically, to decide if a route is worth killing, check if there is a bonus on the plot, if there is, it is worth deletion, unless there is also a river network for the plot, then don't bother.

Next, check all neighbor plots to ensure that this road is the only connection between any adjacent roads, if it is, most likely you break a route.

It isn't FLAWLESS, but it is pretty effective for the majority of the cases where it is worthwhile to break a single road.
 
I'm sorry but I don't get why I have to search for all instances of "canAirBombAt"?

EDIT: Ok, I think I know now why I want to search for "canAirBombAt". But now my problem is that I don't know how to check the tiles around the tile we are checking if we want to bomb it. I think I have to use the loop for it, but I think you know better. I don't actually even know how the loop works and can you even use it inside another loop.
 
That code you posted is exactly what I was talking about. It looks in the search range of the unit to calculate the best plot (if any) to bomb. That is where you would add a check for a route and bump up the value if so.

To check if a tile is part of a linear route, you would loop over the eight adjacent tiles. If exactly 2 of them have routes (this ignores rivers), bombing this tile will break the connection between those two tiles. Whether or not this actually breaks a trade network is a much tougher problem and would kill performance to check.
 
Ok, I managed to get the AI to air bomb routes. I don't know if the code is the best you could get, some might even call it cheating...
I attached some screenshots to make you to believe. But still could someone tell me more about the loops so I could improve the code?
 
Yes, you can place loops inside loops. Here is a sample of looping all neighbors.

Code:
		for (iI = 0; iI < NUM_DIRECTION_TYPES; iI++)
		{
			CvPlot* pCheckPlot = plotDirection(pLoopPlot->getX_INLINE(), pLoopPlot->getY_INLINE(), (DirectionTypes)iI);
			if (pCheckPlot != NULL)
			{
				if (pCheckPlot->
 
Something asked for arguments, and you didn't provide any (or it asked for none and you forgot to explicitly pass none)

ie:

CvFoo::doBar(int X, int Y)

Requires that you call it using:

pFoo->doBar(X,Y)

And instead you used:

pFoo->doBar()

Or, possibly you were working with:

CvFoo::doBar()

and you stated:

pFoo->doBar

instead of

pFoo->doBar()

Not sure if the warning for the failure to include the () is worded that way.
 
I found it. :D I was trying to call function isCity
Code:
if (pCheckPlot->isCity)
and forgot the () from the end. Visual C++ is good because it tells you what a function wants you to give it. Unfortunately I don't know what I have to give it. I checked the fuction and it looks like this:
Code:
bool CvPlot::isCity(bool bCheckImprovement, TeamTypes eForTeam) const
{
	if (bCheckImprovement && NO_IMPROVEMENT != getImprovementType())
	{
		if (GC.getImprovementInfo(getImprovementType()).isActsAsCity())
		{
			if (NO_TEAM == eForTeam || (NO_TEAM == getTeam() && GC.getImprovementInfo(getImprovementType()).isOutsideBorders()) || GET_TEAM(eForTeam).isFriendlyTerritory(getTeam()))
			{
				return true;
			}
		}
	}

	return (getPlotCity() != NULL);
}
The first argument seems to be only true or false, but what about the other one? The function seems to check if there is an improvement witch acts as a city. Should I just put the first argument to 0? What arguments should I actually give the function if I want to check only if the plot is city?

EDIT: I searched the real code that I haven't changed and even there was parts with only isCity(), noting inside the (). Why the compiler gives me warning if I do so too? :confused:

EDIT2: Now this is just weird. I built the solution again and got no warnings about it. :crazyeye: Weird... :twitch:

EDIT3: More thoughts. So will the isCity return true if there is fort?
 
If you want it to consider a fort or other improvement that can act as a city as an actual city, pass true for the first argument and the team ID that's asking as the second.
 
If you look at the header (*.h file) for this function, you'll see that it was declared with some variables defined (bool bCheckImprovement = false, TeamTypes eForTeam = NO_TEAM) Anything which has defined values in their header is optional for sending to the function when you call to it. Since both are optional in this case, you can call with 0, 1 or 2 passed variables. They must be in order though, so you cannot specify a team without also answering what boolean value you want first.
 
So if I won't pass any arguments to the function, it should return true only when the plot is real city, because the bCheckImprovement is defined false and it won't pass the first if? Did I get it correct?
 
Actually I did. :p It ended to CvCity* getCity(IDInfo city) in CvGameCoreUtils.cpp. I don't actually even know what the IDs are and do so that's why I asked. Do you know any good tutorial about them?

EDIT: I have started reading your guide. The name sounds good for me. :lol: Mayby next thing I'll do is to add some tags to promotion witch alter the ranged bombard. BTW, phungus420 called this a "modcomp", but I never dreamed of releasing this. Should I do it?
 
The IDs are just a pair of numbers, one to indicate the player, a second as a unique ID for the object (City, unit...)

Honestly, IDInfo doesn't NEED to be understood, you use it in the precise format you just saw and it does the job it is meant to do. But if you are curious about it, IDInfo is a Struct (something you will rarely have to play with, but if you get complicated with what you add you might eventually want to know how to use).


Code:
struct DllExport IDInfo
{

	IDInfo(PlayerTypes eOwner=NO_PLAYER, int iID=FFreeList::INVALID_INDEX) : eOwner(eOwner), iID(iID) {}
	PlayerTypes eOwner;
	int iID;

	bool operator== (const IDInfo& info) const
	{
		return (eOwner == info.eOwner && iID == info.iID);
	}

	void reset()
	{
		eOwner = NO_PLAYER;
		iID = FFreeList::INVALID_INDEX;
	}
};

Structs are mini-files with their own defined rules. In this case, you declare it with a PlayerTypes item, and an Integer. The PlayerType becomes the eOwner variable, and the integer becomes the iID. The only inbuilt tool is a comparison and a reset function, primarily it just exists as a container to use for lookup tables to find pointers. I've never actually had a class in any Object oriented language, but I am relatively certain that the main reason this is required is because while we COULD store the pointers when we save the game, and it would take up the same space as a single integer, it points to a place in memory, so when the game is loaded in the future, it is quite likely located in a new memory space, thus the pointer is now invalid, so must be relocated/targetted. So by storing integer identifiers, we can look up appropriate pointers when required (which would be any time that we are not 100% certain that the preceding functions have done so themselves since the last time that the game was loaded).

The actual lookup happens in:

Code:
CvCity* getCity(IDInfo city)
{
	if ((city.eOwner >= 0) && city.eOwner < MAX_PLAYERS)
	{
		return (GET_PLAYER((PlayerTypes)city.eOwner).getCity(city.iID));
	}

	return NULL;
}

So you see it loops over all players, fetches the player fresh (GET_PLAYER), checks if it is a match, and then has the player search his stored city ID values for a match.




Typically there is joy to be found in releasing a ModComp, so I'd say go for it. The best releases are things you did for yourself, as then you are less upset if it appears that nobody else is using it at all.
 
Top Bottom