Handling several Type-lists

Blitz3D Forums/Blitz3D Beginners Area/Handling several Type-lists

AJirenius(Posted 2006) [#1]
Ok, here's a problem for you (and me):

I bumped into this one several times now and at this moment I cannot make any ugly code to avoid it so I need to face it and solve it. I will use a previous example just to make you understand the issue.

I am programming a 2-player game and everything works fine. All the bullets I am firing are put as a Type and handled the ordinary way with a "Type-list" (First, Each, Last etc)
Now when Im trying to create the other player everything is fine (due to my EXCELLENT coding :S) but when I come to the Type thing it just wont work as it seems that I cannot create several kinds of list of the same Type.

At first I thought it was like this:

Player1.bullet = New bullet
Player2.bullet = New bullet

and I would have 2 different lists

I was obviously wrong and now I wonder how I can create 2 different lists that will in some way be handled equally. I dont want to create a new type!!!

thanks for help


AJirenius(Posted 2006) [#2]
Ok, that one was confusing I can see. Ill try to make it as simple as possible:

You all know the "Chair-example" in the documentation to descripe Types? Good.

What if I want to create several rooms like this?

Type Building
Field room.Chair
End Type

Now room in the Building must have it's own list with positions for the chairs. But this doesn't work! How do I get it to work? Any ideas??


GfK(Posted 2006) [#3]
Imagine you have a box, labelled 'bullets', and a handful of bullets. Every time someone fires a bullet, it goes into the box.

End result - a box full of bullets that all look the same.

What you need is some way of identifying where each bullet came from.

You can do this by adding an 'owner%' field to your custom datatype. You can then parse that in your For/Each loop to determine whose bullet you're dealing with.


AJirenius(Posted 2006) [#4]
Ok, that might work on the bullets but not on the things Im doing now :P ok I give you my idea now and maybe someone can figure out a good way to confront this:

I'm creating an Effectlib which will make creating visualeffects a la FinalFantasy VII,VIII a lot easier(combining Particlesystems, Meshes, Sprites on a timeline)

Example of such effect would be an explosion where at first an animated sprite is exploding in the middle. At the same time particles are flying in the air and a big halfsphere is lighten up the close area. After a short while the sphere are shrinking and also a particlesystem with smoke appears. After a further while it will diminish and disappear leaving some dustmarks on the ground.

Now my idea so far is to have an object called FX-Entity that contains all these FX-objects (meshes, systems, sprites)
Every FX-object also have a Timeline where actions on the specific object can be taken (move, scale, alphacontrol, rotate etc). Now I thought about setting it up like this:
Type FXEvent
	Field Time	   ; When will this action occur due to FXEntities timeline
	Field Action       ; Which action will occur? (Move, Rotate, Scale, Alpha, Texturechange etc)
End Type


Type FXObject
	Field Ev.FXEvent   ; Events regarding the specific object
End Type


Type FXEntity
	Field Obj.FXObject ; Contains all objects used by the specific Effect.
        Field TimeCounter  ; Starts from 0 and increases for every update.
End Type


Now it surely doesn't work as there is no seperate lists.

Now how would you solve this in a good way. Remember that it might occur several different effects on the screen (but with two separate timelines).


Who was John Galt?(Posted 2006) [#5]
There's no easy, good way. I hit this wall before. You either have to implement your own lists (stick a field next.bullet in bullet and iterate through lists yourself) or create more than 1 identical bullet type so they have separate lists... or buy Blitzmax which has multiple list functionality.


AJirenius(Posted 2006) [#6]
Well.. its a 3Dgame so I have to wait for MAX3D .. :(


b32(Posted 2006) [#7]
I think Gfk is right. To trace back to what object/entity/type a certain type belongs, give it an owner field. You can include a custom type even before it is declared. The idea of Nomen sounds interesting, too, of making an own linked list.


AJirenius(Posted 2006) [#8]
I guess I have to make a Child->Parent system. But I still refer it to uglycode :D


Stevie G(Posted 2006) [#9]
You could make use of blitz arrays but unfortunately they're not dynamic in size. Otherwise go with the linked list suggestion by Lomen.

const MaxObjects = 10

Type FXEntity
	Field Obj[ MaxObjects ].FXObject ; Contains all objects used by the specific Effect.
        Field TimeCounter  ; Starts from 0 and increases for every update.
End Type



(tu) sinu(Posted 2006) [#10]
Try this, it's dynamic and you can have unlimited lists with unlimited object types

usage:

global ListA = CreateList()
global ListB = CreateList()

ThisBullet.bullet = new bullet
listaddlast listA,handle(bullet)

ThisBullet.bullet = new bullet
listaddlast listB,handle(bullet)

count = countlist(listA)
if count
 for i = 1 to count
  ThisBullet.TBullet = object.bullet(listatindex(listA,i))
  if not ThisBullet = null
  ;do stuff
  endif
 next
end if


Const TLIST_DATA_SIZE = 4

Type TList
	Field list
End Type

;-----------------------------------------------------------------------------------------
;-----------------------------------------------------------------------------------------
Function CreateList%()
	
	;DebugLog "list created"
	ThisList.TList = New TList
	ThisList\list = CreateBank(TLIST_DATA_SIZE)
	Return Handle(ThisList)

End Function
;-----------------------------------------------------------------------------------------
;-----------------------------------------------------------------------------------------
Function DeleteList(id%)

ThisList.TList = Object.TList(id)
If ThisList.TList = Null DebugLog "No list to delete" : Return -1

	FreeBank ThisList\list
	Delete ThisList

End Function
;-----------------------------------------------------------------------------------------
;-----------------------------------------------------------------------------------------
Function ListAddFirst%(id%,ObjectHandle%)

ThisList.TList = Object.TList(id)
If ThisList.TList = Null DebugLog "ListAddLast failed no list" : Return -1

If ObjectHandle%<1 DebugLog "ListAddLast failed no object" : Return -1

count = CountList(id)
If Not count
	ListAddLast(id,ObjectHandle%)
	Return 1
EndIf

	TempBank = CreateBank(BankSize(ThisList\list))
	CopyBank ThisList\list,TLIST_DATA_SIZE,TempBank,TLIST_DATA_SIZE,BankSize(ThisList\list)-TLIST_DATA_SIZE
	
	PokeInt ThisList\list,TLIST_DATA_SIZE,ObjectHandle%
	PokeInt ThisList\list,0,count + 1
	
	ResizeBank ThisList\list,BankSize(ThisList\list)+TLIST_DATA_SIZE
	CopyBank TempBank,TLIST_DATA_SIZE,ThisList\list,TLIST_DATA_SIZE*2,BankSize(TempBank)-TLIST_DATA_SIZE
	
	FreeBank TempBank
	Return 1

End Function
;-----------------------------------------------------------------------------------------
;-----------------------------------------------------------------------------------------
Function ListAddLast%(id%,ObjectHandle%)

ThisList.TList = Object.TList(id)
If ThisList.TList = Null DebugLog "ListAddLast failed no list" : Return -1
If ObjectHandle%<1 DebugLog "ListAddLast failed no object" : Return -1

	PokeInt ThisList\list,0,PeekInt(ThisList\list,0)+1
	ResizeBank(ThisList\list,BankSize(ThisList\list)+TLIST_DATA_SIZE)
	PokeInt ThisList\list,BankSize(ThisList\list)-TLIST_DATA_SIZE,ObjectHandle
	Return 1
	
End Function
;-----------------------------------------------------------------------------------------
;-----------------------------------------------------------------------------------------
Function ListAtIndex%(id%,index%)

ThisList.TList = Object.TList(id)
If ThisList.TList = Null DebugLog "No list to ListAtIndex" : Return -1

If index<1 Return -1
If ThisList.TList = Null Return -1

Local Offset = index*TLIST_DATA_SIZE

If BankSize(ThisList\list)<(Offset+TLIST_DATA_SIZE) Return -1

Count = PeekInt(ThisList\list,0)
If count<1 Return -1

	Return PeekInt(ThisList\list,Offset)

End Function
;-----------------------------------------------------------------------------------------
;-----------------------------------------------------------------------------------------
Function ListFirst%(id%)

ThisList.TList = Object.TList(id)
If ThisList.TList = Null Return -1

Local Offset = TLIST_DATA_SIZE

If BankSize(ThisList\list)<(Offset+TLIST_DATA_SIZE) Return -1

	Return PeekInt(ThisList\list,Offset)

End Function
;-----------------------------------------------------------------------------------------
;-----------------------------------------------------------------------------------------
Function ListLast%(id%)

ThisList.TList = Object.TList(id)
If ThisList.TList = Null Return -1

index = PeekInt(ThisList\list,0)

Local Offset = index*TLIST_DATA_SIZE

If BankSize(ThisList\list)<(Offset+TLIST_DATA_SIZE) Return -1

	Return PeekInt(ThisList\list,Offset)

End Function
;-----------------------------------------------------------------------------------------
;-----------------------------------------------------------------------------------------
Function ListDeleteAtIndex%(id%,index%)
If index<1 Return -1

ThisList.TList = Object.TList(id)
If ThisList.TList = Null DebugLog "No list to ListDeleteAtIndex" : Return -1

Local Offset = index*TLIST_DATA_SIZE

If BankSize(ThisList\list)<(Offset+TLIST_DATA_SIZE) Return -1

Count = PeekInt(ThisList\list,0)
If count<1 Return -1

Local offset_2

For i = index To Count-1
	Offset_2 = i*TLIST_DATA_SIZE
	PokeInt ThisList\list,Offset_2,PeekInt(ThisList\list,Offset_2+TLIST_DATA_SIZE)
Next

PokeInt ThisList\list,0,count - 1
ResizeBank ThisList\list,BankSize(ThisList\list)-TLIST_DATA_SIZE


End Function
;-----------------------------------------------------------------------------------------
;-----------------------------------------------------------------------------------------
Function CopyList(id%,id2%)

source.TList = Object.TList(id)
dest.TList = Object.TList(id2)

If source = Null Return -1
If dest = Null Return -1

count = PeekInt(source\list,0)
PokeInt dest\list,0,count

ResizeBank dest\list,(count*TLIST_DATA_SIZE)+TLIST_DATA_SIZE
CopyBank source\list,0,dest\list,0,BankSize(source\list);(count*TLIST_DATA_SIZE)+TLIST_DATA_SIZE

End Function
;-----------------------------------------------------------------------------------------
;-----------------------------------------------------------------------------------------
Function CountList%(id)

ThisList.TList = Object.TList(id)
If ThisList.TList = Null Return -1

	Return PeekInt(ThisList\list,0)

End Function
;-----------------------------------------------------------------------------------------
;-----------------------------------------------------------------------------------------
Function ClearList(id)

ThisList.TList = Object.TList(id)
If ThisList.TList = Null Return -1

ResizeBank ThisList\list,TLIST_DATA_SIZE
PokeInt ThisList\list,0,0

End Function
;-----------------------------------------------------------------------------------------
;-----------------------------------------------------------------------------------------
Function ListIsEmpty%(id%)
;returns if a list is empty or not

ThisList.TList = Object.TList(id)
ListCount = PeekInt(ThisList\list,0)

	If ListCount Return 1 Else Return 0

End Function
;-----------------------------------------------------------------------------------------
;-----------------------------------------------------------------------------------------
Function CheckList(id%,ObjectHandle%)
;remove any <1 values from a list

ThisList.TList = Object.TList(id)
If ThisList = Null Return -1

If ObjectHandle<1 Return -1

Count = PeekInt(ThisList\list,0)


Local Offset_2


TempList = CreateBank(TLIST_DATA_SIZE)
PokeInt TempList,0,0
	
	For i = 1 To Count
	
		Offset_2 = i*TLIST_DATA_SIZE

		If PeekInt(ThisList\list,Offset_2)>0
			ResizeBank(TempList,BankSize(TempList)+TLIST_DATA_SIZE);resize list
			PokeInt TempList,0,PeekInt(TempList,0)+1;add to list count
			PokeInt TempList,BankSize(TempList)-TLIST_DATA_SIZE,PeekInt(ThisList\list,Offset_2)
		EndIf
	Next
	
	Count = PeekInt(TempList,0)
	
	ResizeBank ThisList\list,TLIST_DATA_SIZE
	PokeInt ThisList\list,0,0
	
	For i = 1 To Count
	
		Offset_2 = i*TLIST_DATA_SIZE

		If PeekInt(TempList,Offset_2)>0
			ResizeBank(ThisList\list,BankSize(ThisList\list)+TLIST_DATA_SIZE)
			PokeInt ThisList\list,0,PeekInt(ThisList\list,0)+1
			PokeInt ThisList\list,BankSize(ThisList\list)-TLIST_DATA_SIZE,PeekInt(TempList,Offset_2)
		EndIf
	Next

FreeBank TempList


End Function
;-----------------------------------------------------------------------------------------
;-----------------------------------------------------------------------------------------
Function ListRemove(id%,ObjectHandle%,all% = 0)
;remove the first object from a list, option to remove all instances!!

ThisList.TList = Object.TList(id)
If ThisList = Null Return -1
If ObjectHandle<1 Return -1

Count = PeekInt(ThisList\list,0)

Local found

Local Offset_2

For i = 1 To Count

Offset_2 = i*TLIST_DATA_SIZE

	If PeekInt(ThisList\list,Offset_2) = ObjectHandle
		PokeInt ThisList\list,Offset_2,0
		found = True
			If all>0
				Exit
			EndIf
	EndIf
Next

If found = True

TempList = CreateBank(TLIST_DATA_SIZE)
PokeInt TempList,0,0
	
	For i = 1 To Count
	
		Offset_2 = i*TLIST_DATA_SIZE

		If PeekInt(ThisList\list,Offset_2)>0
			ResizeBank(TempList,BankSize(TempList)+TLIST_DATA_SIZE);resize list
			PokeInt TempList,0,PeekInt(TempList,0)+1;add to list count
			PokeInt TempList,BankSize(TempList)-TLIST_DATA_SIZE,PeekInt(ThisList\list,Offset_2)
		EndIf
	Next
	
	Count = PeekInt(TempList,0)
	
	ResizeBank ThisList\list,TLIST_DATA_SIZE
	PokeInt ThisList\list,0,0
	
	For i = 1 To Count
	
		Offset_2 = i*TLIST_DATA_SIZE

		If PeekInt(TempList,Offset_2)>0
			ResizeBank(ThisList\list,BankSize(ThisList\list)+TLIST_DATA_SIZE)
			PokeInt ThisList\list,0,PeekInt(ThisList\list,0)+1
			PokeInt ThisList\list,BankSize(ThisList\list)-TLIST_DATA_SIZE,PeekInt(TempList,Offset_2)
		EndIf
	Next

FreeBank TempList

EndIf

End Function
;-----------------------------------------------------------------------------------------
;-----------------------------------------------------------------------------------------
Function ListContains%(id%,ObjectHandle%)
;get if an object exists in the list

ThisList.TList = Object.TList(id)
If ThisList = Null Return -1

If ObjectHandle<1 Return -1

Count = PeekInt(ThisList\list,0)
If count
	For i = 1 To Count
		If PeekInt(ThisList\list,i*TLIST_DATA_SIZE) = ObjectHandle
			Return 1
		EndIf
	Next
EndIf

	Return 0

End Function
;-----------------------------------------------------------------------------------------
;-----------------------------------------------------------------------------------------
Function ReverseList(id%)
;swap the order of the list around

ThisList.TList = Object.TList(id)
If ThisList = Null Return -1

Count = PeekInt(ThisList\list,0)
If count<1 Return -1

TempList = CreateBank(TLIST_DATA_SIZE)
PokeInt TempList,0,0

PokeInt ThisList\list,(index*TLIST_DATA_SIZE),0



For i = count To 1 Step -1
		ResizeBank(TempList,BankSize(TempList)+TLIST_DATA_SIZE)
		PokeInt TempList,0,PeekInt(TempList,0)+1
		PokeInt TempList,BankSize(TempList)-TLIST_DATA_SIZE,PeekInt(ThisList\list,(i*TLIST_DATA_SIZE))
Next

Count = PeekInt(TempList,0)

ResizeBank ThisList\list,TLIST_DATA_SIZE
PokeInt ThisList\list,0,0

For i = 1 To count
		ResizeBank(ThisList\list,BankSize(ThisList\list)+TLIST_DATA_SIZE)
		PokeInt ThisList\list,0,PeekInt(ThisList\list,0)+1
		PokeInt ThisList\list,BankSize(ThisList\list)-TLIST_DATA_SIZE,PeekInt(TempList,(i*TLIST_DATA_SIZE))
Next

FreeBank TempList


End Function
;-----------------------------------------------------------------------------------------
;-----------------------------------------------------------------------------------------
Function SwapLists%(id%,id2%)
;swap the contents of 2 lists

ThisListA.TList = Object.TList(id)
ThisListB.TList = Object.TList(id2)

If ThisListA = Null Return -1
If ThisListB = Null Return -1


TempListB = CreateList()
CopyList(id2,TempListB)

CopyList(id,id2)
CopyList(TempListB,id)

DeleteList(TempListB)

	Return 1
	
End Function
;-----------------------------------------------------------------------------------------
;-----------------------------------------------------------------------------------------

;-----------------------------------------------------------------------------------------
;-----------------------------------------------------------------------------------------




octothorpe(Posted 2006) [#11]
An age old problem for Blitz3d developers, AJirenius, and my favourite one.

Blitz3d exposes its internal linked-list to the developer (via First, Last, Each, etc) which is handy for simple projects, but often useless for more complicated projects. The conventional approach is to add linked-list node code to every Type - this means a lot of duplicated code scattered around your project. Ugh.

Blitz3d provides one (undocumented) way to deal with objects polymorphically: Object() and Handle(). Using these, you can develop separate list management code and not have to duplicate it for every type. This is precisely what (tu) sinu is doing above using banks, and what I've done in three different ways, including banks and linked-lists. Check out the link to Container Classes in my sig.

(tu) sinu, we have a very similar approach. I would call your "List" above a "vector container." The term "list" tends to be associated to linked-lists. Also, use a [codebox]! :)


Adam Novagen(Posted 2006) [#12]
Alright, I'm not sure if this is completely relevant or not, but I think there's a simple solution for distinguishing between types. Recently, I made some functions that could create a "debug window" in my program, a window of a pre-specified size that could display individual pages of data. Each debug "page" had its own title, and contained 100 variables which could be set to variables used in my program, so that I could see what was equal to what at any given time. My problem was to be able to select individual debug pages seperately from the others. However...

The whole problem was solved when I included a type field called "id".


b32(Posted 2006) [#13]
Maybe it is an idea to use one type for two lists, and define the lists by storing their first/last item?



BlackJumper(Posted 2006) [#14]
apparently Object() and Handle() are now 'official' and documented in the new manual


Buggy(Posted 2006) [#15]
I just have an ID field for the player and one for the bullet, and use:
For player.player = each player

     For bullet.bullet = each bullet

          If bullet\ID = player\ID

               ...

          Endif

     Next

Next


Call it ugly, but to me ugly is over-coding something that can be solved simply and quickly.


Curtastic(Posted 2006) [#16]
That's not ugly its just slow code. And I do code it that way when the speed doesn't matter.


(tu) sinu(Posted 2006) [#17]
if you use my TList as an example you could have a bullets list in the type player and just add any bullets created for the player to that list and then just loop through that list, knowing already that the bullet belongs to that player.


octothorpe(Posted 2006) [#18]
Have you looked at my code yet, (tu) sinu?


(tu) sinu(Posted 2006) [#19]
did scan over it once but as i didn't need it i didn't look at it in much depth. Im gonna do it right now though.


Adam Novagen(Posted 2006) [#20]
That's not ugly its just slow code.

SLOW?!?! How can a single extra type field be SLOW, as compared to well over two hundred lines of code, ignoring blank lines & comments? I mean, come on. Am I missing something about types here?


octothorpe(Posted 2006) [#21]
How can a single extra type field be SLOW, as compared to well over two hundred lines of code...


Simple isn't always fast. Compare a quicksort to a bubblesort. Also compare a query on a database table with a relevant index to one without.

But typically, things like this aren't "slow" or "fast" - they perform differently for different operations.

For example, if you use this "single extra type field," the only way to loop over all the objects belonging to a single list is to loop over all the objects in every list. If you had a thousand lists of a thousand elements each, you'd have to loop over a million elements just to process one list! Conversely, if you keep separate linked-lists, you can loop through each list without having to deal with the others.

And if you look at it the other way, an "id field" saves you time when creating and destroying objects, since you don't need to go and update any linked lists that own them... albeit not much.

P.S. Spelt is "an ancient wheat ... with spikelets containing two light red grains"