muddy/
muddy/CVS/
muddy/area/
muddy/area/CVS/
muddy/clans/CVS/
muddy/classes/CVS/
muddy/doc/
muddy/doc/CVS/
muddy/etc/CVS/
muddy/etc/i3/
muddy/etc/i3/CVS/
muddy/imc/CVS/
muddy/lang/CVS/
muddy/licenses/CVS/
muddy/msgdb/CVS/
muddy/new/CVS/
muddy/notes/
muddy/player/
muddy/races/CVS/
muddy/religions/CVS/
muddy/src/CVS/
muddy/src/comm/CVS/
muddy/src/db/CVS/
muddy/src/intermud/
muddy/src/intermud/CVS/
muddy/src/irc/CVS/
muddy/src/olc/CVS/
/* $Id: stock.c,v 1.666 2004/09/20 10:49:53 shrike Exp $                              */
 
/************************************************************************************
 *    Copyright 2004 Astrum Metaphora consortium                                    *
 *                                                                                  *
 *    Licensed under the Apache License, Version 2.0 (the "License");               *
 *    you may not use this file except in compliance with the License.              *
 *    You may obtain a copy of the License at                                       *
 *                                                                                  *
 *    http://www.apache.org/licenses/LICENSE-2.0                                    *
 *                                                                                  *
 *    Unless required by applicable law or agreed to in writing, software           *
 *    distributed under the License is distributed on an "AS IS" BASIS,             *
 *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.      *
 *    See the License for the specific language governing permissions and           *
 *    limitations under the License.                                                *
 *                                                                                  *
 ************************************************************************************/


#include <sys/types.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "merc.h"
#include "db/db.h"
#include "stock.h"
#include "conquer.h"

extern OBJ_DATA * fread_obj  (CHAR_DATA * ch, FILE * fp);
extern void fwrite_obj(CHAR_DATA * ch, OBJ_DATA * obj, FILE * fp, int iNest, bool is_quit, const char * header);

static void clanstock_help ( CHAR_DATA *ch, const char *argument ); 
static void clanstock_list ( CHAR_DATA *ch, const char *argument ); 
static void clanstock_empty ( CHAR_DATA *ch, const char *argument ); 
static void clanstock_clear ( CHAR_DATA *ch, const char *argument ); 
static void clanstock_put ( CHAR_DATA *ch, const char *argument ); 
static void clanstock_get ( CHAR_DATA *ch, const char *argument ); 
static void clanstock_stat ( CHAR_DATA *ch, const char *argument ); 
static THING_DATA * new_thing_data ( void );
static void free_thing_data ( THING_DATA * thing );

static CMD_DATA clanstock_cmd_table[] = {
//    name        do_fn             min_pos       min_level       qp  gold min_clanstatus  extra      
    { "help",     clanstock_help,   POS_DEAD,     1,              0,  0,   CONQ_ALL,       0},
    { "list",     clanstock_list,   POS_DEAD,     1,              0,  0,   CONQ_ALL,       0},
    { "put",      clanstock_put,    POS_DEAD,     1,              0,  0,   CONQ_ALL,       0},
    { "get",      clanstock_get,    POS_DEAD,     1,              0,  0,   CONQ_ALL,       0},
    { "empty",    clanstock_empty,  POS_DEAD,     1,              0,  0,   CONQ_ELITE,     0},
    { "clear",    clanstock_clear,  POS_DEAD,     1,              0,  0,   CONQ_ELITE,     0},
    { "stat",     clanstock_stat,   POS_DEAD,     93,             0,  0,   CONQ_ALL,       0},
    { NULL }
};

// local functions needed cause there can be different stocks (clan, personal etc);
static void show_stock_list (CHAR_DATA * ch, STOCK_DATA * stock);
static void put_obj_stock (CHAR_DATA * ch, const char * argument, STOCK_DATA * stock);
static void get_obj_stock (CHAR_DATA * ch, const char *argument, STOCK_DATA * stock);
static void save_stock (STOCK_DATA * stock, const char * path, const char * filename);
void load_stock (STOCK_DATA * stock, const char * path, const char * filename);
static void fread_thing (FILE * fp, STOCK_DATA * stock);

static THING_DATA * new_thing_data ( void );
static void free_thing_data ( THING_DATA * thing );

//-------------------------------------------------------------------------------
// function called from interpreter
//-------------------------------------------------------------------------------
DO_FUN (do_clanstock)
{
    if (!ch || !ch->in_room)
        return;

    if (!ch->clan)
    {
        char_act ("This command is for clan members only.", ch);
        return;
    }

    if (!IS_SET (ch->in_room->room_flags, ROOM_CLANBANK) && argument[0] != '\0'
        && !IS_IMMORTAL(ch))
    {
        char_act ("You must be in the clanbank to do this.", ch);
        return;
    }

    // this stuff is from conquer.c
    if (!parse_command(ch, argument, clanstock_cmd_table))
    {
        show_command_list (ch, clanstock_cmd_table);
        char_act (".", ch);
        char_act ("Use {CHELP CLANSTOCK{x for more information.", ch);
    }
}

static void clanstock_help ( CHAR_DATA *ch, const char *argument )
{
    do_help (ch, "'1.CLANSTOCK'");
}

static void clanstock_list ( CHAR_DATA *ch, const char *argument )
{
    show_stock_list (ch, &(clan_lookup (ch->clan)->clan_stock));
}

static void clanstock_empty ( CHAR_DATA *ch, const char *argument )
{
}

static void clanstock_clear ( CHAR_DATA *ch, const char *argument )
{
}

static void clanstock_put ( CHAR_DATA *ch, const char *argument )
{
    clan_t * clan;
    char     filename[MAX_INPUT_LENGTH];

    clan = clan_lookup (ch->clan);
    put_obj_stock (ch, argument, &(clan->clan_stock));
    sprintf (filename, "%s", clan->name);
    save_stock (&(clan->clan_stock), CLANS_PATH, strcat (filename, ".stock"));
    save_char_obj (ch, FALSE, FALSE);
}

static void clanstock_get ( CHAR_DATA *ch, const char *argument )
{
    clan_t * clan;
    char     filename[MAX_INPUT_LENGTH];

    clan = clan_lookup (ch->clan);
    get_obj_stock (ch, argument, &(clan_lookup (ch->clan)->clan_stock));
    sprintf (filename, "%s", clan->name);
    save_stock (&(clan->clan_stock), CLANS_PATH, strcat (filename, ".stock"));
    save_char_obj (ch, FALSE, FALSE);
}

static void clanstock_stat ( CHAR_DATA *ch, const char *argument )
{
    BUFFER   * output;
    int        i; 
    STOCK_DATA stock;

    output = buf_new(ch->lang);

    buf_add (output, "Clan stock information:\n");
    for (i = 1; i < clans.nused; ++i)
    {
        stock = CLAN(i)->clan_stock;
        if (stock.curr_things == 0)
            buf_printf(output, "{W[{Y%-12.12s{W]{x: empty.\n", CLAN(i)->name);
        else
            buf_printf(output, "{W[{Y%-12.12s{W]{x: %d of %d things.\n", CLAN(i)->name, stock.curr_things, stock.max_things == 0 ? 50 : stock.max_things);
    }

    page_to_char(buf_string(output), ch);
    buf_free(output);
}

//-------------------------------------------------------------------------------
static void show_stock_list (CHAR_DATA * ch, STOCK_DATA * stock)
{
    THING_DATA     * thing;
    BUFFER         * output;
    int              i;

    if (stock->curr_things <= 0)
    {
        char_act ("The stock is empty.", ch);
        return;
    }

    output = buf_new(ch->lang);

    for (thing = stock->things, i = 1; thing != NULL; thing = thing->next, ++i)
    {
        buf_printf (output, "%3d. %s %s\n", 
            i, 
            fmt_color_str(mlstr_cval(thing->obj->short_descr, ch), 40), 
            thing->owner);
    }

    char_act(buf_string(output), ch);
    buf_free(output);
}

static void put_obj_stock (CHAR_DATA * ch, const char *argument, STOCK_DATA * stock)
{
    char             arg[MAX_INPUT_LENGTH];
    OBJ_DATA       * obj;
    THING_DATA     * thing;

    argument = one_argument(argument, arg, sizeof(arg));

    if (stock->max_things == 0)
        stock->max_things = 50;

    if (arg[0] == '\0')
    {
        char_act("Put in the stock what?", ch);
        return;
    }

    if ((obj = get_obj_carry(ch, arg)) == NULL)
    {
        char_act("You do not have that item.", ch);
        return;
    }

    if (!can_drop_obj(ch, obj))
    {
        char_act("You can't let go of it.", ch);
        return;
    }

    if (obj->pIndexData->limit > 0)
    {
        char_act("You can't put limited objects into the stock.", ch);
        return;
    }

    if (!IS_NULLSTR (obj->owner))
    {
        char_printf(ch, "This object belongs to %s. You can't put it into the stock.\n", obj->owner);
        return;
    }

    if (is_clan_item(obj))
    {
        char_act("You can't put clan items into the stock.", ch);
        return;
    }

    if ((obj->pIndexData->item_type == ITEM_CONTAINER) && (!IS_SET (muddy_mode, MUDDY_STOCK_CONT)))
    {
        char_act("You can't put containers into the stock.", ch);
        return;
    }

    if (stock->curr_things >= stock->max_things)
    {
        char_act("Sorry, but the stock is full.", ch);
        return;
    }

    if (IS_OBJ_STAT(obj, ITEM_QUEST)) 
    {
        char_act("You can't put quest objects into the stock.", ch);
        return;
    }

    thing = new_thing_data ();

    if (thing == NULL)
        return;

    thing->obj = obj;
    thing->owner = str_dup (ch->name);
    thing->next = NULL;

    if (stock->things == NULL)
    {
        stock->things = thing;
        stock->last_thing = thing;
        stock->curr_things = 1;
    }
    else
    {
        stock->last_thing->next = thing;
        stock->last_thing = thing;
        stock->curr_things++;
    }

    obj_from_char(obj);

    act("$n puts $p into the stock.", 
        ch, obj, NULL, TO_ROOM | (IS_AFFECTED(ch, AFF_SNEAK) ? ACT_NOMORTAL : 0));
    act_puts("You put $p into the stock.", ch, obj, NULL, TO_CHAR, POS_DEAD);
}

static void get_obj_stock (CHAR_DATA * ch, const char *argument, STOCK_DATA * stock)
{
    int num, i;
    THING_DATA * thing, * prev;

    if (stock->curr_things <= 0)
    {
        char_act ("The stock is empty.", ch);
        return;
    }

    if (is_number (argument))
    {
        num = atoi (argument);
        if ((num <= 0) || (num > stock->curr_things))
        {
            char_printf (ch, "There are only %d things in the stock.\n", stock->curr_things);
            return;
        }
        thing = stock->things;
        prev = stock->things;
        for (i = 1; i < num; ++i)
        {
            prev = thing;
            thing = thing->next;
        }

        // todo: checks
        obj_to_char (thing->obj, ch);
        act("$n gets $p from the stock.", 
            ch, thing->obj, NULL, TO_ROOM | (IS_AFFECTED(ch, AFF_SNEAK) ? ACT_NOMORTAL : 0));
        act_puts("You get $p from the stock.", ch, thing->obj, NULL, TO_CHAR, POS_DEAD);

        if (thing != stock->things)
        {
            prev->next = thing->next;
            if (thing == stock->last_thing)
                stock->last_thing = prev;
        }
        else
        {
            stock->things = thing->next;
        }
        --stock->curr_things;
        free_thing_data (thing);
    }
}

static void save_stock (STOCK_DATA * stock, const char * path, const char * filename)
{
    FILE        * fp;
    THING_DATA  * thing;

    if (!(fp = dfopen(path, filename, "w")))
    {
        log_printf("ERROR!!! save_stock: can't open output file %s/%s", path, filename);
        return;
    }

    for (thing = stock->things; thing != NULL; thing = thing->next)
    {
        fprintf (fp, "#THING\n");
        fwrite_string(fp, "Owner", thing->owner); 
        fprintf (fp, "End\n\n");

        fwrite_obj (NULL, thing->obj, fp, 0, FALSE, "#O");
    }

    fprintf(fp, "#END\n");
    fclose(fp);
}

void load_clan_stocks ( void )
{
    int i;
    char     filename[MAX_INPUT_LENGTH];

    for (i = 1; i < clans.nused; ++i)
    {
        sprintf (filename, "%s", CLAN(i)->name);
        load_stock (&(CLAN(i)->clan_stock), CLANS_PATH, strcat (filename, ".stock"));
    }
}

void load_stock (STOCK_DATA * stock, const char * path, const char * filename)
{
    FILE     * fp;
    OBJ_DATA * obj;

    if ((fp = dfopen(path, filename, "r")))
    {
        for (;;)
        {
            char   letter ;
            char * word   ;

            letter = fread_letter (fp) ;
            if (letter == '*')
            {
                fread_to_eol (fp) ;
                continue ;
            }
            if (letter != '#')
            {
                log_printf ("Load_stat_record: # not found in %s.", filename) ;
                break ;
            }
            word = fread_word (fp) ;
            if (!str_cmp (word, "THING")) 
                fread_thing (fp, stock);
            else if (!str_cmp (word, "O")) 
            {
                obj = fread_obj  (NULL, fp);
                if (obj != NULL)
                    stock->last_thing->obj = obj;
            }
            else if (!str_cmp (word, "END")) break ;
            else
            {
                bug ("Load_stat_record: bad section.", 0) ;
                break ;
            }
        }
        fclose (fp) ;
    }
}

static void fread_thing (FILE * fp, STOCK_DATA * stock)
{
    char       * word;
    bool         fMatch;
    THING_DATA * thing;

    thing = new_thing_data ();

    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"))
            {
                if (stock->things == NULL)
                {
                    stock->things = thing;
                    stock->last_thing = thing;
                    stock->curr_things = 1;
                }
                else
                {
                    stock->last_thing->next = thing;
                    stock->last_thing = thing;
                    stock->curr_things++;
                }
                return;
            }
            break;

        case 'O':
            SKEY("Owner", thing->owner);
            break;
        }

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

//-------------------------------------------------------------------------------
// recycling
//-------------------------------------------------------------------------------
static THING_DATA * new_thing_data ( void )
{
    THING_DATA * thing;

    thing = calloc(1, sizeof(*thing));
    if (thing) // just to be sure
    {
        thing->next = NULL;
        thing->obj  = NULL;
    }
    return thing;
}

static void free_thing_data ( THING_DATA * thing )
{
    free_string(thing->owner);
    thing->owner = NULL;
    free (thing);
}