AI Land Transport Usage

Is the land transport able to load up onto a sea transport. I believe you could fix this by adding a new special unit type called transport or something so they can't load into transports.
 
I thought the Amphibious promotion simply removed the penalty of attacking from a transport. I wasn't aware that you could unload onto a land square and then move from it.

I think that calling finishMoves() on the unloaded unit should be enough. Without any movement points you can't attack or move. Problem solved?
 
This topic has obviously attracted a lot of interest!

Another thing from the Dune Wars experience is that land units which are loaded onto transports should be disallowed from taking most actions. It is apparently possible to pillage while inside a transport. It may be an interesting design question as to which actions should be allowed while in a transport. We probably want to avoid a weak unit hiding inside a transport and casting spells. Since any python code can add an action button, and no existing python code will check to see if the unit is on land but inside a transport, disallowing mod specific action buttons may be an additional challenge.

EDIT: the more I think about this as a general capability, the more questions come up. Should a unit inside a transport take collateral damage? Should an attacker with an ability like Marksman or flanking attacks be allowed to target units inside transports? None of these questions come up when the unit inside the transport is on an ocean square; the game automatically prevents a lot of these situations.
 
The collateral damage issue must be specifically addressed for sea transports because some vessels cause it but probably don't damage the units inside Transports (I haven't tested this, but it makes sense). I would think the transport vehicle is the one that takes the damage, and if it dies so does its cargo.

As an aside, I don't like having Gunships carry units. They are powerful because they are small and nimble and all teeth. I would prefer to see a weaker Transport Chopper that must be defended by Gunships or other more powerful units.

I would also not like to see units onboard a land transport be able to take actions outside the vehicle without disembarking and becoming vulnerable.
 
As an aside, I don't like having Gunships carry units. They are powerful because they are small and nimble and all teeth. I would prefer to see a weaker Transport Chopper that must be defended by Gunships or other more powerful units.

Plenty of stuff around in the DL-database, I guess: Hueys, Chinooks, etc. you name it and it's probably there :D
Just add them to your mod and adjust the stats :)
 
Bravo! This is definitely going to be used a lot.

I'm not so sure about ASSAULT_AIR, but I highly suggest implementing the missile one.
 
Excellent!

So, how do you all feel about the potential additions? Let me know if you want them and (if there is enough demand) I'll do it. I'm thinking that the UNITAI_MISSILE_CARRIER_LAND would be the easier of the two.

Amongst other things, this one would solve many (or even most) of the AI's combat problems in Final Frontier and mods derived from it (except those that don't have squadrons or missiles, anyway).

In FF, "space" is actually land so it never loads fighter or bomber squadrons on any carrier or starbase, or missiles on cruisers (although it does build cruisers, but almost never builds carriers). Correcting these issues would give it some chance of actually winning a war.
 
If we have land transports which are weak, I wonder if the AI will ever build them. Loading units onto powerful, fast gunships is a relatively obvious move which the AI should find. As I understand it, the logic to build up amphibious naval assault fleets is relatively complicated: you need transports, escorts, and ground attack units. I do not imagine that this logic will apply to land transports. So new code will be needed to help the AI decide to build land transports.
 
I hope this gets merged with other mods, like Revolutions or Better AI.

Any word on whether Maniac's fears are founded or not?
 
Regarding the unload/attack/load problem...

I'm thinking that I agree with Maniac in viewing this as more of a "feature", though I do believe it is one that needs some form of balancing. I like the idea of the Transport Chopper (thank you EF) having the capability to be a fast-moving "ground assault" platform able to carry units behind enemy lines to wreak havoc, but it really should be balanced somehow. I wonder if it is possible to make them (UNITCOMBAT_HELICOPTER units) interceptable but only by ground units (i.e. Machine Guns, Mech Infantry and SAM Infantry), if there was a chance that the Transport Chopper would be intercepted whenever it loaded or unloaded but only once per move, well, hold on example:

I have my fully loaded Transport Chopper and I am within range of an enemy SAM Infantry, I unload one of my cargo units, theres an Interception chance, if I want I can unload the rest of them there too with no Interception chance, but if I reload the same cargo unit that just unloaded or move one tile and unload another (or all) of my cargo units, there is a second Interception chance. Does this make sense?

The collateral damage issue must be specifically addressed for sea transports because some vessels cause it but probably don't damage the units inside Transports (I haven't tested this, but it makes sense). I would think the transport vehicle is the one that takes the damage, and if it dies so does its cargo.

I do not agree with the idea that just because the transport unit is killed so should the cargo unit. I am not saying that it should escape unscathed or even have a 100% survival rate, but I do feel that it should have some form of chance at survival. Though how I'd go about this is another question. Not quite sure.

As an aside, I don't like having Gunships carry units. They are powerful because they are small and nimble and all teeth. I would prefer to see a weaker Transport Chopper that must be defended by Gunships or other more powerful units.

I am all for incorporating other UNITCOMBAT_HELICOPTER models/new UNITCLASSes for diversification similar to UNITCOMBAT_NAVAL (I actually have the Gunships Mod incorporated into my custom assets for just that reason). As well as the motorized infantry that I believe I saw in LoR. Also, we can't forget about the Wagon Train which is what got seasnake posting about this in the first place. Can't forget that gunships aren't the only option.

I would also not like to see units onboard a land transport be able to take actions outside the vehicle without disembarking and becoming vulnerable.

I am in agreement that cargo units should need to disembark to participate in anything other than being cargo.

...I do not imagine that this logic will apply to land transports. So new code will be needed to help the AI decide to build land transports.

Precisely why I released it, if I can get people discussing it, perhaps I can get people to collaborate with me (directly or not).

and correct me if I am wrong, I count three votes for UNITAI_MISSILE_CARRIER_LAND? Anyone else interested?

@The_J
All told several hours spread out over a couple of days. Little bit here, little bit there, chase my two year old around for a little, code (or rather, adapt) a little
 
I guess it is clear from all the questions that this is not "done, just need to merge into other mods", but rather, "an interesting starting point".
 
I guess it is clear from all the questions that this is not "done, just need to merge into other mods", but rather, "an interesting starting point".

I thought it was done, but Maniac made me second-guess myself and so I venture to say that I think with collaboration we can accomplish this and accomplish this well. Think of it as an SDK version of the collaboration that happened with "The Alexander the Great Project". We can do this and we can do this well!
 
On the particular detail of preventing actions while loaded, does any interested reader know where in the code this could be checked? Pillaging while loaded is one example, also all worker actions. Is there a way to tell if a unit is loaded, while deciding which action buttons to draw? Separately, can the AI be prevented from taking these actions?
 
I believe part of the check to see if the button should be enabled checks if the action can be performed in the first place. This function is where you'd want to check if the unit is loaded using CvUnit::isCargo().
 
What I get from your first point is that I need to make a second assault mission and attach it to UNITAI_ASSAULT_LAND the way that the original assault mission is attached to UNITAI_ASSAULT_SEA.

I'm not sure that would be the best way to go. Mind you, I have little experience with unit AI. But if my understanding is correct the assault mission causes units to sit on their asses in cities until they get picked up by a transport. If the same thing is done for land transports, there may be cases where units wait for transports while, if there aren't enough available, it could have been faster to walk to the target city directly.

So perhaps a better way might be not to add extra code to let potential cargo move to land transports, but rather let land transports join up with city attack stacks. Then if there are enough land transports to put ALL units of the stack in the cargo (or at least all those with fewer movement points than the land transport), you could let them pick up the entire stack.

I'm even less sure what to do then though. Is it possible to then let the transports move to the same target city the city attack stack was moving to? Will all the transports stick together and move to the same target?
 
Alright, so I was up until dawn last night trying to piece things together and so far this is what I've come up with. While I have the SDK "up on blocks" as it were, I am going to go ahead and do UNITAI_MISSILE_CARRIER_LAND as well as some others. All told, in order to approximate on land what the AI does on the water, I will be adding a total of four new UNITAI's (three of which will be essentially clones of their _SEA counterparts and a fourth inspired by UNITAI_PIRATE_SEA). In addition, I will be adding some UNITCLASSes to reflect new default UNITAI's. This is what I'm thinking so far, check it out and feel free to post questions/comments/opinions.

Added UNITCLASS: Transport Chopper (a fat-body chopper I've seen somewhere around here)
Available with: Flight and Rocketry
DefaultAI: UNITAI_ASSAULT_LAND
Secondary AI's: None

Added UNITCLASS: Motorized Infantry (from LoR)
Available with: Combustion and Industrialism
DefaultAI: UNITAI_ASSAULT_LAND
Secondary AI's: None

Changed UNITCLASS: Mechanized Infantry
Available with: Combustion and Computers
DefaultAI UNITAI_ASSAULT_LAND
Secondary AI's: Unchanged

A look at void CvUnitAI::AI_assaultLandMove()
Spoiler :
Code:
void CvUnitAI::AI_assaultLandMove()
{
	PROFILE_FUNC();
	
	FAAssert(AI_getUnitAIType() == UNITAI_ASSAULT_LAND);
	
	bool bEmpty = !getGroup()->hasCargo();
	if (bEmpty)
	{
		if (AI_anyAttack(1, 65))
		{
			return;
		}
		if (AI_anyAttack(1, 40))
		{
			return;
		}
	}
	bool bAttack = false;
	bool bLandWar = false;
	int iTargetStackSize = std::max(4, 1 + (AI_stackOfDoomExtra()));
	int iCargo = getGroup()->getCargo();
	bool bFull = getGroup()->AI_isFull();
	
	AreaAITypes eAreaAIType = area()->getAreaAIType(getTeam());
	bLandWar = !isBarbarian() && ((eAreaAIType == AREAAI_OFFENSIVE) || (eAreaAIType == AREAAI_DEFENSIVE) || (eAreaAIType == AREAAI_MASSING));
	
	if (eAreaAIType == AREAAI_ASSAULT)
	{
		if (iCargo >= iTargetStackSize)
		{
			bAttack = true;
		}
	}
	if (!bAttack)
	{
		if (eAreaAIType == AREAAI_ASSAULT && iCargo > 0)
		{
			int iAttackers = GET_PLAYER(getOwnerINLINE()).AI_enemyTargetMissionAIs(MISSIONAI_ASSAULT_LAND, getGroup());
			if (iAttackers >= iTargetStackSize)
			{
				if (bFull)
				{
					//Join the Attack
					bAttack = true;
				}
			}
		}
		else if (GET_PLAYER(getOwnerINLINE()).AI_getPlotDanger(plot(), 2))
		{
			if (getGroup()->hasCargo())
			{
				getGroup()->unloadAll();
				getGroup()->pushMission(MISSION_SKIP);
				return;
			}
		}
		else if (bLandWar)
		{
			if (bFull && (eAreaAIType != AREAAI_DEFENSIVE))
			{
				if (AI_assaultLandTransport(false))
				{
					return;
				}
			}
			else if (iCargo > 0)
			{
				getGroup()->pushMission(MISSION_SKIP);
				return;
			}
		}
		else if (GET_PLAYER(getOwnerINLINE()).AI_unitTargetMissionAIs(this, MISSIONAI_LOAD_ASSAULT_LAND) > 0)
		{
			if (!bFull)
			{
				getGroup()->pushMission(MISSION_SKIP);
				return;
			}
		}
		else
		{
			if (!bLandWar && iCargo > 0)
			{
				if (AI_group(UNITAI_ASSAULT_LAND))
				{
					return;
				}
			}
		}
	}
	if (plot()->getTeam() == getTeam())
	{
		if ((iCargo == 0) && getGroup()->getNumUnits() > 1)
		{
			getGroup()->AI_makeForceSeparate();
		}
	}
	if (plot()->isOwned() && isEnemy(plot()->getTeam()))
	{
		if (iCargo == 0)
		{
			//if we just made a dropoff, pillage the tile if we can
			if ((getGroup()->countNumUnitAIType(UNITAI_ATTACK) + getGroup()->countNumUnitAIType(UNITAI_RESERVE)) > 0)
			{
				bool bMissionPushed = false;
				
				if (AI_pillageRange(1))
				{
					bMissionPushed = true;
				}
				
				CvSelectionGroup* pOldGroup = getGroup();
				
					//Release any support units to finish the job.
					getGroup()->AI_seperateAI(UNITAI_ATTACK);
					getGroup()->AI_seperateAI(UNITAI_RESERVE);
					
				if (pOldGroup == getGroup() && getUnitType() == UNITAI_ASSAULT_LAND)
				{
					if (AI_retreatToCity(true))
					{
						bMissionPushed = true;
					}
				}
				
				if (bMissionPushed)
				{
					return;
				}
			}
		}
	}
	
	if ((iCargo > iTargetStackSize) || bFull)
	{
		bAttack = true;
	}
	
	if (isBarbarian())
	{
		if(getGroup()->isFull())
		{
			if (AI_assaultLandTransport(false))
			{
				return;
			}
		}
		else
		{
			if (AI_pickup(UNITAI_ATTACK_CITY))
			{
				return;
			}
			
			if (AI_pickup(UNITAI_ATTACK))
			{
				return;
			}
		}
	}
	else
	{
		bool bAttackBarbarian = false;
		
		if (GET_TEAM(getTeam()).getAtWarCount(true) == 0)
		{
			bAttackBarbarian = true;
		}
		
		if (bAttack)
		{
			FAssert(getGroup()->hasCargo());
			if (AI_assaultLandTransport(bAttackBarbarian))
			{
				return;
			}
		}
	}
	
	if (bFull)
	{
		if(AI_group(UNITAI_ASSAULT_LAND, -1 /*iMaxOwnUnitAI*/ -1, -1, true))
		{
			return;
		}
	}
	else
	{
		if (AI_pickup(UNITAI_ATTACK_CITY))
		{
			return;
		}
		
		if (AI_pickup(UNITAI_ATTACK))
		{
			return;
		}
		
		if (AI_pickup(UNITAI_COUNTER))
		{
			return;
		}
	}
	
	if (AI_retreatToCity(true))
	{
		return;
	}
	
	if (AI_retreatToCity())
	{
		return;
	}
	
	if (AI_safety())
	{
		return;
	}
	
	getGroup()->pushMission(MISSION_SKIP);
	return;
}

Added/Changed UNITCLASSES: Amphibious Escort Chopper (AH_1 Cobra from Gunships v1.25), Escort Chopper (BTS Gunship)
Available with: Flight and Rocketry
DefaultAI: UNITAI_ESCORT_LAND
Secondary AI's: None

Added UNITCLASS: Armored Scout (from LoR)
Available with: Combustion, Radio and Rifling
DefaultAI: UNITAI_ESCORT_LAND
Secondary AI's: UNITAI_RAIDER


A look at void CvUnitAI::AI_escortLandMove()
Spoiler :
Code:
void CvUnitAI::AI_escortLandMove()
{
	PROFILE_FUNC();
	
	if (AI_heal(30, 1))
	{
		return;
	}

	if (AI_anyAttack(1, 55))
	{
		return;
	}
	
	if (AI_group(UNITAI_MISSILE_CARRIER_LAND, -1, /*iMaxOwnUnitAI*/ 0, -1, /*bIgnoreFaster*/ true))
	{
		return;
	}
		
	if (AI_group(UNITAI_ASSAULT_LAND, -1, /*iMaxOwnUnitAI*/ 0, -1, /*bIgnoreFaster*/ true, false, false, /*iMaxPath*/ 3))
	{
		return;
	}

	if (AI_heal(50, 3))
	{
		return;
	}

	if (AI_pillageRange(2))
	{
		return;
	}
	
	if (AI_group(UNITAI_MISSILE_CARRIER_LAND, 1, 1, true))
	{
		return;
	}

	if (AI_group(UNITAI_ASSAULT_LAND, 1, /*iMaxOwnUnitAI*/ 0, /*iMinUnitAI*/ -1, /*bIgnoreFaster*/ true))
	{
		return;
	}
	
	if (AI_group(UNITAI_ASSAULT_LAND, -1, /*iMaxOwnUnitAI*/ 2, /*iMinUnitAI*/ -1, /*bIgnoreFaster*/ true))
	{
		return;
	}
	
	if (AI_group(UNITAI_MISSILE_CARRIER_LAND, -1, /*iMaxOwnUnitAI*/ 2, /*iMinUnitAI*/ -1, /*bIgnoreFaster*/ true))
	{
		return;
	}
	
	if (AI_group(UNITAI_ASSAULT_LAND, -1, /*iMaxOwnUnitAI*/ 4, /*iMinUnitAI*/ -1, /*bIgnoreFaster*/ true))
	{
		return;
	}	

	if (AI_heal())
	{
		return;
	}

	if (AI_travelToUpgradeCity())
	{
		return;
	}

	if (AI_retreatToCity())
	{
		return;
	}

	if (AI_safety())
	{
		return;
	}

	getGroup()->pushMission(MISSION_SKIP);
	return;
}

Added UNITCLASS: AH_64 (Apache Attack Helicopter from Gunships v1.25)
Available with: Composites and Computers
DefaultAI: UNITAI_ATTACK
Secondary AI's: UNITAI_ESCORT_LAND, UNITAI_RAIDER

Added UNITCLASS: RAH_66 (Stealth Attack Helicopter from Gunships v1.25)
Available with: Fiber Optics, Robotics and Stealth
DefaultAI: UNITAI_RAIDER
Secondary AI's: UNITAI_ATTACK, UNITAI_ESCORT_LAND

A look at void CvUnitAI::AI_raiderMove()
Spoiler :
Code:
void CvUnitAI::AI_raiderMove()
{
	PROFILE_FUNC();
	
	if (plot()->isCity())
	{
		if (AI_heal())
		{
			return;
		}
	}
	if (plot()->isOwned() && (plot()->getTeam() == getTeam()))
	{
		AreaAITypes eAreaAIType = area()->getAreaAIType(getTeam());
		bLandWar = !isBarbarian() && ((eAreaAIType == AREAAI_OFFENSIVE) || (eAreaAIType == AREAAI_DEFENSIVE) || (eAreaAIType == AREAAI_MASSING));
			
		if ((((AI_getBirthmark() / 8) % 2) == 0) && (bLandWar))
		{
			bool bIsStealth = (getInvisibleType() != NO_INVISIBLE);
			
			if (bIsStealth)
			{	
				pForeignSoil = ((plot()->getOwnerINLINE() != getOwnerINLINE()) && (!plot->isImpassable())
				if (pForeignSoil->getNumUnrevealedTiles() > 0)
				{
					if (GET_PLAYER(getOwnerINLINE()).AI_areaMissionAIs(pForeignSoil, MISSIONAI_EXPLORE, getGroup()) < (GET_PLAYER(getOwnerINLINE()).AI_neededExplorers(pForeignSoil)))
					{
						if (AI_exploreRange(2))
						{
							return;
						}
					}
				}
			}
			if (AI_shadow(UNITAI_ASSAULT_LAND, 1, 50)
			{
				return;
			}
		}
	}
	if (plot()->isOwned() && (isEnemy(plot()->getTeam())))
	{
		if (bIsStealth)
		{
			if (AI_pillageRange(4))
			{
				return;
			}
		
			if (AI_pillageRange(3))
			{
				return;
			}
		
			if (AI_pillageRange(2))
			{
				return;
			}
			
			if (AI_pillageRange(1))
			{
				return;
			}
		else
		{
			if (AI_anyAttack(2, 25))
			{
				return;
			}
			
			if (AI_pillageRange(4))
			{
				return;
			}
		
			if (AI_pillageRange(3))
			{
				return;
			}
		
			if (AI_pillageRange(2))
			{
				return;
			}
			
			if (AI_pillageRange(1))
			{
				return;
			}
		}
	}
	
	if (AI_retreatToCity())
	{
		return;
	}
	
	if (AI_safety())
	{
		return;
	}
	
	getGroup()->pushMission(MISSION_SKIP)'
	return;
}

Added UNITCLASS: Missile Launcher (from Nuclear Arsenal 1.1)
Available with: Rocketry
DefaultAI: UNITAI_MISSILE_CARRIER_LAND
Secondary AI's: None

A look at void CvUnitAI::AI_missileCarrierLandMove()
Spoiler :
Code:
void CvUnitAI::AI_missileCarrierLandMove()
{
	bool bIsProtected = (getGroup()->countNumUnitAIType(UNITAI_ATTACK) + getGroup()->countNumUnitAIType(UNITAI_ESCORT_LAND)) > 0);
	
	if ((plot()->isCity() && plot()->getTeam() == getTeam()) || (bIsProtected))
	{
		if (AI_heal())
		{
			return;
		}
	}
	
	if (((plot()->getTeam() != getTeam()) && getGroup()->hasCargo()) || getGroup()->AI_isFull())
	{
		if (bIsProtected)
		{
			if (AI_carrierLandTransport())
			{
				return;
			}
			
			if (AI_shadow(UNITAI_ASSAULT_LAND, 1, 50))
			{
				return;
			}
		}
	}
	
	if (AI_retreatToCity())
	{
		return;
	}
	
	getGroup()->pushMission(MISSION_SKIP);
}

That is what I am thinking right now, I do have some other ideas (like including the nuke units from Nuclear Arsenal 1.1 and tuning UNITAI_ICBM for their use, as well as other UNITAI's that I'd rather discuss after getting this particular project done). Anyway, like I asked above, questions? comments?? opinions??? Anyone with a better understanding of the AI than me notice anything I missed that would be glaring had I a better understanding of the AI (hint, hint JDog and other BBAI thread regulars)?
 
UNITAI_RAIDER <- can this be used for units which can attack without declaring war? or will they pillage only?
 
UNITAI_RAIDER <- can this be used for units which can attack without declaring war? or will they pillage only?

that is a thought that I did not have, that would require <bHiddenNationality> to be flipped to 1 in the XML right? I think that is an idea with some potential, a raider "unit strand" like the militia one, I'm thinking it would be just a basic typical unitclass for the age (like warrior in the ancient age and horseman or swordsman in the classical age and so on similar to that) with the only difference being a slight increase in cost, but also having <bHiddenNationality>1</bHiddenNationality> and defaulting to UNITAI_RAIDER. Sound about right?
 
Top Bottom