Merge branch 'ship_collision_avoidance' into jgrpp

This commit is contained in:
Jonathan G Rennison
2018-01-28 20:58:18 +00:00
3 changed files with 54 additions and 36 deletions

View File

@@ -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. */ /** 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 }, { DIAGDIR_NE, INVALID_DIAGDIR, DIAGDIR_SW, INVALID_DIAGDIR },
{ INVALID_DIAGDIR, DIAGDIR_SE, INVALID_DIAGDIR, DIAGDIR_NW }, { INVALID_DIAGDIR, DIAGDIR_SE, INVALID_DIAGDIR, DIAGDIR_NW },
{ INVALID_DIAGDIR, DIAGDIR_NE, DIAGDIR_NW, INVALID_DIAGDIR }, { INVALID_DIAGDIR, DIAGDIR_NE, DIAGDIR_NW, INVALID_DIAGDIR },

View File

@@ -15,6 +15,8 @@
#include "vehicle_base.h" #include "vehicle_base.h"
#include "water_map.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); void GetShipSpriteSize(EngineID engine, uint &width, uint &height, int &xoffs, int &yoffs, EngineImageType image_type);
WaterClass GetEffectiveWaterClass(TileIndex tile); WaterClass GetEffectiveWaterClass(TileIndex tile);

View File

@@ -548,34 +548,30 @@ static const byte _ship_subcoord[4][6][3] = {
} }
}; };
/* Used for finding tile exit direction using the enter direction and following track */ /** Temporary data storage for testing collisions. */
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 { struct ShipCollideChecker {
TrackBits track_bits; ///< Trackbits (track that was chosen by the pathfinder converted in trackbits.) . TrackBits track_bits; ///< Pathfinder chosen track converted to trackbits, or is v->state of requesting ship. (one bit set)
TileIndex tile; ///< Tile of where ship was found, used to determine distance between ships. 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. */ /** Helper function for collision avoidance. */
static Vehicle *FindShipOnTile(Vehicle *v, void *data) 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; ShipCollideChecker *scc = (ShipCollideChecker*)data;
scc->tile = v->tile;
/* Don't detect vehicles on different parallel tracks. */ /* Don't detect vehicles on different parallel tracks. */
TrackBits bits = scc->track_bits | Ship::From(v)->state; TrackBits bits = scc->track_bits | Ship::From(v)->state;
if (bits == TRACK_BIT_HORZ || bits == TRACK_BIT_VERT) return NULL; 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; return v;
} }
@@ -584,42 +580,62 @@ static Vehicle *FindShipOnTile(Vehicle *v, void *data)
* @param tile Tile that the ship is about to enter. * @param tile Tile that the ship is about to enter.
* @param v Ship that does the request. * @param v Ship that does the request.
* @param tracks The available tracks that could be followed. * @param tracks The available tracks that could be followed.
* @param track The track that the pathfinder assigned. * @param track_old The track that the pathfinder assigned.
* @param diagdir The DiagDirection that tile will be entered. * @param diagdir The DiagDirection that tile will be entered.
* @return The new track if found. * @return The new track if found.
*/ */
static void CheckDistanceBetweenShips(TileIndex tile, Ship *v, TrackBits tracks, Track *track_old, DiagDirection diagdir) 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. 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; Track track = *track_old;
TrackBits track_bits = TrackToTrackBits(track);
TileIndex tile_plus_one = TileAddByDiagDir(tile, _diagdir_tile_exit[track][diagdir]); /* 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 && 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; 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; ShipCollideChecker scc;
scc.z_pos = v->z_pos;
scc.track_bits = track_bits;
bool found = HasVehicleOnPos(tile, &scc, FindShipOnTile);
scc.track_bits = v->state; scc.track_bits = v->state;
bool found = HasVehicleOnPos(tile_plus_one, &scc, &FindShipOnTile); if (!found) found = HasVehicleOnPos(tile_plus_one, &scc, FindShipOnTile);
scc.track_bits = TrackToTrackBits(track); scc.track_bits = track_bits;
if (!found) found = HasVehicleOnPos(tile, &scc, &FindShipOnTile); if (!found) found = HasVehicleOnPos(tile_plus_two, &scc, FindShipOnTile);
if (found) { if (found) {
/* Speed adjustment related to distance. */ /* 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) { /* Clean none wanted trackbits, including pathfinder track, TRACK_BIT_WORMHOLE and no 90 degree turns. */
default: break; tracks = IsDiagonalTrack(track) ? KillFirstBit(tracks) : (tracks & TRACK_BIT_CROSS);
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; /* Just follow track 1 tile and see if there is a track to follow. (try not to bang in coast or ship) */
case TRACK_BIT_3WAY_SW: track == TRACK_LEFT ? track = TRACK_X : track = TRACK_LOWER; break; while (tracks != TRACK_BIT_NONE) {
case TRACK_BIT_3WAY_NW: track == TRACK_UPPER ? track = TRACK_Y : track = TRACK_LEFT; break; track = RemoveFirstTrack(&tracks);
}
/* Don't bump in coast, don't get stuck. */ TileIndex tile_check = TileAddByDiagDir(tile, _ship_search_directions[track][diagdir]);
if (track != *track_old && !IsWaterTile(TileAddByDiagDir(tile, _diagdir_tile_exit[track][diagdir]))) return; 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; *track_old = track;
break;
}
}
} }
} }
@@ -715,7 +731,7 @@ static void ShipController(Ship *v)
if (track == INVALID_TRACK) goto reverse_direction; if (track == INVALID_TRACK) goto reverse_direction;
/* Try to avoid collision and keep distance between ships. */ /* 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]; b = _ship_subcoord[diagdir][track];