blob: a9778546840c9fb6886d6dabf831af02d52869dd [file] [log] [blame]
Zesstra6b5ac892019-11-26 21:28:16 +01001#pragma save_types,strong_types,rtt_checks
zesstra42fd8bf2016-06-27 22:04:14 +02002
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{
Zesstra260613d2019-12-09 21:02:58 +010041 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.
Zesstra260613d2019-12-09 21:02:58 +010070 if ((QueryProp(MNPC_DELAY)+QueryProp(MNPC_RANDOM))<MAX_MASTER_TIME)
zesstra42fd8bf2016-06-27 22:04:14 +020071 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...
bugfixaf2be4f2020-03-22 19:13:07 +0100274 if ((!catch(tmp=call_other(vc,"NoParaObjects")) && (!tmp)) &&
zesstra42fd8bf2016-06-27 22:04:14 +0200275 (!catch(call_other( fn, "???" ))))
276 dest=fn;
277 }
278 }
279
bugfixaf2be4f2020-03-22 19:13:07 +0100280 res = call_other(ME, "move", dest, M_NOCHECK);
zesstra42fd8bf2016-06-27 22:04:14 +0200281
282 if (oldenv==environment())
283 return res;
284
285 // als erstes die Meldung fuer das Verlassen des Raumes...
286 if ( method & M_TPORT )
bugfixaf2be4f2020-03-22 19:13:07 +0100287 textout = QueryProp(P_MMSGOUT) || QueryProp(P_MSGOUT);
zesstra42fd8bf2016-06-27 22:04:14 +0200288 else
289 {
bugfixaf2be4f2020-03-22 19:13:07 +0100290 mout = explode( QueryProp(P_MSGOUT) || "", "#" );
291 textout = mout[0] || QueryProp(P_MMSGOUT);
zesstra42fd8bf2016-06-27 22:04:14 +0200292 }
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 )
bugfixaf2be4f2020-03-22 19:13:07 +0100311 textin = QueryProp(P_MMSGIN);
zesstra42fd8bf2016-06-27 22:04:14 +0200312 else
bugfixaf2be4f2020-03-22 19:13:07 +0100313 textin = QueryProp(P_MSGIN);
zesstra42fd8bf2016-06-27 22:04:14 +0200314
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{
zesstra42fd8bf2016-06-27 22:04:14 +0200329 if (!environment())
330 {
331 // darf eigentlich nicht vorkommen.
332 raise_error("MNPC ohne Environment.\n");
333 }
334
335 int flags=QueryProp(MNPC_FLAGS);
336 if (!(flags & MNPC_WALK))
337 return 0;
338
339 //ggf. neuen Callout eintragen, bevor irgendwas anderes gemacht wird.
Zesstra260613d2019-12-09 21:02:58 +0100340 if ((QueryProp(MNPC_DELAY)+QueryProp(MNPC_RANDOM))>=MAX_MASTER_TIME)
zesstra42fd8bf2016-06-27 22:04:14 +0200341 call_out("Walk", QueryProp(MNPC_DELAY)+random(QueryProp(MNPC_RANDOM)));
342
343 // Im Kampf ggf. nicht weitergehen.
344 if ((flags & MNPC_NO_WALK_IN_FIGHT) && InFight())
345 {
346 meet_last_player=time();
347 return 1;
348 }
349
350 // MNPC anhalten, wenn lange kein Spielerkontakt
351 if (QueryProp(MNPC_WALK_TIME)+meet_last_player < time()
352 && !sizeof(filter(all_inventory(environment()),
353 #'query_once_interactive))
354 )
355 {
356 // anhalten und ggf. auch direkt nach Hause gehen.
357 Stop(flags & MNPC_GO_HOME_WHEN_STOPPED);
358 return 0;
359 }
360
Zesstra0fa33852020-04-15 10:29:40 +0200361 // Ausgaenge ermitteln, zunaechst aber keine Special Exits.
Zesstracc606972020-01-12 21:58:26 +0100362 mapping exits = (environment()->QueryProp(P_EXITS));
Zesstra0fa33852020-04-15 10:29:40 +0200363 string *rooms = m_values(exits);
Zesstracc606972020-01-12 21:58:26 +0100364 string *dirs = m_indices(exits);
365 string *ex = ({});
zesstra42fd8bf2016-06-27 22:04:14 +0200366
Zesstra0fa33852020-04-15 10:29:40 +0200367 // Bei normalen Ausgaengen wird geprueft, ob wir in den Zielraum
368 // reinduerfen.
Zesstracc606972020-01-12 21:58:26 +0100369 for (int i=sizeof(rooms)-1; i>=0; i--)
zesstra42fd8bf2016-06-27 22:04:14 +0200370 {
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 {
Zesstracc606972020-01-12 21:58:26 +0100380 string tmp=ex[random(sizeof(ex))];
zesstra42fd8bf2016-06-27 22:04:14 +0200381 direct_move(explode(exits[tmp], "#")[<1], M_GO, "nach "+capitalize(tmp));
382 }
383 else
384 {
Zesstra0def98f2019-06-28 19:13:44 +0200385 // Hngl. Nach Hause... Aber nicht anhalten.
zesstra42fd8bf2016-06-27 22:04:14 +0200386 direct_move(QueryProp(MNPC_HOME), M_TPORT|M_NOCHECK, 0);
zesstra42fd8bf2016-06-27 22:04:14 +0200387 }
388 }
389 else if (flags & MNPC_ONLY_EXITS)
390 {
391 // logischerweise auch keine SEs benutzen...
392 if (sizeof(ex))
393 {
394 command(ex[random(sizeof(ex))]); /* Irgendwohin gehen */
395 }
396 else
397 {
Zesstra0def98f2019-06-28 19:13:44 +0200398 // Hngl. Nach Hause... Aber nicht anhalten.
zesstra42fd8bf2016-06-27 22:04:14 +0200399 move(QueryProp(MNPC_HOME), M_TPORT|M_NOCHECK);
zesstra42fd8bf2016-06-27 22:04:14 +0200400 }
401 }
402 else
403 {
Zesstra0fa33852020-04-15 10:29:40 +0200404 // erst jetzt die special exits mitbenutzen.
zesstra42fd8bf2016-06-27 22:04:14 +0200405 ex += m_indices(ENV()->QueryProp(P_SPECIAL_EXITS));
406 if (sizeof(ex))
407 {
408 command(ex[random(sizeof(ex))]); /* Irgendwohin gehen */
409 }
410 else
411 {
Zesstra0def98f2019-06-28 19:13:44 +0200412 // Hngl. Gar keine Ausgaenge. Nach Hause... aber nicht anhalten.
zesstra42fd8bf2016-06-27 22:04:14 +0200413 move(QueryProp(MNPC_HOME), M_TPORT|M_NOCHECK);
zesstra42fd8bf2016-06-27 22:04:14 +0200414 }
415 }
416
417 return 1;
418}