TTLG|Thief|Bioshock|System Shock|Deus Ex|Mobile
Results 1 to 10 of 10

Thread: Understanding Squirrel Scripts

  1. #1
    Member
    Registered: Jan 2001
    Location: Formby, NW England

    Understanding Squirrel Scripts

    Squirrel script has some documentation, but for people who don't do programming some things may be confusing. The purpose of this thread is to explain things so more people can have a go and know what they're doing, rather than just copying and pasting code samples. It isn't my intention to explain everything myself; other people are welcome to explain other things.

    The readme explains the basic structure of a script, but I'd like to explain one thing:
    Code:
    class MyScript extends SqRootScript
    {
    }
    "extends SqRootScript"
    Extending another script allows MyScript to access all of the functions of that script. You can think of it like the Object Hierarchy, where Door10 inherits the properties of door4x8, which inherits from spinny_door and each of its other parent archetypes.

    Getting a script to do something.
    Scripts respond to messages. Some messages are sent to all objects by default, such as Sim and Create. Other messages are sent by some trigger, e.g. a button sends a TurnOn message to all objects on the other end of a ControlDevice link.

    For a Squirrel script, we can set up functions that respond to these standard messages. E.g:

    Code:
    class MyScript extends SqRootScript
    {
        function OnTurnOn()
        {
            something...
        }
    }
    The { and } are used to define the start and end of each thing (the script, the actions of each of its functions, etc).

    Within a function you can call other functions. As well as functions you've written yourself, there are plenty of existing functions. Those ones are listed in API-reference_services.txt

    In that file is a set of blocks laid out like this:
    Code:
    Thing
    {
        something_1
        something_2
        ...
    }
    "Thing" is a class name
    each of the "somethings" is a function you can call. Example:
    Inside Object {...} there is
    Code:
    ObjID Create(object archetype_or_clone);
    The fist part (ObjID) is the type of value the function generates, the second part (Create) is the function's name. Inside the brackets are the required parameters. Each parameter starts with the type of thing and a name which the function uses as an alias.
    In this case, there is only one parameter, whose type is object.

    What is an object?
    Open up API-reference.txt.
    an ObjID or an object name string
    We are told that an ObjID is just an integer, and that there some other types, e.g.LinkID, that are also just integers.

    To explain how to use parameters, a useful example is creating an object at a specific location. There isn't a single function for this, so two have to be used. Object.Create and Object.Teleport.

    Object.Create("WoodCrateSealed"); will create that object at 0,0,0.

    Object.Teleport requires an object (ID or name), two vector types, which allow the location and orientation of the object to be set, and another object. You'll notice the parameter list has this: object ref_frame = 0. The use of = means the parameter is optional, and if no value is supplied, in this case a value of 0 will be used. This object can be used to have the crate teleported somewhere relative to the ref_frame object.

    Object.Create returns an object, so that can be used to generate the first parameter for Object.Teleport.

    Do something like this:
    local crateObj = Object.Create("WoodCrateSealed");
    Object.Teleport(crateObj, ...)

    local creates a variable which can only be accessed from within the current pair of { and }, and any pairs inside them, but not outside. Its type is determined by the value it's given, so in this example crateObj is of the type ObjID.

    The next parameter, position, has to be a vector, which can be created by calling the vector function. In API-reference.txt, go to the GLOBAL FUNCTIONS section. Here you'll see that there are three ways to create a vector:
    vector() which generates a vector type with values of (0,0,0). The other two versions of this function allow other values to be used, so a specific location can be set.

    The next parameter, facing, can be set in the same way. The three values refer to the object's axes, not heading, pitch and bank, so vector(0,0,45) will rotate it about the Z axis.

    As mentioned, the ref_frame parameter is optional, but it can be useful, as this shows:

    The self object.

    When a script requires an object parameter, you can use the keyword self (which does not use double quotes) to mean 'the object that has this script'.

    Code:
    class MakeWoodCrate extends SqRootScript
    {
        function OnTurnOn()
        {
            local crateObj = Object.Create("WoodCrateSealed");
            Object.Teleport(crateObj, vector(), vector(), self);
        }
    This will create a WoodCrateSealed at the location of the object with the script. It's a very basic script but hopefully it shows you how to use the documentation to work out what you can use, and how you can use it.

  2. #2
    Member
    Registered: Mar 2001
    Location: Ireland
    I also recommend that anyone interested in Squirrel scripting read An Introduction to Scripts .
    Most of it should also apply to scripts written in Squirrel.

  3. #3
    ZylonBane
    Registered: Sep 2000
    Location: ZylonBane
    It would probably be useful to create a "Squirrel Cookbook" sort of document that described how to script various common tasks.

  4. #4
    Member
    Registered: Jan 2012
    Location: Gèrmany
    I would like to link to the documentation which helped me to learn squirrel:

    Official documentation:
    http://www.squirrel-lang.org/doc/squirrel3.html Not the most reason version but it's a big html website and you can find stuff with ctrl+f. Most recent documentation: (Online or PDF)

    Additional:
    The official documentation is lacking examples here comes a real bushiness website which does the necessary additions:
    https://electricimp.com/docs/squirrel/

    On the right you can click and learn about the different datatypes/libraries which are available for squirrel. Also you will find find some better explanation+examples of the so called built-in functions like string.slice(start,end). Which are only roughly documented in the official documentation.


    Last but not least the site provides a 2-side "Cheat sheet" with all the basic knowledge you need for programming: datatypes, operators, syntax...

    https://electricimp.com/docs/attachments/images/squirrelrefsheet.pdf
    Last edited by Daraan; 10th Apr 2017 at 05:21.

  5. #5
    Member
    Registered: Jan 2001
    Location: Formby, NW England
    I had a go at doing a script that allows a custom [script name]On parameter, specified in a base class which can be derived from.

    Here's an example, but the script itself doesn't do anything.

    Code:
    class AllYourBase extends SqRootScript
    {
    	function getOnMessage()
    	{
    		local onMsg = "TurnOn"; //default
    		if (HasProperty("DesignNote"))
    		{
    			local scrNameOn = GetClassName() + "On";
    			if(scrNameOn in userparams())
    			{
    				onMsg = userparams()[scrNameOn];
    			}
    		}
    		return onMsg;
    	}
    }
    
    class UselessScript extends AllYourBase
    {
    	function OnMessage()
    	{
    		if(MessageIs(getOnMessage()))
    		{
    			... do things here ...
    		}
    	}
    }
    "UselessScript" goes on the object. When it receives any message (which magically triggers the OnMessage() function), it asks if the message is the value generated by the getOnMessage() function, which is inherited from the base script. This starts by setting the on message to TurnOn to create a default value. If the object has a Design Note parameter called [script name]On, it changes the value of the on message to the value of the parameter.
    Last edited by R Soul; 29th Apr 2017 at 14:41.

  6. #6
    Member
    Registered: Aug 2001
    Location: Virginia, USA
    This is where I'm disappointed with Squirrel.

    Code:
    SQUIRREL ERROR> D:\Games\T2\FM\tnhdemo\sq_scripts\test.nut(18) [in constructor()]:
    AN ERROR HAS OCCURED [class instances do not support the new slot operator]
    Otherwise you could just create new functions on the fly to handle custom messages.

    In fact, it doesn't look like the OSM lets you change functions after script creation. I tried

    Code:
    class Test extends SqRootScript {
        constructor() {
    	Test["OnTurnOn"] <- Test.doCustomMessage
        }
        function doCustomMessage() {
            print("Custom Message")
        }
    }
    Doesn't work. But this does.

    Code:
    class Test extends SqRootScript {
        constructor() {
        }
        function doCustomMessage() {
            print("Custom Message")
        }
    }
    Test["OnTurnOn"] <- Test.doCustomMessage
    hmm... what about

    Code:
    class Test extends SqRootScript {
        constructor() {
    	Test["OnTurnOn"] <- Test.doCustomMessage2
        }
        function doCustomMessage() {
            print("Custom Message")
        }
        function doCustomMessage2() {
            print("Another Message")
        }
    
    }
    Test["OnTurnOn"] <- Test.doCustomMessage
    Prints "Another message". So you can change functions, just not create new slots. Too bad.

  7. #7
    ZylonBane
    Registered: Sep 2000
    Location: ZylonBane
    I have some questions about the script data services, setData/getData/etc.

    Does every instance of every script get its own namespace for variable names? So I could just save something under, say, "x" and not worry about it conflicting with any other script.

    And, is this data persisted into savegames? So my OnBeginScript should check to see if it's resuming from an already initialized state?

  8. #8
    Member
    Registered: Mar 2001
    Location: Ireland
    IIRC, yes to both.

    Fairly sure they don't survive level transitions, though, so you're still better off using properties / params to store data if your object can travel between levels.

  9. #9
    Member
    Registered: Jan 2012
    Location: Gèrmany
    SetData is savegame persistant and to confirm NV it is as you said linked to the script instance.

    if you enter edit_scriptdata you can see which data is stored by which script on which object and also some other information.

  10. #10
    ZylonBane
    Registered: Sep 2000
    Location: ZylonBane
    Fun with dynamic lights.



    Code:
    class colorcycle extends SqRootScript {
    
    	function OnBeginScript() {
    		SetOneShotTimer("ColorChange", 0.05);
    		SetData("hue", 0.0);
    	}
    
    	function OnTimer() {
    		if (message().name != "ColorChange") {
    			return;
    		}
    		local color = GetData("hue");
    		hue += 0.01;
    		if (hue > 1) {
    			hue = 0;
    		}
    		SetData("hue", hue);
    		SetProperty("LightColor", "hue", hue);
    		SetOneShotTimer("ColorChange", 0.05);
    	}
    }

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •