blob: 07f41f3b5f2d2ff03642227d68deee558e7e88f7 [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). */
Zesstrae805e3a2018-02-12 19:48:06 +010054// Letzter Spielerkontakt. Das muss != 0 sein (sonst funktioniert der
55// Mechanismus zum Fortsetzen der Route nach einer Pause nicht ordentlich,
56// daher wird es auf 1 initialisiert.
57nosave int meet_last_player = 1;
Zesstra179db0d2016-11-26 13:13:41 +010058
59private void unsubscribe_init();
60private int subscribe_init();
MG Mud User88f12472016-06-24 23:31:02 +020061
62/*
63 ********** Management der builtin-properties **********
64 */
65
66string _query_short()
67{
68 if (roomCode) return Query(P_SHORT);
69 return 0;
70}
71
72mixed _query_transparent()
73{
74 if (roomCode) return Query(P_TRANSPARENT);
75 return 0;
76}
77
Zesstra179db0d2016-11-26 13:13:41 +010078static mixed *_set_route(mixed *r) { return route = r; }
79static mixed *_query_route() { return route; }
80static int _query_mnpc_last_meet() { return meet_last_player; }
MG Mud User88f12472016-06-24 23:31:02 +020081
82/*
83 **************** Zugriffsfunktionen ***************
84 */
85
Zesstra179db0d2016-11-26 13:13:41 +010086public void Halt()
MG Mud User88f12472016-06-24 23:31:02 +020087{
Zesstra179db0d2016-11-26 13:13:41 +010088 // stop, but keep rpos counter.
MG Mud User88f12472016-06-24 23:31:02 +020089 while (remove_call_out( "changeHp" )>-1);
90 while (remove_call_out( "disconnect" )>-1);
91}
92
93// Aktualisiert/Setzt die Route im TravelD, wenn erlaubt (d.h. kein
94// P_NO_TRAVELING)
95private void ReportRoute()
96{
97 if(!QueryProp(P_NO_TRAVELING))
98 {
99 mixed tmp = filter(route, function int (mixed arr)
100 {
101 return arr[0] == HP_ROOM;
102 } );
103 string *route = map(tmp, function string (mixed arr)
104 { return arr[1]; }
105 );
106 TRAVELD->AddRoute(object_name(this_object()),route);
107 }
108}
109
Zesstra179db0d2016-11-26 13:13:41 +0100110public varargs void Start(int pos)
MG Mud User88f12472016-06-24 23:31:02 +0200111{
112 Halt();
Zesstra5d2ace02017-06-20 23:21:36 +0200113 // negative pos sind ein Fehler
114 if (pos<0)
115 raise_error(sprintf("Start(): Positionszaehler < 0: %d\n",pos));
116
117 // wenn pos zu gross fuer die Route ist, rpos auf Ende der Route setzen
118 // (i.e. sizeof(route)-1), damit bei der naechsten Bewegung am Anfang der
119 // Route begonnen wird.
120 rpos = min(pos, sizeof(route)-1);
121
MG Mud User88f12472016-06-24 23:31:02 +0200122 call_out("changeHp",0);
123 // Tell TRAVELD our current route
124 ReportRoute();
125}
126
Zesstra179db0d2016-11-26 13:13:41 +0100127// continues the current route at the point we stopped.
128public int Continue()
129{
130 if (find_call_out("changeHp") == -1
131 && find_call_out("disconnect") == -1)
132 {
Zesstra019987a2017-10-24 22:36:08 +0200133 // Nach einer Pause wird die Route am aktuellen Haltepunkt fortgesetzt
134 // (im Regelfall also am Ende der Route). Am Routenende wird auch
135 // geprueft, wann der letzte Spielerkontakt war. Das darf nach einem
136 // Continue() aber nicht passieren, sonst wuerde der Transporter ggf.
137 // sofort wieder anhalten.
138 meet_last_player*=-1; // neg. vorzeichen als Markierung
Zesstra179db0d2016-11-26 13:13:41 +0100139 unsubscribe_init();
140 Start(rpos);
141 return 1;
142 }
143 return 0;
144}
145
146// pauses the transporter temporarily in a way that it continues along its
147// route as soon as a living enters one of the stop points. If that is not
148// possible, we do nothing.
149public int Pause()
150{
151 // ok, stop
152 if (subscribe_init() == 1)
153 {
154 Halt();
155 return 1;
156 }
157 return 0;
158}
159
MG Mud User88f12472016-06-24 23:31:02 +0200160void SetTravelCmds()
161{
162 if (pointerp(QueryProp(P_LEAVECMDS)))
163 AddCmd(QueryProp(P_LEAVECMDS),"GoOutside");
164 if (pointerp(QueryProp(P_ENTERCMDS)))
165 AddCmd(QueryProp(P_ENTERCMDS),"GoInside");
166 if (pointerp(QueryProp(P_TRAVEL_CMDS)))
167 AddCmd(QueryProp(P_TRAVEL_CMDS),"GoInAndOutside");
168 return;
169}
170
171mixed HasRoute(mixed dest)
172{
173 int i,s,z;
174 string str;
175 object ob;
176 mixed harb;
177
178 s = sizeof(route);
179
180 for (i = rpos;i <= rpos+s-1;i++)
181 {
182 if (route[i%s][0] == HP_ROOM)
183 {
184 if (member(route[i%s][5],dest) != -1 &&
185 objectp(ob=load_object(route[i%s][1])) &&
186 pointerp(harb=ob->QueryProp(P_HARBOUR)) &&
187 sizeof(harb))
188 {
189 return ({ route[i%s][1], harb[0] });
190 }
191 }
192 }
193 return 0;
194}
195
196public varargs void AddRoute(string room, int stay, int next,
197 string harbour_desc, string|string* dest_ids, string deststr)
198{
199 // Daten aus dem Zielanleger abfragen.
200 <string|string*>* harbour = room->QueryProp(P_HARBOUR)||({});
201 string* harbour_ids = ({});
202
203 // IDs des Zielanlegers fuer Syntaxpruefung
204 if ( sizeof(harbour)==2 )
205 {
206 if ( pointerp(harbour[1]) )
207 harbour_ids = harbour[1];
208 else
209 harbour_ids = ({harbour[1]});
210 }
211
212 // <dest_ids> in ein Array umwandeln, ist dann ggf. leer
213 if ( !dest_ids )
214 {
215 dest_ids = ({});
216 }
217 if ( stringp(dest_ids) )
218 {
219 dest_ids = ({dest_ids});
220 }
221
222 // explizit angegebene IDs stehen jetzt in <dest_ids>, die IDs des
223 // Zielhafens aus P_HARBOUR werden addiert.
224 dest_ids += harbour_ids;
225
226 // Ist <dest> immer noch leer, versuchen wir, aus <harbour_desc> ein paar
227 // Stichwoerter zu erzeugen, die man als Zielangabe in der Syntax
228 // "reise nach <ziel>" verwenden kann.
229 if ( !sizeof(dest_ids) )
230 {
231 // Grossgeschriebene Begriffe in <harbour_desc> in <dest> eintragen. Dazu:
232 // 1) <code> erstmal so zerschneiden, dass alle ueblichen Satzzeichen
233 // rausfliegen (es gibt Transporter, die sowas in <harbour_desc>
234 // uebergeben).
235 dest_ids = regexplode(harbour_desc, "[(),.;:&\+_ ]",
236 RE_OMIT_DELIM|RE_GLOBAL);
237 // 2a) So filtern, dass nur grossgeschriebene Woerter uebrig bleiben,
238 // von 1) uebriggebliebene Leerstrings gleich mit wegwerfen.
239 // 2b) Ergebnis kleinschreiben, damit die Syntaxpruefung damit arbeiten
240 // kann.
241 dest_ids = map( filter(dest_ids, function int (string key) {
242 return (key!="" && key[0]>='A' && key[0]<='Z');
243 }), #'lower_case);
244 }
245 // Sollte <dest> jetzt immer noch leer sein, wurde an allen drei Stellen
246 // nichts oder nur Muell uebergeben.
247 if ( !sizeof(dest_ids) )
248 {
249 raise_error("Transporterfehlfunktion in AddRoute(): Identifikations"
250 "matrix unzureichend definiert. Transporter unbenutzbar fuer "
251 "Spieler. Bitte mindestens eine Ziel-ID via P_HARBOUR oder als "
252 "Argument to AddRoute().");
253 }
254 route += ({ ({ HP_ROOM, room, stay, next, harbour_desc, dest_ids,
255 deststr }) });
256}
257
258varargs void AddMsg(string msg, int next)
259{
260 route += ({ ({ HP_MSG, msg, next }) });
261}
262
263void AddFun(string fun, int next) { route += ({ ({ HP_FUN, fun, next }) }); }
264
265string QueryArrived() { return roomCode; }
266
267mixed* QueryPosition()
268{
269 return ({ route[rpos][1],route[(rpos+1)<sizeof(route)?(rpos+1):0][1] });
270}
271
272object* QueryPassengers()
273{
274 return filter(all_inventory(),#'query_once_interactive);
275}
276
277varargs string *QueryHarbours(int textflag)
278{
279 string *ret = ({});
280
281 foreach( mixed* entry : route )
282 {
283 if ( entry[0] == HP_ROOM )
284 {
285 if ( textflag )
286 {
287 string *hp_ids = entry[1]->QueryProp(P_HARBOUR)[1];
288 if (pointerp(hp_ids) && sizeof(hp_ids))
289 {
290 string *h = map( explode(hp_ids[0]," "), #'capitalize);
291 ret += ({ implode(h, " ") });
292 }
293 }
294 else
295 {
296 ret += ({ entry[1] });
297 }
298 }
299 }
300 return ret;
301}
302
303// beim zerstoeren sollte auch die route und der Transporter aus dem traveld
304// abgemeldet werden.
305public varargs int remove(int silent)
306{
307 TRAVELD->RemoveTransporter(this_object());
308 return ::remove(silent);
309}
310
311void RemoveRoute()
312{
313 Halt();
314 route = ({ });
315 rpos = 0;
316 TRAVELD->RemoveTransporter(this_object());
317}
318
319varargs int Enter(object who)
320{
321 string *emsg;
322 mixed efail;
323
324 if (!objectp(who)) who = this_player();
325 if (environment(who) == this_object())
326 {
327 tell_object(who,"Da bist Du doch bereits, schon vergessen?\n");
328 return 1;
329 }
330 if (!QueryArrived()) return 0;
331 if (QueryProp(P_MAX_PASSENGERS) &&
332 (sizeof(QueryPassengers()) >= QueryProp(P_MAX_PASSENGERS)))
333 {
334 if (pointerp(efail=QueryProp(P_ENTERFAIL)))
335 {
336 if (sizeof(efail) == 2)
337 tell_room(this_object(),who->Name(WER,2)+" "+process_string(efail[1])+
338 ".\n",({who}));
339 tell_object(who,process_string(efail[0])+".\n");
340 }
341 else if (stringp(efail))
342 tell_object(who,process_string(efail)+".\n");
343 else if (closurep(efail)) funcall(efail);
344 return 1;
345 }
346
347 tell_object(who,"Du betrittst "+name(WEN,1)+".\n");
348 if (pointerp(emsg=QueryProp(P_ENTERMSG)) && sizeof(emsg) == 2)
349 return who->move(this_object(),M_GO,"",process_string(emsg[0]),
350 process_string(emsg[1]));
351 return who->move(this_object(),M_GO,
352 name(WEN,1),"betritt","kommt herein");
353}
354
355varargs int Leave(object who)
356{
357 string *lmsg;
358 mixed lfail;
359
360 if (!objectp(who)) who = this_player();
361 if (environment(who) != this_object())
362 {
363 if (QueryArrived())
364 {
365 tell_object(who,"Dafuer muesstest Du erstmal dort sein.\n");
366 return 1;
367 }
368 return 0;
369 }
370 if (!QueryArrived())
371 {
372 if (lfail=QueryProp(P_LEAVEFAIL))
373 {
374 if (pointerp(lfail) && sizeof(lfail))
375 {
376 if (sizeof(lfail) == 2)
377 tell_room(this_object(),who->Name(WER,2)+" "+process_string(
378 lfail[1])+".\n",({who}));
379 tell_object(who,process_string(lfail[0])+".\n");
380 }
381 else if (stringp(lfail))
382 tell_object(who,process_string(lfail)+".\n");
383 else if (closurep(lfail)) funcall(lfail);
384 return 1;
385 }
386 tell_object(who,"Fehler beim Verlassen des Transporters.\n"
387 "Bitte zustaendigen Magier verstaendigen.\n");
388 return 1;
389 }
390
391 if (who->QueryProp(P_TRAVEL_INFO)) who->SetProp(P_TRAVEL_INFO,0);
392 tell_object(who,"Du verlaesst "+name(WEN,1)+".\n");
393 if (pointerp(lmsg=QueryProp(P_LEAVEMSG)) && sizeof(lmsg) == 2)
394 return who->move(environment(),M_GO,"",process_string(lmsg[0]),
395 process_string(lmsg[1]));
396 return who->move(environment(),M_GO,
397 name(WEN,1),"verlaesst","kommt herein");
398}
399
400/*
401 ****************** Internal Functions ******************
402 */
403
404static int GoInside(string str)
405{
406 _notify_fail("Was moechtest Du denn genau?\n");
407 if (stringp(str) && id(str)) {
408 Enter();
409 return 1;
410 }
411 return 0;
412}
413
414static int GoOutside(string str)
415{
416 _notify_fail("Was moechtest Du denn genau?\n");
417 if (stringp(str) && id(str)) {
418 Leave();
419 return 1;
420 }
421 return 0;
422}
423
424static int GoInAndOutside(string str)
425{
426 string to;
427
428 _notify_fail("Was moechtest Du denn genau?\n");
429 if (!sizeof(str)) return 0;
430 if ((sscanf(str,"auf %s",to) == 1 || sscanf(str,"in %s",to) == 1) && id(to))
431 return Enter(),1;
432 if ((sscanf(str,"von %s",to) == 1 || sscanf(str,"aus %s",to) == 1) && id(to))
433 return Leave(),1;
434 return 0;
435}
436
437protected void create()
438{
439 ::create();
440
441 route = ({});
442
443 SetProp(P_LEAVEFAIL,"Das ist momentan viel zu gefaehrlich");
444 SetProp(P_ENTERFAIL,"Dort ist kein Platz mehr fuer Dich");
445 SetProp(P_TRANSPARENT,1);
446
447 AddId("Transporter");
Zesstra179db0d2016-11-26 13:13:41 +0100448
MG Mud User88f12472016-06-24 23:31:02 +0200449 call_out("SetTravelCmds",1);
450}
451
452static varargs void disconnect(int change, int change_time)
453{
454 object room;
455 mixed *departmsg;
456
457 departmsg = QueryProp(P_DEPARTMSG);
458
459 if ((room = environment()) && pointerp(departmsg))
460 {
461 tell_room(this_object(),process_string(departmsg[0]));
462 tell_room(room,process_string(departmsg[1]));
463 }
464
465 roomCode = 0;
466
467 if (change) call_out("changeHp",change_time);
468}
469
470static varargs void connect(string room, string code)
471{
472 mixed *arrivemsg, *t;
473 object *trav, ob;
474 string *trs, *msgs;
475 int i;
476
477 if (roomCode) disconnect();
478
479 roomCode = code?code:"";
480
481 if (catch(move(room,M_SILENT|M_NOCHECK);publish))
482 {
483 roomCode = 0;
484 return;
485 }
486
487 arrivemsg = QueryProp(P_ARRIVEMSG);
488
489 if (pointerp(arrivemsg))
490 {
491 tell_room(this_object(),process_string(arrivemsg[0]));
492 tell_room(room,process_string(arrivemsg[1]));
493 }
494
495 trav = filter(all_inventory(this_object()),#'living);
496
497 i = sizeof(trav);
498 while(i--)
499 {
500 if (pointerp(t = trav[i]->QueryProp(P_TRAVEL_INFO))&&
501 t[0]==this_object()&&t[2]==room)
502 {
503 if (trav[i]->InFight())
504 tell_object(trav[i],break_string("Du solltest Deinen Kampf "
505 "schnell beenden,denn eigentlich wolltest Du hier "
506 "aussteigen.",78));
507 else
508 Leave(trav[i]);
509 if (environment(trav[i])!=this_object())
510 trav[i]->SetProp(P_TRAVEL_INFO,0);
511 }
512 }
513 trav = filter(all_inventory(find_object(room))-trav,#'living);
514 i=sizeof(trav);
515 while(i--)
516 {
517 if (objectp(trav[i]) && pointerp(t = trav[i]->QueryProp(P_TRAVEL_INFO))&&
518 t[0] == environment(trav[i]) && t[1] == this_object())
519 {
520 if ( trav[i]->InFight() )
521 tell_object(trav[i],
522 break_string("Du solltest Deinen Kampf schnell beenden, denn "
523 "eigentlich wolltest Du mit "+name(WEM,1)+
524 " reisen.",78));
525 else
526 Enter(trav[i]);
527 if (environment(trav[i]) == this_object())
528 {
529 t[0] = this_object();
530 trav[i]->SetProp(P_TRAVEL_INFO,t);
531 }
532 }
533 }
534}
535
536// this object never performs any clean-up, the driver should not call it
537// again.
538int clean_up(int arg) { return 0; }
539
Zesstra179db0d2016-11-26 13:13:41 +0100540public void init()
541{
542 "*"::init();
543 // if we have player contact (even if the player is just in the same
544 // environment), we update the time.
545 if (this_player() && query_once_interactive(this_player()))
546 meet_last_player = time();
547}
548
549// we try to continue our route once some living triggers init.
550private mixed InitHookCallback(object source, int hookid, mixed hookdata)
551{
552 if (hookid == H_HOOK_INIT && previous_object() == source)
553 Continue();
554
555 return ({H_NO_MOD, hookdata});
556}
557
558// subscribes to H_HOOK_INIT in all rooms along the route
559// == 1 for success, < 0 for the number of errors
560private int subscribe_init()
561{
562 // subscribe to the H_HOOK_INIT of all rooms in the route...
563 int no_hook;
564 foreach(mixed* arr : route)
565 {
566 if (arr[0] == HP_ROOM)
567 {
568 if (arr[1]->HRegisterToHook(H_HOOK_INIT, #'InitHookCallback,
569 H_HOOK_LIBPRIO(1), H_LISTENER,
570 0) <= 0)
571 --no_hook; // Count non-success while subscribing
572 }
573 }
574 return no_hook < 0 ? no_hook : 1;
575}
576
577// unsubscribes from all the H_HOOK_INIT.
578private void unsubscribe_init()
579{
580 foreach(mixed* arr : route)
581 {
582 if (arr[0] == HP_ROOM)
583 arr[1]->HUnregisterFromHook(H_HOOK_INIT, #'InitHookCallback);
584 }
585}
586
587private int maybe_pause()
588{
589 // we check for time of last player contact. If too long ago, we pause our
590 // service.
591 if (meet_last_player < time() - 600)
592 {
593 // we don't stop if players currently are in the transporter or in the same
594 // environment (e.g. idling).
595 object *pls = filter(all_inventory(this_object())
596 + all_inventory(environment(this_object())),
597 #'interactive);
598 if (!sizeof(pls))
599 return Pause();
600 }
601 return 0;
602}
603
MG Mud User88f12472016-06-24 23:31:02 +0200604void changeHp()
605{
Zesstrafffd2a82017-10-24 22:03:27 +0200606 // Nicht am Ende der Route? Eins weiter.
607 if (rpos < sizeof(route) - 1)
608 ++rpos;
609 else
MG Mud User88f12472016-06-24 23:31:02 +0200610 {
Zesstra019987a2017-10-24 22:36:08 +0200611 // Routenende
612 // Nach einem expliziten Continue() ist meet_last_player < 0. Dann wird
613 // nicht geprueft, ob wir sofort wieder anhalten. Auch muss dann die Route
614 // nicht uebermittelt werden (hat Start() schon gemacht).
615 if (meet_last_player >= 0)
616 {
617 // TRAVELD die aktuelle Route uebermitteln
618 ReportRoute();
619 // everytime, we pass the end of our route, we check if we should
620 // pause our service.
621 if (maybe_pause())
622 return;
623 }
624 else
625 // Wieder pruefen im naechsten Durchlauf.
626 meet_last_player=abs(meet_last_player);
627
Zesstrafffd2a82017-10-24 22:03:27 +0200628 // wenn keine Pause, wieder zum Anfang der Route bewegen.
629 rpos = 0;
MG Mud User88f12472016-06-24 23:31:02 +0200630 }
Zesstrafffd2a82017-10-24 22:03:27 +0200631
MG Mud User88f12472016-06-24 23:31:02 +0200632 if (route[rpos][0] == HP_MSG)
633 {
634 call_out("changeHp",route[rpos][2]);
635 tell_room(this_object(),route[rpos][1]);
636 }
637 else if (route[rpos][0] == HP_FUN)
638 {
639 call_out("changeHp",route[rpos][2]);
640 call_other(this_object(),route[rpos][1]);
641 }
642 else
643 {
644 call_out("disconnect",route[rpos][2],1,route[rpos][3]);
645 connect(route[rpos][1],route[rpos][4]);
646 }
647}
648