Jibble

Author Topic: Custom Event  (Read 628 times)

Dualnames

  • AGS Baker
  • Rottwheelers
  • Pretty Badass
    • Dualnames worked on one or more games that won an AGS Award!
    •  
    • Dualnames worked on one or more games that was nominated for an AGS Award!
Custom Event
« on: 02 Apr 2019, 02:56 »
I'm trying to remember how this works, I remember vividly a module that had like 2 extra custom events appended to the on_event function.
Sorry for the creating of many topics lately <3

It's important that I'd have that, cause it would help me avoid 'working around' which means a lot of effort and code.
No more military army stuff. I'm alive and back.

Re: Custom Event
« Reply #1 on: 02 Apr 2019, 09:57 »
I have what may be a nice workaround, but never tested this myself.

You could reuse some of the built-in events and convert event's argument into custom event type.

For example, eEventGotScore may be less used event type. Call it passing your custom event id as a "score amount":
Code: Adventure Game Studio
  1. function CallCustomEvent(int custom_code)
  2. {
  3.      GiveScore(custom_code);
  4. }
  5.  

and then
Code: Adventure Game Studio
  1. function on_event (EventType event, int data)
  2. {
  3.      if (event == eEventGotScore)
  4.          event = 100 + data; // convert to custom event id
  5. }
  6.  

you could also probably use eEventAddInventory event for this, if you've got a room for dummy items that would mean custom events. Inventory items also have custom properties, which may let you pass all kinds of "event arguments" this way. E.g. you may spare 1 dummy item for this, and setup its properties to configure type of event you are sending.

Dualnames

  • AGS Baker
  • Rottwheelers
  • Pretty Badass
    • Dualnames worked on one or more games that won an AGS Award!
    •  
    • Dualnames worked on one or more games that was nominated for an AGS Award!
Re: Custom Event
« Reply #2 on: 02 Apr 2019, 19:09 »
How come i didn't think of the score event roflmao, yeah, that's a fantastic idea!
No more military army stuff. I'm alive and back.

Re: Custom Event
« Reply #3 on: 13 Jul 2019, 19:11 »
Hey, I think the code below may work, as simple complication of the CW idea! Add it at top of your scripts to be able to use events to ignore script order dependency to call and listen events.

CustomEvent.asc
Code: Adventure Game Studio
  1. Dictionary* events;
  2. Dictionary* id_to_event_map;
  3. int eventIDs;
  4.  
  5. void emitCustomEvent(String CustomEvent){
  6.   if(events==null){
  7.     events = Dictionary.Create();
  8.     id_to_event_map = Dictionary.Create();
  9.   }
  10.  
  11.   if(!events.Contains(CustomEvent)){  
  12.     events.Set(CustomEvent, String.Format("%d",eventIDs));
  13.     id_to_event_map.Set(String.Format("%d",eventIDs), CustomEvent);
  14.     eventIDs++;
  15.   }
  16.  
  17.   String event_id_asString = events.Get(CustomEvent);
  18.  
  19.   GiveScore(100+event_id_asString.AsInt);
  20. }
  21.  
  22. bool listenCustomEvent(EventType event, int data, String CustomEvent){
  23.   if(event==eEventGotScore && data>= 100){
  24.     int event_id = data - 100;
  25.    
  26.     if(id_to_event_map.Get(String.Format("%d",event_id)) == CustomEvent){
  27.       return true;
  28.     }
  29.   }
  30.   return false;
  31. }
  32.  

CustomEvent.ash
Code: Adventure Game Studio
  1. import void emitCustomEvent(String CustomEvent);
  2. import bool listenCustomEvent(EventType event, int data, String CustomEvent);
  3.  

To emit custom events

Code: Adventure Game Studio
  1. emitCustomEvent("MyCustomEvent");
  2.  


To listen to custom events

Code: Adventure Game Studio
  1. void on_event(EventType event, int data){
  2.   if(  listenCustomEvent( event, data, "MyCustomEvent") ){
  3.     // do something
  4.   }
  5. }
  6.  
« Last Edit: 13 Jul 2019, 19:31 by eri0o »

Re: Custom Event
« Reply #4 on: 13 Jul 2019, 19:25 »
eri0o, if you are making custom events map anyway, then you do not really need "on_event" and eEventGotScore, you may as well listen in repeatedly_execute and check whether events queue is not empty.

Re: Custom Event
« Reply #5 on: 16 Jul 2019, 13:33 »
That's a good point CW. This is the code I ended up using.

CustomEvent.ash
Code: Adventure Game Studio
  1. // Custom Event Module Header
  2. // Add enums as needed for your events
  3. enum CustomEventType {
  4.   eCET_DialogStarted,
  5.   eCET_DialogEnded,
  6. };
  7.  
  8. import void emitCustomEvent(CustomEventType customEvent);
  9. import bool listenCustomEvent(EventType event, int data, CustomEventType customEvent);
  10.  

CustomEvent.asc
Code: Adventure Game Studio
  1. // Custom Event Module Script
  2.  
  3. void emitCustomEvent(CustomEventType customEvent){
  4.   GiveScore(100+customEvent);
  5. }
  6.  
  7. bool listenCustomEvent(EventType event, int data, CustomEventType customEvent){
  8.    if(event==eEventGotScore && data>=100){
  9.      if(data-100 == customEvent){
  10.        return true;
  11.      }
  12.    }
  13.    
  14.   return false;
  15. }
  16.  

Khris

  • partook in silencing a crtitc despite facts
    • Lifetime Achievement Award Winner
    • I can help with play testing
    • I can help with scripting
    • I can help with translating
    • Khris worked on one or more games that was nominated for an AGS Award!
Re: Custom Event
« Reply #6 on: 16 Jul 2019, 13:46 »
I must be missing something because I don't see how any of this is an improvement over just calling a regular old function?
Fail at Floaty Rog' now!  still having to deal with what games are going through

Re: Custom Event
« Reply #7 on: 16 Jul 2019, 14:06 »
I must be missing something because I don't see how any of this is an improvement over just calling a regular old function?

IDK what's the original posters' intentions were, but in theory
1) this lets to connect independent modules that do not know about each other. If you add new module it already may reuse same events without modifying any existing code.
2) indirectly call function in module located below
3) maybe implement custom "claimable event" similar to built-in ClaimEvent function when the first module that processes event can claim it, preventing passing it to others.

Re: Custom Event
« Reply #8 on: 16 Jul 2019, 15:01 »
Yes, overall, I use for ignoring script ordering, and I think emiting and consuming events are good for things that don't happen often, instead of having to check conditions on repeatedly execute. They feel more organized too, for some cases.
« Last Edit: 16 Jul 2019, 15:18 by eri0o »

Dualnames

  • AGS Baker
  • Rottwheelers
  • Pretty Badass
    • Dualnames worked on one or more games that won an AGS Award!
    •  
    • Dualnames worked on one or more games that was nominated for an AGS Award!
Re: Custom Event
« Reply #9 on: 16 Jul 2019, 22:42 »
My intention was to be able to use on_event on several of my modules for my savegame system, but honestly this is a savesystem I had to code while the project is/was at the time more than halfway done, so it was harder to do it properly, to be fair I've already gotten an answer from you, CW, but I like that eri0o is discussing it, I think it's an interesting topic.
No more military army stuff. I'm alive and back.

Re: Custom Event
« Reply #10 on: 20 Sep 2019, 03:32 »
I will add a failed attempt at event listener here just because I like this topic...

So, I know we can't have function pointers right now in AGS, but I do know struct pointers exist... And we can extend structs. You may already guessed now what was my mistake, since I too thought it wouldn't work, still, I had to try it.

EventModule.ash , the header.
Code: Adventure Game Studio
  1. // event module header
  2.  
  3. /// an empty custom listener to be extended and have act() overloaded
  4. managed struct CustomListener {};
  5.  
  6. /// subscribe a new listener to an event
  7. import void AddEventListener(String eventName, CustomListener* listener);
  8.  
  9. /// emit an event
  10. import bool emit(String eventName);

EventModule.asc , the script, this is ugly, so I will hide with spoiler.
Spoiler: ShowHide
Code: Adventure Game Studio
  1. // new module script
  2. #define MAX_EVENT_COUNT 32
  3. #define MAX_EVENT_TYPES 64
  4. #define MAX_LISTENER_COUNT 16
  5. readonly int _max_events = MAX_EVENT_COUNT;
  6. readonly int _max_listeners = MAX_LISTENER_COUNT;
  7.  
  8. String _event_pipe[MAX_EVENT_COUNT];
  9.  
  10. struct Event_Listener{
  11.   int ListenerCount;
  12.   CustomListener* Listeners[MAX_LISTENER_COUNT];
  13. };
  14. Dictionary* _event_map;
  15. int _event_ids;
  16.  
  17. Event_Listener _event_listeners[MAX_EVENT_TYPES];
  18. int _event_count;
  19.  
  20. //to be overloaded later on an extended struct
  21. function act(this CustomListener*) {
  22.   Display("undefined CustomListener acted");
  23. }
  24.  
  25. bool emit(String eventName){
  26.   if(_event_count>=_max_events){
  27.     return false;
  28.   }
  29.  
  30.   _event_pipe[_event_count] = eventName;
  31.   _event_count++;
  32.   return true;
  33. }
  34.  
  35. void AddEventListener(String eventName, CustomListener* listener){
  36.   if(_event_map==null) _event_map = Dictionary.Create();
  37.  
  38.   if(!_event_map.Contains(eventName)){  
  39.     _event_map.Set(eventName, String.Format("%d",_event_ids));
  40.     _event_ids++;
  41.   }
  42.  
  43.   String event_id_asString = _event_map.Get(eventName);
  44.  
  45.   int listenerCount = _event_listeners[event_id_asString.AsInt].ListenerCount;
  46.   _event_listeners[event_id_asString.AsInt].Listeners[listenerCount] = listener;
  47.   _event_listeners[event_id_asString.AsInt].ListenerCount++;
  48. }
  49.  
  50. void ProccessEvents(){
  51.   while(_event_count>0){
  52.     _event_count--;
  53.     String event_id_asString = _event_map.Get(_event_pipe[_event_count]);
  54.        
  55.     int i=0;
  56.     while( i < _event_listeners[event_id_asString.AsInt].ListenerCount){
  57.       _event_listeners[event_id_asString.AsInt].Listeners[i].act();
  58.       i++;
  59.     }    
  60.   }  
  61. }
  62.  
  63. void repeatedly_execute(){
  64.   ProccessEvents();
  65. }
  66.  


The usage, on a room1.asc script.
Code: Adventure Game Studio
  1. // room script file
  2. managed struct BurnListener extends CustomListener{};
  3. void act(this BurnListener*) {
  4.   Display("Burn with fire");
  5. }
  6.  
  7. function room_Load() {
  8.   AddEventListener("fire",new BurnListener);
  9. }
  10.  
  11. function hGlowingOrb_AnyClick() {
  12.   emit("fire");
  13. }

So, because the pointer to the unextended (parent?) struct is passed, it points to the original extender function. So once fire is emitted, the original CustomListener.act() method is executed instead of the new BurnListener.act() being executed. Just exploring workarounds to create a neat JS like events API using AGS script. Not there yet, but it was fun to explore.

Re: Custom Event
« Reply #11 on: 20 Sep 2019, 14:53 »
So, because the pointer to the unextended (parent?) struct is passed, it points to the original extender function. So once fire is emitted, the original CustomListener.act() method is executed instead of the new BurnListener.act() being executed.

Well, yes, since AGS script does not support virtual inheritance or type information, base struct pointers do not know what child struct they are pointing to and you cannot call functions from child struct using base pointer.