david.the-haleys.org
SMAUG Movement Messages

Movement messages let players choose a different message than the default "walks east", much like mobs. Players choose a movement message from a pre-defined list, such as "limp", "scamper", etc.

NOTE: This is not a step-by-step snippet. I assume that you know enough about C++ and SMAUG to put these functions where they belong. Given that this is a C++ snippet, and that the vast majority of SMAUG MUDs are in C, my assumption is that if you're trying to use this snippet you already have a fair amount of programming knowledge.

The functions below define the bulk of the infrastructure you will need. You will, naturally, need to add code to actually print the movement messages instead of the defaults; you will also need to implement the method for actually setting/saving/storing the movement message (CHAR_DATA::setMovementMessage).

Thanks go to Darkstone player Pracllik for the original version of this snippet. My major modifications:

  • changed to C++
  • allowed runtime modification of movement messages

Now for the snippet...

act_moves.cpp


// Global table for mapping movement message name to (enter, exit) message pair
typedef map<string, pair<string, string> > MovementMessageMap;
MovementMessageMap MovementMessages;

    

/** \brief Get a movement entry/exit message pair.
 *
 * \param message The movement message to look up.
 * \return A std::pair whose first element is the entry message and whose
 *         second element is the exit message.
 */
const pair<string, string> & GetMovementMessage(const std::string & message)
{
    MovementMessageMap::iterator it = MovementMessages.find(message);

    if ( it == MovementMessages.end() )
    {
        TheWorld->LogBugString("Unknown movement message: " +
message);

        if ( message == "default" )
        {
            TheWorld->LogBugString("No default movement message?!?");
            return MovementMessages["????"];
        }
        
        return GetMovementMessage("default");
    }

    return it->second;
}   
        

/** \brief Convenience wrapper around GetMovementMessage.
 *
 * Directly accesses the entry message.
 *          
 * \param movementMessage The movement message to look up.
 */     
const string & GetEntryMessage(const string
& movementMessage)
{       
    return GetMovementMessage(movementMessage).first;
}       
        
/** \brief Convenience wrapper around GetMovementMessage.
 *      
 * Directly accesses the exit message.
 *          
 * \param movementMessage The movement message to look up.
 */

const string & GetExitMessage(const string
& movementMessage)
{
    return GetMovementMessage(movementMessage).second;
}

/**
 * Load the movement messages from a file. The file is formatted
 * with one entry/exit pair per line, like so:
 * \code
 * name~entry message~exit message
 * name2~entry message2~exit message2
 * etc.
 * \endcode
 *
 * \param filename The file to load the messages from.
 */
void LoadMovementMessages(const string &
filename)
{
    ifstream in( filename.c_str() );

    if ( in.fail() )
    {
        TheWorld->LogBugString("Couldn't open movement messages
file!");
        return;
    }

    while ( true )
    {
        string line;
        getline(in, line);

        if ( in.fail() == true )
            break;

        // Everything until the first twiddle is the movement message name;

        // everything afterwards is the movement message
        // The movement message has another twiddle, which separates
entry/exit messages

        string::size_type pos = line.find("~");
        string::size_type pos2 = line.find("~", pos+1);

        if ( pos == string::npos || pos2 == string::npos )
        {
            TheWorld->LogBugString("Error parsing movement message line:
" + line);
            continue;
        }
    
        string msgName = line.substr(0, pos); // everything up until twiddle

        
        string enter = line.substr(pos+1, pos2-(pos+1) );
        string exit = line.substr(pos2+1);
    
        MovementMessages.insert( make_pair(msgName, make_pair(enter, exit) ) );
    }   
        
    MovementMessageMap::iterator it;
        
    string result;

    for ( it = MovementMessages.begin(); it != MovementMessages.end();
it++ )
    {
        result += it->first + " ";
    }
        
    TheWorld->LogString("Loaded character movement types: " + result);
}           
            
/** \brief Save the movement messages to file.
 *
 * \param filename The file to save to.
 */
void SaveMovementMessages(const string &
filename)
{       
    ofstream out( filename.c_str() );
            
    MovementMessageMap::iterator it;

    for ( it = MovementMessages.begin(); it != MovementMessages.end();
it++ )
        out << it->first << "~" << it->second.first
<< "~" << it->second.second << endl;

    TheWorld->LogString("Movement messages saved to " + filename);
}       
    

void do_editmovementmessage( cCharacter * ch, char * argument )
{   
    char arg1[MAX_INPUT_LENGTH];
    char arg2[MAX_INPUT_LENGTH];
            
    argument = one_argument( argument, arg1 );
    argument = one_argument( argument, arg2 );

    string cmd = strlower(arg1);
    string msgName = strlower(arg2);

    // Make sure basic syntax is correct, or print usage message.

    if ( cmd == "" || (cmd != "list" &&
msgName == "") )
    {
        ch->sendText("EditMovementMessage syntax:\n\r");
        ch->sendText("\teditmovementmessage command
movement-message-name\n\r" );
        ch->sendText("\t'command' is one of: set, delete, list");

        return;
    }

    if ( cmd == "set" )
    {
        string msg = argument;

        if ( msg == "" )
        {
            ch->sendText("Can't set a message to nothing; use delete
instead.\n\r");
            return;
        }

        string::size_type pos = msg.find("~");

        if ( pos == string::npos )
        {
            ch->sendText("Invalid message: must be of form: enter
message~exit message");
            return;
        }

        string enterMsg = msg.substr(0, pos);
        string exitMsg = msg.substr(pos+1);

        MovementMessages[msgName] = make_pair(enterMsg, exitMsg);
        ch->sendText("Set movement message \"" + msgName + "\" to \"" + msg +
"\"\n\r");
    }
    else if ( cmd == "delete" )
    {
        if ( msgName == "default" )
        {
            ch->sendText("Can't delete default movement
message!\n\r");
        }

        MovementMessages.erase(msgName);
        ch->sendText("Deleted movement message \"" + msgName +
"\"\n\r" );
    }
    else if ( cmd == "list" )
    {
        MovementMessageMap::iterator it;

        ch->sendText("Movement messages: \n\r");

        for ( it = MovementMessages.begin(); it != MovementMessages.end();
it++ )
            ch->sendText(it->first + ": " + it->second.first + "; " +
it->second.second + "\n\r");

        return;
    }
    else

    {
        ch->sendText("Unrecognized edit-movement-message command: '" + cmd +
"'.\n\r");

        // display usage message
        do_editmovementmessage(ch, "");
        return;
    }

    SaveMovementMessages(MOVEMENT_MESSAGE_FILE);
}

void do_movementmessage( cCharacter * ch, char *
argument )
{
    char arg1[MAX_INPUT_LENGTH];
    one_argument( argument, arg1 );

    if( arg1[0] == '\0' )
    {
        send_to_char( "MovementMessage Command\n\r", ch );
        send_to_char( "Syntax: movementmessage <movement message
type>\n\r", ch );
        send_to_char( "Where type is one of the following:\n\r", ch );

        MovementMessageMap::iterator it;

        for ( it = MovementMessages.begin(); it != MovementMessages.end();
it++ )
            ch->sendText( "\t" + it->first + " - " + it->second.first +
"; " + it->second.second + "\n\r" );

        return;
    }

    MovementMessageMap::iterator msgIt = MovementMessages.find(arg1);

    if ( msgIt == MovementMessages.end() )
    {
        ch->sendText( "That is not a valid movement message.\n\r" );
        ch->sendText( "Valid messages:\n\r" );

        MovementMessageMap::iterator it;

        for ( it = MovementMessages.begin(); it != MovementMessages.end();
it++ )
            ch->sendText( "\t" + it->first + " - " + it->second.first +
"; " + it->second.second + "\n\r" );

        return;
    }

    ch->setMovementMessage(msgIt->first);

    ch->sendText("Ok.\n\r");

}