# 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 }