(svn r2764) -Feature: Clone vehicles

-This allows a player to clone an excisting vehicle of his own
  -[fix]: this uncovered an excisting bug in CmdBuildRailVehicle() where depots could build trains of the wrong track type. This is fixed
  -Thanks to Celestar for drawing the sprites and _luca_ for including them in openttd.grf
replace/41b28d7194a279bdc17475d4fbe2ea6ec885a466
bjarni 19 years ago
parent 50e41dc1d1
commit 546c34f2e5

@ -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;

@ -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},

@ -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);

@ -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 */

@ -136,6 +136,9 @@ enum {
CMD_CHANGE_PATCH_SETTING = 114,
CMD_REPLACE_VEHICLE = 115,
CMD_CLONE_VEHICLE = 116,
};
enum {

Binary file not shown.

@ -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

@ -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},

@ -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;
if (v->owner != _local_player)
disabled |= 1<<8 | 1<<7;
w->disabled_state = disabled;
disabled = 0;
/* draw widgets & caption */
SetDParam(0, v->string_id);
SetDParam(1, v->unitnumber);
DrawWindowWidgets(w);
if (v->owner != _local_player)
disabled |= 1<<8 | 1<<7;
w->disabled_state = disabled;
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_LOADING:
case OT_LEAVESTATION:
str = STR_882F_LOADING_UNLOADING;
break;
/* draw widgets & caption */
SetDParam(0, v->string_id);
SetDParam(1, v->unitnumber);
DrawWindowWidgets(w);
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;
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_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));
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);
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;
}
} 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_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, 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},

@ -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

@ -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[])

@ -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 <b>NEEDS TO GO</b>
* @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;

@ -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);

@ -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

Loading…
Cancel
Save