From c5888dad9f82fc3b6233055417c8031aa586d5c1 Mon Sep 17 00:00:00 2001 From: HackaLittleBit Date: Mon, 22 Jan 2018 23:49:23 +0000 Subject: [PATCH] Changes to ship collision avoidance algorithm * Change some names and create a seperate procedure for finding ships. * Check if destiny tile is buoy. If so, distance checking continues. * Remove water tile check. It was done before and coast can't have 3 way tracks. * Add a check for map edges. * Add speed adjustment related to distance between ships * Do not detect ships on tiles with parallel tracks --- src/ship_cmd.cpp | 93 ++++++++++++++++++++++++++++++++++-------------- 1 file changed, 67 insertions(+), 26 deletions(-) diff --git a/src/ship_cmd.cpp b/src/ship_cmd.cpp index f2085a996a..4bbb8bf19e 100644 --- a/src/ship_cmd.cpp +++ b/src/ship_cmd.cpp @@ -509,14 +509,20 @@ static const byte _ship_subcoord[4][6][3] = { } }; -/* Used to find DiagDirection from tile to next tile if track is followed */ -static const DiagDirection _diagdir_to_next_tile[6][4] = { - { DIAGDIR_NE, DIAGDIR_SE, DIAGDIR_SW, DIAGDIR_NW }, - { DIAGDIR_NE, DIAGDIR_SE, DIAGDIR_SW, DIAGDIR_NW }, - { DIAGDIR_SE, DIAGDIR_NE, DIAGDIR_NW, DIAGDIR_SW }, - { DIAGDIR_SE, DIAGDIR_NW, DIAGDIR_NE, DIAGDIR_SW }, - { DIAGDIR_NW, DIAGDIR_SW, DIAGDIR_SE, DIAGDIR_NE }, - { DIAGDIR_SW, DIAGDIR_NW, DIAGDIR_SE, DIAGDIR_NE } +/* 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.) . + TileIndex tile; ///< Tile of where ship was found, used to determine distance between ships. }; /** Helper function for collision avoidance. */ @@ -524,9 +530,60 @@ static Vehicle *FindShipOnTile(Vehicle *v, void *data) { if (v->type != VEH_SHIP || v->vehstatus & VS_STOPPED) 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; + 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. + * @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; + + TileIndex tile_plus_one = TileAddByDiagDir(tile, _diagdir_tile_exit[track][diagdir]); + if (!IsValidTile(tile_plus_one)) tile_plus_one = tile; + + ShipCollideChecker scc; + + 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) { + + /* Speed adjustment related to distance. */ + v->cur_speed /= scc.tile == tile_plus_one ? 2 : 4; + + 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; + } + /* Don't bump in coast, don't get stuck. */ + if (track != *track_old && !IsWaterTile(TileAddByDiagDir(tile, _diagdir_tile_exit[track][diagdir]))) return; + *track_old = track; + } +} + static void ShipController(Ship *v) { uint32 r; @@ -619,24 +676,8 @@ static void ShipController(Ship *v) track = ChooseShipTrack(v, gp.new_tile, diagdir, tracks); if (track == INVALID_TRACK) goto reverse_direction; - /* Try to avoid collision and keep distance between each other. */ - if (_settings_game.pf.forbid_90_deg && _settings_game.vehicle.ship_collision_avoidance && DistanceManhattan(v->dest_tile, gp.new_tile) > 3) { - if (HasVehicleOnPos(gp.new_tile, NULL, &FindShipOnTile) || - HasVehicleOnPos(TileAddByDiagDir(gp.new_tile, _diagdir_to_next_tile[track][diagdir]), NULL, &FindShipOnTile)) { - - v->cur_speed /= 4; // Go quarter speed. - - Track old = track; - 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; - } - if (!IsWaterTile(gp.new_tile) || !IsWaterTile(TileAddByDiagDir(gp.new_tile, _diagdir_to_next_tile[track][diagdir]))) track = old; // Don't bump in coast, don't get stuck. - } - } + /* Try to avoid collision and keep distance between ships. */ + CheckDistanceBetweenShips(gp.new_tile, v, tracks, &track, diagdir); b = _ship_subcoord[diagdir][track];