Putting all code in scripts that are not the global script?

Started by tor.brandt, Sun 06/11/2016 23:19:33

Previous topic - Next topic

tor.brandt

I know there are some types of code that (at least per default) need to be in the global script, such as inventory item functions and GUI functions.

I get confused very easily, and therefore it would be of great help for me, if it was possible to put all types of code in other scripts (e.g. to make a script solely for inventory item functions, one solely for GUI functions, etc. etc.).

Therefore I'd like to hear if it should after all be possible in some way to work around the limitations of AGS, and put all types of code in scripts that are not the global script?

Thanks!

Gurok

I do it by using little stubs. My global script is just a series of lines like this:

Code: ags
/* gSaveGame */
void btnSaveGameSave_OnClick(GUIControl *a, MouseButton b) { GSaveGame.btnSaveGameSave_OnClick(a, b); }
void lstSaveGame_OnSelectionChanged(GUIControl *a) { GSaveGame.lstSaveGame_OnSelectionChanged(a); }
void btnSaveGameDelete_OnClick(GUIControl *a, MouseButton b) { GSaveGame.btnSaveGameDelete_OnClick(a, b); }
void txtSaveGameName_OnActivate(GUIControl *a) { GSaveGame.txtSaveGameName_OnActivate(control); }
void btnSaveGameListUp_OnClick(GUIControl *a, MouseButton b) { GSaveGame.btnSaveGameListUp_OnClick(a, b); }
void btnSaveGameListDown_OnClick(GUIControl *a, MouseButton b) { GSaveGame.btnSaveGameListDown_OnClick(a, b); }
void gSaveGame_OnKeyPress(eKeyCode a) { GSaveGame.OnKeyPress(a); }
/* gLoadGame */
void lstLoadGame_OnSelectionChanged(GUIControl *a) { GLoadGame.lstLoadGame_OnSelectionChanged(a); }
void btnLoadGameDelete_OnClick(GUIControl *a, MouseButton b) { GLoadGame.btnLoadGameDelete_OnClick(a, b); }
void btnLoadGameLoad_OnClick(GUIControl *a, MouseButton b) { GLoadGame.btnLoadGameLoad_OnClick(a, b); }
void btnLoadGameListUp_OnClick(GUIControl *a, MouseButton b) { GLoadGame.btnLoadGameListUp_OnClick(a, b); }
void btnLoadGameListDown_OnClick(GUIControl *a, MouseButton b) { GLoadGame.btnLoadGameListDown_OnClick(a, b); }
void gLoadGame_OnKeyPress(eKeyCode a) { GLoadGame.OnKeyPress(a); }


The stubs act like mappings from one function to another. I have a separate module for each GUI and a struct that's used to contain all methods for a GUI. For instance, inside my GSaveGame module, I have:

Code: ags
struct GSaveGame
{
	import static void btnSaveGameSave_OnClick(GUIControl *control, MouseButton button);
	import static void lstSaveGame_OnSelectionChanged(GUIControl *control);
	import static void btnSaveGameDelete_OnClick(GUIControl *control, MouseButton button);
	import static void txtSaveGameName_OnActivate(GUIControl *control);
	import static void btnSaveGameListUp_OnClick(GUIControl *control, MouseButton button);
	import static void btnSaveGameListDown_OnClick(GUIControl *control, MouseButton button);
	import static void OnKeyPress(eKeyCode keycode);
	import static void Show();
};


I'm sure there are other approaches, but this works for me. My global script is about 120 lines long and contains mappings for GUIs, inventory items and characters.

By the way, you can put functions like game_start and repeatedly_execute_always in any module and they'll get called.
[img]http://7d4iqnx.gif;rWRLUuw.gi

tor.brandt

@Gurok:

Thanks for your reply!

Alas, I do not really understand what you've written.
I do not yet understand what "void", "struct", and "import static void" mean.
Besides I've not yet quite cracked the nut regarding pointers (although I'm slowly beginning to use them in my scripting), and I can't really grasp the syntax of your stubs...
So much still to learn!

If you want to take the time to tell me, I'd really like to know more about how your example works, though :grin:

Gurok

Hi,

I'm sorry. I didn't mean to confuse with all of those imports and voids.

What I'm proposing isn't a particularly clever AGS trick of any kind. It's just an organisation method I find eliminates the confusion you mentioned.

Let's suppose you have this inventory item function in your global script:

GlobalScript.asc
Code: ags
function iBottleOfWater_Look()
{
  cEgo.Say("It looks like an ordinary bottle.");
  cEgo.Say("I'd better not touch it though.");
  cEgo.Say("It might explode!");
}


You can't put this function in another module. For it to be bound, it has to be in the global script. You can, however, write your own function in a separate script:

Inventory.asc
Code: ags
function Inv_iBottleOfWater_Look()
{
  cEgo.Say("It looks like an ordinary bottle.");
  cEgo.Say("I'd better not touch it though.");
  cEgo.Say("It might explode!");
}


And if you import the function in the header of your new script, it will be accessible to modules below it.

To import a function, just write out the first line again, and put "import" at the start and ";" at the end:

Inventory.ash
Code: ags
import function Inv_iBottleOfWater_Look();


Now, as I mentioned, you can't bind to a function inside "Inventory.asc" directly. You can do the next best thing, however. Now that "Inv_iBottleOfWater_Look" is accessible from your global script, you can replace the function in your global script with:

GlobalScript.asc
Code: ags
function iBottleOfWater_Look()
{
    Inv_iBottleOfWater_Look();
}


So now, looking at your bottle of water will call "iBottleOfWater_Look", which in turn calls "Inv_iBottleOfWater_Look" in the separate inventory script.

I like to call these small functions "stubs" and write them out on one line. The following is equivalent, but makes things a little easier to browse through:

GlobalScript.asc
Code: ags
function iBottleOfWater_Look() { Inv_iBottleOfWater_Look(); }


That's the gist of it. You'll have less code in your global script, but there will always be some code there.

There are downsides to this approach. It is more code to write out each time you bind a function. Bear this in mind. Weigh up whether organising your scripts is that important, and see if it's right for you.

The stuff about structs? You don't really need to worry about that. I use structs to help me organise things. The "Inv_" above does about the same thing. It is just a question of style.

As this question is all about organisation, you can also define regions in 3.4.0, which might be simpler. A region is a collapsible block (much like a function body). To define the region, use the #region pragma:

GlobalScript.asc
Code: ags
#region Inventory Functions

function iBottleOfWater_Look()
{
  cEgo.Say("It looks like an ordinary bottle.");
  cEgo.Say("I'd better not touch it though.");
  cEgo.Say("It might explode!");
}

#endregion


Click the [-] next to the #region line and all of the code inside will temporarily be hidden. That might help you stay organised without the extra work involved in moving things into separate modules.
[img]http://7d4iqnx.gif;rWRLUuw.gi

tor.brandt

Ah, I see.
Thank you for the elaboration!

And thank you for drawing attention to the region function - I wasn't aware that you could do that,
and that seems like a really simple and great way to keep order.
Actually, I think I will simply go with that method instead of splitting into multiple scripts then :smiley:

NicolaGs

I, also, thank you, Gurok.
I'm more and more convinced that one of the AGS's most important features is its great community...

I needed for some time to "clear" my GlobalScript mess but wasn't sure of how to do it without breaking everything in my project. Now, I know. Thanks...
My first game : I Want Out!

cat

Bookmarked! I really need to start tidying up my global script...

Khris

A slightly different method is using a single handler for multiple buttons:

Code: ags
// GlobalScript.asc
// handle all of Save GUI's buttons
void SaveGUIButton_OnClick(GUIControl *a, MouseButton b) { sgb_click(a, b); }


Code: ags
// module header
import void sgb_click(GUIControl *a, MouseButton b);

// module script
void sgb_click(GUIControl *control, MouseButton button) {
  if (button != eMouseLeft) return;
  Button *b = control.AsButton;

  if (b == btnSaveGameSave) {
     ...
  }
  else if (b == btnSaveGameCancel) {
    ...
  }
  ...
}


Now put "SaveGUIButton_OnClick" in each Save GUI button's event field.

tor.brandt

By the way;

It's really nice that I can put all my functions/function types in seperate regions, and then open one at a time to keep the script manageable, but it's a bit annoying that I have to close all the regions first thing every time I open AGS.

Is there a way to set the #regions to be closed per default when AGS opens?

SMF spam blocked by CleanTalk