diff --git a/aircraft_cmd.c b/aircraft_cmd.c index 6642ba09d3..aba653d365 100644 --- a/aircraft_cmd.c +++ b/aircraft_cmd.c @@ -332,7 +332,7 @@ bool IsAircraftHangarTile(TileIndex tile) (_m[tile].m5 == 32 || _m[tile].m5 == 65 || _m[tile].m5 == 86); } -static bool CheckStoppedInHangar(Vehicle *v) +bool CheckStoppedInHangar(Vehicle *v) { if (!(v->vehstatus & VS_STOPPED) || !IsAircraftHangarTile(v->tile)) { _error_message = STR_A01B_AIRCRAFT_MUST_BE_STOPPED; diff --git a/aircraft_gui.c b/aircraft_gui.c index fe57537233..b79a07d4c2 100644 --- a/aircraft_gui.c +++ b/aircraft_gui.c @@ -89,6 +89,15 @@ void CcBuildAircraft(bool success, TileIndex tile, uint32 p1, uint32 p2) } } +void CcCloneAircraft(bool success, uint tile, uint32 p1, uint32 p2) +{ + Vehicle *v; + + if (success) { + v = GetVehicle(_new_aircraft_id); + ShowAircraftViewWindow(v); + } +} static void NewAircraftWndProc(Window *w, WindowEvent *e) { @@ -496,11 +505,14 @@ static const Widget _aircraft_view_widgets[] = { { WWT_PUSHIMGBTN, RESIZE_LR, 14, 232, 249, 50, 67, 0x2B4, STR_A03B_REFIT_AIRCRAFT_TO_CARRY }, { WWT_PUSHIMGBTN, RESIZE_LR, 14, 232, 249, 68, 85, 0x2B2, STR_A028_SHOW_AIRCRAFT_S_ORDERS }, { WWT_PUSHIMGBTN, RESIZE_LR, 14, 232, 249, 86, 103, 0x2B3, STR_A02B_SHOW_AIRCRAFT_DETAILS }, +{ WWT_PUSHIMGBTN, RESIZE_LR, 14, 232, 249, 32, 49, SPR_CLONE_AIRCRAFT, STR_CLONE_AIRCRAFT_INFO }, { WWT_PANEL, RESIZE_LRB, 14, 232, 249, 104, 103, 0x0, STR_NULL }, { WWT_RESIZEBOX, RESIZE_LRTB, 14, 238, 249, 104, 115, 0x0, STR_NULL }, { WIDGETS_END } }; +bool CheckStoppedInHangar(Vehicle *v); + static void AircraftViewWndProc(Window *w, WindowEvent *e) { switch(e->event) { @@ -587,6 +599,12 @@ static void AircraftViewWndProc(Window *w, WindowEvent *e) case 10: /* show details */ ShowAircraftDetailsWindow(v); break; + case 11: { + /* clone vehicle */ + Vehicle *v; + v = GetVehicle(w->window_number); + DoCommandP(v->tile, v->index, _ctrl_pressed ? 1 : 0, CcCloneAircraft, CMD_CLONE_VEHICLE | CMD_MSG(STR_A008_CAN_T_BUILD_AIRCRAFT)); + } break; } } break; @@ -602,6 +620,19 @@ static void AircraftViewWndProc(Window *w, WindowEvent *e) DeleteWindowById(WC_VEHICLE_REFIT, w->window_number); DeleteWindowById(WC_VEHICLE_DETAILS, w->window_number); break; + + case WE_MOUSELOOP: + { + Vehicle *v; + uint32 h; + v = GetVehicle(w->window_number); + h = CheckStoppedInHangar(v) ? (1<< 7) : (1 << 11); + if (h != w->hidden_state) { + w->hidden_state = h; + SetWindowDirty(w); + } + } break; + } } @@ -636,7 +667,7 @@ static void DrawAircraftDepotWindow(Window *w) /* setup disabled buttons */ w->disabled_state = - IsTileOwner(tile, _local_player) ? 0 : ((1 << 4) | (1 << 7)); + IsTileOwner(tile, _local_player) ? 0 : ((1<<4) | (1<<7) | (1<<8)); /* determine amount of items for scroller */ num = 0; @@ -741,6 +772,42 @@ static void AircraftDepotClickAircraft(Window *w, int x, int y) } } +/** + * Clones an aircraft + * @param *v is the original vehicle to clone + * @param *w is the window of the hangar where the clone is build + */ +static bool HandleCloneVehClick(Vehicle *v, Window *w) +{ + + if (!v){ + return false; + } + + if (v->type != VEH_Aircraft) { + // it's not an aircraft, do nothing + return false; + } + + + DoCommandP(w->window_number, v->index, _ctrl_pressed ? 1 : 0,CcCloneAircraft,CMD_CLONE_VEHICLE | CMD_MSG(STR_882B_CAN_T_BUILD_RAILROAD_VEHICLE)); + + ResetObjectToPlace(); + + return true; +} + +static void ClonePlaceObj(uint tile, Window *w) +{ + Vehicle *v; + + + v = CheckMouseOverVehicle(); + if (v && HandleCloneVehClick(v, w)) + return; +} + + static void AircraftDepotWndProc(Window *w, WindowEvent *e) { switch(e->event) { @@ -754,14 +821,48 @@ static void AircraftDepotWndProc(Window *w, WindowEvent *e) AircraftDepotClickAircraft(w, e->click.pt.x, e->click.pt.y); break; case 7: /* show build aircraft window */ + ResetObjectToPlace(); ShowBuildAircraftWindow(w->window_number); break; - case 8: /* scroll to tile */ + + case 8: /* clone button */ + InvalidateWidget(w, 8); + TOGGLEBIT(w->click_state, 8); + + if (HASBIT(w->click_state, 8)) { + _place_clicked_vehicle = NULL; + SetObjectToPlaceWnd(SPR_CURSOR_CLONE, VHM_RECT, w); + } else { + ResetObjectToPlace(); + } + break; + case 9: /* scroll to tile */ + ResetObjectToPlace(); ScrollMainWindowToTile(w->window_number); break; } break; + +case WE_PLACE_OBJ: { + ClonePlaceObj(e->place.tile, w); + } break; + + case WE_ABORT_PLACE_OBJ: { + CLRBIT(w->click_state, 8); + InvalidateWidget(w, 8); + } break; + + // check if a vehicle in a depot was clicked.. + case WE_MOUSELOOP: { + Vehicle *v = _place_clicked_vehicle; + // since OTTD checks all open depot windows, we will make sure that it triggers the one with a clicked clone button + if (v != NULL && HASBIT(w->click_state, 8)) { + _place_clicked_vehicle = NULL; + HandleCloneVehClick( v, w); + } + } break; + case WE_DESTROY: DeleteWindowById(WC_BUILD_VEHICLE, w->window_number); break; @@ -824,8 +925,9 @@ static const Widget _aircraft_depot_widgets[] = { { WWT_MATRIX, RESIZE_RB, 14, 0, 295, 14, 61, 0x204, STR_A021_AIRCRAFT_CLICK_ON_AIRCRAFT}, { WWT_SCROLLBAR, RESIZE_LRB, 14, 319, 330, 14, 61, 0x0, STR_0190_SCROLL_BAR_SCROLLS_LIST}, -{ WWT_PUSHTXTBTN, RESIZE_TB, 14, 0, 159, 62, 73, STR_A003_NEW_AIRCRAFT, STR_A022_BUILD_NEW_AIRCRAFT}, -{ WWT_PUSHTXTBTN, RESIZE_TB, 14, 160, 318, 62, 73, STR_00E4_LOCATION, STR_A024_CENTER_MAIN_VIEW_ON_HANGAR}, +{ WWT_PUSHTXTBTN, RESIZE_TB, 14, 0, 105, 62, 73, STR_A003_NEW_AIRCRAFT, STR_A022_BUILD_NEW_AIRCRAFT}, +{WWT_NODISTXTBTN, RESIZE_TB, 14, 106, 212, 62, 73, STR_CLONE_AIRCRAFT, STR_CLONE_AIRCRAFT_INFO_HANGAR_WINDOW}, +{ WWT_PUSHTXTBTN, RESIZE_TB, 14, 213, 318, 62, 73, STR_00E4_LOCATION, STR_A024_CENTER_MAIN_VIEW_ON_HANGAR}, { WWT_PANEL, RESIZE_RTB, 14, 319, 318, 62, 73, 0x0, STR_NULL}, { WWT_RESIZEBOX, RESIZE_LRTB, 14, 319, 330, 62, 73, 0x0, STR_RESIZE_BUTTON}, { WIDGETS_END}, diff --git a/callback_table.c b/callback_table.c index 61cf42db83..c5fc153543 100644 --- a/callback_table.c +++ b/callback_table.c @@ -10,6 +10,7 @@ /* aircraft_gui.c */ CommandCallback CcBuildAircraft; +CommandCallback CcCloneAircraft; /* airport_gui.c */ CommandCallback CcBuildAirport; @@ -41,13 +42,16 @@ CommandCallback CcRoadDepot; /* roadveh_gui.c */ CommandCallback CcBuildRoadVeh; +CommandCallback CcCloneRoadVeh; /* ship_gui.c */ CommandCallback CcBuildShip; +CommandCallback CcCloneShip; /* train_gui.c */ CommandCallback CcBuildWagon; CommandCallback CcBuildLoco; +CommandCallback CcCloneTrain; CommandCallback *_callback_table[] = { /* 0x00 */ NULL, @@ -70,7 +74,11 @@ CommandCallback *_callback_table[] = { /* 0x11 */ CcPlaySound1D, /* 0x12 */ CcPlaySound1E, /* 0x13 */ CcStation, - /* 0x14 */ CcTerraform + /* 0x14 */ CcTerraform, + /* 0x15 */ CcCloneAircraft, + /* 0x16 */ CcCloneRoadVeh, + /* 0x17 */ CcCloneShip, + /* 0x18 */ CcCloneTrain, }; const int _callback_table_count = lengthof(_callback_table); diff --git a/command.c b/command.c index 2e3056ed6a..c31985c7a7 100644 --- a/command.c +++ b/command.c @@ -159,6 +159,9 @@ DEF_COMMAND(CmdRemoveSignalTrack); DEF_COMMAND(CmdReplaceVehicle); +DEF_COMMAND(CmdCloneVehicle); + + /* The master command table */ static const Command _command_proc_table[] = { {CmdBuildRailroadTrack, 0}, /* 0 */ @@ -300,6 +303,7 @@ static const Command _command_proc_table[] = { {CmdGiveMoney, 0}, /* 113 */ {CmdChangePatchSetting, CMD_SERVER}, /* 114 */ {CmdReplaceVehicle, 0}, /* 115 */ + {CmdCloneVehicle, 0}, /* 116 */ }; /* This function range-checks a cmd, and checks if the cmd is not NULL */ diff --git a/command.h b/command.h index b3d6390e3b..9dd384bbb1 100644 --- a/command.h +++ b/command.h @@ -136,6 +136,9 @@ enum { CMD_CHANGE_PATCH_SETTING = 114, CMD_REPLACE_VEHICLE = 115, + + CMD_CLONE_VEHICLE = 116, + }; enum { diff --git a/data/openttd.grf b/data/openttd.grf index d6166f23e2..5a784f20e5 100644 Binary files a/data/openttd.grf and b/data/openttd.grf differ diff --git a/lang/english.txt b/lang/english.txt index 61124ae22f..2f834f1fec 100644 --- a/lang/english.txt +++ b/lang/english.txt @@ -2404,6 +2404,12 @@ STR_881C_NEW_RAIL_VEHICLES :{WHITE}New Rail STR_881D_NEW_MONORAIL_VEHICLES :{WHITE}New Monorail Vehicles STR_881E_NEW_MAGLEV_VEHICLES :{WHITE}New Maglev Vehicles STR_881F_BUILD_VEHICLE :{BLACK}Build Vehicle +STR_CLONE_ROAD_VEHICLE :{BLACK}Clone Vehicle +STR_CLONE_ROAD_VEHICLE_INFO :{BLACK}This will build a copy of the road vehicle. Control-click will share the orders +STR_CLONE_ROAD_VEHICLE_DEPOT_INFO :{BLACK}This will build a copy of a road vehicle. Click this button and then on a road vehicle inside or outside the depot. Control-click will share the orders +STR_CLONE_TRAIN :{BLACK}Clone Train +STR_CLONE_TRAIN_INFO :{BLACK}This will build a copy of the train including all cars. Control-click will share the orders +STR_CLONE_TRAIN_DEPOT_INFO :{BLACK}This will build a copy of a train including all cars. Click this button and then on a train inside or outside the depot. Control-click will share the orders STR_8820_RENAME :{BLACK}Rename STR_8823_SKIP :{BLACK}Skip STR_8824_DELETE :{BLACK}Delete @@ -2560,6 +2566,9 @@ STR_9806_CAN_T_BUILD_SHIPS :{WHITE}Can't bu STR_9807_MUST_BUILD_SHIP_DEPOT_FIRST :{WHITE}Must build ship depot first STR_9808_NEW_SHIPS :{WHITE}New Ships STR_9809_BUILD_SHIP :{BLACK}Build Ship +STR_CLONE_SHIP :{BLACK}Clone Ship +STR_CLONE_SHIP_INFO :{BLACK}This will build a copy of the ship. Control-click will share the orders +STR_CLONE_SHIP_DEPOT_INFO :{BLACK}This will build a copy of a ship. Click this button and then on a ship inside or outside the depot. Control-click will share the orders STR_980B_SHIP_MUST_BE_STOPPED_IN :{WHITE}Ship must be stopped in depot STR_980C_CAN_T_SELL_SHIP :{WHITE}Can't sell ship... STR_980D_CAN_T_BUILD_SHIP :{WHITE}Can't build ship... @@ -2624,6 +2633,9 @@ STR_A000_AIRPORTS :{WHITE}Airports STR_A001_CAN_T_BUILD_AIRPORT_HERE :{WHITE}Can't build airport here... STR_A002_AIRCRAFT_HANGAR :{WHITE}{STATION} Aircraft Hangar STR_A003_NEW_AIRCRAFT :{BLACK}New Aircraft +STR_CLONE_AIRCRAFT :{BLACK}Clone Aircraft +STR_CLONE_AIRCRAFT_INFO :{BLACK}This will build a copy of the aircraft. Control-click will share the orders +STR_CLONE_AIRCRAFT_INFO_HANGAR_WINDOW :{BLACK}This will build a copy of an aircraft. Click this button and then on an aircraft inside or outside the hangar. Control-click will share the orders STR_A004_INFORMATION :{BLACK}Information STR_A005_NEW_AIRCRAFT :{WHITE}New Aircraft STR_A006_BUILD_AIRCRAFT :{BLACK}Build Aircraft diff --git a/roadveh_gui.c b/roadveh_gui.c index c979be9124..99541604a5 100644 --- a/roadveh_gui.c +++ b/roadveh_gui.c @@ -230,6 +230,16 @@ static void ShowRoadVehDetailsWindow(Vehicle *v) w->caption_color = v->owner; } +void CcCloneRoadVeh(bool success, uint tile, uint32 p1, uint32 p2) +{ + Vehicle *v; + + if (!success) return; + + v = GetVehicle(_new_roadveh_id); + ShowRoadVehViewWindow(v); +} + static void RoadVehViewWndProc(Window *w, WindowEvent *e) { switch(e->event) { @@ -308,6 +318,12 @@ static void RoadVehViewWndProc(Window *w, WindowEvent *e) case 10: /* show details */ ShowRoadVehDetailsWindow(v); break; + case 11: { + /* clone vehicle */ + Vehicle *v; + v = GetVehicle(w->window_number); + DoCommandP(v->tile, v->index, _ctrl_pressed ? 1 : 0, CcCloneRoadVeh, CMD_CLONE_VEHICLE | CMD_MSG(STR_9009_CAN_T_BUILD_ROAD_VEHICLE)); + } break; } } break; @@ -322,6 +338,18 @@ static void RoadVehViewWndProc(Window *w, WindowEvent *e) DeleteWindowById(WC_VEHICLE_ORDERS, w->window_number); DeleteWindowById(WC_VEHICLE_DETAILS, w->window_number); break; + + case WE_MOUSELOOP: + { + Vehicle *v; + uint32 h; + v = GetVehicle(w->window_number); + h = IsTileDepotType(v->tile, TRANSPORT_ROAD) && (v->vehstatus&VS_STOPPED) ? (1<< 7) : (1 << 11); + if (h != w->hidden_state) { + w->hidden_state = h; + SetWindowDirty(w); + } + } } } @@ -337,6 +365,7 @@ static const Widget _roadveh_view_widgets[] = { { WWT_PUSHIMGBTN, RESIZE_LR, 14, 232, 249, 50, 67, 0x2CB, STR_9020_FORCE_VEHICLE_TO_TURN_AROUND }, { WWT_PUSHIMGBTN, RESIZE_LR, 14, 232, 249, 68, 85, 0x2B2, STR_901D_SHOW_VEHICLE_S_ORDERS }, { WWT_PUSHIMGBTN, RESIZE_LR, 14, 232, 249, 86, 103, 0x2B3, STR_9021_SHOW_ROAD_VEHICLE_DETAILS }, +{ WWT_PUSHIMGBTN, RESIZE_LR, 14, 232, 249, 32, 49, SPR_CLONE_ROADVEH, STR_CLONE_ROAD_VEHICLE_INFO }, { WWT_PANEL, RESIZE_LRB, 14, 232, 249, 104, 103, 0x0, STR_NULL }, { WWT_RESIZEBOX, RESIZE_LRTB, 14, 238, 249, 104, 115, 0x0, STR_NULL }, { WIDGETS_END } @@ -536,7 +565,7 @@ static void DrawRoadDepotWindow(Window *w) /* setup disabled buttons */ w->disabled_state = - IsTileOwner(tile, _local_player) ? 0 : ((1 << 4) | (1 << 7)); + IsTileOwner(tile, _local_player) ? 0 : ((1<<4) | (1<<7) | (1<<8)); /* determine amount of items for scroller */ num = 0; @@ -640,6 +669,41 @@ static void RoadDepotClickVeh(Window *w, int x, int y) } } +/** + * Clones a road vehicle + * @param *v is the original vehicle to clone + * @param *w is the window of the depot where the clone is build + */ +static bool HandleCloneVehClick(Vehicle *v, Window *w) +{ + + if (!v){ + return false; + } + + if (v->type != VEH_Road) { + // it's not a road vehicle, do nothing + return false; + } + + + DoCommandP(w->window_number, v->index, _ctrl_pressed ? 1 : 0,CcCloneRoadVeh,CMD_CLONE_VEHICLE | CMD_MSG(STR_882B_CAN_T_BUILD_RAILROAD_VEHICLE)); + + ResetObjectToPlace(); + + return true; +} + +static void ClonePlaceObj(uint tile, Window *w) +{ + Vehicle *v; + + + v = CheckMouseOverVehicle(); + if (v && HandleCloneVehClick(v, w)) + return; +} + static void RoadDepotWndProc(Window *w, WindowEvent *e) { switch(e->event) { @@ -654,12 +718,45 @@ static void RoadDepotWndProc(Window *w, WindowEvent *e) break; case 7: + ResetObjectToPlace(); ShowBuildRoadVehWindow(w->window_number); break; + + case 8: /* clone button */ + InvalidateWidget(w, 8); + TOGGLEBIT(w->click_state, 8); + + if (HASBIT(w->click_state, 8)) { + _place_clicked_vehicle = NULL; + SetObjectToPlaceWnd(SPR_CURSOR_CLONE, VHM_RECT, w); + } else { + ResetObjectToPlace(); + } + break; + + case 9: /* scroll to tile */ + ResetObjectToPlace(); + ScrollMainWindowToTile(w->window_number); + break; + } + } break; + + case WE_PLACE_OBJ: { + ClonePlaceObj(e->place.tile, w); + } break; - case 8: /* scroll to tile */ - ScrollMainWindowToTile(w->window_number); - break; + case WE_ABORT_PLACE_OBJ: { + CLRBIT(w->click_state, 8); + InvalidateWidget(w, 8); + } break; + + // check if a vehicle in a depot was clicked.. + case WE_MOUSELOOP: { + Vehicle *v = _place_clicked_vehicle; + // since OTTD checks all open depot windows, we will make sure that it triggers the one with a clicked clone button + if (v != NULL && HASBIT(w->click_state, 8)) { + _place_clicked_vehicle = NULL; + HandleCloneVehClick( v, w); } } break; @@ -729,8 +826,9 @@ static const Widget _road_depot_widgets[] = { { WWT_MATRIX, RESIZE_RB, 14, 0, 279, 14, 55, 0x305, STR_9022_VEHICLES_CLICK_ON_VEHICLE}, { WWT_SCROLLBAR, RESIZE_LRB, 14, 303, 314, 14, 55, 0x0, STR_0190_SCROLL_BAR_SCROLLS_LIST}, -{ WWT_PUSHTXTBTN, RESIZE_TB, 14, 0, 150, 56, 67, STR_9004_NEW_VEHICLES, STR_9023_BUILD_NEW_ROAD_VEHICLE}, -{ WWT_PUSHTXTBTN, RESIZE_TB, 14, 151, 302, 56, 67, STR_00E4_LOCATION, STR_9025_CENTER_MAIN_VIEW_ON_ROAD}, +{ WWT_PUSHTXTBTN, RESIZE_TB, 14, 0, 100, 56, 67, STR_9004_NEW_VEHICLES, STR_9023_BUILD_NEW_ROAD_VEHICLE}, +{WWT_NODISTXTBTN, RESIZE_TB, 14, 101, 200, 56, 67, STR_CLONE_ROAD_VEHICLE, STR_CLONE_ROAD_VEHICLE_DEPOT_INFO}, +{ WWT_PUSHTXTBTN, RESIZE_TB, 14, 201, 302, 56, 67, STR_00E4_LOCATION, STR_9025_CENTER_MAIN_VIEW_ON_ROAD}, { WWT_PANEL, RESIZE_RTB, 14, 303, 302, 56, 67, 0x0, STR_NULL}, { WWT_RESIZEBOX, RESIZE_LRTB, 14, 303, 314, 56, 67, 0x0, STR_RESIZE_BUTTON}, { WIDGETS_END}, diff --git a/ship_gui.c b/ship_gui.c index 4b37a02a27..538d608f72 100644 --- a/ship_gui.c +++ b/ship_gui.c @@ -320,6 +320,15 @@ void CcBuildShip(bool success, TileIndex tile, uint32 p1, uint32 p2) ShowShipViewWindow(v); } +void CcCloneShip(bool success, uint tile, uint32 p1, uint32 p2) +{ + Vehicle *v; + if (!success) return; + + v = GetVehicle(_new_ship_id); + ShowShipViewWindow(v); +} + static void NewShipWndProc(Window *w, WindowEvent *e) { switch(e->event) { @@ -465,60 +474,60 @@ static void ShowBuildShipWindow(TileIndex tile) static void ShipViewWndProc(Window *w, WindowEvent *e) { switch(e->event) { - case WE_PAINT: { - Vehicle *v = GetVehicle(w->window_number); - uint32 disabled = 1<<8; - StringID str; + case WE_PAINT: { + Vehicle *v = GetVehicle(w->window_number); + uint32 disabled = 1<<8; + StringID str; - // Possible to refit? - if (ShipVehInfo(v->engine_type)->refittable && + // Possible to refit? + if (ShipVehInfo(v->engine_type)->refittable && v->vehstatus&VS_STOPPED && v->u.ship.state == 0x80 && IsTileDepotType(v->tile, TRANSPORT_WATER)) - disabled = 0; + disabled = 0; - if (v->owner != _local_player) - disabled |= 1<<8 | 1<<7; - w->disabled_state = disabled; + if (v->owner != _local_player) + disabled |= 1<<8 | 1<<7; + w->disabled_state = disabled; - /* draw widgets & caption */ - SetDParam(0, v->string_id); - SetDParam(1, v->unitnumber); - DrawWindowWidgets(w); + /* draw widgets & caption */ + SetDParam(0, v->string_id); + SetDParam(1, v->unitnumber); + DrawWindowWidgets(w); - if (v->breakdown_ctr == 1) { - str = STR_885C_BROKEN_DOWN; - } else if (v->vehstatus & VS_STOPPED) { - str = STR_8861_STOPPED; - } else { - switch (v->current_order.type) { - case OT_GOTO_STATION: { - SetDParam(0, v->current_order.station); - SetDParam(1, v->cur_speed * 10 >> 5); - str = STR_HEADING_FOR_STATION + _patches.vehicle_speed; - } break; + if (v->breakdown_ctr == 1) { + str = STR_885C_BROKEN_DOWN; + } else if (v->vehstatus & VS_STOPPED) { + str = STR_8861_STOPPED; + } else { + switch (v->current_order.type) { + case OT_GOTO_STATION: { + SetDParam(0, v->current_order.station); + SetDParam(1, v->cur_speed * 10 >> 5); + str = STR_HEADING_FOR_STATION + _patches.vehicle_speed; + } break; - case OT_GOTO_DEPOT: { - Depot *depot = GetDepot(v->current_order.station); - SetDParam(0, depot->town_index); - SetDParam(1, v->cur_speed * 10 >> 5); - str = STR_HEADING_FOR_SHIP_DEPOT + _patches.vehicle_speed; - } break; + case OT_GOTO_DEPOT: { + Depot *depot = GetDepot(v->current_order.station); + SetDParam(0, depot->town_index); + SetDParam(1, v->cur_speed * 10 >> 5); + str = STR_HEADING_FOR_SHIP_DEPOT + _patches.vehicle_speed; + } break; - case OT_LOADING: - case OT_LEAVESTATION: - str = STR_882F_LOADING_UNLOADING; - break; - - default: - if (v->num_orders == 0) { - str = STR_NO_ORDERS + _patches.vehicle_speed; - SetDParam(0, v->cur_speed * 10 >> 5); - } else - str = STR_EMPTY; - break; + case OT_LOADING: + case OT_LEAVESTATION: + str = STR_882F_LOADING_UNLOADING; + break; + + default: + if (v->num_orders == 0) { + str = STR_NO_ORDERS + _patches.vehicle_speed; + SetDParam(0, v->cur_speed * 10 >> 5); + } else + str = STR_EMPTY; + break; + } } - } /* draw the flag plus orders */ DrawSprite(v->vehstatus & VS_STOPPED ? 0xC12 : 0xC13, 2, w->widget[5].top + 1); @@ -526,43 +535,61 @@ static void ShipViewWndProc(Window *w, WindowEvent *e) { DrawWindowViewport(w); } break; - case WE_CLICK: { - Vehicle *v = GetVehicle(w->window_number); + case WE_CLICK: { + Vehicle *v = GetVehicle(w->window_number); - switch(e->click.widget) { - case 5: /* start stop */ - DoCommandP(v->tile, v->index, 0, NULL, CMD_START_STOP_SHIP | CMD_MSG(STR_9818_CAN_T_STOP_START_SHIP)); + switch(e->click.widget) { + case 5: /* start stop */ + DoCommandP(v->tile, v->index, 0, NULL, CMD_START_STOP_SHIP | CMD_MSG(STR_9818_CAN_T_STOP_START_SHIP)); + break; + case 6: /* center main view */ + ScrollMainWindowTo(v->x_pos, v->y_pos); + break; + case 7: /* goto hangar */ + DoCommandP(v->tile, v->index, 0, NULL, CMD_SEND_SHIP_TO_DEPOT | CMD_MSG(STR_9819_CAN_T_SEND_SHIP_TO_DEPOT)); + break; + case 8: /* refit */ + ShowShipRefitWindow(v); + break; + case 9: /* show orders */ + ShowOrdersWindow(v); + break; + case 10: /* show details */ + ShowShipDetailsWindow(v); + break; + case 11: { + /* clone vehicle */ + Vehicle *v; + v = GetVehicle(w->window_number); + DoCommandP(v->tile, v->index, _ctrl_pressed ? 1 : 0, CcCloneShip, CMD_CLONE_VEHICLE | CMD_MSG(STR_980D_CAN_T_BUILD_SHIP)); + } break; + } + } break; + + case WE_RESIZE: + w->viewport->width += e->sizing.diff.x; + w->viewport->height += e->sizing.diff.y; + w->viewport->virtual_width += e->sizing.diff.x; + w->viewport->virtual_height += e->sizing.diff.y; break; - case 6: /* center main view */ - ScrollMainWindowTo(v->x_pos, v->y_pos); - break; - case 7: /* goto hangar */ - DoCommandP(v->tile, v->index, 0, NULL, CMD_SEND_SHIP_TO_DEPOT | CMD_MSG(STR_9819_CAN_T_SEND_SHIP_TO_DEPOT)); - break; - case 8: /* refit */ - ShowShipRefitWindow(v); - break; - case 9: /* show orders */ - ShowOrdersWindow(v); - break; - case 10: /* show details */ - ShowShipDetailsWindow(v); + + case WE_DESTROY: + DeleteWindowById(WC_VEHICLE_ORDERS, w->window_number); + DeleteWindowById(WC_VEHICLE_REFIT, w->window_number); + DeleteWindowById(WC_VEHICLE_DETAILS, w->window_number); break; + + case WE_MOUSELOOP: + { + Vehicle *v; + uint32 h; + v = GetVehicle(w->window_number); + h = IsTileDepotType(v->tile, TRANSPORT_WATER) && v->vehstatus & VS_HIDDEN ? (1<< 7) : (1 << 11); + if (h != w->hidden_state) { + w->hidden_state = h; + SetWindowDirty(w); + } } - } break; - - case WE_RESIZE: - w->viewport->width += e->sizing.diff.x; - w->viewport->height += e->sizing.diff.y; - w->viewport->virtual_width += e->sizing.diff.x; - w->viewport->virtual_height += e->sizing.diff.y; - break; - - case WE_DESTROY: - DeleteWindowById(WC_VEHICLE_ORDERS, w->window_number); - DeleteWindowById(WC_VEHICLE_REFIT, w->window_number); - DeleteWindowById(WC_VEHICLE_DETAILS, w->window_number); - break; } } @@ -578,6 +605,7 @@ static const Widget _ship_view_widgets[] = { { WWT_PUSHIMGBTN, RESIZE_LR, 14, 232, 249, 50, 67, 0x2B4, STR_983A_REFIT_CARGO_SHIP_TO_CARRY}, { WWT_PUSHIMGBTN, RESIZE_LR, 14, 232, 249, 68, 85, 0x2B2, STR_9828_SHOW_SHIP_S_ORDERS}, { WWT_PUSHIMGBTN, RESIZE_LR, 14, 232, 249, 86, 103, 0x2B3, STR_982B_SHOW_SHIP_DETAILS}, +{ WWT_PUSHIMGBTN, RESIZE_LR, 14, 232, 249, 32, 49, SPR_CLONE_SHIP, STR_CLONE_SHIP_INFO}, { WWT_PANEL, RESIZE_LRB, 14, 232, 249, 104, 103, 0x0, STR_NULL }, { WWT_RESIZEBOX, RESIZE_LRTB, 14, 238, 249, 104, 115, 0x0, STR_NULL }, { WIDGETS_END } @@ -720,6 +748,41 @@ static void ShipDepotClick(Window *w, int x, int y) } } +/** + * Clones a ship + * @param *v is the original vehicle to clone + * @param *w is the window of the depot where the clone is build + */ +static bool HandleCloneVehClick(Vehicle *v, Window *w) +{ + + if (!v){ + return false; + } + + if (v->type != VEH_Ship) { + // it's not a ship, do nothing + return false; + } + + + DoCommandP(w->window_number, v->index, _ctrl_pressed ? 1 : 0,CcCloneShip,CMD_CLONE_VEHICLE | CMD_MSG(STR_882B_CAN_T_BUILD_RAILROAD_VEHICLE)); + + ResetObjectToPlace(); + + return true; +} + +static void ClonePlaceObj(uint tile, Window *w) +{ + Vehicle *v; + + + v = CheckMouseOverVehicle(); + if (v && HandleCloneVehClick(v, w)) + return; +} + static void ShipDepotWndProc(Window *w, WindowEvent *e) { switch(e->event) { case WE_PAINT: @@ -733,14 +796,49 @@ static void ShipDepotWndProc(Window *w, WindowEvent *e) { break; case 7: + ResetObjectToPlace(); ShowBuildShipWindow(w->window_number); break; + + case 8: /* clone button */ + InvalidateWidget(w, 8); + TOGGLEBIT(w->click_state, 8); + + if (HASBIT(w->click_state, 8)) { + _place_clicked_vehicle = NULL; + SetObjectToPlaceWnd(SPR_CURSOR_CLONE, VHM_RECT, w); + } else { + ResetObjectToPlace(); + } + break; - case 8: /* scroll to tile */ + case 9: /* scroll to tile */ + ResetObjectToPlace(); ScrollMainWindowToTile(w->window_number); break; } break; + + case WE_PLACE_OBJ: { + //ClonePlaceObj(e->place.tile, w); + ClonePlaceObj(w->window_number, w); + } break; + + case WE_ABORT_PLACE_OBJ: { + CLRBIT(w->click_state, 8); + InvalidateWidget(w, 8); + } break; + + // check if a vehicle in a depot was clicked.. + case WE_MOUSELOOP: { + Vehicle *v = _place_clicked_vehicle; + + // since OTTD checks all open depot windows, we will make sure that it triggers the one with a clicked clone button + if (v != NULL && HASBIT(w->click_state, 8)) { + _place_clicked_vehicle = NULL; + HandleCloneVehClick(v, w); + } + } break; case WE_DESTROY: DeleteWindowById(WC_BUILD_VEHICLE, w->window_number); @@ -804,8 +902,9 @@ static const Widget _ship_depot_widgets[] = { { WWT_MATRIX, RESIZE_RB, 14, 0, 269, 14, 61, 0x203, STR_981F_SHIPS_CLICK_ON_SHIP_FOR}, { WWT_SCROLLBAR, RESIZE_LRB, 14, 293, 304, 14, 61, 0x0, STR_0190_SCROLL_BAR_SCROLLS_LIST}, -{ WWT_PUSHTXTBTN, RESIZE_TB, 14, 0, 146, 62, 73, STR_9804_NEW_SHIPS, STR_9820_BUILD_NEW_SHIP}, -{ WWT_PUSHTXTBTN, RESIZE_TB, 14, 147, 292, 62, 73, STR_00E4_LOCATION, STR_9822_CENTER_MAIN_VIEW_ON_SHIP}, +{ WWT_PUSHTXTBTN, RESIZE_TB, 14, 0, 96, 62, 73, STR_9804_NEW_SHIPS, STR_9820_BUILD_NEW_SHIP}, +{WWT_NODISTXTBTN, RESIZE_TB, 14, 97, 194, 62, 73, STR_CLONE_SHIP, STR_CLONE_SHIP_DEPOT_INFO}, +{ WWT_PUSHTXTBTN, RESIZE_TB, 14, 195, 292, 62, 73, STR_00E4_LOCATION, STR_9822_CENTER_MAIN_VIEW_ON_SHIP}, { WWT_PANEL, RESIZE_RTB, 14, 293, 292, 62, 73, 0x0, STR_NULL}, { WWT_RESIZEBOX, RESIZE_LRTB, 14, 293, 304, 62, 73, 0x0, STR_RESIZE_BUTTON}, { WIDGETS_END}, diff --git a/spritecache.c b/spritecache.c index 26a7391e84..bc2317564f 100644 --- a/spritecache.c +++ b/spritecache.c @@ -732,7 +732,7 @@ static const char * const _cached_filenames[4] = { "cached_sprites.xx3", }; -#define OPENTTD_SPRITES_COUNT 98 +#define OPENTTD_SPRITES_COUNT 100 static const SpriteID _openttd_grf_indexes[] = { SPR_OPENTTD_BASE + 0, SPR_OPENTTD_BASE + 7, // icons etc 134, 134, // euro symbol medium size diff --git a/table/sprites.h b/table/sprites.h index 82426efcb7..1537a6b839 100644 --- a/table/sprites.h +++ b/table/sprites.h @@ -64,6 +64,11 @@ enum Sprites { SPR_ARROW_LEFT = SPR_OPENTTD_BASE + 97, SPR_ARROW_RIGHT = SPR_OPENTTD_BASE + 98, + /* Clone vehicles stuff */ + SPR_CLONE_AIRCRAFT = SPR_OPENTTD_BASE + 99, + SPR_CLONE_ROADVEH = SPR_OPENTTD_BASE + 99, + SPR_CLONE_TRAIN = SPR_OPENTTD_BASE + 99, + SPR_CLONE_SHIP = SPR_OPENTTD_BASE + 99, /* Network GUI sprites */ SPR_SQUARE = SPR_OPENTTD_BASE + 23, // colored square (used for newgrf compatibility) @@ -942,6 +947,8 @@ typedef enum CursorSprites { SPR_CURSOR_BUS_STATION = 2725, SPR_CURSOR_TRUCK_STATION = 2726, SPR_CURSOR_ROAD_TUNNEL = 2433, + + SPR_CURSOR_CLONE = SPR_OPENTTD_BASE + 100, } CursorSprite; /// Animation macro in table/animcursors.h (_animcursors[]) diff --git a/train_cmd.c b/train_cmd.c index 89477110e2..6883293fee 100644 --- a/train_cmd.c +++ b/train_cmd.c @@ -568,7 +568,7 @@ void AddRearEngineToMultiheadedTrain(Vehicle *v, Vehicle *u, bool building) /** Build a railroad vehicle. * @param x,y tile coordinates (depot) where rail-vehicle is built * @param p1 engine type id - * @param p2 unused + * @param p2 build only one engine, even if it is a dualheaded engine. It also prevents any free cars from being added to the train */ int32 CmdBuildRailVehicle(int x, int y, uint32 flags, uint32 p1, uint32 p2) { @@ -594,10 +594,19 @@ int32 CmdBuildRailVehicle(int x, int y, uint32 flags, uint32 p1, uint32 p2) SET_EXPENSES_TYPE(EXPENSES_NEW_VEHICLES); rvi = RailVehInfo(p1); + e = GetEngine(p1); + + /* Check if depot and new engine uses the same kind of tracks */ + if (!IsCompatibleRail(e->railtype, GetRailType(tile))) return CMD_ERROR; if (rvi->flags & RVI_WAGON) return CmdBuildRailWagon(p1, tile, flags); value = EstimateTrainCost(rvi); + + //make sure we only pay for half a dualheaded engine if we only requested half of it + if (rvi->flags&RVI_MULTIHEAD && HASBIT(p2,0)) + value /= 2; + if (!(flags & DC_QUERY_COST)) { v = AllocateVehicle(); @@ -633,7 +642,6 @@ int32 CmdBuildRailVehicle(int x, int y, uint32 flags, uint32 p1, uint32 p2) v->dest_tile = 0; v->engine_type = (byte)p1; - e = GetEngine(p1); v->reliability = e->reliability; v->reliability_spd_dec = e->reliability_spd_dec; @@ -651,12 +659,16 @@ int32 CmdBuildRailVehicle(int x, int y, uint32 flags, uint32 p1, uint32 p2) VehiclePositionChanged(v); - if (rvi->flags&RVI_MULTIHEAD && (u = AllocateVehicle()) != NULL) - AddRearEngineToMultiheadedTrain(v, u, true); + if (rvi->flags&RVI_MULTIHEAD && (u = AllocateVehicle()) != NULL && !HASBIT(p2,0)) { + AddRearEngineToMultiheadedTrain(v, u, true); + } TrainConsistChanged(v); UpdateTrainAcceleration(v); - NormalizeTrainVehInDepot(v); + + if (!HASBIT(p2,0)) { // do not move the cars if HASBIT(p2,0) is set + NormalizeTrainVehInDepot(v); + } InvalidateWindow(WC_VEHICLE_DEPOT, tile); RebuildVehicleLists(); @@ -1472,10 +1484,7 @@ int32 CmdForceTrainProceed(int x, int y, uint32 flags, uint32 p1, uint32 p2) /** Refits a train to the specified cargo type. * @param x,y unused * @param p1 vehicle ID of the train to refit - * @param p2 various bitstuffed elements - * - p2 = (bit 0-7) - the new cargo type to refit to (p2 & 0xFF) - * - p2 = (bit 8) - skip check for stopped in depot, used by autoreplace (p2 & 0x100) - * @todo p2 bit8 check NEEDS TO GO + * @param p2 the new cargo type to refit to (p2 & 0xFF) */ int32 CmdRefitRailVehicle(int x, int y, uint32 flags, uint32 p1, uint32 p2) { @@ -1483,14 +1492,13 @@ int32 CmdRefitRailVehicle(int x, int y, uint32 flags, uint32 p1, uint32 p2) int32 cost; uint num; CargoID new_cid = p2 & 0xFF; //gets the cargo number - bool SkipStoppedInDepotCheck = !!HASBIT(p2, 8); // XXX - needs to go, yes? if (!IsVehicleIndex(p1)) return CMD_ERROR; v = GetVehicle(p1); if (v->type != VEH_Train || !CheckOwnership(v->owner)) return CMD_ERROR; - if (!SkipStoppedInDepotCheck && CheckTrainStoppedInDepot(v) < 0) return_cmd_error(STR_TRAIN_MUST_BE_STOPPED); + if (CheckTrainStoppedInDepot(v) < 0) return_cmd_error(STR_TRAIN_MUST_BE_STOPPED); /* Check cargo */ if (new_cid > NUM_CARGO) return CMD_ERROR; @@ -1537,10 +1545,7 @@ int32 CmdRefitRailVehicle(int x, int y, uint32 flags, uint32 p1, uint32 p2) cost += (_price.build_railvehicle >> 8); num += amount; if (flags & DC_EXEC) { - //autorefitted train cars wants to keep the cargo - //it will be checked if the cargo is valid in CmdReplaceVehicle - if (!(SkipStoppedInDepotCheck)) - v->cargo_count = 0; + v->cargo_count = 0; v->cargo_type = new_cid; v->cargo_cap = amount; InvalidateWindow(WC_VEHICLE_DETAILS, v->index); @@ -1548,8 +1553,7 @@ int32 CmdRefitRailVehicle(int x, int y, uint32 flags, uint32 p1, uint32 p2) } } } - // SkipStoppedInDepotCheck is called by CmdReplace and it should only apply to the single car it is called for - } while ( (v=v->next) != NULL || SkipStoppedInDepotCheck ); + } while ( (v=v->next) != NULL ); _returned_refit_amount = num; diff --git a/train_gui.c b/train_gui.c index c753427f15..cc9f224ce7 100644 --- a/train_gui.c +++ b/train_gui.c @@ -154,6 +154,17 @@ void CcBuildLoco(bool success, TileIndex tile, uint32 p1, uint32 p2) ShowTrainViewWindow(v); } +void CcCloneTrain(bool success, uint tile, uint32 p1, uint32 p2) +{ + Vehicle *v; + + if (!success) + return; + + v = GetVehicle(_new_train_id); + ShowTrainViewWindow(v); +} + static void engine_drawing_loop(int *x, int *y, int *pos, int *sel, int *selected_id, byte railtype, byte show_max, bool is_engine) { @@ -366,7 +377,7 @@ static void DrawTrainDepotWindow(Window *w) /* setup disabled buttons */ w->disabled_state = - IsTileOwner(tile, _local_player) ? 0 : ((1 << 4) | (1 << 5) | (1 << 8)); + IsTileOwner(tile, _local_player) ? 0 : ((1 << 4) | (1 << 5) | (1 << 8) | (1<<9)); /* determine amount of items for scroller */ num = 0; @@ -580,6 +591,47 @@ static void TrainDepotClickTrain(Window *w, int x, int y) } } +/** + * Clones a train + * @param *v is the original vehicle to clone + * @param *w is the window of the depot where the clone is build + */ +static bool HandleCloneVehClick(Vehicle *v, Window *w) +{ + + if (!v){ + return false; + } + + // for train vehicles: subtype 0 for locs and not zero for others + if (v->type == VEH_Train && v->subtype != 0) { + v = GetFirstVehicleInChain(v); + if (v->subtype != 0) // This happens when clicking on a train in depot with no loc attached + return false; + }else{ + if (v->type != VEH_Train) { + // it's not a train, Do Nothing + return false; + } + } + + DoCommandP(w->window_number, v->index, _ctrl_pressed ? 1 : 0, CcCloneTrain, CMD_CLONE_VEHICLE | CMD_MSG(STR_882B_CAN_T_BUILD_RAILROAD_VEHICLE)); + + ResetObjectToPlace(); + + return true; +} + +static void ClonePlaceObj(uint tile, Window *w) +{ + Vehicle *v; + + + v = CheckMouseOverVehicle(); + if (v && HandleCloneVehClick(v, w)) + return; +} + static void TrainDepotWndProc(Window *w, WindowEvent *e) { switch(e->event) { @@ -590,17 +642,51 @@ static void TrainDepotWndProc(Window *w, WindowEvent *e) case WE_CLICK: { switch(e->click.widget) { case 8: + ResetObjectToPlace(); ShowBuildTrainWindow(w->window_number); break; - case 9: + case 10: + ResetObjectToPlace(); ScrollMainWindowToTile(w->window_number); break; case 6: TrainDepotClickTrain(w, e->click.pt.x, e->click.pt.y); break; + case 9: /* clone button */ + InvalidateWidget(w, 9); + TOGGLEBIT(w->click_state, 9); + + if (HASBIT(w->click_state, 9)) { + _place_clicked_vehicle = NULL; + SetObjectToPlaceWnd(SPR_CURSOR_CLONE, VHM_RECT, w); + } else { + ResetObjectToPlace(); + } + break; + + } + } break; + + case WE_PLACE_OBJ: { + ClonePlaceObj(e->place.tile, w); + } break; + + case WE_ABORT_PLACE_OBJ: { + CLRBIT(w->click_state, 9); + InvalidateWidget(w, 9); + } break; + + // check if a vehicle in a depot was clicked.. + case WE_MOUSELOOP: { + Vehicle *v = _place_clicked_vehicle; + // since OTTD checks all open depot windows, we will make sure that it triggers the one with a clicked clone button + if (v != NULL && HASBIT(w->click_state, 9)) { + _place_clicked_vehicle = NULL; + HandleCloneVehClick( v, w); } } break; + case WE_DESTROY: DeleteWindowById(WC_BUILD_VEHICLE, w->window_number); break; @@ -680,10 +766,14 @@ static const Widget _train_depot_widgets[] = { { WWT_MATRIX, RESIZE_RB, 14, 0, 325, 14, 97, 0x601, STR_883F_TRAINS_CLICK_ON_TRAIN_FOR}, { WWT_SCROLLBAR, RESIZE_LRB, 14, 349, 360, 14, 109, 0x0, STR_0190_SCROLL_BAR_SCROLLS_LIST}, -{ WWT_PUSHTXTBTN, RESIZE_TB, 14, 0, 167, 110, 121, STR_8815_NEW_VEHICLES, STR_8840_BUILD_NEW_TRAIN_VEHICLE}, -{ WWT_PUSHTXTBTN, RESIZE_TB, 14, 168, 348, 110, 121, STR_00E4_LOCATION, STR_8842_CENTER_MAIN_VIEW_ON_TRAIN}, +{ WWT_PUSHTXTBTN, RESIZE_TB, 14, 0, 116, 110, 121, STR_8815_NEW_VEHICLES, STR_8840_BUILD_NEW_TRAIN_VEHICLE}, +{WWT_NODISTXTBTN, RESIZE_TB, 14, 117, 232, 110, 121, STR_CLONE_TRAIN, STR_CLONE_TRAIN_DEPOT_INFO}, +{ WWT_PUSHTXTBTN, RESIZE_TB, 14, 233, 348, 110, 121, STR_00E4_LOCATION, STR_8842_CENTER_MAIN_VIEW_ON_TRAIN}, + + { WWT_HSCROLLBAR, RESIZE_RTB, 14, 0, 325, 98, 109, 0x0, STR_HSCROLL_BAR_SCROLLS_LIST}, { WWT_PANEL, RESIZE_RTB, 14, 349, 348, 110, 121, 0x0, STR_NULL}, + { WWT_RESIZEBOX, RESIZE_LRTB, 14, 349, 360, 110, 121, 0x0, STR_RESIZE_BUTTON}, { WIDGETS_END}, }; @@ -803,6 +893,7 @@ static Widget _train_view_widgets[] = { { WWT_PUSHIMGBTN, RESIZE_LR, 14, 232, 249, 86, 103, 0x2B2, STR_8847_SHOW_TRAIN_S_ORDERS }, { WWT_PUSHIMGBTN, RESIZE_LR, 14, 232, 249, 104, 121, 0x2B3, STR_884C_SHOW_TRAIN_DETAILS }, { WWT_PUSHIMGBTN, RESIZE_LR, 14, 232, 249, 68, 85, 0x2B4, STR_RAIL_REFIT_VEHICLE_TO_CARRY }, +{ WWT_PUSHIMGBTN, RESIZE_LR, 14, 232, 249, 32, 49, SPR_CLONE_TRAIN, STR_CLONE_TRAIN_INFO }, { WWT_PANEL, RESIZE_LRB, 14, 232, 249, 122, 121, 0x0, STR_NULL }, { WWT_RESIZEBOX, RESIZE_LRTB, 14, 238, 249, 122, 133, 0x0, STR_NULL }, { WIDGETS_END } @@ -833,7 +924,7 @@ static void TrainViewWndProc(Window *w, WindowEvent *e) /* draw widgets & caption */ SetDParam(0, v->string_id); - SetDParam(1, v->unitnumber); + SetDParam(1, v->unitnumber); DrawWindowWidgets(w); if (v->u.rail.crash_anim_pos != 0) { @@ -920,6 +1011,9 @@ static void TrainViewWndProc(Window *w, WindowEvent *e) case 12: ShowRailVehicleRefitWindow(v); break; + case 13: + DoCommandP(v->tile, v->index, _ctrl_pressed ? 1 : 0, NULL, CMD_CLONE_VEHICLE | CMD_MSG(STR_882B_CAN_T_BUILD_RAILROAD_VEHICLE)); + break; } } break; @@ -942,7 +1036,7 @@ static void TrainViewWndProc(Window *w, WindowEvent *e) v = GetVehicle(w->window_number); assert(v->type == VEH_Train); - h = CheckTrainStoppedInDepot(v) >= 0 ? (1 << 9) : (1 << 12); + h = CheckTrainStoppedInDepot(v) >= 0 ? (1 << 9)| (1 << 7) : (1 << 12) | (1 << 13); if (h != w->hidden_state) { w->hidden_state = h; SetWindowDirty(w); diff --git a/vehicle.c b/vehicle.c index dda0d2b269..5b889eb8b0 100644 --- a/vehicle.c +++ b/vehicle.c @@ -21,6 +21,7 @@ #include "vehicle_gui.h" #include "depot.h" #include "station.h" +#include "gui.h" #include "rail.h" #define INVALID_COORD (-0x8000) @@ -1669,6 +1670,122 @@ void MaybeReplaceVehicle(Vehicle *v) _current_player = OWNER_NONE; } +int32 CmdCloneOrder(int x, int y, uint32 flags, uint32 veh1_veh2, uint32 mode); +int32 CmdMoveRailVehicle(int x, int y, uint32 flags, uint32 p1, uint32 p2); +int32 CmdBuildRailVehicle(int x, int y, uint32 flags, uint32 p1, uint32 p2); +int32 CmdBuildRoadVeh(int x, int y, uint32 flags, uint32 p1, uint32 p2); +int32 CmdBuildShip(int x, int y, uint32 flags, uint32 p1, uint32 p2); +int32 CmdBuildAircraft(int x, int y, uint32 flags, uint32 p1, uint32 p2); + + +typedef int32 VehBuildProc(int x, int y, uint32 flags, uint32 p1, uint32 p2); + +static VehBuildProc * const _veh_build_proc_table[] = { + CmdBuildRailVehicle, + CmdBuildRoadVeh, + CmdBuildShip, + CmdBuildAircraft, +}; + +static VehicleID * _new_vehicle_id_proc_table[] = { + &_new_train_id, + &_new_roadveh_id, + &_new_ship_id, + &_new_aircraft_id, +}; + +/** Clone a vehicle. If it is a train, it will clone all the cars too + * @param x,y unused + * @param p1 the original vehicle's index + * @param p2 1 = shared orders, else copied orders + */ +int32 CmdCloneVehicle(int x, int y, uint32 flags, uint32 p1, uint32 p2) +{ + Vehicle *vfront, *v; + Vehicle *wfront, *w1, *w2; + int cost, total_cost; + VehBuildProc *proc; + VehicleID *new_id; + uint refit_command = 0; + byte needs_refitting = 255; + + if (!IsVehicleIndex(p1)) + return CMD_ERROR; + v = GetVehicle(p1); + wfront = v; + w1 = v; + vfront = v; + + if (!CheckOwnership(v->owner)) + return CMD_ERROR; + + if (v->type == VEH_Train && v->subtype != TS_Front_Engine) return CMD_ERROR; + + //no need to check if it is a depot since the build command do that + switch (v->type) { + case VEH_Train: refit_command = CMD_REFIT_RAIL_VEHICLE; break; + case VEH_Road: break; + case VEH_Ship: refit_command = CMD_REFIT_SHIP; break; + case VEH_Aircraft: refit_command = CMD_REFIT_AIRCRAFT; break; + default: return CMD_ERROR; + } + + proc = _veh_build_proc_table[v->type - VEH_Train]; + new_id = _new_vehicle_id_proc_table[v->type - VEH_Train]; + total_cost = proc(x, y, flags, v->engine_type, 1); + if (total_cost == CMD_ERROR) + return CMD_ERROR; + + if (flags & DC_EXEC) { + wfront = GetVehicle(*new_id); + w1 = wfront; + CmdCloneOrder(x, y, flags, (v->index << 16) | w1->index, p2 & 1 ? CO_SHARE : CO_COPY); + + if (wfront->cargo_type != v->cargo_type) { + //a refit is needed + needs_refitting = v->cargo_type; + } + } + if (v->type == VEH_Train) { + // now we handle the cars + v = v->next; + while (v != NULL) { + cost = proc(x, y, flags, v->engine_type, 1); + if (cost == CMD_ERROR) + return CMD_ERROR; + total_cost += cost; + + if (flags & DC_EXEC) { + // add this unit to the end of the train + w2 = GetVehicle(RailVehInfo(v->engine_type)->flags & RVI_WAGON ? _new_wagon_id : _new_train_id); + CmdMoveRailVehicle(x, y, flags, (w1->index << 16) | w2->index, 0); + w1 = w2; + } + v = v->next; + } + + if (flags & DC_EXEC) { + _new_train_id = wfront->index; + v = vfront; + w1 = wfront; + while (w1 != NULL && v != NULL) { + w1->spritenum = v->spritenum; // makes sure that multiheaded engines are facing the correct way + if (w1->cargo_type != v->cargo_type) // checks if a refit is needed + needs_refitting = v->cargo_type; + w1 = w1->next; + v = v->next; + } + + } + } + if (flags && DC_EXEC && needs_refitting != 255 && v->type != VEH_Road) { // right now we do not refit road vehicles + if (DoCommandByTile(wfront->tile, wfront->index, needs_refitting, 0, refit_command) != CMD_ERROR) + DoCommandByTile(wfront->tile, wfront->index, needs_refitting, DC_EXEC, refit_command); + } + return total_cost; +} + + /** Give a custom name to your vehicle * @param x,y unused * @param p1 vehicle ID to name