/**************************************************************************/
// 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;
}
/**************************************************************************/
/**************************************************************************/