From d275109e79c191dde34b71f07e5a39cfeb11fba7 Mon Sep 17 00:00:00 2001 From: truelight Date: Sat, 15 Jan 2005 19:06:22 +0000 Subject: [PATCH] (svn r1525) -Codechange: rewrote the _order_array, now it can be made dynamic. -Codechange: renamed all 'schedule' stuff to 'order(list)' -Codechange: moved all order-stuff to order_cmd.c / order.h -Codechange: vehicles that share orders are now linked to eachother with next_shared/prev_shared in Vehicle Developers: please use AssignOrder to assign data to an order. If not, you _WILL_ make the save-routine to assert! --- ai.c | 2 +- ai_new.c | 14 +- aircraft_cmd.c | 53 +-- aircraft_gui.c | 17 +- misc.c | 4 +- oldloader.c | 51 ++- order.h | 124 ++++++ order_cmd.c | 1059 +++++++++++++++++++++++++++++++++++------------- order_gui.c | 214 +++++----- rail_cmd.c | 5 +- roadveh_cmd.c | 44 +- saveload.c | 12 +- saveload.h | 10 +- ship_cmd.c | 53 ++- ship_gui.c | 23 +- station.h | 2 +- station_cmd.c | 7 +- train_cmd.c | 98 ++--- ttd.c | 1 + vehicle.c | 203 ++-------- vehicle.h | 132 +++--- vehicle_gui.c | 8 +- 22 files changed, 1280 insertions(+), 856 deletions(-) create mode 100644 order.h diff --git a/ai.c b/ai.c index 0bd26e46fc..3560ad6390 100644 --- a/ai.c +++ b/ai.c @@ -3618,7 +3618,7 @@ static void AiStateRemoveStation(Player *p) // Get a list of all stations that are in use by a vehicle memset(in_use, 0, sizeof(in_use)); - for (ord = _order_array; ord != _ptr_to_next_order; ++ord) { + FOR_ALL_ORDERS(ord) { if (ord->type == OT_GOTO_STATION) in_use[ord->station] = 1; } diff --git a/ai_new.c b/ai_new.c index 91219642e2..c209ab1ba5 100644 --- a/ai_new.c +++ b/ai_new.c @@ -502,14 +502,12 @@ static bool AiNew_CheckVehicleStation(Player *p, Station *st) { // Also check if we don't have already a lot of busses to this city... FOR_ALL_VEHICLES(v) { if (v->owner == _current_player) { - const Order *sched = v->schedule_ptr; - if (sched != NULL) { - for (; sched->type != OT_NOTHING; ++sched) { - if (sched->type == OT_GOTO_STATION && - GetStation(sched->station) == st) { - // This vehicle has this city in his list - count++; - } + const Order *order; + + FOR_VEHICLE_ORDERS(v, order) { + if (order->type == OT_GOTO_STATION && GetStation(order->station) == st) { + // This vehicle has this city in its list + count++; } } } diff --git a/aircraft_cmd.c b/aircraft_cmd.c index 4fa94a345f..c241155ac3 100644 --- a/aircraft_cmd.c +++ b/aircraft_cmd.c @@ -126,7 +126,7 @@ int32 CmdBuildAircraft(int x, int y, uint32 flags, uint32 p1, uint32 p2) // allocate 2 or 3 vehicle structs, depending on type if (!AllocateVehicles(vl, (avi->subtype & 1) == 0 ? 3 : 2) || - _ptr_to_next_order >= endof(_order_array)) + IsOrderPoolFull()) return_cmd_error(STR_00E1_TOO_MANY_VEHICLES_IN_GAME); unit_num = GetFreeUnitNumber(VEH_Aircraft); @@ -200,9 +200,6 @@ int32 CmdBuildAircraft(int x, int y, uint32 flags, uint32 p1, uint32 p2) _new_aircraft_id = v->index; - _ptr_to_next_order->type = OT_NOTHING; - _ptr_to_next_order->flags = 0; - v->schedule_ptr = _ptr_to_next_order++; // the AI doesn't click on a tile to build airplanes, so the below code will // never work. Therefore just assume the AI's planes always come from Hangar0 // On hold for NewAI @@ -490,7 +487,7 @@ static void CheckIfAircraftNeedsService(Vehicle *v) v->current_order.flags & OF_FULL_LOAD) return; - if (_patches.gotodepot && ScheduleHasDepotOrders(v->schedule_ptr)) + if (_patches.gotodepot && VehicleHasDepotOrders(v)) return; st = GetStation(v->current_order.station); @@ -510,13 +507,13 @@ static void CheckIfAircraftNeedsService(Vehicle *v) void InvalidateAircraftWindows(const Vehicle *v) { - const Order *o; + const Order *order; InvalidateWindow(WC_AIRCRAFT_LIST, v->owner); - for ( o = v->schedule_ptr; o->type != OT_NOTHING; o++) { - if (o->type == OT_GOTO_STATION ) { - InvalidateWindow(WC_AIRCRAFT_LIST, o->station << 16 | v->owner); + FOR_VEHICLE_ORDERS(v, order) { + if (order->type == OT_GOTO_STATION ) { + InvalidateWindow(WC_AIRCRAFT_LIST, (order->station << 16) | v->owner); } } } @@ -1024,21 +1021,9 @@ static void HandleAircraftSmoke(Vehicle *v) } } -// returns true if the vehicle does have valid orders -// false if none are valid -static bool CheckForValidOrders(Vehicle *v) -{ - int i; - for (i = 0; i < v->num_orders; i++) { - if( v->schedule_ptr[i].type != OT_DUMMY ) - return true; - } - return false; -} - static void ProcessAircraftOrder(Vehicle *v) { - Order order; + Order *order; // OT_GOTO_DEPOT, OT_LOADING if (v->current_order.type == OT_GOTO_DEPOT || @@ -1057,32 +1042,32 @@ static void ProcessAircraftOrder(Vehicle *v) if (v->cur_order_index >= v->num_orders) v->cur_order_index = 0; - order = v->schedule_ptr[v->cur_order_index]; + order = GetVehicleOrder(v, v->cur_order_index); - if (order.type == OT_NOTHING) { + if (order == NULL) { v->current_order.type = OT_NOTHING; v->current_order.flags = 0; return; } - if ( order.type == OT_DUMMY && !CheckForValidOrders(v)) + if (order->type == OT_DUMMY && !CheckForValidOrders(v)) CrashAirplane(v); - if (order.type == v->current_order.type && - order.flags == v->current_order.flags && - order.station == v->current_order.station) + if (order->type == v->current_order.type && + order->flags == v->current_order.flags && + order->station == v->current_order.station) return; - v->current_order = order; + v->current_order = *order; // orders are changed in flight, ensure going to the right station - if (order.type == OT_GOTO_STATION && v->u.air.state == FLYING) { + if (order->type == OT_GOTO_STATION && v->u.air.state == FLYING) { AircraftNextAirportPos_and_Order(v); - v->u.air.targetairport = order.station; + v->u.air.targetairport = order->station; } - InvalidateVehicleOrderWidget(v); - + InvalidateVehicleOrder(v); + InvalidateAircraftWindows(v); } @@ -1116,7 +1101,7 @@ static void HandleAircraftLoading(Vehicle *v, int mode) } } v->cur_order_index++; - InvalidateVehicleOrderWidget(v); + InvalidateVehicleOrder(v); } static void CrashAirplane(Vehicle *v) diff --git a/aircraft_gui.c b/aircraft_gui.c index a2f6fd79c4..31a081f27a 100644 --- a/aircraft_gui.c +++ b/aircraft_gui.c @@ -920,16 +920,13 @@ void ShowAircraftDepotWindow(uint tile) } } -static void DrawSmallSchedule(Vehicle *v, int x, int y) { - const Order *sched; - int sel; - Order ord; - int i = 0; +static void DrawSmallOrderList(Vehicle *v, int x, int y) { + const Order *order; + int sel, i = 0; - sched = v->schedule_ptr; sel = v->cur_order_index; - while ((ord = *sched++).type != OT_NOTHING) { + FOR_VEHICLE_ORDERS(v, order) { if (sel == 0) { _stringwidth_base = 0xE0; DoDrawString( "\xAF", x-6, y, 16); @@ -937,8 +934,8 @@ static void DrawSmallSchedule(Vehicle *v, int x, int y) { } sel--; - if (ord.type == OT_GOTO_STATION) { - SetDParam(0, ord.station); + if (order->type == OT_GOTO_STATION) { + SetDParam(0, order->station); DrawString(x, y, STR_A036, 0); y += 6; @@ -1052,7 +1049,7 @@ static void PlayerAircraftWndProc(Window *w, WindowEvent *e) DrawString(x + 19, y, STR_01AB, 0); } - DrawSmallSchedule(v, x + 136, y); + DrawSmallOrderList(v, x + 136, y); y += PLY_WND_PRC__SIZE_OF_ROW_BIG; } diff --git a/misc.c b/misc.c index da2ad80ee8..1e2a0024ab 100644 --- a/misc.c +++ b/misc.c @@ -150,6 +150,8 @@ void CSleep(int milliseconds) #endif } +void InitializeVehicles(); +void InitializeOrders(); void InitializeClearLand(); void InitializeRail(); void InitializeRailGui(); @@ -206,7 +208,7 @@ void InitializeGame() } InitializeVehicles(); - _backup_orders_tile = 0; + InitializeOrders(); InitNewsItemStructs(); InitializeLandscape(); diff --git a/oldloader.c b/oldloader.c index cdeafa6221..16c08d7448 100644 --- a/oldloader.c +++ b/oldloader.c @@ -752,6 +752,22 @@ static void FixDepot(Depot *n, OldDepot *o, int num) } while (n++,o++,--num); } +static void FixOrder(uint16 *o, int num) +{ + Order *order; + int i; + + for (i = 0; i < num; ++i) { + order = GetOrder(i); + AssignOrder(order, UnpackOldOrder(*o)); + /* Recover the next list */ + if (i > 0 && order->type != OT_NOTHING) + GetOrder(i - 1)->next = order; + + o++; + } +} + static void FixVehicle(OldVehicle *o, int num) { Vehicle *n; @@ -767,10 +783,9 @@ static void FixVehicle(OldVehicle *o, int num) n->subtype = o->subtype; if (o->schedule_ptr == 0xFFFFFFFF || o->schedule_ptr == 0) { - n->schedule_ptr = NULL; + n->orders = NULL; } else { - n->schedule_ptr = _order_array + REMAP_ORDER_IDX(o->schedule_ptr); - assert(n->schedule_ptr >= _order_array && n->schedule_ptr < _ptr_to_next_order); + n->orders = GetOrder(REMAP_ORDER_IDX(o->schedule_ptr)); } n->current_order.type = o->next_order & 0x0f; @@ -870,6 +885,31 @@ static void FixVehicle(OldVehicle *o, int num) break; } } while (i++,o++,--num); + + /* Check for shared orders, and link them correctly */ + { + Vehicle *v; + + FOR_ALL_VEHICLES(v) { + Vehicle *u; + + if (v->type == 0) + continue; + + FOR_ALL_VEHICLES_FROM(u, v->index + 1) { + if (u->type == 0) + continue; + + /* If a vehicle has the same orders, add the link to eachother + in both vehicles */ + if (v->orders == u->orders) { + v->next_shared = u; + u->prev_shared = v; + break; + } + } + } + } } static void FixSubsidy(Subsidy *n, OldSubsidy *o, int num) @@ -1447,15 +1487,12 @@ bool LoadOldSaveGame(const char *file) } } - for (i = 0; i < lengthof(m->order_list); ++i) - _order_array[i] = UnpackOldOrder(m->order_list[i]); - _ptr_to_next_order = _order_array + REMAP_ORDER_IDX(m->ptr_to_next_order); - FixTown(m->town_list, lengthof(m->town_list), m->town_name_type); FixIndustry(m->industries, lengthof(m->industries)); FixStation(m->stations, lengthof(m->stations)); FixDepot(_depots, m->depots, lengthof(m->depots)); + FixOrder(m->order_list, lengthof(m->order_list)); FixVehicle(m->vehicles, lengthof(m->vehicles)); FixSubsidy(_subsidies, m->subsidies, lengthof(m->subsidies)); diff --git a/order.h b/order.h new file mode 100644 index 0000000000..dc7da1fa7a --- /dev/null +++ b/order.h @@ -0,0 +1,124 @@ +#ifndef ORDER_H +#define ORDER_H + +/* Order types */ +enum { + OT_NOTHING = 0, + OT_GOTO_STATION = 1, + OT_GOTO_DEPOT = 2, + OT_LOADING = 3, + OT_LEAVESTATION = 4, + OT_DUMMY = 5, + OT_GOTO_WAYPOINT = 6 +}; + +/* Order flags -- please use OFB instead OF and use HASBIT/SETBIT/CLEARBIT */ +enum { + OF_UNLOAD = 0x2, + OF_FULL_LOAD = 0x4, // Also used when to force an aircraft into a depot + OF_NON_STOP = 0x8 +}; + +/* Order flags bits */ +enum { + OFB_UNLOAD = 1, + OFB_FULL_LOAD = 2, + OFB_NON_STOP = 3 +}; + +/* Possible clone options */ +enum { + CO_SHARE = 0, + CO_COPY = 1, + CO_UNSHARE = 2 +}; + +/* If you change this, keep in mind that it is saved on 3 places: + - Load_ORDR, all the global orders + - Vehicle -> current_order + - REF_SHEDULE (all REFs are currently limited to 16 bits!!) */ +typedef struct Order { + uint8 type; + uint8 flags; + uint16 station; + + struct Order *next; //! Pointer to next order. If NULL, end of list + + uint16 index; //! Index of the order, is not saved or anything, just for reference +} Order; + +typedef struct { + VehicleID clone; + byte orderindex; + Order order[41]; + uint16 service_interval; + char name[32]; +} BackuppedOrders; + +VARDEF TileIndex _backup_orders_tile; +VARDEF BackuppedOrders _backup_orders_data[1]; + +VARDEF Order _orders[5000]; +VARDEF uint32 _orders_size; + +static inline Order *GetOrder(uint index) +{ + assert(index < _orders_size); + return &_orders[index]; +} + +#define FOR_ALL_ORDERS_FROM(o, from) for(o = GetOrder(from); o != &_orders[_orders_size]; o++) +#define FOR_ALL_ORDERS(o) FOR_ALL_ORDERS_FROM(o, 0) + +#define FOR_VEHICLE_ORDERS(v, order) for (order = v->orders; order != NULL; order = order->next) + +static inline bool HasOrderPoolFree(uint amount) +{ + Order *order; + + FOR_ALL_ORDERS(order) + if (order->type == OT_NOTHING) + if (--amount == 0) + return true; + + return false; +} + +static inline bool IsOrderPoolFull() +{ + return !HasOrderPoolFree(1); +} + +/* Pack and unpack routines */ + +static inline uint32 PackOrder(const Order *order) +{ + return order->station << 16 | order->flags << 8 | order->type; +} + +static inline Order UnpackOrder(uint32 packed) +{ + Order order; + order.type = (packed & 0x000000FF); + order.flags = (packed & 0x0000FF00) >> 8; + order.station = (packed & 0xFFFF0000) >> 16; + order.next = NULL; + return order; +} + +/* Functions */ +void BackupVehicleOrders(Vehicle *v, BackuppedOrders *order); +void RestoreVehicleOrders(Vehicle *v, BackuppedOrders *order); +void DeleteDestinationFromVehicleOrder(Order dest); +void InvalidateVehicleOrder(const Vehicle *v); +bool VehicleHasDepotOrders(const Vehicle *v); +bool CheckOrders(const Vehicle *v); +void DeleteVehicleOrders(Vehicle *v); +bool IsOrderListShared(const Vehicle *v); +void AssignOrder(Order *order, Order data); +bool CheckForValidOrders(Vehicle *v); + +Order UnpackVersion4Order(uint16 packed); +Order UnpackOldOrder(uint16 packed); + +#endif /* ORDER_H */ diff --git a/order_cmd.c b/order_cmd.c index 7332b0513b..1e074bcd4f 100644 --- a/order_cmd.c +++ b/order_cmd.c @@ -6,27 +6,139 @@ #include "station.h" #include "player.h" #include "news.h" +#include "saveload.h" -/* p1 & 0xFFFF = vehicle - * p1 >> 16 = index in order list - * p2 = order command to insert +/** + * + * Unpacks a order from savegames made with TTD(Patch) + * */ -int32 CmdInsertOrder(int x, int y, uint32 flags, uint32 p1, uint32 p2) +Order UnpackOldOrder(uint16 packed) { - Vehicle *v = GetVehicle(p1 & 0xFFFF); - int sel = p1 >> 16; - Order new_order = UnpackOrder(p2); + Order order; + order.type = (packed & 0x000F); + order.flags = (packed & 0x00F0) >> 4; + order.station = (packed & 0xFF00) >> 8; + order.next = NULL; - if (sel > v->num_orders) return_cmd_error(STR_EMPTY); - if (_ptr_to_next_order == endof(_order_array)) return_cmd_error(STR_8831_NO_MORE_SPACE_FOR_ORDERS); - if (v->num_orders >= 40) return_cmd_error(STR_8832_TOO_MANY_ORDERS); + // Sanity check + // TTD stores invalid orders as OT_NOTHING with non-zero flags/station + if (order.type == OT_NOTHING && (order.flags != 0 || order.station != 0)) { + order.type = OT_DUMMY; + order.flags = 0; + } - // for ships, make sure that the station is not too far away from the previous destination. + return order; +} + +/** + * + * Unpacks a order from savegames with version 4 and lower + * + */ +Order UnpackVersion4Order(uint16 packed) +{ + Order order; + order.type = (packed & 0x000F); + order.flags = (packed & 0x00F0) >> 4; + order.station = (packed & 0xFF00) >> 8; + order.next = NULL; + return order; +} + +/** + * + * Updates the widgets of a vehicle which contains the order-data + * + */ +void InvalidateVehicleOrder(const Vehicle *v) +{ + InvalidateWindow(WC_VEHICLE_VIEW, v->index); + InvalidateWindow(WC_VEHICLE_ORDERS, v->index); +} + +/** + * + * Swap two orders + * + */ +static void SwapOrders(Order *order1, Order *order2) +{ + Order temp_order; + + temp_order = *order1; + *order1 = *order2; + *order2 = temp_order; +} + +/** + * + * Allocate a new order + * + * @return Order* if a free space is found, else NULL. + * + */ +static Order *AllocateOrder() +{ + Order *order; + + FOR_ALL_ORDERS(order) { + if (order->type == OT_NOTHING) { + uint index = order->index; + memset(order, 0, sizeof(Order)); + order->index = index; + return order; + } + } + + return NULL; +} + +/** + * + * Assign data to an order (from an other order) + * This function makes sure that the index is maintained correctly + * + */ +void AssignOrder(Order *order, Order data) +{ + order->type = data.type; + order->flags = data.flags; + order->station = data.station; +} + +/** + * + * Add an order to the orderlist of a vehicle + * + * @param veh_sel First 16 bits are the ID of the vehicle. The next 16 are the selected order (if any) + * If the lastone is given, order will be inserted above thatone + * @param packed_order Packed order to insert + * + */ +int32 CmdInsertOrder(int x, int y, uint32 flags, uint32 veh_sel, uint32 packed_order) +{ + Vehicle *v = GetVehicle(veh_sel & 0xFFFF); + int sel = veh_sel >> 16; + Order new_order = UnpackOrder(packed_order); + + if (sel > v->num_orders) + return_cmd_error(STR_EMPTY); + + if (IsOrderPoolFull()) + return_cmd_error(STR_8831_NO_MORE_SPACE_FOR_ORDERS); + + /* XXX - This limit is only here because the backuppedorders can't + handle any more then this.. */ + if (v->num_orders >= 40) + return_cmd_error(STR_8832_TOO_MANY_ORDERS); + + /* For ships, make sure that the station is not too far away from the previous destination. */ if (v->type == VEH_Ship && IS_HUMAN_PLAYER(v->owner) && - sel != 0 && v->schedule_ptr[sel - 1].type == OT_GOTO_STATION) { + sel != 0 && GetVehicleOrder(v, sel - 1)->type == OT_GOTO_STATION) { int dist = GetTileDist( - GetStation(v->schedule_ptr[sel - 1].station)->xy, + GetStation(GetVehicleOrder(v, sel - 1)->station)->xy, GetStation(new_order.station)->xy ); if (dist >= 130) @@ -34,101 +146,158 @@ int32 CmdInsertOrder(int x, int y, uint32 flags, uint32 p1, uint32 p2) } if (flags & DC_EXEC) { - Order *s1; - Order *s2; + Order *new; Vehicle *u; - s1 = &v->schedule_ptr[sel]; - s2 = _ptr_to_next_order++; - do s2[1] = s2[0]; while (--s2 >= s1); - *s1 = new_order; + new = AllocateOrder(); + AssignOrder(new, new_order); - s1 = v->schedule_ptr; + /* Create new order and link in list */ + if (v->orders == NULL) { + v->orders = new; + } else { + /* Try to get the previous item (we are inserting above the + selected) */ + Order *order = GetVehicleOrder(v, sel - 1); - FOR_ALL_VEHICLES(u) { - if (u->type != 0 && u->schedule_ptr != NULL) { - if (s1 < u->schedule_ptr) { - u->schedule_ptr++; - } else if (s1 == u->schedule_ptr) { // handle shared orders - u->num_orders++; - - if ((byte)sel <= u->cur_order_index) { - sel++; - if ((byte)sel < u->num_orders) - u->cur_order_index = sel; - } - InvalidateWindow(WC_VEHICLE_VIEW, u->index); - InvalidateWindow(WC_VEHICLE_ORDERS, u->index); - } + if (order == NULL && GetVehicleOrder(v, sel) != NULL) { + /* There is no previous item, so we are altering v->orders itself + But because the orders can be shared, we copy the info over + the v->orders, so we don't have to change the pointers of + all vehicles */ + SwapOrders(v->orders, new); + /* Now update the next pointers */ + v->orders->next = new; + } else if (order == NULL) { + /* 'sel' is a non-existing order, add him to the end */ + order = GetLastVehicleOrder(v); + order->next = new; + } else { + /* Put the new order in between */ + new->next = order->next; + order->next = new; } } + u = GetFirstVehicleFromSharedList(v); + while (u != NULL) { + /* Increase amount of orders */ + u->num_orders++; + + /* If the orderlist was empty, assign it */ + if (u->orders == NULL) + u->orders = v->orders; + + assert(v->orders == u->orders); + + /* If there is added an order before the current one, we need + to update the selected order */ + if (sel <= u->cur_order_index) { + uint cur = u->cur_order_index + 1; + /* Check if we don't go out of bound */ + if (cur < u->num_orders) + u->cur_order_index = cur; + } + /* Update any possible open window of the vehicle */ + InvalidateVehicleOrder(u); + + u = u->next_shared; + } + + /* Make sure to rebuild the whole list */ RebuildVehicleLists(); } return 0; } +/** + * + * Declone an order-list + * + */ static int32 DecloneOrder(Vehicle *dst, uint32 flags) { - if (_ptr_to_next_order == endof(_order_array)) - return_cmd_error(STR_8831_NO_MORE_SPACE_FOR_ORDERS); - if (flags & DC_EXEC) { - DeleteVehicleSchedule(dst); - - dst->num_orders = 0; - _ptr_to_next_order->type = OT_NOTHING; - _ptr_to_next_order->flags = 0; - dst->schedule_ptr = _ptr_to_next_order++; - - InvalidateWindow(WC_VEHICLE_ORDERS, dst->index); + /* Delete orders from vehicle */ + DeleteVehicleOrders(dst); + InvalidateVehicleOrder(dst); RebuildVehicleLists(); } return 0; } -/* p1 = vehicle - * p2 = sel +/** + * + * Delete an order from the orderlist of a vehicle + * + * @param vehicle_id The ID of the vehicle + * @param selected The order to delete + * */ -int32 CmdDeleteOrder(int x, int y, uint32 flags, uint32 p1, uint32 p2) +int32 CmdDeleteOrder(int x, int y, uint32 flags, uint32 vehicle_id, uint32 selected) { - Vehicle *v = GetVehicle(p1), *u; - uint sel = (uint)p2; + Vehicle *v = GetVehicle(vehicle_id), *u; + uint sel = selected; + Order *order; + /* XXX -- Why is this here? :s */ _error_message = STR_EMPTY; + + /* If we did not select an order, we maybe want to de-clone the orders */ if (sel >= v->num_orders) return DecloneOrder(v, flags); + order = GetVehicleOrder(v, sel); + if (order == NULL) + return CMD_ERROR; + if (flags & DC_EXEC) { - Order *s1 = &v->schedule_ptr[sel]; - - // copy all orders to get rid of the hole - do s1[0] = s1[1]; while (++s1 != _ptr_to_next_order); - _ptr_to_next_order--; - - s1 = v->schedule_ptr; - - FOR_ALL_VEHICLES(u) { - if (u->type != 0 && u->schedule_ptr != NULL) { - if (s1 < u->schedule_ptr) { - u->schedule_ptr--; - } else if (s1 == u->schedule_ptr) {// handle shared orders - u->num_orders--; - if ((byte)sel < u->cur_order_index) - u->cur_order_index--; - - if ((byte)sel == u->cur_order_index && - u->current_order.type == OT_LOADING && - u->current_order.flags & OF_NON_STOP) { - u->current_order.flags = 0; - } - - InvalidateWindow(WC_VEHICLE_VIEW, u->index); - InvalidateWindow(WC_VEHICLE_ORDERS, u->index); - } + if (GetVehicleOrder(v, sel - 1) == NULL) { + if (GetVehicleOrder(v, sel + 1) != NULL) { + /* First item, but not the last, so we need to alter v->orders + Because we can have shared order, we copy the data + from the next item over the deleted */ + order = GetVehicleOrder(v, sel + 1); + SwapOrders(v->orders, order); + } else { + /* Last item, so clean the list */ + v->orders = NULL; } + } else { + GetVehicleOrder(v, sel - 1)->next = order->next; + } + + /* Give the item free */ + order->type = OT_NOTHING; + + u = GetFirstVehicleFromSharedList(v); + while (u != NULL) { + u->num_orders--; + + if (sel < u->cur_order_index) + u->cur_order_index--; + + /* If we removed the last order, make sure the shared vehicles + also set their orders to NULL */ + if (v->orders == NULL) + u->orders = NULL; + + assert(v->orders == u->orders); + + /* NON-stop flag is misused to see if a train is in a station that is + on his order list or not */ + if (sel == u->cur_order_index && + u->current_order.type == OT_LOADING && + HASBIT(u->current_order.flags, OFB_NON_STOP)) { + u->current_order.flags = 0; + } + + /* Update any possible open window of the vehicle */ + InvalidateVehicleOrder(u); + + u = u->next_shared; } RebuildVehicleLists(); @@ -137,321 +306,651 @@ int32 CmdDeleteOrder(int x, int y, uint32 flags, uint32 p1, uint32 p2) return 0; } -/* p1 = vehicle */ -int32 CmdSkipOrder(int x, int y, uint32 flags, uint32 p1, uint32 p2) +/** + * + * Goto next order of order-list + * + * @param vehicle_id The ID of the vehicle + * + */ +int32 CmdSkipOrder(int x, int y, uint32 flags, uint32 vehicle_id, uint32 not_used) { - Vehicle *v = GetVehicle(p1); + Vehicle *v = GetVehicle(vehicle_id); if (flags & DC_EXEC) { + /* Goto next order */ { byte b = v->cur_order_index + 1; - if (b >= v->num_orders) b = 0; + if (b >= v->num_orders) + b = 0; + v->cur_order_index = b; if (v->type == VEH_Train) v->u.rail.days_since_order_progr = 0; } + /* NON-stop flag is misused to see if a train is in a station that is + on his order list or not */ if (v->current_order.type == OT_LOADING && - v->current_order.flags & OF_NON_STOP) { + HASBIT(v->current_order.flags, OFB_NON_STOP)) { v->current_order.flags = 0; } - InvalidateWindow(WC_VEHICLE_ORDERS, v->index); + InvalidateVehicleOrder(v); } - //we have an aircraft, they have a mini-schedule, so update them all + /* We have an aircraft/ship, they have a mini-schedule, so update them all */ if (v->type == VEH_Aircraft) InvalidateAircraftWindows(v); - - //same goes for ships if (v->type == VEH_Ship) InvalidateShipWindows(v); return 0; } -/* p1 = vehicle - * p2&0xFF = sel - * p2>>8 = mode - */ -int32 CmdModifyOrder(int x, int y, uint32 flags, uint32 p1, uint32 p2) -{ - Vehicle *v = GetVehicle(p1); - byte sel = (byte)p2; - Order *sched; +/** + * + * Add an order to the orderlist of a vehicle + * + * @param veh_sel First 16 bits are the ID of the vehicle. The next 16 are the selected order (if any) + * If the lastone is given, order will be inserted above thatone + * @param mode Mode to change the order to + * + */ +int32 CmdModifyOrder(int x, int y, uint32 flags, uint32 veh_sel, uint32 mode) +{ + Vehicle *v = GetVehicle(veh_sel & 0xFFFF); + byte sel = veh_sel >> 16; + Order *order; + + /* Is it a valid order? */ if (sel >= v->num_orders) return CMD_ERROR; - sched = &v->schedule_ptr[sel]; - if (sched->type != OT_GOTO_STATION && - (sched->type != OT_GOTO_DEPOT || (p2 >> 8) == 1) && - (sched->type != OT_GOTO_WAYPOINT || (p2 >> 8) != 2)) + order = GetVehicleOrder(v, sel); + if (order->type != OT_GOTO_STATION && + (order->type != OT_GOTO_DEPOT || mode == OFB_UNLOAD) && + (order->type != OT_GOTO_WAYPOINT || mode != OFB_NON_STOP)) return CMD_ERROR; if (flags & DC_EXEC) { - switch (p2 >> 8) { - case 0: // full load - sched->flags ^= OF_FULL_LOAD; - if (sched->type != OT_GOTO_DEPOT) sched->flags &= ~OF_UNLOAD; + switch (mode) { + case OFB_FULL_LOAD: + TOGGLEBIT(order->flags, OFB_FULL_LOAD); + if (order->type != OT_GOTO_DEPOT) + CLRBIT(order->flags, OFB_UNLOAD); break; - case 1: // unload - sched->flags ^= OF_UNLOAD; - sched->flags &= ~OF_FULL_LOAD; + case OFB_UNLOAD: + TOGGLEBIT(order->flags, OFB_UNLOAD); + CLRBIT(order->flags, OFB_FULL_LOAD); break; - case 2: // non stop - sched->flags ^= OF_NON_STOP; + case OFB_NON_STOP: + TOGGLEBIT(order->flags, OFB_NON_STOP); break; } - sched = v->schedule_ptr; - FOR_ALL_VEHICLES(v) { - if (v->schedule_ptr == sched) - InvalidateWindow(WC_VEHICLE_ORDERS, v->index); - } - - } - - return 0; -} - -// Clone an order -// p1 & 0xFFFF is destination vehicle -// p1 >> 16 is source vehicle - -// p2 is -// 0 - clone -// 1 - copy -// 2 - unclone - - -int32 CmdCloneOrder(int x, int y, uint32 flags, uint32 p1, uint32 p2) -{ - Vehicle *dst = GetVehicle(p1 & 0xFFFF); - - if (!(dst->type && dst->owner == _current_player)) - return CMD_ERROR; - - switch(p2) { - - // share vehicle orders? - case 0: { - Vehicle *src = GetVehicle(p1 >> 16); - - // sanity checks - if (!(src->owner == _current_player && dst->type == src->type && dst != src)) - return CMD_ERROR; - - // let's see what happens with road vehicles - if (src->type == VEH_Road) { - if (src->cargo_type != dst->cargo_type && (src->cargo_type == CT_PASSENGERS || dst->cargo_type == CT_PASSENGERS)) - return CMD_ERROR; - } - - if (flags & DC_EXEC) { - DeleteVehicleSchedule(dst); - dst->schedule_ptr = src->schedule_ptr; - dst->num_orders = src->num_orders; - - InvalidateWindow(WC_VEHICLE_ORDERS, src->index); - InvalidateWindow(WC_VEHICLE_ORDERS, dst->index); - - RebuildVehicleLists(); - } - break; - } - - // copy vehicle orders? - case 1: { - Vehicle *src = GetVehicle(p1 >> 16); - int delta; - - // sanity checks - if (!(src->owner == _current_player && dst->type == src->type && dst != src)) - return CMD_ERROR; - - // let's see what happens with road vehicles - if (src->type == VEH_Road) { - const Order *i; - TileIndex required_dst; - - for (i = src->schedule_ptr; i->type != OT_NOTHING; ++i) { - if (i->type == OT_GOTO_STATION) { - const Station *st = GetStation(i->station); - required_dst = (dst->cargo_type == CT_PASSENGERS) ? st->bus_tile : st->lorry_tile; - if ( !required_dst ) - return CMD_ERROR; - } + /* Update the windows, also for vehicles that share the same order list */ + { + Vehicle *u = GetFirstVehicleFromSharedList(v); + while (u != NULL) { + InvalidateVehicleOrder(u); + u = u->next_shared; } } - - // make sure there's orders available - delta = IsScheduleShared(dst) ? src->num_orders + 1 : src->num_orders - dst->num_orders; - if (delta > endof(_order_array) - _ptr_to_next_order) - return_cmd_error(STR_8831_NO_MORE_SPACE_FOR_ORDERS); - - if (flags & DC_EXEC) { - DeleteVehicleSchedule(dst); - dst->schedule_ptr = _ptr_to_next_order; - dst->num_orders = src->num_orders; - _ptr_to_next_order += src->num_orders + 1; - memcpy(dst->schedule_ptr, src->schedule_ptr, (src->num_orders + 1) * sizeof(Order)); - - InvalidateWindow(WC_VEHICLE_ORDERS, dst->index); - - RebuildVehicleLists(); - } - break; - } - - // declone vehicle orders? - case 2: return DecloneOrder(dst, flags); } return 0; } +/** + * + * Clone/share/copy an order-list of an other vehicle + * + * @param veh1_veh2 First 16 bits are of destination vehicle, last 16 of source vehicle + * @param mode Mode of cloning (CO_SHARE, CO_COPY, CO_UNSHARE) + * + */ +int32 CmdCloneOrder(int x, int y, uint32 flags, uint32 veh1_veh2, uint32 mode) +{ + Vehicle *dst = GetVehicle(veh1_veh2 & 0xFFFF); + + if (dst->type == 0 || dst->owner != _current_player) + return CMD_ERROR; + + switch(mode) { + case CO_SHARE: { + Vehicle *src = GetVehicle(veh1_veh2 >> 16); + + /* Sanity checks */ + if (src->type == 0 || src->owner != _current_player || dst->type != src->type || dst == src) + return CMD_ERROR; + + /* Trucks can't share orders with busses (and visa versa) */ + if (src->type == VEH_Road) { + if (src->cargo_type != dst->cargo_type && (src->cargo_type == CT_PASSENGERS || dst->cargo_type == CT_PASSENGERS)) + return CMD_ERROR; + } + + /* Is the vehicle already in the shared list? */ + { + Vehicle *u = GetFirstVehicleFromSharedList(src); + while (u != NULL) { + if (u == dst) + return CMD_ERROR; + u = u->next_shared; + } + } + + if (flags & DC_EXEC) { + /* If the destination vehicle had a OrderList, destroy it */ + DeleteVehicleOrders(dst); + + dst->orders = src->orders; + dst->num_orders = src->num_orders; + + /* Link this vehicle in the shared-list */ + dst->next_shared = src->next_shared; + dst->prev_shared = src; + if (src->next_shared != NULL) + src->next_shared->prev_shared = dst; + src->next_shared = dst; + + InvalidateVehicleOrder(dst); + InvalidateVehicleOrder(src); + + RebuildVehicleLists(); + } + } break; + + case CO_COPY: { + Vehicle *src = GetVehicle(veh1_veh2 >> 16); + int delta; + + /* Sanity checks */ + if (src->type == 0 || src->owner != _current_player || dst->type != src->type || dst == src) + return CMD_ERROR; + + /* Trucks can't copy all the orders from busses (and visa versa) */ + if (src->type == VEH_Road) { + const Order *order; + TileIndex required_dst; + + FOR_VEHICLE_ORDERS(src, order) { + if (order->type == OT_GOTO_STATION) { + const Station *st = GetStation(order->station); + required_dst = (dst->cargo_type == CT_PASSENGERS) ? st->bus_tile : st->lorry_tile; + /* This station has not the correct road-bay, so we can't copy! */ + if (!required_dst) + return CMD_ERROR; + } + } + } + + /* make sure there are orders available */ + delta = IsOrderListShared(dst) ? src->num_orders + 1 : src->num_orders - dst->num_orders; + if (!HasOrderPoolFree(delta)) + return_cmd_error(STR_8831_NO_MORE_SPACE_FOR_ORDERS); + + if (flags & DC_EXEC) { + const Order *order; + Order **order_dst; + + /* If the destination vehicle had a OrderList, destroy it */ + DeleteVehicleOrders(dst); + + order_dst = &dst->orders; + FOR_VEHICLE_ORDERS(src, order) { + *order_dst = AllocateOrder(); + AssignOrder(*order_dst, *order); + order_dst = &(*order_dst)->next; + } + + dst->num_orders = src->num_orders; + + InvalidateVehicleOrder(dst); + + RebuildVehicleLists(); + } + } break; + + case CO_UNSHARE: + return DecloneOrder(dst, flags); + } + + return 0; +} + +/** + * + * Backup a vehicle order-list, so you can replace a vehicle + * without loosing the order-list + * + */ void BackupVehicleOrders(Vehicle *v, BackuppedOrders *bak) { - Vehicle *u = IsScheduleShared(v); + bool shared = IsOrderListShared(v); - bak->orderindex = v->cur_order_index; + /* Save general info */ + bak->orderindex = v->cur_order_index; bak->service_interval = v->service_interval; + /* Safe custom string, if any */ if ((v->string_id & 0xF800) != 0x7800) { bak->name[0] = 0; } else { GetName(v->string_id & 0x7FF, bak->name); } - // stored shared orders in this special way? - if (u != NULL) { + /* If we have shared orders, store it on a special way */ + if (shared) { + Vehicle *u; + if (v->next_shared) + u = v->next_shared; + else + u = v->prev_shared; + bak->clone = u->index; } else { - Order *sched = v->schedule_ptr; - Order *os = bak->order; + /* Else copy the orders */ + Order *order, *dest; + dest = bak->order; + + /* We do not have shared orders */ bak->clone = INVALID_VEHICLE; - do { - *os++ = *sched++; - } while (sched->type != OT_NOTHING); - /* Make sure the last item is OT_NOTHING */ - os->type = OT_NOTHING; + /* Copy the orders */ + FOR_VEHICLE_ORDERS(v, order) { + *dest = *order; + dest++; + } + /* End the list with an OT_NOTHING */ + dest->type = OT_NOTHING; } } +/** + * + * Restore vehicle orders that are backupped via BackupVehicleOrders + * + */ void RestoreVehicleOrders(Vehicle *v, BackuppedOrders *bak) { int i; - if (bak->name[0]) { + /* If we have a custom name, process that */ + if (bak->name[0] != 0) { strcpy((char*)_decode_parameters, bak->name); DoCommandP(0, v->index, 0, NULL, CMD_NAME_VEHICLE); } - DoCommandP(0, v->index, bak->orderindex|(bak->service_interval<<16) , NULL, CMD_RESTORE_ORDER_INDEX); + /* Restore vehicle number and service interval */ + DoCommandP(0, v->index, bak->orderindex | (bak->service_interval << 16) , NULL, CMD_RESTORE_ORDER_INDEX); + /* If we had shared orders, recover that */ if (bak->clone != INVALID_VEHICLE) { - DoCommandP(0, v->index | bak->clone << 16, 0, NULL, CMD_CLONE_ORDER); + DoCommandP(0, v->index | (bak->clone << 16), 0, NULL, CMD_CLONE_ORDER); return; } - // CMD_NO_TEST_IF_IN_NETWORK is used here, because CMD_INSERT_ORDER checks if the - // order number is one more than the current amount of orders, and because - // in network the commands are queued before send, the second insert always - // fails in test mode. By bypassing the test-mode, that no longer is a problem. - for (i = 0; bak->order[i].type != OT_NOTHING; ++i) + /* CMD_NO_TEST_IF_IN_NETWORK is used here, because CMD_INSERT_ORDER checks if the + order number is one more than the current amount of orders, and because + in network the commands are queued before send, the second insert always + fails in test mode. By bypassing the test-mode, that no longer is a problem. */ + for (i = 0; bak->order[i].type != OT_NOTHING; i++) if (!DoCommandP(0, v->index + (i << 16), PackOrder(&bak->order[i]), NULL, CMD_INSERT_ORDER | CMD_NO_TEST_IF_IN_NETWORK)) break; } -/* p1 = vehicle - * upper 16 bits p2 = service_interval - * lower 16 bits p2 = cur_order_index +/** + * + * Restore the current-order-index of a vehicle and sets service-interval + * + * @param vehicle_id The ID of the vehicle + * @param data First 16 bits are the current-order-index + * The last 16 bits are the service-interval + * */ -int32 CmdRestoreOrderIndex(int x, int y, uint32 flags, uint32 p1, uint32 p2) +int32 CmdRestoreOrderIndex(int x, int y, uint32 flags, uint32 vehicle_id, uint32 data) { - // nonsense to update the windows, since, train rebought will have its window deleted if (flags & DC_EXEC) { - Vehicle *v = GetVehicle(p1); - v->service_interval = (uint16)(p2>>16); - v->cur_order_index = (byte)(p2&0xFFFF); + Vehicle *v = GetVehicle(vehicle_id); + v->service_interval = data >> 16; + v->cur_order_index = data & 0xFFFF; } + return 0; } -int CheckOrders(Vehicle *v) +/** + * + * Check the orders of a vehicle, to see if there are invalid orders and stuff + * + */ +bool CheckOrders(const Vehicle *v) { - if (!_patches.order_review_system) //User doesn't want things to be checked - return 0; + /* Does the user wants us to check things? */ + if (_patches.order_review_system == 0) + return false; + /* Do nothing for crashed vehicles */ if(v->vehstatus & VS_CRASHED) - return 0; + return false; + /* Do nothing for stopped vehicles if setting is '1' */ if ( (_patches.order_review_system == 1) && (v->vehstatus & VS_STOPPED) ) - return 0; + return false; - /* only check every 20 days, so that we don't flood the message log */ + /* Only check every 20 days, so that we don't flood the message log */ if ( ( ( v->day_counter % 20) == 0 ) && (v->owner == _local_player) ) { - Order order; - Order old_order; - int i, n_st, problem_type = -1; - Station *st; - int message=0; - TileIndex required_tile=-1; + int n_st, problem_type = -1; + const Order *order; + const Station *st; + int message = 0; - /* check the order list */ - order = v->schedule_ptr[0]; + /* Check the order list */ n_st = 0; - old_order.type = OT_NOTHING; - old_order.flags = 0; - for (i = 0; order.type != OT_NOTHING; i++) { - order = v->schedule_ptr[i]; - if (order.type == old_order.type && - order.flags == old_order.flags && - order.station == old_order.station) { - problem_type = 2; - break; - } - if (order.type == OT_DUMMY) { + FOR_VEHICLE_ORDERS(v, order) { + /* Dummy order? */ + if (order->type == OT_DUMMY) { problem_type = 1; break; } - if (order.type == OT_GOTO_STATION /*&& (order != old_order) */) { - //I uncommented this in order not to get two error messages - //when two identical entries are in the list + /* Does station have a load-bay for this vehicle? */ + if (order->type == OT_GOTO_STATION) { + TileIndex required_tile; + n_st++; - st = GetStation(order.station); - required_tile = GetStationTileForVehicle(v,st); - if (!required_tile) problem_type = 3; + st = GetStation(order->station); + required_tile = GetStationTileForVehicle(v, st); + if (!required_tile) + problem_type = 3; } - old_order = order; //store the old order } - //Now, check the last and the first order - //as the last order is the end of order marker, jump back 2 - if (i > 2 && - v->schedule_ptr[0].type == v->schedule_ptr[i - 2].type && - v->schedule_ptr[0].flags == v->schedule_ptr[i - 2].flags && - v->schedule_ptr[0].station == v->schedule_ptr[i - 2].station) + /* Check if the last and the first order are the same */ + if (v->num_orders > 1 && + v->orders->type == GetLastVehicleOrder(v)->type && + v->orders->flags == GetLastVehicleOrder(v)->flags && + v->orders->station == GetLastVehicleOrder(v)->station) problem_type = 2; - if ( (n_st < 2) && (problem_type == -1) ) problem_type = 0; + /* Do we only have 1 station in our order list? */ + if ((n_st < 2) && (problem_type == -1)) + problem_type = 0; - SetDParam(0, v->unitnumber); + /* We don't have a problem */ + if (problem_type < 0) + return false; message = (STR_TRAIN_HAS_TOO_FEW_ORDERS) + (((v->type) - VEH_Train) << 2) + problem_type; - if (problem_type < 0) return 0; - + SetDParam(0, v->unitnumber); AddNewsItem( message, - NEWS_FLAGS(NM_SMALL, NF_VIEWPORT|NF_VEHICLE, NT_ADVICE, 0), + NEWS_FLAGS(NM_SMALL, NF_VIEWPORT | NF_VEHICLE, NT_ADVICE, 0), v->index, 0); } - // End of order check - return 1; + return true; } + +/** + * + * Delete a destination (like station, waypoint, ..) from the orders of vehicles + * + * @param dest type and station has to be set. This order will be removed from all orders of vehicles + * + */ +void DeleteDestinationFromVehicleOrder(Order dest) +{ + Vehicle *v; + Order *order; + bool need_invalidate; + + /* Go through all vehicles */ + FOR_ALL_VEHICLES(v) { + if (v->type == 0 || v->orders == NULL) + continue; + + /* Forget about this station if this station is removed */ + if (v->last_station_visited == dest.station && dest.type == OT_GOTO_STATION) + v->last_station_visited = 0xFFFF; + + /* Check the current order */ + if (v->current_order.type == dest.type && + v->current_order.station == dest.station) { + /* Mark the order as DUMMY */ + v->current_order.type = OT_DUMMY; + v->current_order.flags = 0; + InvalidateWindow(WC_VEHICLE_VIEW, v->index); + } + + /* Clear the order from the order-list */ + need_invalidate = false; + FOR_VEHICLE_ORDERS(v, order) { + if (order->type == dest.type && order->station == dest.station) { + /* Mark the order as DUMMY */ + order->type = OT_DUMMY; + order->flags = 0; + + need_invalidate = true; + } + } + + /* Only invalidate once, and if needed */ + if (need_invalidate) + InvalidateWindow(WC_VEHICLE_ORDERS, v->index); + } +} + +/** + * + * Checks if a vehicle has a GOTO_DEPOT in his order list + * + * @return True if this is true (lol ;)) + * + */ +bool VehicleHasDepotOrders(const Vehicle *v) +{ + const Order *order; + + FOR_VEHICLE_ORDERS(v, order) { + if (order->type == OT_GOTO_DEPOT) + return true; + } + + return false; +} + +/** + * + * Delete all orders from a vehicle + * + */ +void DeleteVehicleOrders(Vehicle *v) +{ + Order *order, *cur; + + /* If we have a shared order-list, don't delete the list, but just + remove our pointer */ + if (IsOrderListShared(v)) { + const Vehicle *u = v; + + v->orders = NULL; + v->num_orders = 0; + + /* Unlink ourself */ + if (v->prev_shared != NULL) { + v->prev_shared->next_shared = v->next_shared; + u = v->prev_shared; + } + if (v->next_shared != NULL) { + v->next_shared->prev_shared = v->prev_shared; + u = v->next_shared; + } + v->prev_shared = NULL; + v->next_shared = NULL; + + /* We only need to update this-one, because if there is a third + vehicle which shares the same order-list, nothing will change. If + this is the last vehicle, the last line of the order-window + will change from Shared order list, to Order list, so it needs + an update */ + InvalidateVehicleOrder(u); + return; + } + + /* Remove the orders */ + cur = v->orders; + v->orders = NULL; + v->num_orders = 0; + + order = NULL; + while (cur != NULL) { + if (order != NULL) { + order->type = OT_NOTHING; + order->next = NULL; + } + + order = cur; + cur = cur->next; + } + + if (order != NULL) { + order->type = OT_NOTHING; + order->next = NULL; + } +} + +/** + * + * Check if we share our orders with an other vehicle + * + * @return Returns the vehicle who has the same order + * + */ +bool IsOrderListShared(const Vehicle *v) +{ + if (v->next_shared != NULL) + return true; + + if (v->prev_shared != NULL) + return true; + + return false; +} + +/** + * + * Check if a vehicle has any valid orders + * + * @return false if there are no valid orders + * + */ +bool CheckForValidOrders(Vehicle *v) +{ + const Order *order; + + FOR_VEHICLE_ORDERS(v, order) + if (order->type != OT_DUMMY) + return true; + + return false; +} + +void InitializeOrders(void) +{ + Order *order; + int i; + + memset(&_orders, 0, sizeof(_orders[0]) * _orders_size); + + i = 0; + FOR_ALL_ORDERS(order) + order->index = i++; + + _backup_orders_tile = 0; +} + +static const byte _order_desc[] = { + SLE_VAR(Order,type, SLE_UINT8), + SLE_VAR(Order,flags, SLE_UINT8), + SLE_VAR(Order,station, SLE_UINT16), + SLE_REF(Order,next, REF_ORDER), + + // reserve extra space in savegame here. (currently 10 bytes) + SLE_CONDARR(NullStruct,null,SLE_FILE_U8 | SLE_VAR_NULL, 10, 5, 255), + SLE_END() +}; + +static void Save_ORDR() +{ + Order *order; + + FOR_ALL_ORDERS(order) { + if (order->type != OT_NOTHING) { + SlSetArrayIndex(order->index); + SlObject(order, _order_desc); + } + } +} + +static void Load_ORDR() +{ + if (_sl.full_version <= 0x501) { + /* Version older than 0x502 did not have a ->next pointer. Convert them + (in the old days, the orderlist was 5000 items big) */ + uint len = SlGetFieldLength(); + uint i; + + if (_sl.version < 5) { + /* Pre-version 5 had an other layout for orders + (uint16 instead of uint32) */ + uint16 orders[5000]; + + len /= sizeof(uint16); + assert (len <= _orders_size); + + SlArray(orders, len, SLE_UINT16); + + for (i = 0; i < len; ++i) { + AssignOrder(GetOrder(i), UnpackVersion4Order(orders[i])); + } + } else if (_sl.full_version <= 0x501) { + uint32 orders[5000]; + + len /= sizeof(uint32); + assert (len <= _orders_size); + + SlArray(orders, len, SLE_UINT32); + + for (i = 0; i < len; ++i) { + AssignOrder(GetOrder(i), UnpackOrder(orders[i])); + } + } + + /* Update all the next pointer */ + for (i = 1; i < len; ++i) { + /* The orders were built like this: + Vehicle one had order[0], and as long as order++.type was not + OT_NOTHING, it was part of the order-list of that vehicle */ + if (GetOrder(i)->type != OT_NOTHING) + GetOrder(i - 1)->next = GetOrder(i); + } + } else { + int index; + + while ((index = SlIterateArray()) != -1) { + Order *order = GetOrder(index); + + SlObject(order, _order_desc); + } + } +} + +const ChunkHandler _order_chunk_handlers[] = { + { 'ORDR', Save_ORDR, Load_ORDR, CH_ARRAY | CH_LAST}, +}; diff --git a/order_gui.c b/order_gui.c index 9023f3febf..6699e62d9b 100644 --- a/order_gui.c +++ b/order_gui.c @@ -14,32 +14,23 @@ static int OrderGetSel(Window *w) { Vehicle *v = GetVehicle(w->window_number); - const Order *sched = v->schedule_ptr; int num = WP(w,order_d).sel; - int count = 0; - if (num == 0) - return 0; + if (num < 0 || num > v->num_orders) + return v->num_orders; - while (sched->type != OT_NOTHING) { - sched++; - count++; - if (--num == 0) - break; - } - - return count; + return num; } static void DrawOrdersWindow(Window *w) { - Vehicle *v; - int num, sel; - const Order *sched; - Order ord; - int y, i; + const Vehicle *v; + const Order *order; StringID str; - bool shared_schedule; + int sel; + int y, i; + bool shared_orders; + byte color; v = GetVehicle(w->window_number); @@ -57,40 +48,41 @@ static void DrawOrdersWindow(Window *w) w->disabled_state |= 1 << 6; } - shared_schedule = IsScheduleShared(v) != NULL; + shared_orders = IsOrderListShared(v); - sched = v->schedule_ptr; - num=0; - while (sched->type != OT_NOTHING) { - sched++; - num++; - } - - if ((uint)num + shared_schedule <= (uint)WP(w,order_d).sel) + if ((uint)v->num_orders + shared_orders <= (uint)WP(w,order_d).sel) SETBIT(w->disabled_state, 5); /* delete */ - if (num == 0) + if (v->num_orders == 0) SETBIT(w->disabled_state, 4); /* skip */ - SetVScrollCount(w, num+1); + SetVScrollCount(w, v->num_orders + 1); sel = OrderGetSel(w); + SetDParam(2, STR_8827_FULL_LOAD); - SetDParam(2,STR_8827_FULL_LOAD); - switch (v->schedule_ptr[sel].type) { - case OT_GOTO_STATION: - break; - case OT_GOTO_DEPOT: - SETBIT(w->disabled_state, 9); /* unload */ - SetDParam(2,STR_SERVICE); - break; + order = GetVehicleOrder(v, sel); - case OT_GOTO_WAYPOINT: - SETBIT(w->disabled_state, 8); /* full load */ - SETBIT(w->disabled_state, 9); /* unload */ - break; + if (order != NULL) { + switch (order->type) { + case OT_GOTO_STATION: + break; + case OT_GOTO_DEPOT: + SETBIT(w->disabled_state, 9); /* unload */ + SetDParam(2,STR_SERVICE); + break; - default: + case OT_GOTO_WAYPOINT: + SETBIT(w->disabled_state, 8); /* full load */ + SETBIT(w->disabled_state, 9); /* unload */ + break; + + default: + SETBIT(w->disabled_state, 6); /* nonstop */ + SETBIT(w->disabled_state, 8); /* full load */ + SETBIT(w->disabled_state, 9); /* unload */ + } + } else { SETBIT(w->disabled_state, 6); /* nonstop */ SETBIT(w->disabled_state, 8); /* full load */ SETBIT(w->disabled_state, 9); /* unload */ @@ -102,61 +94,62 @@ static void DrawOrdersWindow(Window *w) y = 15; - i = 0; - for(;;) { - str = ((byte)v->cur_order_index == i) ? STR_8805 : STR_8804; + i = w->vscroll.pos; + order = GetVehicleOrder(v, i); + while (order != NULL) { + str = (v->cur_order_index == i) ? STR_8805 : STR_8804; - ord = v->schedule_ptr[i]; + if (i - w->vscroll.pos < 6) { + SetDParam(1, 6); - if ( (uint)(i - w->vscroll.pos) < 6) { - - if (ord.type == OT_NOTHING) { - str = shared_schedule ? STR_END_OF_SHARED_ORDERS : STR_882A_END_OF_ORDERS; - } else { - SetDParam(1, 6); - - if (ord.type == OT_GOTO_STATION) { - SetDParam(1, STR_8806_GO_TO + (ord.flags >> 1)); - SetDParam(2, ord.station); - } else if (ord.type == OT_GOTO_DEPOT) { - StringID s = STR_NULL; - if (v->type == VEH_Aircraft) { - s = STR_GO_TO_AIRPORT_HANGAR; - SetDParam(2, ord.station); - } else { - SetDParam(2, _depots[ord.station].town_index); - switch (v->type) { - case VEH_Train: s = STR_880E_GO_TO_TRAIN_DEPOT; break; - case VEH_Road: s = STR_9038_GO_TO_ROADVEH_DEPOT; break; - case VEH_Ship: s = STR_GO_TO_SHIP_DEPOT; break; - } - } - if (v->type == VEH_Train && ord.flags & OF_NON_STOP) s += 2; - if (ord.flags & OF_FULL_LOAD) ++s; /* XXX service */ - SetDParam(1, s); - } else if (ord.type == OT_GOTO_WAYPOINT) { - SetDParam(2, ord.station); - SetDParam(1, (ord.flags & OF_NON_STOP) ? STR_GO_NON_STOP_TO_WAYPOINT : STR_GO_TO_WAYPOINT); - } - } - { - byte color = (i == WP(w,order_d).sel) ? 0xC : 0x10; - SetDParam(0, i+1); - if (ord.type != OT_DUMMY) { - DrawString(2, y, str, color); + if (order->type == OT_GOTO_STATION) { + SetDParam(1, STR_8806_GO_TO + (order->flags >> 1)); + SetDParam(2, order->station); + } else if (order->type == OT_GOTO_DEPOT) { + StringID s = STR_NULL; + if (v->type == VEH_Aircraft) { + s = STR_GO_TO_AIRPORT_HANGAR; + SetDParam(2, order->station); } else { - SetDParam(1, STR_INVALID_ORDER); - SetDParam(2, ord.station); - DrawString(2, y, str, color); + SetDParam(2, _depots[order->station].town_index); + switch (v->type) { + case VEH_Train: s = STR_880E_GO_TO_TRAIN_DEPOT; break; + case VEH_Road: s = STR_9038_GO_TO_ROADVEH_DEPOT; break; + case VEH_Ship: s = STR_GO_TO_SHIP_DEPOT; break; + } } + if (v->type == VEH_Train && order->flags & OF_NON_STOP) + s += 2; + + if (order->flags & OF_FULL_LOAD) + s++; /* XXX service */ + + SetDParam(1, s); + } else if (order->type == OT_GOTO_WAYPOINT) { + SetDParam(1, (order->flags & OF_NON_STOP) ? STR_GO_NON_STOP_TO_WAYPOINT : STR_GO_TO_WAYPOINT); + SetDParam(2, order->station); + } + + color = (i == WP(w,order_d).sel) ? 0xC : 0x10; + SetDParam(0, i + 1); + if (order->type != OT_DUMMY) { + DrawString(2, y, str, color); + } else { + SetDParam(1, STR_INVALID_ORDER); + SetDParam(2, order->station); + DrawString(2, y, str, color); } y += 10; } i++; + order = order->next; + } - if (ord.type == OT_NOTHING) - break; + if (i - w->vscroll.pos < 6) { + str = shared_orders ? STR_END_OF_SHARED_ORDERS : STR_882A_END_OF_ORDERS; + color = (i == WP(w,order_d).sel) ? 0xC : 0x10; + DrawString(2, y, str, color); } } @@ -317,12 +310,6 @@ static void OrdersPlaceObj(Vehicle *v, uint tile, Window *w) } } -enum OrderFlags { - FULL_LOAD = 0, - UNLOAD = 1, - NON_STOP = 2 -}; - static void OrderClick_Goto(Window *w, Vehicle *v) { InvalidateWidget(w, 7); @@ -337,27 +324,27 @@ static void OrderClick_Goto(Window *w, Vehicle *v) static void OrderClick_FullLoad(Window *w, Vehicle *v) { - DoCommandP(v->tile, v->index, OrderGetSel(w) | (FULL_LOAD << 8), NULL, CMD_MODIFY_ORDER | CMD_MSG(STR_8835_CAN_T_MODIFY_THIS_ORDER)); + DoCommandP(v->tile, v->index + (OrderGetSel(w) << 16), OFB_FULL_LOAD, NULL, CMD_MODIFY_ORDER | CMD_MSG(STR_8835_CAN_T_MODIFY_THIS_ORDER)); } static void OrderClick_Unload(Window *w, Vehicle *v) { - DoCommandP(v->tile, v->index, OrderGetSel(w) | (UNLOAD << 8), NULL, CMD_MODIFY_ORDER | CMD_MSG(STR_8835_CAN_T_MODIFY_THIS_ORDER)); -} - -static void OrderClick_Skip(Window *w, Vehicle *v) -{ - DoCommandP(v->tile,v->index, 0, NULL, CMD_SKIP_ORDER); -} - -static void OrderClick_Delete(Window *w, Vehicle *v) -{ - DoCommandP(v->tile,v->index, OrderGetSel(w), NULL, CMD_DELETE_ORDER | CMD_MSG(STR_8834_CAN_T_DELETE_THIS_ORDER)); + DoCommandP(v->tile, v->index + (OrderGetSel(w) << 16), OFB_UNLOAD, NULL, CMD_MODIFY_ORDER | CMD_MSG(STR_8835_CAN_T_MODIFY_THIS_ORDER)); } static void OrderClick_Nonstop(Window *w, Vehicle *v) { - DoCommandP(v->tile, v->index, OrderGetSel(w) | (NON_STOP << 8), NULL, CMD_MODIFY_ORDER | CMD_MSG(STR_8835_CAN_T_MODIFY_THIS_ORDER)); + DoCommandP(v->tile, v->index + (OrderGetSel(w) << 16), OFB_NON_STOP, NULL, CMD_MODIFY_ORDER | CMD_MSG(STR_8835_CAN_T_MODIFY_THIS_ORDER)); +} + +static void OrderClick_Skip(Window *w, Vehicle *v) +{ + DoCommandP(v->tile, v->index, 0, NULL, CMD_SKIP_ORDER); +} + +static void OrderClick_Delete(Window *w, Vehicle *v) +{ + DoCommandP(v->tile, v->index, OrderGetSel(w), NULL, CMD_DELETE_ORDER | CMD_MSG(STR_8834_CAN_T_DELETE_THIS_ORDER)); } typedef void OnButtonClick(Window *w, Vehicle *v); @@ -399,18 +386,18 @@ static void OrdersWndProc(Window *w, WindowEvent *e) sel += w->vscroll.pos; - if (_ctrl_pressed && sel < v->num_orders) { // watch out for schedule_ptr overflow - Order ord = v->schedule_ptr[sel]; + if (_ctrl_pressed && sel < v->num_orders) { + Order *ord = GetVehicleOrder(v, sel); int xy = 0; - switch (ord.type) { + switch (ord->type) { case OT_GOTO_STATION: /* station order */ - xy = GetStation(ord.station)->xy ; + xy = GetStation(ord->station)->xy ; break; case OT_GOTO_DEPOT: /* goto depot order */ - xy = _depots[ord.station].xy; + xy = _depots[ord->station].xy; break; case OT_GOTO_WAYPOINT: /* goto waypoint order */ - xy = _waypoints[ord.station].xy; + xy = _waypoints[ord->station].xy; } if (xy) @@ -447,7 +434,6 @@ static void OrdersWndProc(Window *w, WindowEvent *e) case 9: /* unload button */ OrderClick_Unload(w, v); break; - } } break; @@ -473,7 +459,7 @@ static void OrdersWndProc(Window *w, WindowEvent *e) case WE_RCLICK: { Vehicle *v = GetVehicle(w->window_number); if (e->click.widget != 8) break; - if (v->schedule_ptr[OrderGetSel(w)].type == OT_GOTO_DEPOT) + if (GetVehicleOrder(v, OrderGetSel(w))->type == OT_GOTO_DEPOT) GuiShowTooltips(STR_SERVICE_HINT); else GuiShowTooltips(STR_8857_MAKE_THE_HIGHLIGHTED_ORDER); diff --git a/rail_cmd.c b/rail_cmd.c index d75404e69b..5e42ced3a4 100644 --- a/rail_cmd.c +++ b/rail_cmd.c @@ -768,10 +768,11 @@ static void DoDeleteWaypoint(Waypoint *cp) { Order order; cp->xy = 0; + order.type = OT_GOTO_WAYPOINT; - order.flags = 0; order.station = cp - _waypoints; - DeleteCommandFromVehicleSchedule(order); + DeleteDestinationFromVehicleOrder(order); + if (~cp->town_or_string & 0xC000) DeleteName(cp->town_or_string); RedrawWaypointSign(cp); } diff --git a/roadveh_cmd.c b/roadveh_cmd.c index 0e8d808c2f..957561bdda 100644 --- a/roadveh_cmd.c +++ b/roadveh_cmd.c @@ -122,7 +122,7 @@ int32 CmdBuildRoadVeh(int x, int y, uint32 flags, uint32 p1, uint32 p2) return cost; v = AllocateVehicle(); - if (v == NULL || _ptr_to_next_order >= endof(_order_array)) + if (v == NULL || IsOrderPoolFull()) return_cmd_error(STR_00E1_TOO_MANY_VEHICLES_IN_GAME); /* find the first free roadveh id */ @@ -172,9 +172,6 @@ int32 CmdBuildRoadVeh(int x, int y, uint32 flags, uint32 p1, uint32 p2) _new_roadveh_id = v->index; v->string_id = STR_SV_ROADVEH_NAME; - _ptr_to_next_order->type = OT_NOTHING; - _ptr_to_next_order->flags = 0; - v->schedule_ptr = _ptr_to_next_order++; v->service_interval = _patches.servint_roadveh; @@ -554,11 +551,11 @@ static void HandleBrokenRoadVeh(Vehicle *v) static void ProcessRoadVehOrder(Vehicle *v) { - Order order; - Station *st; + const Order *order; + const Station *st; if (v->current_order.type >= OT_GOTO_DEPOT && v->current_order.type <= OT_LEAVESTATION) { - // Let a depot order in the schedule interrupt. + // Let a depot order in the orderlist interrupt. if (v->current_order.type != OT_GOTO_DEPOT || !(v->current_order.flags & OF_UNLOAD)) return; @@ -573,34 +570,35 @@ static void ProcessRoadVehOrder(Vehicle *v) if (v->cur_order_index >= v->num_orders) v->cur_order_index = 0; - order = v->schedule_ptr[v->cur_order_index]; + order = GetVehicleOrder(v, v->cur_order_index); - if (order.type == OT_NOTHING) { + if (order == NULL) { v->current_order.type = OT_NOTHING; v->current_order.flags = 0; v->dest_tile = 0; return; } - if (order.type == v->current_order.type && - order.flags == v->current_order.flags && - order.station == v->current_order.station) + if (order->type == v->current_order.type && + order->flags == v->current_order.flags && + order->station == v->current_order.station) return; - v->current_order = order; + v->current_order = *order; v->dest_tile = 0; - if (order.type == OT_GOTO_STATION) { - if (order.station == v->last_station_visited) + if (order->type == OT_GOTO_STATION) { + if (order->station == v->last_station_visited) v->last_station_visited = 0xFFFF; - st = GetStation(order.station); - v->dest_tile = v->cargo_type==CT_PASSENGERS ? st->bus_tile : st->lorry_tile; - } else if (order.type == OT_GOTO_DEPOT) { - v->dest_tile = _depots[order.station].xy; + + st = GetStation(order->station); + v->dest_tile = v->cargo_type == CT_PASSENGERS ? st->bus_tile : st->lorry_tile; + } else if (order->type == OT_GOTO_DEPOT) { + v->dest_tile = _depots[order->station].xy; } - InvalidateVehicleOrderWidget(v); + InvalidateVehicleOrder(v); } static void HandleRoadVehLoading(Vehicle *v) @@ -634,7 +632,7 @@ static void HandleRoadVehLoading(Vehicle *v) } v->cur_order_index++; - InvalidateVehicleOrderWidget(v); + InvalidateVehicleOrder(v); } static void StartRoadVehSound(Vehicle *v) @@ -1390,7 +1388,7 @@ void RoadVehEnterDepot(Vehicle *v) v->current_order.type = OT_DUMMY; v->current_order.flags = 0; - // Part of the schedule? + // Part of the orderlist? if (t.flags & OF_UNLOAD) { v->cur_order_index++; } else if (t.flags & OF_FULL_LOAD) { @@ -1436,7 +1434,7 @@ static void CheckIfRoadVehNeedsService(Vehicle *v) if (v->vehstatus & VS_STOPPED) return; - if (_patches.gotodepot && ScheduleHasDepotOrders(v->schedule_ptr)) + if (_patches.gotodepot && VehicleHasDepotOrders(v)) return; // Don't interfere with a depot visit scheduled by the user, or a diff --git a/saveload.c b/saveload.c index bfba0ff7b7..4d44f4d3d9 100644 --- a/saveload.c +++ b/saveload.c @@ -8,7 +8,7 @@ enum { SAVEGAME_MAJOR_VERSION = 5, - SAVEGAME_MINOR_VERSION = 1, + SAVEGAME_MINOR_VERSION = 2, SAVEGAME_LOADABLE_VERSION = (SAVEGAME_MAJOR_VERSION << 8) + SAVEGAME_MINOR_VERSION }; @@ -869,6 +869,7 @@ static void UninitWriteZlib() extern const ChunkHandler _misc_chunk_handlers[]; extern const ChunkHandler _player_chunk_handlers[]; extern const ChunkHandler _veh_chunk_handlers[]; +extern const ChunkHandler _order_chunk_handlers[]; extern const ChunkHandler _town_chunk_handlers[]; extern const ChunkHandler _sign_chunk_handlers[]; extern const ChunkHandler _station_chunk_handlers[]; @@ -880,6 +881,7 @@ extern const ChunkHandler _animated_tile_chunk_handlers[]; static const ChunkHandler * const _chunk_handlers[] = { _misc_chunk_handlers, _veh_chunk_handlers, + _order_chunk_handlers, _industry_chunk_handlers, _economy_chunk_handlers, _engine_chunk_handlers, @@ -912,9 +914,7 @@ static uint ReferenceToInt(void *v, uint t) case REF_VEHICLE: return ((Vehicle *)v)->index + 1; case REF_STATION: return ((Station *)v)->index + 1; case REF_TOWN: return ((Town *)v)->index + 1; - - case REF_SCHEDULE: - return ((byte*)v - (byte*)_order_array) / sizeof(_order_array[0]) + 1; + case REF_ORDER: return ((Order *)v)->index + 1; default: NOT_REACHED(); @@ -935,13 +935,11 @@ void *IntToReference(uint r, uint t) return NULL; switch (t) { + case REF_ORDER: return GetOrder(r - 1); case REF_VEHICLE: return GetVehicle(r - 1); case REF_STATION: return GetStation(r - 1); case REF_TOWN: return GetTown(r - 1); - case REF_SCHEDULE: - return (byte*)_order_array + (r - 1) * sizeof(_order_array[0]); - case REF_VEHICLE_OLD: { /* Old vehicles were saved differently: invalid vehicle was 0xFFFF, and the index was not - 1.. correct for this */ diff --git a/saveload.h b/saveload.h index 1592525907..86d41f1c53 100644 --- a/saveload.h +++ b/saveload.h @@ -70,11 +70,11 @@ typedef struct { extern SaverLoader _sl; enum { - REF_SCHEDULE = 0, - REF_VEHICLE = 1, - REF_STATION = 2, - REF_TOWN = 3, - REF_VEHICLE_OLD = 4, + REF_ORDER = 0, + REF_VEHICLE = 1, + REF_STATION = 2, + REF_TOWN = 3, + REF_VEHICLE_OLD = 4 }; diff --git a/ship_cmd.c b/ship_cmd.c index 3ed827fa12..161939be27 100644 --- a/ship_cmd.c +++ b/ship_cmd.c @@ -23,13 +23,13 @@ static byte GetTileShipTrackStatus(uint tile) { void InvalidateShipWindows(const Vehicle *v) { - const Order *o; + const Order *order; InvalidateWindow(WC_SHIPS_LIST, v->owner); - for ( o = v->schedule_ptr; o->type != OT_NOTHING; o++) { - if (o->type == OT_GOTO_STATION ) { - InvalidateWindow(WC_SHIPS_LIST, o->station << 16 | v->owner); + FOR_VEHICLE_ORDERS(v, order) { + if (order->type == OT_GOTO_STATION ) { + InvalidateWindow(WC_SHIPS_LIST, (order->station << 16) | v->owner); } } } @@ -113,7 +113,7 @@ static void CheckIfShipNeedsService(Vehicle *v) v->current_order.flags & OF_FULL_LOAD) return; - if (_patches.gotodepot && ScheduleHasDepotOrders(v->schedule_ptr)) + if (_patches.gotodepot && VehicleHasDepotOrders(v)) return; i = FindClosestShipDepot(v); @@ -215,8 +215,7 @@ static const TileIndexDiffC _dock_offs[] = { static void ProcessShipOrder(Vehicle *v) { - Order order; - Station *st; + const Order *order; if (v->current_order.type >= OT_GOTO_DEPOT && v->current_order.type <= OT_LEAVESTATION) { @@ -235,36 +234,39 @@ static void ProcessShipOrder(Vehicle *v) if (v->cur_order_index >= v->num_orders) v->cur_order_index = 0; - order = v->schedule_ptr[v->cur_order_index]; + order = GetVehicleOrder(v, v->cur_order_index); - if (order.type == OT_NOTHING) { - v->current_order.type = OT_NOTHING; + if (order == NULL) { + v->current_order.type = OT_NOTHING; v->current_order.flags = 0; v->dest_tile = 0; return; } - if (order.type == v->current_order.type && - order.flags == v->current_order.flags && - order.station == v->current_order.station) + if (order->type == v->current_order.type && + order->flags == v->current_order.flags && + order->station == v->current_order.station) return; - v->current_order = order; + v->current_order = *order; - if (order.type == OT_GOTO_STATION) { - if (order.station == v->last_station_visited) + if (order->type == OT_GOTO_STATION) { + const Station *st; + + if (order->station == v->last_station_visited) v->last_station_visited = 0xFFFF; - st = GetStation(order.station); + st = GetStation(order->station); if (st->dock_tile != 0) { v->dest_tile = TILE_ADD(st->dock_tile, ToTileIndexDiff(_dock_offs[_map5[st->dock_tile]-0x4B])); } - } else if (order.type == OT_GOTO_DEPOT) { - v->dest_tile = _depots[order.station].xy; + } else if (order->type == OT_GOTO_DEPOT) { + v->dest_tile = _depots[order->station].xy; } else { v->dest_tile = 0; } - InvalidateVehicleOrderWidget(v); + + InvalidateVehicleOrder(v); InvalidateShipWindows(v); } @@ -301,7 +303,7 @@ static void HandleShipLoading(Vehicle *v) } v->cur_order_index++; - InvalidateVehicleOrderWidget(v); + InvalidateVehicleOrder(v); } static void UpdateShipDeltaXY(Vehicle *v, int dir) @@ -692,7 +694,7 @@ static void ShipController(Vehicle *v) v->last_station_visited = v->current_order.station; - /* Process station in the schedule. Don't do that for buoys (HVOT_BUOY) */ + /* Process station in the orderlist. Don't do that for buoys (HVOT_BUOY) */ st = GetStation(v->current_order.station); if (!(st->had_vehicle_of_type & HVOT_BUOY) && (st->facilities & FACIL_DOCK)) { /* ugly, ugly workaround for problem with ships able to drop off cargo at wrong stations */ @@ -711,7 +713,7 @@ static void ShipController(Vehicle *v) v->current_order.type = OT_LEAVESTATION; v->current_order.flags = 0; v->cur_order_index++; - InvalidateVehicleOrderWidget(v); + InvalidateVehicleOrder(v); } goto else_end; } @@ -830,7 +832,7 @@ int32 CmdBuildShip(int x, int y, uint32 flags, uint32 p1, uint32 p2) return value; v = AllocateVehicle(); - if (v == NULL || _ptr_to_next_order >= endof(_order_array) || + if (v == NULL || IsOrderPoolFull() || (unit_num = GetFreeUnitNumber(VEH_Ship)) > _patches.max_ships) return_cmd_error(STR_00E1_TOO_MANY_VEHICLES_IN_GAME); @@ -871,9 +873,6 @@ int32 CmdBuildShip(int x, int y, uint32 flags, uint32 p1, uint32 p2) v->string_id = STR_SV_SHIP_NAME; v->u.ship.state = 0x80; - _ptr_to_next_order->type = OT_NOTHING; - _ptr_to_next_order->flags = 0; - v->schedule_ptr = _ptr_to_next_order++; v->service_interval = _patches.servint_ships; v->date_of_last_service = _date; diff --git a/ship_gui.c b/ship_gui.c index f7c54a10fe..92b6cffd00 100644 --- a/ship_gui.c +++ b/ship_gui.c @@ -863,15 +863,14 @@ void ShowShipDepotWindow(uint tile) } -static void DrawSmallShipSchedule(Vehicle *v, int x, int y) { - Order *sched; - int sel; - Station *st; - int i = 0; +static void DrawSmallOrderList(Vehicle *v, int x, int y) { + const Order *order; + int sel, i = 0; sel = v->cur_order_index; + order = v->orders; - for (sched = v->schedule_ptr; sched->type != OT_NOTHING; ++sched) { + while (order != NULL) { if (sel == 0) { _stringwidth_base = 0xE0; DoDrawString( "\xAF", x-6, y, 16); @@ -879,11 +878,9 @@ static void DrawSmallShipSchedule(Vehicle *v, int x, int y) { } sel--; - if (sched->type == OT_GOTO_STATION) { - st = GetStation(sched->station); - - if (!(st->had_vehicle_of_type & HVOT_BUOY)) { - SetDParam(0, sched->station); + if (order->type == OT_GOTO_STATION) { + if (!(GetStation(order->station)->had_vehicle_of_type & HVOT_BUOY)) { + SetDParam(0, order->station); DrawString(x, y, STR_A036, 0); y += 6; @@ -891,6 +888,8 @@ static void DrawSmallShipSchedule(Vehicle *v, int x, int y) { break; } } + + order = order->next; } } @@ -998,7 +997,7 @@ static void PlayerShipsWndProc(Window *w, WindowEvent *e) DrawString(x + 12, y, STR_01AB, 0); } - DrawSmallShipSchedule(v, x + 138, y); + DrawSmallOrderList(v, x + 138, y); y += PLY_WND_PRC__SIZE_OF_ROW_BIG; } diff --git a/station.h b/station.h index 17a0bb4da1..34e48b91e3 100644 --- a/station.h +++ b/station.h @@ -88,7 +88,7 @@ enum { void ModifyStationRatingAround(TileIndex tile, byte owner, int amount, uint radius); -TileIndex GetStationTileForVehicle(Vehicle *v, Station *st); +TileIndex GetStationTileForVehicle(const Vehicle *v, const Station *st); void ShowStationViewWindow(int station); void UpdateAllStationVirtCoord(); diff --git a/station_cmd.c b/station_cmd.c index 29b23855cf..9ee23292b0 100644 --- a/station_cmd.c +++ b/station_cmd.c @@ -94,7 +94,7 @@ static Station *GetStationAround(uint tile, int w, int h, int closest_station) return (closest_station == -1) ? NULL : GetStation(closest_station); } -TileIndex GetStationTileForVehicle(Vehicle *v, Station *st) +TileIndex GetStationTileForVehicle(const Vehicle *v, const Station *st) { switch (v->type) { case VEH_Train: return st->train_tile; @@ -2257,10 +2257,11 @@ static void DeleteStation(Station *st) index = st->index; DeleteWindowById(WC_STATION_VIEW, index); + order.type = OT_GOTO_STATION; - order.flags = 0; order.station = index; - DeleteCommandFromVehicleSchedule(order); + DeleteDestinationFromVehicleOrder(order); + DeleteSubsidyWithStation(index); } diff --git a/train_cmd.c b/train_cmd.c index e11656e2d1..228d1c18e0 100644 --- a/train_cmd.c +++ b/train_cmd.c @@ -395,7 +395,7 @@ int32 CmdBuildRailVehicle(int x, int y, uint32 flags, uint32 p1, uint32 p2) if (!(flags & DC_QUERY_COST)) { v = AllocateVehicle(); - if (v == NULL || _ptr_to_next_order >= endof(_order_array)) + if (v == NULL || IsOrderPoolFull()) return_cmd_error(STR_00E1_TOO_MANY_VEHICLES_IN_GAME); unit_num = GetFreeUnitNumber(VEH_Train); @@ -417,24 +417,13 @@ int32 CmdBuildRailVehicle(int x, int y, uint32 flags, uint32 p1, uint32 p2) v->u.rail.track = 0x80; v->u.rail.first_engine = 0xffff; v->vehstatus = VS_HIDDEN | VS_STOPPED | VS_DEFPAL; -// v->subtype = 0; v->spritenum = rvi->image_index; v->cargo_type = rvi->cargo_type; v->cargo_cap = rvi->capacity; v->max_speed = rvi->max_speed; -// v->cargo_count = 0; v->value = value; -// v->day_counter = 0; -// v->current_order = 0; -// v->next_station = 0; -// v->load_unload_time_rem = 0; -// v->progress = 0; -// v->targetairport = 0; -// v->crash_anim_pos = 0; v->last_station_visited = 0xFFFF; v->dest_tile = 0; -// v->profit_last_year = 0; -// v->profit_this_year = 0; v->engine_type = (byte)p1; e = &_engines[p1]; @@ -444,23 +433,10 @@ int32 CmdBuildRailVehicle(int x, int y, uint32 flags, uint32 p1, uint32 p2) v->max_age = e->lifelength * 366; v->string_id = STR_SV_TRAIN_NAME; -// v->cur_speed = 0; -// v->subspeed = 0; v->u.rail.railtype = e->railtype; _new_train_id = v->index; -// v->cur_order_index = 0; -// v->num_orders = 0; - - _ptr_to_next_order->type = OT_NOTHING; - _ptr_to_next_order->flags = 0; - v->schedule_ptr = _ptr_to_next_order++; -// v->next_in_chain = 0xffff; -// v->next = NULL; v->service_interval = _patches.servint_trains; -// v->breakdown_ctr = 0; -// v->breakdowns_since_last_service = 0; -// v->unk4D = 0; v->date_of_last_service = _date; v->build_year = _cur_year; v->type = VEH_Train; @@ -680,12 +656,9 @@ int32 CmdMoveRailVehicle(int x, int y, uint32 flags, uint32 p1, uint32 p2) // move the train to an empty line. for locomotives, we set the type to 0. for wagons, 4. if (is_loco) { if (src->subtype != 0) { - // setting the type to 0 also involves setting up the schedule_ptr field. + // setting the type to 0 also involves setting up the orders field. src->subtype = 0; - assert(src->schedule_ptr == NULL); - _ptr_to_next_order->type = OT_NOTHING; - _ptr_to_next_order->flags = 0; - src->schedule_ptr = _ptr_to_next_order++; + assert(src->orders == NULL); src->num_orders = 0; } dst_head = src; @@ -695,9 +668,9 @@ int32 CmdMoveRailVehicle(int x, int y, uint32 flags, uint32 p1, uint32 p2) src->u.rail.first_engine = 0xffff; } else { if (src->subtype == 0) { - // the vehicle was previously a loco. need to free the schedule list and delete vehicle windows etc. + // the vehicle was previously a loco. need to free the order list and delete vehicle windows etc. DeleteWindowById(WC_VEHICLE_VIEW, src->index); - DeleteVehicleSchedule(src); + DeleteVehicleOrders(src); } src->subtype = 2; @@ -1644,13 +1617,13 @@ bad:; static bool ProcessTrainOrder(Vehicle *v) { - Order order; + const Order *order; bool result; // These are un-interruptible if (v->current_order.type >= OT_GOTO_DEPOT && v->current_order.type <= OT_LEAVESTATION) { - // Let a depot order in the schedule interrupt. + // Let a depot order in the orderlist interrupt. if (v->current_order.type != OT_GOTO_DEPOT || !(v->current_order.flags & OF_UNLOAD)) return false; @@ -1676,10 +1649,11 @@ static bool ProcessTrainOrder(Vehicle *v) // Get the current order if (v->cur_order_index >= v->num_orders) v->cur_order_index = 0; - order = v->schedule_ptr[v->cur_order_index]; + + order = GetVehicleOrder(v, v->cur_order_index); // If no order, do nothing. - if (order.type == OT_NOTHING) { + if (order == NULL) { v->current_order.type = OT_NOTHING; v->current_order.flags = 0; v->dest_tile = 0; @@ -1687,31 +1661,37 @@ static bool ProcessTrainOrder(Vehicle *v) } // If it is unchanged, keep it. - if (order.type == v->current_order.type && - order.flags == v->current_order.flags && - order.station == v->current_order.station) + if (order->type == v->current_order.type && + order->flags == v->current_order.flags && + order->station == v->current_order.station) return false; // Otherwise set it, and determine the destination tile. - v->current_order = order; + v->current_order = *order; v->dest_tile = 0; result = false; - if (order.type == OT_GOTO_STATION) { - if (order.station == v->last_station_visited) - v->last_station_visited = 0xFFFF; - v->dest_tile = GetStation(order.station)->xy; - result = CheckReverseTrain(v); - } else if (order.type == OT_GOTO_DEPOT) { - v->dest_tile = _depots[order.station].xy; - result = CheckReverseTrain(v); - } else if (order.type == OT_GOTO_WAYPOINT) { - v->dest_tile = _waypoints[order.station].xy; - result = CheckReverseTrain(v); + switch (order->type) { + case OT_GOTO_STATION: + if (order->station == v->last_station_visited) + v->last_station_visited = 0xFFFF; + v->dest_tile = GetStation(order->station)->xy; + result = CheckReverseTrain(v); + break; + + case OT_GOTO_DEPOT: + v->dest_tile = _depots[order->station].xy; + result = CheckReverseTrain(v); + break; + + case OT_GOTO_WAYPOINT: + v->dest_tile = _waypoints[order->station].xy; + result = CheckReverseTrain(v); + break; } - InvalidateVehicleOrderWidget(v); + InvalidateVehicleOrder(v); return result; } @@ -1771,7 +1751,7 @@ static void HandleTrainLoading(Vehicle *v, bool mode) v->u.rail.days_since_order_progr = 0; v->cur_order_index++; - InvalidateVehicleOrderWidget(v); + InvalidateVehicleOrder(v); } static int UpdateTrainSpeed(Vehicle *v) @@ -2669,7 +2649,7 @@ void TrainEnterDepot(Vehicle *v, uint tile) v->current_order.type = OT_DUMMY; v->current_order.flags = 0; - if (t.flags & OF_UNLOAD) { // Part of the schedule? + if (t.flags & OF_UNLOAD) { // Part of the orderlist? v->u.rail.days_since_order_progr = 0; v->cur_order_index++; } else if (t.flags & OF_FULL_LOAD) { // User initiated? @@ -2701,7 +2681,7 @@ static void CheckIfTrainNeedsService(Vehicle *v) if (v->vehstatus & VS_STOPPED) return; - if (_patches.gotodepot && ScheduleHasDepotOrders(v->schedule_ptr)) + if (_patches.gotodepot && VehicleHasDepotOrders(v)) return; // Don't interfere with a depot visit scheduled by the user, or a @@ -2834,11 +2814,3 @@ void InitializeTrains() { _age_cargo_skip_counter = 1; } - -int ScheduleHasDepotOrders(const Order *schedule) -{ - for (; schedule->type != OT_NOTHING; schedule++) - if (schedule->type == OT_GOTO_DEPOT) - return true; - return false; -} diff --git a/ttd.c b/ttd.c index 82dbd4e9c8..df0d7a5e4c 100644 --- a/ttd.c +++ b/ttd.c @@ -496,6 +496,7 @@ static void InitializeDynamicVariables(void) _industry_sort = NULL; _sign_size = lengthof(_sign_list); + _orders_size = lengthof(_orders); } static void UnInitializeDynamicVariables(void) diff --git a/vehicle.c b/vehicle.c index 318bfdc57e..746b724d3d 100644 --- a/vehicle.c +++ b/vehicle.c @@ -33,24 +33,6 @@ bool VehicleNeedsService(const Vehicle *v) (v->date_of_last_service + v->service_interval < _date); } -Order UnpackOldOrder(uint16 packed) -{ - Order order; - order.type = (packed & 0x000f); - order.flags = (packed & 0x00f0) >> 4; - order.station = (packed & 0xff00) >> 8; - - // Sanity check - // TTD stores invalid orders as OT_NOTHING with non-zero flags/station - if (order.type == OT_NOTHING && (order.flags != 0 || order.station != 0)) { - order.type = OT_DUMMY; - order.flags = 0; - } - - return order; -} - - void VehicleInTheWayErrMsg(Vehicle *v) { StringID id; @@ -177,11 +159,6 @@ void AfterLoadVehicles() if (v->subtype == 0) UpdateTrainAcceleration(v); } - -#if defined(_DEBUG) - if (!(v->schedule_ptr == NULL || (v->schedule_ptr >= _order_array && v->schedule_ptr < _ptr_to_next_order))) - v->schedule_ptr = NULL; -#endif } } @@ -196,7 +173,7 @@ static Vehicle *InitializeVehicle(Vehicle *v) memset(v, 0, sizeof(Vehicle)); v->index = index; - assert(v->schedule_ptr == NULL); + assert(v->orders == NULL); v->left_coord = INVALID_COORD; v->next = NULL; @@ -353,8 +330,6 @@ void InitializeVehicles() v->index = i++; memset(_vehicle_position_hash, -1, sizeof(_vehicle_position_hash)); - - _ptr_to_next_order = _order_array; } Vehicle *GetLastVehicleInChain(Vehicle *v) @@ -439,91 +414,6 @@ uint GetWaypointByTile(uint tile) return i; } - -Vehicle *IsScheduleShared(Vehicle *u) -{ - const Order *sched = u->schedule_ptr; - Vehicle *v; - - FOR_ALL_VEHICLES(v) { - if (v->schedule_ptr == sched && u != v && v->type != 0) - return v; - } - return NULL; -} - -void DeleteVehicleSchedule(Vehicle *v) -{ - Order *sched; - Order *cur; - int num; - Vehicle *u; - - // if the schedule is shared, don't delete it. - if ((u = IsScheduleShared(v)) != NULL) { - v->schedule_ptr = NULL; - InvalidateWindow(WC_VEHICLE_ORDERS, u->index); - return; - } - - sched = v->schedule_ptr; - v->schedule_ptr = NULL; - - num = v->num_orders + 1; - - _ptr_to_next_order -= num; - - cur = sched; - while (cur != _ptr_to_next_order) { - assert(cur < _ptr_to_next_order); - cur[0] = cur[num]; - cur++; - } - - FOR_ALL_VEHICLES(v) { - if (v->schedule_ptr != NULL && sched < v->schedule_ptr) { - v->schedule_ptr -= num; - } - } -} - -void DeleteCommandFromVehicleSchedule(Order cmd) -{ - Vehicle *v; - bool need_invalidate; - - FOR_ALL_VEHICLES(v) { - if (v->type != 0 && v->schedule_ptr != NULL) { - Order *sched; - - // clear last station visited - if (v->last_station_visited == cmd.station && cmd.type == OT_GOTO_STATION) - v->last_station_visited = 0xFFFF; - - // check the next order - if (v->current_order.type == cmd.type && - v->current_order.station == cmd.station) { - v->current_order.type = OT_DUMMY; - v->current_order.flags = 0; - InvalidateWindow(WC_VEHICLE_VIEW, v->index); - } - - // clear the order list - need_invalidate = false; - for (sched = v->schedule_ptr; sched->type != OT_NOTHING; ++sched) { - if (sched->type == cmd.type && sched->station == cmd.station) { - sched->type = OT_DUMMY; - sched->flags = 0; - need_invalidate = true; - } - } - - if (need_invalidate) - InvalidateWindow(WC_VEHICLE_ORDERS, v->index); - } - } -} - void DoDeleteDepot(uint tile) { Order order; @@ -538,9 +428,8 @@ void DoDeleteDepot(uint tile) d->xy = 0; order.type = OT_GOTO_DEPOT; - order.flags = 0; order.station = dep_index; - DeleteCommandFromVehicleSchedule(order); + DeleteDestinationFromVehicleOrder(order); // Delete the depot DeleteWindowById(WC_VEHICLE_DEPOT, tile); @@ -553,8 +442,8 @@ void DeleteVehicle(Vehicle *v) UpdateVehiclePosHash(v, INVALID_COORD, 0); v->next_hash = 0xffff; - if (v->schedule_ptr != NULL) - DeleteVehicleSchedule(v); + if (v->orders != NULL) + DeleteVehicleOrders(v); } void DeleteVehicleChain(Vehicle *v) @@ -1699,12 +1588,6 @@ void EndVehicleMove(Vehicle *v) ); } -void InvalidateVehicleOrderWidget(Vehicle *v) -{ - InvalidateWindowWidget(WC_VEHICLE_VIEW, v->index, STATUS_BAR); - InvalidateWindowWidget(WC_VEHICLE_ORDERS, v->index, 2); -} - /* returns true if staying in the same tile */ bool GetNewVehiclePos(Vehicle *v, GetNewVehiclePosResult *gp) { @@ -1846,7 +1729,7 @@ const byte _common_veh_desc[] = { SLE_CONDVARX(offsetof(Vehicle, current_order) + offsetof(Order, flags), SLE_UINT8, 5, 255), SLE_CONDVARX(offsetof(Vehicle, current_order) + offsetof(Order, station), SLE_UINT16, 5, 255), - SLE_REF(Vehicle,schedule_ptr, REF_SCHEDULE), + SLE_REF(Vehicle,orders, REF_ORDER), SLE_VAR(Vehicle,age, SLE_UINT16), SLE_VAR(Vehicle,max_age, SLE_UINT16), @@ -1869,9 +1752,11 @@ const byte _common_veh_desc[] = { SLE_VAR(Vehicle,random_bits, SLE_UINT8), SLE_VAR(Vehicle,waiting_triggers, SLE_UINT8), - // reserve extra space in savegame here. (currently 14 bytes) + SLE_REF(Vehicle,next_shared, REF_VEHICLE), + SLE_REF(Vehicle,prev_shared, REF_VEHICLE), + + // reserve extra space in savegame here. (currently 10 bytes) SLE_CONDARR(NullStruct,null,SLE_FILE_U8 | SLE_VAR_NULL, 2, 2, 255), /* 2 */ - SLE_CONDARR(NullStruct,null,SLE_FILE_U16 | SLE_VAR_NULL, 2, 2, 255), /* 4 */ SLE_CONDARR(NullStruct,null,SLE_FILE_U32 | SLE_VAR_NULL, 2, 2, 255), /* 8 */ SLE_END() @@ -2068,6 +1953,29 @@ static void Load_VEHS() w->u.rail.first_engine = v->engine_type; } + /* Check for shared order-lists (we now use pointers for that) */ + if (_sl.full_version < 0x502) { + FOR_ALL_VEHICLES(v) { + Vehicle *u; + + if (v->type == 0) + continue; + + FOR_ALL_VEHICLES_FROM(u, v->index + 1) { + if (u->type == 0) + continue; + + /* If a vehicle has the same orders, add the link to eachother + in both vehicles */ + if (v->orders == u->orders) { + v->next_shared = u; + u->prev_shared = v; + break; + } + } + } + } + /* This is to ensure all pointers are within the limits of _vehicles_size */ if (_vehicle_id_ctr_day >= _vehicles_size) @@ -2131,55 +2039,8 @@ static void Load_CHKP() } } - -static void Save_ORDR() -{ - uint32 orders[lengthof(_order_array)]; - uint len = _ptr_to_next_order - _order_array; - uint i; - - assert (len <= lengthof(orders)); - - for (i = 0; i < len; ++i) - orders[i] = PackOrder(&_order_array[i]); - - SlArray(orders, len, SLE_UINT32); -} - -static void Load_ORDR() -{ - uint len = SlGetFieldLength(); - uint i; - - if (_sl.version < 5) { - /* Older version had an other layout for orders.. convert them correctly */ - uint16 orders[lengthof(_order_array)]; - - len /= sizeof(uint16); - assert (len <= lengthof(orders)); - - SlArray(orders, len, SLE_UINT16); - - for (i = 0; i < len; ++i) - _order_array[i] = UnpackVersion4Order(orders[i]); - } else { - uint32 orders[lengthof(_order_array)]; - - len /= sizeof(uint32); - assert (len <= lengthof(orders)); - - SlArray(orders, len, SLE_UINT32); - - for (i = 0; i < len; ++i) - _order_array[i] = UnpackOrder(orders[i]); - } - - _ptr_to_next_order = _order_array + len; -} - const ChunkHandler _veh_chunk_handlers[] = { { 'VEHS', Save_VEHS, Load_VEHS, CH_SPARSE_ARRAY}, - { 'ORDR', Save_ORDR, Load_ORDR, CH_RIFF}, { 'DEPT', Save_DEPT, Load_DEPT, CH_ARRAY}, { 'CHKP', Save_CHKP, Load_CHKP, CH_ARRAY | CH_LAST}, }; diff --git a/vehicle.h b/vehicle.h index bdc9ca8537..8a0aae4bd8 100644 --- a/vehicle.h +++ b/vehicle.h @@ -2,42 +2,7 @@ #define VEHICLE_H #include "vehicle_gui.h" - -/* If you change this, keep in mind that it is saved on 3 places: - - Load_ORDR, all the global orders - - Vehicle -> current_order - - REF_SHEDULE (all REFs are currently limited to 16 bits!!) */ -typedef struct Order { - uint8 type; - uint8 flags; - uint16 station; -} Order; - -static inline uint32 PackOrder(const Order *order) -{ - return order->station << 16 | order->flags << 8 | order->type; -} - -static inline Order UnpackOrder(uint32 packed) -{ - Order order; - order.type = (packed & 0x000000FF); - order.flags = (packed & 0x0000FF00) >> 8; - order.station = (packed & 0xFFFF0000) >> 16; - return order; -} - -static inline Order UnpackVersion4Order(uint16 packed) -{ - Order order; - order.type = (packed & 0x000F); - order.flags = (packed & 0x00F0) >> 4; - order.station = (packed & 0xFF00) >> 8; - return order; -} - -Order UnpackOldOrder(uint16 packed); - +#include "order.h" typedef struct VehicleRail { uint16 last_speed; // NOSAVE: only used in UI @@ -177,11 +142,16 @@ struct Vehicle { byte day_counter; // increased by one for each day byte tick_counter;// increased by one for each tick - // related to the current order - byte cur_order_index; - byte num_orders; - Order current_order; - Order *schedule_ptr; + /* Begin Order-stuff */ + Order current_order; //! The current order (+ status, like: loading) + byte cur_order_index; //! The index to the current order + + Order *orders; //! Pointer to the first order for this vehicle + byte num_orders; //! How many orders there are in the list + + Vehicle *next_shared; //! If not NULL, this points to the next vehicle that shared the order + Vehicle *prev_shared; //! If not NULL, this points to the prev vehicle that shared the order + /* End Order-stuff */ // Boundaries for the current position in the world and a next hash link. // NOSAVE: All of those can be updated with VehiclePositionChanged() @@ -248,25 +218,6 @@ enum { VEH_Disaster = 0x15, }; -/* Order types */ -enum { - OT_NOTHING = 0, - OT_GOTO_STATION = 1, - OT_GOTO_DEPOT = 2, - OT_LOADING = 3, - OT_LEAVESTATION = 4, - OT_DUMMY = 5, - OT_GOTO_WAYPOINT = 6, -}; - -/* Order flags */ -enum { - OF_UNLOAD = 0x2, - OF_FULL_LOAD = 0x4, // Also used when to force an aircraft into a depot - OF_NON_STOP = 0x8 -}; - - enum VehStatus { VS_HIDDEN = 1, VS_STOPPED = 2, @@ -302,22 +253,11 @@ enum { typedef void VehicleTickProc(Vehicle *v); typedef void *VehicleFromPosProc(Vehicle *v, void *data); -typedef struct { - VehicleID clone; - byte orderindex; - Order order[41]; - uint16 service_interval; - char name[32]; -} BackuppedOrders; - void VehicleServiceInDepot(Vehicle *v); -void BackupVehicleOrders(Vehicle *v, BackuppedOrders *order); -void RestoreVehicleOrders(Vehicle *v, BackuppedOrders *order); Vehicle *AllocateVehicle(); Vehicle *ForceAllocateVehicle(); Vehicle *ForceAllocateSpecialVehicle(); void UpdateVehiclePosHash(Vehicle *v, int x, int y); -void InitializeVehicles(); void VehiclePositionChanged(Vehicle *v); void AfterLoadVehicles(); Vehicle *GetLastVehicleInChain(Vehicle *v); @@ -328,8 +268,6 @@ void DeleteVehicle(Vehicle *v); void DeleteVehicleChain(Vehicle *v); void *VehicleFromPos(TileIndex tile, void *data, VehicleFromPosProc *proc); void CallVehicleTicks(); -void DeleteVehicleSchedule(Vehicle *v); -Vehicle *IsScheduleShared(Vehicle *v); Depot *AllocateDepot(); Waypoint *AllocateWaypoint(); @@ -373,16 +311,12 @@ void CheckVehicleBreakdown(Vehicle *v); void AgeVehicle(Vehicle *v); void MaybeReplaceVehicle(Vehicle *v); -void DeleteCommandFromVehicleSchedule(Order cmd); - void BeginVehicleMove(Vehicle *v); void EndVehicleMove(Vehicle *v); bool IsAircraftHangarTile(TileIndex tile); void ShowAircraftViewWindow(Vehicle *v); -void InvalidateVehicleOrderWidget(Vehicle *v); - bool IsShipDepotTile(TileIndex tile); uint GetFreeUnitNumber(byte type); @@ -397,9 +331,6 @@ int32 GetTrainRunningCost(Vehicle *v); int CheckStoppedInDepot(Vehicle *v); -int ScheduleHasDepotOrders(const Order *schedule); -int CheckOrders(Vehicle *v); - bool VehicleNeedsService(const Vehicle *v); void InvalidateAircraftWindows(const Vehicle *v); @@ -439,8 +370,43 @@ static inline Vehicle *GetVehicle(uint index) #define FOR_ALL_VEHICLES(v) for(v = _vehicles; v != &_vehicles[_vehicles_size]; v++) #define FOR_ALL_VEHICLES_FROM(v, from) for(v = GetVehicle(from); v != &_vehicles[_vehicles_size]; v++) -VARDEF Order _order_array[5000]; -VARDEF Order *_ptr_to_next_order; +/* Returns order 'index' of a vehicle or NULL when it doesn't exists */ +static inline Order *GetVehicleOrder(const Vehicle *v, int index) +{ + Order *order = v->orders; + + if (index < 0) + return NULL; + + while (order != NULL && index-- > 0) + order = order->next; + + return order; +} + +/* Returns the last order of a vehicle, or NULL if it doesn't exists */ +static inline Order *GetLastVehicleOrder(const Vehicle *v) +{ + Order *order = v->orders; + + if (order == NULL) + return NULL; + + while (order->next != NULL) + order = order->next; + + return order; +} + +/* Get the first vehicle of a shared-list, so we only have to walk forwards */ +static inline Vehicle *GetFirstVehicleFromSharedList(Vehicle *v) +{ + Vehicle *u = v; + while (u->prev_shared != NULL) + u = u->prev_shared; + + return u; +} VARDEF Depot _depots[255]; @@ -465,8 +431,6 @@ VARDEF TileIndex _last_built_train_depot_tile; VARDEF TileIndex _last_built_road_depot_tile; VARDEF TileIndex _last_built_aircraft_depot_tile; VARDEF TileIndex _last_built_ship_depot_tile; -VARDEF TileIndex _backup_orders_tile; -VARDEF BackuppedOrders _backup_orders_data[1]; // for each player, for each vehicle type, keep a list of the vehicles. //VARDEF Vehicle *_vehicle_arr[8][4]; diff --git a/vehicle_gui.c b/vehicle_gui.c index 6166db2b93..601e7730c9 100644 --- a/vehicle_gui.c +++ b/vehicle_gui.c @@ -90,14 +90,16 @@ void BuildVehicleList(vehiclelist_d *vl, int type, int owner, int station) const Vehicle *v; FOR_ALL_VEHICLES(v) { if (v->type == type && v->subtype <= subtype) { - const Order *ord; - for (ord = v->schedule_ptr; ord->type != OT_NOTHING; ++ord) - if (ord->type == OT_GOTO_STATION && ord->station == station) { + const Order *order; + + FOR_VEHICLE_ORDERS(v, order) { + if (order->type == OT_GOTO_STATION && order->station == station) { _vehicle_sort[n].index = v->index; _vehicle_sort[n].owner = v->owner; ++n; break; } + } } } } else {