Match objects with drawing lines (Bingo! Solved)

Started by Amir, Mon 06/06/2022 07:43:06

Previous topic - Next topic

Crimson Wizard

#20
Quote from: Amir on Thu 09/06/2022 11:27:40But I can't make this on_call. what am I doing wrong?
this is the reset button. All lines should be deleted.

Your on_call code works for me.

I may only suggest begin with checking if the button script is called, on_call is called, and the conditions work correctly. This may be tested for instance with "Display" command, or placing breakpoints in the editor and running game with F5.

Also:
- is the button clickable;
- is the button covered by any clickable guis.

Amir

Oh, I probably clicked wrong.

Code: ags

function gReset_OnClick(GUI *theGui, MouseButton button)
{
if (cBerny.Room == 19)  
CallRoomScript(1);
}
 


That was the gui, not the button  (laugh)

They get deleted but when you want to draw again, it gets drawed then disappears because we deleted spriteForAll.Delete(); . Something is probably missing in else if
Truly, truly, I say to you, blessed are those who play adventure games, for theirs is the kingdom of heaven.

Crimson Wizard

Quote from: Amir on Thu 09/06/2022 13:44:35
They get deleted but when you want to draw again, it gets drawed then disappears because we deleted spriteForAll.Delete(); . Something is probably missing in else if

Well, the solution is to not just delete spriteForAll when you click on button, but to clear it instead. Or delete it and recreate again.

Code: ags

function on_call (int Lines)
{
  if (Lines == 1)
  {
     if (spriteForLine) {
       spriteForLine.Delete();
     }

     DrawingSurface *surface = spriteForAll.GetDrawingSurface();
     surface.Clear();
     surface.Release();
  }
}


This will preserve the main sprite, but clear it with transparent color

Amir

Aaah I understood the principle now of these DrawingSurface. Thank u  :)
Truly, truly, I say to you, blessed are those who play adventure games, for theirs is the kingdom of heaven.

Amir

Part 2 of the puzzle that is killing me. check if all lines are correct. I'm trying to do somthing like that with global variables.
according to CW's code. (I use the picture in the first post as an example)

Code: ags

   if ((draw == false) && (ex == mouse.x && ey == mouse.y))
  {
  if ((Hotspot.GetAtScreenXY(sx, sy) == hotspot[1]) && (Hotspot.GetAtScreenXY(ex, ey) == hotspot[2]))
  AnchorBoat = true;
  
  else if ((Hotspot.GetAtScreenXY(sx, sy) == hotspot[3]) && (Hotspot.GetAtScreenXY(ex, ey) == hotspot[4]))
  ColaHotdog = true; 
  } 


Button OK

Code: ags

function Button58_OnClick(GUIControl *control, MouseButton button)
{
if ((AnchorBoat == true)  &&  (ColaHotdog == true) )
cBerny.Say("Bingo."); 

}


That dosn't work of course.
It occurred to me that this is for just one line, not multiple, am I right? how do I do this way for multiple lines? Does anyone have an idea.


Truly, truly, I say to you, blessed are those who play adventure games, for theirs is the kingdom of heaven.

Crimson Wizard

Quote from: Amir on Thu 09/06/2022 19:15:56
It occurred to me that this is for just one line, not multiple, am I right? how do I do this way for multiple lines? Does anyone have an idea.

There are two approaches, either:

1) Make a test when player finishes drawing line and save the test result.
2) Save final line coordinates (sx,sy,ex,ey) and test later (on button press?).

Since you have multiple lines you will have to make an array of final line coordinates or test results.

for a quick example:
Code: ags

struct Line {
    int sx,sy,ex,ey;
};

#define MAX_LINES 100
Line Lines[MAX_LINES];


Remember the line count in a integer variable, increase each time a line was finished, and reset to 0 when lines are cleared.

Then in the end, you may do a check:
Code: ags

    for (int i = 0; i < line_count; i++) {
        // test Lines[i] values:
        if (Hotspot.GetAtScreenXY(Lines[i].sx, Lines[i].sy) == ...) { // where line starts
        }
        if (Hotspot.GetAtScreenXY(Lines[i].ex, Lines[i].ey) == ...) { // where line ends
        }
    }

Amir

Ah right Pogwizd was talking about array, I totally forgot about that. I'll try that after dinner.

// where line starts and // where line ends = where the hotspots are? x and y of the hotspots? because I don't know where exactly the player will draw the line to the hotspot, up from the hotspot down, right or left.
Truly, truly, I say to you, blessed are those who play adventure games, for theirs is the kingdom of heaven.

Amir

Hi CW,

could you check please, because Berny doesn't say bingo. (Im trying first only for 2 lines, 4 hotspots)

In room_RepExec

Code: ags

 for (int i = 0; i < line_count; i++)   // that's for loop for each line the player will draw, right?
 {
        // test Lines[i] values:
        if (Hotspot.GetAtScreenXY(Lines[i].sx, Lines[i].sy) == hotspot[1]) { // where line starts
        }
        if (Hotspot.GetAtScreenXY(Lines[i].ex, Lines[i].ey) == hotspot[2]) { // where line ends
        }
        if (Hotspot.GetAtScreenXY(Lines[i].sx, Lines[i].sy) == hotspot[3]) { // where line starts
        }
        if (Hotspot.GetAtScreenXY(Lines[i].ex, Lines[i].ey) == hotspot[4]) { // where line ends
        }
  }


In Button

Code: ags

if ((Hotspot.GetAtScreenXY(sx, sy) == hotspot[1]) && (Hotspot.GetAtScreenXY(ex, ey) == hotspot[2])
&& (Hotspot.GetAtScreenXY(sx, sy) == hotspot[3]) && (Hotspot.GetAtScreenXY(ex, ey) == hotspot[4])
)

cBerny.SayBubble("Bingo.");


I tried also in Button with if (draw == false) but it doesn't work.

I have a second problem with on_call. I want to make 2 calls for Reset and OK button.

Code: ags

function on_call (int LinesReset, int LinesOK) 
{
  
if (LinesReset == 1)
{
     if (spriteForLine) {
       spriteForLine.Delete();
     }
 
     DrawingSurface *surface = spriteForAll.GetDrawingSurface();
     surface.Clear();
     surface.Release();
}

if (LinesOK == 2)
{
if ((Hotspot.GetAtScreenXY(sx, sy) == hotspot[1]) && (Hotspot.GetAtScreenXY(ex, ey) == hotspot[2]))
cBerny.SayBubble("Bingo.");
}
}


In global script

Code: ags

function Button59_OnClick(GUIControl *control, MouseButton button)
{
if (cBerny.Room == 19)  
CallRoomScript(1);
}

function Button58_OnClick(GUIControl *control, MouseButton button)
{ 
if (cBerny.Room == 19)  // OK button
CallRoomScript(2);
}


I'm getting an error message .... function on_call (expected 2, supplied 1). what am I doing wrong?  :-\
Truly, truly, I say to you, blessed are those who play adventure games, for theirs is the kingdom of heaven.

Crimson Wizard

#28
I think you misunderstood my examples. The "room_RepExec" code you posted does nothing, it's just "if" condition with nothing inside. My code was a placeholder that should be filled with something. For instance, you could remember which items are connected, similar to the code you posted yesterday:
https://www.adventuregamestudio.co.uk/forums/index.php?topic=60037.msg636646898#msg636646898

The code "In Button" makes more sense, but I don't know where did you get "sx,sy" etc variables from. The idea was to use the stored variables from Lines instead.

So, the code called from the button press could look like:
Code: ags

  // test all lines to see what do they connect
  for (int i = 0; i < line_count; i++)
 {
     if ((Hotspot.GetAtScreenXY(Lines[i].sx, Lines[i].sy) == hotspot[1]) && (Hotspot.GetAtScreenXY(Lines[i].ex, Lines[i].ey) == hotspot[2]))
          AnchorBoat = true;
     else if ((Hotspot.GetAtScreenXY((Lines[i].sx, Lines[i].sy) == hotspot[3]) && (Hotspot.GetAtScreenXY(Lines[i].ex, Lines[i].ey) == hotspot[4]))
          ColaHotdog = true;
     // and so on -- other variants here
  }


afterwards, when you found and remembered which combinations are correct, you could display results, for example:
Code: ags

  if (AnchorBoat && ColaHotdog)
    cBerny.SayBubble("Bingo.");




In regards to the on_call problem, the on_call can take only 1 argument, as shown in the manual. You should compare that 1 argument with all possible values.
Code: ags

function on_call (int event) 
{
  
  if (event == 1) // lines reset
  {
     // do something for lines reset
  }
  else if (event == 2) // lines ok
  {
     // do something for lines check
  }
}


If you don't like using simple numbers, and prefer some text instead, you may create a enum. For example you may put this in the global header:
Code: ags

enum LinePuzzleEvents {
    LinePuzz_Reset,
    LinePuzz_Check
};


And then just do
Code: ags

function on_call (int event) 
{
  if (event == LinePuzz_Reset) // lines reset
  {
     // do something for lines reset
  }
  else if (event == LinePuzz_Check) // lines ok
  {
     // do something for lines check
  }
}


And similarly when doing CallRoomScript:
Code: ags

CallRoomScript(LinePuzz_Reset); // and so on

Amir

#29
Oh did you mean with the boolean variables. Yes I got it totally wrong.

Quotebut I don't know where did you get "sx,sy" etc variables from.

lol

I don't know why I like it that way  ;-D
Code: ags

  if ((AnchorBoat == true) && (ColaHotdog == true))
    cBerny.SayBubble("Bingo.");



Thanks for the information. I'll try it.
Truly, truly, I say to you, blessed are those who play adventure games, for theirs is the kingdom of heaven.

Amir

#30
It doesn't work  :-\ Maybe something else is missing?

I'm trying with just one line and bool. AnchorBoat = true;

I don't think it makes any difference.
Code: ags

//hotspot[1].Enabled = false;  
//hotspot[2].Enabled = false; 
Truly, truly, I say to you, blessed are those who play adventure games, for theirs is the kingdom of heaven.

Crimson Wizard

Quote from: Amir on Fri 10/06/2022 11:39:42
It doesn't work  :-\ Maybe something else is missing?

I'm trying with just one line and bool. AnchorBoat = true;

Please post your current code?

Amir

Ok,

in room_RepExec (under the code of drawing)

Code: ags

for (int i = 0; i < line_count; i++)
 {
        // test Lines[i] values:
     if ((Hotspot.GetAtScreenXY(Lines[i].sx, Lines[i].sy) == hotspot[1]) && (Hotspot.GetAtScreenXY(Lines[i].ex, Lines[i].ey) == hotspot[2]))
          VBaer = true;
     //else if ((Hotspot.GetAtScreenXY((Lines[i].sx, Lines[i].sy) == hotspot[3]) && (Hotspot.GetAtScreenXY(Lines[i].ex, Lines[i].ey) == hotspot[4]))
         // ColaHotdog = true;
  }


Button

Code: ags

function on_call (int LinesOK) 
{

if (LinesOK == 2)
{
if (VBaer == true) cBerny.SayBubble("Bingo");
}
}


Global

Code: ags


function Button58_OnClick(GUIControl *control, MouseButton button)
{
if (cBerny.Room == 19)  // OK button
CallRoomScript(2);
}
Truly, truly, I say to you, blessed are those who play adventure games, for theirs is the kingdom of heaven.

Crimson Wizard

Quote from: Amir on Fri 10/06/2022 12:19:19
Ok,

in room_RepExec (under the code of drawing)


What else is in room_RepExec? how do you fill Lines array?

Crimson Wizard

Actually,.... if you are using Lines array, then you should not be checking hotspots in RepExec. Or, if you check hotspots in RepExec, then you do not need Lines array at all.

There are 2 approaches:
1) Save Lines array in rep exec. Test both the line results and final results after button is pressed (in on_call).
2) Don't use Lines array at all. Test line results in rep exec and save booleans (VBaer and others) there. Then test these booleans in on_call.

Amir

Quote from: Crimson Wizard on Fri 10/06/2022 17:18:14
Quote from: Amir on Fri 10/06/2022 12:19:19
Ok,

in room_RepExec (under the code of drawing)


What else is in room_RepExec? how do you fill Lines array?

No nothing, Lines array just like you wrote me. I didn't change anything. Take a look please, there is everything. It's not much in this room.

Code: ags


struct Line {
    int sx,sy,ex,ey;
};
 
#define MAX_LINES 100
Line Lines[MAX_LINES];

int line_count;

bool accept_input = true;
bool draw = false;
int LINE_WIDTH = 5;
int sx, sy, ex, ey;
DynamicSprite *spriteForLine;
DynamicSprite *spriteForAll;
int line_color = 53248;

function room_Load()
{
hotspot[1].Enabled = false;  
hotspot[2].Enabled = false;  
hotspot[3].Enabled = false;  
hotspot[4].Enabled = false; 
  
gSchliessen.Visible = true;
gSchliessen.SetPosition(1140, 16);


 gInventoryBar.Visible = false;
 gInventoryBar.Transparency = 100;
 
 if (Game.TranslationFilename == "English") object[1].Visible = true;
 
 gverbinden.Visible = true;
 gReset.Visible = true;
 
  spriteForAll = DynamicSprite.Create(Room.Width, Room.Height);
  gGuiAllLines.BackgroundGraphic = spriteForAll.Graphic;
 
 gGuiLine.Visible = true;
 gGuiAllLines.Visible = true;
}


/*
function on_call (int event) 
{
  
  if (event == 1) // lines reset
  {
       if (spriteForLine) {
       spriteForLine.Delete();
     }
 
     DrawingSurface *surface = spriteForAll.GetDrawingSurface();
     surface.Clear();
     surface.Release();  
    
  }
  else if (event == 2) // lines ok
  {
   if (VBaer == true) cBerny.SayBubble("Bingo"); 
     
  }
}
*/

function on_call (int LinesOK) 
{

if (LinesOK == 2)
{
if (VBaer == true) cBerny.SayBubble("Bingo");
}

}


function room_Leave()
{
  
  if (spriteForLine) {
    spriteForLine.Delete();
  }
  if (spriteForAll) {
    spriteForAll.Delete();
  }
 
 gGuiLine.Visible = false;
 gGuiAllLines.Visible = false;
 
  
}


function room_RepExec()
{ 
  
  bool mouse_down = Mouse.IsButtonDown(eMouseLeft);
  //if the left mouse button is down and the game accepts input, 
  //store x and y of the current mouse position
  if (mouse_down && accept_input)
  {
    accept_input = false;
    draw = true;
    sx = mouse.x;
    sy = mouse.y;
    ex = sx;
    ey = sy;
    spriteForLine = DynamicSprite.Create(Room.Width, Room.Height);
    gGuiLine.BackgroundGraphic = spriteForLine.Graphic;
  //draw line from the place where the mouse was pressed to the current position of the mouse
  } 
  else if (mouse_down && draw)
  { 
    DrawingSurface *surface = spriteForLine.GetDrawingSurface();
    // first erase old line by repainting it with a transparent color
    surface.DrawingColor = COLOR_TRANSPARENT;
    surface.DrawLine(sx, sy, ex, ey, LINE_WIDTH);
    ex = mouse.x;
    ey = mouse.y;
    surface.DrawingColor = line_color;
    surface.DrawLine(sx, sy, ex, ey, LINE_WIDTH);
    surface.Release();
  // if the mouse button is released and input set to false, reset accept_input and stop drawing lines
  } 
  else if (!mouse_down && !accept_input)
  {
    accept_input = true;
    draw = false;
    // paste resulting line onto the final sprite with many lines
    DrawingSurface *surface = spriteForAll.GetDrawingSurface();
    surface.DrawImage(0, 0, spriteForLine.Graphic);
    surface.Release();
    // don't display single-line sprite anymore
    gGuiLine.BackgroundGraphic = 0;
    spriteForLine.Delete();
    
  }
  
  
  /////////
  
 
 for (int i = 0; i < line_count; i++)
 {
        // test Lines[i] values:
      
     if ((Hotspot.GetAtScreenXY(Lines[i].sx, Lines[i].sy) == hotspot[1]) && (Hotspot.GetAtScreenXY(Lines[i].ex, Lines[i].ey) == hotspot[2]))
          VBaer = true;
       
     //else if ((Hotspot.GetAtScreenXY((Lines[i].sx, Lines[i].sy) == hotspot[3]) && (Hotspot.GetAtScreenXY(Lines[i].ex, Lines[i].ey) == hotspot[4]))
         // ColaHotdog = true;
  }
 
  
}


Truly, truly, I say to you, blessed are those who play adventure games, for theirs is the kingdom of heaven.

Amir

Quote from: Crimson Wizard on Fri 10/06/2022 17:38:39
Actually,.... if you are using Lines array, then you should not be checking hotspots in RepExec. Or, if you check hotspots in RepExec, then you do not need Lines array at all.

There are 2 approaches:
1) Save Lines array in rep exec. Test both the line results and final results after button is pressed (in on_call).
2) Don't use Lines array at all. Test line results in rep exec and save booleans (VBaer and others) there. Then test these booleans in on_call.


Ok, all right.
Truly, truly, I say to you, blessed are those who play adventure games, for theirs is the kingdom of heaven.

Crimson Wizard

#37
Quote from: Amir on Fri 10/06/2022 17:39:22
No nothing, Lines array just like you wrote me. I didn't change anything.

The thing is, what I wrote about Lines was just a starting example + tips of how you may approach this, it was not a complete code.
I am not explaining this well.

Let's discuss the logic first.

1) Each time the player finishes drawing the line, the line should be remembered.
2) When player presses the button to check the puzzle, two things should be performed:
2.1) first we check all the remembered lines, and find out what hotspots they connect. Each time there's a good connection, we save that result in corresponding bool.
2.2) second we check the resulting bools, to find out the actual state of the puzzle, and do something about it (display the message, and so on).
3) When we reset the line sprites we should also reset the line counter.


Now, let's look at the RepExec where we draw the line. The code responsible for finishing the line is this:
Code: ags

  else if (!mouse_down && !accept_input)
  {
    accept_input = true;
    draw = false;
    // paste resulting line onto the final sprite with many lines
    DrawingSurface *surface = spriteForAll.GetDrawingSurface();
    surface.DrawImage(0, 0, spriteForLine.Graphic);
    surface.Release();
    // don't display single-line sprite anymore
    gGuiLine.BackgroundGraphic = 0;
    spriteForLine.Delete();
  }


We need to save the line coordinates right there, under this "if", and increase the line counter to know how many lines are drawn:
Code: ags

     ..............
     // don't display single-line sprite anymore
     gGuiLine.BackgroundGraphic = 0;
     spriteForLine.Delete();
     // save line
     Lines[line_count].sx = sx;
     Lines[line_count].sy = sy;
     Lines[line_count].ex = ex;
     Lines[line_count].ey = ey;
     line_count++; // increase line counter


Now, we may go to on_call and put line tests there; but on another thought, it may be more convenient to write a separate function for that, to keep code tidy. So we create our own function that just test lines and saves bools:
Code: ags

function TestLines() {
    for (int i = 0; i < line_count; i++) {
        if ((Hotspot.GetAtScreenXY(Lines[i].sx, Lines[i].sy) == hotspot[1]) && (Hotspot.GetAtScreenXY(Lines[i].ex, Lines[i].ey) == hotspot[2]))
          VBaer = true;
        // ..... and more pair tests here
    }
}


And then in on_call we just call TestLines and perform final test:
Code: ags

function on_call (int event) 
{
    // LinesOk event
    if (event== 2) {
         // test all lines and save bools
         TestLines(); 
         // now final test(s)
         if (VBaer == true) cBerny.SayBubble("Bingo");
    }
}


We also may need to reset line counter along with the sprites. We need to do this in both room_Leave() and on_call where you reset the lines.
E.g. in on_call:
Code: ags

  if (event == 1) // lines reset
  {
     if (spriteForLine) {
       spriteForLine.Delete();
     }
 
     DrawingSurface *surface = spriteForAll.GetDrawingSurface();
     surface.Clear();
     surface.Release();

     line_count = 0; // <---- reset lines counter
  }

Amir

Yes, I completely understand the logic. But don't ask me why I didn't save the line coordinates after drawing them. I didn't think of that  :-\  I don't have your experience  :) moreover the code is not a simple code. but yes, now it looks a lot more logical. But... it still doesn't work. Now my wish of my life is to see him say this F. Bingo.

QuoteActually,.... if you are using Lines array, then you should not be checking hotspots in RepExec. Or, if you check hotspots in RepExec, then you do not need Lines array at all.

There are 2 approaches:
1) Save Lines array in rep exec. Test both the line results and final results after button is pressed (in on_call).
2) Don't use Lines array at all. Test line results in rep exec and save booleans (VBaer and others) there. Then test these booleans in on_call.

Is that correct after u saw the error in the code?

I think without the Lines array will not work.
Truly, truly, I say to you, blessed are those who play adventure games, for theirs is the kingdom of heaven.

Crimson Wizard

#39
This is my room code that works:

Note that when making tests, I check both directions, because player could draw line from hotspot 2 to 1 too.

Code: ags

// room script file

bool accept_input = true;
bool draw = false;
int LINE_WIDTH = 5;
int sx, sy, ex, ey;
DynamicSprite *spriteForLine;
DynamicSprite *spriteForAll;
int line_color = 53248;

struct Line {
    int sx,sy,ex,ey;
};
 
#define MAX_LINES 100
Line Lines[MAX_LINES];
int line_count;

// Line linking results
bool VBaer;


function room_Load()
{
  spriteForAll = DynamicSprite.Create(Room.Width, Room.Height);
  gGuiAllLines.BackgroundGraphic = spriteForAll.Graphic;
}

// ResetLines function clears lines up, clears results too.
// pass "true" if you want to delete full sprite, or only clear it
function ResetLines(bool delete_all)
{
  if (spriteForLine) {
    spriteForLine.Delete();
    spriteForLine = null;
  }

  // delete full lines sprite or only clear it
  if (delete_all) {
    spriteForAll.Delete();
    spriteForAll = null;
  } else {
    DrawingSurface *surface = spriteForAll.GetDrawingSurface();
    surface.Clear();
    surface.Release();
  }
  
  // Reset saved lines and results
  line_count = 0;
  VBaer = false;
}

function room_Leave()
{
  ResetLines(true);
}
 
function room_RepExec()
{ 
  bool mouse_down = Mouse.IsButtonDown(eMouseLeft);
  //if the left mouse button is down and the game accepts input, 
  //store x and y of the current mouse position
  if (mouse_down && accept_input){  
    accept_input = false;
    draw = true;
    sx = mouse.x;
    sy = mouse.y;
    ex = sx;
    ey = sy;
    spriteForLine = DynamicSprite.Create(Room.Width, Room.Height);
    gGuiLine.BackgroundGraphic = spriteForLine.Graphic;
  } else if (mouse_down && draw) {
    DrawingSurface *surface = spriteForLine.GetDrawingSurface();
    // first erase old line by repainting it with a transparent color
    surface.DrawingColor = COLOR_TRANSPARENT;
    surface.DrawLine(sx, sy, ex, ey, LINE_WIDTH);
    //draw line from the place where the mouse was pressed to the current position of the mouse
    ex = mouse.x;
    ey = mouse.y;
    surface.DrawingColor = line_color;
    surface.DrawLine(sx, sy, ex, ey, LINE_WIDTH);
    surface.Release();
  } else if (!mouse_down && !accept_input) {
    //if the mouse button is released and input set to false, reset accept_input and stop drawing lines
    accept_input = true;
    draw = false;
    // paste resulting line onto the final sprite with many lines
    DrawingSurface *surface = spriteForAll.GetDrawingSurface();
    surface.DrawImage(0, 0, spriteForLine.Graphic);
    surface.Release();
    // don't display single-line sprite anymore
    gGuiLine.BackgroundGraphic = 0;
    spriteForLine.Delete();
    // save line
    Lines[line_count].sx = sx;
    Lines[line_count].sy = sy;
    Lines[line_count].ex = ex;
    Lines[line_count].ey = ey;
    line_count++; // increase line counter
  }
}

// Perform line link tests and save results in boolean variables
function TestLines()
{
  for (int i = 0; i < line_count; i++) {
    Hotspot* h1 = Hotspot.GetAtScreenXY(Lines[i].sx, Lines[i].sy);
    Hotspot* h2 = Hotspot.GetAtScreenXY(Lines[i].ex, Lines[i].ey);
    if ((h1 == hotspot[1]) && (h2 == hotspot[2]) ||
        (h1 == hotspot[2]) && (h2 == hotspot[1]))
      VBaer = true;
    // ..... and more pair tests here
  }
}

function on_call(int event)
{
  // reset lines
  if (event == 1) {
    ResetLines(false);
  }
  // check lines
  else if (event == 2) {
    // test all lines and save bools
    TestLines();
    // now final test(s)
    if (VBaer == true)
      player.Say("Bingo");
    ResetLines(false);
  }
}

SMF spam blocked by CleanTalk