Variable Variables?

Post your scripting questions / solutions here

Moderator: Moderators

AfterDeath
Corporal
Posts: 26
Joined: Fri Apr 09, 2004 4:34 am
Contact:

Variable Variables?

Post by AfterDeath »

My question is concerned with the MoHAA scripting language itself rather than something that I am trying to do specifically. Hopefully I will be able to explain myself clearly.

Let say I want to do the following ("hyphotetical" coding):

Code: Select all

spawn trigger_use "targetname" "bla1"
$bla1.origin = ( 11 11 11 )
$bla1.angles = ( 11 11 11 )
// more properties
But I want that code to be repeated several times, for "bla2" etc. Also, instead of having to write all that, I would just do something like this:

Code: Select all

new_bla local.origin local.angles:
    local.ent = spawn trigger_use
    local.ent.origin = local.origin
    local.ent.angles = local.angles
    // more properties
end local.ent

level.blas[0] = waitthread new_bla ( 11 11 11 ) ( 11 11 11 )
level.blas[1] = waitthread new_bla ( 22 22 22 ) ( 22 22 22 )
level.blas[2] = waitthread new_bla ( 33 33 33 ) ( 33 33 33 )
// etc.
So far so good, except I need each "bla" created in "new_bla" to have a different name (bla1, bla2, etc.). Actually, not necessarily a different name, but a different object, so it could be bla[0], bla[1]. It will also have to be accessible outside the thread.

What I want to know is if there's any way I can dynamically "targetname" the trigger_use I spawned, so that I can refer to it using the "$" symbol using the concept of variable variables (such as in PHP, with $$x returning $a if $x=='a').

This would be something like this (note I added local.id to this... I don't know if it would be necessary):

Code: Select all

new_bla local.id local.origin local.angles:
    spawn trigger_use "targetname" "bla"+local.id
    // Would this create a variable that I can refer to using $bla0 ?
    // If so, would I be able to do something like the following? How would it actually spell out? -->
    $bla+local.id = local.origin // eg. $bla1 = local.origin
    $bla+local.id = local.angles
    // more properties
end local.ent

level.blas[0] = waitthread new_bla 0 ( 11 11 11 ) ( 11 11 11 )
level.blas[1] = waitthread new_bla 1 ( 22 22 22 ) ( 22 22 22 )
level.blas[2] = waitthread new_bla 2 ( 33 33 33 ) ( 33 33 33 )
// etc.
I realize that the above explaination might suck, but I am strongly hoping someone will understand what I'm trying to convey, eheh. Thanks!

Read next post... :D
Last edited by AfterDeath on Fri Aug 27, 2004 11:24 am, edited 1 time in total.
AfterDeath
Corporal
Posts: 26
Joined: Fri Apr 09, 2004 4:34 am
Contact:

Post by AfterDeath »

If you don't understand what I meant to ask in the post above, maybe you can help me otherwise... here are some more direct questions.

- Are there variable variables in the MoHAA scripting language (as in PHP)?

In PHP, it works like this:

$a = 5;
$varname = 'a';
echo $$varname;

If MoHAA scripting provides for this, can you give me an example of how it is used?
jv_map
Site Admin
Posts: 6521
Joined: Tue Sep 03, 2002 2:53 pm
Location: The Netherlands
Contact:

Post by jv_map »

It took me a while but I finally think I understand what you are going for... basically a variable targetname is all you want :)

Example:

Code: Select all

local.id = 1
waitthread makebla local.id local.origin local.angles

// ...

$("bla" + local.id) dosomething
end

makebla local.id local.origin local.angles:
  local.bla = spawn bla origin local.origin angles local.angles
  local.bla.targetname = "bla" + local.id // string concatenation
end
Image
AfterDeath
Corporal
Posts: 26
Joined: Fri Apr 09, 2004 4:34 am
Contact:

Post by AfterDeath »

Aha! Thanks a lot man. All I had to do was look at...

Code: Select all

$("bla" + local.id)
And all of the sudden I had this HUGE smile in my face :D

I love how it works... thanks a lot man! :wink:
jv_map
Site Admin
Posts: 6521
Joined: Tue Sep 03, 2002 2:53 pm
Location: The Netherlands
Contact:

Post by jv_map »

Cool :) g/l :wink:
Image
AfterDeath
Corporal
Posts: 26
Joined: Fri Apr 09, 2004 4:34 am
Contact:

Post by AfterDeath »

While we're at it, is there a difference between ending a thread/method with "end local.whatever" or simply "end" ?

I've seen places where they end it with the variable being used... is this necessary? If so, what if you have more than one variable being manipulated? Thanks...

Added: I just came across another question while trying to implement your solution (above). It explains how to make "bla0" "bla1" etc, but what if I want them to be arrays -- bla[0], bla[1], etc.?

I understand I can access them by simply doing $bla[local.id], but how would I make...

Code: Select all

local.bla.targetname = "bla" + local.id
... work for an array? Would this work? (probably not, lol)

Code: Select all

local.bla.targetname = "bla[" + local.id + "]"
Thanks!!
jv_map
Site Admin
Posts: 6521
Joined: Tue Sep 03, 2002 2:53 pm
Location: The Netherlands
Contact:

Post by jv_map »

Targetnames can only be strings, not arrays (but if you have multiple entities with the same targetname they'll form a targetname array, and you can use $bla[1], $bla[2] etc all having targetname 'bla'). A nicer way however is to just use level.bla[1] etc, like you have in your first post.

end local.bla is to return a value, like

local.somevalue = waitthread somemethod local.somearg1 local.somearg2 //...

If you do not want your method to return a value, simply use 'end'.

If you want your method to return more than one value, there are a number of solutions available:

1) use an array:

Code: Select all

local.somearray = waitthread somemethod local.somearg
local.someresult1 = local.somearray[1]
local.someresult2 = local.somearray[2]
//..
end

somemethod local.somearg:

local.result1 = dosomething
local.result2 = dosomething

end (local.result1::local.result2) // const array
2) use a dummy listener object

Code: Select all

local.listener = spawn listener // or 'local createlistener'
waitthread somemethod local.listener
local.result1 = local.listener.result1
local.result2 = local.listener.result2

// remove the listener to free up mem
local.listener remove

//..
end

somemethod local.listener:
  local.listener.result1 = dosomething
  local.listener.result2 = dosomething
end
There may be even more ways but this should get you started :)
Image
AfterDeath
Corporal
Posts: 26
Joined: Fri Apr 09, 2004 4:34 am
Contact:

Post by AfterDeath »

Sweet. Thanks for all the info.

The idea behind having it as an array instead, is so that I don't have to pass the local.id as an argument, as in:

Code: Select all

waitthread makebla 0 ( a b c ) ( x y z )
waitthread makebla 1 ( a b c ) ( x y z )
// ...
waitthread makebla N ( a b c ) ( x y z )
Also, because this is actually for a script that will spawn multiple bombs in a map (and is intented to work for any map... code reuse), I would like to be able to do the following...

Code: Select all

thread win_bomb $bombs
(And have that thread loop through the $bombs array to check if all (or any) bombs have been blown up)... instead of something like:

Code: Select all

thread win_bomb $bomb0 $bomb1 $bomb2 // for 3 bombs
And though it seems too small of a detail for me to be worried about, I am really looking forward to learning some specifics about the language.

Now you did say that "if you have multiple entities with the same targetname they'll form a targetname array", however I am left wondering how that works. I am thinking that the array is generated as soon as I run...

Code: Select all

local.bomb = spawn script_model
local.bomb.targetname = "bomb"
... for the second time. Am I correct? Is there a size-1 array already there even after the very first run? (bomb[0]) Also, if multiple entities with the same targetname form a target name array, how do I refer to the "current" (the most recently created) one? Would it be something like the following...?
$bombs[bombs.size-1].property
Hopefully you will be able to understand what I am trying to accomplish. :D In any case, thank you VERY much for trying to understand my poorly explained scenarios. :wink:
jv_map
Site Admin
Posts: 6521
Joined: Tue Sep 03, 2002 2:53 pm
Location: The Netherlands
Contact:

Post by jv_map »

Not sure where the problem lies but maybe this helps :)
docs/Script Files.txt wrote:Targetname array
Created by the $ targetname operator if more than one entity exists for that targetname.
For example, $player is an array if more than one player is in the game.
Targetname arrays start their indexing at 1.
Image
AfterDeath
Corporal
Posts: 26
Joined: Fri Apr 09, 2004 4:34 am
Contact:

Post by AfterDeath »

Clears it up a bit, but I'm still not sure how I'd use it. But because I don't need it at any cost at this point, I'll pass. Maybe latter on I'll run some tests to answer my own questions.

Though not exactly how I wanted it first, I got my thingie working in a very nice way which suffices. Thought I would share with you:

Code: Select all

place_bombs:
    waitthread place_bomb 1  ( -2884 2752 92 )  ( 0 0 0 )
    waitthread place_bomb 2  ( -3267 1190 335 ) ( 0 0 0 )
    thread win_bomb ($bomb1::$bomb2)
    thread win_time
end

win_bomb local.bombs:
    local.whileloop = 1
    while (local.whileloop) {
        local.whileloop = 0
        for (local.i = 1; local.i < (local.bombs.size + 1); local.i++)
            if (!local.bombs[local.i].exploded)
                local.whileloop = 1
        wait .1
    }
    teamwin allies
end
With one line of coding ("waitthread place_bomb X (abc) (ijk)") a new bomb is placed, everything is done at the place_bomb thread. Then you update the "thread win_bomb ($bomb1::$bomb2)" line to reflect the number of bombs you have.

Round is over (with allies winning) once ALL bombs are blown up, no matter how many you have.

Not bad for a guy with some C++ experience who had a hard time with copying/pasting parts of scripts 8 hours ago. :D (maybe I should be in the bragging section now :wink:)

Thanks JV :D
jv_map
Site Admin
Posts: 6521
Joined: Tue Sep 03, 2002 2:53 pm
Location: The Netherlands
Contact:

Post by jv_map »

Not bad I agree :), but I do think your code is a bit expedient... I was actually gonna type another sentence here to reassure you your script is good, but realised I suck at sentence making so I'll just skip to some scripting right away ;)

Code: Select all

win_bomb local.bombs:
  for(local.i = 1; local.i <= local.bombs.size; local.i++)
  {
    local.bomb = local.bombs[local.i]
    while(local.bomb.exploded != 1)
      waitframe
  }
  teamwin allies
end
I'm not claiming that this is better than what you made, but it seems shorter and easier when you look at it (I could be biased, though ;)).

On a sidenote, notice that the exact same code (both yours and mine) would work perfectly with a targetname array, i.e. you'd make a number of bombs with targetname 'bomb' and then do:

thread win_bomb $bomb

Anyway yes you can go to the bragging section now :)
Image
AfterDeath
Corporal
Posts: 26
Joined: Fri Apr 09, 2004 4:34 am
Contact:

Post by AfterDeath »

Oh thanks. I will make that change into my coding. Thanks for putting the time and effort into it.

I actually never liked my way of doing it, I just couldn't think of any other way. Probably due to my lack of knowledge... I didn't know what waitframe did or how to use it... but now I see it.

I will try playing with the spawn targetname array later on. I think I see it now too. Thanks! :D
User avatar
tltrude
Chuck Norris
Posts: 4774
Joined: Sun Jul 07, 2002 4:03 am
Location: Oklahoma, USA
Contact:

waitframe

Post by tltrude »

In this case the "waitframe" line makes the thread hold for one frame per loop--until the while loop becomes false. He could of also used a fast wait time like "wait .001". All loops need some kind of wait, or they go too fast and crash the game.

Here is how the obj_team2 script does the wait lines.

Code: Select all

allies_win_bomb local.bomb1 local.bomb2:

	while (local.bomb1.exploded != 1)
		wait .1
	while (local.bomb2.exploded != 1)
		wait .1

	teamwin allies
end
And here is how obj_team3 does it.

Code: Select all

allies_win_bomb local.bomb1 local.bomb2:

	while (local.bomb1.exploded != 1)
		waitframe

	while (local.bomb2.exploded != 1)
		waitframe
	
	teamwin allies
end
You can also combine the while loops like this example form obj_team4.

//-----------------------------------------------------------------
axis_win_bomb local.bomb1 local.bomb2:

while ((local.bomb1.exploded != 1) && (local.bomb2.exploded != 1))
wait .1

teamwin axis

end
//-----------------------------------------------------------------


I hope that helps!
Tom Trude,

Image
AfterDeath
Corporal
Posts: 26
Joined: Fri Apr 09, 2004 4:34 am
Contact:

Post by AfterDeath »

Thanks for the suggestions. However these work for a fixed number of bombs, and I am looking at something that works for a variable number of bombs.

One question though... why does V2 and Omaha do it differently, if in the end it all works the same? Why "waitframe" instead of "wait .1" ? Any specific reason? :?

And yet another question. I don't fully understand how the following piece of coding works:

Code: Select all

win_time:
	level waittill axiswin
end
Again ignorance. How does waittill work? What if I want the allies to win instead? allieswin? I like the teamwin allies much better. Any thoughts?
jv_map
Site Admin
Posts: 6521
Joined: Tue Sep 03, 2002 2:53 pm
Location: The Netherlands
Contact:

Post by jv_map »

You can omit the win_time thread... it doesn't do anything at all (i.e. it waits till the axis have won and then just exits :?)

waitframe is equal to 'wait 0.05'... in this case it doesn't really matter what delay you use, anything will work (though, if you use a very large wait the timelimit might run out before your script detects victory).
Image
Post Reply