Merge branch 'cargo_type_order' into jgrpp

# Conflicts:
#	src/order_base.h
#	src/order_gui.cpp
#	src/order_type.h
#	src/saveload/extended_ver_sl.cpp
#	src/saveload/extended_ver_sl.h
#	src/vehicle_base.h
This commit is contained in:
Jonathan G Rennison
2016-09-08 00:12:48 +01:00
19 changed files with 1079 additions and 159 deletions

View File

@@ -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<typename Titem, typename Tindex, Tindex Tgrowth_step, Tindex Tmax_size>
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<SimplePoolPoolItem, Tgrowth_step> 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<const SmallStack *>(
@@ -277,7 +259,6 @@ protected:
inline void Branch()
{
if (this->next != Tmax_size) {
ThreadMutexLocker lock(_pool.GetMutex());
++(_pool.Get(this->next).branch_count);
}
}

View File

@@ -1251,6 +1251,30 @@ 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 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)
{
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 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)
{
return v->First()->current_order.GetCargoUnloadType(v->cargo_type);
}
/**
* Prepare the vehicle to be unloaded.
* @param curr_station the station where the consist is at the moment
@@ -1274,16 +1298,17 @@ 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()) {
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,
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);
}
@@ -1436,8 +1461,9 @@ 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
/**
* Create a finalizing action.
@@ -1445,9 +1471,10 @@ 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) :
consist_capleft(consist_capleft), st(st), next_station(next_station), do_reserve(do_reserve) {}
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) {}
/**
* Reserve cargo from the station and update the remaining consist capacities with the
@@ -1457,9 +1484,9 @@ struct FinalizeRefitAction
*/
bool operator()(Vehicle *v)
{
if (this->do_reserve) {
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;
@@ -1474,7 +1501,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;
@@ -1492,7 +1519,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.
@@ -1524,23 +1551,26 @@ 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();
}
struct ReserveCargoAction {
Station *st;
StationIDStack *next_station;
const CargoStationIDStackSet &next_station;
Vehicle *cargo_type_loading;
ReserveCargoAction(Station *st, StationIDStack *next_station) :
st(st), next_station(next_station) {}
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)
{
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;
@@ -1555,8 +1585,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, 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"
@@ -1572,9 +1603,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.GetCargoLoadTypeRaw(v->cargo_type) & OLFB_FULL_LOAD)) continue;
(*consist_capleft)[v->cargo_type] += v->cargo_cap - v->cargo.RemainingCount();
}
}
@@ -1611,14 +1643,25 @@ 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;
if (_settings_game.order.improved_load && use_autorefit ?
front->cargo_payment == NULL : (front->current_order.GetLoadType() & OLFB_FULL_LOAD) != 0) {
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) should_reserve_consist = true;
} else {
if ((front->current_order.GetLoadType() & OLFB_FULL_LOAD) || (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);
next_station,
reserve_consist_cargo_type_loading);
}
/* We have not waited enough time till the next round of loading/unloading */
@@ -1658,7 +1701,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?
@@ -1668,7 +1711,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<VehicleCargoList::MTA_DELIVER, VehicleCargoList::MTA_TRANSFER>(
v->cargo.ActionCount(VehicleCargoList::MTA_DELIVER), INVALID_STATION);
@@ -1725,7 +1768,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) {
@@ -1768,7 +1811,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. */
@@ -1828,6 +1871,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.GetCargoLoadTypeRaw(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
@@ -1838,7 +1891,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);
}
@@ -1847,7 +1900,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 */

View File

@@ -4276,6 +4276,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
@@ -4283,6 +4284,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
@@ -4398,31 +4400,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 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 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 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 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 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 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 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 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
@@ -4518,6 +4537,19 @@ STR_DATE_YEAR_TOOLTIP :{BLACK}Select y
STR_DATE_MINUTES_DAY_TOOLTIP :{BLACK}Select minute
STR_DATE_MINUTES_MONTH_TOOLTIP :{BLACK}Select hour
# 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

View File

@@ -24,20 +24,44 @@
* @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)
/* 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. */
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<uint>(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<uint>(iter_cargo_mask, [&](CargoID cargo) -> uint {
return o->GetCargoLoadType(cargo) & (OLFB_NO_LOAD);
});
}
}
}
/* 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;
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);
LinkRefresher refresher(v, &seen_hops, allow_merge, is_full_loading, iter_cargo_mask);
refresher.RefreshLinks(first, first, v->last_loading_station != INVALID_STATION ? 1 << HAS_CARGO : 0);
refresher.RefreshLinks(first, first, (iter_cargo_mask & have_cargo_mask) ? 1 << HAS_CARGO : 0);
}
cargo_mask &= ~iter_cargo_mask;
}
}
/**
@@ -68,9 +92,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 +199,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 +215,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 +236,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 +248,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 +339,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 {

View File

@@ -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, uint32 cargo_mask = ~0);
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();

View File

@@ -21,11 +21,18 @@
#include "vehicle_type.h"
#include "date_type.h"
#include <memory>
#include <vector>
typedef Pool<Order, OrderID, 256, 64000> OrderPool;
typedef Pool<OrderList, OrderListID, 128, 64000> OrderListPool;
extern OrderPool _order_pool;
extern OrderListPool _orderlist_pool;
struct 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:
* - Load_ORDR, all the global orders
* - Vehicle -> current_order
@@ -36,6 +43,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.
@@ -46,10 +55,20 @@ private:
uint8 occupancy; ///< Estimate of vehicle occupancy on departure, for the current order, 0 indicates invalid, 1 - 101 indicate 0 - 100%
int8 jump_counter; ///< Counter for the 'jump xx% of times' option
std::unique_ptr<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
@@ -58,6 +77,21 @@ public:
Order(uint32 packed);
Order(const Order& other)
{
*this = other;
}
Order(Order&& other) = default;
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.
@@ -138,9 +172,87 @@ public:
bool UpdateJumpCounter(uint8 percent);
/** How must the consist be loaded? */
inline OrderLoadFlags GetLoadType() const { return (OrderLoadFlags)GB(this->flags, 4, 3); }
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?
* @pre GetLoadType() == OLFB_CARGO_TYPE_LOAD
* @param cargo_id The cargo type index.
* @return The load type for this cargo.
*/
inline OrderLoadFlags GetCargoLoadTypeRaw(CargoID cargo_id) const
{
assert(cargo_id < NUM_CARGO);
if (!this->extra) return OLF_LOAD_IF_POSSIBLE;
return (OrderLoadFlags) GB(this->extra->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, 3); }
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?
* @pre GetUnloadType() == OUFB_CARGO_TYPE_UNLOAD
* @param cargo_id The cargo type index.
* @return The unload type for this cargo.
*/
inline OrderUnloadFlags GetCargoUnloadTypeRaw(CargoID cargo_id) const
{
assert(cargo_id < NUM_CARGO);
if (!this->extra) return OUF_UNLOAD_IF_POSSIBLE;
return (OrderUnloadFlags) GB(this->extra->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;
}
template <typename F> 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? */
@@ -161,9 +273,45 @@ 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)
{
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.
* @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);
this->CheckExtraInfoAlloced();
SB(this->extra->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)
{
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.
* @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);
this->CheckExtraInfoAlloced();
SB(this->extra->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. */
@@ -246,8 +394,7 @@ public:
inline void SetOccupancy(uint8 occupancy) { this->occupancy = occupancy; }
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;
@@ -273,6 +420,58 @@ 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<CargoMaskedStationIDStack> 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);
};
template <typename F> 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 <typename T, typename F> 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.
@@ -345,8 +544,8 @@ public:
*/
inline VehicleOrderID GetNumManualOrders() const { return this->num_manual_orders; }
StationIDStack GetNextStoppingStation(const Vehicle *v, const Order *first = NULL, uint hops = 0) const;
const Order *GetNextDecisionNode(const Order *next, uint hops) 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, uint32 &cargo_mask) const;
void InsertOrderAt(Order *new_order, int index);
void DeleteOrderAt(int index);

View File

@@ -24,6 +24,7 @@
#include "cargotype.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"
@@ -73,6 +74,7 @@ void Order::Free()
this->flags = 0;
this->dest = 0;
this->next = NULL;
DeAllocExtraInfo();
}
/**
@@ -267,6 +269,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->occupancy = 0;
@@ -316,6 +319,38 @@ void Order::AssignOrder(const Order &other)
this->jump_counter = other.jump_counter;
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) && other.extra != NULL) {
this->AllocExtraInfo();
*(this->extra) = *(other.extra);
} else {
this->DeAllocExtraInfo();
}
}
void Order::AllocExtraInfo()
{
if (!this->extra) {
this->extra.reset(new OrderExtraInfo());
}
}
void Order::DeAllocExtraInfo()
{
this->extra.reset();
}
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);
}
}
/**
@@ -394,14 +429,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)) {
@@ -411,7 +449,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)) {
@@ -419,8 +457,23 @@ 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. */
can_load_or_unload = CargoMaskValueFilter<bool>(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;
}
}
if (!can_load_or_unload) {
return this->GetNextDecisionNode(this->GetNext(next), hops + 1, cargo_mask);
}
return next;
@@ -429,21 +482,23 @@ 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);
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
@@ -454,37 +509,46 @@ StationIDStack OrderList::GetNextStoppingStation(const Vehicle *v, const Order *
}
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) {
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);
/* 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) {
/* 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<bool>(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);
return next->GetDestination();
return CargoMaskedStationIDStack(cargo_mask, next->GetDestination());
}
/**
@@ -1345,6 +1409,7 @@ CommandCost CmdMoveOrder(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32
* @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
*/
@@ -1354,6 +1419,7 @@ CommandCost CmdModifyOrder(TileIndex tile, DoCommandFlag flags, uint32 p1, uint3
VehicleID veh = GB(p1, 0, 20);
ModifyOrderFlags mof = Extract<ModifyOrderFlags, 0, 4>(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;
@@ -1369,7 +1435,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:
@@ -1402,17 +1468,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;
@@ -1495,11 +1571,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:
@@ -2462,24 +2546,16 @@ 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);
}
/**

View File

@@ -35,9 +35,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->GetCargoLoadTypeRaw(cargo_id) : (uint8) order->GetCargoUnloadTypeRaw(cargo_id);
this->GetWidget<NWidgetCore>(WID_CTO_CARGO_DROPDOWN_FIRST + i)->SetDataTip(this->cargo_type_order_dropdown[order_type], tooltip);
}
this->set_to_all_dropdown_sel = 0;
this->GetWidget<NWidgetCore>(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->GetCargoLoadTypeRaw(cargo_id) : (uint8) order->GetCargoUnloadTypeRaw(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<NWidgetCore>(WID_CTO_CAPTION)->SetDataTip(STR_CARGO_TYPE_ORDERS_LOAD_CAPTION + this->variant, STR_TOOLTIP_WINDOW_TITLE_DRAG_THIS);
this->GetWidget<NWidgetCore>(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<NWidgetCore>(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<NWidgetCore>(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. */
{
@@ -46,30 +342,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. */
@@ -79,30 +412,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,
}
}
};
@@ -121,6 +491,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
};
@@ -130,6 +504,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
};
@@ -671,14 +1049,18 @@ 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;
}
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);
}
/**
* Handle the 'no loading' hotkey
*/
@@ -727,18 +1109,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;
}
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);
}
}
@@ -1451,7 +1837,7 @@ public:
if (this->GetWidget<NWidgetLeaf>(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, 0, DDSF_LOST_FOCUS);
ShowDropDownMenu(this, _order_full_load_drowdown, this->vehicle->GetOrder(this->OrderGetSel())->GetLoadType(), WID_O_FULL_LOAD, 0, 0xE2 /* 1110 0010 */, 0, DDSF_LOST_FOCUS);
}
break;
@@ -1459,7 +1845,7 @@ public:
if (this->GetWidget<NWidgetLeaf>(widget)->ButtonHit(pt)) {
this->OrderClick_Unload(-1);
} else {
ShowDropDownMenu(this, _order_unload_drowdown, this->vehicle->GetOrder(this->OrderGetSel())->GetUnloadType(), WID_O_UNLOAD, 0, 8, 0, DDSF_LOST_FOCUS);
ShowDropDownMenu(this, _order_unload_drowdown, this->vehicle->GetOrder(this->OrderGetSel())->GetUnloadType(), WID_O_UNLOAD, 0, 0xE8 /* 1110 1000 */, 0, DDSF_LOST_FOCUS);
}
break;

View File

@@ -61,6 +61,8 @@ 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.
OUFB_CARGO_TYPE_UNLOAD_ENCODING = (1 << 0) | (1 << 2), ///< Raw encoding of OUFB_CARGO_TYPE_UNLOAD
};
/**
@@ -71,6 +73,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_ENCODING = (1 << 1) | 4, ///< Raw encoding of OLFB_CARGO_TYPE_LOAD
};
/**
@@ -171,6 +175,8 @@ enum ModifyOrderFlags {
MOF_COND_VALUE, ///< The value to set the condition to.
MOF_COND_DESTINATION,///< Change the destination of a conditional order.
MOF_WAYPOINT_FLAGS, ///< Change the waypoint flags
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<ModifyOrderFlags> : MakeEnumPropsT<ModifyOrderFlags, byte, MOF_NON_STOP, MOF_END, MOF_END, 4> {};

View File

@@ -70,6 +70,8 @@ const SlxiSubChunkInfo _sl_xv_sub_chunk_infos[] = {
{ XSLFI_LINKGRAPH_DAY_SCALE, XSCF_NULL, 1, 1, "linkgraph_day_scale", NULL, NULL, NULL },
{ XSLFI_TEMPLATE_REPLACEMENT, XSCF_NULL, 1, 1, "template_replacement", NULL, NULL, "TRPL,TMPL" },
{ XSLFI_MORE_RAIL_TYPES, XSCF_NULL, 1, 1, "more_rail_types", 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
};

View File

@@ -44,6 +44,7 @@ enum SlXvFeatureIndex {
XSLFI_LINKGRAPH_DAY_SCALE, ///< Linkgraph job duration & interval may be in non-scaled days
XSLFI_TEMPLATE_REPLACEMENT, ///< Template-based train replacement
XSLFI_MORE_RAIL_TYPES, ///< Increased number of rail types
XSLFI_CARGO_TYPE_ORDERS, ///< Cargo-specific load/unload order flags
XSLFI_RIFF_HEADER_60_BIT, ///< Size field in RIFF chunk header is 60 bit
XSLFI_HEIGHT_8_BIT, ///< Map tile height is 8 bit instead of 4 bit, but savegame version may be before this became true in trunk

View File

@@ -197,6 +197,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) {
SlSetArrayIndex(order->index);
SlObject(order->extra.get(), GetOrderExtraInfoDescription());
}
}
}
void Load_ORDX()
{
int index;
while ((index = SlIterateArray()) != -1) {
Order *order = Order::GetIfValid(index);
assert(order != NULL);
order->AllocExtraInfo();
SlObject(order->extra.get(), GetOrderExtraInfoDescription());
}
}
static void Ptrs_ORDR()
{
/* Orders from old savegames have pointers corrected in Load_ORDR */
@@ -312,5 +345,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},
};

View File

@@ -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
@@ -1957,6 +1969,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 {

View File

@@ -3695,28 +3695,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)
{

View File

@@ -1409,6 +1409,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();

View File

@@ -1867,6 +1867,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);
@@ -2339,6 +2340,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.
@@ -2449,12 +2477,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);
@@ -2486,6 +2509,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)
@@ -2500,21 +2538,54 @@ 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 (this->current_order.CanLeaveWithCargo(this->last_loading_station != INVALID_STATION)) {
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);
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;
} else {
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 (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;
}
} else {
u->last_loading_station = last_loading_station;
}
}
SetBit(this->vehicle_flags, VF_LAST_LOAD_ST_SEP);
}
}

View File

@@ -58,6 +58,7 @@ enum VehicleFlags {
// Additional flags not in trunk are added at the end to avoid clashing with any new
// flags which get added in future trunk, and to avoid re-ordering flags which are in trunk already,
// as this breaks savegame compatibility.
VF_LAST_LOAD_ST_SEP = 13, ///< Each vehicle of this chain has its last_loading_station field set separately
VF_TIMETABLE_SEPARATION = 14,///< Whether the vehicle should manage the timetable automatically.
VF_AUTOMATE_TIMETABLE = 15, ///< Whether the vehicle should manage the timetable automatically.
};
@@ -252,7 +253,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)
@@ -290,6 +291,8 @@ public:
/** We want to 'destruct' the right class. */
virtual ~Vehicle();
uint32 GetLastLoadingStationValidCargoMask() const;
void BeginLoading();
void CancelReservation(StationID next, Station *st);
void LeaveStation();
@@ -688,9 +691,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 RecalculateOrderOccupancyAverage();

View File

@@ -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.
@@ -47,4 +49,19 @@ enum OrderWidgets {
WID_O_OCCUPANCY_TOGGLE, ///< Toggle display of occupancy measures.
};
/** 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 */

View File

@@ -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