Difference between revisions of "Introduction to Modding"

From Open Surge Engine Wiki
Jump to: navigation, search
Line 8: Line 8:
 
Lets start by learning a little about how Open Surge reads data from your images so that it can be displayed in-game.  To do this, we will look at the ring sprite file included with the game.
 
Lets start by learning a little about how Open Surge reads data from your images so that it can be displayed in-game.  To do this, we will look at the ring sprite file included with the game.
 
  ...
 
  ...
// Ring
+
// Ring
sprite SD_RING
+
sprite SD_RING
{
+
{
 
     source_file    images/rings.png
 
     source_file    images/rings.png
 
     source_rect    0 128 176 16
 
     source_rect    0 128 176 16
 
     frame_size      16 16
 
     frame_size      16 16
 
     hot_spot        8 12
 
     hot_spot        8 12
 
 
     // spinning
 
     // spinning
 
     animation 0
 
     animation 0
Line 23: Line 22:
 
         data        0 1 2 3 4 5 6 7
 
         data        0 1 2 3 4 5 6 7
 
     }
 
     }
 
 
     // disappearing
 
     // disappearing
 
     animation 1
 
     animation 1
Line 31: Line 29:
 
         data        8 9 10
 
         data        8 9 10
 
     }
 
     }
}
+
}
 
  ...
 
  ...
 
In the above file, there are several things you will notice:
 
In the above file, there are several things you will notice:
1. there are lines that begin with //, these lines are there merely for your convince and are ignored by the engine.
+
* '''sprite "SD_RING"''': This is what this definition is called, you must make sure that no two sprites have the same name.
2. '''source_file''': This is where the image you are reading from is located.  The engine assumes that images are located somewhere in the game directory folder, so you do not need to specify the full file path.  Instead, you just have to specify where the images are located in the game folder.
+
* there are lines that begin with //, these lines are there merely for your convince and are ignored by the engine.
3. '''source_rect''': The first two numbers give the X and Y coordinate of the top left corner of the image you are defining.  The second two numbers give how big of a rectangle it would take to completely enclose every frame of the animation.
+
* '''source_file''': This is where the image you are reading from is located.  The engine assumes that images are located somewhere in the game directory folder, so you do not need to specify the full file path.  Instead, you just have to specify where the images are located in the game folder.
4. '''frame_size''': Defines the size of each frame of your animation.  In this instance, the size of the source rectangle is 176 by 16, but each piece of the animation is only 16 by 16.
+
* '''source_rect''': The first two numbers give the X and Y coordinate of the top left corner of the image you are defining.  The second two numbers give how big of a rectangle it would take to completely enclose every frame of the animation.
5. '''hot_spot''': This is the place which the engine will use to determine the x and y position of your object.  It also is the place where the level editor grabs your object when you select it.  There are different reasons for placing hotspots in different places, however it is mostly a matter of preference.
+
* '''frame_size''': Defines the size of each frame of your animation.  In this instance, the size of the source rectangle is 176 by 16, but each piece of the animation is only 16 by 16.
6. '''animations''': These are what your object will do in the game.  There are basically three different things that animations need to be fully defined.
+
* '''hot_spot''': This is the place which the engine will use to determine the x and y position of your object.  It also is the place where the level editor grabs your object when you select it.  There are different reasons for placing hotspots in different places, however it is mostly a matter of preference.
a) '''repeat''': this tells the engine if you want it to keep doing the animation as long as it is the selected one.  In this case, while the ring is inactive it will continuously spin because  repeat is set to TRUE, but when it touches a player and the animation changes to 1 it will play the animation 1 time and disappear.
+
* '''animations''': These are what your object will do in the game.  There are basically three different things that animations need to be fully defined.
b) '''fps''': This defines how fast the engine will cycle through your frames.  In this case it plays 16 frames every second for animation 0 and 8 for animation 1.
+
* '''repeat''': this tells the engine if you want it to keep doing the animation as long as it is the selected one.  In this case, while the ring is inactive it will continuously spin because  repeat is set to TRUE, but when it touches a player and the animation changes to 1 it will play the animation 1 time and disappear.
c) '''data''': This defines which frame you want to play at any particular moment.  In this case it plays frames 0-7 and then repeats under animation 0, and plays frames 8-10 and doesn't repeat under animation 1.
+
* '''fps''': This defines how fast the engine will cycle through your frames.  In this case it plays 16 frames every second for animation 0 and 8 for animation 1.
 +
* '''data''': This defines which frame you want to play at any particular moment.  In this case it plays frames 0-7 and then repeats under animation 0, and plays frames 8-10 and doesn't repeat under animation 1.
  
 
Now that we've examined a sprite file and determined what each of the components do, we'll modify it so that we replace the standard rings in the game with our own.   
 
Now that we've examined a sprite file and determined what each of the components do, we'll modify it so that we replace the standard rings in the game with our own.   
Line 49: Line 48:
 
Below, I will provide a sprite which I wrote to include my own ring design in-game.  Afterward we will examine how my sprite differs from the original.
 
Below, I will provide a sprite which I wrote to include my own ring design in-game.  Afterward we will examine how my sprite differs from the original.
 
  ...
 
  ...
/ Ring
+
// Ring
sprite SD_RING
+
sprite SD_RING
{
+
{
 
     source_file    images/collectables.png
 
     source_file    images/collectables.png
 
     source_rect    1 1 176 16
 
     source_rect    1 1 176 16
 
     frame_size      16 16
 
     frame_size      16 16
 
     hot_spot        9 12
 
     hot_spot        9 12
 
 
     // idle
 
     // idle
 
     animation 0
 
     animation 0
Line 64: Line 62:
 
         data        0 1 2 3 4 5 2 6
 
         data        0 1 2 3 4 5 2 6
 
     }
 
     }
 
 
     // end
 
     // end
 
     animation 1
 
     animation 1
Line 72: Line 69:
 
         data        7 8 9 10
 
         data        7 8 9 10
 
     }
 
     }
}
+
}
 
  ...
 
  ...
 
Here are some things you should notice right off the bat:
 
Here are some things you should notice right off the bat:
1. I use different lines that start with //, because they help me keep track of what my sprite is doing.  Remember, these are entirely up to the individual user and aren't required.
+
* sprite SD_RING(located at the top) remains the same. This is so I don't have to modify any game code to replace all rings with my rings.  It also means that I have to remove the old sprite definitions from my sprites folder so that the engine doesn't try to define what a ring is twice.
2. The source_file is different, this is because I have created an image and it has a different file-name than the original.
+
* I use different lines that start with //, because they help me keep track of what my sprite is doing.  Remember, these are entirely up to the individual user and aren't required.
3. The source_rect is different, this is because my images are at a different location inside my image than the originals were.
+
* The source_file is different, this is because I have created an image and it has a different file-name than the original.
4. The frame_size is the same, this is because I used a standard size rectangle to create my rings so that they can seamlessly replace the built in rings.
+
* The source_rect is different, this is because my images are at a different location inside my image than the originals were.
5. The hot_spot is the same, this is because I didn't see any need to change this from what it already was.
+
* The frame_size is the same, this is because I used a standard size rectangle to create my rings so that they can seamlessly replace the built in rings.
6. The repeats are set to the same values, this is because my ring acts similar to the original.  
+
* The hot_spot is the same, this is because I didn't see any need to change this from what it already was.
7. The fps is the same, this could be modified if I wanted to speed up or slow down the animation.
+
* The repeats are set to the same values, this is because my ring acts similar to the original.  
8. The data is different, this is because my frames must be presented in a different order to be a complete animation.
+
* The fps is the same, this could be modified if I wanted to speed up or slow down the animation.
9. sprite SD_RING(located at the top) remains the sameThis is so I don't have to modify any game code to replace all rings with my ringsIt also means that I have to remove the old sprite definitions from my sprites folder so that the engine doesn't try to define what a ring is twice.
+
* The data is different, this is because my frames must be presented in a different order to be a complete animation.
 +
 
 +
Now that we understand how the engine reads these files, it is easier to know what input we need to give it in order for our sprites to be displayed correctly.
 +
 
 +
== Creating an object ==
 +
Now that we can define an animation, we need to be able to put our object into the gameIn the above tutorial, the coding for the ring was included in-game, so we didn't have to do any coding of our own.  In this example though, I will show you how we can use the object scripting engine to create an enemy which looks like a ring.
 +
 
 +
'''Step One:Script the object.'''
 +
If we want to add anything to the game we have two choices.  We can either modify the source code to include our mod (the hard to do and distribute path), or we can script an objectThere are a few things to remember when scripting an object:
 +
* 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.
 +
* 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.
 +
* Change state after you create an object, or you will find a massive amount of those objects are created
 +
* Use the nanoparser; if you have a bug it will usually tell you where to look for it.
 +
Now I will provide a script which could be used to turn a standard looking ring into a mercilessness enemy. '''(Note: This is where your hotspot has a major effect on your game.  If you want to make an enemy that can walk, ensure that your hotspot is in the bottom right corner 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" //This allows you to sort your objects into categories
 +
state "main" //Must have a main state.
 +
{
 +
set_animation "SD_RING" 0 //Tells the engine which animation to use.
 +
enemy 150 //Tells the engine to treat this object as an enemy, meaning that if the player collides with it without attacking they will take damage
 +
}
 +
}
 +
...
 +
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 I 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 time the enemy uses variables, so it will require 0.2.0
 +
category "enemy"
 +
state "main"
 +
{
 +
set_animation "SD_RING" 0
 +
let "$shake = 10" //Defines the variable "$shake" as ten, in this case we will use it to determine how many times the player must press left and right before they lose the object.
 +
on_player_attack "destroy" //Tells the object to change to state destroy if the player attacks it.
 +
on_player_collision "grab" //This tells the object to change to state "grab" when the player touches it.
 +
}
 +
state "grab"
 +
{
 +
attach_to_player 0 -25 //Makes the object grab the player.
 +
let "$original_time = elapsed_time()" // This defines $original_time as how long you've been playing.
 +
if "$state" "caught_1" //Tells the object to change to state "caught_1" if $state is 1
 +
change_state "caught" //Tells the object to 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 //Not persistent, so we have to redefine this wherever we want the enemy to be attached to the player
 +
let "$time = elapsed_time()-$original_time"
 +
if "$shake <= 1" "uncatch" //changes the state to "uncatch" if the player has shaken enough.
 +
if "$time >= 1" "lose" //Tells the engine to change to state "lose" if $time is greater than or equal to 1
 +
on_button_pressed "left" "shake" //Tells the engine to change to state "shake" if player presses left
 +
}
 +
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 <= 1" "uncatch"
 +
if "$time >= 1" "lose"
 +
on_button_pressed "right" "shake_1"
 +
}
 +
state "shake"
 +
{
 +
attach_to_player 0 -25
 +
let "$shake -= 1" //Tells the engine to subtract 1 from shake.
 +
change_state "caught_1" //instead of caught so that the player has to press right too.
 +
}
 +
state "shake_1"
 +
{
 +
attach_to_player 0 -25
 +
let "$shake -= 1"
 +
change_state "caught"
 +
}
 +
state "lose"
 +
{
 +
attach_to_player 0 -25
 +
add_collectibles -1 //removes 1 ring from player score
 +
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 //deletes the object from the level
 +
}
 +
}
 +
...
 +
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 <nowiki>*.obj</nowiki> in the objects folder of your game directory, where <nowiki>*</nowiki> 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.
  
'''More Coming Soon'''
+
== More Advanced Editing ==
 +
Beyond creating Objects, the only way to change the way the game acts is to modify the source code.  This is fairly difficult and requires knowledge of programming, so it isn't recommended for the faint of heart.  Not only that, but after you make the mod you will have to find some way to distribute the entire executable instead of just a simple script.  If you want to try though, you can find the source in the folder "src" in your game directory.  After you modify the source code, you will have to recompile the game to make the change take effect, you can find details of how to do that elsewhere on the wiki.

Revision as of 21:19, 13 January 2011

Modding

Open Surge is a game developed to allow user created content as much as possible. The modding scene has been around since the early 90's when people hacked games, found out how the content was generated and substituted it's content with their own.

In Open Surge, it is fairly easy to modify any game content you wish. It ranges in difficulty from replacing sprite files and adding images to actually modifying the source code to add functionality to the engine.


To Begin

Lets start by learning a little about how Open Surge reads data from your images so that it can be displayed in-game. To do this, we will look at the ring sprite file included with the game.

...
// Ring
sprite SD_RING
{
   source_file     images/rings.png
   source_rect     0 128 176 16
   frame_size      16 16
   hot_spot        8 12
   // spinning
   animation 0
   {
       repeat      TRUE
       fps         16
       data        0 1 2 3 4 5 6 7
   }
   // disappearing
   animation 1
   {
       repeat      FALSE
       fps         8
       data        8 9 10
   }
}
...

In the above file, there are several things you will notice:

  • sprite "SD_RING": This is what this definition is called, you must make sure that no two sprites have the same name.
  • there are lines that begin with //, these lines are there merely for your convince and are ignored by the engine.
  • source_file: This is where the image you are reading from is located. The engine assumes that images are located somewhere in the game directory folder, so you do not need to specify the full file path. Instead, you just have to specify where the images are located in the game folder.
  • source_rect: The first two numbers give the X and Y coordinate of the top left corner of the image you are defining. The second two numbers give how big of a rectangle it would take to completely enclose every frame of the animation.
  • frame_size: Defines the size of each frame of your animation. In this instance, the size of the source rectangle is 176 by 16, but each piece of the animation is only 16 by 16.
  • hot_spot: This is the place which the engine will use to determine the x and y position of your object. It also is the place where the level editor grabs your object when you select it. There are different reasons for placing hotspots in different places, however it is mostly a matter of preference.
  • animations: These are what your object will do in the game. There are basically three different things that animations need to be fully defined.
  • repeat: this tells the engine if you want it to keep doing the animation as long as it is the selected one. In this case, while the ring is inactive it will continuously spin because repeat is set to TRUE, but when it touches a player and the animation changes to 1 it will play the animation 1 time and disappear.
  • fps: This defines how fast the engine will cycle through your frames. In this case it plays 16 frames every second for animation 0 and 8 for animation 1.
  • data: This defines which frame you want to play at any particular moment. In this case it plays frames 0-7 and then repeats under animation 0, and plays frames 8-10 and doesn't repeat under animation 1.

Now that we've examined a sprite file and determined what each of the components do, we'll modify it so that we replace the standard rings in the game with our own. Warning: Changing sprite files makes a major change to the game. Make sure to backup your sprite files before you change them so if something goes wrong you will be able to restore the original functionality to the game.

Below, I will provide a sprite which I wrote to include my own ring design in-game. Afterward we will examine how my sprite differs from the original.

...
// Ring
sprite SD_RING
{
   source_file     images/collectables.png
   source_rect     1 1 176 16
   frame_size      16 16
   hot_spot        9 12
   // idle
   animation 0
   {
       repeat      TRUE
       fps         16
       data        0 1 2 3 4 5 2 6
   }
   // end
   animation 1
   {
       repeat      FALSE
       fps         8
       data        7 8 9 10
   }
}
...

Here are some things you should notice right off the bat:

  • sprite SD_RING(located at the top) remains the same. This is so I don't have to modify any game code to replace all rings with my rings. It also means that I have to remove the old sprite definitions from my sprites folder so that the engine doesn't try to define what a ring is twice.
  • I use different lines that start with //, because they help me keep track of what my sprite is doing. Remember, these are entirely up to the individual user and aren't required.
  • The source_file is different, this is because I have created an image and it has a different file-name than the original.
  • The source_rect is different, this is because my images are at a different location inside my image than the originals were.
  • The frame_size is the same, this is because I used a standard size rectangle to create my rings so that they can seamlessly replace the built in rings.
  • The hot_spot is the same, this is because I didn't see any need to change this from what it already was.
  • The repeats are set to the same values, this is because my ring acts similar to the original.
  • The fps is the same, this could be modified if I wanted to speed up or slow down the animation.
  • The data is different, this is because my frames must be presented in a different order to be a complete animation.

Now that we understand how the engine reads these files, it is easier to know what input we need to give it in order for our sprites to be displayed correctly.

Creating an object

Now that we can define an animation, we need to be able to put our object into the game. In the above tutorial, the coding for the ring was included in-game, so we didn't have to do any coding of our own. In this example though, I will show you how we can use the object scripting engine to create an enemy which looks like a ring.

Step One:Script the object. If we want to add anything to the game we have two choices. We can either modify the source code to include our mod (the hard to do and distribute path), or we can script an object. There are a few things to remember when scripting an object:

  • 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.
  • 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.
  • Change state after you create an object, or you will find a massive amount of those objects are created
  • Use the nanoparser; if you have a bug it will usually tell you where to look for it.

Now I will provide a script which could be used to turn a standard looking ring into a mercilessness enemy. (Note: This is where your hotspot has a major effect on your game. If you want to make an enemy that can walk, ensure that your hotspot is in the bottom right corner 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" //This allows you to sort your objects into categories
state "main" //Must have a main state.
{
set_animation "SD_RING" 0 //Tells the engine which animation to use.
enemy 150 //Tells the engine to treat this object as an enemy, meaning that if the player collides with it without attacking they will take damage
}
}
...

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 I 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 time the enemy uses variables, so it will require 0.2.0
category "enemy"
state "main" 
{
set_animation "SD_RING" 0
let "$shake = 10" //Defines the variable "$shake" as ten, in this case we will use it to determine how many times the player must press left and right before they lose the object.
on_player_attack "destroy" //Tells the object to change to state destroy if the player attacks it.
on_player_collision "grab" //This tells the object to change to state "grab" when the player touches it.
}
state "grab"
{
attach_to_player 0 -25 //Makes the object grab the player.
let "$original_time = elapsed_time()" // This defines $original_time as how long you've been playing.
if "$state" "caught_1" //Tells the object to change to state "caught_1" if $state is 1
change_state "caught" //Tells the object to 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 //Not persistent, so we have to redefine this wherever we want the enemy to be attached to the player
let "$time = elapsed_time()-$original_time"
if "$shake <= 1" "uncatch" //changes the state to "uncatch" if the player has shaken enough.
if "$time >= 1" "lose" //Tells the engine to change to state "lose" if $time is greater than or equal to 1
on_button_pressed "left" "shake" //Tells the engine to change to state "shake" if player presses left
}
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 <= 1" "uncatch"
if "$time >= 1" "lose"
on_button_pressed "right" "shake_1"
}
state "shake"
{
attach_to_player 0 -25
let "$shake -= 1" //Tells the engine to subtract 1 from shake.
change_state "caught_1" //instead of caught so that the player has to press right too.
}
state "shake_1"
{
attach_to_player 0 -25
let "$shake -= 1"
change_state "caught"
}
state "lose"
{
attach_to_player 0 -25
add_collectibles -1 //removes 1 ring from player score
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 //deletes the object from the level
}
}
...

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.

More Advanced Editing

Beyond creating Objects, the only way to change the way the game acts is to modify the source code. This is fairly difficult and requires knowledge of programming, so it isn't recommended for the faint of heart. Not only that, but after you make the mod you will have to find some way to distribute the entire executable instead of just a simple script. If you want to try though, you can find the source in the folder "src" in your game directory. After you modify the source code, you will have to recompile the game to make the change take effect, you can find details of how to do that elsewhere on the wiki.