/* Do not remove the headers from this file! see /USAGE for more info. */ // Effects system, idea based on the system used in Sorrows (ex Discworld?) inherit CLASS_EFFECT; private array effects = ({}); // Queue of effects to call nosave private int tag; // Tag for call_out //:MODULE // Handles effects (ie delayed/repeating events) // Each effect calls start_effect in the specified file when the effect starts // Calls reinstate_effect when it restarts (at login etc) // Calls do_effect periodically // Calls end_effect when it ends // Each of these functions takes arguments : effect sufferer, args, counter // where counter is the number of times it will repeat // args is a mixed set of args as appropriate to the effect //:FUNCTION time_to_next_effect // Returns time remaining in call_out to the next effect int time_to_next_effect() { mixed * callouts = call_out_info(); foreach(mixed callout in callouts) { if(callout[0] == this_object()) if(callout[1] == "next_effect") return callout[2]; } return -1; } //:FUNCTION actual_period // Converts period to an actual interval, with suitable randomisation // Formats : int val : period = val // int array ({ fixed, var}) : period = fixed + random(var) // function : evaluate it int actual_period( mixed val ) { if(intp(val)) return val; if(arrayp(val)) if(sizeof(val) == 2) if(intp(val[0]) && intp(val[1])) return val[0] + random(val[1]); if(functionp(val)) return evaluate(val); return 0; } //###TODO add "special case" for time adjustment for removal of first effect // where call_out remaining should be used in place of delay //:FUNCTION remove_effect_at // Removes effect from queue at appropriate point void remove_effect_at(int pos) { effects[pos-1]->delay += effects[pos]->delay; effects = effects[0..pos-1] + effects[pos+1..]; } //:FUNCTION find_effect_index // Return position of effect with specified object in queue // Return -1 on failure int find_effect_index(string ob) { if(ob) for(int i = 0; i<sizeof(effects); i++) { if(effects[i]->effect_ob == ob) return i; } return -1; } //:FUNCTION find_effect_name_index // Return position of effect with specified name in queue // Return -1 on failure int find_effect_name_index(string name) { if(name) for(int i = 0; i<sizeof(effects); i++) { if(effects[i]->name == name) return i; } return -1; } //:FUNCTION find_effect_indexes_matching // Return array of positions of effects (part-)matching specified name // Return ({}) int array find_effect_indexes_matching(string name) { int array res = ({}); if(name) for(int i = 0; i<sizeof(effects); i++) { if(effects[i]->name[0..strlen(name)-1] == name) res += ({i}); } return res; } //:FUNCTION find_effect // Return effect with specified object in queue // Return 0 on failure int find_effect(string ob) { int idx = find_effect_index(ob); if(idx>-1) return effects[idx]; return 0; } //:FUNCTION query_effect_args // Return args of specified effect mixed query_effect_args(string ob) { int idx = find_effect_index(ob); if(idx>-1) return effects[idx]->args; return 0; } //:FUNCTION remove_effect // Locate effect matching the specified ob and remove it // Return 1 on success, 0 on failure int remove_effect(string ob) { int index = find_effect_index(ob); if(index>-1) if(remove_effect_at(index)) return 1; return 0; } //:FUNCTION remove_effect_named // Locate effect matching the specified name and remove it // Return 1 on success, 0 on failure int remove_effect_named(string name) { int index = find_effect_name_index(name); if(index>-1) if(remove_effect_at(index)) return 1; return 0; } //:FUNCTION remove_effects_matching // Locate effects matching the specified name and remove them // Return 1 on success, 0 on failure int remove_effects_matching(string name) { int array matches = find_effect_indexes_matching(name); if(sizeof(matches)) { foreach(int idx in sort_array(matches, -1)) remove_effect_at(idx); return 1; } return 0; } //:FUNCTION insert_effect_at // Inserts effect into queue at appropriate point void insert_effect_at(class effect_class effect, int pos) { effects = effects[0..pos-1] + ({ effect }) + effects[pos..]; } //:FUNCTION insert_effect // Finds appropriate point in queue to insert effect // Adjusts delay to following effect // Returns 1 on success, 0 on failure. int insert_effect(class effect_class effect) { class effect_class this_effect = new ( class effect_class ); int cum_delay, prev_delay; // If queue is empty, just put this effect in the queue if(!effects || !sizeof(effects)) { effects = ({ effect }); return 1; } // Walk through queue, calculating cumulative delay // and when this exceeds delay required for the effect to be inserted // insert the effect for(int i=0; i<sizeof(effects); i++) { this_effect = effects[i]; prev_delay = cum_delay; cum_delay += this_effect->delay; if(effect->delay<cum_delay) { insert_effect_at(effect,i); // If there is an effect following this in the queue, // adjust its relative delay if(sizeof(effects)>i+1) { this_effect->delay -= (effect->delay - cum_delay); effects[i+1] = this_effect; } // If there is an effect before this one in the queue, // adjust the relative delay of this effect if(sizeof(effects)>1) { this_effect=effects[i]; this_effect->delay -= prev_delay; effects[i] = this_effect; } return 1; } } return 0; } //:FUNCTION next_effect // Call the function specified in the function at the head of the queue // Move it to appropriate place in queue // Then call out to next effect in the queue void next_effect() { class effect_class this_effect; object ob; // Finish if the queue is empty if(!effects || !sizeof(effects)) return; // Do whatever the current head of the queue wants this_effect = effects[0]; ob=load_object(this_effect->effect_ob); if(!ob) return; // Remove head of queue effects = effects[1..]; // If counter is not zero, move current effect to later in the queue if(this_effect->counter!=0) { // If counter >0, decrease it if(this_effect->counter>0) this_effect->counter --; this_effect->args = ob->do_effect( this_object(), this_effect->args, this_effect->counter ); // Calculate delay to next occurence this_effect->delay = actual_period(this_effect->interval); // Put it into queue again insert_effect(this_effect); } else { ob->end_effect( this_object(), this_effect->args ); } // If there is anything in the queue, do a callout to it if(sizeof(effects)) { this_effect = effects[0]; tag = call_out("next_effect", this_effect->delay); } else tag = 0; } //:FUNCTION clear_effects // Clears the effects queue void clear_effects() { object ob; foreach(class effect_class effect in effects) { ob = load_object(effect->effect_ob); if(ob) if(function_exists("end_effect", ob)) ob->end_effect( this_object(), effect->args, effect->counter ); } effects = ({}); tag = 0; } //:FUNCTION query_effects // Returns copy of the effects queue array query_effects() { return copy(effects); } //:FUNCTION add_effect // Adds the specified effect // Usage: add_effect(string ob, mixed args, int repeats, mixed interval) // repeats will default to ob->query_repeats() // interval will default to ob->query_interval() void add_effect(string ob, mixed args, int repeats, mixed interval) { class effect_class this_effect = new(class effect_class); object obj = load_object(ob); if(!obj) return; if(undefinedp(repeats)) repeats = ob->query_repeats(); if(undefinedp(interval)) interval = ob->query_interval(); this_effect->effect_ob = ob; this_effect->name = ob->query_name(); this_effect->args = args; this_effect->interval = interval; this_effect->counter = repeats; this_effect->delay = actual_period(interval); if(function_exists("start_effect", obj)) this_effect->args = obj->start_effect( this_object(), args, repeats, interval ); insert_effect(this_effect); if(!tag) tag = call_out("next_effect", this_effect->delay); } //:FUNCTION reinstate_effects // Called on relogging to restart effects. void reinstate_effects() { object ob; foreach(class effect_class effect in effects) { ob = load_object(effect->effect_ob); if(!ob) continue; if(function_exists("reinstate_effect", ob)) effect->args = ob->reinstate_effect( this_object(), effect->args, effect->counter, ); } next_effect(); }