skylib_fluffos_v3/
skylib_fluffos_v3/bin/
skylib_fluffos_v3/bin/db/
skylib_fluffos_v3/fluffos-2.9-ds2.04/
skylib_fluffos_v3/fluffos-2.9-ds2.04/ChangeLog.old/
skylib_fluffos_v3/fluffos-2.9-ds2.04/Win32/
skylib_fluffos_v3/fluffos-2.9-ds2.04/compat/
skylib_fluffos_v3/fluffos-2.9-ds2.04/compat/simuls/
skylib_fluffos_v3/fluffos-2.9-ds2.04/include/
skylib_fluffos_v3/fluffos-2.9-ds2.04/testsuite/
skylib_fluffos_v3/fluffos-2.9-ds2.04/testsuite/clone/
skylib_fluffos_v3/fluffos-2.9-ds2.04/testsuite/command/
skylib_fluffos_v3/fluffos-2.9-ds2.04/testsuite/data/
skylib_fluffos_v3/fluffos-2.9-ds2.04/testsuite/etc/
skylib_fluffos_v3/fluffos-2.9-ds2.04/testsuite/include/
skylib_fluffos_v3/fluffos-2.9-ds2.04/testsuite/inherit/
skylib_fluffos_v3/fluffos-2.9-ds2.04/testsuite/inherit/master/
skylib_fluffos_v3/fluffos-2.9-ds2.04/testsuite/log/
skylib_fluffos_v3/fluffos-2.9-ds2.04/testsuite/single/
skylib_fluffos_v3/fluffos-2.9-ds2.04/testsuite/single/tests/compiler/
skylib_fluffos_v3/fluffos-2.9-ds2.04/testsuite/single/tests/efuns/
skylib_fluffos_v3/fluffos-2.9-ds2.04/testsuite/single/tests/operators/
skylib_fluffos_v3/fluffos-2.9-ds2.04/testsuite/u/
skylib_fluffos_v3/fluffos-2.9-ds2.04/tmp/
skylib_fluffos_v3/fluffos-2.9-ds2.04/windows/
skylib_fluffos_v3/mudlib/
skylib_fluffos_v3/mudlib/cmds/
skylib_fluffos_v3/mudlib/cmds/admin/
skylib_fluffos_v3/mudlib/cmds/guild-race/
skylib_fluffos_v3/mudlib/cmds/living/broken/
skylib_fluffos_v3/mudlib/cmds/player/group_cmds/
skylib_fluffos_v3/mudlib/cmds/playtester/
skylib_fluffos_v3/mudlib/d/admin/
skylib_fluffos_v3/mudlib/d/admin/room/
skylib_fluffos_v3/mudlib/d/admin/room/we_care/
skylib_fluffos_v3/mudlib/d/admin/save/
skylib_fluffos_v3/mudlib/d/admin/text/
skylib_fluffos_v3/mudlib/d/learning/TinyTown/buildings/
skylib_fluffos_v3/mudlib/d/learning/TinyTown/map/
skylib_fluffos_v3/mudlib/d/learning/TinyTown/roads/
skylib_fluffos_v3/mudlib/d/learning/chars/
skylib_fluffos_v3/mudlib/d/learning/functions/
skylib_fluffos_v3/mudlib/d/learning/handlers/
skylib_fluffos_v3/mudlib/d/learning/help_topics/
skylib_fluffos_v3/mudlib/d/learning/help_topics/npcs/
skylib_fluffos_v3/mudlib/d/learning/help_topics/objects/
skylib_fluffos_v3/mudlib/d/learning/help_topics/rcs_demo/
skylib_fluffos_v3/mudlib/d/learning/help_topics/rcs_demo/RCS/
skylib_fluffos_v3/mudlib/d/learning/help_topics/rooms/
skylib_fluffos_v3/mudlib/d/learning/help_topics/rooms/crowd/
skylib_fluffos_v3/mudlib/d/learning/help_topics/rooms/situations/
skylib_fluffos_v3/mudlib/d/learning/save/
skylib_fluffos_v3/mudlib/d/learning/school/
skylib_fluffos_v3/mudlib/d/learning/school/add_sc/
skylib_fluffos_v3/mudlib/d/learning/school/characters/
skylib_fluffos_v3/mudlib/d/learning/school/general/
skylib_fluffos_v3/mudlib/d/learning/school/getting-started/
skylib_fluffos_v3/mudlib/d/learning/school/getting-started/basic_commands/
skylib_fluffos_v3/mudlib/d/learning/school/getting-started/edtutor/
skylib_fluffos_v3/mudlib/d/learning/school/getting-started/unix_tutor/
skylib_fluffos_v3/mudlib/d/learning/school/items/
skylib_fluffos_v3/mudlib/d/learning/school/npc_school/
skylib_fluffos_v3/mudlib/d/learning/school/room_school/
skylib_fluffos_v3/mudlib/d/learning/school/room_school/room_basic/
skylib_fluffos_v3/mudlib/d/learning/school/room_school/situations/
skylib_fluffos_v3/mudlib/d/learning/school/room_school/terrain_tutor/
skylib_fluffos_v3/mudlib/d/learning/text/
skylib_fluffos_v3/mudlib/d/liaison/
skylib_fluffos_v3/mudlib/d/mudlib/
skylib_fluffos_v3/mudlib/d/mudlib/changes/
skylib_fluffos_v3/mudlib/d/playtesters/
skylib_fluffos_v3/mudlib/d/playtesters/effects/
skylib_fluffos_v3/mudlib/d/playtesters/handlers/
skylib_fluffos_v3/mudlib/d/playtesters/items/
skylib_fluffos_v3/mudlib/d/sage/
skylib_fluffos_v3/mudlib/doc/
skylib_fluffos_v3/mudlib/doc/creator/
skylib_fluffos_v3/mudlib/doc/driver/
skylib_fluffos_v3/mudlib/doc/driver/efuns/arrays/
skylib_fluffos_v3/mudlib/doc/driver/efuns/buffers/
skylib_fluffos_v3/mudlib/doc/driver/efuns/calls/
skylib_fluffos_v3/mudlib/doc/driver/efuns/compile/
skylib_fluffos_v3/mudlib/doc/driver/efuns/filesystem/
skylib_fluffos_v3/mudlib/doc/driver/efuns/floats/
skylib_fluffos_v3/mudlib/doc/driver/efuns/functions/
skylib_fluffos_v3/mudlib/doc/driver/efuns/general/
skylib_fluffos_v3/mudlib/doc/driver/efuns/mappings/
skylib_fluffos_v3/mudlib/doc/driver/efuns/mixed/
skylib_fluffos_v3/mudlib/doc/driver/efuns/mudlib/
skylib_fluffos_v3/mudlib/doc/driver/efuns/numbers/
skylib_fluffos_v3/mudlib/doc/driver/efuns/parsing/
skylib_fluffos_v3/mudlib/doc/login/
skylib_fluffos_v3/mudlib/doc/lpc/basic_manual/
skylib_fluffos_v3/mudlib/doc/lpc/intermediate/
skylib_fluffos_v3/mudlib/doc/new/add_command/
skylib_fluffos_v3/mudlib/doc/new/events/
skylib_fluffos_v3/mudlib/doc/new/handlers/
skylib_fluffos_v3/mudlib/doc/new/living/race/
skylib_fluffos_v3/mudlib/doc/new/living/spells/
skylib_fluffos_v3/mudlib/doc/new/object/
skylib_fluffos_v3/mudlib/doc/new/player/
skylib_fluffos_v3/mudlib/doc/new/room/guild/
skylib_fluffos_v3/mudlib/doc/new/room/outside/
skylib_fluffos_v3/mudlib/doc/new/room/storeroom/
skylib_fluffos_v3/mudlib/doc/object/
skylib_fluffos_v3/mudlib/doc/playtesters/
skylib_fluffos_v3/mudlib/doc/policy/
skylib_fluffos_v3/mudlib/doc/weapons/
skylib_fluffos_v3/mudlib/global/
skylib_fluffos_v3/mudlib/global/creator/
skylib_fluffos_v3/mudlib/handlers/
skylib_fluffos_v3/mudlib/include/casino/
skylib_fluffos_v3/mudlib/include/cmds/
skylib_fluffos_v3/mudlib/include/effects/
skylib_fluffos_v3/mudlib/include/npc/
skylib_fluffos_v3/mudlib/include/room/
skylib_fluffos_v3/mudlib/include/shops/
skylib_fluffos_v3/mudlib/net/daemon/
skylib_fluffos_v3/mudlib/net/daemon/chars/
skylib_fluffos_v3/mudlib/net/inherit/
skylib_fluffos_v3/mudlib/net/obj/
skylib_fluffos_v3/mudlib/net/obj/BACKUPS/
skylib_fluffos_v3/mudlib/obj/amulets/
skylib_fluffos_v3/mudlib/obj/armours/plate/
skylib_fluffos_v3/mudlib/obj/b_day/
skylib_fluffos_v3/mudlib/obj/clothes/transport/horse/
skylib_fluffos_v3/mudlib/obj/faith/symbols/
skylib_fluffos_v3/mudlib/obj/fungi/
skylib_fluffos_v3/mudlib/obj/gatherables/
skylib_fluffos_v3/mudlib/obj/instruments/
skylib_fluffos_v3/mudlib/obj/media/
skylib_fluffos_v3/mudlib/obj/misc/player_shop/
skylib_fluffos_v3/mudlib/obj/monster/godmother/
skylib_fluffos_v3/mudlib/obj/monster/transport/
skylib_fluffos_v3/mudlib/obj/rings/
skylib_fluffos_v3/mudlib/obj/scabbards/
skylib_fluffos_v3/mudlib/obj/spells/
skylib_fluffos_v3/mudlib/obj/stationery/
skylib_fluffos_v3/mudlib/obj/stationery/envelopes/
skylib_fluffos_v3/mudlib/obj/toys/
skylib_fluffos_v3/mudlib/obj/vessels/
skylib_fluffos_v3/mudlib/obj/weapons/axes/
skylib_fluffos_v3/mudlib/obj/weapons/chains/
skylib_fluffos_v3/mudlib/obj/weapons/maces/BACKUPS/
skylib_fluffos_v3/mudlib/save/autodoc/
skylib_fluffos_v3/mudlib/save/book_handler/
skylib_fluffos_v3/mudlib/save/books/history/calarien/
skylib_fluffos_v3/mudlib/save/mail/
skylib_fluffos_v3/mudlib/save/new_soul/data/
skylib_fluffos_v3/mudlib/save/parcels/
skylib_fluffos_v3/mudlib/save/playerinfo/
skylib_fluffos_v3/mudlib/save/players/d/
skylib_fluffos_v3/mudlib/save/players/s/
skylib_fluffos_v3/mudlib/save/random_names/
skylib_fluffos_v3/mudlib/save/random_names/data/
skylib_fluffos_v3/mudlib/save/terrains/
skylib_fluffos_v3/mudlib/save/terrains/tutorial_desert/
skylib_fluffos_v3/mudlib/save/terrains/tutorial_grassy_field/
skylib_fluffos_v3/mudlib/save/terrains/tutorial_mountain/
skylib_fluffos_v3/mudlib/save/todo_lists/
skylib_fluffos_v3/mudlib/secure/
skylib_fluffos_v3/mudlib/secure/cmds/admin/
skylib_fluffos_v3/mudlib/secure/cmds/lord/
skylib_fluffos_v3/mudlib/secure/config/
skylib_fluffos_v3/mudlib/secure/handlers/autodoc/
skylib_fluffos_v3/mudlib/secure/handlers/intermud/
skylib_fluffos_v3/mudlib/secure/include/global/
skylib_fluffos_v3/mudlib/secure/save/
skylib_fluffos_v3/mudlib/secure/save/handlers/
skylib_fluffos_v3/mudlib/secure/std/
skylib_fluffos_v3/mudlib/secure/std/classes/
skylib_fluffos_v3/mudlib/secure/std/modules/
skylib_fluffos_v3/mudlib/std/creator/
skylib_fluffos_v3/mudlib/std/dom/
skylib_fluffos_v3/mudlib/std/effects/
skylib_fluffos_v3/mudlib/std/effects/external/
skylib_fluffos_v3/mudlib/std/effects/fighting/
skylib_fluffos_v3/mudlib/std/effects/magic/
skylib_fluffos_v3/mudlib/std/effects/magic/BACKUPS/
skylib_fluffos_v3/mudlib/std/effects/other/BACKUPS/
skylib_fluffos_v3/mudlib/std/effects/priest/
skylib_fluffos_v3/mudlib/std/effects/room/
skylib_fluffos_v3/mudlib/std/environ/
skylib_fluffos_v3/mudlib/std/guilds/
skylib_fluffos_v3/mudlib/std/guilds/old/
skylib_fluffos_v3/mudlib/std/languages/
skylib_fluffos_v3/mudlib/std/liquids/
skylib_fluffos_v3/mudlib/std/npc/
skylib_fluffos_v3/mudlib/std/npc/goals/
skylib_fluffos_v3/mudlib/std/npc/goals/basic/
skylib_fluffos_v3/mudlib/std/npc/goals/misc/
skylib_fluffos_v3/mudlib/std/npc/plans/
skylib_fluffos_v3/mudlib/std/npc/plans/basic/
skylib_fluffos_v3/mudlib/std/npc/types/
skylib_fluffos_v3/mudlib/std/npc/types/helper/
skylib_fluffos_v3/mudlib/std/npcs/
skylib_fluffos_v3/mudlib/std/outsides/
skylib_fluffos_v3/mudlib/std/races/shadows/
skylib_fluffos_v3/mudlib/std/room/basic/BACKUPS/
skylib_fluffos_v3/mudlib/std/room/basic/topography/
skylib_fluffos_v3/mudlib/std/room/controller/
skylib_fluffos_v3/mudlib/std/room/inherit/topography/
skylib_fluffos_v3/mudlib/std/room/topography/area/
skylib_fluffos_v3/mudlib/std/room/topography/iroom/
skylib_fluffos_v3/mudlib/std/room/topography/milestone/
skylib_fluffos_v3/mudlib/std/shadows/curses/
skylib_fluffos_v3/mudlib/std/shadows/disease/
skylib_fluffos_v3/mudlib/std/shadows/fighting/
skylib_fluffos_v3/mudlib/std/shadows/healing/
skylib_fluffos_v3/mudlib/std/shadows/magic/
skylib_fluffos_v3/mudlib/std/shadows/poison/
skylib_fluffos_v3/mudlib/std/shadows/room/
skylib_fluffos_v3/mudlib/std/shops/controllers/
skylib_fluffos_v3/mudlib/std/shops/objs/
skylib_fluffos_v3/mudlib/std/shops/player_shop/
skylib_fluffos_v3/mudlib/std/socket/
skylib_fluffos_v3/mudlib/std/soul/d/
skylib_fluffos_v3/mudlib/std/soul/e/
skylib_fluffos_v3/mudlib/std/soul/i/
skylib_fluffos_v3/mudlib/std/soul/j/
skylib_fluffos_v3/mudlib/std/soul/k/
skylib_fluffos_v3/mudlib/std/soul/l/
skylib_fluffos_v3/mudlib/std/soul/n/
skylib_fluffos_v3/mudlib/std/soul/o/
skylib_fluffos_v3/mudlib/std/soul/q/
skylib_fluffos_v3/mudlib/std/soul/r/
skylib_fluffos_v3/mudlib/std/soul/u/
skylib_fluffos_v3/mudlib/std/soul/v/
skylib_fluffos_v3/mudlib/std/soul/y/
skylib_fluffos_v3/mudlib/std/soul/z/
skylib_fluffos_v3/mudlib/std/stationery/
skylib_fluffos_v3/mudlib/w/
skylib_fluffos_v3/mudlib/w/default/
skylib_fluffos_v3/mudlib/w/default/armour/
skylib_fluffos_v3/mudlib/w/default/clothes/
skylib_fluffos_v3/mudlib/w/default/item/
skylib_fluffos_v3/mudlib/w/default/npc/
skylib_fluffos_v3/mudlib/w/default/room/
skylib_fluffos_v3/mudlib/w/default/weapon/
skylib_fluffos_v3/mudlib/www/
skylib_fluffos_v3/mudlib/www/java/
skylib_fluffos_v3/mudlib/www/secure/
skylib_fluffos_v3/mudlib/www/secure/lpc/advanced/
skylib_fluffos_v3/mudlib/www/secure/lpc/intermediate/
skylib_fluffos_v3/win32/
/**
 * This is the situation changer object associated with a room object.
 */

#include <time.h>
#include <situations.h>

#define RANDOM(a,b) ("/handlers/random_num"->random(a,b))

class situation_timing {
  mixed label;
  mixed duration;
  mixed when;
  int chance;
  int *endat;
  mixed background;
  mixed category;
  int it;
  mapping it_data;
}

nosave int offset;
nosave string *els;
nosave mixed *currentsits,*autosits;
nosave mapping sitdata;
nosave mapping sitwords;
nosave class situation_timing *sittiming;
/* By default chat fairly frequently (1-2 minutes) because
 * these situations will not necessarily be around that long */
nosave int chatmin=60,chatmax=120;
nosave object ownerroom;
nosave int automate=0; /* 0=no, 1=yes, 2=sleeping */
nosave mixed cco; /* cco[0] = last handle, cco[i] = call_outs for handle i>0 */

varargs mixed change_situation( mixed label, mixed duration,
  mixed words, mixed handle );

object set_room(object room_o) {
  //  tell_creator("shaggy","Room set: %O %O %O\n",room_o,TO,previous_object());
  ownerroom=room_o;
  return TO;
}

object query_room() { return ownerroom; }
/**
 * This function selects word replacements for #n in the text.
 * @param label Situation label to choose for
 * @param choice A random seed (integer) or
 *               a set of pairs exactly the same as the second
 *               argument to replace.
 * @see replace
 * @example
 *      choose_words( "frog", ({ "#1", "tadpole", "#2", "pond" }));
 */
void choose_words( mixed label, mixed choice ) {
  int i;
  class situation sit;
  string *wc;

  sit = sitdata[ label ];
  if (sizeof(sit->random_words)) {
    if (!sitwords) sitwords= ([ ]);

    if (intp(choice)) {
      wc=({ });
      for (i=0;i<sizeof(sit->random_words);i++) {
        wc+=({ "#"+(i+1),
          ((sit->random_words)[i])[
          RANDOM(sizeof((sit->random_words)[i]),choice+i*3347483647)] });
      }
      sitwords[label] = wc;
    }
    else
      sitwords[label] = choice;
  }
}

/**
 * Replaces #1 in text with the one of the first array of words in wordlist
 * and #2 with one of the second array of words and so on...
 * Each string in the string array is changed.
 */
string *insert_words_chats(class situation sit, string *words) {
  string *outarray=({ });
  string s1;

  if (!sizeof(sit->chats)) return ({ });
  foreach (s1 in sit->chats) outarray+= ({ replace(s1,words) });
  return outarray;
}

/**
 * Replaces #1 in text with the one of the first array of words in wordlist
 * and #2 with one of the second array of words and so on...
 * For items only the item text is changed, not the key words.
 */
mixed *insert_words_items(class situation sit, string *words) {
  mixed *outarray=({ });
  mixed ai,e0,e1;

  if (!sizeof(sit->add_items)) return ({ });
  foreach (ai in sit->add_items) {
    if (sizeof(ai)>=2) {
      e0=ai[0];
      if (arrayp(e0)) e0=explode(lower_case(replace(implode(e0,"|"),words)),"|");
      else e0=lower_case(replace(e0,words));
      e1=ai[1];
      if (arrayp(e1)) e1=explode(replace(implode(e1,"|"),words),"|");
      else e1=replace(e1,words);
      ai = ({ e0,e1 })+ai[2..];
    }
    outarray+= ({ ai });
  }
  return outarray;
}

/**
 * returns status of situation manager.
 * If it is sleeping it will turn on again if a
 * player enters the room.
 * @return status 0 off 1 on 2 sleeping
 */
int query_status() { return automate; }

/**
 * returns situations currently turned on.
 * @return int array of situation labels
 */
int *query_current_situations() { return ({ currentsits, autosits }); }

/**
 * returns sittiming class with info about automated situations
 * @return class sittiming
 */
mixed *query_sittiming() { return sittiming; }

/**
 * returns mapping of situations.
 * @return mapping of situations
 */
mapping query_situations() { return sitdata; }

void dest_me() {
  destruct(TO);
} /* dest_me() */

/** @ignore yes */
int clean_up( int parent ) {
/* Clones of this object are pointless without a room to manage. */
  if (parent) {
    return 0;
  }
  return 1;
} /* clean_up() */

/**
 * Adds a situation to the room.  These situations can be
 * invoked manually with start_situation or automatically via
 * automate_situation.
 * @param label string or number labelling the situation
 * @param sit a structure (class) containing all the bits
 * of the situation you want to add.
 * eg.
 * start_func function to be called at start of situation
 *             that might be used to load NPC's or anything
 *             beyond a message.
 *
 *             The start function is passed the label,
 *             a do_start_mess flag and the room object.
 *             If the flag is 1 the situation is starting
 *             rather than being reloaded.  Thus if
 *             do_start_mess is 0 then you should avoid
 *             any obvious start messages and make it look
 *             like the situation is already underway.
 *
 * end_func function to be called an the end of a situation.
 *             The end function is only
 *             passed the label and the room object.
 *
 * start_mess message told to the room at start of situation
 *
 * end_mess message told to the room at end of situation
 *
 * extra_look extra look string appended to rooms long
 *             during the situation
 *
 * chats an array of chat strings to be active
 *             during the situation
 *
 * add_items a mixed array of ({ item, item description })
 *             pairs to be active during the situation
 *
 * random_words sets of words of the form ({ ({ "option1","option2" }),
 *             ({ "adjective1","adjective2" }), ... }).  One of the
 *             the first set replaces #1 in any text above and one of
 *             the second set replaces #2 in any text above and so on.
 *             The random choice is fixed for the duration of any one
 *             situation.
 *
 * @see start_situation
 * @see automate_situation
 * @see add_item
 * @see room_chat
 * @see add_extra_look
 * @example
 * #include <situations.h>
 *
 * class situation frogs;
 * frogs = new(class situation,
 *     start_mess: "Water seeps out of the ground to form puddles.",
 *     extra_look: "There are large puddles on the ground here.",
 *     chats: ({"A hidden frog croaks quietly.",
 *              "There is a blooping sound." }),
 *     add_items:({ ({"puddle", "The puddles are dark and murky.  "
 *                    "They will probably dry up given time." }) }) );
 * add_situation( "frogs", frogs );
 * @example
 * add_situation( "frogs", new(class situation,
 *      start_mess: "Water seeps out of the ground to form puddles.",
 *      extra_look: "There are large puddles on the ground here.",
 *      chats: ({"A hidden frog croaks quietly.",
 *               "There is a blooping sound." }),
 *      add_items: ({ ({"puddle", "The puddles are dark and murky.  "
 *                      "They will probably dry up given time." }) }) ));
 */
void add_situation( mixed label, class situation sit ) {
  if (!sizeof(sitdata)) {
    sitdata= ([ label : sit ]);
    sitwords= ([ ]);
  }
  else {
    sitdata+= ([ label : sit ]);
  }
}

/**
 * Starts a situation previously added to the room
 * that is managed by this object.
 * These situations can be invoked manually with
 * start_situation or automatically via
 * automate_situation.
 * @param label string or number labelling the situation
 * @param do_start_mess 0 to supress the start_mess string
 *        This is to fake it that a situation has been
 *        going for a while when really you just loaded it.
 * @see add_situation
 * @see end_situation
 * @see automate_situation
 */
void start_situation(mixed label, int do_start_mess) {
  class situation sdata;
  string *chats;
  mixed item,*items;
  object chatter;
  string *words;

  sdata=sitdata[ label ];
  words=sitwords[ label ];
  //  tell_creator("shaggy","label starting: %O\n",label);

  if (!currentsits) currentsits=({ label });
  else currentsits=currentsits+({ label });

  //  tell_creator("shaggy","Start seed: %O\n",seed);

  if (sizeof(sdata->extra_look)) {
    if (els && sizeof(els))
      els+=({ replace(sdata->extra_look,words) });
    else {
      els=({ replace(sdata->extra_look,words) });
      ownerroom->add_extra_look(TO);
    }
  }
  if (do_start_mess && sizeof(sdata->start_mess))
    tell_room(ownerroom, replace(sdata->start_mess,words)+"\n");
  chats=insert_words_chats(sdata,words);
  if (chats && sizeof(chats)) {
    //    tell_creator("shaggy","Adding: %O %O %O\n",sdata->chat_rate,chats,ownerroom->query_chatter());
/* Note we use the old chatter because it is more efficient unless
   we know for certain that we need chats with different rates
   in which case we use the multichatter
*/
    if (!(chatter=ownerroom->query_chatter())) {
      if (sdata->chat_rate) {
        ownerroom->room_chat( ({ chatmin,chatmax, ({ }) }),
                      clone_object("/std/room/basic/multichatter")  );
        chatter=ownerroom->query_chatter();
        chatter->setup_chatter( ownerroom,
        ({ (sdata->chat_rate)[0],(sdata->chat_rate)[1], chats }) );
      } else {
        ownerroom->room_chat( ({ chatmin,chatmax, chats }) );
        chatter=ownerroom->query_chatter();
      }
      chatter->check_chat();
    } else {
      if (sdata->chat_rate) {
        if (!(chatter->query_multichatter())) {
          mixed *args;
          args=(chatter->query_room_chats())[0..2];
          chatter->dest_me();
          ownerroom->room_chat( args,
                        clone_object("/std/room/basic/multichatter")  );
          chatter = ownerroom->query_chatter();
        }
        chatter->setup_chatter( ownerroom,
          ({ (sdata->chat_rate)[0],(sdata->chat_rate)[1], chats }) );
        chatter->check_chat();
      }
      else chatter->add_room_chats(chats);
    }
  }
  items=insert_words_items(sdata,words);
  if (sizeof(items)) {
    foreach(item in items) {
      ownerroom->add_item(item[0],item[1]);
      //      tell_creator("shaggy","Adding item: %O:%O\n",item[0],item[1]);
    }
  }
  if (sdata->start_func) { /* functionp */
    (*(sdata->start_func))(label,do_start_mess,ownerroom);
  }
} /* start_situation() */

/**
 * Ends a situation previously added and started on the room
 * that is managed by this object.
 * These situations can be invoked manually with start_situation
 * or automatically via automate_situation.
 * @param label string or number labelling the situation
 * @see add_situation
 * @see start_situation
 * @see automate_situation
 */
void end_situation(mixed label) {
  class situation sdata;
  string *chats;
  mixed item,*items;
  object chatter;
  string *words;

  sdata=sitdata[ label ];
  words=sitwords[ label ];
//  tell_creator("shaggy","label ending: %O\n",label);

  if (currentsits) currentsits=currentsits-({ label });
  if (els && sizeof(sdata->extra_look)) {
    els-=({ replace(sdata->extra_look,words) });
    if (!sizeof(els)) ownerroom->remove_extra_look(TO);
  }

  chats=insert_words_chats(sdata,words);
  if (chats && sizeof(chats) && (chatter=ownerroom->query_chatter())) {
    //tell_creator("shaggy","Removing: %O\n",chats);
    chatter->remove_room_chats(chats);
  }
  items=insert_words_items(sdata,words);
  if (sizeof(items)) {
    foreach(item in items) {
      if (arrayp(item[0]))
        ownerroom->remove_item((item[0])[0]);
      else
        ownerroom->remove_item(item[0]);
      //        tell_creator("shaggy","Removing item: %O:%O\n",item[0],item[1]);
    }
  }
  if (sizeof(sdata->end_mess))
    tell_room(ownerroom,replace(sdata->end_mess,words)+"\n");
  if (sdata->end_func) { /* functionp */
    (*(sdata->end_func))(label,ownerroom);
  }
} /* end_situation() */

/**
 * Starts one or more situations that will end after a
 * specified duration.  You can use an array and make
 * further situations commence when others end.
 * @param label (mixed) label of the situation to start
 *  up.  If you pass an array such as ({ "frog1", "frog2" }) for the
 * label then that set of situations are started one at
 * a time and the total duration is split evenly between them.
 * Label is usually an integer or a string or an array of
 * integers and/or strings.
 * If the string is a list of labels
 * separated by , then multiple situations
 * are started using those labels.
 * @param duration (int) total time (seconds) the overall situation
 * should last.  You can put an array of durations -- one for each
 * situation if the label lists more than one situation and then
 * the overall time is the sum of the numbers.
 * -1 means indefinite so having any situations after
 * something with -1 duration is pointless.
 * @param handle is an internal thing that should only be called with 0
 * unless you really know what you are doing.
 * @param words is a list of replacements for #n in the text OR
 *        a random number seed, it is
 *        passed to choose_words.
 *        eg. ({ "#1", "frog", "#2", "honey" }) or 22
 * @return call_out that is propogating the changes
 * This is useful if you want to be able to kill the whole
 * set without disturbing other situations.
 * @see start_situation
 * @see end_situation
 * @see add_situation
 * @see choose_words
 */
varargs mixed change_situation( mixed label, mixed duration,
     mixed words, mixed handle  ) {
  mixed frog,bing;
  mixed ending;

//  tell_creator("shaggy","CHANGE: %O %O\n",label,duration);

  if (!cco) cco=({ 1, 0 });
  if (!handle) {
    handle=member_array(0,cco[1..<1])+1;
    if (!handle) {
      cco+=({ 0 });
      handle=sizeof(cco)-1;
    }
  }
  cco[0]=handle;

  if (!duration) {
    if (arrayp(label)) frog=label[0];
    else frog=label;
    if (stringp(frog)) frog=explode(frog,",");
    else frog=({ frog });
    foreach(bing in frog) end_situation(bing);
    cco[handle]=0;
    return 0;
  }
  if (!arrayp(duration)) {
    if (arrayp(label)) {
      bing=duration/sizeof(label);
      duration=({ });
      foreach (frog in label) duration+=({ bing });
    }
    else duration=({ duration });
  }

  if (arrayp(label) && sizeof(label)>1 && sizeof(label)>sizeof(duration)) {
    frog=label[0];
    if (stringp(frog)) ending=explode(frog,",");
    else ending=({ frog });
    label=label[1..<1];
  }

  if (sizeof(duration)==1) {
    if (arrayp(label)) frog=label[0];
    else frog=label;
    if (stringp(frog)) frog=explode(frog,",");
    else frog=({ frog });
    if (ending) {
      ending-=frog;
      foreach(bing in ending) end_situation(bing);
    }
    if (currentsits) frog=frog-currentsits;
    foreach(bing in frog) {
      if (!sizeof(words)) choose_words( bing, time()*335423611 );
      else choose_words( bing, words );
      start_situation(bing,1);
    }
    if (duration[0]!=-1)
      cco[handle]=call_out("change_situation",duration[0],
                           label,0,words,handle);

    return handle;
  }
  if (arrayp(label)) frog=label[0];
  else frog=label;
  if (stringp(frog)) frog=explode(frog,",");
  else frog=({ frog });
  if (ending) {
    ending-=frog;
    foreach(bing in ending) end_situation(bing);
  }
  if (currentsits) frog=frog-currentsits;
  foreach(bing in frog) start_situation(bing,1);
  if (duration[0]!=-1)
    cco[handle]=
    call_out("change_situation",duration[0],
             label,duration[1..<1],words,handle);

  return handle;
} /* change_situation */

int query_possible(class situation_timing sit, int it, int tod, int cnt) {
  int possible;
  class situation_timing sit2;
  int cnt2,it2,tod2,possible2;

  if (functionp(sit->when)) possible=(*(sit->when))(tod);
  else possible=sit->when;

  possible = ( possible & ( 1 << TIME_H->query_hour(tod) ) ) &&
      ( RANDOM( 1000, it * 1147483647 + cnt * 2047483243 ) < sit->chance );
  // tell_creator("shaggy","trying %O (%O %O %O %O %O %O)\n",sit->label,possible,sit->when,(1<<(TIME_H->query_hour(tod))),RANDOM(1000,it*1147483647+cnt*2047483243),tod,it);
  // tell_creator("shaggy","trying %O %O %O %O %O %O\n",time(),t,offset,ttmp,tod,it);
  if (possible && sit->category) {
    /* If it is a member of a category we must check that there
       are no others of the category going first */
    cnt2=0;
    foreach (sit2 in sittiming) {
      cnt2++;
      if (cnt2!=cnt && sit2->category == sit->category) {
        it2=(tod+offset+(cnt2*234231))/sit2->duration;
        tod2=it2*sit2->duration-offset-(cnt2*234231);
        if (functionp(sit2->when)) possible2=(*(sit2->when))(tod2);
        else possible2=sit2->when;
        possible2=
          (possible2&(1<<(TIME_H->query_hour(tod2))))
          &&
          (RANDOM(1000,it2*1147483647+cnt2*2047483243) < sit2->chance);
        //        tell_creator("shaggy","Comparing %O (%O %O %O %O %O %O)\n",sit2->label,possible2,sit2->when,(1<<(TIME_H->query_hour(tod2))),RANDOM(1000,it2*1147483647+cnt2*2047483243),tod2,it2);
        if (possible2 && (tod2<tod || (tod==tod2 && cnt2<cnt))) {
          possible=0;
          break;
        }
      }
    }
  }

  return possible;

} /* query_possible() */

/**
 * Starts and ends situations according to the information
 * in the sittiming array.  It is called continuously
 * automatically while there are interactives in the room.
 */
void manage_situations() {
  class situation_timing sit;
  int t,t0,it,tod,cnt,possible,dt,tc,i;
  mixed label,lb;
  mixed *newsits;
  mixed *changes;
  mixed *background_on,*background_off; /* background situations */
  int ttmp,tstep;

//  tell_creator("shaggy","Managing: %s %O\n",ownerroom->query_short(),sittiming);

  if (!automate) return;
  if (!sizeof(filter_array( INV( ownerroom ),(: interactive($1) :)))) {
    automate=2;
    return;
  }
  //  tell_creator("shaggy","Time %O\n",time());
  t0=time()+offset;
  newsits=({});
  tstep=2147483648;
  background_on=({ });
  background_off=({ });

  cnt=0;
  foreach (sit in sittiming) {
    cnt++;
    t=t0+(cnt*234231);
    ttmp=sit->duration;
    it=t/ttmp;
    /* time of day it started */
    tod=it*ttmp-offset-(cnt*234231);

    /* possible values cached in it_data mapping */
    possible=sit->it_data[ it ];

    /* If it's not in cache -- work it out */
    if (undefinedp(possible)) {
      possible=query_possible(sit, it, tod, cnt);
      sit->it_data[ it ]=possible;
      if (!undefinedp(sit->it_data[ it-1 ]))
        map_delete(sit->it_data,it-1);
    } else {
      //      tell_creator("shaggy","Test for %O cached %O\n",sit->label,possible);
    }

    if (possible) {
      if (sit->background) background_off+=explode(sit->background,",");
      if (arrayp(sit->label)) {
        //      label=(sit->label) [((t % ttmp)*sizeof(sit->label))/tmp];
        dt=(t % ttmp);
        i=0;
        foreach (tc in sit->endat) {
          if (tc>dt) break;
          i++;
        }
        label=(sit->label)[i];
        ttmp=tc-dt;
      }
      else label=sit->label;

      if (stringp(label)) {
        label=explode(label,",");
        newsits=newsits+label;
        foreach(lb in label) choose_words(lb,it*1147483647+cnt*2047555777);
      }
      else {
        newsits=newsits+({ label });
        choose_words(label,it*1147483647+cnt*2047555777);
      }
    }
    else {
      if (sit->background) background_on+=explode(sit->background,",");
      /* pretest next possible time to avoid retesting every situation,
         every time one of them might start */
      /* possible values cached in it_data mapping */
      possible=sit->it_data[ it+1 ];
      /* If it's not in cache -- work it out */
      if (undefinedp(possible)) {
        possible=query_possible(sit, it+1, tod+sit->duration, cnt);
        sit->it_data[ it+1 ]=possible;
        if (!undefinedp(sit->it_data[ it-1 ]))
          map_delete(sit->it_data,it-1);
      }
      if (possible)
        ttmp=ttmp-(t % ttmp);
      else
        ttmp=2*ttmp-(t % ttmp);
    }

    if (ttmp<tstep) tstep=ttmp;
  }

  //  tell_creator("shaggy","newsits: %O tstep: %O t: %O/%O \n",newsits,tstep,dt,tc);

  call_out("manage_situations",tstep+1);

  newsits = newsits+(background_on-background_off);

  if (autosits && sizeof(autosits)) {
    changes=autosits-newsits;
    if (sizeof(changes)) {
      foreach(label in changes) end_situation(label);
      autosits=autosits-changes;
    }
    changes=newsits-autosits;
  }
  else changes=newsits;
  if (sizeof(changes)) {
    foreach(label in changes) start_situation(label,2-automate);
    if (autosits) autosits=autosits+changes;
    else autosits=changes;
  }

  automate=1;
} /* manage_situations() */

/**
 * Tests for enabling situation managing.
 * If situation managing is already active or turned off
 * it does nothing.
 * @see automate_situation
 */
void check_situations() {
  if (automate==2 && interactive(TP)) manage_situations();
}

/**
 * Makes a seed value for the random part of when
 * situations turn on and off.  The two ints must be
 * constant for a given room -- like the coordinates.
 * @param xval integer to use to make a seed (eg. x coordinate)
 * @param yval integer to use to make a seed (eg. y coordinate)
 */
void make_seed(int xval, int yval) {
  /* The >>2 makes time() + offset always positive */
  offset=(xval*38547+yval*1232444311)>>2;
} /* set_seed() */

/**
 * Automate starting and ending of a situations.
 * These situations can be invoked manually with start_situation.
 * The automated starting and ending is unaffected by the room
 * unloading.  When the room reloads the situation will be
 * restarted unless its duration is up.
 * You must include the file situations.h for the definitions
 * of the when masks.
 * @param label (mixed) label of the situation to start
 *  up.  If you pass an array such as ({ "frog1", "frog2" }) for the
 * label then that set of situations are started one at
 * a time and the total duration is split evenly between them.
 * Label is usually an integer or a string or an array of
 * integers and/or strings.
 * If the string is a list of labels
 * separated by , then multiple situations
 * are started using those labels.
 * @param duration (int) total time (seconds) the overall situation
 * should last.  If an array is specified for duration each
 * situation gets it's own little one.  If -1 is specified as
 * a duration for one part that situation is background complimentary
 * situation to the rest that is on when the rest are off.
 * @param when (int) a time of the day mask.  This limits when
 * the situation is allowed to occur.  The mask is composed of
 * the allowed hours in mud time ( 24 hours clock, (1<<hour) and
 * combined with | (OR) ).   You can just use these
 * predefined masks and ingore how it works:
 *  WHEN_WEE_HOURS, WHEN_EARLY_MORNING, WHEN_LATE_MORNING, WHEN_AFTERNOON
 *  WHEN_EVENING, WHEN_LATENIGHT, WHEN_MIDDAY, WHEN_MORNING,
 *  WHEN_EARLY_MORNING, WHEN_LATE_MORNING, WHEN_NIGHT, WHEN_DAY
 *  WHEN_ANY_TIME
 * The masks are defined in /include/situations.h.
 * @param chance  (int) chance in 1000 of starting the situation
 *  This is tested every duration seconds.
 * @see add_situation
 * @see start_situation
 * @see end_situation
 * @see evolvingroom.h
 * @example
 * #include <situations.h>
 *
 *  automate_situation( "frog", 300, WHEN_ANY_TIME, 200 );
 *
 * This will automatically start the situation labelled "frog"
 * at a random time that is any time of the day with a 200/1000
 * chance of it starting per 300 seconds.  It will last for
 * 300 seconds (5 minutes).
 * @example
 *  automate_situation( ({"frog1","frog2"}), 240, WHEN_EVENING|WHEN_NIGHT,
 * , 300 );
 *
 * This will automatically start a situation that is a combination
 * of "frog1" followed by "frog2" at a random time but only in the
 * evening or at night.  There will be a 300/1000 chance of it
 * starting per 240 seconds.  Both the "frog1" and "frog2"
 * situations will get half the total time (as there are two),
 * 120 seconds each, for a total duration of 240 seconds (4 minutes).
 */
void automate_situation( mixed label, mixed duration, mixed when, mixed chance,
     mixed category ) {
  mixed chatargs,endat,background,labels;
  int i,d;
  string ook;

//  tell_creator("shaggy","Automating %O\n",label);

  if (!offset) {
    //    tell_creator("shaggy","automate_sit: %O %O %O\n",ownerroom,TO,previous_object());
    ook=file_name(ownerroom);
    if (!ook || ook=="") {
      write( "Bad file name" );
      return;
    }
    /* generate a seed value that is all the characters in the
       filename multiplied together:  not wonderful but you
       can specify your own with situation_changer_seed */
    offset=1;
    foreach (i in ook) offset*=i;
    /* The >>2 makes time() + offset always positive */
    offset=(offset>>2)+1;

  }
  if (ownerroom->query_chatter()) {
    chatargs=(ownerroom->query_chatter())->query_room_chats();
    if (sizeof(chatargs)==3) {
      chatmin=chatargs[0];
      chatmax=chatargs[1];
    }
  }

  endat=({ });
  if (arrayp(duration)) {
    if (!arrayp(label) || sizeof(duration)!=sizeof(label)) {
      write("Duration must be an array of the same length as label");
      return;
    }
    d=0;
    labels=({ });
    for (i=0;i<sizeof(duration);i++) {
      if (duration[i]!=-1) {
        d+=duration[i]; /* -1 means infinite duration background situation */
        endat+=({ d });
        labels=labels+({ label[i] });
      }
      else {
        if (background) background=implode(explode(background,",")+({ label[i] }),",");
        else background=label[i];
      }
    }
  } else {
    labels=label;
    d=duration;
    if (arrayp(label)) {
      for (i=0;i<sizeof(label);i++) {
        endat+=({ ((i+1)*duration)/sizeof(label) });
      }
    } else {
      endat=({ duration });
    }
  }
  //  tell_creator("shaggy","%O endat %O\n",label,endat);

  if (!sittiming)
    sittiming=
      ({ new(class situation_timing, label: labels, duration: d,
             when: when, chance: chance, endat: endat, background: background,
             category: category, it: 0, it_data: ([ ]) ) });
  else
    sittiming=sittiming+
      ({ new(class situation_timing, label: labels, duration: d,
             when: when, chance: chance, endat: endat, background: background,
             category: category, it: 0, it_data: ([ ]) ) });
  automate=2; /* start sleeping */
} /* automate_situation() */

/**
 * Shuts down all current and pending situations.  It also turns off the
 * automated situation manager so no more are added.  It does not
 * destruct this object so all the add_situations are still loaded
 * and may be recommenced with automate_situation or change_situation.
 * dest_me is the appropriate call to permanently remove all situations.
 * @see automate_situation
 * @see change_situation
 */
void shutdown_all_situations() {
  int label;
  mixed h;

  if (sizeof(cco)>1) {
    foreach (h in cco[1..<1]) {
       if (h) remove_call_out(h);
    }
  }
  cco=({ 0 });
  if (currentsits && sizeof(currentsits)) {
    foreach(label in currentsits) end_situation(label);
  }
  autosits=0;
  automate=0;
} /* shutdown_all_situations() */

/**
 * Shuts down a change_situation based on the call_out handle
 * returned by the call to change_situation.
 * @param handle call_out handle.  If 0 then the last
 * known handle is used.
 * @param label label or array of labels of situations to clean
 * up with end_situation
 * @param the_room the room
 * @see automate_situation
 * @see change_situation
 */
void shutdown_situation(int handle, mixed label) {
  mixed frog,bing;

  if (sizeof(cco)) {
    if (!handle) handle=cco[0];
    if (handle && cco[handle]) remove_call_out(cco[handle]);
    cco[handle]=0;
  }

  if (arrayp(label)) {
    frog=({ });
    foreach(bing in label) {
      if (stringp(bing)) frog+=explode(bing,",");
      else frog+=({ bing });
    }
  }
  else if (stringp(label)) label=explode(label,",");
  else label=({ label });

} /* shutdown_sit uation() */

string extra_look() {
  if (!els || !sizeof(els)) {
    return "";
  }
  return implode(els,"  ")+"\n";
} /* add_extra_look() */