/**************************************************************
 * Locker Code                                                *
 *                                                            *
 *        /|\      Life always travels forward.               *
 *       /-|-\     Always look ahead, never back.             *
 *      /--|--\    To do so is to loose sight of the future,  *
 *     /---|---\   while travelling on the highways of life.  *
 *                 Code by Aidan 5/4/2006                     *
 *                                                            *
 *          This code has been written as freeware            *
 *          and may be used without any limitations           *
 *          of any sort whatsoever. Contact me at             *
 *          immortal@chaos-ascending.com if you               *
 *          need help or have questions. --Aidan              *
 *                                                            *
 **************************************************************/
 

/*
 * Just an addendum for use in a helpfile if you wish.
 *
 * The Locker system has been designed and setup to
 * save items that have not been setup on a reset
 * in rooms that have been flagged as a "locker" room.

 * Just a side note on this. To minimize the amount of
 * objects being saved over reboots in every locker room
 * the max objects has been limited to 20.
 *
 * This can be adjusted by changing MAX_ITEM_SAVE which
 * is defined below.
 *
 * Player corpses will be saved throughout the world,
 * no matter where they are or how many items are in
 * the room. This is helpful if you  are in development
 * and suffer a crash, etc.
 * I suggest putting a call to save_lockers( ) in the 
 * raw_kill function located in fight.c for Merc/Rom
 * derivitives.
 *
 * An important thing to remember. A call to save_lockers( )
 * should be included in copyover, reboot, shutdown, etc, to
 * insure proper saving of locker things on normal game resets.
 *
 * You have to call load_lockers in int boot_db located in db.c
 * so that these items are re-loaded when you start the game up.
 * This should optimally be done near the end when the function
 * is starting to finish up the last minute boot up items and 
 * before copyover_recover. The reason for this is we can't sit 
 * and load a lot of items that don't exist yet into rooms that
 * don't exist yet.
 */


#include "merc.h"

#define MAX_NEST 100
#define MAX_ITEM_SAVE 20
#define LOCKER_FILE "../area/lockers.txt"

void do_save_lockers(CHAR_DATA *ch, char *argument)
{
    save_lockers();
    send_to_char("Lockers have been saved.\n\r",ch);
    return;
}

void save_lockers( )
{
    FILE *LockerFile;
    ROOM_INDEX_DATA *room;
    RESET_DATA *reset;
    
    /* Close the reserve file */
    fclose(fpReserve);
    /* Open the Locker File for saving */
    LockerFile = fopen(LOCKER_FILE,"w");

    /* Okay, lets loop through all of the rooms on the game */
    for(vnum = 0; vnum < 32768; vnum++)
    {
        if((room = get_room_index(vnum)) != NULL)
        {
            OBJ_DATA *obj = NULL;
            int count = 0;

            for(obj = room->contents; obj; obj = obj->next_content)
            {
                bool hasReset = FALSE;

                /* Are we being carried? Are we in a valid room? */
                if(obj->carried_by)
                    continue;

                /* Is this a Locker Room? Is it a player corpse? */
                if(!IS_SET(room->room_flags, ROOM_LOCKER) && obj->item_type != ITME_CORPSE_PC)
                    continue;

                /* Okay, so now let's see if the item is on a reset */
                for(reset = room->reset_first; reset; reset = reset->next)
                {
                    if(((reset->command == 'O'
                    || reset->command == 'P')
                    && reset->vnum == obj->pIndexData->vnum)
                    || (reset->command == 'E'
                    && reset->vnum == obj->pIndexData->vnum))
                    {
                        hasReset = TRUE;
                        break;
                    }
                }

                /* No saving items on reset */
                if(hasReset)
                    continue;

                /* No saving items above MAX_ITEM_SAVE */
                if(++count > MAX_ITEM_SAVE && obj->item_type != ITEM_CORPSE_PC)
                    continue;

                /* Write the obj to file */
                fwrite_locker(obj,LockerFile,0);
            }
        }
    }
    /* Print the end statement */
    fprintf(LockerFile,"#END\n");
    /* Close the LockerFile */
    fclose(LockerFile);
    /* Re-Open the reserve file */
    fpReserve = fopen(NULL_FILE,"r");
    return;
}

/*
 * fwrite_locker is used to write the locker
 * data out to file. This file is called on
 * boot to replace stored items.
 */
void fwrite_locker(OBJ_DATA *obj, FILE *fp, int iNest)
{
    EXTRA_DESCR_DATA *ed;
    AFFECT_DATA *af;

    if(obj->next_content && iNet > 0)
        fwrite_locker(obj->next_content,fp,iNest);

    fprintf(fp,"#O\n"); // Object ID
    fprintf(fp,"Vnum %d\n", obj->pIndexData->vnum); // Object Vnum
    if(obj->enchanged) // Enchanted?
        fprintf(fp,"Enchanged\n");
    fprintf(fp,"Nest %d\n",iNest); // Nest

    if(obj->name != obj->pIndexData->name) // Name
        fprintf(fp,"Name %s~\n",obj->name);
    if(obj->short_descr != obj->pIndexData->short_descr) // Short Desc
        fprintf(fp,"ShD %s~\n",obj->short_descr);
    if(obj->description != obj->pIndexData->description) // Long Desc
        fprintf(fp,"Desc %s~\n",obj->description);
    fprintf(fp,"Wear %lld\n",obj->wear_loc); // Wear Locations
    fprintf(fp,"Lev %d\n",obj->level); // Level
    if(obj->timer != 0)
        fprintf(fp,"Time %d\n",obj->timer); // Timer
    if(obj->in_room)
        fprintf(fp,"InRm %d\n",obj->in_room->vnum); // room vnum
    if(obj->value[0] != obj->pIndexData->value[0] // V Slots
     || obj->value[1] != obj->pIndexData->value[1]
     || obj->value[2] != obj->pIndexData->value[2]
     || obj->value[3] != obj->pIndexData->value[3]
     || obj->value[4] != obj->pIndexData->value[4] )
        fprintf(fp,"Val  %d %d %d %d %d\n",
                                           obj->value[0],
                                           obj->value[1],
                                           obj->value[2],
                                           obj->value[3],
                                           obj->value[4]);
     
     for(af = obj->affected; af; af = af->next) // Affects
     {
         if(!skill_table[af->type].name)
             continue;
         fprintf(fp,"AffD '%s' %d %d %d %d %d\n",
                                                 skill_table[af->type].name,
                                                 af->level,
                                                 af->duration,
                                                 af->modifier,
                                                 af->location,
                                                 af->bitvector);
     }

     for(ed = obj->extra_descr; ed; ed = ed->next) // Extended Descs
        fprintf(fp,"ExDe %s~ %s~\n",ed->keyword, ed->description);
     fprintf(fp,"End\n\n");

     if(obj->contains)
         fwrite_locker(obj->contains,fp,iNest+1);
     return;
}

void load_lockers( )
{
    FILE *LockerFile;

    fclose(fpReserve);
    if((LockerFile = fopen(LOCKER_FILE,"r")) == NULL)
    {
        log_string("No locker file found.");
        fclose(LockerFile);
        fpReserve = fopen(NULL_FILE,"r");
        return;
    }

    for( ; ; )
    {
        char *letter;
        char letter = fread_letter(LockerFile);

        if(letter != '#')
        {
            bug("Load_Lockers: # not found.",0);
            break;
        }

        word = fread_word(LockerFile);

        if(!str_cmp(word,"O"))
            fread_locker_obj(LockerFile);
        else if(!str_cmp(word,"END"))
            break;
        else
        {
            bug("Load_Lockers: bad section.",0);
            break;
        }
    }
    fclose(LockerFile);
    fpReserve = fopen(NULL_FILE,"r");
    return;
}

void fread_locker_obj(FILE *fp);
{
    OBJ_DATA *obj = NULL;
    char *word = NULL;
    int iNest = 0;
    bool fMatch = FALSE;
    bool fNest = FALSE;
    bool fVnum = FALSE;
    bool first = TRUE;
    bool new_format = FALSE;
    bool make_new = FALSE;

    word = feof(fp) ? (char *)"End" : fread_word(fp);

    if(!str_cmp(word,"Vnum"))
    {
        int vnum = fread_number(fp);
        first = FALSE;

        if(get_obj_index(vnum) == NULL)
            bug("Fread_obj: bad vnum %d.",vnum);
        else
        {
            obj = create_object(get_obj_index(vnum),-1);
            new_format = TRUE;
        }
    }

    if(!obj)
    {
        obj = new_obj( );
        obj->name = str_dup("");
        obj->short_descr = str_dup("");
        obj->description = str_dup("");
    }
    fVnum = TRUE;

    for( ; ; )
    {
        fMatch = FALSE;

        if(first)
            first = FALSE;
        else
            word = feof(fp) ? (char *)"End" : fread_word(fp);

        switch(UPPER(word[0]))
        {
        case 'A':
            if(!str_cmp(word,"AffD"))
            {
                AFFECT_DATA *af;
                char *skill;
                int sn;

                af = new_affect( );
                skill = fread_word(fp);
                sn = skill_lookup(skill);

                if(sn < 0)
                    bug("Fread_obj: unknown skill.",0);
                else
                {
                    paf->type = sn;
                    paf->level = fread_number(fp);
                    paf->duration = fread_number(fp);
                    paf->modifier = fread_number(fp);
                    paf->location = fread_number(fp);
                    paf->bitvector = fread_number(fp);
                    paf->next = obj->affected;
                    obj->affected = af;
                    fMatch = TRUE;
                    break;
                }
            }
            break;

        case 'E':
            if(!str_cmp(word,"Enchanged"))
            {
                obj->enchanted = TRUE;
                fMatch = TRUE;
                break;
            }
            if(!str_cmp(word,"ExDe"))
            {
                EXTRA_DESCR_DATA *ed;

                ed = new_extra_descr( );
                ed->keyword = fread_string(fp);
                ed->description = fread_string(fp);
                ed->next = obj->extra_descr;
                obj->extra_descr = ed;
                fMatch = TRUE;
                break;
            }
            if(!str_cmp(word,"End"))
            {
                if(!fNest || !fVnum || !obj->pIndexData)
                {
                    bug("Fread_obj: incomplete object.",0);
                    free_obj(obj);
                    return;
                }
                else
                {
                    if(!new_format)
                    {
                        obj->next = object_list;
                        object_list = obj;
                        obj->pIndexData->count++;
                    }

                    if(make_new)
                    {
                        int wear = obj->wear_loc;
                        extract_obj(obj);
                        obj = create_object(obj->pIndexData,0);
                        obj->wear_loc = wear;
                    }
                    if(iNest == 0 || rgObjNest[iNest] == NULL)
                        obj_to_room(obj,obj->in_room);
                    else
                        obj_to_obj(obj,rgObjNest[iNest-1]);
                    return;
                }
            }
            break;
            
        case 'D':
            KEY("Desc", obj->description, fread_string(fp));
            break;

        case 'I':
            KEY("InRm", obj->in_room, fread_number(fp));
            break;

        case 'L':
            KEY("Lev", obj->level, fread_number(fp));
            break;

        case 'N':
            KEY("Name", obj->name, fread_string(fp);
            if(!str_cmp(word,"Nest"))
            {
                iNest = fread_number(fp);

                if(iNest < 0 || iNest >= MAX_NEST)
                    bug("Fread_obj: bad nest %d.",iNest);
                else
                {
                    rgObjiNest[iNest] = obj;
                    fNest = TRUE;
                }
                fMatch = TRUE;
                break;
            }
            break;

        case 'S':
            KEY("ShD", obj->short_descr, fread_string(fp);
            break;

        case 'T':
            KEY("Time", obj->timer, fread_number(fp));
            break;

        case 'V':
            if(!str_cmp(word,"Val"))
            {
                int i;

                for(i = 0; i < 5; i++)
                    obj->value[i] = fread_number(fp);
                fMatch = TRUE;
                break;
            }
            break;

            if(!str_cmp(word,"Vnum"))
            {
                int vnum = fread_number(fp);

                if((obj->pIndexData = get_obj_index(vnum)) == NULL)
                    bug("Fread_obj: bad vnum %d.",vnum);
                else
                    fVnum = TRUE;
                fMatch = TRUE;
                break;
            }
            break;

        case 'W':
            KEY("Wear", obj->wear_loc, fread_number(fp));
            break;
        }

        if(!fMatch)
        {
            bug("Fread_obj: no match.",0);
            fread_to_eol(fp);
        }
    }
    return;
}
