• Hey Guest! Ever feel like entering a Game Jam, but the time limit is always too much pressure? We get it... You lead a hectic life and dedicating 3 whole days to make a game just doesn't work for you! So, why not enter the GMC SLOW JAM? Take your time! Kick back and make your game over 4 months! Interested? Then just click here!

Legacy GM z axis arc jump

I could use some help trying to figure out how to implement jumping for one of my enemy AIs. Normally the solution would be as simple as using gravity and vspeed, but for the type of game I'm making that won't work. I'm working on a grid-based battling game in the vain of Megaman Battle Network (MBN). In MBN, the player and enemy can move to tiles on their respective sides of the grid. In my game, each panel is numbered from 0 to 23 as you can see in the image below:
numbered panels.png

I want to make it so that an enemy can jump from tile to tile, but I'm not entirely sure how to implement this. My best guess is using z-axis instead of the normal method used in making stuff like platformers, but I think I'd also need the enemy to maybe jump at an arc. For example, I might want the enemy to be able to jump from 20 to 23, 7 to 21, or 7 to 4.

arc jump.png arc jump2.png arc jump3.png
Any idea of how I can go about coding this?
 
N

nickvm98

Guest
Use:
draw_sprite_ext(sprite, subimg, x, y, xscale, yscale, rot, colour, alpha);
and in the y parameter write:
draw_sprite_ext( spr_enemy,0, x, y+z,1,1,0, c_white,1);
Now here is the complicated part. How do you want to project the parabolic path of the jump animation? The cheap way to cycle z to a nth height and just land on a square at a certain time or do you actually want a kinematic arc like this: https://www.quora.com/How-do-they-calculate-the-distance-of-the-six-in-a-cricket-match
 
Use:
draw_sprite_ext(sprite, subimg, x, y, xscale, yscale, rot, colour, alpha);
and in the y parameter write:
draw_sprite_ext( spr_enemy,0, x, y+z,1,1,0, c_white,1);
Now here is the complicated part. How do you want to project the parabolic path of the jump animation? The cheap way to cycle z to a nth height and just land on a square at a certain time or do you actually want a kinematic arc like this: https://www.quora.com/How-do-they-calculate-the-distance-of-the-six-in-a-cricket-match
Not sure, but I think a kinematic arc is what I'm looking to achieve.
 
D

direstatus

Guest
Use:
draw_sprite_ext(sprite, subimg, x, y, xscale, yscale, rot, colour, alpha);
and in the y parameter write:
draw_sprite_ext( spr_enemy,0, x, y+z,1,1,0, c_white,1);
Now here is the complicated part. How do you want to project the parabolic path of the jump animation? The cheap way to cycle z to a nth height and just land on a square at a certain time or do you actually want a kinematic arc like this: https://www.quora.com/How-do-they-calculate-the-distance-of-the-six-in-a-cricket-match
To hopefully explain the implementation of this you'd take the equation you get and create an "x-counter" variable (that's what I'd call it at least), which acts as the driving factor behind your y=f(x) equation. Essentially you'll be setting x to a predetermined value (either via a table, or via a loop... ex, for x=0...x<numframes...x++{x++}... and in each frame, you'll be setting your sprite's y value to be equal to y=f(x). So, if y=sin(x)^2*16 (just picking a random equation), you'll literally have to check at each frame what y=sin(x)^2*16 is...which will likely require rounding and other stuff, but x will always be an integer because you were literally just going x+=1 the whole time. I hope that makes sense as to how to implement these sorts of equations in a graphical sense.
 
N

nickvm98

Guest
During your jump animation, what you want to do is calculate the distance at which you want to move your enemy from point A to point B. Use:
point_distance(x1, y1, x2, y2) (x1,y1) will be your current position. (x2,y2) will be your destination.

Secondly you need to decide the gravity and launch angle of the enemy's jump. 10px/s^2 gravity and 60 degrees is reasonable. Change them if you want, its up to you.

The velocity of the launch is required so I transposed the formula:
Range=((Velocity^2)*dsin(2*Angle))/Gravity into Velocity=sqrt((Range*Gravity)/sin(2*Angle))

Next is the time is will take which you can decide on your own or just calculate it with the horizontal component of the launch velocity which would be:
Time=Distance/(Velocity*dcos(Angle))*Motion_Speed

Motion_Speed is the speed of the animation if you want a slow motion effect but take it out of you don't want it.

You next need the height, create a variable max_z which will be:
max_z=Velocity^2/(2*Gravity)

Okay this is where we have figured out all our variables and just need to move our enemy. To move our enemy in the horizontal x/y dimension simply move the enemy over to the second location with:
move_towards_point( x,y, sp ); Speed will be Velocity*dcos(Angle).

For our z movement we need a time variable and use a kinematic formula to determine the current z location of the enemy which is:
Height=-0.5*Gravity*Time+Velocity*Time+Distance

Just run the time variable from 0 to its maximum time and the enemy will jump from point A to B like an actual realistic ball. You may be surprised by the realism of the enemy's jump.

Code:
//Create
distance=point_distance(x,y,target_x,target_y);
gravity=10*x;       //x is the amount of pixels that make up an in-game meter.
angle=x;              //x is anything between 1 and 89. You cant throw a ball at 0 or 90 degrees.
velocity=sqrt((distance*gravity)/sin(2*angle));
t=0;
z=0;
max_time=Distance/(Velocity*dcos(Angle));
max_z=velocity^2/(2*gravity)

//Step
move_towards_point(target_x,target_y,dcos(angle));
t=median(0,t+1,time);
z=-0.5*gravity*t+velocity*t+distance

I haven't tested it but let me know if you need explanation or if there's a mistake.
 
During your jump animation, what you want to do is calculate the distance at which you want to move your enemy from point A to point B. Use:
point_distance(x1, y1, x2, y2) (x1,y1) will be your current position. (x2,y2) will be your destination.

Secondly you need to decide the gravity and launch angle of the enemy's jump. 10px/s^2 gravity and 60 degrees is reasonable. Change them if you want, its up to you.

The velocity of the launch is required so I transposed the formula:
Range=((Velocity^2)*dsin(2*Angle))/Gravity into Velocity=sqrt((Range*Gravity)/sin(2*Angle))

Next is the time is will take which you can decide on your own or just calculate it with the horizontal component of the launch velocity which would be:
Time=Distance/(Velocity*dcos(Angle))*Motion_Speed

Motion_Speed is the speed of the animation if you want a slow motion effect but take it out of you don't want it.

You next need the height, create a variable max_z which will be:
max_z=Velocity^2/(2*Gravity)

Okay this is where we have figured out all our variables and just need to move our enemy. To move our enemy in the horizontal x/y dimension simply move the enemy over to the second location with:
move_towards_point( x,y, sp ); Speed will be Velocity*dcos(Angle).

For our z movement we need a time variable and use a kinematic formula to determine the current z location of the enemy which is:
Height=-0.5*Gravity*Time+Velocity*Time+Distance

Just run the time variable from 0 to its maximum time and the enemy will jump from point A to B like an actual realistic ball. You may be surprised by the realism of the enemy's jump.

Code:
//Create
distance=point_distance(x,y,target_x,target_y);
gravity=10*x;       //x is the amount of pixels that make up an in-game meter.
angle=x;              //x is anything between 1 and 89. You cant throw a ball at 0 or 90 degrees.
velocity=sqrt((distance*gravity)/sin(2*angle));
t=0;
z=0;
max_time=Distance/(Velocity*dcos(Angle));
max_z=velocity^2/(2*gravity)

//Step
move_towards_point(target_x,target_y,dcos(angle));
t=median(0,t+1,time);
z=-0.5*gravity*t+velocity*t+distance

I haven't tested it but let me know if you need explanation or if there's a mistake.
I’m a bit confused as to what I should enter for gravity. You said 10px/s^2, does that mean I should have 10 / SOME NUMBER ^ 2 or that a need to multiply the square root of some number by 10? In the code provided you just have 10* x so I assume x should be substituted for a number and it’s not simply the horizontal value of the object.

You had some capital letters for some variable names, which I saw and corrected. Must’ve been late for you as well, which is why I couldn’t test the code as much as I needed to. Did manage to run the game a few times and I don’t see my object at all so I’m guessing it flew off screen or something.

A few other things...

So t is the time variable, correct? If so, I’m guessing you meant to put max_time instead of just time in the step event. Wouldn’t it be more efficient to use a for loop instead of using median?

edit: Drew the variables to the screen and noticed that t (time?) isn't changing so I changed the code to this instead:
Code:
 if t < max_time { t++; }
However, the enemy just disappears (probably moving quickly off the screen) still. The max_time shown is 1.J, which is quite odd. There must be a mistake somewhere.
 
Last edited:

GMWolf

aka fel666
Let's bust out the equations of motion:
S = ut + 1/2 at^2

So first decide on horizontal (X,y) speed. From that, calculate the time it would take to reach the tile.

Decide on a jump height.
Decide on some acceleration (gravity)

Then we can use suvat to calculate the z speed and acceleration.
S = ut + 0.5at^2

S - 0.5at^2 = ut
(S - 0.5at^2)/t = u
S/t - 0.5at = u


Where u is you initial z speed. S is the jump height, t is the time to get to the tile (based on horizontal movement) and a is gravity.
 
Let me start by saying I suck at math. lol so from what I'm understanding in my create event I'd have the following variables:

Code:
hspd = 6; // Horizontal Speed
t = ?; // Time
grav = 0.04; // Gravity
zspeed = height / time - 0.5 * power ( grav, 2 ); // z speed
height = zspeed * time + 0.5 * grav * power( time, 2 ); // Jump height
Not sure what I should set time to, guessing I'd divide both sides of the original equation by t...?

Really confused with a lot of this. Never was any good with math in school.
 
N

nickvm98

Guest
I’m a bit confused as to what I should enter for gravity. You said 10px/s^2, does that mean I should have 10 / SOME NUMBER ^ 2 or that a need to multiply the square root of some number by 10? In the code provided you just have 10* x so I assume x should be substituted for a number and it’s not simply the horizontal value of the object.

You had some capital letters for some variable names, which I saw and corrected. Must’ve been late for you as well, which is why I couldn’t test the code as much as I needed to. Did manage to run the game a few times and I don’t see my object at all so I’m guessing it flew off screen or something.

A few other things...

So t is the time variable, correct? If so, I’m guessing you meant to put max_time instead of just time in the step event. Wouldn't it be more efficient to use a for loop instead of using median?

edit: Drew the variables to the screen and noticed that t (time?) isn't changing so I changed the code to this instead:
Code:
 if t < max_time { t++; }
However, the enemy just disappears (probably moving quickly off the screen) still. The max_time shown is 1.J, which is quite odd. There must be a mistake somewhere.
I don't know how your game works. You could have something there I cannot foresee so its not unreasonable for you to do some leg work. Best thing to do is to help you understand or consolidate your design of your game.
Some of the issues you had were 10px, variable names, lack of enemy visibility and miscellaneous problems.

10px/s^2 means 10 pixels per second per second. Acceleration is change in velocity. 10 pixels per second is the velocity, the second per second part means every second your velocity increases by 10px per second. You want to simulate the realistic pull of Earth's gravity which is 10 metres per second^2. You have to work out what 1 metre in pixels is for your game which an easy way to estimate is to get the height of your enemy and use about half its sprite_height. Thats why I put 10*x, x is a constant of how many pixels in your in game metre.

Im pretty sure I use lower cased variables in the code section of my post.

Visibility may be because you don't use object states to divide and organise your animation state or your jumping state. Only use the code in the animation stage of your game, perhaps use a script. Or just could be an error or mistake on my end since I realised you should draw your sprite at x,y-z not x,y+z.

I think its best if you used medians for your number limits. median(x1,x2,x3...) takes the middle number so if time is above the max, the median wouldn't be your time.

If you want, send me your code and I'll patch it up. I think kinematics is too advanced for you to understand, literally physics and calculus. Perhaps the first option is better which I think GMWolf has experience with either in maths or just game design.
 
Yeah, I never took physics and calculus classes and nearly flunked my math courses, so this is almost like another language to me.

You accidentally uppercase the words 'Distance' and 'Angle', which gave me an error at first. No big deal though.

Right now I'm just trying to test the code using a separate object and just setting the jump variables when I press control button:

Create Event
Code:
jump = false; // Boolean to test jumping

target_x = noone;
target_y = noone

distance = noone;
gravity = 0; // x is the amount of pixels that make up an in-game meter. -- Setting to 0 by default for now
angle = 60; // anything between 1 and 89. You cant throw a ball at 0 or 90 degrees.
velocity = noone;
t = 0;
z = 0;
max_time = noone;
max_z = noone;
Step Event
Code:
if jump == true
{
    move_towards_point( target_x, target_y, dcos(angle));
    t = median(0,t+1, max_time )
    z = -0.5 * gravity * t + velocity * t + distance
}
else
{
    gravity = 0;
}
User Event 0 ( Called when control key is pressed)
Code:
/// Set jumping variables
target_x = 340
target_y = 180

distance = point_distance( x, y, target_x, target_y );
gravity = 0.04; // x is the amount of pixels that make up an in-game meter. -- Setting to 0 by default for now
angle = 60; // anything between 1 and 89. You cant throw a ball at 0 or 90 degrees.
velocity = sqrt((distance*gravity)/sin(2*angle));
t = 0;
z = 0;
max_time = distance / (velocity*dcos( angle ));
max_z = velocity^2 / ( 2 * gravity )

// Will likely change state or set boolean to trigger step event jumping code
jump = true;
Draw Event
Code:
draw_sprite_ext( sprite_index, 0, x, y + z, 1, 1, 0, c_white, 1);
Something I did notice after testing it again, if I keep pressing the control key the object eventually shows up, but it definitely isn't behaving the way it should. It eventually appears at the bottom the screen and moves a bit until it is suddenly on the tile from what I can tell.

You have to work out what 1 metre in pixels is for your game which an easy way to estimate is to get the height of your enemy and use about half its sprite_height. Thats why I put 10*x, x is a constant of how many pixels in your in game metre.
Oh okay, so since my sprite's height is about 60, x would be 30, which would give me a gravity of 300?
 
Last edited:
N

nickvm98

Guest
Try this:
//Create
target_x=tar_x //you need to put this in
target_y=tar_y //and this
distance=point_distance(x,y,target_x,target_y);
gravity=10*sprite_get_height(spr_enemy)/2; //x is the amount of pixels that make up an in-game meter.
angle=60 //anything between 1 and 89. You cant throw a ball at 0 or 90 degrees.
velocity=sqrt((distance*gravity)/dsin(2*angle)); //check that all trig functions or dsin or dcos.
time = distance / (velocity*dcos( angle ));
t=0;
z=0;
jump=0;

//Step
if keyboard_pressed_check(vk_space) {jump=1}
if x!=target_x && y!=target_y && jump=1 {move_towards_point(target_x,target_y,velocity*dcos(angle))};
t=median(0,t+jump,time);
if t=time {t=0 jump=0};
z=-0.5*gravity*t+(velocity*dsin(angle))*t

//Draw
draw_sprite_ext( sprite_index, 0, floor(x), floor(y-z), 1, 1, 0, c_white, 1);
 
Last edited by a moderator:

GMWolf

aka fel666
Let me start by saying I suck at math. lol so from what I'm understanding in my create event I'd have the following variables:

Code:
hspd = 6; // Horizontal Speed
t = ?; // Time
grav = 0.04; // Gravity
zspeed = height / time - 0.5 * power ( grav, 2 ); // z speed
height = zspeed * time + 0.5 * grav * power( time, 2 ); // Jump height
Not sure what I should set time to, guessing I'd divide both sides of the original equation by t...?

Really confused with a lot of this. Never was any good with math in school.
Almost there.
First of all, you need to calculate t.
You do that by ignoring the z axis because that makes it easy.
It's just horizontal distance / horizontal speed. (Here horizontal means x,y. Not just X).

Then you choose a jump height. That's artistic preference.

Now you can calculate your zspeed using the equations.

So now you have your initial z speed and gravity. Use that like you use X and Y.
 
Try this:
//Create
target_x=tar_x //you need to put this in
target_y=tar_y //and this
distance=point_distance(x,y,target_x,target_y);
gravity=10*sprite_get_height(spr_enemy)/2; //x is the amount of pixels that make up an in-game meter.
angle=60 //anything between 1 and 89. You cant throw a ball at 0 or 90 degrees.
velocity=sqrt((distance*gravity)/dsin(2*angle)); //check that all trig functions or dsin or dcos.
time = distance / (velocity*dcos( angle ));
t=0;
z=0;
jump=0;

//Step
if keyboard_pressed_check(vk_space) {jump=1}
if x!=target_x && y!=target_y && jump=1 {move_towards_point(target_x,target_y,velocity*dcos(angle))};
t=median(0,t+jump,time);
if t=time {t=0 jump=0};
z=-0.5*gravity*t+(velocity*dsin(angle))*t

//Draw
draw_sprite_ext( sprite_index, 0, floor(x), floor(y-z), 1, 1, 0, c_white, 1);
Nope, still doesn't work right. Here is what's going on. After pressing the key multiple times it is visible again, but...
Ignore the enemy at the top on panel 4. The object you want to look at is on the bottom right on panel 23.
Almost there.
First of all, you need to calculate t.
You do that by ignoring the z axis because that makes it easy.
It's just horizontal distance / horizontal speed. (Here horizontal means x,y. Not just X).

Then you choose a jump height. That's artistic preference.

Now you can calculate your zspeed using the equations.

So now you have your initial z speed and gravity. Use that like you use X and Y.
So when you say horizontal distance, I should be using distance_to_point function like in nickvm98's solution? Since horizontal includes the y axis do I need a vspd variable too? You confused me by the way you explained it prior.

So...

time = distance_to_point( x, y, target_x, target_y ) / hspd

OR something like this:

time = distance_to_point( x, y, target_x, target_y ) / hspd - vspd

The jump height can be anything I want? It doesn't have to derive from the equation?
height = zspeed * time + 0.5 * grav * power( time, 2 );

I'd just set zspeed using this equation in the create event
zspeed = height / time - 0.5 * power ( grav, 2 );

Then I'd manipulate the grav and zspeed until I get the object to the coordinates I want?

Also, I don't need a separate z variable?

Sorry know I'm asking a lot of questions just trying to wrap my head around all of this.
 
S

seanm

Guest
Honestly I would say these responses are a bit much for what you are ultimately trying to achieve here.

Here's a really hacky way of doing it that is much easier to think about.

Perform a normal platformer physics jump in the zaxis, and record how long it takes.
When the enemy jumps, move the player x/y based on how many frames the jump will take.

So lets say a jump takes 60 frames, and you need to move 100 pixels horizontally and 32 vertically.
All you'd need to do is say
Code:
//when you start your jump
zspd = -5;
hspd = 100/60; // xdistance/jumpTime
vspd = 32/60; // ydistance/jumpTime

This will result in a jump that always goes the same height, and always takes the same amount of time, but will move faster or slower in the x/y axis depending on how far away the jump target is.
 
Honestly I would say these responses are a bit much for what you are ultimately trying to achieve here.

Here's a really hacky way of doing it that is much easier to think about.

Perform a normal platformer physics jump in the zaxis, and record how long it takes.
When the enemy jumps, move the player x/y based on how many frames the jump will take.

So lets say a jump takes 60 frames, and you need to move 100 pixels horizontally and 32 vertically.
All you'd need to do is say
Code:
//when you start your jump
zspd = -5;
hspd = 100/60; // xdistance/jumpTime
vspd = 32/60; // ydistance/jumpTime

This will result in a jump that always goes the same height, and always takes the same amount of time, but will move faster or slower in the x/y axis depending on how far away the jump target is.
Okay, so I think I'm getting close now. I assume if I want to get the enemy to a certain set of coordinates I can use the distance_to_point function.

Create Event
Code:
jump_time = 60;
jump = false;

target_x = noone;
target_y = noone;
target_x_dir = 0 
target_y_dir = 0;

z = 0; // z coordinate (0 by default... Meaning on the ground or not moving upward)
zgrav = .5; // Z-Gravity Amount (Default is .5)

zspd = 0 // Used to apply Z-Gravity to object or cause the object to be moved off the ground.
hspd = 0;
vspd = 0
Step Event
Code:
/// z Gravity + Jump

zspd += zgrav; // Implement constant z-gravity that pushes object downwards
z += zspd; // Increase z coordinate downward

// Prevent z from exceeding 0, which will prevent the object from constantly falling.
if ( z > 0 )
{
    z = 0;
    zspd = 0;
}



if jump == true
{ 
    x += target_x_dir * hspd;
    y += target_y_dir * vspd;
}
else
{
    zspd = 0;
}
User Event 0 (for testing purposes triggered when I press control key)
Code:
// Set the target coordinates that the enemy must land on
    target_x = 340; 
    target_y = 180;
// Next, set the target direction variable based on the enemy's current x and y.
   if x < target_x { target_x_dir = -1; } else { target_x_dir = 1; }
   if y < target_y { target_y_dir = -1; } else { target_y_dir = 1; }
// Set the hspd, vspd, and zspd when the enemy is getting ready to jump
    zspd = -10;
    hspd = distance_to_point( target_x, y ) / jump_time
    vspd = distance_to_point( x, target_y ) / jump_time 
// Tell the game that the enemy is jumping
    jump = true;
Draw Event
Code:
draw_sprite_ext( sprite_index, 0, x, y + z, 1, 1, 0, c_white, 1);

draw_text( 40, 80, "x: " + string( x ) + "      y: " + string( y ) + "#zgrav: " + string( zgrav ) + "     z: " + string( z ) +
"#zspd: " + string( zspd ) + "    hspd: " + string( hspd ) + "#vspd: " + string( vspd ))
As you can see, the enemy is finally moving in arc, but fails to move to the correct location (panel 20), which is the other direction.
 
Figured out why the enemy was moving in the wrong direction had to change the direction code to this:
Code:
// Next, set the target direction variable based on the enemy's current x and y.
   if target_x < x { target_x_dir = -1; } else { target_x_dir = 1; }
   if target_y < y { target_y_dir = -1; } else { target_y_dir = 1; }
Now, the problem I face is getting the enemy to stop at the target coordinates. I tried adjusted the jump_time to 40 to get the correct jump time for testing in this instance.

Looked at the code last night... Not experienced using lendir_x and lendir_y. Looked at some youtube videos this morning I understand a bit better, but I'm not entirely sure how to implement this so that it works within my game. I will need the enemy to land on some exact coordinates and sometimes the enemy might need to move to a panel above or below it, so I'm not sure about using this method.
 
S

seanm

Guest
Your code looks good except for one thing, you shouldn't be using distance_to_point. I don't want to explain if you don't know trig, but basically, you want the x and y components of that distance.

Do this instead.
Code:
hspd = (targetX - x) / jumpTime;
vspd = (targetY - y) / jumpTime;

To stop the jump at the end of the arc, you could do a lot of things. Here's one option

Code:
if isJumping and x == targetX and y == targetY
{
    isJumping = false;
    hspd = 0;
    vspd = 0;
    zspd = 0;
}
 
Had to change the code slightly because the enemy was moving horizontally in the wrong direction:
Code:
    hspd = abs( target_x - x ) / jump_time;
    vspd = abs( target_y - y ) / jump_time;
Unfortunately, the code to stop the object from moving isn't working. Guessing because the code is checking for exactly the target values when my enemy's x/y includes a decimal value.
 
S

seanm

Guest
Oh yeah, right.
If you know the jump time then use that instead.

Code:
//activate the jump 
{
isJumping = true;
hspd = abs( target_x - x ) / jump_time;
vspd = abs( target_y - y ) / jump_time;
zspd = ...
jumpAlarm = jump_time
}

if isJumping
{
jumpAlarm --;
}

if jumpAlarm == 0
{
hspd = 0;
vspd = 0;
zspd = 0;
isJumping = false;
}
 
Okay, finally did it. Thanks, a lot! One last thing, how do I set the depth now given that z is being used?
Currently using this script to set the depth for all my objects
Code:
if place_meeting( x, y, obj_tile)
{
    IP = instance_place( x, y, obj_tile);
    depth = (IP.y * -1) - argument0;
}
Do I just do this?
Code:
  depth = (IP.y + z * -1) - argument0;
 
S

seanm

Guest
That's what I would try first

but you might have to do this.
Code:
   depth = ((IP.y + z) * -1) - argument0;
 
S

seanm

Guest
Where are the origin points of your objects located? Are they all on middle bottom?

If they aren't then you're going to want to use bbox_bottom instead of y.

see what happens if you get rid of z from the depth as well.
Code:
depth = -IP.bbox_bottom - argument0;
 
The jumping enemy's origin wasn't centered to the middle bottom like the other enemy. As soon as I centered it, the entire object could be seen once it landed, but it still looked like it was behind the enemy at the top.
z jump depth correction.png
Using the code you suggested, however, fixed it so the jumping enemy appears over-top the other enemy while jumping and the two objects happen to overlap.

Thanks, so much for all your help. I'll be sure to add your name in the game credits.
 
Top