diff options
-rw-r--r-- | command.c | 2 | ||||
-rw-r--r-- | command.h | 2 | ||||
-rw-r--r-- | depot_gui.c | 12 | ||||
-rw-r--r-- | lang/english.txt | 2 | ||||
-rw-r--r-- | vehicle.c | 96 |
5 files changed, 102 insertions, 12 deletions
@@ -161,6 +161,7 @@ DEF_COMMAND(CmdSetAutoReplace); DEF_COMMAND(CmdCloneVehicle); DEF_COMMAND(CmdMassStartStopVehicle); DEF_COMMAND(CmdDepotSellAllVehicles); +DEF_COMMAND(CmdDepotMassAutoReplace); /* The master command table */ @@ -307,6 +308,7 @@ static const Command _command_proc_table[] = { {CmdCloneVehicle, 0}, /* 116 */ {CmdMassStartStopVehicle, 0}, /* 117 */ {CmdDepotSellAllVehicles, 0}, /* 118 */ + {CmdDepotMassAutoReplace, 0}, /* 119 */ }; /* This function range-checks a cmd, and checks if the cmd is not NULL */ @@ -138,7 +138,7 @@ enum { CMD_CLONE_VEHICLE = 116, CMD_MASS_START_STOP = 117, CMD_DEPOT_SELL_ALL_VEHICLES = 118, - + CMD_DEPOT_MASS_AUTOREPLACE = 119, }; enum { diff --git a/depot_gui.c b/depot_gui.c index 609c7b181..799ed3723 100644 --- a/depot_gui.c +++ b/depot_gui.c @@ -58,6 +58,7 @@ typedef enum DepotWindowWidgets { DEPOT_WIDGET_BUILD, DEPOT_WIDGET_CLONE, DEPOT_WIDGET_LOCATION, + DEPOT_WIDGET_AUTOREPLACE, DEPOT_WIDGET_RESIZE, DEPOT_WIDGET_LAST, // used to assert if DepotWindowWidgets and widget_moves got different lengths. Due to this usage, it needs to be last } DepotWindowWidget; @@ -78,6 +79,7 @@ static const byte widget_moves[] = { DEPOT_MOVE_DOWN, // DEPOT_WIDGET_BUILD DEPOT_MOVE_DOWN, // DEPOT_WIDGET_CLONE DEPOT_MOVE_DOWN, // DEPOT_WIDGET_LOCATION + DEPOT_MOVE_DOWN_RIGHT, // DEPOT_WIDGET_AUTOREPLACE DEPOT_MOVE_DOWN_RIGHT, // DEPOT_WIDGET_RESIZE }; @@ -112,6 +114,7 @@ static const Widget _depot_widgets[] = { { WWT_PUSHTXTBTN, RESIZE_TB, 14, 0, 96, 84, 95, 0x0, STR_NULL}, // DEPOT_WIDGET_BUILD {WWT_NODISTXTBTN, RESIZE_TB, 14, 97, 194, 84, 95, 0x0, STR_NULL}, // DEPOT_WIDGET_CLONE { WWT_PUSHTXTBTN, RESIZE_TB, 14, 195, 292, 84, 95, STR_00E4_LOCATION, STR_NULL}, // DEPOT_WIDGET_LOCATION + { WWT_PUSHIMGBTN, RESIZE_LRTB, 14, 281, 292, 84, 95, 0x0, STR_DEPOT_AUTOREPLACE_TIP}, // DEPOT_WIDGET_AUTOREPLACE { WWT_RESIZEBOX, RESIZE_LRTB, 14, 293, 304, 84, 95, 0x0, STR_RESIZE_BUTTON}, // DEPOT_WIDGET_RESIZE { WIDGETS_END}, }; @@ -256,7 +259,7 @@ static void DrawDepotWindow(Window *w) w->disabled_state = IsTileOwner(tile, _local_player) ? 0 : ( (1 << DEPOT_WIDGET_STOP_ALL) | (1 << DEPOT_WIDGET_START_ALL) | (1 << DEPOT_WIDGET_SELL) | (1 << DEPOT_WIDGET_SELL_CHAIN) | (1 << DEPOT_WIDGET_SELL_ALL) | - (1 << DEPOT_WIDGET_BUILD) | (1 << DEPOT_WIDGET_CLONE)); + (1 << DEPOT_WIDGET_BUILD) | (1 << DEPOT_WIDGET_CLONE) | (1 << DEPOT_WIDGET_AUTOREPLACE)); /* determine amount of items for scroller */ if (WP(w, depot_d).type == VEH_Train) { @@ -578,7 +581,7 @@ static void ResizeDepotButtons(Window *w) /* We got the widget moved around. Now we will make some widgets to fill the gab between some widgets in equal sizes */ /* Make the buttons in the bottom equal in size */ - w->widget[DEPOT_WIDGET_LOCATION].right = w->widget[DEPOT_WIDGET_RESIZE].left - 1; + w->widget[DEPOT_WIDGET_LOCATION].right = w->widget[DEPOT_WIDGET_AUTOREPLACE].left - 1; w->widget[DEPOT_WIDGET_BUILD].right = w->widget[DEPOT_WIDGET_LOCATION].right / 3; w->widget[DEPOT_WIDGET_LOCATION].left = w->widget[DEPOT_WIDGET_BUILD].right * 2; w->widget[DEPOT_WIDGET_CLONE].left = w->widget[DEPOT_WIDGET_BUILD].right + 1; @@ -652,6 +655,10 @@ static void DepotWndProc(Window *w, WindowEvent *e) ShowDepotSellAllWindow(w->window_number, WP(w, depot_d).type); break; + case DEPOT_WIDGET_AUTOREPLACE: + DoCommandP(w->window_number, WP(w, depot_d).type, 0, NULL, CMD_DEPOT_MASS_AUTOREPLACE); + break; + } break; @@ -812,6 +819,7 @@ static void SetupStringsForDepotWindow(Window *w, byte type) w->widget[DEPOT_WIDGET_STOP_ALL].tooltips = STR_MASS_STOP_HANGAR_TOOLTIP; w->widget[DEPOT_WIDGET_START_ALL].tooltips= STR_MASS_START_HANGAR_TOOLTIP; w->widget[DEPOT_WIDGET_SELL_ALL].tooltips = STR_DEPOT_SELL_ALL_BUTTON_HANGAR_TIP; + w->widget[DEPOT_WIDGET_AUTOREPLACE].tooltips = STR_DEPOT_AUTOREPLACE_HANGAR_TIP; break; } } diff --git a/lang/english.txt b/lang/english.txt index 32816303b..203fb6519 100644 --- a/lang/english.txt +++ b/lang/english.txt @@ -2910,6 +2910,8 @@ STR_DEPOT_SELL_ALL_CANCEL_TIP :{BLACK}Do not s STR_DEPOT_SELL_ALL_CANCEL_HANGAR_TIP :{BLACK}Do not sell all aircraft in the hangar STR_DEPOT_SELL_ALL_BUTTON_TIP :{BLACK}Sell all vehicles in the depot STR_DEPOT_SELL_ALL_BUTTON_HANGAR_TIP :{BLACK}Sell all aircraft in the hangar +STR_DEPOT_AUTOREPLACE_TIP :{BLACK}Autoreplace all vehicles in the depot +STR_DEPOT_AUTOREPLACE_HANGAR_TIP :{BLACK}Autoreplace all aircraft in the hangar STR_REPLACE_VEHICLES :{BLACK}Replace Vehicles STR_REPLACE_VEHICLES_WHITE :{WHITE}Replace {STRING} @@ -26,6 +26,7 @@ #include "station.h" #include "rail.h" #include "train.h" +#include "aircraft.h" #include "industry_map.h" #include "station_map.h" #include "water_map.h" @@ -590,7 +591,7 @@ void Ship_Tick(Vehicle *v); void Train_Tick(Vehicle *v); static void EffectVehicle_Tick(Vehicle *v); void DisasterVehicle_Tick(Vehicle *v); -static void MaybeReplaceVehicle(Vehicle *v); +static int32 MaybeReplaceVehicle(Vehicle **original_vehicle, bool check, bool display_costs); // head of the linked list to tell what vehicles that visited a depot in a tick static Vehicle* _first_veh_in_depot_list; @@ -670,7 +671,7 @@ void CallVehicleTicks(void) while (v != NULL) { Vehicle *w = v->depot_list; v->depot_list = NULL; // it should always be NULL at the end of each tick - MaybeReplaceVehicle(v); + MaybeReplaceVehicle(&v, false, true); v = w; } } @@ -1678,6 +1679,72 @@ int32 CmdDepotSellAllVehicles(TileIndex tile, uint32 flags, uint32 p1, uint32 p2 return cost; } +/** Autoreplace all vehicles in the depot +* @param tile Tile of the depot where the vehicles are +* @param p1 Type of vehicle +* @param p2 Unused +*/ +int32 CmdDepotMassAutoReplace(TileIndex tile, uint32 flags, uint32 p1, uint32 p2) +{ + Vehicle **vl = NULL; + uint16 engine_list_length = 0; + uint16 engine_count = 0; + uint i, x = 0, y = 0, z = 0; + int32 cost = 0; + byte vehicle_type = GB(p1, 0, 8); + + + if (!IsTileOwner(tile, _current_player)) return CMD_ERROR; + + /* Get the list of vehicles in the depot */ + BuildDepotVehicleList(vehicle_type, tile, &vl, &engine_list_length, &engine_count, NULL, NULL, NULL); + + + for (i = 0; i < engine_count; i++) { + Vehicle *v = vl[i]; + bool stopped = !(v->vehstatus & VS_STOPPED); + int32 ret; + + /* Ensure that the vehicle completely in the depot */ + if ((vehicle_type == VEH_Train && !CheckTrainInDepot(v, false)) || + (vehicle_type == VEH_Road && !IsRoadVehInDepot(v) ) || + (vehicle_type == VEH_Ship && !IsShipInDepot(v) ) || + (vehicle_type == VEH_Aircraft && !IsAircraftInHangar(v)) ) continue; + + if (stopped) v->vehstatus &= ~VS_STOPPED; // Stop the vehicle + ret = MaybeReplaceVehicle(&v, !(flags & DC_EXEC), false); + if (stopped) v->vehstatus |= VS_STOPPED; // restart the vehicle if we stopped it for being replaced + + if (!CmdFailed(ret)) { + cost += ret; + if (!(flags & DC_EXEC)) break; + x = v->x_pos; + y = v->y_pos; + z = v->z_pos; + /* There is a problem with autoreplace and newgrf + * It's impossible to tell the length of a train after it's being replaced before it's actually done + * Because of this, we can't estimate costs due to wagon removal and we will have to always return 0 and pay manually + * Since we pay after each vehicle is replaced and MaybeReplaceVehicle() check if the player got enough money + * we should never reach a condition where the player will end up with negative money from doing this */ + SET_EXPENSES_TYPE(EXPENSES_NEW_VEHICLES); + SubtractMoneyFromPlayer(ret); + } + } + + if (cost == 0) { + cost = CMD_ERROR; + } else { + if (flags & DC_EXEC) { + /* Display the cost animation now that DoCommandP() can't do it for us (see previous comments) */ + if (IsLocalPlayer()) ShowCostOrIncomeAnimation(x, y, z, cost); + } + cost = 0; + } + + free(vl); + return cost; +} + /** Clone a vehicle. If it is a train, it will clone all the cars too * @param tile tile of the depot where the cloned vehicle is build * @param p1 the original vehicle's index @@ -1980,10 +2047,13 @@ static int32 ReplaceVehicle(Vehicle **w, byte flags, int32 total_cost) * (used to be called autorenew) * @param v The vehicle to replace * if the vehicle is a train, v needs to be the front engine - * @return pointer to the new vehicle, which is the same as the argument if nothing happened + * @param check Checks if the replace is valid. No action is done at all + * @param display_costs If set, a cost animation is shown (only if check is false) + * @return CMD_ERROR if something went wrong. Otherwise the price of the replace */ -static void MaybeReplaceVehicle(Vehicle *v) +static int32 MaybeReplaceVehicle(Vehicle **original_vehicle, bool check, bool display_costs) { + Vehicle *v = *original_vehicle; Vehicle *w; const Player *p = GetPlayer(v->owner); byte flags = 0; @@ -2047,7 +2117,7 @@ static void MaybeReplaceVehicle(Vehicle *v) } while (w->type == VEH_Train && (w = GetNextVehicle(w)) != NULL); if (!(flags & DC_EXEC) && (p->money64 < (int32)(cost + p->engine_renew_money) || cost == 0)) { - if (p->money64 < (int32)(cost + p->engine_renew_money) && ( _local_player == v->owner ) && cost != 0) { + if (!check && p->money64 < (int32)(cost + p->engine_renew_money) && ( _local_player == v->owner ) && cost != 0) { StringID message; SetDParam(0, v->unitnumber); switch (v->type) { @@ -2063,11 +2133,15 @@ static void MaybeReplaceVehicle(Vehicle *v) } if (stopped) v->vehstatus &= ~VS_STOPPED; _current_player = OWNER_NONE; - return; + return CMD_ERROR; } if (flags & DC_EXEC) { break; // we are done replacing since the loop ran once with DC_EXEC + } else if (check) { + /* It's a test only and we know that we can do this + * NOTE: payment for wagon removal is NOT included in this price */ + return cost; } // now we redo the loop, but this time we actually do stuff since we know that we can do it flags |= DC_EXEC; @@ -2093,14 +2167,18 @@ static void MaybeReplaceVehicle(Vehicle *v) w = GetNextVehicle(w); DoCommand(0, (INVALID_VEHICLE << 16) | temp->index, 0, DC_EXEC, CMD_MOVE_RAIL_VEHICLE); MoveVehicleCargo(v, temp); - cost += DoCommand(0, temp->index, 0, flags, CMD_SELL_VEH(temp->type)); + cost += DoCommand(0, temp->index, 0, DC_EXEC, CMD_SELL_RAIL_WAGON); } } - if (IsLocalPlayer()) ShowCostOrIncomeAnimation(v->x_pos, v->y_pos, v->z_pos, cost); + original_vehicle = &v; if (stopped) v->vehstatus &= ~VS_STOPPED; - _current_player = OWNER_NONE; + if (display_costs) { + if (IsLocalPlayer()) ShowCostOrIncomeAnimation(v->x_pos, v->y_pos, v->z_pos, cost); + _current_player = OWNER_NONE; + } + return cost; } /* Extend the list size for BuildDepotVehicleList() */ |