Hi coders!

**Required Knowledge**This guide is intended for people who have covered the

beginner's guide, done a little basic map-making of their own to get used to things, and now are ready to move on and learn about the structural commands and techniques needed for more advanced scripting.

**Scope**This guide looks almost exclusively at

**function LevelLogic()** - it is assumed that a suitable

**LevelSetup()** already exists with appropriate asteroids added, etc. It will cover the syntax of various important structural commands, various commands from the maths library, and usage examples.

**Could be heavy going..!**Yea, I know you're eying the size of the scrollbar nervously. This IS a long document. However, I have split this guide into numbered chunks, each of which should take no longer than 20 minutes to read and thoroughly understand. Please, give section 1 a read through, and hopefully you will see that it's not all that difficult. :> I promise to regale you with my distinctive brand of humour along the way. Or something.

**Contents:****1. while GameRunning() do****2. for** loops**3.** Arrays**4. math.random****5. math.sin**, **math.cos****6.** Build your own functions!

**1. While GameRunning() do****i)**`function LevelLogic()`

while GameRunning() do

-- later on we'll put some commands here!

coroutine.yield()

end

end

The commands above create a loop that is run continuously, at a rate of 60 times per second, until the game ends.

This specific type of While loop is useful for all sorts of scripting, and I recommend using it as a base for any advanced scripting level. It's the only

**while** loop you will need in your code - in fact I'd suggest you should avoid using other While loops altogether.

Any code you place after this loop's

**end** will never run.

`function LevelLogic()`

while GameRunning() do

-- later on we'll put some commands here!

coroutine.yield()

end

-- CODE PLACED HERE WILL NEVER RUN

end

That is because the condition of the while loop is "while the game is running". The game is always running! :> The game is only considered to have ended when the player quits, or a

**Quit()** command is triggered, causing the player to return to the menu.

In the beginner's guide, we looked at how you can use the

**OnAsteroidTaken(id,owner)** function to trigger a Victory or Loss.

Now we'll look at another method of doing this using a

**while GameRunning() do** loop, which will work much better alongside our other scripts.

**ii)**Suppose we want the player to win if they've taken over the whole galaxy, and lose if they've lost all their asteroids.

First lets make a

**While GameRunning() do** loop and populate it with comments to remind ourselves what each bit of code needs to do, and where it needs to be inserted.

`function LevelLogic()`

while GameRunning() do

-- if player 1 has conquered whole galaxy then

-- Quit(true)

-- if player 1 has lost all asteroids then

-- Quit(false)

coroutine.yield()

end

end

**iii)**So how to tell if the player has conquered the whole galaxy?

One way would be to check if the number of asteroids the player owns is equal to the number of asteroids in the level.

If it is, that means the player must own every asteroid - fulfilling the victory condition.

` if GetEmpire(1):GetNumOwnedAsteroids() == 20 then`

Pause()

MessageBox("You have won")

WaitDialog()

Unpause()

Quit(true)

end

This would work fine if the number of asteroids in the level is indeed 20.

But what if you decide later on to go back and add more asteroids? If there are 25 asteroids in the level but the

**If** statement is only looking for 20, then the player will win when there are 5 asteroids still to conquer.

So although this would work, it's not ideal because it means every time we add or remove asteroids from the level we also have to change the number of asteroids the victory condition is checking for.

So what other method could we use that avoids this problem?

**iv)**Well, maybe we could check how many asteroids are owned by the other empires.

If this map only has empire 2 in it, then maybe we could check it like this:

` if GetEmpire(2):GetNumOwnedAsteroids() == 0 then`

Pause()

MessageBox("You have won")

WaitDialog()

Unpause()

Quit(true)

end

Then when Empire 2 has no asteroids left, player 1 would win. Doing it like this would mean subsequent changes to the number of asteroids in the level would not break the win condition. :>

But there's a potential problem with this too. Suppose there are 20 asteroids and the player conquers 5 of them, then goes straight on to conquer all 5 of Empire 2's asteroids. That leaves 10 asteroids uncolonised, ie they belong to the greys (empire 0).

The player would win at this point even though they've only conquered half the galaxy - because Empire 2 has 0 asteroids left.

This behaviour isn't bad, but the behaviour we were actually looking for is "if the player has conquered

the whole galaxy"...

**v)**If we really want the player to have to take over the whole galaxy to score a win, we would need to make a modification to our

**If** statement to include Empire 0:

` if GetEmpire(2):GetNumOwnedAsteroids() == 0 and GetEmpire(0):GetNumOwnedAsteroids() == 0 then`

Pause()

MessageBox("You have won")

WaitDialog()

Unpause()

Quit(true)

end

That's better. :> We're saying If Empire 2 doesn't have any asteroids left, and Empire 0 doesn't have any asteroids left, then the player wins.

Now this will mean the player

*must* conquer every asteroid in the level in order to win.

**vi)**Ok but what if you have Empire 3, too? What then? The statement in its current form doesn't check for Empire 3 at all, so if there is an Empire 3 in the level then the player might win even when Empire 3 still owns some asteroids!

We could make another modification to our

**If** statement to also take Empire 3 into account:

` if GetEmpire(2):GetNumOwnedAsteroids() == 0 and GetEmpire(3):GetNumOwnedAsteroids() == 0 and GetEmpire(0):GetNumOwnedAsteroids() == 0 then`

Pause()

MessageBox("You have won")

WaitDialog()

Unpause()

Quit(true)

end

So there's our victory condition.

**vii)**What about the loss condition?

Well, in our comments we said that the player should lose "if the player has lost all asteroids".

So we can represent that like this:

` if GetEmpire(1):GetNumOwnedAsteroids() == 0 then`

Pause()

MessageBox("You have lost")

WaitDialog()

Unpause()

Quit(false)

end

Looking at this code, it seems it should work no matter how many asteroids or empires we add or remove. :>

The loss condition is a lot simpler to make than the win condition..

**viii)**Lets return to the template that we wrote out at the start:

`function LevelLogic()`

while GameRunning() do

-- if player 1 has conquered whole galaxy then

-- Quit(true)

-- if player 2 has lost all asteroids then

-- Quit(false)

coroutine.yield()

end

end

**ix)**...and populate it with the Win Condition we made in

**step vi)**....

`function LevelLogic()`

while GameRunning() do

-- win condition

if GetEmpire(2):GetNumOwnedAsteroids() == 0 and GetEmpire(3):GetNumOwnedAsteroids() == 0 and GetEmpire(0):GetNumOwnedAsteroids() == 0 then

Pause()

MessageBox("You have won")

WaitDialog()

Unpause()

Quit(true)

end

-- if player 2 has lost all asteroids then

-- Quit(false)

coroutine.yield()

end

end

**x)**... and the Lose Condition from

**step vii)**. :>

`function LevelLogic()`

while GameRunning() do

-- win condition

if GetEmpire(2):GetNumOwnedAsteroids() == 0 and GetEmpire(3):GetNumOwnedAsteroids() == 0 and GetEmpire(0):GetNumOwnedAsteroids() == 0 then

Pause()

MessageBox("You have won")

WaitDialog()

Unpause()

Quit(true)

end

-- lose condition

if GetEmpire(1):GetNumOwnedAsteroids() == 0 then

Pause()

MessageBox("You have lost")

WaitDialog()

Unpause()

Quit(false)

end

coroutine.yield()

end

end

Now we've made a working Win/Lose condition using a

**while GameRunning() do** loop. :> This will set us up for scripting some advanced behaviours.

*This data tunnel is what advanced coding ***actually looks like**...**2. For loops****i)**The

**for** loop is an essential tool when it comes to coding.

A typical

**for** loop might look like this:

`for i = 0,9 do`

-- some code goes here

end

As you can see, it too needs an "end" to close it off.

What are the numbers about though? And what's that "i =" doing there?

**ii)**This type of loop counts from the first number (0) to the second number (9), and runs the code within the loop for each number counted.

The example above would start with the counter called "i" on "0", and then run all the code inside the loop. Then, it would increment the counter by one, and run through all the code again. Then it increments "i" again, runs the code again, and so on.

After the code has been run for the 10th time (ie "i" = 9) then the loop will stop and exit at the

**end****iii)**The first and most obvious use of a

**for** loop is to run a piece of code multiple times. For example, the code below would produce 3 message boxes:

` for i = 0,2 do`

MessageBox("Hallo Fluffy")

end

**iv)**The second thing that

**for** loops are useful for, is to change values for lots of different entities that are

**numbered**. For example, asteroids. :>

Asteroids are numbered according to their ID. Suppose we had 100 asteroids and wanted to change the Send Distance of all of them at once. Here is how we could do it:

` for i = 0,99 do`

GetAsteroid(i).SendDistance = 5000

end

What will happen is that each time the loop runs through, "i" is increased by 1, and then when we refer to "GetAsteroid(i)" it's simply getting whichever asteroid ID currently corresponds to the value of "i".

So on the first run through of the loop, the command effectively reduces to this:

` GetAsteroid(0).SendDistance = 5000`

On the second pass of the loop, it would resolve to this:

` GetAsteroid(1).SendDistance = 5000`

...and so on.

**v)**Thus, this:

` for i = 0,99 do`

GetAsteroid(i).SendDistance = 5000

end

...is just a (much) shorter way of writing this:

`GetAsteroid(0).SendDistance = 5000`

GetAsteroid(1).SendDistance = 5000

GetAsteroid(2).SendDistance = 5000

GetAsteroid(3).SendDistance = 5000

GetAsteroid(4).SendDistance = 5000

GetAsteroid(5).SendDistance = 5000

GetAsteroid(6).SendDistance = 5000

GetAsteroid(7).SendDistance = 5000

GetAsteroid(8).SendDistance = 5000

GetAsteroid(9).SendDistance = 5000

GetAsteroid(10).SendDistance = 5000

GetAsteroid(11).SendDistance = 5000

GetAsteroid(12).SendDistance = 5000

GetAsteroid(13).SendDistance = 5000

GetAsteroid(14).SendDistance = 5000

GetAsteroid(15).SendDistance = 5000

GetAsteroid(16).SendDistance = 5000

GetAsteroid(17).SendDistance = 5000

GetAsteroid(18).SendDistance = 5000

GetAsteroid(19).SendDistance = 5000

GetAsteroid(20).SendDistance = 5000

GetAsteroid(21).SendDistance = 5000

GetAsteroid(22).SendDistance = 5000

GetAsteroid(23).SendDistance = 5000

GetAsteroid(24).SendDistance = 5000

GetAsteroid(25).SendDistance = 5000

GetAsteroid(26).SendDistance = 5000

GetAsteroid(27).SendDistance = 5000

GetAsteroid(28).SendDistance = 5000

GetAsteroid(29).SendDistance = 5000

GetAsteroid(30).SendDistance = 5000

GetAsteroid(31).SendDistance = 5000

GetAsteroid(32).SendDistance = 5000

GetAsteroid(33).SendDistance = 5000

GetAsteroid(34).SendDistance = 5000

GetAsteroid(35).SendDistance = 5000

GetAsteroid(36).SendDistance = 5000

GetAsteroid(37).SendDistance = 5000

GetAsteroid(38).SendDistance = 5000

GetAsteroid(39).SendDistance = 5000

GetAsteroid(40).SendDistance = 5000

GetAsteroid(41).SendDistance = 5000

GetAsteroid(42).SendDistance = 5000

GetAsteroid(43).SendDistance = 5000

GetAsteroid(44).SendDistance = 5000

-- ok i can't be bothered writing them all out.

-- hopefully you get the idea...

-- ...

GetAsteroid(97).SendDistance = 5000

GetAsteroid(98).SendDistance = 5000

GetAsteroid(99).SendDistance = 5000

**vi)**Finally, just so you're aware, you don't

*have* to use "i" for the counter. You can use whatever name you want. For example:

` for fluffy = 0,99 do`

GetAsteroid(fluffy):AddSeedlings(fluffy)

end

That would put 0 seedlings on asteroid 0, 1 seedling on asteroid 1, 2 seedlings on asteroid 2, etc.

It doesn't matter that we called the counter "fluffy" instead of "i". It will still work just fine. :>

*This explosion is HUGE....***3. Arrays****i)**Arrays are special variables that have lots of different "slots" for storing values, rather than just being one big slot like the variables we've seen so far.

Another way to think about arrays is to imagine them as numbered lists.

**ii)**Lets look at some array syntax.

Before you can use an array, you must first declare it like this:

` fluffy = {}`

Note the unusual shaped brackets. This creates an empty array called

**fluffy**.

The usual place to put this command is anywhere in

**function LevelSetup()**, but for now, to keep everything together, I'll put mine at the very top of

**function LevelLogic()**.

`function LevelLogic()`

-- declare the array

fluffy = {}

while GameRunning() do

-- some commands go here later

coroutine.yield()

end

end

**iii)**Now that we've initialised the

**fluffy** array, we can start assigning values to the slots in it. But how to refer to the different slots in

**fluffy**?

The answer is with some square brackets tagged on the end like this:

**fluffy[0]**That would refer to slot 0 of

**fluffy**.

From now on when we refer to the array, we always refer to the slot number that we are interested in as well.

By convention, we usually start with slot 0.

So we can use commands like these:

` fluffy[0] = 2`

fluffy[1] = 4

fluffy[2] = 7

fluffy[3] = 9

fluffy[4] = 5

**iv)**Then, when we want to know about the value in a slot, we can test it using the same square brackets as above. For example:

` if fluffy[0] == 2 then`

MessageBox("w00t!")

end

So what practical use is this?

Well, as I discovered, it turns out there are all sorts of reasons why you might want to store things in an array like this.

To illustrate why, and also to show you a good example of using an array to do useful work, lets implement an example advanced behaviour using some arrays.

What we will do is create a mechanic whereby the player's lasermine can fly from one asteroid to another, and each asteroid they fly to changes the background to a different colour.

So when the game starts, the player's lasermine is orbiting the first asteroid, and the background is black. When it flies to a nearby second asteroid, the background turns red. When it flies to the next asteroid, the background turns blue, and so on.

We will store the colours in three arrays named

**red**,

**green** and

**blue** because those are nice logical names that we can plug into our

**SetBackDropColour** command later on.

**v)**In this example lets assume there are 5 asteroids in the level, numbered 0, 1, 2, 3 and 4.

At the top of our

**function LevelLogic()**, before the start of the

**while** loop, I'll initialise the arrays and assign some values to them.

`function LevelLogic()`

-- initialise arrays

red = {}

green = {}

blue = {}

-- assign values for the different slots in the arrays

red[0] = 0

green[0] = 0

blue[0] = 0

red[1] = 255

green[1] = 0

blue[1] = 0

red[2] = 0

green[2] = 255

blue[2] = 0

red[3] = 0

green[3] = 0

blue[3] = 255

red[4] = 127

green[4] = 0

blue[4] = 127

while GameRunning() do

-- later on we'll put some commands here!

coroutine.yield()

end

end

**vi)**Now we'll just need to make some code in our

**while** loop to check if the player has a mine at any of the asteroids.

Here we can combine the

**for** loops we learned about earlier with the arrays we've just initialised.

`function LevelLogic()`

-- initialise arrays

red = {}

green = {}

blue = {}

-- assign values for the different slots in the arrays

red[0] = 0

green[0] = 0

blue[0] = 0

red[1] = 255

green[1] = 0

blue[1] = 0

red[2] = 0

green[2] = 255

blue[2] = 0

red[3] = 0

green[3] = 0

blue[3] = 255

red[4] = 127

green[4] = 0

blue[4] = 127

while GameRunning() do

-- Check all the asteroids in turn

for i = 0,4 do

-- if an asteroid has a player mine on it...

if GetAsteroid(i):GetNumMines(1) > 0 then

-- set the backdrop colour to the values for slot i in the red, green and blue arrays

SetBackdropColour(red[i], green[i], blue[i])

end

end

coroutine.yield()

end

end

**And that is how we can use arrays to do useful things for us.**You can download a playable level file containing this example script and check out the functionality for yourself. It's attached to the bottom of this post, named "

**array example.lua**".

**vii)**Before we finish up with arrays, lets look at how you might set the values for hundreds of slots in an array using a formula placed inside a

**for** loop.

Imagine the example above, but instead of 5 colour-changing asteroids, we have 500 colour-changing asteroids!

Clearly setting these manually would take a very long time.

Therefore it becomes necessary to think up some way to set the colours for all 500 at once.

Lets take the

**red** array as our example, and figure out some way to generate different values for all 500 slots.

`for i = 0,500 do`

red[i] = ?

end

Any ideas? Well, the very most simple thing we could do is to make the slot [

**i**] equal to the value

**i** itself.

`for i = 0,500 do`

red[i] = i

end

Then slot 0 would contain the value 0, slot 230 would contain the value 230, and so on.

**viii)**That's a bit boring though - and besides, Asteroid 500 would have the corresponding red value of 500, and that's more than the maximum 255 that

**SetBackdropColour** is expecting.

Maybe we can use some maths to make a more interesting series of values!

`for i = 0,500 do`

red[i] = 1 / i

end

That should be pretty cool. Imagine what this would work out as, for all the different values of

**i**.

If

**i** = 2, then the calculation would be 1 / 2 =

**0.5**If

**i** = 5, then the calculation would be 1 / 5 =

**0.2**If

**i** = 10, then the calculation would be 1 / 10 =

**0.1**If

**i** = 100, then the calculation would be 1 / 100 =

**0.01****ix)**There's a problem with this though.

What do you think happens when i = 0?

If

**i** = 0, then the calculation would be 1 / 0 = ........

Dividing by zero makes Eufloria crash. It's because if you divide something by zero, the answer is always infinity.

To combat this problem, we can make a change:

`for i = 0,500 do`

red[i] = 1 / (i + 1)

end

Why do you think we need "(i + 1)" and not simply "

**i**" on its own?

We add 1 to i. That way on the first cycle it works out as 1 / 1, which is just a nice harmless 1 instead of an infinity. :>

The curly brackets just mean that part of the calculation will be worked out first, as a seperate chunk - before the result is used in the rest of the calculation.

**x)**This calculation will then give us an array called

**red** that has all its slots filled with values between 0 and 1.

This is ideal mathematically, because it means we can just multiply in the range that we need. In this case, SetBackdropColour expects values between 0 and 255.

So we just multiply each value by 255:

`for i = 0,500 do`

red[i] = 1 / (i + 1)

red[i] = red[i] * 255

end

Then, using some similar example calculations as we used above...

If

**i** = 0, then the calculation would be (1 / (0 + 1)) * 255 = 1 * 255 =

**255**If

**i** = 1, then the calculation would be (1 / (1 + 1)) * 255 = 0.5 * 255 =

**127**If

**i** = 4, then the calculation would be (1 / (4 + 1)) * 255 = 0.2 * 255 =

**51**If

**i** = 9, then the calculation would be (1 / (9 + 1)) * 255 = 0.1 * 255 =

**26**If

**i** = 99, then the calculation would be (1 / (99 + 1)) * 255 = 0.01 * 255 =

**3**Hopefully you should be able to see from this selection of examples that this gives us a nice smooth gradiant of different values. The asteroids with the lowest ID numbers will be reddest. :>

*Multiple arrays can be combined to represent a matrix. We'll cover matrix usage in a future Advanced Guide.*It's up to you to find your own formulas to assign values for your arrays. The maths sections below will help you with this.

**4. math.random****i)**The command

**math.random(a,b)** generates a random number between

**a** and

**b**.

So for example, the following command would mean that the variable "

**MyRandomNumber**" is assigned a value between 1 and 6.

`MyRandomNumber = math.random(1,6)`

This is the equivalent of rolling a six-sided dice. :>

**ii)**So what use is this?

Well, it's useful for all sorts of things. One good example is randomising the stats of your asteroids. This can make the level different every time it is played, adding variety.

Suppose we have an asteroid like this one...

`a = AddAsteroidWithAttribs(1500,1200, 0.5,0.5,0.5)`

a.owner = 1

a.TreeCap = 3

a.radius = 200

a.SendDistance = 2000

a.Moveable = false

a:AddSeedlings(20)

We could make some of the stats random, like this:

`a = AddAsteroidWithAttribs(1500,1200, 0.5,0.5,0.5)`

a.owner = 1

a.TreeCap = math.random(2,5)

a.radius = math.random(100,450)

a.SendDistance = 2000

a.Moveable = false

a:AddSeedlings(math.random(10,50))

Now the asteroid will have a different treecap, radius and number of seedlings each time we play. :>

**iii)**How else could we improve this code?

Well, we could randomise the send distance, too.

That would be like this:

`a.SendDistance = math.random(1500,2500)`

But the problem with that approach is that we lose the relationship between an asteroid's radius, and its Send Distance. It's visually pleasing to have asteroids with send distances proportional to their size.

So maybe we could do something else instead, that preserves the relationship between size and send distance...

`a.radius = math.random(100,450)`

a.SendDistance = a.radius * 10

This means that if the asteroid spawns with a radius of 100, its send distance would be 1000; if it spawns with radius 250, its send distance would be 2500, and so on.

This technique allows us to model the send distance after the asteroid size, even when we don't know in advance exactly what size the asteroid will be.

**iv)**What else could we do to make it even more random?

Well, we could try adjusting the X and Y coordinates of the asteroid.

So our

**a = AddAsteroidWithAttribs(1500,1200, 0.5,0.5,0.5)** becomes this:

`a = AddAsteroidWithAttribs(math.random(-1500,1500),math.random(-1200,1200), 0.5,0.5,0.5)`

Negative numbers like those in the line above work fine in math.random. :>

But, there is a problem with randomising asteroid positions, and that is that there's a chance asteroids may spawn on top of each other!

This clearly will not do, so to fix it we can change the

**Moveable** command from

**false** to

**true**.

`a.Moveable = true`

Then, if asteroids spawn on top of each other, the game will move them apart for us. :>

**v)**Lets take a look at what we've got so far:

`a = AddAsteroidWithAttribs(math.random(-1500,1500),math.random(-1200,1200), 0.5,0.5,0.5)`

a.owner = 1

a.TreeCap = math.random(2,5)

a.radius = math.random(100,450)

a.SendDistance = a.radius * 10

a.Moveable = true

a:AddSeedlings(math.random(10,50))

That's pretty damn random!

We might get a tiny asteorid with treecap 5 and 50 seedlings to the northeast, or we might get a large asteroid with treecap 2 and 10 seedlings to the south... And so forth.

There's one more thing I want to show you while we're looking at math.random, and that is the question of how to randomise asteroid attributes effectively.

**vi)**Consider this command:

`a = AddAsteroidWithAttribs(math.random(-1500,1500),math.random(-1200,1200), math.random(0,1),math.random(0,1),math.random(0,1))`

Ok so it's a long one, but basically if you study that, you should get that the asteroid attributes should be between 0 and 1.

Well, that's correct. But there's a problem that you would quickly discover if you were to use this type of command on one of your asteroids. See if you can figure out what it is. :> You'll be doing well if you manage, because I haven't explicitly mentioned it yet...

In the mean time, lets reorganise that line so it's much easier to read:

`-- generate X and Y`

x = math.random(-1500,1500)

y = math.random(-1200,1200)

-- generate stats

energy = math.random(0,1)

strength = math.random(0,1)

speed = math.random(0,1)

-- add the asteroid

a = AddAsteroidWithAttribs(x,y, energy,strength,speed)

That's better. Did you spot what the problem could be?

**vii)**The problem we will face is that the asteroid's attributes will be set to either total maximum (1) or nothing whatsoever (0).

There's no in between!

To have nice stats for our asteroids, we actually wanted values

**between** 0 and 1 -

not 0 or 1 themselves!More generally, we can say that the

**math.random(a,b)** command generates an

integer (a whole number) between A and B.

So suppose we used

**math.random(1,5)**Values we might get from that command:

Values we definitely WOULD NOT get from that command:

Hopefully this is crystal clear to you now. Assuming so, lets press on and solve the asteroid attributes problem!

**viii)**If we really need random values like 0.3, we are going to have to find a way to make them out of integers.

Here's one way:

`-- generate stats`

energy = math.random(0,10) / 10

strength = math.random(0,10) / 10

speed = math.random(0,10) / 10

So lets follow through what happens here.

Take

**energy** for our example. First, a random number between 0 and 10 is generated; lets say we generate a

**6**.

Then, that 6 is divided by 10. What's 6 divided by 10? It's 0.6!

So then the asteroid would have 60% energy.

**ix)**The problem with that approach, once again, is that simply randomising the asteroid's attributes sometimes results in tiny asteroids with awesome stats, and huge asteroids with rubbish stats.

This breaks the relationship we talked about earlier between the size of an asteroid, and its quality.

What do we really need, then?

Lets think about this. It's important to put the solution into words (and perhaps diagrams) before we try to code it.

We need asteroids to have their stats correspond with their size. But we don't simply want flat values for energy, strength and speed - we want the

*proportions of each stat* to be random.

So for example, we want an average sized asteroid to have stats like 0.3,0.7,0.5 - NOT 0.5,0.5,0.5.

I know a good solution to this problem, but instead of me telling you what it is yet again, I'm going to leave this problem open-ended and see if you guys can work it out. :> If you've read this far down the guide you already know everything you need to know in terms of commands needed to code it.

**5. math.sin, math.cos****i)**Maybe you remember these dreaded symbols from school. If you're anything like me, the "sin" and "cos" buttons on my calculator taunted me back in maths class with their mystery. Nobody ever really bothered to explain to me what they actually

*are*. I mean, what are they? Sure, you put numbers into them and a result magically comes out, but

*how*?

Incidentally, I had forgotten just about everything I used to know about their usage when I started teaching myself to code in lua. I had to figure this out on my own from researching it on the internets. So even if you've never heard these terms before in your life, don't worry. I'll help you to understand them. :>

The first half of this section will focus on helping you to conceptualise what sine and cosine are.

The second half will focus on using the calculations in your scripts to do useful (and often spectacular!) work.

**ii)**The commands might be used inside of a

**while GameRunning() do** loop, as follows:

`x = math.sin(GetGameTime())`

y = math.cos(GetGameTime())

...or to make it slightly clearer, lets assign the game time to a variable first:

`time = GetGameTime()`

x = math.sin(time)

y = math.cos(time)

We feed a value

*into* the

**math.sin** or

**math.cos** (in this case,

**time**).

Then we record the result to a variable, in this case

**x** and

**y** respectively.

What sort of values will we get for

**x** and

**y** as the Game Time proceeds?

**iii)**It will

*always* be a value between -1 and 1. So it might be 0.33351, or it might be -0.778391, or it might even be 0. It all depends on what value is fed into it.

If

**sin** is plotted on a graph, it produces a wavy line:

This wavy line repeats itself for as long as the value fed in (eg GetGameTime()) increases.

The graph for

**cos** is almost exactly the same. The only difference is that it starts with the line in a different position. Don't worry about this for now, it will all become clear (hopefully!)

**iv)**Lets proceed to conceptualising how this "value between -1 and 1" is calculated.

Imagine you are a bird hovering above a field.

In the field below you is a circular train track.

A train drives in circles round the track.

The

distance the train has travelled so far is what we will feed into our sin and cos to produce the values between -1 and 1.

**math.sin(DistanceTravelled)** is equal to wherever on the

**vertical axis** the train would be after travelling that far.

**math.cos(DistanceTravelled)** is equal to wherever on the

**horizontal axis** the train would be after travelling that far.

So for example, if math.cos(DistanceTravelled) = -1, the train must be on the west side of the track.

Lets look at another diagram to try to visualise this better:

**v)**That's the whole deal.

**Sin** and

**cos** are simply representations of each axis when imagining travelling round and round in a circle.

So if the train had gone round the circle exactly once, the output would be the same as if it had gone round the circle exactly twice, or exactly 3 times.

Please download and run the level file attached to this post called "

**sincos example.lua**"

Play the level in Eufloria and you can see another example of what

**sin** and

**cos** do. Check the names of each asteroid to see what they represent.

**vi)**The commands

**math.sin()** and

**math.cos()** both expect a number inside their brackets, as we've seen.

This input is technically thought to be radians, rather than train tracks or seconds of game time, but in reality you can use any unit of measurement you want and it will be considered as the radians-equivalent. Time is a very common thing to use as the input for

**sin** and

**cos**, because it continually counts up at a steady pace, making it ideal for achieving regular pulsing motions.

Now lets look at some useful things we can do with this pulsing effect.

**vii)**First of all lets build our usual wrapper for this to go into:

`function LevelLogic()`

while GameRunning() do

-- some code goes here later!

coroutine.yield()

end

end

Now we'll record the game time to a variable called

**time**...

`function LevelLogic()`

while GameRunning() do

-- record the time

time = GetGameTime()

coroutine.yield()

end

end

Now lets create a variable called.... oh... I don't know, I guess we'll go with something generic like

**input**.

`function LevelLogic()`

while GameRunning() do

-- record the time

time = GetGameTime()

-- calculate a value for "input" by taking the math.sin of time

input = math.sin(time)

coroutine.yield()

end

end

A good start. Now the variable called

**input** will pulse between -1 and 1 as the game progresses.

**1****-1**In this graph, the X-axis represents game time. The Y-axis represents the value we'll get for

**input**.

**viii)**Now that we've got our pulsing variable, lets figure out something that it could control.

How about the radius of an asteroid?

We could make an asteroid grow bigger, then smaller... then bigger, then smaller...

For that, values between -1 and 1 are no good. We need values between, say, 50 and 500!

Time to do some maths to change

**input** to our desired range of values.

`-- input is a value between -1 and 1`

input = input + 1

-- now input is a value between 0 and 2

input = input / 2

-- now input is a value between 0 and 1

input = input * 450

-- now input is a value between 0 and 450

input = input + 50

-- now input is a value between 50 and 500!

I did it in some slow steps there to make it easy to follow, but to make things a bit neater, I could just bundle all of that into one line like this:

`input = (((input + 1) / 2) * 450) + 50`

**iv)**Now lets fire that line into the rest of our code:

`function LevelLogic()`

while GameRunning() do

-- record the time

time = GetGameTime()

-- calculate a value for "input" by taking the math.sin of time

input = math.sin(time)

-- now input is between -1 and 1

input = (((input + 1) / 2) * 450) + 50

-- now input is between 50 and 500

coroutine.yield()

end

end

Finally, lets change the radius of Asteroid 0 using our "input".

`function LevelLogic()`

while GameRunning() do

-- record the time

time = GetGameTime()

-- calculate a value for "input" by taking the math.sin of time

input = math.sin(time)

-- now input is between -1 and 1

input = (((input + 1) / 2) * 450) + 50

-- now input is between 50 and 500

GetAsteroid(0).radius = input

coroutine.yield()

end

end

And there we have it. Download the level file "

**pulse example.lua**" and play it in Eufloria to see this code in action.

**6. Build your own functions!****i)**This is it. This is the part where you realise you are actually inside the matrix.

Well, I guess we'll start at the beginning. What is a function?

A function is a container for code.

You put code inside the container, and then you "call" the function from elsewhere.

This very general description likely makes little sense to you for now. Don't worry! Lets look at some examples.

**ii)**For our first example, lets imagine it's the start of the game and you want to bring up a MessageBox to greet the player.

Your

**function LevelLogic()** might look like this:

`function LevelLogic()`

Pause()

MessageBox("Welcome to happyfuntimeland. Is nice here.")

WaitDialog()

Unpause()

end

Four lines of code just to bring up a message on the screen!

It's not a problem for now really, but when you have a

**LevelLogic()** that is several hundred lines long, it can start to become a nightmare to keep track of where everything is.

Lets put those 4 commands into a function.

We place this text

below everything else, including the "

**end**" of your

**LevelLogic()**.

`function GreetPlayer()`

Pause()

MessageBox("Welcome to happyfuntimeland. Is nice here.")

WaitDialog()

Unpause()

end

**iii)**Now that we've moved that code into its own function, we will need to "call" it.

`function LevelLogic()`

GreetPlayer()

end

function GreetPlayer()

Pause()

MessageBox("Welcome to happyfuntimeland. Is nice here.")

WaitDialog()

Unpause()

end

Ok so what happens is that when the level loads, LevelLogic() will run the command "GreetPlayer()". This will then run the commands in the function with the corresponding name. It's practically as if the compiler copies and pastes the code inside our new function in place of anywhere we put our "GreetPlayer()" command.

**iv)**What about other occasions when we need a Message Box?

If we know that we'll always pause the game when these messages appear, then the only thing that changes each time is the content of the message.

We could have lots of different functions, with names like "GreetPlayer()", "PlayerLoseMessage()", "PlayerWinMessage()", and so on. But that would mean writing out loads of functions and cluttering up our code again, when really we only need one function to do this.

How can we display different messages with just the one function, you ask?

Well, what we do is that we

**pass a variable into the function**.

From now on when we use our "GreetPlayer()" command, the command will expect a variable or value of some kind in the brackets.

Like this:

`function LevelLogic()`

DisplayMessage("Welcome to happyfuntimeland. Is nice here.")

end

function DisplayMessage(message)

Pause()

MessageBox(message)

WaitDialog()

Unpause()

end

**v)**So lets walk through what happens there.

First, the level loads and

**LevelLogic()** starts running. Straight away, it runs the DisplayMessage command which activates the DisplayMessage function. It also passes a value into the function, in this case the value is "Welcome to happyfuntimeland. Is nice here."

The function receives this value, and stores it in a variable called "message".

Then, the game is paused, and a MessageBox is displayed, containing the contents of the variable called "message".

When the MessageBox is clicked, it will Unpause the game.

**vi)**If you followed all that, you should be able to figure out what this code would do:

`function LevelLogic()`

DisplayMessage("Welcome to happyfuntimeland. Is nice here.")

DisplayMessage("Chase rabbits down holes!!")

DisplayMessage("It's fun and the rabbits enjoy it.")

end

function DisplayMessage(message)

Pause()

MessageBox(message)

WaitDialog()

Unpause()

end

It would display 3 message boxes one after the other, all with different messages!

This is already just 9 lines of code. Doing it the "normal" way would be 12 lines. Hopefully you can see how, if you have a lot of message boxes, this would save you a lot of space!

**viii)**You can also call functions from within functions.

For example, consider this code:

`function LevelSetup()`

energy = {}

strength = {}

speed = {}

radius = {}

senddist = {}

x = {}

y = {}

for i = 0,math.random(5,15) do

energy[i] = math.random(1,10) / 10

strength[i] = math.random(1,10) / 10

speed[i] = math.random(1,10) / 10

radius[i] = math.random (100,500)

senddist[i] = radius[i] * 10

x[i] = math.random(-5000,5000)

y[i] = math.radom(-5000,5000)

a = AddAsteroidWithAttribs(x[i],y[i],energy[i],strength[i],speed[i])

a.radius = radius[i]

a.SendDistance = senddist[i]

a.Moveable = true

end

end

We could split it up like this....

`function LevelSetup()`

InitArrays()

for i = 0,math.random(5,15) do

RandomiseAttributes(i)

PlaceTheRoids(i)

end

end

function InitArrays()

energy = {}

strength = {}

speed = {}

radius = {}

senddist = {}

x = {}

y = {}

end

function RandomiseAttributes(RoidID)

energy[RoidID] = math.random(1,10) / 10

strength[RoidID] = math.random(1,10) / 10

speed[RoidID] = math.random(1,10) / 10

radius[RoidID] = math.random (100,500)

senddist[RoidID] = radius[i] * 10

x[RoidID] = math.random(-5000,5000)

y[RoidID] = math.radom(-5000,5000)

end

function PlaceTheRoids(RoidID)

a = AddAsteroidWithAttribs(x[RoidID],y[RoidID],energy[RoidID],strength[RoidID],speed[RoidID])

a.radius = radius[RoidID]

a.SendDistance = senddist[RoidID]

a.Moveable = true

end

... and then like this.

`function LevelSetup()`

GenerateLevel()

end

function GenerateLevel()

InitArrays()

for i = 0,math.random(5,15) do

RandomiseAttributes(i)

PlaceTheRoids(i)

end

end

function InitArrays()

energy = {}

strength = {}

speed = {}

radius = {}

senddist = {}

x = {}

y = {}

end

function RandomiseAttributes(RoidID)

energy[RoidID] = math.random(1,10) / 10

strength[RoidID] = math.random(1,10) / 10

speed[RoidID] = math.random(1,10) / 10

radius[RoidID] = math.random (100,500)

senddist[RoidID] = radius[i] * 10

x[RoidID] = math.random(-5000,5000)

y[RoidID] = math.radom(-5000,5000)

end

function PlaceTheRoids(RoidID)

a = AddAsteroidWithAttribs(x[RoidID],y[RoidID],energy[RoidID],strength[RoidID],speed[RoidID])

a.radius = radius[RoidID]

a.SendDistance = senddist[RoidID]

a.Moveable = true

end

Note how some functions are called from within another function. You can "nest" functions as deep as you like in this way.

**viii)**Have you realised what's going on yet?

You really are somewhere down the rabbit hole, Alice... because

**function LevelSetup()** and

**function LevelLogic()** are themselves just functions which are being called by the Eufloria engine.

You've been inside The Matrix all along!