Adventure Game Studio

AGS Support => Beginners' Technical Questions => Topic started by: Ghostlady on Wed 30/10/2024 03:11:56

Title: What is the best way to handle multiple character mouse graphics?
Post by: Ghostlady on Wed 30/10/2024 03:11:56
I have two characters. Right now, the mouse graphics default is the same for both.  Is there a better way to change the mouse graphics for the 2nd character without putting this in every room?
if (player == cWoman) {
         mouse.UseModeGraphic(eModeDapHandU); }
Title: Re: What is the best way to handle multiple character mouse graphics?
Post by: Khris on Wed 30/10/2024 07:25:34
Not sure what you mean by "putting this in every room", the mouse is a global thing. It doesn't reset on a room change.

Anyway, the proper way is to (permanently) change the graphic of the mode when you switch the player character. Like:
function SwitchToWoman() {
  Mouse.ChangeModeGraphic(eModeInteract, 123); // sprite slot 123 is woman's interact cursor
  Mouse.ChangeModeGraphic(eModeTalkto, 124)); // sprite slot 124 is woman's interact cursor
  cWoman.SetAsPlayer();
}

Title: Re: What is the best way to handle multiple character mouse graphics?
Post by: Ghostlady on Thu 31/10/2024 05:04:17
When I make the change it only affects the mouse on an interact area, not the default mouse. I would want it to change for both.  The eModeDapHandU is a mouse cursor I set up.

Here was the code in the global script:
function repeatedly_execute() {
  // put anything you want to happen every game cycle here
  if (GetLocationType(mouse.x,mouse.y) > 0) {
//    Mouse.UseModeGraphic(eModePointer); }
    if(Mouse.Mode!=eModeUseinv) mouse.UseModeGraphic(eModePointer);}
  else {
    Mouse.UseDefaultGraphic(); }

Here is what I changed it to:
function repeatedly_execute() {
  // put anything you want to happen every game cycle here
  if (GetLocationType(mouse.x,mouse.y) > 0) {
//    Mouse.UseModeGraphic(eModePointer); }
    if(Mouse.Mode!=eModeUseinv) mouse.UseModeGraphic(eModePointer);}
  else  if (player == cWoman) { 
        mouse.UseModeGraphic(eModeDapHandU); }
  else  if (player == cMan) { 
        Mouse.UseDefaultGraphic(); }
Title: Re: What is the best way to handle multiple character mouse graphics?
Post by: Khris on Thu 31/10/2024 07:38:33
Here's a better way to do this:

bool wasOverActiveArea;
void HandleCursor() {
  if (Mouse.Mode == eModeUseinv) return; // do nothing
  bool isOverActiveArea = GetLocationType(mouse.x, mouse.y) != eLocationNothing; // compare to enum!
  if (isOverActiveArea && !wasOverActiveArea) {
    // mouse just moved over active area
    Mouse.UseModeGraphic(eModePointer); // pointer for both
  }
  else if (!isOverActiveArea && wasOverActiveArea) {
    // mouse just left active area
    if (player == cWoman) Mouse.UseModeGraphic(eModeDapHandU);
    else Mouse.UseDefaultGraphic();
  }
}

function repeatedly_execute() {
  HandleCursor();
  // other stuff
}
Title: Re: What is the best way to handle multiple character mouse graphics?
Post by: Ghostlady on Thu 31/10/2024 17:34:56
Hi, I added the change right before the repeatedly_execute and now the mouse for the female player is not working at all but using the default cursor.
Did I do this correctly?

SetGlobalInt(360,0);
  SetGlobalInt(370,0);
  SetGlobalInt(380,0);
  SetGlobalInt(390,0);
  SetGlobalInt(400,0);
  SetGlobalInt(410,0);
}
#sectionend game_start  // DO NOT EDIT OR REMOVE THIS LINE

bool wasOverActiveArea;
void HandleCursor() {
  if (Mouse.Mode == eModeUseinv) return; // do nothing
  bool isOverActiveArea = GetLocationType(mouse.x, mouse.y) != eLocationNothing; // compare to enum!
  if (isOverActiveArea && !wasOverActiveArea) {
    // mouse just moved over active area
    Mouse.UseModeGraphic(eModePointer); // pointer for both
  }
  else if (!isOverActiveArea && wasOverActiveArea) {
    // mouse just left active area
    if (player == cWoman) Mouse.UseModeGraphic(eModeDapHandU);
    else Mouse.UseDefaultGraphic();
} }

#sectionstart repeatedly_execute  // DO NOT EDIT OR REMOVE THIS LINE
function repeatedly_execute() {
  // put anything you want to happen every game cycle here
  //if (GetLocationType(mouse.x,mouse.y) > 0) {
//     Mouse.UseModeGraphic(eModePointer); }
  //   if(Mouse.Mode!=eModeUseinv) mouse.UseModeGraphic(eModePointer);}
  //else  if (player == cWoman) {   
 //       mouse.UseModeGraphic(eModeDapHandU); }
  //else  if (player == cMan) {   
   //     Mouse.UseDefaultGraphic(); }
   
   HandleCursor();
     
  if (player == cWoman) {
     StrFormat(herscore,"  Puzzles:  %d",GetGlobalInt(10));   
     ScoreHer.SetText(herscore);
     ScoreHer.TextColor = cWoman.SpeechColor; }
   
  if (player == cMan) {
     StrFormat(hisscore,"  Puzzles:  %d",GetGlobalInt(20));   
     ScoreHim.SetText(hisscore);
     ScoreHim.TextColor = cMan.SpeechColor; }
Title: Re: What is the best way to handle multiple character mouse graphics?
Post by: Khris on Sun 03/11/2024 17:07:57
In my snippet, the relevant lines are 7, 11 and 12.
Over active areas, the cursor should change to a pointer for both characters. Outside active areas, it should use the default one of the player is the man and the custom one when they're a woman.

What exactly fails?

I can't tell what "the mouse for the female player is not working at all" means exactly.
Title: Re: What is the best way to handle multiple character mouse graphics?
Post by: Ghostlady on Wed 02/04/2025 23:06:51
Hi, I got busy and did not have time to test this.  I am back on it and can't get it to work.
To refresh.  I currently have a blue hand cursor for all the hotspots, objects, etc. and an arrow cursor for when you are just walking around.  I want to be able to switch the graphic for the woman character with a red hand. I want this switch whenever the character interacts with a hotspot or object.  The cursor "pointer" is the man's blue hand and the cursor "DapHandU" is the woman's red hand. Right now with the script changed, I no longer get the arrow, only the hand, which should only be for hotspots etc.

I added a video to show how it is currently working, before the new changes, for better understanding. You will only see the blue hand in this video no matter who the character is.

https://www.youtube.com/watch?v=X9CLy6HqyYw

Here is the code in the script.   

#sectionend game_start  // DO NOT EDIT OR REMOVE THIS LINE

//New Area
bool wasOverActiveArea;
void HandleCursor() {
  if (Mouse.Mode == eModeUseinv) return; // do nothing
  bool isOverActiveArea = GetLocationType(mouse.x, mouse.y) != eLocationNothing; // compare to enum!
    if (isOverActiveArea && !wasOverActiveArea) {
    // mouse just moved over active area
    if (player == cWoman) Mouse.UseModeGraphic(eModeDapHandU);
    else if (player == cMan) Mouse.UseModeGraphic (eModePointer);
    else  Mouse.UseModeGraphic(eModePointer);
    else if (!isOverActiveArea && wasOverActiveArea) { Mouse.UseDefaultGraphic();  }
    // mouse just left active area
} }  
  //End New Area

#sectionstart repeatedly_execute  // DO NOT EDIT OR REMOVE THIS LINE
function repeatedly_execute() {
  // put anything you want to happen every game cycle here
 
  //New Area
     HandleCursor();
  //End New Area
 
//Old cursor logic
//  if (GetLocationType(mouse.x,mouse.y) > 0) {
//     if(Mouse.Mode!=eModeUseinv) mouse.UseModeGraphic(eModePointer);}
//  else {
 //     Mouse.UseDefaultGraphic(); }
//Old cursor logic end 
Title: Re: What is the best way to handle multiple character mouse graphics?
Post by: Khris on Thu 03/04/2025 07:46:54
Here's code that switches the cursor mode as opposed to the graphic:

bool wasOverActiveArea;
void HandleCursor() {
  if (Mouse.Mode == eModeUseinv) return; // do nothing
  bool isOverActiveArea = GetLocationType(mouse.x, mouse.y) != eLocationNothing; // compare to enum!
  if (isOverActiveArea && !wasOverActiveArea) {
    // mouse just moved over active area
    Mouse.Mode = eModeInteract;
  else if (!isOverActiveArea && wasOverActiveArea) {
    // mouse just left active area
    Mouse.Mode = eModeWalkto;
  }
}

Switching to the woman should call
  Mouse.ChangeModeGraphic(eModeInteract, SPRITE_SLOT_OF_RED_HAND);
and switching to the man should call
  Mouse.ChangeModeGraphic(eModeInteract, SPRITE_SLOT_OF_BLUE_HAND);
To recap: moving the mouse only switches cursor modes between walking and interacting, switching the player character changes the sprite of the eModeInteract cursor.

(As an aside, I find the Mouse.UseModeGraphic() command quite problematic. It essentially fools the player, and is used as a quick fix instead of properly coding mouse behavior. It shouldn't be used.)
Title: Re: What is the best way to handle multiple character mouse graphics?
Post by: Ghostlady on Fri 04/04/2025 03:43:54
I must be missing something.  Here's my code.  Basically the eModeWalkto area is not kicking in.  This should be my arrow and it never appears.  The correct color is showing for each character now so that means it is working correctly over an active area of the screen.  I also want to note that in my global script and in my rooms I have this: mouse.DisableMode(eModeWalkto); probably since it is a first person game. What is the mode that defaults for just moving room to room?

bool wasOverActiveArea;
void HandleCursor() {
  if (Mouse.Mode == eModeUseinv) return; // do nothing
  bool isOverActiveArea = GetLocationType(mouse.x, mouse.y) != eLocationNothing; // compare to enum!
  if (isOverActiveArea && !wasOverActiveArea) {
    // mouse just moved over active area
    Mouse.Mode = eModeInteract;
    if (player == cWoman)  Mouse.ChangeModeGraphic(eModeInteract, 485);
    if (player == cMan) Mouse.ChangeModeGraphic(eModeInteract,  16); }
  else if (!isOverActiveArea && wasOverActiveArea)
      Mouse.Mode = eModeWalkto;
      Mouse.ChangeModeGraphic(eModeWalkto, 22); {
    // mouse just left active area
  }
}
Title: Re: What is the best way to handle multiple character mouse graphics?
Post by: Ghostlady on Fri 04/04/2025 03:56:05
Also, I changed the eModeWalkto to Pointer and I get my arrow back but it only shows on the interface dropdown on top of screen.  It seems like the whole screen is being read as an active area.
Title: Re: What is the best way to handle multiple character mouse graphics?
Post by: Ghostlady on Fri 04/04/2025 05:15:13
Update:  I got it working by removing the "was over active area" statements.  On some screens I can see the graphic on the cursor switching images and the buttons on my save/load gui are now unclickable.

//New Area
bool wasOverActiveArea;
void HandleCursor() {
  if (Mouse.Mode == eModeUseinv) return; // do nothing
  bool isOverActiveArea = GetLocationType(mouse.x, mouse.y) != eLocationNothing; // compare to enum!
 // if (isOverActiveArea && !wasOverActiveArea) {
  if (isOverActiveArea)  {
    // mouse just moved over active area
  Mouse.Mode = eModeInteract; }
  else  if (!isOverActiveArea) {
  Mouse.Mode = eModePointer;
  Mouse.ChangeModeGraphic(eModePointer, 22); }
    // mouse just left active area
    //End New Area
}
#sectionstart repeatedly_execute  // DO NOT EDIT OR REMOVE THIS LINE
function repeatedly_execute() {
  // put anything you want to happen every game cycle here
 
  //New Area
    HandleCursor();
  //End New Area
 
//Old cursor logic
//  if (GetLocationType(mouse.x,mouse.y) > 0) {
//    if(Mouse.Mode!=eModeUseinv) mouse.UseModeGraphic(eModePointer);}
//  else {
//    Mouse.UseDefaultGraphic(); }
//Old cursor logic end 

//New cursor logic
  if (GetLocationType(mouse.x,mouse.y) > 0) {
    if ((Mouse.Mode!=eModeUseinv) && (Mouse.Mode == eModeInteract)) {
      if (player == cWoman)  Mouse.ChangeModeGraphic(eModeInteract, 485);
    else  if (player == cMan) Mouse.ChangeModeGraphic(eModeInteract,  16); }
 }
//New cursor logic end
     
Title: Re: What is the best way to handle multiple character mouse graphics?
Post by: Khris on Fri 04/04/2025 08:58:54
The HandleCursor function tracks changes in LocationType. Each time the mouse leaves or enters an active area, the relevant block is triggered and changes the cursor accordingly.

For a first-person game that doesn't use eModeWalkto, the code needs to be adjusted accordingly.

You said the cursor is supposed to be a hand over objects and the like, and a pointer sprite is used for walking around. How did you implement room changes? Because if room exits are hotspots, how does the game differentiate between exits and other hotspots? And which event are you using to process clicks? AnyClick? Or Interact?
Title: Re: What is the best way to handle multiple character mouse graphics?
Post by: Ghostlady on Fri 04/04/2025 15:52:31
1. I went through though the global script and found entries related to the mouse.  At the beginning I disable modes Walkto, Talkto and Lookat. 

2. I am using this to determine either arrow pointer or finger pointer. I am curious what defines Mouse User Default Graphic.

if (GetLocationType(mouse.x,mouse.y) > 0) {
    if(Mouse.Mode!=eModeUseinv) mouse.UseModeGraphic(eModePointer);}
  else {
    Mouse.UseDefaultGraphic();

3. There are multiple instances on Guis that the Mouse Mode is being selected.  Here's a few:
These deal with the Inventory box buttons:

function btnInvOK_Click(GUIControl *control, MouseButton button) {
 
    // They pressed the OK button, close the GUI
    gInventory.Visible = false;
    mouse.UseDefaultGraphic();
}
#sectionend btnInvOK_Click  // DO NOT EDIT OR REMOVE THIS LINE


function btnInvSelect_Click(GUIControl *control, MouseButton button) {
 
    // They pressed SELECT, so switch to the Get cursor
    mouse.Mode = eModeInteract;
    // But, override the appearance to look like the arrow
    mouse.UseModeGraphic(eModePointer);
}
#sectionend btnInvSelect_Click  // DO NOT EDIT OR REMOVE THIS LINE


#sectionstart btnIconCurInv_Click  // DO NOT EDIT OR REMOVE THIS LINE
function btnIconCurInv_Click(GUIControl *control, MouseButton button) {
 
  if (player.ActiveInventory != null)
    mouse.Mode = eModeUseinv;
}
#sectionend btnIconCurInv_Click  // DO NOT EDIT OR REMOVE THIS LINE


This is the icon bar or interface bar
// hide the icon bar and show a GUI
function open_gui(GUI* gui_to_open)
{
 // if (gui_to_open != gInventory)
 // {
 //  lblOverHotspot.Visible = false;
 // }

  gGui3.Visible = false;
  mouse.UseModeGraphic(eModePointer);
  gui_to_open.Visible = true;
}

// hide the GUI and show the icon bar
function close_gui(GUI* gui_to_close)
{
  gui_to_close.Visible = false;
  mouse.UseDefaultGraphic();
  gGui3.Visible = true;
//  lblOverHotspot.Visible = true;
//  gIconbar.Visible = true;
}

4. For movement in the rooms, when I turn left, right, up or down, and the curser finger pointer turns direction, I have created User modes with the different pointing direction graphic. 



Multiple hotspots/entryways
if (Hotspot.GetAtScreenXY(mouse.x, mouse.y) == hrighthall) {
      mouse.UseModeGraphic(eModeUsermode2); }

if (Hotspot.GetAtScreenXY(mouse.x, mouse.y) == hlefthall) {
      mouse.UseModeGraphic(eModeUsermode3); } 

if (Hotspot.GetAtScreenXY(mouse.x, mouse.y) == hdownstairs) {
      mouse.UseModeGraphic(eModeUsermode5); }



5. To sum this all up, I think #2 above is the "all control of the pointer vs finger and any changes to that scenario will be notated in the global script or the room script.  If this is true, then I don't understand how it works on hotspots and objects based on only GetLocationType.

Title: Re: What is the best way to handle multiple character mouse graphics?
Post by: Khris on Sat 05/04/2025 09:31:11
First of all, this:
  if (GetLocationType(mouse.x,mouse.y) > 0) {
might cause issues. Afaik, enum values start at 1 by default, so unless set to 0 by the engine explicitly, eLocationNothing might have a value > 0.
To be safe, change this to
  if (GetLocationType(mouse.x,mouse.y) != eLocationNothing) {
Next, the exit arrow stuff doesn't require code in every room if you use a hotspot custom property. Global behavior should never be implemented on a per-room basis, that's just loads of duplicate code. If you use a second property to store the room number the exit is leading to, you don't even need to have event functions for the exit hotspots, just some global code that detects a click on an exit.

Anyway, as far as I can tell you're using just eModeInteract and eModeUseinv and keep changing the cursor sprite.
This approach is fine but could be streamlined a great deal.
For instance the cursor sprite can simply be changed by using Mouse.ChangeModeGraphic(CursorMode, int slot); there's no need to set up custom cursors just to use their assigned sprite.
Title: Re: What is the best way to handle multiple character mouse graphics?
Post by: Ghostlady on Sat 05/04/2025 22:01:32
Ok...so I got this figured out but I still don't understand. See code below.  If I use Mouse.UserModeGraphic with my custom cursor mode it works.  If I use Mouse.ChangeModeGraphic with the sprite # and with the Interact Mode, I lose my arrow. (see commented out lines)

//New cursor logic
  if (GetLocationType(mouse.x,mouse.y) != eLocationNothing) {
    if(Mouse.Mode!=eModeUseinv)  {
      if (player == cWoman)  Mouse.UseModeGraphic(eModeDapHandU);
//    if (player == cWoman)  Mouse.ChangeModeGraphic(eModeInteract, 485);
      if (player == cMan) Mouse.UseModeGraphic(eModePointer); } }
//    if (player == cMan) Mouse.ChangeModeGraphic(eModeInteract,  16); } }
  else { Mouse.UseDefaultGraphic (); }
  //New cursor logic end

Here are two videos showing working correctly and incorrectly (when I lose the arrow).

Correct:  https://youtu.be/EQFJbFcnn2A

Inccorrect: https://youtu.be/7I21LJAYhQE