In a mud world, communication is maybe the most important aspect of them all. This course will try to show you how to make monsters and players communicate with each other.
Btw, I hope you remember how to add commandpaths, since most of the examples are commands. ;-)
Let's start off with some simple player<->player communication.
This is a funny little command. Just don't exploit it. People could get annoyed by it. Use it against friends only.
The command will send a delayed message to a player. It will wait 60 seconds before sending it. You can even quit the game and the message will still be delivered. Of course, the recipient must still be logged in, and available to tells.
There's one new function here, 'find_player()'. This function will search for a player with the name specified in the argument. There is a function called ' find_living()' also. This function works just as 'find_player()', with the exception of that while 'find_player()' only searches for players, 'find_living()' will search for both monsters and players.
! NB ! find_living() will only work for those monsters that have used set_living_name() to set their names.
As you can see, communication is not really something new. Even though we haven't used the term communication before, we have used communication throughout almost all the courses.
This function is a multitell function. It will send a specified message to several players. The format for this command is
'mtell <startlevel> <endlevel> <message>' Ex: 'mtell 1 19 Pitiful mortals! Hah! I loath you!'
The example may not be a very nice message to send, and is not recommended. ;-) Anyways, the two levels are the start and end level of the players that shall receive the message. You should be careful not to abuse this, people might get annoyed and you might find yourself kicked out... ;-0
There are two new functions to examine..
void message( string class, string message, mixed target, mixed exclude );
object *ranged_users(int start, int end);
Let's take the last one first. 'ranged_users()' will return all users within the specified range. There is another function called 'users()', but this one will list ALL users, and we're not interested in all of them.
The 'message()' function
requires special attention. This function will
send a 'message' of a special 'class' to all 'target' users, except all
The first argument should be the class of the message, it can be of several types: 'tells', 'hityou', 'party', etc... We intend to use the 'tells' class since this is a "normal" tell. The second argument is the message to be sent. The third argument is the receiver(s) of the message. This may be of several types (read the man file for more info), but the most used is either an object or an array of objects. The fourth argument is of the same type as the third, but it defines all objects that should NOT receive the message. This argument is optional.
In the 'mtell' command, you may set 'TP' as the fourth argument to 'message()' if you don't want to have the message sent to yourself.
Before we proceed, we should discuss colours. If you are familiar
with the command 'colour' in the mud, you should know what I'm talking
about here. It's possible to use colours when sending messages to players.
You can, for instance BOLD or UNDERLINE important words in a sentence,
or you can have the whole sentence in RED. This is only possible, of course,
if the player's terminal supports these colours.
Special colourcodes in the mudlib are prepended and appended with the two characters '%^'. If you, for instance, want to send a BOLDED message to the player gunner, you can do this (using the standard tell command).
'tell gunner %^BOLD%^WAKE UP! WAKE UP!%^END%^'
Actually, if you're looking at the file inside the mud using one of the standard display commands (cat/more), you will probably just see a bolded line in the example and not the colourcodes themselves. ;-) Well, you can get a full listing of all the colourcodes if you take a look at the file called '/std/wrapper.c'. The colourcodes are written without the two special characters in the mapping colour at the start of the file. Or you can wait until the next command('ctell').
There is also one more new thing to discuss, the foreach() loop. It is faster and has a bit nicer syntax than for(), IMO.
Syntax(for arrays): foreach(<var1> in <array>)
Let's start off with a comparison example:
//---Using for()--- int counter; object *players = users(); for(counter=0;counter<sizeof(players);counter++) printf("User: %s\n",players[counter]->query_name()); //---End of for()--- //---Using foreach()--- object player; foreach(player in users()) printf("User: %s\n",player->query_name()); //---End of foreach()---
These two examples do the same, but IMHO, the last one is preferable. The last one is also faster. The for() loop executes the argument every single loop, while the argument of the foreach() is only executed once.
It is a lot cleaner, so please read through 'man foreach' carefully. (We will discuss the mapping part of foreach() later in this course)
This time, we have messed around with the colourcodes. The 'ctell' command will take the specified message and colour it before sending it off to the recepient.
There's nothing really new in this command. It creates an array containing all the colourcodes, which it chooses the colour from in the 'query_ccode()' function.
This command will send a specified message to all users inside a special area. The area must be specified with its path as far as needed to make it unique.
source_file_name(ob) - Returns the filename of ob, without the object instance number, which all objects get. users() - Will return all living objects.
These two monsters talk a lot. Maybe even too much. ;-) And, they're not actually friendly to eachother, either.
Well, these files should give you an idea of how to hook messages
and reply to them. This hook is available to monsters only. There is
no way of hooking messages to a player. It wouldn't be a very nice
to do, either. If existed, this could be exploited to listen in
on conversations between players.
Just be aware of one thing about this hook. It is called BEFORE the hooked message is displayed. Meaning that you must do a call_out() if you want a message to appear after it (This is not a very good way of doing it, but it's the only way today).
Oh, and it hooks ALL messages the monster receives. That includes messages in the room like 'Gunner enters.' and such.
And it's time to discuss datafiles and mappings. Datafiles, or objectfiles, usually have a suffix of '.o', to seperate them from code files which ends in '.c'. As with codefiles, the mudlib (or rather driver) figures out what suffixes to use, so please don't use '.o' suffixes at all. There are two functions that are used:
They both use the filename in which to save the datafile which in our case is the filename of the command (except with changed suffixes). To figure out this fully qualified pathname, we use source_file_name(), which is a good function to remember when working with objectfiles. We also use file_size() to see if the objectfile exists before we try to load it, as the mudlib (or rather driver) will complain if we try to load a non-existant objectfile.
There's one more important thing to note when discussing our load/save functions.
if (filename[-2..-1] == ".c") filename = filename[0..-3];
What do these two lines really do? As filename is a string, it consists of several characters. These characters can be referenced/indexed quite easily. filename[0..5] would signify the first 6 characters(0-5), but what about negative values? They're actually quite easy, and can be very powerful. They count backwards from the end of the string, meaning that -1 is the last character in the string, -2 is the second last, etc. So that filename[-2..-1] just means the two last characters in the string. Easy, eh? :-)
If you look further down the file, you'll also notice that we have created a create() function. Remember that it is called in all objects when they are created. And that suits us just fine, as that would be the perfect time to load in the emails database, wouldn't it? :-) And don't worry about the first '...Could not read the objectfile...' error, because the email database doesn't exist yet.
Now, the save_object() function just saves the global variables into an objectfile, but we're also using the flag. When the flag is set, it will also save any empty variables. The restore_object() also has a flag(which we're not using) which keeps the global variables not getting zero'ed out if they're not in the objectfile.
I think that's enough about datafiles/objectfiles. Let's turn our attention over to something harder. Mappings can be hard to understand at first, but once you get used to them, they're a blast to work with. Mappings are very similar to arrays, except for one important thing, they don't need integers for indexes. Some examples:
mapping names = (); // Initialize the mapping. names["joe"] = "John Doe"; names["gunner"] = "Roger Niva"; names["jack"] = "Jack of all Trades";
If you later on need to find out who 'gunner' really is, a simple 'names["gunner"]' will suffice(returning 'Roger Niva'). You can also use objects as keys or values, as well as most other LPC types. Have a look at the examplefile, and also have a close look at 'map mappings'. It's really worth the extra effort to learn how to use mapping. Just be careful not to overdo it as mappings requires a lot of work for the driver.
Now that we have discussed both player and monster communications,
you may have gotten some ideas about how to use this in your monsters.
Having the monsters "call" for help is one way of doing it. Using this
kind of communication makes us able to create monsters that helps eachother
without the need of an actual "link"(for instance a function) between them.
This feature is possible to use in guildmonsters, for instance. Wouldn't
it be cool if you can get help just by calling for it, if someone attacks
you inside your own guild? ;-)
This would be pretty easy to implement, too. All you need would be a hook in the monster for __catch_tell, and a function that checks the message for the words 'help me'. This could for instance be done with the strsrch() function.
Maybe an idea for your future guild? ;-)
And we have arrived at the end of yet another course. By this time, you should be getting familiar with coding different commands, monsters, functionfiles and all the other fun stuff.
The next course will debate the coding of special items, which are not boring +stat equipment, but items that both helps the players and makes the players' lives miserable without the use of +stats. +Stat EQ may be cool, but after a while, it gets kinda dull.
So, let's get ready, and start coding some really cool items...