• 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 Wait until mouse_click

Dagoba

Member
Hello, long time no see!
I am currently programming a Texas Hold'Em online game, and I am encountering a problem.

There are turns for each player on the table, and it's running by for loop for each player.
I need to make a method that waits until the player has clicked either call, check, fold, raise or all-in button.
It is made like this:
Code:
for (var i = 0; i < tablecount; i++) {
    /* Some code here that already works
    *
    *
    *
    *
    */
    //Now the method to check if player has clicked a button
    if (!turn_used) {
        if (position_meeting(mouse_x, mouse_y, obj_btn_call) && mouse_check_button_pressed(mb_left)) {
            turn_used = true;
        }
        if (position_meeting(mouse_x, mouse_y, obj_btn_fold) && mouse_check_button_pressed(mb_left)) {
            turn_used = true;
        }
        // etc etc...
    }
    if (i == tablecount) {
        alarm[0] = 30; // Now draw the cards to the table (3)
    }
    
}
I know, that I should use while(!turn_used), but everytime I use while loop in Game Maker, the game will freeze due to infinite loop.
Now the code doesn't work because it checks if player has clicked a button, it has not because the loop goes so fast, that it starts with another players turn.

Now how can I "stop" this for loop until the player has ran out of time (30 sec) OR has clicked any button?
Is there any way to make some kind of while loop without causing a freezing?

Thanks in advance!
 
Short answer: you can't. Why does it have to be in a loop? The code doesn't seem to use anything relevant to it. Maybe knowing the working code would help form a solution.
 

Dagoba

Member
Because it needs to check for every object (the seat object) where a player can sit on and start playing.
Also it needs to check if the seat object is joined, has the player folded and all kind of variables inside of it, if the seat object is empty (no players sitting on it), then skip it.
Why would I not use for loop for this?

And you really do not need my full code, because the part I commented is full of local variables declaring like small blinds and stuff, that is not relevant to the problem I am having...
 
The code doesn't use the for loop in any way, so you're just polling the mouse and checking for collisions several times per frame for no discernible purpose. The reason I asked for the working code is because I wanted to see this code in relation to that in order to find a proper solution that doesn't break what you currently have that is working. Right now, all I can recommend is that you move the code just outside the for loop.
 

Dagoba

Member
I do need the for loop, to check if table(i) is done this and that.
Fine, I post the entire script:
Code:
var tablecount = instance_number(obj_sit_in);
for (var i = 0; i < tablecount; i++)
{
    with(obj_sit_in)
    {
        if (seat_number == i && !my_turn && !turn_used && joined && !folded)
        {
            my_turn = true;
            show_message("Player " + string(i+1) + "'s turn.");
            var sb = obj_controller.small_blind;
            var bb = sb * 2;
            var lc, lc_amount; // Lower cash than the small blind, lc_amount = remaining cash
            var betID; // This players bet ID
            if (seat_cash >= sb) seat_cash -= sb; // Fee the small blind
            else
            {
                lc = true;
                lc_amount = seat_cash;
                seat_cash = 0;
            }
            with(par_btn_turn)active = true;

            if (!turn_used)
            {
                if (position_meeting(mouse_x, mouse_y, obj_btn_call) && mouse_check_button_pressed(mb_left))
                {
                    // Check if the player needs to call or check and then do the method
                    turn_used = true;
                }
                if (position_meeting(mouse_x, mouse_y, obj_btn_fold) && mouse_check_button_pressed(mb_left))
                {
                    folded = true;
                    turn_used = true;
                }
                if (position_meeting(mouse_x, mouse_y, obj_btn_bet) && mouse_check_button_pressed(mb_left))
                {
                    betId = 0;//read value from the bet string
                    if (global.bet < betID) global.bet = betID;
                    else seat_cash = 0;
                    turn_used = true;
                }
                if (position_meeting(mouse_x, mouse_y, obj_btn_allin) && mouse_check_button_pressed(mb_left))
                {
                    betID = seat_cash;
                    
                    // Checks if there is a higher bet already
                    if (global.bet < betID) global.bet = betID;
                    else seat_cash = 0;
                    
                    turn_used = true;
                }
            }
        }
    }
    // After the loop, draw the cards
    // ALSO ADD IF EVERYBODY HAS RAISED/CHECKED
    if (i == tablecount-1)
    {
        i++;
        show_message("Soon alarm...");
        total_middle_bet += round_middle_bet;
        round_middle_bet = 0;
        alarm[1] = 30;  // Draw the cards to the table
    }
}
 

TheouAegis

Member
obviously you do not want to freeze your game, so stop using loops. allow the program to run over multiple steps and wait until all players have provided their input. You do not want to freeze the game at all, so get this out of your head. that is what battlerifle is basically saying, that your loop does absolutely nothing pertinent to progress your code. Use a variable to keep track of what the program should be doing each step.
 
Thanks for the code. It actually makes a huge difference to know that the code sits inside a with statement. That's where all my confusion came from.

However, it still leaves me at the same conclusion. The for loop isn't necessary as you don't need to track every player every frame. At a glance, you could remove the for statement and use a more appropriately named variable to replace i which states whose turn it currently is (which also replaces the my_turn variable) and it will accomplish the same feat without needing to worry about stopping loops.

Also, you can optimize the mouse bits by checking for the button only once.

if (mouse pressed)
{
if (here)
else if (there)
}
 

Dagoba

Member
if (mouse pressed)
{
if (here)
else if (there)
}
This is true, would be faster and more efficient code. Thanks for the tip! :)

However I am quite unsure how to do this, so just a variable like seatID that starts from number 0, after the player has done their turn, the seatID would increase by one.
But then I should use something like "do this method until seatID == instance_number(obj_sit_in)", but doesn't that need some kind of loop or should I just check it at the beginning of the code that if (seatID < instance_number(obj_sit_in)) ?
 
You don't need any loops at all. Think of it this way: your current logic dictates that the dealer on every play asks every player if it's their turn, and whoever says yes is allowed to do something. What logic you need is that the dealer simply asks whose turn it is, and the applicable person speaks up and performs an action. If you think about it, using the with statement and the for loop meant that if you had five players, there would be 25 checks for whose turn it was since the with statement would check all five players, and the for statement would make this action repeat another five times.

Your very first if statement checks to see if the seat number is equal to i. Instead of using a loop and scanning through all of the players, the with statement will already take care of that, so all you need to do is check if the seat number is equal to the current turn set by the control object calling this code, which, as you have already pointed out, would simply be a variable that would increase by one. Once this variable count exceeds the number of players minus 1, it loops back over; you could use mod to keep the calculation simple. This also removes the need for the my_turn and turn_used variables since those were already redundant anyway.

The only thing left is dealing with a player who has folded, otherwise the game will soft-lock. You could structure your code as such:
Code:
if (seat_number == current_turn)
{
    if (!folded)
    {
        // stuff
    }
    else
        current_turn = (current_turn + 1) mod instance_number(obj_sit_in);
}

I'm not too familiar with poker, so I can't speak on the "joined" variable.
 

Dagoba

Member
Yeah, will try to make something like that.
Thank you for the advices, and the "joined" variable means, if a player has joined on the table (aka. sitting on the seat). The seats can be empty as well, so skip those that are.
 
S

SkeetSkeetNinja

Guest
mouse_check_button_pressed(mb_left)
the above function its self is the problem its a one off function.
from: http://docs.yoyogames.com/source/dadiospice/002_reference/mouse, keyboard and other controls/mouse input/mouse_check_button_pressed.html
This function will only be triggered once for any mouse button when it is first pressed and to trigger it again the button will need to have been released and pressed again.

edit: try this approach instead:
Code:
   //Now the method to check if player has clicked a button
   if (!turn_used && mouse_check_button_pressed(mb_left)) {
       if (position_meeting(mouse_x, mouse_y, obj_btn_call)) {
           turn_used = true;
       }
       if (position_meeting(mouse_x, mouse_y, obj_btn_fold)) {
           turn_used = true;
       }
       // etc etc...
*Note: sorry if the link is not clickable (i'm under 5 posts)
 

Dagoba

Member
Yeah. There is now another problem; whenever I click any of those buttons, nothing happens.
I guess the code reaches its end because it checks if !turn_used, it goes through but then there's if mouse_pressed, it doesn't go through because the user doesn't press anything, and will end the code I guess.
It still doesn't "wait" until user input.

Could this be achieved by another method, like if (mouse_pressed) { bla bla} else { doThisMethod(); } and in doThisMethod it goes back to my original script to check if the user has clicked.
Or any other suggestions?
 
My recommendation was to get rid of the turn_used variable because the seat check would already be handling that. I'm guessing there's some interference. Would you mind posting your updated code?
 

Dagoba

Member
Yeah, I should've deleted that.
Anyways here is the current code with !turn_used, but gonna remove it afterwards:
Code:
var tablecount = instance_number(obj_sit_in);
if (obj_controller.current_turn != tablecount)
{
    with(obj_sit_in)
    {
        if (seat_number == obj_controller.current_turn && joined && !folded)
        {
            var sb = obj_controller.small_blind;
            var bb = sb * 2;
            var lc, lc_amount; // Lower cash than the small blind, lc_amount = remaining cash
            var betID; // This players bet ID
            if (seat_cash >= sb) seat_cash -= sb; // Fee the small blind
            else
            {
                lc = true;
                lc_amount = seat_cash;
                seat_cash = 0;
            }
            with(par_btn_turn)active = true;

            if (!turn_used && mouse_check_button_pressed(mb_left))
            {
                if (place_meeting(mouse_x, mouse_y, obj_btn_call))
                {
                    // Check if the player needs to call or check and then do the method
                    obj_controller.current_turn++;
                }
                if (position_meeting(mouse_x, mouse_y, obj_btn_fold))
                {
                    folded = true;
                    obj_controller.current_turn++;
                }
                if (position_meeting(mouse_x, mouse_y, obj_btn_bet))
                {
                    betId = 0;//read value from the bet string
                    if (global.bet < betID) global.bet = betID;
                    else seat_cash = 0;
                    obj_controller.current_turn++;
                }
                if (position_meeting(mouse_x, mouse_y, obj_btn_allin))
                {
                    betID = seat_cash;
                    round_middle_bet += betID;
                        
                    // Checks if there is a higher bet already
                    if (global.bet < betID) global.bet = betID;
                    else seat_cash = 0;
                        
                    obj_controller.current_turn++;
                }
            }
        }
    }
}
// After the loop, draw the cards
// ALSO ADD IF EVERYBODY HAS RAISED/CHECKED
if (obj_controller.current_turn == tablecount-1)
{
    total_middle_bet += round_middle_bet;
    round_middle_bet = 0;
    alarm[1] = 30;  // Draw the cards to the table
}
 
Keep in mind that we're no longer in loop territory, so there's no more waiting for things to happen before other things can execute. Each frame, both major if statements are running, checking all seats to see whose turn it is before doing anything, rather than just waiting on the active player that's next.

- Change the second current turn check (after everything) into a mere else statement. If you have 5 players, once it's player 5's turn, the second code will run (and will run continually) since his turn will be 4 and tablecount-1 is also 4.
- Consider my approach to handling folded/joined turns. You don't have anything to move onto the next player, and my guess is this soft lock is the brokenness you're experiencing. For example, if player 1 just performed an action, player 2 will be next but since he has already folded and player 3 isn't even at the table, the code will not move on from player 1 to 4, it will still wait for player 2 to do something.
 
Top