/**************************************************************************/ // mxp.cpp - mxp code /*************************************************************************** * The Dawn of Time v1.69r (c)1997-2004 Michael Garratt * * >> A number of people have contributed to the Dawn codebase, with the * * majority of code written by Michael Garratt - www.dawnoftime.org * * >> To use this source code, you must fully comply with the dawn license * * in licenses.txt... In particular, you may not remove this copyright * * notice. * **************************************************************************/ #include "include.h" #include "mxp.h" /**************************************************************************/ // converts '<' into MXP_BEGIN_TAG and '>' into MXP_END_TAG char *mxp_tagify(const char *mxp_text_with_unencoded_tags) { static char result[5][MSL*2]; static int i; ++i%=5; char *r=result[i]; for(const char *p=mxp_text_with_unencoded_tags; !IS_NULLSTR(p); p++){ if(*p=='&'){ *r++=MXP_AMPERSAND; }else if(*p=='<'){ *r++=MXP_BEGIN_TAG; }else if(*p=='>'){ *r++=MXP_END_TAG; }else{ *r++=*p; } } *r='\0'; return result[i]; } /**************************************************************************/ // converts '<' into <, '>' into > and & into & char *mxp_convert_to_mnemonics(const char *text_with_raw_characters) { static char result[5][MSL*2]; static int i; ++i%=5; char *r=result[i]; for(const char *p=text_with_raw_characters; !IS_NULLSTR(p); p++){ if(*p=='&'){ *r++='&'; *r++='a'; *r++='m'; *r++='p'; *r++=';'; }else if(*p=='<'){ *r++='&'; *r++='l'; *r++='t'; *r++=';'; }else if(*p=='>'){ *r++='&'; *r++='g'; *r++='t'; *r++=';'; }else{ *r++=*p; } } *r='\0'; return result[i]; } /**************************************************************************/ // put the secure prefix before every line char *mxp_prefix_secure(const char *text_to_be_prefixed) { static char result[5][MSL*2]; static int i; ++i%=5; char *r=result[i]; for(const char *p=text_to_be_prefixed; !IS_NULLSTR(p); p++){ if(*p=='\n' || *p=='}' && *(p-1)=='`'){ *r++=*p; { char *prefix=MXP_SECURE_PREFIX; while(!IS_NULLSTR(prefix)){ *r++=*prefix++; } } }else{ *r++=*p; } } *r='\0'; return result[i]; } /**************************************************************************/ void do_mxp( char_data *ch, char *argument ) { pc_data *pcdata=TRUE_CH(ch)->pcdata; // the characters pcdata we want to work on if(!pcdata){ ch->println("Players can only use this command"); return; } if(IS_NULLSTR(argument)){ ch->titlebar("MUD EXTENSION PROTOCOL OPTIONS"); ch->println("Syntax: `=Cmxp off`x - mxp is permanately off."); ch->wrapln("Syntax: `=Cmxp auto`x - mud will attempt to automatically " "detect if you have mud client that supports mxp, if one is detected " "the mud will send you mxp tags"); ch->wrapln("Syntax: `=Cmxp on`x - mxp is permanately on, mud will send you mxp " "tags even if your mud client doesn't support mxp."); ch->println(""); ch->printlnf("Your mxp preference is currently set to %s", preference_word(pcdata->preference_mxp)); if(pcdata->preference_mxp==PREF_AUTOSENSE){ ch->printlnf("`WYour connections mxp support has %sbeen automatically detected.`x", (ch->desc && IS_SET(ch->desc->flags, CONNECTFLAG_MXP_DETECTED))?"":"not "); } ch->titlebar(""); return; } PREFERENCE_TYPE pt; if(!str_prefix(argument, "off")){ pt=PREF_OFF; }else if(!str_prefix(argument, "autosense")){ pt=PREF_AUTOSENSE; }else if(!str_prefix(argument, "on")){ pt=PREF_ON; }else{ ch->printlnf("Unsupported mxp option '%s'", argument); do_mxp(ch,""); return; } if(pcdata->preference_mxp==pt){ ch->printlnf("Your mxp preference is already set to %s", preference_word(pt)); return; } ch->printlnf("mxp preference changed from %s to %s", preference_word(pcdata->preference_mxp), preference_word(pt)); pcdata->preference_mxp=pt; ch->mxp_send_init(); // update the MXP cached info } /*************************************************************************/ void do_mxpreflect( char_data *ch, char *argument ) { if(IS_NULLSTR(argument)){ ch->titlebar("MXP REFLECT"); ch->println("Syntax: `=Cmxpreflect <text you want reflected/echoed back to you as mxp>`x"); ch->println(""); ch->wrapln("Explaination: Normally the < and > symbols are converted into < and > " "by the mud - this command can be used to test MXP codes, each line is " "prefixed by ESC[1z (the secure line tag). Use ``1 to start a new line " "for multilined echos."); ch->titlebar(""); return; } ch->printlnf("MXPReflect of '%s' follows:", argument); ch->printlnf("%s%s", MXP_SECURE_PREFIX, mxp_prefix_secure(mxp_tagify(argument))); } /**************************************************************************/ // creates the mxp tag for mxp_create_tag(), const char *mxp_create_tag_core(const char *tagname, const char *txt) { // get our return buffer static int i; static char result[5][MSL*2]; ++i%=5; // setup our pointers char *r=result[i]; // r is a pointer to what will be returned const char *p=txt; // p is a pointer to the source text const char *t=NULL; // t is a misc pointer used for various tasks // copy any leading whitespace from txt into the tag while(is_space(*p)){ *r++=*p++; } // if there is no more source text left then we have just formatted // an empty string or a string of whitespace - regardless return it. if(*p=='\0'){ *r='\0'; // terminate the result first return result[i]; } // we now have any leading whitespace outside of where the tag // starts, now start the tag *r++=MXP_BEGIN_TAG; t=tagname; while(*t){ *r++=*t++; } *r++=MXP_END_TAG; // r now points at where the non whitespace text will begin // p points at the first non whitespace, non null character in the input text // next find the last non whitespace character in the input text for(t=p; *t; t++){ // fast forward to the end of the input text } t--; // jump back to the last character in the string // now backup till we hit the last non whitespace character in the string while(is_space(*t)){ t--; } // copy from p into r until p is a character past t (the last non whitespace character) while(p<=t){ *r++=*p++; } // put the closing tag on the result *r++=MXP_BEGIN_TAG; *r++='/'; t=tagname; // close the tag up to the end of the string or first whitespace while(*t && !is_space(*t)){ *r++=*t++; } *r++=MXP_END_TAG; // copy the remaining whitespace from p to r while(*p){ *r++=*p++; } // terminate the result *r='\0'; // return the result :) return result[i]; } /**************************************************************************/ // create an mxp tag for a character - Kal June 01 const char *mxp_create_tag(char_data *ch, const char *tagname, const char *txt) { // if they dont have MXP or the tag is empty, just return the txt if(!HAS_MXP(ch) || IS_NULLSTR(tagname)){ return txt; } return mxp_create_tag_core(tagname, txt); } /**************************************************************************/ // creates an mxp tag for those with MXP on... and strips whitespace around // the tag to outside the tag. If they have MXP off it just returns the // formatted result. const char *mxp_create_tagf(char_data *ch, const char *tagname, const char *fmt, ...) { static int i; static char result[5][MSL*2]; ++i%=5; char *r=result[i]; va_list args; va_start(args, fmt); vsnprintf(r, MSL*2, fmt, args); va_end(args); if(HAS_MXP(ch)){ return mxp_create_tag(ch, tagname, r); } return r; } /**************************************************************************/ const char *mxp_create_send(char_data *ch, const char *command, const char *text) { return mxp_create_tag(ch, FORMATF("send href=\"%s\"", command), text); } /**************************************************************************/ const char *mxp_create_send(char_data *ch, const char *command_and_text) { return mxp_create_send(ch, command_and_text, command_and_text); } /**************************************************************************/ const char *mxp_create_send_prompt(char_data *ch, const char *command, const char *text) { return mxp_create_tag(ch, FORMATF("send href=\"%s\" prompt", command), text); } /**************************************************************************/ const char *mxp_create_send_prompt(char_data *ch, const char *command_and_text) { return mxp_create_send_prompt(ch, command_and_text, command_and_text); } /**************************************************************************/ #define MXPELEMENT_IMMONLY (A) /**************************************************************************/ struct mxp_element_type { char *name; int flags; char *attributes; char *variable; char *text; }; /**************************************************************************/ mxp_element_type mxp_elements[]= { // name, tableflags, attribute=, client_variable { "help", 0, "", "", "<send href=\"help &text;\">" }, { "rname", 0, "", "RoomName", "" }, { "rdesc", 0, "", "RoomDesc", "" }, { "rexits", 0, "", "RoomExit", "" }, { "ex", 0, "", "", "<send>" }, { "buy", 0, "", "", "<send href=\"buy &text;\">" }, { "buy-uid", 0, "uid=0", "", "<send href=\"buy #&uid;\" " " hint=\"Buy &text;\">" }, // immortal only entries //============ CHARACTER - ch_ prefix // character(uid) name { "ch-uid_name", MXPELEMENT_IMMONLY, "uid=0", "", "<send href=\"at #&uid; look|dlook &text;|score &text;|charinfo &text;\" " "hint=\"Look in the room where &text; is|description look &text;|score &text;|charinfo &text;\">" }, { "oolcvnum", 0, "", "", "<send href=\"oshow &text;|oedit &text;\" " " hint=\"oshow &text;|oedit &text;\">" }, { "molcvnum", 0, "", "", "<send href=\"mshow &text;|medit &text;\" " " hint=\"mshow &text;|medit &text;\">" }, { "mbvnum", MXPELEMENT_IMMONLY, "uid=0", "", "<send href=\"at #&uid; look|at #&uid; look #&uid;|goto #&uid;|mshow &text;|medit &text;\" " " hint=\"Look in the room of this mob|Look at the mob itself|Goto the room the mob is in|mshow &text;|medit &text;\">" }, { "mprogvnum", 0, "uid=0", "", "<send href=\"mpshow &text;|mpedit &text;\">" }, { "rmvnum", MXPELEMENT_IMMONLY, "uid=0", "", "<send href=\"goto &text;|at &text; look\">" }, { "rmv", 0, "roomvnum=0 tl=\"\"", "", "<send href=\"goto &roomvnum;|at &roomvnum; look\">" }, { "rmvh", 0, "roomvnum=0 tl=\"\"", "", "<send href=\"goto room &roomvnum; - &tl;-|goto &roomvnum;|at &roomvnum; look\">" }, //============ MOB - mb_ prefix // basic mob - (basic mob) { "mb-uid-nm", 0, "uid=0 nm=\"\"", "", "<send href=\"look #&uid;|con #&uid;\" " " hint=\"Look at &nm;|Consider &nm;\">" }, //============ OBJECT - ob_ prefix // object used as equipment { "objeq", 0, "uid=0 nm=\"\"", "", "<send href=\"remove #&uid;|look #&uid;|examine #&uid;\" " " hint=\"Remove &text;|Look at &text;|Examine &text;\">" }, // basic object { "ob-uid-nm", 0, "uid=0 nm=\"\"", "", "<send href=\"look #&uid;|examine #&uid;\" " " hint=\"Look at &text;|Examine &text;\">" }, // basic object - getable { "ob-uid-nm_g", 0, "uid=0 nm=\"\"", "", "<send href=\"get #&uid;|look #&uid;|examine #&uid;\" " " hint=\"get &nm;|Look at &nm;|Examine &nm;\">" }, // food { "ob-uid-nm_food", 0, "uid=0 nm=\"\"", "", "<send href=\"eat #&uid;|look #&uid;|examine #&uid;\" " " hint=\"Eat &nm;|Look at &nm;|Examine &nm;\">" }, // food { "ob-uid-nm_food_g", 0, "uid=0 nm=\"\"", "", "<send href=\"get #&uid;|eat #&uid;|look #&uid;|examine #&uid;\" " " hint=\"Get &nm;|Eat &nm;|Look at &nm;|Examine &nm;\">" }, // drink { "ob-uid-nm_drink", 0, "uid=0 nm=\"\"", "", "<send href=\"drink #&uid;|look #&uid;|examine #&uid;|fill #&uid;\" " " hint=\"Drink &nm;|Look at &nm;|Examine &nm;|Fill &nm;\">" }, // drink { "ob-uid-nm_drink_g", 0, "uid=0 nm=\"\"", "", "<send href=\"get #&uid;|drink #&uid;|look #&uid;|examine #&uid;|fill #&uid;\" " " hint=\"Get &nm;|Drink &nm;|Look at &nm;|Examine &nm;|Fill &nm;\">" }, // fountain { "ob-uid-nm_fount", 0, "uid=0 nm=\"\"", "", "<send href=\"drink #&uid;|look #&uid;|examine #&uid;\" " " hint=\"Drink from &nm;|Look at &nm;|Examine &nm;\">" }, //============ TELLS - tl_ prefix // tell reply { "tl_rp", 0, "", "", "<send href=\"reply \" PROMPT>" }, // tell(name) name 'reply', 'tell name' { "tl-nm_rp_tlnm", 0, "nm", "", "<send href=\"reply |tell &nm; \" PROMPT>" }, // tell(name) name 'reply', 'tell name' { "tl-nm_rp_tlnm", 0, "nm", "", "<send href=\"reply |tell &nm; \" PROMPT>" }, // tell(name) name 'retell', 'tell name' { "tl-nm_rt_tlnm", 0, "nm", "", "<send href=\">|tell &nm;\" " "hint=\"Retell &nm;|tell &nm;\" PROMPT>" }, {""} // end of the table }; /**************************************************************************/ void mxp_define_objinv_elements_to_char(char_data *ch) { char attribs[MIL],send[MIL], hint[MIL]; for(int i=0; i<6; i++){ sprintf(attribs, "<!ELEMENT objinv%d ATT='uid=0 nm", i); strcpy(send, "'<send href=\""); strcpy(hint, " hint=\""); for(int j=0; j<i; j++){ strcat(attribs, FORMATF(" a%d", j)); strcat(send, FORMATF("&a%d; #&uid;|", j)); strcat(hint, FORMATF("&a%d; &nm;|", j)); } strcat(attribs,"'"); strcat(send, "look #&uid;|examine #&uid;|drop #&uid;\""); strcat(hint, "Look at &text;|Examine &text;|Drop &text;\">'"); ch->print( mxp_tagify( FORMATF( "%s %s %s >", attribs, send, hint))); } } /**************************************************************************/ void mxp_define_elements_to_char(char_data *ch) { ch->print( MXP_SECURE_MODE); // send mxp elements for(mxp_element_type *e=mxp_elements; !IS_NULLSTR(e->name); e++){ if(IS_SET(e->flags, MXPELEMENT_IMMONLY) && !IS_IMMORTAL(ch)){ // define the element to be empty ch->print( mxp_tagify( FORMATF( "<!ELEMENT %s>", e->name))); continue; } ch->print( mxp_tagify( FORMATF( "<!ELEMENT %s%s%s %s>", e->name, IS_NULLSTR(e->attributes)?"": FORMATF(" ATT='%s'", e->attributes), IS_NULLSTR(e->variable)?"": FORMATF(" FLAG='%s'", e->variable), IS_NULLSTR(e->text)?"": FORMATF("'%s'", e->text) ))); } mxp_define_objinv_elements_to_char(ch); ch->print( mxp_tagify("<version><support>") ); } /**************************************************************************/ char_data *find_keeper( char_data *ch ); /**************************************************************************/ char *mxp_tag_for_inventory_object(char_data * ch, OBJ_DATA *obj) { // the object must be in the inventory of ch assert(obj->carried_by==ch); static int i; static char result[5][MIL]; ++i%=5; char *r=result[i]; char att[MSL]; int att_count=0; // put on the basic stuff at the start strcpy(att, FORMATF("%d \"%s\"", obj->uid, strip_colour(obj->short_descr))); /* if you have it in your inventory you can do the following: inventory, always: - look - examine - drop - give* - put* - show Some times you can: - drink, eat, wield and second, look in // primary action - sell and value (shop keeper around) - wear Sell Value Drink */ const char *a=""; // create additional actions based on the object type switch ( obj->item_type ) { default: break; case ITEM_CAULDRON: case ITEM_CONTAINER: case ITEM_FLASK: case ITEM_MORTAR: a=" \"Look in\""; att_count++; break; case ITEM_POTION: a=" Quaff"; att_count++; break; case ITEM_WEAPON: a=" Wield Second"; att_count+=2; break; case ITEM_FOOD: a=" Eat"; att_count++; break; case ITEM_DRINK_CON: a=" Drink"; att_count++; break; } strcat(att, a); // can we hold the object? if(CAN_WEAR(obj, OBJWEAR_HOLD)){ strcat(att, " Hold"); att_count++; }else{ // can we wear the object in a way other than hold long wf=obj->wear_flags; REMOVE_BIT(wf, OBJWEAR_TAKE | OBJWEAR_WIELD | OBJWEAR_HOLD | OBJWEAR_LODGED_ARM | OBJWEAR_LODGED_LEG | OBJWEAR_LODGED_RIB | OBJWEAR_SHEATHED | OBJWEAR_CONCEALED); if(wf){ // still have some wear flags strcat(att, " Wear"); att_count++; } } // check if we are in a shop that buys this type of item // note: this is very inefficient, it should be written so // in the room data, it is recorded if a shopkeeper is present. for ( char_data *keeper = ch->in_room->people; keeper; keeper = keeper->next_in_room ) { if( IS_NPC(keeper) ){ SHOP_DATA *pShop=keeper->pIndexData->pShop; if(pShop){ for ( int itype = 0; itype < MAX_TRADE; itype++ ) { if ( obj->item_type == pShop->buy_type[itype] ) { // will by the type strcat(att, " Sell Value"); att_count+=2; break; } } } } } strcpy(r, FORMATF("objinv%d %s", att_count, att)); return r; } /**************************************************************************/ // return the correct tag code for a particular object char *mxp_tag_for_object(char_data * ch, OBJ_DATA *obj) { if(!HAS_MXP(ch)){ return ""; } if(obj->carried_by){ if(obj->carried_by==ch){ if(obj->wear_loc==WEAR_NONE){ // object is in inventory return mxp_tag_for_inventory_object(ch, obj); }else{ // object is equipment return(FORMATF("objeq %d \"%s\"", obj->uid, strip_colour(obj->short_descr))); } }else{ // object is carried by someone other than ch return ""; } } static int i; static char result[5][MIL]; ++i%=5; char *r=result[i]; strcpy(r, "ob-uid-nm"); switch ( obj->item_type ) { default: if(obj->in_room && CAN_WEAR(obj, OBJWEAR_TAKE)){ strcat(r,"_g"); } break; case ITEM_FOOD: strcat(r,"_food"); if(obj->in_room && CAN_WEAR(obj, OBJWEAR_TAKE)){ strcat(r,"_g"); } break; case ITEM_DRINK_CON: strcat(r,"_drink"); if(obj->in_room && CAN_WEAR(obj, OBJWEAR_TAKE)){ strcat(r,"_g"); } break; case ITEM_FOUNTAIN: strcat(r,"_fount"); break; } // put the uid on the end of the tag strcat(r, FORMATF(" %d \"%s\"", obj->uid, strip_colour(obj->short_descr))); return r; } /**************************************************************************/ // return the correct tag code for a particular mob // Very basic for now... will be improved when the mxp spec improves or // someone has time/desire. - Kal, July 01 char *mxp_tag_for_mob(char_data * ch, char_data *mob) { if(!HAS_MXP(ch) || !ch || !mob || (ch->in_room!=mob->in_room) ){ return ""; } return(FORMATF("mb-uid-nm %d \"%s\"", mob->uid, strip_colour(mob->short_descr))); } /**************************************************************************/ /**************************************************************************/