/
ColdCore-3.0a9.02/
ColdCore-3.0a9.02/src/
new object $has_reactions: $foundation;

var $has_reactions active = 0;
var $has_reactions last_id = 0;
var $has_reactions reactions = 0;
var $root created_on = 809243338;
var $root flags = ['methods, 'code, 'variables, 'core];
var $root inited = 1;
var $root managed = [$has_reactions];
var $root manager = $has_reactions;

public method .active() {
    return active;
};

public method .add_reaction() {
    arg @args;
    var r, id;
    
    // make sure it doesn't already exist..
    for r in (reactions || #[]) {
        if (((r[2])[2]) == (args[2])) {
            if ((r[2]) == args)
                return r[1];
        }
    }
    (> .check_reaction_args(@args) <);
    reactions = dict_add(reactions || #[], ++last_id, args);
};

private method .check_reaction_args() {
    arg method, template, type, chance, times, hook, min, max;
    
    if (type(method) != 'symbol)
        throw(~type, "Match type (arg 1) is not a symbol.");
    if (type(template) != 'string)
        throw(~type, "Match template (arg 2) is not a string.");
    if (type(type) != 'symbol)
        throw(~type, "Reaction type (arg 3) is not a symbol.");
    if (type(chance) != 'integer)
        throw(~type, "Reaction chance (arg 4) is not a integer.");
    if (type(times) != 'integer)
        throw(~type, "Reaction times (arg 5) is not a integer.");
    if (type(hook) != 'list)
        throw(~type, "Reaction hook (arg 6) is not a list.");
    if ((!hook) || (type(hook[1]) != 'symbol))
        throw(~type, "Reaction hook method (arg 6[1]) is not a symbol.");
    if ((listlen(hook) == 1) || (type(hook[2]) != 'list))
        throw(~type, "Reaction hook arguments (arg 6[2]) is not a list.");
    if (type(min) != 'integer)
        throw(~type, "Reaction minimum delay (arg 7) is not a integer.");
    if (type(max) != 'integer)
        throw(~type, "Reaction maximum delay (arg 8) is not a integer.");
};

protected method .check_reactions() {
    arg type, str, sender;
    var rnum, t, id, chance, times, method, template, m;
    
    rnum = random(100);
    for t in ([type, 'any]) {
        if (!dict_contains(active, t))
            continue;
        for id in (dict_keys(active[t])) {
            [chance, times] = (active[t])[id];
            if (rnum > chance)
                break;
            [method, template] = reactions[id];
            if ((m = str.(method)(template))) {
                if (times == 1)
                    .remove_active(t, id);
                else if (times > 1)
                    .update_active(t, id, [chance, --times]);
                if ((.do_reaction(str, m, id, sender)) != 'continue)
                    return;
            }
        }
    }
};

public method .del_reaction() {
    arg id;
    
    // cruft up later
    reactions = reactions.del(id);
};

protected method .do_reaction(): forked  {
    arg str, match, id, sender;
    var method, args, min, max, time;
    
    [[method, args], min, max] = sublist(reactions[id], 6);
    time = random(max - min) + min;
    $scheduler.sleep(time);
    catch any
        return (> .(method)(str, match, sender, @args) <);
    with
        .tell_traceback(traceback());
    return 'stop;
};

protected method .remove_active() {
    arg key, id;
    
    active = dict_add(active, key, dict_del(active[key], id));
};

public method .set_active() {
    arg id;
    var ra, key, e, inserted, chance, times;
    
    if ((!reactions) || (!dict_contains(reactions, id)))
        throw(~noreaction, ("No reaction with id '" + id) + "'.");
    if (!active)
        active = #[];
    [key, chance, times] = sublist(reactions[id], 3, 3);
    if (dict_contains(active, key)) {
        ra = active[key];
        if (ra.contains(id))
            return .update_active(key, id, [chance, times]);
        active = dict_del(active, key);
        ra = map e in (ra) to (e);
    } else {
        ra = [];
    }
    for e in [1 .. listlen(ra)] {
        if ((((ra[e])[2])[1]) < chance) {
            ra = insert(ra, e, [id, [chance, times]]);
            inserted++;
        }
    }
    if (!inserted)
        ra += [[id, [chance, times]]];
    active = dict_add(active, key, hash e in (ra) to (e));
};

public method .tell_traceback() {
    arg @args;
    
};

protected method .update_active() {
    arg key, id, value;
    
    active = dict_add(active, key, dict_add(active[key], id, value));
};