mirror of https://github.com/OpenTTD/OpenTTD.git
Feature: Contextual actions for vehicles grouped by shared orders (#8425)
This commit is contained in:
parent
5e14a20b3b
commit
8a78fa7121
|
@ -24,8 +24,10 @@
|
|||
#include "tilehighlight_func.h"
|
||||
#include "window_gui.h"
|
||||
#include "vehiclelist.h"
|
||||
#include "vehicle_func.h"
|
||||
#include "order_backup.h"
|
||||
#include "zoom_func.h"
|
||||
#include "error.h"
|
||||
#include "depot_cmd.h"
|
||||
#include "train_cmd.h"
|
||||
#include "vehicle_cmd.h"
|
||||
|
@ -917,6 +919,49 @@ struct DepotWindow : Window {
|
|||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Clones a vehicle from a vehicle list. If this doesn't make sense (because not all vehicles in the list have the same orders), then it displays an error.
|
||||
* @return This always returns true, which indicates that the contextual action handled the mouse click.
|
||||
* Note that it's correct behaviour to always handle the click even though an error is displayed,
|
||||
* because users aren't going to expect the default action to be performed just because they overlooked that cloning doesn't make sense.
|
||||
*/
|
||||
bool OnVehicleSelect(VehicleList::const_iterator begin, VehicleList::const_iterator end) override
|
||||
{
|
||||
if (!_ctrl_pressed) {
|
||||
/* If CTRL is not pressed: If all the vehicles in this list have the same orders, then copy orders */
|
||||
if (AllEqual(begin, end, [](const Vehicle *v1, const Vehicle *v2) {
|
||||
return VehiclesHaveSameEngineList(v1, v2);
|
||||
})) {
|
||||
if (AllEqual(begin, end, [](const Vehicle *v1, const Vehicle *v2) {
|
||||
return VehiclesHaveSameOrderList(v1, v2);
|
||||
})) {
|
||||
OnVehicleSelect(*begin);
|
||||
} else {
|
||||
ShowErrorMessage(STR_ERROR_CAN_T_BUY_TRAIN + (*begin)->type, STR_ERROR_CAN_T_COPY_ORDER_VEHICLE_LIST, WL_INFO);
|
||||
}
|
||||
} else {
|
||||
ShowErrorMessage(STR_ERROR_CAN_T_BUY_TRAIN + (*begin)->type, STR_ERROR_CAN_T_CLONE_VEHICLE_LIST, WL_INFO);
|
||||
}
|
||||
} else {
|
||||
/* If CTRL is pressed: If all the vehicles in this list share orders, then copy orders */
|
||||
if (AllEqual(begin, end, [](const Vehicle *v1, const Vehicle *v2) {
|
||||
return VehiclesHaveSameEngineList(v1, v2);
|
||||
})) {
|
||||
if (AllEqual(begin, end, [](const Vehicle *v1, const Vehicle *v2) {
|
||||
return v1->FirstShared() == v2->FirstShared();
|
||||
})) {
|
||||
OnVehicleSelect(*begin);
|
||||
} else {
|
||||
ShowErrorMessage(STR_ERROR_CAN_T_BUY_TRAIN + (*begin)->type, STR_ERROR_CAN_T_SHARE_ORDER_VEHICLE_LIST, WL_INFO);
|
||||
}
|
||||
} else {
|
||||
ShowErrorMessage(STR_ERROR_CAN_T_BUY_TRAIN + (*begin)->type, STR_ERROR_CAN_T_CLONE_VEHICLE_LIST, WL_INFO);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void OnPlaceObjectAbort() override
|
||||
{
|
||||
/* abort clone */
|
||||
|
|
|
@ -884,14 +884,14 @@ public:
|
|||
}
|
||||
|
||||
case GB_SHARED_ORDERS: {
|
||||
const Vehicle *v = vehgroup.vehicles_begin[0];
|
||||
/* We do not support VehicleClicked() here since the contextual action may only make sense for individual vehicles */
|
||||
|
||||
if (vindex == v->index) {
|
||||
if (vehgroup.NumVehicles() == 1) {
|
||||
ShowVehicleViewWindow(v);
|
||||
} else {
|
||||
ShowVehicleListWindow(v);
|
||||
if (!VehicleClicked(vehgroup)) {
|
||||
const Vehicle* v = vehgroup.vehicles_begin[0];
|
||||
if (vindex == v->index) {
|
||||
if (vehgroup.NumVehicles() == 1) {
|
||||
ShowVehicleViewWindow(v);
|
||||
} else {
|
||||
ShowVehicleListWindow(v);
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
|
|
@ -5029,6 +5029,8 @@ STR_ERROR_CAN_T_CHANGE_SERVICING :{WHITE}Can't ch
|
|||
|
||||
STR_ERROR_VEHICLE_IS_DESTROYED :{WHITE}... vehicle is destroyed
|
||||
|
||||
STR_ERROR_CAN_T_CLONE_VEHICLE_LIST :{WHITE}... not all vehicles are identical
|
||||
|
||||
STR_ERROR_NO_VEHICLES_AVAILABLE_AT_ALL :{WHITE}No vehicles will be available at all
|
||||
STR_ERROR_NO_VEHICLES_AVAILABLE_AT_ALL_EXPLANATION :{WHITE}Change your NewGRF configuration
|
||||
STR_ERROR_NO_VEHICLES_AVAILABLE_YET :{WHITE}No vehicles are available yet
|
||||
|
@ -5055,6 +5057,8 @@ STR_ERROR_CAN_T_SKIP_TO_ORDER :{WHITE}Can't sk
|
|||
STR_ERROR_CAN_T_COPY_SHARE_ORDER :{WHITE}... vehicle can't go to all stations
|
||||
STR_ERROR_CAN_T_ADD_ORDER :{WHITE}... vehicle can't go to that station
|
||||
STR_ERROR_CAN_T_ADD_ORDER_SHARED :{WHITE}... a vehicle sharing this order can't go to that station
|
||||
STR_ERROR_CAN_T_COPY_ORDER_VEHICLE_LIST :{WHITE}... not all vehicles have the same orders
|
||||
STR_ERROR_CAN_T_SHARE_ORDER_VEHICLE_LIST :{WHITE}... not all vehicles are sharing orders
|
||||
|
||||
STR_ERROR_CAN_T_SHARE_ORDER_LIST :{WHITE}Can't share order list...
|
||||
STR_ERROR_CAN_T_STOP_SHARING_ORDER_LIST :{WHITE}Can't stop sharing order list...
|
||||
|
|
|
@ -29,6 +29,9 @@
|
|||
#include "aircraft.h"
|
||||
#include "engine_func.h"
|
||||
#include "vehicle_func.h"
|
||||
#include "vehiclelist.h"
|
||||
#include "vehicle_func.h"
|
||||
#include "error.h"
|
||||
#include "order_cmd.h"
|
||||
#include "company_cmd.h"
|
||||
|
||||
|
@ -1466,6 +1469,40 @@ public:
|
|||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Clones an order list from a vehicle list. If this doesn't make sense (because not all vehicles in the list have the same orders), then it displays an error.
|
||||
* @return This always returns true, which indicates that the contextual action handled the mouse click.
|
||||
* Note that it's correct behaviour to always handle the click even though an error is displayed,
|
||||
* because users aren't going to expect the default action to be performed just because they overlooked that cloning doesn't make sense.
|
||||
*/
|
||||
bool OnVehicleSelect(VehicleList::const_iterator begin, VehicleList::const_iterator end) override
|
||||
{
|
||||
bool share_order = _ctrl_pressed || this->goto_type == OPOS_SHARE;
|
||||
if (this->vehicle->GetNumOrders() != 0 && !share_order) return false;
|
||||
|
||||
if (!share_order) {
|
||||
/* If CTRL is not pressed: If all the vehicles in this list have the same orders, then copy orders */
|
||||
if (AllEqual(begin, end, [](const Vehicle *v1, const Vehicle *v2) {
|
||||
return VehiclesHaveSameOrderList(v1, v2);
|
||||
})) {
|
||||
OnVehicleSelect(*begin);
|
||||
} else {
|
||||
ShowErrorMessage(STR_ERROR_CAN_T_COPY_ORDER_LIST, STR_ERROR_CAN_T_COPY_ORDER_VEHICLE_LIST, WL_INFO);
|
||||
}
|
||||
} else {
|
||||
/* If CTRL is pressed: If all the vehicles in this list share orders, then copy orders */
|
||||
if (AllEqual(begin, end, [](const Vehicle *v1, const Vehicle *v2) {
|
||||
return v1->FirstShared() == v2->FirstShared();
|
||||
})) {
|
||||
OnVehicleSelect(*begin);
|
||||
} else {
|
||||
ShowErrorMessage(STR_ERROR_CAN_T_SHARE_ORDER_LIST, STR_ERROR_CAN_T_SHARE_ORDER_VEHICLE_LIST, WL_INFO);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void OnPlaceObjectAbort() override
|
||||
{
|
||||
this->goto_type = OPOS_NONE;
|
||||
|
|
|
@ -3013,3 +3013,39 @@ uint32 Vehicle::GetDisplayMinPowerToWeight() const
|
|||
if (max_weight == 0) return 0;
|
||||
return GetGroundVehicleCache()->cached_power * 10u / max_weight;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if two vehicle chains have the same list of engines.
|
||||
* @param v1 First vehicle chain.
|
||||
* @param v1 Second vehicle chain.
|
||||
* @return True if same, false if different.
|
||||
*/
|
||||
bool VehiclesHaveSameEngineList(const Vehicle *v1, const Vehicle *v2)
|
||||
{
|
||||
while (true) {
|
||||
if (v1 == nullptr && v2 == nullptr) return true;
|
||||
if (v1 == nullptr || v2 == nullptr) return false;
|
||||
if (v1->GetEngine() != v2->GetEngine()) return false;
|
||||
v1 = v1->GetNextVehicle();
|
||||
v2 = v2->GetNextVehicle();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if two vehicles have the same list of orders.
|
||||
* @param v1 First vehicles.
|
||||
* @param v1 Second vehicles.
|
||||
* @return True if same, false if different.
|
||||
*/
|
||||
bool VehiclesHaveSameOrderList(const Vehicle *v1, const Vehicle *v2)
|
||||
{
|
||||
const Order *o1 = v1->GetFirstOrder();
|
||||
const Order *o2 = v2->GetFirstOrder();
|
||||
while (true) {
|
||||
if (o1 == nullptr && o2 == nullptr) return true;
|
||||
if (o1 == nullptr || o2 == nullptr) return false;
|
||||
if (!o1->Equals(*o2)) return false;
|
||||
o1 = o1->next;
|
||||
o2 = o2->next;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -174,4 +174,7 @@ void GetVehicleSet(VehicleSet &set, Vehicle *v, uint8 num_vehicles);
|
|||
|
||||
void CheckCargoCapacity(Vehicle *v);
|
||||
|
||||
bool VehiclesHaveSameEngineList(const Vehicle *v1, const Vehicle *v2);
|
||||
bool VehiclesHaveSameOrderList(const Vehicle *v1, const Vehicle *v2);
|
||||
|
||||
#endif /* VEHICLE_FUNC_H */
|
||||
|
|
|
@ -2018,16 +2018,16 @@ public:
|
|||
|
||||
case GB_SHARED_ORDERS: {
|
||||
assert(vehgroup.NumVehicles() > 0);
|
||||
const Vehicle *v = vehgroup.vehicles_begin[0];
|
||||
/* We do not support VehicleClicked() here since the contextual action may only make sense for individual vehicles */
|
||||
|
||||
if (_ctrl_pressed) {
|
||||
ShowOrdersWindow(v);
|
||||
} else {
|
||||
if (vehgroup.NumVehicles() == 1) {
|
||||
ShowVehicleViewWindow(v);
|
||||
if (!VehicleClicked(vehgroup)) {
|
||||
const Vehicle *v = vehgroup.vehicles_begin[0];
|
||||
if (_ctrl_pressed) {
|
||||
ShowOrdersWindow(v);
|
||||
} else {
|
||||
ShowVehicleListWindow(v);
|
||||
if (vehgroup.NumVehicles() == 1) {
|
||||
ShowVehicleViewWindow(v);
|
||||
} else {
|
||||
ShowVehicleListWindow(v);
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
@ -3288,6 +3288,33 @@ bool VehicleClicked(const Vehicle *v)
|
|||
return _thd.GetCallbackWnd()->OnVehicleSelect(v);
|
||||
}
|
||||
|
||||
/**
|
||||
* Dispatch a "vehicle group selected" event if any window waits for it.
|
||||
* @param begin iterator to the start of the range of vehicles
|
||||
* @param end iterator to the end of the range of vehicles
|
||||
* @return did any window accept vehicle group selection?
|
||||
*/
|
||||
bool VehicleClicked(VehicleList::const_iterator begin, VehicleList::const_iterator end)
|
||||
{
|
||||
assert(begin != end);
|
||||
if (!(_thd.place_mode & HT_VEHICLE)) return false;
|
||||
|
||||
/* If there is only one vehicle in the group, act as if we clicked a single vehicle */
|
||||
if (begin + 1 == end) return _thd.GetCallbackWnd()->OnVehicleSelect(*begin);
|
||||
|
||||
return _thd.GetCallbackWnd()->OnVehicleSelect(begin, end);
|
||||
}
|
||||
|
||||
/**
|
||||
* Dispatch a "vehicle group selected" event if any window waits for it.
|
||||
* @param vehgroup the GUIVehicleGroup representing the vehicle group
|
||||
* @return did any window accept vehicle group selection?
|
||||
*/
|
||||
bool VehicleClicked(const GUIVehicleGroup &vehgroup)
|
||||
{
|
||||
return VehicleClicked(vehgroup.vehicles_begin, vehgroup.vehicles_end);
|
||||
}
|
||||
|
||||
void StopGlobalFollowVehicle(const Vehicle *v)
|
||||
{
|
||||
Window *w = FindWindowById(WC_MAIN_WINDOW, 0);
|
||||
|
|
|
@ -12,6 +12,8 @@
|
|||
|
||||
#include "window_type.h"
|
||||
#include "vehicle_type.h"
|
||||
#include "vehicle_gui_base.h"
|
||||
#include "vehiclelist.h"
|
||||
#include "order_type.h"
|
||||
#include "station_type.h"
|
||||
#include "engine_type.h"
|
||||
|
@ -102,6 +104,8 @@ static inline WindowClass GetWindowClassForVehicleType(VehicleType vt)
|
|||
/* Unified window procedure */
|
||||
void ShowVehicleViewWindow(const Vehicle *v);
|
||||
bool VehicleClicked(const Vehicle *v);
|
||||
bool VehicleClicked(VehicleList::const_iterator begin, VehicleList::const_iterator end);
|
||||
bool VehicleClicked(const GUIVehicleGroup &vehgroup);
|
||||
void StartStopVehicle(const Vehicle *v, bool texteffect);
|
||||
|
||||
Vehicle *CheckClickOnVehicle(const struct Viewport *vp, int x, int y);
|
||||
|
|
|
@ -11,7 +11,10 @@
|
|||
#define WINDOW_GUI_H
|
||||
|
||||
#include <list>
|
||||
#include <algorithm>
|
||||
#include <functional>
|
||||
|
||||
#include "vehiclelist.h"
|
||||
#include "vehicle_type.h"
|
||||
#include "viewport_type.h"
|
||||
#include "company_type.h"
|
||||
|
@ -681,11 +684,20 @@ public:
|
|||
|
||||
/**
|
||||
* The user clicked on a vehicle while HT_VEHICLE has been set.
|
||||
* @param v clicked vehicle. It is guaranteed to be v->IsPrimaryVehicle() == true
|
||||
* @return True if the click is handled, false if it is ignored.
|
||||
* @param v clicked vehicle
|
||||
* @return true if the click is handled, false if it is ignored
|
||||
* @pre v->IsPrimaryVehicle() == true
|
||||
*/
|
||||
virtual bool OnVehicleSelect(const struct Vehicle *v) { return false; }
|
||||
|
||||
/**
|
||||
* The user clicked on a vehicle while HT_VEHICLE has been set.
|
||||
* @param v clicked vehicle
|
||||
* @return True if the click is handled, false if it is ignored
|
||||
* @pre v->IsPrimaryVehicle() == true
|
||||
*/
|
||||
virtual bool OnVehicleSelect(VehicleList::const_iterator begin, VehicleList::const_iterator end) { return false; }
|
||||
|
||||
/**
|
||||
* The user cancelled a tile highlight mode that has been set.
|
||||
*/
|
||||
|
@ -806,6 +818,19 @@ public:
|
|||
using IterateFromFront = AllWindows<true>; //!< Iterate all windows in Z order from front to back.
|
||||
};
|
||||
|
||||
/**
|
||||
* Generic helper function that checks if all elements of the range are equal with respect to the given predicate.
|
||||
* @param begin The start of the range.
|
||||
* @param end The end of the range.
|
||||
* @param pred The predicate to use.
|
||||
* @return True if all elements are equal, false otherwise.
|
||||
*/
|
||||
template <class It, class Pred>
|
||||
inline bool AllEqual(It begin, It end, Pred pred)
|
||||
{
|
||||
return std::adjacent_find(begin, end, std::not_fn(pred)) == end;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the nested widget with number \a widnum from the nested widget tree.
|
||||
* @tparam NWID Type of the nested widget.
|
||||
|
|
Loading…
Reference in New Issue