Understandable, I didn't really elaborate. Basically, the variable for the item in the foreach loop ("current" in my example) is a reference back to the element inside the array (bullet[current's index]). This doesn't really match up with the rest of the language. For example:
type MyType
a as integer
b as integer
endtype
var1 as MyType
var2 as MyType
var1.a = 5
var2 = var1
Print(var1.a) // 5
Print(var2.a) // 5
var2.a = 2
Print(var1.a) // 5
Print(var2.a) // 2
That example should make perfect sense, since UDTs are essentially structs. When var2 is assigned to var1, var1's values are copied into var2, it does not become a pointer. If they were reference types:
referencetype MyType
a as integer
b as integer
endtype
// some pseudocode since reference types don't actually exist in AGK
var1 as MyType : var1 = new MyType
var2 as MyType
var1.a = 5
var2 = var1 // var2 is a reference to var1
Print(var1.a) // 5
Print(var2.a) // 5
var2.a = 2
Print(var1.a) // 2 <-- var1.a is also 2!
Print(var2.a) // 2
Now, the problem here is that the current item in the foreach loop looks a lot like that sort of reference. So somebody might be tempted to do this:
foreach bullet in bullets[]
MoveBullet(bullet)
next bullet
function MoveBullet(obj as BulletType)
obj.x = obj.x + 1
endfunction
However, that won't work. 'bullet' in the foreach loop is a reference/pointer type to bullets['bullet''s index]. However, when we pass it as a parameter, we are just copying it into 'obj'. So we need the index in order to act on bullet in another scope, such as that of a function. This may be confusing and, for a large project, makes foreach more of a convenience for loops that are entirely self contained than something that can be used for entity management.
If the foreach loop treats the item variable like the rest of the language treats UDTs, then any modifications you make to it won't be saved back to the array, which diminishes its value a bit (but would be in line with how foreach on an array of structs works in C#).