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