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