/* Everybody needs a soul, or they will wind up being called Wayne or Garth * Written by Profezzorn@nannymud * I guess I'm setting a new record in cpu-usage.... * * future enhancements: * think * You think * (Profezzorn thinks you smile.) * * Thanks goes to Xantrax (& Mav) @ Ultraworld for the TMI-2 mudlib support. */ /* Normally you shouldn't make objects that contains 'feelings' because they should be handled by /obj/soul. If you however feel that your feelings doesn't belong in the standard soul it is now possible to patch them into the standard soul with the use of the following functions in the soul. =========================================================================== NAME void add_verb(mapping m) DESCRIPTION This function adds verbs to the soul. They will stay there until removed or until soul is destructed. (ie. when player log off) The argument m is a mapping of the same format as the one further down in this program as the format is quite complex I will not go into details here but advice you to learn from the numerous examples in this program. There is one special thing I'd like to mention though, if a verb is defined as a string or an object, reduce_verb will be called in that object instead of this object. (See reduce_verb) This makes it possible to go outside the normal SIMP/DEFA etc., or even disable certain verbs. =========================================================================== NAME void remove_verb(string *v) DESCRIPTION Remove_verbs removes verbs previously added to the soul, note that the standard verbs present from the beginning in the soul cannot be changed nor removed. =========================================================================== NAME void add_adverb(string *v) DESCRIPTION With this function you can add adverbs to the soul. It's as simple as that. In all other resepects it works just as add_verbs. =========================================================================== NAME void remove_adverb(string *v) DESCRIPTION With this function you can remove adverbs previously added. Note that you can not remove adverbs that are there by default. =========================================================================== NAME string query_last_action() DESCRIPTION This function is only valid during the call of a catch_tell() from this object. It returns the feeling used last with adverbs and persons expanded to their full names. =========================================================================== NAME mixed *query_brokendown_data() DESCRIPTION This call is also only valid during calls of catch_tell() from this object. It returns an easy-to-parse version of the command written on the following format: ({ meta-verb , ({ verb , ({ persons }), ({ adverbs }), message, ({ where }) }), ...... }) =========================================================================== NAME mixed *query_feeling_for(object *who) DESCRIPTION Works exactly like query_brokendown_data(), but returns entries used towards the object who only. =========================================================================== */ /* define this if you get eval cost too big when you try 'help adverbs' * with LOW_EVAL_COST, soul_adverbs _must_ be sorted in alphabetical order. */ /* #define LOW_EVAL_COST */ #define DEBUG 0 #ifdef __NANNYMUD__ #define HAVE_SPRINTF #define BUGGY_EXPLODE #define MOVE(X,Y) move_object(X,Y) #define NAME(X) lower_case((string)(X)->query_name()) #define CAP_NAME(X) ((string)(X)->query_name()) #define ISLIVING(X) ((int)(X)->query_living()) #define ISVISIBLE(X) (!!((string)(X)->short())) #define POSSESSIVE(X) ((string)(X)->query_possessive()) #define OBJECTIVE(X) ((string)(X)->query_objective()) #define PRONOUN(X) ((string)(X)->query_pronoun()) #define MORE_ROWS() ((int)this_player()->query_rows()) #define FORCE_SELF(X) command((X),this_player()) #define SORT(X) sort_array((X),"letterorder",this_object()) #define SUB_ARRAY(X,Y) ((X) - (Y)) #define CONFIGURED #endif /* Change to 1 if you use TMI-lib */ #if 0 #define HAVE_SPRINTF #define MOVE(X,Y) move_object(X,Y) #define NAME(X) ((string)(X)->query("name")) #define CAP_NAME(X) ((string)(X)->query("cap_name")) #define ISLIVING(X) (living(X) || userp(X)) #define ISVISIBLE(X) (!((int)(X)->query("invisible"))) #define POSSESSIVE(X) possessive(X) #define OBJECTIVE(X) objective(X) #define PRONOUN(X) pronoun(X) #define MORE_ROWS() ((int)this_player()->getenv("LINES")) #define FORCE_SELF(X) ((int)this_player()->force_us(X)) #define SORT(X) sort_array((X)) #define USE_UID #define SUB_ARRAY(X,Y) ((mixed *)(X) - (mnixed *)(Y)) #include #include #define TMI_LIB #define CONFIGURED #endif #ifndef CONFIGURED /* #define HAVE_SPRINTF */ /* most people have buggy explode, if you don't. comment this out */ #define BUGGY_EXPLODE #define DEBUG 0 #define replace(X,Y,Z) implode(my_explode(X,Y),Z) /* Change this to move objects */ #define MOVE(X,Y) move_object(X,Y) #define NAME(X) lower_case((X)->query_name()) #define CAP_NAME(X) (X)->query_name() #define ISLIVING(X) living(X) #define ISVISIBLE(X) (!!((string)(X)->short())) #define FORCE_SELF(X) command((X),this_player()) #define POSSESSIVE(X) (({"its","his","hers"})[(int)(X)->query_gender(X)]) #define OBJECTIVE(X) (({"it","him","her"})[(int)(X)->query_gender(X)]) #define PRONOUN(X) (({"it","he","she"})[(int)(X)->query_gender(X)]) #define MORE_ROWS() 22 #define SORT(X) sort_array((X),"letterorder",this_object()) #define SUB_ARRAY(X,Y) ((X) - (Y)) #endif #define SIMP 0 #define DEFA 1 #define DEUX 2 #define PERS 3 #define QUAD 4 #define PREV 5 #define SHRT 6 #define PHYS 7 #define FULL 8 /* if we use the exact same 'spaces' string evewhere we save some memory * it also look nicer. */ #define SPACES " " string *my_explode(string a,string b) { #ifdef BUGGY_EXPLODE string *c; if(a==b) return ({"",""}); if(strlen(b) && a[0..strlen(b)-1]==b) return ({ "" })+ my_explode(a[strlen(b)..strlen(a)-1],b); if(c=explode(a+b,b)) return c; return ({}); #else return explode(a,b); #endif } #ifdef HAVE_SPRINTF /* This linebreak has usually a limit about 10000 bytes */ string fast_linebreak(string text,string pre,int width) { return sprintf("%s%-=*s",pre,width,text); } #else /* have_sprintf */ /* This doesn't have to be able to linebreak very large strings */ string fast_linebreak(string text,string pre,int width) { string *parts,pre2; int part; parts=my_explode(text,"\n"); pre2=SPACES[1..strlen(pre)]; for(part=0;part=width) { len=strlen(words[word])+1; words[word]="\n"+pre2+words[word]; } } parts[part]=implode(words," "); } return pre+implode(parts,"\n")[strlen(pre)..100000000]; } #endif /* have_sprintf */ #ifdef LPC4 string big_linebreak(string text,string pre,int width) { return fast_linebreak(text,pre,width); } #else /* LPC4 */ /* Standard sprintf has an internal buffer of 10000 bytes. To work around * this bug we call fast_linebreak a few times..... This also helps if you * don't have sprintf and is linebreaking something that has more words * than can be fitted into you arrays. */ string big_linebreak(string s,string pre,int width) { int e; string done,a,b; done=""; while(strlen(s)) { if(strlen(s)<5000) { e=5000; }else{ e=5000; while(e>=0 && s[e]!=' ') e--; if(e==-1) return done+" "+s+"\n"; } done+=fast_linebreak(s[0..e-1],pre,width); pre=SPACES[1..strlen(pre)]; s=s[e+1..strlen(s)-1]; for(e=strlen(done)-1;e>=0 && done[e]!='\n';e--); a=0; b=""; sscanf(s,"%s %s",a,b); while(a && strlen(a)+strlen(done)-e<=width+2) { done+=" "+a; s=b; a=0; b=""; sscanf(s,"%s %s",a,b); } done+="\n"; } return done; } #endif /* LPC4 */ int get() { return 1; } int drop() { return 1; } int id(string s) { return s=="soul"; } void move(object x) { move_object(this_object(),x); } #ifdef MUDOS mixed *m_indices(mapping m) { return keys(m); } mixed *m_values(mapping m) { return values(m); } mapping m_delete(mapping m, mixed elem) { map_delete(m, elem); return m; } int m_sizeof(mapping m) { return sizeof(m); } mapping mkmapping(mixed *indices,mixed *values) { mapping tmp; int e; tmp=allocate_mapping(sizeof(indices)); for(e=0; e"); input_to("more_flush"); } } string last_action; string parsed_part,unparsed_part,uncertain_part; mixed *brokendown_data; mapping brokendown_on_person; mixed *query_brokendown_data() { return brokendown_data; } string query_last_action() { return last_action; } mixed *query_feeling_for(object o) { int e,d; mixed *tmp; /* this way might be slower, but it can take a lot of action */ if(!brokendown_on_person) { brokendown_on_person=([]); for(e=1;eb) return 1; if(ab; #endif } string verb_string; string get_verb_string() { if(verb_string) return verb_string; return verb_string=share_string(linebreak(implode_nicely(SORT(m_indices(verbs))))); } string adverb_string; string get_adverb_string() { #ifndef LOW_EVAL_COST if(adverb_string) return adverb_string; return adverb_string=share_string(linebreak(implode_nicely(SORT(m_indices(adverbs))))); #else string *q; if(adverb_string) return adverb_string; q=(string *)low_get_adverbs(); return adverb_string=share_string(linebreak(implode_nicely(q))); #endif } string xverb_string; string get_xverb_string() { if(xverb_string) return xverb_string; return xverb_string=share_string(linebreak(implode_nicely(SORT(m_indices(xverbs))))); } string xadverb_string; string get_xadverb_string() { if(xadverb_string) return xadverb_string; return xadverb_string=share_string(linebreak(implode_nicely(SORT(m_indices(xadverbs))))); } void reset() { if(SOUL) return; SOUL=file_name(this_object()); sscanf(SOUL,"/%s",SOUL); sscanf(SOUL,"%s#",SOUL); verbs=(mapping)SOUL->get_verbs(); adverbs=(mapping)SOUL->get_adverbs(); how=(mapping)SOUL->get_how(); bodydata=(mapping)SOUL->get_bodydata(); messages=([]); xverbs=([]); xadverbs=([]); morestring=""; xverb_string=""; xadverb_string=""; } void create() { #ifdef USE_UID seteuid(getuid(this_player())); #endif reset(); } string globber_one_player(mapping ve); help(s) { string res; if(!s) return 0; switch(s) { case "feelings": more("General commands available:\n"); more(get_verb_string()); if(m_sizeof(xverbs)) { more("Extra commands available:\n"); more(get_xverb_string()); } more("grades:\n"+linebreak(implode_nicely(m_indices(how)))); more("All of these commands can be combinated with 'and' to make it\n"); more("possible to do several things in several ways to several people.\n"); more("All feelings can also be prepended with: suddenly, fail, again or dont\n"); more("Persons and adverbs can be shortened to shorted unique prefix.\n"); more("See also: help adverbs and help feeling list\n"); more_flush(); return 1; case "adverbs": more("Adverbs that can be used together with feeling-commands:\n"); more(get_adverb_string()); more_flush(); if(m_sizeof(xadverbs)) { more("Extra adverbs available:\n"); more(get_xadverb_string()); } return 1; case "feeling list": if(!(res=SOUL->query_total_list())) { res=globber_one_player(verbs); SOUL->set_total_list(res); if(m_sizeof(xverbs)) { res+=" (try again)\n"; } }else{ res+=globber_one_player(xverbs); } res="Verb Short description\n"+res; more(res); more_flush(); return 1; case "soul version": write("Soul version 1.2, written by hubbe@lysator.liu.se.\n"); return 1; } } string get_name(object o) { return NAME(o); } /* This function should return 1 if the object is a player or monster * that is not invisible. */ int isplay(object o) { return ISLIVING(o) && ISVISIBLE(o); } object *get_persons() { return filter_array(all_inventory(environment(this_player())), "isplay",this_object()); } mixed prefix(string *dum,string pr,string errm) { string *q; int len; /* pr=replace(pr,"\\","\\\\"); pr=replace(pr,"(","\\("); pr=replace(pr,")","\\"); pr=replace(pr,"[","\\["); pr=replace(pr,"]","\\["); pr=replace(pr,".","\\."); pr=replace(pr,"*","\\*"); pr=replace(pr,"+","\\+"); pr=replace(pr,"|","\\|"); No need to regexp on wierd characters. Mats 960925 */ len = strlen(pr); while(len--) { if ((pr[len] < 'a' || pr[len] > 'z') && pr[len]!=' ') return 0; } q=regexp(dum,"^"+pr); if (!pointerp(q) || !sizeof(q)) return 0; /* Brom 960415 */ if(sizeof(q)==1) return q[0]; #ifdef HAVE_SPRINTF notify_fail(sprintf("%s\n%-#79s\n",errm,implode(q,"\n"))); #else notify_fail(errm+"\n"+linebreak(implode_nicely(q,"or"))); #endif return -1; } string WHO(object o,object who) { if(who==o) { if(o==this_player()) return "yourself"; else return "you"; }else{ if(o==this_player()) return OBJECTIVE(o)+"self"; else return CAP_NAME(o); } } string POSS(object o,object who) { if(who==o) { if(o==this_player()) return "your own"; else return "your"; }else{ if(o==this_player()) return POSSESSIVE(o)+" own"; else { string s; s=CAP_NAME(o); if(s[strlen(s)-1]=='s') return s+"'"; else return s+"'s"; } } } string gloerp(string q,object *t,mixed who,int prev) { string *s,b,mess; int e; s=my_explode(q,"\n"); mess=s[0]; for(e=1;e1) { if(member_array(who,t)!=-1) mess+="your"+b; else mess+="their"+b; }else{ if(t[0]==who) mess+="your"+b; else mess+=POSSESSIVE(t[0])+b; } }else if(sscanf(s[e],"OBJ%s",b) || (prev && sscanf(s[e],"WHO%s",b))){ if(sizeof(t)>1) { if(member_array(who,t)!=-1) mess+="all of you"+b; else mess+="them"+b; }else{ if(t[0]==who) { if(who==this_player()) mess+="yourself"+b; else mess+="you"+b; }else{ if(t[0]==this_player()) mess+=OBJECTIVE(this_player())+"self"+b; else mess+=OBJECTIVE(t[0])+b; } } }else if(sscanf(s[e],"SUBJ%s",b)){ if(sizeof(t)>1) { if(member_array(who,t)!=-1) mess+="you"+b; else mess+="they"+b; }else{ if(t[0]==who) mess+="you"+b; else mess+=PRONOUN(t[0])+b; } }else if(sscanf(s[e],"IS%s",b)){ if(member_array(who,t)!=-1) mess+="are"+b; else if(sizeof(t)<=1) mess+="is"+b; else mess+="are"+b; }else{ mess+="\n"+s[e]; } } return mess; } /* Output parsed feeling to */ feel(mixed *d,int flag) { int e,w,prev; object tp; mixed *q; q=({}); for(e=0;ereduce_verb(verb,who,adverb,mess,body); } if(pointerp(q=verbdata[1])) { if(!sizeof(adverb) && sizeof(q)>0 && q[0]) adverb=({q[0]}); if((!mess || mess=="") && sizeof(q)>1 && q[1]) { mess=q[1]; if(mess[0]=='\'') mess=msg=" "+extract(mess,1); } if(!sizeof(body) && sizeof(q)>2 && q[2]) body=({q[2]}); } if(!mess || mess=="") { mess=""; if(!msg) msg=""; }else{ if(!msg) msg=" '"+mess+"'"; mess=" "+mess; } where=""; if(sizeof(body)) where=" "+implode_nicely(body); how=implode_nicely(SUB_ARRAY(adverb,({""}))); switch(verbdata[0]) { case DEFA: if(!a) a=" "+verb+"$ \nHOW \nAT"; case PREV: if(!a) a=" "+verb+"$"+verbdata[2]+" \nWHO \nHOW"; case PHYS: if(!a) a=" "+verb+"$"+verbdata[2]+" \nWHO \nHOW \nWHERE"; case SHRT: if(!a) a=" "+verb+"$"+verbdata[2]+" \nHOW"; case PERS: if(!a) if(sizeof(who)) a=verbdata[3]; else a=verbdata[2]; case SIMP: if(!a) a=verbdata[2]; if(sizeof(who) && sizeof(verbdata)>3) { a=replace(a," \nAT",verbdata[3]+" \nWHO"); }else{ a=replace(a," \nAT",""); } if(!sizeof(who) && (sscanf(a,"%s\nWHO",c) || sscanf(a,"%s\nPOSS",c) || sscanf(a,"%s\nTHEIR",c) || sscanf(a,"%s\nOBJ",c))) return notify_fail("Need person for verb "+verb+".\n"),0; if(how=="") { a=replace(a," \nHOW",""); }else{ a=replace(a," \nHOW"," "+how); } a=replace(a," \nWHERE",where); a=replace(a," \nWHAT",mess); a=replace(a," \nMSG",msg); b=a; a=replace(a,"$",""); b=replace(b,"$","s"); return ({({who,a,b,b,a,a,a})}); case DEUX: a=verbdata[2]; b=verbdata[3]; if(!sizeof(who) && (sscanf(a,"%s\nWHO",c) || sscanf(a,"%s\nPOSS",c) || sscanf(a,"%s\nTHEIR",c) || sscanf(a,"%s\nOBJ",c))) return notify_fail("Need person for verb "+verb+".\n"),0; a=replace(a," \nWHERE",where); b=replace(b," \nWHERE",where); a=replace(a," \nWHAT",mess); a=replace(a," \nMSG",msg); b=replace(b," \nWHAT",mess); b=replace(b," \nMSG",msg); if(how=="") { a=replace(a," \nHOW",""); b=replace(b," \nHOW",""); }else{ a=replace(a," \nHOW"," "+how); b=replace(b," \nHOW"," "+how); } return ({({who,a,b,b,a,a,a})}); case QUAD: if(!sizeof(who)) { a=verbdata[2]; b=verbdata[3]; }else{ a=verbdata[4]; b=verbdata[5]; } a=replace(a," \nWHERE",where); b=replace(b," \nWHERE",where); a=replace(a," \nWHAT",mess); a=replace(a," \nMSG",msg); b=replace(b," \nWHAT",mess); b=replace(b," \nMSG",msg); if(how=="") { a=replace(a," \nHOW",""); b=replace(b," \nHOW",""); }else{ a=replace(a," \nHOW"," "+how); b=replace(b," \nHOW"," "+how); } return ({({who,a,b,b,a,a,a})}); case FULL: if(!sizeof(who)) { aa=verbdata[2..7]; }else{ aa=verbdata[8..13]; } for(e=0;e