|
@@ -668,97 +668,97 @@ static void DeleteOrderWarnings(const Ve
|
|
|
DeleteVehicleNews(v->index, STR_NEWS_VEHICLE_HAS_VOID_ORDER);
|
|
|
DeleteVehicleNews(v->index, STR_NEWS_VEHICLE_HAS_DUPLICATE_ENTRY);
|
|
|
DeleteVehicleNews(v->index, STR_NEWS_VEHICLE_HAS_INVALID_ENTRY);
|
|
|
DeleteVehicleNews(v->index, STR_NEWS_PLANE_USES_TOO_SHORT_RUNWAY);
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
* Returns a tile somewhat representing the order destination (not suitable for pathfinding).
|
|
|
* @param v The vehicle to get the location for.
|
|
|
* @param airport Get the airport tile and not the station location for aircraft.
|
|
|
* @return destination of order, or INVALID_TILE if none.
|
|
|
*/
|
|
|
TileIndex Order::GetLocation(const Vehicle *v, bool airport) const
|
|
|
{
|
|
|
switch (this->GetType()) {
|
|
|
case OT_GOTO_WAYPOINT:
|
|
|
case OT_GOTO_STATION:
|
|
|
case OT_IMPLICIT:
|
|
|
if (airport && v->type == VEH_AIRCRAFT) return Station::Get(this->GetDestination())->airport.tile;
|
|
|
return BaseStation::Get(this->GetDestination())->xy;
|
|
|
|
|
|
case OT_GOTO_DEPOT:
|
|
|
if ((this->GetDepotActionType() & ODATFB_NEAREST_DEPOT) != 0) return INVALID_TILE;
|
|
|
return (v->type == VEH_AIRCRAFT) ? Station::Get(this->GetDestination())->xy : Depot::Get(this->GetDestination())->xy;
|
|
|
|
|
|
default:
|
|
|
return INVALID_TILE;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
* Get the distance between two orders of a vehicle. Conditional orders are resolved
|
|
|
* and the bigger distance of the two order branches is returned.
|
|
|
* @param prev Origin order.
|
|
|
* @param cur Destination order.
|
|
|
* @param v The vehicle to get the distance for.
|
|
|
* @param conditional_depth Internal param for resolving conditional orders.
|
|
|
* @return Maximum distance between the two orders.
|
|
|
*/
|
|
|
uint GetOrderDistance(const Order *prev, const Order *cur, const Vehicle *v, int conditional_depth)
|
|
|
{
|
|
|
if (cur->IsType(OT_CONDITIONAL)) {
|
|
|
if (conditional_depth > v->GetNumOrders()) return 0;
|
|
|
|
|
|
conditional_depth++;
|
|
|
|
|
|
int dist1 = GetOrderDistance(prev, v->GetOrder(cur->GetConditionSkipToOrder()), v, conditional_depth);
|
|
|
int dist2 = GetOrderDistance(prev, cur->next == nullptr ? v->orders.list->GetFirstOrder() : cur->next, v, conditional_depth);
|
|
|
return max(dist1, dist2);
|
|
|
return std::max(dist1, dist2);
|
|
|
}
|
|
|
|
|
|
TileIndex prev_tile = prev->GetLocation(v, true);
|
|
|
TileIndex cur_tile = cur->GetLocation(v, true);
|
|
|
if (prev_tile == INVALID_TILE || cur_tile == INVALID_TILE) return 0;
|
|
|
return v->type == VEH_AIRCRAFT ? DistanceSquare(prev_tile, cur_tile) : DistanceManhattan(prev_tile, cur_tile);
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
* Add an order to the orderlist of a vehicle.
|
|
|
* @param tile unused
|
|
|
* @param flags operation to perform
|
|
|
* @param p1 various bitstuffed elements
|
|
|
* - p1 = (bit 0 - 19) - ID of the vehicle
|
|
|
* - p1 = (bit 24 - 31) - the selected order (if any). If the last order is given,
|
|
|
* the order will be inserted before that one
|
|
|
* the maximum vehicle order id is 254.
|
|
|
* @param p2 packed order to insert
|
|
|
* @param text unused
|
|
|
* @return the cost of this operation or an error
|
|
|
*/
|
|
|
CommandCost CmdInsertOrder(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text)
|
|
|
{
|
|
|
VehicleID veh = GB(p1, 0, 20);
|
|
|
VehicleOrderID sel_ord = GB(p1, 20, 8);
|
|
|
Order new_order(p2);
|
|
|
|
|
|
Vehicle *v = Vehicle::GetIfValid(veh);
|
|
|
if (v == nullptr || !v->IsPrimaryVehicle()) return CMD_ERROR;
|
|
|
|
|
|
CommandCost ret = CheckOwnership(v->owner);
|
|
|
if (ret.Failed()) return ret;
|
|
|
|
|
|
/* Check if the inserted order is to the correct destination (owner, type),
|
|
|
* and has the correct flags if any */
|
|
|
switch (new_order.GetType()) {
|
|
|
case OT_GOTO_STATION: {
|
|
|
const Station *st = Station::GetIfValid(new_order.GetDestination());
|
|
|
if (st == nullptr) return CMD_ERROR;
|
|
|
|
|
|
if (st->owner != OWNER_NONE) {
|
|
|
CommandCost ret = CheckOwnership(st->owner);
|
|
|
if (ret.Failed()) return ret;
|
|
|
}
|
|
|
|
|
|
if (!CanVehicleUseStation(v, st)) return_cmd_error(STR_ERROR_CAN_T_ADD_ORDER);
|
|
|
for (Vehicle *u = v->FirstShared(); u != nullptr; u = u->NextShared()) {
|
|
|
if (!CanVehicleUseStation(u, st)) return_cmd_error(STR_ERROR_CAN_T_ADD_ORDER_SHARED);
|
|
@@ -1048,97 +1048,97 @@ static void CancelLoadingDueToDeletedOrd
|
|
|
|
|
|
/**
|
|
|
* Delete an order but skip the parameter validation.
|
|
|
* @param v The vehicle to delete the order from.
|
|
|
* @param sel_ord The id of the order to be deleted.
|
|
|
*/
|
|
|
void DeleteOrder(Vehicle *v, VehicleOrderID sel_ord)
|
|
|
{
|
|
|
v->orders.list->DeleteOrderAt(sel_ord);
|
|
|
|
|
|
Vehicle *u = v->FirstShared();
|
|
|
DeleteOrderWarnings(u);
|
|
|
for (; u != nullptr; u = u->NextShared()) {
|
|
|
assert(v->orders.list == u->orders.list);
|
|
|
|
|
|
if (sel_ord == u->cur_real_order_index && u->current_order.IsType(OT_LOADING)) {
|
|
|
CancelLoadingDueToDeletedOrder(u);
|
|
|
}
|
|
|
|
|
|
if (sel_ord < u->cur_real_order_index) {
|
|
|
u->cur_real_order_index--;
|
|
|
} else if (sel_ord == u->cur_real_order_index) {
|
|
|
u->UpdateRealOrderIndex();
|
|
|
}
|
|
|
|
|
|
if (sel_ord < u->cur_implicit_order_index) {
|
|
|
u->cur_implicit_order_index--;
|
|
|
} else if (sel_ord == u->cur_implicit_order_index) {
|
|
|
/* Make sure the index is valid */
|
|
|
if (u->cur_implicit_order_index >= u->GetNumOrders()) u->cur_implicit_order_index = 0;
|
|
|
|
|
|
/* Skip non-implicit orders for the implicit-order-index (e.g. if the current implicit order was deleted */
|
|
|
while (u->cur_implicit_order_index != u->cur_real_order_index && !u->GetOrder(u->cur_implicit_order_index)->IsType(OT_IMPLICIT)) {
|
|
|
u->cur_implicit_order_index++;
|
|
|
if (u->cur_implicit_order_index >= u->GetNumOrders()) u->cur_implicit_order_index = 0;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
/* Update any possible open window of the vehicle */
|
|
|
InvalidateVehicleOrder(u, sel_ord | (INVALID_VEH_ORDER_ID << 8));
|
|
|
}
|
|
|
|
|
|
/* As we delete an order, the order to skip to will be 'wrong'. */
|
|
|
VehicleOrderID cur_order_id = 0;
|
|
|
for (Order *order : v->Orders()) {
|
|
|
if (order->IsType(OT_CONDITIONAL)) {
|
|
|
VehicleOrderID order_id = order->GetConditionSkipToOrder();
|
|
|
if (order_id >= sel_ord) {
|
|
|
order_id = max(order_id - 1, 0);
|
|
|
order_id = std::max(order_id - 1, 0);
|
|
|
}
|
|
|
if (order_id == cur_order_id) {
|
|
|
order_id = (order_id + 1) % v->GetNumOrders();
|
|
|
}
|
|
|
order->SetConditionSkipToOrder(order_id);
|
|
|
}
|
|
|
cur_order_id++;
|
|
|
}
|
|
|
|
|
|
InvalidateWindowClassesData(GetWindowClassForVehicleType(v->type), 0);
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
* Goto order of order-list.
|
|
|
* @param tile unused
|
|
|
* @param flags operation to perform
|
|
|
* @param p1 The ID of the vehicle which order is skipped
|
|
|
* @param p2 the selected order to which we want to skip
|
|
|
* @param text unused
|
|
|
* @return the cost of this operation or an error
|
|
|
*/
|
|
|
CommandCost CmdSkipToOrder(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text)
|
|
|
{
|
|
|
VehicleID veh_id = GB(p1, 0, 20);
|
|
|
VehicleOrderID sel_ord = GB(p2, 0, 8);
|
|
|
|
|
|
Vehicle *v = Vehicle::GetIfValid(veh_id);
|
|
|
|
|
|
if (v == nullptr || !v->IsPrimaryVehicle() || sel_ord == v->cur_implicit_order_index || sel_ord >= v->GetNumOrders() || v->GetNumOrders() < 2) return CMD_ERROR;
|
|
|
|
|
|
CommandCost ret = CheckOwnership(v->owner);
|
|
|
if (ret.Failed()) return ret;
|
|
|
|
|
|
if (flags & DC_EXEC) {
|
|
|
if (v->current_order.IsType(OT_LOADING)) v->LeaveStation();
|
|
|
|
|
|
v->cur_implicit_order_index = v->cur_real_order_index = sel_ord;
|
|
|
v->UpdateRealOrderIndex();
|
|
|
|
|
|
InvalidateVehicleOrder(v, VIWD_MODIFY_ORDERS);
|
|
|
}
|
|
|
|
|
|
/* We have an aircraft/ship, they have a mini-schedule, so update them all */
|
|
|
if (v->type == VEH_AIRCRAFT) SetWindowClassesDirty(WC_AIRCRAFT_LIST);
|
|
|
if (v->type == VEH_SHIP) SetWindowClassesDirty(WC_SHIPS_LIST);
|
|
|
|
|
|
return CommandCost();
|
|
|
}
|
|
@@ -1940,97 +1940,97 @@ static bool CheckForValidOrders(const Ve
|
|
|
default:
|
|
|
break;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
return false;
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
* Compare the variable and value based on the given comparator.
|
|
|
*/
|
|
|
static bool OrderConditionCompare(OrderConditionComparator occ, int variable, int value)
|
|
|
{
|
|
|
switch (occ) {
|
|
|
case OCC_EQUALS: return variable == value;
|
|
|
case OCC_NOT_EQUALS: return variable != value;
|
|
|
case OCC_LESS_THAN: return variable < value;
|
|
|
case OCC_LESS_EQUALS: return variable <= value;
|
|
|
case OCC_MORE_THAN: return variable > value;
|
|
|
case OCC_MORE_EQUALS: return variable >= value;
|
|
|
case OCC_IS_TRUE: return variable != 0;
|
|
|
case OCC_IS_FALSE: return variable == 0;
|
|
|
default: NOT_REACHED();
|
|
|
}
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
* Process a conditional order and determine the next order.
|
|
|
* @param order the order the vehicle currently has
|
|
|
* @param v the vehicle to update
|
|
|
* @return index of next order to jump to, or INVALID_VEH_ORDER_ID to use the next order
|
|
|
*/
|
|
|
VehicleOrderID ProcessConditionalOrder(const Order *order, const Vehicle *v)
|
|
|
{
|
|
|
if (order->GetType() != OT_CONDITIONAL) return INVALID_VEH_ORDER_ID;
|
|
|
|
|
|
bool skip_order = false;
|
|
|
OrderConditionComparator occ = order->GetConditionComparator();
|
|
|
uint16 value = order->GetConditionValue();
|
|
|
|
|
|
switch (order->GetConditionVariable()) {
|
|
|
case OCV_LOAD_PERCENTAGE: skip_order = OrderConditionCompare(occ, CalcPercentVehicleFilled(v, nullptr), value); break;
|
|
|
case OCV_RELIABILITY: skip_order = OrderConditionCompare(occ, ToPercent16(v->reliability), value); break;
|
|
|
case OCV_MAX_RELIABILITY: skip_order = OrderConditionCompare(occ, ToPercent16(v->GetEngine()->reliability), value); break;
|
|
|
case OCV_MAX_SPEED: skip_order = OrderConditionCompare(occ, v->GetDisplayMaxSpeed() * 10 / 16, value); break;
|
|
|
case OCV_AGE: skip_order = OrderConditionCompare(occ, v->age / DAYS_IN_LEAP_YEAR, value); break;
|
|
|
case OCV_REQUIRES_SERVICE: skip_order = OrderConditionCompare(occ, v->NeedsServicing(), value); break;
|
|
|
case OCV_UNCONDITIONALLY: skip_order = true; break;
|
|
|
case OCV_REMAINING_LIFETIME: skip_order = OrderConditionCompare(occ, max(v->max_age - v->age + DAYS_IN_LEAP_YEAR - 1, 0) / DAYS_IN_LEAP_YEAR, value); break;
|
|
|
case OCV_REMAINING_LIFETIME: skip_order = OrderConditionCompare(occ, std::max(v->max_age - v->age + DAYS_IN_LEAP_YEAR - 1, 0) / DAYS_IN_LEAP_YEAR, value); break;
|
|
|
default: NOT_REACHED();
|
|
|
}
|
|
|
|
|
|
return skip_order ? order->GetConditionSkipToOrder() : (VehicleOrderID)INVALID_VEH_ORDER_ID;
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
* Update the vehicle's destination tile from an order.
|
|
|
* @param order the order the vehicle currently has
|
|
|
* @param v the vehicle to update
|
|
|
* @param conditional_depth the depth (amount of steps) to go with conditional orders. This to prevent infinite loops.
|
|
|
* @param pbs_look_ahead Whether we are forecasting orders for pbs reservations in advance. If true, the order indices must not be modified.
|
|
|
*/
|
|
|
bool UpdateOrderDest(Vehicle *v, const Order *order, int conditional_depth, bool pbs_look_ahead)
|
|
|
{
|
|
|
if (conditional_depth > v->GetNumOrders()) {
|
|
|
v->current_order.Free();
|
|
|
v->SetDestTile(0);
|
|
|
return false;
|
|
|
}
|
|
|
|
|
|
switch (order->GetType()) {
|
|
|
case OT_GOTO_STATION:
|
|
|
v->SetDestTile(v->GetOrderStationLocation(order->GetDestination()));
|
|
|
return true;
|
|
|
|
|
|
case OT_GOTO_DEPOT:
|
|
|
if ((order->GetDepotOrderType() & ODTFB_SERVICE) && !v->NeedsServicing()) {
|
|
|
assert(!pbs_look_ahead);
|
|
|
UpdateVehicleTimetable(v, true);
|
|
|
v->IncrementRealOrderIndex();
|
|
|
break;
|
|
|
}
|
|
|
|
|
|
if (v->current_order.GetDepotActionType() & ODATFB_NEAREST_DEPOT) {
|
|
|
/* We need to search for the nearest depot (hangar). */
|
|
|
TileIndex location;
|
|
|
DestinationID destination;
|
|
|
bool reverse;
|
|
|
|
|
|
if (v->FindClosestDepot(&location, &destination, &reverse)) {
|
|
|
/* PBS reservations cannot reverse */
|
|
|
if (pbs_look_ahead && reverse) return false;
|
|
|
|
|
|
v->SetDestTile(location);
|
|
|
v->current_order.MakeGoToDepot(destination, v->current_order.GetDepotOrderType(), v->current_order.GetNonStopType(), (OrderDepotActionFlags)(v->current_order.GetDepotActionType() & ~ODATFB_NEAREST_DEPOT), v->current_order.GetRefitCargo());
|
|
|
|
|
|
/* If there is no depot in front, reverse automatically (trains only) */
|