Break out the global script into multiple, smaller scripts

Started by Intangible, Tue 19/04/2011 00:58:18

Previous topic - Next topic

Intangible

I've been dabbling with AGS for a few weeks now, following tutorials and figuring things out, and for the most part things are fairly straightforward. However, one thing I'm getting worried about is the size of the Global Script: I only have a couple of rooms, characters and items, and it's already getting close to 800 lines long. The global script is going to get huge if I put everything there.

I've been browsing the forum and dabbling with the import/export commands, but I haven't quite figured out what the answer is to this. AGS lets you add additional script files to your project, so I'd assume that there's a "best practice" for having your code in multiple script files, but so far I haven't found a working solution. I can declare an "import function" in the GlobalScript.ash and then implement it in GlobalScript.asc, and all of my rooms will have access to it. However, if I create a second script, "Test", it will not have access to the "import functions" I declared in GlobalScript.ash. Am I supposed to declare the import function in "Test.ash", as well? Or am I entirely barking up the wrong tree?

I just don't want to end up with a mammoth, 20,000 line GlobalScript.asc that has all of the code for all of my GUI, characters and item interactions... I was hoping to "farm things out" a little and make it more manageable (even moving all of that GUI stuff out into a separate script file would be pretty nice, but I was hoping to group my code into a number of different script files). Any guidance or recommendations you could provide would be greatly appreciated.

monkey0506

Well, to be honest, unfortunately the global script is presently the only place where Character, GUI, GUIControl, and InventoryItem event handlers can be placed.

That doesn't strictly mean that the global script is the only place that the logic for those event handlers, or the respective interactions, can be placed though, as you can call any existing functions from within the event handlers. So, you could put the code into separate scripts, but you would still have to call some functions from the global script or the events wouldn't be fired.

Regarding the use of multiple scripts, I've explained this somewhere else recently, but here goes again! :=

Every script file (except dialog and room scripts) have an associated script header (the .ASH file). So if you create a new script called "Test" then there will be two files: "Test.ash", the script header, and "Test.asc", the main script file.

The contents of your script headers are copied into the main script files at compile-time (when you click "Build"). The headers are also copied into every subsequent script file at compile-time (so, "Test.ash" would be copied into the global script, room scripts, and dialog scripts). Because of this, you should never define anything such as variables or functions in a script header. That's what imports are for. The exception to this rule is defining structs or enums. If you need a struct or enum to be accessible in other scripts, then it should go in a script header, because that is defining a new type, and other scripts won't know what it is unless it's defined in a header.

So, putting this together with your question about moving some of the event handlers into other scripts, you could do something like this. You could create a script called "CharacterEvents". In the CharacterEvents.asc you would define some custom functions to handle your various character events:

Code: ags
// CharacterEvents.asc

function cEgo_LookHandler()
{
  Display("I'm looking good!");
}

function cEgo_InteractHandler()
{
  Display("You rub your hands up and down your body.");
}

function cEgo_AnyClickHandler()
{
  Display("You..did something..with yourself. I don't feel comfortable saying more than that.");
}


We've added "Handler" to the normal function names so they don't conflict with the functions that AGS is going to use internally for the events. The next step for these functions is to import them so that your other scripts will have access to them.

Code: ags
// CharacterEvents.ash

import function cEgo_LookHandler();
import function cEgo_InteractHandler();
import function cEgo_AnyClickHandler();


Now you can call these functions from any script, provided it is below "CharacterEvents" in the script list (this also includes room and dialog script files). So, the final step to linking these events so that AGS will call them is to open up the global script. You should already have linked the events like you would normally, but don't put any interaction code there. Instead, do this:

Code: ags
// GlobalScript.asc

function cEgo_Look()
{
  cEgo_LookHandler();
}

function cEgo_Interact()
{
  cEgo_InteractHandler();
}

function cEgo_AnyClick()
{
  cEgo_AnyClickHandler();
}


As you can see, we're now using the normal AGS event functions to call our custom functions from the other script file. If you change the functions in CharacterEvents.asc, then the next time you test your game, you will see that the changes were correctly implemented!

Hopefully this helps explain how multiple scripts work, even though the event handlers for these functions are currently required to be in the global script.

Khris

On an additional note, you can link one function to two or more events. So to keep things tidy you could e.g. use a single function per inv item and character, like this:

Code: ags
// Global.asc

// A
function iKnife_event() {
  iKnife_Handler();
}

// B
function cMerchant_event() {
  Handle_Character(cMerchant);
}


You would then use mouse.Mode to determine which event was fired.

In the additional script, A) means having one handler function for every item/character, B) could be done to use a single function to handle all items/characters. Which one is a matter of personal preference though; A clutters up the function dropdown list, B) involves long if-else ifs.

Intangible

Thanks for the quick replies! I can definitely use these ideas to better organize my code; I can just use "stubs" for a lot the global script functions and write the main implementations elsewhere.

I was afraid this had probably been answered in a post somewhere else, but I was having a miserable time figuring out how to search for it ("code split", "different code files", "organize code"... searching can be tough sometimes  :) )

Intangible

I might have missed a trick, here, but I've hit a little wrinkle. I defined an import function in "FunctionLibrary.ash", and implemented it in "FunctionLibrary.asc". Now, I can call that function in "GlobalScript.asc", but I can't call it in "CharacterLibrary.asc", which is another script I created. Is there any way to create a true "utility function" that can be called from any script file, not just dialog/room/global script? Or do I have to create a local, non-imported "duplicate" of a desired function in any additional scripts that need to use it?

hedgefield

Well the thing is that the scripts are cascading. If you look at the list of scripts in the editor, everything you defined in the topmost script can be used by all scripts below it. But if you define something in the script at the bottom, only that script can use it (unless you move it up in the list).

Defining interdependant functions that way can be a real pain, I know :)

Intangible

Ah, I hadn't even noticed that you could reorder scripts. Thanks again! Is there a tutorial I missed about this sort of thing, or is it just one of those things you learn with experience?

Snarky

There's a section in the manual (available from within the AGS Editor under Help or by pressing F1) on using multiple scripts.

In general, good sources of AGS information include the manual, the AGS Wiki (which has a set of relevant articles), the forums, and densming's YouTube tutorials. However, I personally find that one of the best ways is to look at code other people have written, so check out some of the modules and open-source AGS games (e.g. the Oceanspirit Dennis games).

And yes, it can be difficult to find out where in all this the information you are looking for can be found.

Knox

#8
I asked pretty much the same question a good while ago when I was just starting out: http://www.adventuregamestudio.co.uk/yabb/index.php?topic=41336.0

As you gain experience, you'll be able to start making your own custom functions that greatly reduce redundant code + number of lines. I think there's a thread somewhere on "scripting tricks" that helped me out a lot in the beginning (like pressing shift-tab brings selected lines back, etc...)

Id be willing to share a few practical funtions with you if you wish!
--All that is necessary for evil to triumph is for good men to do nothing.


SMF spam blocked by CleanTalk