From 35f03ab9076bbfcab956afc6ef255c0c0f439448 Mon Sep 17 00:00:00 2001
From: Kalle Olavi Niemitalo <kon@iki.fi>
Date: Sun, 30 Oct 2011 17:15:45 +0200
Subject: [PATCH 2/2] Save AI state in AiState[AiPlayer()]

---
 doc/scripts/ai.html           |   56 ++++++++-
 doc/scripts/index.html        |    2 +
 engine/ai/ai.cpp              |   41 +++++--
 scripts/ai.lua                |   20 ++--
 scripts/ais/air_attack.lua    |   50 ++++++---
 scripts/ais/attackdefense.lua |  263 +++++++++++++++++++++--------------------
 scripts/ais/blitz.lua         |  103 ++++++++++-------
 scripts/ais/broke.lua         |  202 +++++++++++++++++---------------
 scripts/ais/default.lua       |  113 ++++++++++--------
 scripts/ais/just_attack.lua   |   86 ++++++++------
 scripts/ais/naval.lua         |  116 ++++++++++--------
 scripts/ais/passive.lua       |   17 ++-
 scripts/ais/rush.lua          |  103 ++++++++++-------
 scripts/ais/spacious.lua      |  171 ++++++++++++++-------------
 scripts/ais/tankrush.lua      |   99 +++++++++-------
 scripts/ais/zombies.lua       |  103 ++++++++++-------
 scripts/boswars.lua           |    4 +-
 17 files changed, 892 insertions(+), 657 deletions(-)
 mode change 100755 => 100644 scripts/ais/broke.lua

diff --git a/doc/scripts/ai.html b/doc/scripts/ai.html
index 6876902..88f01cc 100644
--- a/doc/scripts/ai.html
+++ b/doc/scripts/ai.html
@@ -30,6 +30,7 @@
 <a href="index.html">LUA Index</a>
 <hr>
 <a href="#AiTypes">AiTypes</a>
+<a href="#AiState">AiState</a>
 <a href="#DefineAiType">DefineAiType</a>
 <a href="#DefineAiHelper">DefineAiHelper</a>
 <a href="#AiCheckForce">AiCheckForce</a>
@@ -102,11 +103,22 @@ entry that is a table with these keys:</p>
   <dd>Name of the AI the Player sees and which gets translated.
     The engine does not use this; only the Lua code does.</dd>
   <dt>Init = function or nil</dt>
-  <dd>Initializes the AI state of the player to whatever the AI type
-    requires.  When starting a new game, the engine calls the Init
-    function for each player that is going to use this AI type.
-    <b>FIXME: not yet correctly implemented.</b></dd>
-  <dt>EachSecond = function</dt>
+  <dd id="AiTypes.Init">Initializes
+    <a href="#AiState">AiState</a>[<a href="#AiPlayer">AiPlayer()</a>]
+    to whatever the AI type requires.
+    When starting a new game, the engine calls the Init function
+    for each player that is going to use this AI type.
+    Typically, the Init function creates a table and writes it to
+    AiState[AiPlayer()], and the <a href="#AiTypes.EachSecond">EachSecond</a>
+    function will then maintain that table.
+    Even if the AI type does not use AiState for its own purposes,
+    the Init function must regardless write a non-nil value to
+    AiState[AiPlayer()], so that the engine will know the AI state
+    has been initialized and not call the Init function again
+    if the game is later saved and loaded.
+    (Of course, if the AI type has no Init function, then there
+    is no such requirement.)</dd>
+  <dt id="AiTypes.EachSecond">EachSecond = function</dt>
   <dd>Decides AI actions: primarily, what to build.
     The engine calls the EachSecond function once per second
     (at the default game speed).</dd>
@@ -123,6 +135,38 @@ modify it or its elements directly.  Instead, it should call
 <a href="#DefineAiType">DefineAiType</a>, so that the engine
 knows to update its data structures accordingly.</p>
 
+<a name="AiState"></a>
+<h3>AiState</h3>
+
+<p>The AI state of each player.
+The Lua scripts of each AI type should write all persistent
+state into AiState[<a href="#AiPlayer">AiPlayer()</a>]
+so that the engine will save and load it as part of the game,
+and so that the engine can easily clear the state when
+starting a new game.</p>
+
+<p>Typically, AiState[AiPlayer()] is a table created by the
+<a href="#AiTypes.Init">Init</a> function of the AI type;
+the Lua scripts can then use the fields of this table as they please.
+If AiState[AiPlayer()] is nil, that means the engine has not
+called the Init function yet.</p>
+
+<p>The engine does not currently preserve object identity when saving
+and loading a game.  That means, if the AI script does</p>
+
+<pre>
+   local x = {}
+   AiState[AiPlayer()].first = x
+   AiState[AiPlayer()].second = x
+</pre>
+
+<p>then a comparison like
+<code>AiState[AiPlayer()].first == AiState[AiPlayer()].second</code>
+will initially return true, but if the game is saved and loaded, the
+comparison will start returning false because <code>first</code> and
+<code>second</code> now refer to different empty tables.
+However, scripts should not depend on this behavior.</p>
+
 <h2>Functions</h2>
 <a name="DefineAiType"></a>
 <h3>DefineAiType(table)</h3>
@@ -470,7 +514,7 @@ additional could be requested.
 <h3>AiPlayer()</h3>
 
 Return the player number of the running AI.
-
+This is often used for indexing the <a href="#AiState">AiState</a> table.
 
 <h4>Example</h4>
 
diff --git a/doc/scripts/index.html b/doc/scripts/index.html
index 6824309..71950f4 100644
--- a/doc/scripts/index.html
+++ b/doc/scripts/index.html
@@ -93,6 +93,8 @@
 <dd></dd>
 <dt><a href="ai.html#AiSleep">AiSleep</a></dt>
 <dd></dd>
+<dt><a href="ai.html#AiState">AiState</a></dt>
+<dd></dd>
 <dt><a href="ai.html#AiTypes">AiTypes</a></dt>
 <dd></dd>
 <dt><a href="ai.html#AiWait">AiWait</a></dt>
diff --git a/engine/ai/ai.cpp b/engine/ai/ai.cpp
index 2c08218..ee28133 100644
--- a/engine/ai/ai.cpp
+++ b/engine/ai/ai.cpp
@@ -55,10 +55,11 @@
 ** Initialise all global varaibles and structures.
 ** Called before AiInit, or before game loading.
 **
-** ::AiInit(::Player)
+** ::AiInit(::CPlayer)
 **
 ** Called for each player, to setup the AI structures
-** Player::Aiin the player structure. It can use Player::AiName to
+** CPlayer::Ai in the player structure and possibly call
+** AI initialization scripts. It can use CPlayer::AiName to
 ** select different AI's.
 **
 ** ::CleanAi()
@@ -153,17 +154,24 @@ PlayerAi *AiPlayer;             /// Current AI player
 ----------------------------------------------------------------------------*/
 
 /**
-**  Execute the AI Script.
+**  Execute a Lua function of the AI type.
 */
-static void AiExecuteScript()
+static void AiExecuteFunction(const char *field)
 {
 	if (AiPlayer->AiType != NULL)
 	{
 		lua_getfield(Lua, LUA_GLOBALSINDEX, "AiTypes");
 		lua_getfield(Lua, -1, AiPlayer->AiType->Name.c_str());
-		lua_getfield(Lua, -1, "EachSecond");
-		LuaCall(0, 1);	// removes the function from the stack
-		lua_pop(Lua, 2); // remove the tables too
+		lua_getfield(Lua, -1, field);
+		if (!lua_isnil(Lua, -1))
+		{
+			LuaCall(0, 1);	// removes the function from the stack
+			lua_pop(Lua, 2); // remove the tables too
+		}
+		else
+		{
+			lua_pop(Lua, 3);
+		}
 	}
 }
 
@@ -481,6 +489,23 @@ void AiInit(CPlayer *player)
 
 	pai->AiType = ait;
 	player->Ai = pai;
+
+	// Initialize the AI state of the player if it has not been
+	// already initialized, i.e. we're not loading a saved game.
+	lua_getfield(Lua, LUA_GLOBALSINDEX, "AiState"); // stack: AiState
+	lua_pushnumber(Lua, player->Index);             // stack: AiState, player
+	lua_gettable(Lua, -2);                          // stack: AiState, AiState[player]
+	bool needs_init = lua_isnil(Lua, -1);
+	lua_pop(Lua, 2);
+	if (needs_init)
+	{
+		AiPlayer = pai;
+		AiExecuteFunction("Init");
+	}
+
+	// Cause a reproduceable crash if something expected us to
+	// preserve the value of AiPlayer.
+	AiPlayer = NULL;
 }
 
 /**
@@ -1048,7 +1073,7 @@ void AiEachSecond(CPlayer *player)
 	Assert(AiPlayer != NULL);
 
 	// Advance script
-	AiExecuteScript();
+	AiExecuteFunction("EachSecond");
 
 	// Look if everything is fine.
 	AiCheckUnits();
diff --git a/scripts/ai.lua b/scripts/ai.lua
index 23f96f5..200cf26 100644
--- a/scripts/ai.lua
+++ b/scripts/ai.lua
@@ -30,20 +30,22 @@
 -- Table of registered AI types in BOS.  See HTML documentation for details.
 AiTypes = {}
 
+-- AI state of each player.  See HTML documentation for details.
+-- Initialize to nil so we'll notice if ClearAiState is not called
+-- when it should.
+AiState = nil
+
 -- This call examines the buttons that were defined with DefineButton
 -- and figures out which types of units can build/train/repair others.
 -- We don't currently have any "unit-equiv" information to provide here.
 DefineAiHelper()
 
--- Execute all AI init scripts
--- FIXME: needs to be done per player; probably move to C++
-function InitAiScripts()
-  for ident,ai_type in next,AiTypes do
-    -- check if this AI actually has an init script
-    if (ai_type.Init ~= nil) then
-      ai_type.Init()
-    end
-  end
+-- Clear the AI state of each player.  This is called before starting
+-- a new game, and before loading a saved game.  This does not call AI
+-- initialization functions; the engine will do that after it has
+-- selected the AI type for each player.
+function ClearAiState()
+  AiState = {}
 end
 
 -- Find and load all Ais
diff --git a/scripts/ais/air_attack.lua b/scripts/ais/air_attack.lua
index 2bf144b..235e784 100644
--- a/scripts/ais/air_attack.lua
+++ b/scripts/ais/air_attack.lua
@@ -29,24 +29,33 @@
 --      $Id: air_attack.lua  $
 --
 
-local player
+-- What we registered in AiTypes.
+local this_ai_type
 
-local function AiLoop(loop_funcs, loop_pos)
-  local ret
+-- Same as AiState[AiPlayer()].  Valid only during AiLoop.
+local state
 
-  player = AiPlayer() + 1
+local function AiLoop(funcs)
+  state = AiState[AiPlayer()]
   while (true) do
-    ret = loop_funcs[loop_pos[player]]()
+    local ret = funcs[state.loop_pos]()
     if (ret) then
-        break
+      break
     end
-    loop_pos[player] = loop_pos[player] + 1
+    state.loop_pos = state.loop_pos + 1
   end
   return true
 end
 
-function InitAiScripts_air_attack()
-  ai_pos      = {1, 1, 1, 1, 1, 1, 1, 1}
+local function LocalDebugPrint(text)
+  DebugPrint(this_ai_type.Ident .. " player " .. AiPlayer() .. " " .. text)
+  return false
+end
+
+local function InitAiScripts_air_attack()
+  AiState[AiPlayer()] = {
+    loop_pos = 1
+  }
 end
 
 local ai_funcs = {
@@ -89,14 +98,23 @@ local ai_funcs = {
   function() return AiNeed("unit-powerplant") end,
   function() return AiNeed("unit-magmapump") end,
   function() return AiSleep((SyncRand(60)+40)*GameSettings.Difficulty) end, 
+
+  function()
+    LocalDebugPrint("Reached the end of AI script and will loop");
+    state.loop_pos = 0;
+    return false
+  end,
 }
 
-function AiAirAttack()
-    return AiLoop(ai_funcs, ai_pos)
+local function AiAirAttack()
+  LocalDebugPrint("Script position " .. AiState[AiPlayer()].loop_pos);
+  return AiLoop(ai_funcs)
 end
 
-DefineAiType({
-	Ident = "ai-air_attack",
-	Name = _("Air attack"),
-	Init = InitAiScripts_air_attack,
-	EachSecond = AiAirAttack })
+this_ai_type = {
+  Ident = "ai-air_attack",
+  Name = _("Air attack"),
+  Init = InitAiScripts_air_attack,
+  EachSecond = AiAirAttack,
+}
+DefineAiType(this_ai_type)
diff --git a/scripts/ais/attackdefense.lua b/scripts/ais/attackdefense.lua
index 68970fa..0e71082 100644
--- a/scripts/ais/attackdefense.lua
+++ b/scripts/ais/attackdefense.lua
@@ -31,129 +31,45 @@
 --      $Id: attackdefense.lua  $
 --
 
-local player
+-- What we registered in AiTypes.
+local this_ai_type
 
-local function AiLoop(loop_funcs, loop_pos)
-  local ret
+-- Same as AiState[AiPlayer()].  Valid only during AiLoop.
+local state
 
-  player = AiPlayer() + 1
+local function AiLoop(funcs)
+  state = AiState[AiPlayer()]
   while (true) do
-    ret = loop_funcs[loop_pos[player]]()
+    local ret = funcs[state.loop_pos]()
     if (ret) then
-     break
+      break
     end
-    loop_pos[player] = loop_pos[player] + 1
+    state.loop_pos = state.loop_pos + 1
   end
   return true
 end
 
-function InitAiScripts_attackdefense()
-  ai_pos      = {1, 1, 1, 1, 1, 1, 1, 1}
-  ai_loop_pos = {1, 1, 1, 1, 1, 1, 1, 1}
-  hotspotexists = nil
+local function LocalDebugPrint(text)
+  DebugPrint(this_ai_type.Ident .. " player " .. AiPlayer() .. " " .. text)
+  return false
 end
 
-local ai_loop_funcs = {
-
-  -- Attack wave
-  function() 
-    -- print("=== Ai " .. player .. " is starting loop.");
-    -- print("Ai " .. player .. " is making 6 bazoos.");
-    return AiForce(4, {"unit-bazoo", 6}) 
-  end,
-  function() return AiWaitForce(4) end,
-  function() return AiSleep((SyncRand(100)+40)*GameSettings.Difficulty) end,
-  function()
-    -- print("Ai " .. player .. " is attacking with force 4 (6 bazoos).");
-    return AiAttackWithForce(4)
-  end,
-
-  function() return AiForce(4, {"unit-grenadier", 7}) end,
-  function() return AiWaitForce(4) end,
-  function() return AiSleep((SyncRand(100)+40)*GameSettings.Difficulty) end,
-  function() return AiAttackWithForce(4) end,
-
-  function() return AiForce(4, {"unit-tank", 3}) end,
-  function() return AiWaitForce(4) end,
-  function() return AiSleep((SyncRand(100)+40)*GameSettings.Difficulty) end,
-  function() return AiAttackWithForce(4) end,
-
-  function() return AiForce(4, {"unit-jet", 2}) end,
-  function() return AiWaitForce(4) end,
-  function() return AiSleep((SyncRand(100)+40)*GameSettings.Difficulty) end,
-  function() return AiAttackWithForce(4) end,
-
-  function() return AiForce(4, {"unit-rtank", 2}) end,
-  function() return AiWaitForce(4) end,
-  function() return AiSleep((SyncRand(100)+40)*GameSettings.Difficulty) end,
-  function() return AiAttackWithForce(4) end,
-
-  -- Defense (resources and static guns)
-  function() return AiSet("unit-nukepowerplant", 2) end,
-  function() return AiSet("unit-gturret", 5) end,
-  function() return AiSet("unit-vfac", 2) end,
-  function() return AiSet("unit-camp", 2) end,
-  function() return AiSet("unit-engineer", 8) end,
-  function() return AiSet("unit-magmapump",7) end,
-  function() return AiSet("unit-biggunturret", 3) end,
-
-  -- Defense force 0 is sent to a building under attack
-  function() return AiForce(0, {"unit-assault", 3,
-                                "unit-bazoo", 2,
-                                "unit-buggy", 2,
-                                "unit-jet", 1,
-                                "unit-tank", 1}) end,
-
-  -- Attack wave (for buildings with weak defense)
-  function() return AiForce(5, {"unit-assault", 4,
-                                "unit-bazoo", 3,
-                                "unit-rtank", 1,
-                                "unit-jet", 1,
-                                "unit-bomber", 1,
-                                "unit-tank", 2}) end,
-  function() return AiWaitForce(5) end,
-  function() return AiSleep((SyncRand(100)+40)*GameSettings.Difficulty) end,
-  function() return AiAttackWithForce(5) end,
-
-  -- Attack wave (slow units)
-  function() return AiForce(6, {"unit-bazoo", 12,
-                                "unit-artil", 2,
-                                "unit-chopper", 2}) end,
-  function() return AiWaitForce(6) end,
-  function() return AiSleep((SyncRand(100)+40)*GameSettings.Difficulty) end,
-  function() return AiAttackWithForce(6) end,
-
-  -- Attack wave (fast units)
-  function() return AiForce(7, {"unit-grenadier", 6,
-                                "unit-buggy", 2,
-                                "unit-heli", 1,
-                                "unit-jet", 1}) end,
-  function() return AiWaitForce(7) end,
-  function() return AiSleep((SyncRand(100)+40)*GameSettings.Difficulty) end,
-  function() return AiAttackWithForce(7) end,
-
-  -- Defense
-  function() return AiSet("unit-biggunturret", 6) end,
-  -- Let's not build too many cannons, since the 
-  -- AI places them mostly in useless locations:
-  function() return AiSet("unit-cannon", 1) end,
-  -- A radar is needed for the cannon to see its targets:
-  function() return AiSet("unit-radar", 1) end,
-
-  function()
-    print("Ai " .. player .. " is looping");
-    ai_loop_pos[player] = 0;
-    return false
-  end,
-}
+local function InitAiScripts_attackdefense()
+  AiState[AiPlayer()] = {
+    loop_pos = 1,
+    loop_start = nil,
+    hotspotexists = nil,
+    build_order = nil,
+  }
+end
 
 local function HotSpotExists()
-  if (hotspotexists == nil) then
+  if (state.hotspotexists == nil) then
     local hotspot = UnitTypeByIdent("unit-hotspot")
     local count = Players[PlayerNumNeutral].UnitTypesCount[hotspot.Slot]
-    hotspotexists = (count ~= 0)
+    state.hotspotexists = (count ~= 0)
   end
-  return hotspotexists
+  return state.hotspotexists
 end
 
 local function CheckPower()
@@ -187,33 +103,30 @@ local ai_funcs = {
 
   -- Build magma pump or power plant first depending on resources
   function()
-    local order = GetBuildOrder()
-    if (order[1] ~= nil) then
-      return AiNeed(order[1])
+    state.build_order = GetBuildOrder()
+    if (state.build_order[1] ~= nil) then
+      return AiNeed(state.build_order[1])
     else
       return false
     end
   end,
   function()
-    local order = GetBuildOrder()
-    if (order[1] ~= nil) then
-      return AiWait(order[1])
+    if (state.build_order[1] ~= nil) then
+      return AiWait(state.build_order[1])
     else
       return false
     end
   end,
   function()
-    local order = GetBuildOrder()
-    if (order[2] ~= nil) then
-      return AiNeed(order[2])
+    if (state.build_order[2] ~= nil) then
+      return AiNeed(state.build_order[2])
     else
       return false
     end
   end,
   function()
-    local order = GetBuildOrder()
-    if (order[2] ~= nil) then
-      return AiWait(order[2])
+    if (state.build_order[2] ~= nil) then
+      return AiWait(state.build_order[2])
     else
       return false
     end
@@ -294,16 +207,110 @@ local ai_funcs = {
   function() return AiNeed("unit-aircraftfactory") end,
   -- function() return AiWait("unit-aircraftfactory") end,
 
-  function() return AiLoop(ai_loop_funcs, ai_loop_pos) end,
+  -- ============================================================
+
+  -- Attack wave
+  function() 
+    LocalDebugPrint("is starting loop.");
+    state.loop_start = state.loop_pos;
+    LocalDebugPrint("is making 6 bazoos.");
+    return AiForce(4, {"unit-bazoo", 6}) 
+  end,
+  function() return AiWaitForce(4) end,
+  function() return AiSleep((SyncRand(100)+40)*GameSettings.Difficulty) end,
+  function()
+    LocalDebugPrint("is attacking with force 4 (6 bazoos).");
+    return AiAttackWithForce(4)
+  end,
+
+  function() return AiForce(4, {"unit-grenadier", 7}) end,
+  function() return AiWaitForce(4) end,
+  function() return AiSleep((SyncRand(100)+40)*GameSettings.Difficulty) end,
+  function() return AiAttackWithForce(4) end,
+
+  function() return AiForce(4, {"unit-tank", 3}) end,
+  function() return AiWaitForce(4) end,
+  function() return AiSleep((SyncRand(100)+40)*GameSettings.Difficulty) end,
+  function() return AiAttackWithForce(4) end,
+
+  function() return AiForce(4, {"unit-jet", 2}) end,
+  function() return AiWaitForce(4) end,
+  function() return AiSleep((SyncRand(100)+40)*GameSettings.Difficulty) end,
+  function() return AiAttackWithForce(4) end,
+
+  function() return AiForce(4, {"unit-rtank", 2}) end,
+  function() return AiWaitForce(4) end,
+  function() return AiSleep((SyncRand(100)+40)*GameSettings.Difficulty) end,
+  function() return AiAttackWithForce(4) end,
+
+  -- Defense (resources and static guns)
+  function() return AiSet("unit-nukepowerplant", 2) end,
+  function() return AiSet("unit-gturret", 5) end,
+  function() return AiSet("unit-vfac", 2) end,
+  function() return AiSet("unit-camp", 2) end,
+  function() return AiSet("unit-engineer", 8) end,
+  function() return AiSet("unit-magmapump",7) end,
+  function() return AiSet("unit-biggunturret", 3) end,
+
+  -- Defense force 0 is sent to a building under attack
+  function() return AiForce(0, {"unit-assault", 3,
+                                "unit-bazoo", 2,
+                                "unit-buggy", 2,
+                                "unit-jet", 1,
+                                "unit-tank", 1}) end,
+
+  -- Attack wave (for buildings with weak defense)
+  function() return AiForce(5, {"unit-assault", 4,
+                                "unit-bazoo", 3,
+                                "unit-rtank", 1,
+                                "unit-jet", 1,
+                                "unit-bomber", 1,
+                                "unit-tank", 2}) end,
+  function() return AiWaitForce(5) end,
+  function() return AiSleep((SyncRand(100)+40)*GameSettings.Difficulty) end,
+  function() return AiAttackWithForce(5) end,
+
+  -- Attack wave (slow units)
+  function() return AiForce(6, {"unit-bazoo", 12,
+                                "unit-artil", 2,
+                                "unit-chopper", 2}) end,
+  function() return AiWaitForce(6) end,
+  function() return AiSleep((SyncRand(100)+40)*GameSettings.Difficulty) end,
+  function() return AiAttackWithForce(6) end,
+
+  -- Attack wave (fast units)
+  function() return AiForce(7, {"unit-grenadier", 6,
+                                "unit-buggy", 2,
+                                "unit-heli", 1,
+                                "unit-jet", 1}) end,
+  function() return AiWaitForce(7) end,
+  function() return AiSleep((SyncRand(100)+40)*GameSettings.Difficulty) end,
+  function() return AiAttackWithForce(7) end,
+
+  -- Defense
+  function() return AiSet("unit-biggunturret", 6) end,
+  -- Let's not build too many cannons, since the 
+  -- AI places them mostly in useless locations:
+  function() return AiSet("unit-cannon", 1) end,
+  -- A radar is needed for the cannon to see its targets:
+  function() return AiSet("unit-radar", 1) end,
+
+  function()
+    LocalDebugPrint("Reached the end of AI script and will loop");
+    state.loop_pos = state.loop_start - 1; -- AiLoop will immediately increment it.
+    return false
+  end,
 }
 
-function AiAttackDefense()
---    print(AiPlayer() .. " position ".. ai_pos[AiPlayer() + 1]);
-    return AiLoop(ai_funcs, ai_pos)
+local function AiAttackDefense()
+  LocalDebugPrint("Script position " .. AiState[AiPlayer()].loop_pos);
+  return AiLoop(ai_funcs)
 end
 
-DefineAiType({
-	Ident = "ai-attackdefense",
-	Name = _("Attack/Defense"),
-	Init = InitAiScripts_attackdefense,
-	EachSecond = AiAttackDefense })
+this_ai_type = {
+  Ident = "ai-attackdefense",
+  Name = _("Attack/Defense"),
+  Init = InitAiScripts_attackdefense,
+  EachSecond = AiAttackDefense,
+}
+DefineAiType(this_ai_type)
diff --git a/scripts/ais/blitz.lua b/scripts/ais/blitz.lua
index 0940d9a..a1c5370 100644
--- a/scripts/ais/blitz.lua
+++ b/scripts/ais/blitz.lua
@@ -29,46 +29,45 @@
 --	$Id: blitz.lua 626 2007-10-14 18:10:03Z timberdragon $
 --
 
-local player
+-- What we registered in AiTypes.
+local this_ai_type
 
-local function AiLoop(loop_funcs, loop_pos)
-  local ret
+-- Same as AiState[AiPlayer()].  Valid only during AiLoop.
+local state
 
-  player = AiPlayer() + 1
+local function AiLoop(funcs)
+  state = AiState[AiPlayer()]
   while (true) do
-    ret = loop_funcs[loop_pos[player]]()
+    local ret = funcs[state.loop_pos]()
     if (ret) then
-     break
+      break
     end
-    loop_pos[player] = loop_pos[player] + 1
+    state.loop_pos = state.loop_pos + 1
   end
   return true
 end
 
-function InitAiScripts_blitz()
-  ai_pos      = {1, 1, 1, 1, 1, 1, 1, 1}
-  ai_loop_pos = {1, 1, 1, 1, 1, 1, 1, 1}
-  hotspotexists = nil
+local function LocalDebugPrint(text)
+  DebugPrint(this_ai_type.Ident .. " player " .. AiPlayer() .. " " .. text)
+  return false
 end
 
-local ai_loop_funcs = {
-  function() print("Looping !"); return false end,
-  function() return AiForce(1, {"unit-assault", 20, 
-                                "unit-grenadier", 8, 
-                                "unit-bazoo", 8}) end,
-  function() return AiWaitForce(1) end,  -- wait until attack party is completed
-  function() return AiSleep(50*GameSettings.Difficulty) end,
-  function() return AiAttackWithForce(1) end,
-  function() ai_loop_pos[player] = 0; return false end,
-}
+local function InitAiScripts_blitz()
+  AiState[AiPlayer()] = {
+    loop_pos = 1,
+    loop_start = nil,
+    hotspotexists = nil,
+    build_order = nil,
+  }
+end
 
 local function HotSpotExists()
-  if (hotspotexists == nil) then
+  if (state.hotspotexists == nil) then
     local hotspot = UnitTypeByIdent("unit-hotspot")
     local count = Players[PlayerNumNeutral].UnitTypesCount[hotspot.Slot]
-    hotspotexists = (count ~= 0)
+    state.hotspotexists = (count ~= 0)
   end
-  return hotspotexists
+  return state.hotspotexists
 end
 
 local function GetBuildOrder()
@@ -93,25 +92,22 @@ local ai_funcs = {
 
   -- Build magma pump or power plant first depending on resources
   function()
-    local order = GetBuildOrder()
-    return AiNeed(order[1])
+    state.build_order = GetBuildOrder()
+    return AiNeed(state.build_order[1])
   end,
   function()
-    local order = GetBuildOrder()
-    return AiWait(order[1])
+    return AiWait(state.build_order[1])
   end,
   function()
-    local order = GetBuildOrder()
-    if (order[2] ~= nil) then
-      return AiNeed(order[2])
+    if (state.build_order[2] ~= nil) then
+      return AiNeed(state.build_order[2])
     else
       return false
     end
   end,
   function()
-    local order = GetBuildOrder()
-    if (order[2] ~= nil) then
-      return AiWait(order[2])
+    if (state.build_order[2] ~= nil) then
+      return AiWait(state.build_order[2])
     else
       return false
     end
@@ -187,16 +183,37 @@ local ai_funcs = {
   function() return AiWaitForce(2) end, 
   function() return AiAttackWithForce(2) end,
 
-  function() return AiLoop(ai_loop_funcs, ai_loop_pos) end,
+  -- ============================================================
+
+  function() 
+    LocalDebugPrint("is starting loop.");
+    state.loop_start = state.loop_pos;
+    return false
+  end,
+
+  function() return AiForce(1, {"unit-assault", 20, 
+                                "unit-grenadier", 8, 
+                                "unit-bazoo", 8}) end,
+  function() return AiWaitForce(1) end,  -- wait until attack party is completed
+  function() return AiSleep(50*GameSettings.Difficulty) end,
+  function() return AiAttackWithForce(1) end,
+
+  function()
+    LocalDebugPrint("Reached the end of AI script and will loop");
+    state.loop_pos = state.loop_start - 1; -- AiLoop will immediately increment it.
+    return false
+  end,
 }
 
-function AiBlitz()
---    print(AiPlayer() .. " position ".. ai_pos[AiPlayer() + 1]);
-    return AiLoop(ai_funcs, ai_pos)
+local function AiBlitz()
+  LocalDebugPrint("Script position " .. AiState[AiPlayer()].loop_pos);
+  return AiLoop(ai_funcs)
 end
 
-DefineAiType({
-	Ident = "ai-blitz",
-	Name = _("Blitz"),
-	Init = InitAiScripts_blitz,
-	EachSecond = AiBlitz })
+this_ai_type = {
+  Ident = "ai-blitz",
+  Name = _("Blitz"),
+  Init = InitAiScripts_blitz,
+  EachSecond = AiBlitz,
+}
+DefineAiType(this_ai_type)
diff --git a/scripts/ais/broke.lua b/scripts/ais/broke.lua
old mode 100755
new mode 100644
index 78a0f3b..045709e
--- a/scripts/ais/broke.lua
+++ b/scripts/ais/broke.lua
@@ -31,85 +31,48 @@
 --      $Id:  $
 --
 
-local player
+-- What we registered in AiTypes.
+local this_ai_type
 
-local function AiLoop(loop_funcs, loop_pos)
-  local ret
+-- Same as AiState[AiPlayer()].  Valid only during AiLoop.
+local state
 
-  player = AiPlayer() + 1
+local function AiLoop(funcs)
+  state = AiState[AiPlayer()]
   while (true) do
-    ret = loop_funcs[loop_pos[player]]()
+    local ret = funcs[state.loop_pos]()
     if (ret) then
-     break
+      break
     end
-    loop_pos[player] = loop_pos[player] + 1
+    state.loop_pos = state.loop_pos + 1
   end
   return true
 end
 
-function InitAiScripts_broke()
-  ai_pos      = {1, 1, 1, 1, 1, 1, 1, 1}
-  ai_loop_pos = {1, 1, 1, 1, 1, 1, 1, 1}
-  hotspotexists = nil
+local function LocalDebugPrint(text)
+  DebugPrint(this_ai_type.Ident .. " player " .. AiPlayer() .. " " .. text)
+  return false
 end
 
-local ai_loop_funcs = {
-  -- function() print("Looping !"); return false end,
-
-  -- Attack wave (land)
-  function() return AiForce(1, {"unit-assault", 4}) end,
-
-    -- Attack wave (air)
-  function() return AiForce(2, {"unit-jet", 2}) end,
-
-  -- Attack wave (vehicles)
-  function() return AiForce(3, {"unit-rtank", 1,
-                                "unit-tank", 3}) end,
-
-  -- Attack wave (mixed land)
-  function() return AiForce(4, {"unit-buggy", 2,
-                                "unit-artil", 1}) end,
-
-  -- Attack wave (air)
-  function() return AiForce(5, {"unit-chopper", 1}) end,
-
-  -- Attack wave (land, slow)
-  function() return AiForce(6, {"unit-bazoo", 6}) end,
-
-  -- Defense force 0 is sent to a building under attack
-  function() return AiForce(0, {"unit-assault", 4,
-                                "unit-bazoo", 3,
-                                "unit-grenadier", 3}) end,
-
-  -- wait 10 to 40 s:
-  function() return AiSleep((SyncRand(900)+300) * GameSettings.Difficulty) end,
-
-  function()
-    local f
-    for f=1,6 do
-      if (AiCheckForce(f)) then
-        -- print("Ai " .. player .. " is attacking with force " .. f .. ".");
-        -- AiWaitForce(f);
-        return AiAttackWithForce(f)
-      end
-    end
-    return AiSleep((SyncRand(100)+40) * GameSettings.Difficulty)
-  end,
-
-
-  function() ai_loop_pos[player] = 0; return false end,
-}
+local function InitAiScripts_broke()
+  AiState[AiPlayer()] = {
+    loop_pos = 1,
+    loop_start = nil,
+    hotspotexists = nil,
+    build_order = nil,
+  }
+end
 
 local function HotSpotExists()
-  if (hotspotexists == nil) then
+  if (state.hotspotexists == nil) then
     local hotspot = UnitTypeByIdent("unit-hotspot")
     local whotspot = UnitTypeByIdent("unit-weakhotspot")
     local count = Players[PlayerNumNeutral].UnitTypesCount[hotspot.Slot]
     local wcount = Players[PlayerNumNeutral].UnitTypesCount[whotspot.Slot]
-    -- print ("This map has " .. count .. " hotspots and " .. wcount .. " weak hotspots.")
-    hotspotexists = ((count + wcount) ~= 0)
+    LocalDebugPrint("This map has " .. count .. " hotspots and " .. wcount .. " weak hotspots.")
+    state.hotspotexists = ((count + wcount) ~= 0)
   end
-  return hotspotexists
+  return state.hotspotexists
 end
 
 local function CheckPower()
@@ -141,37 +104,32 @@ end
 local ai_funcs = {
   -- Build magma pump or power plant first depending on resources
   function()
-    local order = GetBuildOrder()
-    return AiNeed(order[1])
+    state.build_order = GetBuildOrder()
+    return AiNeed(state.build_order[1])
   end,
   function()
-    -- local order = GetBuildOrder()
-    -- print("Player" .. AiPlayer() .. " waits for " .. order[1]);
+    LocalDebugPrint("waits for " .. state.build_order[1]);
     return false
   end,
   function()
-    local order = GetBuildOrder()
-    return AiWait(order[1])
+    return AiWait(state.build_order[1])
   end,
   function()
-    local order = GetBuildOrder()
-    if (order[2] ~= nil) then
-      return AiNeed(order[2])
+    if (state.build_order[2] ~= nil) then
+      return AiNeed(state.build_order[2])
     else
       return false
     end
   end,
   function()
-    local order = GetBuildOrder()
-    if (order[2] ~= nil) then    
-      -- print("Player" .. AiPlayer() .. " waits for " .. order[2]);
+    if (state.build_order[2] ~= nil) then    
+      LocalDebugPrint("waits for " .. state.build_order[2]);
     end
     return false
   end,
   function()
-    local order = GetBuildOrder()
-    if (order[2] ~= nil) then
-      return AiWait(order[2])
+    if (state.build_order[2] ~= nil) then
+      return AiWait(state.build_order[2])
     else
       return false
     end
@@ -179,7 +137,7 @@ local ai_funcs = {
 
   function() return AiNeed("unit-nukepowerplant") end,
   function()
-    -- print("Player" .. AiPlayer() .. " waits for nuke powerplant.");
+    LocalDebugPrint("waits for nuke powerplant.");
     return false
   end,
   function() return AiWait("unit-nukepowerplant") end,
@@ -204,7 +162,7 @@ local ai_funcs = {
   function() return AiForce(0, {"unit-assault", 4}) end,
   function() return AiWaitForce(0) end,
   function()
-    -- print("Player" .. AiPlayer() .. " force 0 finished.");
+    LocalDebugPrint("force 0 finished.");
     return false
   end,
   function() return AiSet("unit-camp", 2) end,
@@ -213,13 +171,13 @@ local ai_funcs = {
   function() return AiForce(1, {"unit-grenadier", 6}) end,
   function() return AiWaitForce(1) end,
   function()
-    -- print("Player" .. AiPlayer() .. " force 1 (6 grenadiers) finished.");
+    LocalDebugPrint("force 1 (6 grenadiers) finished.");
     return false
   end,
   function() return AiSleep((SyncRand(500)+500)*GameSettings.Difficulty) end,
   function() return AiAttackWithForce(1) end,
   function()
-    -- print("Player" .. AiPlayer() .. " force 1 (6 grenadiers) attacking.");
+    LocalDebugPrint("force 1 (6 grenadiers) attacking.");
     return false;
   end,
 
@@ -228,7 +186,7 @@ local ai_funcs = {
   function() return AiWaitForce(1) end,
 
   function()
-    -- print("Player" .. AiPlayer() .. " force 1 (8 assaults, 2 grenadiers) finished.");
+    LocalDebugPrint("force 1 (8 assaults, 2 grenadiers) finished.");
     return false;
   end,
   function() return AiAttackWithForce(1) end,
@@ -239,21 +197,21 @@ local ai_funcs = {
   function() return AiSet("unit-gturret", 2) end,
   
   function()
-    -- print("Player" .. AiPlayer() .. " waiting for vfac.");
+    LocalDebugPrint("waiting for vfac.");
     return false
   end,
   function() return AiWait("unit-vfac") end,
 
   -- Attack wave
   function()
-    -- print("Player" .. AiPlayer() .. " vfac finished.");
+    LocalDebugPrint("vfac finished.");
     return false
   end,
   function() return AiForce(1, {"unit-assault", SyncRand(5)+1, 
                                 "unit-buggy", SyncRand(2)+1}) end,
   
   function()
-    -- print("Player" .. AiPlayer() .. " waiting for force 1 (assaults + buggies).");
+    LocalDebugPrint("waiting for force 1 (assaults + buggies).");
     return false
   end,
   function() return AiWaitForce(1) end,
@@ -261,7 +219,7 @@ local ai_funcs = {
   function() return AiAttackWithForce(1) end,
 
   function()
-    -- print("Player" .. AiPlayer() .. " attacking with force 1.");
+    LocalDebugPrint("attacking with force 1.");
     return false;
   end,
   function() return AiSet("unit-biggunturret", 6) end,
@@ -270,21 +228,75 @@ local ai_funcs = {
 
   -- Start flying:
   function()
-    -- print("Player" .. AiPlayer() .. " more biggunturret, vfac, gturret.");
+    LocalDebugPrint("more biggunturret, vfac, gturret.");
     return false
   end,
   function() return AiSet("unit-aircraftfactory", 2) end,
 
-  function() return AiLoop(ai_loop_funcs, ai_loop_pos) end,
+  -- ============================================================
+
+  function() 
+    LocalDebugPrint("is starting loop.");
+    state.loop_start = state.loop_pos;
+    return false
+  end,
+
+  -- Attack wave (land)
+  function() return AiForce(1, {"unit-assault", 4}) end,
+
+    -- Attack wave (air)
+  function() return AiForce(2, {"unit-jet", 2}) end,
+
+  -- Attack wave (vehicles)
+  function() return AiForce(3, {"unit-rtank", 1,
+                                "unit-tank", 3}) end,
+
+  -- Attack wave (mixed land)
+  function() return AiForce(4, {"unit-buggy", 2,
+                                "unit-artil", 1}) end,
+
+  -- Attack wave (air)
+  function() return AiForce(5, {"unit-chopper", 1}) end,
+
+  -- Attack wave (land, slow)
+  function() return AiForce(6, {"unit-bazoo", 6}) end,
+
+  -- Defense force 0 is sent to a building under attack
+  function() return AiForce(0, {"unit-assault", 4,
+                                "unit-bazoo", 3,
+                                "unit-grenadier", 3}) end,
+
+  -- wait 10 to 40 s:
+  function() return AiSleep((SyncRand(900)+300) * GameSettings.Difficulty) end,
+
+  function()
+    local f
+    for f=1,6 do
+      if (AiCheckForce(f)) then
+        LocalDebugPrint("is attacking with force " .. f .. ".");
+        -- AiWaitForce(f);
+        return AiAttackWithForce(f)
+      end
+    end
+    return AiSleep((SyncRand(100)+40) * GameSettings.Difficulty)
+  end,
+
+  function()
+    LocalDebugPrint("Reached the end of AI script and will loop");
+    state.loop_pos = state.loop_start - 1; -- AiLoop will immediately increment it.
+    return false
+  end,
 }
 
-function AiBroke()
-    -- print(AiPlayer() .. " position ".. ai_pos[AiPlayer() + 1]);
-    return AiLoop(ai_funcs, ai_pos)
+local function AiBroke()
+  LocalDebugPrint("Script position " .. AiState[AiPlayer()].loop_pos);
+  return AiLoop(ai_funcs)
 end
 
-DefineAiType({
-	Ident = "ai-broke",
-	Name = _("Broke"),
-	Init = InitAiScripts_broke,
-	EachSecond = AiBroke })
+this_ai_type = {
+  Ident = "ai-broke",
+  Name = _("Broke"),
+  Init = InitAiScripts_broke,
+  EachSecond = AiBroke,
+}
+DefineAiType(this_ai_type)
diff --git a/scripts/ais/default.lua b/scripts/ais/default.lua
index 0e962b3..39c01ad 100644
--- a/scripts/ais/default.lua
+++ b/scripts/ais/default.lua
@@ -28,51 +28,45 @@
 --	$Id: rush.lua 626 2006-11-25 18:10:03Z feb $
 --
 
-local player
+-- What we registered in AiTypes.
+local this_ai_type
 
-local function AiLoop(loop_funcs, loop_pos)
-  local ret
+-- Same as AiState[AiPlayer()].  Valid only during AiLoop.
+local state
 
-  player = AiPlayer() + 1
+local function AiLoop(funcs)
+  state = AiState[AiPlayer()]
   while (true) do
-    ret = loop_funcs[loop_pos[player]]()
+    local ret = funcs[state.loop_pos]()
     if (ret) then
-     break
+      break
     end
-    loop_pos[player] = loop_pos[player] + 1
+    state.loop_pos = state.loop_pos + 1
   end
   return true
 end
 
-function InitAiScripts_default()
-  ai_pos      = {1, 1, 1, 1, 1, 1, 1, 1}
-  ai_loop_pos = {1, 1, 1, 1, 1, 1, 1, 1}
-  hotspotexists = nil
+local function LocalDebugPrint(text)
+  DebugPrint(this_ai_type.Ident .. " player " .. AiPlayer() .. " " .. text)
+  return false
 end
 
-local ai_loop_funcs = {
-  function() return AiForce(1, {"unit-bazoo", 10, 
-                                "unit-grenadier", 10, 
-                                "unit-tank", 4,
-                                "unit-rtank", 4}) end,
-  function() return AiWaitForce(1) end,
-  function() return AiSleep(50*GameSettings.Difficulty) end,
-  function() return AiAttackWithForce(1) end,
-
-  function()
-    print("Ai " .. player .. " is looping");
-    ai_loop_pos[player] = 0;
-    return false
-  end,
-}
+local function InitAiScripts_default()
+  AiState[AiPlayer()] = {
+    loop_pos = 1,
+    loop_start = nil,
+    hotspotexists = nil,
+    build_order = nil,
+  }
+end
 
 local function HotSpotExists()
-  if (hotspotexists == nil) then
+  if (state.hotspotexists == nil) then
     local hotspot = UnitTypeByIdent("unit-hotspot")
     local count = Players[PlayerNumNeutral].UnitTypesCount[hotspot.Slot]
-    hotspotexists = (count ~= 0)
+    state.hotspotexists = (count ~= 0)
   end
-  return hotspotexists
+  return state.hotspotexists
 end
 
 local function CheckPower()
@@ -106,33 +100,30 @@ local ai_funcs = {
 
   -- Build magma pump or power plant first depending on resources
   function()
-    local order = GetBuildOrder()
-    if (order[1] ~= nil) then
-      return AiNeed(order[1])
+    state.build_order = GetBuildOrder()
+    if (state.build_order[1] ~= nil) then
+      return AiNeed(state.build_order[1])
     else
       return false
     end
   end,
   function()
-    local order = GetBuildOrder()
-    if (order[1] ~= nil) then
-      return AiWait(order[1])
+    if (state.build_order[1] ~= nil) then
+      return AiWait(state.build_order[1])
     else
       return false
     end
   end,
   function()
-    local order = GetBuildOrder()
-    if (order[2] ~= nil) then
-      return AiNeed(order[2])
+    if (state.build_order[2] ~= nil) then
+      return AiNeed(state.build_order[2])
     else
       return false
     end
   end,
   function()
-    local order = GetBuildOrder()
-    if (order[2] ~= nil) then
-      return AiWait(order[2])
+    if (state.build_order[2] ~= nil) then
+      return AiWait(state.build_order[2])
     else
       return false
     end
@@ -178,16 +169,38 @@ local ai_funcs = {
   function() return AiWaitForce(1) end, 
   function() return AiAttackWithForce(1) end,
 
-  function() return AiLoop(ai_loop_funcs, ai_loop_pos) end,
+  -- ============================================================
+
+  function() 
+    LocalDebugPrint("is starting loop.");
+    state.loop_start = state.loop_pos;
+    return false
+  end,
+
+  function() return AiForce(1, {"unit-bazoo", 10, 
+                                "unit-grenadier", 10, 
+                                "unit-tank", 4,
+                                "unit-rtank", 4}) end,
+  function() return AiWaitForce(1) end,
+  function() return AiSleep(50*GameSettings.Difficulty) end,
+  function() return AiAttackWithForce(1) end,
+
+  function()
+    LocalDebugPrint("Reached the end of AI script and will loop");
+    state.loop_pos = state.loop_start - 1; -- AiLoop will immediately increment it.
+    return false
+  end,
 }
 
-function AiDefault()
---    print(AiPlayer() .. " position ".. ai_pos[AiPlayer() + 1]);
-    return AiLoop(ai_funcs, ai_pos)
+local function AiDefault()
+  LocalDebugPrint("Script position " .. AiState[AiPlayer()].loop_pos);
+  return AiLoop(ai_funcs)
 end
 
-DefineAiType({
-	Ident = "ai-default",
-	Name = _("Default"),
-	Init = InitAiScripts_default,
-	EachSecond = AiDefault })
+this_ai_type = {
+  Ident = "ai-default",
+  Name = _("Default"),
+  Init = InitAiScripts_default,
+  EachSecond = AiDefault,
+}
+DefineAiType(this_ai_type)
diff --git a/scripts/ais/just_attack.lua b/scripts/ais/just_attack.lua
index 102b609..a5ce3f2 100644
--- a/scripts/ais/just_attack.lua
+++ b/scripts/ais/just_attack.lua
@@ -30,78 +30,92 @@
 --      $Id: just_attack.lua  $
 --
 
-local player
+-- What we registered in AiTypes.
+local this_ai_type
 
-local function AiLoop(loop_funcs, loop_pos)
-  local ret
+-- Same as AiState[AiPlayer()].  Valid only during AiLoop.
+local state
 
-  player = AiPlayer() + 1
+local function AiLoop(funcs)
+  state = AiState[AiPlayer()]
   while (true) do
-    ret = loop_funcs[loop_pos[player]]()
+    local ret = funcs[state.loop_pos]()
     if (ret) then
-        break
+      break
     end
-    loop_pos[player] = loop_pos[player] + 1
+    state.loop_pos = state.loop_pos + 1
   end
   return true
 end
 
-function InitAiScripts_just_attack()
-  ai_pos      = {1, 1, 1, 1, 1, 1, 1, 1}
-  ai_loop_pos = {1, 1, 1, 1, 1, 1, 1, 1}
+local function LocalDebugPrint(text)
+  DebugPrint(this_ai_type.Ident .. " player " .. AiPlayer() .. " " .. text)
+  return false
 end
 
+local function InitAiScripts_just_attack()
+  AiState[AiPlayer()] = {
+    loop_pos = 1,
+    loop_start = nil,
+  }
+end
+
+local ai_funcs = {
+  --function() AiDebug(false) return false end,
 
-local ai_loop_funcs = {
   function() return AiForce(0, {"unit-assault", 200}) end,
+  -- function() return AiWaitForce(0) end,
   function() return AiAttackWithForce(0) end,
 
+  -- Attack wave
   function() return AiForce(2, {"unit-grenadier", 200}) end,
+  --function() return AiWaitForce(2) end,
   function() return AiSleep((SyncRand(70)+150)*GameSettings.Difficulty) end, 
   function() return AiAttackWithForce(2) end,
 
+  -- Attack wave
   function() return AiForce(1, {"unit-bazoo", 200}) end,
-  function() return AiSleep((SyncRand(70)+150)*GameSettings.Difficulty) end, 
+  --function() return AiWaitForce(1) end,
+  function() return AiSleep((SyncRand(70)+150)*GameSettings.Difficulty) end,
   function() return AiAttackWithForce(1) end,
 
-  function()
-    -- print("Ai " .. player .. " is looping");
-    ai_loop_pos[player] = 0;
-    return false
-  end,
-}
+  function() return AiSleep((SyncRand(70)+150)*GameSettings.Difficulty) end,
 
+  -- ============================================================
 
-local ai_funcs = {
-  --function() AiDebug(false) return false end,
+  function() 
+    LocalDebugPrint("is starting loop.");
+    state.loop_start = state.loop_pos;
+    return false
+  end,
 
   function() return AiForce(0, {"unit-assault", 200}) end,
-  -- function() return AiWaitForce(0) end,
   function() return AiAttackWithForce(0) end,
 
-  -- Attack wave
   function() return AiForce(2, {"unit-grenadier", 200}) end,
-  --function() return AiWaitForce(2) end,
   function() return AiSleep((SyncRand(70)+150)*GameSettings.Difficulty) end, 
   function() return AiAttackWithForce(2) end,
 
-  -- Attack wave
   function() return AiForce(1, {"unit-bazoo", 200}) end,
-  --function() return AiWaitForce(1) end,
-  function() return AiSleep((SyncRand(70)+150)*GameSettings.Difficulty) end,
+  function() return AiSleep((SyncRand(70)+150)*GameSettings.Difficulty) end, 
   function() return AiAttackWithForce(1) end,
 
-  function() return AiSleep((SyncRand(70)+150)*GameSettings.Difficulty) end,
-  function() return AiLoop(ai_loop_funcs, ai_loop_pos) end,
+  function()
+    LocalDebugPrint("Reached the end of AI script and will loop");
+    state.loop_pos = state.loop_start - 1; -- AiLoop will immediately increment it.
+    return false
+  end,
 }
 
-function AiJustAttack()
-    -- print("Just attack " .. AiPlayer() + 1 .. " position ".. ai_pos[AiPlayer() + 1]);
-    return AiLoop(ai_funcs, ai_pos)
+local function AiJustAttack()
+  LocalDebugPrint("Script position " .. AiState[AiPlayer()].loop_pos);
+  return AiLoop(ai_funcs)
 end
 
-DefineAiType({
-	Ident = "ai-just_attack",
-	Name = _("Just attack"),
-	Init = InitAiScripts_just_attack,
-	EachSecond = AiJustAttack })
+this_ai_type = {
+  Ident = "ai-just_attack",
+  Name = _("Just attack"),
+  Init = InitAiScripts_just_attack,
+  EachSecond = AiJustAttack,
+}
+DefineAiType(this_ai_type)
diff --git a/scripts/ais/naval.lua b/scripts/ais/naval.lua
index f1b81cd..80402b1 100644
--- a/scripts/ais/naval.lua
+++ b/scripts/ais/naval.lua
@@ -29,48 +29,35 @@
 --      $Id: naval.lua  $
 --
 
-local player
+-- What we registered in AiTypes.
+local this_ai_type
 
-local function AiLoop(loop_funcs, loop_pos)
-  local ret
+-- Same as AiState[AiPlayer()].  Valid only during AiLoop.
+local state
 
-  player = AiPlayer() + 1
+local function AiLoop(funcs)
+  state = AiState[AiPlayer()]
   while (true) do
-    ret = loop_funcs[loop_pos[player]]()
+    local ret = funcs[state.loop_pos]()
     if (ret) then
-        break
+      break
     end
-    loop_pos[player] = loop_pos[player] + 1
+    state.loop_pos = state.loop_pos + 1
   end
   return true
 end
 
-function InitAiScripts_naval()
-  ai_pos      = {1, 1, 1, 1, 1, 1, 1, 1}
-  ai_loop_pos = {1, 1, 1, 1, 1, 1, 1, 1}
+local function LocalDebugPrint(text)
+  DebugPrint(this_ai_type.Ident .. " player " .. AiPlayer() .. " " .. text)
+  return false
 end
 
-local ai_loop_funcs = {
-  function() return AiForce(0, {"unit-destroyer", 3}) end,
-  function() return AiWaitForce(0) end,
-  function() return AiAttackWithForce(0) end,
-
-  -- Always attack as soon as the 3 destroyers are finished:
-  --function()
-  --  if (AiCheckForce(0)) then
-  --    AiWaitForce(0);
-  --    return AiAttackWithForce(0)
-  --  end
-  --  return true
-  --end,
-
-  function()
-    -- print("Ai " .. player .. " is looping"); -- debug only
-    ai_loop_pos[player] = 0;
-    return false
-  end,
-}
-
+local function InitAiScripts_naval()
+  AiState[AiPlayer()] = {
+    loop_pos = 1,
+    loop_start = nil,
+  }
+end
 
 local ai_funcs = {
   -- Use all 4 engineers to help building a magnapump:
@@ -85,7 +72,7 @@ local ai_funcs = {
   -- This also does not work with the AiSet() function.
   function() return AiWait("unit-magmapump") end,
   function() -- debug only
-    print("Naval " .. AiPlayer() + 1 .. " Magnapump finished.");
+    LocalDebugPrint("Magnapump finished.");
     return false
   end,
 
@@ -93,7 +80,7 @@ local ai_funcs = {
   -- Do not start building anything else until we have the energy:
   function() return AiWait("unit-nukepowerplant") end,
   function() -- debug only
-    print("Naval " .. AiPlayer() + 1 .. " NukePowerplant finished.");
+    LocalDebugPrint("NukePowerplant finished.");
     return false
   end,
 
@@ -101,7 +88,7 @@ local ai_funcs = {
   -- function() return AiNeed("unit-vault") end,
   -- function() return AiWait("unit-vault") end,
   -- function() -- debug only
-  --   print("Naval " .. AiPlayer() + 1 .. " Vault finished.");
+  --   LocalDebugPrint("Vault finished.");
   --   return false
   -- end,
 
@@ -111,34 +98,61 @@ local ai_funcs = {
   function() return AiNeed("unit-gturret") end,
 
   function() return AiWait("unit-shipyard") end,
-  -- function() -- debug only
-  --  print("Naval " .. AiPlayer() + 1 .. " Shipyard finished.");
-  --  return false
-  -- end,
+  function() -- debug only
+    LocalDebugPrint("Shipyard finished.");
+    return false
+  end,
 
   function() return AiForce(0, {"unit-destroyer", 2}) end,
   function() return AiWaitForce(0) end,
   function() return AiAttackWithForce(0) end,
-  -- function() -- debug only
-  --  print(AiPlayer() + 1 .. " Attack force of 2 destroyers is ready - attacking.");
-  --  return false
-  --end,
+  function() -- debug only
+    LocalDebugPrint("Attack force of 2 destroyers is ready - attacking.");
+    return false
+  end,
 
   function() return AiSet("unit-magmapump",2) end,
   function() return AiSet("unit-nukepowerplant", 1) end,
   function() return AiSet("unit-engineer", 4) end,
   function() return AiSet("unit-gturret", 4) end,
 
-  function() return AiLoop(ai_loop_funcs, ai_loop_pos) end,
+  -- ============================================================
+
+  function() 
+    LocalDebugPrint("is starting loop.");
+    state.loop_start = state.loop_pos;
+    return false
+  end,
+
+  function() return AiForce(0, {"unit-destroyer", 3}) end,
+  function() return AiWaitForce(0) end,
+  function() return AiAttackWithForce(0) end,
+
+  -- Always attack as soon as the 3 destroyers are finished:
+  --function()
+  --  if (AiCheckForce(0)) then
+  --    AiWaitForce(0);
+  --    return AiAttackWithForce(0)
+  --  end
+  --  return true
+  --end,
+
+  function()
+    LocalDebugPrint("Reached the end of AI script and will loop");
+    state.loop_pos = state.loop_start - 1; -- AiLoop will immediately increment it.
+    return false
+  end,
 }
 
-function AiNaval()
-    -- print("Naval " .. AiPlayer() + 1 .. " position ".. ai_pos[AiPlayer() + 1]); -- debug only
-    return AiLoop(ai_funcs, ai_pos)
+local function AiNaval()
+  LocalDebugPrint("Script position " .. AiState[AiPlayer()].loop_pos);
+  return AiLoop(ai_funcs)
 end
 
-DefineAiType({
-	Ident = "ai-naval",
-	Name = _("Naval"),
-	Init = InitAiScripts_naval,
-	EachSecond = AiNaval })
+this_ai_type = {
+  Ident = "ai-naval",
+  Name = _("Naval"),
+  Init = InitAiScripts_naval,
+  EachSecond = AiNaval,
+}
+DefineAiType(this_ai_type)
diff --git a/scripts/ais/passive.lua b/scripts/ais/passive.lua
index 1e20213..65b1a35 100644
--- a/scripts/ais/passive.lua
+++ b/scripts/ais/passive.lua
@@ -28,11 +28,16 @@
 --	$Id: ai.lua 626 2006-11-25 18:10:03Z feb $
 --
 
-function AiPassive()
+-- What we registered in AiTypes.
+local this_ai_type
+
+local function AiPassive()
 end
 
-DefineAiType({
-	Ident = "ai-passive",
-	Name = _("Passive"),
-	Init = nil,
-	EachSecond = AiPassive })
+this_ai_type = {
+  Ident = "ai-passive",
+  Name = _("Passive"),
+  Init = nil,
+  EachSecond = AiPassive,
+}
+DefineAiType(this_ai_type)
diff --git a/scripts/ais/rush.lua b/scripts/ais/rush.lua
index 73c4e9b..66268cd 100644
--- a/scripts/ais/rush.lua
+++ b/scripts/ais/rush.lua
@@ -32,46 +32,45 @@
 --      $Id: rush.lua 626 2006-11-25 18:10:03Z feb $
 --
 
-local player
+-- What we registered in AiTypes.
+local this_ai_type
 
-local function AiLoop(loop_funcs, loop_pos)
-  local ret
+-- Same as AiState[AiPlayer()].  Valid only during AiLoop.
+local state
 
-  player = AiPlayer() + 1
+local function AiLoop(funcs)
+  state = AiState[AiPlayer()]
   while (true) do
-    ret = loop_funcs[loop_pos[player]]()
+    local ret = funcs[state.loop_pos]()
     if (ret) then
-     break
+      break
     end
-    loop_pos[player] = loop_pos[player] + 1
+    state.loop_pos = state.loop_pos + 1
   end
   return true
 end
 
-function InitAiScripts_rush()
-  ai_pos      = {1, 1, 1, 1, 1, 1, 1, 1}
-  ai_loop_pos = {1, 1, 1, 1, 1, 1, 1, 1}
-  hotspotexists = nil
+local function LocalDebugPrint(text)
+  DebugPrint(this_ai_type.Ident .. " player " .. AiPlayer() .. " " .. text)
+  return false
 end
 
-local ai_loop_funcs = {
-  function() print("Looping !"); return false end,
-  function() return AiForce(1, {"unit-assault", 20, 
-                                "unit-grenadier", 8, 
-                                "unit-bazoo", 8}) end,
-  function() return AiWaitForce(1) end,  -- wait until attack party is completed
-  function() return AiSleep(50*GameSettings.Difficulty) end,
-  function() return AiAttackWithForce(1) end,
-  function() ai_loop_pos[player] = 0; return false end,
-}
+local function InitAiScripts_rush()
+  AiState[AiPlayer()] = {
+    loop_pos = 1,
+    loop_start = nil,
+    hotspotexists = nil,
+    build_order = nil,
+  }
+end
 
 local function HotSpotExists()
-  if (hotspotexists == nil) then
+  if (state.hotspotexists == nil) then
     local hotspot = UnitTypeByIdent("unit-hotspot")
     local count = Players[PlayerNumNeutral].UnitTypesCount[hotspot.Slot]
-    hotspotexists = (count ~= 0)
+    state.hotspotexists = (count ~= 0)
   end
-  return hotspotexists
+  return state.hotspotexists
 end
 
 local function GetBuildOrder()
@@ -96,25 +95,22 @@ local ai_funcs = {
 
   -- Build magma pump or power plant first depending on resources
   function()
-    local order = GetBuildOrder()
-    return AiNeed(order[1])
+    state.build_order = GetBuildOrder()
+    return AiNeed(state.build_order[1])
   end,
   function()
-    local order = GetBuildOrder()
-    return AiWait(order[1])
+    return AiWait(state.build_order[1])
   end,
   function()
-    local order = GetBuildOrder()
-    if (order[2] ~= nil) then
-      return AiNeed(order[2])
+    if (state.build_order[2] ~= nil) then
+      return AiNeed(state.build_order[2])
     else
       return false
     end
   end,
   function()
-    local order = GetBuildOrder()
-    if (order[2] ~= nil) then
-      return AiWait(order[2])
+    if (state.build_order[2] ~= nil) then
+      return AiWait(state.build_order[2])
     else
       return false
     end
@@ -150,16 +146,37 @@ local ai_funcs = {
   function() return AiWaitForce(1) end, 
   function() return AiAttackWithForce(1) end,
 
-  function() return AiLoop(ai_loop_funcs, ai_loop_pos) end,
+  -- ============================================================
+
+  function() 
+    LocalDebugPrint("is starting loop.");
+    state.loop_start = state.loop_pos;
+    return false
+  end,
+
+  function() return AiForce(1, {"unit-assault", 20, 
+                                "unit-grenadier", 8, 
+                                "unit-bazoo", 8}) end,
+  function() return AiWaitForce(1) end,  -- wait until attack party is completed
+  function() return AiSleep(50*GameSettings.Difficulty) end,
+  function() return AiAttackWithForce(1) end,
+
+  function()
+    LocalDebugPrint("Reached the end of AI script and will loop");
+    state.loop_pos = state.loop_start - 1; -- AiLoop will immediately increment it.
+    return false
+  end,
 }
 
-function AiRush()
---    print(AiPlayer() .. " position ".. ai_pos[AiPlayer() + 1]);
-    return AiLoop(ai_funcs, ai_pos)
+local function AiRush()
+  LocalDebugPrint("Script position " .. AiState[AiPlayer()].loop_pos);
+  return AiLoop(ai_funcs)
 end
 
-DefineAiType({
-	Ident = "ai-rush",
-	Name = _("Rush"),
-	Init = InitAiScripts_rush,
-	EachSecond = AiRush })
+this_ai_type = {
+  Ident = "ai-rush",
+  Name = _("Rush"),
+  Init = InitAiScripts_rush,
+  EachSecond = AiRush,
+}
+DefineAiType(this_ai_type)
diff --git a/scripts/ais/spacious.lua b/scripts/ais/spacious.lua
index 2bce869..a8062af 100644
--- a/scripts/ais/spacious.lua
+++ b/scripts/ais/spacious.lua
@@ -30,84 +30,35 @@
 --	$Id: spacious.lua  $
 --
 
-local player
+-- What we registered in AiTypes.
+local this_ai_type
 
-local function AiLoop(loop_funcs, loop_pos)
-  local ret
+-- Same as AiState[AiPlayer()].  Valid only during AiLoop.
+local state
 
-  player = AiPlayer() + 1
+local function AiLoop(funcs)
+  state = AiState[AiPlayer()]
   while (true) do
-    ret = loop_funcs[loop_pos[player]]()
+    local ret = funcs[state.loop_pos]()
     if (ret) then
-     break
+      break
     end
-    loop_pos[player] = loop_pos[player] + 1
+    state.loop_pos = state.loop_pos + 1
   end
   return true
 end
 
-function InitAiScripts_spacious()
-  ai_pos      = {1, 1, 1, 1, 1, 1, 1, 1}
-  ai_loop_pos = {1, 1, 1, 1, 1, 1, 1, 1}
+local function LocalDebugPrint(text)
+  DebugPrint(this_ai_type.Ident .. " player " .. AiPlayer() .. " " .. text)
+  return false
 end
 
-local ai_loop_funcs = {
-
-  -- Attack wave (air)
-  function() return AiForce(2, {"unit-jet", 2}) end,
-
-
-  -- Defense force 0 is sent to a building under attack
-  function() return AiForce(0, {"unit-assault", 4,
-                                "unit-bazoo", 3,
-                                "unit-grenadier", 3}) end,
-
-  -- Attack wave (air)
-  function() return AiForce(5, {"unit-chopper", 1}) end,
-
-  -- Attack wave (land)
-  function() return AiForce(1, {"unit-assault", 4}) end,
-
-  -- Attack wave (vehicles)
-  function() return AiForce(3, {"unit-rtank", 1,
-                                "unit-tank", 3}) end,
-
-  -- Attack wave (mixed land)
-  function() return AiForce(4, {"unit-buggy", 2,
-                                "unit-rtank", 2}) end,
-
-  -- Attack wave (land)
-  function() return AiForce(6, {"unit-bazoo", 6}) end,
-
-  -- wait 10 to 40 s:
-  function() return AiSleep((SyncRand(900)+300) * GameSettings.Difficulty) end,
-
-  function()
-    local f
-    for f=1,6 do
-      if (AiCheckForce(f)) then
-        -- print("Ai " .. player .. " is attacking with force " .. f .. ".");
-        AiWaitForce(f);
-        return AiAttackWithForce(f)
-      end
-    end
-    return AiSleep((SyncRand(100)+40) * GameSettings.Difficulty)
-  end,
-
-  -- Let's not build too many cannons, since the
-  -- AI places them mostly in useless locations:
-  function() return AiSet("unit-cannon", 1) end,
-  -- A radar is needed for the cannon to see its targets:
-  function() return AiSet("unit-radar", 1) end,
-
-  function()
-    -- print("Ai " .. player .. " is looping.");
-    ai_loop_pos[player] = 0;
-    return false -- to indicate the end of the loop (?)
-  end,
-}
-
-
+local function InitAiScripts_spacious()
+  AiState[AiPlayer()] = {
+    loop_pos = 1,
+    loop_start = nil,
+  }
+end
 
 local ai_funcs = {
   -- If we loose an engineer, we want to keep the number of engineers on 3:
@@ -127,7 +78,7 @@ local ai_funcs = {
 
   -- First contact - Very Small Attack wave
   function()
-    -- print("Ai " .. player .. " now has the basic resources to start building an attack force.");
+    LocalDebugPrint("now has the basic resources to start building an attack force.");
     return AiForce(1, {"unit-assault", 2})
   end,
   -- In the meanwhile, the engineers can build a second magnapump:
@@ -142,7 +93,7 @@ local ai_funcs = {
   -- they do not all come at the same time.
   function() return AiSleep((SyncRand(700)+300) * GameSettings.Difficulty) end,
   function() 
-    -- print("Ai " .. player .. " is attacking with force 1a.");
+    LocalDebugPrint("is attacking with force 1a.");
     return AiAttackWithForce(1)
   end,
 
@@ -154,7 +105,7 @@ local ai_funcs = {
   function() return AiForce(1, {"unit-assault", 3}) end,
   function() return AiWaitForce(1) end,
   function()
-    -- print("Ai " .. player .. " is attacking with force 1b.");
+    LocalDebugPrint("is attacking with force 1b.");
     return AiAttackWithForce(1)
   end,
 
@@ -175,7 +126,7 @@ local ai_funcs = {
 
   function() return AiSleep((SyncRand(600)+400) * GameSettings.Difficulty) end,
   function()
-    -- print("Ai " .. player .. " is attacking with force 1c.");
+    LocalDebugPrint("is attacking with force 1c.");
     return AiAttackWithForce(1)
   end,
 
@@ -185,7 +136,7 @@ local ai_funcs = {
   function() return AiWaitForce(4) end,
   function() return AiSleep((SyncRand(600)+400) * GameSettings.Difficulty) end,
   function()
-    -- print("Ai " .. player .. " is attacking with force 1d.");
+    LocalDebugPrint("is attacking with force 1d.");
     return AiAttackWithForce(4)
   end,
 
@@ -212,17 +163,77 @@ local ai_funcs = {
   function() return AiWait("unit-aircraftfactory") end,
   function() return AiSet("unit-aircraftfactory", 2) end,
 
+  -- ============================================================
+
+  function() 
+    LocalDebugPrint("is starting loop.");
+    state.loop_start = state.loop_pos;
+    return false
+  end,
+
+  -- Attack wave (air)
+  function() return AiForce(2, {"unit-jet", 2}) end,
+
+
+  -- Defense force 0 is sent to a building under attack
+  function() return AiForce(0, {"unit-assault", 4,
+                                "unit-bazoo", 3,
+                                "unit-grenadier", 3}) end,
+
+  -- Attack wave (air)
+  function() return AiForce(5, {"unit-chopper", 1}) end,
+
+  -- Attack wave (land)
+  function() return AiForce(1, {"unit-assault", 4}) end,
+
+  -- Attack wave (vehicles)
+  function() return AiForce(3, {"unit-rtank", 1,
+                                "unit-tank", 3}) end,
+
+  -- Attack wave (mixed land)
+  function() return AiForce(4, {"unit-buggy", 2,
+                                "unit-rtank", 2}) end,
+
+  -- Attack wave (land)
+  function() return AiForce(6, {"unit-bazoo", 6}) end,
+
+  -- wait 10 to 40 s:
+  function() return AiSleep((SyncRand(900)+300) * GameSettings.Difficulty) end,
+
+  function()
+    local f
+    for f=1,6 do
+      if (AiCheckForce(f)) then
+        LocalDebugPrint("is attacking with force " .. f .. ".");
+        AiWaitForce(f);
+        return AiAttackWithForce(f)
+      end
+    end
+    return AiSleep((SyncRand(100)+40) * GameSettings.Difficulty)
+  end,
+
+  -- Let's not build too many cannons, since the
+  -- AI places them mostly in useless locations:
+  function() return AiSet("unit-cannon", 1) end,
+  -- A radar is needed for the cannon to see its targets:
+  function() return AiSet("unit-radar", 1) end,
+
   function()
-    return AiLoop(ai_loop_funcs, ai_loop_pos)
+    LocalDebugPrint("Reached the end of AI script and will loop");
+    state.loop_pos = state.loop_start - 1; -- AiLoop will immediately increment it.
+    return false
   end,
 }
 
-function AiSpacious()
-    return AiLoop(ai_funcs, ai_pos)
+local function AiSpacious()
+  LocalDebugPrint("Script position " .. AiState[AiPlayer()].loop_pos);
+  return AiLoop(ai_funcs)
 end
 
-DefineAiType({
-	Ident = "ai-spacious",
-	Name = _("Spacious"),
-	Init = InitAiScripts_spacious,
-	EachSecond = AiSpacious })
+this_ai_type = {
+  Ident = "ai-spacious",
+  Name = _("Spacious"),
+  Init = InitAiScripts_spacious,
+  EachSecond = AiSpacious,
+}
+DefineAiType(this_ai_type)
diff --git a/scripts/ais/tankrush.lua b/scripts/ais/tankrush.lua
index 55d5af7..01d75b3 100644
--- a/scripts/ais/tankrush.lua
+++ b/scripts/ais/tankrush.lua
@@ -28,44 +28,45 @@
 --      $Id: tankrush.lua 626 2006-11-25 18:10:03Z feb $
 --
 
-local player
+-- What we registered in AiTypes.
+local this_ai_type
 
-local function AiLoop(loop_funcs, loop_pos)
-  local ret
+-- Same as AiState[AiPlayer()].  Valid only during AiLoop.
+local state
 
-  player = AiPlayer() + 1
+local function AiLoop(funcs)
+  state = AiState[AiPlayer()]
   while (true) do
-    ret = loop_funcs[loop_pos[player]]()
+    local ret = funcs[state.loop_pos]()
     if (ret) then
-     break
+      break
     end
-    loop_pos[player] = loop_pos[player] + 1
+    state.loop_pos = state.loop_pos + 1
   end
   return true
 end
 
-function InitAiScripts_tankrush()
-  ai_pos      = {1, 1, 1, 1, 1, 1, 1, 1}
-  ai_loop_pos = {1, 1, 1, 1, 1, 1, 1, 1}
-  hotspotexists = nil
+local function LocalDebugPrint(text)
+  DebugPrint(this_ai_type.Ident .. " player " .. AiPlayer() .. " " .. text)
+  return false
 end
 
-local ai_loop_funcs = {
-  function() print("Looping !"); return false end,
-  function() return AiForce(1, {"unit-tank", 4, "unit-rtank", 4}) end,
-  function() return AiWaitForce(1) end,  -- wait until attack party is completed
-  function() return AiSleep(50*GameSettings.Difficulty) end,
-  function() return AiAttackWithForce(1) end,
-  function() ai_loop_pos[player] = 0; return false end,
-}
+local function InitAiScripts_tankrush()
+  AiState[AiPlayer()] = {
+    loop_pos = 1,
+    loop_start = nil,
+    hotspotexists = nil,
+    build_order = nil,
+  }
+end
 
 local function HotSpotExists()
-  if (hotspotexists == nil) then
+  if (state.hotspotexists == nil) then
     local hotspot = UnitTypeByIdent("unit-hotspot")
     local count = Players[PlayerNumNeutral].UnitTypesCount[hotspot.Slot]
-    hotspotexists = (count ~= 0)
+    state.hotspotexists = (count ~= 0)
   end
-  return hotspotexists
+  return state.hotspotexists
 end
 
 local function GetBuildOrder()
@@ -90,25 +91,22 @@ local ai_funcs = {
 
   -- Build magma pump or power plant first depending on resources
   function()
-    local order = GetBuildOrder()
-    return AiNeed(order[1])
+    state.build_order = GetBuildOrder()
+    return AiNeed(state.build_order[1])
   end,
   function()
-    local order = GetBuildOrder()
-    return AiWait(order[1])
+    return AiWait(state.build_order[1])
   end,
   function()
-    local order = GetBuildOrder()
-    if (order[2] ~= nil) then
-      return AiNeed(order[2])
+    if (state.build_order[2] ~= nil) then
+      return AiNeed(state.build_order[2])
     else
       return false
     end
   end,
   function()
-    local order = GetBuildOrder()
-    if (order[2] ~= nil) then
-      return AiWait(order[2])
+    if (state.build_order[2] ~= nil) then
+      return AiWait(state.build_order[2])
     else
       return false
     end
@@ -146,16 +144,35 @@ local ai_funcs = {
   function() return AiWaitForce(1) end, 
   function() return AiAttackWithForce(1) end,
 
-  function() return AiLoop(ai_loop_funcs, ai_loop_pos) end,
+  -- ============================================================
+
+  function() 
+    LocalDebugPrint("is starting loop.");
+    state.loop_start = state.loop_pos;
+    return false
+  end,
+
+  function() return AiForce(1, {"unit-tank", 4, "unit-rtank", 4}) end,
+  function() return AiWaitForce(1) end,  -- wait until attack party is completed
+  function() return AiSleep(50*GameSettings.Difficulty) end,
+  function() return AiAttackWithForce(1) end,
+
+  function()
+    LocalDebugPrint("Reached the end of AI script and will loop");
+    state.loop_pos = state.loop_start - 1; -- AiLoop will immediately increment it.
+    return false
+  end,
 }
 
-function AiTankRush()
---    print(AiPlayer() .. " position ".. ai_pos[AiPlayer() + 1]);
-    return AiLoop(ai_funcs, ai_pos)
+local function AiTankRush()
+  LocalDebugPrint("Script position " .. AiState[AiPlayer()].loop_pos);
+  return AiLoop(ai_funcs)
 end
 
-DefineAiType({
-	Ident = "ai-tankrush",
-	Name = _("Tank rush"),
-	Init = InitAiScripts_tankrush,
-	EachSecond = AiTankRush })
+this_ai_type = {
+  Ident = "ai-tankrush",
+  Name = _("Tank rush"),
+  Init = InitAiScripts_tankrush,
+  EachSecond = AiTankRush,
+}
+DefineAiType(this_ai_type)
diff --git a/scripts/ais/zombies.lua b/scripts/ais/zombies.lua
index e0606b9..9e666f4 100644
--- a/scripts/ais/zombies.lua
+++ b/scripts/ais/zombies.lua
@@ -32,46 +32,45 @@
 --      $Id:  $
 --
 
-local player
+-- What we registered in AiTypes.
+local this_ai_type
 
-local function AiLoop(loop_funcs, loop_pos)
-  local ret
+-- Same as AiState[AiPlayer()].  Valid only during AiLoop.
+local state
 
-  player = AiPlayer() + 1
+local function AiLoop(funcs)
+  state = AiState[AiPlayer()]
   while (true) do
-    ret = loop_funcs[loop_pos[player]]()
+    local ret = funcs[state.loop_pos]()
     if (ret) then
-     break
+      break
     end
-    loop_pos[player] = loop_pos[player] + 1
+    state.loop_pos = state.loop_pos + 1
   end
   return true
 end
 
-function InitAiScripts_zombies()
-  ai_pos      = {1, 1, 1, 1, 1, 1, 1, 1}
-  ai_loop_pos = {1, 1, 1, 1, 1, 1, 1, 1}
-  hotspotexists = nil
+local function LocalDebugPrint(text)
+  DebugPrint(this_ai_type.Ident .. " player " .. AiPlayer() .. " " .. text)
+  return false
 end
 
-local ai_loop_funcs = {
-  -- function() print("Looping !"); return false end,
-  function() return AiForce(1, {"unit-assault", SyncRand(20)+10, 
-                                "unit-grenadier", SyncRand(12)+1, 
-                                "unit-bazoo", SyncRand(12)+1}) end,
-  function() return AiWaitForce(1) end,  -- wait until attack party is completed
-  function() return AiSleep((SyncRand(200)+30)*GameSettings.Difficulty) end,
-  function() return AiAttackWithForce(1) end,
-  function() ai_loop_pos[player] = 0; return false end,
-}
+local function InitAiScripts_zombies()
+  AiState[AiPlayer()] = {
+    loop_pos = 1,
+    loop_start = nil,
+    hotspotexists = nil,
+    build_order = nil,
+  }
+end
 
 local function HotSpotExists()
-  if (hotspotexists == nil) then
+  if (state.hotspotexists == nil) then
     local hotspot = UnitTypeByIdent("unit-hotspot")
     local count = Players[PlayerNumNeutral].UnitTypesCount[hotspot.Slot]
-    hotspotexists = (count ~= 0)
+    state.hotspotexists = (count ~= 0)
   end
-  return hotspotexists
+  return state.hotspotexists
 end
 
 local function GetBuildOrder()
@@ -94,25 +93,22 @@ end
 local ai_funcs = {
   -- Build magma pump or power plant first depending on resources
   function()
-    local order = GetBuildOrder()
-    return AiNeed(order[1])
+    state.build_order = GetBuildOrder()
+    return AiNeed(state.build_order[1])
   end,
   function()
-    local order = GetBuildOrder()
-    return AiWait(order[1])
+    return AiWait(state.build_order[1])
   end,
   function()
-    local order = GetBuildOrder()
-    if (order[2] ~= nil) then
-      return AiNeed(order[2])
+    if (state.build_order[2] ~= nil) then
+      return AiNeed(state.build_order[2])
     else
       return false
     end
   end,
   function()
-    local order = GetBuildOrder()
-    if (order[2] ~= nil) then
-      return AiWait(order[2])
+    if (state.build_order[2] ~= nil) then
+      return AiWait(state.build_order[2])
     else
       return false
     end
@@ -148,16 +144,37 @@ local ai_funcs = {
   function() return AiWaitForce(1) end, 
   function() return AiAttackWithForce(1) end,
 
-  function() return AiLoop(ai_loop_funcs, ai_loop_pos) end,
+  -- ============================================================
+
+  function() 
+    LocalDebugPrint("is starting loop.");
+    state.loop_start = state.loop_pos;
+    return false
+  end,
+
+  function() return AiForce(1, {"unit-assault", SyncRand(20)+10, 
+                                "unit-grenadier", SyncRand(12)+1, 
+                                "unit-bazoo", SyncRand(12)+1}) end,
+  function() return AiWaitForce(1) end,  -- wait until attack party is completed
+  function() return AiSleep((SyncRand(200)+30)*GameSettings.Difficulty) end,
+  function() return AiAttackWithForce(1) end,
+
+  function()
+    LocalDebugPrint("Reached the end of AI script and will loop");
+    state.loop_pos = state.loop_start - 1; -- AiLoop will immediately increment it.
+    return false
+  end,
 }
 
-function AiZombies()
---    print(AiPlayer() .. " position ".. ai_pos[AiPlayer() + 1]);
-    return AiLoop(ai_funcs, ai_pos)
+local function AiZombies()
+  LocalDebugPrint("Script position " .. AiState[AiPlayer()].loop_pos);
+  return AiLoop(ai_funcs)
 end
 
-DefineAiType({
-	Ident = "ai-zombies",
-	Name = _("Zombies"),
-	Init = InitAiScripts_zombies,
-	EachSecond = AiZombies })
+this_ai_type = {
+  Ident = "ai-zombies",
+  Name = _("Zombies"),
+  Init = InitAiScripts_zombies,
+  EachSecond = AiZombies,
+}
+DefineAiType(this_ai_type)
diff --git a/scripts/boswars.lua b/scripts/boswars.lua
index e8aeac7..30b4612 100644
--- a/scripts/boswars.lua
+++ b/scripts/boswars.lua
@@ -141,8 +141,8 @@ DefinePlayerColors({
 })
 
 function InitGameVariables()
-   SetSpeeds(1)
-   InitAiScripts()
+  SetSpeeds(1)
+  ClearAiState()
 end
 
 --  Pathfinder
-- 
1.7.7

