diff options
Diffstat (limited to 'app/bin/ctrain.c')
-rw-r--r-- | app/bin/ctrain.c | 2586 |
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 ); +} + |