Drag and drop puzzle help

Started by rongel, Mon 25/08/2014 16:31:09

Previous topic - Next topic

rongel

Hi!

I'm trying to do simple drag & drop puzzle. I have a room (without characters), and some objects that you are supposed to drag to right places. Currently i'm using following script:

Code: ags

function oMyItem_Interact(){
  while(mouse.IsButtonDown(eMouseLeft)){
    oMyItem.X = mouse.x;
    oMyItem.Y = mouse.y;
    Wait(1);


That works for dragging the item around (even though my item "jumps" a bit annoyingly every time it's clicked). But how can I drop the item in the right place so that new command is activated? For example if my puzzle had a draggable key and I would drop it over a lock, what is the best way to make it work? I have read the forums, but haven't found an answer (many of the old links are not working anymore...)

Anyway, all help is welcome, thanks in advance!
Dreams in the Witch House on Steam & GOG

Crimson Wizard

#1
You do that in a non-optimal way: you are locking the dragging process inside one function, thus blocking the rest of the game.
Instead, I suggest to perform processing in "repeatedly_execute":
Code: ags

bool WasDragging;
int OldMyItemX;
int OldMyItemY;

function room_RepExec()
{
    if (mouse.IsButtonDown(eMouseLeft)) {
        // have we pressed a button over our object?
        if (!WasDragging && Object.GetAtScreenXY(mouse.x, mouse.y) == oMyItem) {
            // just started draggin? remember where you took it
            OldMyItemX = oMyItem.X;
            OldMyItemY = oMyItem.Y;
            WasDragging = true;
        }
        else if (WasDragging) {
            // mouse button still pressed? continue dragging
            oMyItem.X = mouse.x;
            oMyItem.Y = mouse.y;
        }
    } else if (WasDragging) {
        // was dragging but mouse button now released? drop it
        WasDragging = false;
        // ... you drop code here ...
        // for example:
        if (oMyItem.IsCollidingWithObject(oLock))
            UnlockTheLock(); // success!
        else {
            // failure (return the item on previous place?)
            oMyItem.X = OldMyItemX;
            oMyItem.Y = OldMyItemY;
        }
    }
}



To solve the "jumping" problem, remember the offset between mouse coordinates and object's coordinates at the moment you take it:
Code: ags

int DragOffsetX; <-------------------------
int DragOffsetY; <-------------------------

<...>
    if (mouse.IsButtonDown(eMouseLeft)) {
        // have we pressed a button over our object?
        if (!WasDragging && Object.GetAtScreenXY(mouse.x, mouse.y) == oMyItem) {
            // just started draggin? remember where you took it
            OldMyItemX = oMyItem.X;
            OldMyItemY = oMyItem.Y;
            DragOffsetX = mouse.x - oMyItem.X; <-------------------------
            DragOffsetY = mouse.y - oMyItem.Y; <-------------------------
            WasDragging = true;
        }
        else if (WasDragging) {
            // mouse button still pressed? continue dragging
            oMyItem.X = mouse.x - DragOffsetX; <-------------------------
            oMyItem.Y = mouse.y - DragOffsetY; <-------------------------
        }
    }


EDIT: duh! fixed silly mistake.
EDIT2: fixed typos.

rongel

Thanks for the super-quick answer!

That is quite heavy stuff... I did try your example, but my object didn't move anywhere, but at least no script errors!

Is the onMyItem a new item, what is it's difference with oMyItem? And is UnlockTheLock(); just a place for any command, for example: Display("Succes!");

Anyway, thanks for the info, I'll keep trying!


Dreams in the Witch House on Steam & GOG

Crimson Wizard

#3
Quote from: rongel on Mon 25/08/2014 18:25:00
Is the onMyItem a new item, what is it's difference with oMyItem?
That's a typo!

Quote from: rongel on Mon 25/08/2014 18:25:00
And is UnlockTheLock(); just a place for any command, for example: Display("Succes!");
Yes, it's an example of calling your custom function. Just put your own code there.

Sorry, I did not really test the code, just typed it by memory. I am going to check it out myself now :).

Another mistake: in rooms you usually use "function room_RepExec()". Create one by going to Room's events and create the handler for "Repeatedly execute" (like you did for Object's interact event).
Then paste function contents there.

rongel

#4
Ok, finally fot it working!

Well mostly, for some reason nothing happens when I drag my "key" to my "lock", the key just jumps back to it's starting location. Hmm?

EDIT:

Does (oMyItem.X == oLock.X && oMyItem.Y == oLock.Y) mean that they have to be exactly on the same position? My lock object is large, but does that mean I need to find the exact pixel?
Dreams in the Witch House on Steam & GOG

Crimson Wizard

Quote from: rongel on Mon 25/08/2014 19:42:57
Does (oMyItem.X == oLock.X && oMyItem.Y == oLock.Y) mean that they have to be exactly on the same position? My lock object is large, but does that mean I need to find the exact pixel?
Yes,... that was a bad example.
Of course, in normal situation you would need to compare to rectangle bounds, or do a pixel-perfect detection.
Code: ags

if (oMyItem.IsCollidingWithObject(oLock))

rongel

Got it working, thanks! :-D

Maybe it needs a bit fine tuning, but overall it's great, I hope this helps others as well!
Dreams in the Witch House on Steam & GOG

Tremulas

Can we apply isCollidingWithObject on inventory items ?

Crimson Wizard

#8
Quote from: Tremulas on Sat 26/03/2016 20:47:48
Can we apply isCollidingWithObject on inventory items ?

No. InventoryItem cannot really collide with anything, nor be placed anywhere in the room. It exists in "separate dimension" (so to say), not related to characters and objects.

Kumpel

To actually place items somewhere in a room you have to combine them with objects representing that item. And these objects are of course appliable with isCollidingWithObject.

Or am I misunderstanding your question?

Tremulas

Actually I was wondering if I could use IsCollidingWithObject and the whole Drag and Drop System in my Inventory ,to combine inventory items .

Crimson Wizard

#11
Quote from: Tremulas on Sun 27/03/2016 10:33:17
Actually I was wondering if I could use IsCollidingWithObject and the whole Drag and Drop System in my Inventory ,to combine inventory items .
No, InventoryItems cannot be moved, dragged etc. They simply do not have coordinates as such. Conceptually they are part of linear list.
The inventory window is displaying them arranged in grid using its own logic, but items themselves do not posess such property as "position on screen".

You might need to script something that represents inventory item (similar to how room object may represent an item lying in the room).
For example, you could create graphical Overlay to drag item image around; but you will have to test whether this overlay is above another item yourself, calculating item position in window using InventoryWindow properties, such as its coordinates, ItemWidth and ItemHeight values.

Unfortunately I do not have much free time right now, but I may post some experimental example of script later, unless someone else will before.

Tremulas

Ok , thank you so much for the information. I'll be looking forward for your example script in case I wont find a solution !

Thanks again !

Crimson Wizard

Hmmmmm... I forgot that overlays are always drawn behind GUI.
Perhaps using cursor graphic is the only way to visually depict dragging inventory item (same as when you select ActiveInventory).

Amir

Hi,
I'm trying to do the same but for all objects in room 22. In global script because I have a button in the room, if all objects are on the right place and the player push the button, then bingo. I use isCollidingWithObject for that as well. But the code doesn't work. What am I doing wrong? Could someone fix the code please?

Code: ags

bool WasDragging;
int OldMyItemX;
int OldMyItemY;
function repeatedly_execute()
{
if ((mouse.IsButtonDown(eMouseLeft)) && (cBerny.Room == 22))
  {
    Object *obj = Object.GetAtScreenXY(mouse.x, mouse.y);
        // have we pressed a button over our object?
        if ((obj != null) && (!WasDragging))
        
        {
            // just started draggin? remember where you took it
            OldMyItemX = obj.X;
            OldMyItemY = obj.Y;
            WasDragging = true;
            
        }
        
        else if (WasDragging) 
        {
            // mouse button still pressed? continue dragging
            OldMyItemX = mouse.x;
            OldMyItemY = mouse.y;

        }   
        
    } 
    
    else if (WasDragging)  
    {
    // was dragging but mouse button now released? drop it
    WasDragging = false;
    }  
}


And for the button

Code: ags

function bBuecher_OnClick(GUIControl *control, MouseButton button)
{
if (object[2].IsCollidingWithObject(object[9]))  && (object[3].IsCollidingWithObject(object[10])) && (object[4].IsCollidingWithObject(object[11]))

Player.Say("Bingo");

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

Crimson Wizard

#15
Quote from: Amir on Sun 29/05/2022 01:04:30But the code doesn't work.
I think you pasted the code incorrectly, this part seem wrong:
Code: ags

            // mouse button still pressed? continue dragging
            OldMyItemX = mouse.x;
            OldMyItemY = mouse.y;

should be
Code: ags

            // mouse button still pressed? continue dragging
            obj.X = mouse.x;
            obj.Y = mouse.y;


For the correct results you should also remember the dragged object in a variable when the dragging starts, and use that here instead of local "obj" instead, because if player moves mouse too fast, the cursor will appear no over the dragged object and local "obj" will be null.

By the way, this code assumes non-scrolling room, where screen coordinates match room coordinates.


On a side note, have you tried the Drag & Drop module? It already has all the functionality:
https://www.adventuregamestudio.co.uk/forums/index.php?topic=53332.0

Amir

#16
Yes, with obj.X and Y I’m getting the problem null pointer referenced. All rooms of the puzzles are non-scrolling rooms.
Is there no solution to this before I try the module? I like this code. Small but nice and an abbreviation of the module.
Truly, truly, I say to you, blessed are those who play adventure games, for theirs is the kingdom of heaven.

Crimson Wizard

#17
Quote from: Amir on Sun 29/05/2022 06:04:02
Yes, with obj.X and Y I’m getting the problem null pointer referenced.
Is there no solution to this before I try the module?

Like I mentioned, for the correct results you should also remember the dragged object in a variable when the dragging starts, and use that here instead of local "obj" instead.
It should probably be like:
Code: ags

bool WasDragging;
int OldMyItemX;
int OldMyItemY;
Object *DraggedObj;
function repeatedly_execute()
{
  if ((mouse.IsButtonDown(eMouseLeft)) && (cBerny.Room == 22))
  {
        Object *obj = Object.GetAtScreenXY(mouse.x, mouse.y);
        // have we pressed a button over our object?
        if ((obj != null) && (!WasDragging))
        {
            // just started draggin? remember where you took it
            OldMyItemX = obj.X;
            OldMyItemY = obj.Y;
            DraggedObj = obj;
            WasDragging = true;
        }
        else if (WasDragging) 
        {
            // mouse button still pressed? continue dragging
            DraggedObj.X = mouse.x;
            DraggedObj.Y = mouse.y;
        }
    }
    else if (WasDragging)  
    {
       // was dragging but mouse button now released? drop it
       WasDragging = false;
       DraggedObj = null;
    }  
}




Actually, in this case WasDragging may perhaps be omited and DraggedObj used instead in conditions. A cleaner version of the code would look like:

Code: ags

Object *DraggedObj;
int OldPosX; 
int OldPosY;

function repeatedly_execute()
{
    if ((mouse.IsButtonDown(eMouseLeft)) && (cBerny.Room == 22))
    {
        // was not dragging yet?
        if (DraggedObj == null)
        {
            // have we pressed a button over some object?
            Object *obj = Object.GetAtScreenXY(mouse.x, mouse.y);
            if (obj != null)
            {
                // just started draggin? remember what and where you hooked up
                OldPosX = obj.X;
                OldPosY = obj.Y;
                DraggedObj = obj;
            }
        }
        else if (DraggedObj != null) 
        {
            // mouse button still pressed? continue dragging
            DraggedObj.X = mouse.x;
            DraggedObj.Y = mouse.y;
        }
    }
    else if (DraggedObj != null)  
    {
       // was dragging but mouse button now released? drop it
       DraggedObj = null;
    }  
}

Amir

Quoteyou should also remember the dragged object in a variable when the dragging starts

Sorry, I understood that differently. I could not have created this arranging on my own. Thank u very very much.

I just had to add this so that the object is centered on MY mouse cursor. (if anyone needs it)
Code: ags

DraggedObj.X = mouse.x-14;
DraggedObj.Y = mouse.y+30;
Truly, truly, I say to you, blessed are those who play adventure games, for theirs is the kingdom of heaven.

SMF spam blocked by CleanTalk