Legacy GM How do I create a follower?

  • Thread starter fawfulthegreat64
  • Start date
F

fawfulthegreat64

Guest
So I was able to finally get a working player sprite to move in 8 directions and look good. Now I need my Luigi (follower, since this is a Mario & Luigi style game). Is there an easy way to make an object follow you everywhere and also face the correct direction? I'm looking to have three followers that each follow behind the next. Like Mario & Luigi: Paper Jam only with Paper Luigi as well.

On another note, while Mario moves smoothly in 8 directions I can't get him to stop and face a diagonal direction. He can walk in those directions but always turns to face up, down, left or right when stopping. I'm sure there's some code I can use to fix this, but I do not know it.

Here's a video of what I have now.
 
J

jackhigh24

Guest
looks great but this should be in programming ask a mod or admin to move it, and pop your code on for your movement and directions as you must be setting it to face forward when nothing is pressed, edit ther is also a game maker tutorial built into game maker that shows how to have a few followers linked up, cant remember what its called though.
 
F

fawfulthegreat64

Guest
The only place in the manual I can find is confusing.

This is the code I am using for eight directional movement:

Code:
if (keyboard_check(vk_up) && keyboard_check(vk_right))
{x+=2; y-=2; sprite_index=mario_run_upright;}

else if (keyboard_check(vk_up) && keyboard_check(vk_left))
{x-=2; y-=2; sprite_index=mario_run_upleft;}

else if (keyboard_check(vk_down) && keyboard_check(vk_right))
{x+=2; y+=2; sprite_index=mario_run_downright;}

else if (keyboard_check(vk_down) && keyboard_check(vk_left))
{x-=2; y+=2; sprite_index=mario_run_downleft;}

else if keyboard_check(vk_up)
{x+=0; y-=4; sprite_index=mario_run_back;}

else if keyboard_check(vk_left)
{x-=4; y-=0; sprite_index=mario_run_left;}

else if keyboard_check(vk_down)
{x+=0; y+=4; sprite_index=mario_run_front;}

else if keyboard_check(vk_right)
{x+=4; y-=0; sprite_index=mario_run_right;}
Also thank you to the staff member who moved this.
 
C

Ctl-F

Guest
One way to accomplish this would be to implement a queue in the follower object. So that whenever the player moves, it logs the movement in the follower's queue so that the follower knows how to move next.
Here's what I mean:
Code:
/// Luigi
// [create event]
move_queue = ds_queue_create();

// [destroy event]
ds_queue_destroy(move_queue);

// [step event]
// get the next action in the queue
var next_move = -1;
if(ds_queue_size(move_queue) > 0){
    next_move = ds_queue_dequeue(move_queue);
}

if(next_move == 0){
    // move down left
    x += 2;
    y -= 2;
    sprite_index = spr_luigi_run_downleft;
    /*
        if luigi had a folower he'd add it to their queue here.
    */
}
else if(next_move == 1){
   // move down right
}
else if(next_move == 2){
   //move up left
}
else if(...){
    //...
}

/// Mario
// [create event]
follower = instance_create(relposX, relposY, obj_luigi);

// [step event]
if(keyboard_check(vk_down) && keyboard_check(vk_left)){
    x += 2;
    y -= 2;
    sprite_index = spr_mario_run_downleft;
    ds_queue_enqueue(follower.move_queue, 0);
}
What number corresponds to what movement is up to you, and you could even create an enumerator
Code:
enum movementCodes {
    UPLEFT = 0,
    UPRIGHT = 1,
    ...
}
Also note, that the method is not perfect. If you get a follower stuck behind a wall, or they get knocked back by an enemy they have currently have no way to move back the to desired position. Also: you should check if each follower instance exists before trying to add to it's queue, because if a follower dies and you try to access it's queue, you'll cause an error.

Do you get the idea?
 
F

fawfulthegreat64

Guest
Er, I tried that code and it said there was an error on one of the lines. If it needs to be modified I have no idea how beyond editing sprite names. My sprite names don't have spr_ at the beginning because that made the names overly long IMO. So Luigi's sprites are luigi_run_up, etc. Mario's "up" and "down" is replaced with "back" and "front" because I was being dumb when I made those and named them after what side of him is visible. But yeah I would need you to elaborate on how to edit that code since I know next to nothing about the coding language.
 
F

fawfulthegreat64

Guest
Code:
ERROR at line 30 pos 10: Unexpected symbol in expression.
On another note I am watching a GML coding tutorial atm. Hopefully I learn some stuff.
 
F

fawfulthegreat64

Guest
Yeah, but I changed the sprite names a bit to match what I had. It all looks like gibberish to me, I have no idea what any of it means. That's why I'm trying to watch tutorials to learn.
 
C

Ctl-F

Guest
okay, sorry, you weren't supposed to copy that directly.
when I triple commented
/// luigi
/// mario
I was saying that the following code was for that object
when I double commented with brackets
// [create event]
I was saying that the following code should go in that event in the current object.
Does that make sense?
 
F

fawfulthegreat64

Guest
I think I get it, but I don't know what numbers correspond to what directions (in your example 0 was down left, 1 was down right and 2 was up left. What about the rest of them?)
 
C

Ctl-F

Guest
that's up to you, all that matters is that when you log a number for a direction, that number is used for the same direction in the follower.
 
F

fawfulthegreat64

Guest
So I thought I had it all down correctly, when...

Code:
___________________________________________
############################################################################################
FATAL ERROR in
action number 1
of Create Event
for object obj_mario:

Variable obj_mario.relposY(100002, -2147483648) not set before reading it.
at gml_Object_obj_mario_CreateEvent_1 (line 1) - follower = instance_create(relposX, relposY, obj_luigi)
############################################################################################
 
C

Ctl-F

Guest
relposX and relposY are just the offset that you want the follower to be from the player.
So with this setup you wouldn't place a luigi object in the room, the mario object would create it and it would be created at the position.
So if you wanted luigi to be 16 pixels lower than mario, you'd write
Code:
follower = instance_create(x, y+16, obj_luigi);
 

Yal

šŸ§ *penguin noises*
GMC Elder
I'd use a list instead of a queue, and log the last 30 X/Y positions in it. (Or actually use three lists, one for X and one for Y and one for the facing direction). The player put the most recent position at the front of the list each step (but only the steps the player has moved), then deletes the last entry of the lists. Follower 1 moves to the 10th position in the list, follower 2 to the 20th position, follower 3 to the 30th. I think this approach (teleport to the appropriate position) is a lot easier than moving directly, there's no risk of 'detatch' issues if something messes with the movement.
 
F

fawfulthegreat64

Guest
Now Luigi runs in the opposite direction of Mario and is never idle (he is always doing the run animation even when stationary)
I'd use a list instead of a queue, and log the last 30 X/Y positions in it. (Or actually use three lists, one for X and one for Y and one for the facing direction). The player put the most recent position at the front of the list each step (but only the steps the player has moved), then deletes the last entry of the lists. Follower 1 moves to the 10th position in the list, follower 2 to the 20th position, follower 3 to the 30th. I think this approach (teleport to the appropriate position) is a lot easier than moving directly, there's no risk of 'detatch' issues if something messes with the movement.
Can you explain how to code this?
 
C

Ctl-F

Guest
@Yal: I completely agree, that is a better solution than direct movement.

@fawfulthegreat64
An example of how to implement it:
Code:
/// mario
// [create event]

// create the lists to keep track of movement
historyX = ds_list_create();
historyY = ds_list_create();
historyDir = ds_list_create();
currentDir = "still";

follower = instance_create(x, y+16, obj_luigi);
follower.dir = "still";

// [destroy event]
// clean up clean up...
ds_list_destroy(historyX);
ds_list_destroy(historyY);
ds_list_destroy(historyDir);

// [step event]
var hasmoved = false;

if(keyboard_check(vk_left) && keyboard_check(vk_up)){
    hasmoved = true;

    ds_list_insert(historyX, 0, x);
    ds_list_insert(historyY, 0, y);
    ds_list_insert(historyDir, 0, currentDir);

    if(ds_list_size(historyX) >= 30){ // since they should be the same size, we only check one
        ds_list_delete(historyX, ds_list_size(historyX)-1);
        ds_list_delete(historyY, ds_list_size(historyY)-1);
        ds_list_delete(historyDir, ds_list_size(historyDir)-1);
    }

    x -= 2;
    y -= 2;
    sprite_index = [insert sprite here];
    currentDir = "upleft";
}
else if(keyboard_check(vk_right) && keyboard_check(vk_up)){
    hasmoved = true;

    ds_list_insert(historyX, 0, x);
    ds_list_insert(historyY, 0, y);
    ds_list_insert(historyDir, 0, currentDir);

    if(ds_list_size(historyX) >= 30){
        ds_list_delete(historyX, ds_list_size(historyX)-1);
        ds_list_delete(historyY, ds_list_size(historyY)-1);
        ds_list_delete(historyDir, ds_list_size(historyDir)-1);
    }

    x += 2;
    y -= 2;
    sprite_index = [insert sprite here];
    currentDir = "upright";

}
else [insert the rest of the movement directions here

if(hasmoved && ds_list_size(historyX) > 10){
    follower.x = historyX[| 10];
    follower.y = historyY[| 10];
    follower.dir = historyDir[| 10];
}

/// luigi
// [create event]
change_timer = 0; // timer to measure pauses

// [step event]
if(dir == "still"){
    [set the sprite_index to the standing still sprite based on the current directional sprite]
}
else if(dir == "upleft"){
    [set the sprite_index to the up left running animation
}

if(x == xprevious && y == yprevious){
    // no change has occured, we need to start counting;
    if(change_timer++ > 10){
        // it has been 10 steps since we have changed positions at all, we should be standing still now
        dir = "still";
    }
}
/// NOTE: I used strings for the dir variable to make it more readable to beginners.
 
F

fawfulthegreat64

Guest
I have most of it down now, but one part that confuses me is the part where it says [set the sprite_index to the standing still sprite based on the current directional sprite]

How do I set that so it always uses the correct sprite? I can't just use a random idle sprite can I?
 
F

fawfulthegreat64

Guest
I set it to luigi_idle_right and now Luigi always faces right when stopped.

Also when Luigi is supposed to be overlapping Mario (such as when they are walking up) Mario overlaps him instead and looks like he is walking on his head.
 
C

Ctl-F

Guest
you can fix the depth issue adding depth = -y; to your step event.
and you are supposed to do some if/else statements for the stop event.
eg.
Code:
if(sprite_index == spr_luigi_walk_upright) {
    sprite_index = spr_luigi_stand_upright;
}
else if(sprite_index == spr_luigi_walk_upleft){
    sprite_index = spr_luigi_stand_upleft;
}
else if(...){
...
}
 
F

fawfulthegreat64

Guest
Thanks, that worked. But Mario still can't face diagonally idle. Also my next step is to add two more followers, Paper Mario and Paper Luigi, but they are paper so they have turning animations that resemble flipping. I could make additional frames to emulate this in Photoshop. First of all is it the same thing to add a follower to Luigi? Where would that code go (I already have step code for Luigi)
 
F

fawfulthegreat64

Guest
So here is what I have for Mario's step code.

It throws an error:
Variable obj_mario.dir(100007, -2147483648) not set before reading it.


Code:
var hasmoved = false;

if(keyboard_check(vk_left) && keyboard_check(vk_up)){
    hasmoved = true;

    ds_list_insert(historyX, 0, x);
    ds_list_insert(historyY, 0, y);
    ds_list_insert(historyDir, 0, currentDir);

    if(ds_list_size(historyX) >= 30){ // since they should be the same size, we only check one
        ds_list_delete(historyX, ds_list_size(historyX)-1);
        ds_list_delete(historyY, ds_list_size(historyY)-1);
        ds_list_delete(historyDir, ds_list_size(historyDir)-1);
    }

    x -= 2;
    y -= 2;
    sprite_index = mario_run_upleft;
    currentDir = "upleft";
}
else if(keyboard_check(vk_right) && keyboard_check(vk_up)){
    hasmoved = true;

    ds_list_insert(historyX, 0, x);
    ds_list_insert(historyY, 0, y);
    ds_list_insert(historyDir, 0, currentDir);

    if(ds_list_size(historyX) >= 30){
        ds_list_delete(historyX, ds_list_size(historyX)-1);
        ds_list_delete(historyY, ds_list_size(historyY)-1);
        ds_list_delete(historyDir, ds_list_size(historyDir)-1);
    }

    x += 2;
    y -= 2;
    sprite_index = mario_run_upright;
    currentDir = "upright";

}
else if(keyboard_check(vk_right) && keyboard_check(vk_down)){
    hasmoved = true;

    ds_list_insert(historyX, 0, x);
    ds_list_insert(historyY, 0, y);
    ds_list_insert(historyDir, 0, currentDir);

    if(ds_list_size(historyX) >= 30){
        ds_list_delete(historyX, ds_list_size(historyX)-1);
        ds_list_delete(historyY, ds_list_size(historyY)-1);
        ds_list_delete(historyDir, ds_list_size(historyDir)-1);
    }

    x += 2;
    y += 2;
    sprite_index = mario_run_downright;
    currentDir = "downright";
   
}
else if (keyboard_check(vk_left) && keyboard_check (vk_down)){
    hasmoved = true;
   
    ds_list_insert (historyX, 0, x);
    ds_list_insert (historyY, 0, y);
    ds_list_insert(historyDir, 0, currentDir);
   
    if (ds_list_size(historyX) >= 30){
        ds_list_delete (historyX, ds_list_size (historyX)-1);
        ds_list_delete(historyY, ds_list_size(historyY)-1);
        ds_list_delete(historyDir, ds_list_size(historyDir)-1);
    }
    x -= 2;
    y += 2;
    sprite_index = mario_run_downleft;
    currentDir = "downleft";
   
}
else if (keyboard_check(vk_left)){
    hasmoved = true;
   
    ds_list_insert (historyX, 0, x);
    ds_list_insert (historyY, 0, y);
    ds_list_insert(historyDir, 0, currentDir);
   
    if (ds_list_size(historyX) >= 30){
        ds_list_delete (historyX, ds_list_size (historyX)-1);
        ds_list_delete(historyY, ds_list_size(historyY)-1);
        ds_list_delete(historyDir, ds_list_size(historyDir)-1);
    }
    x -= 4;
    y -= 0;
    sprite_index = mario_run_left;
    currentDir = "left";
   
}else if (keyboard_check(vk_right)){
    hasmoved = true;
   
    ds_list_insert (historyX, 0, x);
    ds_list_insert (historyY, 0, y);
    ds_list_insert(historyDir, 0, currentDir);
   
    if (ds_list_size(historyX) >= 30){
        ds_list_delete (historyX, ds_list_size (historyX)-1);
        ds_list_delete(historyY, ds_list_size(historyY)-1);
        ds_list_delete(historyDir, ds_list_size(historyDir)-1);
    }
    x += 4;
    y += 0;
    sprite_index = mario_run_right;
    currentDir = "right";
   
}else if (keyboard_check(vk_down)){
    hasmoved = true;
   
    ds_list_insert (historyX, 0, x);
    ds_list_insert (historyY, 0, y);
    ds_list_insert(historyDir, 0, currentDir);
   
    if (ds_list_size(historyX) >= 30){
        ds_list_delete (historyX, ds_list_size (historyX)-1);
        ds_list_delete(historyY, ds_list_size(historyY)-1);
        ds_list_delete(historyDir, ds_list_size(historyDir)-1);
    }
    x += 0;
    y += 4;
    sprite_index = mario_run_front;
    currentDir = "down";
   
}else if (keyboard_check(vk_up)){
    hasmoved = true;
   
    ds_list_insert (historyX, 0, x);
    ds_list_insert (historyY, 0, y);
    ds_list_insert(historyDir, 0, currentDir);
   
    if (ds_list_size(historyX) >= 30){
        ds_list_delete (historyX, ds_list_size (historyX)-1);
        ds_list_delete(historyY, ds_list_size(historyY)-1);
        ds_list_delete(historyDir, ds_list_size(historyDir)-1);
    }
    x += 0;
    y -= 4;
    sprite_index = mario_run_back;
    currentDir = "up";
   
}
if(hasmoved && ds_list_size(historyX) > 10){
    follower.x = historyX[| 10];
    follower.y = historyY[| 10];
    follower.dir = historyDir[| 10];
    }
depth = -y;

if(dir == "still"){
if(sprite_index == mario_run_upright) {
    sprite_index = mario_idle_upright;
}
else if(sprite_index == mario_run_upleft){
    sprite_index = mario_idle_upleft;
}
else if(sprite_index == mario_run_downleft){
    sprite_index = mario_idle_downleft;
}
else if(sprite_index == mario_run_downright){
    sprite_index = mario_idle_downright;
}
else if(sprite_index == mario_run_left){
    sprite_index = mario_idle_left;
}
else if(sprite_index == mario_run_right){
    sprite_index = mario_idle_right;
}
else if(sprite_index == mario_run_back){
    sprite_index = mario_idle_back;
}
else if(sprite_index == mario_run_front){
    sprite_index = mario_idle_front;
}
}
 
C

Ctl-F

Guest
Did you remember to set dir to 'still' in the create event.
And yes, you can repeat this in luigi, or you can do it in mario. Whichever you understand better.
 
F

fawfulthegreat64

Guest
Code:
historyX = ds_list_create();
historyY = ds_list_create();
historyDir = ds_list_create();
currentDir = "still";
follower = instance_create(x, y+16, obj_luigi);
follower.dir = "still";
Is this correct?

Edit: I added dir = "still"; and now Mario moves around without running.
 
F

fawfulthegreat64

Guest
I've tried everything and I can't get Mario to both stop at a diagonal angle and actually have a running animation. What's the other variable besides "still" ? Maybe if I can set that to something else Mario will move properly.
 
F

fawfulthegreat64

Guest
Here's what I have for Mario in the step event:
Code:
var hasmoved = false;



if(keyboard_check(vk_left) && keyboard_check(vk_up)){
    hasmoved = true;

    ds_list_insert(historyX, 0, x);
    ds_list_insert(historyY, 0, y);
    ds_list_insert(historyDir, 0, currentDir);

    if(ds_list_size(historyX) >= 30){ // since they should be the same size, we only check one
        ds_list_delete(historyX, ds_list_size(historyX)-1);
        ds_list_delete(historyY, ds_list_size(historyY)-1);
        ds_list_delete(historyDir, ds_list_size(historyDir)-1);
    }

    x -= 2;
    y -= 2;
    sprite_index = mario_run_upleft;
    currentDir = "upleft";
}
else if(keyboard_check(vk_right) && keyboard_check(vk_up)){
    hasmoved = true;

    ds_list_insert(historyX, 0, x);
    ds_list_insert(historyY, 0, y);
    ds_list_insert(historyDir, 0, currentDir);

    if(ds_list_size(historyX) >= 30){
        ds_list_delete(historyX, ds_list_size(historyX)-1);
        ds_list_delete(historyY, ds_list_size(historyY)-1);
        ds_list_delete(historyDir, ds_list_size(historyDir)-1);
    }

    x += 2;
    y -= 2;
    sprite_index = mario_run_upright;
    currentDir = "upright";

}
else if(keyboard_check(vk_right) && keyboard_check(vk_down)){
    hasmoved = true;

    ds_list_insert(historyX, 0, x);
    ds_list_insert(historyY, 0, y);
    ds_list_insert(historyDir, 0, currentDir);

    if(ds_list_size(historyX) >= 30){
        ds_list_delete(historyX, ds_list_size(historyX)-1);
        ds_list_delete(historyY, ds_list_size(historyY)-1);
        ds_list_delete(historyDir, ds_list_size(historyDir)-1);
    }

    x += 2;
    y += 2;
    sprite_index = mario_run_downright;
    currentDir = "downright";
  
}
else if (keyboard_check(vk_left) && keyboard_check (vk_down)){
    hasmoved = true;
  
    ds_list_insert (historyX, 0, x);
    ds_list_insert (historyY, 0, y);
    ds_list_insert(historyDir, 0, currentDir);
  
    if (ds_list_size(historyX) >= 30){
        ds_list_delete (historyX, ds_list_size (historyX)-1);
        ds_list_delete(historyY, ds_list_size(historyY)-1);
        ds_list_delete(historyDir, ds_list_size(historyDir)-1);
    }
    x -= 2;
    y += 2;
    sprite_index = mario_run_downleft;
    currentDir = "downleft";
  
}
else if (keyboard_check(vk_left)){
    hasmoved = true;
  
    ds_list_insert (historyX, 0, x);
    ds_list_insert (historyY, 0, y);
    ds_list_insert(historyDir, 0, currentDir);
  
    if (ds_list_size(historyX) >= 30){
        ds_list_delete (historyX, ds_list_size (historyX)-1);
        ds_list_delete(historyY, ds_list_size(historyY)-1);
        ds_list_delete(historyDir, ds_list_size(historyDir)-1);
    }
    x -= 4;
    y -= 0;
    sprite_index = mario_run_left;
    currentDir = "left";
  
}else if (keyboard_check(vk_right)){
    hasmoved = true;
  
    ds_list_insert (historyX, 0, x);
    ds_list_insert (historyY, 0, y);
    ds_list_insert(historyDir, 0, currentDir);
  
    if (ds_list_size(historyX) >= 30){
        ds_list_delete (historyX, ds_list_size (historyX)-1);
        ds_list_delete(historyY, ds_list_size(historyY)-1);
        ds_list_delete(historyDir, ds_list_size(historyDir)-1);
    }
    x += 4;
    y += 0;
    sprite_index = mario_run_right;
    currentDir = "right";
  
}else if (keyboard_check(vk_down)){
    hasmoved = true;
  
    ds_list_insert (historyX, 0, x);
    ds_list_insert (historyY, 0, y);
    ds_list_insert(historyDir, 0, currentDir);
  
    if (ds_list_size(historyX) >= 30){
        ds_list_delete (historyX, ds_list_size (historyX)-1);
        ds_list_delete(historyY, ds_list_size(historyY)-1);
        ds_list_delete(historyDir, ds_list_size(historyDir)-1);
    }
    x += 0;
    y += 4;
    sprite_index = mario_run_front;
    currentDir = "down";
  
}else if (keyboard_check(vk_up)){
    hasmoved = true;
  
    ds_list_insert (historyX, 0, x);
    ds_list_insert (historyY, 0, y);
    ds_list_insert(historyDir, 0, currentDir);
  
    if (ds_list_size(historyX) >= 30){
        ds_list_delete (historyX, ds_list_size (historyX)-1);
        ds_list_delete(historyY, ds_list_size(historyY)-1);
        ds_list_delete(historyDir, ds_list_size(historyDir)-1);
    }
    x += 0;
    y -= 4;
    sprite_index = mario_run_back;
    currentDir = "up";
  
}
if(hasmoved && ds_list_size(historyX) > 10){
    follower.x = historyX[| 10];
    follower.y = historyY[| 10];
    follower.dir = historyDir[| 10];
    }
  
if(dir == "still"){
if(currentDir = "upright") {
    sprite_index = mario_idle_upright;
}
else if(currentDir = "upleft"){
    sprite_index = mario_idle_upleft;
}
else if(currentDir = "downleft"){
    sprite_index = mario_idle_downleft;
}
else if(currentDir = "downright"){
    sprite_index = mario_idle_downright;
}
else if(currentDir = "left"){
    sprite_index = mario_idle_left;
}
else if(currentDir = "right"){
    sprite_index = mario_idle_right;
}
else if(currentDir = "up"){
    sprite_index = mario_idle_back;
}
else if(currentDir = "down"){
    sprite_index = mario_idle_front;
}}
depth = -y;
He still floats instead of running.

Edit: I think I boiled it down to this code here:

Code:
if(dir == "still"){
if(currentDir = "upright") {
    sprite_index = mario_idle_upright;
}
else if(currentDir = "upleft"){
    sprite_index = mario_idle_upleft;
}
else if(currentDir = "downleft"){
    sprite_index = mario_idle_downleft;
}
else if(currentDir = "downright"){
    sprite_index = mario_idle_downright;
}
else if(currentDir = "left"){
    sprite_index = mario_idle_left;
}
else if(currentDir = "right"){
    sprite_index = mario_idle_right;
}
else if(currentDir = "up"){
    sprite_index = mario_idle_back;
}
else if(currentDir = "down"){
    sprite_index = mario_idle_front;
}}
Removing that allowed him to do the run animation again, but still can't face diagonally while idle.
 
Last edited by a moderator:
F

fawfulthegreat64

Guest
I need to find a way to make Mario stop the running animation but also face the direction his last animation was facing when he stops moving. I can't do this using drag and drop, can I?

Luigi is working perfectly, couldn't be better. But what is in his code that Mario doesn't have? I can't figure it out.
 
C

Ctl-F

Guest
Yes it's possible in drag'n'drop if that's what you are more comfortable with. PM me if you want me to step you through the drag'n'drop version. I'd rather teach you drag'n'drop and have you understand it then throw a bunch of code at you and tell you "good luck"
 
Top