diff options
author | bjarni <bjarni@openttd.org> | 2005-11-07 23:20:47 +0000 |
---|---|---|
committer | bjarni <bjarni@openttd.org> | 2005-11-07 23:20:47 +0000 |
commit | 5481dcd191a1b763330345d11264de6eac91a106 (patch) | |
tree | 419d4e9a7f19cca429bc209799068506c9d20f9d | |
parent | 723e789b2f1ac1d6f8eaa122d9626f41595c51cf (diff) | |
download | openttd-5481dcd191a1b763330345d11264de6eac91a106.tar.xz |
(svn r3155) -Feature: [autoreplace] autoreplace can now remove cars from too long trains
-Trains will now remember the length of stations it visits and sell cars
when being autoreplaced if they became too long
-If it needs to remove cars, then it starts from the front and sells
all it can find until the train is short enough
-This only works for trains, that knows the station length of the route
so a full uninterrupted run is needed
-a train needs 1-2 runs to detect if the shortest station is expanded
-This feature can be turned on and off in the train replace window
and each company can have it's own setting
-NOTE: minor savegame version bump
-rw-r--r-- | lang/english.txt | 3 | ||||
-rw-r--r-- | openttd.c | 17 | ||||
-rw-r--r-- | order_cmd.c | 4 | ||||
-rw-r--r-- | player.h | 1 | ||||
-rw-r--r-- | players.c | 23 | ||||
-rw-r--r-- | saveload.c | 2 | ||||
-rw-r--r-- | station.h | 5 | ||||
-rw-r--r-- | station_cmd.c | 5 | ||||
-rw-r--r-- | train_cmd.c | 24 | ||||
-rw-r--r-- | vehicle.c | 35 | ||||
-rw-r--r-- | vehicle.h | 11 | ||||
-rw-r--r-- | vehicle_gui.c | 13 |
12 files changed, 129 insertions, 14 deletions
diff --git a/lang/english.txt b/lang/english.txt index fc542bb06..e078a9cb0 100644 --- a/lang/english.txt +++ b/lang/english.txt @@ -943,6 +943,7 @@ STR_TRAIN_AUTORENEW_FAILED :{WHITE}Autorene STR_ROADVEHICLE_AUTORENEW_FAILED :{WHITE}Autorenew failed on road vehicle {COMMA} (money limit) STR_SHIP_AUTORENEW_FAILED :{WHITE}Autorenew failed on ship {COMMA} (money limit) STR_AIRCRAFT_AUTORENEW_FAILED :{WHITE}Autorenew failed on aircraft {COMMA} (money limit) +STR_TRAIN_TOO_LONG_AFTER_REPLACEMENT :{WHITE}Train {COMMA} is too long after replacement STR_CONFIG_PATCHES :{BLACK}Configure Patches STR_CONFIG_PATCHES_TIP :{BLACK}Configure the patches @@ -2752,6 +2753,8 @@ STR_REPLACE_HELP_START_BUTTON :{BLACK}Press to STR_REPLACE_HELP_RAILTYPE :{BLACK}Choose the railtype you want to replace engines for STR_REPLACE_HELP_REPLACE_INFO_TAB :{BLACK}Displays which engine the left selected engine is being replaced with, if any STR_REPLACE_HELP :{BLACK}This allows you to replace one engine type with another type, when trains of the original type enter a depot +STR_REPLACE_REMOVE_WAGON :{BLACK}Wagon removal: {ORANGE}{SKIP}{STRING} +STR_REPLACE_REMOVE_WAGON_HELP :{BLACK}Setting this to "On" will make autoreplace remove wagons from trains to make them keep their length if they exceed length of the shortest station in their orders.{}It will remove as many wagons as needed starting from the front STR_SHORT_DATE :{WHITE}{DATE_TINY} STR_SIGN_LIST_CAPTION :{WHITE}Sign List - {COMMA} Sign{P "" s} @@ -1300,6 +1300,23 @@ bool AfterLoadGame(uint version) } } + /* In version 16.1 of the savegame, trains became aware of station lengths + need to initialized to the invalid state + players needs to set renew_keep_length too */ + if (version < 0x1001) { + Vehicle *v; + FOR_ALL_PLAYERS(p) { + p->renew_keep_length = false; + } + + FOR_ALL_VEHICLES(v) { + if (v->type == VEH_Train) { + v->u.rail.shortest_platform[0] = 255; + v->u.rail.shortest_platform[1] = 0; + } + } + } + FOR_ALL_PLAYERS(p) { p->avail_railtypes = GetPlayerRailtypes(p->index); } diff --git a/order_cmd.c b/order_cmd.c index 227152cde..fda452ba8 100644 --- a/order_cmd.c +++ b/order_cmd.c @@ -388,6 +388,7 @@ int32 CmdInsertOrder(int x, int y, uint32 flags, uint32 p1, uint32 p2) } /* Update any possible open window of the vehicle */ InvalidateVehicleOrder(u); + if (u->type == VEH_Train) u->u.rail.shortest_platform[1] = 0; // we changed the orders so we invalidate the station length collector u = u->next_shared; } @@ -520,6 +521,7 @@ int32 CmdSkipOrder(int x, int y, uint32 flags, uint32 p1, uint32 p2) if (v->current_order.type == OT_LOADING && HASBIT(v->current_order.flags, OFB_NON_STOP)) v->current_order.flags = 0; + if (v->type == VEH_Train) v->u.rail.shortest_platform[1] = 0; // we changed the orders so we invalidate the station length collector InvalidateVehicleOrder(v); } @@ -663,6 +665,7 @@ int32 CmdCloneOrder(int x, int y, uint32 flags, uint32 p1, uint32 p2) InvalidateVehicleOrder(src); RebuildVehicleLists(); + if (dst->type == VEH_Train) dst->u.rail.shortest_platform[1] = 0; // we changed the orders so we invalidate the station length collector } } break; @@ -722,6 +725,7 @@ int32 CmdCloneOrder(int x, int y, uint32 flags, uint32 p1, uint32 p2) InvalidateVehicleOrder(dst); RebuildVehicleLists(); + if (dst->type == VEH_Train) dst->u.rail.shortest_platform[1] = 0; // we changed the orders so we invalidate the station length collector } } break; @@ -190,6 +190,7 @@ typedef struct Player { PlayerEconomyEntry old_economy[24]; EngineID engine_replacement[TOTAL_NUM_ENGINES]; bool engine_renew; + bool renew_keep_length; int16 engine_renew_months; uint32 engine_renew_money; } Player; @@ -497,6 +497,7 @@ Player *DoStartupNewPlayer(bool is_ai) for (i = 0; i < TOTAL_NUM_ENGINES; i++) p->engine_replacement[i] = INVALID_ENGINE; + p->renew_keep_length = false; p->engine_renew = false; p->engine_renew_months = -6; p->engine_renew_money = 100000; @@ -654,6 +655,7 @@ static void DeletePlayerStuff(PlayerID pi) * - p1 = 2 - change auto renew money * - p1 = 3 - change auto renew array * - p1 = 4 - change bool, months & money all together + * - p1 = 5 - change renew_keep_length * @param p2 value to set * if p1 = 0, then: * - p2 = enable engine renewal @@ -668,6 +670,8 @@ static void DeletePlayerStuff(PlayerID pi) * - p1 bit 15 = enable engine renewal * - p1 bits 16-31 = months left before engine expires to replace it * - p2 bits 0-31 = minimum amount of money available + * if p1 = 5, then + * - p2 = enable renew_keep_length */ int32 CmdReplaceVehicle(int x, int y, uint32 flags, uint32 p1, uint32 p2) { @@ -754,8 +758,19 @@ int32 CmdReplaceVehicle(int x, int y, uint32 flags, uint32 p1, uint32 p2) } } break; - } + case 5: + if (p->renew_keep_length == (bool)GB(p2, 0, 1)) + return CMD_ERROR; + + if (flags & DC_EXEC) { + p->renew_keep_length = (bool)GB(p2, 0, 1); + if (IsLocalPlayer()) { + InvalidateWindow(WC_REPLACE_VEHICLE, VEH_Train); + } + } + break; + } return 0; } @@ -1131,9 +1146,11 @@ static const SaveLoad _player_desc[] = { SLE_CONDVAR(Player,engine_renew, SLE_UINT8, 16, 255), SLE_CONDVAR(Player,engine_renew_months, SLE_INT16, 16, 255), SLE_CONDVAR(Player,engine_renew_money, SLE_UINT32, 16, 255), + SLE_CONDVAR(Player,renew_keep_length, SLE_UINT8, 2, 255), // added with 16.1, but was blank since 2 - // reserve extra space in savegame here. (currently 64 bytes) - SLE_CONDARR(NullStruct,null,SLE_FILE_U64 | SLE_VAR_NULL, 8, 2, 255), + // reserve extra space in savegame here. (currently 63 bytes) + SLE_CONDARR(NullStruct,null,SLE_FILE_U8 | SLE_VAR_NULL, 7, 2, 255), + SLE_CONDARR(NullStruct,null,SLE_FILE_U64 | SLE_VAR_NULL, 7, 2, 255), SLE_END() }; diff --git a/saveload.c b/saveload.c index 499bd3006..05bfb4475 100644 --- a/saveload.c +++ b/saveload.c @@ -30,7 +30,7 @@ enum { SAVEGAME_MAJOR_VERSION = 16, - SAVEGAME_MINOR_VERSION = 0, + SAVEGAME_MINOR_VERSION = 1, SAVEGAME_LOADABLE_VERSION = (SAVEGAME_MAJOR_VERSION << 8) + SAVEGAME_MINOR_VERSION }; @@ -320,6 +320,11 @@ static inline bool IsBuoyTile(TileIndex tile) return IsTileType(tile, MP_STATION) && _m[tile].m5 == 0x52; } +static inline bool TileBelongsToRailStation(const Station *st, TileIndex tile) +{ + return IsTileType(tile, MP_STATION) && _m[tile].m2 == st->index && _m[tile].m5 < 8; +} + /* Get's the direction the station exit points towards. Ie, returns 0 for a * station with the exit NE. */ static inline byte GetRoadStationDir(TileIndex tile) diff --git a/station_cmd.c b/station_cmd.c index 1a5e81bda..ec643fcf7 100644 --- a/station_cmd.c +++ b/station_cmd.c @@ -1083,11 +1083,6 @@ int32 CmdBuildRailroadStation(int x, int y, uint32 flags, uint32 p1, uint32 p2) return cost; } -static bool TileBelongsToRailStation(const Station *st, TileIndex tile) -{ - return IsTileType(tile, MP_STATION) && _m[tile].m2 == st->index && _m[tile].m5 < 8; -} - static void MakeRailwayStationAreaSmaller(Station *st) { uint w = st->trainst_w; diff --git a/train_cmd.c b/train_cmd.c index 0e79e76a0..e5daaf014 100644 --- a/train_cmd.c +++ b/train_cmd.c @@ -752,6 +752,9 @@ int32 CmdBuildRailVehicle(int x, int y, uint32 flags, uint32 p1, uint32 p2) v->type = VEH_Train; v->cur_image = 0xAC2; + v->u.rail.shortest_platform[0] = 255; + v->u.rail.shortest_platform[1] = 0; + VehiclePositionChanged(v); if (rvi->flags & RVI_MULTIHEAD && !HASBIT(p2, 0)) { @@ -2349,6 +2352,27 @@ static bool ProcessTrainOrder(Vehicle *v) v->dest_tile = 0; + // store the station length if no shorter station was visited this order round + if (v->cur_order_index == 0) { + if (v->u.rail.shortest_platform[1] != 0 && v->u.rail.shortest_platform[1] != 255) { + // we went though a whole round of orders without interruptions, so we store the length of the shortest station + v->u.rail.shortest_platform[0] = v->u.rail.shortest_platform[1]; + } + // all platforms are shorter than 255, so now we can find the shortest in the next order round. They might have changed size + v->u.rail.shortest_platform[1] = 255; + } + + if (v->last_station_visited != INVALID_STATION) { + Station *st = GetStation(v->last_station_visited); + if (TileBelongsToRailStation(st, v->tile)) { + byte length = GetStationPlatforms(st, v->tile); + + if (length < v->u.rail.shortest_platform[1]) { + v->u.rail.shortest_platform[1] = length; + } + } + } + result = false; switch (order->type) { case OT_GOTO_STATION: @@ -1693,6 +1693,8 @@ static int32 ReplaceVehicle(Vehicle **w, byte flags) if (old_v->type == VEH_Train){ // move the entire train to the new engine, including the old engine. It will be sold in a moment anyway DoCommand(0, 0, (new_v->index << 16) | old_v->index, 1, DC_EXEC, CMD_MOVE_RAIL_VEHICLE); + new_v->u.rail.shortest_platform[0] = old_v->u.rail.shortest_platform[0]; + new_v->u.rail.shortest_platform[1] = old_v->u.rail.shortest_platform[1]; } } } @@ -1720,6 +1722,7 @@ static void MaybeReplaceVehicle(Vehicle *v) byte flags = 0; int32 cost, temp_cost = 0; bool stopped = false; + bool train_fits_in_station = false; _current_player = v->owner; @@ -1733,6 +1736,11 @@ static void MaybeReplaceVehicle(Vehicle *v) stopped = true; } + if (v->type == VEH_Train && v->u.rail.shortest_platform[0]*16 <= v->u.rail.cached_total_length && GetPlayer(v->owner)->renew_keep_length) { + // the train is not too long for the stations it visits. We should try to keep it that way if we change anything + train_fits_in_station = true; + } + while (true) { cost = 0; w = v; @@ -1794,6 +1802,27 @@ static void MaybeReplaceVehicle(Vehicle *v) flags |= DC_EXEC; } + if (train_fits_in_station) { + // the train fitted in the stations it got in it's orders, so we should make sure that it still do + Vehicle *temp; + w = v; + while (v->u.rail.shortest_platform[0]*16 < v->u.rail.cached_total_length) { + // the train is too long. We will remove cars one by one from the start of the train until it's short enough + while (w != NULL && !(RailVehInfo(w->engine_type)->flags&RVI_WAGON) ) { + w = GetNextVehicle(w); + } + if (w == NULL) { + // we failed to make the train short enough + SetDParam(0, v->unitnumber); + AddNewsItem(STR_TRAIN_TOO_LONG_AFTER_REPLACEMENT, NEWS_FLAGS(NM_SMALL, NF_VIEWPORT|NF_VEHICLE, NT_ADVICE, 0), v->index, 0); + break; + } + temp = w; + w = GetNextVehicle(w); + cost += DoCommand(0, 0, temp->index, 0, flags, CMD_SELL_VEH(temp->type)); + } + } + if (IsLocalPlayer()) ShowCostOrIncomeAnimation(v->x_pos, v->y_pos, v->z_pos, cost); if (stopped) @@ -2085,8 +2114,10 @@ static const SaveLoad _train_desc[] = { SLE_CONDVARX(offsetof(Vehicle,u)+offsetof(VehicleRail,pbs_status), SLE_UINT8, 2, 255), SLE_CONDVARX(offsetof(Vehicle,u)+offsetof(VehicleRail,pbs_end_tile), SLE_UINT32, 2, 255), SLE_CONDVARX(offsetof(Vehicle,u)+offsetof(VehicleRail,pbs_end_trackdir), SLE_UINT8, 2, 255), - // reserve extra space in savegame here. (currently 7 bytes) - SLE_CONDARR(NullStruct,null,SLE_FILE_U8 | SLE_VAR_NULL, 7, 2, 255), + SLE_CONDVARX(offsetof(Vehicle,u)+offsetof(VehicleRail,shortest_platform[0]), SLE_UINT8, 2, 255), // added with 16.1, but was blank since 2 + SLE_CONDVARX(offsetof(Vehicle,u)+offsetof(VehicleRail,shortest_platform[1]), SLE_UINT8, 2, 255), // added with 16.1, but was blank since 2 + // reserve extra space in savegame here. (currently 5 bytes) + SLE_CONDARR(NullStruct,null,SLE_FILE_U8 | SLE_VAR_NULL, 5, 2, 255), SLE_END() }; @@ -85,6 +85,14 @@ typedef struct VehicleRail { byte pbs_status; TileIndex pbs_end_tile; Trackdir pbs_end_trackdir; + + /** + * stuff to figure out how long a train should be. Used by autoreplace + * first byte holds the length of the shortest station. Updated each time order 0 is reached + * last byte is the shortest station reached this round though the orders. It can be invalidated by + * skip station and alike by setting it to 0. That way we will ensure that a complete loop is used to find the shortest station + */ + byte shortest_platform[2]; } VehicleRail; enum { @@ -177,7 +185,6 @@ struct Vehicle { int32 x_pos; // coordinates int32 y_pos; - bool leave_depot_instantly; // NOSAVE: stores if the vehicle needs to leave the depot it just entered. Used by autoreplace byte z_pos; byte direction; // facing @@ -248,6 +255,8 @@ struct Vehicle { byte breakdown_chance; byte build_year; + bool leave_depot_instantly; // NOSAVE: stores if the vehicle needs to leave the depot it just entered. Used by autoreplace + uint16 load_unload_time_rem; int32 profit_this_year; diff --git a/vehicle_gui.c b/vehicle_gui.c index 9edb40caa..ebe2afebc 100644 --- a/vehicle_gui.c +++ b/vehicle_gui.c @@ -748,10 +748,10 @@ static void DrawEngineArrayInReplaceWindow(Window *w, int x, int y, int x2, int static void ReplaceVehicleWndProc(Window *w, WindowEvent *e) { static const StringID _vehicle_type_names[4] = {STR_019F_TRAIN, STR_019C_ROAD_VEHICLE, STR_019E_SHIP,STR_019D_AIRCRAFT}; + const Player *p = GetPlayer(_local_player); switch (e->event) { case WE_PAINT: { - const Player *p = GetPlayer(_local_player); int pos = w->vscroll.pos; int selected_id[2] = {-1,-1}; int x = 1; @@ -839,6 +839,12 @@ static void ReplaceVehicleWndProc(Window *w, WindowEvent *e) // now the actual drawing of the window itself takes place SetDParam(0, _vehicle_type_names[WP(w, replaceveh_d).vehicletype - VEH_Train]); + + if (WP(w, replaceveh_d).vehicletype == VEH_Train) { + // set on/off for renew_keep_length + SetDParam(1, p->renew_keep_length ? STR_CONFIG_PATCHES_ON : STR_CONFIG_PATCHES_OFF); + } + DrawWindowWidgets(w); // sets up the string for the vehicle that is being replaced to @@ -925,6 +931,9 @@ static void ReplaceVehicleWndProc(Window *w, WindowEvent *e) ShowDropDownMenu(w, _rail_types_list, _railtype_selected_in_replace_gui, 15, 0, ~GetPlayer(_local_player)->avail_railtypes); break; } + case 17: { /* toggle renew_keep_length */ + DoCommandP(0, 5, p->renew_keep_length ? 0 : 1, NULL, CMD_REPLACE_VEHICLE); + } break; case 4: { /* Start replacing */ EngineID veh_from = WP(w, replaceveh_d).sel_engine[0]; EngineID veh_to = WP(w, replaceveh_d).sel_engine[1]; @@ -991,7 +1000,7 @@ static const Widget _replace_rail_vehicle_widgets[] = { { WWT_PANEL, RESIZE_TB, 14, 154, 277, 210, 221, STR_NULL, STR_REPLACE_HELP_RAILTYPE}, { WWT_CLOSEBOX, RESIZE_TB, 14, 278, 289, 210, 221, STR_0225, STR_REPLACE_HELP_RAILTYPE}, { WWT_PANEL, RESIZE_TB, 14, 290, 305, 210, 221, STR_NULL, STR_NULL}, -{ WWT_PANEL, RESIZE_TB, 14, 317, 455, 198, 209, STR_NULL, STR_NULL}, +{ WWT_PUSHTXTBTN, RESIZE_TB, 14, 317, 455, 198, 209, STR_REPLACE_REMOVE_WAGON, STR_REPLACE_REMOVE_WAGON_HELP}, // end of train specific stuff { WWT_RESIZEBOX, RESIZE_TB, 14, 444, 455, 210, 221, STR_NULL, STR_RESIZE_BUTTON}, { WIDGETS_END}, |