| // MorgenGrauen MUDlib |
| // |
| // living/moving.c -- moving of living objects |
| // |
| // $Id: moving.c 9448 2016-01-22 17:52:28Z Zesstra $ |
| #pragma strong_types |
| #pragma save_types |
| #pragma range_check |
| #pragma no_clone |
| #pragma pedantic |
| |
| inherit "/std/thing/moving"; |
| |
| #define NEED_PROTOTYPES |
| #include <hook.h> |
| #include <living/moving.h> |
| #include <living/skills.h> |
| #include <thing/properties.h> |
| #include <thing/description.h> |
| #include <moving.h> |
| #include <new_skills.h> |
| #include <living.h> |
| |
| #undef NEED_PROTOTYPES |
| |
| #include <config.h> |
| #include <properties.h> |
| #include <language.h> |
| #include <wizlevels.h> |
| #include <defines.h> |
| |
| |
| protected void create() |
| { |
| if (object_name(this_object()) == __FILE__[0..<3]) |
| { |
| return; |
| } |
| offerHook(H_HOOK_MOVE,1); |
| } |
| |
| public void AddPursuer(object ob) |
| { |
| mixed *pur; |
| |
| if (!objectp(ob)) |
| return; |
| |
| if (!pointerp(pur=Query(P_PURSUERS))) |
| pur=({0,({})}); |
| else if (member(pur[1],ob)!=-1) |
| return; |
| |
| SetProp(P_PURSUERS,({ pur[0], pur[1]+({ob})-({0}) })); |
| ob->_SetPursued(ME); |
| } |
| |
| public void RemovePursuer(object ob) |
| { |
| mixed *pur; |
| |
| if (pointerp(pur=Query(P_PURSUERS,F_VALUE)) |
| && member(pur[1],ob)!=-1) |
| { |
| pur[1]-=({ob,0}); |
| if (ob) |
| ob->_RemovePursued(ME); |
| if (!pur[0]&&!sizeof(pur[1])) |
| pur=0; |
| SetProp(P_PURSUERS,pur); |
| } |
| } |
| |
| public void _SetPursued(object ob) |
| { |
| mixed *pur; |
| |
| if (!pointerp(pur=Query(P_PURSUERS))) |
| pur=({0,({})}); |
| else |
| if (objectp(pur[0])) |
| pur[0]->RemovePursuer(ME); |
| pur[0]=ob; |
| pur[1]-=({0}); |
| Set(P_PURSUERS,pur); |
| } |
| |
| public void _RemovePursued(object ob) |
| { |
| mixed *pur; |
| |
| if (!pointerp(pur=Query(P_PURSUERS)) || pur[0]!=ob) |
| return; |
| pur[0]=0; |
| pur[1]-=({0}); |
| if (!sizeof(pur[1])) |
| pur=0; |
| Set(P_PURSUERS,pur); |
| } |
| |
| |
| private void kampfende( object en ) { |
| if (!objectp(en)) return; |
| tell_object( ME, capitalize(en->name()) + |
| " ist jetzt hinter Dir her.\n" ); |
| tell_object( en, "Du verfolgst jetzt " + name(WEN) + ".\n" ); |
| en->InsertSingleEnemy(ME); |
| } |
| |
| private int _is_learner(object pl) { |
| return IS_LEARNER(pl); |
| } |
| |
| |
| // a) Pruefungen, ob das move erlaubt ist. |
| // b) zum Ueberschreiben |
| protected int PreventMove(object dest, object oldenv, int method) { |
| |
| // M_NOCHECK? -> Bewegung eh erlaubt (und Rueckgabewert wuerde ignoriert), |
| // aber PreventInsert/PreventLeave() rufen und ignorieren. |
| if ((method&M_NOCHECK)) { |
| // erst PreventLeaveLiving() rufen... |
| if(environment()) |
| environment()->PreventLeaveLiving(this_object(), dest); |
| // dann PreventInsertLiving() im Ziel-Env. |
| dest->PreventInsertLiving(this_object()); |
| // und raus... |
| return(0); |
| } |
| |
| // bei Lebewesen muss die Bewegungsmethode M_GO und M_TPORT sein. Dies ist |
| // gleichzeigt die Restriktion gegen das Nehmen von Lebewesen, da dort |
| // M_GET/M_GIVE/M_PUT etc. verwendet wuerde. Bei M_GO und M_TPORT findet |
| // keine Pruefung statt, ob das Objekt ins Ziel 'reinpasst' (Gewicht, Anzahl |
| // Objekte usw.). |
| // Ich finde es etwas merkwuerdig gebaut (Zesstra). |
| if ( !(method & (M_GO | M_TPORT)) ) |
| return ME_PLAYER; |
| |
| // alte und neue Umgebung auf NO_TPORT pruefen. |
| if ( (method & M_TPORT) ) { |
| if ( environment() && |
| (environment()->QueryProp(P_NO_TPORT) & (NO_TPORT_OUT|NO_TPORT)) ) |
| return ME_CANT_TPORT_OUT; |
| else if ( dest->QueryProp(P_NO_TPORT) & (NO_TPORT_IN|NO_TPORT) ) |
| return ME_CANT_TPORT_IN; |
| } |
| |
| // erst PreventLeaveLiving() testen... |
| if( environment() && environment()->PreventLeaveLiving(this_object(), dest)) |
| return ME_CANT_LEAVE_ENV; |
| // dann PreventInsertLiving() im Ziel-Env |
| if (dest->PreventInsertLiving(this_object())) |
| return ME_CANT_BE_INSERTED; |
| |
| return 0; |
| } |
| |
| // Krams nach dem Move machen und nebenbei zum Ueberschreiben. |
| protected void NotifyMove(object dest, object oldenv, int method) { |
| mixed res; |
| object enem; |
| |
| // Begruessungsschlag fuer die Gegener |
| if ( !(method & M_NO_ATTACK) ) |
| InitAttack(); |
| |
| if (!objectp(ME)) return; |
| |
| // Verfolger nachholen. |
| if ( pointerp(res = Query(P_PURSUERS)) && sizeof(res[1]) ) { |
| while ( remove_call_out( "TakeFollowers" ) >= 0 ); |
| |
| call_out( "TakeFollowers", 0 ); |
| } |
| |
| // und noch das Team nachholen. |
| if ( oldenv != dest |
| && objectp(ME) |
| && QueryProp(P_TEAM_AUTOFOLLOW) |
| && objectp( enem = IsTeamLeader() ) ) |
| enem->StartFollow(oldenv); // Teamverfolgung |
| |
| } |
| |
| varargs public int move( object|string dest, int method, string direction, |
| string textout, string textin ) |
| { |
| int para, nightvis, invis, tmp; |
| object oldenv, *inv; |
| string fn,vc; |
| mixed res; |
| mixed hookData, hookRes; |
| |
| if (!objectp(dest) && !stringp(dest)) |
| raise_error(sprintf("Wrong argument 1 to move(). 'dest' must be a " |
| "string or object! Argument was: %.100O\n", |
| dest)); |
| |
| // altes Env erstmal merken. |
| oldenv = environment(); |
| |
| //erstmal den richtigen Zielraum suchen, bevor irgendwelche Checks gemacht |
| //werden... |
| // Ist der Spieler in einer Parallelwelt? |
| if ( (para = QueryProp(P_PARA)) && intp(para) ) { |
| fn = objectp(dest) ? object_name(dest) : dest; |
| |
| // Falls der Zielraum nicht schon explizit in der Parallelwelt ist, |
| // neuen Zielraum suchen. Aber nur, wenn fn kein # enthaelt (also kein |
| // Clone ist), sonst wuerde eine Bewegung nach raum#42^para versucht, |
| // was dann buggt. ;-) Problem wird offenbar, wenn ein Para-Lebewesen |
| // im create() eines VC-Raums in Para in den Raum bewegt wird, da |
| // dieser dann noch nicht vom Driver umbenannt wurde und raum#42 |
| // heisst. |
| if ( !sizeof(regexp( ({ fn }), "\\^[1-9][0-9]*$" )) && |
| strrstr(fn,"#")==-1 ) |
| { |
| fn += "^" + para; |
| |
| // Der Parallelwelt-Raum muss existieren und fuer Spieler |
| // freigegeben sein, damit er zum neuen Ziel wird. Ansonsten |
| // duerfen nur NPCs, Testspieler und Magier herein. |
| if ( (find_object(fn) |
| || ((file_size(fn+".c")>0 || |
| (file_size(vc=implode(explode(fn,"/")[0..<2],"/")+ |
| "/virtual_compiler.c")>0 && |
| !catch(tmp=(int)call_other(vc,"QueryValidObject",fn); |
| publish) && tmp>0)) && |
| !catch(load_object(fn);publish) )) && |
| (!interactive(ME) || !fn->QueryProp(P_NO_PLAYERS) || |
| (method & M_NOCHECK) || IS_LEARNER(ME) || |
| (stringp(res = QueryProp(P_TESTPLAYER)) && |
| IS_LEARNER( lower_case(res) ))) ) |
| { |
| dest = fn; |
| } |
| else |
| { |
| // Wir bleiben in der Normalwelt. |
| para = 0; |
| } |
| } |
| } |
| |
| // jetzt erstmal Hooks abpruefen, da sie ggf. die Daten aendern. |
| // alten P_TMP_MOVE_HOOK pruefen. |
| if ( res = QueryProp(P_TMP_MOVE_HOOK) ){ |
| if ( pointerp(res) && sizeof(res) >= 3 |
| && intp(res[0]) && time()<res[0] |
| && objectp(res[1]) && stringp(res[2]) ){ |
| if ( res = call_other( res[1], res[2], dest, method, direction, |
| textout, textin ) ){ |
| if ( pointerp(res) && sizeof(res) == 5 ){ |
| dest = res[0]; |
| method = res[1]; |
| direction = res[2]; |
| textout = res[3]; |
| textin = res[4]; |
| } |
| else if ( intp(res) && res == -1 ) |
| return ME_CANT_LEAVE_ENV; |
| } |
| } else |
| SetProp( P_TMP_MOVE_HOOK, 0 ); |
| } |
| // move hook nach neuem Hooksystem triggern. |
| hookData=({dest,method,direction,textout,textin}); |
| hookRes=HookFlow(H_HOOK_MOVE,hookData); |
| if(hookRes && pointerp(hookRes) && sizeof(hookRes)>H_RETDATA) { |
| if(hookRes[H_RETCODE]==H_CANCELLED) { |
| return ME_CANT_LEAVE_ENV; |
| } |
| else if(hookRes[H_RETCODE]==H_ALTERED && hookRes[H_RETDATA] && |
| pointerp(hookRes[H_RETDATA]) && sizeof(hookRes[H_RETDATA])>=5 ){ |
| dest = hookRes[H_RETDATA][0]; |
| method = hookRes[H_RETDATA][1]; |
| direction = hookRes[H_RETDATA][2]; |
| textout = hookRes[H_RETDATA][3]; |
| textin = hookRes[H_RETDATA][4]; |
| } |
| } |
| |
| // dest auf Object normieren |
| if (stringp(dest)) dest=load_object(dest); |
| |
| // jetzt Checks durchfuehren, ob das Move durchgefuehrt werden darf. |
| if (tmp=PreventMove(dest, oldenv, method)) { |
| // auf gueltigen Fehler pruefen, wer weiss, was Magier da evtl. |
| // versehentlich zurueckgeben. |
| if (VALID_MOVE_ERROR(tmp)) |
| return(tmp); |
| else |
| return(ME_DONT_WANT_TO_BE_MOVED); |
| } |
| |
| if ( invis = QueryProp(P_INVIS) ) |
| method |= M_SILENT; |
| |
| if ( objectp(oldenv) ) { |
| if ( !(method & M_SILENT) ) { |
| string *mout; |
| if ( !textout ){ |
| if ( method & M_TPORT ) |
| textout = (string) QueryProp(P_MMSGOUT) || |
| (string) QueryProp(P_MSGOUT); |
| else |
| textout = (mout = explode( (string) |
| QueryProp(P_MSGOUT) || "", |
| "#" ))[0] |
| || (string)QueryProp(P_MMSGOUT); |
| } |
| |
| if ( !sizeof(direction) ) |
| direction = 0; |
| |
| inv = all_inventory(environment()) - ({ this_object() }); |
| inv = filter( inv, #'living/*'*/ ); |
| inv -= filter_objects( inv, "CannotSee", 1 ); |
| |
| filter( inv, #'tell_object/*'*/, |
| Name( WER, 2 ) + " " + textout + |
| (direction ? " " + direction : "") + |
| (sizeof(mout) > 1 ? mout[1] : "") + ".\n" ); |
| } |
| // Magier sehen auch Bewegungen, die M_SILENT sind |
| else if ( interactive(ME) ){ |
| inv = (all_inventory(environment()) & users()) |
| - ({ this_object() }); |
| inv = filter( inv, #'_is_learner/*'*/ ); |
| |
| if ( invis ) |
| fn = "(" + capitalize(getuid(ME)) + ") verschwindet " |
| "unsichtbar.\n"; |
| else |
| fn = capitalize(getuid(ME)) + " verschwindet ganz leise.\n"; |
| |
| filter( inv, #'tell_object/*'*/, fn ); |
| } |
| |
| // Nackenschlag beim Fluechten: |
| if ( !(method & M_NO_ATTACK) && objectp(ME) ) |
| ExitAttack(); |
| //falls nach ExitAttack() das Living nicht mehr existiert, muss das |
| //move() auch nicht mehr fortgesetzt werden. Weiter unten gibt es auch |
| //min. eine Stelle, die nicht prueft und ggf. buggt. Daher erfolgt |
| //hier ggf. ein Abbruch. 15.11.06 Zesstra |
| if (!objectp(ME)) return(ME_WAS_DESTRUCTED); |
| |
| // Nackenschlag kann ME in den Todesraum bewegt haben... |
| if ( oldenv == environment() ) { |
| // Fuer alle anwesenden gegner kampfende() aufrufen |
| filter((QueryEnemies()[0] & all_inventory(oldenv))-({0}), |
| #'kampfende); |
| // Bugs im exit() sind ohne catch() einfach mist. |
| catch(environment()->exit(ME, dest);publish); |
| } |
| } |
| |
| // irgendwas kann das Objekt zerstoert haben, z.B. env->exit(). |
| if (!objectp(ME)) return(ME_WAS_DESTRUCTED); |
| |
| if ( oldenv != environment() ) |
| // Der Nackenschlag oder exit() koennen einen schon bewegt haben. |
| // Und wenn es in den Todesraum ist. ;^) |
| return MOVE_OK; |
| |
| SetProp( P_PREPARED_SPELL, 0 ); // Spruchvorbereitung abgebrochen |
| SetProp( P_LAST_MOVE, time() ); // Zeitpunkt der letzten Bewegung |
| |
| move_object(ME, dest); |
| if (!objectp(ME)) |
| return(ME_WAS_DESTRUCTED); |
| |
| dest = environment(); |
| |
| nightvis = UseSkill(SK_NIGHTVISION); |
| // Meldungen an nicht-Blinde ausgeben, falls keine 'stille' Bewegung |
| if ( !(method & M_SILENT) ) { |
| if ( !textin ) { |
| if ( method & M_TPORT ) |
| textin = (string) QueryProp(P_MMSGIN); |
| else |
| textin = (string) QueryProp(P_MSGIN); |
| } |
| |
| inv = all_inventory(environment()) - ({ this_object() }); |
| inv = filter( inv, #'living/*'*/ ); |
| inv -= filter_objects( inv, "CannotSee", 1 ); |
| filter( inv, #'tell_object/*'*/, |
| capitalize(name( WER, 0 )) + " " + textin + ".\n" ); |
| } |
| // sonst: Magier sehen auch M_SILENT-Bewegungen, hier also nur an Magier |
| // ausgeben, alle anderen sehen eh nix. |
| else if ( interactive(ME) ) { |
| inv = (all_inventory(environment()) & users()) - ({this_object()}); |
| inv = filter( inv, #'_is_learner/*'*/ ); |
| if ( invis ) |
| fn = "(" + capitalize(getuid(ME)) + ") taucht unsichtbar auf.\n"; |
| else |
| fn = capitalize(getuid(ME)) + " schleicht leise herein.\n"; |
| filter( inv, #'tell_object, fn ); |
| } |
| |
| // "Objekt" ueber das Move informieren. |
| NotifyMove(dest, oldenv, method); |
| |
| // InitAttack() in NotifyMove() kann das Objekt zerstoert haben. |
| if (!objectp(ME)) |
| return(ME_WAS_DESTRUCTED); |
| |
| //scheint wohl geklappt zu haben. |
| return MOVE_OK; |
| } |
| |
| public void TakeFollowers() |
| { |
| mixed *f,env; |
| int meth,i,r; |
| |
| f=Query(P_PURSUERS); |
| if (!pointerp(f)) |
| return; |
| env=environment(); |
| if(object_name(env) == "/room/netztot") return; |
| foreach(object follower: f[1]-({0}) ) { |
| // die pruefung auf objectp ist nicht verrueckt, es kann theo. sein, dass |
| // Verfolger im PreventFollow() oder in ihrem move/init andere Verfolger |
| // zerstoeren. |
| if (objectp(follower) && environment(follower)!=env) { |
| //meth=M_NOCHECK; |
| meth=M_GO; |
| if (follower->Query(P_FOLLOW_SILENT)) |
| meth|=M_SILENT|M_NO_SHOW; |
| catch(r=follower->PreventFollow(env);publish); |
| if (!r) |
| follower->move(env,meth); |
| else if (r==2) |
| RemovePursuer(follower); |
| } |
| } |
| } |
| |
| varargs public int remove() |
| { object team; |
| |
| if (environment()) |
| { |
| if ( objectp(team=Query(P_TEAM)) ) |
| catch(team->RemoveMember(ME);publish); |
| |
| environment()->NotifyRemove(ME); |
| } |
| destruct(ME); |
| return 1; |
| } |
| |