Chapter 2 - Structure of a Script

From Frictional Wiki
< HPL3‎ | Scripting/AngelScript Fundamentals
Revision as of 11:17, 15 August 2020 by TiMan (talk | contribs) (Created page with "{{shortPageTitle}} {{AngelScriptGuideMenu}} The first thing to do is to take a look at what a typical script file for a typical SOMA map would look like. First, launch the Lev...")
(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)
Jump to navigation Jump to search

The first thing to do is to take a look at what a typical script file for a typical SOMA map would look like. First, launch the LevelEditor program within your SOMA installation file. Once it’s open, save the blank map somewhere on your computer.

<note>When the LevelEditor saves a map for the first time, it creates a bunch of different files (at least 20). For this reason, it’s recommended that you save your map inside an empty folder. Also, for reasons of convenience, it’s also a good idea to save this folder somewhere in the SOMA installation folder, typically within a folder called “mods”. When it’s all said and done, the typical folder layout would look like “SOMA/mods/YourModName/maps/YourMapName/<map files>”.</note>

Now that you’ve done that, go ahead and take a look at the files you just saved. There are quite a lot, but the one we are most interested in is the one that ends in “.hps”.

Open the .hps file in your editor of choice. You should see something resembling the following (which has been formatted for reasons of length):

#include "interfaces/Map_Interface.hps"
#include "base/Inputhandler_Types.hps"

#include "helpers/helper_map.hps"
#include "helpers/helper_props.hps"
#include "helpers/helper_effects.hps"
#include "helpers/helper_audio.hps"
#include "helpers/helper_imgui.hps"
#include "helpers/helper_sequences.hps"
#include "helpers/helper_game.hps"
#include "helpers/helper_modules.hps"
#include "helpers/helper_ai.hps"

//--------------------------------------------------
/*Place any global values here. These must be const variables as they will not be saved*/
/*This is also the place for enums and classes, but these should be avoided whenever possible*/
//--------------------------------------------------
class cScrMap : iScrMap
{
    //--------------------------------------------
    //////////////////////////////////////////////////////////////////////////////////////////
    // ==============
    // MAIN CALLBACKS
    // ==============
    //{///////////////////////////////////////////////////////////////////////////////////////
    //-------------------------------------------------------
    ////////////////////////////
    // Set up map environment
    void Setup() {}

    //-------------------------------------------------------

    ////////////////////////////
    // Run first time starting map
    void OnStart() {}

    //-------------------------------------------------------

    ////////////////////////////
    // Run when entering map
    void OnEnter() {}

    //-------------------------------------------------------

    ////////////////////////////
    // Run when leaving map
    void OnLeave() {}

    //-------------------------------------------------------

    ////////////////////////////
    // The player has died.
    void OnPlayerKilled(int alRecentDeaths, const tString&in asSource) {}

    //-------------------------------------------------------

    ////////////////////////////
    // To get when player makes input (mostly used for debug)
    void OnAction(int alAction, bool abPressed) {}

    //-------------------------------------------------------

    ////////////////////////////
    // This only used for pure debug purposes when info needs to printed.
    float DrawDebugOutput(cGuiSet @apSet,iFontData @apFont,float afY) { return afY; }
    
    //-------------------------------------------------------
    //} END MAIN CALLBACKS
    //////////////////////////////////////////////////////////////////////////////////////////
    // ==============
    // MAIN FUNCTIONS
    // ==============
    //{///////////////////////////////////////////////////////////////////////////////////////
    //-------------------------------------------------------
    /*Put any variables that are used in more than one scene here.*/
    //-------------------------------------------------------
    /*Put any functions that are used in more than one scene here.*/
    //-------------------------------------------------------
    //} END MAIN FUNCTIONS
    //////////////////////////////////////////////////////////////////////////////////////////
    // ==============
    // SCENE X *NAME OF SCENE*
    // ==============
    //{//////////////////////////////////////////////////////////////////////////////////////
         /////////////////////////////////////////
         // General
         //{//////////////////////////////////////

        //-------------------------------------------------------

        /*Put any variables that are used by many events in Scene X here.*/

        //-------------------------------------------------------

        /*Put any functions that are used in more than one event in Scene X here.*/

        //-------------------------------------------------------

        //} END General

         /////////////////////////////////////////
         // Event *Name Of Event*
         //{//////////////////////////////////////

         //-------------------------------------------------------

         /*Put any variables that are only used in Scene X, Event X here.*/

         //-------------------------------------------------------

         /*Put any functions that are only used in Scene X, Event X here.*/

         //-------------------------------------------------------

         //} END Event *Name Of Event*

     //} END SCENE X
     /////////////////////////////////////////
     // ==============
     // TERMINALS
     // ==============
     //{//////////////////////////////////////
         //-------------------------------------------------------

         /////////////////////////////////////////
         // Terminal *Name Of Terminal*
         //{//////////////////////////////////////

         //-------------------------------------------------------

         /*Put any variables that are only used Terminal here.*/

         //-------------------------------------------------------

         /*Put any functions that are only used Terminal here.*/

         //-------------------------------------------------------

         //} END Terminal *Name Of Terminal*

    //} END TERMINALS
}

A bit wordy, perhaps, but it all boils down to the following categories, each of which you will learn about in a particular lesson:

  • Includes (Lesson 6)
  • Class Declaration (Lesson 7)
  • Functions (Lesson 5)
  • Types and Variables (Lesson 2)
  • Callback Functions (Appendix 2)
  • Comments

Most of these aspects are explained in later lessons. There is one that I can teach you about now, however: comments.

Commenting

Most of the things that go into a script is part of the program's execution - you want it to do something here, then do something there, then do something to those two somethings over there. Sometimes, however, you just want to write a reminder of what some code does so that you don't have to go through it all and figure it out later. That's where comments come in. Anything that has been marked as a comment is ignored by the program, so you can type in whatever you want without worrying that it will screw up the program.

There are two types of comments in AngelScript - inline comments and block comments:

    // This is an inline comment

    /* 
    This is a block comment
    */

Inline comments are just for a single line. Anything after the comment marker ("%%%%") becomes a comment, but on the next line, it's back to business as usual.

Block comments are for multiple lines. A block comment is marked as everything between the starting marker ("/*") and the ending marker ("*/"). This can span many lines, and can even mark your entire program as a comment if you aren't careful.

Hello World

As per tradition, every introductory programming course needs a “Hello World” program, and this tutorial is no exception. In your map’s script, find the section of the code that contains the following:

    ////////////////////////////
    // Run when entering map
    void OnEnter()
    {

    }

Inside those curly brackets, add “cLux_AddDebugMessage(“Hello SOMA”);”. Don’t worry what it means just yet. When you’re done, the above code snippet should now look like this:

    ////////////////////////////
    // Run when entering map
    void OnEnter()
    {
        cLux_AddDebugMessage("Hello SOMA");
    }

Go ahead and save your script, if you haven’t done that already.

Now it’s time to start up SOMA in mod development mode. To do this, there’s a file in your SOMA installation directory called “SomaDev.bat”. When you open this file, it starts SOMA in developer mode. For the first little bit, the game will be loading, but once you hear sounds start to play, hit the F1 button. This brings up the developer panel, and on it contains a lot of commands and tools for testing and proofing your map.

For now, scroll down until you find the “Load Map” button. Click that button, then navigate to where you saved your map. Open your map from there (it will be the “.hpm” file that you see). Once you do, you should get basically a black screen with a handful of text around the edges. In the lower left corner, you should see the text “Hello SOMA”.

<note>If you don’t see the text, make sure the developer panel is hidden by pressing F1 again. This is because the game is effectively frozen while the panel is visible by default, so the script may not appear right away if the panel is visible. If you still do not see the text, press F5, which reloads the map and causes it to become visible again.</note>

If all went well, then congratulations. You just created your first SOMA mod. It may not be very shiny, but like I said in Lesson 0, we all have to start somewhere.

So let’s look at what we just did in pieces:

    cLux_AddDebugMessage(“Hello SOMA”);

First, we used the code <html>cLux_AddDebugMessage</html> followed by an opening and (eventually) closing parentheses. This is a function call, which you will learn more about in Lesson 6. For now, just know that this function’s job is to print text onto the screen.

Within the parentheses is some text within quotation marks, <html>“Hello SOMA”</html>. This is what is known as a “string literal”. You’ll learn about them and other types in Lesson 2. The important part to note here is that it is the text that actually appeared in the game itself.

And finally, after the closing parenthesis, there is a lonely little semicolon. That semicolon marks the end of a line of code. Do not forget this: every line of code that isn’t a class or function declaration (more on them later) needs a semicolon at the end of it. If you do not put a semicolon at the end of a line of code, SOMA will complain about it and refuse to run your script. I cannot stress this enough: do not forget the semicolons!!!

I say that, not to make sure you remember the semicolons, but because you will forget about semicolons at least once in a while. No programmer is immune from that mistake, no matter how many decades they’ve been a guru in their field. You’d be surprised how many times I’ve been asked by someone to help them figure out a confusing error in their code, and the cause turned out that they forgot to put a semicolon somewhere. As long as you remember your semicolons, then when the errors happen, you know what to check first.

Alright, now that you’ve taken a satisfactory initial plunge into AngelScript, let’s take a breather. In the next lesson, you start to learn the basics for real.