[solved] new left/right click infterface

Started by digitalray, Tue 12/04/2011 18:09:59

Previous topic - Next topic

digitalray

hello there,

this is my first day i am using ags and i have been doing a lot of things today in customizing my own adventure, so far been finding out how to create own gui's (even text gui's), remove the text background, make an outlined text that is only used in speech but not in the menus, and recoded some minor stuff that i didn't like (added escape key option for inventory), done a transparent inventory with only 2 options and created own alpha channel graphics (i guess i can only use 1 colour as transparent, not really use an alpha channel so i could use anti-aliased sprites around the edges of the objects ?)


now i have found out how to use eMode for the mouse clicks.


what i want to do is : left click does walking AND using objects (and inventory objects), right click does look.

i implemented that already, but all i am missing is a trigger that tells me, if the cursor is on a usable object or useable hotspot, so i can set the mouse.mode to interact, else i would set the mouse.mode to walk.


here is my script so far:

function on_mouse_click(MouseButton button) {
 // called when a mouse button is clicked. button is either LEFT or RIGHT
 if (IsGamePaused() == 1) {
   // Game is paused, so do nothing (ie. don't allow mouse click)
 }
 else if (button == eMouseLeft) {  // LEFT MOUSE BUTTON
   if (CURSOR ON A USABLE OBJECT OR HOTSPOT) {
     if (player.ActiveInventory!=null){ // IF INVENTORY OBJECT IS SELECTED
       mouse.Mode=eModeUseinv; // SELECT USE INVENTORY OBJECT MODE
     }
     else {
       mouse.Mode=eModeInteract; // ELSE SELECT INTERACT MODE
     }
   ProcessClick(mouse.x, mouse.y, mouse.Mode); // DO LEFT CLICK WITH SELECTED MOUSE MODE
   }
   else {
   mouse.Mode=eModeWalkto;  // IF CURSOR IS ON NO OBJECT SELECT WALK MODE
   ProcessClick(mouse.x, mouse.y, mouse.Mode); // DO LEFT CLICK WITH SELECTED MOUSE MODE
   }
 }
 else if (button == eMouseRight){
   mouse.Mode=eModeLookat;  // SELECT LOOK AT MODE
   ProcessClick(mouse.x, mouse.y, mouse.Mode); // DO LEFT CLICK WITH SELECTED MOUSE MODE
 }
}


i have been searching the forum and looking at faqs and variables, but couldn't find anything specific to this.

i could of course download an existing left/right mod, but i looked at the existing one and it only makes use of the lookat feature together with walk mode, that is available to all objects and hotspots, so i couldn't find the trigger i am looking for in there.

the trigger is this line in the beginning  - if (CURSOR ON A USABLE OBJECT OR HOTSPOT) -

if there is no function for this (like checking mouse.x and mouse.y for objects 0 to 99 beeing there in that room or something), would there be another way to do this ?

Khris

What you need is GetLocationType().

You'll also need to set inventory clicks to be handled in script in General settings and eModeLeft/RightInv.

There's also the .GetAtScreenXY() function for InventoryItems, Characters, Hotspots and Objects.

Quote from: digitalray on Tue 12/04/2011 18:09:59i guess i can only use 1 colour as transparent, not really use an alpha channel so i could use anti-aliased sprites around the edges of the objects ?

If you set the game's color depth to 32bit, you can import PNGs with alpha channels. There are some quirks when you want to use them as GUI buttons though, but as character/object graphics and the like they work fine.

Btw, you can use the [code] tag to encapsulate code to make it more readable.

digitalray

works ! :)

thanx khris !! thanks a lot !


it didn't work when i only used IF GetLocationType == character || object, but it started working, when i just used IF GetLocationType != nothing.

don't know why, but it runs the other way rund with the != function and "nothing" ;)

TomatoesInTheHead

Operators like || and && work only on boolean (true/false) expressions like equations, you can't directly substitute them for any "or" or "and" in a natural language sentence. So "if the location is a character or object" must be rewritten as "if the location is a character or the location is an object" before you can turn it into a condition.

digitalray

#4
ok, i thought it would translate it automatically.

i also wondered if i need some more round brackets or anything to somehow combine the expressions to one, but now i see that i would need an extra step to do this.

thanks for the hint !

would it work the way like i did it with boolean like this (imagined there is a boolean GetLocationType_NOTHING) and would it make sense at all (i mean true and false are opposite, why would anyone use them with an AND or OR :) ) ?

IF (GetLocationType_NOTHING(mouse.x,mouse.y) == true || false) {}

but i guess you meant TWO OR MORE booleans (not values) in one IF.


does ANDOR exist then or would i have to take two OR IFs, one with an AND, even with boolean ?

i will find out... :)

thanks again for your help so far.

Matti

#5
What he meant is that you have to write

Code: ags

if (GetLocationType(mouse.x,mouse.y) == eLocationCharacter || GetLocationType(mouse.x,mouse.y) == eLocationObject)


instead of

Code: ags

if (GetLocationType(mouse.x,mouse.y) == eLocationCharacter || eLocationObject)

digitalray

oh thanks matti, so it does indeed work with extra round brackets like i thought and i don't need a second IF..

have to try that the next days.

Matti

Sorry, you don't need extra brackets, that was a mistake.

digitalray

#8
oh :( .. ok ;)


on to my next problem

i just coded a function called set_mouse_mode () and defined it in the header to make it work, that checks where your mouse cursor is with the GetLocationType function and sets the appropriate symbol and mouse mode automatically. so the cursor is a hand when i can use an object or the walking man when i can just walk somewhere. this already works.

but i would need another information.. i want to use the eye symbol if there is no interact event set at a hotspot or an object and it should only change to the hand symbol if an interact event is set for the hotspot/object the mouse cursor is currently hovering on.

how can i check if there is an Event set for Interact in this hotspot or event ?

- i could use GetAtScreenXY with hotspot or object to get the name of it as a Hotspot* pointer.

like

       if ((Hotspot.GetAtScreenXY(mouse.x, mouse.y)).RunInteraction(eModeInteract) != 0) {
         mouse.Mode=eModeInteract; // ELSE SELECT INTERACT MODE
       }
       else mouse.Mode=eModeLookat; // ELSE SELECT INTERACT MODE


but this does not work, because runinteract needs a hotspot pointer value before .RunInteraction.
i don't even know, if RunInteraction returns a 0 if there is no interaction set.

Could i set the Hotspot* Pointer in place of a Hotspot value before the .RunInteraction ?

would RunInteraction (hotspot) give me a specific return i could use if there was no interaction set for the hotspot like a zero if there is no interaction set ?


well i got it half running (it checks, and if the hotspot runinteraction value is not NOT 0, it displays the eye icon) with this code (i defined a new Hotspot* hsnow as variable to use in RunInteraction):

Code: ags

Hotspot* hsnow;

function set_mouse_mode ()
{
    if (GetLocationType(mouse.x,mouse.y) != eLocationNothing) { // MOUSE IS ON AN OBJECT OR HOTSPOT OR CHARACTER
      if (player.ActiveInventory!=null){ // IF INVENTORY OBJECT IS SELECTED
        mouse.Mode=eModeUseinv; // SELECT USE INVENTORY OBJECT MODE
      }
      else {
        hsnow = Hotspot.GetAtScreenXY(mouse.x, mouse.y);
        if (hsnow.RunInteraction(eModeInteract) != 0) {
          mouse.Mode=eModeInteract; // ELSE SELECT INTERACT MODE
        }
        else mouse.Mode=eModeLookat; // ELSE SELECT INTERACT MODE
      }
    }
    else {
    mouse.Mode=eModeWalkto;  // IF CURSOR IS ON NO OBJECT SELECT WALK MODE
    }
}


the problem is though ;) ... : if the runinteraction (hotspot) IS in fact not 0 (so the runinteraction function does something), the interaction is exectued with the runinteraction () as soon as the mouse gets over an interactable object.

secondly would i need a static hotspot* variable ?

is there a way to "check" or "test" the runinteraction () without really executing it ?


in this situation i don't really like "closed source" applications.. :-/


another idea on this:

what can i do with the "Interact hotspot" and it's value "hHotspot3_Interact" (this value is in fact a function that can be empty, hooray ;)) that i can see in AGS Editor in the Room Events ?

how can i get to this valued function  ?

get the hotspot name through getatlocationXY and use my own hsnow variable with it so i have the hotspot name. so far so good, but how can i reach it's interact function name from it's name ?

and how would i convert the different types into a string that i can use as a function name in an IF ??


third way to think: Hotspot.GetLocationXY has an enum called hotspot that saves the name of the hotspot in # 2... maybe anyone knows if the hotspot interact value (that should be empty and so == 0) is also stored in the hotspot enum ? edit: ok, wrong guess.. the hotspot enum just hold the numbered hotspots for the room.. :/


ok, blind guess 4: is GetDefaultInteractionAtScreenXY of any help ?

CursorMode GetDefaultInteractionAtScreenXY(int x, int y)

Returns the value of the property DefaultInteraction for whatever is shown on the screen at the specified (X, Y) co-ordinates. This can be a Character, Hotspot, InventoryItem, or an Object.



try 5: might check the functions against NULL to see if they exist ?
but still.. how to get the interact hotspot function name (hHotspot3_Interact) from the hotspot name (hBox) ?

Khris

Slow down there... :)

First of all, a Hotspot is just one type of game object; you'll also need handling for Objects and Characters for a complete interface.

This line:
Code: ags
    if (hsnow.RunInteraction(eModeInteract) != 0) {

will, as you found out already, run the interaction (or do nothing if no function is linked).
AGS (and any other similar programming language) doesn't care that it's inside an if condition.

To find out whether an interaction function is available/linked, there's IsInteractionAvailable(int x, int y, int mode).

You can't get to the linked function's name, you can only find out if there is one and you can run it (that's what .RunInteraction is for; whatever that returns, it's most likely useless and you don't need the return value anyway).

Regarding multiple checks inside one if condition:
Like Matti said, you don't need extra brackets.
The following is perfectly fine:
Code: ags
  if (a == 1 || a == 3 || a == 5 || a == 7 || a == 9)


ANDOR is the same as OR and is expressed using ||.
You probably meant XOR. XOR can only be done bitwise; since true is evaluated as 1 and false as 0, it should work. The operator is ^.

TomatosInTheHead didn't put it exactly right; it's perfectly possible to use any operator on things other than equations. If you do that, everything that's not false, null or 0 will be evaluated as true/1.
In other words, you can do this:
Code: ags
  if (IsInteractionAvailable(mouse.x, mouse.y, eModeInteract))

without having to put "== 1" at the end.

Regarding your very last question: I have no idea what you mean.
I'd recommend reading the complete scripting reference at least once to get an idea what's available.

digitalray

#10
khris, you are a hero. it works like it should.

and i really don't know why i didn't find this simple function. i searched for interact anywhere for hours.. and i thought i looked for that keyword in the functions page of ags. seems i just didn't.

thanks for all the other information.


i will keep reading the manual and stuff today some more at work.

can't believe i overlooked this function on the ags global functions page..  ::)

http://www.adventuregamestudio.co.uk/manual/IsInteractionAvailable.htm

Khris

Quote from: digitalray on Tue 12/04/2011 21:45:34
CursorMode GetDefaultInteractionAtScreenXY(int x, int y)

Just for reference: this is from monkey's Properties module, not a regular AGS command.

digitalray

#12
...

digitalray

#13
i tried again for an hour to get this function here running, but it just won't work..

Code: ags

function on_mouse_click(MouseButton button) { // called when a mouse button is clicked. button is either LEFT or RIGHT
  if (IsGamePaused() == 1) {
    if (GUI.GetAtScreenXY(mouse.x,mouse.y) != gInventory) // if game is paused and NOT in Inventory mode
    {} // do nothing
    if (GUI.GetAtScreenXY(mouse.x,mouse.y) == gInventory) {
        if (button == eMouseRight){
          if (mouse.Mode==eModeUseinv) {
            mouse.Mode=eModeLookat;  
            player.ActiveInventory=null;
          }
          else {
            ProcessClick(mouse.x, mouse.y, eModeInteract); // SELECT THE INVENTORY ITEM the mouse is currently on
            mouse.Mode = eModeUseinv; // make the cursor be the inventory object
          }
        }
        if (button == eMouseLeft) {
          ProcessClick(mouse.x, mouse.y, mouse.Mode);
        }
    }
  }  
  else {
    if (button == eMouseLeft) {  // LEFT MOUSE BUTTON
      ProcessClick(mouse.x, mouse.y, mouse.Mode); // DO LEFT CLICK WITH SELECTED MOUSE MODE
    }
    else if (button == eMouseRight){
      if (mouse.Mode!=eModeUseinv) {
        mouse.Mode=eModeLookat;  
        ProcessClick(mouse.x, mouse.y, mouse.Mode); // DO LEFT CLICK WITH SELECTED MOUSE MODE
      }
      else {
        mouse.Mode=eModeLookat;  
        player.ActiveInventory=null;
      }
    }
  }
}



what i want to do is select an inventory item with right mouse click.

what this function does:
check if the mouse x,y is over the inventory GUI.
- if yes: check if left or right mouse button was clicked
  - if the right mouse button was clicked on the inventory, the processclick is run with the mouse.mode interact and mouse mode (incl. cursor) set to mouse.Mode = eModeUseinv.

this is in fact the same command as if i would select the inventory mouse arrow (mouse.mode=interact) to select an item.

so why won't this work ?

if i do a left click lookat works and i get the display command for the item, strangely right click does the same ?
it seems that "if (GUI.GetAtScreenXY(mouse.x,mouse.y) == gInventory)" is ignored and the two else cases are run instead.


if the game is NOT paused (inventory not visible), the mouse.mode is selected automatically in another function.

Code: ags

function set_mouse_mode ()
{
  if (IsGamePaused() != 1) {
    if (GetLocationType(mouse.x,mouse.y) != eLocationNothing) { // MOUSE IS ON AN OBJECT OR HOTSPOT OR CHARACTER
      if (player.ActiveInventory!=null){ // IF INVENTORY OBJECT IS SELECTED
        mouse.Mode=eModeUseinv; // SELECT USE INVENTORY OBJECT MODE
      }
      else {
        if (IsInteractionAvailable(mouse.x,mouse.y, eModeInteract)) mouse.Mode=eModeInteract; // ELSE SELECT INTERACT MODE
        else mouse.Mode=eModeLookat; // ELSE SELECT INTERACT MODE
      }
    }
    else {
      if (player.ActiveInventory!=null) {
        mouse.Mode=eModeUseinv;
      }
      else mouse.Mode=eModeWalkto;  // IF CURSOR IS ON NO OBJECT SELECT WALK MODE
    }
  }
}

Khris

Turning off the inventory:

Code: ags
bool mouse_has_been_over_invgui;

void repeatedly_execute_always() {

  GUI*g = GUI.GetAtScreenXY(mouse.x,mouse.y);

  if (gInventory.Visible) {
    if (g == null && mouse_has_been_over_invgui) {   // mouse was moved away from inventory
      gInventory.Visible = false;
      mouse_has_been_over_invgui = false;  // reset flag
    }
    else if (g == gInventory) mouse_has_been_over_invgui = true;
  }
}


Without the flag, the inventory would turn immediately back off if it was turned on without being under the mouse.


Handling inv clicks:

-If you set the game so that inventory clicks are handled in script, interacting with an InventoryItem will no longer select it.

-button is only eMouseLeftInv or eMouseRightInv if the player clicked on an inventory item, even clicking an empty spot of the InventoryWindow won't trigger that.

What I do is this:

Code: ags
// inside on_mouse_click:

  InventoryItem*ai = player.ActiveInventory;
  InventoryItem*ia = inventory[game.inv_activated];   // item the player clicked on
  ...
  else if (button == eMouseLeftInv) {  // left click on an inventory item (ia)

    if (mouse.Mode == eModeUseinv) {  // use item on item
      if (ai != ia) ia.RunInteraction(eModeUseinv);  // only if item wasn't used on itself
    }
    else {  // pick up item
      player.ActiveInventory = ia;  // this also sets mouse.Mode to eModeUseinv automatically
    }
  }
  else if (button == eMouseRightInv) {

    if (mouse.Mode == eModeUseinv) {  // lose active inv
      player.ActiveInventory = null;
    }
    else {  // look at item
      ia.RunInteraction(eModeLookat);
    }
  }
  ...
}


Note that several commands aren't executed immediately but only after the current function has finished. Among them are Dialog.Start(), player.ChangeRoom() and, most importantly, ProcessClick().
Thus you can't use it to select the item, just set it as player.ActiveInventory instead.

digitalray

#15
ok, now i can select the inventory object with right click and left click shows the display message.

but i can't deselect it with right click if it is selected and i am somewhere in the inventory. it only deselect's it, if i am rightclicking on the inventory item itself.

how can i change it, so it get's deselected when right clicking anywhere in the inventory ?


got that thing with the inventory on my own just before you typed it and use my inv_yes bool ;)..

thanks a lot khris, i don't know.. but for me it seems, it's not really that easy for people who are not into coding to use ags with a different interface than ags default at all ;))

i coded an adventure engine back then in 92 together with my dad, he made routines in asm (like direct adressing the video memory for sprites) and i used them in turbo pascal ;)

i coded some bbs apps for proboard (.pex) the next years and some other stuff, went into c++ coding with directx9 after that, but ags is really not as easy as directx9, i have to say ;))

if you know ags very good, it may be simple, but for a newbie into ags.. i really didn't know where to get this from..

InventoryItem*ia = inventory[game.inv_activated];   // item the player clicked on


i still don't know where to look for them in the manual..

Khris

You should be able to deselect it if you right-click on any inventory item, not just the active one.
To deselect it over the GUI, use the GUI's OnClick function.
Open the GUI in the editor and double-click it.

This creates
Code: ags
function gInventory_OnClick(GUI *theGui, MouseButton button) {

}


In there, put:
Code: ags
  if (button == eMouseRight && player.ActiveInventory != null) player.ActiveInventory = null;

digitalray


Matti

What didn't work? What's the error you get?

digitalray

#19
no error, i inserted that line in the onclick event of the gInventory GUI.

it compiles, but when right clicking in the inventory with a selected item, the item is not deselected.
it still only deselects if right clicking over an inventory item.

i also tried with button == eMouseRightInv, but no change..

the gui is set to clickable, checked that.


i found out why it's not working..

it only works on the GUI (4 pixel edges), but not in the InventoryWindow, when no other items are in there to right click on.

seems that the InventoryWindow Area is an extra area and not the same as the GUI.


i would need a function to check if the mouse(x,y) are on the InventoryWindow area..

SMF spam blocked by CleanTalk