diff --git a/projects/openttd_vs100.vcxproj b/projects/openttd_vs100.vcxproj
index 1b83b39684..9316526c11 100644
--- a/projects/openttd_vs100.vcxproj
+++ b/projects/openttd_vs100.vcxproj
@@ -331,6 +331,7 @@
+
@@ -484,6 +485,7 @@
+
diff --git a/projects/openttd_vs100.vcxproj.filters b/projects/openttd_vs100.vcxproj.filters
index e8a80eb9d9..f67f313219 100644
--- a/projects/openttd_vs100.vcxproj.filters
+++ b/projects/openttd_vs100.vcxproj.filters
@@ -222,6 +222,9 @@
Source Files
+
+ Source Files
+
Source Files
@@ -681,6 +684,9 @@
Header Files
+
+ Header Files
+
Header Files
diff --git a/projects/openttd_vs80.vcproj b/projects/openttd_vs80.vcproj
index 1a46dd1ea9..1f690158dd 100644
--- a/projects/openttd_vs80.vcproj
+++ b/projects/openttd_vs80.vcproj
@@ -598,6 +598,10 @@
RelativePath=".\..\src\highscore.cpp"
>
+
+
@@ -1226,6 +1230,10 @@
RelativePath=".\..\src\industrytype.h"
>
+
+
diff --git a/projects/openttd_vs90.vcproj b/projects/openttd_vs90.vcproj
index 30dad32c2e..86ec976b31 100644
--- a/projects/openttd_vs90.vcproj
+++ b/projects/openttd_vs90.vcproj
@@ -595,6 +595,10 @@
RelativePath=".\..\src\highscore.cpp"
>
+
+
@@ -1223,6 +1227,10 @@
RelativePath=".\..\src\industrytype.h"
>
+
+
diff --git a/source.list b/source.list
index ebb4ea31ef..91e8dd26b8 100644
--- a/source.list
+++ b/source.list
@@ -40,6 +40,7 @@ goal.cpp
ground_vehicle.cpp
heightmap.cpp
highscore.cpp
+infrastructure.cpp
hotkeys.cpp
ini.cpp
ini_load.cpp
@@ -227,6 +228,7 @@ house_type.h
industry.h
industry_type.h
industrytype.h
+infrastructure_func.h
ini_type.h
landscape.h
landscape_type.h
diff --git a/src/aircraft_cmd.cpp b/src/aircraft_cmd.cpp
index d81a1e3f16..9097590e65 100644
--- a/src/aircraft_cmd.cpp
+++ b/src/aircraft_cmd.cpp
@@ -35,6 +35,7 @@
#include "engine_base.h"
#include "core/random_func.hpp"
#include "core/backup_type.hpp"
+#include "infrastructure_func.h"
#include "zoom_func.h"
#include "disaster_vehicle.h"
@@ -126,7 +127,7 @@ static StationID FindNearestHangar(const Aircraft *v)
const AircraftVehicleInfo *avi = AircraftVehInfo(v->engine_type);
FOR_ALL_STATIONS(st) {
- if (st->owner != v->owner || !(st->facilities & FACIL_AIRPORT)) continue;
+ if (!IsInfraUsageAllowed(VEH_AIRCRAFT, v->owner, st->owner) || !(st->facilities & FACIL_AIRPORT)) continue;
const AirportFTAClass *afc = st->airport.GetFTA();
if (!st->airport.HasHangar() || (
@@ -1624,7 +1625,7 @@ static void AircraftEventHandler_Flying(Aircraft *v, const AirportFTAClass *apc)
Station *st = Station::Get(v->targetairport);
/* Runway busy, not allowed to use this airstation or closed, circle. */
- if (CanVehicleUseStation(v, st) && (st->owner == OWNER_NONE || st->owner == v->owner) && !(st->airport.flags & AIRPORT_CLOSED_block)) {
+ if (CanVehicleUseStation(v, st) && IsInfraUsageAllowed(VEH_AIRCRAFT, v->owner, st->owner) && !(st->airport.flags & AIRPORT_CLOSED_block)) {
/* {32,FLYING,NOTHING_block,37}, {32,LANDING,N,33}, {32,HELILANDING,N,41},
* if it is an airplane, look for LANDING, for helicopter HELILANDING
* it is possible to choose from multiple landing runways, so loop until a free one is found */
diff --git a/src/company_cmd.cpp b/src/company_cmd.cpp
index 36c902ce91..b2a8c010a3 100644
--- a/src/company_cmd.cpp
+++ b/src/company_cmd.cpp
@@ -220,14 +220,16 @@ static void SubtractMoneyFromAnyCompany(Company *c, CommandCost cost)
if (HasBit(1 << EXPENSES_TRAIN_INC |
1 << EXPENSES_ROADVEH_INC |
1 << EXPENSES_AIRCRAFT_INC |
- 1 << EXPENSES_SHIP_INC, cost.GetExpensesType())) {
+ 1 << EXPENSES_SHIP_INC |
+ 1 << EXPENSES_SHARING_INC, cost.GetExpensesType())) {
c->cur_economy.income -= cost.GetCost();
} else if (HasBit(1 << EXPENSES_TRAIN_RUN |
1 << EXPENSES_ROADVEH_RUN |
1 << EXPENSES_AIRCRAFT_RUN |
1 << EXPENSES_SHIP_RUN |
1 << EXPENSES_PROPERTY |
- 1 << EXPENSES_LOAN_INT, cost.GetExpensesType())) {
+ 1 << EXPENSES_LOAN_INT |
+ 1 << EXPENSES_SHARING_COST, cost.GetExpensesType())) {
c->cur_economy.expenses -= cost.GetCost();
}
diff --git a/src/company_gui.cpp b/src/company_gui.cpp
index 1343acb91b..4a6a7d1b32 100644
--- a/src/company_gui.cpp
+++ b/src/company_gui.cpp
@@ -63,6 +63,8 @@ static ExpensesType _expenses_list_1[] = {
EXPENSES_AIRCRAFT_INC,
EXPENSES_SHIP_INC,
EXPENSES_LOAN_INT,
+ EXPENSES_SHARING_INC,
+ EXPENSES_SHARING_COST,
EXPENSES_OTHER,
};
@@ -72,6 +74,7 @@ static ExpensesType _expenses_list_2[] = {
EXPENSES_ROADVEH_INC,
EXPENSES_AIRCRAFT_INC,
EXPENSES_SHIP_INC,
+ EXPENSES_SHARING_INC,
INVALID_EXPENSES,
EXPENSES_TRAIN_RUN,
EXPENSES_ROADVEH_RUN,
@@ -79,6 +82,7 @@ static ExpensesType _expenses_list_2[] = {
EXPENSES_SHIP_RUN,
EXPENSES_PROPERTY,
EXPENSES_LOAN_INT,
+ EXPENSES_SHARING_COST,
INVALID_EXPENSES,
EXPENSES_CONSTRUCTION,
EXPENSES_NEW_VEHICLES,
diff --git a/src/depot_gui.cpp b/src/depot_gui.cpp
index fe75708ddf..cb9b86cb84 100644
--- a/src/depot_gui.cpp
+++ b/src/depot_gui.cpp
@@ -26,6 +26,8 @@
#include "tilehighlight_func.h"
#include "window_gui.h"
#include "vehiclelist.h"
+#include "company_base.h"
+#include "infrastructure_func.h"
#include "order_backup.h"
#include "zoom_func.h"
@@ -686,7 +688,7 @@ struct DepotWindow : Window {
/* Setup disabled buttons. */
TileIndex tile = this->window_number;
- this->SetWidgetsDisabledState(!IsTileOwner(tile, _local_company),
+ this->SetWidgetsDisabledState(!Company::IsValidID(_local_company) || !IsInfraTileUsageAllowed(this->type, _local_company, tile),
WID_D_STOP_ALL,
WID_D_START_ALL,
WID_D_SELL,
diff --git a/src/economy.cpp b/src/economy.cpp
index d78d2cccec..7b6228747d 100644
--- a/src/economy.cpp
+++ b/src/economy.cpp
@@ -43,6 +43,7 @@
#include "economy_base.h"
#include "core/pool_func.hpp"
#include "core/backup_type.hpp"
+#include "infrastructure_func.h"
#include "cargo_type.h"
#include "water.h"
#include "game/game.hpp"
@@ -425,7 +426,8 @@ void ChangeOwnershipOfCompanyItems(Owner old_owner, Owner new_owner)
}
}
- {
+ /* Change ownership of vehicles */
+ if (new_owner != INVALID_OWNER) {
FreeUnitIDGenerator unitidgen[] = {
FreeUnitIDGenerator(VEH_TRAIN, new_owner), FreeUnitIDGenerator(VEH_ROAD, new_owner),
FreeUnitIDGenerator(VEH_SHIP, new_owner), FreeUnitIDGenerator(VEH_AIRCRAFT, new_owner)
@@ -483,6 +485,10 @@ void ChangeOwnershipOfCompanyItems(Owner old_owner, Owner new_owner)
}
if (new_owner != INVALID_OWNER) GroupStatistics::UpdateAutoreplace(new_owner);
+ } else {
+ /* Depending on sharing settings, other companies could be affected too.
+ * Let the infrastructure sharing code handle this. */
+ HandleSharingCompanyDeletion(old_owner);
}
/* Change ownership of tiles */
@@ -497,22 +503,14 @@ void ChangeOwnershipOfCompanyItems(Owner old_owner, Owner new_owner)
* and signals were not propagated
* Similar with crossings - it is needed to bar crossings that weren't before
* because of different owner of crossing and approaching train */
- tile = 0;
- do {
- if (IsTileType(tile, MP_RAILWAY) && IsTileOwner(tile, new_owner) && HasSignals(tile)) {
- TrackBits tracks = GetTrackBits(tile);
- do { // there may be two tracks with signals for TRACK_BIT_HORZ and TRACK_BIT_VERT
- Track track = RemoveFirstTrack(&tracks);
- if (HasSignalOnTrack(tile, track)) AddTrackToSignalBuffer(tile, track, new_owner);
- } while (tracks != TRACK_BIT_NONE);
- } else if (IsLevelCrossingTile(tile) && IsTileOwner(tile, new_owner)) {
- UpdateLevelCrossing(tile);
+ UpdateAllBlockSignals(new_owner);
+ } else if (_settings_game.economy.infrastructure_sharing[VEH_TRAIN]) {
+ /* tracks are being removed while sharing is enabled.
+ * Thus, update all signals and crossings. */
+ UpdateAllBlockSignals();
}
- } while (++tile != MapSize());
- }
-
- /* update signals in buffer */
+ /* Update any signals in the buffer */
UpdateSignalsInBuffer();
}
@@ -1213,7 +1211,13 @@ void CargoPayment::PayFinalDelivery(const CargoPacket *cp, uint count)
/* Handle end of route payment */
Money profit = DeliverGoods(count, this->ct, this->current_station, cp->SourceStationXY(), cp->DaysInTransit(), this->owner, cp->SourceSubsidyType(), cp->SourceSubsidyID());
+#ifndef INFRASTRUCTURE_FUNC_H
this->route_profit += profit;
+#else
+ /* For Infrastructure patch. Handling transfers between other companies */
+ this->route_profit += profit - cp->FeederShare();
+#endif
+
/* The vehicle's profit is whatever route profit there is minus feeder shares. */
this->visual_profit += profit - cp->FeederShare(count);
@@ -1236,6 +1240,9 @@ Money CargoPayment::PayTransfer(const CargoPacket *cp, uint count)
profit = profit * _settings_game.economy.feeder_payment_share / 100;
+#ifdef INFRASTRUCTURE_FUNC_H
+ this->route_profit += profit;
+#endif
this->visual_transfer += profit; // accumulate transfer profits for whole vehicle
return profit; // account for the (virtual) profit already made for the cargo packet
}
diff --git a/src/economy_type.h b/src/economy_type.h
index 7e7a572413..870a00d129 100644
--- a/src/economy_type.h
+++ b/src/economy_type.h
@@ -161,6 +161,8 @@ enum ExpensesType {
EXPENSES_SHIP_INC, ///< Income from ships.
EXPENSES_LOAN_INT, ///< Interest payments over the loan.
EXPENSES_OTHER, ///< Other expenses.
+ EXPENSES_SHARING_COST, ///< Infrastructure sharing costs
+ EXPENSES_SHARING_INC, ///< Infrastructure sharing income
EXPENSES_END, ///< Number of expense types.
INVALID_EXPENSES = 0xFF, ///< Invalid expense type.
};
diff --git a/src/infrastructure.cpp b/src/infrastructure.cpp
new file mode 100644
index 0000000000..d0fa7bc337
--- /dev/null
+++ b/src/infrastructure.cpp
@@ -0,0 +1,346 @@
+/* $Id$ */
+
+/*
+ * This file is part of OpenTTD.
+ * OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
+ * OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see .
+ */
+
+/** @file infrastructure.cpp Implementation of infrastructure sharing */
+
+#include "stdafx.h"
+#include "infrastructure_func.h"
+#include "train.h"
+#include "aircraft.h"
+#include "error.h"
+#include "vehicle_func.h"
+#include "station_base.h"
+#include "depot_base.h"
+#include "pbs.h"
+#include "signal_func.h"
+#include "window_func.h"
+#include "gui.h"
+#include "pathfinder/yapf/yapf_cache.h"
+#include "company_base.h"
+
+#include "table/strings.h"
+
+/**
+ * Helper function for transferring sharing fees
+ * @param v The vehicle involved
+ * @param infra_owner The owner of the infrastructure
+ * @param cost Amount to transfer as money fraction (shifted 8 bits to the left)
+ */
+static void PaySharingFee(Vehicle *v, Owner infra_owner, Money cost)
+{
+ Company *c = Company::Get(v->owner);
+ if (!_settings_game.economy.sharing_payment_in_debt) {
+ /* Do not allow fee payment to drop (money - loan) below 0. */
+ cost = min(cost, (c->money - c->current_loan) << 8);
+ if (cost <= 0) return;
+ }
+ v->profit_this_year -= cost;
+ SubtractMoneyFromCompanyFract(v->owner, CommandCost(EXPENSES_SHARING_COST, cost));
+ SubtractMoneyFromCompanyFract(infra_owner, CommandCost(EXPENSES_SHARING_INC, -cost));
+}
+
+/**
+ * Pay the fee for spending a single tick inside a station.
+ * @param v The vehicle that is using the station.
+ * @param st The station that it uses.
+ */
+void PayStationSharingFee(Vehicle *v, const Station *st)
+{
+ if (v->owner == st->owner || st->owner == OWNER_NONE || v->type == VEH_TRAIN) return;
+ Money cost = _settings_game.economy.sharing_fee[v->type];
+ PaySharingFee(v, st->owner, (cost << 8) / DAY_TICKS);
+}
+
+uint16 is2_GetWeight(Train *v)
+{
+ uint16 weight = (CargoSpec::Get(v->cargo_type)->weight * v->cargo.StoredCount() * FreightWagonMult(v->cargo_type)) / 16;
+ /* Vehicle weight is not added for articulated parts. */
+ if (!v->IsArticulatedPart()) {
+ weight += GetVehicleProperty(v, PROP_TRAIN_WEIGHT, RailVehInfo(v->engine_type)->weight);
+ }
+ /* Powered wagons have extra weight added. */
+ if (HasBit(v->flags, VRF_POWEREDWAGON)) {
+ weight += RailVehInfo(v->gcache.first_engine)->pow_wag_weight;
+ }
+ return weight;
+}
+
+
+/**
+ * Pay the daily fee for trains on foreign tracks.
+ * @param v The vehicle to pay the fee for.
+ */
+void PayDailyTrackSharingFee(Train *v)
+{
+ Owner owner = GetTileOwner(v->tile);
+ if (owner == v->owner) return;
+ Money cost = _settings_game.economy.sharing_fee[VEH_TRAIN] << 8;
+ /* Cost is calculated per 1000 tonnes */
+ cost = cost * is2_GetWeight(v) / 1000;
+ /* Only pay the required fraction */
+ cost = cost * v->running_ticks / DAY_TICKS;
+ if (cost != 0) PaySharingFee(v, owner, cost);
+}
+
+/**
+ * Check whether a vehicle is in an allowed position.
+ * @param v The vehicle to check.
+ * @param owner Owner whose infrastructure is not allowed, because the company will be removed. Ignored if INVALID_OWNER.
+ * @return True if the vehicle is compeletely in an allowed position.
+ */
+static bool VehiclePositionIsAllowed(const Vehicle *v, Owner owner = INVALID_OWNER)
+{
+ switch (v->type) {
+ case VEH_TRAIN:
+ for (const Vehicle *u = v; u != NULL; u = u->Next()) {
+ if (!IsInfraTileUsageAllowed(VEH_TRAIN, v->owner, u->tile) || GetTileOwner(u->tile) == owner) return false;
+ }
+ return true;
+ case VEH_ROAD:
+ for (const Vehicle *u = v; u != NULL; u = u->Next()) {
+ if (IsRoadDepotTile(u->tile) || IsStandardRoadStopTile(u->tile)) {
+ if (!IsInfraTileUsageAllowed(VEH_ROAD, v->owner, u->tile) || GetTileOwner(u->tile) == owner) return false;
+ }
+ }
+ return true;
+ case VEH_SHIP:
+ if (IsShipDepotTile(v->tile) && v->IsStoppedInDepot()) {
+ if (!IsInfraTileUsageAllowed(VEH_SHIP, v->owner, v->tile) || GetTileOwner(v->tile) == owner) return false;
+ }
+ return true;
+ case VEH_AIRCRAFT: {
+ const Aircraft *a = Aircraft::From(v);
+ if (a->state != FLYING && Station::IsValidID(a->targetairport)) {
+ Owner station_owner = Station::Get(a->targetairport)->owner;
+ if (!IsInfraUsageAllowed(VEH_AIRCRAFT, a->owner, station_owner) || station_owner == owner) return false;
+ }
+ return true;
+ }
+ default: return true;
+ }
+}
+
+/**
+ * Check whether an order has a destination that is allowed.
+ * I.e. it refers to a station/depot/waypoint the vehicle is allowed to visit.
+ * @param order The order to check
+ * @param v The vehicle this order belongs to.
+ * @param owner Owner whose infrastructure is not allowed, because the company will be removed. Ignored if INVALID_OWNER.
+ * @return True if the order has an allowed destination.
+ */
+static bool OrderDestinationIsAllowed(const Order *order, const Vehicle *v, Owner owner = INVALID_OWNER)
+{
+ Owner dest_owner;
+ switch (order->GetType()) {
+ case OT_GOTO_STATION:
+ case OT_GOTO_WAYPOINT: dest_owner = BaseStation::Get(order->GetDestination())->owner; break;
+ case OT_GOTO_DEPOT: dest_owner = (v->type == VEH_AIRCRAFT) ? Station::Get(order->GetDestination())->owner : GetTileOwner(Depot::Get(order->GetDestination())->xy); break;
+ case OT_LOADING: dest_owner = Station::Get(v->last_station_visited)->owner; break;
+ default: return true;
+ }
+ return dest_owner != owner && IsInfraUsageAllowed(v->type, v->owner, dest_owner);
+}
+
+/**
+ * Sell a vehicle, no matter where it may be.
+ * @param v The vehicle to sell
+ * @param give_money Do we actually need to give money to the vehicle owner?
+ */
+static void RemoveAndSellVehicle(Vehicle *v, bool give_money)
+{
+ assert(v->Previous() == NULL);
+
+ if (give_money) {
+ /* compute total value and give that to the owner */
+ Money value = 0;
+ for (Vehicle *u = v->First(); u != NULL; u = u->Next()) {
+ value += v->value;
+ }
+ CompanyID old = _current_company;
+ _current_company = v->owner;
+ SubtractMoneyFromCompany(CommandCost(EXPENSES_NEW_VEHICLES, -value));
+ _current_company = old;
+ }
+
+ /* take special measures for trains, but not when sharing is disabled or when the train is a free wagon chain */
+ if (_settings_game.economy.infrastructure_sharing[VEH_TRAIN] && v->type == VEH_TRAIN && Train::From(v)->IsFrontEngine()) {
+ DeleteVisibleTrain(Train::From(v));
+ } else {
+ delete v;
+ }
+}
+
+/**
+ * Check all path reservations, and reserve a new path if the current path is invalid.
+ */
+static void FixAllReservations()
+{
+ /* if this function is called, we can safely assume that sharing of rails is being switched off */
+ assert(!_settings_game.economy.infrastructure_sharing[VEH_TRAIN]);
+ Train *v;
+ FOR_ALL_TRAINS(v) {
+ if (!v->IsPrimaryVehicle() || (v->vehstatus & VS_CRASHED) != 0) continue;
+ /* It might happen that the train reserved additional tracks,
+ * but FollowTrainReservation can't detect those because they are no longer reachable.
+ * detect this by first finding the end of the reservation,
+ * then switch sharing on and try again. If these two ends differ,
+ * unreserve the path, switch sharing off and try to reserve a new path */
+ PBSTileInfo end_tile_info = FollowTrainReservation(v);
+
+ /* first do a quick test to determine whether the next tile has any reservation at all */
+ TileIndex next_tile = end_tile_info.tile + TileOffsByDiagDir(TrackdirToExitdir(end_tile_info.trackdir));
+ /* If the next tile doesn't have a reservation at all, the reservation surely ends here. Thus all is well */
+ if (GetReservedTrackbits(next_tile) == TRACK_BIT_NONE) continue;
+
+ /* change sharing setting temporarily */
+ _settings_game.economy.infrastructure_sharing[VEH_TRAIN] = true;
+ PBSTileInfo end_tile_info2 = FollowTrainReservation(v);
+ /* if these two reservation ends differ, unreserve the path and try to reserve a new path */
+ if (end_tile_info.tile != end_tile_info2.tile || end_tile_info.trackdir != end_tile_info2.trackdir) {
+ FreeTrainTrackReservation(v);
+ _settings_game.economy.infrastructure_sharing[VEH_TRAIN] = false;
+ TryPathReserve(v, true);
+ } else {
+ _settings_game.economy.infrastructure_sharing[VEH_TRAIN] = false;
+ }
+ }
+}
+
+/**
+ * Check if a sharing change is possible.
+ * If vehicles are still on others' infrastructure or using others' stations,
+ * The change is not possible and false is returned.
+ * @param type The type of vehicle whose setting will be changed.
+ * @return True if the change can take place, false otherwise.
+ */
+bool CheckSharingChangePossible(VehicleType type)
+{
+ if (type != VEH_AIRCRAFT) YapfNotifyTrackLayoutChange(INVALID_TILE, INVALID_TRACK);
+ /* Only do something when sharing is being disabled */
+ if (_settings_game.economy.infrastructure_sharing[type]) return true;
+
+ StringID error_message = STR_NULL;
+ Vehicle *v;
+ FOR_ALL_VEHICLES(v) {
+ if (type != v->type) continue;
+ if (v->Previous() != NULL) continue;
+
+ /* Check vehicle positiion */
+ if (!VehiclePositionIsAllowed(v)) {
+ error_message = STR_CONFIG_SETTING_SHARING_USED_BY_VEHICLES;
+ /* Break immediately, this error message takes precedence over the others. */
+ break;
+ }
+
+ /* Check current order */
+ if (!OrderDestinationIsAllowed(&v->current_order, v)) {
+ error_message = STR_CONFIG_SETTING_SHARING_ORDERS_TO_OTHERS;
+ }
+
+ /* Check order list */
+ if (v->FirstShared() != v) continue;
+ Order *o;
+ FOR_VEHICLE_ORDERS(v, o) {
+ if (!OrderDestinationIsAllowed(o, v)) {
+ error_message = STR_CONFIG_SETTING_SHARING_ORDERS_TO_OTHERS;
+ }
+ }
+ }
+
+ if (error_message != STR_NULL) {
+ ShowErrorMessage(error_message, INVALID_STRING_ID, WL_ERROR);
+ return false;
+ }
+
+ if (type == VEH_TRAIN) FixAllReservations();
+
+ return true;
+}
+
+/**
+ * Handle the removal (through reset_company or bankruptcy) of a company.
+ * i.e. remove all vehicles owned by that company or on its infrastructure,
+ * and delete all now-invalid orders.
+ * @param Owner the company to be removed.
+ */
+void HandleSharingCompanyDeletion(Owner owner)
+{
+ YapfNotifyTrackLayoutChange(INVALID_TILE, INVALID_TRACK);
+
+ Vehicle *v;
+ FOR_ALL_VEHICLES(v) {
+ if (!IsCompanyBuildableVehicleType(v) || v->Previous() != NULL) continue;
+ /* vehicle position */
+ if (v->owner == owner || !VehiclePositionIsAllowed(v, owner)) {
+ RemoveAndSellVehicle(v, v->owner != owner);
+ continue;
+ }
+ /* current order */
+ if (!OrderDestinationIsAllowed(&v->current_order, v, owner)) {
+ if (v->current_order.IsType(OT_LOADING)) {
+ v->LeaveStation();
+ } else {
+ v->current_order.MakeDummy();
+ }
+ SetWindowDirty(WC_VEHICLE_VIEW, v->index);
+ }
+
+ /* order list */
+ if (v->FirstShared() != v) continue;
+
+ Order *o;
+ int id = -1;
+ FOR_VEHICLE_ORDERS(v, o) {
+ id++;
+ if (OrderDestinationIsAllowed(o, v, owner)) continue;
+
+ o->MakeDummy();
+ for (const Vehicle *w = v; w != NULL; w = w->NextShared()) {
+ /* In GUI, simulate by removing the order and adding it back */
+ InvalidateVehicleOrder(w, id | (INVALID_VEH_ORDER_ID << 8));
+ InvalidateVehicleOrder(w, (id << 8) | INVALID_VEH_ORDER_ID);
+ }
+ }
+ }
+}
+
+/**
+ * Update all block signals on the map.
+ * To be called after the setting for sharing of rails changes.
+ * @param owner Owner whose signals to update. If INVALID_OWNER, update everything.
+ */
+void UpdateAllBlockSignals(Owner owner)
+{
+ Owner last_owner = INVALID_OWNER;
+ TileIndex tile = 0;
+ do {
+ if (IsTileType(tile, MP_RAILWAY) && HasSignals(tile)) {
+ Owner track_owner = GetTileOwner(tile);
+ if (owner != INVALID_OWNER && track_owner != owner) continue;
+
+ if (!IsOneSignalBlock(track_owner, last_owner)) {
+ /* Cannot update signals of two different companies in one run,
+ * if these signal blocks are not joined */
+ UpdateSignalsInBuffer();
+ last_owner = track_owner;
+ }
+ TrackBits bits = GetTrackBits(tile);
+ do {
+ Track track = RemoveFirstTrack(&bits);
+ if (HasSignalOnTrack(tile, track)) {
+ AddTrackToSignalBuffer(tile, track, track_owner);
+ }
+ } while (bits != TRACK_BIT_NONE);
+ } else if (IsLevelCrossingTile(tile) && (owner == INVALID_OWNER || GetTileOwner(tile) == owner)) {
+ UpdateLevelCrossing(tile);
+ }
+ } while (++tile != MapSize());
+
+ UpdateSignalsInBuffer();
+}
diff --git a/src/infrastructure_func.h b/src/infrastructure_func.h
new file mode 100644
index 0000000000..31f481bc39
--- /dev/null
+++ b/src/infrastructure_func.h
@@ -0,0 +1,107 @@
+
+/* $Id$ */
+
+/*
+ * This file is part of OpenTTD.
+ * OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
+ * OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see .
+ */
+
+/** @file infrastructure_func.h Functions for access to (shared) infrastructure */
+
+#ifndef INFRASTRUCTURE_FUNC_H
+#define INFRASTRUCTURE_FUNC_H
+
+#include "vehicle_base.h"
+#include "settings_type.h"
+#include "command_type.h"
+#include "company_func.h"
+#include "tile_map.h"
+
+void PayStationSharingFee(Vehicle *v, const Station *st);
+void PayDailyTrackSharingFee(Train *v);
+
+bool CheckSharingChangePossible(VehicleType type);
+void HandleSharingCompanyDeletion(Owner owner);
+void UpdateAllBlockSignals(Owner owner = INVALID_OWNER);
+
+/**
+ * Check whether a vehicle of a given owner and type can use the infrastrucutre of a given company.
+ * @param type Type of vehicle we are talking about.
+ * @param veh_owner Owner of the vehicle in question.
+ * @param infra_owner The owner of the infrastructure.
+ * @return True if infrastructure usage is allowed, false otherwise.
+ */
+static inline bool IsInfraUsageAllowed(VehicleType type, Owner veh_owner, Owner infra_owner)
+{
+ return infra_owner == veh_owner || infra_owner == OWNER_NONE || _settings_game.economy.infrastructure_sharing[type];
+}
+
+/**
+ * Check whether a vehicle of a given owner and type can use the infrastrucutre on a given tile.
+ * @param type Type of vehicle we are talking about.
+ * @param veh_owner Owner of the vehicle in question.
+ * @param tile The tile that may or may not be used.
+ * @return True if infrastructure usage is allowed, false otherwise.
+ */
+static inline bool IsInfraTileUsageAllowed(VehicleType type, Owner veh_owner, TileIndex tile)
+{
+ return IsInfraUsageAllowed(type, veh_owner, GetTileOwner(tile));
+}
+
+/**
+ * Is a vehicle owned by _current_company allowed to use the infrastructure of infra_owner?
+ * If this is not allowed, this function provides the appropriate error message.
+ * @see IsInfraUsageAllowed
+ * @see CheckOwnership
+ * @param type Type of vehicle.
+ * @param infra_owner Owner of the infrastructure.
+ * @param tile Tile of the infrastructure.
+ * @return CommandCost indicating success or failure.
+ */
+static inline CommandCost CheckInfraUsageAllowed(VehicleType type, Owner infra_owner, TileIndex tile = 0)
+{
+ if (infra_owner == OWNER_NONE || _settings_game.economy.infrastructure_sharing[type]) return CommandCost();
+ return CheckOwnership(infra_owner, tile);
+}
+
+/**
+ * Check whether a given company can control this vehicle.
+ * Controlling a vehicle means permission to start, stop or reverse it or to make it ignore signals.
+ * @param v The vehicle which may or may not be controlled.
+ * @param o The company which may or may not control this vehicle.
+ * @return True if the given company is allowed to control this vehicle.
+ */
+static inline bool IsVehicleControlAllowed(const Vehicle *v, Owner o)
+{
+ return v->owner == o || (v->type == VEH_TRAIN && IsTileOwner(v->tile, o));
+}
+
+/**
+ * Check whether _current_company can control this vehicle.
+ * If this is not allowed, this function provides the appropriate error message.
+ * @see IsVehicleControlAllowed
+ * @param v The vehicle which may or may not be controlled.
+ * @return CommandCost indicating success or failure.
+ */
+static inline CommandCost CheckVehicleControlAllowed(const Vehicle *v)
+{
+ if (v->type == VEH_TRAIN && IsTileOwner(v->tile, _current_company)) return CommandCost();
+ return CheckOwnership(v->owner);
+}
+
+/**
+ * Do signal states propagate from the tracks of one owner to the other?
+ * @note This function should be consistent, so if it returns true for (a, b) and (b, c),
+ * it should also return true for (a, c).
+ * @param o1 First track owner.
+ * @param o2 Second track owner.
+ * @return True if tracks of the two owners are part of the same signal block.
+ */
+static inline bool IsOneSignalBlock(Owner o1, Owner o2)
+{
+ return o1 == o2 || _settings_game.economy.infrastructure_sharing[VEH_TRAIN];
+}
+
+#endif /* INFRASTRUCTURE_FUNC_H */
diff --git a/src/lang/english.txt b/src/lang/english.txt
index 4350477ef2..56f008eeb7 100644
--- a/src/lang/english.txt
+++ b/src/lang/english.txt
@@ -1457,6 +1457,7 @@ STR_CONFIG_SETTING_DEFAULT_RAIL_TYPE_HELPTEXT :Rail type to se
STR_CONFIG_SETTING_DEFAULT_RAIL_TYPE_FIRST :First available
STR_CONFIG_SETTING_DEFAULT_RAIL_TYPE_LAST :Last available
STR_CONFIG_SETTING_DEFAULT_RAIL_TYPE_MOST_USED :Most used
+
STR_CONFIG_SETTING_SHOW_TRACK_RESERVATION :Show path reservations for tracks: {STRING2}
STR_CONFIG_SETTING_SHOW_TRACK_RESERVATION_HELPTEXT :Give reserved tracks a different colour to assist in problems with trains refusing to enter path-based blocks
STR_CONFIG_SETTING_PERSISTENT_BUILDINGTOOLS :Keep building tools active after usage: {STRING2}
@@ -1512,6 +1513,18 @@ STR_CONFIG_SETTING_AI_IN_MULTIPLAYER_HELPTEXT :Allow AI comput
STR_CONFIG_SETTING_SCRIPT_MAX_OPCODES :#opcodes before scripts are suspended: {STRING2}
STR_CONFIG_SETTING_SCRIPT_MAX_OPCODES_HELPTEXT :Maximum number of computation steps that a script can take in one turn
+STR_CONFIG_SETTING_SHARING_RAIL :Enable sharing of railways: {STRING2}
+STR_CONFIG_SETTING_SHARING_ROAD :Enable sharing of road stops and depots: {STRING2}
+STR_CONFIG_SETTING_SHARING_WATER :Enable sharing of docks and ship depots: {STRING2}
+STR_CONFIG_SETTING_SHARING_AIR :Enable sharing of airports: {STRING2}
+STR_CONFIG_SETTING_SHARING_FEE_RAIL :Daily track toll for trains: {STRING2} per 1000 tonnes
+STR_CONFIG_SETTING_SHARING_FEE_ROAD :Stopping fee for road vehicles: {STRING2} per day
+STR_CONFIG_SETTING_SHARING_FEE_WATER :Docking fee for ships: {STRING2} per day
+STR_CONFIG_SETTING_SHARING_FEE_AIR :Terminal fee for aircraft: {STRING2} per day
+STR_CONFIG_SETTING_SHARING_PAYMENT_IN_DEBT :Allow companies in debt to pay sharing fees: {STRING2}
+STR_CONFIG_SETTING_SHARING_USED_BY_VEHICLES :Can't change this setting, vehicles are using shared infrastructure.
+STR_CONFIG_SETTING_SHARING_ORDERS_TO_OTHERS :Can't change this setting, vehicles have orders to destinations of others.
+
STR_CONFIG_SETTING_SERVINT_ISPERCENT :Service intervals are in percents: {STRING2}
STR_CONFIG_SETTING_SERVINT_ISPERCENT_HELPTEXT :Choose whether servicing of vehicles is triggered by the time passed since last service or by reliability dropping by a certain percentage of the maximum reliability
STR_CONFIG_SETTING_SERVINT_TRAINS :Default service interval for trains: {STRING2}
@@ -1749,6 +1762,7 @@ STR_CONFIG_SETTING_ENVIRONMENT_CARGODIST :{ORANGE}Cargo d
STR_CONFIG_SETTING_AI :{ORANGE}Competitors
STR_CONFIG_SETTING_AI_NPC :{ORANGE}Computer players
STR_CONFIG_SETTING_VIEWPORT_MAP_OPTIONS :{ORANGE}Map mode
+STR_CONFIG_SETTING_SHARING :{ORANGE}Infrastructure sharing
STR_CONFIG_SETTING_PATHFINDER_OPF :Original
STR_CONFIG_SETTING_PATHFINDER_NPF :NPF
@@ -3565,6 +3579,8 @@ STR_FINANCES_SECTION_AIRCRAFT_INCOME :{GOLD}Aircraft
STR_FINANCES_SECTION_SHIP_INCOME :{GOLD}Ship Income
STR_FINANCES_SECTION_LOAN_INTEREST :{GOLD}Loan Interest
STR_FINANCES_SECTION_OTHER :{GOLD}Other
+STR_FINANCES_SECTION_INFRASTRUCTURE_COSTS :{GOLD}Infrastructure Sharing Costs
+STR_FINANCES_SECTION_INFRASTRUCTURE_INCOME :{GOLD}Infrastructure Sharing Income
STR_FINANCES_NEGATIVE_INCOME :{BLACK}-{CURRENCY_LONG}
STR_FINANCES_POSITIVE_INCOME :{BLACK}+{CURRENCY_LONG}
STR_FINANCES_TOTAL_CAPTION :{WHITE}Total:
diff --git a/src/order_cmd.cpp b/src/order_cmd.cpp
index 0a30fccb71..00f524a2a1 100644
--- a/src/order_cmd.cpp
+++ b/src/order_cmd.cpp
@@ -26,6 +26,7 @@
#include "station_base.h"
#include "waypoint_base.h"
#include "company_base.h"
+#include "infrastructure_func.h"
#include "order_backup.h"
#include "cheat_type.h"
@@ -750,7 +751,7 @@ CommandCost CmdInsertOrder(TileIndex tile, DoCommandFlag flags, uint32 p1, uint3
if (st == NULL) return CMD_ERROR;
if (st->owner != OWNER_NONE) {
- CommandCost ret = CheckOwnership(st->owner);
+ CommandCost ret = CheckInfraUsageAllowed(v->type, st->owner);
if (ret.Failed()) return ret;
}
@@ -795,7 +796,7 @@ CommandCost CmdInsertOrder(TileIndex tile, DoCommandFlag flags, uint32 p1, uint3
if (st == NULL) return CMD_ERROR;
- CommandCost ret = CheckOwnership(st->owner);
+ CommandCost ret = CheckInfraUsageAllowed(v->type, st->owner);
if (ret.Failed()) return ret;
if (!CanVehicleUseStation(v, st) || !st->airport.HasHangar()) {
@@ -806,7 +807,7 @@ CommandCost CmdInsertOrder(TileIndex tile, DoCommandFlag flags, uint32 p1, uint3
if (dp == NULL) return CMD_ERROR;
- CommandCost ret = CheckOwnership(GetTileOwner(dp->xy));
+ CommandCost ret = CheckInfraUsageAllowed(v->type, GetTileOwner(dp->xy), dp->xy);
if (ret.Failed()) return ret;
switch (v->type) {
@@ -844,7 +845,7 @@ CommandCost CmdInsertOrder(TileIndex tile, DoCommandFlag flags, uint32 p1, uint3
case VEH_TRAIN: {
if (!(wp->facilities & FACIL_TRAIN)) return_cmd_error(STR_ERROR_CAN_T_ADD_ORDER);
- CommandCost ret = CheckOwnership(wp->owner);
+ CommandCost ret = CheckInfraUsageAllowed(v->type, wp->owner);
if (ret.Failed()) return ret;
break;
}
@@ -852,7 +853,7 @@ CommandCost CmdInsertOrder(TileIndex tile, DoCommandFlag flags, uint32 p1, uint3
case VEH_SHIP:
if (!(wp->facilities & FACIL_DOCK)) return_cmd_error(STR_ERROR_CAN_T_ADD_ORDER);
if (wp->owner != OWNER_NONE) {
- CommandCost ret = CheckOwnership(wp->owner);
+ CommandCost ret = CheckInfraUsageAllowed(v->type, wp->owner);
if (ret.Failed()) return ret;
}
break;
diff --git a/src/order_gui.cpp b/src/order_gui.cpp
index 741ea24b51..ea8146231b 100644
--- a/src/order_gui.cpp
+++ b/src/order_gui.cpp
@@ -26,6 +26,7 @@
#include "station_base.h"
#include "waypoint_base.h"
#include "core/geometry_func.hpp"
+#include "infrastructure_func.h"
#include "hotkeys.h"
#include "aircraft.h"
#include "engine_func.h"
@@ -370,7 +371,7 @@ static Order GetOrderCmdFromTile(const Vehicle *v, TileIndex tile)
order.index = 0;
/* check depot first */
- if (IsDepotTypeTile(tile, (TransportType)(uint)v->type) && IsTileOwner(tile, _local_company)) {
+ if (IsDepotTypeTile(tile, (TransportType)(uint)v->type) && IsInfraTileUsageAllowed(v->type, v->owner, tile)) {
order.MakeGoToDepot(v->type == VEH_AIRCRAFT ? GetStationIndex(tile) : GetDepotIndex(tile),
ODTFB_PART_OF_ORDERS,
(_settings_client.gui.new_nonstop && v->IsGroundVehicle()) ? ONSF_NO_STOP_AT_INTERMEDIATE_STATIONS : ONSF_STOP_EVERYWHERE);
@@ -383,7 +384,7 @@ static Order GetOrderCmdFromTile(const Vehicle *v, TileIndex tile)
/* check rail waypoint */
if (IsRailWaypointTile(tile) &&
v->type == VEH_TRAIN &&
- IsTileOwner(tile, _local_company)) {
+ IsInfraTileUsageAllowed(VEH_TRAIN, v->owner, tile)) {
order.MakeGoToWaypoint(GetStationIndex(tile));
if (_settings_client.gui.new_nonstop != _ctrl_pressed) order.SetNonStopType(ONSF_NO_STOP_AT_ANY_STATION);
return order;
@@ -399,7 +400,7 @@ static Order GetOrderCmdFromTile(const Vehicle *v, TileIndex tile)
StationID st_index = GetStationIndex(tile);
const Station *st = Station::Get(st_index);
- if (st->owner == _local_company || st->owner == OWNER_NONE) {
+ if (IsInfraUsageAllowed(v->type, v->owner, st->owner)) {
byte facil;
(facil = FACIL_DOCK, v->type == VEH_SHIP) ||
(facil = FACIL_TRAIN, v->type == VEH_TRAIN) ||
diff --git a/src/pathfinder/follow_track.hpp b/src/pathfinder/follow_track.hpp
index 3995526354..57b5750757 100644
--- a/src/pathfinder/follow_track.hpp
+++ b/src/pathfinder/follow_track.hpp
@@ -19,6 +19,7 @@
#include "../tunnelbridge.h"
#include "../tunnelbridge_map.h"
#include "../depot_map.h"
+#include "../infrastructure_func.h"
#include "pf_performance_timer.hpp"
/**
@@ -299,6 +300,11 @@ protected:
m_err = EC_NO_WAY;
return false;
}
+ /* road stops shouldn't be entered unless allowed to */
+ if (!IsInfraTileUsageAllowed(VEH_ROAD, m_veh_owner, m_new_tile)) {
+ m_err = EC_OWNER;
+ return false;
+ }
}
/* single tram bits can only be entered from one direction */
@@ -317,8 +323,8 @@ protected:
m_err = EC_NO_WAY;
return false;
}
- /* don't try to enter other company's depots */
- if (GetTileOwner(m_new_tile) != m_veh_owner) {
+ /* don't try to enter other company's depots if not allowed */
+ if (!IsInfraTileUsageAllowed(VEH_ROAD, m_veh_owner, m_new_tile)) {
m_err = EC_OWNER;
return false;
}
@@ -331,8 +337,8 @@ protected:
}
}
- /* rail transport is possible only on tiles with the same owner as vehicle */
- if (IsRailTT() && GetTileOwner(m_new_tile) != m_veh_owner) {
+ /* rail transport is possible only on allowed tiles */
+ if (IsRailTT() && !IsInfraTileUsageAllowed(VEH_TRAIN, m_veh_owner, m_new_tile)) {
/* different owner */
m_err = EC_NO_WAY;
return false;
diff --git a/src/pathfinder/npf/npf.cpp b/src/pathfinder/npf/npf.cpp
index 6a5a717584..6b07125f8c 100644
--- a/src/pathfinder/npf/npf.cpp
+++ b/src/pathfinder/npf/npf.cpp
@@ -14,6 +14,7 @@
#include "../../viewport_func.h"
#include "../../ship.h"
#include "../../roadstop_base.h"
+#include "../../infrastructure_func.h"
#include "../pathfinder_func.h"
#include "../pathfinder_type.h"
#include "../follow_track.hpp"
@@ -664,25 +665,31 @@ static void NPFSaveTargetData(AyStar *as, OpenListNode *current)
*/
static bool CanEnterTileOwnerCheck(Owner owner, TileIndex tile, DiagDirection enterdir)
{
- if (IsTileType(tile, MP_RAILWAY) || // Rail tile (also rail depot)
- HasStationTileRail(tile) || // Rail station tile/waypoint
- IsRoadDepotTile(tile) || // Road depot tile
- IsStandardRoadStopTile(tile)) { // Road station tile (but not drive-through stops)
- return IsTileOwner(tile, owner); // You need to own these tiles entirely to use them
- }
-
switch (GetTileType(tile)) {
+ case MP_RAILWAY:
+ return IsInfraTileUsageAllowed(VEH_TRAIN, owner, tile); // Rail tile (also rail depot)
+
case MP_ROAD:
/* rail-road crossing : are we looking at the railway part? */
if (IsLevelCrossing(tile) &&
DiagDirToAxis(enterdir) != GetCrossingRoadAxis(tile)) {
- return IsTileOwner(tile, owner); // Railway needs owner check, while the street is public
+ return IsInfraTileUsageAllowed(VEH_TRAIN, owner, tile); // Railway needs owner check, while the street is public
+ } else if (IsRoadDepot(tile)) { // Road depot tile
+ return IsInfraTileUsageAllowed(VEH_ROAD, owner, tile);
+ }
+ break;
+
+ case MP_STATION:
+ if (HasStationRail(tile)) { // Rail station tile/waypoint
+ return IsInfraTileUsageAllowed(VEH_TRAIN, owner, tile);
+ } else if (IsStandardRoadStopTile(tile)) { // Road station tile (but not drive-through stops)
+ return IsInfraTileUsageAllowed(VEH_ROAD, owner, tile);
}
break;
case MP_TUNNELBRIDGE:
if (GetTunnelBridgeTransportType(tile) == TRANSPORT_RAIL) {
- return IsTileOwner(tile, owner);
+ return IsInfraTileUsageAllowed(VEH_TRAIN, owner, tile);
}
break;
diff --git a/src/roadveh_cmd.cpp b/src/roadveh_cmd.cpp
index 91b34d1fa0..010b161ac9 100644
--- a/src/roadveh_cmd.cpp
+++ b/src/roadveh_cmd.cpp
@@ -33,6 +33,7 @@
#include "core/random_func.hpp"
#include "company_base.h"
#include "core/backup_type.hpp"
+#include "infrastructure_func.h"
#include "newgrf.h"
#include "zoom_func.h"
@@ -379,7 +380,7 @@ CommandCost CmdTurnRoadVeh(TileIndex tile, DoCommandFlag flags, uint32 p1, uint3
if (!v->IsPrimaryVehicle()) return CMD_ERROR;
- CommandCost ret = CheckOwnership(v->owner);
+ CommandCost ret = CheckVehicleControlAllowed(v);
if (ret.Failed()) return ret;
if ((v->vehstatus & VS_STOPPED) ||
@@ -898,14 +899,14 @@ static Trackdir RoadFindPathToDest(RoadVehicle *v, TileIndex tile, DiagDirection
TrackdirBits trackdirs = TrackStatusToTrackdirBits(ts);
if (IsTileType(tile, MP_ROAD)) {
- if (IsRoadDepot(tile) && (!IsTileOwner(tile, v->owner) || GetRoadDepotDirection(tile) == enterdir || (GetRoadTypes(tile) & v->compatible_roadtypes) == 0)) {
+ if (IsRoadDepot(tile) && (!IsInfraTileUsageAllowed(VEH_ROAD, v->owner, tile) || GetRoadDepotDirection(tile) == enterdir || (GetRoadTypes(tile) & v->compatible_roadtypes) == 0)) {
/* Road depot owned by another company or with the wrong orientation */
trackdirs = TRACKDIR_BIT_NONE;
}
} else if (IsTileType(tile, MP_STATION) && IsStandardRoadStopTile(tile)) {
/* Standard road stop (drive-through stops are treated as normal road) */
- if (!IsTileOwner(tile, v->owner) || GetRoadStopDir(tile) == enterdir || v->HasArticulatedPart()) {
+ if (!IsInfraTileUsageAllowed(VEH_ROAD, v->owner, tile) || GetRoadStopDir(tile) == enterdir || v->HasArticulatedPart()) {
/* different station owner or wrong orientation or the vehicle has articulated parts */
trackdirs = TRACKDIR_BIT_NONE;
} else {
@@ -1412,7 +1413,7 @@ again:
/* In case an RV is stopped in a road stop, why not try to load? */
if (v->cur_speed == 0 && IsInsideMM(v->state, RVSB_IN_DT_ROAD_STOP, RVSB_IN_DT_ROAD_STOP_END) &&
v->current_order.ShouldStopAtStation(v, GetStationIndex(v->tile)) &&
- v->owner == GetTileOwner(v->tile) && !v->current_order.IsType(OT_LEAVESTATION) &&
+ IsInfraTileUsageAllowed(VEH_ROAD, v->owner, v->tile) && !v->current_order.IsType(OT_LEAVESTATION) &&
GetRoadStopType(v->tile) == (v->IsBus() ? ROADSTOP_BUS : ROADSTOP_TRUCK)) {
Station *st = Station::GetByTile(v->tile);
v->last_station_visited = st->index;
@@ -1445,7 +1446,7 @@ again:
_road_stop_stop_frame[v->state - RVSB_IN_ROAD_STOP + (_settings_game.vehicle.road_side << RVS_DRIVE_SIDE)] == v->frame) ||
(IsInsideMM(v->state, RVSB_IN_DT_ROAD_STOP, RVSB_IN_DT_ROAD_STOP_END) &&
v->current_order.ShouldStopAtStation(v, GetStationIndex(v->tile)) &&
- v->owner == GetTileOwner(v->tile) &&
+ IsInfraTileUsageAllowed(VEH_ROAD, v->owner, v->tile) &&
GetRoadStopType(v->tile) == (v->IsBus() ? ROADSTOP_BUS : ROADSTOP_TRUCK) &&
v->frame == RVC_DRIVE_THROUGH_STOP_FRAME))) {
diff --git a/src/saveload/afterload.cpp b/src/saveload/afterload.cpp
index 1b0f960713..334a598c38 100644
--- a/src/saveload/afterload.cpp
+++ b/src/saveload/afterload.cpp
@@ -50,6 +50,7 @@
#include "../engine_func.h"
#include "../rail_gui.h"
#include "../core/backup_type.hpp"
+#include "../core/mem_func.hpp"
#include "../smallmap_gui.h"
#include "../news_func.h"
#include "../order_backup.h"
@@ -2389,6 +2390,20 @@ bool AfterLoadGame()
FOR_ALL_DEPOTS(d) d->build_date = _date;
}
+ if (SlXvIsFeatureMissing(XSLFI_INFRA_SHARING)) {
+ Company *c;
+ FOR_ALL_COMPANIES(c) {
+ /* yearly_expenses has 3*15 entries now, saveload code gave us 3*13.
+ * Move the old data to the right place in the new array and clear the new data.
+ * The move has to be done in reverse order (first 2, then 1). */
+ MemMoveT(&c->yearly_expenses[2][0], &c->yearly_expenses[1][11], 13);
+ MemMoveT(&c->yearly_expenses[1][0], &c->yearly_expenses[0][13], 13);
+ /* Clear the old location of just-moved data, so sharing income/expenses is set to 0 */
+ MemSetT(&c->yearly_expenses[0][13], 0, 2);
+ MemSetT(&c->yearly_expenses[1][13], 0, 2);
+ }
+ }
+
/* In old versions it was possible to remove an airport while a plane was
* taking off or landing. This gives all kind of problems when building
* another airport in the same station so we don't allow that anymore.
diff --git a/src/saveload/company_sl.cpp b/src/saveload/company_sl.cpp
index 6ac22c2685..f90203e228 100644
--- a/src/saveload/company_sl.cpp
+++ b/src/saveload/company_sl.cpp
@@ -283,7 +283,8 @@ static const SaveLoad _company_desc[] = {
/* yearly expenses was changed to 64-bit in savegame version 2. */
SLE_CONDARR(CompanyProperties, yearly_expenses, SLE_FILE_I32 | SLE_VAR_I64, 3 * 13, 0, 1),
- SLE_CONDARR(CompanyProperties, yearly_expenses, SLE_INT64, 3 * 13, 2, SL_MAX_VERSION),
+ SLE_CONDARR_X(CompanyProperties, yearly_expenses, SLE_INT64, 3 * 13, 2, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_INFRA_SHARING, 0, 0)),
+ SLE_CONDARR_X(CompanyProperties, yearly_expenses, SLE_INT64, 3 * 15, 2, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_INFRA_SHARING)),
SLE_CONDVAR(CompanyProperties, is_ai, SLE_BOOL, 2, SL_MAX_VERSION),
SLE_CONDNULL(1, 107, 111), ///< is_noai
diff --git a/src/saveload/extended_ver_sl.cpp b/src/saveload/extended_ver_sl.cpp
index 9ba1fa5705..55e274dda3 100644
--- a/src/saveload/extended_ver_sl.cpp
+++ b/src/saveload/extended_ver_sl.cpp
@@ -58,6 +58,7 @@ const SlxiSubChunkInfo _sl_xv_sub_chunk_infos[] = {
{ XSLFI_AUTO_TIMETABLE, XSCF_NULL, 1, 1, "auto_timetables", NULL, NULL, NULL },
{ XSLFI_VEHICLE_REPAIR_COST, XSCF_NULL, 1, 1, "vehicle_repair_cost", NULL, NULL, NULL },
{ XSLFI_ENH_VIEWPORT_PLANS, XSCF_IGNORABLE_ALL, 1, 1, "enh_viewport_plans", NULL, NULL, "PLAN,PLLN" },
+ { XSLFI_INFRA_SHARING, XSCF_NULL, 1, 1, "infra_sharing", NULL, NULL, NULL },
{ XSLFI_NULL, XSCF_NULL, 0, 0, NULL, NULL, NULL, NULL },// This is the end marker
};
diff --git a/src/saveload/extended_ver_sl.h b/src/saveload/extended_ver_sl.h
index 68779d3cea..051bc8161b 100644
--- a/src/saveload/extended_ver_sl.h
+++ b/src/saveload/extended_ver_sl.h
@@ -33,6 +33,7 @@ enum SlXvFeatureIndex {
XSLFI_AUTO_TIMETABLE, ///< Auto timetables and separation patch
XSLFI_VEHICLE_REPAIR_COST, ///< Vehicle repair costs patch
XSLFI_ENH_VIEWPORT_PLANS, ///< Enhanced viewport patch: plans
+ XSLFI_INFRA_SHARING, ///< Infrastructure sharing patch
XSLFI_SIZE, ///< Total count of features, including null feature
};
diff --git a/src/settings.cpp b/src/settings.cpp
index bc2fb2ad45..5b04e5c087 100644
--- a/src/settings.cpp
+++ b/src/settings.cpp
@@ -69,6 +69,7 @@
#include "void_map.h"
#include "station_base.h"
+#include "infrastructure_func.h"
#include "table/strings.h"
#include "table/settings.h"
@@ -1309,6 +1310,28 @@ static bool StationCatchmentChanged(int32 p1)
return true;
}
+static bool CheckSharingRail(int32 p1)
+{
+ if (!CheckSharingChangePossible(VEH_TRAIN)) return false;
+ UpdateAllBlockSignals();
+ return true;
+}
+
+static bool CheckSharingRoad(int32 p1)
+{
+ return CheckSharingChangePossible(VEH_ROAD);
+}
+
+static bool CheckSharingWater(int32 p1)
+{
+ return CheckSharingChangePossible(VEH_SHIP);
+}
+
+static bool CheckSharingAir(int32 p1)
+{
+ return CheckSharingChangePossible(VEH_AIRCRAFT);
+}
+
static bool MaxVehiclesChanged(int32 p1)
{
InvalidateWindowClassesData(WC_BUILD_TOOLBAR);
@@ -1316,7 +1339,6 @@ static bool MaxVehiclesChanged(int32 p1)
return true;
}
-
#ifdef ENABLE_NETWORK
static bool UpdateClientName(int32 p1)
diff --git a/src/settings_gui.cpp b/src/settings_gui.cpp
index 1ae77508c9..925aaa4cd0 100644
--- a/src/settings_gui.cpp
+++ b/src/settings_gui.cpp
@@ -1790,6 +1790,19 @@ static SettingsContainer &GetSettingsTree()
npc->Add(new SettingEntry("ai.ai_disable_veh_ship"));
}
+ SettingsPage *sharing = ai->Add(new SettingsPage(STR_CONFIG_SETTING_SHARING));
+ {
+ sharing->Add(new SettingEntry("economy.infrastructure_sharing[0]"));
+ sharing->Add(new SettingEntry("economy.infrastructure_sharing[1]"));
+ sharing->Add(new SettingEntry("economy.infrastructure_sharing[2]"));
+ sharing->Add(new SettingEntry("economy.infrastructure_sharing[3]"));
+ sharing->Add(new SettingEntry("economy.sharing_fee[0]"));
+ sharing->Add(new SettingEntry("economy.sharing_fee[1]"));
+ sharing->Add(new SettingEntry("economy.sharing_fee[2]"));
+ sharing->Add(new SettingEntry("economy.sharing_fee[3]"));
+ sharing->Add(new SettingEntry("economy.sharing_payment_in_debt"));
+ }
+
ai->Add(new SettingEntry("economy.give_money"));
ai->Add(new SettingEntry("economy.allow_shares"));
}
diff --git a/src/settings_type.h b/src/settings_type.h
index 2707662d51..3d555ad3c1 100644
--- a/src/settings_type.h
+++ b/src/settings_type.h
@@ -526,6 +526,9 @@ struct EconomySettings {
TownFoundingByte found_town; ///< town founding, @see TownFounding
bool station_noise_level; ///< build new airports when the town noise level is still within accepted limits
uint16 town_noise_population[3]; ///< population to base decision on noise evaluation (@see town_council_tolerance)
+ bool infrastructure_sharing[4]; ///< enable infrastructure sharing for rail/road/water/air
+ uint sharing_fee[4]; ///< fees for infrastructure sharing for rail/road/water/air
+ bool sharing_payment_in_debt; ///< allow fee payment for companies with more loan than money (switch off to prevent MP exploits)
bool allow_town_level_crossings; ///< towns are allowed to build level crossings
int8 town_cargo_factor; ///< power-of-two multiplier for town (passenger, mail) generation. May be negative.
bool infrastructure_maintenance; ///< enable monthly maintenance fee for owner infrastructure
diff --git a/src/ship_cmd.cpp b/src/ship_cmd.cpp
index 40d5942302..7383080d5f 100644
--- a/src/ship_cmd.cpp
+++ b/src/ship_cmd.cpp
@@ -31,6 +31,7 @@
#include "pathfinder/opf/opf_ship.h"
#include "engine_base.h"
#include "company_base.h"
+#include "infrastructure_func.h"
#include "tunnelbridge_map.h"
#include "zoom_func.h"
@@ -144,7 +145,7 @@ static const Depot *FindClosestShipDepot(const Vehicle *v, uint max_distance)
FOR_ALL_DEPOTS(depot) {
TileIndex tile = depot->xy;
- if (IsShipDepotTile(tile) && IsTileOwner(tile, v->owner)) {
+ if (IsShipDepotTile(tile) && IsInfraTileUsageAllowed(VEH_SHIP, v->owner, tile)) {
uint dist = DistanceManhattan(tile, v->tile);
if (dist < best_dist) {
best_dist = dist;
diff --git a/src/signal.cpp b/src/signal.cpp
index be087ccd42..a68861f82f 100644
--- a/src/signal.cpp
+++ b/src/signal.cpp
@@ -21,6 +21,7 @@
#include "table/strings.h"
#include "programmable_signals.h"
#include "error.h"
+#include "infrastructure_func.h"
#include "safeguards.h"
@@ -308,7 +309,7 @@ static SigInfo ExploreSegment(Owner owner)
switch (GetTileType(tile)) {
case MP_RAILWAY: {
- if (GetTileOwner(tile) != owner) continue; // do not propagate signals on others' tiles (remove for tracksharing)
+ if (!IsOneSignalBlock(owner, GetTileOwner(tile))) continue;
if (IsRailDepot(tile)) {
if (enterdir == INVALID_DIAGDIR) { // from 'inside' - train just entered or left the depot
@@ -385,7 +386,7 @@ static SigInfo ExploreSegment(Owner owner)
case MP_STATION:
if (!HasStationRail(tile)) continue;
- if (GetTileOwner(tile) != owner) continue;
+ if (!IsOneSignalBlock(owner, GetTileOwner(tile))) continue;
if (DiagDirToAxis(enterdir) != GetRailStationAxis(tile)) continue; // different axis
if (IsStationTileBlocked(tile)) continue; // 'eye-candy' station tile
@@ -395,7 +396,7 @@ static SigInfo ExploreSegment(Owner owner)
case MP_ROAD:
if (!IsLevelCrossing(tile)) continue;
- if (GetTileOwner(tile) != owner) continue;
+ if (!IsOneSignalBlock(owner, GetTileOwner(tile))) continue;
if (DiagDirToAxis(enterdir) == GetCrossingRoadAxis(tile)) continue; // different axis
if (!(info.flags & SF_TRAIN) && HasVehicleOnPos(tile, NULL, &TrainOnTileEnum)) info.flags |= SF_TRAIN;
@@ -403,7 +404,7 @@ static SigInfo ExploreSegment(Owner owner)
break;
case MP_TUNNELBRIDGE: {
- if (GetTileOwner(tile) != owner) continue;
+ if (!IsOneSignalBlock(owner, GetTileOwner(tile))) continue;
if (GetTunnelBridgeTransportType(tile) != TRANSPORT_RAIL) continue;
DiagDirection dir = GetTunnelBridgeDirection(tile);
@@ -671,8 +672,9 @@ void AddTrackToSignalBuffer(TileIndex tile, Track track, Owner owner)
DIAGDIR_SW, DIAGDIR_NW, DIAGDIR_NW, DIAGDIR_SW, DIAGDIR_NW, DIAGDIR_NE
};
- /* do not allow signal updates for two companies in one run */
- assert(_globset.IsEmpty() || owner == _last_owner);
+ /* do not allow signal updates for two companies in one run,
+ * if these companies are not part of the same signal block */
+ assert(_globset.IsEmpty() || IsOneSignalBlock(owner, _last_owner));
_last_owner = owner;
@@ -696,8 +698,9 @@ void AddTrackToSignalBuffer(TileIndex tile, Track track, Owner owner)
*/
void AddSideToSignalBuffer(TileIndex tile, DiagDirection side, Owner owner)
{
- /* do not allow signal updates for two companies in one run */
- assert(_globset.IsEmpty() || owner == _last_owner);
+ /* do not allow signal updates for two companies in one run,
+ * if these companies are not part of the same signal block */
+ assert(_globset.IsEmpty() || IsOneSignalBlock(owner, _last_owner));
_last_owner = owner;
diff --git a/src/table/settings.ini b/src/table/settings.ini
index 3d82046855..7b8cb6b593 100644
--- a/src/table/settings.ini
+++ b/src/table/settings.ini
@@ -48,6 +48,10 @@ static bool UpdateClientName(int32 p1);
static bool UpdateServerPassword(int32 p1);
static bool UpdateRconPassword(int32 p1);
static bool UpdateClientConfigValues(int32 p1);
+static bool CheckSharingRail(int32 p1);
+static bool CheckSharingRoad(int32 p1);
+static bool CheckSharingWater(int32 p1);
+static bool CheckSharingAir(int32 p1);
#endif /* ENABLE_NETWORK */
/* End - Callback Functions for the various settings */
@@ -1531,6 +1535,98 @@ cat = SC_EXPERT
extver = SlXvFeatureTest(XSLFTO_AND, XSLFI_PROG_SIGS)
patxname = ""programmable_signals.construction.maximum_signal_evaluations""
+[SDT_BOOL]
+base = GameSettings
+var = economy.infrastructure_sharing[0]
+def = false
+str = STR_CONFIG_SETTING_SHARING_RAIL
+proc = CheckSharingRail
+extver = SlXvFeatureTest(XSLFTO_AND, XSLFI_INFRA_SHARING)
+patxname = ""infra_sharing.economy.infrastructure_sharing.rail""
+
+[SDT_BOOL]
+base = GameSettings
+var = economy.infrastructure_sharing[1]
+def = false
+str = STR_CONFIG_SETTING_SHARING_ROAD
+proc = CheckSharingRoad
+extver = SlXvFeatureTest(XSLFTO_AND, XSLFI_INFRA_SHARING)
+patxname = ""infra_sharing.economy.infrastructure_sharing.road""
+
+[SDT_BOOL]
+base = GameSettings
+var = economy.infrastructure_sharing[2]
+def = false
+str = STR_CONFIG_SETTING_SHARING_WATER
+proc = CheckSharingWater
+extver = SlXvFeatureTest(XSLFTO_AND, XSLFI_INFRA_SHARING)
+patxname = ""infra_sharing.economy.infrastructure_sharing.water""
+
+[SDT_BOOL]
+base = GameSettings
+var = economy.infrastructure_sharing[3]
+def = false
+str = STR_CONFIG_SETTING_SHARING_AIR
+proc = CheckSharingAir
+extver = SlXvFeatureTest(XSLFTO_AND, XSLFI_INFRA_SHARING)
+patxname = ""infra_sharing.economy.infrastructure_sharing.air""
+
+[SDT_VAR]
+base = GameSettings
+var = economy.sharing_fee[0]
+type = SLE_UINT
+def = 100
+min = 0
+max = 1000000
+interval = 10
+str = STR_CONFIG_SETTING_SHARING_FEE_RAIL
+extver = SlXvFeatureTest(XSLFTO_AND, XSLFI_INFRA_SHARING)
+patxname = ""infra_sharing.economy.sharing_fee.rail""
+
+[SDT_VAR]
+base = GameSettings
+var = economy.sharing_fee[1]
+type = SLE_UINT
+def = 100
+min = 0
+max = 1000000
+interval = 10
+str = STR_CONFIG_SETTING_SHARING_FEE_ROAD
+extver = SlXvFeatureTest(XSLFTO_AND, XSLFI_INFRA_SHARING)
+patxname = ""infra_sharing.economy.sharing_fee.road""
+
+[SDT_VAR]
+base = GameSettings
+var = economy.sharing_fee[2]
+type = SLE_UINT
+def = 100
+min = 0
+max = 1000000
+interval = 10
+str = STR_CONFIG_SETTING_SHARING_FEE_WATER
+extver = SlXvFeatureTest(XSLFTO_AND, XSLFI_INFRA_SHARING)
+patxname = ""infra_sharing.economy.sharing_fee.water""
+
+[SDT_VAR]
+base = GameSettings
+var = economy.sharing_fee[3]
+type = SLE_UINT
+def = 100
+min = 0
+max = 1000000
+interval = 10
+str = STR_CONFIG_SETTING_SHARING_FEE_AIR
+extver = SlXvFeatureTest(XSLFTO_AND, XSLFI_INFRA_SHARING)
+patxname = ""infra_sharing.economy.sharing_fee.air""
+
+[SDT_BOOL]
+base = GameSettings
+var = economy.sharing_payment_in_debt
+def = false
+str = STR_CONFIG_SETTING_SHARING_PAYMENT_IN_DEBT
+extver = SlXvFeatureTest(XSLFTO_AND, XSLFI_INFRA_SHARING)
+patxname = ""infra_sharing.economy.sharing_payment_in_debt""
+
; previously ai-new setting.
[SDT_NULL]
length = 1
diff --git a/src/train.h b/src/train.h
index 9c745676b4..d615b40933 100644
--- a/src/train.h
+++ b/src/train.h
@@ -72,6 +72,8 @@ void CheckTrainsLengths();
void FreeTrainTrackReservation(const Train *v, TileIndex origin = INVALID_TILE, Trackdir orig_td = INVALID_TRACKDIR);
bool TryPathReserve(Train *v, bool mark_as_stuck = false, bool first_tile_okay = false);
+void DeleteVisibleTrain(Train *v);
+
int GetTrainStopLocation(StationID station_id, TileIndex tile, const Train *v, int *station_ahead, int *station_length);
void CheckBreakdownFlags(Train *v);
void GetTrainSpriteSize(EngineID engine, uint &width, uint &height, int &xoffs, int &yoffs, EngineImageType image_type);
diff --git a/src/train_cmd.cpp b/src/train_cmd.cpp
index bb85d0327b..d6321fa6e1 100644
--- a/src/train_cmd.cpp
+++ b/src/train_cmd.cpp
@@ -32,6 +32,7 @@
#include "core/random_func.hpp"
#include "company_base.h"
#include "newgrf.h"
+#include "infrastructure_func.h"
#include "order_backup.h"
#include "zoom_func.h"
#include "newgrf_debug.h"
@@ -2072,7 +2073,7 @@ CommandCost CmdForceTrainProceed(TileIndex tile, DoCommandFlag flags, uint32 p1,
if (!t->IsPrimaryVehicle()) return CMD_ERROR;
- CommandCost ret = CheckOwnership(t->owner);
+ CommandCost ret = CheckVehicleControlAllowed(t);
if (ret.Failed()) return ret;
@@ -3012,7 +3013,7 @@ static void TrainEnterStation(Train *v, StationID station)
/* Check if the vehicle is compatible with the specified tile */
static inline bool CheckCompatibleRail(const Train *v, TileIndex tile)
{
- return IsTileOwner(tile, v->owner) &&
+ return IsInfraTileUsageAllowed(VEH_TRAIN, v->owner, tile) &&
(!v->IsFrontEngine() || HasBit(v->compatible_railtypes, GetRailType(tile)));
}
@@ -4323,6 +4324,9 @@ void Train::OnNewDay()
/* running costs */
CommandCost cost(EXPENSES_TRAIN_RUN, this->GetRunningCost() * this->running_ticks / (DAYS_IN_YEAR * DAY_TICKS));
+ /* sharing fee */
+ PayDailyTrackSharingFee(this);
+
this->profit_this_year -= cost.GetCost();
this->running_ticks = 0;
@@ -4357,3 +4361,49 @@ Trackdir Train::GetVehicleTrackdir() const
return TrackDirectionToTrackdir(FindFirstTrack(this->track), this->direction);
}
+
+/**
+ * Delete a train while it is visible.
+ * This happens when a company bankrupts when infrastructure sharing is enabled.
+ * @param v The train to delete.
+ */
+void DeleteVisibleTrain(Train *v)
+{
+ FreeTrainTrackReservation(v);
+ TileIndex crossing = TrainApproachingCrossingTile(v);
+
+ /* delete train from back to front */
+ Train *u;
+ Train *prev = v->Last();
+ do {
+ u = prev;
+ prev = u->Previous();
+ if (prev != NULL) prev->SetNext(NULL);
+
+ /* 'u' shouldn't be accessed after it has been deleted */
+ TileIndex tile = u->tile;
+ TrackBits trackbits = u->track;
+
+ delete u;
+
+ if (trackbits == TRACK_BIT_WORMHOLE) {
+ /* Vehicle is inside a wormhole, u->track contains no useful value then. */
+ trackbits = DiagDirToDiagTrackBits(GetTunnelBridgeDirection(tile));
+ }
+
+ Track track = TrackBitsToTrack(trackbits);
+ if (HasReservedTracks(tile, trackbits)) UnreserveRailTrack(tile, track);
+ if (IsLevelCrossingTile(tile)) UpdateLevelCrossing(tile);
+
+ /* Update signals */
+ if (IsTileType(tile, MP_TUNNELBRIDGE) || IsRailDepotTile(tile)) {
+ AddSideToSignalBuffer(tile, INVALID_DIAGDIR, GetTileOwner(tile));
+ } else {
+ AddTrackToSignalBuffer(tile, track, GetTileOwner(tile));
+ }
+ } while (prev != NULL);
+
+ if (crossing != INVALID_TILE) UpdateLevelCrossing(crossing);
+
+ UpdateSignalsInBuffer();
+}
diff --git a/src/vehicle.cpp b/src/vehicle.cpp
index c234d8128f..cffc35afbf 100644
--- a/src/vehicle.cpp
+++ b/src/vehicle.cpp
@@ -41,6 +41,7 @@
#include "roadstop_base.h"
#include "core/random_func.hpp"
#include "core/backup_type.hpp"
+#include "infrastructure_func.h"
#include "order_backup.h"
#include "sound_func.h"
#include "effectvehicle_func.h"
@@ -2476,6 +2477,9 @@ void Vehicle::HandleLoading(bool mode)
this->current_loading_time = this->current_order_time;
}
+ /* Pay the loading fee for using someone else's station, if appropriate */
+ if (!mode && this->type != VEH_TRAIN) PayStationSharingFee(this, Station::Get(this->last_station_visited));
+
/* Not the first call for this tick, or still loading */
if (mode || !HasBit(this->vehicle_flags, VF_LOADING_FINISHED) || this->current_order_time < wait_time) return;
diff --git a/src/vehicle_cmd.cpp b/src/vehicle_cmd.cpp
index 8c0c0106e0..abfa1bfeb7 100644
--- a/src/vehicle_cmd.cpp
+++ b/src/vehicle_cmd.cpp
@@ -28,6 +28,7 @@
#include "autoreplace_gui.h"
#include "group.h"
#include "order_backup.h"
+#include "infrastructure_func.h"
#include "ship.h"
#include "newgrf.h"
#include "company_base.h"
@@ -439,7 +440,7 @@ CommandCost CmdRefitVehicle(TileIndex tile, DoCommandFlag flags, uint32 p1, uint
Vehicle *front = v->First();
- CommandCost ret = CheckOwnership(front->owner);
+ CommandCost ret = CheckVehicleControlAllowed(v);
if (ret.Failed()) return ret;
bool auto_refit = HasBit(p2, 6);
@@ -517,7 +518,7 @@ CommandCost CmdStartStopVehicle(TileIndex tile, DoCommandFlag flags, uint32 p1,
Vehicle *v = Vehicle::GetIfValid(p1);
if (v == NULL || !v->IsPrimaryVehicle()) return CMD_ERROR;
- CommandCost ret = CheckOwnership(v->owner);
+ CommandCost ret = CheckVehicleControlAllowed(v);
if (ret.Failed()) return ret;
if (v->vehstatus & VS_CRASHED) return_cmd_error(STR_ERROR_VEHICLE_IS_DESTROYED);
@@ -679,7 +680,7 @@ CommandCost CmdDepotMassAutoReplace(TileIndex tile, DoCommandFlag flags, uint32
VehicleType vehicle_type = Extract(p1);
if (!IsCompanyBuildableVehicleType(vehicle_type)) return CMD_ERROR;
- if (!IsDepotTile(tile) || !IsTileOwner(tile, _current_company)) return CMD_ERROR;
+ if (!IsDepotTile(tile) || !IsInfraUsageAllowed(vehicle_type, _current_company, GetTileOwner(tile))) return CMD_ERROR;
/* Get the list of vehicles in the depot */
BuildDepotVehicleList(vehicle_type, tile, &list, &list, true);
diff --git a/src/vehicle_gui.cpp b/src/vehicle_gui.cpp
index 19009c77ee..275e7936ec 100644
--- a/src/vehicle_gui.cpp
+++ b/src/vehicle_gui.cpp
@@ -36,6 +36,7 @@
#include "company_base.h"
#include "engine_func.h"
#include "station_base.h"
+#include "infrastructure_func.h"
#include "tilehighlight_func.h"
#include "train.h"
#include "zoom_func.h"
@@ -2649,6 +2650,7 @@ public:
{
const Vehicle *v = Vehicle::Get(this->window_number);
bool is_localcompany = v->owner == _local_company;
+ bool can_control = IsVehicleControlAllowed(v, _local_company);
bool refitable_and_stopped_in_depot = IsVehicleRefitable(v);
this->SetWidgetDisabledState(WID_VV_GOTO_DEPOT, !is_localcompany);
@@ -2657,8 +2659,8 @@ public:
if (v->type == VEH_TRAIN) {
this->SetWidgetLoweredState(WID_VV_FORCE_PROCEED, Train::From(v)->force_proceed == TFP_SIGNAL);
- this->SetWidgetDisabledState(WID_VV_FORCE_PROCEED, !is_localcompany);
- this->SetWidgetDisabledState(WID_VV_TURN_AROUND, !is_localcompany);
+ this->SetWidgetDisabledState(WID_VV_FORCE_PROCEED, !can_control);
+ this->SetWidgetDisabledState(WID_VV_TURN_AROUND, !can_control);
}
this->DrawWidgets();