tron@2186: /* $Id$ */ tron@2186: rubidium@9111: /** @file order_cmd.cpp Handling of orders. */ belugas@6351: truelight@0: #include "stdafx.h" Darkvater@1891: #include "openttd.h" rubidium@8784: #include "order_base.h" rubidium@8784: #include "order_func.h" tron@1718: #include "airport.h" rubidium@8962: #include "order_base.h" tron@1718: #include "waypoint.h" rubidium@8116: #include "command_func.h" rubidium@8254: #include "player_func.h" rubidium@8763: #include "news_func.h" truelight@1024: #include "saveload.h" matthijs@1752: #include "vehicle_gui.h" peter1138@6329: #include "cargotype.h" glx@7683: #include "aircraft.h" rubidium@8114: #include "strings_func.h" rubidium@8130: #include "core/alloc_func.hpp" rubidium@8131: #include "functions.h" rubidium@8131: #include "window_func.h" rubidium@8211: #include "settings_type.h" rubidium@8214: #include "string_func.h" rubidium@8787: #include "newgrf_cargo.h" rubidium@8827: #include "timetable.h" rubidium@8827: #include "vehicle_func.h" smatz@8847: #include "oldpool_func.h" rubidium@8962: #include "depot_base.h" truelight@0: rubidium@8264: #include "table/strings.h" rubidium@8264: rubidium@8767: /* DestinationID must be at least as large as every these below, because it can rubidium@8767: * be any of them rubidium@8767: */ rubidium@8767: assert_compile(sizeof(DestinationID) >= sizeof(DepotID)); rubidium@8767: assert_compile(sizeof(DestinationID) >= sizeof(WaypointID)); rubidium@8767: assert_compile(sizeof(DestinationID) >= sizeof(StationID)); rubidium@8767: rubidium@8268: TileIndex _backup_orders_tile; rubidium@8268: BackuppedOrders _backup_orders_data; rubidium@8268: rubidium@8848: DEFINE_OLD_POOL_GENERIC(Order, Order); rubidium@8848: rubidium@8834: void Order::Free() rubidium@8834: { rubidium@8834: this->type = OT_NOTHING; rubidium@8834: this->flags = 0; rubidium@8834: this->dest = 0; rubidium@8834: this->next = NULL; rubidium@8834: } rubidium@8834: rubidium@8836: void Order::MakeGoToStation(StationID destination) rubidium@8836: { rubidium@8836: this->type = OT_GOTO_STATION; rubidium@8836: this->flags = 0; rubidium@8836: this->dest = destination; rubidium@8836: } rubidium@8836: rubidium@8853: void Order::MakeGoToDepot(DepotID destination, OrderDepotTypeFlags order, CargoID cargo, byte subtype) rubidium@8836: { rubidium@8836: this->type = OT_GOTO_DEPOT; rubidium@8848: this->flags = 0; rubidium@8853: this->SetDepotOrderType(order); rubidium@8853: if (!(order & ODTFB_PART_OF_ORDERS)) { rubidium@8848: this->SetNonStopType(ONSF_NO_STOP_AT_INTERMEDIATE_STATIONS); rubidium@8848: } rubidium@8836: this->dest = destination; rubidium@8838: this->SetRefit(cargo, subtype); rubidium@8836: } rubidium@8836: rubidium@8836: void Order::MakeGoToWaypoint(WaypointID destination) rubidium@8836: { rubidium@8836: this->type = OT_GOTO_WAYPOINT; rubidium@8836: this->flags = 0; rubidium@8836: this->dest = destination; rubidium@8836: } rubidium@8836: rubidium@8843: void Order::MakeLoading(bool ordered) rubidium@8836: { rubidium@8836: this->type = OT_LOADING; rubidium@8843: if (!ordered) this->flags = 0; rubidium@8836: } rubidium@8836: rubidium@8836: void Order::MakeLeaveStation() rubidium@8836: { rubidium@8836: this->type = OT_LEAVESTATION; rubidium@8836: this->flags = 0; rubidium@8836: } rubidium@8836: rubidium@8836: void Order::MakeDummy() rubidium@8836: { rubidium@8836: this->type = OT_DUMMY; rubidium@8836: this->flags = 0; rubidium@8836: } rubidium@8836: rubidium@8900: void Order::MakeConditional(VehicleOrderID order) rubidium@8900: { rubidium@8900: this->type = OT_CONDITIONAL; rubidium@8901: this->flags = order; rubidium@8901: this->dest = 0; rubidium@8900: } rubidium@8900: rubidium@8838: void Order::SetRefit(CargoID cargo, byte subtype) rubidium@8838: { rubidium@8838: this->refit_cargo = cargo; rubidium@8838: this->refit_subtype = subtype; rubidium@8838: } rubidium@8838: rubidium@8834: void Order::FreeChain() rubidium@8834: { rubidium@8834: if (next != NULL) next->FreeChain(); rubidium@8834: delete this; rubidium@8834: } rubidium@8834: rubidium@8835: bool Order::Equals(const Order &other) const rubidium@8835: { rubidium@8835: return rubidium@8835: this->type == other.type && rubidium@8835: this->flags == other.flags && rubidium@8835: this->dest == other.dest; rubidium@8835: } rubidium@8835: rubidium@8834: static bool HasOrderPoolFree(uint amount) rubidium@8834: { rubidium@8834: const Order *order; rubidium@8834: rubidium@8834: /* There is always room if not all blocks in the pool are reserved */ rubidium@8834: if (_Order_pool.CanAllocateMoreBlocks()) return true; rubidium@8834: rubidium@8834: FOR_ALL_ORDERS(order) if (!order->IsValid() && --amount == 0) return true; rubidium@8834: rubidium@8834: return false; rubidium@8834: } rubidium@8834: rubidium@8839: uint32 Order::Pack() const rubidium@8834: { rubidium@8839: return this->dest << 16 | this->flags << 8 | this->type; rubidium@8834: } rubidium@8834: rubidium@8839: Order::Order(uint32 packed) rubidium@8834: { rubidium@8839: this->type = (OrderType)GB(packed, 0, 8); rubidium@8839: this->flags = GB(packed, 8, 8); rubidium@8839: this->dest = GB(packed, 16, 16); rubidium@8839: this->next = NULL; rubidium@8839: this->refit_cargo = CT_NO_REFIT; rubidium@8839: this->refit_subtype = 0; rubidium@8839: this->wait_time = 0; rubidium@8839: this->travel_time = 0; rubidium@8839: } rubidium@8839: rubidium@8881: void Order::ConvertFromOldSavegame() rubidium@8881: { rubidium@8881: /* First handle non-stop, because those bits are going to be reused. */ rubidium@9354: if (_settings.gui.sg_new_nonstop) { rubidium@8881: this->SetNonStopType((this->flags & 0x08) ? ONSF_NO_STOP_AT_ANY_STATION : ONSF_NO_STOP_AT_INTERMEDIATE_STATIONS); rubidium@8881: } else { rubidium@8881: this->SetNonStopType((this->flags & 0x08) ? ONSF_NO_STOP_AT_INTERMEDIATE_STATIONS : ONSF_STOP_EVERYWHERE); rubidium@8881: } rubidium@8881: rubidium@8881: switch (this->GetType()) { rubidium@8881: /* Only a few types need the other savegame conversions. */ rubidium@8881: case OT_GOTO_DEPOT: case OT_GOTO_STATION: case OT_LOADING: break; rubidium@8881: default: return; rubidium@8881: } rubidium@8881: rubidium@8881: /* Then the load/depot action flags because those bits are going to be reused too rubidium@8881: * and they reuse the non-stop bits. */ rubidium@8881: if (this->GetType() != OT_GOTO_DEPOT) { smatz@9355: if ((this->flags & 2) != 0) { smatz@9355: this->SetLoadType(OLFB_NO_LOAD); smatz@9355: } else if ((this->flags & 4) == 0) { rubidium@8881: this->SetLoadType(OLF_LOAD_IF_POSSIBLE); rubidium@8881: } else { rubidium@9354: this->SetLoadType(_settings.gui.sg_full_load_any ? OLF_FULL_LOAD_ANY : OLFB_FULL_LOAD); rubidium@8881: } rubidium@8881: } else { rubidium@8881: this->SetDepotActionType(((this->flags & 6) == 4) ? ODATFB_HALT : ODATF_SERVICE_ONLY); rubidium@8881: } rubidium@8881: rubidium@8881: /* Finally fix the unload/depot type flags. */ rubidium@8881: if (this->GetType() != OT_GOTO_DEPOT) { rubidium@8881: uint t = ((this->flags & 1) == 0) ? OUF_UNLOAD_IF_POSSIBLE : OUFB_TRANSFER; rubidium@8881: if ((this->flags & 2) != 0) t |= OUFB_UNLOAD; rubidium@8881: this->SetUnloadType((OrderUnloadFlags)t); rubidium@8885: rubidium@8885: if ((this->GetUnloadType() & (OUFB_UNLOAD | OUFB_TRANSFER)) == (OUFB_UNLOAD | OUFB_TRANSFER)) { rubidium@8885: this->SetUnloadType(OUFB_TRANSFER); rubidium@8885: this->SetLoadType(OLFB_NO_LOAD); rubidium@8885: } rubidium@8881: } else { rubidium@8881: uint t = ((this->flags & 6) == 6) ? ODTFB_SERVICE : ODTF_MANUAL; rubidium@8881: if ((this->flags & 2) != 0) t |= ODTFB_PART_OF_ORDERS; rubidium@8881: this->SetDepotOrderType((OrderDepotTypeFlags)t); rubidium@8881: } rubidium@8881: } rubidium@8881: rubidium@8839: /** rubidium@8839: * rubidium@8839: * Unpacks a order from savegames with version 4 and lower rubidium@8839: * rubidium@8839: */ rubidium@8839: static Order UnpackVersion4Order(uint16 packed) rubidium@8839: { rubidium@8839: return Order(GB(packed, 8, 8) << 16 | GB(packed, 4, 4) << 8 | GB(packed, 0, 4)); rubidium@8834: } rubidium@8834: truelight@1024: /** truelight@1024: * truelight@1024: * Unpacks a order from savegames made with TTD(Patch) truelight@1024: * truelight@0: */ truelight@1024: Order UnpackOldOrder(uint16 packed) truelight@0: { rubidium@8839: Order order = UnpackVersion4Order(packed); truelight@0: rubidium@8839: /* rubidium@8839: * Sanity check rubidium@8839: * TTD stores invalid orders as OT_NOTHING with non-zero flags/station rubidium@8839: */ rubidium@8926: if (!order.IsValid() && packed != 0) order.MakeDummy(); truelight@0: truelight@1024: return order; truelight@1024: } truelight@1024: truelight@1024: /** truelight@1024: * truelight@1024: * Updates the widgets of a vehicle which contains the order-data truelight@1024: * truelight@1024: */ truelight@1024: void InvalidateVehicleOrder(const Vehicle *v) truelight@1024: { maedhros@6980: InvalidateWindow(WC_VEHICLE_VIEW, v->index); maedhros@6980: InvalidateWindow(WC_VEHICLE_ORDERS, v->index); maedhros@6980: InvalidateWindow(WC_VEHICLE_TIMETABLE, v->index); truelight@1024: } truelight@1024: truelight@1024: /** truelight@1024: * truelight@1024: * Swap two orders truelight@1024: * truelight@1024: */ truelight@1024: static void SwapOrders(Order *order1, Order *order2) truelight@1024: { truelight@1024: Order temp_order; truelight@1024: truelight@1024: temp_order = *order1; rubidium@8835: order1->AssignOrder(*order2); truelight@1043: order1->next = order2->next; rubidium@8835: order2->AssignOrder(temp_order); truelight@1043: order2->next = temp_order.next; truelight@1024: } truelight@1024: truelight@1024: /** truelight@1024: * truelight@1024: * Assign data to an order (from an other order) truelight@1024: * This function makes sure that the index is maintained correctly truelight@1024: * truelight@1024: */ rubidium@8835: void Order::AssignOrder(const Order &other) truelight@1024: { rubidium@8835: this->type = other.type; rubidium@8835: this->flags = other.flags; rubidium@8835: this->dest = other.dest; bjarni@4712: rubidium@8835: this->refit_cargo = other.refit_cargo; rubidium@8835: this->refit_subtype = other.refit_subtype; maedhros@6980: rubidium@8835: this->wait_time = other.wait_time; rubidium@8835: this->travel_time = other.travel_time; truelight@1024: } truelight@1024: tron@3139: tron@3139: /** tron@3139: * Delete all news items regarding defective orders about a vehicle tron@3139: * This could kill still valid warnings (for example about void order when just tron@3139: * another order gets added), but assume the player will notice the problems, tron@3139: * when (s)he's changing the orders. tron@3139: */ tron@3139: static void DeleteOrderWarnings(const Vehicle* v) tron@3139: { bjarni@5955: DeleteVehicleNews(v->index, STR_TRAIN_HAS_TOO_FEW_ORDERS + v->type * 4); bjarni@5955: DeleteVehicleNews(v->index, STR_TRAIN_HAS_VOID_ORDER + v->type * 4); bjarni@5955: DeleteVehicleNews(v->index, STR_TRAIN_HAS_DUPLICATE_ENTRY + v->type * 4); bjarni@5955: DeleteVehicleNews(v->index, STR_TRAIN_HAS_INVALID_ENTRY + v->type * 4); tron@3139: } tron@3139: tron@3139: tron@6071: static TileIndex GetOrderLocation(const Order& o) tron@6071: { rubidium@8836: switch (o.GetType()) { tron@6071: default: NOT_REACHED(); rubidium@8840: case OT_GOTO_STATION: return GetStation(o.GetDestination())->xy; rubidium@8840: case OT_GOTO_DEPOT: return GetDepot(o.GetDestination())->xy; tron@6071: } tron@6071: } tron@6071: tron@6071: Darkvater@1786: /** Add an order to the orderlist of a vehicle. tron@3491: * @param tile unused belugas@6481: * @param flags operation to perform Darkvater@1786: * @param p1 various bitstuffed elements tron@2140: * - p1 = (bit 0 - 15) - ID of the vehicle Darkvater@1786: * - p1 = (bit 16 - 31) - the selected order (if any). If the last order is given, tron@2140: * the order will be inserted before that one tron@2140: * only the first 8 bits used currently (bit 16 - 23) (max 255) Darkvater@1786: * @param p2 packed order to insert truelight@1024: */ rubidium@6943: CommandCost CmdInsertOrder(TileIndex tile, uint32 flags, uint32 p1, uint32 p2) truelight@1024: { tron@1718: Vehicle *v; tron@2140: VehicleID veh = GB(p1, 0, 16); truelight@4391: VehicleOrderID sel_ord = GB(p1, 16, 16); rubidium@8839: Order new_order(p2); truelight@1024: truelight@4352: if (!IsValidVehicleID(veh)) return CMD_ERROR; truelight@4352: Darkvater@1786: v = GetVehicle(veh); truelight@4352: truelight@4352: if (!CheckOwnership(v->owner)) return CMD_ERROR; tron@1718: Darkvater@1786: /* Check if the inserted order is to the correct destination (owner, type), Darkvater@1786: * and has the correct flags if any */ rubidium@8836: switch (new_order.GetType()) { tron@1718: case OT_GOTO_STATION: { rubidium@8840: if (!IsValidStationID(new_order.GetDestination())) return CMD_ERROR; tron@1718: rubidium@8840: const Station *st = GetStation(new_order.GetDestination()); tron@1718: tron@6065: if (st->owner != OWNER_NONE && !CheckOwnership(st->owner)) { tron@1718: return CMD_ERROR; tron@1718: } tron@1718: tron@1718: switch (v->type) { rubidium@6259: case VEH_TRAIN: tron@1718: if (!(st->facilities & FACIL_TRAIN)) return CMD_ERROR; tron@1718: break; tron@1718: rubidium@6259: case VEH_ROAD: peter1138@6329: if (IsCargoInClass(v->cargo_type, CC_PASSENGERS)) { tron@1718: if (!(st->facilities & FACIL_BUS_STOP)) return CMD_ERROR; tron@1718: } else { tron@1718: if (!(st->facilities & FACIL_TRUCK_STOP)) return CMD_ERROR; tron@1718: } tron@1718: break; tron@1718: rubidium@6259: case VEH_SHIP: tron@1718: if (!(st->facilities & FACIL_DOCK)) return CMD_ERROR; tron@1718: break; tron@1718: rubidium@6259: case VEH_AIRCRAFT: glx@7683: if (!(st->facilities & FACIL_AIRPORT) || !CanAircraftUseStation(v->engine_type, st)) { glx@7683: return CMD_ERROR; glx@7683: } tron@1718: break; tron@1718: Darkvater@1786: default: return CMD_ERROR; tron@1718: } tron@1718: rubidium@8856: /* Non stop not allowed for non-trains. */ rubidium@8919: if (new_order.GetNonStopType() != ONSF_STOP_EVERYWHERE && v->type != VEH_TRAIN && v->type != VEH_ROAD) return CMD_ERROR; rubidium@8845: rubidium@8856: /* Full load and unload are mutual exclusive. */ rubidium@8856: if ((new_order.GetLoadType() & OLFB_FULL_LOAD) && (new_order.GetUnloadType() & OUFB_UNLOAD)) return CMD_ERROR; Darkvater@2433: rubidium@8856: /* Filter invalid load/unload types. */ rubidium@8873: switch (new_order.GetLoadType()) { rubidium@9090: case OLF_LOAD_IF_POSSIBLE: case OLFB_FULL_LOAD: case OLF_FULL_LOAD_ANY: case OLFB_NO_LOAD: break; rubidium@8873: default: return CMD_ERROR; rubidium@8873: } rubidium@9090: switch (new_order.GetUnloadType()) { rubidium@9090: case OUF_UNLOAD_IF_POSSIBLE: case OUFB_UNLOAD: case OUFB_TRANSFER: case OUFB_NO_UNLOAD: break; rubidium@9090: default: return CMD_ERROR; rubidium@9090: } tron@1718: break; tron@1718: } tron@1718: tron@1718: case OT_GOTO_DEPOT: { rubidium@8894: if (new_order.GetDepotActionType() != ODATFB_NEAREST_DEPOT) { rubidium@8894: if (v->type == VEH_AIRCRAFT) { rubidium@8894: if (!IsValidStationID(new_order.GetDestination())) return CMD_ERROR; tron@1718: rubidium@8894: const Station *st = GetStation(new_order.GetDestination()); rubidium@8894: rubidium@8894: if (!CheckOwnership(st->owner) || rubidium@8894: !(st->facilities & FACIL_AIRPORT) || rubidium@8894: st->Airport()->nof_depots == 0 || rubidium@8894: !CanAircraftUseStation(v->engine_type, st)) { rubidium@8894: return CMD_ERROR; rubidium@8894: } rubidium@8894: } else { rubidium@8894: if (!IsValidDepotID(new_order.GetDestination())) return CMD_ERROR; rubidium@8894: rubidium@8894: const Depot *dp = GetDepot(new_order.GetDestination()); rubidium@8894: rubidium@8894: if (!CheckOwnership(GetTileOwner(dp->xy))) return CMD_ERROR; rubidium@8894: rubidium@8894: switch (v->type) { rubidium@8894: case VEH_TRAIN: smatz@8961: if (!IsRailDepotTile(dp->xy)) return CMD_ERROR; rubidium@8894: break; rubidium@8894: rubidium@8894: case VEH_ROAD: smatz@8961: if (!IsRoadDepotTile(dp->xy)) return CMD_ERROR; rubidium@8894: break; rubidium@8894: rubidium@8894: case VEH_SHIP: smatz@8961: if (!IsShipDepotTile(dp->xy)) return CMD_ERROR; rubidium@8894: break; rubidium@8894: rubidium@8894: default: return CMD_ERROR; rubidium@8894: } tron@1718: } tron@1718: } else { rubidium@8894: if (!IsPlayerBuildableVehicleType(v)) return CMD_ERROR; tron@1718: } tron@1718: rubidium@8919: if (new_order.GetNonStopType() != ONSF_STOP_EVERYWHERE && v->type != VEH_TRAIN && v->type != VEH_ROAD) return CMD_ERROR; rubidium@8856: if (new_order.GetDepotOrderType() & ~ODTFB_PART_OF_ORDERS) return CMD_ERROR; rubidium@8894: if (new_order.GetDepotActionType() & ~ODATFB_NEAREST_DEPOT) return CMD_ERROR; tron@1718: break; tron@1718: } tron@1718: tron@1718: case OT_GOTO_WAYPOINT: { tron@1718: rubidium@6259: if (v->type != VEH_TRAIN) return CMD_ERROR; tron@1718: rubidium@8840: if (!IsValidWaypointID(new_order.GetDestination())) return CMD_ERROR; rubidium@8840: const Waypoint *wp = GetWaypoint(new_order.GetDestination()); tron@1718: tron@1718: if (!CheckOwnership(GetTileOwner(wp->xy))) return CMD_ERROR; tron@1718: Darkvater@2433: /* Order flags can be any of the following for waypoints: Darkvater@2433: * [non-stop] Darkvater@2433: * non-stop orders (if any) are only valid for trains */ rubidium@8881: if (new_order.GetNonStopType() != ONSF_STOP_EVERYWHERE && v->type != VEH_TRAIN) return CMD_ERROR; maedhros@8863: break; tron@1718: } tron@1718: rubidium@8900: case OT_CONDITIONAL: { rubidium@8900: if (!IsPlayerBuildableVehicleType(v)) return CMD_ERROR; rubidium@8900: rubidium@8900: VehicleOrderID skip_to = new_order.GetConditionSkipToOrder(); rubidium@8900: if (skip_to >= v->num_orders) return CMD_ERROR; rubidium@8900: if (new_order.GetNonStopType() != ONSF_STOP_EVERYWHERE) return CMD_ERROR; rubidium@8900: } break; rubidium@8900: Darkvater@1786: default: return CMD_ERROR; tron@1718: } tron@1718: Darkvater@1786: if (sel_ord > v->num_orders) return CMD_ERROR; truelight@1024: tron@5922: if (!HasOrderPoolFree(1)) return_cmd_error(STR_8831_NO_MORE_SPACE_FOR_ORDERS); truelight@1024: rubidium@9354: if (v->type == VEH_SHIP && IsHumanPlayer(v->owner) && _settings.pf.pathfinder_for_ships != VPF_NPF) { smatz@8551: /* Make sure the new destination is not too far away from the previous */ tron@6071: const Order *prev = NULL; tron@6071: uint n = 0; truelight@193: tron@6071: /* Find the last goto station or depot order before the insert location. tron@6071: * If the order is to be inserted at the beginning of the order list this tron@6071: * finds the last order in the list. */ tron@6071: for (const Order *o = v->orders; o != NULL; o = o->next) { rubidium@8836: if (o->IsType(OT_GOTO_STATION) || o->IsType(OT_GOTO_DEPOT)) prev = o; tron@6071: if (++n == sel_ord && prev != NULL) break; tron@6071: } tron@6071: if (prev != NULL) { tron@6071: uint dist = DistanceManhattan( tron@6071: GetOrderLocation(*prev), tron@6071: GetOrderLocation(new_order) tron@6071: ); tron@6071: if (dist >= 130) { tron@6071: return_cmd_error(STR_0210_TOO_FAR_FROM_PREVIOUS_DESTINATIO); tron@6071: } tron@6071: } truelight@0: } truelight@0: truelight@0: if (flags & DC_EXEC) { truelight@0: Vehicle *u; rubidium@7391: Order *new_o = new Order(); rubidium@8835: new_o->AssignOrder(new_order); truelight@193: truelight@1024: /* Create new order and link in list */ truelight@1024: if (v->orders == NULL) { rubidium@5587: v->orders = new_o; truelight@1024: } else { truelight@1024: /* Try to get the previous item (we are inserting above the truelight@1024: selected) */ Darkvater@1786: Order *order = GetVehicleOrder(v, sel_ord - 1); truelight@0: Darkvater@1786: if (order == NULL && GetVehicleOrder(v, sel_ord) != NULL) { truelight@1024: /* There is no previous item, so we are altering v->orders itself truelight@1024: But because the orders can be shared, we copy the info over truelight@1024: the v->orders, so we don't have to change the pointers of truelight@1024: all vehicles */ rubidium@5587: SwapOrders(v->orders, new_o); truelight@1024: /* Now update the next pointers */ rubidium@5587: v->orders->next = new_o; truelight@1024: } else if (order == NULL) { truelight@1024: /* 'sel' is a non-existing order, add him to the end */ truelight@1024: order = GetLastVehicleOrder(v); rubidium@5587: order->next = new_o; truelight@1024: } else { truelight@1024: /* Put the new order in between */ rubidium@5587: new_o->next = order->next; rubidium@5587: order->next = new_o; truelight@0: } truelight@0: } tron@588: tron@3139: u = GetFirstVehicleFromSharedList(v); tron@3139: DeleteOrderWarnings(u); tron@3139: for (; u != NULL; u = u->next_shared) { truelight@1024: /* Increase amount of orders */ truelight@1024: u->num_orders++; truelight@1024: truelight@1024: /* If the orderlist was empty, assign it */ Darkvater@1786: if (u->orders == NULL) u->orders = v->orders; truelight@1024: truelight@1024: assert(v->orders == u->orders); truelight@1024: truelight@1024: /* If there is added an order before the current one, we need truelight@1024: to update the selected order */ Darkvater@1786: if (sel_ord <= u->cur_order_index) { truelight@1024: uint cur = u->cur_order_index + 1; truelight@1024: /* Check if we don't go out of bound */ truelight@1024: if (cur < u->num_orders) truelight@1024: u->cur_order_index = cur; truelight@1024: } truelight@1024: /* Update any possible open window of the vehicle */ truelight@1024: InvalidateVehicleOrder(u); truelight@1024: } truelight@1024: rubidium@8900: /* As we insert an order, the order to skip to will be 'wrong'. */ rubidium@8900: VehicleOrderID cur_order_id = 0; rubidium@8900: Order *order; rubidium@8900: FOR_VEHICLE_ORDERS(v, order) { rubidium@8900: if (order->IsType(OT_CONDITIONAL)) { rubidium@8900: VehicleOrderID order_id = order->GetConditionSkipToOrder(); rubidium@8900: if (order_id >= sel_ord) { rubidium@8900: order->SetConditionSkipToOrder(order_id + 1); rubidium@8900: } rubidium@8900: if (order_id == cur_order_id) { rubidium@8900: order->SetConditionSkipToOrder((order_id + 1) % v->num_orders); rubidium@8900: } rubidium@8900: } rubidium@8900: cur_order_id++; rubidium@8900: } rubidium@8900: truelight@1024: /* Make sure to rebuild the whole list */ rubidium@9297: InvalidateWindowClassesData(GetWindowClassForVehicleType(v->type), 0); truelight@0: } truelight@0: rubidium@6950: return CommandCost(); truelight@0: } truelight@0: Darkvater@1786: /** Declone an order-list Darkvater@1786: * @param *dst delete the orders of this vehicle Darkvater@1786: * @param flags execution flags truelight@1024: */ rubidium@6943: static CommandCost DecloneOrder(Vehicle *dst, uint32 flags) truelight@0: { truelight@0: if (flags & DC_EXEC) { truelight@1024: DeleteVehicleOrders(dst); truelight@1024: InvalidateVehicleOrder(dst); rubidium@9297: InvalidateWindowClassesData(GetWindowClassForVehicleType(dst->type), 0); truelight@0: } rubidium@6950: return CommandCost(); truelight@0: } truelight@0: truelight@6336: /** truelight@6336: * Remove the VehicleList that shows all the vehicles with the same shared truelight@6336: * orders. truelight@6336: */ truelight@6336: static void RemoveSharedOrderVehicleList(Vehicle *v) truelight@6336: { truelight@6370: assert(v->orders != NULL); truelight@6336: WindowClass window_class = WC_NONE; truelight@6336: truelight@6336: switch (v->type) { truelight@6336: default: NOT_REACHED(); truelight@6336: case VEH_TRAIN: window_class = WC_TRAINS_LIST; break; truelight@6336: case VEH_ROAD: window_class = WC_ROADVEH_LIST; break; truelight@6336: case VEH_SHIP: window_class = WC_SHIPS_LIST; break; truelight@6336: case VEH_AIRCRAFT: window_class = WC_AIRCRAFT_LIST; break; truelight@6336: } truelight@6336: DeleteWindowById(window_class, (v->orders->index << 16) | (v->type << 11) | VLW_SHARED_ORDERS | v->owner); truelight@6336: } truelight@6336: Darkvater@1786: /** Delete an order from the orderlist of a vehicle. tron@3491: * @param tile unused belugas@6481: * @param flags operation to perform Darkvater@1786: * @param p1 the ID of the vehicle Darkvater@1786: * @param p2 the order to delete (max 255) truelight@0: */ rubidium@6943: CommandCost CmdDeleteOrder(TileIndex tile, uint32 flags, uint32 p1, uint32 p2) truelight@0: { Darkvater@1786: Vehicle *v, *u; Darkvater@1786: VehicleID veh_id = p1; truelight@4391: VehicleOrderID sel_ord = p2; truelight@1024: Order *order; truelight@0: truelight@4352: if (!IsValidVehicleID(veh_id)) return CMD_ERROR; truelight@4352: Darkvater@1786: v = GetVehicle(veh_id); truelight@4352: truelight@4352: if (!CheckOwnership(v->owner)) return CMD_ERROR; tron@1718: truelight@1024: /* If we did not select an order, we maybe want to de-clone the orders */ Darkvater@1786: if (sel_ord >= v->num_orders) truelight@0: return DecloneOrder(v, flags); truelight@0: Darkvater@1786: order = GetVehicleOrder(v, sel_ord); Darkvater@1786: if (order == NULL) return CMD_ERROR; truelight@0: truelight@1024: if (flags & DC_EXEC) { Darkvater@1786: if (GetVehicleOrder(v, sel_ord - 1) == NULL) { Darkvater@1786: if (GetVehicleOrder(v, sel_ord + 1) != NULL) { truelight@1024: /* First item, but not the last, so we need to alter v->orders truelight@1024: Because we can have shared order, we copy the data truelight@1024: from the next item over the deleted */ Darkvater@1786: order = GetVehicleOrder(v, sel_ord + 1); truelight@1024: SwapOrders(v->orders, order); truelight@1024: } else { truelight@6336: /* XXX -- The system currently can't handle a shared-order vehicle list truelight@6336: * open when there aren't any orders in the list, so close the window truelight@6336: * in this case. Of course it needs a better fix later */ truelight@6336: RemoveSharedOrderVehicleList(v); truelight@1024: /* Last item, so clean the list */ truelight@1024: v->orders = NULL; truelight@1024: } truelight@1024: } else { Darkvater@1786: GetVehicleOrder(v, sel_ord - 1)->next = order->next; truelight@1024: } truelight@0: truelight@1024: /* Give the item free */ bjarni@6263: order->Free(); truelight@0: tron@3139: u = GetFirstVehicleFromSharedList(v); tron@3139: DeleteOrderWarnings(u); tron@3139: for (; u != NULL; u = u->next_shared) { truelight@1024: u->num_orders--; truelight@1024: Darkvater@1786: if (sel_ord < u->cur_order_index) truelight@1024: u->cur_order_index--; truelight@1024: truelight@1024: /* If we removed the last order, make sure the shared vehicles Darkvater@1786: * also set their orders to NULL */ Darkvater@1786: if (v->orders == NULL) u->orders = NULL; truelight@1024: truelight@1024: assert(v->orders == u->orders); truelight@1024: truelight@1024: /* NON-stop flag is misused to see if a train is in a station that is Darkvater@1786: * on his order list or not */ rubidium@8843: if (sel_ord == u->cur_order_index && u->current_order.IsType(OT_LOADING)) { rubidium@8848: u->current_order.SetNonStopType(ONSF_STOP_EVERYWHERE); truelight@0: } truelight@1024: truelight@1024: /* Update any possible open window of the vehicle */ truelight@1024: InvalidateVehicleOrder(u); truelight@0: } tron@588: rubidium@8900: /* As we delete an order, the order to skip to will be 'wrong'. */ rubidium@8900: VehicleOrderID cur_order_id = 0; rubidium@8900: FOR_VEHICLE_ORDERS(v, order) { rubidium@8900: if (order->IsType(OT_CONDITIONAL)) { rubidium@8900: VehicleOrderID order_id = order->GetConditionSkipToOrder(); rubidium@8900: if (order_id >= sel_ord) { rubidium@8900: order->SetConditionSkipToOrder(max(order_id - 1, 0)); rubidium@8900: } rubidium@8900: if (order_id == cur_order_id) { rubidium@8900: order->SetConditionSkipToOrder((order_id + 1) % v->num_orders); rubidium@8900: } rubidium@8900: } rubidium@8900: cur_order_id++; rubidium@8900: } rubidium@8900: rubidium@9297: InvalidateWindowClassesData(GetWindowClassForVehicleType(v->type), 0); truelight@0: } truelight@193: rubidium@6950: return CommandCost(); truelight@0: } truelight@0: rubidium@6794: /** Goto order of order-list. tron@3491: * @param tile unused belugas@6481: * @param flags operation to perform Darkvater@1786: * @param p1 The ID of the vehicle which order is skipped rubidium@6794: * @param p2 the selected order to which we want to skip truelight@1024: */ rubidium@6943: CommandCost CmdSkipToOrder(TileIndex tile, uint32 flags, uint32 p1, uint32 p2) truelight@0: { tron@1718: Vehicle *v; Darkvater@1786: VehicleID veh_id = p1; rubidium@6794: VehicleOrderID sel_ord = p2; tron@1718: truelight@4352: if (!IsValidVehicleID(veh_id)) return CMD_ERROR; truelight@4352: Darkvater@1786: v = GetVehicle(veh_id); truelight@4352: rubidium@6794: if (!CheckOwnership(v->owner) || sel_ord == v->cur_order_index || rubidium@6794: sel_ord >= v->num_orders || v->num_orders < 2) return CMD_ERROR; celestar@1020: truelight@0: if (flags & DC_EXEC) { rubidium@6794: v->cur_order_index = sel_ord; truelight@193: rubidium@6259: if (v->type == VEH_ROAD) ClearSlot(v); truelight@0: rubidium@8843: if (v->current_order.IsType(OT_LOADING)) v->LeaveStation(); truelight@0: truelight@1024: InvalidateVehicleOrder(v); truelight@0: } celestar@1020: truelight@1024: /* We have an aircraft/ship, they have a mini-schedule, so update them all */ rubidium@6259: if (v->type == VEH_AIRCRAFT) InvalidateWindowClasses(WC_AIRCRAFT_LIST); rubidium@6259: if (v->type == VEH_SHIP) InvalidateWindowClasses(WC_SHIPS_LIST); celestar@1020: rubidium@6950: return CommandCost(); truelight@0: } truelight@0: rubidium@6832: /** rubidium@6832: * Move an order inside the orderlist rubidium@6832: * @param tile unused rubidium@6832: * @param p1 the ID of the vehicle rubidium@6832: * @param p2 order to move and target rubidium@6832: * bit 0-15 : the order to move rubidium@6832: * bit 16-31 : the target order rubidium@6832: * @note The target order will move one place down in the orderlist rubidium@6832: * if you move the order upwards else it'll move it one place down rubidium@6832: */ rubidium@6943: CommandCost CmdMoveOrder(TileIndex tile, uint32 flags, uint32 p1, uint32 p2) rubidium@6832: { rubidium@6832: VehicleID veh = p1; rubidium@6832: VehicleOrderID moving_order = GB(p2, 0, 16); rubidium@6832: VehicleOrderID target_order = GB(p2, 16, 16); rubidium@6832: rubidium@6832: if (!IsValidVehicleID(veh)) return CMD_ERROR; rubidium@6832: rubidium@6832: Vehicle *v = GetVehicle(veh); rubidium@6832: if (!CheckOwnership(v->owner)) return CMD_ERROR; rubidium@6832: rubidium@6832: /* Don't make senseless movements */ rubidium@6832: if (moving_order >= v->num_orders || target_order >= v->num_orders || rubidium@6832: moving_order == target_order || v->num_orders <= 1) rubidium@6832: return CMD_ERROR; rubidium@6832: rubidium@6832: Order *moving_one = GetVehicleOrder(v, moving_order); rubidium@6832: /* Don't move an empty order */ rubidium@6832: if (moving_one == NULL) return CMD_ERROR; rubidium@6832: rubidium@6832: if (flags & DC_EXEC) { rubidium@6832: /* Take the moving order out of the pointer-chain */ rubidium@6832: Order *one_before = GetVehicleOrder(v, moving_order - 1); rubidium@6832: Order *one_past = GetVehicleOrder(v, moving_order + 1); rubidium@6832: rubidium@6832: if (one_before == NULL) { rubidium@6832: /* First order edit */ rubidium@6832: v->orders = moving_one->next; rubidium@6832: } else { rubidium@6832: one_before->next = moving_one->next; rubidium@6832: } rubidium@6832: rubidium@6832: /* Insert the moving_order again in the pointer-chain */ rubidium@6832: one_before = GetVehicleOrder(v, target_order - 1); rubidium@6832: one_past = GetVehicleOrder(v, target_order); rubidium@6832: rubidium@6832: moving_one->next = one_past; rubidium@6832: rubidium@6832: if (one_before == NULL) { rubidium@6832: /* first order edit */ rubidium@6832: SwapOrders(v->orders, moving_one); rubidium@6832: v->orders->next = moving_one; rubidium@6832: } else { rubidium@6832: one_before->next = moving_one; rubidium@6832: } rubidium@6832: rubidium@6832: /* Update shared list */ rubidium@6832: Vehicle *u = GetFirstVehicleFromSharedList(v); rubidium@6832: rubidium@6832: DeleteOrderWarnings(u); rubidium@6832: rubidium@6832: for (; u != NULL; u = u->next_shared) { rubidium@6832: /* Update the first order */ rubidium@6832: if (u->orders != v->orders) u->orders = v->orders; rubidium@6832: rubidium@6832: /* Update the current order */ rubidium@6832: if (u->cur_order_index == moving_order) { rubidium@6832: u->cur_order_index = target_order; rubidium@6832: } else if(u->cur_order_index > moving_order && u->cur_order_index <= target_order) { rubidium@6832: u->cur_order_index--; rubidium@6832: } else if(u->cur_order_index < moving_order && u->cur_order_index >= target_order) { rubidium@6832: u->cur_order_index++; rubidium@6832: } rubidium@6832: rubidium@6832: assert(v->orders == u->orders); rubidium@6832: /* Update any possible open window of the vehicle */ rubidium@6832: InvalidateVehicleOrder(u); rubidium@6832: } rubidium@6832: rubidium@8900: /* As we move an order, the order to skip to will be 'wrong'. */ rubidium@8900: Order *order; rubidium@8900: FOR_VEHICLE_ORDERS(v, order) { rubidium@8900: if (order->IsType(OT_CONDITIONAL)) { rubidium@8900: VehicleOrderID order_id = order->GetConditionSkipToOrder(); rubidium@8900: if (order_id == moving_order) { rubidium@8900: order_id = target_order; rubidium@8900: } else if(order_id > moving_order && order_id <= target_order) { rubidium@8900: order_id--; rubidium@8900: } else if(order_id < moving_order && order_id >= target_order) { rubidium@8900: order_id++; rubidium@8900: } rubidium@8900: order->SetConditionSkipToOrder(order_id); rubidium@8900: } rubidium@8900: } rubidium@8900: rubidium@6832: /* Make sure to rebuild the whole list */ rubidium@9297: InvalidateWindowClassesData(GetWindowClassForVehicleType(v->type), 0); rubidium@6832: } rubidium@6832: rubidium@6950: return CommandCost(); rubidium@6832: } truelight@1024: Darkvater@1786: /** Modify an order in the orderlist of a vehicle. tron@3491: * @param tile unused belugas@6481: * @param flags operation to perform Darkvater@1786: * @param p1 various bitstuffed elements tron@2140: * - p1 = (bit 0 - 15) - ID of the vehicle Darkvater@1786: * - p1 = (bit 16 - 31) - the selected order (if any). If the last order is given, tron@2140: * the order will be inserted before that one tron@2140: * only the first 8 bits used currently (bit 16 - 23) (max 255) rubidium@8869: * @param p2 various bitstuffed elements rubidium@8900: * - p2 = (bit 0 - 3) - what data to modify (@see ModifyOrderFlags) rubidium@8900: * - p2 = (bit 4 - 15) - the data to modify truelight@0: */ rubidium@6943: CommandCost CmdModifyOrder(TileIndex tile, uint32 flags, uint32 p1, uint32 p2) truelight@0: { truelight@4391: VehicleOrderID sel_ord = GB(p1, 16, 16); // XXX - automatically truncated to 8 bits. rubidium@8869: VehicleID veh = GB(p1, 0, 16); rubidium@8900: ModifyOrderFlags mof = (ModifyOrderFlags)GB(p2, 0, 4); rubidium@8900: uint16 data = GB(p2, 4, 11); truelight@0: rubidium@8900: if (mof >= MOF_END) return CMD_ERROR; truelight@4352: if (!IsValidVehicleID(veh)) return CMD_ERROR; Darkvater@1786: rubidium@8869: Vehicle *v = GetVehicle(veh); truelight@4352: if (!CheckOwnership(v->owner)) return CMD_ERROR; tron@1718: truelight@1024: /* Is it a valid order? */ Darkvater@1786: if (sel_ord >= v->num_orders) return CMD_ERROR; truelight@0: rubidium@8869: Order *order = GetVehicleOrder(v, sel_ord); rubidium@8869: switch (order->GetType()) { rubidium@8869: case OT_GOTO_STATION: rubidium@8900: if (mof == MOF_COND_VARIABLE || mof == MOF_COND_COMPARATOR || mof == MOF_DEPOT_ACTION || mof == MOF_COND_VALUE || GetStation(order->GetDestination())->IsBuoy()) return CMD_ERROR; rubidium@8869: break; rubidium@8869: rubidium@8869: case OT_GOTO_DEPOT: rubidium@8900: if (mof != MOF_NON_STOP && mof != MOF_DEPOT_ACTION) return CMD_ERROR; rubidium@8869: break; rubidium@8869: rubidium@8869: case OT_GOTO_WAYPOINT: rubidium@8869: if (mof != MOF_NON_STOP) return CMD_ERROR; rubidium@8869: break; rubidium@8869: rubidium@8900: case OT_CONDITIONAL: rubidium@8900: if (mof != MOF_COND_VARIABLE && mof != MOF_COND_COMPARATOR && mof != MOF_COND_VALUE) return CMD_ERROR; rubidium@8900: break; rubidium@8900: rubidium@8869: default: rubidium@8869: return CMD_ERROR; rubidium@8869: } rubidium@8869: rubidium@8869: switch (mof) { rubidium@8900: default: NOT_REACHED(); rubidium@8900: rubidium@8869: case MOF_NON_STOP: rubidium@8919: if (v->type != VEH_TRAIN && v->type != VEH_ROAD) return CMD_ERROR; rubidium@8869: if (data >= ONSF_END) return CMD_ERROR; rubidium@8869: if (data == order->GetNonStopType()) return CMD_ERROR; rubidium@8869: break; rubidium@8869: rubidium@8869: case MOF_UNLOAD: rubidium@8883: if ((data & ~(OUFB_UNLOAD | OUFB_TRANSFER | OUFB_NO_UNLOAD)) != 0) return CMD_ERROR; rubidium@8883: /* Unload and no-unload are mutual exclusive and so are transfer and no unload. */ rubidium@8883: if (data != 0 && ((data & (OUFB_UNLOAD | OUFB_TRANSFER)) != 0) == ((data & OUFB_NO_UNLOAD) != 0)) return CMD_ERROR; rubidium@8869: if (data == order->GetUnloadType()) return CMD_ERROR; rubidium@8869: break; rubidium@8869: rubidium@8869: case MOF_LOAD: rubidium@8883: if (data > OLFB_NO_LOAD || data == 1) return CMD_ERROR; rubidium@8869: if (data == order->GetLoadType()) return CMD_ERROR; rubidium@8869: break; rubidium@8869: rubidium@8869: case MOF_DEPOT_ACTION: rubidium@8869: if (data != 0) return CMD_ERROR; rubidium@8869: break; rubidium@8900: rubidium@8900: case MOF_COND_VARIABLE: rubidium@8900: if (data >= OCV_END) return CMD_ERROR; rubidium@8900: break; rubidium@8900: rubidium@8900: case MOF_COND_COMPARATOR: rubidium@8900: if (data >= OCC_END) return CMD_ERROR; rubidium@8900: switch (order->GetConditionVariable()) { rubidium@8903: case OCV_UNCONDITIONALLY: return CMD_ERROR; rubidium@8903: rubidium@8900: case OCV_REQUIRES_SERVICE: rubidium@8900: if (data != OCC_IS_TRUE && data != OCC_IS_FALSE) return CMD_ERROR; rubidium@8900: break; rubidium@8900: rubidium@8900: default: rubidium@8900: if (data == OCC_IS_TRUE || data == OCC_IS_FALSE) return CMD_ERROR; rubidium@8900: break; rubidium@8900: } rubidium@8900: break; rubidium@8900: rubidium@8900: case MOF_COND_VALUE: rubidium@8900: switch (order->GetConditionVariable()) { rubidium@8903: case OCV_UNCONDITIONALLY: return CMD_ERROR; rubidium@8903: rubidium@8900: case OCV_LOAD_PERCENTAGE: rubidium@8900: case OCV_RELIABILITY: rubidium@8900: if (data > 100) return CMD_ERROR; rubidium@8900: break; rubidium@8900: rubidium@8900: default: rubidium@8900: if (data > 2047) return CMD_ERROR; rubidium@8900: break; rubidium@8900: } rubidium@8900: break; tron@4077: } truelight@0: truelight@0: if (flags & DC_EXEC) { rubidium@8869: switch (mof) { rubidium@8869: case MOF_NON_STOP: rubidium@8869: order->SetNonStopType((OrderNonStopFlags)data); rubidium@8869: break; rubidium@8869: rubidium@8869: case MOF_UNLOAD: rubidium@8869: order->SetUnloadType((OrderUnloadFlags)data); rubidium@8885: if ((data & OUFB_NO_UNLOAD) != 0 && (order->GetLoadType() & OLFB_NO_LOAD) != 0) { rubidium@8883: order->SetLoadType((OrderLoadFlags)(order->GetLoadType() & ~OLFB_NO_LOAD)); rubidium@8883: } rubidium@8869: break; rubidium@8869: rubidium@8869: case MOF_LOAD: rubidium@8869: order->SetLoadType((OrderLoadFlags)data); rubidium@8885: if ((data & OLFB_NO_LOAD) != 0 && (order->GetUnloadType() & OUFB_NO_UNLOAD) != 0) { rubidium@8885: /* No load + no unload isn't compatible */ rubidium@8885: order->SetUnloadType((OrderUnloadFlags)(order->GetUnloadType() & ~OUFB_NO_UNLOAD)); rubidium@8883: } rubidium@8845: break; rubidium@8869: rubidium@8869: case MOF_DEPOT_ACTION: rubidium@8869: order->SetDepotOrderType((OrderDepotTypeFlags)(order->GetDepotOrderType() ^ ODTFB_SERVICE)); rubidium@8845: break; rubidium@8869: rubidium@8900: case MOF_COND_VARIABLE: { rubidium@8900: order->SetConditionVariable((OrderConditionVariable)data); rubidium@8900: rubidium@8900: OrderConditionComparator occ = order->GetConditionComparator(); rubidium@8900: switch (order->GetConditionVariable()) { rubidium@8903: case OCV_UNCONDITIONALLY: rubidium@8903: order->SetConditionComparator(OCC_EQUALS); rubidium@8903: order->SetConditionValue(0); rubidium@8903: break; rubidium@8903: rubidium@8900: case OCV_REQUIRES_SERVICE: rubidium@8900: if (occ != OCC_IS_TRUE && occ != OCC_IS_FALSE) order->SetConditionComparator(OCC_IS_TRUE); rubidium@8900: break; rubidium@8900: rubidium@8900: case OCV_LOAD_PERCENTAGE: rubidium@8900: case OCV_RELIABILITY: rubidium@8900: if (order->GetConditionValue() > 100) order->SetConditionValue(100); rubidium@8900: /* FALL THROUGH */ rubidium@8900: default: rubidium@8900: if (occ == OCC_IS_TRUE || occ == OCC_IS_FALSE) order->SetConditionComparator(OCC_EQUALS); rubidium@8900: break; rubidium@8900: } rubidium@8900: } break; rubidium@8900: rubidium@8900: case MOF_COND_COMPARATOR: rubidium@8900: order->SetConditionComparator((OrderConditionComparator)data); rubidium@8900: break; rubidium@8900: rubidium@8900: case MOF_COND_VALUE: rubidium@8900: order->SetConditionValue(data); rubidium@8900: break; rubidium@8900: rubidium@8845: default: NOT_REACHED(); truelight@0: } truelight@0: matthijs@1760: /* Update the windows and full load flags, also for vehicles that share the same order list */ rubidium@8869: Vehicle *u = GetFirstVehicleFromSharedList(v); rubidium@8869: DeleteOrderWarnings(u); rubidium@8872: for (; u != NULL; u = u->next_shared) { rubidium@8872: /* Toggle u->current_order "Full load" flag if it changed. rubidium@8872: * However, as the same flag is used for depot orders, check rubidium@8872: * whether we are not going to a depot as there are three rubidium@8872: * cases where the full load flag can be active and only rubidium@8872: * one case where the flag is used for depot orders. In the rubidium@8872: * other cases for the OrderTypeByte the flags are not used, rubidium@8872: * so do not care and those orders should not be active rubidium@8872: * when this function is called. rubidium@8872: */ rubidium@8872: if (sel_ord == u->cur_order_index && rubidium@8872: (u->current_order.IsType(OT_GOTO_STATION) || u->current_order.IsType(OT_LOADING)) && rubidium@8872: u->current_order.GetLoadType() != order->GetLoadType()) { rubidium@8872: u->current_order.SetLoadType(order->GetLoadType()); truelight@1024: } rubidium@8872: InvalidateVehicleOrder(u); truelight@0: } truelight@0: } truelight@193: rubidium@6950: return CommandCost(); truelight@0: } truelight@0: Darkvater@1793: /** Clone/share/copy an order-list of an other vehicle. belugas@6481: * @param tile unused belugas@6481: * @param flags operation to perform Darkvater@1793: * @param p1 various bitstuffed elements Darkvater@1793: * - p1 = (bit 0-15) - destination vehicle to clone orders to (p1 & 0xFFFF) Darkvater@1793: * - p1 = (bit 16-31) - source vehicle to clone orders from, if any (none for CO_UNSHARE) Darkvater@1793: * @param p2 mode of cloning: CO_SHARE, CO_COPY, or CO_UNSHARE truelight@1024: */ rubidium@6943: CommandCost CmdCloneOrder(TileIndex tile, uint32 flags, uint32 p1, uint32 p2) truelight@1024: { Darkvater@1793: Vehicle *dst; tron@2140: VehicleID veh_src = GB(p1, 16, 16); tron@2140: VehicleID veh_dst = GB(p1, 0, 16); truelight@0: truelight@4352: if (!IsValidVehicleID(veh_dst)) return CMD_ERROR; truelight@0: Darkvater@1793: dst = GetVehicle(veh_dst); Darkvater@1793: truelight@4352: if (!CheckOwnership(dst->owner)) return CMD_ERROR; Darkvater@1793: Darkvater@1793: switch (p2) { truelight@1024: case CO_SHARE: { Darkvater@1793: Vehicle *src; Darkvater@1793: truelight@4352: if (!IsValidVehicleID(veh_src)) return CMD_ERROR; Darkvater@1793: Darkvater@1793: src = GetVehicle(veh_src); tron@588: truelight@1024: /* Sanity checks */ truelight@4352: if (!CheckOwnership(src->owner) || dst->type != src->type || dst == src) truelight@1024: return CMD_ERROR; truelight@0: truelight@1024: /* Trucks can't share orders with busses (and visa versa) */ rubidium@6259: if (src->type == VEH_ROAD) { peter1138@6329: if (src->cargo_type != dst->cargo_type && (IsCargoInClass(src->cargo_type, CC_PASSENGERS) || IsCargoInClass(dst->cargo_type, CC_PASSENGERS))) truelight@1024: return CMD_ERROR; truelight@1024: } truelight@193: truelight@1024: /* Is the vehicle already in the shared list? */ truelight@1024: { tron@2989: const Vehicle* u; tron@2989: tron@2989: for (u = GetFirstVehicleFromSharedList(src); u != NULL; u = u->next_shared) { tron@2989: if (u == dst) return CMD_ERROR; darkvater@29: } darkvater@29: } truelight@0: truelight@1024: if (flags & DC_EXEC) { truelight@1024: /* If the destination vehicle had a OrderList, destroy it */ truelight@1024: DeleteVehicleOrders(dst); truelight@0: truelight@1024: dst->orders = src->orders; truelight@1024: dst->num_orders = src->num_orders; tron@588: truelight@1024: /* Link this vehicle in the shared-list */ truelight@1024: dst->next_shared = src->next_shared; truelight@1024: dst->prev_shared = src; tron@2951: if (src->next_shared != NULL) src->next_shared->prev_shared = dst; truelight@1024: src->next_shared = dst; truelight@0: truelight@1024: InvalidateVehicleOrder(dst); truelight@1024: InvalidateVehicleOrder(src); truelight@1024: rubidium@9297: InvalidateWindowClassesData(GetWindowClassForVehicleType(dst->type), 0); truelight@1024: } truelight@1024: } break; truelight@1024: truelight@1024: case CO_COPY: { Darkvater@1793: Vehicle *src; truelight@1024: int delta; truelight@1024: truelight@4352: if (!IsValidVehicleID(veh_src)) return CMD_ERROR; Darkvater@1793: Darkvater@1793: src = GetVehicle(veh_src); Darkvater@1793: truelight@1024: /* Sanity checks */ truelight@4352: if (!CheckOwnership(src->owner) || dst->type != src->type || dst == src) truelight@1024: return CMD_ERROR; truelight@1024: truelight@1024: /* Trucks can't copy all the orders from busses (and visa versa) */ rubidium@6259: if (src->type == VEH_ROAD) { truelight@1024: const Order *order; celestar@1297: TileIndex required_dst = INVALID_TILE; truelight@1024: truelight@1024: FOR_VEHICLE_ORDERS(src, order) { rubidium@8836: if (order->IsType(OT_GOTO_STATION)) { rubidium@8840: const Station *st = GetStation(order->GetDestination()); peter1138@6329: if (IsCargoInClass(dst->cargo_type, CC_PASSENGERS)) { celestar@1297: if (st->bus_stops != NULL) required_dst = st->bus_stops->xy; celestar@1297: } else { celestar@1297: if (st->truck_stops != NULL) required_dst = st->truck_stops->xy; celestar@1297: } truelight@1024: /* This station has not the correct road-bay, so we can't copy! */ celestar@1297: if (required_dst == INVALID_TILE) truelight@1024: return CMD_ERROR; truelight@1024: } truelight@1024: } truelight@1024: } truelight@1024: truelight@1024: /* make sure there are orders available */ belugas@8469: delta = dst->IsOrderListShared() ? src->num_orders + 1 : src->num_orders - dst->num_orders; truelight@1024: if (!HasOrderPoolFree(delta)) truelight@1024: return_cmd_error(STR_8831_NO_MORE_SPACE_FOR_ORDERS); truelight@1024: truelight@1024: if (flags & DC_EXEC) { truelight@1024: const Order *order; truelight@1024: Order **order_dst; truelight@1024: truelight@1024: /* If the destination vehicle had a OrderList, destroy it */ truelight@1024: DeleteVehicleOrders(dst); truelight@1024: truelight@1024: order_dst = &dst->orders; truelight@1024: FOR_VEHICLE_ORDERS(src, order) { rubidium@7391: *order_dst = new Order(); rubidium@8835: (*order_dst)->AssignOrder(*order); truelight@1024: order_dst = &(*order_dst)->next; truelight@1024: } truelight@1024: truelight@1024: dst->num_orders = src->num_orders; truelight@1024: truelight@1024: InvalidateVehicleOrder(dst); truelight@1024: rubidium@9297: InvalidateWindowClassesData(GetWindowClassForVehicleType(dst->type), 0); truelight@1024: } truelight@1024: } break; truelight@1024: Darkvater@1793: case CO_UNSHARE: return DecloneOrder(dst, flags); Darkvater@1796: default: return CMD_ERROR; truelight@0: } truelight@0: rubidium@6950: return CommandCost(); truelight@0: } truelight@0: bjarni@4712: /** Add/remove refit orders from an order bjarni@4712: * @param tile Not used belugas@6481: * @param flags operation to perform bjarni@4712: * @param p1 VehicleIndex of the vehicle having the order bjarni@4712: * @param p2 bitmask bjarni@4712: * - bit 0-7 CargoID bjarni@4712: * - bit 8-15 Cargo subtype bjarni@4712: * - bit 16-23 number of order to modify bjarni@4712: */ rubidium@6943: CommandCost CmdOrderRefit(TileIndex tile, uint32 flags, uint32 p1, uint32 p2) bjarni@4712: { bjarni@4712: const Vehicle *v; bjarni@4712: Order *order; bjarni@4712: VehicleID veh = GB(p1, 0, 16); bjarni@4712: VehicleOrderID order_number = GB(p2, 16, 8); bjarni@4712: CargoID cargo = GB(p2, 0, 8); bjarni@4712: byte subtype = GB(p2, 8, 8); bjarni@4712: bjarni@4712: if (!IsValidVehicleID(veh)) return CMD_ERROR; bjarni@4712: bjarni@4712: v = GetVehicle(veh); bjarni@4712: bjarni@4712: if (!CheckOwnership(v->owner)) return CMD_ERROR; bjarni@4712: bjarni@4712: order = GetVehicleOrder(v, order_number); bjarni@4712: if (order == NULL) return CMD_ERROR; bjarni@4712: bjarni@4712: if (flags & DC_EXEC) { bjarni@4712: Vehicle *u; bjarni@4712: rubidium@8838: order->SetRefit(cargo, subtype); bjarni@4712: bjarni@4712: u = GetFirstVehicleFromSharedList(v); bjarni@4712: for (; u != NULL; u = u->next_shared) { bjarni@4712: /* Update any possible open window of the vehicle */ bjarni@4712: InvalidateVehicleOrder(u); bjarni@4712: bjarni@4712: /* If the vehicle already got the current depot set as current order, then update current order as well */ rubidium@8853: if (u->cur_order_index == order_number && u->current_order.GetDepotOrderType() & ODTFB_PART_OF_ORDERS) { rubidium@8838: u->current_order.SetRefit(cargo, subtype); bjarni@4712: } bjarni@4712: } bjarni@4712: } bjarni@4712: rubidium@6950: return CommandCost(); bjarni@4712: } bjarni@4712: truelight@1024: /** truelight@1024: * truelight@1024: * Backup a vehicle order-list, so you can replace a vehicle truelight@1024: * without loosing the order-list truelight@1024: * truelight@1024: */ Darkvater@1796: void BackupVehicleOrders(const Vehicle *v, BackuppedOrders *bak) truelight@0: { rubidium@7653: /* Make sure we always have freed the stuff */ rubidium@7653: free(bak->order); rubidium@7653: bak->order = NULL; peter1138@8258: free(bak->name); peter1138@8258: bak->name = NULL; rubidium@7653: truelight@1024: /* Save general info */ truelight@1024: bak->orderindex = v->cur_order_index; glx@8650: bak->group = v->group_id; truelight@0: bak->service_interval = v->service_interval; peter1138@8258: if (v->name != NULL) bak->name = strdup(v->name); truelight@0: truelight@1024: /* If we have shared orders, store it on a special way */ belugas@8469: if (v->IsOrderListShared()) { Darkvater@1796: const Vehicle *u = (v->next_shared) ? v->next_shared : v->prev_shared; truelight@1024: tron@555: bak->clone = u->index; tron@555: } else { truelight@1024: /* Else copy the orders */ truelight@1024: truelight@1024: /* We do not have shared orders */ tron@555: bak->clone = INVALID_VEHICLE; tron@555: rubidium@7653: rubidium@7653: /* Count the number of orders */ rubidium@7653: uint cnt = 0; rubidium@7653: const Order *order; rubidium@7653: FOR_VEHICLE_ORDERS(v, order) cnt++; rubidium@7653: rubidium@7653: /* Allocate memory for the orders plus an end-of-orders marker */ rubidium@7653: bak->order = MallocT(cnt + 1); rubidium@7653: rubidium@7653: Order *dest = bak->order; rubidium@7653: truelight@1024: /* Copy the orders */ truelight@1024: FOR_VEHICLE_ORDERS(v, order) { bjarni@9061: memcpy(dest, order, sizeof(Order)); truelight@1024: dest++; truelight@1024: } bjarni@6263: /* End the list with an empty order */ bjarni@6263: dest->Free(); truelight@0: } truelight@0: } truelight@0: truelight@1024: /** truelight@1024: * truelight@1024: * Restore vehicle orders that are backupped via BackupVehicleOrders truelight@1024: * truelight@1024: */ rubidium@7653: void RestoreVehicleOrders(const Vehicle *v, const BackuppedOrders *bak) truelight@0: { truelight@1024: /* If we have a custom name, process that */ peter1138@8258: if (bak->name != NULL) { tron@1820: _cmd_text = bak->name; truelight@0: DoCommandP(0, v->index, 0, NULL, CMD_NAME_VEHICLE); truelight@0: } truelight@0: truelight@1024: /* If we had shared orders, recover that */ tron@555: if (bak->clone != INVALID_VEHICLE) { smatz@8064: DoCommandP(0, v->index | (bak->clone << 16), CO_SHARE, NULL, CMD_CLONE_ORDER); rubidium@7820: } else { truelight@0: rubidium@7820: /* CMD_NO_TEST_IF_IN_NETWORK is used here, because CMD_INSERT_ORDER checks if the rubidium@7820: * order number is one more than the current amount of orders, and because rubidium@7820: * in network the commands are queued before send, the second insert always rubidium@7820: * fails in test mode. By bypassing the test-mode, that no longer is a problem. */ rubidium@7820: for (uint i = 0; bak->order[i].IsValid(); i++) { rubidium@8839: if (!DoCommandP(0, v->index + (i << 16), bak->order[i].Pack(), NULL, glx@8650: CMD_INSERT_ORDER | CMD_NO_TEST_IF_IN_NETWORK)) { rubidium@7820: break; glx@8650: } glx@8650: glx@8679: /* Copy timetable if enabled */ rubidium@9354: if (_settings.order.timetabling && !DoCommandP(0, v->index | (i << 16) | (1 << 25), glx@8650: bak->order[i].wait_time << 16 | bak->order[i].travel_time, NULL, glx@8650: CMD_CHANGE_TIMETABLE | CMD_NO_TEST_IF_IN_NETWORK)) { glx@8650: break; glx@8650: } rubidium@7820: } Darkvater@1796: } Darkvater@1796: Darkvater@1796: /* Restore vehicle order-index and service interval */ Darkvater@1796: DoCommandP(0, v->index, bak->orderindex | (bak->service_interval << 16) , NULL, CMD_RESTORE_ORDER_INDEX); glx@8650: glx@8650: /* Restore vehicle group */ glx@8650: DoCommandP(0, bak->group, v->index, NULL, CMD_ADD_VEHICLE_GROUP); truelight@0: } truelight@0: bjarni@9061: /** Restores vehicle orders that was previously backed up by BackupVehicleOrders() bjarni@9061: * This will restore to the point where it was at the time of the backup meaning bjarni@9061: * it will presume the same order indexes can be used. bjarni@9061: * This is needed when restoring a backed up vehicle bjarni@9061: * @param v The vehicle that should gain the orders bjarni@9061: * @param bak the backup of the orders bjarni@9061: */ bjarni@9061: void RestoreVehicleOrdersBruteForce(Vehicle *v, const BackuppedOrders *bak) bjarni@9061: { bjarni@9061: if (bak->name != NULL) { bjarni@9061: /* Restore the name. */ bjarni@9061: v->name = strdup(bak->name); bjarni@9061: } bjarni@9061: bjarni@9061: /* If we had shared orders, recover that */ bjarni@9061: if (bak->clone != INVALID_VEHICLE) { bjarni@9061: /* We will place it at the same location in the linked list as it previously was. */ bjarni@9061: if (v->prev_shared != NULL) { bjarni@9061: assert(v->prev_shared->next_shared == v->next_shared); bjarni@9061: v->prev_shared->next_shared = v; bjarni@9061: } bjarni@9061: if (v->next_shared != NULL) { bjarni@9061: assert(v->next_shared->prev_shared == v->prev_shared); bjarni@9061: v->next_shared->prev_shared = v; bjarni@9061: } bjarni@9061: } else { bjarni@9061: /* Restore the orders at the indexes they originally were. */ bjarni@9061: for (Order *order = bak->order; order->IsValid(); order++) { bjarni@9061: Order *dst = GetOrder(order->index); bjarni@9061: /* Since we are restoring something we removed a moment ago all the orders should be free. */ bjarni@9061: assert(!dst->IsValid()); bjarni@9061: memcpy(dst, order, sizeof(Order)); bjarni@9061: } bjarni@9061: } bjarni@9061: } bjarni@9061: Darkvater@1796: /** Restore the current order-index of a vehicle and sets service-interval. tron@3491: * @param tile unused belugas@6481: * @param flags operation to perform Darkvater@1796: * @param p1 the ID of the vehicle Darkvater@1796: * @param p2 various bistuffed elements Darkvater@1796: * - p2 = (bit 0-15) - current order-index (p2 & 0xFFFF) Darkvater@1796: * - p2 = (bit 16-31) - service interval (p2 >> 16) Darkvater@1796: * @todo Unfortunately you cannot safely restore the unitnumber or the old vehicle Darkvater@1796: * as far as I can see. We can store it in BackuppedOrders, and restore it, but Darkvater@1796: * but we have no way of seeing it has been tampered with or not, as we have no Darkvater@1796: * legit way of knowing what that ID was.@n Darkvater@1796: * If we do want to backup/restore it, just add UnitID uid to BackuppedOrders, and Darkvater@1796: * restore it as parameter 'y' (ugly hack I know) for example. "v->unitnumber = y;" truelight@0: */ rubidium@6943: CommandCost CmdRestoreOrderIndex(TileIndex tile, uint32 flags, uint32 p1, uint32 p2) truelight@0: { Darkvater@1796: Vehicle *v; truelight@4391: VehicleOrderID cur_ord = GB(p2, 0, 16); tron@2635: uint16 serv_int = GB(p2, 16, 16); tron@1718: truelight@4352: if (!IsValidVehicleID(p1)) return CMD_ERROR; Darkvater@1796: Darkvater@1796: v = GetVehicle(p1); truelight@4352: Darkvater@1796: /* Check the vehicle type and ownership, and if the service interval and order are in range */ truelight@4352: if (!CheckOwnership(v->owner)) return CMD_ERROR; Darkvater@1796: if (serv_int != GetServiceIntervalClamped(serv_int) || cur_ord >= v->num_orders) return CMD_ERROR; tron@1718: truelight@0: if (flags & DC_EXEC) { Darkvater@1796: v->cur_order_index = cur_ord; Darkvater@1796: v->service_interval = serv_int; truelight@0: } truelight@1024: rubidium@6950: return CommandCost(); truelight@0: } dominik@19: tron@4177: tron@4177: static TileIndex GetStationTileForVehicle(const Vehicle* v, const Station* st) tron@4177: { tron@4177: switch (v->type) { tron@4177: default: NOT_REACHED(); rubidium@6259: case VEH_TRAIN: return st->train_tile; glx@7683: case VEH_AIRCRAFT: return CanAircraftUseStation(v->engine_type, st) ? st->airport_tile : 0; rubidium@6259: case VEH_SHIP: return st->dock_tile; rubidium@6259: case VEH_ROAD: peter1138@6329: if (IsCargoInClass(v->cargo_type, CC_PASSENGERS)) { tron@4177: return (st->bus_stops != NULL) ? st->bus_stops->xy : 0; tron@4177: } else { tron@4177: return (st->truck_stops != NULL) ? st->truck_stops->xy : 0; tron@4177: } tron@4177: } tron@4177: } tron@4177: tron@4177: truelight@1024: /** truelight@1024: * truelight@1024: * Check the orders of a vehicle, to see if there are invalid orders and stuff truelight@1024: * truelight@1024: */ tron@3140: void CheckOrders(const Vehicle* v) dominik@19: { truelight@1024: /* Does the user wants us to check things? */ rubidium@9354: if (_settings.gui.order_review_system == 0) return; dominik@19: truelight@1024: /* Do nothing for crashed vehicles */ tron@3140: if (v->vehstatus & VS_CRASHED) return; truelight@1024: truelight@1024: /* Do nothing for stopped vehicles if setting is '1' */ rubidium@9354: if (_settings.gui.order_review_system == 1 && v->vehstatus & VS_STOPPED) tron@3140: return; truelight@1024: celestar@1053: /* do nothing we we're not the first vehicle in a share-chain */ tron@3140: if (v->next_shared != NULL) return; celestar@1053: truelight@1024: /* Only check every 20 days, so that we don't flood the message log */ tron@2549: if (v->owner == _local_player && v->day_counter % 20 == 0) { truelight@1024: int n_st, problem_type = -1; truelight@1024: const Order *order; truelight@1024: int message = 0; dominik@55: truelight@1024: /* Check the order list */ dominik@55: n_st = 0; dominik@55: truelight@1024: FOR_VEHICLE_ORDERS(v, order) { truelight@1024: /* Dummy order? */ rubidium@8836: if (order->IsType(OT_DUMMY)) { dominik@55: problem_type = 1; dominik@55: break; dominik@55: } truelight@1024: /* Does station have a load-bay for this vehicle? */ rubidium@8836: if (order->IsType(OT_GOTO_STATION)) { rubidium@8840: const Station* st = GetStation(order->GetDestination()); tron@4177: TileIndex required_tile = GetStationTileForVehicle(v, st); truelight@1024: dominik@19: n_st++; tron@2639: if (required_tile == 0) problem_type = 3; dominik@19: } dominik@19: } dominik@19: truelight@1024: /* Check if the last and the first order are the same */ tron@4077: if (v->num_orders > 1) { tron@4077: const Order* last = GetLastVehicleOrder(v); tron@4077: rubidium@8835: if (v->orders->Equals(*last)) { tron@4077: problem_type = 2; tron@4077: } tron@4077: } truelight@193: truelight@1024: /* Do we only have 1 station in our order list? */ tron@2639: if (n_st < 2 && problem_type == -1) problem_type = 0; truelight@193: truelight@1024: /* We don't have a problem */ tron@3140: if (problem_type < 0) return; truelight@193: bjarni@5955: message = STR_TRAIN_HAS_TOO_FEW_ORDERS + (v->type << 2) + problem_type; Darkvater@5380: //DEBUG(misc, 3, "Triggered News Item for vehicle %d", v->index); truelight@193: truelight@1024: SetDParam(0, v->unitnumber); tron@3140: AddNewsItem( dominik@55: message, rubidium@9234: NS_ADVICE, dominik@55: v->index, tron@3140: 0 tron@3140: ); dominik@19: } dominik@19: } truelight@1024: truelight@1024: /** truelight@4351: * Removes an order from all vehicles. Triggers when, say, a station is removed. truelight@4351: * @param type The type of the order (OT_GOTO_[STATION|DEPOT|WAYPOINT]). truelight@4351: * @param destination The destination. Can be a StationID, DepotID or WaypointID. truelight@1024: */ truelight@4389: void RemoveOrderFromAllVehicles(OrderType type, DestinationID destination) truelight@1024: { truelight@1024: Vehicle *v; truelight@1024: tron@4528: /* Aircraft have StationIDs for depot orders and never use DepotIDs tron@4528: * This fact is handled specially below tron@4528: */ tron@4528: truelight@1024: /* Go through all vehicles */ truelight@1024: FOR_ALL_VEHICLES(v) { tron@4533: Order *order; tron@4533: bool invalidate; tron@4533: truelight@1024: /* Forget about this station if this station is removed */ tron@4533: if (v->last_station_visited == destination && type == OT_GOTO_STATION) { truelight@1266: v->last_station_visited = INVALID_STATION; tron@4533: } truelight@1024: tron@4533: order = &v->current_order; rubidium@8836: if ((v->type == VEH_AIRCRAFT && order->IsType(OT_GOTO_DEPOT) ? OT_GOTO_STATION : order->GetType()) == type && rubidium@8840: v->current_order.GetDestination() == destination) { rubidium@8836: order->MakeDummy(); truelight@1024: InvalidateWindow(WC_VEHICLE_VIEW, v->index); truelight@1024: } truelight@1024: truelight@1024: /* Clear the order from the order-list */ tron@4533: invalidate = false; truelight@1024: FOR_VEHICLE_ORDERS(v, order) { rubidium@8836: if ((v->type == VEH_AIRCRAFT && order->IsType(OT_GOTO_DEPOT) ? OT_GOTO_STATION : order->GetType()) == type && rubidium@8840: order->GetDestination() == destination) { rubidium@8836: order->MakeDummy(); tron@4533: invalidate = true; truelight@1024: } truelight@1024: } truelight@1024: truelight@1024: /* Only invalidate once, and if needed */ tron@4533: if (invalidate) InvalidateWindow(WC_VEHICLE_ORDERS, v->index); truelight@1024: } truelight@1024: } truelight@1024: truelight@1024: /** truelight@1024: * truelight@1024: * Checks if a vehicle has a GOTO_DEPOT in his order list truelight@1024: * truelight@1024: * @return True if this is true (lol ;)) truelight@1024: * truelight@1024: */ truelight@1024: bool VehicleHasDepotOrders(const Vehicle *v) truelight@1024: { truelight@1024: const Order *order; truelight@1024: truelight@1024: FOR_VEHICLE_ORDERS(v, order) { rubidium@8836: if (order->IsType(OT_GOTO_DEPOT)) truelight@1024: return true; truelight@1024: } truelight@1024: truelight@1024: return false; truelight@1024: } truelight@1024: truelight@1024: /** truelight@1024: * truelight@1024: * Delete all orders from a vehicle truelight@1024: * truelight@1024: */ truelight@1024: void DeleteVehicleOrders(Vehicle *v) truelight@1024: { tron@3139: DeleteOrderWarnings(v); tron@3139: truelight@1024: /* If we have a shared order-list, don't delete the list, but just truelight@1024: remove our pointer */ belugas@8469: if (v->IsOrderListShared()) { truelight@6337: Vehicle *u = v; truelight@1024: truelight@1024: v->orders = NULL; truelight@1024: v->num_orders = 0; truelight@1024: truelight@1024: /* Unlink ourself */ truelight@1024: if (v->prev_shared != NULL) { truelight@1024: v->prev_shared->next_shared = v->next_shared; truelight@1024: u = v->prev_shared; truelight@1024: } truelight@1024: if (v->next_shared != NULL) { truelight@1024: v->next_shared->prev_shared = v->prev_shared; truelight@1024: u = v->next_shared; truelight@1024: } truelight@1024: v->prev_shared = NULL; truelight@1024: v->next_shared = NULL; truelight@1024: truelight@6337: /* If we are the only one left in the Shared Order Vehicle List, truelight@6337: * remove it, as we are no longer a Shared Order Vehicle */ truelight@6370: if (u->prev_shared == NULL && u->next_shared == NULL && u->orders != NULL) RemoveSharedOrderVehicleList(u); truelight@6337: truelight@1024: /* We only need to update this-one, because if there is a third rubidium@4549: * vehicle which shares the same order-list, nothing will change. If rubidium@4549: * this is the last vehicle, the last line of the order-window rubidium@4549: * will change from Shared order list, to Order list, so it needs rubidium@4549: * an update */ truelight@1024: InvalidateVehicleOrder(u); truelight@1024: return; truelight@1024: } truelight@1024: truelight@1024: /* Remove the orders */ bjarni@6263: Order *cur = v->orders; truelight@6336: /* Delete the vehicle list of shared orders, if any */ truelight@6336: if (cur != NULL) RemoveSharedOrderVehicleList(v); truelight@1024: v->orders = NULL; truelight@1024: v->num_orders = 0; truelight@1024: bjarni@4735: if (cur != NULL) { bjarni@6263: cur->FreeChain(); // Free the orders. truelight@1024: } truelight@1024: } truelight@1024: rubidium@8211: Date GetServiceIntervalClamped(uint index) rubidium@8211: { rubidium@9354: return (_settings.vehicle.servint_ispercent) ? Clamp(index, MIN_SERVINT_PERCENT, MAX_SERVINT_PERCENT) : Clamp(index, MIN_SERVINT_DAYS, MAX_SERVINT_DAYS); rubidium@8211: } rubidium@8211: rubidium@8827: /** rubidium@8830: * rubidium@8830: * Check if a vehicle has any valid orders rubidium@8830: * rubidium@8830: * @return false if there are no valid orders rubidium@8830: * rubidium@8830: */ rubidium@8830: static bool CheckForValidOrders(const Vehicle *v) rubidium@8830: { rubidium@8830: const Order *order; rubidium@8830: rubidium@8836: FOR_VEHICLE_ORDERS(v, order) if (!order->IsType(OT_DUMMY)) return true; rubidium@8830: rubidium@8830: return false; rubidium@8830: } rubidium@8830: rubidium@8830: /** rubidium@8900: * Compare the variable and value based on the given comparator. rubidium@8900: */ rubidium@8900: static bool OrderConditionCompare(OrderConditionComparator occ, int variable, int value) rubidium@8900: { rubidium@8900: switch (occ) { rubidium@8900: case OCC_EQUALS: return variable == value; rubidium@8900: case OCC_NOT_EQUALS: return variable != value; rubidium@8900: case OCC_LESS_THAN: return variable < value; rubidium@8900: case OCC_LESS_EQUALS: return variable <= value; rubidium@8900: case OCC_MORE_THAN: return variable > value; rubidium@8900: case OCC_MORE_EQUALS: return variable >= value; rubidium@8900: case OCC_IS_TRUE: return variable != 0; rubidium@8900: case OCC_IS_FALSE: return variable == 0; rubidium@8900: default: NOT_REACHED(); rubidium@8900: } rubidium@8900: } rubidium@8900: rubidium@8900: /** rubidium@8827: * Handle the orders of a vehicle and determine the next place rubidium@8827: * to go to if needed. rubidium@8827: * @param v the vehicle to do this for. rubidium@8827: * @return true *if* the vehicle is eligible for reversing rubidium@8827: * (basically only when leaving a station). rubidium@8827: */ rubidium@8827: bool ProcessOrders(Vehicle *v) rubidium@8827: { rubidium@8836: switch (v->current_order.GetType()) { rubidium@8827: case OT_GOTO_DEPOT: rubidium@8827: /* Let a depot order in the orderlist interrupt. */ rubidium@8853: if (!(v->current_order.GetDepotOrderType() & ODTFB_PART_OF_ORDERS)) return false; rubidium@8827: frosch@8862: if ((v->current_order.GetDepotOrderType() & ODTFB_SERVICE) && !v->NeedsServicing()) { rubidium@8827: UpdateVehicleTimetable(v, true); rubidium@8827: v->cur_order_index++; rubidium@8827: } rubidium@8827: break; rubidium@8827: rubidium@8827: case OT_LOADING: rubidium@8830: return false; rubidium@8830: rubidium@8827: case OT_LEAVESTATION: rubidium@8830: if (v->type != VEH_AIRCRAFT) return false; rubidium@8830: break; rubidium@8827: rubidium@8827: default: break; rubidium@8827: } rubidium@8827: rubidium@8827: /** rubidium@8827: * Reversing because of order change is allowed only just after leaving a rubidium@8827: * station (and the difficulty setting to allowed, of course) rubidium@8827: * this can be detected because only after OT_LEAVESTATION, current_order rubidium@8827: * will be reset to nothing. (That also happens if no order, but in that case rubidium@8827: * it won't hit the point in code where may_reverse is checked) rubidium@8827: */ rubidium@8836: bool may_reverse = v->current_order.IsType(OT_NOTHING); rubidium@8827: rubidium@8827: /* Check if we've reached the waypoint? */ rubidium@8836: if (v->current_order.IsType(OT_GOTO_WAYPOINT) && v->tile == v->dest_tile) { rubidium@8827: UpdateVehicleTimetable(v, true); rubidium@8827: v->cur_order_index++; rubidium@8827: } rubidium@8827: rubidium@8848: /* Check if we've reached a non-stop station.. */ frosch@8941: if (v->current_order.IsType(OT_GOTO_STATION) && (v->current_order.GetNonStopType() & ONSF_NO_STOP_AT_DESTINATION_STATION) && rubidium@8827: IsTileType(v->tile, MP_STATION) && rubidium@8840: v->current_order.GetDestination() == GetStationIndex(v->tile)) { rubidium@8840: v->last_station_visited = v->current_order.GetDestination(); rubidium@8827: UpdateVehicleTimetable(v, true); rubidium@8827: v->cur_order_index++; rubidium@8827: } rubidium@8827: rubidium@8827: /* Get the current order */ rubidium@8827: if (v->cur_order_index >= v->num_orders) v->cur_order_index = 0; rubidium@8827: rubidium@8827: const Order *order = GetVehicleOrder(v, v->cur_order_index); rubidium@8827: rubidium@8827: /* If no order, do nothing. */ rubidium@8836: if (order == NULL || (v->type == VEH_AIRCRAFT && order->IsType(OT_DUMMY) && !CheckForValidOrders(v))) { rubidium@8830: if (v->type == VEH_AIRCRAFT) { rubidium@8830: /* Aircraft do something vastly different here, so handle separately */ rubidium@8830: extern void HandleMissingAircraftOrders(Vehicle *v); rubidium@8830: HandleMissingAircraftOrders(v); rubidium@8830: return false; rubidium@8830: } rubidium@8830: rubidium@8827: v->current_order.Free(); rubidium@8827: v->dest_tile = 0; rubidium@8827: if (v->type == VEH_ROAD) ClearSlot(v); rubidium@8827: return false; rubidium@8827: } rubidium@8827: rubidium@8827: /* If it is unchanged, keep it. */ rubidium@8911: if (order->Equals(v->current_order) && (v->type == VEH_AIRCRAFT || v->dest_tile != 0) && rubidium@8840: (v->type != VEH_SHIP || !order->IsType(OT_GOTO_STATION) || GetStation(order->GetDestination())->dock_tile != 0)) { rubidium@8827: return false; rubidium@8827: } rubidium@8827: rubidium@8827: /* Otherwise set it, and determine the destination tile. */ rubidium@8827: v->current_order = *order; rubidium@8827: rubidium@8827: InvalidateVehicleOrder(v); rubidium@8827: switch (v->type) { rubidium@8827: default: rubidium@8827: NOT_REACHED(); rubidium@8827: rubidium@8827: case VEH_ROAD: rubidium@8827: case VEH_TRAIN: rubidium@8827: break; rubidium@8827: rubidium@8830: case VEH_AIRCRAFT: rubidium@8827: case VEH_SHIP: smatz@9043: InvalidateWindowClasses(GetWindowClassForVehicleType(v->type)); rubidium@8827: break; rubidium@8827: } rubidium@8827: rubidium@8836: switch (order->GetType()) { rubidium@8827: case OT_GOTO_STATION: rubidium@8840: v->dest_tile = v->GetOrderStationLocation(order->GetDestination()); rubidium@8827: break; rubidium@8827: rubidium@8827: case OT_GOTO_DEPOT: rubidium@8894: if (v->current_order.GetDepotActionType() & ODATFB_NEAREST_DEPOT) { rubidium@8894: /* We need to search for the nearest depot (hangar). */ rubidium@8894: TileIndex location; rubidium@8894: DestinationID destination; rubidium@8894: bool reverse; rubidium@8894: rubidium@8894: if (v->FindClosestDepot(&location, &destination, &reverse)) { rubidium@8894: v->dest_tile = location; rubidium@8894: v->current_order.MakeGoToDepot(destination, ODTFB_PART_OF_ORDERS); rubidium@8894: rubidium@8894: /* If there is no depot in front, reverse automatically (trains only) */ rubidium@8894: if (v->type == VEH_TRAIN && reverse) DoCommand(v->tile, v->index, 0, DC_EXEC, CMD_REVERSE_TRAIN_DIRECTION); rubidium@8894: rubidium@8894: if (v->type == VEH_AIRCRAFT && v->u.air.state == FLYING && v->u.air.targetairport != destination) { rubidium@8894: /* The aircraft is now heading for a different hangar than the next in the orders */ rubidium@8894: extern void AircraftNextAirportPos_and_Order(Vehicle *v); rubidium@8894: AircraftNextAirportPos_and_Order(v); rubidium@8894: } rubidium@8894: } else { rubidium@8894: UpdateVehicleTimetable(v, true); rubidium@8894: v->cur_order_index++; rubidium@8894: } rubidium@8894: } else if (v->type != VEH_AIRCRAFT) { rubidium@8894: v->dest_tile = GetDepot(order->GetDestination())->xy; rubidium@8894: } rubidium@8827: break; rubidium@8827: rubidium@8827: case OT_GOTO_WAYPOINT: rubidium@8840: v->dest_tile = GetWaypoint(order->GetDestination())->xy; rubidium@8827: break; rubidium@8827: rubidium@8900: case OT_CONDITIONAL: { rubidium@8900: bool skip_order = false; rubidium@8900: OrderConditionComparator occ = order->GetConditionComparator(); rubidium@8900: uint16 value = order->GetConditionValue(); rubidium@8900: rubidium@8900: switch (order->GetConditionVariable()) { rubidium@8900: case OCV_LOAD_PERCENTAGE: skip_order = OrderConditionCompare(occ, CalcPercentVehicleFilled(v, NULL), value); break; rubidium@8900: case OCV_RELIABILITY: skip_order = OrderConditionCompare(occ, v->reliability * 100 >> 16, value); break; rubidium@8900: case OCV_MAX_SPEED: skip_order = OrderConditionCompare(occ, v->GetDisplayMaxSpeed(), value); break; rubidium@8900: case OCV_AGE: skip_order = OrderConditionCompare(occ, v->age / 366, value); break; rubidium@8900: case OCV_REQUIRES_SERVICE: skip_order = OrderConditionCompare(occ, v->NeedsServicing(), value); break; rubidium@8903: case OCV_UNCONDITIONALLY: skip_order = true; break; rubidium@8900: default: NOT_REACHED(); rubidium@8900: } rubidium@8900: UpdateVehicleTimetable(v, true); rubidium@8900: if (skip_order) { rubidium@8900: v->cur_order_index = order->GetConditionSkipToOrder(); rubidium@8900: } else { rubidium@8900: v->cur_order_index++; rubidium@8900: } rubidium@8900: } return false; rubidium@8900: rubidium@8827: default: rubidium@8827: v->dest_tile = 0; rubidium@8827: return false; rubidium@8827: } rubidium@8827: rubidium@8827: return may_reverse; rubidium@8827: } truelight@1024: rubidium@8832: /** rubidium@8832: * Check whether the given vehicle should stop at the given station rubidium@8832: * based on this order and the non-stop settings. rubidium@8832: * @param v the vehicle that might be stopping. rubidium@8832: * @param station the station to stop at. rubidium@8832: * @return true if the vehicle should stop. rubidium@8832: */ rubidium@8832: bool Order::ShouldStopAtStation(const Vehicle *v, StationID station) const rubidium@8832: { frosch@8941: bool is_dest_station = this->IsType(OT_GOTO_STATION) && this->dest == station; rubidium@8832: return rubidium@9076: (!this->IsType(OT_GOTO_DEPOT) || (this->GetDepotOrderType() & ODTFB_PART_OF_ORDERS) != 0) && rubidium@8832: v->last_station_visited != station && // Do stop only when we've not just been there rubidium@8848: /* Finally do stop when there is no non-stop flag set for this type of station. */ frosch@8941: !(this->GetNonStopType() & (is_dest_station ? ONSF_NO_STOP_AT_DESTINATION_STATION : ONSF_NO_STOP_AT_INTERMEDIATE_STATIONS)); rubidium@8832: } rubidium@8832: rubidium@6247: void InitializeOrders() truelight@1024: { rubidium@7401: _Order_pool.CleanPool(); rubidium@7401: _Order_pool.AddBlockToPool(); truelight@1024: truelight@1024: _backup_orders_tile = 0; truelight@1024: } truelight@1024: rubidium@8836: const SaveLoad *GetOrderDescription() { Darkvater@1881: static const SaveLoad _order_desc[] = { tron@4527: SLE_VAR(Order, type, SLE_UINT8), tron@4527: SLE_VAR(Order, flags, SLE_UINT8), tron@4527: SLE_VAR(Order, dest, SLE_UINT16), tron@4527: SLE_REF(Order, next, REF_ORDER), maedhros@6980: SLE_CONDVAR(Order, refit_cargo, SLE_UINT8, 36, SL_MAX_VERSION), maedhros@6980: SLE_CONDVAR(Order, refit_subtype, SLE_UINT8, 36, SL_MAX_VERSION), maedhros@6980: SLE_CONDVAR(Order, wait_time, SLE_UINT16, 67, SL_MAX_VERSION), maedhros@6980: SLE_CONDVAR(Order, travel_time, SLE_UINT16, 67, SL_MAX_VERSION), truelight@1024: bjarni@4712: /* Leftover from the minor savegame version stuff bjarni@4712: * We will never use those free bytes, but we have to keep this line to allow loading of old savegames */ bjarni@4712: SLE_CONDNULL(10, 5, 35), truelight@1024: SLE_END() truelight@1024: }; rubidium@8836: return _order_desc; rubidium@8836: } truelight@1024: rubidium@6247: static void Save_ORDR() truelight@1024: { truelight@1024: Order *order; truelight@1024: truelight@1024: FOR_ALL_ORDERS(order) { truelight@4346: SlSetArrayIndex(order->index); rubidium@8836: SlObject(order, GetOrderDescription()); truelight@1024: } truelight@1024: } truelight@1024: rubidium@6247: static void Load_ORDR() truelight@1024: { truelight@2685: if (CheckSavegameVersionOldStyle(5, 2)) { truelight@2685: /* Version older than 5.2 did not have a ->next pointer. Convert them truelight@1024: (in the old days, the orderlist was 5000 items big) */ truelight@1024: uint len = SlGetFieldLength(); truelight@1024: uint i; truelight@1024: truelight@2685: if (CheckSavegameVersion(5)) { truelight@1024: /* Pre-version 5 had an other layout for orders truelight@1024: (uint16 instead of uint32) */ truelight@1024: len /= sizeof(uint16); rubidium@8373: uint16 *orders = MallocT(len + 1); truelight@1024: truelight@1024: SlArray(orders, len, SLE_UINT16); truelight@1024: truelight@1024: for (i = 0; i < len; ++i) { rubidium@7391: Order *order = new (i) Order(); rubidium@8835: order->AssignOrder(UnpackVersion4Order(orders[i])); truelight@1024: } rubidium@8373: rubidium@8373: free(orders); truelight@2685: } else if (CheckSavegameVersionOldStyle(5, 2)) { rubidium@8373: len /= sizeof(uint16); rubidium@8373: uint16 *orders = MallocT(len + 1); truelight@1024: truelight@1024: SlArray(orders, len, SLE_UINT32); truelight@1024: truelight@1024: for (i = 0; i < len; ++i) { rubidium@8839: new (i) Order(orders[i]); truelight@1024: } rubidium@8373: rubidium@8373: free(orders); truelight@1024: } truelight@1024: truelight@1024: /* Update all the next pointer */ truelight@1024: for (i = 1; i < len; ++i) { truelight@1024: /* The orders were built like this: bjarni@6263: * While the order is valid, set the previous will get it's next pointer set bjarni@6263: * We start with index 1 because no order will have the first in it's next pointer */ bjarni@6263: if (GetOrder(i)->IsValid()) truelight@1024: GetOrder(i - 1)->next = GetOrder(i); truelight@1024: } truelight@1024: } else { truelight@1024: int index; truelight@1024: truelight@1024: while ((index = SlIterateArray()) != -1) { rubidium@7391: Order *order = new (index) Order(); rubidium@8836: SlObject(order, GetOrderDescription()); truelight@1024: } truelight@1024: } truelight@1024: } truelight@1024: rubidium@5587: extern const ChunkHandler _order_chunk_handlers[] = { truelight@1024: { 'ORDR', Save_ORDR, Load_ORDR, CH_ARRAY | CH_LAST}, truelight@1024: };