infrastructure.cpp

Go to the documentation of this file.
00001 /* $Id$ */
00002 
00003 /*
00004  * This file is part of OpenTTD.
00005  * 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.
00006  * 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.
00007  * 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 <http://www.gnu.org/licenses/>.
00008  */
00009 
00012 #include "stdafx.h"
00013 #include "infrastructure_func.h"
00014 #include "train.h"
00015 #include "aircraft.h"
00016 #include "vehicle_func.h"
00017 #include "station_base.h"
00018 #include "depot_base.h"
00019 #include "pbs.h"
00020 #include "signal_func.h"
00021 #include "window_func.h"
00022 #include "gui.h"
00023 #include "pathfinder/yapf/yapf_cache.h"
00024 #include "company_base.h"
00025 
00026 #include "table/strings.h"
00027 
00034 static void PaySharingFee(Vehicle *v, Owner infra_owner, Money cost)
00035 {
00036   Company *c = Company::Get(v->owner);
00037   if (!_settings_game.economy.sharing_payment_in_debt) {
00038     /* Do not allow fee payment to drop (money - loan) below 0. */
00039     cost = min(cost, (c->money - c->current_loan) << 8);
00040     if (cost <= 0) return;
00041   }
00042   v->profit_this_year -= cost;
00043   SubtractMoneyFromCompanyFract(v->owner, CommandCost(EXPENSES_SHARING_COST, cost));
00044   SubtractMoneyFromCompanyFract(infra_owner, CommandCost(EXPENSES_SHARING_INC, -cost));
00045 }
00046 
00052 void PayStationSharingFee(Vehicle *v, const Station *st)
00053 {
00054   if (v->owner == st->owner || st->owner == OWNER_NONE || v->type == VEH_TRAIN) return;
00055   Money cost = _settings_game.economy.sharing_fee[v->type];
00056   PaySharingFee(v, st->owner, (cost << 8) / DAY_TICKS);
00057 }
00058 
00063 void PayDailyTrackSharingFee(Train *v)
00064 {
00065   Owner owner = GetTileOwner(v->tile);
00066   if (owner == v->owner) return;
00067   Money cost = _settings_game.economy.sharing_fee[VEH_TRAIN] << 8;
00068   /* Cost is calculated per 1000 tonnes */
00069   cost = cost * v->gcache.cached_weight / 1000;
00070   /* Only pay the required fraction */
00071   cost = cost * v->running_ticks / DAY_TICKS;
00072   if (cost != 0) PaySharingFee(v, owner, cost);
00073 }
00074 
00081 static bool VehiclePositionIsAllowed(const Vehicle *v, Owner owner = INVALID_OWNER)
00082 {
00083   switch (v->type) {
00084     case VEH_TRAIN:
00085       for (const Vehicle *u = v; u != NULL; u = u->Next()) {
00086         if (!IsInfraTileUsageAllowed(VEH_TRAIN, v->owner, u->tile) || GetTileOwner(u->tile) == owner) return false;
00087       }
00088       return true;
00089     case VEH_ROAD:
00090       for (const Vehicle *u = v; u != NULL; u = u->Next()) {
00091         if (IsRoadDepotTile(u->tile) || IsStandardRoadStopTile(u->tile)) {
00092           if (!IsInfraTileUsageAllowed(VEH_ROAD, v->owner, u->tile) || GetTileOwner(u->tile) == owner) return false;
00093         }
00094       }
00095       return true;
00096     case VEH_SHIP:
00097       if (IsShipDepotTile(v->tile) && v->IsStoppedInDepot()) {
00098         if (!IsInfraTileUsageAllowed(VEH_SHIP, v->owner, v->tile) || GetTileOwner(v->tile) == owner) return false;
00099       }
00100       return true;
00101     case VEH_AIRCRAFT: {
00102       const Aircraft *a = Aircraft::From(v);
00103       if (a->state != FLYING && Station::IsValidID(a->targetairport)) {
00104         Owner station_owner = Station::Get(a->targetairport)->owner;
00105         if (!IsInfraUsageAllowed(VEH_AIRCRAFT, a->owner, station_owner) || station_owner == owner) return false;
00106       }
00107       return true;
00108     }
00109     default: return true;
00110   }
00111 }
00112 
00121 static bool OrderDestinationIsAllowed(const Order *order, const Vehicle *v, Owner owner = INVALID_OWNER)
00122 {
00123   Owner dest_owner;
00124   switch (order->GetType()) {
00125     case OT_GOTO_STATION:
00126     case OT_GOTO_WAYPOINT: dest_owner = BaseStation::Get(order->GetDestination())->owner; break;
00127     case OT_GOTO_DEPOT:    dest_owner = (v->type == VEH_AIRCRAFT) ? Station::Get(order->GetDestination())->owner : GetTileOwner(Depot::Get(order->GetDestination())->xy); break;
00128     case OT_LOADING:       dest_owner = Station::Get(v->last_station_visited)->owner; break;
00129     default: return true;
00130   }
00131   return dest_owner != owner && IsInfraUsageAllowed(v->type, v->owner, dest_owner);
00132 }
00133 
00139 static void RemoveAndSellVehicle(Vehicle *v, bool give_money)
00140 {
00141   assert(v->Previous() == NULL);
00142 
00143   if (give_money) {
00144     /* compute total value and give that to the owner */
00145     Money value = 0;
00146     for (Vehicle *u = v->First(); u != NULL; u = u->Next()) {
00147       value += v->value;
00148     }
00149     CompanyID old = _current_company;
00150     _current_company = v->owner;
00151     SubtractMoneyFromCompany(CommandCost(EXPENSES_NEW_VEHICLES, -value));
00152     _current_company = old;
00153   }
00154 
00155   /* take special measures for trains, but not when sharing is disabled or when the train is a free wagon chain */
00156   if (_settings_game.economy.infrastructure_sharing[VEH_TRAIN] && v->type == VEH_TRAIN && Train::From(v)->IsFrontEngine()) {
00157     DeleteVisibleTrain(Train::From(v));
00158   } else {
00159     delete v;
00160   }
00161 }
00162 
00166 static void FixAllReservations()
00167 {
00168   /* if this function is called, we can safely assume that sharing of rails is being switched off */
00169   assert(!_settings_game.economy.infrastructure_sharing[VEH_TRAIN]);
00170   Train *v;
00171   FOR_ALL_TRAINS(v) {
00172     if (!v->IsPrimaryVehicle() || (v->vehstatus & VS_CRASHED) != 0) continue;
00173     /* It might happen that the train reserved additional tracks,
00174      * but FollowTrainReservation can't detect those because they are no longer reachable.
00175      * detect this by first finding the end of the reservation,
00176      * then switch sharing on and try again. If these two ends differ,
00177      * unreserve the path, switch sharing off and try to reserve a new path */
00178     PBSTileInfo end_tile_info = FollowTrainReservation(v);
00179 
00180     /* first do a quick test to determine whether the next tile has any reservation at all */
00181     TileIndex next_tile = end_tile_info.tile + TileOffsByDiagDir(TrackdirToExitdir(end_tile_info.trackdir));
00182     /* If the next tile doesn't have a reservation at all, the reservation surely ends here. Thus all is well */
00183     if (GetReservedTrackbits(next_tile) == TRACK_BIT_NONE) continue;
00184 
00185     /* change sharing setting temporarily */
00186     _settings_game.economy.infrastructure_sharing[VEH_TRAIN] = true;
00187     PBSTileInfo end_tile_info2 = FollowTrainReservation(v);
00188     /* if these two reservation ends differ, unreserve the path and try to reserve a new path */
00189     if (end_tile_info.tile != end_tile_info2.tile || end_tile_info.trackdir != end_tile_info2.trackdir) {
00190       FreeTrainTrackReservation(v);
00191       _settings_game.economy.infrastructure_sharing[VEH_TRAIN] = false;
00192       TryPathReserve(v, true);
00193     } else {
00194       _settings_game.economy.infrastructure_sharing[VEH_TRAIN] = false;
00195     }
00196   }
00197 }
00198 
00206 bool CheckSharingChangePossible(VehicleType type)
00207 {
00208   if (type != VEH_AIRCRAFT) YapfNotifyTrackLayoutChange(INVALID_TILE, INVALID_TRACK);
00209   /* Only do something when sharing is being disabled */
00210   if (_settings_game.economy.infrastructure_sharing[type]) return true;
00211 
00212   StringID error_message = STR_NULL;
00213   Vehicle *v;
00214   FOR_ALL_VEHICLES(v) {
00215     if (type != v->type) continue;
00216     if (v->Previous() != NULL) continue;
00217 
00218     /* Check vehicle positiion */
00219     if (!VehiclePositionIsAllowed(v)) {
00220       error_message = STR_CONFIG_SETTING_SHARING_USED_BY_VEHICLES;
00221       /* Break immediately, this error message takes precedence over the others. */
00222       break;
00223     }
00224 
00225     /* Check current order */
00226     if (!OrderDestinationIsAllowed(&v->current_order, v)) {
00227       error_message = STR_CONFIG_SETTING_SHARING_ORDERS_TO_OTHERS;
00228     }
00229 
00230     /* Check order list */
00231     if (v->FirstShared() != v) continue;
00232     Order *o;
00233     FOR_VEHICLE_ORDERS(v, o) {
00234       if (!OrderDestinationIsAllowed(o, v)) {
00235         error_message = STR_CONFIG_SETTING_SHARING_ORDERS_TO_OTHERS;
00236       }
00237     }
00238   }
00239 
00240   if (error_message != STR_NULL) {
00241     ShowErrorMessage(error_message, INVALID_STRING_ID, WL_ERROR);
00242     return false;
00243   }
00244 
00245   if (type == VEH_TRAIN) FixAllReservations();
00246 
00247   return true;
00248 }
00249 
00256 void HandleSharingCompanyDeletion(Owner owner)
00257 {
00258   YapfNotifyTrackLayoutChange(INVALID_TILE, INVALID_TRACK);
00259 
00260   Vehicle *v;
00261   FOR_ALL_VEHICLES(v) {
00262     if (!IsCompanyBuildableVehicleType(v) || v->Previous() != NULL) continue;
00263     /* vehicle position */
00264     if (v->owner == owner || !VehiclePositionIsAllowed(v, owner)) {
00265       RemoveAndSellVehicle(v, v->owner != owner);
00266       continue;
00267     }
00268     /* current order */
00269     if (!OrderDestinationIsAllowed(&v->current_order, v, owner)) {
00270       if (v->current_order.IsType(OT_LOADING)) {
00271         v->LeaveStation();
00272       } else {
00273         v->current_order.MakeDummy();
00274       }
00275       SetWindowDirty(WC_VEHICLE_VIEW, v->index);
00276     }
00277 
00278     /* order list */
00279     if (v->FirstShared() != v) continue;
00280 
00281     Order *o;
00282     int id = -1;
00283     FOR_VEHICLE_ORDERS(v, o) {
00284       id++;
00285       if (OrderDestinationIsAllowed(o, v, owner)) continue;
00286 
00287       o->MakeDummy();
00288       for (const Vehicle *w = v; w != NULL; w = w->NextShared()) {
00289         /* In GUI, simulate by removing the order and adding it back */
00290         InvalidateVehicleOrder(w, id | (INVALID_VEH_ORDER_ID << 8));
00291         InvalidateVehicleOrder(w, (id << 8) | INVALID_VEH_ORDER_ID);
00292       }
00293     }
00294   }
00295 }
00296 
00302 void UpdateAllBlockSignals(Owner owner)
00303 {
00304   Owner last_owner = INVALID_OWNER;
00305   TileIndex tile = 0;
00306   do {
00307     if (IsTileType(tile, MP_RAILWAY) && HasSignals(tile)) {
00308       Owner track_owner = GetTileOwner(tile);
00309       if (owner != INVALID_OWNER && track_owner != owner) continue;
00310 
00311       if (!IsOneSignalBlock(track_owner, last_owner)) {
00312         /* Cannot update signals of two different companies in one run,
00313          * if these signal blocks are not joined */
00314         UpdateSignalsInBuffer();
00315         last_owner = track_owner;
00316       }
00317       TrackBits bits = GetTrackBits(tile);
00318       do {
00319         Track track = RemoveFirstTrack(&bits);
00320         if (HasSignalOnTrack(tile, track)) {
00321           AddTrackToSignalBuffer(tile, track, track_owner);
00322         }
00323       } while (bits != TRACK_BIT_NONE);
00324     } else if (IsLevelCrossingTile(tile) && (owner == INVALID_OWNER || GetTileOwner(tile) == owner)) {
00325       UpdateLevelCrossing(tile);
00326     }
00327   } while (++tile != MapSize());
00328 
00329   UpdateSignalsInBuffer();
00330 }