blob: 92bc202ac0761f2292158848240f544f910e2331 [file] [log] [blame]
MG Mud User88f12472016-06-24 23:31:02 +02001// MorgenGrauen MUDlib
2//
3// transport.c -- Basisklasse fuer Schiffe und aehnliche Transporter
4//
5// $Id: transport.c 9400 2015-12-11 21:56:14Z Zesstra $
Zesstra@Morgengrauen2315aa12016-10-30 22:36:26 +01006#pragma strong_types,rtt_checks
MG Mud User88f12472016-06-24 23:31:02 +02007#pragma range_check
8#pragma no_clone
9#pragma pedantic
10
11inherit "/std/thing/moving";
12inherit "/std/room";
13
14#include <properties.h>
15#include <moving.h>
16#include <defines.h>
17#include <language.h>
18#include <transport.h>
19#include <regexp.h>
Zesstra179db0d2016-11-26 13:13:41 +010020#include <hook.h>
21
MG Mud User88f12472016-06-24 23:31:02 +020022
23/* transport.c
24 *
25 * Ueberarbeitete und
26 * erweiterte Version : Tilly@MorgenGrauen, 10.01.02
27 * Basierend auf : transport.c@SilberLand (Woody@SilberLand), 05.12.99
28 * Basierend auf : Hates und Rumatas generisches Transport Objekt
29 * MorgenGrauen 15.02.93
30 */
31
32/*
33 ********************* Variablen *********************
34 */
35
36// TODO: langfristig waer ja private schoen...
37//
38// Datenstruktur von 'route' (bei HP_ROOM)
39// 0 ({string ID, : HP_ROOM
40// 1 string room, : Dateiname Zielraum
41// 2 int stay, : Dauer Haltezeit
42// 3 int next, : Dauer naechste Fahrtzeit
43// 4 string code, : Haltestellenname fuer QueryArrived
44// 5 mixed dest, : Haltestellen-IDs fuer HasRoute (reise nach)
45// 6 mixed deststr }): unbenutzt.
46//
47// Datenstruktur von 'route' (bei HP_MSG, HP_FUN)
48// 0 ({string ID, : HP_MSG
49// 1 string message, : Meldung oder string fun : Funktionsname
50// 2 int next}) : Dauer bis zum naechsten Ereignis
51nosave mixed *route; /* Liste der Haltepunkte. */
52nosave int rpos; /* Momentane Position in obiger Liste. */
53nosave string roomCode; /* Code des aktuellen Raumes (oder 0). */
Zesstra179db0d2016-11-26 13:13:41 +010054nosave int meet_last_player; // Letzter Spielerkontakt
55
56private void unsubscribe_init();
57private int subscribe_init();
MG Mud User88f12472016-06-24 23:31:02 +020058
59/*
60 ********** Management der builtin-properties **********
61 */
62
63string _query_short()
64{
65 if (roomCode) return Query(P_SHORT);
66 return 0;
67}
68
69mixed _query_transparent()
70{
71 if (roomCode) return Query(P_TRANSPARENT);
72 return 0;
73}
74
Zesstra179db0d2016-11-26 13:13:41 +010075static mixed *_set_route(mixed *r) { return route = r; }
76static mixed *_query_route() { return route; }
77static int _query_mnpc_last_meet() { return meet_last_player; }
MG Mud User88f12472016-06-24 23:31:02 +020078
79/*
80 **************** Zugriffsfunktionen ***************
81 */
82
Zesstra179db0d2016-11-26 13:13:41 +010083public void Halt()
MG Mud User88f12472016-06-24 23:31:02 +020084{
Zesstra179db0d2016-11-26 13:13:41 +010085 // stop, but keep rpos counter.
MG Mud User88f12472016-06-24 23:31:02 +020086 while (remove_call_out( "changeHp" )>-1);
87 while (remove_call_out( "disconnect" )>-1);
88}
89
90// Aktualisiert/Setzt die Route im TravelD, wenn erlaubt (d.h. kein
91// P_NO_TRAVELING)
92private void ReportRoute()
93{
94 if(!QueryProp(P_NO_TRAVELING))
95 {
96 mixed tmp = filter(route, function int (mixed arr)
97 {
98 return arr[0] == HP_ROOM;
99 } );
100 string *route = map(tmp, function string (mixed arr)
101 { return arr[1]; }
102 );
103 TRAVELD->AddRoute(object_name(this_object()),route);
104 }
105}
106
Zesstra179db0d2016-11-26 13:13:41 +0100107public varargs void Start(int pos)
MG Mud User88f12472016-06-24 23:31:02 +0200108{
109 Halt();
Zesstra5d2ace02017-06-20 23:21:36 +0200110 // negative pos sind ein Fehler
111 if (pos<0)
112 raise_error(sprintf("Start(): Positionszaehler < 0: %d\n",pos));
113
114 // wenn pos zu gross fuer die Route ist, rpos auf Ende der Route setzen
115 // (i.e. sizeof(route)-1), damit bei der naechsten Bewegung am Anfang der
116 // Route begonnen wird.
117 rpos = min(pos, sizeof(route)-1);
118
MG Mud User88f12472016-06-24 23:31:02 +0200119 call_out("changeHp",0);
120 // Tell TRAVELD our current route
121 ReportRoute();
122}
123
Zesstra179db0d2016-11-26 13:13:41 +0100124// continues the current route at the point we stopped.
125public int Continue()
126{
127 if (find_call_out("changeHp") == -1
128 && find_call_out("disconnect") == -1)
129 {
130 unsubscribe_init();
131 Start(rpos);
132 return 1;
133 }
134 return 0;
135}
136
137// pauses the transporter temporarily in a way that it continues along its
138// route as soon as a living enters one of the stop points. If that is not
139// possible, we do nothing.
140public int Pause()
141{
142 // ok, stop
143 if (subscribe_init() == 1)
144 {
145 Halt();
146 return 1;
147 }
148 return 0;
149}
150
MG Mud User88f12472016-06-24 23:31:02 +0200151void SetTravelCmds()
152{
153 if (pointerp(QueryProp(P_LEAVECMDS)))
154 AddCmd(QueryProp(P_LEAVECMDS),"GoOutside");
155 if (pointerp(QueryProp(P_ENTERCMDS)))
156 AddCmd(QueryProp(P_ENTERCMDS),"GoInside");
157 if (pointerp(QueryProp(P_TRAVEL_CMDS)))
158 AddCmd(QueryProp(P_TRAVEL_CMDS),"GoInAndOutside");
159 return;
160}
161
162mixed HasRoute(mixed dest)
163{
164 int i,s,z;
165 string str;
166 object ob;
167 mixed harb;
168
169 s = sizeof(route);
170
171 for (i = rpos;i <= rpos+s-1;i++)
172 {
173 if (route[i%s][0] == HP_ROOM)
174 {
175 if (member(route[i%s][5],dest) != -1 &&
176 objectp(ob=load_object(route[i%s][1])) &&
177 pointerp(harb=ob->QueryProp(P_HARBOUR)) &&
178 sizeof(harb))
179 {
180 return ({ route[i%s][1], harb[0] });
181 }
182 }
183 }
184 return 0;
185}
186
187public varargs void AddRoute(string room, int stay, int next,
188 string harbour_desc, string|string* dest_ids, string deststr)
189{
190 // Daten aus dem Zielanleger abfragen.
191 <string|string*>* harbour = room->QueryProp(P_HARBOUR)||({});
192 string* harbour_ids = ({});
193
194 // IDs des Zielanlegers fuer Syntaxpruefung
195 if ( sizeof(harbour)==2 )
196 {
197 if ( pointerp(harbour[1]) )
198 harbour_ids = harbour[1];
199 else
200 harbour_ids = ({harbour[1]});
201 }
202
203 // <dest_ids> in ein Array umwandeln, ist dann ggf. leer
204 if ( !dest_ids )
205 {
206 dest_ids = ({});
207 }
208 if ( stringp(dest_ids) )
209 {
210 dest_ids = ({dest_ids});
211 }
212
213 // explizit angegebene IDs stehen jetzt in <dest_ids>, die IDs des
214 // Zielhafens aus P_HARBOUR werden addiert.
215 dest_ids += harbour_ids;
216
217 // Ist <dest> immer noch leer, versuchen wir, aus <harbour_desc> ein paar
218 // Stichwoerter zu erzeugen, die man als Zielangabe in der Syntax
219 // "reise nach <ziel>" verwenden kann.
220 if ( !sizeof(dest_ids) )
221 {
222 // Grossgeschriebene Begriffe in <harbour_desc> in <dest> eintragen. Dazu:
223 // 1) <code> erstmal so zerschneiden, dass alle ueblichen Satzzeichen
224 // rausfliegen (es gibt Transporter, die sowas in <harbour_desc>
225 // uebergeben).
226 dest_ids = regexplode(harbour_desc, "[(),.;:&\+_ ]",
227 RE_OMIT_DELIM|RE_GLOBAL);
228 // 2a) So filtern, dass nur grossgeschriebene Woerter uebrig bleiben,
229 // von 1) uebriggebliebene Leerstrings gleich mit wegwerfen.
230 // 2b) Ergebnis kleinschreiben, damit die Syntaxpruefung damit arbeiten
231 // kann.
232 dest_ids = map( filter(dest_ids, function int (string key) {
233 return (key!="" && key[0]>='A' && key[0]<='Z');
234 }), #'lower_case);
235 }
236 // Sollte <dest> jetzt immer noch leer sein, wurde an allen drei Stellen
237 // nichts oder nur Muell uebergeben.
238 if ( !sizeof(dest_ids) )
239 {
240 raise_error("Transporterfehlfunktion in AddRoute(): Identifikations"
241 "matrix unzureichend definiert. Transporter unbenutzbar fuer "
242 "Spieler. Bitte mindestens eine Ziel-ID via P_HARBOUR oder als "
243 "Argument to AddRoute().");
244 }
245 route += ({ ({ HP_ROOM, room, stay, next, harbour_desc, dest_ids,
246 deststr }) });
247}
248
249varargs void AddMsg(string msg, int next)
250{
251 route += ({ ({ HP_MSG, msg, next }) });
252}
253
254void AddFun(string fun, int next) { route += ({ ({ HP_FUN, fun, next }) }); }
255
256string QueryArrived() { return roomCode; }
257
258mixed* QueryPosition()
259{
260 return ({ route[rpos][1],route[(rpos+1)<sizeof(route)?(rpos+1):0][1] });
261}
262
263object* QueryPassengers()
264{
265 return filter(all_inventory(),#'query_once_interactive);
266}
267
268varargs string *QueryHarbours(int textflag)
269{
270 string *ret = ({});
271
272 foreach( mixed* entry : route )
273 {
274 if ( entry[0] == HP_ROOM )
275 {
276 if ( textflag )
277 {
278 string *hp_ids = entry[1]->QueryProp(P_HARBOUR)[1];
279 if (pointerp(hp_ids) && sizeof(hp_ids))
280 {
281 string *h = map( explode(hp_ids[0]," "), #'capitalize);
282 ret += ({ implode(h, " ") });
283 }
284 }
285 else
286 {
287 ret += ({ entry[1] });
288 }
289 }
290 }
291 return ret;
292}
293
294// beim zerstoeren sollte auch die route und der Transporter aus dem traveld
295// abgemeldet werden.
296public varargs int remove(int silent)
297{
298 TRAVELD->RemoveTransporter(this_object());
299 return ::remove(silent);
300}
301
302void RemoveRoute()
303{
304 Halt();
305 route = ({ });
306 rpos = 0;
307 TRAVELD->RemoveTransporter(this_object());
308}
309
310varargs int Enter(object who)
311{
312 string *emsg;
313 mixed efail;
314
315 if (!objectp(who)) who = this_player();
316 if (environment(who) == this_object())
317 {
318 tell_object(who,"Da bist Du doch bereits, schon vergessen?\n");
319 return 1;
320 }
321 if (!QueryArrived()) return 0;
322 if (QueryProp(P_MAX_PASSENGERS) &&
323 (sizeof(QueryPassengers()) >= QueryProp(P_MAX_PASSENGERS)))
324 {
325 if (pointerp(efail=QueryProp(P_ENTERFAIL)))
326 {
327 if (sizeof(efail) == 2)
328 tell_room(this_object(),who->Name(WER,2)+" "+process_string(efail[1])+
329 ".\n",({who}));
330 tell_object(who,process_string(efail[0])+".\n");
331 }
332 else if (stringp(efail))
333 tell_object(who,process_string(efail)+".\n");
334 else if (closurep(efail)) funcall(efail);
335 return 1;
336 }
337
338 tell_object(who,"Du betrittst "+name(WEN,1)+".\n");
339 if (pointerp(emsg=QueryProp(P_ENTERMSG)) && sizeof(emsg) == 2)
340 return who->move(this_object(),M_GO,"",process_string(emsg[0]),
341 process_string(emsg[1]));
342 return who->move(this_object(),M_GO,
343 name(WEN,1),"betritt","kommt herein");
344}
345
346varargs int Leave(object who)
347{
348 string *lmsg;
349 mixed lfail;
350
351 if (!objectp(who)) who = this_player();
352 if (environment(who) != this_object())
353 {
354 if (QueryArrived())
355 {
356 tell_object(who,"Dafuer muesstest Du erstmal dort sein.\n");
357 return 1;
358 }
359 return 0;
360 }
361 if (!QueryArrived())
362 {
363 if (lfail=QueryProp(P_LEAVEFAIL))
364 {
365 if (pointerp(lfail) && sizeof(lfail))
366 {
367 if (sizeof(lfail) == 2)
368 tell_room(this_object(),who->Name(WER,2)+" "+process_string(
369 lfail[1])+".\n",({who}));
370 tell_object(who,process_string(lfail[0])+".\n");
371 }
372 else if (stringp(lfail))
373 tell_object(who,process_string(lfail)+".\n");
374 else if (closurep(lfail)) funcall(lfail);
375 return 1;
376 }
377 tell_object(who,"Fehler beim Verlassen des Transporters.\n"
378 "Bitte zustaendigen Magier verstaendigen.\n");
379 return 1;
380 }
381
382 if (who->QueryProp(P_TRAVEL_INFO)) who->SetProp(P_TRAVEL_INFO,0);
383 tell_object(who,"Du verlaesst "+name(WEN,1)+".\n");
384 if (pointerp(lmsg=QueryProp(P_LEAVEMSG)) && sizeof(lmsg) == 2)
385 return who->move(environment(),M_GO,"",process_string(lmsg[0]),
386 process_string(lmsg[1]));
387 return who->move(environment(),M_GO,
388 name(WEN,1),"verlaesst","kommt herein");
389}
390
391/*
392 ****************** Internal Functions ******************
393 */
394
395static int GoInside(string str)
396{
397 _notify_fail("Was moechtest Du denn genau?\n");
398 if (stringp(str) && id(str)) {
399 Enter();
400 return 1;
401 }
402 return 0;
403}
404
405static int GoOutside(string str)
406{
407 _notify_fail("Was moechtest Du denn genau?\n");
408 if (stringp(str) && id(str)) {
409 Leave();
410 return 1;
411 }
412 return 0;
413}
414
415static int GoInAndOutside(string str)
416{
417 string to;
418
419 _notify_fail("Was moechtest Du denn genau?\n");
420 if (!sizeof(str)) return 0;
421 if ((sscanf(str,"auf %s",to) == 1 || sscanf(str,"in %s",to) == 1) && id(to))
422 return Enter(),1;
423 if ((sscanf(str,"von %s",to) == 1 || sscanf(str,"aus %s",to) == 1) && id(to))
424 return Leave(),1;
425 return 0;
426}
427
428protected void create()
429{
430 ::create();
431
432 route = ({});
433
434 SetProp(P_LEAVEFAIL,"Das ist momentan viel zu gefaehrlich");
435 SetProp(P_ENTERFAIL,"Dort ist kein Platz mehr fuer Dich");
436 SetProp(P_TRANSPARENT,1);
437
438 AddId("Transporter");
Zesstra179db0d2016-11-26 13:13:41 +0100439
MG Mud User88f12472016-06-24 23:31:02 +0200440 call_out("SetTravelCmds",1);
441}
442
443static varargs void disconnect(int change, int change_time)
444{
445 object room;
446 mixed *departmsg;
447
448 departmsg = QueryProp(P_DEPARTMSG);
449
450 if ((room = environment()) && pointerp(departmsg))
451 {
452 tell_room(this_object(),process_string(departmsg[0]));
453 tell_room(room,process_string(departmsg[1]));
454 }
455
456 roomCode = 0;
457
458 if (change) call_out("changeHp",change_time);
459}
460
461static varargs void connect(string room, string code)
462{
463 mixed *arrivemsg, *t;
464 object *trav, ob;
465 string *trs, *msgs;
466 int i;
467
468 if (roomCode) disconnect();
469
470 roomCode = code?code:"";
471
472 if (catch(move(room,M_SILENT|M_NOCHECK);publish))
473 {
474 roomCode = 0;
475 return;
476 }
477
478 arrivemsg = QueryProp(P_ARRIVEMSG);
479
480 if (pointerp(arrivemsg))
481 {
482 tell_room(this_object(),process_string(arrivemsg[0]));
483 tell_room(room,process_string(arrivemsg[1]));
484 }
485
486 trav = filter(all_inventory(this_object()),#'living);
487
488 i = sizeof(trav);
489 while(i--)
490 {
491 if (pointerp(t = trav[i]->QueryProp(P_TRAVEL_INFO))&&
492 t[0]==this_object()&&t[2]==room)
493 {
494 if (trav[i]->InFight())
495 tell_object(trav[i],break_string("Du solltest Deinen Kampf "
496 "schnell beenden,denn eigentlich wolltest Du hier "
497 "aussteigen.",78));
498 else
499 Leave(trav[i]);
500 if (environment(trav[i])!=this_object())
501 trav[i]->SetProp(P_TRAVEL_INFO,0);
502 }
503 }
504 trav = filter(all_inventory(find_object(room))-trav,#'living);
505 i=sizeof(trav);
506 while(i--)
507 {
508 if (objectp(trav[i]) && pointerp(t = trav[i]->QueryProp(P_TRAVEL_INFO))&&
509 t[0] == environment(trav[i]) && t[1] == this_object())
510 {
511 if ( trav[i]->InFight() )
512 tell_object(trav[i],
513 break_string("Du solltest Deinen Kampf schnell beenden, denn "
514 "eigentlich wolltest Du mit "+name(WEM,1)+
515 " reisen.",78));
516 else
517 Enter(trav[i]);
518 if (environment(trav[i]) == this_object())
519 {
520 t[0] = this_object();
521 trav[i]->SetProp(P_TRAVEL_INFO,t);
522 }
523 }
524 }
525}
526
527// this object never performs any clean-up, the driver should not call it
528// again.
529int clean_up(int arg) { return 0; }
530
Zesstra179db0d2016-11-26 13:13:41 +0100531public void init()
532{
533 "*"::init();
534 // if we have player contact (even if the player is just in the same
535 // environment), we update the time.
536 if (this_player() && query_once_interactive(this_player()))
537 meet_last_player = time();
538}
539
540// we try to continue our route once some living triggers init.
541private mixed InitHookCallback(object source, int hookid, mixed hookdata)
542{
543 if (hookid == H_HOOK_INIT && previous_object() == source)
544 Continue();
545
546 return ({H_NO_MOD, hookdata});
547}
548
549// subscribes to H_HOOK_INIT in all rooms along the route
550// == 1 for success, < 0 for the number of errors
551private int subscribe_init()
552{
553 // subscribe to the H_HOOK_INIT of all rooms in the route...
554 int no_hook;
555 foreach(mixed* arr : route)
556 {
557 if (arr[0] == HP_ROOM)
558 {
559 if (arr[1]->HRegisterToHook(H_HOOK_INIT, #'InitHookCallback,
560 H_HOOK_LIBPRIO(1), H_LISTENER,
561 0) <= 0)
562 --no_hook; // Count non-success while subscribing
563 }
564 }
565 return no_hook < 0 ? no_hook : 1;
566}
567
568// unsubscribes from all the H_HOOK_INIT.
569private void unsubscribe_init()
570{
571 foreach(mixed* arr : route)
572 {
573 if (arr[0] == HP_ROOM)
574 arr[1]->HUnregisterFromHook(H_HOOK_INIT, #'InitHookCallback);
575 }
576}
577
578private int maybe_pause()
579{
580 // we check for time of last player contact. If too long ago, we pause our
581 // service.
582 if (meet_last_player < time() - 600)
583 {
584 // we don't stop if players currently are in the transporter or in the same
585 // environment (e.g. idling).
586 object *pls = filter(all_inventory(this_object())
587 + all_inventory(environment(this_object())),
588 #'interactive);
589 if (!sizeof(pls))
590 return Pause();
591 }
592 return 0;
593}
594
MG Mud User88f12472016-06-24 23:31:02 +0200595void changeHp()
596{
597 if (++rpos == sizeof(route))
598 {
599 rpos = 0;
600 //TRAVELD die aktuelle Route uebermitteln
601 ReportRoute();
Zesstra179db0d2016-11-26 13:13:41 +0100602 // everytime, we pass the end of our route, we check if we should
603 // pause our service.
604 if (maybe_pause())
605 return;
MG Mud User88f12472016-06-24 23:31:02 +0200606 }
607 if (route[rpos][0] == HP_MSG)
608 {
609 call_out("changeHp",route[rpos][2]);
610 tell_room(this_object(),route[rpos][1]);
611 }
612 else if (route[rpos][0] == HP_FUN)
613 {
614 call_out("changeHp",route[rpos][2]);
615 call_other(this_object(),route[rpos][1]);
616 }
617 else
618 {
619 call_out("disconnect",route[rpos][2],1,route[rpos][3]);
620 connect(route[rpos][1],route[rpos][4]);
621 }
622}
623