[SDK] Advanced Unit Automations

Thought I'd just pop in to say I tried merging this in with PIG and while I found it compiled successfully, it crashed when I tried to start a new game with it (after custom game screen). I'm working off of mainly Fuyu's latest BULLAI version so I made sure to avoid copying the BBAI functions you noted were redundant in the code.

I might have another go later, and I'm not sure whether it's possible to have the slightest clue what I might have done wrong with so little info but nonetheless any suggestions or advice is much appreciated.

Anyway, if you're listening Fuyu, I think it would be great if you merged this into BULLAI. Updating my code from yours usually goes well.

If it's possible, and doesn't cause too much trouble, perhaps it can be wrapped with one of those fancy compile-time flags?

Alternatively, perhaps if someone has managed to merge this with Fuyu's BULLAI code they could post the merged source files, please?

Thanks again Afforess for giving us this mod.
 
As long as BULLAI doesn't crash without AUA I see no problem ;)

I once planned to merge it in, but didn't. For a while I was even worried about savegame compatibility but then I started to notice that I must also have a big aversion against anything automated in civ, so in the end I abandoned it.

At the moment I have other things to code (civics ai) so me merging in something I dislike is not going to happen anytime soon but there was one guy who already merged it:
(...)
i have merged it with your bbai bug, saw no problem so far - have i overlooked something??
(...)
If you already have a merge though, would you mind making the changed SDK files available to me? That might save me some effort.
I have not gotten any reply to that though, afaik.
If it ever becomes part of BULLAI I will enclose it with ifdefs, of course, I will need to turn it off for my own compiles after all.
 
The biggest problem are the new player options. Every settings get reset to default when switching between mods with a different number of player options - like switching between vanilla bts and your whatever mod with AUA. That's annoying. :-/
 
I used player options because not every mod uses BUG; and I didn't want to make that a requirement for AUA; but you could easily rewrite the player options into BUG options if you are familiar with BUG.
 
Ugh, I had my second go at merging this, this time with the help of keldath's existing merge with BULLAI (thanks, keldath! by the way), and again it compiles successfully but now my mod is failing to even load.

EDIT...Some good news. Vanilla BtS is even failing to load so the problem is probably somewhere else. I probably messed with a file in the assets directory by accident.
 


border patrol does not work yet correctly for ships. this ship is stuck and when I add a modern ship like destroyer and set it to border patrol, the whole game gets slowed down - "laggy interface".

also, other people tell me, their ship is driving everywhere (around the sea, even outside the borders) when set to border patrol.

I've made a couple changes for a future version (which will be out in a few weeks, I'm afraid...) that will allow you to force Border Patrol units to stay inside their borders, and I changed the AI a bit. Anyway, you should be aware, Border Patrol units CAN leave the borders, and go up to 2 tiles away before they come back. I meant it to be that way so they could kill units on the outskirts of the borders.

I haven't tested much with late-game units, I'll do that when I'm back. Ciao.

Okay, lets debug AUA together. ;)

And Cybahs report is right. The ships will go everywhere. The reason is simple. You have set the searchrange to 2* BaseMove for DOMAIN_SEA. So destroyers will not go 2 plots away from the border they will go 10 plots and more from the border. And so they are out of control. And that is also the reason for the slowdown. The game has to check all plots in radius 14 sometimes instead of radius 2. A lot of plots. ;)

And perhaps you like this code from CCV:

PHP:
//AI_patrolBorders relys heavily on the units current facing direction to determine where the next
//move should be. For example, units facing north should not turn around south, or any southerly
//direction (southwest, southeast) to patrol, since that would cause them to move back and forth 
//in a nonsensical manner. Instead, they should appear to be intelligent, and move around 
//the players borders in a circuit, without turning back or leaving the boundries of 
//the cultural borders. This is not in fact the most optimal method of patroling, but it 
//produces results that make sense to the average human, which is the actual goal, since 
//the AI actually never use this function, only automated human units do.
bool CvUnitAI::AI_patrolBorders()
{
	PROFILE_FUNC();
	
	CvPlot* pBestPlot;
	int iValue;
	int iBestValue;

	iBestValue = -1;
	pBestPlot = NULL;
	
	int iDX, iDY;

	int iSearchRange = baseMoves();

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

			if (pLoopPlot != NULL)
			{
				if (canMoveInto(pLoopPlot))
				{
					DirectionTypes eNewDirection = estimateDirection(plot(), pLoopPlot);
					// offset of 250 to make sure to generate a result >= 1
					iValue = 250 + GC.getGameINLINE().getSorenRandNum(1000, "AI Border Patrol");
					if (pLoopPlot->isBorder(true))
					{
						// increased value to be sure to better than any non border plot!
						// 1500 because: 1000 of max. random offset and 500 of max. random non border value addition
						iValue += 1500;
						iValue += GC.getGameINLINE().getSorenRandNum(1000, "AI Border Patrol");
					}
					else if (pLoopPlot->isBorder(false))
					{
						iValue += GC.getGameINLINE().getSorenRandNum(500, "AI Border Patrol");
					}
					//Avoid heading backwards, we want to circuit our borders, if possible.
					if (eNewDirection == getOppositeDirection(getFacingDirection(false)))
					{
						iValue /= 25;
					}
					else if (isAdjacentDirection(getOppositeDirection(getFacingDirection(false)), eNewDirection))
					{
						iValue /= 10;
					}
					if (pLoopPlot->getOwnerINLINE() != getOwnerINLINE())
					{
						if (GET_PLAYER(getOwnerINLINE()).isOption(PLAYEROPTION_BORDER_STAY_IN_BORDERS))
						{
							iValue = -1;
						}
						else
						{
							iValue /= 10;
						}
						
					}
					if (iValue > iBestValue)
					{
						iBestValue = iValue;
						pBestPlot = pLoopPlot;
					}
				}
			}
		}
	}
	if (pBestPlot != NULL)
	{
		FAssert(!atPlot(pBestPlot));
		getGroup()->pushMission(MISSION_MOVE_TO, pBestPlot->getX_INLINE(), pBestPlot->getY_INLINE());
		return true;
	}
	
	return false;
}

And it is useful to do some other changes. Just to be sure. Changes like this:

PHP:
							if (iValue > iBestValue && getPathEndTurnPlot() != NULL)
							{
								iBestValue = iValue;
								pBestPlot = getPathEndTurnPlot();
							}

and

PHP:
iBestValue = -1;
 
Did I say radius 14? :lol: Radius 70, 80 or even 100!!!

PHP:
	if (AI_huntRange(10, 60, true, 20))
	{
		return;
	}

Whole map.
 
Okay, lets debug AUA together. ;)

And Cybahs report is right. The ships will go everywhere. The reason is simple. You have set the searchrange to 2* BaseMove for DOMAIN_SEA. So destroyers will not go 2 plots away from the border they will go 10 plots and more from the border. And so they are out of control. And that is also the reason for the slowdown. The game has to check all plots in radius 14 sometimes instead of radius 2. A lot of plots. ;)

And perhaps you like this code from CCV:

PHP:
//AI_patrolBorders relys heavily on the units current facing direction to determine where the next
//move should be. For example, units facing north should not turn around south, or any southerly
//direction (southwest, southeast) to patrol, since that would cause them to move back and forth 
//in a nonsensical manner. Instead, they should appear to be intelligent, and move around 
//the players borders in a circuit, without turning back or leaving the boundries of 
//the cultural borders. This is not in fact the most optimal method of patroling, but it 
//produces results that make sense to the average human, which is the actual goal, since 
//the AI actually never use this function, only automated human units do.
bool CvUnitAI::AI_patrolBorders()
{
	PROFILE_FUNC();
	
	CvPlot* pBestPlot;
	int iValue;
	int iBestValue;

	iBestValue = -1;
	pBestPlot = NULL;
	
	int iDX, iDY;

	int iSearchRange = baseMoves();

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

			if (pLoopPlot != NULL)
			{
				if (canMoveInto(pLoopPlot))
				{
					DirectionTypes eNewDirection = estimateDirection(plot(), pLoopPlot);
					// offset of 250 to make sure to generate a result >= 1
					iValue = 250 + GC.getGameINLINE().getSorenRandNum(1000, "AI Border Patrol");
					if (pLoopPlot->isBorder(true))
					{
						// increased value to be sure to better than any non border plot!
						// 1500 because: 1000 of max. random offset and 500 of max. random non border value addition
						iValue += 1500;
						iValue += GC.getGameINLINE().getSorenRandNum(1000, "AI Border Patrol");
					}
					else if (pLoopPlot->isBorder(false))
					{
						iValue += GC.getGameINLINE().getSorenRandNum(500, "AI Border Patrol");
					}
					//Avoid heading backwards, we want to circuit our borders, if possible.
					if (eNewDirection == getOppositeDirection(getFacingDirection(false)))
					{
						iValue /= 25;
					}
					else if (isAdjacentDirection(getOppositeDirection(getFacingDirection(false)), eNewDirection))
					{
						iValue /= 10;
					}
					if (pLoopPlot->getOwnerINLINE() != getOwnerINLINE())
					{
						if (GET_PLAYER(getOwnerINLINE()).isOption(PLAYEROPTION_BORDER_STAY_IN_BORDERS))
						{
							iValue = -1;
						}
						else
						{
							iValue /= 10;
						}
						
					}
					if (iValue > iBestValue)
					{
						iBestValue = iValue;
						pBestPlot = pLoopPlot;
					}
				}
			}
		}
	}
	if (pBestPlot != NULL)
	{
		FAssert(!atPlot(pBestPlot));
		getGroup()->pushMission(MISSION_MOVE_TO, pBestPlot->getX_INLINE(), pBestPlot->getY_INLINE());
		return true;
	}
	
	return false;
}
Okay; so I misunderstood how AI_searchRange worked. Thanks for alerting me to the issue.

And it is useful to do some other changes. Just to be sure. Changes like this:

PHP:
							if (iValue > iBestValue && getPathEndTurnPlot() != NULL)
							{
								iBestValue = iValue;
								pBestPlot = getPathEndTurnPlot();
							}
Unless I misunderstand how getPathEndTurnPlot is calculated; it should be returning NULL there, since I never tried to generate a path. NULL would cause the function to fail incorrectly, so units wouldn't be able to patrol. Try it and see; can units ever border patrol anymore?
 
The check is getPathEndTurnPlot() != NULL.

So even if a higher value is found a valid plot (pBestPlot != NULL) will never be replaced bei NULL! That's what it is good for. Maybe that will never happen. But this is save in all possible cases.
And yes, my code works in CCV. And my ships are patroling as expected in a situation as seen in Cybahs picture.

&#8364;: Here are my CvUnitAI files from CCV 4.15. I've also done some changes to reduce the very strange behaviour of modern ship types.

And what I also should mention is a problem with your air missions. The airship can't be automated with your code. But a trick can help. Increase the collateral damage to a value > 0. So they are bombers for your code. But keep the related damagelimit at 0. So nothing will happen but the airship can be automated.
But the main problem is that your missions will not do what you describe in the help. Bombers will attack buildings as you say but they will attack units too. Same with fighters. They will do also both. But the description suggerates something else. And if you remove the lines that make the difference between fighters and bombers (modder option) it makes really no sence to have both missions at all. So you may notice that there is only one mission (airattack) for all air units.
 
I'm with Afforess there, getPathEndTurnPlot() should only return something useful if you did a generatePath() before. Which doesn't happen there.
If you really replace
Code:
                    if (iValue > iBestValue)
                    {
                        iBestValue = iValue;
                        pBestPlot = pLoopPlot;
                    }

with
Code:
                            if (iValue > iBestValue && getPathEndTurnPlot() != NULL)
                            {
                                iBestValue = iValue;
                                pBestPlot = getPathEndTurnPlot();
                            }
in AI_patrolBorders then patrolling should simply not happen, because no pathfinding means no LastNode means NULL is returned.
That is, unless you did some pathfinding with that same unit/selectiongroup in some other function before, but the only way this could happen is if anything was in huntrange but the decision was made not to hunt, which would mean that the unit will move towards that last-found enemy unit anyway. Or at least that's what I think would happen.

So what exactly is that code supposed to do? I don't get it.
 
And what I also should mention is a problem with your air missions. The airship can't be automated with your code. But a trick can help. Increase the collateral damage to a value > 0. So they are bombers for your code. But keep the related damagelimit at 0. So nothing will happen but the airship can be automated.

I believe I made a comment to that effect in the code.

But the main problem is that your missions will not do what you describe in the help. Bombers will attack buildings as you say but they will attack units too. Same with fighters. They will do also both. But the description suggerates something else. And if you remove the lines that make the difference between fighters and bombers (modder option) it makes really no sence to have both missions at all. So you may notice that there is only one mission (airattack) for all air units.

Considering that units can perform both missions, it's more or less a flaw in the way BTS works. I'm not sure why Firaxis wanted fighters to be able to airbomb, but whatever.
 
You never get my intention. My English must be terrible. Sorry.



I'm with Afforess there, getPathEndTurnPlot() should only return something useful if you did a generatePath() before. Which doesn't happen there.
If you really replace
Code:
                    if (iValue > iBestValue)
                    {
                        iBestValue = iValue;
                        pBestPlot = pLoopPlot;
                    }

with
Code:
                            if (iValue > iBestValue && getPathEndTurnPlot() != NULL)
                            {
                                iBestValue = iValue;
                                pBestPlot = getPathEndTurnPlot();
                            }
in AI_patrolBorders then patrolling should simply not happen, because no pathfinding means no LastNode means NULL is returned.
That is, unless you did some pathfinding with that same unit/selectiongroup in some other function before, but the only way this could happen is if anything was in huntrange but the decision was made not to hunt, which would mean that the unit will move towards that last-found enemy unit anyway. Or at least that's what I think would happen.

So what exactly is that code supposed to do? I don't get it.

I've never suggested to use this lines in the patrol function from above. Have a look at my code. No, there are other code parts where the best plot is set to the getPathEndTurnPlot() but without a check if it is a valid plot and != NULL. The same with the offset I suggested. Not 0 in most cases. -1 will work better. I've posted my files.

About the airmissions. All I want to say is that the descriptions doesn't fit to the results.

€: File attached
 
I believe I made a comment to that effect in the code.

Exactly. So remove the line you commented and you will have a unit the gets both buttons. But while your description says that the first button will make the unit attack units it says that the other button will make the unit attack buildings. But that is not true. The unit will do both. Doesn't matter what button is pushed.
And the effect is also there without the modder modification. You push the attack unit button of a fighter and the fighter will also attack buildings.

More clearly now what I mean?
 
Also, airships cannot use any automation... because of 0 collateral damage. o_O
 
I've never suggested to use this lines in the patrol function from above. Have a look at my code. No, there are other code parts where the best plot is set to the getPathEndTurnPlot() but without a check if it is a valid plot and != NULL. The same with the offset I suggested. Not 0 in most cases. -1 will work better. I've posted my files.

Ah, I see. That makes sense. ;)

About the airmissions. All I want to say is that the descriptions doesn't fit to the results.
Exactly. So remove the line you commented and you will have a unit the gets both buttons. But while your description says that the first button will make the unit attack units it says that the other button will make the unit attack buildings. But that is not true. The unit will do both. Doesn't matter what button is pushed.
And the effect is also there without the modder modification. You push the attack unit button of a fighter and the fighter will also attack buildings.

More clearly now what I mean?
I know exactly what you are talking about; but why shouldn't fighters be able to bomb? I just use different buttons so as not to confuse players; I realize they have the same effect.
 
Ah, I see. That makes sense. ;)



I know exactly what you are talking about; but why shouldn't fighters be able to bomb? I just use different buttons so as not to confuse players; I realize they have the same effect.

That is absolutly okay. But in that case don't make a difference in the descriptions! Thats it. :)
 
Great; well I'm glad we understand each other. I found a minor bug in AUA no one has mention yet; so I'll post an updated version in a few hours.
 
You never get my intention. My English must be terrible. Sorry.

I've never suggested to use this lines in the patrol function from above. Have a look at my code. No, there are other code parts where the best plot is set to the getPathEndTurnPlot() but without a check if it is a valid plot and != NULL. The same with the offset I suggested. Not 0 in most cases. -1 will work better. I've posted my files.

&#8364;: File attached
You can try german too, with me at least ;)

If there is no valid plot check, add one. Before generating the path to that plot, of course. If a path is found at all (which means when generatePath returns true), getPathEndTurnPlot() will always return a valid plot != NULL, so a getPathEndTurnPlot() != NULL check is always unnecessary.

.

As for the air missions: I'm all for merging them but there is a way to satisfy the "need" for seperate buttons too: Make 3 buttons/automate types.
1) Air Bomb (for units that can air bomb)
2) Air Strike (for units that can air strike)
3) Air Attack (for units that can do both)
BTS units will have all 3 buttons because they can do both. Air Attack would use the same AI_autoAirStrike as it exists right now, Air Bomb/Air Strike would need a different version of that function that only contains the one or the other mission.
All you need is a new button.

edit: unless of course everyone wants every unit to do everything. Then the second button really is only decoration and the description might be misleading but at least you all agree with each other :p
 
Top Bottom