/********************************************************** *************** S U N D E R M U D *** 2 . 0 ************** ********************************************************** * The unique portions of the SunderMud code as well as * * the integration efforts for code from other sources is * * based primarily on the efforts of: * * * * Lotherius <aelfwyne@operamail.com> (Alvin W. Brinson) * * and many others, see "help sundermud" in the mud. * **********************************************************/ #include "everything.h" /* * In this file, always check for MSP/MXP before checking for Pueblo. * * The reason? Newer ZMuds "support" pueblo. However, if you use Pueblo commands, * most of them are broken and behave very badly. zMud returns a Puebloclient string, * however, which results in lots of broken things that would've worked just fine with * the MXP code. Lesson: Check for MXP before checking for Pueblo. */ /* * Function to strip linefeed. Because ZMud barfs with <SEND></SEND> on a simple linefeed. */ char *strip_lf ( char *str ) { static char newstr[MAX_STRING_LENGTH]; int i, j; for ( i = j = 0; str[i] != '\0'; i++ ) if ( str[i] != '\n' ) { newstr[j++] = str[i]; } newstr[j] = '\0'; return newstr; } /* Sound Stuff */ void do_sound ( CHAR_DATA * ch, char *argument ) { char arg[MAX_STRING_LENGTH]; if ( IS_NPC(ch) || !ch->desc ) return; argument = one_argument ( argument, arg ); if ( !ch->desc->msp && !ch->desc->mxp && !ch->desc->pueblo ) { ch->desc->msp = TRUE; send_to_char ( "Attempting to enable MSP sound. Note: Pueblo and MXP clients do not need to enable sound via this command.\n\r\n\r", ch ); send_to_char ( "!!SOUND(off U=" TXT_SOUNDURL ")",ch ); send_to_char ( "!!MUSIC(off U=" TXT_SOUNDURL ")",ch ); send_to_char ( "\n\rIf you have an older version of Zmud you must read {Whelp MSP{w\n\r", ch); send_to_char ( "for information on how to install sounds. Other clients may vary.\n\r", ch); send_to_char ( "{WMSP sound is on. You must have an MSP client. (Help MSP)\n\r", ch ); send_to_char ( "Now showing the sound control panel:\n\r\n\r", ch); ch->pcdata->svolume = 75; ch->pcdata->mvolume = 50; } if ( !str_cmp ( arg, "off" ) ) { if ( ch->desc->mxp ) { send_to_char ( "Cannot disable sound in MXP completely, but we will set the volume to Zero.\n\r", ch ); ch->pcdata->mvolume = 0; ch->pcdata->svolume = 0; } else if ( ch->desc->pueblo ) { send_to_char ( "Please disable sound in your client with Pueblo.\n\r", ch ); } else if ( ch->desc->msp ) { send_to_char ( "Sound is now off.\n\r", ch ); send_to_char ( "!!SOUND(off)",ch ); send_to_char ( "!!MUSIC(off)",ch ); ch->desc->msp = FALSE; } else { send_to_char ( "You don't have sounds enabled to begin with.\n\r", ch ); return; } send_to_char ( "\n\rIf you prefer, the \"stop\" command will stop the current sound and leave sound on.\n\r", ch); return; } if ( !str_cmp ( arg, "m" ) || !str_cmp (arg, "music") ) { if ( atoi(argument) < 0 || atoi(argument) > 100) { send_to_char ("Volume values range from 0 (Off) to 100\%\n\r", ch); return; } form_to_char ( ch, "Music volume: %d\n\r", atoi(argument) ); ch->pcdata->mvolume = atoi(argument); return; } if ( !str_cmp ( arg, "s" ) || !str_cmp (arg, "sounds") ) { if ( atoi(argument) < 0 || atoi(argument) > 100) { send_to_char ("Volume values range from 0 (Off) to 100\%\n\r", ch); return; } form_to_char ( ch, "Sound Effects volume: %d\n\r", atoi(argument) ); ch->pcdata->svolume = atoi(argument); return; } form_to_char ( ch, "{G[{W---{G]{w Music {G[{W---{G]{w Sound Effects\n\r"); send_to_char ( "----------------------------------------------\n\r", ch); form_to_char ( ch, "{G[{W%3s{G]{w (m) Music Volume {G[{W%3s{G]{w (s) Sounds Volume\n\r", ( ch->pcdata->mvolume > 0 ? itos(ch->pcdata->mvolume) : "OFF" ), ( ch->pcdata->svolume > 0 ? itos(ch->pcdata->svolume) : "OFF" ) ); send_to_char ( "----------------------------------------------\n\r", ch); send_to_char("Syntax: sound <type> <vol>\n\r (Enter 0 for \"OFF\", type is \"m\" or \"s\")\n\r", ch); if ( ch->desc->msp ) send_to_char("Use \"sound off\" to turn MSP off completely.\n\r", ch); return; } void do_stop ( CHAR_DATA *ch) { if (IS_NPC(ch) || !ch->desc) return; else if ( ch->desc->pueblo ) { send_to_char ( "<img xch_sound=stop>", ch ); send_to_char ( "Currently playing sounds and music halted.\n\r", ch); } else if ( ch->desc->mxp ) { send_to_char ( MXP_SECURE "<SOUND off><MUSIC off>" MXP_LLOCK, ch ); send_to_char ( "Currently playing sounds and music halted.\r\n", ch ); } else if ( ch->desc->msp ) { /* We're not going to clear "playing" because we don't want to restart the song yet. */ send_to_char ( "!!SOUND(off)",ch ); send_to_char ( "!!MUSIC(off)",ch ); send_to_char ( "Currently playing sounds and music halted.\n\r", ch); } else send_to_char("You don't have sound enabled...\n\r", ch); return; } // Stops music on a desc with no visible output. void stop_music ( DESCRIPTOR_DATA *d ) { if ( d->pueblo ) { send_to_desc ( d, "<img xch_sound=stop>" ); return; } else if ( d->mxp ) { send_to_desc ( d, MXP_SECURE "<MUSIC off>" MXP_LLOCK ); return; } else if ( d->msp ) { send_to_desc ( d, "!!MUSIC(off)" ); return; } return; } /* Call this with: filename, ch */ /* eventually add sound classes */ void sound ( const char *fname, CHAR_DATA *ch ) { if ( IS_NPC(ch) || !ch->desc ) return; if ( ch->desc->mxp ) { form_to_char ( ch, MXP_SECURE "<SOUND %s V=%d U=" TXT_SOUNDURL ">" MXP_LLOCK, fname, ch->pcdata->svolume ); } else if ( ch->desc->pueblo ) { form_to_char ( ch, "</xch_mudtext><img xch_volume=%d xch_device=wave>", ch->pcdata->svolume ); form_to_char ( ch, "<img xch_sound=play href=\"" TXT_SOUNDURL "%s\"><xch_mudtext>", fname ); } else if ( ch->desc->msp ) { // URL Tag is from V.3 specification. V.2 spec doesn't have it. form_to_char ( ch, "!!SOUND(%s V=%d U=" TXT_SOUNDURL "%s)", fname, ch->pcdata->svolume, fname ); } return; } void music ( const char *fname, CHAR_DATA *ch, bool repeat ) { if ( IS_NPC(ch) || !ch->desc ) return; if ( !ch->desc->msp && !ch->desc->pueblo && !ch->desc->mxp ) return; /* Let's not repeat the same thing over and over. */ /* This saves bandwidth, and keeps a song that has been stopped from playing */ if ( !str_cmp ( fname, ch->pcdata->mplaying ) ) return; free_string ( ch->pcdata->mplaying ); ch->pcdata->mplaying = str_dup ( fname ); if (repeat) { if ( ch->desc->mxp ) form_to_char ( ch, MXP_SECURE "<MUSIC %s V=%d L=-1 C=1 U=" TXT_SOUNDURL ">" MXP_LLOCK, fname, ch->pcdata->mvolume ); else if ( ch->desc->pueblo ) { form_to_char ( ch, "</xch_mudtext><img xch_volume=%d xch_device=midi>", ch->pcdata->mvolume ); form_to_char ( ch, "<img xch_sound=loop href=\"" TXT_SOUNDURL "%s\"><xch_mudtext>", fname ); } else if ( ch->desc->msp ) form_to_char ( ch, "!!MUSIC(%s V=%d L=-1 C=1 U=" TXT_SOUNDURL "%s)", fname, ch->pcdata->mvolume, fname ); } else { if ( ch->desc->mxp ) form_to_char ( ch, MXP_SECURE "<MUSIC %s V=%d C=1 U=" TXT_SOUNDURL ">" MXP_LLOCK, fname, ch->pcdata->mvolume ); else if ( ch->desc->pueblo ) { form_to_char ( ch, "</xch_mudtext><img xch_volume=%d xch_device=midi>", ch->pcdata->mvolume ); form_to_char ( ch, "<img xch_sound=play href=\"" TXT_SOUNDURL "%s\"><xch_mudtext>", fname ); } else if ( ch->desc->msp ) form_to_char ( ch, "!!MUSIC(%s V=%d C=1 U=" TXT_SOUNDURL "%s)", fname, ch->pcdata->mvolume, fname ); } return; } // Sets up MXP // // If you have any entities to declare, put them here. // But keep in mind, colors don't translate in entities correctly... Grr... // void mxp_init ( DESCRIPTOR_DATA *d ) { char buf[MSL]; SNP ( buf, MXP_SECURE "<!ELEMENT RName '<FONT \"Comic Sans MS\" COLOR=CYAN> <B>' FLAG=\"RoomName\">" "<!ELEMENT RDesc FLAG='RoomDesc'>" "<!ELEMENT RExits FLAG='RoomExit'>" "<!ELEMENT Ex '<SEND>'>" ); write_to_buffer ( d, buf, 0 ); SNP ( buf, MXP_SECURE "<SUPPORT image send frame stat>" ); write_to_buffer ( d, buf, 0 ); SNP ( buf, MXP_LLOCK ); // Locked mode, no player MXP tags..... // if ( d->pueblo ) // write_to_buffer ( d, "<xch_mudtext>", 0 ); // write_to_buffer ( d, buf, 0 ); return; } /* * Argument is the pathname of the image to display. * * Bitmap or GIF (why no PNG support in the clients???) * * if pageit is true, the text will be paged rather than sent. * * Argh.... ZMud displays PUEBLO images just fine, but fuxors its own format! * */ void inline_image ( DESCRIPTOR_DATA *d, char *image, char *align, bool pageit ) { char buf[MSL]; if ( d->mxp ) { SNP ( buf, "<IMAGE FName=\"%s\" URL=\"" TXT_IMAGEURL "\" ALIGN=\"%s\">", strip_cr(strip_lf(image)), align ); if ( pageit ) { if ( !d->character ) { bugf( "No character on inline_image paged call." ); return; } page_to_char ( MXP_SECURE, d->character ); page_to_char ( buf, d->character ); page_to_char ( MXP_LLOCK, d->character ); } else { send_to_desc ( d, MXP_SECURE ); send_to_desc ( d, buf ); send_to_desc ( d, MXP_LLOCK ); } } else if ( d->pueblo && !d->mxp ) // VEry careful NOT to use Pueblo if we have MXP since ZMUD Barfs on Pueblo "emulation" { SNP ( buf, "<img src=\"" TXT_IMAGEURL "%s\" align=%s>", image, align ); if ( pageit ) { if ( !d->character ) { bugf( "No character on inline_image paged call." ); return; } page_to_char ( "</xch_mudtext>", d->character ); page_to_char ( buf, d->character ); page_to_char ( "<xch_mudtext>", d->character ); } else { send_to_desc ( d, "</xch_mudtext>" ); send_to_desc ( d, buf ); send_to_desc ( d, "<xch_mudtext>" ); } } return; } // Allows user to specify an image to display to self from the default location. void do_image ( CHAR_DATA *ch, char *argument ) { inline_image ( ch->desc, argument, "bottom", FALSE ); } /* * Sends an appropriate client tag to center text. * * Not only does MXP not have a <center> tag that I can find, * it also refuses to properly display the Pueblo tag through * its compatibility mode. So, no MXP clients get the tag. */ void tag_center ( DESCRIPTOR_DATA *d, bool onoff, bool pageit ) { char buf[MSL]; if ( !d->pueblo ) return; if ( onoff ) // True = turn centering on. { if ( d->pueblo && !d->mxp ) { SNP ( buf, "</xch_mudtext><center><xch_mudtext>" ); } } else // False = turn centering off. { if ( d->pueblo && !d->mxp ) { SNP ( buf, "</xch_mudtext></center><xch_mudtext>" ); } } if ( pageit ) // Can only page to a char { if ( !d->character ) { bugf ( "No character on tag_center paged call." ); return; } page_to_char ( buf, d->character ); } else send_to_desc ( d, buf ); return; } /* * Opens a "secure" channel for tags. * Implemented as char so it can be embedded in other text. */ char *tag_secure ( DESCRIPTOR_DATA *d ) { static char buf[128]; buf[0] = '\0'; if ( d->mxp ) SLCAT ( buf, MXP_SECURE ); else if ( d->pueblo ) SLCAT ( buf, "</xch_mudtext>" ); else SLCAT ( buf, "" ); return buf; } /* * Closes secure tag channel * Implemented as char so it can be embedded in other text. */ char *tag_close ( DESCRIPTOR_DATA *d ) { static char buf[128]; buf[0] = '\0'; if ( d->mxp ) SLCAT ( buf, MXP_LOPEN ); /* We're going to use locked open mode now. */ else if ( d->pueblo ) SLCAT ( buf, "<xch_mudtext>" ); else SLCAT ( buf, "" ); return buf; } /* * Returns a wrapped string that is clickable. * * Unfortunately, Pueblo doesn't support a drop-down menu like MXP so this only allows one item. */ char *click_cmd ( DESCRIPTOR_DATA *d, char *text, char *command, char *mouseover ) { static char buf[MSL]; buf[0] = '\0'; if ( d->mxp ) { SNP ( buf, MXP_SECURE "<send \"%s\" hint=\"%s\">%s</SEND>" MXP_LLOCK, command, mouseover, strip_cr(strip_lf(text)) ); } else if ( d->pueblo ) { SNP ( buf, "</xch_mudtext><a xch_cmd=\"%s\" xch_hint=\"%s\">%s</a><xch_mudtext>", command, mouseover, text ); } else { SNP ( buf, text ); } return buf; }