Index: src/settings.cpp
===================================================================
--- src/settings.cpp (revision 18190)
+++ src/settings.cpp (working copy)
@@ -66,6 +66,8 @@
#include "void_map.h"
#include "station_base.h"
+#include "infrastructure_func.h"
+#include "core/mem_func.hpp"
#include "table/strings.h"
#include "table/settings.h"
@@ -969,6 +971,93 @@
return true;
}
+/**
+ * Copy the global sharing settings to all companies.
+ * @param force_update_signals Force updating all block signals
+ */
+static void CopySharingSettingsToCompanies(bool force_update_signals)
+{
+ Company *c;
+ FOR_ALL_COMPANIES(c) {
+ c->sharing_future.CopyFromGameSettings();
+ c->sharing_delay = 0;
+ }
+ ProcessSharingChanges(force_update_signals);
+}
+
+/**
+ * Update the sharing settings of all companies if sharing settings are globally fixed
+ * @param p1 unused
+ * @return always true
+ */
+static bool UpdateAllSharingSettings(int32 p1)
+{
+ if (_settings_game.sharing.sharing_mode == SHARING_GLOBAL && _game_mode != GM_MENU) {
+ CopySharingSettingsToCompanies(false);
+ }
+ return true;
+}
+
+/**
+ * Update all sharing settings if sharing is globally enabled / disabled.
+ * @param p1 New value of the setting
+ * @return always true
+ */
+static bool UpdateSharingEnabled(int32 p1)
+{
+ if (_game_mode == GM_MENU) return true;
+
+ switch (p1) {
+ case SHARING_NONE: {
+ Company *c;
+ FOR_ALL_COMPANIES(c) {
+ /* Disable all sharing options */
+ VehicleType vt;
+ FOR_ALL_SHARING_VEHTYPES(vt) {
+ c->sharing_future.SetSharingAllowed(vt, false);
+ }
+ /* Allow only our own company, erase all others */
+ c->sharing_future.allowed_companies = 1 << c->index;
+ c->sharing_delay = 0;
+ }
+ /* Process all these changes */
+ ProcessSharingChanges(true);
+ break;
+ }
+ case SHARING_GLOBAL:
+ CopySharingSettingsToCompanies(true);
+ break;
+ case SHARING_INDIVIDUAL:
+ /* Update all block signals */
+ ProcessSharingChanges(true);
+ break;
+ }
+
+ SetWindowClassesDirty(WC_FINANCES);
+ SetWindowClassesDirty(WC_COMPANY);
+ if (p1 != SHARING_INDIVIDUAL) DeleteWindowByClass(WC_SHARING_OPTIONS);
+
+ return true;
+}
+
+/**
+ * Cap sharing_delay for all companies to the new delay setting
+ * @param p1 new delay setting
+ * @return always true
+ */
+static bool UpdateSharingDelay(int32 p1)
+{
+ if (_settings_game.sharing.sharing_mode != SHARING_INDIVIDUAL) return true;
+
+ Company *c;
+ FOR_ALL_COMPANIES(c) {
+ c->sharing_delay = min(c->sharing_delay, p1);
+ }
+ /* Changes have to be applied right now if delay is set to 0 */
+ if (p1 == 0) ProcessSharingChanges();
+ return true;
+}
+
#ifdef ENABLE_NETWORK
static bool UpdateClientName(int32 p1)
Index: src/lang/english.txt
===================================================================
--- src/lang/english.txt (revision 18190)
+++ src/lang/english.txt (working copy)
@@ -784,6 +784,12 @@
STR_NEWS_SHIP_IS_WAITING :{WHITE}{VEHICLE} is waiting in depot
STR_NEWS_AIRCRAFT_IS_WAITING :{WHITE}{VEHICLE} is waiting in the aircraft hangar
+STR_NEWS_SHARING_TITLE :{BLACK}{BIGFONT}Infrastructure sharing news!
+STR_NEWS_SHARING_CHANGE_ANNOUNCED :{BLACK}{BIGFONT}{RAW_STRING} announced to change its infrastructure sharing settings in {NUM} month{P "" s}!
+STR_NEWS_SHARING_CHANGE_HAPPENED :{BLACK}{BIGFONT}{RAW_STRING} has changed its infrastructure sharing settings!
+STR_NEWS_SHARING_SOLD_VEHICLES :{BLACK}{BIGFONT}Because of a change in infrastructure sharing settings, {COMMA} of your vehicles {P was were} sold!
+STR_NEWS_SHARING_UNABLE_TO_PAY :{BLACK}{BIGFONT}{RAW_STRING} is in debt and cannot pay sharing fees to you!
+
# Start of order review system.
# DON'T ADD OR REMOVE LINES HERE
STR_NEWS_VEHICLE_HAS_TOO_FEW_ORDERS :{WHITE}{VEHICLE} has too few orders in the schedule
@@ -1256,6 +1262,21 @@
STR_CONFIG_SETTING_CITY_SIZE_MULTIPLIER :{LTBLUE}Initial city size multiplier: {ORANGE}{STRING1}
STR_CONFIG_SETTING_MODIFIED_ROAD_REBUILD :{LTBLUE}Remove absurd road-elements during the road construction: {ORANGE}{STRING1}
+STR_CONFIG_SETTING_SHARING_ENABLE :{LTBLUE}Infrastructure sharing mode: {ORANGE}{STRING1}
+STR_CONFIG_SETTING_SHARING_ENABLE_OFF :Off
+STR_CONFIG_SETTING_SHARING_ENABLE_GLOBAL :On, global settings
+STR_CONFIG_SETTING_SHARING_ENABLE_INDIVIDUAL :On, individual settings
+STR_CONFIG_SETTING_SHARING_INDIVIDUAL_TIME :{LTBLUE}Delay for new individual settings to take effect: {ORANGE}{STRING1} months
+STR_CONFIG_SETTING_SHARING_PAYMENT_DEBT :{LTBLUE}Allow companies in debt to pay sharing fees: {ORANGE}{STRING1}
+STR_CONFIG_SETTING_SHARING_RAIL :{LTBLUE}Railways: {ORANGE}{STRING1}
+STR_CONFIG_SETTING_SHARING_ROAD :{LTBLUE}Road stops and depots: {ORANGE}{STRING1}
+STR_CONFIG_SETTING_SHARING_WATER :{LTBLUE}Docks and ship depots: {ORANGE}{STRING1}
+STR_CONFIG_SETTING_SHARING_AIR :{LTBLUE}Airports: {ORANGE}{STRING1}
+STR_CONFIG_SETTING_SHARING_RAIL_FEE :{LTBLUE}Monthly track toll: {ORANGE}{STRING1} per train tile
+STR_CONFIG_SETTING_SHARING_ROAD_FEE :{LTBLUE}Stopping fee for road vehicles: {ORANGE}{STRING1} per day
+STR_CONFIG_SETTING_SHARING_WATER_FEE :{LTBLUE}Docking fee for ship: {ORANGE}{STRING1} per day
+STR_CONFIG_SETTING_SHARING_AIR_FEE :{LTBLUE}Terminal fee for aircraft: {ORANGE}{STRING1} per day
+
STR_CONFIG_SETTING_GUI :{ORANGE}Interface
STR_CONFIG_SETTING_CONSTRUCTION :{ORANGE}Construction
STR_CONFIG_SETTING_VEHICLES :{ORANGE}Vehicles
@@ -1267,6 +1288,7 @@
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_AI_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
@@ -2502,6 +2524,8 @@
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 Costs
+STR_FINANCES_SECTION_INFRASTRUCTURE_INCOME :{GOLD}Infrastructure Income
STR_FINANCES_NEGATIVE_INCOME :{BLACK}-{CURRENCY}
STR_FINANCES_POSITIVE_INCOME :{BLACK}+{CURRENCY}
STR_FINANCES_TOTAL_CAPTION :{WHITE}Total:
@@ -2535,6 +2559,9 @@
STR_COMPANY_VIEW_RELOCATE_HQ :{BLACK}Relocate HQ
STR_COMPANY_VIEW_RELOCATE_COMPANY_HEADQUARTERS :{BLACK}Rebuild company headquarters elsewhere for 1% cost of company value
+STR_COMPANY_VIEW_SHARING_BUTTON :{BLACK}Sharing
+STR_COMPANY_VIEW_SHARING_TOOLTIP :{BLACK}Set the sharing options for your infrastructure
+
STR_COMPANY_VIEW_NEW_FACE_BUTTON :{BLACK}New Face
STR_COMPANY_VIEW_NEW_FACE_TOOLTIP :{BLACK}Select new face for manager
STR_COMPANY_VIEW_COLOUR_SCHEME_BUTTON :{BLACK}Colour Scheme
@@ -2554,6 +2581,33 @@
STR_BUY_COMPANY_MESSAGE :{WHITE}We are looking for a transport company to take-over our company.{}{}Do you want to purchase {COMPANY} for {CURRENCY}?
+#Infrastructure sharing window
+STR_SHARING_CAPTION :{WHITE}{COMPANY} - Infrastructure sharing
+STR_SHARING_PANEL_TOOLTIP :{BLACK}View and edit the infrastructure sharing settings for this company.
+STR_SHARING_OPTIONS :{BLACK}Options
+STR_SHARING_OPTIONS_TOOLTIP :{BLACK}View and edit the sharing options
+STR_SHARING_COMPANIES :{BLACK}Companies
+STR_SHARING_COMPANIES_TOOLTIP :{BLACK}View and edit which companies are allowed on infrastructure of this company
+
+STR_SHARING_CURRENT :{BLACK}Currently:
+STR_SHARING_FUTURE :{BLACK}In the future:
+STR_SHARING_SETTING_RAIL :{BLACK}Railways:
+STR_SHARING_SETTING_ROAD :{BLACK}Road stops and depots:
+STR_SHARING_SETTING_WATER :{BLACK}Docks and ship depots:
+STR_SHARING_SETTING_AIR :{BLACK}Airports:
+STR_SHARING_SETTING_MONTHLY_TOLL :{BLACK}Monthly toll per train tile:
+STR_SHARING_SETTING_FEE :{BLACK}Loading fee per day:
+STR_SHARING_SETTING_COMPANY :{BLACK}{COMPANY}:
+STR_SHARING_SETTING_NO_COMPANIES :{BLACK}No other companies
+
+STR_SHARING_VALUE_ALLOWED :{ORANGE}Allowed
+STR_SHARING_VALUE_NOT_ALLOWED :{ORANGE}Not allowed
+
+STR_SHARING_CHANGES_TAKE_EFFECT :{WHITE}Changes will take effect in:
+STR_SHARING_TAKE_EFFECT_MONTHS :{ORANGE}{NUM} month{P "" s}
+
+STR_SHARING_CHANGE_FEE_CAPTION :{WHITE}Change the sharing fee
+
# Industry directory
STR_INDUSTRY_DIRECTORY_CAPTION :{WHITE}Industries
STR_INDUSTRY_DIRECTORY_NONE :{ORANGE}- None -
@@ -3283,6 +3337,7 @@
STR_ERROR_CAN_T_BUY_25_SHARE_IN_THIS :{WHITE}Can't buy 25% share in this company...
STR_ERROR_CAN_T_SELL_25_SHARE_IN :{WHITE}Can't sell 25% share in this company...
STR_ERROR_PROTECTED :{WHITE}This company is not old enough to trade shares yet...
+STR_ERROR_CAN_T_CHANGE_SHARING_SETTING :{WHITE}Can't change the infrastructure sharing settings...
# Town related errors
STR_ERROR_CAN_T_GENERATE_TOWN :{WHITE}Can't build any towns
Index: src/settings_gui.cpp
===================================================================
--- src/settings_gui.cpp (revision 18190)
+++ src/settings_gui.cpp (working copy)
@@ -1342,8 +1342,25 @@
/** Computer players sub-page */
static SettingsPage _settings_ai_npc_page = {_settings_ai_npc, lengthof(_settings_ai_npc)};
+static SettingEntry _settings_ai_sharing[] = {
+ SettingEntry("sharing.sharing_mode"),
+ SettingEntry("sharing.new_individual_delay"),
+ SettingEntry("sharing.payment_in_debt"),
+ SettingEntry("sharing.rail_sharing"),
+ SettingEntry("sharing.road_sharing"),
+ SettingEntry("sharing.water_sharing"),
+ SettingEntry("sharing.air_sharing"),
+ SettingEntry("sharing.rail_fee"),
+ SettingEntry("sharing.road_fee"),
+ SettingEntry("sharing.water_fee"),
+ SettingEntry("sharing.air_fee"),
+};
+/** Infrastructure sharing sub-page */
+static SettingsPage _settings_ai_sharing_page = {_settings_ai_sharing, lengthof(_settings_ai_sharing)};
+
static SettingEntry _settings_ai[] = {
SettingEntry(&_settings_ai_npc_page, STR_CONFIG_SETTING_AI_NPC),
+ SettingEntry(&_settings_ai_sharing_page, STR_CONFIG_SETTING_AI_SHARING),
SettingEntry("economy.give_money"),
SettingEntry("economy.allow_shares"),
};
Index: src/npf.cpp
===================================================================
--- src/npf.cpp (revision 18190)
+++ src/npf.cpp (working copy)
@@ -21,6 +21,7 @@
#include "pbs.h"
#include "pathfind.h"
#include "train.h"
+#include "infrastructure_func.h"
static AyStar _npf_aystar;
@@ -553,25 +554,30 @@
*/
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 IsInfraUsageAllowed(tile, owner, VEH_TRAIN);
- switch (GetTileType(tile)) {
case MP_ROAD:
+ /* road depot */
+ if (IsRoadDepot(tile)) return IsInfraUsageAllowed(tile, owner, VEH_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 IsInfraUsageAllowed(tile, owner, VEH_TRAIN); // Railway needs owner check, while the street is public
}
break;
+ case MP_STATION:
+ /* rail station */
+ if (HasStationTileRail(tile)) return IsInfraUsageAllowed(tile, owner, VEH_TRAIN);
+ /* standard road stop */
+ if (IsStandardRoadStopTile(tile)) return IsInfraUsageAllowed(tile, owner, VEH_ROAD);
+ break;
+
case MP_TUNNELBRIDGE:
if (GetTunnelBridgeTransportType(tile) == TRANSPORT_RAIL) {
- return IsTileOwner(tile, owner);
+ return IsInfraUsageAllowed(tile, owner, VEH_TRAIN);
}
break;
@@ -982,7 +988,7 @@
FOR_ALL_DEPOTS(depot) {
/* Check if this is really a valid depot, it is of the needed type and
* owner */
- if (IsDepotTypeTile(depot->xy, type) && IsTileOwner(depot->xy, owner))
+ if (IsDepotTypeTile(depot->xy, type) && IsInfraUsageAllowed(depot->xy, owner, (VehicleType)type))
/* If so, let's add it to the queue, sorted by distance */
depots.push(&depots, depot, DistanceManhattan(tile, depot->xy));
}
Index: src/news_func.h
===================================================================
--- src/news_func.h (revision 18190)
+++ src/news_func.h (working copy)
@@ -16,6 +16,7 @@
#include "vehicle_type.h"
#include "station_type.h"
#include "industry_type.h"
+#include "company_type.h"
void AddNewsItem(StringID string, NewsSubtype subtype, NewsReferenceType reftype1 = NR_NONE, uint32 ref1 = UINT32_MAX, NewsReferenceType reftype2 = NR_NONE, uint32 ref2 = UINT32_MAX, void *free_data = NULL);
Index: src/table/settings.h
===================================================================
--- src/table/settings.h (revision 18190)
+++ src/table/settings.h (working copy)
@@ -32,6 +32,9 @@
static bool CheckFreeformEdges(int32 p1);
static bool ChangeDynamicEngines(int32 p1);
static bool StationCatchmentChanged(int32 p1);
+static bool UpdateAllSharingSettings(int32 p1);
+static bool UpdateSharingEnabled(int32 p1);
+static bool UpdateSharingDelay(int32 p1);
#ifdef ENABLE_NETWORK
static bool UpdateClientName(int32 p1);
@@ -502,6 +505,18 @@
SDT_CONDVAR(GameSettings, pf.yapf.road_crossing_penalty, SLE_UINT, 33, SL_MAX_VERSION, 0, 0, 3 * YAPF_TILE_LENGTH, 0, 1000000, 0, STR_NULL, NULL),
SDT_CONDVAR(GameSettings, pf.yapf.road_stop_penalty, SLE_UINT, 47, SL_MAX_VERSION, 0, 0, 8 * YAPF_TILE_LENGTH, 0, 1000000, 0, STR_NULL, NULL),
+ SDT_CONDVAR(GameSettings, sharing.sharing_mode, SLE_UINT8,SL_IS, SL_MAX_VERSION, 0,MS, SHARING_NONE, SHARING_NONE, SHARING_INDIVIDUAL, 0, STR_CONFIG_SETTING_SHARING_ENABLE,UpdateSharingEnabled),
+ SDT_CONDVAR(GameSettings, sharing.new_individual_delay, SLE_UINT8,SL_IS, SL_MAX_VERSION, 0, 0, 12, 0, 120, 1, STR_CONFIG_SETTING_SHARING_INDIVIDUAL_TIME,UpdateSharingDelay),
+ SDT_CONDBOOL(GameSettings, sharing.payment_in_debt, SL_IS, SL_MAX_VERSION, 0, 0, false, STR_CONFIG_SETTING_SHARING_PAYMENT_DEBT, NULL),
+ SDT_CONDBOOL(GameSettings, sharing.rail_sharing, SL_IS, SL_MAX_VERSION, 0, 0, false, STR_CONFIG_SETTING_SHARING_RAIL, UpdateAllSharingSettings),
+ SDT_CONDBOOL(GameSettings, sharing.road_sharing, SL_IS, SL_MAX_VERSION, 0, 0, false, STR_CONFIG_SETTING_SHARING_ROAD, UpdateAllSharingSettings),
+ SDT_CONDBOOL(GameSettings, sharing.water_sharing, SL_IS, SL_MAX_VERSION, 0, 0, false, STR_CONFIG_SETTING_SHARING_WATER, UpdateAllSharingSettings),
+ SDT_CONDBOOL(GameSettings, sharing.air_sharing, SL_IS, SL_MAX_VERSION, 0, 0, false, STR_CONFIG_SETTING_SHARING_AIR, UpdateAllSharingSettings),
+ SDT_CONDVAR(GameSettings, sharing.rail_fee, SLE_UINT16,SL_IS, SL_MAX_VERSION, 0,CR, 100, 0, MAX_SHARING_FEE, 10, STR_CONFIG_SETTING_SHARING_RAIL_FEE, UpdateAllSharingSettings),
+ SDT_CONDVAR(GameSettings, sharing.road_fee, SLE_UINT16,SL_IS, SL_MAX_VERSION, 0,CR, 100, 0, MAX_SHARING_FEE, 10, STR_CONFIG_SETTING_SHARING_ROAD_FEE, UpdateAllSharingSettings),
+ SDT_CONDVAR(GameSettings, sharing.water_fee, SLE_UINT16,SL_IS, SL_MAX_VERSION, 0,CR, 100, 0, MAX_SHARING_FEE, 10, STR_CONFIG_SETTING_SHARING_WATER_FEE, UpdateAllSharingSettings),
+ SDT_CONDVAR(GameSettings, sharing.air_fee, SLE_UINT16,SL_IS, SL_MAX_VERSION, 0,CR, 100, 0, MAX_SHARING_FEE, 10, STR_CONFIG_SETTING_SHARING_AIR_FEE, UpdateAllSharingSettings),
+
SDT_CONDVAR(GameSettings, game_creation.land_generator, SLE_UINT8, 30, SL_MAX_VERSION, 0,MS, 1, 0, 1, 0, STR_CONFIG_SETTING_LAND_GENERATOR, NULL),
SDT_CONDVAR(GameSettings, game_creation.oil_refinery_limit, SLE_UINT8, 30, SL_MAX_VERSION, 0, 0, 32, 12, 48, 0, STR_CONFIG_SETTING_OIL_REF_EDGE_DISTANCE, NULL),
SDT_CONDVAR(GameSettings, game_creation.tgen_smoothness, SLE_UINT8, 30, SL_MAX_VERSION, 0,MS, 1, 0, 3, 0, STR_CONFIG_SETTING_ROUGHNESS_OF_TERRAIN, NULL),
Index: src/news_type.h
===================================================================
--- src/news_type.h (revision 18190)
+++ src/news_type.h (working copy)
@@ -50,6 +50,7 @@
NS_COMPANY_MERGER, ///< NT_COMPANY_INFO (merger)
NS_COMPANY_BANKRUPT, ///< NT_COMPANY_INFO (bankrupt)
NS_COMPANY_NEW, ///< NT_COMPANY_INFO (new company)
+ NS_COMPANY_SHARING, ///< NT_COMPANY_INFO (change of sharing settings)
NS_INDUSTRY_OPEN, ///< NT_INDUSTRY_OPEN
NS_INDUSTRY_CLOSE, ///< NT_INDUSTRY_CLOSE
NS_ECONOMY, ///< NT_ECONOMY
Index: src/ship_cmd.cpp
===================================================================
--- src/ship_cmd.cpp (revision 18190)
+++ src/ship_cmd.cpp (working copy)
@@ -35,6 +35,7 @@
#include "ai/ai.hpp"
#include "pathfind.h"
#include "landscape_type.h"
+#include "infrastructure_func.h"
#include "table/strings.h"
#include "table/sprites.h"
@@ -122,7 +123,7 @@
FOR_ALL_DEPOTS(depot) {
TileIndex tile = depot->xy;
- if (IsShipDepotTile(tile) && IsTileOwner(tile, v->owner)) {
+ if (IsShipDepotTile(tile) && IsInfraUsageAllowed(tile, v)) {
uint dist = DistanceManhattan(tile, v->tile);
if (dist < best_dist) {
best_dist = dist;
@@ -758,7 +759,7 @@
/* The ai_new queries the vehicle cost before building the route,
* so we must check against cheaters no sooner than now. --pasky */
if (!IsShipDepotTile(tile)) return CMD_ERROR;
- if (!IsTileOwner(tile, _current_company)) return CMD_ERROR;
+ if (!IsInfraUsageAllowed(tile, _current_company, VEH_SHIP)) return CMD_ERROR;
unit_num = (flags & DC_AUTOREPLACE) ? 0 : GetFreeUnitNumber(VEH_SHIP);
Index: src/order_cmd.cpp
===================================================================
--- src/order_cmd.cpp (revision 18190)
+++ src/order_cmd.cpp (working copy)
@@ -28,6 +28,7 @@
#include "roadveh.h"
#include "station_base.h"
#include "waypoint_base.h"
+#include "infrastructure_func.h"
#include "table/strings.h"
@@ -465,7 +466,7 @@
const Station *st = Station::GetIfValid(new_order.GetDestination());
if (st == NULL) return CMD_ERROR;
- if (st->owner != OWNER_NONE && !CheckOwnership(st->owner)) return CMD_ERROR;
+ if (!IsInfraUsageAllowed(st->owner, v)) return CMD_ERROR;
if (!CanVehicleUseStation(v, st)) return_cmd_error(STR_ERROR_CAN_T_ADD_ORDER);
for (Vehicle *u = v->FirstShared(); u != NULL; u = u->NextShared()) {
@@ -509,7 +510,7 @@
if (v->type == VEH_AIRCRAFT) {
const Station *st = Station::GetIfValid(new_order.GetDestination());
- if (st == NULL || !CheckOwnership(st->owner) ||
+ if (st == NULL || !IsInfraUsageAllowed(st->owner, v) ||
!CanVehicleUseStation(v, st) ||
st->Airport()->nof_depots == 0) {
return CMD_ERROR;
@@ -517,7 +518,7 @@
} else {
const Depot *dp = Depot::GetIfValid(new_order.GetDestination());
- if (dp == NULL || !CheckOwnership(GetTileOwner(dp->xy))) return CMD_ERROR;
+ if (dp == NULL || !IsInfraUsageAllowed(dp->xy, v)) return CMD_ERROR;
switch (v->type) {
case VEH_TRAIN:
@@ -549,19 +550,9 @@
case OT_GOTO_WAYPOINT: {
const Waypoint *wp = Waypoint::GetIfValid(new_order.GetDestination());
if (wp == NULL) return CMD_ERROR;
+ if (v->type != VEH_TRAIN && v->type != VEH_SHIP) return CMD_ERROR;
+ if (!IsInfraUsageAllowed(wp->owner, v)) return CMD_ERROR;
- switch (v->type) {
- default: return CMD_ERROR;
-
- case VEH_TRAIN:
- if (!CheckOwnership(wp->owner)) return CMD_ERROR;
- break;
-
- case VEH_SHIP:
- if (!CheckOwnership(wp->owner) && wp->owner != OWNER_NONE) return CMD_ERROR;
- break;
- }
-
/* Order flags can be any of the following for waypoints:
* [non-stop]
* non-stop orders (if any) are only valid for trains */
@@ -1839,6 +1830,7 @@
bool is_dest_station = this->IsType(OT_GOTO_STATION) && this->dest == station;
return
(!this->IsType(OT_GOTO_DEPOT) || (this->GetDepotOrderType() & ODTFB_PART_OF_ORDERS) != 0) &&
+ IsInfraUsageAllowed(Station::Get(station)->owner, v) && // Only stop when allowed to
v->last_station_visited != station && // Do stop only when we've not just been there
/* Finally do stop when there is no non-stop flag set for this type of station. */
!(this->GetNonStopType() & (is_dest_station ? ONSF_NO_STOP_AT_DESTINATION_STATION : ONSF_NO_STOP_AT_INTERMEDIATE_STATIONS));
Index: src/window_type.h
===================================================================
--- src/window_type.h (revision 18190)
+++ src/window_type.h (working copy)
@@ -106,6 +106,7 @@
WC_AI_DEBUG,
WC_AI_LIST,
WC_AI_SETTINGS,
+ WC_SHARING_OPTIONS,
WC_INVALID = 0xFFFF
};
Index: src/company_cmd.cpp
===================================================================
--- src/company_cmd.cpp (revision 18190)
+++ src/company_cmd.cpp (working copy)
@@ -77,6 +77,7 @@
InvalidateWindowData(WC_GRAPH_LEGEND, 0, (int)index);
InvalidateWindowData(WC_PERFORMANCE_DETAIL, 0, (int)index);
InvalidateWindowData(WC_COMPANY_LEAGUE, 0, 0);
+ InvalidateWindowClassesData(WC_SHARING_OPTIONS);
}
/**
@@ -180,14 +181,16 @@
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_SHARED_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_SHARED_RUN, cost.GetExpensesType())) {
c->cur_economy.expenses -= cost.GetCost();
}
@@ -465,11 +468,28 @@
SetDefaultCompanySettings(c->index);
+ /* sharing settings */
+ if (_settings_game.sharing.sharing_mode == SHARING_GLOBAL) {
+ c->sharing_current.CopyFromGameSettings();
+ c->sharing_future.Copy(&c->sharing_current);
+ /* enable this company in all companies */
+ Company *d;
+ FOR_ALL_COMPANIES(d) {
+ d->sharing_current.SetCompanyAllowed(c->index, true);
+ d->sharing_future.SetCompanyAllowed(c->index, true);
+ }
+ } else {
+ /* only allow own company */
+ c->sharing_current.SetCompanyAllowed(c->index, true);
+ c->sharing_future.SetCompanyAllowed(c->index, true);
+ }
+
GeneratePresidentName(c);
SetWindowDirty(WC_GRAPH_LEGEND, 0);
SetWindowDirty(WC_TOOLBAR_MENU, 0);
SetWindowDirty(WC_CLIENT_LIST, 0);
+ InvalidateWindowClassesData(WC_SHARING_OPTIONS);
if (is_ai && (!_networking || _network_server)) AI::StartNew(c->index);
Index: src/vehicle_cmd.cpp
===================================================================
--- src/vehicle_cmd.cpp (revision 18190)
+++ src/vehicle_cmd.cpp (working copy)
@@ -27,6 +27,7 @@
#include "string_func.h"
#include "depot_map.h"
#include "vehiclelist.h"
+#include "infrastructure_func.h"
#include "table/strings.h"
@@ -74,7 +75,7 @@
if ((flags & DC_AUTOREPLACE) == 0) SetBit(p2, 0);
Vehicle *v = Vehicle::GetIfValid(p1);
- if (v == NULL || !CheckOwnership(v->owner) || !v->IsPrimaryVehicle()) return CMD_ERROR;
+ if (v == NULL || !IsVehicleControlAllowed(v) || !v->IsPrimaryVehicle()) return CMD_ERROR;
switch (v->type) {
case VEH_TRAIN:
@@ -217,7 +218,7 @@
VehicleType vehicle_type = (VehicleType)GB(p1, 0, 8);
bool all_or_nothing = HasBit(p2, 0);
- if (!IsDepotTile(tile) || !IsTileOwner(tile, _current_company)) return CMD_ERROR;
+ if (!IsDepotTile(tile) || !IsInfraUsageAllowed(tile, _current_company, vehicle_type)) return CMD_ERROR;
/* Get the list of vehicles in the depot */
BuildDepotVehicleList(vehicle_type, tile, &list, &list, true);
Index: src/train_cmd.cpp
===================================================================
--- src/train_cmd.cpp (revision 18190)
+++ src/train_cmd.cpp (working copy)
@@ -36,6 +36,7 @@
#include "gamelog.h"
#include "network/network.h"
#include "spritecache.h"
+#include "infrastructure_func.h"
#include "table/strings.h"
#include "table/train_cmd.h"
@@ -58,7 +59,7 @@
* @param track vehicle track bits
* @return side of tile the train will leave
*/
-static inline DiagDirection TrainExitDir(Direction direction, TrackBits track)
+DiagDirection TrainExitDir(Direction direction, TrackBits track)
{
static const TrackBits state_dir_table[DIAGDIR_END] = { TRACK_BIT_RIGHT, TRACK_BIT_LOWER, TRACK_BIT_LEFT, TRACK_BIT_UPPER };
@@ -853,7 +854,7 @@
/* Check if the train is actually being built in a depot belonging
* to the company. Doesn't matter if only the cost is queried */
if (!IsRailDepotTile(tile)) return CMD_ERROR;
- if (!IsTileOwner(tile, _current_company)) return CMD_ERROR;
+ if (!IsInfraUsageAllowed(tile, _current_company, VEH_TRAIN)) return CMD_ERROR;
if (rvi->railveh_type == RAILVEH_WAGON) return CmdBuildRailWagon(p1, tile, flags);
@@ -2028,7 +2029,7 @@
CommandCost CmdReverseTrainDirection(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text)
{
Train *v = Train::GetIfValid(p1);
- if (v == NULL || !CheckOwnership(v->owner)) return CMD_ERROR;
+ if (v == NULL || !IsVehicleControlAllowed(v)) return CMD_ERROR;
if (p2 != 0) {
/* turn a single unit around */
@@ -2088,7 +2089,7 @@
CommandCost CmdForceTrainProceed(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text)
{
Train *t = Train::GetIfValid(p1);
- if (t == NULL || !CheckOwnership(t->owner)) return CMD_ERROR;
+ if (t == NULL || !IsVehicleControlAllowed(t)) return CMD_ERROR;
if (flags & DC_EXEC) t->force_proceed = 0x50;
@@ -2151,8 +2152,8 @@
static bool NtpCallbFindDepot(TileIndex tile, TrainFindDepotData *tfdd, int track, uint length)
{
if (IsTileType(tile, MP_RAILWAY) &&
- IsTileOwner(tile, tfdd->owner) &&
- IsRailDepot(tile)) {
+ IsRailDepot(tile) &&
+ IsInfraUsageAllowed(tile, tfdd->owner, VEH_TRAIN)) {
/* approximate number of tiles by dividing by DIAG_FACTOR */
tfdd->best_length = length / DIAG_FACTOR;
tfdd->tile = tile;
@@ -2479,7 +2480,7 @@
}
/** Clear the reservation of a tile that was just left by a wagon on track_dir. */
-static void ClearPathReservation(const Train *v, TileIndex tile, Trackdir track_dir)
+void ClearPathReservation(const Train *v, TileIndex tile, Trackdir track_dir)
{
DiagDirection dir = TrackdirToExitdir(track_dir);
@@ -3421,7 +3422,7 @@
static inline bool CheckCompatibleRail(const Train *v, TileIndex tile)
{
return
- IsTileOwner(tile, v->owner) && (
+ IsInfraUsageAllowed(tile, v) && (
!v->IsFrontEngine() ||
HasBit(v->compatible_railtypes, GetRailType(tile))
);
@@ -4540,6 +4541,9 @@
/* 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;
Index: src/command.cpp
===================================================================
--- src/command.cpp (revision 18190)
+++ src/command.cpp (working copy)
@@ -198,6 +198,7 @@
DEF_COMMAND(CmdChangeTimetable);
DEF_COMMAND(CmdSetVehicleOnTime);
DEF_COMMAND(CmdAutofillTimetable);
+DEF_COMMAND(CmdChangeSharingSetting);
#undef DEF_COMMAND
/**
@@ -343,6 +344,7 @@
{CmdChangeTimetable, 0}, // CMD_CHANGE_TIMETABLE
{CmdSetVehicleOnTime, 0}, // CMD_SET_VEHICLE_ON_TIME
{CmdAutofillTimetable, 0}, // CMD_AUTOFILL_TIMETABLE
+ {CmdChangeSharingSetting, 0}, // CMD_CHANGE_SHARING_SETTING
};
/*!
Index: src/company_base.h
===================================================================
--- src/company_base.h (revision 18190)
+++ src/company_base.h (working copy)
@@ -23,6 +23,7 @@
#include "economy_type.h"
#include "tile_type.h"
#include "settings_type.h"
+#include "infrastructure.h"
struct CompanyEconomyEntry {
Money income;
@@ -87,6 +88,12 @@
CompanySettings settings; ///< settings specific for each company
uint16 *num_engines; ///< caches the number of engines of each type the company owns (no need to save this)
+ byte sharing_delay; ///< number of months before new settings go in effect
+ CompanySharingSettingsEntry sharing_current; ///< settings currently in effect
+ CompanySharingSettingsEntry sharing_future; ///< future settings, will go in effect after (sharing_delay) months.
+ Date sharing_setting_change_news_date; ///< NOSAVE: date when a news message regarding an announced change of sharing settings was issued of the last time
+ Date sharing_insolvency_news_date; ///< NOSAVE: date when a news message regarding insolvency was issued for the last time
+
static FORCEINLINE bool IsValidAiID(size_t index)
{
const Company *c = Company::GetIfValid(index);
Index: src/settings_type.h
===================================================================
--- src/settings_type.h (revision 18190)
+++ src/settings_type.h (working copy)
@@ -193,6 +193,23 @@
uint32 ai_max_opcode_till_suspend; ///< max opcode calls till AI will suspend
};
+/** Settings related to sharing. */
+struct SharingSettings {
+ byte sharing_mode; ///< main infrastructure sharing setting, (off / on with global settings / on with individual settings)
+ byte new_individual_delay; ///< time (in months) it takes for new individual settings to take effect
+ bool payment_in_debt; ///< whether to allow fee payment for companies with more loan than money. Switch off to prevent MP exploits.
+
+ bool rail_sharing; ///< sharing rail
+ bool road_sharing; ///< sharing road
+ bool water_sharing; ///< sharing water
+ bool air_sharing; ///< sharing air
+
+ uint16 rail_fee; ///< train fee
+ uint16 road_fee; ///< road vehicle fee
+ uint16 water_fee; ///< ship fee
+ uint16 air_fee; ///< aircraft fee
+};
+
/** Settings related to the old pathfinder. */
struct OPFSettings {
uint16 pf_maxlength; ///< maximum length when searching for a train route for new pathfinder
@@ -375,6 +392,7 @@
EconomySettings economy; ///< settings to change the economy
StationSettings station; ///< settings related to station management
LocaleSettings locale; ///< settings related to used currency/unit system in the current game
+ SharingSettings sharing; ///< settings related to infrastructure sharing
};
/** All settings that are only important for the local client. */
Index: src/depot_gui.cpp
===================================================================
--- src/depot_gui.cpp (revision 18190)
+++ src/depot_gui.cpp (working copy)
@@ -29,6 +29,7 @@
#include "tilehighlight_func.h"
#include "window_gui.h"
#include "vehiclelist.h"
+#include "infrastructure_func.h"
#include "table/strings.h"
#include "table/sprites.h"
@@ -697,7 +698,7 @@
/* Setup disabled buttons. */
TileIndex tile = this->window_number;
- this->SetWidgetsDisabledState(!IsTileOwner(tile, _local_company),
+ this->SetWidgetsDisabledState(!IsInfraUsageAllowed(tile, _local_company, this->type),
DEPOT_WIDGET_STOP_ALL,
DEPOT_WIDGET_START_ALL,
DEPOT_WIDGET_SELL,
Index: src/command_type.h
===================================================================
--- src/command_type.h (revision 18190)
+++ src/command_type.h (working copy)
@@ -294,6 +294,7 @@
CMD_CHANGE_TIMETABLE, ///< change the timetable for a vehicle
CMD_SET_VEHICLE_ON_TIME, ///< set the vehicle on time feature (timetable)
CMD_AUTOFILL_TIMETABLE, ///< autofill the timetable
+ CMD_CHANGE_SHARING_SETTING, ///< change one of the settings for infrastructure sharing
};
/**
Index: src/order_gui.cpp
===================================================================
--- src/order_gui.cpp (revision 18190)
+++ src/order_gui.cpp (working copy)
@@ -30,6 +30,7 @@
#include "network/network.h"
#include "station_base.h"
#include "waypoint_base.h"
+#include "infrastructure_func.h"
#include "table/sprites.h"
#include "table/strings.h"
@@ -297,18 +298,16 @@
if (_settings_game.order.gotodepot) {
switch (GetTileType(tile)) {
case MP_RAILWAY:
- if (v->type == VEH_TRAIN && IsTileOwner(tile, _local_company)) {
- if (IsRailDepot(tile)) {
+ if (v->type == VEH_TRAIN && IsRailDepot(tile) && IsInfraUsageAllowed(tile, v)) {
order.MakeGoToDepot(GetDepotIndex(tile), ODTFB_PART_OF_ORDERS,
- _settings_client.gui.new_nonstop ? ONSF_NO_STOP_AT_INTERMEDIATE_STATIONS : ONSF_STOP_EVERYWHERE);
- if (_ctrl_pressed) order.SetDepotOrderType((OrderDepotTypeFlags)(order.GetDepotOrderType() ^ ODTFB_SERVICE));
- return order;
- }
+ _settings_client.gui.new_nonstop ? ONSF_NO_STOP_AT_INTERMEDIATE_STATIONS : ONSF_STOP_EVERYWHERE);
+ if (_ctrl_pressed) order.SetDepotOrderType((OrderDepotTypeFlags)(order.GetDepotOrderType() ^ ODTFB_SERVICE));
+ return order;
}
break;
case MP_ROAD:
- if (IsRoadDepot(tile) && v->type == VEH_ROAD && IsTileOwner(tile, _local_company)) {
+ if (IsRoadDepot(tile) && v->type == VEH_ROAD && IsInfraUsageAllowed(tile, v)) {
order.MakeGoToDepot(GetDepotIndex(tile), ODTFB_PART_OF_ORDERS,
_settings_client.gui.new_nonstop ? ONSF_NO_STOP_AT_INTERMEDIATE_STATIONS : ONSF_STOP_EVERYWHERE);
if (_ctrl_pressed) order.SetDepotOrderType((OrderDepotTypeFlags)(order.GetDepotOrderType() ^ ODTFB_SERVICE));
@@ -318,7 +317,7 @@
case MP_STATION:
if (v->type != VEH_AIRCRAFT) break;
- if (IsHangar(tile) && IsTileOwner(tile, _local_company)) {
+ if (IsHangar(tile) && IsInfraUsageAllowed(tile, v)) {
order.MakeGoToDepot(GetStationIndex(tile), ODTFB_PART_OF_ORDERS, ONSF_STOP_EVERYWHERE);
if (_ctrl_pressed) order.SetDepotOrderType((OrderDepotTypeFlags)(order.GetDepotOrderType() ^ ODTFB_SERVICE));
return order;
@@ -327,7 +326,7 @@
case MP_WATER:
if (v->type != VEH_SHIP) break;
- if (IsShipDepot(tile) && IsTileOwner(tile, _local_company)) {
+ if (IsShipDepot(tile) && IsInfraUsageAllowed(tile, v)) {
order.MakeGoToDepot(GetDepotIndex(tile), ODTFB_PART_OF_ORDERS, ONSF_STOP_EVERYWHERE);
if (_ctrl_pressed) order.SetDepotOrderType((OrderDepotTypeFlags)(order.GetDepotOrderType() ^ ODTFB_SERVICE));
return order;
@@ -341,7 +340,7 @@
/* check waypoint */
if (IsRailWaypointTile(tile) &&
v->type == VEH_TRAIN &&
- IsTileOwner(tile, _local_company)) {
+ IsInfraUsageAllowed(Waypoint::GetByTile(tile)->owner, v)) {
order.MakeGoToWaypoint(Waypoint::GetByTile(tile)->index);
if (_settings_client.gui.new_nonstop != _ctrl_pressed) order.SetNonStopType(ONSF_NO_STOP_AT_ANY_STATION);
return order;
@@ -356,7 +355,7 @@
StationID st_index = GetStationIndex(tile);
const Station *st = Station::Get(st_index);
- if (st->owner == _local_company || st->owner == OWNER_NONE) {
+ if (IsInfraUsageAllowed(st->owner, v)) {
byte facil;
(facil = FACIL_DOCK, v->type == VEH_SHIP) ||
(facil = FACIL_TRAIN, v->type == VEH_TRAIN) ||
Index: src/infrastructure.cpp
===================================================================
--- src/infrastructure.cpp (revision 0)
+++ src/infrastructure.cpp (revision 0)
@@ -0,0 +1,511 @@
+/* $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 "infrastructure_func.h"
+#include "command_func.h"
+#include "window_func.h"
+#include "depot_base.h"
+#include "train.h"
+#include "aircraft.h"
+#include "vehicle_func.h"
+#include "news_func.h"
+#include "strings_func.h"
+#include "yapf/yapf.h"
+#include "date_func.h"
+#include "gfx_func.h"
+#include "company_func.h"
+#include "station_base.h"
+#include "waypoint_base.h"
+
+#include "table/strings.h"
+
+extern DiagDirection TrainExitDir(Direction direction, TrackBits track);
+extern void ClearPathReservation(const Train *v, TileIndex tile, Trackdir track_dir);
+
+bool CompanySharingSettingsEntry::Equals(const CompanySharingSettingsEntry *o) const
+{
+ VehicleType vt;
+ FOR_ALL_SHARING_VEHTYPES(vt) {
+ if (this->IsSharingAllowed(vt) != o->IsSharingAllowed(vt) || this->GetSharingFee(vt) != o->GetSharingFee(vt)) return false;
+ }
+ return this->allowed_companies == o->allowed_companies;
+}
+
+byte CompanySharingSettingsEntry::Copy(const CompanySharingSettingsEntry *o)
+{
+ /* (x & y) != x returns true if any of the bits in x is not set in y.
+ * i.e. if any company, permission, etc is removed */
+ byte changed_vehtypes = 0;
+ VehicleType vt;
+ FOR_ALL_SHARING_VEHTYPES(vt) {
+ if (this->IsSharingAllowed(vt) && !o->IsSharingAllowed(vt)) {
+ SetBit(changed_vehtypes, vt);
+ }
+ }
+ if ((this->allowed_companies & o->allowed_companies) != this->allowed_companies) changed_vehtypes = ALL_SHARING_VEHTYPES;
+
+ /* Copy the entire struct */
+ *this = *o;
+
+ return changed_vehtypes;
+}
+
+byte CompanySharingSettingsEntry::CopyFromGameSettings()
+{
+ /* Create a struct with the same settings as in the game settings,
+ * then copy it to us using the normal Copy method. */
+ CompanySharingSettingsEntry game_settings;
+
+ game_settings.allowed_types = 0;
+ game_settings.SetSharingAllowed(VEH_TRAIN, _settings_game.sharing.rail_sharing);
+ game_settings.SetSharingAllowed(VEH_ROAD, _settings_game.sharing.road_sharing);
+ game_settings.SetSharingAllowed(VEH_SHIP, _settings_game.sharing.water_sharing);
+ game_settings.SetSharingAllowed(VEH_AIRCRAFT, _settings_game.sharing.air_sharing);
+
+ game_settings.SetSharingFee(VEH_TRAIN, _settings_game.sharing.rail_fee);
+ game_settings.SetSharingFee(VEH_ROAD, _settings_game.sharing.road_fee);
+ game_settings.SetSharingFee(VEH_SHIP, _settings_game.sharing.water_fee);
+ game_settings.SetSharingFee(VEH_AIRCRAFT, _settings_game.sharing.air_fee);
+
+ game_settings.allowed_companies = 0;
+ Company *c;
+ FOR_ALL_COMPANIES(c) {
+ game_settings.SetCompanyAllowed(c->index, true);
+ }
+
+ return this->Copy(&game_settings);
+}
+
+/**
+ * Helper function for transferring sharing fees
+ * @param v The vehicle involved
+ * @param 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 owner, Money cost)
+{
+ Company *c = Company::GetIfValid(v->owner);
+ assert(c != NULL);
+ if (_settings_game.sharing.payment_in_debt || (c->money - c->current_loan) >= (cost >> 8)) {
+ v->profit_this_year -= cost;
+ SubtractMoneyFromCompanyFract(v->owner, CommandCost(EXPENSES_SHARED_RUN, cost));
+ SubtractMoneyFromCompanyFract(owner, CommandCost(EXPENSES_SHARED_INC, -cost));
+ } else if (owner == _local_company && (c->sharing_insolvency_news_date + SHARING_NEWS_INTERVAL_INSOLVENCY) <= _date) {
+ AddSharingNewsItem(STR_NEWS_SHARING_UNABLE_TO_PAY, c);
+ c->sharing_insolvency_news_date = _date;
+ }
+}
+
+/**
+ * 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 = GetSharingFee(st->owner, v->type);
+ PaySharingFee(v, st->owner, (cost << 8) / DAY_TICKS);
+}
+
+/**
+ * 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 = GetSharingFee(owner, VEH_TRAIN);
+ cost = (cost << 8) * v->tcache.cached_total_length / 16; //cost is per 16 units = 1 tile
+ cost = cost * 12 / DAYS_IN_YEAR; //cost is per month, but we pay daily.
+ PaySharingFee(v, owner, cost);
+}
+
+/**
+ * Add a sharing-related news message to the news stack.
+ * @param string The message string to show.
+ * @param c Company this message is about.
+ * @param param Optional additional parameter to use for drawing the string.
+ */
+void AddSharingNewsItem(StringID string, Company *c, uint32 param)
+{
+ CompanyNewsInformation *cni = MallocT(1);
+ cni->FillData(c);
+
+ SetDParam(0, STR_NEWS_SHARING_TITLE);
+ SetDParam(1, string);
+ SetDParamStr(2, cni->company_name);
+ SetDParam(3, param);
+
+ AddCompanyNewsItem(STR_MESSAGE_NEWS_FORMAT, NS_COMPANY_SHARING, cni);
+}
+
+/**
+ * Change the sharing settings of a company.
+ * @see ModifySharingFlag
+ * @param tile unused
+ * @param flags operation to perform
+ * @param p1 bitstuffed elements regarding the variable to change:
+ * - p1 = (bit 0 - 7) What to change (boolean option, fee or allowed company)
+ * - p1 = (bit 8 - 15) The vehicle type or company to change it for.
+ * @param p2 value to change the aforementioned variable to
+ * @param text unused
+ * @return the cost of this operation or an error
+ */
+CommandCost CmdChangeSharingSetting(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text)
+{
+ if (_settings_game.sharing.sharing_mode != SHARING_INDIVIDUAL) return CMD_ERROR;
+ byte index = GB(p1, 8, 8); //vehicle type or company id
+
+ Company *c = Company::GetIfValid(_current_company);
+ if (c == NULL) return CMD_ERROR;
+
+ ModifySharingFlag msf = (ModifySharingFlag)GB(p1, 0, 8);
+ switch (msf) {
+ case MODIFY_SHARING_OPTION:
+ if (index >= NUM_SHARING_VEHTYPES) return CMD_ERROR;
+ if (!!p2 == c->sharing_future.IsSharingAllowed((VehicleType)index)) return CMD_ERROR;
+ break;
+ case MODIFY_SHARING_FEE:
+ if (index >= NUM_SHARING_VEHTYPES) return CMD_ERROR;
+ if (p2 > MAX_SHARING_FEE || p2 == c->sharing_future.GetSharingFee((VehicleType)index)) return CMD_ERROR;
+ break;
+ case MODIFY_SHARING_COMPANIES:
+ if (!Company::IsValidID(index) || index == _current_company) return CMD_ERROR;
+ if (!!p2 == c->sharing_future.IsCompanyAllowed((CompanyID)index)) return CMD_ERROR;
+ break;
+ default: return CMD_ERROR;
+ }
+
+ if (flags & DC_EXEC) {
+ switch (msf) {
+ case MODIFY_SHARING_OPTION: c->sharing_future.SetSharingAllowed((VehicleType)index, p2 != 0); break;
+ case MODIFY_SHARING_FEE: c->sharing_future.SetSharingFee((VehicleType)index, p2); break;
+ case MODIFY_SHARING_COMPANIES: c->sharing_future.SetCompanyAllowed((CompanyID)index, p2 != 0); break;
+ default: NOT_REACHED();
+ }
+
+ /* Set the sharing delay, and copy the future settings to the current settings if needed */
+ if (c->sharing_future.Equals(&c->sharing_current)) {
+ c->sharing_delay = 0;
+ } else if (_settings_game.sharing.new_individual_delay == 0) {
+ assert(c->sharing_delay == 0);
+ ProcessSharingChanges();
+ } else {
+ c->sharing_delay = _settings_game.sharing.new_individual_delay;
+ if (_current_company != _local_company && c->sharing_setting_change_news_date + SHARING_NEWS_INTERVAL_SETTING_CHANGE <= _date) {
+ c->sharing_setting_change_news_date = _date;
+ AddSharingNewsItem(STR_NEWS_SHARING_CHANGE_ANNOUNCED, c, c->sharing_delay);
+ }
+ }
+ InvalidateWindowData(WC_SHARING_OPTIONS, _current_company);
+ }
+ return CommandCost();
+}
+
+/**
+ * Update all level crossings in front of a train.
+ * This has to be done before all other actions.
+ */
+static void UpdateAllCrossings()
+{
+ Train *v;
+ FOR_ALL_TRAINS(v) {
+ TileIndex crossing = v->tile + TileOffsByDiagDir(TrainExitDir(v->direction, v->track));
+ if (IsLevelCrossingTile(crossing)) UpdateLevelCrossing(crossing, false);
+ }
+}
+
+/**
+ * Sell a vehicle, no matter where it may be.
+ * Also make sure no artifacts are left, such as PBS paths
+ * @param v The vehicle to sell
+ */
+static void RemoveAndSellVehicle(Vehicle *v)
+{
+ static SmallVector crossing_tiles;
+
+ /* Crashed vehicles will remove themselves, no need for action */
+ if (v->vehstatus & VS_CRASHED) return;
+
+ if (v->type == VEH_TRAIN) {
+ /* Clear path reservation. This will not clear any reservation that has become invalid, we will do that later */
+ FreeTrainTrackReservation(Train::From(v));
+ for (const Train *u = Train::From(v); u != NULL; u = u->Next()) {
+ /* We cannot remove these reservations reliably,
+ * since wormholes can contain more than one vehicle.
+ * Therefore, we leave the reservation, it will be dealt with later. */
+ if (!IsTileType(u->tile, MP_TUNNELBRIDGE)) {
+ ClearPathReservation(u, u->tile, u->GetVehicleTrackdir());
+ }
+ if (IsLevelCrossingTile(u->tile) && !crossing_tiles.Contains(u->tile)) {
+ *crossing_tiles.Append() = u->tile;
+ }
+ }
+ }
+ SubtractMoneyFromCompanyFract(v->owner, CommandCost(EXPENSES_NEW_VEHICLES, -(v->value << 8)));
+ delete v;
+
+ for (uint i = 0; i < crossing_tiles.Length(); i++) {
+ UpdateLevelCrossing(crossing_tiles[i], false);
+ }
+ crossing_tiles.Clear();
+}
+
+
+/**
+ * Check the positions of all vehicles.
+ * If it is not in an allowed position,
+ * (e.g. it is in a depot when sharing of depots is disabled)
+ * then it is sold immediately.
+ * @param changed_vehtypes bitmask of vehicle types that were affected by a sharing setting change.
+ * @return true if any *trains* were removed.
+ */
+static bool CheckAllVehiclePositions(byte changed_vehtypes)
+{
+ bool sold_trains = false;
+ uint num_sold_local_vehicles = 0;
+ Vehicle *v;
+ FOR_ALL_VEHICLES(v) {
+ if (!(v->IsPrimaryVehicle() || (v->type == VEH_TRAIN && Train::From(v)->IsFreeWagon()))) continue;
+ if (!HasBit(changed_vehtypes, v->type)) continue;
+ switch (v->type) {
+ case VEH_TRAIN:
+ for (Vehicle *u = v; u != NULL; u = u->Next()) {
+ /* If any wagon is on a disallowed tile, sell the entire train */
+ if (!IsInfraUsageAllowed(u->tile, u)) {
+ if (v->owner == _local_company) num_sold_local_vehicles++;
+ sold_trains = true;
+ RemoveAndSellVehicle(v);
+ break;
+ }
+ }
+ break;
+ case VEH_ROAD:
+ for (Vehicle *u = v; u != NULL; u = u->Next()) {
+ /* Check all articulated parts */
+ if ((IsRoadDepotTile(u->tile) && !IsInfraUsageAllowed(u->tile, u)) ||
+ (IsStandardRoadStopTile(u->tile) && !IsInfraUsageAllowed(u->tile, u))) {
+ if (v->owner == _local_company) num_sold_local_vehicles++;
+ RemoveAndSellVehicle(v);
+ break;
+ }
+ }
+ break;
+ case VEH_SHIP:
+ if (IsShipDepotTile(v->tile) && v->IsStoppedInDepot() && !IsInfraUsageAllowed(v->tile, v)) {
+ if (v->owner == _local_company) num_sold_local_vehicles++;
+ RemoveAndSellVehicle(v);
+ }
+ break;
+ case VEH_AIRCRAFT: {
+ Aircraft *a = Aircraft::From(v);
+ if (a->state == FLYING) break;
+ if (Station::IsValidID(a->targetairport) &&
+ !IsInfraUsageAllowed(Station::Get(a->targetairport)->owner, v)) {
+ if (v->owner == _local_company) num_sold_local_vehicles++;
+ RemoveAndSellVehicle(v);
+ }
+ break;
+ }
+ default: break;
+ }
+ }
+ if (num_sold_local_vehicles > 0) {
+ SetDParam(0, num_sold_local_vehicles);
+ AddNewsItem(STR_NEWS_SHARING_SOLD_VEHICLES, NS_GENERAL);
+ }
+
+ return sold_trains;
+}
+
+/**
+ * Check whether an order has an allowed destination.
+ * I.e. it refers to a station/depot/waypoint the vehicle is allowed to visit.
+ * @param o The order to check
+ * @param v The vehicle this order belongs to.
+ * @return true if the order has an allowed destination.
+ */
+static bool OrderDestinationIsAllowed(const Order *o, const Vehicle *v)
+{
+ DestinationID dest = o->GetDestination();
+ switch (o->GetType()) {
+ case OT_GOTO_STATION: return IsInfraUsageAllowed(Station::Get(dest)->owner, v);
+ case OT_GOTO_DEPOT: return v->type == VEH_AIRCRAFT ? IsInfraUsageAllowed(Station::Get(dest)->owner, v) : IsInfraUsageAllowed(Depot::Get(dest)->xy, v);
+ case OT_GOTO_WAYPOINT: return IsInfraUsageAllowed(Waypoint::Get(dest)->owner, v);
+ default: return true;
+ }
+}
+
+/**
+ * Review the orders of all vehicles
+ * This makes sure they don't have any orders to destinations
+ * where they are not allowed to go.
+ * @param changed_vehtypes bitmask of vehicle types that were affected by a sharing setting change.
+ */
+static void ReviewAllVehicleOrders(byte changed_vehtypes)
+{
+ Vehicle *v;
+ FOR_ALL_VEHICLES(v) {
+ if (!(v->IsPrimaryVehicle() && HasBit(changed_vehtypes, v->type))) continue;
+
+ if (!OrderDestinationIsAllowed(&v->current_order, v)) {
+ v->current_order.MakeDummy();
+ SetWindowDirty(WC_VEHICLE_VIEW, v->index);
+ } else if (v->current_order.IsType(OT_LOADING)) {
+ if (!IsInfraUsageAllowed(Station::Get(v->last_station_visited)->owner, v)) {
+ v->LeaveStation();
+ }
+ }
+
+ if (v->FirstShared() != v) continue;
+
+ Order *o;
+ int id = -1;
+ FOR_VEHICLE_ORDERS(v, o) {
+ id++;
+ if (OrderDestinationIsAllowed(o, v)) continue;
+
+ o->MakeDummy();
+ for (const Vehicle *w = v->FirstShared(); 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, (INVALID_VEH_ORDER_ID << 8) | id);
+ }
+ }
+ }
+}
+/**
+ * Helper proc for HasVehicleOnPos to determine whether there is a train on a certain track.
+ * @param v The vehicle that may be on this track
+ * @param track the track the train should be on.
+ * @return v if it is on this track, or null otherwise.
+ */
+static Vehicle *HasTrainOnTrackProc(Vehicle *v, void *track)
+{
+ if (v->type != VEH_TRAIN) return NULL;
+ Train *t = Train::From(v);
+ if (TrackBitsToTrack(t->track) == *(Track *)track || t->track == TRACK_BIT_WORMHOLE) {
+ return v;
+ }
+ return NULL;
+}
+
+/**
+ * Check the reservations of all tiles.
+ * If these to not seem to belong to a train or the train is not allowed to use them, unreserve them.
+ */
+static void CheckAllReservations()
+{
+ for (TileIndex tile = 0; tile < MapSize(); tile++) {
+ TrackBits bits = GetReservedTrackbits(tile);
+ while (bits != TRACK_BIT_NONE) {
+ Track track = RemoveFirstTrack(&bits);
+ /* Don't bother continuing if there is a vehicle on this tile */
+ if (HasVehicleOnPos(tile, &track, &HasTrainOnTrackProc)) continue;
+
+ Train *v = GetTrainForReservation(tile, track);
+ if (v == NULL) {
+ UnreserveRailTrack(tile, track);
+ /* make any PBS signal red */
+ Trackdir td = TrackToTrackdir(track);
+ if (HasPbsSignalOnTrackdir(tile, td)) SetSignalStateByTrackdir(tile, td, SIGNAL_STATE_RED);
+ if (HasPbsSignalOnTrackdir(tile, ReverseTrackdir(td))) SetSignalStateByTrackdir(tile, ReverseTrackdir(td), SIGNAL_STATE_RED);
+ }
+ }
+ }
+}
+
+/** Small struct to store a tile/track combination */
+struct SignalPosition {
+ TileIndex tile; ///< Tile of the signal.
+ Track track; ///< Track piece of the signal.
+ /** Create a new SignalPosition based on given tile and track piece */
+ SignalPosition(TileIndex _tile, Track _track) : tile(_tile), track(_track) {};
+};
+
+/**
+ * Update all signals on the map.
+ * Since whe do not know which ones are affected by the sharing change, we invalidate them all.
+ * Updating signals can only be done on a per-company basis.
+ * Therefore, we cache all signals into lists, one per company
+ * Then we update all signals in each list.
+ */
+static void UpdateBlockSignals()
+{
+ static SmallVector signal_lists[MAX_COMPANIES];
+
+ for (TileIndex tile = 0; tile < MapSize(); tile++) {
+ if (IsTileType(tile, MP_RAILWAY) && HasSignals(tile)) {
+ TrackBits bits = GetTrackBits(tile);
+ do {
+ Track track = RemoveFirstTrack(&bits);
+ if (HasSignalOnTrack(tile, track)) {
+ *signal_lists[GetTileOwner(tile)].Append() = SignalPosition(tile, track);
+ }
+ } while (bits != TRACK_BIT_NONE);
+ }
+ }
+ for (CompanyID c = COMPANY_FIRST; c < MAX_COMPANIES; c++) {
+ for (uint i = 0; i < signal_lists[c].Length(); i++) {
+ AddTrackToSignalBuffer(signal_lists[c][i].tile, signal_lists[c][i].track, c);
+ }
+ UpdateSignalsInBuffer();
+ signal_lists[c].Reset();
+ }
+}
+
+/**
+ * Handle a change of sharing settings.
+ * This involves checking all orders,
+ * making sure all vehicles are not on disallowed territory,
+ * checking all PBS paths
+ * and updating signals.
+ * @param update_signals Force updating all signals on the map.
+ */
+void ProcessSharingChanges(bool update_signals)
+{
+ byte changed_vehtypes = 0;
+
+ /* Copy all future settings to the current settings, if applicable */
+ Company *c;
+ FOR_ALL_COMPANIES(c) {
+ if (c->sharing_delay == 0 && !c->sharing_current.Equals(&c->sharing_future)) {
+ byte changes = c->sharing_current.Copy(&c->sharing_future);
+ if (c->index != _local_company) AddSharingNewsItem(STR_NEWS_SHARING_CHANGE_HAPPENED, c);
+ changed_vehtypes |= changes;
+ }
+ }
+ InvalidateWindowClassesData(WC_SHARING_OPTIONS);
+
+ /* No changes at all? abort */
+ if (changed_vehtypes == 0 && !update_signals) return;
+
+ /* Invalidate the entire cache */
+ YapfNotifyTrackLayoutChange(INVALID_TILE, INVALID_TRACK);
+
+ /* Almost any change in settings can causes changes in the level crossings.
+ * So always review those. */
+ UpdateAllCrossings();
+
+ if (changed_vehtypes != 0) {
+ update_signals |= CheckAllVehiclePositions(changed_vehtypes);
+ ReviewAllVehicleOrders(changed_vehtypes);
+ }
+ if (HasBit(changed_vehtypes, VEH_TRAIN)) {
+ CheckAllReservations();
+ }
+ if (update_signals) {
+ UpdateBlockSignals();
+ }
+ MarkWholeScreenDirty();
+}
Index: src/economy.cpp
===================================================================
--- src/economy.cpp (revision 18190)
+++ src/economy.cpp (working copy)
@@ -47,6 +47,7 @@
#include "waypoint_base.h"
#include "economy_base.h"
#include "core/pool_func.hpp"
+#include "infrastructure_func.h"
#include "table/strings.h"
#include "table/sprites.h"
@@ -388,6 +389,47 @@
}
}
+ /* make the sharing settings the most permissive of the two companies */
+ if (new_owner != INVALID_OWNER) {
+ Company *c_new = Company::Get(new_owner);
+ CompanySharingSettingsEntry *old_settings = &Company::Get(old_owner)->sharing_current;
+ CompanySharingSettingsEntry *new_settings = &c_new->sharing_current;
+
+ VehicleType vt;
+ FOR_ALL_SHARING_VEHTYPES(vt) {
+ if (old_settings->IsSharingAllowed(vt)) new_settings->SetSharingAllowed(vt, true);
+ new_settings->SetSharingFee(vt, min(old_settings->GetSharingFee(vt), new_settings->GetSharingFee(vt)));
+ }
+ new_settings->allowed_companies |= old_settings->allowed_companies;
+
+ /* Make the future settings for the new company the same as the (new) current ones */
+ c_new->sharing_future.Copy(new_settings);
+ c_new->sharing_delay = 0;
+ } else {
+ /* Disable sharing for the old company
+ * This will cause all vehicles on the old company's infrastructure
+ * to be removed when ProcessSharingChanges is called */
+ Company *c_old = Company::Get(old_owner);
+ VehicleType vt;
+ FOR_ALL_SHARING_VEHTYPES(vt) {
+ c_old->sharing_future.SetSharingAllowed(vt, false);
+ }
+ c_old->sharing_delay = 0;
+ ProcessSharingChanges();
+ }
+
+ /* disallow the old company from all infrastructure */
+ Company *c;
+ FOR_ALL_COMPANIES(c) {
+ c->sharing_current.SetCompanyAllowed(old_owner, false);
+ c->sharing_future.SetCompanyAllowed(old_owner, false);
+ /* Future and current settings could now be equal
+ * In that case, set sharing_delay to 0 */
+ if (c->sharing_current.Equals(&c->sharing_future)) {
+ c->sharing_delay = 0;
+ }
+ }
+
/* Change ownership of tiles */
{
TileIndex tile = 0;
@@ -700,6 +742,18 @@
}
}
+/**
+ * Update the sharing settings of a all companies, if needed.
+ */
+static void CompaniesUpdateSharing()
+{
+ Company *c;
+ FOR_ALL_COMPANIES(c) {
+ if (c->sharing_delay > 0) --c->sharing_delay;
+ }
+ ProcessSharingChanges();
+}
+
static void HandleEconomyFluctuations()
{
if (_settings_game.difficulty.economy != 0) {
@@ -1403,6 +1457,7 @@
RecomputePrices();
}
CompaniesPayInterest();
+ CompaniesUpdateSharing();
/* Reset the _current_company flag */
_current_company = OWNER_NONE;
HandleEconomyFluctuations();
Index: src/company_gui.cpp
===================================================================
--- src/company_gui.cpp (revision 18190)
+++ src/company_gui.cpp (working copy)
@@ -29,6 +29,9 @@
#include "date_func.h"
#include "widgets/dropdown_type.h"
#include "tilehighlight_func.h"
+#include "company_gui.h"
+#include "currency.h"
+#include "openttd.h"
#include "table/strings.h"
@@ -41,8 +44,9 @@
};
static void DoSelectCompanyManagerFace(Window *parent, bool show_big, int top = FIRST_GUI_CALL, int left = FIRST_GUI_CALL);
+static void ShowCompanySharingSettings(CompanyID company);
-/** Standard unsorted list of expenses. */
+/** Standard unsorted list of expenses with infrastructure sharing disabled. */
static ExpensesType _expenses_list_1[] = {
EXPENSES_CONSTRUCTION,
EXPENSES_NEW_VEHICLES,
@@ -59,7 +63,7 @@
EXPENSES_OTHER,
};
-/** Grouped list of expenses. */
+/** Grouped list of expenses with infrastructure sharing disabled. */
static ExpensesType _expenses_list_2[] = {
EXPENSES_TRAIN_INC,
EXPENSES_ROADVEH_INC,
@@ -79,6 +83,47 @@
INVALID_EXPENSES,
};
+/** Standard unsorted list of expenses with infrastructure sharing enabled. */
+static ExpensesType _expenses_list_3[] = {
+ EXPENSES_CONSTRUCTION,
+ EXPENSES_NEW_VEHICLES,
+ EXPENSES_TRAIN_RUN,
+ EXPENSES_ROADVEH_RUN,
+ EXPENSES_AIRCRAFT_RUN,
+ EXPENSES_SHIP_RUN,
+ EXPENSES_SHARED_RUN,
+ EXPENSES_PROPERTY,
+ EXPENSES_TRAIN_INC,
+ EXPENSES_ROADVEH_INC,
+ EXPENSES_AIRCRAFT_INC,
+ EXPENSES_SHIP_INC,
+ EXPENSES_SHARED_INC,
+ EXPENSES_LOAN_INT,
+ EXPENSES_OTHER,
+};
+
+/** Grouped list of expenses with infrastructure sharing enabled. */
+static ExpensesType _expenses_list_4[] = {
+ EXPENSES_TRAIN_INC,
+ EXPENSES_ROADVEH_INC,
+ EXPENSES_AIRCRAFT_INC,
+ EXPENSES_SHIP_INC,
+ EXPENSES_SHARED_INC,
+ INVALID_EXPENSES,
+ EXPENSES_TRAIN_RUN,
+ EXPENSES_ROADVEH_RUN,
+ EXPENSES_AIRCRAFT_RUN,
+ EXPENSES_SHIP_RUN,
+ EXPENSES_SHARED_RUN,
+ EXPENSES_PROPERTY,
+ EXPENSES_LOAN_INT,
+ INVALID_EXPENSES,
+ EXPENSES_CONSTRUCTION,
+ EXPENSES_NEW_VEHICLES,
+ EXPENSES_OTHER,
+ INVALID_EXPENSES,
+};
+
/** Expense list container. */
struct ExpensesList {
const ExpensesType *et; ///< Expenses items.
@@ -118,6 +163,8 @@
static const ExpensesList _expenses_list_types[] = {
ExpensesList(_expenses_list_1, lengthof(_expenses_list_1), 0),
ExpensesList(_expenses_list_2, lengthof(_expenses_list_2), 3),
+ ExpensesList(_expenses_list_3, lengthof(_expenses_list_3), 0),
+ ExpensesList(_expenses_list_4, lengthof(_expenses_list_4), 3),
};
/** Widgets of the company finances windows. */
@@ -158,7 +205,7 @@
DrawString(r.left, r.right, y, STR_FINANCES_EXPENDITURE_INCOME_TITLE, TC_FROMSTRING, SA_CENTER, true);
y += FONT_HEIGHT_NORMAL + EXP_LINESPACE;
- int type = _settings_client.gui.expenses_layout;
+ int type = _settings_game.sharing.sharing_mode != SHARING_NONE ? _settings_client.gui.expenses_layout + 2 : _settings_client.gui.expenses_layout;
for (uint i = 0; i < _expenses_list_types[type].length; i++) {
const ExpensesType et = _expenses_list_types[type].et[i];
if (et == INVALID_EXPENSES) {
@@ -207,7 +254,7 @@
Money sum = 0;
Money subtotal = 0;
- int type = _settings_client.gui.expenses_layout;
+ int type = _settings_game.sharing.sharing_mode != SHARING_NONE ? _settings_client.gui.expenses_layout + 2: _settings_client.gui.expenses_layout;
for (uint i = 0; i < _expenses_list_types[type].length; i++) {
const ExpensesType et = _expenses_list_types[type].et[i];
if (et == INVALID_EXPENSES) {
@@ -322,7 +369,7 @@
virtual void UpdateWidgetSize(int widget, Dimension *size, const Dimension &padding, Dimension *resize)
{
- int type = _settings_client.gui.expenses_layout;
+ int type = _settings_game.sharing.sharing_mode != SHARING_NONE ? _settings_client.gui.expenses_layout + 2: _settings_client.gui.expenses_layout;
switch (widget) {
case CFW_EXPS_CATEGORY:
size->width = _expenses_list_types[type].GetCategoriesWidth();
@@ -412,7 +459,7 @@
{
if (!small) {
/* Check that the expenses panel height matches the height needed for the layout. */
- int type = _settings_client.gui.expenses_layout;
+ int type = _settings_game.sharing.sharing_mode != SHARING_NONE ? _settings_client.gui.expenses_layout + 2 : _settings_client.gui.expenses_layout;
if (_expenses_list_types[type].GetHeight() != this->GetWidget(CFW_EXPS_CATEGORY)->current_y) {
this->SetupWidgets();
this->ReInit();
@@ -1430,6 +1477,9 @@
CW_WIDGET_SELECT_RELOCATE, ///< View/hide the 'Relocate HQ' button.
CW_WIDGET_RELOCATE_EMPTY, ///< Empty widget to hide the relocate HQ button.
CW_WIDGET_RELOCATE_HQ,
+ CW_WIDGET_SELECT_SHARING, ///< View/hide the 'sharing' button.
+ CW_WIDGET_SHARING_EMPTY, ///< Empty widget to hide the sharing button.
+ CW_WIDGET_SHARING, ///< Button to open the infrastructure sharing window.
CW_WIDGET_BUY_SHARE,
CW_WIDGET_SELL_SHARE,
CW_WIDGET_SELECT_MULTIPLAYER, ///< Multiplayer selection panel.
@@ -1459,7 +1509,17 @@
EndContainer(),
NWidget(WWT_EMPTY, INVALID_COLOUR, CW_WIDGET_RELOCATE_EMPTY), SetFill(true, true),
EndContainer(),
- NWidget(NWID_SPACER), SetMinimalSize(0, 94),
+ NWidget(NWID_SPACER), SetMinimalSize(0, 2),
+ /* Sharing button */
+ NWidget(NWID_SELECTION, INVALID_COLOUR, CW_WIDGET_SELECT_SHARING),
+ NWidget(NWID_HORIZONTAL),
+ NWidget(NWID_SPACER), SetFill(true, false),
+ NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, CW_WIDGET_SHARING), SetMinimalSize(90, 12), SetPadding(0, 4, 0, 0),
+ SetDataTip(STR_COMPANY_VIEW_SHARING_BUTTON, STR_COMPANY_VIEW_SHARING_TOOLTIP),
+ EndContainer(),
+ NWidget(WWT_EMPTY, INVALID_COLOUR, CW_WIDGET_SHARING_EMPTY), SetFill(true, true),
+ EndContainer(),
+ NWidget(NWID_SPACER), SetMinimalSize(0, 80),
/* Multi player buttons. */
NWidget(NWID_SELECTION, INVALID_COLOUR, CW_WIDGET_SELECT_MULTIPLAYER),
NWidget(WWT_EMPTY, INVALID_COLOUR, CW_WIDGET_MP_EMPTY), SetFill(true, true),
@@ -1585,6 +1645,10 @@
CWP_RELOCATE_SHOW = 0, ///< Show the relocate HQ button.
CWP_RELOCATE_HIDE, ///< Hide the relocate HQ button.
+ /* Display planes of the #CW_WIDGET_SELECT_SHARING selection widget. */
+ CWP_SHARING_SHOW = 0, ///< Show the sharing button.
+ CWP_SHARING_HIDE, ///< Hide the sharing button.
+
/* Display planes of the #CW_WIDGET_SELECT_BUTTONS selection widget. */
CWP_BUTTONS_LOCAL = 0, ///< Buttons of the local company.
CWP_BUTTONS_OTHER, ///< Buttons of the other companies.
@@ -1626,6 +1690,15 @@
return;
}
+ /* Enable/disable 'Sharing' button. */
+ plane = (_settings_game.sharing.sharing_mode == SHARING_INDIVIDUAL) ? CWP_SHARING_SHOW : CWP_SHARING_HIDE;
+ wi = this->GetWidget(CW_WIDGET_SELECT_SHARING);
+ if (plane != wi->shown_plane) {
+ wi->SetDisplayedPlane(plane);
+ this->SetDirty();
+ return;
+ }
+
/* Multiplayer buttons. */
plane = ((!_networking) ? CWP_MP_EMPTY : (local ? CWP_MP_C_PWD : CWP_MP_C_JOIN));
wi = this->GetWidget(CW_WIDGET_SELECT_MULTIPLAYER);
@@ -1754,6 +1827,10 @@
DoCommandP(0, this->window_number, 0, CMD_SELL_SHARE_IN_COMPANY | CMD_MSG(STR_ERROR_CAN_T_SELL_25_SHARE_IN));
break;
+ case CW_WIDGET_SHARING:
+ ShowCompanySharingSettings((CompanyID)this->window_number);
+ break;
+
#ifdef ENABLE_NETWORK
case CW_WIDGET_COMPANY_PASSWORD:
if (this->window_number == _local_company) ShowNetworkCompanyPasswordWindow(this);
@@ -1945,3 +2022,456 @@
{
AllocateWindowDescFront(&_buy_company_desc, company);
}
+
+/** per-company sharing settings window */
+enum SharingSettingsWindowWidgets {
+ SSW_WIDGET_CLOSEBOX = 0,
+ SSW_WIDGET_CAPTION,
+ SSW_WIDGET_STICKY,
+ SSW_WIDGET_PANEL,
+ SSW_WIDGET_ICONS,
+ SSW_WIDGET_SETTINGS,
+ SSW_WIDGET_CURRENT,
+ SSW_WIDGET_BUTTONS,
+ SSW_WIDGET_FUTURE,
+ SSW_WIDGET_SCROLL,
+ SSW_WIDGET_OPTIONS,
+ SSW_WIDGET_COMPANIES,
+ SSW_WIDGET_RESIZE,
+};
+
+/** Window that shows per-company infrastructure sharing settings and allows changing them */
+struct SharingSettingsWindow : public Window {
+private:
+ /** order of lines in the options tab */
+ enum SharingSettingsWindowLine {
+ SSWL_HEADERS = 0,
+ SSWL_COMPANY_FIRST = 1,
+ SSWL_RAIL_OPTION = 1,
+ SSWL_RAIL_FEE,
+ SSWL_ROAD_OPTION = 4,
+ SSWL_ROAD_FEE,
+ SSWL_WATER_OPTION = 7,
+ SSWL_WATER_FEE,
+ SSWL_AIR_OPTION = 10,
+ SSWL_AIR_FEE,
+ SSWL_END,
+ };
+
+ Company *company; ///< The company this window belongs to
+ CompanySharingSettingsEntry *future; ///< future sharing settings
+ CompanySharingSettingsEntry *current; ///< current sharing settings
+
+ bool companies_tab; ///< Is the companies tab currently open?
+
+ VehicleType current_query_type; ///< type of vehicle for which a querystring window is open.
+ byte clicked_row; ///< which of the arrow sets was clicked
+ byte clicked_side; ///< was the left or the right arrow clicked?
+
+ uint line_height; ///< Height of a single line of text + buttons
+
+ /**
+ * Draw a string on top of a certain widget.
+ * @param widget The widget to draw the string on.
+ * @param y The y-coordinate to draw on.
+ * @param string The string to draw.
+ * @param colour TextColour to use.
+ */
+ inline void DrawWidgetString(int widget, uint y, StringID string, TextColour colour = TC_FROMSTRING)
+ {
+ NWidgetCore *wi = this->GetWidget(widget);
+ DrawString(wi->pos_x, wi->pos_x + wi->current_x - 1, y, string, colour);
+ }
+
+ /**
+ * Draw the value of a boolean setting ([Not] Allowed) in the column 'current' or 'future'.
+ * @param y The y-coordinate where the string should be drawn.
+ * @param value The boolean value to draw (Allowed / Not allowed).
+ * @param column_future If true, draw in the column 'future', if false, draw in the column 'current'.
+ */
+ inline void DrawBoolValue(uint y, bool value, bool column_future)
+ {
+ this->DrawWidgetString(column_future ? SSW_WIDGET_FUTURE : SSW_WIDGET_CURRENT, y, value ? STR_SHARING_VALUE_ALLOWED : STR_SHARING_VALUE_NOT_ALLOWED);
+ }
+
+ /**
+ * Draw a boolean sharing setting.
+ * @param str The string to draw. (any parameters can be set beforehand)
+ * @param y The y-coordinate where the string should be drawn. Is increased by this->line_height after drawing.
+ * @param value_current The boolean value to draw (Allowed / Not allowed) in the column 'current'.
+ * @param value_future The boolean value to draw (Allowed / Not allowed) in the column 'future'.
+ */
+ void DrawSharingOption(StringID str, uint &y, bool value_current, bool value_future)
+ {
+ static const Colours _button_colour_table[2][2] = {{COLOUR_CREAM, COLOUR_RED}, {COLOUR_DARK_GREEN, COLOUR_GREEN}};
+
+ NWidgetCore *wi = this->GetWidget(SSW_WIDGET_BUTTONS);
+ DrawFrameRect(wi->pos_x, y, wi->pos_x + wi->current_x - 1, y + 8,
+ _button_colour_table[!!value_future][_local_company == this->owner], value_future ? FR_LOWERED : FR_NONE);
+
+ this->DrawWidgetString(SSW_WIDGET_SETTINGS, y, str);
+ this->DrawBoolValue(y, value_current, false);
+ this->DrawBoolValue(y, value_future, true);
+ y += this->line_height;
+ }
+
+ /**
+ * Should the text line given by the parameter be drawn on de screen? (depends on scrolling)
+ * Parameter 'line' is passed by reference, and always incremented by one.
+ * @param Line the line to draw or not to draw.
+ * @return True if the line is to be drawn.
+ */
+ inline bool ShouldDrawLine(uint &line)
+ {
+ return this->vscroll.IsVisible(line++);
+ }
+
+ /**
+ * Handle a click on a boolean option.
+ * @param type The vehicle type that was clicked on.
+ */
+ void HandleOptionClick(VehicleType type)
+ {
+ DoCommandP(0, MODIFY_SHARING_OPTION | (type << 8), !this->company->sharing_future.IsSharingAllowed(type), CMD_CHANGE_SHARING_SETTING | CMD_MSG(STR_ERROR_CAN_T_CHANGE_SHARING_SETTING));
+ }
+
+ /**
+ * Handle a click on one of the fees.
+ * @param type The vehicle type for which the fee is to be adjusted.
+ * @param x The x-coordinate of the click, relative to the left edge of the button.
+ * This is used to determine whether the left or right button was clicked.
+ */
+ void HandleFeeClick(VehicleType type, int x)
+ {
+ if ((this->flags4 & WF_TIMEOUT_MASK) > WF_TIMEOUT_TRIGGER) {
+ _left_button_clicked = false;
+ return;
+ }
+ int half_button_width = this->GetWidget(SSW_WIDGET_BUTTONS)->current_x / 2;
+
+ uint16 value = this->company->sharing_future.GetSharingFee(type);
+ uint16 new_value = value;
+ if (x < half_button_width && value > 0) {
+ /* left button */
+ this->clicked_row = type;
+ this->clicked_side = 1;
+ new_value = value - min(10, value);
+ } else if (x >= half_button_width && value < MAX_SHARING_FEE) {
+ /* right button */
+ this->clicked_row = type;
+ this->clicked_side = 2;
+ new_value = value + min(10, MAX_SHARING_FEE - value);
+ }
+ if (new_value != value) {
+ DoCommandP(0, MODIFY_SHARING_FEE | (type << 8), new_value, CMD_CHANGE_SHARING_SETTING | CMD_MSG(STR_ERROR_CAN_T_CHANGE_SHARING_SETTING));
+ this->flags4 |= WF_TIMEOUT_BEGIN;
+ _left_button_clicked = false;
+ }
+ }
+
+ /**
+ * Compute the total number of items (text lines) in the window
+ */
+ void ComputeScrollCount()
+ {
+ byte num_scroll;
+ if (this->companies_tab) {
+ num_scroll = SSWL_COMPANY_FIRST;
+ Company *c;
+ FOR_ALL_COMPANIES(c) {
+ if (this->company != c) num_scroll++;
+ }
+ } else {
+ num_scroll = SSWL_END;
+ }
+ if (this->company->sharing_delay > 0) {
+ num_scroll += 2; // 1 blank line and 1 line for the delay
+ }
+ this->vscroll.SetCount(num_scroll);
+ }
+
+ /**
+ * Compute the number of items (text lines) that fit in the window
+ */
+ void ComputeScrollCapacity()
+ {
+ this->vscroll.SetCapacity((this->GetWidget(SSW_WIDGET_PANEL)->current_y - 2) / this->line_height);
+ /* Make sure the size is exactly correct */
+ assert(this->GetWidget(SSW_WIDGET_PANEL)->current_y == (this->vscroll.GetCapacity() * this->line_height + 2));
+ }
+
+public:
+ /** Constructor */
+ SharingSettingsWindow(const WindowDesc *desc, WindowNumber number) : Window()
+ {
+ this->company = Company::GetIfValid(number);
+ assert(this->company != NULL);
+
+ this->InitNested(desc, number);
+
+ /* Set this->owner now, because otherwise InitNested overrides this value
+ * This means we cannot use this variable for anything (including size computations!) in the initialization stage */
+ this->owner = (Owner)number;
+
+ this->ComputeScrollCount();
+ this->ComputeScrollCapacity();
+
+ this->DisableWidget(SSW_WIDGET_OPTIONS);
+ }
+
+ virtual void OnPaint()
+ {
+ this->DrawWidgets();
+
+ CompanySharingSettingsEntry *future = &this->company->sharing_future;
+ CompanySharingSettingsEntry *current = &this->company->sharing_current;
+
+ uint y = this->GetWidget(SSW_WIDGET_PANEL)->pos_y + 1;
+ uint current_line = SSWL_HEADERS;
+
+ /* draw text headers */
+ if (this->ShouldDrawLine(current_line)) {
+ this->DrawWidgetString(SSW_WIDGET_CURRENT, y, STR_SHARING_CURRENT);
+ this->DrawWidgetString(SSW_WIDGET_FUTURE, y, STR_SHARING_FUTURE);
+ y += this->line_height;
+ }
+
+ if (this->companies_tab) {
+ Company *c;
+ FOR_ALL_COMPANIES(c) {
+ if (this->company == c) continue; //Skip our own company, as it would be silly to allow / disallow yourself.
+ if (this->ShouldDrawLine(current_line)) {
+ DrawCompanyIcon(c->index, this->GetWidget(SSW_WIDGET_ICONS)->pos_x + 2, y + 1);
+ SetDParam(0, c->index);
+ this->DrawSharingOption(STR_SHARING_SETTING_COMPANY, y, current->IsCompanyAllowed(c->index), future->IsCompanyAllowed(c->index));
+ }
+ }
+ if (current_line == SSWL_COMPANY_FIRST) {
+ /* it seems there are no companies at all, apart from ourselves */
+ if (this->ShouldDrawLine(current_line)) {
+ this->DrawWidgetString(SSW_WIDGET_SETTINGS, y, STR_SHARING_SETTING_NO_COMPANIES);
+ return; //don't draw the sharing delay
+ }
+ } else if (this->ShouldDrawLine(current_line)) {
+ /* Blank line */
+ y += this->line_height;
+ }
+ } else {
+ /* draw text and buttons for all vehicle types */
+ VehicleType type;
+ FOR_ALL_SHARING_VEHTYPES(type) {
+ SubSprite sub = {0, (this->vscroll.GetPosition() - current_line) * this->line_height, INT32_MAX, (this->vscroll.GetPosition() + this->vscroll.GetCapacity() - current_line) * this->line_height - 1};
+ DrawSprite(SPR_IMG_TRAINLIST + type, PAL_NONE, this->GetWidget(SSW_WIDGET_ICONS)->pos_x, y, &sub);
+
+ if (this->ShouldDrawLine(current_line)) {
+ this->DrawSharingOption(STR_SHARING_SETTING_RAIL + type, y,
+ current->IsSharingAllowed(type),
+ future->IsSharingAllowed(type));
+ }
+
+ /* sharing fee */
+ if (this->ShouldDrawLine(current_line)) {
+ /* Draw arrow buttons in the correct state */
+ bool l_clickable = this->owner == _local_company && future->GetSharingFee(type) > 0;
+ bool r_clickable = this->owner == _local_company && future->GetSharingFee(type) < MAX_SHARING_FEE;
+ byte arrow_clicked = (type == this->clicked_row) ? this->clicked_side : 0;
+ if ((arrow_clicked == 1 && !l_clickable) || (arrow_clicked == 2 && !r_clickable)) {
+ arrow_clicked = 0;
+ }
+ DrawArrowButtons(this->GetWidget(SSW_WIDGET_BUTTONS)->pos_x, y, COLOUR_YELLOW, arrow_clicked, l_clickable, r_clickable);
+
+ /* Draw the fee strings */
+ this->DrawWidgetString(SSW_WIDGET_SETTINGS, y, type == VEH_TRAIN ? STR_SHARING_SETTING_MONTHLY_TOLL : STR_SHARING_SETTING_FEE, TC_BLACK);
+ SetDParam(0, current->GetSharingFee(type));
+ this->DrawWidgetString(SSW_WIDGET_CURRENT, y, STR_JUST_CURRENCY, TC_ORANGE);
+ SetDParam(0, future->GetSharingFee(type));
+ this->DrawWidgetString(SSW_WIDGET_FUTURE, y, STR_JUST_CURRENCY, TC_ORANGE);
+ y += this->line_height;
+ }
+ /* Add some spacing */
+ if (this->ShouldDrawLine(current_line)) y += this->line_height;
+ }
+ }
+
+ /* draw sharing delay */
+ if (this->ShouldDrawLine(current_line) && this->company->sharing_delay > 0) {
+ this->DrawWidgetString(SSW_WIDGET_SETTINGS, y, STR_SHARING_CHANGES_TAKE_EFFECT);
+ SetDParam(0, this->company->sharing_delay);
+ this->DrawWidgetString(SSW_WIDGET_CURRENT, y, STR_SHARING_TAKE_EFFECT_MONTHS);
+ }
+ }
+
+ virtual void SetStringParameters(int widget) const
+ {
+ if (widget == SSW_WIDGET_CAPTION) SetDParam(0, this->company->index);
+ }
+
+ virtual void UpdateWidgetSize(int widget, Dimension *size, const Dimension &padding, Dimension *resize)
+ {
+ Dimension d = {0, 0};
+ switch (widget) {
+ case SSW_WIDGET_ICONS:
+ VehicleType type;
+ FOR_ALL_SHARING_VEHTYPES(type) {
+ d = maxdim(d, GetSpriteSize(SPR_IMG_TRAINLIST + type));
+ }
+ break;
+ case SSW_WIDGET_SETTINGS:
+ this->line_height = max(11, FONT_HEIGHT_NORMAL + 1); //minimal line height is 11
+ resize->height = this->line_height;
+
+ d = maxdim(GetStringBoundingBox(STR_SHARING_SETTING_MONTHLY_TOLL), GetStringBoundingBox(STR_SHARING_SETTING_FEE));
+ d = maxdim(d, GetStringBoundingBox(STR_SHARING_SETTING_NO_COMPANIES));
+ d = maxdim(d, GetStringBoundingBox(STR_SHARING_CHANGES_TAKE_EFFECT));
+ for (CompanyID c = COMPANY_FIRST; c < MAX_COMPANIES; c++) {
+ if (Company::IsValidID(c) && c != this->company->index) {
+ SetDParam(0, c);
+ d = maxdim(d, GetStringBoundingBox(STR_SHARING_SETTING_COMPANY));
+ }
+ }
+ /* make the minimal height a multiple of line_height ( + 2 pixels of spacing) */
+ {
+ uint num_lines = (size->height - 1) / this->line_height + 1; //round upwards
+ d.height = num_lines * this->line_height + 2;
+ }
+ break;
+ case SSW_WIDGET_CURRENT:
+ case SSW_WIDGET_FUTURE:
+ d = maxdim(GetStringBoundingBox(STR_SHARING_VALUE_ALLOWED), GetStringBoundingBox(STR_SHARING_VALUE_NOT_ALLOWED));
+ SetDParam(0, MAX_SHARING_FEE);
+ d = maxdim(d, GetStringBoundingBox(STR_JUST_CURRENCY));
+ d = maxdim(d, GetStringBoundingBox((widget == SSW_WIDGET_CURRENT) ? STR_SHARING_CURRENT : STR_SHARING_FUTURE));
+ break;
+ default: return;
+ }
+ *size = maxdim(*size, d);
+ }
+
+ virtual void OnClick(Point pt, int widget)
+ {
+ if (widget == SSW_WIDGET_OPTIONS || widget == SSW_WIDGET_COMPANIES) {
+ this->companies_tab = (widget == SSW_WIDGET_COMPANIES);
+ this->SetWidgetDisabledState(SSW_WIDGET_OPTIONS, !this->companies_tab);
+ this->SetWidgetDisabledState(SSW_WIDGET_COMPANIES, this->companies_tab);
+ this->ComputeScrollCount();
+ this->SetDirty();
+ return;
+ }
+ if (widget != SSW_WIDGET_BUTTONS && widget != SSW_WIDGET_FUTURE) return;
+ if (this->owner != _local_company) return;
+
+ int y = pt.y - this->GetWidget(SSW_WIDGET_BUTTONS)->pos_y - 1;
+ /* don't allow clicking on the bottommost or topmost pixel of a line */
+ if (y <= 0 || y % this->line_height == 0 || y % this->line_height == this->line_height - 1) return;
+ int line = (y / this->line_height) + this->vscroll.GetPosition();
+
+ if (widget == SSW_WIDGET_FUTURE) {
+ /* Possibly clicked a fee, if that is the case, show a query string window. */
+ switch (line) {
+ case SSWL_RAIL_FEE: this->current_query_type = VEH_TRAIN; break;
+ case SSWL_ROAD_FEE: this->current_query_type = VEH_ROAD; break;
+ case SSWL_WATER_FEE: this->current_query_type = VEH_SHIP; break;
+ case SSWL_AIR_FEE: this->current_query_type = VEH_AIRCRAFT; break;
+ default: return;
+ }
+ SetDParam(0, this->company->sharing_future.GetSharingFee(this->current_query_type) * _currency->rate);
+ ShowQueryString(STR_JUST_INT, STR_SHARING_CHANGE_FEE_CAPTION, 10, 0, this, CS_NUMERAL, QSF_NONE);
+ } else if (this->companies_tab) { //else: SSW_WIDGET_BUTTONS
+ line -= SSWL_COMPANY_FIRST;
+ /* Find out which company button we clicked on */
+ Company *c;
+ FOR_ALL_COMPANIES(c) {
+ if (c == this->company) continue;
+ if (line-- == 0) {
+ DoCommandP(0, MODIFY_SHARING_COMPANIES | (c->index << 8), !this->company->sharing_future.IsCompanyAllowed(c->index), CMD_CHANGE_SHARING_SETTING | CMD_MSG(STR_ERROR_CAN_T_CHANGE_SHARING_SETTING));
+ break;
+ }
+ }
+ } else {
+ int x = pt.x - this->GetWidget(SSW_WIDGET_BUTTONS)->pos_x;
+ switch (line) {
+ case SSWL_RAIL_OPTION: this->HandleOptionClick(VEH_TRAIN); return;
+ case SSWL_RAIL_FEE: this->HandleFeeClick(VEH_TRAIN, x); return;
+ case SSWL_ROAD_OPTION: this->HandleOptionClick(VEH_ROAD); return;
+ case SSWL_ROAD_FEE: this->HandleFeeClick(VEH_ROAD, x); return;
+ case SSWL_WATER_OPTION: this->HandleOptionClick(VEH_SHIP); return;
+ case SSWL_WATER_FEE: this->HandleFeeClick(VEH_SHIP, x); return;
+ case SSWL_AIR_OPTION: this->HandleOptionClick(VEH_AIRCRAFT); return;
+ case SSWL_AIR_FEE: this->HandleFeeClick(VEH_AIRCRAFT, x); return;
+ default: return;
+ }
+ }
+ }
+
+ virtual void OnInvalidateData(int data = 0)
+ {
+ this->ComputeScrollCount();
+ }
+
+ virtual void OnResize()
+ {
+ this->ComputeScrollCapacity();
+ }
+
+ virtual void OnQueryTextFinished(char *str)
+ {
+ if (StrEmpty(str)) return;
+ int32 value = atoi(str);
+ value /= _currency->rate;
+ value = Clamp(value, 0, MAX_SHARING_FEE);
+ DoCommandP(0, MODIFY_SHARING_FEE | (this->current_query_type << 8), value, CMD_CHANGE_SHARING_SETTING | CMD_MSG(STR_ERROR_CAN_T_CHANGE_SHARING_SETTING));
+ }
+
+ virtual void OnTimeout()
+ {
+ this->clicked_row = 0;
+ this->clicked_side = 0;
+ this->SetDirty();
+ }
+};
+
+static const NWidgetPart _nested_sharing_settings_widgets[] = {
+ NWidget(NWID_HORIZONTAL), // Window header
+ NWidget(WWT_CLOSEBOX, COLOUR_GREY, SSW_WIDGET_CLOSEBOX), SetFill(false, true),
+ NWidget(WWT_CAPTION, COLOUR_GREY, SSW_WIDGET_CAPTION), SetDataTip(STR_SHARING_CAPTION, STR_TOOLTIP_WINDOW_TITLE_DRAG_THIS),
+ NWidget(WWT_STICKYBOX, COLOUR_GREY, SSW_WIDGET_STICKY), SetFill(false, true),
+ EndContainer(),
+ NWidget(NWID_HORIZONTAL),
+ NWidget(WWT_PANEL, COLOUR_GREY, SSW_WIDGET_PANEL), SetDataTip(0x0, STR_SHARING_PANEL_TOOLTIP), SetFill(true, true),
+ NWidget(NWID_HORIZONTAL), SetPIP(2, 2, 2),
+ /* Empty widgets to position the text columns */
+ NWidget(WWT_EMPTY, COLOUR_GREY, SSW_WIDGET_ICONS), SetMinimalSize(20, 90), SetResize(0, 1),
+ NWidget(WWT_EMPTY, COLOUR_GREY, SSW_WIDGET_SETTINGS), SetMinimalSize(160, 90), SetResize(1, 1),
+ NWidget(WWT_EMPTY, COLOUR_GREY, SSW_WIDGET_CURRENT), SetMinimalSize(80, 90), SetResize(1, 1),
+ NWidget(WWT_EMPTY, COLOUR_GREY, SSW_WIDGET_BUTTONS), SetMinimalSize(20, 90), SetResize(0, 1), SetPadding(0, 2, 0, 2),
+ NWidget(WWT_EMPTY, COLOUR_GREY, SSW_WIDGET_FUTURE), SetMinimalSize(80, 90), SetResize(1, 1),
+ EndContainer(),
+ EndContainer(),
+ NWidget(WWT_SCROLLBAR, COLOUR_GREY, SSW_WIDGET_SCROLL),
+ EndContainer(),
+ NWidget(NWID_HORIZONTAL),
+ NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, SSW_WIDGET_OPTIONS), SetMinimalSize(0, 12), SetResize(1, 0),
+ SetDataTip(STR_SHARING_OPTIONS, STR_SHARING_OPTIONS_TOOLTIP), SetFill(true, false),
+ NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, SSW_WIDGET_COMPANIES), SetMinimalSize(0, 12), SetResize(1, 0),
+ SetDataTip(STR_SHARING_COMPANIES, STR_SHARING_COMPANIES_TOOLTIP), SetFill(true, false),
+ NWidget(WWT_RESIZEBOX, COLOUR_GREY, SSW_WIDGET_RESIZE), SetFill(false, true),
+ EndContainer(),
+};
+
+static const WindowDesc _sharing_settings_desc(
+ WDP_AUTO, WDP_AUTO, 388, 215,
+ WC_SHARING_OPTIONS, WC_COMPANY,
+ WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET | WDF_UNCLICK_BUTTONS | WDF_STICKY_BUTTON | WDF_RESIZABLE,
+ _nested_sharing_settings_widgets, lengthof(_nested_sharing_settings_widgets)
+);
+
+/**
+ * Show the sharing window of a company.
+ * @param company The company whose window to show.
+ */
+static void ShowCompanySharingSettings(CompanyID company)
+{
+ if ((_settings_game.sharing.sharing_mode == SHARING_INDIVIDUAL) && Company::IsValidID(company)) {
+ AllocateWindowDescFront(&_sharing_settings_desc, company);
+ }
+}
Index: src/news_gui.cpp
===================================================================
--- src/news_gui.cpp (revision 18190)
+++ src/news_gui.cpp (working copy)
@@ -238,6 +238,7 @@
{ NT_COMPANY_INFO, NF_NONE, &_company_news_desc }, ///< NS_COMPANY_MERGER
{ NT_COMPANY_INFO, NF_NONE, &_company_news_desc }, ///< NS_COMPANY_BANKRUPT
{ NT_COMPANY_INFO, NF_NONE, &_company_news_desc }, ///< NS_COMPANY_NEW
+ { NT_COMPANY_INFO, NF_NONE, &_company_news_desc }, ///< NS_COMPANY_SHARING
{ NT_INDUSTRY_OPEN, (NF_NO_TRANSPARENT | NF_SHADE), &_thin_news_desc }, ///< NS_INDUSTRY_OPEN
{ NT_INDUSTRY_CLOSE, (NF_NO_TRANSPARENT | NF_SHADE), &_thin_news_desc }, ///< NS_INDUSTRY_CLOSE
{ NT_ECONOMY, NF_NONE, &_normal_news_desc }, ///< NS_ECONOMY
@@ -311,6 +312,10 @@
this->GetWidget(NTW_TITLE)->widget_data = STR_NEWS_COMPANY_LAUNCH_TITLE;
break;
+ case NS_COMPANY_SHARING:
+ this->GetWidget(NTW_TITLE)->widget_data = STR_NEWS_SHARING_TITLE;
+ break;
+
default:
break;
}
@@ -526,6 +531,12 @@
SetDParam(1, this->ni->params[3]);
return STR_NEWS_COMPANY_LAUNCH_DESCRIPTION;
+ case NS_COMPANY_SHARING:
+ SetDParam(0, ni->params[2]);
+ SetDParam(1, ni->params[3]);
+ return ni->params[1];
+ break;
+
default:
NOT_REACHED();
}
Index: src/vehicle_gui.cpp
===================================================================
--- src/vehicle_gui.cpp (revision 18190)
+++ src/vehicle_gui.cpp (working copy)
@@ -40,6 +40,7 @@
#include "articulated_vehicles.h"
#include "cargotype.h"
#include "spritecache.h"
+#include "infrastructure_func.h"
#include "table/sprites.h"
#include "table/strings.h"
@@ -1870,6 +1871,7 @@
{
const Vehicle *v = Vehicle::Get(this->window_number);
bool is_localcompany = v->owner == _local_company;
+ bool can_control = IsVehicleControlAllowed(v);
bool refitable_and_stopped_in_depot = IsVehicleRefitable(v);
this->SetWidgetDisabledState(VVW_WIDGET_GOTO_DEPOT, !is_localcompany);
@@ -1877,8 +1879,8 @@
this->SetWidgetDisabledState(VVW_WIDGET_CLONE_VEH, !is_localcompany);
if (v->type == VEH_TRAIN) {
- this->SetWidgetDisabledState(VVW_WIDGET_FORCE_PROCEED, !is_localcompany);
- this->SetWidgetDisabledState(VVW_WIDGET_TURN_AROUND, !is_localcompany);
+ this->SetWidgetDisabledState(VVW_WIDGET_FORCE_PROCEED, !can_control);
+ this->SetWidgetDisabledState(VVW_WIDGET_TURN_AROUND, !can_control);
}
this->DrawWidgets();
Index: src/saveload/saveload.cpp
===================================================================
--- src/saveload/saveload.cpp (revision 18190)
+++ src/saveload/saveload.cpp (working copy)
@@ -47,8 +47,9 @@
#include "saveload_internal.h"
-extern const uint16 SAVEGAME_VERSION = 127;
+extern const uint16 SAVEGAME_VERSION = SL_IS;
+
SavegameType _savegame_type; ///< type of savegame we are loading
uint32 _ttdp_version; ///< version of TTDP savegame (if applicable)
Index: src/saveload/company_sl.cpp
===================================================================
--- src/saveload/company_sl.cpp (revision 18190)
+++ src/saveload/company_sl.cpp (working copy)
@@ -123,6 +123,8 @@
SLE_VAR(Company, num_valid_stat_ent, SLE_UINT8),
+ SLE_CONDVAR(Company, sharing_delay, SLE_UINT8, SL_IS, SL_MAX_VERSION),
+
SLE_VAR(Company, quarters_of_bankruptcy,SLE_UINT8),
SLE_CONDVAR(Company, bankrupt_asked, SLE_FILE_U8 | SLE_VAR_U16, 0, 103),
SLE_CONDVAR(Company, bankrupt_asked, SLE_UINT16, 104, SL_MAX_VERSION),
@@ -132,7 +134,8 @@
/* yearly expenses was changed to 64-bit in savegame version 2. */
SLE_CONDARR(Company, yearly_expenses, SLE_FILE_I32 | SLE_VAR_I64, 3 * 13, 0, 1),
- SLE_CONDARR(Company, yearly_expenses, SLE_INT64, 3 * 13, 2, SL_MAX_VERSION),
+ SLE_CONDARR(Company, yearly_expenses, SLE_INT64, 3 * 13, 2, SL_IS - 1),
+ SLE_CONDARR(Company, yearly_expenses, SLE_INT64, 3 * 15, SL_IS, SL_MAX_VERSION),
SLE_CONDVAR(Company, is_ai, SLE_BOOL, 2, SL_MAX_VERSION),
SLE_CONDNULL(1, 107, 111), ///< is_noai
@@ -226,6 +229,13 @@
SLE_END()
};
+static const SaveLoad _company_sharing_desc[] = {
+ SLE_CONDVAR(CompanySharingSettingsEntry, allowed_types, SLE_UINT8, SL_IS, SL_MAX_VERSION),
+ SLE_CONDVAR(CompanySharingSettingsEntry, allowed_companies, SLE_UINT16, SL_IS, SL_MAX_VERSION),
+ SLE_CONDARR(CompanySharingSettingsEntry, fee, SLE_UINT16, 4, SL_IS, SL_MAX_VERSION),
+ SLE_END()
+};
+
static void SaveLoad_PLYR(Company *c)
{
int i;
@@ -243,6 +253,10 @@
}
}
+ /* Write sharing settings */
+ SlObject(&c->sharing_current, _company_sharing_desc);
+ SlObject(&c->sharing_future, _company_sharing_desc);
+
/* Write economy */
SlObject(&c->cur_economy, _company_economy_desc);
Index: src/saveload/afterload.cpp
===================================================================
--- src/saveload/afterload.cpp (revision 18190)
+++ src/saveload/afterload.cpp (working copy)
@@ -1956,6 +1956,33 @@
}
}
+ if (CheckSavegameVersion(SL_IS)) {
+ /* Set the sharing settings to the standard values (no sharing)
+ * as we do not want these to be copied from the newgame settings. */
+ _settings_game.sharing.sharing_mode = SHARING_NONE;
+ _settings_game.sharing.new_individual_delay = 12;
+ _settings_game.sharing.rail_sharing = false;
+ _settings_game.sharing.road_sharing = false;
+ _settings_game.sharing.water_sharing = false;
+ _settings_game.sharing.air_sharing = false;
+ _settings_game.sharing.rail_fee = 100;
+ _settings_game.sharing.road_fee = 100;
+ _settings_game.sharing.water_fee = 100;
+ _settings_game.sharing.air_fee = 100;
+
+ 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);
+ }
+ }
+
AfterLoadLabelMaps();
GamelogPrintDebug(1);
Index: src/saveload/saveload.h
===================================================================
--- src/saveload/saveload.h (revision 18190)
+++ src/saveload/saveload.h (working copy)
@@ -80,6 +80,7 @@
};
#define SL_MAX_VERSION 255
+#define SL_IS 128
enum {
INC_VEHICLE_COMMON = 0,
Index: src/yapf/yapf_costrail.hpp
===================================================================
--- src/yapf/yapf_costrail.hpp (revision 18190)
+++ src/yapf/yapf_costrail.hpp (working copy)
@@ -34,6 +34,7 @@
Trackdir td;
TileType tile_type;
RailType rail_type;
+ Owner owner;
TILE()
{
@@ -41,6 +42,7 @@
td = INVALID_TRACKDIR;
tile_type = MP_VOID;
rail_type = INVALID_RAILTYPE;
+ owner = INVALID_OWNER;
}
TILE(TileIndex tile, Trackdir td)
@@ -49,6 +51,7 @@
this->td = td;
this->tile_type = GetTileType(tile);
this->rail_type = GetTileRailType(tile);
+ this->owner = GetTileOwner(tile);
}
TILE(const TILE &src)
@@ -57,6 +60,7 @@
td = src.td;
tile_type = src.tile_type;
rail_type = src.rail_type;
+ owner = src.owner;
}
};
@@ -450,10 +454,15 @@
if (!tf_local.Follow(cur.tile, cur.td)) {
assert(tf_local.m_err != TrackFollower::EC_NONE);
/* Can't move to the next tile (EOL?). */
- if (tf_local.m_err == TrackFollower::EC_RAIL_TYPE) {
- end_segment_reason |= ESRB_RAIL_TYPE;
- } else {
- end_segment_reason |= ESRB_DEAD_END;
+ switch (tf_local.m_err) {
+ case TrackFollower::EC_RAIL_TYPE:
+ end_segment_reason |= ESRB_RAIL_TYPE;
+ break;
+ case TrackFollower::EC_OWNER:
+ end_segment_reason |= ESRB_OWNER;
+ break;
+ default:
+ end_segment_reason |= ESRB_DEAD_END;
}
if (TrackFollower::DoTrackMasking() && !HasOnewaySignalBlockingTrackdir(cur.tile, cur.td)) {
@@ -484,6 +493,13 @@
break;
}
+ /* Check the next tile for the owner. */
+ if (next.owner != cur.owner) {
+ /* Segment must consist from tiles with the same owner. */
+ end_segment_reason |= ESRB_OWNER;
+ break;
+ }
+
/* Avoid infinite looping. */
if (next.tile == n.m_key.m_tile && next.td == n.m_key.m_td) {
end_segment_reason |= ESRB_INFINITE_LOOP;
Index: src/yapf/yapf_node_rail.hpp
===================================================================
--- src/yapf/yapf_node_rail.hpp (revision 18190)
+++ src/yapf/yapf_node_rail.hpp (working copy)
@@ -66,6 +66,7 @@
/* The following reasons can be saved into cached segment */
ESR_DEAD_END = 0, ///< track ends here
ESR_RAIL_TYPE, ///< the next tile has a different rail type than our tiles
+ ESR_OWNER, ///< the next tile has a different owner than our tiles
ESR_INFINITE_LOOP, ///< infinite loop detected
ESR_SEGMENT_TOO_LONG, ///< the segment is too long (possible infinite loop)
ESR_CHOICE_FOLLOWS, ///< the next tile contains a choice (the track splits to more than one segments)
@@ -90,6 +91,7 @@
ESRB_DEAD_END = 1 << ESR_DEAD_END,
ESRB_RAIL_TYPE = 1 << ESR_RAIL_TYPE,
+ ESRB_OWNER = 1 << ESR_OWNER,
ESRB_INFINITE_LOOP = 1 << ESR_INFINITE_LOOP,
ESRB_SEGMENT_TOO_LONG = 1 << ESR_SEGMENT_TOO_LONG,
ESRB_CHOICE_FOLLOWS = 1 << ESR_CHOICE_FOLLOWS,
@@ -109,7 +111,7 @@
ESRB_POSSIBLE_TARGET = ESRB_DEPOT | ESRB_WAYPOINT | ESRB_STATION | ESRB_SAFE_TILE,
/* What reasons can be stored back into cached segment. */
- ESRB_CACHED_MASK = ESRB_DEAD_END | ESRB_RAIL_TYPE | ESRB_INFINITE_LOOP | ESRB_SEGMENT_TOO_LONG | ESRB_CHOICE_FOLLOWS | ESRB_DEPOT | ESRB_WAYPOINT | ESRB_STATION | ESRB_SAFE_TILE,
+ ESRB_CACHED_MASK = ESRB_DEAD_END | ESRB_RAIL_TYPE | ESRB_OWNER | ESRB_INFINITE_LOOP | ESRB_SEGMENT_TOO_LONG | ESRB_CHOICE_FOLLOWS | ESRB_DEPOT | ESRB_WAYPOINT | ESRB_STATION | ESRB_SAFE_TILE,
/* Reasons to abort pathfinding in this direction. */
ESRB_ABORT_PF_MASK = ESRB_DEAD_END | ESRB_PATH_TOO_LONG | ESRB_INFINITE_LOOP | ESRB_FIRST_TWO_WAY_RED,
@@ -120,7 +122,7 @@
inline CStrA ValueStr(EndSegmentReasonBits bits)
{
static const char * const end_segment_reason_names[] = {
- "DEAD_END", "RAIL_TYPE", "INFINITE_LOOP", "SEGMENT_TOO_LONG", "CHOICE_FOLLOWS",
+ "DEAD_END", "RAIL_TYPE", "OWNER", "INFINITE_LOOP", "SEGMENT_TOO_LONG", "CHOICE_FOLLOWS",
"DEPOT", "WAYPOINT", "STATION", "SAFE_TILE",
"PATH_TOO_LONG", "FIRST_TWO_WAY_RED", "LOOK_AHEAD_END", "TARGET_REACHED"
};
Index: src/yapf/follow_track.hpp
===================================================================
--- src/yapf/follow_track.hpp (revision 18190)
+++ src/yapf/follow_track.hpp (working copy)
@@ -16,6 +16,7 @@
#include "../depot_map.h"
#include "../roadveh.h"
#include "../train.h"
+#include "../infrastructure_func.h"
/** Track follower helper template class (can serve pathfinders and vehicle
* controllers). See 6 different typedefs below for 3 different transport
@@ -62,12 +63,12 @@
{
assert(!IsRailTT() || (v != NULL && v->type == VEH_TRAIN));
m_veh = v;
- Init(v != NULL ? v->owner : INVALID_OWNER, IsRailTT() && railtype_override == INVALID_RAILTYPES ? Train::From(v)->compatible_railtypes : railtype_override, pPerf);
+ Init(v != NULL ? v->owner : INVALID_OWNER, (IsRailTT() && railtype_override == INVALID_RAILTYPES && v != NULL) ? Train::From(v)->compatible_railtypes : railtype_override, pPerf);
}
FORCEINLINE void Init(Owner o, RailTypes railtype_override, CPerformanceTimer *pPerf)
{
- assert((!IsRoadTT() || m_veh != NULL) && (!IsRailTT() || railtype_override != INVALID_RAILTYPES));
+ assert(!IsRoadTT() || m_veh != NULL);
m_veh_owner = o;
m_pPerf = pPerf;
/* don't worry, all is inlined so compiler should remove unnecessary initializations */
@@ -272,6 +273,7 @@
/** return true if we can enter m_new_tile from m_exitdir */
FORCEINLINE bool CanEnterNewTile()
{
+ bool ignore_owner = (m_veh_owner == INVALID_OWNER);
if (IsRoadTT() && IsStandardRoadStopTile(m_new_tile)) {
/* road stop can be entered from one direction only unless it's a drive-through stop */
DiagDirection exitdir = GetRoadStopDir(m_new_tile);
@@ -279,6 +281,11 @@
m_err = EC_NO_WAY;
return false;
}
+ /* road stops shouldn't be entered unless allowed to */
+ if (!(ignore_owner || IsInfraUsageAllowed(m_new_tile, m_veh_owner, VEH_ROAD))) {
+ m_err = EC_OWNER;
+ return false;
+ }
}
/* single tram bits can only be entered from one direction */
@@ -297,8 +304,8 @@
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 companies' depots, unless allowed to */
+ if (!(ignore_owner || IsInfraUsageAllowed(m_new_tile, m_veh_owner, VEH_ROAD))) {
m_err = EC_OWNER;
return false;
}
@@ -309,18 +316,23 @@
m_err = EC_NO_WAY;
return false;
}
+ if (!(ignore_owner || IsInfraUsageAllowed(m_new_tile, m_veh_owner, VEH_TRAIN))) {
+ m_err = EC_OWNER;
+ return false;
+ }
}
- /* 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 tiles where the vehicle is allowed */
+ if (IsRailTT() && !(ignore_owner || IsInfraUsageAllowed(m_new_tile, m_veh_owner, VEH_TRAIN))) {
/* different owner */
- m_err = EC_NO_WAY;
+ m_err = EC_OWNER;
return false;
}
/* rail transport is possible only on compatible rail types */
if (IsRailTT()) {
RailType rail_type = GetTileRailType(m_new_tile);
+ /* If INVALID_RAILTYPES is specified, all bits are set and this test is always passed */
if (!HasBit(m_railtypes, rail_type)) {
/* incompatible rail type */
m_err = EC_RAIL_TYPE;
Index: src/roadveh_cmd.cpp
===================================================================
--- src/roadveh_cmd.cpp (revision 18190)
+++ src/roadveh_cmd.cpp (working copy)
@@ -37,6 +37,7 @@
#include "roadstop_base.h"
#include "cargotype.h"
#include "spritecache.h"
+#include "infrastructure_func.h"
#include "table/strings.h"
#include "table/sprites.h"
@@ -203,7 +204,7 @@
/* The ai_new queries the vehicle cost before building the route,
* so we must check against cheaters no sooner than now. --pasky */
if (!IsRoadDepotTile(tile)) return CMD_ERROR;
- if (!IsTileOwner(tile, _current_company)) return CMD_ERROR;
+ if (!IsInfraUsageAllowed(tile, _current_company, VEH_ROAD)) return CMD_ERROR;
if (HasTileRoadType(tile, ROADTYPE_TRAM) != HasBit(e->info.misc_flags, EF_ROAD_TRAM)) return_cmd_error(STR_ERROR_DEPOT_WRONG_DEPOT_TYPE);
@@ -382,7 +383,7 @@
tile += TileOffsByDiagDir(_road_pf_directions[trackdir]);
if (IsRoadDepotTile(tile) &&
- IsTileOwner(tile, rfdd->owner) &&
+ IsInfraUsageAllowed(tile, rfdd->owner, VEH_ROAD) &&
length < rfdd->best_length) {
rfdd->best_length = length;
rfdd->tile = tile;
@@ -479,7 +480,7 @@
CommandCost CmdTurnRoadVeh(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text)
{
RoadVehicle *v = RoadVehicle::GetIfValid(p1);
- if (v == NULL || !CheckOwnership(v->owner)) return CMD_ERROR;
+ if (v == NULL || !IsVehicleControlAllowed(v)) return CMD_ERROR;
if ((v->vehstatus & VS_STOPPED) ||
(v->vehstatus & VS_CRASHED) ||
@@ -1043,14 +1044,14 @@
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) && (!IsInfraUsageAllowed(tile, v) || 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 (!IsInfraUsageAllowed(tile, v) || GetRoadStopDir(tile) == enterdir || v->HasArticulatedPart()) {
/* different station owner or wrong orientation or the vehicle has articulated parts */
trackdirs = TRACKDIR_BIT_NONE;
} else {
@@ -1654,7 +1655,7 @@
_road_veh_data_1[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) &&
+ IsInfraUsageAllowed(v->tile, v) &&
GetRoadStopType(v->tile) == (IsCargoInClass(v->cargo_type, CC_PASSENGERS) ? ROADSTOP_BUS : ROADSTOP_TRUCK) &&
v->frame == RVC_DRIVE_THROUGH_STOP_FRAME))) {
Index: src/economy_type.h
===================================================================
--- src/economy_type.h (revision 18190)
+++ src/economy_type.h (working copy)
@@ -136,6 +136,8 @@
EXPENSES_SHIP_INC,
EXPENSES_LOAN_INT,
EXPENSES_OTHER,
+ EXPENSES_SHARED_RUN,
+ EXPENSES_SHARED_INC,
EXPENSES_END,
INVALID_EXPENSES = 0xFF,
};
Index: src/pbs.cpp
===================================================================
--- src/pbs.cpp (revision 18190)
+++ src/pbs.cpp (working copy)
@@ -242,7 +242,7 @@
if (v->type != VEH_TRAIN || (v->vehstatus & VS_CRASHED)) return NULL;
Train *t = Train::From(v);
- if (HasBit((TrackBits)t->track, TrackdirToTrack(info->res.trackdir))) {
+ if (HasBit((TrackBits)t->track, TrackdirToTrack(info->res.trackdir)) || t->track == TRACK_BIT_WORMHOLE) {
t = t->First();
/* ALWAYS return the lowest ID (anti-desync!) */
@@ -277,6 +277,40 @@
}
/**
+ * Find a train on a reservation in a single direction.
+ * @param tile The reserved tile.
+ * @param trackdir The reserved trackdir to start searching.
+ * @param owner The owner of the vehicle. If no owner is known yet, specify INVALID_OWNER.
+ * @param rts The railtypes the vehicle can use. If not known yet, specify INVALID_RAILTYPES.
+ * @return The vehicle this reservation (possibly) belongs to.
+ */
+static Train *FindTrainForReservationDir(TileIndex tile, Trackdir trackdir, Owner owner, RailTypes rts)
+{
+ FindTrainOnTrackInfo ftoti;
+ /* Ignore one-way signals, as we might be searching the wrong way */
+ ftoti.res = FollowReservation(owner, rts, tile, trackdir, true);
+
+ FindVehicleOnPos(ftoti.res.tile, &ftoti, FindTrainOnTrackEnum);
+ if (ftoti.best != NULL) return ftoti.best;
+
+ /* Special case for stations: check the whole platform for a vehicle. */
+ if (HasStationTileRail(ftoti.res.tile)) {
+ TileIndexDiff diff = TileOffsByDiagDir(TrackdirToExitdir(ReverseTrackdir(ftoti.res.trackdir)));
+ for (TileIndex st_tile = ftoti.res.tile + diff; IsCompatibleTrainStationTile(st_tile, ftoti.res.tile); st_tile += diff) {
+ FindVehicleOnPos(st_tile, &ftoti, FindTrainOnTrackEnum);
+ if (ftoti.best != NULL) return ftoti.best;
+ }
+ }
+
+ /* Special case for bridges/tunnels: check the other end as well. */
+ if (IsTileType(ftoti.res.tile, MP_TUNNELBRIDGE)) {
+ FindVehicleOnPos(GetOtherTunnelBridgeEnd(ftoti.res.tile), &ftoti, FindTrainOnTrackEnum);
+ if (ftoti.best != NULL) return ftoti.best;
+ }
+ return NULL;
+}
+
+/**
* Find the train which has reserved a specific path.
*
* @param tile A tile on the path.
@@ -288,32 +322,23 @@
assert(HasReservedTracks(tile, TrackToTrackBits(track)));
Trackdir trackdir = TrackToTrackdir(track);
- RailTypes rts = GetRailTypeInfo(GetTileRailType(tile))->compatible_railtypes;
-
/* Follow the path from tile to both ends, one of the end tiles should
- * have a train on it. We need FollowReservation to ignore one-way signals
- * here, as one of the two search directions will be the "wrong" way. */
+ * have a train on it. Since we don't know the vehicle yet,
+ * we don't know which tiles it can access with regards to ownership and infrastructure sharing,
+ * nor do we know which rail types it is compatible with.
+ * Therefore we first do a test run without ownership or railtype checks.
+ * If this yields a vehicle, we test again using the now-known owner and railtype info.
+ * If this still yields a vehicle, we return it as it is the correct one.
+ */
for (int i = 0; i < 2; ++i, trackdir = ReverseTrackdir(trackdir)) {
- FindTrainOnTrackInfo ftoti;
- ftoti.res = FollowReservation(GetTileOwner(tile), rts, tile, trackdir, true);
-
- FindVehicleOnPos(ftoti.res.tile, &ftoti, FindTrainOnTrackEnum);
- if (ftoti.best != NULL) return ftoti.best;
-
- /* Special case for stations: check the whole platform for a vehicle. */
- if (IsRailStationTile(ftoti.res.tile)) {
- TileIndexDiff diff = TileOffsByDiagDir(TrackdirToExitdir(ReverseTrackdir(ftoti.res.trackdir)));
- for (TileIndex st_tile = ftoti.res.tile + diff; IsCompatibleTrainStationTile(st_tile, ftoti.res.tile); st_tile += diff) {
- FindVehicleOnPos(st_tile, &ftoti, FindTrainOnTrackEnum);
- if (ftoti.best != NULL) return ftoti.best;
- }
+ Train *t = FindTrainForReservationDir(tile, trackdir, INVALID_OWNER, INVALID_RAILTYPES);
+ if (t == NULL) continue;
+ RailTypes rts = GetRailTypeInfo(t->railtype)->compatible_railtypes;
+ t = FindTrainForReservationDir(tile, trackdir, t->owner, rts);
+ /* Also verify that the train can actally use the start tile */
+ if (t != NULL && IsInfraUsageAllowed(tile, t) && HasBit(rts, GetTileRailType(tile))) {
+ return t;
}
-
- /* Special case for bridges/tunnels: check the other end as well. */
- if (IsTileType(ftoti.res.tile, MP_TUNNELBRIDGE)) {
- FindVehicleOnPos(GetOtherTunnelBridgeEnd(ftoti.res.tile), &ftoti, FindTrainOnTrackEnum);
- if (ftoti.best != NULL) return ftoti.best;
- }
}
return NULL;
Index: src/signal.cpp
===================================================================
--- src/signal.cpp (revision 18190)
+++ src/signal.cpp (working copy)
@@ -16,6 +16,7 @@
#include "vehicle_func.h"
#include "functions.h"
#include "train.h"
+#include "infrastructure_func.h"
/** these are the maximums used for updating signal blocks */
@@ -278,7 +279,7 @@
switch (GetTileType(tile)) {
case MP_RAILWAY: {
- if (GetTileOwner(tile) != owner) continue; // do not propagate signals on others' tiles (remove for tracksharing)
+ if (!AreSignalBlocksJoined(owner, GetTileOwner(tile))) continue; // check whether tile should be part of the same block, owner-wise
if (IsRailDepot(tile)) {
if (enterdir == INVALID_DIAGDIR) { // from 'inside' - train just entered or left the depot
@@ -351,7 +352,7 @@
case MP_STATION:
if (!HasStationRail(tile)) continue;
- if (GetTileOwner(tile) != owner) continue;
+ if (!AreSignalBlocksJoined(owner, GetTileOwner(tile))) continue;
if (DiagDirToAxis(enterdir) != GetRailStationAxis(tile)) continue; // different axis
if (IsStationTileBlocked(tile)) continue; // 'eye-candy' station tile
@@ -361,7 +362,7 @@
case MP_ROAD:
if (!IsLevelCrossing(tile)) continue;
- if (GetTileOwner(tile) != owner) continue;
+ if (!AreSignalBlocksJoined(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 @@
break;
case MP_TUNNELBRIDGE: {
- if (GetTileOwner(tile) != owner) continue;
+ if (!AreSignalBlocksJoined(owner, GetTileOwner(tile))) continue;
if (GetTunnelBridgeTransportType(tile) != TRANSPORT_RAIL) continue;
DiagDirection dir = GetTunnelBridgeDirection(tile);
Index: src/vehicle.cpp
===================================================================
--- src/vehicle.cpp (revision 18190)
+++ src/vehicle.cpp (working copy)
@@ -45,6 +45,7 @@
#include "network/network.h"
#include "core/pool_func.hpp"
#include "economy_base.h"
+#include "infrastructure_func.h"
#include "table/sprites.h"
#include "table/strings.h"
@@ -1507,6 +1508,9 @@
case OT_LOADING: {
uint wait_time = max(this->current_order.wait_time - this->lateness_counter, 0);
+ /* Pay the loading fee when using someone else's station */
+ 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) ||
(_settings_game.order.timetabling && this->current_order_time < wait_time)) return;
Index: src/infrastructure.h
===================================================================
--- src/infrastructure.h (revision 0)
+++ src/infrastructure.h (revision 0)
@@ -0,0 +1,149 @@
+/* $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.h Define struct and enums for (shared) infrastructure */
+
+#ifndef INFRASTRUCTURE_H
+#define INFRASTRUCTURE_H
+
+#include "stdafx.h"
+#include "core/bitmath_func.hpp"
+#include "vehicle_type.h"
+#include "company_type.h"
+
+/** Sharing modes; possible values for the sharing_mode setting. */
+enum SharingMode {
+ SHARING_NONE, ///< No infrastructure sharing at all
+ SHARING_GLOBAL, ///< Sharing enabled with global (advanced) settings
+ SHARING_INDIVIDUAL, ///< Sharing enabled with individually configurable settings
+};
+
+/** which part of the sharing settings do we want to modify? */
+enum ModifySharingFlag {
+ MODIFY_SHARING_OPTION, ///< Modify access to station / depot / tracks
+ MODIFY_SHARING_FEE, ///< Modify the sharing fee
+ MODIFY_SHARING_COMPANIES, ///< Modify which companies are allowed
+};
+
+/** Some constants regarding the time between two (similar) news messages */
+enum {
+ SHARING_NEWS_INTERVAL_INSOLVENCY = 60, ///< 60 days between two warnings about company XX not paying because of debts
+ SHARING_NEWS_INTERVAL_SETTING_CHANGE = 10, ///< 10 days between two messages about company XX announcing to change its sharing settings
+};
+
+/** maximum fee (in pounds) for any of the sharing fees */
+static const uint MAX_SHARING_FEE = 50000;
+
+/** number of relevant vehicle types */
+static const byte NUM_SHARING_VEHTYPES = 4;
+
+/** Bitmask of all relevant vehicle types. */
+static const byte ALL_SHARING_VEHTYPES = (1 << VEH_TRAIN) | (1 << VEH_ROAD) | (1 << VEH_SHIP) | (1 << VEH_AIRCRAFT);
+
+/** Struct to store all the sharing settings of a company (either current or future ones) */
+struct CompanySharingSettingsEntry {
+ byte allowed_types; ///< Bitmask of vehicle types for which sharing is enabled.
+ CompanyMask allowed_companies; ///< Bitmask of companies for which sharing is enabled.
+ uint16 fee[NUM_SHARING_VEHTYPES]; ///< Fees for rail/road/water/air.
+
+ /** Constructor */
+ CompanySharingSettingsEntry() {}
+
+ /**
+ * Compare two sets of sharing settings with eachother.
+ * @param o The sharing settings entry to compare with.
+ * @return True if all settings are equal, false otherwise.
+ */
+ bool Equals(const CompanySharingSettingsEntry *o) const;
+
+ /**
+ * Copy the data of a specified sharing settings entry to this one.
+ * @param o The sharing settings entry to use as source.
+ * @return a bitmask of vehicle types whose permissions were reduced.
+ */
+ byte Copy(const CompanySharingSettingsEntry *o);
+
+ /**
+ * Fill this struct with the values from _settings_game.
+ * This also enables all companies in the allowed_companies mask.
+ * @return a bitmask of vehicle types whose permissions were reduced.
+ */
+ byte CopyFromGameSettings();
+
+ /**
+ * Is sharing allowed for a vehicle type.
+ * @param type The vehicle type.
+ * @return True if infrastructure sharing is enabled for that vehicle type.
+ */
+ FORCEINLINE bool IsSharingAllowed(VehicleType type) const
+ {
+ assert(type < NUM_SHARING_VEHTYPES);
+ return HasBit(this->allowed_types, type);
+ }
+
+ /**
+ * Check whether a company is allowed to use the infrastructure.
+ * i.e. it's corresponding bit in allowed_companies is set.
+ * @param c The company to check.
+ * @return true if the given company is allowed to use the infrastructure
+ */
+ FORCEINLINE bool IsCompanyAllowed(CompanyID c) const
+ {
+ assert(c < MAX_COMPANIES);
+ return HasBit(this->allowed_companies, c);
+ }
+
+ /**
+ * Get the sharing fee for a specified vehicle type.
+ * @param type The vehicle type.
+ * @return The sharing fee for the given vehicle type.
+ */
+ FORCEINLINE uint16 GetSharingFee(VehicleType type) const
+ {
+ assert(type < NUM_SHARING_VEHTYPES);
+ return this->fee[type];
+ }
+
+ /**
+ * Allow or disallow sharing for a vehicle type.
+ * @param type The vehicle type.
+ * @param value Allow sharing if true, disallow if false.
+ */
+ FORCEINLINE void SetSharingAllowed(VehicleType type, bool value)
+ {
+ assert(type < NUM_SHARING_VEHTYPES);
+ SB(this->allowed_types, type, 1, !!value);
+ }
+
+ /**
+ * Allow or disallow a company to use the infrastructure.
+ * @param c The index of the company to allow or disallow.
+ * @param value Allow sharing if true, disallow if false.
+ */
+ FORCEINLINE void SetCompanyAllowed(CompanyID c, bool value)
+ {
+ assert(c < MAX_COMPANIES);
+ SB(this->allowed_companies, c, 1, !!value);
+ }
+
+ /**
+ * Set the sharing fee for a specified vehicle type.
+ * @param type The vehicle type.
+ * @param value The new sharing fee.
+ */
+ FORCEINLINE void SetSharingFee(VehicleType type, uint16 value)
+ {
+ assert(type < NUM_SHARING_VEHTYPES);
+ this->fee[type] = value;
+ }
+};
+
+#define FOR_ALL_SHARING_VEHTYPES(var) for (var = VEH_TRAIN; var < NUM_SHARING_VEHTYPES; var++)
+
+#endif /* INFRASTRUCTURE_H */
Index: src/infrastructure_func.h
===================================================================
--- src/infrastructure_func.h (revision 0)
+++ src/infrastructure_func.h (revision 0)
@@ -0,0 +1,109 @@
+/* $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 infrastructure sharing */
+
+#ifndef INFRASTRUCTURE_FUNC_H
+#define INFRASTRUCTURE_FUNC_H
+
+#include "infrastructure.h"
+#include "functions.h"
+#include "company_base.h"
+#include "vehicle_base.h"
+
+void PayStationSharingFee(Vehicle *v, const Station *st);
+void PayDailyTrackSharingFee(Train *v);
+void ProcessSharingChanges(bool update_signals = false);
+void AddSharingNewsItem(StringID string, Company *c, uint32 param = 0);
+
+/**
+ * Get whether a vehicle of a given owner and type can use the infrastrucutre of a company.
+ * This function has three other versions with slightly different parameters.
+ * All functions basically work the same. Possible parameter variations:
+ * - A tile instead of the infrastructure owner. The infrastructure owner is then equal to GetTileOwner(tile).
+ * - A vehicle instead of a vehicle owner and vehicle type. Use the owner and type of the vehicle.
+ * @param infra_owner The owner of the infrastructure.
+ * @param veh_owner Owner of the vehicle in question.
+ * @param type Type of vehicle we are talking about.
+ * @return True if sharing is enabled, false otherwise.
+ */
+static inline bool IsInfraUsageAllowed(Owner infra_owner, Owner veh_owner, VehicleType type)
+{
+ if (infra_owner == veh_owner || infra_owner == OWNER_NONE) return true;
+ assert(Company::IsValidID(infra_owner));
+ Company *c = Company::Get(infra_owner);
+ return c->sharing_current.IsSharingAllowed(type) && c->sharing_current.IsCompanyAllowed(veh_owner);
+}
+
+/** @see IsInfraUsageAllowed(Owner, Owner, VehicleType) */
+static FORCEINLINE bool IsInfraUsageAllowed(Owner owner, const Vehicle *v)
+{
+ return IsInfraUsageAllowed(owner, v->owner, v->type);
+}
+
+/** @see IsInfraUsageAllowed(Owner, Owner, VehicleType) */
+static FORCEINLINE bool IsInfraUsageAllowed(TileIndex tile, Owner veh_owner, VehicleType type)
+{
+ return IsInfraUsageAllowed(GetTileOwner(tile), veh_owner, type);
+}
+
+/** @see IsInfraUsageAllowed(Owner, Owner, VehicleType) */
+static FORCEINLINE bool IsInfraUsageAllowed(TileIndex tile, const Vehicle *v)
+{
+ return IsInfraUsageAllowed(GetTileOwner(tile), v->owner, v->type);
+}
+
+/**
+ * Get the sharing fee the infrastructure owner asks for using the infrastructure for a vehicle type.
+ * @param owner The owner of the infrastructure.
+ * @param type The vehicle type.
+ * @return The sharing fee that applies here.
+ */
+static inline uint16 GetSharingFee(Owner owner, VehicleType type)
+{
+ assert(Company::IsValidID(owner));
+ return Company::Get(owner)->sharing_current.GetSharingFee(type);
+}
+
+
+/**
+ * Check whether a given company can control this vehicle.
+ * The vehicle owner can always control a vehicle.
+ * Trains on foreign tracks can also be controlled by the track owner.
+ * Controlling a train means permission to start, stop or reverse it or to make it ignore signals.
+ * This function has one alias, which does not take a company as parameter. Instead it uses _current_company.
+ * This is very useful in DoCommands, since it uses Check[Tile]Ownership. (It gives nice error messages when failing)
+ * @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));
+}
+
+/** @see IsVehicleControlAllowed(const Vehicle *, Owner) */
+static inline bool IsVehicleControlAllowed(const Vehicle *v)
+{
+ return CheckOwnership(v->owner) || (v->type == VEH_TRAIN && CheckTileOwnership(v->tile));
+}
+
+/**
+ * Are the tracks of two owners part of the same signal block?
+ * This is the case if the owners are equal, or if infrastructure sharing is enabled
+ * @param o1 First company.
+ * @param o2 Second company.
+ * @return true if the tracks of the given owners are part of the same signal block
+ */
+static inline bool AreSignalBlocksJoined(Owner o1, Owner o2)
+{
+ return (o1 == o2) || (_settings_game.sharing.sharing_mode != SHARING_NONE);
+}
+
+#endif /* INFRASTRUCTURE_FUNC_H */
Index: src/aircraft_cmd.cpp
===================================================================
--- src/aircraft_cmd.cpp (revision 18190)
+++ src/aircraft_cmd.cpp (working copy)
@@ -35,6 +35,7 @@
#include "effectvehicle_func.h"
#include "station_base.h"
#include "cargotype.h"
+#include "infrastructure_func.h"
#include "table/strings.h"
#include "table/sprites.h"
@@ -113,7 +114,7 @@
const AircraftVehicleInfo *avi = AircraftVehInfo(v->engine_type);
FOR_ALL_STATIONS(st) {
- if (st->owner != v->owner || !(st->facilities & FACIL_AIRPORT)) continue;
+ if (!(st->facilities & FACIL_AIRPORT) || !IsInfraUsageAllowed(st->owner, v)) continue;
const AirportFTAClass *afc = st->Airport();
if (afc->nof_depots == 0 || (
@@ -144,7 +145,7 @@
FOR_VEHICLE_ORDERS(v, order) {
const Station *st = Station::Get(order->station);
- if (st->owner == v->owner && (st->facilities & FACIL_AIRPORT)) {
+ if (IsInfraUsageAllowed(st->owner, v) && (st->facilities & FACIL_AIRPORT)) {
/* If an airport doesn't have a hangar, skip it */
if (st->Airport()->nof_depots != 0)
return true;
@@ -247,7 +248,7 @@
/* to just query the cost, it is not neccessary to have a valid tile (automation/AI) */
if (flags & DC_QUERY_COST) return value;
- if (!IsHangarTile(tile) || !IsTileOwner(tile, _current_company)) return CMD_ERROR;
+ if (!IsHangarTile(tile) || !IsInfraUsageAllowed(tile, _current_company, VEH_AIRCRAFT)) return CMD_ERROR;
/* Prevent building aircraft types at places which can't handle them */
if (!CanVehicleUseStation(p1, Station::GetByTile(tile))) return CMD_ERROR;
@@ -1565,7 +1566,7 @@
/* runway busy or not allowed to use this airstation, circle */
if ((apc->flags & (v->subtype == AIR_HELICOPTER ? AirportFTAClass::HELICOPTERS : AirportFTAClass::AIRPLANES)) &&
st->airport_tile != INVALID_TILE &&
- (st->owner == OWNER_NONE || st->owner == v->owner)) {
+ IsInfraUsageAllowed(st->owner, v)) {
/* {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 */
Index: projects/openttd_vs80.vcproj
===================================================================
--- projects/openttd_vs80.vcproj (revision 18190)
+++ projects/openttd_vs80.vcproj (working copy)
@@ -588,6 +588,10 @@
>
+
+
@@ -1088,6 +1092,14 @@
>
+
+
+
+
Index: projects/openttd_vs90.vcproj
===================================================================
--- projects/openttd_vs90.vcproj (revision 18190)
+++ projects/openttd_vs90.vcproj (working copy)
@@ -585,6 +585,10 @@
>
+
+
@@ -1085,6 +1089,14 @@
>
+
+
+
+
Index: source.list
===================================================================
--- source.list (revision 18190)
+++ source.list (working copy)
@@ -32,6 +32,7 @@
gfxinit.cpp
heightmap.cpp
highscore.cpp
+infrastructure.cpp
ini.cpp
landscape.cpp
map.cpp
@@ -181,6 +182,8 @@
industry.h
industry_type.h
industrytype.h
+infrastructure.h
+infrastructure_func.h
ini_type.h
landscape.h
landscape_type.h