/**************************************************************************/ // help.cpp - dawn help system /*************************************************************************** * 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 "help.h" #include "msp.h" #include "olc_ex.h" #include "colour.h" help_data *help_first; help_data *help_last; helpfile_data *helpfile_first; helpfile_data *helpfile_last; // prototypes void colour_convert_helps( helpfile_data *pHelpfile ); /**************************************************************************/ GIO_START(help_data) GIO_STR(keyword) GIO_STR(title) GIO_STR(command_reference) GIO_SHINT(level) GIO_WFLAGH(flags, "helpflags ", help_flags) GIO_WFLAGH(category, "category ", help_category_types) GIO_STR(parent_help) GIO_STR(see_also) GIO_STR(immsee_also) GIO_STR(spell_name) GIO_STR(continues) GIO_STR(text) GIO_STR(last_editor) GIO_LONG(last_editdate) GIO_INT(assigned_editor) GIO_FINISH_STRDUP_EMPTY GIO_START(helpfile_data) GIO_CHAR(colourcode) GIO_STR(title) GIO_STR(editors) GIO_SHINT(security) GIO_FINISH_STRDUP_EMPTY #define HELP_QUICKLOOKUP_HASH (83) class help_quicklookup_data { public: help_data *pHelp; help_quicklookup_data *next; ~help_quicklookup_data() {if(next) delete next;}; }; help_quicklookup_data *help_quicklookup_table[HELP_QUICKLOOKUP_HASH]; /**************************************************************************/ void get_widest_line_stats(const char *text, bool ignore_colour_codes, sh_int *line_number, sh_int *line_width) { // idiot check if(!text){ *line_number=0; *line_width=0; return; } const char*p=text; sh_int max_width=0; sh_int max_line=0; sh_int current_line=1; sh_int current_width=0; for( ;!IS_NULLSTR(p); p++){ if(*p=='\r'){ continue; } if(*p=='\n'){ if(current_width>max_width){ max_width=current_width; max_line=current_line; } current_line++; current_width=0; continue; } // dont count the width of colour codes // doesn't take into account if a new line occurs before // a colour code is completed if(ignore_colour_codes && *p=='`'){ if(*(p+1)=='='){ current_width-=3; // '`' '=' and custom colour code }else{ current_width-=2; // '`' and colour code } } current_width++; } // end of the text, lets check if the bottom line is the longest if(current_width>max_width){ max_width=current_width; max_line=current_line; } *line_number=max_line; *line_width=max_width; } /**************************************************************************/ // display the history - debugging code - not enabled by default void do_helphistory( char_data *ch, char *argument ) { //return; // make sure we have a pc_data - used to record/display history pc_data*p; p=ch?TRUE_CH(ch)->pcdata:NULL; if(!p){ ch->println("players only sorry"); return; } int i; for(i=0; i<MAX_HELP_HISTORY; i++){ ch->printlnf("%2d] %s", i, p->help_history[i]); } ch->printlnf("p->help_history_index=%d",p->help_history_index); ch->printlnf("p->help_next_count=%d", p->help_next_count); } /**************************************************************************/ help_data *help_allocate_new_entry( void ) { help_data *pHelp; static help_data help_zero; pHelp= (help_data *)alloc_perm( sizeof(*pHelp) ); *pHelp = help_zero; pHelp->title =str_dup(""); pHelp->parent_help =str_dup(""); pHelp->see_also =str_dup(""); pHelp->immsee_also =str_dup(""); pHelp->spell_name =str_dup(""); pHelp->continues =str_dup(""); pHelp->keyword =str_dup(""); pHelp->text =str_dup(""); pHelp->undo_edittext=str_dup(""); pHelp->undo_wraptext=str_dup(""); pHelp->command_reference=str_dup(""); return pHelp; } /**************************************************************************/ helpfile_data *helpfile_allocate_new_entry( void ) { helpfile_data *pHelpFD; static helpfile_data helpFD_zero; pHelpFD=(helpfile_data *)alloc_perm( sizeof(*pHelpFD) ); *pHelpFD = helpFD_zero; pHelpFD->colourcode='{'; // oldstyle colour codes for now return pHelpFD; } /**************************************************************************/ bool is_keyword_delimiter( char c) { if ( c == '\'' || c == '"' || c == '%' || c == '(' || c == '`'){ return true; } return false; } /**************************************************************************/ // finds what can be a valid keyword from the input... stores it into // keyword and then returns the last character inputted into the keyword. char *help_find_keyword( char *input, char *keyword, char_data *looker) { char cEnd; bool matched=false; char *keyword_start=keyword; // trim all leading whitespace while ( is_space(*input) ){ input++; } cEnd = ' '; if ( is_keyword_delimiter(*input) && *input!='`' ){ if ( *input == '(' ) { cEnd = ')'; input++; } else cEnd = *input++; } while ( *input != '\0' ) { if( (is_space(cEnd) && is_space(*input)) || *input== cEnd || *input== '`' || *input== ',' || *input== '\n' || (!is_keyword_delimiter(cEnd) && cEnd!=')' && (is_keyword_delimiter(*input)) || *input==')') ) { break; } *keyword++ = *input++; matched=true; } *keyword = '\0'; if(matched && is_space(cEnd)){ input--; // the last matching character } if(*input=='`'){ input--; // we had a colour code } // if we have a help keyword ending with a . and there is // no matching help entry for it, reverse by one character. keyword--; if(*keyword=='.' && str_len(keyword_start)>0 && !help_get_by_keyword(keyword_start, looker, true)) { *keyword='\0'; input--; } return input; } /**************************************************************************/ // returns a helpfile found by its name helpfile_data *helpfile_get_by_filename( char * argument) { helpfile_data *pHelpFD; // get the helpfile name if (IS_NULLSTR(argument)) return NULL; if(str_suffix(".txt",argument)) { strcat(argument,".txt"); } for ( pHelpFD = helpfile_first; pHelpFD; pHelpFD = pHelpFD->next ) { if (!str_cmp( argument, pHelpFD->file_name) ) { return pHelpFD; } } return NULL; } /**************************************************************************/ void help_init_quicklookup_table() { int i; for(i=0; i<HELP_QUICKLOOKUP_HASH; i++){ if(help_quicklookup_table[i]){ delete help_quicklookup_table[i]; help_quicklookup_table[i]=NULL; } } // loop thru adding all the helps for(help_data *pHelp=help_last; pHelp; pHelp=pHelp->prev){ help_quicklookup_data *node; char keyword[MIL]; char *p=pHelp->keyword; while(*p){ p=one_argument(p, keyword); // forces lowercase if(keyword[0] && keyword[1] && keyword[2]){ // words 3 characters and longer are indexed int hashkey=keyword[0] + keyword[1] + keyword[2]; hashkey%=HELP_QUICKLOOKUP_HASH; node=new help_quicklookup_data; node->pHelp=pHelp; node->next=help_quicklookup_table[hashkey]; help_quicklookup_table[hashkey]=node; } } } } /**************************************************************************/ // - Kal void save_helpfile_NAFF( helpfile_data *pHelpfile ) { FILE *fp; fclose( fpReserve ); char name_without_path[MIL]; if(count_char(pHelpfile->file_name,'/')){ strcpy(name_without_path, strrchr(pHelpfile->file_name, '/')+1); }else{ strcpy(name_without_path,pHelpfile->file_name); } char newfilename[MIL]; sprintf(newfilename, "%s%s.save", BACKUP_HELP_DIR, name_without_path); if ( !( fp = fopen( newfilename, "w" ) ) ) { bugf("save_helpfile_NAFF(): fopen '%s' for write - error %d (%s)", newfilename, errno, strerror( errno)); exit_error( 1 , "save_helpfile_NAFF", "write error"); } fprintf(fp, "#HELPFILEDATA\n"); GIO_SAVE_RECORD(helpfile_data, pHelpfile, fp, NULL); fprintf(fp, "#HELPENTRIES\n"); for( help_data *pHelp = help_first; pHelp != NULL; pHelp = pHelp->next ) { if (pHelp->helpfile==pHelpfile && !IS_SET(pHelp->flags, HELP_REMOVEHELP)) { if(GAMESETTING2(GAMESET2_DONT_SAVE_LASTEDITORS)){ replace_string(pHelp->last_editor,""); pHelp->last_editdate=0; } GIO_SAVE_RECORD(help_data, pHelp, fp, NULL); } } fprintf(fp, "EOF~\n"); // mark the end of a GIO based help section fprintf(fp, "#$\n"); // mark the end of the file for boot_db(); fclose( fp ); fpReserve = fopen( NULL_FILE, "r" ); // rename help/*.txt to bak_help/*.txt.old, // then bak_help/*.txt.save to help/*.txt { char old_filename[MIL]; sprintf(old_filename, "%s%s.old", BACKUP_HELP_DIR, name_without_path); char filename[MIL]; sprintf(filename, "%s%s", HELP_DIR, pHelpfile->file_name); #ifdef WIN32 unlink(old_filename); #endif if(rename(filename,old_filename)!=0){ bugf("An error occured renaming '%s' to '%s'!.. exiting to avoid helpfile corruption.", filename, old_filename); exit_error( 1 , "save_helpfile_NAFF", "error occured while renaming old helpfile"); } if(rename(newfilename, filename)!=0){ bugf("An error occured renaming '%s' to '%s'!.. exiting to avoid helpfile corruption.", newfilename, filename); exit_error( 1 , "save_helpfile_NAFF", "error occured while renaming new helpfile"); } } return; } /**************************************************************************/ extern char strArea[MIL]; /**************************************************************************/ // - Kal void load_helpfile_NAFF( FILE *fp) { helpfile_data *pHelpfile=helpfile_allocate_new_entry(); GIO_LOAD_RECORD(helpfile_data, pHelpfile, fp); pHelpfile->file_name = str_dup((char *) &strArea[str_len(HELP_DIR)]); pHelpfile->vnum=top_helpfile; fread_word(fp); // #HELPENTRIES help_data *pHelp; for(;;){ pHelp=help_allocate_new_entry(); if(GIO_LOAD_RECORD(help_data, pHelp, fp)>1){ break; // hit an EOF~ }; // link it into the list if ( !help_first){ help_first = pHelp; help_first->prev=NULL; } if ( help_last){ help_last->next = pHelp; pHelp->prev = help_last; } help_last = pHelp; pHelp->next = NULL; pHelp->helpfile=pHelpfile; pHelp->helpfile->entries++; // patching up the help entries if(has_colour(pHelp->keyword)){ replace_string(pHelp->keyword,rtrim_string(ltrim_string(strip_colour(pHelp->keyword)))); } if(pHelp->level==-1){ pHelp->level=0; SET_BIT(pHelp->flags, HELP_HIDE_KEYWORDS); } pHelp->undo_edittext=str_dup(""); pHelp->undo_wraptext=str_dup(""); get_widest_line_stats(pHelp->text, true, &pHelp->widest_line, &pHelp->widest_line_width); /* // help keyword has single quotes in it... remove and convert 'a b' to a-b // and trim any whitespace to the right of the keywords if(count_char(pHelp->keyword, '\'')){ char newkey[MSL]; newkey[0]='\0'; char *p=pHelp->keyword; char pword[MIL]; while(*p){ p=first_arg(p, pword, false); if(has_whitespace(pword)){ for(char *pw=pword; *pw; pw++){ if(is_space(*pw)){ *pw='-'; } } } if(!is_exact_name(pword, newkey)){ strcat(newkey, pword); strcat(newkey," "); } } replace_string(pHelp->keyword,rtrim_string(newkey)); }else{ replace_string(pHelp->keyword,rtrim_string(pHelp->keyword)); } */ top_help++; } // add helpfile to the list of helpfiles if (helpfile_first) { helpfile_last->next=pHelpfile; helpfile_last=pHelpfile; } else { helpfile_first=pHelpfile; helpfile_last=pHelpfile; } top_helpfile++; colour_convert_helps(pHelpfile); return; } /**************************************************************************/ char *help_generate_prev_next_for_char(help_data *pHelp, char_data *ch) { static char result[MSL*2]; result[0]='\0'; pc_data *p=TRUE_CH(ch)->pcdata; // [PREV][NEXT] links if(ch->desc && ch->desc->connected_state==CON_PLAYING){ if(!IS_SET(pHelp->flags, HELP_HIDE_PREVNEXT) && HAS_MXP(ch)){ if(!IS_NULLSTR(p->help_history[p->help_history_index])){ strcat(result, "`=[["); strcat(result, mxp_create_send(ch,"helpprev","PREV")); strcat(result, "]"); }else{ strcat(result, "`=][PREV]"); } if(p->help_next_count>0){ strcat(result, "`=[["); strcat(result, mxp_create_send(ch, "helpnext","NEXT")); strcat(result, "]"); }else{ strcat(result, "`=][NEXT]"); } } } return result; } /**************************************************************************/ // returns the references as a list of helplinks (only for those which exist) char *help_generate_references_list_linked(char_data *ch, const char *header, char *references) { static char result[MSL]; if(IS_NULLSTR(references)){ return ""; } char *p=references; result[0]='\0'; char pword[MIL]; while(*p){ p=first_arg(p, pword, false); if(IS_IMMORTAL(ch) || help_get_by_keyword(pword, ch, false)){ if(has_space(pword)){ strcat(result, FORMATF(" `=_'%s',", pword)); }else{ strcat(result, FORMATF(" `=_%s,", pword)); } } } if(!IS_NULLSTR(result)){ // trim the trailing comma result[str_len(result)-1]='\0'; // insert the header, and prepend a new line strcpy(result, FORMATF("%s%s\r\n", header, result)); } return result; } /**************************************************************************/ // returns the references as a list of helplinks (only for those which exist) char *help_generate_references_links(char_data *ch, help_data *pHelp) { static char result[MSL*2]; result[0]='\0'; strcat(result, help_generate_references_list_linked(ch, "`=|PARENT HELP: ", pHelp->parent_help)); strcat(result, help_generate_references_list_linked(ch, "`=|>>>SEE ALSO: ", pHelp->see_also)); strcat(result, help_generate_references_list_linked(ch, "`=|>>>IMMSEE ALSO: ", pHelp->immsee_also)); return result; } /**************************************************************************/ char * get_spinfo_data_requirements(char_data *ch, char *text, char have_colour, char havenot_colour); /**************************************************************************/ char *help_generate_help_entry_for_char(help_data *pHelp, char_data *ch) { if(!pHelp){ bugf("help_generate_help_entry_for_char() Empty help to display!?!?"); do_abort(); return ""; } bool use_prevnext_bar=false; if(!ch || !TRUE_CH(ch)->pcdata){ return "help_generate_help_entry_for_char(): players only sorry"; } // the maximum size a help can get is roughtly MAX_HELP_SIZE // + MIL for each extra format option (header, title, see also, parent, continues) static char result[MAX_HELP_SIZE + 6*MIL]; result[0]='\0'; char prefix[MIL]; prefix[0]='\0'; // display the category and edit option if(pHelp->category){ char *cat=flag_string(help_category_types, pHelp->category); strcat(prefix, FORMATF("`S[`x%s`s]", mxp_create_send(ch, FORMATF("helpcat %s", cat), cat))); if(HAS_SECURITY(ch, 9) && HAS_MXP(ch)){ strcat(prefix, mxp_create_send(ch, FORMATF("hedit %s", pHelp->keyword), "edit")); } }else{ if(HAS_SECURITY(ch, 9) && HAS_MXP(ch)){ strcat(prefix,"`S"); strcat(prefix, mxp_create_send(ch, FORMATF("hedit %s", pHelp->keyword), "edit")); } } // display the header if(!GAMESETTING3(GAMESET3_HELP_HEADER_FOOTER_BAR_DISABLED) && !IS_NULLSTR(game_settings->help_header_bar) && !IS_SET(pHelp->flags, HELP_HIDE_HEADER_FOOTER)) { strcat(result, "`=\xad"); // default colour for the bar strcat(result, game_settings->help_header_bar); strcat(result, "`x\r\n"); } // display the keywords if appropriate unless they are hidden if( IS_SET(pHelp->flags, HELP_HIDE_KEYWORDS) ) { if (IS_IMMORTAL(ch) && ch->desc->connected_state==CON_PLAYING){ strcat(result, prefix); strcat(result,FORMATF("`g[%2d - %s]`S%s`x\r\n", pHelp->level, pHelp->helpfile? mxp_create_send(ch, FORMATF("hlist %s", pHelp->helpfile->file_name), pHelp->helpfile->file_name) :"`#`Runknown!!!`^", pHelp->keyword)); } }else{ if (IS_IMMORTAL(ch)){ strcat(result, prefix); strcat(result, FORMATF("`G[%2d - %s]`=J%s`x\r\n", pHelp->level, pHelp->helpfile? mxp_create_send(ch, FORMATF("hlist %s", pHelp->helpfile->file_name), pHelp->helpfile->file_name) :"`#`Runknown!!!`^", pHelp->keyword)); }else{ strcat(result,prefix); strcat(result, FORMATF("`=J%s`x\r\n", pHelp->keyword)); } } // display the title 'centered' if(!IS_NULLSTR(pHelp->title)){ int spaces=(70-c_str_len(pHelp->title))/2; strcat(result, FORMATF("%*c`#`=u%s`&\r\n\r\n",spaces,' ',pHelp->title)); } // display the command reference - left align if(!IS_NULLSTR(pHelp->command_reference)){ strcat(result, FORMATF("`#`=uCommand Reference: %s`&\r\n\r\n",pHelp->command_reference)); } // display redirection comment if(IS_SET(pHelp->flags, HELP_REDIRECTION_ENTRY)){ strcat(result, "`=JRedirection help file:\r\n\r\n"); } // check if help entry isn't too long if (str_len(pHelp->text)>MAX_HELP_SIZE) { char logbuf[MSL]; // logging of extra long helps sprintf(logbuf, "Help entry found '%s' - but it is too long to be " "displayed... please inform an admin so it can be fixed.\r\n", pHelp->keyword); strcat(result, logbuf); sprintf(logbuf, "longhelp: %s found help entry too long '%s'!", ch->name, pHelp->keyword); append_datetime_ch_to_file( ch, NO_HELP_FILE, logbuf); strcat (logbuf, "\r\n"); wiznet(logbuf,ch,NULL,WIZ_NOHELP,0,get_trust(ch)); return result; } // if this help has a spellname insert the casting syntax int sn=-1; if(!IS_NULLSTR(pHelp->spell_name)){ sn=spell_exact_lookup(pHelp->spell_name); int cla; int ct; char *target; if(sn>=0){ // get the target info switch ( skill_table[sn].target ) { default: case TAR_CHAR_SELF: target=""; break; case TAR_IGNORE: target="<target>"; break; case TAR_MOB_OFFENSIVE: case TAR_CHAR_OFFENSIVE: target="<target> `S(offensive)"; break; case TAR_CHAR_DEFENSIVE: target="<target>"; break; case TAR_OBJ_INV: target="<object in inventory>"; break; case TAR_DIRECTION: target="<direction>"; break; case TAR_OBJ_MOB_OFF: case TAR_OBJ_CHAR_OFF: target="<target|object> `S(offensive)"; break; case TAR_OBJ_CHAR_DEF: target="<target|object>"; break; } // loop thru all the cast types, checking if we have a // class which can cast the spell for(ct=CCT_NONE+1; !IS_NULLSTR(castnames_types[ct].name); ct++){ // find a class which uses this cast type // that has access to the spell for(cla=0; !IS_NULLSTR(class_table[cla].name); cla++){ if(class_table[cla].class_cast_type==ct && skill_table[sn].skill_level[cla]>0 && skill_table[sn].skill_level[cla]<=LEVEL_HERO) { if(has_space(skill_table[sn].name)){ strcat(result, FORMATF("`=lSyntax: `=?%s '%s' %s\r\n", castcommand_types[ct].name, skill_table[sn].name, target)); }else{ strcat(result, FORMATF("`=lSyntax: `=?%s %s %s\r\n", castcommand_types[ct].name, skill_table[sn].name, target)); } break; // skip to next cast type } } } strcat(result, "\r\n"); } } strcat(result, "`=?"); // help text defaults to CC_HELP_DEFAULT :) // display the text body of the help // Strip leading '.' to allow initial blanks. strcat(result, pHelp->text[0] == '.'?pHelp->text+1:pHelp->text); // if it is a spell, display the realms/spheres etc if(sn>=0){ // check if we need to add the newline int lines=0; { bool in_colour_code=false; char *pstr; pstr=&pHelp->text[UMAX(str_len(pHelp->text)-50, 0)]; // jump back up to 50 characters from end for(; *pstr; pstr++){ switch(*pstr){ case '\n': lines++; break; // ignore these case '\r': case ' ': break; case COLOURCODE: in_colour_code=true; break; default: { if(in_colour_code){ if(*pstr!='='){ in_colour_code=false; } }else{ lines=0; } } break; } } } if(lines<2){ strcat(result, "\r\n"); } char *txt; // realms txt=get_spinfo_data_requirements( ch, flag_string( realm_flags, skill_table[sn].realms), 'r', 'R'); if(strcmp(strip_colour(txt), "none")){ strcat(result, FORMATF("`xRealms: %s\r\n", txt)); } // spheres txt=get_spinfo_data_requirements( ch, flag_string( sphere_flags, skill_table[sn].spheres), 'g', 'G'); if(strcmp(strip_colour(txt), "none")){ strcat(result, FORMATF("`xSpheres: %s\r\n", txt)); } // elements txt=get_spinfo_data_requirements( ch, flag_string( element_flags, skill_table[sn].elements), 'b', 'B'); if(strcmp(strip_colour(txt), "none")){ strcat(result, FORMATF("`xElements & Seasons: %s\r\n", txt)); } // compositions txt=get_spinfo_data_requirements( ch, flag_string( composition_flags, skill_table[sn].compositions), 'c', 'C'); if(strcmp(strip_colour(txt), "none")){ strcat(result, FORMATF("`xCompositions: %s\r\n", txt)); } strcat(result, FORMATF("`xMana: %d DamType: %s\r\n", skill_table[sn].min_mana, flag_string(damtype_types, skill_table[sn].damtype))); } if(IS_SET(pHelp->flags, HELP_DISPLAY_MXP_DOUBLE)){ strcat(result, "============================== MXP VERSION ==============================\r\n"); strcat(result, mxp_tagify(pHelp->text[0] == '.'?pHelp->text+1:pHelp->text)); } if(!IS_NULLSTR(pHelp->continues)){ strcat(result, FORMATF("\r\n`=|>>>This help continues in `=_%s\r\n", pHelp->continues)); } // put [PREV][NEXT] above the see also stuff if appropriate if(GAMESETTING4(GAMESET4_HELP_PREV_NEXT_SEPARATE_FROM_FOOTER) && GAMESETTING4(GAMESET4_HELP_PREV_NEXT_ABOVE_SEE_ALSO)) { strcat(result, help_generate_prev_next_for_char(pHelp, ch)); strcat(result, "\r\n"); } // put in the see also related references strcat(result, help_generate_references_links(ch, pHelp)); if(GAMESETTING5(GAMESET5_MXP_EDIT_AT_BOTTOM_OF_HELPS)){ if(HAS_SECURITY(ch, 9) && HAS_MXP(ch)){ strcat(result,"`S"); strcat(result, mxp_create_send(ch, FORMATF("hedit %s", pHelp->keyword), "edit")); strcat(result,"\r\n"); } } // put [PREV][NEXT] below the see also stuff if appropriate if(GAMESETTING4(GAMESET4_HELP_PREV_NEXT_SEPARATE_FROM_FOOTER) && GAMESETTING4(GAMESET4_HELP_PREV_NEXT_ABOVE_SEE_ALSO)) { strcat(result, help_generate_prev_next_for_char(pHelp, ch)); strcat(result, "\r\n"); } // put [PREV][NEXT] as the start of the footer bar if appropriate if(!GAMESETTING4(GAMESET4_HELP_PREV_NEXT_SEPARATE_FROM_FOOTER)){ char *pn=help_generate_prev_next_for_char(pHelp, ch); if(!IS_NULLSTR(pn)){ strcat(result, pn); use_prevnext_bar=true; } } // display the footer if(!GAMESETTING3(GAMESET3_HELP_HEADER_FOOTER_BAR_DISABLED) && !IS_NULLSTR(game_settings->help_footer_bar) && !IS_NULLSTR(game_settings->help_prevnext_footer_bar) && !IS_SET(pHelp->flags, HELP_HIDE_HEADER_FOOTER)) { strcat(result, "`=\xad"); // default colour for the bar if(use_prevnext_bar){ strcat(result, game_settings->help_prevnext_footer_bar); }else{ strcat(result, game_settings->help_footer_bar); } strcat(result, "\r\n"); }else{ // if we didn't display a footer, but displayed a [PREV][NEXT] // to form the start of the bar, we need to put a new line if(!GAMESETTING4(GAMESET4_HELP_PREV_NEXT_SEPARATE_FROM_FOOTER)){ strcat(result, "\r\n"); } } strcat(result,"`x"); return result; } /**************************************************************************/ void help_display_to_char(help_data *pHelp, char_data *ch) { ch->sendpage(help_generate_help_entry_for_char(pHelp, ch)); } /**************************************************************************/ bool help_valid_for_char(help_data *pHelp, char_data *ch) { if(!ch){ if(pHelp->level>LEVEL_IMMORTAL || IS_SET( pHelp->flags, HELP_NSUPPORT ) || IS_SET( pHelp->flags, HELP_NOBLE ) || IS_SET( pHelp->flags, HELP_RPSUPPORT ) ) { return false; } return true; } if ( pHelp->level > get_trust( ch ) ){ return false; } if ( IS_IMMORTAL( ch )){ return true; } if ( IS_SET( pHelp->flags, HELP_NSUPPORT )&& !IS_NEWBIE_SUPPORT(ch)){ return false; } if ( IS_SET( pHelp->flags, HELP_NOBLE ) && !IS_NOBLE(ch)){ return false; } if ( IS_SET( pHelp->flags, HELP_RPSUPPORT )&&!HAS_CONFIG( ch, CONFIG_RP_SUPPORT )){ return false; } if ( IS_SET( pHelp->flags, HELP_BUILDER ) && GET_SECURITY(ch)==0){ return false; } return true; } /**************************************************************************/ // return a help based on the keyword for a particular character help_data *help_get_by_keyword(char * keyword, char_data *ch, bool exact_match) { static bool space_to_dash=false; char buf[MSL]; int count=0; int number=1; help_data *pHelp; char key[MIL]; if(space_to_dash){ strncpy(buf,keyword, MSL-1); buf[MSL-1]='\0'; keyword=buf; bool found=false; // if this is set, we convert all spaces in the keyword to dashes for(char *sp=keyword;*sp; sp++){ if(*sp==' '){ *sp='-'; found=true; } } // if we didn't find any spaces, then the previous search didn't have // any spaces, therefore no point in searching again if(!found){ return NULL; } } // support x.keyword syntax number = number_argument( keyword, key); // use the hash table quick search system for words longer 3 characters+ if(str_len(key)>2 && !is_space(key[1]) && !is_space(key[2])){ int hashkey=LOWER(key[0]) + LOWER(key[1]) + LOWER(key[2]); if(key[0]=='\''){ hashkey-='\''; hashkey+=LOWER(key[3]); } hashkey%=HELP_QUICKLOOKUP_HASH; if(key[0]!='\'' || (key[3] && !is_space(key[3]))){ // because of the ' at the start it needs to be 4 characters help_quicklookup_data *node=help_quicklookup_table[hashkey]; while(node){ if( (exact_match && is_exact_name(key, node->pHelp->keyword)) || (!exact_match && is_name(key, node->pHelp->keyword)) ) { if(help_valid_for_char(node->pHelp, ch) && ++count == number){ return node->pHelp; } } node=node->next; } // if the search which has just failed to match didn't use // space_to_dash, retry using it. if(!space_to_dash){ space_to_dash=true; help_data *result=help_get_by_keyword(keyword, ch, exact_match); space_to_dash=false; return result; } return NULL; } } // for the 3 character or less words for(pHelp=help_first; pHelp; pHelp=pHelp->next){ if(is_name(key, pHelp->keyword)){ if(help_valid_for_char(pHelp, ch) && ++count == number){ return pHelp; } } } // if the search which has just failed to match didn't use // space_to_dash, retry using it. if(!space_to_dash){ space_to_dash=true; help_data *result=help_get_by_keyword(keyword, ch, exact_match); space_to_dash=false; return result; } return NULL; } /**************************************************************************/ // display a previously viewed help entry void do_helpprev( char_data *ch, char *argument ) { help_data *pHelp; // make sure we have a pc_data - used to record/display history pc_data*p; p=ch?TRUE_CH(ch)->pcdata:NULL; if(!p){ ch->println("players only sorry"); return; } // get the previous keyword p->help_history_index= (p->help_history_index - 1 + MAX_HELP_HISTORY) % MAX_HELP_HISTORY; char *keywords=p->help_history[p->help_history_index]; if(IS_NULLSTR(keywords)){ ++p->help_history_index%=MAX_HELP_HISTORY; ch->println("There are no more previous helps"); return; } // search for it pHelp=help_get_by_keyword(keywords, ch, true); if(!pHelp){ ch->printlnf("No help entry '%s' appears to exist anymore.",keywords); return; } // display it p->help_next_count++; p->help_history_index= (p->help_history_index - 1 + MAX_HELP_HISTORY) % MAX_HELP_HISTORY; help_display_to_char(pHelp, ch); ++p->help_history_index%=MAX_HELP_HISTORY; } /**************************************************************************/ // display the next help in the help history after using help prev void do_helpnext( char_data *ch, char *argument ) { help_data *pHelp; // make sure we have a pc_data - used to record/display history pc_data*p; p=ch?TRUE_CH(ch)->pcdata:NULL; if(!p){ ch->println("players only sorry"); return; } // get the next keyword ++p->help_history_index%=MAX_HELP_HISTORY; p->help_next_count--; char *keywords=p->help_history[p->help_history_index]; if(IS_NULLSTR(keywords)){ ++p->help_history_index%=MAX_HELP_HISTORY; ch->println("There are no more previous helps"); return; } // search for it pHelp=help_get_by_keyword(keywords, ch, true); if(!pHelp){ ch->printlnf("No help entry '%s' appears to exist anymore.",keywords); return; } // display it p->help_history_index= (p->help_history_index - 1 + MAX_HELP_HISTORY) % MAX_HELP_HISTORY; help_display_to_char(pHelp, ch); ++p->help_history_index%=MAX_HELP_HISTORY; } /**************************************************************************/ void do_help( char_data *ch, char *argument ) { help_data *pHelp; pc_data*p; p=ch?TRUE_CH(ch)->pcdata:NULL; if(!p){ ch->println("Players only sorry"); return; } if(IS_NULLSTR(argument)){ argument = "summary"; } // try exact match first pHelp=help_get_by_keyword(argument, ch, true); if(!pHelp){ // try substring match as fall back pHelp=help_get_by_keyword(argument, ch, false); } if(pHelp){ if(p->help_next_count){ p->help_history_index= (p->help_history_index + p->help_next_count)%MAX_HELP_HISTORY; p->help_next_count=0; } help_display_to_char(pHelp, ch); // record the last seen help entry - unless it is exactly like the current if(str_cmp(p->help_history[p->help_history_index], pHelp->keyword)){ ++p->help_history_index%=MAX_HELP_HISTORY; replace_string(p->help_history[p->help_history_index], pHelp->keyword); // wipe the next to prevent reverse looping replace_string(p->help_history[(p->help_history_index+1)%MAX_HELP_HISTORY],""); } return; } msp_to_room(MSPT_ACTION, MSP_SOUND_NOHELP, 0, ch, true, false ); ch->printlnf( "Sorry, no help on the keyword '%s' was found.", argument); ch->printlnf( "Try using 'helplist' with the first few letters of\r\n" "what you are looking for." ); // log missed help entry into NO_HELP_FILE append_datetime_ch_to_file( ch, NO_HELP_FILE, argument); char logbuf[MSL]; sprintf(logbuf, "no_help: %s<%d> found no help for '%s'\n", ch->name, ch->level, argument); wiznet(logbuf,ch,NULL,WIZ_NOHELP,0,get_trust(ch)); return; } /**************************************************************************/ // Count all the help entries in a particular category... Kal - Apr 01 int help_count_in_category( int category) { help_data *pHelp; int total=0; for ( pHelp = help_first; pHelp; pHelp = pHelp->next ){ if(pHelp->category==category){ total++; } } return total; } /**************************************************************************/ // Kal - Apr 01 void do_helpcat( char_data *ch, char *argument ) { int i; int max_undefined=UMAX(ch->lines, 20); int max_per_category=250; if(IS_NULLSTR(argument)){ // display all the categories ch->titlebar("HELP CATEGORIES"); for(i=0; !IS_NULLSTR(help_category_types[i].name); ){ ch->printf(FORMATF(" %%s%%-%ds", 22-str_len(help_category_types[i].name)), mxp_create_send(ch,FORMATF("helpcat %s", help_category_types[i].name), help_category_types[i].name), FORMATF("(%d)",help_count_in_category(i))); if(++i%3==0){ ch->println(""); } } if(i%3!=0){ ch->println(""); } ch->titlebar(""); return; } int index=flag_value(help_category_types, argument); if(index==NO_FLAG){ ch->printlnf("Couldn't find any '%s' help category", argument); return; } // display the content of one category help_data *pHelp; ch->titlebarf("HELP CATEGORIES: %s", flag_string(help_category_types, index)); i=0; for ( pHelp = help_first; pHelp; pHelp = pHelp->next ) { if(pHelp->category!=index){ continue; } if(!help_valid_for_char(pHelp, ch)){ continue; }; i++; if(i>max_per_category){ continue; } if(i>max_undefined && index==0){ continue; } ch->printf(" %s", IS_NULLSTR(pHelp->title)?"":pHelp->flags?"+":"*"); if(str_len(pHelp->keyword)>70){ ch->printlnf("`=_\"%-72.72s\"`x", FORMATF("%s",pHelp->keyword)); }else{ ch->printlnf("`=_%-72.72s`x", FORMATF("\"%s\"",pHelp->keyword)); } } ch->printf(" `B%s`x ", mxp_create_send(ch, "helpcat", "Show all categories")); if(i>max_undefined && index==0){ ch->printlnf(" `S%d undefined entries, displaying first %d.`x", i, max_undefined); }else if(i>max_per_category){ ch->printlnf(" `S%d entries, displaying first %d.`x", i, max_per_category); }else{ ch->printlnf(" `S%d entries displayed.`x", i); } ch->titlebarf("HELP CATEGORIES: %s", flag_string(help_category_types, index)); } /**************************************************************************/ void do_helplist( char_data *ch, char *argument ) { help_data *pHelp; char argall[MIL],argone[MIL]; char logbuf[MSL]; // no_help logging stuff int count=0; if ( IS_NULLSTR(argument)) { do_help(ch,"HELPLIST"); return; } // this parts handles help a b so that it returns help 'a b' argall[0] = '\0'; while (argument[0] != '\0' ) { argument = one_argument(argument,argone); if (argall[0] != '\0') strcat(argall," "); strcat(argall,argone); } for ( pHelp = help_first; pHelp; pHelp = pHelp->next ) { if ( is_name( argall, pHelp->keyword )) { if(!help_valid_for_char(pHelp, ch)){ continue; }; count++; if (IS_IMMORTAL(ch)){ ch->printlnf( "%s%3d)`x %s`x (length =%d bytes) <%s> [%d]%s", IS_SET(pHelp->flags,HELP_REMOVEHELP)?"`R***":"", count, mxp_create_send(ch, FORMATF("help %s", pHelp->keyword), pHelp->keyword), (int) str_len(pHelp->text), pHelp->helpfile->file_name, pHelp->level, IS_SET(pHelp->flags,HELP_REMOVEHELP)?" `RFLAGGED FOR REMOVAL`x":""); }else{ ch->printlnf( "%3d) %s`x", count, mxp_create_send(ch, FORMATF("help %s", pHelp->keyword), pHelp->keyword)); } } } if (count) // found { ch->println( "To access one of these help entries type help <number>.keyword" ); ch->println( "e.g. 'help 2.who'" ); } else // not found { ch->println( "No help entries found with that contain that keyword." ); // log missed help entry into NO_HELP_FILE append_datetime_ch_to_file( ch, NO_HELP_FILE, argall); msp_to_room(MSPT_ACTION, MSP_SOUND_NOHELP, 0, ch, true, false ); sprintf(logbuf, "nohlist: %s found no helplist for '%s'\n", ch->name, argall); wiznet(logbuf,ch,NULL,WIZ_NOHELP,0,get_trust(ch)); } return; } /**************************************************************************/ void do_testhelps( char_data *ch, char * ) { help_data *pHelp; bool found= false; char logbuf[MSL]; /* no_help logging stuff */ for ( pHelp = help_first; pHelp != NULL; pHelp = pHelp->next ) { if (str_len(pHelp->text)>8000) { sprintf(logbuf, "[%2d] %s - HELP ENTRY TOO LONG!!! (length =%ld bytes!) <%s>\r\n", pHelp->level, pHelp->keyword, (long) str_len(pHelp->text), pHelp->helpfile->file_name); ch->printf( "%s", logbuf ); /* log to long help entry into NO_HELP_FILE */ append_datetime_ch_to_file( ch, NO_HELP_FILE, logbuf); found = true; } } if (found) ch->println("Any entry longer than 10000 characters can't be viewed."); else ch->println("All help entries are less than 8000 characters."); return; } /**************************************************************************/ // Kal void do_hlist( char_data *ch, char *argument ) { helpfile_data *pHelpFD; help_data *pHelp; if (!HAS_SECURITY(ch,1)) { ch->println("The hlist command is an olc command, you dont have olc permissions."); return; } pHelpFD=helpfile_get_by_filename(argument); if (pHelpFD) { int count =0; ch->printf("`?`#-===[`YLVL`^]=<`Y LEN `^>(`YWL`^)=== " "`YHelp entries contained in %s `^================-`x\r\n", pHelpFD->file_name); // ch->printf("{`%s{x", // makef_titlebar("Help entries contained in %s", pHelpFD->file_name)); for ( pHelp = help_first; pHelp != NULL; pHelp = pHelp->next ) { if ( pHelp->helpfile==pHelpFD) { count++; if (IS_IMMORTAL(ch)) { ch->printlnf("%3d)[%3d]%s<%5d>(%s%2d`x) (%s)`=_%-56.56s`x", count, pHelp->level, mxp_create_send(ch,FORMATF("hedit %s", pHelp->keyword), "*"), (int) str_len(pHelp->text), (pHelp->widest_line_width<79?(IS_SET(pHelp->flags, HELP_WORDWRAPPED)?"`S":""):"`R"), pHelp->widest_line_width, mxp_create_send(ch,FORMATF("helpcat %s", flag_string(help_category_types,pHelp->category)), FORMATF("%-15.15s", flag_string(help_category_types,pHelp->category))), FORMATF("\"%s\"",pHelp->keyword) ); }else{ ch->printlnf("%3d) %s`x", count, pHelp->keyword); } } } if(HAS_MXP(ch)){ ch->println(mxp_create_send(ch, "hlist", "`S[list of hlist files]`x")); } } else { ch->titlebar("HLIST FILES"); int c=0; for ( pHelpFD = helpfile_first; pHelpFD; pHelpFD = pHelpFD->next ) { ch->printf(" [%2d] %-20s count=%s", pHelpFD->vnum, pHelpFD->file_name, mxp_create_send(ch, FORMATF("hlist %s", pHelpFD->file_name), FORMATF("%3d", pHelpFD->entries)) ); if(++c%2==0){ ch->println(""); }else{ ch->print(" "); } } if(c%2!=0){ ch->println(""); } ch->println("To see all the help entries in one of these files type `=Chlist <filename>`x."); } return; } /**************************************************************************/ // Version of help that does only exact matching, keywords are prefixed with // code_ and function returns true if it finds an exact matching help entry, // false if it doesnt find one - Kal Feb 99 bool codehelp( char_data *ch, char *keyword, int report_unfound_codehelp_flags) { help_data *pHelp; char lookfor[MIL]; char logbuf[MSL]; sprintf(lookfor,"code_%s", keyword); pHelp=help_get_by_keyword(lookfor, ch, true); if(pHelp){ help_display_to_char(pHelp, ch); return true; } // codehelp entry not found... pick up the pieces sprintf(logbuf, "no_codehelp: %s found no help for '%s'", ch->name, lookfor); if(IS_SET(report_unfound_codehelp_flags, CODEHELP_LOG)){ logf("%s", logbuf); } if(IS_SET(report_unfound_codehelp_flags, CODEHELP_NO_HELPFILE)){ append_datetime_ch_to_file( ch, NO_HELP_FILE, logbuf); } if( (IS_SET(report_unfound_codehelp_flags, CODEHELP_IMM) && IS_IMMORTAL(ch)) || (IS_SET(report_unfound_codehelp_flags, CODEHELP_ADMIN) && IS_ADMIN(ch)) || IS_SET(report_unfound_codehelp_flags, CODEHELP_EVERYONE)) { ch->printlnf("Error: codehelp() - no help found for '%s'", lookfor); ch->println("Please report this to the admin."); } if(IS_SET(report_unfound_codehelp_flags,CODEHELP_WIZNET)){ wiznet(logbuf,ch,NULL,WIZ_NOHELP,0,get_trust(ch)); } return false; } /**************************************************************************/ /**************************************************************************/