diff --git a/client/src/board/boardareawidget.cpp b/client/src/board/boardareawidget.cpp index fc199811..9805c428 100644 --- a/client/src/board/boardareawidget.cpp +++ b/client/src/board/boardareawidget.cpp @@ -518,6 +518,8 @@ void BoardAreaWidget::paintEvent(QPaintEvent* event) const TileId id = it.second.id(); const TileRotate a = it.second.rotate(); + const uint8_t state = it.second.state; + const bool isReserved = (state != 0); painter.setBrush(Qt::NoBrush); const QRectF r = drawTileRect(it.first.x - tileOriginX, it.first.y - tileOriginY, it.second.width(), it.second.height(), tileSize); @@ -526,16 +528,11 @@ void BoardAreaWidget::paintEvent(QPaintEvent* event) case TileId::RailStraight: case TileId::RailCurve45: case TileId::RailCurve90: - case TileId::RailCross45: - case TileId::RailCross90: - case TileId::RailBridge45Left: - case TileId::RailBridge45Right: - case TileId::RailBridge90: case TileId::RailBufferStop: case TileId::RailTunnel: case TileId::RailOneWay: case TileId::RailLink: - tilePainter.draw(id, r, a); + tilePainter.draw(id, r, a, isReserved); break; case TileId::RailTurnoutLeft45: @@ -548,24 +545,35 @@ void BoardAreaWidget::paintEvent(QPaintEvent* event) case TileId::RailTurnout3Way: case TileId::RailTurnoutSingleSlip: case TileId::RailTurnoutDoubleSlip: - tilePainter.drawTurnout(id, r, a, getTurnoutPosition(it.first)); + tilePainter.drawTurnout(id, r, a, static_cast(state), getTurnoutPosition(it.first)); + break; + + case TileId::RailCross45: + case TileId::RailCross90: + tilePainter.drawCross(id, r, a, static_cast(state)); + break; + + case TileId::RailBridge45Left: + case TileId::RailBridge45Right: + case TileId::RailBridge90: + tilePainter.drawBridge(id, r, a, state & 0x01, state & 0x02); break; case TileId::RailSensor: - tilePainter.drawSensor(id, r, a, getSensorState(it.first)); + tilePainter.drawSensor(id, r, a, isReserved, getSensorState(it.first)); break; case TileId::RailSignal2Aspect: case TileId::RailSignal3Aspect: - tilePainter.drawSignal(id, r, a, getSignalAspect(it.first)); + tilePainter.drawSignal(id, r, a, isReserved, getSignalAspect(it.first)); break; case TileId::RailBlock: - tilePainter.drawBlock(id, r, a, m_board.board().getTileObject(it.first)); + tilePainter.drawBlock(id, r, a, state & 0x01, state & 0x02, m_board.board().getTileObject(it.first)); break; case TileId::RailDirectionControl: - tilePainter.drawDirectionControl(id, r, a, getDirectionControlState(it.first)); + tilePainter.drawDirectionControl(id, r, a, isReserved, getDirectionControlState(it.first)); break; case TileId::PushButton: @@ -573,11 +581,11 @@ void BoardAreaWidget::paintEvent(QPaintEvent* event) break; case TileId::RailDecoupler: - tilePainter.drawRailDecoupler(r, a, getDecouplerState(it.first)); + tilePainter.drawRailDecoupler(r, a, isReserved, getDecouplerState(it.first)); break; case TileId::RailNXButton: - tilePainter.drawRailNX(r, a, getNXButtonPressed(it.first)); + tilePainter.drawRailNX(r, a, isReserved, getNXButtonPressed(it.first)); break; case TileId::None: diff --git a/client/src/board/boardcolorscheme.cpp b/client/src/board/boardcolorscheme.cpp index 9dc9d177..296c5f89 100644 --- a/client/src/board/boardcolorscheme.cpp +++ b/client/src/board/boardcolorscheme.cpp @@ -27,6 +27,8 @@ const BoardColorScheme BoardColorScheme::dark = { /*.foreground =*/ {0xFF, 0xFF, 0xFF}, /*.track =*/ {0xC0, 0xC0, 0xC0}, /*.trackDisabled =*/ {0x40, 0x40, 0x40}, + /*.trackReserved =*/ {Qt::yellow}, + /*.trackReservedDisabled =*/ {0x99, 0x99, 0x00}, /*.blockFree =*/ {0x66, 0xC6, 0x66}, /*.blockReserved =*/ {Qt::yellow}, /*.blockOccupied =*/ {0xC6, 0x66, 0x66}, @@ -47,6 +49,8 @@ const BoardColorScheme BoardColorScheme::light = { /*.foreground =*/ {0x00, 0x00, 0x00}, /*.track =*/ {0x00, 0x00, 0x00}, /*.trackDisabled =*/ {0xA0, 0xA0, 0xA0}, + /*.trackReserved =*/ {Qt::yellow}, + /*.trackReservedDisabled =*/ {0x99, 0x99, 0x00}, /*.blockFree =*/ {0x44, 0xC6, 0x44}, /*.blockReserved =*/ {Qt::yellow}, /*.blockOccupied =*/ {0xC6, 0x44, 0x44}, diff --git a/client/src/board/boardcolorscheme.hpp b/client/src/board/boardcolorscheme.hpp index 5a5b8566..a0e19c5a 100644 --- a/client/src/board/boardcolorscheme.hpp +++ b/client/src/board/boardcolorscheme.hpp @@ -34,6 +34,8 @@ struct BoardColorScheme const QColor foreground; const QColor track; const QColor trackDisabled; + const QColor trackReserved; + const QColor trackReservedDisabled; const QColor blockFree; const QColor blockReserved; const QColor blockOccupied; diff --git a/client/src/board/boardwidget.cpp b/client/src/board/boardwidget.cpp index 6e364ede..46ba63bc 100644 --- a/client/src/board/boardwidget.cpp +++ b/client/src/board/boardwidget.cpp @@ -695,11 +695,11 @@ void BoardWidget::tileClicked(int16_t x, int16_t y) image.fill(Qt::transparent); if(isRailTurnout(tileId)) - tilePainter.drawTurnout(tileId, image.rect(), tileRotate, static_cast(n)); + tilePainter.drawTurnout(tileId, image.rect(), tileRotate, TurnoutPosition::Unknown, static_cast(n)); else if(isRailSignal(tileId)) - tilePainter.drawSignal(tileId, image.rect(), tileRotate, static_cast(n)); + tilePainter.drawSignal(tileId, image.rect(), tileRotate, false, static_cast(n)); else if(tileId == TileId::RailDirectionControl) - tilePainter.drawDirectionControl(tileId, image.rect(), tileRotate, static_cast(n)); + tilePainter.drawDirectionControl(tileId, image.rect(), tileRotate, false, static_cast(n)); connect(menu.addAction(QIcon(QPixmap::fromImage(image)), translateEnum(value->enumName(), n)), &QAction::triggered, [this, setValue, n]() diff --git a/client/src/board/tilepainter.cpp b/client/src/board/tilepainter.cpp index 5f11d738..e5c3e66b 100644 --- a/client/src/board/tilepainter.cpp +++ b/client/src/board/tilepainter.cpp @@ -42,68 +42,42 @@ TilePainter::TilePainter(QPainter& painter, int tileSize, const BoardColorScheme m_blockPen{m_colorScheme.track}, m_trackPen(m_colorScheme.track, m_trackWidth, Qt::SolidLine, Qt::FlatCap), m_trackDisabledPen(m_colorScheme.trackDisabled, m_trackWidth, Qt::SolidLine, Qt::FlatCap), + m_trackReservedPen(m_colorScheme.trackReserved, m_trackWidth, Qt::SolidLine, Qt::FlatCap), + m_trackReservedDisabledPen(m_colorScheme.trackReservedDisabled, m_trackWidth, Qt::SolidLine, Qt::FlatCap), m_trackErasePen(m_colorScheme.background, m_trackWidth * 2, Qt::SolidLine, Qt::FlatCap), m_turnoutStatePen(m_colorScheme.turnoutState, (m_trackWidth + 1) / 2, Qt::SolidLine, Qt::FlatCap), m_painter{painter} { } -void TilePainter::draw(TileId id, const QRectF& r, TileRotate rotate) +void TilePainter::draw(TileId id, const QRectF& r, TileRotate rotate, bool isReserved) { switch(id) { case TileId::RailStraight: - setTrackPen(); + setTrackPen(isReserved); drawStraight(r, rotate); break; case TileId::RailCurve45: - setTrackPen(); + setTrackPen(isReserved); drawCurve45(r, rotate); break; case TileId::RailCurve90: - setTrackPen(); + setTrackPen(isReserved); drawCurve90(r, rotate); break; case TileId::RailCross45: - setTrackPen(); - drawStraight(r, rotate); - drawStraight(r, rotate - TileRotate::Deg45); - break; - case TileId::RailCross90: - setTrackPen(); - drawStraight(r, rotate); - drawStraight(r, rotate + TileRotate::Deg90); + drawCross(id, r, rotate); break; case TileId::RailBridge45Left: - setTrackPen(); - drawStraight(r, rotate); - setTrackErasePen(); - drawStraight(r, rotate - TileRotate::Deg45); - setTrackPen(); - drawStraight(r, rotate - TileRotate::Deg45); - break; - case TileId::RailBridge45Right: - setTrackPen(); - drawStraight(r, rotate); - setTrackErasePen(); - drawStraight(r, rotate + TileRotate::Deg45); - setTrackPen(); - drawStraight(r, rotate + TileRotate::Deg45); - break; - case TileId::RailBridge90: - setTrackPen(); - drawStraight(r, rotate); - setTrackErasePen(); - drawStraight(r, rotate + TileRotate::Deg90); - setTrackPen(); - drawStraight(r, rotate + TileRotate::Deg90); + drawBridge(id, r, rotate); break; case TileId::RailTurnoutLeft45: @@ -124,13 +98,13 @@ void TilePainter::draw(TileId id, const QRectF& r, TileRotate rotate) break; case TileId::RailBufferStop: - setTrackPen(); + setTrackPen(isReserved); drawBufferStop(r, rotate); break; case TileId::RailSignal2Aspect: case TileId::RailSignal3Aspect: - drawSignal(id, r, rotate); + drawSignal(id, r, rotate, isReserved); break; case TileId::RailBlock: @@ -139,7 +113,7 @@ void TilePainter::draw(TileId id, const QRectF& r, TileRotate rotate) case TileId::RailTunnel: { - setTrackPen(); + setTrackPen(isReserved); drawStraight(r, rotate); // tunnel arc: @@ -155,14 +129,14 @@ void TilePainter::draw(TileId id, const QRectF& r, TileRotate rotate) break; } case TileId::RailDirectionControl: - drawDirectionControl(id, r, rotate); + drawDirectionControl(id, r, rotate, isReserved); break; case TileId::RailOneWay: { - setTrackPen(); + setTrackPen(isReserved); drawStraight(r, rotate); - m_painter.setBrush(m_trackPen.color()); + m_painter.setBrush(m_painter.pen().color()); const qreal m = r.width() / 4; QRectF rTriangle = r.adjusted(m, m, -m, -m); @@ -182,16 +156,16 @@ void TilePainter::draw(TileId id, const QRectF& r, TileRotate rotate) break; case TileId::RailLink: - setTrackPen(); + setTrackPen(isReserved); drawLink(r, rotate); break; case TileId::RailDecoupler: - drawRailDecoupler(r, rotate); + drawRailDecoupler(r, rotate, isReserved); break; case TileId::RailNXButton: - drawRailNX(r, rotate); + drawRailNX(r, rotate, isReserved); break; case TileId::None: @@ -200,13 +174,118 @@ void TilePainter::draw(TileId id, const QRectF& r, TileRotate rotate) } } -void TilePainter::drawSensor(TileId id, const QRectF& r, TileRotate rotate, SensorState state) +void TilePainter::drawBridge(TileId id, const QRectF& r, TileRotate rotate, bool isReservedAC, bool isReservedBD) +{ + switch(id) + { + case TileId::RailBridge45Left: + setTrackPen(isReservedAC); + drawStraight(r, rotate); + setTrackErasePen(); + drawStraight(r, rotate - TileRotate::Deg45); + setTrackPen(isReservedBD); + drawStraight(r, rotate - TileRotate::Deg45); + break; + + case TileId::RailBridge45Right: + setTrackPen(isReservedAC); + drawStraight(r, rotate); + setTrackErasePen(); + drawStraight(r, rotate + TileRotate::Deg45); + setTrackPen(isReservedBD); + drawStraight(r, rotate + TileRotate::Deg45); + break; + + case TileId::RailBridge90: + setTrackPen(isReservedAC); + drawStraight(r, rotate); + setTrackErasePen(); + drawStraight(r, rotate + TileRotate::Deg90); + setTrackPen(isReservedBD); + drawStraight(r, rotate + TileRotate::Deg90); + break; + + default: + break; + } +} + +void TilePainter::drawCross(TileId id, const QRectF& r, TileRotate rotate, CrossState reservedState) +{ + switch(id) + { + case TileId::RailCross45: + setTrackPen(); + if(reservedState != CrossState::AC) + { + drawStraight(r, rotate); + } + if(reservedState != CrossState::BD) + { + drawStraight(r, rotate - TileRotate::Deg45); + } + + if(reservedState != CrossState::Unset) + { + setTrackPen(true); + switch(reservedState) + { + case CrossState::AC: + drawStraight(r, rotate); + break; + + case CrossState::BD: + drawStraight(r, rotate - TileRotate::Deg45); + break; + + default: + break; + } + } + break; + + case TileId::RailCross90: + setTrackPen(); + if(reservedState != CrossState::AC) + { + drawStraight(r, rotate); + } + if(reservedState != CrossState::BD) + { + drawStraight(r, rotate - TileRotate::Deg90); + } + + if(reservedState != CrossState::Unset) + { + setTrackPen(true); + switch(reservedState) + { + case CrossState::AC: + drawStraight(r, rotate); + break; + + case CrossState::BD: + drawStraight(r, rotate - TileRotate::Deg90); + break; + + default: + break; + } + } + break; + + default: + break; + } +} + +void TilePainter::drawSensor(TileId id, const QRectF& r, TileRotate rotate, bool isReserved, SensorState state) { switch(id) { case TileId::RailSensor: { - setTrackPen(); + setTrackPen(isReserved); drawStraight(r, rotate); const qreal sz = r.width() / 4; drawLED(r.adjusted(sz, sz, -sz, -sz), sensorStateToColor(state), m_colorScheme.track); @@ -217,13 +296,13 @@ void TilePainter::drawSensor(TileId id, const QRectF& r, TileRotate rotate, Sens } } -void TilePainter::drawDirectionControl(TileId id, const QRectF& r, TileRotate rotate, DirectionControlState state) +void TilePainter::drawDirectionControl(TileId id, const QRectF& r, TileRotate rotate, bool isReserved, DirectionControlState state) { switch(id) { case TileId::RailDirectionControl: { - setTrackPen(); + setTrackPen(isReserved); drawStraight(r, rotate); QPen pen{m_trackPen}; @@ -286,253 +365,24 @@ void TilePainter::drawDirectionControl(TileId id, const QRectF& r, TileRotate ro } } -void TilePainter::drawTurnout(TileId id, const QRectF& r, TileRotate rotate, TurnoutPosition position) +void TilePainter::drawTurnout(TileId id, const QRectF& r, TileRotate rotate, TurnoutPosition reservedPosition, TurnoutPosition position) { switch(id) { case TileId::RailTurnoutLeft45: - setTurnoutPen(); - drawStraight(r, rotate); - drawCurve45(r, rotate); - - setTurnoutStatePen(); - switch(position) - { - case TurnoutPosition::Straight: - drawStraight(turnoutStateRect(r), rotate); - break; - - case TurnoutPosition::Left: - drawCurve45(turnoutStateRect(r), rotate); - break; - - default: - break; - } - break; - case TileId::RailTurnoutLeft90: - setTurnoutPen(); - drawStraight(r, rotate); - drawCurve90(r, rotate); - - setTurnoutStatePen(); - switch(position) - { - case TurnoutPosition::Straight: - drawStraight(turnoutStateRect(r), rotate); - break; - - case TurnoutPosition::Left: - drawCurve90(turnoutStateRect(r), rotate); - break; - - default: - break; - } - break; - case TileId::RailTurnoutLeftCurved: - setTurnoutPen(); - drawCurve45(r, rotate); - drawCurve90(r, rotate); - - setTurnoutStatePen(); - switch(position) - { - case TurnoutPosition::Straight: - drawCurve45(turnoutStateRect(r), rotate); - break; - - case TurnoutPosition::Left: - drawCurve90(turnoutStateRect(r), rotate); - break; - - default: - break; - } - break; - case TileId::RailTurnoutRight45: - setTurnoutPen(); - drawStraight(r, rotate); - drawCurve45(r, rotate + TileRotate::Deg225); - - setTurnoutStatePen(); - switch(position) - { - case TurnoutPosition::Straight: - drawStraight(turnoutStateRect(r), rotate); - break; - - case TurnoutPosition::Right: - drawCurve45(turnoutStateRect(r), rotate + TileRotate::Deg225); - break; - - default: - break; - } - break; - case TileId::RailTurnoutRight90: - setTurnoutPen(); - drawStraight(r, rotate); - drawCurve90(r, rotate + TileRotate::Deg270); - - setTurnoutStatePen(); - switch(position) - { - case TurnoutPosition::Straight: - drawStraight(turnoutStateRect(r), rotate); - break; - - case TurnoutPosition::Right: - drawCurve90(turnoutStateRect(r), rotate + TileRotate::Deg270); - break; - - default: - break; - } - break; - case TileId::RailTurnoutRightCurved: - setTurnoutPen(); - drawCurve45(r, rotate + TileRotate::Deg225); - drawCurve90(r, rotate + TileRotate::Deg270); - - setTurnoutStatePen(); - switch(position) - { - case TurnoutPosition::Straight: - drawCurve45(turnoutStateRect(r), rotate + TileRotate::Deg225); - break; - - case TurnoutPosition::Right: - drawCurve90(turnoutStateRect(r), rotate + TileRotate::Deg270); - break; - - default: - break; - } - break; - case TileId::RailTurnoutWye: - setTurnoutPen(); - drawCurve45(r, rotate); - drawCurve45(r, rotate + TileRotate::Deg225); - - setTurnoutStatePen(); - switch(position) - { - case TurnoutPosition::Left: - drawCurve45(turnoutStateRect(r), rotate); - break; - - case TurnoutPosition::Right: - drawCurve45(turnoutStateRect(r), rotate + TileRotate::Deg225); - break; - - default: - break; - } - break; - case TileId::RailTurnout3Way: - setTurnoutPen(); - drawStraight(r, rotate); - drawCurve45(r, rotate); - drawCurve45(r, rotate + TileRotate::Deg225); - - setTurnoutStatePen(); - switch(position) - { - case TurnoutPosition::Straight: - drawStraight(turnoutStateRect(r), rotate); - break; - - case TurnoutPosition::Left: - drawCurve45(turnoutStateRect(r), rotate); - break; - - case TurnoutPosition::Right: - drawCurve45(turnoutStateRect(r), rotate + TileRotate::Deg225); - break; - - default: - break; - } + drawTurnoutStandard(id, r, rotate, reservedPosition, position); break; case TileId::RailTurnoutSingleSlip: - setTurnoutPen(); - drawStraight(r, rotate); - drawStraight(r, rotate - TileRotate::Deg45); - drawCurve45(r, rotate); - - setTurnoutStatePen(); - switch(position) - { - case TurnoutPosition::Crossed: - drawStraight(turnoutStateRect(r), rotate); - drawStraight(turnoutStateRect(r), rotate - TileRotate::Deg45); - break; - - case TurnoutPosition::Diverged: - drawCurve45(turnoutStateRect(r), rotate); - break; - - case TurnoutPosition::DoubleSlipStraightA: - drawStraight(turnoutStateRect(r), rotate); - break; - - case TurnoutPosition::DoubleSlipStraightB: - drawStraight(turnoutStateRect(r), rotate - TileRotate::Deg45); - break; - - default: - break; - } - break; - case TileId::RailTurnoutDoubleSlip: - setTurnoutPen(); - drawStraight(r, rotate); - drawStraight(r, rotate - TileRotate::Deg45); - drawCurve45(r, rotate); - drawCurve45(r, rotate + TileRotate::Deg180); - - setTurnoutStatePen(); - switch(position) - { - case TurnoutPosition::Left: - drawCurve45(turnoutStateRect(r), rotate); - break; - - case TurnoutPosition::Right: - drawCurve45(turnoutStateRect(r), rotate + TileRotate::Deg180); - break; - - case TurnoutPosition::Crossed: - drawStraight(turnoutStateRect(r), rotate); - drawStraight(turnoutStateRect(r), rotate - TileRotate::Deg45); - break; - - case TurnoutPosition::Diverged: - drawCurve45(turnoutStateRect(r), rotate); - drawCurve45(turnoutStateRect(r), rotate + TileRotate::Deg180); - break; - - case TurnoutPosition::DoubleSlipStraightA: - drawStraight(turnoutStateRect(r), rotate); - break; - - case TurnoutPosition::DoubleSlipStraightB: - drawStraight(turnoutStateRect(r), rotate - TileRotate::Deg45); - break; - - default: - break; - } + drawTurnoutSlip(id, r, rotate, reservedPosition, position); break; default: @@ -541,19 +391,19 @@ void TilePainter::drawTurnout(TileId id, const QRectF& r, TileRotate rotate, Tur } } -void TilePainter::drawSignal(TileId id, const QRectF& r, TileRotate rotate, SignalAspect aspect) +void TilePainter::drawSignal(TileId id, const QRectF& r, TileRotate rotate, bool isReserved, SignalAspect aspect) { switch(id) { case TileId::RailSignal2Aspect: - setTrackPen(); + setTrackPen(isReserved); drawStraight(r, rotate); drawSignalDirection(r, rotate); drawSignal2Aspect(r, rotate, aspect); break; case TileId::RailSignal3Aspect: - setTrackPen(); + setTrackPen(isReserved); drawStraight(r, rotate); drawSignalDirection(r, rotate); drawSignal3Aspect(r, rotate, aspect); @@ -565,12 +415,12 @@ void TilePainter::drawSignal(TileId id, const QRectF& r, TileRotate rotate, Sign } } -void TilePainter::drawBlock(TileId id, const QRectF& r, TileRotate rotate, const ObjectPtr& blockTile) +void TilePainter::drawBlock(TileId id, const QRectF& r, TileRotate rotate, bool isReservedA, bool isReservedB, const ObjectPtr& blockTile) { switch(id) { case TileId::RailBlock: - drawRailBlock(r, rotate, blockTile); + drawRailBlock(r, rotate, isReservedA, isReservedB, blockTile); break; default: @@ -922,6 +772,302 @@ void TilePainter::drawLED(const QRectF& r, const QColor& color, const QColor& bo m_painter.drawEllipse(r); } +void TilePainter::drawTurnoutStandard(TileId id, const QRectF& r, TileRotate rotate, TurnoutPosition reservedPosition, TurnoutPosition position) +{ + const bool hasLeft90 = + id == TileId::RailTurnoutLeft90 || + id == TileId::RailTurnoutLeftCurved; + + const bool hasLeft = + hasLeft90 || + id == TileId::RailTurnoutLeft45 || + id == TileId::RailTurnoutWye || + id == TileId::RailTurnout3Way; + + const bool hasStraight = + id == TileId::RailTurnoutLeft45 || + id == TileId::RailTurnoutLeft90 || + id == TileId::RailTurnoutLeftCurved || + id == TileId::RailTurnoutRight45 || + id == TileId::RailTurnoutRight90 || + id == TileId::RailTurnoutRightCurved || + id == TileId::RailTurnout3Way; + + const bool hasRight90 = + id == TileId::RailTurnoutRight90 || + id == TileId::RailTurnoutRightCurved; + + const bool hasRight = + hasRight90 || + id == TileId::RailTurnoutRight45 || + id == TileId::RailTurnoutWye || + id == TileId::RailTurnout3Way; + + setTurnoutPen(); + if(hasLeft && (m_turnoutDrawState || position != TurnoutPosition::Left) && reservedPosition != TurnoutPosition::Left) + { + if(hasLeft90) + { + drawCurve90(r, rotate); + } + else + { + drawCurve45(r, rotate); + } + } + if(hasStraight && (m_turnoutDrawState || position != TurnoutPosition::Straight) && reservedPosition != TurnoutPosition::Straight) + { + if(id == TileId::RailTurnoutLeftCurved) + { + drawCurve45(r, rotate); + } + else if(id == TileId::RailTurnoutRightCurved) + { + drawCurve45(r, rotate + TileRotate::Deg225); + } + else + { + drawStraight(r, rotate); + } + } + if(hasRight && (m_turnoutDrawState || position != TurnoutPosition::Right) && reservedPosition != TurnoutPosition::Right) + { + if(hasRight90) + { + drawCurve90(r, rotate + TileRotate::Deg270); + } + else + { + drawCurve45(r, rotate + TileRotate::Deg225); + } + } + + if(reservedPosition != TurnoutPosition::Unknown) + { + m_painter.setPen(position == reservedPosition ? m_trackReservedPen : m_trackReservedDisabledPen); + + switch(reservedPosition) + { + case TurnoutPosition::Left: + if(hasLeft90) + { + drawCurve90(r, rotate); + } + else + { + drawCurve45(r, rotate); + } + break; + + case TurnoutPosition::Straight: + if(id == TileId::RailTurnoutLeftCurved) + { + drawCurve45(r, rotate); + } + else if(id == TileId::RailTurnoutRightCurved) + { + drawCurve45(r, rotate + TileRotate::Deg225); + } + else + { + drawStraight(r, rotate); + } + break; + + case TurnoutPosition::Right: + if(hasRight90) + { + drawCurve90(r, rotate + TileRotate::Deg270); + } + else + { + drawCurve45(r, rotate + TileRotate::Deg225); + } + break; + + default: + break; + } + } + + if(m_turnoutDrawState || position != reservedPosition) + { + setTurnoutStatePen(); + switch(position) + { + case TurnoutPosition::Left: + if(hasLeft90) + { + drawCurve90(turnoutStateRect(r), rotate); + } + else + { + drawCurve45(turnoutStateRect(r), rotate); + } + break; + + case TurnoutPosition::Straight: + if(id == TileId::RailTurnoutLeftCurved) + { + drawCurve45(turnoutStateRect(r), rotate); + } + else if(id == TileId::RailTurnoutRightCurved) + { + drawCurve45(turnoutStateRect(r), rotate + TileRotate::Deg225); + } + else + { + drawStraight(turnoutStateRect(r), rotate); + } + break; + + case TurnoutPosition::Right: + if(hasRight90) + { + drawCurve90(turnoutStateRect(r), rotate + TileRotate::Deg270); + } + else + { + drawCurve45(turnoutStateRect(r), rotate + TileRotate::Deg225); + } + break; + + default: + break; + } + } +} + +void TilePainter::drawTurnoutSlip(TileId id, const QRectF& r, TileRotate rotate, TurnoutPosition reservedPosition, TurnoutPosition position) +{ + // Double: Single: + // C C + // |\ | + // B --+-- D B --+-- D + // \| \| + // A A + + const bool isDoubleSlip = id == TileId::RailTurnoutDoubleSlip; + + const bool positionAB = position == TurnoutPosition::Left || position == TurnoutPosition::Diverged; + const bool positionAC = position == TurnoutPosition::DoubleSlipStraightA || position == TurnoutPosition::Crossed; + const bool positionBD = position == TurnoutPosition::DoubleSlipStraightB || position == TurnoutPosition::Crossed; + const bool positionCD = isDoubleSlip && (position == TurnoutPosition::Right || position == TurnoutPosition::Diverged); + + const bool reservedPositionAB = reservedPosition == TurnoutPosition::Left || reservedPosition == TurnoutPosition::Diverged; + const bool reservedPositionAC = reservedPosition == TurnoutPosition::DoubleSlipStraightA || reservedPosition == TurnoutPosition::Crossed; + const bool reservedPositionBD = reservedPosition == TurnoutPosition::DoubleSlipStraightB || reservedPosition == TurnoutPosition::Crossed; + const bool reservedPositionCD = isDoubleSlip && (reservedPosition == TurnoutPosition::Right || reservedPosition == TurnoutPosition::Diverged); + + setTurnoutPen(); + if((m_turnoutDrawState || !positionAB) && !reservedPositionAB) + { + drawCurve45(r, rotate); + } + if((m_turnoutDrawState || !positionAC) && !reservedPositionAC) + { + drawStraight(r, rotate); + } + if((m_turnoutDrawState || !positionBD) && !reservedPositionBD) + { + drawStraight(r, rotate - TileRotate::Deg45); + } + if(isDoubleSlip && (m_turnoutDrawState || !positionCD) && !reservedPositionCD) + { + drawCurve45(r, rotate + TileRotate::Deg180); + } + + if(!m_turnoutDrawState) + { + setTrackPen(); + if(position == TurnoutPosition::Crossed) + { + if(!reservedPositionAC) + { + drawStraight(r, rotate); + } + if(!reservedPositionBD) + { + drawStraight(r, rotate - TileRotate::Deg45); + } + } + else if(position == TurnoutPosition::Diverged) + { + if(!reservedPositionAB) + { + drawCurve45(r, rotate); + } + if(isDoubleSlip && !reservedPositionCD) + { + drawCurve45(r, rotate + TileRotate::Deg180); + } + } + } + + if(reservedPosition != TurnoutPosition::Unknown) + { + if(reservedPositionAB && !positionAB) + { + m_painter.setPen(m_trackReservedDisabledPen); + drawCurve45(r, rotate); + } + if(reservedPositionAC && !positionAC) + { + m_painter.setPen(m_trackReservedDisabledPen); + drawStraight(r, rotate); + } + if(reservedPositionBD && !positionBD) + { + m_painter.setPen(m_trackReservedDisabledPen); + drawStraight(r, rotate - TileRotate::Deg45); + } + if(reservedPositionCD && !positionCD) + { + m_painter.setPen(m_trackReservedDisabledPen); + drawCurve45(r, rotate + TileRotate::Deg180); + } + + if(reservedPositionAB && positionAB) + { + m_painter.setPen(m_trackReservedPen); + drawCurve45(r, rotate); + } + if(reservedPositionAC && positionAC) + { + m_painter.setPen(m_trackReservedPen); + drawStraight(r, rotate); + } + if(reservedPositionBD && positionBD) + { + m_painter.setPen(m_trackReservedPen); + drawStraight(r, rotate - TileRotate::Deg45); + } + if(reservedPositionCD && positionCD) + { + m_painter.setPen(m_trackReservedPen); + drawCurve45(r, rotate + TileRotate::Deg180); + } + } + + setTurnoutStatePen(); + if(positionAB && (m_turnoutDrawState || reservedPositionAC || reservedPositionBD || (!reservedPositionAB && position != TurnoutPosition::Diverged))) + { + drawCurve45(turnoutStateRect(r), rotate); + } + if(positionAC && (m_turnoutDrawState || reservedPositionAB || reservedPositionCD || (!reservedPositionAC && position != TurnoutPosition::Crossed))) + { + drawStraight(turnoutStateRect(r), rotate); + } + if(positionBD && (m_turnoutDrawState || reservedPositionAB || reservedPositionCD || (!reservedPositionBD && position != TurnoutPosition::Crossed))) + { + drawStraight(turnoutStateRect(r), rotate - TileRotate::Deg45); + } + if(positionCD && (m_turnoutDrawState || reservedPositionAC || reservedPositionBD || (!reservedPositionCD && position != TurnoutPosition::Diverged))) + { + drawCurve45(turnoutStateRect(r), rotate + TileRotate::Deg180); + } +} + void TilePainter::drawSignal3Aspect(QRectF r, TileRotate rotate, SignalAspect aspect) { m_painter.save(); @@ -999,7 +1145,7 @@ void TilePainter::drawSignalDirection(QRectF r, TileRotate rotate) m_painter.restore(); } -void TilePainter::drawRailBlock(const QRectF& r, TileRotate rotate, const ObjectPtr& blockTile) +void TilePainter::drawRailBlock(const QRectF& r, TileRotate rotate, bool isReservedA, bool isReservedB, const ObjectPtr& blockTile) { const BlockState state = blockTile ? blockTile->getPropertyValueEnum("state", BlockState::Unknown) : BlockState::Unknown; std::vector subStates; @@ -1052,11 +1198,13 @@ void TilePainter::drawRailBlock(const QRectF& r, TileRotate rotate, const Object label = blockTile->getPropertyValueString("name"); } - setTrackPen(); - if(rotate == TileRotate::Deg0) { - m_painter.drawLine(topCenter(r), bottomCenter(r)); + setTrackPen(isReservedA); + m_painter.drawLine(topCenter(r), r.center()); + setTrackPen(isReservedB); + m_painter.drawLine(r.center(), bottomCenter(r)); + setBlockStateBrush(state); m_painter.setPen(m_blockPen); const qreal m = 0.5 + qFloor(r.width() / 10); @@ -1089,7 +1237,11 @@ void TilePainter::drawRailBlock(const QRectF& r, TileRotate rotate, const Object } else if(rotate == TileRotate::Deg90) { - m_painter.drawLine(centerLeft(r), centerRight(r)); + setTrackPen(isReservedA); + m_painter.drawLine(centerLeft(r), r.center()); + setTrackPen(isReservedB); + m_painter.drawLine(r.center(), centerRight(r)); + setBlockStateBrush(state); m_painter.setPen(m_blockPen); const qreal m = 0.5 + qFloor(r.height() / 10); @@ -1121,9 +1273,9 @@ void TilePainter::drawRailBlock(const QRectF& r, TileRotate rotate, const Object assert(false); } -void TilePainter::drawRailDecoupler(const QRectF& r, TileRotate rotate, DecouplerState state) +void TilePainter::drawRailDecoupler(const QRectF& r, TileRotate rotate, bool isReserved, DecouplerState state) { - setTrackPen(); + setTrackPen(isReserved); drawStraight(r, rotate); m_painter.save(); @@ -1137,9 +1289,9 @@ void TilePainter::drawRailDecoupler(const QRectF& r, TileRotate rotate, Decouple m_painter.restore(); } -void TilePainter::drawRailNX(const QRectF& r, TileRotate rotate, bool pressed) +void TilePainter::drawRailNX(const QRectF& r, TileRotate rotate, bool isReserved, bool pressed) { - setTrackPen(); + setTrackPen(isReserved); drawStraight(r, rotate); drawPushButton(r, pressed ? Color::White : Color::Blue); } diff --git a/client/src/board/tilepainter.hpp b/client/src/board/tilepainter.hpp index 614a6ba8..62ee910a 100644 --- a/client/src/board/tilepainter.hpp +++ b/client/src/board/tilepainter.hpp @@ -29,6 +29,7 @@ #include #include #include +#include #include #include #include @@ -55,6 +56,8 @@ class TilePainter const QPen m_blockPen; const QPen m_trackPen; const QPen m_trackDisabledPen; + const QPen m_trackReservedPen; + const QPen m_trackReservedDisabledPen; const QPen m_trackErasePen; const QPen m_turnoutPen; const QPen m_turnoutStatePen; @@ -62,6 +65,7 @@ class TilePainter QPainter& m_painter; inline void setTrackPen() { m_painter.setPen(m_trackPen); } + inline void setTrackPen(bool isReserved) { m_painter.setPen(isReserved ? m_trackReservedPen : m_trackPen); } inline void setTrackDisabledPen() { m_painter.setPen(m_trackDisabledPen); } inline void setTrackErasePen() { m_painter.setPen(m_trackErasePen); } inline void setTurnoutPen() { m_painter.setPen(m_turnoutDrawState ? m_trackPen : m_trackDisabledPen); } @@ -81,27 +85,32 @@ class TilePainter void drawTriangle(const QRectF& r); void drawLED(const QRectF& r, const QColor& color, const QColor& borderColor); + void drawTurnoutStandard(TileId id, const QRectF& r, TileRotate rotate, TurnoutPosition reservedPosition = TurnoutPosition::Unknown, TurnoutPosition position = TurnoutPosition::Unknown); + void drawTurnoutSlip(TileId id, const QRectF& r, TileRotate rotate, TurnoutPosition reservedPosition = TurnoutPosition::Unknown, TurnoutPosition position = TurnoutPosition::Unknown); + void drawSignal2Aspect(QRectF r, TileRotate rotate, SignalAspect aspect); void drawSignal3Aspect(QRectF r, TileRotate rotate, SignalAspect aspect); void drawSignalDirection(QRectF r, TileRotate rotate); - void drawRailBlock(const QRectF& r, TileRotate rotate, const ObjectPtr& blockTile = {}); + void drawRailBlock(const QRectF& r, TileRotate rotate, bool isReservedA = false, bool isReservedB = false, const ObjectPtr& blockTile = {}); public: TilePainter(QPainter& painter, int tileSize, const BoardColorScheme& colorScheme); - void draw(TileId id, const QRectF& r, TileRotate rotate); - void drawSensor(TileId id, const QRectF& r, TileRotate rotate, SensorState state = SensorState::Unknown); - void drawDirectionControl(TileId id, const QRectF& r, TileRotate rotate, DirectionControlState state = DirectionControlState::Both); - void drawTurnout(TileId id, const QRectF& r, TileRotate rotate, TurnoutPosition position = TurnoutPosition::Unknown); - void drawSignal(TileId id, const QRectF& r, TileRotate rotate, SignalAspect aspect = SignalAspect::Unknown); - void drawBlock(TileId id, const QRectF& r, TileRotate rotate, const ObjectPtr& blockTile = {}); + void draw(TileId id, const QRectF& r, TileRotate rotate, bool isReserved = false); + void drawBridge(TileId id, const QRectF& r, TileRotate rotate, bool isReservedAC = false, bool isReservedBD = false); + void drawCross(TileId id, const QRectF& r, TileRotate rotate, CrossState reservedState = CrossState::Unset); + void drawSensor(TileId id, const QRectF& r, TileRotate rotate, bool isReserved = false, SensorState state = SensorState::Unknown); + void drawDirectionControl(TileId id, const QRectF& r, TileRotate rotate, bool isReserved = false, DirectionControlState state = DirectionControlState::Both); + void drawTurnout(TileId id, const QRectF& r, TileRotate rotate, TurnoutPosition reservedPosition = TurnoutPosition::Unknown, TurnoutPosition position = TurnoutPosition::Unknown); + void drawSignal(TileId id, const QRectF& r, TileRotate rotate, bool isReserved = false, SignalAspect aspect = SignalAspect::Unknown); + void drawBlock(TileId id, const QRectF& r, TileRotate rotate, bool isReservedA = false, bool isReservedB = false, const ObjectPtr& blockTile = {}); void drawPushButton(const QRectF& r, Color color = Color::Yellow); - void drawRailDecoupler(const QRectF& r, TileRotate rotate, DecouplerState active = DecouplerState::Deactivated); + void drawRailDecoupler(const QRectF& r, TileRotate rotate, bool isReserved = false, DecouplerState active = DecouplerState::Deactivated); - void drawRailNX(const QRectF& r, TileRotate rotate, bool pressed = false); + void drawRailNX(const QRectF& r, TileRotate rotate, bool isReserved = false, bool pressed = false); }; #endif diff --git a/manual/luadoc/object/blockrailtile.json b/manual/luadoc/object/blockrailtile.json index d40ef5c7..201585f1 100644 --- a/manual/luadoc/object/blockrailtile.json +++ b/manual/luadoc/object/blockrailtile.json @@ -9,6 +9,19 @@ } ] }, + "on_train_reserved": { + "parameters": [ + { + "name": "train" + }, + { + "name": "block" + }, + { + "name": "direction" + } + ] + }, "on_train_removed": { "parameters": [ { diff --git a/manual/luadoc/terms/en-us.json b/manual/luadoc/terms/en-us.json index 31ced2c7..65f16f71 100644 --- a/manual/luadoc/terms/en-us.json +++ b/manual/luadoc/terms/en-us.json @@ -1365,7 +1365,7 @@ }, { "term": "object.blockrailtile.on_train_removed.parameter.block:description", - "definition": "The block that the train is assigned to." + "definition": "The block that the train is removed from." }, { "term": "object.clock.freeze:description", @@ -1646,5 +1646,41 @@ { "term": "object.identificationlist:title", "definition": "Identification list" + }, + { + "term": "enum.block_train_direction:title", + "definition": "Block train direction" + }, + { + "term": "enum.block_train_direction:description", + "definition": "Train direction from the block perspective." + }, + { + "term": "enum.block_train_direction.towards_a:description", + "definition": "Towards the west for horizontal blocks, towards the south for vertical blocks." + }, + { + "term": "enum.block_train_direction.towards_b:description", + "definition": "Towards the east for horizontal blocks, towards the north for vertical blocks." + }, + { + "term": "enum.block_train_direction.unknown:description", + "definition": "Unknown or invalid direction." + }, + { + "term": "object.blockrailtile.on_train_reserved:description", + "definition": "Fired when a {ref:object.train} reserves the block." + }, + { + "term": "object.blockrailtile.on_train_reserved.parameter.train:description", + "definition": "{ref:object.train} that is reserved the block." + }, + { + "term": "object.blockrailtile.on_train_reserved.parameter.block:description", + "definition": "The block that is reserved." + }, + { + "term": "object.blockrailtile.on_train_reserved.parameter.direction:description", + "definition": "Train direction from the block perspective, a {ref:enum.block_train_direction} value." } ] \ No newline at end of file diff --git a/server/src/board/map/blockpath.cpp b/server/src/board/map/blockpath.cpp index d3aff6ef..9a028762 100644 --- a/server/src/board/map/blockpath.cpp +++ b/server/src/board/map/blockpath.cpp @@ -26,6 +26,7 @@ #include "node.hpp" #include "link.hpp" #include "../tile/rail/blockrailtile.hpp" +#include "../tile/rail/bridgerailtile.hpp" #include "../tile/rail/crossrailtile.hpp" #include "../tile/rail/directioncontrolrailtile.hpp" #include "../tile/rail/signal/signalrailtile.hpp" @@ -33,8 +34,9 @@ #include "../tile/rail/linkrailtile.hpp" #include "../tile/rail/nxbuttonrailtile.hpp" #include "../../core/objectproperty.tpp" +#include "../../enum/bridgepath.hpp" -std::vector> BlockPath::find(BlockRailTile& startBlock) +std::vector> BlockPath::find(BlockRailTile& startBlock) { const auto& node = startBlock.node()->get(); const auto& linkA = node.getLink(0); @@ -47,21 +49,21 @@ std::vector> BlockPath::find(BlockRailTile& startBloc struct Position { - std::unique_ptr path; + std::shared_ptr path; const Node* node; const Link* link; }; - std::vector> paths; + std::vector> paths; std::queue todo; if(linkA) { - todo.emplace(Position{std::make_unique(startBlock, Side::A), &node, linkA.get()}); + todo.emplace(Position{std::make_shared(startBlock, BlockSide::A), &node, linkA.get()}); } if(linkB) { - todo.emplace(Position{std::make_unique(startBlock, Side::B), &node, linkB.get()}); + todo.emplace(Position{std::make_shared(startBlock, BlockSide::B), &node, linkB.get()}); } while(!todo.empty()) @@ -72,6 +74,12 @@ std::vector> BlockPath::find(BlockRailTile& startBloc todo.pop(); // drop it, dead end continue; } + + for(const auto& tile : current.link->tiles()) // add passive tiles to reserve + { + current.path->m_tiles.emplace_back(std::static_pointer_cast(tile)); + } + assert(current.node); const auto& nextNode = current.link->getNext(*current.node); auto& tile = nextNode.tile(); @@ -80,10 +88,6 @@ std::vector> BlockPath::find(BlockRailTile& startBloc { case TileId::RailBlock: { - // temp dummy use to fix warnings: - (void)current.path->m_fromBlock; - (void)current.path->m_fromSide; - if(current.node->tile().tileId() == TileId::RailNXButton) { current.path->m_nxButtonTo = current.node->tile().shared_ptr(); @@ -91,7 +95,7 @@ std::vector> BlockPath::find(BlockRailTile& startBloc auto& block = static_cast(tile); current.path->m_toBlock = block.shared_ptr(); - current.path->m_toSide = nextNode.getLink(0).get() == current.link ? Side::A : Side::B; + current.path->m_toSide = nextNode.getLink(0).get() == current.link ? BlockSide::A : BlockSide::B; paths.emplace_back(std::move(current.path)); todo.pop(); // complete break; @@ -115,7 +119,7 @@ std::vector> BlockPath::find(BlockRailTile& startBloc { for(size_t i = 1; i < links.size(); ++i) { - auto path = std::make_unique(*current.path); // "fork" path + auto path = std::make_shared(*current.path); // "fork" path path->m_turnouts.emplace_back(turnout->shared_ptr(), links[i].turnoutPosition); todo.emplace(Position{std::move(path), &nextNode, nextNode.getLink(links[i].linkIndex).get()}); } @@ -134,6 +138,7 @@ std::vector> BlockPath::find(BlockRailTile& startBloc // 0 if(nextNode.getLink(0).get() == current.link) // 0 -> 1 = allowed { + current.path->m_tiles.emplace_back(tile.shared_ptr()); current.node = &nextNode; current.link = nextNode.getLink(1).get(); } @@ -170,6 +175,7 @@ std::vector> BlockPath::find(BlockRailTile& startBloc { current.node = &nextNode; current.link = nextNode.getLink((i + 2) % 4).get(); // opposite + current.path->m_bridges.emplace_back(tile.shared_ptr(), i % 2 == 0 ? BridgePath::AC : BridgePath::BD); break; } } @@ -199,6 +205,8 @@ std::vector> BlockPath::find(BlockRailTile& startBloc auto& linkTile = static_cast(tile); if(linkTile.link) // is connected to another link { + current.path->m_tiles.emplace_back(linkTile.shared_ptr()); + current.path->m_tiles.emplace_back(linkTile.link->shared_ptr()); assert(linkTile.link->node()); auto& linkNode = linkTile.link->node()->get(); current.node = &linkNode; @@ -221,10 +229,12 @@ std::vector> BlockPath::find(BlockRailTile& startBloc else // 1 -> 0 = backside of signal, just pass { current.link = nextNode.getLink(0).get(); + current.path->m_tiles.emplace_back(tile.shared_ptr()); } break; case TileId::RailDecoupler: + current.path->m_tiles.emplace_back(tile.shared_ptr()); current.node = &nextNode; current.link = otherLink(nextNode, *current.link).get(); break; @@ -232,7 +242,11 @@ std::vector> BlockPath::find(BlockRailTile& startBloc case TileId::RailNXButton: if(¤t.node->tile() == &startBlock) { - current.path->m_nxButtonFrom = nextNode.tile().shared_ptr(); + current.path->m_nxButtonFrom = tile.shared_ptr(); + } + else + { + current.path->m_tiles.emplace_back(tile.shared_ptr()); } current.node = &nextNode; current.link = otherLink(nextNode, *current.link).get(); @@ -248,10 +262,10 @@ std::vector> BlockPath::find(BlockRailTile& startBloc } -BlockPath::BlockPath(BlockRailTile& block, Side side) +BlockPath::BlockPath(BlockRailTile& block, BlockSide side) : m_fromBlock{block} , m_fromSide{side} - , m_toSide{static_cast(-1)} + , m_toSide{static_cast(-1)} { } @@ -264,3 +278,138 @@ std::shared_ptr BlockPath::nxButtonTo() const { return m_nxButtonTo.lock(); } + +bool BlockPath::reserve(const std::shared_ptr& train, bool dryRun) +{ + if(!dryRun && !reserve(train, true)) // dry run first, to make sure it will succeed (else we need rollback support) + { + return false; + } + + if(!m_fromBlock.reserve(train, m_fromSide, dryRun)) + { + assert(dryRun); + return false; + } + + if(auto toBlock = m_toBlock.lock()) /*[[likely]]*/ + { + if(!toBlock->reserve(train, m_toSide, dryRun)) + { + assert(dryRun); + return false; + } + } + else + { + return false; + } + + for(const auto& [turnoutWeak, position] : m_turnouts) + { + if(auto turnout = turnoutWeak.lock()) + { + if(!turnout->reserve(position, dryRun)) + { + assert(dryRun); + return false; + } + } + else /*[[unlikely]]*/ + { + assert(dryRun); + return false; + } + } + + for(const auto& [directionControlWeak, state] : m_directionControls) + { + if(auto directionControl = directionControlWeak.lock()) + { + if(!directionControl->reserve(state, dryRun)) + { + assert(dryRun); + return false; + } + } + else /*[[unlikely]]*/ + { + assert(dryRun); + return false; + } + } + + for(const auto& [crossWeak, state] : m_crossings) + { + if(auto cross = crossWeak.lock()) + { + if(!cross->reserve(state, dryRun)) + { + assert(dryRun); + return false; + } + } + else /*[[unlikely]]*/ + { + assert(dryRun); + return false; + } + } + + for(const auto& [bridgeWeak, path] : m_bridges) + { + if(auto bridge = bridgeWeak.lock()) + { + if(!bridge->reserve(path, dryRun)) + { + assert(dryRun); + return false; + } + } + else /*[[unlikely]]*/ + { + assert(dryRun); + return false; + } + } + + for(const auto& signalWeak : m_signals) + { + if(auto signal = signalWeak.lock()) + { + if(!signal->reserve(SignalRailTile::Pass{}, dryRun)) + { + assert(dryRun); + return false; + } + } + else /*[[unlikely]]*/ + { + assert(dryRun); + return false; + } + } + + if(!dryRun) + { + for(const auto& tileWeak : m_tiles) + { + if(auto tile = tileWeak.lock()) /*[[likely]]*/ + { + static_cast(*tile).reserve(); + } + } + + if(auto nxButton = m_nxButtonFrom.lock()) + { + nxButton->reserve(); + } + + if(auto nxButton = m_nxButtonTo.lock()) + { + nxButton->reserve(); + } + } + + return true; +} diff --git a/server/src/board/map/blockpath.hpp b/server/src/board/map/blockpath.hpp index 5a878676..a75b6387 100644 --- a/server/src/board/map/blockpath.hpp +++ b/server/src/board/map/blockpath.hpp @@ -29,8 +29,12 @@ #include #include #include +#include "../../enum/blockside.hpp" +class RailTile; class BlockRailTile; +class BridgeRailTile; +enum class BridgePath : uint8_t; class CrossRailTile; enum class CrossState : uint8_t; class DirectionControlRailTile; @@ -39,35 +43,31 @@ class SignalRailTile; class NXButtonRailTile; class Node; class Link; +class Train; /** * \brief A path between two blocks */ class BlockPath : public Path { - public: - enum class Side - { - A = 0, - B = 1, - }; - private: - BlockRailTile const& m_fromBlock; - const Side m_fromSide; + BlockRailTile& m_fromBlock; + const BlockSide m_fromSide; std::weak_ptr m_toBlock; - Side m_toSide; + BlockSide m_toSide; + std::vector> m_tiles; //!< passive tiles to reserve std::vector, TurnoutPosition>> m_turnouts; //!< required turnout positions for the path std::vector, DirectionControlState>> m_directionControls; //!< required direction control states for the path std::vector, CrossState>> m_crossings; //!< required crossing states for the path + std::vector, BridgePath>> m_bridges; //!< bridges to reserve std::vector> m_signals; //!< signals in path std::weak_ptr m_nxButtonFrom; std::weak_ptr m_nxButtonTo; public: - static std::vector> find(BlockRailTile& block); + static std::vector> find(BlockRailTile& block); - BlockPath(BlockRailTile& block, Side side); + BlockPath(BlockRailTile& block, BlockSide side); bool hasNXButtons() const { @@ -79,13 +79,25 @@ class BlockPath : public Path return m_fromBlock; } + BlockSide fromSide() const + { + return m_fromSide; + } + std::shared_ptr toBlock() const { return m_toBlock.lock(); } + BlockSide toSide() const + { + return m_toSide; + } + std::shared_ptr nxButtonFrom() const; std::shared_ptr nxButtonTo() const; + + bool reserve(const std::shared_ptr& train, bool dryRun = false); }; #endif diff --git a/server/src/board/nx/nxmanager.cpp b/server/src/board/nx/nxmanager.cpp index d42a68f6..341e4be2 100644 --- a/server/src/board/nx/nxmanager.cpp +++ b/server/src/board/nx/nxmanager.cpp @@ -27,6 +27,7 @@ #include "../../core/method.tpp" #include "../../core/objectproperty.tpp" #include "../../log/log.hpp" +#include "../../train/trainblockstatus.hpp" #include "../../world/getworld.hpp" NXManager::NXManager(Object& parent_, std::string_view parentPropertyName) @@ -91,14 +92,30 @@ void NXManager::released(NXButtonRailTile& button) bool NXManager::selectPath(const NXButtonRailTile& from, const NXButtonRailTile& to) { - for(const auto& path : from.block->paths()) + for(auto& path : from.block->paths()) { if(path->nxButtonTo().get() == &to && path->nxButtonFrom().get() == &from) { - LOG_DEBUG("Path selected:", path->fromBlock().name.value(), "->", path->toBlock()->name.value()); + LOG_DEBUG("Path found:", path->fromBlock().name.value(), "->", path->toBlock()->name.value()); + + if(from.block->trains.empty()) + { + continue; // no train in from block + } + + const auto& status = path->fromSide() == BlockSide::A ? from.block->trains.front() : from.block->trains.back(); + if(!status->train) + { + continue; // no train assigned in from block + } + + if(!path->reserve(status->train.value())) + { + continue; // can't reserve path + } return true; } } - return false; + return false; // no path found } diff --git a/server/src/board/tile/rail/blockrailtile.cpp b/server/src/board/tile/rail/blockrailtile.cpp index 08b7146b..501156e5 100644 --- a/server/src/board/tile/rail/blockrailtile.cpp +++ b/server/src/board/tile/rail/blockrailtile.cpp @@ -159,6 +159,7 @@ BlockRailTile::BlockRailTile(World& world, std::string_view _id) : } }} , onTrainAssigned{*this, "on_train_assigned", EventFlags::Scriptable} + , onTrainReserved{*this, "on_train_reserved", EventFlags::Scriptable} , onTrainRemoved{*this, "on_train_removed", EventFlags::Scriptable} { inputMap.setValueInternal(std::make_shared(*this, inputMap.name())); @@ -196,6 +197,7 @@ BlockRailTile::BlockRailTile(World& world, std::string_view _id) : m_interfaceItems.add(flipTrain); m_interfaceItems.add(onTrainAssigned); + m_interfaceItems.add(onTrainReserved); m_interfaceItems.add(onTrainRemoved); updateHeightWidthMax(); @@ -269,6 +271,56 @@ void BlockRailTile::identificationEvent(BlockInputMapItem& /*item*/, Identificat } } +bool BlockRailTile::reserve(const std::shared_ptr& train, BlockSide side, bool dryRun) +{ + const uint8_t mask = 1 << static_cast(side); + + if(state == BlockState::Unknown) + { + return false; // can't reserve block with unknown state + } + + if((reservedState() & mask)) + { + return false; // already reserved + } + + if(state != BlockState::Free) + { + if(trains.empty()) + { + return false; // not free block, but no train + } + + const auto& status = (side == BlockSide::A) ? *trains.front() : *trains.back(); + if(!status.train || status.train.value() != train) + { + return false; // no train or other train assigned to block + } + + if((side == BlockSide::A && status.direction != BlockTrainDirection::TowardsA) || + (side == BlockSide::B && status.direction != BlockTrainDirection::TowardsB)) + { + //! \todo allow direction change, add block property, automatic for dead ends + return false; // invalid train direction + } + } + + if(!dryRun) + { + RailTile::reserve(reservedState() | mask); + if(state == BlockState::Free) + { + const auto direction = side == BlockSide::A ? BlockTrainDirection::TowardsB : BlockTrainDirection::TowardsA; + trains.appendInternal(TrainBlockStatus::create(*this, *train, direction)); + fireEvent&, const std::shared_ptr&>(onTrainReserved, train, shared_ptr(), direction); + } + updateState(); + } + + return true; +} + void BlockRailTile::updateState() { if(!inputMap->items.empty()) diff --git a/server/src/board/tile/rail/blockrailtile.hpp b/server/src/board/tile/rail/blockrailtile.hpp index 0674be6e..f1adc30e 100644 --- a/server/src/board/tile/rail/blockrailtile.hpp +++ b/server/src/board/tile/rail/blockrailtile.hpp @@ -24,10 +24,12 @@ #define TRAINTASTIC_SERVER_BOARD_TILE_RAIL_BLOCKRAILTILE_HPP #include "railtile.hpp" +#include #include "../../map/node.hpp" #include "../../../core/method.hpp" #include "../../../core/objectproperty.hpp" #include "../../../core/vectorproperty.hpp" +#include "../../../enum/blockside.hpp" #include "../../../enum/blockstate.hpp" #include "../../../hardware/input/map/blockinputmap.hpp" @@ -44,7 +46,7 @@ class BlockRailTile : public RailTile private: Node m_node; - std::vector> m_paths; + std::vector> m_paths; void updateHeightWidthMax(); @@ -71,6 +73,7 @@ class BlockRailTile : public RailTile Method removeTrain; Method flipTrain; Event&, const std::shared_ptr&> onTrainAssigned; + Event&, const std::shared_ptr&, BlockTrainDirection> onTrainReserved; Event&, const std::shared_ptr&> onTrainRemoved; BlockRailTile(World& world, std::string_view _id); @@ -79,13 +82,15 @@ class BlockRailTile : public RailTile std::optional> node() final { return m_node; } void getConnectors(std::vector& connectors) const final; - const std::vector>& paths() const + const std::vector>& paths() const { return m_paths; } void inputItemValueChanged(BlockInputMapItem& item); void identificationEvent(BlockInputMapItem& item, IdentificationEventType eventType, uint16_t identifier, Direction direction, uint8_t category); + + bool reserve(const std::shared_ptr& train, BlockSide side, bool dryRun = false); }; #endif diff --git a/server/src/board/tile/rail/bridgerailtile.cpp b/server/src/board/tile/rail/bridgerailtile.cpp index 201dd43d..9a1d9751 100644 --- a/server/src/board/tile/rail/bridgerailtile.cpp +++ b/server/src/board/tile/rail/bridgerailtile.cpp @@ -3,7 +3,7 @@ * * This file is part of the traintastic source code. * - * Copyright (C) 2022 Reinder Feenstra + * Copyright (C) 2022-2023 Reinder Feenstra * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -28,3 +28,20 @@ BridgeRailTile::BridgeRailTile(World& world, std::string_view _id, TileId tileId { assert(isRailBridge(tileId_)); } + +bool BridgeRailTile::reserve(BridgePath path, bool dryRun) +{ + const uint8_t mask = 1 << static_cast(path); + + if((reservedState() & mask)) + { + return false; // already reserved + } + + if(!dryRun) + { + RailTile::reserve(reservedState() | mask); + } + + return true; +} diff --git a/server/src/board/tile/rail/bridgerailtile.hpp b/server/src/board/tile/rail/bridgerailtile.hpp index 2abbf34c..ea2a98d8 100644 --- a/server/src/board/tile/rail/bridgerailtile.hpp +++ b/server/src/board/tile/rail/bridgerailtile.hpp @@ -3,7 +3,7 @@ * * This file is part of the traintastic source code. * - * Copyright (C) 2022 Reinder Feenstra + * Copyright (C) 2022-2023 Reinder Feenstra * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -26,6 +26,8 @@ #include "railtile.hpp" #include "../../map/node.hpp" +enum class BridgePath : uint8_t; + class BridgeRailTile : public RailTile { private: @@ -37,6 +39,8 @@ class BridgeRailTile : public RailTile public: std::optional> node() const final { return m_node; } std::optional> node() final { return m_node; } + + bool reserve(BridgePath path, bool dryRun = false); }; #endif diff --git a/server/src/board/tile/rail/crossrailtile.cpp b/server/src/board/tile/rail/crossrailtile.cpp index d20f5253..562d9a29 100644 --- a/server/src/board/tile/rail/crossrailtile.cpp +++ b/server/src/board/tile/rail/crossrailtile.cpp @@ -3,7 +3,7 @@ * * This file is part of the traintastic source code. * - * Copyright (C) 2022 Reinder Feenstra + * Copyright (C) 2022-2023 Reinder Feenstra * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -21,10 +21,28 @@ */ #include "crossrailtile.hpp" +#include CrossRailTile::CrossRailTile(World& world, std::string_view _id, TileId tileId_) : RailTile(world, _id, tileId_) , m_node{*this, 4} + , m_crossState{CrossState::Unset} { assert(isRailCross(tileId_)); } + +bool CrossRailTile::reserve(CrossState crossState, bool dryRun) +{ + if(m_crossState != CrossState::Unset) + { + return false; + } + + if(!dryRun) + { + m_crossState = crossState; + RailTile::reserve(static_cast(m_crossState)); + } + + return true; +} diff --git a/server/src/board/tile/rail/crossrailtile.hpp b/server/src/board/tile/rail/crossrailtile.hpp index dc6bbacc..2736f5e0 100644 --- a/server/src/board/tile/rail/crossrailtile.hpp +++ b/server/src/board/tile/rail/crossrailtile.hpp @@ -3,7 +3,7 @@ * * This file is part of the traintastic source code. * - * Copyright (C) 2022 Reinder Feenstra + * Copyright (C) 2022-2023 Reinder Feenstra * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -26,10 +26,13 @@ #include "railtile.hpp" #include "../../map/node.hpp" +enum class CrossState : uint8_t; + class CrossRailTile : public RailTile { private: Node m_node; + CrossState m_crossState; //!< indicates which path is reserved protected: CrossRailTile(World& world, std::string_view _id, TileId tileId_); @@ -37,6 +40,8 @@ class CrossRailTile : public RailTile public: std::optional> node() const final { return m_node; } std::optional> node() final { return m_node; } + + bool reserve(CrossState crossState, bool dryRun = false); }; #endif diff --git a/server/src/board/tile/rail/directioncontrolrailtile.cpp b/server/src/board/tile/rail/directioncontrolrailtile.cpp index 28844506..3acbe6d1 100644 --- a/server/src/board/tile/rail/directioncontrolrailtile.cpp +++ b/server/src/board/tile/rail/directioncontrolrailtile.cpp @@ -3,7 +3,7 @@ * * This file is part of the traintastic source code. * - * Copyright (C) 2022 Reinder Feenstra + * Copyright (C) 2022-2023 Reinder Feenstra * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -119,6 +119,21 @@ DirectionControlRailTile::DirectionControlRailTile(World& world, std::string_vie updateStateValues(); } +bool DirectionControlRailTile::reserve(DirectionControlState directionControlState, bool dryRun) +{ + if(state != directionControlState && state != DirectionControlState::Both) + { + return false; + } + + if(!dryRun) + { + StraightRailTile::reserve(); + } + + return true; +} + void DirectionControlRailTile::loaded() { StraightRailTile::loaded(); diff --git a/server/src/board/tile/rail/directioncontrolrailtile.hpp b/server/src/board/tile/rail/directioncontrolrailtile.hpp index 5feb3dfb..48b3ea56 100644 --- a/server/src/board/tile/rail/directioncontrolrailtile.hpp +++ b/server/src/board/tile/rail/directioncontrolrailtile.hpp @@ -3,7 +3,7 @@ * * This file is part of the traintastic source code. * - * Copyright (C) 2022 Reinder Feenstra + * Copyright (C) 2022-2023 Reinder Feenstra * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -60,6 +60,8 @@ class DirectionControlRailTile final : public StraightRailTile std::optional> node() const final { return m_node; } std::optional> node() final { return m_node; } + + bool reserve(DirectionControlState turnoutPosition, bool dryRun = false); }; #endif diff --git a/server/src/board/tile/rail/railtile.cpp b/server/src/board/tile/rail/railtile.cpp index 90a2e360..757da712 100644 --- a/server/src/board/tile/rail/railtile.cpp +++ b/server/src/board/tile/rail/railtile.cpp @@ -3,7 +3,7 @@ * * This file is part of the traintastic source code. * - * Copyright (C) 2020 Reinder Feenstra + * Copyright (C) 2020,2023 Reinder Feenstra * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -21,8 +21,25 @@ */ #include "railtile.hpp" +#include "../../board.hpp" RailTile::RailTile(World& world, std::string_view _id, TileId tileId) : Tile(world, _id, tileId) { } + +void RailTile::reserve(uint8_t state) +{ + assert(state != 0); + setReservedState(state); +} + +void RailTile::setReservedState(uint8_t value) +{ + if(m_reservedState != value) + { + m_reservedState = value; + auto& board = getBoard(); + board.tileDataChanged(board, location(), data()); + } +} diff --git a/server/src/board/tile/rail/railtile.hpp b/server/src/board/tile/rail/railtile.hpp index 8065bafe..834a5bea 100644 --- a/server/src/board/tile/rail/railtile.hpp +++ b/server/src/board/tile/rail/railtile.hpp @@ -3,7 +3,7 @@ * * This file is part of the traintastic source code. * - * Copyright (C) 2020 Reinder Feenstra + * Copyright (C) 2020,2023 Reinder Feenstra * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -27,8 +27,26 @@ class RailTile : public Tile { + private: + uint8_t m_reservedState = 0; + + void setReservedState(uint8_t value); + protected: RailTile(World& world, std::string_view _id, TileId tileId); + + uint8_t reservedState() const final + { + return m_reservedState; + } + + void reserve(uint8_t state); + + public: + inline void reserve() + { + reserve(1); + } }; #endif diff --git a/server/src/board/tile/rail/signal/signalrailtile.cpp b/server/src/board/tile/rail/signal/signalrailtile.cpp index 8b37f066..56ec4425 100644 --- a/server/src/board/tile/rail/signal/signalrailtile.cpp +++ b/server/src/board/tile/rail/signal/signalrailtile.cpp @@ -3,7 +3,7 @@ * * This file is part of the traintastic source code. * - * Copyright (C) 2020-2022 Reinder Feenstra + * Copyright (C) 2020-2023 Reinder Feenstra * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -54,6 +54,17 @@ SignalRailTile::SignalRailTile(World& world, std::string_view _id, TileId tileId SignalRailTile::~SignalRailTile() = default; // default here, so we can use a forward declaration of SignalPath in the header. +bool SignalRailTile::reserve(Pass, bool dryRun) +{ + // no conditions yet... + + if(!dryRun) + { + RailTile::reserve(); + } + return true; +} + void SignalRailTile::worldEvent(WorldState state, WorldEvent event) { StraightRailTile::worldEvent(state, event); diff --git a/server/src/board/tile/rail/signal/signalrailtile.hpp b/server/src/board/tile/rail/signal/signalrailtile.hpp index 5ec23083..6ef64f61 100644 --- a/server/src/board/tile/rail/signal/signalrailtile.hpp +++ b/server/src/board/tile/rail/signal/signalrailtile.hpp @@ -3,7 +3,7 @@ * * This file is part of the traintastic source code. * - * Copyright (C) 2020-2022 Reinder Feenstra + * Copyright (C) 2020-2023 Reinder Feenstra * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -47,6 +47,10 @@ class SignalRailTile : public StraightRailTile virtual bool doSetAspect(SignalAspect value); public: + struct Pass + { + }; + Property name; Property aspect; ObjectProperty outputMap; @@ -56,6 +60,8 @@ class SignalRailTile : public StraightRailTile std::optional> node() const final { return m_node; } std::optional> node() final { return m_node; } + + bool reserve(Pass, bool dryRun = false); }; #endif diff --git a/server/src/board/tile/rail/turnout/turnoutrailtile.cpp b/server/src/board/tile/rail/turnout/turnoutrailtile.cpp index 1c493221..652ff9cd 100644 --- a/server/src/board/tile/rail/turnout/turnoutrailtile.cpp +++ b/server/src/board/tile/rail/turnout/turnoutrailtile.cpp @@ -3,7 +3,7 @@ * * This file is part of the traintastic source code. * - * Copyright (C) 2020-2022 Reinder Feenstra + * Copyright (C) 2020-2023 Reinder Feenstra * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -53,6 +53,24 @@ TurnoutRailTile::TurnoutRailTile(World& world, std::string_view _id, TileId tile // setPosition is added by sub class } +bool TurnoutRailTile::reserve(TurnoutPosition turnoutPosition, bool dryRun) +{ + if(!isValidPosition(turnoutPosition)) + { + return false; + } + if(!dryRun) + { + if(!doSetPosition(turnoutPosition)) /*[[unlikely]]*/ + { + return false; + } + + RailTile::reserve(static_cast(turnoutPosition)); + } + return true; +} + void TurnoutRailTile::worldEvent(WorldState state, WorldEvent event) { RailTile::worldEvent(state, event); @@ -62,12 +80,19 @@ void TurnoutRailTile::worldEvent(WorldState state, WorldEvent event) Attributes::setEnabled(name, editable); } -bool TurnoutRailTile::doSetPosition(TurnoutPosition value) +bool TurnoutRailTile::isValidPosition(TurnoutPosition value) { const auto* values = setPosition.tryGetValuesAttribute(AttributeName::Values); assert(values); - if(!values->contains(static_cast(value))) + return values->contains(static_cast(value)); +} + +bool TurnoutRailTile::doSetPosition(TurnoutPosition value) +{ + if(!isValidPosition(value)) + { return false; + } (*outputMap)[value]->execute(); position.setValueInternal(value); positionChanged(*this, value); diff --git a/server/src/board/tile/rail/turnout/turnoutrailtile.hpp b/server/src/board/tile/rail/turnout/turnoutrailtile.hpp index 37e565d7..28544509 100644 --- a/server/src/board/tile/rail/turnout/turnoutrailtile.hpp +++ b/server/src/board/tile/rail/turnout/turnoutrailtile.hpp @@ -3,7 +3,7 @@ * * This file is part of the traintastic source code. * - * Copyright (C) 2020-2022 Reinder Feenstra + * Copyright (C) 2020-2023 Reinder Feenstra * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -42,6 +42,7 @@ class TurnoutRailTile : public RailTile void worldEvent(WorldState state, WorldEvent event) override; + bool isValidPosition(TurnoutPosition value); virtual bool doSetPosition(TurnoutPosition value); public: @@ -54,6 +55,8 @@ class TurnoutRailTile : public RailTile std::optional> node() const final { return m_node; } std::optional> node() final { return m_node; } + + virtual bool reserve(TurnoutPosition turnoutPosition, bool dryRun = false); }; #endif diff --git a/server/src/board/tile/tile.cpp b/server/src/board/tile/tile.cpp index 4c585d65..37ae1193 100644 --- a/server/src/board/tile/tile.cpp +++ b/server/src/board/tile/tile.cpp @@ -3,7 +3,7 @@ * * This file is part of the traintastic source code. * - * Copyright (C) 2020-2021 Reinder Feenstra + * Copyright (C) 2020-2021,2023 Reinder Feenstra * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -22,6 +22,9 @@ #include "tile.hpp" #include "../../core/attributes.hpp" +#include "../board.hpp" +#include "../boardlist.hpp" +#include "../../world/world.hpp" Tile::Tile(World& world, std::string_view _id, TileId tileId) : IdObject(world, _id) @@ -51,6 +54,19 @@ Tile::Tile(World& world, std::string_view _id, TileId tileId) m_interfaceItems.add(width); } +Board& Tile::getBoard() +{ + for(const auto& board : *m_world.boards) + { + if(board->getTile(location()).get() == this) + { + return *board; + } + } + assert(false); + abort(); +} + bool Tile::resize(uint8_t w, uint8_t h) { assert(w >= 1); diff --git a/server/src/board/tile/tile.hpp b/server/src/board/tile/tile.hpp index 3c677c05..fac6b847 100644 --- a/server/src/board/tile/tile.hpp +++ b/server/src/board/tile/tile.hpp @@ -3,7 +3,7 @@ * * This file is part of the traintastic source code. * - * Copyright (C) 2020-2022 Reinder Feenstra + * Copyright (C) 2020-2023 Reinder Feenstra * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -31,6 +31,7 @@ #include "../../enum/tilerotate.hpp" class Node; +class Board; class Tile : public IdObject { @@ -42,6 +43,13 @@ class Tile : public IdObject Tile(World& world, std::string_view _id, TileId tileId); + Board& getBoard(); + + virtual uint8_t reservedState() const + { + return 0; + } + virtual void boardModified() {} virtual void setRotate(TileRotate value) { rotate.setValueInternal(value); } bool resize(uint8_t w, uint8_t h); @@ -57,7 +65,7 @@ class Tile : public IdObject TileId tileId() const { return m_tileId; } inline TileLocation location() const { return {x.value(), y.value()}; } - inline TileData data() const { return TileData{m_tileId, rotate, width, height}; } + inline TileData data() const { return TileData{m_tileId, rotate, width, height, reservedState()}; } virtual std::optional> node() const { return {}; } virtual std::optional> node() { return {}; } diff --git a/server/src/core/objectvectorproperty.hpp b/server/src/core/objectvectorproperty.hpp index 59764cd0..7b142844 100644 --- a/server/src/core/objectvectorproperty.hpp +++ b/server/src/core/objectvectorproperty.hpp @@ -53,6 +53,16 @@ class ObjectVectorProperty : public AbstractObjectVectorProperty inline const_reverse_iterator rbegin() const { return m_values.rbegin(); } inline const_reverse_iterator rend() const { return m_values.rend(); } + inline const std::shared_ptr& front() const + { + return m_values.front(); + } + + inline const std::shared_ptr& back() const + { + return m_values.back(); + } + const std::shared_ptr& operator [](size_t index) const { return m_values[index]; diff --git a/server/src/enum/blockside.hpp b/server/src/enum/blockside.hpp new file mode 100644 index 00000000..e63984fc --- /dev/null +++ b/server/src/enum/blockside.hpp @@ -0,0 +1,34 @@ +/** + * server/src/enum/blockside.hpp + * + * This file is part of the traintastic source code. + * + * Copyright (C) 2023 Reinder Feenstra + * + * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef TRAINTASTIC_SERVER_ENUM_BLOCKSIDE_HPP +#define TRAINTASTIC_SERVER_ENUM_BLOCKSIDE_HPP + +#include + +enum class BlockSide : uint8_t +{ + A = 0, + B = 1, +}; + +#endif diff --git a/server/src/enum/bridgepath.hpp b/server/src/enum/bridgepath.hpp new file mode 100644 index 00000000..ca79a672 --- /dev/null +++ b/server/src/enum/bridgepath.hpp @@ -0,0 +1,34 @@ +/** + * server/src/enum/bridgepath.hpp + * + * This file is part of the traintastic source code. + * + * Copyright (C) 2023 Reinder Feenstra + * + * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef TRAINTASTIC_SERVER_ENUM_BRIDGEPATH_HPP +#define TRAINTASTIC_SERVER_ENUM_BRIDGEPATH_HPP + +#include + +enum class BridgePath : uint8_t +{ + AC = 0, + BD = 1, +}; + +#endif diff --git a/server/src/lua/enums.hpp b/server/src/lua/enums.hpp index ab434ef1..93f50bfb 100644 --- a/server/src/lua/enums.hpp +++ b/server/src/lua/enums.hpp @@ -25,6 +25,7 @@ #include #include "enum.hpp" +#include #include #include "../../src/enum/direction.hpp" #include "../../src/enum/directioncontrolstate.hpp" @@ -37,6 +38,7 @@ #include "../../src/enum/worldscale.hpp" #define LUA_ENUMS \ + BlockTrainDirection, \ DecoderProtocol, \ Direction, \ DirectionControlState, \ diff --git a/shared/src/traintastic/board/tiledata.hpp b/shared/src/traintastic/board/tiledata.hpp index 9e5c73dc..65777735 100644 --- a/shared/src/traintastic/board/tiledata.hpp +++ b/shared/src/traintastic/board/tiledata.hpp @@ -3,7 +3,7 @@ * * This file is part of the traintastic source code. * - * Copyright (C) 2020-2021 Reinder Feenstra + * Copyright (C) 2020-2021,2023 Reinder Feenstra * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -33,12 +33,12 @@ struct TileData uint16_t _header; uint8_t _size; - uint8_t _reserved; + uint8_t state; - inline TileData(TileId _id = TileId::None, TileRotate _rotate = TileRotate::Deg0, uint8_t _width = 1, uint8_t _height = 1) : + inline TileData(TileId _id = TileId::None, TileRotate _rotate = TileRotate::Deg0, uint8_t _width = 1, uint8_t _height = 1, uint8_t state_ = 0) : _header{static_cast((static_cast(_id) << 4) | (static_cast(_rotate) << 1) | (::isActive(_id) ? 1 : 0))}, _size{0}, - _reserved{0} + state{state_} { setSize(_width, _height); } diff --git a/shared/src/traintastic/enum/crossstate.hpp b/shared/src/traintastic/enum/crossstate.hpp index 7339fb64..618c9945 100644 --- a/shared/src/traintastic/enum/crossstate.hpp +++ b/shared/src/traintastic/enum/crossstate.hpp @@ -28,7 +28,7 @@ enum class CrossState : uint8_t { - Unknown = 0, + Unset = 0, AC = 1, BD = 2, };