From 65b9398100bdd25855b8226735b398cd3f63b20f Mon Sep 17 00:00:00 2001
From: johns <>
Date: Tue, 19 Mar 2002 22:44:00 +0000
Subject: [PATCH] AI uses the transporters better, code cleanups.

---
 src/ai/ai_force.cpp  |  52 ++++++++++---
 src/ai/ai_magic.cpp  | 174 +++++++++++++++++++++++++++++--------------
 src/ai/ai_plan.cpp   |   2 +
 src/ai/script_ai.cpp |   2 +-
 4 files changed, 164 insertions(+), 66 deletions(-)

diff --git a/src/ai/ai_force.cpp b/src/ai/ai_force.cpp
index c566d28f2..873d2b651 100644
--- a/src/ai/ai_force.cpp
+++ b/src/ai/ai_force.cpp
@@ -356,10 +356,19 @@ global void AiAttackWithForce(int force)
     }
 }
 
+//----------------------------------------------------------------------------
+//	Handle attack of force with transporter.
+//----------------------------------------------------------------------------
+
 /**
+**	Step 1)
 **	Load force on transporters.
 **
 **	@param force	Force pointer.
+**
+**	@todo	If an unit can't reach the transporter the code hangs.
+**		We must the transporter land on a new position.
+**		Or the board action will be better written.
 */
 local void AiLoadForce(AiForce* force)
 {
@@ -427,9 +436,14 @@ local void AiLoadForce(AiForce* force)
 }
 
 /**
-**	Send force off transporters.
+**	Step 2)
+**	Send force awaay in transporters, to unload at target position.
 **
 **	@param force	Force pointer.
+**
+**	@todo	The transporter should avoid enemy contact and should land
+**		at an unfortified coast. If we send more transporters they
+**		should land on different positions.
 */
 local void AiSendTransporter(AiForce* force)
 {
@@ -440,8 +454,13 @@ local void AiSendTransporter(AiForce* force)
     //
     aiunit=force->Units;
     while( aiunit ) {
+	//	Transporter to unload units
 	if( aiunit->Unit->Type->Transporter ) {
-	    CommandMove(aiunit->Unit, force->GoalX, force->GoalY,
+	    CommandUnload(aiunit->Unit, force->GoalX, force->GoalY, NoUnitP,
+		    FlushCommands);
+	//	Ships to defend transporter
+	} else if( aiunit->Unit->Type->UnitType==UnitTypeNaval ) {
+	    CommandAttack(aiunit->Unit, force->GoalX, force->GoalY, NoUnitP,
 		    FlushCommands);
 	}
 	aiunit=aiunit->Next;
@@ -450,14 +469,19 @@ local void AiSendTransporter(AiForce* force)
 }
 
 /**
+**	Step 3)
 **	Wait for transporters landed.
 **
 **	@param force	Force pointer.
+**
+**	@todo	If units blocks the unload process we should move them away.
+**		FIXME: hangs if the unit can't unloaded.
 */
 local void AiWaitLanded(AiForce* force)
 {
     AiUnit* aiunit;
     int i;
+    int j;
 
     DebugLevel0Fn("Waiting\n");
     //
@@ -466,13 +490,20 @@ local void AiWaitLanded(AiForce* force)
     i=1;
     aiunit=force->Units;
     while( aiunit ) {
-	if( aiunit->Unit->Type->Transporter
-		&& aiunit->Unit->Orders[0].Action==UnitActionStill ) {
-	    DebugLevel0Fn("Unloading\n");
-	    CommandUnload(aiunit->Unit,aiunit->Unit->X,aiunit->Unit->Y,
-		    NoUnitP,FlushCommands);
-	} else {
-	    i=0;
+	if( aiunit->Unit->Type->Transporter ) {
+	    if( aiunit->Unit->Orders[0].Action==UnitActionStill ) {
+		DebugLevel0Fn("Unloading\n");
+		for( j=0; j<MAX_UNITS_ONBOARD; ++j ) {
+		    if( aiunit->Unit->OnBoard[j] ) {
+			CommandUnload(aiunit->Unit,force->GoalX,force->GoalY,
+			    NoUnitP,FlushCommands);
+			i=0;
+			break;
+		    }
+		}
+	    } else {
+		i=0;
+	    }
 	}
 	aiunit=aiunit->Next;
     }
@@ -482,7 +513,8 @@ local void AiWaitLanded(AiForce* force)
 }
 
 /**
-**	Force on attack ride.
+**	Step 4)
+**	Force on attack ride. We attack until there is no unit or enemy left.
 **
 **	@param force	Force pointer.
 */
diff --git a/src/ai/ai_magic.cpp b/src/ai/ai_magic.cpp
index 9230c5ff9..23fc3c1f6 100644
--- a/src/ai/ai_magic.cpp
+++ b/src/ai/ai_magic.cpp
@@ -93,69 +93,96 @@ local void AiInitMagic(void)
 }
 
 /**
-**	Do magic for ogre-mage.
+**	Check if the unit should cast the "bloodlust" spell.
+**
+**	If the spell is available and the unit has enough mana, the surrounding
+**	of the unit is checked if any enemy units are in sight. If enemy units
+**	are in sight the spell is casted on own units in range. 
+**
+**	@param unit	Magic unit.
+**
+**	@return		True, if a spell is casted.
+**
+**	@note This function can also be used for auto bloodlust.
 */
-local void AiDoOgreMage(Unit* unit)
+local int AiBloodlustSpell(Unit* unit)
 {
+    Unit* best;
+    Unit* table[UnitMax];
     int r;
+    int i;
+    int n;
 
-    if( UpgradeIdentAvailable(AiPlayer->Player,"upgrade-bloodlust") ) {
-	if( unit->Mana>AiBloodlust->ManaCost ) {
-	    Unit* table[UnitMax];
-	    int n;
-	    int i;
+    if (UpgradeIdentAvailable(AiPlayer->Player, "upgrade-bloodlust")
+	    && unit->Mana > AiBloodlust->ManaCost) {
 
-	    r=unit->Type->ReactRangeComputer;
-	    n=SelectUnits(unit->X-r,unit->Y-r,unit->X+r+1,unit->Y+r+1,table);
-	    if( n ) {
-		for( i=0; i<n; ++i ) {
-		    Unit* best;
+	r = unit->Type->ReactRangeComputer;
+	n = SelectUnits(unit->X - r, unit->Y - r, unit->X + r + 1,
+	    unit->Y + r + 1, table);
 
-		    // a friend or neutral
-		    if( !IsEnemy(unit->Player,table[i]) ) {
-			continue;
-		    }
-		    if( !table[i]->Type->CanAttack ) {
-			continue;
-		    }
-		    //
-		    //	We have an enemy in range.
-		    //
-		    best=NoUnitP;
-		    for( i=0; i<n; ++i ) {
-			if( table[i]==unit 
-				|| table[i]->Bloodlust
-				|| !table[i]->Type->CanAttack ) {
-			    continue;
-			}
-			// Allied unit
-			// FIXME: should ally to self
-			if( unit->Player!=table[i]->Player && 
-				!IsAllied(unit->Player,table[i]) ) {
-			    continue;
-			}
-			r=MapDistanceBetweenUnits(unit,table[i]);
-			DebugLevel0Fn("Distance %d\n",r);
-			if( r<=1 ) {
-			    DebugLevel0Fn("`%s' cast bloodlust\n"
-				_C_ unit->Type->Ident);
-			    CommandSpellCast(unit,0,0,table[i],
-				AiBloodlust,FlushCommands);
-			    break;
-			}
-			if( r==2 ) {
-			    best=table[i];
-			}
-		    }
-		    if( best ) {
-			CommandSpellCast(unit,0,0,best,
-			    AiBloodlust,FlushCommands);
-		    }
-		    break;
+	for (i = 0; i < n; ++i) {
+
+	    // an enemy which can attack
+	    if (!IsEnemy(unit->Player, table[i])
+		    || (!table[i]->Type->CanAttack)) {
+		continue;
+	    }
+	    //
+	    //      We have an enemy in range.
+	    //
+	    best = NoUnitP;
+	    for (i = 0; i < n; ++i) {
+		// not self, not already bloodlust and can attack
+		if (table[i] == unit || table[i]->Bloodlust
+			|| !table[i]->Type->CanAttack) {
+		    continue;
+		}
+		// Allied unit
+		// FIXME: should ally to self
+		if (unit->Player != table[i]->Player
+			&& !IsAllied(unit->Player, table[i])) {
+		    continue;
+		}
+		r = MapDistanceBetweenUnits(unit, table[i]);
+		DebugLevel0Fn("Distance %d\n", r);
+		if (r <= 1) {
+		    DebugLevel0Fn("`%s' cast bloodlust\n" _C_ unit->Type->
+			Ident);
+		    CommandSpellCast(unit, 0, 0, table[i], AiBloodlust,
+			FlushCommands);
+		    return 1;
+		}
+		if (r == 2) {
+		    best = table[i];
 		}
 	    }
+	    if (best) {
+		CommandSpellCast(unit, 0, 0, best, AiBloodlust,
+		    FlushCommands);
+		return 1;
+	    }
+	    break;
 	}
     }
+    return 0;
+}
+
+/**
+**	Check if the unit should cast the "eye of vision" spell.
+**
+**	If the unit has nothing to do and the spell is available and the unit
+**	has full mana cast with a change of 1/32 the spell. The spells does
+**	nothing, because the AI cheats and already knows the surroundings.
+**
+**	@param unit	Magic unit.
+**
+**	@return		True, if a spell is casted.
+**
+**	@note This function can also be used for auto eye of vision.
+*/
+local int AiEyeOfVisionSpell(Unit* unit)
+{
+    int r;
 
     if( unit->Orders[0].Action==UnitActionStill ) {
 	if( UpgradeIdentAvailable(AiPlayer->Player,"upgrade-eye-of-kilrogg")
@@ -169,16 +196,41 @@ local void AiDoOgreMage(Unit* unit)
 
 		    CommandSpellCast(unit,unit->X,unit->Y,NoUnitP,
 			AiEyeOfVision,FlushCommands);
+		    return 1;
 		}
 	    }
 	}
     }
+    return 0;
 }
 
 /**
-**	Do magic for paladin.
+**	Do magic for ogre-mage.
 */
-local void AiDoPaladin(Unit* unit)
+local void AiDoOgreMage(Unit* unit)
+{
+    if( AiBloodlustSpell(unit) ) {
+	return;
+    }
+    if( AiEyeOfVisionSpell(unit) ) {
+	return;
+    }
+}
+
+/**
+**	Check if the unit should cast the "holy vision" spell.
+**
+**	If the unit has nothing to do and the spell is available and the unit
+**	has full mana cast with a change of 1/32 the spell. The spells does
+**	nothing, because the AI cheats and already knows the surroundings.
+**
+**	@param unit	Magic unit.
+**
+**	@return		True, if a spell is casted.
+**
+**	@note This function can also be used for auto holy vision.
+*/
+local int AiHolyVisionSpell(Unit* unit)
 {
     int r;
 
@@ -200,10 +252,22 @@ local void AiDoPaladin(Unit* unit)
 		    y=SyncRand()%TheMap.Height;
 		    CommandSpellCast(unit,x,y,NoUnitP,
 			AiHolyVision,FlushCommands);
+		    return 1;
 		}
 	    }
 	}
     }
+    return 0;
+}
+
+/**
+**	Do magic for paladin.
+*/
+local void AiDoPaladin(Unit* unit)
+{
+    if( AiHolyVisionSpell(unit) ) {
+	return;
+    }
 }
 
 /**
diff --git a/src/ai/ai_plan.cpp b/src/ai/ai_plan.cpp
index 64422a46d..88f437afc 100644
--- a/src/ai/ai_plan.cpp
+++ b/src/ai/ai_plan.cpp
@@ -288,6 +288,8 @@ local int AiFindTarget(const Unit* unit,unsigned char* matrix,
 		    }
 
 		    // Check targets on tile?
+		    // FIXME: the move code didn't likes a shore building as
+		    //		target
 		    if( EnemyOnMapTile(unit,x,y) ) {
 			DebugLevel0Fn("Target found %d,%d-%d\n"
 				_C_ x _C_ y _C_ state);
diff --git a/src/ai/script_ai.cpp b/src/ai/script_ai.cpp
index 77965a526..8863e57a2 100644
--- a/src/ai/script_ai.cpp
+++ b/src/ai/script_ai.cpp
@@ -776,7 +776,7 @@ local SCM CclAiSleep(SCM value)
 {
     int i;
 
-    DebugLevel0Fn("%d %d\n",FrameCounter,AiPlayer->SleepFrames);
+    DebugLevel3Fn("%d %d\n",FrameCounter,AiPlayer->SleepFrames);
     if( AiPlayer->SleepFrames ) {
 	if( AiPlayer->SleepFrames<FrameCounter ) {
 	    AiPlayer->SleepFrames=0;