truelight@0: #include "stdafx.h" truelight@0: #include "ttd.h" tron@507: #include "table/strings.h" truelight@0: #include "vehicle.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" truelight@0: 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; truelight@1024: order.type = (packed & 0x000F); truelight@1024: order.flags = (packed & 0x00F0) >> 4; truelight@1024: order.station = (packed & 0xFF00) >> 8; truelight@1024: order.next = NULL; truelight@0: truelight@1024: // Sanity check truelight@1024: // TTD stores invalid orders as OT_NOTHING with non-zero flags/station truelight@1024: if (order.type == OT_NOTHING && (order.flags != 0 || order.station != 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: */ truelight@1024: Order UnpackVersion4Order(uint16 packed) truelight@1024: { truelight@1024: Order order; truelight@1024: order.type = (packed & 0x000F); truelight@1024: order.flags = (packed & 0x00F0) >> 4; truelight@1024: order.station = (packed & 0xFF00) >> 8; truelight@1024: order.next = NULL; 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@1024: FOR_ALL_ORDERS(order) { truelight@1024: if (order->type == OT_NOTHING) { truelight@1024: uint index = order->index; truelight@1024: memset(order, 0, sizeof(Order)); truelight@1024: order->index = index; truelight@1111: order->next = NULL; truelight@1024: return order; truelight@1024: } truelight@1024: } truelight@1024: 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: { truelight@1024: order->type = data.type; truelight@1024: order->flags = data.flags; truelight@1024: order->station = data.station; truelight@1024: } truelight@1024: truelight@1024: /** truelight@1024: * truelight@1024: * Add an order to the orderlist of a vehicle truelight@1024: * truelight@1024: * @param veh_sel First 16 bits are the ID of the vehicle. The next 16 are the selected order (if any) truelight@1024: * If the lastone is given, order will be inserted above thatone truelight@1024: * @param packed_order Packed order to insert truelight@1024: * truelight@1024: */ truelight@1024: int32 CmdInsertOrder(int x, int y, uint32 flags, uint32 veh_sel, uint32 packed_order) truelight@1024: { truelight@1024: Vehicle *v = GetVehicle(veh_sel & 0xFFFF); truelight@1024: int sel = veh_sel >> 16; truelight@1024: Order new_order = UnpackOrder(packed_order); truelight@1024: truelight@1024: if (sel > v->num_orders) truelight@1024: return_cmd_error(STR_EMPTY); truelight@1024: truelight@1024: if (IsOrderPoolFull()) truelight@1024: 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 truelight@1024: handle any more then this.. */ truelight@1024: if (v->num_orders >= 40) truelight@1024: return_cmd_error(STR_8832_TOO_MANY_ORDERS); truelight@1024: truelight@1024: /* For ships, make sure that the station is not too far away from the previous destination. */ truelight@0: if (v->type == VEH_Ship && IS_HUMAN_PLAYER(v->owner) && truelight@1024: sel != 0 && GetVehicleOrder(v, sel - 1)->type == OT_GOTO_STATION) { truelight@193: tron@555: int dist = GetTileDist( truelight@1024: GetStation(GetVehicleOrder(v, sel - 1)->station)->xy, truelight@919: GetStation(new_order.station)->xy 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@1024: Order *new; truelight@0: Vehicle *u; truelight@0: truelight@1024: 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) */ truelight@1024: Order *order = GetVehicleOrder(v, sel - 1); truelight@0: truelight@1024: if (order == NULL && GetVehicleOrder(v, sel) != 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: truelight@1024: u = GetFirstVehicleFromSharedList(v); truelight@1024: while (u != NULL) { truelight@1024: /* Increase amount of orders */ truelight@1024: u->num_orders++; truelight@1024: truelight@1024: /* If the orderlist was empty, assign it */ truelight@1024: if (u->orders == NULL) truelight@1024: 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 */ truelight@1024: if (sel <= 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: u = u->next_shared; 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: truelight@1024: /** truelight@1024: * truelight@1024: * Declone an order-list truelight@1024: * truelight@1024: */ truelight@0: static int32 DecloneOrder(Vehicle *dst, uint32 flags) truelight@0: { truelight@0: if (flags & DC_EXEC) { truelight@1024: /* Delete orders from vehicle */ truelight@1024: DeleteVehicleOrders(dst); truelight@193: truelight@1024: InvalidateVehicleOrder(dst); tron@588: RebuildVehicleLists(); truelight@0: } truelight@0: return 0; truelight@0: } truelight@0: truelight@1024: /** truelight@1024: * truelight@1024: * Delete an order from the orderlist of a vehicle truelight@1024: * truelight@1024: * @param vehicle_id The ID of the vehicle truelight@1024: * @param selected The order to delete truelight@1024: * truelight@0: */ truelight@1024: int32 CmdDeleteOrder(int x, int y, uint32 flags, uint32 vehicle_id, uint32 selected) truelight@0: { truelight@1024: Vehicle *v = GetVehicle(vehicle_id), *u; truelight@1024: uint sel = selected; truelight@1024: Order *order; truelight@0: truelight@1024: /* XXX -- Why is this here? :s */ truelight@0: _error_message = STR_EMPTY; truelight@1024: truelight@1024: /* If we did not select an order, we maybe want to de-clone the orders */ truelight@0: if (sel >= v->num_orders) truelight@0: return DecloneOrder(v, flags); truelight@0: truelight@1024: order = GetVehicleOrder(v, sel); truelight@1024: if (order == NULL) truelight@1024: return CMD_ERROR; truelight@0: truelight@1024: if (flags & DC_EXEC) { truelight@1024: if (GetVehicleOrder(v, sel - 1) == NULL) { truelight@1024: if (GetVehicleOrder(v, sel + 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 */ truelight@1024: order = GetVehicleOrder(v, sel + 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 { truelight@1024: GetVehicleOrder(v, sel - 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: truelight@1024: u = GetFirstVehicleFromSharedList(v); truelight@1024: while (u != NULL) { truelight@1024: u->num_orders--; truelight@1024: truelight@1024: if (sel < 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 truelight@1024: also set their orders to NULL */ truelight@1024: if (v->orders == NULL) truelight@1024: 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 truelight@1024: on his order list or not */ truelight@1024: if (sel == u->cur_order_index && truelight@1024: 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@1024: truelight@1024: u = u->next_shared; truelight@0: } tron@588: tron@588: RebuildVehicleLists(); truelight@0: } truelight@193: truelight@0: return 0; truelight@0: } truelight@0: truelight@1024: /** truelight@1024: * truelight@1024: * Goto next order of order-list truelight@1024: * truelight@1024: * @param vehicle_id The ID of the vehicle truelight@1024: * truelight@1024: */ truelight@1024: int32 CmdSkipOrder(int x, int y, uint32 flags, uint32 vehicle_id, uint32 not_used) truelight@0: { truelight@1024: Vehicle *v = GetVehicle(vehicle_id); celestar@1020: truelight@0: if (flags & DC_EXEC) { truelight@1024: /* Goto next order */ truelight@0: { truelight@0: byte b = v->cur_order_index + 1; truelight@1024: if (b >= v->num_orders) truelight@1024: b = 0; truelight@1024: truelight@0: v->cur_order_index = b; truelight@193: truelight@0: if (v->type == VEH_Train) truelight@0: v->u.rail.days_since_order_progr = 0; truelight@0: } truelight@0: truelight@1024: /* NON-stop flag is misused to see if a train is in a station that is truelight@1024: on his order list or not */ tron@555: if (v->current_order.type == OT_LOADING && truelight@1024: HASBIT(v->current_order.flags, OFB_NON_STOP)) { tron@555: v->current_order.flags = 0; tron@555: } 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: truelight@1024: /** truelight@1024: * truelight@1024: * Add an order to the orderlist of a vehicle truelight@1024: * truelight@1024: * @param veh_sel First 16 bits are the ID of the vehicle. The next 16 are the selected order (if any) truelight@1024: * If the lastone is given, order will be inserted above thatone truelight@1024: * @param mode Mode to change the order to truelight@1024: * truelight@0: */ truelight@1024: int32 CmdModifyOrder(int x, int y, uint32 flags, uint32 veh_sel, uint32 mode) truelight@0: { truelight@1024: Vehicle *v = GetVehicle(veh_sel & 0xFFFF); truelight@1024: byte sel = veh_sel >> 16; truelight@1024: Order *order; truelight@0: truelight@1024: /* Is it a valid order? */ truelight@0: if (sel >= v->num_orders) truelight@0: return CMD_ERROR; truelight@0: truelight@1024: order = GetVehicleOrder(v, sel); truelight@1024: if (order->type != OT_GOTO_STATION && truelight@1024: (order->type != OT_GOTO_DEPOT || mode == OFB_UNLOAD) && truelight@1024: (order->type != OT_GOTO_WAYPOINT || mode != OFB_NON_STOP)) truelight@0: return CMD_ERROR; truelight@0: truelight@0: if (flags & DC_EXEC) { truelight@1024: switch (mode) { truelight@1024: case OFB_FULL_LOAD: truelight@1024: TOGGLEBIT(order->flags, OFB_FULL_LOAD); truelight@1024: if (order->type != OT_GOTO_DEPOT) truelight@1024: 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; truelight@0: } truelight@0: truelight@1024: /* Update the windows, also for vehicles that share the same order list */ truelight@1024: { truelight@1024: Vehicle *u = GetFirstVehicleFromSharedList(v); truelight@1024: while (u != NULL) { truelight@1024: InvalidateVehicleOrder(u); truelight@1024: u = u->next_shared; truelight@1024: } truelight@0: } truelight@0: } truelight@193: truelight@0: return 0; truelight@0: } truelight@0: truelight@1024: /** truelight@1024: * truelight@1024: * Clone/share/copy an order-list of an other vehicle truelight@1024: * truelight@1024: * @param veh1_veh2 First 16 bits are of destination vehicle, last 16 of source vehicle truelight@1024: * @param mode Mode of cloning (CO_SHARE, CO_COPY, CO_UNSHARE) truelight@1024: * truelight@1024: */ truelight@1024: int32 CmdCloneOrder(int x, int y, uint32 flags, uint32 veh1_veh2, uint32 mode) truelight@1024: { truelight@1024: Vehicle *dst = GetVehicle(veh1_veh2 & 0xFFFF); truelight@0: truelight@1024: if (dst->type == 0 || dst->owner != _current_player) truelight@0: return CMD_ERROR; truelight@0: truelight@1024: switch(mode) { truelight@1024: case CO_SHARE: { truelight@1024: Vehicle *src = GetVehicle(veh1_veh2 >> 16); tron@588: truelight@1024: /* Sanity checks */ truelight@1024: if (src->type == 0 || src->owner != _current_player || 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: { truelight@1024: Vehicle *u = GetFirstVehicleFromSharedList(src); truelight@1024: while (u != NULL) { truelight@1024: if (u == dst) darkvater@29: return CMD_ERROR; truelight@1024: u = u->next_shared; 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; truelight@1024: if (src->next_shared != NULL) truelight@1024: 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: { truelight@1024: Vehicle *src = GetVehicle(veh1_veh2 >> 16); truelight@1024: int delta; truelight@1024: truelight@1024: /* Sanity checks */ truelight@1024: if (src->type == 0 || src->owner != _current_player || 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; truelight@1024: TileIndex required_dst; truelight@1024: truelight@1024: FOR_VEHICLE_ORDERS(src, order) { truelight@1024: if (order->type == OT_GOTO_STATION) { truelight@1024: const Station *st = GetStation(order->station); truelight@1024: required_dst = (dst->cargo_type == CT_PASSENGERS) ? st->bus_tile : st->lorry_tile; truelight@1024: /* This station has not the correct road-bay, so we can't copy! */ truelight@1024: if (!required_dst) 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: truelight@1024: case CO_UNSHARE: truelight@1024: return DecloneOrder(dst, flags); truelight@0: } truelight@0: truelight@0: return 0; truelight@0: } truelight@0: 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: */ truelight@0: void BackupVehicleOrders(Vehicle *v, BackuppedOrders *bak) truelight@0: { truelight@1024: bool shared = IsOrderListShared(v); 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 */ truelight@0: if ((v->string_id & 0xF800) != 0x7800) { truelight@0: bak->name[0] = 0; truelight@0: } else { truelight@0: GetName(v->string_id & 0x7FF, bak->name); truelight@0: } truelight@0: truelight@1024: /* If we have shared orders, store it on a special way */ truelight@1024: if (shared) { truelight@1024: Vehicle *u; truelight@1024: if (v->next_shared) truelight@1024: u = v->next_shared; truelight@1024: else truelight@1024: u = 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: truelight@1024: 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: */ truelight@0: void RestoreVehicleOrders(Vehicle *v, BackuppedOrders *bak) truelight@0: { tron@555: int i; truelight@0: truelight@1024: /* If we have a custom name, process that */ truelight@1024: if (bak->name[0] != 0) { truelight@0: strcpy((char*)_decode_parameters, bak->name); truelight@0: DoCommandP(0, v->index, 0, NULL, CMD_NAME_VEHICLE); truelight@0: } truelight@0: truelight@1024: /* Restore vehicle number and service interval */ truelight@1024: DoCommandP(0, v->index, bak->orderindex | (bak->service_interval << 16) , NULL, CMD_RESTORE_ORDER_INDEX); truelight@193: 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 truelight@1024: order number is one more than the current amount of orders, and because truelight@1024: in network the commands are queued before send, the second insert always truelight@1024: fails in test mode. By bypassing the test-mode, that no longer is a problem. */ truelight@1024: 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; truelight@0: } truelight@0: truelight@1024: /** truelight@1024: * truelight@1024: * Restore the current-order-index of a vehicle and sets service-interval truelight@1024: * truelight@1024: * @param vehicle_id The ID of the vehicle truelight@1024: * @param data First 16 bits are the current-order-index truelight@1024: * The last 16 bits are the service-interval truelight@1024: * truelight@0: */ truelight@1024: int32 CmdRestoreOrderIndex(int x, int y, uint32 flags, uint32 vehicle_id, uint32 data) truelight@0: { truelight@0: if (flags & DC_EXEC) { truelight@1024: Vehicle *v = GetVehicle(vehicle_id); truelight@1024: v->service_interval = data >> 16; truelight@1024: v->cur_order_index = data & 0xFFFF; truelight@0: } truelight@1024: truelight@0: return 0; truelight@0: } dominik@19: 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: */ celestar@1053: bool CheckOrders(uint data_a, uint data_b) dominik@19: { celestar@1053: Vehicle *v = GetVehicle(data_a); truelight@1024: /* Does the user wants us to check things? */ truelight@1024: if (_patches.order_review_system == 0) truelight@1024: return false; dominik@19: truelight@1024: /* Do nothing for crashed vehicles */ truelight@1024: if(v->vehstatus & VS_CRASHED) truelight@1024: return false; truelight@1024: truelight@1024: /* Do nothing for stopped vehicles if setting is '1' */ truelight@1024: if ( (_patches.order_review_system == 1) && (v->vehstatus & VS_STOPPED) ) truelight@1024: return false; truelight@1024: celestar@1053: /* do nothing we we're not the first vehicle in a share-chain */ celestar@1053: if (v->next_shared != NULL) celestar@1053: return false; celestar@1053: truelight@1024: /* Only check every 20 days, so that we don't flood the message log */ dominik@19: if ( ( ( v->day_counter % 20) == 0 ) && (v->owner == _local_player) ) { truelight@1024: int n_st, problem_type = -1; truelight@1024: const Order *order; truelight@1024: const Station *st; truelight@1024: int message = 0; dominik@55: truelight@1024: /* Check the order list */ dominik@55: n_st = 0; dominik@55: celestar@1053: /*if (data_b == OC_INIT) { celestar@1053: DEBUG(misc, 3) ("CheckOrder called in mode 0 (initiation mode) for %d", v->index); celestar@1053: } else { celestar@1053: DEBUG(misc, 3) ("CheckOrder called in mode 1 (validation mode) for %d", v->index); celestar@1053: }*/ truelight@1106: 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) { truelight@1024: TileIndex required_tile; truelight@1024: dominik@19: n_st++; truelight@1024: st = GetStation(order->station); truelight@1024: required_tile = GetStationTileForVehicle(v, st); truelight@1024: if (!required_tile) truelight@1024: problem_type = 3; dominik@19: } dominik@19: } dominik@19: truelight@1024: /* Check if the last and the first order are the same */ truelight@1024: if (v->num_orders > 1 && truelight@1024: v->orders->type == GetLastVehicleOrder(v)->type && truelight@1024: v->orders->flags == GetLastVehicleOrder(v)->flags && truelight@1024: v->orders->station == GetLastVehicleOrder(v)->station) dominik@55: problem_type = 2; truelight@193: truelight@1024: /* Do we only have 1 station in our order list? */ truelight@1024: if ((n_st < 2) && (problem_type == -1)) truelight@1024: problem_type = 0; truelight@193: truelight@1024: /* We don't have a problem */ celestar@1053: if (problem_type < 0) { celestar@1053: /*if (data_b == OC_INIT) { celestar@1053: DEBUG(misc, 3) ("CheckOrder mode 0: no problems found for %d", v->index); celestar@1053: } else { celestar@1053: DEBUG(misc, 3) ("CheckOrder mode 1: news item surpressed for %d", v->index); celestar@1053: }*/ truelight@1024: return false; celestar@1053: } celestar@1053: celestar@1053: /* we have a problem, are we're just in the validation process celestar@1053: so don't display an error message */ celestar@1053: if (data_b == OC_VALIDATE) { celestar@1053: /*DEBUG(misc, 3) ("CheckOrder mode 1: new item validated for %d", v->index);*/ celestar@1053: return true; celestar@1053: } truelight@193: dominik@55: message = (STR_TRAIN_HAS_TOO_FEW_ORDERS) + (((v->type) - VEH_Train) << 2) + problem_type; celestar@1053: /*DEBUG(misc, 3) ("Checkorder mode 0: Triggered News Item for %d", v->index);*/ truelight@193: truelight@1024: SetDParam(0, v->unitnumber); celestar@1053: AddValidatedNewsItem( dominik@55: message, truelight@1024: NEWS_FLAGS(NM_SMALL, NF_VIEWPORT | NF_VEHICLE, NT_ADVICE, 0), dominik@55: v->index, celestar@1053: OC_VALIDATE, //next time, just validate the orders celestar@1053: CheckOrders); dominik@19: } dominik@19: truelight@1024: return true; dominik@19: } truelight@1024: truelight@1024: /** truelight@1024: * truelight@1024: * Delete a destination (like station, waypoint, ..) from the orders of vehicles truelight@1024: * truelight@1024: * @param dest type and station has to be set. This order will be removed from all orders of vehicles truelight@1024: * truelight@1024: */ truelight@1024: void DeleteDestinationFromVehicleOrder(Order dest) truelight@1024: { truelight@1024: Vehicle *v; truelight@1024: Order *order; truelight@1024: bool need_invalidate; truelight@1024: truelight@1024: /* Go through all vehicles */ truelight@1024: FOR_ALL_VEHICLES(v) { truelight@1024: if (v->type == 0 || v->orders == NULL) truelight@1024: continue; truelight@1024: truelight@1024: /* Forget about this station if this station is removed */ truelight@1024: if (v->last_station_visited == dest.station && dest.type == OT_GOTO_STATION) truelight@1024: v->last_station_visited = 0xFFFF; truelight@1024: truelight@1024: /* Check the current order */ truelight@1024: if (v->current_order.type == dest.type && truelight@1024: v->current_order.station == dest.station) { truelight@1024: /* Mark the order as DUMMY */ truelight@1024: v->current_order.type = OT_DUMMY; truelight@1024: v->current_order.flags = 0; truelight@1024: InvalidateWindow(WC_VEHICLE_VIEW, v->index); truelight@1024: } truelight@1024: truelight@1024: /* Clear the order from the order-list */ truelight@1024: need_invalidate = false; truelight@1024: FOR_VEHICLE_ORDERS(v, order) { truelight@1024: if (order->type == dest.type && order->station == dest.station) { truelight@1024: /* Mark the order as DUMMY */ truelight@1024: order->type = OT_DUMMY; truelight@1024: order->flags = 0; truelight@1024: truelight@1024: need_invalidate = true; truelight@1024: } truelight@1024: } truelight@1024: truelight@1024: /* Only invalidate once, and if needed */ truelight@1024: if (need_invalidate) truelight@1024: 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@1024: Order *order, *cur; truelight@1024: 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 truelight@1024: vehicle which shares the same order-list, nothing will change. If truelight@1024: this is the last vehicle, the last line of the order-window truelight@1024: will change from Shared order list, to Order list, so it needs truelight@1024: 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: truelight@1024: order = NULL; truelight@1024: while (cur != NULL) { truelight@1024: if (order != NULL) { truelight@1024: order->type = OT_NOTHING; truelight@1024: order->next = NULL; truelight@1024: } truelight@1024: truelight@1024: order = cur; truelight@1024: cur = cur->next; truelight@1024: } truelight@1024: truelight@1024: if (order != NULL) { truelight@1024: order->type = OT_NOTHING; truelight@1024: order->next = NULL; 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: { truelight@1024: if (v->next_shared != NULL) truelight@1024: return true; truelight@1024: truelight@1024: if (v->prev_shared != NULL) truelight@1024: return true; truelight@1024: truelight@1024: return false; 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: */ truelight@1024: bool CheckForValidOrders(Vehicle *v) truelight@1024: { truelight@1024: const Order *order; truelight@1024: truelight@1024: FOR_VEHICLE_ORDERS(v, order) truelight@1024: if (order->type != OT_DUMMY) truelight@1024: return true; truelight@1024: truelight@1024: return false; truelight@1024: } truelight@1024: truelight@1024: void InitializeOrders(void) truelight@1024: { truelight@1024: Order *order; truelight@1024: int i; truelight@1024: truelight@1024: memset(&_orders, 0, sizeof(_orders[0]) * _orders_size); truelight@1024: truelight@1024: i = 0; truelight@1024: FOR_ALL_ORDERS(order) truelight@1024: order->index = i++; truelight@1024: truelight@1024: _backup_orders_tile = 0; truelight@1024: } truelight@1024: truelight@1024: static const byte _order_desc[] = { truelight@1024: SLE_VAR(Order,type, SLE_UINT8), truelight@1024: SLE_VAR(Order,flags, SLE_UINT8), truelight@1024: SLE_VAR(Order,station, SLE_UINT16), truelight@1024: SLE_REF(Order,next, REF_ORDER), truelight@1024: truelight@1024: // reserve extra space in savegame here. (currently 10 bytes) truelight@1024: SLE_CONDARR(NullStruct,null,SLE_FILE_U8 | SLE_VAR_NULL, 10, 5, 255), 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@1024: if (order->type != OT_NOTHING) { truelight@1024: SlSetArrayIndex(order->index); truelight@1024: SlObject(order, _order_desc); truelight@1024: } truelight@1024: } truelight@1024: } truelight@1024: tron@1093: static void Load_ORDR(void) truelight@1024: { truelight@1024: if (_sl.full_version <= 0x501) { truelight@1024: /* Version older than 0x502 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@1024: if (_sl.version < 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@1024: assert (len <= _orders_size); truelight@1024: truelight@1024: SlArray(orders, len, SLE_UINT16); truelight@1024: truelight@1024: for (i = 0; i < len; ++i) { truelight@1024: AssignOrder(GetOrder(i), UnpackVersion4Order(orders[i])); truelight@1024: } truelight@1024: } else if (_sl.full_version <= 0x501) { truelight@1024: uint32 orders[5000]; truelight@1024: truelight@1024: len /= sizeof(uint32); truelight@1024: assert (len <= _orders_size); truelight@1024: truelight@1024: SlArray(orders, len, SLE_UINT32); truelight@1024: truelight@1024: for (i = 0; i < len; ++i) { 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: truelight@1024: Vehicle one had order[0], and as long as order++.type was not truelight@1024: 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@1024: Order *order = GetOrder(index); truelight@1024: 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: };