/*************************************************************************** * 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++; } }