tron@2186: /* $Id$ */ tron@2186: truelight@0: #include "stdafx.h" Darkvater@1891: #include "openttd.h" Darkvater@2433: #include "order.h" tron@1718: #include "airport.h" tron@1718: #include "depot.h" tron@2163: #include "functions.h" tron@507: #include "table/strings.h" truelight@0: #include "vehicle.h" tron@1718: #include "waypoint.h" truelight@0: #include "command.h" truelight@0: #include "station.h" truelight@0: #include "player.h" dominik@19: #include "news.h" truelight@1024: #include "saveload.h" matthijs@1752: #include "vehicle_gui.h" truelight@0: truelight@1314: /** truelight@1314: * Called if a new block is added to the order-pool truelight@1314: */ truelight@1314: static void OrderPoolNewBlock(uint start_item) truelight@1314: { truelight@1314: Order *order; truelight@1314: truelight@4346: /* We don't use FOR_ALL here, because FOR_ALL skips invalid items. truelight@4346: * TODO - This is just a temporary stage, this will be removed. */ tron@4977: for (order = GetOrder(start_item); order != NULL; order = (order->index + 1U < GetOrderPoolSize()) ? GetOrder(order->index + 1U) : NULL) order->index = start_item++; truelight@1314: } truelight@1314: matthijs@5216: DEFINE_OLD_POOL(Order, Order, OrderPoolNewBlock, NULL) truelight@1314: 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: { truelight@1024: Order order; tron@2140: order.type = GB(packed, 0, 4); tron@2140: order.flags = GB(packed, 4, 4); tron@4527: order.dest = GB(packed, 8, 8); truelight@1024: order.next = NULL; truelight@0: bjarni@4780: order.refit_cargo = CT_NO_REFIT; bjarni@4780: order.refit_subtype = 0; bjarni@4780: order.index = 0; // avoid compiler warning bjarni@4780: truelight@1024: // Sanity check truelight@1024: // TTD stores invalid orders as OT_NOTHING with non-zero flags/station tron@4527: if (order.type == OT_NOTHING && (order.flags != 0 || order.dest != 0)) { truelight@1024: order.type = OT_DUMMY; truelight@1024: order.flags = 0; truelight@1024: } truelight@0: truelight@1024: return order; truelight@1024: } truelight@1024: truelight@1024: /** truelight@1024: * truelight@1024: * Unpacks a order from savegames with version 4 and lower truelight@1024: * truelight@1024: */ tron@2817: static Order UnpackVersion4Order(uint16 packed) truelight@1024: { truelight@1024: Order order; tron@4527: order.type = GB(packed, 0, 4); tron@4527: order.flags = GB(packed, 4, 4); tron@4527: order.dest = GB(packed, 8, 8); tron@4527: order.next = NULL; tron@4527: order.index = 0; // avoid compiler warning bjarni@4780: order.refit_cargo = CT_NO_REFIT; bjarni@4715: order.refit_subtype = 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: { truelight@1024: InvalidateWindow(WC_VEHICLE_VIEW, v->index); truelight@1024: InvalidateWindow(WC_VEHICLE_ORDERS, 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; truelight@1043: AssignOrder(order1, *order2); truelight@1043: order1->next = order2->next; truelight@1043: AssignOrder(order2, temp_order); truelight@1043: order2->next = temp_order.next; truelight@1024: } truelight@1024: truelight@1024: /** truelight@1024: * truelight@1024: * Allocate a new order truelight@1024: * truelight@1024: * @return Order* if a free space is found, else NULL. truelight@1024: * truelight@1024: */ tron@1093: static Order *AllocateOrder(void) truelight@1024: { truelight@1024: Order *order; truelight@1024: truelight@4346: /* We don't use FOR_ALL here, because FOR_ALL skips invalid items. truelight@4346: * TODO - This is just a temporary stage, this will be removed. */ tron@4977: for (order = GetOrder(0); order != NULL; order = (order->index + 1U < GetOrderPoolSize()) ? GetOrder(order->index + 1U) : NULL) { truelight@4346: if (!IsValidOrder(order)) { truelight@4392: OrderID index = order->index; truelight@1314: tron@2639: memset(order, 0, sizeof(*order)); truelight@1024: order->index = index; truelight@1111: order->next = NULL; bjarni@4780: order->refit_cargo = CT_NO_REFIT; bjarni@4780: order->refit_subtype = 0; truelight@1314: truelight@1024: return order; truelight@1024: } truelight@1024: } truelight@1024: truelight@1314: /* Check if we can add a block to the pool */ tron@4977: if (AddBlockToPool(&_Order_pool)) return AllocateOrder(); truelight@1314: truelight@1024: return NULL; 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: */ truelight@1024: void AssignOrder(Order *order, Order data) truelight@1024: { tron@4527: order->type = data.type; tron@4527: order->flags = data.flags; tron@4527: order->dest = data.dest; bjarni@4712: bjarni@4780: order->refit_cargo = data.refit_cargo; bjarni@4780: order->refit_subtype = data.refit_subtype; 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: { tron@3139: DeleteVehicleNews(v->index, STR_TRAIN_HAS_TOO_FEW_ORDERS + (v->type - VEH_Train) * 4); tron@3139: DeleteVehicleNews(v->index, STR_TRAIN_HAS_VOID_ORDER + (v->type - VEH_Train) * 4); tron@3139: DeleteVehicleNews(v->index, STR_TRAIN_HAS_DUPLICATE_ENTRY + (v->type - VEH_Train) * 4); tron@3139: DeleteVehicleNews(v->index, STR_TRAIN_HAS_INVALID_ENTRY + (v->type - VEH_Train) * 4); tron@3139: } tron@3139: tron@3139: Darkvater@1786: /** Add an order to the orderlist of a vehicle. tron@3491: * @param tile unused 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: */ tron@3491: int32 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); Darkvater@1786: Order new_order = UnpackOrder(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 */ tron@1718: switch (new_order.type) { tron@1718: case OT_GOTO_STATION: { Darkvater@1786: const Station *st; tron@1718: tron@4527: if (!IsValidStationID(new_order.dest)) return CMD_ERROR; tron@4527: st = GetStation(new_order.dest); tron@1718: truelight@4352: if (st->airport_type != AT_OILRIG && !IsBuoy(st) && !CheckOwnership(st->owner)) { tron@1718: return CMD_ERROR; tron@1718: } tron@1718: tron@1718: switch (v->type) { tron@1718: case VEH_Train: tron@1718: if (!(st->facilities & FACIL_TRAIN)) return CMD_ERROR; tron@1718: break; tron@1718: tron@1718: case VEH_Road: tron@1718: if (v->cargo_type == CT_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: tron@1718: case VEH_Ship: tron@1718: if (!(st->facilities & FACIL_DOCK)) return CMD_ERROR; tron@1718: break; tron@1718: tron@1718: case VEH_Aircraft: tron@1718: if (!(st->facilities & FACIL_AIRPORT)) return CMD_ERROR; tron@1718: break; tron@1718: Darkvater@1786: default: return CMD_ERROR; tron@1718: } tron@1718: Darkvater@2433: /* Order flags can be any of the following for stations: Darkvater@2433: * [full-load | unload] [+ transfer] [+ non-stop] Darkvater@2433: * non-stop orders (if any) are only valid for trains */ tron@1718: switch (new_order.flags) { tron@2639: case 0: tron@1718: case OF_FULL_LOAD: tron@2639: case OF_FULL_LOAD | OF_TRANSFER: tron@1718: case OF_UNLOAD: tron@2639: case OF_UNLOAD | OF_TRANSFER: Darkvater@2433: case OF_TRANSFER: Darkvater@2433: break; Darkvater@2433: tron@1718: case OF_NON_STOP: tron@1718: case OF_NON_STOP | OF_FULL_LOAD: tron@2639: case OF_NON_STOP | OF_FULL_LOAD | OF_TRANSFER: tron@1718: case OF_NON_STOP | OF_UNLOAD: tron@2639: case OF_NON_STOP | OF_UNLOAD | OF_TRANSFER: Darkvater@2433: case OF_NON_STOP | OF_TRANSFER: Darkvater@2433: if (v->type != VEH_Train) return CMD_ERROR; tron@1718: break; tron@1718: Darkvater@1786: default: return CMD_ERROR; tron@1718: } tron@1718: break; tron@1718: } tron@1718: tron@1718: case OT_GOTO_DEPOT: { tron@1718: if (v->type == VEH_Aircraft) { tron@1718: const Station* st; tron@1718: tron@4527: if (!IsValidStationID(new_order.dest)) return CMD_ERROR; tron@4527: st = GetStation(new_order.dest); tron@1718: truelight@4352: if ((st->airport_type != AT_OILRIG && !CheckOwnership(st->owner)) || tron@1718: !(st->facilities & FACIL_AIRPORT) || tron@1718: GetAirport(st->airport_type)->nof_depots == 0) { tron@1718: return CMD_ERROR; tron@1718: } tron@1718: } else { tron@1718: const Depot* dp; tron@1718: tron@4527: if (!IsValidDepotID(new_order.dest)) return CMD_ERROR; tron@4527: dp = GetDepot(new_order.dest); tron@1718: truelight@4352: if (!CheckOwnership(GetTileOwner(dp->xy))) return CMD_ERROR; tron@1718: tron@1718: switch (v->type) { tron@1718: case VEH_Train: tron@1718: if (!IsTileDepotType(dp->xy, TRANSPORT_RAIL)) return CMD_ERROR; tron@1718: break; tron@1718: tron@1718: case VEH_Road: tron@1718: if (!IsTileDepotType(dp->xy, TRANSPORT_ROAD)) return CMD_ERROR; tron@1718: break; tron@1718: tron@1718: case VEH_Ship: tron@1718: if (!IsTileDepotType(dp->xy, TRANSPORT_WATER)) return CMD_ERROR; tron@1718: break; tron@1718: Darkvater@1786: default: return CMD_ERROR; tron@1718: } tron@1718: } tron@1718: Darkvater@2433: /* Order flags can be any of the following for depots: Darkvater@2433: * order [+ halt] [+ non-stop] Darkvater@2433: * non-stop orders (if any) are only valid for trains */ tron@1718: switch (new_order.flags) { tron@1718: case OF_PART_OF_ORDERS: tron@1718: case OF_PART_OF_ORDERS | OF_HALT_IN_DEPOT: Darkvater@2433: break; Darkvater@2433: tron@1718: case OF_NON_STOP | OF_PART_OF_ORDERS: tron@1718: case OF_NON_STOP | OF_PART_OF_ORDERS | OF_HALT_IN_DEPOT: Darkvater@2433: if (v->type != VEH_Train) return CMD_ERROR; tron@1718: break; tron@1718: Darkvater@1786: default: return CMD_ERROR; tron@1718: } tron@1718: break; tron@1718: } tron@1718: tron@1718: case OT_GOTO_WAYPOINT: { tron@1718: const Waypoint* wp; tron@1718: tron@1718: if (v->type != VEH_Train) return CMD_ERROR; tron@1718: tron@4527: if (!IsValidWaypointID(new_order.dest)) return CMD_ERROR; tron@4527: wp = GetWaypoint(new_order.dest); 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 */ tron@1718: switch (new_order.flags) { Darkvater@2433: case 0: break; Darkvater@2433: tron@1718: case OF_NON_STOP: Darkvater@2433: if (v->type != VEH_Train) return CMD_ERROR; tron@1718: break; tron@1718: Darkvater@1786: default: return CMD_ERROR; tron@1718: } tron@1718: break; tron@1718: } tron@1718: Darkvater@1786: default: return CMD_ERROR; tron@1718: } tron@1718: Darkvater@1786: if (sel_ord > v->num_orders) return CMD_ERROR; truelight@1024: Darkvater@1786: if (IsOrderPoolFull()) return_cmd_error(STR_8831_NO_MORE_SPACE_FOR_ORDERS); truelight@1024: truelight@1024: /* XXX - This limit is only here because the backuppedorders can't Darkvater@1786: * handle any more then this.. */ Darkvater@2433: if (v->num_orders >= MAX_BACKUP_ORDER_COUNT) return_cmd_error(STR_8832_TOO_MANY_ORDERS); truelight@1024: matthijs@1247: /* For ships, make sure that the station is not too far away from the matthijs@1247: * previous destination, for human players with new pathfinding disabled */ Darkvater@4845: if (v->type == VEH_Ship && IsHumanPlayer(v->owner) && Darkvater@1786: sel_ord != 0 && GetVehicleOrder(v, sel_ord - 1)->type == OT_GOTO_STATION matthijs@1247: && !_patches.new_pathfinding_all) { truelight@193: tron@1245: int dist = DistanceManhattan( tron@4527: GetStation(GetVehicleOrder(v, sel_ord - 1)->dest)->xy, tron@4527: GetStation(new_order.dest)->xy // XXX type != OT_GOTO_STATION? tron@555: ); truelight@0: if (dist >= 130) truelight@0: return_cmd_error(STR_0210_TOO_FAR_FROM_PREVIOUS_DESTINATIO); truelight@0: } truelight@0: truelight@0: if (flags & DC_EXEC) { truelight@0: Vehicle *u; Darkvater@1786: Order *new = AllocateOrder(); truelight@1024: AssignOrder(new, new_order); truelight@193: truelight@1024: /* Create new order and link in list */ truelight@1024: if (v->orders == NULL) { truelight@1024: v->orders = new; 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 */ truelight@1024: SwapOrders(v->orders, new); truelight@1024: /* Now update the next pointers */ truelight@1024: v->orders->next = new; truelight@1024: } else if (order == NULL) { truelight@1024: /* 'sel' is a non-existing order, add him to the end */ truelight@1024: order = GetLastVehicleOrder(v); truelight@1024: order->next = new; truelight@1024: } else { truelight@1024: /* Put the new order in between */ truelight@1024: new->next = order->next; truelight@1024: order->next = new; 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: truelight@1024: /* Make sure to rebuild the whole list */ tron@588: RebuildVehicleLists(); truelight@0: } truelight@0: truelight@0: return 0; 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: */ truelight@0: static int32 DecloneOrder(Vehicle *dst, uint32 flags) truelight@0: { truelight@0: if (flags & DC_EXEC) { truelight@1024: DeleteVehicleOrders(dst); truelight@1024: InvalidateVehicleOrder(dst); tron@588: RebuildVehicleLists(); truelight@0: } truelight@0: return 0; truelight@0: } truelight@0: Darkvater@1786: /** Delete an order from the orderlist of a vehicle. tron@3491: * @param tile unused Darkvater@1786: * @param p1 the ID of the vehicle Darkvater@1786: * @param p2 the order to delete (max 255) truelight@0: */ tron@3491: int32 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@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 */ truelight@1024: order->type = OT_NOTHING; truelight@1106: order->next = NULL; 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 */ Darkvater@1786: if (sel_ord == u->cur_order_index && u->current_order.type == OT_LOADING && truelight@1024: HASBIT(u->current_order.flags, OFB_NON_STOP)) { truelight@1024: u->current_order.flags = 0; truelight@0: } truelight@1024: truelight@1024: /* Update any possible open window of the vehicle */ truelight@1024: InvalidateVehicleOrder(u); truelight@0: } tron@588: tron@588: RebuildVehicleLists(); truelight@0: } truelight@193: truelight@0: return 0; truelight@0: } truelight@0: Darkvater@1786: /** Goto next order of order-list. tron@3491: * @param tile unused Darkvater@1786: * @param p1 The ID of the vehicle which order is skipped Darkvater@1786: * @param p2 unused truelight@1024: */ tron@3491: int32 CmdSkipOrder(TileIndex tile, uint32 flags, uint32 p1, uint32 p2) truelight@0: { tron@1718: Vehicle *v; Darkvater@1786: VehicleID veh_id = p1; tron@1718: 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; celestar@1020: truelight@0: if (flags & DC_EXEC) { truelight@1024: /* Goto next order */ truelight@4391: VehicleOrderID b = v->cur_order_index + 1; Darkvater@1786: if (b >= v->num_orders) b = 0; truelight@1024: Darkvater@1786: v->cur_order_index = b; truelight@193: Darkvater@1786: if (v->type == VEH_Train) v->u.rail.days_since_order_progr = 0; celestar@1217: celestar@3123: if (v->type == VEH_Road) ClearSlot(v); truelight@0: 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 */ Darkvater@1786: if (v->current_order.type == OT_LOADING && HASBIT(v->current_order.flags, OFB_NON_STOP)) tron@555: v->current_order.flags = 0; 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 */ Celestar@1055: if (v->type == VEH_Aircraft) InvalidateWindowClasses(WC_AIRCRAFT_LIST); Celestar@1055: if (v->type == VEH_Ship) InvalidateWindowClasses(WC_SHIPS_LIST); celestar@1020: truelight@0: return 0; truelight@0: } truelight@0: truelight@1024: Darkvater@1786: /** Modify an order in the orderlist of a vehicle. tron@3491: * @param tile unused 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 mode to change the order to (always set) truelight@0: */ tron@3491: int32 CmdModifyOrder(TileIndex tile, uint32 flags, uint32 p1, uint32 p2) truelight@0: { tron@1718: Vehicle *v; truelight@1024: Order *order; truelight@4391: VehicleOrderID sel_ord = GB(p1, 16, 16); // XXX - automatically truncated to 8 bits. tron@2140: VehicleID veh = GB(p1, 0, 16); truelight@0: truelight@4352: if (!IsValidVehicleID(veh)) return CMD_ERROR; celestar@1935: if (p2 != OFB_FULL_LOAD && p2 != OFB_UNLOAD && p2 != OFB_NON_STOP && p2 != OFB_TRANSFER) return CMD_ERROR; Darkvater@1786: Darkvater@1786: v = GetVehicle(veh); truelight@4352: 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: Darkvater@1786: order = GetVehicleOrder(v, sel_ord); truelight@1024: if (order->type != OT_GOTO_STATION && tron@4077: (order->type != OT_GOTO_DEPOT || p2 == OFB_UNLOAD) && tron@4077: (order->type != OT_GOTO_WAYPOINT || p2 != OFB_NON_STOP)) { truelight@0: return CMD_ERROR; tron@4077: } truelight@0: truelight@0: if (flags & DC_EXEC) { Darkvater@1786: switch (p2) { truelight@1024: case OFB_FULL_LOAD: truelight@1024: TOGGLEBIT(order->flags, OFB_FULL_LOAD); tron@2639: if (order->type != OT_GOTO_DEPOT) CLRBIT(order->flags, OFB_UNLOAD); truelight@0: break; truelight@1024: case OFB_UNLOAD: truelight@1024: TOGGLEBIT(order->flags, OFB_UNLOAD); truelight@1024: CLRBIT(order->flags, OFB_FULL_LOAD); truelight@0: break; truelight@1024: case OFB_NON_STOP: truelight@1024: TOGGLEBIT(order->flags, OFB_NON_STOP); truelight@0: break; celestar@1935: case OFB_TRANSFER: celestar@1935: TOGGLEBIT(order->flags, OFB_TRANSFER); celestar@1935: break; Darkvater@1786: 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 */ truelight@1024: { tron@2989: Vehicle* u; tron@2989: tron@3139: u = GetFirstVehicleFromSharedList(v); tron@3139: DeleteOrderWarnings(u); tron@3139: for (; u != NULL; u = u->next_shared) { rubidium@5562: /* Toggle u->current_order "Full load" flag if it changed. rubidium@5562: * However, as the same flag is used for depot orders, check rubidium@5562: * whether we are not going to a depot as there are three rubidium@5562: * cases where the full load flag can be active and only rubidium@5562: * one case where the flag is used for depot orders. In the rubidium@5562: * other cases for the OrderType the flags are not used, rubidium@5562: * so do not care and those orders should not be active rubidium@5562: * when this function is called. rubidium@5562: */ Darkvater@1786: if (sel_ord == u->cur_order_index && rubidium@5562: u->current_order.type != OT_GOTO_DEPOT && tron@2989: HASBIT(u->current_order.flags, OFB_FULL_LOAD) != HASBIT(order->flags, OFB_FULL_LOAD)) { matthijs@1760: TOGGLEBIT(u->current_order.flags, OFB_FULL_LOAD); tron@2989: } truelight@1024: InvalidateVehicleOrder(u); truelight@1024: } truelight@0: } truelight@0: } truelight@193: truelight@0: return 0; truelight@0: } truelight@0: Darkvater@1793: /** Clone/share/copy an order-list of an other vehicle. 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: */ tron@3491: int32 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) */ truelight@1024: if (src->type == VEH_Road) { truelight@1024: if (src->cargo_type != dst->cargo_type && (src->cargo_type == CT_PASSENGERS || dst->cargo_type == CT_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: truelight@1024: RebuildVehicleLists(); 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) */ truelight@1024: 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) { truelight@1024: if (order->type == OT_GOTO_STATION) { tron@4527: const Station *st = GetStation(order->dest); celestar@1297: if (dst->cargo_type == CT_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 */ truelight@1024: delta = IsOrderListShared(dst) ? 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) { truelight@1024: *order_dst = AllocateOrder(); truelight@1024: AssignOrder(*order_dst, *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: truelight@1024: RebuildVehicleLists(); 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: truelight@0: return 0; truelight@0: } truelight@0: bjarni@4712: /** Add/remove refit orders from an order bjarni@4712: * @param tile Not used 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: */ bjarni@4712: int32 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: bjarni@4712: order->refit_cargo = cargo; bjarni@4712: order->refit_subtype = 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 */ bjarni@4712: if (u->cur_order_index == order_number && HASBIT(u->current_order.flags, OFB_PART_OF_ORDERS)) { bjarni@4712: u->current_order.refit_cargo = cargo; bjarni@4712: u->current_order.refit_subtype = subtype; bjarni@4712: } bjarni@4712: } bjarni@4712: } bjarni@4712: bjarni@4712: return 0; 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: { truelight@1024: /* Save general info */ truelight@1024: bak->orderindex = v->cur_order_index; truelight@0: bak->service_interval = v->service_interval; truelight@193: truelight@1024: /* Safe custom string, if any */ peter1138@4862: if (!IsCustomName(v->string_id)) { tron@2639: bak->name[0] = '\0'; truelight@0: } else { Darkvater@4912: GetName(bak->name, v->string_id & 0x7FF, lastof(bak->name)); truelight@0: } truelight@0: truelight@1024: /* If we have shared orders, store it on a special way */ Darkvater@1796: if (IsOrderListShared(v)) { 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: Order *order, *dest; tron@555: Darkvater@1796: dest = bak->order; truelight@1024: truelight@1024: /* We do not have shared orders */ tron@555: bak->clone = INVALID_VEHICLE; tron@555: truelight@1024: /* Copy the orders */ truelight@1024: FOR_VEHICLE_ORDERS(v, order) { truelight@1024: *dest = *order; truelight@1024: dest++; truelight@1024: } truelight@1024: /* End the list with an OT_NOTHING */ truelight@1024: dest->type = OT_NOTHING; truelight@1106: dest->next = NULL; truelight@0: } truelight@0: } truelight@0: truelight@1024: /** truelight@1024: * truelight@1024: * Restore vehicle orders that are backupped via BackupVehicleOrders truelight@1024: * truelight@1024: */ tron@2630: void RestoreVehicleOrders(const Vehicle* v, const BackuppedOrders* bak) truelight@0: { Darkvater@1796: uint i; truelight@0: truelight@1024: /* If we have a custom name, process that */ truelight@1024: if (bak->name[0] != 0) { 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) { truelight@1024: DoCommandP(0, v->index | (bak->clone << 16), 0, NULL, CMD_CLONE_ORDER); truelight@0: return; truelight@0: } truelight@0: truelight@1024: /* CMD_NO_TEST_IF_IN_NETWORK is used here, because CMD_INSERT_ORDER checks if the rubidium@4549: * order number is one more than the current amount of orders, and because rubidium@4549: * in network the commands are queued before send, the second insert always rubidium@4549: * fails in test mode. By bypassing the test-mode, that no longer is a problem. */ Darkvater@1796: for (i = 0; bak->order[i].type != OT_NOTHING; i++) { tron@555: if (!DoCommandP(0, v->index + (i << 16), PackOrder(&bak->order[i]), NULL, CMD_INSERT_ORDER | CMD_NO_TEST_IF_IN_NETWORK)) truelight@0: break; 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); truelight@0: } truelight@0: Darkvater@1796: /** Restore the current order-index of a vehicle and sets service-interval. tron@3491: * @param tile unused 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: */ tron@3491: int32 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: truelight@0: return 0; 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(); tron@4177: case VEH_Train: return st->train_tile; tron@4177: case VEH_Aircraft: return st->airport_tile; tron@4177: case VEH_Ship: return st->dock_tile; tron@4177: case VEH_Road: tron@4177: if (v->cargo_type == CT_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? */ tron@3140: if (_patches.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' */ tron@2549: if (_patches.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? */ truelight@1024: if (order->type == OT_DUMMY) { dominik@55: problem_type = 1; dominik@55: break; dominik@55: } truelight@1024: /* Does station have a load-bay for this vehicle? */ truelight@1024: if (order->type == OT_GOTO_STATION) { tron@4527: const Station* st = GetStation(order->dest); 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: tron@4527: if (v->orders->type == last->type && tron@4527: v->orders->flags == last->flags && tron@4527: v->orders->dest == last->dest) { 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: tron@2549: message = STR_TRAIN_HAS_TOO_FEW_ORDERS + ((v->type - VEH_Train) << 2) + problem_type; Darkvater@5568: //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, truelight@1024: NEWS_FLAGS(NM_SMALL, NF_VIEWPORT | NF_VEHICLE, NT_ADVICE, 0), 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; tron@4533: if ((v->type == VEH_Aircraft && order->type == OT_GOTO_DEPOT ? OT_GOTO_STATION : order->type) == type && tron@4528: v->current_order.dest == destination) { tron@4533: order->type = OT_DUMMY; tron@4533: order->flags = 0; 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) { tron@4528: if ((v->type == VEH_Aircraft && order->type == OT_GOTO_DEPOT ? OT_GOTO_STATION : order->type) == type && tron@4528: order->dest == destination) { truelight@1024: order->type = OT_DUMMY; truelight@1024: order->flags = 0; 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) { truelight@1024: if (order->type == 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: { truelight@4384: Order *cur, *next; 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 */ truelight@1024: if (IsOrderListShared(v)) { truelight@1024: const 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@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 */ truelight@1024: cur = v->orders; truelight@1024: v->orders = NULL; truelight@1024: v->num_orders = 0; truelight@1024: bjarni@4735: if (cur != NULL) { bjarni@4735: /* Delete the vehicle list of shared orders, if any */ bjarni@4735: int window_type = 0; bjarni@4735: bjarni@4735: switch (v->type) { bjarni@4735: case VEH_Train: window_type = WC_TRAINS_LIST; break; bjarni@4735: case VEH_Road: window_type = WC_ROADVEH_LIST; break; bjarni@4735: case VEH_Ship: window_type = WC_SHIPS_LIST; break; bjarni@4735: case VEH_Aircraft: window_type = WC_AIRCRAFT_LIST; break; bjarni@4735: default: NOT_REACHED(); bjarni@4735: } bjarni@4735: DeleteWindowById(window_type, (cur->index << 16) | (v->type << 11) | VLW_SHARED_ORDERS | v->owner); bjarni@4735: } bjarni@4735: bjarni@4735: while (cur != NULL) { truelight@4384: next = cur->next; truelight@4384: DeleteOrder(cur); truelight@4384: cur = next; truelight@1024: } truelight@1024: } truelight@1024: truelight@1024: /** truelight@1024: * truelight@1024: * Check if we share our orders with an other vehicle truelight@1024: * truelight@1024: * @return Returns the vehicle who has the same order truelight@1024: * truelight@1024: */ truelight@1024: bool IsOrderListShared(const Vehicle *v) truelight@1024: { tron@2645: return v->next_shared != NULL || v->prev_shared != NULL; truelight@1024: } truelight@1024: truelight@1024: /** truelight@1024: * truelight@1024: * Check if a vehicle has any valid orders truelight@1024: * truelight@1024: * @return false if there are no valid orders truelight@1024: * truelight@1024: */ tron@2630: bool CheckForValidOrders(const Vehicle* v) truelight@1024: { truelight@1024: const Order *order; truelight@1024: tron@2639: FOR_VEHICLE_ORDERS(v, order) if (order->type != OT_DUMMY) return true; truelight@1024: truelight@1024: return false; truelight@1024: } truelight@1024: truelight@1024: void InitializeOrders(void) truelight@1024: { tron@4977: CleanPool(&_Order_pool); tron@4977: AddBlockToPool(&_Order_pool); truelight@1024: truelight@1024: _backup_orders_tile = 0; truelight@1024: } truelight@1024: 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), bjarni@4712: SLE_CONDVAR(Order, refit_cargo, SLE_UINT8, 36, SL_MAX_VERSION), bjarni@4712: SLE_CONDVAR(Order, refit_subtype, SLE_UINT8, 36, 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: }; truelight@1024: tron@1093: static void Save_ORDR(void) truelight@1024: { truelight@1024: Order *order; truelight@1024: truelight@1024: FOR_ALL_ORDERS(order) { truelight@4346: SlSetArrayIndex(order->index); truelight@4346: SlObject(order, _order_desc); truelight@1024: } truelight@1024: } truelight@1024: tron@1093: static void Load_ORDR(void) 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: uint16 orders[5000]; truelight@1024: truelight@1024: len /= sizeof(uint16); truelight@1314: assert (len <= lengthof(orders)); truelight@1024: truelight@1024: SlArray(orders, len, SLE_UINT16); truelight@1024: truelight@1024: for (i = 0; i < len; ++i) { tron@4977: if (!AddBlockIfNeeded(&_Order_pool, i)) truelight@1314: error("Orders: failed loading savegame: too many orders"); truelight@1314: truelight@1024: AssignOrder(GetOrder(i), UnpackVersion4Order(orders[i])); truelight@1024: } truelight@2685: } else if (CheckSavegameVersionOldStyle(5, 2)) { truelight@1024: uint32 orders[5000]; truelight@1024: truelight@1024: len /= sizeof(uint32); truelight@1314: assert (len <= lengthof(orders)); truelight@1024: truelight@1024: SlArray(orders, len, SLE_UINT32); truelight@1024: truelight@1024: for (i = 0; i < len; ++i) { tron@4977: if (!AddBlockIfNeeded(&_Order_pool, i)) truelight@1314: error("Orders: failed loading savegame: too many orders"); truelight@1314: truelight@1024: AssignOrder(GetOrder(i), UnpackOrder(orders[i])); truelight@1024: } 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: rubidium@4549: * Vehicle one had order[0], and as long as order++.type was not rubidium@4549: * OT_NOTHING, it was part of the order-list of that vehicle */ truelight@1024: if (GetOrder(i)->type != OT_NOTHING) 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) { truelight@1314: Order *order; truelight@1024: tron@4977: if (!AddBlockIfNeeded(&_Order_pool, index)) truelight@1314: error("Orders: failed loading savegame: too many orders"); truelight@1314: truelight@1314: order = GetOrder(index); truelight@1024: SlObject(order, _order_desc); truelight@1024: } truelight@1024: } truelight@1024: } truelight@1024: truelight@1024: const ChunkHandler _order_chunk_handlers[] = { truelight@1024: { 'ORDR', Save_ORDR, Load_ORDR, CH_ARRAY | CH_LAST}, truelight@1024: };