Merge remote-tracking branch 'upstream/master'

This commit is contained in:
dP
2025-09-28 02:33:49 +05:00
926 changed files with 37901 additions and 27368 deletions
+154 -158
View File
@@ -253,7 +253,7 @@ static void StationsWndShowStationRating(int left, int right, int y, CargoType c
int padding = ScaleGUITrad(1);
int width = right - left;
int colour = cs->rating_colour;
PixelColour colour = cs->rating_colour;
TextColour tc = GetContrastColour(colour);
uint w = std::min(amount + 5, units_full) * width / units_full;
@@ -497,7 +497,7 @@ public:
}
case WID_STL_LIST:
resize.height = std::max(GetCharacterHeight(FS_NORMAL), GetCharacterHeight(FS_SMALL) + ScaleGUITrad(3));
fill.height = resize.height = std::max(GetCharacterHeight(FS_NORMAL), GetCharacterHeight(FS_SMALL) + ScaleGUITrad(3));
size.height = padding.height + 5 * resize.height;
/* Determine appropriate width for mini station rating graph */
@@ -659,8 +659,8 @@ public:
this->filter.facilities.Flip(static_cast<StationFacility>(widget - WID_STL_TRAIN));
this->ToggleWidgetLoweredState(widget);
} else {
for (uint i : SetBitIterator(this->filter.facilities.base())) {
this->RaiseWidget(i + WID_STL_TRAIN);
for (StationFacility i : this->filter.facilities) {
this->RaiseWidget(to_underlying(i) + WID_STL_TRAIN);
}
this->filter.facilities = {};
this->filter.facilities.Set(static_cast<StationFacility>(widget - WID_STL_TRAIN));
@@ -696,7 +696,7 @@ public:
}
}
void OnDropdownSelect(int widget, int index) override
void OnDropdownSelect(WidgetID widget, int index, int) override
{
if (widget == WID_STL_SORTDROPBTN) {
if (this->stations.SortType() != index) {
@@ -876,30 +876,6 @@ static constexpr NWidgetPart _nested_station_view_widgets[] = {
EndContainer(),
};
/**
* Draws icons of waiting cargo in the StationView window
*
* @param cargo type of cargo
* @param waiting number of waiting units
* @param left left most coordinate to draw on
* @param right right most coordinate to draw on
* @param y y coordinate
*/
static void DrawCargoIcons(CargoType cargo, uint waiting, int left, int right, int y)
{
int width = ScaleSpriteTrad(10);
uint num = std::min<uint>((waiting + (width / 2)) / width, (right - left) / width); // maximum is width / 10 icons so it won't overflow
if (num == 0) return;
SpriteID sprite = CargoSpec::Get(cargo)->GetCargoIcon();
int x = _current_text_dir == TD_RTL ? left : right - num * width;
do {
DrawSprite(sprite, PAL_NONE, x, y);
x += width;
} while (--num);
}
enum SortOrder : uint8_t {
SO_DESCENDING,
SO_ASCENDING
@@ -917,9 +893,13 @@ enum class CargoSortType : uint8_t {
class CargoSorter {
public:
using is_transparent = void;
CargoSorter(CargoSortType t = CargoSortType::StationID, SortOrder o = SO_ASCENDING) : type(t), order(o) {}
CargoSortType GetSortType() {return this->type;}
bool operator()(const CargoDataEntry *cd1, const CargoDataEntry *cd2) const;
bool operator()(const CargoDataEntry &cd1, const CargoDataEntry &cd2) const;
bool operator()(const CargoDataEntry &cd1, const std::unique_ptr<CargoDataEntry> &cd2) const { return this->operator()(cd1, *cd2); }
bool operator()(const std::unique_ptr<CargoDataEntry> &cd1, const CargoDataEntry &cd2) const { return this->operator()(*cd1, cd2); }
bool operator()(const std::unique_ptr<CargoDataEntry> &cd1, const std::unique_ptr<CargoDataEntry> &cd2) const { return this->operator()(*cd1, *cd2); }
private:
CargoSortType type;
@@ -927,16 +907,16 @@ private:
template <class Tid>
bool SortId(Tid st1, Tid st2) const;
bool SortCount(const CargoDataEntry *cd1, const CargoDataEntry *cd2) const;
bool SortStation (StationID st1, StationID st2) const;
bool SortCount(const CargoDataEntry &cd1, const CargoDataEntry &cd2) const;
bool SortStation(StationID st1, StationID st2) const;
};
typedef std::set<CargoDataEntry *, CargoSorter> CargoDataSet;
typedef std::set<std::unique_ptr<CargoDataEntry>, CargoSorter> CargoDataSet;
/**
* A cargo data entry representing one possible row in the station view window's
* top part. Cargo data entries form a tree where each entry can have several
* children. Parents keep track of the sums of their childrens' cargo counts.
* children. Parents keep track of the sums of their children's cargo counts.
*/
class CargoDataEntry {
public:
@@ -948,7 +928,7 @@ public:
* @param station ID of the station for which an entry shall be created or retrieved
* @return a child entry associated with the given station.
*/
CargoDataEntry *InsertOrRetrieve(StationID station)
CargoDataEntry &InsertOrRetrieve(StationID station)
{
return this->InsertOrRetrieve<StationID>(station);
}
@@ -958,7 +938,7 @@ public:
* @param cargo type of the cargo for which an entry shall be created or retrieved
* @return a child entry associated with the given cargo.
*/
CargoDataEntry *InsertOrRetrieve(CargoType cargo)
CargoDataEntry &InsertOrRetrieve(CargoType cargo)
{
return this->InsertOrRetrieve<CargoType>(cargo);
}
@@ -972,7 +952,7 @@ public:
void Remove(StationID station)
{
CargoDataEntry t(station);
this->Remove(&t);
this->Remove(t);
}
/**
@@ -982,7 +962,7 @@ public:
void Remove(CargoType cargo)
{
CargoDataEntry t(cargo);
this->Remove(&t);
this->Remove(t);
}
/**
@@ -993,7 +973,7 @@ public:
CargoDataEntry *Retrieve(StationID station) const
{
CargoDataEntry t(station);
return this->Retrieve(this->children->find(&t));
return this->Retrieve(this->children->find(t));
}
/**
@@ -1004,7 +984,7 @@ public:
CargoDataEntry *Retrieve(CargoType cargo) const
{
CargoDataEntry t(cargo);
return this->Retrieve(this->children->find(&t));
return this->Retrieve(this->children->find(t));
}
void Resort(CargoSortType type, SortOrder order);
@@ -1055,19 +1035,19 @@ public:
void SetTransfers(bool value) { this->transfers = value; }
void Clear();
private:
CargoDataEntry(StationID station, uint count, CargoDataEntry *parent);
CargoDataEntry(CargoType cargo, uint count, CargoDataEntry *parent);
CargoDataEntry(StationID station);
CargoDataEntry(CargoType cargo);
private:
CargoDataEntry *Retrieve(CargoDataSet::iterator i) const;
template <class Tid>
CargoDataEntry *InsertOrRetrieve(Tid s);
CargoDataEntry &InsertOrRetrieve(Tid s);
void Remove(CargoDataEntry *entry);
void Remove(CargoDataEntry &entry);
void IncrementSize();
CargoDataEntry *parent; ///< the parent of this entry.
@@ -1080,7 +1060,7 @@ private:
};
uint num_children; ///< the number of subentries belonging to this entry.
uint count; ///< sum of counts of all children or amount of cargo for this entry.
CargoDataSet *children; ///< the children of this entry.
std::unique_ptr<CargoDataSet> children; ///< the children of this entry.
};
CargoDataEntry::CargoDataEntry() :
@@ -1088,7 +1068,7 @@ CargoDataEntry::CargoDataEntry() :
station(StationID::Invalid()),
num_children(0),
count(0),
children(new CargoDataSet(CargoSorter(CargoSortType::CargoType)))
children(std::make_unique<CargoDataSet>(CargoSorter(CargoSortType::CargoType)))
{}
CargoDataEntry::CargoDataEntry(CargoType cargo, uint count, CargoDataEntry *parent) :
@@ -1096,7 +1076,7 @@ CargoDataEntry::CargoDataEntry(CargoType cargo, uint count, CargoDataEntry *pare
cargo(cargo),
num_children(0),
count(count),
children(new CargoDataSet)
children(std::make_unique<CargoDataSet>())
{}
CargoDataEntry::CargoDataEntry(StationID station, uint count, CargoDataEntry *parent) :
@@ -1104,7 +1084,7 @@ CargoDataEntry::CargoDataEntry(StationID station, uint count, CargoDataEntry *pa
station(station),
num_children(0),
count(count),
children(new CargoDataSet)
children(std::make_unique<CargoDataSet>())
{}
CargoDataEntry::CargoDataEntry(StationID station) :
@@ -1126,7 +1106,6 @@ CargoDataEntry::CargoDataEntry(CargoType cargo) :
CargoDataEntry::~CargoDataEntry()
{
this->Clear();
delete this->children;
}
/**
@@ -1134,13 +1113,7 @@ CargoDataEntry::~CargoDataEntry()
*/
void CargoDataEntry::Clear()
{
if (this->children != nullptr) {
for (auto &it : *this->children) {
assert(it != this);
delete it;
}
this->children->clear();
}
if (this->children != nullptr) this->children->clear();
if (this->parent != nullptr) this->parent->count -= this->count;
this->count = 0;
this->num_children = 0;
@@ -1152,13 +1125,10 @@ void CargoDataEntry::Clear()
* which only contains the ID of the entry to be removed. In this case child is
* not deleted.
*/
void CargoDataEntry::Remove(CargoDataEntry *entry)
void CargoDataEntry::Remove(CargoDataEntry &entry)
{
CargoDataSet::iterator i = this->children->find(entry);
if (i != this->children->end()) {
delete *i;
this->children->erase(i);
}
if (i != this->children->end()) this->children->erase(i);
}
/**
@@ -1168,17 +1138,16 @@ void CargoDataEntry::Remove(CargoDataEntry *entry)
* @return the new or retrieved subentry
*/
template <class Tid>
CargoDataEntry *CargoDataEntry::InsertOrRetrieve(Tid child_id)
CargoDataEntry &CargoDataEntry::InsertOrRetrieve(Tid child_id)
{
CargoDataEntry tmp(child_id);
CargoDataSet::iterator i = this->children->find(&tmp);
CargoDataSet::iterator i = this->children->find(tmp);
if (i == this->children->end()) {
IncrementSize();
return *(this->children->insert(new CargoDataEntry(child_id, 0, this)).first);
return **(this->children->insert(std::make_unique<CargoDataEntry>(child_id, 0, this)).first);
} else {
CargoDataEntry *ret = *i;
assert(this->children->value_comp().GetSortType() != CargoSortType::Count);
return ret;
return **i;
}
}
@@ -1204,9 +1173,9 @@ void CargoDataEntry::IncrementSize()
void CargoDataEntry::Resort(CargoSortType type, SortOrder order)
{
CargoDataSet *new_subs = new CargoDataSet(this->children->begin(), this->children->end(), CargoSorter(type, order));
delete this->children;
this->children = new_subs;
auto new_children = std::make_unique<CargoDataSet>(CargoSorter(type, order));
new_children->merge(*this->children);
this->children = std::move(new_children);
}
CargoDataEntry *CargoDataEntry::Retrieve(CargoDataSet::iterator i) const
@@ -1215,21 +1184,21 @@ CargoDataEntry *CargoDataEntry::Retrieve(CargoDataSet::iterator i) const
return nullptr;
} else {
assert(this->children->value_comp().GetSortType() != CargoSortType::Count);
return *i;
return i->get();
}
}
bool CargoSorter::operator()(const CargoDataEntry *cd1, const CargoDataEntry *cd2) const
bool CargoSorter::operator()(const CargoDataEntry &cd1, const CargoDataEntry &cd2) const
{
switch (this->type) {
case CargoSortType::StationID:
return this->SortId<StationID>(cd1->GetStation(), cd2->GetStation());
return this->SortId<StationID>(cd1.GetStation(), cd2.GetStation());
case CargoSortType::CargoType:
return this->SortId<CargoType>(cd1->GetCargo(), cd2->GetCargo());
return this->SortId<CargoType>(cd1.GetCargo(), cd2.GetCargo());
case CargoSortType::Count:
return this->SortCount(cd1, cd2);
case CargoSortType::StationString:
return this->SortStation(cd1->GetStation(), cd2->GetStation());
return this->SortStation(cd1.GetStation(), cd2.GetStation());
default:
NOT_REACHED();
}
@@ -1241,12 +1210,12 @@ bool CargoSorter::SortId(Tid st1, Tid st2) const
return (this->order == SO_ASCENDING) ? st1 < st2 : st2 < st1;
}
bool CargoSorter::SortCount(const CargoDataEntry *cd1, const CargoDataEntry *cd2) const
bool CargoSorter::SortCount(const CargoDataEntry &cd1, const CargoDataEntry &cd2) const
{
uint c1 = cd1->GetCount();
uint c2 = cd2->GetCount();
uint c1 = cd1.GetCount();
uint c2 = cd2.GetCount();
if (c1 == c2) {
return this->SortStation(cd1->GetStation(), cd2->GetStation());
return this->SortStation(cd1.GetStation(), cd2.GetStation());
} else if (this->order == SO_ASCENDING) {
return c1 < c2;
} else {
@@ -1331,6 +1300,8 @@ struct StationViewWindow : public Window {
uint expand_shrink_width = 0; ///< The width allocated to the expand/shrink 'button'
int rating_lines = RATING_LINES; ///< Number of lines in the cargo ratings view.
int accepts_lines = ACCEPTS_LINES; ///< Number of lines in the accepted cargo view.
int line_height = 0; ///< Height of a cargo line.
Dimension cargo_icon_size{}; ///< Size of largest cargo icon.
Scrollbar *vscroll = nullptr;
/* Height of the #WID_SV_ACCEPT_RATING_LIST widget for different views. */
@@ -1391,6 +1362,13 @@ struct StationViewWindow : public Window {
this->owner = Station::Get(window_number)->owner;
}
void OnInit() override
{
this->cargo_icon_size = GetLargestCargoIconSize();
this->line_height = std::max<int>(GetCharacterHeight(FS_NORMAL), this->cargo_icon_size.height);
this->expand_shrink_width = std::max(GetCharacterWidth(FS_NORMAL, '-'), GetCharacterWidth(FS_NORMAL, '+'));
}
void Close([[maybe_unused]] int data = 0) override
{
CloseWindowById(WC_TRAINS_LIST, VehicleListIdentifier(VL_STATION_LIST, VEH_TRAIN, this->owner, this->window_number).ToWindowNumber(), false);
@@ -1421,25 +1399,25 @@ struct StationViewWindow : public Window {
switch (groupings[i]) {
case GR_CARGO:
assert(i == 0);
data = data->InsertOrRetrieve(cargo);
data = &data->InsertOrRetrieve(cargo);
data->SetTransfers(source != this->window_number);
expand = expand->Retrieve(cargo);
break;
case GR_SOURCE:
if (auto_distributed || source != this->window_number) {
data = data->InsertOrRetrieve(source);
data = &data->InsertOrRetrieve(source);
expand = expand->Retrieve(source);
}
break;
case GR_NEXT:
if (auto_distributed) {
data = data->InsertOrRetrieve(next);
data = &data->InsertOrRetrieve(next);
expand = expand->Retrieve(next);
}
break;
case GR_DESTINATION:
if (auto_distributed) {
data = data->InsertOrRetrieve(dest);
data = &data->InsertOrRetrieve(dest);
expand = expand->Retrieve(dest);
}
break;
@@ -1452,9 +1430,8 @@ struct StationViewWindow : public Window {
{
switch (widget) {
case WID_SV_WAITING:
resize.height = GetCharacterHeight(FS_NORMAL);
fill.height = resize.height = this->line_height;
size.height = 4 * resize.height + padding.height;
this->expand_shrink_width = std::max(GetStringBoundingBox("-").width, GetStringBoundingBox("+").width);
break;
case WID_SV_ACCEPT_RATING_LIST:
@@ -1490,7 +1467,7 @@ struct StationViewWindow : public Window {
this->SetWidgetLoweredState(WID_SV_CLOSE_AIRPORT, st->facilities.Test(StationFacility::Airport) && st->airport.blocks.Test(AirportBlock::AirportClosed));
extern const Station *_viewport_highlight_station;
this->SetWidgetDisabledState(WID_SV_CATCHMENT, st->facilities == StationFacilities{});
this->SetWidgetDisabledState(WID_SV_CATCHMENT, st->facilities.None());
this->SetWidgetLoweredState(WID_SV_CATCHMENT, citymania::IsHighlightCoverageStation(st));
this->DrawWidgets();
@@ -1527,7 +1504,7 @@ struct StationViewWindow : public Window {
/* Draw waiting cargo. */
NWidgetBase *nwi = this->GetWidget<NWidgetBase>(WID_SV_WAITING);
Rect waiting_rect = nwi->GetCurrentRect().Shrink(WidgetDimensions::scaled.framerect);
this->DrawEntries(&cargo, waiting_rect, pos, maxrows, 0);
this->DrawEntries(cargo, waiting_rect, pos, maxrows, 0);
scroll_to_row = INT_MAX;
}
}
@@ -1550,20 +1527,20 @@ struct StationViewWindow : public Window {
void RecalcDestinations(CargoType cargo)
{
const Station *st = Station::Get(this->window_number);
CargoDataEntry *entry = cached_destinations.InsertOrRetrieve(cargo);
entry->Clear();
CargoDataEntry &entry = cached_destinations.InsertOrRetrieve(cargo);
entry.Clear();
if (!st->goods[cargo].HasData()) return;
for (const auto &it : st->goods[cargo].GetData().flows) {
StationID from = it.first;
CargoDataEntry *source_entry = entry->InsertOrRetrieve(from);
CargoDataEntry &source_entry = entry.InsertOrRetrieve(from);
uint32_t prev_count = 0;
for (const auto &flow_it : *it.second.GetShares()) {
StationID via = flow_it.second;
CargoDataEntry *via_entry = source_entry->InsertOrRetrieve(via);
CargoDataEntry &via_entry = source_entry.InsertOrRetrieve(via);
if (via == this->window_number) {
via_entry->InsertOrRetrieve(via)->Update(flow_it.first - prev_count);
via_entry.InsertOrRetrieve(via).Update(flow_it.first - prev_count);
} else {
EstimateDestinations(cargo, from, via, flow_it.first - prev_count, via_entry);
}
@@ -1581,7 +1558,7 @@ struct StationViewWindow : public Window {
* @param count Size of the batch of cargo.
* @param dest CargoDataEntry to save the results in.
*/
void EstimateDestinations(CargoType cargo, StationID source, StationID next, uint count, CargoDataEntry *dest)
void EstimateDestinations(CargoType cargo, StationID source, StationID next, uint count, CargoDataEntry &dest)
{
if (Station::IsValidID(next) && Station::IsValidID(source)) {
GoodsEntry &ge = Station::Get(next)->goods[cargo];
@@ -1594,19 +1571,19 @@ struct StationViewWindow : public Window {
const FlowStat::SharesMap *shares = map_it->second.GetShares();
uint32_t prev_count = 0;
for (FlowStat::SharesMap::const_iterator i = shares->begin(); i != shares->end(); ++i) {
tmp.InsertOrRetrieve(i->second)->Update(i->first - prev_count);
tmp.InsertOrRetrieve(i->second).Update(i->first - prev_count);
prev_count = i->first;
}
}
if (tmp.GetCount() == 0) {
dest->InsertOrRetrieve(StationID::Invalid())->Update(count);
dest.InsertOrRetrieve(StationID::Invalid()).Update(count);
} else {
uint sum_estimated = 0;
while (sum_estimated < count) {
for (CargoDataSet::iterator i = tmp.Begin(); i != tmp.End() && sum_estimated < count; ++i) {
CargoDataEntry *child = *i;
uint estimate = DivideApprox(child->GetCount() * count, tmp.GetCount());
CargoDataEntry &child = **i;
uint estimate = DivideApprox(child.GetCount() * count, tmp.GetCount());
if (estimate == 0) estimate = 1;
sum_estimated += estimate;
@@ -1616,10 +1593,10 @@ struct StationViewWindow : public Window {
}
if (estimate > 0) {
if (child->GetStation() == next) {
dest->InsertOrRetrieve(next)->Update(estimate);
if (child.GetStation() == next) {
dest.InsertOrRetrieve(next).Update(estimate);
} else {
EstimateDestinations(cargo, source, child->GetStation(), estimate, dest);
EstimateDestinations(cargo, source, child.GetStation(), estimate, dest);
}
}
}
@@ -1627,7 +1604,7 @@ struct StationViewWindow : public Window {
}
}
} else {
dest->InsertOrRetrieve(StationID::Invalid())->Update(count);
dest.InsertOrRetrieve(StationID::Invalid()).Update(count);
}
}
@@ -1647,8 +1624,8 @@ struct StationViewWindow : public Window {
for (FlowStat::SharesMap::const_iterator flow_it = shares->begin(); flow_it != shares->end(); ++flow_it) {
const CargoDataEntry *via_entry = source_entry->Retrieve(flow_it->second);
for (CargoDataSet::iterator dest_it = via_entry->Begin(); dest_it != via_entry->End(); ++dest_it) {
CargoDataEntry *dest_entry = *dest_it;
ShowCargo(entry, cargo, from, flow_it->second, dest_entry->GetStation(), dest_entry->GetCount());
CargoDataEntry &dest_entry = **dest_it;
ShowCargo(entry, cargo, from, flow_it->second, dest_entry.GetStation(), dest_entry.GetCount());
}
}
}
@@ -1681,7 +1658,7 @@ struct StationViewWindow : public Window {
uint remaining = cp->Count();
for (CargoDataSet::iterator dest_it = via_entry->Begin(); dest_it != via_entry->End();) {
CargoDataEntry *dest_entry = *dest_it;
CargoDataEntry &dest_entry = **dest_it;
/* Advance iterator here instead of in the for statement to test whether this is the last entry */
++dest_it;
@@ -1693,10 +1670,10 @@ struct StationViewWindow : public Window {
* not matching GoodsEntry::TotalCount() */
val = remaining;
} else {
val = std::min<uint>(remaining, DivideApprox(cp->Count() * dest_entry->GetCount(), via_entry->GetCount()));
val = std::min<uint>(remaining, DivideApprox(cp->Count() * dest_entry.GetCount(), via_entry->GetCount()));
remaining -= val;
}
this->ShowCargo(entry, cargo, cp->GetFirstStation(), next, dest_entry->GetStation(), val);
this->ShowCargo(entry, cargo, cp->GetFirstStation(), next, dest_entry.GetStation(), val);
}
}
this->ShowCargo(entry, cargo, NEW_STATION, NEW_STATION, NEW_STATION, packets.ReservedCount());
@@ -1730,16 +1707,16 @@ struct StationViewWindow : public Window {
* Mark a specific row, characterized by its CargoDataEntry, as expanded.
* @param entry The row to be marked as expanded.
*/
void SetDisplayedRow(const CargoDataEntry *entry)
void SetDisplayedRow(const CargoDataEntry &entry)
{
std::list<StationID> stations;
const CargoDataEntry *parent = entry->GetParent();
const CargoDataEntry *parent = entry.GetParent();
if (parent->GetParent() == nullptr) {
this->displayed_rows.push_back(RowDisplay(&this->expanded_rows, entry->GetCargo()));
this->displayed_rows.push_back(RowDisplay(&this->expanded_rows, entry.GetCargo()));
return;
}
StationID next = entry->GetStation();
StationID next = entry.GetStation();
while (parent->GetParent()->GetParent() != nullptr) {
stations.push_back(parent->GetStation());
parent = parent->GetParent();
@@ -1793,9 +1770,10 @@ struct StationViewWindow : public Window {
* @param column The "column" the entry will be shown in.
* @return either STR_STATION_VIEW_VIA or STR_STATION_VIEW_NONSTOP.
*/
StringID SearchNonStop(CargoDataEntry *cd, StationID station, int column)
StringID SearchNonStop(CargoDataEntry &cd, StationID station, int column)
{
CargoDataEntry *parent = cd->GetParent();
assert(column < NUM_COLUMNS);
CargoDataEntry *parent = cd.GetParent();
for (int i = column - 1; i > 0; --i) {
if (this->groupings[i] == GR_DESTINATION) {
if (parent->GetStation() == station) {
@@ -1807,10 +1785,10 @@ struct StationViewWindow : public Window {
parent = parent->GetParent();
}
if (this->groupings[column + 1] == GR_DESTINATION) {
CargoDataSet::iterator begin = cd->Begin();
CargoDataSet::iterator end = cd->End();
if (begin != end && ++(cd->Begin()) == end && (*(begin))->GetStation() == station) {
if (column < NUM_COLUMNS - 1 && this->groupings[column + 1] == GR_DESTINATION) {
CargoDataSet::iterator begin = cd.Begin();
CargoDataSet::iterator end = cd.End();
if (begin != end && ++(cd.Begin()) == end && (*(begin))->GetStation() == station) {
return STR_STATION_VIEW_NONSTOP;
} else {
return STR_STATION_VIEW_VIA;
@@ -1820,6 +1798,28 @@ struct StationViewWindow : public Window {
return STR_STATION_VIEW_VIA;
}
/**
* Draw icons of waiting cargo.
* @param cargo type of cargo
* @param waiting number of waiting units
* @param r Rect to draw within
*/
void DrawCargoIcons(CargoType cargo, uint waiting, const Rect &r) const
{
int width = ScaleSpriteTrad(10);
uint num = std::min<uint>((waiting + (width / 2)) / width, r.Width() / width); // maximum is width / 10 icons so it won't overflow
if (num == 0) return;
SpriteID sprite = CargoSpec::Get(cargo)->GetCargoIcon();
int x = _current_text_dir == TD_RTL ? r.left : r.right - num * width;
int y = CentreBounds(r.top, r.bottom, this->cargo_icon_size.height);
do {
DrawSprite(sprite, PAL_NONE, x, y);
x += width;
} while (--num);
}
/**
* Draw the given cargo entries in the station GUI.
* @param entry Root entry for all cargo to be drawn.
@@ -1830,32 +1830,34 @@ struct StationViewWindow : public Window {
* @param cargo Current cargo being drawn (if cargo column has been passed).
* @return row (in "pos" counting) after the one we have last drawn to.
*/
int DrawEntries(CargoDataEntry *entry, const Rect &r, int pos, int maxrows, int column, CargoType cargo = INVALID_CARGO)
int DrawEntries(CargoDataEntry &entry, const Rect &r, int pos, int maxrows, int column, CargoType cargo = INVALID_CARGO)
{
assert(column < NUM_COLUMNS);
if (this->sortings[column] == CargoSortType::AsGrouping) {
if (this->groupings[column] != GR_CARGO) {
entry->Resort(CargoSortType::StationString, this->sort_orders[column]);
entry.Resort(CargoSortType::StationString, this->sort_orders[column]);
}
} else {
entry->Resort(CargoSortType::Count, this->sort_orders[column]);
entry.Resort(CargoSortType::Count, this->sort_orders[column]);
}
for (CargoDataSet::iterator i = entry->Begin(); i != entry->End(); ++i) {
CargoDataEntry *cd = *i;
int text_y_offset = (this->line_height - GetCharacterHeight(FS_NORMAL)) / 2;
for (CargoDataSet::iterator i = entry.Begin(); i != entry.End(); ++i) {
CargoDataEntry &cd = **i;
Grouping grouping = this->groupings[column];
if (grouping == GR_CARGO) cargo = cd->GetCargo();
if (grouping == GR_CARGO) cargo = cd.GetCargo();
bool auto_distributed = _settings_game.linkgraph.GetDistributionType(cargo) != DT_MANUAL;
if (pos > -maxrows && pos <= 0) {
StringID str = STR_EMPTY;
StationID station = StationID::Invalid();
int y = r.top - pos * GetCharacterHeight(FS_NORMAL);
int y = r.top - pos * this->line_height;
if (this->groupings[column] == GR_CARGO) {
str = STR_STATION_VIEW_WAITING_CARGO;
DrawCargoIcons(cd->GetCargo(), cd->GetCount(), r.left + this->expand_shrink_width, r.right - this->expand_shrink_width, y);
this->DrawCargoIcons(cd.GetCargo(), cd.GetCount(), Rect(r.left + this->expand_shrink_width, y, r.right - this->expand_shrink_width, y + this->line_height - 1));
} else {
if (!auto_distributed) grouping = GR_SOURCE;
station = cd->GetStation();
station = cd.GetStation();
str = this->GetGroupingString(grouping, station);
if (grouping == GR_NEXT && str == STR_STATION_VIEW_VIA) str = this->SearchNonStop(cd, station, column);
@@ -1868,11 +1870,11 @@ struct StationViewWindow : public Window {
Rect text = r.Indent(column * WidgetDimensions::scaled.hsep_indent, rtl).Indent(this->expand_shrink_width, !rtl);
Rect shrink = r.WithWidth(this->expand_shrink_width, !rtl);
DrawString(text.left, text.right, y, GetString(str, cargo, cd->GetCount(), station));
DrawString(text.left, text.right, y + text_y_offset, GetString(str, cargo, cd.GetCount(), station));
if (column < NUM_COLUMNS - 1) {
const char *sym = nullptr;
if (cd->GetNumChildren() > 0) {
std::string_view sym;
if (cd.GetNumChildren() > 0) {
sym = "-";
} else if (auto_distributed && str != STR_STATION_VIEW_RESERVED) {
sym = "+";
@@ -1881,17 +1883,17 @@ struct StationViewWindow : public Window {
const GoodsEntry &ge = Station::Get(this->window_number)->goods[cargo];
if (ge.HasData()) {
const StationCargoList &cargo_list = ge.GetData().cargo;
if (grouping == GR_CARGO && (cargo_list.ReservedCount() > 0 || cd->HasTransfers())) {
if (grouping == GR_CARGO && (cargo_list.ReservedCount() > 0 || cd.HasTransfers())) {
sym = "+";
}
}
}
if (sym != nullptr) DrawString(shrink.left, shrink.right, y, sym, TC_YELLOW);
if (!sym.empty()) DrawString(shrink.left, shrink.right, y + text_y_offset, sym, TC_YELLOW);
}
this->SetDisplayedRow(cd);
}
--pos;
if (auto_distributed || column == 0) {
if ((auto_distributed || column == 0) && column < NUM_COLUMNS - 1) {
pos = this->DrawEntries(cd, r, pos, maxrows, column + 1, cargo);
}
}
@@ -2148,7 +2150,7 @@ struct StationViewWindow : public Window {
this->SetDirty();
}
void OnDropdownSelect(WidgetID widget, int index) override
void OnDropdownSelect(WidgetID widget, int index, int) override
{
if (widget == WID_SV_SORT_BY) {
this->SelectSortBy(index);
@@ -2237,14 +2239,12 @@ static std::vector<StationID> _stations_nearby_list;
* Add station on this tile to _stations_nearby_list if it's fully within the
* station spread.
* @param tile Tile just being checked
* @param user_data Pointer to TileArea context
* @param ctx Pointer to TileArea context
* @tparam T the station filter type
*/
template <class T>
static bool AddNearbyStation(TileIndex tile, void *user_data)
static void AddNearbyStation(TileIndex tile, TileArea *ctx)
{
TileArea *ctx = (TileArea *)user_data;
/* First check if there were deleted stations here */
for (auto it = _deleted_stations_nearby.begin(); it != _deleted_stations_nearby.end(); /* nothing */) {
if (it->tile == tile) {
@@ -2256,21 +2256,19 @@ static bool AddNearbyStation(TileIndex tile, void *user_data)
}
/* Check if own station and if we stay within station spread */
if (!IsTileType(tile, MP_STATION)) return false;
if (!IsTileType(tile, MP_STATION)) return;
StationID sid = GetStationIndex(tile);
/* This station is (likely) a waypoint */
if (!T::IsValidID(sid)) return false;
if (!T::IsValidID(sid)) return;
BaseStation *st = BaseStation::Get(sid);
if (st->owner != _local_company || std::ranges::find(_stations_nearby_list, sid) != _stations_nearby_list.end()) return false;
if (st->owner != _local_company || std::ranges::find(_stations_nearby_list, sid) != _stations_nearby_list.end()) return;
if (st->rect.BeforeAddRect(ctx->tile, ctx->w, ctx->h, StationRect::ADD_TEST).Succeeded()) {
_stations_nearby_list.push_back(sid);
}
return false; // We want to include *all* nearby stations
}
/**
@@ -2283,7 +2281,7 @@ static bool AddNearbyStation(TileIndex tile, void *user_data)
* @tparam T the station filter type, for stations to look for
*/
template <class T>
static const BaseStation *FindStationsNearby(TileArea ta, bool distant_join)
static void FindStationsNearby(TileArea ta, bool distant_join)
{
TileArea ctx = ta;
@@ -2291,11 +2289,6 @@ static const BaseStation *FindStationsNearby(TileArea ta, bool distant_join)
_stations_nearby_list.push_back(NEW_STATION);
_deleted_stations_nearby.clear();
/* Check the inside, to return, if we sit on another station */
for (TileIndex t : ta) {
if (t < Map::Size() && IsTileType(t, MP_STATION) && T::IsValidID(GetStationIndex(t))) return BaseStation::GetByTile(t);
}
/* Look for deleted stations */
for (const BaseStation *st : BaseStation::Iterate()) {
if (T::IsValidBaseStation(st) && !st->IsInUse() && st->owner == _local_company) {
@@ -2312,16 +2305,20 @@ static const BaseStation *FindStationsNearby(TileArea ta, bool distant_join)
}
}
/* Add stations that are within station tile area. Stations do not have to occupy all tiles */
for (auto t : ta) {
AddNearbyStation<T>(t, &ctx);
}
/* Only search tiles where we have a chance to stay within the station spread.
* The complete check needs to be done in the callback as we don't know the
* extent of the found station, yet. */
if (distant_join && std::min(ta.w, ta.h) >= _settings_game.station.station_spread) return nullptr;
if (distant_join && std::min(ta.w, ta.h) >= _settings_game.station.station_spread) return;
uint max_dist = distant_join ? _settings_game.station.station_spread - std::min(ta.w, ta.h) : 1;
TileIndex tile = TileAddByDir(ctx.tile, DIR_N);
CircularTileSearch(&tile, max_dist, ta.w, ta.h, AddNearbyStation<T>, &ctx);
return nullptr;
for (auto tile : SpiralTileSequence(TileAddByDir(ctx.tile, DIR_N), max_dist, ta.w, ta.h)) {
AddNearbyStation<T>(tile, &ctx);
}
}
static constexpr NWidgetPart _nested_select_station_widgets[] = {
@@ -2377,7 +2374,7 @@ struct SelectStationWindow : Window {
if (widget != WID_JS_PANEL) return;
/* Determine the widest string */
Dimension d = GetStringBoundingBox(T::IsWaypoint() ? STR_JOIN_WAYPOINT_CREATE_SPLITTED_WAYPOINT : STR_JOIN_STATION_CREATE_SPLITTED_STATION);
Dimension d = GetStringBoundingBox(T::IsWaypoint() ? STR_JOIN_WAYPOINT_CREATE_SPLIT_WAYPOINT : STR_JOIN_STATION_CREATE_SPLIT_STATION);
for (const auto &station : _stations_nearby_list) {
if (station == NEW_STATION) continue;
const BaseStation *st = BaseStation::Get(station);
@@ -2386,7 +2383,7 @@ struct SelectStationWindow : Window {
: GetString(STR_STATION_LIST_STATION, st->index, st->facilities)));
}
resize.height = d.height;
fill.height = resize.height = d.height;
d.height *= 5;
d.width += padding.width;
d.height += padding.height;
@@ -2401,7 +2398,7 @@ struct SelectStationWindow : Window {
auto [first, last] = this->vscroll->GetVisibleRangeIterators(_stations_nearby_list);
for (auto it = first; it != last; ++it, tr.top += this->resize.step_height) {
if (*it == NEW_STATION) {
DrawString(tr, T::IsWaypoint() ? STR_JOIN_WAYPOINT_CREATE_SPLITTED_WAYPOINT : STR_JOIN_STATION_CREATE_SPLITTED_STATION);
DrawString(tr, T::IsWaypoint() ? STR_JOIN_WAYPOINT_CREATE_SPLIT_WAYPOINT : STR_JOIN_STATION_CREATE_SPLIT_STATION);
} else {
const BaseStation *st = BaseStation::Get(*it);
DrawString(tr, T::IsWaypoint()
@@ -2484,7 +2481,7 @@ static WindowDesc _select_station_desc(
* @return whether we need to show the station selection window.
*/
template <class T>
static bool StationJoinerNeeded(TileArea ta, const StationPickerCmdProc &proc)
static bool StationJoinerNeeded(const StationPickerCmdProc &proc)
{
/* Only show selection if distant join is enabled in the settings */
if (!_settings_game.station.distant_join_stations) return false;
@@ -2501,9 +2498,7 @@ static bool StationJoinerNeeded(TileArea ta, const StationPickerCmdProc &proc)
/* only show the popup, if we press ctrl */
if (!citymania::_fn_mod) return false;
/* Now check if we could build there */
if (!proc(true, StationID::Invalid())) return false;
return FindStationsNearby<T>(ta, false) == nullptr;
return proc(true, StationID::Invalid());
}
/**
@@ -2515,8 +2510,9 @@ static bool StationJoinerNeeded(TileArea ta, const StationPickerCmdProc &proc)
template <class T>
void ShowSelectBaseStationIfNeeded(TileArea ta, StationPickerCmdProc&& proc)
{
if (StationJoinerNeeded<T>(ta, proc)) {
if (StationJoinerNeeded<T>(proc)) {
if (!_settings_client.gui.persistent_buildingtools) ResetObjectToPlace();
FindStationsNearby<T>(ta, false);
new SelectStationWindow<T>(_select_station_desc, ta, std::move(proc));
} else {
proc(false, StationID::Invalid());