dawn/notes/
dawn/src/
dawn/src/docs/
/**************************************************************************/
// langedit.cpp - olc based language editor, Kalahn
/***************************************************************************
 * 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 "olc.h"
#include "security.h"

extern language_data *languages;
/**************************************************************************/
bool langedit_show(char_data *ch, char *)
{
	language_data *lang;
	EDIT_LANGUAGE(ch,lang);
	SET_BIT(ch->dyn,DYN_SHOWFLAGS);

	ch->titlebarf("LANGUAGE EDIT%s %s", IS_SET(lang->flags, LANGFLAG_CHANGED)?"*":":", uppercase(lang->name));
  	ch->printlnf("`=rName:   `x%s    `=rSkillName: `x%s `=X%-9s   `=rCommandName: `x%s", 
		capitalize(lang->name), capitalize(lang->skillname), lang->gsn==-1?"UNFOUND!":"",
		capitalize(lang->commandname));	
	int flags=lang->flags;
	REMOVE_BIT(lang->flags, LANGFLAG_CHANGED);
	mxp_display_olc_flags_ex(ch, language_flags, lang->flags, "flags", "Flags:", 77, 8 , 8);	
	lang->flags=flags;
	
	ch->titlebar("What The Language Flags Do");
	ch->wrapln("no_scramble      `S- The language isn't scrambled.`x");
	ch->wrapln("no_holyspeech    `S- Holyspeech doesn't make everyone can understand, even if they dont' know the language.`x");
	ch->wrapln("no_order         `S- People can't be ordered to change to start speaking in this language.`x");
	ch->wrapln("reverse          `S- Output is backwards if not understood.`x");
	ch->wrapln("no_language_name `S- The name of the language isn't displayed.`x");
	ch->titlebar("BASE WORDMAP");

	{
		// create the wordlist so we can display it, will be deallocated later
		lang->words_recreate_list();

		wordmapping_data *word;
		int count=0;
		int extrawordcount=0;
		size_t len;
		size_t widestto=0;
		size_t widestfrom=0;
		// loop thru doing all single letter mappings
		for(word=lang->words; word; word=word->next){
			len=str_len(word->to);
			widestto=UMAX(len, widestto);
			len=str_len(word->from);
			widestfrom=UMAX(len, widestfrom);
			if(len==1){
				if(count%6!=0){
					ch->print(" `=t|`x");
				}else{
					ch->print(" ");
				}
				ch->printf(" %s -> %-5s", word->from, word->to);
				if(++count%6==0){
					ch->println("");
				}
			}else{
				extrawordcount++;
			}
		}
		if(count%6!=0){
			ch->println("");
		}	
		lang->words_deallocate_list();
		ch->println("  'recreatebase' can be used to automatically create a new base wordmap");
		ch->printlnf("  There are %d extra word%s mapped for this language, viewed with 'words'",	
			extrawordcount, extrawordcount==1?"":"s");
		ch->println("  Wordmappings can be manipulated using 'addword' and 'delword'");		
	}

	ch->titlebar("");
	REMOVE_BIT(ch->dyn,DYN_SHOWFLAGS);
    return false;
}
/**************************************************************************/
void language_recreatebase(language_data *lang)
{
	// create an array of all the letters in the alphabet
	char shuffle[26];
	int i;
	for(i=0; i<26; i++){
		shuffle[i]=i+'a';
	}
	// shuffle the array using a system of swapping
	for(i=0; i<25; i++){
		int pos=number_range(i+1, 25);
		char t=shuffle[i];
		shuffle[i]=shuffle[pos];
		shuffle[pos]=t;
	}

	// load the shuffled array as new words
	char from[2],to[2];
	from[1]='\0';
	to[1]='\0';
	for(i=0; i<26; i++){
		from[0]=i+'a';
		to[0]=shuffle[i];
		lang->add_wordmap_to_tree(from, to);
	}
}
/**************************************************************************/
bool langedit_recreatebase(char_data *ch, char *argument)
{	
	char confirm[MIL];
	argument=one_argument(argument, confirm);

	if(IS_NULLSTR(confirm) || str_cmp(confirm, "confirm")){
		ch->println("syntax: recreatebase confirm");
		return false;
	}

	language_data *lang;
	EDIT_LANGUAGE(ch,lang);
		
	language_recreatebase(lang);

	SET_BIT(lang->flags, LANGFLAG_CHANGED);
	langedit_show(ch, "");
	return true;
}

/**************************************************************************/
//	Entry Point for editing the languages
void do_langedit( char_data *ch, char *argument )
{
	int count=0;
	language_data *language;

	if(ch){
		if ( IS_NPC( ch )){
			ch->println("Players only.");
			return;
		}

		// do security checks
		if (!HAS_SECURITY(ch, 2)){
			ch->println( "You must have an olc security 2 or higher to use this command." );
			return;
		}

		// do security checks
		if (!HAS_SECURITY(ch, LANGEDIT_MINSECURITY))
		{
    		ch->printlnf("You must have an olc security %d or higher to use this command.",
				LANGEDIT_MINSECURITY);
			return;
		}

		if ( !IS_TRUSTED(ch, LANGEDIT_MINTRUST)) {
			ch->printlnf("You must have a trust of %d or above "
				"to use this command.", LANGEDIT_MINTRUST);
			return;
		}
	}

	if (IS_NULLSTR(argument)){		
		ch->titlebar("LANGEDIT");
		ch->println("syntax: langedit <language>");
		ch->println("syntax: langedit create <newlanguage>");
		ch->println("syntax: langedit list    - list the languages in a table");
		ch->println("This command takes you into an olc editor, to edit/create languages");
		ch->println("`x");
		ch->println("The following uneditable system languages exist:");
		for(language=languages; language; language=language->next){
			if(IS_SET(language->flags, LANGFLAG_SYSTEM_LANGUAGE)){
				ch->printf("    `S%-24s", language->name);
				if(++count%3==0){
					ch->print_blank_lines(1);
				}
			}
		}
		
		if(count%3!=0){
			ch->print_blank_lines(1);
		}
		count=0;
		ch->println("`x");
		ch->println("The following existing languages can be edited:");
		for(language=languages; language; language=language->next){
			if(!IS_SET(language->flags, LANGFLAG_SYSTEM_LANGUAGE)){
				ch->printf("    `=C%s", mxp_create_send(ch,FORMATF("langedit %s", language->name), FORMATF("%-24s", language->name)));
				if(++count%3==0){
					ch->print_blank_lines(1);
				}
			}
		}
		if(count%3!=0){
			ch->print_blank_lines(1);
		}
		ch->titlebar("");
		return;
	}
	
	char arg[MIL];
	argument=one_argument(argument,arg);

	if(!str_cmp(arg,"create")){
		argument=one_argument(argument,arg);
		if(IS_NULLSTR(arg)){
			ch->println("You must specify the name of the language you wish to create.");
			return;
		}

		// Allow only letters, numbers and underscore.
		int length=str_len(arg);
		for(int i=0; i<length; i++){
			if(!is_alnum(arg[i]) && arg[i]!='_'){
				ch->println( "Only letters, the underscore character '_' and numbers are valid in a language name." );
				return;
			}
		}

		// check if there is an existing language with matching name
		language=language_exact_lookup(arg);
		if(language){
			ch->printlnf("There is already a language named '%s'", language->name);
			return;
		}
		language=new language_data;
		language->name=str_dup(lowercase(arg));
		language->flags=0;
		if(ch){
			language->skillname=str_dup("");
		}else{
			language->skillname=str_dup(language->name);
		}
		language->gsn=skill_exact_lookup(language->skillname);
		language->commandname=str_dup("-");
		language->words=NULL;
		language->initialise_tree();

		// add new language to the front of the list
		language->next=languages;		
		languages=language;
		language_recreatebase(language);

		if(ch){
			ch->printlnf("Language '%s' created", arg);
			ch->desc->pEdit	= (void*)language;
			ch->desc->editor = ED_LANGUAGE;
			langedit_show(ch, "");		
		}
		return;
	}

	if(!str_cmp(arg,"list")){
		for(language=languages; language; language=language->next){
			if(IS_SET(language->flags, LANGFLAG_SYSTEM_LANGUAGE)){
  				ch->printlnf("`=$Name: `x%-18s `=$SkillName: `x%-18s `=$CommandName: `x%s", 
					capitalize(language->name), capitalize(language->skillname), 
					capitalize(language->commandname));				
			}else{
  				ch->printlnf("`=rName: `x%-18s `=rSkillName: `x%-18s `=rCommandName: `x%s", 
					mxp_create_send(ch,FORMATF("langedit %s", language->name), FORMATF("%-18s", capitalize(language->name))),
					capitalize(language->skillname), 
					capitalize(language->commandname));	
			}
		}
		return;
	}

	// find an existing social
	language=language_lookup(arg);
	if(!language){
		ch->printlnf("There is no language named '%s'", arg);
		do_langedit(ch, "");
		return;
	}

	// ensure it isn't a system language
	if(IS_SET(language->flags, LANGFLAG_SYSTEM_LANGUAGE)){
		ch->printlnf("'%s' is a system language, therefore can not be edited.", language->name);
		return;
	}

    ch->desc->pEdit	= (void*)language;
	ch->desc->editor = ED_LANGUAGE;
	ch->printlnf("Editing '%s' language.", language->name);
	langedit_show(ch, "");
	return;
}

/**************************************************************************/
bool langedit_rename(char_data *ch, char *argument)
{
	char arg[MIL];
	argument=one_argument(argument,arg);

    if ( IS_NULLSTR(arg))
    {
		ch->println( "Syntax: rename <string>");
		ch->println( "Rename the language.");
		return false;
    }

	// Allow only letters, numbers and underscore.
	int length=str_len(arg);
	for(int i=0; i<length; i++){
		if(!is_alnum(arg[i]) && arg[i]!='_'){
			ch->println( "Only letters, the underscore character '_' and numbers are valid in a language name." );
			return false;
		}
	}

	language_data *lang;

	// check if there is an existing language with matching name
	lang=language_exact_lookup(arg);
	if(lang){
		ch->printlnf("There is already a language named '%s'", lang->name);
		return false;
	}

	EDIT_LANGUAGE(ch,lang);
    ch->printlnf("Language name changed from '%s' to '%s'.", 
		lang->name, lowercase(arg) );

    replace_string( lang->name, lowercase(arg) );
    return true;
}
/**************************************************************************/
bool langedit_skillname(char_data *ch, char *argument)
{
	char arg[MIL];
	argument=one_argument(argument,arg);

    if ( IS_NULLSTR(arg))
    {
		ch->println( "Syntax: skillname <string>");
		ch->println( "Set/change the skillname of the language.");
		return false;
    }

	language_data *lang;
	EDIT_LANGUAGE(ch,lang);

	// find the skill first
	int sn=skill_exact_lookup(arg);
	if(sn<0){
		ch->printlnf("There is no skill called '%s'", arg);
		return false;
	}
	if(lang->gsn==sn){
		ch->printlnf("Language skillname is already set to %s", lang->skillname);
	}

    ch->printlnf("Language skillname changed from '%s' to '%s'.", 
		lang->skillname, lowercase(arg) );

    replace_string( lang->skillname, lowercase(arg) );
	lang->gsn=sn;
    return true;
}
/**************************************************************************/
bool langedit_commandname(char_data *ch, char *argument)
{
	char arg[MIL];
	argument=one_argument(argument,arg);

    if ( IS_NULLSTR(arg))
    {
		ch->println( "Syntax: commandname <string>");
		ch->println( "Syntax: commandname -         (to clear the command name)");
		ch->println( "Set/change the commandname of the language.");
		ch->println( "Note: if this is blank, the language can't be accessed as a command.");
		return false;
    }

	language_data *lang;
	EDIT_LANGUAGE(ch,lang);

	if(!str_cmp(arg, "-")){
		ch->println("Commandname cleared.");
		replace_string(lang->commandname,"-");
		return true;
	}

    ch->printlnf("Language commandname changed from '%s' to '%s'.", 
		lang->commandname, lowercase(arg) );

    replace_string( lang->commandname, lowercase(arg) );
    return true;
}
/**************************************************************************/
bool langedit_flags(char_data *ch, char *argument)
{
	language_data *lang;
	EDIT_LANGUAGE(ch,lang);
	return olc_generic_flag_toggle(ch, argument,
				"flags", "flags", language_flags, &lang->flags);
}
/**************************************************************************/
// this is inefficient, but the code will very rarely be used
bool langedit_words(char_data *ch, char *argument)
{
	language_data *lang;
	EDIT_LANGUAGE(ch,lang);

	char buf[MSL];
	BUFFER *output;
	output	= new_buf();
	
	// create the wordlist so we can display it, will be deallocated later
	lang->words_recreate_list();

	wordmapping_data *word;
	int count=0;
	int len;
	int widestto=0;
	int widestfrom=0;
	// loop thru doing all single letter mappings
	add_buf( output, makef_titlebar(FORMATF("%s WORD MAPPINGS", uppercase(lang->name))));
	for(word=lang->words; word; word=word->next){
		len=str_len(word->to);
		widestto=UMAX(len, widestto);
		len=str_len(word->from);
		widestfrom=UMAX(len, widestfrom);
	}

	count=0;
	for(word=lang->words; word; word=word->next){
		if(!IS_NULLSTR(argument)){ // support infix word matching
			if(str_infix(argument, word->from) 
				&& str_infix(argument, word->to)){
				continue;
			}
		}

		if(str_len(word->from)!=1){
			if(count%3!=0){
				add_buf( output, " `=t|`x");
			}else{
				add_buf( output, " ");
			}
			sprintf(buf, 
				FORMATF(" %%-%ds -> %%-%ds", widestfrom, widestto)				
				,word->from, word->to);
			if(++count%3==0){
				strcat(buf, "\r\n");
			}
			add_buf( output, buf);
		}
	}
	if(count){
		if(count%3!=0){
			add_buf( output, "\r\n");
		}
	}
	add_buf( output, "Add/change word mappings using the addword command\r\n");
	add_buf( output, "You can delete words with delword and infix search this list.\r\n");
	add_buf( output, makef_titlebar("="));
	
	ch->sendpage(buf_string(output));
	free_buf(output);

	lang->words_deallocate_list();
    return false;	
}
/**************************************************************************/
bool langedit_addword(char_data *ch, char *argument)
{
	char from[MIL];
	char to[MIL];
	argument=one_argument(argument, from);
	argument=one_argument(argument, to);

	if(IS_NULLSTR(to)){
		ch->println("syntax: addword <word_from> <word_to>");
		return false;
	}
	if(str_len(from)==1){
		if(!is_alnum(*from)){
			ch->println("You can't add single character 'words' other than letters and numbers");
			return false;
		}
	}

	language_data *lang;
	EDIT_LANGUAGE(ch,lang);
	languagetree_data *tail;
	// look for an existing word first
	int length=lang->find(from, &tail);

	if(length==str_len(from)){
		if(!str_cmp(to, tail->stored_word)){
			ch->printlnf("No changed required, '%s' is already mapped to '%s'.", 
				from, to);		
			return false;
		}
		ch->printlnf("Modified mapping '%s'->'%s' to '%s'->'%s'", 
			from, tail->stored_word, from, to);		
		lang->add_wordmap_to_tree(from, to);
	}else{
		ch->printlnf("Added new word mapping to '%s'->'%s'", from, to);
		lang->add_wordmap_to_tree(from, to);
	}
	return true;
}
/**************************************************************************/
bool langedit_delword(char_data *ch, char *argument)
{	
	char delword[MIL];
	argument=one_argument(argument, delword);

	if(IS_NULLSTR(delword)){
		ch->println("syntax: delword <word_to_delete>");
		return false;
	}

	if(str_len(delword)==1 && is_alpha(*delword) ){
		ch->wrapln("You can't delete single character 'words' that are letters, "
			"only change them with addword.  To blank a single character "
			"wordmapping use \"addword <word> ''\"");
		return false;
	}

	language_data *lang;
	EDIT_LANGUAGE(ch,lang);
	languagetree_data *tail;
	// first check the delword exists, before trying to remove it
	int length=lang->find(delword, &tail);

	if(length!=str_len(delword)){
		ch->printlnf("Couldn't find any word '%s' to remove.", delword);
		return false;
	}

	// create the wordlist so we can pull it out then reinitialise the tree
	lang->words_recreate_list();

	wordmapping_data *word, *prev;
	prev=NULL;
	bool found=false;
	for(word=lang->words; !found && word; word=word->next){
		if(!str_cmp(delword, word->from)){
			if(prev){
				prev->next=word->next;
			}else{
				// we are the head of the list
				lang->words=word->next;
			}
			// deallocate the memory used by the deleted node
			free_string(word->from);
			free_string(word->to);
			word->next=NULL;
			delete word;

			found=true;
			// we don't have to worry about words_last here, as we call words_deallocate_list() below
		}
		prev=word;
	}

	// it has to be found, because lang->find() matched the word
	assert(found);

	// reinitialise the tree from the wordmap list which has 
	// just had the word we are deleting removed from it
	lang->reinitialise_tree();

	// return the memory
	lang->words_deallocate_list();

	ch->printlnf("Word mapping for '%s' has been removed.", delword);
	return true;
}
/**************************************************************************/
/**************************************************************************/