dawn/notes/
dawn/src/
dawn/src/docs/
/**************************************************************************/
// grpedit.cpp - olc based skillgroup editing
/***************************************************************************
 * 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"
/**************************************************************************/
// prototypes
const char *pack_word(const char *word); // areas.cpp
/**************************************************************************/
const struct flag_type skillgroup_flags[] =
{
	{ "creation_selectable",		SKILLGROUP_CREATION_SELECTABLE,		true},
	{ "free_for_all",				SKILLGROUP_FREE_FOR_ALL,			true},
	{ NULL,0,0} // table terminator
};

/**************************************************************************/
/**************************************************************************/
// generic IO setup

// custom read/write functions
/**************************************************************************/
// write the class rating for each class listed in the skillgroup table
void skillgroup_WriteClassRatings(gio_type *gio_table, int tableIndex, void *data, FILE *fp)
{
	int classIndex, value;
	skillgroup_type * pG= (skillgroup_type *) data;

	fprintf(fp, "%s\n", gio_table[tableIndex].heading);
	for (classIndex=0; !IS_NULLSTR(class_table[classIndex].name); classIndex++){
		// write only valid values
		if (pG->rating[classIndex]<0 || pG->rating[classIndex]>50){
			value=-1;
		}else{
			value=pG->rating[classIndex];
		}
		if(value!=-1){
			fprintf(fp, "\t%s %d\n", class_table[classIndex].name, value);
		}
	};
	fprintf(fp, "~\n");
}

/**************************************************************************/
// read the class ratings for each class listed in the group table
void skillgroup_ReadClassRatings(gio_type *, int, void *data, FILE *fp)
{
	int classIndex;
	skillgroup_type * pG= (skillgroup_type *) data;

	char className[MIL];
	int rating;

	// initialise all values to -1 (no classes have access to the skill group)
	for (classIndex=0; !IS_NULLSTR(class_table[classIndex].name); classIndex++){
		pG->rating[classIndex]=-1;
	}

	while (true){
		// get the class name and rating
		fscanf(fp, "%s ", className);
		if (className[0]=='~' && className[1]=='\0'){
			break;
		}

		// read in the rating
		fscanf(fp, "%d ", &rating);

		classIndex =class_lookup(className);

		if (classIndex<0){
			bugf( "group_ReadClassRatings: class '%s' not found... "
				"ignoring rating for that class.", className);
		}else{
			pG->rating[classIndex]=rating;
		}
	}
}

/**************************************************************************/
// write all the skills in the skillgroup to disk
void skillgroup_WriteSkills(gio_type *gio_table, int tableIndex, void *data, FILE *fp)
{
	int i;
	skillgroup_type * pG;
	
	pG= (skillgroup_type *) data;

	fprintf(fp, "%s\n", gio_table[tableIndex].heading);

	// write each skill out
	for (i = 0; pG->skills[i]>-1; i++){
		fprintf(fp, "\t%s\n", pack_word(skill_table[pG->skills[i]].name));
	}
	fprintf(fp, "~\n");
}
/**************************************************************************/
// write all the skills in the skillgroup to disk
void skillgroup_WriteSkillsWithValues(gio_type *gio_table, int tableIndex, void *data, FILE *fp)
{
	int i;
	skillgroup_type * pG;
	
	pG= (skillgroup_type *) data;

	fprintf(fp, "%s\n", gio_table[tableIndex].heading);

	// write each skill out
	for (i = 0; pG->skills[i]>-1; i++){
		fprintf(fp, "\t%s %d\n", 
			pack_word(skill_table[pG->skills[i]].name),
			pG->skillsvalue[i]);
	}
	fprintf(fp, "~\n");
}
/**************************************************************************/
// read all the skills from disk into a skillgroup
void skillgroup_ReadSkills(gio_type *, int, void *data, FILE *fp)
{
	skillgroup_type * pG= (skillgroup_type *) data;
	int sn;
	int sindex=0;
	char *readword;

	// initialise all skills to -1 (no skills in the skillgroup)
	memset(pG->skills, -1, sizeof(pG->skills));

	while (true){
		// get the skill name
		readword=fread_word(fp);
		if (readword[0]=='~' && readword[1]=='\0'){
			break;
		}

		sn=skill_exact_lookup(readword);

		if (sn<0){
			bugf( "group_ReadSkills: skill '%s' not found... ignoring skill for skillgroup '%s'.", 
				readword, pG->name);
		}else{
			if(sindex>=MAX_IN_GROUP){
				bugf("group_ReadSkills(): Too many skills (%d) to read into skillgroup '%s' "
					"- increase MAX_IN_GROUP.", sindex, pG->name);
				do_abort();
			}
			pG->skills[sindex]=sn;
			pG->skillsvalue[sindex]=1; // 1% is the default
			sindex++;
		}
	}
}

/**************************************************************************/
// read all the skills from disk into a skillgroup
void skillgroup_ReadSkillsWithValues(gio_type *, int, void *data, FILE *fp)
{
	skillgroup_type * pG= (skillgroup_type *) data;
	int sn;
	int sindex=0;
	char *readword;

	// initialise all skills to -1 (no skills in the skillgroup)
	memset(pG->skills, -1, sizeof(pG->skills));

	while (true){
		int value;
		// get the skill name
		readword=fread_word(fp);
		if (readword[0]=='~' && readword[1]=='\0'){
			break;
		}
		value=fread_number(fp);

		sn=skill_exact_lookup(readword);

		if (sn<0){
			bugf( "group_ReadSkills: skill '%s' not found... ignoring skill for skillgroup '%s'.", 
				readword, pG->name);
		}else{
			if(sindex>=MAX_IN_GROUP){
				bugf("group_ReadSkills(): Too many skills (%d) to read into skillgroup '%s' "
					"- increase MAX_IN_GROUP.", sindex, pG->name);
				do_abort();
			}
			pG->skills[sindex]=sn;
			pG->skillsvalue[sindex]=value;
			sindex++;
		}
	}
}

/**************************************************************************/
// GIO structure
GIO_START(skillgroup_type)
	GIO_INT(remort)
	GIO_WFLAGH(flags,		"flags",			skillgroup_flags)
	GIO_CUSTOM_WRITEH(name,	"skillswithvalues",	skillgroup_WriteSkillsWithValues)
	GIO_CUSTOM_READH(name,	"skillswithvalues",	skillgroup_ReadSkillsWithValues)
	GIO_CUSTOM_READH(name,	"skills",			skillgroup_ReadSkills)
	GIO_CUSTOM_WRITEH(name,	"class_ratings",	skillgroup_WriteClassRatings)
	GIO_CUSTOM_READH(name,	"class_ratings",	skillgroup_ReadClassRatings)	
GIO_FINISH_NOCLEAR	

/**************************************************************************/
// Output the skillgroup_table[] to disk 
void do_write_skillgroups(char_data *ch, char *)
{
	FILE *fp;
	int i;

	logf("Writing skillgroup_table[] to " SKILLGROUPS_FILE ".write ...");
	fclose( fpReserve );

    if ( ( fp = fopen( SKILLGROUPS_FILE ".write", "w" ) ) == NULL )
    {
		bugf("do_write_skillgroups(): fopen '%s' for write - error %d (%s)",
			SKILLGROUPS_FILE ".write", errno, strerror( errno));
		bug("An error occured opening " SKILLGROUPS_FILE ".write for writing!");
		ch->println("An error occured opening " SKILLGROUPS_FILE ".write for writing!");
		autonote(NOTE_SNOTE, "do_write_skillgroups()", "Problems saving skillgroups table", "code cc: imm", 
			"An error occured opening " SKILLGROUPS_FILE ".write for writing!\r\n", true);
    }else{
		// LOOP thru everything in the table, writing it
		for ( i = 0; !IS_NULLSTR(skillgroup_table[i].name); i++ )
		{
			fprintf(fp,"######SKILLGROUP    %s~\n", skillgroup_table[i].name);
			GIO_SAVE_RECORD(skillgroup_type, &skillgroup_table[i], fp, NULL);
			fprintf(fp,"\n");
		}

		int bytes_written=fprintf(fp, "EOF~\n");
		fclose( fp );
		if(   bytes_written != str_len("EOF~\n") ){
			bugf("do_write_skillgroups(): fprint to '%s' incomplete - error %d (%s)",
				SKILLGROUPS_FILE ".write", errno, strerror( errno));
			bugf("Incomplete write of " SKILLGROUPS_FILE ".write, write aborted - check diskspace!");
			ch->printf("Incomplete write of " SKILLGROUPS_FILE ".write, write aborted - check diskspace!\r\n");
			autonote(NOTE_SNOTE, "do_write_skillgroups()", "Problems saving skillgroup_table[]", "code cc: imm", 
				"Incomplete write of " SKILLGROUPS_FILE ".write, write aborted - check diskspace!\r\n", true);
		}else{		
			ch->printf("Finished writing skillgroup_table[] to "SKILLGROUPS_FILE ".write\r\n");

			ch->printf("Renaming old " SKILLGROUPS_FILE " to " SKILLGROUPS_FILE ".bak\r\n");
			unlink(SKILLGROUPS_FILE".bak");
			rename(SKILLGROUPS_FILE, SKILLGROUPS_FILE".bak");

			ch->printf("Renaming new " SKILLGROUPS_FILE ".write to " SKILLGROUPS_FILE "\r\n");
			unlink(SKILLGROUPS_FILE);
			rename(SKILLGROUPS_FILE".write", SKILLGROUPS_FILE);
		}
    }
	fpReserve = fopen( NULL_FILE, "r" );
	logf("Finished writing skillgroup_table[].");
}

/**************************************************************************/
extern	struct	group_type	group_table	[MAX_GROUP_TABLE+1];
/**************************************************************************/
// convert group_table[] into skillgroup_table[]
void convert_group_table()
{
	int i, gn;
	int class_total_ratings[MAX_CLASS];

	memset(class_total_ratings, 0, sizeof(class_total_ratings));
	memset(skillgroup_table, 0, sizeof(skillgroup_table));

    for( gn = 0; !IS_NULLSTR(group_table[gn].oldname); gn++ )
    {
		memset(skillgroup_table[gn].skills, -1, sizeof(skillgroup_table[gn].skills));
		memset(skillgroup_table[gn].rating, -1, sizeof(skillgroup_table[gn].rating));
		// copy the name across
		skillgroup_table[gn].name=str_dup(group_table[gn].oldname);

		for ( i = 0; !IS_NULLSTR(group_table[gn].oldspells[i]); i++){
			skillgroup_table[gn].skills[i]=skill_lookup(group_table[gn].oldspells[i]);
		}

		// loop thru, transfering all class ratings, if all are 0, then set the free_for_all flag
		bool all_free=true;
		for(i=0; !IS_NULLSTR(class_table[i].name); i++){
			skillgroup_table[gn].rating[i]=group_table[gn].oldrating[i];
			if(skillgroup_table[gn].rating[i]!=0){
				all_free=false;
			}
			// record the combined total ratings for each class
			class_total_ratings[i]+=skillgroup_table[gn].rating[i]; 
		}
		if(all_free){
			SET_BIT(skillgroup_table[gn].flags, SKILLGROUP_FREE_FOR_ALL);
		}
		SET_BIT(skillgroup_table[gn].flags, SKILLGROUP_CREATION_SELECTABLE);
	}

	// for all classes with a combined total rating 0 (i.e. everything free)
	// set to -1 except on those which are free_for_all
	for(i=0; !IS_NULLSTR(class_table[i].name); i++){
		if(class_total_ratings[i]==0){
			for( gn = 0; !IS_NULLSTR(skillgroup_table[gn].name); gn++ )
			{
				if(!IS_SET(skillgroup_table[gn].flags, SKILLGROUP_FREE_FOR_ALL)){
					skillgroup_table[gn].rating[i]=-1;
				}
			}
		}

	}
	
}

/**************************************************************************/
// read in the skillgroup_table from disk
void do_read_skillgroups(char_data *ch, char *)
{
	FILE *fp;
	int count=0;

	// because system doesn't support reassigning group numbers, only allow 
	// this to run at bootup and when one imm is on
	if(runlevel!=RUNLEVEL_BOOTING){
		ch->println("do_read_skillgroups() currently doesn't support being called a game time... boot only!");
		ch->println("Doing so in its current state might stuff up everyones skill ");
		ch->println("groups... BASICALLY DONT DO IT!");
		return;
	}

	logf("Reading in skillgroup_table[] from " SKILLGROUPS_FILE "...");
	fclose( fpReserve );

    if ( ( fp = fopen( SKILLGROUPS_FILE, "r" ) ) == NULL )
    {	// couldn't find the file to read in
		bugf("do_read_skillgroups(): fopen '%s' for read - error %d (%s)",
			SKILLGROUPS_FILE , errno, strerror( errno));
		bugf("An error occured trying to open " SKILLGROUPS_FILE " for reading!");
		ch->printf("An error occured trying to open " SKILLGROUPS_FILE " for reading!\r\n");
		if(file_exists(SKILLGROUPS_FILE )){
			bugf("File " SKILLGROUPS_FILE "found, but it wasnt openned for some reason?!?");
			write_shutdown_file(NULL);
			do_abort();
		}else{
			ch->println("Converting old skillgroup_table[] and creating a new " SKILLGROUPS_FILE " file.");
			logf("Converting old skillgroup_table[] and creating a new " SKILLGROUPS_FILE " file.");
			convert_group_table();
			fpReserve = fopen( NULL_FILE, "r" );
			do_write_skillgroups(NULL,"");
		}
		return;
    }
	
	// SKILLGROUPS_FILE was found, and sucessfully opened for input
	bool morefile=true;
	char *readword;
	
	if(runlevel==RUNLEVEL_BOOTING){
		// *** Mark the existing table as empty - since we are loading everything in
		// - This could cause skillgroup corruption if done when people are online...
		//   that is why it can be only done if one is online or during booting.
		memset(skillgroup_table, 0, sizeof(skillgroup_table));
	}

	// loop thru till we get the end of the table
	while (morefile && !feof(fp)) {
		int load_index;
		readword= fread_word(fp);

		if (!str_cmp(readword, "EOF") || !str_cmp(readword, "EOF~")){
			morefile=false;
		}else{
			if(str_cmp(readword, "######SKILLGROUP")){
				// unexpected file format
				bugf("Unexpected fileformat in '%s' - found '%s' "
					"expecting '######SKILLGROUP'", SKILLGROUPS_FILE , readword);
				ch->printf( "Unexpected fileformat in '%s' - found '%s' "
					"expecting '######SKILLGROUP'\r\n", SKILLGROUPS_FILE , readword);
				flush_char_outbuffer(ch);
				write_shutdown_file(NULL);
				do_abort();
				return;
			}

			// get the name of the skillgroup
			readword=fread_string(fp);

			// check if the skillgroup hasn't already been loaded - no duplicates
			load_index=skillgroup_exact_lookup(readword);
				
			if(load_index>-1){ // we have already loaded a skill group with that name!?!
				bugf("do_read_skillgroups(): skillgroup '%s' has already been loaded!", readword);
				do_abort();
			}

			// we have a new skill group to read in... allocate space for it

			for ( load_index=0; !IS_NULLSTR(skillgroup_table[load_index].name); load_index++){
				// do nothing till we find the end
			}
			//load_index now points to the end of the list
			if(load_index>=MAX_SKILLGROUP){
				bugf("Too many skillgroups (%d) trying to load skillgroup '%s' from %s, increase MAX_SKILLGROUP - aborting",
						load_index, readword, SKILLGROUPS_FILE);
				write_shutdown_file(NULL);
				exit_error( 1 , "do_read_skillgroups", "Too many skill groups");
			}

			// fill in the defaults for our entry
			memset(skillgroup_table[load_index].skills, -1, sizeof(skillgroup_table[0].skills));
			memset(skillgroup_table[load_index].rating, -1, sizeof(skillgroup_table[0].rating));
			skillgroup_table[load_index].name=str_dup(readword);

			// mark the new end of the table
			skillgroup_table[load_index+1].name=NULL; 

			// read in the data
			GIO_LOAD_RECORD(skillgroup_type, &skillgroup_table[load_index], fp);
			count++;
		}
	}
	fclose( fp );

	ch->printf("Finished reading skillgroup_table from " SKILLGROUPS_FILE  ". (read in %d)\r\n", count);
	logf("Finished reading skillgroup_table from " SKILLGROUPS_FILE ". (read in %d)", count);

	fpReserve = fopen( NULL_FILE, "r" );
	return;
}

/**************************************************************************/
//	Entry Point for editing the skill groups
void do_skillgroupedit( char_data *ch, char *argument )
{
	char arg[MIL];
	if(!str_infix("create", argument)){		
		argument=one_argument(argument, arg);
	}else{
		strcpy(arg, argument);
	}

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

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

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

	if (IS_NULLSTR(arg)){
		ch->println("Syntax: `=Cskgrpedit create <newskillgroup>`x");
		ch->println("    or: `=Cskgrpedit <skillgroup>`x");
		ch->println("Where <skillgroup> is one of the following:");
		
		ch->println(" [remort number]<number of skills in group> group name");
		// groups up below
		int col=0;
		for (int gn = 0; !IS_NULLSTR(skillgroup_table[gn].name); gn++)
		{
			// count the total skills in a group
			int skcnt =0;			
			for ( int skcnti= 0; skillgroup_table[gn].skills[skcnti]>-1; skcnti++) {
				skcnt++;
			}

			// display the group
			ch->printf("`%c  [%d]%2d %-33s `x", 
				IS_SET(skillgroup_table[gn].flags, SKILLGROUP_CREATION_SELECTABLE)?
					(IS_SET(skillgroup_table[gn].flags, SKILLGROUP_FREE_FOR_ALL)?'G':'x')
					:IS_SET(skillgroup_table[gn].flags, SKILLGROUP_FREE_FOR_ALL)?'g':'S',
				skillgroup_table[gn].remort,skcnt,
				skillgroup_table[gn].name);
			if (++col % 2 == 0){
				ch->println("");
			}
		}
		if(col % 2 != 0){
			ch->println("");
		}
		ch->println("NumberPrefix = remort, Colour coding flag guide: CS=creation selectable, FFA = free for all");
		ch->println("`Gbright green= CS,FFA  `gdark green = FFA   `xwhite=CS  `Ssilver=both flags unset.`x");
		ch->println("CS=creation selectable, FFA = free for all (everyone can get it for free).");
		return;
	}

	if ( !str_cmp( arg, "create" ))
	{
		// allocate space for a new skillgroup
		int gn;

		for ( gn=0; !IS_NULLSTR(skillgroup_table[gn].name); gn++){
			// do nothing till we find the end
		}
		//load_index now points to the end of the list
		if(gn>=MAX_SKILLGROUP){
			ch->printlnf("There are too many skillgroups (%d), increase MAX_SKILLGROUP if you want to add more",
				gn);
			return;
		}

		// fill in the defaults for our entry
		memset(skillgroup_table[gn].skills, -1, sizeof(skillgroup_table[0].skills));
		memset(skillgroup_table[gn].rating, -1, sizeof(skillgroup_table[0].rating));
		skillgroup_table[gn].name=str_dup(argument);

		// mark the new end of the table
		skillgroup_table[gn+1].name=NULL; 

		// update them on what is happening
		ch->wraplnf( "`xCreated skillgroup '%s'",argument);
		ch->println( "Type `=Cdone`x to finish editing." );
		ch->desc->pEdit	= (void*)&skillgroup_table[gn];
		ch->desc->editor = ED_SKILLGROUP;
		return;
	}

	int skillgroupindex=skillgroup_lookup(arg);
	if(skillgroupindex<0){
		ch->printlnf("Could not find a skillgroup '%s' to edit.", arg);
		return;
	}
    ch->desc->pEdit	= (void*)&skillgroup_table[skillgroupindex];
	ch->desc->editor = ED_SKILLGROUP;
	ch->printlnf("Editing skillgroup '%s' with skillgroup editor.", 
		skillgroup_table[skillgroupindex].name);
	return;
}
/**************************************************************************/
// returns a negative amount if 
int skillgroupedit_calc_individual_cost(skillgroup_type * pG, int classindex)
{
	int total=0;	
	bool unattainable_skill=false;
    for ( int i = 0; pG->skills[i]>-1; i++)
    {
		if(IS_SKILL_VALID_FOR_CLASS(pG->skills[i], classindex)){
			total+=skill_table[pG->skills[i]].rating[classindex];
		}else{
			unattainable_skill=true;
		}
    }
	if(unattainable_skill){
		return (total+1)*-1;
	}else{
		return total;
	}
}
/**************************************************************************/
bool skillgroupedit_show(char_data *ch, char *argument)
{
	int i, count, showclass, classcost;
	char skillcolour;
	bool unused_skills=false;

	skillgroup_type * pG;
	EDIT_SKILLGROUP(ch, pG);
	SET_BIT(ch->dyn,DYN_SHOWFLAGS);

	ch->titlebarf("SKILLGROUPEDIT: %s", uppercase(pG->name));
  	ch->printlnf( "`=rSkillgroup Name: `x%-10s       `=rRemort: `x%d ", 
		capitalize(pG->name), pG->remort);
	mxp_display_olc_flags(ch, skillgroup_flags, pG->flags,	"flags", "Flags:");

	// display the skills in the group, first count them then display
	count =0;
    for ( i = 0; pG->skills[i]>-1; i++) {
		count++;
	}

	showclass=class_lookup(argument);
	if(showclass<0){
		skillcolour='x';
		ch->titlebarf("SKILLS IN GROUP - %d TOTAL", count);
	}else{
		ch->titlebarf("SKILLS IN GROUP - %d TOTAL (CLASS FILTER=%s)", 
			count, uppercase(class_table[showclass].name));
	}
    for ( i = 0; pG->skills[i]>-1; i++)
    {
		if(showclass>-1 && !IS_SKILL_VALID_FOR_CLASS(pG->skills[i], showclass)){
				skillcolour='R';
		}else{
				skillcolour='x';
		}

		if(i%3==2){
			ch->printlnf("   `%c%s`x", skillcolour, 
				FORMATF("%s [%d%%]", skill_table[pG->skills[i]].name, pG->skillsvalue[i]));
		}else{
			ch->printf("   `%c%-27s`x", skillcolour, 
				FORMATF("%s [%d%%]", skill_table[pG->skills[i]].name, pG->skillsvalue[i]));
		}
    }
	if(i%3!=0){
		ch->println("");
	}

	// display the cost in trains for each class
	ch->titlebar("COST (TRAINS)");
	for(i=0; !IS_NULLSTR(class_table[i].name); i++){
		// the free for all flag overwrites the cost
		if(IS_SET(pG->flags, SKILLGROUP_FREE_FOR_ALL)){
			classcost=0;
		}else{
			classcost=pG->rating[i];
		}

		if(classcost<0){
			ch->printf(" %-23s `S------`x", class_table[i].name);
		}else{
			count=skillgroupedit_calc_individual_cost(pG, i);

			if(count<0){
				ch->printf(" %-23s `%c%2d`x(`R%2d`x)", 
					class_table[i].name, 
					(classcost<1?'G':'x'),
					pG->rating[i], 
					(count*-1)-1);
				unused_skills=true;
			}else{
				ch->printf(" %-23s `%c%2d`x(%2d)", 
					class_table[i].name, 
					(classcost<1?'G':'x'),
					pG->rating[i], 
					skillgroupedit_calc_individual_cost(pG, i));
			}
		}
		if(i%2==0){
			ch->print("              ");
		}else{
			ch->println("");
		}
	}
	ch->println("");
	if(i%2!=0){
		ch->println("");
	}
	ch->wrapln("The number before the brackets after a class name, is how much the group "
		"costs for that class to gain/add.  The number in brackets is the total of how "
		"much each individual skill costs for that class.");
	if(IS_SET(pG->flags, SKILLGROUP_FREE_FOR_ALL)){
		ch->println("`S   note: because 'free_for_all' is set, all classes can get this for 0 trains.");
		ch->println("         and will automatically get it during creation (hence green on all)`x.");
	}else{
		ch->print_blank_lines(2);
	}
	if(unused_skills && showclass==-1){
		  //        "-===========================================================================-"
		ch->println("`S   Classes with a red total (`R##`S) don't get access to all the skills listed");
		ch->println("   above... use 'show <classname>' to see what skills they can/can't get.");
	}else{
		ch->print_blank_lines(2);
	}
	ch->titlebar("");

	REMOVE_BIT(ch->dyn,DYN_SHOWFLAGS);
    return false;
}
/**************************************************************************/
// add a skill to the currently edited group
bool skillgroupedit_addskill(char_data *ch, char *argument)
{
	char arg[MIL];
	int sn, i;

	skillgroup_type * pG;
	EDIT_SKILLGROUP(ch, pG);

	// check for arguments -> show syntax if none
	if (IS_NULLSTR(argument)){
		ch->println("groupedit_addskill(): syntax: `=Caddskill <skillname>`x");
		ch->println("note: use `=Cremoveskill`x to remove skills from a skillgroup.");
		return false;
	}

	// find the skill we are talking about
	if (!str_prefix("'",argument)){  // ' symbol optional
		argument = one_argument( argument, arg );
	}else{
		strcpy(arg, argument);
	}

	sn=skill_lookup(arg);

	if(sn<0){
		ch->printlnf("groupedit_addskill(): Couldn't find the skill '%s' to add.", arg);
		return false;
	}

	// we have the gsn of a skill in sn... check it is not a spell
	if(IS_SPELL(sn)){
		ch->printlnf("groupedit_addskill(): '%s' is a spell, you can't add spells to skillgroups.", 
			skill_table[sn].name);
		return false;
	}

	// check if the skill is already allocated
	for (i = 0; pG->skills[i]>-1; i++){
		if(pG->skills[i]==sn){
			ch->printlnf("groupedit_addskill(): '%s' is already a skill in '%s'... use removeskill to remove it.", 
				skill_table[sn].name, pG->name);
			return false;
		}
	}

	// find the next free slot to store it
	for (i = 0; pG->skills[i]>-1; i++){
	}
	if(i>=MAX_IN_GROUP){
		ch->wraplnf("groupedit_addskill(): There is a maximum of %d skills in a skillgroup... "
			"no room for any more skills... increase MAX_IN_GROUP in the code if more per "
			"group is required.",
			MAX_IN_GROUP);
		return false;
	}

	// add the skill to the group
	pG->skills[i+1]=-1; // safety code
	pG->skills[i]=sn;
	pG->skillsvalue[i]=1; // default to 1%

	ch->printlnf("groupedit_addskill(): '%s' added to skillgroup '%s'.", 
			skill_table[sn].name, pG->name);
	return true;
}
/**************************************************************************/
// change the skillvalue on a skill
bool skillgroupedit_skillvalue(char_data *ch, char *argument)
{
	char arg[MIL];
	char valarg[MIL];
	int sn, i, value;

	skillgroup_type * pG;
	EDIT_SKILLGROUP(ch, pG);

	// check for arguments -> show syntax if none
	if (IS_NULLSTR(argument)){
		ch->println("syntax: `=Cskillvalue <skillname> <value>`x");
		ch->println("note: value is the percentage they will get the skill at.");
		ch->println("note: use `=Cremoveskill`x to remove skills from a skillgroup.");
		return false;
	}

	// find the skill we are talking about
	argument = one_argument( argument, arg );
	argument = one_argument( argument, valarg);
	
	sn=skill_lookup(arg);

	if(sn<0){
		ch->printlnf("No such skill '%s' exists.", arg);
		return false;
	}

	// we have the gsn of a skill in sn... check it is not a spell
	if(IS_SPELL(sn)){
		ch->printlnf("'%s' is a spell, you can't add spells to skillgroups.", 
			skill_table[sn].name);
		return false;
	}

	value=atoi(valarg);
	if(value<0 || value>101 || !is_number(valarg)){
		ch->printlnf("A sensible value is required for the percentage"
			" - '%s' isn't valid here.", valarg);
		return false;
	}

	// check if the skill is already allocated
	for (i = 0; pG->skills[i]>-1; i++){
		if(pG->skills[i]==sn){	
			ch->printlnf("Skill value of '%s' changed from %d to %d.", 
				skill_table[sn].name, pG->skillsvalue[i], value);
			pG->skillsvalue[i]=value;
			return true;
		}
	}

	ch->printlnf("Couldn't find any '%s' to set a value on.", skill_table[sn].name);
	return false;
}
/**************************************************************************/
// remove a skill from the currently edited group
bool skillgroupedit_removeskill(char_data *ch, char *argument)
{
	char arg[MIL];
	int sn, i, last;

	skillgroup_type * pG;
	EDIT_SKILLGROUP(ch, pG);

	// check for arguments -> show syntax if none
	if (IS_NULLSTR(argument)){
		ch->println("groupedit_removeskill(): syntax: `=Cremoveskill <skillname>`x");
		ch->println("note: use `=Caddskill`x to add skills to a skillgroup.");
		return false;
	}

	// find the skill we are talking about
	if (!str_prefix("'",argument)){  // ' symbol optional
		argument = one_argument( argument, arg );
	}else{
		strcpy(arg, argument);
	}

	sn=skill_lookup(arg);

	if(sn<0){
		ch->printlnf("groupedit_removeskill(): Couldn't find the skill '%s' to remove.", arg);
		return false;
	}

	// find the skill 
	for (i = 0; pG->skills[i]>-1; i++){
		if(pG->skills[i]==sn){
			break;
		}
	}
	if(i>=MAX_IN_GROUP){
		ch->wraplnf("groupedit_removeskill(): the skill '%s' was not found in the skillgroup '%s' to remove.", 
			skill_table[sn].name, pG->name);
		return false;
	}

	// find the last skill
	for (last = 0; pG->skills[last]>-1; last++){
	}
	last--;

	// remove the skill
	if(last==i){
		pG->skills[i]=-1;
	}else{
		pG->skills[i]=pG->skills[last];
		pG->skills[last]=-1;
	}

	ch->wraplnf("groupedit_removeskill(): '%s' removed from skillgroup '%s'.", 
			skill_table[sn].name, pG->name);
	return true;
}


/**************************************************************************/
bool skillgroupedit_flags(char_data *ch, char *argument)
{
    int value;
	skillgroup_type * pG;
	EDIT_SKILLGROUP(ch, pG);
	
    if(!IS_NULLSTR(argument)){
		if ( ( value = flag_value( skillgroup_flags, argument ) ) != NO_FLAG )
		{
			pG->flags ^= value;
			
			ch->println("SkillGroup flag toggled.");
			return true;
		}
    }

	show_olc_options(ch, skillgroup_flags, "flags", "skillgroup", pG->flags );
    return false;	
}

/**************************************************************************/
// adjust the remort level a skillgroup requires
bool skillgroupedit_remort(char_data *ch, char *argument)
{
	skillgroup_type * pG;
	EDIT_SKILLGROUP(ch, pG);
	int			value;

    if( IS_NULLSTR(argument) || !is_number(argument)) {
		ch->println("Syntax: remort <number>");
		ch->println("Remort 0 = skillgroup available at first creation.");
		ch->println("Remort 1 = skillgroup available at after first remort creation...");
		return false;
	}

	value = atoi( argument );
	if(value<0 || value>5){
		ch->println("The remort value must be between 0 and 5.");
		return false;
	}
	ch->printlnf("Remort value changed from %d to %d.",
		pG->remort, value);
	pG->remort= value;
	return true;
}

/**************************************************************************/
bool skillgroupedit_rename(char_data *ch, char *argument)
{
	skillgroup_type * pG;
	EDIT_SKILLGROUP(ch, pG);

    if ( IS_NULLSTR(argument))
    {
		ch->println( "Syntax:  rename <string>" );
		return false;
    }

	if(IS_SET(pG->flags, SKILLGROUP_CREATION_SELECTABLE)){
		ch->println("You cant rename a skillgroup that is creation selectable.");
		return false;
	}

    ch->wraplnf("Skillgroup name changed from '%s' to '%s'.", pG->name, argument);
	replace_string(pG->name, argument);
	return true;
}
/**************************************************************************/
// adjust the cost a particular class pays for the skillgroup
bool skillgroupedit_cost(char_data *ch, char *argument)
{
	if ( IS_NULLSTR(argument))
    {
		ch->println( "Syntax:  cost <class> <amount>" );
		ch->println( "Syntax:  cost <class> free" );
		ch->println( "Syntax:  cost <class> na   (not available)" );
		return false;
    }

	// check we have the class
	char classname[MIL];
	
	argument=one_argument(argument, classname);

	int class_index=class_lookup(classname);

	if(class_index<0){
		ch->printlnf("There is no class '%s'", classname);
		return false;
	}

	skillgroup_type * pG;
	EDIT_SKILLGROUP(ch, pG);

	if(!str_cmp(argument,"free")){
		pG->rating[class_index]=0;
		ch->printlnf("Skillgroup '%s' is now free for the %s class.",
			pG->name, class_table[class_index].name);
		return false;
	}
	if(!str_cmp(argument,"na")){
		pG->rating[class_index]=-1;
		ch->printlnf("Skillgroup '%s' is now unavailable for the %s class (assuming the free_for_all flag is unset).",
			pG->name, class_table[class_index].name);
		return false;
	}

	if(!is_number(argument)){
		ch->printlnf("Second argument must be 'free', 'na' or a numeric value, '%s' is none of these.",
			argument);
		skillgroupedit_cost(ch,"");
		return false;
	}

	int value=atoi(argument);
	if(value<1 || value >50){
		ch->println("The cost value range is 1 to 50.");
		return false;
	}

	if(pG->rating[class_index]>=0){
		ch->printlnf("Skillgroup '%s' cost changed from %d to %d for the %s class.",
			pG->name, pG->rating[class_index], value, class_table[class_index].name);
	}else{
		ch->printlnf("Skillgroup '%s' cost changed from being unavailable to %d for the %s class.",
			pG->name, value, class_table[class_index].name);
	}
	pG->rating[class_index]=value;

	value=skillgroupedit_calc_individual_cost(pG, class_index);
	if(value<0){
		value=(value+1)*-1;
		ch->printlnf("The individual cost of all the skills in the skillgroup for '%s' is %d.",
			class_table[class_index].name, value);
		ch->println("Note: this total only applies to the skills which the class can get");		
	}else{
		ch->printlnf("The individual cost of all the skills in the skillgroup for '%s' is %d.",
			class_table[class_index].name, value);
	}

	if(value<pG->rating[class_index]){
		ch->println("`Rnote: it costs more to purchase the skillgroup than the skills individually!`x");
	}
	return true;
}
/**************************************************************************/