Previously I described how to set up a location, create some simple objects in that location, and set up some simple verbs that sent messages to players when they (for instance) meditated or rubbed a statue's belly.
Using just those tools, it's already possible to create a lot of atmosphere. But it can be more fun to have objects and verbs that can have 'real' effects--that can change an object's description (or other properties), or can move an object around. This combined with the previous methods can create some very nifty effects.
There are several tutorials around that do a good job explaining how to create virtual pets. I recommend checking them out whether or not you like my approach.
In this tutorial I will explain how to create a fishbowl with fish in it and fish food which players can feed to them.
So you want to create a fishbowl with fish in it. There are two ways to go about this that I can think of.
I'll be taking that second approach in this tutorial; it's simpler, and should be good enough for most purposes.
One complication with this approach is that we want people to be able to interact with the fish, to use the feed food to fish command for instance.
To get around this problem I use the following cheat: If you define 'fish' to be an alias for the 'fishbowl' object, then if I define a feed verb on the object players will be able to type feed stuff to fish and the correct verb will be called.
So let's go ahead and create the fishbowl, giving it the alias 'fish'. In the previous tutorial the command to do this kind of thing was revealed to be:
@create $thing called fishbowl:fish
Now give it an appropriate description. Remember to include the fish in the description!
@describe fishbowl as "This is your standard oval-shaped fishbowl. It's mostly filled with water, with gravel and a miniature treasure chest on the bottom. Several guppies swim around happily."
There are several verbs that might be appropriate here that can be implemented using the techniques in the previous tutorial. For instance, you could allow people to 'tap on fishbowl' or you might tell people who try to 'catch fish' not to be so mean.
A quick example:
@verb fish:eat this none noneDo you understand what the above commands do? If not, try to figure it out, or try typing them in and see what happens when you try to 'eat fish'. (Note that, because 'fish' is an alias for the 'fishbowl' object, the same thing happens if you try to 'eat fishbowl'. Oh well.)@program fish:eat
/* What happens if someone tries to eat the fish? */
/* @verb fish:eat this none none */
player:tell("These aren't the kind of fish you use for sushi, bucko!");
this.location:announce(player.name + " eyes the fish hungrily, then turns away.");
.
There are still some unanswered questions. What should being consumed entail for the food? Does it disappear entirely from the MOO, never to return? But then you'll have to recreate it every time you want to feed the fish (or every time you want someone else to be able to feed them).
Another approach which is probably better for this situation is to have the fish food 'disappear' by moving it to some predefined location. Then if someone wants to feed the fish again they just have to retrieve the food from that location and they're ready to go.
One way to go about this is to have a 'box of fish food' container and move the fish food to that whenever it is 'consumed'. So let's do that.
@create $thing called "fish food":foodCreating a box to keep the fish food is trickier, but not too far removed from what we've done before.@describe food as "Small flaky bits of fish food."
Fortunately, there's already a generic 'container' object that you can base your own containers on. You can clone it as follows:
@create $container called "box of fish food"(Note that where in previous commands I had '$thing' I now have '$container'.)
If you now 'examine box' you'll see that there are a bunch of verbs built in to your new container:
Obvious verbs: p*ut/in*sert/d*rop <anything> in box re*move/ta*ke/g*et <anything> from box open box @lock_for_open box with <anything> close box @unlock_for_open box @opacity box is <anything> g*et/t*ake box d*rop/th*row box gi*ve/ha*nd box to <anything>Get, drop, and give work the same as with other objects. "put food in box" puts the fish food in the box and "take food from box" takes it back out again. The other verbs are not particularly important to the project at hand (but if you want to learn more about them there is plenty of information in the LambdaCore Database User's Manual).
Setting a .home can be useful for several reasons. For instance, if a guest player is holding an object when he or she disconnects normally the object is sent to nowhere, a sort of limbo, until its owner retrieves it in some way. However, if the object has a .home property set it goes there instead. (For this reason you may want to go back and give other things you've created .home properties at some point.)
In this case, the fish food in some sense 'belongs' in the fish food box, so let's set its home to the box:
To test that this is working, try the following command:
@eject food from meThe '@eject' command sends the object in question (if you are holding it) to its home, or if it has no home defined then it sends it to nowhere. If you now:
open boxyou should see that the food is now in the box. (If it's not, you can get it back using '@teleport #objnum to me'.)look in box
@verb fish:feed any to this
One way to do it would be to simply use the object number of the 'fish food' object whenever you wanted to refer to the fish food. This is clumsy, though, and may make it difficult to interpret your code later.
Another way is to create an appropriately-named property of the fish which you can then refer to in the feed verb's program. (This has the added advantage, though a slight one in this case, that if you later want to change what people can feed to the fish you only need to change this property rather than going through the program and changing every instance of the property number.) So let's do that:
@property fish.fishfood #objnum(Substitute the object number of the fish food object for objnum above.)
Now if you refer to 'this.fishfood' in the verb, it will look at the .fishfood property of the fish object and find a reference to the actual fish food. Yay!
Fortunately there's a trick that can be used here. Previously, we discussed iobjstr and dobjstr, which hold respectively the text that the player typed for the indirect and direct objects following the verb.
If the direct or indirect object actually refers to (or may refer to) an actual object, you can refer directly to that using iobj and dobj. So for instance if a player typed 'feed rock to fish, if there were a rock in the room, dobj would hold the object number of the rock and iobj would hold the object number of the fish.
This gives us our first stab at the feed verb's program:
/* @verb fish:feed any to this */
/* Make sure the player is feeding fish food to the fish */
if (dobj == this.fishfood)
/* OK, s/he's giving the fish the fish food */
else
/* The player is feeding something other than fish food to the fish */
player:tell("You can't feed that to the fish!");
endif
.
Every object has a property, the .location property, that says where it is. If the object is in a room, its .location is the room's object number. If the object's in a container, the object's .location is that container. And if it's being carried by someone, the object's .location is the player who's carrying it.
So here's the next stab at the program (the new part is in bold):
/* @verb fish:feed any to this */
/* Make sure the player is feeding fish food to the fish */
if (dobj == this.fishfood)
/* OK, s/he's giving the fish the fish food */
if (this.fishfood.location == player)
/* the fish gets fed */
else
player:tell("I'm sorry, but you're not carrying the fish food.");
endif
else
/* The player is feeding something other than fish food to the fish */
player:tell("You can't feed that to the fish!");
endif
.
player:tell("You drop some of the fish food in the fishbowl. The fish gobble it up happily!");To do the second part, you have to know two things:
this.location:announce(player.name + " drops some fish food in the fishbowl. The fish gobble it up happily!");
this.fishfood:moveto(this.fishfood.home);Assembling all the bits, we get:
/* @verb fish:feed any to this */We're done!
/* Make sure the player is feeding fish food to the fish */
if (dobj == this.fishfood)
/* OK, s/he's giving the fish the fish food */
if (this.fishfood.location == player)
/* the fish gets fed */
player:tell("You drop some of the fish food in the fishbowl. The fish gobble it up happily!");
this.location:announce(player.name + " drops some fish food in the fishbowl. The fish gobble it up happily!");
this.fishfood:moveto(this.fishfood.home);
else
player:tell("I'm sorry, but you're not carrying the fish food.");
endif
else
/* The player is feeding something other than fish food to the fish */
player:tell("You can't feed that to the fish!");
endif
.