|
||||||||||||
|
:: Dungeon Siege University - U203b :: Here is the 'U203b' stuff: Siege University 203B: Triggers II1. Introduction
This tutorial will be necessary in understanding the following future tutorials (numbers subject to change):
There are basically two kinds of trigger Conditions: Spatial, which we've learned a bit about in Siege U: 203A, and Message Handlers. To reiterate, Spatial Conditions test to see whether a particular kind of object (party member, actor, go) is entering, is within, or is leaving a specified area (be it a sphere, a cube/rectangle, or a set of nodes). A Message Handler tests to see whether the trigger has received a particular Message. As soon as it receives the message, it tests true. As mentioned in Siege U: 203A, Messages are sent via the game's message delivery system to and from both objects and the game engine itself. There is a third kind of trigger Condition: one with both a Message Handler and a Spatial component together. We'll talk about these "Mixed" Conditions after we understand Pure Message Handlers. 3. The Pure Message Handler Concepts covered:
We're going to make a small trigger chain that turns on a light source five seconds after the trigger enters the world. We'll use two triggers, both of which will be "Pure Message Handlers". A "Pure Message Handler" is simply an instance trigger whose only Condition is a Message Handler. Only one Condition is classified as a Message Handler, and that is "receive_world_message". Tutorial The "receive_world_message" Condition Bring up your test map and place a single trigger_generic gizmo. Give it a trigger instance and add the Condition "receive_world_message". This Condition looks for the message specified in the "World Message Event" parameter, and tests "true" when it is received, depending on the Message Check (similar to the Boundary Check, except we're not dealing with boundaries since this isn't a Spatial Condition). Message Checks for Message Handlers are easy, since there are only two: "on_first_message", and "on_every_message". The Message Check determines whether the Condition will test "true" on either the first receipt of the specified message, or on every receipt. Configure the First Trigger The desired behavior we want for this trigger is to test true every single time it enters the world. We can do this by testing for the "we_entered_world" message with an "on_every_message" Message Check. [SIDEBAR: "WE_ENTERED_WORLD": Any time you want to test to see if an object has been created, you should always test for the "we_entered_world" message. EVERY object receives this message when it has become a valid game object capable of sending and receiving messages and doing everything a valid game object can. DO NOT use the "we_created" message-the creation of an object and its entrance into the world are two separate events, and trying to trigger actions based on an object's creation will not yield good results.] Double-click on the "World Message Event" value (currently "we_req_activate"), and select the "we_entered_world" message. You'll have to scroll up to the top of the list. By the way, don't be intimidated by the amount of messages you see listed; in basic to intermediate triggering, there are only a few messages that are truly useful to you. Make sure the Message Check is set to "on_every_message". Add the "send_world_message" Action (default message is "we_req_activate") and click OK. Our new trigger uses the second-most common message used with a Message Handler: we_entered_world. It will now test true every single time it enters the world, and send a "we_req_activate" message to a target we haven't specified yet. Incidentally, the "we_req_activate" message is THE most common message used with a Message Handler, and not coincidentally, our second trigger will test for that. Configure the Second Trigger Drop down a second trigger_generic gizmo nearby. Copy this new trigger's SCID and paste it into the "send to object" field of our first trigger's Action, and click OK. Open the new trigger again and add a trigger instance, then give it a "receive_world_message" Condition with the "we_req_activate" parameter. Add the "send_world_message" Action, leaving the message at "we_req_activate" and the target SCID empty for now, and click OK. Our second trigger will listen for the "we_req_activate" message sent from the first trigger, and itself will send a "we_req_activate" message to our final target-the light_enable gizmo-that we'll place next. If you're wondering why we didn't just send an activate message to the light_enable gizmo from the first trigger, there's no reason why we couldn't have. We're using two triggers in this example, each receiving a different message, to demonstrate the two most common ways a Message Handler Condition is used: either to detect when the trigger enters the world, or to receive an activate message from another object. Place and Wire the Light Objects Now let's drop a "light_enable" gizmo (/Game Objects/gizmos/commands/effects/light_enable) and a Point Light (/Game Objects/Lights/Point Light) nearby. If you don't see the Point Light when you place it, make sure "View Lights" and "Light Editing Mode" are both toggled on via the Toolbar buttons. The Point Light is one of only two placeable objects (the other being the Spot Light) that can affect lighting locally in a region. The Point Light sheds light in all directions in a certain radius, while the Spot Light shines in one general direction. All placed lights are ON unless an object like the "light_enable" gizmo modifies its properties. Left-click and hold on the Point Light, hold down SHIFT, move it a few feet above the ground, then release. You should see the ground around the light illuminate. If you don't, be sure "Ignore Vertex Color" in Preferences is OFF. If necessary, move the light close to your triggers, then Save. When the Save dialog box appears, check both "Save Lights" AND "Save Nodes" (in addition to "Save Game Objects"). You're saving lights to save the position of the light object, but you're saving nodes so the nodes will appear illuminated in the game. If you don't save Nodes, the light effects won't be visible on node textures. Now that we know our light will appear in the region, we need to wire it so it won't start lit. The "light_enable" gizmo basically turns light objects on and off when it receives a "we_req_activate" message. In order for the light_enable gizmo to know which light to affect, it needs to know the light's special identifier, the "Light Guid". Right-click on the light object and select "Copy Light Guid to Clipboard". Open the light_enable gizmo's properties and paste the Light Guid value into the "siege_light" field under the [light_enable] block in the Component Fields section of the Template Properties tab (whew). Also set the following flags:
Finally, copy the SCID of the light_enable gizmo and paste it into the "send to object" field of the second trigger's Action. We're not done quite yet, though. We're going to set a delay on this trigger's Action. Set the Delay Our second trigger is going to send a "we_req_activate" message to the light_enable gizmo every time it itself receives a "we_req_activate" message from our first trigger. However, we don't want the second trigger to send its message to the light_enable gizmo right away-we want to give that Action a delay. The delay value determines how many seconds AFTER THE CONDITION TESTS TRUE the message will be delivered. In actuality, an Action's message is sent immediately to the game' messaging system when the Condition tests "true", but isn't delivered to the target object until the delay has elapsed. If you look at the "Trigger Properties" tab, you might notice that there are THREE fields to set a delay! There is a "Delay" box in the "Trigger Properties" section at the top of this tab, there is a "Send delay" parameter as part of the "send_world_message" Action, and there is a "Delay" column on the right side of the Action block. To reduce confusion and possible errors, we are only ever going to use ONE of these: the "Delay" column on the right side of the Action block. This Delay field exists in every kind of Action block, and will be very visible when looking at triggers with multiple Actions. So, to be clear: THE OFFICIALLY SANCTIONED PLACE TO SET THE DELAY FOR AN ACTION IS THE FIRST ROW (PER ACTION) OF THE "DELAY" COLUMN ON THE RIGHT SIDE OF THE ACTION BLOCK. The other two fields are legacy fields from previous versions. To be honest, nobody is sure if the other two fields even work, and if they do, where exactly their delay is programmed to take place. Because they cause confusion, they may be removed in future versions of SE. In order to ensure that your delays will work today and across future patches, use the field described above. Now that we know what field to use, just plunk the value "5" in there, which will cause our message to be delivered five seconds after this trigger receives the "we_req_activate" message. Click OK. Before we move on, notice the white text above the green "wire" connecting this trigger to the light_enable gizmo. It should now say "we_req_activate (5.00)", showing that the message has a delay of five seconds. Double-check Our Work Our first trigger should have a Condition of "receive_world_message" with a parameter of "we_entered_world". Its Action should be sending a "we_req_activate" message to our second trigger's SCID. Our second trigger should have a Condition of "receive_world_message" with a parameter of "we_req_activate". Its Action should be sending a "we_req_activate" message to the light_enable's SCID with a Delay of 5 seconds. Both triggers should have a Message Check value of "on_every_message". Our light_enable should have the Light Guid of the Point Light pasted into its "siege_light" field, have "initially_active" set to "false", and have "is_toggle" set to "true". Save the region making sure that "Save Objects", "Save Nodes", and "Save Lights" are checked. By the way…did you provide documentation text for each trigger and its Actions? Look at Siege U: 203A if you don't remember how. That habit will make interpreting your more complex trigger webs MUCH easier, for you and anyone who comes later. Test the Trigger Open your test map. If the first trigger isn't already in the frustum, walk there. Five seconds after the trigger enters the world, a white light should illuminate the terrain. Walk far away so the light is completely out of the frustum, and walk back. Five seconds after the light enters the world this time, it should shut off. An oddity: if your light is next to your start position, you may see the light ON for a split-second before the light_enable turns it off. This is a limitation of how lights work, but since most lights enter the world at the edge of the frustum, it is rarely visible to the player. If your light doesn't work, double-check all the Conditions, Messages, and SCIDs. Make sure you used the Point Light's "Light Guid" instead of its SCID. Also make sure you can see the light in SE-if you place the Point Light too far above the terrain, the terrain may not be within the light's effect radius. Moving On Now that we've seen how Message Handlers work, let's talk about our last type of trigger/Condition: the Mixed Condition. 4. Mixed Conditions (Message Handler with Spatial Condition) Concepts covered:
Overview This is a slightly more advanced trigger, and requires a good grasp of everything we've covered so far to understand its uses. The Mixed Condition tests "true", and therefore fires its Actions, only when both of its Conditions have individually tested "true". Usually, the Spatial component of the Condition will wait to evaluate itself until the Message Handler's target message is received (i.e. it gets a "we_req_activate"). It is technically possible to use Boundary Checks to achieve different effects with Mixed Conditions (see SIDEBAR, below), but we'll use them the way they are implemented in DS. Some restrictions: a Mixed Condition can only ever have one Message Handler. There is no such thing as a Mixed Condition with multiple Message Handlers. In the case of multiple Spatial Conditions (which I don't think were used in DS), Spatial Conditions are tested in the order they appear in the Condition block, from the top down. The Message Handler Condition is ALWAYS the first Condition in the block-SE will automatically move a Message Handler Condition to the top if it is added after a Spatial Condition. Why Use a Mixed Condition? A Mixed Condition gives you more control over the use of a Spatial Condition: specifically, when that Spatial Condition gets tested; and by extension, who gets collected as the Result of Condition. Normally, on a trigger with a single Spatial Condition, that trigger's Action will be executed as soon as an object interacts with the border of the spatial area, i.e. when a party member walks into a sphere. Additionally, if the Action requires a "Result of Condition" value, it will only see the object that triggered it. But what if you want to assign a mood to every party member in a specific area, like the entire region? What if you need to assign that mood to the party only AFTER they have entered the spatial area, and not when they cross into or out of it? We're going to create a trigger sequence that assigns a mood to ALL party members in the region the first time ONE of those party members enters a bounding box INSIDE the same region. Tutorial Place the Triggers Open your test map for editing, and place two trigger_generic gizmos reasonably close to one another. The first trigger will be our bounding box, and the second will assign the mood to everyone in the region. Configure the First Trigger as a One Shot Give the first trigger the "party_member_within_bounding_box" condition with the "on_first_enter" Boundary Check. Place it where your character can walk. Wire a "we_req_activate" message to the second trigger. We set the "on_first_enter" Boundary Check because, in this example, we only want to set the mood once for everyone currently in the region, but not when a second character walks into the bounding box. This essentially makes this trigger good for "one shot", and then we never need to use it again. Unfortunately, the game doesn't know we don't need it again, and will keep a small bit of memory set aside to account for this trigger both in RAM and in every save-game hereafter-UNLESS we set the "One Shot" flag in the "Trigger Properties" section of the "Trigger Properties" tab. If a trigger is set to "One Shot", it will fire once and then delete itself, freeing system resources. It is good practice then to set the "One Shot" flag anytime you need a trigger to fire once, and only once. If your trigger has a Boundary Check like "on_first_enter", it is already a one-shot in function, so it only helps to set the "One Shot" flag. Configure the Second Trigger Open the second trigger, and give it the Message Handler component of our Mixed Condition: the "receive_world_message" Condition. Leave the default message as the ever-useful "we_req_activate", and set the Message Check as "on_first_message". Because our Message Handler will only test "true" on the first reception of a "we_req_activate", it is a one-shot trigger. Set the "One Shot" flag. Now add the spatial component: the "party_member_within_node" Condition. Like in 203A, copy the Region GUID from the "Settings | Region" pull-down menu on the main SE window into the "Region ID" field of the Condition, and leave the rest of the fields as "-1". We are now detecting party members in every node of the region. The Boundary Check (wait_for_first_message / wait_for_every_message) There are really only two Boundary Checks you should ever use with a Mixed Condition: wait_for_first_message, and wait_for_every_message. These are relatively new Boundary Checks made specifically for this type of Condition. These tell the Spatial component of our Mixed Condition (party_member_within_node) to WAIT to evaluate itself UNTIL the Message Handler portion of the Condition has been satisfied, i.e. when it receives the "we_req_activate" message. "Wait_for_first_message" will evaluate the Spatial component on only the FIRST reception of the Message Handler's target message (effectively making the trigger one-shot), while "wait_for_every_message" will allow the Spatial component to evaluate itself EVERY time the message is received. In other words, the "party_member_within_node" condition will not check to see if anyone is within its node range until the trigger receives a "we_req_activate" message. If a party member is within the node range when the activate message arrives, the entire Condition tests "true", and the Action is fired. In addition, every party member inside the region when the trigger fires will be collected as the "Result of Condition", and Actions (like moods) that require Result of Condition can apply their effects to all of them at once. Since we know this trigger is already one-shot, set the Boundary Check to "wait_for_first_message". [SIDEBAR: Mixed Condition Boundary Checks] The following is optional information about how Mixed Conditions actually work. Feel free to skip it if you promise to only use "wait_for_first_message" and "wait_for_every_message" Boundary Checks with Mixed Conditions. If you want to know why these Boundary Checks are a good idea, or want to use triggers in ways we didn't use them in DS (but are theoretically possible), read on. The all-important factor in the behavior of a Mixed Condition is the Boundary Check of the spatial component. Here's why: When the Message Handler component receives its target message (in this case, the we_req_activate from our first trigger), it then checks the status of the Spatial component. If the Spatial component also tests "true", the Action fires. HOWEVER: the Spatial component is independently keeping track of who has satisfied its Condition, and updates itself every time its Boundary Check is satisfied--not just when the Message Handler tests true. When the Message Handler comes along to check on the Spatial component, the Spatial component will report whether or not it has been set to "true" yet, and if "true", who satisfied the Condition. Since the Boundary Check determines when a Spatial Condition is satisfied, and who is detected satisfying it, the Boundary Check has the most effect on what our Mixed Condition does. (Warning: the following example is somewhat theoretical. We stopped creating triggers this way, and the description may not be completely accurate.) For example, right now our Spatial component, the "party_member_within_node" Condition, has a default Boundary Check of "on_every_enter". This means that the Spatial component will detect every party member who walks into the node range-in our case, the entire region-at all times. The first time a party member walks into this region, the Spatial component will test "true" and stay "true", and it will remember the GUID of the party member who did it. Let's say a second party member comes along an hour later (for example purposes), enters the region, and then walks through the bounding box. When the second character walks into the region, it too is logged by the Spatial component as having satisfied the "on_every_enter" Boundary Check. Then, when the second character walks into the bounding box, our Mixed Condition's Message Handler receives its target message and tests "true". It checks the Spatial component, finds that it is already "true", and the Result of Condition are BOTH party members: the one who just entered the region and walked into the bounding box, AND the one who walked into the region an hour before. If the Action uses Result of Condition, like a mood, then both characters will receive the mood-even the party member who is now an hour away. [End Sidebar] Set the Second Trigger's Action We now have two triggers. The first sends an activate message to the second when a party member walks through a bounding box. The second detects that message and then checks for all party members within the region. Our second trigger needs an Action, and that Action will assign a mood to all the party members detected by the trigger. Add the "mood_change" Action to the second trigger, and enter "map_world_path2nt_2" into the "Mood name" parameter. This mood starts white fog, snow, and music. For the full story on Moods, see Siege U: 204 - Moods, but for now, know this: the "mood_change" Action automatically uses the Result of Condition from any and all Spatial Conditions from the Condition block. Moods cannot be assigned to anyone from a Pure Message Handler. Double-check Our Work First trigger:
Test the Triggers Start your test map. If possible, use a multi-character party just so you can see that the mood is assigned to more than one character in the region, even though a single character walked through the initial bounding box. Walk your character into the region if he isn't there already. Note that the mood does NOT change when your character enters the region. Walk your character into the bounding box. Immediately upon entering, EVERY party member in the region should see white fog and snow, and hear pleasant music! If we had given the mood_change action to the bounding box trigger, only the character who had walked into the bounding box would receive this mood, and not everyone in the region (even if another character was standing next to the first). If the Message/Boundary checks were set to fire on "every" message, then every time a party member walked through the bounding box, every party member in the region would be assigned the mood. Many of the Boss Monster encounters in DS use these types of mood triggers. Look at "dl_r1", and note what happens when the Dragon is woken up and when he dies. Another interesting use of moods and triggering is the Gresh encounter in "cf_r1". 5. Wrapping It Up Congratulations! By now, you should have at least a cursory knowledge of fundamental trigger concepts, terminology, and applications. Even though we tried to introduce you to various aspects of the game with different trigger examples, it is by far not a comprehensive treatment. The best place to glean additional working knowledge of triggers and how they're used is to look in the game, and of course experiment on your own! If you have followed each tutorial so far, you should be well able to recognize most of the triggers used in the game. Moods, and especially Fades, might be a little foreign at the moment, and there are many, many Gizmos that you have yet to learn the properties of. Some of these Gizmos will be covered in future tutorials, and many can be understood simply by looking at their Properties. The following two appendices are for reference; feel free to look them over when you're ready. The first section talks about everything on the "Trigger Properties" tab, including every Condition, Boundary Check, and Action. The second identifies commonly used Messages. I hope this Tutorial has been helpful in learning about Triggers! If you've gone this far, pat yourself on the back, since we've really gone beyond a beginner's treatment of triggers into something more intermediate/advanced. Don't hesitate to ask questions online to your fellow classmates, and if you find someone having a hard time figuring this out, give them a hand! Thanks for reading, and have fun! Appendix A: A Detailed Look at the Trigger Properties Tab There are four sections to the Trigger Properties tab:
The "Trigger" section lists each trigger instance that has been created inside the selected gizmo. The name of the gizmo is at the top of the window, and any trigger instances it contains will depend off of it with a Windows Explorer-like convention. If multiple gizmos are selected, this window will list each selected gizmo, each expandable to show their individual trigger instances. Pressing the "New" button will add a new trigger instance to the selected gizmo (or automatically add an instance if only one gizmo is in the list) each time it is pressed. To edit a trigger instance, click the plus (+) sign next to the gizmo name to expand the instance list, and select the instance by left-clicking on it. It is not possible to select (and therefore edit) multiple instances at once. To remove a trigger instance, select the instance and press the "Remove" button. Removing a trigger instance will delete all Conditions and Actions associated with it. Multiple trigger instances act independently of each other, and are functionally unaware of each other's existence. All trigger instances in a single object share the same SCID. The "Trigger Properties" Section Don't get this section confused with the "Trigger Properties" tab, on which this section is found. They should probably have different names, but we'll persevere for now. :) All flags in this section pertain ONLY to the selected instance. Multiple instances can and will use these flags differently. These values will change visibly when different instances are selected.
The Conditions section lists one or more Conditions in a spreadsheet block. To add a Condition, simply press the "Add Condition" button on the right. To delete a Condition, select the Condition's "Name" field and press "Remove Condition". Each Condition block has six columns:
There are two types of Conditions: Message Handlers, and Spatial Conditions. Only one Message Handler may exist per Condition, but a Condition can technically contain multiple Spatial Conditions, although this was not used in DS. Conditions are always evaluated from the top down, stopping if one tests "false". A Condition with both a Message Handler and one or more Spatial Conditions is known as a Mixed Condition, and the Message Handler is ALWAYS the first Condition in a Mixed Condition block. All Conditions are listed below with their parameters. Boundary Checks for Spatial Conditions will appear at the end of the section. Message Handlers:
Some of the Spatial Conditions are very similar: they test for a particular object within a particular kind of area. The kind of objects tested for are:
The Actions section lists one or more Actions in a spreadsheet block. To add an Action, simply press the "Add Action" button on the right. To delete an Action, select the Action's "Name" field and press "Remove Action". Each Action block has seven columns:
There are many kinds of Actions that perform a wide variety of functions. It is beyond the immediate scope of this Lesson to fully explain each of them, especially since some of them are covered in future Tutorials. However, we will at least categorize them so you know something of what they do. The same goes for Messages. There are literally dozens of Messages to send, but we only need to know about a few of them. We'll cover these more common messages in this next section. Sending Messages
Quests and Victory Conditions
Other
Triggers use Messages to either detect when certain events take place (by listening for them with a Condition), or cause certain events to take place (by sending them with an Action). Actions are sent and received by almost every object in the game, AND the game engine itself. Most messages are sent automatically on a certain event; for example, every time an object enters the world, it sends a "we_entered_world" message to the game. Every time an actor dies, it sends the "we_killed" message. The game listens for these messages to keep track of what is going on, but our triggers can also listen for them to execute Actions at appropriate times. Many objects also listen for messages, since they are programmed to perform a certain function when they receive it. For example, the emt_sound_act emitter will start playing a sound when it receives a "we_req_activate". A dungeon door will open when it receives a "we_req_use". There are dozens upon dozens of Messages that can be used with the "receive_world_message" Condition and the "send_world_message" Action. We are going to look at the most common Messages to use with triggers. Most of these Messages can be sent via Actions, but some can only be received via the "receive_world_message" Condition. The first two are Actions specific to trigger function:
|
|
||||||||||
![]() |
©2002 eXtensive Design |