ID:154483
 
All atoms in LexyQuest are defined with three variables: trigger, triggername, and triggermessage. For most objects, these values will be null. When the map is initialized, any object that has a text value set for the trigger is added to a list of objects called "triggers".

Whenever an action is performed on an object that has an assigned triggername (actions include: a mob is awakened or killed, an item is picked up or dropped, a room or turf is entered or exited, a room is searched, etc.), a proc searches the triggers list for any object whose trigger var matches the object's triggername. If the trigger object has a value set for triggermessage, it also checks to make sure the action (passed along through the triggering proc as a message) matches the triggermessage.

For instance, let's say we want a wall to disappear when a golem is slain. For this to work, we need to make a new instance of the turf/wall, and of the mob/golem. The golem needs a triggername, and the wall needs a trigger. It doesn't really matter what we decide to call these, as long as they're the same thing. For simplicty's sake, we'll just call them "golem".

Now, whenever the mob we've labeled "golem" is activated, it checks for triggers with the same label. We don't want the wall to actually vanish until the golem is dead, though, so we need to specify a triggermessage for the wall. The message passed along when a mob is slain is simply "slain."

Turning into a floor tile is the default action for walls... turning into a wall, by contrast, is the default action for floors. This makes it easy to make toggleable passageways. Similarly, the default action for closed doors is to open and for open doors is to close. Of course, all of these actions are accomplished by creating a new turf on top of the old... but by setting a flag called "inheritance", you can make the new turf inherit the same trigger values as the old one.

Each type of object in the game has a default action when triggered. Some have more than one possible, based on the message passed along. For instance, a triggered door would open by default, but could be locked, if the right triggermessage was passed along.

There are also trigger objects, which are simply used to modify a trigger's message. They have the variables trigger, triggername, triggermessage, and a special variable called "outgoing", which is the message they pass along. For instance, let's say we want a door to unlock when a monster is slain. We can't link the monster directly to the locked door... if we did, we couldn't specify any triggermessage, since "awake" and "slain" mean nothing to a door. We can make a trigger object, which checks for the "slain" message, and triggers the door in turn. You would also use a trigger object if you wanted a normal door to lock, rather than open... no object by itself generates a "lock" triggermessage.

Trigger objects can also be used for special effects: they can send text messages to the person who started the trigger chain, the room in which the event is occuring, and/or the game itself. They can also trigger the end of the game. Features planned for them include adding conditionals, such as only activating if the character who started the trigger is (or isn't) a member of certain classes, or is or isn't carrying an object with a particular triggername.

This whole system might seem a little pointless and contrived, when there's a whole powerful programming language available for coding "special" mobs and turfs and areas and objs... but the idea is to allow such things to be soft-coded, so that completely new and original scenarios can be devised and run from within the game environment.
Interesting...when I reached the point of wanting to do this in my first RPG engine, I started researching creating my own in-game compiler so that GMs could put together scripts. They wouldn't actually write code in game, but would put together statements from a list which would turn into the code underneath. Very similar to creating mail rules in Outlook Express.

My solution was way too complicated (especially for me) and pretty much ended that engine.

Your solution sounds much more doable!
In response to Deadron
My solution was way too complicated (especially for me) and pretty much ended that engine.

Your solution sounds much more doable!

I was planning a condition-action system instead, similar to Starcraft, for one of my many cancelled projects (I called it Gi, and it was supposed to be a player-built MUD).

Essentially, it loops through one trigger per tick (or more if there are a lot of triggers) and checks to see if the conditions of that trigger are true. If so, it then proceeds to activate all of the actions inside the trigger.

This method has merits and drawbacks... instead of having to create dozens of separate instances, you can create a generic trigger, that affects anything that meets the criteria. Unfortunately, there is no encapsulation involved... if you delete the mob, you must remember to delete the trigger, too, or else it's processing a trigger that doesn't even work. Also, it requires one to come up with an extensive list of actions and conditions to provide for almost every circumstance that players desire.

Fortunately, this method has an additional advantage of being able to check multiple conditions... for example, if you had to kill 3 golems for the wall to open, and killing but two, or killing the leader golem, would do nothing.

In my case, it would be similar to this:
(The names are the values of 'id_string' chars.)

Conditions:
If !exist golem_1
If !exist golem_2
If !exist golem_3

Actions:
Del wall 12,14,1
New floor 12,14,1



A good idea would be a slight combination of both methods... but don't ask me what that combination would be weighted as. =)
In response to Spuzzum
On 8/5/01 4:25 am Spuzzum wrote:
My solution was way too complicated (especially for me) and pretty much ended that engine.

Your solution sounds much more doable!

I was planning a condition-action system instead, similar to Starcraft, for one of my many cancelled projects (I called it Gi, and it was supposed to be a player-built MUD).

Essentially, it loops through one trigger per tick (or more if there are a lot of triggers) and checks to see if the conditions of that trigger are true. If so, it then proceeds to activate all of the actions inside the trigger.

This method has merits and drawbacks... instead of having to create dozens of separate instances, you can create a generic trigger, that affects anything that meets the criteria. Unfortunately, there is no encapsulation involved... if you delete the mob, you must remember to delete the trigger, too, or else it's processing a trigger that doesn't even work. Also, it requires one to come up with an extensive list of actions and conditions to provide for almost every circumstance that players desire.

Fortunately, this method has an additional advantage of being able to check multiple conditions... for example, if you had to kill 3 golems for the wall to open, and killing but two, or killing the leader golem, would do nothing.

In my case, it would be similar to this:
(The names are the values of 'id_string' chars.)

Conditions:
If !exist golem_1
If !exist golem_2
If !exist golem_3

Actions:
Del wall 12,14,1
New floor 12,14,1



A good idea would be a slight combination of both methods... but don't ask me what that combination would be weighted as. =)

Multiple conditions can be handled with trigger objs... they also have a "count" variable, which is the number of times the trigger obj must be triggered before it will pass its own trigger message along. As I write this, it occurs to me that it would also be a good idea to give them a variable that is the maxium amount of times they can be triggered... that way, you could make an action occur after any 3 out of X golems were killed, but never happen again.

I'll admit that my system has limitations, and would be cumbersome on a large scale, but on the small scope of a LexyQuest quest, it behaves adequately. :)
I've got a similar trigger system working in Delve!. Or at least I think I do. I coded it a long time ago and didn't even finish putting in all the functionality I intended to because I didn't need it yet and now I largely forget how it's set up. Oh well.

Anyways, as far as the contrivedness goes, it's worth mentioning to less experienced coders that while you can code every single special case of something individually, if you've got more than a couple special cases and you're doing some similar actions in most or all of them, you're better off generalizing the code and creating outside procs to handle the functions that each case has to.
In response to LexyBitch
I'll admit that my system has limitations, and would be cumbersome on a large scale, but on the small scope of a LexyQuest quest, it behaves adequately. :)

Too bad computers don't understand English and don't read our minds to see exactly what we'd like to happen and when. ;-)

"Hello, Spuzzum, what event would you like to add to LexyQuest v932.1 today?"
"When these three particular golems here are killed, I want you to open the wall I click on now. [Click]"
"Sure thing, Jeremy. Would you like another event to occur?"
"Nope, that's all I'd like for now. Save this to a savefile that seems appropriate, and then test it out for me while I go get a can of root beer."
"Sure thing, Spuzzum...
Saved to 'spuzzum-test-file-001.sav'...
Creating AI characters...
Characters have defeated golems...
Wall opened...
Is the result satisfactory?"
"Yup! ...Wait a minute... how'd you know my name was Jeremy?"