AI Land Transport Usage

No, I am saying that cities are not handled specially. If the cargo unit is on the same land plot as the transport unit which can only happen in a city or fort, the unit may load onto the transport. No special code is needed to make this happen.

You have added code to canLoad() that returns false if the unit doesn't have any moves left. This is special code to handle land transports, but it affects sea transports as I noted above. I think that if you are in your own or friendly city/port with any type of transport, you should be able to load/unload without any moves left.
 
You are saying that cities are handled specially for loading and unloading. That is fine. There is no evidence of this in the functions I modified. Therefore, there must be some other functions which provide the special handling of cities. I would like to look at those to understand them better. Can you help me to figure out where this special handling happens?

There are two ways of getting a unit into the existing sea-based transports:

1) Move the unit from land into the transport in water.

2) Move the unit into a city or fort that has a transport in it. Click on the unit's "load" button to get unit into the transport. Note that in this case the transport is actually on a land tile.

To do case 1, the unit must spend a movement point to get to the transport (so it must have started with at least one). Any remaining movement is also lost.

In case 2, the unit does not need to have any movement points left to be loaded. Any remaining movement is lost (this prevents loading, moving the transport, and doing an amphibious attack on the same turn).

There are two ways to get a unit off a transport.

1) When the transport is in a coastal tile, move the unit off the transport onto land. This is an amphibious attack if there is an enemy unit in the land tile. This uses up all movement points.

2) When the transport is in a city or fort you can do one of three things. First, you can just select a unit on the transport and move it to some tile outside the city or fort - this costs the normal amount for moving the unit as if it were not on the transport (so you can't do this if the unit has no movement left, which it won't if it was just loaded on the same turn). Second, you can click on the transport's "unload all" button which unloads the unit and does not use any of the unit's movement. Third, you click on the unit's "unload" button which also does not use any of the unit's movement.

It is possible to load a unit, move the transport into a city or fort, and unload the unit all in one turn since the unloading doesn't use any movement when the transport is in the city or fort so the unit having no movement left (due to being loaded onto the transport) doesn't matter.

It may not be entirely clear how to translate some of this to land transports. But it is aparent that unloading in a city or fort with the existing sea transports does not use any movement for the unit being unloaded. Unloading anyplace else uses all of the unit's movement - but currently this requires it to move one tile to get from the coastal tile the transport is in to the land tile it is moving to.
 
Thank you for the details. I guess the special handling for cities really comes because ocean transports are allowed into city squares. That is the only time that a transport and a unit (not being transported) can be in the same square. With land transports, this happens all the time, so it requires a little more thought.

In Dune Wars, we have only all terrain transports. I guess it is more complicated if there are ocean transports, and a separate type of land transport. The feedback in DW so far is that it is too easy to load/unload while in enemy territory. We will try out the changes I have described and see if there is any other funny behavior. Other systems may wish to try slightly different implementations.

If anybody follows up on koma's lead on having the AI construct the right mix of units, I would be very interested.
 
...If anybody follows up on koma's lead on having the AI construct the right mix of units, I would be very interested.

This is something I looked at last night, I tweaked the whole block of code that starts with int iBestSeaAssaultCapacity = 0 by copying it this is what I came up with. Changes are most of the way down.

Spoiler :
Code:
int iBestSeaAssaultCapacity = 0;
		if (NULL != pWaterArea && (bAssault))
		{
			UnitTypes eBestAssaultUnit = NO_UNIT;  
			kPlayer.AI_bestCityUnitAIValue(UNITAI_ASSAULT_SEA, this, &eBestAssaultUnit);
			if (eBestAssaultUnit != NO_UNIT)
			{
				iBestSeaAssaultCapacity = GC.getUnitInfo(eBestAssaultUnit).getCargoSpace();
			}
			
			int iUnitsToTransport = kPlayer.AI_totalAreaUnitAIs(pArea, UNITAI_ATTACK_CITY);
			iUnitsToTransport += kPlayer.AI_totalAreaUnitAIs(pArea, UNITAI_ATTACK);
			iUnitsToTransport += kPlayer.AI_totalAreaUnitAIs(pArea, UNITAI_COUNTER);
			
			int iTransports = kPlayer.AI_totalAreaUnitAIs(pArea, UNITAI_ASSAULT_SEA);
			iTransports += kPlayer.AI_totalAreaUnitAIs(pWaterArea, UNITAI_ASSAULT_SEA);
			
			int iEscorts = kPlayer.AI_totalAreaUnitAIs(pArea, UNITAI_ESCORT_SEA);
			iEscorts += kPlayer.AI_totalAreaUnitAIs(pWaterArea, UNITAI_ESCORT_SEA);
			
			//The way of calculating numbers is a bit fuzzy since the ships
			//can make return trips. When massing for a war it'll train enough
			//ships to move it's entire army. Once the war is underway it'll stop
			//training so many ships on the assumption that those out at sea
			//will return...
			
			if ((iEscorts < ((1 + 2 * iTransports) / 3)) && (GC.getGame().getSorenRandNum(2, "AI train escort sea") == 0))
			{
				if (AI_chooseUnit(UNITAI_ESCORT_SEA))
				{
					AI_chooseBuilding(BUILDINGFOCUS_DOMAINSEA);
					return;
				}
			}
			
			UnitTypes eBestAttackSeaUnit = NO_UNIT;  
			kPlayer.AI_bestCityUnitAIValue(UNITAI_ATTACK_SEA, this, &eBestAttackSeaUnit);
			if (eBestAttackSeaUnit != NO_UNIT)
			{
				if (GC.getUnitInfo(eBestAttackSeaUnit).getBombardRate() > 0)
				{
					int iAttackSea = kPlayer.AI_totalAreaUnitAIs(pArea, UNITAI_ATTACK_SEA);
					iAttackSea += kPlayer.AI_totalAreaUnitAIs(pWaterArea, UNITAI_ATTACK_SEA);
						
					if ((iAttackSea < ((1 + 2 * iTransports) / 2)) && (GC.getGame().getSorenRandNum(2, "AI train attack sea") == 0))
					{
						if (AI_chooseUnit(UNITAI_ATTACK_SEA))
						{
							AI_chooseBuilding(BUILDINGFOCUS_DOMAINSEA);
							return;
						}
					}
				}
			}
			
			if (iUnitsToTransport > (iTransports * iBestSeaAssaultCapacity))
			{
				if (AI_chooseUnit(UNITAI_ASSAULT_SEA))
				{
					AI_chooseBuilding(BUILDINGFOCUS_DOMAINSEA);
					return;
				}
			}
			
			int iCarriers = kPlayer.AI_totalUnitAIs(UNITAI_CARRIER_SEA);
			
			if (iCarriers > 0)
			{
				UnitTypes eBestCarrierUnit = NO_UNIT;  
				kPlayer.AI_bestCityUnitAIValue(UNITAI_CARRIER_SEA, this, &eBestCarrierUnit);
				if (eBestCarrierUnit != NO_UNIT)
				{
					FAssert(GC.getUnitInfo(eBestCarrierUnit).getDomainCargo() == DOMAIN_AIR);
					
					int iCarrierAirNeeded = iCarriers * GC.getUnitInfo(eBestCarrierUnit).getCargoSpace();
					
					if (kPlayer.AI_totalUnitAIs(UNITAI_CARRIER_AIR) < iCarrierAirNeeded)
					{
						if (AI_chooseUnit(UNITAI_CARRIER_AIR))
						{
							return;
						}
					}
				}
			}
			
			if (iCarriers < (kPlayer.AI_totalUnitAIs(UNITAI_ASSAULT_SEA) / 4))
			{
				if (AI_chooseUnit(UNITAI_CARRIER_SEA))
				{
					return;
				}
			}
			
			int iMissileCarriers = kPlayer.AI_totalUnitAIs(UNITAI_MISSILE_CARRIER_SEA);
			
			if (iMissileCarriers > 0)
			{
				UnitTypes eBestMissileCarrierUnit = NO_UNIT;  
				kPlayer.AI_bestCityUnitAIValue(UNITAI_MISSILE_CARRIER_SEA, this, &eBestMissileCarrierUnit);
				if (eBestMissileCarrierUnit != NO_UNIT)
				{
					FAssert(GC.getUnitInfo(eBestMissileCarrierUnit).getDomainCargo() == DOMAIN_AIR);
					
					int iMissileCarrierAirNeeded = iMissileCarriers * GC.getUnitInfo(eBestMissileCarrierUnit).getCargoSpace();
					
					if ((kPlayer.AI_totalUnitAIs(UNITAI_MISSILE_AIR) < iMissileCarrierAirNeeded) || 
						(bPrimaryArea && (kPlayer.AI_totalAreaUnitAIs(pArea, UNITAI_MISSILE_CARRIER_SEA) * GC.getUnitInfo(eBestMissileCarrierUnit).getCargoSpace() < kPlayer.AI_totalAreaUnitAIs(pArea, UNITAI_MISSILE_AIR))))
					{
						if (AI_chooseUnit(UNITAI_MISSILE_AIR))
						{
							return;
						}
					}
				}
			}
		}
		//TLO: AI Land Transport Usage Start
		else if (NULL == pWaterArea && (bAssault))
		{
			int iBestLandAssaultCapacity = 0;
			UnitTypes eBestAssaultUnit = NO_UNIT;  
			kPlayer.AI_bestCityUnitAIValue(UNITAI_ASSAULT_LAND, this, &eBestAssaultUnit);
			if (eBestAssaultUnit != NO_UNIT)
			{
				iBestSeaAssaultCapacity = GC.getUnitInfo(eBestAssaultUnit).getCargoSpace();
			}
			
			int iUnitsToTransport = kPlayer.AI_totalAreaUnitAIs(pArea, UNITAI_ATTACK_CITY);
			iUnitsToTransport += kPlayer.AI_totalAreaUnitAIs(pArea, UNITAI_ATTACK);
			iUnitsToTransport += kPlayer.AI_totalAreaUnitAIs(pArea, UNITAI_COUNTER);
			
			int iTransports = kPlayer.AI_totalAreaUnitAIs(pArea, UNITAI_ASSAULT_LAND);
						
			int iEscorts = kPlayer.AI_totalAreaUnitAIs(pArea, UNITAI_ESCORT_LAND);
						
			//The way of calculating numbers is a bit fuzzy since the ships
			//can make return trips. When massing for a war it'll train enough
			//ships to move it's entire army. Once the war is underway it'll stop
			//training so many ships on the assumption that those out in the field
			//will return...
			
			if ((iEscorts < ((1 + 2 * iTransports) / 3)) && (GC.getGame().getSorenRandNum(2, "AI train escort land") == 0))
			{
				if (AI_chooseUnit(UNITAI_ESCORT_LAND))
				{
					AI_chooseBuilding(BUILDINGFOCUS_EXPERIENCE);
					return;
				}
			}
			
			if (iUnitsToTransport > (iTransports * iBestLandAssaultCapacity))
			{
				if (AI_chooseUnit(UNITAI_ASSAULT_LAND))
				{
					AI_chooseBuilding(BUILDINGFOCUS_EXPERIENCE);
					return;
				}
			}
			
			int iMissileCarriers = kPlayer.AI_totalUnitAIs(UNITAI_MISSILE_CARRIER_LAND);
			
			if (iMissileCarriers > 0)
			{
				UnitTypes eBestMissileCarrierUnit = NO_UNIT;  
				kPlayer.AI_bestCityUnitAIValue(UNITAI_MISSILE_CARRIER_LAND, this, &eBestMissileCarrierUnit);
				if (eBestMissileCarrierUnit != NO_UNIT)
				{
					FAssert(GC.getUnitInfo(eBestMissileCarrierUnit).getDomainCargo() == DOMAIN_AIR);
					
					int iMissileCarrierAirNeeded = iMissileCarriers * GC.getUnitInfo(eBestMissileCarrierUnit).getCargoSpace();
					
					if ((kPlayer.AI_totalUnitAIs(UNITAI_MISSILE_AIR) < iMissileCarrierAirNeeded) || 
						(bPrimaryArea && (kPlayer.AI_totalAreaUnitAIs(pArea, UNITAI_MISSILE_CARRIER_LAND) * GC.getUnitInfo(eBestMissileCarrierUnit).getCargoSpace() < kPlayer.AI_totalAreaUnitAIs(pArea, UNITAI_MISSILE_AIR))))
					{
						if (AI_chooseUnit(UNITAI_MISSILE_AIR))
						{
							return;
						}
					}
				}
			}
		//TLO: AI Land Transport Usage End

Thanks for the discussion guys, some great ideas, I'll get that if(isCargo) check in, though I'm probably going to change it to something like:
Code:
if (isCargo() && pTransportUnit == DOMAIN_LAND)

I'm hoping to compile and test this evening at the latest and I will definitely post the results asap.
 
I'll get that if(isCargo) check in, though I'm probably going to change it to something like: if (isCargo() && pTransportUnit == DOMAIN_LAND)

In planetfall and Dune Wars, the transports are DOMAIN_SEA and have the XML flag bCanMoveAllTerrain. We call them "all terrain transports". Perhaps the check should be a little more specific, but I am not sure what. Many of the situations (such as pillaging and worker builds) could not happen while on a transport anyway because the domain would not allow it.
 
If the code for the Land Transports is going to be essentially the same only geared towards DOMAIN_LAND, would it be simpler to just change your transports to DOMAIN_LAND and bCanMoveAllTerrain?
 
everything is compiling fine, was getting some minor weirdness (workboats couldn't automate?) with my test last night, things have been hectic most of the day, just finshed compiling next test, will try this evening and post results tomorrow, right now, I'm figuring that if I present the AI with the options AI_load or AI_canLoad enough, eventually it will work, might be a little unwieldy, but my goal is to get it to work, once it works it can be refined, though I am starting to feel like this may have been the wrong sdk project to tackle first, but I will accomplish it (unless of course there is something in the exe that forbids AI usage of land transports).
 
I will say that I agree with you 100% that you chose one of the hardest things to tackle in the SDK, even for a veteran modder. There is no shame or harm in setting this one aside and picking something a bit easier and more likely to build your experience and confidence to try something harder.

The bunny slope is there for a very good reason. :D Using it vastly increases your odds of making it onto the double black diamond runs in one piece.
 
Ah, but you must realize that I have decided that I will accomplish this, even if it means that I'm going down the slope on the hospital bed, the hospital bed and I will stick the landing and go home with the gold. :D
 
As soon as you get this to work I'm implementing a Mi-24 Hind D. Attack chopper with transport will be so fun to use.
 
I need to become much more familiar with the SDK for this project to be realized. I cannot see a way that it can be done without copying everything related to transports (i.e. cargo, load/unload functions) and dedicating it to land transports. In addition, it would require that I also hijack all of the functions where the AI decides where to attack and what to build in its preparations for war. In short, I haven't given up, but I wouldn't expect anything workable soon. I apologize once again.
 
Top Bottom