diff options
Diffstat (limited to 'src/train_cmd.cpp')
-rw-r--r-- | src/train_cmd.cpp | 153 |
1 files changed, 102 insertions, 51 deletions
diff --git a/src/train_cmd.cpp b/src/train_cmd.cpp index 2ba40618e..bd245cafd 100644 --- a/src/train_cmd.cpp +++ b/src/train_cmd.cpp @@ -41,8 +41,8 @@ #include "table/train_cmd.h" static Track ChooseTrainTrack(Train *v, TileIndex tile, DiagDirection enterdir, TrackBits tracks, bool force_res, bool *got_reservation, bool mark_stuck); -static bool TrainCheckIfLineEnds(Train *v); -static void TrainController(Train *v, Vehicle *nomove); +static bool TrainCheckIfLineEnds(Train *v, bool reverse = true); +bool TrainController(Train *v, Vehicle *nomove, bool reverse = true); // Also used in vehicle_sl.cpp. static TileIndex TrainApproachingCrossingTile(const Train *v); static void CheckIfTrainNeedsService(Train *v); static void CheckNextTrainTile(Train *v); @@ -95,7 +95,7 @@ void CheckTrainsLengths() for (const Train *u = v, *w = v->Next(); w != NULL; u = w, w = w->Next()) { if (u->track != TRACK_BIT_DEPOT) { if ((w->track != TRACK_BIT_DEPOT && - max(abs(u->x_pos - w->x_pos), abs(u->y_pos - w->y_pos)) != u->gcache.cached_veh_length) || + max(abs(u->x_pos - w->x_pos), abs(u->y_pos - w->y_pos)) != u->CalcNextVehicleOffset()) || (w->track == TRACK_BIT_DEPOT && TicksToLeaveDepot(u) <= 0)) { SetDParam(0, v->index); SetDParam(1, v->owner); @@ -305,11 +305,8 @@ int GetTrainStopLocation(StationID station_id, TileIndex tile, const Train *v, i } /* Subtract half the front vehicle length of the train so we get the real - * stop location of the train. - * Actually, the center of all vehicles is half a normal vehicle's length - * from the front of the vehicle, so even in case the vehicle is 1/8th - * long, the center is still at 1/2 of VEHICLE_LENGTH. Basically FS#3569. */ - return stop - VEHICLE_LENGTH / 2; + * stop location of the train. */ + return stop - (v->gcache.cached_veh_length + 1) / 2; } @@ -1382,25 +1379,66 @@ CommandCost CmdSellRailWagon(DoCommandFlag flags, Vehicle *t, uint16 data, uint3 void Train::UpdateDeltaXY(Direction direction) { -#define MKIT(a, b, c, d) ((a & 0xFF) << 24) | ((b & 0xFF) << 16) | ((c & 0xFF) << 8) | ((d & 0xFF) << 0) - static const uint32 _delta_xy_table[8] = { - MKIT(3, 3, -1, -1), - MKIT(3, 7, -1, -3), - MKIT(3, 3, -1, -1), - MKIT(7, 3, -3, -1), - MKIT(3, 3, -1, -1), - MKIT(3, 7, -1, -3), - MKIT(3, 3, -1, -1), - MKIT(7, 3, -3, -1), - }; -#undef MKIT + /* Set common defaults. */ + this->x_offs = -1; + this->y_offs = -1; + this->x_extent = 3; + this->y_extent = 3; + this->z_extent = 6; + this->x_bb_offs = 0; + this->y_bb_offs = 0; + + if (!IsDiagonalDirection(direction)) { + static const int _sign_table[] = + { + // x, y + -1, -1, // DIR_N + -1, 1, // DIR_E + 1, 1, // DIR_S + 1, -1, // DIR_W + }; + + int half_shorten = (VEHICLE_LENGTH - this->gcache.cached_veh_length) / 2; + + /* For all straight directions, move the bound box to the centre of the vehicle, but keep the size. */ + this->x_offs -= half_shorten * _sign_table[direction]; + this->y_offs -= half_shorten * _sign_table[direction + 1]; + this->x_extent += this->x_bb_offs = half_shorten * _sign_table[direction]; + this->y_extent += this->y_bb_offs = half_shorten * _sign_table[direction + 1]; + } else { + switch (direction) { + /* Shorten southern corner of the bounding box according the vehicle length + * and center the bounding box on the vehicle. */ + case DIR_NE: + this->x_offs = 1 - (this->gcache.cached_veh_length + 1) / 2; + this->x_extent = this->gcache.cached_veh_length - 1; + this->x_bb_offs = -1; + break; - uint32 x = _delta_xy_table[direction]; - this->x_offs = GB(x, 0, 8); - this->y_offs = GB(x, 8, 8); - this->x_extent = GB(x, 16, 8); - this->y_extent = GB(x, 24, 8); - this->z_extent = 6; + case DIR_NW: + this->y_offs = 1 - (this->gcache.cached_veh_length + 1) / 2; + this->y_extent = this->gcache.cached_veh_length - 1; + this->y_bb_offs = -1; + break; + + /* Move northern corner of the bounding box down according to vehicle length + * and center the bounding box on the vehicle. */ + case DIR_SW: + this->x_offs = 1 + (this->gcache.cached_veh_length + 1) / 2 - VEHICLE_LENGTH; + this->x_extent = VEHICLE_LENGTH - 1; + this->x_bb_offs = VEHICLE_LENGTH - this->gcache.cached_veh_length - 1; + break; + + case DIR_SE: + this->y_offs = 1 + (this->gcache.cached_veh_length + 1) / 2 - VEHICLE_LENGTH; + this->y_extent = VEHICLE_LENGTH - 1; + this->y_bb_offs = VEHICLE_LENGTH - this->gcache.cached_veh_length - 1; + break; + + default: + NOT_REACHED(); + } + } } /** Mark a train as stuck and stop it if it isn't stopped right now. */ @@ -1478,7 +1516,7 @@ static void UpdateStatusAfterSwap(Train *v) v->UpdateViewport(true, true); } -static void ReverseTrainSwapVeh(Train *v, int l, int r) +void ReverseTrainSwapVeh(Train *v, int l, int r) { Train *a, *b; @@ -1626,7 +1664,7 @@ static void AdvanceWagonsBeforeSwap(Train *v) last = last->Previous(); first = first->Next(); - int differential = base->gcache.cached_veh_length - last->gcache.cached_veh_length; + int differential = base->CalcNextVehicleOffset() - last->CalcNextVehicleOffset(); /* do not update images now * negative differential will be handled in AdvanceWagonsAfterSwap() */ @@ -1686,7 +1724,7 @@ static void AdvanceWagonsAfterSwap(Train *v) last = last->Previous(); first = first->Next(); - int differential = last->gcache.cached_veh_length - base->gcache.cached_veh_length; + int differential = last->CalcNextVehicleOffset() - base->CalcNextVehicleOffset(); /* do not update images now */ for (int i = 0; i < differential; i++) TrainController(first, (nomove ? last->Next() : NULL)); @@ -1697,7 +1735,7 @@ static void AdvanceWagonsAfterSwap(Train *v) } -static void ReverseTrainDirection(Train *v) +void ReverseTrainDirection(Train *v) { if (IsRailDepotTile(v->tile)) { InvalidateWindowData(WC_VEHICLE_DEPOT, v->tile); @@ -2840,7 +2878,8 @@ static Vehicle *FindTrainCollideEnum(Vehicle *v, void *data) if (hash & ~15) return NULL; /* Slower check using multiplication */ - if (x_diff * x_diff + y_diff * y_diff > 25) return NULL; + int min_diff = (Train::From(v)->gcache.cached_veh_length + 1) / 2 + (tcc->v->gcache.cached_veh_length + 1) / 2 - 1; + if (x_diff * x_diff + y_diff * y_diff > min_diff * min_diff) return NULL; /* Happens when there is a train under bridge next to bridge head */ if (abs(v->z_pos - tcc->v->z_pos) > 5) return NULL; @@ -2906,7 +2945,14 @@ static Vehicle *CheckTrainAtSignal(Vehicle *v, void *data) return t; } -static void TrainController(Train *v, Vehicle *nomove) +/** + * Move a vehicle chain one movement stop forwards. + * @param v First vehicle to move. + * @param nomove Stop moving this and all following vehicles. + * @param reverse Set to false to not execute the vehicle reversing. This does not change any other logic. + * @return True if the vehicle could be moved forward, false otherwise. + */ +bool TrainController(Train *v, Vehicle *nomove, bool reverse) { Train *first = v->First(); Train *prev; @@ -2930,7 +2976,7 @@ static void TrainController(Train *v, Vehicle *nomove) /* Not inside depot */ /* Reverse when we are at the end of the track already, do not move to the new position */ - if (v->IsFrontEngine() && !TrainCheckIfLineEnds(v)) return; + if (v->IsFrontEngine() && !TrainCheckIfLineEnds(v, reverse)) return false; uint32 r = VehicleEnterTile(v, gp.new_tile, gp.x, gp.y); if (HasBit(r, VETS_CANNOT_ENTER)) { @@ -2997,13 +3043,13 @@ static void TrainController(Train *v, Vehicle *nomove) Trackdir i = FindFirstTrackdir(trackdirbits); /* Don't handle stuck trains here. */ - if (HasBit(v->flags, VRF_TRAIN_STUCK)) return; + if (HasBit(v->flags, VRF_TRAIN_STUCK)) return false; if (!HasSignalOnTrackdir(gp.new_tile, ReverseTrackdir(i))) { v->cur_speed = 0; v->subspeed = 0; v->progress = 255 - 100; - if (!_settings_game.pf.reverse_at_signals || ++v->wait_counter < _settings_game.pf.wait_oneway_signal * 20) return; + if (!_settings_game.pf.reverse_at_signals || ++v->wait_counter < _settings_game.pf.wait_oneway_signal * 20) return false; } else if (HasSignalOnTrackdir(gp.new_tile, i)) { v->cur_speed = 0; v->subspeed = 0; @@ -3015,7 +3061,7 @@ static void TrainController(Train *v, Vehicle *nomove) exitdir = ReverseDiagDir(exitdir); /* check if a train is waiting on the other side */ - if (!HasVehicleOnPos(o_tile, &exitdir, &CheckTrainAtSignal)) return; + if (!HasVehicleOnPos(o_tile, &exitdir, &CheckTrainAtSignal)) return false; } } @@ -3026,7 +3072,7 @@ static void TrainController(Train *v, Vehicle *nomove) if (!_settings_game.pf.reverse_at_signals && !HasOnewaySignalBlockingTrackdir(gp.new_tile, i) && UpdateSignalsOnSegment(v->tile, enterdir, v->owner) == SIGSEG_PBS) { v->wait_counter = 0; - return; + return false; } goto reverse_train_direction; } else { @@ -3209,17 +3255,21 @@ static void TrainController(Train *v, Vehicle *nomove) if (direction_changed) first->tcache.cached_max_curve_speed = first->GetCurveSpeedLimit(); - return; + return true; invalid_rail: /* We've reached end of line?? */ if (prev != NULL) error("Disconnecting train"); reverse_train_direction: - v->wait_counter = 0; - v->cur_speed = 0; - v->subspeed = 0; - ReverseTrainDirection(v); + if (reverse) { + v->wait_counter = 0; + v->cur_speed = 0; + v->subspeed = 0; + ReverseTrainDirection(v); + } + + return false; } /** @@ -3389,9 +3439,10 @@ static const uint16 _breakdown_speeds[16] = { * * @param v front train engine * @param signal not line end, just a red signal + * @param reverse Set to false to not execute the vehicle reversing. This does not change any other logic. * @return true iff we did NOT have to reverse */ -static bool TrainApproachingLineEnd(Train *v, bool signal) +static bool TrainApproachingLineEnd(Train *v, bool signal, bool reverse) { /* Calc position within the current tile */ uint x = v->x_pos & 0xF; @@ -3413,13 +3464,12 @@ static bool TrainApproachingLineEnd(Train *v, bool signal) /* Do not reverse when approaching red signal. Make sure the vehicle's front * does not cross the tile boundary when we do reverse, but as the vehicle's * location is based on their center, use half a vehicle's length as offset. - * Actually, the center of all vehicles is half a normal vehicle's length - * from the front of the vehicle, so even in case the vehicle is 1/8th - * long, the center is still at 1/2 of VEHICLE_LENGTH. Basically FS#3569. */ - if (!signal && x + VEHICLE_LENGTH / 2 >= TILE_SIZE) { + * Multiply the half-length by two for straight directions to compensate that + * we only get odd x offsets there. */ + if (!signal && x + (v->gcache.cached_veh_length + 1) / 2 * (IsDiagonalDirection(v->direction) ? 1 : 2) >= TILE_SIZE) { /* we are too near the tile end, reverse now */ v->cur_speed = 0; - ReverseTrainDirection(v); + if (reverse) ReverseTrainDirection(v); return false; } @@ -3491,9 +3541,10 @@ static TileIndex TrainApproachingCrossingTile(const Train *v) * Checks for line end. Also, bars crossing at next tile if needed * * @param v vehicle we are checking + * @param reverse Set to false to not execute the vehicle reversing. This does not change any other logic. * @return true iff we did NOT have to reverse */ -static bool TrainCheckIfLineEnds(Train *v) +static bool TrainCheckIfLineEnds(Train *v, bool reverse) { /* First, handle broken down train */ @@ -3531,11 +3582,11 @@ static bool TrainCheckIfLineEnds(Train *v) /* no suitable trackbits at all || unusable rail (wrong type or owner) */ if (bits == TRACK_BIT_NONE || !CheckCompatibleRail(v, tile)) { - return TrainApproachingLineEnd(v, false); + return TrainApproachingLineEnd(v, false, reverse); } /* approaching red signal */ - if ((trackdirbits & red_signals) != 0) return TrainApproachingLineEnd(v, true); + if ((trackdirbits & red_signals) != 0) return TrainApproachingLineEnd(v, true, reverse); /* approaching a rail/road crossing? then make it red */ if (IsLevelCrossingTile(tile)) MaybeBarCrossingWithSound(tile); |