(svn r13615) [NoAI] -Fix: fixed most, if not all, problems around AIAbstractList. It is now safe to remove values while looping, among other things. noai
authortruebrain
Mon, 23 Jun 2008 13:52:21 +0000
branchnoai
changeset 11058 3305a425f55b
parent 11057 188a9ca6d8de
child 11059 b1751c5973cc
(svn r13615) [NoAI] -Fix: fixed most, if not all, problems around AIAbstractList. It is now safe to remove values while looping, among other things.
[NoAI] -Add: allow foreach() usage for AIAbstractList
[NoAI] -Add: allow [] usage for AIAbstractList (read-only)
[NoAI] -Add: allow [] usage for AIList (read/write)
bin/ai/regression/regression.nut
bin/ai/regression/regression.txt
src/ai/api/Doxyfile
src/ai/api/ai_abstractlist.cpp
src/ai/api/ai_abstractlist.hpp
src/ai/api/ai_abstractlist.hpp.sq
src/ai/api/ai_list.cpp
src/ai/api/ai_list.hpp
src/ai/api/ai_list.hpp.sq
src/ai/api/squirrel_export.awk
--- a/bin/ai/regression/regression.nut	Mon Jun 23 12:46:38 2008 +0000
+++ b/bin/ai/regression/regression.nut	Mon Jun 23 13:52:21 2008 +0000
@@ -24,6 +24,146 @@
 	print(" min(3, 6): " + min(3, 6));
 	print(" max(6, 3): " + max(6, 3));
 	print(" max(3, 6): " + max(3, 6));
+
+	print(" AIList Consistency Tests");
+	print("");
+	print(" Value Descending");
+	local list = AIList();
+	list.AddItem( 5, 10);
+	list.AddItem(10, 10);
+	list.AddItem(15, 20);
+	list.AddItem(20, 20);
+	list.AddItem(25, 30);
+	list.AddItem(30, 30);
+	list.AddItem(35, 40);
+	list.AddItem(40, 40);
+
+	for (local i = list.Begin(); list.HasNext(); i = list.Next()) {
+		list.RemoveItem(i - 10);
+		list.RemoveItem(i - 5);
+		list.RemoveItem(i);
+		print("   " + i);
+	}
+
+	list.AddItem(10, 10);
+	list.AddItem(20, 20);
+	list.AddItem(30, 30);
+	list.AddItem(40, 40);
+
+	print("");
+	for (local i = list.Begin(); list.HasNext(); i = list.Next()) {
+		list.SetValue(i, 2);
+		print("   " + i);
+	}
+	print("");
+	for (local i = list.Begin(); list.HasNext(); i = list.Next()) {
+		print("   " + i);
+	}
+
+	list = AIList();
+	list.Sort(AIAbstractList.SORT_BY_VALUE, true);
+	print("");
+	print(" Value Ascending");
+	list.AddItem( 5, 10);
+	list.AddItem(10, 10);
+	list.AddItem(15, 20);
+	list.AddItem(20, 20);
+	list.AddItem(25, 30);
+	list.AddItem(30, 30);
+	list.AddItem(35, 40);
+	list.AddItem(40, 40);
+
+	for (local i = list.Begin(); list.HasNext(); i = list.Next()) {
+		list.RemoveItem(i + 10);
+		list.RemoveItem(i + 5);
+		list.RemoveItem(i);
+		print("   " + i);
+	}
+
+	list.AddItem(10, 10);
+	list.AddItem(20, 20);
+	list.AddItem(30, 30);
+	list.AddItem(40, 40);
+
+	print("");
+	for (local i = list.Begin(); list.HasNext(); i = list.Next()) {
+		list.SetValue(i, 50);
+		print("   " + i);
+	}
+	print("");
+	for (local i = list.Begin(); list.HasNext(); i = list.Next()) {
+		print("   " + i);
+	}
+
+	list = AIList();
+	list.Sort(AIAbstractList.SORT_BY_ITEM, false);
+	print("");
+	print(" Item Descending");
+	list.AddItem( 5, 10);
+	list.AddItem(10, 10);
+	list.AddItem(15, 20);
+	list.AddItem(20, 20);
+	list.AddItem(25, 30);
+	list.AddItem(30, 30);
+	list.AddItem(35, 40);
+	list.AddItem(40, 40);
+
+	for (local i = list.Begin(); list.HasNext(); i = list.Next()) {
+		list.RemoveItem(i - 10);
+		list.RemoveItem(i - 5);
+		list.RemoveItem(i);
+		print("   " + i);
+	}
+
+	list.AddItem(10, 10);
+	list.AddItem(20, 20);
+	list.AddItem(30, 30);
+	list.AddItem(40, 40);
+
+	print("");
+	for (local i = list.Begin(); list.HasNext(); i = list.Next()) {
+		list.SetValue(i, 2);
+		print("   " + i);
+	}
+	print("");
+	for (local i = list.Begin(); list.HasNext(); i = list.Next()) {
+		print("   " + i);
+	}
+
+	list = AIList();
+	list.Sort(AIAbstractList.SORT_BY_ITEM, true);
+	print("");
+	print(" Item Ascending");
+	list.AddItem( 5, 10);
+	list.AddItem(10, 10);
+	list.AddItem(15, 20);
+	list.AddItem(20, 20);
+	list.AddItem(25, 30);
+	list.AddItem(30, 30);
+	list.AddItem(35, 40);
+	list.AddItem(40, 40);
+
+	for (local i = list.Begin(); list.HasNext(); i = list.Next()) {
+		list.RemoveItem(i + 10);
+		list.RemoveItem(i + 5);
+		list.RemoveItem(i);
+		print("   " + i);
+	}
+
+	list.AddItem(10, 10);
+	list.AddItem(20, 20);
+	list.AddItem(30, 30);
+	list.AddItem(40, 40);
+
+	print("");
+	for (local i = list.Begin(); list.HasNext(); i = list.Next()) {
+		list.SetValue(i, 50);
+		print("   " + i);
+	}
+	print("");
+	for (local i = list.Begin(); list.HasNext(); i = list.Next()) {
+		print("   " + i);
+	}
 }
 
 function Regression::Std()
@@ -593,7 +733,7 @@
 	}
 	list2.Clear();
 	for (local i = 4000; i < 4003; i++) {
-		list2.AddItem(i, i);
+		list2.AddItem(i, i * 2);
 	}
 	list2.AddItem(1005, 1005);
 	list.AddList(list2);
@@ -601,6 +741,15 @@
 	for (local i = list.Begin(); list.HasNext(); i = list.Next()) {
 		print("    " + i + " => " + list.GetValue(i));
 	}
+	list[4000] = 50;
+	list[4006] = 12;
+
+	print("  foreach():");
+	foreach (idx, val in list) {
+		print("    " + idx + " => " + val);
+	}
+	print("  []:");
+	print("    4000 => " + list[4000]);
 
 	list.Clear();
 	print("  IsEmpty():     " + list.IsEmpty());
@@ -753,6 +902,10 @@
 	for (local i = list.Begin(); list.HasNext(); i = list.Next()) {
 		print("    " + i + " => " + list.GetValue(i));
 	}
+	print("  foreach():");
+	foreach (idx, val in list) {
+		print("    " + idx + " => " + val);
+	}
 }
 
 function Regression::Pathfinder()
@@ -1427,6 +1580,8 @@
 	this.Vehicle();
 	/* Order has to be after Vehicle */
 	this.Order();
+	print("");
+	print("  First Subsidy Test");
 	PrintSubsidy(0);
 
 	/* Sleep now, to give time for events to happen */
--- a/bin/ai/regression/regression.txt	Mon Jun 23 12:46:38 2008 +0000
+++ b/bin/ai/regression/regression.txt	Mon Jun 23 13:52:21 2008 +0000
@@ -10,6 +10,75 @@
  min(3, 6): 3
  max(6, 3): 6
  max(3, 6): 6
+ AIList Consistency Tests
+
+ Value Descending
+   40
+   25
+   10
+
+   40
+   30
+   20
+   10
+   40
+   30
+   20
+   10
+
+   40
+   30
+   20
+   10
+
+ Value Ascending
+   5
+   20
+   35
+
+   10
+   20
+   30
+   40
+   10
+   20
+   30
+   40
+
+   10
+   20
+   30
+   40
+
+ Item Descending
+   40
+   25
+   10
+
+   40
+   30
+   20
+   10
+
+   40
+   30
+   20
+   10
+
+ Item Ascending
+   5
+   20
+   35
+
+   10
+   20
+   30
+   40
+
+   10
+   20
+   30
+   40
 
 --Std--
  abs(-21): 21
@@ -488,9 +557,17 @@
     1005 => 1911873386
   AddList({1005, 4000, 4001, 4002}):
     1005 => 1005
-    4000 => 4000
-    4001 => 4001
-    4002 => 4002
+    4000 => 8000
+    4001 => 8002
+    4002 => 8004
+  foreach():
+    1005 => 1005
+    4000 => 50
+    4001 => 8002
+    4002 => 8004
+    4006 => 12
+  []:
+    4000 => 50
   IsEmpty():     true
 
 --AIAirport--
@@ -7215,6 +7292,10 @@
   Count():             1
   Location ListDump:
     11 => 33417
+  foreach():
+    11 => 33417
+
+  First Subsidy Test
       --Subsidy (0) --
         IsValidSubsidy():     false
         IsAwarded():          false
--- a/src/ai/api/Doxyfile	Mon Jun 23 12:46:38 2008 +0000
+++ b/src/ai/api/Doxyfile	Mon Jun 23 13:52:21 2008 +0000
@@ -200,7 +200,7 @@
 SEARCH_INCLUDES        = YES
 INCLUDE_PATH           =
 INCLUDE_FILE_PATTERNS  =
-PREDEFINED             =
+PREDEFINED             = DOXYGEN_SKIP
 EXPAND_AS_DEFINED      = DEF_COMMAND
 SKIP_FUNCTION_MACROS   = YES
 #---------------------------------------------------------------------------
--- a/src/ai/api/ai_abstractlist.cpp	Mon Jun 23 12:46:38 2008 +0000
+++ b/src/ai/api/ai_abstractlist.cpp	Mon Jun 23 13:52:21 2008 +0000
@@ -34,6 +34,11 @@
 	 * See if there is a next item of the sorter.
 	 */
 	virtual bool HasNext() = 0;
+
+	/**
+	 * Callback from the list if an item gets removed.
+	 */
+	virtual void Remove(int item) = 0;
 };
 
 /**
@@ -44,43 +49,77 @@
 	AIAbstractList::AIAbstractListBucket::iterator bucket_iter;
 	AIAbstractList::AIItemList *bucket_list;
 	AIAbstractList::AIItemList::iterator bucket_list_iter;
+	bool has_no_more_items;
+	int32 item_next;
 
 public:
 	AIAbstractListSorterValueAscending(AIAbstractList *list)
 	{
 		this->list = list;
 		this->bucket_list = NULL;
+		this->has_no_more_items = true;
+
+		this->item_next = 0;
 	}
 
 	int32 Begin()
 	{
 		if (this->list->buckets.empty()) return 0;
+		this->has_no_more_items = false;
 
 		this->bucket_iter = this->list->buckets.begin();
 		this->bucket_list = &(*this->bucket_iter).second;
 		this->bucket_list_iter = this->bucket_list->begin();
-		return *bucket_list_iter;
+		this->item_next = *this->bucket_list_iter;
+
+		int32 item_current = this->item_next;
+		FindNext();
+		return item_current;
+	}
+
+	void FindNext()
+	{
+		if (this->bucket_list == NULL) {
+			this->has_no_more_items = true;
+			return;
+		}
+
+		this->bucket_list_iter++;
+		if (this->bucket_list_iter == this->bucket_list->end()) {
+			this->bucket_iter++;
+			if (this->bucket_iter == this->list->buckets.end()) {
+				this->bucket_list = NULL;
+				return;
+			}
+			this->bucket_list = &(*this->bucket_iter).second;
+			this->bucket_list_iter = this->bucket_list->begin();
+		}
+		this->item_next = *this->bucket_list_iter;
 	}
 
 	int32 Next()
 	{
-		if (this->list->buckets.empty() || this->bucket_list == NULL) return 0;
+		if (!this->HasNext()) return 0;
 
-		this->bucket_list_iter++;
-		if (this->bucket_list_iter == this->bucket_list->end()) {
-			this->bucket_iter++;
-			if (this->bucket_iter == this->list->buckets.end()) return 0;
-			this->bucket_list = &(*this->bucket_iter).second;
-			this->bucket_list_iter = this->bucket_list->begin();
+		int32 item_current = this->item_next;
+		FindNext();
+		return item_current;
+	}
+
+	void Remove(int item)
+	{
+		if (!this->HasNext()) return;
+
+		/* If we remove the 'next' item, skip to the next */
+		if (item == this->item_next) {
+			FindNext();
+			return;
 		}
-		return *bucket_list_iter;
 	}
 
 	bool HasNext()
 	{
-		if (this->list->buckets.empty() || this->bucket_list == NULL) return false;
-
-		return this->bucket_iter != this->list->buckets.end() && this->bucket_list_iter != this->bucket_list->end();
+		return !(this->list->buckets.empty() || this->has_no_more_items);
 	}
 };
 
@@ -89,46 +128,88 @@
  */
 class AIAbstractListSorterValueDescending : public AIAbstractListSorter {
 private:
-	AIAbstractList::AIAbstractListBucket::reverse_iterator bucket_iter;
+	AIAbstractList::AIAbstractListBucket::iterator bucket_iter;
 	AIAbstractList::AIItemList *bucket_list;
-	AIAbstractList::AIItemList::reverse_iterator bucket_list_iter;
+	AIAbstractList::AIItemList::iterator bucket_list_iter;
+	bool has_no_more_items;
+	int32 item_next;
 
 public:
 	AIAbstractListSorterValueDescending(AIAbstractList *list)
 	{
 		this->list = list;
 		this->bucket_list = NULL;
+		this->has_no_more_items = true;
+
+		this->item_next = 0;
 	}
 
 	int32 Begin()
 	{
 		if (this->list->buckets.empty()) return 0;
+		this->has_no_more_items = false;
 
-		this->bucket_iter = this->list->buckets.rbegin();
+		/* Go to the end of the bucket-list */
+		this->bucket_iter = this->list->buckets.begin();
+		for (int i = this->list->buckets.size(); i > 1; i--) this->bucket_iter++;
 		this->bucket_list = &(*this->bucket_iter).second;
-		this->bucket_list_iter = this->bucket_list->rbegin();
-		return *bucket_list_iter;
+
+		/* Go to the end of the items in the bucket */
+		this->bucket_list_iter = this->bucket_list->begin();
+		for (int i = this->bucket_list->size(); i > 1; i--) this->bucket_list_iter++;
+		this->item_next = *this->bucket_list_iter;
+
+		int32 item_current = this->item_next;
+		FindNext();
+		return item_current;
+	}
+
+	void FindNext()
+	{
+		if (this->bucket_list == NULL) {
+			this->has_no_more_items = true;
+			return;
+		}
+
+		if (this->bucket_list_iter == this->bucket_list->begin()) {
+			if (this->bucket_iter == this->list->buckets.begin()) {
+				this->bucket_list = NULL;
+				return;
+			}
+			this->bucket_iter--;
+			this->bucket_list = &(*this->bucket_iter).second;
+			/* Go to the end of the items in the bucket */
+			this->bucket_list_iter = this->bucket_list->begin();
+			for (int i = this->bucket_list->size(); i > 1; i--) this->bucket_list_iter++;
+		} else {
+			this->bucket_list_iter--;
+		}
+		this->item_next = *this->bucket_list_iter;
 	}
 
 	int32 Next()
 	{
-		if (this->list->buckets.empty() || this->bucket_list == NULL) return 0;
+		if (!this->HasNext()) return 0;
 
-		this->bucket_list_iter++;
-		if (this->bucket_list_iter == this->bucket_list->rend()) {
-			this->bucket_iter++;
-			if (this->bucket_iter == this->list->buckets.rend()) return 0;
-			this->bucket_list = &(*this->bucket_iter).second;
-			this->bucket_list_iter = this->bucket_list->rbegin();
+		int32 item_current = this->item_next;
+		FindNext();
+		return item_current;
+	}
+
+	void Remove(int item)
+	{
+		if (!this->HasNext()) return;
+
+		/* If we remove the 'next' item, skip to the next */
+		if (item == this->item_next) {
+			FindNext();
+			return;
 		}
-		return *bucket_list_iter;
 	}
 
 	bool HasNext()
 	{
-		if (this->list->buckets.empty() || this->bucket_list == NULL) return false;
-
-		return this->bucket_iter != this->list->buckets.rend() && this->bucket_list_iter != this->bucket_list->rend();
+		return !(this->list->buckets.empty() || this->has_no_more_items);
 	}
 };
 
@@ -138,34 +219,61 @@
 class AIAbstractListSorterItemAscending : public AIAbstractListSorter {
 private:
 	AIAbstractList::AIAbstractListMap::iterator item_iter;
+	bool has_no_more_items;
+	int32 item_next;
 
 public:
 	AIAbstractListSorterItemAscending(AIAbstractList *list)
 	{
 		this->list = list;
+		this->has_no_more_items = true;
 	}
 
 	int32 Begin()
 	{
 		if (this->list->items.empty()) return 0;
+		this->has_no_more_items = false;
 
 		this->item_iter = this->list->items.begin();
-		return (*this->item_iter).first;
+		this->item_next = (*this->item_iter).first;
+
+		int32 item_current = this->item_next;
+		FindNext();
+		return item_current;
+	}
+
+	void FindNext()
+	{
+		if (this->item_iter == this->list->items.end()) {
+			this->has_no_more_items = true;
+			return;
+		}
+		this->item_iter++;
+		if (this->item_iter != this->list->items.end()) item_next = (*this->item_iter).first;
 	}
 
 	int32 Next()
 	{
-		if (this->list->items.empty()) return 0;
+		if (!this->HasNext()) return 0;
 
-		this->item_iter++;
-		return (*this->item_iter).first;
+		int32 item_current = this->item_next;
+		FindNext();
+		return item_current;
+	}
+
+	void Remove(int item) {
+		if (!this->HasNext()) return;
+
+		/* If we remove the 'next' item, skip to the next */
+		if (item == this->item_next) {
+			FindNext();
+			return;
+		}
 	}
 
 	bool HasNext()
 	{
-		if (this->list->items.empty()) return false;
-
-		return this->item_iter != this->list->items.end();
+		return !(this->list->items.empty() || this->has_no_more_items);
 	}
 };
 
@@ -175,34 +283,62 @@
 class AIAbstractListSorterItemDescending : public AIAbstractListSorter {
 private:
 	AIAbstractList::AIAbstractListMap::reverse_iterator item_iter;
+	bool has_no_more_items;
+	int32 item_next;
 
 public:
 	AIAbstractListSorterItemDescending(AIAbstractList *list)
 	{
 		this->list = list;
+		this->has_no_more_items = true;
 	}
 
 	int32 Begin()
 	{
 		if (this->list->items.empty()) return 0;
+		this->has_no_more_items = false;
 
 		this->item_iter = this->list->items.rbegin();
-		return (*this->item_iter).first;
+		this->item_next = (*this->item_iter).first;
+
+		int32 item_current = this->item_next;
+		FindNext();
+		return item_current;
+	}
+
+	void FindNext()
+	{
+		if (this->item_iter == this->list->items.rend()) {
+			this->has_no_more_items = true;
+			return;
+		}
+		this->item_iter++;
+		if (this->item_iter != this->list->items.rend()) item_next = (*this->item_iter).first;
 	}
 
 	int32 Next()
 	{
-		if (this->list->items.empty()) return 0;
+		if (!this->HasNext()) return 0;
 
-		this->item_iter++;
-		return (*this->item_iter).first;
+		int32 item_current = this->item_next;
+		FindNext();
+		return item_current;
+	}
+
+	void Remove(int item)
+	{
+		if (!this->HasNext()) return;
+
+		/* If we remove the 'next' item, skip to the next */
+		if (item == this->item_next) {
+			FindNext();
+			return;
+		}
 	}
 
 	bool HasNext()
 	{
-		if (this->list->items.empty()) return false;
-
-		return this->item_iter != this->list->items.rend();
+		return !(this->list->items.empty() || this->has_no_more_items);
 	}
 };
 
@@ -245,8 +381,11 @@
 {
 	if (!this->HasItem(item)) return;
 
-	this->buckets[this->GetValue(item)].erase(item);
-	if (this->buckets[this->GetValue(item)].empty()) this->buckets.erase(this->GetValue(item));
+	int32 value = this->GetValue(item);
+
+	this->sorter->Remove(item);
+	this->buckets[value].erase(item);
+	if (this->buckets[value].empty()) this->buckets.erase(value);
 	this->items.erase(item);
 }
 
@@ -295,8 +434,11 @@
 {
 	if (!this->HasItem(item)) return false;
 
-	this->buckets[this->GetValue(item)].erase(item);
-	if (this->buckets[this->GetValue(item)].empty()) this->buckets.erase(this->GetValue(item));
+	int32 value_old = this->GetValue(item);
+
+	this->sorter->Remove(item);
+	this->buckets[value_old].erase(item);
+	if (this->buckets[value_old].empty()) this->buckets.erase(value_old);
 	this->items[item] = value;
 	this->buckets[value].insert(item);
 
@@ -539,6 +681,37 @@
 	this->RemoveList(&tmp);
 }
 
+SQInteger AIAbstractList::_get(HSQUIRRELVM vm) {
+	if (sq_gettype(vm, 2) != OT_INTEGER) return SQ_ERROR;
+
+	SQInteger idx;
+	sq_getinteger(vm, 2, &idx);
+
+	if (!this->HasItem(idx)) return SQ_ERROR;
+
+	sq_pushinteger(vm, this->GetValue(idx));
+	return 1;
+}
+
+SQInteger AIAbstractList::_nexti(HSQUIRRELVM vm) {
+	if (sq_gettype(vm, 2) == OT_NULL) {
+		sq_pushinteger(vm, this->Begin());
+		return 1;
+	}
+
+	SQInteger idx;
+	sq_getinteger(vm, 2, &idx);
+
+	int val = this->Next();
+	if (!this->HasNext()) {
+		sq_pushnull(vm);
+		return 1;
+	}
+
+	sq_pushinteger(vm, val);
+	return 1;
+}
+
 SQInteger AIAbstractList::Valuate(HSQUIRRELVM vm) {
 	int nparam = sq_gettop(vm) - 2;
 
@@ -548,12 +721,10 @@
 	sq_getstackobj(vm, 2, &obj_func);
 
 	if (sq_isclass(obj_list)) {
-		sq_throwerror(vm, _SC("parameter 1 has an invalid type (expected instance)"));
-		return -1;
+		return sq_throwerror(vm, _SC("parameter 1 has an invalid type (expected instance)"));
 	}
 	if (sq_isfunction(obj_func)) {
-		sq_throwerror(vm, _SC("parameter 2 has an invalid type (expected function)"));
-		return -1;
+		return sq_throwerror(vm, _SC("parameter 2 has an invalid type (expected function)"));
 	}
 
 	sq_addref(vm, &obj_func);
@@ -582,7 +753,7 @@
 		}
 
 		/* Call the function */
-		if (SQ_FAILED(sq_call(vm, nparam + 2, SQTrue, SQTrue))) return -1;
+		if (SQ_FAILED(sq_call(vm, nparam + 2, SQTrue, SQTrue))) return SQ_ERROR;
 
 		/* Retreive the return value */
 		SQInteger value;
@@ -602,8 +773,7 @@
 				sq_release(vm, &obj_func);
 				for (int i = 0; i < nparam; i++) sq_release(vm, &obj_params[i]);
 
-				sq_throwerror(vm, _SC("return value of valuator is not valid (not integer/bool)"));
-				return -1;
+				return sq_throwerror(vm, _SC("return value of valuator is not valid (not integer/bool)"));
 			}
 		}
 		/* Remove junk */
--- a/src/ai/api/ai_abstractlist.hpp	Mon Jun 23 12:46:38 2008 +0000
+++ b/src/ai/api/ai_abstractlist.hpp	Mon Jun 23 13:52:21 2008 +0000
@@ -113,6 +113,8 @@
 	 * @param item the item to set the value for.
 	 * @param value the value to give to the item
 	 * @return true if we could set the item to value, false otherwise.
+	 * @note Changing values of items while looping through a list might cause
+	 *  entries to be skipped. Be very careful with such operations.
 	 */
 	bool SetValue(int32 item, int32 value);
 
@@ -222,15 +224,31 @@
 	 */
 	void KeepList(AIAbstractList *list);
 
+#ifndef DOXYGEN_SKIP
+	/**
+	 * Used for 'foreach()' and [] get from Squirrel.
+	 */
+	SQInteger _get(HSQUIRRELVM vm);
+
+	/**
+	 * Used for 'foreach()' from Squirrel.
+	 */
+	SQInteger _nexti(HSQUIRRELVM vm);
+
+	/**
+	 * The Valuate() wrapper from Squirrel.
+	 */
+	SQInteger Valuate(HSQUIRRELVM vm);
+#else
 	/**
 	 * Give all items a value defined by the valuator you give.
-	 * @param vm Internal pointer, not something you need to specify.
-	 * @return The result of the valuation.
-	 * @note The first param for this function is the function you want to use
-	 *  to valuate the items in the list. It should accept at least one integer
-	 *  which will be set to the item, and should return an integer, which will
-	 *  become the value of the item. Additional parameters are possible, then
-	 *  you need to add them to the Valuator to. Example:
+	 * @param valuator_function The function which will be doing the valuation.
+	 * @param params The params to give to the valuators (minus the first param,
+	 *  which is always the index-value we are valuating).
+	 * @note You can write your own valuators and use them. Just remember that
+	 *  the first parameter should be the index-value, and it should return
+	 *  an integer.
+	 * @note Example:
 	 *  list.Valuate(AIBridge.GetPrice, 5);
 	 *  list.Valuate(AIBridge.GetMaxLength);
 	 *  function MyVal(bridge_id, myparam) {
@@ -238,7 +256,8 @@
 	 *  }
 	 *  list.Valuate(MyVal, 12);
 	 */
-	SQInteger Valuate(HSQUIRRELVM vm);
+	void Valuate(void *valuator_function, int params, ...);
+#endif /* DOXYGEN_SKIP */
 };
 
 #endif /* AI_LIST_HPP */
--- a/src/ai/api/ai_abstractlist.hpp.sq	Mon Jun 23 12:46:38 2008 +0000
+++ b/src/ai/api/ai_abstractlist.hpp.sq	Mon Jun 23 13:52:21 2008 +0000
@@ -51,6 +51,8 @@
 	SQAIAbstractList.DefSQMethod(engine, &AIAbstractList::KeepTop,            "KeepTop",            2, "xi");
 	SQAIAbstractList.DefSQMethod(engine, &AIAbstractList::KeepBottom,         "KeepBottom",         2, "xi");
 	SQAIAbstractList.DefSQMethod(engine, &AIAbstractList::KeepList,           "KeepList",           2, "xx");
+	SQAIAbstractList.DefSQAdvancedMethod(engine, &AIAbstractList::_get,       "_get");
+	SQAIAbstractList.DefSQAdvancedMethod(engine, &AIAbstractList::_nexti,     "_nexti");
 	SQAIAbstractList.DefSQAdvancedMethod(engine, &AIAbstractList::Valuate,    "Valuate");
 
 	SQAIAbstractList.PostRegister(engine);
--- a/src/ai/api/ai_list.cpp	Mon Jun 23 12:46:38 2008 +0000
+++ b/src/ai/api/ai_list.cpp	Mon Jun 23 13:52:21 2008 +0000
@@ -2,6 +2,7 @@
 
 /** @file ai_list.cpp Implementation of AIList. */
 
+#include <squirrel.h>
 #include "ai_list.hpp"
 
 void AIList::AddItem(int32 item, int32 value)
@@ -19,3 +20,26 @@
 {
 	AIAbstractList::RemoveItem(item);
 }
+
+SQInteger AIList::_set(HSQUIRRELVM vm) {
+	if (sq_gettype(vm, 2) != OT_INTEGER) return SQ_ERROR;
+	if (sq_gettype(vm, 3) != OT_INTEGER || sq_gettype(vm, 3) == OT_NULL) {
+		return sq_throwerror(vm, _SC("you can only assign integers to this list"));
+	}
+
+	SQInteger idx, val;
+	sq_getinteger(vm, 2, &idx);
+	if (sq_gettype(vm, 3) == OT_NULL) {
+		this->RemoveItem(idx);
+		return 0;
+	}
+
+	sq_getinteger(vm, 3, &val);
+	if (!this->HasItem(idx)) {
+		this->AddItem(idx, val);
+		return 0;
+	}
+
+	this->ChangeItem(idx, val);
+	return 0;
+}
--- a/src/ai/api/ai_list.hpp	Mon Jun 23 12:46:38 2008 +0000
+++ b/src/ai/api/ai_list.hpp	Mon Jun 23 13:52:21 2008 +0000
@@ -35,6 +35,13 @@
 	 * @param item the item to remove.
 	 */
 	void RemoveItem(int32 item);
+
+#ifndef DOXYGEN_SKIP
+	/**
+	 * Used for [] set from Squirrel.
+	 */
+	SQInteger _set(HSQUIRRELVM vm);
+#endif /* DOXYGEN_SKIP */
 };
 
 #endif /* AI_LIST_HPP */
--- a/src/ai/api/ai_list.hpp.sq	Mon Jun 23 12:46:38 2008 +0000
+++ b/src/ai/api/ai_list.hpp.sq	Mon Jun 23 13:52:21 2008 +0000
@@ -22,6 +22,7 @@
 	SQAIList.DefSQMethod(engine, &AIList::AddItem,    "AddItem",    3, "xii");
 	SQAIList.DefSQMethod(engine, &AIList::ChangeItem, "ChangeItem", 3, "xii");
 	SQAIList.DefSQMethod(engine, &AIList::RemoveItem, "RemoveItem", 2, "xi");
+	SQAIList.DefSQAdvancedMethod(engine, &AIList::_set, "_set");
 
 	SQAIList.PostRegister(engine);
 }
--- a/src/ai/api/squirrel_export.awk	Mon Jun 23 12:46:38 2008 +0000
+++ b/src/ai/api/squirrel_export.awk	Mon Jun 23 13:52:21 2008 +0000
@@ -81,6 +81,20 @@
 /^(	*)protected/ { if (cls_level == 1) public = "false"; next; }
 /^(	*)private/   { if (cls_level == 1) public = "false"; next; }
 
+# Ignore special doxygen blocks
+/^#ifndef DOXYGEN_SKIP/ { doxygen_skip = "next"; next; }
+/^#ifdef DOXYGEN_SKIP/  { doxygen_skip = "true"; next; }
+/^#endif/               { doxygen_skip = "false"; next; }
+/^#else/                {
+	if (doxygen_skip == "next") {
+		doxygen_skip = "true";
+	} else {
+		doxygen_skip = "false";
+	}
+	next;
+}
+{ if (doxygen_skip == "true") next }
+
 # Ignore the comments
 /^#/             { next; }
 /\/\*.*\*\//     { comment = "false"; next; }