OpenBOR Wiki Script Collections

Collections

Collections are groups of child properties attached to a parent property in a one to many relationship. For example, a single level might have one or more holes defined for you to fall into. To access a specific item in a collection, you must break down step by step from parent to child, and then to instance. For example, if you wanted to work with the pause value of an attack, the collection tree would look something like this:

models->model->animations->animation->frames->frame->attacks->attack->pause

Although collection layouts can vary, the procedure to work with an item in the tree is always the same. Start with the highest level parent, and continue down until you reach the desired property.

Use Case & Walk-through

The Basics

Getting Started

For our example case, let’s use hole access. We’ll start out by finding the X position of a hole.

1. Get Collection Handle

For the first step, we’ll assume that the hole we’re after is in the current level. So all we need to do is call our get level property function with a blank level pointer (more on pointers below) and the property constant we want.

void hole_collection; 

hole_collection = get_level_property(NULL(), "hole_collection"); 

The engine has now populated hole_collection with a pointer that refers to the collection of holes that belong to this level. If you are familiar with C, or C++ at all the concept of pointers is old hat. If not, don’t worry about it. All you need to know is hole_collection is the collection of holes.

2. Get Individual Pointer

Now we need to get a handle for an individual hole in the holes collection. Collections are just arrays of handles to individual items, so all we need is an index. For this exercise, we’ll assume that the first hole is the one we want. Remember that in script, items are 0 indexed, so the first hole is 0, not 1. Let’s see this in action.

void hole_instance; 

hole_instance = get_hole_instance(hole_instance, 0); 

See how it works? The get hole instance function gives us the handle to the first hole in the collection of holes.

3. Get Property

Now that we have a handle to the individual hole, we can use that handle to access and manipulate its properties. Let’s get the hole’s X position in the level.

int pos_x; 

pos_x = get_hole_property(hole_instance, openborconstant("HOLE_PROP_POSITION_X")); 
4. Modify Property

Alright, so we got a property, why not modify it too? That’s no problem. We already have the handle, so all that remains is to call the function and insert a new value.

pos_x += 10; // Increase by 10 pixels forward. 

set_hole_property(hole_instance, "position_x", pos_x);

Here we’ve taken the X position from earlier, added 10 to it, and set that as the new value for our hole instance.

After all steps are completed, our full example case should look something like this:

void hole_collection; // Collection of holes. 
void hole_instance; // Single hole instance. 
int pos_x; // X position.

/* Get hole collection of current level.  */
hole_collection = get_level_property(NULL(),"hole_collection");

/* Get the first item from hole collection. */ 
hole_instance = get_hole_instance(hole_instance, 0);

/* Get the X position of hole. */
pos_x = get_hole_property(hole_instance, "position_x");

/* Add 10 to the hole position. */ 
pos_x += 10;

/* Apply new position to the hole. */ 
set_hole_property(hole_instance, "position_x"), pos_x);

Not bad ehh? It takes us a couple of steps to get down to the item we want to modify, but like any variable, once we have the item pointer we can re-use it until the script instance is terminated. That’s just scratching the surface. What happens if we aren’t sure which hole we want to mess with, or maybe we just want to mess with all of them. Move onto Iteration and let’s find out!

Advanced

Iteration

There will be times when we just aren’t sure which item in the collection we need. Maybe we’re looking for one with a specific name or other property. Maybe we just want to do something to all of them. This is where iteration comes in. Remember how earlier we talked about collections being indexed arrays? Throw in a counter variable and the number of items in a collection, and you are ready to get started.

1. Get Collection Handle

Just like the basic example above, the first thing we need to do is get the collection handle.

void hole_collection = get_level_property(NULL(), "hole_collection");
2. Get Collection Count

Before we can iterate, we’ll need to know just how many items the collection has. All collections will have this information available.

int hole_count = get_level_property(NULL(), "hole_count");
3. Build Loop

This is where things get interesting. Let’s add a counter variable, and iterate through the collection of holes. Along the way, we’ll get the individual handle.

 int i = 0; 

for(i=0; i<hole_count; i++) 
{ 
     hole_instance = get_hole_instance(hole_instance, i); 
}

So far we’ve instructed the engine to run a loop that keeps going until our counter i is greater than the number of holes. Inside the loop, we use the value of i as our index to get the handle for that hole instance, re-writing the hole_instance variable each time. In short, we’ve acquired the pointer for every hole in the level, one at a time. The next step is using that pointer to do something useful.

4. Take Action

In our basic example we finished up by moving the first hole 10 pixels forward. Why don’t we do that to all the holes? It’s no problem at all using our loop!

int i = 0; 
int pos_x = 0; 

for(i=0; i<hole_count; i++)
{ 
     /* Get the item from hole collection using i as index. */ 
     hole_instance = get_hole_instance(hole_instance, i); 

     /* Get the position of current hole index. */ 
     pos_x = get_hole_property(hole_instance, "position_x); 

     /* Add 10 pixels to hole position. */ 
     pos_x += 10; 
     
     /* Apply new position to the hole. */ 
     set_hole_property(hole_instance, "position_x", pos_x); 
}

Just like that, every hole in the level will shift 10 pixels forward. Let’s review what’s happening step by step.

  1. Our loop iterates through every hole in the collection.
  2. In each pass, the loop counter is used as an array index to get an individual item handle.
  3. The original property value is acquired.
  4. The value is modified.
  5. Finally, the new value is applied to property.
  6. When the last index is reached, loop will exit and the host function continues.

Once again, let’s look at the whole code from start to finish.

void hole_instance = NULL(); // Single hole instance. 
int pos_x = 0; // X position. 
int i = 0; // Counter.

/* Get hole collection of current level. */ 
void hole_collection = get_level_property(NULL(), "hole_collection");

for(i=0; i<hole_count; i++)
{ 
     /* Get the item from hole collection using i as index. */ 
     hole_instance = get_hole_instance(hole_instance, i); 

     /* Get the position of current hole index. */ 
     pos_x = get_hole_property(hole_instance, "position_x); 

     /* Add 10 pixels to hole position. */ 
     pos_x += 10; 
     
     /* Apply new position to the hole. */ 
     set_hole_property(hole_instance, "position_x", pos_x); 
}
Logic

Once you have a working iterator, you can begin to insert more advanced logic. As an example, maybe you’d only like to move the holes that are at position 400 and below. No problem!

if(pos_x <= 400) 
{
     /* Add 10 pixels to hole position. */
     pos_x += 10;
   
     /* Apply new position to the hole. */
     set_hole_property(hole_instance,"position_x", pos_x);   
}

Now the full operation looks like this:

void hole_instance = NULL(); // Single hole instance. 
int pos_x = 0; // X position. 
int i = 0; // Counter.

/* Get hole collection of current level. */ 
void hole_collection = get_level_property(NULL(), "hole_collection");

for(i=0; i<hole_count; i++)
{ 
     /* Get the item from hole collection using i as index. */ 
     hole_instance = get_hole_instance(hole_instance, i); 

     /* Get the position of current hole index. */ 
     pos_x = get_hole_property(hole_instance, "position_x); 

     if(pos_x <= 400) 
     {
          /* Add 10 pixels to hole position. */
          pos_x += 10;
   
          /* Apply new position to the hole. */
          set_hole_property(hole_instance,"position_x", pos_x);   
     }
}

Using access to collection pointers and in turn the pointers for each element in a collection, you can do nearly anything with a little ingenuity.

  • Transferring collections from one parent property to another.
  • Creating the child collection first, and attaching its pointer to the parent. You could effectively build a module at run time!
  • Develop your own object style functions based on collection pointers.

Details on what collections are available and their properties can be found in specific areas of this manual. Experiment and try some things for yourself! As always, see Chrono Crash for any questions.

Related Post