Magsbot Class, Session 5
Saturday April 5, 2003 3pm VRT
(this class was actually delayed an hour and started at 4pm VRT)
note: added remarks or paraphrases are in [brackets] and/or italicized, and the order of some chat has been changed for clarity.
also some parts have been colorized for clarity.

Magine: so, any questions from last time?
ZebLith: No questions. :)
Magine: ok
Magine: so does this class seem to be making things more clear at all? :D
ZebLith: In many areas, yes. :)
Magine: have you taken a look at the standard behavior table, btw?
ZebLith: Standard behaviour table meaning the behaviour table I have set up (alt+b) or a "default"?
Magine: the table that comes in the .zip
Magine: did you set one up of your own?
ZebLith: Not really, I played around with a few commands but never saved them.  So it basically is the one from the zip file.
Magine: yes
Magine: i thought today i would just go thru some parts of the table and try to clarify them
Magine: if that's possible, heheh
ZebLith: heh well let's see ^^
Magine: just trying to decide what part to start with....
Magine: any section that interests you in particular?
ZebLith: Hmm...nothing in particular....
Magine: well, let's start at the top.....
Magine: alt-B to open it (saying that for the benefit of log readers)
Magine: the first section handles recording...but that's a bit complicated to start with, so let's move down to the survey/build section
Magine: the first row after the comment is event=any, action=check 1
Magine: all that does is let you "turn off" that section of the table if you aren't going to use it
Magine: if you wanted to deactivate that section, you would change "check 1" to "check 0"
ZebLith: ah okay
Magine: all the check command does is turn row-by-row checking on or off
Magine: check 0 turns it off and check 1 turns it back on, of course
Magine: when checking is turned off, then only rows with the ANY event will be examined
Magine: until the next "check 1" occurs
Magine: this is somewhat similar to the ENDCHECK command, which tells the program to stop checking rows entirely
Magine: well, stop checking rows that match the particular event
Magine: hm, let me try to explain that better...
Magine: when an event occurs, it is sent to the behavior table, as we talked about before,
Magine: but only rows whose event column has that particular event are actually examined
Magine: so for instance during a "HEAR" event,
Magine: only rows with event=hear (or event=any) will be looked at.
Magine: rows with other events will be skipped over entirely.
Magine: make sense? :)
ZebLith: perfect sense. :)
Magine: ok, so the next row of the survey/build section, following ANY/CHECK 1, has Event=STARTUP
Magine: this is an event that's internal to magsbot, unlike most events which represent messages from the world server.
Magine: any rows of the behavior table that have event=startup will be checked when the program begins
Magine: (unless checking is turned off for a row due to a previous row having CHECK 0 or ENDCHECK)
Magine: so, in this case, the STARTUP row is just initializing some global variables.
Magine: initializing means giving them some value to begin with, of course....and we talked about global variables before
ZebLith: right and yes. :)
Magine: so the variables we see here, @clicklog, @selllog, @surveylog and $logger are all global variables that are used by the build/survey section of the behaior table.
Magine: there are several kinds of survey that can be done using the buttons on the action panel,
Magine: and those global variables are used to specify which kind of survey.
Magine: when you click a survey button on the actions panel, some of those variables are given a value of 1, depending on the kind of survey operation you're doing.
Magine: so, on the next row,
Magine: we see event OBJECTCLICK
Magine: followed by an expression @gv_[clicklog]&&@eq[$avname,$gv_[logger]]
Magine: the first part of that, @gv_[clicklog], just gets the value of global variable @clicklog
Magine: and the second part,
Magine: compares $avname (which as you recall is a macro for $atr[avatar_name], which in turn indicates the name of the avatar responsible for the event) ...with the global variable $logger.
ZebLith: and $logger would be the person you select for magsbot to recognize to do the clicking for object logging, right?
Magine: correct
Magine: let me clarify some things here :)
Magine: when you click the "Log Objects by Click" button on the actions panel,
Magine: it creates a pop-up dialog that lets you choose an avatar from the list of nearby avs,
Magine: and assigns that name to $logger
Magine: so you can see here that if someone clicks on an object,
Magine: the OBJECTCLICK event will occur and be sent to the bot, which in turn sends that to the behavior table,
Magine: and when the row we're looking at is checked,
Magine: the action will only be triggered if
Magine: the @clicklog global variable is "true" (1)  ...which it will be if you clicked the "log objects by click" button earlier
Magine: and the name of the avatar who clicked on the object equals that name that was assigned in the "log objects by click" button
Magine: so knowing all that, does the expression there make sense to you? :)
ZebLith: perfectly. ^^
Magine: ok :)  ...two other things i'll mention about the expression,
Magine: first, the @eq function there
Magine: is used because as you will recall, in magsbot's language you can't compare strings directly
Magine: so you could not say $logger=$avname, for example
Magine: instead you say @eq[$logger,$avname]
Magine: ...and the second thing about the expression that i should mention is this:
Magine: you may wonder why i use the @gv_ function instead of just putting
Magine: OBJECTCLICK @clicklog && @eq[$logger,$avname]
Magine: do you want to take a guess? :)
ZebLith: My guess would be that you want it to look at the global variables first and that there may be a local variable of the same name you want it to ignore.
Magine: well, that's a good guess, but no :D
ZebLith: lol so it is there to...? ^^
Magine: you don't need to do that because at this point, in the event, there aren't going to be any local variables yet
Magine: (unless you created them in the expression itself)
Magine: so you know $logger and @clicklog must refer to globals
Magine: but the reason you use @gv_ and $gv_ instead of referring to them directly,
Magine: is because you aren't sure if those variables exist at all!
Magine: if someone hasn't used the "log objects by click" button, then there may not be any variables named $logger or @clicklog
Magine: (well actually, that's sort of untrue....you know that there are because they were created in the STARTUP event above)
Magine: (but if it wasn't for that STARTUP event, you wouldn't know if those variables existed or not)
Magine: (actually i should get rid of the STARTUP there, it was put there in an earlier version, but it's not really needed now because the @gv_ and $gv_ are being used)
Magine: anyway, the point is...
ZebLith: well in my behaviour table, it's just @clicklog, not @gv_[clicklog]
Magine: ah ok :)
Magine: you must have an older table
Magine: that's why i put the STARTUP event there originally,
Magine: to make sure that @clicklog and $logger exist
Magine: because if you refer directly to a variable that doesn't exist, you get an error message
Magine: but in more recent versions of magsbot,
Magine: i try to use the @gv_ and $gv_ functions to refer to globals in the behavior table
Magine: because the @gv_ and $gv_ will NOT cause an error if the variable doesn't exist
Magine: instead the @gv_ will return 0 and the $gv_ will return an empty string "" if the variables don't exist.
Magine: of course i could change magsbot so it doesn't give an error when you directly refer to nonexistant variables,
Magine: but sometimes it's a good thing to give an error, so the user will know that something is wrong.
Magine: so anyway...the point is, that you would use @gv_[x] instead of just @x if you aren't sure @x exists and don't want to get an error message
Magine: or you would use $gv_[x] instead of $x, the same way, the avoid an error message when unsure if $x exists.
Magine: so does that make sense? :)
ZebLith: yes :)  (And as to my previous thought, doesn't it refer to globals first anyways? >_<)
Magine: nope, just the opposite
Magine: when magsbot sees a variable like @x
Magine: it FIRST looks to see if there is a local variable with that name
Magine: and if it doesn't exist, then it looks for a global of that name.
ZebLith: okay, I don't know why I thought that, lol okay, gotcha.
Magine: ok...so now for the Action on that same row of the table...
Magine: first there is just a comment, a REMark statement:
Magine: REM log clicked object;
Magine: then there is the command CLICKBTN "Survey/[LogObject]",
Magine: which causes the button "[LogObject]" on the Survey tab of the actions panel to be activated
             [which causes the information for the object to be put into the list named ObjLog...more on that in a moment]
Magine: and finally the command WHISPER $logger $gv_[$fmt["objlog:%s",@objnum]]
Magine: which causes a formatted message to be whispered to the person who clicked on the object.
Magine: (within the action we don't need to use $gv_[logger] because the action will never occur unless $logger exists.)
Magine: but what is that other stuff there? :D the $gv_[$fmt["objlog:%s",@objnum]]  looks rather confusing, huh? :D
ZebLith: oh yes very! lol
Magine: well first let's just look at the inner part, $fmt["objlog:%s",@objnum]
Magine: the $fmt function is a string formatting function
Magine: similar to printf in C, by the way :)
ZebLith: ah :)
Magine: the first parameter ("argument") that we give the $fmt function is a format string
Magine: and the following arguments, of which there can be up to 11, [twelve arguments max for a magsbot function]
Magine: specifies the values that we want to insert into the format string.
Magine: in other words,
Magine: whereever there is a %s in the format string,
Magine: the $fmt function will insert one of the values from the rest of the arguments
Magine: so $fmt["objlog:%s",@objnum] will take the value of @objnum and insert it into "objlog:%s" where the %s is.
Magine: so if @objnum was 12345 then $fmt["objlog:%s",@objnum] would return "objlog:12345"
Magine: make sense? :)
ZebLith: yeah :)
Magine: i'll comment here for the benefit of log readers who might know some C,
Magine: that unlike the C sprint function that has other specifiers besides %s
Magine: in magsbot you always use %s because all magsbot variables are "variant" type
Magine: (if you don't know what all that means, don't worry, you don't need to, heheh)
Magine: (i only mentioned this so any C users won't [get confused trying to use] %d or other specifiers besides %s like you do in C)
ZebLith: okay, a question on $fmt for a second?
Magine: sure, what?
ZebLith: lets say for example...$fmt["objlog:%s %s",@objnum,@objnumX] would it print @objnum first THEN @objnumX? or @objnumX then @objnum?
Magine: if @objnum was 123 and @objnumX was 456,
Magine: then $fmt["objlog:%s %s",@objnum,@objnumX]  would give you
Magine: "objlog:123 456"
Magine: @objnum comes first in your list, so it gets inserted first.
ZebLith: okay! great :)
Magine: ok....so now we know what that $fmt function in the OBJECTCLICK row means, sort of :D
ZebLith: ^^
Magine: the $fmt["objlog:%s",@objnum] will create a string like "objlog:12345" using the value of @objnum
Magine: but what is @objnum? :D
Magine: well, it's another macro like $avname
Magine: it's a convenient abbreviation for @atr[object_number]
Magine: which in this case refers to the object number of the object that was clicked on
Magine: so what is happening here, is this....
Magine: when someone clicks an object, there is an objectclick event,
Magine: and if the person doing the clicking is the $logger
Magine: (if their name = the value stored in global variable $logger)
Magine: and if the @clicklog global is 1 (meaning that logging objects by click has been turned on)
Magine: then the [LogObject] button in the survey category of the action panel will be clicked
Magine: (which, though I didn't say so before, will create an item in a list named ObjLog, with a name like $objlog:12345)
Magine: and then the bot will whisper the value of that list item to the person who clicked the object.
Magine: of course to really understand this you also need to know what the [LogObject] button is doing
Magine: and next time we'll look at some button code,
Magine: but for now all you need to know is what the [LogObject] button does
Magine: which is, as i said, to create an item in the string list named ObjLog.
Magine: the item will have a name like $objlog:12345, where 12345 is the object number
Magine: and the value stored in  $objlog:12345 will be a string containing information about the object clicked.
Magine: (that's what [LogObject] does...it creates that string with info about the object, and puts the string into the ObjLog list)
Magine: i hope that makes some kind of sense :D
ZebLith: heheh :)
Magine: actually the process was much more complicated before the [more recent versions of the sdk]
Magine: because in earlier versions you only got the object number when you clicked an object,
Magine: you didn't get any other information about the object when clicking it,
Magine: so the "log objects by click" routine had to also do a survey afterward in order to get the object information.
ZebLith: aah
Magine: but now that you get all the object info when you click an object, it's much simpler.
Magine: you just take the info when the person clicks the object, and put it directly into ObjLog
Magine: so is it becoming clear?
ZebLith: *nods*
Magine: no questions? :)
ZebLith: None right now :)
Magine: ok
Magine: well, the next row of the behavior table, which has Event=OBJECTSELECT @gv_[sellog]&&@eq[$avname,$gv_[logger]]
Magine: does much the same thing as the row above,
Magine: the only difference is,
Magine: instead of logging objects that are clicked on,
Magine: it logs objects that are *selected*
Magine: the @sellog global is like the @clicklog global....@sellog gets assigned a value of 1 when someone clicks the "Log Objects by Select" button
Magine: otherwise it's all the same.
Magine: ok, so....the row after that one, has Event=CELLOBJECT @gv_[surveylog]
Magine: now that row also does something similar...it also puts object information into the ObjLog list
Magine: but instead of doing that when someone clicks on an object or selects an object,
Magine: it does it when an CELLOBJECT event occurs....which means, during a survey.
Magine: that's how a survey works, in fact: the bot gets a lot of CELLOBJECT messages,
Magine: each containing information about one object in the survey.
Magine: so...when a user clicks the "Log from Survey" button on the actions panel,
Magine: it assigns 1 to @surveylog, and starts a survey going.
Magine: then during the survey,
Magine: the behavior table receives numerous CELLOBJECT messages
Magine: and since @surveylog=1, that row of the behavior table responds to each CELLOBJECT message,
Magine: by putting the object info into the ObjLog list.
Magine: the same as the rows above put object info into the ObjLog list when a user clicks an object or selects an object.
Magine: does that make sense? :)
ZebLith: yes :D
Magine: the one additional complication here, you will notice if you look in the action column for this row,
Magine: is that a different function is used to store the information in the list,
Magine: depending on the value of the global @relsurv.
Magine: you can see by looking at the code in the action column,
Magine: it says
Magine: IF @gv_[relsurv] { CLICKBTN "Survey/[LogObj]" }ELSE { CLICKBTN "Survey/[LogObject]" }
Magine: so, if @relsurv is 1, then the [LogObj] button is used,
Magine: or if @relsurv is 0, then the [LogObject] button is used ObjLog instead.
             [both [LogObj] and [LogObject] put object information into the ObjLog list,]
Magine: the only difference between the [LogObj] and [LogObject] buttons is,
Magine: that [LogObject] puts the information into ObjLog just as it is,
Magine: while [LogObj] adjusts the object location information to make it relative to the bot's location
ZebLith: oooh I see
Magine: so if @relsurv is 1, you get a "relative survey" and if @relsurv is 0, you get a regular survey.
Magine: so all that making sense?
ZebLith: yes ^^
Magine: you getting a general idea of how things are done in the behavior table?
ZebLith: much clearer now :)
Magine: well, we could keep going line by line in the table, but that would take forever....so maybe we should at least skip to a different part of the table and see how some different things besides surveys are done.
Magine: the next section is "remote control"....
Magine: this is where the bot responds to spoken commands.
Magine: found it?
ZebLith: yep :)
Magine: ok....as you can see there are a lot of rows there with Events like HEAR @cmd[come], HEAR @cmd[follow] and so forth.
Magine: the @cmd there is a macro that looks in the chat string and returns "true" (1) if the specified word is the first word of the chat
Magine: and if the chat has been directed at the bot, by using the bot's name or by whispering.
Magine: if you were to look in userdefs.udf,
Magine: you would see @cmd defined as
Magine: @cmd[$]= (@tell&&@eq[$w1,~1])||@cmd_[~1]
Magine: which, as complicated as it looks :) only means
Magine: if the bot is being spoken to and the first word equals the specified word, or if the first word begins with a / , then return 1
Magine: you can break down the (@tell&&@eq[$w1,~1])||@cmd_[~1]
Magine: into (@tell && @eq[$w1,~1])   ||  @cmd_[~1]           [colors and fonts have been added here to identify the different parts]
             [also note that the parentheses determine the order that the parts of the expression are calculated]
Magine: which probably still seems pretty complicated i guess :D
ZebLith: a little... ^^
Magine: ok, one part at a time....
Magine: the @tell part is just another macro, which returns true (1) if the chat is whispered or if the first word of the chat is the bot's name
Magine: the @eq[$w1,~1] part just means
Magine: does the first word of the chat string equal the word specified in @cmd?
Magine: the ~1 refers to whatever parameter is first in the macro
Magine: so if you put @cmd[foobar]
Magine: then ~1 would be foobar
Magine: if you see what i mean :)
ZebLith: got that bit :)
Magine: the @cmd means "if the bot is being whispered to, or the first word of the chat is the bot's name, AND the first word of the chat not counting the bot's name is the specified word"
Magine: then added to all that is @cmd_[~1]
Magine: the @cmd_ macro returns true if the first word of the chat string is the specified word prefaced by a slash /
Magine: so, if you wanted to tell the bot to come, for instance
Magine: you could say
Magine: mags come
[Mags]: Be right there.
Magine: or you could whisper just "come"
Magine: (to [Mags]) come
[Mags]: Be right there.
Magine: or you could say
Magine: /come
[Mags]: Be right there.
ZebLith: aaah :D
Magine: and all those different possibilites are contained in the single @cmd macro
Magine: so instead of having to put
Magine: ((@botcmd||@whispered)&&@eq[$w1,foobar])||@eq[$w1,$cat["/",foobar]]
Magine: you can just put
Magine: @cmd[foobar]
Magine: and it means the same thing :)
ZebLith: and so much simpler! ;)
Magine: and actually $w1 is also a macro, so it would be even more complicated than that :D
ZebLith: O_O lol
Magine: $w1 is a macro that returns the first word of the chat string, not counting the bot's name if that is the first word
Magine: so the whole thing would be the same as
Magine: ((@botcmd||@whispered)&&@eq[$word[@if[@eq[$botname,$word[1]],2,1]],foobar])||@eq[$word[@if[@eq[$botname,$word[1]],2,1]],$cat["/",foobar]]
Magine: lol :D
ZebLith: WAAAAAAAAY too long! X_x  lol :D
Magine: so you can see that macros are indispensible
Magine: all that crap is contained in the simple @cmd macro
Magine: oh, no, it's still not fully expanded :D  [because @botcmd itself is a macro]
Magine: ((@eq[$botname,$word[1]]||@whispered)&&@eq[$word[@if[@eq[$botname,$word[1]],2,1]],foobar])||@eq[$word[@if[@eq[$botname,$word[1]],2,1]],$cat["/",foobar]]

And even that is still not the the whole thing, because to simplify just a bit, I also removed the part that checks to see if the first word of the chat string is an alias for the bot name, not just the bot name itself.  So @botcmd is actually not just @eq[$botname,$word[1]], but is @eq[$botname,$word[1]]||@ind[$word[1],$v_[$cat["alias:",$botname]],"|"].
Magine: there...anyway you get the point :D   [which is, you can use macros within other macros to simplify things]
ZebLith: lol :D
Magine: so all that stuff is that same as saying
Magine: "if the chat is whispered or the first word of the chat is the bot's name, and the first word of the chat other than the bot's name is the specified word, OR if the first word of the chat other than the bot's name is the specified word preceeded by a slash"
Magine: the whole point being that an impossibly complicated expression can be broken down into smaller parts and finally into a single simple macro
Magine: so you don't (thankfully) need to understand or deal with a monster expression :D
Magine: well we're out of time again....are you overwhelmed yet? :D
       [long pause]
Magine: uh oh, i knocked him unconscious :D
ZebLith: nope, enjoying these classes very much!  and today I finally figured out why I kept having so many problems with the behaviour table in the past. :D
Magine: why is that?
ZebLith: The little event=any action=check 0 thing.  I put all of my new things at the bottom, after an event=0.  now [that I changed it] they work fine :)
Magine: ah :)
Magine: yes, it's usually best to insert your own stuff at the top [of the behavior table]
Magine: that way none of the standard stuff will interfere with it (as you discovered)
Magine: and also your own stuff will be checked first, so it will be a bit faster.
ZebLith: heh yeah... ^^  thank you for another great class, can't wait to play around with this. :)
Magine: ok, have fun and hope to see you next time :)
ZebLith: bye Magine :D
Magine: bye :)

You can read more about expressions here, and more about macros here.