/*
 * transient.c - reaction object, resulting from the mixing of two
 *     substances.
 *
 * The following pseudo-objects may be used in progress strings:
 *    #obj# - replaced by the filename of this object (virtual objects
 *            don't seem to handle file_name correctly)
 *    #env# - replaced by the filename of the environment
 *    #env2# - replaced by the filename of the environment of the environment
 *    #result# - replaced by the filename of the result object
 */
// This use to be /obj/cont_medium, but /obj/food has more features
inherit "/obj/food";
#include <move_failures.h>
// Default amount per unit weight (for setting weight of non-continuous
// items)
#define AMT_PER_WGT 200
// progress_msg is an array of data structures of the form:
//   ({({ msg_1, ({function_str1a, function_str1b, ...})}),
//     ({ msg_2, ({function_str2a, function_str2b, ...})}),
//     ...
//   })
// At interval n, msg_n is printed out and function_strnx is called, after
// having the substitutions above performed and being passed through
// process_string.  This should give plenty of functionality (if not, I
// can always add more :).
mixed *progress_msg = ({});
int duration, interval, counter, hp_base;
float result_amt;
string result, result_alias;
string final = "boom";
int in_progress;
void set_progress_msg(mixed *msg) { progress_msg = msg; }
void set_final(string s) { final = s; }
void set_result(string s) { result = s; }
void set_result_alias(string s) { result_alias = s; }
void set_result_amt(float amt) { result_amt = amt; }
void set_duration(int time) {
    // NOTE that this must be called after the progress_msg has been set
    duration = time;
    if (sizeof(progress_msg)) {
        interval = duration / sizeof(progress_msg);
    } else {
        interval = duration;
    }
}
void add_progress_msg(mixed *msg) {
    progress_msg += ({msg});
    set_duration(duration); // calculate new interval
}
// Note: hp_base is hps of damage done per unit.  If it's being carried
// by a living thing, it does full damage.  Otherwise, it does 1/5 damage
// to everyone in the room (sort of an arbitrary decision, but I'm trying
// to keep the number of parameters down).
void set_hp_base(int hp) { hp_base = hp; }
void start_reaction() {
    /*
     * NOTE: this is the function that sets off the call_outs.  Call it
     * after everything else has been set up.
     */
    if (sizeof(progress_msg) > 1) {
        call_out("progress", interval);
    } else {
        call_out(final, interval);
    }
    in_progress = 1;
}
void stop_reaction() {
    if (counter < sizeof(progress_msg)-1) {
        remove_call_out("progress");
    } else {
        remove_call_out(final);
    }
    in_progress = 0;
}
void restart_reaction() {
    if (counter < sizeof(progress_msg)-1) {
        call_out("progress", interval);
    } else {
        call_out(final, interval);
    }
    in_progress = 1;
}
void progress() {
    object env, env2;
    string msg, fcn;
    int i;
    env = environment(this_object());
    env2 = environment(env);
    if (progress_msg[counter][0]) {
        msg = replace(progress_msg[counter][0],
                      ({"#env#", file_name(env),
                        "#env2#", env2?file_name(env2):0,
                        "#obj#", file_name(this_object()),
                        "#result#", ""}));
        msg = process_string(msg);
        tell_room(env2, msg);
    }
    if (progress_msg[counter][1]) {
        for (i = 0; i < sizeof(progress_msg[counter][1]); i++) {
            fcn = replace(progress_msg[counter][1][i],
                  ({"#env#", file_name(env),
                    "#env2#", env2?file_name(env2):0,
                    "#obj#", file_name(this_object()),
                    "#result#", ""}));
            process_string("@@"+fcn+"@@");
        }
    }
    counter++;
    if (counter < sizeof(progress_msg)-1) {
        call_out("progress", interval);
    } else {
        call_out(final, interval);
    }
}
void boom() {
    object env, env2, cont, new_env;
    object *victims, *contents;
    object result_ob;
    int hp, i, amt, tmp;
    string msg, fcn;
    
    // Assume substance is in a container
    env = environment(this_object());
    env2 = environment(env);
    amt = query_amount();
    if (living(env)) {
        // Held by a living
        hp = hp_base*amt;
        env->adjust_hp(-hp);
    } else if (living(env2)) {
        // In a container held by a living
        hp = (hp_base*amt)/2;
        env2->adjust_hp(-hp);
    } else if (env->query_co_ord()) {
        // Sitting in a room
        hp = (hp_base*amt)/2;
        victims = all_inventory(env);
        for (i = 0; i < sizeof(victims); i++) {
            if (living(victims[i])) {
                victims[i]->adjust_hp(-hp);
            }
        }
    } else if (env2->query_co_ord()) {
        // Sitting in a container in a room
        hp = (hp_base*amt)/5;
        victims = all_inventory(env2);
        for (i = 0; i < sizeof(victims); i++) {
            if (living(victims[i])) {
                victims[i]->adjust_hp(-hp);
            }
        }
    }
    if (result) {
        result_ob = clone_object(result);
        if (result_alias) {
            // result_alias should be 0 if this isn't a cont_medium
            result_ob->set_medium_alias(result_alias);
        }
        if (function_exists("set_amount", result_ob)) {
            result_ob->set_amount(to_int(result_amt*query_amount()));
        } else if (result_amt > 0.0) {
            result_ob->set_weight(to_int(result_amt*query_amount()/AMT_PER_WGT));
        }
    }
    if (sizeof(progress_msg)) {
        if (progress_msg[counter][0]) {
            msg = replace(progress_msg[counter][0],
                          ({"#env#", file_name(env),
                            "#env2#", env2?file_name(env2):0,
                            "#obj#", file_name(this_object()),
                            "#result#", result_ob?file_name(result_ob):0}));
            msg = process_string(msg);
            tell_room(env2, msg);
        }
        if (progress_msg[counter][1]) {
            for (i = 0; i < sizeof(progress_msg[counter][1]); i++) {
                fcn = replace(progress_msg[counter][1][i],
                              ({"#env#", file_name(env),
                                "#env2#", env2?file_name(env2):0,
                                "#obj#", file_name(this_object()),
                                "#result#", ""}));
                process_string("@@"+fcn+"@@");
            }
        }
    }
    move("/room/void");
    // Destroy container (unless living or a room) and move contents
    // up.
    contents = all_inventory(env);
    if (result_ob) contents += ({result_ob});
    for (i = 0; i < sizeof(contents); i++) {
        if (contents[i]->query_continuous()) {
            contents[i]->dest_me();
            continue;
        }
        new_env = env2;
        while (new_env && ((tmp=contents[i]->move(new_env)) != MOVE_OK)) {
            new_env = environment(new_env);
        }
        if (!new_env) contents[i]->dest_me();
    }
    if (!living(env) && !function_exists("query_co_ord", env)) {
        env->dest_me();
    }
    call_out("dest_me", 0);
}
object morph() {
    // Returns the object created...
    object env, env2, result_ob, new_env, *contents;
    string msg, fcn;
    int i, mv_stat;
    env = environment(this_object());
    env2 = environment(env);
    // Need to move out of the container to make room
    move("/room/void");
    if (stringp(result)) {
        result_ob = clone_object(result);
        if (stringp(result_alias)) {
            // result_alias should be 0 if this isn't a cont_medium
            result_ob->set_medium_alias(result_alias);
            result_ob->set_amount(to_int(result_amt*query_amount()));
        } else if (result_amt > 0.0) {
            result_ob->set_weight(to_int(result_amt*query_amount()/AMT_PER_WGT));
        }
        mv_stat = result_ob->move(env);
        if (mv_stat) {
            //write("Uh oh, move error: " + mv_stat + "\n");
            // Destroy container (unless living or a room) and contents.
            contents = all_inventory(env);
            for (i = 0; i < sizeof(contents); i++) {
                contents[i]->dest_me();
            }
            if (!living(env) && !function_exists("query_co_ord", env)) {
                tell_room(env2, "The " + env->short(0) +
                          " explodes, splattering the contents all over.\n");
                env->dest_me();
            }
            result_ob->dest_me();
            result_ob = 0;
        }
    }
    if (sizeof(progress_msg)) {
        if (progress_msg[counter][0]) {
            msg = replace(progress_msg[counter][0],
                          ({"#env#", env?file_name(env):0,
                            "#env2#", env2?file_name(env2):0,
                            "#obj#", file_name(this_object()),
                            "#result#", result_ob?file_name(result_ob):0}));
            msg = process_string(msg);
            tell_room(env2, msg);
        }
        if (progress_msg[counter][1]) {
            for (i = 0; i < sizeof(progress_msg[counter][1]); i++) {
                fcn = replace(progress_msg[counter][1][i],
                          ({"#env#", env?file_name(env):0,
                            "#env2#", env2?file_name(env2):0,
                            "#obj#", file_name(this_object()),
                            "#result#", result_ob?file_name(result_ob):0}));
                process_string("@@"+fcn+"@@");
            }
        }
    }
    call_out("dest_me", 0);
    return result_ob;
}
    
string query_pounds() { return "about " + ::query_weight()/9; }
void query_progress_msg() {
    int i, j;
    
    write("({\n");
    for (i = 0; i < sizeof(progress_msg); i++) {
        write(progress_msg[i][0] + " ({\n");
        if (!progress_msg[i][0]) {
            write("0})\n");
        } else {
            for (j = 0; j < sizeof(progress_msg[i][1]); j++) {
                write(progress_msg[i][1][j] + "\n");
            }
            write("})\n");
        }
    }
}
mixed stats() {
  return ::stats() + ({
      ({ "duration", duration }),
      ({ "interval", interval }),
      ({ "counter", counter }),
      ({ "hp base", hp_base }),
      ({ "result amt", result_amt }),
      ({ "result", result }),
      ({ "result alias", result_alias }),
      ({ "final", final }),
      ({ "in progress", in_progress }),
  });
} /* stats() */
mapping query_dynamic_auto_load() {
    return ([
             "::": ::query_dynamic_auto_load(),
             "progress_msg" : progress_msg,
             "duration" : duration,
             "interval" : interval,
             "counter" : counter,
             "hp_base" : hp_base,
             "result_amt" : result_amt,
             "result" : result,
             "result_alias" : result_alias,
             "final" : final,
             "in_progress" : in_progress,
           ]);
} /* query_dynamic_auto_load() */
void init_dynamic_arg(mapping args) {
    if (args["::"])
      ::init_dynamic_arg(args["::"]);
    if (!undefinedp(args["progress_msg"]))
      progress_msg = args["progress_msg"];
    if (!undefinedp(args["duration"]))
      duration = args["duration"];
    if (!undefinedp(args["interval"]))
      interval = args["interval"];
    if (!undefinedp(args["counter"]))
      counter = args["counter"];
    if (!undefinedp(args["hp_base"]))
      hp_base = args["hp_base"];
    if (!undefinedp(args["result_amt"]))
      result_amt = args["result_amt"];
    if (!undefinedp(args["result"]))
      result = args["result"];
    if (!undefinedp(args["result_alias"]))
      result_alias = args["result_alias"];
    if (!undefinedp(args["final"]))
      final = args["final"];
    if (!undefinedp(args["in_progress"])) {
        in_progress = args["in_progress"];
        if (in_progress)
          restart_reaction();
    }
} /* init_dynamic_arg() */
mapping int_query_static_auto_load() {
   return ([
      "::": ::int_query_static_auto_load(),
   ]);
} /* int_query_static_auto_load() */
mapping query_static_auto_load() {
   if ( file_name( this_object() )[ 0 .. 22 ] == "/obj/reagents/transient" )
      return int_query_static_auto_load();
   return 0;
} /* query_static_auto_load() */
void init_static_arg( mapping map ) {
   if ( map[ "::" ] )
      ::init_static_arg( map[ "::" ] );
} /* init_static_arg() */