use a string variable to call a function

Started by Dadalus, Thu 21/08/2014 12:50:05

Previous topic - Next topic

Dadalus

I have been looking for an answer to this by searching the manual, and the forums but cant find anything. I'm hoping someone can give me a simple yes/No answer.

Can you call a function using a string without using a lot of  if...else if...statements, something along the lines of this:

Code: ags
//Pseudo code
CallFunction("fMyFunction");


I realize you could use:
Code: ags
if (strFunction="fMyFunction1")
{
  fMyFunction1();
}
else if ((strFunction="fMyFunction2")
{
  fMyFunction2();
}...


but I think thats unwieldy.


This has been a 'Mouse fetishist' approved message.

Crimson Wizard

AGS Script can't do this at the moment.

Dadalus

Thanks for the quick and simple response. I thought I was missing something :)
This has been a 'Mouse fetishist' approved message.

Khris

What are you trying to do? I'm sure there's a simple workaround for it.

Dadalus

#4
Ive built a simple "event handler" so that anything that needs doing (in repeatedly_execute) only happens once per game loop. I'm using a simple list to tell me which event has to be handled if the event cant be handled its pushed to the end of the queue. There are only 10 "events" so I'm using IF..ELSE IF to call the appropriate function that handles each "event". Its working really well and greatly speeds up the game. It also helps when you need to wait for something (eg waiting for the player character to stop moving) and with creating non blocking code.

What I am thinking of doing now is building some kind of triggering system for puzzles. My idea is to create a trigger struct with links to those triggers that depend on it and those that it depends on. Then when something is triggered it calls those that depend on it to check if their dependencies are all triggered (I think that sentence is correct :)). The effect of changing the state of one trigger would then be tracked through all the triggers that are affected.

I could build something like it using the principles from my 'event handler' code. I wanted to be able to store a string within my struct that has the name of the handler function for a particular trigger (to deal with any sequence of commands that occur when a triggers state is changed). I didn't want to have 100s of if..else if commands.

I have considered using the rooms from 300 to 999, and the on_call function. Thats just an idea at this stage I haven't done any testing to see if its possible.

The other idea I had was to build a plugin to create a 'CallFunction' command. I wouldn't need to pass any parameters. I'm not overly familiar with C++, my background is in VB, so I am a little wary of doing it even though it should be simple. Its also been 7 years since I did any serious coding so I'm rustier than a rednecks pick up truck.

I don't want anyone to write the code for me (I will never learn all the Ins and Outs of AGS if I rely on others to code for me :)), but I'm always grateful for any thoughts on the correct approach to tackle a problem.


This has been a 'Mouse fetishist' approved message.

Crimson Wizard

#5
Having system like that in mind, you may consider using Lua plugin instead: http://www.adventuregamestudio.co.uk/forums/index.php?topic=38765.0

I am not saying it's impossible to do in AGS Script, but could appear cumbersome.
We are working to bring more things to AGS, like pointers to user structs and possibly pointers to functions, but I can't predict how much more time that will take.

Dadalus

I guess what I'm trying to do is build a state machine, and there are probably libraries for Lua. Ill do some research.

Good to see that this may be available in the future.

Now on to EVAL.. :) only kidding.
This has been a 'Mouse fetishist' approved message.

Snarky

There are several state machine modules for AGS, actually...

www.adventuregamestudio.co.uk/forums/index.php?topic=43948
http://www.adventuregamestudio.co.uk/forums/index.php?topic=21282.msg259991#msg259991

Gilbert

I think to build state machines, instead of allowing the calling of a function by its names in a string, a better possible enhancement to the engine is to have the switch command. Things will be much cleaner and readable than using if...else with it.

Dadalus

#9
Snarky thanks for the links. Ill have a look at them to see how others are tackling the same problem. I want to limit the use of other peoples code (for reasons I stated above), so will probably build my own version. Its probably not quite a state machine I'm after but very close. I am not overly familiar with the concept of state Machines (researching them at the moment). Creating my own version will give me a better understanding of how it works.

Iceybot I agree it would be nice but its not essential.

Quick note on calling a function using a variable. My thoughts on using rooms wouldn't work as using on_call only works within the room. I did conduct a test using a characters UserMode8 and Run Interaction. I found I could make a workaround (using the character ID instead of a string), the only trouble was it as widely more unwieldy than if...else... :)

Thanks for the advice all.
This has been a 'Mouse fetishist' approved message.

Crimson Wizard

Quote from: Dadalus on Fri 22/08/2014 10:19:58I did conduct a test using a characters UserMode8 and Run Interaction. I found I could make a workaround (using the character ID instead of a string), the only trouble was it as widely more unwieldy than if...else... :)
This sounds like something strange... can you elaborate how do you use the "user mode" and "character id instead of string"?
A piece of code maybe?

Dadalus

#11
I should have said 'UserMode1" it comes up as Mode8 in the code.

1. Set up a blank character, called "trigger001" or whatever you want.
2. set up the event usermode1 with a call to the function.
Code: ags
function trigger001_Mode8()
{
  fMyFunction001();
}


3. Then within struct for triggers store an int with the character ID.
4. Whenever you need to call a function from this variable use:
Code: ags
character[MyTriggers[1].CharacterID].RunInteraction(eModeUsermode1);


You would need a separate character for each trigger.

Cumbersome, wasteful and inelegant. :)

This has been a 'Mouse fetishist' approved message.

Gurok

Oh, you could probably (ab)use GiveScore and the eEventGotScore event to achieve the same effect. It might even be a bit simpler.

GiveScore(1) to activate the first function, GiveScore(2) to activate the second.

In on_event, a series of if/else statements for the eEventGotScore event, checking what the value of data was.

Of course, this would break the built-in scoring mechanism, but it's pretty easy to write your own if you need one ;-).
[img]http://7d4iqnx.gif;rWRLUuw.gi

Crimson Wizard

#13
That's one of the most awesome workarounds I saw in AGS.... You are basically creating an analogue of functor, or delegate.

But you may also store character pointer instead of id.
Then it will be
Code: ags

MyTriggers[1].Char = cTriggerChar;
....
MyTriggers[1].Char.RunInteraction(eModeUsermode1);


Dadalus

#14
Crimson Wizard :Didn't know about 'char' that would be a better way of doing it. (Not sure if its awesome though, but thanks for the compliment :))

Gurok : Thanks for that another bit of AGS I wasn't aware of (there are sooooo many) I think that would be simpler.

But its a mute point as I cant see me using this approach its way more time consuming and cumbersome than the if..else if.. approach.
This has been a 'Mouse fetishist' approved message.

Crimson Wizard

#15
Quote from: Dadalus on Fri 22/08/2014 11:09:25
Crimson Wizard :Didn't know about 'char' that would be a better way of doing it.

I'll elaborate just in case. "Char" is just a name of variable I invented right now. I was speaking of using a Character* pointer:
Here how it is declared:
Code: ags

struct Trigger
{
    Character *ch;
}

Then you may assign any character to it.


BTW (just having fun with ideas) you may also use Dialogs and dialog script. If the dialog has only one enabled option, that option will run automatically, without showing up selection. Thus you may have a dialog with multiple options, turn them on and off as the game progresses, and run certain dialog script when needed.
You can have pointers to dialogs too.
I wonder if that will work... :) never tried myself.

Dadalus

#16
I understood what you meant with char but the clarification was nice.( EDIT:No it turns out I didn't :) just looked up the char data type)

As for using dialogs thats a great idea, I may do some testing along those lines.
This has been a 'Mouse fetishist' approved message.

Khris

Actually, I think the Character* workaround is not that bad. You can use multiple events, not just UserMode1, and you can enter fMyFunction001(); directly into the event table; you don't have to use function trigger001_Mode8() since all it does is call the actual function.

If you use five standard actions: lookat, interact, talkto, pickup, useinv, you won't need tons of blank characters either.

Code: ags
struct Event {
  int triggerID; // consecutive number, starting at 1 (lookat first trigger char)
  import void Trigger();
};

void Event::Trigger() {
  int actions[5];
  // alphabetical order, just like in the editor
  actions[0] = eModeInteract; actions[1] = eModeLookat; actions[2] = eModePickup;
  actions[3] = eModeTalkto; actions[4] = eModeUseinv;
  // first trigger char has ID 10
  character[(this.triggerID-1) / 5 + 10].RunInteraction(actions[(this.triggerID-1) % 5]);
}


Now you can freely add functions to the characters' events, and name them according to the respective ID:
character 10's interact event calls fMyFunction001()
character 10's lookat event calls fMyFunction002()
character 11's interact event calls fMyFunction006()
etc.

When you setup your events, just give them consecutive IDs and call .Trigger() on them to run the associated function.

Dadalus

#18
Khris : I like your thoughts on using more than one character event (I'm using walk,talk,look,interact but the others are free EDIT: Im being silly of course I can use the walk,talk etc events, doh!), and I wasn't aware that you could enter the function directly .

Id considered using consecutive characters but thought I might have problems with any expanding of the game (I would need to have all my game characters set up before my trigger characters, or pad with lots of blank characters for later expansion). I'm developing the story/characters etc at the same time as the game. I have the first section fully planned and an overall plot, but I may need to add game characters later.

I could probably just modify the code your suggesting to include the character as part of the struct (along the lines Crimson Wizard suggested), though it would make it a lot easier if they were consecutive.

Everyone: On a side note, the original post was just to get a simple Yes/No answer, who knew Id get so much out of it! Ive been very interested in your replies/views. Thank you.
This has been a 'Mouse fetishist' approved message.

Crimson Wizard

#19
In a worst case, if you really want to force a consecutive range, you may implement a helper conversion array.
Something like -
Code: ags

#define MAX_TRIGGER_CHAR 10
Character *TriggerCharacters[MAX_TRIGGER_CHARS];

...

TriggerCharacters[0] = cTrigger1;
TriggerCharacters[1] = cTrigger2;
...etc..

// Using trigger character:
Character *ch = TriggerCharacters[this.triggerID-1) / 5];
ch.RunInteraction(actions[(this.triggerID-1) % 5]);


This way you won't depend on characters order in your project.

SMF spam blocked by CleanTalk