/
roa/
roa/lib/boards/
roa/lib/config/
roa/lib/edits/
roa/lib/help/
roa/lib/misc/
roa/lib/plrobjs/
roa/lib/quests/
roa/lib/socials/
roa/lib/www/
roa/lib/www/LEDSign/
roa/lib/www/LEDSign/fonts/
roa/lib/www/LEDSign/scripts/
roa/src/s_inc/
roa/src/sclient/
roa/src/sclient/binary/
roa/src/sclient/text/
roa/src/util/
/************************************************************************
	Realms of Aurealis 		James Rhone aka Vall of RoA

roomact.c				Procs related to rooms...

		******** Heavily modified and expanded ********
		******** 100% Completely Original Code ********
		*** BE AWARE OF ALL RIGHTS AND RESERVATIONS ***
		******** Heavily modified and expanded ********
		******** 100% Completely Original Code ********
		        All rights reserved henceforth. 

    Please note that no guarantees are associated with any code from
Realms of Aurealis.  All code which has been released to the general
public has been done so with an 'as is' pretense.  RoA is based on both
Diku and CircleMUD and ALL licenses from both *MUST* be adhered to as well
as the RoA license.   *** Read, Learn, Understand, Improve ***
*************************************************************************/
#include "conf.h"
#include "sysdep.h"

#include "structures.h"
#include "utils.h"
#include "db.h"
#include "comm.h"
#include "interpreter.h"
#include "acmd.h"
#include "handler.h"
#include "magic.h"
#include "bard.h"
#include "house.h"
#include "fight.h"
#include "affect.h"
#include "lists.h"
#include "objsave.h"
#include "darkenelf.h"

/* ext vars */
extern int	rev_dir[];
extern char	*rev_dir_str[];
extern char	*dirs[];

/* find these in nature.c */
extern void do_room_wind(int i);
extern void do_room_drift(int i);
extern void send_soundfile_to_client(dsdata *d, char *sname);
extern void do_room_rproc(rmdata *r);

// internally used
int check_room_affects(chdata *ch, int room);
void room_affect_update(int room);
void null_char_room_affects(chdata *ch);

// for each client connection... check room sound status for resend
void check_room_music(void)
{
  dsdata *d;

  for (d = descriptor_list; d;d=d->next)
  if (D_CHECK(d) && HAS_CLIENT(d) && d->room_stimer > -1)
  {
    d->room_stimer--;
    if (d->room_stimer == 0)
    {
      int toroom;
      int vroom;
      toroom = d->character->in_room;

      if (INVALID_ROOM(toroom))
        continue;

      // use vnums for consistency...
      vroom = world[toroom].number;

      // if sound file is gone or not in the same room... reset
      if (!world[toroom].sound_file || vroom != d->music_room)
      {
        d->room_stimer = -1;
        d->music_room  = -1;
        continue;
      }

      // else, send it and set the #s
      send_soundfile_to_client(d, world[toroom].sound_file);
      d->room_stimer = world[toroom].music_timer;
      d->music_room  = vroom;
    }
  }
}


/* formerly hard wired */
void do_room_dump(chdata *ch, obdata *obj)
{
   obdata *o, *tmp_o;
   int	value = 0;
   extern int gain_exp(chdata *ch, int value);

   if (ch->in_room <= NOWHERE)
   {
     log("SYSERR: nowhere sent to dump");
     return;
   }

   for (o = world[ch->in_room].contents; o; o = tmp_o) 
   {
      tmp_o = o->next_content;
      extract_obj(o);
   }

   o = obj;
   value = MAX(1, MIN(50, o->cost / 10));
   extract_obj(o);

   if (value) 
   {
      act("You have been rewarded.", FALSE, ch, 0, 0, TO_CHAR);
      if (GET_LEVEL(ch) < 3)
	 gain_exp(ch, value);
      else
	 GET_GOLD(ch) += value;
   }
   return;
}

SPECIAL(pet_shops)
{
   char	buf[MAX_STRING_LENGTH], pet_name[256];
   int	pet_room;
   chdata *pet;

   pet_room = ch->in_room + 1;

   if (cmd == 63) { /* List */
      send_to_char("Available pets are:\n\r", ch);
      for (pet = world[pet_room].people; pet; pet = pet->next_in_room) {
	 sprintf(buf, "%8d - %s\n\r", 3 * GET_EXP(pet), pet->player.short_descr);
	 send_to_char(buf, ch);
      }
      return(TRUE);
   } else if (cmd == 60) { /* Buy */

      arg = one_argument(arg, buf);
      arg = one_argument(arg, pet_name);
      /* Pet_Name is for later use when I feel like it */

      if (!(pet = get_char_room(buf, pet_room))) {
	 send_to_char("There is no such pet!\n\r", ch);
	 return(TRUE);
      }

      if (GET_GOLD(ch) < (GET_EXP(pet) * 3)) {
	 send_to_char("You don't have enough money!\n\r", ch);
	 return(TRUE);
      }

      GET_GOLD(ch) -= GET_EXP(pet) * 3;

      pet = read_mobile(pet->nr, REAL);
      GET_EXP(pet) = 0;
      SET_BIT(pet->specials.affected_by, AFF_CHARM);

      if (*pet_name) {
	 sprintf(buf, "%s %s", pet->player.name, pet_name);
	 pet->player.name = str_dup(buf);

	 sprintf(buf, "%sA small sign on a chain around the neck says 'My Name is %s'\n\r",
	     pet->player.description, pet_name);
	 pet->player.description = str_dup(buf);
      }

      char_to_room(pet, ch->in_room);
      add_follower(pet, ch);

      /* Be certain that pet's can't get/carry/use/weild/wear items */
      IS_CARRYING_W(pet) = 1000;
      IS_CARRYING_N(pet) = 100;

      send_to_char("May you enjoy your pet.\n\r", ch);
      act("$n buys $N as a pet.", FALSE, ch, 0, pet, TO_ROOM);

      return(TRUE);
   }

   /* All commands except list and buy */
   return(FALSE);
}

/* display one of the randoms defined by players -roa */
void do_room_random(int room)
{
  int num;

  if (room == NOWHERE) return;
  if (number(0,1))
  {
    num = number(0, 4);  // pick a number 0 - 4 (5 total possibilities)
    if (world[room].randoms[num] && *world[room].randoms[num] != '&')
      send_to_room_not_busy(world[room].randoms[num], room);
  }
}

void do_room_portal(int room)
{
  chdata *ch;
  chdata *next_ch;
  BOOL found = FALSE;
  byte num;
  int i, single = 0;

  for (i = 0, num = 0; i<4; i++)
    if (RPORTAL(room, i) > 0)
    {
	found = TRUE;
	num++;
	single = i;
    }

  if (!found)
     return;  /* nowhere to portal to */

  if (num == 1) /* tele to one room only */
  {
     for (ch = world[room].people; ch; ch=next_ch)
     {
       next_ch = ch->next_in_room;
       if (IS_PC(ch) && (real_room(RPORTAL(room, single)) > 0))
       {
	char_from_room(ch);
	char_to_room(ch, real_room(RPORTAL(room, single)));
	act("$n appears amidst a brilliant flash of light.", FALSE, ch, 0, 0, TO_ROOM);
	do_look_at_room(ch, 0, 0);
	if (check_room_affects(ch, ch->in_room) == CHAR_DIED)
	  return;
	if (check_death_trap(ch, NULL))
	  return;
       }
     }
  }
  else  /* its a random teleport */
  {
     for (ch = world[room].people; ch; ch=next_ch)
     {
	next_ch = ch->next_in_room;
	single = number(0,3);
        if (IS_PC(ch) && (real_room(RPORTAL(room, single)) > 0))
        {
	 char_from_room(ch);
	 char_to_room(ch, real_room(RPORTAL(room, single)));
	 act("$n appears amidst a brilliant flash of light!", FALSE, ch, 0, 0, TO_ROOM);
	 do_look_at_room(ch, 0, 0);
 	 if (check_room_affects(ch, ch->in_room) == CHAR_DIED)
	   return;
	 if (check_death_trap(ch, NULL))
	   return;
        }
     }
  }
}

#define IS_DT(rm) (ROOM_FLAGGED((rm), DEATH) || ROOM_FLAGGED((rm),FLY_DEATH))

void do_room_alter(int room)
{
   chdata *ch, *next_ch;
   BOOL hit = FALSE, mana = FALSE, move = FALSE;
   BOOL decrease = world[room].alter_type;
   int amount;

   if (IS_DT(room))
     return;

   if (world[room].numdice < 1 || world[room].sizedice < 1)
     return;

   if (ROOM_FLAGGED(room, ALTERHIT)) hit = TRUE;
   if (ROOM_FLAGGED(room, ALTERMANA)) mana = TRUE;
   if (ROOM_FLAGGED(room, ALTERMOVE)) move = TRUE;

   if (!hit && !mana && !move)
     return;

   for(ch = world[room].people; ch; ch=next_ch)
   {
     next_ch = ch->next_in_room;
     if (IS_PC(ch))
     {
	amount = dice(world[room].numdice, world[room].sizedice);
	if (hit && GET_HIT(ch) > 0)
	  if (decrease)
	    GET_HIT(ch) -= amount;
	  else
	    GET_HIT(ch) += amount;

	if (mana)
	  if (decrease)
	    GET_MANA(ch) -= amount;
	  else
	    GET_MANA(ch) += amount;

	if (move)
	  if (decrease)
	    GET_MOVE(ch) -= amount;
	  else
	    GET_MOVE(ch) += amount;

	GET_HIT(ch) = MIN(GET_HIT(ch), GET_MAX_HIT(ch));
	GET_HIT(ch) = MAX(0, GET_HIT(ch));

	GET_MANA(ch) = MIN(GET_MANA(ch), GET_MAX_MANA(ch));
	GET_MANA(ch) = MAX(0, GET_MANA(ch));

	GET_MOVE(ch) = MIN(GET_MOVE(ch), GET_MAX_MOVE(ch));
	GET_MOVE(ch) = MAX(0, GET_MOVE(ch));

	update_pos(ch);

	if (FIGHTING(ch) && GET_HIT(ch) <= 0)
	  stop_fighting(ch);
     }
   }
}

#define TOROOM(x, y) (DIR(x, y)->to_room)

// park a transport room at dock, send messages, check next port
int transport_dock(int room)
{
  int orig_port;

  world[room].wait_time = world[room].dock_wait;

  if (world[room].randoms[4] && *world[room].randoms[4])
   send_to_room_not_busy(world[room].randoms[4], room);
  else
   send_to_room_not_busy("LUCY!!! WE'RE HOME!!!",room);

  /* now find next avail port */
  orig_port = world[room].to_port;  /* save orig port */

  if ((world[room].to_port++) > 3)
    world[room].to_port = 0;

  while (world[room].portals[(int)world[room].to_port] <= 0)
  {
    world[room].to_port++;

    if (world[room].to_port > 3)
      world[room].to_port = 0;

    if (world[room].to_port == orig_port)
    {
      sprintf(buf, "SYSERR: Room #%d, not enough ports (case2 transport)",
 	      world[room].number);
      mudlog(buf, BRF, LEV_IMM, FALSE);
      return FALSE;
    }
  }

  return TRUE;
}

// room moves from src to trg in the dir direction
int transport_travel(int room, int dir, int src, int trg)
{
  chdata *ch;

  /* set new location to vnum of dir_option */
  world[room].location = world[TOROOM(src,dir)].number;

  /* we still travelling */
  /* how long to wait during travel */
  world[room].wait_time = world[room].travel_wait;

  /* now erase our trail */
  world[src].trans_present = -1;

  /* lucy, we home... */
  world[trg].trans_present = world[room].number;

  if (world[room].randoms[1] && *world[room].randoms[1])
   sprintf(buf1, "%s",world[room].randoms[1]);
  else
   sprintf(buf1, "%s",world[room].name);

  sprintf(buf, "%s moves %s.", buf1, dirs[dir]);	
  send_to_room_not_busy(buf, src);

  if (world[room].randoms[3] && *world[room].randoms[3])
   sprintf(buf, "%s %s.", world[room].randoms[3], dirs[dir]);

  send_to_room_not_busy(buf, room);
 
  sprintf(buf, "%s arrives from %s.", buf1, rev_dir_str[dir]);	
  send_to_room_not_busy(buf, trg);

  /* have everybody do_look here perhaps */
  for (ch = world[room].people; ch; ch = ch->next_in_room)
  {
    ch->in_room = trg;
    do_look_at_room(ch,0,-1);
    ch->in_room = room;
  }

  return TRUE;
}

// move trannie from one room to another
int move_transport_room(int room)
{
   int src, trg, curr, dir;
   extern int find_first_step_terrain(int src, int trg);
   int zone = world[room].zone;

   if (ZONE_IDLE(zone) && ZONE_FREED(zone))
    return FALSE;

   src = real_room(world[room].location);
   if (src < 0)
   {
     mudlog("SYSERR:  src room not valid, move_transport_room, graph.c", BRF, LEV_IMM, TRUE);
     return FALSE;
   }

   curr = world[room].to_port;  /* note can only be 0 - 3 */
   if (curr < 0 || curr > 3)
   {
     mudlog("SYSERR:  invalid to_port room, move_transport, graph.c", BRF, LEV_IMM, TRUE);
     return FALSE;
   }

   trg = real_room(RPORTAL(room, curr));
   if (trg < 0)
   {
     mudlog("SYSERR:  trg room not valid, move_transport_room, graph.c",BRF, LEV_IMM, TRUE);
     return FALSE;
   }

   dir = find_first_step_terrain(src, trg);

   switch(dir) 
   {
      case BFS_ERROR:
	 sprintf(buf, "SYSERR: Room #%d, BFS_ERROR (case1 transport)", world[room].number);
	 mudlog(buf, BRF, LEV_IMM, FALSE);
	 return FALSE;
         break;

      case BFS_ALREADY_THERE:
	 if (world[room].wait_time > 0)
	 {
	   world[room].wait_time--;
	   return TRUE;
	 }      
	 return (transport_dock(room));
         break;

      case BFS_NO_PATH:
	 sprintf(buf, "SYSERR: Room #%d, BFS_NO_PATH ", world[room].number);
	 mudlog(buf, BRF, LEV_IMM, FALSE);
	 return FALSE;
         break;

      default:  	// ok, take next step, check if arrived
	 if (world[room].wait_time)
	 {
	   world[room].wait_time--;
	   return TRUE;
	 }      

	 /* check it */
	 if ((trg = real_room(world[TOROOM(src, dir)].number)) < 0)
	 {
           log("SYSERR:  trg room not valid, move_transport_room, graph.c");
	   return FALSE;
	 }

	 // move once
	 transport_travel(room, dir, src, trg);

	 // if we're there, dock it, set up next port
	 if (trg == real_room(RPORTAL(room, (int) world[room].to_port)))
	   return (transport_dock(room));

	 return TRUE;
         break;
   }
}

void close_door(int orig, int i)
{
  int room;

  if (DIR(orig, i) && EXIT_ISDOOR(DIR(orig, i)) && EXIT_OPEN(DIR(orig, i)))
  {
    FLAG_EXIT(DIR(orig, i), EX_CLOSED);
    for (room = 0; room < top_of_world; room++)
      if (DIR(room, rev_dir[i]) && EXIT_ISDOOR(DIR(room, rev_dir[i])) &&
	  EXIT_OPEN(DIR(room, rev_dir[i])) && DIR(room, rev_dir[i])->to_room == orig)
      {
	FLAG_EXIT(DIR(room, rev_dir[i]), EX_CLOSED);
        sprintf(buf, "The direction %s has closed.",dirs[rev_dir[i]]);
        send_to_room_not_busy(buf, room);
      }
  }
}

void lock_door(int orig, int i)
{
  int room;

  if (DIR(orig, i) && EXIT_ISDOOR(DIR(orig, i)) && !EXIT_OPEN(DIR(orig, i)))
  {
    FLAG_EXIT(DIR(orig, i), EX_LOCKED);
    for (room = 0; room < top_of_world; room++)
      if (DIR(room, rev_dir[i]) && EXIT_ISDOOR(DIR(room, rev_dir[i])) &&
	  !EXIT_OPEN(DIR(room, rev_dir[i])) && DIR(room, rev_dir[i])->to_room == orig)
      {
	FLAG_EXIT(DIR(room, rev_dir[i]), EX_LOCKED);
      }
  }
}

void do_exit_autoshut(int room)
{
  int i;

  for (i = 0; i < NUM_OF_DIRS; i++)
    if (DIR(room, i) && EXIT_ISDOOR(DIR(room, i)) && EXIT_OPEN(DIR(room, i)) && 
	EXIT_FLAGGED(DIR(room, i), EX_AUTOSHUT) && !EXIT_JAMMED(DIR(room, i)))  
    {
      close_door(room, i);
      if (EXIT_FLAGGED(DIR(room, i), EX_AUTOLOCK))
        lock_door(room, i);
      sprintf(buf, "The direction %s has closed.",dirs[i]);
      send_to_room_not_busy(buf, room);
    }
}

// portal all ppl to their loadroom (used to be hard wired into 1904 newbie area) 5/23/98 -jtrhone
void do_hometown_portal(int i)
{
  chdata *ch, *next_ch;

  for (ch = world[i].people; ch; ch=next_ch)
  {
    next_ch = ch->next_in_room;
    if (IS_PC(ch) && !IS_IMMORTAL(ch))
    {
      if (real_room(GET_LOADROOM(ch)) >= 0)
      {
	send_to_char("You feel your very soul being shifted to another place!\n\r",ch);
	char_from_room(ch);
	char_to_room(ch, real_room(GET_LOADROOM(ch)));
	do_save(ch, "", 0, 0);
	do_look_at_room(ch, 0, 0);
	check_room_affects(ch, ch->in_room);
      }
    }
  }
}

/* control various room aspects */
// add rprocs  6/5/98 -jtrhone
void	room_activity(BOOL dtime)
{
   int i;
   chdata *ch, *next_ch;
   int tmp;

   /* do the drifting and tumbling stuff RoA*/
   for (i = 0; i < top_of_world; i++)
   {
      if (ZONE_FLAGGED(world[i].zone, Z_IDLE))
	continue;

      // if we at dtime, only update dtime rooms
      if (dtime && !ROOM_FLAGGED2(i, RPROC_DTIME))
        continue;

      // if any room affects, and ppl in it, do an update
      if (world[i].people && world[i].room_affects)
	room_affect_update(i);

      if (world[i].people && ROOM_FLAGGED(i, RANDOM))
        do_room_random(i);  /* for random rooms */

      /* room can't be in an idle zone */
      if (ROOM_FLAGGED2(i, TRANSPORT))
      {
	if (!(tmp = move_transport_room(i)))
	{
	  sprintf(buf, "SYSERR: Transport failure, room #%d.", world[i].number);
	  mudlog(buf, BRF, LEV_IMM, FALSE);
	  continue;  /* next room pleez */
	}
      }
      else  /* we use the same integers here, so must be one or the other */
      if (world[i].people && ROOM_FLAGGED(i, PORTAL))
        do_room_portal(i);  /* for portal rooms */

      if (world[i].people && ROOM_FLAGGED(i, ALTERHIT | ALTERMOVE | ALTERMANA))
        do_room_alter(i);

      if (ROOM_FLAGGED(i, WINDY))
        do_room_wind(i);  /* air stuff */

      if (world[i].terrain_type == TERRAIN_UWATER ||
	  world[i].terrain_type == TERRAIN_WATER_SWIM ||
	  world[i].terrain_type == TERRAIN_WATER_NOSWIM)
        do_room_drift(i);  /* water stuff, uwater and water rooms */

      do_exit_autoshut(i); 

      // port all pcs to their loadrooms... 5/23/98 -jtrhone
      if (ROOM_FLAGGED2(i, HTOWN_PORTAL) && world[i].people)
        do_hometown_portal(i);

      /* do the builder defined rprocs for these rooms */
      if (ROOM_FLAGGED(i, RPROC))
        do_room_rproc(&world[i]);
   }

   /* PORTAL ROOM IN STANNEG RoA */
   if (((i = real_room(3762)) > 0) && world[i].people)
   {
     for (ch = world[i].people; ch; ch=next_ch)
     {
       next_ch = ch->next_in_room;
       if (IS_PC(ch))
       {
          send_to_char("You feel your very soul being shifted to another place!\n\r",ch);
	  act("A %Bblinding light%0 encircles $n and $e disappears.", FALSE, ch, 0, 0, TO_ROOM);
	  switch (GET_CLASS(ch))
	  {
	    case CLASS_WARRIOR:
	        char_from_room(ch);
		char_to_room(ch, real_room(3706));
		break;
	    case CLASS_BARD:
	        char_from_room(ch);
		char_to_room(ch, real_room(3773));
		break;
	    case CLASS_RANGER:
	        char_from_room(ch);
		char_to_room(ch, real_room(3763));
		break;
	    case CLASS_CLERIC:
	        char_from_room(ch);
		char_to_room(ch, real_room(3707));
		break;
	    case CLASS_MAGE:
	        char_from_room(ch);
		char_to_room(ch, real_room(3708));
		break;
	    case CLASS_THIEF:
	        char_from_room(ch);
		char_to_room(ch, real_room(3709));
		break;
	    case CLASS_SHAMAN:
	        char_from_room(ch);
		char_to_room(ch, real_room(3710));
		break;
	    case CLASS_WARLOCK:
	        char_from_room(ch);
		char_to_room(ch, real_room(598));
		break;
	    case CLASS_MONK:
	        char_from_room(ch);
		char_to_room(ch, real_room(599));
		break;
	    default: 
		break;
	  }
	  do_look_at_room(ch, 0, 0);
       }
     }
   }


   // check all the descriptors for room music hooks...
   check_room_music();
}

// if player leaves world for whatever reason, set caster to NULL
// for each affect he/she has
void null_char_room_affects(chdata *ch)
{
  int i;
  struct room_affect_type *raf;

  for (i = 0; i < top_of_world; i++)
    for (raf = world[i].room_affects; raf; raf = raf->next)
      if (raf->caster == ch)
	raf->caster = NULL;
}

/* 
   goes thru room affect list and waxes each member along with ptr
   to it -roa 
*/
void free_room_affects(struct room_affect_type *head)
{
  struct room_affect_type *ths;
  struct room_affect_type *temp;

  ths = head;

  while(ths)
  {
    REMOVE_FROM_LIST(ths, head, next);
    free_log(ths, "free_room_affects");
    ths = head;
  }
}

void room_affect_modify(int room, struct room_affect_type *af, BOOL add)
{
  // add any related affects to the affect type
  if (add)
  {
    switch (af->spell) {
      case SPELL_ROCK_TO_MUD:
	send_to_room_not_busy("The ground suddenly becomes a mess of mud!", room);
	break;
      case SPELL_NATURES_CALTROPS:
	send_to_room_not_busy("From the ground springs wickedly-pointed spikes!", room);
	break;
      case SPELL_FIRESTORM:
	send_to_room_not_busy("A roaring fills your ears, and the area is consumed in a %B%1storm of fire%0!", room);
	break;
      case SPELL_SILENCE:
	send_to_room_not_busy("An aura of %Bholy silence%0 surrounds the area!",room);
	break;
      case SPELL_FIREWALL:
	send_to_room_not_busy("A %B%1wall of deadly fire%0 suddenly erupts!",room);
	break;
      case SPELL_ICEWALL:
	send_to_room_not_busy("A %B%6storm of deadly ice%0 engulfs the area!",room);
	break;
      case SPELL_TYPHOON:
	send_to_room_not_busy("A deadly %1typhoon%0 explodes out of nothingness!",room);
	break;
      case SPELL_VOID:
	send_to_room_not_busy("A %4temporal void%0 suddenly appears!",room);
	break;
      case SPELL_GASCLOUD:
	send_to_room_not_busy("You cough as a %2cloud of gas%0 suddenly appears!",room);
	break;
      case SPELL_BLINDWALL:
	send_to_room_not_busy("An uneasy %4darkness%0 engulfs the area!",room);
	break;
      case SPELL_CONFUSION:
	send_to_room_not_busy("Something in this area makes you feel confused!",room);
	break;
      case SKILL_NOTRACK:
	send_to_room_not_busy("The tracks around this area slowly fade out of view!",room);
	break;
      case SPELL_SHOWEROFLIFE:
	send_to_room_not_busy("A %B%6shower of heavenly rain%0 suddenly begins to fall!",room);
	break;
      case SPELL_CURSED_GROUNDS:
	send_to_room_not_busy("An uncomfortable feeling suddenly falls over the area!",room);
	break;
      case SPELL_ABSOLUTE_WARD:
      case SPELL_CIRCLE_OF_WARDING:
	send_to_room_not_busy("This area seems to be warded!",room);
	break;
      case SPELL_WALLOFFOG:
      case SPELL_OCCLUSION:
	send_to_room_not_busy("%BA wall of fog hangs heavy over the area%0!",room);
	break;
      case SPELL_CAMPFIRE:
	send_to_room_not_busy("A %B%1campfire%0 begins to warm the area!",room);
	break;
      case SPELL_AREA_OF_RETURN:
	break;
      case SPELL_PROJECT:
	break;
      default: 
	break;
    }
  }
  else
  {
    switch (af->spell) {
      case SPELL_ROCK_TO_MUD:
        send_to_room_not_busy("The ground suddenly solidifies.", room);
        break;
      case SPELL_NATURES_CALTROPS:
        send_to_room_not_busy("The spikes slowly disappear beneath the surface of the ground.", room);
        break;
      case SPELL_FIRESTORM:	// 04/23/98 -callahan
        send_to_room_not_busy("The %B%1storm of fire%0 slowly subsides.", room);
        break;
      case SPELL_SILENCE:
	send_to_room_not_busy("An aura of %Bholy silence%0 has been lifted!",room);
	break;
      case SPELL_FIREWALL:
	send_to_room_not_busy("A %B%1wall of deadly fire%0 disappears!",room);
	break;
      case SPELL_ICEWALL:
	send_to_room_not_busy("An %B%6ice storm%0 disappears!",room);
	break;
      case SPELL_TYPHOON:
	send_to_room_not_busy("A deadly %1typhoon%0 wavers in intensity!",room);
	break;
      case SPELL_VOID:
	send_to_room_not_busy("A %4temporal void%0 shimmers and disappears.",room);
	break;
      case SPELL_GASCLOUD:
	send_to_room_not_busy("Suddenly, the %2gas cloud%0 disperses.",room);
	break;
      case SPELL_BLINDWALL:
	send_to_room_not_busy("The strange %4cloak of darkness%0 has been lifted.",room);
	break;
      case SPELL_CONFUSION:
	send_to_room_not_busy("You no longer feel confused.",room);
	break;
      case SKILL_NOTRACK:
	send_to_room_not_busy("The tracks in this area slowly reappear.",room);
	break;
      case SPELL_SHOWEROFLIFE:
	send_to_room_not_busy("A %B%6shower of heavenly rain%0 slowly stops falling.",room);
	break;
      case SPELL_CURSED_GROUNDS:
	send_to_room_not_busy("The uncomfortable feeling in this area dissipates.",room);
	break;
      case SPELL_ABSOLUTE_WARD:
      case SPELL_CIRCLE_OF_WARDING:
	send_to_room_not_busy("The ward in this area dissipates.",room);
	break;
      case SPELL_WALLOFFOG:
      case SPELL_OCCLUSION:
	send_to_room_not_busy("%BThe wall of fog dissipates%0!",room);
	break;
      case SPELL_CAMPFIRE:
	send_to_room_not_busy("The %B%1campfire%0 dissipates!",room);
	break;
      case SPELL_AREA_OF_RETURN:
        if (af->caster)
        {
          sprintf(buf, "The aura of %s fades from view.", GET_NAME(af->caster));
          send_to_room_not_busy(buf, room);

          send_to_char("Your area of return has dissipated.\n\r",af->caster);
        }
	break;
      case SPELL_PROJECT:
        if (af->caster)
        {
          sprintf(buf, "The projection of %s fades from view.", GET_NAME(af->caster));
          send_to_room_not_busy(buf, room);

          send_to_char("A projection of yours has dissipated.\n\r",af->caster);
        }
	break;
      default: 
	break;
    }
  }
}

// add a certain affect to room affects list
void affect_to_room(int room, struct room_affect_type *af)
{
  rmdata *rm = NULL;
  struct room_affect_type *affected_alloc;

  if (INVALID_ROOM(room))
  {
    mudlog("SYSERR: Invalid room sent to affect_to_room.", BRF, LEV_IMM, TRUE);
    return;
  }

  rm = &world[room];

  // create memory for it
  CREATE(affected_alloc, struct room_affect_type, 1);

  // dupe it over
  *affected_alloc = *af;

  // insert into room_affect list
  affected_alloc->next = rm->room_affects;
  rm->room_affects = affected_alloc;

  // do affects to room based on it's addition
  room_affect_modify(room, affected_alloc, TRUE);
}

// yank from room affect list, called when duration reaches 0
// af must NEVER BE NULL -roa
void room_affect_remove(int room, struct room_affect_type *af)
{
  rmdata *rm = NULL;
  struct room_affect_type *temp;

  if (INVALID_ROOM(room))
  {
    mudlog("SYSERR: Invalid room sent to room_affect_remove.", BRF, LEV_IMM, TRUE);
    return;
  }

  rm = &world[room];
  assert(rm->room_affects);

  REMOVE_FROM_LIST(af, rm->room_affects, next);

  // do affects to room based on it's removal (if any)
  room_affect_modify(room, af, FALSE);

  free_log(af, "room_affect_remove");
  af = NULL;
}

/* Call room_affect_remove with every spell of spelltype "skill" */
void	spell_affect_from_room(int spell, int room)
{
   struct room_affect_type *hjp, *nxt;  /* <-- use nxt ptr to one !! */
   rmdata *rm = NULL;

   if (INVALID_ROOM(room))
   {
     mudlog("SYSERR: Invalid room sent to spell_affect_from_room.", BRF, LEV_IMM, TRUE);
     return;
   }

   rm = &world[room];

   for (hjp = rm->room_affects; hjp; hjp = nxt)
   {
     nxt = hjp->next;
     if (hjp->spell == spell)
       room_affect_remove(room, hjp);
   }
}

// check character ptrs on this one for when they die or leave game
void	char_affect_from_room(chdata *ch, int room)
{
   struct room_affect_type *hjp, *nxt;  /* <-- use nxt ptr to one !! */
   rmdata *rm = NULL;

   if (INVALID_ROOM(room))
   {
     mudlog("SYSERR: Invalid room sent to char_affect_from_room.", BRF, LEV_IMM, TRUE);
     return;
   }

   rm = &world[room];

   for (hjp = rm->room_affects; hjp; hjp = nxt)
   {
     nxt = hjp->next;
     if (hjp->caster == ch)
       room_affect_remove(room, hjp);
   }
}

// check character ptrs on this one for when they die or leave game
void	char_spell_affect_from_room(chdata *ch, int spell, int room)
{
   struct room_affect_type *hjp, *nxt;  /* <-- use nxt ptr to one !! */
   rmdata *rm = NULL;

   if (INVALID_ROOM(room))
   {
     mudlog("SYSERR: Invalid room sent to char_spell_affect_from_room.", BRF, LEV_IMM, TRUE);
     return;
   }

   rm = &world[room];

   for (hjp = rm->room_affects; hjp; hjp = nxt)
   {
     nxt = hjp->next;
     if (hjp->caster == ch && hjp->spell == spell)
       room_affect_remove(room, hjp);
   }
}

// check character ptrs on this one for when they die or leave game
BOOL	char_affects_room(chdata *ch, int room)
{
   struct room_affect_type *hjp;
   rmdata *rm = NULL;

   if (INVALID_ROOM(room))
     return FALSE;

   rm = &world[room];

   for (hjp = rm->room_affects; hjp; hjp = hjp->next)
     if (hjp->caster == ch)
	return TRUE;
  return FALSE;
}

// return TRUE if this character casted this spell on this room
struct room_affect_type *char_spell_affects_room(chdata *ch, int spell, int room)
{
   struct room_affect_type *hjp;
   rmdata *rm = NULL;

   if (INVALID_ROOM(room))
     return NULL;

   rm = &world[room];

   for (hjp = rm->room_affects; hjp; hjp = hjp->next)
     if (hjp->caster == ch && hjp->spell == spell)
	return hjp;
  return NULL;
}

// scan list for particular spell in room affects, return it if found
struct room_affect_type *spell_affects_room(int room, int spell)
{
  struct room_affect_type *raf;
  rmdata *rm = NULL;

  if (INVALID_ROOM(room))
    return NULL;

  rm = &world[room];

  if (!rm->room_affects)
    return NULL;

  // scan list and apply any affects
  for (raf = rm->room_affects; raf; raf = raf->next)
    if (raf->spell == spell)
      return raf;

  return NULL;
}

BOOL room_align_probs(chdata *ch, int bitv)
{
  if (IS_SET(bitv, RMAFF_VS_EVIL) && IS_EVIL(ch))
    return TRUE;
  if (IS_SET(bitv, RMAFF_VS_NEUTRAL) && IS_NEUTRAL(ch))
    return TRUE;
  if (IS_SET(bitv, RMAFF_VS_GOOD) && IS_GOOD(ch))
    return TRUE;
  return FALSE;
}

// called when character enters room
int check_room_affects(chdata *ch, int room)
{
  struct room_affect_type *raf;
  struct song_affect *sg;
  struct affected_type af; 
  int dam;
  rmdata *rm = NULL;

  if (INVALID_ROOM(room))
    return CHAR_OK;

  rm = &world[room];
  if (!rm->room_affects)
    return CHAR_OK;

  // scan list and apply any affects
  for (raf = rm->room_affects; raf; raf = raf->next)
  {
    switch (raf->spell) {
    case SPELL_FIRESTORM:	// 04/23/98 -callahan
      if (saves_spell(ch, SPELL_FIRESTORM))
        act("The %B%1storm of fire%0 passes you harmlessly by!", 
            FALSE, ch, 0, 0, TO_CHAR);
      else {
        act("The %B%1storm of fire%0 agonizingly caresses you!",
            FALSE, ch, 0, 0, TO_CHAR);
        act("The %B%1storm of fire%0 caresses $n painfully!",
            FALSE, ch, 0, 0, TO_ROOM);

        if (raf->caster)
          dam = number(1, Level(raf->caster));
        else
          dam = number(1, (LEV_IMPL / 2));

        Hits(ch) -= dam;
      }
      break;

     case SPELL_SILENCE:
	// remove all songs from character entering here
	if (SINGING(ch))
	  do_finish(ch, "song", 0, 0);
	if (PLAYING(ch))
	  do_finish(ch, "tune", 0, 0);
	if ((sg = ch->specials.songs))
	  while (sg)
	  {
	    song_from_char(ch, sg);
	    sg = ch->specials.songs;
	  }
	act("This area is strangely %Bsilent%0!!",FALSE,ch,0,0,TO_CHAR);
	break;

     case SPELL_FIREWALL:
	if (saves_spell(ch, SPELL_FIREWALL))
	  act("You resist the %B%1firewall%0!",FALSE,ch,0,0,TO_CHAR);
	else
	{
	  act("The %B%1firewall%0 sears your skin!",FALSE,ch,0,0,TO_CHAR);
	  act("The %B%1firewall%0 sears $n's skin!",FALSE,ch,0,0,TO_ROOM);
	  if (raf->caster)
	    dam = number(1, GET_LEVEL(raf->caster));
	  else
	    dam = number(1, (LEV_IMPL / 2));
	  GET_HIT(ch) -= dam;
	}
	break;

     case SPELL_ICEWALL:
	if (saves_spell(ch, SPELL_ICEWALL))
	  act("You resist the %B%6ice storm%0!",FALSE,ch,0,0,TO_CHAR);
	else
	{
	  act("The %B%6ice storm%0 pelts your skin!",FALSE,ch,0,0,TO_CHAR);
	  act("The %B%6ice storm%0 pelts $n's skin!",FALSE,ch,0,0,TO_ROOM);
	  if (raf->caster)
	    dam = number(1, GET_LEVEL(raf->caster));
	  else
	    dam = number(1, (LEV_IMPL / 2));
	  GET_HIT(ch) -= dam * 3/2;
	}
	break;

     case SPELL_TYPHOON:
	if (saves_spell(ch, SPELL_TYPHOON))
	  act("You resist the %1typhoon%0!",FALSE,ch,0,0,TO_CHAR);
	else
	{
	  act("A deadly %1typhoon%0 rampages around you!",FALSE,ch,0,0,TO_CHAR);
	  if (raf->caster)
	    dam = number(1, GET_LEVEL(raf->caster));
	  else
	    dam = number(1, (LEV_IMPL / 2));
	  GET_HIT(ch) -= dam * 2;
	}
	break;

     case SPELL_VOID: 
	act("A %4temporal void%0 shimmers slightly around you!",FALSE,ch,0,0,TO_CHAR);
	break;

     case SPELL_GASCLOUD:
	if (saves_spell(ch, SPELL_GASCLOUD) || affected_by_spell(ch, SPELL_GASCLOUD))
	  act("You resist the %2gas cloud%0!",FALSE,ch,0,0,TO_CHAR);
	else
	{
	  act("You suddenly feel very sick!",FALSE,ch,0,0,TO_CHAR);
	  act("$n suddenly looks very sick!",FALSE,ch,0,0,TO_ROOM);
	  if (raf->caster)
	    af.duration = GET_LEVEL(raf->caster) / 5;
	  else
	    af.duration = (LEV_IMPL / 2) / 5;

	  af.type 	= SPELL_GASCLOUD;
	  af.modifier 	= -2;
	  af.location 	= APPLY_STR;
	  af.bitvector 	= AFF_POISON;
	  af.bitvector2 = 0;
	  affect_to_char(ch, &af);

	  af.location 	= APPLY_DEX;
	  affect_to_char(ch, &af);
	}
	break;

     case SPELL_BLINDWALL:
	if (saves_spell(ch, SPELL_BLINDWALL) || affected_by_spell(ch, SPELL_BLINDWALL))
	  act("You resist the %4strange darkness%0!",FALSE,ch,0,0,TO_CHAR);
	else
	{
	  act("Your vision has vanished!",FALSE,ch,0,0,TO_CHAR);
	  act("$n's eyes turn a strange color!",FALSE,ch,0,0,TO_ROOM);
	  if (raf->caster)
	  {
	    af.duration = GET_LEVEL(raf->caster) / 5;
	    act("Visions of $N %B%1rage%0 through your mind!",FALSE,ch,0,raf->caster,TO_CHAR);
	  }
	  else
	    af.duration = (LEV_IMPL / 2) / 5;

	  af.type 	= SPELL_BLINDWALL;
	  af.modifier 	= -4;
	  af.location 	= APPLY_DEX;
	  af.bitvector 	= AFF_BLIND;
	  af.bitvector2 = 0;
	  affect_to_char(ch, &af);
	}
	break;

     case SPELL_CONFUSION: 
	if (IS_IMMORTAL(ch) && PLR_FLAGGED(ch, PLR_LOGALL))
	  act("A confusion spell affects this room.",FALSE,ch,0,0,TO_CHAR);
	break;

     case SKILL_NOTRACK: 
	if (IS_IMMORTAL(ch) && PLR_FLAGGED(ch, PLR_LOGALL))
	  act("A notrack skill affects this room.",FALSE,ch,0,0,TO_CHAR);
	break;

     case SPELL_SHOWEROFLIFE:
	act("The %B%6shower of life%0 soothes your spirit!",FALSE,ch,0,0,TO_CHAR);
	act("The %B%6shower of life%0 soothes $n's spirit!",FALSE,ch,0,0,TO_ROOM);
	if (raf->caster)
	  dam = number(1, GET_LEVEL(raf->caster)/2);
	else
	  dam = number(1, (LEV_IMPL / 4));
	GET_HIT(ch) += dam;
	GET_HIT(ch) = MIN(GET_MAX_HIT(ch), GET_HIT(ch));
	break;

     case SPELL_CURSED_GROUNDS:
	if (saves_spell(ch, SPELL_CURSED_GROUNDS) || affected_by_spell(ch, SPELL_CURSE))
	  act("You resist the curse spread over this area!",FALSE,ch,0,0,TO_CHAR);
	else
	{
	  act("You suddenly feel very uncomfortable!",FALSE,ch,0,0,TO_CHAR);
          act("$n briefly reveals a red aura!", FALSE, ch, 0, 0, TO_ROOM);
	  if (raf->caster)
	    act("Visions of $N %B%1rage%0 through your mind!",FALSE,ch,0,raf->caster,TO_CHAR);

          af.type      = SPELL_CURSE;
          af.duration  = 24 * 7;       /* 7 Days */
          af.modifier  = -1;
          af.location  = APPLY_HITROLL;
          af.bitvector = AFF_CURSE;
          af.bitvector2 = 0;
          affect_to_char(ch, &af);

          af.location = APPLY_SV_MAGIC;
          af.modifier = -30; /* Make worse */
          affect_to_char(ch, &af);
	}
	break;

     case SPELL_ABSOLUTE_WARD: 
        if (raf->caster != ch)
	  act("A %4ward%0 seems to hang over the area.",FALSE,ch,0,0,TO_CHAR);
	break;

     case SPELL_CIRCLE_OF_WARDING: 
        if (raf->caster != ch && room_align_probs(ch, raf->bitvector))
	  act("A %4ward%0 seems to hang over the area!",FALSE,ch,0,0,TO_CHAR);
	break;

     case SPELL_WALLOFFOG: 
     case SPELL_OCCLUSION: // ranger add 3/27/98 -jtrhone
        if (raf->caster != ch)
	  act("A %Bwall of fog%0 seems to hang over the area.",FALSE,ch,0,0,TO_CHAR);
	break;

     // ranger add 3/27/98 -jtrhone
     case SPELL_CAMPFIRE:
	act("The warmth from the %B%1campfire%0 soothes your soul!",FALSE,ch,0,0,TO_CHAR);
	act("The warmth from the %B%1campfire%0 soothes $n's soul!",FALSE,ch,0,0,TO_ROOM);
	if (raf->caster)
	  dam = number(1, GET_LEVEL(raf->caster)/2);
	else
	  dam = number(1, (LEV_IMPL / 4));
	GET_HIT(ch) += dam;
	GET_HIT(ch) = MIN(GET_MAX_HIT(ch), GET_HIT(ch));

	GET_MOVE(ch) += dam;
	GET_MOVE(ch) = MIN(GET_MAX_MOVE(ch), GET_MOVE(ch));
	break;

     case SPELL_AREA_OF_RETURN: 
	if (IS_IMMORTAL(ch) && PLR_FLAGGED(ch, PLR_LOGALL))
	  act("An area of return spell affects this room.",FALSE,ch,0,0,TO_CHAR);
	break;

     case SPELL_PROJECT: 
	if (IS_IMMORTAL(ch) && PLR_FLAGGED(ch, PLR_LOGALL))
	  act("An projection spell affects this room.",FALSE,ch,0,0,TO_CHAR);
	break;

     default: 
	break;
    }  // end of switch

    update_pos(ch);

    if (GET_POS(ch) == POS_DEAD)
    { 
      die(ch, FALSE);
      return CHAR_DIED;
    }
  }
  return CHAR_OK;
}

// update / check saves for every player in room
// short and sweet? lesse if this works ok
void room_affect_update(int room)
{
  struct room_affect_type *raf, *nxtraf, dupraf;
  chdata *ch, *next_ch;
  int dir;
  rmdata *rm = NULL;

  if (INVALID_ROOM(room))
    return;

  rm = &world[room];
  for (ch = rm->people; ch; ch = next_ch)
  {
    next_ch = ch->next_in_room;
    check_room_affects(ch, room);
  }

  if (!rm->room_affects)
    return;

  // scan list and move special room affectual spells (ex. typhoon)
  for (raf = rm->room_affects; raf; raf = nxtraf)
  {
    nxtraf = raf->next;
    switch (raf->spell) {
     case SPELL_TYPHOON:
	// find direction to go
       dir = number(0, NUM_OF_DIRS - 1);
	// if found, transfer affects (dupe over)
       if (DIR(room, dir) && DIR(room, dir)->to_room > 0)
       {
	dupraf = *raf;
	room_affect_remove(room, raf);
	affect_to_room(DIR(room,dir)->to_room, &dupraf);
	sprintf(buf, "A deadly %%1typhoon%%0 moves %s!",dirs[dir]);
	send_to_room_not_busy(buf, room);
	sprintf(buf, "A deadly %%1typhoon%%0 arrives from %s!",rev_dir_str[dir]);
	send_to_room_not_busy(buf, DIR(room, dir)->to_room);
       }
       break;
    }
  }
}

// No longer save object files in BINARY format
// save them in text format (re: objsave.c)  -roa
void crashsave_room(int vnum)
{
  int rnum;
  char fname[128];
  FILE *fp;
  extern int inv_count(obdata *ob);
  extern void extract_norents(obdata *ob);

  if ((rnum = real_room(vnum)) < 0)
    return;

  sprintf(fname, "rmobjs/%d.roa",vnum);
  if (!(fp = fopen(fname, "w"))) {
    sprintf(buf, "SYSERR: Error opening room object file %d",vnum);
    mudlog(buf, BRF, LEV_IMM, TRUE);
    return;
  }

  if (inv_count(world[rnum].contents) > MAX_HOUSE_OBJS)
  {
    sprintf(buf, "%%1WARNING%%0: Max room contents (%d) exceeded.\n\r"
                 "Room contents not saved.\n\r", MAX_HOUSE_OBJS);
    send_to_room(buf, rnum);
    fclose(fp);
    return;
  }

  // if nothing in room, remove file and return
  if (!world[rnum].contents)
  {
    fclose(fp);
    remove(fname);
    return;
  }

  // get rid of the non rentables in the room (keys/mails etc) -roa
  extract_norents(world[rnum].contents);

  // check one more time just so we're not calling a function for no reason
  // if the room only had norents, now it has nothing, remove file and return 
  if (!world[rnum].contents)
  {
    fclose(fp);
    remove(fname);
    return;
  }

  // save_object is recursive, just send the first object in the list
  // re: objsave.c
  if (!save_object(world[rnum].contents, fp)) 
  {
    fclose(fp);
    send_to_room("Error saving room contents, notify an immortal...\n\r",rnum);
    return;
  }

  fclose(fp);
  send_to_room("Saving room...\n\r", rnum);
  REMOVE_BIT(ROOM_FLAGS2(rnum), HOUSE_CRASH | ROOM_CRASH);

  sprintf(buf, "SYSUPD: Room objects #%d saved.", vnum);
  mudlog(buf, BUG, LEV_IMM, FALSE);
}

// wax a room file from disk, mudlog any errors -roa
void delete_room_file(int vnum)
{
  char fname[128];
  FILE *fp;

  sprintf(fname, "rmobjs/%d.roa",vnum);
  if (!(fp = fopen(fname, "r"))) 
  {
    if (errno != ENOENT) {
      sprintf(buf, "SYSERR: Error deleting room object file #%d. (1)", vnum);
      mudlog(buf, BRF, LEV_IMM, TRUE);
    }
    return;
  }

  fclose(fp);
  if (unlink(fname) < 0) {
    sprintf(buf, "SYSERR: Error deleting room object file #%d. (2)", vnum);
    mudlog(buf, BRF, LEV_IMM, TRUE);
  }
}

void room_save_all(void)
{
  int i;

  for (i=0; i < top_of_world; i++)
    if (ROOM_FLAGGED2(i, ROOM_CRASH))
      crashsave_room(world[i].number);
}