/**************************************************************************/
// dynamics.cpp - code for dynamic loading of tables etc
/***************************************************************************
* 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 "dynamics.h"
#include "magic.h"
/**************************************************************************/
// Prototypes
extern dynlookup_type dynlookup_table[];
extern dynspell_type spellpairs_table[];
char *flag_string( const struct flag_type *flag_table, int bits );
char *tochar_spellfunction(SPELL_FUN *psp);
sh_int spellfunctionindex_fromchar(const char *name);
int class_lookup (const char *name);
bool flush_char_outbuffer(char_data *ch);
void autonote(int type, char *sender, char *subject, char *to, char *text, bool reformat);
void do_write_socials( char_data *ch, char * );
/**************************************************************************/
// Returns the index of the class it just created, -1 if couldn't create it
// if a class with the same name already exists, it returns that class
// all classes created using this function have creation selectable set to false
// - Kal March 00
int create_class(char * class_name)
{
int result;
result=class_exact_lookup(class_name);
if(result!=-1){
bugf("create_class(): class '%s' already exists!", class_name);
return result;
}
// find the end of the table
int i;
for(i=0;!IS_NULLSTR(class_table[i].name); i++){
// do nothing but count thru table
}
// check if we are going to overflow the table
if(i+1>=MAX_CLASS){
bugf("create_class(): attempting to add class '%s', but there are already %d classes (as defined by MAX_CLASS), this would exceed the maximum - canceling",
class_name, MAX_CLASS);
return -1;
}
class_table[i+1].name=NULL; // mark the new end of the table
class_table[i].name=str_dup(class_name);
class_table[i].short_name=str_dup("???");
char temp[MIL];
sprintf(temp,"%s default", class_name);
class_table[i].default_group=str_dup(temp);
class_table[i].spinfo_letter=str_dup("-");
class_table[i].creation_selectable=false;
class_table[i].hp_min=1;
class_table[i].hp_max=5;
class_table[i].skill_adept=75;
class_table[i].remort_number=0;
// add our entry in the classnames_table
classnames_flags[i].name=str_dup(class_table[i].name);
classnames_flags[i].bit=1<<i;
classnames_flags[i].settable=true;
classnames_flags[i+1].name=NULL; // mark end of table
// clear out the class specific entries in
// the other tables for the new class
{
int j;
// skill_table
for(j=0;!IS_NULLSTR(skill_table[j].name);j++){
skill_table[j].skill_level[i]=0;
skill_table[j].rating[i]=0;
skill_table[j].low_percent_level[i]=0;
skill_table[j].maxprac_percent[i]=0;
skill_table[j].learn_scale_percent[i]=0;
skill_table[j].alignrestrict_flags[i]=0;
}
if(race_table){
for(j=0; race_table[j]; j++){
race_table[j]->class_exp[i]=1000;
}
}
}
logf("create_class(): dynamically added class '%s'.", class_name);
return i;
}
/**************************************************************************/
// Cant be bothered writing word information for the class table :)
GIO_START(class_type)
GIO_STRH(short_name, "Who_name ")
GIO_SHINT_ARRAY(attr_prime, 2)
GIO_SHINTH(skill_adept, "skill_adept ")
GIO_SHINTH(thac0_00, "thac0_00 ")
GIO_SHINTH(thac0_32, "thac0_32 ")
GIO_SHINTH(hp_min, "hp_min ")
GIO_SHINTH(hp_max, "hp_max ")
GIO_BOOLH( fMana, "fMana ")
GIO_STRH(spinfo_letter, "spinfo_letter ")
GIO_STRH(default_group, "default_group ")
GIO_BOOLH(creation_selectable, "creation_selectable ")
GIO_INTH(class_cast_type, "class_cast_type ") // dont need this once file format has been converted
GIO_WFLAGH(class_cast_type,"cast_type ", castnames_types)
GIO_SHINTH(core_clss, "core_clss ")
GIO_INTH(object_restriction_index, "object_restriction_index ")
GIO_INTH(objrestrict, "objrestrict ")
GIO_SHINTH(class_id, "class_id ")
GIO_WFLAGH(flags, "Flags ", classflag_flags)
GIO_SHINTH(remort_number,"remort_number ")
GIO_INT(recall)
GIO_INT(morgue)
GIO_READ_TO_EOL("base_group") // remove from system
GIO_STR_ARRAYH(pose_self, "pose_self ", MAX_LEVEL)
GIO_STR_ARRAYH(pose_others, "pose_others ", MAX_LEVEL)
GIO_FINISH_NOCLEAR
/**************************************************************************/
void skilltype_write_generic_short_class_entry(gio_type *gio_table, int tableIndex,
void *data, FILE *fp)
{
short *array=(short*)((char *)data + gio_table[tableIndex].index);
int i;
// make sure we have something to write before starting into it
for(i=0; !IS_NULLSTR(class_table[i].name); i++)
{
if(array[i]!=0){
break;
}
}
if(IS_NULLSTR(class_table[i].name)){
return; // nothing to write
}
// put out the header
fprintf(fp, "%s\n",gio_table[tableIndex].heading);
// the data for the non 0 classes
for(i=0; !IS_NULLSTR(class_table[i].name); i++)
{
if(array[i]!=0){
if(has_space(class_table[i].name)){
fprintf(fp, "'%-16s' %d\n",
class_table[i].name,
array[i]);
}else{
fprintf(fp, "%-16s %d\n",
class_table[i].name,
array[i]);
}
}
}
// the footer
fprintf(fp, "/\n");
}
/**************************************************************************/
void skilltype_read_generic_short_class_entry(gio_type *gio_table, int tableIndex,
void *data, FILE *fp)
{
short *array=(short*)((char *)data + gio_table[tableIndex].index);
char *className;
short value;
int classIndex;
// table has already been nulled by the gio system
while (true){
// get the class name and modifier
className=fread_word(fp);
if (className[0]=='/' && className[1]=='\0'){
break;
}
// read in the experience modifier
value=fread_number(fp);
classIndex =class_lookup(className);
if (classIndex<0){
if (className[0]!='*'){
bugf( "skilltype_read_generic_short_class_entry: class '%s' not found... "
"ignoring '%s' field for that class!", className, gio_table[tableIndex].heading);
}
}else{
array[classIndex]=value;
}
}
}
/**************************************************************************/
GIO_CUSTOM_FUNCTION_PROTOTYPE( skilltype_write_spellfunction );
GIO_CUSTOM_FUNCTION_PROTOTYPE( skilltype_read_spellfunction );
GIO_CUSTOM_FUNCTION_PROTOTYPE(racetype_write_generic_races_set_for_n_array);
GIO_CUSTOM_FUNCTION_PROTOTYPE(racetype_read_generic_races_set_for_n_array);
/**************************************************************************/
// create skill_type GIO lookup table
GIO_START(skill_type)
// *** char * name - is written by the table handling code
//###TODO skill_level[MAX_CLASS]
//###TODO rating[MAX_CLASS]
#define STWGSCE skilltype_write_generic_short_class_entry // work smarter not harder :)
#define STRGSCE skilltype_read_generic_short_class_entry
GIO_CUSTOM_WRITEH(skill_level, "===skill_level", STWGSCE)
GIO_CUSTOM_WRITEH(rating, "===rating", STWGSCE)
GIO_CUSTOM_WRITEH(low_percent_level, "===low_percent_level", STWGSCE)
GIO_CUSTOM_WRITEH(maxprac_percent, "===maxprac_percent", STWGSCE)
GIO_CUSTOM_WRITEH(learn_scale_percent, "===learn_scale_percent", STWGSCE)
GIO_CUSTOM_WRITEH(alignrestrict_flags, "===alignrestrict_flags", STWGSCE) // storing as a number (doesn't matter really)
GIO_CUSTOM_READH(skill_level, "===skill_level", STRGSCE)
GIO_CUSTOM_READH(rating, "===rating", STRGSCE)
GIO_CUSTOM_READH(low_percent_level, "===low_percent_level", STRGSCE)
GIO_CUSTOM_READH(maxprac_percent, "===maxprac_percent", STRGSCE)
GIO_CUSTOM_READH(learn_scale_percent, "===learn_scale_percent", STRGSCE)
GIO_CUSTOM_READH(alignrestrict_flags, "===alignrestrict_flags", STRGSCE)
GIO_CUSTOM_WRITEH(spell_fun, "SpellFunction ", skilltype_write_spellfunction)
GIO_CUSTOM_READH(spell_fun, "SpellFunction ", skilltype_read_spellfunction)
GIO_SHWFLAGH(minimum_position, "Position ", position_types)
GIO_SHINTH(min_mana, "Mana ")
GIO_SHINTH(beats, "Beats ")
GIO_STRH(noun_damage, "Noun_damage ")
GIO_STRH(msg_off, "Wearoff_msg ")
GIO_STRH(msg_obj, "Obj_wearoff_msg ")
GIO_CUSTOM_WRITEH(race_restrict_n, "RaceRestriction ", racetype_write_generic_races_set_for_n_array)
GIO_CUSTOM_READH(race_restrict_n, "RaceRestriction ", racetype_read_generic_races_set_for_n_array)
GIO_WFLAGH(flags, "Flags ", skflags_flags)
GIO_SHWFLAGH(category, "Category ", category_types)
GIO_SHWFLAGH(type, "Type ", sktype_types)
GIO_SHWFLAGH(damtype, "Damtype ", damtype_types)
GIO_WFLAGH(realms, "Realms ", realm_flags)
GIO_WFLAGH(spheres, "Spheres ", sphere_flags)
GIO_WFLAGH(elements, "Elements ", element_flags)
GIO_WFLAGH(spellgroup, "Spell_Group ", spell_group_flags)
GIO_WFLAGH(sect_restrict, "Sector_Restrict ", sectorbit_flags)
GIO_WFLAGH(sect_enhance, "Sector_Enhance ", sectorbit_flags)
GIO_WFLAGH(sect_dampen, "Sector_Dampen ", sectorbit_flags)
GIO_SHINTH(component_based, "Component ")
GIO_STRH(msp_sound, "MSPSound ")
GIO_FINISH_NOCLEAR
/**************************************************************************/
// Output the class_table to disk
void do_write_classes(char_data *ch, char *)
{
FILE *fp;
int i;
logf("Writing class table to " CLASSES_LIST ".write ...");
fclose( fpReserve );
if ( ( fp = fopen( CLASSES_LIST ".write", "w" ) ) == NULL )
{
bugf("do_write_classes(): fopen '%s' for write - error %d (%s)",
CLASSES_LIST ".write", errno, strerror( errno));
ch->printf("An error occured opening " CLASSES_LIST ".write for writing!\r\n");
autonote(NOTE_SNOTE, "do_write_classes()", "Problems saving class table", "code cc: imm",
"An error occured opening " CLASSES_LIST ".write for writing!\r\n", true);
}else{
// LOOP thru everything in the table, writing it
for ( i = 0; !IS_NULLSTR(class_table[i].name); i++ )
{
fprintf(fp,"######NAME %s~\n", class_table[i].name);
GIO_SAVE_RECORD(class_type, &class_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_classes(): fprintf to '%s' - error %d (%s)",
CLASSES_LIST ".write", errno, strerror( errno));
bugf("Incomplete write of " CLASSES_LIST ".write, write aborted - check diskspace!");
ch->printf("Incomplete write of " CLASSES_LIST ".write, write aborted - check diskspace!\r\n");
autonote(NOTE_SNOTE, "do_write_classes()", "Problems saving class table", "code cc: imm",
"Incomplete write of " CLASSES_LIST ".write, write aborted - check diskspace!\r\n", true);
}else{
ch->printf("Finished writing class table to "CLASSES_LIST ".write\r\n");
ch->printf("Renaming old " CLASSES_LIST " to " CLASSES_LIST ".bak\r\n");
unlink(CLASSES_LIST".bak");
rename(CLASSES_LIST, CLASSES_LIST".bak");
ch->printf("Renaming new " CLASSES_LIST ".write to " CLASSES_LIST "\r\n");
unlink(CLASSES_LIST);
rename(CLASSES_LIST".write", CLASSES_LIST);
}
}
fpReserve = fopen( NULL_FILE, "r" );
logf("Finished writing class table.");
}
/**************************************************************************/
// read in the class_table from disk
void do_read_classes(char_data *ch, char *)
{
FILE *fp;
int count=0;
int i;
// because system doesn't support reassigning class numbers, only allow
// this to run at bootup and when one imm is on
if(runlevel!=RUNLEVEL_BOOTING){
// if(player_list && !player_list->next_player){ // only one in the game
// ch->println("Allowing class read in due to a single person being logged in.");
// }else{
ch->println("do_read_classes() currently doesn't support being called a game time... boot only!");
ch->println("Doing so in its current state might stuff up everyones class,");
ch->println("and object classrestrictions in area files... DONT DO IT!");
return;
// }
}
logf("Reading in class table from " CLASSES_LIST "...");
fclose( fpReserve );
if ( ( fp = fopen( CLASSES_LIST, "r" ) ) == NULL )
{ // couldn't find the file to read in
bugf("do_read_classes(): fopen '%s' for read - error %d (%s)",
CLASSES_LIST, errno, strerror( errno));
bugf("An error occured trying to open " CLASSES_LIST " for reading!");
ch->printf("An error occured trying to open " CLASSES_LIST " for reading!\r\n");
if(file_exists(CLASSES_LIST )){
bugf("File " CLASSES_LIST "found, but it wasnt openned for some reason?!?");
write_shutdown_file(NULL);
do_abort();
}else{
if(runlevel==RUNLEVEL_BOOTING){
log_notef("File " CLASSES_LIST " not found... the mud can't boot without this file!`1"
"`1"
"This file contains the basic information about all classes, if the mud "
"booted without this file then there would be no classes, and therefore "
"none would be able to login.`1"
"`1"
"If you are in the process of setting up this mud, it is most likely that you "
"either haven't downloaded the dawn support files (which include the required system "
"files, helps and optional area set), or haven't correctly installed the support files. "
"Download and install them now, then if you are still having "
"problems which you can't resolve, ask for assistance on the dawn forums at "
"http://forums.dawnoftime.org/`1"
"`1"
"If this error has just started to occur on a mud which was previously "
"working, the information of the previous class table might be "
"contained in " CLASSES_LIST ".bak or "
"secondly " CLASSES_LIST ".write... (the bak file is probably the better "
"of the two if you have both) if you have either of these files copy them to "
CLASSES_LIST" then restart the mud.`1");
write_shutdown_file(NULL);
exit_error( 1 , "do_read_classes", "classes list not found");
}else{
ch->printf("Couldn't find " CLASSES_LIST " to read in! - read the inote for more detail!\r\n");
autonote(NOTE_SNOTE, "do_read_classes()", "Problems finding class table file", "code cc: imm",
"Couldn't find " CLASSES_LIST " to read into the class table!`1"
"The mud will not be able to automatically start up if this file "
"is not there next time the mud starts... the recommended fix to "
"this condition (`RAssuming there is NO corruption to the class "
"information in the currently running mud`x) is to make sure there "
"is enough free diskspace to store this file then get the mud to "
"recreate this file using the write_classes command.`1`1"
"It is important that everyone does not do this, it should be done "
"only once, and it is best if it is not done if the mud has "
"already rebooted since the date of this note... if you dont "
"understand what this note is explaining, it is best you leave it.",
true);
}
}
}else{ // CLASSES_LIST was found, read in
bool morefile=true;
char *readword;
if(runlevel==RUNLEVEL_BOOTING){
// *** Mark the existing table as empty - since we are loading everything in
// - This could cause class corruption if done when people are online...
// that is why it can be only done if one is online or during booting.
class_table[0].name=NULL;
}
for ( i=0; i<MAX_CLASS; i++)
{
class_table[i].already_loaded=false;
}
// 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, "######NAME")){
// get the name of the class
readword=fread_string(fp);
// check if the class hasn't already been loaded - no duplicates
load_index=class_exact_lookup(readword);
if(load_index==-1){ // if not found, allocate a space for a new class
// find the end of the class table
for ( load_index=0; !IS_NULLSTR(class_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_CLASS){
bugf("Too many classes (%d) trying to load class '%s' from %s, increase MAX_CLASS - aborting",
i, readword, CLASSES_LIST);
ch->printlnf("Too many classes (%d) trying to load class '%s' from %s, increase MAX_CLASS - aborting",
i, readword, CLASSES_LIST);
flush_char_outbuffer(ch);
write_shutdown_file(NULL);
exit_error( 1 , "do_read_classes", "too many classes");
}
class_table[load_index+1].name=NULL; // mark the new end of the table
class_table[load_index].name=str_dup(readword);
}
// prevent duplicate classes in the file
if(class_table[load_index].already_loaded){
bugf("Duplicate class '%s' in %s, aborting",
readword, CLASSES_LIST);
ch->printlnf("Duplicate class '%s' in %s, aborting",
readword, CLASSES_LIST);
flush_char_outbuffer(ch);
write_shutdown_file(NULL);
exit_error( 1 , "do_read_classes", "duplicate class");
}
// initialise the pose data
for(i=0; i<MAX_LEVEL; i++){
class_table[load_index].pose_self[i]=str_dup("");
class_table[load_index].pose_others[i]=str_dup("");
}
// read in the data
GIO_LOAD_RECORD(class_type, &class_table[load_index], fp);
class_table[load_index].already_loaded=true;
count++;
}else{// unexpected file format
bugf("Unexpected fileformat in '%s' - found '%s' "
"expecting '######NAME'", CLASSES_LIST , readword);
ch->printf( "Unexpected fileformat in '%s' - found '%s' "
"expecting '######NAME'\r\n", CLASSES_LIST , readword);
flush_char_outbuffer(ch);
write_shutdown_file(NULL);
do_abort();
return;
}
}
}
fclose( fp );
ch->printlnf("Finished reading class_table from " CLASSES_LIST ". (read in %d)", count);
logf("Finished reading class_table from " CLASSES_LIST ". (read in %d)", count);
for ( i=0; !IS_NULLSTR(class_table[i].name); i++){
if(!class_table[i].already_loaded){
ch->printlnf("Class '`R%s`x' wasn't found in file to load in!, after next reboot it wont be there.",
class_table[i].name);
bugf("Class '%s' wasn't found in file to load in.",
class_table[i].name);
}
}
}
fpReserve = fopen( NULL_FILE, "r" );
// create a new the classnames_types
{
int i;
logf("Creating a new class lookup table in classnames_types[]");
// first attempt exact match
for (i=0; !IS_NULLSTR(class_table[i].name); i++)
{
classnames_flags[i].name=str_dup(class_table[i].name);
classnames_flags[i].bit=1<<i;
classnames_flags[i].settable=true;
}
classnames_flags[i].name=NULL; // mark end of table
}
return;
}
/**************************************************************************/
char *tochar_spellfunction(SPELL_FUN *psp)
{
sh_int sn;
for ( sn = 0; !IS_NULLSTR(spellpairs_table[sn].name); sn++ )
{
if(spellpairs_table[sn].psp==psp){
return spellpairs_table[sn].name;
}
}
bugf("tochar_spellfunction: spell function not found, returning "
"\"spell_null\" sn=%d",sn);
return "spell_null";
}
/**************************************************************************/
sh_int spellfunctionindex_fromchar(const char *name)
{
sh_int sn;
for ( sn = 0; !IS_NULLSTR(spellpairs_table[sn].name); sn++ )
{
if(!str_cmp(spellpairs_table[sn].name,name)){
return sn;
}
}
return -1;
}
/**************************************************************************/
// write the SpellFunction section
void skilltype_write_spellfunction(gio_type *gio_table, int tableIndex,
void *data, FILE *fp)
{
skill_type * psk;
psk= (skill_type*) data;
if( psk->spell_fun!= NULL
&& psk->spell_fun!=spell_null)
{
fprintf(fp, "%s %s~\n",
gio_table[tableIndex].heading,
tochar_spellfunction(psk->spell_fun));
}
}
/**************************************************************************/
// read the spell function in
void skilltype_read_spellfunction(gio_type *, int, void *data, FILE *fp)
{
skill_type * psk;
char *pstr;
psk= (skill_type*) data;
pstr=fread_string(fp);
psk->spell_function_index=spellfunctionindex_fromchar(pstr);
if(psk->spell_function_index>-1){
psk->target=spellpairs_table[psk->spell_function_index].target;
psk->spell_fun= spellpairs_table[psk->spell_function_index].psp;
}else{
psk->spell_fun=spell_null;
psk->target=TAR_IGNORE;
}
free_string(pstr);
}
/**************************************************************************/
// writes to the fp a table for cross referencing spell names and
// their spell function, this assumes that the function is named based
// of the actual spell name in the table
void write_dynlookup_table(FILE *fp)
{
sh_int sn;
char buf[MIL];
char type[MIL];
fprintf(fp,"dynlookup_type dynlookup_table[]=" BRACKET_OPEN "\n");
for ( sn = 0; sn < MAX_SKILL; sn++ )
{
if ( skill_table[sn].name == NULL )
break;
sprintf(buf,"\"%s\",",skill_table[sn].name);
// generate the type
if(IS_SPELL(sn)){
sprintf(type,"DYNTYPE_SPELL");
}else if(IS_REALM(sn)){
sprintf(type,"DYNTYPE_REALM");
}else if(IS_SKILL(sn)){
sprintf(type,"DYNTYPE_SKILL");
}else{
sprintf(type,"DYNTYPE_UNDEFINED");
}
fprintf(fp, "\t` %-25s %s, &gsn_%s },\n",
buf, type, underscore_word(skill_table[sn].name)
);
}
fprintf(fp,"\t` NULL, DYNTYPE_UNDEFINED, NULL}\n");
fprintf(fp,"};\n\n");
}
/**************************************************************************/
void write_spellpairs_table(FILE *fp)
{
sh_int sn;
fprintf(fp,"dynspell_type spellpairs_table[]="BRACKET_OPEN"\n");
fprintf(fp, "\t` \"spell_null\", spell_null },\n");
for ( sn = 0; !IS_NULLSTR(skill_table[sn].name); sn++ )
{
if(skill_table[sn].spell_fun== NULL
|| skill_table[sn].spell_fun==spell_null)
continue;
fprintf(fp, "\t` \"spell_%s\", spell_%s, false},\n",
underscore_word(skill_table[sn].name),
underscore_word(skill_table[sn].name));
}
fprintf(fp,"\t` \"\", NULL}\n");
fprintf(fp,"};\n");
}
/**************************************************************************/
// this function is no longer used, but included because someone one day
// may find it useful - Kal, Nov 03
void do_write_dynamic_include(char_data *ch, char *)
{
FILE *fp;
fclose( fpReserve );
#ifndef WIN32
if ( ( fp = fopen( DYNAMIC_INCLUDE, "w" ) ) == NULL )
{
bugf("do_write_dynamic_include(): fopen '%s' for write - error %d (%s)",
DYNAMIC_INCLUDE, errno, strerror( errno));
ch->printf("An error occured! writing to " DYNAMIC_INCLUDE "\r\n");
}
#else
if ( ( fp = fopen( SRC_DIR DYNAMIC_INCLUDE, "w" ) ) == NULL )
{
bugf("do_write_dynamic_include(): fopen '%s' for write - error %d (%s)",
SRC_DIR DYNAMIC_INCLUDE, errno, strerror( errno));
ch->printf("An error occured! writing to " SRC_DIR DYNAMIC_INCLUDE "\r\n");
}
#endif
else
{
fprintf(fp,
"// Dyntable.cpp - The Dawn of time dynamically written cpp file.\n"
"// used for dynamic table lookups.\n"
"#include \"include.h\"\n"
"#include \"dynamics.h\"\n"
"#include \"magic.h\"\n\n");
write_dynlookup_table(fp);
write_spellpairs_table(fp);
fclose( fp );
ch->println("Done.");
}
fpReserve = fopen( NULL_FILE, "r" );
return;
}
/**************************************************************************/
// initialises the skill table with the info in dynlookup_table stored in
// dyntable.cpp
void init_skilltable(void)
{
static skill_type skill_zero;
sh_int sn;
int count=0;
skill_zero.spell_fun=spell_null;
logf("init_skilltable: initialising table...");
// add all our entries
for ( sn = 0; !IS_NULLSTR(dynlookup_table[sn].name); sn++ )
{
skill_table[sn]=skill_zero;
skill_table[sn].name=str_dup(dynlookup_table[sn].name);
skill_table[sn].pgsn=dynlookup_table[sn].pgsn;
if ( skill_table[sn].pgsn != NULL )
*skill_table[sn].pgsn = sn;
count++;
}
// mark the end of the table
skill_table[sn].name=&str_empty[0];
logf("init_skilltable: %d entr%s added.", count, count==1?"y":"ies");
}
/**************************************************************************/
// a basic linked list function which sorts insertion by name alphabetically
void addlist(name_linkedlist_type **list,char *name, int tag, bool duplicates, bool reversed)
{
//handle the easy cases first
name_linkedlist_type *plist,*newnode, *prev;
int val=0;
if(IS_NULLSTR(name))
return;
newnode=new name_linkedlist_type;
newnode->name=str_dup(name);
newnode->tag=tag;
newnode->count=1;
// logf("added %s", name);
plist=*list;
prev=NULL;
if(plist){// insert sorted into the list
// loop thru until
while(plist)
{
val=strcmp(plist->name,name);
if(reversed){
if(val<0) // later
break;
}else{
if(val>=0) // earlier
break;
}
prev=plist;
plist=plist->next;
}
// add it into the list if not a duplicate
if(duplicates || val)
{
if(prev){
newnode->next=prev->next;
prev->next=newnode;
}else{
newnode->next=*list;
*list=newnode;
}
}else{
plist->count++; // increase the count of number of duplicates
// deallocate the memory
free_string(newnode->name);
delete newnode;
}
}else{// new list, just add us at the top
newnode->next=NULL;
*list=newnode;
}
};
/**************************************************************************/
// loads in the skill table using the following steps
// - Reading in the skill file, making linked lists of the
// different skills/spells/realms etc while loading, sorting them
// alphabetically while loading in. These linked lists only hold the
// name of the entry, if this is successful it continues.
// - Clears the skill_table
// - These names are then copied into the skill table and the first_spell
// and last_spell short ints are assigned. Also in the copy it leaves
// space after the bottom spell, and the first skill allowing the addition
// of a few spells without rebooting. (after the next reboot there
// will be more space)
// - The gsn pointers are then copied, and their values are set.
// - The skill text file is then reread, this time copying in the information
// from the text file into the appropriate places
void do_loadskilltable(char_data *ch, char *)
{
FILE *fp;
int count, index;
skill_type skill_prescan;
int sn;
// declare and setup our reading in lists.
name_linkedlist_type *list[10], *plist;
for(count=0;count<10; count++){
list[count]=NULL;
}
count=0;
static skill_type skill_zero;
skill_zero.spell_fun=spell_null;
// read in the skill text file making sorted linked lists.
//#define SKTYPE_UNDEFINED 0
//#define SKTYPE_SPELL 1
//#define SKTYPE_SKILL 2
//#define SKTYPE_OTHER 3
logf("Doing prescan of " SKILLS_FILE "... for skills, spells etc");
// open the file for input
fclose( fpReserve );
if ( ( fp = fopen( SKILLS_FILE, "r" ) ) == NULL )
{
bugf("do_loadskilltable(): fopen '%s' for read - error %d (%s)",
SKILLS_FILE, errno, strerror( errno));
ch->printf("An error occured trying to open " SKILLS_FILE" for reading!\n");
write_shutdown_file(NULL);
do_abort();
}
{
bool morefile=true;
char *readword;
char buf[MIL];
while (morefile && !feof(fp)) {
readword= fread_word(fp);
if (!str_cmp(readword, "EOF") || !str_cmp(readword, "EOF~")){
morefile=false;
}else{
if(!str_cmp(readword, "######NAME")){
readword=fread_string(fp);
sprintf(buf,"%s", readword);
// load the skill entry for the prescan
GIO_LOAD_RECORD(skill_type, &skill_prescan, fp);
if(str_cmp(readword, "reserved"))
{
count++;
// add to list, tag of skill_prescan.type, allow duplicates = true
addlist(&list[skill_prescan.type],readword, 0, true, false);
// different types
// {"undefined", SKTYPE_UNDEFINED, true},
// {"spell", SKTYPE_SPELL, true},
// {"skill", SKTYPE_SKILL, true},
// {"other", SKTYPE_OTHER, true},
}
}else{// unexpected file format
bugf("Unexpected fileformat in '%s' - found '%s' "
"expecting '######NAME'", SKILLS_FILE, readword);
write_shutdown_file(NULL);
do_abort();
return;
}
}
}
logf("Prescan found %d skill%s.", count, count==1?"":"s");
if(count+MAX_RESERVED_SPELL_SPACE>=MAX_SKILL){
logf("Prescan +%d found more skills than MAX_SKILL, increase MAX_SKILL above %d\n",
MAX_RESERVED_SPELL_SPACE, MAX_SKILL);
logf("The +%d is due to space allocation to allow adding of spells while online.\n",
MAX_RESERVED_SPELL_SPACE);
write_shutdown_file(NULL);
exit_error( 1 , "do_loadskilltable", "prescan found more skills than allocated storage space.");
}
fclose( fp );
// manually add all the skills/spells not found in the prescan
// that values exist in the gsn table
// - this means that no manual editing is required to add the skill
for ( sn = 0; !IS_NULLSTR(dynlookup_table[sn].name); sn++ )
{
if(dynlookup_table[sn].type==DYNTYPE_SKILL){
// add it to the skill list without duplicates
addlist(&list[SKTYPE_SKILL], dynlookup_table[sn].name, SKTYPE_SKILL, false, false);
}
if(dynlookup_table[sn].type==DYNTYPE_SPELL){
// add it to the spell list without duplicates
addlist(&list[SKTYPE_SPELL], dynlookup_table[sn].name, SKTYPE_SPELL, false, false);
}
}
{
static skill_type skill_zero;
skill_zero.spell_fun=spell_null;
index=0;
for(count=0;count<10; count++){
if(list[count]==NULL)
continue;
// logf("list #%d",count);
if(count==SKTYPE_SPELL)
{
FIRST_SPELL=index;
}
for(plist=list[count];plist; plist=plist->next){
// logf("--->>%d <- %s", index, plist->name);
// check for duplicate already.
skill_table[index+1].name=NULL;
if(skill_exact_lookup(plist->name)!=-1){
bugf("DUPLICATE ENTRY '%s' - last entry used.", plist->name);
}else{
skill_table[index]=skill_zero; // clear entry fully
skill_table[index].name=plist->name; // no need to str_dup it
// because addlist str_dup's
// set the default type in order to work with loading the gsn
// table entries, and getting the correct type when they
// haven't manually been added to the skills.txt file
if(plist->tag){
logf("Detected '%s' not in prescan, but in dyntable - added.",
plist->name);
skill_table[index].type=plist->tag;
// bind the spell function if possible
if(plist->tag==SKTYPE_SPELL){
char possible_spellname[MIL];
// get the lowercase with spaces converted to underscores version
sprintf(possible_spellname,"spell_%s", underscore_word(plist->name));
skill_table[index].spell_function_index=spellfunctionindex_fromchar(possible_spellname);
if(skill_table[index].spell_function_index>-1){
logf("SPELL '%s' - binding spell func %s",
plist->name, possible_spellname);
skill_table[index].target=spellpairs_table[skill_table[index].spell_function_index].target;
skill_table[index].spell_fun= spellpairs_table[skill_table[index].spell_function_index].psp;
}else{
logf("SPELL '%s' couldn't find %s, binding spell_null instead!",
plist->name, possible_spellname);
skill_table[index].spell_fun=spell_null;
skill_table[index].target=TAR_IGNORE;
}
}
}
index++;
}
}
// add extra space for adding new spells
if(count==SKTYPE_SPELL)
{
int space;
LAST_SPELL=index;
for(space=0;space<MAX_RESERVED_SPELL_SPACE; space++){
skill_table[index].name=str_dup("reserved");
index++;
}
}
}
logf("Marked end of skill table at index %d.", index);
skill_table[index].name=NULL; // mark it end of the table
TOP_SKILL=index;
}
}
fpReserve = fopen( NULL_FILE, "r" );
logf("Finished prescan of skill file, assigning gsn's");
// copy the gsn pointers and assign them
{
sh_int index;
count=0;
for ( sn = 0; !IS_NULLSTR(dynlookup_table[sn].name); sn++ )
{
index=skill_lookup(dynlookup_table[sn].name);
if(index>=0){
skill_table[index].pgsn=dynlookup_table[sn].pgsn;
if (skill_table[index].pgsn)
{
*skill_table[index].pgsn = index;
count++;
}
}else{
logf("Ignoring dyntable's '%s' gsn since wasn't found in prescan.",
dynlookup_table[sn].name);
}
}
logf("%d gsn%s assigned, loading in all data from skill file now.",
count, count==1?"":"s");
}
// load in the whole skill table now
// open the file for input
fclose( fpReserve );
if ( ( fp = fopen( SKILLS_FILE, "r" ) ) == NULL )
{
bugf("do_loadskilltable(): fopen '%s' for read - error %d (%s)",
SKILLS_FILE, errno, strerror( errno));
ch->printf("An error occured trying to open " SKILLS_FILE" for reading!\n");
write_shutdown_file(NULL);
do_abort();
}
{
bool morefile=true;
char *readword;
char buf[MIL];
sh_int sn;
count=0;
while (morefile && !feof(fp)) {
readword= fread_word(fp);
if (!str_cmp(readword, "EOF") || !str_cmp(readword, "EOF~")){
morefile=false;
}else{
if(!str_cmp(readword, "######NAME")){
readword=fread_string(fp);
if(str_cmp(readword, "reserved"))
{
sprintf(buf,"%s", readword);
// lookup the skill and read it in.
sn=skill_lookup(buf);
if(sn>=0){
GIO_LOAD_RECORD(skill_type, &skill_table[sn], fp);
count++;
// count the spell functions in use
if(skill_table[sn].spell_function_index>=0){
spellpairs_table[skill_table[sn].spell_function_index].count++;
}
// this isn't the most efficient way, but I can't be bothered
// writing something more efficient
if(!IS_NULLSTR(race_get_races_set_for_n_array(skill_table[sn].race_restrict_n))){
SET_BIT(skill_table[sn].flags, SKFLAGS_USE_RACE_RESTRICTIONS);
}
}else{
// if we can't find the entry in the skill table then
// the skill file has been modified in the 2 second
// period or something it corrupting info, therefore abort
bugf("do_loadskilltable: Something very screwy is going on!\n"
"a NEW entry ('%s') have appeared in the skill file '%s' since the prescan!n"
"Aborting!", buf,SKILLS_FILE);
write_shutdown_file(NULL);
do_abort();
return;
}
}else{
// ignore the reserved record
GIO_LOAD_RECORD(skill_type, &skill_prescan, fp);
}
}else{// unexpected file format
bugf("Unexpected fileformat in '%s' - found '%s' "
"expecting '######NAME'", SKILLS_FILE, readword);
write_shutdown_file(NULL);
exit_error( 1 , "do_loadskilltable", "unexpected file format");
return;
}
}
}
logf("Finished reading skill file. (read in %d total)", count);
}
fclose( fp );
fpReserve = fopen( NULL_FILE, "r" );
return;
}
/**************************************************************************/
// Output the skill_table to disk
void do_write_skills(char_data *ch, char *)
{
FILE *fp;
sh_int sn;
logf("Writing skill table to " SKILLS_FILE ".write ...");
fclose( fpReserve );
if ( ( fp = fopen( SKILLS_FILE".write", "w" ) ) == NULL )
{
bugf("do_write_skills(): fopen '%s' for write - error %d (%s)",
SKILLS_FILE".write", errno, strerror( errno));
ch->printf("An error occured opening " SKILLS_FILE".write for writing!\r\n");
autonote(NOTE_SNOTE, "do_write_skills()", "Problems saving skill table", "code cc: imm",
"An error occured opening " SKILLS_FILE".write for writing!\r\n", true);
}else{
// LOOP thru everything in the table, writing it
for ( sn = 0; !IS_NULLSTR(skill_table[sn].name); sn++ )
{
if(!str_cmp("reserved", skill_table[sn].name)){
continue;
}
fprintf(fp,"######NAME %s~\n", skill_table[sn].name);
GIO_SAVE_RECORD(skill_type, &skill_table[sn], fp, NULL);
fprintf(fp,"\n");
}
fprintf(fp, "EOF~\n");
int bytes_written=fprintf(fp, "EOF~\n");
fclose( fp );
if( bytes_written != str_len("EOF~\n") ){
bugf("do_write_skills(): fprintf to '%s' incomplete - error %d (%s)",
SKILLS_FILE".write", errno, strerror( errno));
bugf("Incomplete write of " SKILLS_FILE".write, write aborted - check diskspace!");
ch->printf("Incomplete write of " SKILLS_FILE".write, write aborted - check diskspace!\r\n");
autonote(NOTE_SNOTE, "do_write_skills()", "Problems saving skill table", "code cc: imm",
"Incomplete write of " SKILLS_FILE ".write, write aborted - check diskspace!\r\n", true);
}else{
ch->printf("Finished writing skill table to "SKILLS_FILE ".write\r\n");
logf("Finished writing skill table to "SKILLS_FILE ".write");
ch->printf("Renaming old " SKILLS_FILE " to " SKILLS_FILE ".bak\r\n");
logf("Renaming old " SKILLS_FILE " to " SKILLS_FILE ".bak");
unlink(SKILLS_FILE ".bak");
rename(SKILLS_FILE , SKILLS_FILE ".bak");
ch->printf("Renaming new " SKILLS_FILE ".write to " SKILLS_FILE "\r\n");
logf("Renaming new " SKILLS_FILE ".write to " SKILLS_FILE);
unlink(SKILLS_FILE );
rename(SKILLS_FILE ".write", SKILLS_FILE );
logf("Finished writing skill table.");
}
}
fpReserve = fopen( NULL_FILE, "r" );
}
/**************************************************************************/