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 co-inheritable for rooms containing water.  It handles adding
 * the appropriate effects to objects entering the water, skillchecks to see
 * if they can move around, making their positions appropriate and things like
 * that.  It also takes care of some miscellaneous stuff like salinity,
 * turbidity, currents etc.  Rather than using this co-inheritable directly, it
 * is recommended that you inherit either /std/room/water_inside.c or
 * /std/room/water_outside.c.
 * When using this file, remember the following: if you mask event_enter you
 * should call the inherited function, and unforeseen consequences may result
 * from using modify_exit with the "function", "exit mess" or "enter mess"
 * options.  Using "exit mess" or "enter mess" will only disrupt the tailored
 * swimming/drifting/floating/sinking exit and entry messages, but changing the
 * exit function will allow people through exits without passing the
 * appropriate swimming skillcheck.  For this reason, it is recommended to mask
 * swim_exit instead, and check that the inherited function returns 1 before
 * allowing them to pass.
 * @author Bakhtosh
 * @see /std/room/water_inside
 * @see /std/room/water_outside
 * @see /std/effects/other/immersed
 */

#include <armoury.h>
#include <position.h>
#include <room.h>
#include <tasks.h>

#define ANCHOR_PROP "anchor"
#define GILLS_PROP "gills"
#define FLOATING_PROP "floating"
#define LIVES_IN_WATER_PROP "lives in water"
#define SWIMMING_SKILL "general.swimming"
#define OBJECT_WET_EFFECT "/std/effects/object/wet"
#define LIVING_WET_EFFECT "/std/effects/other/wetness"
#define SWIM_EFFECT "/std/effects/other/water_effect"

mixed query_property(string);
varargs mixed *query_dest_other(string);
varargs int add_property(string, mixed, int);
int modify_exit(mixed, mixed*);
string query_destination(string);
string *query_exits();
int set_water_light(int);
int query_my_light();
varargs string *query_dest_dir(object);
int lives_in_water( object ob );

varargs void soak(object, int);
object get_water();
int water_override(string);
int do_float();
int do_drift();
int do_swim();
int get_swim_enum(object);
int query_bottom();
int query_surface();
string get_exit_mess(object, string);
string get_enter_mess(object, string);
object query_above_room();
void update_water_light();

private string on_bottom = "lying on the bottom",
        non_float = "drifting nearby",
        floating = "floating nearby",
        sinking = "sinking nearby",
        s_in_mess = "$N sink$s $down$ from $F.",
        s_out_mess = "$N sink$s $down$.",
        f_in_mess = "$N float$s $up$ from $F.",
        f_out_mess = "$N float$s $up$.",
        c_in_mess = "$N $V$0=is,are$V$ swept in from $F by the current.",
        c_out_mess = "$N $V$0=is,are$V$ swept $T by the current.",
        up_dir = "up", down_dir = "down";
        
private int bottom = -1, surface = -1, clarity = 90, salinity = 0,
        turbulence = 100, update_light = 1, light_first_queried = 0,
        last_speech_volume = 0;
        
private mapping flows = ([ ]), exit_messes = ([ ]), enter_messes = ([ ]),
        origins = ([ ]);

/**
 * This sets up different search returns for the default 'not there'
 * search result.
 */
string* query_default_search_description() {
   return ({
       "Funnily enough there is nothing interesting in the water.\n",
       "You search around and discover a whole bunch of water.\n",
       "You look up and down, left and right, then up and down again but "
       "all you can find is water.\n"
           });
}

/**
 * @ignore yes
 */
mixed *query_default_position(object ob) {
  return ({SWIMMING, "%^BOLD%^You start to swim.%^RESET%^\n",
           "$C$"+ob->one_short()+" "
           "$V$0=starts,start$V$ to swim.\n"});
} /* query_default_position() */

/**
 * @ignore yes
 */
int is_allowed_position(string poss) {
  if (poss == SWIMMING) {
    return 1;
  }
  return 0;
} /* is_allowed_position() */

/**
 * This function sets the position that non-living items will have when they
 * are lying on the bottom in this room.  The default is "lying on the bottom".
 * @param mess the new position for items lying on the bottom
 * @see query_bottom_mess
 * @example
 *   set_bottom_mess("lying in the seaweed");
 */
void set_bottom_mess(string mess) {
  on_bottom = mess;
}

/**
 * This function returns the position that non-living items will have when they
 * are lying on the bottom in this room.
 * @returns the current position for items lying on the bottom
 * @see set_bottom_mess
 */
string query_bottom_mess() {
  return on_bottom;
}

/**
 * This function sets the position that non-living items will have when they
 * are neither sinking nor floating in this room.  The default is "drifting
 * nearby".
 * @param mess the new position for drifting items
 * @see query_nonfloat_mess
 * @example
 *   set_nonfloat_mess("drifting amid the seaweed");
 */
void set_nonfloat_mess(string mess) {
   non_float = mess;
}

/**
 * This function returns the position that non-living items will have when they
 * are neither sinking nor floating in this room.
 * @return the current position for drifting items
 * @see set_nonfloat_mess
 */
string query_nonfloat_mess() {
  return non_float;
}

/**
 * This function sets the position that non-living items will have when they
 * are floating in this room.  The default is "floating nearby".
 * @param mess the new position for floating items
 * @see query_float_mess
 * @example
 *   set_float_mess("floating above the seaweed");
 */
void set_float_mess(string mess) {
  floating = mess;
}

/**
 * This function returns the position that non-living items will have when they
 * are floating in this room.
 * @return the current position for floating items
 * @see set_float_mess
 */
string query_float_mess() {
  return floating;
}

/**
 * This function sets the position that non-living items will have when they
 * are sinking in this room.  The default is "sinking nearby".
 * @param mess the new position for sinking items
 * @see query_sinking_mess
 * @example
 *   set_sinking_mess("sinking into the seaweed");
 */
void set_sinking_mess(string mess) {
  sinking = mess;
}

/**
 * This function returns the position that non-living items will have when they
 * are sinking in this room.
 * @return the current position for sinking items
 * @see set_sinking_mess
 */
string query_sinking_mess() {
  return sinking;
}

/**
 * This function sets the message that will be displayed when an object sinks
 * into this room.  The string "$down$" will be replaced by the current down
 * direction for this room (as set by set_down_dir), and the usual other
 * $-expansion for messages will occur, including the replacement of "$F" by
 * the direction from which they are arriving.  The default is "$N sink$s
 * $down$ from $F.".
 * @param mess the new entry message for sinking objects
 * @see query_sink_in_mess
 * @see set_down_dir
 * @see query_down_dir
 * @example
 *   set_sink_in_mess("$N sink$s into the cavern from $F.");
 */
void set_sink_in_mess(string mess) {
  s_in_mess = mess;
}

/**
 * This function returns the message that will be displayed when an object
 * sinks into this room, with the usual $-expansion not done.
 * @return the current entry message for sinking objects
 * @see set_sink_in_mess
 */
string query_sink_in_mess() {
  return replace(s_in_mess, ({"$down$", down_dir}));
}

/**
 * This function sets the message that will be displayed when an object sinks
 * out of this room.  The string "$down$" will be replaced by the current down
 * direction for this room (as set by set_down_dir), and the usual other
 * $-expansion for messages will occur.  The default is "$N sink$s $down$.".
 * @param mess the new exit message for sinking objects
 * @see query_sink_out_mess
 * @see set_down_dir
 * @see query_down_dir
 * @example
 *   set_sink_out_mess("$N sink$s $down$ out of the cavern.");
 */
void set_sink_out_mess(string mess) {
  s_out_mess = mess;
}

/**
 * This function returns the message that will be displayed when an object
 * sinks out of this room, with the usual $-expansion not done.
 * @return the current exit message for sinking objects
 * @see set_sink_out_mess
 */
string query_sink_out_mess() {
  return replace(s_out_mess, ({"$down$", down_dir}));;
}

/**
 * This function sets the message that will be displayed when an object floats
 * into this room.  The string "$up$" will be replaced by the current up
 * direction for this room (as set by set_up_dir), and the usual other
 * $-expansion for messages will occur, including the replacement of "$F" by
 * the direction from which they are arriving.  The default is "$N float$s $up$
 * from $F.".
 * @param mess the new entry message for floating objects
 * @see query_float_in_mess
 * @see set_up_dir
 * @see query_up_dir
 * @example
 *   set_float_in_mess("$N float$s into the cavern from $F.");
 */
void set_float_in_mess(string mess) {
  f_in_mess = mess;
}

/**
 * This function returns the message that will be displayed when an object
 * floats into this room, with the usual $-expansion not done.
 * @return the current entry message for floating objects
 * @see set_float_in_mess
 */
string query_float_in_mess() {
  return replace(f_in_mess, ({"$up$", up_dir}));
}

/**
 * This function sets the message that will be displayed when an object floats
 * out of this room.  The string "$up$" will be replaced by the current up
 * direction for this room (as set by set_up_dir), and the usual other
 * $-expansion for messages will occur.  The default is "$N float$s $up$.".
 * @param mess the new exit message for floating objects
 * @see query_float_out_mess
 * @see set_up_dir
 * @see query_up_dir
 * @example
 *   set_float_out_mess("$N float$s $up$ out of the cavern.");
 */
void set_float_out_mess(string mess) {
  f_out_mess = mess;
}

/**
 * This function returns the message that will be displayed when an object
 * floats out of this room, with the usual $-expansion not done.
 * @return the current entry message for floating objects
 * @see set_float_out_mess
 */
string query_float_out_mess() {
  return replace(f_out_mess, ({"$up$", up_dir}));
}

/**
 * This function sets the message that will be displayed when an object is
 * swept into this room by a current.  The usual $-expansion for messages
 * will occur.  The default is "$N $V$0=is,are$V$ swept in from $F by the
 * current.".
 * @param mess the new entry message for objects swept by a current
 * @see query_sweep_in_mess
 * @example
 *   set_sweep_in_mess("$N $V$0=is,are$V$ washed in from $F by the raging "
 *              "torrent.");
 */
void set_sweep_in_mess(string mess) {
  c_in_mess = mess;
}

/**
 * This function returns the message that will be displayed when an object
 * is swept into this room by a current, with the $-expansion not done.
 * @return the current entry message for objects swept by a current
 * @see set_sweep_in_mess
 */
string query_sweep_in_mess() {
  return c_in_mess;
}

/**
 * This function sets the message that will be displayed when an object is
 * swept out of this room by a current.  The usual $-expansion for messages
 * will occur, including the replacement of "$T" by the direction in which they
 * are moving.  The default is "$N $V$0=is,are$V$ swept $T by the current.".
 * @param mess the new exit message for objects swept by a current
 * @see query_sweep_out_mess
 * @example
 *   set_sweep_out_mess("$N $V$0=is,are$V$ washed away $T by the raging "
 *              "torrent.");
 */
void set_sweep_out_mess(string mess) {
  c_out_mess = mess;
}

/**
 * This function returns the message that will be displayed when an object
 * is swept out of this room by a current, with the $-expansion not done.
 * @return the current exit message for objects swept by a current
 * @see set_sweep_in_mess
 */
string query_sweep_out_mess() {
  return c_out_mess;
}

/**
 * This function sets the direction that is considered to be up by this room.
 * This is used by several exit messages, and by the water effect to decide
 * which way a panicking player will flee to try to reach the surface.  For
 * this reason, it should be set to the name of an exit which leads towards a
 * surface room.  Also, buoyant objects will be inclined to move in this
 * direction.  The default, of course, is "up".
 * @param mess the new direction to be considered to be up
 * @see query_up_dir
 * @see set_float_in_mess
 * @see set_float_out_mess
 * @see flee_drowning
 * @example
 *   set_up_dir("upwest");
 */
void set_up_dir(string dir) {
  up_dir = dir;
}

/**
 * This function returns the direction that is currently considered to be up in
 * this room.
 * @return the current direction considered to be up
 * @see set_up_dir
 */
string query_up_dir() {
  return up_dir;
}

/**
 * This function sets the direction that is considered to be down by this room.
 * This is used by several exit messages.  Also, dense objects will be inclined
 * to move in this direction.  The default, of course, is "down".
 * @param mes the new direction to be considered to be down
 * @see query_down_dir
 * @see set_sink_in_mess
 * @see set_sink_out_mess
 * @example
 *   set_down_dir("downeast");
 */
void set_down_dir(string dir) {
  down_dir = dir;
}

/**
 * This function returns the direction that is currently considered to be down
 * in this room.
 * @return the current direction considered to be down
 * @see set_down_dir
 */
string query_down_dir() {
  return down_dir;
}

/**
 * This function sets whether or not this room has a solid surface or bottom in
 * it.  If it does, then items may appear as being on the bottom, and living
 * objects are more able to resist currents (as they have something to hold on
 * to).  The parameter of this function can be 1 to make this room have a
 * bottom, 0 to make it have no bottom, or -1 (the default) to make it decide
 * whether or not it has a bottom by checking to see if it has any exits in the
 * current down direction (as set by set_down_dir).
 * @param val whether or not the room has a bottom
 * @see query_bottom
 * @see set_bottom_mess
 * @see set_down_dir
 */
void set_bottom(int val) {
  bottom = val;
  if (bottom > 1 || bottom < -1) {
    bottom = 1;
  }
}

/**
 * This function sets whether or not this room has an interface with air or
 * surface in it.  If it does, then living objects may breathe here, and
 * turbidity does not effect visibility.  The parameter of this function can be
 * 1 to make this room have a surface, 0 to make it have no surface, or -1 (the
 * default) to make it decide whether or not it has a surface by checking to
 * see if it has any exits in the current up direction (as set by set_up_dir).
 * @param val whether or not the room has a surface
 * @see query_surface
 * @see set_clarity
 * @see set_turbidity
 * @see set_up_dir
 */
void set_surface(int val) {
  surface = val;
  if (surface > 1 || surface < -1) {
    surface = 1;
  }
} /* set_surface() */


/**
 * This function sets the clarity of the water as an integer variable between 1
 * and 100.  The main effect of this is to reduce the light levels of the room
 * to simulate the obscuring effect of turbid water.  The default clarity is
 * 90.
 * @param how_clear the new clarity of the water
 * @see query_clarity
 * @see set_turbidity
 */
void set_clarity(int how_clear) {
  clarity = how_clear;
  if (clarity > 100) {
    clarity = 100;
  }
  else if (clarity < 0) {
    clarity = 0;
  }
  if (!query_property("dark mess")) {
    if (clarity < 20) {
      add_property("dark mess", "The water here is very murky.");
    }
    else if (clarity < 50) {
      add_property("dark mess", "The water here is quite murky.");
    }
    else if (clarity < 80) {
      add_property("dark mess", "The water here is slightly murky.");
    }
  }
}

/**
 * This function returns the current clarity of the water, which is an integer
 * variable between 1 and 100.
 * @return the current clarity of the water
 * @see set_clarity
 * @see query_turbidity
 */
int query_clarity() {
  return clarity;
}

/**
 * This function is an alternative method of setting the clarity of the water.
 * The turbidity is defined as 100 minus the clarity of the water.  The default
 * turbidity is 10.
 * @param how_murky the new turbidity of the water
 * @see set_clarity
 * @see query_turbidity
 */
void set_turbidity(int how_murky) {
  set_clarity(100 - how_murky);
}

/**
 * This function returns the current turbidity of the water, which is an
 * integer variable between 1 and 100.  It will be equal to 100 minus the
 * current clarity of the water.
 * @return the current turbidity of the water
 * @see set_turbidity
 * @see query_clarity
 */
int query_turbidity() {
  return 100 - query_clarity();
}

/**
 * This function sets the salinity of the water as an integer variable between
 * 0 and 100.  Higher salinity will have a small positive effect on the
 * buoyancy of objects in the room.  The default salinity is 0.
 * @param how_salty the new salinity of the water
 * @see query_salinity
 * @see calc_buoyancy
 */
void set_salinity(int how_salty) {
  salinity = how_salty;
  if (salinity > 100) {
    salinity = 100;
  }
  else if (salinity < 0) {
    salinity = 0;
  }
}

/**
 * This function returns the current salinity of the water, which is an integer
 * variable between 0 and 100.
 * @return the current salinity of the water
 * @see set_salinity
 */
int query_salinity() {
  return salinity;
}

/**
 * This function sets the turbulence of the water as a non-negative integer
 * variable.  A random number up to the turbulence is added to the difficulty
 * of all skillchecks to leave the room via a water exit with swim_exit as the
 * exit function, except when the exiting object is not moving of its own
 * accord (such as when it is being swept along by a current).  The default
 * turbulence is 100.
 * @param how_turbulent the new turbulence of the water
 * @see query_turbulence
 * @see swim_exit
 */
void set_turbulence(int how_turbulent) {
  turbulence = how_turbulent;
  if (turbulence < 0) {
    turbulence = 0;
  }
}

/**
 * This function returns the current turbulence of the water, which is a
 * non-negative integer.
 * @return the current turbulence of the water
 * @see set_turbulence
 */
int query_turbulence() {
  return turbulence;
}

/**
 * This function sets whether the room will use the light levels of the
 * surface to determine its own.  If the function is called with a non-zero
 * value, the the current light level in the room will be overridden by a new
 * value based on the light of the room found by query_above_room.  If the room
 * above is an outside room, with light levels that depend on the time of day,
 * then the light in this room will be updated every time it is queried.  A
 * water room will default to using this option, but it will be overridden by
 * any calls to adjust_light (including calls to set_light).  It is possible to
 * call set_water_light to avoid this, but it should not be necessary.  If a
 * series of rooms with vertical exits between them are all set to use surface
 * light, then the effect will be for the light of the room at the top of the
 * stack to filter down through the rest, appropriately attenuated by
 * turbidity.
 * @param val whether the room should use light from the surface
 * @see query_above_room
 * @see query_water_surface_light
 * @see update_water_light
 * @see set_water_light
 * @see set_turbidity
 * @see set_clarity
 */
void set_use_surface_light(int val) {
  int surface_light;
  object above;
  update_light = 0;
  if (!val) {
    return;
  }
  above = query_above_room();
  if (!above) {
    set_water_light(0);
    return;
  }
  if (function_exists("query_day_light", above) ||
           above->water_surface_light_varies()) {
    update_light = 1;
    return;
  }
  if (function_exists("query_water_surface_light", above)) {
    surface_light = above->query_water_surface_light();
  }
  else {
    surface_light = above->query_my_light();
  }
  set_water_light(surface_light);
}


/**
 * This function returns the amount of light that will filter down to rooms
 * below this one that have had set_use_surface_light called in them.  Its
 * default is to return a value based on the light in this room and the clarity
 * of the water.
 * @return the amount of light that filters down to rooms below this one
 * @see set_use_surface_light
 * @see set_turbidity
 * @see set_clarity
 */
int query_water_surface_light() {
  update_water_light();
  return query_my_light()*query_clarity()/100;
}

/**
 * This function returns the amount of light that will filter down to this
 * room from those above it if set_use_surface_light has been called.  It is
 * intended to be used only by the update_water_light function, but has been
 * separated out to allow it to be masked.
 * @return the amount of light that filters down from rooms above this one
 * @see update_water_light
 * @see query_above_room
 * @see query_water_surface_light
 */
int get_water_surface_light() {
  object above = query_above_room();
  if (!above) {
    return 0;
  }
  if (function_exists("query_water_surface_light", above)) {
    return above->query_water_surface_light();
  }
  return above->query_my_light();
}

/**
 * This function updates the amount of light filtering down to this room from
 * the rooms above it, if it is necessary to do so.  It is called by
 * query_light in /std/room/water_inside and /std/room/water_outside.
 * @see set_use_surface_light
 * @see set_light
 * @see query_water_surface_light
 * @see get_water_surface_light
 */
void update_water_light() {
  if (!update_light) {
    return;
  }
  if (!light_first_queried) {
    set_use_surface_light(1);
    light_first_queried = 1;
    update_water_light();
    return;
  }
  set_water_light(get_water_surface_light());
}

/**
 * This function adds a water current flowing through a particular exit, which
 * may sweep objects through it or make it harder for them to swim through.
 * The second argument is the rate of flow.  If this is positive, then water
 * will be flowing from this room into the next one.  If it is positive, then
 * water will be flowing from the next room into this one.  It is up to the
 * coder of the specific rooms to ensure that the currents in different rooms
 * match one another.
 * @param dir the exit through which the current is flowing
 * @param rate the strength of the current
 * @see delete_flow
 * @see query_flow
 * @see query_flows
 * @see drift
 * @see swim_exit
 * @example
 *   // There is a current flowing south.
 *   add_flow("north", -80);
 *   add_flow("south", 80);
 */
void add_flow(string dir, int rate) {
  flows[dir] = rate;
}

/**
 * This function removes a water current through a particular exit.
 * @param dir the exit for which any water current should be removed
 * @see add_flow
 * @see query_flow
 * @see query_flows
 */
void delete_flow(string dir) {
  map_delete(flows, dir);
}

/**
 * This function returns a mapping of all the current flows through exits in
 * this room.  The keys of the mapping are the exits through which the currents
 * flow, and the values are the rates of flow.
 * @return a mapping of the current flows through exits in this room
 * @see add_flow
 * @see delete_flow
 * @see query_flow
 */
mapping query_flows() {
  return copy(flows);
}

/**
 * This function returns the strength of the current flowing through a
 * particular exit, if any.  A positive value represents a current flowing from
 * this room into the next one, and a negative value represents a current
 * flowing from the next room into this one.
 * @param dir the exit which should have its current returned
 * @return the current through this ext
 * @see add_flow
 * @see delete_flow
 * @see query_flows
 */
int query_flow(string dir) {
  return flows[dir];
}

/**
 * This function returns the bonus that objects get to move along the bottom or
 * to resist such movement here.  If this should be anything unusual, such as
 * for a very smooth bottom or one with handles, this function should be masked
 * to return something different.
 * @param thing the object that is moving along the bottom
 * @param buoyancy the object's relative buoyancy
 * @return the traction bonus on the bottom here
 * @see swim_exit
 * @see drift
 * @see calc_buoyancy
 */
int query_water_traction_bonus(object thing, int buoyancy) {
  if (buoyancy < 0) {
    buoyancy = -buoyancy;
    return buoyancy/3 + random(buoyancy/3);
  }
  return 0;
}

/**
 * This function returns the room above this one, in the direction set by
 * set_up_dir.
 * @return the room above this one
 * @see set_up_dir
 * @see query_surface_room
 */
object query_above_room() {
  string destination = query_destination(up_dir);
  if (!destination) {
    return 0;
  }
  return load_object(destination);
}

/**
 * This function returns the room below this one, in the direction set by
 * set_down_dir.
 * @return the room below this one
 * @see set_down_dir
 * @see query_bottom_room
 */
object query_below_room() {
  string destination = query_destination(down_dir);
  if (!destination) {
    return 0;
  }
  return load_object(destination);
}

/**
 * This function returns the top room in a vertical stack of water rooms.  If
 * there is a surface water room, it is returned, else, if the top room is not
 * water, or there is no surface room, 0 is returned.
 * @return the first room above this one to be on the surface
 * @see query_above_room
 * @see query_surface
 */
object query_surface_room() {
  object next;
  if (query_surface()) {
    return this_object();
  }
  next = query_above_room();
  if (next && next->query_water()) {
    return next->query_surface_room();
  }
  return 0;
}

/**
 * This function returns the bottom room in a vertical stack of water rooms.
 * If there is a bottom water room, it is returned, else, if the bottom room is
 * not water, or there is no bottom room, 0 is returned.
 * @return the first room below this one to be on the bottom
 * @see query_below_room
 * @see query_bottom
 */
object query_bottom_room() {
  object next;
  if (query_bottom()) {
    return this_object();
  }
  next = query_below_room();
  if (next && next->query_water()) {
    return next->query_bottom_room();
  }
  return 0;
}

/**
 * @ignore yes
 */
void event_enter(object ob, string mess, object from)  {
  int effnum, buoyancy, *effects, *args;
  effects = ob->effects_matching(SWIM_EFFECT->query_classification());
 


  if (!effects || !sizeof(effects)) {
    if (from && !from->query_water() ) {
      if( !lives_in_water( ob ) ) 
        ob->add_effect(SWIM_EFFECT, 2);
    }
  }

  //Some of the code in this if relies on the water effect, some other code 
  //adds the water effect, either way, we don't want it for things that live in
  //water.
  if ( !lives_in_water( ob ) ) {
      effnum = get_swim_enum(ob);
      args = ob->arg_of(effnum);
      if (living(ob) && query_surface() && !ob->query_property("dead") && from &&
               from->query_water() && !from->query_surface()) {
        if (args[1] < 150 || ob->query_property(GILLS_PROP)) {
          tell_object(ob, "You break the surface.\n");
        }
        else {
          tell_object(ob, "You break the surface and take a deep breath.\n");
        }
        if (args[1] != 0) {
          ob->set_arg_of(effnum, ({args[0], 0}));
        }
      }
  }

  if (!living(ob)) {
    buoyancy = SWIM_EFFECT->calc_buoyancy(ob);
    if (buoyancy < 0 && query_bottom()) {
      ob->add_property("there", on_bottom);
    }
    else if (buoyancy >= 0 && (buoyancy > ob->query_property(ANCHOR_PROP)
           || query_surface())) {
      ob->add_property("there", floating);
    }
    else if (buoyancy < 0 && buoyancy < -ob->query_property(ANCHOR_PROP)) {
      ob->add_property("there", sinking);
    }
    else {
      ob->add_property("there", non_float);
    }
  } else {
    ob->return_to_default_position();
  }
}

/**
 * This function makes things wet.  Anything entering a water room has this
 * function called on it by event_enter, and will have the wetness effect added
 * to it if appropriate, as well as to any appropriate objects inside it if it
 * isn't waterproof.  Open containers will also be filled with water.  The
 * function checks to see that the object is indeed inside the room, unless the
 * optional extra argument is non-zero.
 * @param ob the object to be soaked
 * @param ignore_location whether the object should be soaked wherever it is
 * @see get_water
 */
varargs void soak(object ob, int ignore_location) {
  int wetness = 0, *effects;
  object env, water, *things;
  env = ob;
  if (!ignore_location) {
    while (env && env = environment(env)) {
      if (env == this_object()) {
        break;
      }
      if ((env->query_closed() && env->query_waterproof()) ||
           env->query_dry_cargo()) {
        env = 0;
        break;
      }
    }
  }
  if (!env) {
    return;
  }
  if (ob->query_property(FLOATING_PROP) && query_surface()) {
    return;
  }
  if (living(ob)) {
    if (ob->query_property("dead") || lives_in_water( ob ) ) {
      return;
    }
    effects = ob->effects_matching(LIVING_WET_EFFECT->query_classification());
    if (effects && sizeof(effects)) {
      wetness = ob->arg_of(effects[0]);
    }
    wetness = ob->query_weight() - wetness;
    if (wetness > 0) {
      ob->add_effect(LIVING_WET_EFFECT, wetness);
    }
  }
  else if (ob->id("towel")) {
    effects = ob->effects_matching(OBJECT_WET_EFFECT->query_classification());
    if (effects && sizeof(effects)) {
      wetness = ob->arg_of(effects[0]);
    }
    wetness = 200*ob->query_weight() - wetness;
    if (wetness > 0) {
      ob->add_effect(OBJECT_WET_EFFECT, wetness);
    }
  }
  if (ob->query_max_volume() && !ob->query_closed() &&
           ob->query_max_volume() - ob->query_volume() > 0) {
    water = get_water();
    water->set_amount(ob->query_max_volume() - ob->query_volume());
    water->move(ob);
  }
  if (!(ob->query_closed() && ob->query_waterproof()) &&
           !ob->query_dry_cargo()) {
    things = all_inventory(ob);

    if (things && sizeof(things)) {
      map(things, (: soak($1, 1) :));
    }
  }
}

/**
 * This function returns some appropriate water from the room.  Its appearance
 * will depend on the clarity and salinity set in the room.  The quantity of
 * the water object that it returns is not fixed, and will generally be set by
 * whatever function called it.
 * @return some water from the room
 * @see soak
 * @see set_clarity
 * @see set_turbidity
 * @see set_salinity
 */
object get_water() {
  object water = ARMOURY->request_item("water");
  switch (query_clarity()) {
  case 0..20:
    water->set_short("very muddy water");
    water->add_adjective(({"very", "muddy"}));
    water->set_long("This is $amount_size$ of very muddy water.\n");
    return water;
  case 21..50:
    water->set_short("muddy water");
    water->add_adjective("muddy");
    water->set_long("This is $amount_size$ of muddy water.\n");
    return water;
  case 51..80:
    water->set_short("slightly muddy water");
    water->add_adjective(({"slightly", "muddy"}));
    water->set_long("This is $amount_size$ of slightly muddy water.\n");
  }
  switch (query_salinity()) {
  case 51..100:
    water->set_short("brine");
    water->add_alias("brine");
    water->set_long("This is $amount_size$ of very salty water.\n");
    return water;
  case 11..50:
    water->set_short("salty water");
    water->add_adjective("salty");
    water->set_long("This is $amount_size$ of salty water.\n");
  }
  return water;
}

/**
 * This is an exit function set for any exits in a water room.  It finds the
 * difficulty of swimming through the exit, on the basis of the current through
 * that exit, the buoyancy of the object and the extra difficulty that it has
 * in swimming.  The swimming/immersion effect calculates the latter two with
 * calc_buoyancy and swim_difficulty respectively.  The object is subjected to
 * a skillcheck in other.movement.swimming to see if it can move through the
 * exit.
 * There is a guildpoint cost equal to 1/20th of the difficulty of the
 * skillcheck.
 * @param dir the direction in which the object is leaving
 * @param ob the object that is trying to leave
 * @param mess a weird extra string that isn't relevant here
 * @return whether or not the object can move through the exit
 * @see modify_exit
 * @see exit_function
 * @see calc_buoyancy
 * @see swim_difficulty
 * @see query_water_traction_bonus
 * @see perform_task
 */
int swim_exit(string dir, object ob, string mess) {
  int difficulty, buoyancy = 0, place, this_turb, gp_cost, effnum, *arg;
  string *places_to_go, *tm_messes;
  object destination;

  if (!living(ob) || ob->query_property("dead") || lives_in_water( ob ) ) {
    return 1;
  }

  effnum = get_swim_enum(ob);
  arg = ob->arg_of(effnum);
  if (-2 == arg[0]) {
    // If you're not moving of your own accord, assume that it's already been
    // checked that you can pass through the exit.
    return 1;
  }
  difficulty = -flows[dir];
  buoyancy = SWIM_EFFECT->calc_buoyancy(ob);
  if (ob->query_weight()) {
    buoyancy /= ob->query_weight();
  }
  places_to_go = query_dest_dir();
  place = member_array(dir, places_to_go);
  if (-1 == place || !(destination = load_object(places_to_go[place+1]))) {
    return 0;
  }
  if (dir == up_dir) {
    // It's harder to go up if you're not buoyant.
    difficulty -= buoyancy + 50;
  } else if (dir == down_dir) {
    // It's harder to go down if you're buoyant.
    difficulty += buoyancy - 50;
  } else if (buoyancy < 0) {
    // It's harder to swim if you keep sinking.
    difficulty -= buoyancy/5;
  } else {
    // It's harder to swim if you keep floating.
    difficulty += buoyancy/5;
  }
  if (query_bottom() && (!destination->query_water() ||
           destination->query_bottom())) {
    // It's easier to walk along the bottom if you're not buoyant.
    difficulty -= query_water_traction_bonus(ob, buoyancy);
  } 
  this_turb = random(query_turbulence());
  difficulty += this_turb;
  if (arg[0] == -1) {
    // You get a bonus if you're desperately trying to reach the surface.
    difficulty -= 50;
  }
  if (!destination->query_water()) {
    // It's easier if you can pull yourself ashore.
    difficulty -= 50;
  }
  difficulty *= SWIM_EFFECT->swim_difficulty(ob);
  difficulty /= 100;
  if (difficulty <= 0) {
    return 1;
  }
  if (arg[0] != -1) {
    gp_cost = difficulty/20;
    if (gp_cost > 50) {
      gp_cost = 50;
    }
    else if (gp_cost < 1) {
      gp_cost = 1;
    }
    if (ob->query_specific_gp("other") < gp_cost) {
      tell_object(ob, "You're too "+({"fatigued", "tired", "weary",
           "exhausted"})[random(4)]+" to swim "+dir+" at the moment.\n");
      notify_fail("");
      return 0;
    }
  }
  ob->adjust_gp(-gp_cost);
  switch (TASKER->perform_task(ob, SWIMMING_SKILL, difficulty,
           TM_CONTINUOUS)) {
  case AWARD:
    tm_messes = ({"You move more surely as you glide through the water.",
                  "You discover a more efficient stroke.",
                  "You find a better way to streamline your body.",
                  "You find a more efficient swimming rhythm.",
                  "You begin to move more confidently through the water."});
    tell_object(ob, "%^YELLOW%^"+tm_messes[random(sizeof(tm_messes))]+
           "%^RESET%^\n");
  case SUCCEED:
    return 1;
  }
  notify_fail("");
  if (dir == up_dir && buoyancy < -50) {
    tell_object(ob, "You struggle to leave "+up_dir+" but, with the load "
           "you're carrying, you can't make any headway.\n");
    tell_room(this_object(), "$C$"+ob->one_short()+" "
           "$V$0=struggles,struggle$V$ to leave "+up_dir+", but can't make "
           "any headway.\n", ob);
    return 0;
  }
  if (dir == down_dir && buoyancy > 50) {
    tell_object(ob, "You struggle to leave "+down_dir+" but, with your "
           "buoyancy, you can't make any headway.\n");
    tell_room(this_object(), "$C$"+ob->one_short()+" "
           "$V$0=struggles,struggle$V$ to leave "+down_dir+", but can't make "
           "any headway.\n", ob);
    return 0;
  }
  if (flows[dir] < 0) {
    tell_object(ob, "You struggle to leave "+dir+", but you can't make any "
           "headway against the current.\n");
    tell_room(this_object(), "$C$"+ob->one_short()+" "
           "$V$0=struggles,struggle$V$ to leave "+dir+", but can't make any "
           "headway against the current.\n", ob);
    return 0;
  }
  if (this_turb > 0) {
    tell_object(ob, "You struggle to leave "+dir+", but you can't make any "
           "headway in the turbulent waters.\n");
    tell_room(this_object(), "$C$"+ob->one_short()+" "
           "$V$0=struggles,struggle$V$ to leave "+dir+", but can't make any "
           "headway.\n", ob);
    return 0;
  }
  tell_object(ob, "You struggle to leave "+dir+", but you can't make any "
           "headway.\n");
  tell_room(this_object(), "$C$"+ob->one_short()+" $V$0=struggles,struggle$V$ "
           "to leave "+dir+", but can't make any headway.\n", ob);
  return 0;
}

/**
 * With this function here, creators will be able to point and laugh when
 * someone dies by drowning in a water room because they'll see the death
 * reason in an inform.  It will also show up in the death log.  It would be a
 * good idea to mask this function to return a reason more specific to the
 * place where the drowning may occur (such as "drowning in the Djel"), or at
 * least a humorous one (such as "failing to realise that scuba gear hasn't
 * been invented yet") so that creators will have something to laugh at.
 * @return the reason for death
 * @see do_death
 */
string query_death_reason() {
  return "drowning";
}

/**
 * @ignore yes
 */
int add_exit(string direc, mixed dest, string type) {
    mixed *messy = query_dest_other(direc);
  
    if (messy && arrayp(messy[ROOM_ENTER]) && sizeof(messy[ROOM_ENTER]) == 2){
        origins[direc] = messy[ROOM_ENTER][1];
    }
    modify_exit(direc, ({"function", "swim_exit",
                       "exit mess", (: get_exit_mess($1, $(direc)) :),
                       "enter mess", (: get_enter_mess($1, $(direc)) :)}));

    // We can't bury objects here if they can sink
    if( direc == "down" ) {
        add_property("no burial", 1);
    }

}

/**
 * This function returns the opposite to the direction of a particular exit.
 * This information is normally only accessible within the room handler, but it
 * is cached here in the origins mapping by the add_exit in this file.  If no
 * value is found, "elsewhere" will be returned.  This value is used in exit
 * messages to correctly display where they are coming from.
 * @param dir the exit for which the opposite direction should be found
 * @return the opposite of the specified direction
 * @see add_exit
 * @see room_handler
 */
string query_origin(string dir) {
  if (origins[dir]) {
    return origins[dir];
  }
  return "elsewhere";
}

/**
 * This function sets the next exit message from this room for a particular
 * object.  It is used automagically by the swim_exit function, but can be used
 * for other purposes if you feel like it.
 * @param ob the object for which the next exit message should be set
 * @param mess the next exit message for the object
 * @see get_exit_mess
 * @see add_enter_mess
 * @see get_enter_mess
 * @see swim_exit
 */
void add_exit_mess(object ob, string mess) {
  exit_messes[file_name(ob)] = mess;
}

/**
 * This function sets the next entry message from this room for a particular
 * object.  It is used automagically by the swim_exit function, but can be used
 * for other purposes if you feel like it.
 * @param ob the object for which the next entry message should be set
 * @param mess the next entry message for the object
 * @see get_enter_mess
 * @see add_exit_mess
 * @see get_exit_mess
 * @see swim_exit
 */
void add_enter_mess(object ob, string mess) {
  enter_messes[file_name(ob)] = mess;
}

/**
 * This function returns the appropriate exit message for the specified object
 * in the specified direction.  If a value has been set by add_exit_mess then
 * it is returned.  It is used automagically by the swim_exit function, but can
 * be overridden if you feel like it.
 * @param ob the object for which the exit message should be found
 * @param direc the direction in which the object is exiting
 * @return the exit message for this object
 * @see add_exit_mess
 * @see add_enter_mess
 * @see get_enter_mess
 */
string get_exit_mess(object ob, string direc) {
  string retval;
  if (retval = exit_messes[file_name(ob)]) {
    map_delete(exit_messes, file_name(ob));
    return retval;
  }
  return "$N $V$0=swims,swim$V$ $T.";
}

/**
 * This function returns the appropriate entry message for the specified object
 * in the specified direction.  If a value has been set by add_enter_mess then
 * it is returned.  The query_origin function is used to find a replacement for
 * the "$F" token.  It is used automagically by the swim_exit function, but can
 * be overridden if you feel like it.
 * @param ob the object for which the entry message should be found
 * @param direc the direction in which the object is exiting
 * @return the entry message for this object
 * @see add_enter_mess
 * @see add_exit_mess
 * @see get_exit_mess
 * @see query_origin
 */
string get_enter_mess(object ob, string direc) {
  string retval;
  if (retval = enter_messes[file_name(ob)]) {
    map_delete(enter_messes, file_name(ob));
  }
  else if (direc == up_dir) {
    retval = "$N $V$0=swims,swim$V$ up from $F.";
  }
  else if (direc == down_dir) {
    retval = "$N $V$0=swims,swim$V$ down from $F.";
  }
  else {
    retval = "$N $V$0=swims,swim$V$ in from $F.";
  }
  retval = replace(retval, "$F", query_origin(direc));
  return retval;
}

/**
 * @ignore yes
 */
void init()  {
  add_command("float", "", (: do_float() :));
  add_command("drift", "", (: do_drift() :));
  add_command("swim", "", (: do_swim() :));
}

/**
 * @ignore yes
 */
string mangle_speech(string type, string words, mixed target) {
  int drown;
  string garbled = "";
  if (query_surface() || !this_player() ||
           this_player()->query_property("dead")) {
    return words;
  }
  switch (type) {
  case "whisper":
    drown = 20 + random(20);
    break;
  case "lsay":
  case "mock":
    drown = 80 + random(80);
    break;
  case "shout":
    drown = 120 + random(120);
    break;
  default:
    drown = 40 + random(40);
  }
  for (int inc = (strlen(words) / 10) + 1;inc > 0;--inc) {
    garbled += ({"blub", "glub", "gloog", "arrrble"})[random(4)]+" ... ";
  }
  if (drown > 70) {
    garbled = garbled[0..(strlen(garbled) - 6)]+"!";
  } else {
    garbled = garbled[0..(strlen(garbled) - 2)];
  }
  garbled = capitalize(garbled);
  last_speech_volume = drown;
  return garbled;
}

/**
 * @ignore yes
 */
void event_person_say(object ob, string start, string mess, string lang,
           string accent) {
  int effnum, *args;
  if (!ob || ob->query_property("dead") || ob->query_property(GILLS_PROP) ||
      lives_in_water( ob ) || environment(ob) != this_object() || query_surface()) {
    last_speech_volume = 0;
    return;
  }

  if (!last_speech_volume) {
    last_speech_volume = 40 + random(40);
  }
  effnum = get_swim_enum(ob);
  args = ob->arg_of(effnum);
  args[1] += last_speech_volume;
  ob->set_arg_of(effnum, args);
  ob->adjust_tmp_con(-random((last_speech_volume / 50) + 1));
  switch (last_speech_volume) {
  case 0..30:
    tell_object(ob, "You inhale a bit of water.\n");
    break;
  case 31..80:
    tell_object(ob, "You inhale some water.\n");
    break;
  case 81..150:
    tell_object(ob, "You inhale a fair amount of water.\n");
    break;
  default:
    tell_object(ob, "You inhale about a lungful of water.\n");
  }
  last_speech_volume = 0;
}

/**
 * This function is for the float command, which will allow someone to start
 * floating freely rather than swimming.  It is identical to the drift command,
 * except for the messages.
 * @return whether the command succeeded
 * @see init
 * @see do_drift
 * @see do_swim
 */
int do_float()  {
  int *args, effnum = get_swim_enum(this_player());
  args = this_player()->arg_of(effnum);
  if (!args[0]) {
    if (sizeof(filter(query_flows(), (: $2 :)))) {
      add_failed_mess("You are already floating on the current.\n");
    }
    else {
      add_failed_mess("You are already floating freely.\n");
    }
    return 0;
  }
  this_player()->set_arg_of(effnum, ({0, args[1]}));
  if (sizeof(filter(query_flows(), (: $2 :)))) {
    add_succeeded_mess(({"You begin to float freely on the current.\n", ""}));
  }
  else {
    add_succeeded_mess(({"You begin to float freely.\n", ""}));
  }
  return 1;
}

/**
 * This function is for the drift command, which will allow someone to start
 * drifting freely rather than swimming.  It is identical to the float command,
 * except for the messages.
 * @return whether the command succeeded
 * @see init
 * @see do_float
 * @see do_swim
 */
int do_drift()  {
  int *args, effnum = get_swim_enum(this_player());
  args = this_player()->arg_of(effnum);
  if (!args[0]) {
    if (sizeof(filter(query_flows(), (: $2 :)))) {
      add_failed_mess("You are already drifting with the current.\n");
    }
    else {
      add_failed_mess("You are already drifting freely.\n");
    }
    return 0;
  }
  this_player()->set_arg_of(effnum, ({0, args[1]}));
  if (sizeof(filter(query_flows(), (: $2 :)))) {
    add_succeeded_mess(({"You begin to drift freely with the current.\n",
           ""}));
  }
  else {
    add_succeeded_mess(({"You begin to drift freely.\n", ""}));
  }
  return 1;
}

/**
 * This function is for the swim command, which will allow someone to stop
 * drifting freely and start swimming and resisting anything that tries to move
 * them.
 * @return whether the command succeeded
 * @see init
 * @see do_float
 * @see do_drift
 */
int do_swim()  {
  int *args, effnum = get_swim_enum(this_player());
  args = this_player()->arg_of(effnum);
  if (args[0]) {
    if (sizeof(filter(query_flows(), (: $2 :)))) {
      add_failed_mess("You are already swimming against the current.\n");
    }
    else {
      add_failed_mess("You are already swimming.\n");
    }
    return 0;
  }
  this_player()->set_arg_of(effnum, ({1, args[1]}));
  if (sizeof(filter(query_flows(), (: $2 :)))) {
    add_succeeded_mess(({"You begin to swim against the current.\n", ""}));
  }
  else {
    add_succeeded_mess(({"You begin to swim.\n", ""}));
  }
  return 1;
}

/**
 * This function returns the effect number of the swimming/immersion effect on
 * the object specified.  If there isn't one, it adds the effect.
 * @param thing the object for which the swimming effect number should be found
 * @return the swimming effect number
 * @see effects_matching
 * @see query_classification
 */
int get_swim_enum(object thing) {
  int *effects = thing->effects_matching(SWIM_EFFECT->query_classification());
  if (!effects || !sizeof(effects)) {
    thing->add_effect(SWIM_EFFECT, 1);
    effects = thing->effects_matching(SWIM_EFFECT->query_classification());
    call_out("soak", 1, thing);
  } else if (!random(50)) {
    call_out("soak", 1, thing);
  }
  return effects[0];
}

/**
 * This function returns 1 if this is a bottom room, and 0 otherwise.
 * @return whether this is a bottom room
 * @see set_bottom
 */
int query_bottom()  {
  if (bottom == -1) {
    return member_array(down_dir, query_exits()) == -1;
  }
  return bottom;
}

/**
 * This function returns 1 if this is a surface room, and 0 otherwise.
 * @return whether this is a surface room
 * @see set_surface
 * @see query_underwater
 */
int query_surface() {
    if (surface == -1) {
        return member_array(up_dir, query_exits()) == -1;
    }
    return surface;
}

/**
 * This function returns 1 if this room is underwater (that is, it is not a
 * surface room), and 0 otherwise.
 * @return whether this is an underwater room
 * @see query_surface
 * @see set_surface
 */
int query_underwater() {
  return !query_surface();
}

/**
 * This function returns 1 to indicate that this is a water room.  It fulfills
 * the same purpose as the inherits efun in this case, but may be slightly
 * easier to use.
 * @return 1
 * @see inherits
 */
int query_water()  {
  return 1;
}

int lives_in_water( object ob ) {
  string race_ob = ob->query_race_ob();

  //Corpses don't live in water.
  if ( !living( ob ) ) {
    return 0;
  }

  if(ob->query_property(LIVES_IN_WATER_PROP) ||
     ob->lives_in_water() ||
     (race_ob && race_ob->lives_in_water()))
    return 1;
  return 0;
}

// The colour this room should use for the terrain map.
string query_terrain_map_colour() { return "%^CYAN%^"; }