/****************************************************************************
* [S]imulated [M]edieval [A]dventure multi[U]ser [G]ame | \\._.// *
* -----------------------------------------------------------| (0...0) *
* SMAUG 1.4 (C) 1994, 1995, 1996, 1998 by Derek Snider | ).:.( *
* -----------------------------------------------------------| {o o} *
* SMAUG code team: Thoric, Altrag, Blodkai, Narn, Haus, | / ' ' \ *
* Scryn, Rennard, Swordbearer, Gorog, Grishnakh, Nivek, |~'~.VxvxV.~'~*
* Tricops and Fireblade | *
* ------------------------------------------------------------------------ *
* Merc 2.1 Diku Mud improvments copyright (C) 1992, 1993 by Michael *
* Chastain, Michael Quan, and Mitchell Tse. *
* Original Diku Mud copyright (C) 1990, 1991 by Sebastian Hammer, *
* Michael Seifert, Hans Henrik St{rfeldt, Tom Madsen, and Katja Nyboe. *
* ------------------------------------------------------------------------ *
* Table load/save Module *
****************************************************************************/
#include <stdio.h>
#include <string.h>
#include <dlfcn.h>
#include "mud.h"
SKILLTYPE *skill_table[MAX_SKILL];
const SKILLTYPE *skill_table_bytype[MAX_SKILL];
/*
* Function used by qsort to sort skills; sorts by name, not case sensitive.
*/
int skill_comp( SKILLTYPE ** sk1, SKILLTYPE ** sk2 )
{
SKILLTYPE *skill1 = ( *sk1 );
SKILLTYPE *skill2 = ( *sk2 );
if( !skill1 && skill2 )
return 1;
if( skill1 && !skill2 )
return -1;
if( !skill1 && !skill2 )
return 0;
// Sort without regard to case.
return strcasecmp( skill1->name, skill2->name );
}
/*
* Function used by qsort to sort skills; sorts by type,
* then by name, not case sensitive.
*/
int skill_comp_bytype( SKILLTYPE ** sk1, SKILLTYPE ** sk2 )
{
SKILLTYPE *skill1 = ( *sk1 );
SKILLTYPE *skill2 = ( *sk2 );
if( !skill1 && skill2 )
return 1;
if( skill1 && !skill2 )
return -1;
if( !skill1 && !skill2 )
return 0;
// sort by type, first
if( skill1->type < skill2->type )
return -1;
if( skill1->type > skill2->type )
return 1;
// Sort without regard to case.
return strcasecmp( skill1->name, skill2->name );
}
/*
* Sort the skill table with qsort
*/
void sort_skill_table( )
{
int i;
log_string( "Sorting skill table..." );
qsort( &skill_table[1], num_skills - 1, sizeof( SKILLTYPE * ), ( int ( * )( const void *, const void * ) )skill_comp );
log_string( "Creating skill table sorted by type..." );
// first, refresh the bytype table from the skill table
for( i = 0; i < MAX_SKILL; ++i )
{
skill_table_bytype[i] = skill_table[i];
}
qsort( &skill_table_bytype[1], num_skills - 1, sizeof( SKILLTYPE * ),
( int ( * )( const void *, const void * ) )skill_comp_bytype );
}
/*
* Remap slot numbers to sn values
*/
void remap_slot_numbers( )
{
SKILLTYPE *skill;
SMAUG_AFF *aff;
char tmp[32];
int sn;
log_string( "Remapping slots to sns" );
for( sn = 0; sn <= num_skills; ++sn )
{
if( ( skill = skill_table[sn] ) != NULL )
{
for( aff = skill->first_affect; aff; aff = aff->next )
if( aff->location == APPLY_WEAPONSPELL
|| aff->location == APPLY_WEARSPELL
|| aff->location == APPLY_REMOVESPELL
|| aff->location == APPLY_STRIPSN || aff->location == APPLY_RECURRINGSPELL )
{
snprintf( tmp, 32, "%d", slot_lookup( atoi( aff->modifier ) ) );
DISPOSE( aff->modifier );
aff->modifier = str_dup( tmp );
}
}
}
}
int get_skill( const char *skilltype )
{
if( !str_cmp( skilltype, "Racial" ) )
return SKILL_RACIAL;
if( !str_cmp( skilltype, "Spell" ) )
return SKILL_SPELL;
if( !str_cmp( skilltype, "Skill" ) )
return SKILL_SKILL;
if( !str_cmp( skilltype, "Weapon" ) )
return SKILL_WEAPON;
if( !str_cmp( skilltype, "Tongue" ) )
return SKILL_TONGUE;
if( !str_cmp( skilltype, "Herb" ) )
return SKILL_HERB;
return SKILL_UNKNOWN;
}
SKILLTYPE *fread_skill( FILE * fp )
{
const char *word;
bool fMatch;
bool got_info = false;
SKILLTYPE *skill;
int x;
CREATE( skill, SKILLTYPE, 1 );
skill->slot = 0;
skill->min_mana = 0;
skill->guild = -1;
skill->target = 0;
skill->spell_sector = 0;
for( ;; )
{
word = feof( fp ) ? "End" : fread_word( fp );
fMatch = false;
switch ( UPPER( word[0] ) )
{
case '*':
fMatch = true;
fread_to_eol( fp );
break;
case 'A':
if( !str_cmp( word, "Affect" ) )
{
SMAUG_AFF *aff;
CREATE( aff, SMAUG_AFF, 1 );
aff->duration = str_dup( fread_word( fp ) );
aff->location = fread_number( fp );
aff->modifier = str_dup( fread_word( fp ) );
aff->bitvector = fread_number( fp );
if( !got_info )
{
for( x = 0; x < 32; ++x )
{
if( IS_SET( aff->bitvector, 1 << x ) )
{
aff->bitvector = x;
break;
}
}
if( x == 32 )
aff->bitvector = -1;
}
LINK( aff, skill->first_affect, skill->last_affect, next, prev );
fMatch = true;
break;
}
break;
case 'C':
if( !str_cmp( word, "Class" ) )
{
fread_number( fp );
fread_number( fp );
fread_number( fp );
fMatch = true;
break;
}
if( !str_cmp( word, "Code" ) )
{
skill->skill_fun_name = fread_string_nohash( fp );
fMatch = true;
break;
}
KEY( "Components", skill->components, fread_string_nohash( fp ) );
break;
case 'D':
KEY( "Dammsg", skill->noun_damage, fread_string_nohash( fp ) );
KEY( "Dice", skill->dice, fread_string_nohash( fp ) );
KEY( "Diechar", skill->die_char, fread_string_nohash( fp ) );
KEY( "Dieroom", skill->die_room, fread_string_nohash( fp ) );
KEY( "Dievict", skill->die_vict, fread_string_nohash( fp ) );
KEY( "Difficulty", skill->difficulty, fread_number( fp ) );
break;
case 'E':
if( !str_cmp( word, "End" ) )
{
if( skill->saves != 0 && SPELL_SAVE( skill ) == SE_NONE )
{
bug( "%s (%s): Has saving throw (%d) with no saving effect.", __FUNCTION__, skill->name, skill->saves );
SET_SSAV( skill, SE_NEGATE );
}
return skill;
}
break;
case 'F':
if( !str_cmp( word, "Flags" ) )
{
skill->flags = fread_number( fp );
/*
* convert to new style -Thoric
*/
if( !got_info )
{
skill->info = skill->flags & ( BV11 - 1 );
if( IS_SET( skill->flags, OLD_SF_SAVE_NEGATES ) )
{
if( IS_SET( skill->flags, OLD_SF_SAVE_HALF_DAMAGE ) )
{
SET_SSAV( skill, SE_QUARTERDAM );
REMOVE_BIT( skill->flags, OLD_SF_SAVE_HALF_DAMAGE );
}
else
SET_SSAV( skill, SE_NEGATE );
REMOVE_BIT( skill->flags, OLD_SF_SAVE_NEGATES );
}
else if( IS_SET( skill->flags, OLD_SF_SAVE_HALF_DAMAGE ) )
{
SET_SSAV( skill, SE_HALFDAM );
REMOVE_BIT( skill->flags, OLD_SF_SAVE_HALF_DAMAGE );
}
skill->flags >>= 11;
}
fMatch = true;
break;
}
break;
case 'G':
KEY( "Guild", skill->guild, fread_number( fp ) );
break;
case 'H':
KEY( "Hitchar", skill->hit_char, fread_string_nohash( fp ) );
KEY( "Hitdest", skill->hit_dest, fread_string_nohash( fp ) );
KEY( "Hitroom", skill->hit_room, fread_string_nohash( fp ) );
KEY( "Hitvict", skill->hit_vict, fread_string_nohash( fp ) );
break;
case 'I':
KEY( "Immchar", skill->imm_char, fread_string_nohash( fp ) );
KEY( "Immroom", skill->imm_room, fread_string_nohash( fp ) );
KEY( "Immvict", skill->imm_vict, fread_string_nohash( fp ) );
if( !str_cmp( word, "Info" ) )
{
skill->info = fread_number( fp );
got_info = true;
fMatch = true;
break;
}
break;
case 'M':
KEY( "Mana", skill->min_mana, fread_number( fp ) );
if( !str_cmp( word, "Minlevel" ) )
{
fread_to_eol( fp );
fMatch = true;
break;
}
/*
*
*/
if( !str_cmp( word, "Minpos" ) )
{
fMatch = true;
skill->minimum_position = fread_number( fp );
if( skill->minimum_position < 100 )
{
switch ( skill->minimum_position )
{
default:
case 0:
case 1:
case 2:
case 3:
case 4:
break;
case 5:
skill->minimum_position = 6;
break;
case 6:
skill->minimum_position = 8;
break;
case 7:
skill->minimum_position = 9;
break;
case 8:
skill->minimum_position = 12;
break;
case 9:
skill->minimum_position = 13;
break;
case 10:
skill->minimum_position = 14;
break;
case 11:
skill->minimum_position = 15;
break;
}
}
else
skill->minimum_position -= 100;
break;
}
KEY( "Misschar", skill->miss_char, fread_string_nohash( fp ) );
KEY( "Missroom", skill->miss_room, fread_string_nohash( fp ) );
KEY( "Missvict", skill->miss_vict, fread_string_nohash( fp ) );
break;
case 'N':
KEY( "Name", skill->name, fread_string_nohash( fp ) );
break;
case 'P':
KEY( "Participants", skill->participants, fread_number( fp ) );
break;
case 'R':
KEY( "Range", skill->range, fread_number( fp ) );
KEY( "Rounds", skill->beats, fread_number( fp ) );
if( !str_cmp( word, "Race" ) )
{
fread_number( fp );
fread_number( fp );
fread_number( fp );
fMatch = true;
break;
}
break;
case 'S':
KEY( "Saves", skill->saves, fread_number( fp ) );
KEY( "Slot", skill->slot, fread_number( fp ) );
KEY( "Ssector", skill->spell_sector, fread_number( fp ) );
break;
case 'T':
KEY( "Target", skill->target, fread_number( fp ) );
KEY( "Teachers", skill->teachers, fread_string_nohash( fp ) );
KEY( "Type", skill->type, get_skill( fread_word( fp ) ) );
break;
case 'V':
KEY( "Value", skill->value, fread_number( fp ) );
break;
case 'W':
KEY( "Wearoff", skill->msg_off, fread_string_nohash( fp ) );
break;
}
if( !fMatch )
{
bug( "%s: no match: %s", __FUNCTION__, word );
fread_to_eol( fp );
}
}
}
void load_skill_table( )
{
FILE *fp;
if( ( fp = fopen( SKILL_FILE, "r" ) ) != NULL )
{
num_skills = 0;
for( ;; )
{
char letter;
char *word;
letter = fread_letter( fp );
if( letter == '*' )
{
fread_to_eol( fp );
continue;
}
if( letter != '#' )
{
bug( "%s: # not found.", __FUNCTION__ );
break;
}
word = fread_word( fp );
if( !str_cmp( word, "SKILL" ) )
{
if( num_skills >= MAX_SKILL )
{
bug( "%s: more skills than MAX_SKILL %d", __FUNCTION__, MAX_SKILL );
fclose( fp );
fp = NULL;
return;
}
skill_table[num_skills++] = fread_skill( fp );
continue;
}
else if( !str_cmp( word, "END" ) )
break;
else
{
bug( "%s: bad section.", __FUNCTION__ );
continue;
}
}
fclose( fp );
fp = NULL;
}
else
{
perror( SKILL_FILE );
bug( "Cannot open %s", SKILL_FILE );
exit( 0 );
}
}