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