Callbacks

Started by duckwizard, Thu 10/03/2011 23:43:18

Previous topic - Next topic

duckwizard

I wasn't able to use QueueScriptFunction for what I originally wanted, but it did come in handy for something else - namely callbacks.

This is a really simple plugin (surprised it hasn't been done already, judging by my scan of the plugins board).  It exports two functions

void ExecuteFunction(String fnName, int global)
void ExecuteFunctionParam(String fnName, int global, int param)

The first form just executes the function called fnName.  If global is zero, it executes that function in the current room script.  Otherwise, it executes that function in the global script.

The second form does the same, but allows you to pass a single int parameter to the function you want to call.

Here is the DLL if anyone is interested.

Example use case for callbacks:

My character has a wallet with a certain amount of money in it.  When he uses the wallet on something, I want a GUI to come up that asks him how much money he wants to give.  The GUI has a "give" button that closes the wallet GUI.  And then what happens?

Without callbacks, you probably stored some variable that said whom or what you were giving the money to. Then, in the WalletGiveButton_OnClick handler you do ALL the logic for every possible thing you could give money to.

With callbacks, your OpenWallet function takes the name of the function that you want to be called when money is given:

Code: ags

//room5.asc
function oBeggar_UseInv()
{
	if(cEgo.ActiveInventory == iWallet) // not an apple product (yet)
	{
		OpenWallet("gaveBeggarMoney");	
	}
}

function gaveBeggarMoney(int howMuch)
{
	if(howMuch < 20)
	{
		Display("What a cheapo!");	
	}
	else
	{
		BeggarGivesTreasureMapOrSomething();	
	}	
}

//GlobalScript.asc
String walletCallback;
function OpenWallet(String roomCallback)
{
	walletCallback = roomCallback;
	gWallet.Visible = true;
}

function btnWalletGive_OnClick()
{
	if(walletCallback == null) return;
	ExecuteFunctionParam(walletCallback, 0, sldWalletAmount.Value);
	walletCallback = null;
	gWallet.Visible = false;
}



Now whenever you want to open the wallet from any room script, you can just pass in a string with the name of the room function that you want to be called when the player has selected the money amount.  The money amount will be passed in to that function.  Whee!

Tiny source code (didn't bother to rename the template source file)

Hereby public domain (on my end anyway - source contains a copyright message from CJ), feel free to use, improve, or lambaste me for making something useless :)

For example, you could add a way to call a function with two parameters as that's supported by QueueScriptFunction.  But I don't have a need for it right now, so I didn't.

Edited: forgot to clean up wallet GUI in use case code; it probably would have crashed.  Be careful :)

Khris

Just for reference, I'd define an enum with all the room events triggered by global stuff, then use CallRoomScript / on_call().

Example:

Code: ags
// header

enum RoomEvent {
  eREgaveBeggarMoney,
  ...
};

// GUIButton_OnClick:

  CallRoomScript(eREgiveBeggarMoney);

// room

void on_call(int re) {
 
  if (re == eREgiveBeggarMoney) {
    ...
  }
}


I don't think that method is less elegant than yours; plus the newest version allows code folding so IMO having several if-elses is preferable to having several functions.

Plus, you can always split the logic into several functions; nowhere does it say you have to handle all of it in a single if-else if mess. In fact that's what I'd do anyway if I had tons of possible events like that; the thing is you rarely are in that situation and even then, the logic in this case is already split among rooms.

duckwizard

Quote from: Khris on Fri 11/03/2011 00:23:11
Just for reference, I'd define an enum with all the room events triggered by global stuff, then use CallRoomScript / on_call().

You're right, that's a decent way of doing it as far as readability is concerned.  I still like callbacks better personally, and the plugin took 5 minutes to make, so why not?  And I figured some people might want to do it who don't have a C++ compiler, so... *shrug* To each his own, I guess (and my "own" is to avoid on_call wherever possible because I hate it for some reason I can't explain)

Quote from: Khris on Fri 11/03/2011 00:23:11
Plus, you can always split the logic into several functions; nowhere does it say you have to handle all of it in a single if-else if mess. In fact that's what I'd do anyway if I had tons of possible events like that; the thing is you rarely are in that situation and even then, the logic in this case is already split among rooms.

Sure, but even if you split the logic up, you still need the if-else to call the functions that you moved the logic into.  I would even argue that the enums and the callbacks are functionally equivalent - both use a variable to decide what function to call.  The callbacks simply remove the need for the switch block by storing the actual function rather than a numeric abstraction if it.

Plus, the plug-in has the bonus of crashing if you use it wrong :)

monkey0506

Quote from: duckwizard on Fri 11/03/2011 00:52:07Plus, the plug-in has the bonus of crashing if you use it wrong :)

Sold! :D

SMF spam blocked by CleanTalk