blob: c6149ffa7cb8a72da273c9c62fddc7287dfad43d [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 $
6#pragma strong_types
7#pragma save_types
8#pragma range_check
9#pragma no_clone
10#pragma pedantic
11
12inherit "/std/thing/moving";
13
14#define NEED_PROTOTYPES
15#include <hook.h>
16#include <living/moving.h>
17#include <living/skills.h>
18#include <thing/properties.h>
19#include <thing/description.h>
20#include <moving.h>
21#include <new_skills.h>
22#include <living.h>
23
24#undef NEED_PROTOTYPES
25
26#include <config.h>
27#include <properties.h>
28#include <language.h>
29#include <wizlevels.h>
30#include <defines.h>
31
32
33protected void create()
34{
35 if (object_name(this_object()) == __FILE__[0..<3])
36 {
37 return;
38 }
39 offerHook(H_HOOK_MOVE,1);
40}
41
42public void AddPursuer(object ob)
43{
44 mixed *pur;
45
46 if (!objectp(ob))
47 return;
48
49 if (!pointerp(pur=Query(P_PURSUERS)))
50 pur=({0,({})});
51 else if (member(pur[1],ob)!=-1)
52 return;
53
54 SetProp(P_PURSUERS,({ pur[0], pur[1]+({ob})-({0}) }));
55 ob->_SetPursued(ME);
56}
57
58public void RemovePursuer(object ob)
59{
60 mixed *pur;
61
62 if (pointerp(pur=Query(P_PURSUERS,F_VALUE))
63 && member(pur[1],ob)!=-1)
64 {
65 pur[1]-=({ob,0});
66 if (ob)
67 ob->_RemovePursued(ME);
68 if (!pur[0]&&!sizeof(pur[1]))
69 pur=0;
70 SetProp(P_PURSUERS,pur);
71 }
72}
73
74public void _SetPursued(object ob)
75{
76 mixed *pur;
77
78 if (!pointerp(pur=Query(P_PURSUERS)))
79 pur=({0,({})});
80 else
81 if (objectp(pur[0]))
82 pur[0]->RemovePursuer(ME);
83 pur[0]=ob;
84 pur[1]-=({0});
85 Set(P_PURSUERS,pur);
86}
87
88public void _RemovePursued(object ob)
89{
90 mixed *pur;
91
92 if (!pointerp(pur=Query(P_PURSUERS)) || pur[0]!=ob)
93 return;
94 pur[0]=0;
95 pur[1]-=({0});
96 if (!sizeof(pur[1]))
97 pur=0;
98 Set(P_PURSUERS,pur);
99}
100
101
102private void kampfende( object en ) {
103 if (!objectp(en)) return;
104 tell_object( ME, capitalize(en->name()) +
105 " ist jetzt hinter Dir her.\n" );
106 tell_object( en, "Du verfolgst jetzt " + name(WEN) + ".\n" );
107 en->InsertSingleEnemy(ME);
108}
109
110private int _is_learner(object pl) {
111 return IS_LEARNER(pl);
112}
113
114
115// a) Pruefungen, ob das move erlaubt ist.
116// b) zum Ueberschreiben
117protected int PreventMove(object dest, object oldenv, int method) {
118
119 // M_NOCHECK? -> Bewegung eh erlaubt (und Rueckgabewert wuerde ignoriert),
120 // aber PreventInsert/PreventLeave() rufen und ignorieren.
121 if ((method&M_NOCHECK)) {
122 // erst PreventLeaveLiving() rufen...
123 if(environment())
124 environment()->PreventLeaveLiving(this_object(), dest);
125 // dann PreventInsertLiving() im Ziel-Env.
126 dest->PreventInsertLiving(this_object());
127 // und raus...
128 return(0);
129 }
130
131 // bei Lebewesen muss die Bewegungsmethode M_GO und M_TPORT sein. Dies ist
132 // gleichzeigt die Restriktion gegen das Nehmen von Lebewesen, da dort
133 // M_GET/M_GIVE/M_PUT etc. verwendet wuerde. Bei M_GO und M_TPORT findet
134 // keine Pruefung statt, ob das Objekt ins Ziel 'reinpasst' (Gewicht, Anzahl
135 // Objekte usw.).
136 // Ich finde es etwas merkwuerdig gebaut (Zesstra).
137 if ( !(method & (M_GO | M_TPORT)) )
138 return ME_PLAYER;
139
140 // alte und neue Umgebung auf NO_TPORT pruefen.
141 if ( (method & M_TPORT) ) {
142 if ( environment() &&
143 (environment()->QueryProp(P_NO_TPORT) & (NO_TPORT_OUT|NO_TPORT)) )
144 return ME_CANT_TPORT_OUT;
145 else if ( dest->QueryProp(P_NO_TPORT) & (NO_TPORT_IN|NO_TPORT) )
146 return ME_CANT_TPORT_IN;
147 }
148
149 // erst PreventLeaveLiving() testen...
150 if( environment() && environment()->PreventLeaveLiving(this_object(), dest))
151 return ME_CANT_LEAVE_ENV;
152 // dann PreventInsertLiving() im Ziel-Env
153 if (dest->PreventInsertLiving(this_object()))
154 return ME_CANT_BE_INSERTED;
155
156 return 0;
157}
158
159// Krams nach dem Move machen und nebenbei zum Ueberschreiben.
160protected void NotifyMove(object dest, object oldenv, int method) {
161 mixed res;
162 object enem;
163
164 // Begruessungsschlag fuer die Gegener
165 if ( !(method & M_NO_ATTACK) )
166 InitAttack();
167
168 if (!objectp(ME)) return;
169
170 // Verfolger nachholen.
171 if ( pointerp(res = Query(P_PURSUERS)) && sizeof(res[1]) ) {
172 while ( remove_call_out( "TakeFollowers" ) >= 0 );
173
174 call_out( "TakeFollowers", 0 );
175 }
176
177 // und noch das Team nachholen.
178 if ( oldenv != dest
179 && objectp(ME)
180 && QueryProp(P_TEAM_AUTOFOLLOW)
181 && objectp( enem = IsTeamLeader() ) )
182 enem->StartFollow(oldenv); // Teamverfolgung
183
184}
185
186varargs public int move( object|string dest, int method, string direction,
187 string textout, string textin )
188{
189 int para, nightvis, invis, tmp;
190 object oldenv, *inv;
191 string fn,vc;
192 mixed res;
193 mixed hookData, hookRes;
194
195 if (!objectp(dest) && !stringp(dest))
196 raise_error(sprintf("Wrong argument 1 to move(). 'dest' must be a "
197 "string or object! Argument was: %.100O\n",
198 dest));
199
200 // altes Env erstmal merken.
201 oldenv = environment();
202
203 //erstmal den richtigen Zielraum suchen, bevor irgendwelche Checks gemacht
204 //werden...
205 // Ist der Spieler in einer Parallelwelt?
206 if ( (para = QueryProp(P_PARA)) && intp(para) ) {
207 fn = objectp(dest) ? object_name(dest) : dest;
208
209 // Falls der Zielraum nicht schon explizit in der Parallelwelt ist,
210 // neuen Zielraum suchen. Aber nur, wenn fn kein # enthaelt (also kein
211 // Clone ist), sonst wuerde eine Bewegung nach raum#42^para versucht,
212 // was dann buggt. ;-) Problem wird offenbar, wenn ein Para-Lebewesen
213 // im create() eines VC-Raums in Para in den Raum bewegt wird, da
214 // dieser dann noch nicht vom Driver umbenannt wurde und raum#42
215 // heisst.
216 if ( !sizeof(regexp( ({ fn }), "\\^[1-9][0-9]*$" )) &&
217 strrstr(fn,"#")==-1 )
218 {
219 fn += "^" + para;
220
221 // Der Parallelwelt-Raum muss existieren und fuer Spieler
222 // freigegeben sein, damit er zum neuen Ziel wird. Ansonsten
223 // duerfen nur NPCs, Testspieler und Magier herein.
224 if ( (find_object(fn)
225 || ((file_size(fn+".c")>0 ||
226 (file_size(vc=implode(explode(fn,"/")[0..<2],"/")+
227 "/virtual_compiler.c")>0 &&
228 !catch(tmp=(int)call_other(vc,"QueryValidObject",fn);
229 publish) && tmp>0)) &&
230 !catch(load_object(fn);publish) )) &&
231 (!interactive(ME) || !fn->QueryProp(P_NO_PLAYERS) ||
232 (method & M_NOCHECK) || IS_LEARNER(ME) ||
233 (stringp(res = QueryProp(P_TESTPLAYER)) &&
234 IS_LEARNER( lower_case(res) ))) )
235 {
236 dest = fn;
237 }
238 else
239 {
240 // Wir bleiben in der Normalwelt.
241 para = 0;
242 }
243 }
244 }
245
246 // jetzt erstmal Hooks abpruefen, da sie ggf. die Daten aendern.
247 // alten P_TMP_MOVE_HOOK pruefen.
248 if ( res = QueryProp(P_TMP_MOVE_HOOK) ){
249 if ( pointerp(res) && sizeof(res) >= 3
250 && intp(res[0]) && time()<res[0]
251 && objectp(res[1]) && stringp(res[2]) ){
252 if ( res = call_other( res[1], res[2], dest, method, direction,
253 textout, textin ) ){
254 if ( pointerp(res) && sizeof(res) == 5 ){
255 dest = res[0];
256 method = res[1];
257 direction = res[2];
258 textout = res[3];
259 textin = res[4];
260 }
261 else if ( intp(res) && res == -1 )
262 return ME_CANT_LEAVE_ENV;
263 }
264 } else
265 SetProp( P_TMP_MOVE_HOOK, 0 );
266 }
267 // move hook nach neuem Hooksystem triggern.
268 hookData=({dest,method,direction,textout,textin});
269 hookRes=HookFlow(H_HOOK_MOVE,hookData);
270 if(hookRes && pointerp(hookRes) && sizeof(hookRes)>H_RETDATA) {
271 if(hookRes[H_RETCODE]==H_CANCELLED) {
272 return ME_CANT_LEAVE_ENV;
273 }
274 else if(hookRes[H_RETCODE]==H_ALTERED && hookRes[H_RETDATA] &&
275 pointerp(hookRes[H_RETDATA]) && sizeof(hookRes[H_RETDATA])>=5 ){
276 dest = hookRes[H_RETDATA][0];
277 method = hookRes[H_RETDATA][1];
278 direction = hookRes[H_RETDATA][2];
279 textout = hookRes[H_RETDATA][3];
280 textin = hookRes[H_RETDATA][4];
281 }
282 }
283
284 // dest auf Object normieren
285 if (stringp(dest)) dest=load_object(dest);
286
287 // jetzt Checks durchfuehren, ob das Move durchgefuehrt werden darf.
288 if (tmp=PreventMove(dest, oldenv, method)) {
289 // auf gueltigen Fehler pruefen, wer weiss, was Magier da evtl.
290 // versehentlich zurueckgeben.
291 if (VALID_MOVE_ERROR(tmp))
292 return(tmp);
293 else
294 return(ME_DONT_WANT_TO_BE_MOVED);
295 }
296
297 if ( invis = QueryProp(P_INVIS) )
298 method |= M_SILENT;
299
300 if ( objectp(oldenv) ) {
301 if ( !(method & M_SILENT) ) {
302 string *mout;
303 if ( !textout ){
304 if ( method & M_TPORT )
305 textout = (string) QueryProp(P_MMSGOUT) ||
306 (string) QueryProp(P_MSGOUT);
307 else
308 textout = (mout = explode( (string)
309 QueryProp(P_MSGOUT) || "",
310 "#" ))[0]
311 || (string)QueryProp(P_MMSGOUT);
312 }
313
314 if ( !sizeof(direction) )
315 direction = 0;
316
317 inv = all_inventory(environment()) - ({ this_object() });
318 inv = filter( inv, #'living/*'*/ );
319 inv -= filter_objects( inv, "CannotSee", 1 );
320
321 filter( inv, #'tell_object/*'*/,
322 Name( WER, 2 ) + " " + textout +
323 (direction ? " " + direction : "") +
324 (sizeof(mout) > 1 ? mout[1] : "") + ".\n" );
325 }
326 // Magier sehen auch Bewegungen, die M_SILENT sind
327 else if ( interactive(ME) ){
328 inv = (all_inventory(environment()) & users())
329 - ({ this_object() });
330 inv = filter( inv, #'_is_learner/*'*/ );
331
332 if ( invis )
333 fn = "(" + capitalize(getuid(ME)) + ") verschwindet "
334 "unsichtbar.\n";
335 else
336 fn = capitalize(getuid(ME)) + " verschwindet ganz leise.\n";
337
338 filter( inv, #'tell_object/*'*/, fn );
339 }
340
341 // Nackenschlag beim Fluechten:
342 if ( !(method & M_NO_ATTACK) && objectp(ME) )
343 ExitAttack();
344 //falls nach ExitAttack() das Living nicht mehr existiert, muss das
345 //move() auch nicht mehr fortgesetzt werden. Weiter unten gibt es auch
346 //min. eine Stelle, die nicht prueft und ggf. buggt. Daher erfolgt
347 //hier ggf. ein Abbruch. 15.11.06 Zesstra
348 if (!objectp(ME)) return(ME_WAS_DESTRUCTED);
349
350 // Nackenschlag kann ME in den Todesraum bewegt haben...
351 if ( oldenv == environment() ) {
352 // Fuer alle anwesenden gegner kampfende() aufrufen
353 filter((QueryEnemies()[0] & all_inventory(oldenv))-({0}),
354 #'kampfende);
355 // Bugs im exit() sind ohne catch() einfach mist.
356 catch(environment()->exit(ME, dest);publish);
357 }
358 }
359
360 // irgendwas kann das Objekt zerstoert haben, z.B. env->exit().
361 if (!objectp(ME)) return(ME_WAS_DESTRUCTED);
362
363 if ( oldenv != environment() )
364 // Der Nackenschlag oder exit() koennen einen schon bewegt haben.
365 // Und wenn es in den Todesraum ist. ;^)
366 return MOVE_OK;
367
368 SetProp( P_PREPARED_SPELL, 0 ); // Spruchvorbereitung abgebrochen
369 SetProp( P_LAST_MOVE, time() ); // Zeitpunkt der letzten Bewegung
370
371 move_object(ME, dest);
372 if (!objectp(ME))
373 return(ME_WAS_DESTRUCTED);
374
375 dest = environment();
376
377 nightvis = UseSkill(SK_NIGHTVISION);
378 // Meldungen an nicht-Blinde ausgeben, falls keine 'stille' Bewegung
379 if ( !(method & M_SILENT) ) {
380 if ( !textin ) {
381 if ( method & M_TPORT )
382 textin = (string) QueryProp(P_MMSGIN);
383 else
384 textin = (string) QueryProp(P_MSGIN);
385 }
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;
419 int meth,i,r;
420
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;
433 if (follower->Query(P_FOLLOW_SILENT))
434 meth|=M_SILENT|M_NO_SHOW;
435 catch(r=follower->PreventFollow(env);publish);
436 if (!r)
437 follower->move(env,meth);
438 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)) )
450 catch(team->RemoveMember(ME);publish);
451
452 environment()->NotifyRemove(ME);
453 }
454 destruct(ME);
455 return 1;
456}
457