From 828e9bece29eea24863b4c89db0c0f84556ec2dc Mon Sep 17 00:00:00 2001 From: HackaLittleBit Date: Thu, 25 Jan 2018 18:22:53 +0000 Subject: [PATCH 1/4] De-duplicate ship entry to exit direction map table --- src/pathfinder/opf/opf_ship.cpp | 2 +- src/ship.h | 2 ++ src/ship_cmd.cpp | 14 ++------------ 3 files changed, 5 insertions(+), 13 deletions(-) diff --git a/src/pathfinder/opf/opf_ship.cpp b/src/pathfinder/opf/opf_ship.cpp index 023c6a4a09..31a1e30167 100644 --- a/src/pathfinder/opf/opf_ship.cpp +++ b/src/pathfinder/opf/opf_ship.cpp @@ -121,7 +121,7 @@ static void OPFShipFollowTrack(TileIndex tile, DiagDirection direction, TrackPat } /** Directions to search towards given track bits and the ship's enter direction. */ -static const DiagDirection _ship_search_directions[6][4] = { +extern const DiagDirection _ship_search_directions[6][4] = { { DIAGDIR_NE, INVALID_DIAGDIR, DIAGDIR_SW, INVALID_DIAGDIR }, { INVALID_DIAGDIR, DIAGDIR_SE, INVALID_DIAGDIR, DIAGDIR_NW }, { INVALID_DIAGDIR, DIAGDIR_NE, DIAGDIR_NW, INVALID_DIAGDIR }, diff --git a/src/ship.h b/src/ship.h index 18f04c8faf..61b25e682f 100644 --- a/src/ship.h +++ b/src/ship.h @@ -15,6 +15,8 @@ #include "vehicle_base.h" #include "water_map.h" +extern const DiagDirection _ship_search_directions[TRACK_END][DIAGDIR_END]; + void GetShipSpriteSize(EngineID engine, uint &width, uint &height, int &xoffs, int &yoffs, EngineImageType image_type); WaterClass GetEffectiveWaterClass(TileIndex tile); diff --git a/src/ship_cmd.cpp b/src/ship_cmd.cpp index 4bbb8bf19e..0224e58f79 100644 --- a/src/ship_cmd.cpp +++ b/src/ship_cmd.cpp @@ -509,16 +509,6 @@ static const byte _ship_subcoord[4][6][3] = { } }; -/* Used for finding tile exit direction using the enter direction and following track */ -static const DiagDirection _diagdir_tile_exit[TRACK_END][DIAGDIR_END] = { - { DIAGDIR_NE , INVALID_DIAGDIR, DIAGDIR_SW , INVALID_DIAGDIR}, // TRACK_X = 0 - { INVALID_DIAGDIR, DIAGDIR_SE , INVALID_DIAGDIR, DIAGDIR_NW }, // TRACK_Y = 1 - { INVALID_DIAGDIR, DIAGDIR_NE , DIAGDIR_NW , INVALID_DIAGDIR}, // TRACK_UPPER = 2 - { DIAGDIR_SE , INVALID_DIAGDIR, INVALID_DIAGDIR, DIAGDIR_SW }, // TRACK_LOWER = 3 - { DIAGDIR_NW , DIAGDIR_SW , INVALID_DIAGDIR, INVALID_DIAGDIR}, // TRACK_LEFT = 4 - { INVALID_DIAGDIR, INVALID_DIAGDIR, DIAGDIR_SE , DIAGDIR_NE } // TRACK_RIGHT = 5 -}; - struct ShipCollideChecker { TrackBits track_bits; ///< Trackbits (track that was chosen by the pathfinder converted in trackbits.) . @@ -556,7 +546,7 @@ static void CheckDistanceBetweenShips(TileIndex tile, Ship *v, TrackBits tracks, Track track = *track_old; - TileIndex tile_plus_one = TileAddByDiagDir(tile, _diagdir_tile_exit[track][diagdir]); + TileIndex tile_plus_one = TileAddByDiagDir(tile, _ship_search_directions[track][diagdir]); if (!IsValidTile(tile_plus_one)) tile_plus_one = tile; ShipCollideChecker scc; @@ -579,7 +569,7 @@ static void CheckDistanceBetweenShips(TileIndex tile, Ship *v, TrackBits tracks, case TRACK_BIT_3WAY_NW: track == TRACK_UPPER ? track = TRACK_Y : track = TRACK_LEFT; break; } /* Don't bump in coast, don't get stuck. */ - if (track != *track_old && !IsWaterTile(TileAddByDiagDir(tile, _diagdir_tile_exit[track][diagdir]))) return; + if (track != *track_old && !IsWaterTile(TileAddByDiagDir(tile, _ship_search_directions[track][diagdir]))) return; *track_old = track; } } From e2d3c95ae14dbb9941698d9a367e7d6515f6b502 Mon Sep 17 00:00:00 2001 From: HackaLittleBit Date: Thu, 25 Jan 2018 18:27:07 +0000 Subject: [PATCH 2/4] Enable ships to pass on both sides of other ships. --- src/ship_cmd.cpp | 69 +++++++++++++++++++++++++++++++----------------- 1 file changed, 45 insertions(+), 24 deletions(-) diff --git a/src/ship_cmd.cpp b/src/ship_cmd.cpp index 0224e58f79..bce2534ee7 100644 --- a/src/ship_cmd.cpp +++ b/src/ship_cmd.cpp @@ -509,68 +509,89 @@ static const byte _ship_subcoord[4][6][3] = { } }; +/** Temporary data storage for testing collisions. */ struct ShipCollideChecker { - TrackBits track_bits; ///< Trackbits (track that was chosen by the pathfinder converted in trackbits.) . - TileIndex tile; ///< Tile of where ship was found, used to determine distance between ships. + TrackBits track_bits; ///< Pathfinder chosen track converted to trackbits, or is v->state of requesting ship. (one bit set) + TileIndex tile; ///< Tile where ship was found, used to determine distance between ships. }; /** Helper function for collision avoidance. */ static Vehicle *FindShipOnTile(Vehicle *v, void *data) { - if (v->type != VEH_SHIP || v->vehstatus & VS_STOPPED) return NULL; + if (v->type != VEH_SHIP) return NULL; ShipCollideChecker *scc = (ShipCollideChecker*)data; - scc->tile = v->tile; /* Don't detect vehicles on different parallel tracks. */ TrackBits bits = scc->track_bits | Ship::From(v)->state; if (bits == TRACK_BIT_HORZ || bits == TRACK_BIT_VERT) return NULL; + scc->tile = v->tile; + return v; } /** * If there is imminent collision or worse, direction and speed will be adjusted. - * @param tile Tile that the ship is about to enter. - * @param v Ship that does the request. - * @param tracks The available tracks that could be followed. - * @param track The track that the pathfinder assigned. - * @param diagdir The DiagDirection that tile will be entered. + * @param tile Tile that the ship is about to enter. + * @param v Ship that does the request. + * @param tracks The available tracks that could be followed. + * @param track_old The track that the pathfinder assigned. + * @param diagdir The DiagDirection that tile will be entered. * @return The new track if found. */ static void CheckDistanceBetweenShips(TileIndex tile, Ship *v, TrackBits tracks, Track *track_old, DiagDirection diagdir) { - if (!_settings_game.pf.forbid_90_deg || !_settings_game.vehicle.ship_collision_avoidance) return; if (DistanceManhattan(v->dest_tile, tile) <= 3 && !v->current_order.IsType(OT_GOTO_WAYPOINT)) return; // No checking close to docks and depots. Track track = *track_old; + TrackBits track_bits = TrackToTrackBits(track); + + /* Only check for collision when pathfinder did not change direction. + * This is done in order to keep ships moving towards the intended target. */ + TrackBits combine = (v->state | track_bits); + if (combine != TRACK_BIT_HORZ && combine != TRACK_BIT_VERT && combine != track_bits) return; TileIndex tile_plus_one = TileAddByDiagDir(tile, _ship_search_directions[track][diagdir]); if (!IsValidTile(tile_plus_one)) tile_plus_one = tile; + TileIndex tile_plus_two = TileAddByDiagDir(tile_plus_one, diagdir); + if (!IsValidTile(tile_plus_two)) tile_plus_two = tile_plus_one; ShipCollideChecker scc; + scc.track_bits = track_bits; + bool found = HasVehicleOnPos(tile, &scc, FindShipOnTile); scc.track_bits = v->state; - bool found = HasVehicleOnPos(tile_plus_one, &scc, &FindShipOnTile); - scc.track_bits = TrackToTrackBits(track); - if (!found) found = HasVehicleOnPos(tile, &scc, &FindShipOnTile); + if (!found) found = HasVehicleOnPos(tile_plus_one, &scc, FindShipOnTile); + scc.track_bits = track_bits; + if (!found) found = HasVehicleOnPos(tile_plus_two, &scc, FindShipOnTile); if (found) { /* Speed adjustment related to distance. */ - v->cur_speed /= scc.tile == tile_plus_one ? 2 : 4; + v->cur_speed /= scc.tile == tile ? 8 : 2; - switch (tracks) { - default: break; - case TRACK_BIT_3WAY_NE: track == TRACK_RIGHT ? track = TRACK_X : track = TRACK_UPPER; break; - case TRACK_BIT_3WAY_SE: track == TRACK_LOWER ? track = TRACK_Y : track = TRACK_RIGHT; break; - case TRACK_BIT_3WAY_SW: track == TRACK_LEFT ? track = TRACK_X : track = TRACK_LOWER; break; - case TRACK_BIT_3WAY_NW: track == TRACK_UPPER ? track = TRACK_Y : track = TRACK_LEFT; break; + /* Clean none wanted trackbits, including pathfinder track and no 90 degree turns. */ + tracks = IsDiagonalTrack(track) ? KillFirstBit(tracks) : (tracks & TRACK_BIT_CROSS); + + /* Just follow track 1 tile and see if there is a track to follow. (try not to bang in coast or ship) */ + while (tracks != TRACK_BIT_NONE) { + track = RemoveFirstTrack(&tracks); + + TileIndex tile_check = TileAddByDiagDir(tile, _ship_search_directions[track][diagdir]); + if (!IsValidTile(tile_check)) continue; + + if (HasVehicleOnPos(tile_check, &scc, &FindShipOnTile)) continue; + + TrackBits bits = GetAvailShipTracks(tile_check, _ship_search_directions[track][diagdir]); + if (!IsDiagonalTrack(track)) bits &= TRACK_BIT_CROSS; // No 90 degree turns. + + if (bits != INVALID_TRACK_BIT && bits != TRACK_BIT_NONE) { + *track_old = track; + break; + } } - /* Don't bump in coast, don't get stuck. */ - if (track != *track_old && !IsWaterTile(TileAddByDiagDir(tile, _ship_search_directions[track][diagdir]))) return; - *track_old = track; } } @@ -667,7 +688,7 @@ static void ShipController(Ship *v) if (track == INVALID_TRACK) goto reverse_direction; /* Try to avoid collision and keep distance between ships. */ - CheckDistanceBetweenShips(gp.new_tile, v, tracks, &track, diagdir); + if (_settings_game.vehicle.ship_collision_avoidance) CheckDistanceBetweenShips(gp.new_tile, v, tracks, &track, diagdir); b = _ship_subcoord[diagdir][track]; From 354ecfdf3a19e261742e14110ca7d7b27541c461 Mon Sep 17 00:00:00 2001 From: HackaLittleBit Date: Thu, 25 Jan 2018 18:31:11 +0000 Subject: [PATCH 3/4] Handle ship collision detection on aquaducts. --- src/ship_cmd.cpp | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/src/ship_cmd.cpp b/src/ship_cmd.cpp index bce2534ee7..14c2f86fd0 100644 --- a/src/ship_cmd.cpp +++ b/src/ship_cmd.cpp @@ -514,6 +514,7 @@ struct ShipCollideChecker { TrackBits track_bits; ///< Pathfinder chosen track converted to trackbits, or is v->state of requesting ship. (one bit set) TileIndex tile; ///< Tile where ship was found, used to determine distance between ships. + int32 z_pos; ///< z_pos of requesting ship. }; /** Helper function for collision avoidance. */ @@ -527,6 +528,9 @@ static Vehicle *FindShipOnTile(Vehicle *v, void *data) TrackBits bits = scc->track_bits | Ship::From(v)->state; if (bits == TRACK_BIT_HORZ || bits == TRACK_BIT_VERT) return NULL; + /* Don't detect ships passing on aquaduct. */ + if (abs(v->z_pos - scc->z_pos) >= 8) return NULL; + scc->tile = v->tile; return v; @@ -551,7 +555,7 @@ static void CheckDistanceBetweenShips(TileIndex tile, Ship *v, TrackBits tracks, /* Only check for collision when pathfinder did not change direction. * This is done in order to keep ships moving towards the intended target. */ TrackBits combine = (v->state | track_bits); - if (combine != TRACK_BIT_HORZ && combine != TRACK_BIT_VERT && combine != track_bits) return; + if (combine != TRACK_BIT_HORZ && combine != TRACK_BIT_VERT && combine != track_bits && v->state != TRACK_BIT_WORMHOLE) return; TileIndex tile_plus_one = TileAddByDiagDir(tile, _ship_search_directions[track][diagdir]); if (!IsValidTile(tile_plus_one)) tile_plus_one = tile; @@ -559,6 +563,7 @@ static void CheckDistanceBetweenShips(TileIndex tile, Ship *v, TrackBits tracks, if (!IsValidTile(tile_plus_two)) tile_plus_two = tile_plus_one; ShipCollideChecker scc; + scc.z_pos = v->z_pos; scc.track_bits = track_bits; bool found = HasVehicleOnPos(tile, &scc, FindShipOnTile); @@ -572,7 +577,7 @@ static void CheckDistanceBetweenShips(TileIndex tile, Ship *v, TrackBits tracks, /* Speed adjustment related to distance. */ v->cur_speed /= scc.tile == tile ? 8 : 2; - /* Clean none wanted trackbits, including pathfinder track and no 90 degree turns. */ + /* Clean none wanted trackbits, including pathfinder track, TRACK_BIT_WORMHOLE and no 90 degree turns. */ tracks = IsDiagonalTrack(track) ? KillFirstBit(tracks) : (tracks & TRACK_BIT_CROSS); /* Just follow track 1 tile and see if there is a track to follow. (try not to bang in coast or ship) */ @@ -714,6 +719,12 @@ static void ShipController(Ship *v) } else { /* On a bridge */ if (!IsTileType(gp.new_tile, MP_TUNNELBRIDGE) || !HasBit(VehicleEnterTile(v, gp.new_tile, gp.x, gp.y), VETS_ENTERED_WORMHOLE)) { + if (_settings_game.vehicle.ship_collision_avoidance && gp.old_tile != gp.new_tile){ + DiagDirection diagdir = DiagdirBetweenTiles(gp.old_tile, gp.new_tile); + track = AxisToTrack(DiagDirToAxis(diagdir)); + CheckDistanceBetweenShips(gp.new_tile, v, v->state, &track, diagdir); + v->tile = gp.new_tile; // Note! Trains and cars keep v->tile fixed on tunnel entrance ships not!! (avoid spaghetti code) + } v->x_pos = gp.x; v->y_pos = gp.y; v->UpdatePosition(); From e4a10e9313a6b6f6fe290407bb8545cbcb72d0ba Mon Sep 17 00:00:00 2001 From: HackaLittleBit Date: Fri, 26 Jan 2018 20:06:50 +0000 Subject: [PATCH 4/4] Revert part of collision detection on aqueducts, on aqueducts collision detection will not be done. --- src/ship_cmd.cpp | 6 ------ 1 file changed, 6 deletions(-) diff --git a/src/ship_cmd.cpp b/src/ship_cmd.cpp index 14c2f86fd0..4f95e93255 100644 --- a/src/ship_cmd.cpp +++ b/src/ship_cmd.cpp @@ -719,12 +719,6 @@ static void ShipController(Ship *v) } else { /* On a bridge */ if (!IsTileType(gp.new_tile, MP_TUNNELBRIDGE) || !HasBit(VehicleEnterTile(v, gp.new_tile, gp.x, gp.y), VETS_ENTERED_WORMHOLE)) { - if (_settings_game.vehicle.ship_collision_avoidance && gp.old_tile != gp.new_tile){ - DiagDirection diagdir = DiagdirBetweenTiles(gp.old_tile, gp.new_tile); - track = AxisToTrack(DiagDirToAxis(diagdir)); - CheckDistanceBetweenShips(gp.new_tile, v, v->state, &track, diagdir); - v->tile = gp.new_tile; // Note! Trains and cars keep v->tile fixed on tunnel entrance ships not!! (avoid spaghetti code) - } v->x_pos = gp.x; v->y_pos = gp.y; v->UpdatePosition();