diff --git a/src/lang/english.txt b/src/lang/english.txt index eba78574a4..2bb5fa5ad1 100644 --- a/src/lang/english.txt +++ b/src/lang/english.txt @@ -4965,6 +4965,9 @@ STR_VEHICLE_DETAILS_CARGO_EMPTY :{LTBLUE}Empty STR_VEHICLE_DETAILS_CARGO_FROM :{LTBLUE}{CARGO_LONG} from {STATION} STR_VEHICLE_DETAILS_CARGO_FROM_MULT :{LTBLUE}{CARGO_LONG} from {STATION} (x{NUM}) +STR_VEHICLE_DETAILS_TRAIN_TOTAL_WEIGHT :{BLACK}Total weight: {LTBLUE}{WEIGHT_SHORT} (empty) - {LTBLUE}{WEIGHT_SHORT} (loaded) +STR_VEHICLE_DETAILS_TRAIN_MAX_SPEED :{BLACK}Max. speed: {LTBLUE}{VELOCITY} (empty) - {LTBLUE}{VELOCITY} (loaded) + STR_VEHICLE_DETAIL_TAB_CARGO :{BLACK}Cargo STR_VEHICLE_DETAILS_TRAIN_CARGO_TOOLTIP :{BLACK}Show details of cargo carried STR_VEHICLE_DETAIL_TAB_INFORMATION :{BLACK}Information diff --git a/src/train.h b/src/train.h index 6fd5186dd6..4efcef018d 100644 --- a/src/train.h +++ b/src/train.h @@ -276,6 +276,48 @@ public: } } + /** + * Gets the weight of this vehicle when empty. + * @return Empty weight in tonnes. + */ + uint16 GetEmptyWeight() const + { + uint16 weight = 0; + + /* Vehicle weight is not added for articulated parts. */ + if (!this->IsArticulatedPart()) { + weight += GetVehicleProperty(this, PROP_TRAIN_WEIGHT, RailVehInfo(this->engine_type)->weight); + } + + /* Powered wagons have extra weight added. */ + if (HasBit(this->flags, VRF_POWEREDWAGON)) { + weight += RailVehInfo(this->gcache.first_engine)->pow_wag_weight; + } + + return weight * (this->IsWagon() ? FreightWagonMult(this->cargo_type) : 1); + } + + /** + * Gets the weight of this vehicle when fully loaded. + * @return Loaded weight in tonnes. + */ + uint16 GetLoadedWeight() const + { + uint16 weight = (CargoSpec::Get(this->cargo_type)->weight * this->cargo_cap) / 16; + + /* Vehicle weight is not added for articulated parts. */ + if (!this->IsArticulatedPart()) { + weight += GetVehicleProperty(this, PROP_TRAIN_WEIGHT, RailVehInfo(this->engine_type)->weight); + } + + /* Powered wagons have extra weight added. */ + if (HasBit(this->flags, VRF_POWEREDWAGON)) { + weight += RailVehInfo(this->gcache.first_engine)->pow_wag_weight; + } + + return weight * (this->IsWagon() ? FreightWagonMult(this->cargo_type) : 1); + } + protected: // These functions should not be called outside acceleration code. /** * Gets the speed a broken down train (low speed breakdown) is limited to. diff --git a/src/train_gui.cpp b/src/train_gui.cpp index 51b50e4e25..db5b4a7103 100644 --- a/src/train_gui.cpp +++ b/src/train_gui.cpp @@ -390,7 +390,7 @@ int GetTrainDetailsWndVScroll(VehicleID veh_id, TrainDetailsWindowTabs det_tab) for (CargoID i = 0; i < NUM_CARGO; i++) { if (max_cargo[i] > 0) num++; // only count carriages that the train has } - num++; // needs one more because first line is description string + num += 5; // needs five more because first line is description string and we have the weight and speed info } else { for (const Train *v = Train::Get(veh_id); v != nullptr; v = v->GetNextVehicle()) { GetCargoSummaryOfArticulatedVehicle(v, &_cargo_summary); @@ -512,19 +512,52 @@ void DrawTrainDetails(const Train *v, int left, int right, int y, int vscroll_po CargoArray act_cargo; CargoArray max_cargo; Money feeder_share = 0; + uint16 empty_weight = 0; + uint16 loaded_weight = 0; + uint16 empty_max_speed = 0; + uint16 loaded_max_speed = 0; for (const Vehicle *u = v; u != nullptr; u = u->Next()) { act_cargo[u->cargo_type] += u->cargo.StoredCount(); max_cargo[u->cargo_type] += u->cargo_cap; feeder_share += u->cargo.FeederShare(); + empty_weight += Train::From(u)->GetEmptyWeight(); + loaded_weight += Train::From(u)->GetLoadedWeight(); } - /* draw total cargo tab */ - DrawString(left, right, y + text_y_offset, STR_VEHICLE_DETAILS_TRAIN_TOTAL_CAPACITY_TEXT); - y += line_height; + const auto rolling_friction = 15 * (512 + v->GetDisplayMaxSpeed()) / 512; + const auto tractive_effort_empty = empty_weight * rolling_friction; + const auto tractive_effort_loaded = loaded_weight * rolling_friction; + const auto power = v->gcache.cached_power * 746ll; + const auto max_te = v->gcache.cached_max_te; + empty_max_speed = std::min(v->GetDisplayMaxSpeed(), (tractive_effort_empty == 0 || tractive_effort_empty > max_te) ? 0 : (3.6 * (power / tractive_effort_empty))); + loaded_max_speed = std::min(v->GetDisplayMaxSpeed(), (tractive_effort_loaded == 0 || tractive_effort_loaded > max_te) ? 0 : (3.6 * (power / tractive_effort_loaded))); + + if (--vscroll_pos < 0 && vscroll_pos >= -vscroll_cap) { + SetDParam(0, empty_weight); + SetDParam(1, loaded_weight); + DrawString(left, right, y + text_y_offset, STR_VEHICLE_DETAILS_TRAIN_TOTAL_WEIGHT); + y += line_height; + } + + if (--vscroll_pos < 0 && vscroll_pos >= -vscroll_cap) { + SetDParam(0, empty_max_speed); + SetDParam(1, loaded_max_speed); + DrawString(left, right, y + text_y_offset, STR_VEHICLE_DETAILS_TRAIN_MAX_SPEED); + y += line_height; + } + + if (--vscroll_pos < 0 && vscroll_pos >= -vscroll_cap) { + y += line_height; + } + + if (--vscroll_pos < 0 && vscroll_pos >= -vscroll_cap) { + DrawString(left, right, y + text_y_offset, STR_VEHICLE_DETAILS_TRAIN_TOTAL_CAPACITY_TEXT); + y += line_height; + } for (CargoID i = 0; i < NUM_CARGO; i++) { - if (max_cargo[i] > 0 && --vscroll_pos < 0 && vscroll_pos > -vscroll_cap) { + if (max_cargo[i] > 0 && --vscroll_pos < 0 && vscroll_pos >= -vscroll_cap) { SetDParam(0, i); // {CARGO} #1 SetDParam(1, act_cargo[i]); // {CARGO} #2 SetDParam(2, i); // {SHORTCARGO} #1 @@ -534,7 +567,16 @@ void DrawTrainDetails(const Train *v, int left, int right, int y, int vscroll_po y += line_height; } } - SetDParam(0, feeder_share); - DrawString(left, right, y + text_y_offset, STR_VEHICLE_INFO_FEEDER_CARGO_VALUE); + + for (const Vehicle *u = v; u != nullptr; u = u->Next()) { + act_cargo[u->cargo_type] += u->cargo.StoredCount(); + max_cargo[u->cargo_type] += u->cargo_cap; + feeder_share += u->cargo.FeederShare(); + } + + if (--vscroll_pos < 0 && vscroll_pos >= -vscroll_cap) { + SetDParam(0, feeder_share); + DrawString(left, right, y + text_y_offset, STR_VEHICLE_INFO_FEEDER_CARGO_VALUE); + } } }