/** \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 #endif #include #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; inxtrain == 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 ) 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<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((unsigned char)*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 ); }