blob: c8687570d416866df0537acb2f69e0b886946533 [file] [log] [blame]
zesstra42fd8bf2016-06-27 22:04:14 +02001#pragma save_types,strong_types,rtt_checks,pedantic
2
3inherit "/std/living/moving";
4
5#include <moving.h>
6#include <defines.h>
7#include <properties.h>
8#include "/p/service/padreic/mnpc/mnpc.h"
9#define NEED_PROTOTYPES
10#include <living/combat.h>
11#undef NEED_PROTOTYPES
12#include <combat.h>
13
14#define ENV environment
15#define PO previous_object()
16
17// Letzter Spielerkontakt. -1, wenn der MNPC inaktiv zuhause rumsteht
Zesstra0def98f2019-06-28 19:13:44 +020018nosave int meet_last_player;
zesstra42fd8bf2016-06-27 22:04:14 +020019
20static void mnpc_create()
21{
22 if (PO && member(inherit_list(PO), "/std/room.c")!=-1)
23 SetProp(MNPC_HOME, object_name(PO));
24 else if (PL && ENV(PL))
25 SetProp(MNPC_HOME, object_name(ENV(PL)));
26 else
27 SetProp(MNPC_HOME, MNPC_DFLT_HOME);
28 SetProp(P_MNPC, 1);
29 SetProp(MNPC_AREA, ({}));
30 SetProp(MNPC_DELAY, MNPC_DFLT_DELAY);
31 SetProp(MNPC_FUNC, 0);
32 SetProp(MNPC_RANDOM, 0);
33 SetProp(MNPC_WALK_TIME, MNPC_DFLT_WALK);
34 SetProp(MNPC_FLAGS, 0);
35 SetProp(P_ENABLE_IN_ATTACK_OUT, 1);
36 meet_last_player=time();
37}
38
39protected void RegisterWalk()
40{
41 if ((QueryProp(MNPC_DELAY)+QueryProp(MNPC_RANDOM))<=MAX_MASTER_TIME)
Zesstra0def98f2019-06-28 19:13:44 +020042 {
43 if (!WALK_MASTER->Registration())
44 WALK_MASTER->RegisterWalker(QueryProp(MNPC_DELAY),
45 QueryProp(MNPC_RANDOM));
46 }
zesstra42fd8bf2016-06-27 22:04:14 +020047 else
Zesstra0def98f2019-06-28 19:13:44 +020048 {
49 if (find_call_out("Walk") == -1)
50 call_out("Walk", QueryProp(MNPC_DELAY)+random(QueryProp(MNPC_RANDOM)));
51 }
zesstra42fd8bf2016-06-27 22:04:14 +020052}
53
54// Can be used to manually restart the MNPC from a different object even if
Zesstra0def98f2019-06-28 19:13:44 +020055// the MNPC had no player contact.
zesstra42fd8bf2016-06-27 22:04:14 +020056public int RestartWalk()
57{
58 int flags = QueryProp(MNPC_FLAGS);
59 // Falls nicht laufend, wird gar nichts gemacht.
60 if (flags & MNPC_WALK)
61 {
62 //Spielerkontakt simulieren
63 meet_last_player=time();
64 // Falls MNPC noch registriert ist oder noch einen Callout auf Walk hat,
65 // muss nichts weiter gemacht werden.
66 if (WALK_MASTER->Registration()
67 || find_call_out("Walk") > -1)
68 return -1;
69 // ansonsten MNPC registrieren, falls geeignet.
70 if ((QueryProp(MNPC_DELAY)+QueryProp(MNPC_RANDOM))<=MAX_MASTER_TIME)
71 WALK_MASTER->RegisterWalker(QueryProp(MNPC_DELAY), QueryProp(MNPC_RANDOM));
72 // und mit kurzer Verzoegerung einmal laufen. (ja, absicht, hier
73 // MNPC_DELAY zu nutzen - denn solange dauert das Walk vom Master
74 // mindestens.)
75 call_out("Walk",1+random( min(QueryProp(MNPC_DELAY)-1,8) ));
76 return 1;
77 }
78 return 0;
79}
80
81protected void Stop(int movehome)
82{
83 if (WALK_MASTER->Registration())
84 WALK_MASTER->RemoveWalker();
85 else if (find_call_out("Walk")!=-1)
86 remove_call_out("Walk");
87 if (movehome)
88 {
89 move(QueryProp(MNPC_HOME), M_TPORT|M_NOCHECK);
90 meet_last_player=-1;
91 }
92}
93
94static int _set_mnpc_flags(int flags)
95{
96 if (flags & MNPC_WALK)
97 {
98 if (!QueryProp(MNPC_HOME))
99 raise_error("unknown MNPC_HOME\n");
Zesstra0def98f2019-06-28 19:13:44 +0200100 // RegisterWalk prueft, ob der MNPC schon angemeldet ist.
zesstra42fd8bf2016-06-27 22:04:14 +0200101 RegisterWalk();
102 }
103 // else nicht von Bedeutung, da in Walk() das flag getestet wird
104 if (flags & MNPC_FOLLOW_PLAYER)
105 {
106 if (!QueryProp(MNPC_PURSUER))
107 { // wurde dieses Flag neu eingeschaltet?
108 if (environment())
109 { // Verfolgung aufnehmen...
110 object *pursuer = filter(all_inventory(ENV()), #'interactive);
111 filter_objects(pursuer, "AddPursuer", ME);
112 SetProp(MNPC_PURSUER, pursuer);
113 }
114 else
115 SetProp(MNPC_PURSUER, ({}));
116 }
117 }
118 else if (pointerp(QueryProp(MNPC_PURSUER)))
119 { // wird derzeit irgendwer verfolgt?
120 // alle Verfolgungen abbrechen...
121 filter_objects(QueryProp(MNPC_PURSUER)-({ 0 }), "RemovePursuer", ME);
122 SetProp(MNPC_PURSUER, 0); // Speicher freigeben...
123 }
124 else
125 SetProp(MNPC_PURSUER, 0);
126
127 // nur livings koennen command_me nutzen...
128 if (!living(ME))
129 flags |= MNPC_DIRECT_MOVE;
130
131 return Set(MNPC_FLAGS, flags, F_VALUE);
132}
133
134static void mnpc_InsertEnemy(object enemy)
135{
136 if ( (QueryProp(MNPC_FLAGS) & MNPC_FOLLOW_ENEMY) &&
137 (member(QueryProp(MNPC_PURSUER), PL)==-1))
138 {
139 PL->AddPursuer(ME);
140 SetProp(MNPC_PURSUER, QueryProp(MNPC_PURSUER)+({ PL }));
141 }
142}
143
144static void mnpc_reset()
145{
146 int flags = QueryProp(MNPC_FLAGS);
147 // meet_last_player < 0 zeigt an, dass der MNPC schon zuhause ist.
148 if (meet_last_player < 0
149 || !(flags & MNPC_WALK)
150 || (flags & MNPC_NO_MOVE_HOME))
151 return;
152
153 // Lange keinen Spielerkontakt und kein Spieler im Raum: nach Hause gehen.
154 if (QueryProp(MNPC_WALK_TIME)+meet_last_player < time()
155 && environment() && !sizeof(filter(
156 all_inventory(environment()), #'query_once_interactive)))
157 {
158 // Abschalten und Heimgehen und dort warten.
159 Stop(1);
160 }
161}
162
163static int _query_mnpc_last_meet()
164{ return meet_last_player; }
165
166static void mnpc_init()
167{
168 if (interactive(PL))
169 {
Zesstra0def98f2019-06-28 19:13:44 +0200170 // Wenn noetig, Wandern wieder aufnehmen.
zesstra42fd8bf2016-06-27 22:04:14 +0200171 if (meet_last_player<=0)
172 {
173 RegisterWalk();
174 }
Bugfixca4dfd32016-12-22 18:22:42 +0100175 if ( ((QueryProp(MNPC_FLAGS) & MNPC_FOLLOW_PLAYER) &&
176 (member(QueryProp(MNPC_PURSUER), PL)==-1)) ||
177 ((QueryProp(MNPC_FLAGS) & MNPC_FOLLOW_ENEMY) && IsEnemy(PL)))
zesstra42fd8bf2016-06-27 22:04:14 +0200178 {
179 PL->AddPursuer(ME);
180 SetProp(MNPC_PURSUER, QueryProp(MNPC_PURSUER)+({ PL }));
181 }
182 meet_last_player=time();
183 }
184 else
185 {
186 // Wenn der reinkommende auch ein MNPC_LAST_MEET groesser 0 hat, ist es
187 // ein MNPC, der noch laeuft. Wenn wir nicht laufen, laufen wir los.
188 // In diesem und auch im anderen Fall uebernehmen wir aber mal seinen
189 // letzten Spielerkontakt, denn der ist juenger als unserer.
190 int lm = PL->QueryProp(MNPC_LAST_MEET);
191 if (meet_last_player<=0 && lm>0)
192 {
193 RegisterWalk();
194 meet_last_player=lm;
195 }
196 else if (meet_last_player<lm)
197 meet_last_player=lm;
198 }
199}
200
201static void mnpc_move()
202{
203 if (environment() && (QueryProp(MNPC_FLAGS) & MNPC_FOLLOW_PLAYER))
204 {
205 object *liv = QueryProp(MNPC_PURSUER) & all_inventory(environment());
206 filter_objects(QueryProp(MNPC_PURSUER)-liv-({ 0 }), "RemovePursuer", ME);
207 SetProp(MNPC_PURSUER, liv);
208 }
209 if (QueryProp(MNPC_FUNC))
Zesstrabddbe3d2016-07-06 20:47:43 +0200210 call_other(ME, QueryProp(MNPC_FUNC));
zesstra42fd8bf2016-06-27 22:04:14 +0200211}
212
213static int PreventEnter(string file)
214// darf der Raum betreten werden?
215{
216 string *area;
217
218 if (!sizeof(area=QueryProp(MNPC_AREA)))
219 return 0; // Raum darf betreten werden
220 else
221 {
222 int i;
223 status exactmatch;
224 exactmatch=QueryProp(MNPC_FLAGS) & MNPC_EXACT_AREA_MATCH;
225 if ((i=strstr(file, "#"))!=-1) file=file[0..i-1];
226 for (i=sizeof(area)-1; i>=0; i--)
227 {
228 if (exactmatch)
229 {
230 //exakter Vergleich, kein Substringvergleich gewuenscht
231 if (file==area[i])
232 return 0; //betreten
233 }
234 else
235 {
236 if (strstr(file, area[i])==0)
237 return 0; // Raum betreten
238 }
239 }
240 return 1; // Raum darf nicht betreten werden
241 }
242}
243
244static int mnpc_PreventFollow(object dest)
245{
246 if (dest && PreventEnter(object_name(dest)))
247 return 2;
248 return 0;
249}
250
251// Bewegungssimulation (Bewegungsmeldung) fuer bewegende non-livings
252static int direct_move(mixed dest, int method, string direction)
253{
254 int res, para, tmp;
255 string textout, textin, *mout, vc, fn;
256 object oldenv, *inv;
257
258 if (living(ME))
259 return call_other(ME, "move", dest, method);
260 else
261 {
262 oldenv = environment();
263 para=QueryProp(P_PARA);
264 if ((para>0) && stringp(dest))
265 {
266 fn=dest+"^"+para;
267 if (find_object(fn) || (file_size(fn+".c")>0))
268 dest=fn;
269 else if (file_size(vc=implode(explode(fn,"/")[0..<2],"/")
270 +"/virtual_compiler.c")>0)
271 {
Zesstra1c7da1d2019-10-24 23:01:15 +0200272 // wenn ein VC existiert, prüfen ob dieser ParaObjecte unterstuetzt
zesstra42fd8bf2016-06-27 22:04:14 +0200273 // wenn ja, dann testen ob sich Raum laden laesst...
274 if ((!catch(tmp=(int)call_other(vc,"NoParaObjects")) && (!tmp)) &&
275 (!catch(call_other( fn, "???" ))))
276 dest=fn;
277 }
278 }
279
280 res = (int)call_other(ME, "move", dest, M_NOCHECK);
281
282 if (oldenv==environment())
283 return res;
284
285 // als erstes die Meldung fuer das Verlassen des Raumes...
286 if ( method & M_TPORT )
287 textout = (string) QueryProp(P_MMSGOUT) || (string) QueryProp(P_MSGOUT);
288 else
289 {
290 mout = explode( (string) QueryProp(P_MSGOUT) || "", "#" );
291 textout = mout[0] || (string) QueryProp(P_MMSGOUT);
292 }
293
294 if (stringp(textout))
295 {
296 if ( !sizeof(direction) )
297 direction = 0;
298
299 inv = all_inventory(oldenv) - ({ this_object() });
300 inv = filter( inv, #'living);
301 inv -= filter_objects( inv, "CannotSee", 1 );
302 filter( inv, #'tell_object,
303 Name( WER, 2 ) + " " + textout +
304 (direction ? " " + direction : "") +
305 (sizeof(mout) > 1 ? mout[1] : "") + ".\n" );
306 }
307
Zesstra1c7da1d2019-10-24 23:01:15 +0200308 // nun die Meldung für das "Betreten" des Raumes...
zesstra42fd8bf2016-06-27 22:04:14 +0200309
310 if ( method & M_TPORT )
311 textin = (string) QueryProp(P_MMSGIN);
312 else
313 textin = (string) QueryProp(P_MSGIN);
314
315 if (stringp(textin))
316 {
317 inv = all_inventory(environment()) - ({ this_object() });
318 inv = filter( inv, #'living);
319 inv -= filter_objects( inv, "CannotSee", 1 );
320 filter( inv, #'tell_object,
321 capitalize(name( WER, 0 )) + " " + textin + ".\n" );
322 }
323 }
324 return res;
325}
326
327int Walk()
328{
329 int i;
330 mapping exits;
331 string *rooms, *dirs, *ex, tmp;
332
333 if (!environment())
334 {
335 // darf eigentlich nicht vorkommen.
336 raise_error("MNPC ohne Environment.\n");
337 }
338
339 int flags=QueryProp(MNPC_FLAGS);
340 if (!(flags & MNPC_WALK))
341 return 0;
342
343 //ggf. neuen Callout eintragen, bevor irgendwas anderes gemacht wird.
344 if ((QueryProp(MNPC_DELAY)+QueryProp(MNPC_RANDOM))>MAX_MASTER_TIME)
345 call_out("Walk", QueryProp(MNPC_DELAY)+random(QueryProp(MNPC_RANDOM)));
346
347 // Im Kampf ggf. nicht weitergehen.
348 if ((flags & MNPC_NO_WALK_IN_FIGHT) && InFight())
349 {
350 meet_last_player=time();
351 return 1;
352 }
353
354 // MNPC anhalten, wenn lange kein Spielerkontakt
355 if (QueryProp(MNPC_WALK_TIME)+meet_last_player < time()
356 && !sizeof(filter(all_inventory(environment()),
357 #'query_once_interactive))
358 )
359 {
360 // anhalten und ggf. auch direkt nach Hause gehen.
361 Stop(flags & MNPC_GO_HOME_WHEN_STOPPED);
362 return 0;
363 }
364
365 // Ausgaenge ermitteln.
366 exits = (environment()->QueryProp(P_EXITS));
367 rooms = m_values(exits);
368 dirs = m_indices(exits);
369 ex = ({});
370
371 for (i=sizeof(rooms)-1; i>=0; i--)
372 {
373 if (!PreventEnter(rooms[i]))
374 ex += ({ dirs[i] });
375 }
376 /* Hier muessen wir auf die Zuverlaessigkeit unserer Magier bauen ... */
377 if (flags & MNPC_DIRECT_MOVE)
378 {
379 // im direct mode keine SEs benutzbar...
380 if (sizeof(ex))
381 {
382 tmp=ex[random(sizeof(ex))];
383 direct_move(explode(exits[tmp], "#")[<1], M_GO, "nach "+capitalize(tmp));
384 }
385 else
386 {
Zesstra0def98f2019-06-28 19:13:44 +0200387 // Hngl. Nach Hause... Aber nicht anhalten.
zesstra42fd8bf2016-06-27 22:04:14 +0200388 direct_move(QueryProp(MNPC_HOME), M_TPORT|M_NOCHECK, 0);
zesstra42fd8bf2016-06-27 22:04:14 +0200389 }
390 }
391 else if (flags & MNPC_ONLY_EXITS)
392 {
393 // logischerweise auch keine SEs benutzen...
394 if (sizeof(ex))
395 {
396 command(ex[random(sizeof(ex))]); /* Irgendwohin gehen */
397 }
398 else
399 {
Zesstra0def98f2019-06-28 19:13:44 +0200400 // Hngl. Nach Hause... Aber nicht anhalten.
zesstra42fd8bf2016-06-27 22:04:14 +0200401 move(QueryProp(MNPC_HOME), M_TPORT|M_NOCHECK);
zesstra42fd8bf2016-06-27 22:04:14 +0200402 }
403 }
404 else
405 {
406 // Special Exits mitbenutzen.
407 ex += m_indices(ENV()->QueryProp(P_SPECIAL_EXITS));
408 if (sizeof(ex))
409 {
410 command(ex[random(sizeof(ex))]); /* Irgendwohin gehen */
411 }
412 else
413 {
Zesstra0def98f2019-06-28 19:13:44 +0200414 // Hngl. Gar keine Ausgaenge. Nach Hause... aber nicht anhalten.
zesstra42fd8bf2016-06-27 22:04:14 +0200415 move(QueryProp(MNPC_HOME), M_TPORT|M_NOCHECK);
zesstra42fd8bf2016-06-27 22:04:14 +0200416 }
417 }
418
419 return 1;
420}