blob: 84a9ef8e07b763fec392d2d2ff3e766e416be58e [file] [log] [blame]
MG Mud User88f12472016-06-24 23:31:02 +02001// MorgenGrauen MUDlib
2//
3// living/moving.c -- moving of living objects
4//
5// $Id: moving.c 9448 2016-01-22 17:52:28Z Zesstra $
Vanion50652322020-03-10 21:13:25 +01006#pragma strict_types
MG Mud User88f12472016-06-24 23:31:02 +02007#pragma save_types
8#pragma range_check
9#pragma no_clone
MG Mud User88f12472016-06-24 23:31:02 +020010
11inherit "/std/thing/moving";
12
13#define NEED_PROTOTYPES
14#include <hook.h>
15#include <living/moving.h>
16#include <living/skills.h>
17#include <thing/properties.h>
18#include <thing/description.h>
19#include <moving.h>
20#include <new_skills.h>
21#include <living.h>
22
23#undef NEED_PROTOTYPES
24
25#include <config.h>
26#include <properties.h>
27#include <language.h>
28#include <wizlevels.h>
29#include <defines.h>
30
31
32protected void create()
33{
34 if (object_name(this_object()) == __FILE__[0..<3])
35 {
36 return;
37 }
38 offerHook(H_HOOK_MOVE,1);
39}
40
41public void AddPursuer(object ob)
42{
43 mixed *pur;
44
45 if (!objectp(ob))
46 return;
47
48 if (!pointerp(pur=Query(P_PURSUERS)))
49 pur=({0,({})});
50 else if (member(pur[1],ob)!=-1)
51 return;
52
53 SetProp(P_PURSUERS,({ pur[0], pur[1]+({ob})-({0}) }));
bugfixd94d0932020-04-08 11:27:13 +020054 ({void})ob->_SetPursued(ME);
MG Mud User88f12472016-06-24 23:31:02 +020055}
56
57public void RemovePursuer(object ob)
58{
59 mixed *pur;
60
61 if (pointerp(pur=Query(P_PURSUERS,F_VALUE))
62 && member(pur[1],ob)!=-1)
63 {
64 pur[1]-=({ob,0});
65 if (ob)
bugfixd94d0932020-04-08 11:27:13 +020066 ({void})ob->_RemovePursued(ME);
MG Mud User88f12472016-06-24 23:31:02 +020067 if (!pur[0]&&!sizeof(pur[1]))
68 pur=0;
69 SetProp(P_PURSUERS,pur);
70 }
71}
72
73public void _SetPursued(object ob)
74{
75 mixed *pur;
76
77 if (!pointerp(pur=Query(P_PURSUERS)))
78 pur=({0,({})});
79 else
80 if (objectp(pur[0]))
bugfixd94d0932020-04-08 11:27:13 +020081 ({void})pur[0]->RemovePursuer(ME);
MG Mud User88f12472016-06-24 23:31:02 +020082 pur[0]=ob;
83 pur[1]-=({0});
84 Set(P_PURSUERS,pur);
85}
86
87public void _RemovePursued(object ob)
88{
89 mixed *pur;
90
91 if (!pointerp(pur=Query(P_PURSUERS)) || pur[0]!=ob)
92 return;
93 pur[0]=0;
94 pur[1]-=({0});
95 if (!sizeof(pur[1]))
96 pur=0;
97 Set(P_PURSUERS,pur);
98}
99
100
101private void kampfende( object en ) {
102 if (!objectp(en)) return;
bugfixd94d0932020-04-08 11:27:13 +0200103 tell_object( ME, capitalize(({string})en->name()) +
MG Mud User88f12472016-06-24 23:31:02 +0200104 " ist jetzt hinter Dir her.\n" );
105 tell_object( en, "Du verfolgst jetzt " + name(WEN) + ".\n" );
bugfixd94d0932020-04-08 11:27:13 +0200106 ({int})en->InsertSingleEnemy(ME);
MG Mud User88f12472016-06-24 23:31:02 +0200107}
108
109private int _is_learner(object pl) {
110 return IS_LEARNER(pl);
111}
112
113
114// a) Pruefungen, ob das move erlaubt ist.
115// b) zum Ueberschreiben
116protected int PreventMove(object dest, object oldenv, int method) {
117
118 // M_NOCHECK? -> Bewegung eh erlaubt (und Rueckgabewert wuerde ignoriert),
119 // aber PreventInsert/PreventLeave() rufen und ignorieren.
120 if ((method&M_NOCHECK)) {
121 // erst PreventLeaveLiving() rufen...
122 if(environment())
bugfixd94d0932020-04-08 11:27:13 +0200123 ({int})environment()->PreventLeaveLiving(this_object(), dest);
MG Mud User88f12472016-06-24 23:31:02 +0200124 // dann PreventInsertLiving() im Ziel-Env.
bugfixd94d0932020-04-08 11:27:13 +0200125 ({int})dest->PreventInsertLiving(this_object());
MG Mud User88f12472016-06-24 23:31:02 +0200126 // und raus...
127 return(0);
128 }
129
130 // bei Lebewesen muss die Bewegungsmethode M_GO und M_TPORT sein. Dies ist
131 // gleichzeigt die Restriktion gegen das Nehmen von Lebewesen, da dort
132 // M_GET/M_GIVE/M_PUT etc. verwendet wuerde. Bei M_GO und M_TPORT findet
133 // keine Pruefung statt, ob das Objekt ins Ziel 'reinpasst' (Gewicht, Anzahl
134 // Objekte usw.).
135 // Ich finde es etwas merkwuerdig gebaut (Zesstra).
136 if ( !(method & (M_GO | M_TPORT)) )
137 return ME_PLAYER;
138
139 // alte und neue Umgebung auf NO_TPORT pruefen.
140 if ( (method & M_TPORT) ) {
141 if ( environment() &&
bugfixd94d0932020-04-08 11:27:13 +0200142 (({int})environment()->QueryProp(P_NO_TPORT) & (NO_TPORT_OUT|NO_TPORT)) )
MG Mud User88f12472016-06-24 23:31:02 +0200143 return ME_CANT_TPORT_OUT;
bugfixd94d0932020-04-08 11:27:13 +0200144 else if ( ({int})dest->QueryProp(P_NO_TPORT) & (NO_TPORT_IN|NO_TPORT) )
MG Mud User88f12472016-06-24 23:31:02 +0200145 return ME_CANT_TPORT_IN;
146 }
147
148 // erst PreventLeaveLiving() testen...
bugfixd94d0932020-04-08 11:27:13 +0200149 if( environment() && ({int})environment()->PreventLeaveLiving(this_object(), dest))
MG Mud User88f12472016-06-24 23:31:02 +0200150 return ME_CANT_LEAVE_ENV;
151 // dann PreventInsertLiving() im Ziel-Env
bugfixd94d0932020-04-08 11:27:13 +0200152 if (({int})dest->PreventInsertLiving(this_object()))
MG Mud User88f12472016-06-24 23:31:02 +0200153 return ME_CANT_BE_INSERTED;
154
155 return 0;
156}
157
158// Krams nach dem Move machen und nebenbei zum Ueberschreiben.
159protected void NotifyMove(object dest, object oldenv, int method) {
160 mixed res;
161 object enem;
162
163 // Begruessungsschlag fuer die Gegener
164 if ( !(method & M_NO_ATTACK) )
165 InitAttack();
166
167 if (!objectp(ME)) return;
168
169 // Verfolger nachholen.
170 if ( pointerp(res = Query(P_PURSUERS)) && sizeof(res[1]) ) {
171 while ( remove_call_out( "TakeFollowers" ) >= 0 );
172
173 call_out( "TakeFollowers", 0 );
174 }
175
176 // und noch das Team nachholen.
177 if ( oldenv != dest
178 && objectp(ME)
179 && QueryProp(P_TEAM_AUTOFOLLOW)
180 && objectp( enem = IsTeamLeader() ) )
bugfixd94d0932020-04-08 11:27:13 +0200181 ({void})enem->StartFollow(oldenv); // Teamverfolgung
MG Mud User88f12472016-06-24 23:31:02 +0200182
183}
184
185varargs public int move( object|string dest, int method, string direction,
186 string textout, string textin )
187{
Arathorneae85b92021-05-13 21:19:57 +0200188 int para, invis, tmp;
MG Mud User88f12472016-06-24 23:31:02 +0200189 object oldenv, *inv;
190 string fn,vc;
191 mixed res;
192 mixed hookData, hookRes;
193
194 if (!objectp(dest) && !stringp(dest))
195 raise_error(sprintf("Wrong argument 1 to move(). 'dest' must be a "
196 "string or object! Argument was: %.100O\n",
197 dest));
198
199 // altes Env erstmal merken.
200 oldenv = environment();
201
202 //erstmal den richtigen Zielraum suchen, bevor irgendwelche Checks gemacht
203 //werden...
204 // Ist der Spieler in einer Parallelwelt?
205 if ( (para = QueryProp(P_PARA)) && intp(para) ) {
206 fn = objectp(dest) ? object_name(dest) : dest;
207
208 // Falls der Zielraum nicht schon explizit in der Parallelwelt ist,
209 // neuen Zielraum suchen. Aber nur, wenn fn kein # enthaelt (also kein
210 // Clone ist), sonst wuerde eine Bewegung nach raum#42^para versucht,
211 // was dann buggt. ;-) Problem wird offenbar, wenn ein Para-Lebewesen
212 // im create() eines VC-Raums in Para in den Raum bewegt wird, da
213 // dieser dann noch nicht vom Driver umbenannt wurde und raum#42
214 // heisst.
215 if ( !sizeof(regexp( ({ fn }), "\\^[1-9][0-9]*$" )) &&
216 strrstr(fn,"#")==-1 )
217 {
218 fn += "^" + para;
219
220 // Der Parallelwelt-Raum muss existieren und fuer Spieler
221 // freigegeben sein, damit er zum neuen Ziel wird. Ansonsten
222 // duerfen nur NPCs, Testspieler und Magier herein.
223 if ( (find_object(fn)
224 || ((file_size(fn+".c")>0 ||
225 (file_size(vc=implode(explode(fn,"/")[0..<2],"/")+
226 "/virtual_compiler.c")>0 &&
Vanion50652322020-03-10 21:13:25 +0100227 !catch(tmp=({int})call_other(vc,"QueryValidObject",fn);
MG Mud User88f12472016-06-24 23:31:02 +0200228 publish) && tmp>0)) &&
229 !catch(load_object(fn);publish) )) &&
bugfixd94d0932020-04-08 11:27:13 +0200230 (!interactive(ME) || !({int})fn->QueryProp(P_NO_PLAYERS) ||
MG Mud User88f12472016-06-24 23:31:02 +0200231 (method & M_NOCHECK) || IS_LEARNER(ME) ||
232 (stringp(res = QueryProp(P_TESTPLAYER)) &&
233 IS_LEARNER( lower_case(res) ))) )
234 {
235 dest = fn;
236 }
237 else
238 {
239 // Wir bleiben in der Normalwelt.
240 para = 0;
241 }
242 }
243 }
244
245 // jetzt erstmal Hooks abpruefen, da sie ggf. die Daten aendern.
246 // alten P_TMP_MOVE_HOOK pruefen.
247 if ( res = QueryProp(P_TMP_MOVE_HOOK) ){
248 if ( pointerp(res) && sizeof(res) >= 3
249 && intp(res[0]) && time()<res[0]
250 && objectp(res[1]) && stringp(res[2]) ){
bugfixd94d0932020-04-08 11:27:13 +0200251 if ( res = ({mixed})call_other( res[1], res[2], dest, method, direction,
MG Mud User88f12472016-06-24 23:31:02 +0200252 textout, textin ) ){
253 if ( pointerp(res) && sizeof(res) == 5 ){
254 dest = res[0];
255 method = res[1];
256 direction = res[2];
257 textout = res[3];
258 textin = res[4];
259 }
260 else if ( intp(res) && res == -1 )
261 return ME_CANT_LEAVE_ENV;
262 }
263 } else
264 SetProp( P_TMP_MOVE_HOOK, 0 );
265 }
266 // move hook nach neuem Hooksystem triggern.
267 hookData=({dest,method,direction,textout,textin});
268 hookRes=HookFlow(H_HOOK_MOVE,hookData);
269 if(hookRes && pointerp(hookRes) && sizeof(hookRes)>H_RETDATA) {
270 if(hookRes[H_RETCODE]==H_CANCELLED) {
271 return ME_CANT_LEAVE_ENV;
272 }
273 else if(hookRes[H_RETCODE]==H_ALTERED && hookRes[H_RETDATA] &&
274 pointerp(hookRes[H_RETDATA]) && sizeof(hookRes[H_RETDATA])>=5 ){
275 dest = hookRes[H_RETDATA][0];
276 method = hookRes[H_RETDATA][1];
277 direction = hookRes[H_RETDATA][2];
278 textout = hookRes[H_RETDATA][3];
279 textin = hookRes[H_RETDATA][4];
280 }
281 }
282
283 // dest auf Object normieren
284 if (stringp(dest)) dest=load_object(dest);
285
286 // jetzt Checks durchfuehren, ob das Move durchgefuehrt werden darf.
287 if (tmp=PreventMove(dest, oldenv, method)) {
288 // auf gueltigen Fehler pruefen, wer weiss, was Magier da evtl.
289 // versehentlich zurueckgeben.
290 if (VALID_MOVE_ERROR(tmp))
291 return(tmp);
292 else
293 return(ME_DONT_WANT_TO_BE_MOVED);
294 }
295
296 if ( invis = QueryProp(P_INVIS) )
297 method |= M_SILENT;
298
299 if ( objectp(oldenv) ) {
300 if ( !(method & M_SILENT) ) {
301 string *mout;
302 if ( !textout ){
303 if ( method & M_TPORT )
Vanion50652322020-03-10 21:13:25 +0100304 textout = ({string}) QueryProp(P_MMSGOUT) ||
305 ({string}) QueryProp(P_MSGOUT);
MG Mud User88f12472016-06-24 23:31:02 +0200306 else
Vanion50652322020-03-10 21:13:25 +0100307 textout = (mout = explode( ({string})
MG Mud User88f12472016-06-24 23:31:02 +0200308 QueryProp(P_MSGOUT) || "",
309 "#" ))[0]
Vanion50652322020-03-10 21:13:25 +0100310 || ({string})QueryProp(P_MMSGOUT);
MG Mud User88f12472016-06-24 23:31:02 +0200311 }
312
313 if ( !sizeof(direction) )
314 direction = 0;
315
316 inv = all_inventory(environment()) - ({ this_object() });
317 inv = filter( inv, #'living/*'*/ );
318 inv -= filter_objects( inv, "CannotSee", 1 );
319
320 filter( inv, #'tell_object/*'*/,
321 Name( WER, 2 ) + " " + textout +
322 (direction ? " " + direction : "") +
323 (sizeof(mout) > 1 ? mout[1] : "") + ".\n" );
324 }
325 // Magier sehen auch Bewegungen, die M_SILENT sind
326 else if ( interactive(ME) ){
327 inv = (all_inventory(environment()) & users())
328 - ({ this_object() });
329 inv = filter( inv, #'_is_learner/*'*/ );
330
331 if ( invis )
332 fn = "(" + capitalize(getuid(ME)) + ") verschwindet "
333 "unsichtbar.\n";
334 else
335 fn = capitalize(getuid(ME)) + " verschwindet ganz leise.\n";
336
337 filter( inv, #'tell_object/*'*/, fn );
338 }
339
340 // Nackenschlag beim Fluechten:
341 if ( !(method & M_NO_ATTACK) && objectp(ME) )
342 ExitAttack();
343 //falls nach ExitAttack() das Living nicht mehr existiert, muss das
344 //move() auch nicht mehr fortgesetzt werden. Weiter unten gibt es auch
345 //min. eine Stelle, die nicht prueft und ggf. buggt. Daher erfolgt
346 //hier ggf. ein Abbruch. 15.11.06 Zesstra
347 if (!objectp(ME)) return(ME_WAS_DESTRUCTED);
348
349 // Nackenschlag kann ME in den Todesraum bewegt haben...
350 if ( oldenv == environment() ) {
351 // Fuer alle anwesenden gegner kampfende() aufrufen
352 filter((QueryEnemies()[0] & all_inventory(oldenv))-({0}),
353 #'kampfende);
354 // Bugs im exit() sind ohne catch() einfach mist.
bugfixd94d0932020-04-08 11:27:13 +0200355 catch(({void})environment()->exit(ME, dest);publish);
MG Mud User88f12472016-06-24 23:31:02 +0200356 }
357 }
358
359 // irgendwas kann das Objekt zerstoert haben, z.B. env->exit().
360 if (!objectp(ME)) return(ME_WAS_DESTRUCTED);
361
362 if ( oldenv != environment() )
363 // Der Nackenschlag oder exit() koennen einen schon bewegt haben.
364 // Und wenn es in den Todesraum ist. ;^)
365 return MOVE_OK;
366
367 SetProp( P_PREPARED_SPELL, 0 ); // Spruchvorbereitung abgebrochen
368 SetProp( P_LAST_MOVE, time() ); // Zeitpunkt der letzten Bewegung
369
370 move_object(ME, dest);
371 if (!objectp(ME))
372 return(ME_WAS_DESTRUCTED);
373
374 dest = environment();
Arathorneae85b92021-05-13 21:19:57 +0200375
376 UseSkill(SK_NIGHTVISION);
377
MG Mud User88f12472016-06-24 23:31:02 +0200378 // Meldungen an nicht-Blinde ausgeben, falls keine 'stille' Bewegung
379 if ( !(method & M_SILENT) ) {
380 if ( !textin ) {
381 if ( method & M_TPORT )
Vanion50652322020-03-10 21:13:25 +0100382 textin = ({string}) QueryProp(P_MMSGIN);
MG Mud User88f12472016-06-24 23:31:02 +0200383 else
Vanion50652322020-03-10 21:13:25 +0100384 textin = ({string}) QueryProp(P_MSGIN);
MG Mud User88f12472016-06-24 23:31:02 +0200385 }
386
387 inv = all_inventory(environment()) - ({ this_object() });
388 inv = filter( inv, #'living/*'*/ );
389 inv -= filter_objects( inv, "CannotSee", 1 );
390 filter( inv, #'tell_object/*'*/,
391 capitalize(name( WER, 0 )) + " " + textin + ".\n" );
392 }
393 // sonst: Magier sehen auch M_SILENT-Bewegungen, hier also nur an Magier
394 // ausgeben, alle anderen sehen eh nix.
395 else if ( interactive(ME) ) {
396 inv = (all_inventory(environment()) & users()) - ({this_object()});
397 inv = filter( inv, #'_is_learner/*'*/ );
398 if ( invis )
399 fn = "(" + capitalize(getuid(ME)) + ") taucht unsichtbar auf.\n";
400 else
401 fn = capitalize(getuid(ME)) + " schleicht leise herein.\n";
402 filter( inv, #'tell_object, fn );
403 }
404
405 // "Objekt" ueber das Move informieren.
406 NotifyMove(dest, oldenv, method);
407
408 // InitAttack() in NotifyMove() kann das Objekt zerstoert haben.
409 if (!objectp(ME))
410 return(ME_WAS_DESTRUCTED);
411
412 //scheint wohl geklappt zu haben.
413 return MOVE_OK;
414}
415
416public void TakeFollowers()
417{
418 mixed *f,env;
Arathorneae85b92021-05-13 21:19:57 +0200419 int meth,r;
MG Mud User88f12472016-06-24 23:31:02 +0200420
421 f=Query(P_PURSUERS);
422 if (!pointerp(f))
423 return;
424 env=environment();
425 if(object_name(env) == "/room/netztot") return;
426 foreach(object follower: f[1]-({0}) ) {
427 // die pruefung auf objectp ist nicht verrueckt, es kann theo. sein, dass
428 // Verfolger im PreventFollow() oder in ihrem move/init andere Verfolger
429 // zerstoeren.
430 if (objectp(follower) && environment(follower)!=env) {
431 //meth=M_NOCHECK;
432 meth=M_GO;
bugfixd94d0932020-04-08 11:27:13 +0200433 if (({int})follower->Query(P_FOLLOW_SILENT))
MG Mud User88f12472016-06-24 23:31:02 +0200434 meth|=M_SILENT|M_NO_SHOW;
bugfixd94d0932020-04-08 11:27:13 +0200435 catch(r=({int})follower->PreventFollow(env);publish);
MG Mud User88f12472016-06-24 23:31:02 +0200436 if (!r)
bugfixd94d0932020-04-08 11:27:13 +0200437 ({int})follower->move(env,meth);
MG Mud User88f12472016-06-24 23:31:02 +0200438 else if (r==2)
439 RemovePursuer(follower);
440 }
441 }
442}
443
444varargs public int remove()
445{ object team;
446
447 if (environment())
448 {
449 if ( objectp(team=Query(P_TEAM)) )
bugfixd94d0932020-04-08 11:27:13 +0200450 catch(({int})team->RemoveMember(ME);publish);
MG Mud User88f12472016-06-24 23:31:02 +0200451
bugfixd94d0932020-04-08 11:27:13 +0200452 ({void})environment()->NotifyRemove(ME);
MG Mud User88f12472016-06-24 23:31:02 +0200453 }
454 destruct(ME);
455 return 1;
456}
457