Advice for script developers

From Open Surge Wiki
Jump to: navigation, search

The legacy scripting system has been replaced by SurgeScript.

This section is deprecated due to newer versions. We ask that you don't use the information provided in this section, but read up on the most recent information available on this wiki or in the forum.

Motivation

Scripting is a really useful feature of Open Surge. In this page we give some advice to anyone who is interested in writing scripts. The guidelines we provide are not strict rules, but they can save you some time and make your code easier to read and to maintain.

  • COMMENT YOUR CODE. Use //; these symbols represent text which the engine doesn't read, meaning you can use them to keep track of why you did certain things within your code. Make meaningful comments, writing what a particular piece of code do, not how it does it.

Example:

// BAD: meaningless comments 

// a state named "damaged boss"
state "damaged boss"
{
    let "$health -= 1" // subtracts 1 from a variable named health
    if "$health <= 0" "dead" // tells the engine to change state to dead if health lower or equal than 0
    change_state "alive" // tells the engine to change the state to "alive"
}
// GOOD: meaningful comments

// this is called when the boss gets damaged. Jumps to "dead" if it must die, or "alive" otherwise
state "damaged boss"
{
    let "$health -= 1"
    if "$health <= 0" "dead"
    change_state "alive"
}
  • INDENT YOUR CODE. Code without indentation is harder to read. The convention is to use four spaces per block.

Example:

// BAD: harder to read

object "Mad Ring: Advanced"
{
requires 0.2.0
category "enemy"
state "main" 
{
set_animation "SD_RING" 0
let "$shake = 10"
on_player_attack "destroy"
on_player_collision "grab"
}
// ... more code ...
}


// GOOD: easier to read

object "Mad Ring: Advanced"
{
    requires 0.2.0
    category "enemy"
    
    state "main" 
    {
        set_animation "SD_RING" 0
        let "$shake = 10"
        on_player_attack "destroy"
        on_player_collision "grab"
    }
    
    // ... more code ...
}
  • Give meaningful names to your objects.
  • Every detail counts; if you have an opening bracket you must include a closing bracket.
  • Remember the syntax; wrong syntax means more debugging.
  • Use reference material; if you are unsure of how to properly use a command check the API Reference. It contains valuable information on how to use each command.
  • Change state after you create an object, or you will find a massive amount of those objects are created.
  • Pay attention to nanoparser; if you have a bug it will usually tell you where to look for it.

Example

We will provide a script which could be used to turn a standard looking ring into a mercilessness enemy.

Note: this is where your hotspot (see: Sprites) has a major effect on the game. If you want to make an enemy that walks properly, ensure that your hotspot is in the 'feet' of your sprite.

...

// -----------------------------------------------------
// madring.obj
// This is a ring which got tired of being a ring.
// Version 1.0
// Author: lunarrush (Your name should go here)
// -----------------------------------------------------

object "Mad Ring"
{
    requires 0.2.0 // could be reduced to 0.1.4 if you removed the category
    category "enemy"
    
    state "main" //Must have a main state.
    {
        set_animation "SD_RING" 0
        enemy 150
    }
    
}

...

The above script is fairly simple, but will work if you save it without the ... at the beginning and end as madring.obj in the objects folder of your game directory. After this, if you place this object in your level it will act like a ring but hurt the player on contact instead of giving them a ring. Scripts range in length and difficulty to write based on what you want the object to do. Below, we will provide a more advanced version of the Mad Ring enemy which is intended to attach itself to the player and drain a ring every second until they shake it off.

...

// -----------------------------------------------------
// advancedmadring.obj
// This is a ring which got tired of being a ring and now drains them.
// Version 1.0
// Author: lunarrush (Your name should go here)
// -----------------------------------------------------

object "Mad Ring: Advanced"
{
    requires 0.2.0 // this enemy uses variables, so requires at least version 0.2.0 of the engine
    category "enemy"
    
    state "main" 
    {
        set_animation "SD_RING" 0
        let "$shake = 10" // We will use $shake to determine how many times the player must press left and right before they lose the object.
        on_player_attack "destroy"
        on_player_collision "grab" // Change to state "grab" when the player touches (while not attacking) it.
    }
    
    state "grab"
    {
        attach_to_player 0 -25
        let "$original_time = elapsed_time()"
        if "$state" "caught_1" // Tells the object to change to state "caught_1" if $state is true (i.e., not zero)
        change_state "caught" // If the above condition does not hold, change to state "caught"
    }
    
    state "caught"
    {
        let "$state = 0" // Placed to make sure that losing rings doesn't change the way player has to shake.
        attach_to_player 0 -25 // Since attach_to_player is not persistent, we have to redefine this wherever we want the enemy to be attached to the player
        let "$time = elapsed_time()-$original_time"
        if "$shake <= 0" "uncatch" // Changes the state to "uncatch" if the player has shaken enough.
        if "$time >= 1" "lose"
        on_button_pressed "left" "shake"
    }
    
    state "caught_1" // To make the player press right as well
    {
        let "$state = 1"
        attach_to_player 0 -25
        let "$time = elapsed_time()-$original_time"
        if "$shake <= 0" "uncatch"
        if "$time >= 1" "lose"
        on_button_pressed "right" "shake_1"
    }
    
    state "shake"
    {
        attach_to_player 0 -25
        let "$shake -= 1"
        change_state "caught_1"
    }
    
    state "shake_1"
    {
        attach_to_player 0 -25
        let "$shake -= 1"
        change_state "caught"
    }
    
    state "lose"
    {
        attach_to_player 0 -25
        add_collectibles -1
        change_state "grab"
    }
    
    state "uncatch"
    {
        on_player_attack "destroy" // allows the player to attack it again
        on_timeout 2 "main" // gets the enemy ready to catch the player again if they don't get away
    }
    
    state "destroy"
    {
        destroy
    }
}

...

This enemy was much more advanced in behaviour, and thus required a great deal more scripting. If you wish to use this enemy save the script without the ... at the beginning and end as *.obj in the objects folder of your game directory, where * is any name. Of course, any variable in the above example could be changed to change the way the object works, but you'll have to make that decision for your objects. Remember that with object scripting you can do almost anything, so if you don't know a way to do it right off the bat don't give up.