-
Notifications
You must be signed in to change notification settings - Fork 2
Obstacle Header File
There are multiple Asset (Obstacle) headers for different types of Assets. These reside in /headers/Obstacles. These are basically template files on how to interface with Assets, since they usually do so on their own channel for performance and flexibility reasons. If none of these standard headers fit your type of Asset, feel free to roll your own!
Let's take a look at how they're usually built. These are just best practice guidelines and you don't HAVE to use them. But I suggest doing so if you want other people to use your Assets in their own levels!
First off, let's look at the full header file:
#ifndef __Trap
#define __Trap
#define TrapConst$CHAN (OBSTACLE_CHAN+0x7) // Listener
#define TrapTask$seat 1 // (key)player
#define TrapTask$attack 2 //
#define TrapTask$unSeat 3 //
#define Trap$seat( targ, label, player ) \
llRegionSayTo(targ, TrapConst$CHAN, mkarr(label + TrapTask$seat + (player)))
#define Trap$attack( targ, label, args ) \
llRegionSayTo(targ, TrapConst$CHAN, mkarr(label + TrapTask$attack + args))
#define Trap$attackAll( label, args ) \
llRegionSay(TrapConst$CHAN, mkarr(label + TrapTask$attack + args))
// Level event bindings
#define LevelCustomType$TRAP "oTrap" // Generic type for traps like the lasher
#define LevelCustomEvt$TRAP$hit 1 // (key)player1, (key)player2... - Trap has hit a player
#define LevelCustomEvt$TRAP$seated 2 // (key)player1, (key)player2... - Trap has been sat on by one or more players
#define LevelCustomEvt$TRAP$unseated 3 // (key)player1, (key)player2... - Trap has been unsat by one or more players
#define onTrapHit( trap, players ) \
if( isEventLevelCustom() AND argStr(1) == LevelCustomType$TRAP AND argInt(2) == LevelCustomEvt$TRAP$hit ){ \
key trap = argKey(0); \
list players = llDeleteSubList(METHOD_ARGS, 0, 2);
#define onTrapSeated( trap, players ) \
if( isEventLevelCustom() AND argStr(1) == LevelCustomType$TRAP AND argInt(2) == LevelCustomEvt$TRAP$seated ){ \
key trap = argKey(0); \
list players = llDeleteSubList(METHOD_ARGS, 0, 2);
#define onTrapUnseated( trap, players ) \
if( isEventLevelCustom() AND argStr(1) == LevelCustomType$TRAP AND argInt(2) == LevelCustomEvt$TRAP$unseated ){ \
key trap = argKey(0); \
list players = llDeleteSubList(METHOD_ARGS, 0, 2);
// Useful templates
// Put this into onTrapHit to auto seat a viable player when a trap is triggered
// Auto seats with a filter function, the filter function is passed the arguments key trap, key player
#define _TRAPS_autoSeat(filterFunction){ \
\
players = llListRandomize(players, 1); \
integer i; \
for(; i < count(players); ++i ){ \
\
key p = l2k(players, i); \
if( ~llListFindList(PLAYERS, [(str)p]) ){ \
\
if( filterFunction(trap, p) ){ \
\
Trap$seat(trap, "*", p); \
i = count(players); \
\
} \
\
} \
\
} \
}
#endif
#ifndef __Trap
#define __Trap
Each header file should start with this, defining __ if not already defined. This is to prevent trying to include the script multiple times. C++ programmers will recognize this pattern.
#define TrapConst$CHAN (OBSTACLE_CHAN+0x7) // Listener
Defines the channel this obstacle type listens on. Usually OBSTACLE_CHAN plus some random number. Defined as <Script>Const$CHAN
#define TrapTask$seat 1 // (key)player
#define TrapTask$attack 2 //
#define TrapTask$unSeat 3 //
Defines tasks we can run on this Asset. Defined as <Script>Task$ and is usually an integer. These look similar to ObstacleScript methods, but are their own thing, since usually when you communicate with an Asset you just sent a JSON array on that type's channel with [id, task, arg1, arg2...] for performance reasons.
#define Trap$seat( targ, label, player ) \
llRegionSayTo(targ, TrapConst$CHAN, mkarr(label + TrapTask$seat + (player)))
...
Creates some macros. As stated before, these actions are usually sent as simple JSON arrays.
// Level event bindings
#define LevelCustomType$TRAP "oTrap" // Generic type for traps like the lasher
#define LevelCustomEvt$TRAP$hit 1 // (key)player1, (key)player2... - Trap has hit a player
#define LevelCustomEvt$TRAP$seated 2 // (key)player1, (key)player2... - Trap has been sat on by one or more players
#define LevelCustomEvt$TRAP$unseated 3 // (key)player1, (key)player2... - Trap has been unsat by one or more players
Next we define event bindings if this Asset should raise events on the Game Controller.
- LevelCustomType$TRAP - This is the custom event type that's raised, so we know what Asset type raised the event. It should be defined as
LevelCustomType$<SCRIPT> "o<Script>"
. The o stands for Obstacle, since there are other custom level events that aren't tied to obstacles. Such as water balloons. - LevelCustomEvt$TRAP$hit - This is the event id for our Asset type. Defined as
LevelCustomEvt$<SCRIPT>$<event> <index>
. This way when we raise the event, the Game Controller can tell that the event was raised from a Trap, and the trap event was hit. You can define as many of these events as you want. Just make sure to use a unique index for each, and adding comments showing what data is passed alongside the event.
#define onTrapHit( trap, players ) \
if( isEventLevelCustom() AND argStr(1) == LevelCustomType$TRAP AND argInt(2) == LevelCustomEvt$TRAP$hit ){ \
key trap = argKey(0); \
list players = llDeleteSubList(METHOD_ARGS, 0, 2);
...
Next we setup the event handlers that can be used in the Game Controller. We tie the first one to the LevelCustomEvt$TRAP$hit event. It's basically an if statement and some variable definitions. Let's look at it piece by piece:
-
#define onTrapHit( trap, players )
Sets up our event handler macro. We map the data to trap and players, where trap is the UUID of the trap and players are the players hit by it. -
if( isEventLevelCustom() AND argStr(1) == LevelCustomType$TRAP AND argInt(2) == LevelCustomEvt$TRAP$hit ){
isLevelEventCustom() is used because when an obstacle raises an event on the Game Controller, it gets tunneled through the LevelEvt$custom event in the Level script. This event is raised with the arguments (key)sender, (str)customEventType, (int)evt, (var)arg1, (var)arg2... For instance raising the LevelCustomEvt$TRAP$hit will cause Level to raise LevelEvt$custom with the parameters"cf2625ff-b1...", "oTrap", 1, "cf2625ff-b1..."
. That's why we first check isLevelEventCustom(). Then we check that the first argument is LevelCustomType$TRAP ("oTrap"), and the second argument is LevelCustomEvt$TRAP$hit. - If it is, we map up the trap and players variables. As specified above, the first argument in the LevelEvt$custom is the UUID of the object that tunneled the event (in this case the our anemone trap), so we map that to trap. The second and third arguments contain the trap type, and trap event. The remaining arguments are the parameters passed to the event (the players). So we map players to any arguments past the third one.
#define _TRAPS_autoSeat(filterFunction){ \
\
players = llListRandomize(players, 1); \
integer i; \
for(; i < count(players); ++i ){ \
\
key p = l2k(players, i); \
if( ~llListFindList(PLAYERS, [(str)p]) ){ \
\
if( filterFunction(trap, p) ){ \
\
Trap$seat(trap, "*", p); \
i = count(players); \
\
} \
\
} \
\
} \
}
You can create some templates if you want to abstract some code away into useful macros. Since players might be invulnerable, we can create a quick macro called _TRAP_autoSeat which can automatically seat the first viable player in the onTrapHit event onto the trap.
#endif
Don't forget to end your header with an endif and a blank line!