// DragonBall Arena 2 has been written by:
//   Matt Brown (Antor), arkaine@sympatico.ca, 2000-2002
// Please follow all previous licenses. Enjoy!

#include <sys/types.h>
#include <stdio.h>
#include <string.h>
#include "merc.h"
#include "recycle.h"
#include "tables.h"
#include "olc.h"
#include "db.h"
#include "rand.h"

// **Locals**
RoomData        *AddRoom          args ( ( int nX, int nY, MapData *pMap ) );
char            *GenRoomDescr     args ( ( ) );
int              GetRandMobVnum   args ( ( int nDiffLow, int nDiffHigh ) );
void             CreateMap        args ( ( int nMapVnum, MapData *pMap ) );
RoomData        *Dig              args ( ( RoomData *pRoom, int nDir, int nState, MapData *pMap ) );
AREA_DATA       *GenerateRandArea args ( ( int nVnumLow, int nDiffLow, int nDiffHigh ) );
void             AddWpnSpec       args ( ( OBJ_DATA *pObj, WpnEffectData *pEffect, WpnSpecData *pSpec ) );
// Prototypes from mem.c
AREA_DATA	    *new_area		  args ( ( void ) );
EXIT_DATA	    *new_exit		  args ( ( void ) );
ROOM_INDEX_DATA *new_room_index	  args ( ( void ) );

// ie north is (0,-1) relative to a position (1 up), east (1,0) (1 right)
int nExitDirEW[6] = {0,1,0,-1,0,0};
int nExitDirNS[6] = {-1,0,1,0,0,0};
// Opposite directions for each direction
int nOppDir[6] = {DIR_SOUTH, DIR_WEST, DIR_NORTH, DIR_EAST, DIR_DOWN, DIR_UP};

#define DESCR_COUNT 12
DescrData ForestDescr[] = {
    {6, "There is a %s here, %s.", {"stone", "boulder", "tombstone"}, {"covered with moss","littered with bones","cracked asunder"}, {"","",""}},
    {5, "A %s %s tree launches towards the sky, %s.", {"small", "large", "massive"}, {"oak", "maple", "pine"}, {"casting deep shadows", "covering the ground with leaves", "offering solace from the sun"}},
    {2, "%s footprints run through a patch of %s, evidence of a recent %s.", {"Fox", "Deer", "Boar"}, {"sand", "mud", "dirt"}, {"hunt", "rainfall", "expedition"}},
    {2, "A %s spring %s %s, offering water to thirsty passerbys.", {"small, crystalline", "large", "beautiful"}, {"erupts", "trickles", "flows"}, {"from under a tree", "out of a rock", "into a pool"}},
    {3, "Birds %s in the %s %s.", {"chirp", "sing", "flutter"}, {"nearby", "surrounding", "immense"}, {"trees", "foliage", "grass"}},
    {1, "Bloods %s on a %s, %s.", {"lays splattered", "lays speckled", "flows down"}, {"rock", "tree", "trunk"}, {"indicating a recent battle", "signs of a fight", "the remains of an unfortunate victim"}},
    {3, "A %s, %s %s sits off in the woods.", {"split", "decaying", "rotten"}, {"ancient", "newly-fallen", "bare"}, {"tree", "tree trunk", "branch"}},
    {1, "A%s skull is %s on a %s, warding off visitors.", {"n icerian", " saiya-jin", " human"}, {"impaled", "ruthelessly stuck", "smashed onto"}, {"pike", "spear", "stick"}},
    {1, "A monument of Antor is erected here. He flexes his muscles, showing off his godly powers. Obviously, he is quite strong.", {"","",""},{"","",""},{"","",""}},
    {8, "Sunlight %s the %s, highlighting %s.", {"filters through", "plays amidst", "beams through"}, {"overhead branches", "foliage", "high-reaching trees"}, {"various flowers on the forest flower", "animals playing in the leaves", "cast aside stones and pebbles"}},
    {4, "%s can heard from deep within.", {"Chattering", "Rustling", "Singing"}, {"","",""}, {"","",""}},
    {8, "The %s continues, %s and %s.", {"path", "trail", "walk"}, {"although it is quite muddy", "torn apart by many feet", "marked with flagstones"}, {"covered by many small twigs", "littered with cast-aside garbage", "encroached upon by many persistant weeds"}},
    {0, "", {"","",""}, {"","",""}, {"","",""}} // Dont include in DESCR_COUNT
};

RoomData *room_free = NULL;

RoomData *new_room (void) {
    static RoomData room_zero;
    RoomData *room;

    if (room_free == NULL)
        room = alloc_perm (sizeof (*room));
    else {
        room = room_free;
        room_free = room_free->pNext;
    }

    *room = room_zero;
    VALIDATE (room);
    return room;
}
void free_room (RoomData * room){
    if (!IS_VALID (room))
        return;
    INVALIDATE (room);

    room->pNext = room_free;
    room_free = room;
}

RoomData *AddRoom (int nX, int nY, MapData *pMap) {
    RoomData *pNewRoom;
    int i;

    pNewRoom = new_room();
    pNewRoom->nX = nX;
    pNewRoom->nY = nY;
    pNewRoom->nNumber = pMap->nRoomCount;
    ++pMap->nRoomCount;
    pMap->pRoomGrid[nX][nY] = pNewRoom;
    for (i = 0; i < 6; ++i)
        pNewRoom->pExit[i] = NULL;
    pNewRoom->pRoomIndex = NULL;
    pNewRoom->pNextProcess = NULL;
    pNewRoom->nState = 0;
    pNewRoom->nMobDiff = 0;
    pNewRoom->pNext = pMap->pTopRoom;
    pMap->pTopRoom = pNewRoom;
    return pNewRoom;
}

char *GenRoomDescr (DescrData *pDescr) {
    char szBuf[MAX_STRING_LENGTH];
    int nLastLine = -1, nLineCount = 0;
    //DescrData *pDescr = ForestDescr;
    szBuf[0] = '\0';
    while (nLineCount < 2) {
        int nLine = 0;//, nLineNum = 0, nChance, nCur = 0;
        int nArg;
        char szArg1[40], szArg2[40], szArg3[40];
        // Find the line to use
        /*
        while (TRUE) {
            if (pDescr[nCur].szLine[0] == '\0') // Last line
                break;
            else if ((nChance = number_range(1,1000)) > nLineNum) {
                nLineNum = nChance;
                nLine = nCur;
                nCur++;
            }
        }
        */
        do {
            nLine = number_range(0,DESCR_COUNT-1);
            if (number_range(1,10) > pDescr[nLine].nWeight)
                nLine = nLastLine;
        } while (nLine == nLastLine);
        if (nLastLine == -1)
            nLastLine = nLine;
        ++nLineCount;

        // Find the args
        szArg1[0] = '\0';
        szArg2[0] = '\0';
        szArg3[0] = '\0';
        while (pDescr[nLine].szOption1[0][0] != '\0') { // Make sure there is actually an option
            nArg = number_range(0,2);
            if (pDescr[nLine].szOption1[nArg][0] == '\0')
                continue;
            strcpy (szArg1, pDescr[nLine].szOption1[nArg]);
            break;
        }
        while (pDescr[nLine].szOption2[0][0] != '\0') {
            nArg = number_range(0,2);
            if (pDescr[nLine].szOption2[nArg][0] == '\0')
                continue;
            strcpy (szArg2, pDescr[nLine].szOption2[nArg]);
            break;
        }
        while (pDescr[nLine].szOption3[0][0] != '\0') {
            nArg = number_range(0,2);
            if (pDescr[nLine].szOption3[nArg][0] == '\0')
                continue;
            strcpy (szArg3, pDescr[nLine].szOption3[nArg]);
            break;
        }
        if (szArg1[0] == '\0')
            sprintf (szBuf+strlen(szBuf), pDescr[nLine].szLine);
        else if (szArg2[0] == '\0')
            sprintf (szBuf+strlen(szBuf), pDescr[nLine].szLine, szArg1);
        else if (szArg3[0] == '\0')
            sprintf (szBuf+strlen(szBuf), pDescr[nLine].szLine, szArg1, szArg2);
        else
            sprintf (szBuf+strlen(szBuf), pDescr[nLine].szLine, szArg1, szArg2, szArg3);
    }
    return (format_string(str_dup(szBuf)));
}

int GetRandMobVnum (int nDiffLow, int nDiffHigh) {
    CHAR_DATA *target = NULL;
    CHAR_DATA *pCreated = NULL;
    MOB_INDEX_DATA *pIndex;
    int nVnum, nCount = 0;
    MOB_INDEX_DATA *pList[250];

    // Populate the list
    for (nVnum = 0; nVnum < top_vnum_mob; ++nVnum) {
        if ((pIndex = get_mob_index (nVnum)) == NULL)
            continue;
        if (!pIndex->pCreated) // No mobs of this vnum loaded
            pCreated = create_mobile (pIndex);
        target = pIndex->pCreated;
        if (//IS_NPC(target)AL(target) || (ch->alignment > 0 ? IS_EVIL(target) : IS_GOOD(target)))
            target->nDifficulty >= nDiffLow
            && target->nDifficulty <= nDiffHigh
            && !IS_SET(target->act, ACT_IS_HEALER)
            && !IS_SET(target->act, ACT_IS_CHANGER)
            && !IS_SET(target->imm_flags, IMM_SUMMON)
            && !IS_SET(target->affected_by, AFF_CHARM)
            && target->pIndexData->mprogs == NULL) {
            if (nCount >= 250)
                pList[number_range(0,249)] = pIndex;
            else
                pList[nCount++] = pIndex;
        }
        if (pCreated) {
            extract_char (pCreated, TRUE);
            pCreated = NULL;
        }
    }
    if (nCount == 0)
        return (0);
    pIndex = pList[number_range(0, nCount - 1)]; // Random index
    return (pIndex->vnum);
}

void CreateMap (int nMapVnum, MapData *pMap) {
    int nXLower, nXUpper, nYLower, nYUpper, x, y;
    char buf[MAX_STRING_LENGTH];
    RoomData *pCur;
    OBJ_INDEX_DATA *pMapObj;
    bool bBreak;

    pMapObj = get_obj_index(nMapVnum);
    // Trim it down
    bBreak = FALSE;
    for (nYLower = 0; nYLower < GRIDSIZE; ++nYLower) {
        for (x = 0; x < GRIDSIZE; ++x) {
            if (pMap->pRoomGrid[x][nYLower] != NULL) {
                bBreak = TRUE;
                break;
            }
        }
        if (bBreak)
            break;
    }
    bBreak = FALSE;
    for (nYUpper = GRIDSIZE-1; nYUpper >= 0; --nYUpper) {
        for (x = 0; x < GRIDSIZE; ++x) {
            if (pMap->pRoomGrid[x][nYUpper] != NULL) {
                bBreak = TRUE;
                break;
            }
        }
        if (bBreak)
            break;
    }
    bBreak = FALSE;
    for (nXLower = 0; nXLower < GRIDSIZE; ++nXLower) {
        for (y = 0; y < GRIDSIZE; ++y) {
            if (pMap->pRoomGrid[nXLower][y] != NULL) {
                bBreak = TRUE;
                break;
            }
        }
        if (bBreak)
            break;
    }
    bBreak = FALSE;
    for (nXUpper = GRIDSIZE-1; nXUpper >= 0; --nXUpper) {
        for (y = 0; y < GRIDSIZE; ++y) {
            if (pMap->pRoomGrid[nXUpper][y] != NULL) {
                bBreak = TRUE;
                break;
            }
        }
        if (bBreak)
            break;
    }

    strcpy (buf, "{x");
    for (y = nYLower; y <= nYUpper; ++y) {
        for (x = nXLower; x <= nXUpper; ++x) {
            if ((pCur = pMap->pRoomGrid[x][y]) == NULL)
                strcat (buf, "  ");
            else if (pCur->pExit[DIR_NORTH])
                strcat (buf, " |");
            else
                strcat (buf, "  ");

        }
        strcat (buf, "\n\r");
        for (x = nXLower; x <= nXUpper; ++x) {
            if ((pCur = pMap->pRoomGrid[x][y]) == NULL)
                strcat (buf, "  ");
            else if (pCur->pExit[DIR_WEST])
                strcat (buf, (x == GRIDSIZE/2 && y == GRIDSIZE/2) ? "-{RC{x" : "-O");
            else
                strcat (buf, (x == GRIDSIZE/2 && y == GRIDSIZE/2) ? " {RC{x" : " O");

        }
        strcat (buf, "\n\r");
    }
    free_string (pMapObj->description);
    pMapObj->description = str_dup (buf);
}

// Tries to add a room from a specified room out in a direction
RoomData *Dig (RoomData *pRoom, int nDir, int nState, MapData *pMap) {
    RoomData *pDest;
    // Check if we go off the bounds of the grid
    if (pRoom->nX + nExitDirEW[nDir] <  0 ||
        pRoom->nX + nExitDirEW[nDir] > GRIDSIZE - 1)
        return NULL;
    else if (pRoom->nY + nExitDirNS[nDir] < 0 ||
        pRoom->nY + nExitDirNS[nDir] > GRIDSIZE - 1)
        return NULL;
    if ((pDest = pMap->pRoomGrid[pRoom->nX + nExitDirEW[nDir]][pRoom->nY + nExitDirNS[nDir]])) {
        // Connect anyways?
        if (number_chance(50))
            return NULL;
        pRoom->pExit[nDir] = pDest;
        pDest->pExit[nOppDir[nDir]] = pRoom;
    }
    else {
        pDest = AddRoom (pRoom->nX + nExitDirEW[nDir], pRoom->nY + nExitDirNS[nDir], pMap);
        pDest->nState = nState;
        pDest->nMobDiff = pRoom->nMobDiff + (number_range(1,5) == 1 ? 2 : 0);
        // Link exits
        pRoom->pExit[nDir] = pDest;
        pDest->pExit[nOppDir[nDir]] = pRoom;
        // Add to the list of rooms to process
        if (pMap->pProcessLast != NULL)
            pMap->pProcessLast->pNextProcess = pDest;
        else
            pMap->pProcess = pDest;
        pMap->pProcessLast = pDest;
    }
    return pDest;
}

#define CHANCE_FUZZ (map.nRoomCount < 25 ? 3 : 1)
//((nMaxRooms - 2*map.nRoomCount) / 10)

ShapeData dungeon = {15, 15, 35,   75,  0};
ShapeData forest =  {1,   1,  0,  100, 10};

AREA_DATA *GenerateRandArea (int nVnumLow, int nDiffLow, int nDiffHigh) {
    MapData map;
    //RoomData *pRoomGrid[GRIDSIZE][GRIDSIZE];
    //RoomData *pTopRoom = NULL;
    //int nRoomCount = 0;
    AREA_DATA *pArea;
    RoomData *pCur, *pNext;//, *pProcess, *pProcessLast;
    int nMaxRooms = number_range(1000, 1999), nExit, x, y;
    ShapeData *pExitChance = &forest;

    map.nRoomCount = 0;
    map.pTopRoom = NULL;
    // Clear out the grid
    for (y = 0; y < GRIDSIZE; ++y)
        for (x = 0; x < GRIDSIZE; ++x)
            map.pRoomGrid[x][y] = NULL;
    // Start with a single room
    map.pProcess = AddRoom(GRIDSIZE/2,GRIDSIZE/2, &map);
    do
        map.pProcess->nState = number_range(FIRST_STATE, LAST_STATE);
    while (map.pProcess->nState == ROOM);
    map.pProcess->nMobDiff = 10;
    map.pProcessLast = map.pProcess;
    // Now go through the process list
    while ((pCur = map.pProcess) && map.nRoomCount < nMaxRooms) {
        map.pProcess = map.pProcess->pNextProcess;
        if (map.pProcess == NULL)
            map.pProcessLast = NULL;

        // have arrays with different sets of values for each chance,
        // depending on land type
        if (pCur->nState == HALL_N || pCur->nState == HALL_NS) {
            if (number_chance(pExitChance->nFourway*CHANCE_FUZZ))
                Dig (pCur, DIR_NORTH, HALL_IN, &map);
            else if (number_chance(pExitChance->nIntersection*CHANCE_FUZZ))
                Dig (pCur, DIR_NORTH, HALL_EW, &map);
            else if (number_chance(pExitChance->nRoom) && map.nRoomCount >= 25)
                Dig (pCur, DIR_NORTH, ROOM, &map);
            else if (number_chance(pExitChance->nWinding*CHANCE_FUZZ))
                Dig (pCur, number_chance(50) ? DIR_WEST : DIR_EAST, HALL_N, &map);
            else if (number_chance(pExitChance->nContinue*CHANCE_FUZZ))
                Dig (pCur, DIR_NORTH, HALL_NS, &map);
        }
        if (pCur->nState == HALL_E || pCur->nState == HALL_EW) {
            if (number_chance(pExitChance->nFourway*CHANCE_FUZZ))
                Dig (pCur, DIR_EAST, HALL_IN, &map);
            else if (number_chance(pExitChance->nIntersection*CHANCE_FUZZ))
                Dig (pCur, DIR_EAST, HALL_NS, &map);
            else if (number_chance(pExitChance->nRoom) && map.nRoomCount >= 25)
                Dig (pCur, DIR_EAST, ROOM, &map);
            else if (number_chance(pExitChance->nWinding*CHANCE_FUZZ))
                Dig (pCur, number_chance(50) ? DIR_NORTH : DIR_SOUTH, HALL_E, &map);
            else if (number_chance(pExitChance->nContinue*CHANCE_FUZZ))
                Dig (pCur, DIR_EAST, HALL_EW, &map);
        }
        if (pCur->nState == HALL_S || pCur->nState == HALL_NS) {
            if (number_chance(pExitChance->nFourway*CHANCE_FUZZ))
                Dig (pCur, DIR_SOUTH, HALL_IN, &map);
            else if (number_chance(pExitChance->nIntersection*CHANCE_FUZZ))
                Dig (pCur, DIR_SOUTH, HALL_EW, &map);
            else if (number_chance(pExitChance->nRoom) && map.nRoomCount >= 25)
                Dig (pCur, DIR_SOUTH, ROOM, &map);
            else if (number_chance(pExitChance->nWinding*CHANCE_FUZZ))
                Dig (pCur, number_chance(50) ? DIR_WEST : DIR_EAST, HALL_S, &map);
            else if (number_chance(pExitChance->nContinue*CHANCE_FUZZ))
                Dig (pCur, DIR_SOUTH, HALL_NS, &map);
        }
        if (pCur->nState == HALL_W || pCur->nState == HALL_EW) {
            if (number_chance(pExitChance->nFourway*CHANCE_FUZZ))
                Dig (pCur, DIR_WEST, HALL_IN, &map);
            else if (number_chance(pExitChance->nIntersection*CHANCE_FUZZ))
                Dig (pCur, DIR_WEST, HALL_NS, &map);
            else if (number_chance(pExitChance->nRoom) && map.nRoomCount >= 25)
                Dig (pCur, DIR_WEST, ROOM, &map);
            else if (number_chance(pExitChance->nWinding*CHANCE_FUZZ))
                Dig (pCur, number_chance(50) ? DIR_NORTH : DIR_SOUTH, HALL_W, &map);
            else if (number_chance(pExitChance->nContinue*CHANCE_FUZZ))
                Dig (pCur, DIR_WEST, HALL_EW, &map);
        }
        if (pCur->nState == HALL_IN) {
            Dig (pCur, DIR_NORTH, HALL_NS, &map);
            Dig (pCur, DIR_SOUTH, HALL_NS, &map);
            Dig (pCur, DIR_EAST, HALL_EW, &map);
            Dig (pCur, DIR_WEST, HALL_EW, &map);
        }
           /*
            case ROOM:
                if (number_chance(20*CHANCE_FUZZ))
                    Dig (pCur, DIR_NORTH, number_chance(25) ? ROOM : HALL_NS, &map);
                if (number_chance(20*CHANCE_FUZZ))
                    Dig (pCur, DIR_SOUTH, number_chance(25) ? ROOM : HALL_NS, &map);
                if (number_chance(20*CHANCE_FUZZ))
                    Dig (pCur, DIR_EAST, number_chance(25) ? ROOM : HALL_EW, &map);
                if (number_chance(20*CHANCE_FUZZ))
                    Dig (pCur, DIR_WEST, number_chance(25) ? ROOM : HALL_EW, &map);
                break;*/
    }

    // Now convert this prototype to an actual in-game area
    {
        ROOM_INDEX_DATA *pRoom;
        ROOM_INDEX_DATA *toRoom;
        EXIT_DATA *pExit;
        RESET_DATA *pReset;
        int iHash, i, nMobCount;
        //int nMob1 = 0, nMob2 = 0, nMob3 = 0;
        int nMobVnumList[15][3];

        for (i = 0; i < 15; ++i) {
            nMobVnumList[i][1] = GetRandMobVnum(i * 150, (i+1) * 150);
            nMobVnumList[i][2] = GetRandMobVnum(i * 150, (i+1) * 150);
            nMobVnumList[i][3] = GetRandMobVnum(i * 150, (i+1) * 150);
        }

        // Pick three different mobs to populate the area with
        //nMob1 = GetRandMobVnum(nDiffLow,nDiffHigh);
        //nMob2 = GetRandMobVnum(nDiffLow,nDiffHigh);
        //nMob3 = GetRandMobVnum(nDiffLow,nDiffHigh);

        pArea = new_area ();
        area_last->next = pArea;
        area_last = pArea;
        pArea->name = str_dup ("Random Realm");
        pArea->credits = str_dup ("The cOmPuTeR");
        pArea->file_name = str_dup ("rand0000.are");
        pArea->area_flags = AREA_DONTSAVE;
        pArea->min_vnum = nVnumLow;
        pArea->max_vnum = nVnumLow+map.nRoomCount;
        // Now create rooms
        for (pCur = map.pTopRoom; pCur; pCur = pCur->pNext) {
            pRoom = new_room_index ();
            pRoom->area = pArea;
            pRoom->vnum = nVnumLow + pCur->nNumber;
            pRoom->description = GenRoomDescr(ForestDescr);
            pCur->pRoomIndex = pRoom; // For ease of linkage

            if (pRoom->vnum > top_vnum_room)
                top_vnum_room = pRoom->vnum;

            iHash = pRoom->vnum % MAX_KEY_HASH;
            pRoom->next = room_index_hash[iHash];
            room_index_hash[iHash] = pRoom;

            // Add a mob reset
            if (nMobVnumList[UMIN(15,pCur->nMobDiff/150)][1] != 0) {
                nMobCount = number_range(1, 25);
                if (nMobCount == 25)
                    nMobCount = 5;
                else if (nMobCount >= 23)
                    nMobCount = 4;
                else if (nMobCount >= 21)
                    nMobCount = 3;
                else if (nMobCount >= 16)
                    nMobCount = 2;
                else
                    nMobCount = 1;
                for (i = 0; i < nMobCount; ++i) {
                    pReset = new_reset_data ();
                    pReset->command = 'M';
                    pReset->arg1 = number_range(1,10);
                    if (pReset->arg1 == 10 && nMobVnumList[UMIN(15,pCur->nMobDiff/150)][3] != 0)
                        pReset->arg1 = nMobVnumList[UMIN(15,pCur->nMobDiff/150)][3];
                    else if (pReset->arg1 >= 7 && nMobVnumList[UMIN(15,pCur->nMobDiff/150)][2] != 0)
                        pReset->arg1 = nMobVnumList[UMIN(15,pCur->nMobDiff/150)][2];
                    else
                        pReset->arg1 = nMobVnumList[UMIN(15,pCur->nMobDiff/150)][1];
                    pReset->arg2 = 100;
                    pReset->arg3 = pRoom->vnum;
                    pReset->arg4 = 3; // number in room
                    add_reset (pRoom, pReset, 0);
                }
            }
        }
        // Link the rooms
        for (pCur = map.pTopRoom; pCur; pCur = pCur->pNext) {
            pRoom = pCur->pRoomIndex;
            for (nExit = 0; nExit < 6; ++nExit) {
                // No exit
                if (pCur->pExit[nExit] == NULL)
                    continue;
                if ((toRoom = pCur->pExit[nExit]->pRoomIndex) == NULL)
                    continue;

                // Other side's exit already exists
                if (toRoom->exit[nOppDir[nExit]])
                    continue;

                if (!pRoom->exit[nExit])
                    pRoom->exit[nExit] = new_exit ();

                pRoom->exit[nExit]->u1.to_room = toRoom;
                pRoom->exit[nExit]->orig_door = nExit;

                nExit = nOppDir[nExit];
                pExit = new_exit ();
                pExit->u1.to_room = pRoom;
                pExit->orig_door = nExit;
                toRoom->exit[nExit] = pExit;
            }
        }
    }

    // Create some standard items
    {
        OBJ_INDEX_DATA *pObj;
        RESET_DATA *pReset;
        int iHash;

        // The Map
        pObj = new_obj_index ();
        iHash = nVnumLow % MAX_KEY_HASH;
        pObj->next = obj_index_hash[iHash];
        obj_index_hash[iHash] = pObj;
        pObj->vnum = nVnumLow;
        pObj->area = pArea;
        pObj->reset_num = 0;
        pObj->name = str_dup("map");
        pObj->short_descr = str_dup("A Map");
        pObj->description = str_dup("This is the map");
        pObj->material = str_dup("paper");
		pObj->durability = 1;
        pObj->item_type = ITEM_TRASH;
        pObj->extra_flags = 0;
        pObj->wear_flags = ITEM_TAKE|ITEM_HOLD;
        pObj->value[0] = 0;
        pObj->value[1] = 0;
        pObj->value[2] = 0;
        pObj->value[3] = 0;
        pObj->value[4] = 0;
        pObj->llPl = 1;
        pObj->weight = 1;
        pObj->cost = 1;
        CreateMap (nVnumLow, &map);

        // The Portal Out
        pObj = new_obj_index ();
        iHash = (nVnumLow+1) % MAX_KEY_HASH;
        pObj->next = obj_index_hash[iHash];
        obj_index_hash[iHash] = pObj;
        pObj->vnum = nVnumLow+1;
        pObj->area = pArea;
        pObj->reset_num = 0;
        pObj->name = str_dup("rift");
        pObj->short_descr = str_dup("The Rift");
        pObj->description = str_dup("The Rift");
        pObj->material = str_dup("unknown");
		pObj->durability = 1;
        pObj->item_type = ITEM_PORTAL;
        pObj->extra_flags = ITEM_NOPURGE;
        pObj->wear_flags = 0;
        pObj->value[0] = 0;
        pObj->value[1] = 0;
        pObj->value[2] = 0;
        pObj->value[3] = 201;
        pObj->value[4] = 0;
        pObj->llPl = 1;
        pObj->weight = 1;
        pObj->cost = 1;
        // Add to the starting room
        pReset = new_reset_data ();
        pReset->command = 'O';
        pReset->arg1 = nVnumLow+1;
        pReset->arg2 = 0;
        pReset->arg3 = nVnumLow;
        pReset->arg4 = 0;
        add_reset (get_room_index(nVnumLow), pReset, 0);

        if (nVnumLow+1 > top_vnum_obj)
            top_vnum_obj = nVnumLow+1;
        newobjs += 2;
    }

    // Delete the prototype rooms
    for (pCur = map.pTopRoom; pCur; pCur = pNext) {
        pNext = pCur->pNext;
        free_room (pCur);
    }
    map.pTopRoom = NULL;
    for (y = 0; y < GRIDSIZE; ++y)
        for (x = 0; x < GRIDSIZE; ++x)
            map.pRoomGrid[x][y] = NULL;
    map.nRoomCount = 0;
    return (pArea);
}

void GenerateVoid () {
    int i, nLastVnum = VNUM_RANDOM_LOW-1;
    AREA_DATA *pArea, *pLastArea = NULL;
    ROOM_INDEX_DATA *pRoom1, *pRoom2;
    for (i = 0; i < 1; ++i) {
        pArea = GenerateRandArea (nLastVnum+1, i * 150, (i+1) * 150);

        // Link the two areas
        if (pLastArea != NULL) {
            // Pick one in the last half of the vnums
            pRoom1 = get_room_index(number_range((pArea->max_vnum - pArea->min_vnum) / 2 + pArea->min_vnum, pArea->max_vnum));
            pRoom2 = get_room_index(number_range((pLastArea->max_vnum - pLastArea->min_vnum) / 2 + pLastArea->min_vnum, pLastArea->max_vnum));

            // Exit up
            pRoom1->exit[4] = new_exit ();
            pRoom1->exit[4]->u1.to_room = pRoom2;
            pRoom1->exit[4]->orig_door = 4;

            // Exit down
            pRoom2->exit[5] = new_exit ();
            pRoom2->exit[5]->u1.to_room = pRoom1;
            pRoom2->exit[5]->orig_door = 5;
        }
        reset_area (pArea);
        nLastVnum = pArea->max_vnum;
        pLastArea = pArea;
    }
}




// Base weapon types
BaseWeaponData BaseWpnTable[BASEITEM_COUNT] = {
// {"name list",        "name",       "on ground name",              "material", weight, cost, class,          low_dam, high_dam, dam_type
 {"dagger",             "dagger",     "A dagger sits here.",           "steel",    20,      1,   WEAPON_DAGGER,  1,       6,        "pierce"},
 {"shortsword sword",   "shortsword", "A shortsword sits here.",       "steel",    50,      4,   WEAPON_SWORD,   1,       8,        "stab"},
 {"longsword sword",    "longsword",  "A longsword sits here.",        "steel",    125,     10,  WEAPON_SWORD,   1,       12,       "slash"},
 {"katana",             "katana",     "A katana collects dust.",       "steel",    150,     85,  WEAPON_SWORD,   2,       14,       "cleave"},
 {"club",               "club",       "A primitive club lies around.", "wood",     30,      1,   WEAPON_MACE,    1,       8,        "crush"},
 {"mace",               "mace",       "A mace is here.",               "iron",     100,     10,  WEAPON_MACE,    1,       12,       "smash"},
 {"flail",              "flail",      "A flail lies in the dirt.",     "steel",    150,     15,  WEAPON_FLAIL,   3,       12,       "twack"},
 {"spear",              "spear",      "A spear sits here.",            "steel",    150,     4,   WEAPON_SPEAR,   1,       10,       "pierce"},
 {"staff",              "staff",      "A staff lies around.",          "wood",     100,     1,   WEAPON_SPEAR,   1,       8,        "pound"},
 {"handaxe axe",        "handaxe",    "A handaxe lies around",         "iron",     50,      3,   WEAPON_AXE,     1,       8,        "chop"},
 {"battleaxe axe",      "battleaxe",  "A battleaxe lies in the dirt.", "steel",    125,     15,  WEAPON_AXE,     1,       13,       "chop"},
 {"irgaak",             "irgaak",     "An irgaak sits here.",          "steel",    300,     100, WEAPON_POLEARM, 2,       20,       "slice"}
};

// Weapon prefixes.
// Sort by level, then rarity -- this is mandatory
WpnEffectData WpnPreTable[WPNPRE_COUNT] = {
// {"name",     "world list",  level, rarity, {{location, add/mult, nMod1, nMod2} ...} }
 {"",           "",              0,   NORMAL, {{LOC_NONE,   ADD,  0, 0, 0, 0},              {LOC_NONE,   NONE,  0, 0, 0, 0},             {LOC_NONE,   NONE, 0, 0, 0, 0},          {LOC_NONE,  NONE, 0, 0, 0, 0}}},
 {"sharp",      "sharp",         1,   NORMAL, {{LOC_DAM,    ADD,  1, 0, 0, 0},              {LOC_NONE,   NONE,  0, 0, 0, 0},             {LOC_NONE,   NONE, 0, 0, 0, 0},          {LOC_NONE,  NONE, 0, 0, 0, 0}}},
 {"expensive",  "expensive",     1,   NORMAL, {{LOC_COST,   MULT, 5, 0, 0, 0},              {LOC_NONE,   NONE,  0, 0, 0, 0},             {LOC_NONE,   NONE, 0, 0, 0, 0},          {LOC_NONE,  NONE, 0, 0, 0, 0}}},
 {"stone",      "stone",         1,   NORMAL, {{LOC_OBJAFF, NONE, APPLY_HITROLL, 1, 0, 0},  {LOC_OBJAFF, NONE, APPLY_DAMROLL, 1, 0, 0},  {LOC_NONE,   NONE, 0, 0, 0, 0},          {LOC_NONE,  NONE, 0, 0, 0, 0}}},
 {"massive",    "massive",       1,   RARE,   {{LOC_DAM,    ADD,  2, 0, 0, 0},              {LOC_WEIGHT, MULT,  3, 0, 0, 0},             {LOC_NONE,   NONE, 0, 0, 0, 0},          {LOC_NONE,  NONE, 0, 0, 0, 0}}},
 {"keen",       "keen",          5,   NORMAL, {{LOC_DAM,    ADD,  2, 0, 0, 0},              {LOC_NONE,   NONE,  0, 0, 0, 0},             {LOC_NONE,   NONE, 0, 0, 0, 0},          {LOC_NONE,  NONE, 0, 0, 0, 0}}},
 {"bronze",     "bronze",       11,   NORMAL, {{LOC_OBJAFF, NONE, APPLY_HITROLL, 2, 0, 0},  {LOC_OBJAFF, NONE, APPLY_DAMROLL, 2, 0, 0},  {LOC_NONE,   NONE, 0, 0, 0, 0},          {LOC_NONE,  NONE, 0, 0, 0, 0}}},
 {"blessed",    "blessed",      10,   RARE,   {{LOC_EXTRA,  NONE, ITEM_BLESS, 0, 0, 0},     {LOC_NONE,   NONE,  0, 0, 0, 0},             {LOC_NONE,   NONE, 0, 0, 0, 0},          {LOC_NONE,  NONE, 0, 0, 0, 0}}},
 {"iron",       "iron",         21,   NORMAL, {{LOC_OBJAFF, NONE, APPLY_HITROLL, 3, 0, 0},  {LOC_OBJAFF, NONE, APPLY_DAMROLL, 3, 0, 0},  {LOC_NONE,   NONE, 0, 0, 0, 0},          {LOC_NONE,  NONE, 0, 0, 0, 0}}},
 {"steel",      "steel",        31,   NORMAL, {{LOC_OBJAFF, NONE, APPLY_HITROLL, 4, 0, 0},  {LOC_OBJAFF, NONE, APPLY_DAMROLL, 4, 0, 0},  {LOC_NONE,   NONE, 0, 0, 0, 0},          {LOC_NONE,  NONE, 0, 0, 0, 0}}},
 {"swift",      "swift",        35,   RARE,   {{LOC_AFFECT, NONE, 0, 0, -1, AFF_HASTE},     {LOC_NONE,   NONE,  0, 0, 0, 0},             {LOC_NONE,   NONE, 0, 0, 0, 0},          {LOC_NONE,  NONE, 0, 0, 0, 0}}},
 {"silver",     "silver",       41,   NORMAL, {{LOC_OBJAFF, NONE, APPLY_HITROLL, 5, 0, 0},  {LOC_OBJAFF, NONE, APPLY_DAMROLL, 5, 0, 0},  {LOC_NONE,   NONE, 0, 0, 0, 0},          {LOC_NONE,  NONE, 0, 0, 0, 0}}},
 {"gold",       "gold",         51,   NORMAL, {{LOC_OBJAFF, NONE, APPLY_HITROLL, 6, 0, 0},  {LOC_OBJAFF, NONE, APPLY_DAMROLL, 6, 0, 0},  {LOC_NONE,   NONE, 0, 0, 0, 0},          {LOC_NONE,  NONE, 0, 0, 0, 0}}},
 {"seeking",    "seeking",      50,   NORMAL, {{LOC_OBJAFF, NONE, APPLY_HITROLL, 10, 0, 0}, {LOC_NONE,   NONE,  0, 0, 0, 0},             {LOC_NONE,   NONE, 0, 0, 0, 0},          {LOC_NONE,  NONE, 0, 0, 0, 0}}},
 {"adamantium", "adamantium",   61,   NORMAL, {{LOC_OBJAFF, NONE, APPLY_HITROLL, 7, 0, 0},  {LOC_OBJAFF, NONE, APPLY_DAMROLL, 7, 0, 0},  {LOC_NONE,   NONE, 0, 0, 0, 0},          {LOC_NONE,  NONE, 0, 0, 0, 0}}},
 {"Heightened", "heightened",  100,   UNIQUE, {{LOC_OBJAFF, NONE, APPLY_HITROLL, 25, 0, 0}, {LOC_OBJAFF, NONE, APPLY_DAMROLL, 25, 0, 0}, {LOC_AFFECT, NONE, 0, 0, -1, AFF_HASTE}, {LOC_EXTRA, NONE, ITEM_BLESS|ITEM_HUM, 0, 0, 0}, }}
};
// Weapon suffixes.
// Also sort by level then rarity (has to be this way)
WpnEffectData WpnSufTable[WPNSUF_COUNT] = {
 {"",                 "",              0, NORMAL, {{LOC_NONE,   NONE, 0, 0, 0, 0},         {LOC_NONE,   NONE,   0, 0, 0, 0}, {LOC_NONE, NONE, 0, 0, 0, 0}, {LOC_NONE, NONE, 0, 0, 0, 0}}},
 {"of sharpness",     "sharpness",     1, NORMAL, {{LOC_DAM,    ADD,  1, 0, 0, 0},         {LOC_NONE,   NONE,   0, 0, 0, 0}, {LOC_NONE, NONE, 0, 0, 0, 0}, {LOC_NONE, NONE, 0, 0, 0, 0}}},
 {"of expensiveness", "expensiveness", 1, NORMAL, {{LOC_COST,   MULT, 5, 0, 0, 0},         {LOC_NONE,   NONE,   0, 0, 0, 0}, {LOC_NONE, NONE, 0, 0, 0, 0}, {LOC_NONE, NONE, 0, 0, 0, 0}}},
 {"of massiveness",   "massiveness",   1, RARE,   {{LOC_DAM,    ADD,  2, 0, 0, 0},         {LOC_WEIGHT, MULT,   3, 0, 0, 0}, {LOC_NONE, NONE, 0, 0, 0, 0}, {LOC_NONE, NONE, 0, 0, 0, 0}}},
 {"of humming",       "humming",       3, RARE,   {{LOC_EXTRA,  NONE, ITEM_HUM, 0, 0, 0},  {LOC_COST,   ADD,  150, 0, 0, 0}, {LOC_NONE, NONE, 0, 0, 0, 0}, {LOC_NONE, NONE, 0, 0, 0, 0}}},
 {"of glowing",       "glowing",       3, RARE,   {{LOC_EXTRA,  NONE, ITEM_GLOW, 0, 0, 0}, {LOC_COST,   ADD,  150, 0, 0, 0}, {LOC_NONE, NONE, 0, 0, 0, 0}, {LOC_NONE, NONE, 0, 0, 0, 0}}},
 {"of energy",        "energy",       10, NORMAL, {{LOC_OBJAFF, NONE, APPLY_KI, 50, 0, 0}, {LOC_NONE,   NONE,   0, 0, 0, 0}, {LOC_NONE, NONE, 0, 0, 0, 0}, {LOC_NONE, NONE, 0, 0, 0, 0}}}
};

void AddWpnSpec (OBJ_DATA *pObj, WpnEffectData *pEffect, WpnSpecData *pSpec) {
    AFFECT_DATA *pAf;
    switch (pSpec->nLoc) {
        case LOC_EXTRA:
            pObj->extra_flags |= pSpec->nMod1;
            break;
        case LOC_DAM:
            if (pSpec->nOperation == ADD) {
                pObj->value[1] += pSpec->nMod1;
                pObj->value[2] += pSpec->nMod1;
            }
            else {
                pObj->value[1] *= pSpec->nMod1;
                pObj->value[2] *= pSpec->nMod1;
            }
            break;
        case LOC_COST:
            if (pSpec->nOperation == ADD)
                pObj->cost += pSpec->nMod1;
            else
                pObj->cost *= pSpec->nMod1;
            break;
        case LOC_WEIGHT:
            if (pSpec->nOperation == ADD)
                pObj->weight += pSpec->nMod1;
            else
                pObj->weight *= pSpec->nMod1;
            break;
        case LOC_DURAB:
            if (pSpec->nOperation == ADD)
                pObj->durability += pSpec->nMod1;
            else
                pObj->durability *= pSpec->nMod1;
            break;
        case LOC_OBJAFF:
            pAf = new_affect();
            pAf->location = pSpec->nMod1;
            pAf->modifier = pSpec->nMod2;
            pAf->where = TO_OBJECT;
            pAf->type = pSpec->nMod3;
            pAf->duration = -1;
            pAf->bitvector = pSpec->nMod4;
            pAf->skill_lvl = pEffect->nLevel;
            pAf->next = pObj->affected;
            pObj->affected = pAf;
            break;
        case LOC_WPNAFF:
            pAf = new_affect();
            pAf->location = pSpec->nMod1;
            pAf->modifier = pSpec->nMod2;
            pAf->where = TO_WEAPON;
            pAf->type = pSpec->nMod3;
            pAf->duration = -1;
            pAf->bitvector = pSpec->nMod4;
            pAf->skill_lvl = pEffect->nLevel;
            pAf->next = pObj->affected;
            pObj->affected = pAf;
            break;
        case LOC_AFFECT:
            pAf = new_affect();
            pAf->location = pSpec->nMod1;
            pAf->modifier = pSpec->nMod2;
            pAf->where = TO_AFFECTS;
            pAf->type = pSpec->nMod3;
            pAf->duration = -1;
            pAf->bitvector = pSpec->nMod4;
            pAf->skill_lvl = pEffect->nLevel;
            pAf->next = pObj->affected;
            pObj->affected = pAf;
            break;
    }
}


OBJ_DATA *CreateRandWeapon (int nLevel) {
    OBJ_DATA *pObj;
    int nWpn;
    int i, nPrefix;
    int nSuffix;
    int nPreCount = 0, nSufCount = 0;
    char szBuf[MAX_STRING_LENGTH], cLetter;
    bool bPrefix, bSuffix, bVowel;

    nWpn = number_range(1, BASEITEM_COUNT) - 1;

    if (get_obj_index(OBJ_VNUM_RANDOM) == NULL) {
        logstr (LOG_BUG, "CreateRandWeapon: OBJ_VNUM_RANDOM not found");
        return NULL;
    }
    pObj = create_object(get_obj_index(OBJ_VNUM_RANDOM), 0);

    for (i = 0; i < WPNPRE_COUNT; ++i)
        if (nLevel - WpnPreTable[i].nLevel <= 10 &&
            nLevel - WpnPreTable[i].nLevel >= -10)
            ++nPreCount;
    if (nPreCount == 0)
        nPrefix = 0;
    else {
        while (TRUE) {
            nPrefix = number_range(0, WPNPRE_COUNT-1);
            if (nLevel - WpnPreTable[nPrefix].nLevel <= 10 &&
                nLevel - WpnPreTable[nPrefix].nLevel >= -10) {
                if (WpnPreTable[nPrefix].nRarity == NORMAL && number_range (1, 4) < 4)
                    break;
                else if (WpnPreTable[nPrefix].nRarity == RARE && number_range (1, 5) == 1)
                    break;
                else if (WpnPreTable[nPrefix].nRarity == UNIQUE && number_range (1, 20) == 1)
                    break;
            }
        }
    }
    for (i = 0; i < WPNSUF_COUNT; ++i)
        if (nLevel - WpnSufTable[i].nLevel <= 10 &&
            nLevel - WpnSufTable[i].nLevel >= -10)
            ++nSufCount;
    if (nSufCount == 0)
        nSuffix = 0;
    else {
        while (TRUE) {
            nSuffix = number_range(0, WPNSUF_COUNT-1);
            if (nLevel - WpnSufTable[nSuffix].nLevel <= 10 &&
                nLevel - WpnSufTable[nSuffix].nLevel >= -10) {
                if (WpnSufTable[nSuffix].nRarity == NORMAL && number_range (1, 4) < 4)
                    break;
                else if (WpnSufTable[nSuffix].nRarity == RARE && number_range (1, 5) == 1)
                    break;
                else if (WpnSufTable[nSuffix].nRarity == UNIQUE && number_range (1, 20) == 1)
                    break;
            }
        }
    }

    // Create the right name
    cLetter = WpnPreTable[nPrefix].szName[0];
    if (cLetter == '\0') {
        bPrefix = FALSE;
        cLetter = BaseWpnTable[nWpn].szBaseName[0];
    }
    else
        bPrefix = TRUE;
    if (WpnSufTable[nSuffix].szName[0] == '\0')
        bSuffix = FALSE;
    else
        bSuffix = TRUE;
    if (cLetter == 'a' || cLetter == 'e' || cLetter == 'i' || cLetter == 'o' || cLetter == 'u')
        bVowel = TRUE;
    else
        bVowel = FALSE;
    sprintf (szBuf, "a%s %s%s%s%s%s",
             bVowel ? "n" : "", WpnPreTable[nPrefix].szName, bPrefix ? " " : "", BaseWpnTable[nWpn].szBaseName,
             bSuffix ? " " : "", WpnSufTable[nSuffix].szName);
    pObj->short_descr = str_dup(szBuf);
    
    strcpy (szBuf, BaseWpnTable[nWpn].szName);
    if (WpnPreTable[nPrefix].szWordList[0] != '\0') {
        strcat (szBuf, " ");
        strcat (szBuf, WpnPreTable[nPrefix].szWordList);
    }
    if (WpnSufTable[nSuffix].szWordList[0] != '\0') {
        strcat (szBuf, " ");
        strcat (szBuf, WpnSufTable[nSuffix].szWordList);
    }
    pObj->name = str_dup(szBuf);

    pObj->item_type = ITEM_WEAPON;
    pObj->wear_flags = ITEM_TAKE|ITEM_WIELD;
    pObj->extra_flags = ITEM_RANDOM;
    pObj->description = str_dup(BaseWpnTable[nWpn].szLong);
    pObj->material = str_dup(BaseWpnTable[nWpn].szMaterial);
    pObj->weight = BaseWpnTable[nWpn].nWeight;
    pObj->cost = BaseWpnTable[nWpn].nCost;
    pObj->value[0]= BaseWpnTable[nWpn].nClass;
    pObj->value[1] = BaseWpnTable[nWpn].nLowDam;
    pObj->value[2] = BaseWpnTable[nWpn].nHighDam;
    pObj->value[3] = attack_lookup(BaseWpnTable[nWpn].szDamType);
    pObj->value[4] = 0;

    for (i = 0; i < 4; ++i) {
        AddWpnSpec (pObj, &WpnPreTable[nPrefix], &WpnPreTable[nPrefix].spec[i]);
        AddWpnSpec (pObj, &WpnSufTable[nSuffix], &WpnSufTable[nSuffix].spec[i]);
    }

    return pObj;
}