Welcome, Guest. Please login or register.
Kingdoms of Ahln
Pages: [1]
  Print  
Author Topic: Scripting Guide  (Read 453 times)
Justin
Administrator
Citizen
*****
Posts: 189



View Profile
« on: January 05, 2009, 02:29:04 pm »

Introduction

Scripting in KAMS is accomplished via reactions to events. Typically, events which occur in a room are broadcast to everything in the room. Objects can then react to the event. Only objects which mix in the Reacts module can have reactions, but if you set up reactions in-game for an object, the Reacts module will automatically be added to the object, so you can have individual objects that react without having all objects of that type have the overhead of reacting.

Scripts for reactions are kept in the objects/reactions directory and end in the '.rx' extension. These are just plain-text files which contain instructions on what events to react to and how to react. Any object can use any reaction file or files. Note, however, that once an object loads the reaction file the reactions are stored with the object and subsequent changes to the file will not be reflected in the object unless it is explicitly reloaded.

Reactions

Each reaction has three parts: a list of actions, a test, and the reaction itself. The list of actions narrows down which events will be reacted to. The test provides another check before the reaction is executed. This is usually used to see if the event concerns the current object or not. The reaction itself is the code which will be executed if the test returns true.

Actions
This is a just a space-separated list of actions. It looks like:

Code:
!action
smile bow hi

Test
The test should be a short piece of Ruby code which returns a boolean. This can be multiple lines.

Code:
!test
object_is_me? event

Reaction
This is Ruby code which will be executed in the context of the object which is reacting. Currently, and rather dangerously, this means reactions have full access to the server and could really mess things up. So, don't accept reactions from strangers  Wink

Reactions look like:
Code:
!reaction
"say Hello, there."

All reactions have access to at least the following variables:
  • event - the event it is reacting to
  • self - the current object
  • room - where the event is occurring
  • info - the object's Info object
  • $manager - the server's manager

The reaction should have a string or nil as its last value (like a return value in Ruby). If the value is a string, it will be parsed as a command. If successful, this command will then be run as an event.

Matching Reactions

All reactions which match an event and whose test passes will be executed. The reactions are executed after all matches are made. This means you may have multiple reactions which fire for the same event.

Future Events

Future events are events which are executed after some delay. These can be created very simply with the after command. There are two forms: one which takes an event and one which takes a block of code.
First form:
Code:
after 1, :sec, event

The first parameter is how long to wait, the second parameter is the unit of time, and the third is the event to execute. But it is typically easier to use this form:
Code:
after 1 do
"say hello!"
end

If the code block returns a string, it will be parsed as a command. If it returns an event, the event will be added to the queue. Otherwise, whatever the code does is what it does.

Multiple Events

There are other ways to make the object react. One way is to parse a string into a command using the CommandParser and then manually output the resulting event. This works like so:
Code:
event = CommandParser.parse(self, "say hello")
add_event event

Note that any events resulting from reactions will be executed when the reaction is completely finished.

Administrator Commands

Reactions can be managed in-game using the AREACT commands.

Useful Things

Every GameObject has its own Info object, so use the info object to store arbitrary data.

object_is_me? event is a convenient way to test if an event has anything to do with the current object.

Lines beginning with a pound sign (#) are comments and will not be parsed.

All mobiles will have Inventory and Equipment objects accessible via the inventory and equipment variables.

New objects may be created using $manager.create_object:
Code:
scroll = $manager.create_object Scroll
inventory << scroll
Logged
Justin
Administrator
Citizen
*****
Posts: 189



View Profile
« Reply #1 on: January 05, 2009, 02:41:32 pm »

Here is a full reaction file example which the bookman uses:

Code:
!action
say
!test
object_is_me? event
!reaction
player = event[:player]
phrase = event[:phrase].downcase
if phrase.include? "scroll"
        if player.inventory.find("scroll")
                "sayto #{player.name} I would give you a scroll, but you already have one."
        elsif info.scroll and info.scroll[player.name]
                "sayto #{player.name} Pfft. Didn't I just give you a scroll?"
        else
                scroll = $manager.create_object(Scroll)
                inventory << scroll
                give = CommandParser.parse(self, "give scroll to #{player.name}")
                if give.nil?
                        $manager.delete scroll
                        "say Hrm, I seem to be having trouble finding a scroll for you."
                else
                        res = CommandParser.parse(self, "sayto #{player.name} Here. Enjoy.")
                        self.add_event(res)
                        self.add_event(give)
                        info.scroll ||= {}
                        info.scroll[player.name] = 240
                        ""
                end
        end
else
        "sayto #{player.name} I have some blank scrolls, in you are interested."
end


!action
tick
!test
true
!reaction
if info.scroll
        people = info.scroll.keys
        people.each do |n|
                if info.scroll[n] <= 0
                        info.scroll.delete n
                else
                        info.scroll[n] -= 1
                end
        end
end
""

!action
hi bow
!test
object_is_me? event
!reaction
player = event[:player]
"sayto #{player.name} Hello, welcome to my shop. If you would like a nice, blank scroll, just let me know."

Let's go through it one piece at a time.

Code:
!action
say
!test
object_is_me? event
Here, we are defining a reaction to a say event which has the current object as the target. In other words, someone did "sayto Bob something something".

Code:
player = event[:player]
phrase = event[:phrase].downcase
This is just setting up some variables for convenience.

Code:
if phrase.include? "scroll"
Check if the player said anything about scrolls. That is what we are really interested in.

Code:
if player.inventory.find("scroll")
                "sayto #{player.name} I would give you a scroll, but you already have one."
        elsif info.scroll and info.scroll[player.name]
                "sayto #{player.name} Pfft. Didn't I just give you a scroll?"
Check if the player already has a scroll or we gave them one recently. We set up info.scroll later on.

 
Code:
else
                scroll = $manager.create_object(Scroll)
                inventory << scroll
Create a new scroll and put it in the object's inventory.

Code:
give = CommandParser.parse(self, "give scroll to #{player.name}")
                if give.nil?
                        $manager.delete scroll
                        "say Hrm, I seem to be having trouble finding a scroll for you."
Create the event to give the scroll to the person who asked. If there is some kind of problem and instead of an event we get nil, handle that.

Code:
else
                        res = CommandParser.parse(self, "sayto #{player.name} Here. Enjoy.")
                        self.add_event(res)
                        self.add_event(give)
If everything is good, tell the player to enjoy and give them the scroll.

Code:
                        info.scroll ||= {}
                        info.scroll[player.name] = 240
                        ""
                end
Here we add a hash table to our info object to keep track of how long ago we gave someone a scroll.
Code:
||=
means "assign unless it exists already." The empty string at the end is so the server doesn't try to parse a command from the reaction.

Code:
else
        "sayto #{player.name} I have some blank scrolls, in you are interested."
end
If the say event didn't have anything to do with scrolls, mention them casually.


Code:
!action
tick
!test
true
tick is a special event which occurs every time the manager updates all the objects. Since we always want to run this reaction, the test is a simple true.

Code:
!reaction
if info.scroll
        people = info.scroll.keys
        people.each do |n|
                if info.scroll[n] <= 0
                        info.scroll.delete n
                else
                        info.scroll[n] -= 1
                end
        end
end
""
All this code does is decrease the time since we gave each person a scroll. When it reaches zero, it forgets about it.


Code:
!action
hi bow
!test
object_is_me? event
!reaction
player = event[:player]
"sayto #{player.name} Hello, welcome to my shop. If you would like a nice, blank scroll, just let me know."

The final reaction is pretty simple compared to the first two. The object just says hello when someone uses the hi or bow emote towards the object.
Logged
Pages: [1]
  Print  
 
Jump to:  

Powered by SMF 1.1.15 | SMF © 2006-2009, Simple Machines
Terra97 design by Bloc