changeset 19247:2cf452279593 draft

(svn r24136) -Feature [FS#4465]: Autoreplace vehicles only when they get old. (Vikthor)
author michi_cc <michi_cc@openttd.org>
date Tue, 17 Apr 2012 19:44:02 +0000
parents 4863c11de674
children 4677208c0fe2
files src/autoreplace.cpp src/autoreplace_base.h src/autoreplace_cmd.cpp src/autoreplace_func.h src/autoreplace_gui.cpp src/lang/english.txt src/saveload/autoreplace_sl.cpp src/saveload/saveload.cpp src/vehicle.cpp src/vehicle_base.h src/widget.cpp src/widget_type.h src/widgets/dropdown.cpp src/window.cpp
diffstat 14 files changed, 126 insertions(+), 49 deletions(-) [+]
line wrap: on
line diff
--- a/src/autoreplace.cpp
+++ b/src/autoreplace.cpp
@@ -57,16 +57,18 @@
  * @param erl The renewlist to search in.
  * @param engine Engine type to be replaced.
  * @param group The group related to this replacement.
+ * @param[out] replace_when_old Set to true if the replacement should be done when old.
  * @return The engine type to replace with, or INVALID_ENGINE if no
  * replacement is in the list.
  */
-EngineID EngineReplacement(EngineRenewList erl, EngineID engine, GroupID group)
+EngineID EngineReplacement(EngineRenewList erl, EngineID engine, GroupID group, bool *replace_when_old)
 {
 	const EngineRenew *er = GetEngineReplacement(erl, engine, group);
 	if (er == NULL && (group == DEFAULT_GROUP || (Group::IsValidID(group) && !Group::Get(group)->replace_protection))) {
 		/* We didn't find anything useful in the vehicle's own group so we will try ALL_GROUP */
 		er = GetEngineReplacement(erl, engine, ALL_GROUP);
 	}
+	if (replace_when_old != NULL) *replace_when_old = er == NULL ? false : er->replace_when_old;
 	return er == NULL ? INVALID_ENGINE : er->to;
 }
 
@@ -76,10 +78,11 @@
  * @param old_engine The original engine type.
  * @param new_engine The replacement engine type.
  * @param group The group related to this replacement.
+ * @param replace_when_old Replace when old or always?
  * @param flags The calling command flags.
  * @return 0 on success, CMD_ERROR on failure.
  */
-CommandCost AddEngineReplacement(EngineRenewList *erl, EngineID old_engine, EngineID new_engine, GroupID group, DoCommandFlag flags)
+CommandCost AddEngineReplacement(EngineRenewList *erl, EngineID old_engine, EngineID new_engine, GroupID group, bool replace_when_old, DoCommandFlag flags)
 {
 	/* Check if the old vehicle is already in the list */
 	EngineRenew *er = GetEngineReplacement(*erl, old_engine, group);
@@ -93,6 +96,7 @@
 	if (flags & DC_EXEC) {
 		er = new EngineRenew(old_engine, new_engine);
 		er->group_id = group;
+		er->replace_when_old = replace_when_old;
 
 		/* Insert before the first element */
 		er->next = (EngineRenew *)(*erl);
--- a/src/autoreplace_base.h
+++ b/src/autoreplace_base.h
@@ -37,6 +37,7 @@
 	EngineID to;
 	EngineRenew *next;
 	GroupID group_id;
+	bool replace_when_old; ///< Do replacement only when vehicle is old.
 
 	EngineRenew(EngineID from = INVALID_ENGINE, EngineID to = INVALID_ENGINE) : from(from), to(to) {}
 	~EngineRenew() {}
--- a/src/autoreplace_cmd.cpp
+++ b/src/autoreplace_cmd.cpp
@@ -225,10 +225,11 @@
  * Get the EngineID of the replacement for a vehicle
  * @param v The vehicle to find a replacement for
  * @param c The vehicle's owner (it's faster to forward the pointer than refinding it)
+ * @param always_replace Always replace, even if not old.
  * @param [out] e the EngineID of the replacement. INVALID_ENGINE if no replacement is found
  * @return Error if the engine to build is not available
  */
-static CommandCost GetNewEngineType(const Vehicle *v, const Company *c, EngineID &e)
+static CommandCost GetNewEngineType(const Vehicle *v, const Company *c, bool always_replace, EngineID &e)
 {
 	assert(v->type != VEH_TRAIN || !v->IsArticulatedPart());
 
@@ -239,7 +240,9 @@
 		return CommandCost();
 	}
 
-	e = EngineReplacementForCompany(c, v->engine_type, v->group_id);
+	bool replace_when_old;
+	e = EngineReplacementForCompany(c, v->engine_type, v->group_id, &replace_when_old);
+	if (!always_replace && replace_when_old && !v->NeedsAutorenewing(c, false)) e = INVALID_ENGINE;
 
 	/* Autoreplace, if engine is available */
 	if (e != INVALID_ENGINE && IsEngineBuildable(e, v->type, _current_company)) {
@@ -271,7 +274,7 @@
 	/* Shall the vehicle be replaced? */
 	const Company *c = Company::Get(_current_company);
 	EngineID e;
-	CommandCost cost = GetNewEngineType(old_veh, c, e);
+	CommandCost cost = GetNewEngineType(old_veh, c, true, e);
 	if (cost.Failed()) return cost;
 	if (e == INVALID_ENGINE) return CommandCost(); // neither autoreplace is set, nor autorenew is triggered
 
@@ -683,7 +686,7 @@
 	bool any_replacements = false;
 	while (w != NULL) {
 		EngineID e;
-		CommandCost cost = GetNewEngineType(w, c, e);
+		CommandCost cost = GetNewEngineType(w, c, false, e);
 		if (cost.Failed()) return cost;
 		any_replacements |= (e != INVALID_ENGINE);
 		w = (!free_wagon && w->type == VEH_TRAIN ? Train::From(w)->GetNextUnit() : NULL);
@@ -736,6 +739,7 @@
  * @param tile unused
  * @param flags operation to perform
  * @param p1 packed data
+ *   - bit      0 = replace when engine gets old?
  *   - bits 16-31 = engine group
  * @param p2 packed data
  *   - bits  0-15 = old engine type
@@ -760,7 +764,7 @@
 		if (!Engine::IsValidID(new_engine_type)) return CMD_ERROR;
 		if (!CheckAutoreplaceValidity(old_engine_type, new_engine_type, _current_company)) return CMD_ERROR;
 
-		cost = AddEngineReplacementForCompany(c, old_engine_type, new_engine_type, id_g, flags);
+		cost = AddEngineReplacementForCompany(c, old_engine_type, new_engine_type, id_g, HasBit(p1, 0), flags);
 	} else {
 		cost = RemoveEngineReplacementForCompany(c, old_engine_type, id_g, flags);
 	}
--- a/src/autoreplace_func.h
+++ b/src/autoreplace_func.h
@@ -16,8 +16,8 @@
 #include "company_base.h"
 
 void RemoveAllEngineReplacement(EngineRenewList *erl);
-EngineID EngineReplacement(EngineRenewList erl, EngineID engine, GroupID group);
-CommandCost AddEngineReplacement(EngineRenewList *erl, EngineID old_engine, EngineID new_engine, GroupID group, DoCommandFlag flags);
+EngineID EngineReplacement(EngineRenewList erl, EngineID engine, GroupID group, bool *replace_when_old = NULL);
+CommandCost AddEngineReplacement(EngineRenewList *erl, EngineID old_engine, EngineID new_engine, GroupID group, bool replace_when_old, DoCommandFlag flags);
 CommandCost RemoveEngineReplacement(EngineRenewList *erl, EngineID engine, GroupID group, DoCommandFlag flags);
 
 /**
@@ -34,12 +34,13 @@
  * @param c company.
  * @param engine Engine type.
  * @param group The group related to this replacement.
+ * @param[out] replace_when_old Set to true if the replacement should be done when old.
  * @return The engine type to replace with, or INVALID_ENGINE if no
  * replacement is in the list.
  */
-static inline EngineID EngineReplacementForCompany(const Company *c, EngineID engine, GroupID group)
+static inline EngineID EngineReplacementForCompany(const Company *c, EngineID engine, GroupID group, bool *replace_when_old = NULL)
 {
-	return EngineReplacement(c->engine_renew_list, engine, group);
+	return EngineReplacement(c->engine_renew_list, engine, group, replace_when_old);
 }
 
 /**
@@ -55,17 +56,32 @@
 }
 
 /**
+ * Check if a company has a replacement set up for the given engine when it gets old.
+ * @param c Company.
+ * @param engine Engine type to be replaced.
+ * @param group The group related to this replacement.
+ * @return True if a replacement when old was set up, false otherwise.
+ */
+static inline bool EngineHasReplacementWhenOldForCompany(const Company *c, EngineID engine, GroupID group)
+{
+	bool replace_when_old;
+	EngineReplacement(c->engine_renew_list, engine, group, &replace_when_old);
+	return replace_when_old;
+}
+
+/**
  * Add an engine replacement for the company.
  * @param c Company.
  * @param old_engine The original engine type.
  * @param new_engine The replacement engine type.
  * @param group The group related to this replacement.
+ * @param replace_when_old Replace when old or always?
  * @param flags The calling command flags.
  * @return 0 on success, CMD_ERROR on failure.
  */
-static inline CommandCost AddEngineReplacementForCompany(Company *c, EngineID old_engine, EngineID new_engine, GroupID group, DoCommandFlag flags)
+static inline CommandCost AddEngineReplacementForCompany(Company *c, EngineID old_engine, EngineID new_engine, GroupID group, bool replace_when_old, DoCommandFlag flags)
 {
-	return AddEngineReplacement(&c->engine_renew_list, old_engine, new_engine, group, flags);
+	return AddEngineReplacement(&c->engine_renew_list, old_engine, new_engine, group, replace_when_old, flags);
 }
 
 /**
--- a/src/autoreplace_gui.cpp
+++ b/src/autoreplace_gui.cpp
@@ -24,6 +24,7 @@
 #include "settings_func.h"
 #include "core/geometry_func.hpp"
 #include "rail_gui.h"
+#include "widgets/dropdown_func.h"
 
 #include "widgets/autoreplace_widget.h"
 
@@ -67,6 +68,12 @@
 	InvalidateWindowClassesData(WC_BUILD_VEHICLE); // The build windows needs updating as well
 }
 
+static const StringID _start_replace_dropdown[] = {
+	STR_REPLACE_VEHICLES_NOW,
+	STR_REPLACE_VEHICLES_WHEN_OLD,
+	INVALID_STRING_ID
+};
+
 /**
  * Window for the autoreplacing of vehicles.
  */
@@ -170,6 +177,18 @@
 		this->reset_sel_engine = false;
 	}
 
+	/**
+	 * Handle click on the start replace button.
+	 * @param replace_when_old Replace now or only when old?
+	 */
+	void ReplaceClick_StartReplace(bool replace_when_old)
+	{
+		EngineID veh_from = this->sel_engine[0];
+		EngineID veh_to = this->sel_engine[1];
+		DoCommandP(0, (replace_when_old ? 1 : 0) | (this->sel_group << 16), veh_from + (veh_to << 16), CMD_SET_AUTOREPLACE);
+		this->SetDirty();
+	}
+
 public:
 	ReplaceVehicleWindow(const WindowDesc *desc, VehicleType vehicletype, GroupID id_g) : Window()
 	{
@@ -270,6 +289,17 @@
 				*size = maxdim(*size, d);
 				break;
 			}
+
+			case WID_RV_START_REPLACE: {
+				Dimension d = GetStringBoundingBox(STR_REPLACE_VEHICLES_START);
+				for (int i = 0; _start_replace_dropdown[i] != INVALID_STRING_ID; i++) {
+					d = maxdim(d, GetStringBoundingBox(_start_replace_dropdown[i]));
+				}
+				d.width += padding.width;
+				d.height += padding.height;
+				*size = maxdim(*size, d);
+				break;
+			}
 		}
 	}
 
@@ -301,8 +331,10 @@
 					if (!EngineHasReplacementForCompany(c, this->sel_engine[0], this->sel_group)) {
 						SetDParam(0, STR_REPLACE_NOT_REPLACING);
 					} else {
-						SetDParam(0, STR_ENGINE_NAME);
-						SetDParam(1, EngineReplacementForCompany(c, this->sel_engine[0], this->sel_group));
+						bool when_old = false;
+						EngineID e = EngineReplacementForCompany(c, this->sel_engine[0], this->sel_group, &when_old);
+						SetDParam(0, when_old ? STR_REPLACE_REPLACING_WHEN_OLD : STR_ENGINE_NAME);
+						SetDParam(1, e);
 					}
 				} else {
 					SetDParam(0, STR_REPLACE_NOT_REPLACING_VEHICLE_SELECTED);
@@ -334,13 +366,11 @@
 
 		/* Disable the "Start Replacing" button if:
 		 *    Either engines list is empty
-		 * or The selected replacement engine has a replacement (to prevent loops)
-		 * or The right engines list (new replacement) has the existing replacement vehicle selected */
+		 * or The selected replacement engine has a replacement (to prevent loops). */
 		this->SetWidgetDisabledState(WID_RV_START_REPLACE,
 										this->sel_engine[0] == INVALID_ENGINE ||
 										this->sel_engine[1] == INVALID_ENGINE ||
-										EngineReplacementForCompany(c, this->sel_engine[1], this->sel_group) != INVALID_ENGINE ||
-										EngineReplacementForCompany(c, this->sel_engine[0], this->sel_group) == this->sel_engine[1]);
+										EngineReplacementForCompany(c, this->sel_engine[1], this->sel_group) != INVALID_ENGINE);
 
 		/* Disable the "Stop Replacing" button if:
 		 *   The left engines list (existing vehicle) is empty
@@ -349,9 +379,6 @@
 										this->sel_engine[0] == INVALID_ENGINE ||
 										!EngineHasReplacementForCompany(c, this->sel_engine[0], this->sel_group));
 
-		/* now the actual drawing of the window itself takes place */
-		SetDParam(0, STR_REPLACE_VEHICLE_TRAIN + this->window_number);
-
 		if (this->window_number == VEH_TRAIN) {
 			/* sets the colour of that art thing */
 			this->GetWidget<NWidgetCore>(WID_RV_TRAIN_FLUFF_LEFT)->colour  = _company_colours[_local_company];
@@ -401,10 +428,13 @@
 				break;
 
 			case WID_RV_START_REPLACE: { // Start replacing
-				EngineID veh_from = this->sel_engine[0];
-				EngineID veh_to = this->sel_engine[1];
-				DoCommandP(0, this->sel_group << 16, veh_from + (veh_to << 16), CMD_SET_AUTOREPLACE);
-				this->SetDirty();
+				if (this->GetWidget<NWidgetLeaf>(widget)->ButtonHit(pt)) {
+					this->HandleButtonClick(WID_RV_START_REPLACE);
+					ReplaceClick_StartReplace(false);
+				} else {
+					bool replacment_when_old = EngineHasReplacementWhenOldForCompany(Company::Get(_local_company), this->sel_engine[0], this->sel_group);
+					ShowDropDownMenu(this, _start_replace_dropdown, replacment_when_old ? 1 : 0, WID_RV_START_REPLACE, !this->replace_engines ? 1 << 1 : 0, 0);
+				}
 				break;
 			}
 
@@ -441,17 +471,26 @@
 
 	virtual void OnDropdownSelect(int widget, int index)
 	{
-		RailType temp = (RailType)index;
-		if (temp == sel_railtype) return; // we didn't select a new one. No need to change anything
-		sel_railtype = temp;
-		/* Reset scrollbar positions */
-		this->vscroll[0]->SetPosition(0);
-		this->vscroll[1]->SetPosition(0);
-		/* Rebuild the lists */
-		this->engines[0].ForceRebuild();
-		this->engines[1].ForceRebuild();
-		this->reset_sel_engine = true;
-		this->SetDirty();
+		switch (widget) {
+			case WID_RV_TRAIN_RAILTYPE_DROPDOWN: {
+				RailType temp = (RailType)index;
+				if (temp == sel_railtype) return; // we didn't select a new one. No need to change anything
+				sel_railtype = temp;
+				/* Reset scrollbar positions */
+				this->vscroll[0]->SetPosition(0);
+				this->vscroll[1]->SetPosition(0);
+				/* Rebuild the lists */
+				this->engines[0].ForceRebuild();
+				this->engines[1].ForceRebuild();
+				this->reset_sel_engine = true;
+				this->SetDirty();
+				break;
+			}
+
+			case WID_RV_START_REPLACE:
+				this->ReplaceClick_StartReplace(index != 0);
+				break;
+		}
 	}
 
 	virtual void OnResize()
@@ -497,7 +536,7 @@
 		NWidget(WWT_PANEL, COLOUR_GREY, WID_RV_RIGHT_DETAILS), SetMinimalSize(228, 102), SetResize(1, 0), EndContainer(),
 	EndContainer(),
 	NWidget(NWID_HORIZONTAL),
-		NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_RV_START_REPLACE), SetMinimalSize(139, 12), SetDataTip(STR_REPLACE_VEHICLES_START, STR_REPLACE_HELP_START_BUTTON),
+		NWidget(NWID_PUSHBUTTON_DROPDOWN, COLOUR_GREY, WID_RV_START_REPLACE), SetMinimalSize(139, 12), SetDataTip(STR_REPLACE_VEHICLES_START, STR_REPLACE_HELP_START_BUTTON),
 		NWidget(WWT_PANEL, COLOUR_GREY, WID_RV_INFO_TAB), SetMinimalSize(167, 12), SetDataTip(0x0, STR_REPLACE_HELP_REPLACE_INFO_TAB), SetResize(1, 0),
 		EndContainer(),
 		NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_RV_STOP_REPLACE), SetMinimalSize(150, 12), SetDataTip(STR_REPLACE_VEHICLES_STOP, STR_REPLACE_HELP_STOP_BUTTON),
@@ -537,7 +576,7 @@
 		NWidget(WWT_PANEL, COLOUR_GREY, WID_RV_RIGHT_DETAILS), SetMinimalSize(228, 92), SetResize(1, 0), EndContainer(),
 	EndContainer(),
 	NWidget(NWID_HORIZONTAL),
-		NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_RV_START_REPLACE), SetMinimalSize(139, 12), SetDataTip(STR_REPLACE_VEHICLES_START, STR_REPLACE_HELP_START_BUTTON),
+		NWidget(NWID_PUSHBUTTON_DROPDOWN, COLOUR_GREY, WID_RV_START_REPLACE), SetMinimalSize(139, 12), SetDataTip(STR_REPLACE_VEHICLES_START, STR_REPLACE_HELP_START_BUTTON),
 		NWidget(WWT_PANEL, COLOUR_GREY, WID_RV_INFO_TAB), SetMinimalSize(167, 12), SetDataTip(0x0, STR_REPLACE_HELP_REPLACE_INFO_TAB), SetResize(1, 0), EndContainer(),
 		NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_RV_STOP_REPLACE), SetMinimalSize(138, 12), SetDataTip(STR_REPLACE_VEHICLES_STOP, STR_REPLACE_HELP_STOP_BUTTON),
 		NWidget(WWT_RESIZEBOX, COLOUR_GREY),
--- a/src/lang/english.txt
+++ b/src/lang/english.txt
@@ -3112,9 +3112,12 @@
 STR_REPLACE_HELP_RIGHT_ARRAY                                    :{BLACK}Select the new engine type you would like to use in place of the left selected engine type
 
 STR_REPLACE_VEHICLES_START                                      :{BLACK}Start Replacing Vehicles
+STR_REPLACE_VEHICLES_NOW                                        :Replace all vehicles now
+STR_REPLACE_VEHICLES_WHEN_OLD                                   :Replace only old vehicles
 STR_REPLACE_HELP_START_BUTTON                                   :{BLACK}Press to begin replacement of the left selected engine type with the right selected engine type
 STR_REPLACE_NOT_REPLACING                                       :{BLACK}Not replacing
 STR_REPLACE_NOT_REPLACING_VEHICLE_SELECTED                      :{BLACK}No vehicle selected
+STR_REPLACE_REPLACING_WHEN_OLD                                  :{ENGINE} when old
 STR_REPLACE_VEHICLES_STOP                                       :{BLACK}Stop Replacing Vehicles
 STR_REPLACE_HELP_STOP_BUTTON                                    :{BLACK}Press to stop the replacement of the engine type selected on the left
 
--- a/src/saveload/autoreplace_sl.cpp
+++ b/src/saveload/autoreplace_sl.cpp
@@ -20,6 +20,7 @@
 
 	    SLE_REF(EngineRenew, next,     REF_ENGINE_RENEWS),
 	SLE_CONDVAR(EngineRenew, group_id, SLE_UINT16, 60, SL_MAX_VERSION),
+	SLE_CONDVAR(EngineRenew, replace_when_old, SLE_BOOL, 175, SL_MAX_VERSION),
 	SLE_END()
 };
 
--- a/src/saveload/saveload.cpp
+++ b/src/saveload/saveload.cpp
@@ -238,8 +238,9 @@
  *  172   23947
  *  173   23967   1.2.0-RC1
  *  174   23973   1.2.x
+ *  175   24136
  */
-extern const uint16 SAVEGAME_VERSION = 174; ///< Current savegame version of OpenTTD.
+extern const uint16 SAVEGAME_VERSION = 175; ///< Current savegame version of OpenTTD.
 
 SavegameType _savegame_type; ///< type of savegame we are loading
 
--- a/src/vehicle.cpp
+++ b/src/vehicle.cpp
@@ -67,9 +67,10 @@
 /**
  * Function to tell if a vehicle needs to be autorenewed
  * @param *c The vehicle owner
+ * @param use_renew_setting Should the company renew setting be considered?
  * @return true if the vehicle is old enough for replacement
  */
-bool Vehicle::NeedsAutorenewing(const Company *c) const
+bool Vehicle::NeedsAutorenewing(const Company *c, bool use_renew_setting) const
 {
 	/* We can always generate the Company pointer when we have the vehicle.
 	 * However this takes time and since the Company pointer is often present
@@ -77,7 +78,7 @@
 	 * argument rather than finding it again. */
 	assert(c == Company::Get(this->owner));
 
-	if (!c->settings.engine_renew) return false;
+	if (use_renew_setting && !c->settings.engine_renew) return false;
 	if (this->age - this->max_age < (c->settings.engine_renew_months * 30)) return false;
 
 	/* Only engines need renewing */
@@ -131,10 +132,13 @@
 	if (needed_money > c->money) return false;
 
 	for (const Vehicle *v = this; v != NULL; v = (v->type == VEH_TRAIN) ? Train::From(v)->GetNextUnit() : NULL) {
-		EngineID new_engine = EngineReplacementForCompany(c, v->engine_type, v->group_id);
+		bool replace_when_old = false;
+		EngineID new_engine = EngineReplacementForCompany(c, v->engine_type, v->group_id, &replace_when_old);
 
 		/* Check engine availability */
 		if (new_engine == INVALID_ENGINE || !HasBit(Engine::Get(new_engine)->company_avail, v->owner)) continue;
+		/* Is the vehicle old if we are not always replacing? */
+		if (replace_when_old && !v->NeedsAutorenewing(c, false)) continue;
 
 		/* Check refittability */
 		uint32 available_cargo_types, union_mask;
--- a/src/vehicle_base.h
+++ b/src/vehicle_base.h
@@ -603,7 +603,7 @@
 
 	bool HandleBreakdown();
 
-	bool NeedsAutorenewing(const Company *c) const;
+	bool NeedsAutorenewing(const Company *c, bool use_renew_setting = true) const;
 
 	bool NeedsServicing() const;
 	bool NeedsAutomaticServicing() const;
--- a/src/widget.cpp
+++ b/src/widget.cpp
@@ -2050,6 +2050,7 @@
 		case WWT_TEXT:
 		case WWT_MATRIX:
 		case NWID_BUTTON_DROPDOWN:
+		case NWID_PUSHBUTTON_DROPDOWN:
 		case WWT_ARROWBTN:
 		case WWT_PUSHARROWBTN:
 			this->SetFill(0, 0);
@@ -2254,7 +2255,8 @@
 			break;
 		}
 		case WWT_DROPDOWN:
-		case NWID_BUTTON_DROPDOWN: {
+		case NWID_BUTTON_DROPDOWN:
+		case NWID_PUSHBUTTON_DROPDOWN: {
 			static const Dimension extra = {WD_DROPDOWNTEXT_LEFT + WD_DROPDOWNTEXT_RIGHT, WD_DROPDOWNTEXT_TOP + WD_DROPDOWNTEXT_BOTTOM};
 			padding = &extra;
 			if (this->index >= 0) w->SetStringParameters(this->index);
@@ -2380,6 +2382,7 @@
 			break;
 
 		case NWID_BUTTON_DROPDOWN:
+		case NWID_PUSHBUTTON_DROPDOWN:
 			if (this->index >= 0) w->SetStringParameters(this->index);
 			DrawButtonDropdown(r, this->colour, clicked, (this->disp_flags & ND_DROPDOWN_ACTIVE) != 0, this->widget_data);
 			break;
@@ -2582,7 +2585,7 @@
 
 			default:
 				if (*dest != NULL) return num_used;
-				assert((parts->type & WWT_MASK) < WWT_LAST || parts->type == NWID_BUTTON_DROPDOWN);
+				assert((parts->type & WWT_MASK) < WWT_LAST || (parts->type & WWT_MASK) == NWID_BUTTON_DROPDOWN);
 				*dest = new NWidgetLeaf(parts->type, parts->u.widget.colour, parts->u.widget.index, 0x0, STR_NULL);
 				*biggest_index = max(*biggest_index, (int)parts->u.widget.index);
 				break;
--- a/src/widget_type.h
+++ b/src/widget_type.h
@@ -102,6 +102,7 @@
 	WWT_PUSHTXTBTN    = WWT_TEXTBTN  | WWB_PUSHBUTTON,
 	WWT_PUSHIMGBTN    = WWT_IMGBTN   | WWB_PUSHBUTTON,
 	WWT_PUSHARROWBTN  = WWT_ARROWBTN | WWB_PUSHBUTTON,
+	NWID_PUSHBUTTON_DROPDOWN = NWID_BUTTON_DROPDOWN | WWB_PUSHBUTTON,
 };
 
 /** Different forms of sizing nested widgets, using NWidgetBase::AssignSizePosition() */
--- a/src/widgets/dropdown.cpp
+++ b/src/widgets/dropdown.cpp
@@ -172,7 +172,7 @@
 		if (w2 != NULL) {
 			if (w2->nested_array != NULL) {
 				NWidgetCore *nwi2 = w2->GetWidget<NWidgetCore>(this->parent_button);
-				if (nwi2->type == NWID_BUTTON_DROPDOWN) {
+				if ((nwi2->type & WWT_MASK) == NWID_BUTTON_DROPDOWN) {
 					nwi2->disp_flags &= ~ND_DROPDOWN_ACTIVE;
 				} else {
 					w2->RaiseWidget(this->parent_button);
@@ -358,7 +358,7 @@
 	wi_rect.bottom = nwi->pos_y + nwi->current_y - 1;
 	wi_colour = nwi->colour;
 
-	if (nwi->type == NWID_BUTTON_DROPDOWN) {
+	if ((nwi->type & WWT_MASK) == NWID_BUTTON_DROPDOWN) {
 		nwi->disp_flags |= ND_DROPDOWN_ACTIVE;
 	} else {
 		w->LowerWidget(button);
--- a/src/window.cpp
+++ b/src/window.cpp
@@ -305,7 +305,7 @@
 void Window::RaiseButtons(bool autoraise)
 {
 	for (uint i = 0; i < this->nested_array_size; i++) {
-		if (this->nested_array[i] != NULL && (this->nested_array[i]->type & ~WWB_PUSHBUTTON) < WWT_LAST &&
+		if (this->nested_array[i] != NULL && ((this->nested_array[i]->type & ~WWB_PUSHBUTTON) < WWT_LAST || this->nested_array[i]->type == NWID_PUSHBUTTON_DROPDOWN) &&
 				(!autoraise || (this->nested_array[i]->type & WWB_PUSHBUTTON)) && this->IsWidgetLowered(i)) {
 			this->RaiseWidget(i);
 			this->SetWidgetDirty(i);