From 8deac54c9fc46247472ddb307a3643fb27d3de23 Mon Sep 17 00:00:00 2001 From: Jonathan G Rennison Date: Sun, 4 Sep 2016 10:29:20 +0100 Subject: [PATCH 01/11] Remove mutex from SmallStack/StationIDStack. It is only ever used from the main thread. --- src/core/smallstack_type.hpp | 27 ++++----------------------- 1 file changed, 4 insertions(+), 23 deletions(-) diff --git a/src/core/smallstack_type.hpp b/src/core/smallstack_type.hpp index 31edba0817..99ad5d1268 100644 --- a/src/core/smallstack_type.hpp +++ b/src/core/smallstack_type.hpp @@ -13,7 +13,6 @@ #define SMALLSTACK_TYPE_HPP #include "smallvec_type.hpp" -#include "../thread/thread.h" /** * A simplified pool which stores values instead of pointers and doesn't @@ -23,15 +22,7 @@ template class SimplePool { public: - inline SimplePool() : first_unused(0), first_free(0), mutex(ThreadMutex::New()) {} - inline ~SimplePool() { delete this->mutex; } - - /** - * Get the mutex. We don't lock the mutex in the pool methods as the - * SmallStack isn't necessarily in a consistent state after each method. - * @return Mutex. - */ - inline ThreadMutex *GetMutex() { return this->mutex; } + inline SimplePool() : first_unused(0), first_free(0) {} /** * Get the item at position index. @@ -86,7 +77,6 @@ private: Tindex first_unused; Tindex first_free; - ThreadMutex *mutex; SmallVector data; }; @@ -124,10 +114,8 @@ struct SmallStackItem { * 5. You can choose your own index type, so that you can align it with your * value type. E.G. value types of 16 bits length like to be combined with * index types of the same length. - * 6. All accesses to the underlying pool are guarded by a mutex and atomic in - * the sense that the mutex stays locked until the pool has reacquired a - * consistent state. This means that even though a common data structure is - * used the SmallStack is still reentrant. + * 6. This data structure is only ever used from the main thread, so + * accesses to the underlying pool are not guarded by locks. * @tparam Titem Value type to be used. * @tparam Tindex Index type to use for the pool. * @tparam Tinvalid Invalid item to keep at the bottom of each stack. @@ -195,7 +183,6 @@ public: inline void Push(const Titem &item) { if (this->value != Tinvalid) { - ThreadMutexLocker lock(_pool.GetMutex()); Tindex new_item = _pool.Create(); if (new_item != Tmax_size) { PooledSmallStack &pushed = _pool.Get(new_item); @@ -218,17 +205,13 @@ public: if (this->next == Tmax_size) { this->value = Tinvalid; } else { - ThreadMutexLocker lock(_pool.GetMutex()); PooledSmallStack &popped = _pool.Get(this->next); this->value = popped.value; if (popped.branch_count == 0) { _pool.Destroy(this->next); } else { --popped.branch_count; - /* We can't use Branch() here as we already have the mutex.*/ - if (popped.next != Tmax_size) { - ++(_pool.Get(popped.next).branch_count); - } + this->Branch(); } /* Accessing popped here is no problem as the pool will only set * the validity flag, not actually delete the item, on Destroy(). @@ -257,7 +240,6 @@ public: { if (item == Tinvalid || item == this->value) return true; if (this->next != Tmax_size) { - ThreadMutexLocker lock(_pool.GetMutex()); const SmallStack *in_list = this; do { in_list = static_cast( @@ -277,7 +259,6 @@ protected: inline void Branch() { if (this->next != Tmax_size) { - ThreadMutexLocker lock(_pool.GetMutex()); ++(_pool.Get(this->next).branch_count); } } From c1c983ea163dccfb6990703b18ce445a8acd870c Mon Sep 17 00:00:00 2001 From: Jonathan G Rennison Date: Sat, 3 Sep 2016 22:18:01 +0100 Subject: [PATCH 02/11] Import cargo type orders patch https://www.tt-forums.net/viewtopic.php?p=1047749#p1047749 Port to current trunk, resolve various conflicts, etc. Adjust bit allocations for CmdModifyOrder. Use save_ext framework for added order flags. --- src/economy.cpp | 93 +++++-- src/lang/english.txt | 32 +++ src/order_base.h | 62 ++++- src/order_cmd.cpp | 36 ++- src/order_gui.cpp | 418 +++++++++++++++++++++++++++++-- src/order_type.h | 4 + src/saveload/extended_ver_sl.cpp | 2 + src/saveload/extended_ver_sl.h | 1 + src/saveload/order_sl.cpp | 1 + src/script/api/script_window.hpp | 27 ++ src/widgets/order_widget.h | 17 ++ src/window_type.h | 12 + 12 files changed, 661 insertions(+), 44 deletions(-) diff --git a/src/economy.cpp b/src/economy.cpp index cdfa059e86..376e7da82d 100644 --- a/src/economy.cpp +++ b/src/economy.cpp @@ -1240,6 +1240,35 @@ Money CargoPayment::PayTransfer(const CargoPacket *cp, uint count) return profit; // account for the (virtual) profit already made for the cargo packet } +/** + * Returns the load type of a vehicle. + * In case of cargo type order, the load type returned depends of the cargo carriable by the vehicle. + * @pre v != NULL + * @param v A pointer to a vehicle. + * @return the load type of this vehicle. + */ +static OrderLoadFlags GetLoadType(const Vehicle *v) +{ + const Vehicle *front = v->First(); + OrderLoadFlags olf = front->current_order.GetLoadType(); + if (olf == OLFB_CARGO_TYPE_LOAD) olf = front->current_order.GetLoadType(v->cargo_type); + return olf; +} + +/** + * Returns the unload type of a vehicle. + * In case of cargo type order, the unload type returned depends of the cargo carriable be the vehicle. + * @param v A pointer to a vehicle. + * @return The unload type of this vehicle. + */ +static OrderUnloadFlags GetUnloadType(const Vehicle *v) +{ + const Vehicle *front = v->First(); + OrderUnloadFlags ouf = front->current_order.GetUnloadType(); + if (ouf == OUFB_CARGO_TYPE_UNLOAD) ouf = front->current_order.GetUnloadType(v->cargo_type); + return ouf; +} + /** * Prepare the vehicle to be unloaded. * @param curr_station the station where the consist is at the moment @@ -1267,12 +1296,13 @@ void PrepareUnload(Vehicle *front_v) if (front_v->orders.list == NULL || (front_v->current_order.GetUnloadType() & OUFB_NO_UNLOAD) == 0) { Station *st = Station::Get(front_v->last_station_visited); for (Vehicle *v = front_v; v != NULL; v = v->Next()) { + if (GetUnloadType(v) & OUFB_NO_UNLOAD) continue; const GoodsEntry *ge = &st->goods[v->cargo_type]; if (v->cargo_cap > 0 && v->cargo.TotalCount() > 0) { v->cargo.Stage( HasBit(ge->status, GoodsEntry::GES_ACCEPTANCE), front_v->last_station_visited, next_station, - front_v->current_order.GetUnloadType(), ge, + GetUnloadType(v), ge, front_v->cargo_payment); if (v->cargo.UnloadCount() > 0) SetBit(v->vehicle_flags, VF_CARGO_UNLOADING); } @@ -1427,6 +1457,7 @@ struct FinalizeRefitAction Station *st; ///< Station to reserve cargo from. StationIDStack &next_station; ///< Next hops to reserve cargo for. bool do_reserve; ///< If the vehicle should reserve. + Vehicle *cargo_type_loading; ///< Non-null if vehicle should reserve if the cargo type of the vehicle is a cargo-specific full-load order using this pointer /** * Create a finalizing action. @@ -1435,8 +1466,8 @@ struct FinalizeRefitAction * @param next_station Next hops to reserve cargo for. * @param do_reserve If we should reserve cargo or just add up the capacities. */ - FinalizeRefitAction(CargoArray &consist_capleft, Station *st, StationIDStack &next_station, bool do_reserve) : - consist_capleft(consist_capleft), st(st), next_station(next_station), do_reserve(do_reserve) {} + FinalizeRefitAction(CargoArray &consist_capleft, Station *st, StationIDStack &next_station, bool do_reserve, Vehicle *cargo_type_loading) : + consist_capleft(consist_capleft), st(st), next_station(next_station), do_reserve(do_reserve), cargo_type_loading(cargo_type_loading) {} /** * Reserve cargo from the station and update the remaining consist capacities with the @@ -1446,7 +1477,7 @@ struct FinalizeRefitAction */ bool operator()(Vehicle *v) { - if (this->do_reserve) { + if (this->do_reserve || (cargo_type_loading == NULL || (cargo_type_loading->current_order.GetLoadType(v->cargo_type) & OLFB_FULL_LOAD))) { this->st->goods[v->cargo_type].cargo.Reserve(v->cargo_cap - v->cargo.RemainingCount(), &v->cargo, st->xy, this->next_station); } @@ -1513,7 +1544,8 @@ static void HandleStationRefit(Vehicle *v, CargoArray &consist_capleft, Station /* Add new capacity to consist capacity and reserve cargo */ IterateVehicleParts(v_start, FinalizeRefitAction(consist_capleft, st, next_station, - is_auto_refit || (v->First()->current_order.GetLoadType() & OLFB_FULL_LOAD) != 0)); + is_auto_refit || (v->First()->current_order.GetLoadType() & OLFB_FULL_LOAD) != 0, + (v->First()->current_order.GetLoadType() == OLFB_CARGO_TYPE_LOAD) ? v->First() : NULL)); cur_company.Restore(); } @@ -1521,12 +1553,14 @@ static void HandleStationRefit(Vehicle *v, CargoArray &consist_capleft, Station struct ReserveCargoAction { Station *st; StationIDStack *next_station; + Vehicle *cargo_type_loading; - ReserveCargoAction(Station *st, StationIDStack *next_station) : - st(st), next_station(next_station) {} + ReserveCargoAction(Station *st, StationIDStack *next_station, Vehicle *cargo_type_loading) : + st(st), next_station(next_station), cargo_type_loading(cargo_type_loading) {} bool operator()(Vehicle *v) { + if (cargo_type_loading != NULL && !(cargo_type_loading->current_order.GetLoadType(v->cargo_type) & OLFB_FULL_LOAD)) return true; if (v->cargo_cap > v->cargo.RemainingCount()) { st->goods[v->cargo_type].cargo.Reserve(v->cargo_cap - v->cargo.RemainingCount(), &v->cargo, st->xy, *next_station); @@ -1544,8 +1578,9 @@ struct ReserveCargoAction { * @param u Front of the loading vehicle consist. * @param consist_capleft If given, save free capacities after reserving there. * @param next_station Station(s) the vehicle will stop at next. + * @param cargo_type_loading check cargo-specific loading type */ -static void ReserveConsist(Station *st, Vehicle *u, CargoArray *consist_capleft, StationIDStack *next_station) +static void ReserveConsist(Station *st, Vehicle *u, CargoArray *consist_capleft, StationIDStack *next_station, bool cargo_type_loading) { /* If there is a cargo payment not all vehicles of the consist have tried to do the refit. * In that case, only reserve if it's a fixed refit and the equivalent of "articulated chain" @@ -1561,9 +1596,10 @@ static void ReserveConsist(Station *st, Vehicle *u, CargoArray *consist_capleft, (v->type != VEH_TRAIN || !Train::From(v)->IsRearDualheaded()) && (v->type != VEH_AIRCRAFT || Aircraft::From(v)->IsNormalAircraft()) && (must_reserve || u->current_order.GetRefitCargo() == v->cargo_type)) { - IterateVehicleParts(v, ReserveCargoAction(st, next_station)); + IterateVehicleParts(v, ReserveCargoAction(st, next_station, cargo_type_loading ? u : NULL)); } if (consist_capleft == NULL || v->cargo_cap == 0) continue; + if (cargo_type_loading && !(u->current_order.GetLoadType(v->cargo_type) & OLFB_FULL_LOAD)) continue; (*consist_capleft)[v->cargo_type] += v->cargo_cap - v->cargo.RemainingCount(); } } @@ -1603,11 +1639,20 @@ static void LoadUnloadVehicle(Vehicle *front) StationIDStack next_station = front->GetNextStoppingStation(); bool use_autorefit = front->current_order.IsRefit() && front->current_order.GetRefitCargo() == CT_AUTO_REFIT; CargoArray consist_capleft; - if (_settings_game.order.improved_load && use_autorefit ? - front->cargo_payment == NULL : (front->current_order.GetLoadType() & OLFB_FULL_LOAD) != 0) { - ReserveConsist(st, front, - (use_autorefit && front->load_unload_ticks != 0) ? &consist_capleft : NULL, - &next_station); + if (_settings_game.order.improved_load && use_autorefit) { + if (front->cargo_payment == NULL) { + ReserveConsist(st, front, + (use_autorefit && front->load_unload_ticks != 0) ? &consist_capleft : NULL, + &next_station, + false); + } + } else { + if ((front->current_order.GetLoadType() & OLFB_FULL_LOAD) || (front->current_order.GetLoadType() == OLFB_CARGO_TYPE_LOAD)) { + ReserveConsist(st, front, + (use_autorefit && front->load_unload_ticks != 0) ? &consist_capleft : NULL, + &next_station, + (front->current_order.GetLoadType() == OLFB_CARGO_TYPE_LOAD)); + } } /* We have not waited enough time till the next round of loading/unloading */ @@ -1647,7 +1692,7 @@ static void LoadUnloadVehicle(Vehicle *front) GoodsEntry *ge = &st->goods[v->cargo_type]; - if (HasBit(v->vehicle_flags, VF_CARGO_UNLOADING) && (front->current_order.GetUnloadType() & OUFB_NO_UNLOAD) == 0) { + if (HasBit(v->vehicle_flags, VF_CARGO_UNLOADING) && (GetUnloadType(v) & OUFB_NO_UNLOAD) == 0) { uint cargo_count = v->cargo.UnloadCount(); uint amount_unloaded = _settings_game.order.gradual_loading ? min(cargo_count, load_amount) : cargo_count; bool remaining = false; // Are there cargo entities in this vehicle that can still be unloaded here? @@ -1657,7 +1702,7 @@ static void LoadUnloadVehicle(Vehicle *front) if (!HasBit(ge->status, GoodsEntry::GES_ACCEPTANCE) && v->cargo.ActionCount(VehicleCargoList::MTA_DELIVER) > 0) { /* The station does not accept our goods anymore. */ - if (front->current_order.GetUnloadType() & (OUFB_TRANSFER | OUFB_UNLOAD)) { + if (GetUnloadType(v) & (OUFB_TRANSFER | OUFB_UNLOAD)) { /* Transfer instead of delivering. */ v->cargo.Reassign( v->cargo.ActionCount(VehicleCargoList::MTA_DELIVER), INVALID_STATION); @@ -1714,7 +1759,7 @@ static void LoadUnloadVehicle(Vehicle *front) } /* Do not pick up goods when we have no-load set or loading is stopped. */ - if (front->current_order.GetLoadType() & OLFB_NO_LOAD || HasBit(front->vehicle_flags, VF_STOP_LOADING)) continue; + if (GetLoadType(v) & OLFB_NO_LOAD || HasBit(front->vehicle_flags, VF_STOP_LOADING)) continue; /* This order has a refit, if this is the first vehicle part carrying cargo and the whole vehicle is empty, try refitting. */ if (front->current_order.IsRefit() && artic_part == 1) { @@ -1817,6 +1862,16 @@ static void LoadUnloadVehicle(Vehicle *front) if (!anything_unloaded) delete payment; ClrBit(front->vehicle_flags, VF_STOP_LOADING); + + bool has_full_load_order = front->current_order.GetLoadType() & OLFB_FULL_LOAD; + if (front->current_order.GetLoadType() == OLFB_CARGO_TYPE_LOAD) { + for (Vehicle *v = front; v != NULL; v = v->Next()) { + if (front->current_order.GetLoadType(v->cargo_type) & OLFB_FULL_LOAD) { + has_full_load_order = true; + break; + } + } + } if (anything_loaded || anything_unloaded) { if (_settings_game.order.gradual_loading) { /* The time it takes to load one 'slice' of cargo or passengers depends @@ -1827,7 +1882,7 @@ static void LoadUnloadVehicle(Vehicle *front) } /* We loaded less cargo than possible for all cargo types and it's not full * load and we're not supposed to wait any longer: stop loading. */ - if (!anything_unloaded && full_load_amount == 0 && reservation_left == 0 && !(front->current_order.GetLoadType() & OLFB_FULL_LOAD) && + if (!anything_unloaded && full_load_amount == 0 && reservation_left == 0 && !has_full_load_order && front->current_order_time >= (uint)max(front->current_order.GetTimetabledWait() - front->lateness_counter, 0)) { SetBit(front->vehicle_flags, VF_STOP_LOADING); } @@ -1836,7 +1891,7 @@ static void LoadUnloadVehicle(Vehicle *front) } else { UpdateLoadUnloadTicks(front, st, 20); // We need the ticks for link refreshing. bool finished_loading = true; - if (front->current_order.GetLoadType() & OLFB_FULL_LOAD) { + if (has_full_load_order) { if (front->current_order.GetLoadType() == OLF_FULL_LOAD_ANY) { /* if the aircraft carries passengers and is NOT full, then * continue loading, no matter how much mail is in */ diff --git a/src/lang/english.txt b/src/lang/english.txt index aaa794bffd..34aaf8d837 100644 --- a/src/lang/english.txt +++ b/src/lang/english.txt @@ -3755,6 +3755,7 @@ STR_ORDER_DROP_LOAD_IF_POSSIBLE :Load if availab STR_ORDER_DROP_FULL_LOAD_ALL :Full load all cargo STR_ORDER_DROP_FULL_LOAD_ANY :Full load any cargo STR_ORDER_DROP_NO_LOADING :No loading +STR_ORDER_DROP_CARGO_TYPE_LOAD :Load by cargo type STR_ORDER_TOOLTIP_FULL_LOAD :{BLACK}Change the loading behaviour of the highlighted order STR_ORDER_TOGGLE_UNLOAD :{BLACK}Unload all @@ -3762,6 +3763,7 @@ STR_ORDER_DROP_UNLOAD_IF_ACCEPTED :Unload if accep STR_ORDER_DROP_UNLOAD :Unload all STR_ORDER_DROP_TRANSFER :Transfer STR_ORDER_DROP_NO_UNLOADING :No unloading +STR_ORDER_DROP_CARGO_TYPE_UNLOAD :Unload by cargo type STR_ORDER_TOOLTIP_UNLOAD :{BLACK}Change the unloading behaviour of the highlighted order STR_ORDER_REFIT :{BLACK}Refit @@ -3845,31 +3847,48 @@ STR_ORDER_IMPLICIT :(Implicit) STR_ORDER_FULL_LOAD :(Full load) STR_ORDER_FULL_LOAD_ANY :(Full load any cargo) STR_ORDER_NO_LOAD :(No loading) +STR_ORDER_CARGO_TYPE_LOAD :(Load by cargo type) STR_ORDER_UNLOAD :(Unload and take cargo) STR_ORDER_UNLOAD_FULL_LOAD :(Unload and wait for full load) STR_ORDER_UNLOAD_FULL_LOAD_ANY :(Unload and wait for any full load) STR_ORDER_UNLOAD_NO_LOAD :(Unload and leave empty) +STR_ORDER_UNLOAD_CARGO_TYPE_LOAD :(Unload and wait for load by cargo type) STR_ORDER_TRANSFER :(Transfer and take cargo) STR_ORDER_TRANSFER_FULL_LOAD :(Transfer and wait for full load) STR_ORDER_TRANSFER_FULL_LOAD_ANY :(Transfer and wait for any full load) STR_ORDER_TRANSFER_NO_LOAD :(Transfer and leave empty) +STR_ORDER_TRANSFER_CARGO_TYPE_LOAD :(Transfer and wait for load by cargo type) STR_ORDER_NO_UNLOAD :(No unloading and take cargo) STR_ORDER_NO_UNLOAD_FULL_LOAD :(No unloading and wait for full load) STR_ORDER_NO_UNLOAD_FULL_LOAD_ANY :(No unloading and wait for any full load) STR_ORDER_NO_UNLOAD_NO_LOAD :(No unloading and no loading) +STR_ORDER_NO_UNLOAD_CARGO_TYPE_LOAD :(No unloading and wait for load by cargo type) +STR_ORDER_CARGO_TYPE_UNLOAD :(Unload by cargo type) +STR_ORDER_CARGO_TYPE_UNLOAD_FULL_LOAD :(Unload by cargo type and wait for full load) +STR_ORDER_CARGO_TYPE_UNLOAD_FULL_LOAD_ANY :(Unload by cargo type and wait for any full load) +STR_ORDER_CARGO_TYPE_UNLOAD_NO_LOAD :(Unload by cargo type and leave empty) +STR_ORDER_CARGO_TYPE_UNLOAD_CARGO_TYPE_LOAD :(Unload by cargo type and wait for load by cargo type) STR_ORDER_AUTO_REFIT :(Refit to {STRING}) STR_ORDER_FULL_LOAD_REFIT :(Full load with refit to {STRING}) STR_ORDER_FULL_LOAD_ANY_REFIT :(Full load any cargo with refit to {STRING}) +STR_ORDER_CARGO_TYPE_LOAD_REFIT :(Load by cargo type with auto-refit to {STRING}) STR_ORDER_UNLOAD_REFIT :(Unload and take cargo with refit to {STRING}) STR_ORDER_UNLOAD_FULL_LOAD_REFIT :(Unload and wait for full load with refit to {STRING}) STR_ORDER_UNLOAD_FULL_LOAD_ANY_REFIT :(Unload and wait for any full load with refit to {STRING}) +STR_ORDER_UNLOAD_CARGO_TYPE_LOAD_REFIT :(Unload and wait for load by cargo type with auto-refit to {STRING}) STR_ORDER_TRANSFER_REFIT :(Transfer and take cargo with refit to {STRING}) STR_ORDER_TRANSFER_FULL_LOAD_REFIT :(Transfer and wait for full load with refit to {STRING}) STR_ORDER_TRANSFER_FULL_LOAD_ANY_REFIT :(Transfer and wait for any full load with refit to {STRING}) +STR_ORDER_TRANSFER_CARGO_TYPE_LOAD_REFIT :(Transfer and wait for load by cargo type with auto-refit to {STRING}) STR_ORDER_NO_UNLOAD_REFIT :(No unloading and take cargo with refit to {STRING}) STR_ORDER_NO_UNLOAD_FULL_LOAD_REFIT :(No unloading and wait for full load with refit to {STRING}) STR_ORDER_NO_UNLOAD_FULL_LOAD_ANY_REFIT :(No unloading and wait for any full load with refit to {STRING}) +STR_ORDER_NO_UNLOAD_CARGO_TYPE_LOAD_REFIT :(No unloading and wait for load by cargo type with auto-refit to {STRING}) +STR_ORDER_CARGO_TYPE_UNLOAD_REFIT :(Unload by cargo type and take cargo with auto-refit to {STRING}) +STR_ORDER_CARGO_TYPE_UNLOAD_FULL_LOAD_REFIT :(Unload by cargo type and wait for full load with auto-refit to {STRING}) +STR_ORDER_CARGO_TYPE_UNLOAD_FULL_LOAD_ANY_REFIT :(Unload by cargo type and wait for any full load with auto-refit to {STRING}) +STR_ORDER_CARGO_TYPE_UNLOAD_CARGO_TYPE_LOAD_REFIT :(Unload by cargo type and wait for load by cargo type with auto-refit to {STRING}) STR_ORDER_AUTO_REFIT_ANY :available cargo @@ -3953,6 +3972,19 @@ STR_DATE_DAY_TOOLTIP :{BLACK}Select d STR_DATE_MONTH_TOOLTIP :{BLACK}Select month STR_DATE_YEAR_TOOLTIP :{BLACK}Select year +# Cargo type orders Window +STR_CARGO_TYPE_ORDERS_LOAD_CAPTION :{WHITE}{VEHICLE} ({NUM}: Load at {STATION}) +STR_CARGO_TYPE_ORDERS_UNLOAD_CAPTION :{WHITE}{VEHICLE} ({NUM}: Unload at {STATION}) +STR_CARGO_TYPE_ORDERS_LOAD_TITLE :{GOLD}Select load order per cargo type: +STR_CARGO_TYPE_ORDERS_UNLOAD_TITLE :{GOLD}Select unload order per cargo type: +STR_CARGO_TYPE_ORDERS_CLOSE_BUTTON :{BLACK}Close + +STR_CARGO_TYPE_ORDERS_DROP_FULL_LOAD :Full load +STR_CARGO_TYPE_LOAD_ORDERS_DROP_TOOLTIP :{BLACK}Change the loading behaviour for this cargo type +STR_CARGO_TYPE_UNLOAD_ORDERS_DROP_TOOLTIP :{BLACK}Change the unloading behaviour for this cargo type + +STR_CARGO_TYPE_ORDERS_SET_TO_ALL_LABEL :{BLACK}Set all to: +STR_CARGO_TYPE_ORDERS_SET_TO_ALL_TOOLTIP :{BLACK}Set all cargo type orders to the one selected by the dropdown # AI debug window STR_AI_DEBUG :{WHITE}AI/Game Script Debug diff --git a/src/order_base.h b/src/order_base.h index a67cf69bb4..93351dd7c8 100644 --- a/src/order_base.h +++ b/src/order_base.h @@ -43,6 +43,8 @@ private: CargoID refit_cargo; ///< Refit CargoID + uint8 cargo_type_flags[NUM_CARGO]; ///< Load/unload types for each cargo type. + 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. uint16 max_speed; ///< How fast the vehicle may go on the way to the destination. @@ -126,9 +128,35 @@ public: void SetRefit(CargoID cargo); /** How must the consist be loaded? */ - inline OrderLoadFlags GetLoadType() const { return (OrderLoadFlags)GB(this->flags, 4, 3); } + inline OrderLoadFlags GetLoadType() const { return (OrderLoadFlags)GB(this->flags, 4, 4); } + + /** + * How must the consist be loaded for this type of cargo? + * @pre GetLoadType() == OLFB_CARGO_TYPE_LOAD + * @param cargo_id The cargo type index. + * @return The load type for this cargo. + */ + inline OrderLoadFlags GetLoadType(CargoID cargo_id) const + { + assert(cargo_id < NUM_CARGO); + return (OrderLoadFlags) GB(this->cargo_type_flags[cargo_id], 4, 4); + } + /** How must the consist be unloaded? */ - inline OrderUnloadFlags GetUnloadType() const { return (OrderUnloadFlags)GB(this->flags, 0, 3); } + inline OrderUnloadFlags GetUnloadType() const { return (OrderUnloadFlags)GB(this->flags, 0, 4); } + + /** + * How must the consist be unloaded for this type of cargo? + * @pre GetUnloadType() == OUFB_CARGO_TYPE_UNLOAD + * @param cargo_id The cargo type index. + * @return The unload type for this cargo. + */ + inline OrderUnloadFlags GetUnloadType(CargoID cargo_id) const + { + assert(cargo_id < NUM_CARGO); + return (OrderUnloadFlags) GB(this->cargo_type_flags[cargo_id], 0, 4); + } + /** At which stations must we stop? */ inline OrderNonStopFlags GetNonStopType() const { return (OrderNonStopFlags)GB(this->type, 6, 2); } /** Where must we stop at the platform? */ @@ -147,9 +175,35 @@ public: inline uint16 GetConditionValue() const { return GB(this->dest, 0, 11); } /** Set how the consist must be loaded. */ - inline void SetLoadType(OrderLoadFlags load_type) { SB(this->flags, 4, 3, load_type); } + inline void SetLoadType(OrderLoadFlags load_type) { SB(this->flags, 4, 4, load_type); } + + /** + * Set how the consist must be loaded for this type of cargo. + * @pre GetLoadType() == OLFB_CARGO_TYPE_LOAD + * @param load_type The load type. + * @param cargo_id The cargo type index. + */ + inline void SetLoadType(OrderLoadFlags load_type, CargoID cargo_id) + { + assert(cargo_id < NUM_CARGO); + SB(this->cargo_type_flags[cargo_id], 4, 4, load_type); + } + /** Set how the consist must be unloaded. */ - inline void SetUnloadType(OrderUnloadFlags unload_type) { SB(this->flags, 0, 3, unload_type); } + inline void SetUnloadType(OrderUnloadFlags unload_type) { SB(this->flags, 0, 4, unload_type); } + + /** + * Set how the consist must be unloaded for this type of cargo. + * @pre GetUnloadType() == OUFB_CARGO_TYPE_UNLOAD + * @param unload_type The unload type. + * @param cargo_id The cargo type index. + */ + inline void SetUnloadType(OrderUnloadFlags unload_type, CargoID cargo_id) + { + assert(cargo_id < NUM_CARGO); + SB(this->cargo_type_flags[cargo_id], 0, 4, unload_type); + } + /** Set whether we must stop at stations or not. */ inline void SetNonStopType(OrderNonStopFlags non_stop_type) { SB(this->type, 6, 2, non_stop_type); } /** Set where we must stop at the platform. */ diff --git a/src/order_cmd.cpp b/src/order_cmd.cpp index 57b29f3f53..9ade1b726a 100644 --- a/src/order_cmd.cpp +++ b/src/order_cmd.cpp @@ -282,6 +282,12 @@ void Order::AssignOrder(const Order &other) this->wait_time = other.wait_time; this->travel_time = other.travel_time; this->max_speed = other.max_speed; + + if (this->GetUnloadType() == OUFB_CARGO_TYPE_UNLOAD || this->GetLoadType() == OLFB_CARGO_TYPE_LOAD) { + for (uint i = 0; i < NUM_CARGO; i++) { + this->cargo_type_flags[i] = other.cargo_type_flags[i]; + } + } } /** @@ -1277,12 +1283,13 @@ CommandCost CmdMoveOrder(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 * @param flags operation to perform * @param p1 various bitstuffed elements * - p1 = (bit 0 - 19) - ID of the vehicle - * - p1 = (bit 24 - 31) - the selected order (if any). If the last order is given, + * - p1 = (bit 20 - 27) - the selected order (if any). If the last order is given, * the order will be inserted before that one * the maximum vehicle order id is 254. * @param p2 various bitstuffed elements * - p2 = (bit 0 - 3) - what data to modify (@see ModifyOrderFlags) * - p2 = (bit 4 - 15) - the data to modify + * - p2 = (bit 16 - 23) - a CargoID for cargo type orders (MOF_CARGO_TYPE_UNLOAD or MOF_CARGO_TYPE_LOAD) * @param text unused * @return the cost of this operation or an error */ @@ -1292,6 +1299,7 @@ CommandCost CmdModifyOrder(TileIndex tile, DoCommandFlag flags, uint32 p1, uint3 VehicleID veh = GB(p1, 0, 20); ModifyOrderFlags mof = Extract(p2); uint16 data = GB(p2, 4, 11); + CargoID cargo_id = (mof == MOF_CARGO_TYPE_UNLOAD || mof == MOF_CARGO_TYPE_LOAD) ? (CargoID) GB(p2, 16, 8) : (CargoID) CT_INVALID; if (mof >= MOF_END) return CMD_ERROR; @@ -1307,7 +1315,7 @@ CommandCost CmdModifyOrder(TileIndex tile, DoCommandFlag flags, uint32 p1, uint3 Order *order = v->GetOrder(sel_ord); switch (order->GetType()) { case OT_GOTO_STATION: - if (mof != MOF_NON_STOP && mof != MOF_STOP_LOCATION && mof != MOF_UNLOAD && mof != MOF_LOAD) return CMD_ERROR; + if (mof != MOF_NON_STOP && mof != MOF_STOP_LOCATION && mof != MOF_UNLOAD && mof != MOF_LOAD && mof != MOF_CARGO_TYPE_UNLOAD && mof != MOF_CARGO_TYPE_LOAD) return CMD_ERROR; break; case OT_GOTO_DEPOT: @@ -1340,17 +1348,27 @@ CommandCost CmdModifyOrder(TileIndex tile, DoCommandFlag flags, uint32 p1, uint3 if (data >= OSL_END) return CMD_ERROR; break; + case MOF_CARGO_TYPE_UNLOAD: + if (cargo_id >= NUM_CARGO) return CMD_ERROR; + if (data == OUFB_CARGO_TYPE_UNLOAD) return CMD_ERROR; + /* FALL THROUGH */ case MOF_UNLOAD: if (order->GetNonStopType() & ONSF_NO_STOP_AT_DESTINATION_STATION) return CMD_ERROR; - if ((data & ~(OUFB_UNLOAD | OUFB_TRANSFER | OUFB_NO_UNLOAD)) != 0) return CMD_ERROR; + if ((data & ~(OUFB_UNLOAD | OUFB_TRANSFER | OUFB_NO_UNLOAD | OUFB_CARGO_TYPE_UNLOAD)) != 0) return CMD_ERROR; /* Unload and no-unload are mutual exclusive and so are transfer and no unload. */ - if (data != 0 && ((data & (OUFB_UNLOAD | OUFB_TRANSFER)) != 0) == ((data & OUFB_NO_UNLOAD) != 0)) return CMD_ERROR; + if (data != 0 && (data & OUFB_CARGO_TYPE_UNLOAD) == 0 && ((data & (OUFB_UNLOAD | OUFB_TRANSFER)) != 0) == ((data & OUFB_NO_UNLOAD) != 0)) return CMD_ERROR; + /* Cargo-type-unload exclude all the other flags. */ + if ((data & OUFB_CARGO_TYPE_UNLOAD) != 0 && data != OUFB_CARGO_TYPE_UNLOAD) return CMD_ERROR; if (data == order->GetUnloadType()) return CMD_ERROR; break; + case MOF_CARGO_TYPE_LOAD: + if (cargo_id >= NUM_CARGO) return CMD_ERROR; + if (data == OLFB_CARGO_TYPE_LOAD || data == OLF_FULL_LOAD_ANY) return CMD_ERROR; + /* FALL THROUGH */ case MOF_LOAD: if (order->GetNonStopType() & ONSF_NO_STOP_AT_DESTINATION_STATION) return CMD_ERROR; - if (data > OLFB_NO_LOAD || data == 1) return CMD_ERROR; + if ((data > OLFB_NO_LOAD && data != OLFB_CARGO_TYPE_LOAD) || data == 1) return CMD_ERROR; if (data == order->GetLoadType()) return CMD_ERROR; break; @@ -1418,11 +1436,19 @@ CommandCost CmdModifyOrder(TileIndex tile, DoCommandFlag flags, uint32 p1, uint3 order->SetUnloadType((OrderUnloadFlags)data); break; + case MOF_CARGO_TYPE_UNLOAD: + order->SetUnloadType((OrderUnloadFlags)data, cargo_id); + break; + case MOF_LOAD: order->SetLoadType((OrderLoadFlags)data); if (data & OLFB_NO_LOAD) order->SetRefit(CT_NO_REFIT); break; + case MOF_CARGO_TYPE_LOAD: + order->SetLoadType((OrderLoadFlags)data, cargo_id); + break; + case MOF_DEPOT_ACTION: { switch (data) { case DA_ALWAYS_GO: diff --git a/src/order_gui.cpp b/src/order_gui.cpp index 3ca29e087a..c43b41ae40 100644 --- a/src/order_gui.cpp +++ b/src/order_gui.cpp @@ -34,9 +34,305 @@ #include "safeguards.h" +enum CargoTypeOrdersWindowVariant { + CTOWV_LOAD = 0, + CTOWV_UNLOAD = 1, +}; + +/** Cargo type orders strings for load dropdowns. */ +static const StringID _cargo_type_load_order_drowdown[] = { + STR_ORDER_DROP_LOAD_IF_POSSIBLE, // OLF_LOAD_IF_POSSIBLE + STR_EMPTY, + STR_CARGO_TYPE_ORDERS_DROP_FULL_LOAD, // OLFB_FULL_LOAD + STR_EMPTY, + STR_ORDER_DROP_NO_LOADING, // OLFB_NO_LOAD + INVALID_STRING_ID +}; +static const uint32 _cargo_type_load_order_drowdown_hidden_mask = 0xA; // 01010 + +/** Cargo type orders strings for unload dropdowns. */ +static const StringID _cargo_type_unload_order_drowdown[] = { + STR_ORDER_DROP_UNLOAD_IF_ACCEPTED, // OUF_UNLOAD_IF_POSSIBLE + STR_ORDER_DROP_UNLOAD, // OUFB_UNLOAD + STR_ORDER_DROP_TRANSFER, // OUFB_TRANSFER + STR_EMPTY, + STR_ORDER_DROP_NO_UNLOADING, // OUFB_NO_UNLOAD + INVALID_STRING_ID +}; +static const uint32 _cargo_type_unload_order_drowdown_hidden_mask = 0x8; // 01000 + +struct CargoTypeOrdersWindow : public Window { +private: + CargoTypeOrdersWindowVariant variant; + + const Vehicle *vehicle; ///< Vehicle owning the orders being displayed and manipulated. + VehicleOrderID order_id; ///< Index of the order concerned by this window. + + static const uint8 CARGO_ICON_WIDTH = 12; + static const uint8 CARGO_ICON_HEIGHT = 8; + + const StringID *cargo_type_order_dropdown; ///< Strings used to populate order dropdowns. + uint32 cargo_type_order_dropdown_hmask; ///< Hidden mask for order dropdowns. + + uint max_cargo_name_width; ///< Greatest width of cargo names. + uint max_cargo_dropdown_width; ///< Greatest width of order names. + + uint set_to_all_dropdown_sel; ///< Selected entry for the 'set to all' dropdown + + /** + * Initialize \c max_cargo_name_width and \c max_cargo_dropdown_width. + * @post \c max_cargo_name_width + * @post \c max_cargo_dropdown_width + */ + void InitMaxWidgetWidth() + { + this->max_cargo_name_width = 0; + for (int i = 0; i < _sorted_standard_cargo_specs_size; i++) { + SetDParam(0, _sorted_cargo_specs[i]->name); + this->max_cargo_name_width = max(this->max_cargo_name_width, GetStringBoundingBox(STR_JUST_STRING).width); + } + this->max_cargo_dropdown_width = 0; + for (int i = 0; this->cargo_type_order_dropdown[i] != INVALID_STRING_ID; i++) { + SetDParam(0, this->cargo_type_order_dropdown[i]); + this->max_cargo_dropdown_width = max(this->max_cargo_dropdown_width, GetStringBoundingBox(STR_JUST_STRING).width); + } + } + + /** Populate the selected entry of order dropdowns. */ + void InitDropdownSelectedTypes() + { + StringID tooltip = STR_CARGO_TYPE_LOAD_ORDERS_DROP_TOOLTIP + this->variant; + const Order *order = this->vehicle->GetOrder(this->order_id); + for (int i = 0; i < _sorted_standard_cargo_specs_size; i++) { + const CargoSpec *cs = _sorted_cargo_specs[i]; + CargoID cargo_id = GetCargoIDByBitnum(cs->bitnum); + uint8 order_type = (this->variant == CTOWV_LOAD) ? (uint8) order->GetLoadType(cargo_id) : (uint8) order->GetUnloadType(cargo_id); + this->GetWidget(WID_CTO_CARGO_DROPDOWN_FIRST + i)->SetDataTip(this->cargo_type_order_dropdown[order_type], tooltip); + } + this->set_to_all_dropdown_sel = 0; + this->GetWidget(WID_CTO_SET_TO_ALL_DROPDOWN)->widget_data = this->cargo_type_order_dropdown[this->set_to_all_dropdown_sel]; + } + + /** + * Returns the load/unload type of this order for the specified cargo. + * @param cargo_id The cargo index for wich we want the load/unload type. + * @return an OrderLoadFlags if \c load_variant = true, an OrderUnloadFlags otherwise. + */ + uint8 GetOrderActionTypeForCargo(CargoID cargo_id) + { + const Order *order = this->vehicle->GetOrder(this->order_id); + return (this->variant == CTOWV_LOAD) ? (uint8) order->GetLoadType(cargo_id) : (uint8) order->GetUnloadType(cargo_id); + } + +public: + /** + * Instantiate a new CargoTypeOrdersWindow. + * @param desc The window description. + * @param v The vehicle the order belongs to. + * @param order_id Which order to display/edit. + * @param variant Which aspect of the order to display/edit: load or unload. + * @pre \c v != NULL + */ + CargoTypeOrdersWindow(WindowDesc *desc, const Vehicle *v, VehicleOrderID order_id, CargoTypeOrdersWindowVariant variant) : Window(desc) + { + this->variant = variant; + this->cargo_type_order_dropdown = (this->variant == CTOWV_LOAD) ? _cargo_type_load_order_drowdown : _cargo_type_unload_order_drowdown; + this->cargo_type_order_dropdown_hmask = (this->variant == CTOWV_LOAD) ? _cargo_type_load_order_drowdown_hidden_mask : _cargo_type_unload_order_drowdown_hidden_mask; + this->InitMaxWidgetWidth(); + + this->vehicle = v; + this->order_id = order_id; + + this->CreateNestedTree(desc); + this->GetWidget(WID_CTO_CAPTION)->SetDataTip(STR_CARGO_TYPE_ORDERS_LOAD_CAPTION + this->variant, STR_TOOLTIP_WINDOW_TITLE_DRAG_THIS); + this->GetWidget(WID_CTO_HEADER)->SetDataTip(STR_CARGO_TYPE_ORDERS_LOAD_TITLE + this->variant, STR_NULL); + this->InitDropdownSelectedTypes(); + this->FinishInitNested(v->index); + + this->owner = v->owner; + } + + virtual void UpdateWidgetSize(int widget, Dimension *size, const Dimension &padding, Dimension *fill, Dimension *resize) + { + if (widget == WID_CTO_HEADER) { + (*size).height = max((*size).height, (uint) WD_FRAMERECT_TOP + FONT_HEIGHT_NORMAL + WD_FRAMERECT_BOTTOM); + } else if (WID_CTO_CARGO_LABEL_FIRST <= widget && widget <= WID_CTO_CARGO_LABEL_LAST) { + (*size).width = max((*size).width, WD_FRAMERECT_LEFT + this->CARGO_ICON_WIDTH + WD_FRAMETEXT_LEFT + this->max_cargo_name_width + WD_FRAMETEXT_RIGHT + padding.width); + (*size).height = max((*size).height, (uint) WD_FRAMERECT_TOP + FONT_HEIGHT_NORMAL + WD_FRAMERECT_BOTTOM); + } else if ((WID_CTO_CARGO_DROPDOWN_FIRST <= widget && widget <= WID_CTO_CARGO_DROPDOWN_LAST) || widget == WID_CTO_SET_TO_ALL_DROPDOWN) { + (*size).width = max((*size).width, WD_DROPDOWNTEXT_LEFT + this->max_cargo_dropdown_width + WD_DROPDOWNTEXT_RIGHT); + (*size).height = max((*size).height, (uint) WD_DROPDOWNTEXT_TOP + FONT_HEIGHT_NORMAL + WD_DROPDOWNTEXT_BOTTOM); + } else if (widget == WID_CTO_SET_TO_ALL_LABEL) { + (*size).width = max((*size).width, this->max_cargo_name_width + WD_FRAMETEXT_RIGHT + padding.width); + (*size).height = max((*size).height, (uint) WD_FRAMERECT_TOP + FONT_HEIGHT_NORMAL + WD_FRAMERECT_BOTTOM); + } + } + + virtual void DrawWidget(const Rect &r, int widget) const + { + if (WID_CTO_CARGO_LABEL_FIRST <= widget && widget <= WID_CTO_CARGO_LABEL_LAST) { + const CargoSpec *cs = _sorted_cargo_specs[widget - WID_CTO_CARGO_LABEL_FIRST]; + bool rtl = (_current_text_dir == TD_RTL); + + /* Draw cargo icon. */ + int rect_left = rtl ? r.right - WD_FRAMETEXT_RIGHT - this->CARGO_ICON_WIDTH : r.left + WD_FRAMERECT_LEFT; + int rect_right = rect_left + this->CARGO_ICON_WIDTH; + int rect_top = r.top + WD_FRAMERECT_TOP + ((r.bottom - WD_FRAMERECT_BOTTOM - r.top - WD_FRAMERECT_TOP) - this->CARGO_ICON_HEIGHT) / 2; + int rect_bottom = rect_top + this->CARGO_ICON_HEIGHT; + GfxFillRect(rect_left, rect_top, rect_right, rect_bottom, PC_BLACK); + GfxFillRect(rect_left + 1, rect_top + 1, rect_right - 1, rect_bottom - 1, cs->legend_colour); + + /* Draw cargo name */ + int text_left = rtl ? r.left + WD_FRAMETEXT_LEFT : rect_right + WD_FRAMETEXT_LEFT; + int text_right = rtl ? rect_left - WD_FRAMETEXT_LEFT : r.right - WD_FRAMETEXT_RIGHT; + int text_top = r.top + WD_FRAMERECT_TOP; + SetDParam(0, cs->name); + DrawString(text_left, text_right, text_top, STR_BLACK_STRING); + } + } + + virtual void OnClick(Point pt, int widget, int click_count) + { + if (widget == WID_CTO_CLOSEBTN) { + delete this; + } else if (WID_CTO_CARGO_DROPDOWN_FIRST <= widget && widget <= WID_CTO_CARGO_DROPDOWN_LAST) { + const CargoSpec *cs = _sorted_cargo_specs[widget - WID_CTO_CARGO_DROPDOWN_FIRST]; + CargoID cargo_id = GetCargoIDByBitnum(cs->bitnum); + + ShowDropDownMenu(this, this->cargo_type_order_dropdown, this->GetOrderActionTypeForCargo(cargo_id), widget, 0, this->cargo_type_order_dropdown_hmask); + } else if (widget == WID_CTO_SET_TO_ALL_DROPDOWN) { + ShowDropDownMenu(this, this->cargo_type_order_dropdown, this->set_to_all_dropdown_sel, widget, 0, this->cargo_type_order_dropdown_hmask); + } + } + + virtual void OnDropdownSelect(int widget, int action_type) + { + if (WID_CTO_CARGO_DROPDOWN_FIRST <= widget && widget <= WID_CTO_CARGO_DROPDOWN_LAST) { + const CargoSpec *cs = _sorted_cargo_specs[widget - WID_CTO_CARGO_DROPDOWN_FIRST]; + CargoID cargo_id = GetCargoIDByBitnum(cs->bitnum); + uint8 order_action_type = this->GetOrderActionTypeForCargo(cargo_id); + + if (action_type == order_action_type) return; + + ModifyOrderFlags mof = (this->variant == CTOWV_LOAD) ? MOF_CARGO_TYPE_LOAD : MOF_CARGO_TYPE_UNLOAD; + DoCommandP(this->vehicle->tile, this->vehicle->index + (this->order_id << 20), mof | (action_type << 4) | (cargo_id << 16), CMD_MODIFY_ORDER | CMD_MSG(STR_ERROR_CAN_T_MODIFY_THIS_ORDER)); + + this->GetWidget(widget)->SetDataTip(this->cargo_type_order_dropdown[this->GetOrderActionTypeForCargo(cargo_id)], STR_CARGO_TYPE_LOAD_ORDERS_DROP_TOOLTIP + this->variant); + this->SetWidgetDirty(widget); + } else if (widget == WID_CTO_SET_TO_ALL_DROPDOWN) { + for (int i = 0; i < _sorted_standard_cargo_specs_size; i++) { + this->OnDropdownSelect(i + WID_CTO_CARGO_DROPDOWN_FIRST, action_type); + } + + if (action_type != (int) this->set_to_all_dropdown_sel) { + this->set_to_all_dropdown_sel = action_type; + this->GetWidget(widget)->widget_data = this->cargo_type_order_dropdown[this->set_to_all_dropdown_sel]; + this->SetWidgetDirty(widget); + } + } + } + + virtual void SetStringParameters(int widget) const + { + if (widget == WID_CTO_CAPTION) { + SetDParam(0, this->vehicle->index); + SetDParam(1, this->order_id + 1); + SetDParam(2, this->vehicle->GetOrder(this->order_id)->GetDestination()); + } + } +}; + +/** + * Make a list of panel for each available cargo type. + * Each panel contains a label to display the cargo name. + * @param biggest_index Storage for collecting the biggest index used in the returned tree + * @return A vertical container of cargo type orders rows. + * @post \c *biggest_index contains the largest used index in the tree. + */ +static NWidgetBase *MakeCargoTypeOrdersRows(int *biggest_index) +{ + NWidgetVertical *ver = new NWidgetVertical; + + for (int i = 0; i < _sorted_standard_cargo_specs_size; i++) { + /* Cargo row */ + NWidgetBackground *panel = new NWidgetBackground(WWT_PANEL, COLOUR_GREY, WID_CTO_CARGO_ROW_FIRST + i); + ver->Add(panel); + NWidgetHorizontal *horiz = new NWidgetHorizontal; + panel->Add(horiz); + /* Cargo label */ + NWidgetBackground *label = new NWidgetBackground(WWT_PANEL, COLOUR_GREY, WID_CTO_CARGO_LABEL_FIRST + i); + label->SetFill(1, 0); + label->SetResize(1, 0); + horiz->Add(label); + /* Orders dropdown */ + NWidgetLeaf *dropdown = new NWidgetLeaf(WWT_DROPDOWN, COLOUR_GREY, WID_CTO_CARGO_DROPDOWN_FIRST + i, STR_NULL, STR_EMPTY); + dropdown->SetFill(1, 0); + dropdown->SetResize(1, 0); + horiz->Add(dropdown); + } + + *biggest_index = WID_CTO_CARGO_DROPDOWN_LAST; + return ver; +} + +/** Widgets definition of CargoTypeOrdersWindow. */ +static const NWidgetPart _nested_cargo_type_orders_widgets[] = { + NWidget(NWID_HORIZONTAL), + NWidget(WWT_CLOSEBOX, COLOUR_GREY), + NWidget(WWT_CAPTION, COLOUR_GREY, WID_CTO_CAPTION), SetDataTip(STR_NULL, STR_TOOLTIP_WINDOW_TITLE_DRAG_THIS), + EndContainer(), + NWidget(WWT_PANEL, COLOUR_GREY), + NWidget(WWT_LABEL, COLOUR_GREY, WID_CTO_HEADER), SetFill(1, 0), SetResize(1, 0), SetDataTip(STR_NULL, STR_NULL), + EndContainer(), + NWidgetFunction(MakeCargoTypeOrdersRows), + NWidget(WWT_PANEL, COLOUR_GREY), SetMinimalSize(1, 4), SetFill(1, 0), SetResize(1, 0), EndContainer(), // SPACER + NWidget(NWID_HORIZONTAL), + NWidget(WWT_PANEL, COLOUR_GREY), + NWidget(WWT_TEXT, COLOUR_GREY, WID_CTO_SET_TO_ALL_LABEL), SetPadding(0, 0, 0, WD_FRAMERECT_LEFT + 12 + WD_FRAMETEXT_LEFT), SetFill(1, 0), SetResize(1, 0), SetDataTip(STR_CARGO_TYPE_ORDERS_SET_TO_ALL_LABEL, STR_CARGO_TYPE_ORDERS_SET_TO_ALL_TOOLTIP), + EndContainer(), + NWidget(WWT_DROPDOWN, COLOUR_GREY, WID_CTO_SET_TO_ALL_DROPDOWN), SetFill(1, 0), SetResize(1, 0), SetDataTip(STR_NULL, STR_CARGO_TYPE_ORDERS_SET_TO_ALL_TOOLTIP), + EndContainer(), + NWidget(NWID_HORIZONTAL), + NWidget(WWT_TEXTBTN, COLOUR_GREY, WID_CTO_CLOSEBTN), SetFill(1, 0), SetResize(1, 0), SetDataTip(STR_CARGO_TYPE_ORDERS_CLOSE_BUTTON, STR_TOOLTIP_CLOSE_WINDOW), + NWidget(WWT_RESIZEBOX, COLOUR_GREY), + EndContainer(), +}; + +/** Window description for the 'load' variant of CargoTypeOrdersWindow. */ +static WindowDesc _cargo_type_load_orders_widgets ( + WDP_AUTO, "view_cargo_type_load_order", 195, 186, + WC_VEHICLE_CARGO_TYPE_LOAD_ORDERS, WC_VEHICLE_ORDERS, + WDF_CONSTRUCTION, + _nested_cargo_type_orders_widgets, lengthof(_nested_cargo_type_orders_widgets) +); + +/** Window description for the 'unload' variant of CargoTypeOrdersWindow. */ +static WindowDesc _cargo_type_unload_orders_widgets ( + WDP_AUTO, "view_cargo_type_unload_order", 195, 186, + WC_VEHICLE_CARGO_TYPE_UNLOAD_ORDERS, WC_VEHICLE_ORDERS, + WDF_CONSTRUCTION, + _nested_cargo_type_orders_widgets, lengthof(_nested_cargo_type_orders_widgets) +); + +/** + * Show the CargoTypeOrdersWindow for an order. + * @param v The vehicle the order belongs to. + * @param parent The parent window. + * @param order_id Which order to display/edit. + * @param variant Which aspect of the order to display/edit: load or unload. + * @pre \c v != NULL + */ +void ShowCargoTypeOrdersWindow(const Vehicle *v, Window *parent, VehicleOrderID order_id, CargoTypeOrdersWindowVariant variant) +{ + WindowDesc &desc = (variant == CTOWV_LOAD) ? _cargo_type_load_orders_widgets : _cargo_type_unload_orders_widgets; + DeleteWindowById(desc.cls, v->index); + CargoTypeOrdersWindow *w = new CargoTypeOrdersWindow(&desc, v, order_id, variant); + w->parent = parent; +} + /** Order load types that could be given to station orders. */ -static const StringID _station_load_types[][5][5] = { +static const StringID _station_load_types[][9][9] = { { /* No refitting. */ { @@ -45,30 +341,67 @@ static const StringID _station_load_types[][5][5] = { STR_ORDER_FULL_LOAD, STR_ORDER_FULL_LOAD_ANY, STR_ORDER_NO_LOAD, + INVALID_STRING_ID, + INVALID_STRING_ID, + INVALID_STRING_ID, + STR_ORDER_CARGO_TYPE_LOAD, }, { STR_ORDER_UNLOAD, INVALID_STRING_ID, STR_ORDER_UNLOAD_FULL_LOAD, STR_ORDER_UNLOAD_FULL_LOAD_ANY, STR_ORDER_UNLOAD_NO_LOAD, + INVALID_STRING_ID, + INVALID_STRING_ID, + INVALID_STRING_ID, + STR_ORDER_UNLOAD_CARGO_TYPE_LOAD, }, { STR_ORDER_TRANSFER, INVALID_STRING_ID, STR_ORDER_TRANSFER_FULL_LOAD, STR_ORDER_TRANSFER_FULL_LOAD_ANY, STR_ORDER_TRANSFER_NO_LOAD, + INVALID_STRING_ID, + INVALID_STRING_ID, + INVALID_STRING_ID, + STR_ORDER_TRANSFER_CARGO_TYPE_LOAD, }, { /* Unload and transfer do not work together. */ - INVALID_STRING_ID, - INVALID_STRING_ID, - INVALID_STRING_ID, - INVALID_STRING_ID, + INVALID_STRING_ID, INVALID_STRING_ID, INVALID_STRING_ID, + INVALID_STRING_ID, INVALID_STRING_ID, INVALID_STRING_ID, + INVALID_STRING_ID, INVALID_STRING_ID, INVALID_STRING_ID, }, { STR_ORDER_NO_UNLOAD, INVALID_STRING_ID, STR_ORDER_NO_UNLOAD_FULL_LOAD, STR_ORDER_NO_UNLOAD_FULL_LOAD_ANY, STR_ORDER_NO_UNLOAD_NO_LOAD, + INVALID_STRING_ID, + INVALID_STRING_ID, + INVALID_STRING_ID, + STR_ORDER_NO_UNLOAD_CARGO_TYPE_LOAD, + }, { + INVALID_STRING_ID, INVALID_STRING_ID, INVALID_STRING_ID, + INVALID_STRING_ID, INVALID_STRING_ID, INVALID_STRING_ID, + INVALID_STRING_ID, INVALID_STRING_ID, INVALID_STRING_ID, + }, { + INVALID_STRING_ID, INVALID_STRING_ID, INVALID_STRING_ID, + INVALID_STRING_ID, INVALID_STRING_ID, INVALID_STRING_ID, + INVALID_STRING_ID, INVALID_STRING_ID, INVALID_STRING_ID, + }, { + INVALID_STRING_ID, INVALID_STRING_ID, INVALID_STRING_ID, + INVALID_STRING_ID, INVALID_STRING_ID, INVALID_STRING_ID, + INVALID_STRING_ID, INVALID_STRING_ID, INVALID_STRING_ID, + }, { + STR_ORDER_CARGO_TYPE_UNLOAD, + INVALID_STRING_ID, + STR_ORDER_CARGO_TYPE_UNLOAD_FULL_LOAD, + STR_ORDER_CARGO_TYPE_UNLOAD_FULL_LOAD_ANY, + STR_ORDER_CARGO_TYPE_UNLOAD_NO_LOAD, + INVALID_STRING_ID, + INVALID_STRING_ID, + INVALID_STRING_ID, + STR_ORDER_CARGO_TYPE_UNLOAD_CARGO_TYPE_LOAD, } }, { /* With auto-refitting. No loading and auto-refitting do not work together. */ @@ -78,30 +411,67 @@ static const StringID _station_load_types[][5][5] = { STR_ORDER_FULL_LOAD_REFIT, STR_ORDER_FULL_LOAD_ANY_REFIT, INVALID_STRING_ID, + INVALID_STRING_ID, + INVALID_STRING_ID, + INVALID_STRING_ID, + STR_ORDER_CARGO_TYPE_LOAD_REFIT, }, { STR_ORDER_UNLOAD_REFIT, INVALID_STRING_ID, STR_ORDER_UNLOAD_FULL_LOAD_REFIT, STR_ORDER_UNLOAD_FULL_LOAD_ANY_REFIT, INVALID_STRING_ID, + INVALID_STRING_ID, + INVALID_STRING_ID, + INVALID_STRING_ID, + STR_ORDER_UNLOAD_CARGO_TYPE_LOAD_REFIT, }, { STR_ORDER_TRANSFER_REFIT, INVALID_STRING_ID, STR_ORDER_TRANSFER_FULL_LOAD_REFIT, STR_ORDER_TRANSFER_FULL_LOAD_ANY_REFIT, INVALID_STRING_ID, + INVALID_STRING_ID, + INVALID_STRING_ID, + INVALID_STRING_ID, + STR_ORDER_TRANSFER_CARGO_TYPE_LOAD_REFIT, }, { /* Unload and transfer do not work together. */ - INVALID_STRING_ID, - INVALID_STRING_ID, - INVALID_STRING_ID, - INVALID_STRING_ID, + INVALID_STRING_ID, INVALID_STRING_ID, INVALID_STRING_ID, + INVALID_STRING_ID, INVALID_STRING_ID, INVALID_STRING_ID, + INVALID_STRING_ID, INVALID_STRING_ID, INVALID_STRING_ID, }, { STR_ORDER_NO_UNLOAD_REFIT, INVALID_STRING_ID, STR_ORDER_NO_UNLOAD_FULL_LOAD_REFIT, STR_ORDER_NO_UNLOAD_FULL_LOAD_ANY_REFIT, INVALID_STRING_ID, + INVALID_STRING_ID, + INVALID_STRING_ID, + INVALID_STRING_ID, + STR_ORDER_NO_UNLOAD_CARGO_TYPE_LOAD_REFIT, + }, { + INVALID_STRING_ID, INVALID_STRING_ID, INVALID_STRING_ID, + INVALID_STRING_ID, INVALID_STRING_ID, INVALID_STRING_ID, + INVALID_STRING_ID, INVALID_STRING_ID, INVALID_STRING_ID, + }, { + INVALID_STRING_ID, INVALID_STRING_ID, INVALID_STRING_ID, + INVALID_STRING_ID, INVALID_STRING_ID, INVALID_STRING_ID, + INVALID_STRING_ID, INVALID_STRING_ID, INVALID_STRING_ID, + }, { + INVALID_STRING_ID, INVALID_STRING_ID, INVALID_STRING_ID, + INVALID_STRING_ID, INVALID_STRING_ID, INVALID_STRING_ID, + INVALID_STRING_ID, INVALID_STRING_ID, INVALID_STRING_ID, + }, { + STR_ORDER_CARGO_TYPE_UNLOAD_REFIT, + INVALID_STRING_ID, + STR_ORDER_CARGO_TYPE_UNLOAD_FULL_LOAD_REFIT, + STR_ORDER_CARGO_TYPE_UNLOAD_FULL_LOAD_ANY_REFIT, + INVALID_STRING_ID, + INVALID_STRING_ID, + INVALID_STRING_ID, + INVALID_STRING_ID, + STR_ORDER_CARGO_TYPE_UNLOAD_CARGO_TYPE_LOAD_REFIT, } } }; @@ -120,6 +490,10 @@ static const StringID _order_full_load_drowdown[] = { STR_ORDER_DROP_FULL_LOAD_ALL, STR_ORDER_DROP_FULL_LOAD_ANY, STR_ORDER_DROP_NO_LOADING, + STR_EMPTY, + STR_EMPTY, + STR_EMPTY, + STR_ORDER_DROP_CARGO_TYPE_LOAD, INVALID_STRING_ID }; @@ -129,6 +503,10 @@ static const StringID _order_unload_drowdown[] = { STR_ORDER_DROP_TRANSFER, STR_EMPTY, STR_ORDER_DROP_NO_UNLOADING, + STR_EMPTY, + STR_EMPTY, + STR_EMPTY, + STR_ORDER_DROP_CARGO_TYPE_UNLOAD, INVALID_STRING_ID }; @@ -573,12 +951,16 @@ private: VehicleOrderID sel_ord = this->OrderGetSel(); const Order *order = this->vehicle->GetOrder(sel_ord); - if (order == NULL || order->GetLoadType() == load_type) return; + if (order == NULL || (order->GetLoadType() == load_type && load_type != OLFB_CARGO_TYPE_LOAD)) return; if (load_type < 0) { load_type = order->GetLoadType() == OLF_LOAD_IF_POSSIBLE ? OLF_FULL_LOAD_ANY : OLF_LOAD_IF_POSSIBLE; } - DoCommandP(this->vehicle->tile, this->vehicle->index + (sel_ord << 20), MOF_LOAD | (load_type << 4), CMD_MODIFY_ORDER | CMD_MSG(STR_ERROR_CAN_T_MODIFY_THIS_ORDER)); + if (order->GetLoadType() != load_type) { + DoCommandP(this->vehicle->tile, this->vehicle->index + (sel_ord << 20), MOF_LOAD | (load_type << 4), CMD_MODIFY_ORDER | CMD_MSG(STR_ERROR_CAN_T_MODIFY_THIS_ORDER)); + } + + if (load_type == OLFB_CARGO_TYPE_LOAD) ShowCargoTypeOrdersWindow(this->vehicle, this, sel_ord, CTOWV_LOAD); } /** @@ -627,18 +1009,22 @@ private: VehicleOrderID sel_ord = this->OrderGetSel(); const Order *order = this->vehicle->GetOrder(sel_ord); - if (order == NULL || order->GetUnloadType() == unload_type) return; + if (order == NULL || (order->GetUnloadType() == unload_type && unload_type != OUFB_CARGO_TYPE_UNLOAD)) return; if (unload_type < 0) { unload_type = order->GetUnloadType() == OUF_UNLOAD_IF_POSSIBLE ? OUFB_UNLOAD : OUF_UNLOAD_IF_POSSIBLE; } - DoCommandP(this->vehicle->tile, this->vehicle->index + (sel_ord << 20), MOF_UNLOAD | (unload_type << 4), CMD_MODIFY_ORDER | CMD_MSG(STR_ERROR_CAN_T_MODIFY_THIS_ORDER)); + if (order->GetUnloadType() != unload_type) { + DoCommandP(this->vehicle->tile, this->vehicle->index + (sel_ord << 20), MOF_UNLOAD | (unload_type << 4), CMD_MODIFY_ORDER | CMD_MSG(STR_ERROR_CAN_T_MODIFY_THIS_ORDER)); + } - /* Transfer orders with leave empty as default */ if (unload_type == OUFB_TRANSFER) { + /* Transfer orders with leave empty as default */ DoCommandP(this->vehicle->tile, this->vehicle->index + (sel_ord << 20), MOF_LOAD | (OLFB_NO_LOAD << 4), CMD_MODIFY_ORDER); this->SetWidgetDirty(WID_O_FULL_LOAD); + } else if(unload_type == OUFB_CARGO_TYPE_UNLOAD) { + ShowCargoTypeOrdersWindow(this->vehicle, this, sel_ord, CTOWV_UNLOAD); } } @@ -1255,7 +1641,7 @@ public: if (this->GetWidget(widget)->ButtonHit(pt)) { this->OrderClick_FullLoad(-1); } else { - ShowDropDownMenu(this, _order_full_load_drowdown, this->vehicle->GetOrder(this->OrderGetSel())->GetLoadType(), WID_O_FULL_LOAD, 0, 2); + ShowDropDownMenu(this, _order_full_load_drowdown, this->vehicle->GetOrder(this->OrderGetSel())->GetLoadType(), WID_O_FULL_LOAD, 0, 0xE2 /* 1110 0010 */); } break; @@ -1263,7 +1649,7 @@ public: if (this->GetWidget(widget)->ButtonHit(pt)) { this->OrderClick_Unload(-1); } else { - ShowDropDownMenu(this, _order_unload_drowdown, this->vehicle->GetOrder(this->OrderGetSel())->GetUnloadType(), WID_O_UNLOAD, 0, 8); + ShowDropDownMenu(this, _order_unload_drowdown, this->vehicle->GetOrder(this->OrderGetSel())->GetUnloadType(), WID_O_UNLOAD, 0, 0xE8 /* 1110 1000 */); } break; diff --git a/src/order_type.h b/src/order_type.h index d3a771ce8b..05625c422e 100644 --- a/src/order_type.h +++ b/src/order_type.h @@ -60,6 +60,7 @@ enum OrderUnloadFlags { OUFB_UNLOAD = 1 << 0, ///< Force unloading all cargo onto the platform, possibly not getting paid. OUFB_TRANSFER = 1 << 1, ///< Transfer all cargo onto the platform. OUFB_NO_UNLOAD = 1 << 2, ///< Totally no unloading will be done. + OUFB_CARGO_TYPE_UNLOAD = 1 << 3, ///< Unload actions are defined per cargo type. }; /** @@ -70,6 +71,7 @@ enum OrderLoadFlags { OLFB_FULL_LOAD = 1 << 1, ///< Full load all cargoes of the consist. OLF_FULL_LOAD_ANY = 3, ///< Full load a single cargo of the consist. OLFB_NO_LOAD = 4, ///< Do not load anything. + OLFB_CARGO_TYPE_LOAD = 1 << 3 ///< Load actions are defined per cargo type. }; /** @@ -155,6 +157,8 @@ enum ModifyOrderFlags { MOF_COND_COMPARATOR, ///< A comparator changes. MOF_COND_VALUE, ///< The value to set the condition to. MOF_COND_DESTINATION,///< Change the destination of a conditional order. + MOF_CARGO_TYPE_UNLOAD, ///< Passes an OrderUnloadType and a CargoID. + MOF_CARGO_TYPE_LOAD, ///< Passes an OrderLoadType and a CargoID. MOF_END }; template <> struct EnumPropsT : MakeEnumPropsT {}; diff --git a/src/saveload/extended_ver_sl.cpp b/src/saveload/extended_ver_sl.cpp index 6e13994e67..4a2a7f1cf2 100644 --- a/src/saveload/extended_ver_sl.cpp +++ b/src/saveload/extended_ver_sl.cpp @@ -45,6 +45,8 @@ std::vector _sl_xv_discardable_chunk_ids; ///< list of chunks static const uint32 _sl_xv_slxi_chunk_version = 0; ///< current version os SLXI chunk const SlxiSubChunkInfo _sl_xv_sub_chunk_infos[] = { + { XSLFI_CARGO_TYPE_ORDERS, XSCF_NULL, 1, 1, "cargo_type_orders", NULL, NULL, NULL }, + { XSLFI_NULL, XSCF_NULL, 0, 0, NULL, NULL, NULL, NULL },// This is the end marker }; diff --git a/src/saveload/extended_ver_sl.h b/src/saveload/extended_ver_sl.h index 39a03478c2..4e5ab2ae4a 100644 --- a/src/saveload/extended_ver_sl.h +++ b/src/saveload/extended_ver_sl.h @@ -21,6 +21,7 @@ */ enum SlXvFeatureIndex { XSLFI_NULL = 0, ///< Unused value, to indicate that no extended feature test is in use + XSLFI_CARGO_TYPE_ORDERS, ///< Cargo-specific load/unload order flags XSLFI_SIZE, ///< Total count of features, including null feature }; diff --git a/src/saveload/order_sl.cpp b/src/saveload/order_sl.cpp index d55d577cb6..f6a8fae441 100644 --- a/src/saveload/order_sl.cpp +++ b/src/saveload/order_sl.cpp @@ -113,6 +113,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_CONDARR_X(Order, cargo_type_flags, SLE_UINT8, NUM_CARGO, 0, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_CARGO_TYPE_ORDERS, 1)), /* 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/script/api/script_window.hpp b/src/script/api/script_window.hpp index 58e114734e..0c0c03f46c 100644 --- a/src/script/api/script_window.hpp +++ b/src/script/api/script_window.hpp @@ -289,6 +289,18 @@ public: */ WC_VEHICLE_ORDERS = ::WC_VEHICLE_ORDERS, + /** + * Vehicle cargo type load orders; %Window numbers: + * - #VehicleID = #CargoTypeOrdersWidgets + */ + WC_VEHICLE_CARGO_TYPE_LOAD_ORDERS = ::WC_VEHICLE_CARGO_TYPE_LOAD_ORDERS, + + /** + * Vehicle cargo type unload orders; %Window numbers: + * - #VehicleID = #CargoTypeOrdersWidgets + */ + WC_VEHICLE_CARGO_TYPE_UNLOAD_ORDERS = ::WC_VEHICLE_CARGO_TYPE_UNLOAD_ORDERS, + /** * Replace vehicle window; %Window numbers: * - #VehicleType = #ReplaceVehicleWidgets @@ -1945,6 +1957,21 @@ public: WID_O_SHARED_ORDER_LIST = ::WID_O_SHARED_ORDER_LIST, ///< Open list of shared vehicles. }; + /** Widgets of the #CargoTypeOrdersWindow class. */ + enum CargoTypeOrdersWidgets { + WID_CTO_CAPTION = ::WID_CTO_CAPTION, ///< Caption of the window. + WID_CTO_HEADER = ::WID_CTO_HEADER, ///< Window header. + WID_CTO_CLOSEBTN = ::WID_CTO_CLOSEBTN, ///< Close button. + WID_CTO_SET_TO_ALL_LABEL = ::WID_CTO_SET_TO_ALL_LABEL, ///< 'Set to all' dropdown label + WID_CTO_SET_TO_ALL_DROPDOWN = ::WID_CTO_SET_TO_ALL_DROPDOWN, ///< 'Set to all' dropdown + WID_CTO_CARGO_ROW_FIRST = ::WID_CTO_CARGO_ROW_FIRST, ///< First cargo type order row. + WID_CTO_CARGO_ROW_LAST = ::WID_CTO_CARGO_ROW_LAST, ///< Last cargo type order row. + WID_CTO_CARGO_LABEL_FIRST = ::WID_CTO_CARGO_LABEL_FIRST, ///< First cargo label. + WID_CTO_CARGO_LABEL_LAST = ::WID_CTO_CARGO_LABEL_LAST, ///< Last cargo label. + WID_CTO_CARGO_DROPDOWN_FIRST = ::WID_CTO_CARGO_DROPDOWN_FIRST, ///< First order dropdown. + WID_CTO_CARGO_DROPDOWN_LAST = ::WID_CTO_CARGO_DROPDOWN_LAST, ///< Last order dropdown. + }; + /* automatically generated from ../../widgets/osk_widget.h */ /** Widgets of the #OskWindow class. */ enum OnScreenKeyboardWidgets { diff --git a/src/widgets/order_widget.h b/src/widgets/order_widget.h index 825f791ea8..8b26d91741 100644 --- a/src/widgets/order_widget.h +++ b/src/widgets/order_widget.h @@ -12,6 +12,8 @@ #ifndef WIDGETS_ORDER_WIDGET_H #define WIDGETS_ORDER_WIDGET_H +#include "../cargo_type.h" + /** Widgets of the #OrdersWindow class. */ enum OrderWidgets { WID_O_CAPTION, ///< Caption of the window. @@ -41,4 +43,19 @@ enum OrderWidgets { WID_O_SHARED_ORDER_LIST, ///< Open list of shared vehicles. }; +/** Widgets of the #CargoTypeOrdersWindow class. */ +enum CargoTypeOrdersWidgets { + WID_CTO_CAPTION, ///< Caption of the window. + WID_CTO_HEADER, ///< Window header. + WID_CTO_CLOSEBTN, ///< Close button. + WID_CTO_SET_TO_ALL_LABEL, ///< 'Set to all' dropdown label + WID_CTO_SET_TO_ALL_DROPDOWN, ///< 'Set to all' dropdown + WID_CTO_CARGO_ROW_FIRST, ///< First cargo type order row. + WID_CTO_CARGO_ROW_LAST = WID_CTO_CARGO_ROW_FIRST + NUM_CARGO - 1, ///< Last cargo type order row. + WID_CTO_CARGO_LABEL_FIRST, ///< First cargo label. + WID_CTO_CARGO_LABEL_LAST = WID_CTO_CARGO_LABEL_FIRST + NUM_CARGO - 1, ///< Last cargo label. + WID_CTO_CARGO_DROPDOWN_FIRST, ///< First order dropdown. + WID_CTO_CARGO_DROPDOWN_LAST = WID_CTO_CARGO_DROPDOWN_FIRST + NUM_CARGO - 1, ///< Last order dropdown. +}; + #endif /* WIDGETS_ORDER_WIDGET_H */ diff --git a/src/window_type.h b/src/window_type.h index 809e81d485..81968e2af0 100644 --- a/src/window_type.h +++ b/src/window_type.h @@ -206,6 +206,18 @@ enum WindowClass { */ WC_VEHICLE_ORDERS, + /** + * Vehicle cargo type load orders; %Window numbers: + * - #VehicleID = #CargoTypeOrdersWidgets + */ + WC_VEHICLE_CARGO_TYPE_LOAD_ORDERS, + + /** + * Vehicle cargo type unload orders; %Window numbers: + * - #VehicleID = #CargoTypeOrdersWidgets + */ + WC_VEHICLE_CARGO_TYPE_UNLOAD_ORDERS, + /** * Replace vehicle window; %Window numbers: * - #VehicleType = #ReplaceVehicleWidgets From 7178ef22e58f5ed7163f728d5d1e62a8e863b957 Mon Sep 17 00:00:00 2001 From: Jonathan G Rennison Date: Sun, 4 Sep 2016 18:52:09 +0100 Subject: [PATCH 03/11] Adjust order line load/unload by cargo type strings. --- src/lang/english.txt | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/src/lang/english.txt b/src/lang/english.txt index 34aaf8d837..a9119cba0d 100644 --- a/src/lang/english.txt +++ b/src/lang/english.txt @@ -3852,43 +3852,43 @@ STR_ORDER_UNLOAD :(Unload and tak STR_ORDER_UNLOAD_FULL_LOAD :(Unload and wait for full load) STR_ORDER_UNLOAD_FULL_LOAD_ANY :(Unload and wait for any full load) STR_ORDER_UNLOAD_NO_LOAD :(Unload and leave empty) -STR_ORDER_UNLOAD_CARGO_TYPE_LOAD :(Unload and wait for load by cargo type) +STR_ORDER_UNLOAD_CARGO_TYPE_LOAD :(Unload and load by cargo type) STR_ORDER_TRANSFER :(Transfer and take cargo) STR_ORDER_TRANSFER_FULL_LOAD :(Transfer and wait for full load) STR_ORDER_TRANSFER_FULL_LOAD_ANY :(Transfer and wait for any full load) STR_ORDER_TRANSFER_NO_LOAD :(Transfer and leave empty) -STR_ORDER_TRANSFER_CARGO_TYPE_LOAD :(Transfer and wait for load by cargo type) +STR_ORDER_TRANSFER_CARGO_TYPE_LOAD :(Transfer and load by cargo type) STR_ORDER_NO_UNLOAD :(No unloading and take cargo) STR_ORDER_NO_UNLOAD_FULL_LOAD :(No unloading and wait for full load) STR_ORDER_NO_UNLOAD_FULL_LOAD_ANY :(No unloading and wait for any full load) STR_ORDER_NO_UNLOAD_NO_LOAD :(No unloading and no loading) -STR_ORDER_NO_UNLOAD_CARGO_TYPE_LOAD :(No unloading and wait for load by cargo type) +STR_ORDER_NO_UNLOAD_CARGO_TYPE_LOAD :(No unloading and load by cargo type) STR_ORDER_CARGO_TYPE_UNLOAD :(Unload by cargo type) STR_ORDER_CARGO_TYPE_UNLOAD_FULL_LOAD :(Unload by cargo type and wait for full load) STR_ORDER_CARGO_TYPE_UNLOAD_FULL_LOAD_ANY :(Unload by cargo type and wait for any full load) STR_ORDER_CARGO_TYPE_UNLOAD_NO_LOAD :(Unload by cargo type and leave empty) -STR_ORDER_CARGO_TYPE_UNLOAD_CARGO_TYPE_LOAD :(Unload by cargo type and wait for load by cargo type) +STR_ORDER_CARGO_TYPE_UNLOAD_CARGO_TYPE_LOAD :(Unload by cargo type and load by cargo type) STR_ORDER_AUTO_REFIT :(Refit to {STRING}) STR_ORDER_FULL_LOAD_REFIT :(Full load with refit to {STRING}) STR_ORDER_FULL_LOAD_ANY_REFIT :(Full load any cargo with refit to {STRING}) -STR_ORDER_CARGO_TYPE_LOAD_REFIT :(Load by cargo type with auto-refit to {STRING}) +STR_ORDER_CARGO_TYPE_LOAD_REFIT :(Load by cargo type with refit to {STRING}) STR_ORDER_UNLOAD_REFIT :(Unload and take cargo with refit to {STRING}) STR_ORDER_UNLOAD_FULL_LOAD_REFIT :(Unload and wait for full load with refit to {STRING}) STR_ORDER_UNLOAD_FULL_LOAD_ANY_REFIT :(Unload and wait for any full load with refit to {STRING}) -STR_ORDER_UNLOAD_CARGO_TYPE_LOAD_REFIT :(Unload and wait for load by cargo type with auto-refit to {STRING}) +STR_ORDER_UNLOAD_CARGO_TYPE_LOAD_REFIT :(Unload and load by cargo type with refit to {STRING}) STR_ORDER_TRANSFER_REFIT :(Transfer and take cargo with refit to {STRING}) STR_ORDER_TRANSFER_FULL_LOAD_REFIT :(Transfer and wait for full load with refit to {STRING}) STR_ORDER_TRANSFER_FULL_LOAD_ANY_REFIT :(Transfer and wait for any full load with refit to {STRING}) -STR_ORDER_TRANSFER_CARGO_TYPE_LOAD_REFIT :(Transfer and wait for load by cargo type with auto-refit to {STRING}) +STR_ORDER_TRANSFER_CARGO_TYPE_LOAD_REFIT :(Transfer and load by cargo type with refit to {STRING}) STR_ORDER_NO_UNLOAD_REFIT :(No unloading and take cargo with refit to {STRING}) STR_ORDER_NO_UNLOAD_FULL_LOAD_REFIT :(No unloading and wait for full load with refit to {STRING}) STR_ORDER_NO_UNLOAD_FULL_LOAD_ANY_REFIT :(No unloading and wait for any full load with refit to {STRING}) -STR_ORDER_NO_UNLOAD_CARGO_TYPE_LOAD_REFIT :(No unloading and wait for load by cargo type with auto-refit to {STRING}) -STR_ORDER_CARGO_TYPE_UNLOAD_REFIT :(Unload by cargo type and take cargo with auto-refit to {STRING}) -STR_ORDER_CARGO_TYPE_UNLOAD_FULL_LOAD_REFIT :(Unload by cargo type and wait for full load with auto-refit to {STRING}) -STR_ORDER_CARGO_TYPE_UNLOAD_FULL_LOAD_ANY_REFIT :(Unload by cargo type and wait for any full load with auto-refit to {STRING}) -STR_ORDER_CARGO_TYPE_UNLOAD_CARGO_TYPE_LOAD_REFIT :(Unload by cargo type and wait for load by cargo type with auto-refit to {STRING}) +STR_ORDER_NO_UNLOAD_CARGO_TYPE_LOAD_REFIT :(No unloading and load by cargo type with refit to {STRING}) +STR_ORDER_CARGO_TYPE_UNLOAD_REFIT :(Unload by cargo type and take cargo with refit to {STRING}) +STR_ORDER_CARGO_TYPE_UNLOAD_FULL_LOAD_REFIT :(Unload by cargo type and wait for full load with refit to {STRING}) +STR_ORDER_CARGO_TYPE_UNLOAD_FULL_LOAD_ANY_REFIT :(Unload by cargo type and wait for any full load with refit to {STRING}) +STR_ORDER_CARGO_TYPE_UNLOAD_CARGO_TYPE_LOAD_REFIT :(Unload by cargo type and load by cargo type with refit to {STRING}) STR_ORDER_AUTO_REFIT_ANY :available cargo From ed99dd2583f9da33fe620fcce25aaf4d193e99f0 Mon Sep 17 00:00:00 2001 From: Jonathan G Rennison Date: Sat, 3 Sep 2016 22:59:19 +0100 Subject: [PATCH 04/11] Change name of Order cargo-typed get load/unload type accessors. Move helper functions to get cargo-specific type in cargo-specific mode, into Order class. --- src/economy.cpp | 23 +++++++++-------------- src/order_base.h | 30 ++++++++++++++++++++++++++++-- src/order_gui.cpp | 4 ++-- 3 files changed, 39 insertions(+), 18 deletions(-) diff --git a/src/economy.cpp b/src/economy.cpp index 376e7da82d..fb4fc6dcc8 100644 --- a/src/economy.cpp +++ b/src/economy.cpp @@ -1242,31 +1242,26 @@ Money CargoPayment::PayTransfer(const CargoPacket *cp, uint count) /** * Returns the load type of a vehicle. - * In case of cargo type order, the load type returned depends of the cargo carriable by the vehicle. + * In case of cargo type order, the load type returned depends on the cargo carriable by the vehicle. * @pre v != NULL * @param v A pointer to a vehicle. * @return the load type of this vehicle. */ static OrderLoadFlags GetLoadType(const Vehicle *v) { - const Vehicle *front = v->First(); - OrderLoadFlags olf = front->current_order.GetLoadType(); - if (olf == OLFB_CARGO_TYPE_LOAD) olf = front->current_order.GetLoadType(v->cargo_type); - return olf; + return v->First()->current_order.GetCargoLoadType(v->cargo_type); } /** * Returns the unload type of a vehicle. - * In case of cargo type order, the unload type returned depends of the cargo carriable be the vehicle. + * In case of cargo type order, the unload type returned depends on the cargo carriable by the vehicle. + * @pre v != NULL * @param v A pointer to a vehicle. * @return The unload type of this vehicle. */ static OrderUnloadFlags GetUnloadType(const Vehicle *v) { - const Vehicle *front = v->First(); - OrderUnloadFlags ouf = front->current_order.GetUnloadType(); - if (ouf == OUFB_CARGO_TYPE_UNLOAD) ouf = front->current_order.GetUnloadType(v->cargo_type); - return ouf; + return v->First()->current_order.GetCargoUnloadType(v->cargo_type); } /** @@ -1477,7 +1472,7 @@ struct FinalizeRefitAction */ bool operator()(Vehicle *v) { - if (this->do_reserve || (cargo_type_loading == NULL || (cargo_type_loading->current_order.GetLoadType(v->cargo_type) & OLFB_FULL_LOAD))) { + if (this->do_reserve || (cargo_type_loading == NULL || (cargo_type_loading->current_order.GetCargoLoadTypeRaw(v->cargo_type) & OLFB_FULL_LOAD))) { this->st->goods[v->cargo_type].cargo.Reserve(v->cargo_cap - v->cargo.RemainingCount(), &v->cargo, st->xy, this->next_station); } @@ -1560,7 +1555,7 @@ struct ReserveCargoAction { bool operator()(Vehicle *v) { - if (cargo_type_loading != NULL && !(cargo_type_loading->current_order.GetLoadType(v->cargo_type) & OLFB_FULL_LOAD)) return true; + if (cargo_type_loading != NULL && !(cargo_type_loading->current_order.GetCargoLoadTypeRaw(v->cargo_type) & OLFB_FULL_LOAD)) return true; if (v->cargo_cap > v->cargo.RemainingCount()) { st->goods[v->cargo_type].cargo.Reserve(v->cargo_cap - v->cargo.RemainingCount(), &v->cargo, st->xy, *next_station); @@ -1599,7 +1594,7 @@ static void ReserveConsist(Station *st, Vehicle *u, CargoArray *consist_capleft, IterateVehicleParts(v, ReserveCargoAction(st, next_station, cargo_type_loading ? u : NULL)); } if (consist_capleft == NULL || v->cargo_cap == 0) continue; - if (cargo_type_loading && !(u->current_order.GetLoadType(v->cargo_type) & OLFB_FULL_LOAD)) continue; + if (cargo_type_loading && !(u->current_order.GetCargoLoadTypeRaw(v->cargo_type) & OLFB_FULL_LOAD)) continue; (*consist_capleft)[v->cargo_type] += v->cargo_cap - v->cargo.RemainingCount(); } } @@ -1866,7 +1861,7 @@ static void LoadUnloadVehicle(Vehicle *front) bool has_full_load_order = front->current_order.GetLoadType() & OLFB_FULL_LOAD; if (front->current_order.GetLoadType() == OLFB_CARGO_TYPE_LOAD) { for (Vehicle *v = front; v != NULL; v = v->Next()) { - if (front->current_order.GetLoadType(v->cargo_type) & OLFB_FULL_LOAD) { + if (front->current_order.GetCargoLoadTypeRaw(v->cargo_type) & OLFB_FULL_LOAD) { has_full_load_order = true; break; } diff --git a/src/order_base.h b/src/order_base.h index 93351dd7c8..ff0a003c13 100644 --- a/src/order_base.h +++ b/src/order_base.h @@ -136,12 +136,25 @@ public: * @param cargo_id The cargo type index. * @return The load type for this cargo. */ - inline OrderLoadFlags GetLoadType(CargoID cargo_id) const + inline OrderLoadFlags GetCargoLoadTypeRaw(CargoID cargo_id) const { assert(cargo_id < NUM_CARGO); return (OrderLoadFlags) GB(this->cargo_type_flags[cargo_id], 4, 4); } + /** + * How must the consist be loaded for this type of cargo? + * @param cargo_id The cargo type index. + * @return The load type for this cargo. + */ + inline OrderLoadFlags GetCargoLoadType(CargoID cargo_id) const + { + assert(cargo_id < NUM_CARGO); + OrderLoadFlags olf = this->GetLoadType(); + if (olf == OLFB_CARGO_TYPE_LOAD) olf = this->GetCargoLoadTypeRaw(cargo_id); + return olf; + } + /** How must the consist be unloaded? */ inline OrderUnloadFlags GetUnloadType() const { return (OrderUnloadFlags)GB(this->flags, 0, 4); } @@ -151,12 +164,25 @@ public: * @param cargo_id The cargo type index. * @return The unload type for this cargo. */ - inline OrderUnloadFlags GetUnloadType(CargoID cargo_id) const + inline OrderUnloadFlags GetCargoUnloadTypeRaw(CargoID cargo_id) const { assert(cargo_id < NUM_CARGO); return (OrderUnloadFlags) GB(this->cargo_type_flags[cargo_id], 0, 4); } + /** + * How must the consist be unloaded for this type of cargo? + * @param cargo_id The cargo type index. + * @return The unload type for this cargo. + */ + inline OrderUnloadFlags GetCargoUnloadType(CargoID cargo_id) const + { + assert(cargo_id < NUM_CARGO); + OrderUnloadFlags ouf = this->GetUnloadType(); + if (ouf == OUFB_CARGO_TYPE_UNLOAD) ouf = this->GetCargoUnloadTypeRaw(cargo_id); + return ouf; + } + /** At which stations must we stop? */ inline OrderNonStopFlags GetNonStopType() const { return (OrderNonStopFlags)GB(this->type, 6, 2); } /** Where must we stop at the platform? */ diff --git a/src/order_gui.cpp b/src/order_gui.cpp index c43b41ae40..21914689fe 100644 --- a/src/order_gui.cpp +++ b/src/order_gui.cpp @@ -106,7 +106,7 @@ private: for (int i = 0; i < _sorted_standard_cargo_specs_size; i++) { const CargoSpec *cs = _sorted_cargo_specs[i]; CargoID cargo_id = GetCargoIDByBitnum(cs->bitnum); - uint8 order_type = (this->variant == CTOWV_LOAD) ? (uint8) order->GetLoadType(cargo_id) : (uint8) order->GetUnloadType(cargo_id); + uint8 order_type = (this->variant == CTOWV_LOAD) ? (uint8) order->GetCargoLoadTypeRaw(cargo_id) : (uint8) order->GetCargoUnloadTypeRaw(cargo_id); this->GetWidget(WID_CTO_CARGO_DROPDOWN_FIRST + i)->SetDataTip(this->cargo_type_order_dropdown[order_type], tooltip); } this->set_to_all_dropdown_sel = 0; @@ -121,7 +121,7 @@ private: uint8 GetOrderActionTypeForCargo(CargoID cargo_id) { const Order *order = this->vehicle->GetOrder(this->order_id); - return (this->variant == CTOWV_LOAD) ? (uint8) order->GetLoadType(cargo_id) : (uint8) order->GetUnloadType(cargo_id); + return (this->variant == CTOWV_LOAD) ? (uint8) order->GetCargoLoadTypeRaw(cargo_id) : (uint8) order->GetCargoUnloadTypeRaw(cargo_id); } public: From a670966acfb9b04d640fe7c4b151c33c85594fd9 Mon Sep 17 00:00:00 2001 From: Jonathan G Rennison Date: Sun, 4 Sep 2016 12:01:18 +0100 Subject: [PATCH 05/11] Change GetNextStoppingStation to return per-cargo result(s). --- src/economy.cpp | 49 ++++++++++++++++--------------- src/order_base.h | 33 ++++++++++++++++++++- src/order_cmd.cpp | 72 +++++++++++++++++++++++++++++++++++++++------- src/vehicle_base.h | 6 ++-- 4 files changed, 123 insertions(+), 37 deletions(-) diff --git a/src/economy.cpp b/src/economy.cpp index fb4fc6dcc8..5c12a97550 100644 --- a/src/economy.cpp +++ b/src/economy.cpp @@ -1287,7 +1287,7 @@ void PrepareUnload(Vehicle *front_v) assert(CargoPayment::CanAllocateItem()); front_v->cargo_payment = new CargoPayment(front_v); - StationIDStack next_station = front_v->GetNextStoppingStation(); + CargoStationIDStackSet next_station = front_v->GetNextStoppingStation(); if (front_v->orders.list == NULL || (front_v->current_order.GetUnloadType() & OUFB_NO_UNLOAD) == 0) { Station *st = Station::Get(front_v->last_station_visited); for (Vehicle *v = front_v; v != NULL; v = v->Next()) { @@ -1296,7 +1296,7 @@ void PrepareUnload(Vehicle *front_v) if (v->cargo_cap > 0 && v->cargo.TotalCount() > 0) { v->cargo.Stage( HasBit(ge->status, GoodsEntry::GES_ACCEPTANCE), - front_v->last_station_visited, next_station, + front_v->last_station_visited, next_station.Get(v->cargo_type), GetUnloadType(v), ge, front_v->cargo_payment); if (v->cargo.UnloadCount() > 0) SetBit(v->vehicle_flags, VF_CARGO_UNLOADING); @@ -1450,7 +1450,7 @@ struct FinalizeRefitAction { CargoArray &consist_capleft; ///< Capacities left in the consist. Station *st; ///< Station to reserve cargo from. - StationIDStack &next_station; ///< Next hops to reserve cargo for. + const CargoStationIDStackSet &next_station; ///< Next hops to reserve cargo for. bool do_reserve; ///< If the vehicle should reserve. Vehicle *cargo_type_loading; ///< Non-null if vehicle should reserve if the cargo type of the vehicle is a cargo-specific full-load order using this pointer @@ -1460,8 +1460,9 @@ struct FinalizeRefitAction * @param st Station to reserve cargo from. * @param next_station Next hops to reserve cargo for. * @param do_reserve If we should reserve cargo or just add up the capacities. + * @param cargo_type_loading Non-null if vehicle should reserve if the cargo type of the vehicle is a cargo-specific full-load order using this pointer */ - FinalizeRefitAction(CargoArray &consist_capleft, Station *st, StationIDStack &next_station, bool do_reserve, Vehicle *cargo_type_loading) : + FinalizeRefitAction(CargoArray &consist_capleft, Station *st, const CargoStationIDStackSet &next_station, bool do_reserve, Vehicle *cargo_type_loading) : consist_capleft(consist_capleft), st(st), next_station(next_station), do_reserve(do_reserve), cargo_type_loading(cargo_type_loading) {} /** @@ -1474,7 +1475,7 @@ struct FinalizeRefitAction { if (this->do_reserve || (cargo_type_loading == NULL || (cargo_type_loading->current_order.GetCargoLoadTypeRaw(v->cargo_type) & OLFB_FULL_LOAD))) { this->st->goods[v->cargo_type].cargo.Reserve(v->cargo_cap - v->cargo.RemainingCount(), - &v->cargo, st->xy, this->next_station); + &v->cargo, st->xy, this->next_station.Get(v->cargo_type)); } this->consist_capleft[v->cargo_type] += v->cargo_cap - v->cargo.RemainingCount(); return true; @@ -1489,7 +1490,7 @@ struct FinalizeRefitAction * @param next_station Possible next stations the vehicle can travel to. * @param new_cid Target cargo for refit. */ -static void HandleStationRefit(Vehicle *v, CargoArray &consist_capleft, Station *st, StationIDStack next_station, CargoID new_cid) +static void HandleStationRefit(Vehicle *v, CargoArray &consist_capleft, Station *st, CargoStationIDStackSet next_station, CargoID new_cid) { Vehicle *v_start = v->GetFirstEnginePart(); if (!IterateVehicleParts(v_start, IsEmptyAction())) return; @@ -1507,7 +1508,7 @@ static void HandleStationRefit(Vehicle *v, CargoArray &consist_capleft, Station CargoID cid; new_cid = v_start->cargo_type; FOR_EACH_SET_CARGO_ID(cid, refit_mask) { - if (st->goods[cid].cargo.HasCargoFor(next_station)) { + if (st->goods[cid].cargo.HasCargoFor(next_station.Get(cid))) { /* Try to find out if auto-refitting would succeed. In case the refit is allowed, * the returned refit capacity will be greater than zero. */ DoCommand(v_start->tile, v_start->index, cid | 1U << 6 | 0xFF << 8 | 1U << 16, DC_QUERY_COST, GetCmdRefitVeh(v_start)); // Auto-refit and only this vehicle including artic parts. @@ -1547,10 +1548,10 @@ static void HandleStationRefit(Vehicle *v, CargoArray &consist_capleft, Station struct ReserveCargoAction { Station *st; - StationIDStack *next_station; + const CargoStationIDStackSet &next_station; Vehicle *cargo_type_loading; - ReserveCargoAction(Station *st, StationIDStack *next_station, Vehicle *cargo_type_loading) : + ReserveCargoAction(Station *st, const CargoStationIDStackSet &next_station, Vehicle *cargo_type_loading) : st(st), next_station(next_station), cargo_type_loading(cargo_type_loading) {} bool operator()(Vehicle *v) @@ -1558,7 +1559,7 @@ struct ReserveCargoAction { if (cargo_type_loading != NULL && !(cargo_type_loading->current_order.GetCargoLoadTypeRaw(v->cargo_type) & OLFB_FULL_LOAD)) return true; if (v->cargo_cap > v->cargo.RemainingCount()) { st->goods[v->cargo_type].cargo.Reserve(v->cargo_cap - v->cargo.RemainingCount(), - &v->cargo, st->xy, *next_station); + &v->cargo, st->xy, next_station.Get(v->cargo_type)); } return true; @@ -1575,7 +1576,7 @@ struct ReserveCargoAction { * @param next_station Station(s) the vehicle will stop at next. * @param cargo_type_loading check cargo-specific loading type */ -static void ReserveConsist(Station *st, Vehicle *u, CargoArray *consist_capleft, StationIDStack *next_station, bool cargo_type_loading) +static void ReserveConsist(Station *st, Vehicle *u, CargoArray *consist_capleft, const CargoStationIDStackSet &next_station, bool cargo_type_loading) { /* If there is a cargo payment not all vehicles of the consist have tried to do the refit. * In that case, only reserve if it's a fixed refit and the equivalent of "articulated chain" @@ -1631,24 +1632,26 @@ static void LoadUnloadVehicle(Vehicle *front) StationID last_visited = front->last_station_visited; Station *st = Station::Get(last_visited); - StationIDStack next_station = front->GetNextStoppingStation(); + CargoStationIDStackSet next_station = front->GetNextStoppingStation(); + bool use_autorefit = front->current_order.IsRefit() && front->current_order.GetRefitCargo() == CT_AUTO_REFIT; CargoArray consist_capleft; + bool should_reserve_consist = false; + bool reserve_consist_cargo_type_loading = false; if (_settings_game.order.improved_load && use_autorefit) { - if (front->cargo_payment == NULL) { - ReserveConsist(st, front, - (use_autorefit && front->load_unload_ticks != 0) ? &consist_capleft : NULL, - &next_station, - false); - } + if (front->cargo_payment == NULL) should_reserve_consist = true; } else { if ((front->current_order.GetLoadType() & OLFB_FULL_LOAD) || (front->current_order.GetLoadType() == OLFB_CARGO_TYPE_LOAD)) { - ReserveConsist(st, front, - (use_autorefit && front->load_unload_ticks != 0) ? &consist_capleft : NULL, - &next_station, - (front->current_order.GetLoadType() == OLFB_CARGO_TYPE_LOAD)); + should_reserve_consist = true; + reserve_consist_cargo_type_loading = (front->current_order.GetLoadType() == OLFB_CARGO_TYPE_LOAD); } } + if (should_reserve_consist) { + ReserveConsist(st, front, + (use_autorefit && front->load_unload_ticks != 0) ? &consist_capleft : NULL, + next_station, + reserve_consist_cargo_type_loading); + } /* We have not waited enough time till the next round of loading/unloading */ if (front->load_unload_ticks != 0) return; @@ -1797,7 +1800,7 @@ static void LoadUnloadVehicle(Vehicle *front) if (_settings_game.order.gradual_loading) cap_left = min(cap_left, load_amount); if (v->cargo.StoredCount() == 0) TriggerVehicle(v, VEHICLE_TRIGGER_NEW_CARGO); - uint loaded = ge->cargo.Load(cap_left, &v->cargo, st->xy, next_station); + uint loaded = ge->cargo.Load(cap_left, &v->cargo, st->xy, next_station.Get(v->cargo_type)); if (v->cargo.ActionCount(VehicleCargoList::MTA_LOAD) > 0) { /* Remember if there are reservations left so that we don't stop * loading before they're loaded. */ diff --git a/src/order_base.h b/src/order_base.h index ff0a003c13..cc462e2a20 100644 --- a/src/order_base.h +++ b/src/order_base.h @@ -21,6 +21,8 @@ #include "vehicle_type.h" #include "date_type.h" +#include + typedef Pool OrderPool; typedef Pool OrderListPool; extern OrderPool _order_pool; @@ -325,6 +327,35 @@ public: void InsertOrder(Vehicle *v, Order *new_o, VehicleOrderID sel_ord); void DeleteOrder(Vehicle *v, VehicleOrderID sel_ord); +struct CargoMaskedStationIDStack { + uint32 cargo_mask; + StationIDStack station; + + CargoMaskedStationIDStack(uint32 cargo_mask, StationIDStack station) + : cargo_mask(cargo_mask), station(station) {} +}; + +struct CargoStationIDStackSet { +private: + CargoMaskedStationIDStack first; + std::vector more; + +public: + CargoStationIDStackSet() + : first(~0, INVALID_STATION) {} + + const StationIDStack& Get(CargoID cargo) const + { + if (HasBit(first.cargo_mask, cargo)) return first.station; + for (size_t i = 0; i < more.size(); i++) { + if (HasBit(more[i].cargo_mask, cargo)) return more[i].station; + } + NOT_REACHED(); + } + + void FillNextStoppingStation(const Vehicle *v, const OrderList *o, const Order *first = NULL, uint hops = 0); +}; + /** * Shared order list linking together the linked list of orders and the list * of vehicles sharing this order list. @@ -397,7 +428,7 @@ public: */ inline VehicleOrderID GetNumManualOrders() const { return this->num_manual_orders; } - StationIDStack GetNextStoppingStation(const Vehicle *v, const Order *first = NULL, uint hops = 0) const; + CargoMaskedStationIDStack GetNextStoppingStation(const Vehicle *v, uint32 cargo_mask, const Order *first = NULL, uint hops = 0) const; const Order *GetNextDecisionNode(const Order *next, uint hops) const; void InsertOrderAt(Order *new_order, int index); diff --git a/src/order_cmd.cpp b/src/order_cmd.cpp index 9ade1b726a..54f642e65e 100644 --- a/src/order_cmd.cpp +++ b/src/order_cmd.cpp @@ -19,6 +19,7 @@ #include "timetable.h" #include "vehicle_func.h" #include "depot_base.h" +#include "core/bitmath_func.hpp" #include "core/pool_func.hpp" #include "core/random_func.hpp" #include "aircraft.h" @@ -290,6 +291,19 @@ void Order::AssignOrder(const Order &other) } } +void CargoStationIDStackSet::FillNextStoppingStation(const Vehicle *v, const OrderList *o, const Order *first, uint hops) +{ + this->more.clear(); + this->first = o->GetNextStoppingStation(v, ~0, first, hops); + if (this->first.cargo_mask != (uint32) ~0) { + uint32 have_cargoes = this->first.cargo_mask; + do { + this->more.push_back(o->GetNextStoppingStation(v, ~have_cargoes, first, hops)); + have_cargoes |= this->more.back().cargo_mask; + } while (have_cargoes != (uint32) ~0); + } +} + /** * Recomputes everything. * @param chain first order in the chain @@ -401,21 +415,26 @@ const Order *OrderList::GetNextDecisionNode(const Order *next, uint hops) const /** * Recursively determine the next deterministic station to stop at. * @param v The vehicle we're looking at. + * @param uint32 cargo_mask Bit-set of the cargo IDs of interest. * @param first Order to start searching at or NULL to start at cur_implicit_order_index + 1. * @param hops Number of orders we have already looked at. - * @return Next stoppping station or INVALID_STATION. + * @return A CargoMaskedStationIDStack of the cargo mask the result is valid for, and the next stoppping station or INVALID_STATION. * @pre The vehicle is currently loading and v->last_station_visited is meaningful. * @note This function may draw a random number. Don't use it from the GUI. */ -StationIDStack OrderList::GetNextStoppingStation(const Vehicle *v, const Order *first, uint hops) const +CargoMaskedStationIDStack OrderList::GetNextStoppingStation(const Vehicle *v, uint32 cargo_mask, const Order *first, uint hops) const { + assert(cargo_mask != 0); + CargoID first_cargo_id = FindFirstBit(cargo_mask); + uint32 other_cargo_mask = cargo_mask; + ClrBit(other_cargo_mask, first_cargo_id); const Order *next = first; if (first == NULL) { next = this->GetOrderAt(v->cur_implicit_order_index); if (next == NULL) { next = this->GetFirstOrder(); - if (next == NULL) return INVALID_STATION; + if (next == NULL) return CargoMaskedStationIDStack(cargo_mask, INVALID_STATION); } else { /* GetNext never returns NULL if there is a valid station in the list. * As the given "next" is already valid and a station in the list, we @@ -440,23 +459,54 @@ StationIDStack OrderList::GetNextStoppingStation(const Vehicle *v, const Order * } else if (skip_to == NULL || skip_to == first) { next = (advance == first) ? NULL : advance; } else { - StationIDStack st1 = this->GetNextStoppingStation(v, skip_to, hops); - StationIDStack st2 = this->GetNextStoppingStation(v, advance, hops); - while (!st2.IsEmpty()) st1.Push(st2.Pop()); + CargoMaskedStationIDStack st1 = this->GetNextStoppingStation(v, cargo_mask, skip_to, hops); + cargo_mask &= st1.cargo_mask; + CargoMaskedStationIDStack st2 = this->GetNextStoppingStation(v, cargo_mask, advance, hops); + st1.cargo_mask &= st2.cargo_mask; + while (!st2.station.IsEmpty()) st1.station.Push(st2.station.Pop()); return st1; } ++hops; } + if (next == NULL) return CargoMaskedStationIDStack(cargo_mask, INVALID_STATION); + + if ((next->IsType(OT_GOTO_STATION) || next->IsType(OT_IMPLICIT)) && (next->GetUnloadType() == OUFB_CARGO_TYPE_UNLOAD || next->GetLoadType() == OLFB_CARGO_TYPE_LOAD)) { + /* This is a cargo-specific load/unload order. + * If the first cargo is both a no-load and no-unload order, skip it. + * Drop cargoes which don't match the first one. */ + bool should_drop = next->GetCargoLoadType(first_cargo_id) & OLFB_NO_LOAD && next->GetCargoUnloadType(first_cargo_id) & OUFB_NO_UNLOAD; + CargoID cargo; + FOR_EACH_SET_BIT(cargo, other_cargo_mask) { + if (should_drop != (next->GetCargoLoadType(cargo) & OLFB_NO_LOAD && next->GetCargoUnloadType(cargo) & OUFB_NO_UNLOAD)) { + ClrBit(cargo_mask, cargo); + } + } + other_cargo_mask &= cargo_mask; + if (should_drop) continue; + } + /* Don't return a next stop if the vehicle has to unload everything. */ - if (next == NULL || ((next->IsType(OT_GOTO_STATION) || next->IsType(OT_IMPLICIT)) && - next->GetDestination() == v->last_station_visited && - (next->GetUnloadType() & (OUFB_TRANSFER | OUFB_UNLOAD)) != 0)) { - return INVALID_STATION; + if ((next->IsType(OT_GOTO_STATION) || next->IsType(OT_IMPLICIT)) && + next->GetDestination() == v->last_station_visited) { + bool invalid = ((next->GetCargoUnloadType(first_cargo_id) & (OUFB_TRANSFER | OUFB_UNLOAD)) != 0); + if (next->GetUnloadType() == OUFB_CARGO_TYPE_UNLOAD) { + /* This is a cargo-specific load/unload order. + * Don't return a next stop if first cargo has transfer or unload set. + * Drop cargoes which don't match the first one. */ + CargoID cargo; + FOR_EACH_SET_BIT(cargo, other_cargo_mask) { + if (invalid != ((next->GetCargoUnloadType(cargo) & (OUFB_TRANSFER | OUFB_UNLOAD)) != 0)) { + ClrBit(cargo_mask, cargo); + } + } + other_cargo_mask &= cargo_mask; + } + if (invalid) return CargoMaskedStationIDStack(cargo_mask, INVALID_STATION); } } while (next->IsType(OT_GOTO_DEPOT) || next->GetDestination() == v->last_station_visited); - return next->GetDestination(); + return CargoMaskedStationIDStack(cargo_mask, next->GetDestination()); } /** diff --git a/src/vehicle_base.h b/src/vehicle_base.h index f923c2d791..9aedc7facd 100644 --- a/src/vehicle_base.h +++ b/src/vehicle_base.h @@ -639,9 +639,11 @@ public: * Get the next station the vehicle will stop at. * @return ID of the next station the vehicle will stop at or INVALID_STATION. */ - inline StationIDStack GetNextStoppingStation() const + inline CargoStationIDStackSet GetNextStoppingStation() const { - return (this->orders.list == NULL) ? INVALID_STATION : this->orders.list->GetNextStoppingStation(this); + CargoStationIDStackSet set; + if (this->orders.list != NULL) set.FillNextStoppingStation(this, this->orders.list); + return set; } void ResetRefitCaps(); From 3507b51d9e11b7d4260b9e06c56365e412071dfd Mon Sep 17 00:00:00 2001 From: Jonathan G Rennison Date: Sun, 4 Sep 2016 16:49:27 +0100 Subject: [PATCH 06/11] Initial LinkRefresher support for cargo-specific links. --- src/linkgraph/refresh.cpp | 82 ++++++++++++++++++++++++++++++++------- src/linkgraph/refresh.h | 5 ++- src/order_base.h | 5 +-- src/order_cmd.cpp | 75 +++++++++++++++++------------------ src/station_cmd.cpp | 22 ----------- src/train_cmd.cpp | 9 +++++ src/vehicle.cpp | 80 ++++++++++++++++++++++++++++++-------- src/vehicle_base.h | 4 +- 8 files changed, 188 insertions(+), 94 deletions(-) diff --git a/src/linkgraph/refresh.cpp b/src/linkgraph/refresh.cpp index 501f063520..ae2c5fa6e7 100644 --- a/src/linkgraph/refresh.cpp +++ b/src/linkgraph/refresh.cpp @@ -25,19 +25,69 @@ * @param allow_merge If the refresher is allowed to merge or extend link graphs. * @param is_full_loading If the vehicle is full loading. */ -/* static */ void LinkRefresher::Run(Vehicle *v, bool allow_merge, bool is_full_loading) +/* static */ void LinkRefresher::Run(Vehicle *v, bool allow_merge, bool is_full_loading, bool check_cargo_can_leave) { /* If there are no orders we can't predict anything.*/ if (v->orders.list == NULL) return; - /* Make sure the first order is a useful order. */ - const Order *first = v->orders.list->GetNextDecisionNode(v->GetOrder(v->cur_implicit_order_index), 0); - if (first == NULL) return; + /* Scan orders for cargo-specific load/unload, and run LinkRefresher separately for each set of cargoes where they differ. */ + uint32 cargoes_to_check = ~0; + while (cargoes_to_check != 0) { + uint32 cargo_mask = cargoes_to_check; + const CargoID first_cargo_id = FindFirstBit(cargo_mask); + for (const Order *o = v->orders.list->GetFirstOrder(); o != NULL; o = o->next) { + if (o->IsType(OT_GOTO_STATION) || o->IsType(OT_IMPLICIT)) { + if (o->GetUnloadType() == OUFB_CARGO_TYPE_UNLOAD) { + OrderUnloadFlags ouf = o->GetCargoUnloadType(first_cargo_id); + uint32 other_cargo_mask = cargo_mask; + ClrBit(other_cargo_mask, first_cargo_id); + CargoID cargo; + FOR_EACH_SET_BIT(cargo, other_cargo_mask) { + if (((ouf ^ o->GetCargoUnloadType(cargo)) & (OUFB_TRANSFER | OUFB_UNLOAD | OUFB_NO_UNLOAD)) != 0) ClrBit(cargo_mask, cargo); + } + } + if (o->GetLoadType() == OLFB_CARGO_TYPE_LOAD) { + OrderLoadFlags olf = o->GetCargoLoadType(first_cargo_id); + uint32 other_cargo_mask = cargo_mask; + ClrBit(other_cargo_mask, first_cargo_id); + CargoID cargo; + FOR_EACH_SET_BIT(cargo, other_cargo_mask) { + if (((olf ^ o->GetCargoLoadType(cargo)) & (OLFB_NO_LOAD)) != 0) ClrBit(cargo_mask, cargo); + } + } + } + } - HopSet seen_hops; - LinkRefresher refresher(v, &seen_hops, allow_merge, is_full_loading); + bool has_cargo; + if (!HasBit(v->vehicle_flags, VF_LAST_LOAD_ST_SEP)) { + has_cargo = v->last_loading_station != INVALID_STATION; + } else { + has_cargo = false; + for (const Vehicle *u = v; u != NULL; u = u->Next()) { + if (u->cargo_type < NUM_CARGO && HasBit(cargo_mask, u->cargo_type) && u->last_loading_station != INVALID_STATION) { + has_cargo = true; + break; + } + } + } - refresher.RefreshLinks(first, first, v->last_loading_station != INVALID_STATION ? 1 << HAS_CARGO : 0); + if (check_cargo_can_leave && (v->current_order.IsType(OT_GOTO_STATION) || v->current_order.IsType(OT_IMPLICIT)) && + (!v->current_order.CanLeaveWithCargo(has_cargo, first_cargo_id))) { + cargoes_to_check &= ~cargo_mask; + continue; + } + + /* Make sure the first order is a useful order. */ + const Order *first = v->orders.list->GetNextDecisionNode(v->GetOrder(v->cur_implicit_order_index), 0, cargo_mask); + if (first != NULL) { + HopSet seen_hops; + LinkRefresher refresher(v, &seen_hops, allow_merge, is_full_loading, cargo_mask); + + refresher.RefreshLinks(first, first, has_cargo ? 1 << HAS_CARGO : 0); + } + + cargoes_to_check &= ~cargo_mask; + } } /** @@ -68,9 +118,9 @@ bool LinkRefresher::Hop::operator<(const Hop &other) const * @param allow_merge If the refresher is allowed to merge or extend link graphs. * @param is_full_loading If the vehicle is full loading. */ -LinkRefresher::LinkRefresher(Vehicle *vehicle, HopSet *seen_hops, bool allow_merge, bool is_full_loading) : +LinkRefresher::LinkRefresher(Vehicle *vehicle, HopSet *seen_hops, bool allow_merge, bool is_full_loading, uint32 cargo_mask) : vehicle(vehicle), seen_hops(seen_hops), cargo(CT_INVALID), allow_merge(allow_merge), - is_full_loading(is_full_loading) + is_full_loading(is_full_loading), cargo_mask(cargo_mask) { memset(this->capacities, 0, sizeof(this->capacities)); @@ -175,8 +225,10 @@ const Order *LinkRefresher::PredictNextOrder(const Order *cur, const Order *next SetBit(flags, USE_NEXT); if (next->IsType(OT_CONDITIONAL)) { + uint32 this_cargo_mask = this->cargo_mask; const Order *skip_to = this->vehicle->orders.list->GetNextDecisionNode( - this->vehicle->orders.list->GetOrderAt(next->GetConditionSkipToOrder()), num_hops); + this->vehicle->orders.list->GetOrderAt(next->GetConditionSkipToOrder()), num_hops, this_cargo_mask); + assert(this_cargo_mask == this->cargo_mask); if (skip_to != NULL && num_hops < this->vehicle->orders.list->GetNumOrders()) { /* Make copies of capacity tracking lists. There is potential * for optimization here: If the vehicle never refits we don't @@ -189,8 +241,10 @@ const Order *LinkRefresher::PredictNextOrder(const Order *cur, const Order *next /* Reassign next with the following stop. This can be a station or a * depot.*/ + uint32 this_cargo_mask = this->cargo_mask; next = this->vehicle->orders.list->GetNextDecisionNode( - this->vehicle->orders.list->GetNext(next), num_hops++); + this->vehicle->orders.list->GetNext(next), num_hops++, this_cargo_mask); + assert(this_cargo_mask == this->cargo_mask); } return next; } @@ -208,6 +262,8 @@ void LinkRefresher::RefreshStats(const Order *cur, const Order *next) for (CargoID c = 0; c < NUM_CARGO; c++) { /* Refresh the link and give it a minimum capacity. */ + if (!HasBit(this->cargo_mask, c)) continue; + uint cargo_quantity = this->capacities[c]; if (cargo_quantity == 0) continue; @@ -218,7 +274,7 @@ void LinkRefresher::RefreshStats(const Order *cur, const Order *next) } /* A link is at least partly restricted if a vehicle can't load at its source. */ - EdgeUpdateMode restricted_mode = (cur->GetLoadType() & OLFB_NO_LOAD) == 0 ? + EdgeUpdateMode restricted_mode = (cur->GetCargoLoadType(c) & OLFB_NO_LOAD) == 0 ? EUM_UNRESTRICTED : EUM_RESTRICTED; /* If the vehicle is currently full loading, increase the capacities at the station @@ -309,7 +365,7 @@ void LinkRefresher::RefreshLinks(const Order *cur, const Order *next, uint8 flag } if (cur->IsType(OT_GOTO_STATION) || cur->IsType(OT_IMPLICIT)) { - if (cur->CanLeaveWithCargo(HasBit(flags, HAS_CARGO))) { + if (cur->CanLeaveWithCargo(HasBit(flags, HAS_CARGO), FindFirstBit(this->cargo_mask))) { SetBit(flags, HAS_CARGO); this->RefreshStats(cur, next); } else { diff --git a/src/linkgraph/refresh.h b/src/linkgraph/refresh.h index 42067ebcf9..3d30853274 100644 --- a/src/linkgraph/refresh.h +++ b/src/linkgraph/refresh.h @@ -23,7 +23,7 @@ */ class LinkRefresher { public: - static void Run(Vehicle *v, bool allow_merge = true, bool is_full_loading = false); + static void Run(Vehicle *v, bool allow_merge = true, bool is_full_loading = false, bool check_cargo_can_leave = false); protected: /** @@ -89,8 +89,9 @@ protected: CargoID cargo; ///< Cargo given in last refit order. bool allow_merge; ///< If the refresher is allowed to merge or extend link graphs. bool is_full_loading; ///< If the vehicle is full loading. + uint32 cargo_mask; ///< Bit-mask of cargo IDs to refresh. - LinkRefresher(Vehicle *v, HopSet *seen_hops, bool allow_merge, bool is_full_loading); + LinkRefresher(Vehicle *v, HopSet *seen_hops, bool allow_merge, bool is_full_loading, uint32 cargo_mask); bool HandleRefit(CargoID refit_cargo); void ResetRefit(); diff --git a/src/order_base.h b/src/order_base.h index cc462e2a20..11cb78c47b 100644 --- a/src/order_base.h +++ b/src/order_base.h @@ -300,8 +300,7 @@ public: inline void SetMaxSpeed(uint16 speed) { this->max_speed = speed; } bool ShouldStopAtStation(const Vehicle *v, StationID station) const; - bool CanLoadOrUnload() const; - bool CanLeaveWithCargo(bool has_cargo) const; + bool CanLeaveWithCargo(bool has_cargo, CargoID cargo) const; TileIndex GetLocation(const Vehicle *v, bool airport = false) const; @@ -429,7 +428,7 @@ public: inline VehicleOrderID GetNumManualOrders() const { return this->num_manual_orders; } CargoMaskedStationIDStack GetNextStoppingStation(const Vehicle *v, uint32 cargo_mask, const Order *first = NULL, uint hops = 0) const; - const Order *GetNextDecisionNode(const Order *next, uint hops) const; + const Order *GetNextDecisionNode(const Order *next, uint hops, uint32 &cargo_mask) const; void InsertOrderAt(Order *new_order, int index); void DeleteOrderAt(int index); diff --git a/src/order_cmd.cpp b/src/order_cmd.cpp index 54f642e65e..9d93ba1cbc 100644 --- a/src/order_cmd.cpp +++ b/src/order_cmd.cpp @@ -380,14 +380,17 @@ Order *OrderList::GetOrderAt(int index) const * or refit at a depot or evaluate a non-trivial condition. * @param next The order to start looking at. * @param hops The number of orders we have already looked at. + * @param cargo_mask The bit set of cargoes that the we are looking at, this may be reduced to indicate the set of cargoes that the result is valid for. * @return Either of * \li a station order * \li a refitting depot order * \li a non-trivial conditional order * \li NULL if the vehicle won't stop anymore. */ -const Order *OrderList::GetNextDecisionNode(const Order *next, uint hops) const +const Order *OrderList::GetNextDecisionNode(const Order *next, uint hops, uint32 &cargo_mask) const { + assert(cargo_mask != 0); + if (hops > this->GetNumOrders() || next == NULL) return NULL; if (next->IsType(OT_CONDITIONAL)) { @@ -397,7 +400,7 @@ const Order *OrderList::GetNextDecisionNode(const Order *next, uint hops) const * the same as regular order progression. */ return this->GetNextDecisionNode( this->GetOrderAt(next->GetConditionSkipToOrder()), - hops + 1); + hops + 1, cargo_mask); } if (next->IsType(OT_GOTO_DEPOT)) { @@ -405,8 +408,30 @@ const Order *OrderList::GetNextDecisionNode(const Order *next, uint hops) const if (next->IsRefit()) return next; } - if (!next->CanLoadOrUnload()) { - return this->GetNextDecisionNode(this->GetNext(next), hops + 1); + bool can_load_or_unload = false; + if ((next->IsType(OT_GOTO_STATION) || next->IsType(OT_IMPLICIT)) && + (next->GetNonStopType() & ONSF_NO_STOP_AT_DESTINATION_STATION) == 0) { + if (next->GetUnloadType() == OUFB_CARGO_TYPE_UNLOAD || next->GetLoadType() == OLFB_CARGO_TYPE_LOAD) { + /* This is a cargo-specific load/unload order. + * If the first cargo is both a no-load and no-unload order, skip it. + * Drop cargoes which don't match the first one. */ + CargoID first_cargo_id = FindFirstBit(cargo_mask); + can_load_or_unload = ((next->GetCargoLoadType(first_cargo_id) & OLFB_NO_LOAD) == 0 || (next->GetCargoUnloadType(first_cargo_id) & OUFB_NO_UNLOAD) == 0); + uint32 other_cargo_mask = cargo_mask; + ClrBit(other_cargo_mask, first_cargo_id); + CargoID cargo; + FOR_EACH_SET_BIT(cargo, other_cargo_mask) { + if (can_load_or_unload != ((next->GetCargoLoadType(cargo) & OLFB_NO_LOAD) == 0 || (next->GetCargoUnloadType(cargo) & OUFB_NO_UNLOAD) == 0)) { + ClrBit(cargo_mask, cargo); + } + } + } else if ((next->GetLoadType() & OLFB_NO_LOAD) == 0 || (next->GetUnloadType() & OUFB_NO_UNLOAD) == 0) { + can_load_or_unload = true; + } + } + + if (!can_load_or_unload) { + return this->GetNextDecisionNode(this->GetNext(next), hops + 1, cargo_mask); } return next; @@ -425,9 +450,6 @@ const Order *OrderList::GetNextDecisionNode(const Order *next, uint hops) const CargoMaskedStationIDStack OrderList::GetNextStoppingStation(const Vehicle *v, uint32 cargo_mask, const Order *first, uint hops) const { assert(cargo_mask != 0); - CargoID first_cargo_id = FindFirstBit(cargo_mask); - uint32 other_cargo_mask = cargo_mask; - ClrBit(other_cargo_mask, first_cargo_id); const Order *next = first; if (first == NULL) { @@ -445,15 +467,15 @@ CargoMaskedStationIDStack OrderList::GetNextStoppingStation(const Vehicle *v, ui } do { - next = this->GetNextDecisionNode(next, ++hops); + next = this->GetNextDecisionNode(next, ++hops, cargo_mask); /* Resolve possibly nested conditionals by estimation. */ while (next != NULL && next->IsType(OT_CONDITIONAL)) { /* We return both options of conditional orders. */ const Order *skip_to = this->GetNextDecisionNode( - this->GetOrderAt(next->GetConditionSkipToOrder()), hops); + this->GetOrderAt(next->GetConditionSkipToOrder()), hops, cargo_mask); const Order *advance = this->GetNextDecisionNode( - this->GetNext(next), hops); + this->GetNext(next), hops, cargo_mask); if (advance == NULL || advance == first || skip_to == advance) { next = (skip_to == first) ? NULL : skip_to; } else if (skip_to == NULL || skip_to == first) { @@ -471,36 +493,23 @@ CargoMaskedStationIDStack OrderList::GetNextStoppingStation(const Vehicle *v, ui if (next == NULL) return CargoMaskedStationIDStack(cargo_mask, INVALID_STATION); - if ((next->IsType(OT_GOTO_STATION) || next->IsType(OT_IMPLICIT)) && (next->GetUnloadType() == OUFB_CARGO_TYPE_UNLOAD || next->GetLoadType() == OLFB_CARGO_TYPE_LOAD)) { - /* This is a cargo-specific load/unload order. - * If the first cargo is both a no-load and no-unload order, skip it. - * Drop cargoes which don't match the first one. */ - bool should_drop = next->GetCargoLoadType(first_cargo_id) & OLFB_NO_LOAD && next->GetCargoUnloadType(first_cargo_id) & OUFB_NO_UNLOAD; - CargoID cargo; - FOR_EACH_SET_BIT(cargo, other_cargo_mask) { - if (should_drop != (next->GetCargoLoadType(cargo) & OLFB_NO_LOAD && next->GetCargoUnloadType(cargo) & OUFB_NO_UNLOAD)) { - ClrBit(cargo_mask, cargo); - } - } - other_cargo_mask &= cargo_mask; - if (should_drop) continue; - } - /* Don't return a next stop if the vehicle has to unload everything. */ if ((next->IsType(OT_GOTO_STATION) || next->IsType(OT_IMPLICIT)) && next->GetDestination() == v->last_station_visited) { + CargoID first_cargo_id = FindFirstBit(cargo_mask); bool invalid = ((next->GetCargoUnloadType(first_cargo_id) & (OUFB_TRANSFER | OUFB_UNLOAD)) != 0); if (next->GetUnloadType() == OUFB_CARGO_TYPE_UNLOAD) { /* This is a cargo-specific load/unload order. * Don't return a next stop if first cargo has transfer or unload set. * Drop cargoes which don't match the first one. */ + uint32 other_cargo_mask = cargo_mask; + ClrBit(other_cargo_mask, first_cargo_id); CargoID cargo; FOR_EACH_SET_BIT(cargo, other_cargo_mask) { if (invalid != ((next->GetCargoUnloadType(cargo) & (OUFB_TRANSFER | OUFB_UNLOAD)) != 0)) { ClrBit(cargo_mask, cargo); } } - other_cargo_mask &= cargo_mask; } if (invalid) return CargoMaskedStationIDStack(cargo_mask, INVALID_STATION); } @@ -2339,22 +2348,14 @@ bool Order::ShouldStopAtStation(const Vehicle *v, StationID station) const !(this->GetNonStopType() & (is_dest_station ? ONSF_NO_STOP_AT_DESTINATION_STATION : ONSF_NO_STOP_AT_INTERMEDIATE_STATIONS)); } -bool Order::CanLoadOrUnload() const -{ - return (this->IsType(OT_GOTO_STATION) || this->IsType(OT_IMPLICIT)) && - (this->GetNonStopType() & ONSF_NO_STOP_AT_DESTINATION_STATION) == 0 && - ((this->GetLoadType() & OLFB_NO_LOAD) == 0 || - (this->GetUnloadType() & OUFB_NO_UNLOAD) == 0); -} - /** * A vehicle can leave the current station with cargo if: * 1. it can load cargo here OR * 2a. it could leave the last station with cargo AND * 2b. it doesn't have to unload all cargo here. */ -bool Order::CanLeaveWithCargo(bool has_cargo) const +bool Order::CanLeaveWithCargo(bool has_cargo, CargoID cargo) const { - return (this->GetLoadType() & OLFB_NO_LOAD) == 0 || (has_cargo && - (this->GetUnloadType() & (OUFB_UNLOAD | OUFB_TRANSFER)) == 0); + return (this->GetCargoLoadType(cargo) & OLFB_NO_LOAD) == 0 || (has_cargo && + (this->GetCargoUnloadType(cargo) & (OUFB_UNLOAD | OUFB_TRANSFER)) == 0); } diff --git a/src/station_cmd.cpp b/src/station_cmd.cpp index eb90c29ab5..f14d85e9bf 100644 --- a/src/station_cmd.cpp +++ b/src/station_cmd.cpp @@ -3563,28 +3563,6 @@ void IncreaseStats(Station *st, CargoID cargo, StationID next_station_id, uint c } } -/** - * Increase capacity for all link stats associated with vehicles in the given consist. - * @param st Station to get the link stats from. - * @param front First vehicle in the consist. - * @param next_station_id Station the consist will be travelling to next. - */ -void IncreaseStats(Station *st, const Vehicle *front, StationID next_station_id) -{ - for (const Vehicle *v = front; v != NULL; v = v->Next()) { - if (v->refit_cap > 0) { - /* The cargo count can indeed be higher than the refit_cap if - * wagons have been auto-replaced and subsequently auto- - * refitted to a higher capacity. The cargo gets redistributed - * among the wagons in that case. - * As usage is not such an important figure anyway we just - * ignore the additional cargo then.*/ - IncreaseStats(st, v->cargo_type, next_station_id, v->refit_cap, - min(v->refit_cap, v->cargo.StoredCount()), EUM_INCREASE); - } - } -} - /* called for every station each tick */ static void StationHandleSmallTick(BaseStation *st) { diff --git a/src/train_cmd.cpp b/src/train_cmd.cpp index dca456d976..9d0b942984 100644 --- a/src/train_cmd.cpp +++ b/src/train_cmd.cpp @@ -1332,6 +1332,15 @@ CommandCost CmdMoveRailVehicle(TileIndex tile, DoCommandFlag flags, uint32 p1, u CheckCargoCapacity(dst_head); } + if (src_head != NULL) { + src_head->last_loading_station = INVALID_STATION; + ClrBit(src_head->vehicle_flags, VF_LAST_LOAD_ST_SEP); + } + if (dst_head != NULL) { + dst_head->last_loading_station = INVALID_STATION; + ClrBit(dst_head->vehicle_flags, VF_LAST_LOAD_ST_SEP); + } + if (src_head != NULL) src_head->First()->MarkDirty(); if (dst_head != NULL) dst_head->First()->MarkDirty(); diff --git a/src/vehicle.cpp b/src/vehicle.cpp index da2cfeb807..44f279a95f 100644 --- a/src/vehicle.cpp +++ b/src/vehicle.cpp @@ -1460,6 +1460,7 @@ void VehicleEnterDepot(Vehicle *v) * before the stop to the station after the stop can't be predicted * we shouldn't construct it when the vehicle visits the next stop. */ v->last_loading_station = INVALID_STATION; + ClrBit(v->vehicle_flags, VF_LAST_LOAD_ST_SEP); if (v->owner == _local_company) { SetDParam(0, v->index); AddVehicleAdviceNewsItem(STR_NEWS_TRAIN_IS_WAITING + v->type, v->index); @@ -1930,6 +1931,33 @@ void Vehicle::DeleteUnreachedImplicitOrders() } } +/** + * Increase capacity for all link stats associated with vehicles in the given consist. + * @param st Station to get the link stats from. + * @param front First vehicle in the consist. + * @param next_station_id Station the consist will be travelling to next. + */ +static void VehicleIncreaseStats(const Vehicle *front) +{ + for (const Vehicle *v = front; v != NULL; v = v->Next()) { + StationID last_loading_station = HasBit(front->vehicle_flags, VF_LAST_LOAD_ST_SEP) ? v->last_loading_station : front->last_loading_station; + if (v->refit_cap > 0 && + last_loading_station != INVALID_STATION && + last_loading_station != front->last_station_visited && + ((front->current_order.GetCargoLoadType(v->cargo_type) & OLFB_NO_LOAD) == 0 || + (front->current_order.GetCargoUnloadType(v->cargo_type) & OUFB_NO_UNLOAD) == 0)) { + /* The cargo count can indeed be higher than the refit_cap if + * wagons have been auto-replaced and subsequently auto- + * refitted to a higher capacity. The cargo gets redistributed + * among the wagons in that case. + * As usage is not such an important figure anyway we just + * ignore the additional cargo then.*/ + IncreaseStats(Station::Get(last_loading_station), v->cargo_type, front->last_station_visited, v->refit_cap, + min(v->refit_cap, v->cargo.StoredCount()), EUM_INCREASE); + } + } +} + /** * Prepare everything to begin the loading when arriving at a station. * @pre IsTileType(this->tile, MP_STATION) || this->type == VEH_SHIP. @@ -2040,12 +2068,7 @@ void Vehicle::BeginLoading() this->current_order.MakeLoading(false); } - if (this->last_loading_station != INVALID_STATION && - this->last_loading_station != this->last_station_visited && - ((this->current_order.GetLoadType() & OLFB_NO_LOAD) == 0 || - (this->current_order.GetUnloadType() & OUFB_NO_UNLOAD) == 0)) { - IncreaseStats(Station::Get(this->last_loading_station), this, this->last_station_visited); - } + VehicleIncreaseStats(this); PrepareUnload(this); @@ -2093,19 +2116,44 @@ void Vehicle::LeaveStation() if ((this->current_order.GetLoadType() & OLFB_NO_LOAD) == 0 || (this->current_order.GetUnloadType() & OUFB_NO_UNLOAD) == 0) { - if (this->current_order.CanLeaveWithCargo(this->last_loading_station != INVALID_STATION)) { - /* Refresh next hop stats to make sure we've done that at least once - * during the stop and that refit_cap == cargo_cap for each vehicle in - * the consist. */ + if (HasBit(this->vehicle_flags, VF_LAST_LOAD_ST_SEP) || (this->current_order.GetLoadType() == OLFB_CARGO_TYPE_LOAD) || (this->current_order.GetUnloadType() == OUFB_CARGO_TYPE_UNLOAD)) { + /* Always run these, check is handled by check_cargo_can_leave param of LinkRefresher::Run */ this->ResetRefitCaps(); - LinkRefresher::Run(this); + LinkRefresher::Run(this, true, false, true); - /* if the vehicle could load here or could stop with cargo loaded set the last loading station */ - this->last_loading_station = this->last_station_visited; + /* NB: this is saved here as we overwrite it on the first iteration of the loop below */ + StationID head_last_loading_station = this->last_loading_station; + for (Vehicle *u = this; u != NULL; u = u->Next()) { + StationID last_loading_station = HasBit(this->vehicle_flags, VF_LAST_LOAD_ST_SEP) ? u->last_loading_station : head_last_loading_station; + if ((this->current_order.GetCargoLoadType(u->cargo_type) & OLFB_NO_LOAD) == 0 || + (this->current_order.GetCargoUnloadType(u->cargo_type) & OUFB_NO_UNLOAD) == 0) { + if (this->current_order.CanLeaveWithCargo(last_loading_station != INVALID_STATION, u->cargo_type)) { + u->last_loading_station = this->last_station_visited; + } else { + u->last_loading_station = INVALID_STATION; + } + } else { + u->last_loading_station = last_loading_station; + } + } + SetBit(this->vehicle_flags, VF_LAST_LOAD_ST_SEP); } else { - /* if the vehicle couldn't load and had to unload or transfer everything - * set the last loading station to invalid as it will leave empty. */ - this->last_loading_station = INVALID_STATION; + if (this->current_order.CanLeaveWithCargo(this->last_loading_station != INVALID_STATION, 0)) { + /* Refresh next hop stats to make sure we've done that at least once + * during the stop and that refit_cap == cargo_cap for each vehicle in + * the consist. */ + this->ResetRefitCaps(); + LinkRefresher::Run(this); + + /* if the vehicle could load here or could stop with cargo loaded set the last loading station */ + this->last_loading_station = this->last_station_visited; + ClrBit(this->vehicle_flags, VF_LAST_LOAD_ST_SEP); + } else { + /* if the vehicle couldn't load and had to unload or transfer everything + * set the last loading station to invalid as it will leave empty. */ + this->last_loading_station = INVALID_STATION; + ClrBit(this->vehicle_flags, VF_LAST_LOAD_ST_SEP); + } } } diff --git a/src/vehicle_base.h b/src/vehicle_base.h index 9aedc7facd..555ab54bf9 100644 --- a/src/vehicle_base.h +++ b/src/vehicle_base.h @@ -51,6 +51,8 @@ enum VehicleFlags { VF_PATHFINDER_LOST, ///< Vehicle's pathfinder is lost. VF_SERVINT_IS_CUSTOM, ///< Service interval is custom. VF_SERVINT_IS_PERCENT, ///< Service interval is percent. + + VF_LAST_LOAD_ST_SEP = 13, ///< Each vehicle of this chain has its last_loading_station field set separately }; /** Bit numbers used to indicate which of the #NewGRFCache values are valid. */ @@ -243,7 +245,7 @@ public: byte waiting_triggers; ///< Triggers to be yet matched before rerandomizing the random bits. StationID last_station_visited; ///< The last station we stopped at. - StationID last_loading_station; ///< Last station the vehicle has stopped at and could possibly leave from with any cargo loaded. + StationID last_loading_station; ///< Last station the vehicle has stopped at and could possibly leave from with any cargo loaded. (See VF_LAST_LOAD_ST_SEP). CargoID cargo_type; ///< type of cargo this vehicle is carrying byte cargo_subtype; ///< Used for livery refits (NewGRF variations) From 871d26098e7a9bc11fb87209a20b740fae7c8554 Mon Sep 17 00:00:00 2001 From: Jonathan G Rennison Date: Sun, 4 Sep 2016 20:50:20 +0100 Subject: [PATCH 07/11] Move Order::cargo_type_flags into a separately allocated object. Change savegame format, add a new chunk for cargo_type_flags. --- src/order_base.h | 45 +++++++++++++++++++++++++++----- src/order_cmd.cpp | 32 ++++++++++++++++++++--- src/saveload/extended_ver_sl.cpp | 2 +- src/saveload/order_sl.cpp | 37 ++++++++++++++++++++++++-- 4 files changed, 103 insertions(+), 13 deletions(-) diff --git a/src/order_base.h b/src/order_base.h index 11cb78c47b..73582d926e 100644 --- a/src/order_base.h +++ b/src/order_base.h @@ -28,6 +28,12 @@ typedef Pool OrderListPool; extern OrderPool _order_pool; extern OrderListPool _orderlist_pool; +struct OrderExtraInfo { + uint8 cargo_type_flags[NUM_CARGO]; ///< Load/unload types for each cargo type. + + OrderExtraInfo(); +}; + /* If you change this, keep in mind that it is saved on 3 places: * - Load_ORDR, all the global orders * - Vehicle -> current_order @@ -38,6 +44,8 @@ private: friend const struct SaveLoad *GetVehicleDescription(VehicleType vt); ///< Saving and loading the current order of vehicles. friend void Load_VEHS(); ///< Loading of ancient vehicles. friend const struct SaveLoad *GetOrderDescription(); ///< Saving and loading of orders. + friend void Load_ORDX(); ///< Saving and loading of orders. + friend void Save_ORDX(); ///< Saving and loading of orders. uint8 type; ///< The type of order + non-stop flags uint8 flags; ///< Load/unload types, depot order/action types. @@ -45,20 +53,41 @@ private: CargoID refit_cargo; ///< Refit CargoID - uint8 cargo_type_flags[NUM_CARGO]; ///< Load/unload types for each cargo type. + OrderExtraInfo *extra;///< Extra order info 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. uint16 max_speed; ///< How fast the vehicle may go on the way to the destination. + void AllocExtraInfo(); + void DeAllocExtraInfo(); + + inline void CheckExtraInfoAlloced() + { + if (!this->extra) this->AllocExtraInfo(); + } + public: Order *next; ///< Pointer to next order. If NULL, end of list - Order() : refit_cargo(CT_NO_REFIT), max_speed(UINT16_MAX) {} + Order() : refit_cargo(CT_NO_REFIT), extra(NULL), max_speed(UINT16_MAX) {} ~Order(); Order(uint32 packed); + Order(const Order& other) : extra(NULL) + { + *this = other; + } + + inline Order& operator=(Order const& other) + { + AssignOrder(other); + this->next = other.next; + this->index = other.index; + return *this; + } + /** * Check whether this order is of the given type. * @param type the type to check against. @@ -141,7 +170,8 @@ public: inline OrderLoadFlags GetCargoLoadTypeRaw(CargoID cargo_id) const { assert(cargo_id < NUM_CARGO); - return (OrderLoadFlags) GB(this->cargo_type_flags[cargo_id], 4, 4); + if (!this->extra) return OLF_LOAD_IF_POSSIBLE; + return (OrderLoadFlags) GB(this->extra->cargo_type_flags[cargo_id], 4, 4); } /** @@ -169,7 +199,8 @@ public: inline OrderUnloadFlags GetCargoUnloadTypeRaw(CargoID cargo_id) const { assert(cargo_id < NUM_CARGO); - return (OrderUnloadFlags) GB(this->cargo_type_flags[cargo_id], 0, 4); + if (!this->extra) return OUF_UNLOAD_IF_POSSIBLE; + return (OrderUnloadFlags) GB(this->extra->cargo_type_flags[cargo_id], 0, 4); } /** @@ -214,7 +245,8 @@ public: inline void SetLoadType(OrderLoadFlags load_type, CargoID cargo_id) { assert(cargo_id < NUM_CARGO); - SB(this->cargo_type_flags[cargo_id], 4, 4, load_type); + this->CheckExtraInfoAlloced(); + SB(this->extra->cargo_type_flags[cargo_id], 4, 4, load_type); } /** Set how the consist must be unloaded. */ @@ -229,7 +261,8 @@ public: inline void SetUnloadType(OrderUnloadFlags unload_type, CargoID cargo_id) { assert(cargo_id < NUM_CARGO); - SB(this->cargo_type_flags[cargo_id], 0, 4, unload_type); + this->CheckExtraInfoAlloced(); + SB(this->extra->cargo_type_flags[cargo_id], 0, 4, unload_type); } /** Set whether we must stop at stations or not. */ diff --git a/src/order_cmd.cpp b/src/order_cmd.cpp index 9d93ba1cbc..55815c3e47 100644 --- a/src/order_cmd.cpp +++ b/src/order_cmd.cpp @@ -48,6 +48,7 @@ INSTANTIATE_POOL_METHODS(OrderList) /** Clean everything up. */ Order::~Order() { + DeAllocExtraInfo(); if (CleaningPool()) return; /* We can visit oil rigs and buoys that are not our own. They will be shown in @@ -68,6 +69,7 @@ void Order::Free() this->flags = 0; this->dest = 0; this->next = NULL; + DeAllocExtraInfo(); } /** @@ -238,6 +240,7 @@ Order::Order(uint32 packed) this->type = (OrderType)GB(packed, 0, 8); this->flags = GB(packed, 8, 8); this->dest = GB(packed, 16, 16); + this->extra = NULL; this->next = NULL; this->refit_cargo = CT_NO_REFIT; this->wait_time = 0; @@ -284,13 +287,34 @@ void Order::AssignOrder(const Order &other) this->travel_time = other.travel_time; this->max_speed = other.max_speed; - if (this->GetUnloadType() == OUFB_CARGO_TYPE_UNLOAD || this->GetLoadType() == OLFB_CARGO_TYPE_LOAD) { - for (uint i = 0; i < NUM_CARGO; i++) { - this->cargo_type_flags[i] = other.cargo_type_flags[i]; - } + if ((this->GetUnloadType() == OUFB_CARGO_TYPE_UNLOAD || this->GetLoadType() == OLFB_CARGO_TYPE_LOAD) && other.extra != NULL) { + this->AllocExtraInfo(); + *(this->extra) = *(other.extra); + } else { + this->DeAllocExtraInfo(); } } +void Order::AllocExtraInfo() +{ + if (this->extra == NULL) { + this->extra = new OrderExtraInfo(); + } +} + +void Order::DeAllocExtraInfo() +{ + if (this->extra != NULL) { + delete this->extra; + this->extra = NULL; + } +} + +OrderExtraInfo::OrderExtraInfo() +{ + memset(cargo_type_flags, 0, sizeof(cargo_type_flags)); +} + void CargoStationIDStackSet::FillNextStoppingStation(const Vehicle *v, const OrderList *o, const Order *first, uint hops) { this->more.clear(); diff --git a/src/saveload/extended_ver_sl.cpp b/src/saveload/extended_ver_sl.cpp index 4a2a7f1cf2..2966516881 100644 --- a/src/saveload/extended_ver_sl.cpp +++ b/src/saveload/extended_ver_sl.cpp @@ -45,7 +45,7 @@ std::vector _sl_xv_discardable_chunk_ids; ///< list of chunks static const uint32 _sl_xv_slxi_chunk_version = 0; ///< current version os SLXI chunk const SlxiSubChunkInfo _sl_xv_sub_chunk_infos[] = { - { XSLFI_CARGO_TYPE_ORDERS, XSCF_NULL, 1, 1, "cargo_type_orders", NULL, NULL, NULL }, + { XSLFI_CARGO_TYPE_ORDERS, XSCF_NULL, 1, 1, "cargo_type_orders", NULL, NULL, "ORDX" }, { XSLFI_NULL, XSCF_NULL, 0, 0, NULL, NULL, NULL, NULL },// This is the end marker }; diff --git a/src/saveload/order_sl.cpp b/src/saveload/order_sl.cpp index f6a8fae441..4e3d5ff391 100644 --- a/src/saveload/order_sl.cpp +++ b/src/saveload/order_sl.cpp @@ -113,7 +113,6 @@ 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_CONDARR_X(Order, cargo_type_flags, SLE_UINT8, NUM_CARGO, 0, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_CARGO_TYPE_ORDERS, 1)), /* 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 */ @@ -195,6 +194,39 @@ static void Load_ORDR() } } +const SaveLoad *GetOrderExtraInfoDescription() +{ + static const SaveLoad _order_extra_info_desc[] = { + SLE_ARR(OrderExtraInfo, cargo_type_flags, SLE_UINT8, NUM_CARGO), + SLE_END() + }; + + return _order_extra_info_desc; +} + +void Save_ORDX() +{ + Order *order; + + FOR_ALL_ORDERS(order) { + if (order->extra != NULL) { + SlSetArrayIndex(order->index); + SlObject(order->extra, GetOrderExtraInfoDescription()); + } + } +} + +void Load_ORDX() +{ + int index; + while ((index = SlIterateArray()) != -1) { + Order *order = Order::GetIfValid(index); + assert(order != NULL); + order->AllocExtraInfo(); + SlObject(order->extra, GetOrderExtraInfoDescription()); + } +} + static void Ptrs_ORDR() { /* Orders from old savegames have pointers corrected in Load_ORDR */ @@ -309,5 +341,6 @@ static void Ptrs_BKOR() extern const ChunkHandler _order_chunk_handlers[] = { { 'BKOR', Save_BKOR, Load_BKOR, Ptrs_BKOR, NULL, CH_ARRAY}, { 'ORDR', Save_ORDR, Load_ORDR, Ptrs_ORDR, NULL, CH_ARRAY}, - { 'ORDL', Save_ORDL, Load_ORDL, Ptrs_ORDL, NULL, CH_ARRAY | CH_LAST}, + { 'ORDL', Save_ORDL, Load_ORDL, Ptrs_ORDL, NULL, CH_ARRAY}, + { 'ORDX', Save_ORDX, Load_ORDX, NULL, NULL, CH_SPARSE_ARRAY | CH_LAST}, }; From e90b266af1dd117fa2823e6cf3990cfbafaf9f3a Mon Sep 17 00:00:00 2001 From: Jonathan G Rennison Date: Sun, 4 Sep 2016 23:58:17 +0100 Subject: [PATCH 08/11] De-duplicate cargo masking boilerplate using C++11. --- src/linkgraph/refresh.cpp | 20 ++++++-------------- src/order_base.h | 13 +++++++++++++ src/order_cmd.cpp | 34 +++++++++------------------------- 3 files changed, 28 insertions(+), 39 deletions(-) diff --git a/src/linkgraph/refresh.cpp b/src/linkgraph/refresh.cpp index ae2c5fa6e7..f872fc1bfd 100644 --- a/src/linkgraph/refresh.cpp +++ b/src/linkgraph/refresh.cpp @@ -38,22 +38,14 @@ for (const Order *o = v->orders.list->GetFirstOrder(); o != NULL; o = o->next) { if (o->IsType(OT_GOTO_STATION) || o->IsType(OT_IMPLICIT)) { if (o->GetUnloadType() == OUFB_CARGO_TYPE_UNLOAD) { - OrderUnloadFlags ouf = o->GetCargoUnloadType(first_cargo_id); - uint32 other_cargo_mask = cargo_mask; - ClrBit(other_cargo_mask, first_cargo_id); - CargoID cargo; - FOR_EACH_SET_BIT(cargo, other_cargo_mask) { - if (((ouf ^ o->GetCargoUnloadType(cargo)) & (OUFB_TRANSFER | OUFB_UNLOAD | OUFB_NO_UNLOAD)) != 0) ClrBit(cargo_mask, cargo); - } + CargoMaskValueFilter(cargo_mask, [&](CargoID cargo) -> uint { + return o->GetCargoUnloadType(cargo) & (OUFB_TRANSFER | OUFB_UNLOAD | OUFB_NO_UNLOAD); + }); } if (o->GetLoadType() == OLFB_CARGO_TYPE_LOAD) { - OrderLoadFlags olf = o->GetCargoLoadType(first_cargo_id); - uint32 other_cargo_mask = cargo_mask; - ClrBit(other_cargo_mask, first_cargo_id); - CargoID cargo; - FOR_EACH_SET_BIT(cargo, other_cargo_mask) { - if (((olf ^ o->GetCargoLoadType(cargo)) & (OLFB_NO_LOAD)) != 0) ClrBit(cargo_mask, cargo); - } + CargoMaskValueFilter(cargo_mask, [&](CargoID cargo) -> uint { + return o->GetCargoLoadType(cargo) & (OLFB_NO_LOAD); + }); } } } diff --git a/src/order_base.h b/src/order_base.h index 73582d926e..d739044012 100644 --- a/src/order_base.h +++ b/src/order_base.h @@ -388,6 +388,19 @@ public: void FillNextStoppingStation(const Vehicle *v, const OrderList *o, const Order *first = NULL, uint hops = 0); }; +template T CargoMaskValueFilter(uint32 &cargo_mask, F filter_func) +{ + CargoID first_cargo_id = FindFirstBit(cargo_mask); + T value = filter_func(first_cargo_id); + uint32 other_cargo_mask = cargo_mask; + ClrBit(other_cargo_mask, first_cargo_id); + CargoID cargo; + FOR_EACH_SET_BIT(cargo, other_cargo_mask) { + if (value != filter_func(cargo)) ClrBit(cargo_mask, cargo); + } + return value; +} + /** * Shared order list linking together the linked list of orders and the list * of vehicles sharing this order list. diff --git a/src/order_cmd.cpp b/src/order_cmd.cpp index 55815c3e47..66b467a4ec 100644 --- a/src/order_cmd.cpp +++ b/src/order_cmd.cpp @@ -439,16 +439,9 @@ const Order *OrderList::GetNextDecisionNode(const Order *next, uint hops, uint32 /* This is a cargo-specific load/unload order. * If the first cargo is both a no-load and no-unload order, skip it. * Drop cargoes which don't match the first one. */ - CargoID first_cargo_id = FindFirstBit(cargo_mask); - can_load_or_unload = ((next->GetCargoLoadType(first_cargo_id) & OLFB_NO_LOAD) == 0 || (next->GetCargoUnloadType(first_cargo_id) & OUFB_NO_UNLOAD) == 0); - uint32 other_cargo_mask = cargo_mask; - ClrBit(other_cargo_mask, first_cargo_id); - CargoID cargo; - FOR_EACH_SET_BIT(cargo, other_cargo_mask) { - if (can_load_or_unload != ((next->GetCargoLoadType(cargo) & OLFB_NO_LOAD) == 0 || (next->GetCargoUnloadType(cargo) & OUFB_NO_UNLOAD) == 0)) { - ClrBit(cargo_mask, cargo); - } - } + can_load_or_unload = CargoMaskValueFilter(cargo_mask, [&](CargoID cargo) { + return ((next->GetCargoLoadType(cargo) & OLFB_NO_LOAD) == 0 || (next->GetCargoUnloadType(cargo) & OUFB_NO_UNLOAD) == 0); + }); } else if ((next->GetLoadType() & OLFB_NO_LOAD) == 0 || (next->GetUnloadType() & OUFB_NO_UNLOAD) == 0) { can_load_or_unload = true; } @@ -520,21 +513,12 @@ CargoMaskedStationIDStack OrderList::GetNextStoppingStation(const Vehicle *v, ui /* Don't return a next stop if the vehicle has to unload everything. */ if ((next->IsType(OT_GOTO_STATION) || next->IsType(OT_IMPLICIT)) && next->GetDestination() == v->last_station_visited) { - CargoID first_cargo_id = FindFirstBit(cargo_mask); - bool invalid = ((next->GetCargoUnloadType(first_cargo_id) & (OUFB_TRANSFER | OUFB_UNLOAD)) != 0); - if (next->GetUnloadType() == OUFB_CARGO_TYPE_UNLOAD) { - /* This is a cargo-specific load/unload order. - * Don't return a next stop if first cargo has transfer or unload set. - * Drop cargoes which don't match the first one. */ - uint32 other_cargo_mask = cargo_mask; - ClrBit(other_cargo_mask, first_cargo_id); - CargoID cargo; - FOR_EACH_SET_BIT(cargo, other_cargo_mask) { - if (invalid != ((next->GetCargoUnloadType(cargo) & (OUFB_TRANSFER | OUFB_UNLOAD)) != 0)) { - ClrBit(cargo_mask, cargo); - } - } - } + /* This is a cargo-specific load/unload order. + * Don't return a next stop if first cargo has transfer or unload set. + * Drop cargoes which don't match the first one. */ + bool invalid = CargoMaskValueFilter(cargo_mask, [&](CargoID cargo) { + return ((next->GetCargoUnloadType(cargo) & (OUFB_TRANSFER | OUFB_UNLOAD)) != 0); + }); if (invalid) return CargoMaskedStationIDStack(cargo_mask, INVALID_STATION); } } while (next->IsType(OT_GOTO_DEPOT) || next->GetDestination() == v->last_station_visited); From f8f8e642dcb2b367eced42d45d3991cd5904d888 Mon Sep 17 00:00:00 2001 From: Jonathan G Rennison Date: Mon, 5 Sep 2016 01:36:55 +0100 Subject: [PATCH 09/11] Use unique_ptr and initialiser init for OrderExtraInfo. --- src/order_base.h | 13 +++++++------ src/order_cmd.cpp | 15 +++------------ src/saveload/order_sl.cpp | 6 +++--- 3 files changed, 13 insertions(+), 21 deletions(-) diff --git a/src/order_base.h b/src/order_base.h index d739044012..7be22873a3 100644 --- a/src/order_base.h +++ b/src/order_base.h @@ -21,6 +21,7 @@ #include "vehicle_type.h" #include "date_type.h" +#include #include typedef Pool OrderPool; @@ -29,9 +30,7 @@ extern OrderPool _order_pool; extern OrderListPool _orderlist_pool; struct OrderExtraInfo { - uint8 cargo_type_flags[NUM_CARGO]; ///< Load/unload types for each cargo type. - - OrderExtraInfo(); + uint8 cargo_type_flags[NUM_CARGO] = {}; ///< Load/unload types for each cargo type. }; /* If you change this, keep in mind that it is saved on 3 places: @@ -53,7 +52,7 @@ private: CargoID refit_cargo; ///< Refit CargoID - OrderExtraInfo *extra;///< Extra order info + std::unique_ptr extra; ///< Extra order info 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. @@ -70,16 +69,18 @@ private: public: Order *next; ///< Pointer to next order. If NULL, end of list - Order() : refit_cargo(CT_NO_REFIT), extra(NULL), max_speed(UINT16_MAX) {} + Order() : refit_cargo(CT_NO_REFIT), max_speed(UINT16_MAX) {} ~Order(); Order(uint32 packed); - Order(const Order& other) : extra(NULL) + Order(const Order& other) { *this = other; } + Order(Order&& other) = default; + inline Order& operator=(Order const& other) { AssignOrder(other); diff --git a/src/order_cmd.cpp b/src/order_cmd.cpp index 66b467a4ec..81fc313889 100644 --- a/src/order_cmd.cpp +++ b/src/order_cmd.cpp @@ -48,7 +48,6 @@ INSTANTIATE_POOL_METHODS(OrderList) /** Clean everything up. */ Order::~Order() { - DeAllocExtraInfo(); if (CleaningPool()) return; /* We can visit oil rigs and buoys that are not our own. They will be shown in @@ -297,22 +296,14 @@ void Order::AssignOrder(const Order &other) void Order::AllocExtraInfo() { - if (this->extra == NULL) { - this->extra = new OrderExtraInfo(); + if (!this->extra) { + this->extra.reset(new OrderExtraInfo()); } } void Order::DeAllocExtraInfo() { - if (this->extra != NULL) { - delete this->extra; - this->extra = NULL; - } -} - -OrderExtraInfo::OrderExtraInfo() -{ - memset(cargo_type_flags, 0, sizeof(cargo_type_flags)); + this->extra.reset(); } void CargoStationIDStackSet::FillNextStoppingStation(const Vehicle *v, const OrderList *o, const Order *first, uint hops) diff --git a/src/saveload/order_sl.cpp b/src/saveload/order_sl.cpp index 4e3d5ff391..03335506ff 100644 --- a/src/saveload/order_sl.cpp +++ b/src/saveload/order_sl.cpp @@ -209,9 +209,9 @@ void Save_ORDX() Order *order; FOR_ALL_ORDERS(order) { - if (order->extra != NULL) { + if (order->extra) { SlSetArrayIndex(order->index); - SlObject(order->extra, GetOrderExtraInfoDescription()); + SlObject(order->extra.get(), GetOrderExtraInfoDescription()); } } } @@ -223,7 +223,7 @@ void Load_ORDX() Order *order = Order::GetIfValid(index); assert(order != NULL); order->AllocExtraInfo(); - SlObject(order->extra, GetOrderExtraInfoDescription()); + SlObject(order->extra.get(), GetOrderExtraInfoDescription()); } } From 6031cca2d784002bb6ab4dc8f5e5ebb8601dd9c6 Mon Sep 17 00:00:00 2001 From: Jonathan G Rennison Date: Mon, 5 Sep 2016 20:47:31 +0100 Subject: [PATCH 10/11] Refactor Link Refresher cargo mask handling. Add some separate helper functions. Move cargo checks out of LinkRefresher::Run. --- src/linkgraph/refresh.cpp | 42 +++++++--------------- src/linkgraph/refresh.h | 2 +- src/order_base.h | 24 +++++++++++++ src/vehicle.cpp | 73 +++++++++++++++++++++++++-------------- src/vehicle_base.h | 2 ++ 5 files changed, 87 insertions(+), 56 deletions(-) diff --git a/src/linkgraph/refresh.cpp b/src/linkgraph/refresh.cpp index f872fc1bfd..c28876fca0 100644 --- a/src/linkgraph/refresh.cpp +++ b/src/linkgraph/refresh.cpp @@ -24,61 +24,43 @@ * @param v Vehicle to refresh links for. * @param allow_merge If the refresher is allowed to merge or extend link graphs. * @param is_full_loading If the vehicle is full loading. + * @param cargo_mask Mask of cargoes to refresh */ -/* static */ void LinkRefresher::Run(Vehicle *v, bool allow_merge, bool is_full_loading, bool check_cargo_can_leave) +/* static */ void LinkRefresher::Run(Vehicle *v, bool allow_merge, bool is_full_loading, uint32 cargo_mask) { /* If there are no orders we can't predict anything.*/ if (v->orders.list == NULL) return; + uint32 have_cargo_mask = v->GetLastLoadingStationValidCargoMask(); + /* Scan orders for cargo-specific load/unload, and run LinkRefresher separately for each set of cargoes where they differ. */ - uint32 cargoes_to_check = ~0; - while (cargoes_to_check != 0) { - uint32 cargo_mask = cargoes_to_check; - const CargoID first_cargo_id = FindFirstBit(cargo_mask); + while (cargo_mask != 0) { + uint32 iter_cargo_mask = cargo_mask; for (const Order *o = v->orders.list->GetFirstOrder(); o != NULL; o = o->next) { if (o->IsType(OT_GOTO_STATION) || o->IsType(OT_IMPLICIT)) { if (o->GetUnloadType() == OUFB_CARGO_TYPE_UNLOAD) { - CargoMaskValueFilter(cargo_mask, [&](CargoID cargo) -> uint { + CargoMaskValueFilter(iter_cargo_mask, [&](CargoID cargo) -> uint { return o->GetCargoUnloadType(cargo) & (OUFB_TRANSFER | OUFB_UNLOAD | OUFB_NO_UNLOAD); }); } if (o->GetLoadType() == OLFB_CARGO_TYPE_LOAD) { - CargoMaskValueFilter(cargo_mask, [&](CargoID cargo) -> uint { + CargoMaskValueFilter(iter_cargo_mask, [&](CargoID cargo) -> uint { return o->GetCargoLoadType(cargo) & (OLFB_NO_LOAD); }); } } } - bool has_cargo; - if (!HasBit(v->vehicle_flags, VF_LAST_LOAD_ST_SEP)) { - has_cargo = v->last_loading_station != INVALID_STATION; - } else { - has_cargo = false; - for (const Vehicle *u = v; u != NULL; u = u->Next()) { - if (u->cargo_type < NUM_CARGO && HasBit(cargo_mask, u->cargo_type) && u->last_loading_station != INVALID_STATION) { - has_cargo = true; - break; - } - } - } - - if (check_cargo_can_leave && (v->current_order.IsType(OT_GOTO_STATION) || v->current_order.IsType(OT_IMPLICIT)) && - (!v->current_order.CanLeaveWithCargo(has_cargo, first_cargo_id))) { - cargoes_to_check &= ~cargo_mask; - continue; - } - /* Make sure the first order is a useful order. */ - const Order *first = v->orders.list->GetNextDecisionNode(v->GetOrder(v->cur_implicit_order_index), 0, cargo_mask); + const Order *first = v->orders.list->GetNextDecisionNode(v->GetOrder(v->cur_implicit_order_index), 0, iter_cargo_mask); if (first != NULL) { HopSet seen_hops; - LinkRefresher refresher(v, &seen_hops, allow_merge, is_full_loading, cargo_mask); + LinkRefresher refresher(v, &seen_hops, allow_merge, is_full_loading, iter_cargo_mask); - refresher.RefreshLinks(first, first, has_cargo ? 1 << HAS_CARGO : 0); + refresher.RefreshLinks(first, first, (iter_cargo_mask & have_cargo_mask) ? 1 << HAS_CARGO : 0); } - cargoes_to_check &= ~cargo_mask; + cargo_mask &= ~iter_cargo_mask; } } diff --git a/src/linkgraph/refresh.h b/src/linkgraph/refresh.h index 3d30853274..0b834a3e54 100644 --- a/src/linkgraph/refresh.h +++ b/src/linkgraph/refresh.h @@ -23,7 +23,7 @@ */ class LinkRefresher { public: - static void Run(Vehicle *v, bool allow_merge = true, bool is_full_loading = false, bool check_cargo_can_leave = false); + static void Run(Vehicle *v, bool allow_merge = true, bool is_full_loading = false, uint32 cargo_mask = ~0); protected: /** diff --git a/src/order_base.h b/src/order_base.h index 7be22873a3..2e1a864783 100644 --- a/src/order_base.h +++ b/src/order_base.h @@ -217,6 +217,20 @@ public: return ouf; } + template uint32 FilterLoadUnloadTypeCargoMask(F filter_func, uint32 cargo_mask = ~0) + { + if ((this->GetLoadType() == OLFB_CARGO_TYPE_LOAD) || (this->GetUnloadType() == OUFB_CARGO_TYPE_UNLOAD)) { + CargoID cargo; + uint32 output_mask = cargo_mask; + FOR_EACH_SET_BIT(cargo, cargo_mask) { + if (!filter_func(this, cargo)) ClrBit(output_mask, cargo); + } + return output_mask; + } else { + return filter_func(this, FindFirstBit(cargo_mask)) ? cargo_mask : 0; + } + } + /** At which stations must we stop? */ inline OrderNonStopFlags GetNonStopType() const { return (OrderNonStopFlags)GB(this->type, 6, 2); } /** Where must we stop at the platform? */ @@ -389,6 +403,16 @@ public: void FillNextStoppingStation(const Vehicle *v, const OrderList *o, const Order *first = NULL, uint hops = 0); }; +template uint32 FilterCargoMask(F filter_func, uint32 cargo_mask = ~0) +{ + CargoID cargo; + uint32 output_mask = cargo_mask; + FOR_EACH_SET_BIT(cargo, cargo_mask) { + if (!filter_func(cargo)) ClrBit(output_mask, cargo); + } + return output_mask; +} + template T CargoMaskValueFilter(uint32 &cargo_mask, F filter_func) { CargoID first_cargo_id = FindFirstBit(cargo_mask); diff --git a/src/vehicle.cpp b/src/vehicle.cpp index 44f279a95f..8965f4a4a2 100644 --- a/src/vehicle.cpp +++ b/src/vehicle.cpp @@ -2100,6 +2100,21 @@ void Vehicle::CancelReservation(StationID next, Station *st) } } +uint32 Vehicle::GetLastLoadingStationValidCargoMask() const +{ + if (!HasBit(this->vehicle_flags, VF_LAST_LOAD_ST_SEP)) { + return (this->last_loading_station != INVALID_STATION) ? ~0 : 0; + } else { + uint32 cargo_mask = 0; + for (const Vehicle *u = this; u != NULL; u = u->Next()) { + if (u->cargo_type < NUM_CARGO && u->last_loading_station != INVALID_STATION) { + SetBit(cargo_mask, u->cargo_type); + } + } + return cargo_mask; + } +} + /** * Perform all actions when leaving a station. * @pre this->current_order.IsType(OT_LOADING) @@ -2114,20 +2129,45 @@ void Vehicle::LeaveStation() /* Only update the timetable if the vehicle was supposed to stop here. */ if (this->current_order.GetNonStopType() != ONSF_STOP_EVERYWHERE) UpdateVehicleTimetable(this, false); - if ((this->current_order.GetLoadType() & OLFB_NO_LOAD) == 0 || - (this->current_order.GetUnloadType() & OUFB_NO_UNLOAD) == 0) { - if (HasBit(this->vehicle_flags, VF_LAST_LOAD_ST_SEP) || (this->current_order.GetLoadType() == OLFB_CARGO_TYPE_LOAD) || (this->current_order.GetUnloadType() == OUFB_CARGO_TYPE_UNLOAD)) { - /* Always run these, check is handled by check_cargo_can_leave param of LinkRefresher::Run */ + uint32 cargoes_can_load_unload = this->current_order.FilterLoadUnloadTypeCargoMask([&](const Order *o, CargoID cargo) { + return ((o->GetCargoLoadType(cargo) & OLFB_NO_LOAD) == 0) || ((o->GetCargoUnloadType(cargo) & OUFB_NO_UNLOAD) == 0); + }); + uint32 has_cargo_mask = this->GetLastLoadingStationValidCargoMask(); + uint32 cargoes_can_leave_with_cargo = FilterCargoMask([&](CargoID cargo) { + return this->current_order.CanLeaveWithCargo(HasBit(has_cargo_mask, cargo), cargo); + }, cargoes_can_load_unload); + + if (cargoes_can_load_unload != 0) { + if (cargoes_can_leave_with_cargo != 0) { + /* Refresh next hop stats to make sure we've done that at least once + * during the stop and that refit_cap == cargo_cap for each vehicle in + * the consist. */ this->ResetRefitCaps(); - LinkRefresher::Run(this, true, false, true); + LinkRefresher::Run(this, true, false, cargoes_can_leave_with_cargo); + } + + if (cargoes_can_leave_with_cargo == (uint32) ~0) { + /* can leave with all cargoes */ + + /* if the vehicle could load here or could stop with cargo loaded set the last loading station */ + this->last_loading_station = this->last_station_visited; + ClrBit(this->vehicle_flags, VF_LAST_LOAD_ST_SEP); + } else if (cargoes_can_leave_with_cargo == 0) { + /* can leave with no cargoes */ + + /* if the vehicle couldn't load and had to unload or transfer everything + * set the last loading station to invalid as it will leave empty. */ + this->last_loading_station = INVALID_STATION; + ClrBit(this->vehicle_flags, VF_LAST_LOAD_ST_SEP); + } else { + /* mix of cargoes loadable or could not leave with all cargoes */ /* NB: this is saved here as we overwrite it on the first iteration of the loop below */ StationID head_last_loading_station = this->last_loading_station; for (Vehicle *u = this; u != NULL; u = u->Next()) { StationID last_loading_station = HasBit(this->vehicle_flags, VF_LAST_LOAD_ST_SEP) ? u->last_loading_station : head_last_loading_station; - if ((this->current_order.GetCargoLoadType(u->cargo_type) & OLFB_NO_LOAD) == 0 || - (this->current_order.GetCargoUnloadType(u->cargo_type) & OUFB_NO_UNLOAD) == 0) { - if (this->current_order.CanLeaveWithCargo(last_loading_station != INVALID_STATION, u->cargo_type)) { + if (u->cargo_type < NUM_CARGO && HasBit(cargoes_can_load_unload, u->cargo_type)) { + if (HasBit(cargoes_can_leave_with_cargo, u->cargo_type)) { u->last_loading_station = this->last_station_visited; } else { u->last_loading_station = INVALID_STATION; @@ -2137,23 +2177,6 @@ void Vehicle::LeaveStation() } } SetBit(this->vehicle_flags, VF_LAST_LOAD_ST_SEP); - } else { - if (this->current_order.CanLeaveWithCargo(this->last_loading_station != INVALID_STATION, 0)) { - /* Refresh next hop stats to make sure we've done that at least once - * during the stop and that refit_cap == cargo_cap for each vehicle in - * the consist. */ - this->ResetRefitCaps(); - LinkRefresher::Run(this); - - /* if the vehicle could load here or could stop with cargo loaded set the last loading station */ - this->last_loading_station = this->last_station_visited; - ClrBit(this->vehicle_flags, VF_LAST_LOAD_ST_SEP); - } else { - /* if the vehicle couldn't load and had to unload or transfer everything - * set the last loading station to invalid as it will leave empty. */ - this->last_loading_station = INVALID_STATION; - ClrBit(this->vehicle_flags, VF_LAST_LOAD_ST_SEP); - } } } diff --git a/src/vehicle_base.h b/src/vehicle_base.h index 555ab54bf9..785f605446 100644 --- a/src/vehicle_base.h +++ b/src/vehicle_base.h @@ -279,6 +279,8 @@ public: /** We want to 'destruct' the right class. */ virtual ~Vehicle(); + uint32 GetLastLoadingStationValidCargoMask() const; + void BeginLoading(); void CancelReservation(StationID next, Station *st); void LeaveStation(); From bc04b07f2be187258f32fa0d64d757a680665a83 Mon Sep 17 00:00:00 2001 From: Jonathan G Rennison Date: Thu, 8 Sep 2016 00:12:27 +0100 Subject: [PATCH 11/11] Fix double-use of bits 3 and 7 in Order::flags. Use a different encoding for the cargo type load/unload bit in flags. --- src/order_base.h | 26 ++++++++++++++++++++++---- src/order_type.h | 4 +++- 2 files changed, 25 insertions(+), 5 deletions(-) diff --git a/src/order_base.h b/src/order_base.h index 2e1a864783..9b4292db15 100644 --- a/src/order_base.h +++ b/src/order_base.h @@ -160,7 +160,12 @@ public: void SetRefit(CargoID cargo); /** How must the consist be loaded? */ - inline OrderLoadFlags GetLoadType() const { return (OrderLoadFlags)GB(this->flags, 4, 4); } + inline OrderLoadFlags GetLoadType() const + { + OrderLoadFlags type = (OrderLoadFlags)GB(this->flags, 4, 3); + if (type == OLFB_CARGO_TYPE_LOAD_ENCODING) type = OLFB_CARGO_TYPE_LOAD; + return type; + } /** * How must the consist be loaded for this type of cargo? @@ -189,7 +194,12 @@ public: } /** How must the consist be unloaded? */ - inline OrderUnloadFlags GetUnloadType() const { return (OrderUnloadFlags)GB(this->flags, 0, 4); } + inline OrderUnloadFlags GetUnloadType() const + { + OrderUnloadFlags type = (OrderUnloadFlags)GB(this->flags, 0, 3); + if (type == OUFB_CARGO_TYPE_UNLOAD_ENCODING) type = OUFB_CARGO_TYPE_UNLOAD; + return type; + } /** * How must the consist be unloaded for this type of cargo? @@ -249,7 +259,11 @@ public: inline uint16 GetConditionValue() const { return GB(this->dest, 0, 11); } /** Set how the consist must be loaded. */ - inline void SetLoadType(OrderLoadFlags load_type) { SB(this->flags, 4, 4, load_type); } + inline void SetLoadType(OrderLoadFlags load_type) + { + if (load_type == OLFB_CARGO_TYPE_LOAD) load_type = OLFB_CARGO_TYPE_LOAD_ENCODING; + SB(this->flags, 4, 3, load_type); + } /** * Set how the consist must be loaded for this type of cargo. @@ -265,7 +279,11 @@ public: } /** Set how the consist must be unloaded. */ - inline void SetUnloadType(OrderUnloadFlags unload_type) { SB(this->flags, 0, 4, unload_type); } + inline void SetUnloadType(OrderUnloadFlags unload_type) + { + if (unload_type == OUFB_CARGO_TYPE_UNLOAD) unload_type = OUFB_CARGO_TYPE_UNLOAD_ENCODING; + SB(this->flags, 0, 3, unload_type); + } /** * Set how the consist must be unloaded for this type of cargo. diff --git a/src/order_type.h b/src/order_type.h index 05625c422e..78f7ca23c7 100644 --- a/src/order_type.h +++ b/src/order_type.h @@ -61,6 +61,7 @@ enum OrderUnloadFlags { OUFB_TRANSFER = 1 << 1, ///< Transfer all cargo onto the platform. OUFB_NO_UNLOAD = 1 << 2, ///< Totally no unloading will be done. OUFB_CARGO_TYPE_UNLOAD = 1 << 3, ///< Unload actions are defined per cargo type. + OUFB_CARGO_TYPE_UNLOAD_ENCODING = (1 << 0) | (1 << 2), ///< Raw encoding of OUFB_CARGO_TYPE_UNLOAD }; /** @@ -71,7 +72,8 @@ enum OrderLoadFlags { OLFB_FULL_LOAD = 1 << 1, ///< Full load all cargoes of the consist. OLF_FULL_LOAD_ANY = 3, ///< Full load a single cargo of the consist. OLFB_NO_LOAD = 4, ///< Do not load anything. - OLFB_CARGO_TYPE_LOAD = 1 << 3 ///< Load actions are defined per cargo type. + OLFB_CARGO_TYPE_LOAD = 1 << 3, ///< Load actions are defined per cargo type. + OLFB_CARGO_TYPE_LOAD_ENCODING = (1 << 1) | 4, ///< Raw encoding of OLFB_CARGO_TYPE_LOAD }; /**