diff --git a/src/core/smallvec_type.hpp b/src/core/smallvec_type.hpp index ccf8dbaece..62de176a54 100644 --- a/src/core/smallvec_type.hpp +++ b/src/core/smallvec_type.hpp @@ -144,6 +144,20 @@ public: return &this->data[begin]; } + /** + * Set the size of the vector, effectively truncating items from the end or appending uninitialised ones. + * @param num_items Target size. + */ + inline void Resize(uint num_items) + { + this->items = num_items; + + if (this->items > this->capacity) { + this->capacity = Align(this->items, S); + this->data = ReallocT(this->data, this->capacity); + } + } + /** * Search for the first occurrence of an item. * The '!=' operator of T is used for comparison. @@ -212,6 +226,21 @@ public: *item = this->data[--this->items]; } + /** + * Remove items from the vector while preserving the order of other items. + * @param pos First item to remove. + * @param count Number of consecutive items to remove. + */ + void ErasePreservingOrder(uint pos, uint count = 1) + { + if (count == 0) return; + assert(pos < this->items); + assert(pos + count <= this->items); + this->items -= count; + uint to_move = this->items - pos; + if (to_move > 0) MemMoveT(this->data + pos, this->data + pos + count, to_move); + } + /** * Tests whether a item is present in the vector, and appends it to the end if not. * The '!=' operator of T is used for comparison. diff --git a/src/vehicle_gui.cpp b/src/vehicle_gui.cpp index fd70a1aa2f..724f2056ba 100644 --- a/src/vehicle_gui.cpp +++ b/src/vehicle_gui.cpp @@ -395,6 +395,15 @@ struct RefitWindow : public Window { continue; } + bool first_vehicle = this->list[current_index].Length() == 0; + if (first_vehicle) { + /* Keeping the current subtype is always an option. It also serves as the option in case of no subtypes */ + RefitOption *option = this->list[current_index].Append(); + option->cargo = cid; + option->subtype = 0xFF; + option->string = STR_EMPTY; + } + /* Check the vehicle's callback mask for cargo suffixes. * This is not supported for ordered refits, since subtypes only have a meaning * for a specific vehicle at a specific point in time, which conflicts with shared orders, @@ -415,13 +424,40 @@ struct RefitWindow : public Window { v->InvalidateNewGRFCache(); StringID subtype = GetCargoSubtypeText(v); - if (refit_cyc != 0 && subtype == STR_EMPTY) break; - RefitOption option; - option.cargo = cid; - option.subtype = refit_cyc; - option.string = subtype; - this->list[current_index].Include(option); + if (first_vehicle) { + /* Append new subtype (don't add duplicates though) */ + if (subtype == STR_EMPTY) break; + + RefitOption option; + option.cargo = cid; + option.subtype = refit_cyc; + option.string = subtype; + this->list[current_index].Include(option); + } else { + /* Intersect the subtypes of earlier vehicles with the subtypes of this vehicle */ + if (subtype == STR_EMPTY) { + /* No more subtypes for this vehicle, delete all subtypes >= refit_cyc */ + SubtypeList &l = this->list[current_index]; + /* 0xFF item is in front, other subtypes are sorted. So just truncate the list in the right spot */ + for (uint i = 1; i < l.Length(); i++) { + if (l[i].subtype >= refit_cyc) { + l.Resize(i); + break; + } + } + break; + } else { + /* Check whether the subtype matches with the subtype of earlier vehicles. */ + uint pos = 1; + SubtypeList &l = this->list[current_index]; + while (pos < l.Length() && l[pos].subtype != refit_cyc) pos++; + if (pos < l.Length() && l[pos].string != subtype) { + /* String mismatch, remove item keeping the order */ + l.ErasePreservingOrder(pos); + } + } + } } /* Reset the vehicle's cargo type */ @@ -431,13 +467,6 @@ struct RefitWindow : public Window { /* And make sure we haven't tainted the cache */ v->First()->InvalidateNewGRFCache(); v->InvalidateNewGRFCache(); - } else { - /* No cargo suffix callback -- use no subtype */ - RefitOption option; - option.cargo = cid; - option.subtype = 0; - option.string = STR_EMPTY; - this->list[current_index].Include(option); } current_index++; }