diff --git a/aircraft_cmd.c b/aircraft_cmd.c --- 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 --- a/aircraft_gui.c +++ b/aircraft_gui.c @@ -89,6 +89,15 @@ void CcBuildAircraft(bool success, TileI } } +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_widge { 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 * 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 * 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(Wind /* 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(W } } +/** + * 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 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_widg { 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 --- 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 --- 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 --- 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 d6166f23e22522fddf9bd000bcc0ff162e17ab79..5a784f20e5201f16a27c954d8f0172b990adb843 GIT binary patch literal 23690 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�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 diff --git a/lang/english.txt b/lang/english.txt --- a/lang/english.txt +++ b/lang/english.txt @@ -2404,6 +2404,12 @@ STR_881C_NEW_RAIL_VEHICLES 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 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 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 --- a/roadveh_gui.c +++ b/roadveh_gui.c @@ -230,6 +230,16 @@ static void ShowRoadVehDetailsWindow(Veh 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 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 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_widget { 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 * /* 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, } } +/** + * 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, 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 --- a/ship_gui.c +++ b/ship_gui.c @@ -320,6 +320,15 @@ void CcBuildShip(bool success, TileIndex 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(TileInde 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, W 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)); - 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); + 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 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; - } - } 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; + 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); + } + } } } @@ -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, in } } +/** + * 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, 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 --- a/spritecache.c +++ b/spritecache.c @@ -732,7 +732,7 @@ static const char * const _cached_filena "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 --- 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 --- a/train_cmd.c +++ b/train_cmd.c @@ -568,7 +568,7 @@ void AddRearEngineToMultiheadedTrain(Veh /** 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, 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, 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, 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, /** 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, 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, 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, } } } - // 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 --- a/train_gui.c +++ b/train_gui.c @@ -154,6 +154,17 @@ void CcBuildLoco(bool success, TileIndex 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 /* 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 } } +/** + * 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, 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, /* 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, 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, 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 --- 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