Event system

Events in the Orchid Rain map editor system are a custom solution for communicating different objects in a given map. The event system is comprised of two types of components: Triggers and Receivers.

Triggers

Triggers should not be confused with the trigger of a Unity Collider. In the Orchid Rain map editor, a Trigger is a custom component that will "send a message" to any Receiver listening to it. A Trigger will get activated when a predetermined action occurs in the game. There are many types of Triggers, and their differences are in the actions required to activate each of them. Depending on the type of Trigger, it can be set to repeat itself indefinitely, or a specific number of times.

All types of Triggers have at least two parameters. The first parameter is "Event Trigger Id" which has the purpose of determining which Receivers will listen to it. You can think of this field as the channel on a radio, or "walkie talkie". If Event Trigger Id is set to 3, for example, then it will send its message on "channel" 3. Every Receiver that wants to listen to it will need to be "tuned" to channel 3. The second parameter "Repetitions" defines how many times the Trigger will run, or how many times it can be activated. For example, in the case of a button that will open a door, you will give the button a Trigger and set its repetitions to infinite (by typing "-1"), so that the button will work every time it is pushed. If you want this button to open a door, but not close it when the player presses it a second time, then you would set its Repetitions to 1. This setting could also be thought of as the charge on a battery. If you set it to 10, then after 10 uses the Trigger's battery will be depleted and won't send any more messages.

Area Trigger

Activated when the player enters a defined area making use of any Unity Collider with its "trigger" option enabled. Add an Area Trigger component to a GameObject with a Collider that has its trigger setting enabled.

Interact Trigger

Activated when the player interacts (presses the E key) with an object, such as a button. Add an Interact Trigger component to a GameObject with a Collider on it. Every time the player presses the interaction key (E), while looking at this GameObject, the trigger will be activated.

Kill Trigger

Activated when an NPC is killed. This Trigger should be added to a spawner, and all NPCs that spawn from it will have this Trigger. Add a Kill Trigger component to an NpcSpawner. Every NPC spawned from this spawner will have the trigger on them, which will be activated when any of them is killed. If Non Lethal Activates is checked, then this trigger will be activated when the respective NPC is taken out with a non-lethal weapon (in addition to lethal ones), like the Dart Gun or the Fists. If this option is unchecked, this trigger will only activate if the NPC is killed, and it won't activate when using non-lethal weapons .

Destroy [uncertain]

[It's possible that wont be necessary, as it could overlap with the Kill Trigger]

Timer Trigger

Activated after a given number of seconds following the start of a map. Add a Timer Trigger component to any GameObject. This trigger will be activated after the seconds you input pass after the map is started. When used together with TimerReceiver (seen below), this Timer will run only after its corresponding TimerReceiver gets called by any other Trigger.

Level Start Trigger

Activated when a map starts. Add a Level Start Trigger to any GameObject. This trigger will be activated right after the level loads.

Level End Trigger

Activated as all Mission Objectives are completed on a map. Add a Level End Trigger to any GameObject. This trigger will be activated just before a level ends.

Objective Success Trigger

Activated when a specific Mission Objective has succeeded. Add an Objective Success Trigger to any GameObject. This trigger will activate when the given Objective Number is completed.

Objective Fail Trigger

Activated when a specific Mission Objective has failed. Add an Objective Fail Trigger to any GameObject. This trigger will activate when the given Objective Number is failed.

Counter Trigger

Works together with CounterReceiver. Will automatically create a CounterReceiver when added to any GameObject. This trigger will internally count when its corresponding CounterReceiver gets activated (by any other Trigger, like a KillTrigger for example). When the count reaches this Trigger's Count Value number, it will be activated. An example will be provided.

Receivers

Receivers are a custom component that will perform an action after a Trigger has been activated. The action of a Receiver is directly tied to its type, meaning that you can't create a personalized action; you can only perform the predefined actions of the Receivers that come with the map editor. If you have a request for a new type of Receiver, you can let me know in the Discord, in the #questions-and-suggestions channel.

All Receivers have at least one parameter, which is "Event Trigger Id". This field should be exactly the same value as the Trigger you want to activate this Receiver.

Spawn Receiver

Makes a spawner spawn an NPC, according to the spawner's settings. This Receiver should be added to an NpcSpawner.

Pickup Spawn Receiver

Makes a pickup spawner spawn a pickup, according to the spawner's settings. This Receiver should be added to any pickup spawner, like WeaponSpawner, ItemSpawner or HealthSpawner.

Visual Novel Receiver

Displays a specific Visual Novel dialogue on screen. This Receiver can be added to any GameObject. An example of its usage will be given in the next chapter of this guide.

Objective Receiver

Forces a "pending" Mission Objective to either Succeed or Fail. It cannot change the state of a Mission Objective once it has either Succeed of Failed. This Receiver can be added to any GameObject.

Animation Receiver

Plays an animation. Specifically, it switches the value of a given Bool parameter from an Animator. This Receiver should be added to an object with an Animator component.

Npc Health Receiver

Damages or Heals an NPC, or any object that has health [it's possible that destructible objects could be destroyed with this Receiver]. Can also define a type of damage and area of damage. This Receiver should be added to an NpcSpawner.

NPC faction change [unconfirmed]

Changes the Faction of an NPC. This Receiver should be added to a spawner. This hasn't yet been implemented.

Sound Receiver

Plays either a pre-defined or custom audio file. This Receiver should be added to an object with a Generic Audio Player component. There two components that derive from Generic Audio Player, those are Button Sound Player and Door Sound Player. The only difference is that these two components will give you the option to select pre-defined sounds that are already in the game. The Stop checkbox will make the Generic Audio Player stop its sound, instead of playing it. You can make one receiver Play a looping sound clip, and then make it Stop with another receiver.

Message Receiver

Displays a message on screen. The message can be "big" like the mission messages, or "small' like the pickup messages. This Receiver can be added to any GameObject.

Counter Receiver

Works together with CounterTrigger. Will automatically create a CounterTrigger when added to any GameObject. CounterReceiver will make its corresponding CounterTrigger count. When this Receiver gets called by any Trigger, it will make CounterTrigger add one to an internal counter.

Timer Receiver

Works together with TimerTrigger. Will automatically create a TimerTrigger when added to any GameObject. TimerReceiver will make its corresponding TimerTrigger only start when this Receiver gets called.

Trigger Switch Receiver

This receiver will either enable or disable all Triggers contained in the same GameObject it resides in. When the Deactivate field is unchecked, this receiver will make sure its neighbor Triggers will only "turn on" after the Trigger Switch Receiver gets called by its own Event Trigger Id.

Level Music Receiver

Activates or Deactivates the xMusic variant of the level. Each map has a set of two songs. Normal music and xMusic. xMusic can be activated to emphasize a moment of high intensity, like a big battle. Check "X Musix" to make this receiver switch to the xMusic variant. Uncheck this option to make this trigger switch to the Normal Music -which you should at some point, probably when the combat ends. Enable X Music Once will only be read if X Music is checked; it will make the xMusic song be played only once, thus not requiring another receiver to turn it off.

Examples

Example 1: Spawn multiple NPCs after a time

Let's say you want your level to get harder if the player takes too long beating it. After 10 minutes of gameplay you could spawn many NPCs throughout the level. We'll do that for this example. In Unity, with a GameObject selected, you can press Ctrl + D to duplicate it. Before doing that, I deleted the MissionObjective component.

With all your spawners selected, add to the a Spawn Receiver component and uncheck Spawn At Start. You can edit and add components to multiple GameObjects at the same time, as long as they are all selected.

Now, create a new empty GameObject and give it a Timer Trigger component. Don't put the Timer Trigger in the same GameObject as the NpcSpawners. If you add this Trigger multiple times, all the Receivers listening will activated multiple times as well. Just one Trigger will be enough. Also, if you add this Trigger to a NpcSpawner, the spawned NPCs will also inherit it. As a consequence, you'll get infinite NPCs spawning.

Note that the Event Trigger Id property is set to 0 for both the Trigger and all the Receivers. I'm giving it a time of 10 seconds to test that it works. If we wanted it to activate after 10 minutes, we can just write "60 * 10" instead an hit Enter.

Make sure that you don't have any other Trigger on this map for the moment. Your NPCs should spawn after 10 seconds. You only needed one Trigger. In fact, you can use only one Trigger to activate as many Receivers as you'd like. We'll add another Trigger to the map.

Example 2: Knock out many NPCs at once

Create an new empty GameObject, give it a Collider with the Is Trigger setting enabled and add an Area Trigger component to it. You now need to give it another Event Trigger Id to differentiate it from your Timer Trigger. For this case, we'll also leave the Repetitions value at -1, which means infinite. Each time a Trigger gets activated (entering the Area Trigger for this case), the Repetitions value will lose 1 point, regardless if the Trigger has a Receiver or not. The player might enter this Area Trigger before the NPCs spawn, and if its Repetitions were 1, then that would mean that once the NPCs spawn, this Trigger would be already spent. That's why we'll leave it on infinite, or -1.

It would be better if you change the layer of this GameObject to MissionTrigger. The reason for this is that Objects on MissionTrigger Layer will ignore bullets and projectiles. If you have a very big Collider set as a trigger, and try to shoot through it, your bullets will impact with it, NPCs on the other side will be shielded. As a rule of thumb, set all Colliders with its Is Trigger option enabled to the Layer "18: MissionTrigger" always.

Now, grab all your spawners and give them an NpcHealthReceiver component. After that, set its Event Trigger Id to the same value you gave to your Area Trigger. If you leave it on 0, this Receiver will activate with the Timer Trigger, and we don't want that.

Let's review the parameters of this component.

  • Damage

    • A positive value will damage the NPCs that spawn from this receiver

    • A negative value will heal them instead

    • If you want to kill an NPC, just set this value to a big number, like 999

  • Hurt Type

    • This determines the hit box that will be damaged

    • Different hit boxes will make different animations play when the NPC is hurt or killed

  • Damage Type

    • Different values of Damage Type will make the NPC play different animations, either when hurt or when killed

    • Ragdoll

      • Makes the NPC turn ragdoll after death

    • Gibbing

      • Makes the NPC have a chance of gibbing after death

    • Dismemberment_Slash

      • Makes the NPC have a chance to lose a limb when dying

    • Dismemberment_Shot

      • Makes the NPC have a chance to lose a limb when dying, with the difference that the lost limb will disappear completely

    • Pass Out

      • Makes the NPC not die, but be knocked off instead

    • Insta Gib

      • Makes the NPC gib always

  • Hit Box Type

    • Determines which limb will be lost on dismemberment death

After setting your Area Trigger and Npc Health Receiver to the same Event Trigger Id, you can test your make and see what happens when you enter the Area Trigger.

Example 3: Show a message once the player takes out 3 enemies

First, select three NpcSpawners and add a KillTrigger component to all of them. I'm setting the Event Trigger Id value to 5 in order to not activate the other receivers that I already have in this map. I'm also checking Non Lethal Activates, which will make this Trigger activate if the NPCs are knocked out with a Dart Gun, or any other non-lethal weapon.

Now, you can create an empty GameObject, or use any other GameObject. For order's sake, I'll create a new one with Ctrl + Shit + N and call it "[Events]". In this new empty GameObject, add a CounterReceiver component. Once you add it, you will see that a CounterTrigger component will be automatically created, in addition to your new CounterReceiver. These two components are linked, and you should not delete one without the other, as they won't work. This next part can be a little confusing, so I will try to exaplain in detail.

CounterReceiver will be called by any other Trigger with a matching Event Trigger Id number. Each time this Receiver gets called, it will internally call the CounterTrigger that was automatically created and make it "count". CounterTrigger will only activate once its internal counter variable reaches the number you set in the Count Value field. When it activates, CounterTrigger will call any other Receiver with its same Event Trigger Id number. You have to ensure that both CounterReceiver and CounterTrigger DO NOT share the same Event Trigger Id, as not only do they not need it to be the same to communicate, but if they have the same Event Trigger Id, then they will infinitely call each other in a loop that could potentially freeze the game.

For our example, I will give the CounterReceiver an Event Trigger Id value of 5, so that the 3 KillTrigger components I added to the NpcSpawners will call it. The CounterTrigger Count Value field will be 3, so that it activates when the CounterReceiver makes it count 3 times (one time for each NPC (with a KillTrigger) taken out. Then, I'll want to call some other Receiver when the CounterTrigger gets activated, so I'll give its Event Trigger Id a value of 6.

Now I need a last Receiver to be called when we take out these 3 NPCs. You can add a Message Receiver to this same GameObject (called "[Events]" in my case). I'll set its Event Trigger Id to 6, so that the CounterTrigger will call it and make it show a custom message.

You can also use an ObjectiveReceiver if you want to have a Mission Objective that needs the player to take out these 3 NPCs.

Example 4: Enable an Area Trigger after the player takes an NPC out

Imagine that you want to spawn a wave of enemy reinforcements when the player enters an area, but only if an Officer has been neutralized. If the player doesn't do anything to that specific NPC, then they will be safe to enter that area without spawning any enemies.

Add an NpcSpawner and give it a Kill Trigger component. I'll use an Event Trigger Id of 16 in this case.

Then, set your Area Trigger as usual: create a new GameObject, give it any Collider, mark it as trigger and set its dimensions as needed. Then, add an Area Trigger and a Trigger Switch Receiver. Note that the Trigger you use needs to have a different Event Trigger Id than the Trigger Switch Receiver (T.S.R.). This is because the T.S.R. will be called by the Kill Trigger of the Officer NPC, and that will make the T.S.R. enable the Area Trigger, but not actually trigger it (remember, the event that will trigger the Area Trigger is the player entering the area inside the Collider).

If the Deactivate setting in unchecked, the Trigger Switch Receiver will "switch off" any other Triggers contained in the same GameObject (you can think of them as neighbors) as soon as the game starts; and will "switch them on" back again when it gets called (by some different Trigger). If Deactivate is instead checked, all other Triggers contained in the same GameObject won't be switched off when the game starts, they will be left as they are, that is enabled; but when that T.S.R. gets called, all its neighbor Triggers will be disabled, so they won't work anymore (unless they are re-enabled again).

So, to make a wave of reinforcements spawn when the player enters that Area Trigger, just add a bunch of NpcSpawners and give them a Spawn Receiver with the same Event Trigger Id as the Area Trigger (17, in my case).

Organize your Receivers and Triggers

If you are making a fairly complex network of Receivers and Triggers, my advice is that you create a hierarchy of GameObjects to "house" your Receiver/Trigger components. Create one Empty GameObject. Inside, create roughly one empty GameObject per Event Trigger Id. In this example, I'm naming these GameObjects with the ID first, followed by a short description of what that set of Receivers and Triggers do. Then, I only add the components that will share (to an extent) that ID. This way, I won't get confused when I have multiple objects communicating using the custom Event System.

By now you should have a more solid grasp on how to mix Triggers and Receivers to make things happen in game. Remember that you can always ask for support in the Discord, using the #mod-troubleshooting channel.