Merge PR #274 into jgrpp

pull/279/head
Jonathan G Rennison 3 years ago
commit 3392d25aa3

@ -1464,6 +1464,12 @@ STR_CONFIG_SETTING_VEHICLE_NAMES_LONG :Long
STR_CONFIG_SETTING_SHADED_TREES_ON_SLOPES :Shade trees on slopes: {STRING2}
STR_CONFIG_SETTING_SHADED_TREES_ON_SLOPES_HELPTEXT :Change brightness of trees drawn on slopes. Improves the look of tree cover in mountainous areas.
STR_CONFIG_SETTING_STATION_RATING_TOOLTIP_MODE :Station rating tooltips: {STRING2}
STR_CONFIG_SETTING_STATION_RATING_TOOLTIP_MODE_HELPTEXT :Set whether station rating tooltips are shown and the level of information detail.
STR_CONFIG_SETTING_STATION_RATING_TOOLTIP_MODE_OFF :Off
STR_CONFIG_SETTING_STATION_RATING_TOOLTIP_MODE_SIMPLE :Simple
STR_CONFIG_SETTING_STATION_RATING_TOOLTIP_MODE_DETAILED :Detailed
STR_CONFIG_SETTING_ADV_SIG_BRIDGE_TUN_MODES :Enable signals on bridges/tunnels advanced modes: {STRING2}
STR_CONFIG_SETTING_ADV_SIG_BRIDGE_TUN_MODES_HELPTEXT :Enables use of advanced modes of signal simulation on bridges and tunnels. When disabled, bridges/tunnels which are not already in an advanced mode cannot be changed to an advanced mode, however other players may choose to enable this setting and use an advanced mode.
@ -6521,6 +6527,61 @@ STR_ZONING_2x2_GRID :2x2 town road g
STR_ZONING_3x3_GRID :3x3 town road grid
STR_ZONING_ONE_WAY_ROAD :One way roads
STR_STATION_RATING_TOOLTIP_RATING_DETAILS :{STRING} Rating Details
STR_STATION_RATING_TOOLTIP_TOTAL_RATING :Total target rating: {LTBLUE}{NUM}%
STR_STATION_RATING_TOOLTIP_USING_CHEAT :Cheat active
STR_STATION_RATING_TOOLTIP_NEWGRF_RATING :NewGRF station rating: {STRING1} {BLACK}based on
STR_STATION_RATING_TOOLTIP_NEWGRF_RATING_0 :{GOLD}{PLUS_NUM}%
STR_STATION_RATING_TOOLTIP_NEWGRF_RATING_1 :{LTBLUE}{PLUS_NUM}%
STR_STATION_RATING_TOOLTIP_NEWGRF_SPEED :Max speed of last vehicle: {LTBLUE}{VELOCITY} ({STRING2})
STR_STATION_RATING_TOOLTIP_NEWGRF_WAITUNITS :Cargo waiting (on average per next stop): {LTBLUE}{NUM}
STR_STATION_RATING_TOOLTIP_NEWGRF_WAITTIME :Time since last pickup: {LTBLUE}{NUM} day{P "" s}
STR_STATION_RATING_MAX_PERCENTAGE : (max {PLUS_NUM}%)
STR_STATION_RATING_MAX_PERCENTAGE_COMMA :, max {PLUS_NUM}%
STR_STATION_RATING_PERCENTAGE_COMMA :, {PLUS_NUM}%
STR_STATION_RATING_TOOLTIP_SPEED :Max speed of last vehicle{STRING1}: {STRING3} ({STRING})
STR_STATION_RATING_TOOLTIP_SPEED_ZERO :{RED}{VELOCITY}{STRING1}
STR_STATION_RATING_TOOLTIP_SPEED_0 :{ORANGE}{VELOCITY}{STRING1}
STR_STATION_RATING_TOOLTIP_SPEED_1 :{GOLD}{VELOCITY}{STRING1}
STR_STATION_RATING_TOOLTIP_SPEED_2 :{YELLOW}{VELOCITY}{STRING1}
STR_STATION_RATING_TOOLTIP_SPEED_3 :{GREEN}{VELOCITY}{STRING1}
STR_STATION_RATING_TOOLTIP_AGE :Age of last vehicle{STRING1}: {STRING3}
STR_STATION_RATING_TOOLTIP_AGE_0 :{ORANGE}{NUM} year{P "" s}{STRING1}
STR_STATION_RATING_TOOLTIP_AGE_1 :{GOLD}{NUM} year{P "" s}{STRING1}
STR_STATION_RATING_TOOLTIP_AGE_2 :{YELLOW}{NUM} year{P "" s}{STRING1}
STR_STATION_RATING_TOOLTIP_AGE_3 :{GREEN}{NUM} year{P "" s}{STRING1}
STR_STATION_RATING_TOOLTIP_WAITTIME :Time since last pickup{STRING1}: {STRING3}
STR_STATION_RATING_TOOLTIP_WAITTIME_SHIP :Time since last pickup{STRING1}: {STRING3} (by ship)
STR_STATION_RATING_TOOLTIP_WAITTIME_0 :{RED}{NUM} day{P "" s}{STRING1}
STR_STATION_RATING_TOOLTIP_WAITTIME_1 :{ORANGE}{NUM} day{P "" s}{STRING1}
STR_STATION_RATING_TOOLTIP_WAITTIME_2 :{GOLD}{NUM} day{P "" s}{STRING1}
STR_STATION_RATING_TOOLTIP_WAITTIME_3 :{YELLOW}{NUM} day{P "" s}{STRING1}
STR_STATION_RATING_TOOLTIP_WAITTIME_4 :{GREEN}{NUM} day{P "" s}{STRING1}
STR_STATION_RATING_TOOLTIP_WAITUNITS :Cargo waiting (on average per next stop{STRING1}): {STRING3}
STR_STATION_RATING_TOOLTIP_WAITUNITS_0 :{RED}{NUM}{STRING1}
STR_STATION_RATING_TOOLTIP_WAITUNITS_1 :{ORANGE}{NUM}{STRING1}
STR_STATION_RATING_TOOLTIP_WAITUNITS_2 :{GOLD}{NUM}{STRING1}
STR_STATION_RATING_TOOLTIP_WAITUNITS_3 :{YELLOW}{NUM}{STRING1}
STR_STATION_RATING_TOOLTIP_WAITUNITS_4 :{YELLOW}{NUM}{STRING1}
STR_STATION_RATING_TOOLTIP_WAITUNITS_5 :{GREEN}{NUM}{STRING1}
STR_STATION_RATING_TOOLTIP_STATUE :Statue in town{STRING1}: {STRING2}
STR_STATION_RATING_TOOLTIP_STATUE_NO :{GOLD}no{STRING1}
STR_STATION_RATING_TOOLTIP_STATUE_YES :{GREEN}yes{STRING1}
STR_STATION_RATING_TOOLTIP_TRAIN :Train
STR_STATION_RATING_TOOLTIP_ROAD_VEHICLE :Road Vehicle
STR_STATION_RATING_TOOLTIP_SHIP :Ship
STR_STATION_RATING_TOOLTIP_AIRCRAFT :Aircraft
STR_STATION_RATING_TOOLTIP_INVALID :N/A
STR_TMPL_RPL_TITLE :{WHITE}Template Replacement
STR_TMPL_TEMPLATE_REPLACEMENT :Template Replacement
STR_TMPL_TRAINS_IN_GROUP :{BLACK}Trains in group

@ -1839,6 +1839,7 @@ static SettingsContainer &GetSettingsTree()
interface->Add(new SettingEntry("gui.show_depot_sell_gui"));
interface->Add(new SettingEntry("gui.open_vehicle_gui_clone_share"));
interface->Add(new SettingEntry("gui.vehicle_names"));
interface->Add(new SettingEntry("gui.station_rating_tooltip_mode"));
}
SettingsPage *advisors = main->Add(new SettingsPage(STR_CONFIG_SETTING_ADVISORS));

@ -220,6 +220,7 @@ struct GUISettings : public TimeSettings {
uint8 linkgraph_colours; ///< linkgraph overlay colours
uint8 vehicle_names; ///< Vehicle naming scheme
bool shade_trees_on_slopes; ///< Shade trees on slopes
uint8 station_rating_tooltip_mode; ///< Station rating tooltip mode
uint16 console_backlog_timeout; ///< the minimum amount of time items should be in the console backlog before they will be removed in ~3 seconds granularity.
uint16 console_backlog_length; ///< the minimum amount of items in the console backlog before items will be removed.

@ -3767,6 +3767,136 @@ static void TruncateCargo(const CargoSpec *cs, GoodsEntry *ge, uint amount = UIN
}
}
bool GetNewGrfRating(const Station *st, const CargoSpec *cs, const GoodsEntry *ge, int *new_grf_rating)
{
*new_grf_rating = 0;
bool is_using_newgrf_rating = false;
/* Perform custom station rating. If it succeeds the speed, days in transit and
* waiting cargo ratings must not be executed. */
/* NewGRFs expect last speed to be 0xFF when no vehicle has arrived yet. */
uint last_speed = ge->HasVehicleEverTriedLoading() && ge->IsSupplyAllowed() ? ge->last_speed : 0xFF;
uint32 var18 = std::min<uint>(ge->time_since_pickup, 0xFFu)
| (std::min<uint>(ge->max_waiting_cargo, 0xFFFFu) << 8)
| (std::min<uint>(last_speed, 0xFFu) << 24);
/* Convert to the 'old' vehicle types */
uint32 var10 = (ge->last_vehicle_type == VEH_INVALID) ? 0x0 : (ge->last_vehicle_type + 0x10);
uint16 callback = GetCargoCallback(CBID_CARGO_STATION_RATING_CALC, var10, var18, cs);
if (callback != CALLBACK_FAILED) {
is_using_newgrf_rating = true;
*new_grf_rating = GB(callback, 0, 14);
/* Simulate a 15 bit signed value */
if (HasBit(callback, 14)) *new_grf_rating -= 0x4000;
}
return is_using_newgrf_rating;
}
int GetSpeedRating(const GoodsEntry *ge)
{
const int b = ge->last_speed - 85;
return (b >= 0) ? (b >> 2) : 0;
}
int GetWaitTimeRating(const CargoSpec *cs, const GoodsEntry *ge)
{
int rating = 0;
uint wait_time = ge->time_since_pickup;
if (_settings_game.station.cargo_class_rating_wait_time) {
if (cs->classes & CC_PASSENGERS) {
wait_time *= 3;
} else if (cs->classes & CC_REFRIGERATED) {
wait_time *= 2;
} else if (cs->classes & (CC_MAIL | CC_ARMOURED | CC_EXPRESS)) {
wait_time += (wait_time >> 1);
} else if (cs->classes & (CC_BULK | CC_LIQUID)) {
wait_time >>= 2;
}
}
if (ge->last_vehicle_type == VEH_SHIP) wait_time >>= 2;
if (wait_time <= 21) rating += 25;
if (wait_time <= 12) rating += 25;
if (wait_time <= 6) rating += 45;
if (wait_time <= 3) rating += 35;
return rating;
}
int GetWaitingCargoRating(const Station *st, const GoodsEntry *ge)
{
int rating = -90;
uint normalised_max_waiting_cargo = ge->max_waiting_cargo;
if (_settings_game.station.station_size_rating_cargo_amount) {
normalised_max_waiting_cargo *= 8;
if (st->station_tiles > 1) normalised_max_waiting_cargo /= st->station_tiles;
}
if (normalised_max_waiting_cargo <= 1500) rating += 55;
if (normalised_max_waiting_cargo <= 1000) rating += 35;
if (normalised_max_waiting_cargo <= 600) rating += 10;
if (normalised_max_waiting_cargo <= 300) rating += 20;
if (normalised_max_waiting_cargo <= 100) rating += 10;
return rating;
}
int GetStatueRating(const Station *st)
{
return Company::IsValidID(st->owner) && HasBit(st->town->statues, st->owner) ? 26 : 0;
}
int GetVehicleAgeRating(const GoodsEntry *ge)
{
int rating = 0;
const byte age = ge->last_age;
if (age < 30) rating += 10;
if (age < 20) rating += 10;
if (age < 10) rating += 13;
return rating;
}
int GetTargetRating(const Station *st, const CargoSpec *cs, const GoodsEntry *ge)
{
bool skip = false;
int rating = 0;
if (_extra_cheats.station_rating.value) {
rating = 255;
skip = true;
} else if (HasBit(cs->callback_mask, CBM_CARGO_STATION_RATING_CALC)) {
int new_grf_rating;
if (GetNewGrfRating(st, cs, ge, &new_grf_rating)) {
skip = true;
rating += new_grf_rating;
}
}
if (!skip) {
rating += GetSpeedRating(ge);
rating += GetWaitTimeRating(cs, ge);
rating += GetWaitingCargoRating(st, ge);
}
rating += GetStatueRating(st);
rating += GetVehicleAgeRating(ge);
return Clamp(rating, 0, 255);
}
static void UpdateStationRating(Station *st)
{
bool waiting_changed = false;
@ -3776,6 +3906,7 @@ static void UpdateStationRating(Station *st)
for (const CargoSpec *cs : CargoSpec::Iterate()) {
GoodsEntry *ge = &st->goods[cs->Index()];
/* Slowly increase the rating back to his original level in the case we
* didn't deliver cargo yet to this station. This happens when a bribe
* failed while you didn't moved that cargo yet to a station. */
@ -3786,6 +3917,7 @@ static void UpdateStationRating(Station *st)
/* Only change the rating if we are moving this cargo */
if (ge->HasRating()) {
byte_inc_sat(&ge->time_since_pickup);
if (ge->time_since_pickup == 255 && _settings_game.order.selectgoods) {
ClrBit(ge->status, GoodsEntry::GES_RATING);
ge->last_speed = 0;
@ -3794,95 +3926,28 @@ static void UpdateStationRating(Station *st)
continue;
}
bool skip = false;
int rating = 0;
uint waiting = ge->cargo.AvailableCount();
/* num_dests is at least 1 if there is any cargo as
* INVALID_STATION is also a destination.
*/
uint num_dests = (uint)ge->cargo.Packets()->MapSize();
/* Average amount of cargo per next hop, but prefer solitary stations
* with only one or two next hops. They are allowed to have more
* cargo waiting per next hop.
* With manual cargo distribution waiting_avg = waiting / 2 as then
* INVALID_STATION is the only destination.
*/
uint waiting_avg = waiting / (num_dests + 1);
if (_extra_cheats.station_rating.value) {
ge->rating = rating = 255;
skip = true;
} else if (HasBit(cs->callback_mask, CBM_CARGO_STATION_RATING_CALC)) {
/* Perform custom station rating. If it succeeds the speed, days in transit and
* waiting cargo ratings must not be executed. */
/* NewGRFs expect last speed to be 0xFF when no vehicle has arrived yet. */
uint last_speed = ge->HasVehicleEverTriedLoading() && ge->IsSupplyAllowed() ? ge->last_speed : 0xFF;
uint32 var18 = std::min<uint>(ge->time_since_pickup, 0xFFu)
| (std::min<uint>(ge->max_waiting_cargo, 0xFFFFu) << 8)
| (std::min<uint>(last_speed, 0xFFu) << 24);
/* Convert to the 'old' vehicle types */
uint32 var10 = (ge->last_vehicle_type == VEH_INVALID) ? 0x0 : (ge->last_vehicle_type + 0x10);
uint16 callback = GetCargoCallback(CBID_CARGO_STATION_RATING_CALC, var10, var18, cs);
if (callback != CALLBACK_FAILED) {
skip = true;
rating = GB(callback, 0, 14);
/* Simulate a 15 bit signed value */
if (HasBit(callback, 14)) rating -= 0x4000;
}
}
{
int rating = GetTargetRating(st, cs, ge);
if (!skip) {
int b = ge->last_speed - 85;
if (b >= 0) rating += b >> 2;
uint waittime = ge->time_since_pickup;
if (_settings_game.station.cargo_class_rating_wait_time) {
if (cs->classes & CC_PASSENGERS) {
waittime *= 3;
} else if (cs->classes & CC_REFRIGERATED) {
waittime *= 2;
} else if (cs->classes & (CC_MAIL | CC_ARMOURED | CC_EXPRESS)) {
waittime += (waittime >> 1);
} else if (cs->classes & (CC_BULK | CC_LIQUID)) {
waittime >>= 2;
}
}
if (ge->last_vehicle_type == VEH_SHIP) waittime >>= 2;
if (waittime <= 21) rating += 25;
if (waittime <= 12) rating += 25;
if (waittime <= 6) rating += 45;
if (waittime <= 3) rating += 35;
rating -= 90;
uint normalised_max_waiting_cargo = ge->max_waiting_cargo;
if (_settings_game.station.station_size_rating_cargo_amount) {
normalised_max_waiting_cargo *= 8;
if (st->station_tiles > 1) normalised_max_waiting_cargo /= st->station_tiles;
}
if (normalised_max_waiting_cargo <= 1500) rating += 55;
if (normalised_max_waiting_cargo <= 1000) rating += 35;
if (normalised_max_waiting_cargo <= 600) rating += 10;
if (normalised_max_waiting_cargo <= 300) rating += 20;
if (normalised_max_waiting_cargo <= 100) rating += 10;
}
uint waiting = ge->cargo.AvailableCount();
if (Company::IsValidID(st->owner) && HasBit(st->town->statues, st->owner)) rating += 26;
/* num_dests is at least 1 if there is any cargo as
* INVALID_STATION is also a destination.
*/
const uint num_dests = (uint)ge->cargo.Packets()->MapSize();
byte age = ge->last_age;
if (age < 3) rating += 10;
if (age < 2) rating += 10;
if (age < 1) rating += 13;
/* Average amount of cargo per next hop, but prefer solitary stations
* with only one or two next hops. They are allowed to have more
* cargo waiting per next hop.
* With manual cargo distribution waiting_avg = waiting / 2 as then
* INVALID_STATION is the only destination.
*/
const uint waiting_avg = waiting / (num_dests + 1);
{
int or_ = ge->rating; // old rating
const int old_rating = ge->rating; // old rating
/* only modify rating in steps of -2, -1, 0, 1 or 2 */
ge->rating = rating = or_ + Clamp(Clamp(rating, 0, 255) - or_, -2, 2);
ge->rating = rating = old_rating + Clamp(rating - old_rating, -2, 2);
/* if rating is <= 64 and more than 100 items waiting on average per destination,
* remove some random amount of goods from the station */
@ -3941,6 +4006,7 @@ static void UpdateStationRating(Station *st)
}
StationID index = st->index;
if (waiting_changed) {
SetWindowDirty(WC_STATION_VIEW, index); // update whole window
} else {

@ -42,7 +42,17 @@
#include <set>
#include <vector>
#include "cheat_func.h"
#include "newgrf_callbacks.h"
#include "newgrf_cargo.h"
#include "safeguards.h"
#include "widgets/misc_widget.h"
enum StationRatingTooltipMode {
SRTM_OFF,
SRTM_SIMPLE,
SRTM_DETAILED,
};
/**
* Calculates and draws the accepted or supplied cargo around the selected tile(s)
@ -1313,6 +1323,7 @@ struct StationViewWindow : public Window {
int scroll_to_row; ///< If set, scroll the main viewport to the station pointed to by this row.
int grouping_index; ///< Currently selected entry in the grouping drop down.
int ratings_list_y = 0; ///< Y coordinate of first line in station ratings panel.
Mode current_mode; ///< Currently selected display mode of cargo view.
Grouping groupings[NUM_COLUMNS]; ///< Grouping modes for the different columns.
@ -1435,6 +1446,31 @@ struct StationViewWindow : public Window {
}
}
bool OnTooltip(Point pt, int widget, TooltipCloseCondition close_cond) override
{
if (widget != WID_SV_ACCEPT_RATING_LIST || this->GetWidget<NWidgetCore>(WID_SV_ACCEPTS_RATINGS)->widget_data == STR_STATION_VIEW_RATINGS_BUTTON ||
_settings_client.gui.station_rating_tooltip_mode == SRTM_OFF) {
return false;
}
int ofs_y = pt.y - this->ratings_list_y;
if (ofs_y < 0) return false;
const Station *st = Station::Get(this->window_number);
const CargoSpec *cs;
FOR_ALL_SORTED_STANDARD_CARGOSPECS(cs) {
const GoodsEntry *ge = &st->goods[cs->Index()];
if (!ge->HasRating()) continue;
ofs_y -= FONT_HEIGHT_NORMAL;
if (ofs_y < 0) {
GuiShowStationRatingTooltip(this, st, cs);
break;
}
}
return true;
}
void OnPaint() override
{
const Station *st = Station::Get(this->window_number);
@ -1869,7 +1905,7 @@ struct StationViewWindow : public Window {
* @param r Rectangle of the widget.
* @return Number of lines needed for drawing the cargo ratings.
*/
int DrawCargoRatings(const Rect &r) const
int DrawCargoRatings(const Rect &r)
{
const Station *st = Station::Get(this->window_number);
int y = r.top + WD_FRAMERECT_TOP;
@ -1883,6 +1919,8 @@ struct StationViewWindow : public Window {
DrawString(r.left + WD_FRAMERECT_LEFT, r.right - WD_FRAMERECT_RIGHT, y, STR_STATION_VIEW_SUPPLY_RATINGS_TITLE);
y += FONT_HEIGHT_NORMAL;
this->ratings_list_y = y;
const CargoSpec *cs;
FOR_ALL_SORTED_STANDARD_CARGOSPECS(cs) {
const GoodsEntry *ge = &st->goods[cs->Index()];
@ -2538,3 +2576,373 @@ void ShowSelectWaypointIfNeeded(const CommandContainer &cmd, TileArea ta)
{
ShowSelectBaseStationIfNeeded<Waypoint>(cmd, ta);
}
static const NWidgetPart _nested_station_rating_tooltip_widgets[] = {
NWidget(WWT_PANEL, COLOUR_GREY, WID_TT_BACKGROUND), SetMinimalSize(64, 32), EndContainer(),
};
static WindowDesc _station_rating_tooltip_desc(
WDP_MANUAL, nullptr, 0, 0,
WC_STATION_RATING_TOOLTIP, WC_NONE,
0,
_nested_station_rating_tooltip_widgets, lengthof(_nested_station_rating_tooltip_widgets)
);
bool GetNewGrfRating(const Station *st, const CargoSpec *cs, const GoodsEntry *ge, int *new_grf_rating);
int GetSpeedRating(const GoodsEntry *ge);
int GetWaitTimeRating(const CargoSpec *cs, const GoodsEntry *ge);
int GetWaitingCargoRating(const Station *st, const GoodsEntry *ge);
int GetStatueRating(const Station *st);
int GetVehicleAgeRating(const GoodsEntry *ge);
struct StationRatingTooltipWindow : public Window
{
private:
const Station *st;
const CargoSpec *cs;
bool newgrf_rating_used;
static const uint RATING_TOOLTIP_LINE_BUFF_SIZE = 512;
static const uint RATING_TOOLTIP_MAX_LINES = 9;
static const uint RATING_TOOLTIP_NEWGRF_INDENT = 20;
public:
char data[RATING_TOOLTIP_MAX_LINES + 1][RATING_TOOLTIP_LINE_BUFF_SIZE] {};
StationRatingTooltipWindow(Window *parent, const Station *st, const CargoSpec *cs) : Window(&_station_rating_tooltip_desc)
{
this->parent = parent;
this->st = st;
this->cs = cs;
this->newgrf_rating_used = false;
this->InitNested();
CLRBITS(this->flags, WF_WHITE_BORDER);
}
Point OnInitialPosition(int16 sm_width, int16 sm_height, int window_number) override
{
const int scr_top = GetMainViewTop() + 2;
const int scr_bot = GetMainViewBottom() - 2;
Point pt {};
pt.y = Clamp(_cursor.pos.y + _cursor.total_size.y + _cursor.total_offs.y + 5, scr_top, scr_bot);
if (pt.y + sm_height > scr_bot) pt.y = std::min(_cursor.pos.y + _cursor.total_offs.y - 5, scr_bot) - sm_height;
pt.x = sm_width >= _screen.width ? 0 : Clamp(_cursor.pos.x - (sm_width >> 1), 0, _screen.width - sm_width);
return pt;
}
static int RoundRating(const int rating) {
return RoundDivSU(rating * 101, 256);
}
void OnInit() override
{
const GoodsEntry *ge = &this->st->goods[this->cs->Index()];
SetDParam(0, this->cs->name);
GetString(this->data[0], STR_STATION_RATING_TOOLTIP_RATING_DETAILS, lastof(this->data[0]));
if (!ge->HasRating()) {
this->data[1][0] = '\0';
return;
}
uint line_nr = 1;
// Calculate target rating.
bool skip = false;
int total_rating = 0;
const bool detailed = _settings_client.gui.station_rating_tooltip_mode == SRTM_DETAILED;
if (_extra_cheats.station_rating.value) {
total_rating = 255;
skip = true;
GetString(this->data[line_nr], STR_STATION_RATING_TOOLTIP_USING_CHEAT, lastof(this->data[line_nr]));
line_nr++;
} else if (HasBit(cs->callback_mask, CBM_CARGO_STATION_RATING_CALC)) {
int new_grf_rating;
this->newgrf_rating_used = GetNewGrfRating(st, cs, ge, &new_grf_rating);
if (this->newgrf_rating_used) {
skip = true;
total_rating += new_grf_rating;
new_grf_rating = RoundRating(new_grf_rating);
SetDParam(0, STR_STATION_RATING_TOOLTIP_NEWGRF_RATING_0 + (new_grf_rating <= 0 ? 0 : 1));
SetDParam(1, new_grf_rating);
GetString(this->data[line_nr], STR_STATION_RATING_TOOLTIP_NEWGRF_RATING, lastof(this->data[line_nr]));
line_nr++;
const uint last_speed = ge->HasVehicleEverTriedLoading() && ge->IsSupplyAllowed() ? ge->last_speed : 0xFF;
SetDParam(0, std::min<uint>(last_speed, 0xFFu));
switch (ge->last_vehicle_type)
{
case VEH_TRAIN:
SetDParam(1, STR_STATION_RATING_TOOLTIP_TRAIN);
break;
case VEH_ROAD:
SetDParam(1, STR_STATION_RATING_TOOLTIP_ROAD_VEHICLE);
break;
case VEH_SHIP:
SetDParam(1, STR_STATION_RATING_TOOLTIP_SHIP);
break;
case VEH_AIRCRAFT:
SetDParam(1, STR_STATION_RATING_TOOLTIP_AIRCRAFT);
break;
default:
SetDParam(1, STR_STATION_RATING_TOOLTIP_INVALID);
break;
}
GetString(this->data[line_nr], STR_STATION_RATING_TOOLTIP_NEWGRF_SPEED, lastof(this->data[line_nr]));
line_nr++;
SetDParam(0, std::min(ge->max_waiting_cargo, 0xFFFFu));
GetString(this->data[line_nr],
STR_STATION_RATING_TOOLTIP_NEWGRF_WAITUNITS,
lastof(this->data[line_nr]));
line_nr++;
SetDParam(0, ge->time_since_pickup * STATION_RATING_TICKS / (DAY_TICKS * _settings_game.economy.day_length_factor));
GetString(this->data[line_nr], STR_STATION_RATING_TOOLTIP_NEWGRF_WAITTIME, lastof(this->data[line_nr]));
line_nr++;
}
}
if (!skip) {
// Speed
{
const auto speed_rating = GetSpeedRating(ge);
const auto rounded_speed_rating = RoundRating(speed_rating);
SetDParam(0, detailed ? STR_STATION_RATING_MAX_PERCENTAGE : STR_EMPTY);
SetDParam(1, 17);
if (ge->last_speed == 255) {
SetDParam(2, STR_STATION_RATING_TOOLTIP_SPEED_3);
}
else if (rounded_speed_rating == 0) {
SetDParam(2, STR_STATION_RATING_TOOLTIP_SPEED_ZERO);
}
else {
SetDParam(2, STR_STATION_RATING_TOOLTIP_SPEED_0 + std::min(3, speed_rating / 42));
}
SetDParam(3, ge->last_speed);
SetDParam(4, detailed ? STR_STATION_RATING_PERCENTAGE_COMMA : STR_EMPTY);
SetDParam(5, rounded_speed_rating);
switch (ge->last_vehicle_type)
{
case VEH_TRAIN:
SetDParam(6, STR_STATION_RATING_TOOLTIP_TRAIN);
break;
case VEH_ROAD:
SetDParam(6, STR_STATION_RATING_TOOLTIP_ROAD_VEHICLE);
break;
case VEH_SHIP:
SetDParam(6, STR_STATION_RATING_TOOLTIP_SHIP);
break;
case VEH_AIRCRAFT:
SetDParam(6, STR_STATION_RATING_TOOLTIP_AIRCRAFT);
break;
default:
SetDParam(6, STR_STATION_RATING_TOOLTIP_INVALID);
break;
}
GetString(this->data[line_nr], STR_STATION_RATING_TOOLTIP_SPEED, lastof(this->data[line_nr]));
line_nr++;
total_rating += speed_rating;
}
// Wait time
{
const auto wait_time_rating = GetWaitTimeRating(cs, ge);
int wait_time_stage = 0;
if (wait_time_rating >= 130) {
wait_time_stage = 4;
} else if (wait_time_rating >= 95) {
wait_time_stage = 3;
} else if (wait_time_rating >= 50) {
wait_time_stage = 2;
} else if (wait_time_rating >= 25) {
wait_time_stage = 1;
}
SetDParam(0, detailed ? STR_STATION_RATING_MAX_PERCENTAGE : STR_EMPTY);
SetDParam(1, 51);
SetDParam(2, STR_STATION_RATING_TOOLTIP_WAITTIME_0 + wait_time_stage);
SetDParam(3, ge->max_waiting_cargo);
SetDParam(4, detailed ? STR_STATION_RATING_PERCENTAGE_COMMA : STR_EMPTY);
SetDParam(5, RoundRating(wait_time_rating));
GetString(this->data[line_nr],
(ge->last_vehicle_type == VEH_SHIP) ?
STR_STATION_RATING_TOOLTIP_WAITTIME_SHIP :
STR_STATION_RATING_TOOLTIP_WAITTIME,
lastof(this->data[line_nr]));
line_nr++;
total_rating += wait_time_rating;
}
// Waiting cargo
{
const auto cargo_rating = GetWaitingCargoRating(st, ge);
int wait_units_stage = 0;
if (cargo_rating >= 40) {
wait_units_stage = 5;
} else if (cargo_rating >= 30) {
wait_units_stage = 4;
} else if (cargo_rating >= 10) {
wait_units_stage = 3;
} else if (cargo_rating >= 0) {
wait_units_stage = 2;
} else if (cargo_rating >= -35) {
wait_units_stage = 1;
}
SetDParam(0, detailed ? STR_STATION_RATING_MAX_PERCENTAGE_COMMA : STR_EMPTY);
SetDParam(1, 16);
SetDParam(2, STR_STATION_RATING_TOOLTIP_WAITUNITS_0 + wait_units_stage);
SetDParam(3, ge->max_waiting_cargo);
SetDParam(4, detailed ? STR_STATION_RATING_PERCENTAGE_COMMA : STR_EMPTY);
SetDParam(5, RoundRating(cargo_rating));
GetString(this->data[line_nr],
STR_STATION_RATING_TOOLTIP_WAITUNITS,
lastof(this->data[line_nr]));
line_nr++;
total_rating += cargo_rating;
}
}
if (!_extra_cheats.station_rating.value) {
// Statue
const auto statue_rating = GetStatueRating(st);
if (statue_rating > 0 || detailed) {
SetDParam(0, detailed ? STR_STATION_RATING_MAX_PERCENTAGE : STR_EMPTY);
SetDParam(1, 10);
SetDParam(2, (statue_rating > 0) ? STR_STATION_RATING_TOOLTIP_STATUE_YES : STR_STATION_RATING_TOOLTIP_STATUE_NO);
SetDParam(3, detailed ? STR_STATION_RATING_PERCENTAGE_COMMA : STR_EMPTY);
SetDParam(4, (statue_rating > 0) ? 10 : 0);
GetString(this->data[line_nr], STR_STATION_RATING_TOOLTIP_STATUE, lastof(this->data[line_nr]));
line_nr++;
total_rating += statue_rating;
}
// Vehicle age
{
const auto age_rating = GetVehicleAgeRating(ge);
int age_stage = 0;
if (age_rating >= 33) {
age_stage = 3;
} else if (age_rating >= 20) {
age_stage = 2;
} else if (age_rating >= 10) {
age_stage = 1;
}
SetDParam(0, detailed ? STR_STATION_RATING_MAX_PERCENTAGE : STR_EMPTY);
SetDParam(1, 13);
SetDParam(2, STR_STATION_RATING_TOOLTIP_AGE_0 + age_stage);
SetDParam(3, ge->last_age);
SetDParam(4, detailed ? STR_STATION_RATING_PERCENTAGE_COMMA : STR_EMPTY);
SetDParam(5, RoundRating(age_rating));
GetString(this->data[line_nr], STR_STATION_RATING_TOOLTIP_AGE, lastof(this->data[line_nr]));
line_nr++;
total_rating += age_rating;
}
}
total_rating = Clamp(total_rating, 0, 255);
if (detailed) {
SetDParam(0, ToPercent8(total_rating));
GetString(this->data[line_nr], STR_STATION_RATING_TOOLTIP_TOTAL_RATING, lastof(this->data[line_nr]));
line_nr++;
}
this->data[line_nr][0] = '\0';
}
void UpdateWidgetSize(int widget, Dimension *size, const Dimension &padding, Dimension *fill, Dimension *resize) override
{
if (widget != 0) return;
size->height = WD_FRAMETEXT_TOP + WD_FRAMETEXT_BOTTOM + 2;
for (uint i = 0; i <= RATING_TOOLTIP_MAX_LINES; i++) {
if (StrEmpty(this->data[i])) break;
uint width = GetStringBoundingBox(this->data[i]).width + WD_FRAMETEXT_LEFT + WD_FRAMETEXT_RIGHT + 2;
if (this->newgrf_rating_used && i >= 2 && i <= 4) {
width += RATING_TOOLTIP_NEWGRF_INDENT;
}
size->width = std::max(size->width, width);
size->height += FONT_HEIGHT_NORMAL + WD_PAR_VSEP_NORMAL;
}
size->height -= WD_PAR_VSEP_NORMAL;
}
void DrawWidget(const Rect &r, int widget) const override
{
GfxDrawLine(r.left, r.top, r.right, r.top, PC_BLACK);
GfxDrawLine(r.left, r.bottom, r.right, r.bottom, PC_BLACK);
GfxDrawLine(r.left, r.top, r.left, r.bottom, PC_BLACK);
GfxDrawLine(r.right, r.top, r.right, r.bottom, PC_BLACK);
int y = r.top + WD_FRAMETEXT_TOP + 1;
const int left0 = r.left + WD_FRAMETEXT_LEFT + 1;
const int right0 = r.right - WD_FRAMETEXT_RIGHT - 1;
DrawString(left0, right0, y, this->data[0], TC_LIGHT_BLUE, SA_CENTER);
y += FONT_HEIGHT_NORMAL + WD_PAR_VSEP_NORMAL;
for (uint i = 1; i <= RATING_TOOLTIP_MAX_LINES; i++) {
if (StrEmpty(this->data[i])) break;
int left = left0, right = right0;
if (this->newgrf_rating_used && i >= 2 && i <= 4) {
if (_current_text_dir == TD_RTL) {
right -= RATING_TOOLTIP_NEWGRF_INDENT;
}
else {
left += RATING_TOOLTIP_NEWGRF_INDENT;
}
}
DrawString(left, right, y, this->data[i], TC_BLACK);
y += FONT_HEIGHT_NORMAL + WD_PAR_VSEP_NORMAL;
}
}
void OnMouseLoop() override
{
if (!_cursor.in_window || !_mouse_hovering) {
delete this;
}
}
};
void GuiShowStationRatingTooltip(Window *parent, const Station *st, const CargoSpec *cs) {
DeleteWindowById(WC_STATION_RATING_TOOLTIP, 0);
new StationRatingTooltipWindow(parent, st, cs);
}

@ -14,6 +14,9 @@
#include "tilearea_type.h"
#include "window_type.h"
struct Station;
struct CargoSpec;
/** Types of cargo to display for station coverage. */
enum StationCoverageType {
@ -28,4 +31,6 @@ void CheckRedrawStationCoverage(Window *w);
void ShowSelectStationIfNeeded(const CommandContainer &cmd, TileArea ta);
void ShowSelectWaypointIfNeeded(const CommandContainer &cmd, TileArea ta);
void GuiShowStationRatingTooltip(Window *parent, const Station *st, const CargoSpec *cs);
#endif /* STATION_GUI_H */

@ -1331,6 +1331,15 @@ static char *FormatString(char *buff, const char *str_arg, StringParameters *arg
buff = FormatNoCommaNumber(buff, args->GetInt64(SCC_NUM), last);
break;
case SCC_PLUS_NUM: { // {PLUS_NUM}
int64 num = args->GetInt64(SCC_PLUS_NUM);
if (num > 0) {
buff += seprintf(buff, last, "+");
}
buff = FormatNoCommaNumber(buff, num, last);
break;
}
case SCC_ZEROFILL_NUM: { // {ZEROFILL_NUM}
int64 num = args->GetInt64();
buff = FormatZerofillNumber(buff, num, args->GetInt64(), last);

@ -96,6 +96,7 @@ enum StringControlCode {
SCC_DECIMAL,
SCC_DECIMAL1,
SCC_NUM,
SCC_PLUS_NUM,
SCC_ZEROFILL_NUM,
SCC_HEX,
SCC_BYTES,

@ -5468,6 +5468,19 @@ strhelp = STR_CONFIG_SETTING_SHADED_TREES_ON_SLOPES_HELPTEXT
proc = RedrawScreen
cat = SC_BASIC
[SDTC_VAR]
var = gui.station_rating_tooltip_mode
type = SLE_UINT8
flags = SLF_NOT_IN_SAVE | SLF_NO_NETWORK_SYNC
guiflags = SGF_MULTISTRING
def = 1
min = 0
max = 2
str = STR_CONFIG_SETTING_STATION_RATING_TOOLTIP_MODE
strhelp = STR_CONFIG_SETTING_STATION_RATING_TOOLTIP_MODE_HELPTEXT
strval = STR_CONFIG_SETTING_STATION_RATING_TOOLTIP_MODE_OFF
cat = SC_BASIC
; For the dedicated build we'll enable dates in logs by default.
[SDTC_BOOL]
ifdef = DEDICATED

@ -111,6 +111,7 @@ static const CmdStruct _cmd_structs[] = {
{"DECIMAL", EmitSingleChar, SCC_DECIMAL, 2, 0, C_NONE}, // Number with comma and fractional part. Second parameter is number of fractional digits, first parameter is number times 10**(second parameter).
{"DECIMAL1", EmitSingleChar, SCC_DECIMAL1, 1, 0, C_NONE}, // Decimal with fixed second parameter of 1
{"NUM", EmitSingleChar, SCC_NUM, 1, 0, C_NONE}, // Signed number
{"PLUS_NUM", EmitSingleChar, SCC_PLUS_NUM, 1, 0, C_NONE}, // Signed number, with sign (+ or -) shown for both positive and negative numbers
{"ZEROFILL_NUM", EmitSingleChar, SCC_ZEROFILL_NUM, 2, 0, C_NONE}, // Unsigned number with zero fill, e.g. "02". First parameter is number, second minimum length
{"BYTES", EmitSingleChar, SCC_BYTES, 1, 0, C_NONE}, // Unsigned number with "bytes", i.e. "1.02 MiB or 123 KiB"
{"HEX", EmitSingleChar, SCC_HEX, 1, 0, C_NONE}, // Hexadecimally printed number

@ -107,6 +107,12 @@ enum WindowClass {
* - 0 = #ToolTipsWidgets
*/
WC_TOOLTIPS,
/**
* Station rating tooltip window; %Window numbers:
* - 0 = #ToolTipsWidgets
*/
WC_STATION_RATING_TOOLTIP,
/**
* Query string window; %Window numbers:

Loading…
Cancel
Save