diff --git a/src/departures.cpp b/src/departures.cpp index 83876f2878..8f7d7d20ff 100644 --- a/src/departures.cpp +++ b/src/departures.cpp @@ -111,7 +111,7 @@ DepartureList* MakeDepartureList(StationID station, bool show_vehicle_types[5], continue; } - /* MAX_COMPANIES is probably the wrong thing to put here, but it works. GenerateVehicleSortList doesn't check the company when the type of list is VL_STATION_LIST (r20801). */ + /* MAX_COMPANIES is probably the wrong thing to put here, but it works. GenerateVehicleSortList doesn't check the company when the type of list is VL_STATION_LIST (r20801). */ if (!GenerateVehicleSortList(&vehicles, VehicleListIdentifier(VL_STATION_LIST, (VehicleType)(VEH_TRAIN + i), MAX_COMPANIES, station).Pack())) { /* Something went wrong: panic! */ return result; @@ -138,7 +138,7 @@ DepartureList* MakeDepartureList(StationID station, bool show_vehicle_types[5], } const Order *order = (*v)->GetOrder((*v)->cur_implicit_order_index % (*v)->GetNumOrders()); - DateTicks start_date = (DateTicks)_date_fract - (*v)->current_order_time; + DateTicks start_date = (DateTicks)_date_fract - ((*v)->current_order_time / _settings_game.economy.day_length_factor); DepartureStatus status = D_TRAVELLING; /* If the vehicle is stopped in a depot, ignore it. */ @@ -417,45 +417,45 @@ DepartureList* MakeDepartureList(StationID station, bool show_vehicle_types[5], if (found_terminus) { /* Add the departure to the result list. */ - bool duplicate = false; + bool duplicate = false; - if (_settings_client.gui.departure_merge_identical) { - for (uint i = 0; i < result->Length(); ++i) { - if (*d == **(result->Get(i))) { - duplicate = true; - break; - } - } - } + if (_settings_client.gui.departure_merge_identical) { + for (uint i = 0; i < result->Length(); ++i) { + if (*d == **(result->Get(i))) { + duplicate = true; + break; + } + } + } - if (!duplicate) { - *(result->Append(1)) = d; + if (!duplicate) { + *(result->Append(1)) = d; - if (_settings_client.gui.departure_smart_terminus && type == D_DEPARTURE) { - for (uint i = 0; i < result->Length()-1; ++i) { - Departure *d_first = *(result->Get(i)); - uint k = d_first->calling_at.Length()-2; - for (uint j = d->calling_at.Length(); j > 0; --j) { - CallAt c = CallAt(*(d->calling_at.Get(j-1))); + if (_settings_client.gui.departure_smart_terminus && type == D_DEPARTURE) { + for (uint i = 0; i < result->Length()-1; ++i) { + Departure *d_first = *(result->Get(i)); + uint k = d_first->calling_at.Length()-2; + for (uint j = d->calling_at.Length(); j > 0; --j) { + CallAt c = CallAt(*(d->calling_at.Get(j-1))); - if (d_first->terminus >= c && d_first->calling_at.Length() >= 2) { - d_first->terminus = CallAt(*(d_first->calling_at.Get(k))); + if (d_first->terminus >= c && d_first->calling_at.Length() >= 2) { + d_first->terminus = CallAt(*(d_first->calling_at.Get(k))); - if (k == 0) break; + if (k == 0) break; - k--; - } - } - } - } + k--; + } + } + } + } - /* If the vehicle is expected to be late, we want to know what time it will arrive rather than depart. */ - /* This is done because it looked silly to me to have a vehicle not be expected for another few days, yet it be at the same time pulling into the station. */ - if (d->status != D_ARRIVED && - d->lateness > 0) { - d->lateness -= least_order->order->GetWaitTime(); - } - } + /* If the vehicle is expected to be late, we want to know what time it will arrive rather than depart. */ + /* This is done because it looked silly to me to have a vehicle not be expected for another few days, yet it be at the same time pulling into the station. */ + if (d->status != D_ARRIVED && + d->lateness > 0) { + d->lateness -= least_order->order->GetWaitTime(); + } + } } } else { /* Computing arrivals: */ @@ -521,20 +521,20 @@ DepartureList* MakeDepartureList(StationID station, bool show_vehicle_types[5], d->terminus = CallAt((StationID)candidate_origin->GetDestination()); if (found_origin) { - bool duplicate = false; + bool duplicate = false; - if (_settings_client.gui.departure_merge_identical) { - for (uint i = 0; i < result->Length(); ++i) { - if (*d == **(result->Get(i))) { - duplicate = true; - break; - } - } - } + if (_settings_client.gui.departure_merge_identical) { + for (uint i = 0; i < result->Length(); ++i) { + if (*d == **(result->Get(i))) { + duplicate = true; + break; + } + } + } - if (!duplicate) { - *(result->Append(1)) = d; - } + if (!duplicate) { + *(result->Append(1)) = d; + } } } @@ -639,6 +639,22 @@ DepartureList* MakeDepartureList(StationID station, bool show_vehicle_types[5], } } + /* Avoid leaking OrderDate structs */ + for (uint i = 0; i < next_orders.Length(); ++i) { + OrderDate *od = *(next_orders.Get(i)); + delete od; + } + + uint8 day_len = _settings_game.economy.day_length_factor; + if (day_len > 1) { + DateTicks now = ((DateTicks)_date * DAY_TICKS) + _date_fract; + for (size_t i = 0; i < result->Length(); i++) { + Departure *dep = (*result)[i]; + dep->scheduled_date = now + (dep->scheduled_date - now) / day_len; + dep->lateness /= day_len; + } + } + /* Done. Phew! */ return result; } diff --git a/src/departures_type.h b/src/departures_type.h index cb06b144ff..b92509463c 100644 --- a/src/departures_type.h +++ b/src/departures_type.h @@ -80,20 +80,20 @@ typedef struct Departure { calling_at.Reset(); } - inline bool operator==(const Departure& d) const { - if (this->calling_at.Length() != d.calling_at.Length()) return false; + inline bool operator==(const Departure& d) const { + if (this->calling_at.Length() != d.calling_at.Length()) return false; - for (uint i = 0; i < this->calling_at.Length(); ++i) { - if (*(this->calling_at.Get(i)) != *(d.calling_at.Get(i))) return false; - } + for (uint i = 0; i < this->calling_at.Length(); ++i) { + if (*(this->calling_at.Get(i)) != *(d.calling_at.Get(i))) return false; + } - return - (this->scheduled_date / DATE_UNIT_SIZE) == (d.scheduled_date / DATE_UNIT_SIZE) && - this->vehicle->type == d.vehicle->type && - this->via == d.via && - this->type == d.type - ; - } + return + (this->scheduled_date / DATE_UNIT_SIZE) == (d.scheduled_date / DATE_UNIT_SIZE) && + this->vehicle->type == d.vehicle->type && + this->via == d.via && + this->type == d.type + ; + } } Departure; typedef SmallVector DepartureList; diff --git a/src/dock_gui.cpp b/src/dock_gui.cpp index 448a0f8963..fd2c20d24e 100644 --- a/src/dock_gui.cpp +++ b/src/dock_gui.cpp @@ -101,6 +101,7 @@ struct BuildDocksToolbarWindow : Window { this->InitNested(window_number); this->OnInvalidateData(); if (_settings_client.gui.link_terraform_toolbar) ShowTerraformToolbar(this); + SetWidgetDisabledState(WID_DT_RIVER, !_settings_game.construction.enable_build_river); } ~BuildDocksToolbarWindow() @@ -160,7 +161,7 @@ struct BuildDocksToolbarWindow : Window { break; case WID_DT_RIVER: // Build river button (in scenario editor) - if (_game_mode != GM_EDITOR) return; + if (_game_mode != GM_EDITOR && !_settings_game.construction.enable_build_river) return; HandlePlacePushButton(this, WID_DT_RIVER, SPR_CURSOR_RIVER, HT_RECT); break; @@ -324,6 +325,7 @@ static const NWidgetPart _nested_build_docks_toolbar_widgets[] = { NWidget(WWT_IMGBTN, COLOUR_DARK_GREEN, WID_DT_DEPOT), SetMinimalSize(22, 22), SetFill(0, 1), SetDataTip(SPR_IMG_SHIP_DEPOT, STR_WATERWAYS_TOOLBAR_BUILD_DEPOT_TOOLTIP), NWidget(WWT_IMGBTN, COLOUR_DARK_GREEN, WID_DT_STATION), SetMinimalSize(22, 22), SetFill(0, 1), SetDataTip(SPR_IMG_SHIP_DOCK, STR_WATERWAYS_TOOLBAR_BUILD_DOCK_TOOLTIP), NWidget(WWT_IMGBTN, COLOUR_DARK_GREEN, WID_DT_BUOY), SetMinimalSize(22, 22), SetFill(0, 1), SetDataTip(SPR_IMG_BUOY, STR_WATERWAYS_TOOLBAR_BUOY_TOOLTIP), + NWidget(WWT_IMGBTN, COLOUR_DARK_GREEN, WID_DT_RIVER), SetMinimalSize(22, 22), SetFill(0, 1), SetDataTip(SPR_IMG_BUILD_RIVER, STR_WATERWAYS_TOOLBAR_CREATE_RIVER_TOOLTIP), NWidget(WWT_IMGBTN, COLOUR_DARK_GREEN, WID_DT_BUILD_AQUEDUCT), SetMinimalSize(23, 22), SetFill(0, 1), SetDataTip(SPR_IMG_AQUEDUCT, STR_WATERWAYS_TOOLBAR_BUILD_AQUEDUCT_TOOLTIP), EndContainer(), }; diff --git a/src/ini.cpp b/src/ini.cpp index ccce192393..905ebfacf8 100644 --- a/src/ini.cpp +++ b/src/ini.cpp @@ -16,7 +16,6 @@ #include "fileio_func.h" #if (defined(_POSIX_C_SOURCE) && _POSIX_C_SOURCE >= 199309L) || (defined(_XOPEN_SOURCE) && _XOPEN_SOURCE >= 500) -# define WITH_FDATASYNC # include #endif @@ -80,7 +79,7 @@ bool IniFile::SaveToDisk(const char *filename) * APIs to do so. We only need to flush the data as the metadata itself * (modification date etc.) is not important to us; only the real data is. */ -#ifdef WITH_FDATASYNC +#if defined(_POSIX_SYNCHRONIZED_IO) && _POSIX_SYNCHRONIZED_IO > 0 int ret = fdatasync(fileno(f)); fclose(f); if (ret != 0) return false; diff --git a/src/lang/dutch.txt b/src/lang/dutch.txt index c051f4fdba..947f0736c8 100644 --- a/src/lang/dutch.txt +++ b/src/lang/dutch.txt @@ -30,7 +30,7 @@ STR_CARGO_PLURAL_NOTHING : STR_CARGO_PLURAL_PASSENGERS :Passagiers STR_CARGO_PLURAL_COAL :Kolen STR_CARGO_PLURAL_MAIL :Post -STR_CARGO_PLURAL_OIL :Olie +STR_CARGO_PLURAL_OIL :Aardolie STR_CARGO_PLURAL_LIVESTOCK :Vee STR_CARGO_PLURAL_GOODS :Goederen STR_CARGO_PLURAL_GRAIN :Graan @@ -64,7 +64,7 @@ STR_CARGO_SINGULAR_NOTHING : STR_CARGO_SINGULAR_PASSENGER :Passagier STR_CARGO_SINGULAR_COAL :Kolen STR_CARGO_SINGULAR_MAIL :Post -STR_CARGO_SINGULAR_OIL :Olie +STR_CARGO_SINGULAR_OIL :Aardolie STR_CARGO_SINGULAR_LIVESTOCK :Vee STR_CARGO_SINGULAR_GOODS :Goederen STR_CARGO_SINGULAR_GRAIN :Graan @@ -98,7 +98,7 @@ STR_QUANTITY_NOTHING : STR_QUANTITY_PASSENGERS :{COMMA}{NBSP}passagier{P "" s} STR_QUANTITY_COAL :{WEIGHT_LONG} kolen STR_QUANTITY_MAIL :{COMMA}{NBSP}zak{P "" ken} post -STR_QUANTITY_OIL :{VOLUME_LONG} olie +STR_QUANTITY_OIL :{VOLUME_LONG} Vaten Olie STR_QUANTITY_LIVESTOCK :{COMMA}{NBSP}stuk{P "" s} vee STR_QUANTITY_GOODS :{COMMA}{NBSP}krat{P "" ten} goederen STR_QUANTITY_GRAIN :{WEIGHT_LONG} graan @@ -168,7 +168,7 @@ STR_ABBREV_ALL :{TINY_FONT}ALLE STR_PASSENGERS :{COMMA}{NBSP}passagier{P "" s} STR_BAGS :{COMMA}{NBSP}zak{P "" ken} STR_TONS :{COMMA}{NBSP}ton -STR_LITERS :{COMMA}{NBSP}liter +STR_LITERS :{COMMA}{NBSP}liter{P "" s} STR_ITEMS :{COMMA}{NBSP}stuk{P "" s} STR_CRATES :{COMMA}{NBSP}krat{P "" ten} @@ -4357,7 +4357,7 @@ STR_ERROR_CAN_T_PURCHASE_THIS_LAND :{WHITE}Kan dit STR_ERROR_YOU_ALREADY_OWN_IT :{WHITE}... het is al van jou! # Group related errors -STR_ERROR_GROUP_CAN_T_CREATE :{WHITE}Kan groep niet maken... +STR_ERROR_GROUP_CAN_T_CREATE :{WHITE}Kan groep niet creëren... STR_ERROR_GROUP_CAN_T_DELETE :{WHITE}Kan deze groep niet verwijderen... STR_ERROR_GROUP_CAN_T_RENAME :{WHITE}Kan deze groep niet hernoemen... STR_ERROR_GROUP_CAN_T_SET_PARENT :{WHITE}Kan huidige groep niet instellen... diff --git a/src/lang/english.txt b/src/lang/english.txt index b81cec8f15..03cdcef7ad 100644 --- a/src/lang/english.txt +++ b/src/lang/english.txt @@ -1467,6 +1467,9 @@ STR_CONFIG_SETTING_PERSISTENT_BUILDINGTOOLS_HELPTEXT :Keep the buildi STR_CONFIG_SETTING_EXPENSES_LAYOUT :Group expenses in company finance window: {STRING2} STR_CONFIG_SETTING_EXPENSES_LAYOUT_HELPTEXT :Define the layout for the company expenses window +STR_CONFIG_SETTING_ENABLE_BUILD_RIVER :Enable building rivers: {STRING2} +STR_CONFIG_SETTING_ENABLE_BUILD_RIVER_HELPTEXT :Enable building rivers outside of the scenario editor + STR_CONFIG_SETTING_SOUND_TICKER :News ticker: {STRING2} STR_CONFIG_SETTING_SOUND_TICKER_HELPTEXT :Play sound for summarised news messages STR_CONFIG_SETTING_SOUND_NEWS :Newspaper: {STRING2} @@ -1640,6 +1643,11 @@ STR_CONFIG_SETTING_EXTRA_TREE_PLACEMENT_NONE :None {RED}(brea STR_CONFIG_SETTING_EXTRA_TREE_PLACEMENT_RAINFOREST :Only in rain forests STR_CONFIG_SETTING_EXTRA_TREE_PLACEMENT_ALL :Everywhere +STR_CONFIG_SETTING_TREES_AROUND_SNOWLINE :Adjusted arctic tree placement: {STRING2} +STR_CONFIG_SETTING_TREES_AROUND_SNOWLINE_HELPTEXT :Adjust placement of trees around snow line in artic climate. Trees thin out above snowline. Trees are a mix of arctic and temperate just below snowline. Below that trees are temperate. +STR_CONFIG_SETTING_TREES_AROUND_SNOWLINE_RANGE :Arctic tree range: {STRING2} +STR_CONFIG_SETTING_TREES_AROUND_SNOWLINE_RANGE_HELPTEXT :Approximate range of arctic trees around snow line + STR_CONFIG_SETTING_TOOLBAR_POS :Position of main toolbar: {STRING2} STR_CONFIG_SETTING_TOOLBAR_POS_HELPTEXT :Horizontal position of the main toolbar at the top of the screen STR_CONFIG_SETTING_STATUSBAR_POS :Position of status bar: {STRING2} @@ -1763,6 +1771,7 @@ STR_CONFIG_SETTING_ENVIRONMENT_AUTHORITIES :{ORANGE}Authori STR_CONFIG_SETTING_ENVIRONMENT_TOWNS :{ORANGE}Towns STR_CONFIG_SETTING_ENVIRONMENT_INDUSTRIES :{ORANGE}Industries STR_CONFIG_SETTING_ENVIRONMENT_CARGODIST :{ORANGE}Cargo distribution +STR_CONFIG_SETTING_ENVIRONMENT_TREES :{ORANGE}Trees STR_CONFIG_SETTING_AI :{ORANGE}Competitors STR_CONFIG_SETTING_AI_NPC :{ORANGE}Computer players STR_CONFIG_SETTING_VIEWPORT_MAP_OPTIONS :{ORANGE}Map mode @@ -4209,6 +4218,18 @@ STR_ORDER_CONDITIONAL_AGE :Age (years) STR_ORDER_CONDITIONAL_REQUIRES_SERVICE :Requires service STR_ORDER_CONDITIONAL_UNCONDITIONALLY :Always STR_ORDER_CONDITIONAL_REMAINING_LIFETIME :Remaining lifetime (years) +STR_ORDER_CONDITIONAL_CARGO_WAITING :Waiting cargo +STR_ORDER_CONDITIONAL_ACCEPTANCE_DROPDOWN :Accepted cargo +STR_ORDER_CONDITIONAL_FREE_PLATFORMS :Free platforms +STR_ORDER_CONDITIONAL_PERCENT :Percent of times + +STR_ORDER_CONDITIONAL_REQUIRES_SERVICE_ORDER :Requires service {STRING} +STR_ORDER_CONDITIONAL_CARGO_WAITING_ORDER :Next station {STRING} {STRING} waiting +STR_ORDER_CONDITIONAL_ACCEPTANCE :Next station {STRING} {STRING} +STR_CONDITIONAL_FREE_PLATFORMS :Jump to order {COMMA} when Next station {STRING} {COMMA} free platform{P "" s} +STR_CONDITIONAL_PERCENT :Jump to order {COMMA} {COMMA} percent of times + +STR_ORDER_CONDITIONAL_NEXT_STATION :Next station STR_ORDER_CONDITIONAL_COMPARATOR_TOOLTIP :{BLACK}How to compare the vehicle data to the given value STR_ORDER_CONDITIONAL_COMPARATOR_EQUALS :is equal to @@ -4221,8 +4242,18 @@ STR_ORDER_CONDITIONAL_COMPARATOR_IS_TRUE :is true STR_ORDER_CONDITIONAL_COMPARATOR_IS_FALSE :is false STR_ORDER_CONDITIONAL_VALUE_TOOLTIP :{BLACK}The value to compare the vehicle data against +STR_ORDER_CONDITIONAL_CARGO_TOOLTIP :{BLACK}The cargo to compare the station data against STR_ORDER_CONDITIONAL_VALUE_CAPT :{WHITE}Enter value to compare against +STR_ORDER_CONDITIONAL_COMPARATOR_ACCEPTS :accepts +STR_ORDER_CONDITIONAL_COMPARATOR_DOES_NOT_ACCEPT :does not accept +STR_ORDER_CONDITIONAL_COMPARATOR_HAS :has +STR_ORDER_CONDITIONAL_COMPARATOR_HAS_NO :does not have +STR_ORDER_CONDITIONAL_COMPARATOR_HAS_LESS_THAN :has less than +STR_ORDER_CONDITIONAL_COMPARATOR_HAS_LESS_EQUALS :has less than or exactly +STR_ORDER_CONDITIONAL_COMPARATOR_HAS_MORE_THAN :has more than +STR_ORDER_CONDITIONAL_COMPARATOR_HAS_MORE_EQUALS :has more than or exactly + STR_ORDERS_SKIP_BUTTON :{BLACK}Skip STR_ORDERS_SKIP_TOOLTIP :{BLACK}Skip the current order, and start the next. Ctrl+Click skips to the selected order @@ -4308,6 +4339,7 @@ STR_ORDER_OUT_OF_RANGE :{RED} (Next des STR_ORDER_CONDITIONAL_UNCONDITIONAL :Jump to order {COMMA} STR_ORDER_CONDITIONAL_NUM :Jump to order {COMMA} when {STRING} {STRING} {COMMA} +STR_ORDER_CONDITIONAL_CARGO :Jump to order {COMMA} when {STRING} {STRING} {STRING} STR_ORDER_CONDITIONAL_TRUE_FALSE :Jump to order {COMMA} when {STRING} {STRING} STR_INVALID_ORDER :{RED} (Invalid Order) diff --git a/src/lang/russian.txt b/src/lang/russian.txt index a9220df663..e35a8e6307 100644 --- a/src/lang/russian.txt +++ b/src/lang/russian.txt @@ -4078,6 +4078,16 @@ STR_ORDER_OUT_OF_RANGE :{RED} (Расс STR_ORDER_CONDITIONAL_UNCONDITIONAL :Перейти к заданию {COMMA} STR_ORDER_CONDITIONAL_NUM :Перейти к заданию {COMMA}, если {STRING} {STRING} {COMMA} STR_ORDER_CONDITIONAL_TRUE_FALSE :Перейти к заданию {COMMA}, если {STRING} - {STRING} +STR_CONDITIONAL_FREE_PLATFORMS :Перейти к заданию {COMMA} когда след. станция {STRING} {COMMA} свободн{P ая ые ых} платформ{P а ы ""} +STR_CONDITIONAL_PERCENT :Перейти к заданию {COMMA} {COMMA} процентов времени +STR_ORDER_CONDITIONAL_COMPARATOR_ACCEPTS :принимает +STR_ORDER_CONDITIONAL_COMPARATOR_DOES_NOT_ACCEPT :не принимает +STR_ORDER_CONDITIONAL_COMPARATOR_HAS :имеет +STR_ORDER_CONDITIONAL_COMPARATOR_HAS_NO :не имеет +STR_ORDER_CONDITIONAL_COMPARATOR_HAS_LESS_THAN :имеет меньше чем +STR_ORDER_CONDITIONAL_COMPARATOR_HAS_LESS_EQUALS :имеет меньше или равно +STR_ORDER_CONDITIONAL_COMPARATOR_HAS_MORE_THAN :имеет больше чем +STR_ORDER_CONDITIONAL_COMPARATOR_HAS_MORE_EQUALS :имеет больше или равно STR_INVALID_ORDER :{RED} (Неверное место назначения) diff --git a/src/order_base.h b/src/order_base.h index d36f23594e..2edcf9145c 100644 --- a/src/order_base.h +++ b/src/order_base.h @@ -44,6 +44,7 @@ private: CargoID refit_cargo; ///< Refit CargoID uint8 occupancy; ///< Estimate of vehicle occupancy on departure, for the current order, 0 indicates invalid, 1 - 101 indicate 0 - 100% + int8 jump_counter; ///< Counter for the 'jump xx% of times' option uint16 wait_time; ///< How long in ticks to wait at the destination. uint16 travel_time; ///< How long in ticks the journey to this destination should take. @@ -128,6 +129,14 @@ public: void SetRefit(CargoID cargo); + /** + * Update the jump_counter of this order. + * @param the jump chance in %. + * @return whether to jump or not. + * @pre IsType(OT_CONDITIONAL) && this->GetConditionVariable() == OCV_PERCENT. + */ + bool UpdateJumpCounter(uint8 percent); + /** How must the consist be loaded? */ inline OrderLoadFlags GetLoadType() const { return (OrderLoadFlags)GB(this->flags, 4, 3); } /** How must the consist be unloaded? */ diff --git a/src/order_cmd.cpp b/src/order_cmd.cpp index b440624275..42f3b0f5d0 100644 --- a/src/order_cmd.cpp +++ b/src/order_cmd.cpp @@ -17,6 +17,11 @@ #include "news_func.h" #include "strings_func.h" #include "timetable.h" +#include "station_base.h" +#include "station_map.h" +#include "station_func.h" +#include "map_func.h" +#include "cargotype.h" #include "vehicle_func.h" #include "depot_base.h" #include "core/pool_func.hpp" @@ -120,6 +125,25 @@ void Order::MakeLoading(bool ordered) if (!ordered) this->flags = 0; } +/** + * Update the jump counter, for percent probability + * conditional orders + * + * Not that jump_counter is signed and may become + * negative when a jump has been taken + * + * @return true if the jump should be taken + */ +bool Order::UpdateJumpCounter(byte percent) +{ + if (this->jump_counter >= 0) { + this->jump_counter += (percent - 100); + return true; + } + this->jump_counter += percent; + return false; +} + /** * Makes this order a Leave Station order. */ @@ -248,6 +272,7 @@ Order::Order(uint32 packed) this->occupancy = 0; this->wait_time = 0; this->travel_time = 0; + this->jump_counter = 0; this->max_speed = UINT16_MAX; } @@ -287,6 +312,8 @@ void Order::AssignOrder(const Order &other) this->refit_cargo = other.refit_cargo; this->wait_time = other.wait_time; + + this->jump_counter = other.jump_counter; this->travel_time = other.travel_time; this->max_speed = other.max_speed; } @@ -875,6 +902,10 @@ CommandCost CmdInsertOrder(TileIndex tile, DoCommandFlag flags, uint32 p1, uint3 OrderConditionComparator occ = new_order.GetConditionComparator(); if (occ >= OCC_END) return CMD_ERROR; switch (new_order.GetConditionVariable()) { + case OCV_CARGO_WAITING: + case OCV_CARGO_ACCEPTANCE: + if (!CargoSpec::Get(new_order.GetConditionValue())->IsValid()) return CMD_ERROR; + /* FALL THROUGH */ case OCV_REQUIRES_SERVICE: if (occ != OCC_IS_TRUE && occ != OCC_IS_FALSE) return CMD_ERROR; break; @@ -884,6 +915,14 @@ CommandCost CmdInsertOrder(TileIndex tile, DoCommandFlag flags, uint32 p1, uint3 if (new_order.GetConditionValue() != 0) return CMD_ERROR; break; + case OCV_FREE_PLATFORMS: + if (v->type != VEH_TRAIN) return CMD_ERROR; + if (occ == OCC_IS_TRUE || occ == OCC_IS_FALSE) return CMD_ERROR; + break; + + case OCV_PERCENT: + if (occ != OCC_EQUALS) return CMD_ERROR; + /* FALL THROUGH */ case OCV_LOAD_PERCENTAGE: case OCV_RELIABILITY: if (new_order.GetConditionValue() > 100) return CMD_ERROR; @@ -1033,6 +1072,18 @@ static CommandCost DecloneOrder(Vehicle *dst, DoCommandFlag flags) return CommandCost(); } +/** + * Get the first cargoID that points to a valid cargo (usually 0) + */ +static CargoID GetFirstValidCargo() +{ + for (CargoID i = 0; i < NUM_CARGO; i++) { + if (CargoSpec::Get(i)->IsValid()) return i; + } + /* No cargos defined -> 'Houston, we have a problem!' */ + NOT_REACHED(); +} + /** * Delete an order from the orderlist of a vehicle. * @param tile unused @@ -1370,15 +1421,20 @@ CommandCost CmdModifyOrder(TileIndex tile, DoCommandFlag flags, uint32 p1, uint3 break; case MOF_COND_VARIABLE: + if (data == OCV_FREE_PLATFORMS && v->type != VEH_TRAIN) return CMD_ERROR; if (data >= OCV_END) return CMD_ERROR; break; case MOF_COND_COMPARATOR: if (data >= OCC_END) return CMD_ERROR; switch (order->GetConditionVariable()) { - case OCV_UNCONDITIONALLY: return CMD_ERROR; + case OCV_UNCONDITIONALLY: + case OCV_PERCENT: + return CMD_ERROR; case OCV_REQUIRES_SERVICE: + case OCV_CARGO_ACCEPTANCE: + case OCV_CARGO_WAITING: if (data != OCC_IS_TRUE && data != OCC_IS_FALSE) return CMD_ERROR; break; @@ -1396,9 +1452,15 @@ CommandCost CmdModifyOrder(TileIndex tile, DoCommandFlag flags, uint32 p1, uint3 case OCV_LOAD_PERCENTAGE: case OCV_RELIABILITY: + case OCV_PERCENT: if (data > 100) return CMD_ERROR; break; + case OCV_CARGO_ACCEPTANCE: + case OCV_CARGO_WAITING: + if (!(data < NUM_CARGO && CargoSpec::Get(data)->IsValid())) return CMD_ERROR; + break; + default: if (data > 2047) return CMD_ERROR; break; @@ -1460,6 +1522,8 @@ CommandCost CmdModifyOrder(TileIndex tile, DoCommandFlag flags, uint32 p1, uint3 } case MOF_COND_VARIABLE: { + /* Check whether old conditional variable had a cargo as value */ + bool old_var_was_cargo = (order->GetConditionVariable() == OCV_CARGO_ACCEPTANCE || order->GetConditionVariable() == OCV_CARGO_WAITING); order->SetConditionVariable((OrderConditionVariable)data); OrderConditionComparator occ = order->GetConditionComparator(); @@ -1469,16 +1533,26 @@ CommandCost CmdModifyOrder(TileIndex tile, DoCommandFlag flags, uint32 p1, uint3 order->SetConditionValue(0); break; + case OCV_CARGO_ACCEPTANCE: + case OCV_CARGO_WAITING: + if (!old_var_was_cargo) order->SetConditionValue((uint16) GetFirstValidCargo()); + if (occ != OCC_IS_TRUE && occ != OCC_IS_FALSE) order->SetConditionComparator(OCC_IS_TRUE); + break; case OCV_REQUIRES_SERVICE: + if (old_var_was_cargo) order->SetConditionValue(0); if (occ != OCC_IS_TRUE && occ != OCC_IS_FALSE) order->SetConditionComparator(OCC_IS_TRUE); order->SetConditionValue(0); break; + case OCV_PERCENT: + order->SetConditionComparator(OCC_EQUALS); + /* FALL THROUGH */ case OCV_LOAD_PERCENTAGE: case OCV_RELIABILITY: if (order->GetConditionValue() > 100) order->SetConditionValue(100); /* FALL THROUGH */ default: + if (old_var_was_cargo) order->SetConditionValue(0); if (occ == OCC_IS_TRUE || occ == OCC_IS_FALSE) order->SetConditionComparator(OCC_EQUALS); break; } @@ -2019,6 +2093,52 @@ static bool OrderConditionCompare(OrderConditionComparator occ, int variable, in } } +/* Get the number of free (train) platforms in a station. + * @param st_id The StationID of the station. + * @return The number of free train platforms. + */ +static uint16 GetFreeStationPlatforms(StationID st_id) +{ + assert(Station::IsValidID(st_id)); + const Station *st = Station::Get(st_id); + if (!(st->facilities & FACIL_TRAIN)) return 0; + bool is_free; + TileIndex t2; + uint16 counter = 0; + TILE_AREA_LOOP(t1, st->train_station) { + if (st->TileBelongsToRailStation(t1)) { + /* We only proceed if this tile is a track tile and the north(-east/-west) end of the platform */ + if (IsCompatibleTrainStationTile(t1 + TileOffsByDiagDir(GetRailStationAxis(t1) == AXIS_X ? DIAGDIR_NE : DIAGDIR_NW), t1) || IsStationTileBlocked(t1)) continue; + is_free = true; + t2 = t1; + do { + if (GetStationReservationTrackBits(t2)) { + is_free = false; + break; + } + t2 += TileOffsByDiagDir(GetRailStationAxis(t1) == AXIS_X ? DIAGDIR_SW : DIAGDIR_SE); + } while (IsCompatibleTrainStationTile(t2, t1)); + if (is_free) counter++; + } + } + return counter; +} + +/** Gets the next 'real' station in the order list + * @param v the vehicle in question + * @param order the current (conditional) order + * @return the StationID of the next valid station in the order list, or INVALID_STATION if there is none. + */ +static StationID GetNextRealStation(const Vehicle *v, const Order *order, int conditional_depth = 0) +{ + if (order->IsType(OT_GOTO_STATION)) { + if (Station::IsValidID(order->GetDestination())) return order->GetDestination(); + } + //nothing conditional about this + if (conditional_depth > v->GetNumOrders()) return INVALID_STATION; + return GetNextRealStation(v, (order->next != NULL) ? order->next : v->GetFirstOrder(), ++conditional_depth); +} + /** * Process a conditional order and determine the next order. * @param order the order the vehicle currently has @@ -2040,6 +2160,27 @@ VehicleOrderID ProcessConditionalOrder(const Order *order, const Vehicle *v) case OCV_AGE: skip_order = OrderConditionCompare(occ, v->age / DAYS_IN_LEAP_YEAR, value); break; case OCV_REQUIRES_SERVICE: skip_order = OrderConditionCompare(occ, v->NeedsServicing(), value); break; case OCV_UNCONDITIONALLY: skip_order = true; break; + case OCV_CARGO_WAITING: { + StationID next_station = GetNextRealStation(v, order); + if (Station::IsValidID(next_station)) skip_order = OrderConditionCompare(occ, !Station::Get(next_station)->goods[value].cargo.AvailableCount() > 0, value); + break; + } + case OCV_CARGO_ACCEPTANCE: { + StationID next_station = GetNextRealStation(v, order); + if (Station::IsValidID(next_station)) skip_order = OrderConditionCompare(occ, HasBit(Station::Get(next_station)->goods[value].status, GoodsEntry::GES_ACCEPTANCE), value); + break; + } + case OCV_FREE_PLATFORMS: { + StationID next_station = GetNextRealStation(v, order); + if (Station::IsValidID(next_station)) skip_order = OrderConditionCompare(occ, GetFreeStationPlatforms(next_station), value); + break; + } + case OCV_PERCENT: { + /* get a non-const reference to the current order */ + Order *ord = const_cast(order); + skip_order = ord->UpdateJumpCounter((byte)value); + break; + } case OCV_REMAINING_LIFETIME: skip_order = OrderConditionCompare(occ, max(v->max_age - v->age + DAYS_IN_LEAP_YEAR - 1, 0) / DAYS_IN_LEAP_YEAR, value); break; default: NOT_REACHED(); } diff --git a/src/order_gui.cpp b/src/order_gui.cpp index 341b85db93..7dca85bcc7 100644 --- a/src/order_gui.cpp +++ b/src/order_gui.cpp @@ -157,6 +157,10 @@ static const OrderConditionVariable _order_conditional_variable[] = { OCV_AGE, OCV_REMAINING_LIFETIME, OCV_REQUIRES_SERVICE, + OCV_CARGO_WAITING, + OCV_CARGO_ACCEPTANCE, + OCV_FREE_PLATFORMS, + OCV_PERCENT, OCV_UNCONDITIONALLY, }; @@ -172,6 +176,30 @@ static const StringID _order_conditional_condition[] = { INVALID_STRING_ID, }; +static const StringID _order_conditional_condition_has[] = { + STR_ORDER_CONDITIONAL_COMPARATOR_HAS, + STR_ORDER_CONDITIONAL_COMPARATOR_HAS_NO, + STR_ORDER_CONDITIONAL_COMPARATOR_HAS_LESS_THAN, + STR_ORDER_CONDITIONAL_COMPARATOR_HAS_LESS_EQUALS, + STR_ORDER_CONDITIONAL_COMPARATOR_HAS_MORE_THAN, + STR_ORDER_CONDITIONAL_COMPARATOR_HAS_MORE_EQUALS, + STR_ORDER_CONDITIONAL_COMPARATOR_HAS, + STR_ORDER_CONDITIONAL_COMPARATOR_HAS_NO, + INVALID_STRING_ID, +}; + +static const StringID _order_conditional_condition_accepts[] = { + STR_NULL, + STR_NULL, + STR_NULL, + STR_NULL, + STR_NULL, + STR_NULL, + STR_ORDER_CONDITIONAL_COMPARATOR_ACCEPTS, + STR_ORDER_CONDITIONAL_COMPARATOR_DOES_NOT_ACCEPT, + INVALID_STRING_ID, +}; + extern uint ConvertSpeedToDisplaySpeed(uint speed); extern uint ConvertDisplaySpeedToSpeed(uint speed); @@ -327,19 +355,46 @@ void DrawOrderString(const Vehicle *v, const Order *order, int order_index, int SetDParam(1, order->GetDestination()); break; - case OT_CONDITIONAL: + case OT_CONDITIONAL: { SetDParam(1, order->GetConditionSkipToOrder() + 1); - if (order->GetConditionVariable() == OCV_UNCONDITIONALLY) { + const OrderConditionVariable ocv = order->GetConditionVariable( ); + /* handle some non-ordinary cases seperately */ + if (ocv == OCV_UNCONDITIONALLY) { SetDParam(0, STR_ORDER_CONDITIONAL_UNCONDITIONAL); + } else if (ocv == OCV_PERCENT) { + SetDParam(0, STR_CONDITIONAL_PERCENT); + SetDParam(2, order->GetConditionValue()); + } else if (ocv == OCV_FREE_PLATFORMS) { + SetDParam(0, STR_CONDITIONAL_FREE_PLATFORMS ); + SetDParam(2, STR_ORDER_CONDITIONAL_COMPARATOR_HAS + order->GetConditionComparator()); + SetDParam(3, order->GetConditionValue()); } else { OrderConditionComparator occ = order->GetConditionComparator(); - SetDParam(0, (occ == OCC_IS_TRUE || occ == OCC_IS_FALSE) ? STR_ORDER_CONDITIONAL_TRUE_FALSE : STR_ORDER_CONDITIONAL_NUM); - SetDParam(2, STR_ORDER_CONDITIONAL_LOAD_PERCENTAGE + order->GetConditionVariable()); - SetDParam(3, STR_ORDER_CONDITIONAL_COMPARATOR_EQUALS + occ); + bool is_cargo = ocv == OCV_CARGO_ACCEPTANCE || ocv == OCV_CARGO_WAITING; + SetDParam(0, is_cargo ? STR_ORDER_CONDITIONAL_CARGO : (occ == OCC_IS_TRUE || occ == OCC_IS_FALSE) ? STR_ORDER_CONDITIONAL_TRUE_FALSE : STR_ORDER_CONDITIONAL_NUM); + SetDParam(2, (ocv == OCV_CARGO_ACCEPTANCE || ocv == OCV_CARGO_WAITING || ocv == OCV_FREE_PLATFORMS) + ? STR_ORDER_CONDITIONAL_NEXT_STATION : STR_ORDER_CONDITIONAL_LOAD_PERCENTAGE + ocv); uint value = order->GetConditionValue(); - if (order->GetConditionVariable() == OCV_MAX_SPEED) value = ConvertSpeedToDisplaySpeed(value); - SetDParam(4, value); + switch (ocv) { + case OCV_CARGO_ACCEPTANCE: + SetDParam(3, STR_ORDER_CONDITIONAL_COMPARATOR_ACCEPTS + occ - OCC_IS_TRUE); + SetDParam(4, CargoSpec::Get( value )->name ); + break; + case OCV_CARGO_WAITING: + SetDParam(3, STR_ORDER_CONDITIONAL_COMPARATOR_HAS + occ - OCC_IS_TRUE); + SetDParam(4, CargoSpec::Get( value )->name ); + break; + case OCV_REQUIRES_SERVICE: + SetDParam(3, STR_ORDER_CONDITIONAL_COMPARATOR_EQUALS + occ); + break; + case OCV_MAX_SPEED: + value = ConvertSpeedToDisplaySpeed(value); + /* FALL THROUGH */ + default: + SetDParam(3, STR_ORDER_CONDITIONAL_COMPARATOR_EQUALS + occ); + SetDParam(4, value); + } } if (timetable && order->GetWaitTime() > 0) { @@ -348,7 +403,9 @@ void DrawOrderString(const Vehicle *v, const Order *order, int order_index, int } else { SetDParam(5, STR_EMPTY); } + break; + } default: NOT_REACHED(); } @@ -509,6 +566,11 @@ private: /* WID_O_SEL_TOP_ROW */ DP_ROW_LOAD = 0, ///< Display 'load' / 'unload' / 'refit' buttons in the top row of the ship/airplane order window. DP_ROW_DEPOT = 1, ///< Display 'refit' / 'service' buttons in the top row of the ship/airplane order window. + + /* WID_O_SEL_COND_VALUE */ + DP_COND_VALUE_NUMBER = 0, ///< Display number widget + DP_COND_VALUE_CARGO = 1, ///< Display dropdown widget cargo types + DP_ROW_CONDITIONAL = 2, ///< Display the conditional order buttons in the top row of the ship/airplane order window. /* WID_O_SEL_BOTTOM_MIDDLE */ @@ -523,6 +585,8 @@ private: Scrollbar *vscroll; bool can_do_refit; ///< Vehicle chain can be refitted in depot. bool can_do_autorefit; ///< Vehicle chain can be auto-refitted. + StringID cargo_names_list[NUM_CARGO + 1]; + uint32 cargo_bitmask; /** * Return the memorised selected order. @@ -555,6 +619,28 @@ private: return (sel <= vehicle->GetNumOrders() && sel >= 0) ? sel : INVALID_VEH_ORDER_ID; } + /** + * Determine which strings should be displayed in the conditional comparator dropdown + * + * @param order the order to evaluate + * @return the StringIDs to display + */ + static const StringID *GetComparatorStrings(const Order *order) + { + if (order == NULL) return _order_conditional_condition; + switch (order->GetConditionVariable()) { + case OCV_FREE_PLATFORMS: + case OCV_CARGO_WAITING: + return _order_conditional_condition_has; + + case OCV_CARGO_ACCEPTANCE: + return _order_conditional_condition_accepts; + + default: + return _order_conditional_condition; + } + } + /** * Handle the click on the goto button. */ @@ -851,6 +937,18 @@ public: break; } } + + + /* Create cargo bitmask */ + assert_compile(NUM_CARGO <= 32); + for (CargoID c = 0; c < NUM_CARGO; c++) { + if (CargoSpec::Get(c)->IsValid()) { + this->cargo_names_list[c] = CargoSpec::Get(c)->name; + SetBit(this->cargo_bitmask, c); + } + } + this->cargo_bitmask = ~this->cargo_bitmask; + this->cargo_names_list[NUM_CARGO] = INVALID_STRING_ID; } /** @@ -1061,11 +1159,20 @@ public: } else { train_row_sel->SetDisplayedPlane(DP_GROUNDVEHICLE_ROW_CONDITIONAL); } - OrderConditionVariable ocv = order->GetConditionVariable(); + OrderConditionVariable ocv = (order == NULL) ? OCV_LOAD_PERCENTAGE : order->GetConditionVariable(); + bool is_cargo = (ocv == OCV_CARGO_ACCEPTANCE || ocv == OCV_CARGO_WAITING); + + if (is_cargo) { + this->GetWidget(WID_O_COND_CARGO)->widget_data = cargo_names_list[(order == NULL) ? 0 : order->GetConditionValue()]; + this->GetWidget(WID_O_SEL_COND_VALUE)->SetDisplayedPlane(DP_COND_VALUE_CARGO); + } else { + this->GetWidget(WID_O_SEL_COND_VALUE)->SetDisplayedPlane(DP_COND_VALUE_NUMBER); + } + /* Set the strings for the dropdown boxes. */ this->GetWidget(WID_O_COND_VARIABLE)->widget_data = STR_ORDER_CONDITIONAL_LOAD_PERCENTAGE + ocv; - this->GetWidget(WID_O_COND_COMPARATOR)->widget_data = _order_conditional_condition[order->GetConditionComparator()]; - this->SetWidgetDisabledState(WID_O_COND_COMPARATOR, ocv == OCV_UNCONDITIONALLY); + this->GetWidget(WID_O_COND_COMPARATOR)->widget_data = GetComparatorStrings(order)[order->GetConditionComparator()]; + this->SetWidgetDisabledState(WID_O_COND_COMPARATOR, ocv == OCV_UNCONDITIONALLY || ocv == OCV_PERCENT); this->SetWidgetDisabledState(WID_O_COND_VALUE, ocv == OCV_REQUIRES_SERVICE || ocv == OCV_UNCONDITIONALLY); break; } @@ -1352,6 +1459,12 @@ public: } break; + case WID_O_COND_CARGO: { + uint value = this->vehicle->GetOrder(this->OrderGetSel())->GetConditionValue(); + ShowDropDownMenu(this, cargo_names_list, value, WID_O_COND_CARGO, 0, cargo_bitmask); + break; + } + case WID_O_TIMETABLE_VIEW: ShowTimetableWindow(this->vehicle); break; @@ -1367,7 +1480,9 @@ public: case WID_O_COND_COMPARATOR: { const Order *o = this->vehicle->GetOrder(this->OrderGetSel()); - ShowDropDownMenu(this, _order_conditional_condition, o->GetConditionComparator(), WID_O_COND_COMPARATOR, 0, (o->GetConditionVariable() == OCV_REQUIRES_SERVICE) ? 0x3F : 0xC0, 0, DDSF_LOST_FOCUS); + OrderConditionVariable cond_var = o->GetConditionVariable(); + ShowDropDownMenu(this, GetComparatorStrings(o), o->GetConditionComparator(), WID_O_COND_COMPARATOR, 0, + (cond_var == OCV_REQUIRES_SERVICE || cond_var == OCV_CARGO_ACCEPTANCE || cond_var == OCV_CARGO_WAITING) ? 0x3F : 0xC0, 0, DDSF_LOST_FOCUS); break; } @@ -1403,6 +1518,7 @@ public: value = ConvertDisplaySpeedToSpeed(value); break; + case OCV_PERCENT: case OCV_RELIABILITY: case OCV_LOAD_PERCENTAGE: value = Clamp(value, 0, 100); @@ -1455,6 +1571,10 @@ public: case WID_O_COND_COMPARATOR: DoCommandP(this->vehicle->tile, this->vehicle->index + (this->OrderGetSel() << 20), MOF_COND_COMPARATOR | index << 4, CMD_MODIFY_ORDER | CMD_MSG(STR_ERROR_CAN_T_MODIFY_THIS_ORDER)); break; + + case WID_O_COND_CARGO: + DoCommandP(this->vehicle->tile, this->vehicle->index + (this->OrderGetSel() << 20), MOF_COND_VALUE | index << 4, CMD_MODIFY_ORDER | CMD_MSG(STR_ERROR_CAN_T_MODIFY_THIS_ORDER)); + break; } } @@ -1672,8 +1792,12 @@ static const NWidgetPart _nested_orders_train_widgets[] = { SetDataTip(STR_NULL, STR_ORDER_CONDITIONAL_VARIABLE_TOOLTIP), SetResize(1, 0), NWidget(WWT_DROPDOWN, COLOUR_GREY, WID_O_COND_COMPARATOR), SetMinimalSize(124, 12), SetFill(1, 0), SetDataTip(STR_NULL, STR_ORDER_CONDITIONAL_COMPARATOR_TOOLTIP), SetResize(1, 0), - NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_O_COND_VALUE), SetMinimalSize(124, 12), SetFill(1, 0), + NWidget(NWID_SELECTION, INVALID_COLOUR, WID_O_SEL_COND_VALUE), + NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_O_COND_VALUE), SetMinimalSize(124, 12), SetFill(1, 0), SetDataTip(STR_BLACK_COMMA, STR_ORDER_CONDITIONAL_VALUE_TOOLTIP), SetResize(1, 0), + NWidget(WWT_DROPDOWN, COLOUR_GREY, WID_O_COND_CARGO), SetMinimalSize(124, 12), SetFill(1, 0), + SetDataTip(STR_NULL, STR_ORDER_CONDITIONAL_CARGO_TOOLTIP), SetResize(1, 0), + EndContainer(), EndContainer(), EndContainer(), NWidget(WWT_TEXTBTN, COLOUR_GREY, WID_O_OCCUPANCY_TOGGLE), SetMinimalSize(12, 12), SetDataTip(STR_ORDERS_OCCUPANCY_BUTTON, STR_ORDERS_OCCUPANCY_BUTTON_TOOLTIP), diff --git a/src/order_type.h b/src/order_type.h index aa772bc58b..646d38147e 100644 --- a/src/order_type.h +++ b/src/order_type.h @@ -125,6 +125,10 @@ enum OrderConditionVariable { OCV_REQUIRES_SERVICE, ///< Skip when the vehicle requires service OCV_UNCONDITIONALLY, ///< Always skip OCV_REMAINING_LIFETIME, ///< Skip based on the remaining lifetime + OCV_CARGO_WAITING, ///< Skip if specified cargo is waiting at next station + OCV_CARGO_ACCEPTANCE, ///< Skip if specified cargo is accepted at next station + OCV_FREE_PLATFORMS, ///< Skip based on free platforms at next station + OCV_PERCENT, ///< Skip xx percent of times OCV_END }; diff --git a/src/rail_cmd.cpp b/src/rail_cmd.cpp index 565b79bffd..7a7af1461c 100644 --- a/src/rail_cmd.cpp +++ b/src/rail_cmd.cpp @@ -1981,8 +1981,8 @@ static void DrawSingleSignal(TileIndex tile, const RailtypeInfo *rti, Track trac if (!is_custom_sprite && variant == SIG_ELECTRIC && IsRestrictedSignal(tile) && GetExistingTraceRestrictProgram(tile, track) != NULL) { if (type == SIGTYPE_PBS || type == SIGTYPE_PBS_ONEWAY) { - static const SubSprite lower_part { -50, -10, 50, 50 }; - static const SubSprite upper_part { -50, -50, 50, -11 }; + static const SubSprite lower_part = { -50, -10, 50, 50 }; + static const SubSprite upper_part = { -50, -50, 50, -11 }; AddSortableSpriteToDraw(sprite, SPR_TRACERESTRICT_BASE, x, y, 1, 1, BB_HEIGHT_UNDER_BRIDGE, GetSaveSlopeZ(x, y, track), false, 0, 0, 0, &lower_part); AddSortableSpriteToDraw(sprite, PAL_NONE, x, y, 1, 1, BB_HEIGHT_UNDER_BRIDGE, GetSaveSlopeZ(x, y, track), false, 0, 0, 0, &upper_part); diff --git a/src/saveload/extended_ver_sl.cpp b/src/saveload/extended_ver_sl.cpp index 8c32f65a93..cc81735f57 100644 --- a/src/saveload/extended_ver_sl.cpp +++ b/src/saveload/extended_ver_sl.cpp @@ -61,6 +61,7 @@ const SlxiSubChunkInfo _sl_xv_sub_chunk_infos[] = { { XSLFI_INFRA_SHARING, XSCF_NULL, 1, 1, "infra_sharing", NULL, NULL, NULL }, { XSLFI_VARIABLE_DAY_LENGTH, XSCF_NULL, 1, 1, "variable_day_length", NULL, NULL, NULL }, { XSLFI_ORDER_OCCUPANCY, XSCF_NULL, 1, 1, "order_occupancy", NULL, NULL, NULL }, + { XSLFI_MORE_COND_ORDERS, XSCF_NULL, 1, 1, "more_cond_orders", NULL, NULL, NULL }, { XSLFI_NULL, XSCF_NULL, 0, 0, NULL, NULL, NULL, NULL },// This is the end marker }; @@ -169,6 +170,7 @@ void SlXvCheckSpecialSavegameVersions() _sl_xv_feature_versions[XSLFI_IMPROVED_BREAKDOWNS] = 1; _sl_xv_feature_versions[XSLFI_INFRA_SHARING] = 1; _sl_xv_feature_versions[XSLFI_AUTO_TIMETABLE] = 1; + _sl_xv_feature_versions[XSLFI_MORE_COND_ORDERS] = 1; _sl_xv_discardable_chunk_ids.push_back('SNOW'); } diff --git a/src/saveload/extended_ver_sl.h b/src/saveload/extended_ver_sl.h index 6bc8a380db..7f010b59ec 100644 --- a/src/saveload/extended_ver_sl.h +++ b/src/saveload/extended_ver_sl.h @@ -36,6 +36,7 @@ enum SlXvFeatureIndex { XSLFI_INFRA_SHARING, ///< Infrastructure sharing patch XSLFI_VARIABLE_DAY_LENGTH, ///< Variable day length patch XSLFI_ORDER_OCCUPANCY, ///< Running average of order occupancy + XSLFI_MORE_COND_ORDERS, ///< More conditional orders patch XSLFI_RIFF_HEADER_60_BIT, ///< Size field in RIFF chunk header is 60 bit XSLFI_HEIGHT_8_BIT, ///< Map tile height is 8 bit instead of 4 bit, but savegame version may be before this became true in trunk diff --git a/src/saveload/order_sl.cpp b/src/saveload/order_sl.cpp index ca3e13c062..f45346b183 100644 --- a/src/saveload/order_sl.cpp +++ b/src/saveload/order_sl.cpp @@ -115,7 +115,7 @@ const SaveLoad *GetOrderDescription() SLE_CONDVAR(Order, wait_time, SLE_UINT16, 67, SL_MAX_VERSION), SLE_CONDVAR(Order, travel_time, SLE_UINT16, 67, SL_MAX_VERSION), SLE_CONDVAR(Order, max_speed, SLE_UINT16, 172, SL_MAX_VERSION), - SLE_CONDNULL_X(1, 0, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_SPRINGPP)), + SLE_CONDVAR_X(Order, jump_counter, SLE_INT8, 0, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_MORE_COND_ORDERS)), /* Leftover from the minor savegame version stuff * We will never use those free bytes, but we have to keep this line to allow loading of old savegames */ diff --git a/src/settings.cpp b/src/settings.cpp index e01fa50e7f..f2019fdbde 100644 --- a/src/settings.cpp +++ b/src/settings.cpp @@ -2303,6 +2303,18 @@ static void SaveSettings(const SettingDesc *sd, void *object) /** Sorted list of PATX settings, generated by MakeSettingsPatxList */ static std::vector _sorted_patx_settings; +/** + * Internal structure used in LoadSettingsPatx() + * placed outside for legacy compiler compatibility + * this makes me miss lambdas :/ + */ +struct StringSorter { + bool operator()(const SettingDesc *a, const SettingDesc *b) + { + return strcmp(a->patx_name, b->patx_name) < 0; + } +}; + /** * Prepare a sorted list of settings to be potentially be loaded out of the PATX chunk * This is to enable efficient lookup of settings by name @@ -2320,16 +2332,38 @@ static void MakeSettingsPatxList(const SettingDesc *sd) _sorted_patx_settings.push_back(desc); } - // this makes me miss lambdas :/ - struct StringSorter { - bool operator()(const SettingDesc *a, const SettingDesc *b) - { - return strcmp(a->patx_name, b->patx_name) < 0; - } - }; std::sort(_sorted_patx_settings.begin(), _sorted_patx_settings.end(), StringSorter()); } +/** + * Internal structure used in LoadSettingsPatx() + * placed outside for legacy compiler compatibility + */ +struct SettingsPatxLoad { + uint32 flags; + char name[256]; + uint32 setting_length; +}; + +/** + * Internal structure used in LoadSettingsPatx() + * placed outside for legacy compiler compatibility + * this is effectively a reference capture lambda + */ +struct StringSearcher { + bool &m_exact_match; + + StringSearcher(bool &exact_match) + : m_exact_match(exact_match) { } + + bool operator()(const SettingDesc *a, const char *b) + { + int result = strcmp(a->patx_name, b); + if (result == 0) m_exact_match = true; + return result < 0; + } +}; + /** * Load handler for settings which go in the PATX chunk * @param osd SettingDesc struct containing all information @@ -2340,11 +2374,6 @@ static void LoadSettingsPatx(const SettingDesc *sd, void *object) { MakeSettingsPatxList(sd); - struct SettingsPatxLoad { - uint32 flags; - char name[256]; - uint32 setting_length; - }; SettingsPatxLoad current_setting; static const SaveLoad _settings_patx_desc[] = { @@ -2367,19 +2396,6 @@ static void LoadSettingsPatx(const SettingDesc *sd, void *object) // now try to find corresponding setting, this would be much easier with C++11 support... bool exact_match = false; - struct StringSearcher { - bool &m_exact_match; - - StringSearcher(bool &exact_match) - : m_exact_match(exact_match) { } - - bool operator()(const SettingDesc *a, const char *b) - { - int result = strcmp(a->patx_name, b); - if (result == 0) m_exact_match = true; - return result < 0; - } - }; std::vector::iterator iter = std::lower_bound(_sorted_patx_settings.begin(), _sorted_patx_settings.end(), current_setting.name, StringSearcher(exact_match)); if (exact_match) { @@ -2400,6 +2416,15 @@ static void LoadSettingsPatx(const SettingDesc *sd, void *object) } } +/** + * Internal structure used in SaveSettingsPatx() + * placed outside for legacy compiler compatibility + */ +struct SettingToAdd { + const SettingDesc *setting; + uint32 setting_length; +}; + /** * Save handler for settings which go in the PATX chunk * @param sd SettingDesc struct containing all information @@ -2422,10 +2447,6 @@ static void SaveSettingsPatx(const SettingDesc *sd, void *object) SLE_END() }; - struct SettingToAdd { - const SettingDesc *setting; - uint32 setting_length; - }; std::vector settings_to_add; size_t length = 8; @@ -2442,7 +2463,9 @@ static void SaveSettingsPatx(const SettingDesc *sd, void *object) // add length of actual setting length += setting_length; - settings_to_add.push_back({ desc, setting_length }); + // duplicate copy made for compiler backwards compatibility + SettingToAdd new_setting = { desc, setting_length }; + settings_to_add.push_back(new_setting); } SlSetLength(length); diff --git a/src/settings_gui.cpp b/src/settings_gui.cpp index a6bbf32d70..4ca152c0b5 100644 --- a/src/settings_gui.cpp +++ b/src/settings_gui.cpp @@ -1702,6 +1702,7 @@ static SettingsContainer &GetSettingsTree() limitations->Add(new SettingEntry("construction.road_stop_on_competitor_road")); limitations->Add(new SettingEntry("vehicle.disable_elrails")); limitations->Add(new SettingEntry("construction.maximum_signal_evaluations")); + limitations->Add(new SettingEntry("construction.enable_build_river")); } SettingsPage *disasters = main->Add(new SettingsPage(STR_CONFIG_SETTING_ACCIDENTS)); @@ -1774,9 +1775,14 @@ static SettingsContainer &GetSettingsTree() cdist->Add(new SettingEntry("linkgraph.demand_size")); cdist->Add(new SettingEntry("linkgraph.short_path_saturation")); } + SettingsPage *treedist = environment->Add(new SettingsPage(STR_CONFIG_SETTING_ENVIRONMENT_TREES)); + { + treedist->Add(new SettingEntry("construction.extra_tree_placement")); + treedist->Add(new SettingEntry("construction.trees_around_snow_line_enabled")); + treedist->Add(new SettingEntry("construction.trees_around_snow_line_range")); + } environment->Add(new SettingEntry("station.modified_catchment")); - environment->Add(new SettingEntry("construction.extra_tree_placement")); } SettingsPage *ai = main->Add(new SettingsPage(STR_CONFIG_SETTING_AI)); diff --git a/src/settings_type.h b/src/settings_type.h index 56aac6a19b..31ac6a772b 100644 --- a/src/settings_type.h +++ b/src/settings_type.h @@ -342,9 +342,12 @@ struct ConstructionSettings { uint8 industry_platform; ///< the amount of flat land around an industry bool freeform_edges; ///< allow terraforming the tiles at the map edges uint8 extra_tree_placement; ///< (dis)allow building extra trees in-game + uint8 trees_around_snow_line_range; ///< range around snowline for mixed and arctic forest. + bool trees_around_snow_line_enabled; ///< enable mixed and arctic forest around snowline, and no trees above snowline uint8 command_pause_level; ///< level/amount of commands that can't be executed while paused uint16 maximum_signal_evaluations; ///< maximum number of programmable signals which may be evaluated in one pass byte simulated_wormhole_signals; ///< simulate signals in tunnel + bool enable_build_river; ///< enable building rivers in-game uint32 terraform_per_64k_frames; ///< how many tile heights may, over a long period, be terraformed per 65536 frames? uint16 terraform_frame_burst; ///< how many tile heights may, over a short period, be terraformed? diff --git a/src/table/settings.ini b/src/table/settings.ini index ba200d5cab..de90d90c77 100644 --- a/src/table/settings.ini +++ b/src/table/settings.ini @@ -468,6 +468,15 @@ str = STR_CONFIG_SETTING_COMMAND_PAUSE_LEVEL strhelp = STR_CONFIG_SETTING_COMMAND_PAUSE_LEVEL_HELPTEXT strval = STR_CONFIG_SETTING_COMMAND_PAUSE_LEVEL_NO_ACTIONS +[SDT_BOOL] +base = GameSettings +var = construction.enable_build_river +def = false +cat = SC_BASIC +str = STR_CONFIG_SETTING_ENABLE_BUILD_RIVER +strhelp = STR_CONFIG_SETTING_ENABLE_BUILD_RIVER_HELPTEXT +patxname = ""enable_build_river.construction.enable_build_river"" + [SDT_VAR] base = GameSettings var = construction.terraform_per_64k_frames @@ -2704,6 +2713,28 @@ strhelp = STR_CONFIG_SETTING_EXTRA_TREE_PLACEMENT_HELPTEXT strval = STR_CONFIG_SETTING_EXTRA_TREE_PLACEMENT_NONE cat = SC_BASIC +[SDT_BOOL] +base = GameSettings +var = construction.trees_around_snow_line_enabled +def = true +str = STR_CONFIG_SETTING_TREES_AROUND_SNOWLINE +strhelp = STR_CONFIG_SETTING_TREES_AROUND_SNOWLINE_HELPTEXT +cat = SC_BASIC +patxname = ""everest_treeline.construction.trees_around_snow_line_enabled"" + +[SDT_VAR] +base = GameSettings +var = construction.trees_around_snow_line_range +type = SLE_UINT8 +def = 8 +min = 1 +max = 20 +str = STR_CONFIG_SETTING_TREES_AROUND_SNOWLINE_RANGE +strhelp = STR_CONFIG_SETTING_TREES_AROUND_SNOWLINE_RANGE_HELPTEXT +strval = STR_JUST_COMMA +cat = SC_BASIC +patxname = ""everest_treeline.construction.trees_around_snow_line_range"" + [SDT_VAR] base = GameSettings var = game_creation.custom_sea_level diff --git a/src/timetable_gui.cpp b/src/timetable_gui.cpp index aea0be5459..13822e5953 100644 --- a/src/timetable_gui.cpp +++ b/src/timetable_gui.cpp @@ -51,10 +51,10 @@ void SetTimetableParams(int param1, int param2, Ticks ticks) SetDParam(param2, ticks); SetDParam(param1, STR_TIMETABLE_TICKS); } else if (_settings_client.gui.time_in_minutes) { - SetDParam(param2, ticks / DATE_UNIT_SIZE); + SetDParam(param2, ticks / (DATE_UNIT_SIZE * _settings_game.economy.day_length_factor)); SetDParam(param1, STR_TIMETABLE_MINUTES); } else { - SetDParam(param2, ticks / DATE_UNIT_SIZE); + SetDParam(param2, ticks / (DATE_UNIT_SIZE * _settings_game.economy.day_length_factor)); SetDParam(param1, STR_TIMETABLE_DAYS); } } @@ -98,6 +98,7 @@ static void FillTimetableArrivalDepartureTable(const Vehicle *v, VehicleOrderID Ticks sum = offset; VehicleOrderID i = start; const Order *order = v->GetOrder(i); + uint8 day_len = _settings_game.economy.day_length_factor; /* Pre-initialize with unknown time */ for (int i = 0; i < v->GetNumOrders(); ++i) { @@ -114,12 +115,12 @@ static void FillTimetableArrivalDepartureTable(const Vehicle *v, VehicleOrderID if (travelling || i != start) { if (!CanDetermineTimeTaken(order, true)) return; sum += order->GetTimetabledTravel(); - table[i].arrival = sum; + table[i].arrival = sum / day_len; } if (!CanDetermineTimeTaken(order, false)) return; sum += order->GetTimetabledWait(); - table[i].departure = sum; + table[i].departure = sum / day_len; } ++i; @@ -136,7 +137,7 @@ static void FillTimetableArrivalDepartureTable(const Vehicle *v, VehicleOrderID if (!travelling) { if (!CanDetermineTimeTaken(order, true)) return; sum += order->GetTimetabledTravel(); - table[i].arrival = sum; + table[i].arrival = sum / day_len; } } @@ -198,7 +199,7 @@ struct TimetableWindow : Window { assert(HasBit(v->vehicle_flags, VF_TIMETABLE_STARTED)); bool travelling = (!(v->current_order.IsType(OT_LOADING) || v->current_order.IsType(OT_WAITING)) || v->current_order.GetNonStopType() == ONSF_STOP_EVERYWHERE); - Ticks start_time = _date_fract - v->current_order_time; + Ticks start_time = _date_fract - (v->current_order_time / _settings_game.economy.day_length_factor); FillTimetableArrivalDepartureTable(v, v->cur_real_order_index % v->GetNumOrders(), travelling, table, start_time); @@ -462,7 +463,7 @@ struct TimetableWindow : Window { int y = r.top + WD_FRAMERECT_TOP; bool show_late = this->show_expected && v->lateness_counter > DATE_UNIT_SIZE; - Ticks offset = show_late ? 0 : -v->lateness_counter; + Ticks offset = show_late ? 0 : -(v->lateness_counter / _settings_game.economy.day_length_factor); bool rtl = _current_text_dir == TD_RTL; int abbr_left = rtl ? r.right - WD_FRAMERECT_RIGHT - this->deparr_abbr_width : r.left + WD_FRAMERECT_LEFT; @@ -589,7 +590,7 @@ struct TimetableWindow : Window { if (order != NULL) { uint time = (selected % 2 == 1) ? order->GetTravelTime() : order->GetWaitTime(); - if (!_settings_client.gui.timetable_in_ticks) time /= DATE_UNIT_SIZE; + if (!_settings_client.gui.timetable_in_ticks) time /= (DATE_UNIT_SIZE * _settings_game.economy.day_length_factor); if (time != 0) { SetDParam(0, time); @@ -683,7 +684,7 @@ struct TimetableWindow : Window { if (this->query_is_speed_query) { val = ConvertDisplaySpeedToKmhishSpeed(val); } else { - if (!_settings_client.gui.timetable_in_ticks) val *= DATE_UNIT_SIZE; + if (!_settings_client.gui.timetable_in_ticks) val *= DATE_UNIT_SIZE * _settings_game.economy.day_length_factor; } uint32 p2 = minu(val, UINT16_MAX); diff --git a/src/tree_cmd.cpp b/src/tree_cmd.cpp index e52528a243..630321b837 100644 --- a/src/tree_cmd.cpp +++ b/src/tree_cmd.cpp @@ -118,6 +118,50 @@ static void PlantTreesOnTile(TileIndex tile, TreeType treetype, uint count, uint MakeTree(tile, treetype, count, growth, ground, density); } +/** + * Previous value of _settings_game.construction.trees_around_snow_line_range + * used to calculate _arctic_tree_occurance + */ +static uint8 _previous_trees_around_snow_line_range = 255; + +/** + * Array of probabilities for artic trees to appear, + * by normalised distance from snow line + */ +static uint8 _arctic_tree_occurance[24]; + +/** Recalculate _arctic_tree_occurance */ +static void RecalculateArcticTreeOccuranceArray() +{ + /* + * Approximate: 256 * exp(-3 * distance / range) + * By using: + * 256 * ((1 + (-3 * distance / range) / 6) ** 6) + * ((256 - (128 * distance / range)) ** 6) >> (5 * 8); + */ + uint8 range = _settings_game.construction.trees_around_snow_line_range; + _previous_trees_around_snow_line_range = range; + _arctic_tree_occurance[0] = 255; + uint i = 1; + for (; i < lengthof(_arctic_tree_occurance); i++) { + if (range == 0) break; + uint x = 256 - ((128 * i) / range); + uint32 output = x; + output *= x; + output *= x; + output *= x; + output >>= 16; + output *= x; + output *= x; + output >>= 24; + if (output == 0) break; + _arctic_tree_occurance[i] = output; + } + for (; i < lengthof(_arctic_tree_occurance); i++) { + _arctic_tree_occurance[i] = 0; + } +} + /** * Get a random TreeType for the given tile based on a given seed * @@ -135,9 +179,30 @@ static TreeType GetRandomTreeType(TileIndex tile, uint seed) case LT_TEMPERATE: return (TreeType)(seed * TREE_COUNT_TEMPERATE / 256 + TREE_TEMPERATE); - case LT_ARCTIC: - return (TreeType)(seed * TREE_COUNT_SUB_ARCTIC / 256 + TREE_SUB_ARCTIC); + case LT_ARCTIC: { + if (!_settings_game.construction.trees_around_snow_line_enabled) { + return (TreeType)(seed * TREE_COUNT_SUB_ARCTIC / 256 + TREE_SUB_ARCTIC); + } + uint8 range = _settings_game.construction.trees_around_snow_line_range; + if (range != _previous_trees_around_snow_line_range) RecalculateArcticTreeOccuranceArray(); + + int z = GetTileZ(tile); + int height_above_snow_line = z - _settings_game.game_creation.snow_line_height; + uint normalised_distance = (height_above_snow_line < 0) ? -height_above_snow_line : height_above_snow_line + 1; + bool arctic_tree = false; + if (normalised_distance < lengthof(_arctic_tree_occurance)) { + uint adjusted_seed = (seed ^ tile) & 0xFF; + arctic_tree = adjusted_seed < _arctic_tree_occurance[normalised_distance]; + } + if (height_above_snow_line < 0) { + /* Below snow level mixed forest. */ + return (arctic_tree) ? (TreeType)(seed * TREE_COUNT_SUB_ARCTIC / 256 + TREE_SUB_ARCTIC) : (TreeType)(seed * TREE_COUNT_TEMPERATE / 256 + TREE_TEMPERATE); + } else { + /* Above is Arctic trees and thinning out. */ + return (arctic_tree) ? (TreeType)(seed * TREE_COUNT_SUB_ARCTIC / 256 + TREE_SUB_ARCTIC) : TREE_INVALID; + } + } case LT_TROPIC: switch (GetTropicZone(tile)) { case TROPICZONE_NORMAL: return (TreeType)(seed * TREE_COUNT_SUB_TROPICAL / 256 + TREE_SUB_TROPICAL); diff --git a/src/viewport.cpp b/src/viewport.cpp index 86f2df4d30..5727740825 100644 --- a/src/viewport.cpp +++ b/src/viewport.cpp @@ -162,7 +162,7 @@ typedef SmallVector StringSpriteToDrawVector; typedef SmallVector ParentSpriteToDrawVector; typedef SmallVector ChildScreenSpriteToDrawVector; -typedef std::list> RankOrderTypeList; +typedef std::list > RankOrderTypeList; typedef std::map RouteStepsMap; /** Data structure storing rendering information */ diff --git a/src/water_cmd.cpp b/src/water_cmd.cpp index b158cb147c..ecfe4a6e48 100644 --- a/src/water_cmd.cpp +++ b/src/water_cmd.cpp @@ -387,7 +387,7 @@ bool RiverModifyDesertZone(TileIndex tile, void *) * @param tile end tile of stretch-dragging * @param flags type of operation * @param p1 start tile of stretch-dragging - * @param p2 waterclass to build. sea and river can only be built in scenario editor + * @param p2 waterclass to build. sea and river can only be built in scenario editor, unless enable_build_river is enabled * @param text unused * @return the cost of this operation or an error */ @@ -397,7 +397,13 @@ CommandCost CmdBuildCanal(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 if (p1 >= MapSize() || wc == WATER_CLASS_INVALID) return CMD_ERROR; /* Outside of the editor you can only build canals, not oceans */ - if (wc != WATER_CLASS_CANAL && _game_mode != GM_EDITOR) return CMD_ERROR; + if (_game_mode != GM_EDITOR) { + if (wc == WATER_CLASS_RIVER) { + if (!_settings_game.construction.enable_build_river) return CMD_ERROR; + } else if (wc != WATER_CLASS_CANAL) { + return CMD_ERROR; + } + } TileArea ta(tile, p1); @@ -452,6 +458,9 @@ CommandCost CmdBuildCanal(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 } cost.AddCost(_price[PR_BUILD_CANAL]); + if (wc == WATER_CLASS_RIVER) { + cost.AddCost(_price[PR_BUILD_CANAL] * 3); + } } if (cost.GetCost() == 0) { diff --git a/src/widgets/order_widget.h b/src/widgets/order_widget.h index aaf86a0ea9..fade59c3ea 100644 --- a/src/widgets/order_widget.h +++ b/src/widgets/order_widget.h @@ -32,6 +32,8 @@ enum OrderWidgets { WID_O_COND_VARIABLE, ///< Choose condition variable. WID_O_COND_COMPARATOR, ///< Choose condition type. WID_O_COND_VALUE, ///< Choose condition value. + WID_O_COND_CARGO, + WID_O_SEL_COND_VALUE, ///< widget for conditional value or conditional cargo type. WID_O_SEL_TOP_LEFT, ///< #NWID_SELECTION widget for left part of the top row of the 'your train' order window. WID_O_SEL_TOP_MIDDLE, ///< #NWID_SELECTION widget for middle part of the top row of the 'your train' order window. WID_O_SEL_TOP_RIGHT, ///< #NWID_SELECTION widget for right part of the top row of the 'your train' order window.