mud_dist/area/
/***************************************************************************
 *  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.                              *
 *                                                                         *
 *  Envy Diku Mud improvements copyright (C) 1994 by Michael Quan, David   *
 *  Love, Guilherme 'Willie' Arnold, and Mitchell Tse.                     *
 *                                                                         *
 *  In order to use any part of this Envy Diku Msud, you must comply with  *
 *  the original Diku license in 'license.doc', the Merc license in        *
 *  'license.txt', as well as the Envy license in 'license.nvy'.           *
 *  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.                                                  *
 ***************************************************************************/

/* Religious Quest Generation
	Idea: Tyrion
	Code: Ahsile
*/


/*$Id: rel_quest.c,v 1.18 2005/02/22 23:55:18 ahsile Exp $*/

#if defined( macintosh )
#include <types.h>
#else
#include <sys/types.h>
#endif
#include <stdio.h>
#include <time.h>
#include <string.h>
#include "merc.h"

/* 
	DO NOT CHANGE!
   	DO NOT CHANGE!!
   	DO NOT CHANGE!!!
   	DO NOT CHANGE!!!!
		- Ahsile
*/

#define  ROOM_MAX		25
#define  GRID_MAX             	20
#define  LEVEL_MAX            	12

#define  ROOM_START           	10

/*
	Below this point is customizable.
		- Ahsile
*/

/* Tell the mud where good/evil/neutral areas start */
#define  REL_GOOD_LOWER       	24601
#define  REL_EVIL_LOWER       	24701
#define  REL_NEUT_LOWER       	24901

/*  Object vnums. Good/Evil starts.
   (No neutral. You either start good or evil)
    Defaults are Seperated by (LEVEL_MAX-1) vnums
*/
#define  REL_PORTAL_GOOD      	24612
#define  REL_PORTAL_EVIL      	24601
#define  REL_PORTAL_EXIT      	24623

#define  REL_ITEM_QUEST		24651
#define  REL_ITEM_COUNT		1

/* 
    Religious mob equipment starts.
    Will load randomly    
*/
#define  REL_EQUIP_GOOD		24701
#define	 REL_EQUIP_EVIL		24766
#define  REL_EQUIP_NEUT		24833

/* Mob vnums */
#define  REL_MOB_EVIL_GRUNT    	24601
#define  REL_MOB_EVIL_BOSS	24650
#define  REL_MOB_NEUT_GRUNT	24701
#define	 REL_MOB_NEUT_BOSS	24750
#define  REL_MOB_GOOD_GRUNT	24801
#define  REL_MOB_GOOD_BOSS	24650

/* 
   Room stuff 
   Names, and Descriptions 
*/
#define  ROOM_HELL_0 		"This is Hell"
#define  ROOM_HELL_1    	"The Pits of Hell"
#define  ROOM_HELL_2    	"Burning Flames"
#define  ROOM_HELL_3    	"The Fall of Light"
#define  ROOM_HELL_D 		"Your are deep in hell.\n\r"

#define  ROOM_ERTH_0    	"On the Plane of Mortals"
#define  ROOM_ERTH_1    	"Walking the Earth"
#define  ROOM_ERTH_2    	"Upon the Soil"
#define  ROOM_ERTH_3    	"The Land of Men and Beasts"
#define  ROOM_ERTH_D    	"You walk the earth.\n\r"

#define  ROOM_HEAV_0 		"The Lair of the Gods"
#define  ROOM_HEAV_1		"A Kingdom of Clouds"
#define  ROOM_HEAV_2		"Holy Power"
#define  ROOM_HEAV_3		"An Air of Sweetness"
#define  ROOM_HEAV_D		"You walk the heavenly plane.\n\r"

// Room data struct
struct RoomData
{
    int index;
    ROOM_INDEX_DATA* Room;
};

// external variable for religion.c to use
bool relquest;

// external function for religion.c to use
bool rel_quest_gen  (CHAR_DATA* ch, int align, int levels);
void rel_quest_goals(CHAR_DATA* ch, int levels, int align);

// local functions
void draw_map(CHAR_DATA* ch, int level, struct RoomData[LEVEL_MAX][GRID_MAX][GRID_MAX]);
void make_and_link(struct RoomData* r, int Level, int Align, int index, int cur, int vnum, int dir1, int dir2);
void make_portal(ROOM_INDEX_DATA* r, int Level, int Align);
void generate_mobs(struct RoomData* r, int Level, int Align);
void equip_mob( CHAR_DATA* ch, int Level, int Align );


bool rel_quest_gen(CHAR_DATA* ch, int align, int levels)
{
    int  Current_Room;
    int  Chance;
    int  index;
    int  Align;
    int  TotalLevels;
    int  GridX;
    int  GridY;
    int  Level;
    int  vnum;
    char buf[MAX_STRING_LENGTH];

    /* 
	Make two arrays. 
	One is indexed by the room number
	The other is a grid for verifying links to other rooms;
    */ 
    struct RoomData Rooms[LEVEL_MAX][ROOM_MAX];
    struct RoomData Grid [LEVEL_MAX][GRID_MAX][GRID_MAX];

    /* Init variables */
    TotalLevels = 0;
    Align = -1;
    index = 0;
    Chance = -1;
    Current_Room = -1;

    /* seed random timer */
    srand( (unsigned)time( NULL ) );

    /* level paramater correct? */
    if (levels < 1 || levels > 4)
	return FALSE;

    /* calc total levels */
    TotalLevels = levels * 3;
    
    /* use our own align variable... why, I dunno */
    Align = align;

    /* align parameter not neutral or empty? */
    if (!Align)
	return FALSE;

    /* choose a starting point based on align */
    if (Align == ALIGN_EVIL)
	vnum = REL_VNUM_LOWER;
    else if (Align == ALIGN_GOOD)
	vnum = REL_VNUM_UPPER;
    else
        return FALSE;
   
    /* Clear Room Array */
    for (Level = 0; Level < TotalLevels;Level++)
    {
        for (Chance = 0; Chance < ROOM_MAX; Chance++)
 	{
                Rooms[Level][Chance].Room = NULL;
                Rooms[Level][Chance].index = -1;
	}
    }

    /* Unlink OLD dungeon */
    for (Level = REL_VNUM_LOWER; Level <= REL_VNUM_UPPER; Level++)
    {
	ROOM_INDEX_DATA* clearRoom = get_room_index(Level);
	if (clearRoom)
	{
	    CHAR_DATA* mob;
	    CHAR_DATA* people[MAX_GROUP];
	    OBJ_DATA*  obj;
	    int chCount = 0;
	    for (index = DIR_NORTH; index < DIR_UP; index++)
	    {
                if (clearRoom->exit[index])
                {
                     free_exit(clearRoom->exit[index]);
		     clearRoom->exit[index]=NULL;		  
	        }
	    }
	    // Kill objects and mobs to avoid duplication.
	    // Sanity check for PC's... no camping the relquest areas
	    for(obj = clearRoom->contents; obj; obj=obj->next_content) 
		{
			if (!obj->deleted)				 
				extract_obj ( obj ); 
		};
	    for(mob=clearRoom->people; mob; mob=mob->next_in_room  ) 
		{
			if (IS_NPC(mob))
			{ 
				if (!mob->deleted)
					extract_char( mob, TRUE );
			}
			else
			{
				people[chCount] = mob;
				chCount++;	
			} 
		};
	    for(Chance=0; Chance<chCount; Chance++)
	    {
		char_from_room( people[Chance] );
		char_to_room( people[Chance], get_room_index( ROOM_VNUM_ALTAR ) );
		send_to_char( AT_WHITE, "The gods remove you from their domain!\n\r", people[Chance]);
	   }
	}
    } 

    /* Init index */
    index = 0;
    
    /* Loop through our levels */
    for (Level = 0; Level < TotalLevels; Level++)
    {
		int loops;
        /* Clear Grid */
        for (Chance=0; Chance < GRID_MAX;Chance++) 
	{
            for (Current_Room = 0; Current_Room < GRID_MAX; Current_Room++)
	    {
		Grid[Level][Chance][Current_Room].Room = NULL;
                Grid[Level][Chance][Current_Room].index = -1;
            }
	}
        
        Grid[Level][ROOM_START][ROOM_START].index = index;
        Grid[Level][ROOM_START][ROOM_START].Room = get_room_index( vnum );
        Rooms[Level][0] = Grid[Level][ROOM_START][ROOM_START];
	
	GridX = ROOM_START;
        GridY = ROOM_START;
        
        if ((index % 25)==0) 
	{
        	/* Prime the vnum. We used one to start */
        	if (Align == ALIGN_GOOD) { vnum--; }
		else { vnum++; }
		index += 1; 
		strcpy(buf, "The Beginning of Your Quest\n\r");
		Rooms[Level][0].Room->name  	  = str_dup(buf); 
        	Rooms[Level][0].Room->description = str_dup("Welcome to the start your journey.\n\r"); 
	} /* First room is already taken care of */
        

	/* Ok, loop until all of our rooms are completed */
		loops = 0;
        do
        {
            int FindX;
            int FindY;
            int RoomIndex;
            int tempX;
            int tempY;
            float temp;

			loops++;
	    temp = ((float)rand()/(float)RAND_MAX);
            Current_Room = (int)(temp*(index - (Level * ROOM_MAX))); /* Pick a Room from 0-X */
                                                              /* Where X = Rooms on this Level */

            /* Find our position on the "grid" */
            if( index > 1)
	    {
                for (FindY = 0; FindY<GRID_MAX;FindY++)
		{
                    for (FindX = 0;FindX<GRID_MAX;FindX++)
		    {
                        if (Grid[Level][FindY][FindX].index == Rooms[Level][Current_Room].index)
			{
                            GridX = FindX;
                            GridY = FindY;
			}
		    }
		}
	    }
            
            /* No exits available, don't bother going through the rest */
            if (
		    Rooms[Level][Current_Room].Room->exit[DIR_NORTH] &&
		    Rooms[Level][Current_Room].Room->exit[DIR_EAST]  &&
		    Rooms[Level][Current_Room].Room->exit[DIR_SOUTH] &&
		    Rooms[Level][Current_Room].Room->exit[DIR_WEST] )
	    {
		continue;
	    }

            /* Translate Index into 0-24, and pick a random chance of exits
               Then, check if the exit is available to be linked */

            RoomIndex = (index - (Level * ROOM_MAX));
            Chance = number_range(0,100);
            
            if (Chance < 25)    /* North */
                {
		    tempY = GridY - 1;
                    tempX = GridX;
                    if (Rooms[Level][Current_Room].Room->exit[DIR_NORTH] ||
		 	tempY < 0 || Grid[Level][tempY][tempX].index != -1)
		    {
			continue; 
		    }

                    make_and_link(Rooms[Level], Level, Align, RoomIndex, Current_Room, vnum, DIR_SOUTH, DIR_NORTH);

		}
	     else if (Chance < 50)    /* East */
                {
		    tempY = GridY;
                    tempX = GridX + 1;
                    if (Rooms[Level][Current_Room].Room->exit[DIR_EAST] ||
			tempX > 19 || Grid[Level][tempY][tempX].index != -1) 
		    {
			continue;
		    }

                    make_and_link(Rooms[Level], Level, Align, RoomIndex, Current_Room, vnum, DIR_WEST, DIR_EAST);

		}
	    else if (Chance < 75)   /* South */
                {
		    tempY = GridY + 1;
                    tempX = GridX;
			
                    if (Rooms[Level][Current_Room].Room->exit[DIR_SOUTH] ||
			tempY > 19 || Grid[Level][tempY][tempX].index != -1)
		    {
			continue;
		    }

		    make_and_link(Rooms[Level], Level, Align, RoomIndex, Current_Room, vnum, DIR_NORTH, DIR_SOUTH);

		}
           else       /* West */
		{
                    tempY = GridY;
                    tempX = GridX - 1;

                    if (Rooms[Level][Current_Room].Room->exit[DIR_WEST] ||
			tempX < 0 || Grid[Level][tempY][tempX].index != -1)
		    {
			continue;
		    } 

		    make_and_link(Rooms[Level], Level, Align, RoomIndex, Current_Room, vnum, DIR_EAST, DIR_WEST);

		}

            /* Ok, everything went good. Finish off the room creation
               And put it on the grid */
            Rooms[Level][RoomIndex].index = index;
            Grid[Level][tempY][tempX] = Rooms[Level][RoomIndex];
            Grid[Level][GridY][GridX] = Rooms[Level][Current_Room];
	
	    if (Align == ALIGN_GOOD)
	    {
		vnum--;
	    } else
	    {
		vnum++;
	    }
            
            index++;

	} while ((index - (Level * ROOM_MAX)) < ROOM_MAX);
    
	// Make a portal to the next level
	if (Level < (TotalLevels - 1))
		make_portal(Rooms[Level][number_range(1, (ROOM_MAX-1))].Room, Level, Align);	

	// Create Mobs and equip them
	generate_mobs(Rooms[Level], Level, Align);
    }

    // Now make the exit portal
    Rooms[TotalLevels-1][number_range(1, (ROOM_MAX-1))].Room->contents = create_object( get_obj_index( REL_PORTAL_EXIT), TotalLevels-1);
    
    for (Level=0;Level<TotalLevels;Level++)
    {
	draw_map(ch,Level,Grid);
    }
    
    /* Announce the quest */
    sprintf(buf, "The ground shakes as the gods send %s's party upon a quest!\n\r",ch->name);
    talk_channel_info( ch, buf, CHANNEL_INFO, "info" );

    return TRUE;
}

void make_portal(ROOM_INDEX_DATA* r, int Level, int Align)
{
	// Load a portal to the next level
	OBJ_DATA* o;

	if (Align==ALIGN_GOOD)
		o = create_object( get_obj_index( REL_PORTAL_GOOD + Level ), Level);
	else
		o = create_object( get_obj_index( REL_PORTAL_EVIL + Level ), Level);

	obj_to_room( o, r);
}

void make_and_link(struct RoomData* r, int Level, int Align, int index, int cur, int vnum, int dir1, int dir2)
{
	char* name = NULL;
	char* desc = NULL;
	int   rdm  = number_range(0, 3);

	/* Make New Room */
	r[index].Room    			= get_room_index ( vnum );
        r[index].Room->exit[dir1]  	      	= new_exit();
	r[index].Room->room_flags		= ROOM_NO_RECALL | ROOM_NO_ASTRAL_IN | ROOM_NO_ASTRAL_OUT;
	r[cur].Room->exit[dir2] 	  	= new_exit();
	/* Link Rooms */
	r[index].Room->exit[dir1]->to_room     	= r[cur].Room;
        r[index].Room->exit[dir1]->vnum        	= r[cur].Room->vnum;
        r[cur].Room->exit[dir2]->to_room  	= r[index].Room;
	r[cur].Room->exit[dir2]->vnum		= vnum;

	/* Pick random names and descriptions */
	if (Level < 4)
	{
		if (Align==ALIGN_GOOD)
		{
			switch(rdm)
			{
				case 0:
					name = str_dup(ROOM_HEAV_0);
					break;
				case 1:
					name = str_dup(ROOM_HEAV_1);
					break;
				case 2:
					name = str_dup(ROOM_HEAV_2);
					break;
				case 3:
					name = str_dup(ROOM_HEAV_3);
					break;
			}
			desc = str_dup(ROOM_HEAV_D);			
		} else
		{
			switch(rdm)
			{
				case 0:
					name = str_dup(ROOM_HELL_0);
					break;
				case 1:
					name = str_dup(ROOM_HELL_1);
					break;
				case 2:
					name = str_dup(ROOM_HELL_2);
					break;
				case 3:
					name = str_dup(ROOM_HELL_3);
					break;
			}
			desc = str_dup(ROOM_HELL_D);			
		}
	}
	else if (Level < 8)
	{
		switch(rdm)
		{
			case 0:
				name = str_dup(ROOM_ERTH_0);
				break;
			case 1:
				name = str_dup(ROOM_ERTH_1);
				break;
			case 2:
				name = str_dup(ROOM_ERTH_2);
				break;
			case 3:
				name = str_dup(ROOM_ERTH_3);
				break;
		}
		desc = str_dup(ROOM_ERTH_D);			
	}
	else
	{
		if (Align==ALIGN_EVIL)
		{
			switch(rdm)
			{
				case 0:
					name = str_dup(ROOM_HEAV_0);
					break;
				case 1:
					name = str_dup(ROOM_HEAV_1);
					break;
				case 2:
					name = str_dup(ROOM_HEAV_2);
					break;
				case 3:
					name = str_dup(ROOM_HEAV_3);
					break;
			}
			desc = str_dup(ROOM_HEAV_D);			
		} else
		{
			switch(rdm)
			{
				case 0:
					name = str_dup(ROOM_HELL_0);
					break;
				case 1:
					name = str_dup(ROOM_HELL_1);
					break;
				case 2:
					name = str_dup(ROOM_HELL_2);
					break;
				case 3:
					name = str_dup(ROOM_HELL_3);
					break;
			}
			desc = str_dup(ROOM_HELL_D);			
		}
	}
	r[index].Room->name = name;
	r[index].Room->description = desc;
}

void generate_mobs(struct RoomData* r, int Level, int Align)
{
	CHAR_DATA* mob;
	int i;
	int min_grunt, max_grunt, min_boss;
	/* ****
	   not needed right now
	   ****
	   int m_lvl, m_hp, m_align;
	*/

	// Make a random mob
	for(i = 0; i < ROOM_MAX - 1; i++)
	{

		if (Level < 4)
		{
			if (Align == ALIGN_EVIL)
			{
				min_grunt = REL_MOB_GOOD_GRUNT;
				max_grunt = REL_MOB_GOOD_BOSS - 1;
				min_boss  = REL_MOB_GOOD_BOSS; 
			} else
			{
				min_grunt = REL_MOB_EVIL_GRUNT;
				max_grunt = REL_MOB_EVIL_BOSS - 1;
				min_boss  = REL_MOB_EVIL_BOSS; 
			}
		}
		else if (Level < 8)
		{
			min_grunt = REL_MOB_NEUT_GRUNT;
			max_grunt = REL_MOB_NEUT_BOSS - 1;
			min_boss  = REL_MOB_NEUT_BOSS; 

		}
		else
		{
			if (Align == ALIGN_EVIL)
			{
				min_grunt = REL_MOB_EVIL_GRUNT;
				max_grunt = REL_MOB_EVIL_BOSS - 1;
				min_boss  = REL_MOB_EVIL_BOSS; 
			} else
			{
				min_grunt = REL_MOB_GOOD_GRUNT;
				max_grunt = REL_MOB_GOOD_BOSS - 1;
				min_boss  = REL_MOB_GOOD_BOSS; 
			}
		}

		if (number_percent() < 75)
		{ 
			mob = create_mobile( get_mob_index( number_range( min_grunt, max_grunt ) ) );
			equip_mob(mob, Level, Align);
			char_to_room( mob, r[i].Room);
		}
	}

	mob = create_mobile( get_mob_index( min_boss + Level ) );
	SET_BIT(mob->act, ACT_RELBOSS);
	SET_BIT(mob->affected_by4, AFF_IMMORTAL);
	mob->long_descr = str_dup("The boss of this level.\n\r");
	equip_mob(mob, Level, Align);
	char_to_room( mob, r[ROOM_MAX-1].Room);

	return;
}

void equip_mob( CHAR_DATA* mob, int Level, int Align )
{
	int i = 0;
	int MAX_EQP = 27;
	OBJ_DATA* obj;
	// Randomly equip mob
	for (i = 0; i < MAX_EQP; i++)
	{
		if (number_percent() < 30)
		{
			obj = create_object( get_obj_index( REL_EQUIP_GOOD + i), mob->level);
			if (!str_cmp(obj->name, str_empty))
			{
				obj->name 	= str_dup(wear_flags[i].name);
				obj->short_descr= str_dup(wear_flags[i].name);
			}
			/*
			{
				char buf[MAX_STRING_LENGTH];
				sprintf(buf, "Equipping mob %s with %s", mob->short_descr, obj->short_descr);
				bug(buf, 0);
			}
			*/
			obj_to_char( obj, mob);
			equip_char( mob, obj, i);
		}	
	}
}

void draw_map(CHAR_DATA* ch, int level, struct RoomData Grid[LEVEL_MAX][GRID_MAX][GRID_MAX])
{
   // Draw the map
   int Row = 0;
   int Col = 0;
   int rIndex = 0;
   char line[MAX_STRING_LENGTH];
   char fname[MAX_STRING_LENGTH];
   FILE* fp;

   /* Open a file for the MAP command to read */
   sprintf(fname, "level%d.map", level);
   fp = fopen(fname,"w");
   
   /* 	
	The second reason for the grid!
	Easy drawing! Dump the grid to the file
   */
   for (Row = 0;Row<GRID_MAX; Row++)
   {
	// start blank line
	strcpy(line,"");
	for (Col = 0;Col<GRID_MAX;Col++)
	{
	    // is there a room here?
	    if (Grid[level][Row][Col].index != -1)
	    {
		char buf[MAX_STRING_LENGTH];
		// calculate the index (0 to ROOM_MAX-1)
		rIndex = (Grid[level][Row][Col].index - (level * ROOM_MAX));
		sprintf(buf, "Level: %d Row: %d Col: %d Orig Idx: %d New Idx: %d", level, Row, Col, Grid[level][Row][Col].index, rIndex);
	  	log_string(buf, CHANNEL_NONE, -1);

		// starting point
		if (rIndex == 0)
			strcat(line, "O");
		// a portal (the only room contents on start)
		// if someone drops something, it gets killed later
		else if (Grid[level][Row][Col].Room->contents)
			strcat(line, "#");
		// Regular room
		else
			strcat(line, "X");
	    } else
	    {
		// blank
		strcat(line, " ");
	    }
	}
	// is there a room on this line? if not, don't bother spitting
	// it out. 
	if (strstr(line,"X") || strstr(line,"#") || strstr(line,"O"))
	{
	   strcat(line,"\n");
	   fprintf(fp,"%s",line);
	}
    }
    fclose(fp);
}

void rel_quest_goals(CHAR_DATA* ch, int levels, int align)
{
	int i, start, end;
	OBJ_DATA* obj;
	ROOM_INDEX_DATA* room;
	if (align==ALIGN_GOOD)
	{
		start = (levels * 3) - 1;
		end = -1;
	} else
	{
		start = 0;
		end = levels *3;
	}
	for (i = 0; i < (levels * 3); i++)
	{
		while(1)
		{	
			room = get_room_index( number_range( REL_VNUM_LOWER + (start*ROOM_MAX) + 1, REL_VNUM_LOWER + ((start+1)*ROOM_MAX - 2 ) ) );

			if (!room)
				continue;

			if (number_percent() < 40)
			{
				obj = create_object( get_obj_index( number_range( REL_ITEM_QUEST, REL_ITEM_QUEST + REL_ITEM_COUNT - 1) ), 1 );
				if (!obj)
					continue;

				obj->timer = ch->rcountdown * 2;
				obj_to_room( obj, room);
				ch->rquestobj[i] = obj->pIndexData->vnum;
				break;
			} else
			{
				if (!room->people)
					continue;
			
				ch->rquestmob[i] = room->people->pIndexData->vnum;
				break;
			}
		};
		if (start > end)
		    start--;
		else
		    start++;

	}
}