mirror of
https://github.com/OpenTTD/OpenTTD.git
synced 2025-03-09 07:29:44 +00:00
Feature: Vehicle add-ons can now group engines in purchase list.
Grouped engines are collapsed by default but can be expanded. This allows similar engines to be grouped together to avoid cluttering the list. Suggested uses for this are e.g.: * Liveries; same stats but different paint job. * Re-gearing; engine design is mostly the same but different stats. ... but avoiding complex hidden cargo subtype refit systems. Grouped engines are otherwise separate, so can be independently autoreplaced, even between variants.
This commit is contained in:
parent
94167dfd34
commit
85814b29d4
@ -125,6 +125,10 @@ class ReplaceVehicleWindow : public Window {
|
||||
|
||||
/* Add variants if not folded */
|
||||
if ((item.flags & (EngineDisplayFlags::HasVariants | EngineDisplayFlags::IsFolded)) == EngineDisplayFlags::HasVariants) {
|
||||
/* Add this engine again as a child */
|
||||
if ((item.flags & EngineDisplayFlags::Shaded) == EngineDisplayFlags::None) {
|
||||
target.emplace_back(item.engine_id, item.engine_id, EngineDisplayFlags::None, indent + 1);
|
||||
}
|
||||
AddChildren(source, target, item.engine_id, indent + 1, side);
|
||||
}
|
||||
}
|
||||
@ -617,7 +621,22 @@ public:
|
||||
uint i = this->vscroll[click_side]->GetScrolledRowFromWidget(pt.y, this, widget);
|
||||
size_t engine_count = this->engines[click_side].size();
|
||||
|
||||
EngineID e = engine_count > i ? this->engines[click_side][i].engine_id : INVALID_ENGINE;
|
||||
EngineID e = INVALID_ENGINE;
|
||||
if (i < engine_count) {
|
||||
const auto &item = this->engines[click_side][i];
|
||||
const Rect r = this->GetWidget<NWidgetBase>(widget)->GetCurrentRect().Shrink(WidgetDimensions::scaled.matrix).WithWidth(WidgetDimensions::scaled.hsep_indent * (item.indent + 1), _current_text_dir == TD_RTL);
|
||||
if ((item.flags & EngineDisplayFlags::HasVariants) != EngineDisplayFlags::None && IsInsideMM(r.left, r.right, pt.x)) {
|
||||
/* toggle folded flag on engine */
|
||||
assert(item.variant_id != INVALID_ENGINE);
|
||||
Engine *engine = Engine::Get(item.variant_id);
|
||||
engine->display_flags ^= EngineDisplayFlags::IsFolded;
|
||||
|
||||
InvalidateWindowData(WC_REPLACE_VEHICLE, (VehicleType)this->window_number, 0); // Update the autoreplace window
|
||||
InvalidateWindowClassesData(WC_BUILD_VEHICLE); // The build windows needs updating as well
|
||||
return;
|
||||
}
|
||||
if ((item.flags & EngineDisplayFlags::Shaded) == EngineDisplayFlags::None) e = item.engine_id;
|
||||
}
|
||||
|
||||
/* If Ctrl is pressed on the left side and we don't have any engines of the selected type, stop autoreplacing.
|
||||
* This is most common when we have finished autoreplacing the engine and want to remove it from the list. */
|
||||
|
@ -974,9 +974,10 @@ void DrawEngineList(VehicleType type, const Rect &r, const GUIEngineList &eng_li
|
||||
int sprite_left = GetVehicleImageCellSize(type, EIT_PURCHASE).extend_left;
|
||||
int sprite_right = GetVehicleImageCellSize(type, EIT_PURCHASE).extend_right;
|
||||
int sprite_width = sprite_left + sprite_right;
|
||||
int circle_width = std::max(GetScaledSpriteSize(SPR_CIRCLE_FOLDED).width, GetScaledSpriteSize(SPR_CIRCLE_UNFOLDED).width);
|
||||
int linecolour = _colour_gradient[COLOUR_ORANGE][4];
|
||||
|
||||
Rect ir = r.WithHeight(step_size).Shrink(WidgetDimensions::scaled.matrix);
|
||||
int sprite_x = ir.WithWidth(sprite_width, rtl).left + sprite_left;
|
||||
int sprite_y_offset = ScaleSpriteTrad(sprite_y_offsets[type]) + ir.Height() / 2;
|
||||
|
||||
Dimension replace_icon = {0, 0};
|
||||
@ -987,7 +988,7 @@ void DrawEngineList(VehicleType type, const Rect &r, const GUIEngineList &eng_li
|
||||
count_width = GetStringBoundingBox(STR_TINY_BLACK_COMA).width;
|
||||
}
|
||||
|
||||
Rect tr = ir.Indent(sprite_width + WidgetDimensions::scaled.hsep_wide, rtl); // Name position
|
||||
Rect tr = ir.Indent(circle_width + WidgetDimensions::scaled.hsep_normal + sprite_width + WidgetDimensions::scaled.hsep_wide, rtl); // Name position
|
||||
Rect cr = tr.Indent(replace_icon.width + WidgetDimensions::scaled.hsep_wide, !rtl).WithWidth(count_width, !rtl); // Count position
|
||||
Rect rr = tr.WithWidth(replace_icon.width, !rtl); // Replace icon position
|
||||
if (show_count) tr = tr.Indent(count_width + WidgetDimensions::scaled.hsep_normal + replace_icon.width + WidgetDimensions::scaled.hsep_wide, !rtl);
|
||||
@ -998,22 +999,40 @@ void DrawEngineList(VehicleType type, const Rect &r, const GUIEngineList &eng_li
|
||||
|
||||
int y = ir.top;
|
||||
for (; min < max; min++, y += step_size) {
|
||||
const EngineID engine = eng_list[min].engine_id;
|
||||
const auto &item = eng_list[min];
|
||||
uint indent = item.indent * WidgetDimensions::scaled.hsep_indent;
|
||||
bool has_variants = (item.flags & EngineDisplayFlags::HasVariants) != EngineDisplayFlags::None;
|
||||
bool is_folded = (item.flags & EngineDisplayFlags::IsFolded) != EngineDisplayFlags::None;
|
||||
bool shaded = (item.flags & EngineDisplayFlags::Shaded) != EngineDisplayFlags::None;
|
||||
/* Note: num_engines is only used in the autoreplace GUI, so it is correct to use _local_company here. */
|
||||
const uint num_engines = GetGroupNumEngines(_local_company, selected_group, engine);
|
||||
const uint num_engines = GetGroupNumEngines(_local_company, selected_group, item.engine_id);
|
||||
|
||||
const Engine *e = Engine::Get(engine);
|
||||
const Engine *e = Engine::Get(item.engine_id);
|
||||
bool hidden = HasBit(e->company_hidden, _local_company);
|
||||
StringID str = hidden ? STR_HIDDEN_ENGINE_NAME : STR_ENGINE_NAME;
|
||||
TextColour tc = (engine == selected_id) ? TC_WHITE : (TC_NO_SHADE | (hidden ? TC_GREY : TC_BLACK));
|
||||
TextColour tc = (item.engine_id == selected_id) ? TC_WHITE : (TC_NO_SHADE | ((hidden | shaded) ? TC_GREY : TC_BLACK));
|
||||
|
||||
SetDParam(0, engine);
|
||||
DrawString(tr.left, tr.right, y + normal_text_y_offset, str, tc);
|
||||
DrawVehicleEngine(r.left, r.right, sprite_x, y + sprite_y_offset, engine, (show_count && num_engines == 0) ? PALETTE_CRASH : GetEnginePalette(engine, _local_company), EIT_PURCHASE);
|
||||
SetDParam(0, item.engine_id);
|
||||
Rect itr = tr.Indent(indent, rtl);
|
||||
DrawString(itr.left, itr.right, y + normal_text_y_offset, str, tc);
|
||||
int sprite_x = ir.Indent(indent + circle_width + WidgetDimensions::scaled.hsep_normal, rtl).WithWidth(sprite_width, rtl).left + sprite_left;
|
||||
DrawVehicleEngine(r.left, r.right, sprite_x, y + sprite_y_offset, item.engine_id, (show_count && num_engines == 0) ? PALETTE_CRASH : GetEnginePalette(item.engine_id, _local_company), EIT_PURCHASE);
|
||||
if (show_count) {
|
||||
SetDParam(0, num_engines);
|
||||
DrawString(cr.left, cr.right, y + small_text_y_offset, STR_TINY_BLACK_COMA, TC_FROMSTRING, SA_RIGHT | SA_FORCE);
|
||||
if (EngineHasReplacementForCompany(Company::Get(_local_company), engine, selected_group)) DrawSprite(SPR_GROUP_REPLACE_ACTIVE, num_engines == 0 ? PALETTE_CRASH : PAL_NONE, rr.left, y + replace_icon_y_offset);
|
||||
if (EngineHasReplacementForCompany(Company::Get(_local_company), item.engine_id, selected_group)) DrawSprite(SPR_GROUP_REPLACE_ACTIVE, num_engines == 0 ? PALETTE_CRASH : PAL_NONE, rr.left, y + replace_icon_y_offset);
|
||||
}
|
||||
if (has_variants) {
|
||||
Rect fr = ir.Indent(indent, rtl).WithWidth(circle_width, rtl);
|
||||
DrawSpriteIgnorePadding(is_folded ? SPR_CIRCLE_FOLDED : SPR_CIRCLE_UNFOLDED, PAL_NONE, {fr.left, y, fr.right, y + ir.Height() - 1}, false, SA_CENTER);
|
||||
}
|
||||
if (indent > 0) {
|
||||
/* Draw tree lines */
|
||||
Rect fr = ir.Indent(indent - WidgetDimensions::scaled.hsep_indent, rtl).WithWidth(circle_width, rtl);
|
||||
int ycenter = y + normal_text_y_offset + FONT_HEIGHT_NORMAL / 2;
|
||||
bool continues = (min + 1U) < eng_list.size() && eng_list[min + 1].indent == item.indent;
|
||||
GfxDrawLine(fr.left + circle_width / 2, y - WidgetDimensions::scaled.matrix.top, fr.left + circle_width / 2, continues ? y - WidgetDimensions::scaled.matrix.top + step_size - 1 : ycenter, linecolour, WidgetDimensions::scaled.fullbevel.top);
|
||||
GfxDrawLine(fr.left + circle_width / 2, ycenter, fr.right, ycenter, linecolour, WidgetDimensions::scaled.fullbevel.top);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1090,6 +1109,10 @@ struct BuildVehicleWindow : Window {
|
||||
|
||||
/* Add variants if not folded */
|
||||
if ((item.flags & (EngineDisplayFlags::HasVariants | EngineDisplayFlags::IsFolded)) == EngineDisplayFlags::HasVariants) {
|
||||
/* Add this engine again as a child */
|
||||
if ((item.flags & EngineDisplayFlags::Shaded) == EngineDisplayFlags::None) {
|
||||
this->eng_list.emplace_back(item.engine_id, item.engine_id, EngineDisplayFlags::None, indent + 1);
|
||||
}
|
||||
AddChildren(source, item.engine_id, indent + 1);
|
||||
}
|
||||
}
|
||||
@ -1487,7 +1510,23 @@ struct BuildVehicleWindow : Window {
|
||||
case WID_BV_LIST: {
|
||||
uint i = this->vscroll->GetScrolledRowFromWidget(pt.y, this, WID_BV_LIST);
|
||||
size_t num_items = this->eng_list.size();
|
||||
this->SelectEngine((i < num_items) ? this->eng_list[i].engine_id : INVALID_ENGINE);
|
||||
EngineID e = INVALID_ENGINE;
|
||||
if (i < num_items) {
|
||||
const auto &item = this->eng_list[i];
|
||||
const Rect r = this->GetWidget<NWidgetBase>(widget)->GetCurrentRect().Shrink(WidgetDimensions::scaled.matrix).WithWidth(WidgetDimensions::scaled.hsep_indent * (item.indent + 1), _current_text_dir == TD_RTL);
|
||||
if ((item.flags & EngineDisplayFlags::HasVariants) != EngineDisplayFlags::None && IsInsideMM(r.left, r.right, pt.x)) {
|
||||
/* toggle folded flag on engine */
|
||||
assert(item.variant_id != INVALID_ENGINE);
|
||||
Engine *engine = Engine::Get(item.variant_id);
|
||||
engine->display_flags ^= EngineDisplayFlags::IsFolded;
|
||||
|
||||
InvalidateWindowData(WC_REPLACE_VEHICLE, this->vehicle_type, 0); // Update the autoreplace window
|
||||
InvalidateWindowClassesData(WC_BUILD_VEHICLE); // The build windows needs updating as well
|
||||
return;
|
||||
}
|
||||
if ((item.flags & EngineDisplayFlags::Shaded) == EngineDisplayFlags::None) e = item.engine_id;
|
||||
}
|
||||
this->SelectEngine(e);
|
||||
this->SetDirty();
|
||||
if (_ctrl_pressed) {
|
||||
this->OnClick(pt, WID_BV_SHOW_HIDE, 1);
|
||||
@ -1523,6 +1562,20 @@ struct BuildVehicleWindow : Window {
|
||||
} else {
|
||||
Command<CMD_BUILD_VEHICLE>::Post(GetCmdBuildVehMsg(this->vehicle_type), CcBuildPrimaryVehicle, this->window_number, sel_eng, true, cargo, INVALID_CLIENT_ID);
|
||||
}
|
||||
|
||||
/* Update last used variant and refresh if necessary. */
|
||||
bool refresh = false;
|
||||
int recursion = 10; /* In case of infinite loop */
|
||||
for (Engine *e = Engine::Get(sel_eng); recursion > 0; e = Engine::Get(e->info.variant_id), --recursion) {
|
||||
refresh |= (e->display_last_variant != sel_eng);
|
||||
e->display_last_variant = sel_eng;
|
||||
if (e->info.variant_id == INVALID_ENGINE) break;
|
||||
}
|
||||
if (refresh) {
|
||||
InvalidateWindowData(WC_REPLACE_VEHICLE, this->vehicle_type, 0); // Update the autoreplace window
|
||||
InvalidateWindowClassesData(WC_BUILD_VEHICLE); // The build windows needs updating as well
|
||||
return;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
@ -9001,7 +9001,7 @@ static void FinaliseEngineArray()
|
||||
|
||||
/* Set appropriate flags on variant engine */
|
||||
if (e->info.variant_id != INVALID_ENGINE) {
|
||||
Engine::Get(e->info.variant_id)->display_flags |= EngineDisplayFlags::HasVariants;
|
||||
Engine::Get(e->info.variant_id)->display_flags |= EngineDisplayFlags::HasVariants | EngineDisplayFlags::IsFolded;
|
||||
}
|
||||
|
||||
/* Skip wagons, there livery is defined via the engine */
|
||||
|
Loading…
Reference in New Issue
Block a user