/* run.c - Created by Giampiero Caprino This file is part of Train Director Train Director is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2, or (at your option) any later version. Train Director is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Train Director; see the file COPYING. If not, write to the Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include #include #if !defined(__unix__) #include #else #include #endif #include #include "ask.h" #include "trsim.h" void open_all_fleeted(void); #define HOMAN_030330 #define MERGE_TRAINS 1 int assign_ok = 1; int changed; /* Vector handling routines. * * Vectors are used to store path information during the simulation. * Note that these are not the paths that automatically compute * the entry/exit times from a .pth file. * The simulation paths are the list of track elements that the * train will have to travel trough. * A path always ends at the next signal or at the end of the track. */ Vector *new_Vector(void) { Vector *v; v = (Vector *)calloc(1, sizeof(Vector)); return v; } void Vector_addElement(Vector *v, void *e, int flag) { if(v->size >= v->maxelem) { v->maxelem += 10; if(v->ptr) { v->ptr = (struct _track **)realloc(v->ptr, v->maxelem * sizeof(struct _track *)); v->flags = (char *)realloc(v->flags, v->maxelem * sizeof(char)); } else { v->ptr = (struct _track **)malloc(v->maxelem * sizeof(struct _track *)); v->flags = (char *)malloc(v->maxelem * sizeof(char)); } } v->flags[v->size] = flag; v->ptr[v->size++] = e; } void *Vector_elementAt(Vector *v, int i) { if(i >= v->size) { printf("Bad index %d: only %d elements in vector!\n", i, v->size); #if defined(__unix__) abort(); #else abort(0); #endif } return v->ptr[i]; } int Vector_flagAt(Vector *v, int i) { if(i >= v->size) { printf("Bad index %d: only %d elements in vector!\n", i, v->size); #if defined(__unix__) abort(); #else abort(0); #endif } return v->flags[i]; } void Vector_empty(Vector *v) { v->size = 0; } void Vector_delete(Vector *v) { if(v->ptr) free(v->ptr); if(v->flags) free(v->flags); free(v); } void Vector_deleteElement(Vector *v, int del) { while(del + 1 < v->size) { v->flags[del] = v->flags[del + 1]; v->ptr[del] = v->ptr[del + 1]; ++del; } --v->size; } int Vector_find(Vector *v, Track *trk) { int i; for(i = 0; i < v->size; ++i) if(v->ptr[i] == trk) return i; return -1; } void Vector_merge(Vector *dest, Vector *src) { int i; for(i = 0; i < dest->size; ++i) { if(Vector_find(dest, Vector_elementAt(src, i)) < 0) Vector_addElement(dest, Vector_elementAt(src, i), Vector_flagAt(src, i)); } } void Vector_Print(Vector *v, char * str, short pos) { Track *t; int i; /* For debugging purposes */ printf("%s = [", str); for(i = 0; i < v->size; i++) { t = (Track *)(v->ptr[i]); if(t) { if(pos == i) printf("[*%hd,%hd*]", t->x, t->y); else printf("[%hd,%hd]", t->x, t->y); } } printf("]\n"); } void Vector_dump(Train *tr, char *suff) { Track *t; int i; FILE *fp; Vector *v; char buff[256]; /* For debugging purposes */ sprintf(buff, "\n%s - %s\n", tr->name, suff); layout_error(buff); v = tr->path; for(i = 0; i < v->size; i++) { t = v->ptr[i]; if(!t) { sprintf(buff, "%3d - NULL\n", i); continue; } sprintf(buff, "%3d - %3d, %3d %s\n", i, t->x, t->y, tr->pathpos == i ? " <-" : ""); layout_error(buff); } if(!tr->length || !tr->tail || !tr->tail->path) { end_layout_error(); return; } sprintf(buff, "Tail:\n", tr->name); layout_error(buff); v = tr->tail->path; for(i = 0; i < v->size; i++) { t = v->ptr[i]; if(!t) sprintf(buff, "%3d - NULL\n", i); else sprintf(buff, "%3d - %3d, %3d\n", i, t->x, t->y); layout_error(buff); } sprintf(buff, "\n"); layout_error(buff); end_layout_error(); } int sameSubPath(Vector *v1, Vector *v2) { int i, e; Track *t1, *t2; e = v2->size - 1; for(i = v1->size - 1; i >= 0; --i) { t1 = Vector_elementAt(v1, i); t2 = Vector_elementAt(v2, e); if(t1->x != t2->x || t1->y != t2->y) return 0; if(!e--) return 1; } return 1; } Vector *appendPath(Vector *oldpath, Vector *newelems) { int i, j; if(!oldpath) oldpath = new_Vector(); for(i = 0; i < newelems->size; ++i) { for(j = 0; j < oldpath->size; ++j) if(Vector_elementAt(oldpath, j) == Vector_elementAt(newelems, i)) break; if(j < oldpath->size) continue; Vector_addElement(oldpath, Vector_elementAt(newelems, i), Vector_flagAt(newelems, i)); } return oldpath; } /* findPath0 * * This is one of the main functions of the program. * It is responsible for computing and validating the * next path that the train will have to travel, * based on the position of the switches and the * type of the tracks. * * The path is first computed by "walking" each track. * The track routines return the next track based on * the current track and the track or switch orientation. * * When we reach the next signal, or the end of the line * we validate the path by walking back. This is necessary * because switches might point us to the wrong direction * when travelled backwards. * * Also note that we don't care about any train being * on the path. pathIsBusy() below takes care of that. */ Vector *findPath0(Vector *path, Track *t, int dir) { Track *t1, *tbck; Track *to; int cx, cy; int i; Track *sw; trkdir ndir; if(!path) path = new_Vector(); else Vector_empty(path); agn: Vector_addElement(path, t, dir); cx = t->x; cy = t->y; to = t; if(dir == E_W || dir == N_S) { /* westbound */ ndir = dir; while(1) { if(to->type == TRACK) t1 = track_walkwest(to, &ndir); else t1 = swtch_walkwest(to, &ndir); if(t1 == 0) break; nxtw: if(path->size >= total_track_number) goto err; t = findTrack(t1->x, t1->y); if(t != 0) { if(t->wsignal != 0 && ndir != S_N) break; if(t->x == to->wlinkx && t->y == to->wlinky && t->wlinkx == to->x && t->wlinky == to->y) { dir = W_E; if(t->wsignal != 0) break; goto agn; } if(t->direction == XH_SW_NE) { Vector_addElement(path, t, ndir); if(to->y != t1->y) ++t1->y; --t1->x; to = t; goto nxtw; } if(t->direction == XH_NW_SE) { Vector_addElement(path, t, ndir); if(to->y != t1->y) --t1->y; --t1->x; to = t; goto nxtw; } if(t->direction == X_X) { Vector_addElement(path, t, ndir); if(to->y < t->y) ++t1->y; else --t1->y; if(to->x < t->x) ++t1->x; else --t1->x; goto nxtw; } if(t->direction == X_PLUS) { Vector_addElement(path, t, ndir); if(to->y < t->y) ++t1->y; else if(to->y > t->y) --t1->y; else --t1->x; goto nxtw; } if(ndir == W_E || ndir == S_N) { dir = ndir; goto agn; } Vector_addElement(path, t, ndir); to = t; cx = t->x; cy = t->y; continue; } sw = findSwitch(t1->x, t1->y); if(sw != 0) { if(ndir == W_E) { if(sw->direction == 8 || sw->direction == 9 || sw->direction == 16 || sw->direction == 17) goto we89; t = sw; dir = W_E; goto agn; } ew89: Vector_addElement(path, sw, ndir); cx = sw->x; cy = sw->y; if(sw->direction == 8) {/* special case: english switch */ --t1->x; if(to->y == cy) {/* we come from a horiz track */ if(sw->switched) ++t1->y; } else if(!sw->switched) ++t1->y; to = sw; goto nxtw; } if(sw->direction == 9) {/* special case: english switch */ --t1->x; if(to->y == cy) {/* we come from a horiz track */ if(sw->switched) --t1->y; } else if(!sw->switched) --t1->y; to = sw; goto nxtw; } if(sw->direction == 16) {/* special case: english switch sw-ne */ if(sw->switched) { if(ndir == N_S) { if(to->x == cx) { --t1->x; ++t1->y; ndir = E_W; } else { ++t1->y; } } else if(ndir == S_N) { if(to->x == cx) { --t1->y; ++t1->x; ndir = W_E; } else --t1->y; } else { /* E_W */ if(to->x == cx) { ++t1->y; --t1->x; } else { ++t1->y; ndir = N_S; } } } else { if(ndir == N_S) { if(to->x == cx) { ++t1->y; } else { ++t1->y; --t1->x; } } else if(ndir == S_N) { if(to->x == cx) --t1->y; else { --t1->y; --t1->x; } } else { /* E_W */ if(to->x == cx) { ++t1->y; ndir = N_S; } else { ++t1->y; --t1->x; } } } to = sw; goto nxtw; } if(sw->direction == 17) {/* special case: english switch nw-se */ if(sw->switched) { if(ndir == N_S) { if(to->x == cx) { ++t1->x; ++t1->y; ndir = W_E; goto nxte; } else { ++t1->y; } } else { /* E_W */ if(to->x == cx) { --t1->y; --t1->x; } else { --t1->y; ndir = S_N; goto nxte; } } } else { if(ndir == N_S) { if(to->x == cx) { ++t1->y; } else { ++t1->y; ++t1->x; } } else { /* E_W */ if(to->x == cx) { --t1->y; ndir = S_N; goto nxte; } else { --t1->y; --t1->x; } } } to = sw; goto nxtw; } to = sw; continue; } tbck = findLinkTo(to->x, to->y); if(tbck != 0) { Vector_addElement(path, tbck, ndir); break; } printf("No trk west of %d,%d", cx, cy); break; } if(ndir == N_S) ndir = S_N; else if(ndir == S_N) ndir = N_S; else ndir = W_E; for(i = path->size - 1; i > 0; --i) { chkw: to = Vector_elementAt(path, i); t1 = Vector_elementAt(path, i - 1); if(to->type == TEXT && to->elinkx && to->elinky) { tbck = findTrack(to->elinkx, to->elinky); if(!tbck || tbck->x != t1->x || tbck->y != t1->y) goto err; continue; } if(to->type == TRACK) { if(to->direction == XH_NW_SE) continue; if(to->direction == XH_SW_NE) continue; if(to->direction == X_X || to->direction == X_PLUS) continue; tbck = track_walkeast(to, &ndir); if(!tbck || tbck->x != t1->x || tbck->y != t1->y) goto err; if(Vector_flagAt(path, i - 1) == W_E && i > 1) { --i; goto chke; } continue; } if(to->direction == 8) { if(t1->x == to->x + 1 && t1->y != to->y + 1) continue; goto err; } if(to->direction == 9) { if(t1->x == to->x + 1 && t1->y != to->y - 1) continue; goto err; } if(to->direction == 16) { // if(t1->y == to->y - 1 && t1->x != to->x + 1) // continue; if(to->switched && ndir == E_W) ndir = N_S; if(to->switched && ndir == W_E) ndir = S_N; continue; } if(to->direction == 17) { // if(t1->y == to->y - 1 && t1->x != to->x - 1) if(to->switched && ndir == W_E) ndir = N_S; if(to->switched && ndir == E_W) ndir = S_N; continue; } tbck = swtch_walkeast(to, &ndir); if(!tbck || tbck->x != t1->x || tbck->y != t1->y) goto err; if(Vector_flagAt(path, i - 1) == W_E && i > 1) { --i; goto chke; } } } else { /* eastbound */ ndir = dir; while(1) { if(to->type == TRACK) t1 = track_walkeast(to, &ndir); else t1 = swtch_walkeast(to, &ndir); if(t1 == 0) break; nxte: if(path->size >= total_track_number) goto err; t = findTrack(t1->x, t1->y); if(t != 0) { if(t->esignal != 0 && ndir != N_S) break; if(t->x == to->elinkx && t->y == to->elinky && t->elinkx == to->x && t->elinky == to->y) { dir = E_W; if(t->esignal != 0) break; goto agn; } if(t->direction == XH_SW_NE) { Vector_addElement(path, t, ndir); if(to->y != t1->y) --t1->y; ++t1->x; to = t; goto nxte; } if(t->direction == XH_NW_SE) { Vector_addElement(path, t, ndir); if(to->y != t1->y) ++t1->y; ++t1->x; to = t; goto nxte; } if(t->direction == X_X) { Vector_addElement(path, t, ndir); if(to->y < t->y) ++t1->y; else --t1->y; if(to->x < t->x) ++t1->x; else --t1->x; goto nxte; } if(t->direction == X_PLUS) { Vector_addElement(path, t, ndir); if(to->y < t->y) ++t1->y; else if(to->y > t->y) --t1->y; else ++t1->x; goto nxte; } if(ndir == E_W || ndir == N_S) { dir = ndir; goto agn; } Vector_addElement(path, t, ndir); to = t; cx = t->x; cy = t->y; continue; } sw = findSwitch(t1->x, t1->y); if(sw != 0) { if(ndir == E_W) { if(sw->direction == 8 || sw->direction == 9 || sw->direction == 16 || sw->direction == 17) goto ew89; t = sw; dir = E_W; goto agn; } we89: Vector_addElement(path, sw, ndir); cx = sw->x; cy = sw->y; if(sw->direction == 8) {/* special case: english switch */ ++t1->x; if(to->y == cy) {/* we come from a horiz track */ if(sw->switched) --t1->y; } else if(!sw->switched) --t1->y; to = sw; goto nxte; } if(sw->direction == 9) {/* special case: english switch */ ++t1->x; if(to->y == cy) {/* we come from a horiz track */ if(sw->switched) ++t1->y; } else if(!sw->switched) ++t1->y; to = sw; goto nxte; } if(sw->direction == 16) {/* special case: english switch sw-ne */ if(sw->switched) { if(ndir == S_N) { if(to->x == cx) { --t1->y; ++t1->x; ndir = W_E; goto nxte; } else { --t1->y; } } else if(ndir == N_S) { if(to->x == cx) { ++t1->y; --t1->x; ndir = E_W; goto nxtw; } else { ++t1->y; } } else { /* W_E */ if(to->x == cx) { --t1->y; ++t1->x; } else { --t1->y; // --t1->x; ndir = S_N; } goto nxte; } } else { if(ndir == S_N) { if(to->x == cx) { --t1->y; } else { --t1->y; ++t1->x; ndir = W_E; } } else if(ndir == N_S) { if(to->x == cx) { ++t1->y; } else { ++t1->y; --t1->x; ndir = E_W; } } else { /* W_E */ if(to->x == cx) { --t1->y; --t1->x; ndir = S_N; } else { --t1->y; ++t1->x; } } } to = sw; goto nxte; } if(sw->direction == 17) {/* special case: english switch */ if(sw->switched) { if(ndir == S_N) { if(to->x == cx) { --t1->x; --t1->y; ndir = E_W; goto nxtw; } else { --t1->y; } } else if(ndir == N_S) { if(to->x == cx) { ++t1->x; ++t1->y; ndir = W_E; goto nxte; } else { ++t1->y; } } else { /* W_E */ if(to->x == cx) { ++t1->y; ++t1->x; } else { ++t1->y; ndir = N_S; } goto nxtw; } } else { /* switch is not thrown */ if(ndir == S_N) { if(to->x == cx) { --t1->y; } else { --t1->y; --t1->x; ndir = W_E; } } else if(ndir == N_S) { if(to->x == cx) ++t1->y; else { ++t1->y; ++t1->x; } } else { /* W_E */ if(to->x == cx) { --t1->y; ndir = S_N; } else { ++t1->y; ++t1->x; } } } to = sw; goto nxte; } to = sw; continue; } tbck = findLinkTo(to->x, to->y); if(tbck != 0) { Vector_addElement(path, tbck, ndir); break; } printf("No trk east of %d,%d\n", cx, cy); break; } if(ndir == N_S) ndir = S_N; else if(ndir == S_N) ndir = N_S; else ndir = E_W; for(i = path->size - 1; i > 0; --i) { chke: to = Vector_elementAt(path, i); t1 = Vector_elementAt(path, i - 1); if(to->type == TEXT && to->wlinkx && to->wlinky) { tbck = findTrack(to->wlinkx, to->wlinky); if(!tbck || tbck->x != t1->x || tbck->y != t1->y) goto err; continue; } if(to->type == TRACK) { if(to->direction == XH_NW_SE) continue; if(to->direction == XH_SW_NE) continue; if(to->direction == X_X || to->direction == X_PLUS) continue; tbck = track_walkwest(to, &ndir); if(!tbck || tbck->x != t1->x || tbck->y != t1->y) goto err; if(Vector_flagAt(path, i - 1) == E_W && i > 1) { --i; goto chkw; } continue; } if(to->direction == 8) { if(t1->x == to->x - 1 && t1->y != to->y - 1) continue; goto err; } if(to->direction == 9) { if(t1->x == to->x - 1 && t1->y != to->y + 1) continue; goto err; } if(to->direction == 16) { // if(t1->y == to->y - 1 && t1->x != to->x - 1) if(to->switched && ndir == E_W) ndir = N_S; if(to->switched && ndir == W_E) ndir = S_N; continue; } if(to->direction == 17) { if(to->switched && ndir == W_E) ndir = N_S; if(to->switched && ndir == E_W) ndir = S_N; continue; } tbck = swtch_walkwest(to, &ndir); if(!tbck || tbck->x != t1->x || tbck->y != t1->y) goto err; if(Vector_flagAt(path, i - 1) == E_W && i > 1) { --i; goto chkw; } continue; } } path->pathlen = 0; for(cx = 0; cx < path->size; ++cx) { t1 = Vector_elementAt(path, cx); path->pathlen += t1->length; } return path; err: Vector_delete(path); return 0; } Vector *findPath(Track *t, int dir) { return findPath0(NULL, t, dir); } /* pathIsBusy * * Return 0 if path is clear from start to end. * Return number of busy path element + 1 otherwise. */ int pathIsBusy(Train *tr, Vector *path, int dir) { int el, nel; Track *t; Track *trk; Train *trn; if(!path) return 0; nel = path->size; for(el = 0; el < nel; ++el) { t = (Track *)Vector_elementAt(path, el); if(t->busy) { trk = findTrack(t->x, t->y); if(trk == 0) { if(findText(t->x, t->y) != 0) return 0; return el + 1; } switch(dir) { case W_E: case signal_EAST_FLEETED: case S_N: case signal_NORTH_FLEETED: if(trk->esignal != 0) return 0; break; case E_W: case signal_WEST_FLEETED: case N_S: case signal_SOUTH_FLEETED: if(trk->wsignal != 0) return 0; break; } /*printf("busy at %d,%d\n", trk->x, trk->y);*/ return el + 1; } if((trn = findTrain(t->x, t->y)) != 0 && trn != tr) { /*printf("busy for train at %d,%d\n", t->x, t->y);*/ return el + 1; } if((trn = findStranded(t->x, t->y)) != 0 && trn != tr) { /*printf("busy for train at %d,%d\n", t->x, t->y);*/ return el + 1; } if(t->fgcolor == color_green || t->fgcolor == color_orange || t->fgcolor == color_red) return el + 1; } return 0; } void colorPartialPath0(Vector *path, int state, int start, int end) { grcolor c; int el, nel; int busy; Track *t; if(path == 0) return; busy = 0; c = conf.fgcolor; /* ST_FREE is the default */ if(state == ST_GREEN) { c = color_green; busy = 1; } else if(state == ST_RED) { c = color_orange; busy = 1; } else if(state == ST_WHITE) { c = color_white; busy = 1; } nel = end; for(el = start; el < nel; ++el) { t = (Track *)Vector_elementAt(path, el); if(t == 0) continue; t->fgcolor = c; change_coord(t->x, t->y); /* t->busy = busy; */ } } void colorPartialPath(Vector *path, int state, int start) { colorPartialPath0(path, state, start, path->size); } void colorPathStart(Vector *path, int state, int end) { colorPartialPath0(path, state, 0, end); } void colorPath(Vector *path, int state) { colorPartialPath0(path, state, 0, path->size); } /* new_train_status * * This is a utility function to keep track of * the number of trains in each different state. * The data is just displayed to the user. */ void new_train_status(Train *t, int status) { if(t->status == status) return; switch(t->status) { case train_WAITING: --ntrains_waiting; break; case train_STOPPED: --ntrains_stopped; break; case train_READY: --ntrains_ready; break; case train_ARRIVED: --ntrains_arrived; break; case train_RUNNING: --ntrains_running; } t->status = status; switch(t->status) { case train_WAITING: ++ntrains_waiting; break; case train_STOPPED: ++ntrains_stopped; break; case train_READY: ++ntrains_ready; break; case train_ARRIVED: ++ntrains_arrived; break; case train_RUNNING: ++ntrains_running; } } void reset_schedule(void) { Train *t; TrainStop *ts; for(t = schedule; t; t = t->next) { if(t->tail) { if(t->tail->path) { colorPartialPath(t->tail->path, ST_FREE, t->tail->pathpos); Vector_delete(t->tail->path); t->tail->path = 0; } t->tail->position = 0; } if(t->path) { colorPartialPath(t->path, ST_FREE, t->pathpos); Vector_delete(t->path); } t->path = 0; } ntrains_arrived = 0; ntrains_stopped = 0; ntrains_waiting = 0; ntrains_ready = 0; for(t = schedule; t; t = t->next) { if(t->fleet) Vector_delete(t->fleet); t->fleet = 0; t->status = train_READY; ++ntrains_ready; t->direction = t->sdirection; t->exited = 0; t->timeexited = 0; t->wrongdest = 0; t->curspeed = 0; t->curmaxspeed = 0; t->trackpos = 0; t->timelate = 0; t->timedelay = 0; t->timered = 0; t->pathpos = 0; t->position = 0; t->timedep = 0; t->arrived = 0; t->timeexited = 0; t->shunting = 0; t->stopping = 0; for(ts = t->stops; ts; ts = ts->next) { ts->late = 0; ts->delay = 0; ts->stopped = 0; } } } int sameStation(char *s1, char *s2) { while(*s1 && *s1 != '@' && *s1 == *s2) ++s1, ++s2; if(!*s1 || *s1 == '@') if(!*s2 || *s2 == '@') return 1; return 0; } /* FindNextTrack * * Find the next track in the specified direction. * This is used when a train enters the layout * or when we are crossing a signal, thus starting * to create a new path. * This is necessary because signals can be attached * to diagonal or vertical tracks. */ Track *findNextTrack1(trkdir direction, int x, int y, trkdir *ndir) { Track *t; Track *t1; *ndir = direction; if(!(t = findTrack(x, y)) && !(t = findSwitch(x, y))) return 0; /* should be impossible */ if(direction == E_W || direction == N_S) {/* westbound */ if(t->type == TRACK) t1 = track_walkwest(t, ndir); else t1 = swtch_walkwest(t, ndir); } else { if(t->type == TRACK) t1 = track_walkeast(t, ndir); else t1 = swtch_walkeast(t, ndir); } if((t = findTrack(t1->x, t1->y))) return t; return findSwitch(t1->x, t1->y); } Track *findNextTrack(trkdir direction, int x, int y) { trkdir ndir; return findNextTrack1(direction, x, y, &ndir); } /* FindEntryTrack * * Find the entry point of a train and * the train's initial direction. * By convention, if the entry point string * is to the left of the track, the direction * will be eastbound. Similarly if the string * is above a vertical track, the direction will * be southbound. */ Track *findEntryTrack(Train *tr, char *entrance) { Track *t, *st; int x, y; st = findStation(entrance); if(!st) return 0; if(st->elinkx && st->elinky) { tr->direction = W_E; x = st->elinkx; y = st->elinky; } else if(st->wlinkx && st->wlinky) { tr->direction = E_W; x = st->wlinkx; y = st->wlinky; } else return 0; if((t = findTrack(x, y))) { if(t->direction == TRK_N_S) tr->direction = st->y < t->y ? N_S : S_N; return t; } t = findSwitch(x, y); if(t->direction >= 12 && t->direction <= 15) tr->direction = st->y < t->y ? N_S : S_N; return t; } /* FindStopPoint * * Look ahead in the path to see if we have to stop * at any station. The distance from the station (or * from the end of the path) is recorded so that we * can start decellerating in time. */ void findStopPoint(Train *t) { Track *trk; Track *sig; TrainStop *stp; int i; int dir; long l1; int nspeed; int overlength; t->stoppoint = 0; trk = Vector_elementAt(t->path, t->path->size - 1); dir = Vector_flagAt(t->path, t->path->size - 1); if((trk = findNextTrack(dir, trk->x, trk->y))) { sig = (dir == W_E || dir == S_N) ? trk->esignal : trk->wsignal; if(sig) { #ifdef HOMAN_030330 t->stoppoint = sig; #else t->stoppoint = trk; #endif t->disttostop = t->path->pathlen; } } nspeed = t->curmaxspeed; l1 = t->pathtravelled; for(i = t->pathpos; i < t->path->size; ++i) { if(!(trk = Vector_elementAt(t->path, i))) continue; if(!trk->isstation) { l1 += trk->length; continue; } if(i == t->pathpos) /* if we're already at station */ continue; /* ignore it */ if(!sameStation(t->exit, trk->station)) { for(stp = t->stops; stp; stp = stp->next) if(sameStation(stp->station, trk->station)) break; if(!stp || !stp->minstop) { l1 += trk->length; /* ignore this station */ continue; } } /* Below is only when stoppoint is at a station */ t->stoppoint = trk; t->disttostop = l1; if(!t->length) { /* pre 1.17 code */ t->disttostop += trk->length / 2; break; } /* 1.18 code */ if(t->length < trk->length) { t->disttostop += (trk->length / 2) + (t->length / 2); return; } /* proceed until end of path, or until half of the * train's length has travelled past the station. */ overlength = t->length / 2 - trk->length / 2; t->disttostop += trk->length; while(++i < t->path->size) { if(!(trk = Vector_elementAt(t->path, i))) break; if(trk->station) break; if(overlength - trk->length > 0) { t->stoppoint = trk; break; } overlength -= trk->length; t->disttostop += trk->length; } break; } } /* FindSlowPoint * * Look ahead in the path to see if we have to slow down * because of a speed limit. The distance from the limit * is recorded so that we can start decellerating in time. */ void findSlowPoint(Train *t) { Track *trk; int i; #ifndef HOMAN_030330 long l2; int speed, nspeed; t->slowpoint = 0; nspeed = t->curmaxspeed; l2 = t->pathtravelled; for(i = t->pathpos; i < t->path->size; ++i) { if(!(trk = Vector_elementAt(t->path, i))) continue; if(!t->slowpoint) { speed = trk->speed[t->type]; if(!speed) speed = trk->speed[0]; if(speed && speed < nspeed) { t->slowpoint = trk; t->disttoslow = l2; } else { if(speed && speed > nspeed) nspeed = speed; /* in case 60 -> 120 -> 90 */ l2 += trk->length; } } } #else long l2, min_dist; int speed, prev_speed, min_speed; t->slowpoint = 0; t->disttoslow = 0; prev_speed = t->curmaxspeed; l2 = t->pathtravelled; for(i = t->pathpos; i < t->path->size; ++i) { if(!(trk = Vector_elementAt(t->path, i))) continue; speed = trk->speed[t->type]; if(!speed) speed = trk->speed[0]; if (speed && speed < prev_speed) { /* This is a track that is slower than previous track, i.e. a slowpoint. */ /* However, if this track is short and next track is slower, maybe one of the next tracks is the true slowpoint.*/ /* For the first found slowpoint set tentatively. For next found slow point, set only if it requires a slower speed */ if(!t->slowpoint) { t->slowpoint = trk; t->disttoslow = l2; min_speed = speed; } else if (max_approach_speed(t, l2 - t->disttoslow, speed) < min_speed) { /* Found a new candidate */ t->slowpoint = trk; t->disttoslow = l2; min_speed = speed; } } l2 += trk->length; if (speed) prev_speed = speed; } #endif } /* Signal_unlock * * Put a signal to green if the following * path is clear. */ void signal_unlock(Track *sig) { Vector *path; if(!sig->controls) return; path = findPath(sig->controls, sig->direction); if(!path) return; if(!pathIsBusy(NULL, path, sig->direction)) { sig->status = ST_GREEN; change_coord(sig->x, sig->y); colorPath(path, ST_GREEN); } Vector_delete(path); } void train_arrived(Train *trn) { int minlate; TrainStop *ts; char buff[256]; char *p; long arrtime; new_train_status(trn, train_ARRIVED);/* becomes available for assign cmd */ arrtime = trn->timeout; if(arrtime < trn->timein) arrtime += 24 * 60 * 60; minlate = (current_time - arrtime) / 60; if(trn->arrived) /* we stopped here before! */ minlate = 0; else trn->timeexited = current_time; trn->arrived = 1; if(minlate > 0) { trn->timelate += minlate; total_late += minlate; ++perf_tot.late_trains; } else for(ts = trn->stops; ts; ts = ts->next) if(ts->late) { ++perf_tot.late_trains; break; } for(ts = trn->stops; ts; ts = ts->next) { strcpy(buff, ts->station); if((p = strchr(buff, '@'))) *p = 0; if(findStation(ts->station) && !ts->stopped) ++perf_tot.nmissed_stops; } #if 0 if(trn->tail) { if(trn->tail->path) { Vector_delete(trn->tail->path); trn->tail->path = 0; } trn->tail->position = 0; } #endif } void check_platform(char *s1, char *s2) { while(*s1 == *s2 && *s1) ++s1, ++s2; if(!*s1) { if(!*s2 || *s2 == '@') return; } else if(!*s2) { if(*s1 == '@') return; } ++perf_tot.wrong_platform; } int stopping_at_this_station(Train *trn, Track *st) { TrainStop *stp; for(stp = trn->stops; stp; stp = stp->next) if(sameStation(stp->station, st->station)) return 1; if(sameStation(st->station, trn->exit)) return 1; if(trn->shunting && trn->outof != st) return 1; return 0; } /* Train_at_station * * A train is at a station. * We have to decide whether we have to stop * at this station (because it's in our schedule * or during shunting), and if so we have to * compute the penalties for late arrivals, * wrong platform and the estimated time of departure. */ int train_at_station(Train *trn, Track *t) { TrainStop *stp, *stp1; int minlate; long arrtime; for(stp = trn->stops; stp; stp = stp->next) if(sameStation(stp->station, t->station)) break; if(trn->shunting) { if(trn->outof == t) /* don't stop */ return 0; new_train_status(trn, trn->oldstatus); trn->stopping = 0; trn->curspeed = 0; trn->shunting = 0; trn->outof = 0; if(stp) trn->timedep = stp->departure; else if(sameStation(t->station, trn->entrance)) trn->timedep = trn->timein; return 1; } trn->stopping = 0; if(!stp) { /* we are not at a stop */ if(!assign_ok || !sameStation(t->station, trn->exit)) return 0; /* but we arrived at our destination! */ check_platform(t->station, trn->exit); train_arrived(trn); } else { check_platform(t->station, stp->station); arrtime = stp->arrival; if(arrtime < trn->timein) arrtime += 24 * 60 * 60; minlate = (current_time - arrtime) / 60; if(!stp->minstop) { /* does not stop */ stp->delay = minlate; return 0; } new_train_status(trn, train_STOPPED); if(stp->stopped) /* we stopped here before! */ minlate = 0; stp->delay = minlate; stp->stopped = 1; for(stp1 = stp->next; stp1; stp1 = stp1->next)/* sometimes we have */ if(sameStation(stp1->station, stp->station))/* multiple entries for the */ stp1->stopped = 1; /* same station. This should be fixed in loadsave! */ if(minlate > 0) { stp->late = 1; trn->timelate += minlate; total_late += minlate; } } trn->curspeed = 0; if(!stp) return 1; trn->timedep = current_time + stp->minstop; if(trn->timedep < stp->departure) trn->timedep = stp->departure; return 1; } void add_update_schedule(Train *t) { t->newsched = 1; } /* Do_triggers * * Handle all triggers associated with the * current train position. */ void do_triggers(Train *t) { Track *trk; int prob; int rnd = rand() % 100; char buff[256]; int found; if(!t || !t->position) return; for(trk = layout; trk; trk = trk->next) { if(trk->type != TRIGGER || !trk->station) continue; if(t->direction != trk->direction) continue; if(trk->wlinkx != t->position->x || trk->wlinky != t->position->y) continue; /* * check to see if this trigger applies to a list of * specific trains. * The list starts with "{" and each train name is * separated from the next by a ',' character. * The list is terminated by "}". */ found = 1; for(rnd = 0; trk->station[rnd]; ++rnd) if(trk->station[rnd] == '{') { found = 0; do { ++rnd; for(prob = 0; trk->station[rnd] && trk->station[rnd] != '}' && trk->station[rnd] != ','; buff[prob++] = trk->station[rnd++]); buff[prob] = 0; if(!strcmp(t->name, buff)) found = 1; } while(trk->station[rnd] && trk->station[rnd] != '}'); if(trk->station[rnd] == '}') ++rnd; } if(!found) /* this train is not in the list */ continue; prob = trk->speed[t->type]; if(!prob) prob = 100; /* * rnd < prob means: * prob = 1, rnd almost never < * prob = 99, rnd almost always < */ if(rnd < prob) { for(prob = rnd = 0; trk->station[prob]; ++prob) { if(trk->station[prob] == '{') { /* skip conditional train sequence */ while(trk->station[prob] && trk->station[prob] != '}') ++prob; if(trk->station[prob]) ++prob; continue; } if(trk->station[prob] == '@') { strcpy(buff + rnd, t->name); rnd += strlen(t->name); } else buff[rnd++] = trk->station[prob]; } buff[rnd] = 0; trainsim_cmd(buff); } } } /* max_approach_speed() * * This computes the maximum speed allowed based on the * distance of the train from the next slow or stop point. * If the current train speed is greater than the computed * speed, the train will be slowed down. * If the current train speed is lower, it will be sped up. */ int max_approach_speed(Train *t, long distance, int targetspeed) { /* It would be neat to have the ability to define the trains retardation, */ /* but then we have to mess with a lot of other stuff, for instance changing the definition of the Schdeule keywords and change to speed as cm/s or something like that. Next project! */ /* v*v - v0*v0 = 2*a*s => v = sqrt(2*a*s + v0*v0) */ /* distance in meters, result in m/s, but we need it in km/h */ /* deceleration is 0.6 m/s2, for now. */ double s, v0; int v; s = (double)distance; v0 = ((double)targetspeed) / 3.6; s = s * 2.0 * 0.6 + v0 * v0; if(s < 0) /* should be impossible, but... */ return t->curspeed; v = (int)(sqrt(s) * 3.6); return v; } /* Speed_limit * * Compute the maximum speed for a train. * The speed is the lowest of the maximum speed * for the train, the last speed limit encountered * by the train, or a new speed limit found on the * current track. */ void speed_limit(Train *t, Track *trk) { int speed; speed = trk->speed[t->type]; if(!speed) speed = trk->speed[0]; if(t->shunting && speed > 30) speed = 30; if(speed > 0) { t->curmaxspeed = speed; if(t->maxspeed && t->curmaxspeed > t->maxspeed) t->curmaxspeed = t->maxspeed; if(t->shunting) t->curmaxspeed = 30; t->speedlimit = t->curmaxspeed; if(t->curmaxspeed && t->curspeed > t->curmaxspeed) t->curspeed = t->curmaxspeed; } } /* Compute_new_speed * * Change the speed of the train based on: * - current speed * - current speed limit * - distance from next stop * - distance from next (lower) speed limit */ void compute_new_speed(Train *t) { #ifndef HOMAN_030330 int maxspeed; /* If we are within 600m from our next stop, * slow down to 30 Km/h. If we are within * 100m from our next stop, slow down to 15 Km/h. * We assume that we can break immediately from * 15 Km/h to 0 Km/h. */ if(t->stoppoint && t->disttostop - t->pathtravelled < 600 && ((t->stoppoint->type == TSIGNAL && t->stoppoint->status != ST_GREEN) || t->stoppoint->isstation)) { maxspeed = 30; if(t->curmaxspeed < maxspeed) maxspeed = t->curmaxspeed; if(t->disttostop - t->pathtravelled < 100) maxspeed = 15; if(t->curspeed > maxspeed) t->curspeed -= 2; else if(t->curspeed < maxspeed) t->curspeed += 1; } else if(t->slowpoint && t->disttoslow - t->pathtravelled < 300) { if(!(maxspeed = t->slowpoint->speed[t->type])) maxspeed = t->slowpoint->speed[0]; if(t->curspeed > maxspeed) t->curspeed -= 2; else if(t->curspeed < maxspeed) t->curspeed += 1; } else if(t->curmaxspeed && t->curspeed < t->curmaxspeed) t->curspeed += 1; #else int maxspeed = -1, maxslowspeed, slowspeed; int speedincr = 1; /* This computes the speed, by using max_approach_speed() to calculate braking curve. Consider first stopping distance and then distance to slowpoint */ if(t->stoppoint && (((t->stoppoint->type == TSIGNAL) && (t->stoppoint->status != ST_GREEN)) || t->stoppoint->isstation)) { /* Check if we need to brake. Set target speed to 5 km/h, so we don't stop too early */ maxspeed = max_approach_speed(t, t->disttostop - t->pathtravelled, 5); if(t->curmaxspeed < maxspeed) maxspeed = t->curmaxspeed; if(t->curspeed > maxspeed) { /* Instead of decelerating, we adjust immediately to target speed. Our train _must not_ speed */ t->curspeed = maxspeed; } else if(t->curspeed < maxspeed) { /* Accelerate, this shouldn't be a fixed number, but OK for now. */ t->curspeed += speedincr; speedincr = 0; } } /* Is slowspeed lower? */ if(t->slowpoint) { if(!(slowspeed = t->slowpoint->speed[t->type])) slowspeed = t->slowpoint->speed[0]; /* Check if we need to brake. */ maxslowspeed = max_approach_speed(t, t->disttoslow - t->pathtravelled, slowspeed); if(maxspeed == -1) maxspeed = maxslowspeed; else { if(maxslowspeed < maxspeed) maxspeed = maxslowspeed; } if(t->curmaxspeed < maxspeed) maxspeed = t->curmaxspeed; if(t->curspeed > maxspeed) { /* Instead of decelerating, we adjust immediately to target speed. Our train _must not_ speed */ t->curspeed = maxspeed; } else if(t->curspeed < maxspeed) { t->curspeed += speedincr; } if (t->disttoslow - t->pathtravelled < 0) { /* This shouldn't really happen... */ t->curspeed = slowspeed; } } else if(t->curmaxspeed && t->curspeed < t->curmaxspeed) { t->curspeed += speedincr; } #endif } /* Signal_unfleet * * Set an automatic block signal to green, if possible. * * When a train exits the section controlled by an * automatic block signal, many things can happen: * the signal is still red and the path is the same as * the one travelled by the train. In this case the * signal should turn back to green. * But it's possible that the user has changed the * path before the train leaves the section. In this case, * we must not turn the signal to green. * Also, when restoring from file, the path of the train * is only a sub-path of the path controlled by the signal. * Thus the check to see if one is a proper sub-path of the other. */ int signal_unfleet(Train *t, Track *sig) { Vector *sigpath; Track *trk1; int ret; if(!sig || !sig->controls) return 0; /* this code is copied from signal_unlock() */ trk1 = t->position; t->position = 0; ret = 0; sigpath = findPath(sig->controls, sig->direction); if(sigpath) { if(!pathIsBusy(NULL, sigpath, sig->direction) /* && sameSubPath(t->path, sigpath) */) { sig->status = ST_GREEN; change_coord(sig->x, sig->y); colorPath(sigpath, ST_GREEN); } Vector_delete(sigpath); ret = 1; } t->position = trk1; return ret; } /* Tail_advance * * Compute the new position of the train's tail. * This is a new algorithm introduced in 1.18k. * It simply locates the head of the train in the * tail's path, and color all the tracks starting from * the head's position until the length of the train * is all covered. Then color in black all the remaining * tracks and remove them from the tail's path. */ void tail_advance(Train *t) { Train *tail; int i, len, dir; Track *s, *s1; Track *sig; if(!(tail = t->tail) || t->tail->tailentry || !tail->path) return; if(t->position) { for(i = 0; i < tail->path->size; ++i) { s = Vector_elementAt(tail->path, i); if(s == t->position) break; } if(i == tail->path->size) return; } if(tail->position) { tail->position->fgcolor = conf.fgcolor; change_coord(tail->position->x, tail->position->y); tail->position = 0; } if(t->position) { len = t->length; len -= t->trackpos; /* portion already travelled by train's head */ } else { /* when exiting, */ len = tail->trackpos;/* this is the length still inside the layout */ i = tail->path->size; } if(len > 0) { --i; while(i >= 0) { changed = 1; s = Vector_elementAt(tail->path, i); if(s->type == TRACK || s->type == SWITCH) { s->fgcolor = color_orange; change_coord(s->x, s->y); tail->position = s; } --i; if(len < s->length) break; len -= s->length; } } else --i; while(i >= 0) { s = Vector_elementAt(tail->path, i); if(s->fgcolor != color_orange) { Vector_deleteElement(tail->path, i); --i; break; } s->fgcolor = conf.fgcolor; change_coord(s->x, s->y); Vector_deleteElement(tail->path, i); --i; changed = 1; } } int fetch_path(Train *t) { char buff[256]; int i; trkdir ndir; Track *trk, *trk1; Track *sig; TrainStop *ts; if(!(trk = t->position) || (trk->type == TEXT && !trk->isstation)) { t->position = 0; if(trk) { trk->fgcolor = conf.fgcolor; change_coord(trk->x, trk->y); } strcpy(buff, t->exit); for(i = 0; buff[i] && buff[i] != ' '; ++i); buff[i] = 0; if(trk && trk->station && !sameStation(trk->station, buff)) { ++perf_tot.wrong_dest; t->wrongdest = 1; t->exited = strdup(trk->station); } /* train is exiting from the layout. */ if(t->tail && t->tail->path && t->tail->pathpos < t->tail->path->size) { if(trk) /* length still inside the layout */ t->tail->trackpos = t->length - t->trackpos; return 0; } if(t->tail) { /* exited for sure! */ t->tail->position = 0; if(t->tail->path) Vector_delete(t->tail->path); t->tail->path = 0; } train_arrived(t); if(trk) change_coord(trk->x, trk->y); t->curspeed = 0; changed = 1; add_update_schedule(t); return 0; } if(!(trk = findNextTrack1(t->direction, t->position->x, t->position->y, &ndir))) { change_coord(t->position->x, t->position->y); new_train_status(t, train_DERAILED); t->position = 0; t->curspeed = 0; add_update_schedule(t); return 0; } if(trk->busy) { /* THIS CODE APPEARS TO BE DEAD */ t->curspeed = 0; if(t->status != train_WAITING) { sprintf(buff, L("Train %s waiting at %d,%d!"), t->name, trk->x, trk->y); do_alert(buff); ++perf_tot.waiting_train; } new_train_status(t, train_WAITING); t->flags |= TFLG_WAITED; add_update_schedule(t); return 0; } if(trk->fgcolor == color_white && t->shunting) { #ifdef MERGE_TRAINS // DIALOG MERGE? int x; Track *wtrk; t->path = findPath0(t->path, trk, t->direction = ndir); for(x = 0; x < t->path->size; ++x) if((wtrk = Vector_elementAt(t->path, x))) if(wtrk->fgcolor != color_white) { t->path->size = x; /* limit to where next train is */ t->flags |= TFLG_MERGING; if(!(t->merging = findTrain(wtrk->x, wtrk->y))) t->merging = findStranded(wtrk->x, wtrk->y); break; } goto proceed; #endif } sig = (t->direction == W_E || t->direction == S_N) ? trk->esignal : trk->wsignal; if(!sig) { t->curspeed = 0; new_train_status(t, train_DERAILED); change_coord(t->position->x, t->position->y); t->position = 0; add_update_schedule(t); return 0; } if(sig->status != ST_GREEN) { t->curspeed = 0; if(t->status != train_WAITING) { sprintf(buff, L("Train %s waiting at"), t->name); if(sig->station) { strcat(buff, sig->station); strcat(buff, " "); } sprintf(buff + strlen(buff), "(%d,%d)", trk->x, trk->y); do_alert(buff); if(!sig->nopenalty) ++perf_tot.waiting_train; } new_train_status(t, train_WAITING); if(t->trackpos > t->position->length) t->trackpos = t->position->length; add_update_schedule(t); return 0; } sig->status = ST_RED; change_coord(sig->x, sig->y); change_coord(t->position->x, t->position->y); t->path = findPath0(t->path, trk, t->direction = ndir); if(!t->path) return -1; proceed: if(t->tail) t->tail->path = appendPath(t->tail->path, t->path); t->pathpos = 0; t->direction = Vector_flagAt(t->path, t->pathpos); t->position = Vector_elementAt(t->path, t->pathpos); t->position->fgcolor = t->tail ? color_orange : conf.fgcolor; change_coord(t->position->x, t->position->y); findStopPoint(t); findSlowPoint(t); ++t->pathpos; changed = 1; return 1; } /* Run_train * * This is the main function for train movement. * It must compute the next position and the next * speed of the train after one time click. */ int run_train(Train *t) { double travelled; double posit; Track *trk; Track *trigger; new_train_status(t, train_RUNNING); if(t->shunting && t->curmaxspeed > 30) t->curmaxspeed = 30; travelled = t->curspeed / 3.6; /* meters travelled in 1 sec. */ trk = t->position; /* if(!strcmp("R11630", t->name)) Vector_dump(t, "run_train"); */ if(!t->position) { /* train is exiting the territory */ if(!t->tail) return 0; compute_new_speed(t); if(t->tail->tailentry) { if((t->tail->tailentry += travelled) >= 0) { /* tail enters the field */ t->tail->trackpos = t->tail->tailentry; t->tail->tailentry = 0; t->tail->pathpos = 0; } } else t->tail->trackpos -= travelled; tail_advance(t); if(t->tail->path->size && t->tail->pathpos < t->tail->path->size) return 1; /* exited! */ fetch_path(t); return 0; } agn: speed_limit(t, trk); trk->busy = 0; trk->flags &= ~TFLG_THROWN; if(!trk->length) trk->length = 1; if(trk->length < 2 && t->needfindstop) { findStopPoint(t); t->needfindstop = 0; } posit = t->trackpos; if(posit < trk->length) { posit += travelled; run_points += time_mult * run_point_base + 1; compute_new_speed(t); /* accelerate/decelerate train */ if(t->stopping && t->pathtravelled + travelled >= t->disttostop) { train_at_station(t, t->stopping); return 0; } t->pathtravelled += travelled; if(posit < trk->length) { t->trackpos = posit; /* we are still in the same track */ if(!t->length || !t->tail)/* no length info for this train */ return 1; if(t->tail->tailentry) { if((t->tail->tailentry += travelled) < 0) return 1; /* tail is still out of field */ /* tail enters the field */ t->tail->trackpos = t->tail->tailentry; t->tail->tailentry = 0; t->tail->pathpos = 0; } else t->tail->trackpos += travelled; tail_advance(t); return 1; } t->trackpos = posit - trk->length;/* meters already travelled in next track */ if(t->tail) { if(t->tail->tailentry) { if((t->tail->tailentry += travelled) >= 0) { /* tail enters the field */ t->tail->trackpos = t->tail->tailentry; t->tail->tailentry = 0; t->tail->pathpos = 0; } } else t->tail->trackpos += travelled; } travelled = 0; } #ifndef HOMAN_030330 if(t->slowpoint && t->position == t->slowpoint) findSlowPoint(t); #endif if(!t->path || t->pathpos == t->path->size) { if(t->stopping) { /* don't advance to another path if * we wanted to stop at a station. */ train_at_station(t, t->stopping); return 0; } #ifdef MERGE_TRAINS if(t->shunting && t->merging) { Train *t2; new_train_status(t, t->oldstatus); t->shunting = 0; t->curspeed = 0; t2 = t->merging; t2->length += t->length; if(t2->flags & TFLG_STRANDED) { change_coord(t->position->x, t->position->y); t->position = t2->position; if(t2 == stranded) stranded = t2->next; else if(t2->next) t2->next = t2->next->next; else stranded = 0; free(t2); t->path = findPath(t->position, t->direction); colorPartialPath(t->path, ST_GREEN, 1); t->pathpos = 0; findStopPoint(t); findSlowPoint(t); t->pathpos = 1; if(t->position->isstation) train_at_station(t, t->position); t->merging = 0; } else { t->position = 0; } // NEED TO MERGE PATHS of tails return 0; } #endif t->pathtravelled = t->trackpos; switch(fetch_path(t)) { case 0: if(t->tail) tail_advance(t); return 0; case -1: return -1; } travelled = t->trackpos; t->trackpos = 0; t->pathtravelled = 0; trk = t->position; goto agn; /* no more tracks in this path, get new path */ } /* advance to next track in this path */ tail_advance(t); if(t->stopping) { if(t->pathtravelled >= t->disttostop) { train_at_station(t, t->stopping); return 0; } } changed = 1; change_coord(trk->x, trk->y); t->direction = Vector_flagAt(t->path, t->pathpos); t->position = trk = Vector_elementAt(t->path, t->pathpos++); trk->fgcolor = t->tail ? color_orange : conf.fgcolor; change_coord(trk->x, trk->y); do_triggers(t); #ifdef HOMAN_030330 if(t->slowpoint && trk == t->slowpoint) { speed_limit(t, trk); /* set maxcurspeed */ findSlowPoint(t); } #endif if(trk->isstation && /*train_at_station(t, trk)*/ stopping_at_this_station(t, trk)) { if(!t->length) { /* pre-1.18 code */ if(!train_at_station(t, trk)) goto agn; return 1; } /* 1.18 code: decide where to stop so that * as much of the train as possible * is at the station. */ t->stopping = trk; /* we're stopping at this station */ return 1; } goto agn; } #define HOUR(h) ((h) * 60 * 60) void crossing_midnight(void) { Train *t; TrainStop *ts; for(t = schedule; t; t = t->next) { if(t->timein < HOUR(12)) t->timein += HOUR(24); if(t->timeout < HOUR(12)) t->timeout += HOUR(24); for(ts = t->stops; ts; ts = ts->next) { if(ts->arrival < HOUR(12)) ts->arrival += HOUR(24); if(ts->departure < HOUR(12)) ts->departure += HOUR(24); } } } void time_step(void) { Train *t, *t1; Track *trk; char buff[256]; char buff1[256]; int i; int speed; int do_beep = 0; current_time += 1; if((current_time % HOUR(24)) == 0) crossing_midnight(); if(frply) /* issue all commands for this time slice */ do_replay(); for(t = schedule; t; t = t->next) { trk = 0; switch(t->status) { case train_ARRIVED: continue; case train_READY: if(!t->entrance || t->timein > current_time) continue; if(t->days && run_day && !(t->days & run_day)) continue; if(t->timein < start_time) /* will always ignore it */ continue; if(t->waitfor) { t1 = findTrainNamed(t->waitfor); if(!t1 || t1->status != train_ARRIVED) continue; if(!t->waittime) t->waittime = 60; /* default we wait 60 seconds */ if(t1->timeexited + t->waittime > current_time) continue; /* can't depart, yet */ strcpy(buff1, t1->exit); for(i = 0; buff1[i] && buff1[i] != ' '; ++i); buff1[i] = 0; if((trk = findStation(buff1)) && trk->type != TRACK) goto startit; /* exited the layout - no need to assign */ if(!t->timedelay) { /* first time, issue an alert */ sprintf(buff, L("You must assign train %s using stock from train %s!"), t->name, t->waitfor); do_alert(buff); } t->timedelay += time_mult; total_delay += time_mult; continue; } do_beep = 1; startit: strcpy(buff1, t->entrance); for(i = 0; buff1[i] && buff1[i] != ' '; ++i); buff1[i] = 0; if(t->position) change_coord(t->position->x, t->position->y); if(!(trk = findEntryTrack(t, buff1))) { new_train_status(t, train_DERAILED); t->position = 0; add_update_schedule(t); sprintf(buff, L("Train %s derailed at %s!"), t->name, buff1); do_alert(buff); continue; } t->path = findPath0(t->path, trk, t->direction); if(!t->path) { derail: new_train_status(t, train_DERAILED); t->position = 0; add_update_schedule(t); if(trk) sprintf(buff, L("Train %s derailed at %d,%d!"), t->name, trk->x, trk->y); else sprintf(buff, L("Train %s derailed!"), t->name); do_alert(buff); continue; } if(t->tail) { t->tail->path = appendPath(t->tail->path, t->path); t->tail->tailentry = -t->length; } if(pathIsBusy(NULL, t->path, t->direction)) { new_train_status(t, train_DELAY); add_update_schedule(t); sprintf(buff, L("Train %s delayed at %s!"), t->name, buff1); do_alert(buff); continue; } t->pathtravelled = 0; start_it: if(do_beep && beep_on_enter) enter_beep(); new_train_status(t, train_RUNNING); t->position = trk; t->pathpos = 1; /* 1 because trk is in path[0] */ t->wrongdest = 0; t->curspeed = t->maxspeed ? t->maxspeed : 60; t->curmaxspeed = t->curspeed; t->trackpos = 0; speed = t->position->speed[t->type]; if(!speed) speed = t->position->speed[0]; if(speed && t->maxspeed && t->curspeed > speed) { t->curspeed = speed; t->curmaxspeed = t->curspeed; } else if(speed && !t->maxspeed && speed > t->curspeed) { t->curspeed = speed; t->curmaxspeed = t->curspeed; } changed = 1; colorPath(t->path, ST_GREEN); trk->fgcolor = t->tail ? color_orange : conf.fgcolor; change_coord(trk->x, trk->y); t->pathpos = 0; findStopPoint(t); findSlowPoint(t); t->pathpos = 1; if(trk->isstation) train_at_station(t, trk); add_update_schedule(t); continue; case train_DELAY: if(!t->path) continue; if(pathIsBusy(NULL, t->path, t->direction)) { t->timedelay += time_mult; total_delay += time_mult; add_update_schedule(t); break; } do_beep = 1; trk = Vector_elementAt(t->path, 0); goto start_it; case train_STOPPED: if(t->timedep > current_time) continue; t->flags = 0; /* clear performance flags */ findStopPoint(t); /* find next stop point */ t->needfindstop = 1; new_train_status(t, train_RUNNING); goto runit; case train_WAITING: switch(fetch_path(t)) { case 0: continue; case -1: goto derail; } t->outof = 0; /* in case we are moving from * one platform to another, we * want to stop at the same station. */ /* fall through */ case train_RUNNING: /*case train_SHUNTING:*/ runit: t->flags &= ~TFLG_TURNED; if(run_train(t) == -1) goto derail; add_update_schedule(t); continue; } } } void click_time(void) { int i; int oldmult; Train *t; changed = 0; for(i = oldmult = time_mult; i > 0; --i) { time_mult = 1; time_step(); } open_all_fleeted(); if(changed) repaint_all(); changed = 0; time_mult = oldmult; update_labels(); for(t = schedule; t; t = t->next) if(t->newsched) update_schedule(t); } void start_stop(void) { if(running) { make_timer(0); running = 0; } else { running = 1; make_timer(1000); } } void assign_train(Train *t, Train *oldtrain) { if(oldtrain->stock && strcmp(t->name, oldtrain->stock)) ++perf_tot.wrong_assign; new_train_status(t, train_STOPPED); update_labels(); t->path = oldtrain->path; t->position = oldtrain->position; t->pathpos = oldtrain->pathpos; t->direction = oldtrain->direction; t->curmaxspeed = oldtrain->curmaxspeed; t->maxspeed = oldtrain->maxspeed; t->timedep = t->timein; if(oldtrain->tail) { if(!t->tail) { t->length = oldtrain->length; t->tail = (Train *)calloc(sizeof(Train), 1); t->ecarpix = oldtrain->ecarpix; t->wcarpix = oldtrain->wcarpix; } else { /* here we should check that the preset length of the * destination train is the same as that of the old train. * If it is, then we can simply copy the path to the * destination train. * If the destination train is longer, we should * notify the player that we don't have enough rolling * stock, and add to the penalty. * If the destination train is longer we should split * the source in two, and leave some cars in the path. * * For the time being we simply assign the source train * to the destination train, since adding train splitting * is going to be another nightmare! */ } t->tail->path = oldtrain->tail->path; t->tail->pathpos = oldtrain->tail->pathpos; t->tail->position = oldtrain->tail->position; t->tail->trackpos = oldtrain->tail->trackpos; t->fleet = oldtrain->fleet; oldtrain->fleet = 0; oldtrain->tail->path = 0; oldtrain->tail->pathpos = 0; oldtrain->tail->trackpos = 0; oldtrain->tail->position = 0; } oldtrain->pathpos = 0; oldtrain->path = 0; oldtrain->position = 0; update_schedule(t); } void shunt_train(Train *t) { t->oldstatus = t->status; new_train_status(t, train_RUNNING); t->shunting = 1; t->stoppoint = 0; t->slowpoint = 0; t->outof = t->position; } void split_train(Train *t) { Train *stk; if(t->flags & TFLG_STRANDED) /* can't split multiple times */ return; stk = (Train *)malloc(sizeof(Train)); memset(stk, 0, sizeof(Train)); stk->name = strdup(""); stk->next = stranded; stk->type = t->type; stk->flags = TFLG_STRANDED; stk->position = t->position; stk->ecarpix = t->ecarpix; stk->wcarpix = t->wcarpix; stk->status = train_ARRIVED; stranded = stk; } void reverse_train(Train *tr) { Track *t, *pos, *headpos, *tailpos; Vector *path, *tailpath; trkdir newdir; int el; if(!tr->position) return; switch(tr->direction) { case W_E: newdir = E_W; break; case E_W: newdir = W_E; break; case N_S: newdir = S_N; break; case S_N: newdir = N_S; } headpos = tr->position; if(tr->tail && (tailpos = tr->tail->position) && tailpos != tr->position) path = findPath(tailpos, newdir); else path = findPath(headpos, newdir); if(!path) { ++perf_tot.denied; update_labels(); return; } if(tr->tail && tailpos) { tailpos->fgcolor = conf.fgcolor; tr->tail->position = 0; } else { tr->position->fgcolor = conf.fgcolor; tr->position = 0; } if(pathIsBusy(tr, path, newdir)) { if(tr->tail && tailpos) { tr->tail->position = tailpos; tailpos->fgcolor = color_orange; } else tr->position = headpos; do_alert(L("Cannot reverse direction. Path is busy.")); Vector_delete(path); ++perf_tot.denied; update_labels(); return; } if(tr->path) { colorPartialPath(tr->path, ST_FREE, tr->pathpos); Vector_delete(tr->path); tr->path = 0; } if(tr->tail && tr->tail->path) { int f; tailpath = new_Vector(); for(el = tr->tail->path->size - 1; el >= tr->tail->pathpos; --el) { t = (Track *)Vector_elementAt(tr->tail->path, el); f = Vector_flagAt(tr->tail->path, el); Vector_addElement(tailpath, t, f); } Vector_delete(tr->tail->path); tr->tail->path = tailpath; tr->tail->pathpos = 0; f = tr->tail->trackpos; tr->tail->trackpos = headpos->length - tr->trackpos; if(tailpos) tr->trackpos = tailpos->length - f; else tr->trackpos = headpos->length - f - tr->length; if(tr->trackpos < 0) tr->trackpos = 0; } tr->direction = newdir; colorPath(path, ST_GREEN); if(tr->tail) { tr->position = tailpos ? tailpos : headpos; tr->tail->position = tailpos ? headpos : NULL; tr->tail->path = appendPath(tr->tail->path, path); change_coord(headpos->x, headpos->y); } else tr->position = headpos; pos = tr->position; pos->fgcolor = conf.fgcolor; change_coord(pos->x, pos->y); if(tr->flags & TFLG_TURNED) { ++perf_tot.turned_train; update_labels(); } tr->flags |= TFLG_TURNED; tr->path = path; tr->pathpos = 1; /* 1 because trk is in path[0] */ if(tr->status != train_STOPPED) {/* waiting at a signal? */ if(tr->status != train_ARRIVED) { new_train_status(tr, train_STOPPED); tr->timedep = current_time; tr->outof = 0; } } tail_advance(tr); /* compute and draw tail path */ repaint_all(); } int toggle_signal_auto(Track *t, int do_log) { Vector *path; if(t->fixedred) { do_alert(L("This signal cannot be turned to green!")); return 0; } path = findPath(t->controls, t->direction); if(!path) return 0; if(flog && do_log) fprintf(flog, "%ld,click %d,%d\n", current_time, t->x, t->y); if(t->status == ST_GREEN) { if(!t->fleeted) { ++perf_tot.cleared_signal; update_labels(); } t->status = ST_RED; t->nowfleeted = 0; change_coord(t->x, t->y); colorPath(path, ST_READY); repaint_all(); return 0; } if(pathIsBusy(NULL, path, t->direction)) { Vector_delete(path); repaint_all(); return 0; } change_coord(t->x, t->y); t->status = ST_GREEN; t->fgcolor = color_green; colorPath(path, ST_GREEN); Vector_delete(path); repaint_all(); return 1; } int toggle_signal(Track *t) { return toggle_signal_auto(t, 1); } void clear_visited(void) { Itinerary *ip; for(ip = itineraries; ip; ip = ip->next) ip->visited = 0; } int check_itinerary(Itinerary *it) { char *nextitin; Track *t1; int i; clear_visited(); agn: if(!it || it->visited) return 0; for(i = 0; i < it->nsects; ++i) { t1 = findSwitch(it->sw[i].x, it->sw[i].y); if(!t1 || t1->fgcolor == color_green) return 0; it->sw[i].oldsw = t1->switched; if(it->sw[i].switched != t1->switched) if((t1 = findSwitch(t1->wlinkx, t1->wlinky))) if(t1->fgcolor == color_green) return 0; } if(!(nextitin = it->nextitin) || !*nextitin) return 1; it->visited = 1; for(it = itineraries; it; it = it->next) if(!strcmp(it->name, nextitin)) break; goto agn; } void toggle_itinerary(Itinerary *it) { Track *t1; int i; char *nextitin; do { for(i = 0; i < it->nsects; ++i) { t1 = findSwitch(it->sw[i].x, it->sw[i].y); if(it->sw[i].switched != t1->switched) { t1->switched = !t1->switched; change_coord(t1->x, t1->y); if((t1 = findSwitch(t1->wlinkx, t1->wlinky))) { t1->switched = !t1->switched; change_coord(t1->x, t1->y); } } } if(!(nextitin = it->nextitin) || !*nextitin) return; for(it = itineraries; it; it = it->next) if(!strcmp(it->name, nextitin)) break; } while(it); /* always true */ } int green_itinerary(Itinerary *it) { Track *t1; Itinerary *ip; char *nextitin; int i; Track *blocks[100]; int nxtblk; nxtblk = 0; for(ip = it; ip; ) { if(!(t1 = findSignalNamed(ip->signame))) return 0; if(t1->status == ST_GREEN) return 0; blocks[nxtblk++] = t1; if(!(nextitin = ip->nextitin) || !*nextitin) break; /* done */ for(ip = itineraries; ip; ip = ip->next) if(!strcmp(ip->name, nextitin)) break; } /* all signals are red, try to turn them green */ for(i = 0; i < nxtblk; ++i) if(!toggle_signal(blocks[i])) break; /* line block is busy */ if(i >= nxtblk) /* success! */ return 1; while(--i >= 0) /* undo signal toggling */ toggle_signal(blocks[i]); return 0; } void itinerary_selected(Itinerary *it) { Track *t1; int i; char *nextitin; if(!check_itinerary(it)) return; toggle_itinerary(it); if(green_itinerary(it)) return; /* success */ /* error - restore switches status */ err: for(i = 0; i < it->nsects; ++i) { t1 = findSwitch(it->sw[i].x, it->sw[i].y); if(!t1) continue; if(it->sw[i].switched == it->sw[i].oldsw) continue; t1->switched = !t1->switched; if((t1 = findSwitch(t1->wlinkx, t1->wlinky))) t1->switched = !t1->switched; } if(!(nextitin = it->nextitin) || !*nextitin) return; for(it = itineraries; it; it = it->next) if(!strcmp(it->name, nextitin)) break; if(it) goto err; } void try_itinerary(int sx, int sy, int ex, int ey) { Itinerary *it = 0; Track *t1, *t2; t1 = findSignal(sx, sy); t2 = findSignal(ex, ey); if(!t1 || !t2) return; if(t1->station && *t1->station && t2->station && *t2->station) { for(it = itineraries; it; it = it->next) if(!strcmp(it->signame, t1->station) && !strcmp(it->endsig, t2->station)) break; } if(!it) { Itinerary *it = find_from_to(t1, t2); return; } itinerary_selected(it); } void track_selected(int x, int y) { Track *t, *t1; Train *trn; Itinerary *it; int i; char buff[40]; if(trn = findTrain(x, y)) { if(trn->curspeed) { do_alert(L("You must wait for train to stop.")); return; } if(ask(L("Reverse train direction?")) != ANSWER_YES) return; sprintf(buff, "reverse %s", trn->name); trainsim_cmd(buff); } else if((t = findSwitch(x, y))) { if(t->fgcolor == color_green) { ++perf_tot.denied; update_labels(); return; } if(flog) fprintf(flog, "%ld,click %d,%d\n", current_time, x, y); if((t1 = findSwitch(t->wlinkx, t->wlinky))) { if(t1->fgcolor == color_green) { ++perf_tot.denied; update_labels(); return; } change_coord(t1->x, t1->y); t1->switched = !t1->switched; } t->switched = !t->switched; if(t->flags & TFLG_THROWN) { ++perf_tot.thrown_switch; update_labels(); } t->flags |= TFLG_THROWN; change_coord(t->x, t->y); repaint_all(); } else if((t = findSignal(x, y)) && t->controls) { i = t->status; if(!toggle_signal(t) && i != ST_GREEN) { ++perf_tot.denied; update_labels(); } } else if((t = findTrackType(x, y, ITIN))) { for(it = itineraries; it; it = it->next) if(!strcmp(it->name, t->station)) break; itinerary_selected(it); } } void track_selected1(int x, int y) { Track *t; Train *tr; char buff[40]; if((t = findSignal(x, y))) { if(!t->fleeted || t->status != ST_GREEN) return; if(flog) fprintf(flog, "%ld,click1 %d,%d\n", current_time, x, y); t->nowfleeted = !t->nowfleeted; change_coord(t->x, t->y); repaint_all(); return; } if((tr = findTrain(x, y))) { if(tr->status == train_ARRIVED && assign_ok) { assign_dialog(tr); return; } if(tr->curspeed) { do_alert(L("You must wait for train to stop.")); return; } if(ask(L("Proceed to next station?")) != ANSWER_YES) return; sprintf(buff, "shunt %s", tr->name); trainsim_cmd(buff); return; } } int track_shift_selected(int x, int y) { Track *t; Train *trn; Vector *path; char buff[256]; if((trn = findTrain(x, y))) { sprintf(buff, "traininfo %s", trn->name); trainsim_cmd(buff); /* train_info_dialog(trn); */ return 1; } if((t = findTrack(x, y)) && t->type == TRACK && t->station) { sprintf(buff, "stationinfo %s", t->station); trainsim_cmd(buff); /* station_sched_dialog(t->station); */ return 1; } return 0; } int track_control_selected(int x, int y) { Track *t; Train *trn; Vector *path; char buff[256]; if((t = findSignal(x, y)) && t->controls) { int i; Track *trk; Train *tr; if(t->status == ST_GREEN) return 0; if(t->fixedred) { /* ADD PENALTY */ return 0; } path = findPath(t->controls, t->direction); if(!path) return 0; if((i = pathIsBusy(NULL, path, t->direction))) { trk = Vector_elementAt(path, i - 1); tr = findTrain(trk->x, trk->y); if(!tr) tr = findStranded(trk->x, trk->y); if(!tr || (tr->status != train_STOPPED && tr->status != train_ARRIVED)) { Vector_delete(path); repaint_all(); return 0; } } else i = path->size + 1; change_coord(t->x, t->y); t->status = ST_WHITE; t->fgcolor = color_white; colorPathStart(path, ST_WHITE, i - 1); Vector_delete(path); repaint_all(); return 1; } return track_shift_selected(x, y); } void fill_itinerary(Itinerary *it, Track *sig) { Track *t; Vector *path; int i; int dir; char buff[64]; if(!sig->controls) /* bad! */ return; if(sig->status != ST_GREEN)/* we want a path after the signal! */ return; path = findPath(sig->controls, sig->direction); if(!path) return; i = path->size - 1; if(i < 0) return; t = (Track *)Vector_elementAt(path, i); dir = Vector_flagAt(path, i); if(t->type == TEXT) goto ok; if(!(t = findNextTrack(dir, t->x, t->y))) return; /* should be impossible */ t = (dir == E_W || dir == N_S) ? t->wsignal : t->esignal; if(!t) return; if(!t->station || !*t->station) { sprintf(buff, "(%d,%d)", t->x, t->y); t->station = strdup(buff); } ok: if(it->endsig && strcmp(it->endsig, t->station)) free(it->endsig); it->endsig = strdup(t->station); for(i = 0; i < path->size; ++i) { t = (Track *)Vector_elementAt(path, i); if(t->type == SWITCH) add_itinerary(it, t->x, t->y, t->switched); } } void open_all_signals(void) { Track *t; for(t = layout; t; t = t->next) { if(t->type == TSIGNAL) { if(t->fleeted) { if(t->nowfleeted || t->status == ST_GREEN || t->signalx) continue; t->nowfleeted = 1; toggle_signal(t); } } } } void open_all_fleeted(void) { Track *s; for(s = signal_list; s; s = s->next1) { if(!s->fleeted) continue; if(!s->nowfleeted || s->status == ST_GREEN) continue; if(!s->controls || s->controls->fgcolor == color_green) continue; toggle_signal_auto(s, 0); } } void accelerate_train(Train *t, long val) { if(t->status != train_RUNNING) return; t->curmaxspeed += val; if(t->curmaxspeed >= t->speedlimit) /* can't go faster than last speed limit */ t->curmaxspeed = t->speedlimit; } void decelerate_train(Train *t, long val) { if(t->status != train_RUNNING) return; if(t->curmaxspeed - val < 0) /* can't stop train! No way to resume, yet */ return; t->curmaxspeed -= val; if(t->curspeed > t->curmaxspeed) t->curspeed = t->curmaxspeed; }