/* Do not remove the headers from this file! see /USAGE for more info. */ /* * Monster following module * Designed to allow NPC's to follow other NPCs or PCs * 092998 Created by Tigran, based on /domains/std/wolf.c */ //:MODULE // // Module designed to allow objects to follow other moving objects. // This module requires functionality from M_SMARTMOVE, M_TRIGGERS and // M_ACTIONS. //:TODO // // 1.) Target selection could be made smarter than it is. // 2.) Add a hook call to prevent following? #include <hooks.h> void follow_hook(string whichway); void arrive_hook(object what); void acquire_follow(); void call_hooks(string, int); void respond(string str); void add_hook(string tag, function hook); private nosave function f_follow=(:follow_hook:); private nosave function arrive=(:arrive_hook:); private mixed array follow_search=({}); private object follow; private nosave object my_location; private nosave int following; //:FUNCTION set_follow_search //Set the names of the objects that the object will follow. //Allowable arguments are strings, objects or function pointers. //Strings must be an id of the object which you want to have //followed. //If a function pointer is used it must return a string, object //or an array of objects and strings. An argument of this_object //is passed to the function pointer. void set_follow_search(mixed array follow...) { follow_search=clean_array(flatten_array(follow)); } //:FUNCTION add_follow_search //Add names to the list of objects that the object will follow //See set_follow_search void add_follow_search(mixed array follow...) { follow_search=clean_array(flatten_array(follow_search+follow)); } //:FUNCTION remove_follow_search //Remove names from the list of objects that the object will follow void remove_follow_search(mixed array follow...) { follow_search-=flatten_array(follow); } //:FUNCTION clear_follow_search //Clears the search array for following void clear_follow_search() { follow_search=({}); } varargs private mixed array expand_follows() { mixed array potentials=({}); foreach(mixed elem in follow_search) { if(functionp(elem)) potentials+=({evaluate(elem,this_object())}); else if(stringp(elem)||objectp(elem) ) potentials+=({elem}); } return clean_array(potentials); } //:FUNCTION query_follow_search //Returns a list of the follow strings that the object will follow string array query_follow_search() { return expand_follows(); } //:FUNCTION set_follow //Set the follow that the object will follow void set_follow(object what) { if(what!=this_object()) follow=what; } //:FUNCTION clear_follow //Clear the follow that's the object will follow void clear_follow() { follow=0; } //:FUNCTION query_follow //Returns the follow object which the object is currently following object query_follow() { return follow; } //:FUNCTION is_following //Returns 1 if the object is currently following another object int is_following() { return !!following; } //:FUNCTION acquire_follow //If there is no current follow, or the current follow is not present. //The new follow is determined by the follow_search //:TODO //perhaps add something to allow weighted choices for follows? void acquire_follow() { int i; mixed array potentials=({}); if(follow && present(follow,environment(this_object() ) ) ) return; potentials=filter(expand_follows(), (: present($1,environment(this_object())) :) ); i=sizeof(potentials); if(i) { set_follow(present(potentials[random(i)],environment(this_object()))); } else clear_follow(); } private int follow_match(mixed check, object ob) { if(functionp(check)) return member_array(ob,eval(check,ob))>-1; if(objectp(check)) return ob==check; if(stringp(check)) return ob->id(check); } //:FUNCTION is_potential_follow //Return 1 if the object is a potential follow to follow int is_potential_follow(object ob) { return sizeof(filter(expand_follows(),(:follow_match:),ob)); } void follow_hook(string whichway) { if(follow==this_body()) { set_this_user(this_object()); respond("go "+whichway); set_this_user(follow->query_link()); } } void arrive_hook(object what) { object env = environment(this_object()); if(!env) return; if((follow && present(follow,env))|| query_follow()==what) return; clear_follow(); if(is_potential_follow(what)) set_follow(what); } protected void did_move() { if(!sizeof(follow_search)) return; if (my_location) { my_location->remove_hook("person_left", f_follow); my_location->remove_hook("object_arrived",arrive); } my_location = environment(); if (my_location) { my_location->add_hook("person_left", f_follow); my_location->add_hook("object_arrived",arrive); } //: HOOK check_follow //This hook allows the follow and/or follow_searches to be changed //due to changing environmental conditions, aggression, whatever. //Return value is ignored. call_hooks("check_follow",HOOK_IGNORE); acquire_follow(); } void mudlib_setup() { add_hook("move",(: did_move :)); } mapping lpscript_attributes() { return (["follow_search":({LPSCRIPT_LIST,"setup","set_follow_search"}) ]); } void do_follow_obj(object ob) { if(member_array(ob,follow_search)>-1) { remove_follow_search(ob); clear_follow(); this_body()->my_action("You stop following "+ob->short()+"."); } else { add_follow_search(ob); this_body()->my_action("You begin following "+ob->short()+"."); acquire_follow(); did_move(); } }