summaryrefslogtreecommitdiff
path: root/app/bin/ctrain.c
diff options
context:
space:
mode:
Diffstat (limited to 'app/bin/ctrain.c')
-rw-r--r--app/bin/ctrain.c2586
1 files changed, 2586 insertions, 0 deletions
diff --git a/app/bin/ctrain.c b/app/bin/ctrain.c
new file mode 100644
index 0000000..b78dc9e
--- /dev/null
+++ b/app/bin/ctrain.c
@@ -0,0 +1,2586 @@
+/** \file ctrain.c
+ * Functions related to running trains
+ *
+ */
+
+/* XTrkCad - Model Railroad CAD
+ * Copyright (C) 2005 Dave Bullis
+ *
+ * This program 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 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef WINDOWS
+#include <errno.h>
+#endif
+#include <ctype.h>
+
+#define PRIVATE_EXTRADATA
+#include "track.h"
+#include "trackx.h"
+#include "ctrain.h"
+#include "compound.h"
+#include "i18n.h"
+
+EXPORT long programMode;
+EXPORT long maxCouplingSpeed = 100;
+EXPORT long hideTrainsInTunnels;
+
+extern int doDrawTurnoutPosition;
+extern void NextTurnoutPosition( track_p );
+
+static TRKTYP_T T_CAR = -1;
+
+typedef enum { ST_NotOnTrack, ST_StopManual, ST_EndOfTrack, ST_OpenTurnout, ST_NoRoom, ST_Crashed } trainStatus_e;
+
+struct extraData {
+ traverseTrack_t trvTrk;
+ long state;
+ carItem_p item;
+ double speed;
+ BOOL_T direction;
+ BOOL_T autoReverse;
+ trainStatus_e status;
+ DIST_T distance;
+ coOrd couplerPos[2];
+ LAYER_T trkLayer;
+ };
+#define NOTALAYER (127)
+
+#define CAR_STATE_IGNORED (1L<<17)
+#define CAR_STATE_PROCESSED (1L<<18)
+#define CAR_STATE_LOCOISMASTER (1L<<19)
+#define CAR_STATE_ONHIDENTRACK (1L<<20)
+
+
+#define IsOnTrack( XX ) ((XX)->trvTrk.trk!=NULL)
+#define IsIgnored( XX ) (((XX)->state&CAR_STATE_IGNORED)!=0)
+#define SetIgnored( XX ) (XX)->state |= CAR_STATE_IGNORED
+#define ClrIgnored( XX ) (XX)->state &= ~CAR_STATE_IGNORED
+#ifdef LATER
+#define IsLocoMaster( XX ) (((XX)->state&CAR_STATE_LOCOISMASTER)!=0)
+#define SetLocoMaster( XX ) (XX)->state |= CAR_STATE_LOCOISMASTER
+#define ClrLocoMaster( XX ) (XX)->state &= ~CAR_STATE_LOCOISMASTER
+#endif
+#define IsLocoMaster( XX ) CarItemIsLocoMaster((XX)->item)
+#define SetLocoMaster( XX ) CarItemSetLocoMaster((XX)->item,TRUE)
+#define ClrLocoMaster( XX ) CarItemSetLocoMaster((XX)->item,FALSE)
+#define IsProcessed( XX ) (((XX)->state&CAR_STATE_PROCESSED)!=0)
+#define SetProcessed( XX ) (XX)->state |= CAR_STATE_PROCESSED
+#define ClrProcessed( XX ) (XX)->state &= ~CAR_STATE_PROCESSED
+
+static wButton_p newcarB;
+
+static void ControllerDialogSyncAll( void );
+static STATUS_T CmdTrain( wAction_t, coOrd );
+static wMenu_p trainPopupM;
+static wMenuPush_p trainPopupMI[8];
+static track_p followTrain;
+static coOrd followCenter;
+static BOOL_T trainsTimeoutPending;
+static enum { TRAINS_STOP, TRAINS_RUN, TRAINS_IDLE, TRAINS_PAUSE } trainsState;
+static wIcon_p stopI, goI;
+static void RestartTrains( void );
+static void DrawAllCars( void );
+static void UncoupleCars( track_p, track_p );
+static void TrainTimeEndPause( void );
+static void TrainTimeStartPause( void );
+
+static int log_trainMove;
+static int log_trainPlayback;
+
+static void PlaceCar( track_p );
+
+
+#define WALK_CARS_START( CAR, XX, DIR ) \
+ while (1) { \
+ (XX) = GetTrkExtraData(CAR);\
+ { \
+
+#define WALK_CARS_END( CAR, XX, DIR ) \
+ } \
+ { \
+ track_p walk_cars_temp1; \
+ if ( (walk_cars_temp1=GetTrkEndTrk(CAR,DIR)) == NULL ) break; \
+ (DIR)=(GetTrkEndTrk(walk_cars_temp1,0)==(CAR)?1:0); \
+ (CAR)=walk_cars_temp1; \
+ } \
+ }
+
+
+/*
+ * Generic Commands
+ */
+
+EXPORT void CarGetPos(
+ track_p car,
+ coOrd * posR,
+ ANGLE_T * angleR )
+{
+ struct extraData * xx = GetTrkExtraData( car );
+ if ( GetTrkType(car) != T_CAR )
+ AbortProg( "getCarPos" );
+ *posR = xx->trvTrk.pos;
+ *angleR = xx->trvTrk.angle;
+}
+
+EXPORT void CarSetVisible(
+ track_p car )
+{
+ struct extraData * xx;
+ int dir;
+ dir = 0;
+ WALK_CARS_START( car, xx, dir )
+ if ( GetTrkType(car) != T_CAR )
+ AbortProg( "carSetVisible" );
+ WALK_CARS_END( car, xx, dir )
+ dir = 1-dir;
+ WALK_CARS_START( car, xx, dir ) {
+ xx->state &= ~(CAR_STATE_ONHIDENTRACK);
+ xx->trkLayer = NOTALAYER;
+ }
+ WALK_CARS_END( car, xx, dir )
+}
+
+
+static struct {
+ long index;
+ coOrd pos;
+ ANGLE_T angle;
+ DIST_T length;
+ DIST_T width;
+ char desc[STR_SIZE];
+ char number[STR_SIZE];
+ } carData;
+typedef enum { IT, PN, AN, LN, WD, DE, NM } carDesc_e;
+static descData_t carDesc[] = {
+/*IT*/ { DESC_LONG, N_("Index"), &carData.index },
+/*PN*/ { DESC_POS, N_("Position"), &carData.pos },
+/*AN*/ { DESC_ANGLE, N_("Angle"), &carData.angle },
+/*LN*/ { DESC_DIM, N_("Length"), &carData.length },
+/*WD*/ { DESC_DIM, N_("Width"), &carData.width },
+/*DE*/ { DESC_STRING, N_("Description"), &carData.desc },
+/*NM*/ { DESC_STRING, N_("Rep Marks"), &carData.number },
+ { DESC_NULL } };
+
+static void UpdateCar(
+ track_p trk,
+ int inx,
+ descData_p descUpd,
+ BOOL_T needUndoStart )
+{
+ BOOL_T titleChanged;
+ const char * cp;
+ if ( inx == -1 ) {
+ titleChanged = FALSE;
+ cp = wStringGetValue( (wString_p)carDesc[NM].control0 );
+ if ( cp && strcmp( carData.number, cp ) != 0 ) {
+ titleChanged = TRUE;
+ strcpy( carData.number, cp );
+ }
+ if ( !titleChanged )
+ return;
+ if ( needUndoStart )
+ UndoStart( _("Change Track"), "Change Track" );
+ UndoModify( trk );
+ UndrawNewTrack( trk );
+ DrawNewTrack( trk );
+ return;
+ }
+ UndrawNewTrack( trk );
+ switch (inx) {
+ case NM:
+ break;
+ default:
+ break;
+ }
+ DrawNewTrack( trk );
+}
+
+
+static void DescribeCar(
+ track_p trk,
+ char * str,
+ CSIZE_T len )
+{
+ struct extraData *xx = GetTrkExtraData(trk);
+ char * cp;
+ coOrd size;
+
+ CarItemSize( xx->item, &size );
+ carData.length = size.x;
+ carData.width = size.y;
+ cp = CarItemDescribe( xx->item, 0, &carData.index );
+ strcpy( carData.number, CarItemNumber(xx->item) );
+ strncpy( str, cp, len );
+ carData.pos = xx->trvTrk.pos;
+ carData.angle = xx->trvTrk.angle;
+ cp = CarItemDescribe( xx->item, -1, NULL );
+ strncpy( carData.desc, cp, sizeof carData.desc );
+ carDesc[IT].mode =
+ carDesc[PN].mode =
+ carDesc[AN].mode =
+ carDesc[LN].mode =
+ carDesc[WD].mode = DESC_RO;
+ carDesc[DE].mode =
+ carDesc[NM].mode = DESC_RO;
+ DoDescribe( _("Car"), trk, carDesc, UpdateCar );
+}
+
+
+EXPORT void FlipTraverseTrack(
+ traverseTrack_p trvTrk )
+{
+ trvTrk->angle = NormalizeAngle( trvTrk->angle + 180.0 );
+ if ( trvTrk->length > 0 )
+ trvTrk->dist = trvTrk->length - trvTrk->dist;
+}
+
+
+EXPORT BOOL_T TraverseTrack2(
+ traverseTrack_p trvTrk0,
+ DIST_T dist0 )
+{
+ traverseTrack_t trvTrk = *trvTrk0;
+ DIST_T dist = dist0;
+ if ( dist0 < 0 ) {
+ dist = -dist;
+ FlipTraverseTrack( &trvTrk );
+ }
+ if ( trvTrk.trk==NULL ||
+ (!TraverseTrack(&trvTrk,&dist)) ||
+ trvTrk.trk==NULL ||
+ dist!=0.0 ) {
+ Translate( &trvTrk.pos, trvTrk.pos, trvTrk.angle, dist );
+ }
+ if ( dist0 < 0 )
+ FlipTraverseTrack( &trvTrk );
+ *trvTrk0 = trvTrk;
+ return TRUE;
+}
+
+
+
+static BOOL_T drawCarEnable = TRUE;
+static BOOL_T noCarDraw = FALSE;
+
+static void DrawCar(
+ track_p car,
+ drawCmd_p d,
+ wDrawColor color )
+{
+ struct extraData * xx = GetTrkExtraData(car);
+ int dir;
+ vector_t coupler[2];
+ track_p car1;
+ struct extraData * xx1;
+ int dir1;
+
+ if ( drawCarEnable == FALSE )
+ return;
+ /*d = &tempD;*/
+/*
+ if ( !IsVisible(xx) )
+ return;
+*/
+ if ( d == &mapD )
+ return;
+ if ( noCarDraw )
+ return;
+ if ( hideTrainsInTunnels &&
+ ( (((xx->state&CAR_STATE_ONHIDENTRACK)!=0) && drawTunnel==0) ||
+ (xx->trkLayer!=NOTALAYER && !GetLayerVisible(xx->trkLayer)) ) )
+ return;
+
+ for ( dir=0; dir<2; dir++ ) {
+ coupler[dir].pos = xx->couplerPos[dir];
+ if ( (car1 = GetTrkEndTrk(car,dir)) ) {
+ xx1 = GetTrkExtraData(car1);
+ dir1 = (GetTrkEndTrk(car1,0)==car)?0:1;
+ coupler[dir].angle = FindAngle( xx->couplerPos[dir], xx1->couplerPos[dir1] );
+ } else {
+ coupler[dir].angle = NormalizeAngle(xx->trvTrk.angle+(dir==0?0.0:180.0)-15.0);
+ }
+ }
+ CarItemDraw( d, xx->item, color, xx->direction, IsLocoMaster(xx), coupler );
+}
+
+
+static DIST_T DistanceCar(
+ track_p trk,
+ coOrd * pos )
+{
+ struct extraData * xx = GetTrkExtraData(trk);
+ DIST_T dist;
+ coOrd pos1;
+ coOrd size;
+
+ xx = GetTrkExtraData(trk);
+ if ( IsIgnored(xx) )
+ return 10000.0;
+
+ CarItemSize( xx->item, &size ); /* TODO assumes xx->trvTrk.pos is the car center */
+ dist = FindDistance( *pos, xx->trvTrk.pos );
+ if ( dist < size.x/2.0 ) {
+ pos1 = *pos;
+ Rotate( &pos1, xx->trvTrk.pos, -xx->trvTrk.angle );
+ pos1.x += -xx->trvTrk.pos.x + size.y/2.0; /* TODO: why not size.x? */
+ pos1.y += -xx->trvTrk.pos.y + size.x/2.0;
+ if ( pos1.x >= 0 && pos1.x <= size.y &&
+ pos1.y >= 0 && pos1.y <= size.x )
+ dist = 0;
+ }
+ *pos = xx->trvTrk.pos;
+ return dist;
+}
+
+
+static void SetCarBoundingBox(
+ track_p car )
+{
+ struct extraData * xx = GetTrkExtraData(car);
+ coOrd lo, hi, p[4];
+ int inx;
+ coOrd size;
+
+/* TODO: should be bounding box of all pieces aligned on track */
+ CarItemSize( xx->item, &size ); /* TODO assumes xx->trvTrk.pos is the car center */
+ Translate( &p[0], xx->trvTrk.pos, xx->trvTrk.angle, size.x/2.0 );
+ Translate( &p[1], p[0], xx->trvTrk.angle+90, size.y/2.0 );
+ Translate( &p[0], p[0], xx->trvTrk.angle-90, size.y/2.0 );
+ Translate( &p[2], xx->trvTrk.pos, xx->trvTrk.angle+180, size.x/2.0 );
+ Translate( &p[3], p[2], xx->trvTrk.angle+90, size.y/2.0 );
+ Translate( &p[2], p[2], xx->trvTrk.angle-90, size.y/2.0 );
+ lo = hi = p[0];
+ for ( inx = 1; inx < 4; inx++ ) {
+ if ( p[inx].x < lo.x )
+ lo.x = p[inx].x;
+ if ( p[inx].y < lo.y )
+ lo.y = p[inx].y;
+ if ( p[inx].x > hi.x )
+ hi.x = p[inx].x;
+ if ( p[inx].y > hi.y )
+ hi.y = p[inx].y;
+ }
+ SetBoundingBox( car, hi, lo );
+
+}
+
+
+EXPORT track_p NewCar(
+ wIndex_t index,
+ carItem_p item,
+ coOrd pos,
+ ANGLE_T angle )
+{
+ track_p trk;
+ struct extraData * xx;
+
+ trk = NewTrack( index, T_CAR, 2, sizeof (*xx) );
+ /*SetEndPts( trk, 0 );*/
+ xx = GetTrkExtraData(trk);
+ /*SetTrkVisible( trk, IsVisible(xx) );*/
+ xx->item = item;
+ xx->trvTrk.pos = pos;
+ xx->trvTrk.angle = angle;
+ xx->state = 0;
+ SetCarBoundingBox( trk );
+ CarItemSetTrack( item, trk );
+ PlaceCar( trk );
+ return trk;
+}
+
+
+static void DeleteCar(
+ track_p trk )
+{
+ struct extraData * xx = GetTrkExtraData(trk);
+ CarItemSetTrack( xx->item, NULL );
+}
+
+
+static void ReadCar(
+ char * line )
+{
+ CarItemRead( line );
+}
+
+
+static BOOL_T WriteCar(
+ track_p trk,
+ FILE * f )
+{
+ BOOL_T rc = TRUE;
+ return rc;
+}
+
+
+static void MoveCar(
+ track_p car,
+ coOrd pos )
+{
+ struct extraData *xx = GetTrkExtraData(car);
+ xx->trvTrk.pos.x += pos.x;
+ xx->trvTrk.pos.y += pos.y;
+ xx->trvTrk.trk = NULL;
+ PlaceCar( car );
+ SetCarBoundingBox(car);
+}
+
+
+static void RotateCar(
+ track_p car,
+ coOrd pos,
+ ANGLE_T angle )
+{
+ struct extraData *xx = GetTrkExtraData(car);
+ Rotate( &xx->trvTrk.pos, pos, angle );
+ xx->trvTrk.angle = NormalizeAngle( xx->trvTrk.angle + angle );
+ xx->trvTrk.trk = NULL;
+ PlaceCar( car );
+ SetCarBoundingBox( car );
+}
+
+
+static BOOL_T QueryCar( track_p trk, int query )
+{
+ switch ( query ) {
+ case Q_NODRAWENDPT:
+ return TRUE;
+ default:
+ return FALSE;
+ }
+}
+
+
+static trackCmd_t carCmds = {
+ "CAR ",
+ DrawCar, /* draw */
+ DistanceCar, /* distance */
+ DescribeCar, /* describe */
+ DeleteCar, /* delete */
+ WriteCar, /* write */
+ ReadCar, /* read */
+ MoveCar, /* move */
+ RotateCar, /* rotate */
+ NULL, /* rescale */
+ NULL, /* audit */
+ NULL, /* getAngle */
+ NULL, /* split */
+ NULL, /* traverse */
+ NULL, /* enumerate */
+ NULL, /* redraw*/
+ NULL, /* trim*/
+ NULL, /* merge*/
+ NULL, /* modify */
+ NULL, /* getLength */
+ NULL, /* getParams */
+ NULL, /* moveEndPt */
+ QueryCar, /* query */
+ NULL, /* ungroup */
+ NULL, /* flip */ };
+
+/*
+ *
+ */
+
+
+static int numTrainDlg;
+
+
+#define SLIDER_WIDTH (20)
+#define SLIDER_HEIGHT (200)
+#define SLIDER_THICKNESS (10)
+#define MAX_SPEED (100.0)
+
+typedef struct {
+ wWin_p win;
+ wIndex_t inx;
+ track_p train;
+ long direction;
+ long followMe;
+ long autoReverse;
+ coOrd pos;
+ char posS[STR_SHORT_SIZE];
+ DIST_T speed;
+ char speedS[10];
+ paramGroup_p trainPGp;
+ } trainControlDlg_t, * trainControlDlg_p;
+static trainControlDlg_t * curTrainDlg;
+
+
+static void SpeedRedraw( wDraw_p, void *, wPos_t, wPos_t );
+static void SpeedAction( wAction_t, coOrd );
+static void LocoListChangeEntry( track_p, track_p );
+static void CmdTrainExit( void * );
+
+drawCmd_t speedD = {
+ NULL,
+ &screenDrawFuncs,
+ 0,
+ 1.0,
+ 0.0,
+ { 0.0, 0.0 },
+ { 0.0, 0.0 },
+ Pix2CoOrd,
+ CoOrd2Pix };
+static paramDrawData_t speedParamData = { SLIDER_WIDTH, SLIDER_HEIGHT, SpeedRedraw, SpeedAction, &speedD };
+#ifndef WINDOWS
+static paramListData_t listData = { 3, 120 };
+#endif
+static char * trainFollowMeLabels[] = { N_("Follow"), NULL };
+static char * trainAutoReverseLabels[] = { N_("Auto Reverse"), NULL };
+static paramData_t trainPLs[] = {
+#define I_LIST (0)
+#ifdef WINDOWS
+/*0*/ { PD_DROPLIST, NULL, "list", PDO_NOPREF|PDO_NOPSHUPD, (void*)120, NULL, 0 },
+#else
+/*0*/ { PD_LIST, NULL, "list", PDO_NOPREF|PDO_NOPSHUPD, &listData, NULL, 0 },
+#endif
+#define I_STATUS (1)
+ { PD_MESSAGE, NULL, NULL, 0, (void*)120 },
+#define I_POS (2)
+ { PD_MESSAGE, NULL, NULL, 0, (void*)120 },
+#define I_SLIDER (3)
+ { PD_DRAW, NULL, "speed", PDO_NOPSHUPD|PDO_DLGSETY, &speedParamData },
+#define I_DIST (4)
+ { PD_STRING, NULL, "distance", PDO_DLGNEWCOLUMN, (void*)(100-SLIDER_WIDTH), NULL, BO_READONLY },
+#define I_ZERO (5)
+ { PD_BUTTON, NULL, "zeroDistance", PDO_NOPSHUPD|PDO_NOPREF|PDO_DLGHORZ, NULL, NULL, BO_ICON },
+#define I_GOTO (6)
+ { PD_BUTTON, NULL, "goto", PDO_NOPSHUPD|PDO_NOPREF|PDO_DLGWIDE, NULL, N_("Find") },
+#define I_FOLLOW (7)
+ { PD_TOGGLE, NULL, "follow", PDO_NOPREF|PDO_DLGWIDE, trainFollowMeLabels, NULL, BC_HORZ|BC_NOBORDER },
+#define I_AUTORVRS (8)
+ { PD_TOGGLE, NULL, "autoreverse", PDO_NOPREF, trainAutoReverseLabels, NULL, BC_HORZ|BC_NOBORDER },
+#define I_DIR (9)
+ { PD_BUTTON, NULL, "direction", PDO_NOPREF|PDO_DLGWIDE, NULL, N_("Forward"), 0 },
+#define I_STOP (10)
+ { PD_BUTTON, NULL, "stop", PDO_DLGWIDE, NULL, N_("Stop") },
+#define I_SPEED (11)
+ { PD_MESSAGE, NULL, NULL, PDO_DLGIGNOREX, (void *)120 } };
+
+static paramGroup_t trainPG = { "train", 0, trainPLs, sizeof trainPLs/sizeof trainPLs[0] };
+
+
+typedef struct {
+ track_p loco;
+ BOOL_T running;
+ } locoList_t;
+dynArr_t locoList_da;
+#define locoList(N) DYNARR_N( locoList_t, locoList_da, N )
+
+static wIndex_t FindLoco(
+ track_p loco )
+{
+ wIndex_t inx;
+ for ( inx = 0; inx<locoList_da.cnt; inx++ ) {
+ if ( locoList(inx).loco == loco )
+ return inx;
+ }
+ return -1;
+}
+
+/**
+ * Update the speed display when running trains. Draw the slider in the
+ * correct position and update the odometer.
+ *
+ * \param d IN drawing area for slider
+ * \param d IN the dialog
+ * \param w, h IN unused?
+ * \return describe the return value
+ */
+
+static void SpeedRedraw(
+ wDraw_p d,
+ void * context,
+ wPos_t w,
+ wPos_t h )
+{
+ wPos_t y, pts[4][2];
+ trainControlDlg_p dlg = (trainControlDlg_p)context;
+ struct extraData * xx;
+ wDrawColor drawColor;
+
+ wDrawClear( d );
+ if ( dlg == NULL || dlg->train == NULL ) return;
+ xx = GetTrkExtraData( dlg->train );
+ if ( xx->speed > MAX_SPEED )
+ xx->speed = MAX_SPEED;
+ if ( xx->speed < 0 )
+ xx->speed = 0;
+ y = (wPos_t)(xx->speed/MAX_SPEED*((SLIDER_HEIGHT-SLIDER_THICKNESS))+SLIDER_THICKNESS/2);
+
+ drawColor = wDrawFindColor( wRGB( 160, 160, 160) );
+ pts[0][1] = pts[1][1] = y-SLIDER_THICKNESS/2;
+ pts[2][1] = pts[3][1] = y+SLIDER_THICKNESS/2;
+ pts[0][0] = pts[3][0] = 0;
+ pts[1][0] = pts[2][0] = SLIDER_WIDTH;
+ wDrawFilledPolygon( d, pts, 4, drawColor, 0 );
+
+ drawColor = wDrawFindColor( wRGB( 220, 220, 220) );
+ pts[0][1] = pts[1][1] = y+SLIDER_THICKNESS/2;
+ pts[2][1] = pts[3][1] = y;
+ pts[0][0] = pts[3][0] = 0;
+ pts[1][0] = pts[2][0] = SLIDER_WIDTH;
+ wDrawFilledPolygon( d, pts, 4, drawColor, 0 );
+
+ wDrawLine( d, 0, y, SLIDER_WIDTH, y, 1, wDrawLineSolid, drawColorRed, 0 );
+ wDrawLine( d, 0, y+SLIDER_THICKNESS/2, SLIDER_WIDTH, y+SLIDER_THICKNESS/2, 1, wDrawLineSolid, drawColorBlack, 0 );
+ wDrawLine( d, 0, y-SLIDER_THICKNESS/2, SLIDER_WIDTH, y-SLIDER_THICKNESS/2, 1, wDrawLineSolid, drawColorBlack, 0 );
+
+ sprintf( dlg->speedS, "%3d %s", (int)(units==UNITS_ENGLISH?xx->speed:xx->speed*1.6), (units==UNITS_ENGLISH?"mph":"km/h") );
+ ParamLoadMessage( dlg->trainPGp, I_SPEED, dlg->speedS );
+ LOG( log_trainPlayback, 3, ( "Speed = %d\n", (int)xx->speed ) );
+}
+
+
+static void SpeedAction(
+ wAction_t action,
+ coOrd pos )
+{
+ /*trainControlDlg_p dlg = (trainControlDlg_p)wDrawGetContext(d);*/
+ trainControlDlg_p dlg = curTrainDlg;
+ struct extraData * xx;
+ FLOAT_T speed;
+ BOOL_T startStop;
+ if ( dlg == NULL || dlg->train == NULL )
+ return;
+ xx = GetTrkExtraData( dlg->train );
+ switch ( action ) {
+ case C_DOWN:
+ InfoMessage( "" );
+ case C_MOVE:
+ case C_UP:
+ TrainTimeEndPause();
+ if ( IsOnTrack(xx) ) {
+ speed = ((FLOAT_T)((pos.y*speedD.dpi)-SLIDER_THICKNESS/2))/(SLIDER_HEIGHT-SLIDER_THICKNESS)*MAX_SPEED;
+ } else {
+ speed = 0;
+ }
+ if ( speed > MAX_SPEED )
+ speed = MAX_SPEED;
+ if ( speed < 0 )
+ speed = 0;
+ startStop = (xx->speed == 0) != (speed == 0);
+ xx->speed = speed;
+ SpeedRedraw( (wDraw_p)dlg->trainPGp->paramPtr[I_SLIDER].control, dlg, SLIDER_WIDTH, SLIDER_HEIGHT );
+ if ( startStop ) {
+ if ( xx->speed == 0 )
+ xx->status = ST_StopManual;
+ LocoListChangeEntry( dlg->train, dlg->train );
+ }
+ TrainTimeStartPause();
+ if ( trainsState == TRAINS_IDLE )
+ RestartTrains();
+ break;
+ default:
+ break;
+ }
+}
+
+
+static void ControllerDialogSync(
+ trainControlDlg_p dlg )
+{
+ struct extraData * xx=NULL;
+ wIndex_t inx;
+ BOOL_T dir;
+ BOOL_T followMe;
+ BOOL_T autoReverse;
+ DIST_T speed;
+ coOrd pos;
+ char * statusMsg;
+ long format;
+
+ if ( dlg == NULL ) return;
+
+ inx = wListGetIndex( (wList_p)dlg->trainPGp->paramPtr[I_LIST].control );
+ if ( dlg->train ) {
+ if ( inx >= 0 && inx < locoList_da.cnt && dlg->train && dlg->train != locoList(inx).loco ) {
+ inx = FindLoco( dlg->train );
+ if ( inx >= 0 ) {
+ wListSetIndex( (wList_p)dlg->trainPGp->paramPtr[I_LIST].control, inx );
+ }
+ }
+ } else {
+ wListSetIndex( (wList_p)dlg->trainPGp->paramPtr[I_LIST].control, -1 );
+ }
+
+ if ( dlg->train ) {
+ xx = GetTrkExtraData(dlg->train);
+ dir = xx->direction==0?0:1;
+ speed = xx->speed;
+ pos = xx->trvTrk.pos;
+ followMe = followTrain == dlg->train;
+ autoReverse = xx->autoReverse;
+ if ( xx->trvTrk.trk == NULL ) {
+ if ( xx->status == ST_Crashed )
+ statusMsg = _("Crashed");
+ else
+ statusMsg = _("Not on Track");
+ } else if ( xx->speed > 0 ) {
+ if ( trainsState == TRAINS_STOP )
+ statusMsg = _("Trains Paused");
+ else
+ statusMsg = _("Running");
+ } else {
+ switch (xx->status ) {
+ case ST_EndOfTrack:
+ statusMsg = _("End of Track");
+ break;
+ case ST_OpenTurnout:
+ statusMsg = _("Open Turnout");
+ break;
+ case ST_StopManual:
+ statusMsg = _("Manual Stop");
+ break;
+ case ST_NoRoom:
+ statusMsg = _("No Room");
+ break;
+ case ST_Crashed:
+ statusMsg = _("Crashed");
+ break;
+ default:
+ statusMsg = _("Unknown Status");
+ break;
+ }
+ }
+ ParamLoadMessage( dlg->trainPGp, I_STATUS, statusMsg );
+ } else {
+ dir = 0;
+ followMe = FALSE;
+ autoReverse = FALSE;
+ ParamLoadMessage( dlg->trainPGp, I_STATUS, _("No trains") );
+ }
+ if ( dlg->followMe != followMe ) {
+ dlg->followMe = followMe;
+ ParamLoadControl( dlg->trainPGp, I_FOLLOW );
+ }
+ if ( dlg->autoReverse != autoReverse ) {
+ dlg->autoReverse = autoReverse;
+ ParamLoadControl( dlg->trainPGp, I_AUTORVRS );
+ }
+ if ( dlg->direction != dir ) {
+ dlg->direction = dir;
+ wButtonSetLabel( (wButton_p)dlg->trainPGp->paramPtr[I_DIR].control, (dlg->direction?_("Reverse"):_("Forward")) );
+ }
+ if ( dlg->train ) {
+ if ( dlg->posS[0] == '\0' ||
+ dlg->pos.x != xx->trvTrk.pos.x ||
+ dlg->pos.y != xx->trvTrk.pos.y ) {
+ dlg->pos = xx->trvTrk.pos;
+ format = GetDistanceFormat();
+ format &= ~DISTFMT_DECS;
+ sprintf( dlg->posS, "X:%s Y:%s",
+ FormatDistanceEx( xx->trvTrk.pos.x, format ),
+ FormatDistanceEx( xx->trvTrk.pos.y, format ) );
+ ParamLoadMessage( dlg->trainPGp, I_POS, dlg->posS );
+ }
+ if ( dlg->speed != xx->speed ) {
+ dlg->speed = xx->speed;
+ sprintf( dlg->speedS, "%3d", (int)(units==UNITS_ENGLISH?xx->speed:xx->speed*1.6) );
+ ParamLoadMessage( dlg->trainPGp, I_SPEED, dlg->speedS );
+ SpeedRedraw( (wDraw_p)dlg->trainPGp->paramPtr[I_SLIDER].control, dlg, SLIDER_WIDTH, SLIDER_HEIGHT );
+ }
+ ParamLoadMessage( dlg->trainPGp, I_DIST, FormatDistance(xx->distance) );
+ } else {
+ if ( dlg->posS[0] != '\0' ) {
+ dlg->posS[0] = '\0';
+ ParamLoadMessage( dlg->trainPGp, I_POS, dlg->posS );
+ }
+ if ( dlg->speed >= 0 ) {
+ dlg->speed = -1;
+ dlg->speedS[0] = '\0';
+ ParamLoadMessage( dlg->trainPGp, I_SPEED, dlg->speedS );
+ wDrawClear( (wDraw_p)dlg->trainPGp->paramPtr[I_SLIDER].control );
+ }
+ ParamLoadMessage( dlg->trainPGp, I_DIST, "" );
+ }
+}
+
+
+static void ControllerDialogSyncAll( void )
+{
+ if ( curTrainDlg )
+ ControllerDialogSync( curTrainDlg );
+}
+
+
+static void LocoListChangeEntry(
+ track_p oldLoco,
+ track_p newLoco )
+{
+ wIndex_t inx = -1;
+ struct extraData * xx;
+
+ if ( curTrainDlg == NULL )
+ return;
+ if ( oldLoco && (inx=FindLoco(oldLoco))>=0 ) {
+ if ( newLoco ) {
+ xx = GetTrkExtraData(newLoco);
+ locoList(inx).loco = newLoco;
+ xx = GetTrkExtraData(newLoco);
+ locoList(inx).running = IsOnTrack(xx) && xx->speed > 0;
+ wListSetValues( (wList_p)curTrainDlg->trainPGp->paramPtr[I_LIST].control, inx, CarItemNumber(xx->item), locoList(inx).running?goI:stopI, newLoco );
+ } else {
+ wListDelete( (wList_p)curTrainDlg->trainPGp->paramPtr[I_LIST].control, inx );
+ for ( ; inx<locoList_da.cnt-1; inx++ )
+ locoList(inx) = locoList(inx+1);
+ locoList_da.cnt -= 1;
+ if ( inx >= locoList_da.cnt )
+ inx--;
+ }
+ } else if ( newLoco ){
+ inx = locoList_da.cnt;
+ DYNARR_APPEND( locoList_t, locoList_da, 10 );
+ locoList(inx).loco = newLoco;
+ xx = GetTrkExtraData(newLoco);
+ locoList(inx).running = IsOnTrack(xx) && xx->speed > 0;
+ wListAddValue( (wList_p)curTrainDlg->trainPGp->paramPtr[I_LIST].control, CarItemNumber(xx->item), locoList(inx).running?goI:stopI, newLoco );
+ }
+ if ( curTrainDlg->train == oldLoco ) {
+ if ( newLoco || locoList_da.cnt <= 0 ) {
+ curTrainDlg->train = newLoco;
+ } else {
+ curTrainDlg->train = wListGetItemContext( (wList_p)curTrainDlg->trainPGp->paramPtr[I_LIST].control, inx );
+ }
+ }
+ ControllerDialogSync( curTrainDlg );
+}
+
+
+static void LocoListInit( void )
+{
+ track_p train;
+ struct extraData * xx;
+
+ locoList_da.cnt = 0;
+ for ( train=NULL; TrackIterate( &train ); ) {
+ if ( GetTrkType(train) != T_CAR ) continue;
+ xx = GetTrkExtraData(train);
+ if ( !CarItemIsLoco(xx->item) ) continue;
+ if ( !IsLocoMaster(xx) ) continue;
+ LocoListChangeEntry( NULL, train );
+ }
+}
+
+
+#ifdef LATER
+static void LoadTrainDlgIndex(
+ trainControlDlg_p dlg )
+{
+ track_p car;
+ struct extraData * xx;
+
+ wListClear( (wList_p)dlg->trainPGp->paramPtr[I_LIST].control );
+ for ( car=NULL; TrackIterate( &car ); ) {
+ if ( GetTrkType(car) != T_CAR ) continue;
+ xx = GetTrkExtraData(car);
+ if ( !CarItemIsLoco(xx->item) ) continue;
+ if ( !IsLocoMaster(xx) ) continue;
+ wListAddValue( (wList_p)dlg->trainPGp->paramPtr[I_LIST].control, CarItemNumber(xx->item), xx->speed>0?goI:stopI, car );
+ }
+ TrainDialogSetIndex( dlg );
+ ControllerDialogSync( curTrainDlg );
+}
+#endif
+
+
+static void SetCurTrain(
+ track_p train )
+{
+ curTrainDlg->train = train;
+ ControllerDialogSync( curTrainDlg );
+}
+
+
+static void StopTrain(
+ track_p train,
+ trainStatus_e status )
+{
+ struct extraData * xx;
+
+ if ( train == NULL )
+ return;
+ xx = GetTrkExtraData(train);
+ xx->speed = 0;
+ xx->status = status;
+ LocoListChangeEntry( train, train );
+}
+
+
+static void MoveMainWindow(
+ coOrd pos,
+ ANGLE_T angle )
+{
+ DIST_T dist;
+ static DIST_T factor = 0.5;
+ ANGLE_T angle1 = angle, angle2;
+ if ( angle1 > 180.0)
+ angle1 = 360.0 - angle1;
+ if ( angle1 > 90.0)
+ angle1 = 180.0 - angle1;
+ angle2 = R2D(atan2(mainD.size.x,mainD.size.y));
+ if ( angle1 < angle2 )
+ dist = mainD.size.y/2.0/cos(D2R(angle1));
+ else
+ dist = mainD.size.x/2.0/cos(D2R(90.0-angle1));
+ dist *= factor;
+ Translate( &pos, pos, angle, dist );
+ DrawMapBoundingBox( FALSE );
+ mainCenter = pos;
+ mainD.orig.x = pos.x-mainD.size.x/2;;
+ mainD.orig.y = pos.y-mainD.size.y/2;;
+ MainRedraw();
+ DrawMapBoundingBox( TRUE );
+}
+
+
+static void SetTrainDirection(
+ track_p train )
+{
+ struct extraData *xx, *xx0=GetTrkExtraData(train);
+ int dir, dir0;
+ track_p car;
+
+ car = train;
+ for ( dir0 = 0; dir0 < 2; dir0++ ) {
+ dir = dir0;
+ WALK_CARS_START( car, xx, dir )
+ if ( car != train ) {
+ if ( CarItemIsLoco(xx->item) ) {
+ xx->direction = (dir==dir0?xx0->direction:!xx0->direction);
+ }
+ }
+ WALK_CARS_END( car, xx, dir )
+ }
+}
+
+
+static void ControllerDialogUpdate(
+ paramGroup_p pg,
+ int inx,
+ void * valueP )
+{
+ trainControlDlg_p dlg = curTrainDlg;
+ track_p train;
+ struct extraData * xx;
+
+ if ( dlg == NULL )
+ return;
+
+ TrainTimeEndPause();
+ switch (inx) {
+ case I_LIST:
+ train = (track_p)wListGetItemContext( (wList_p)pg->paramPtr[inx].control, (wIndex_t)*(long*)valueP );
+ if ( train == NULL ) return;
+ dlg->train = train;
+ ControllerDialogSync( dlg );
+ break;
+ case I_ZERO:
+ if ( dlg->train == NULL ) return;
+ TrainTimeEndPause();
+ xx = GetTrkExtraData( dlg->train );
+ xx->distance = 0.0;
+ ParamLoadMessage( dlg->trainPGp, I_DIST, FormatDistance(xx->distance) );
+ ParamLoadControl( curTrainDlg->trainPGp, I_DIST );
+ TrainTimeStartPause();
+ break;
+ case I_GOTO:
+ if ( dlg->train == NULL ) return;
+ TrainTimeEndPause();
+ xx = GetTrkExtraData( dlg->train );
+ followTrain = NULL;
+ dlg->followMe = FALSE;
+ ParamLoadControl( curTrainDlg->trainPGp, I_FOLLOW );
+ CarSetVisible( dlg->train );
+ MoveMainWindow( xx->trvTrk.pos, xx->trvTrk.angle );
+ TrainTimeStartPause();
+ break;
+ case I_FOLLOW:
+ if ( dlg->train == NULL ) return;
+ if ( *(long*)valueP ) {
+ followTrain = dlg->train;
+ xx = GetTrkExtraData(dlg->train);
+ if ( OFF_MAIND( xx->trvTrk.pos, xx->trvTrk.pos ) ) {
+ MoveMainWindow( xx->trvTrk.pos, xx->trvTrk.angle );
+ }
+ followCenter = mainCenter;
+ } else {
+ followTrain = NULL;
+ }
+ break;
+ case I_AUTORVRS:
+ if ( dlg->train == NULL ) return;
+ xx = GetTrkExtraData(dlg->train);
+ xx->autoReverse = *(long*)valueP!=0;
+ break;
+ case I_DIR:
+ if ( dlg->train == NULL ) return;
+ xx = GetTrkExtraData(dlg->train);
+ dlg->direction = xx->direction = !xx->direction;
+ wButtonSetLabel( (wButton_p)pg->paramPtr[I_DIR].control, (dlg->direction?_("Reverse"):_("Forward")) );
+ SetTrainDirection( dlg->train );
+ DrawAllCars();
+ break;
+ case I_STOP:
+ if ( dlg->train == NULL ) return;
+ TrainTimeEndPause();
+ StopTrain( dlg->train, ST_StopManual );
+ TrainTimeStartPause();
+ break;
+ case -1:
+ /* Close window */
+ CmdTrainExit( NULL );
+ break;
+ }
+ /*ControllerDialogSync( dlg );*/
+ TrainTimeStartPause();
+}
+
+
+static trainControlDlg_p CreateTrainControlDlg( void )
+{
+ trainControlDlg_p dlg;
+ char * title;
+ paramData_p PLp;
+ dlg = (trainControlDlg_p)MyMalloc( sizeof *dlg );
+#ifdef LATER
+ PLp = (paramData_p)MyMalloc( sizeof trainPLs );
+ memcpy( PLp, trainPLs, sizeof trainPLs );
+#endif
+ PLp = trainPLs;
+ dlg->posS[0] = '\0';
+ dlg->speedS[0] = '\0';
+ PLp[I_LIST].valueP = &dlg->inx;
+ PLp[I_LIST].context = dlg;
+ PLp[I_POS].valueP = &dlg->posS;
+ PLp[I_POS].context = dlg;
+ /*PLp[I_GOTO].valueP = NULL;*/
+ PLp[I_GOTO].context = dlg;
+ PLp[I_SLIDER].context = dlg;
+ PLp[I_SPEED].valueP = &dlg->speedS;
+ PLp[I_SPEED].context = dlg;
+ PLp[I_DIR].context = dlg;
+ /*PLp[I_STOP].valueP = NULL;*/
+ PLp[I_STOP].context = dlg;
+ PLp[I_FOLLOW].valueP = &dlg->followMe;
+ PLp[I_FOLLOW].context = dlg;
+ PLp[I_AUTORVRS].valueP = &dlg->autoReverse;
+ PLp[I_AUTORVRS].context = dlg;
+ title = MyStrdup( _("Train Control XXX") );
+ sprintf( title, _("Train Control %d"), ++numTrainDlg );
+ dlg->trainPGp = &trainPG;
+ dlg->win = ParamCreateDialog( dlg->trainPGp, _("Train Control"), NULL, NULL, NULL, FALSE, NULL, 0, ControllerDialogUpdate );
+ return dlg;
+}
+
+
+
+/*
+ * STATE INFO
+ */
+
+static struct {
+ STATE_T state;
+ coOrd pos0;
+ } Dtrain;
+
+
+EXPORT long trainPause = 200;
+static track_p followTrain = NULL;
+/*static int suppressTrainRedraw = 0;*/
+static long setTimeD;
+
+
+
+#ifdef MEMCHECK
+static BOOL_T drawAllCarsDisable;
+static void * top1, * top2;
+static long drawCounter;
+#endif
+static void DrawAllCars( void )
+{
+ track_p car;
+ struct extraData * xx;
+ coOrd size, lo, hi;
+ BOOL_T drawCarEnable1 = drawCarEnable;
+#ifdef MEMCHECK
+drawCounter++;
+top1 = Sbrk( 0 );
+if ( top1 != top2 ) {
+ fprintf( stderr, "incr by %ld at %ld\n", (char*)top1-(char*)top2, drawCounter );
+ top2 = top1;
+}
+#endif
+ drawCarEnable = TRUE;
+ wDrawDelayUpdate( mainD.d, TRUE );
+ wDrawRestoreImage( mainD.d );
+ DrawMarkers();
+ DrawPositionIndicators();
+ for ( car=NULL; TrackIterate(&car); ) {
+ if ( GetTrkType(car) == T_CAR ) {
+ xx = GetTrkExtraData(car);
+ CarItemSize( xx->item, &size ); /* TODO assumes xx->trvTrk.pos is the car center */
+ lo.x = xx->trvTrk.pos.x - size.x/2.0;
+ lo.y = xx->trvTrk.pos.y - size.x/2.0;
+ hi.x = lo.x + size.x;
+ hi.y = lo.y + size.x;
+ if ( !OFF_MAIND( lo, hi ) )
+ DrawCar( car, &mainD, wDrawColorBlack );
+ }
+ }
+ wDrawDelayUpdate( mainD.d, FALSE );
+ drawCarEnable = drawCarEnable1;
+}
+
+
+static DIST_T GetTrainLength2(
+ track_p * car0,
+ BOOL_T * dir )
+{
+ DIST_T length = 0, carLength;
+ struct extraData * xx;
+
+ WALK_CARS_START ( *car0, xx, *dir )
+ carLength = CarItemCoupledLength( xx->item );
+ if ( length == 0 )
+ length = carLength/2.0; /* TODO assumes xx->trvTrk.pos is the car center */
+ else
+ length += carLength;
+ WALK_CARS_END ( *car0, xx, *dir )
+ return length;
+}
+
+
+static DIST_T GetTrainLength(
+ track_p car0,
+ BOOL_T dir )
+{
+ return GetTrainLength2( &car0, &dir );
+}
+
+
+static void PlaceCar(
+ track_p car )
+{
+ struct extraData *xx = GetTrkExtraData(car);
+ DIST_T dists[2];
+ int dir;
+
+ CarItemPlace( xx->item, &xx->trvTrk, dists );
+
+ for ( dir=0; dir<2; dir++ )
+ xx->couplerPos[dir] = CarItemFindCouplerMountPoint( xx->item, xx->trvTrk, dir );
+
+ car->endPt[0].angle = xx->trvTrk.angle;
+ Translate( &car->endPt[0].pos, xx->trvTrk.pos, car->endPt[0].angle, dists[0] );
+ car->endPt[1].angle = NormalizeAngle( xx->trvTrk.angle + 180.0 );
+ Translate( &car->endPt[1].pos, xx->trvTrk.pos, car->endPt[1].angle, dists[1] );
+LOG( log_trainMove, 4, ( "%s @ [%0.3f,%0.3f] A%0.3f\n", CarItemNumber(xx->item), xx->trvTrk.pos.x, xx->trvTrk.pos.y, xx->trvTrk.angle ) )
+ SetCarBoundingBox( car );
+ xx->state &= ~(CAR_STATE_ONHIDENTRACK);
+ xx->trkLayer = NOTALAYER;
+ if ( xx->trvTrk.trk ) {
+ if ( !GetTrkVisible(xx->trvTrk.trk) )
+ xx->state |= CAR_STATE_ONHIDENTRACK;
+ xx->trkLayer = GetTrkLayer(xx->trvTrk.trk);
+ }
+}
+
+
+static track_p FindCar(
+ coOrd * pos )
+{
+ coOrd pos0, pos1;
+ track_p trk, trk1;
+ DIST_T dist1 = 100000, dist;
+ struct extraData * xx;
+
+ trk1 = NULL;
+ for ( trk=NULL; TrackIterate(&trk); ) {
+ if ( GetTrkType(trk) == T_CAR ) {
+ xx = GetTrkExtraData(trk);
+ if ( IsIgnored(xx) )
+ continue;
+ pos0 = *pos;
+ dist = DistanceCar( trk, &pos0 );
+ if ( dist < dist1 ) {
+ dist1 = dist;
+ trk1 = trk;
+ pos1 = pos0;
+ }
+ }
+ }
+ if ( dist1 < 10 ) {
+ *pos = pos1;
+ return trk1;
+ } else {
+ return NULL;
+ }
+}
+
+
+static track_p FindMasterLoco(
+ track_p train,
+ int * dirR )
+{
+ track_p car0;
+ struct extraData *xx0;
+ int dir, dir0;
+
+ for ( dir = 0; dir<2; dir++ ) {
+ car0 = train;
+ dir0 = dir;
+ WALK_CARS_START( car0, xx0, dir0 )
+ if ( CarItemIsLoco(xx0->item) && IsLocoMaster(xx0) ) {
+ if ( dirR ) *dirR = 1-dir0;
+ return car0;
+ }
+ WALK_CARS_END( car0, xx0, dir0 )
+ }
+ return NULL;
+}
+
+
+static track_p PickMasterLoco(
+ track_p car,
+ int dir )
+{
+ track_p loco=NULL;
+ struct extraData *xx;
+
+ WALK_CARS_START( car, xx, dir )
+ if ( CarItemIsLoco(xx->item) ) {
+ if ( IsLocoMaster(xx) )
+ return car;
+ if ( loco == NULL ) loco = car;
+ }
+ WALK_CARS_END( car, xx, dir )
+ if ( loco == NULL )
+ return NULL;
+ xx = GetTrkExtraData(loco);
+ SetLocoMaster(xx);
+ xx->speed = 0;
+ LOG( log_trainMove, 1, ( "%s becomes master\n", CarItemNumber(xx->item) ) )
+ return loco;
+}
+
+
+static void UncoupleCars(
+ track_p car1,
+ track_p car2 )
+{
+ struct extraData * xx1, * xx2;
+ track_p loco, loco1, loco2;
+ int dir1, dir2;
+
+ xx1 = GetTrkExtraData(car1);
+ xx2 = GetTrkExtraData(car2);
+ if ( GetTrkEndTrk(car1,0) == car2 ) {
+ dir1 = 0;
+ } else if ( GetTrkEndTrk(car1,1) == car2 ) {
+ dir1 = 1;
+ } else {
+ ErrorMessage( "uncoupleCars - not coupled" );
+ return;
+ }
+ if ( GetTrkEndTrk(car2,0) == car1 ) {
+ dir2 = 0;
+ } else if ( GetTrkEndTrk(car2,1) == car1 ) {
+ dir2 = 1;
+ } else {
+ ErrorMessage( "uncoupleCars - not coupled" );
+ return;
+ }
+ loco = FindMasterLoco( car1, NULL );
+ car1->endPt[dir1].track = NULL;
+ car2->endPt[dir2].track = NULL;
+ /*DisconnectTracks( car1, dir1, car2, dir2 );*/
+ if ( loco ) {
+ loco1 = PickMasterLoco( car1, 1-dir1 );
+ if ( loco1 != loco )
+ LocoListChangeEntry( NULL, loco1 );
+ loco2 = PickMasterLoco( car2, 1-dir2 );
+ if ( loco2 != loco )
+ LocoListChangeEntry( NULL, loco2 );
+ }
+}
+
+static void CoupleCars(
+ track_p car1,
+ int dir1,
+ track_p car2,
+ int dir2 )
+{
+ struct extraData * xx1, * xx2;
+ track_p loco1, loco2;
+ track_p car;
+ int dir;
+
+ xx1 = GetTrkExtraData(car1);
+ xx2 = GetTrkExtraData(car2);
+ if ( GetTrkEndTrk(car1,dir1) != NULL || GetTrkEndTrk(car2,dir2) != NULL ) {
+ LOG( log_trainMove, 1, ( "coupleCars - already coupled\n" ) )
+ return;
+ }
+ car = car1;
+ dir = 1-dir1;
+ WALK_CARS_START( car, xx1, dir )
+ if ( car == car2 ) {
+ LOG( log_trainMove, 1, ( "coupleCars - already coupled\n" ) )
+ ErrorMessage( "Car coupling loop" );
+ return;
+ }
+ WALK_CARS_END( car, xx1, dir )
+ car = car2;
+ dir = 1-dir2;
+ WALK_CARS_START( car, xx2, dir )
+ if ( car == car1 ) {
+ LOG( log_trainMove, 1, ( "coupleCars - already coupled\n" ) )
+ ErrorMessage( "Car coupling loop" );
+ return;
+ }
+ WALK_CARS_END( car, xx1, dir )
+ loco1 = FindMasterLoco( car1, NULL );
+ loco2 = FindMasterLoco( car2, NULL );
+ car1->endPt[dir1].track = car2;
+ car2->endPt[dir2].track = car1;
+ /*ConnectTracks( car1, dir1, car2, dir2 );*/
+if ( logTable(log_trainMove).level >= 2 ) {
+LogPrintf( "Coupling %s[%d] ", CarItemNumber(xx1->item), dir1 );
+LogPrintf( " and %s[%d]\n", CarItemNumber(xx2->item), dir2 );
+}
+ if ( ( loco1 != NULL && loco2 != NULL ) ) {
+ xx1 = GetTrkExtraData( loco1 );
+ xx2 = GetTrkExtraData( loco2 );
+ if ( xx1->speed == 0 ) {
+ ClrLocoMaster(xx1);
+ LOG( log_trainMove, 2, ( "%s loses master\n", CarItemNumber(xx1->item) ) )
+ if ( followTrain == loco1 )
+ followTrain = loco2;
+ LocoListChangeEntry( loco1, NULL );
+ loco1 = loco2;
+ } else {
+ ClrLocoMaster(xx2);
+ xx1->speed = (xx1->speed + xx2->speed)/2.0;
+ if ( xx1->speed < 0 )
+ xx1->speed = 0;
+ if ( xx1->speed > 100 )
+ xx1->speed = 100;
+ LOG( log_trainMove, 2, ( "%s loses master\n", CarItemNumber(xx2->item) ) )
+ if ( followTrain == loco2 )
+ followTrain = loco1;
+ LocoListChangeEntry( loco2, NULL );
+ }
+ SetTrainDirection( loco1 );
+ }
+}
+
+
+long crashSpeedDecay=5;
+long crashDistFactor=60;
+static void PlaceCars(
+ track_p car0,
+ int dir0,
+ long crashSpeed,
+ BOOL_T crashFlip )
+{
+ struct extraData *xx0 = GetTrkExtraData(car0), *xx;
+ int dir;
+ traverseTrack_t trvTrk;
+ DIST_T length, dist, length1;
+ track_p car_curr;
+ DIST_T flipflop = 1;
+
+ if ( crashFlip )
+ flipflop = -1;
+ dir = dir0;
+ trvTrk = xx0->trvTrk;
+ if ( dir0 )
+ FlipTraverseTrack( &trvTrk );
+ length = CarItemCoupledLength(xx0->item)/2.0;
+ car_curr = car0;
+ ClrIgnored( xx0 );
+ WALK_CARS_START ( car_curr, xx, dir )
+ if ( car_curr != car0 ) {
+ ClrIgnored( xx );
+ length1 = CarItemCoupledLength(xx->item)/2.0;
+ dist = length + length1;
+ crashSpeed = crashSpeed*crashSpeedDecay/10;
+ if ( crashSpeed > 0 )
+ dist -= dist * crashSpeed/crashDistFactor;
+ TraverseTrack2( &trvTrk, dist );
+ xx->trvTrk = trvTrk;
+ if ( crashSpeed > 0 ) {
+ xx->trvTrk.angle = NormalizeAngle( xx->trvTrk.angle + flipflop*crashSpeed );
+ xx->trvTrk.trk = NULL;
+ }
+ flipflop = -flipflop;
+ if ( dir != 0 )
+ FlipTraverseTrack( &xx->trvTrk );
+ PlaceCar( car_curr );
+ length = length1;
+ }
+ WALK_CARS_END ( car_curr, xx, dir )
+}
+
+
+static void CrashTrain(
+ track_p car,
+ int dir,
+ traverseTrack_p trvTrkP,
+ long speed,
+ BOOL_T flip )
+{
+ track_p loco;
+ struct extraData *xx;
+
+ loco = FindMasterLoco(car,NULL);
+ if ( loco != NULL ) {
+ StopTrain( loco, ST_Crashed );
+ }
+ xx = GetTrkExtraData(car);
+ xx->trvTrk = *trvTrkP;
+ if ( dir )
+ FlipTraverseTrack( &xx->trvTrk );
+ PlaceCars( car, 1-dir, speed, flip );
+ if ( flip )
+ speed = - speed;
+ xx->trvTrk.angle = NormalizeAngle( xx->trvTrk.angle - speed );
+ xx->trvTrk.trk = NULL;
+ PlaceCar( car );
+}
+
+
+static FLOAT_T couplerConnAngle = 45.0;
+static BOOL_T CheckCoupling(
+ track_p car0,
+ int dir00,
+ BOOL_T doCheckCrash )
+{
+ track_p car1, loco1;
+ struct extraData *xx0, *xx1;
+ coOrd pos1;
+ DIST_T dist0, distc, dist=100000.0;
+ int dir0, dir1, dirl;
+ ANGLE_T angle;
+ traverseTrack_t trvTrk0, trvTrk1;
+ long speed, speed0, speed1;
+
+ xx0 = xx1 = GetTrkExtraData(car0);
+ /* find length of train from loco to start and end */
+ dir0 = dir00;
+ dist0 = GetTrainLength2( &car0, &dir0 );
+
+ trvTrk0 = xx0->trvTrk;
+ if ( dir00 )
+ FlipTraverseTrack( &trvTrk0 );
+ TraverseTrack2( &trvTrk0, dist0 );
+ pos1 = trvTrk0.pos;
+ car1 = FindCar( &pos1 );
+ if ( !car1 )
+ return TRUE;
+ xx1 = GetTrkExtraData(car1);
+ if ( !IsOnTrack(xx1) )
+ return TRUE;
+ /* determine which EP of the found car to couple to */
+ angle = NormalizeAngle( trvTrk0.angle-xx1->trvTrk.angle );
+ if ( angle > 90 && angle < 270 ) {
+ dir1 = 0;
+ angle = NormalizeAngle( angle+180 );
+ } else {
+ dir1 = 1;
+ }
+ /* already coupled? */
+ if ( GetTrkEndTrk(car1,dir1) != NULL )
+ return TRUE;
+ /* are we close to aligned? */
+ if ( angle > couplerConnAngle && angle < 360.0-couplerConnAngle )
+ return TRUE;
+ /* find pos of found car's coupler, and dist btw couplers */
+ distc = CarItemCoupledLength(xx1->item);
+ Translate( &pos1, xx1->trvTrk.pos, xx1->trvTrk.angle+(dir1?180.0:0.0), distc/2.0 );
+ dist = FindDistance( trvTrk0.pos, pos1 );
+ if ( dist < trackGauge/10 )
+ return TRUE;
+ /* not real close: are we overlapped? */
+ angle = FindAngle( trvTrk0.pos, pos1 );
+ angle = NormalizeAngle( angle - trvTrk0.angle );
+ if ( angle < 90 || angle > 270 )
+ return TRUE;
+ /* are we beyond the end of the found car? */
+ if ( dist > distc )
+ return TRUE;
+ /* are we on the same track? */
+ trvTrk1 = xx1->trvTrk;
+ if ( dir1 )
+ FlipTraverseTrack( &trvTrk1 );
+ TraverseTrack2( &trvTrk1, distc/2.0-dist );
+ if ( trvTrk1.trk != trvTrk0.trk )
+ return TRUE;
+ if ( doCheckCrash ) {
+ speed0 = (long)xx0->speed;
+ if ( (xx0->direction==0) != (dir00==0) )
+ speed0 = - speed0;
+ loco1 = FindMasterLoco( car1, &dirl );
+ xx1 = NULL;
+ if ( loco1 ) {
+ xx1 = GetTrkExtraData(loco1);
+ speed1 = (long)xx1->speed;
+ if ( car1 == loco1 ) {
+ dirl = IsAligned( xx1->trvTrk.angle, FindAngle( trvTrk0.pos, xx1->trvTrk.pos ) )?1:0;
+ }
+ if ( (xx1->direction==1) != (dirl==1) )
+ speed1 = -speed1;
+ } else {
+ speed1 = 0;
+ }
+ speed = (long)labs( speed0 + speed1 );
+ LOG( log_trainMove, 2, ( "coupling speed=%ld\n", speed ) )
+ if ( speed > maxCouplingSpeed ) {
+ CrashTrain( car0, dir0, &trvTrk0, speed, FALSE );
+ CrashTrain( car1, dir1, &trvTrk1, speed, TRUE );
+ return FALSE;
+ }
+ }
+ if ( dir00 )
+ dist = -dist;
+ TraverseTrack2( &xx0->trvTrk, dist );
+ CoupleCars( car0, dir0, car1, dir1 );
+LOG( log_trainMove, 3, ( " -> %0.3f\n", dist ) )
+ return TRUE;
+}
+
+
+static void PlaceTrain(
+ track_p car0,
+ BOOL_T doCheckCrash,
+ BOOL_T doCheckCoupling )
+{
+ track_p car_curr;
+ struct extraData *xx0, *xx;
+ int dir0, dir;
+
+ xx0 = GetTrkExtraData(car0);
+
+ LOG( log_trainMove, 2, ( " placeTrain: %s [%0.3f %0.3f] A%0.3f", CarItemNumber(xx0->item), xx0->trvTrk.pos.x, xx0->trvTrk.pos.y, xx0->trvTrk.angle ) )
+
+ car_curr = car0;
+ for ( dir0=0; dir0<2; dir0++ ) {
+ car_curr = car0;
+ dir = dir0;
+ xx = xx0;
+ WALK_CARS_START( car_curr, xx, dir )
+ SetIgnored(xx);
+ WALK_CARS_END( car_curr, xx, dir );
+ }
+
+ /* check for coupling to other cars */
+ if ( doCheckCoupling ) {
+ if ( xx0->trvTrk.trk )
+ if ( !CheckCoupling( car0, 0, doCheckCrash ) )
+ return;
+ if ( xx0->trvTrk.trk )
+ if ( !CheckCoupling( car0, 1, doCheckCrash ) )
+ return;
+ }
+
+ PlaceCar( car0 );
+
+ for ( dir0=0; dir0<2; dir0++ )
+ PlaceCars( car0, dir0, 0, FALSE );
+}
+
+
+static void PlaceTrainInit(
+ track_p car0,
+ track_p trk0,
+ coOrd pos0,
+ ANGLE_T angle0,
+ BOOL_T doCheckCoupling )
+{
+ struct extraData * xx = GetTrkExtraData(car0);
+ xx->trvTrk.trk = trk0;
+ xx->trvTrk.dist = xx->trvTrk.length = -1;
+ xx->trvTrk.pos = pos0;
+ xx->trvTrk.angle = angle0;
+ PlaceTrain( car0, FALSE, doCheckCoupling );
+}
+
+
+static void FlipTrain(
+ track_p train )
+{
+ DIST_T d0, d1;
+ struct extraData * xx;
+
+ if ( train == NULL )
+ return;
+ d0 = GetTrainLength( train, 0 );
+ d1 = GetTrainLength( train, 1 );
+ xx = GetTrkExtraData(train);
+ TraverseTrack2( &xx->trvTrk, d0-d1 );
+ FlipTraverseTrack( &xx->trvTrk );
+ xx->trvTrk.length = -1;
+ PlaceTrain( train, FALSE, TRUE );
+}
+
+
+static BOOL_T MoveTrain(
+ track_p train,
+ long timeD )
+{
+ DIST_T ips, dist0, dist1;
+ struct extraData *xx, *xx1;
+ traverseTrack_t trvTrk;
+ DIST_T length;
+ track_p car1;
+ int dir1;
+ int measured; /* make sure the distance is only measured once per train */
+
+ if ( train == NULL )
+ return FALSE;
+ xx = GetTrkExtraData(train);
+ if ( xx->speed <= 0 )
+ return FALSE;
+
+ if ( setTimeD )
+ timeD = setTimeD;
+ ips = ((xx->speed*5280.0*12.0)/(60.0*60.0*GetScaleRatio(curScaleInx)));
+ dist0 = ips * timeD/1000.0;
+ length = GetTrainLength( train, xx->direction );
+ dist1 = length + dist0;
+ trvTrk = xx->trvTrk;
+ if ( trvTrk.trk == NULL ) {
+ return FALSE;
+ }
+ LOG( log_trainMove, 1, ( "moveTrain: %s t%ld->%0.3f S%0.3f D%d [%0.3f %0.3f] A%0.3f T%d\n",
+ CarItemNumber(xx->item), timeD, dist0, xx->speed, xx->direction, xx->trvTrk.pos.x, xx->trvTrk.pos.y, xx->trvTrk.angle, xx->trvTrk.trk?GetTrkIndex(xx->trvTrk.trk):-1 ) )
+ if ( xx->direction )
+ FlipTraverseTrack( &trvTrk );
+ TraverseTrack( &trvTrk, &dist1 );
+ if ( dist1 > 0.0 ) {
+ if ( dist1 > dist0 ) {
+ /*ErrorMessage( "%s no room: L%0.3f D%0.3f", CarItemNumber(xx->item), length, dist1 );*/
+ StopTrain( train, ST_NoRoom );
+ return FALSE;
+ } else {
+ dist0 -= dist1;
+ LOG( log_trainMove, 1, ( " %s STOP D%d [%0.3f %0.3f] A%0.3f D%0.3f\n",
+ CarItemNumber(xx->item), xx->direction, xx->trvTrk.pos.x, xx->trvTrk.pos.y, xx->trvTrk.angle, dist0 ) )
+ }
+ /*ErrorMessage( "%s stopped at End Of Track", CarItemNumber(xx->item) );*/
+ if ( xx->autoReverse ) {
+ xx->direction = !xx->direction;
+ SetTrainDirection( train );
+ } else {
+ if ( xx->speed > maxCouplingSpeed ) {
+ car1 = train;
+ dir1 = xx->direction;
+ GetTrainLength2( &car1, &dir1 );
+ CrashTrain( car1, dir1, &trvTrk, (long)xx->speed, FALSE );
+ return TRUE;
+ } else {
+ StopTrain( train, trvTrk.trk?ST_OpenTurnout:ST_EndOfTrack );
+ }
+ }
+ }
+ trvTrk = xx->trvTrk;
+ TraverseTrack2( &xx->trvTrk, xx->direction==0?dist0:-dist0 );
+ car1 = train;
+ dir1 = 0;
+ GetTrainLength2( &car1, &dir1 );
+ dir1 = 1-dir1;
+
+ measured = FALSE;
+ WALK_CARS_START( car1, xx1, dir1 );
+ if ( CarItemIsLoco(xx1->item) && !measured ) {
+ xx->distance += dist0;
+ measured = TRUE;
+ }
+ WALK_CARS_END( car1, xx1, dir1 );
+
+ if ( train == followTrain ) {
+ if ( followCenter.x != mainCenter.x ||
+ followCenter.y != mainCenter.y ) {
+ if ( curTrainDlg->train == followTrain ) {
+ curTrainDlg->followMe = FALSE;
+ ParamLoadControl( curTrainDlg->trainPGp, I_FOLLOW );
+ }
+ followTrain = NULL;
+ } else if ( OFF_MAIND( xx->trvTrk.pos, xx->trvTrk.pos ) ) {
+ MoveMainWindow( xx->trvTrk.pos, NormalizeAngle(xx->trvTrk.angle+(xx->direction?180.0:0.0)) );
+ followCenter = mainCenter;
+ }
+ }
+ PlaceTrain( train, TRUE, TRUE );
+ return TRUE;
+}
+
+
+static BOOL_T MoveTrains( long timeD )
+{
+ BOOL_T trains_moved = FALSE;
+ track_p train;
+ struct extraData * xx;
+
+ for ( train=NULL; TrackIterate( &train ); ) {
+ if ( GetTrkType(train) != T_CAR ) continue;
+ xx = GetTrkExtraData(train);
+ if ( !CarItemIsLoco(xx->item) ) continue;
+ if ( !IsLocoMaster(xx) ) continue;
+ if ( xx->speed == 0 ) continue;
+ trains_moved |= MoveTrain( train, timeD );
+ }
+
+ ControllerDialogSyncAll();
+
+ DrawAllCars();
+
+ return trains_moved;
+}
+
+
+static void MoveTrainsLoop( void )
+{
+ long time1, timeD;
+ static long time0 = 0;
+
+ trainsTimeoutPending = FALSE;
+ if ( trainsState != TRAINS_RUN ) {
+ time0 = 0;
+ return;
+ }
+ if ( time0 == 0 )
+ time0 = wGetTimer();
+ time1 = wGetTimer();
+ timeD = time1-time0;
+ time0 = time1;
+ if ( timeD > 1000 )
+ timeD = 1000;
+ if ( MoveTrains( timeD ) ) {
+ wAlarm( trainPause, MoveTrainsLoop );
+ trainsTimeoutPending = TRUE;
+ } else {
+ time0 = 0;
+ trainsState = TRAINS_IDLE;
+ TrainTimeEndPause();
+ }
+}
+
+
+static void RestartTrains( void )
+{
+ if ( trainsState != TRAINS_RUN )
+ TrainTimeStartPause();
+ trainsState = TRAINS_RUN;
+ if ( !trainsTimeoutPending )
+ MoveTrainsLoop();
+}
+
+
+static long trainTime0 = 0;
+static long playbackTrainPause = 0;
+static drawCmd_t trainMovieD = {
+ NULL,
+ &screenDrawFuncs,
+ 0,
+ 16.0,
+ 0,
+ {0,0}, {1,1},
+ Pix2CoOrd, CoOrd2Pix };
+static long trainMovieFrameDelay;
+static long trainMovieFrameNext;
+
+static void TrainTimeEndPause( void )
+{
+ if ( recordF ) {
+ if (trainTime0 != 0 ) {
+ long delay;
+ delay = wGetTimer()-trainTime0;
+ if ( delay > 0 )
+ fprintf( recordF, "TRAINPAUSE %ld\n", delay );
+ }
+ trainTime0 = 0;
+ }
+}
+
+static void TrainTimeStartPause( void )
+{
+ if ( trainTime0 == 0 )
+ trainTime0 = wGetTimer();
+}
+
+
+static BOOL_T TrainTimeDoPause( char * line )
+{
+ BOOL_T drawCarEnable2;
+ playbackTrainPause = atol( line );
+LOG( log_trainPlayback, 1, ( "DoPause %ld\n", playbackTrainPause ) );
+ trainsState = TRAINS_RUN;
+ if ( trainMovieFrameDelay > 0 ) {
+ drawCarEnable2 = drawCarEnable; drawCarEnable = TRUE;
+ TakeSnapshot( &trainMovieD );
+ drawCarEnable = drawCarEnable2;
+LOG( log_trainPlayback, 1, ( "SNAP 0\n" ) );
+ trainMovieFrameNext = trainMovieFrameDelay;
+ }
+ /*MoveTrains();*/
+ while ( playbackTrainPause > 0 ) {
+ if ( playbackTrainPause > trainPause ) {
+ wPause( trainPause );
+ MoveTrains( trainPause );
+ playbackTrainPause -= trainPause;
+ if ( trainMovieFrameDelay > 0 )
+ trainMovieFrameNext -= trainPause;
+ } else {
+ wPause( playbackTrainPause );
+ MoveTrains( playbackTrainPause );
+ if ( trainMovieFrameDelay > 0 )
+ trainMovieFrameNext -= playbackTrainPause;
+ playbackTrainPause = 0;
+ }
+ if ( trainMovieFrameDelay > 0 &&
+ trainMovieFrameNext <= 0 ) {
+ drawCarEnable2 = drawCarEnable; drawCarEnable = TRUE;
+ TakeSnapshot( &trainMovieD );
+ drawCarEnable = drawCarEnable2;
+LOG( log_trainPlayback, 1, ( "SNAP %ld\n", trainMovieFrameNext ) );
+ trainMovieFrameNext = trainMovieFrameDelay;
+ }
+ }
+ return TRUE;
+}
+
+
+static BOOL_T TrainDoMovie( char * line )
+{
+ /* on/off, scale, orig, size */
+ long fps;
+ if ( trainMovieD.dpi == 0 )
+ trainMovieD.dpi = mainD.dpi;
+ if ( !GetArgs( line, "lfpp", &fps, &trainMovieD.scale, &trainMovieD.orig, &trainMovieD.size ) )
+ return FALSE;
+ if ( fps > 0 ) {
+ trainMovieFrameDelay = 1000/fps;
+ } else {
+ trainMovieFrameDelay = 0;
+ }
+ trainMovieFrameNext = 0;
+ return TRUE;
+}
+
+EXPORT void AttachTrains( void )
+{
+ track_p car;
+ track_p loco;
+ struct extraData * xx;
+ coOrd pos;
+ track_p trk;
+ ANGLE_T angle;
+ EPINX_T ep0, ep1;
+ int dir;
+
+ for ( car=NULL; TrackIterate( &car ); ) {
+ ClrTrkBits( car, TB_CARATTACHED );
+ if ( GetTrkType(car) != T_CAR )
+ continue;
+ xx = GetTrkExtraData(car);
+ ClrProcessed(xx);
+ }
+ for ( car=NULL; TrackIterate( &car ); ) {
+ if ( GetTrkType(car) != T_CAR )
+ continue;
+ xx = GetTrkExtraData(car);
+ if ( IsProcessed(xx) )
+ continue;
+ loco = FindMasterLoco( car, NULL );
+ if ( loco != NULL )
+ xx = GetTrkExtraData(loco);
+ else
+ loco = car;
+ pos = xx->trvTrk.pos;
+ if ( xx->status == ST_Crashed )
+ continue;
+ TRK_ITERATE(trk) {
+ if ( trk == xx->trvTrk.trk )
+ break;
+ }
+ if ( trk!=NULL && !QueryTrack( trk, Q_ISTRACK ) )
+ trk = NULL;
+ if ( trk==NULL || GetTrkDistance(trk,pos)>trackGauge*2.0 )
+ trk = OnTrack2( &pos, FALSE, TRUE, FALSE );
+ if ( trk!=NULL ) {
+ /*if ( trk == xx->trvTrk.trk )
+ continue;*/
+ angle = GetAngleAtPoint( trk, pos, &ep0, &ep1 );
+ if ( NormalizeAngle( xx->trvTrk.angle-angle+90 ) > 180 )
+ angle = NormalizeAngle(angle+180);
+ PlaceTrainInit( loco, trk, pos, angle, TRUE );
+ } else {
+ PlaceTrainInit( loco, NULL, xx->trvTrk.pos, xx->trvTrk.angle, FALSE );
+ }
+ dir = 0;
+ WALK_CARS_START( loco, xx, dir )
+ WALK_CARS_END( loco, xx, dir )
+ dir = 1-dir;
+ WALK_CARS_START( loco, xx, dir )
+ SetProcessed(xx);
+ if ( xx->trvTrk.trk ) {
+ SetTrkBits( xx->trvTrk.trk, TB_CARATTACHED );
+ xx->status = ST_StopManual;
+ } else {
+ xx->status = ST_NotOnTrack;
+ }
+ WALK_CARS_END( loco, xx, dir )
+ }
+ for ( car=NULL; TrackIterate( &car ); ) {
+ if ( GetTrkType(car) != T_CAR )
+ continue;
+ xx = GetTrkExtraData(car);
+ ClrProcessed(xx);
+ }
+}
+
+
+static void UpdateTrainAttachment( void )
+{
+ track_p trk;
+ struct extraData * xx;
+ for ( trk=NULL; TrackIterate( &trk ); ) {
+ ClrTrkBits( trk, TB_CARATTACHED );
+ }
+ for ( trk=NULL; TrackIterate( &trk ); ) {
+ if ( GetTrkType(trk) == T_CAR ) {
+ xx = GetTrkExtraData(trk);
+ if ( xx->trvTrk.trk != NULL )
+ SetTrkBits( xx->trvTrk.trk, TB_CARATTACHED );
+ }
+ }
+}
+
+
+static BOOL_T TrainOnMovableTrack(
+ track_p trk,
+ track_p *trainR )
+{
+ track_p train;
+ struct extraData * xx;
+ int dir;
+
+ for ( train=NULL; TrackIterate(&train); ) {
+ if ( GetTrkType(train) != T_CAR )
+ continue;
+ xx = GetTrkExtraData(train);
+ if ( IsOnTrack(xx) ) {
+ if ( xx->trvTrk.trk == trk )
+ break;
+ }
+ }
+ *trainR = train;
+ if ( train == NULL ) {
+ return TRUE;
+ }
+ dir = 0;
+ WALK_CARS_START( train, xx, dir )
+ WALK_CARS_END( train, xx, dir )
+ dir = 1-dir;
+ WALK_CARS_START( train, xx, dir )
+ if ( xx->trvTrk.trk != trk ) {
+ ErrorMessage( MSG_CANT_MOVE_UNDER_TRAIN );
+ return FALSE;
+ }
+ WALK_CARS_END( train, xx, dir )
+ train = FindMasterLoco( train, NULL );
+ if ( train != NULL )
+ *trainR = train;
+ return TRUE;
+}
+
+/*
+ *
+ */
+
+#define DO_UNCOUPLE (0)
+#define DO_FLIPCAR (1)
+#define DO_FLIPTRAIN (2)
+#define DO_DELCAR (3)
+#define DO_DELTRAIN (4)
+#define DO_MUMASTER (5)
+#define DO_CHANGEDIR (6)
+#define DO_STOP (7)
+static track_p trainFuncCar;
+static coOrd trainFuncPos;
+static wButton_p trainPauseB;
+
+#ifdef LATER
+static char * newCarLabels[3] = { N_("Road"), N_("Number"), NULL };
+#endif
+
+static STATUS_T CmdTrain( wAction_t action, coOrd pos )
+{
+ track_p trk0, trk1;
+ static track_p currCar;
+ coOrd pos0, pos1;
+ static coOrd delta;
+ ANGLE_T angle1;
+ EPINX_T ep0, ep1;
+ int dir;
+ struct extraData * xx=NULL;
+ DIST_T dist;
+ wPos_t w, h;
+
+ switch (action) {
+
+ case C_START:
+ /*UndoStart( "Trains", "Trains" );*/
+ UndoSuspend();
+ programMode = MODE_TRAIN;
+ drawCarEnable = FALSE;
+ doDrawTurnoutPosition = 1;
+ DoChangeNotification( CHANGE_PARAMS|CHANGE_TOOLBAR );
+ if ( CarAvailableCount() <= 0 ) {
+ if ( NoticeMessage( MSG_NO_CARS, _("Yes"), _("No") ) > 0 ) {
+ DoCarDlg();
+ DoChangeNotification( CHANGE_PARAMS );
+ }
+ }
+ EnableCommands();
+ if ( curTrainDlg == NULL )
+ curTrainDlg = CreateTrainControlDlg();
+ curTrainDlg->train = NULL;
+#ifdef LATER
+ if ( trainW == NULL )
+ trainW = ParamCreateDialog( MakeWindowTitle(_("Train")), NULL, trainPGp );
+ ParamLoadControls( trainPGp );
+ wListClear( (wList_p)trainPLs[0].control );
+#endif
+ wListClear( (wList_p)curTrainDlg->trainPGp->paramPtr[I_LIST].control );
+ Dtrain.state = 0;
+ trk0 = NULL;
+ tempSegs_da.cnt = 0;
+ DYNARR_SET( trkSeg_t, tempSegs_da, 8 );
+ /*MainRedraw();*/
+ /*wDrawSaveImage( mainD.d );*/
+ /*trainEnable = FALSE;*/
+ RestartTrains();
+ wButtonSetLabel( trainPauseB, (char*)goI );
+ trainTime0 = 0;
+ AttachTrains();
+ DrawAllCars();
+ curTrainDlg->train = NULL;
+ curTrainDlg->speed = -1;
+ wDrawClear( (wDraw_p)curTrainDlg->trainPGp->paramPtr[I_SLIDER].control );
+ LocoListInit();
+ ControllerDialogSync( curTrainDlg );
+ wShow( curTrainDlg->win );
+ wControlShow( (wControl_p)newcarB, (toolbarSet&(1<<BG_HOTBAR)) == 0 );
+ currCarItemPtr = NULL;
+ return C_CONTINUE;
+
+ case C_TEXT:
+ if ( Dtrain.state == 0 )
+ return C_CONTINUE;
+ else
+ return C_CONTINUE;
+
+ case C_DOWN:
+ /*trainEnable = FALSE;*/
+ InfoMessage( "" );
+ if ( trainsState == TRAINS_RUN ) {
+ trainsState = TRAINS_PAUSE;
+ TrainTimeEndPause();
+ }
+ pos0 = pos;
+ if ( currCarItemPtr != NULL ) {
+#ifdef LATER
+ ParamLoadData( &newCarPG );
+#endif
+ currCar = NewCar( -1, currCarItemPtr, zero, 0.0 );
+ CarItemUpdate( currCarItemPtr );
+ HotBarCancel();
+ if ( currCar == NULL ) {
+ LOG1( log_error, ( "Train: currCar became NULL 1\n" ) )
+ return C_CONTINUE;
+ }
+ xx = GetTrkExtraData(currCar);
+ dist = CarItemCoupledLength(xx->item)/2.0;
+ Translate( &pos, xx->trvTrk.pos, xx->trvTrk.angle, dist );
+ SetTrkEndPoint( currCar, 0, pos, xx->trvTrk.angle );
+ Translate( &pos, xx->trvTrk.pos, xx->trvTrk.angle+180.0, dist );
+ SetTrkEndPoint( currCar, 1, pos, NormalizeAngle(xx->trvTrk.angle+180.0) );
+ /*xx->state |= (xx->item->options&CAR_DESC_BITS);*/
+ ClrLocoMaster(xx);
+ if ( CarItemIsLoco(xx->item) ) {
+ SetLocoMaster(xx);
+ LocoListChangeEntry( NULL, currCar );
+ if ( currCar == NULL ) {
+ LOG1( log_error, ( "Train: currCar became NULL 2\n" ) )
+ return C_CONTINUE;
+ }
+ }
+#ifdef LATER
+ wPrefSetString( "Car Road Name", xx->ITEM->title, newCarRoad );
+ number = strtol( CarItemNumber(xx->item), &cp, 10 );
+ if ( cp == NULL || *cp != 0 )
+ number = -1;
+ wPrefSetInteger( "Car Number", xx->ITEM->title, number );
+#endif
+ if( (trk0 = OnTrack( &pos0, FALSE, TRUE ) ) ) {
+ xx->trvTrk.angle = GetAngleAtPoint( trk0, pos0, &ep0, &ep1 );
+ if ( NormalizeAngle( FindAngle( pos, pos0 ) - xx->trvTrk.angle ) > 180.0 )
+ xx->trvTrk.angle = NormalizeAngle( xx->trvTrk.angle + 180 );
+ xx->status = ST_StopManual;
+ } else {
+ xx->trvTrk.angle = 90;
+ }
+ PlaceTrainInit( currCar, trk0, pos0, xx->trvTrk.angle, (MyGetKeyState()&WKEY_SHIFT) == 0 );
+ /*DrawCars( &tempD, currCar, TRUE );*/
+ } else {
+ currCar = FindCar( &pos );
+ delta.x = pos.x - pos0.x;
+ delta.y = pos.y - pos0.y;
+ if ( logTable(log_trainMove).level >= 1 ) {
+ if ( currCar ) {
+ xx = GetTrkExtraData(currCar);
+ LogPrintf( "selected %s\n", CarItemNumber(xx->item) );
+ for ( dir=0; dir<2; dir++ ) {
+ int dir1 = dir;
+ track_p car1 = currCar;
+ struct extraData * xx1 = GetTrkExtraData(car1);
+ LogPrintf( "dir=%d\n", dir1 );
+ WALK_CARS_START( car1, xx1, dir1 )
+ LogPrintf( " %s [%0.3f,%d]\n", CarItemNumber(xx1->item), xx1->trvTrk.angle, dir1 );
+ WALK_CARS_END( car1, xx1, dir1 )
+ }
+ }
+ }
+ }
+ if ( currCar == NULL )
+ return C_CONTINUE;
+ trk0 = FindMasterLoco( currCar, NULL );
+ if ( trk0 )
+ SetCurTrain( trk0 );
+ DrawAllCars();
+ return C_CONTINUE;
+
+ case C_MOVE:
+ if ( currCar == NULL )
+ return C_CONTINUE;
+ pos.x += delta.x;
+ pos.y += delta.y;
+ pos0 = pos;
+ /*DrawCars( &tempD, currCar, FALSE );*/
+ xx = GetTrkExtraData(currCar);
+ trk0 = OnTrack( &pos0, FALSE, TRUE );
+ if ( /*currCarItemPtr != NULL &&*/ trk0 ) {
+ angle1 = GetAngleAtPoint( trk0, pos0, &ep0, &ep1 );
+ if ( currCarItemPtr != NULL ) {
+ if ( NormalizeAngle( FindAngle( pos, pos0 ) - angle1 ) > 180.0 )
+ angle1 = NormalizeAngle( angle1 + 180 );
+ } else {
+ if ( NormalizeAngle( xx->trvTrk.angle - angle1 + 90.0 ) > 180.0 )
+ angle1 = NormalizeAngle( angle1 + 180 );
+ }
+ xx->trvTrk.angle = angle1;
+ }
+ tempSegs_da.cnt = 1;
+ PlaceTrainInit( currCar, trk0, pos0, xx->trvTrk.angle, (MyGetKeyState()&WKEY_SHIFT) == 0 );
+ ControllerDialogSync( curTrainDlg );
+ DrawAllCars();
+ return C_CONTINUE;
+
+
+ case C_UP:
+ if ( currCar != NULL ) {
+ trk0 = FindMasterLoco( currCar, NULL );
+ if ( trk0 ) {
+ xx = GetTrkExtraData( trk0 );
+ if ( !IsOnTrack(xx) || xx->speed <= 0 )
+ StopTrain( trk0, ST_StopManual );
+ }
+ Dtrain.state = 1;
+ /*MainRedraw();*/
+ ControllerDialogSync( curTrainDlg );
+ }
+ DrawAllCars();
+ InfoSubstituteControls( NULL, NULL );
+ currCar = trk0 = NULL;
+ currCarItemPtr = NULL;
+ /*trainEnable = TRUE;*/
+ if ( trainsState == TRAINS_PAUSE ) {
+ RestartTrains();
+ }
+ return C_CONTINUE;
+
+ case C_LCLICK:
+ if ( MyGetKeyState() & WKEY_SHIFT ) {
+ pos0 = pos;
+ programMode = MODE_DESIGN;
+ if ( (trk0=OnTrack(&pos,FALSE,TRUE)) &&
+ QueryTrack( trk0, Q_CAN_NEXT_POSITION ) &&
+ TrainOnMovableTrack( trk0, &trk1) ) {
+ if ( trk1 ) {
+ xx = GetTrkExtraData(trk1);
+ pos1 = xx->trvTrk.pos;
+ angle1 = xx->trvTrk.angle;
+ } else {
+ pos1 = pos0;
+ angle1 = 0;
+ }
+ AdvancePositionIndicator( trk0, pos0, &pos1, &angle1 );
+ if ( trk1 ) {
+ xx->trvTrk.pos = pos1;
+ xx->trvTrk.angle = angle1;
+ PlaceTrain( trk1, FALSE, TRUE );
+ DrawAllCars();
+ }
+ }
+ programMode = MODE_TRAIN;
+ trk0 = NULL;
+ MainRedraw(); //Make sure track is redrawn after switch thrown
+ } else {
+ trk0 = FindCar( &pos );
+ if ( trk0 == NULL )
+ return C_CONTINUE;
+ trk0 = FindMasterLoco( trk0, NULL );
+ if ( trk0 == NULL )
+ return C_CONTINUE;
+ SetCurTrain( trk0 );
+ }
+ return C_CONTINUE;
+
+ case C_RCLICK:
+ trainFuncPos = pos;
+ trainFuncCar = FindCar( &pos );
+ if ( trainFuncCar == NULL ||
+ GetTrkType(trainFuncCar) != T_CAR )
+ return C_CONTINUE;
+ xx = GetTrkExtraData( trainFuncCar );
+ trk0 = FindMasterLoco(trainFuncCar,NULL);
+ dir = IsAligned( xx->trvTrk.angle, FindAngle(xx->trvTrk.pos,trainFuncPos) ) ? 0 : 1;
+ wMenuPushEnable( trainPopupMI[DO_UNCOUPLE], GetTrkEndTrk( trainFuncCar, dir )!=NULL );
+ wMenuPushEnable( trainPopupMI[DO_MUMASTER], CarItemIsLoco(xx->item) && !IsLocoMaster(xx) );
+ if ( trk0 ) xx = GetTrkExtraData(trk0);
+ wMenuPushEnable( trainPopupMI[DO_CHANGEDIR], trk0!=NULL );
+ wMenuPushEnable( trainPopupMI[DO_STOP], trk0!=NULL && xx->speed>0 );
+ /*trainEnable = FALSE;*/
+#ifdef LATER
+ if ( trainsState == TRAINS_RUN )
+ trainsState = TRAINS_PAUSE;
+#endif
+ trk0 = FindMasterLoco( trainFuncCar, NULL );
+ if ( trk0 )
+ SetCurTrain( trk0 );
+ if ( !inPlayback )
+ wMenuPopupShow( trainPopupM );
+ return C_CONTINUE;
+
+ case C_REDRAW:
+#ifdef LATER
+ if (Dtrain.state == 1 && !suppressTrainRedraw) {
+ mainD.funcs->options = wDrawOptTemp;
+ mainD.funcs->options = 0;
+ }
+#endif
+ wDrawSaveImage(mainD.d);
+ DrawAllCars();
+ wWinGetSize( mainW, &w, &h );
+ w -= wControlGetPosX( newCarControls[0] ) + 4;
+ if ( w > 20 )
+ wListSetSize( (wList_p)newCarControls[0], w, wControlGetHeight( newCarControls[0] ) );
+ return C_CONTINUE;
+
+ case C_CANCEL:
+ /*trainEnable = FALSE;*/
+ trainsState = TRAINS_STOP;
+ TrainTimeEndPause();
+ LOG( log_trainMove, 1, ( "Train Cancel\n" ) )
+ Dtrain.state = 0;
+ doDrawTurnoutPosition = 0;
+ drawCarEnable = TRUE;
+ programMode = MODE_DESIGN;
+ UpdateTrainAttachment();
+ UndoResume();
+ DoChangeNotification( CHANGE_PARAMS|CHANGE_TOOLBAR );
+ if ( curTrainDlg->win )
+ wHide( curTrainDlg->win );
+ MainRedraw();
+ curTrainDlg->train = NULL;
+ return C_CONTINUE;
+
+
+ case C_CONFIRM:
+ /*trainEnable = FALSE;*/
+ if ( trainsState != TRAINS_STOP ) {
+ trainsState = TRAINS_STOP;
+ wButtonSetLabel( trainPauseB, (char*)stopI );
+ TrainTimeEndPause();
+ }
+ currCar = NULL;
+ currCarItemPtr = NULL;
+ HotBarCancel();
+ InfoSubstituteControls( NULL, NULL );
+ return C_TERMINATE;
+
+ }
+
+ return C_CONTINUE;
+
+}
+
+
+/*
+ *
+ */
+
+EXPORT STATUS_T CmdCarDescAction(
+ wAction_t action,
+ coOrd pos )
+{
+ return CmdTrain( action, pos );
+}
+
+#include "bitmaps/train.xpm"
+#include "bitmaps/exit.xpm"
+#include "bitmaps/newcar.xpm"
+#include "bitmaps/zero.xpm"
+#include "bitmaps/ballgreen.xpm"
+#include "bitmaps/ballred.xpm"
+
+
+static void CmdTrainStopGo( void * junk )
+{
+ wIcon_p icon;
+ if ( trainsState == TRAINS_STOP ) {
+ icon = goI;
+ RestartTrains();
+ } else {
+ trainsState = TRAINS_STOP;
+ icon = stopI;
+ TrainTimeEndPause();
+ }
+ ControllerDialogSync( curTrainDlg );
+ wButtonSetLabel( trainPauseB, (char*)icon );
+ if ( recordF )
+ fprintf( recordF, "TRAINSTOPGO %s\n", trainsState==TRAINS_STOP?"STOP":"GO" );
+}
+
+static BOOL_T TrainStopGoPlayback( char * line )
+{
+ while (*line && isspace(*line) ) line++;
+ if ( (strcasecmp( line, "STOP" ) == 0) != (trainsState == TRAINS_STOP) )
+ CmdTrainStopGo(NULL);
+ return TRUE;
+}
+
+
+static void CmdTrainExit( void * junk )
+{
+ Reset();
+ InfoSubstituteControls( NULL, NULL );
+ MainRedraw();
+}
+
+
+static void TrainFunc(
+ void * action )
+{
+ struct extraData * xx, *xx1;
+ ANGLE_T angle;
+ int dir;
+ track_p loco;
+ track_p temp0, temp1;
+ coOrd pos0, pos1;
+ ANGLE_T angle0, angle1;
+ EPINX_T ep0=-1, ep1=-1;
+
+ if ( trainFuncCar == NULL ) {
+ fprintf( stderr, "trainFunc: trainFuncCar==NULL\n" );
+ return;
+ }
+
+ xx = GetTrkExtraData(trainFuncCar);
+ angle = FindAngle( xx->trvTrk.pos, trainFuncPos );
+ angle = NormalizeAngle( angle-xx->trvTrk.angle );
+ dir = (angle>90&&angle<270);
+
+ switch ((int)(long)action) {
+ case DO_UNCOUPLE:
+ if ( GetTrkEndTrk(trainFuncCar,dir) )
+ UncoupleCars( trainFuncCar, GetTrkEndTrk(trainFuncCar,dir) );
+ break;
+ case DO_FLIPCAR:
+ temp0 = GetTrkEndTrk(trainFuncCar,0);
+ pos0 = GetTrkEndPos(trainFuncCar,0);
+ angle0 = GetTrkEndAngle(trainFuncCar,0);
+ temp1 = GetTrkEndTrk(trainFuncCar,1);
+ pos1 = GetTrkEndPos(trainFuncCar,1);
+ angle1 = GetTrkEndAngle(trainFuncCar,1);
+ if ( temp0 ) {
+ ep0 = GetEndPtConnectedToMe(temp0,trainFuncCar);
+ trainFuncCar->endPt[0].track = NULL;
+ temp0->endPt[ep0].track = NULL;
+ }
+ if ( temp1 ) {
+ ep1 = GetEndPtConnectedToMe(temp1,trainFuncCar);
+ trainFuncCar->endPt[1].track = NULL;
+ temp1->endPt[ep1].track = NULL;
+ }
+ xx->direction = !xx->direction;
+ FlipTraverseTrack( &xx->trvTrk );
+ SetTrkEndPoint( trainFuncCar, 0, pos1, angle1 );
+ SetTrkEndPoint( trainFuncCar, 1, pos0, angle0 );
+ if ( temp0 ) {
+ trainFuncCar->endPt[1].track = temp0;
+ temp0->endPt[ep0].track = trainFuncCar;
+ }
+ if ( temp1 ) {
+ trainFuncCar->endPt[0].track = temp1;
+ temp1->endPt[ep1].track = trainFuncCar;
+ }
+ ControllerDialogSync( curTrainDlg );
+ PlaceCar( trainFuncCar );
+ break;
+ case DO_FLIPTRAIN:
+ FlipTrain( trainFuncCar );
+ /*PlaceTrain( trainFuncCar, xx->trk, xx->trvTrk.pos, xx->trvTrk.angle );*/
+ break;
+ case DO_DELCAR:
+ for ( dir=0; dir<2; dir++ )
+ if ( GetTrkEndTrk(trainFuncCar,dir) )
+ UncoupleCars( trainFuncCar, GetTrkEndTrk(trainFuncCar,dir) );
+ if ( CarItemIsLoco(xx->item) )
+ LocoListChangeEntry( trainFuncCar, NULL );
+ trainFuncCar->deleted = TRUE;
+ /*DeleteTrack( trainFuncCar, FALSE );*/
+ CarItemUpdate( xx->item );
+ HotBarCancel();
+ InfoSubstituteControls( NULL, NULL );
+ break;
+ case DO_DELTRAIN:
+ dir = 0;
+ loco = FindMasterLoco( trainFuncCar, NULL );
+ WALK_CARS_START( trainFuncCar, xx, dir )
+ WALK_CARS_END( trainFuncCar, xx, dir )
+ dir = 1-dir;
+ temp0 = NULL;
+ WALK_CARS_START( trainFuncCar, xx, dir )
+ if ( temp0 ) {
+ xx1 = GetTrkExtraData(temp0);
+ temp0->deleted = TRUE;
+ /*DeleteTrack( temp0, FALSE );*/
+ CarItemUpdate( xx1->item );
+ }
+ temp0 = trainFuncCar;
+ WALK_CARS_END( trainFuncCar, xx, dir )
+ if ( temp0 ) {
+ xx1 = GetTrkExtraData(temp0);
+ temp0->deleted = TRUE;
+ /*DeleteTrack( temp0, FALSE );*/
+ CarItemUpdate( xx1->item );
+ }
+ if ( loco )
+ LocoListChangeEntry( loco, NULL );
+ HotBarCancel();
+ InfoSubstituteControls( NULL, NULL );
+ break;
+ case DO_MUMASTER:
+ if ( CarItemIsLoco(xx->item) ) {
+ loco = FindMasterLoco( trainFuncCar, NULL );
+ if ( loco != trainFuncCar ) {
+ SetLocoMaster(xx);
+ LOG( log_trainMove, 1, ( "%s gets master\n", CarItemNumber(xx->item) ) )
+ if ( loco ) {
+ xx1 = GetTrkExtraData( loco );
+ ClrLocoMaster(xx1);
+ LOG( log_trainMove, 1, ( "%s looses master\n", CarItemNumber(xx1->item) ) )
+ xx->speed = xx1->speed;
+ xx1->speed = 0;
+ }
+ LocoListChangeEntry( loco, trainFuncCar );
+ }
+ }
+ break;
+ case DO_CHANGEDIR:
+ loco = FindMasterLoco( trainFuncCar, NULL );
+ if ( loco ) {
+ xx = GetTrkExtraData(loco);
+ xx->direction = !xx->direction;
+ SetTrainDirection(loco);
+ ControllerDialogSync( curTrainDlg );
+ }
+ break;
+ case DO_STOP:
+ loco = FindMasterLoco( trainFuncCar, NULL );
+ if ( loco ) {
+ StopTrain( loco, ST_StopManual );
+ ControllerDialogSync( curTrainDlg );
+ }
+ break;
+ }
+ MainRedraw(); //Redraw if Train altered
+
+ if ( trainsState == TRAINS_PAUSE ) {
+ RestartTrains();
+ } else {
+ DrawAllCars();
+ }
+}
+
+
+EXPORT void InitCmdTrain( wMenu_p menu )
+{
+ log_trainMove = LogFindIndex( "trainMove" );
+ log_trainPlayback = LogFindIndex( "trainPlayback" );
+ trainPLs[I_ZERO].winLabel = (char*)wIconCreatePixMap(zero_xpm);
+ ParamRegister( &trainPG );
+ AddMenuButton( menu, CmdTrain, "cmdTrain", _("Train"), wIconCreatePixMap(train_xpm), LEVEL0_50, IC_POPUP2|IC_LCLICK|IC_RCLICK, 0, NULL );
+ stopI = wIconCreatePixMap( ballred );
+ goI = wIconCreatePixMap( ballgreen );
+ trainPauseB = AddToolbarButton( "cmdTrainPause", stopI, IC_MODETRAIN_ONLY, CmdTrainStopGo, NULL );
+ AddToolbarButton( "cmdTrainExit", wIconCreatePixMap(exit_xpm), IC_MODETRAIN_ONLY, CmdTrainExit, NULL );
+ newcarB = AddToolbarButton( "cmdTrainNewCar", wIconCreatePixMap(newcar_xpm), IC_MODETRAIN_ONLY, CarItemLoadList, NULL );
+
+ T_CAR = InitObject( &carCmds );
+
+#ifdef LATER
+ trainPGp = ParamCreateGroup( "trainW", "train", 0, trainPLs, sizeof trainPLs/sizeof trainPLs[0], NULL, 0, _("Ok"), trainOk, wHide );
+ ParamRegister( trainPGp );
+#endif
+
+ trainPopupM = MenuRegister( "Train Commands" );
+ trainPopupMI[DO_UNCOUPLE] = wMenuPushCreate( trainPopupM, "", _("Uncouple"), 0, TrainFunc, (void*)DO_UNCOUPLE );
+ trainPopupMI[DO_FLIPCAR] = wMenuPushCreate( trainPopupM, "", _("Flip Car"), 0, TrainFunc, (void*)DO_FLIPCAR );
+ trainPopupMI[DO_FLIPTRAIN] = wMenuPushCreate( trainPopupM, "", _("Flip Train"), 0, TrainFunc, (void*)DO_FLIPTRAIN );
+ trainPopupMI[DO_MUMASTER] = wMenuPushCreate( trainPopupM, "", _("MU Master"), 0, TrainFunc, (void*)DO_MUMASTER );
+ trainPopupMI[DO_CHANGEDIR] = wMenuPushCreate( trainPopupM, "", _("Change Direction"), 0, TrainFunc, (void*)DO_CHANGEDIR );
+ trainPopupMI[DO_STOP] = wMenuPushCreate( trainPopupM, "", _("Stop"), 0, TrainFunc, (void*)DO_STOP );
+ wMenuSeparatorCreate( trainPopupM );
+ trainPopupMI[DO_DELCAR] = wMenuPushCreate( trainPopupM, "", _("Remove Car"), 0, TrainFunc, (void*)DO_DELCAR );
+ trainPopupMI[DO_DELTRAIN] = wMenuPushCreate( trainPopupM, "", _("Remove Train"), 0, TrainFunc, (void*)DO_DELTRAIN );
+
+#ifdef LATER
+ ParamRegister( &newCarPG );
+ ParamCreateControls( &newCarPG, NULL );
+ newCarControls[0] = newCarPLs[0].control;
+ newCarControls[1] = newCarPLs[1].control;
+#endif
+ AddPlaybackProc( "TRAINSTOPGO", (playbackProc_p)TrainStopGoPlayback, NULL );
+ AddPlaybackProc( "TRAINPAUSE", (playbackProc_p)TrainTimeDoPause, NULL );
+ AddPlaybackProc( "TRAINMOVIE", (playbackProc_p)TrainDoMovie, NULL );
+}
+