Hello,
I still have my last puzzle in the game where you have to match objects (or hotspots), with an OK button you have to click on it when you have matched to see if it's wrong or right. I have a complicated idea how to do this, using lines as objects. But it's complicated and takes a lot of work and time. I thought differently, I could maybe do it with drawing lines. when the left mouse button is pressed the mouse moves while the button is still down, a line is drawn between where you clicked and you let go the mouse button.
Something like that here: (this is not my puzzle ;-D)
Spoiler
(https://i.ibb.co/2hQ4qFB/Match.jpg) (https://ibb.co/GpLS7dj)
I found this ancient code in this thread, since 2004. It looks like it's right for my puzzle but it doesn't work anymore. https://www.adventuregamestudio.co.uk/forums/index.php?topic=12390.msg148534#msg148534 (https://www.adventuregamestudio.co.uk/forums/index.php?topic=12390.msg148534#msg148534)
I tried to fix it but the commands are from the Jurassic period.
Has anyone written or used some similar code before, or maybe there's a module I haven't discovered yet?
Not sure if this helps, but if you are going to have the pictures as objects in the room, I would, on mouse button click, loop through the objects and check their coordinates and dimensions of their sprites. Knowing the coordinates and dimensions, you can check if the click happened within the boundaries of one of the objects/sprites. If yes, you store the object's id and wait for the release of the mouse button. When the mouse button is released you do the same thing - check where the mouse button was released. If it was released within the boundaries of one of the images you grab its id. Then you can check what two objects/images have been linked (of course you would need to have an array of correct matches to compare against).
As for the drawing the line, once the mouse is pressed, you start drawing a line using dynamic sprites. As far as I remember, you do it by capturing the background before you start drawing the line, and every pixel the mouse is moved, you redraw the background and the new line (starting from the point where the mouse was pressed, and finishing at the current position of the mouse). Once the mouse button is released, you stop redrawing the background and the line.
I can't write the actual code at the moment, but I would probably try doing something along these lines.
It doesn't matter if it's hotsopts or objects, I can change it.
thanks for explaining the method, I understand it but i think I wouldn't be able to do it because i've never handled this Drawing functions before, so I asked if anyone has any code already and that's why I tried Rick's code . This would take me longer and require more work than the complicated lines-as-objects method. I can't find any modules with drawings at all, I might set them up faster so that I can draw lines from mouse.x to mouse.y. I will look again today.
I see. If you don't mind waiting, I will try to fish out my old code from my game in the evening and share it with you. This is a clip from my game, which also draws lines: https://youtu.be/9pksmHxbXnc?t (go to 48:00) If I am not mistaken, this is something similar you want to achieve?
Oh cool, yes, exactly, but with free moving mouse cursor. I see you have limited mouse x and y in this room. I think this is not difficult to change in your code. Take your time, I can wait. Now I have a worse problem with changing the translation. (I posted it in Beginners' Technical section)
Cool game, I like the graphics. I will definitely play it at some point.
Thank u :)
Right. I found the code and managed to pull out the bits that will hopefully shed some light. I haven't tested this particular code, so there might be some imperfections. But, in essence, this is the same logic that worked for me. It's possible you will have to tweak it here and there, though.
bool accept_input = true;
bool draw = false;
int line_count = 0;
int LINE_WIDTH = 5;
int start_x;
int start_y;
DrawingSurface *surface;
DynamicSprite *sprite;
function room_RepExec(){
//if the left mouse button is down and the game accepts input,
//store x and y of the current mouse position
//as well as copy the current view of the screen to sprite (so already drawn lines will stay)
if(Mouse.IsButtonDown(eMouseLeft) && accept_input){
accept_input = false;
start_x = mouse.x;
start_y = mouse.y;
sprite = sprite.CreateFromScreenShot();
line_count++;
}
//if the mouse button is released and input set to false, reset accept_input and stop drawing lines
if(!Mouse.IsButtonDown(eMouseLeft) && !accept_input ){
accept_input = true;
}
//if accept_input is false, this means the button is pressed, so draw the lines
if(!accept_input){
surface = Room.GetDrawingSurfaceForBackground();
//if this is the first line/match to be drawn, draw the background from the 'clean' sprite
//otherwise draw the background with already drawn lines
if(line_count == 0) sprite = sprite.CreateFromExistingSprite(id_of_background_image);
//draw the sprite variable on the screen
surface.DrawImage(0, 0, sprite.Graphic);
//draw line from the place where the mouse was pressed to the current position of the mouse
surface.DrawLine(start_x, start_y, mouse.x, mouse.y, LINE_WIDTH);
surface.DrawingColor = number_of_colour;
surface.Release();
}
}
Note that once you are finished, you should delete the sprite variable with sprite.Delete();
Hope this helps, if not, give ma a shout.
Ok, thank you so much ;-D I'll test it right away in my puzzle room.
I've only tested drawing so far, but there's a problem or two. Whenever I click to draw, the music stutters annoyingly and I have 2 buttons there, whenever I click to draw, the sprites of buttons become more pixelated and look aliased. Do u know why? :-\ I doubted the width, I tried int LINE_WIDTH = 1; but that didn't help. My resolution is 1280 x 720.
I also doubt the mouse cursor because it stays there after you draw. Is there any way to prevent it?
And I cannt change the color surface.DrawingColor = 53248; I want it red but it's always black, but that doesn't matter, that's no problem.
Quote from: Amir on Wed 08/06/2022 15:40:55
I also doubt the mouse cursor because it stays there after you draw. Is there any way to prevent it?
The mouse stays because the game captures whatever is displayed on the screen. I didn't have that problem because the cursor was hidden in my room. I guess you could try hiding the cursor right before this line:
sprite = sprite.CreateFromScreenShot();
...and then showing it again afterwards.
Quote from: Amir on Wed 08/06/2022 15:40:55
And I cannt change the color surface.DrawingColor = 53248; I want it red but it's always black, but that doesn't matter, that's no problem.
Are you sure your colour code is correct? In my game it had the value of 13 and it worked fine. Have you tried any other colour numbers?
Quote from: Amir on Wed 08/06/2022 15:40:55
Whenever I click to draw, the music stutters annoyingly.
I didn't have that issue. Are you doing anything with the audio clips in the same room_RepExec() function?
Quote from: Amir on Wed 08/06/2022 15:40:55
the sprites of buttons become more pixelated and look aliased.
Are these buttons the only GUI elements shown in this puzzle room? I am not an expert, and I am not sure what may be the cause of that. I wonder if the same would happen if you hid the buttons for the time of capturing the screenshot from the background (just like with the cursor)?
QuoteThe mouse stays because the game captures whatever is displayed on the screen. I didn't have that problem because the cursor was hidden in my room. I guess you could try hiding the cursor right before this line
I had tried this and Changemode of the mouse as well, in the code and in on_mouse_click but it dosn't work, it would work if you just click once, the mouse cursor disappears but you can no longer see where you draw the line. It doesn't matter, that's no problem, it doesn't look that bad.
QuoteAre you sure your colour code is correct? In my game it had the value of 13 and it worked fine. Have you tried any other colour numbers?
Yes and I tried many codes, it stays black ;-D that's also no problem. I like
black metal.
QuoteI didn't have that issue. Are you doing anything with the audio clips in the same room_RepExec() function?
No nothing, by the way it's in the Global script because of the buttons "OK" and "Reset" I have 3 Buttons there, ok reset and exit room, it won't look pretty if they keep disappearing and reappearing when you draw and finish drawing. ;-D
I think I know now why this is happening, because of the screenshot the code takes. When I take a screenshot in the game, the music stutters the same as in this room. I don't think it's a good idea to draw with a screenshot at my resolution 1280 x 720. I think a Gui as BackgroundGraphic would be better in my case. Look at this link https://www.adventuregamestudio.co.uk/forums/index.php?topic=54958.0 (https://www.adventuregamestudio.co.uk/forums/index.php?topic=54958.0)
I will try to mix your codes and do it with Gui, like I said, I have never written any code using Drawing Surface. If you can help with that, that would be very nice. (or someone else who reads and knows)
You don't really need a screenshot here, as you may save the finished lines on a separate sprite.
Idea for this is:
Draw on a dynamic sprite, cleared to transparency.
Remember line coordinates, then on each "drawing" step clear the old line by drawing it with transparent color, and draw a new one with updated coordinates.
Assign that sprite to a room object or GUI, so that you don't have to repaint the background all the time either.
Since you have multiple lines, save them on a second sprite, and only paste finished lines there as soon as they are completed. Assign that sprite to another object or gui.
Ok, I will try that with Guis.
Regarding color not working, I noticed that in the above script example DrawingColor is set after DrawLine. You need to swap these two commands.
Quote from: Crimson Wizard on Thu 09/06/2022 09:02:59
Regarding color not working, I noticed that in the above script example DrawingColor is set after DrawLine. You need to swap these two commands.
(laugh) your right, now it's red.
Quote from: Crimson Wizard on Thu 09/06/2022 08:30:53
on each "drawing" step clear the old line by drawing it with transparent color
Surely it's easier and probably more efficient to just wipe the whole sprite by calling
DrawingSurface.Clear(COLOR_TRANSPARENT)?
Quote from: Snarky on Thu 09/06/2022 09:19:37
Quote from: Crimson Wizard on Thu 09/06/2022 08:30:53
on each "drawing" step clear the old line by drawing it with transparent color
Surely it's easier and probably more efficient to just wipe the whole sprite by calling DrawingSurface.Clear(COLOR_TRANSPARENT)?
Clear() sets every pixel on a sprite to certain value. DrawLine sets only necessary pixels, but does extra math to calculate how line is going. A speed test would be required to see the actual difference, which may also depend on the sprite resolution and average line length.
Here's the working script, created by adjusting the above script by Pogwizd.
You only need 2 GUIs to make it work, called gGuiLine and gGuiAllLines.
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()
{
spriteForAll = DynamicSprite.Create(Room.Width, Room.Height);
gGuiAllLines.BackgroundGraphic = spriteForAll.Graphic;
}
function room_Leave()
{
if (spriteForLine) {
spriteForLine.Delete();
}
if (spriteForAll) {
spriteForAll.Delete();
}
}
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();
}
}
Oh cool, I'm trying to do it now with sprite.CreateFromExistingSprite(object[2].Graphic); but when you've already done it with guis, I'll take your code. (not yet tested)
But shouldn't it be in the Global Script? As I said, I have an OK button that the player has to click on if all the lines are correct. And the button is always in Global Script. It won't see the lines like that in the room 19.
Quote from: Amir on Thu 09/06/2022 09:51:26
But shouldn't it be in the Global Script? As I said, I have an OK button that the player has to click on if all the lines are correct. And the button is always in Global Script. It won't see the lines like that in the room 19.
You can put that anywhere you like. For example you may create a separate script module for that, and add functions that turn the line drawing on and off.
But it does not have to be in the global script only to let button do something. You may pass a "signal" into the room script using CallRoomScript command:
https://adventuregamestudio.github.io/ags-manual/Globalfunctions_General.html#callroomscript
Quote from: Crimson Wizard on Thu 09/06/2022 09:55:27
Quote from: Amir on Thu 09/06/2022 09:51:26
But shouldn't it be in the Global Script? As I said, I have an OK button that the player has to click on if all the lines are correct. And the button is always in Global Script. It won't see the lines like that in the room 19.
You can put that anywhere you like. For example you may create a separate script module for that, and add functions that turn the line drawing on and off.
But it does not have to be in the global script only to let button do something. You may pass a "signal" into the room script using CallRoomScript command:
https://adventuregamestudio.github.io/ags-manual/Globalfunctions_General.html#callroomscript
Thank u so much. It works like a charm. But I can't make this on_call. what am I doing wrong?
this is the reset button. All lines should be deleted.
Global scrip
function gReset_OnClick(GUI *theGui, MouseButton button)
{
if (cBerny.Room == 19)
CallRoomScript(1);
}
Room 19
function on_call (int Lines)
{
if (Lines == 1)
{
if (spriteForLine) {
spriteForLine.Delete();
}
if (spriteForAll) {
spriteForAll.Delete();
}
}
}
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.
Oh, I probably clicked wrong.
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
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.
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
Aaah I understood the principle now of these DrawingSurface. Thank u :)
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)
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
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.
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:
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:
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
}
}
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.
Hi CW,
could you check please, because Berny doesn't say bingo. (Im trying first only for 2 lines, 4 hotspots)
In room_RepExec
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
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.
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
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? :-\
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:
// 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:
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.
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:
enum LinePuzzleEvents {
LinePuzz_Reset,
LinePuzz_Check
};
And then just do
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:
CallRoomScript(LinePuzz_Reset); // and so on
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
if ((AnchorBoat == true) && (ColaHotdog == true))
cBerny.SayBubble("Bingo.");
Thanks for the information. I'll try it.
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.
//hotspot[1].Enabled = false;
//hotspot[2].Enabled = false;
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?
Ok,
in room_RepExec (under the code of drawing)
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
function on_call (int LinesOK)
{
if (LinesOK == 2)
{
if (VBaer == true) cBerny.SayBubble("Bingo");
}
}
Global
function Button58_OnClick(GUIControl *control, MouseButton button)
{
if (cBerny.Room == 19) // OK button
CallRoomScript(2);
}
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?
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.
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.
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;
}
}
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.
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:
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:
..............
// 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:
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:
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:
if (event == 1) // lines reset
{
if (spriteForLine) {
spriteForLine.Delete();
}
DrawingSurface *surface = spriteForAll.GetDrawingSurface();
surface.Clear();
surface.Release();
line_count = 0; // <---- reset lines counter
}
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.
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.
// 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);
}
}
Aha this TestLines(); function probably makes a big difference but it doesn't work for me ???
I have to import the 2 new functions, right?
Quote from: Amir on Fri 10/06/2022 20:39:33
Aha this TestLines(); function probably makes a big difference but it doesn't work for me ???
I have to import the 2 new functions, right?
It's not only 2 new functions, there are other differences, compared to the last code you posted above. I guess you could compare the code side by side to see what is different. Or simply paste full code over, and then adjust to your needs.
Quote from: Crimson Wizard on Fri 10/06/2022 20:56:08
Quote from: Amir on Fri 10/06/2022 20:39:33
Aha this TestLines(); function probably makes a big difference but it doesn't work for me ???
I have to import the 2 new functions, right?
It's not only 2 new functions, there are other differences, compared to the last code you posted above. I guess you could compare the code side by side to see what is different. Or simply paste full code over, and then adjust to your needs.
Yes it works now. Thank u so much. ;-D
Quote from: Crimson Wizard on Fri 10/06/2022 20:56:08
Quote from: Amir on Fri 10/06/2022 20:39:33
Aha this TestLines(); function probably makes a big difference but it doesn't work for me ???
I have to import the 2 new functions, right?
It's not only 2 new functions, there are other differences, compared to the last code you posted above. I guess you could compare the code side by side to see what is different. Or simply paste full code over, and then adjust to your needs.
Please forgive me, I have one last question since you have the code. I want to make it so that if you click the OK button and haven't dragged anything yet, nothing happens. I mean if there are no lines there nothing happens when you click OK. It doesn't work like that
else if (event == 2) // lines ok
{
// test all lines and save bools
TestLines();
// now final test(s)
if ((line_count == 0) && (VBaer == false) && (Grim == false) && (Fenster == false) && (Vase == false))
{} // do nothing
else if ((VBaer == true) && (Grim == true) && (Fenster == true) && (Vase == true))
{
// Bingo
}
else
{
// wrong answer
}
Quote from: Amir on Sat 11/06/2022 00:49:40I want to make it so that if you click the OK button and haven't dragged anything yet, nothing happens. I mean if there are no lines there nothing happens when you click OK.
I think it might work if you simply move the "line_count" check up and merge with "if (event == 2)":
if (event == 2 && line_count > 0)
as if there are no lines drawn, then no need to also call TestLines and other things.
Quote from: Crimson Wizard on Sat 11/06/2022 01:34:54
Quote from: Amir on Sat 11/06/2022 00:49:40I want to make it so that if you click the OK button and haven't dragged anything yet, nothing happens. I mean if there are no lines there nothing happens when you click OK.
I think it might work if you simply move the "line_count" check up and merge with "if (event == 2)":
if (event == 2 && line_count > 0)
as if there are no lines drawn, then no need to also call TestLines and other things.
Good morning, it doesn't work, I've tried many things, like this for example.
else if (event == 2) // lines ok
{
// test all lines and save bools
TestLines();
// now final test(s)
if (line_count == 0) {} // do nothing
else if ((line_count > 0) && (VBaer == true) && (Grim == true) && (Fenster == true) && (Vase == true))
{
// Bingo
}
else
{
// wrong answer
}
}
The result is wrong answer. Something doesn't let it be. The loop?
for (int i = 0; i < line_count; i++)Edit: I'll try to do it with
gverbinden.Clickable = false; That is the Gui of the OK button.
Quote from: Amir on Sat 11/06/2022 08:20:09
Good morning, it doesn't work, I've tried many things, like this for example.
This may be because the line drawing code draws lines even when you click on buttons. So when you click on a button, it draws a small line anyway.
The solution may be to either restrict drawing to certain coordinates, or coordinates of gui that you put sprites on, if you make it not cover whole screen but only area where it is allowed to draw.
Or use ZOrder to put gGuiLine below other controls and test GUI.GetAtScreenXY to see if the mouse is above it before starting to draw.
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){
if (GUI.GetAtScreenXY(mouse.x, mouse.y) != gGuiLine) // <--------- add this
return;// <---------
<...>
QuoteThe solution may be to either restrict drawing to certain coordinates, or coordinates of gui that you put sprites on, if you make it not cover whole screen but only area where it is allowed to draw.
It's not on the whole screen, otherwise the buttons would not be clickable. It's there where the hotspots are. But apparently it still draws invisible lines outside the gui.
Quoteuse ZOrder to put gGuiLine below other controls and test GUI.GetAtScreenXY to see if the mouse is above it before starting to draw.
Ok, i'll test it.
Yes with if (GUI.GetAtScreenXY(mouse.x, mouse.y) != gGuiLine) return;// I can click on OK gui but not gGuiline, gGuiline is gone. That's good, but when I change ZOrder and put gGuiline behind the other and click on OK gui, wrong answer, like I said, like I'm drawing an invisible line outside of the gui and that's why it doesn't work.
Quote from: Amir on Sat 11/06/2022 13:12:18
Yes with if (GUI.GetAtScreenXY(mouse.x, mouse.y) != gGuiLine) return;// I can click on OK gui but not gGuiline, gGuiline is gone. That's good, but when I change ZOrder and put gGuiline behind the other and click on OK gui, wrong answer, like I said, like I'm drawing an invisible line outside of the gui and that's why it doesn't work.
That should not happen. What are zorder values of these guis?
There's also gGuiAllLines, it should perhaps be made non-clickable to not interfere with anything.
Quote from: Crimson Wizard on Sat 11/06/2022 14:02:34
Quote from: Amir on Sat 11/06/2022 13:12:18
Yes with if (GUI.GetAtScreenXY(mouse.x, mouse.y) != gGuiLine) return;// I can click on OK gui but not gGuiline, gGuiline is gone. That's good, but when I change ZOrder and put gGuiline behind the other and click on OK gui, wrong answer, like I said, like I'm drawing an invisible line outside of the gui and that's why it doesn't work.
That should not happen. What are zorder values of these guis?
There's also gGuiAllLines, it should perhaps be made non-clickable to not interfere with anything.
They are already both unclickable.
gGuiLine.ZOrder = -100;
gGuiAllLines.ZOrder = -100;
gverbinden.ZOrder = 100; // Ok button
gReset.ZOrder = 100; // Reset Button
Both Guis left 0 and width 540 and the button OK width 100 and left 700. I tried moving the Gui OK to the far right edge 1100 left but // wrong answer, as if it were inside the gGuiline.
Is it really only drawn in GUI and not outside?
I added if (mouse.x < 505) and it seems to be working now.
if (mouse.x < 505)
{
if ((object[0].Visible == true) || (object[3].Visible == true)) {} // nicht malen
else 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();
// 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
}
}
Quote from: Amir on Sat 11/06/2022 15:27:37
They are already both unclickable.
Note that if gGuiLine is also unclickable, then " if (GUI.GetAtScreenXY(mouse.x, mouse.y) != gGuiLine)" won't work, as GUI.GetAtScreenXY only looks for clickable guis.
Quote from: Crimson Wizard on Sat 11/06/2022 20:10:32
Quote from: Amir on Sat 11/06/2022 15:27:37
They are already both unclickable.
Note that if gGuiLine is also unclickable, then " if (GUI.GetAtScreenXY(mouse.x, mouse.y) != gGuiLine)" won't work, as GUI.GetAtScreenXY only looks for clickable guis.
Yes, you're right. That's why it didn't work. I forgot that I made both Guis unclickable. No matter now. It works now after I restricted mouse activation. Thank u.