And.... lists again.

BlitzMax Forums/BlitzMax Beginners Area/And.... lists again.

Bee(Posted 2012) [#1]
I've beat my head on the desk for a few hours and I can't find any reasonable way to manipulate just a single variable inside a single Type. I can't really find any documentation either.

Assuming I'm even supposed to use a List for this in the first place.

I've created a Function that will create up to 16 enemies, but each enemy can/will have different stats. So each Enemy is assigned as so:


Type TEnemy Extends TUnit 'Enemy Unit Structure

Field RaceType$
'Field Faction$ - Later Use
'Field UnitType - Later Use

Function Create:TEnemy() 'Creates Enemy
Local Enemy:TEnemy = New TEnemy
Return Enemy
End Function

Method DecideEnemy() 'Decides what stats enemy should have
If Biome = 1
Health = 100
RaceType = "Xiir"
Else If Biome = 2
Health = 200
RaceType = "Unixx"
EndIf
EndMethod

Method TakeDamage()
Health = Health - 50
EndMethod

Method Update()
End Method

Method DrawSelf()
End Method

EndType



Now lets say my random enemy number rolls a 2 when it loads a map. It's going to run this:


Function EnemySpawn2() ' Spawns 2 Generic Enemy

Enemy1:TEnemy = New TEnemy
Enemy1.DecideEnemy() 'This sets Enemy variables based on other conditions
DrawText Enemy1.Health,0,20
EnemyList.AddLast Enemy1




Enemy2:TEnemy = New TEnemy
Enemy2.DecideEnemy() 'This sets Enemy variables based on other conditions
DrawText Enemy1.Health,0,30
EnemyList.AddLast Enemy2

EndFunction


So Now in the list I have Enemy1 and Enemy2


But when I do something like:

Function DamageTarget()
For Local x:TEnemy = EachIn EnemyList
x.TakeDamage()
Next
End Function

It's going to damage every enemy on the list.


Function DamageTarget()
For Local x:Enemy1 = EachIn EnemyList
x.TakeDamage()
Next
End Function

Would just say "Enemy1 not found"


How do I control the single elements within the list?

I cannot create a function like:

Function Attack()
Enemy1.TakeDamage()
End Function

Because it won't find Enemy1 either.


What is the point of the list in the first place if I can not manipulate data within it? Every game example I see in the tutorials is adding things to the list for ease of Update() and other features to run globally, which I get , but it doesn't seem to work like I think it would.

Last edited 2012


Yasha(Posted 2012) [#2]
OK from the above examples it seems there are a few important things you have missed while learning about scope and types and so on.

1) This:

Local x:Enemy1


...is completely meaningless. Enemy1 is not a type, it's an instance. I assume what you're trying to do here is get the same instance created earlier and assigned to the variable Enemy1 that was local to the function EnemySpawn2. Let's just say you don't do it like that - after the colon can only ever be the name of the type of object. It cannot be a value. Leading into...

2) The variable Enemy1 is Local to the EnemySpawn2 function. It is a slot created on the stack when the function is invoked, and eliminated when the function returns. If the function were to call itself, the second invocation of the function would have a different slot that it referred to by that name; and using that name outside the function either refers to a completely unrelated variable that just happens to have the same name, or is an error (depending on whether you declared it in the outer scope). Scope is the most important thing to learn in procedural and OO programming, so... you might need to scale back to a smaller example if you haven't got a handle on it yet.

3) The whole point of functions is that they operate on data, taking parameter values and doing something to them before returning a result value. Your DamageTarget function can't work, because... it has no target! The list is completely irrelevant to this: what you want it to select the target by some means unconnected to the damage procedure, and pass in the target as a parameter:

Function DamageTarget(tgt:TEnemy)
    tgt.TakeDamage()
End Function


(Let's ignore for the moment that if you can get tgt, you can just call the method on tgt: that's a detail of what DamageTarget happens to do right now.)

A function without parameters is crippled and essentially can't do its job, because there is no way to pass data in! (A method is just a function with special syntax for passing the "self" parameter in automatically.) If you didn't know about declaring parameters... that revelation may have made a few things a bit easier. A useful function should never have to guess the thing it's supposed to operate on without context, which is why this question is so hard to answer: to be brutally honest, the above approach is not even close to the right way to do this.


You've identified the problem through experiment, i.e. you can't easily pass data between scopes. I would very much recommend you take a big step back and try to get comfortable with functions, parameter passing, returned values, and local scopes, before you try to make something involving a large and complicated type hierarchy (since types are a theoretical extension of function scopes, they'll never completely make sense unless you understand functions first).

(Sorry if I come across as brusque. It's just that there's quite a lot to learn here: you'll want to break up the relevant topics into two or three "things" to cover separately.)


TAS(Posted 2012) [#3]
'Try a approach like this;

Global enemy_list:TList=New TList

Type Enemy
	Field xy
	Field ID
	Field Hitpoints
	
	Method New()
		ListAddLast(enemy_list,Self)
		Self.ID=CountList(enemy_list)
		Self.Hitpoints=100
	End Method
	
	Method Damage(n)
		Self.Hitpoints=Self.Hitpoints-n
		If Self.Hitpoints<1 Then ListRemove(enemy_list,Self)
	End Method
End Type

'main .........

For en:enemy EachIn enemy_list
	If en.xy=xy Then en.Damage(10)
Next


Last edited 2012


Bee(Posted 2012) [#4]
@Yasha

You are correct and not brusque at all. Constructive critique hardly offends, and when asking for help sometimes tough-to-hear advice is best. I was trying to consolidate a lot of spaghetti code I made into something that was less 18 pages and Global x=value for 50 lines.

The thing is I actually understand how to manipulate most the data I want to, but it seemed Tlists were an easier way to do things like update() and manage certain params in the gamestate. TBH the spaghetti code was actually better, while less efficient, it did what I wanted it to do.

I have a SelectEnemy type Function setup that will put the next enemy in focus. It will just look through the enemy's stats based on the number of enemies. Then I can run Damage() that will subtract health based on the enemy selected or whatever function I want because it's all Global at that point. I was trying to put all of the enemies in a list, because... it seemed like the right thing to do but it hid all the data from everything.

I'm guessing from the responses, Lists was just making it overly complicated, but I started playing with this idea because almost every single example in the tutorial and built in source examples, uses it somehow, somewhere, which perhaps was a bit misleading. Consider I'm not passing any data where 'real-time' or FPS is concerned (turn based game) then I don't guess being uber-efficient with storage is going to make a huge impact on the game.... 16 enemies with health and a couple stats, really isn't a huge amount of variables but it sure did look like a lot on the screen.

Much like I've learned from music production, there's always a way to do things in an environment with guidelines representing core ideas ,but then there is a BETTER way to do it.... just trying to approach this on how to do things better than what I've done already.

Thank you though for the explanation on orientation a bit more. I had the right idea after all, but it felt dirty.

Last edited 2012

Last edited 2012


Midimaster(Posted 2012) [#5]
There is no way but Lists!

And it is not as complicated as it looks like in the first moment.

A list contains objects you added, even if the origin object's lifecycle ended. That's why you can add a local Enemy1:TEnemy inside a function. This Enemy will live also outside. His name "Enemy1" is not longer connected with him, but he lives.

That the reason, why you can do that:

For e = 1 to 5
     EnemySpawn(e)
Next

Function EnemySpawn(e:int)
	Local Enemy1:TEnemy = New TEnemy
	Enemy1.Health=123
        Enemy1.ID = e
	List.AddLast Enemy1
End Function


at the end you will have 5 enemys, all created under the name Enemy1, but all are independent. The ID is only for you, so you can identify each of them.

If you now want to substract something from fields of the enemys you can do it like that:

For Local locEnemy:TEnemy = EachIn List
	locEnemy.Health=locEnemy.Health-10
Next


or you can use Inside-Type-Function:
If KeyHit(Key_A)
 	TEnemy.ReduceThemAll()
Endif

Type TEnemy
 	Fields ID%, Health%

 	Function ReduceThemAll()
 	 	For Local locEnemy:TEnemy = EachIn List
        	 	locEnemy.Health=locEnemy.Health-10
 	 	Next
 	End Function
End Type


or, and that is the favorite solution, use methods:
Methods are "Function" that always maninpulate one member of TEnemy:

If KeyHit(Key_A)
 	TEnemy.ReduceThemAll()
Endif

Type TEnemy
 	Fields ID%, Health%

 	Function ReduceThemAll()
 	 	For Local locEnemy:TEnemy = EachIn List
        	 	locEnemy.ReduceMe()
 	 	Next
 	End Function

 	Method ReduceMe()
        	 Health=Health-10
  	End Function

End Type


As you see, there is no need to name the object inside a method. Only name the fields is enough. Very comfortable!


Corum(Posted 2012) [#6]
@Bee
Everyone here will confirm: Lists are not complicated at all, but are the way to keep you code clear, readable and to get rid of many global variables.

If you iterate each Enemy, you actually affect properties on each of them, with no criteria.
In the way you're doing, it's perfectly normal that each Enemy takes damage.
You should rethink your logic flow.

Take a bullet Object fired from player's ship, and check FOR collision between the bullet itself and EACH Enemy.
IF collision happens for the current Enemy in the FOR cycle, THEN let it take the proper damage.

As you can see, Lists are THE s perfect match for this kind of situation.
You could operate with arrays or maps, but they're no way simpler than Lists.


Bee(Posted 2012) [#7]
[deleted]

Last edited 2012


Bee(Posted 2012) [#8]
GUYS... guys... GUYSSSSSSS.

I figured it out :)

Thanks [quote]

TAS, you are legend and I am terrible.

This is exactly what I was doing wrong:

I was sitting here tripping on targeting through the normal conventions of something like a text-based game. So I struggled to find a conventional way of pointing at things. Position!! Even if it's hidden it works.

Corum,

Your perspective with the "Bullet" made me realize I could just create an object that would represent the point of conflict. Even though I'm not shooting bullets there would have to be a targeting mechanism.

Combine all the above forces together like Voltron slash Captain Planet and booooom.

My code is so much more realistic and manageable now. I can press forward and will probably spend the next 8 years wrapping my brain around AI.

Thank you all. May many kittens, cupcakes and whatever you fancy will find their way to you.

Last edited 2012


Zeke(Posted 2012) [#9]
Bee, please use {CODE}YOUR CODE{/CODE} AND {CODE} >' A LOT OF BMX CODE {/CODEBOX}--
but insteald {} udsr []

Last edited 2012


Zeke(Posted 2012) [#10]
Bee, please use {CODE}YOUR CODE{/CODE} AND {CODE} >' A LOT OF BMX CODE {/CODEBOX}--
but instead {} use []

Last edited 2012