blob: 1639b40637a1540933a584adf2ffa3ac15645b0d [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();
110 rpos = (pos >= sizeof(route))?-1:pos-1;
111 call_out("changeHp",0);
112 // Tell TRAVELD our current route
113 ReportRoute();
114}
115
Zesstra179db0d2016-11-26 13:13:41 +0100116// continues the current route at the point we stopped.
117public int Continue()
118{
119 if (find_call_out("changeHp") == -1
120 && find_call_out("disconnect") == -1)
121 {
122 unsubscribe_init();
123 Start(rpos);
124 return 1;
125 }
126 return 0;
127}
128
129// pauses the transporter temporarily in a way that it continues along its
130// route as soon as a living enters one of the stop points. If that is not
131// possible, we do nothing.
132public int Pause()
133{
134 // ok, stop
135 if (subscribe_init() == 1)
136 {
137 Halt();
138 return 1;
139 }
140 return 0;
141}
142
MG Mud User88f12472016-06-24 23:31:02 +0200143void SetTravelCmds()
144{
145 if (pointerp(QueryProp(P_LEAVECMDS)))
146 AddCmd(QueryProp(P_LEAVECMDS),"GoOutside");
147 if (pointerp(QueryProp(P_ENTERCMDS)))
148 AddCmd(QueryProp(P_ENTERCMDS),"GoInside");
149 if (pointerp(QueryProp(P_TRAVEL_CMDS)))
150 AddCmd(QueryProp(P_TRAVEL_CMDS),"GoInAndOutside");
151 return;
152}
153
154mixed HasRoute(mixed dest)
155{
156 int i,s,z;
157 string str;
158 object ob;
159 mixed harb;
160
161 s = sizeof(route);
162
163 for (i = rpos;i <= rpos+s-1;i++)
164 {
165 if (route[i%s][0] == HP_ROOM)
166 {
167 if (member(route[i%s][5],dest) != -1 &&
168 objectp(ob=load_object(route[i%s][1])) &&
169 pointerp(harb=ob->QueryProp(P_HARBOUR)) &&
170 sizeof(harb))
171 {
172 return ({ route[i%s][1], harb[0] });
173 }
174 }
175 }
176 return 0;
177}
178
179public varargs void AddRoute(string room, int stay, int next,
180 string harbour_desc, string|string* dest_ids, string deststr)
181{
182 // Daten aus dem Zielanleger abfragen.
183 <string|string*>* harbour = room->QueryProp(P_HARBOUR)||({});
184 string* harbour_ids = ({});
185
186 // IDs des Zielanlegers fuer Syntaxpruefung
187 if ( sizeof(harbour)==2 )
188 {
189 if ( pointerp(harbour[1]) )
190 harbour_ids = harbour[1];
191 else
192 harbour_ids = ({harbour[1]});
193 }
194
195 // <dest_ids> in ein Array umwandeln, ist dann ggf. leer
196 if ( !dest_ids )
197 {
198 dest_ids = ({});
199 }
200 if ( stringp(dest_ids) )
201 {
202 dest_ids = ({dest_ids});
203 }
204
205 // explizit angegebene IDs stehen jetzt in <dest_ids>, die IDs des
206 // Zielhafens aus P_HARBOUR werden addiert.
207 dest_ids += harbour_ids;
208
209 // Ist <dest> immer noch leer, versuchen wir, aus <harbour_desc> ein paar
210 // Stichwoerter zu erzeugen, die man als Zielangabe in der Syntax
211 // "reise nach <ziel>" verwenden kann.
212 if ( !sizeof(dest_ids) )
213 {
214 // Grossgeschriebene Begriffe in <harbour_desc> in <dest> eintragen. Dazu:
215 // 1) <code> erstmal so zerschneiden, dass alle ueblichen Satzzeichen
216 // rausfliegen (es gibt Transporter, die sowas in <harbour_desc>
217 // uebergeben).
218 dest_ids = regexplode(harbour_desc, "[(),.;:&\+_ ]",
219 RE_OMIT_DELIM|RE_GLOBAL);
220 // 2a) So filtern, dass nur grossgeschriebene Woerter uebrig bleiben,
221 // von 1) uebriggebliebene Leerstrings gleich mit wegwerfen.
222 // 2b) Ergebnis kleinschreiben, damit die Syntaxpruefung damit arbeiten
223 // kann.
224 dest_ids = map( filter(dest_ids, function int (string key) {
225 return (key!="" && key[0]>='A' && key[0]<='Z');
226 }), #'lower_case);
227 }
228 // Sollte <dest> jetzt immer noch leer sein, wurde an allen drei Stellen
229 // nichts oder nur Muell uebergeben.
230 if ( !sizeof(dest_ids) )
231 {
232 raise_error("Transporterfehlfunktion in AddRoute(): Identifikations"
233 "matrix unzureichend definiert. Transporter unbenutzbar fuer "
234 "Spieler. Bitte mindestens eine Ziel-ID via P_HARBOUR oder als "
235 "Argument to AddRoute().");
236 }
237 route += ({ ({ HP_ROOM, room, stay, next, harbour_desc, dest_ids,
238 deststr }) });
239}
240
241varargs void AddMsg(string msg, int next)
242{
243 route += ({ ({ HP_MSG, msg, next }) });
244}
245
246void AddFun(string fun, int next) { route += ({ ({ HP_FUN, fun, next }) }); }
247
248string QueryArrived() { return roomCode; }
249
250mixed* QueryPosition()
251{
252 return ({ route[rpos][1],route[(rpos+1)<sizeof(route)?(rpos+1):0][1] });
253}
254
255object* QueryPassengers()
256{
257 return filter(all_inventory(),#'query_once_interactive);
258}
259
260varargs string *QueryHarbours(int textflag)
261{
262 string *ret = ({});
263
264 foreach( mixed* entry : route )
265 {
266 if ( entry[0] == HP_ROOM )
267 {
268 if ( textflag )
269 {
270 string *hp_ids = entry[1]->QueryProp(P_HARBOUR)[1];
271 if (pointerp(hp_ids) && sizeof(hp_ids))
272 {
273 string *h = map( explode(hp_ids[0]," "), #'capitalize);
274 ret += ({ implode(h, " ") });
275 }
276 }
277 else
278 {
279 ret += ({ entry[1] });
280 }
281 }
282 }
283 return ret;
284}
285
286// beim zerstoeren sollte auch die route und der Transporter aus dem traveld
287// abgemeldet werden.
288public varargs int remove(int silent)
289{
290 TRAVELD->RemoveTransporter(this_object());
291 return ::remove(silent);
292}
293
294void RemoveRoute()
295{
296 Halt();
297 route = ({ });
298 rpos = 0;
299 TRAVELD->RemoveTransporter(this_object());
300}
301
302varargs int Enter(object who)
303{
304 string *emsg;
305 mixed efail;
306
307 if (!objectp(who)) who = this_player();
308 if (environment(who) == this_object())
309 {
310 tell_object(who,"Da bist Du doch bereits, schon vergessen?\n");
311 return 1;
312 }
313 if (!QueryArrived()) return 0;
314 if (QueryProp(P_MAX_PASSENGERS) &&
315 (sizeof(QueryPassengers()) >= QueryProp(P_MAX_PASSENGERS)))
316 {
317 if (pointerp(efail=QueryProp(P_ENTERFAIL)))
318 {
319 if (sizeof(efail) == 2)
320 tell_room(this_object(),who->Name(WER,2)+" "+process_string(efail[1])+
321 ".\n",({who}));
322 tell_object(who,process_string(efail[0])+".\n");
323 }
324 else if (stringp(efail))
325 tell_object(who,process_string(efail)+".\n");
326 else if (closurep(efail)) funcall(efail);
327 return 1;
328 }
329
330 tell_object(who,"Du betrittst "+name(WEN,1)+".\n");
331 if (pointerp(emsg=QueryProp(P_ENTERMSG)) && sizeof(emsg) == 2)
332 return who->move(this_object(),M_GO,"",process_string(emsg[0]),
333 process_string(emsg[1]));
334 return who->move(this_object(),M_GO,
335 name(WEN,1),"betritt","kommt herein");
336}
337
338varargs int Leave(object who)
339{
340 string *lmsg;
341 mixed lfail;
342
343 if (!objectp(who)) who = this_player();
344 if (environment(who) != this_object())
345 {
346 if (QueryArrived())
347 {
348 tell_object(who,"Dafuer muesstest Du erstmal dort sein.\n");
349 return 1;
350 }
351 return 0;
352 }
353 if (!QueryArrived())
354 {
355 if (lfail=QueryProp(P_LEAVEFAIL))
356 {
357 if (pointerp(lfail) && sizeof(lfail))
358 {
359 if (sizeof(lfail) == 2)
360 tell_room(this_object(),who->Name(WER,2)+" "+process_string(
361 lfail[1])+".\n",({who}));
362 tell_object(who,process_string(lfail[0])+".\n");
363 }
364 else if (stringp(lfail))
365 tell_object(who,process_string(lfail)+".\n");
366 else if (closurep(lfail)) funcall(lfail);
367 return 1;
368 }
369 tell_object(who,"Fehler beim Verlassen des Transporters.\n"
370 "Bitte zustaendigen Magier verstaendigen.\n");
371 return 1;
372 }
373
374 if (who->QueryProp(P_TRAVEL_INFO)) who->SetProp(P_TRAVEL_INFO,0);
375 tell_object(who,"Du verlaesst "+name(WEN,1)+".\n");
376 if (pointerp(lmsg=QueryProp(P_LEAVEMSG)) && sizeof(lmsg) == 2)
377 return who->move(environment(),M_GO,"",process_string(lmsg[0]),
378 process_string(lmsg[1]));
379 return who->move(environment(),M_GO,
380 name(WEN,1),"verlaesst","kommt herein");
381}
382
383/*
384 ****************** Internal Functions ******************
385 */
386
387static int GoInside(string str)
388{
389 _notify_fail("Was moechtest Du denn genau?\n");
390 if (stringp(str) && id(str)) {
391 Enter();
392 return 1;
393 }
394 return 0;
395}
396
397static int GoOutside(string str)
398{
399 _notify_fail("Was moechtest Du denn genau?\n");
400 if (stringp(str) && id(str)) {
401 Leave();
402 return 1;
403 }
404 return 0;
405}
406
407static int GoInAndOutside(string str)
408{
409 string to;
410
411 _notify_fail("Was moechtest Du denn genau?\n");
412 if (!sizeof(str)) return 0;
413 if ((sscanf(str,"auf %s",to) == 1 || sscanf(str,"in %s",to) == 1) && id(to))
414 return Enter(),1;
415 if ((sscanf(str,"von %s",to) == 1 || sscanf(str,"aus %s",to) == 1) && id(to))
416 return Leave(),1;
417 return 0;
418}
419
420protected void create()
421{
422 ::create();
423
424 route = ({});
425
426 SetProp(P_LEAVEFAIL,"Das ist momentan viel zu gefaehrlich");
427 SetProp(P_ENTERFAIL,"Dort ist kein Platz mehr fuer Dich");
428 SetProp(P_TRANSPARENT,1);
429
430 AddId("Transporter");
Zesstra179db0d2016-11-26 13:13:41 +0100431
MG Mud User88f12472016-06-24 23:31:02 +0200432 call_out("SetTravelCmds",1);
433}
434
435static varargs void disconnect(int change, int change_time)
436{
437 object room;
438 mixed *departmsg;
439
440 departmsg = QueryProp(P_DEPARTMSG);
441
442 if ((room = environment()) && pointerp(departmsg))
443 {
444 tell_room(this_object(),process_string(departmsg[0]));
445 tell_room(room,process_string(departmsg[1]));
446 }
447
448 roomCode = 0;
449
450 if (change) call_out("changeHp",change_time);
451}
452
453static varargs void connect(string room, string code)
454{
455 mixed *arrivemsg, *t;
456 object *trav, ob;
457 string *trs, *msgs;
458 int i;
459
460 if (roomCode) disconnect();
461
462 roomCode = code?code:"";
463
464 if (catch(move(room,M_SILENT|M_NOCHECK);publish))
465 {
466 roomCode = 0;
467 return;
468 }
469
470 arrivemsg = QueryProp(P_ARRIVEMSG);
471
472 if (pointerp(arrivemsg))
473 {
474 tell_room(this_object(),process_string(arrivemsg[0]));
475 tell_room(room,process_string(arrivemsg[1]));
476 }
477
478 trav = filter(all_inventory(this_object()),#'living);
479
480 i = sizeof(trav);
481 while(i--)
482 {
483 if (pointerp(t = trav[i]->QueryProp(P_TRAVEL_INFO))&&
484 t[0]==this_object()&&t[2]==room)
485 {
486 if (trav[i]->InFight())
487 tell_object(trav[i],break_string("Du solltest Deinen Kampf "
488 "schnell beenden,denn eigentlich wolltest Du hier "
489 "aussteigen.",78));
490 else
491 Leave(trav[i]);
492 if (environment(trav[i])!=this_object())
493 trav[i]->SetProp(P_TRAVEL_INFO,0);
494 }
495 }
496 trav = filter(all_inventory(find_object(room))-trav,#'living);
497 i=sizeof(trav);
498 while(i--)
499 {
500 if (objectp(trav[i]) && pointerp(t = trav[i]->QueryProp(P_TRAVEL_INFO))&&
501 t[0] == environment(trav[i]) && t[1] == this_object())
502 {
503 if ( trav[i]->InFight() )
504 tell_object(trav[i],
505 break_string("Du solltest Deinen Kampf schnell beenden, denn "
506 "eigentlich wolltest Du mit "+name(WEM,1)+
507 " reisen.",78));
508 else
509 Enter(trav[i]);
510 if (environment(trav[i]) == this_object())
511 {
512 t[0] = this_object();
513 trav[i]->SetProp(P_TRAVEL_INFO,t);
514 }
515 }
516 }
517}
518
519// this object never performs any clean-up, the driver should not call it
520// again.
521int clean_up(int arg) { return 0; }
522
Zesstra179db0d2016-11-26 13:13:41 +0100523public void init()
524{
525 "*"::init();
526 // if we have player contact (even if the player is just in the same
527 // environment), we update the time.
528 if (this_player() && query_once_interactive(this_player()))
529 meet_last_player = time();
530}
531
532// we try to continue our route once some living triggers init.
533private mixed InitHookCallback(object source, int hookid, mixed hookdata)
534{
535 if (hookid == H_HOOK_INIT && previous_object() == source)
536 Continue();
537
538 return ({H_NO_MOD, hookdata});
539}
540
541// subscribes to H_HOOK_INIT in all rooms along the route
542// == 1 for success, < 0 for the number of errors
543private int subscribe_init()
544{
545 // subscribe to the H_HOOK_INIT of all rooms in the route...
546 int no_hook;
547 foreach(mixed* arr : route)
548 {
549 if (arr[0] == HP_ROOM)
550 {
551 if (arr[1]->HRegisterToHook(H_HOOK_INIT, #'InitHookCallback,
552 H_HOOK_LIBPRIO(1), H_LISTENER,
553 0) <= 0)
554 --no_hook; // Count non-success while subscribing
555 }
556 }
557 return no_hook < 0 ? no_hook : 1;
558}
559
560// unsubscribes from all the H_HOOK_INIT.
561private void unsubscribe_init()
562{
563 foreach(mixed* arr : route)
564 {
565 if (arr[0] == HP_ROOM)
566 arr[1]->HUnregisterFromHook(H_HOOK_INIT, #'InitHookCallback);
567 }
568}
569
570private int maybe_pause()
571{
572 // we check for time of last player contact. If too long ago, we pause our
573 // service.
574 if (meet_last_player < time() - 600)
575 {
576 // we don't stop if players currently are in the transporter or in the same
577 // environment (e.g. idling).
578 object *pls = filter(all_inventory(this_object())
579 + all_inventory(environment(this_object())),
580 #'interactive);
581 if (!sizeof(pls))
582 return Pause();
583 }
584 return 0;
585}
586
MG Mud User88f12472016-06-24 23:31:02 +0200587void changeHp()
588{
589 if (++rpos == sizeof(route))
590 {
591 rpos = 0;
592 //TRAVELD die aktuelle Route uebermitteln
593 ReportRoute();
Zesstra179db0d2016-11-26 13:13:41 +0100594 // everytime, we pass the end of our route, we check if we should
595 // pause our service.
596 if (maybe_pause())
597 return;
MG Mud User88f12472016-06-24 23:31:02 +0200598 }
599 if (route[rpos][0] == HP_MSG)
600 {
601 call_out("changeHp",route[rpos][2]);
602 tell_room(this_object(),route[rpos][1]);
603 }
604 else if (route[rpos][0] == HP_FUN)
605 {
606 call_out("changeHp",route[rpos][2]);
607 call_other(this_object(),route[rpos][1]);
608 }
609 else
610 {
611 call_out("disconnect",route[rpos][2],1,route[rpos][3]);
612 connect(route[rpos][1],route[rpos][4]);
613 }
614}
615