|
@@ -1853,97 +1853,97 @@ CommandCost CmdBuildBuoy(TileIndex tile,
|
|
|
Station *st = new Station(tile);
|
|
|
if (st == NULL) return_cmd_error(STR_3008_TOO_MANY_STATIONS_LOADING);
|
|
|
|
|
|
/* ensure that in case of error (or no DC_EXEC) the station gets deleted upon return */
|
|
|
AutoPtrT<Station> st_auto_delete(st);
|
|
|
|
|
|
st->town = ClosestTownFromTile(tile, (uint)-1);
|
|
|
st->sign.width_1 = 0;
|
|
|
|
|
|
if (!GenerateStationName(st, tile, STATIONNAMING_BUOY)) return CMD_ERROR;
|
|
|
|
|
|
if (flags & DC_EXEC) {
|
|
|
st->dock_tile = tile;
|
|
|
st->facilities |= FACIL_DOCK;
|
|
|
/* Buoys are marked in the Station struct by this flag. Yes, it is this
|
|
|
* braindead.. */
|
|
|
st->had_vehicle_of_type |= HVOT_BUOY;
|
|
|
st->owner = OWNER_NONE;
|
|
|
|
|
|
st->build_date = _date;
|
|
|
|
|
|
MakeBuoy(tile, st->index, GetWaterClass(tile));
|
|
|
|
|
|
UpdateStationVirtCoordDirty(st);
|
|
|
UpdateStationAcceptance(st, false);
|
|
|
RebuildStationLists();
|
|
|
InvalidateWindow(WC_STATION_LIST, st->owner);
|
|
|
InvalidateWindowWidget(WC_STATION_VIEW, st->index, SVW_SHIPS);
|
|
|
/* success, so don't delete the new station */
|
|
|
st_auto_delete.Detach();
|
|
|
}
|
|
|
|
|
|
return CommandCost(EXPENSES_CONSTRUCTION, _price.build_dock);
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
* Tests whether the player's vehicles have this station in orders
|
|
|
* When player == INVALID_PLAYER, then check all vehicles
|
|
|
* @param station station ID
|
|
|
* @param player player ID, INVALID_PLAYER to disable the check
|
|
|
*/
|
|
|
bool HasStationInUse(StationID station, PlayerID player)
|
|
|
{
|
|
|
const Vehicle *v;
|
|
|
FOR_ALL_VEHICLES(v) {
|
|
|
if (player == INVALID_PLAYER || v->owner == player) {
|
|
|
const Order *order;
|
|
|
FOR_VEHICLE_ORDERS(v, order) {
|
|
|
if (order->IsType(OT_GOTO_STATION) && order->dest == station) {
|
|
|
if (order->IsType(OT_GOTO_STATION) && order->GetDestination() == station) {
|
|
|
return true;
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
return false;
|
|
|
}
|
|
|
|
|
|
static CommandCost RemoveBuoy(Station *st, uint32 flags)
|
|
|
{
|
|
|
/* XXX: strange stuff */
|
|
|
if (!IsValidPlayer(_current_player)) return_cmd_error(INVALID_STRING_ID);
|
|
|
|
|
|
TileIndex tile = st->dock_tile;
|
|
|
|
|
|
if (HasStationInUse(st->index, INVALID_PLAYER)) return_cmd_error(STR_BUOY_IS_IN_USE);
|
|
|
/* remove the buoy if there is a ship on tile when company goes bankrupt... */
|
|
|
if (!(flags & DC_BANKRUPT) && !EnsureNoVehicleOnGround(tile)) return CMD_ERROR;
|
|
|
|
|
|
if (flags & DC_EXEC) {
|
|
|
st->dock_tile = 0;
|
|
|
/* Buoys are marked in the Station struct by this flag. Yes, it is this
|
|
|
* braindead.. */
|
|
|
st->facilities &= ~FACIL_DOCK;
|
|
|
st->had_vehicle_of_type &= ~HVOT_BUOY;
|
|
|
|
|
|
InvalidateWindowWidget(WC_STATION_VIEW, st->index, SVW_SHIPS);
|
|
|
|
|
|
/* We have to set the water tile's state to the same state as before the
|
|
|
* buoy was placed. Otherwise one could plant a buoy on a canal edge,
|
|
|
* remove it and flood the land (if the canal edge is at level 0) */
|
|
|
MakeWaterKeepingClass(tile, GetTileOwner(tile));
|
|
|
MarkTileDirtyByTile(tile);
|
|
|
|
|
|
UpdateStationVirtCoordDirty(st);
|
|
|
DeleteStationIfEmpty(st);
|
|
|
}
|
|
|
|
|
|
return CommandCost(EXPENSES_CONSTRUCTION, _price.remove_truck_station);
|
|
|
}
|
|
|
|
|
|
static const TileIndexDiffC _dock_tileoffs_chkaround[] = {
|
|
|
{-1, 0},
|
|
|
{ 0, 0},
|
|
|
{ 0, 0},
|
|
|
{ 0, -1}
|
|
|
};
|
|
|
static const byte _dock_w_chk[4] = { 2, 1, 2, 1 };
|
|
@@ -2392,97 +2392,97 @@ static void ClickTile_Station(TileIndex
|
|
|
ShowStationViewWindow(GetStationIndex(tile));
|
|
|
}
|
|
|
}
|
|
|
|
|
|
static const byte _enter_station_speedtable[12] = {
|
|
|
215, 195, 175, 155, 135, 115, 95, 75, 55, 35, 15, 0
|
|
|
};
|
|
|
|
|
|
static VehicleEnterTileStatus VehicleEnter_Station(Vehicle *v, TileIndex tile, int x, int y)
|
|
|
{
|
|
|
StationID station_id = GetStationIndex(tile);
|
|
|
if (!v->current_order.ShouldStopAtStation(v, station_id)) return VETSB_CONTINUE;
|
|
|
|
|
|
if (v->type == VEH_TRAIN) {
|
|
|
if (IsRailwayStation(tile) && IsFrontEngine(v) &&
|
|
|
!IsCompatibleTrainStationTile(tile + TileOffsByDiagDir(DirToDiagDir(v->direction)), tile)) {
|
|
|
DiagDirection dir = DirToDiagDir(v->direction);
|
|
|
|
|
|
x &= 0xF;
|
|
|
y &= 0xF;
|
|
|
|
|
|
if (DiagDirToAxis(dir) != AXIS_X) Swap(x, y);
|
|
|
if (y == TILE_SIZE / 2) {
|
|
|
if (dir != DIAGDIR_SE && dir != DIAGDIR_SW) x = TILE_SIZE - 1 - x;
|
|
|
if (x == 12) return VETSB_ENTERED_STATION | (VehicleEnterTileStatus)(station_id << VETS_STATION_ID_OFFSET); /* enter station */
|
|
|
if (x < 12) {
|
|
|
uint16 spd;
|
|
|
|
|
|
v->vehstatus |= VS_TRAIN_SLOWING;
|
|
|
spd = _enter_station_speedtable[x];
|
|
|
if (spd < v->cur_speed) v->cur_speed = spd;
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
} else if (v->type == VEH_ROAD) {
|
|
|
if (v->u.road.state < RVSB_IN_ROAD_STOP && !IsReversingRoadTrackdir((Trackdir)v->u.road.state) && v->u.road.frame == 0) {
|
|
|
if (IsRoadStop(tile) && IsRoadVehFront(v)) {
|
|
|
/* Attempt to allocate a parking bay in a road stop */
|
|
|
RoadStop *rs = GetRoadStopByTile(tile, GetRoadStopType(tile));
|
|
|
|
|
|
if (IsDriveThroughStopTile(tile)) {
|
|
|
/* Vehicles entering a drive-through stop from the 'normal' side use first bay (bay 0). */
|
|
|
byte side = ((DirToDiagDir(v->direction) == ReverseDiagDir(GetRoadStopDir(tile))) == (v->u.road.overtaking == 0)) ? 0 : 1;
|
|
|
|
|
|
if (!rs->IsFreeBay(side)) return VETSB_CANNOT_ENTER;
|
|
|
|
|
|
/* Check if the vehicle is stopping at this road stop */
|
|
|
if (GetRoadStopType(tile) == (IsCargoInClass(v->cargo_type, CC_PASSENGERS) ? ROADSTOP_BUS : ROADSTOP_TRUCK) &&
|
|
|
v->current_order.dest == GetStationIndex(tile)) {
|
|
|
v->current_order.GetDestination() == GetStationIndex(tile)) {
|
|
|
SetBit(v->u.road.state, RVS_IS_STOPPING);
|
|
|
rs->AllocateDriveThroughBay(side);
|
|
|
}
|
|
|
|
|
|
/* Indicate if vehicle is using second bay. */
|
|
|
if (side == 1) SetBit(v->u.road.state, RVS_USING_SECOND_BAY);
|
|
|
/* Indicate a drive-through stop */
|
|
|
SetBit(v->u.road.state, RVS_IN_DT_ROAD_STOP);
|
|
|
return VETSB_CONTINUE;
|
|
|
}
|
|
|
|
|
|
/* For normal (non drive-through) road stops */
|
|
|
/* Check if station is busy or if there are no free bays or whether it is a articulated vehicle. */
|
|
|
if (rs->IsEntranceBusy() || !rs->HasFreeBay() || RoadVehHasArticPart(v)) return VETSB_CANNOT_ENTER;
|
|
|
|
|
|
SetBit(v->u.road.state, RVS_IN_ROAD_STOP);
|
|
|
|
|
|
/* Allocate a bay and update the road state */
|
|
|
uint bay_nr = rs->AllocateBay();
|
|
|
SB(v->u.road.state, RVS_USING_SECOND_BAY, 1, bay_nr);
|
|
|
|
|
|
/* Mark the station entrace as busy */
|
|
|
rs->SetEntranceBusy(true);
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
|
|
|
return VETSB_CONTINUE;
|
|
|
}
|
|
|
|
|
|
/* this function is called for one station each tick */
|
|
|
static void StationHandleBigTick(Station *st)
|
|
|
{
|
|
|
UpdateStationAcceptance(st, true);
|
|
|
|
|
|
if (st->facilities == 0 && ++st->delete_ctr >= 8) delete st;
|
|
|
|
|
|
}
|
|
|
|
|
|
static inline void byte_inc_sat(byte *p) { byte b = *p + 1; if (b != 0) *p = b; }
|
|
|
|
|
|
static void UpdateStationRating(Station *st)
|
|
|
{
|
|
|
bool waiting_changed = false;
|
|
|
|
|
|
byte_inc_sat(&st->time_since_load);
|
|
|
byte_inc_sat(&st->time_since_unload);
|
|
|
|