diff --git a/command.c b/command.c index 5ba07db296..18656a52ec 100644 --- a/command.c +++ b/command.c @@ -113,6 +113,7 @@ DEF_COMMAND(CmdStartStopRoadVeh); DEF_COMMAND(CmdSellRoadVeh); DEF_COMMAND(CmdSendRoadVehToDepot); DEF_COMMAND(CmdTurnRoadVeh); +DEF_COMMAND(CmdRefitRoadVeh); DEF_COMMAND(CmdPause); @@ -245,7 +246,7 @@ static const Command _command_proc_table[] = { {CmdSellRoadVeh, 0}, /* 69 */ {CmdSendRoadVehToDepot, 0}, /* 70 */ {CmdTurnRoadVeh, 0}, /* 71 */ - {NULL, 0}, /* 72 */ + {CmdRefitRoadVeh, 0}, /* 72 */ {CmdPause, CMD_SERVER}, /* 73 */ diff --git a/command.h b/command.h index db12972f51..05e9e0f9dc 100644 --- a/command.h +++ b/command.h @@ -91,6 +91,7 @@ enum { CMD_SELL_ROAD_VEH = 69, CMD_SEND_ROADVEH_TO_DEPOT = 70, CMD_TURN_ROADVEH = 71, + CMD_REFIT_ROAD_VEH = 72, CMD_PAUSE = 73, diff --git a/lang/english.txt b/lang/english.txt index bc60eb0121..94626a5c8c 100644 --- a/lang/english.txt +++ b/lang/english.txt @@ -2591,6 +2591,11 @@ STR_9037_CAN_T_RENAME_ROAD_VEHICLE :{WHITE}Can't re STR_9038_GO_TO_ROADVEH_DEPOT :Go to {TOWN} Road Vehicle Depot STR_SERVICE_AT_ROADVEH_DEPOT :Service at {TOWN} Road Vehicle Depot +STR_REFIT_ROAD_VEHICLE_TO_CARRY :{BLACK}Refit road vehicle to carry a different cargo type +STR_REFIT_ROAD_VEHICLE :{BLACK}Refit road vehicle +STR_REFIT_ROAD_VEHICLE_TO_CARRY_HIGHLIGHTED :{BLACK}Refit road vehicle to carry highlighted cargo type +STR_REFIT_ROAD_VEHICLE_CAN_T :{WHITE}Can't refit road vehicle... + ##id 0x9800 STR_9800_DOCK_CONSTRUCTION :Dock construction STR_9801_DOCK_CONSTRUCTION :{WHITE}Dock construction diff --git a/roadveh_cmd.c b/roadveh_cmd.c index bd99fd2274..fc939df196 100644 --- a/roadveh_cmd.c +++ b/roadveh_cmd.c @@ -23,6 +23,7 @@ #include "depot.h" #include "tunnel_map.h" #include "vehicle_gui.h" +#include "newgrf_callbacks.h" #include "newgrf_engine.h" #include "yapf/yapf.h" @@ -1721,3 +1722,87 @@ void RoadVehiclesYearlyLoop(void) } } } + +/** Refit a road vehicle to the specified cargo type + * @param tile unused + * @param p1 Vehicle ID of the vehicle to refit + * @param p2 Bitstuffed elements + * - p2 = (bit 0-7) - the new cargo type to refit to + * - p2 = (bit 8-15) - the new cargo subtype to refit to + */ +int32 CmdRefitRoadVeh(TileIndex tile, uint32 flags, uint32 p1, uint32 p2) +{ + Vehicle *v; + int32 cost; + CargoID new_cid = GB(p2, 0, 8); + byte new_subtype = GB(p2, 8, 8); + uint16 capacity = CALLBACK_FAILED; + + if (!IsVehicleIndex(p1)) return CMD_ERROR; + + v = GetVehicle(p1); + + if (v->type != VEH_Road || !CheckOwnership(v->owner)) return CMD_ERROR; + if (!IsRoadVehInDepotStopped(v)) return_cmd_error(STR_9013_MUST_BE_STOPPED_INSIDE); + + if (new_cid > NUM_CARGO || !CanRefitTo(v->engine_type, new_cid)) return CMD_ERROR; + + SET_EXPENSES_TYPE(EXPENSES_ROADVEH_RUN); + + if (HASBIT(EngInfo(v->engine_type)->callbackmask, CBM_REFIT_CAPACITY)) { + /* Back up the cargo type */ + CargoID temp_cid = v->cargo_type; + byte temp_subtype = v->cargo_subtype; + v->cargo_type = new_cid; + v->cargo_subtype = new_subtype; + + /* Check the refit capacity callback */ + capacity = GetVehicleCallback(CBID_VEHICLE_REFIT_CAPACITY, 0, 0, v->engine_type, v); + + /* Restore the original cargo type */ + v->cargo_type = temp_cid; + v->cargo_subtype = temp_subtype; + } + + if (capacity == CALLBACK_FAILED) { + /* callback failed or not used, use default capacity */ + const RoadVehicleInfo *rvi = RoadVehInfo(v->engine_type); + + CargoID old_cid = rvi->cargo_type; + /* normally, the capacity depends on the cargo type, a vehicle can + * carry twice as much mail/goods as normal cargo, and four times as + * many passengers + */ + capacity = rvi->capacity; + switch (old_cid) { + case CT_PASSENGERS: break; + case CT_MAIL: + case CT_GOODS: capacity *= 2; break; + default: capacity *= 4; break; + } + switch (new_cid) { + case CT_PASSENGERS: break; + case CT_MAIL: + case CT_GOODS: capacity /= 2; break; + default: capacity /= 4; break; + } + } + _returned_refit_capacity = capacity; + + cost = 0; + if (IS_HUMAN_PLAYER(v->owner) && new_cid != v->cargo_type) { + cost = _price.roadveh_base >> 7; + } + + if (flags & DC_EXEC) { + v->cargo_cap = capacity; + v->cargo_count = (v->cargo_type == new_cid) ? min(capacity, v->cargo_count) : 0; + v->cargo_type = new_cid; + v->cargo_subtype = new_subtype; + InvalidateWindow(WC_VEHICLE_DETAILS, v->index); + InvalidateWindow(WC_VEHICLE_DEPOT, v->tile); + RebuildVehicleLists(); + } + + return cost; +} diff --git a/roadveh_gui.c b/roadveh_gui.c index 73052620c3..88b525aa24 100644 --- a/roadveh_gui.c +++ b/roadveh_gui.c @@ -30,6 +30,7 @@ void DrawRoadVehPurchaseInfo(int x, int y, EngineID engine_number) { const RoadVehicleInfo *rvi = RoadVehInfo(engine_number); const Engine* e = GetEngine(engine_number); + bool refittable = (_engine_info[engine_number].refit_mask != 0); YearMonthDay ymd; ConvertDayToYMD(&ymd, e->intro_date); @@ -47,7 +48,7 @@ void DrawRoadVehPurchaseInfo(int x, int y, EngineID engine_number) /* Cargo type + capacity */ SetDParam(0, _cargoc.names_long[rvi->cargo_type]); SetDParam(1, rvi->capacity); - SetDParam(2, STR_EMPTY); + SetDParam(2, refittable ? STR_9842_REFITTABLE : STR_EMPTY); DrawString(x, y, STR_PURCHASE_INFO_CAPACITY, 0); y += 10; @@ -73,6 +74,87 @@ static void DrawRoadVehImage(const Vehicle *v, int x, int y, VehicleID selection } } +static void RoadVehRefitWndProc(Window *w, WindowEvent *e) +{ + switch (e->event) { + case WE_PAINT: { + const Vehicle *v = GetVehicle(w->window_number); + + SetDParam(0, v->string_id); + SetDParam(1, v->unitnumber); + DrawWindowWidgets(w); + + DrawString(1, 15, STR_983F_SELECT_CARGO_TYPE_TO_CARRY, 0); + + WP(w,refit_d).cargo = DrawVehicleRefitWindow(v, WP(w,refit_d).sel); + + if (WP(w,refit_d).cargo != CT_INVALID) { + int32 cost = DoCommand(v->tile, v->index, WP(w,refit_d).cargo, DC_QUERY_COST, CMD_REFIT_ROAD_VEH); + if (!CmdFailed(cost)) { + SetDParam(0, _cargoc.names_long[WP(w,refit_d).cargo]); + SetDParam(1, _returned_refit_capacity); + SetDParam(2, cost); + DrawString(1, 137, STR_9840_NEW_CAPACITY_COST_OF_REFIT, 0); + } + } + + break; + } + + case WE_CLICK: + switch (e->click.widget) { + case 2: { /* List box */ + int y = e->click.pt.y - 25; + if (y >= 0) { + WP(w,refit_d).sel = y / 10; + SetWindowDirty(w); + } + + break; + } + + case 4: /* Refit button */ + if (WP(w,refit_d).cargo != CT_INVALID) { + const Vehicle *v = GetVehicle(w->window_number); + if (DoCommandP(v->tile, v->index, WP(w,refit_d).cargo, NULL, CMD_REFIT_ROAD_VEH | CMD_MSG(STR_REFIT_ROAD_VEHICLE_CAN_T))) + DeleteWindow(w); + } + break; + } + break; + } +} + +static const Widget _road_veh_refit_widgets[] = { +{ WWT_CLOSEBOX, RESIZE_NONE, 14, 0, 10, 0, 13, STR_00C5, STR_018B_CLOSE_WINDOW }, +{ WWT_CAPTION, RESIZE_NONE, 14, 11, 239, 0, 13, STR_983B_REFIT, STR_018C_WINDOW_TITLE_DRAG_THIS }, +{ WWT_IMGBTN, RESIZE_NONE, 14, 0, 239, 14, 135, 0x0, STR_983D_SELECT_TYPE_OF_CARGO_FOR }, +{ WWT_IMGBTN, RESIZE_NONE, 14, 0, 239, 136, 157, 0x0, STR_NULL }, +{ WWT_PUSHTXTBTN, RESIZE_NONE, 14, 0, 239, 158, 169, STR_REFIT_ROAD_VEHICLE, STR_REFIT_ROAD_VEHICLE_TO_CARRY_HIGHLIGHTED }, +{ WIDGETS_END }, +}; + +static const WindowDesc _road_veh_refit_desc = { + -1, -1, 240, 170, + WC_VEHICLE_REFIT, WC_VEHICLE_VIEW, + WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET | WDF_UNCLICK_BUTTONS, + _road_veh_refit_widgets, + RoadVehRefitWndProc, +}; + +static void ShowRoadVehRefitWindow(const Vehicle *v) +{ + Window *w; + + DeleteWindowById(WC_VEHICLE_REFIT, v->index); + + _alloc_wnd_parent_num = v->index; + w = AllocateWindowDesc(&_road_veh_refit_desc); + w->window_number = v->index; + w->caption_color = v->owner; + WP(w,refit_d).sel = -1; +} + static void RoadVehDetailsWndProc(Window *w, WindowEvent *e) { switch (e->event) { @@ -235,7 +317,10 @@ static void RoadVehViewWndProc(Window *w, WindowEvent *e) Vehicle *v = GetVehicle(w->window_number); StringID str; - w->disabled_state = (v->owner != _local_player) ? (1<<8 | 1<<7) : 0; + w->disabled_state = (v->owner != _local_player) ? (1 << 8 | 1 << 7 | 1 << 12) : 0; + + /* Disable refit button if vehicle not refittable */ + if (_engine_info[v->engine_type].refit_mask == 0) SETBIT(w->disabled_state, 12); /* draw widgets & caption */ SetDParam(0, v->string_id); @@ -294,7 +379,7 @@ static void RoadVehViewWndProc(Window *w, WindowEvent *e) case 6: /* center main view */ ScrollMainWindowTo(v->x_pos, v->y_pos); break; - case 7: /* goto hangar */ + case 7: /* goto depot */ DoCommandP(v->tile, v->index, 0, NULL, CMD_SEND_ROADVEH_TO_DEPOT | CMD_MSG(STR_9018_CAN_T_SEND_VEHICLE_TO_DEPOT)); break; case 8: /* turn around */ @@ -306,10 +391,12 @@ static void RoadVehViewWndProc(Window *w, WindowEvent *e) case 10: /* show details */ ShowRoadVehDetailsWindow(v); break; - case 11: { - /* clone vehicle */ + case 11: /* clone vehicle */ DoCommandP(v->tile, v->index, _ctrl_pressed ? 1 : 0, CcCloneRoadVeh, CMD_CLONE_VEHICLE | CMD_MSG(STR_9009_CAN_T_BUILD_ROAD_VEHICLE)); - } break; + break; + case 12: /* Refit vehicle */ + ShowRoadVehRefitWindow(v); + break; } } break; @@ -321,6 +408,7 @@ static void RoadVehViewWndProc(Window *w, WindowEvent *e) break; case WE_DESTROY: + DeleteWindowById(WC_VEHICLE_REFIT, w->window_number); DeleteWindowById(WC_VEHICLE_ORDERS, w->window_number); DeleteWindowById(WC_VEHICLE_DETAILS, w->window_number); break; @@ -330,7 +418,7 @@ static void RoadVehViewWndProc(Window *w, WindowEvent *e) Vehicle *v; uint32 h; v = GetVehicle(w->window_number); - h = IsRoadVehInDepotStopped(v) ? 1 << 7 : 1 << 11; + h = IsRoadVehInDepotStopped(v) ? (1 << 7) | (1 << 8) : (1 << 11) | (1 << 12); if (h != w->hidden_state) { w->hidden_state = h; SetWindowDirty(w); @@ -352,6 +440,7 @@ static const Widget _roadveh_view_widgets[] = { { 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_PUSHIMGBTN, RESIZE_LR, 14, 232, 249, 50, 67, 0x2B4, STR_REFIT_ROAD_VEHICLE_TO_CARRY }, { 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 } diff --git a/vehicle.c b/vehicle.c index b7b5212481..3b9a559de3 100644 --- a/vehicle.c +++ b/vehicle.c @@ -59,7 +59,7 @@ static const uint32 _veh_sell_proc_table[] = { static const uint32 _veh_refit_proc_table[] = { CMD_REFIT_RAIL_VEHICLE, - 0, // road vehicles can't be refitted + CMD_REFIT_ROAD_VEH, CMD_REFIT_SHIP, CMD_REFIT_AIRCRAFT, }; @@ -1658,14 +1658,12 @@ static int32 ReplaceVehicle(Vehicle **w, byte flags) *w = new_v; //we changed the vehicle, so MaybeReplaceVehicle needs to work on the new one. Now we tell it what the new one is /* refit if needed */ - if (new_v->type != VEH_Road) { // road vehicles can't be refitted - if (old_v->cargo_type != new_v->cargo_type && old_v->cargo_cap != 0 && new_v->cargo_cap != 0) {// some train engines do not have cargo capacity - // we add the refit cost to cost, so it's added to the cost animation - // it's not in the calculation of having enough money to actually do the replace since it's rather hard to do by design, but since - // we pay for it, it's nice to make the cost animation include it - int32 temp_cost = DoCommand(0, new_v->index, old_v->cargo_type, DC_EXEC, CMD_REFIT_VEH(new_v->type)); - if (!CmdFailed(temp_cost)) cost += temp_cost; - } + if (old_v->cargo_type != new_v->cargo_type && old_v->cargo_cap != 0 && new_v->cargo_cap != 0) {// some train engines do not have cargo capacity + // we add the refit cost to cost, so it's added to the cost animation + // it's not in the calculation of having enough money to actually do the replace since it's rather hard to do by design, but since + // we pay for it, it's nice to make the cost animation include it + int32 temp_cost = DoCommand(0, new_v->index, old_v->cargo_type, DC_EXEC, CMD_REFIT_VEH(new_v->type)); + if (!CmdFailed(temp_cost)) cost += temp_cost; } if (new_v->type == VEH_Train && HASBIT(old_v->u.rail.flags, VRF_REVERSE_DIRECTION) && !IsMultiheaded(new_v) && !(new_v->next != NULL && IsArticulatedPart(new_v->next))) { // we are autorenewing to a single engine, so we will turn it as the old one was turned as well