Merge PR #274 into jgrpp

This commit is contained in:
Jonathan G Rennison
2021-06-15 02:01:22 +01:00
11 changed files with 659 additions and 87 deletions

View File

@@ -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 :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_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 :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. 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_3x3_GRID :3x3 town road grid
STR_ZONING_ONE_WAY_ROAD :One way roads 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_RPL_TITLE :{WHITE}Template Replacement
STR_TMPL_TEMPLATE_REPLACEMENT :Template Replacement STR_TMPL_TEMPLATE_REPLACEMENT :Template Replacement
STR_TMPL_TRAINS_IN_GROUP :{BLACK}Trains in group STR_TMPL_TRAINS_IN_GROUP :{BLACK}Trains in group

View File

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

View File

@@ -220,6 +220,7 @@ struct GUISettings : public TimeSettings {
uint8 linkgraph_colours; ///< linkgraph overlay colours uint8 linkgraph_colours; ///< linkgraph overlay colours
uint8 vehicle_names; ///< Vehicle naming scheme uint8 vehicle_names; ///< Vehicle naming scheme
bool shade_trees_on_slopes; ///< Shade trees on slopes 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_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. uint16 console_backlog_length; ///< the minimum amount of items in the console backlog before items will be removed.

View File

@@ -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) static void UpdateStationRating(Station *st)
{ {
bool waiting_changed = false; bool waiting_changed = false;
@@ -3776,6 +3906,7 @@ static void UpdateStationRating(Station *st)
for (const CargoSpec *cs : CargoSpec::Iterate()) { for (const CargoSpec *cs : CargoSpec::Iterate()) {
GoodsEntry *ge = &st->goods[cs->Index()]; GoodsEntry *ge = &st->goods[cs->Index()];
/* Slowly increase the rating back to his original level in the case we /* 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 * 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. */ * 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 */ /* Only change the rating if we are moving this cargo */
if (ge->HasRating()) { if (ge->HasRating()) {
byte_inc_sat(&ge->time_since_pickup); byte_inc_sat(&ge->time_since_pickup);
if (ge->time_since_pickup == 255 && _settings_game.order.selectgoods) { if (ge->time_since_pickup == 255 && _settings_game.order.selectgoods) {
ClrBit(ge->status, GoodsEntry::GES_RATING); ClrBit(ge->status, GoodsEntry::GES_RATING);
ge->last_speed = 0; ge->last_speed = 0;
@@ -3794,95 +3926,28 @@ static void UpdateStationRating(Station *st)
continue; 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;
}
}
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;
}
if (Company::IsValidID(st->owner) && HasBit(st->town->statues, st->owner)) rating += 26;
byte age = ge->last_age;
if (age < 3) rating += 10;
if (age < 2) rating += 10;
if (age < 1) rating += 13;
{ {
int or_ = ge->rating; // old rating int rating = GetTargetRating(st, cs, ge);
uint waiting = ge->cargo.AvailableCount();
/* 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();
/* 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);
const int old_rating = ge->rating; // old rating
/* only modify rating in steps of -2, -1, 0, 1 or 2 */ /* 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, /* if rating is <= 64 and more than 100 items waiting on average per destination,
* remove some random amount of goods from the station */ * remove some random amount of goods from the station */
@@ -3941,6 +4006,7 @@ static void UpdateStationRating(Station *st)
} }
StationID index = st->index; StationID index = st->index;
if (waiting_changed) { if (waiting_changed) {
SetWindowDirty(WC_STATION_VIEW, index); // update whole window SetWindowDirty(WC_STATION_VIEW, index); // update whole window
} else { } else {

View File

@@ -42,7 +42,17 @@
#include <set> #include <set>
#include <vector> #include <vector>
#include "cheat_func.h"
#include "newgrf_callbacks.h"
#include "newgrf_cargo.h"
#include "safeguards.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) * 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 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 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. Mode current_mode; ///< Currently selected display mode of cargo view.
Grouping groupings[NUM_COLUMNS]; ///< Grouping modes for the different columns. 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 void OnPaint() override
{ {
const Station *st = Station::Get(this->window_number); const Station *st = Station::Get(this->window_number);
@@ -1869,7 +1905,7 @@ struct StationViewWindow : public Window {
* @param r Rectangle of the widget. * @param r Rectangle of the widget.
* @return Number of lines needed for drawing the cargo ratings. * @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); const Station *st = Station::Get(this->window_number);
int y = r.top + WD_FRAMERECT_TOP; 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); DrawString(r.left + WD_FRAMERECT_LEFT, r.right - WD_FRAMERECT_RIGHT, y, STR_STATION_VIEW_SUPPLY_RATINGS_TITLE);
y += FONT_HEIGHT_NORMAL; y += FONT_HEIGHT_NORMAL;
this->ratings_list_y = y;
const CargoSpec *cs; const CargoSpec *cs;
FOR_ALL_SORTED_STANDARD_CARGOSPECS(cs) { FOR_ALL_SORTED_STANDARD_CARGOSPECS(cs) {
const GoodsEntry *ge = &st->goods[cs->Index()]; const GoodsEntry *ge = &st->goods[cs->Index()];
@@ -2538,3 +2576,373 @@ void ShowSelectWaypointIfNeeded(const CommandContainer &cmd, TileArea ta)
{ {
ShowSelectBaseStationIfNeeded<Waypoint>(cmd, 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);
}

View File

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

View File

@@ -1331,6 +1331,15 @@ static char *FormatString(char *buff, const char *str_arg, StringParameters *arg
buff = FormatNoCommaNumber(buff, args->GetInt64(SCC_NUM), last); buff = FormatNoCommaNumber(buff, args->GetInt64(SCC_NUM), last);
break; 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} case SCC_ZEROFILL_NUM: { // {ZEROFILL_NUM}
int64 num = args->GetInt64(); int64 num = args->GetInt64();
buff = FormatZerofillNumber(buff, num, args->GetInt64(), last); buff = FormatZerofillNumber(buff, num, args->GetInt64(), last);

View File

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

View File

@@ -5468,6 +5468,19 @@ strhelp = STR_CONFIG_SETTING_SHADED_TREES_ON_SLOPES_HELPTEXT
proc = RedrawScreen proc = RedrawScreen
cat = SC_BASIC 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. ; For the dedicated build we'll enable dates in logs by default.
[SDTC_BOOL] [SDTC_BOOL]
ifdef = DEDICATED ifdef = DEDICATED

View File

@@ -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). {"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 {"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 {"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 {"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" {"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 {"HEX", EmitSingleChar, SCC_HEX, 1, 0, C_NONE}, // Hexadecimally printed number

View File

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