From ee791055f909694ad546dea09d660a86ac9fcf4d Mon Sep 17 00:00:00 2001 From: patch-import Date: Thu, 6 Aug 2015 22:24:24 +0100 Subject: [PATCH] Import infrastructure sharing patch Strip trailing whitespace Remove a leftover line form settings.ini http://www.tt-forums.net/viewtopic.php?p=1008843#p1008843 --- projects/openttd_vs100.vcxproj | 2 + projects/openttd_vs80.vcproj | 8 + projects/openttd_vs90.vcproj | 8 + source.list | 2 + src/aircraft_cmd.cpp | 5 +- src/company_cmd.cpp | 6 +- src/company_gui.cpp | 4 + src/depot_gui.cpp | 4 +- src/economy.cpp | 37 ++-- src/economy_type.h | 2 + src/infrastructure.cpp | 346 ++++++++++++++++++++++++++++++++ src/infrastructure_func.h | 107 ++++++++++ src/lang/english.txt | 15 ++ src/order_cmd.cpp | 11 +- src/order_gui.cpp | 11 +- src/pathfinder/follow_track.hpp | 14 +- src/pathfinder/npf/npf.cpp | 25 ++- src/roadveh_cmd.cpp | 11 +- src/saveload/afterload.cpp | 15 ++ src/saveload/company_sl.cpp | 3 +- src/saveload/saveload.cpp | 2 +- src/settings.cpp | 22 ++ src/settings_gui.cpp | 15 ++ src/settings_type.h | 3 + src/ship_cmd.cpp | 3 +- src/signal.cpp | 19 +- src/table/settings.ini | 87 ++++++++ src/train.h | 2 + src/train_cmd.cpp | 54 ++++- src/vehicle.cpp | 4 + src/vehicle_cmd.cpp | 7 +- src/vehicle_gui.cpp | 6 +- 32 files changed, 794 insertions(+), 66 deletions(-) create mode 100644 src/infrastructure.cpp create mode 100644 src/infrastructure_func.h diff --git a/projects/openttd_vs100.vcxproj b/projects/openttd_vs100.vcxproj index be3eb0a0d3..4c6ee7b42a 100644 --- a/projects/openttd_vs100.vcxproj +++ b/projects/openttd_vs100.vcxproj @@ -324,6 +324,7 @@ + @@ -457,6 +458,7 @@ + diff --git a/projects/openttd_vs80.vcproj b/projects/openttd_vs80.vcproj index fb895a43f5..857e1a281f 100644 --- a/projects/openttd_vs80.vcproj +++ b/projects/openttd_vs80.vcproj @@ -567,6 +567,10 @@ > ++ ++ @@ -1103,6 +1107,10 @@ > ++ ++ diff --git a/projects/openttd_vs90.vcproj b/projects/openttd_vs90.vcproj index 37dc590031..3e3cbda1f2 100644 --- a/projects/openttd_vs90.vcproj +++ b/projects/openttd_vs90.vcproj @@ -563,6 +563,10 @@ RelativePath=".\..\src\highscore.cpp" > + + @@ -1099,6 +1103,10 @@ RelativePath=".\..\src\industrytype.h" > + + diff --git a/source.list b/source.list index 774df5d8a8..4240531a7e 100644 --- a/source.list +++ b/source.list @@ -32,6 +32,7 @@ goal.cpp ground_vehicle.cpp heightmap.cpp highscore.cpp +infrastructure.cpp hotkeys.cpp ini.cpp ini_load.cpp @@ -190,6 +191,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 7bdba7deaa..4005bd15c2 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 "table/strings.h" @@ -123,7 +124,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() || ( @@ -1462,7 +1463,7 @@ static void AircraftEventHandler_Flying(Aircraft *v, const AirportFTAClass *apc) Station *st = Station::Get(v->targetairport); /* runway busy or not allowed to use this airstation, circle */ - if (CanVehicleUseStation(v, st) && (st->owner == OWNER_NONE || st->owner == v->owner)) { + if (CanVehicleUseStation(v, st) && IsInfraUsageAllowed(VEH_AIRCRAFT, v->owner, st->owner)) { /* {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 2850ad912d..5824952de1 100644 --- a/src/company_cmd.cpp +++ b/src/company_cmd.cpp @@ -212,14 +212,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 6804e98d12..9789fff90b 100644 --- a/src/company_gui.cpp +++ b/src/company_gui.cpp @@ -60,6 +60,8 @@ static ExpensesType _expenses_list_1[] = { EXPENSES_AIRCRAFT_INC, EXPENSES_SHIP_INC, EXPENSES_LOAN_INT, + EXPENSES_SHARING_INC, + EXPENSES_SHARING_COST, EXPENSES_OTHER, }; @@ -69,6 +71,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, @@ -76,6 +79,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 4b040882e3..001f3a5a26 100644 --- a/src/depot_gui.cpp +++ b/src/depot_gui.cpp @@ -25,6 +25,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" @@ -655,7 +657,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 e449465f8c..3a4d0576f9 100644 --- a/src/economy.cpp +++ b/src/economy.cpp @@ -42,6 +42,7 @@ #include "economy_base.h" #include "core/pool_func.hpp" #include "core/backup_type.hpp" +#include "infrastructure_func.h" #include "water.h" #include "game/game.hpp" @@ -407,7 +408,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) @@ -438,6 +440,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 */ @@ -452,22 +458,14 @@ void ChangeOwnershipOfCompanyItems(Owner old_owner, Owner new_owner) * and signals were not propagated * Similiar 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(); } @@ -1114,7 +1112,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(); @@ -1137,6 +1141,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_profit += 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 a15f4b5e3a..3438e0309b 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..0060e90a6d --- /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.Count() * 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 0b06eeaf31..876ea7fb02 100644 --- a/src/lang/english.txt +++ b/src/lang/english.txt @@ -1276,6 +1276,18 @@ STR_CONFIG_SETTING_AI_BUILDS_SHIPS :{LTBLUE}Disable STR_CONFIG_SETTING_AI_IN_MULTIPLAYER :{LTBLUE}Allow AIs in multiplayer: {ORANGE}{STRING1} STR_CONFIG_SETTING_SCRIPT_MAX_OPCODES :{LTBLUE}#opcodes before scripts are suspended: {ORANGE}{STRING1} +STR_CONFIG_SETTING_SHARING_RAIL :{LTBLUE}Enable sharing of railways: {ORANGE}{STRING1} +STR_CONFIG_SETTING_SHARING_ROAD :{LTBLUE}Enable sharing of road stops and depots: {ORANGE}{STRING1} +STR_CONFIG_SETTING_SHARING_WATER :{LTBLUE}Enable sharing of docks and ship depots: {ORANGE}{STRING1} +STR_CONFIG_SETTING_SHARING_AIR :{LTBLUE}Enable sharing of airports: {ORANGE}{STRING1} +STR_CONFIG_SETTING_SHARING_FEE_RAIL :{LTBLUE}Daily track toll for trains: {ORANGE}{STRING1} per 1000 tonnes +STR_CONFIG_SETTING_SHARING_FEE_ROAD :{LTBLUE}Stopping fee for road vehicles: {ORANGE}{STRING1} per day +STR_CONFIG_SETTING_SHARING_FEE_WATER :{LTBLUE}Docking fee for ships: {ORANGE}{STRING1} per day +STR_CONFIG_SETTING_SHARING_FEE_AIR :{LTBLUE}Terminal fee for aircraft: {ORANGE}{STRING1} per day +STR_CONFIG_SETTING_SHARING_PAYMENT_IN_DEBT :{LTBLUE}Allow companies in debt to pay sharing fees: {ORANGE}{STRING1} +STR_CONFIG_SETTING_SHARING_USED_BY_VEHICLES :{WHITE}Can't change this setting, vehicles are using shared infrastructure. +STR_CONFIG_SETTING_SHARING_ORDERS_TO_OTHERS :{WHITE}Can't change this setting, vehicles have orders to destinations of others. + STR_CONFIG_SETTING_SERVINT_ISPERCENT :{LTBLUE}Service intervals are in percents: {ORANGE}{STRING1} STR_CONFIG_SETTING_SERVINT_TRAINS :{LTBLUE}Default service interval for trains: {ORANGE}{STRING1} day{P 0:1 "" s}/% STR_CONFIG_SETTING_SERVINT_TRAINS_DISABLED :{LTBLUE}Default service interval for trains: {ORANGE}disabled @@ -1361,6 +1373,7 @@ STR_CONFIG_SETTING_INTERACTION :{ORANGE}Interac STR_CONFIG_SETTING_CONSTRUCTION_SIGNALS :{ORANGE}Signals STR_CONFIG_SETTING_STATIONS_CARGOHANDLING :{ORANGE}Cargo handling STR_CONFIG_SETTING_AI_NPC :{ORANGE}Computer players +STR_CONFIG_SETTING_SHARING :{ORANGE}Infrastructure sharing STR_CONFIG_SETTING_VEHICLES_AUTORENEW :{ORANGE}Autorenew STR_CONFIG_SETTING_VEHICLES_SERVICING :{ORANGE}Servicing STR_CONFIG_SETTING_VEHICLES_ROUTING :{ORANGE}Routing @@ -2777,6 +2790,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 61ba4e1212..1397b99f65 100644 --- a/src/order_cmd.cpp +++ b/src/order_cmd.cpp @@ -25,6 +25,7 @@ #include "station_base.h" #include "waypoint_base.h" #include "company_base.h" +#include "infrastructure_func.h" #include "order_backup.h" #include "table/strings.h" @@ -640,7 +641,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; } @@ -685,7 +686,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()) { @@ -696,7 +697,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) { @@ -734,7 +735,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; } @@ -742,7 +743,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 0e7579546c..2e9bb75c61 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" @@ -355,7 +356,7 @@ static Order GetOrderCmdFromTile(const Vehicle *v, TileIndex tile) /* check depot first */ switch (GetTileType(tile)) { case MP_RAILWAY: - if (v->type == VEH_TRAIN && IsTileOwner(tile, _local_company)) { + if (v->type == VEH_TRAIN && IsInfraTileUsageAllowed(VEH_TRAIN, v->owner, tile)) { if (IsRailDepot(tile)) { order.MakeGoToDepot(GetDepotIndex(tile), ODTFB_PART_OF_ORDERS, _settings_client.gui.new_nonstop ? ONSF_NO_STOP_AT_INTERMEDIATE_STATIONS : ONSF_STOP_EVERYWHERE); @@ -376,7 +377,7 @@ static Order GetOrderCmdFromTile(const Vehicle *v, TileIndex tile) case MP_STATION: if (v->type != VEH_AIRCRAFT) break; - if (IsHangar(tile) && IsTileOwner(tile, _local_company)) { + if (IsHangar(tile) && IsInfraTileUsageAllowed(VEH_AIRCRAFT, v->owner, tile)) { order.MakeGoToDepot(GetStationIndex(tile), ODTFB_PART_OF_ORDERS, ONSF_STOP_EVERYWHERE); if (_ctrl_pressed) order.SetDepotOrderType((OrderDepotTypeFlags)(order.GetDepotOrderType() ^ ODTFB_SERVICE)); return order; @@ -385,7 +386,7 @@ static Order GetOrderCmdFromTile(const Vehicle *v, TileIndex tile) case MP_WATER: if (v->type != VEH_SHIP) break; - if (IsShipDepot(tile) && IsTileOwner(tile, _local_company)) { + if (IsShipDepot(tile) && IsInfraTileUsageAllowed(VEH_SHIP, v->owner, tile)) { order.MakeGoToDepot(GetDepotIndex(tile), ODTFB_PART_OF_ORDERS, ONSF_STOP_EVERYWHERE); if (_ctrl_pressed) order.SetDepotOrderType((OrderDepotTypeFlags)(order.GetDepotOrderType() ^ ODTFB_SERVICE)); return order; @@ -399,7 +400,7 @@ static Order GetOrderCmdFromTile(const Vehicle *v, TileIndex tile) /* check waypoint */ if (IsRailWaypointTile(tile) && v->type == VEH_TRAIN && - IsTileOwner(tile, _local_company)) { + IsInfraTileUsageAllowed(VEH_TRAIN, v->owner, tile)) { order.MakeGoToWaypoint(Waypoint::GetByTile(tile)->index); if (_settings_client.gui.new_nonstop != _ctrl_pressed) order.SetNonStopType(ONSF_NO_STOP_AT_ANY_STATION); return order; @@ -414,7 +415,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 1050521d97..18a29748f5 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" /** @@ -298,6 +299,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 */ @@ -316,8 +322,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; } @@ -330,8 +336,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 016db5abe8..78aed101f6 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" @@ -662,25 +663,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 6a82250502..232e040118 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" @@ -356,7 +357,7 @@ CommandCost CmdTurnRoadVeh(TileIndex tile, DoCommandFlag flags, uint32 p1, uint3 RoadVehicle *v = RoadVehicle::GetIfValid(p1); if (v == NULL) return CMD_ERROR; - CommandCost ret = CheckOwnership(v->owner); + CommandCost ret = CheckVehicleControlAllowed(v); if (ret.Failed()) return ret; if ((v->vehstatus & VS_STOPPED) || @@ -857,14 +858,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 { @@ -1377,7 +1378,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; @@ -1411,7 +1412,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 7cb9f0ff07..2826b8f5c6 100644 --- a/src/saveload/afterload.cpp +++ b/src/saveload/afterload.cpp @@ -49,6 +49,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 "../error.h" @@ -2296,6 +2297,20 @@ bool AfterLoadGame() FOR_ALL_DEPOTS(d) d->build_date = _date; } + if (IsSavegameVersionBefore(200)) { + 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 e9cd02d95c..64be5b7a26 100644 --- a/src/saveload/company_sl.cpp +++ b/src/saveload/company_sl.cpp @@ -281,7 +281,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(CompanyProperties, yearly_expenses, SLE_INT64, 3 * 13, 2, 200), + SLE_CONDARR(CompanyProperties, yearly_expenses, SLE_INT64, 3 * 15, 200, SL_MAX_VERSION), SLE_CONDVAR(CompanyProperties, is_ai, SLE_BOOL, 2, SL_MAX_VERSION), SLE_CONDNULL(1, 107, 111), ///< is_noai diff --git a/src/saveload/saveload.cpp b/src/saveload/saveload.cpp index b834123355..6eaae1b9c7 100644 --- a/src/saveload/saveload.cpp +++ b/src/saveload/saveload.cpp @@ -239,7 +239,7 @@ * 173 23967 1.2.0-RC1 * 174 23973 1.2.x */ -extern const uint16 SAVEGAME_VERSION = 174; ///< Current savegame version of OpenTTD. +extern const uint16 SAVEGAME_VERSION = 200; ///< Current savegame version of OpenTTD. SavegameType _savegame_type; ///< type of savegame we are loading diff --git a/src/settings.cpp b/src/settings.cpp index 2800093fe7..505e6765e9 100644 --- a/src/settings.cpp +++ b/src/settings.cpp @@ -67,6 +67,7 @@ #include "void_map.h" #include "station_base.h" +#include "infrastructure_func.h" #include "table/strings.h" #include "table/settings.h" @@ -1185,6 +1186,27 @@ 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); +} #ifdef ENABLE_NETWORK diff --git a/src/settings_gui.cpp b/src/settings_gui.cpp index 308839d51f..59fddb2413 100644 --- a/src/settings_gui.cpp +++ b/src/settings_gui.cpp @@ -1548,8 +1548,23 @@ static SettingEntry _settings_ai_npc[] = { /** Computer players sub-page */ static SettingsPage _settings_ai_npc_page = {_settings_ai_npc, lengthof(_settings_ai_npc)}; +static SettingEntry _settings_sharing[] = { + SettingEntry("economy.infrastructure_sharing[0]"), + SettingEntry("economy.infrastructure_sharing[1]"), + SettingEntry("economy.infrastructure_sharing[2]"), + SettingEntry("economy.infrastructure_sharing[3]"), + SettingEntry("economy.sharing_fee[0]"), + SettingEntry("economy.sharing_fee[1]"), + SettingEntry("economy.sharing_fee[2]"), + SettingEntry("economy.sharing_fee[3]"), + SettingEntry("economy.sharing_payment_in_debt"), +}; +/** Infrastructure sharing sub-page */ +static SettingsPage _settings_sharing_page = {_settings_sharing, lengthof(_settings_sharing)}; + static SettingEntry _settings_ai[] = { SettingEntry(&_settings_ai_npc_page, STR_CONFIG_SETTING_AI_NPC), + SettingEntry(&_settings_sharing_page, STR_CONFIG_SETTING_SHARING), SettingEntry("economy.give_money"), SettingEntry("economy.allow_shares"), }; diff --git a/src/settings_type.h b/src/settings_type.h index 1f91a542a7..78d2c02586 100644 --- a/src/settings_type.h +++ b/src/settings_type.h @@ -420,6 +420,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 bool infrastructure_maintenance; ///< enable monthly maintenance fee for owner infrastructure }; diff --git a/src/ship_cmd.cpp b/src/ship_cmd.cpp index ebb6d9bfff..a03d107dbf 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" @@ -127,7 +128,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 d991d6233d..6da73ea4c8 100644 --- a/src/signal.cpp +++ b/src/signal.cpp @@ -17,6 +17,7 @@ #include "viewport_func.h" #include "train.h" #include "company_base.h" +#include "infrastructure_func.h" /** these are the maximums used for updating signal blocks */ @@ -277,7 +278,7 @@ static SigFlags 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 @@ -351,7 +352,7 @@ static SigFlags 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 @@ -361,7 +362,7 @@ static SigFlags 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 (!(flags & SF_TRAIN) && HasVehicleOnPos(tile, NULL, &TrainOnTileEnum)) flags |= SF_TRAIN; @@ -369,7 +370,7 @@ static SigFlags 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); @@ -583,8 +584,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; @@ -608,8 +610,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 aec498f820..8e68ee2f06 100644 --- a/src/table/settings.ini +++ b/src/table/settings.ini @@ -47,6 +47,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 */ @@ -1123,6 +1127,89 @@ from = 77 def = true str = STR_CONFIG_SETTING_MODIFIED_ROAD_REBUILD +[SDT_BOOL] +base = GameSettings +var = economy.infrastructure_sharing[0] +from = 200 +def = false +str = STR_CONFIG_SETTING_SHARING_RAIL +proc = CheckSharingRail + +[SDT_BOOL] +base = GameSettings +var = economy.infrastructure_sharing[1] +from = 200 +def = false +str = STR_CONFIG_SETTING_SHARING_ROAD +proc = CheckSharingRoad + +[SDT_BOOL] +base = GameSettings +var = economy.infrastructure_sharing[2] +from = 200 +def = false +str = STR_CONFIG_SETTING_SHARING_WATER +proc = CheckSharingWater + +[SDT_BOOL] +base = GameSettings +var = economy.infrastructure_sharing[3] +from = 200 +def = false +str = STR_CONFIG_SETTING_SHARING_AIR +proc = CheckSharingAir + +[SDT_VAR] +base = GameSettings +var = economy.sharing_fee[0] +type = SLE_UINT +from = 200 +def = 100 +min = 0 +max = 1000000 +interval = 10 +str = STR_CONFIG_SETTING_SHARING_FEE_RAIL + +[SDT_VAR] +base = GameSettings +var = economy.sharing_fee[1] +type = SLE_UINT +from = 200 +def = 100 +min = 0 +max = 1000000 +interval = 10 +str = STR_CONFIG_SETTING_SHARING_FEE_ROAD + +[SDT_VAR] +base = GameSettings +var = economy.sharing_fee[2] +type = SLE_UINT +from = 200 +def = 100 +min = 0 +max = 1000000 +interval = 10 +str = STR_CONFIG_SETTING_SHARING_FEE_ROAD + +[SDT_VAR] +base = GameSettings +var = economy.sharing_fee[3] +type = SLE_UINT +from = 200 +def = 100 +min = 0 +max = 1000000 +interval = 10 +str = STR_CONFIG_SETTING_SHARING_FEE_AIR + +[SDT_BOOL] +base = GameSettings +var = economy.sharing_payment_in_debt +from = 200 +def = false +str = STR_CONFIG_SETTING_SHARING_PAYMENT_IN_DEBT + ; previously ai-new setting. [SDT_NULL] length = 1 diff --git a/src/train.h b/src/train.h index 80e8b34944..8df1424d34 100644 --- a/src/train.h +++ b/src/train.h @@ -48,6 +48,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); /** Variables that are cached to improve performance and such */ diff --git a/src/train_cmd.cpp b/src/train_cmd.cpp index 2de9dd5e87..9b1b07f974 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" @@ -1932,7 +1933,7 @@ CommandCost CmdForceTrainProceed(TileIndex tile, DoCommandFlag flags, uint32 p1, Train *t = Train::GetIfValid(p1); if (t == NULL) return CMD_ERROR; - CommandCost ret = CheckOwnership(t->owner); + CommandCost ret = CheckVehicleControlAllowed(t); if (ret.Failed()) return ret; @@ -2802,7 +2803,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))); } @@ -3963,6 +3964,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; @@ -3994,3 +3998,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 4851a4529e..5ac9b20cd9 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" @@ -1998,6 +1999,9 @@ void Vehicle::HandleLoading(bool mode) case OT_LOADING: { uint wait_time = max(this->current_order.wait_time - this->lateness_counter, 0); + /* 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 8899b5f32e..97ae1121a8 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" @@ -429,7 +430,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); @@ -505,7 +506,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); @@ -670,7 +671,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 81f85f534d..c1f6eb2bc3 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 "zoom_func.h" @@ -2363,6 +2364,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); @@ -2371,8 +2373,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();