Refactor station rating and tooltip - multiply vehicle age check by 10

This commit is contained in:
Andreas Schmitt 2021-06-13 05:48:21 +02:00
parent edf0e8eddd
commit c4c2fedbb1
3 changed files with 318 additions and 183 deletions

View File

@ -6508,6 +6508,9 @@ 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}{NUM}%
STR_STATION_RATING_TOOLTIP_NEWGRF_RATING_1 :{LTBLUE}+{NUM}%

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;
skip = GetNewGrfRating(st, cs, ge, &new_grf_rating);
if (!skip) {
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;
}
}
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 */
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 {

View File

@ -42,6 +42,7 @@
#include <set>
#include <vector>
#include "cheat_func.h"
#include "newgrf_callbacks.h"
#include "newgrf_cargo.h"
#include "safeguards.h"
@ -2580,8 +2581,12 @@ static WindowDesc _station_rating_tooltip_desc(
_nested_station_rating_tooltip_widgets, lengthof(_nested_station_rating_tooltip_widgets)
);
static const int _station_rating_age[] = { 0, 10, 20, 33 };
static const int _station_rating_wait_units[] = { -90, -38, 14, 66, 118, 170 };
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
{
@ -2591,7 +2596,7 @@ private:
bool newgrf_rating_used;
static const uint RATING_TOOLTIP_LINE_BUFF_SIZE = 512;
static const uint RATING_TOOLTIP_MAX_LINES = 8;
static const uint RATING_TOOLTIP_MAX_LINES = 9;
static const uint RATING_TOOLTIP_NEWGRF_INDENT = 20;
public:
@ -2635,32 +2640,35 @@ public:
this->data[1][0] = '\0';
return;
}
uint line_nr = 1;
// Calculate target rating.
bool skip = false;
int total_rating = 0;
if (HasBit(cs->callback_mask, CBM_CARGO_STATION_RATING_CALC)) {
const uint last_speed = ge->HasVehicleEverTriedLoading() ? ge->last_speed : 0xFF;
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)) {
const uint32 var18 = std::min(ge->time_since_pickup, (byte)0xFF) | (std::min(ge->max_waiting_cargo, (uint)0xFFFF) << 8) | (std::min(last_speed, (uint)0xFF) << 24);
const uint32 var10 = (ge->last_vehicle_type == VEH_INVALID) ? 0x0 : (ge->last_vehicle_type + 0x10);
const uint16 callback = GetCargoCallback(CBID_CARGO_STATION_RATING_CALC, var10, var18, this->cs);
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);
if (callback != CALLBACK_FAILED) {
int newgrf_rating = GB(callback, 0, 14);
if (HasBit(callback, 14)) newgrf_rating -= 0x4000;
this->newgrf_rating_used = true;
total_rating += newgrf_rating;
newgrf_rating = this->RoundRating(newgrf_rating);
SetDParam(0, STR_STATION_RATING_TOOLTIP_NEWGRF_RATING_0 + (newgrf_rating <= 0 ? 0 : 1));
SetDParam(1, newgrf_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++;
SetDParam(0, std::min(50/*ge->last_unprocessed_speed*/, 0xFF));
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)
{
@ -2684,102 +2692,160 @@ public:
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, (uint)0xFFFF));
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);
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 (!this->newgrf_rating_used) {
int wait_units_stage = 0;
(ge->max_waiting_cargo > 2000) ||
(wait_units_stage = 1, ge->max_waiting_cargo > 1000) ||
(wait_units_stage = 2, ge->max_waiting_cargo > 500) ||
(wait_units_stage = 3, ge->max_waiting_cargo > 250) ||
(wait_units_stage = 4, ge->max_waiting_cargo > 125) ||
(wait_units_stage = 5, true);
total_rating += _station_rating_wait_units[wait_units_stage];
SetDParam(0, STR_STATION_RATING_TOOLTIP_WAITUNITS_0 + wait_units_stage);
SetDParam(1, ge->max_waiting_cargo);
SetDParam(2, RoundRating(_station_rating_wait_units[wait_units_stage]));
GetString(this->data[line_nr],
STR_STATION_RATING_TOOLTIP_WAITUNITS,
lastof(this->data[line_nr]));
line_nr++;
const int b = ge->last_speed - 15;
const int r_speed = b >= 0 ? b >> 2 : 0;
const int r_speed_round = RoundRating(r_speed);
total_rating += r_speed;
if (ge->last_speed == 255) {
SetDParam(0, STR_STATION_RATING_TOOLTIP_SPEED_3);
}
else if (r_speed_round == 0) {
SetDParam(0, STR_STATION_RATING_TOOLTIP_SPEED_ZERO);
}
else {
SetDParam(0, STR_STATION_RATING_TOOLTIP_SPEED_0 + std::min(3, r_speed / 15));
}
SetDParam(1, 50/*ge->last_unprocessed_speed*/);
SetDParam(2, r_speed_round);
switch (ge->last_vehicle_type)
if (!skip) {
// Speed
{
case VEH_TRAIN:
SetDParam(3, STR_STATION_RATING_TOOLTIP_TRAIN);
break;
case VEH_ROAD:
SetDParam(3, STR_STATION_RATING_TOOLTIP_ROAD_VEHICLE);
break;
case VEH_SHIP:
SetDParam(3, STR_STATION_RATING_TOOLTIP_SHIP);
break;
case VEH_AIRCRAFT:
SetDParam(3, STR_STATION_RATING_TOOLTIP_AIRCRAFT);
break;
default:
SetDParam(3, STR_STATION_RATING_TOOLTIP_INVALID);
break;
const auto speed_rating = GetSpeedRating(ge);
const auto rounded_speed_rating = RoundRating(speed_rating);
if (ge->last_speed == 255) {
SetDParam(0, STR_STATION_RATING_TOOLTIP_SPEED_3);
}
else if (rounded_speed_rating == 0) {
SetDParam(0, STR_STATION_RATING_TOOLTIP_SPEED_ZERO);
}
else {
SetDParam(0, STR_STATION_RATING_TOOLTIP_SPEED_0 + std::min(3, speed_rating / 42));
}
SetDParam(1, ge->last_speed);
SetDParam(2, rounded_speed_rating);
switch (ge->last_vehicle_type)
{
case VEH_TRAIN:
SetDParam(3, STR_STATION_RATING_TOOLTIP_TRAIN);
break;
case VEH_ROAD:
SetDParam(3, STR_STATION_RATING_TOOLTIP_ROAD_VEHICLE);
break;
case VEH_SHIP:
SetDParam(3, STR_STATION_RATING_TOOLTIP_SHIP);
break;
case VEH_AIRCRAFT:
SetDParam(3, STR_STATION_RATING_TOOLTIP_AIRCRAFT);
break;
default:
SetDParam(3, 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;
}
GetString(this->data[line_nr], STR_STATION_RATING_TOOLTIP_SPEED, lastof(this->data[line_nr]));
line_nr++;
// 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, STR_STATION_RATING_TOOLTIP_WAITTIME_0 + wait_time_stage);
SetDParam(1, ge->max_waiting_cargo);
SetDParam(2, 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, STR_STATION_RATING_TOOLTIP_WAITUNITS_0 + wait_units_stage);
SetDParam(1, ge->max_waiting_cargo);
SetDParam(2, RoundRating(cargo_rating));
GetString(this->data[line_nr],
STR_STATION_RATING_TOOLTIP_WAITUNITS,
lastof(this->data[line_nr]));
line_nr++;
total_rating += cargo_rating;
}
}
int age_stage = 0;
(ge->last_age >= 30) ||
(age_stage = 1, ge->last_age >= 20) ||
(age_stage = 2, ge->last_age >= 10) ||
(age_stage = 3, true);
if (!_extra_cheats.station_rating.value) {
// Statue
{
const auto statue_rating = GetStatueRating(st);
total_rating += _station_rating_age[age_stage];
SetDParam(0, STR_STATION_RATING_TOOLTIP_AGE_0 + age_stage);
SetDParam(1, ge->last_age);
SetDParam(2, RoundRating(_station_rating_age[age_stage]));
GetString(this->data[line_nr], STR_STATION_RATING_TOOLTIP_AGE, lastof(this->data[line_nr]));
line_nr++;
SetDParam(0, (statue_rating > 0) ? STR_STATION_RATING_TOOLTIP_STATUE_YES : STR_STATION_RATING_TOOLTIP_STATUE_NO);
GetString(this->data[line_nr], STR_STATION_RATING_TOOLTIP_STATUE, lastof(this->data[line_nr]));
line_nr++;
total_rating += statue_rating;
}
if (Company::IsValidID(st->owner) && HasBit(st->town->statues, st->owner)) {
SetDParam(0, STR_STATION_RATING_TOOLTIP_STATUE_YES);
total_rating += 26;
}
else {
SetDParam(0, STR_STATION_RATING_TOOLTIP_STATUE_NO);
// 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, STR_STATION_RATING_TOOLTIP_AGE_0 + age_stage);
SetDParam(1, ge->last_age);
SetDParam(2, RoundRating(age_rating));
GetString(this->data[line_nr], STR_STATION_RATING_TOOLTIP_AGE, lastof(this->data[line_nr]));
line_nr++;
total_rating += age_rating;
}
}
GetString(this->data[line_nr], STR_STATION_RATING_TOOLTIP_STATUE, lastof(this->data[line_nr]));
line_nr++;
total_rating = Clamp(total_rating, 0, 255);
SetDParam(0, ToPercent8(Clamp(total_rating, 0, 255)));
SetDParam(0, ToPercent8(total_rating));
GetString(this->data[line_nr], STR_STATION_RATING_TOOLTIP_TOTAL_RATING, lastof(this->data[line_nr]));
line_nr++;