/***************************************************************************** * DikuMUD (C) 1990, 1991 by: * * Sebastian Hammer, Michael Seifert, Hans Henrik Staefeldt, Tom Madsen, * * and Katja Nyboe. * *---------------------------------------------------------------------------* * MERC 2.1 (C) 1992, 1993 by: * * Michael Chastain, Michael Quan, and Mitchell Tse. * *---------------------------------------------------------------------------* * SMAUG 1.4 (C) 1994, 1995, 1996, 1998 by: Derek Snider. * * Team: Thoric, Altrag, Blodkai, Narn, Haus, Scryn, Rennard, Swordbearer, * * gorog, Grishnakh, Nivek, Tricops, and Fireblade. * *---------------------------------------------------------------------------* * SMAUG 1.7 FUSS by: Samson and others of the SMAUG community. * * Their contributions are greatly appreciated. * *---------------------------------------------------------------------------* * LoP (C) 2006, 2007, 2008, 2009 by: the LoP team. * *****************************************************************************/ #include <stdio.h> #include "h/mud.h" CLASS_TYPE *class_table[MAX_CLASS]; void update_level( CHAR_DATA *ch ); int get_pc_class( char *Class ); int get_char_cnum( CHAR_DATA *ch, char *argument ) { MCLASS_DATA *mclass; int cnt = 0; if( !ch || is_npc( ch ) ) return -1; for( mclass = ch->pcdata->first_mclass; mclass; mclass = mclass->next ) { if( mclass->wclass >= 0 && mclass->wclass < MAX_PC_CLASS && class_table[mclass->wclass] && !str_cmp( class_table[mclass->wclass]->name, argument ) ) return cnt; ++cnt; } return -1; } bool char_is_class( CHAR_DATA *ch, int cnum ) { MCLASS_DATA *mclass; if( !ch || is_npc( ch ) ) return false; for( mclass = ch->pcdata->first_mclass; mclass; mclass = mclass->next ) { if( cnum == mclass->wclass ) return true; } return false; } void write_class_restriction_file( void ) { FILE *fp; char filename[MIL]; int x, count = 0; snprintf( filename, sizeof( filename ), "%screstrictions.dat", SYSTEM_DIR ); if( !( fp = fopen( filename, "w" ) ) ) { bug( "Can't open: %s for writing", filename ); perror( filename ); return; } for( x = 0; x < MAX_PC_CLASS; x++ ) { if( class_table[x] && class_table[x]->name ) { if( xIS_EMPTY( class_table[x]->class_restriction ) ) continue; count++; fprintf( fp, "Restrict %s~ %s~\n", class_table[x]->name, ext_class_string( &class_table[x]->class_restriction ) ); } } fprintf( fp, "$\n" ); fclose( fp ); fp = NULL; if( !count) remove_file( filename ); } void write_class_file( int cl ) { FILE *fp; char filename[MIL]; struct class_type *Class = class_table[cl]; int x, y; if( !Class ) { bug( "%s: NULL class for %d", __FUNCTION__, cl ); return; } if( !Class->name ) { bug( "%s: NULL class->name for %d", __FUNCTION__, cl ); return; } snprintf( filename, sizeof( filename ), "%s%s.class", CLASS_DIR, Class->name ); if( !( fp = fopen( filename, "w" ) ) ) { bug( "Can't open: %s for writing", filename ); perror( filename ); return; } fprintf( fp, "Name %s~\n", Class->name ); fprintf( fp, "Used %d\n", Class->used ); for( x = 0; x < top_sn; x++ ) { if( !skill_table[x] || !skill_table[x]->name ) continue; if( ( y = skill_table[x]->skill_level[cl] ) > 0 ) fprintf( fp, "Skill '%s' %d %d\n", skill_table[x]->name, y, skill_table[x]->skill_adept[cl] ); } fprintf( fp, "End\n" ); fclose( fp ); fp = NULL; } void load_class_restrictions( void ) { FILE *fp; char filename[MSL], *infoflags, flag[MSL]; const char *word; int uclass, iclass; snprintf( filename, sizeof( filename ), "%screstrictions.dat", SYSTEM_DIR ); if( !( fp = fopen( filename, "r" ) ) ) return; for( ;; ) { word = feof( fp ) ? "$" : fread_word( fp ); if( word[0] == '$' || word[0] == EOF ) break; if( !str_cmp( word, "Restrict" ) ) { uclass = get_pc_class( fread_flagstring( fp ) ); if( !class_table[uclass] || !class_table[uclass]->name ) fread_flagstring( fp ); else { infoflags = fread_flagstring( fp ); while( infoflags && infoflags[0] != '\0' ) { infoflags = one_argument( infoflags, flag ); for( iclass = 0; iclass < MAX_PC_CLASS; iclass++ ) { if( !class_table[iclass] || !class_table[iclass]->name ) continue; if( !str_cmp( class_table[iclass]->name, flag ) ) { xSET_BIT( class_table[uclass]->class_restriction, iclass ); break; } } } } } } fclose( fp ); fp = NULL; } void write_class_list( void ) { FILE *fp; int i; if( !( fp = fopen( CLASS_LIST, "w" ) ) ) { bug( "%s: Can't open %s for writing.", __FUNCTION__, CLASS_LIST ); perror( CLASS_LIST ); return; } for( i = 0; i < MAX_PC_CLASS; i++ ) if( class_table[i] && class_table[i]->name ) fprintf( fp, "%s.class\n", class_table[i]->name ); fprintf( fp, "%s", "$\n" ); fclose( fp ); fp = NULL; } CLASS_TYPE *valid_class( char *name ) { CLASS_TYPE *Class = NULL; int ccheck; if( name && name[0] != '\0' ) { if( is_number( name ) ) { if( ( ccheck = atoi( name ) ) >= 0 && ccheck < MAX_PC_CLASS ) Class = class_table[ccheck]; } else { for( ccheck = 0; ccheck < MAX_PC_CLASS && class_table[ccheck]; ccheck++ ) { if( !class_table[ccheck] || !class_table[ccheck]->name ) continue; if( !str_cmp( class_table[ccheck]->name, name ) ) { Class = class_table[ccheck]; break; } } } } return Class; } CMDF( do_classes ) { CLASS_TYPE *Class; int cl, count; if( !ch ) return; for( cl = 0, count = ( is_immortal( ch ) ? 0 : 1 ); cl < MAX_PC_CLASS; cl++, count++ ) { if( !( Class = class_table[cl] ) || !Class->name ) continue; pager_printf( ch, "&w[&W%2d&w] &w(&W%3d&w) &W%s&D\r\n", count, Class->used, Class->name ); } } void show_class( CHAR_DATA *ch, struct class_type *Class ) { int cl, low, hi, ct, i; set_pager_color( AT_PLAIN, ch ); if( !Class ) { send_to_char( "No such class.\r\n", ch ); return; } cl = Class->clistplace; pager_printf( ch, "&wCLASS: &W%s\r\n&w\r\n", Class->name ); pager_printf( ch, "&wUsed: &W%d\r\n", Class->used ); pager_printf( ch, "&wDisallowed classes:\r\n&W%s\r\n", ext_class_string( &Class->class_restriction ) ); ct = 0; send_to_pager( "&wAllowed classes:&W\r\n", ch ); for( i = 0; i < MAX_PC_CLASS; i++ ) { if( !xIS_SET( Class->class_restriction, i ) ) { pager_printf( ch, "%s ", class_table[i]->name ); if( ++ct == 6 ) { send_to_pager( "\r\n", ch ); ct = 0; } } } if( ct != 0 ) send_to_pager( "\r\n", ch ); { int x, y, cnt, last = -1; bool first = true; low = 0; hi = MAX_LEVEL; set_pager_color( AT_BLUE, ch ); for( x = low; x <= hi; x++ ) { cnt = 0; for( y = gsn_first_spell; y < gsn_top_sn; y++ ) { if( skill_table[y]->skill_level[cl] == x ) { if( first ) send_to_pager( "\r\n&wSpells/Skills/Weapons\r\n", ch ); first = false; if( x != last ) { if( cnt != 0 ) { send_to_pager( "\r\n", ch ); cnt = 0; } pager_printf( ch, "&w[Level &W%d&w]\r\n", x ); } last = x; pager_printf( ch, " &w(&W%3d&w)&D%-22s", skill_table[y]->skill_adept[cl], skill_table[y]->name ); if( ++cnt == 3 ) { send_to_pager( "\r\n", ch ); cnt = 0; } } } if( cnt != 0 ) send_to_pager( "\r\n", ch ); } } } /* Create a new class online. - Shaddai */ bool create_new_class( int rcindex, char *argument ) { if( !argument || argument[0] == '\0' ) return false; if( rcindex >= MAX_CLASS || class_table[rcindex] ) return false; CREATE( class_table[rcindex], struct class_type, 1 ); if( !class_table[rcindex] ) return false; class_table[rcindex]->clistplace = rcindex; argument[0] = UPPER( argument[0] ); class_table[rcindex]->name = STRALLOC( argument ); class_table[rcindex]->used = 0; xCLEAR_BITS( class_table[rcindex]->class_restriction ); return true; } /* Edit class information - Thoric */ CMDF( do_setclass ) { char arg1[MIL], arg2[MIL]; CLASS_TYPE *Class; int cl; set_char_color( AT_PLAIN, ch ); argument = one_argument( argument, arg1 ); argument = one_argument( argument, arg2 ); if( arg1 == NULL || arg1[0] == '\0' ) { send_to_char( "Usage: setclass <class> [create/save]\r\n", ch ); send_to_char( "Usage: setclass <class> skill <skill> <level> <adept>\r\n", ch ); send_to_char( "Usage: setclass <class> name <new name>\r\n", ch ); send_to_char( "Usage: setclass <class> classes [<class>]\r\n", ch ); return; } Class = valid_class( arg1 ); if( !str_cmp( arg2, "create" ) && Class ) { send_to_char( "That class already exists!\r\n", ch ); return; } else if( !Class && str_cmp( arg2, "create" ) ) { send_to_char( "No such class.\r\n", ch ); return; } if( arg2 == NULL || arg2[0] == '\0' ) { show_class( ch, Class ); return; } if( !str_cmp( arg2, "create" ) ) { char filename[1024]; if( MAX_PC_CLASS >= MAX_CLASS ) { send_to_char( "You need to up MAX_CLASS in src/h/mud.h and make clean.\r\n", ch ); return; } snprintf( filename, sizeof( filename ), "%s.class", arg1 ); if( !can_use_path( ch, CLASS_DIR, filename ) ) return; if( !( create_new_class( MAX_PC_CLASS, arg1 ) ) ) { send_to_char( "Couldn't create a new class.\r\n", ch ); return; } write_class_file( MAX_PC_CLASS ); MAX_PC_CLASS++; write_class_list( ); send_to_char( "Done.\r\n", ch ); return; } cl = Class->clistplace; if( !str_cmp( arg2, "save" ) ) { write_class_file( cl ); send_to_char( "Class saved.\r\n", ch ); return; } if( !argument || argument[0] == '\0' ) { send_to_char( "You must specify an argument.\r\n", ch ); return; } if( !str_cmp( arg2, "classes" ) ) { int i; bool modified = false; while( argument && argument[0] != '\0' ) { argument = one_argument( argument, arg1 ); for( i = 0; i < MAX_PC_CLASS && class_table[i]; i++ ) { if( !class_table[i] || !class_table[i]->name ) continue; if( !str_cmp( arg1, class_table[i]->name ) ) { xTOGGLE_BIT( Class->class_restriction, i ); modified = true; } } } if( !modified ) send_to_char( "No such class.\r\n", ch ); else { write_class_restriction_file( ); send_to_char( "Class Classes set.\r\n", ch ); } return; } if( !str_cmp( arg2, "skill" ) ) { SKILLTYPE *skill; int sn; argument = one_argument( argument, arg2 ); if( ( sn = skill_lookup( arg2 ) ) < 0 ) { ch_printf( ch, "No such skill as %s.\r\n", arg2 ); return; } if( !( skill = get_skilltype( sn ) ) ) { send_to_char( "Invalid skilltype???\r\n", ch ); return; } argument = one_argument( argument, arg2 ); skill->skill_level[cl] = URANGE( -1, atoi( arg2 ), MAX_LEVEL ); argument = one_argument( argument, arg2 ); skill->skill_adept[cl] = URANGE( 0, atoi( arg2 ), 100 ); write_class_file( cl ); if( skill->skill_level[cl] == -1 ) ch_printf( ch, "Skill \"%s\" now has a level of -1 and means the class no longer gets it.\r\n", skill->name ); else ch_printf( ch, "Skill \"%s\" added at level %d and %d%%.\r\n", skill->name, skill->skill_level[cl], skill->skill_adept[cl] ); return; } if( !str_cmp( arg2, "name" ) ) { char buf[256]; CLASS_TYPE *ccheck; one_argument( argument, arg1 ); if( arg1 == NULL || arg1[0] == '\0' ) { send_to_char( "You can't set a class name to nothing.\r\n", ch ); return; } snprintf( buf, sizeof( buf ), "%s.class", arg1 ); if( !can_use_path( ch, CLASS_DIR, buf ) ) return; if( ( ccheck = valid_class( arg1 ) ) ) { ch_printf( ch, "Already a class called %s.\r\n", arg1 ); return; } snprintf( buf, sizeof( buf ), "%s%s.class", CLASS_DIR, Class->name ); remove_file( buf ); STRSET( Class->name, capitalize( argument ) ); ch_printf( ch, "Class renamed to %s.\r\n", arg1 ); write_class_file( cl ); write_class_list( ); return; } do_setclass( ch, (char *)"" ); } bool load_class_file( const char *fname ) { FILE *fp; struct class_type *Class; const char *word; char buf[MSL]; int cl = -1; bool fMatch; snprintf( buf, sizeof( buf ), "%s%s", CLASS_DIR, fname ); if( !( fp = fopen( buf, "r" ) ) ) { perror( buf ); return false; } CREATE( Class, struct class_type, 1 ); for( cl = 0; cl < MAX_CLASS; cl++ ) { if( !class_table[cl] ) break; } for( ;; ) { word = feof( fp ) ? "End" : fread_word( fp ); fMatch = false; switch( UPPER( word[0] ) ) { case '*': fMatch = true; fread_to_eol( fp ); break; case 'E': if( !str_cmp( word, "End" ) ) { fclose( fp ); fp = NULL; /* Keeps them all in order instead of keeping them at a set number */ if( cl < 0 || cl >= MAX_CLASS ) { bug( "%s: Class (%s) bad/not found (%d)", __FUNCTION__, Class->name ? Class->name : buf, cl ); STRFREE( Class->name ); DISPOSE( Class ); return false; } class_table[cl] = Class; class_table[cl]->clistplace = cl; return true; } break; case 'N': KEY( "Name", Class->name, fread_string( fp ) ); break; case 'S': if( !str_cmp( word, "Skill" ) ) { int sn, lev, adp; word = fread_word( fp ); lev = fread_number( fp ); adp = fread_number( fp ); sn = skill_lookup( word ); if( !is_valid_sn( sn ) ) bug( "%s: Skill %s unknown", __FUNCTION__, word ); else { if( cl >= 0 && cl < MAX_CLASS ) { skill_table[sn]->skill_level[cl] = lev; skill_table[sn]->skill_adept[cl] = adp; } } fMatch = true; break; } break; case 'U': KEY( "Used", Class->used, fread_number( fp ) ); break; } if( !fMatch ) { bug( "%s: no match: %s", __FUNCTION__, word ); fread_to_eol( fp ); } } } void load_classes( void ) { FILE *fp; const char *filename; int i; MAX_PC_CLASS = 0; for( i = 0; i < MAX_CLASS; i++ ) class_table[i] = NULL; if( !( fp = fopen( CLASS_LIST, "r" ) ) ) { bug( "%s: Can't open %s for reading", __FUNCTION__, CLASS_LIST ); perror( CLASS_LIST ); exit( 1 ); } for( ;; ) { filename = feof( fp ) ? "$" : fread_word( fp ); if( filename[0] == '$' || filename[0] == EOF ) break; if( !load_class_file( filename ) ) bug( "%s: Can't load class file: %s", __FUNCTION__, filename ); else MAX_PC_CLASS++; } fclose( fp ); fp = NULL; } void save_classes( void ) { int x; for( x = 0; x < MAX_PC_CLASS; x++ ) if( class_table[x] && class_table[x]->name ) write_class_file( x ); } MCLASS_DATA *add_mclass( CHAR_DATA *ch, int wclass, int level, int cpercent, double exp, bool limit ) { MCLASS_DATA *mclass; int mcount = 0; if( !ch || !ch->pcdata ) return NULL; for( mclass = ch->pcdata->first_mclass; mclass; mclass = mclass->next ) mcount++; if( limit && mcount >= sysdata.mclass ) { send_to_char( "You already have all the classes you can.\r\n", ch ); update_level( ch ); return NULL; } CREATE( mclass, MCLASS_DATA, 1 ); if( !mclass ) { bug( "%s: failed to create mclass.", __FUNCTION__ ); update_level( ch ); return NULL; } mclass->wclass = wclass; mclass->level = level; mclass->cpercent = cpercent; mclass->exp = exp; LINK( mclass, ch->pcdata->first_mclass, ch->pcdata->last_mclass, next, prev ); update_level( ch ); return mclass; }