blob: 46c0a4b597ad1aafd23f5a08c082231b2f28197d [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 {
Zesstrafa7abe62021-04-09 14:26:26 +020049 if (find_call_out("Walk") == -1)
50 call_out("Walk", QueryProp(MNPC_DELAY)+random(QueryProp(MNPC_RANDOM)));
Zesstra0def98f2019-06-28 19:13:44 +020051 }
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()
Zesstrafa7abe62021-04-09 14:26:26 +020067 || find_call_out("Walk") > -1)
zesstra42fd8bf2016-06-27 22:04:14 +020068 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.)
Zesstrafa7abe62021-04-09 14:26:26 +020075 call_out("Walk",1+random( min(QueryProp(MNPC_DELAY)-1,8) ));
zesstra42fd8bf2016-06-27 22:04:14 +020076 return 1;
77 }
78 return 0;
79}
80
81protected void Stop(int movehome)
82{
83 if (WALK_MASTER->Registration())
84 WALK_MASTER->RemoveWalker();
Zesstrafa7abe62021-04-09 14:26:26 +020085 else if (find_call_out("Walk")!=-1)
86 remove_call_out("Walk");
zesstra42fd8bf2016-06-27 22:04:14 +020087 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
Bugfixdf8ed432021-01-23 20:46:24 +0100282 if (oldenv==environment() || living(this_object()))
zesstra42fd8bf2016-06-27 22:04:14 +0200283 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
Bugfixb4f10a62021-01-23 19:51:03 +0100299 send_room(oldenv,
zesstra42fd8bf2016-06-27 22:04:14 +0200300 Name( WER, 2 ) + " " + textout +
301 (direction ? " " + direction : "") +
Bugfixb4f10a62021-01-23 19:51:03 +0100302 (sizeof(mout) > 1 ? mout[1] : "") + ".",
303 MT_LOOK,
304 MA_MOVE_OUT,
305 0,
306 ({}),
307 this_object());
zesstra42fd8bf2016-06-27 22:04:14 +0200308 }
309
Zesstra1c7da1d2019-10-24 23:01:15 +0200310 // nun die Meldung für das "Betreten" des Raumes...
zesstra42fd8bf2016-06-27 22:04:14 +0200311
312 if ( method & M_TPORT )
bugfixaf2be4f2020-03-22 19:13:07 +0100313 textin = QueryProp(P_MMSGIN);
zesstra42fd8bf2016-06-27 22:04:14 +0200314 else
bugfixaf2be4f2020-03-22 19:13:07 +0100315 textin = QueryProp(P_MSGIN);
zesstra42fd8bf2016-06-27 22:04:14 +0200316
317 if (stringp(textin))
318 {
Bugfixb4f10a62021-01-23 19:51:03 +0100319 send_room(environment(this_object()),
320 capitalize(name( WER, 0 )) + " " + textin + ".",
321 MT_LOOK,
322 MA_MOVE_IN,
323 0,
324 ({this_object()}),
325 this_object());
zesstra42fd8bf2016-06-27 22:04:14 +0200326 }
327 }
328 return res;
329}
330
331int Walk()
332{
Zesstrafa7abe62021-04-09 14:26:26 +0200333 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
Zesstra0fa33852020-04-15 10:29:40 +0200365 // Ausgaenge ermitteln, zunaechst aber keine Special Exits.
Zesstracc606972020-01-12 21:58:26 +0100366 mapping exits = (environment()->QueryProp(P_EXITS));
Zesstra0fa33852020-04-15 10:29:40 +0200367 string *rooms = m_values(exits);
Zesstracc606972020-01-12 21:58:26 +0100368 string *dirs = m_indices(exits);
369 string *ex = ({});
zesstra42fd8bf2016-06-27 22:04:14 +0200370
Zesstra0fa33852020-04-15 10:29:40 +0200371 // Bei normalen Ausgaengen wird geprueft, ob wir in den Zielraum
372 // reinduerfen.
Zesstracc606972020-01-12 21:58:26 +0100373 for (int i=sizeof(rooms)-1; i>=0; i--)
zesstra42fd8bf2016-06-27 22:04:14 +0200374 {
375 if (!PreventEnter(rooms[i]))
376 ex += ({ dirs[i] });
377 }
378 /* Hier muessen wir auf die Zuverlaessigkeit unserer Magier bauen ... */
379 if (flags & MNPC_DIRECT_MOVE)
380 {
381 // im direct mode keine SEs benutzbar...
382 if (sizeof(ex))
383 {
Zesstracc606972020-01-12 21:58:26 +0100384 string tmp=ex[random(sizeof(ex))];
zesstra42fd8bf2016-06-27 22:04:14 +0200385 direct_move(explode(exits[tmp], "#")[<1], M_GO, "nach "+capitalize(tmp));
386 }
387 else
388 {
Zesstra0def98f2019-06-28 19:13:44 +0200389 // Hngl. Nach Hause... Aber nicht anhalten.
zesstra42fd8bf2016-06-27 22:04:14 +0200390 direct_move(QueryProp(MNPC_HOME), M_TPORT|M_NOCHECK, 0);
zesstra42fd8bf2016-06-27 22:04:14 +0200391 }
392 }
393 else if (flags & MNPC_ONLY_EXITS)
394 {
395 // logischerweise auch keine SEs benutzen...
396 if (sizeof(ex))
397 {
398 command(ex[random(sizeof(ex))]); /* Irgendwohin gehen */
399 }
400 else
401 {
Zesstra0def98f2019-06-28 19:13:44 +0200402 // Hngl. Nach Hause... Aber nicht anhalten.
zesstra42fd8bf2016-06-27 22:04:14 +0200403 move(QueryProp(MNPC_HOME), M_TPORT|M_NOCHECK);
zesstra42fd8bf2016-06-27 22:04:14 +0200404 }
405 }
406 else
407 {
Zesstra0fa33852020-04-15 10:29:40 +0200408 // erst jetzt die special exits mitbenutzen.
zesstra42fd8bf2016-06-27 22:04:14 +0200409 ex += m_indices(ENV()->QueryProp(P_SPECIAL_EXITS));
410 if (sizeof(ex))
411 {
412 command(ex[random(sizeof(ex))]); /* Irgendwohin gehen */
413 }
414 else
415 {
Zesstra0def98f2019-06-28 19:13:44 +0200416 // Hngl. Gar keine Ausgaenge. Nach Hause... aber nicht anhalten.
zesstra42fd8bf2016-06-27 22:04:14 +0200417 move(QueryProp(MNPC_HOME), M_TPORT|M_NOCHECK);
zesstra42fd8bf2016-06-27 22:04:14 +0200418 }
419 }
zesstra42fd8bf2016-06-27 22:04:14 +0200420
421 return 1;
422}