blob: fffe1e0aa4dda5c464ca814c828a532567b588a8 [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 $
6#pragma strong_types
7//#pragma save_types
8#pragma range_check
9#pragma no_clone
10#pragma pedantic
11
12inherit "/std/thing/moving";
13inherit "/std/room";
14
15#include <properties.h>
16#include <moving.h>
17#include <defines.h>
18#include <language.h>
19#include <transport.h>
20#include <regexp.h>
21
22/* transport.c
23 *
24 * Ueberarbeitete und
25 * erweiterte Version : Tilly@MorgenGrauen, 10.01.02
26 * Basierend auf : transport.c@SilberLand (Woody@SilberLand), 05.12.99
27 * Basierend auf : Hates und Rumatas generisches Transport Objekt
28 * MorgenGrauen 15.02.93
29 */
30
31/*
32 ********************* Variablen *********************
33 */
34
35// TODO: langfristig waer ja private schoen...
36//
37// Datenstruktur von 'route' (bei HP_ROOM)
38// 0 ({string ID, : HP_ROOM
39// 1 string room, : Dateiname Zielraum
40// 2 int stay, : Dauer Haltezeit
41// 3 int next, : Dauer naechste Fahrtzeit
42// 4 string code, : Haltestellenname fuer QueryArrived
43// 5 mixed dest, : Haltestellen-IDs fuer HasRoute (reise nach)
44// 6 mixed deststr }): unbenutzt.
45//
46// Datenstruktur von 'route' (bei HP_MSG, HP_FUN)
47// 0 ({string ID, : HP_MSG
48// 1 string message, : Meldung oder string fun : Funktionsname
49// 2 int next}) : Dauer bis zum naechsten Ereignis
50nosave mixed *route; /* Liste der Haltepunkte. */
51nosave int rpos; /* Momentane Position in obiger Liste. */
52nosave string roomCode; /* Code des aktuellen Raumes (oder 0). */
53
54/*
55 ********** Management der builtin-properties **********
56 */
57
58string _query_short()
59{
60 if (roomCode) return Query(P_SHORT);
61 return 0;
62}
63
64mixed _query_transparent()
65{
66 if (roomCode) return Query(P_TRANSPARENT);
67 return 0;
68}
69
70mixed *_set_route(mixed *r) { return route = r; }
71mixed *_query_route() { return route; }
72
73/*
74 **************** Zugriffsfunktionen ***************
75 */
76
77void Halt()
78{
79 while (remove_call_out( "changeHp" )>-1);
80 while (remove_call_out( "disconnect" )>-1);
81}
82
83// Aktualisiert/Setzt die Route im TravelD, wenn erlaubt (d.h. kein
84// P_NO_TRAVELING)
85private void ReportRoute()
86{
87 if(!QueryProp(P_NO_TRAVELING))
88 {
89 mixed tmp = filter(route, function int (mixed arr)
90 {
91 return arr[0] == HP_ROOM;
92 } );
93 string *route = map(tmp, function string (mixed arr)
94 { return arr[1]; }
95 );
96 TRAVELD->AddRoute(object_name(this_object()),route);
97 }
98}
99
100varargs void Start(int pos)
101{
102 Halt();
103 rpos = (pos >= sizeof(route))?-1:pos-1;
104 call_out("changeHp",0);
105 // Tell TRAVELD our current route
106 ReportRoute();
107}
108
109void SetTravelCmds()
110{
111 if (pointerp(QueryProp(P_LEAVECMDS)))
112 AddCmd(QueryProp(P_LEAVECMDS),"GoOutside");
113 if (pointerp(QueryProp(P_ENTERCMDS)))
114 AddCmd(QueryProp(P_ENTERCMDS),"GoInside");
115 if (pointerp(QueryProp(P_TRAVEL_CMDS)))
116 AddCmd(QueryProp(P_TRAVEL_CMDS),"GoInAndOutside");
117 return;
118}
119
120mixed HasRoute(mixed dest)
121{
122 int i,s,z;
123 string str;
124 object ob;
125 mixed harb;
126
127 s = sizeof(route);
128
129 for (i = rpos;i <= rpos+s-1;i++)
130 {
131 if (route[i%s][0] == HP_ROOM)
132 {
133 if (member(route[i%s][5],dest) != -1 &&
134 objectp(ob=load_object(route[i%s][1])) &&
135 pointerp(harb=ob->QueryProp(P_HARBOUR)) &&
136 sizeof(harb))
137 {
138 return ({ route[i%s][1], harb[0] });
139 }
140 }
141 }
142 return 0;
143}
144
145public varargs void AddRoute(string room, int stay, int next,
146 string harbour_desc, string|string* dest_ids, string deststr)
147{
148 // Daten aus dem Zielanleger abfragen.
149 <string|string*>* harbour = room->QueryProp(P_HARBOUR)||({});
150 string* harbour_ids = ({});
151
152 // IDs des Zielanlegers fuer Syntaxpruefung
153 if ( sizeof(harbour)==2 )
154 {
155 if ( pointerp(harbour[1]) )
156 harbour_ids = harbour[1];
157 else
158 harbour_ids = ({harbour[1]});
159 }
160
161 // <dest_ids> in ein Array umwandeln, ist dann ggf. leer
162 if ( !dest_ids )
163 {
164 dest_ids = ({});
165 }
166 if ( stringp(dest_ids) )
167 {
168 dest_ids = ({dest_ids});
169 }
170
171 // explizit angegebene IDs stehen jetzt in <dest_ids>, die IDs des
172 // Zielhafens aus P_HARBOUR werden addiert.
173 dest_ids += harbour_ids;
174
175 // Ist <dest> immer noch leer, versuchen wir, aus <harbour_desc> ein paar
176 // Stichwoerter zu erzeugen, die man als Zielangabe in der Syntax
177 // "reise nach <ziel>" verwenden kann.
178 if ( !sizeof(dest_ids) )
179 {
180 // Grossgeschriebene Begriffe in <harbour_desc> in <dest> eintragen. Dazu:
181 // 1) <code> erstmal so zerschneiden, dass alle ueblichen Satzzeichen
182 // rausfliegen (es gibt Transporter, die sowas in <harbour_desc>
183 // uebergeben).
184 dest_ids = regexplode(harbour_desc, "[(),.;:&\+_ ]",
185 RE_OMIT_DELIM|RE_GLOBAL);
186 // 2a) So filtern, dass nur grossgeschriebene Woerter uebrig bleiben,
187 // von 1) uebriggebliebene Leerstrings gleich mit wegwerfen.
188 // 2b) Ergebnis kleinschreiben, damit die Syntaxpruefung damit arbeiten
189 // kann.
190 dest_ids = map( filter(dest_ids, function int (string key) {
191 return (key!="" && key[0]>='A' && key[0]<='Z');
192 }), #'lower_case);
193 }
194 // Sollte <dest> jetzt immer noch leer sein, wurde an allen drei Stellen
195 // nichts oder nur Muell uebergeben.
196 if ( !sizeof(dest_ids) )
197 {
198 raise_error("Transporterfehlfunktion in AddRoute(): Identifikations"
199 "matrix unzureichend definiert. Transporter unbenutzbar fuer "
200 "Spieler. Bitte mindestens eine Ziel-ID via P_HARBOUR oder als "
201 "Argument to AddRoute().");
202 }
203 route += ({ ({ HP_ROOM, room, stay, next, harbour_desc, dest_ids,
204 deststr }) });
205}
206
207varargs void AddMsg(string msg, int next)
208{
209 route += ({ ({ HP_MSG, msg, next }) });
210}
211
212void AddFun(string fun, int next) { route += ({ ({ HP_FUN, fun, next }) }); }
213
214string QueryArrived() { return roomCode; }
215
216mixed* QueryPosition()
217{
218 return ({ route[rpos][1],route[(rpos+1)<sizeof(route)?(rpos+1):0][1] });
219}
220
221object* QueryPassengers()
222{
223 return filter(all_inventory(),#'query_once_interactive);
224}
225
226varargs string *QueryHarbours(int textflag)
227{
228 string *ret = ({});
229
230 foreach( mixed* entry : route )
231 {
232 if ( entry[0] == HP_ROOM )
233 {
234 if ( textflag )
235 {
236 string *hp_ids = entry[1]->QueryProp(P_HARBOUR)[1];
237 if (pointerp(hp_ids) && sizeof(hp_ids))
238 {
239 string *h = map( explode(hp_ids[0]," "), #'capitalize);
240 ret += ({ implode(h, " ") });
241 }
242 }
243 else
244 {
245 ret += ({ entry[1] });
246 }
247 }
248 }
249 return ret;
250}
251
252// beim zerstoeren sollte auch die route und der Transporter aus dem traveld
253// abgemeldet werden.
254public varargs int remove(int silent)
255{
256 TRAVELD->RemoveTransporter(this_object());
257 return ::remove(silent);
258}
259
260void RemoveRoute()
261{
262 Halt();
263 route = ({ });
264 rpos = 0;
265 TRAVELD->RemoveTransporter(this_object());
266}
267
268varargs int Enter(object who)
269{
270 string *emsg;
271 mixed efail;
272
273 if (!objectp(who)) who = this_player();
274 if (environment(who) == this_object())
275 {
276 tell_object(who,"Da bist Du doch bereits, schon vergessen?\n");
277 return 1;
278 }
279 if (!QueryArrived()) return 0;
280 if (QueryProp(P_MAX_PASSENGERS) &&
281 (sizeof(QueryPassengers()) >= QueryProp(P_MAX_PASSENGERS)))
282 {
283 if (pointerp(efail=QueryProp(P_ENTERFAIL)))
284 {
285 if (sizeof(efail) == 2)
286 tell_room(this_object(),who->Name(WER,2)+" "+process_string(efail[1])+
287 ".\n",({who}));
288 tell_object(who,process_string(efail[0])+".\n");
289 }
290 else if (stringp(efail))
291 tell_object(who,process_string(efail)+".\n");
292 else if (closurep(efail)) funcall(efail);
293 return 1;
294 }
295
296 tell_object(who,"Du betrittst "+name(WEN,1)+".\n");
297 if (pointerp(emsg=QueryProp(P_ENTERMSG)) && sizeof(emsg) == 2)
298 return who->move(this_object(),M_GO,"",process_string(emsg[0]),
299 process_string(emsg[1]));
300 return who->move(this_object(),M_GO,
301 name(WEN,1),"betritt","kommt herein");
302}
303
304varargs int Leave(object who)
305{
306 string *lmsg;
307 mixed lfail;
308
309 if (!objectp(who)) who = this_player();
310 if (environment(who) != this_object())
311 {
312 if (QueryArrived())
313 {
314 tell_object(who,"Dafuer muesstest Du erstmal dort sein.\n");
315 return 1;
316 }
317 return 0;
318 }
319 if (!QueryArrived())
320 {
321 if (lfail=QueryProp(P_LEAVEFAIL))
322 {
323 if (pointerp(lfail) && sizeof(lfail))
324 {
325 if (sizeof(lfail) == 2)
326 tell_room(this_object(),who->Name(WER,2)+" "+process_string(
327 lfail[1])+".\n",({who}));
328 tell_object(who,process_string(lfail[0])+".\n");
329 }
330 else if (stringp(lfail))
331 tell_object(who,process_string(lfail)+".\n");
332 else if (closurep(lfail)) funcall(lfail);
333 return 1;
334 }
335 tell_object(who,"Fehler beim Verlassen des Transporters.\n"
336 "Bitte zustaendigen Magier verstaendigen.\n");
337 return 1;
338 }
339
340 if (who->QueryProp(P_TRAVEL_INFO)) who->SetProp(P_TRAVEL_INFO,0);
341 tell_object(who,"Du verlaesst "+name(WEN,1)+".\n");
342 if (pointerp(lmsg=QueryProp(P_LEAVEMSG)) && sizeof(lmsg) == 2)
343 return who->move(environment(),M_GO,"",process_string(lmsg[0]),
344 process_string(lmsg[1]));
345 return who->move(environment(),M_GO,
346 name(WEN,1),"verlaesst","kommt herein");
347}
348
349/*
350 ****************** Internal Functions ******************
351 */
352
353static int GoInside(string str)
354{
355 _notify_fail("Was moechtest Du denn genau?\n");
356 if (stringp(str) && id(str)) {
357 Enter();
358 return 1;
359 }
360 return 0;
361}
362
363static int GoOutside(string str)
364{
365 _notify_fail("Was moechtest Du denn genau?\n");
366 if (stringp(str) && id(str)) {
367 Leave();
368 return 1;
369 }
370 return 0;
371}
372
373static int GoInAndOutside(string str)
374{
375 string to;
376
377 _notify_fail("Was moechtest Du denn genau?\n");
378 if (!sizeof(str)) return 0;
379 if ((sscanf(str,"auf %s",to) == 1 || sscanf(str,"in %s",to) == 1) && id(to))
380 return Enter(),1;
381 if ((sscanf(str,"von %s",to) == 1 || sscanf(str,"aus %s",to) == 1) && id(to))
382 return Leave(),1;
383 return 0;
384}
385
386protected void create()
387{
388 ::create();
389
390 route = ({});
391
392 SetProp(P_LEAVEFAIL,"Das ist momentan viel zu gefaehrlich");
393 SetProp(P_ENTERFAIL,"Dort ist kein Platz mehr fuer Dich");
394 SetProp(P_TRANSPARENT,1);
395
396 AddId("Transporter");
397
398 call_out("SetTravelCmds",1);
399}
400
401static varargs void disconnect(int change, int change_time)
402{
403 object room;
404 mixed *departmsg;
405
406 departmsg = QueryProp(P_DEPARTMSG);
407
408 if ((room = environment()) && pointerp(departmsg))
409 {
410 tell_room(this_object(),process_string(departmsg[0]));
411 tell_room(room,process_string(departmsg[1]));
412 }
413
414 roomCode = 0;
415
416 if (change) call_out("changeHp",change_time);
417}
418
419static varargs void connect(string room, string code)
420{
421 mixed *arrivemsg, *t;
422 object *trav, ob;
423 string *trs, *msgs;
424 int i;
425
426 if (roomCode) disconnect();
427
428 roomCode = code?code:"";
429
430 if (catch(move(room,M_SILENT|M_NOCHECK);publish))
431 {
432 roomCode = 0;
433 return;
434 }
435
436 arrivemsg = QueryProp(P_ARRIVEMSG);
437
438 if (pointerp(arrivemsg))
439 {
440 tell_room(this_object(),process_string(arrivemsg[0]));
441 tell_room(room,process_string(arrivemsg[1]));
442 }
443
444 trav = filter(all_inventory(this_object()),#'living);
445
446 i = sizeof(trav);
447 while(i--)
448 {
449 if (pointerp(t = trav[i]->QueryProp(P_TRAVEL_INFO))&&
450 t[0]==this_object()&&t[2]==room)
451 {
452 if (trav[i]->InFight())
453 tell_object(trav[i],break_string("Du solltest Deinen Kampf "
454 "schnell beenden,denn eigentlich wolltest Du hier "
455 "aussteigen.",78));
456 else
457 Leave(trav[i]);
458 if (environment(trav[i])!=this_object())
459 trav[i]->SetProp(P_TRAVEL_INFO,0);
460 }
461 }
462 trav = filter(all_inventory(find_object(room))-trav,#'living);
463 i=sizeof(trav);
464 while(i--)
465 {
466 if (objectp(trav[i]) && pointerp(t = trav[i]->QueryProp(P_TRAVEL_INFO))&&
467 t[0] == environment(trav[i]) && t[1] == this_object())
468 {
469 if ( trav[i]->InFight() )
470 tell_object(trav[i],
471 break_string("Du solltest Deinen Kampf schnell beenden, denn "
472 "eigentlich wolltest Du mit "+name(WEM,1)+
473 " reisen.",78));
474 else
475 Enter(trav[i]);
476 if (environment(trav[i]) == this_object())
477 {
478 t[0] = this_object();
479 trav[i]->SetProp(P_TRAVEL_INFO,t);
480 }
481 }
482 }
483}
484
485// this object never performs any clean-up, the driver should not call it
486// again.
487int clean_up(int arg) { return 0; }
488
489void changeHp()
490{
491 if (++rpos == sizeof(route))
492 {
493 rpos = 0;
494 //TRAVELD die aktuelle Route uebermitteln
495 ReportRoute();
496 }
497 if (route[rpos][0] == HP_MSG)
498 {
499 call_out("changeHp",route[rpos][2]);
500 tell_room(this_object(),route[rpos][1]);
501 }
502 else if (route[rpos][0] == HP_FUN)
503 {
504 call_out("changeHp",route[rpos][2]);
505 call_other(this_object(),route[rpos][1]);
506 }
507 else
508 {
509 call_out("disconnect",route[rpos][2],1,route[rpos][3]);
510 connect(route[rpos][1],route[rpos][4]);
511 }
512}
513
514void __restart(string funname)
515{
516 if (!funname || funname == "" || (funname != "changeHp" &&
517 funname != "disconnect"))
518 return;
519 while(remove_call_out(funname) != -1);
520 call_out(funname,funname == "changeHp"?15:5);
521}