Unhandled Events how are they triggered? (SOLVED)

Started by PERXEO, Mon 17/04/2023 08:58:43

Previous topic - Next topic

PERXEO

I have a code snippet for unhandled_events, which I want to use to give special responses to certain unhandled events.
Code: ags
function unhandled_event(int what, int type)
{
String locname;

locname = Game.GetLocationName(mouse.x,mouse.y);
  int randomNumber=Random(100);
  int usemode=game.used_mode;
  gLabelAction.Text=String.Format("%s, %d, %d", locname, randomNumber, usemode);
...
}


The gLabelAction tag is a label that I use to show the typical text in the status bar, to indicate "Use xxxx" or "View xxxx" LucasArts style.
When I hover over a hotspot, the text initially appears fine, with the name of the hotspot, but when the player hovers over the cursor, the unhandled event is updated with the name of my player. This invalidates the usage because it now says things like "Use this Peter" or "Look at this Peter" instead of "Use this piano" or "Look at this piano" which would be desirable. The randonNumber is only to show me that the unhandled event changes over the time.
What triggers the unhandled-events? on a click? are they like repeatedly-executed? How can I restrict it to the first click on a hotspot?


PERXEO

Thanks, although I knew how the unhandled_event function works, what I was not clear about is "exactly" when the event is fired and I see that it jumps at any time when the action changes, not only on the initial click.

Crimson Wizard

#3
These events are normally fired when you interact with some object which does not have an interaction linked in. For instance, if you click "Look at", and the object does not have a "look at" reaction. The common use-case for them is to say something random, like "There's nothing of interest" or "I cannot use that".

QuoteI see that it jumps at any time when the action changes, not only on the initial click.

Could you elaborate, what do you mean by "action changes"?

Khris

Exactly, I can see no scenario where you would update a verb line inside unhandled_event.

The problem you describe is caused by the player being clickable and thus getting detected by Game.GetLocationName(), it's completely unrelated to unhandled_event.

What is extra confusing here is that you seem to understand exactly how unhandled_event is supposed to be used:
QuoteI have a code snippet for unhandled_events, which I want to use to give special responses to certain unhandled events.
then turn around and suddenly want to use it for a completely different, unrelated and unsuitable purpose...?

PERXEO

I really appreciate the interest you show in the doubts that newbies like me raise. What I mean by the fact that the event occurs with an action is the following, which I best explain with an example.
You click on a hotspot in the scene, with the verb see, in some x,y coordinates, for example. The AGS engine, which does not have this event to watch for this hotspot, collects the name of the element that is in said hotspot with this code:

Code: ags
function unhandled_event(int what, int type) 
{
String locname;
String
locname = Game.GetLocationName(mouse.x,mouse.y);

At this moment, the name of the hotspot on which the cursor is positioned is taken in the locname variable. But while the player character walks towards that point, if we move the cursor and interfere with it, the value of locname changes, because another unhandled event fires. That's why I asked "at what moment does the unhandled event fire" and I see that it is not only activated with the first click, but also with the fact that the player character crosses the path of the cursor.

Khris

Quote from: PERXEO on Mon 17/04/2023 15:29:17But while the player character walks towards that point, if we move the cursor and interfere with it, the value of locname changes, because another unhandled event fires.
No, that is absolutely not what's happening. unhandled_event doesn't fire because you're hovering the mouse over the player. It's repeatedly_execute(_always) that changes the label.

However: by default, when you click with an "active" cursor mode (look, interact, talk) on some hotspot while no related event is assigned, nothing happens at all. The player doesn't walk anywhere (unless you have a walk to point assigned to the hotspot I guess). At this point, AGS calls unhandled_event. Once.

There is no short answer to this problem because it depends on your game design.
We need to clarify how you want interactions to work in general.

Which template are you using?
Is the player always supposed to approach the hotspot, even if an "unhandled" action was picked? If so, how is that implemented? And is the approach going to be blocking, or can it be cancelled by clicking elsewhere?
Also, afaik, at this point the interaction isn't unhandled already, because what is making the player move there?

PERXEO

Sorry for my delay for answer but I had a hard day.
I've used an empty template and I did all from zero.
The purpose of my unhandled-event function is to prepare some variables like name of the object or hotspot or character in wich we interact, the content of some properties like genre and number of these objects and the mouse coordinates. All these parameters are send to some special functions like randomUse, randomSay, randomLook, and so on . My unhandled_event function is this one:

Code: ags
function unhandled_event(int what, int type) 
{
	String locname;
	String invname;
  int generoNumero;
  int theHotSpotID;
  int theObjectID;
  int theCharacterID;
  int theInventoryItemID;
locname = Game.GetLocationName(mouse.x,mouse.y);
  
  String hotSpotProperty;
  if (what==1) //HOTSPOT
  {
    Hotspot *theHotSpot = Hotspot.GetAtScreenXY(mouse.x, mouse.y);
    if (theHotSpot!=null)
    {
      theHotSpotID=theHotSpot.ID;
      generoNumero=hotspot[theHotSpotID].GetProperty("generoNumero");
  
    }else
    {
      Display("No hay un HOTSPOT");
      generoNumero=0;
    }
  }
  else if(what==2) //OBJECT
  {
      Object *theObject = Object.GetAtScreenXY(mouse.x, mouse.y);
      //Display("WHAT 2== %s ", theObject);
      if (theObject!=null)
    {
      theObjectID=theObject.ID;
      generoNumero=object[theObjectID].GetProperty("generoNumero");
     
    }else
    {
      Display("No hay un OBJETO");
      generoNumero=0;
      }
  }
  else if(what==3) //CHARACTER
  {
    
     Character *theCharacter = Character.GetAtScreenXY(mouse.x, mouse.y);
      if (theCharacter!=null && theCharacter!=cPerxeo)
    {
      theCharacterID=theCharacter.ID;
      generoNumero=character[theCharacterID].GetProperty("generoNumero");
    
    }else
    {

      generoNumero=0;
      }
  }
  else if (what==5)//objeto de inventario
  {
   InventoryItem  *theInventoryItem= InventoryItem.GetAtScreenXY(mouse.x,  mouse.y);
   if (theInventoryItem!=null)
   {
     theInventoryItemID=theInventoryItem.ID;
     generoNumero=inventory[theInventoryItemID].GetProperty("generoNumero");
    }else
    {
      Display("No hay un objeto de inventario");
      generoNumero=0;
     }
   }
	RandomLook(locname,  generoNumero,  mouse.x,  mouse.y);
	}
	else if ((what==1)&&(type==2)) {
    //si se interactua con algun hotspot

	RandomUse(locname,  generoNumero,  mouse.x,  mouse.y);
	}else if ((what==1)&&(type==3)) {

    //usar inventario en HOTSPOT OPCION 2 PARA HACER UNHANLED EVENTS ENTRE UN INVENTARIO Y UN HOTSPOT
    String cosa=player.ActiveInventory.Name;
    int idInv=player.ActiveInventory.ID;
    int generoInv=inventory[idInv].GetProperty("generoNumero");
	RandomInvUse(locname,  generoNumero,  cosa,  generoInv,  mouse.x,  mouse.y);
	}
	else if ((what==1)&&(type==7)) {
//COGER UN HOTSPOT
		RandomPickUp(locname,  generoNumero,  mouse.x,  mouse.y);
	}else if ((what==2)&&(type==5)) {
//COGER UN OBJETO
		RandomPickUp(locname,  generoNumero,  mouse.x,  mouse.y);
	}
  else if ((what==5)&&(type==0))
  {
    RandomLook(locname, generoNumero,  mouse.x,  mouse.y);  
  // mirar inventario  
  } 
   else if ((what==5)&&(type==3))
  {
    //usar objeto de inventario en otro
    player.Say("Usar eso asi, no tiene ningun sentido");
    RandomUse(locname, generoNumero,  mouse.x,  mouse.y);
  } else if ((what==1)&&(type==4))
  {
    RandomSay(locname, generoNumero,  mouse.x,  mouse.y);
  } else if ((what==2)&&(type==2))
  {
    RandomSay(locname, generoNumero,  mouse.x,  mouse.y);
  } else if ((what==5)&&(type==2))
  {
    RandomSay(locname, generoNumero,  mouse.x,  mouse.y);
  }
  

}

PERXEO

And one of the randomFunctions as example could be this one (the others are similar):

Code: ags
function RandomSay (String name,  int generoNumero,  int x,  int y)
{
  int random;
   String demostrativo;
   player.Walk(x, y, eNoBlock);
   player.FaceLocation(x, y, eNoBlock);
  if (generoNumero==1)
  {
    demostrativo="ese";
   }else if (generoNumero==2)
   {
     demostrativo="esa";
   }
   else if (generoNumero==3)
   {
     demostrativo="esos";
   }
   else
   {
     demostrativo="esas";
    }
   
// Genera un número aleatorio entre 0 y 5
	random = Random(5); 
  // Mostrar un mensaje correspondiente al número aleatorio
	if (random==0) player.Say("¿Qué puedo decirle a %s %s que tenga sentido?", demostrativo,  name);
	else if (random==1) player.Say("No quiero hablar con %s %s, creo no me va a ayudar.", demostrativo,  name);
	else if (random==2) player.Say("No se que decirle a %s %s. ", demostrativo,  name);
	else if (random==3) player.Say("¿Estás seguro de que quieres hablar con %s %s?", demostrativo,  name);
	else if (random==4) player.Say("¿Hablar con %s %s? ¿Estás loco?.", demostrativo,  name);
	else if (random==5) player.Say("No tiene ningún sentido hablarle a %s %s.", demostrativo,  name);
  Mouse.Mode = eModeWalkto;
}

Crimson Wizard

#9
I do not recommend doing things like "locname = Game.GetLocationName(mouse.x,mouse.y);" and "GetObjectAtScreenXY" in unhandled_event, because that is not exactly logical, as "unhandled_event" may be run for other reasons, not related to mouse clicks.

It's more reliable to remember the last clicked object etc in on_mouse_click, right before you run interaction over it, and save it in global variables.

EDIT: But maybe this is not important for your game, if it does not have such cases...

Also, only to mention this, you could avoid using unhandled_event at all, and make your own function(s) for handling this. Each interactable object has IsInteractionAvailable function, so you could even test this in on_mouse_click:
Code: ags
Object *obj = Object.GetAtScreenXY(mouse.x, mouse.y);
if (obj != null)
{
     if (obj.IsInteractionAvailable(mouse.Mode))
          obj.RunInteraction(mouse.Mode);
     else
          UnhandledObject(obj, mouse.Mode);
}

where "UnhandledObject" will be something like
Code: ags
function UnhandledObject(Object *obj, CursorMode mode)
{
}

Khris

#10
You probably have tons of duplicate code that way; you need to refactor your functions. If you find yourself copy-pasting large portions of code, you're on the wrong track.

Here's the approach I'd use, just for hotspots:

Code: ags
// always put code like this in its own function. If you made a mistake you fix one spot, not dozens
String Article(int genero) {
  if (genero == 1) return "ese";
  if (genero == 2) return "esa";
  if (genero == 3) return "esos";
  return "esas";
}

// core method: choose random message based on location type and cursor mode
void SayRandomMessage(String location, LocationType lt, CursorMode mode) {
  String message[6];
  if (mode == eModeLookat) {
    // ...
  }
  else if (mode == eModeTalkto) {
    if (lt == eLocationCharacter) {
      message[0] = "¿Qué puedo decirle a %s que tenga sentido?";
      message[1] = "No quiero hablar con %s, creo no me va a ayudar.";
      // ...
    }
    else message[0] = "Talking to inanimate objects? Nah.";
  }
  int ri;
  do { ri = Random(5); } while (message[ri] == null);
  player.Say(String.Format(message[ri], location));
}

// this doesn't have to be a separate function but I like to keep on_mouse_click as short and readable as possible
void UnhandledH(this Hotspot*, CursorMode mode) {
  String location = String.Format("%s %s", Article(this.GetProperty("generoNumero")), this.Name);
  // code to properly approach a hotspot goes here ;)
  SayRandomMessage(location, eLocationHotspot, mode);
}

And in on_mouse_click / eMouseLeft:
Code: ags
    int mx = mouse.x, my = mouse.y; // store in variables so the coords don't change
    LocationType lt = GetLocationType(mx, my);
    if (lt == eLocationHotspot) {
      Hotspot* h = Hotspot.GetAtScreenXY(mx, my); // now it's basically 100% guaranteed that h isn't null
      if (h.IsInteractionAvailabe(mouse.Mode)) h.RunInteraction(mouse.Mode);
      else h.UnhandledH(mouse.Mode);
    }

PERXEO

The dedication you have with newbies like me is incredible! Admirable! Thanks a million, I'll check it out right now!

SMF spam blocked by CleanTalk