dw_fluffos_v2/
dw_fluffos_v2/fluffos-2.9-ds2.05/
dw_fluffos_v2/fluffos-2.9-ds2.05/ChangeLog.old/
dw_fluffos_v2/fluffos-2.9-ds2.05/Win32/
dw_fluffos_v2/fluffos-2.9-ds2.05/compat/
dw_fluffos_v2/fluffos-2.9-ds2.05/compat/simuls/
dw_fluffos_v2/fluffos-2.9-ds2.05/include/
dw_fluffos_v2/fluffos-2.9-ds2.05/testsuite/
dw_fluffos_v2/fluffos-2.9-ds2.05/testsuite/clone/
dw_fluffos_v2/fluffos-2.9-ds2.05/testsuite/command/
dw_fluffos_v2/fluffos-2.9-ds2.05/testsuite/data/
dw_fluffos_v2/fluffos-2.9-ds2.05/testsuite/etc/
dw_fluffos_v2/fluffos-2.9-ds2.05/testsuite/include/
dw_fluffos_v2/fluffos-2.9-ds2.05/testsuite/inherit/
dw_fluffos_v2/fluffos-2.9-ds2.05/testsuite/inherit/master/
dw_fluffos_v2/fluffos-2.9-ds2.05/testsuite/log/
dw_fluffos_v2/fluffos-2.9-ds2.05/testsuite/single/
dw_fluffos_v2/fluffos-2.9-ds2.05/testsuite/single/tests/compiler/
dw_fluffos_v2/fluffos-2.9-ds2.05/testsuite/single/tests/efuns/
dw_fluffos_v2/fluffos-2.9-ds2.05/testsuite/single/tests/operators/
dw_fluffos_v2/fluffos-2.9-ds2.05/testsuite/u/
dw_fluffos_v2/fluffos-2.9-ds2.05/tmp/
dw_fluffos_v2/fluffos-2.9-ds2.05/windows/
dw_fluffos_v2/lib/
dw_fluffos_v2/lib/binaries/cmds/
dw_fluffos_v2/lib/binaries/cmds/creator/
dw_fluffos_v2/lib/binaries/cmds/living/
dw_fluffos_v2/lib/binaries/cmds/player/
dw_fluffos_v2/lib/binaries/d/admin/obj/
dw_fluffos_v2/lib/binaries/d/liaison/
dw_fluffos_v2/lib/binaries/global/virtual/
dw_fluffos_v2/lib/binaries/global/virtual/setup_compiler/
dw_fluffos_v2/lib/binaries/obj/handlers/autodoc/
dw_fluffos_v2/lib/binaries/obj/handlers/terrain_things/
dw_fluffos_v2/lib/binaries/obj/misc/
dw_fluffos_v2/lib/binaries/obj/misc/buckets/
dw_fluffos_v2/lib/binaries/obj/monster/
dw_fluffos_v2/lib/binaries/obj/reactions/
dw_fluffos_v2/lib/binaries/obj/reagents/
dw_fluffos_v2/lib/binaries/secure/cmds/creator/
dw_fluffos_v2/lib/binaries/secure/master/
dw_fluffos_v2/lib/binaries/std/
dw_fluffos_v2/lib/binaries/std/dom/
dw_fluffos_v2/lib/binaries/std/effects/object/
dw_fluffos_v2/lib/binaries/std/guilds/
dw_fluffos_v2/lib/binaries/std/languages/
dw_fluffos_v2/lib/binaries/std/races/
dw_fluffos_v2/lib/binaries/std/room/
dw_fluffos_v2/lib/binaries/std/room/basic/
dw_fluffos_v2/lib/binaries/std/shops/
dw_fluffos_v2/lib/binaries/std/shops/inherit/
dw_fluffos_v2/lib/binaries/www/
dw_fluffos_v2/lib/cmds/guild-race/
dw_fluffos_v2/lib/cmds/guild-race/crafts/
dw_fluffos_v2/lib/cmds/guild-race/other/
dw_fluffos_v2/lib/cmds/playtester/
dw_fluffos_v2/lib/cmds/playtester/senior/
dw_fluffos_v2/lib/d/admin/
dw_fluffos_v2/lib/d/admin/log/
dw_fluffos_v2/lib/d/admin/mapper/31-10-01/mapmaker/event/
dw_fluffos_v2/lib/d/admin/meetings/
dw_fluffos_v2/lib/d/admin/obj/
dw_fluffos_v2/lib/d/admin/room/we_care/
dw_fluffos_v2/lib/d/admin/save/
dw_fluffos_v2/lib/d/dist/
dw_fluffos_v2/lib/d/dist/mtf/
dw_fluffos_v2/lib/d/dist/pumpkin/
dw_fluffos_v2/lib/d/dist/pumpkin/chars/
dw_fluffos_v2/lib/d/dist/pumpkin/desert/
dw_fluffos_v2/lib/d/dist/pumpkin/gumboot/
dw_fluffos_v2/lib/d/dist/pumpkin/hospital/
dw_fluffos_v2/lib/d/dist/pumpkin/inherit/
dw_fluffos_v2/lib/d/dist/pumpkin/map/
dw_fluffos_v2/lib/d/dist/pumpkin/plain/
dw_fluffos_v2/lib/d/dist/pumpkin/pumpkin/
dw_fluffos_v2/lib/d/dist/pumpkin/save/
dw_fluffos_v2/lib/d/dist/pumpkin/squash/
dw_fluffos_v2/lib/d/dist/pumpkin/terrain/
dw_fluffos_v2/lib/d/dist/pumpkin/woods/
dw_fluffos_v2/lib/d/dist/start/
dw_fluffos_v2/lib/d/learning/TinyTown/buildings/
dw_fluffos_v2/lib/d/learning/TinyTown/map/
dw_fluffos_v2/lib/d/learning/TinyTown/roads/
dw_fluffos_v2/lib/d/learning/add_command/
dw_fluffos_v2/lib/d/learning/arms_and_weps/
dw_fluffos_v2/lib/d/learning/chars/
dw_fluffos_v2/lib/d/learning/cutnpaste/
dw_fluffos_v2/lib/d/learning/examples/npcs/
dw_fluffos_v2/lib/d/learning/examples/player_houses/npcs/
dw_fluffos_v2/lib/d/learning/examples/terrain_map/basic/
dw_fluffos_v2/lib/d/learning/functions/
dw_fluffos_v2/lib/d/learning/handlers/
dw_fluffos_v2/lib/d/learning/help_topics/npcs/
dw_fluffos_v2/lib/d/learning/help_topics/objects/
dw_fluffos_v2/lib/d/learning/help_topics/rcs_demo/
dw_fluffos_v2/lib/d/learning/help_topics/rooms/
dw_fluffos_v2/lib/d/learning/help_topics/rooms/crowd/
dw_fluffos_v2/lib/d/learning/help_topics/rooms/situations/
dw_fluffos_v2/lib/d/learning/items/
dw_fluffos_v2/lib/d/learning/save/
dw_fluffos_v2/lib/d/liaison/
dw_fluffos_v2/lib/d/liaison/NEWBIE/doc/
dw_fluffos_v2/lib/d/liaison/NEWBIE/save/oldlog/
dw_fluffos_v2/lib/db/
dw_fluffos_v2/lib/doc/
dw_fluffos_v2/lib/doc/creator/
dw_fluffos_v2/lib/doc/creator/autodoc/include/reaction/
dw_fluffos_v2/lib/doc/creator/autodoc/include/ritual_system/
dw_fluffos_v2/lib/doc/creator/autodoc/include/talker/
dw_fluffos_v2/lib/doc/creator/autodoc/include/terrain_map/
dw_fluffos_v2/lib/doc/creator/autodoc/obj/baggage/
dw_fluffos_v2/lib/doc/creator/autodoc/obj/clock/
dw_fluffos_v2/lib/doc/creator/autodoc/obj/clothing/
dw_fluffos_v2/lib/doc/creator/autodoc/obj/cont_save/
dw_fluffos_v2/lib/doc/creator/autodoc/obj/corpse/
dw_fluffos_v2/lib/doc/creator/autodoc/obj/money/
dw_fluffos_v2/lib/doc/creator/autodoc/obj/monster/
dw_fluffos_v2/lib/doc/creator/autodoc/obj/scabbard/
dw_fluffos_v2/lib/doc/creator/autodoc/obj/service_provider/
dw_fluffos_v2/lib/doc/creator/autodoc/obj/state_changer/
dw_fluffos_v2/lib/doc/creator/autodoc/obj/wand/
dw_fluffos_v2/lib/doc/creator/autodoc/std/book_dir/
dw_fluffos_v2/lib/doc/creator/autodoc/std/key/
dw_fluffos_v2/lib/doc/creator/autodoc/std/learning/
dw_fluffos_v2/lib/doc/creator/autodoc/std/map/
dw_fluffos_v2/lib/doc/creator/autodoc/std/race/
dw_fluffos_v2/lib/doc/creator/autodoc/std/weapon_logic/
dw_fluffos_v2/lib/doc/creator/files/
dw_fluffos_v2/lib/doc/creator/policy/
dw_fluffos_v2/lib/doc/creator/room/
dw_fluffos_v2/lib/doc/effects/
dw_fluffos_v2/lib/doc/ideas/
dw_fluffos_v2/lib/doc/known_command/
dw_fluffos_v2/lib/doc/lpc/basic_manual/
dw_fluffos_v2/lib/doc/lpc/intermediate/
dw_fluffos_v2/lib/doc/new/add_command/
dw_fluffos_v2/lib/doc/new/handlers/
dw_fluffos_v2/lib/doc/new/living/
dw_fluffos_v2/lib/doc/new/living/race/
dw_fluffos_v2/lib/doc/new/living/spells/
dw_fluffos_v2/lib/doc/new/player/
dw_fluffos_v2/lib/doc/new/room/guild/
dw_fluffos_v2/lib/doc/new/room/outside/
dw_fluffos_v2/lib/doc/new/room/storeroom/
dw_fluffos_v2/lib/doc/object/
dw_fluffos_v2/lib/doc/playtesters/
dw_fluffos_v2/lib/doc/policy/
dw_fluffos_v2/lib/doc/weapons/
dw_fluffos_v2/lib/global/handlers/
dw_fluffos_v2/lib/global/virtual/setup_compiler/
dw_fluffos_v2/lib/include/
dw_fluffos_v2/lib/include/cmds/
dw_fluffos_v2/lib/include/effects/
dw_fluffos_v2/lib/include/npc/
dw_fluffos_v2/lib/include/shops/
dw_fluffos_v2/lib/net/daemon/chars/
dw_fluffos_v2/lib/net/inherit/
dw_fluffos_v2/lib/net/intermud3/
dw_fluffos_v2/lib/net/intermud3/services/
dw_fluffos_v2/lib/net/obj/
dw_fluffos_v2/lib/net/save/
dw_fluffos_v2/lib/net/smnmp/
dw_fluffos_v2/lib/net/snmp/
dw_fluffos_v2/lib/obj/amulets/
dw_fluffos_v2/lib/obj/b_day/
dw_fluffos_v2/lib/obj/examples/
dw_fluffos_v2/lib/obj/food/alcohol/
dw_fluffos_v2/lib/obj/food/chocolates/
dw_fluffos_v2/lib/obj/food/fruits/
dw_fluffos_v2/lib/obj/food/meat/
dw_fluffos_v2/lib/obj/food/nuts/
dw_fluffos_v2/lib/obj/food/seafood/
dw_fluffos_v2/lib/obj/food/vegetables/
dw_fluffos_v2/lib/obj/fungi/
dw_fluffos_v2/lib/obj/furnitures/artwork/
dw_fluffos_v2/lib/obj/furnitures/bathroom/
dw_fluffos_v2/lib/obj/furnitures/beds/
dw_fluffos_v2/lib/obj/furnitures/cabinets/
dw_fluffos_v2/lib/obj/furnitures/chairs/
dw_fluffos_v2/lib/obj/furnitures/chests/
dw_fluffos_v2/lib/obj/furnitures/clocks/
dw_fluffos_v2/lib/obj/furnitures/crockery/
dw_fluffos_v2/lib/obj/furnitures/cupboards/
dw_fluffos_v2/lib/obj/furnitures/cushions/
dw_fluffos_v2/lib/obj/furnitures/fake_plants/
dw_fluffos_v2/lib/obj/furnitures/lamps/
dw_fluffos_v2/lib/obj/furnitures/mirrors/
dw_fluffos_v2/lib/obj/furnitures/outdoor/
dw_fluffos_v2/lib/obj/furnitures/safes/
dw_fluffos_v2/lib/obj/furnitures/shelves/
dw_fluffos_v2/lib/obj/furnitures/sideboards/
dw_fluffos_v2/lib/obj/furnitures/sofas/
dw_fluffos_v2/lib/obj/furnitures/stoves/
dw_fluffos_v2/lib/obj/furnitures/tables/
dw_fluffos_v2/lib/obj/furnitures/wardrobes/
dw_fluffos_v2/lib/obj/handlers/
dw_fluffos_v2/lib/obj/handlers/autodoc/
dw_fluffos_v2/lib/obj/jewellery/anklets/
dw_fluffos_v2/lib/obj/jewellery/bracelets/
dw_fluffos_v2/lib/obj/jewellery/earrings/
dw_fluffos_v2/lib/obj/jewellery/misc/
dw_fluffos_v2/lib/obj/jewellery/necklaces/
dw_fluffos_v2/lib/obj/jewellery/rings/
dw_fluffos_v2/lib/obj/media/
dw_fluffos_v2/lib/obj/misc/buckets/
dw_fluffos_v2/lib/obj/misc/jars/
dw_fluffos_v2/lib/obj/misc/papers/
dw_fluffos_v2/lib/obj/misc/player_shop/
dw_fluffos_v2/lib/obj/misc/shops/
dw_fluffos_v2/lib/obj/misc/traps/
dw_fluffos_v2/lib/obj/monster/
dw_fluffos_v2/lib/obj/monster/godmother/
dw_fluffos_v2/lib/obj/monster/transport/
dw_fluffos_v2/lib/obj/plants/inherit/
dw_fluffos_v2/lib/obj/potions/
dw_fluffos_v2/lib/open/boards/
dw_fluffos_v2/lib/save/autodoc/
dw_fluffos_v2/lib/save/bank_accounts/
dw_fluffos_v2/lib/save/boards/frog/
dw_fluffos_v2/lib/save/books/bed_catalog/
dw_fluffos_v2/lib/save/creators/
dw_fluffos_v2/lib/save/mail/
dw_fluffos_v2/lib/save/mail/p/
dw_fluffos_v2/lib/save/soul/data/
dw_fluffos_v2/lib/save/tasks/
dw_fluffos_v2/lib/save/vaults/
dw_fluffos_v2/lib/secure/cmds/lord/
dw_fluffos_v2/lib/secure/config/
dw_fluffos_v2/lib/secure/items/
dw_fluffos_v2/lib/secure/player/
dw_fluffos_v2/lib/soul/
dw_fluffos_v2/lib/soul/i/
dw_fluffos_v2/lib/soul/j/
dw_fluffos_v2/lib/soul/k/
dw_fluffos_v2/lib/soul/o/
dw_fluffos_v2/lib/soul/q/
dw_fluffos_v2/lib/soul/to_approve/
dw_fluffos_v2/lib/soul/u/
dw_fluffos_v2/lib/soul/v/
dw_fluffos_v2/lib/soul/wish_list/
dw_fluffos_v2/lib/soul/y/
dw_fluffos_v2/lib/soul/z/
dw_fluffos_v2/lib/std/creator/
dw_fluffos_v2/lib/std/effects/
dw_fluffos_v2/lib/std/effects/attached/
dw_fluffos_v2/lib/std/effects/external/
dw_fluffos_v2/lib/std/effects/fighting/
dw_fluffos_v2/lib/std/effects/other/
dw_fluffos_v2/lib/std/environ/
dw_fluffos_v2/lib/std/guilds/
dw_fluffos_v2/lib/std/hospital/
dw_fluffos_v2/lib/std/house/
dw_fluffos_v2/lib/std/house/onebedhouse/
dw_fluffos_v2/lib/std/house/onebedhut/
dw_fluffos_v2/lib/std/house/tworoomflat/
dw_fluffos_v2/lib/std/languages/
dw_fluffos_v2/lib/std/liquids/
dw_fluffos_v2/lib/std/nationality/
dw_fluffos_v2/lib/std/nationality/accents/
dw_fluffos_v2/lib/std/nationality/accents/national/
dw_fluffos_v2/lib/std/nationality/accents/regional/
dw_fluffos_v2/lib/std/npc/goals/
dw_fluffos_v2/lib/std/npc/goals/basic/
dw_fluffos_v2/lib/std/npc/goals/misc/
dw_fluffos_v2/lib/std/npc/inherit/
dw_fluffos_v2/lib/std/npc/plans/
dw_fluffos_v2/lib/std/npc/plans/basic/
dw_fluffos_v2/lib/std/outsides/
dw_fluffos_v2/lib/std/races/shadows/
dw_fluffos_v2/lib/std/room/basic/topography/
dw_fluffos_v2/lib/std/room/controller/
dw_fluffos_v2/lib/std/room/controller/topography/
dw_fluffos_v2/lib/std/room/furniture/games/
dw_fluffos_v2/lib/std/room/furniture/inherit/
dw_fluffos_v2/lib/std/room/inherit/carriage/
dw_fluffos_v2/lib/std/room/inherit/topography/
dw_fluffos_v2/lib/std/room/punishments/
dw_fluffos_v2/lib/std/room/topography/area/
dw_fluffos_v2/lib/std/room/topography/iroom/
dw_fluffos_v2/lib/std/room/topography/milestone/
dw_fluffos_v2/lib/std/shadows/
dw_fluffos_v2/lib/std/shadows/attached/
dw_fluffos_v2/lib/std/shadows/curses/
dw_fluffos_v2/lib/std/shadows/disease/
dw_fluffos_v2/lib/std/shadows/fighting/
dw_fluffos_v2/lib/std/shadows/room/
dw_fluffos_v2/lib/std/shops/controllers/
dw_fluffos_v2/lib/std/shops/objs/
dw_fluffos_v2/lib/std/shops/player_shop/
dw_fluffos_v2/lib/std/shops/player_shop/office_code/
dw_fluffos_v2/lib/std/socket/
dw_fluffos_v2/lib/www/
dw_fluffos_v2/lib/www/external/autodoc/
dw_fluffos_v2/lib/www/external/java/telnet/Documentation/
dw_fluffos_v2/lib/www/external/java/telnet/Documentation/images/
dw_fluffos_v2/lib/www/external/java/telnet/examples/
dw_fluffos_v2/lib/www/external/java/telnet/tools/
dw_fluffos_v2/lib/www/pics/
dw_fluffos_v2/lib/www/secure/creator/
dw_fluffos_v2/lib/www/secure/editors/
dw_fluffos_v2/lib/www/secure/survey_results/
dw_fluffos_v2/win32/
/*  -*- LPC -*-  */
/*
 * $Locker:  $
 * $Id: reaction.c,v 1.13 2002/02/24 06:18:32 taffyd Exp $
 * $Log: reaction.c,v $
 * Revision 1.13  2002/02/24 06:18:32  taffyd
 * Patched a runtime
 *
 * Revision 1.12  2001/07/26 04:38:17  tannah
 * Ceres' new version with fixes for runtimes.
 *
 * Revision 1.9  2001/07/18 23:35:49  tannah
 * replaced set_amount with adjust_amount and reordered a few things
 * in check_reaction()
 *
 * Revision 1.8  2001/06/30 05:42:04  pinkfish
 * Fix it up to stop spamming /log/catch.
 *
 * Revision 1.7  2000/04/10 05:08:25  ceres
 * No idea.
 *
 * Revision 1.6  1999/05/02 04:46:21  jeremy
 * Added merging of effects.
 *
 * Revision 1.5  1998/09/10 17:02:03  jeremy
 * ratios can now be int or array of ints (previously had to be floats).
 *
 * Revision 1.4  1998/08/04 04:14:35  jeremy
 * Fixed typo (I hope) in range-of-ratios section, and added more comments.
 *
 * Revision 1.3  1998/05/08 03:20:26  jeremy
 * Took out unused variables, and added update_from_files() (untested).
 *
 * Revision 1.2  1998/04/21 03:45:39  jeremy
 * Added check for a->query_amount() going to 0 in the main loop.
 *
 * Revision 1.1  1998/01/06 05:03:33  ceres
 * Initial revision
 *
*/
/*
 *  reaction handler - Handles reactions between substances that have
 *      been mixed together.
 *
 *  A few notes of what I have planned:
 *
 *  the substance mapping will have as its keys the substance names
 *  (as returned by query_medium_alias()).
 *  The value will be another mapping (one for each substance).  This
 *  second-level mapping will have as its keys the substances with
 *  which it reacts.  To save space, the names of the two substances
 *  will be compared, and the alphanumerically lower one will be the
 *  index into the top-level mapping.
 *
 *  The value of the second-level mapping is the tricky part.
 *  It is currently a class that contains all the relevant information.
 *  See the definition of the class for more info.
 *
 *  The following pseudo-objects may be used in the message strings and
 *  function call strings; process_string is explicitly called in both
 *  cases:
 *    #env# - replaced by the filename of the environment
 *    #env2# - replaced by the filename of the environment of the enviroment
 *    #obj_a# - The object being added to the container
 *    #obj_b# - The object with which 'a' is reacting
 *    #obj_x# - The transient object, or the result object if no transient
 *
 *  Various debugging messages will be printed if this_player() has a
 *  property call "debug_rh" with a non-zero value.
 *
 *  To Do:
 *    Handle concentrations.
 *
 */

#include <move_failures.h>
#include <reaction.h>

#define REACTIONS "/obj/reactions/"
#define SUBSTANCES 10

mapping substance = allocate_mapping(SUBSTANCES);
int debug_rh = 0;

void restore_from_files();

void write_dbg(string str) { if (debug_rh) write(str); }

void create() {
    seteuid("/secure/master"->creator_file(file_name(this_object())));
    restore_from_files();
}

// Merge the effects mappings, weighted by relative amounts, and return
// a mapping that can be used to add the new effects.  Note that, since
// add_xxx_effect() is cumulative, the result reflects the difference.
mapping merge_effects(mapping effects_a, mapping effects_m, int q_a, int q_m) {
    int q_t, strength;
    string key;
    mapping ret = ([ ]);

    if(!mapp(effects_a) || !mapp(effects_m))
      return 0;

    q_t = q_a + q_m;
    if ( !q_t ) {
        return 0;
    }

    foreach (key in keys(effects_a)) {
        if (!intp(effects_a[key]) || !intp(effects_m[key])) {
            continue;
        }
        if (undefinedp(effects_m[key])) {
            strength = (effects_a[key]*q_a)/q_t;
        } else {
            strength = (effects_a[key]*q_a + effects_m[key]*q_m)/q_t
                - effects_m[key];
            map_delete(effects_m, key);
        }
        ret += ([key: strength]);
    }
    foreach (key in keys(effects_m)) {
        if (!intp(effects_m[key])) {
            continue;
        }
        strength = (effects_a[key]*q_a + effects_m[key]*q_m)/q_t
            - effects_m[key];
        ret += ([key: strength]);
    }
    return ret;
}

// This is used to merge the new substance with any amounts of the same
// type already in the container, so that the total is checked.
object merge_cont_medium(object a, string medium_alias) {
    object medium;
    int no_join, q_a, q_m;
    mapping effects_a, effects_m, eff;
    string key;

    a->remove_alias(medium_alias);
    no_join = a->query_no_join();
    if ((medium = present(medium_alias, environment(a))) && !no_join) {
        a->set_no_join();
        a->move("/room/void");
        q_a = a->query_amount();
        q_m = medium->query_amount();
        medium->adjust_amount(q_a);
        // Adjust strengths of effects
        effects_a = copy(a->query_eat_effects());
        effects_m = copy(medium->query_eat_effects());
        eff = merge_effects(effects_a, effects_m, q_a, q_m);
        if (mapp(eff)) {
           foreach (key in keys(eff)) {
               medium->add_eat_effect(key, eff[key]);
           }
        }
        effects_a = copy(a->query_external_effects());
        effects_m = copy(medium->query_external_effects());
        eff = merge_effects(effects_a, effects_m, q_a, q_m);
        if(eff) {
          foreach (key in keys(eff)) {
            medium->add_external_effect(key, eff[key]);
          }
        }
        call_out("dest_substance", 0, a);
        return medium;
    } else {
        a->add_alias(medium_alias);
        return a;
    }
}

void check_reaction(object a) {
    int amt_a, need_amt_a, amt_b, need_amt_b;
    string name_a, name_b, i_a, i_b, msg, fcn;
    mixed *fcns;
    int i, j, amt_result;
    object x, *bs, ob_a, ob_b;
    float ratio;
    class reaction rcn;

    if (objectp(this_player()))
      debug_rh = this_player()->query_property("debug_rh");
    else
      debug_rh = 0;
    write_dbg("Entering reaction_handler.\n");
    name_a = a->query_medium_alias();
    if (!name_a || !a->query_continuous()) {
        write_dbg("Leaving reaction_handler because there is no A or it is not "
        "continous.\n");
        return; // Only bother if added item is a cont_medium
    }

    if ( a->query_medium_alias() == a->query_name() ) {
        debug_printf( "WARNING: Your cont_medium alias must "
          "not be the same as your name.\n" );
        return;
    }

    a = merge_cont_medium(a, name_a);
    bs = all_inventory(environment(a));
    debug_printf( "A is %O at %O. Bs are: %O.\n", a, environment( a ), bs );
    if ((sizeof(bs) < 2) || (!a->query_amount())) {
        write_dbg("Leaving reaction_handler because there is no B, or there is "
        "0 amounts of A.\n");
        return;
    }
    write_dbg("Checking the following: ");
    for (i = 0; i < sizeof(bs); i++) {
        write_dbg(bs[i]->query_medium_alias() + ", ");
    }
    write_dbg("\n");
    for (i = 0; i < sizeof(bs); i++) {
        // Check if a has been all used up yet.
        if (!a->query_amount()) {
            write_dbg("Leaving reaction_handler.\n");
            return;
        }
        // Find if a reacts with b
        name_b = bs[i]->query_medium_alias();
        if (!name_b || !bs[i]->query_continuous() || !bs[i]->query_amount()) {
            continue;
        }
        // The substance table is indexed in alphabetical order;
        // ob_a and ob_b are used to keep the two objects straight
        // whether or not they had to be swapped (mainly when dealing
        // with amounts).
        if (name_a < name_b) {
            i_a = name_a;
            i_b = name_b;
            ob_a = a;
            ob_b = bs[i];
        } else {
            i_a = name_b;
            i_b = name_a;
            ob_a = bs[i];
            ob_b = a;
        }
        write_dbg("Checking " + i_a + " against " + i_b + ".\n");
        if (!substance[i_a] || !substance[i_a][i_b]) {
            continue;
        }
        write_dbg("...they react.\n");
        rcn = substance[i_a][i_b];
        amt_a = ob_a->query_amount();
        amt_b = ob_b->query_amount();
        ratio = to_float(amt_a) / amt_b;
        if (intp(rcn->ratio)) {
            // These need to be floats
            rcn->ratio = to_float(rcn->ratio);
        }
        if (floatp(rcn->ratio)) {
            write_dbg("Single ratio...\n");
            if (ratio > rcn->ratio) {
                need_amt_b = amt_b;
                // Round up fractions (maybe this could be random?)
                need_amt_a = to_int(ceil(amt_b*rcn->ratio));
                write_dbg("Setting amt_a to "+ to_int(ceil(amt_b*rcn->ratio)) +".\n" );
            } else {
                need_amt_a = amt_a;
                need_amt_b = to_int(ceil(amt_a/rcn->ratio));
                write_dbg("Setting amt_b to "+ to_int(ceil(amt_a/rcn->ratio)) +".\n" );
            }
        } else if (arrayp(rcn->ratio) && (sizeof(rcn->ratio) == 2)) {
            // Ratio range
            // I've gotten confused by this, so here's my rationale:
            //   a/b should be in the range (r[0], r[1]).
            //   If above range, too much of a: take all of b; take as much of
            //     a as necessary to match upper ratio (ie, b*r[1]).
            //   If below range, too much of b: take all of a; take as much of
            //     b as necessary to match lower ratio (ie, a/r[0]).
            //   If in range, take all of both.
            write_dbg("Range of ratios...");
            if (intp(rcn->ratio[0])) {
                rcn->ratio[0] = to_float(rcn->ratio[0]);
            }
            if (intp(rcn->ratio[1])) {
                rcn->ratio[1] = to_float(rcn->ratio[1]);
            }
            if (ratio > rcn->ratio[1]) {
                write_dbg("above range...(" + ratio + ")\n");
                need_amt_b = amt_b;
                need_amt_a = to_int(ceil(amt_b*rcn->ratio[1]));
                write_dbg("Setting amt_a to "+ to_int(ceil(amt_b*rcn->ratio[1])) +".\n" );
                write_dbg("This is because amta is: "+ amt_b +" and ratio is "+ rcn->ratio[1] + ".\n" );
            } else if (ratio < rcn->ratio[0]) {
                write_dbg("below range...(" + ratio + ")\n");
                need_amt_a = amt_a;
                need_amt_b = to_int(ceil(amt_a/rcn->ratio[0]));
                write_dbg("Setting amt_b to "+ to_int(ceil(amt_a/rcn->ratio[0])) +".\n" );
                write_dbg("This is because amta is: "+ amt_a +" and ratio is "+ rcn->ratio[0] + ".\n" );
            } else {
                // Ratio within range, take all of both
                write_dbg("within range...(" + ratio + ")\n");
                need_amt_a = amt_a;
                need_amt_b = amt_b;
            }
        } else {
            error("Illegal reaction ratio; expecting float, int, or "
                  "array of two floats or ints.");
        }
        amt_result = to_int((need_amt_a + need_amt_b)*rcn->result_amt);
        write_dbg("...got ("+amt_a+","+amt_b+"), taking ("+need_amt_a+","+
                  need_amt_b+"), creating " + amt_result + ".\n");
        write_dbg("...creating "+rcn->result+".\n");
        // Create result object
        x = clone_object(rcn->result);
        if (rcn->result_amt) {
            if (function_exists("set_amount", x)) {
                x->set_amount(amt_result);
            } else {
                x->set_weight(amt_result);
            }
        }
        // Print out message when they mix.  This is pretty much
        // obsolete, since 'func' can do it also; but I kept it in
        // anyway.
        msg = rcn->message;
        if (msg) {
            msg = replace(rcn->message, ({
                "#env#", file_name(environment(a)),
                "#env2#", file_name(environment(environment(a))),
                "#obj_a#", file_name(ob_a),
                "#obj_b#", file_name(ob_b),
                "#obj_x#", file_name(x)
              }));
            tell_room(environment(environment(a)), msg);
        }
        // This is a back-door for any special functions that need
        // to be called.
        fcns = rcn->func;
        if (fcns) {
            for (j = 0; j < sizeof(fcns); j++) {
                if (stringp(fcns[j])) {
                    fcn = replace(fcns[j], ({
                        "#env#", file_name(environment(a)),
                        "#env2#", file_name(environment(environment(a))),
                        "#obj_a#", file_name(ob_a),
                        "#obj_b#", file_name(ob_b),
                        "#obj_x#", file_name(x)
                      }));
                } else if (functionp(fcns[j])) {
                    evaluate(fcns[j], ob_a, ob_b, x, environment(a),
                             environment(environment(a)), need_amt_a,
                             need_amt_b);
                }
            }
        }

        // Question: should I move the new object to the container
        // right now, or save it and add them all at once?  It might
        // make the order of reaction different; then again, it may
        // be arbitrary anyway.
        //x->move(environment(a));
        // I decided to postpone the move until after I leave the
        // handler.  Probably not necessary, but it makes debugging
        // easier.
        call_out("move_substance", 0, ({x, environment(a)}));

        // Decrease amounts and dest if gone
        ob_a->adjust_amount( -need_amt_a );
        ob_b->adjust_amount( -need_amt_b );

    }
    write_dbg("Leaving reaction_handler.\n");
}

void move_substance(object *ob) {
    // Used to move substance from a call_out
    int vol, vol_left, cont, closed, i, mv_stat;
    object *contents;

    if (!ob[0] || !ob[1]) return;
    vol_left = (int)ob[1]->query_max_volume() - (int)ob[1]->query_volume();
    if (cont = ob[0]->query_continuous()) {
        vol = ob[0]->query_amount();
    } else {
        vol = (int)ob[0]->query_weight()*200;
    }
    closed = ob[1]->query_closed();
    ob[1]->set_open();
    if ((mv_stat = ob[0]->move(ob[1])) != MOVE_OK) {
        write_dbg(sprintf("Move error (%d)...", vol));
        if ((vol > vol_left) && cont && !closed) {
            write_dbg("too much...\n");
            ob[0]->set_amount(vol_left);
            tell_room(environment(ob[1]), ob[0]->short(0) +
                      " leaks out of the " + ob[1]->short(0) + ".\n");
            mv_stat = ob[0]->move(ob[1]);
        }
        if (mv_stat == MOVE_OK) {
            if (closed) ob[1]->set_closed();
        } else {
            // Too big to fit! (or other problem)
            // Destroy container (unless living or a room) and contents.
            contents = all_inventory(ob[1]);
            write_dbg(sprintf("contents = %O\n", contents));
            for (i = 0; i < sizeof(contents); i++) {
                contents[i]->dest_me();
            }
            if (!living(ob[1]) && !function_exists("query_co_ord", ob[1])) {
                tell_room(environment(ob[1]), ob[1]->the_short(0) +
                          " explodes, splattering the contents all over.\n");
                //ob[1]->dest_me();
                ob[1]->move("/room/rubbish");
            }
            //ob[0]->dest_me();
            ob[0]->move("/room/rubbish");
        }
    } else {
        if (closed) ob[1]->set_closed();
    }
}

void dest_substance(object ob) {
    // Used to dest substance from a call_out.
    if (ob) {
        ob->dest_me();
    }
}

mixed *query_reaction(string name_a, string name_b) {
    // Return reaction parameters
    if (!substance[name_a] || !substance[name_a][name_b]) {
        return 0;
    }
    return substance[name_a][name_b];
}

mapping query_reactions() { return substance; }

// This should eliminate the need for the (now deleted) database manipulation
// functions.  Basically, all reactions in all files in the directory
// REACTIONS will be loaded.  This uses /obj/handler/data.  See the
// documentation on it and /include/reaction.h for the file format.

void restore_from_files() {
    string *files;
    int i;

    // This is the file with the #include and stuff that should only be
    // there once.
    files = ({ "base.hdr" });
    // These are the real data files
    files += get_dir(REACTIONS + "*.rcn");
    for (i = 0; i < sizeof(files); i++) {
        files[i] = REACTIONS + files[i];
    }
    //printf("files = %O\n", files);
    substance = "/obj/handlers/data"->compile_data(files);
}

void update_from_files(string fn) {
    mapping new_substance;
    string a, b;

    new_substance = "/obj/handlers/data"->compile_data( ({ fn }) );
    foreach (a in keys(new_substance)) {
      if (undefinedp(substance[a])) {
        substance[a] = ([ ]);
      }
      foreach (b in keys(new_substance[a])) {
        substance[a][b] = new_substance[a][b];
      }
    }
}