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: db.c,v 1.666 2004/09/20 10:50:18 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.                                                *
 *                                                                                  *
 ************************************************************************************/
/************************************************************************************
 *     ANATOLIA 2.1 is copyright 1996-1997 Serdar BULUT, Ibrahim CANPUNAR           *
 *     ANATOLIA has been brought to you by ANATOLIA consortium                      *
 *       Serdar BULUT {Chronos}         bulut@rorqual.cc.metu.edu.tr                *
 *       Ibrahim Canpunar  {Asena}      canpunar@rorqual.cc.metu.edu.tr             *
 *       Murat BICER  {KIO}             mbicer@rorqual.cc.metu.edu.tr               *
 *       D.Baris ACAR {Powerman}        dbacar@rorqual.cc.metu.edu.tr               *
 *     By using this code, you have agreed to follow the terms of the               *
 *     ANATOLIA license, in the file Anatolia/anatolia.licence                      *
 ***********************************************************************************/

/************************************************************************************
 *  Original Diku Mud copyright (C) 1990, 1991 by Sebastian Hammer,                 *
 *  Michael Seifert, Hans Henrik St{rfeldt, Tom Madsen, and Katja Nyboe.            *
 *                                                                                  *
 *  Merc Diku Mud improvments copyright (C) 1992, 1993 by Michael                   *
 *  Chastain, Michael Quan, and Mitchell Tse.                                       *
 *                                                                                  *
 *  In order to use any part of this Merc Diku Mud, you must comply with            *
 *  both the original Diku license in 'license.doc' as well the Merc                *
 *  license in 'license.txt'.  In particular, you may not remove either of          *
 *  these copyright notices.                                                        *
 *                                                                                  *
 *  Much time and thought has gone into this software and you are                   *
 *  benefitting.  We hope that you share your changes too.  What goes               *
 *  around, comes around.                                                           *
 ************************************************************************************/

/************************************************************************************
*       ROM 2.4 is copyright 1993-1995 Russ Taylor                                  *
*       ROM has been brought to you by the ROM consortium                           *
*           Russ Taylor (rtaylor@pacinfo.com)                                       *
*           Gabrielle Taylor (gtaylor@pacinfo.com)                                  *
*           Brian Moore (rom@rom.efn.org)                                           *
*       By using this code, you have agreed to follow the terms of the              *
*       ROM license, in the file Rom24/doc/rom.license                              *
*************************************************************************************/

/************************************************************************************
 * Copyright (c) 1998 fjoe <fjoe@iclub.nsu.ru>                                      *
 * All rights reserved.                                                             *
 *                                                                                  *
 * Redistribution and use in source and binary forms, with or without               *
 * modification, are permitted provided that the following conditions               *
 * are met:                                                                         *
 * 1. Redistributions of source code must retain the above copyright                *
 *    notice, this list of conditions and the following disclaimer.                 *
 * 2. Redistributions in binary form must reproduce the above copyright             *
 *    notice, this list of conditions and the following disclaimer in the           *
 *    documentation and/or other materials provided with the distribution.          *
 *                                                                                  *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND           *
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE            *
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE       *
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE          *
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL       *
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS          *
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)            *
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT       *
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY        *
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF           *
 * SUCH DAMAGE.                                                                     *
 ************************************************************************************/
  
#include <errno.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <time.h>
#include <stdarg.h>
#include <ctype.h>
#include <limits.h>
#include <sys/types.h>
// Welesh : compat
#if     !defined (WIN32)
#include <unistd.h>
#include <sys/time.h>
#endif

#if defined (WIN32)
#include <compat/compat.h>
#else
#include <dirent.h>
#endif

#include "merc.h"
#include "rating.h"
#include "socials.h"
#include "update.h"
#include "db.h"
#include "db/lang.h"
#include "olc/olc.h"
#include "mob_prog.h"
#include "quest.h"
#include "conquer.h"
#include "stock.h"

#ifdef SUNOS
#include "compat.h"
#define d_namlen d_reclen
#endif

// Welesh : compat
#if !defined (WIN32)
#define NEW_RAND
#endif

#ifdef NEW_RAND
#include "db/mtwist.h"
#endif

#ifdef SVR4
#define d_namlen d_reclen
#endif

void load_limited_objects();
extern void load_stat_record (void);

extern  int _filbuf     (FILE *);

#if !defined(NEW_RAND)
#if !defined(OLD_RAND)

#if defined(WIN32)
    #define random()    rand()
    #define srandom(x)  srand(x)
int getpid();
time_t time(time_t *tloc);
#endif
#endif
#endif


/* externals for counting purposes */
extern  DESCRIPTOR_DATA *descriptor_free;
extern void sunrise(int day, int month, int year, double breadth, double longitude,int tz);

/*
 * Globals.
 */
flag32_t        mud_options;

SHOP_DATA *     shop_first;
SHOP_DATA *     shop_last;

CHAR_DATA *     char_list;
KILL_DATA       kill_table  [MAX_LEVEL-1];

OBJ_DATA *      object_list;
TIME_INFO_DATA      time_info;
WEATHER_DATA        weather_info;

ROOM_INDEX_DATA *   top_affected_room;
int         reboot_counter;

/*
 * Locals.
 */
MOB_INDEX_DATA *    mob_index_hash      [MAX_KEY_HASH];
OBJ_INDEX_DATA *    obj_index_hash      [MAX_KEY_HASH];
ROOM_INDEX_DATA *   room_index_hash     [MAX_KEY_HASH];

int         line_number;

AREA_DATA *     area_first;
AREA_DATA *     area_last;

int         top_affect;
int         top_area;
int         top_ed;
int         top_exit;
int         top_help;
int         top_mob_index;
int         top_obj_index;
int         top_reset;
int         top_room;
int         top_shop;
int         top_vnum_room;      /* OLC */
int         top_vnum_mob;       /* OLC */
int         top_vnum_obj;       /* OLC */
int         top_mprog_index;    /* OLC */
int         top_oprog_index;
int         top_rprog_index;
int         mobile_count = 0;
int         newmobs = 0;
int         newobjs = 0;

int nAllocBuf;
int sAllocBuf;

/*
 * Semi-locals.
 */
bool            fBootDb;
char            filename[PATH_MAX];

/*
 * Local booting procedures.
*/
void    init_mm         (void);

void    fix_exits   (void);
void    check_mob_progs (void);
void    load_clan_members(void);

void    reset_area  (AREA_DATA * pArea);

int rand_range(int to);

#define CREATE_NOCOUNT  (A)
#define CREATE_NAMED    (B)

int dbfuncmp(const void *p1, const void *p2)
{
    return str_cmp(*(char**)p1, *(char**)p2);
}

int dbfun_qsort(DBFUN *dbfun_table)
{
    int dbfun_count = 0;
    while (dbfun_table[dbfun_count].name)
        dbfun_count++;
    qsort(dbfun_table, dbfun_count, sizeof(*dbfun_table), dbfuncmp);
    return dbfun_count;
}

void db_parse_file(const char *path, const char *file,
                   DBFUN *dbfun_table, int dbfun_count)
{
    FILE *fp;

    snprintf(filename, sizeof(filename), "%s%c%s", path, PATH_SEPARATOR, file);
    if ((fp = fopen(filename, "r")) == NULL)
    {
        perror(filename);
        exit(1);
    }

    line_number = 1;

    for (; ;)
    {
        DBFUN *fn;
        char *word;

        if (fread_letter(fp) != '#')
            db_error("db_parse_file", "'#' not found");

        word = fread_word(fp);
        if (word[0] == '$')
            break;
        fn = bsearch(&word, dbfun_table, dbfun_count,
                     sizeof(*dbfun_table), dbfuncmp);

        if (fn)
            fn->fun(fp);
        else
        {
            log_printf("boot_db (%s): bad section name. (%s)",
                       filename, word);
            exit(1);
        }
    }
    fclose(fp);
}

void db_load_file(const char *path, const char *file,
                  DBFUN *dbfun_table, DBINIT_FUN *dbinit)
{
    int dbfun_count = dbfun_qsort(dbfun_table);
    if (dbinit)
        dbinit();
    db_parse_file(path, file, dbfun_table, dbfun_count);
}

void db_load_list(const char *path, const char *file,
                  DBFUN *dbfun_table, DBINIT_FUN *dbinit)
{
    int dbfun_count;
    FILE *fp;

    if ((fp = dfopen(path, file, "r")) == NULL)
    {
        perror(file);
        exit(1);
    }

    dbfun_count = dbfun_qsort(dbfun_table);
    for (; ;)
    {
        char *name = fread_word(fp);
        if (name[0] == '$')
            break;

        if (dbinit)
            dbinit();
        db_parse_file(path, name, dbfun_table, dbfun_count);
    }
    fclose(fp);
}

/*
 * Big mama top level function.
 */
void boot_db(void)
{
    long lhour, lday, lmonth;
    FILE *fp;

#ifdef __FreeBSD__
//    extern char* malloc_options;
//    malloc_options = "X";
#endif
    /*
     * Init random number generator.
     */
    init_mm();

    /*
     * Set time and weather.
     */

    lhour           = (current_time - 650336715) / (PULSE_TICK / PULSE_PER_SCD);
    time_info.hour  = lhour  % 24;
    lday            = lhour  / 24;
    time_info.day   = lday   % 35;
    lmonth          = lday   / 35;
    time_info.month = lmonth % 17;
    time_info.year  = lmonth / 17;

    if      (time_info.hour <  5) weather_info.sunlight = SUN_DARK;
    else if (time_info.hour <  6) weather_info.sunlight = SUN_RISE;
    else if (time_info.hour < 19) weather_info.sunlight = SUN_LIGHT;
    else if (time_info.hour < 20) weather_info.sunlight = SUN_SET;
    else                          weather_info.sunlight = SUN_DARK;

    weather_info.change = 0;
// For autopatch
/*    weather_info.mmhg   = 960;
    if (time_info.month >= 7 && time_info.month <=12)
        weather_info.mmhg += number_range(1, 50);
    else
        weather_info.mmhg += number_range(1, 80);

         if (weather_info.mmhg <=  980) weather_info.sky = SKY_LIGHTNING;
    else if (weather_info.mmhg <= 1000) weather_info.sky = SKY_RAINING;
    else if (weather_info.mmhg <= 1020) weather_info.sky = SKY_CLOUDY;
    else                                weather_info.sky = SKY_CLOUDLESS;
*/
    weather_info.sky = SKY_CLOUDLESS;
    sunrise(time_info.day, time_info.month, time_info.year, BREADTH, LONGITUDE, TIME_ZONE);

    /* room_affect_data */
    top_affected_room = NULL;

    /* reboot counter */
    reboot_counter = -1;    /* reboot off */

    fBootDb = TRUE;

    db_load_file(ETC_PATH, SECURITY_CONF, db_load_security, NULL);
    log_printf("** [DB]** security load");

    db_load_list(LANG_PATH, LANG_LIST, db_load_langs, init_lang);
    log_printf("** [DB]** lang load");

    load_msgdb();
    log_printf("** [DB]** msgdb load");

    db_load_file(ETC_PATH, COMMANDS_CONF, db_load_commands, NULL);
    log_printf("** [DB]** commands load");

    db_load_file(ETC_PATH, SOCIALS_CONF, db_load_socials, NULL);
    log_printf("** [DB]** socials load");

    db_load_file(ETC_PATH, SKILLS_CONF, db_load_skills, NULL);
    log_printf("** [DB]** skills load");

    db_load_file(ETC_PATH, ALIASES_CONF, db_load_aliases, NULL);
    log_printf("** [DB]** aliases load");

    db_load_file(ETC_PATH, PRICES_CONF, db_load_prices,  NULL);
    log_printf("** [DB]** prices load");

    db_load_file(ETC_PATH, SYSTEM_CONF, db_load_system, NULL);
    log_printf("** [DB]** system load");
    
    db_load_file(ETC_PATH, MATERIAL_CONF, db_load_material, NULL);
    log_printf("** [DB]** materials load");
    
    db_load_file(ETC_PATH, LIQUID_CONF, db_load_liquid, NULL);
    log_printf("** [DB]** liquids load");

    log_printf("** [DB]** namedp_check dofun_table");
    namedp_check(dofun_table);
    log_printf("** [DB]** done");
    
    log_printf("** [DB]** namedp_check gsn_table");
    namedp_check(gsn_table);
    log_printf("** [DB]** done");
    
    log_printf("** [DB]** namedp_check spellfn_table");
    namedp_check(spellfn_table);
    log_printf("** [DB]** done");
    
    db_load_list(RACES_PATH, RACE_LIST, db_load_races, init_race);
    log_printf("** [DB]** races load");

    db_load_list(CLASSES_PATH, CLASS_LIST, db_load_classes, init_class);
    log_printf("** [DB]** classes load");

    db_load_list(CLANS_PATH, CLAN_LIST, db_load_clans, NULL);
    log_printf("** [DB]** clans load");

    db_load_list(RELIGIONS_PATH, RELIGION_LIST, db_load_religions, NULL);
    log_printf("** [DB]** religion load");

    db_load_list(AREA_PATH, AREA_LIST, db_load_areas, init_area);
    log_printf("** [DB]** security load");

    db_load_file(ETC_PATH, HOMETOWNS_CONF, db_load_hometowns, NULL);
    log_printf("** [DB]** hometown load");

    db_load_file(ETC_PATH, RIDDLES_CONF, db_load_riddles, NULL);
    log_printf("** [DB]** riddles load");

    db_load_file(ETC_PATH, IMMORTAL_CONF, db_load_immortal, NULL);
    log_printf("** [DB]** immortals load");

    // load tattooes
    if ((fp = dfopen( ETC_PATH, TATTOO_CONF, "r")) != NULL)
    {
        fclose (fp);
        db_load_file(ETC_PATH, TATTOO_CONF, db_load_tattoo, NULL);
        log_printf("** [DB]** tattooes load");
    }
    else
    {
        log_printf("** [DB]** tattooes file not found");
    }

    // load rituals
    if ((fp = dfopen( ETC_PATH, RITUAL_CONF, "r")) != NULL)
    {
        fclose (fp);
        db_load_file(ETC_PATH, RITUAL_CONF, db_load_ritual, NULL);
        log_printf("** [DB]** rituals load");
    }
    else
    {
        log_printf("** [DB]** rituals file not found");
    }

    // load paints
    if ((fp = dfopen( ETC_PATH, PAINT_CONF, "r")) != NULL)
    {
        fclose (fp);
        db_load_file(ETC_PATH, PAINT_CONF, db_load_paint, NULL);
        log_printf("** [DB]** paints load");
    }
    else
    {
        log_printf("** [DB]** paints file not found");
    }

    db_load_file(ETC_PATH, METEORS_CONF, db_load_meteor, NULL);
    log_printf("** [DB]** meteors load");

    load_meteor_area();
    log_printf("** [DB]** meteors area initialized");
    
    fBootDb = FALSE;

    /*
     * Fix up exits.
     * Declare db booting over.
     * Reset all areas once.
     * Load up the songs, notes and ban files.
     */
    log_printf("** [DB]** fix_exits"); 
    fix_exits();
    log_printf("** [DB]** done");
    
    log_printf("** [DB]** check_mob_progs");
    check_mob_progs();
    log_printf("** [DB]** done");

    log_printf("** [DB]** load_limited_objects");
    load_limited_objects();
    log_printf("** [DB]** done");

    log_printf("** [DB]** converts_objects");
    convert_objects();           /* ROM OLC */
    log_printf("** [DB]** done");

    log_printf("** [DB]** area_update");
    area_update();
    log_printf("** [DB]** done");

    log_printf("** [DB]** load_notes");
    load_notes();
    log_printf("** [DB]** done");

    log_printf("** [DB]** load_bans");
    load_bans();
    log_printf("** [DB]** done");

    log_printf("** [DB]** init_clan");
    init_clan();
    log_printf("** [DB]** done");

    log_printf("** [DB]** load_conquered_areas");
    load_conquered_areas ();
    log_printf("** [DB]** done");

    log_printf("** [DB]** init_clan_members");
    load_clan_members();
    log_printf("** [DB]** done");
    
    log_printf("** [DB]** questglobal_init");
    gq_init (TRUE); /* default state */
    log_printf("** [DB]** done");

    log_printf("** [DB]** winquest_init");
    wq_init (TRUE); /* default state */
    log_printf("** [DB]** done");

    //explore_count_rooms ();
    log_printf("** [DB]** explore_count_rooms");
    explore_count_rooms (FALSE); // with true - write in log
    log_printf ("** [DB]** TotalExplorableRooms: %d", explore_total_rooms ());

    log_printf("** [DB]** ranking_read_config");
    ranking_read_config ();
    log_printf("** [DB]** done");

    log_printf("** [DB]** ranking_read_stats");
    ranking_read_stats  ();
    log_printf("** [DB]** done");

    log_printf("** [DB]** conquer_read_config");    
    conquer_read_config ();
    log_printf("** [DB]** done");

    log_printf("** [DB]** war_init");    
    war_init ();
    log_printf("** [DB]** done");

    log_printf("** [DB]** load_stat_record");    
    load_stat_record ();
    log_printf("** [DB]** done");

    log_printf("** [DB]** load_clan_stocks");    
    load_clan_stocks ();
    log_printf("** [DB]** done");

    log_printf("** [DB]** system is loaded and ready");
}

/*
 * Sets vnum range for area using OLC protection features.
 */
void vnum_check(AREA_DATA *area, int vnum)
{
    if (area->min_vnum == 0 || area->max_vnum == 0)
    {
        log_printf("%s: min_vnum or max_vnum not assigned",
                   area->file_name);
#if 0
        area->min_vnum = area->max_vnum = vnum;
#endif
    }

    if (vnum < area->min_vnum || vnum > area->max_vnum)
    {
        log_printf("%s: %d not in area vnum range",
                   area->file_name, vnum);
#if 0
        if (vnum < area->min_vnum)
            area->min_vnum = vnum;
        else
            area->max_vnum = vnum;
#endif
    }
}

/*
 * Adds a reset to a room.  OLC
 * Similar to add_reset in olc.c
 */
void new_reset(ROOM_INDEX_DATA *pR, RESET_DATA *pReset)
{
    RESET_DATA *pr;

    if (!pR)
        return;

    pr = pR->reset_last;

    if (!pr)
    {
        pR->reset_first = pReset;
        pR->reset_last  = pReset;
    } else
    {
        pR->reset_last->next = pReset;
        pR->reset_last       = pReset;
        pR->reset_last->next = NULL;
    }

    top_reset++;
}

/*
 *  Check mobprogs
 */
void check_mob_progs(void)
{
    MOB_INDEX_DATA *pMobIndex;
    MPTRIG        *mptrig;
    int iHash;

    for (iHash = 0; iHash < MAX_KEY_HASH; iHash++)
    {
        for (pMobIndex   = mob_index_hash[iHash];
            pMobIndex   != NULL;
            pMobIndex   = pMobIndex->next)
        {
            for (mptrig = pMobIndex->mptrig_list; mptrig; mptrig = mptrig->next)
            {
                if (mpcode_lookup(mptrig->vnum) == NULL)
                {
                    log_printf("check_mob_progs: code vnum %d not found.", mptrig->vnum);
                    exit(1);
                }
            }
        }
    }
}

void fix_exits_room(ROOM_INDEX_DATA *room)
{
    int door;

    for (door = 0; door < MAX_DIR; door++)
    {
        EXIT_DATA *pexit;

        if ((pexit = room->exit[door]) == NULL)
            continue;

        pexit->to_room.r = get_room_index(pexit->to_room.vnum);
    }
}

/*
 * Translate all room exits from virtual to real.
 * Has to be done after all rooms are read in.
 * Check for bad reverse exits.
 */
void fix_exits(void)
{
    ROOM_INDEX_DATA *room;
    int iHash;

    for (iHash = 0; iHash < MAX_KEY_HASH; iHash++)
        for (room = room_index_hash[iHash]; room; room = room->next)
            fix_exits_room(room);
}


void print_resetmsg(AREA_DATA *pArea)
{
    DESCRIPTOR_DATA *d;
    bool is_empty = mlstr_null(pArea->resetmsg);

    for (d = descriptor_list; d != NULL; d = d->next)
    {
        CHAR_DATA *ch;

        if (d->connected != CON_PLAYING)
            continue;

        ch = d->character;
        if (IS_NPC(ch) || !IS_AWAKE(ch) || ch->in_room->area != pArea)
            continue;

        if (is_empty)
            char_act("You hear some squeaking sounds...", ch);
        else
            char_act(mlstr_cval(pArea->resetmsg, ch), ch);
    }
}

/*
 * Repopulate areas periodically.
 */
void area_update(void)
{
    AREA_DATA *pArea;

    for (pArea = area_first; pArea != NULL; pArea = pArea->next)
    {
        if (++pArea->age < 3)
            continue;

        /*
         * Check age and reset.
         * Note: Mud School resets every 3 minutes (not 15).
         */
        if ((!pArea->empty && (pArea->nplayer == 0 || pArea->age >= 15))
            ||  pArea->age >= 31)
        {
            ROOM_INDEX_DATA *pRoomIndex;

            /*
             * the rain devastates tracks on the ground
             */
            if (weather_info.sky == SKY_RAINING)
            {
                int i;
                DESCRIPTOR_DATA *d;
                CHAR_DATA *ch;
                for (d = descriptor_list; d!=NULL; d=d->next)
                {
                    if (d->connected != CON_PLAYING)
                        continue;

                    ch = (d->original != NULL) ?
                         d->original : d->character;
                    if (ch->in_room->area == pArea
                        &&  get_skill(ch, gsn_track) > 50
                        && !IS_SET(ch->in_room->room_flags,
                                   ROOM_INDOORS))
                        char_act("Rain devastates the tracks on the ground.", ch);
                }

                for (i = pArea->min_vnum; i < pArea->max_vnum;
                    i++)
                {
                    pRoomIndex = get_room_index(i);
                    if (pRoomIndex == NULL
                        ||  IS_SET(pRoomIndex->room_flags,
                                   ROOM_INDOORS))
                        continue;
                    room_record("erased", pRoomIndex, -1);
                    if (number_percent() < 50)
                        room_record("erased",
                                    pRoomIndex, -1);
                }
            }

            reset_area(pArea);
            wiznet_printf(NULL, NULL, WIZ_RESETS, 0, 0,
                          "%s has just been reset.", pArea->name);

            print_resetmsg(pArea);

            pArea->age = number_range(0, 3);
            pRoomIndex = get_room_index(200);
            if (pRoomIndex != NULL && pArea == pRoomIndex->area)
                pArea->age = 15 - 2;
            pRoomIndex = get_room_index(210);
            if (pRoomIndex != NULL && pArea == pRoomIndex->area)
                pArea->age = 15 - 2;
            pRoomIndex = get_room_index(220);
            if (pRoomIndex != NULL && pArea == pRoomIndex->area)
                pArea->age = 15 - 2;
            pRoomIndex = get_room_index(230);
            if (pRoomIndex != NULL && pArea == pRoomIndex->area)
                pArea->age = 15 - 2;
            pRoomIndex = get_room_index(ROOM_VNUM_SCHOOL);
            if (pRoomIndex != NULL && pArea == pRoomIndex->area)
                pArea->age = 15 - 2;
            else if (pArea->nplayer == 0)
                pArea->empty = TRUE;
        }
    }
}

/*
 * OLC
 * Reset one room.  Called by reset_area and olc.
 */
void reset_room(ROOM_INDEX_DATA *pRoom)
{
    RESET_DATA  *pReset;
    CHAR_DATA   *pMob;
    CHAR_DATA   *mob;
    OBJ_DATA    *pObj;
    CHAR_DATA   *LastMob = NULL;
    OBJ_DATA    *LastObj = NULL;
    int iExit;
    int level = 0;
    bool last;

    if (!pRoom)
        return;

    pMob        = NULL;
    last        = FALSE;

    for (iExit = 0;  iExit < MAX_DIR;  iExit++)
    {
        EXIT_DATA *pExit;
        if ((pExit = pRoom->exit[iExit])
            /*  && !IS_SET(pExit->exit_info, EX_BASHED)   ROM OLC */)
        {
            pExit->exit_info = pExit->rs_flags;
            if ((pExit->to_room.r != NULL)
                && ((pExit = pExit->to_room.r->exit[rev_dir[iExit]])))
            {
                /* nail the other side */
                pExit->exit_info = pExit->rs_flags;
            }
        }
    }

    for (pReset = pRoom->reset_first; pReset != NULL; pReset = pReset->next)
    {
        MOB_INDEX_DATA  *pMobIndex;
        OBJ_INDEX_DATA  *pObjIndex;
        OBJ_INDEX_DATA  *pObjToIndex;
        ROOM_INDEX_DATA *pRoomIndex;
        int count,limit=0;
        EXIT_DATA *pExit;
        int d0;
        int d1;

        switch (pReset->command)
        {
        default:
            bug("Reset_room: bad command %c.", pReset->command);
            break;

        case 'M':
            if (!(pMobIndex = get_mob_index(pReset->arg1)))
            {
                bug("Reset_room: 'M': bad vnum %d.", pReset->arg1);
                continue;
            }

            if ((pRoomIndex = get_room_index(pReset->arg3)) == NULL)
            {
                bug("Reset_area: 'R': bad vnum %d.", pReset->arg3);
                continue;
            }
            if (pMobIndex->count >= pReset->arg2)
            {
                last = FALSE;
                break;
            }
/* */
            count = 0;
            for (mob = pRoomIndex->people; mob != NULL; mob = mob->next_in_room)
                if (mob->pIndexData == pMobIndex)
                {
                    count++;
                    if (count >= pReset->arg4)
                    {
                        last = FALSE;
                        break;
                    }
                }

            if (count >= pReset->arg4)
                break;

            if ((LastMob != NULL) && HAS_TRIGGER_MOB(LastMob, TRIG_START))
                p_percent_trigger(LastMob, NULL, NULL, NULL, NULL, NULL, TRIG_START);

            pMob = create_mob(pMobIndex);

            pMob->zone = pRoom->area;
            char_to_room(pMob, pRoom);

            if (JUST_KILLED(pMob))
                LastMob = NULL;
            else
                LastMob = pMob;

            level  = URANGE(0, pMob->level - 2, LEVEL_HERO - 1); /* -1 ROM */
            last = TRUE;
            break;

        case 'O':
            if (!(pObjIndex = get_obj_index(pReset->arg1)))
            {
                log_printf("reset_room: 'O' 1 : bad vnum %d", pReset->arg1);
                log_printf("reset_room: %c %d %d %d",pReset->arg1, pReset->arg2, pReset->arg3, pReset->arg4);
                continue;
            }

            if (!(pRoomIndex = get_room_index(pReset->arg3)))
            {
                log_printf("reset_room: 'O' 2 : bad vnum %d.", pReset->arg3);
                log_printf("reset_room: %c %d %d %d", pReset->arg1, pReset->arg2, pReset->arg3, pReset->arg4);
                continue;
            }

            if (pRoom->area->nplayer > 0
                || count_obj_list(pObjIndex, pRoom->contents) > 0)
            {
                last = FALSE;
                break;
            }
            if ((pObjIndex->limit != -1) &&
                (pObjIndex->count >= pObjIndex->limit))
            {
                last = FALSE;
                break;
            }

/* replaced by montaron */
/*            pObj = create_obj(pObjIndex,               UMIN - ROM OLC
                  UMIN(number_fuzzy(level), LEVEL_HERO -1));
*/
            pObj = create_obj_rand(pObjIndex, 0);

            pObj->cost = 0;
            obj_to_room(pObj, pRoom);
            last = TRUE;
            break;

        case 'P':
            /* (P)ut command
             * arg1 - vnum of obj to put
             * arg2
             * arg3 - vnum of obj to put into
             * arg4
             */

            if (!(pObjIndex = get_obj_index(pReset->arg1)))
            {
                bug("Reset_room: 'P': bad vnum %d.", pReset->arg1);
                continue;
            }

            if (!(pObjToIndex = get_obj_index(pReset->arg3)))
            {
                log_printf("[*** BUG **] Reset_room: 'P': bad room vnum: %d arg: %d.", pRoom->vnum, pReset->arg3);
                continue;
            }

            if (pReset->arg2 > 50) /* old format */
                limit = 6;
            else if (pReset->arg2 == -1) /* no limit */
                limit = 999;
            else
                limit = pReset->arg2;

            if (pRoom->area->nplayer > 0
                || (LastObj = get_obj_type(pObjToIndex)) == NULL
                || (LastObj->in_room == NULL && !last)
                || (pObjIndex->count >= limit && number_range(0,4) != 0)
                || (count = count_obj_list(pObjIndex, LastObj->contains)) > pReset->arg4)
            {
                last = FALSE;
                break;
            }
            /* lastObj->level  -  ROM */

            if ((pObjIndex->limit != -1             ) &&
                (pObjIndex->count >= pObjIndex->limit))
            {
                last = FALSE;
                log_printf("Reseting area: [P] OBJ limit reached.");
                break;
            }

            while (count < pReset->arg4)
            {
                pObj = create_obj_rand(pObjIndex, 0);
                obj_to_obj(pObj, LastObj);
                count++;
                if (pObjIndex->count >= limit)
                    break;
            }

            /* fix object lock state! */
            LastObj->value[1] = LastObj->pIndexData->value[1];
            last = TRUE;
            break;

        case 'G':
        case 'E':
            if (!(pObjIndex = get_obj_index(pReset->arg1)))
            {
                bug("Reset_room: 'E' or 'G': bad vnum %d.", pReset->arg1);
                continue;
            }

            if (!last)
                break;

            if (!LastMob)
            {
                bug("Reset_room: 'E' or 'G': null mob for vnum %d.",  pReset->arg1);
                last = FALSE;
                break;
            }

            if (LastMob->pIndexData->pShop)   /* Shop-keeper? */
            {
                int olevel=0;

                pObj = create_obj(pObjIndex, olevel);
                SET_BIT(pObj->extra_flags, ITEM_INVENTORY);  /* ROM OLC */
            }
/* Anatolia else version */
            else
            {
                if ((pObjIndex->limit == -1)  ||
                    (pObjIndex->count < pObjIndex->limit))
                    pObj = create_obj_rand(pObjIndex, 0);
                else break;

            }

            obj_to_char(pObj, LastMob);
            if (pReset->command == 'E')
                equip_char(LastMob, pObj, pReset->arg3);
            last = TRUE;
            break;

        case 'D':
            if ((pRoomIndex = get_room_index(pReset->arg1)) == NULL)
            {
                bug("Reset_area: 'D': bad vnum %d.", pReset->arg1);
                continue;
            }

            if ((pExit = pRoomIndex->exit[pReset->arg2]) == NULL)
                break;

            switch (pReset->arg3)
            {
            case 0:
                REMOVE_BIT(pExit->exit_info, EX_CLOSED);
                REMOVE_BIT(pExit->exit_info, EX_LOCKED);
                break;

            case 1:
                SET_BIT(  pExit->exit_info, EX_CLOSED);
                REMOVE_BIT(pExit->exit_info, EX_LOCKED);
                break;

            case 2:
                SET_BIT(  pExit->exit_info, EX_CLOSED);
                SET_BIT(  pExit->exit_info, EX_LOCKED);
                break;
            }

            last = TRUE;
            break;

        case 'R':
            if (!(pRoomIndex = get_room_index(pReset->arg1)))
            {
                bug("Reset_room: 'R': bad vnum %d.", pReset->arg1);
                continue;
            }

            {

                for (d0 = 0; d0 < pReset->arg2 - 1; d0++)
                {
                    d1                   = number_range(d0, pReset->arg2-1);
                    pExit                = pRoomIndex->exit[d0];
                    pRoomIndex->exit[d0] = pRoomIndex->exit[d1];
                    pRoomIndex->exit[d1] = pExit;
                }
            }
            break;
        }
    }
    if ((LastMob != NULL) && HAS_TRIGGER_MOB(LastMob, TRIG_START))
        p_percent_trigger(LastMob, NULL, NULL, NULL, NULL, NULL, TRIG_START);
}

/*
 * OLC
 * Reset one area.
 */
void reset_area(AREA_DATA *pArea)
{
    ROOM_INDEX_DATA *pRoom;
    int vnum;

    for (vnum = pArea->min_vnum; vnum <= pArea->max_vnum; vnum++)
        if ((pRoom = get_room_index(vnum)))
            reset_room(pRoom);
}

/*
 * Create an instance of a mobile.
 */
CHAR_DATA *create_mob_org(MOB_INDEX_DATA *pMobIndex, int flags)
{
    CHAR_DATA *mob;
    int i;
    AFFECT_DATA af;
    race_t *race;

    mobile_count++;

    if (pMobIndex == NULL)
    {
        bug("Create_mobile: NULL pMobIndex.", 0);
        exit(1);
    }

    mob = new_char();

    mob->pIndexData     = pMobIndex;

    mob->name       = str_dup(pMobIndex->name);
    if (!IS_SET(flags, CREATE_NAMED))
    {
        mob->short_descr    = mlstr_dup(pMobIndex->short_descr);
        mob->long_descr     = mlstr_dup(pMobIndex->long_descr);
        mob->description    = mlstr_dup(pMobIndex->description);
        mob->listen_data    = mlstr_dup(pMobIndex->listen_data); 
    }
    mob->id         = get_mob_id();
    mob->spec_fun       = pMobIndex->spec_fun;
    mob->class      = CLASS_CLERIC;

    if (pMobIndex->wealth)
    {
        long wealth;

        wealth = number_range(pMobIndex->wealth/2,
                              3 * pMobIndex->wealth/2);
        mob->gold = number_range(wealth/200,wealth/100);
        mob->silver = wealth - (mob->gold * 100);
    }

    mob->plr_flags      = ACT_NPC;
    mob->comm       = COMM_NOMUSIC;
    mob->affected_by    = pMobIndex->affected_by;
    mob->alignment      = pMobIndex->alignment;
    mob->level      = pMobIndex->level;

    mob->race       = pMobIndex->race;

    for (i = 0; i < MAX_DAM; i++)
        mob->resists[i] = pMobIndex->resists[i];
    mob->immunes = pMobIndex->immunes;

    if ((race = race_lookup(mob->race)) != NULL)
    {
        for (i = 0; i < MAX_DAM; i++)
            mob->resists[i] += race->resists[i];
        mob->immunes = mob->immunes | race->immunes;
    }
    
    mob->start_pos      = pMobIndex->start_pos;
    mob->position       = mob->start_pos;
    mob->default_pos    = pMobIndex->default_pos;
    mob->form       = pMobIndex->form;
    mob->parts      = pMobIndex->parts;
    mob->pcnt       = pMobIndex->pcnt;
    mob->size       = (pMobIndex->size * (85 + number_range(0, 30)) / 100);
    if (mob->size > SIZE_GARGANTUAN_MAX)
        mob->size = SIZE_GARGANTUAN_MAX;
    mob->clan       = pMobIndex->clan;
    mob->invis_level    = pMobIndex->invis_level;
    mob->group      = pMobIndex->group;
    mob->material       = str_dup(pMobIndex->material);

    mob->dam_type       = pMobIndex->dam_type;
    if (mob->dam_type == 0)
        switch (number_range(1,3))
        {
        case (1): mob->dam_type = 3;        break;  /* slash */
        case (2): mob->dam_type = 7;        break;  /* pound */
        case (3): mob->dam_type = 11;       break;  /* pierce */
        }

    mob->sex        = pMobIndex->sex;
    if (mob->sex == SEX_EITHER) /* random sex */
    {
        switch (number_range(123,124))
        {
        case 123:
            mob->sex = SEX_MALE;
            break;
        default:
            mob->sex = SEX_FEMALE;
            break;
        }
    }

    for (i = 0; i < MAX_STATS; i ++)
        mob->perm_stat[i] = UMIN(25, 11 + mob->level/4);

    mob->perm_stat[STAT_STR] += mob->size - SIZE_MEDIUM;
    mob->perm_stat[STAT_CON] += (mob->size - SIZE_MEDIUM) / 12;

    mob->hitroll        = (mob->level / 2) + pMobIndex->hitroll;
    mob->damroll        = pMobIndex->damage[DICE_BONUS];
    mob->max_hit        = dice(pMobIndex->hit[DICE_NUMBER],
                               pMobIndex->hit[DICE_TYPE])
                          + pMobIndex->hit[DICE_BONUS];
    mob->hit        = mob->max_hit;
    mob->max_mana       = dice(pMobIndex->mana[DICE_NUMBER],
                               pMobIndex->mana[DICE_TYPE])
                          + pMobIndex->mana[DICE_BONUS];
    mob->mana       = mob->max_mana;
    mob->psp            = 10;
    mob->damage[DICE_NUMBER]= pMobIndex->damage[DICE_NUMBER];
    mob->damage[DICE_TYPE]  = pMobIndex->damage[DICE_TYPE];
    for (i = 0; i < 4; i++)
        mob->armor[i]   = pMobIndex->ac[i];

    if (IS_SET(pMobIndex->act, ACT_WARRIOR))
    {
        mob->perm_stat[STAT_STR] += 3;
        mob->perm_stat[STAT_INT] -= 1;
        mob->perm_stat[STAT_CON] += 2;
    }

    if (IS_SET(pMobIndex->act, ACT_THIEF))
    {
        mob->perm_stat[STAT_DEX] += 3;
        mob->perm_stat[STAT_INT] += 1;
        mob->perm_stat[STAT_WIS] -= 1;
    }

    if (IS_SET(pMobIndex->act, ACT_CLERIC))
    {
        mob->perm_stat[STAT_WIS] += 3;
        mob->perm_stat[STAT_DEX] -= 1;
        mob->perm_stat[STAT_STR] += 1;
    }

    if (IS_SET(pMobIndex->act, ACT_MAGE))
    {
        mob->perm_stat[STAT_INT] += 3;
        mob->perm_stat[STAT_STR] -= 1;
        mob->perm_stat[STAT_DEX] += 1;
    }

    if (IS_SET(pMobIndex->off_flags, OFF_FAST))
        mob->perm_stat[STAT_DEX] += 2;

    /* let's get some spell action */
    if (IS_AFFECTED(mob,AFF_SANCTUARY))
    {
        af.where    = TO_AFFECTS;
        af.type     = sn_lookup("sanctuary");
        af.level    = mob->level;
        af.duration = -1;
        af.location = APPLY_NONE;
        af.modifier = 0;
        af.bitvector    = AFF_SANCTUARY;
        affect_to_char(mob, &af);
    }

    if (IS_AFFECTED(mob, AFF_HASTE))
    {
        af.where    = TO_AFFECTS;
        af.type     = sn_lookup("haste");
        af.level    = mob->level;
        af.duration = -1;
        af.location = APPLY_DEX;
        af.modifier = 1 + (mob->level >= 18) + (mob->level >= 25) +
                      (mob->level >= 32);
        af.bitvector    = AFF_HASTE;
        affect_to_char(mob, &af);
    }

    if (IS_AFFECTED(mob,AFF_PROTECT_EVIL))
    {
        af.where    = TO_AFFECTS;
        af.type     = sn_lookup("protection evil");
        af.level    = mob->level;
        af.duration = -1;
        af.location = APPLY_SAVES;
        af.modifier = -1;
        af.bitvector    = AFF_PROTECT_EVIL;
        affect_to_char(mob, &af);
    }

    if (IS_AFFECTED(mob,AFF_PROTECT_GOOD))
    {
        af.where    = TO_AFFECTS;
        af.type     = sn_lookup("protection good");
        af.level    = mob->level;
        af.duration = -1;
        af.location = APPLY_SAVES;
        af.modifier = -1;
        af.bitvector    = AFF_PROTECT_GOOD;
        affect_to_char(mob, &af);
    }

    if (IS_AFFECTED(mob,AFF_BLACK_SHROUD))
    {
        af.where     = TO_AFFECTS;
        af.type      = sn_lookup("black shroud");
        af.level     = mob->level;
        af.duration  = -1;
        af.location  = APPLY_SAVING_SPELL;
        af.modifier  = -5;
        af.bitvector = AFF_BLACK_SHROUD;
        affect_to_char(mob, &af);
    }

    /* link the mob to the world list */
    mob->next       = char_list;
    char_list       = mob;
    pMobIndex->count++;
    return mob;
}

CHAR_DATA *create_mob(MOB_INDEX_DATA *pMobIndex)
{
    return create_mob_org(pMobIndex, 0);
}

CHAR_DATA *create_named_mob(MOB_INDEX_DATA *pMobIndex, const char *name)
{
    CHAR_DATA *res;

    res = create_mob_org(pMobIndex, CREATE_NAMED);
    res->short_descr    = mlstr_printf(pMobIndex->short_descr, name);
    res->long_descr     = mlstr_printf(pMobIndex->long_descr, name);
    res->description    = mlstr_printf(pMobIndex->description, name);
    return res;
}

/* duplicate a mobile exactly -- except inventory */
void clone_mob(CHAR_DATA *parent, CHAR_DATA *clone)
{
    int i;
    AFFECT_DATA *paf;

    if (parent == NULL || clone == NULL || !IS_NPC(parent))
        return;

    /* start fixing values */
    clone->name         = str_dup(parent->name);
    clone->version      = parent->version;
    clone->short_descr  = mlstr_dup(parent->short_descr);
    clone->long_descr   = mlstr_dup(parent->long_descr);
    clone->description  = mlstr_dup(parent->description);
    clone->group        = parent->group;
    clone->sex      = parent->sex;
    clone->class        = parent->class;
                          clone->race     = parent->race;
    clone->level        = parent->level;
    clone->timer        = parent->timer;
    clone->wait         = parent->wait;
    clone->hit          = parent->hit;
    clone->max_hit      = parent->max_hit;
    clone->mana         = parent->mana;
    clone->max_mana     = parent->max_mana;
    clone->move         = parent->move;
    clone->max_move     = parent->max_move;
    clone->psp          = parent->psp;
    clone->gold         = parent->gold;
    clone->silver       = parent->silver;
    clone->exp          = parent->exp;
    clone->comm         = parent->comm;

    for (i = 0; i < MAX_DAM; i++)
        clone->resists[i] = parent->resists[i];
    clone->immunes      = parent->immunes;
    
    clone->invis_level  = parent->invis_level;
    clone->affected_by  = parent->affected_by;
    clone->position     = parent->position;
    clone->practice     = parent->practice;
    clone->train        = parent->train;
    clone->saving_throw = parent->saving_throw;
    clone->alignment    = parent->alignment;
    clone->hitroll      = parent->hitroll;
    clone->damroll      = parent->damroll;
    clone->wimpy        = parent->wimpy;
    clone->form         = parent->form;
    clone->parts        = parent->parts;
    clone->pcnt         = parent->pcnt;
    clone->size         = parent->size;
    clone->material     = str_dup(parent->material);
    clone->dam_type     = parent->dam_type;
    clone->start_pos    = parent->start_pos;
    clone->default_pos  = parent->default_pos;
    clone->spec_fun     = parent->spec_fun;
    clone->status       = parent->status;
    clone->hunting      = NULL;
    clone->clan         = parent->clan;
    clone->invis_level  = parent->invis_level;

    for (i = 0; i < 4; i++)
        clone->armor[i] = parent->armor[i];

    for (i = 0; i < MAX_STATS; i++)
    {
        clone->perm_stat[i] = parent->perm_stat[i];
        clone->mod_stat[i]  = parent->mod_stat[i];
    }

    for (i = 0; i < 3; i++)
        clone->damage[i]    = parent->damage[i];

    /* now add the affects */
    for (paf = parent->affected; paf != NULL; paf = paf->next)
        affect_to_char(clone,paf);

}

/*
 * Create an instance of an object.
 */
OBJ_DATA *create_obj_org(OBJ_INDEX_DATA *pObjIndex, int level, int flags)
{
    AFFECT_DATA *paf;
    OBJ_DATA *obj;
    int i;
    OBJ_INDEX_DATA * dummy_obj;

    if (pObjIndex == NULL)
    {
        bug("Create_object: NULL pObjIndex.", 0);
        dummy_obj = get_obj_index(OBJ_VNUM_DUMMY);
        if (dummy_obj == NULL)
        {
            bug("Create_object: dummy_obj doesn't exist!", 0);
            exit(1);
        }
        return create_obj_org (dummy_obj, level, flags);
    }

    obj = new_obj();

    obj->id  = get_obj_id();
    obj->pIndexData = pObjIndex;
    obj->level = level ? level : pObjIndex->level;
    obj->wear_loc   = -1;

    if (!IS_SET(flags, CREATE_NAMED))
    {
        obj->name       = str_qdup(pObjIndex->name);
        obj->short_descr    = mlstr_dup(pObjIndex->short_descr);
        obj->description    = mlstr_dup(pObjIndex->description);
    }
    obj->material       = str_qdup(pObjIndex->material);
    obj->extra_flags    = pObjIndex->extra_flags;
    obj->wear_flags     = pObjIndex->wear_flags;
    obj->value[0]       = pObjIndex->value[0];
    obj->value[1]       = pObjIndex->value[1];
    obj->value[2]       = pObjIndex->value[2];
    obj->value[3]       = pObjIndex->value[3];
    obj->value[4]       = pObjIndex->value[4];
    obj->weight         = pObjIndex->weight;
    obj->owner          = str_dup(str_empty); /* used with body parts */
    obj->condition      = pObjIndex->condition;
    obj->cost           = pObjIndex->cost;
    obj->size           = pObjIndex->size;

    /*
     * Mess with object properties.
     */
    switch (pObjIndex->item_type)
    {
    case ITEM_LIGHT:
        if (obj->value[2] == 999)
            obj->value[2] = -1;
        break;

    case ITEM_JUKEBOX:
        for (i = 0; i < 5; i++)
            obj->value[i] = -1;
        break;
    
/*
    case ITEM_WINDOW:
        for (i = 0; i < 5; i++)
            obj->value[i] = -1;
        break;
*/  }
        
   for (paf = pObjIndex->affected; paf != NULL; paf = paf->next)
        if (paf->location == APPLY_SPELL_AFFECT)
            affect_to_obj(obj,paf);

    obj->next   = object_list;
    object_list = obj;
    if (!IS_SET(flags, CREATE_NOCOUNT))
        pObjIndex->count++;
    return obj;
}

/*
 * Create an object with modifying the count
 */
OBJ_DATA *create_obj(OBJ_INDEX_DATA *pObjIndex, int level)
{
    return create_obj_org(pObjIndex, level, 0);
}

OBJ_DATA *create_named_obj(OBJ_INDEX_DATA *pObjIndex, int level,
                           const char *name)
{
    OBJ_DATA *res;

    res = create_obj_org(pObjIndex, level, CREATE_NAMED);
    res->name       = str_printf(pObjIndex->name, name);
    res->short_descr    = mlstr_printf(pObjIndex->short_descr, name);
    res->description    = mlstr_printf(pObjIndex->description, name);
    return res;
}

/*
 * for player load/quit
 * Create an object and do not modify the count
 */
OBJ_DATA *create_obj_nocount(OBJ_INDEX_DATA *pObjIndex, int level)
{
    return create_obj_org(pObjIndex, level, CREATE_NOCOUNT);
}

/* duplicate an object exactly -- except contents */
void clone_obj(OBJ_DATA *parent, OBJ_DATA *clone)
{
    int i;
    AFFECT_DATA *paf;
    ED_DATA *ed,*ed2;

    if (parent == NULL || clone == NULL)
        return;

    /* start fixing the object */
    clone->name         = str_dup(parent->name);
    clone->short_descr  = mlstr_dup(parent->short_descr);
    clone->description  = mlstr_dup(parent->description);
    clone->extra_flags  = parent->extra_flags;
    clone->wear_flags   = parent->wear_flags;
    clone->weight       = parent->weight;
    clone->cost     = parent->cost;
    clone->level        = parent->level;
    clone->condition    = parent->condition;
    clone->material     = str_dup(parent->material);
    clone->timer        = parent->timer;
    clone->owner        = parent->owner;
    clone->extracted    = parent->extracted;
    clone->size         = parent->size;

    for (i = 0;  i < 5; i ++)
        clone->value[i] = parent->value[i];

    for (paf = parent->affected; paf != NULL; paf = paf->next)
        affect_to_obj(clone,paf);

    /* extended desc */
    for (ed = parent->ed; ed != NULL; ed = ed->next)
    {
        ed2         = ed_new();
        ed2->keyword        = str_dup(ed->keyword);
        ed2->description    = mlstr_dup(ed->description);
        ed2->next       = clone->ed;
        clone->ed       = ed2;
    }

}

/*
 * Create an object with some random stats
 */
OBJ_DATA *create_obj_rand(OBJ_INDEX_DATA *pObjIndex, int level)
{
    AFFECT_DATA *paf;
    OBJ_DATA *obj;
    flag64_t val = 0;
    int i, new_apply, loc, apply, sum_apply;
    bool aff_found;
    const int loc_tab[] = {APPLY_HITROLL, APPLY_DAMROLL, APPLY_HIT, APPLY_MANA,
        APPLY_STR, APPLY_INT, APPLY_WIS, APPLY_DEX, APPLY_CON,
        APPLY_CHA};
    OBJ_INDEX_DATA * dummy_obj;

    if (pObjIndex == NULL)
    {
        bug("Create_object_rand: NULL pObjIndex.", 0);
        dummy_obj = get_obj_index(OBJ_VNUM_DUMMY);
        if (dummy_obj == NULL)
        {
            bug("Create_object_rand: dummy_obj doesn't exist!", 0);
            exit(1);
        }
        return create_obj_org (dummy_obj, level, 0);
    }

    if (pObjIndex->item_type != ITEM_WEAPON &&
        pObjIndex->item_type != ITEM_ARMOR)
        return create_obj_org(pObjIndex, level, 0);
    if (pObjIndex->limit != -1)
        return create_obj_org(pObjIndex, level, 0);
    if (number_percent() < 50)
        return create_obj_org(pObjIndex, level, 0);

    obj = new_obj();

    obj->pIndexData = pObjIndex;
    obj->level = level ? level : pObjIndex->level;
    obj->wear_loc   = -1;

    obj->name       = str_dup(pObjIndex->name);
    obj->short_descr    = mlstr_dup(pObjIndex->short_descr);
    obj->description    = mlstr_dup(pObjIndex->description);

    obj->material           = str_dup(pObjIndex->material);
    obj->extra_flags        = pObjIndex->extra_flags;
    obj->wear_flags         = pObjIndex->wear_flags;
    for (i = 0; i < 5; i++)
        obj->value[i] = pObjIndex->value[i];
    obj->weight             = pObjIndex->weight;
    obj->owner              = str_dup(str_empty); /* used with body parts */
    obj->condition          = pObjIndex->condition;
    obj->cost               = pObjIndex->cost;
    obj->size               = pObjIndex->size;

    for (paf = pObjIndex->affected; paf != NULL; paf = paf->next)
        if (paf->location == APPLY_SPELL_AFFECT)
            affect_to_obj(obj, paf);

/* make some apply to object */

    sum_apply = 0; /* for obj cost */
    if (pObjIndex->item_type == ITEM_ARMOR)
    { /* armor ac */
        for (i = 0; i < 4; i++)
        {
            apply = rand_range(obj->level / 10);
            obj->value[i] += apply;
            sum_apply += apply;
        }
        obj->cost += obj->cost / 100 * sum_apply;
    } else
    { /* weapon */
        for (i = 1; i < 3; i++)
            if (number_percent() > 95)
            {
                apply = rand_range(1);
                obj->value[i] += apply;
                sum_apply += apply;
            }
        obj->cost += obj->cost / 2 * sum_apply;
        if (number_percent() > 97)
        {
            switch (number_bits(2))
            {
            case 0: val = WEAPON_SHARP; break;
            case 1: val = WEAPON_VAMPIRIC; break;
            case 2: val = WEAPON_POISON; break;
            case 3: val = WEAPON_SHOCKING; break;
            }
            if (number_percent() > 50)
            {
                if (!IS_WEAPON_STAT(obj, val))
                {
                    SET_BIT(obj->value[4], val);
                    obj->cost += obj->cost / 2;
                }
            } else
            {
                if (IS_WEAPON_STAT(obj, val))
                {
                    REMOVE_BIT(obj->value[4], val);
                    obj->cost -= obj->cost / 2;
                }
            }
        }
    }

    /* some extra affects up to 3 */
    for (i = 0; i < 3; i++)
    {

        if (number_percent() > 40) break;

        loc = loc_tab[number_range(0, 9)];
        apply = new_apply = (loc == APPLY_HIT || loc == APPLY_MANA) ?
                rand_range(obj->level / 2) : rand_range(obj->level / 10);

        if (!new_apply) continue;

        affect_enchant(obj);

        aff_found = FALSE;
        paf = obj->affected;
        while (paf != NULL)
        {
            if (paf->location == loc)
            {
                if (new_apply > 0 && paf->modifier > 0)
                    /* overpower must die :) */
                    new_apply /= 2;
                new_apply += paf->modifier;
                aff_found = TRUE;
                break;
            }
            paf = paf->next;
        }
        if (aff_found && !new_apply)
        { /* affect = 0, remove it*/
            affect_remove_obj(obj, paf);
            continue; /* for */
        }

        if (aff_found)
        {
            for (paf = obj->affected; paf != NULL; paf = paf->next)
                if (paf->location == loc)
                {
                    paf->modifier = new_apply;
                    paf->type = 0;
                    break;
                }
        } else
        { /* new affect */
            paf = aff_new();
            paf->location   = loc;
            paf->modifier   = new_apply;
            paf->where      = TO_OBJECT;
            paf->type       = 0;
            paf->duration   = -1;
            paf->bitvector  = 0;
            paf->level      = obj->level;
            paf->next   = obj->affected;
            obj->affected   = paf;
        }

        if (apply > 0)
            obj->cost += obj->cost / 5;
        else
            obj->cost -= obj->cost / 5;
    }

    obj->next       = object_list;
    object_list     = obj;
    pObjIndex->count++;
    return obj;
}

/* random range. for random items */
int rand_range(int to)
{
    int appl;

    appl = number_range(1, to);

    switch (number_bits(2))
    {
    case 1: return -appl;
    case 3: return appl;
    default: return 0;
    }
}


/*
 * Get an extra description from a list.
 */
ED_DATA *ed_lookup(const char *name, ED_DATA *ed)
{
    for (; ed != NULL; ed = ed->next)
    {
        if (is_name(name, ed->keyword))
            return ed;
    }
    return NULL;
}

/*
 * Translates mob virtual number to its mob index struct.
 * Hash table lookup.
 */
MOB_INDEX_DATA *get_mob_index(int vnum)
{
    MOB_INDEX_DATA *pMobIndex;

    if (vnum <= 0)
        return NULL;

    for (pMobIndex = mob_index_hash[vnum % MAX_KEY_HASH];
        pMobIndex; pMobIndex = pMobIndex->next)
        if (pMobIndex->vnum == vnum)
            return pMobIndex;

    if (fBootDb)
        db_error("get_mob_index", "bad vnum %d.", vnum);

    return NULL;
}

/*
 * Translates mob virtual number to its obj index struct.
 * Hash table lookup.
 */
OBJ_INDEX_DATA *get_obj_index(int vnum)
{
    OBJ_INDEX_DATA *pObjIndex;

    if (vnum <= 0)
        return NULL;

    for (pObjIndex = obj_index_hash[vnum % MAX_KEY_HASH];
        pObjIndex; pObjIndex = pObjIndex->next)
        if (pObjIndex->vnum == vnum)
            return pObjIndex;

    if (fBootDb)
        db_error("get_obj_index", "bad vnum %d.", vnum);

    return NULL;
}

/*
 * Translates mob virtual number to its room index struct.
 * Hash table lookup.
 */
ROOM_INDEX_DATA *get_room_index(int vnum)
{
    ROOM_INDEX_DATA *pRoomIndex;

    if (vnum <= 0)
        return NULL;

    for (pRoomIndex = room_index_hash[vnum % MAX_KEY_HASH]; pRoomIndex; pRoomIndex = pRoomIndex->next)
        if (pRoomIndex->vnum == vnum)
            return pRoomIndex;

    if (fBootDb)
        db_error("get_room_index", "bad vnum %d.", vnum);

    return NULL;
}

int xgetc(FILE *fp)
{
    int c = getc(fp);
    if (c == '\n')
        line_number++;
    return c;
}

void xungetc(int c, FILE *fp)
{
    if (c == '\n')
        line_number--;
    ungetc(c, fp);
}

/*
 * smash '\r', dup '~'
 */
char *fix_string(const char *s)
{
    static char buf[MAX_STRING_LENGTH * 2];
    char *p = buf;

    if (IS_NULLSTR(s))
        return str_empty;

    for (p = buf; p-buf < sizeof(buf)-2 && *s; s++)
        switch (*s)
        {
        case '\r':
            break;

        case '~':
            *p++ = *s;
            /* FALLTHRU */

        default:
            *p++ = *s;
            break;
        }

    *p = '\0';
    return buf;
}

const char *fread_string(FILE *fp)
{
    char buf[MAX_STRING_LENGTH];
    char *plast;
    int c;

    plast = buf;

    /*
     * Skip blanks.
     * Read first char.
     */
    do
        c = xgetc(fp);
    while (isspace(c));

    for (;;)
    {
        /*
         * Back off the char type lookup,
         *   it was too dirty for portability.
         *   -- Furey
         */

        if (plast - buf >= sizeof(buf) - 1)
        {
            bug("fread_string: line too long (truncated)", 0);
            buf[sizeof(buf)-1] = '\0';
            return str_dup(buf);
        }

        switch (c)
        {
        default:
            *plast++ = c;
            break;

        case EOF:
            db_error("fread_string", "EOF");
            return str_empty;

        case '\r':
            break;

        case '~':
            if ((c = xgetc(fp)) == '~')
            {
                *plast++ = c;
                break;
            }
            xungetc(c, fp);
            *plast = '\0';
            return str_dup(buf);
        }
        c = xgetc(fp);
    }
}

void fwrite_string(FILE *fp, const char *name, const char *str)
{
    if (IS_NULLSTR(name))
        fprintf(fp, "%s~\n", fix_string(str));
    else if (!IS_NULLSTR(str))
        fprintf(fp, "%s %s~\n", name, fix_string(str));
}

/*
 * Read a letter from a file.
 */
char fread_letter(FILE *fp)
{
    char c ;

    do c = xgetc(fp);
    while (c != EOF && isspace(c));

    return c ;
}

/*
 * Read a number from a file.
 */
int fread_number(FILE *fp)
{
    int number;
    bool sign;
    char c;

    do
        c = xgetc(fp);
    while (isspace(c));

    number = 0;

    sign   = FALSE;
    if (c == '+')
        c = xgetc(fp);
    else if (c == '-')
    {
        sign = TRUE;
        c = xgetc(fp);
    }

    if (!isdigit(c))
    {
        if (fBootDb)
            db_error("fread_number", "bad format");
        log("fread_number: bad format");
        exit(1);
    }

    while (isdigit(c))
    {
        number = number * 10 + c - '0';
        c      = xgetc(fp);
    }

    if (sign)
        number = 0 - number;

    if (c == '|')
        number += fread_number(fp);
    else if (c != ' ')
        xungetc(c, fp);

    return number;
}

flag64_t fread_flags(FILE *fp)
{
    flag64_t number;
    char c;
    bool negative = FALSE;

    do
        c = xgetc(fp);
    while (isspace(c));

    if (c == '-')
    {
        negative = TRUE;
        c = xgetc(fp);
    }

    number = 0;

    if (!isdigit(c))
    {
        while (('A' <= c && c <= 'Z') || ('a' <= c && c <= 'z'))
        {
            number += flag_convert(c);
            c = xgetc(fp);
        }
    }

    while (isdigit(c))
    {
        number = number * 10 + c - '0';
        c = xgetc(fp);
    }

    if (c == '|')
        number += fread_flags(fp);
    else if (c != ' ')
        xungetc(c, fp);

    if (negative)
        return -number;

    return number;
}

flag64_t flag_convert(char letter)
{
    flag64_t bitsum = 0;
    char i;

    if ('A' <= letter && letter <= 'Z')
    {
        bitsum = A;
        for (i = letter; i > 'A'; i--)
            bitsum <<= 1;
    } else if ('a' <= letter && letter <= 'z')
    {
        bitsum = aa;
        for (i = letter; i > 'a'; i--)
            bitsum <<= 1;
    }

    return bitsum;
}

const char *fread_string_eol(FILE *fp)
{
    char buf[MAX_STRING_LENGTH];
    char *plast;
    int c;

    plast = buf;

    /*
     * Skip blanks.
     * Read first char.
     */
    do
        c = xgetc(fp);
    while (isspace(c));

    for (;;)
    {
        /*
         * Back off the char type lookup,
         *   it was too dirty for portability.
         *   -- Furey
         */

        if (plast - buf >= sizeof(buf) - 1)
        {
            bug("fread_string_eol: line too long (truncated)", 0);
            buf[sizeof(buf)-1] = '\0';
            return str_dup(buf);
        }

        switch (c)
        {
        default:
            *plast++ = c;
            break;

        case EOF:
            db_error("fread_string_eol", "EOF");
            return str_empty;

        case '\r':
            break;

        case '\n':
            if ((c = xgetc(fp)) == '~')
            {
                *plast++ = c;
                break;
            }
            xungetc(c, fp);
            *plast = '\0';
            return str_dup(buf);
        }
        c = xgetc(fp);
    }
}
/*
 * Read to end of line (for comments).
 */
void fread_to_eol (FILE * fp)
{
    char c ;

    do c = xgetc(fp);
    while (c != EOF && c != '\n' && c != '\r');

    if (c == EOF) return ;

    do c = xgetc(fp);
    while (c == '\n' || c == '\r');

    if (c == EOF) return ;

    xungetc(c, fp);
    return ;
}

/*
 * Read one word (into static buffer).
 */
char * fread_word (FILE * fp)
{
    static char word [MAX_INPUT_LENGTH] ;
    char * pword ;
    char   cEnd  ;

    do cEnd = xgetc(fp);
    while (cEnd && isspace(cEnd));

    if (cEnd == EOF) return NULL ;

    if (cEnd == '\'' || cEnd == '"')
        pword   = word ;
    else
    {
        word[0] = cEnd     ;
        pword   = word + 1 ;
        cEnd    = ' '      ;
    }

    for (; pword < word + MAX_INPUT_LENGTH ; pword++)
    {
        *pword = xgetc (fp);

        if (*pword == EOF)
        {
            *pword = '\0' ;
            return word ;
        }

        if ((cEnd == ' ') ? isspace(*pword) : (*pword == cEnd))
        {
            if (cEnd == ' ') xungetc (*pword, fp);

            *pword = '\0' ;
            return word ;
        }
    }

    db_error ("fread_word", "word too long");
    return NULL ;
}

void *alloc_mem(int sMem)
{
    return calloc(1, sMem);
}

void free_mem(void *p, int sMem)
{
    free(p);
}

void *alloc_perm(int sMem)
{
    return calloc(1, sMem);
}

DO_FUN(do_areas)
{

    AREA_DATA *pArea, *pSort, *pCurr=NULL;
    static char arg[MAX_INPUT_LENGTH];
    vnum_type last_vnum=0;
    bool fSort=FALSE;
    AREA_DATA *pArea1;
    AREA_DATA *pArea2;
    int iArea;
    int iAreaHalf;
    BUFFER *output;

    if (argument[0] != '\0'
        && !IS_IMMORTAL(ch))
    {
        char_act("No argument is used with this command.", ch);
        return;
    }

    if (argument[0] != '\0')
    {
        argument = one_argument(argument, arg, sizeof(arg));

        if (!str_cmp( arg, "sort"))
        {
            fSort=TRUE;
            one_argument(argument, arg, sizeof(arg));
        } else
        {
            char_act("Type SORT for sorting VNUM's.", ch);
            return;
        }

        output = buf_new(-1);
        buf_printf (output, "[%3s] %-20s (%-6s-%6s) [%-16s] %3s %s\n\r",
                    "Num", "Area Name", "lvnum", "uvnum", "Filename", "Sec", "Builders");

        for (pArea = area_first; pArea; pArea = pArea->next)
        {
            if (fSort)
            {
                for (pSort = area_first; pSort; pSort = pSort->next)
                    if (pSort->min_vnum > last_vnum)
                        if (pCurr == NULL || pSort->min_vnum < pCurr->min_vnum)
                            pCurr = pSort;
            } else
                pCurr = pArea;

            if (!pCurr)
                continue;

            if (!IS_NULLSTR (arg) 
                && !is_name (arg, pCurr->name))
                continue;

            buf_printf (output, "[%3d] %s (%-6d-%6d) [%-16s] [%d] %s\n\r",
                        pCurr->vnum,
                        fmt_color_str(pCurr->name, 20),
                        pCurr->min_vnum,
                        pCurr->max_vnum,
                        pCurr->file_name,
                        pCurr->security,
                        pCurr->builders);

            last_vnum = pCurr->max_vnum;
            pCurr = NULL;
        }

        page_to_char(buf_string(output), ch);
        buf_free(output);
        return;
    } else
    {

        iAreaHalf = (top_area + 1) / 2;
        pArea1    = area_first;
        pArea2    = area_first;
        for (iArea = 0; iArea < iAreaHalf; iArea++)
            pArea2 = pArea2->next;

        output = buf_new(-1);
        buf_add(output, "Current areas of {CAstrum Metaphora{x: \n");
        for (iArea = 0; iArea < iAreaHalf; iArea++)
        {
            buf_printf(output,"{{%2d %3d} {B%s{x %8.8s ",
                       pArea1->min_level,pArea1->max_level,
                       fmt_color_str(pArea1->name, 20),
                       pArea1->credits);

            if (pArea2 != NULL)
                buf_printf(output,"{{%2d %3d} {B%s{x %8.8s",
                           pArea2->min_level,pArea2->max_level,
                           fmt_color_str(pArea2->name, 20),
                           pArea2->credits);
            buf_add(output, "\n");

            pArea1 = pArea1->next;
            if (pArea2 != NULL)
                pArea2 = pArea2->next;
        }

        buf_add(output,"\n");
        page_to_char(buf_string(output), ch);
        buf_free(output);
    }
}

DO_FUN(do_memory)
{
    extern int mlstr_count;
    extern int mlstr_real_count;
    extern int str_count;
    extern int str_real_count;

    char_printf(ch, "Affects  : %d\n", top_affect );
    char_printf(ch, "Areas    : %d\n", top_area   );
    char_printf(ch, "ExDes    : %d\n", top_ed     );
    char_printf(ch, "Exits    : %d\n", top_exit   );
    char_printf(ch, "Helps    : %d\n", top_help   );
    char_printf(ch, "Socials  : %d\n", socials.nused);
    char_printf(ch, "Mobs     : %d (%d new format, %d in use)\n", top_mob_index, newmobs, mobile_count);
    char_printf(ch, "Objs     : %d (%d new format)\n",top_obj_index, newobjs);
    char_printf(ch, "Resets   : %d\n", top_reset  );
    char_printf(ch, "Rooms    : %d\n", top_room   );
    char_printf(ch, "Shops    : %d\n", top_shop   );
    char_printf(ch, "Buffers  : %d (%d bytes)\n",     nAllocBuf, sAllocBuf);
    char_printf(ch, "strings  : %d (%d allocated)\n", str_count, str_real_count);
    char_printf(ch, "mlstrings: %d (%d allocated)\n", mlstr_count, mlstr_real_count);
    char_printf(ch, "MobProgs : %d\n", top_mprog_index);
    char_printf(ch, "ObjProgs : %d\n", top_oprog_index);
    char_printf(ch, "RoomProgs: %d\n", top_rprog_index);

}

DO_FUN(do_dump)
{
    int count,count2,num_pcs,aff_count;
    CHAR_DATA *fch;
    MOB_INDEX_DATA *pMobIndex;
    OBJ_DATA *obj;
    OBJ_INDEX_DATA *pObjIndex;
    ROOM_INDEX_DATA *room;
    EXIT_DATA *exit;
    PC_DATA *pc;
    DESCRIPTOR_DATA *d;
    AFFECT_DATA *af;
    FILE *fp;
    int vnum,nMatch = 0;

    /* open file */
    fclose(fpReserve);
    fp = dfopen(TMP_PATH, "mem.dmp", "w");

    /* report use of data structures */

    num_pcs = 0;
    aff_count = 0;

    /* mobile prototypes */
    fprintf(fp,"MobProt %4d (%8d bytes)\n",
            top_mob_index, top_mob_index * (sizeof(*pMobIndex)));

    /* mobs */
    count = 0;
    for (fch = char_list; fch != NULL; fch = fch->next)
    {
        count++;
        if (fch->pcdata != NULL)
            num_pcs++;
        for (af = fch->affected; af != NULL; af = af->next)
            aff_count++;
    }

    fprintf(fp,"Mobs    %4d (%8d bytes)\n",
            count, count * (sizeof(*fch)));

    fprintf(fp,"Pcdata  %4d (%8d bytes)\n",
            num_pcs, num_pcs * (sizeof(*pc)));

    /* descriptors */
    count = 0; count2 = 0;
    for (d = descriptor_list; d != NULL; d = d->next)
        count++;
    for (d= descriptor_free; d != NULL; d = d->next)
        count2++;

    fprintf(fp, "Descs  %4d (%8d bytes), %2d free (%d bytes)\n",
            count, count * (sizeof(*d)), count2, count2 * (sizeof(*d)));

    /* object prototypes */
    for (vnum = 0; nMatch < top_obj_index; vnum++)
        if ((pObjIndex = get_obj_index(vnum)) != NULL)
        {
            for (af = pObjIndex->affected; af != NULL; af = af->next)
                aff_count++;
            nMatch++;
        }

    fprintf(fp,"ObjProt %4d (%8d bytes)\n",
            top_obj_index, top_obj_index * (sizeof(*pObjIndex)));

    /* objects */
    count = 0;
    for (obj = object_list; obj != NULL; obj = obj->next)
    {
        count++;
        for (af = obj->affected; af != NULL; af = af->next)
            aff_count++;
    }

    fprintf(fp,"Objs    %4d (%8d bytes)\n",
            count, count * (sizeof(*obj)));

    /* affects */
    fprintf(fp,"Affects %4d (%8d bytes)\n",
            aff_count, aff_count * (sizeof(*af)));

    /* rooms */
    fprintf(fp,"Rooms   %4d (%8d bytes)\n",
            top_room, top_room * (sizeof(*room)));

    /* exits */
    fprintf(fp,"Exits   %4d (%8d bytes)\n",
            top_exit, top_exit * (sizeof(*exit)));

    fclose(fp);

    /* start printing out mobile data */
    fp = dfopen(TMP_PATH, "mob.dmp", "w");

    fprintf(fp,"\nMobile Analysis\n");
    fprintf(fp,  "---------------\n");
    nMatch = 0;
    for (vnum = 0; nMatch < top_mob_index; vnum++)
        if ((pMobIndex = get_mob_index(vnum)) != NULL)
        {
            nMatch++;
            fprintf(fp,"#%-4d %3d active %3d killed     %s\n",
                    pMobIndex->vnum,pMobIndex->count,
                    pMobIndex->killed,mlstr_mval(pMobIndex->short_descr));
        }
    fclose(fp);

    /* start printing out object data */
    fp = dfopen(TMP_PATH, "obj.dmp", "w");

    fprintf(fp,"\nObject Analysis\n");
    fprintf(fp,  "---------------\n");
    nMatch = 0;
    for (vnum = 0; nMatch < top_obj_index; vnum++)
        if ((pObjIndex = get_obj_index(vnum)) != NULL)
        {
            nMatch++;
            fprintf(fp,"#%-4d %3d active %3d reset      %s\n",
                    pObjIndex->vnum,pObjIndex->count,
                    pObjIndex->reset_num,
                    mlstr_mval(pObjIndex->short_descr));
        }

        /* close file */
    fclose(fp);
    fpReserve = fopen(NULL_FILE, "r");
}

/*
 * Stick a little fuzz on a number.
 */
int number_fuzzy(int number)
{
    switch (number_bits(2))
    {
    case 0:  number -= 1; break;
    case 3:  number += 1; break;
    }

    return UMAX(1, number);
}

/*
 * Generate a random number.
 */
int number_range(int from, int to)
{
    int power;
    int number;

    if (from == 0 && to == 0)
        return 0;

    if ((to = to - from + 1) <= 1)
        return from;

    for (power = 2; power < to; power <<= 1)
        ;

    while ((number = number_mm() & (power -1)) >= to)
        ;

    return from + number;
}

/*
 * Generate a percentile roll.
 */
int number_percent(void)
{
    int percent;

    while ((percent = number_mm() & (128-1)) > 99)
        ;

    return 1 + percent;
}

/*
 * Generate a random door.
 */
int number_door(void)
{
    int door;

    while ((door = number_mm() & (8-1)) > 5)
        ;

    return door;
}

int number_bits(int width)
{
    return number_mm() & ((1 << width) - 1);
}

/*
 * I've gotten too many bad reports on OS-supplied random number generators.
 * This is the Mitchell-Moore algorithm from Knuth Volume II.
 * Best to leave the constants alone unless you've read Knuth.
 * -- Furey
 */

/* I noticed streaking with this random number generator, so I switched
    back to the system srandom call.  If this doesn't work for you,
    define OLD_RAND to use the old system -- Alander */
#if !defined (NEW_RAND)
#if  defined (OLD_RAND)
static  int     rgiState[2+55];
#endif
#endif

void init_mm()
{
#if !defined (NEW_RAND)
#if  defined (OLD_RAND)
    int *piState;
    int iState;

    piState     = &rgiState[2];

    piState[-2] = 55 - 55;
    piState[-1] = 55 - 24;

    piState[0]  = ((int) current_time) & ((1 << 30) - 1);
    piState[1]  = 1;
    for (iState = 2; iState < 55; iState++)
    {
        piState[iState] = (piState[iState-1] + piState[iState-2])
                          & ((1 << 30) - 1);
    }
#else
    srandom(time(NULL)^getpid());
#endif
#else
    mt_bestseed () ; //Using /dev/random. See mtwist.h
#endif
    return;
}

long number_mm(void)
{
#if !defined (NEW_RAND)
#if  defined (OLD_RAND)
    int *piState;
    int iState1;
    int iState2;
    int iRand;

    piState             = &rgiState[2];
    iState1             = piState[-2];
    iState2             = piState[-1];
    iRand               = (piState[iState1] + piState[iState2])
                          & ((1 << 30) - 1);
    piState[iState1]    = iRand;
    if (++iState1 == 55)
        iState1 = 0;
    if (++iState2 == 55)
        iState2 = 0;
    piState[-2]         = iState1;
    piState[-1]         = iState2;
    return iRand >> 6;
    printf ("old random") ;
#else
    return random() >> 6;
    printf ("random") ;
#endif
#else
    return mt_lrand() >> 6 ;
#endif
}

/*
 * Roll some dice.
 */
int dice(int number, int size)
{
    int idice;
    int sum;

    switch (size)
    {
    case 0: return 0;
    case 1: return number;
    }

    for (idice = 0, sum = 0; idice < number; idice++)
        sum += number_range(1, size);

    return sum;
}

/*
 * Simple linear interpolation.
 */
int interpolate(int level, int value_00, int value_32)
{
    return value_00 + level * (value_32 - value_00) / 32;
}

/*
 * Returns an initial-capped string.
 */
char *capitalize(const char *str)
{
    static char strcap[MAX_STRING_LENGTH];
    int i;

    if (!str)
        return "\0";

    for (i = 0; str[i] != '\0'; i++)
        strcap[i] = LOWER(str[i]);
    strcap[i] = '\0';
    strcap[0] = UPPER(strcap[0]);
    return strcap;
}

/*
 * Append a string to a file.
 */
void append_file(CHAR_DATA *ch, const char *file, const char *str)
{
    FILE *fp;

    if (IS_NPC(ch) || str[0] == '\0')
        return;

    fclose(fpReserve);
    if ((fp = dfopen(TMP_PATH, file, "a")) == NULL)
    {
        perror(file);
        char_act("Could not open the file!", ch);
    } 
    else
    {
        fprintf(fp, "[%5d] %s: %s\n",
                ch->in_room ? ch->in_room->vnum : 0, ch->name, str);
        fclose(fp);
    }

    fpReserve = fopen(NULL_FILE, "r");
    return;
}

/*
 * This function is here to aid in debugging.
 * If the last expression in a function is another function call,
 *   gcc likes to generate a JMP instead of a CALL.
 * This is called "tail chaining."
 * It hoses the debugger call stack for that call.
 * So I make this the last call in certain critical functions,
 *   where I really need the call stack to be right for debugging!
 *
 * If you don't understand this, then LEAVE IT ALONE.
 * Don't remove any calls to tail_chain anywhere.
 *
 * -- Furey
 */
void tail_chain(void)
{
    return;
}

/*
 * Add the objects in players not logged on to object count
 */
void load_limited_objects ()
{
    struct dirent * dp ;

    DIR  * dirp   ;
    FILE * pfile  ;
    FILE * hfile  ;
    char   letter ;
    char * word   ;
    int    vnum   ;
    bool   holder ;

    if ((dirp = opendir (PLAYER_PATH)) == NULL)
    {
        bug ("Load_limited_objects: unable to open player directory.", 0);
        exit(1);
    }

    if ((hfile = dfopen (PLAYER_PATH, HOLDER_LIST, "w")) == NULL)
    {
        bug ("Load_limited_objects: unable to create holder.lst.", 0);
        exit(1);
    }

    // check each player if he has limited objects
    for (dp = readdir (dirp); dp != NULL ; dp = readdir (dirp))
    {
        const char * pname ;

        // only BSD implementation has d_type member of dirent structure
        #if defined (__FreeBSD__)
        if (dp->d_namlen < 3 || dp->d_type != DT_REG) continue ;
        #else
        if (strlen(dp->d_name) < 3) continue ;
        #endif

        // do not process HOLDER_LIST file in any
        if (!str_cmp (dp->d_name, HOLDER_LIST)) continue ;

        // open pfile
        if ((pfile = dfopen (PLAYER_PATH, dp->d_name, "r")) == NULL)
        {
            log_printf ("Load_limited_objects: Can't open player file %s.",
                        dp->d_name);
            continue ;
        }

        // initially assume no limited objects are kept
        holder = FALSE ;

        // look through player data
        pname = NULL ;

        for (;;)
        {
            letter = fread_letter (pfile);

            if (letter == '*')
            {
                fread_to_eol (pfile); continue ;
            }

            if (letter != '#')
            {
                log_printf ("Load_limited_objects: # not found in %s.",
                            dp->d_name);
                break ;
            }

            word = fread_word (pfile); // word - static buffer ptr

            // process player section
            if (!str_cmp (word, "PLAYER"))
            {
                for (;;)
                {
                    word = fread_word (pfile); // word - static buffer ptr

                    // until EOF or End
                    if (feof (pfile) || !word || !str_cmp (word, "End")) break;

                    // load player name
                    if (!str_cmp (word, "Name"))
                    {
                        free_string (pname);
                        pname = fread_string (pfile); // allocate here
                    } else
                        if (!str_cmp (word, "PC_Killed"))
                    {
                        // no player name is found yet
                        if (pname == NULL)
                            bug ("load_limited_objsects: "
                                 "PC_Killed before Name in pfile", 0);
                        else
                            // update rating
                            rating_add (pname, fread_number (pfile));
                    } else
                        if (!str_cmp (word, "Desc"))
                    {
                        // workaround to skip description
                        mlstring * desc = mlstr_fread (pfile);
                        mlstr_free (desc);
                    } else fread_to_eol (pfile);
                }
            } else
                // process object section
                if (!str_cmp (word, "O") || !str_cmp (word, "OBJECT"))
            {
                for (;;)
                {
                    word = fread_word (pfile); // word - static buffer ptr

                    // until EOF or End
                    if (feof (pfile) || !word || !str_cmp (word, "End")) break;

                    if (!str_cmp (word, "Vnum"))
                    {
                        OBJ_INDEX_DATA * oid ;
                        vnum = fread_number (pfile);

                        if ((oid = get_obj_index(vnum)) != NULL)
                        {
                            // increase counter for this object in the world
                            oid->count++ ;

                            // check if this object is limited
                            if (oid->limit > -1) holder = TRUE ;
                        }
                    } else fread_to_eol (pfile);
                }
            } else
                if (!str_cmp (word, "PET")) break ;
            /* golem and undead section */
            else
                if (!str_cmp (word, "GOLEM")) break ;
            else
                if (!str_cmp (word, "UNDEAD")) break ;
            /* end golem and undead section */
            else
                if (!str_cmp (word, "EXPLORED")) break ;
            else
                if (!str_cmp (word, "END")) break ;
            else
            {
                log_printf ("Load_limited_objects: bad section in %s.",
                            dp->d_name);
                break ;
            }
        }

        // this player has limited objects
        if (holder && pname) fprintf (hfile, "%s\n", pname);

        free_string (pname);
        fclose      (pfile);
    }

    fclose  (hfile);
    closedir (dirp);
}

#define NBUF 5
#define NBITS 52

char *format_flags(flag64_t flags)
{
    static int cnt;
    static char buf[NBUF][NBITS+1];
    int count, pos = 0;

    cnt = (cnt + 1) % NBUF;

    for (count = 0; count < NBITS;  count++)
        if (IS_SET(flags, (flag64_t) 1 << count))
        {
            if (count < 26)
                buf[cnt][pos] = 'A' + count;
            else
                buf[cnt][pos] = 'a' + (count - 26);
            pos++;
        }

    if (pos == 0)
        buf[cnt][pos++] = '0';

    buf[cnt][pos] = '\0';
    return buf[cnt];
}

void db_error(const char* fn, const char* fmt,...)
{
    char buf[MAX_STRING_LENGTH];
    va_list ap;

    va_start(ap, fmt);
    vsnprintf(buf, sizeof(buf), fmt, ap);
    va_end(ap);

    if (fBootDb)
    {
        log_printf("%s: line %d: %s: %s",
                   filename, line_number, fn, buf);
        exit(1);
    }

    log_printf("%s: %s", fn, buf);
}

/*****************************************************************************
 Name:          convert_objects
 Purpose:   Converts all old format objects to new format
 Called by: boot_db (db.c).
 ****************************************************************************/
void convert_objects(void)
{
    int i;
    if (newobjs == top_obj_index)
        return; /* all objects in new format */

    for (i = 0; i < MAX_KEY_HASH; i++)
    {
        OBJ_INDEX_DATA *pObj;

        for (pObj = obj_index_hash[i]; pObj; pObj = pObj->next)
            if (IS_SET(pObj->extra_flags, ITEM_OLDSTYLE))
                convert_object(pObj);
    }
}

/*****************************************************************************
 Name:      convert_object
 Purpose:   Converts an ITEM_OLDSTYLE obj to new format
 Called by: convert_objects (db2.c).
 Note:          Dug out of create_obj (db.c)
 Author:        Hugin
 ****************************************************************************/
void convert_object(OBJ_INDEX_DATA *pObjIndex)
{
    int level;
    int number, type;  /* for dice-conversion */

    level = pObjIndex->level;

    pObjIndex->cost     = 10*level;

    switch (pObjIndex->item_type)
    {
    default:
        bug("Obj_convert: vnum %d bad type.", pObjIndex->item_type);
        break;

    case ITEM_LIGHT:
    case ITEM_TREASURE:
    case ITEM_FURNITURE:
    case ITEM_TRASH:
    case ITEM_CONTAINER:
    case ITEM_DRINK_CON:
    case ITEM_KEY:
    case ITEM_FOOD:
    case ITEM_BOAT:
    case ITEM_CORPSE_NPC:
    case ITEM_CORPSE_PC:
    case ITEM_FOUNTAIN:
    case ITEM_MAP:
    case ITEM_CLOTHING:
    case ITEM_SCROLL:
        break;

    case ITEM_WAND:
    case ITEM_STAFF:
        pObjIndex->value[2] = pObjIndex->value[1];
        break;

    case ITEM_WEAPON:

        /*
         * The conversion below is based on the values generated
         * in one_hit() (fight.c).  Since I don't want a lvl 50
         * weapon to do 15d3 damage, the min value will be below
         * the one in one_hit, and to make up for it, I've made
         * the max value higher.
         * (I don't want 15d2 because this will hardly ever roll
         * 15 or 30, it will only roll damage close to 23.
         * I can't do 4d8+11, because one_hit there is no dice-
         * bounus value to set...)
         *
         * The conversion below gives:

         level:   dice      min      max      mean
           1:     1d8      1(2)    8(7)     5(5)
           2:     2d5      2(3)   10(8)     6(6)
           3:     2d5      2(3)   10(8)     6(6)
           5:     2d6      2(3)   12(10)     7(7)
          10:     4d5      4(5)   20(14)    12(10)
          20:     5d5      5(7)   25(21)    15(14)
          30:     5d7      5(10)   35(29)    20(20)
          50:     5d11     5(15)   55(44)    30(30)

         */

        number = UMIN(level/4 + 1, 5);
        type   = (level + 7)/number;

        pObjIndex->value[1] = number;
        pObjIndex->value[2] = type;
        break;

    case ITEM_ARMOR:
        pObjIndex->value[0] = level / 5 + 3;
        pObjIndex->value[1] = pObjIndex->value[0];
        pObjIndex->value[2] = pObjIndex->value[0];
        break;

    case ITEM_POTION:
    case ITEM_PILL:
        break;

    case ITEM_MONEY:
        pObjIndex->value[0] = pObjIndex->cost;
        break;
    }

    REMOVE_BIT(pObjIndex->extra_flags, ITEM_OLDSTYLE);
    ++newobjs;
}

/*
 * read flag word (not f-word :)
 */
flag64_t fread_fword(const flag_t *table, FILE *fp)
{
    char *name = fread_word(fp);

    if (is_number(name))
        return atoi(name);

    return flag_value(table, name);
}

flag64_t fread_fstring(const flag_t *table, FILE *fp)
{
    const char *s = fread_string(fp);
    flag64_t val;

    if (is_number(s))
        val = atoi(s);
    else
        val = flag_value(table, s);

    free_string(s);
    return val;
}

void *fread_namedp(namedp_t *table, FILE *fp)
{
    char *name = fread_word(fp);
    namedp_t *np = namedp_lookup(table, name);

    if (np == NULL)
        db_error("fread_namedp", "%s: unknown named pointer", name);

    np->touched = TRUE;
    return np->p;
}

int fread_clan(FILE *fp)
{
    int cn;
    const char *name;

    name = fread_string(fp);
    cn = cln_lookup(name);
    if (cn < 0)
    {
        db_error("fread_clan", "%s: unknown clan", name);
        cn = 0;
    }
    free_string(name);
    return cn;
}

/*
 *  Read a floating point number from file
 */

double  fread_fnumber(FILE *fp)
{
    double  num;

    fscanf(fp, "%lf", &num);

    return  num;
}

/*
 * Stick a certain amount of fuzz on a number.
 */
int percent_fuzzy( int number, int prcnt )
{
    int amnt = prcnt * number / 100;
    return number_range( number - amnt, number + amnt );
}