# 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
    00025 #include "table/strings.h"
    00026
    00033 static void PaySharingFee(Vehicle *v, Owner infra_owner, Money cost)
    00034 {
    00035   Company *c = Company::Get(v->owner);
    00036   if (!_settings_game.sharing.payment_in_debt) {
    00037     /* Do not allow fee payment to drop (money - loan) below 0. */
    00038     cost = min(cost, (c->money - c->current_loan) << 8);
    00039     if (cost <= 0) return;
    00040   }
    00041   v->profit_this_year -= cost;
    00042   SubtractMoneyFromCompanyFract(v->owner, CommandCost(v->GetExpenseType(false), cost));
    00043   SubtractMoneyFromCompanyFract(infra_owner, CommandCost(v->GetExpenseType(true), -cost));
    00044 }
    00045
    00051 void PayStationSharingFee(Vehicle *v, const Station *st)
    00052 {
    00053   if (v->owner == st->owner || st->owner == OWNER_NONE || v->type == VEH_TRAIN) return;
    00054   Money cost;
    00055   switch (v->type) {
    00056     case VEH_ROAD:     cost = _settings_game.sharing.fee_road;  break;
    00057     case VEH_SHIP:     cost = _settings_game.sharing.fee_water; break;
    00058     case VEH_AIRCRAFT: cost = _settings_game.sharing.fee_air;   break;
    00059     default: NOT_REACHED();
    00060   }
    00061   PaySharingFee(v, st->owner, (cost << 8) / DAY_TICKS);
    00062 }
    00063
    00068 void PayDailyTrackSharingFee(Train *v)
    00069 {
    00070   Owner owner = GetTileOwner(v->tile);
    00071   if (owner == v->owner) return;
    00072   Money cost = _settings_game.sharing.fee_rail << 8;
    00073   /* Cost is calculated per 1000 tonnes */
    00074   cost = cost * v->tcache.cached_weight / 1000;
    00075   /* Only pay the required fraction */
    00076   cost = cost * v->running_ticks / DAY_TICKS;
    00077   if (cost != 0) PaySharingFee(v, owner, cost);
    00078 }
    00079
    00086 static bool VehiclePositionIsAllowed(const Vehicle *v, Owner owner = INVALID_OWNER)
    00087 {
    00088   switch (v->type) {
    00089     case VEH_TRAIN:
    00090       for (const Vehicle *u = v; u != NULL; u = u->Next()) {
    00091         if (!IsInfraUsageAllowed(GetTileOwner(u->tile), v->owner, VEH_TRAIN) || GetTileOwner(u->tile) == owner) return false;
    00092       }
    00093       return true;
    00094     case VEH_ROAD:
    00095       for (const Vehicle *u = v; u != NULL; u = u->Next()) {
    00096         if (IsRoadDepotTile(u->tile) || IsStandardRoadStopTile(u->tile)) {
    00097           if (!IsInfraUsageAllowed(GetTileOwner(u->tile), v->owner, VEH_ROAD) || GetTileOwner(u->tile) == owner) return false;
    00098         }
    00099       }
    00100       return true;
    00101     case VEH_SHIP:
    00102       if (IsShipDepotTile(v->tile) && v->IsStoppedInDepot()) {
    00103         return IsInfraUsageAllowed(GetTileOwner(v->tile), v->owner, VEH_SHIP) && GetTileOwner(v->tile) != owner;
    00104       }
    00105       return true;
    00106     case VEH_AIRCRAFT: {
    00107       const Aircraft *a = Aircraft::From(v);
    00108       if (a->state != FLYING && Station::IsValidID(a->targetairport)) {
    00109         Owner station_owner = Station::Get(a->targetairport)->owner;
    00110         return IsInfraUsageAllowed(station_owner, a->owner, VEH_AIRCRAFT) && station_owner != owner;
    00111       }
    00112       return true;
    00113     }
    00114     default: return true;
    00115   }
    00116 }
    00117
    00126 static bool OrderDestinationIsAllowed(const Order *order, const Vehicle *v, Owner owner = INVALID_OWNER)
    00127 {
    00128   Owner dest_owner;
    00129   switch (order->GetType()) {
    00130     case OT_GOTO_STATION:
    00131     case OT_GOTO_WAYPOINT: dest_owner = BaseStation::Get(order->GetDestination())->owner; break;
    00132     case OT_GOTO_DEPOT:    dest_owner = (v->type == VEH_AIRCRAFT) ? Station::Get(order->GetDestination())->owner : GetTileOwner(Depot::Get(order->GetDestination())->xy); break;
    00133     case OT_LOADING:       dest_owner = Station::Get(v->last_station_visited)->owner; break;
    00134     default: return true;
    00135   }
    00136   return dest_owner != owner && IsInfraUsageAllowed(dest_owner, v->owner, v->type);
    00137 }
    00138
    00144 static void RemoveAndSellVehicle(Vehicle *v, bool give_money)
    00145 {
    00146   assert(v->Previous() == NULL);
    00147
    00148   if (give_money) {
    00149     /* compute total value and give that to the owner */
    00150     Money value = 0;
    00151     for (Vehicle *u = v->First(); u != NULL; u = u->Next()) {
    00152       value += v->value;
    00153     }
    00154     CompanyID old = _current_company;
    00155     _current_company = v->owner;
    00156     SubtractMoneyFromCompany(CommandCost(EXPENSES_NEW_VEHICLES, -value));
    00157     _current_company = old;
    00158   }
    00159
    00160   /* take special measures for trains, but not when sharing is disabled or when the train is a free wagon chain */
    00161   if (_settings_game.sharing.sharing_rail && v->type == VEH_TRAIN && Train::From(v)->IsFrontEngine()) {
    00162     DeleteVisibleTrain(Train::From(v));
    00163   } else {
    00164     delete v;
    00165   }
    00166 }
    00167
    00171 static void FixAllReservations()
    00172 {
    00173   /* if this function is called, we can safely assume that sharing of rails is being switched off */
    00174   assert(!_settings_game.sharing.sharing_rail);
    00175   Train *v;
    00176   FOR_ALL_TRAINS(v) {
    00177     if (!v->IsPrimaryVehicle() || (v->vehstatus & VS_CRASHED) != 0) continue;
    00178     /* It might happen that the train reserved additional tracks,
    00179      * but FollowTrainReservation can't detect those because they are no longer reachable.
    00180      * detect this by first finding the end of the reservation,
    00181      * then switch sharing on and try again. If these two ends differ,
    00182      * unreserve the path, switch sharing off and try to reserve a new path */
    00183     PBSTileInfo end_tile_info = FollowTrainReservation(v);
    00184
    00185     /* first do a quick test to determine whether the next tile has any reservation at all */
    00186     TileIndex next_tile = end_tile_info.tile + TileOffsByDiagDir(TrackdirToExitdir(end_tile_info.trackdir));
    00187     /* If the next tile doesn't have a reservation at all, the reservation surely ends here. Thus all is well */
    00188     if (GetReservedTrackbits(next_tile) == TRACK_BIT_NONE) continue;
    00189
    00190     /* change sharing setting temporarily */
    00191     _settings_game.sharing.sharing_rail = true;
    00192     PBSTileInfo end_tile_info2 = FollowTrainReservation(v);
    00193     /* if these two reservation ends differ, unreserve the path and try to reserve a new path */
    00194     if (end_tile_info.tile != end_tile_info2.tile || end_tile_info.trackdir != end_tile_info2.trackdir) {
    00195       FreeTrainTrackReservation(v);
    00196       _settings_game.sharing.sharing_rail = false;
    00197       TryPathReserve(v, true);
    00198     } else {
    00199       _settings_game.sharing.sharing_rail = false;
    00200     }
    00201   }
    00202 }
    00203
    00211 bool CheckSharingChangePossible(VehicleType type)
    00212 {
    00213   if (type != VEH_AIRCRAFT) YapfNotifyTrackLayoutChange(INVALID_TILE, INVALID_TRACK);
    00214   /* Only do something when sharing is being disabled */
    00215   if (GetSharingSetting(type)) return true;
    00216
    00217   StringID error_message = STR_NULL;
    00218   Vehicle *v;
    00219   FOR_ALL_VEHICLES(v) {
    00220     if (type != v->type) continue;
    00221     if (v->Previous() != NULL) continue;
    00222
    00223     /* Check vehicle positiion */
    00224     if (!VehiclePositionIsAllowed(v)) {
    00225       error_message = STR_CONFIG_SETTING_SHARING_USED_BY_VEHICLES;
    00226       /* Break immediately, this error message takes precedence over the others. */
    00227       break;
    00228     }
    00229
    00230     /* Check current order */
    00231     if (!OrderDestinationIsAllowed(&v->current_order, v)) {
    00232       error_message = STR_CONFIG_SETTING_SHARING_ORDERS_TO_OTHERS;
    00233     }
    00234
    00235     /* Check order list */
    00236     if (v->FirstShared() != v) continue;
    00237     Order *o;
    00238     FOR_VEHICLE_ORDERS(v, o) {
    00239       if (!OrderDestinationIsAllowed(o, v)) {
    00240         error_message = STR_CONFIG_SETTING_SHARING_ORDERS_TO_OTHERS;
    00241       }
    00242     }
    00243   }
    00244
    00245   if (error_message != STR_NULL) {
    00246     ShowErrorMessage(error_message, INVALID_STRING_ID, 0, 0);
    00247     return false;
    00248   }
    00249
    00250   if (type == VEH_TRAIN) FixAllReservations();
    00251
    00252   return true;
    00253 }
    00254
    00261 void HandleSharingCompanyDeletion(Owner owner)
    00262 {
    00263   YapfNotifyTrackLayoutChange(INVALID_TILE, INVALID_TRACK);
    00264
    00265   Vehicle *v;
    00266   FOR_ALL_VEHICLES(v) {
    00267     if (!IsCompanyBuildableVehicleType(v) || v->Previous() != NULL) continue;
    00268     /* vehicle position */
    00269     if (v->owner == owner || !VehiclePositionIsAllowed(v, owner)) {
    00270       RemoveAndSellVehicle(v, v->owner != owner);
    00271       continue;
    00272     }
    00273     /* current order */
    00274     if (!OrderDestinationIsAllowed(&v->current_order, v, owner)) {
    00275       if (v->current_order.IsType(OT_LOADING)) {
    00276         v->LeaveStation();
    00277       } else {
    00278         v->current_order.MakeDummy();
    00279       }
    00280       SetWindowDirty(WC_VEHICLE_VIEW, v->index);
    00281     }
    00282
    00283     /* order list */
    00284     if (v->FirstShared() != v) continue;
    00285
    00286     Order *o;
    00287     int id = -1;
    00288     FOR_VEHICLE_ORDERS(v, o) {
    00289       id++;
    00290       if (OrderDestinationIsAllowed(o, v, owner)) continue;
    00291
    00292       o->MakeDummy();
    00293       for (const Vehicle *w = v; w != NULL; w = w->NextShared()) {
    00294         /* In GUI, simulate by removing the order and adding it back */
    00295         InvalidateVehicleOrder(w, id | (INVALID_VEH_ORDER_ID << 8));
    00296         InvalidateVehicleOrder(w, (id << 8) | INVALID_VEH_ORDER_ID);
    00297       }
    00298     }
    00299   }
    00300 }
    00301
    00307 void UpdateAllBlockSignals(Owner owner)
    00308 {
    00309   Owner last_owner = INVALID_OWNER;
    00310   TileIndex tile = 0;
    00311   do {
    00312     if (IsTileType(tile, MP_RAILWAY) && HasSignals(tile)) {
    00313       Owner track_owner = GetTileOwner(tile);
    00314       if (owner != INVALID_OWNER && track_owner != owner) continue;
    00315
    00316       if (!IsOneSignalBlock(track_owner, last_owner)) {
    00317         /* Cannot update signals of two different companies in one run,
    00318          * if these signal blocks are not joined */
    00319         UpdateSignalsInBuffer();
    00320         last_owner = track_owner;
    00321       }
    00322       TrackBits bits = GetTrackBits(tile);
    00323       do {
    00324         Track track = RemoveFirstTrack(&bits);
    00325         if (HasSignalOnTrack(tile, track)) {
    00326           AddTrackToSignalBuffer(tile, track, track_owner);
    00327         }
    00328       } while (bits != TRACK_BIT_NONE);
    00329     } else if (IsLevelCrossingTile(tile) && (owner == INVALID_OWNER || GetTileOwner(tile) == owner)) {
    00330       UpdateLevelCrossing(tile);
    00331     }
    00332   } while (++tile != MapSize());
    00333
    00334   UpdateSignalsInBuffer();
    00335 }