Question About Lists

BlitzMax Forums/BlitzMax Programming/Question About Lists

Takis76(Posted 2015) [#1]
I have one strange issue with list and I would like to see if I am doing something wrong here?

I have one list with name Fired_Spell_Explosion_List.

Global Fired_Spell_Explosion_List:TList = CreateList()


I add some a type object to the list.

The type object:
Type My_Fired_Spell_Explosion

	Field Level:Byte
	Field X:Byte
	Field Y:Byte

	Field Explosion_Frame:Byte 'Animation of the spell explosion
EndType
Global Fired_Spell_Explosion:My_Fired_Spell_Explosion = New My_Fired_Spell_Explosion


Add this 2 objects in Fired_Spell_Explosion_List

Fired_Spell_Explosion.X = 10
Fired_Spell_Explosion.Y = 5
Fired_Spell_Explosion.Level = 1
Fired_Spell_Explosion.Explosion_Frame = 1
ListAddLast(Fired_Spell_Explosion_List, Fired_Spell_Explosion)

Fired_Spell_Explosion.X = 2
Fired_Spell_Explosion.Y = 2
Fired_Spell_Explosion.Level = 1
Fired_Spell_Explosion.Explosion_Frame = 5
ListAddLast(Fired_Spell_Explosion_List, Fired_Spell_Explosion)


Then I have one timer and after 1 second I add one Explosion_Frame+1 to
Fired_Spell_Explosion

Fired_Spell_Explosion.Explosion_Frame:+1



When the variable Fired_Spell_Explosion.Explosion_Frame have the value of 25
I remove 1 object from the Fired_Spell_Explosion_List.

     For Fired_Spell_Explosion = EachIn Fired_Spell_Explosion_List

          If Fired_Spell_Explosion.Explosion_Frame >= 25 Then
          Fired_Spell_Explosion.Explosion_Frame=1
          ListRemove(Fired_Spell_Explosion_List, Fired_Spell_Explosion)
          
          End If

     Next


There are 2 problems.
The objects I added to the list above take the last object values to both objects. (In fact I can't see those values in the list) There is no way to control or select the index of the list.

Fired_Spell_Explosion.X = 2
Fired_Spell_Explosion.Y = 2
Fired_Spell_Explosion.Level = 1
Fired_Spell_Explosion.Explosion_Frame = 5

And when the variable Fired_Spell_Explosion.Explosion_Frame >= 25 all objects clear.
The timer runs continue and add Fired_Spell_Explosion.Explosion_Frame:+1
If the Explosion_Frame become 25 then I change the value to 1 and
remove 1 object.

Why in both of the objects in the list have the last values when I add another one abject?
And why I don't see both of the objects at once , but then one finished , I see the next.

Suppose the for each scans all objects one by one, and all values should be different for each object.

When I will try to change the Fired_Spell_Explosion.X and Fired_Spell_Explosion.Y values for each object change the last only.
Where is my wrong?

This is my example code:
We assume all variables was declared.

The type and the list are above in this post.

The code which adds 2 objects in the list
Fired_Spell_Explosion.X = 2
Fired_Spell_Explosion.Y = 14
Fired_Spell_Explosion.Level = 1
Fired_Spell_Explosion.Spell_number = 1
Fired_Spell_Explosion.Explosion_Frame = 5
ListAddLast(Fired_Spell_Explosion_List, Fired_Spell_Explosion)
Fired_Spell_Explosion.X = 2
Fired_Spell_Explosion.Y = 16
Fired_Spell_Explosion.Level = 1
Fired_Spell_Explosion.Spell_number = 1
Fired_Spell_Explosion.Explosion_Frame = 12
ListAddLast(Fired_Spell_Explosion_List, Fired_Spell_Explosion)


And this code is the timer which controls the frames of the explosion
		For Fired_Spell_Explosion = EachIn Fired_Spell_Explosion_List

						If Fired_Spell_Explosion.x = 0 And Fired_Spell_Explosion.y = 0 Then Continue
						
						'If some explosion exists
						If Fired_Spell_Explosion_List.Count() > 0 Then
							
							'Fast Routine
							'For fireball explosion animation frame
							If Fired_Spell_Explosion.Explosion_Frame >= 25 Then
							Fired_Spell_Explosion.Explosion_Frame = 1

								'Destroys the explosion and stops the sound
								GetPooledChannel("fireball_explosion[" + Fired_Spell_Explosion.Level + "," + Fired_Spell_Explosion.Explosion_Serial_Number + "]").Stop()
								'Fired_Spell_Explosion.X = 0
								'Fired_Spell_Explosion.Y = 0
								ListRemove(Fired_Spell_Explosion_List, Fired_Spell_Explosion)
								Return

							EndIf

							'Slow routine
							If Current_time >= Fired_Spell_Explosion.Explosion_Frame_Deadline_time Then
							Fired_Spell_Explosion.Explosion_Frame_Time_Delay = 1000 '1 second
							Fired_Spell_Explosion.Explosion_Frame_Deadline_time = Current_time + Fired_Spell_Explosion.Explosion_Frame_Time_Delay
		
							'Increase the explosion animation frame
							Fired_Spell_Explosion.Explosion_Frame:+1
							EndIf

						'Continue
						End If

		Next



Thank you :)


Henri(Posted 2015) [#2]
Hi,

Are you using 'New' operator each time to create a new object instance where to assign values to ?

-Henri


therevills(Posted 2015) [#3]
I cant spot the issue directly but a few things are a bit strange in your code:

Why are you using bytes? Just use an int.
Field Level:Byte


No need to do this in your loop:
If Fired_Spell_Explosion_List.Count() > 0 Then


Why reset the frame if the explosion is going to be removed?
Fired_Spell_Explosion.Explosion_Frame = 1


Why return in the middle of the loop?


Brucey(Posted 2015) [#4]
Fired_Spell_Explosion is a single object. This object represents a specific block of memory.
Your following comment is not true :
The code which adds 2 objects in the list

Because you are just reassigning the fields in your single object instance.

As Henri notes, you really want to create a New object for each "thing" in your list.
In your example, your list is populated with the same, single object, multiple times.
So, for example, the correct way here would be :
Fired_Spell_Explosion = New My_Fired_Spell_Explosion
Fired_Spell_Explosion.X = 2
Fired_Spell_Explosion.Y = 14
Fired_Spell_Explosion.Level = 1
Fired_Spell_Explosion.Spell_number = 1
Fired_Spell_Explosion.Explosion_Frame = 5
ListAddLast(Fired_Spell_Explosion_List, Fired_Spell_Explosion)
Fired_Spell_Explosion = New My_Fired_Spell_Explosion
Fired_Spell_Explosion.X = 2
Fired_Spell_Explosion.Y = 16
Fired_Spell_Explosion.Level = 1
Fired_Spell_Explosion.Spell_number = 1
Fired_Spell_Explosion.Explosion_Frame = 12
ListAddLast(Fired_Spell_Explosion_List, Fired_Spell_Explosion)

The Fired_Spell_Explosion variable is assigned a new object (new block of memory), populated, and added to your list.


In other notes, your test here :
If Fired_Spell_Explosion_List.Count() > 0 Then

Is invalid because to get to that line you've already proved there is something in your list (via For/Eachin).


therevills(Posted 2015) [#5]
Fired_Spell_Explosion is a single object. This object represents a specific block of memory.

Good spotting!


Takis76(Posted 2015) [#6]
I use If Fired_Spell_Explosion_List.Count() > 0 Then
to be sure if my list have objects. But it seems is not necessary.
About adding a NEW operator this is the one I always forget. I am so stupid. :P

I added Fired_Spell_Explosion.Explosion_Frame = 1 might is not necessary too , because is going to be removed. This must be set when the object is generated.

About Field Level:Byte I use to use bytes because most of my variables take values less than 255 and I consume only 1 byte memory. In cases I need larger variables I use int.
The field level in my game will take up to 51 in the game so byte is up to 255. 51 is less 255. :)

I believe I consume less memory , or I am wrong?

My issue was fixed. The error was the new operator was missing :)
I removed the rest unnecessary code. Thank you very very much.


coffeedotbean(Posted 2015) [#7]
Hi Takis76

I would not worry about bytes and ints in 2015.

I am not 100% what your code is designed to do but it all looked overly complicated to me so I drew up the below, *not tested* but I hope easy to read and understand. The code will create a fireball at the mouse location and update its animation and kill it once the animation is finished.

FYI I use SuperStrict which is why things might look a little odd.
SuperStrict
‘  // List to store Fireball Explosion objects
Global LFileBalls:TList = New TList

‘ // Fireball explosion object
Type TFireBallExplosion
	Field x:Int
	Field y:Int
	Field frame:Int
        Field maxFame:int=20
	Field animTimer:Int
	Field frameTime:Int = 100 ‘ // 100ms per frame
	Field killMe:Int

	Method Update:Int()
		‘ // Update animation
		If animTimer < Millisecs()
			animTimer = Millisecs()+frameTime
			frame = frame +1
			if frame > maxFrame then killMe = True ‘ //Max frame reached so kill this object at the next opportunity
		End If
		
		‘// Can do other stuff below too such as moving x,y positions etc.

        End Method

	Method Render:Int()
		If killMe = True Then Return 0 ‘ // don’t want to render if it needs to be removed
		Drawimage(blah,x,y,frame) ' // blah = your image handle
	End Method	 

End Type

' // Main program loop starts here ----------------------------------------------
Repeat

‘ // Add a fireball at mouse location when left mouse button is pressed
If MouseHit(MOUSE_LEFT)
	Local a:TFireBallExplosion = New TFireBallExplosion ' // Reference our new fireball as 'a' so we dont need to type as much
	a.x = MouseX()
	a.y = MouseY()
	ListAddLast(LFireBalls,a) ' // add 'a' (fireball object) to our list
End If	

‘ // Update and Render Fireballs, if there are no fireballs on list this will not throw an error
' // Again reference our fireball as 'a' so we dont need to type as much
For Local a: TFireBallExplosion = EachIn LFileBalls
	a.Update
	a.Render
Next

‘ // Remove Fireballs if flagged,  if there are no fireballs on list this will not throw an error
' // Again reference our fireball as 'a' so we dont need to type as much
For Local a: TFireBallExplosion = EachIn LFileBalls
	If a.killMe=True then ListRemove(LFireBalls,a)
Next

Forever



therevills(Posted 2015) [#8]
Here's my take on it:

SuperStrict

Initialise()
Main()

Function Initialise()
	Graphics (800, 600)
	SeedRnd (MilliSecs())
	SetBlend(ALPHABLEND)
EndFunction

Function Main()
	While Not AppTerminate() And Not KeyHit(KEY_ESCAPE)
		If MouseDown(MOUSE_LEFT)
			TFireBallExplosion.Create(MouseX(), MouseY())
		EndIf
		TFireBallExplosion.UpdateAll()
		Cls
			TFireBallExplosion.RenderAll()
		Flip
	Wend
EndFunction

Type TGameObject Abstract
	Field x:Int
	Field y:Int
	Field frame:Int
	Field maxFame:Int=20
	Field animTimer:Int
	Field frameTime:Int = 100
	Field alpha:Float = 0
	
	Method Update:Int() Abstract
	Method Render:Int() Abstract
	Method Kill() Abstract
EndType

Type TFireBallExplosion Extends TGameObject
	Global List:TList

	Function Create:TFireBallExplosion (x:Int, y:Int)
		If TFireBallExplosion.list = Null
			TFireBallExplosion.list = New TList
		EndIf
		Local fb:TFireBallExplosion = New TFireBallExplosion
		fb.x = x
		fb.y = y
		list.AddLast(fb)
		Return fb
	EndFunction 
	
	Method Render:Int()
		SetAlpha(alpha)
		DrawOval(x, y, 10, 10)
		SetAlpha(1)
	EndMethod
	
	Method Update:Int()
		If alpha < 1
			alpha :+ 0.01
		Else
			alpha = 1
		EndIf
		
		animTimer :+ 1
		If animTimer > frameTime
			Kill()
		EndIf
	EndMethod
	
	Method Kill()
		TFireBallExplosion.List.Remove(Self)
	EndMethod
	
	Function UpdateAll:Int()
		If TFireBallExplosion.List = Null Then Return 0
		For Local i:TFireBallExplosion = EachIn TFireBallExplosion.List
			i.Update()
		Next
	EndFunction

	Function RenderAll:Int()
		If TFireBallExplosion.List = Null Then Return 0
		For Local i:TFireBallExplosion = EachIn TFireBallExplosion.List
			i.Render()
		Next
	EndFunction
EndType


Of course with coding there are a million different ways to do it :)


Takis76(Posted 2015) [#9]
@coffeedotbean
The code I was posted wasn't whole , only the part I had the problem with missing New operator.

@therevills
I am not good in inheritance yet , abstract, extends and methods are unknown things and I am not understand them yet.

I will study both of codes and I will save them.
The code I use for now works.

There are no examples, tutorials or books which explain inheritance and methods with blitzmax.

But thank you very very much you help me.


Derron(Posted 2015) [#10]
Instead of "ListRemove(list, variable)" you might use one less wrapper by doing

list.Remove(variable)
list.AddLast(variable)
list.AddFirst(variable)


@coffeedotbean

		‘ // Update animation
		If animTimer < Millisecs()
			animTimer = Millisecs()+frameTime

Your code will run into trouble on computers having an uptime of >27 days (or whatever it was until "Millisecs()" returns a negative value)).
-> for this problem the interested coder might have a look at some threads in that board.


bye
Ron


Takis76(Posted 2015) [#11]
What is the difference from ListRemove and list.Remove?

What problem do I will have about up time of 27? I didn't understand.
You mean when my game remain run more than 27 days?

If my game remain open for 27 day or 648 Hours , a special message will be dropped.

"Hey dude , my game was perfect and you played for 648 hours in the row , but you need to get a little life too. You need to go out for clubbing or make love to your girl. The game will close now. If you like to run the game run it from program files. We apologize for wasting your time , forcing you to restart the game. "

:P


Brucey(Posted 2015) [#12]
Millisecs() returns a signed integer representing the length of time, in milliseconds, since the computer was started. Once the number of milliseconds is greater than 2 billion or so (after so many days), the number will become negative (due to the way signed integers work).

There are workarounds. The neatest is probably via bit masking :

number & $7FFFFFFF


You may say to yourself, "who leaves their PC on for so long?".
An uptime on my PC here says 35 days, 23:29. So, almost 36 days since its last reboot.


@list remove

ListRemove() just calls list.Remove(), so you can save yourself a function call by using list.Remove() directly.


Takis76(Posted 2015) [#13]
An example of this?
Current_Time = Current_Time & $7FFFFFFF

Of a code like this?

'Slow routine
If Current_time & $7FFFFFFF >= Fired_Spell_Explosion.Explosion_Frame_Deadline_time & $7FFFFFFF Then
	Fired_Spell_Explosion.Explosion_Frame_Time_Delay & $7FFFFFFF = 1000 '1 second
	Fired_Spell_Explosion.Explosion_Frame_Deadline_time & $7FFFFFFF = Current_time & $7FFFFFFF + Fired_Spell_Explosion.Explosion_Frame_Time_Delay & $7FFFFFFF



Brucey(Posted 2015) [#14]
Well, no. You wouldn't apply it *everywhere*, as you are needlessly doing extra maths all over the place.

time = MilliSecs() & 7FFFFFFF


Takis76(Posted 2015) [#15]
It says identifier FFFFFFF Not found :)
But like this compiled:

Current_time = MilliSecs() & $7FFFFFFF


coffeedotbean(Posted 2015) [#16]
@Brucey - thanks for that, I was under the impression Millisecs() returned the up time of the program not the system, I think that was a throw back to using Monkey so much. The bitshift is a neat solution.