mirror of
https://github.com/OpenTTD/OpenTTD.git
synced 2025-01-31 11:23:21 +00:00
parent
4dd5f994be
commit
3991e76c96
337
src/ship_cmd.cpp
337
src/ship_cmd.cpp
@ -400,29 +400,30 @@ static bool CheckShipLeaveDepot(Ship *v)
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool ShipAccelerate(Vehicle *v)
|
||||
/**
|
||||
* Accelerates the ship towards its target speed.
|
||||
* @param v Ship to accelerate.
|
||||
* @return Number of steps to move the ship.
|
||||
*/
|
||||
static uint ShipAccelerate(Vehicle *v)
|
||||
{
|
||||
uint spd;
|
||||
byte t;
|
||||
uint speed;
|
||||
|
||||
spd = std::min<uint>(v->cur_speed + 1, v->vcache.cached_max_speed);
|
||||
spd = std::min<uint>(spd, v->current_order.GetMaxSpeed() * 2);
|
||||
speed = std::min<uint>(v->cur_speed + 1, v->vcache.cached_max_speed);
|
||||
speed = std::min<uint>(speed, v->current_order.GetMaxSpeed() * 2);
|
||||
|
||||
/* updates statusbar only if speed have changed to save CPU time */
|
||||
if (spd != v->cur_speed) {
|
||||
v->cur_speed = spd;
|
||||
if (speed != v->cur_speed) {
|
||||
v->cur_speed = speed;
|
||||
SetWindowWidgetDirty(WC_VEHICLE_VIEW, v->index, WID_VV_START_STOP);
|
||||
}
|
||||
|
||||
/* Convert direction-independent speed into direction-dependent speed. (old movement method) */
|
||||
spd = v->GetOldAdvanceSpeed(spd);
|
||||
|
||||
if (spd == 0) return false;
|
||||
if ((byte)++spd == 0) return true;
|
||||
|
||||
v->progress = (t = v->progress) - (byte)spd;
|
||||
|
||||
return (t < v->progress);
|
||||
const uint advance_speed = v->GetAdvanceSpeed(speed);
|
||||
const uint number_of_steps = (advance_speed + v->progress) / v->GetAdvanceDistance();
|
||||
const uint remainder = (advance_speed + v->progress) % v->GetAdvanceDistance();
|
||||
assert(remainder <= std::numeric_limits<byte>::max());
|
||||
v->progress = static_cast<byte>(remainder);
|
||||
return number_of_steps;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -633,13 +634,43 @@ bool IsShipDestinationTile(TileIndex tile, StationID station)
|
||||
return false;
|
||||
}
|
||||
|
||||
static void ReverseShipIntoTrackdir(Ship *v, Trackdir trackdir)
|
||||
{
|
||||
static constexpr Direction _trackdir_to_direction[] = {
|
||||
DIR_NE, DIR_SE, DIR_E, DIR_E, DIR_S, DIR_S, INVALID_DIR, INVALID_DIR,
|
||||
DIR_SW, DIR_NW, DIR_W, DIR_W, DIR_N, DIR_N, INVALID_DIR, INVALID_DIR,
|
||||
};
|
||||
|
||||
v->direction = _trackdir_to_direction[trackdir];
|
||||
assert(v->direction != INVALID_DIR);
|
||||
v->state = TrackdirBitsToTrackBits(TrackdirToTrackdirBits(trackdir));
|
||||
|
||||
/* Remember our current location to avoid movement glitch */
|
||||
v->rotation_x_pos = v->x_pos;
|
||||
v->rotation_y_pos = v->y_pos;
|
||||
v->cur_speed = 0;
|
||||
v->path.clear();
|
||||
|
||||
v->UpdatePosition();
|
||||
v->UpdateViewport(true, true);
|
||||
}
|
||||
|
||||
static void ReverseShip(Ship *v)
|
||||
{
|
||||
v->direction = ReverseDir(v->direction);
|
||||
|
||||
/* Remember our current location to avoid movement glitch */
|
||||
v->rotation_x_pos = v->x_pos;
|
||||
v->rotation_y_pos = v->y_pos;
|
||||
v->cur_speed = 0;
|
||||
v->path.clear();
|
||||
|
||||
v->UpdatePosition();
|
||||
v->UpdateViewport(true, true);
|
||||
}
|
||||
|
||||
static void ShipController(Ship *v)
|
||||
{
|
||||
uint32 r;
|
||||
Track track;
|
||||
TrackBits tracks;
|
||||
GetNewVehiclePosResult gp;
|
||||
|
||||
v->tick_counter++;
|
||||
v->current_order_time++;
|
||||
|
||||
@ -647,7 +678,7 @@ static void ShipController(Ship *v)
|
||||
|
||||
if (v->vehstatus & VS_STOPPED) return;
|
||||
|
||||
if (ProcessOrders(v) && CheckReverseShip(v)) goto reverse_direction;
|
||||
if (ProcessOrders(v) && CheckReverseShip(v)) return ReverseShip(v);
|
||||
|
||||
v->HandleLoading();
|
||||
|
||||
@ -671,159 +702,139 @@ static void ShipController(Ship *v)
|
||||
|
||||
if (ShipMoveUpDownOnLock(v)) return;
|
||||
|
||||
if (!ShipAccelerate(v)) return;
|
||||
const uint number_of_steps = ShipAccelerate(v);
|
||||
for (uint i = 0; i < number_of_steps; ++i) {
|
||||
GetNewVehiclePosResult gp = GetNewVehiclePos(v);
|
||||
if (v->state != TRACK_BIT_WORMHOLE) {
|
||||
/* Not on a bridge */
|
||||
if (gp.old_tile == gp.new_tile) {
|
||||
/* Staying in tile */
|
||||
if (v->IsInDepot()) {
|
||||
gp.x = v->x_pos;
|
||||
gp.y = v->y_pos;
|
||||
} else {
|
||||
/* Not inside depot */
|
||||
const VehicleEnterTileStatus r = VehicleEnterTile(v, gp.new_tile, gp.x, gp.y);
|
||||
if (HasBit(r, VETS_CANNOT_ENTER)) return ReverseShip(v);
|
||||
|
||||
gp = GetNewVehiclePos(v);
|
||||
if (v->state != TRACK_BIT_WORMHOLE) {
|
||||
/* Not on a bridge */
|
||||
if (gp.old_tile == gp.new_tile) {
|
||||
/* Staying in tile */
|
||||
if (v->IsInDepot()) {
|
||||
gp.x = v->x_pos;
|
||||
gp.y = v->y_pos;
|
||||
} else {
|
||||
/* Not inside depot */
|
||||
r = VehicleEnterTile(v, gp.new_tile, gp.x, gp.y);
|
||||
if (HasBit(r, VETS_CANNOT_ENTER)) goto reverse_direction;
|
||||
|
||||
/* A leave station order only needs one tick to get processed, so we can
|
||||
* always skip ahead. */
|
||||
if (v->current_order.IsType(OT_LEAVESTATION)) {
|
||||
v->current_order.Free();
|
||||
SetWindowWidgetDirty(WC_VEHICLE_VIEW, v->index, WID_VV_START_STOP);
|
||||
/* Test if continuing forward would lead to a dead-end, moving into the dock. */
|
||||
DiagDirection exitdir = VehicleExitDir(v->direction, v->state);
|
||||
TileIndex tile = TileAddByDiagDir(v->tile, exitdir);
|
||||
if (TrackStatusToTrackBits(GetTileTrackStatus(tile, TRANSPORT_WATER, 0, exitdir)) == TRACK_BIT_NONE) goto reverse_direction;
|
||||
} else if (v->dest_tile != 0) {
|
||||
/* We have a target, let's see if we reached it... */
|
||||
if (v->current_order.IsType(OT_GOTO_WAYPOINT) &&
|
||||
DistanceManhattan(v->dest_tile, gp.new_tile) <= 3) {
|
||||
/* We got within 3 tiles of our target buoy, so let's skip to our
|
||||
* next order */
|
||||
UpdateVehicleTimetable(v, true);
|
||||
v->IncrementRealOrderIndex();
|
||||
v->current_order.MakeDummy();
|
||||
} else if (v->current_order.IsType(OT_GOTO_DEPOT) &&
|
||||
v->dest_tile == gp.new_tile) {
|
||||
/* Depot orders really need to reach the tile */
|
||||
if ((gp.x & 0xF) == 8 && (gp.y & 0xF) == 8) {
|
||||
VehicleEnterDepot(v);
|
||||
return;
|
||||
}
|
||||
} else if (v->current_order.IsType(OT_GOTO_STATION) && IsDockingTile(gp.new_tile)) {
|
||||
/* Process station in the orderlist. */
|
||||
Station *st = Station::Get(v->current_order.GetDestination());
|
||||
if (st->docking_station.Contains(gp.new_tile) && IsShipDestinationTile(gp.new_tile, st->index)) {
|
||||
v->last_station_visited = st->index;
|
||||
if (st->facilities & FACIL_DOCK) { // ugly, ugly workaround for problem with ships able to drop off cargo at wrong stations
|
||||
ShipArrivesAt(v, st);
|
||||
v->BeginLoading();
|
||||
} else { // leave stations without docks right away
|
||||
v->current_order.MakeLeaveStation();
|
||||
v->IncrementRealOrderIndex();
|
||||
/* A leave station order only needs one tick to get processed, so we can
|
||||
* always skip ahead. */
|
||||
if (v->current_order.IsType(OT_LEAVESTATION)) {
|
||||
v->current_order.Free();
|
||||
SetWindowWidgetDirty(WC_VEHICLE_VIEW, v->index, WID_VV_START_STOP);
|
||||
/* Test if continuing forward would lead to a dead-end, moving into the dock. */
|
||||
const DiagDirection exitdir = VehicleExitDir(v->direction, v->state);
|
||||
const TileIndex tile = TileAddByDiagDir(v->tile, exitdir);
|
||||
if (TrackStatusToTrackBits(GetTileTrackStatus(tile, TRANSPORT_WATER, 0, exitdir)) == TRACK_BIT_NONE) return ReverseShip(v);
|
||||
} else if (v->dest_tile != 0) {
|
||||
/* We have a target, let's see if we reached it... */
|
||||
if (v->current_order.IsType(OT_GOTO_WAYPOINT) &&
|
||||
DistanceManhattan(v->dest_tile, gp.new_tile) <= 3) {
|
||||
/* We got within 3 tiles of our target buoy, so let's skip to our
|
||||
* next order */
|
||||
UpdateVehicleTimetable(v, true);
|
||||
v->IncrementRealOrderIndex();
|
||||
v->current_order.MakeDummy();
|
||||
} else if (v->current_order.IsType(OT_GOTO_DEPOT) &&
|
||||
v->dest_tile == gp.new_tile) {
|
||||
/* Depot orders really need to reach the tile */
|
||||
if ((gp.x & 0xF) == 8 && (gp.y & 0xF) == 8) {
|
||||
VehicleEnterDepot(v);
|
||||
return;
|
||||
}
|
||||
} else if (v->current_order.IsType(OT_GOTO_STATION) && IsDockingTile(gp.new_tile)) {
|
||||
/* Process station in the orderlist. */
|
||||
Station *st = Station::Get(v->current_order.GetDestination());
|
||||
if (st->docking_station.Contains(gp.new_tile) && IsShipDestinationTile(gp.new_tile, st->index)) {
|
||||
v->last_station_visited = st->index;
|
||||
if (st->facilities & FACIL_DOCK) { // ugly, ugly workaround for problem with ships able to drop off cargo at wrong stations
|
||||
ShipArrivesAt(v, st);
|
||||
v->BeginLoading();
|
||||
} else { // leave stations without docks right away
|
||||
v->current_order.MakeLeaveStation();
|
||||
v->IncrementRealOrderIndex();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
/* New tile */
|
||||
if (!IsValidTile(gp.new_tile)) return ReverseShip(v);
|
||||
|
||||
const DiagDirection diagdir = DiagdirBetweenTiles(gp.old_tile, gp.new_tile);
|
||||
assert(diagdir != INVALID_DIAGDIR);
|
||||
const TrackBits tracks = GetAvailShipTracks(gp.new_tile, diagdir);
|
||||
if (tracks == TRACK_BIT_NONE) {
|
||||
Trackdir trackdir = INVALID_TRACKDIR;
|
||||
CheckReverseShip(v, &trackdir);
|
||||
if (trackdir == INVALID_TRACKDIR) return ReverseShip(v);
|
||||
return ReverseShipIntoTrackdir(v, trackdir);
|
||||
}
|
||||
|
||||
/* Choose a direction, and continue if we find one */
|
||||
const Track track = ChooseShipTrack(v, gp.new_tile, diagdir, tracks);
|
||||
if (track == INVALID_TRACK) return ReverseShip(v);
|
||||
|
||||
const ShipSubcoordData &b = _ship_subcoord[diagdir][track];
|
||||
|
||||
gp.x = (gp.x & ~0xF) | b.x_subcoord;
|
||||
gp.y = (gp.y & ~0xF) | b.y_subcoord;
|
||||
|
||||
/* Call the landscape function and tell it that the vehicle entered the tile */
|
||||
const VehicleEnterTileStatus r = VehicleEnterTile(v, gp.new_tile, gp.x, gp.y);
|
||||
if (HasBit(r, VETS_CANNOT_ENTER)) return ReverseShip(v);
|
||||
|
||||
if (!HasBit(r, VETS_ENTERED_WORMHOLE)) {
|
||||
v->tile = gp.new_tile;
|
||||
v->state = TrackToTrackBits(track);
|
||||
|
||||
/* Update ship cache when the water class changes. Aqueducts are always canals. */
|
||||
if (GetEffectiveWaterClass(gp.old_tile) != GetEffectiveWaterClass(gp.new_tile)) v->UpdateCache();
|
||||
}
|
||||
|
||||
const Direction new_direction = b.dir;
|
||||
const DirDiff diff = DirDifference(new_direction, v->direction);
|
||||
switch (diff) {
|
||||
case DIRDIFF_SAME:
|
||||
case DIRDIFF_45RIGHT:
|
||||
case DIRDIFF_45LEFT:
|
||||
/* Continue at speed */
|
||||
v->rotation = v->direction = new_direction;
|
||||
break;
|
||||
|
||||
default:
|
||||
/* Stop for rotation */
|
||||
v->cur_speed = 0;
|
||||
v->direction = new_direction;
|
||||
/* Remember our current location to avoid movement glitch */
|
||||
v->rotation_x_pos = v->x_pos;
|
||||
v->rotation_y_pos = v->y_pos;
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
/* New tile */
|
||||
if (!IsValidTile(gp.new_tile)) goto reverse_direction;
|
||||
|
||||
DiagDirection diagdir = DiagdirBetweenTiles(gp.old_tile, gp.new_tile);
|
||||
assert(diagdir != INVALID_DIAGDIR);
|
||||
tracks = GetAvailShipTracks(gp.new_tile, diagdir);
|
||||
if (tracks == TRACK_BIT_NONE) {
|
||||
Trackdir trackdir = INVALID_TRACKDIR;
|
||||
CheckReverseShip(v, &trackdir);
|
||||
if (trackdir == INVALID_TRACKDIR) goto reverse_direction;
|
||||
static const Direction _trackdir_to_direction[] = {
|
||||
DIR_NE, DIR_SE, DIR_E, DIR_E, DIR_S, DIR_S, INVALID_DIR, INVALID_DIR,
|
||||
DIR_SW, DIR_NW, DIR_W, DIR_W, DIR_N, DIR_N, INVALID_DIR, INVALID_DIR,
|
||||
};
|
||||
v->direction = _trackdir_to_direction[trackdir];
|
||||
assert(v->direction != INVALID_DIR);
|
||||
v->state = TrackdirBitsToTrackBits(TrackdirToTrackdirBits(trackdir));
|
||||
goto direction_changed;
|
||||
/* On a bridge */
|
||||
if (!IsTileType(gp.new_tile, MP_TUNNELBRIDGE) || !HasBit(VehicleEnterTile(v, gp.new_tile, gp.x, gp.y), VETS_ENTERED_WORMHOLE)) {
|
||||
v->x_pos = gp.x;
|
||||
v->y_pos = gp.y;
|
||||
v->UpdatePosition();
|
||||
if ((v->vehstatus & VS_HIDDEN) == 0) v->Vehicle::UpdateViewport(true);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Choose a direction, and continue if we find one */
|
||||
track = ChooseShipTrack(v, gp.new_tile, diagdir, tracks);
|
||||
if (track == INVALID_TRACK) goto reverse_direction;
|
||||
|
||||
const ShipSubcoordData &b = _ship_subcoord[diagdir][track];
|
||||
|
||||
gp.x = (gp.x & ~0xF) | b.x_subcoord;
|
||||
gp.y = (gp.y & ~0xF) | b.y_subcoord;
|
||||
|
||||
/* Call the landscape function and tell it that the vehicle entered the tile */
|
||||
r = VehicleEnterTile(v, gp.new_tile, gp.x, gp.y);
|
||||
if (HasBit(r, VETS_CANNOT_ENTER)) goto reverse_direction;
|
||||
|
||||
if (!HasBit(r, VETS_ENTERED_WORMHOLE)) {
|
||||
v->tile = gp.new_tile;
|
||||
v->state = TrackToTrackBits(track);
|
||||
|
||||
/* Update ship cache when the water class changes. Aqueducts are always canals. */
|
||||
WaterClass old_wc = GetEffectiveWaterClass(gp.old_tile);
|
||||
WaterClass new_wc = GetEffectiveWaterClass(gp.new_tile);
|
||||
if (old_wc != new_wc) v->UpdateCache();
|
||||
}
|
||||
|
||||
Direction new_direction = b.dir;
|
||||
DirDiff diff = DirDifference(new_direction, v->direction);
|
||||
switch (diff) {
|
||||
case DIRDIFF_SAME:
|
||||
case DIRDIFF_45RIGHT:
|
||||
case DIRDIFF_45LEFT:
|
||||
/* Continue at speed */
|
||||
v->rotation = v->direction = new_direction;
|
||||
break;
|
||||
|
||||
default:
|
||||
/* Stop for rotation */
|
||||
v->cur_speed = 0;
|
||||
v->direction = new_direction;
|
||||
/* Remember our current location to avoid movement glitch */
|
||||
v->rotation_x_pos = v->x_pos;
|
||||
v->rotation_y_pos = v->y_pos;
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
/* On a bridge */
|
||||
if (!IsTileType(gp.new_tile, MP_TUNNELBRIDGE) || !HasBit(VehicleEnterTile(v, gp.new_tile, gp.x, gp.y), VETS_ENTERED_WORMHOLE)) {
|
||||
v->x_pos = gp.x;
|
||||
v->y_pos = gp.y;
|
||||
v->UpdatePosition();
|
||||
if ((v->vehstatus & VS_HIDDEN) == 0) v->Vehicle::UpdateViewport(true);
|
||||
return;
|
||||
/* Ship is back on the bridge head, we need to consume its path
|
||||
* cache entry here as we didn't have to choose a ship track. */
|
||||
if (!v->path.empty()) v->path.pop_front();
|
||||
}
|
||||
|
||||
/* Ship is back on the bridge head, we need to consume its path
|
||||
* cache entry here as we didn't have to choose a ship track. */
|
||||
if (!v->path.empty()) v->path.pop_front();
|
||||
/* update image of ship, as well as delta XY */
|
||||
v->x_pos = gp.x;
|
||||
v->y_pos = gp.y;
|
||||
|
||||
v->UpdatePosition();
|
||||
v->UpdateViewport(true, true);
|
||||
}
|
||||
|
||||
/* update image of ship, as well as delta XY */
|
||||
v->x_pos = gp.x;
|
||||
v->y_pos = gp.y;
|
||||
|
||||
getout:
|
||||
v->UpdatePosition();
|
||||
v->UpdateViewport(true, true);
|
||||
return;
|
||||
|
||||
reverse_direction:
|
||||
v->direction = ReverseDir(v->direction);
|
||||
direction_changed:
|
||||
/* Remember our current location to avoid movement glitch */
|
||||
v->rotation_x_pos = v->x_pos;
|
||||
v->rotation_y_pos = v->y_pos;
|
||||
v->cur_speed = 0;
|
||||
v->path.clear();
|
||||
goto getout;
|
||||
}
|
||||
|
||||
bool Ship::Tick()
|
||||
|
Loading…
Reference in New Issue
Block a user