1
0
Fork 0

Compare commits

...

5 Commits

Author SHA1 Message Date
Peter Nelson d6fa43910f
Merge b9eac1ee78 into ae917cb8c6 2025-07-23 04:50:29 +00:00
translators ae917cb8c6 Update: Translations from eints
vietnamese: 134 changes by MagicalDrizzle
2025-07-23 04:48:20 +00:00
Peter Nelson b9eac1ee78
Codechange: Use StrongType and StrongBitSet for CargoType(s). 2025-06-28 21:52:39 +01:00
Peter Nelson 329a2da122
Codechange: Add StrongBitSet helper to simplify making a bitset from a StrongType. 2025-06-28 21:50:56 +01:00
Peter Nelson 98430f99d1
Codechange: Add parameterless Flip() method to BaseBitSet.
This allows flipping all valid bits.
2025-06-28 21:50:56 +01:00
71 changed files with 545 additions and 435 deletions

View File

@ -117,12 +117,12 @@ static inline std::pair<CargoType, uint16_t> GetVehicleDefaultCapacity(EngineID
static inline CargoTypes GetAvailableVehicleCargoTypes(EngineID engine, bool include_initial_cargo_type) static inline CargoTypes GetAvailableVehicleCargoTypes(EngineID engine, bool include_initial_cargo_type)
{ {
const Engine *e = Engine::Get(engine); const Engine *e = Engine::Get(engine);
if (!e->CanCarryCargo()) return 0; if (!e->CanCarryCargo()) return {};
CargoTypes cargoes = e->info.refit_mask; CargoTypes cargoes = e->info.refit_mask;
if (include_initial_cargo_type) { if (include_initial_cargo_type) {
SetBit(cargoes, e->GetDefaultCargoType()); cargoes.Set(e->GetDefaultCargoType());
} }
return cargoes; return cargoes;
@ -165,11 +165,11 @@ CargoArray GetCapacityOfArticulatedParts(EngineID engine)
*/ */
CargoTypes GetCargoTypesOfArticulatedParts(EngineID engine) CargoTypes GetCargoTypesOfArticulatedParts(EngineID engine)
{ {
CargoTypes cargoes = 0; CargoTypes cargoes{};
const Engine *e = Engine::Get(engine); const Engine *e = Engine::Get(engine);
if (auto [cargo, cap] = GetVehicleDefaultCapacity(engine); IsValidCargoType(cargo) && cap > 0) { if (auto [cargo, cap] = GetVehicleDefaultCapacity(engine); IsValidCargoType(cargo) && cap > 0) {
SetBit(cargoes, cargo); cargoes.Set(cargo);
} }
if (!e->IsGroundVehicle()) return cargoes; if (!e->IsGroundVehicle()) return cargoes;
@ -181,7 +181,7 @@ CargoTypes GetCargoTypesOfArticulatedParts(EngineID engine)
if (artic_engine == EngineID::Invalid()) break; if (artic_engine == EngineID::Invalid()) break;
if (auto [cargo, cap] = GetVehicleDefaultCapacity(artic_engine); IsValidCargoType(cargo) && cap > 0) { if (auto [cargo, cap] = GetVehicleDefaultCapacity(artic_engine); IsValidCargoType(cargo) && cap > 0) {
SetBit(cargoes, cargo); cargoes.Set(cargo);
} }
} }
@ -224,7 +224,7 @@ void GetArticulatedRefitMasks(EngineID engine, bool include_initial_cargo_type,
const Engine *e = Engine::Get(engine); const Engine *e = Engine::Get(engine);
CargoTypes veh_cargoes = GetAvailableVehicleCargoTypes(engine, include_initial_cargo_type); CargoTypes veh_cargoes = GetAvailableVehicleCargoTypes(engine, include_initial_cargo_type);
*union_mask = veh_cargoes; *union_mask = veh_cargoes;
*intersection_mask = (veh_cargoes != 0) ? veh_cargoes : ALL_CARGOTYPES; *intersection_mask = veh_cargoes.Any() ? veh_cargoes : ALL_CARGOTYPES;
if (!e->IsGroundVehicle()) return; if (!e->IsGroundVehicle()) return;
if (!e->info.callback_mask.Test(VehicleCallbackMask::ArticEngine)) return; if (!e->info.callback_mask.Test(VehicleCallbackMask::ArticEngine)) return;
@ -234,8 +234,8 @@ void GetArticulatedRefitMasks(EngineID engine, bool include_initial_cargo_type,
if (artic_engine == EngineID::Invalid()) break; if (artic_engine == EngineID::Invalid()) break;
veh_cargoes = GetAvailableVehicleCargoTypes(artic_engine, include_initial_cargo_type); veh_cargoes = GetAvailableVehicleCargoTypes(artic_engine, include_initial_cargo_type);
*union_mask |= veh_cargoes; union_mask->Set(veh_cargoes);
if (veh_cargoes != 0) *intersection_mask &= veh_cargoes; if (veh_cargoes.Any()) *intersection_mask = *intersection_mask & veh_cargoes;
} }
} }
@ -261,12 +261,12 @@ CargoTypes GetUnionOfArticulatedRefitMasks(EngineID engine, bool include_initial
*/ */
CargoTypes GetCargoTypesOfArticulatedVehicle(const Vehicle *v, CargoType *cargo_type) CargoTypes GetCargoTypesOfArticulatedVehicle(const Vehicle *v, CargoType *cargo_type)
{ {
CargoTypes cargoes = 0; CargoTypes cargoes{};
CargoType first_cargo = INVALID_CARGO; CargoType first_cargo = INVALID_CARGO;
do { do {
if (IsValidCargoType(v->cargo_type) && v->GetEngine()->CanCarryCargo()) { if (IsValidCargoType(v->cargo_type) && v->GetEngine()->CanCarryCargo()) {
SetBit(cargoes, v->cargo_type); cargoes.Set(v->cargo_type);
if (!IsValidCargoType(first_cargo)) first_cargo = v->cargo_type; if (!IsValidCargoType(first_cargo)) first_cargo = v->cargo_type;
if (first_cargo != v->cargo_type) { if (first_cargo != v->cargo_type) {
if (cargo_type != nullptr) { if (cargo_type != nullptr) {
@ -299,24 +299,24 @@ void CheckConsistencyOfArticulatedVehicle(const Vehicle *v)
GetArticulatedRefitMasks(v->engine_type, true, &purchase_refit_union, &purchase_refit_intersection); GetArticulatedRefitMasks(v->engine_type, true, &purchase_refit_union, &purchase_refit_intersection);
CargoArray purchase_default_capacity = GetCapacityOfArticulatedParts(v->engine_type); CargoArray purchase_default_capacity = GetCapacityOfArticulatedParts(v->engine_type);
CargoTypes real_refit_union = 0; CargoTypes real_refit_union{};
CargoTypes real_refit_intersection = ALL_CARGOTYPES; CargoTypes real_refit_intersection = ALL_CARGOTYPES;
CargoTypes real_default_cargoes = 0; CargoTypes real_default_cargoes{};
do { do {
CargoTypes refit_mask = GetAvailableVehicleCargoTypes(v->engine_type, true); CargoTypes refit_mask = GetAvailableVehicleCargoTypes(v->engine_type, true);
real_refit_union |= refit_mask; real_refit_union.Set(refit_mask);
if (refit_mask != 0) real_refit_intersection &= refit_mask; if (refit_mask.Any()) real_refit_intersection = real_refit_intersection & refit_mask;
assert(v->cargo_type < NUM_CARGO); assert(v->cargo_type < NUM_CARGO);
if (v->cargo_cap > 0) SetBit(real_default_cargoes, v->cargo_type); if (v->cargo_cap > 0) real_default_cargoes.Set(v->cargo_type);
v = v->HasArticulatedPart() ? v->GetNextArticulatedPart() : nullptr; v = v->HasArticulatedPart() ? v->GetNextArticulatedPart() : nullptr;
} while (v != nullptr); } while (v != nullptr);
/* Check whether the vehicle carries more cargoes than expected */ /* Check whether the vehicle carries more cargoes than expected */
bool carries_more = false; bool carries_more = false;
for (CargoType cargo_type : SetCargoBitIterator(real_default_cargoes)) { for (CargoType cargo_type : real_default_cargoes) {
if (purchase_default_capacity[cargo_type] == 0) { if (purchase_default_capacity[cargo_type] == 0) {
carries_more = true; carries_more = true;
break; break;

View File

@ -47,7 +47,7 @@ static bool EnginesHaveCargoInCommon(EngineID engine_a, EngineID engine_b)
{ {
CargoTypes available_cargoes_a = GetUnionOfArticulatedRefitMasks(engine_a, true); CargoTypes available_cargoes_a = GetUnionOfArticulatedRefitMasks(engine_a, true);
CargoTypes available_cargoes_b = GetUnionOfArticulatedRefitMasks(engine_b, true); CargoTypes available_cargoes_b = GetUnionOfArticulatedRefitMasks(engine_b, true);
return (available_cargoes_a == 0 || available_cargoes_b == 0 || (available_cargoes_a & available_cargoes_b) != 0); return available_cargoes_a.None() || available_cargoes_b.None() || available_cargoes_a.Any(available_cargoes_b);
} }
/** /**
@ -189,8 +189,8 @@ static bool VerifyAutoreplaceRefitForOrders(const Vehicle *v, EngineID engine_ty
if (!o.IsRefit() || o.IsAutoRefit()) continue; if (!o.IsRefit() || o.IsAutoRefit()) continue;
CargoType cargo_type = o.GetRefitCargo(); CargoType cargo_type = o.GetRefitCargo();
if (!HasBit(union_refit_mask_a, cargo_type)) continue; if (!union_refit_mask_a.Test(cargo_type)) continue;
if (!HasBit(union_refit_mask_b, cargo_type)) return false; if (!union_refit_mask_b.Test(cargo_type)) return false;
} }
return true; return true;
@ -213,7 +213,7 @@ static int GetIncompatibleRefitOrderIdForAutoreplace(const Vehicle *v, EngineID
for (VehicleOrderID i = 0; i < orders->GetNumOrders(); i++) { for (VehicleOrderID i = 0; i < orders->GetNumOrders(); i++) {
const Order *o = orders->GetOrderAt(i); const Order *o = orders->GetOrderAt(i);
if (!o->IsRefit()) continue; if (!o->IsRefit()) continue;
if (!HasBit(union_refit_mask, o->GetRefitCargo())) return i; if (!union_refit_mask.Test(o->GetRefitCargo())) return i;
} }
return -1; return -1;
@ -233,11 +233,11 @@ static CargoType GetNewCargoTypeForReplace(Vehicle *v, EngineID engine_type, boo
CargoTypes available_cargo_types, union_mask; CargoTypes available_cargo_types, union_mask;
GetArticulatedRefitMasks(engine_type, true, &union_mask, &available_cargo_types); GetArticulatedRefitMasks(engine_type, true, &union_mask, &available_cargo_types);
if (union_mask == 0) return CARGO_NO_REFIT; // Don't try to refit an engine with no cargo capacity if (union_mask.None()) return CARGO_NO_REFIT; // Don't try to refit an engine with no cargo capacity
CargoType cargo_type; CargoType cargo_type;
CargoTypes cargo_mask = GetCargoTypesOfArticulatedVehicle(v, &cargo_type); CargoTypes cargo_mask = GetCargoTypesOfArticulatedVehicle(v, &cargo_type);
if (!HasAtMostOneBit(cargo_mask)) { if (!HasAtMostOneBit(cargo_mask.base())) {
CargoTypes new_engine_default_cargoes = GetCargoTypesOfArticulatedParts(engine_type); CargoTypes new_engine_default_cargoes = GetCargoTypesOfArticulatedParts(engine_type);
if ((cargo_mask & new_engine_default_cargoes) == cargo_mask) { if ((cargo_mask & new_engine_default_cargoes) == cargo_mask) {
return CARGO_NO_REFIT; // engine_type is already a mixed cargo type which matches the incoming vehicle by default, no refit required return CARGO_NO_REFIT; // engine_type is already a mixed cargo type which matches the incoming vehicle by default, no refit required
@ -257,12 +257,12 @@ static CargoType GetNewCargoTypeForReplace(Vehicle *v, EngineID engine_type, boo
for (v = v->First(); v != nullptr; v = v->Next()) { for (v = v->First(); v != nullptr; v = v->Next()) {
if (!v->GetEngine()->CanCarryCargo()) continue; if (!v->GetEngine()->CanCarryCargo()) continue;
/* Now we found a cargo type being carried on the train and we will see if it is possible to carry to this one */ /* Now we found a cargo type being carried on the train and we will see if it is possible to carry to this one */
if (HasBit(available_cargo_types, v->cargo_type)) return v->cargo_type; if (available_cargo_types.Test(v->cargo_type)) return v->cargo_type;
} }
return CARGO_NO_REFIT; // We failed to find a cargo type on the old vehicle and we will not refit the new one return CARGO_NO_REFIT; // We failed to find a cargo type on the old vehicle and we will not refit the new one
} else { } else {
if (!HasBit(available_cargo_types, cargo_type)) return INVALID_CARGO; // We can't refit the vehicle to carry the cargo we want if (!available_cargo_types.Test(cargo_type)) return INVALID_CARGO; // We can't refit the vehicle to carry the cargo we want
if (part_of_chain && !VerifyAutoreplaceRefitForOrders(v, engine_type)) return INVALID_CARGO; // Some refit orders lose their effect if (part_of_chain && !VerifyAutoreplaceRefitForOrders(v, engine_type)) return INVALID_CARGO; // Some refit orders lose their effect

View File

@ -546,7 +546,7 @@ static bool CargoAndEngineFilter(const GUIEngineListItem *item, const CargoType
return Engine::Get(item->engine_id)->GetPower() != 0; return Engine::Get(item->engine_id)->GetPower() != 0;
} else { } else {
CargoTypes refit_mask = GetUnionOfArticulatedRefitMasks(item->engine_id, true) & _standard_cargo_mask; CargoTypes refit_mask = GetUnionOfArticulatedRefitMasks(item->engine_id, true) & _standard_cargo_mask;
return (cargo_type == CargoFilterCriteria::CF_NONE ? refit_mask == 0 : HasBit(refit_mask, cargo_type)); return (cargo_type == CargoFilterCriteria::CF_NONE ? refit_mask.None() : refit_mask.Test(cargo_type));
} }
} }
@ -557,7 +557,7 @@ static GUIEngineList::FilterFunction * const _engine_filter_funcs[] = {
static uint GetCargoWeight(const CargoArray &cap, VehicleType vtype) static uint GetCargoWeight(const CargoArray &cap, VehicleType vtype)
{ {
uint weight = 0; uint weight = 0;
for (CargoType cargo = 0; cargo < NUM_CARGO; ++cargo) { for (CargoType cargo{}; cargo < NUM_CARGO; ++cargo) {
if (cap[cargo] != 0) { if (cap[cargo] != 0) {
if (vtype == VEH_TRAIN) { if (vtype == VEH_TRAIN) {
weight += CargoSpec::Get(cargo)->WeightOfNUnitsInTrain(cap[cargo]); weight += CargoSpec::Get(cargo)->WeightOfNUnitsInTrain(cap[cargo]);
@ -1261,10 +1261,10 @@ struct BuildVehicleWindow : Window {
StringID GetCargoFilterLabel(CargoType cargo_type) const StringID GetCargoFilterLabel(CargoType cargo_type) const
{ {
switch (cargo_type) { switch (cargo_type.base()) {
case CargoFilterCriteria::CF_ANY: return STR_PURCHASE_INFO_ALL_TYPES; case CargoFilterCriteria::CF_ANY.base(): return STR_PURCHASE_INFO_ALL_TYPES;
case CargoFilterCriteria::CF_ENGINES: return STR_PURCHASE_INFO_ENGINES_ONLY; case CargoFilterCriteria::CF_ENGINES.base(): return STR_PURCHASE_INFO_ENGINES_ONLY;
case CargoFilterCriteria::CF_NONE: return STR_PURCHASE_INFO_NONE; case CargoFilterCriteria::CF_NONE.base(): return STR_PURCHASE_INFO_NONE;
default: return CargoSpec::Get(cargo_type)->name; default: return CargoSpec::Get(cargo_type)->name;
} }
} }
@ -1274,7 +1274,7 @@ struct BuildVehicleWindow : Window {
{ {
/* Set the last cargo filter criteria. */ /* Set the last cargo filter criteria. */
this->cargo_filter_criteria = _engine_sort_last_cargo_criteria[this->vehicle_type]; this->cargo_filter_criteria = _engine_sort_last_cargo_criteria[this->vehicle_type];
if (this->cargo_filter_criteria < NUM_CARGO && !HasBit(_standard_cargo_mask, this->cargo_filter_criteria)) this->cargo_filter_criteria = CargoFilterCriteria::CF_ANY; if (this->cargo_filter_criteria < NUM_CARGO && !_standard_cargo_mask.Test(this->cargo_filter_criteria)) this->cargo_filter_criteria = CargoFilterCriteria::CF_ANY;
this->eng_list.SetFilterFuncs(_engine_filter_funcs); this->eng_list.SetFilterFuncs(_engine_filter_funcs);
this->eng_list.SetFilterState(this->cargo_filter_criteria != CargoFilterCriteria::CF_ANY); this->eng_list.SetFilterState(this->cargo_filter_criteria != CargoFilterCriteria::CF_ANY);
@ -1570,20 +1570,20 @@ struct BuildVehicleWindow : Window {
DropDownList list; DropDownList list;
/* Add item for disabling filtering. */ /* Add item for disabling filtering. */
list.push_back(MakeDropDownListStringItem(this->GetCargoFilterLabel(CargoFilterCriteria::CF_ANY), CargoFilterCriteria::CF_ANY)); list.push_back(MakeDropDownListStringItem(this->GetCargoFilterLabel(CargoFilterCriteria::CF_ANY), CargoFilterCriteria::CF_ANY.base()));
/* Specific filters for trains. */ /* Specific filters for trains. */
if (this->vehicle_type == VEH_TRAIN) { if (this->vehicle_type == VEH_TRAIN) {
/* Add item for locomotives only in case of trains. */ /* Add item for locomotives only in case of trains. */
list.push_back(MakeDropDownListStringItem(this->GetCargoFilterLabel(CargoFilterCriteria::CF_ENGINES), CargoFilterCriteria::CF_ENGINES)); list.push_back(MakeDropDownListStringItem(this->GetCargoFilterLabel(CargoFilterCriteria::CF_ENGINES), CargoFilterCriteria::CF_ENGINES.base()));
/* Add item for vehicles not carrying anything, e.g. train engines. /* Add item for vehicles not carrying anything, e.g. train engines.
* This could also be useful for eyecandy vehicles of other types, but is likely too confusing for joe, */ * This could also be useful for eyecandy vehicles of other types, but is likely too confusing for joe, */
list.push_back(MakeDropDownListStringItem(this->GetCargoFilterLabel(CargoFilterCriteria::CF_NONE), CargoFilterCriteria::CF_NONE)); list.push_back(MakeDropDownListStringItem(this->GetCargoFilterLabel(CargoFilterCriteria::CF_NONE), CargoFilterCriteria::CF_NONE.base()));
} }
/* Add cargos */ /* Add cargos */
Dimension d = GetLargestCargoIconSize(); Dimension d = GetLargestCargoIconSize();
for (const CargoSpec *cs : _sorted_standard_cargo_specs) { for (const CargoSpec *cs : _sorted_standard_cargo_specs) {
list.push_back(MakeDropDownListIconItem(d, cs->GetCargoIcon(), PAL_NONE, cs->name, cs->Index())); list.push_back(MakeDropDownListIconItem(d, cs->GetCargoIcon(), PAL_NONE, cs->name, cs->Index().base()));
} }
return list; return list;
@ -1675,7 +1675,7 @@ struct BuildVehicleWindow : Window {
break; break;
case WID_BV_CARGO_FILTER_DROPDOWN: // Select cargo filtering criteria dropdown menu case WID_BV_CARGO_FILTER_DROPDOWN: // Select cargo filtering criteria dropdown menu
ShowDropDownList(this, this->BuildCargoDropDownList(), this->cargo_filter_criteria, widget); ShowDropDownList(this, this->BuildCargoDropDownList(), this->cargo_filter_criteria.base(), widget);
break; break;
case WID_BV_CONFIGURE_BADGES: case WID_BV_CONFIGURE_BADGES:
@ -1884,7 +1884,7 @@ struct BuildVehicleWindow : Window {
case WID_BV_CARGO_FILTER_DROPDOWN: // Select a cargo filter criteria case WID_BV_CARGO_FILTER_DROPDOWN: // Select a cargo filter criteria
if (this->cargo_filter_criteria != index) { if (this->cargo_filter_criteria != index) {
this->cargo_filter_criteria = index; this->cargo_filter_criteria = static_cast<CargoType>(index);
_engine_sort_last_cargo_criteria[this->vehicle_type] = this->cargo_filter_criteria; _engine_sort_last_cargo_criteria[this->vehicle_type] = this->cargo_filter_criteria;
/* deactivate filter if criteria is 'Show All', activate it otherwise */ /* deactivate filter if criteria is 'Show All', activate it otherwise */
this->eng_list.SetFilterState(this->cargo_filter_criteria != CargoFilterCriteria::CF_ANY); this->eng_list.SetFilterState(this->cargo_filter_criteria != CargoFilterCriteria::CF_ANY);

View File

@ -12,15 +12,19 @@
#include "core/enum_type.hpp" #include "core/enum_type.hpp"
#include "core/strong_typedef_type.hpp" #include "core/strong_typedef_type.hpp"
#include "core/strong_bitset_type.hpp"
#include "core/convertible_through_base.hpp" #include "core/convertible_through_base.hpp"
/** Globally unique label of a cargo type. */ /** Globally unique label of a cargo type. */
using CargoLabel = StrongType::Typedef<uint32_t, struct CargoLabelTag, StrongType::Compare>; using CargoLabel = StrongType::Typedef<uint32_t, struct CargoLabelTag, StrongType::Compare>;
static constexpr uint NUM_ORIGINAL_CARGO = 12; ///< Original number of cargo types.
static constexpr uint NUM_CARGO = 64; ///< Maximum number of cargo types in a game.
/** /**
* Cargo slots to indicate a cargo type within a game. * Cargo slots to indicate a cargo type within a game.
*/ */
using CargoType = uint8_t; using CargoType = StrongType::Typedef<uint8_t, struct CargoTypeTag, StrongType::Compare, StrongType::Integer>;
/** /**
* Available types of cargo * Available types of cargo
@ -71,14 +75,11 @@ static constexpr CargoLabel CT_NONE = CT_PASSENGERS;
static constexpr CargoLabel CT_INVALID{UINT32_MAX}; ///< Invalid cargo type. static constexpr CargoLabel CT_INVALID{UINT32_MAX}; ///< Invalid cargo type.
static const CargoType NUM_ORIGINAL_CARGO = 12; ///< Original number of cargo types.
static const CargoType NUM_CARGO = 64; ///< Maximum number of cargo types in a game.
/* CARGO_AUTO_REFIT and CARGO_NO_REFIT are stored in save-games for refit-orders, so should not be changed. */ /* CARGO_AUTO_REFIT and CARGO_NO_REFIT are stored in save-games for refit-orders, so should not be changed. */
static const CargoType CARGO_AUTO_REFIT = 0xFD; ///< Automatically choose cargo type when doing auto refitting. static constexpr CargoType CARGO_AUTO_REFIT{0xFD}; ///< Automatically choose cargo type when doing auto refitting.
static const CargoType CARGO_NO_REFIT = 0xFE; ///< Do not refit cargo of a vehicle (used in vehicle orders and auto-replace/auto-renew). static constexpr CargoType CARGO_NO_REFIT{0xFE}; ///< Do not refit cargo of a vehicle (used in vehicle orders and auto-replace/auto-renew).
static const CargoType INVALID_CARGO = UINT8_MAX; static constexpr CargoType INVALID_CARGO{UINT8_MAX};
/** Mixed cargo types for definitions with cargo that can vary depending on climate. */ /** Mixed cargo types for definitions with cargo that can vary depending on climate. */
enum MixedCargoType : uint8_t { enum MixedCargoType : uint8_t {
@ -92,25 +93,25 @@ enum MixedCargoType : uint8_t {
* These are used by user interface code only and must not be assigned to any entity. Not all values are valid for every UI filter. * These are used by user interface code only and must not be assigned to any entity. Not all values are valid for every UI filter.
*/ */
namespace CargoFilterCriteria { namespace CargoFilterCriteria {
static constexpr CargoType CF_ANY = NUM_CARGO; ///< Show all items independent of carried cargo (i.e. no filtering) static constexpr CargoType CF_ANY{NUM_CARGO}; ///< Show all items independent of carried cargo (i.e. no filtering)
static constexpr CargoType CF_NONE = NUM_CARGO + 1; ///< Show only items which do not carry cargo (e.g. train engines) static constexpr CargoType CF_NONE{NUM_CARGO + 1}; ///< Show only items which do not carry cargo (e.g. train engines)
static constexpr CargoType CF_ENGINES = NUM_CARGO + 2; ///< Show only engines (for rail vehicles only) static constexpr CargoType CF_ENGINES{NUM_CARGO + 2}; ///< Show only engines (for rail vehicles only)
static constexpr CargoType CF_FREIGHT = NUM_CARGO + 3; ///< Show only vehicles which carry any freight (non-passenger) cargo static constexpr CargoType CF_FREIGHT{NUM_CARGO + 3}; ///< Show only vehicles which carry any freight (non-passenger) cargo
static constexpr CargoType CF_NO_RATING = NUM_CARGO + 4; ///< Show items with no rating (station list) static constexpr CargoType CF_NO_RATING{NUM_CARGO + 4}; ///< Show items with no rating (station list)
static constexpr CargoType CF_SELECT_ALL = NUM_CARGO + 5; ///< Select all items (station list) static constexpr CargoType CF_SELECT_ALL{NUM_CARGO + 5}; ///< Select all items (station list)
static constexpr CargoType CF_EXPAND_LIST = NUM_CARGO + 6; ///< Expand list to show all items (station list) static constexpr CargoType CF_EXPAND_LIST{NUM_CARGO + 6}; ///< Expand list to show all items (station list)
}; };
/** Test whether cargo type is not INVALID_CARGO */ /** Test whether cargo type is not INVALID_CARGO */
inline bool IsValidCargoType(CargoType cargo) { return cargo != INVALID_CARGO; } inline bool IsValidCargoType(CargoType cargo) { return cargo != INVALID_CARGO; }
typedef uint64_t CargoTypes; using CargoTypes = StrongBitSet<CargoType, uint64_t>;
static const CargoTypes ALL_CARGOTYPES = (CargoTypes)UINT64_MAX; static constexpr CargoTypes ALL_CARGOTYPES{UINT64_MAX};
/** Class for storing amounts of cargo */ /** Class for storing amounts of cargo */
struct CargoArray : std::array<uint, NUM_CARGO> { struct CargoArray : TypedIndexContainer<std::array<uint, NUM_CARGO>, CargoType> {
/** /**
* Get the sum of all cargo amounts. * Get the sum of all cargo amounts.
* @return The sum. * @return The sum.

View File

@ -63,7 +63,7 @@ inline CargoMonitorID EncodeCargoIndustryMonitor(CompanyID company, CargoType ct
uint32_t ret = 0; uint32_t ret = 0;
SB(ret, CCB_TOWN_IND_NUMBER_START, CCB_TOWN_IND_NUMBER_LENGTH, ind.base()); SB(ret, CCB_TOWN_IND_NUMBER_START, CCB_TOWN_IND_NUMBER_LENGTH, ind.base());
SetBit(ret, CCB_IS_INDUSTRY_BIT); SetBit(ret, CCB_IS_INDUSTRY_BIT);
SB(ret, CCB_CARGO_TYPE_START, CCB_CARGO_TYPE_LENGTH, ctype); SB(ret, CCB_CARGO_TYPE_START, CCB_CARGO_TYPE_LENGTH, ctype.base());
SB(ret, CCB_COMPANY_START, CCB_COMPANY_LENGTH, company.base()); SB(ret, CCB_COMPANY_START, CCB_COMPANY_LENGTH, company.base());
return ret; return ret;
} }
@ -82,7 +82,7 @@ inline CargoMonitorID EncodeCargoTownMonitor(CompanyID company, CargoType ctype,
uint32_t ret = 0; uint32_t ret = 0;
SB(ret, CCB_TOWN_IND_NUMBER_START, CCB_TOWN_IND_NUMBER_LENGTH, town.base()); SB(ret, CCB_TOWN_IND_NUMBER_START, CCB_TOWN_IND_NUMBER_LENGTH, town.base());
SB(ret, CCB_CARGO_TYPE_START, CCB_CARGO_TYPE_LENGTH, ctype); SB(ret, CCB_CARGO_TYPE_START, CCB_CARGO_TYPE_LENGTH, ctype.base());
SB(ret, CCB_COMPANY_START, CCB_COMPANY_LENGTH, company.base()); SB(ret, CCB_COMPANY_START, CCB_COMPANY_LENGTH, company.base());
return ret; return ret;
} }
@ -104,7 +104,7 @@ inline CompanyID DecodeMonitorCompany(CargoMonitorID num)
*/ */
inline CargoType DecodeMonitorCargoType(CargoMonitorID num) inline CargoType DecodeMonitorCargoType(CargoMonitorID num)
{ {
return GB(num, CCB_CARGO_TYPE_START, CCB_CARGO_TYPE_LENGTH); return static_cast<CargoType>(GB(num, CCB_CARGO_TYPE_START, CCB_CARGO_TYPE_LENGTH));
} }
/** /**

View File

@ -21,7 +21,7 @@
#include "safeguards.h" #include "safeguards.h"
CargoSpec CargoSpec::array[NUM_CARGO]; TypedIndexContainer<std::array<CargoSpec, NUM_CARGO>, CargoType> CargoSpec::array{};
std::array<std::vector<const CargoSpec *>, NUM_TPE> CargoSpec::town_production_cargoes{}; std::array<std::vector<const CargoSpec *>, NUM_TPE> CargoSpec::town_production_cargoes{};
/** /**
@ -63,7 +63,7 @@ void SetupCargoForClimate(LandscapeType l)
{ {
assert(to_underlying(l) < std::size(_default_climate_cargo)); assert(to_underlying(l) < std::size(_default_climate_cargo));
_cargo_mask = 0; _cargo_mask.Reset();
_default_cargo_labels.clear(); _default_cargo_labels.clear();
_climate_dependent_cargo_labels.fill(CT_INVALID); _climate_dependent_cargo_labels.fill(CT_INVALID);
_climate_independent_cargo_labels.fill(CT_INVALID); _climate_independent_cargo_labels.fill(CT_INVALID);
@ -92,9 +92,9 @@ void SetupCargoForClimate(LandscapeType l)
*insert = std::visit(visitor{}, cl); *insert = std::visit(visitor{}, cl);
if (insert->IsValid()) { if (insert->IsValid()) {
SetBit(_cargo_mask, insert->Index()); _cargo_mask.Set(insert->Index());
_default_cargo_labels.push_back(insert->label); _default_cargo_labels.push_back(insert->label);
_climate_dependent_cargo_labels[insert->Index()] = insert->label; _climate_dependent_cargo_labels[insert->Index().base()] = insert->label;
_climate_independent_cargo_labels[insert->bitnum] = insert->label; _climate_independent_cargo_labels[insert->bitnum] = insert->label;
} }
++insert; ++insert;
@ -186,7 +186,7 @@ SpriteID CargoSpec::GetCargoIcon() const
return sprite; return sprite;
} }
std::array<uint8_t, NUM_CARGO> _sorted_cargo_types; ///< Sort order of cargoes by cargo type. TypedIndexContainer<std::array<uint8_t, NUM_CARGO>, CargoType> _sorted_cargo_types; ///< Sort order of cargoes by cargo type.
std::vector<const CargoSpec *> _sorted_cargo_specs; ///< Cargo specifications sorted alphabetically by name. std::vector<const CargoSpec *> _sorted_cargo_specs; ///< Cargo specifications sorted alphabetically by name.
std::span<const CargoSpec *> _sorted_standard_cargo_specs; ///< Standard cargo specifications sorted alphabetically by name. std::span<const CargoSpec *> _sorted_standard_cargo_specs; ///< Standard cargo specifications sorted alphabetically by name.
@ -238,14 +238,14 @@ void InitializeSortedCargoSpecs()
} }
/* Count the number of standard cargos and fill the mask. */ /* Count the number of standard cargos and fill the mask. */
_standard_cargo_mask = 0; _standard_cargo_mask.Reset();
uint8_t nb_standard_cargo = 0; uint8_t nb_standard_cargo = 0;
for (const auto &cargo : _sorted_cargo_specs) { for (const auto &cargo : _sorted_cargo_specs) {
assert(cargo->town_production_effect != INVALID_TPE); assert(cargo->town_production_effect != INVALID_TPE);
CargoSpec::town_production_cargoes[cargo->town_production_effect].push_back(cargo); CargoSpec::town_production_cargoes[cargo->town_production_effect].push_back(cargo);
if (cargo->classes.Test(CargoClass::Special)) break; if (cargo->classes.Test(CargoClass::Special)) break;
nb_standard_cargo++; nb_standard_cargo++;
SetBit(_standard_cargo_mask, cargo->Index()); _standard_cargo_mask.Set(cargo->Index());
} }
/* _sorted_standard_cargo_specs is a subset of _sorted_cargo_specs. */ /* _sorted_standard_cargo_specs is a subset of _sorted_cargo_specs. */

View File

@ -107,7 +107,7 @@ struct CargoSpec {
*/ */
inline CargoType Index() const inline CargoType Index() const
{ {
return this - CargoSpec::array; return static_cast<CargoType>(this - CargoSpec::array.data());
} }
/** /**
@ -126,7 +126,7 @@ struct CargoSpec {
*/ */
static inline size_t GetArraySize() static inline size_t GetArraySize()
{ {
return lengthof(CargoSpec::array); return std::size(CargoSpec::array);
} }
/** /**
@ -134,9 +134,9 @@ struct CargoSpec {
* @param index ID of cargo * @param index ID of cargo
* @pre index is a valid cargo type * @pre index is a valid cargo type
*/ */
static inline CargoSpec *Get(size_t index) static inline CargoSpec *Get(CargoType index)
{ {
assert(index < lengthof(CargoSpec::array)); assert(index.base() < std::size(CargoSpec::array));
return &CargoSpec::array[index]; return &CargoSpec::array[index];
} }
@ -165,12 +165,12 @@ struct CargoSpec {
}; };
bool operator==(const Iterator &other) const { return this->index == other.index; } bool operator==(const Iterator &other) const { return this->index == other.index; }
CargoSpec * operator*() const { return CargoSpec::Get(this->index); } CargoSpec * operator*() const { return CargoSpec::Get(CargoType{static_cast<CargoType::BaseType>(this->index)}); }
Iterator & operator++() { this->index++; this->ValidateIndex(); return *this; } Iterator & operator++() { this->index++; this->ValidateIndex(); return *this; }
private: private:
size_t index; size_t index;
void ValidateIndex() { while (this->index < CargoSpec::GetArraySize() && !(CargoSpec::Get(this->index)->IsValid())) this->index++; } void ValidateIndex() { while (this->index < CargoSpec::GetArraySize() && !(**this)->IsValid()) this->index++; }
}; };
/* /*
@ -195,7 +195,7 @@ struct CargoSpec {
static std::array<std::vector<const CargoSpec *>, NUM_TPE> town_production_cargoes; static std::array<std::vector<const CargoSpec *>, NUM_TPE> town_production_cargoes;
private: private:
static CargoSpec array[NUM_CARGO]; ///< Array holding all CargoSpecs static TypedIndexContainer<std::array<CargoSpec, NUM_CARGO>, CargoType> array; ///< Array holding all CargoSpecs
static inline std::map<CargoLabel, CargoType> label_map{}; ///< Translation map from CargoLabel to Cargo type. static inline std::map<CargoLabel, CargoType> label_map{}; ///< Translation map from CargoLabel to Cargo type.
friend void SetupCargoForClimate(LandscapeType l); friend void SetupCargoForClimate(LandscapeType l);
@ -223,7 +223,7 @@ inline CargoType GetCargoTypeByLabel(CargoLabel label)
Dimension GetLargestCargoIconSize(); Dimension GetLargestCargoIconSize();
void InitializeSortedCargoSpecs(); void InitializeSortedCargoSpecs();
extern std::array<uint8_t, NUM_CARGO> _sorted_cargo_types; extern TypedIndexContainer<std::array<uint8_t, NUM_CARGO>, CargoType> _sorted_cargo_types;
extern std::vector<const CargoSpec *> _sorted_cargo_specs; extern std::vector<const CargoSpec *> _sorted_cargo_specs;
extern std::span<const CargoSpec *> _sorted_standard_cargo_specs; extern std::span<const CargoSpec *> _sorted_standard_cargo_specs;
@ -238,8 +238,6 @@ inline bool IsCargoInClass(CargoType cargo, CargoClasses cc)
return CargoSpec::Get(cargo)->classes.Any(cc); return CargoSpec::Get(cargo)->classes.Any(cc);
} }
using SetCargoBitIterator = SetBitIterator<CargoType, CargoTypes>;
/** Comparator to sort CargoType by according to desired order. */ /** Comparator to sort CargoType by according to desired order. */
struct CargoTypeComparator { struct CargoTypeComparator {
bool operator() (const CargoType &lhs, const CargoType &rhs) const { return _sorted_cargo_types[lhs] < _sorted_cargo_types[rhs]; } bool operator() (const CargoType &lhs, const CargoType &rhs) const { return _sorted_cargo_types[lhs] < _sorted_cargo_types[rhs]; }

View File

@ -29,6 +29,7 @@ add_files(
string_consumer.hpp string_consumer.hpp
string_inplace.cpp string_inplace.cpp
string_inplace.hpp string_inplace.hpp
strong_bitset_type.hpp
strong_typedef_type.hpp strong_typedef_type.hpp
utf8.cpp utf8.cpp
utf8.hpp utf8.hpp

View File

@ -108,6 +108,16 @@ public:
return static_cast<Timpl&>(*this); return static_cast<Timpl&>(*this);
} }
/**
* Flip all bits.
* @returns The bit set
*/
inline constexpr Timpl &Flip()
{
this->data ^= Tmask;
return static_cast<Timpl&>(*this);
}
/** /**
* Flip the value-th bit. * Flip the value-th bit.
* @param value Bit to flip. * @param value Bit to flip.

View File

@ -0,0 +1,42 @@
/*
* This file is part of OpenTTD.
* OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
* OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
*/
/** @file strong_bitset_type.hpp Type helper for making a BitSet out of a Strong Typedef. */
#ifndef STRONG_BITSET_TYPE_HPP
#define STRONG_BITSET_TYPE_HPP
#include "base_bitset_type.hpp"
/**
* Strong bit set.
* @tparam Tvalue_type Type of values to wrap.
* @tparam Tstorage Storage type required to hold values.
*/
template <typename Tvalue_type, typename Tstorage, Tstorage Tmask = std::numeric_limits<Tstorage>::max()>
class StrongBitSet : public BaseBitSet<StrongBitSet<Tvalue_type, Tstorage, Tmask>, Tvalue_type, Tstorage> {
public:
constexpr StrongBitSet() : BaseClass() {}
constexpr StrongBitSet(Tvalue_type value) : BaseClass() { this->Set(value); }
explicit constexpr StrongBitSet(Tstorage data) : BaseClass(data) {}
constexpr StrongBitSet(std::initializer_list<const Tvalue_type> values) : BaseClass()
{
for (const Tvalue_type &value : values) {
this->Set(value);
}
}
static constexpr size_t DecayValueType(Tvalue_type value) { return value.base(); }
constexpr auto operator <=>(const StrongBitSet &) const noexcept = default;
private:
using BaseClass = BaseBitSet<StrongBitSet<Tvalue_type, Tstorage, Tmask>, Tvalue_type, Tstorage, Tmask>;
};
#endif /* STRONG_BITSET_TYPE_HPP */

View File

@ -1101,7 +1101,7 @@ static Money DeliverGoods(int num_pieces, CargoType cargo_type, StationID dest,
uint accepted_ind = DeliverGoodsToIndustry(st, cargo_type, num_pieces, src.type == SourceType::Industry ? src.ToIndustryID() : IndustryID::Invalid(), company->index); uint accepted_ind = DeliverGoodsToIndustry(st, cargo_type, num_pieces, src.type == SourceType::Industry ? src.ToIndustryID() : IndustryID::Invalid(), company->index);
/* If this cargo type is always accepted, accept all */ /* If this cargo type is always accepted, accept all */
uint accepted_total = HasBit(st->always_accepted, cargo_type) ? num_pieces : accepted_ind; uint accepted_total = st->always_accepted.Test(cargo_type) ? num_pieces : accepted_ind;
/* Update station statistics */ /* Update station statistics */
if (accepted_total > 0) { if (accepted_total > 0) {
@ -1394,7 +1394,7 @@ struct PrepareRefitAction
bool operator()(const Vehicle *v) bool operator()(const Vehicle *v)
{ {
this->consist_capleft[v->cargo_type] -= v->cargo_cap - v->cargo.ReservedCount(); this->consist_capleft[v->cargo_type] -= v->cargo_cap - v->cargo.ReservedCount();
this->refit_mask |= EngInfo(v->engine_type)->refit_mask; this->refit_mask.Set(EngInfo(v->engine_type)->refit_mask);
return true; return true;
} }
}; };
@ -1487,7 +1487,7 @@ static void HandleStationRefit(Vehicle *v, CargoArray &consist_capleft, Station
if (is_auto_refit) { if (is_auto_refit) {
/* Get a refittable cargo type with waiting cargo for next_station or StationID::Invalid(). */ /* Get a refittable cargo type with waiting cargo for next_station or StationID::Invalid(). */
new_cargo_type = v_start->cargo_type; new_cargo_type = v_start->cargo_type;
for (CargoType cargo_type : SetCargoBitIterator(refit_mask)) { for (CargoType cargo_type : refit_mask) {
if (st->goods[cargo_type].HasData() && st->goods[cargo_type].GetData().cargo.HasCargoFor(next_station)) { if (st->goods[cargo_type].HasData() && st->goods[cargo_type].GetData().cargo.HasCargoFor(next_station)) {
/* Try to find out if auto-refitting would succeed. In case the refit is allowed, /* Try to find out if auto-refitting would succeed. In case the refit is allowed,
* the returned refit capacity will be greater than zero. */ * the returned refit capacity will be greater than zero. */
@ -1646,10 +1646,10 @@ static void LoadUnloadVehicle(Vehicle *front)
bool completely_emptied = true; bool completely_emptied = true;
bool anything_unloaded = false; bool anything_unloaded = false;
bool anything_loaded = false; bool anything_loaded = false;
CargoTypes full_load_amount = 0; CargoTypes full_load_amount{};
CargoTypes cargo_not_full = 0; CargoTypes cargo_not_full{};
CargoTypes cargo_full = 0; CargoTypes cargo_full{};
CargoTypes reservation_left = 0; CargoTypes reservation_left{};
front->cur_speed = 0; front->cur_speed = 0;
@ -1780,14 +1780,14 @@ static void LoadUnloadVehicle(Vehicle *front)
if (v->cargo.ActionCount(VehicleCargoList::MTA_LOAD) > 0) { if (v->cargo.ActionCount(VehicleCargoList::MTA_LOAD) > 0) {
/* Remember if there are reservations left so that we don't stop /* Remember if there are reservations left so that we don't stop
* loading before they're loaded. */ * loading before they're loaded. */
SetBit(reservation_left, v->cargo_type); reservation_left.Set(v->cargo_type);
} }
/* Store whether the maximum possible load amount was loaded or not.*/ /* Store whether the maximum possible load amount was loaded or not.*/
if (loaded == cap_left) { if (loaded == cap_left) {
SetBit(full_load_amount, v->cargo_type); full_load_amount.Set(v->cargo_type);
} else { } else {
ClrBit(full_load_amount, v->cargo_type); full_load_amount.Reset(v->cargo_type);
} }
/* TODO: Regarding this, when we do gradual loading, we /* TODO: Regarding this, when we do gradual loading, we
@ -1820,9 +1820,9 @@ static void LoadUnloadVehicle(Vehicle *front)
} }
if (v->cargo.StoredCount() >= v->cargo_cap) { if (v->cargo.StoredCount() >= v->cargo_cap) {
SetBit(cargo_full, v->cargo_type); cargo_full.Set(v->cargo_type);
} else { } else {
SetBit(cargo_not_full, v->cargo_type); cargo_not_full.Set(v->cargo_type);
} }
} }
@ -1852,7 +1852,7 @@ static void LoadUnloadVehicle(Vehicle *front)
} }
/* We loaded less cargo than possible for all cargo types and it's not full /* We loaded less cargo than possible for all cargo types and it's not full
* load and we're not supposed to wait any longer: stop loading. */ * load and we're not supposed to wait any longer: stop loading. */
if (!anything_unloaded && full_load_amount == 0 && reservation_left == 0 && !(front->current_order.GetLoadType() & OLFB_FULL_LOAD) && if (!anything_unloaded && full_load_amount.None() && reservation_left.None() && !(front->current_order.GetLoadType() & OLFB_FULL_LOAD) &&
front->current_order_time >= std::max(front->current_order.GetTimetabledWait() - front->lateness_counter, 0)) { front->current_order_time >= std::max(front->current_order.GetTimetabledWait() - front->lateness_counter, 0)) {
front->vehicle_flags.Set(VehicleFlag::StopLoading); front->vehicle_flags.Set(VehicleFlag::StopLoading);
} }
@ -1866,10 +1866,10 @@ static void LoadUnloadVehicle(Vehicle *front)
/* if the aircraft carries passengers and is NOT full, then /* if the aircraft carries passengers and is NOT full, then
* continue loading, no matter how much mail is in */ * continue loading, no matter how much mail is in */
if ((front->type == VEH_AIRCRAFT && IsCargoInClass(front->cargo_type, CargoClass::Passengers) && front->cargo_cap > front->cargo.StoredCount()) || if ((front->type == VEH_AIRCRAFT && IsCargoInClass(front->cargo_type, CargoClass::Passengers) && front->cargo_cap > front->cargo.StoredCount()) ||
(cargo_not_full != 0 && (cargo_full & ~cargo_not_full) == 0)) { // There are still non-full cargoes (cargo_not_full.Any() && cargo_full.Reset(cargo_not_full).None())) { // There are still non-full cargoes
finished_loading = false; finished_loading = false;
} }
} else if (cargo_not_full != 0) { } else if (cargo_not_full.Any()) {
finished_loading = false; finished_loading = false;
} }

View File

@ -927,7 +927,7 @@ static CompanyID GetPreviewCompany(Engine *e)
/* Check whether the company uses similar vehicles */ /* Check whether the company uses similar vehicles */
for (const Vehicle *v : Vehicle::Iterate()) { for (const Vehicle *v : Vehicle::Iterate()) {
if (v->owner != c->index || v->type != e->type) continue; if (v->owner != c->index || v->type != e->type) continue;
if (!v->GetEngine()->CanCarryCargo() || !HasBit(cargomask, v->cargo_type)) continue; if (!v->GetEngine()->CanCarryCargo() || !cargomask.Test(v->cargo_type)) continue;
best_hist = c->old_economy[0].performance_history; best_hist = c->old_economy[0].performance_history;
best_company = c->index; best_company = c->index;
@ -1294,7 +1294,7 @@ bool IsEngineRefittable(EngineID engine)
if (!e->CanCarryCargo()) return false; if (!e->CanCarryCargo()) return false;
const EngineInfo *ei = &e->info; const EngineInfo *ei = &e->info;
if (ei->refit_mask == 0) return false; if (ei->refit_mask.None()) return false;
/* Are there suffixes? /* Are there suffixes?
* Note: This does not mean the suffixes are actually available for every consist at any time. */ * Note: This does not mean the suffixes are actually available for every consist at any time. */
@ -1302,9 +1302,7 @@ bool IsEngineRefittable(EngineID engine)
/* Is there any cargo except the default cargo? */ /* Is there any cargo except the default cargo? */
CargoType default_cargo = e->GetDefaultCargoType(); CargoType default_cargo = e->GetDefaultCargoType();
CargoTypes default_cargo_mask = 0; return IsValidCargoType(default_cargo) && ei->refit_mask != default_cargo;
SetBit(default_cargo_mask, default_cargo);
return IsValidCargoType(default_cargo) && ei->refit_mask != default_cargo_mask;
} }
/** /**

View File

@ -1132,7 +1132,7 @@ struct BaseCargoGraphWindow : BaseGraphWindow {
this->cargo_types = this->GetCargoTypes(number); this->cargo_types = this->GetCargoTypes(number);
this->vscroll = this->GetScrollbar(WID_GRAPH_MATRIX_SCROLLBAR); this->vscroll = this->GetScrollbar(WID_GRAPH_MATRIX_SCROLLBAR);
this->vscroll->SetCount(CountBits(this->cargo_types)); this->vscroll->SetCount(this->cargo_types.Count());
auto *wid = this->GetWidget<NWidgetCore>(WID_GRAPH_FOOTER); auto *wid = this->GetWidget<NWidgetCore>(WID_GRAPH_FOOTER);
wid->SetString(TimerGameEconomy::UsingWallclockUnits() ? footer_wallclock : footer_calendar); wid->SetString(TimerGameEconomy::UsingWallclockUnits() ? footer_wallclock : footer_calendar);
@ -1152,10 +1152,10 @@ struct BaseCargoGraphWindow : BaseGraphWindow {
if (row >= this->vscroll->GetCount()) return std::nullopt; if (row >= this->vscroll->GetCount()) return std::nullopt;
for (const CargoSpec *cs : _sorted_cargo_specs) { for (const CargoSpec *cs : _sorted_cargo_specs) {
if (!HasBit(this->cargo_types, cs->Index())) continue; if (!this->cargo_types.Test(cs->Index())) continue;
if (row-- > 0) continue; if (row-- > 0) continue;
return cs->Index(); return cs->Index().base();
} }
return std::nullopt; return std::nullopt;
@ -1176,7 +1176,7 @@ struct BaseCargoGraphWindow : BaseGraphWindow {
size.height = GetCharacterHeight(FS_SMALL) + WidgetDimensions::scaled.framerect.Vertical(); size.height = GetCharacterHeight(FS_SMALL) + WidgetDimensions::scaled.framerect.Vertical();
for (CargoType cargo_type : SetCargoBitIterator(this->cargo_types)) { for (CargoType cargo_type : this->cargo_types) {
const CargoSpec *cs = CargoSpec::Get(cargo_type); const CargoSpec *cs = CargoSpec::Get(cargo_type);
Dimension d = GetStringBoundingBox(GetString(STR_GRAPH_CARGO_PAYMENT_CARGO, cs->name)); Dimension d = GetStringBoundingBox(GetString(STR_GRAPH_CARGO_PAYMENT_CARGO, cs->name));
@ -1207,12 +1207,12 @@ struct BaseCargoGraphWindow : BaseGraphWindow {
Rect line = r.WithHeight(this->line_height); Rect line = r.WithHeight(this->line_height);
for (const CargoSpec *cs : _sorted_cargo_specs) { for (const CargoSpec *cs : _sorted_cargo_specs) {
if (!HasBit(this->cargo_types, cs->Index())) continue; if (!this->cargo_types.Test(cs->Index())) continue;
if (pos-- > 0) continue; if (pos-- > 0) continue;
if (--max < 0) break; if (--max < 0) break;
bool lowered = !HasBit(this->excluded_data, cs->Index()); bool lowered = !HasBit(this->excluded_data, cs->Index().base());
/* Redraw frame if lowered */ /* Redraw frame if lowered */
if (lowered) DrawFrameRect(line, COLOUR_BROWN, FrameFlag::Lowered); if (lowered) DrawFrameRect(line, COLOUR_BROWN, FrameFlag::Lowered);
@ -1239,14 +1239,14 @@ struct BaseCargoGraphWindow : BaseGraphWindow {
case WID_GRAPH_ENABLE_CARGOES: case WID_GRAPH_ENABLE_CARGOES:
/* Remove all cargoes from the excluded lists. */ /* Remove all cargoes from the excluded lists. */
this->GetExcludedCargoTypes() = {}; this->GetExcludedCargoTypes() = {};
this->excluded_data = this->GetExcludedCargoTypes(); this->excluded_data = this->GetExcludedCargoTypes().base();
this->SetDirty(); this->SetDirty();
break; break;
case WID_GRAPH_DISABLE_CARGOES: { case WID_GRAPH_DISABLE_CARGOES: {
/* Add all cargoes to the excluded lists. */ /* Add all cargoes to the excluded lists. */
this->GetExcludedCargoTypes() = this->cargo_types; this->GetExcludedCargoTypes() = this->cargo_types;
this->excluded_data = this->GetExcludedCargoTypes(); this->excluded_data = this->GetExcludedCargoTypes().base();
this->SetDirty(); this->SetDirty();
break; break;
} }
@ -1256,11 +1256,11 @@ struct BaseCargoGraphWindow : BaseGraphWindow {
if (row >= this->vscroll->GetCount()) return; if (row >= this->vscroll->GetCount()) return;
for (const CargoSpec *cs : _sorted_cargo_specs) { for (const CargoSpec *cs : _sorted_cargo_specs) {
if (!HasBit(this->cargo_types, cs->Index())) continue; if (!this->cargo_types.Test(cs->Index())) continue;
if (row-- > 0) continue; if (row-- > 0) continue;
ToggleBit(this->GetExcludedCargoTypes(), cs->Index()); this->GetExcludedCargoTypes().Flip(cs->Index());
this->excluded_data = this->GetExcludedCargoTypes(); this->excluded_data = this->GetExcludedCargoTypes().base();
this->SetDirty(); this->SetDirty();
break; break;
} }
@ -1332,13 +1332,13 @@ struct PaymentRatesGraphWindow : BaseCargoGraphWindow {
*/ */
void UpdatePaymentRates() void UpdatePaymentRates()
{ {
this->excluded_data = this->GetExcludedCargoTypes(); this->excluded_data = this->GetExcludedCargoTypes().base();
this->data.clear(); this->data.clear();
for (const CargoSpec *cs : _sorted_standard_cargo_specs) { for (const CargoSpec *cs : _sorted_standard_cargo_specs) {
DataSet &dataset = this->data.emplace_back(); DataSet &dataset = this->data.emplace_back();
dataset.colour = cs->legend_colour; dataset.colour = cs->legend_colour;
dataset.exclude_bit = cs->Index(); dataset.exclude_bit = cs->Index().base();
for (uint j = 0; j != this->num_on_x_axis; j++) { for (uint j = 0; j != this->num_on_x_axis; j++) {
dataset.values[j] = GetTransportedGoodsIncome(10, 20, j * 4 + 4, cs->Index()); dataset.values[j] = GetTransportedGoodsIncome(10, 20, j * 4 + 4, cs->Index());
@ -1654,7 +1654,7 @@ struct IndustryProductionGraphWindow : BaseCargoGraphWindow {
if (IsValidCargoType(a.cargo)) SetBit(cargo_types, a.cargo); if (IsValidCargoType(a.cargo)) SetBit(cargo_types, a.cargo);
} }
for (const auto &p : i->produced) { for (const auto &p : i->produced) {
if (IsValidCargoType(p.cargo)) SetBit(cargo_types, p.cargo); if (IsValidCargoType(p.cargo)) cargo_types.Set(p.cargo);
} }
return cargo_types; return cargo_types;
} }
@ -1680,12 +1680,12 @@ struct IndustryProductionGraphWindow : BaseCargoGraphWindow {
mo += 12; mo += 12;
} }
if (!initialize && this->excluded_data == this->GetExcludedCargoTypes() && this->num_on_x_axis == this->num_vert_lines && this->year == yr && this->month == mo) { if (!initialize && this->excluded_data == this->GetExcludedCargoTypes().base() && this->num_on_x_axis == this->num_vert_lines && this->year == yr && this->month == mo) {
/* There's no reason to get new stats */ /* There's no reason to get new stats */
return; return;
} }
this->excluded_data = this->GetExcludedCargoTypes(); this->excluded_data = this->GetExcludedCargoTypes().base();
this->year = yr; this->year = yr;
this->month = mo; this->month = mo;
@ -1700,13 +1700,13 @@ struct IndustryProductionGraphWindow : BaseCargoGraphWindow {
DataSet &produced = this->data.emplace_back(); DataSet &produced = this->data.emplace_back();
produced.colour = cs->legend_colour; produced.colour = cs->legend_colour;
produced.exclude_bit = cs->Index(); produced.exclude_bit = cs->Index().base();
produced.range_bit = 0; produced.range_bit = 0;
auto produced_filler = Filler{produced, &Industry::ProducedHistory::production}; auto produced_filler = Filler{produced, &Industry::ProducedHistory::production};
DataSet &transported = this->data.emplace_back(); DataSet &transported = this->data.emplace_back();
transported.colour = cs->legend_colour; transported.colour = cs->legend_colour;
transported.exclude_bit = cs->Index(); transported.exclude_bit = cs->Index().base();
transported.range_bit = 1; transported.range_bit = 1;
transported.dash = 2; transported.dash = 2;
auto transported_filler = Filler{transported, &Industry::ProducedHistory::transported}; auto transported_filler = Filler{transported, &Industry::ProducedHistory::transported};

View File

@ -708,7 +708,7 @@ public:
return; return;
case WID_GL_FILTER_BY_CARGO: // Select filtering criteria dropdown menu case WID_GL_FILTER_BY_CARGO: // Select filtering criteria dropdown menu
ShowDropDownList(this, this->BuildCargoDropDownList(false), this->cargo_filter_criteria, widget); ShowDropDownList(this, this->BuildCargoDropDownList(false), this->cargo_filter_criteria.base(), widget);
break; break;
case WID_GL_ALL_VEHICLES: // All vehicles button case WID_GL_ALL_VEHICLES: // All vehicles button
@ -999,7 +999,7 @@ public:
break; break;
case WID_GL_FILTER_BY_CARGO: // Select a cargo filter criteria case WID_GL_FILTER_BY_CARGO: // Select a cargo filter criteria
this->SetCargoFilter(index); this->SetCargoFilter(static_cast<CargoType>(index));
break; break;
case WID_GL_MANAGE_VEHICLES_DROPDOWN: case WID_GL_MANAGE_VEHICLES_DROPDOWN:

View File

@ -71,7 +71,7 @@ struct Industry : IndustryPool::PoolItem<&_industry_pool> {
} }
}; };
struct ProducedCargo { struct ProducedCargo {
CargoType cargo = 0; ///< Cargo type CargoType cargo = INVALID_CARGO; ///< Cargo type
uint16_t waiting = 0; ///< Amount of cargo produced uint16_t waiting = 0; ///< Amount of cargo produced
uint8_t rate = 0; ///< Production rate uint8_t rate = 0; ///< Production rate
HistoryData<ProducedHistory> history{}; ///< History of cargo produced and transported for this month and 24 previous months HistoryData<ProducedHistory> history{}; ///< History of cargo produced and transported for this month and 24 previous months
@ -83,7 +83,7 @@ struct Industry : IndustryPool::PoolItem<&_industry_pool> {
}; };
struct AcceptedCargo { struct AcceptedCargo {
CargoType cargo = 0; ///< Cargo type CargoType cargo = INVALID_CARGO; ///< Cargo type
uint16_t waiting = 0; ///< Amount of cargo waiting to processed uint16_t waiting = 0; ///< Amount of cargo waiting to processed
uint32_t accumulated_waiting = 0; ///< Accumulated waiting total over the last month, used to calculate average. uint32_t accumulated_waiting = 0; ///< Accumulated waiting total over the last month, used to calculate average.
TimerGameEconomy::Date last_accepted{}; ///< Last day cargo was accepted by this industry TimerGameEconomy::Date last_accepted{}; ///< Last day cargo was accepted by this industry

View File

@ -458,13 +458,13 @@ static void AddAcceptedCargo_Industry(TileIndex tile, CargoArray &acceptance, Ca
acceptance[cargo] += cargo_acceptance[i]; acceptance[cargo] += cargo_acceptance[i];
/* Maybe set 'always accepted' bit (if it's not set already) */ /* Maybe set 'always accepted' bit (if it's not set already) */
if (HasBit(always_accepted, cargo)) continue; if (always_accepted.Test(cargo)) continue;
/* Test whether the industry itself accepts the cargo type */ /* Test whether the industry itself accepts the cargo type */
if (ind->IsCargoAccepted(cargo)) continue; if (ind->IsCargoAccepted(cargo)) continue;
/* If the industry itself doesn't accept this cargo, set 'always accepted' bit */ /* If the industry itself doesn't accept this cargo, set 'always accepted' bit */
SetBit(always_accepted, cargo); always_accepted.Set(cargo);
} }
} }

View File

@ -1289,12 +1289,12 @@ static bool CargoFilter(const Industry * const *industry, const std::pair<CargoT
bool accepted_cargo_matches; bool accepted_cargo_matches;
switch (accepted_cargo) { switch (accepted_cargo.base()) {
case CargoFilterCriteria::CF_ANY: case CargoFilterCriteria::CF_ANY.base():
accepted_cargo_matches = true; accepted_cargo_matches = true;
break; break;
case CargoFilterCriteria::CF_NONE: case CargoFilterCriteria::CF_NONE.base():
accepted_cargo_matches = !(*industry)->IsCargoAccepted(); accepted_cargo_matches = !(*industry)->IsCargoAccepted();
break; break;
@ -1305,12 +1305,12 @@ static bool CargoFilter(const Industry * const *industry, const std::pair<CargoT
bool produced_cargo_matches; bool produced_cargo_matches;
switch (produced_cargo) { switch (produced_cargo.base()) {
case CargoFilterCriteria::CF_ANY: case CargoFilterCriteria::CF_ANY.base():
produced_cargo_matches = true; produced_cargo_matches = true;
break; break;
case CargoFilterCriteria::CF_NONE: case CargoFilterCriteria::CF_NONE.base():
produced_cargo_matches = !(*industry)->IsCargoProduced(); produced_cargo_matches = !(*industry)->IsCargoProduced();
break; break;
@ -1396,9 +1396,9 @@ protected:
StringID GetCargoFilterLabel(CargoType cargo_type) const StringID GetCargoFilterLabel(CargoType cargo_type) const
{ {
switch (cargo_type) { switch (cargo_type.base()) {
case CargoFilterCriteria::CF_ANY: return STR_INDUSTRY_DIRECTORY_FILTER_ALL_TYPES; case CargoFilterCriteria::CF_ANY.base(): return STR_INDUSTRY_DIRECTORY_FILTER_ALL_TYPES;
case CargoFilterCriteria::CF_NONE: return STR_INDUSTRY_DIRECTORY_FILTER_NONE; case CargoFilterCriteria::CF_NONE.base(): return STR_INDUSTRY_DIRECTORY_FILTER_NONE;
default: return CargoSpec::Get(cargo_type)->name; default: return CargoSpec::Get(cargo_type)->name;
} }
} }
@ -1776,14 +1776,14 @@ public:
DropDownList list; DropDownList list;
/* Add item for disabling filtering. */ /* Add item for disabling filtering. */
list.push_back(MakeDropDownListStringItem(this->GetCargoFilterLabel(CargoFilterCriteria::CF_ANY), CargoFilterCriteria::CF_ANY)); list.push_back(MakeDropDownListStringItem(this->GetCargoFilterLabel(CargoFilterCriteria::CF_ANY), CargoFilterCriteria::CF_ANY.base()));
/* Add item for industries not producing anything, e.g. power plants */ /* Add item for industries not producing anything, e.g. power plants */
list.push_back(MakeDropDownListStringItem(this->GetCargoFilterLabel(CargoFilterCriteria::CF_NONE), CargoFilterCriteria::CF_NONE)); list.push_back(MakeDropDownListStringItem(this->GetCargoFilterLabel(CargoFilterCriteria::CF_NONE), CargoFilterCriteria::CF_NONE.base()));
/* Add cargos */ /* Add cargos */
Dimension d = GetLargestCargoIconSize(); Dimension d = GetLargestCargoIconSize();
for (const CargoSpec *cs : _sorted_standard_cargo_specs) { for (const CargoSpec *cs : _sorted_standard_cargo_specs) {
list.push_back(MakeDropDownListIconItem(d, cs->GetCargoIcon(), PAL_NONE, cs->name, cs->Index())); list.push_back(MakeDropDownListIconItem(d, cs->GetCargoIcon(), PAL_NONE, cs->name, cs->Index().base()));
} }
return list; return list;
@ -1802,11 +1802,11 @@ public:
break; break;
case WID_ID_FILTER_BY_ACC_CARGO: // Cargo filter dropdown case WID_ID_FILTER_BY_ACC_CARGO: // Cargo filter dropdown
ShowDropDownList(this, this->BuildCargoDropDownList(), this->accepted_cargo_filter_criteria, widget); ShowDropDownList(this, this->BuildCargoDropDownList(), this->accepted_cargo_filter_criteria.base(), widget);
break; break;
case WID_ID_FILTER_BY_PROD_CARGO: // Cargo filter dropdown case WID_ID_FILTER_BY_PROD_CARGO: // Cargo filter dropdown
ShowDropDownList(this, this->BuildCargoDropDownList(), this->produced_cargo_filter_criteria, widget); ShowDropDownList(this, this->BuildCargoDropDownList(), this->produced_cargo_filter_criteria.base(), widget);
break; break;
case WID_ID_INDUSTRY_LIST: { case WID_ID_INDUSTRY_LIST: {
@ -1835,13 +1835,13 @@ public:
} }
case WID_ID_FILTER_BY_ACC_CARGO: { case WID_ID_FILTER_BY_ACC_CARGO: {
this->SetAcceptedCargoFilter(index); this->SetAcceptedCargoFilter(static_cast<CargoType>(index));
this->BuildSortIndustriesList(); this->BuildSortIndustriesList();
break; break;
} }
case WID_ID_FILTER_BY_PROD_CARGO: { case WID_ID_FILTER_BY_PROD_CARGO: {
this->SetProducedCargoFilter(index); this->SetProducedCargoFilter(static_cast<CargoType>(index));
this->BuildSortIndustriesList(); this->BuildSortIndustriesList();
break; break;
} }
@ -2648,7 +2648,7 @@ struct IndustryCargoesWindow : public Window {
const IndustrySpec *indsp = GetIndustrySpec(this->ind_cargo); const IndustrySpec *indsp = GetIndustrySpec(this->ind_cargo);
return GetString(STR_INDUSTRY_CARGOES_INDUSTRY_CAPTION, indsp->name); return GetString(STR_INDUSTRY_CARGOES_INDUSTRY_CAPTION, indsp->name);
} else { } else {
const CargoSpec *csp = CargoSpec::Get(this->ind_cargo - NUM_INDUSTRYTYPES); const CargoSpec *csp = CargoSpec::Get(static_cast<CargoType>(this->ind_cargo - NUM_INDUSTRYTYPES));
return GetString(STR_INDUSTRY_CARGOES_CARGO_CAPTION, csp->name); return GetString(STR_INDUSTRY_CARGOES_CARGO_CAPTION, csp->name);
} }
} }
@ -2661,9 +2661,9 @@ struct IndustryCargoesWindow : public Window {
*/ */
static bool HasCommonValidCargo(const std::span<const CargoType> cargoes1, const std::span<const CargoType> cargoes2) static bool HasCommonValidCargo(const std::span<const CargoType> cargoes1, const std::span<const CargoType> cargoes2)
{ {
for (const CargoType cargo_type1 : cargoes1) { for (const CargoType &cargo_type1 : cargoes1) {
if (!IsValidCargoType(cargo_type1)) continue; if (!IsValidCargoType(cargo_type1)) continue;
for (const CargoType cargo_type2 : cargoes2) { for (const CargoType &cargo_type2 : cargoes2) {
if (cargo_type1 == cargo_type2) return true; if (cargo_type1 == cargo_type2) return true;
} }
} }
@ -2677,7 +2677,7 @@ struct IndustryCargoesWindow : public Window {
*/ */
static bool HousesCanSupply(const std::span<const CargoType> cargoes) static bool HousesCanSupply(const std::span<const CargoType> cargoes)
{ {
for (const CargoType cargo_type : cargoes) { for (const CargoType &cargo_type : cargoes) {
if (!IsValidCargoType(cargo_type)) continue; if (!IsValidCargoType(cargo_type)) continue;
TownProductionEffect tpe = CargoSpec::Get(cargo_type)->town_production_effect; TownProductionEffect tpe = CargoSpec::Get(cargo_type)->town_production_effect;
if (tpe == TPE_PASSENGERS || tpe == TPE_MAIL) return true; if (tpe == TPE_PASSENGERS || tpe == TPE_MAIL) return true;
@ -2694,7 +2694,7 @@ struct IndustryCargoesWindow : public Window {
{ {
HouseZones climate_mask = GetClimateMaskForLandscape(); HouseZones climate_mask = GetClimateMaskForLandscape();
for (const CargoType cargo_type : cargoes) { for (const CargoType &cargo_type : cargoes) {
if (!IsValidCargoType(cargo_type)) continue; if (!IsValidCargoType(cargo_type)) continue;
for (const auto &hs : HouseSpec::Specs()) { for (const auto &hs : HouseSpec::Specs()) {
@ -2875,7 +2875,7 @@ struct IndustryCargoesWindow : public Window {
*/ */
void ComputeCargoDisplay(CargoType cargo_type) void ComputeCargoDisplay(CargoType cargo_type)
{ {
this->ind_cargo = cargo_type + NUM_INDUSTRYTYPES; this->ind_cargo = cargo_type.base() + NUM_INDUSTRYTYPES;
_displayed_industries.reset(); _displayed_industries.reset();
this->fields.clear(); this->fields.clear();
@ -3088,7 +3088,7 @@ struct IndustryCargoesWindow : public Window {
DropDownList lst; DropDownList lst;
Dimension d = GetLargestCargoIconSize(); Dimension d = GetLargestCargoIconSize();
for (const CargoSpec *cs : _sorted_standard_cargo_specs) { for (const CargoSpec *cs : _sorted_standard_cargo_specs) {
lst.push_back(MakeDropDownListIconItem(d, cs->GetCargoIcon(), PAL_NONE, cs->name, cs->Index())); lst.push_back(MakeDropDownListIconItem(d, cs->GetCargoIcon(), PAL_NONE, cs->name, cs->Index().base()));
} }
if (!lst.empty()) { if (!lst.empty()) {
int selected = (this->ind_cargo >= NUM_INDUSTRYTYPES) ? (int)(this->ind_cargo - NUM_INDUSTRYTYPES) : -1; int selected = (this->ind_cargo >= NUM_INDUSTRYTYPES) ? (int)(this->ind_cargo - NUM_INDUSTRYTYPES) : -1;
@ -3119,7 +3119,7 @@ struct IndustryCargoesWindow : public Window {
switch (widget) { switch (widget) {
case WID_IC_CARGO_DROPDOWN: case WID_IC_CARGO_DROPDOWN:
this->ComputeCargoDisplay(index); this->ComputeCargoDisplay(static_cast<CargoType>(index));
break; break;
case WID_IC_IND_DROPDOWN: case WID_IC_IND_DROPDOWN:

View File

@ -267,6 +267,7 @@ STR_UNITS_YEARS :{NUM}{NBSP}năm
STR_UNITS_PERIODS :{NUM}{NBSP}kỳ STR_UNITS_PERIODS :{NUM}{NBSP}kỳ
STR_LIST_SEPARATOR :,{SPACE} STR_LIST_SEPARATOR :,{SPACE}
STR_TRUNCATION_ELLIPSIS :...
# Common window strings # Common window strings
STR_LIST_FILTER_TITLE :{BLACK}Lọc: STR_LIST_FILTER_TITLE :{BLACK}Lọc:
@ -285,7 +286,7 @@ STR_TOOLTIP_CLOSE_WINDOW :{BLACK}Đóng c
STR_TOOLTIP_WINDOW_TITLE_DRAG_THIS :{BLACK}Tiêu đề cửa sổ - kéo nó để di chuyển cửa số STR_TOOLTIP_WINDOW_TITLE_DRAG_THIS :{BLACK}Tiêu đề cửa sổ - kéo nó để di chuyển cửa số
STR_TOOLTIP_SHADE :{BLACK}Thu gọn cửa sổ - Chỉ hiển thị thanh tiêu đề STR_TOOLTIP_SHADE :{BLACK}Thu gọn cửa sổ - Chỉ hiển thị thanh tiêu đề
STR_TOOLTIP_DEBUG :{BLACK}Hiện thông tin debug của NewGRF STR_TOOLTIP_DEBUG :{BLACK}Hiện thông tin debug của NewGRF
STR_TOOLTIP_DEFSIZE :{BLACK}Chuyển cửa sổ về kích thước mặc định. Ctrl+Click để lưu kích thước hiện tại làm mặc định STR_TOOLTIP_DEFSIZE :{BLACK}Chuyển cửa sổ về kích thước mặc định. Ctrl+Click để lưu kích thước hiện tại làm mặc định. Ctri+Click kép để thiết lập lại mặc định cũ.
STR_TOOLTIP_STICKY :{BLACK}Đánh dấu không-thể-đóng khi bấm nút "Đóng Tất Cả Cửa Sổ". Ctrl+Click để lưu thành trạng thái mặc định STR_TOOLTIP_STICKY :{BLACK}Đánh dấu không-thể-đóng khi bấm nút "Đóng Tất Cả Cửa Sổ". Ctrl+Click để lưu thành trạng thái mặc định
STR_TOOLTIP_RESIZE :{BLACK}Click và kéo để thay đổi kích thước cửa sổ STR_TOOLTIP_RESIZE :{BLACK}Click và kéo để thay đổi kích thước cửa sổ
STR_TOOLTIP_TOGGLE_LARGE_SMALL_WINDOW :{BLACK}Bật kích cỡ cửa sổ lớn/nhỏ STR_TOOLTIP_TOGGLE_LARGE_SMALL_WINDOW :{BLACK}Bật kích cỡ cửa sổ lớn/nhỏ
@ -451,6 +452,12 @@ STR_SETTINGS_MENU_SANDBOX_OPTIONS :Tuỳ chọn Sa
STR_SETTINGS_MENU_TRANSPARENCY_OPTIONS :Thiết lập hiệu ứng trong suốt STR_SETTINGS_MENU_TRANSPARENCY_OPTIONS :Thiết lập hiệu ứng trong suốt
STR_SETTINGS_MENU_TOWN_NAMES_DISPLAYED :Hiển thị tên thị trấn STR_SETTINGS_MENU_TOWN_NAMES_DISPLAYED :Hiển thị tên thị trấn
STR_SETTINGS_MENU_STATION_NAMES_DISPLAYED :Hiển thị tên nhà ga STR_SETTINGS_MENU_STATION_NAMES_DISPLAYED :Hiển thị tên nhà ga
STR_SETTINGS_MENU_STATION_NAMES_TRAIN :Ga tàu
STR_SETTINGS_MENU_STATION_NAMES_LORRY :Trạm xe tải
STR_SETTINGS_MENU_STATION_NAMES_BUS :Trạm xe buýt
STR_SETTINGS_MENU_STATION_NAMES_SHIP :Cảng
STR_SETTINGS_MENU_STATION_NAMES_PLANE :Sân bay
STR_SETTINGS_MENU_STATION_NAMES_GHOST :Trạm ma
STR_SETTINGS_MENU_WAYPOINTS_DISPLAYED :Hiển thị tên điểm mốc STR_SETTINGS_MENU_WAYPOINTS_DISPLAYED :Hiển thị tên điểm mốc
STR_SETTINGS_MENU_SIGNS_DISPLAYED :Hiển thị ký hiệu STR_SETTINGS_MENU_SIGNS_DISPLAYED :Hiển thị ký hiệu
STR_SETTINGS_MENU_SHOW_COMPETITOR_SIGNS :Hiển thị biển hiệu và tên của đối thủ STR_SETTINGS_MENU_SHOW_COMPETITOR_SIGNS :Hiển thị biển hiệu và tên của đối thủ
@ -627,8 +634,11 @@ STR_GRAPH_CARGO_TOOLTIP_DISABLE_ALL :{BLACK}Không h
STR_GRAPH_CARGO_PAYMENT_TOGGLE_CARGO :{BLACK}Bật/tắt đồ thị cho hàng hóa này STR_GRAPH_CARGO_PAYMENT_TOGGLE_CARGO :{BLACK}Bật/tắt đồ thị cho hàng hóa này
STR_GRAPH_CARGO_PAYMENT_CARGO :{TINY_FONT}{BLACK}{STRING} STR_GRAPH_CARGO_PAYMENT_CARGO :{TINY_FONT}{BLACK}{STRING}
STR_GRAPH_INDUSTRY_CAPTION :{WHITE}{INDUSTRY} - Lịch sử hàng hóa
STR_GRAPH_INDUSTRY_RANGE_PRODUCED :Đã cung cấp STR_GRAPH_INDUSTRY_RANGE_PRODUCED :Đã cung cấp
STR_GRAPH_INDUSTRY_RANGE_TRANSPORTED :Đã vận chuyển STR_GRAPH_INDUSTRY_RANGE_TRANSPORTED :Đã vận chuyển
STR_GRAPH_INDUSTRY_RANGE_DELIVERED :Đã giao
STR_GRAPH_INDUSTRY_RANGE_WAITING :Đang chờ...
STR_GRAPH_PERFORMANCE_DETAIL_TOOLTIP :{BLACK}Hiện chi tiết đánh giá chỉ số năng suất STR_GRAPH_PERFORMANCE_DETAIL_TOOLTIP :{BLACK}Hiện chi tiết đánh giá chỉ số năng suất
@ -830,7 +840,7 @@ STR_STATUSBAR_INFINITE_MONEY :{WHITE}(tiền
# News message history # News message history
STR_MESSAGE_HISTORY :{WHITE}Lịch Sử Thông Điệp STR_MESSAGE_HISTORY :{WHITE}Lịch Sử Thông Điệp
STR_MESSAGE_HISTORY_TOOLTIP :{BLACK}Danh sách những tin tức gần đây STR_MESSAGE_HISTORY_TOOLTIP :{BLACK}Danh sách những tin tức gần đây
STR_MESSAGE_NEWS_FORMAT :{STRING} - {STRING} STR_MESSAGE_NEWS_FORMAT :{STRING} - {STRING}
STR_NEWS_MESSAGE_CAPTION :{WHITE}Thông Điệp STR_NEWS_MESSAGE_CAPTION :{WHITE}Thông Điệp
@ -958,11 +968,14 @@ STR_GAME_OPTIONS_TAB_SOUND :Âm thanh
STR_GAME_OPTIONS_TAB_SOUND_TOOLTIP :Lựa chọn thiết lập cho âm thanh và nhạc STR_GAME_OPTIONS_TAB_SOUND_TOOLTIP :Lựa chọn thiết lập cho âm thanh và nhạc
STR_GAME_OPTIONS_TAB_SOCIAL :Xã hội STR_GAME_OPTIONS_TAB_SOCIAL :Xã hội
STR_GAME_OPTIONS_TAB_SOCIAL_TOOLTIP :Chọn thiết lập các tích hợp xã hội STR_GAME_OPTIONS_TAB_SOCIAL_TOOLTIP :Chọn thiết lập các tích hợp xã hội
STR_GAME_OPTIONS_TAB_ADVANCED :Tùy chọn nâng cao
STR_GAME_OPTIONS_TAB_ADVANCED_TOOLTIP :Thay đổi tùy chọn nâng cao
STR_GAME_OPTIONS_VOLUME :Âm lượng STR_GAME_OPTIONS_VOLUME :Âm lượng
STR_GAME_OPTIONS_SFX_VOLUME :Hiệu ứng âm thanh STR_GAME_OPTIONS_SFX_VOLUME :Hiệu ứng âm thanh
STR_GAME_OPTIONS_MUSIC_VOLUME :Âm nhạc STR_GAME_OPTIONS_MUSIC_VOLUME :Âm nhạc
STR_GAME_OPTIONS_SETTING :{STRING}: {ORANGE}{STRING}
STR_GAME_OPTIONS_VOLUME_MARK :{NUM}% STR_GAME_OPTIONS_VOLUME_MARK :{NUM}%
@ -1016,6 +1029,7 @@ STR_GAME_OPTIONS_CURRENCY_IDR :Rupiah Indonesi
STR_GAME_OPTIONS_CURRENCY_MYR :Ringgit Malaysia STR_GAME_OPTIONS_CURRENCY_MYR :Ringgit Malaysia
STR_GAME_OPTIONS_CURRENCY_LVL :Lát-vi-a Lats STR_GAME_OPTIONS_CURRENCY_LVL :Lát-vi-a Lats
STR_GAME_OPTIONS_CURRENCY_PTE :Escudo Bồ Đào Nha STR_GAME_OPTIONS_CURRENCY_PTE :Escudo Bồ Đào Nha
STR_GAME_OPTIONS_CURRENCY_UAH :Hryvnia Ukraina
STR_GAME_OPTIONS_AUTOSAVE_FRAME :Lưu tự động STR_GAME_OPTIONS_AUTOSAVE_FRAME :Lưu tự động
STR_GAME_OPTIONS_AUTOSAVE_DROPDOWN_TOOLTIP :Lựa chọn khoảng thời gian tự động lưu STR_GAME_OPTIONS_AUTOSAVE_DROPDOWN_TOOLTIP :Lựa chọn khoảng thời gian tự động lưu
@ -1049,6 +1063,7 @@ STR_GAME_OPTIONS_VIDEO_VSYNC_TOOLTIP :Đánh dấu v
STR_GAME_OPTIONS_VIDEO_DRIVER_INFO :Trình điều khiển hiện tại: {STRING} STR_GAME_OPTIONS_VIDEO_DRIVER_INFO :Trình điều khiển hiện tại: {STRING}
STR_GAME_OPTIONS_INTERFACE :Giao diện
STR_GAME_OPTIONS_GUI_SCALE_FRAME :Kích thước giao diện STR_GAME_OPTIONS_GUI_SCALE_FRAME :Kích thước giao diện
STR_GAME_OPTIONS_GUI_SCALE_TOOLTIP :Kéo thanh trượt để điều chỉnh kích thước giao diện. Giữ Ctrl để điều chỉnh liên tục STR_GAME_OPTIONS_GUI_SCALE_TOOLTIP :Kéo thanh trượt để điều chỉnh kích thước giao diện. Giữ Ctrl để điều chỉnh liên tục
@ -1073,6 +1088,7 @@ STR_GAME_OPTIONS_PARTICIPATE_SURVEY_LINK_TOOLTIP :Sẽ mở trìn
STR_GAME_OPTIONS_PARTICIPATE_SURVEY_PREVIEW :Xem trước kết quả khảo sát STR_GAME_OPTIONS_PARTICIPATE_SURVEY_PREVIEW :Xem trước kết quả khảo sát
STR_GAME_OPTIONS_PARTICIPATE_SURVEY_PREVIEW_TOOLTIP :Hiển thị kết quả khảo sát ở ván chơi hiện tại STR_GAME_OPTIONS_PARTICIPATE_SURVEY_PREVIEW_TOOLTIP :Hiển thị kết quả khảo sát ở ván chơi hiện tại
STR_GAME_OPTIONS_DISPLAY :Hiển thị
STR_GAME_OPTIONS_REFRESH_RATE :Tần số quét màn hình STR_GAME_OPTIONS_REFRESH_RATE :Tần số quét màn hình
STR_GAME_OPTIONS_REFRESH_RATE_TOOLTIP :Chọn tần số quét màn hình STR_GAME_OPTIONS_REFRESH_RATE_TOOLTIP :Chọn tần số quét màn hình
@ -1094,7 +1110,7 @@ STR_GAME_OPTIONS_BASE_MUSIC_DESCRIPTION_TOOLTIP :Thông tin thê
STR_GAME_OPTIONS_ONLINE_CONTENT :Tải Nội Dung STR_GAME_OPTIONS_ONLINE_CONTENT :Tải Nội Dung
STR_GAME_OPTIONS_ONLINE_CONTENT_TOOLTIP :Kiểm tra những nội dung mới & cập nhật để tải về STR_GAME_OPTIONS_ONLINE_CONTENT_TOOLTIP :Kiểm tra những nội dung mới & cập nhật để tải về
STR_GAME_OPTIONS_SOCIAL_PLUGINS_NONE :{LTBLUE}(không có plugins được cài đặt để tích hợp vào nền tảng xã hội) STR_GAME_OPTIONS_SOCIAL_PLUGINS_NONE :(không có plugins được cài đặt để tích hợp vào nền tảng xã hội)
STR_GAME_OPTIONS_SOCIAL_PLUGIN_TITLE :{STRING} ({STRING}) STR_GAME_OPTIONS_SOCIAL_PLUGIN_TITLE :{STRING} ({STRING})
STR_GAME_OPTIONS_SOCIAL_PLUGIN_PLATFORM :Nền tảng: STR_GAME_OPTIONS_SOCIAL_PLUGIN_PLATFORM :Nền tảng:
@ -1288,6 +1304,9 @@ STR_CONFIG_SETTING_INTEREST_RATE_HELPTEXT :Lãi xuất vay
STR_CONFIG_SETTING_RUNNING_COSTS :Chi phí hoạt động: {STRING} STR_CONFIG_SETTING_RUNNING_COSTS :Chi phí hoạt động: {STRING}
STR_CONFIG_SETTING_RUNNING_COSTS_HELPTEXT :Thiết lập mức độ tính chi phí bảo trì và vận hành đối với phương tiện và hạ tầng giao thông STR_CONFIG_SETTING_RUNNING_COSTS_HELPTEXT :Thiết lập mức độ tính chi phí bảo trì và vận hành đối với phương tiện và hạ tầng giao thông
###length 3 ###length 3
STR_CONFIG_SETTING_RUNNING_COSTS_LOW :Thấp
STR_CONFIG_SETTING_RUNNING_COSTS_MEDIUM :Trung bình
STR_CONFIG_SETTING_RUNNING_COSTS_HIGH :Cao
STR_CONFIG_SETTING_CONSTRUCTION_SPEED :Tốc độ xây dựng: {STRING} STR_CONFIG_SETTING_CONSTRUCTION_SPEED :Tốc độ xây dựng: {STRING}
STR_CONFIG_SETTING_CONSTRUCTION_SPEED_HELPTEXT :Giới hạn hành động xây dựng của AI STR_CONFIG_SETTING_CONSTRUCTION_SPEED_HELPTEXT :Giới hạn hành động xây dựng của AI
@ -1310,6 +1329,9 @@ STR_CONFIG_SETTING_SUBSIDY_DURATION_DISABLED :Không có tr
STR_CONFIG_SETTING_CONSTRUCTION_COSTS :Chi phí xây dựng: {STRING} STR_CONFIG_SETTING_CONSTRUCTION_COSTS :Chi phí xây dựng: {STRING}
STR_CONFIG_SETTING_CONSTRUCTION_COSTS_HELPTEXT :Thiết lập mức độ xây dựng và chi phí mua sắm STR_CONFIG_SETTING_CONSTRUCTION_COSTS_HELPTEXT :Thiết lập mức độ xây dựng và chi phí mua sắm
###length 3 ###length 3
STR_CONFIG_SETTING_CONSTRUCTION_COSTS_LOW :Thấp
STR_CONFIG_SETTING_CONSTRUCTION_COSTS_MEDIUM :Trung bình
STR_CONFIG_SETTING_CONSTRUCTION_COSTS_HIGH :Cao
STR_CONFIG_SETTING_RECESSIONS :Suy thoái: {STRING} STR_CONFIG_SETTING_RECESSIONS :Suy thoái: {STRING}
STR_CONFIG_SETTING_RECESSIONS_HELPTEXT :Nếu bật, thì các đợt suy thoái sẽ xảy ra vài năm một lần. Trong suy thoái tất cả sản xuất sẽ giảm mạnh (và sẽ trở lại như cũ sau khi suy thoái kết thúc) STR_CONFIG_SETTING_RECESSIONS_HELPTEXT :Nếu bật, thì các đợt suy thoái sẽ xảy ra vài năm một lần. Trong suy thoái tất cả sản xuất sẽ giảm mạnh (và sẽ trở lại như cũ sau khi suy thoái kết thúc)
@ -1979,8 +2001,12 @@ STR_CONFIG_SETTING_TOWN_FOUNDING_FORBIDDEN :không cho phé
STR_CONFIG_SETTING_TOWN_FOUNDING_ALLOWED :cho phép STR_CONFIG_SETTING_TOWN_FOUNDING_ALLOWED :cho phép
STR_CONFIG_SETTING_TOWN_FOUNDING_ALLOWED_CUSTOM_LAYOUT :cho phép, tùy chọn bố trí đô thị STR_CONFIG_SETTING_TOWN_FOUNDING_ALLOWED_CUSTOM_LAYOUT :cho phép, tùy chọn bố trí đô thị
STR_CONFIG_SETTING_HOUSE_PLACER :Đật từng ngôi nhà: {STRING}
STR_CONFIG_SETTING_HOUSE_PLACER_HELPTEXT :Bật tùy chọn này cho phép người chơi đặt nhà cửa bằng tay STR_CONFIG_SETTING_HOUSE_PLACER_HELPTEXT :Bật tùy chọn này cho phép người chơi đặt nhà cửa bằng tay
###length 3 ###length 3
STR_CONFIG_SETTING_HOUSE_PLACER_FORBIDDEN :Không cho phép
STR_CONFIG_SETTING_HOUSE_PLACER_ALLOWED :Cho phép
STR_CONFIG_SETTING_HOUSE_PLACER_FULLY_CONSTRUCTED :Cho phép, đã hoàn thành thi công
STR_CONFIG_SETTING_TOWN_CARGOGENMODE :Nhu cầu vận chuyển hàng đô thị: {STRING} STR_CONFIG_SETTING_TOWN_CARGOGENMODE :Nhu cầu vận chuyển hàng đô thị: {STRING}
STR_CONFIG_SETTING_TOWN_CARGOGENMODE_HELPTEXT :Lượng hàng hoá cần vận chuyển ở trong đô thị, tỉ lệ với tổng dân số của độ thị.{}Tăng tỉ lệ bình phương: một đô thị to gấp 2 sẽ tăng 4 lần số hành khách.{}Tăng tỉ lệ thuận: một đô thị tăng gấp 2 sẽ tăng gấp 2 lần số hành khách STR_CONFIG_SETTING_TOWN_CARGOGENMODE_HELPTEXT :Lượng hàng hoá cần vận chuyển ở trong đô thị, tỉ lệ với tổng dân số của độ thị.{}Tăng tỉ lệ bình phương: một đô thị to gấp 2 sẽ tăng 4 lần số hành khách.{}Tăng tỉ lệ thuận: một đô thị tăng gấp 2 sẽ tăng gấp 2 lần số hành khách
@ -2009,7 +2035,7 @@ STR_CONFIG_SETTING_SOFT_LIMIT :Giới hạn s
STR_CONFIG_SETTING_SOFT_LIMIT_HELPTEXT :Số lượng cửa sổ chưa neo (tối đa) trước khi tự động đóng để nhường chỗ khi mở cửa sổ mới STR_CONFIG_SETTING_SOFT_LIMIT_HELPTEXT :Số lượng cửa sổ chưa neo (tối đa) trước khi tự động đóng để nhường chỗ khi mở cửa sổ mới
STR_CONFIG_SETTING_SOFT_LIMIT_VALUE :{COMMA} STR_CONFIG_SETTING_SOFT_LIMIT_VALUE :{COMMA}
###setting-zero-is-special ###setting-zero-is-special
STR_CONFIG_SETTING_SOFT_LIMIT_DISABLED :tắt STR_CONFIG_SETTING_SOFT_LIMIT_DISABLED :Tắt
STR_CONFIG_SETTING_ZOOM_MIN :Độ phóng to tối đa: {STRING} STR_CONFIG_SETTING_ZOOM_MIN :Độ phóng to tối đa: {STRING}
STR_CONFIG_SETTING_ZOOM_MIN_HELPTEXT :Độ phóng to tối đa của cửa sổ. Độ càng cao thì yêu cầu bộ nhớ càng nhiều STR_CONFIG_SETTING_ZOOM_MIN_HELPTEXT :Độ phóng to tối đa của cửa sổ. Độ càng cao thì yêu cầu bộ nhớ càng nhiều
@ -2061,9 +2087,9 @@ STR_CONFIG_SETTING_DISTRIBUTION_ARMOURED_HELPTEXT :Loại hàng h
STR_CONFIG_SETTING_DISTRIBUTION_DEFAULT :Chế độ phân phối đối với các loại hàng hóa mặc định: {STRING} STR_CONFIG_SETTING_DISTRIBUTION_DEFAULT :Chế độ phân phối đối với các loại hàng hóa mặc định: {STRING}
STR_CONFIG_SETTING_DISTRIBUTION_DEFAULT_HELPTEXT :"Không đối xứng" có nghĩa là số lượng hàng hóa tùy ý có thể được gửi theo một trong hai hướng. "Thủ công" có nghĩa là những loại hàng hóa đó sẽ không được phân phối tự động STR_CONFIG_SETTING_DISTRIBUTION_DEFAULT_HELPTEXT :"Không đối xứng" có nghĩa là số lượng hàng hóa tùy ý có thể được gửi theo một trong hai hướng. "Thủ công" có nghĩa là những loại hàng hóa đó sẽ không được phân phối tự động
###length 3 ###length 3
STR_CONFIG_SETTING_DISTRIBUTION_MANUAL :bằng tay STR_CONFIG_SETTING_DISTRIBUTION_MANUAL :Bằng tay
STR_CONFIG_SETTING_DISTRIBUTION_ASYMMETRIC :bất đối xứng STR_CONFIG_SETTING_DISTRIBUTION_ASYMMETRIC :Bất đối xứng
STR_CONFIG_SETTING_DISTRIBUTION_SYMMETRIC :đối xứng STR_CONFIG_SETTING_DISTRIBUTION_SYMMETRIC :Đối xứng
STR_CONFIG_SETTING_LINKGRAPH_ACCURACY :Độ chính xác phân phối: {STRING} STR_CONFIG_SETTING_LINKGRAPH_ACCURACY :Độ chính xác phân phối: {STRING}
STR_CONFIG_SETTING_LINKGRAPH_ACCURACY_HELPTEXT :Mức chính xác tính toán đồ thị, nếu giá trị càng cao càng tốn CPU và trò chơi có thể chậm phản ứng, tuy nhiên giá trị thấp sẽ khiến việc phân phối sẽ giảm sự chính xác và bạn sẽ thấy sự khác biệt là hàng hóa không gửi đến chỗ cần đến STR_CONFIG_SETTING_LINKGRAPH_ACCURACY_HELPTEXT :Mức chính xác tính toán đồ thị, nếu giá trị càng cao càng tốn CPU và trò chơi có thể chậm phản ứng, tuy nhiên giá trị thấp sẽ khiến việc phân phối sẽ giảm sự chính xác và bạn sẽ thấy sự khác biệt là hàng hóa không gửi đến chỗ cần đến
@ -2124,7 +2150,7 @@ STR_CONFIG_SETTING_LOCALISATION_UNITS_HEIGHT_SI :SI (m)
STR_CONFIG_SETTING_LOCALISATION :Tiêu Chuẩn Đo Lường STR_CONFIG_SETTING_LOCALISATION :Tiêu Chuẩn Đo Lường
STR_CONFIG_SETTING_GRAPHICS :Đồ họa STR_CONFIG_SETTING_GRAPHICS :Đồ họa
STR_CONFIG_SETTING_SOUND :Âm thanh STR_CONFIG_SETTING_SOUND :Âm thanh
STR_CONFIG_SETTING_INTERFACE :Giao Diện STR_CONFIG_SETTING_INTERFACE :Giao diện
STR_CONFIG_SETTING_INTERFACE_GENERAL :Tổng quát STR_CONFIG_SETTING_INTERFACE_GENERAL :Tổng quát
STR_CONFIG_SETTING_INTERFACE_VIEWPORTS :Vùng nhìn STR_CONFIG_SETTING_INTERFACE_VIEWPORTS :Vùng nhìn
STR_CONFIG_SETTING_INTERFACE_CONSTRUCTION :Xây Dựng STR_CONFIG_SETTING_INTERFACE_CONSTRUCTION :Xây Dựng
@ -2178,6 +2204,8 @@ STR_VIDEO_DRIVER_ERROR_NO_HARDWARE_ACCELERATION :{WHITE}... khô
STR_VIDEO_DRIVER_ERROR_HARDWARE_ACCELERATION_CRASH :{WHITE}... trình điều khiển GPU đã làm trò chơi bị lỗi. Tăng tốc phần cứng đã được tắt STR_VIDEO_DRIVER_ERROR_HARDWARE_ACCELERATION_CRASH :{WHITE}... trình điều khiển GPU đã làm trò chơi bị lỗi. Tăng tốc phần cứng đã được tắt
# Intro window # Intro window
STR_INTRO_CAPTION :{WHITE}OpenTTD
STR_INTRO_VERSION :OpenTTD {REV}
STR_INTRO_NEW_GAME :{BLACK}Màn Chơi Mới STR_INTRO_NEW_GAME :{BLACK}Màn Chơi Mới
STR_INTRO_LOAD_GAME :{BLACK}Nạp Ván Chơi STR_INTRO_LOAD_GAME :{BLACK}Nạp Ván Chơi
@ -2311,16 +2339,19 @@ STR_FACE_SIMPLE_TOOLTIP :{BLACK}Trình c
STR_FACE_LOAD :{BLACK}Nạp STR_FACE_LOAD :{BLACK}Nạp
STR_FACE_LOAD_TOOLTIP :{BLACK}Chọn vẻ mặt ưa thích STR_FACE_LOAD_TOOLTIP :{BLACK}Chọn vẻ mặt ưa thích
STR_FACE_LOAD_DONE :{WHITE}Vẻ mặt ưa thích đã được nạp từ file thiết lập của OpenTTD. STR_FACE_LOAD_DONE :{WHITE}Vẻ mặt ưa thích đã được nạp từ file thiết lập của OpenTTD.
STR_FACE_FACECODE :{BLACK}Khuôn mặt thứ. STR_FACE_FACECODE :{BLACK}Mã số khuôn mặt
STR_FACE_FACECODE_TOOLTIP :{BLACK}Xem và/hoặc sửa số vẻ mặt của chủ tịch công ty STR_FACE_FACECODE_TOOLTIP :{BLACK}Xem và/hoặc sửa mã số gương mặt của chủ tịch công ty
STR_FACE_FACECODE_CAPTION :{WHITE}Xem và/hoặc chọn số bộ mặt người chơi STR_FACE_FACECODE_CAPTION :{WHITE}Xem và/hoặc chọn mã số gương mặt người chơi
STR_FACE_FACECODE_SET :{WHITE}Mã số gương mặt mới được thiết lập. STR_FACE_FACECODE_SET :{WHITE}Gương mặt người chơi mới được thiết lập.
STR_FACE_FACECODE_ERR :{WHITE}Không thể thiết lập mã số gương mặt - mã số phải trong khoảng từ 0 đến 4,294,967,295! STR_FACE_FACECODE_ERR :{WHITE}Không thể thiết lập mã số gương mặt - Nhãn và mã số phải hợp lệ
STR_FACE_SAVE :{BLACK}Lưu STR_FACE_SAVE :{BLACK}Lưu
STR_FACE_SAVE_TOOLTIP :{BLACK}Lưu gương mặt yêu thích STR_FACE_SAVE_TOOLTIP :{BLACK}Lưu gương mặt yêu thích
STR_FACE_SAVE_DONE :{WHITE}Gương mặt yêu thích này sẽ được lưu lại trong tập tin cấu hình OpenTTD . STR_FACE_SAVE_DONE :{WHITE}Gương mặt yêu thích này sẽ được lưu lại trong tập tin cấu hình OpenTTD .
STR_FACE_SETTING_TOGGLE :{STRING} {ORANGE}{STRING}
STR_FACE_SETTING_NUMERIC :{STRING} {ORANGE}{NUM} / {NUM}
STR_FACE_YES :Đồng ý STR_FACE_YES :Đồng ý
STR_FACE_NO :Không STR_FACE_NO :Không
STR_FACE_STYLE :Kiểu:
STR_FACE_HAIR :Tóc: STR_FACE_HAIR :Tóc:
STR_FACE_EYEBROWS :Lông mày: STR_FACE_EYEBROWS :Lông mày:
STR_FACE_EYECOLOUR :Màu mắt: STR_FACE_EYECOLOUR :Màu mắt:
@ -2607,7 +2638,7 @@ STR_NETWORK_MESSAGE_CLIENT_LEFT :*** {STRING} r
STR_NETWORK_MESSAGE_NAME_CHANGE :*** {STRING} đã đổi tên thành {STRING} STR_NETWORK_MESSAGE_NAME_CHANGE :*** {STRING} đã đổi tên thành {STRING}
STR_NETWORK_MESSAGE_GIVE_MONEY :*** {STRING} tặng {CURRENCY_LONG} cho {STRING} STR_NETWORK_MESSAGE_GIVE_MONEY :*** {STRING} tặng {CURRENCY_LONG} cho {STRING}
STR_NETWORK_MESSAGE_SERVER_SHUTDOWN :{WHITE}Server kết thúc phiên STR_NETWORK_MESSAGE_SERVER_SHUTDOWN :{WHITE}Server kết thúc phiên
STR_NETWORK_MESSAGE_SERVER_REBOOT :{WHITE}Server khởi động lại...{}Xin chờ... STR_NETWORK_MESSAGE_SERVER_REBOOT :{WHITE}Server khởi động lại...{}{}Xin chờ...
STR_NETWORK_MESSAGE_KICKED :*** {STRING} đã bị đá khỏi ván chơi. Lý do: ({STRING}) STR_NETWORK_MESSAGE_KICKED :*** {STRING} đã bị đá khỏi ván chơi. Lý do: ({STRING})
STR_NETWORK_ERROR_COORDINATOR_REGISTRATION_FAILED :{WHITE}Đăng ký server thất bại STR_NETWORK_ERROR_COORDINATOR_REGISTRATION_FAILED :{WHITE}Đăng ký server thất bại
@ -2793,6 +2824,10 @@ STR_PICKER_MODE_USED_TOOLTIP :Bật/tắt hi
STR_PICKER_MODE_SAVED :Đã lưu STR_PICKER_MODE_SAVED :Đã lưu
STR_PICKER_MODE_SAVED_TOOLTIP :Bật/tắt hiển thị những hạng mục được lưu STR_PICKER_MODE_SAVED_TOOLTIP :Bật/tắt hiển thị những hạng mục được lưu
STR_PICKER_PREVIEW_SHRINK :-
STR_PICKER_PREVIEW_SHRINK_TOOLTIP :Giảm chiều cao của ảnh xem trước. Ctrl+Click để giảm đến mức tối thiểu
STR_PICKER_PREVIEW_EXPAND :+
STR_PICKER_PREVIEW_EXPAND_TOOLTIP :Tăng chiều cao của ảnh xem trước. Ctrl+Click để tăng đến mức tối đa
STR_PICKER_STATION_CLASS_TOOLTIP :Chọn loại ga bến cần hiển thị STR_PICKER_STATION_CLASS_TOOLTIP :Chọn loại ga bến cần hiển thị
STR_PICKER_STATION_TYPE_TOOLTIP :Chọn loại ga bến để xây. Ctrl+Click để thêm hoặc bớt vào danh sách lưu STR_PICKER_STATION_TYPE_TOOLTIP :Chọn loại ga bến để xây. Ctrl+Click để thêm hoặc bớt vào danh sách lưu
@ -2816,6 +2851,7 @@ STR_HOUSE_PICKER_YEARS_FROM :{BLACK}Năm: {O
STR_HOUSE_PICKER_YEARS_UNTIL :{BLACK}Năm: {ORANGE}Đến {NUM} STR_HOUSE_PICKER_YEARS_UNTIL :{BLACK}Năm: {ORANGE}Đến {NUM}
STR_HOUSE_PICKER_SIZE :{BLACK}Kích thước: {ORANGE}{NUM}x{NUM} ô STR_HOUSE_PICKER_SIZE :{BLACK}Kích thước: {ORANGE}{NUM}x{NUM} ô
STR_HOUSE_PICKER_CARGO_ACCEPTED :{BLACK}Hàng hóa được chấp nhận: {ORANGE} STR_HOUSE_PICKER_CARGO_ACCEPTED :{BLACK}Hàng hóa được chấp nhận: {ORANGE}
STR_HOUSE_PICKER_CARGO_PRODUCED :{BLACK}Hàng hóa cung cấp: {ORANGE}{CARGO_LIST}
STR_HOUSE_PICKER_CLASS_ZONE1 :Ngoài rìa STR_HOUSE_PICKER_CLASS_ZONE1 :Ngoài rìa
STR_HOUSE_PICKER_CLASS_ZONE2 :Ngoại ô STR_HOUSE_PICKER_CLASS_ZONE2 :Ngoại ô
@ -2824,6 +2860,7 @@ STR_HOUSE_PICKER_CLASS_ZONE4 :Phía trong ngo
STR_HOUSE_PICKER_CLASS_ZONE5 :Nội thành STR_HOUSE_PICKER_CLASS_ZONE5 :Nội thành
STR_HOUSE_PICKER_PROTECT_TITLE :Ngăn chặn nâng cấp STR_HOUSE_PICKER_PROTECT_TITLE :Ngăn chặn nâng cấp
STR_HOUSE_PICKER_PROTECT_TOOLTIP :Chọn nếu ngôi nhà này có được bảo vệ khỏi việc bị thay thế hay không khi thị trấn phát triển
STR_HOUSE_PICKER_PROTECT_OFF :Tắt STR_HOUSE_PICKER_PROTECT_OFF :Tắt
STR_HOUSE_PICKER_PROTECT_ON :Bật STR_HOUSE_PICKER_PROTECT_ON :Bật
@ -3031,6 +3068,11 @@ STR_FOUND_TOWN_INITIAL_SIZE_TOOLTIP :{BLACK}Chọn q
STR_FOUND_TOWN_CITY :{BLACK}Đô thị STR_FOUND_TOWN_CITY :{BLACK}Đô thị
STR_FOUND_TOWN_CITY_TOOLTIP :{BLACK}Thành phố phát triển nhanh hơn thị trấn{}Tuỳ thuộc thiết lập, chúng lớn hơn khi khai sinh STR_FOUND_TOWN_CITY_TOOLTIP :{BLACK}Thành phố phát triển nhanh hơn thị trấn{}Tuỳ thuộc thiết lập, chúng lớn hơn khi khai sinh
STR_FOUND_TOWN_EXPAND_MODE :{YELLOW}Mở rộng thị trấn:
STR_FOUND_TOWN_EXPAND_BUILDINGS :Công trình
STR_FOUND_TOWN_EXPAND_BUILDINGS_TOOLTIP :Tăng số công trình của thị trấn
STR_FOUND_TOWN_EXPAND_ROADS :Đường sá
STR_FOUND_TOWN_EXPAND_ROADS_TOOLTIP :Tăng số đường sá của thị trấn
STR_FOUND_TOWN_ROAD_LAYOUT :{YELLOW}Quy hoạch đường đô thị: STR_FOUND_TOWN_ROAD_LAYOUT :{YELLOW}Quy hoạch đường đô thị:
STR_FOUND_TOWN_SELECT_LAYOUT_TOOLTIP :{BLACK}Chọn để quy hoạch đường bộ trong đô thị STR_FOUND_TOWN_SELECT_LAYOUT_TOOLTIP :{BLACK}Chọn để quy hoạch đường bộ trong đô thị
@ -3103,6 +3145,8 @@ STR_LANG_AREA_INFORMATION_TRAM_TYPE :{BLACK}Kiểu x
STR_LANG_AREA_INFORMATION_RAIL_SPEED_LIMIT :{BLACK}Giới hạn tốc độ đường ray: {LTBLUE}{VELOCITY} STR_LANG_AREA_INFORMATION_RAIL_SPEED_LIMIT :{BLACK}Giới hạn tốc độ đường ray: {LTBLUE}{VELOCITY}
STR_LANG_AREA_INFORMATION_ROAD_SPEED_LIMIT :{BLACK}Hạn chế tốc độ đường bộ: {LTBLUE}{VELOCITY} STR_LANG_AREA_INFORMATION_ROAD_SPEED_LIMIT :{BLACK}Hạn chế tốc độ đường bộ: {LTBLUE}{VELOCITY}
STR_LANG_AREA_INFORMATION_TRAM_SPEED_LIMIT :{BLACK}Tốc độ xe điện giới hạn: {LTBLUE}{VELOCITY} STR_LANG_AREA_INFORMATION_TRAM_SPEED_LIMIT :{BLACK}Tốc độ xe điện giới hạn: {LTBLUE}{VELOCITY}
STR_LAND_AREA_INFORMATION_TOWN_CAN_UPGRADE :{BLACK}Nâng cấp thị trấn: {LTBLUE}Có thể
STR_LAND_AREA_INFORMATION_TOWN_CANNOT_UPGRADE :{BLACK}Nâng cấp thị trấn: {LTBLUE}Không thể
# Description of land area of different tiles # Description of land area of different tiles
STR_LAI_CLEAR_DESCRIPTION_ROCKS :Đá STR_LAI_CLEAR_DESCRIPTION_ROCKS :Đá
@ -3111,6 +3155,9 @@ STR_LAI_CLEAR_DESCRIPTION_BARE_LAND :Đất trống
STR_LAI_CLEAR_DESCRIPTION_GRASS :Bãi cỏ STR_LAI_CLEAR_DESCRIPTION_GRASS :Bãi cỏ
STR_LAI_CLEAR_DESCRIPTION_FIELDS :Cánh đồng STR_LAI_CLEAR_DESCRIPTION_FIELDS :Cánh đồng
STR_LAI_CLEAR_DESCRIPTION_DESERT :Hoang mạc STR_LAI_CLEAR_DESCRIPTION_DESERT :Hoang mạc
STR_LAI_CLEAR_DESCRIPTION_SNOWY_ROCKS :Đá có tuyết phủ
STR_LAI_CLEAR_DESCRIPTION_SNOWY_ROUGH_LAND :Đất gồ ghề có tuyết phủ
STR_LAI_CLEAR_DESCRIPTION_SNOWY_GRASS :Cỏ có tuyết phủ
STR_LAI_RAIL_DESCRIPTION_TRACK :Đường ray STR_LAI_RAIL_DESCRIPTION_TRACK :Đường ray
STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_NORMAL_SIGNALS :Đường ray với đèn hiệu khóa STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_NORMAL_SIGNALS :Đường ray với đèn hiệu khóa
@ -3579,17 +3626,17 @@ STR_NEWGRF_LIST_COMPATIBLE :{YELLOW}Đã t
STR_NEWGRF_LIST_MISSING :{RED}Thiếu files STR_NEWGRF_LIST_MISSING :{RED}Thiếu files
# NewGRF 'it's broken' warnings # NewGRF 'it's broken' warnings
STR_NEWGRF_BROKEN :{WHITE}Hoạt động của NewGRF '{0:STRING}' có thể gây mất đồng bộ hoặc bị treo. STR_NEWGRF_BROKEN :{WHITE}Hoạt động của NewGRF '{PUSH_COLOUR}{0:STRING}{POP_COLOUR}' có thể gây mất đồng bộ hoặc bị treo.
STR_NEWGRF_BROKEN_POWERED_WAGON :{WHITE}Trạng thái đầu kéo '{1:ENGINE}' được thay đổi khi không ở trong xưởng sửa chữa. STR_NEWGRF_BROKEN_POWERED_WAGON :{WHITE}Trạng thái đầu kéo '{PUSH_COLOUR}{1:ENGINE}{POP_COLOUR}' được thay đổi khi không ở trong xưởng sửa chữa.
STR_NEWGRF_BROKEN_VEHICLE_LENGTH :{WHITE}Nó cắt ngắn độ dài của đoàn tàu '{1:ENGINE}' nếu không ở trong xưởng. STR_NEWGRF_BROKEN_VEHICLE_LENGTH :{WHITE}Nó cắt ngắn độ dài của đoàn tàu '{PUSH_COLOUR}{1:ENGINE}{POP_COLOUR}' nếu không ở trong xưởng.
STR_NEWGRF_BROKEN_CAPACITY :{WHITE}Sức chứa của phương tiện bị thay đổi '{1:ENGINE}' khi không ở trong xưởng hoặc vì cải biến STR_NEWGRF_BROKEN_CAPACITY :{WHITE}Sức chứa của phương tiện bị thay đổi '{PUSH_COLOUR}{1:ENGINE}{POP_COLOUR}' khi không ở trong xưởng hoặc vì cải biến
STR_BROKEN_VEHICLE_LENGTH :{WHITE}Đoàn tàu '{VEHICLE}' của '{COMPANY}' có độ dài không hợp lệ. Sự cố có thể có căn nguyên từ NewGRFs. Ván chơi có thể mất đồng bộ hoặc bị treo STR_BROKEN_VEHICLE_LENGTH :{WHITE}Đoàn tàu '{VEHICLE}' của '{COMPANY}' có độ dài không hợp lệ. Sự cố có thể có căn nguyên từ NewGRFs. Ván chơi có thể mất đồng bộ hoặc bị treo
STR_NEWGRF_BUGGY :{WHITE}NewGRF '{0:STRING}' không hợp lệ. STR_NEWGRF_BUGGY :{WHITE}NewGRF '{PUSH_COLOUR}{0:STRING}{POP_COLOUR}' không hợp lệ.
STR_NEWGRF_BUGGY_ARTICULATED_CARGO :{WHITE}Danh mục hàng hoá/cải biến được cho '{1:ENGINE}' khác với danh mục mua được sau khi đã có. Việc này khiến cho việc tự thay thế hay là tự cải biến không chính xác. STR_NEWGRF_BUGGY_ARTICULATED_CARGO :{WHITE}Danh mục hàng hoá/cải biến được cho '{PUSH_COLOUR}{1:ENGINE}{POP_COLOUR}' khác với danh mục mua được sau khi đã có. Việc này khiến cho việc tự thay thế hay là tự cải biến không chính xác.
STR_NEWGRF_BUGGY_ENDLESS_PRODUCTION_CALLBACK :{WHITE}'{1:STRING}' gây ra một vòng lặp vô tận khi gọi hàm callback. STR_NEWGRF_BUGGY_ENDLESS_PRODUCTION_CALLBACK :{WHITE}'{PUSH_COLOUR}{1:STRING}{POP_COLOUR}' gây ra một vòng lặp vô tận khi gọi hàm callback.
STR_NEWGRF_BUGGY_UNKNOWN_CALLBACK_RESULT :{WHITE}Hàm callback {1:HEX} gửi trả kết quả sai/không rõ {2:HEX} STR_NEWGRF_BUGGY_UNKNOWN_CALLBACK_RESULT :{WHITE}Hàm callback {1:HEX} gửi trả kết quả sai/không rõ {2:HEX}
STR_NEWGRF_BUGGY_INVALID_CARGO_PRODUCTION_CALLBACK :{WHITE}'{1:STRING}' trả về loại hàng hoá sản xuất không hợp lệ khi gọi lại tại {2:HEX} STR_NEWGRF_BUGGY_INVALID_CARGO_PRODUCTION_CALLBACK :{WHITE}'{PUSH_COLOUR}{1:STRING}{POP_COLOUR}' trả về loại hàng hoá sản xuất không hợp lệ khi gọi lại tại {2:HEX}
# 'User removed essential NewGRFs'-placeholders for stuff without specs # 'User removed essential NewGRFs'-placeholders for stuff without specs
STR_NEWGRF_INVALID_CARGO :<sai kiểu hàng> STR_NEWGRF_INVALID_CARGO :<sai kiểu hàng>
@ -3650,6 +3697,10 @@ STR_TOWN_VIEW_RENAME_TOOLTIP :{BLACK}Đổi t
STR_TOWN_VIEW_EXPAND_BUTTON :{BLACK}Mở rộng STR_TOWN_VIEW_EXPAND_BUTTON :{BLACK}Mở rộng
STR_TOWN_VIEW_EXPAND_TOOLTIP :{BLACK}Tăng quy mô đô thị STR_TOWN_VIEW_EXPAND_TOOLTIP :{BLACK}Tăng quy mô đô thị
STR_TOWN_VIEW_EXPAND_BUILDINGS_BUTTON :{BLACK}Mở rộng công trình
STR_TOWN_VIEW_EXPAND_BUILDINGS_TOOLTIP :{BLACK}Tăng số công trình của thị trấn
STR_TOWN_VIEW_EXPAND_ROADS_BUTTON :{BLACK}Mở rộng đường
STR_TOWN_VIEW_EXPAND_ROADS_TOOLTIP :{BLACK}Tăng số đường sá của thị trấn
STR_TOWN_VIEW_DELETE_BUTTON :{BLACK}Xoá STR_TOWN_VIEW_DELETE_BUTTON :{BLACK}Xoá
STR_TOWN_VIEW_DELETE_TOOLTIP :{BLACK}Xoá bỏ đô thị này hoàn toàn STR_TOWN_VIEW_DELETE_TOOLTIP :{BLACK}Xoá bỏ đô thị này hoàn toàn
@ -3975,6 +4026,8 @@ STR_INDUSTRY_VIEW_PRODUCTION_LAST_MONTH_TITLE :{BLACK}Sản l
STR_INDUSTRY_VIEW_PRODUCTION_LAST_MINUTE_TITLE :{BLACK}Sản lượng phút trước: STR_INDUSTRY_VIEW_PRODUCTION_LAST_MINUTE_TITLE :{BLACK}Sản lượng phút trước:
STR_INDUSTRY_VIEW_TRANSPORTED :{YELLOW}{CARGO_LONG}{STRING}{BLACK} ({COMMA}% đã vận chuyển) STR_INDUSTRY_VIEW_TRANSPORTED :{YELLOW}{CARGO_LONG}{STRING}{BLACK} ({COMMA}% đã vận chuyển)
STR_INDUSTRY_VIEW_LOCATION_TOOLTIP :{BLACK}Xem vị trí trung tâm của nhà máy. Ctrl+Click mở cửa sổ mới để xem STR_INDUSTRY_VIEW_LOCATION_TOOLTIP :{BLACK}Xem vị trí trung tâm của nhà máy. Ctrl+Click mở cửa sổ mới để xem
STR_INDUSTRY_VIEW_CARGO_GRAPH :{BLACK}Đồ thị hàng hóa
STR_INDUSTRY_VIEW_CARGO_GRAPH_TOOLTIP :{BLACK}Xem đồ thị lịch sử kinh doanh hàng hóa
STR_INDUSTRY_VIEW_PRODUCTION_LEVEL :{BLACK}Mức sản lượng: {YELLOW}{COMMA}% STR_INDUSTRY_VIEW_PRODUCTION_LEVEL :{BLACK}Mức sản lượng: {YELLOW}{COMMA}%
STR_INDUSTRY_VIEW_INDUSTRY_ANNOUNCED_CLOSURE :{YELLOW}Nhà máy này đã thông báo sắp đóng cửa! STR_INDUSTRY_VIEW_INDUSTRY_ANNOUNCED_CLOSURE :{YELLOW}Nhà máy này đã thông báo sắp đóng cửa!
@ -4386,10 +4439,10 @@ STR_VEHICLE_VIEW_SHIP_ORDERS_TOOLTIP :{BLACK}Hiện l
STR_VEHICLE_VIEW_AIRCRAFT_ORDERS_TOOLTIP :{BLACK}Hiện lộ trình máy bay. Ctrl+Click để hiện lịch trình STR_VEHICLE_VIEW_AIRCRAFT_ORDERS_TOOLTIP :{BLACK}Hiện lộ trình máy bay. Ctrl+Click để hiện lịch trình
###length VEHICLE_TYPES ###length VEHICLE_TYPES
STR_VEHICLE_VIEW_TRAIN_SHOW_DETAILS_TOOLTIP :{BLACK}Hiện chi tiết tàu hoả STR_VEHICLE_VIEW_TRAIN_SHOW_DETAILS_TOOLTIP :{BLACK}Hiện chi tiết tàu hoả. Ctrl+Click vào để hiện nhóm của tàu hỏa
STR_VEHICLE_VIEW_ROAD_VEHICLE_SHOW_DETAILS_TOOLTIP :{BLACK}Hiện chi tiết xe STR_VEHICLE_VIEW_ROAD_VEHICLE_SHOW_DETAILS_TOOLTIP :{BLACK}Hiện chi tiết xe. Ctrl+Click để hiện nhóm phương tiện
STR_VEHICLE_VIEW_SHIP_SHOW_DETAILS_TOOLTIP :{BLACK}Hiện chi tiết tàu thuỷ STR_VEHICLE_VIEW_SHIP_SHOW_DETAILS_TOOLTIP :{BLACK}Hiện chi tiết tàu thuỷ. Ctrl+Click vào để hiện nhóm của tàu thủy
STR_VEHICLE_VIEW_AIRCRAFT_SHOW_DETAILS_TOOLTIP :{BLACK}Hiện chi tiết máy bay STR_VEHICLE_VIEW_AIRCRAFT_SHOW_DETAILS_TOOLTIP :{BLACK}Hiện chi tiết máy bay. Ctrl+Click để hiện nhóm của máy bay
###length VEHICLE_TYPES ###length VEHICLE_TYPES
STR_VEHICLE_VIEW_TRAIN_STATUS_START_STOP_TOOLTIP :{BLACK}Tác động đến tàu hỏa hiện tại - bấm để dừng/chạy tàu hỏa STR_VEHICLE_VIEW_TRAIN_STATUS_START_STOP_TOOLTIP :{BLACK}Tác động đến tàu hỏa hiện tại - bấm để dừng/chạy tàu hỏa
@ -4644,55 +4697,56 @@ STR_ORDER_ROAD_VEHICLE_DEPOT :Xưởng xe
STR_ORDER_SHIP_DEPOT :Xưởng tàu thuỷ STR_ORDER_SHIP_DEPOT :Xưởng tàu thuỷ
###next-name-looks-similar ###next-name-looks-similar
STR_ORDER_GO_TO_NEAREST_HANGAR_FORMAT :{STRING} xưởng sân bay gần nhất
STR_ORDER_GO_TO_NEAREST_DEPOT_FORMAT :{STRING} gần {STRING} nhất STR_ORDER_GO_TO_NEAREST_DEPOT_FORMAT :{STRING} gần {STRING} nhất
STR_ORDER_GO_TO_DEPOT_FORMAT :{STRING} {DEPOT} STR_ORDER_GO_TO_DEPOT_FORMAT :{STRING} {DEPOT}
STR_ORDER_REFIT_ORDER :(Cải biến thành {STRING}) STR_ORDER_REFIT_ORDER :{SPACE}(Cải biến thành {STRING})
STR_ORDER_REFIT_STOP_ORDER :(Cải biến thành {STRING} và dừng) STR_ORDER_REFIT_STOP_ORDER :{SPACE}(Cải biến thành {STRING} và dừng)
STR_ORDER_STOP_ORDER :(Dừng) STR_ORDER_STOP_ORDER :{SPACE}(Dừng)
STR_ORDER_WAIT_TO_UNBUNCH :(Chờ để gỡ gộp) STR_ORDER_WAIT_TO_UNBUNCH :{SPACE}(Chờ để gỡ gộp)
STR_ORDER_GO_TO_STATION :{STRING} {STATION} STR_ORDER_GO_TO_STATION :{STRING} {STATION}
STR_ORDER_GO_TO_STATION_CAN_T_USE_STATION :{PUSH_COLOUR}{RED}(Không thể sử dụng trạm){POP_COLOUR} {STRING} {STATION} STR_ORDER_GO_TO_STATION_CAN_T_USE_STATION :{PUSH_COLOUR}{RED}(Không thể sử dụng trạm){POP_COLOUR} {STRING} {STATION}
STR_ORDER_IMPLICIT :(Chạy ngầm) STR_ORDER_IMPLICIT :{SPACE}(Chạy ngầm)
STR_ORDER_FULL_LOAD :(Bốc đầy hàng) STR_ORDER_FULL_LOAD :{SPACE}(Bốc đầy hàng)
STR_ORDER_FULL_LOAD_ANY :(Bốc đủ bất kỳ hàng nào) STR_ORDER_FULL_LOAD_ANY :{SPACE}(Bốc đủ bất kỳ hàng nào)
STR_ORDER_NO_LOAD :(Không bốc xếp) STR_ORDER_NO_LOAD :{SPACE}(Không bốc xếp)
STR_ORDER_UNLOAD :(Dỡ và lấy hàng khác) STR_ORDER_UNLOAD :{SPACE}(Dỡ và lấy hàng khác)
STR_ORDER_UNLOAD_FULL_LOAD :(Dỡ tất hàng và chờ bốc đầy hàng) STR_ORDER_UNLOAD_FULL_LOAD :{SPACE}(Dỡ tất hàng và chờ bốc đầy hàng)
STR_ORDER_UNLOAD_FULL_LOAD_ANY :(Dỡ tất hàng và chờ bốc đủ bất kỳ hàng nào) STR_ORDER_UNLOAD_FULL_LOAD_ANY :{SPACE}(Dỡ tất hàng và chờ bốc đủ bất kỳ hàng nào)
STR_ORDER_UNLOAD_NO_LOAD :(Dỡ tất hàng và để trống) STR_ORDER_UNLOAD_NO_LOAD :{SPACE}(Dỡ tất hàng và để trống)
STR_ORDER_TRANSFER :(Trung chuyển hàng và lấy hàng khác) STR_ORDER_TRANSFER :{SPACE}(Trung chuyển hàng và lấy hàng khác)
STR_ORDER_TRANSFER_FULL_LOAD :(Trung chuyển và chờ bốc đầy hàng) STR_ORDER_TRANSFER_FULL_LOAD :{SPACE}(Trung chuyển và chờ bốc đầy hàng)
STR_ORDER_TRANSFER_FULL_LOAD_ANY :(Trung chuyển và chờ bốc đủ hàng bất kỳ) STR_ORDER_TRANSFER_FULL_LOAD_ANY :{SPACE}(Trung chuyển và chờ bốc đủ hàng bất kỳ)
STR_ORDER_TRANSFER_NO_LOAD :(Trung chuyển và để trống) STR_ORDER_TRANSFER_NO_LOAD :{SPACE}(Trung chuyển và để trống)
STR_ORDER_NO_UNLOAD :(Không dỡ và lấy hàng) STR_ORDER_NO_UNLOAD :{SPACE}(Không dỡ và lấy hàng)
STR_ORDER_NO_UNLOAD_FULL_LOAD :(Không dỡ và chờ lấy thêm đầy hàng) STR_ORDER_NO_UNLOAD_FULL_LOAD :{SPACE}(Không dỡ và chờ lấy thêm đầy hàng)
STR_ORDER_NO_UNLOAD_FULL_LOAD_ANY :(Không dỡ và chờ lấy đủ hàng bất kỳ) STR_ORDER_NO_UNLOAD_FULL_LOAD_ANY :{SPACE}(Không dỡ và chờ lấy đủ hàng bất kỳ)
STR_ORDER_NO_UNLOAD_NO_LOAD :(Không bốc hàng và không dỡ hàng) STR_ORDER_NO_UNLOAD_NO_LOAD :{SPACE}(Không bốc hàng và không dỡ hàng)
STR_ORDER_AUTO_REFIT :(Tự cải biến thành {STRING}) STR_ORDER_AUTO_REFIT :{SPACE}(Tự cải biến thành {STRING})
STR_ORDER_FULL_LOAD_REFIT :(Tự cải biến và chất đầy {STRING}) STR_ORDER_FULL_LOAD_REFIT :{SPACE}(Tự cải biến và chất đầy {STRING})
STR_ORDER_FULL_LOAD_ANY_REFIT :(Tự cải biến và chất đầy bất kỳ {STRING}) STR_ORDER_FULL_LOAD_ANY_REFIT :{SPACE}(Tự cải biến và chất đầy bất kỳ {STRING})
STR_ORDER_UNLOAD_REFIT :(Dỡ hàng và tự cải biến để lấy {STRING}) STR_ORDER_UNLOAD_REFIT :{SPACE}(Dỡ hàng và tự cải biến để lấy {STRING})
STR_ORDER_UNLOAD_FULL_LOAD_REFIT :(Dỡ hàng và tự cải biến đề bốc đầy {STRING}) STR_ORDER_UNLOAD_FULL_LOAD_REFIT :{SPACE}(Dỡ hàng và tự cải biến đề bốc đầy {STRING})
STR_ORDER_UNLOAD_FULL_LOAD_ANY_REFIT :(Dỡ hàng và tự cải biến để bốc đầy bất kỳ {STRING}) STR_ORDER_UNLOAD_FULL_LOAD_ANY_REFIT :{SPACE}(Dỡ hàng và tự cải biến để bốc đầy bất kỳ {STRING})
STR_ORDER_TRANSFER_REFIT :(Trung chuyển và tự cải biến để lấy {STRING}) STR_ORDER_TRANSFER_REFIT :{SPACE}(Trung chuyển và tự cải biến để lấy {STRING})
STR_ORDER_TRANSFER_FULL_LOAD_REFIT :(Trung chuyển và tự cải biến đề bốc đầy {STRING}) STR_ORDER_TRANSFER_FULL_LOAD_REFIT :{SPACE}(Trung chuyển và tự cải biến đề bốc đầy {STRING})
STR_ORDER_TRANSFER_FULL_LOAD_ANY_REFIT :(Trung chuyển và tự cải biến để bốc đầy bất kỳ {STRING}) STR_ORDER_TRANSFER_FULL_LOAD_ANY_REFIT :{SPACE}(Trung chuyển và tự cải biến để bốc đầy bất kỳ {STRING})
STR_ORDER_NO_UNLOAD_REFIT :(Không dỡ hàng và tự cái biến để lấy {STRING}) STR_ORDER_NO_UNLOAD_REFIT :{SPACE}(Không dỡ hàng và tự cái biến để lấy {STRING})
STR_ORDER_NO_UNLOAD_FULL_LOAD_REFIT :(Không dỡ hàng và tự cải biến để bốc đầy {STRING}) STR_ORDER_NO_UNLOAD_FULL_LOAD_REFIT :{SPACE}(Không dỡ hàng và tự cải biến để bốc đầy {STRING})
STR_ORDER_NO_UNLOAD_FULL_LOAD_ANY_REFIT :(Không dỡ hàng và tự cải biến để bốc đầy bất kỳ {STRING}) STR_ORDER_NO_UNLOAD_FULL_LOAD_ANY_REFIT :{SPACE}(Không dỡ hàng và tự cải biến để bốc đầy bất kỳ {STRING})
STR_ORDER_AUTO_REFIT_ANY :hàng hóa sẵn có STR_ORDER_AUTO_REFIT_ANY :hàng hóa sẵn có
###length 3 ###length 3
STR_ORDER_STOP_LOCATION_NEAR_END :[đỗ ở đầu gần] STR_ORDER_STOP_LOCATION_NEAR_END :{SPACE}[đỗ ở đầu gần]
STR_ORDER_STOP_LOCATION_MIDDLE :[đỗ ở giữa] STR_ORDER_STOP_LOCATION_MIDDLE :{SPACE}[đỗ ở giữa]
STR_ORDER_STOP_LOCATION_FAR_END :[đỗ ở đầu xa] STR_ORDER_STOP_LOCATION_FAR_END :{SPACE}[đỗ ở đầu xa]
STR_ORDER_OUT_OF_RANGE :{RED} (Điểm đến kế tiếp ngoài tầm xa) STR_ORDER_OUT_OF_RANGE :{RED} (Điểm đến kế tiếp ngoài tầm xa)
@ -4712,14 +4766,15 @@ STR_TIMETABLE_TOOLTIP :{BLACK}Lịch t
STR_TIMETABLE_NO_TRAVEL :Không di chuyển STR_TIMETABLE_NO_TRAVEL :Không di chuyển
STR_TIMETABLE_NOT_TIMETABLEABLE :Hành trình (tự động; tính thời gian theo lịch trình thủ công kế tiếp) STR_TIMETABLE_NOT_TIMETABLEABLE :Hành trình (tự động; tính thời gian theo lịch trình thủ công kế tiếp)
STR_TIMETABLE_TRAVEL_NOT_TIMETABLED :Di chuyển (không bó buộc theo lịch trình) STR_TIMETABLE_TRAVEL_NOT_TIMETABLED :Di chuyển (không bó buộc theo lịch trình)
STR_TIMETABLE_TRAVEL_NOT_TIMETABLED_SPEED :Hành trình với tốc độ tối đa là {VELOCITY} (chưa dựng lịch trình)
STR_TIMETABLE_TRAVEL_FOR :Di chuyển trong {STRING} STR_TIMETABLE_TRAVEL_FOR :Di chuyển trong {STRING}
STR_TIMETABLE_TRAVEL_FOR_SPEED :Lộ trình {STRING} với tốc độ tối đa {VELOCITY} STR_TIMETABLE_TRAVEL_FOR_SPEED :Lộ trình {STRING} với tốc độ tối đa {VELOCITY}
STR_TIMETABLE_TRAVEL_FOR_ESTIMATED :Lộ trình (cho {STRING}, chưa có lịch trình) STR_TIMETABLE_TRAVEL_FOR_ESTIMATED :Lộ trình (cho {STRING}, chưa có lịch trình)
STR_TIMETABLE_TRAVEL_FOR_SPEED_ESTIMATED :Lộ trình (cho {STRING}, chưa có lịch trình) với tốc độ đối đa {VELOCITY} STR_TIMETABLE_TRAVEL_FOR_SPEED_ESTIMATED :Lộ trình (cho {STRING}, chưa có lịch trình) với tốc độ đối đa {VELOCITY}
STR_TIMETABLE_STAY_FOR_ESTIMATED :(ở lại {STRING}, chưa có lịch trình) STR_TIMETABLE_STAY_FOR_ESTIMATED :{SPACE}(ở lại {STRING}, chưa có lịch trình)
STR_TIMETABLE_AND_TRAVEL_FOR_ESTIMATED :(di chuyển đến {STRING}, chưa có lịch trình) STR_TIMETABLE_AND_TRAVEL_FOR_ESTIMATED :{SPACE}(di chuyển đến {STRING}, chưa có lịch trình)
STR_TIMETABLE_STAY_FOR :và ở lại trong {STRING} STR_TIMETABLE_STAY_FOR :{SPACE}và ở lại trong {STRING}
STR_TIMETABLE_AND_TRAVEL_FOR :và di chuyển trong {STRING} STR_TIMETABLE_AND_TRAVEL_FOR :{SPACE}và di chuyển trong {STRING}
STR_TIMETABLE_APPROX_TIME :{BLACK}Lịch trình này sẽ mất khoảng {STRING} để hoàn thành STR_TIMETABLE_APPROX_TIME :{BLACK}Lịch trình này sẽ mất khoảng {STRING} để hoàn thành
STR_TIMETABLE_TOTAL_TIME :{BLACK}Lịch trình này sẽ mất {STRING} để hoàn thành STR_TIMETABLE_TOTAL_TIME :{BLACK}Lịch trình này sẽ mất {STRING} để hoàn thành
@ -4738,12 +4793,14 @@ STR_TIMETABLE_START_SECONDS_QUERY :Số giây cho
STR_TIMETABLE_CHANGE_TIME :{BLACK}Đổi thời gian STR_TIMETABLE_CHANGE_TIME :{BLACK}Đổi thời gian
STR_TIMETABLE_WAIT_TIME_TOOLTIP :{BLACK}Thay đổi thời lượng của điểm lộ trình được phép sử dụng. Ctrl+Click đặt thời gian cho mọi lộ trình STR_TIMETABLE_WAIT_TIME_TOOLTIP :{BLACK}Thay đổi thời lượng của điểm lộ trình được phép sử dụng. Ctrl+Click đặt thời gian cho mọi lộ trình
STR_TIMETABLE_CHANGE_TIME_QUERY :Thay đổi thời gian
STR_TIMETABLE_CLEAR_TIME :{BLACK}Xoá thời gian STR_TIMETABLE_CLEAR_TIME :{BLACK}Xoá thời gian
STR_TIMETABLE_CLEAR_TIME_TOOLTIP :{BLACK}Xóa thời lượng áp dụng cho điểm lộ trình. Ctrl+Click xoá tất cả thời gian cho mọi lộ trình STR_TIMETABLE_CLEAR_TIME_TOOLTIP :{BLACK}Xóa thời lượng áp dụng cho điểm lộ trình. Ctrl+Click xoá tất cả thời gian cho mọi lộ trình
STR_TIMETABLE_CHANGE_SPEED :{BLACK}Thay Đổi Giới Hạn Tốc Độ STR_TIMETABLE_CHANGE_SPEED :{BLACK}Thay Đổi Giới Hạn Tốc Độ
STR_TIMETABLE_CHANGE_SPEED_TOOLTIP :{BLACK}Thay đổi tốc độ tối đa của lộ trình được chọn. Ctrl+Click đặt tốc độ cho mọi lộ trình STR_TIMETABLE_CHANGE_SPEED_TOOLTIP :{BLACK}Thay đổi tốc độ tối đa của lộ trình được chọn. Ctrl+Click đặt tốc độ cho mọi lộ trình
STR_TIMETABLE_CHANGE_SPEED_QUERY :Thay đổi giới hạn tốc độ
STR_TIMETABLE_CLEAR_SPEED :{BLACK}Xóa Giới Hạn Tốc Độ STR_TIMETABLE_CLEAR_SPEED :{BLACK}Xóa Giới Hạn Tốc Độ
STR_TIMETABLE_CLEAR_SPEED_TOOLTIP :{BLACK}Xóa tốc độ đối đa đối với lộ trình được chọn. Ctrl+Click xoá tốc độ cho mọi lộ trình STR_TIMETABLE_CLEAR_SPEED_TOOLTIP :{BLACK}Xóa tốc độ đối đa đối với lộ trình được chọn. Ctrl+Click xoá tốc độ cho mọi lộ trình
@ -4907,7 +4964,7 @@ STR_GAME_SAVELOAD_NOT_AVAILABLE :<không có s
STR_WARNING_LOADGAME_REMOVED_TRAMS :{WHITE}Lưu ván chơi sẽ không có xe điện. Những công trình cho xe điện sẽ bị xoá bỏ STR_WARNING_LOADGAME_REMOVED_TRAMS :{WHITE}Lưu ván chơi sẽ không có xe điện. Những công trình cho xe điện sẽ bị xoá bỏ
# Map generation messages # Map generation messages
STR_ERROR_COULD_NOT_CREATE_TOWN :{WHITE}Sinh bản đồ bị ngưng...{}... không có nơi đặt đô thị STR_ERROR_COULD_NOT_CREATE_TOWN :{WHITE}Sinh bản đồ bị ngưng...{}{}... không có nơi đặt đô thị
STR_ERROR_NO_TOWN_IN_SCENARIO :{WHITE}... không có đô thị nào ở màn chơi kịch bản này STR_ERROR_NO_TOWN_IN_SCENARIO :{WHITE}... không có đô thị nào ở màn chơi kịch bản này
STR_ERROR_PNGMAP :{WHITE}Không thể nạp nền từ file PNG... STR_ERROR_PNGMAP :{WHITE}Không thể nạp nền từ file PNG...
@ -4946,6 +5003,7 @@ STR_ERROR_FLAT_LAND_REQUIRED :{WHITE}Yêu c
STR_ERROR_LAND_SLOPED_IN_WRONG_DIRECTION :{WHITE}Tạo dốc bị sai hướng STR_ERROR_LAND_SLOPED_IN_WRONG_DIRECTION :{WHITE}Tạo dốc bị sai hướng
STR_ERROR_CAN_T_DO_THIS :{WHITE}Không làm thế này được... STR_ERROR_CAN_T_DO_THIS :{WHITE}Không làm thế này được...
STR_ERROR_BUILDING_MUST_BE_DEMOLISHED :{WHITE}Cần giải toả nhà cửa trước STR_ERROR_BUILDING_MUST_BE_DEMOLISHED :{WHITE}Cần giải toả nhà cửa trước
STR_ERROR_BUILDING_IS_PROTECTED :{WHITE}... công trình được bảo vệ
STR_ERROR_CAN_T_CLEAR_THIS_AREA :{WHITE}Không thể dọn dẹp khu vực này... STR_ERROR_CAN_T_CLEAR_THIS_AREA :{WHITE}Không thể dọn dẹp khu vực này...
STR_ERROR_SITE_UNSUITABLE :{WHITE}... điểm không phù hợp STR_ERROR_SITE_UNSUITABLE :{WHITE}... điểm không phù hợp
STR_ERROR_ALREADY_BUILT :{WHITE}... đã xây rồi STR_ERROR_ALREADY_BUILT :{WHITE}... đã xây rồi
@ -4998,7 +5056,7 @@ STR_ERROR_TOO_CLOSE_TO_ANOTHER_TOWN :{WHITE}... quá
STR_ERROR_TOO_MANY_TOWNS :{WHITE}... quá nhiều đô thị STR_ERROR_TOO_MANY_TOWNS :{WHITE}... quá nhiều đô thị
STR_ERROR_NO_SPACE_FOR_TOWN :{WHITE}... không còn khoảng trống nào trên bản đồ STR_ERROR_NO_SPACE_FOR_TOWN :{WHITE}... không còn khoảng trống nào trên bản đồ
STR_ERROR_ROAD_WORKS_IN_PROGRESS :{WHITE}Xây dựng cầu đường đang tiến hành STR_ERROR_ROAD_WORKS_IN_PROGRESS :{WHITE}Xây dựng cầu đường đang tiến hành
STR_ERROR_TOWN_CAN_T_DELETE :{WHITE}Không thể xoá đo thị này...{}Có một ga, bến hoặc xưởng thuộc đô thị hoặc là 1 ô đất của đô thị không thể xoá được. STR_ERROR_TOWN_CAN_T_DELETE :{WHITE}Không thể xoá đo thị này...{}{}Có một ga, bến hoặc xưởng thuộc đô thị hoặc là 1 ô đất của đô thị không thể xoá được.
STR_ERROR_STATUE_NO_SUITABLE_PLACE :{WHITE}... không có nơi nào hợp lý để dựng tượng đài ở trung tâm đô thị này STR_ERROR_STATUE_NO_SUITABLE_PLACE :{WHITE}... không có nơi nào hợp lý để dựng tượng đài ở trung tâm đô thị này
STR_ERROR_CAN_T_BUILD_HOUSE :{WHITE}Không thể xây dựng nhà... STR_ERROR_CAN_T_BUILD_HOUSE :{WHITE}Không thể xây dựng nhà...
@ -5825,6 +5883,7 @@ STR_CURRENCY_SHORT_GIGA :{NBSP}tỷ
STR_CURRENCY_SHORT_TERA :{NBSP}ktỷ STR_CURRENCY_SHORT_TERA :{NBSP}ktỷ
STR_JUST_CARGO :{CARGO_LONG} STR_JUST_CARGO :{CARGO_LONG}
STR_JUST_LEFT_ARROW :{LEFT_ARROW}
STR_JUST_RIGHT_ARROW :{RIGHT_ARROW} STR_JUST_RIGHT_ARROW :{RIGHT_ARROW}
STR_JUST_CHECKMARK :{CHECKMARK} STR_JUST_CHECKMARK :{CHECKMARK}
STR_JUST_COMMA :{COMMA} STR_JUST_COMMA :{COMMA}
@ -5864,3 +5923,11 @@ STR_SHIP :{BLACK}{SHIP}
STR_TOOLBAR_RAILTYPE_VELOCITY :{STRING} ({VELOCITY}) STR_TOOLBAR_RAILTYPE_VELOCITY :{STRING} ({VELOCITY})
STR_BADGE_NAME_LIST :{STRING}: {GOLD}{STRING} STR_BADGE_NAME_LIST :{STRING}: {GOLD}{STRING}
STR_BADGE_CONFIG_MENU_TOOLTIP :Mở thiết lập phù hiệu
STR_BADGE_CONFIG_RESET :Thiêt lập lại
STR_BADGE_CONFIG_ICONS :{WHITE}Ảnh phù hiệu
STR_BADGE_CONFIG_FILTERS :{WHITE}Bộ lọc phù hiệu
STR_BADGE_CONFIG_PREVIEW :Ảnh xem trước
STR_BADGE_CONFIG_NAME :Tên
STR_BADGE_FILTER_ANY_LABEL :Bất cứ {STRING} nào
STR_BADGE_FILTER_IS_LABEL :{STRING} là {STRING}

View File

@ -86,7 +86,7 @@ void LinkGraphOverlay::RebuildCache()
StationLinkMap &seen_links = this->cached_links[from]; StationLinkMap &seen_links = this->cached_links[from];
uint supply = 0; uint supply = 0;
for (CargoType cargo : SetCargoBitIterator(this->cargo_mask)) { for (CargoType cargo : this->cargo_mask) {
if (!CargoSpec::Get(cargo)->IsValid()) continue; if (!CargoSpec::Get(cargo)->IsValid()) continue;
if (!LinkGraph::IsValidID(sta->goods[cargo].link_graph)) continue; if (!LinkGraph::IsValidID(sta->goods[cargo].link_graph)) continue;
const LinkGraph &lg = *LinkGraph::Get(sta->goods[cargo].link_graph); const LinkGraph &lg = *LinkGraph::Get(sta->goods[cargo].link_graph);
@ -212,7 +212,7 @@ inline bool LinkGraphOverlay::IsLinkVisible(Point pta, Point ptb, const DrawPixe
*/ */
void LinkGraphOverlay::AddLinks(const Station *from, const Station *to) void LinkGraphOverlay::AddLinks(const Station *from, const Station *to)
{ {
for (CargoType cargo : SetCargoBitIterator(this->cargo_mask)) { for (CargoType cargo : this->cargo_mask) {
if (!CargoSpec::Get(cargo)->IsValid()) continue; if (!CargoSpec::Get(cargo)->IsValid()) continue;
const GoodsEntry &ge = from->goods[cargo]; const GoodsEntry &ge = from->goods[cargo];
if (!LinkGraph::IsValidID(ge.link_graph) || if (!LinkGraph::IsValidID(ge.link_graph) ||
@ -566,7 +566,7 @@ void LinkGraphLegendWindow::SetOverlay(std::shared_ptr<LinkGraphOverlay> overlay
} }
CargoTypes cargoes = this->overlay->GetCargoMask(); CargoTypes cargoes = this->overlay->GetCargoMask();
for (uint c = 0; c < this->num_cargo; c++) { for (uint c = 0; c < this->num_cargo; c++) {
this->SetWidgetLoweredState(WID_LGL_CARGO_FIRST + c, HasBit(cargoes, _sorted_cargo_specs[c]->Index())); this->SetWidgetLoweredState(WID_LGL_CARGO_FIRST + c, cargoes.Test(_sorted_cargo_specs[c]->Index()));
} }
} }
@ -669,10 +669,10 @@ void LinkGraphLegendWindow::UpdateOverlayCompanies()
*/ */
void LinkGraphLegendWindow::UpdateOverlayCargoes() void LinkGraphLegendWindow::UpdateOverlayCargoes()
{ {
CargoTypes mask = 0; CargoTypes mask{};
for (uint c = 0; c < num_cargo; c++) { for (uint c = 0; c < num_cargo; c++) {
if (!this->IsWidgetLowered(WID_LGL_CARGO_FIRST + c)) continue; if (!this->IsWidgetLowered(WID_LGL_CARGO_FIRST + c)) continue;
SetBit(mask, _sorted_cargo_specs[c]->Index()); mask.Set(_sorted_cargo_specs[c]->Index());
} }
this->overlay->SetCargoMask(mask); this->overlay->SetCargoMask(mask);
} }

View File

@ -72,7 +72,7 @@ bool LinkRefresher::HandleRefit(CargoType refit_cargo)
bool any_refit = false; bool any_refit = false;
for (Vehicle *v = this->vehicle; v != nullptr; v = v->Next()) { for (Vehicle *v = this->vehicle; v != nullptr; v = v->Next()) {
const Engine *e = Engine::Get(v->engine_type); const Engine *e = Engine::Get(v->engine_type);
if (!HasBit(e->info.refit_mask, this->cargo)) { if (!e->info.refit_mask.Test(this->cargo)) {
++refit_it; ++refit_it;
continue; continue;
} }
@ -188,7 +188,7 @@ void LinkRefresher::RefreshStats(VehicleOrderID cur, VehicleOrderID next)
Station *st = Station::GetIfValid(orders[cur].GetDestination().ToStationID()); Station *st = Station::GetIfValid(orders[cur].GetDestination().ToStationID());
if (st != nullptr && next_station != StationID::Invalid() && next_station != st->index) { if (st != nullptr && next_station != StationID::Invalid() && next_station != st->index) {
Station *st_to = Station::Get(next_station); Station *st_to = Station::Get(next_station);
for (CargoType cargo = 0; cargo < NUM_CARGO; ++cargo) { for (CargoType cargo{}; cargo < NUM_CARGO; ++cargo) {
/* Refresh the link and give it a minimum capacity. */ /* Refresh the link and give it a minimum capacity. */
uint cargo_quantity = this->capacities[cargo]; uint cargo_quantity = this->capacities[cargo];
@ -260,7 +260,7 @@ void LinkRefresher::RefreshLinks(VehicleOrderID cur, VehicleOrderID next, Refres
} else if (!flags.Test(RefreshFlag::InAutorefit)) { } else if (!flags.Test(RefreshFlag::InAutorefit)) {
flags.Set(RefreshFlag::InAutorefit); flags.Set(RefreshFlag::InAutorefit);
LinkRefresher backup(*this); LinkRefresher backup(*this);
for (CargoType cargo = 0; cargo != NUM_CARGO; ++cargo) { for (CargoType cargo{}; cargo < NUM_CARGO; ++cargo) {
if (CargoSpec::Get(cargo)->IsValid() && this->HandleRefit(cargo)) { if (CargoSpec::Get(cargo)->IsValid() && this->HandleRefit(cargo)) {
this->RefreshLinks(cur, next, flags, num_hops); this->RefreshLinks(cur, next, flags, num_hops);
*this = backup; *this = backup;

View File

@ -221,14 +221,14 @@ struct MainWindow : Window
NWidgetViewport *nvp = this->GetWidget<NWidgetViewport>(WID_M_VIEWPORT); NWidgetViewport *nvp = this->GetWidget<NWidgetViewport>(WID_M_VIEWPORT);
nvp->InitializeViewport(this, TileXY(32, 32), ScaleZoomGUI(ZoomLevel::Viewport)); nvp->InitializeViewport(this, TileXY(32, 32), ScaleZoomGUI(ZoomLevel::Viewport));
this->viewport->overlay = std::make_shared<LinkGraphOverlay>(this, WID_M_VIEWPORT, 0, CompanyMask{}, 2); this->viewport->overlay = std::make_shared<LinkGraphOverlay>(this, WID_M_VIEWPORT, CargoTypes{}, CompanyMask{}, 2);
this->refresh_timeout.Reset(); this->refresh_timeout.Reset();
} }
/** Refresh the link-graph overlay. */ /** Refresh the link-graph overlay. */
void RefreshLinkGraph() void RefreshLinkGraph()
{ {
if (this->viewport->overlay->GetCargoMask() == 0 || if (this->viewport->overlay->GetCargoMask().None() ||
this->viewport->overlay->GetCompanyMask().None()) { this->viewport->overlay->GetCompanyMask().None()) {
return; return;
} }

View File

@ -310,10 +310,10 @@ EngineID GetNewEngineID(const GRFFile *file, VehicleType type, uint16_t internal
*/ */
CargoTypes TranslateRefitMask(uint32_t refit_mask) CargoTypes TranslateRefitMask(uint32_t refit_mask)
{ {
CargoTypes result = 0; CargoTypes result{};
for (uint8_t bit : SetBitIterator(refit_mask)) { for (uint8_t bit : SetBitIterator(refit_mask)) {
CargoType cargo = GetCargoTranslation(bit, _cur_gps.grffile, true); CargoType cargo = GetCargoTranslation(bit, _cur_gps.grffile, true);
if (IsValidCargoType(cargo)) SetBit(result, cargo); if (IsValidCargoType(cargo)) result.Set(cargo);
} }
return result; return result;
} }
@ -641,9 +641,9 @@ static CargoLabel GetActiveCargoLabel(const std::variant<CargoLabel, MixedCargoT
*/ */
static void CalculateRefitMasks() static void CalculateRefitMasks()
{ {
CargoTypes original_known_cargoes = 0; CargoTypes original_known_cargoes{};
for (CargoType cargo_type = 0; cargo_type != NUM_CARGO; ++cargo_type) { for (CargoType cargo_type{}; cargo_type != NUM_CARGO; ++cargo_type) {
if (IsDefaultCargo(cargo_type)) SetBit(original_known_cargoes, cargo_type); if (IsDefaultCargo(cargo_type)) original_known_cargoes.Set(cargo_type);
} }
for (Engine *e : Engine::Iterate()) { for (Engine *e : Engine::Iterate()) {
@ -738,13 +738,13 @@ static void CalculateRefitMasks()
} }
_gted[engine].UpdateRefittability(_gted[engine].cargo_allowed.Any()); _gted[engine].UpdateRefittability(_gted[engine].cargo_allowed.Any());
if (IsValidCargoType(ei->cargo_type)) ClrBit(_gted[engine].ctt_exclude_mask, ei->cargo_type); if (IsValidCargoType(ei->cargo_type)) _gted[engine].ctt_exclude_mask.Reset(ei->cargo_type);
} }
/* Compute refittability */ /* Compute refittability */
{ {
CargoTypes mask = 0; CargoTypes mask{};
CargoTypes not_mask = 0; CargoTypes not_mask{};
CargoTypes xor_mask = ei->refit_mask; CargoTypes xor_mask = ei->refit_mask;
/* If the original masks set by the grf are zero, the vehicle shall only carry the default cargo. /* If the original masks set by the grf are zero, the vehicle shall only carry the default cargo.
@ -754,16 +754,17 @@ static void CalculateRefitMasks()
if (_gted[engine].cargo_allowed.Any()) { if (_gted[engine].cargo_allowed.Any()) {
/* Build up the list of cargo types from the set cargo classes. */ /* Build up the list of cargo types from the set cargo classes. */
for (const CargoSpec *cs : CargoSpec::Iterate()) { for (const CargoSpec *cs : CargoSpec::Iterate()) {
if (cs->classes.Any(_gted[engine].cargo_allowed) && cs->classes.All(_gted[engine].cargo_allowed_required)) SetBit(mask, cs->Index()); if (cs->classes.Any(_gted[engine].cargo_allowed) && cs->classes.All(_gted[engine].cargo_allowed_required)) mask.Set(cs->Index());
if (cs->classes.Any(_gted[engine].cargo_disallowed)) SetBit(not_mask, cs->Index()); if (cs->classes.Any(_gted[engine].cargo_disallowed)) not_mask.Set(cs->Index());
} }
} }
ei->refit_mask = ((mask & ~not_mask) ^ xor_mask) & _cargo_mask; CargoTypes invalid_mask = CargoTypes{_cargo_mask}.Flip();
ei->refit_mask = mask.Reset(not_mask).Flip(xor_mask).Reset(invalid_mask);
/* Apply explicit refit includes/excludes. */ /* Apply explicit refit includes/excludes. */
ei->refit_mask |= _gted[engine].ctt_include_mask; ei->refit_mask.Set(_gted[engine].ctt_include_mask);
ei->refit_mask &= ~_gted[engine].ctt_exclude_mask; ei->refit_mask.Reset(_gted[engine].ctt_exclude_mask);
/* Custom refit mask callback. */ /* Custom refit mask callback. */
const GRFFile *file = _gted[e->index].defaultcargo_grf; const GRFFile *file = _gted[e->index].defaultcargo_grf;
@ -776,8 +777,8 @@ static void CalculateRefitMasks()
case CALLBACK_FAILED: case CALLBACK_FAILED:
case 0: case 0:
break; // Do nothing. break; // Do nothing.
case 1: SetBit(ei->refit_mask, cs->Index()); break; case 1: ei->refit_mask.Set(cs->Index()); break;
case 2: ClrBit(ei->refit_mask, cs->Index()); break; case 2: ei->refit_mask.Reset(cs->Index()); break;
default: ErrorUnknownCallbackResult(file->grfid, CBID_VEHICLE_CUSTOM_REFIT, callback); default: ErrorUnknownCallbackResult(file->grfid, CBID_VEHICLE_CUSTOM_REFIT, callback);
} }
@ -786,24 +787,24 @@ static void CalculateRefitMasks()
} }
/* Clear invalid cargoslots (from default vehicles or pre-NewCargo GRFs) */ /* Clear invalid cargoslots (from default vehicles or pre-NewCargo GRFs) */
if (IsValidCargoType(ei->cargo_type) && !HasBit(_cargo_mask, ei->cargo_type)) ei->cargo_type = INVALID_CARGO; if (IsValidCargoType(ei->cargo_type) && !_cargo_mask.Test(ei->cargo_type)) ei->cargo_type = INVALID_CARGO;
/* Ensure that the vehicle is either not refittable, or that the default cargo is one of the refittable cargoes. /* Ensure that the vehicle is either not refittable, or that the default cargo is one of the refittable cargoes.
* Note: Vehicles refittable to no cargo are handle differently to vehicle refittable to a single cargo. The latter might have subtypes. */ * Note: Vehicles refittable to no cargo are handle differently to vehicle refittable to a single cargo. The latter might have subtypes. */
if (!only_defaultcargo && (e->type != VEH_SHIP || e->u.ship.old_refittable) && IsValidCargoType(ei->cargo_type) && !HasBit(ei->refit_mask, ei->cargo_type)) { if (!only_defaultcargo && (e->type != VEH_SHIP || e->u.ship.old_refittable) && IsValidCargoType(ei->cargo_type) && !ei->refit_mask.Test(ei->cargo_type)) {
ei->cargo_type = INVALID_CARGO; ei->cargo_type = INVALID_CARGO;
} }
/* Check if this engine's cargo type is valid. If not, set to the first refittable /* Check if this engine's cargo type is valid. If not, set to the first refittable
* cargo type. Finally disable the vehicle, if there is still no cargo. */ * cargo type. Finally disable the vehicle, if there is still no cargo. */
if (!IsValidCargoType(ei->cargo_type) && ei->refit_mask != 0) { if (!IsValidCargoType(ei->cargo_type) && ei->refit_mask.Any()) {
/* Figure out which CTT to use for the default cargo, if it is 'first refittable'. */ /* Figure out which CTT to use for the default cargo, if it is 'first refittable'. */
const GRFFile *file = _gted[engine].defaultcargo_grf; const GRFFile *file = _gted[engine].defaultcargo_grf;
if (file == nullptr) file = e->GetGRF(); if (file == nullptr) file = e->GetGRF();
if (file != nullptr && file->grf_version >= 8 && !file->cargo_list.empty()) { if (file != nullptr && file->grf_version >= 8 && !file->cargo_list.empty()) {
/* Use first refittable cargo from cargo translation table */ /* Use first refittable cargo from cargo translation table */
uint8_t best_local_slot = UINT8_MAX; uint8_t best_local_slot = UINT8_MAX;
for (CargoType cargo_type : SetCargoBitIterator(ei->refit_mask)) { for (CargoType cargo_type : ei->refit_mask) {
uint8_t local_slot = file->cargo_map[cargo_type]; uint8_t local_slot = file->cargo_map[cargo_type];
if (local_slot < best_local_slot) { if (local_slot < best_local_slot) {
best_local_slot = local_slot; best_local_slot = local_slot;
@ -814,7 +815,7 @@ static void CalculateRefitMasks()
if (!IsValidCargoType(ei->cargo_type)) { if (!IsValidCargoType(ei->cargo_type)) {
/* Use first refittable cargo slot */ /* Use first refittable cargo slot */
ei->cargo_type = (CargoType)FindFirstBit(ei->refit_mask); ei->cargo_type = *ei->refit_mask.begin();
} }
} }
if (!IsValidCargoType(ei->cargo_type) && e->type == VEH_TRAIN && e->u.rail.railveh_type != RAILVEH_WAGON && e->u.rail.capacity == 0) { if (!IsValidCargoType(ei->cargo_type) && e->type == VEH_TRAIN && e->u.rail.railveh_type != RAILVEH_WAGON && e->u.rail.capacity == 0) {
@ -822,14 +823,14 @@ static void CalculateRefitMasks()
* Fallback to the first available instead, if the cargo type has not been changed (as indicated by * Fallback to the first available instead, if the cargo type has not been changed (as indicated by
* cargo_label not being CT_INVALID). */ * cargo_label not being CT_INVALID). */
if (GetActiveCargoLabel(ei->cargo_label) != CT_INVALID) { if (GetActiveCargoLabel(ei->cargo_label) != CT_INVALID) {
ei->cargo_type = static_cast<CargoType>(FindFirstBit(_standard_cargo_mask)); ei->cargo_type = *_standard_cargo_mask.begin();
} }
} }
if (!IsValidCargoType(ei->cargo_type)) ei->climates = {}; if (!IsValidCargoType(ei->cargo_type)) ei->climates = {};
/* Clear refit_mask for not refittable ships */ /* Clear refit_mask for not refittable ships */
if (e->type == VEH_SHIP && !e->u.ship.old_refittable) { if (e->type == VEH_SHIP && !e->u.ship.old_refittable) {
ei->refit_mask = 0; ei->refit_mask.Reset();
} }
} }
} }

View File

@ -134,7 +134,7 @@ struct GRFFile {
std::vector<GRFLabel> labels{}; ///< List of labels std::vector<GRFLabel> labels{}; ///< List of labels
std::vector<CargoLabel> cargo_list{}; ///< Cargo translation table (local ID -> label) std::vector<CargoLabel> cargo_list{}; ///< Cargo translation table (local ID -> label)
std::array<uint8_t, NUM_CARGO> cargo_map{}; ///< Inverse cargo translation table (CargoType -> local ID) TypedIndexContainer<std::array<uint8_t, NUM_CARGO>, CargoType> cargo_map{}; ///< Inverse cargo translation table (CargoType -> local ID)
std::vector<BadgeID> badge_list{}; ///< Badge translation table (local index -> global index) std::vector<BadgeID> badge_list{}; ///< Badge translation table (local index -> global index)
std::unordered_map<uint16_t, BadgeID> badge_map{}; std::unordered_map<uint16_t, BadgeID> badge_map{};

View File

@ -153,10 +153,10 @@ static ChangeInfoResult AircraftVehicleChangeInfo(uint first, uint last, int pro
_gted[e->index].UpdateRefittability(prop == 0x1D && count != 0); _gted[e->index].UpdateRefittability(prop == 0x1D && count != 0);
if (prop == 0x1D) _gted[e->index].defaultcargo_grf = _cur_gps.grffile; if (prop == 0x1D) _gted[e->index].defaultcargo_grf = _cur_gps.grffile;
CargoTypes &ctt = prop == 0x1D ? _gted[e->index].ctt_include_mask : _gted[e->index].ctt_exclude_mask; CargoTypes &ctt = prop == 0x1D ? _gted[e->index].ctt_include_mask : _gted[e->index].ctt_exclude_mask;
ctt = 0; ctt.Reset();
while (count--) { while (count--) {
CargoType ctype = GetCargoTranslation(buf.ReadByte(), _cur_gps.grffile); CargoType ctype = GetCargoTranslation(buf.ReadByte(), _cur_gps.grffile);
if (IsValidCargoType(ctype)) SetBit(ctt, ctype); if (IsValidCargoType(ctype)) ctt.Set(ctype);
} }
break; break;
} }

View File

@ -34,16 +34,16 @@ static ChangeInfoResult CargoReserveInfo(uint first, uint last, int prop, ByteRe
} }
for (uint id = first; id < last; ++id) { for (uint id = first; id < last; ++id) {
CargoSpec *cs = CargoSpec::Get(id); CargoSpec *cs = CargoSpec::Get(static_cast<CargoType>(id));
switch (prop) { switch (prop) {
case 0x08: // Bit number of cargo case 0x08: // Bit number of cargo
cs->bitnum = buf.ReadByte(); cs->bitnum = buf.ReadByte();
if (cs->IsValid()) { if (cs->IsValid()) {
cs->grffile = _cur_gps.grffile; cs->grffile = _cur_gps.grffile;
SetBit(_cargo_mask, id); _cargo_mask.Set(cs->Index());
} else { } else {
ClrBit(_cargo_mask, id); _cargo_mask.Reset(cs->Index());
} }
BuildCargoLabelMap(); BuildCargoLabelMap();
break; break;

View File

@ -305,7 +305,7 @@ static ChangeInfoResult TownHouseChangeInfo(uint first, uint last, int prop, Byt
uint8_t count = buf.ReadByte(); uint8_t count = buf.ReadByte();
for (uint8_t j = 0; j < count; j++) { for (uint8_t j = 0; j < count; j++) {
CargoType cargo = GetCargoTranslation(buf.ReadByte(), _cur_gps.grffile); CargoType cargo = GetCargoTranslation(buf.ReadByte(), _cur_gps.grffile);
if (IsValidCargoType(cargo)) SetBit(housespec->watched_cargoes, cargo); if (IsValidCargoType(cargo)) housespec->watched_cargoes.Set(cargo);
} }
break; break;
} }

View File

@ -194,10 +194,10 @@ static ChangeInfoResult RoadVehicleChangeInfo(uint first, uint last, int prop, B
_gted[e->index].UpdateRefittability(prop == 0x24 && count != 0); _gted[e->index].UpdateRefittability(prop == 0x24 && count != 0);
if (prop == 0x24) _gted[e->index].defaultcargo_grf = _cur_gps.grffile; if (prop == 0x24) _gted[e->index].defaultcargo_grf = _cur_gps.grffile;
CargoTypes &ctt = prop == 0x24 ? _gted[e->index].ctt_include_mask : _gted[e->index].ctt_exclude_mask; CargoTypes &ctt = prop == 0x24 ? _gted[e->index].ctt_include_mask : _gted[e->index].ctt_exclude_mask;
ctt = 0; ctt.Reset();
while (count--) { while (count--) {
CargoType ctype = GetCargoTranslation(buf.ReadByte(), _cur_gps.grffile); CargoType ctype = GetCargoTranslation(buf.ReadByte(), _cur_gps.grffile);
if (IsValidCargoType(ctype)) SetBit(ctt, ctype); if (IsValidCargoType(ctype)) ctt.Set(ctype);
} }
break; break;
} }

View File

@ -173,10 +173,10 @@ static ChangeInfoResult ShipVehicleChangeInfo(uint first, uint last, int prop, B
_gted[e->index].UpdateRefittability(prop == 0x1E && count != 0); _gted[e->index].UpdateRefittability(prop == 0x1E && count != 0);
if (prop == 0x1E) _gted[e->index].defaultcargo_grf = _cur_gps.grffile; if (prop == 0x1E) _gted[e->index].defaultcargo_grf = _cur_gps.grffile;
CargoTypes &ctt = prop == 0x1E ? _gted[e->index].ctt_include_mask : _gted[e->index].ctt_exclude_mask; CargoTypes &ctt = prop == 0x1E ? _gted[e->index].ctt_include_mask : _gted[e->index].ctt_exclude_mask;
ctt = 0; ctt.Reset();
while (count--) { while (count--) {
CargoType ctype = GetCargoTranslation(buf.ReadByte(), _cur_gps.grffile); CargoType ctype = GetCargoTranslation(buf.ReadByte(), _cur_gps.grffile);
if (IsValidCargoType(ctype)) SetBit(ctt, ctype); if (IsValidCargoType(ctype)) ctt.Set(ctype);
} }
break; break;
} }

View File

@ -292,10 +292,10 @@ ChangeInfoResult RailVehicleChangeInfo(uint first, uint last, int prop, ByteRead
_gted[e->index].UpdateRefittability(prop == 0x2C && count != 0); _gted[e->index].UpdateRefittability(prop == 0x2C && count != 0);
if (prop == 0x2C) _gted[e->index].defaultcargo_grf = _cur_gps.grffile; if (prop == 0x2C) _gted[e->index].defaultcargo_grf = _cur_gps.grffile;
CargoTypes &ctt = prop == 0x2C ? _gted[e->index].ctt_include_mask : _gted[e->index].ctt_exclude_mask; CargoTypes &ctt = prop == 0x2C ? _gted[e->index].ctt_include_mask : _gted[e->index].ctt_exclude_mask;
ctt = 0; ctt.Reset();
while (count--) { while (count--) {
CargoType ctype = GetCargoTranslation(buf.ReadByte(), _cur_gps.grffile); CargoType ctype = GetCargoTranslation(buf.ReadByte(), _cur_gps.grffile);
if (IsValidCargoType(ctype)) SetBit(ctt, ctype); if (IsValidCargoType(ctype)) ctt.Set(ctype);
} }
break; break;
} }

View File

@ -379,7 +379,7 @@ static void CargoMapSpriteGroup(ByteReader &buf, uint8_t idcount)
continue; continue;
} }
CargoSpec *cs = CargoSpec::Get(cargo_type); CargoSpec *cs = CargoSpec::Get(static_cast<CargoType>(cargo_type));
cs->grffile = _cur_gps.grffile; cs->grffile = _cur_gps.grffile;
cs->group = _cur_gps.spritegroups[groupid]; cs->group = _cur_gps.spritegroups[groupid];
} }

View File

@ -49,7 +49,7 @@ struct AnimationBase {
* @param random_animation Whether to pass random bits to the "next frame" callback. * @param random_animation Whether to pass random bits to the "next frame" callback.
* @param extra_data Custom extra callback data. * @param extra_data Custom extra callback data.
*/ */
static void AnimateTile(const Tspec *spec, Tobj *obj, TileIndex tile, bool random_animation, Textra extra_data = 0) static void AnimateTile(const Tspec *spec, Tobj *obj, TileIndex tile, bool random_animation, Textra extra_data = {})
{ {
assert(spec != nullptr); assert(spec != nullptr);
@ -128,7 +128,7 @@ struct AnimationBase {
* @param trigger What triggered this update? To be passed as parameter to the NewGRF. * @param trigger What triggered this update? To be passed as parameter to the NewGRF.
* @param extra_data Custom extra data for callback processing. * @param extra_data Custom extra data for callback processing.
*/ */
static void ChangeAnimationFrame(CallbackID cb, const Tspec *spec, Tobj *obj, TileIndex tile, uint32_t random_bits, uint32_t trigger, Textra extra_data = 0) static void ChangeAnimationFrame(CallbackID cb, const Tspec *spec, Tobj *obj, TileIndex tile, uint32_t random_bits, uint32_t trigger, Textra extra_data = {})
{ {
uint16_t callback = GetCallback(cb, random_bits, trigger, spec, obj, tile, extra_data); uint16_t callback = GetCallback(cb, random_bits, trigger, spec, obj, tile, extra_data);
if (callback == CALLBACK_FAILED) return; if (callback == CALLBACK_FAILED) return;

View File

@ -15,6 +15,7 @@
#include "sprite.h" #include "sprite.h"
#include "core/alloc_type.hpp" #include "core/alloc_type.hpp"
#include "core/convertible_through_base.hpp"
#include "command_type.h" #include "command_type.h"
#include "direction_type.h" #include "direction_type.h"
#include "company_type.h" #include "company_type.h"
@ -454,9 +455,9 @@ struct VariableGRFFileProps : GRFFilePropsBase {
* Sprite groups indexed by CargoType. * Sprite groups indexed by CargoType.
*/ */
struct CargoGRFFileProps : VariableGRFFileProps<CargoType> { struct CargoGRFFileProps : VariableGRFFileProps<CargoType> {
static constexpr CargoType SG_DEFAULT = NUM_CARGO; ///< Default type used when no more-specific cargo matches. static constexpr CargoType SG_DEFAULT{NUM_CARGO}; ///< Default type used when no more-specific cargo matches.
static constexpr CargoType SG_PURCHASE = NUM_CARGO + 1; ///< Used in purchase lists before an item exists. static constexpr CargoType SG_PURCHASE{NUM_CARGO + 1}; ///< Used in purchase lists before an item exists.
static constexpr CargoType SG_DEFAULT_NA = NUM_CARGO + 2; ///< Used only by stations and roads when no more-specific cargo matches. static constexpr CargoType SG_DEFAULT_NA{NUM_CARGO + 2}; ///< Used only by stations and roads when no more-specific cargo matches.
}; };
/** /**

View File

@ -426,7 +426,7 @@ struct NewGRFInspectWindow : Window {
{ {
switch (nip.type) { switch (nip.type) {
case NIT_INT: return GetString(STR_JUST_INT, value); case NIT_INT: return GetString(STR_JUST_INT, value);
case NIT_CARGO: return GetString(IsValidCargoType(value) ? CargoSpec::Get(value)->name : STR_QUANTITY_N_A); case NIT_CARGO: return GetString(IsValidCargoType(static_cast<CargoType>(value)) ? CargoSpec::Get(static_cast<CargoType>(value))->name : STR_QUANTITY_N_A);
default: NOT_REACHED(); default: NOT_REACHED();
} }
} }

View File

@ -435,7 +435,7 @@ static uint32_t VehicleGetVariable(Vehicle *v, const VehicleScopeResolver *objec
case 0x42: { // Consist cargo information case 0x42: { // Consist cargo information
if (!HasBit(v->grf_cache.cache_valid, NCVV_CONSIST_CARGO_INFORMATION)) { if (!HasBit(v->grf_cache.cache_valid, NCVV_CONSIST_CARGO_INFORMATION)) {
std::array<uint8_t, NUM_CARGO> common_cargoes{}; TypedIndexContainer<std::array<uint8_t, NUM_CARGO>, CargoType> common_cargoes{};
uint8_t cargo_classes = 0; uint8_t cargo_classes = 0;
uint8_t user_def_data = 0; uint8_t user_def_data = 0;
@ -470,12 +470,12 @@ static uint32_t VehicleGetVariable(Vehicle *v, const VehicleScopeResolver *objec
/* Note: We have to store the untranslated cargotype in the cache as the cache can be read by different NewGRFs, /* Note: We have to store the untranslated cargotype in the cache as the cache can be read by different NewGRFs,
* which will need different translations */ * which will need different translations */
v->grf_cache.consist_cargo_information = cargo_classes | (common_cargo_type << 8) | (common_subtype << 16) | (user_def_data << 24); v->grf_cache.consist_cargo_information = cargo_classes | (common_cargo_type.base() << 8) | (common_subtype << 16) | (user_def_data << 24);
SetBit(v->grf_cache.cache_valid, NCVV_CONSIST_CARGO_INFORMATION); SetBit(v->grf_cache.cache_valid, NCVV_CONSIST_CARGO_INFORMATION);
} }
/* The cargo translation is specific to the accessing GRF, and thus cannot be cached. */ /* The cargo translation is specific to the accessing GRF, and thus cannot be cached. */
CargoType common_cargo_type = (v->grf_cache.consist_cargo_information >> 8) & 0xFF; CargoType common_cargo_type = static_cast<CargoType>(GB(v->grf_cache.consist_cargo_information, 8, 8));
/* Note: /* Note:
* - Unlike everywhere else the cargo translation table is only used since grf version 8, not 7. * - Unlike everywhere else the cargo translation table is only used since grf version 8, not 7.
@ -822,7 +822,7 @@ static uint32_t VehicleGetVariable(Vehicle *v, const VehicleScopeResolver *objec
case 0x36: return v->subspeed; case 0x36: return v->subspeed;
case 0x37: return v->acceleration; case 0x37: return v->acceleration;
case 0x38: break; // not implemented case 0x38: break; // not implemented
case 0x39: return v->cargo_type; case 0x39: return v->cargo_type.base();
case 0x3A: return v->cargo_cap; case 0x3A: return v->cargo_cap;
case 0x3B: return GB(v->cargo_cap, 8, 8); case 0x3B: return GB(v->cargo_cap, 8, 8);
case 0x3C: return ClampTo<uint16_t>(v->cargo.StoredCount()); case 0x3C: return ClampTo<uint16_t>(v->cargo.StoredCount());

View File

@ -36,7 +36,7 @@ struct GenericScopeResolver : public ScopeResolver {
* @param ai_callback Callback comes from the AI. * @param ai_callback Callback comes from the AI.
*/ */
GenericScopeResolver(ResolverObject &ro, bool ai_callback) GenericScopeResolver(ResolverObject &ro, bool ai_callback)
: ScopeResolver(ro), cargo_type(0), default_selection(0), src_industry(0), dst_industry(0), distance(0), : ScopeResolver(ro), cargo_type(INVALID_CARGO), default_selection(0), src_industry(0), dst_industry(0), distance(0),
event(), count(0), station_size(0), feature(GSF_INVALID), ai_callback(ai_callback) event(), count(0), station_size(0), feature(GSF_INVALID), ai_callback(ai_callback)
{ {
} }
@ -124,7 +124,7 @@ void AddGenericCallback(GrfSpecFeature feature, const GRFFile *file, const Sprit
switch (variable) { switch (variable) {
case 0x40: return this->ro.grffile->cargo_map[this->cargo_type]; case 0x40: return this->ro.grffile->cargo_map[this->cargo_type];
case 0x80: return this->cargo_type; case 0x80: return this->cargo_type.base();
case 0x81: return CargoSpec::Get(this->cargo_type)->bitnum; case 0x81: return CargoSpec::Get(this->cargo_type)->bitnum;
case 0x82: return this->default_selection; case 0x82: return this->default_selection;
case 0x83: return this->src_industry; case 0x83: return this->src_industry;

View File

@ -402,7 +402,7 @@ static uint32_t GetDistanceFromNearbyHouse(uint8_t parameter, TileIndex start_ti
} }
/* Cargo triggered CB 148? */ /* Cargo triggered CB 148? */
if (HasBit(this->watched_cargo_triggers, cargo_type)) SetBit(res, 4); if (this->watched_cargo_triggers.Test(cargo_type)) SetBit(res, 4);
return res; return res;
} }
@ -739,9 +739,9 @@ void TriggerHouseAnimation_WatchedCargoAccepted(TileIndex tile, CargoTypes trigg
HouseID id = GetHouseType(tile); HouseID id = GetHouseType(tile);
const HouseSpec *hs = HouseSpec::Get(id); const HouseSpec *hs = HouseSpec::Get(id);
trigger_cargoes &= hs->watched_cargoes; trigger_cargoes = trigger_cargoes & hs->watched_cargoes;
/* None of the trigger cargoes is watched? */ /* None of the trigger cargoes is watched? */
if (trigger_cargoes == 0) return; if (trigger_cargoes.None()) return;
/* Same random value for all tiles of a multi-tile house. */ /* Same random value for all tiles of a multi-tile house. */
uint16_t r = Random(); uint16_t r = Random();

View File

@ -55,7 +55,7 @@ struct HouseResolverObject : public SpecializedResolverObject<HouseRandomTrigger
HouseResolverObject(HouseID house_id, TileIndex tile, Town *town, HouseResolverObject(HouseID house_id, TileIndex tile, Town *town,
CallbackID callback = CBID_NO_CALLBACK, uint32_t param1 = 0, uint32_t param2 = 0, CallbackID callback = CBID_NO_CALLBACK, uint32_t param1 = 0, uint32_t param2 = 0,
bool not_yet_constructed = false, uint8_t initial_random_bits = 0, CargoTypes watched_cargo_triggers = 0, int view = 0); bool not_yet_constructed = false, uint8_t initial_random_bits = 0, CargoTypes watched_cargo_triggers = {}, int view = 0);
ScopeResolver *GetScope(VarSpriteGroupScope scope = VSG_SCOPE_SELF, uint8_t relative = 0) override ScopeResolver *GetScope(VarSpriteGroupScope scope = VSG_SCOPE_SELF, uint8_t relative = 0) override
{ {
@ -105,7 +105,7 @@ void TriggerHouseAnimation_ConstructionStageChanged(TileIndex tile, bool first_c
void TriggerHouseAnimation_WatchedCargoAccepted(TileIndex tile, CargoTypes trigger_cargoes); void TriggerHouseAnimation_WatchedCargoAccepted(TileIndex tile, CargoTypes trigger_cargoes);
uint16_t GetHouseCallback(CallbackID callback, uint32_t param1, uint32_t param2, HouseID house_id, Town *town, TileIndex tile, std::span<int32_t> regs100 = {}, uint16_t GetHouseCallback(CallbackID callback, uint32_t param1, uint32_t param2, HouseID house_id, Town *town, TileIndex tile, std::span<int32_t> regs100 = {},
bool not_yet_constructed = false, uint8_t initial_random_bits = 0, CargoTypes watched_cargo_triggers = 0, int view = 0); bool not_yet_constructed = false, uint8_t initial_random_bits = 0, CargoTypes watched_cargo_triggers = {}, int view = 0);
bool CanDeleteHouse(TileIndex tile); bool CanDeleteHouse(TileIndex tile);

View File

@ -371,7 +371,7 @@ static uint32_t GetCountAndDistanceOfClosestInstance(const ResolverObject &objec
case 0x87: return this->industry->location.h;// xy dimensions case 0x87: return this->industry->location.h;// xy dimensions
case 0x88: case 0x88:
case 0x89: return this->industry->GetProduced(variable - 0x88).cargo; case 0x89: return this->industry->GetProduced(variable - 0x88).cargo.base();
case 0x8A: return this->industry->GetProduced(0).waiting; case 0x8A: return this->industry->GetProduced(0).waiting;
case 0x8B: return GB(this->industry->GetProduced(0).waiting, 8, 8); case 0x8B: return GB(this->industry->GetProduced(0).waiting, 8, 8);
case 0x8C: return this->industry->GetProduced(1).waiting; case 0x8C: return this->industry->GetProduced(1).waiting;
@ -380,7 +380,7 @@ static uint32_t GetCountAndDistanceOfClosestInstance(const ResolverObject &objec
case 0x8F: return this->industry->GetProduced(variable - 0x8E).rate; case 0x8F: return this->industry->GetProduced(variable - 0x8E).rate;
case 0x90: case 0x90:
case 0x91: case 0x91:
case 0x92: return this->industry->GetAccepted(variable - 0x90).cargo; case 0x92: return this->industry->GetAccepted(variable - 0x90).cargo.base();
case 0x93: return this->industry->prod_level; case 0x93: return this->industry->prod_level;
/* amount of cargo produced so far THIS month. */ /* amount of cargo produced so far THIS month. */
case 0x94: return this->industry->GetProduced(0).history[THIS_MONTH].production; case 0x94: return this->industry->GetProduced(0).history[THIS_MONTH].production;

View File

@ -232,7 +232,7 @@ RoadStopResolverObject::RoadStopResolverObject(const RoadStopSpec *roadstopspec,
/* Pick the first cargo that we have waiting */ /* Pick the first cargo that we have waiting */
for (const auto &[cargo, spritegroup] : roadstopspec->grf_prop.spritegroups) { for (const auto &[cargo, spritegroup] : roadstopspec->grf_prop.spritegroups) {
if (cargo < NUM_CARGO && station->goods[cargo].HasData() && station->goods[cargo].GetData().cargo.TotalCount() > 0) { if (cargo < NUM_CARGO && station->goods[cargo].HasData() && station->goods[cargo].GetData().cargo.TotalCount() > 0) {
ctype = cargo; ctype = static_cast<CargoType>(cargo);
this->root_spritegroup = spritegroup; this->root_spritegroup = spritegroup;
break; break;
} }
@ -422,18 +422,15 @@ void TriggerRoadStopRandomisation(BaseStation *st, TileIndex tile, StationRandom
/* Check the cached cargo trigger bitmask to see if we need /* Check the cached cargo trigger bitmask to see if we need
* to bother with any further processing. * to bother with any further processing.
* Note: cached_roadstop_cargo_triggers must be non-zero even for cargo-independent triggers. */ * Note: cached_roadstop_cargo_triggers must be non-zero even for cargo-independent triggers. */
if (st->cached_roadstop_cargo_triggers == 0) return; if (st->cached_roadstop_cargo_triggers.None()) return;
if (IsValidCargoType(cargo_type) && !HasBit(st->cached_roadstop_cargo_triggers, cargo_type)) return; if (IsValidCargoType(cargo_type) && !st->cached_roadstop_cargo_triggers.Test(cargo_type)) return;
st->waiting_random_triggers.Set(trigger); st->waiting_random_triggers.Set(trigger);
uint32_t whole_reseed = 0; uint32_t whole_reseed = 0;
/* Bitmask of completely empty cargo types to be matched. */ /* Bitmask of completely empty cargo types to be matched. */
CargoTypes empty_mask{}; CargoTypes not_empty_mask = (trigger == StationRandomTrigger::CargoTaken) ? GetEmptyMask(Station::From(st)).Flip() : ALL_CARGOTYPES;
if (trigger == StationRandomTrigger::CargoTaken) {
empty_mask = GetEmptyMask(Station::From(st));
}
StationRandomTriggers used_random_triggers; StationRandomTriggers used_random_triggers;
auto process_tile = [&](TileIndex cur_tile) { auto process_tile = [&](TileIndex cur_tile) {
@ -443,10 +440,10 @@ void TriggerRoadStopRandomisation(BaseStation *st, TileIndex tile, StationRandom
/* Cargo taken "will only be triggered if all of those /* Cargo taken "will only be triggered if all of those
* cargo types have no more cargo waiting." */ * cargo types have no more cargo waiting." */
if (trigger == StationRandomTrigger::CargoTaken) { if (trigger == StationRandomTrigger::CargoTaken) {
if ((ss->cargo_triggers & ~empty_mask) != 0) return; if (ss->cargo_triggers.Any(not_empty_mask)) return;
} }
if (!IsValidCargoType(cargo_type) || HasBit(ss->cargo_triggers, cargo_type)) { if (!IsValidCargoType(cargo_type) || ss->cargo_triggers.Test(cargo_type)) {
RoadStopResolverObject object(ss, st, cur_tile, INVALID_ROADTYPE, GetStationType(cur_tile), GetStationGfx(cur_tile)); RoadStopResolverObject object(ss, st, cur_tile, INVALID_ROADTYPE, GetStationType(cur_tile), GetStationGfx(cur_tile));
object.SetWaitingRandomTriggers(st->waiting_random_triggers); object.SetWaitingRandomTriggers(st->waiting_random_triggers);
@ -621,7 +618,7 @@ void DeallocateSpecFromRoadStop(BaseStation *st, uint8_t specindex)
} else { } else {
st->roadstop_speclist.clear(); st->roadstop_speclist.clear();
st->cached_roadstop_anim_triggers = {}; st->cached_roadstop_anim_triggers = {};
st->cached_roadstop_cargo_triggers = 0; st->cached_roadstop_cargo_triggers.Reset();
return; return;
} }
} }
@ -636,13 +633,13 @@ void DeallocateSpecFromRoadStop(BaseStation *st, uint8_t specindex)
void RoadStopUpdateCachedTriggers(BaseStation *st) void RoadStopUpdateCachedTriggers(BaseStation *st)
{ {
st->cached_roadstop_anim_triggers = {}; st->cached_roadstop_anim_triggers = {};
st->cached_roadstop_cargo_triggers = 0; st->cached_roadstop_cargo_triggers.Reset();
/* Combine animation trigger bitmask for all road stop specs /* Combine animation trigger bitmask for all road stop specs
* of this station. */ * of this station. */
for (const auto &sm : GetStationSpecList<RoadStopSpec>(st)) { for (const auto &sm : GetStationSpecList<RoadStopSpec>(st)) {
if (sm.spec == nullptr) continue; if (sm.spec == nullptr) continue;
st->cached_roadstop_anim_triggers.Set(sm.spec->animation.triggers); st->cached_roadstop_anim_triggers.Set(sm.spec->animation.triggers);
st->cached_roadstop_cargo_triggers |= sm.spec->cargo_triggers; st->cached_roadstop_cargo_triggers.Set(sm.spec->cargo_triggers);
} }
} }

View File

@ -134,7 +134,7 @@ struct RoadStopSpec : NewGRFSpecBase<RoadStopClassID> {
RoadStopCallbackMasks callback_mask{}; RoadStopCallbackMasks callback_mask{};
RoadStopSpecFlags flags{}; RoadStopSpecFlags flags{};
CargoTypes cargo_triggers = 0; ///< Bitmask of cargo types which cause trigger re-randomizing CargoTypes cargo_triggers{}; ///< Bitmask of cargo types which cause trigger re-randomizing
AnimationInfo<StationAnimationTriggers> animation; AnimationInfo<StationAnimationTriggers> animation;

View File

@ -415,7 +415,7 @@ uint32_t Station::GetNewGRFVariable(const ResolverObject &object, uint8_t variab
{ {
switch (variable) { switch (variable) {
case 0x48: { // Accepted cargo types case 0x48: { // Accepted cargo types
uint32_t value = GetAcceptanceMask(this); uint32_t value = GetAcceptanceMask(this).base();
return value; return value;
} }
@ -514,14 +514,14 @@ uint32_t Waypoint::GetNewGRFVariable(const ResolverObject &, uint8_t variable, [
uint cargo = 0; uint cargo = 0;
const Station *st = Station::From(this->station_scope.st); const Station *st = Station::From(this->station_scope.st);
switch (this->station_scope.cargo_type) { switch (this->station_scope.cargo_type.base()) {
case INVALID_CARGO: case INVALID_CARGO.base():
case CargoGRFFileProps::SG_DEFAULT_NA: case CargoGRFFileProps::SG_DEFAULT_NA.base():
case CargoGRFFileProps::SG_PURCHASE: case CargoGRFFileProps::SG_PURCHASE.base():
cargo = 0; cargo = 0;
break; break;
case CargoGRFFileProps::SG_DEFAULT: case CargoGRFFileProps::SG_DEFAULT.base():
for (const GoodsEntry &ge : st->goods) { for (const GoodsEntry &ge : st->goods) {
if (!ge.HasData()) continue; if (!ge.HasData()) continue;
cargo += ge.GetData().cargo.TotalCount(); cargo += ge.GetData().cargo.TotalCount();
@ -771,7 +771,7 @@ void DeallocateSpecFromStation(BaseStation *st, uint8_t specindex)
} else { } else {
st->speclist.clear(); st->speclist.clear();
st->cached_anim_triggers = {}; st->cached_anim_triggers = {};
st->cached_cargo_triggers = 0; st->cached_cargo_triggers.Reset();
return; return;
} }
} }
@ -948,17 +948,14 @@ void TriggerStationRandomisation(BaseStation *st, TileIndex trigger_tile, Statio
/* Check the cached cargo trigger bitmask to see if we need /* Check the cached cargo trigger bitmask to see if we need
* to bother with any further processing. * to bother with any further processing.
* Note: cached_cargo_triggers must be non-zero even for cargo-independent triggers. */ * Note: cached_cargo_triggers must be non-zero even for cargo-independent triggers. */
if (st->cached_cargo_triggers == 0) return; if (st->cached_cargo_triggers.None()) return;
if (IsValidCargoType(cargo_type) && !HasBit(st->cached_cargo_triggers, cargo_type)) return; if (IsValidCargoType(cargo_type) && !st->cached_cargo_triggers.Test(cargo_type)) return;
uint32_t whole_reseed = 0; uint32_t whole_reseed = 0;
ETileArea area = ETileArea(st, trigger_tile, tas[static_cast<size_t>(trigger)]); ETileArea area = ETileArea(st, trigger_tile, tas[static_cast<size_t>(trigger)]);
/* Bitmask of completely empty cargo types to be matched. */ /* Bitmask of completely empty cargo types to be matched. */
CargoTypes empty_mask{}; CargoTypes not_empty_mask = (trigger == StationRandomTrigger::CargoTaken) ? GetEmptyMask(Station::From(st)).Flip() : ALL_CARGOTYPES;
if (trigger == StationRandomTrigger::CargoTaken) {
empty_mask = GetEmptyMask(Station::From(st));
}
/* Store triggers now for var 5F */ /* Store triggers now for var 5F */
st->waiting_random_triggers.Set(trigger); st->waiting_random_triggers.Set(trigger);
@ -973,10 +970,10 @@ void TriggerStationRandomisation(BaseStation *st, TileIndex trigger_tile, Statio
/* Cargo taken "will only be triggered if all of those /* Cargo taken "will only be triggered if all of those
* cargo types have no more cargo waiting." */ * cargo types have no more cargo waiting." */
if (trigger == StationRandomTrigger::CargoTaken) { if (trigger == StationRandomTrigger::CargoTaken) {
if ((ss->cargo_triggers & ~empty_mask) != 0) continue; if (ss->cargo_triggers.Any(not_empty_mask)) continue;
} }
if (!IsValidCargoType(cargo_type) || HasBit(ss->cargo_triggers, cargo_type)) { if (!IsValidCargoType(cargo_type) || ss->cargo_triggers.Test(cargo_type)) {
StationResolverObject object(ss, st, tile, CBID_RANDOM_TRIGGER, 0); StationResolverObject object(ss, st, tile, CBID_RANDOM_TRIGGER, 0);
object.SetWaitingRandomTriggers(st->waiting_random_triggers); object.SetWaitingRandomTriggers(st->waiting_random_triggers);
@ -1016,14 +1013,14 @@ void TriggerStationRandomisation(BaseStation *st, TileIndex trigger_tile, Statio
void StationUpdateCachedTriggers(BaseStation *st) void StationUpdateCachedTriggers(BaseStation *st)
{ {
st->cached_anim_triggers = {}; st->cached_anim_triggers = {};
st->cached_cargo_triggers = 0; st->cached_cargo_triggers.Reset();
/* Combine animation trigger bitmask for all station specs /* Combine animation trigger bitmask for all station specs
* of this station. */ * of this station. */
for (const auto &sm : GetStationSpecList<StationSpec>(st)) { for (const auto &sm : GetStationSpecList<StationSpec>(st)) {
if (sm.spec == nullptr) continue; if (sm.spec == nullptr) continue;
st->cached_anim_triggers.Set(sm.spec->animation.triggers); st->cached_anim_triggers.Set(sm.spec->animation.triggers);
st->cached_cargo_triggers |= sm.spec->cargo_triggers; st->cached_cargo_triggers.Set(sm.spec->cargo_triggers);
} }
} }

View File

@ -122,7 +122,7 @@ using StationSpecFlags = EnumBitSet<StationSpecFlag, uint8_t>;
struct StationSpec : NewGRFSpecBase<StationClassID> { struct StationSpec : NewGRFSpecBase<StationClassID> {
StationSpec() : name(0), StationSpec() : name(0),
disallowed_platforms(0), disallowed_lengths(0), disallowed_platforms(0), disallowed_lengths(0),
cargo_threshold(0), cargo_triggers(0), cargo_threshold(0), cargo_triggers{},
callback_mask(0), flags(0) callback_mask(0), flags(0)
{} {}

View File

@ -862,7 +862,7 @@ static void ProcessNewGRFStringControlCode(char32_t scc, StringConsumer &consume
case SCC_NEWGRF_PRINT_WORD_CARGO_NAME: { case SCC_NEWGRF_PRINT_WORD_CARGO_NAME: {
CargoType cargo = GetCargoTranslation(stack.PopUnsignedWord(), stack.grffile); CargoType cargo = GetCargoTranslation(stack.PopUnsignedWord(), stack.grffile);
params.emplace_back(cargo < NUM_CARGO ? 1ULL << cargo : 0); params.emplace_back(cargo < NUM_CARGO ? CargoTypes{cargo}.base() : 0);
break; break;
} }
} }

View File

@ -629,7 +629,7 @@ static void AddAcceptedCargo_Object(TileIndex tile, CargoArray &acceptance, Carg
CargoType pass = GetCargoTypeByLabel(CT_PASSENGERS); CargoType pass = GetCargoTypeByLabel(CT_PASSENGERS);
if (IsValidCargoType(pass)) { if (IsValidCargoType(pass)) {
acceptance[pass] += std::max(1U, level); acceptance[pass] += std::max(1U, level);
SetBit(always_accepted, pass); always_accepted.Set(pass);
} }
/* Top town building generates 4, HQ can make up to 8. The /* Top town building generates 4, HQ can make up to 8. The
@ -639,7 +639,7 @@ static void AddAcceptedCargo_Object(TileIndex tile, CargoArray &acceptance, Carg
CargoType mail = GetCargoTypeByLabel(CT_MAIL); CargoType mail = GetCargoTypeByLabel(CT_MAIL);
if (IsValidCargoType(mail)) { if (IsValidCargoType(mail)) {
acceptance[mail] += std::max(1U, level / 2); acceptance[mail] += std::max(1U, level / 2);
SetBit(always_accepted, mail); always_accepted.Set(mail);
} }
} }

View File

@ -36,7 +36,7 @@ void DrawRoadVehDetails(const Vehicle *v, const Rect &r)
if (v->HasArticulatedPart()) { if (v->HasArticulatedPart()) {
CargoArray max_cargo{}; CargoArray max_cargo{};
std::array<StringID, NUM_CARGO> subtype_text{}; TypedIndexContainer<std::array<StringID, NUM_CARGO>, CargoType> subtype_text{};
for (const Vehicle *u = v; u != nullptr; u = u->Next()) { for (const Vehicle *u = v; u != nullptr; u = u->Next()) {
max_cargo[u->cargo_type] += u->cargo_cap; max_cargo[u->cargo_type] += u->cargo_cap;

View File

@ -359,7 +359,7 @@ public:
SLE_CONDVAR(CompanyEconomyEntry, delivered_cargo[NUM_CARGO - 1], SLE_INT32, SL_MIN_VERSION, SLV_170), SLE_CONDVAR(CompanyEconomyEntry, delivered_cargo[NUM_CARGO - 1], SLE_INT32, SL_MIN_VERSION, SLV_170),
SLE_CONDARR(CompanyEconomyEntry, delivered_cargo, SLE_UINT32, 32, SLV_170, SLV_EXTEND_CARGOTYPES), SLE_CONDARR(CompanyEconomyEntry, delivered_cargo, SLE_UINT32, 32, SLV_170, SLV_EXTEND_CARGOTYPES),
SLE_CONDARR(CompanyEconomyEntry, delivered_cargo, SLE_UINT32, NUM_CARGO, SLV_EXTEND_CARGOTYPES, SL_MAX_VERSION), SLE_CONDARR(CompanyEconomyEntry, delivered_cargo, SLE_UINT32, NUM_CARGO, SLV_EXTEND_CARGOTYPES, SL_MAX_VERSION),
SLE_VAR(CompanyEconomyEntry, performance_history, SLE_INT32), SLE_VAR(CompanyEconomyEntry, performance_history, SLE_INT32),
}; };
static inline const SaveLoadCompatTable compat_description = _company_economy_compat; static inline const SaveLoadCompatTable compat_description = _company_economy_compat;

View File

@ -383,7 +383,7 @@ public:
{ {
Station *st = Station::From(bst); Station *st = Station::From(bst);
SlSetStructListLength(NUM_CARGO); SlSetStructListLength(std::size(st->goods));
for (GoodsEntry &ge : st->goods) { for (GoodsEntry &ge : st->goods) {
SlStationGoods::cargo_reserved_count = ge.HasData() ? ge.GetData().cargo.reserved_count : 0; SlStationGoods::cargo_reserved_count = ge.HasData() ? ge.GetData().cargo.reserved_count : 0;

View File

@ -32,7 +32,7 @@
{ {
if (!IsValidCargo(cargo_type)) return std::nullopt; if (!IsValidCargo(cargo_type)) return std::nullopt;
return ::StrMakeValid(::GetString(STR_JUST_CARGO_LIST, 1ULL << cargo_type), {}); return ::StrMakeValid(::GetString(STR_JUST_CARGO_LIST, CargoTypes{cargo_type}), {});
} }
/* static */ std::optional<std::string> ScriptCargo::GetCargoLabel(CargoType cargo_type) /* static */ std::optional<std::string> ScriptCargo::GetCargoLabel(CargoType cargo_type)

View File

@ -60,9 +60,9 @@ public:
*/ */
enum SpecialCargoType { enum SpecialCargoType {
/* Note: these values represent part of the in-game CargoTypes enum */ /* Note: these values represent part of the in-game CargoTypes enum */
CT_AUTO_REFIT = ::CARGO_AUTO_REFIT, ///< Automatically choose cargo type when doing auto-refitting. CT_AUTO_REFIT = ::CARGO_AUTO_REFIT.base(), ///< Automatically choose cargo type when doing auto-refitting.
CT_NO_REFIT = ::CARGO_NO_REFIT, ///< Do not refit cargo of a vehicle. CT_NO_REFIT = ::CARGO_NO_REFIT.base(), ///< Do not refit cargo of a vehicle.
CT_INVALID = ::INVALID_CARGO, ///< An invalid cargo type. CT_INVALID = ::INVALID_CARGO.base(), ///< An invalid cargo type.
}; };
/** /**

View File

@ -20,7 +20,7 @@
ScriptCargoList::ScriptCargoList() ScriptCargoList::ScriptCargoList()
{ {
for (const CargoSpec *cs : CargoSpec::Iterate()) { for (const CargoSpec *cs : CargoSpec::Iterate()) {
this->AddItem(cs->Index()); this->AddItem(cs->Index().base());
} }
} }
@ -31,7 +31,7 @@ ScriptCargoList_IndustryAccepting::ScriptCargoList_IndustryAccepting(IndustryID
const Industry *ind = ::Industry::Get(industry_id); const Industry *ind = ::Industry::Get(industry_id);
for (const auto &a : ind->accepted) { for (const auto &a : ind->accepted) {
if (::IsValidCargoType(a.cargo)) { if (::IsValidCargoType(a.cargo)) {
this->AddItem(a.cargo); this->AddItem(a.cargo.base());
} }
} }
} }
@ -43,7 +43,7 @@ ScriptCargoList_IndustryProducing::ScriptCargoList_IndustryProducing(IndustryID
const Industry *ind = ::Industry::Get(industry_id); const Industry *ind = ::Industry::Get(industry_id);
for (const auto &p : ind->produced) { for (const auto &p : ind->produced) {
if (::IsValidCargoType(p.cargo)) { if (::IsValidCargoType(p.cargo)) {
this->AddItem(p.cargo); this->AddItem(p.cargo.base());
} }
} }
} }
@ -53,7 +53,7 @@ ScriptCargoList_StationAccepting::ScriptCargoList_StationAccepting(StationID sta
if (!ScriptStation::IsValidStation(station_id)) return; if (!ScriptStation::IsValidStation(station_id)) return;
const Station *st = ::Station::Get(station_id); const Station *st = ::Station::Get(station_id);
for (CargoType cargo = 0; cargo < NUM_CARGO; ++cargo) { for (CargoType cargo{}; cargo < NUM_CARGO; ++cargo) {
if (st->goods[cargo].status.Test(GoodsEntry::State::Acceptance)) this->AddItem(cargo); if (st->goods[cargo].status.Test(GoodsEntry::State::Acceptance)) this->AddItem(cargo.base());
} }
} }

View File

@ -67,7 +67,7 @@
if (!IsValidEngine(engine_id)) return false; if (!IsValidEngine(engine_id)) return false;
if (!ScriptCargo::IsValidCargo(cargo_type)) return false; if (!ScriptCargo::IsValidCargo(cargo_type)) return false;
return HasBit(::GetUnionOfArticulatedRefitMasks(engine_id, true), cargo_type); return ::GetUnionOfArticulatedRefitMasks(engine_id, true).Test(cargo_type);
} }
/* static */ bool ScriptEngine::CanPullCargo(EngineID engine_id, CargoType cargo_type) /* static */ bool ScriptEngine::CanPullCargo(EngineID engine_id, CargoType cargo_type)

View File

@ -72,7 +72,7 @@
ScriptList *list = new ScriptList(); ScriptList *list = new ScriptList();
for (const CargoType &cargo : ins->produced_cargo) { for (const CargoType &cargo : ins->produced_cargo) {
if (::IsValidCargoType(cargo)) list->AddItem(cargo); if (::IsValidCargoType(cargo)) list->AddItem(cargo.base());
} }
return list; return list;
@ -86,7 +86,7 @@
ScriptList *list = new ScriptList(); ScriptList *list = new ScriptList();
for (const CargoType &cargo : ins->accepts_cargo) { for (const CargoType &cargo : ins->accepts_cargo) {
if (::IsValidCargoType(cargo)) list->AddItem(cargo); if (::IsValidCargoType(cargo)) list->AddItem(cargo.base());
} }
return list; return list;

View File

@ -227,7 +227,7 @@ void BuildLinkStatsLegend()
_legend_linkstats[i].legend = cs->name; _legend_linkstats[i].legend = cs->name;
_legend_linkstats[i].colour = cs->legend_colour; _legend_linkstats[i].colour = cs->legend_colour;
_legend_linkstats[i].type = cs->Index(); _legend_linkstats[i].type = cs->Index().base(); /// IndustryType!?
_legend_linkstats[i].show_on_map = true; _legend_linkstats[i].show_on_map = true;
} }
@ -1254,9 +1254,9 @@ protected:
*/ */
void SetOverlayCargoMask() void SetOverlayCargoMask()
{ {
CargoTypes cargo_mask = 0; CargoTypes cargo_mask{};
for (int i = 0; i != _smallmap_cargo_count; ++i) { for (int i = 0; i != _smallmap_cargo_count; ++i) {
if (_legend_linkstats[i].show_on_map) SetBit(cargo_mask, _legend_linkstats[i].type); if (_legend_linkstats[i].show_on_map) cargo_mask.Set(static_cast<CargoType>(_legend_linkstats[i].type));
} }
this->overlay->SetCargoMask(cargo_mask); this->overlay->SetCargoMask(cargo_mask);
} }
@ -1453,7 +1453,7 @@ public:
SmallMapWindow(WindowDesc &desc, int window_number) : Window(desc) SmallMapWindow(WindowDesc &desc, int window_number) : Window(desc)
{ {
_smallmap_industry_highlight = IT_INVALID; _smallmap_industry_highlight = IT_INVALID;
this->overlay = std::make_unique<LinkGraphOverlay>(this, WID_SM_MAP, 0, this->GetOverlayCompanyMask(), 1); this->overlay = std::make_unique<LinkGraphOverlay>(this, WID_SM_MAP, CargoTypes{}, this->GetOverlayCompanyMask(), 1);
this->CreateNestedTree(); this->CreateNestedTree();
this->LowerWidget(WID_SM_CONTOUR + this->map_type); this->LowerWidget(WID_SM_CONTOUR + this->map_type);

View File

@ -100,7 +100,7 @@ Station::~Station()
if (a->targetairport == this->index) a->targetairport = StationID::Invalid(); if (a->targetairport == this->index) a->targetairport = StationID::Invalid();
} }
for (CargoType cargo = 0; cargo < NUM_CARGO; ++cargo) { for (CargoType cargo{}; cargo < NUM_CARGO; ++cargo) {
LinkGraph *lg = LinkGraph::GetIfValid(this->goods[cargo].link_graph); LinkGraph *lg = LinkGraph::GetIfValid(this->goods[cargo].link_graph);
if (lg == nullptr) continue; if (lg == nullptr) continue;

View File

@ -526,7 +526,7 @@ public:
uint8_t last_vehicle_type = 0; uint8_t last_vehicle_type = 0;
std::list<Vehicle *> loading_vehicles{}; std::list<Vehicle *> loading_vehicles{};
std::array<GoodsEntry, NUM_CARGO> goods; ///< Goods at this station TypedIndexContainer<std::array<GoodsEntry, NUM_CARGO>, CargoType> goods; ///< Goods at this station
CargoTypes always_accepted{}; ///< Bitmask of always accepted cargo types (by houses, HQs, industry tiles when industry doesn't accept cargo) CargoTypes always_accepted{}; ///< Bitmask of always accepted cargo types (by houses, HQs, industry tiles when industry doesn't accept cargo)
IndustryList industries_near{}; ///< Cached list of industries near the station that can accept cargo, @see DeliverGoodsToIndustry() IndustryList industries_near{}; ///< Cached list of industries near the station that can accept cargo, @see DeliverGoodsToIndustry()

View File

@ -499,10 +499,10 @@ void ClearAllStationCachedNames()
*/ */
CargoTypes GetAcceptanceMask(const Station *st) CargoTypes GetAcceptanceMask(const Station *st)
{ {
CargoTypes mask = 0; CargoTypes mask{};
for (auto it = std::begin(st->goods); it != std::end(st->goods); ++it) { for (auto it = std::begin(st->goods); it != std::end(st->goods); ++it) {
if (it->status.Test(GoodsEntry::State::Acceptance)) SetBit(mask, std::distance(std::begin(st->goods), it)); if (it->status.Test(GoodsEntry::State::Acceptance)) mask.Set(static_cast<CargoType>(std::distance(std::begin(st->goods), it)));
} }
return mask; return mask;
} }
@ -514,10 +514,10 @@ CargoTypes GetAcceptanceMask(const Station *st)
*/ */
CargoTypes GetEmptyMask(const Station *st) CargoTypes GetEmptyMask(const Station *st)
{ {
CargoTypes mask = 0; CargoTypes mask{};
for (auto it = std::begin(st->goods); it != std::end(st->goods); ++it) { for (auto it = std::begin(st->goods); it != std::end(st->goods); ++it) {
if (!it->HasData() || it->GetData().cargo.TotalCount() == 0) SetBit(mask, std::distance(std::begin(st->goods), it)); if (!it->HasData() || it->GetData().cargo.TotalCount() == 0) mask.Set(static_cast<CargoType>(std::distance(std::begin(st->goods), it)));
} }
return mask; return mask;
} }
@ -582,7 +582,7 @@ CargoArray GetProductionAroundTiles(TileIndex north_tile, int w, int h, int rad)
CargoArray GetAcceptanceAroundTiles(TileIndex center_tile, int w, int h, int rad, CargoTypes *always_accepted) CargoArray GetAcceptanceAroundTiles(TileIndex center_tile, int w, int h, int rad, CargoTypes *always_accepted)
{ {
CargoArray acceptance{}; CargoArray acceptance{};
if (always_accepted != nullptr) *always_accepted = 0; if (always_accepted != nullptr) always_accepted->Reset();
TileArea ta = TileArea(center_tile, w, h).Expand(rad); TileArea ta = TileArea(center_tile, w, h).Expand(rad);
@ -604,7 +604,7 @@ CargoArray GetAcceptanceAroundTiles(TileIndex center_tile, int w, int h, int rad
static CargoArray GetAcceptanceAroundStation(const Station *st, CargoTypes *always_accepted) static CargoArray GetAcceptanceAroundStation(const Station *st, CargoTypes *always_accepted)
{ {
CargoArray acceptance{}; CargoArray acceptance{};
if (always_accepted != nullptr) *always_accepted = 0; if (always_accepted != nullptr) always_accepted->Reset();
BitmapTileIterator it(st->catchment_tiles); BitmapTileIterator it(st->catchment_tiles);
for (TileIndex tile = it; tile != INVALID_TILE; tile = ++it) { for (TileIndex tile = it; tile != INVALID_TILE; tile = ++it) {
@ -631,7 +631,7 @@ void UpdateStationAcceptance(Station *st, bool show_msg)
} }
/* Adjust in case our station only accepts fewer kinds of goods */ /* Adjust in case our station only accepts fewer kinds of goods */
for (CargoType cargo = 0; cargo < NUM_CARGO; ++cargo) { for (CargoType cargo{}; cargo < NUM_CARGO; ++cargo) {
uint amt = acceptance[cargo]; uint amt = acceptance[cargo];
/* Make sure the station can accept the goods type. */ /* Make sure the station can accept the goods type. */
@ -655,12 +655,12 @@ void UpdateStationAcceptance(Station *st, bool show_msg)
/* show a message to report that the acceptance was changed? */ /* show a message to report that the acceptance was changed? */
if (show_msg && st->owner == _local_company && st->IsInUse()) { if (show_msg && st->owner == _local_company && st->IsInUse()) {
/* Combine old and new masks to get changes */ /* Combine old and new masks to get changes */
CargoTypes accepts = new_acc & ~old_acc; CargoTypes accepts = new_acc & CargoTypes{old_acc}.Flip();
CargoTypes rejects = ~new_acc & old_acc; CargoTypes rejects = CargoTypes{new_acc}.Flip() & old_acc;
/* Show news message if there are any changes */ /* Show news message if there are any changes */
if (accepts != 0) ShowRejectOrAcceptNews(st, accepts, false); if (accepts.Any()) ShowRejectOrAcceptNews(st, accepts, false);
if (rejects != 0) ShowRejectOrAcceptNews(st, rejects, true); if (rejects.Any()) ShowRejectOrAcceptNews(st, rejects, true);
} }
/* redraw the station view since acceptance changed */ /* redraw the station view since acceptance changed */
@ -3760,13 +3760,13 @@ static VehicleEnterTileStates VehicleEnter_Station(Vehicle *v, TileIndex tile, i
void TriggerWatchedCargoCallbacks(Station *st) void TriggerWatchedCargoCallbacks(Station *st)
{ {
/* Collect cargoes accepted since the last big tick. */ /* Collect cargoes accepted since the last big tick. */
CargoTypes cargoes = 0; CargoTypes cargoes{};
for (CargoType cargo_type = 0; cargo_type < NUM_CARGO; cargo_type++) { for (CargoType cargo_type{}; cargo_type < NUM_CARGO; ++cargo_type) {
if (st->goods[cargo_type].status.Test(GoodsEntry::State::AcceptedBigtick)) SetBit(cargoes, cargo_type); if (st->goods[cargo_type].status.Test(GoodsEntry::State::AcceptedBigtick)) cargoes.Set(cargo_type);
} }
/* Anything to do? */ /* Anything to do? */
if (cargoes == 0) return; if (cargoes.None()) return;
/* Loop over all houses in the catchment. */ /* Loop over all houses in the catchment. */
BitmapTileIterator it(st->catchment_tiles); BitmapTileIterator it(st->catchment_tiles);
@ -4027,7 +4027,7 @@ void RerouteCargo(Station *st, CargoType cargo, StationID avoid, StationID avoid
*/ */
void DeleteStaleLinks(Station *from) void DeleteStaleLinks(Station *from)
{ {
for (CargoType cargo = 0; cargo < NUM_CARGO; ++cargo) { for (CargoType cargo{}; cargo < NUM_CARGO; ++cargo) {
const bool auto_distributed = (_settings_game.linkgraph.GetDistributionType(cargo) != DT_MANUAL); const bool auto_distributed = (_settings_game.linkgraph.GetDistributionType(cargo) != DT_MANUAL);
GoodsEntry &ge = from->goods[cargo]; GoodsEntry &ge = from->goods[cargo];
LinkGraph *lg = LinkGraph::GetIfValid(ge.link_graph); LinkGraph *lg = LinkGraph::GetIfValid(ge.link_graph);

View File

@ -76,7 +76,7 @@ using RoadWaypointTypeFilter = GenericWaypointTypeFilter<true, MP_ROAD>;
int DrawStationCoverageAreaText(const Rect &r, StationCoverageType sct, int rad, bool supplies) int DrawStationCoverageAreaText(const Rect &r, StationCoverageType sct, int rad, bool supplies)
{ {
TileIndex tile = TileVirtXY(_thd.pos.x, _thd.pos.y); TileIndex tile = TileVirtXY(_thd.pos.x, _thd.pos.y);
CargoTypes cargo_mask = 0; CargoTypes cargo_mask{};
if (_thd.drawstyle == HT_RECT && tile < Map::Size()) { if (_thd.drawstyle == HT_RECT && tile < Map::Size()) {
CargoArray cargoes; CargoArray cargoes;
if (supplies) { if (supplies) {
@ -86,14 +86,14 @@ int DrawStationCoverageAreaText(const Rect &r, StationCoverageType sct, int rad,
} }
/* Convert cargo counts to a set of cargo bits, and draw the result. */ /* Convert cargo counts to a set of cargo bits, and draw the result. */
for (CargoType cargo = 0; cargo < NUM_CARGO; ++cargo) { for (CargoType cargo{}; cargo < NUM_CARGO; ++cargo) {
switch (sct) { switch (sct) {
case SCT_PASSENGERS_ONLY: if (!IsCargoInClass(cargo, CargoClass::Passengers)) continue; break; case SCT_PASSENGERS_ONLY: if (!IsCargoInClass(cargo, CargoClass::Passengers)) continue; break;
case SCT_NON_PASSENGERS_ONLY: if (IsCargoInClass(cargo, CargoClass::Passengers)) continue; break; case SCT_NON_PASSENGERS_ONLY: if (IsCargoInClass(cargo, CargoClass::Passengers)) continue; break;
case SCT_ALL: break; case SCT_ALL: break;
default: NOT_REACHED(); default: NOT_REACHED();
} }
if (cargoes[cargo] >= (supplies ? 1U : 8U)) SetBit(cargo_mask, cargo); if (cargoes[cargo] >= (supplies ? 1U : 8U)) cargo_mask.Set(cargo);
} }
} }
return DrawStringMultiLine(r, GetString(supplies ? STR_STATION_BUILD_SUPPLIES_CARGO : STR_STATION_BUILD_ACCEPTS_CARGO, cargo_mask)); return DrawStringMultiLine(r, GetString(supplies ? STR_STATION_BUILD_SUPPLIES_CARGO : STR_STATION_BUILD_ACCEPTS_CARGO, cargo_mask));
@ -291,7 +291,7 @@ protected:
Scrollbar *vscroll = nullptr; Scrollbar *vscroll = nullptr;
uint rating_width = 0; uint rating_width = 0;
bool filter_expanded = false; bool filter_expanded = false;
std::array<uint16_t, NUM_CARGO> stations_per_cargo_type{}; ///< Number of stations with a rating for each cargo type. TypedIndexContainer<std::array<uint16_t, NUM_CARGO>, CargoType> stations_per_cargo_type{}; ///< Number of stations with a rating for each cargo type.
uint16_t stations_per_cargo_type_no_rating = 0; ///< Number of stations without a rating. uint16_t stations_per_cargo_type_no_rating = 0; ///< Number of stations without a rating.
/** /**
@ -314,13 +314,13 @@ protected:
if (st->owner == owner || (st->owner == OWNER_NONE && HasStationInUse(st->index, true, owner))) { if (st->owner == owner || (st->owner == OWNER_NONE && HasStationInUse(st->index, true, owner))) {
bool has_rating = false; bool has_rating = false;
/* Add to the station/cargo counts. */ /* Add to the station/cargo counts. */
for (CargoType cargo = 0; cargo < NUM_CARGO; ++cargo) { for (CargoType cargo{}; cargo < NUM_CARGO; ++cargo) {
if (st->goods[cargo].HasRating()) this->stations_per_cargo_type[cargo]++; if (st->goods[cargo].HasRating()) this->stations_per_cargo_type[cargo]++;
} }
for (CargoType cargo = 0; cargo < NUM_CARGO; ++cargo) { for (CargoType cargo{}; cargo < NUM_CARGO; ++cargo) {
if (st->goods[cargo].HasRating()) { if (st->goods[cargo].HasRating()) {
has_rating = true; has_rating = true;
if (HasBit(this->filter.cargoes, cargo)) { if (this->filter.cargoes.Test(cargo)) {
this->stations.push_back(st); this->stations.push_back(st);
break; break;
} }
@ -359,7 +359,7 @@ protected:
{ {
int diff = 0; int diff = 0;
for (CargoType cargo : SetCargoBitIterator(cargo_filter)) { for (CargoType cargo : cargo_filter) {
diff += (a->goods[cargo].HasData() ? a->goods[cargo].GetData().cargo.TotalCount() : 0) - (b->goods[cargo].HasData() ? b->goods[cargo].GetData().cargo.TotalCount() : 0); diff += (a->goods[cargo].HasData() ? a->goods[cargo].GetData().cargo.TotalCount() : 0) - (b->goods[cargo].HasData() ? b->goods[cargo].GetData().cargo.TotalCount() : 0);
} }
@ -371,7 +371,7 @@ protected:
{ {
int diff = 0; int diff = 0;
for (CargoType cargo : SetCargoBitIterator(cargo_filter)) { for (CargoType cargo : cargo_filter) {
diff += (a->goods[cargo].HasData() ? a->goods[cargo].GetData().cargo.AvailableCount() : 0) - (b->goods[cargo].HasData() ? b->goods[cargo].GetData().cargo.AvailableCount() : 0); diff += (a->goods[cargo].HasData() ? a->goods[cargo].GetData().cargo.AvailableCount() : 0) - (b->goods[cargo].HasData() ? b->goods[cargo].GetData().cargo.AvailableCount() : 0);
} }
@ -384,7 +384,7 @@ protected:
uint8_t maxr1 = 0; uint8_t maxr1 = 0;
uint8_t maxr2 = 0; uint8_t maxr2 = 0;
for (CargoType cargo : SetCargoBitIterator(cargo_filter)) { for (CargoType cargo : cargo_filter) {
if (a->goods[cargo].HasRating()) maxr1 = std::max(maxr1, a->goods[cargo].rating); if (a->goods[cargo].HasRating()) maxr1 = std::max(maxr1, a->goods[cargo].rating);
if (b->goods[cargo].HasRating()) maxr2 = std::max(maxr2, b->goods[cargo].rating); if (b->goods[cargo].HasRating()) maxr2 = std::max(maxr2, b->goods[cargo].rating);
} }
@ -398,7 +398,7 @@ protected:
uint8_t minr1 = 255; uint8_t minr1 = 255;
uint8_t minr2 = 255; uint8_t minr2 = 255;
for (CargoType cargo : SetCargoBitIterator(cargo_filter)) { for (CargoType cargo : cargo_filter) {
if (a->goods[cargo].HasRating()) minr1 = std::min(minr1, a->goods[cargo].rating); if (a->goods[cargo].HasRating()) minr1 = std::min(minr1, a->goods[cargo].rating);
if (b->goods[cargo].HasRating()) minr2 = std::min(minr2, b->goods[cargo].rating); if (b->goods[cargo].HasRating()) minr2 = std::min(minr2, b->goods[cargo].rating);
} }
@ -558,9 +558,9 @@ public:
} }
if (widget == WID_STL_CARGODROPDOWN) { if (widget == WID_STL_CARGODROPDOWN) {
if (this->filter.cargoes == 0) return GetString(this->filter.include_no_rating ? STR_STATION_LIST_CARGO_FILTER_ONLY_NO_RATING : STR_STATION_LIST_CARGO_FILTER_NO_CARGO_TYPES); if (this->filter.cargoes.None()) return GetString(this->filter.include_no_rating ? STR_STATION_LIST_CARGO_FILTER_ONLY_NO_RATING : STR_STATION_LIST_CARGO_FILTER_NO_CARGO_TYPES);
if (this->filter.cargoes == _cargo_mask) return GetString(this->filter.include_no_rating ? STR_STATION_LIST_CARGO_FILTER_ALL_AND_NO_RATING : STR_CARGO_TYPE_FILTER_ALL); if (this->filter.cargoes == _cargo_mask) return GetString(this->filter.include_no_rating ? STR_STATION_LIST_CARGO_FILTER_ALL_AND_NO_RATING : STR_CARGO_TYPE_FILTER_ALL);
if (CountBits(this->filter.cargoes) == 1 && !this->filter.include_no_rating) return GetString(CargoSpec::Get(FindFirstBit(this->filter.cargoes))->name); if (this->filter.cargoes.Count() == 1 && !this->filter.include_no_rating) return GetString(CargoSpec::Get(*this->filter.cargoes.begin())->name);
return GetString(STR_STATION_LIST_CARGO_FILTER_MULTIPLE); return GetString(STR_STATION_LIST_CARGO_FILTER_MULTIPLE);
} }
@ -573,7 +573,7 @@ public:
using DropDownListCargoItem = DropDownCheck<DropDownString<DropDownListIconItem, FS_SMALL, true>>; using DropDownListCargoItem = DropDownCheck<DropDownString<DropDownListIconItem, FS_SMALL, true>>;
DropDownList list; DropDownList list;
list.push_back(MakeDropDownListStringItem(STR_STATION_LIST_CARGO_FILTER_SELECT_ALL, CargoFilterCriteria::CF_SELECT_ALL)); list.push_back(MakeDropDownListStringItem(STR_STATION_LIST_CARGO_FILTER_SELECT_ALL, CargoFilterCriteria::CF_SELECT_ALL.base()));
list.push_back(MakeDropDownListDividerItem()); list.push_back(MakeDropDownListDividerItem());
bool any_hidden = false; bool any_hidden = false;
@ -582,7 +582,7 @@ public:
if (count == 0 && !expanded) { if (count == 0 && !expanded) {
any_hidden = true; any_hidden = true;
} else { } else {
list.push_back(std::make_unique<DropDownString<DropDownListCheckedItem, FS_SMALL, true>>(fmt::format("{}", count), 0, this->filter.include_no_rating, GetString(STR_STATION_LIST_CARGO_FILTER_NO_RATING), CargoFilterCriteria::CF_NO_RATING, false, count == 0)); list.push_back(std::make_unique<DropDownString<DropDownListCheckedItem, FS_SMALL, true>>(fmt::format("{}", count), 0, this->filter.include_no_rating, GetString(STR_STATION_LIST_CARGO_FILTER_NO_RATING), CargoFilterCriteria::CF_NO_RATING.base(), false, count == 0));
} }
Dimension d = GetLargestCargoIconSize(); Dimension d = GetLargestCargoIconSize();
@ -591,13 +591,13 @@ public:
if (count == 0 && !expanded) { if (count == 0 && !expanded) {
any_hidden = true; any_hidden = true;
} else { } else {
list.push_back(std::make_unique<DropDownListCargoItem>(HasBit(this->filter.cargoes, cs->Index()), fmt::format("{}", count), d, cs->GetCargoIcon(), PAL_NONE, GetString(cs->name), cs->Index(), false, count == 0)); list.push_back(std::make_unique<DropDownListCargoItem>(this->filter.cargoes.Test(cs->Index()), fmt::format("{}", count), d, cs->GetCargoIcon(), PAL_NONE, GetString(cs->name), cs->Index().base(), false, count == 0));
} }
} }
if (!expanded && any_hidden) { if (!expanded && any_hidden) {
if (list.size() > 2) list.push_back(MakeDropDownListDividerItem()); if (list.size() > 2) list.push_back(MakeDropDownListDividerItem());
list.push_back(MakeDropDownListStringItem(STR_STATION_LIST_CARGO_FILTER_EXPAND, CargoFilterCriteria::CF_EXPAND_LIST)); list.push_back(MakeDropDownListStringItem(STR_STATION_LIST_CARGO_FILTER_EXPAND, CargoFilterCriteria::CF_EXPAND_LIST.base()));
} }
return list; return list;
@ -684,11 +684,11 @@ public:
if (widget == WID_STL_CARGODROPDOWN) { if (widget == WID_STL_CARGODROPDOWN) {
FilterState oldstate = this->filter; FilterState oldstate = this->filter;
if (index >= 0 && index < NUM_CARGO) { if (IsInsideMM(index, 0, NUM_CARGO)) {
if (_ctrl_pressed) { if (_ctrl_pressed) {
ToggleBit(this->filter.cargoes, index); this->filter.cargoes.Flip(static_cast<CargoType>(index));
} else { } else {
this->filter.cargoes = 1ULL << index; this->filter.cargoes = static_cast<CargoType>(index);
this->filter.include_no_rating = false; this->filter.include_no_rating = false;
} }
} else if (index == CargoFilterCriteria::CF_NO_RATING) { } else if (index == CargoFilterCriteria::CF_NO_RATING) {
@ -696,7 +696,7 @@ public:
this->filter.include_no_rating = !this->filter.include_no_rating; this->filter.include_no_rating = !this->filter.include_no_rating;
} else { } else {
this->filter.include_no_rating = true; this->filter.include_no_rating = true;
this->filter.cargoes = 0; this->filter.cargoes.Reset();
} }
} else if (index == CargoFilterCriteria::CF_SELECT_ALL) { } else if (index == CargoFilterCriteria::CF_SELECT_ALL) {
this->filter.cargoes = _cargo_mask; this->filter.cargoes = _cargo_mask;
@ -1047,13 +1047,11 @@ private:
void IncrementSize(); void IncrementSize();
CargoDataEntry *parent; ///< the parent of this entry. CargoDataEntry *parent; ///< the parent of this entry.
const union {
StationID station; ///< ID of the station this entry is associated with. const StationID station = StationID::Invalid(); ///< ID of the station this entry is associated with.
struct { const CargoType cargo = INVALID_CARGO; ///< ID of the cargo this entry is associated with.
CargoType cargo; ///< ID of the cargo this entry is associated with. bool transfers = false; ///< If there are transfers for this cargo.
bool transfers; ///< If there are transfers for this cargo.
};
};
uint num_children; ///< the number of subentries belonging to this entry. 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. uint count; ///< sum of counts of all children or amount of cargo for this entry.
std::unique_ptr<CargoDataSet> children; ///< the children of this entry. std::unique_ptr<CargoDataSet> children; ///< the children of this entry.
@ -1673,7 +1671,7 @@ struct StationViewWindow : public Window {
*/ */
void BuildCargoList(CargoDataEntry *entry, const Station *st) void BuildCargoList(CargoDataEntry *entry, const Station *st)
{ {
for (CargoType cargo = 0; cargo < NUM_CARGO; ++cargo) { for (CargoType cargo{}; cargo < NUM_CARGO; ++cargo) {
if (this->cached_destinations.Retrieve(cargo) == nullptr) { if (this->cached_destinations.Retrieve(cargo) == nullptr) {
this->RecalcDestinations(cargo); this->RecalcDestinations(cargo);
@ -2141,8 +2139,8 @@ struct StationViewWindow : public Window {
void OnInvalidateData([[maybe_unused]] int data = 0, [[maybe_unused]] bool gui_scope = true) override void OnInvalidateData([[maybe_unused]] int data = 0, [[maybe_unused]] bool gui_scope = true) override
{ {
if (gui_scope) { if (gui_scope) {
if (data >= 0 && data < NUM_CARGO) { if (IsInsideMM(data, 0, NUM_CARGO)) {
this->cached_destinations.Remove((CargoType)data); this->cached_destinations.Remove(static_cast<CargoType>(data));
} else { } else {
this->ReInit(); this->ReInit();
} }

View File

@ -1342,10 +1342,10 @@ static void FormatString(StringBuilder &builder, std::string_view str_arg, Strin
/* Tiny description of cargotypes. Layout: /* Tiny description of cargotypes. Layout:
* param 1: cargo type * param 1: cargo type
* param 2: cargo count */ * param 2: cargo count */
CargoType cargo = args.GetNextParameter<CargoType>(); CargoType cargo{args.GetNextParameter<CargoType::BaseType>()};
int64_t amount = args.GetNextParameter<int64_t>(); int64_t amount = args.GetNextParameter<int64_t>();
if (cargo >= CargoSpec::GetArraySize()) { if (cargo.base() >= CargoSpec::GetArraySize()) {
builder += "(invalid cargo type)"; builder += "(invalid cargo type)";
break; break;
} }
@ -1371,10 +1371,10 @@ static void FormatString(StringBuilder &builder, std::string_view str_arg, Strin
/* Short description of cargotypes. Layout: /* Short description of cargotypes. Layout:
* param 1: cargo type * param 1: cargo type
* param 2: cargo count */ * param 2: cargo count */
CargoType cargo = args.GetNextParameter<CargoType>(); CargoType cargo{args.GetNextParameter<CargoType::BaseType>()};
int64_t amount = args.GetNextParameter<int64_t>(); int64_t amount = args.GetNextParameter<int64_t>();
if (cargo >= CargoSpec::GetArraySize()) { if (cargo.base() >= CargoSpec::GetArraySize()) {
builder += "(invalid cargo type)"; builder += "(invalid cargo type)";
break; break;
} }
@ -1408,9 +1408,9 @@ static void FormatString(StringBuilder &builder, std::string_view str_arg, Strin
case SCC_CARGO_LONG: { // {CARGO_LONG} case SCC_CARGO_LONG: { // {CARGO_LONG}
/* First parameter is cargo type, second parameter is cargo count */ /* First parameter is cargo type, second parameter is cargo count */
CargoType cargo = args.GetNextParameter<CargoType>(); CargoType cargo{args.GetNextParameter<CargoType::BaseType>()};
int64_t amount = args.GetNextParameter<int64_t>(); int64_t amount = args.GetNextParameter<int64_t>();
if (cargo < CargoSpec::GetArraySize()) { if (cargo.base() < CargoSpec::GetArraySize()) {
auto tmp_args = MakeParameters(amount); auto tmp_args = MakeParameters(amount);
GetStringWithArgs(builder, CargoSpec::Get(cargo)->quantifier, tmp_args); GetStringWithArgs(builder, CargoSpec::Get(cargo)->quantifier, tmp_args);
} else if (!IsValidCargoType(cargo)) { } else if (!IsValidCargoType(cargo)) {
@ -1427,7 +1427,7 @@ static void FormatString(StringBuilder &builder, std::string_view str_arg, Strin
std::string_view list_separator = GetListSeparator(); std::string_view list_separator = GetListSeparator();
for (const auto &cs : _sorted_cargo_specs) { for (const auto &cs : _sorted_cargo_specs) {
if (!HasBit(cmask, cs->Index())) continue; if (!cmask.Test(cs->Index())) continue;
if (first) { if (first) {
first = false; first = false;

View File

@ -296,8 +296,8 @@ bool FindSubsidyTownCargoRoute()
/* Choose a random cargo that is produced in the town. */ /* Choose a random cargo that is produced in the town. */
uint8_t cargo_number = RandomRange(cargo_count); uint8_t cargo_number = RandomRange(cargo_count);
CargoType cargo_type; CargoType cargo_type{};
for (cargo_type = 0; cargo_type < NUM_CARGO; cargo_type++) { for (; cargo_type < NUM_CARGO; ++cargo_type) {
if (town_cargo_produced[cargo_type] > 0) { if (town_cargo_produced[cargo_type] > 0) {
if (cargo_number == 0) break; if (cargo_number == 0) break;
cargo_number--; cargo_number--;

View File

@ -26,7 +26,7 @@
* @param f Bitmask of the climates * @param f Bitmask of the climates
* @note the 5 between b and f is the load amount * @note the 5 between b and f is the load amount
*/ */
#define MT(a, b, c, d, e, f) { CalendarTime::DAYS_TILL_ORIGINAL_BASE_YEAR + a, TimerGameCalendar::Year{c}, TimerGameCalendar::Year{d}, b, 5, f, INVALID_CARGO, e, 0, 8, EngineMiscFlags{}, VehicleCallbackMasks{}, 0, {}, STR_EMPTY, Ticks::CARGO_AGING_TICKS, EngineID::Invalid() } #define MT(a, b, c, d, e, f) { CalendarTime::DAYS_TILL_ORIGINAL_BASE_YEAR + a, TimerGameCalendar::Year{c}, TimerGameCalendar::Year{d}, b, 5, f, INVALID_CARGO, e, CargoTypes{}, 8, EngineMiscFlags{}, VehicleCallbackMasks{}, 0, {}, STR_EMPTY, Ticks::CARGO_AGING_TICKS, EngineID::Invalid() }
/** /**
* Writes the properties of a multiple-unit train into the EngineInfo struct. * Writes the properties of a multiple-unit train into the EngineInfo struct.
@ -39,7 +39,7 @@
* @param f Bitmask of the climates * @param f Bitmask of the climates
* @note the 5 between b and f is the load amount * @note the 5 between b and f is the load amount
*/ */
#define MM(a, b, c, d, e, f) { CalendarTime::DAYS_TILL_ORIGINAL_BASE_YEAR + a, TimerGameCalendar::Year{c}, TimerGameCalendar::Year{d}, b, 5, f, INVALID_CARGO, e, 0, 8, EngineMiscFlags{EngineMiscFlag::RailIsMU}, VehicleCallbackMasks{}, 0, {}, STR_EMPTY, Ticks::CARGO_AGING_TICKS, EngineID::Invalid() } #define MM(a, b, c, d, e, f) { CalendarTime::DAYS_TILL_ORIGINAL_BASE_YEAR + a, TimerGameCalendar::Year{c}, TimerGameCalendar::Year{d}, b, 5, f, INVALID_CARGO, e, CargoTypes{}, 8, EngineMiscFlags{EngineMiscFlag::RailIsMU}, VehicleCallbackMasks{}, 0, {}, STR_EMPTY, Ticks::CARGO_AGING_TICKS, EngineID::Invalid() }
/** /**
* Writes the properties of a train carriage into the EngineInfo struct. * Writes the properties of a train carriage into the EngineInfo struct.
@ -52,7 +52,7 @@
* @see MT * @see MT
* @note the 5 between b and f is the load amount * @note the 5 between b and f is the load amount
*/ */
#define MW(a, b, c, d, e, f) { CalendarTime::DAYS_TILL_ORIGINAL_BASE_YEAR + a, TimerGameCalendar::Year{c}, TimerGameCalendar::Year{d}, b, 5, f, INVALID_CARGO, e, 0, 8, EngineMiscFlags{}, VehicleCallbackMasks{}, 0, {}, STR_EMPTY, Ticks::CARGO_AGING_TICKS, EngineID::Invalid() } #define MW(a, b, c, d, e, f) { CalendarTime::DAYS_TILL_ORIGINAL_BASE_YEAR + a, TimerGameCalendar::Year{c}, TimerGameCalendar::Year{d}, b, 5, f, INVALID_CARGO, e, CargoTypes{}, 8, EngineMiscFlags{}, VehicleCallbackMasks{}, 0, {}, STR_EMPTY, Ticks::CARGO_AGING_TICKS, EngineID::Invalid() }
/** /**
* Writes the properties of a road vehicle into the EngineInfo struct. * Writes the properties of a road vehicle into the EngineInfo struct.
@ -65,7 +65,7 @@
* @param f Bitmask of the climates * @param f Bitmask of the climates
* @note the 5 between b and f is the load amount * @note the 5 between b and f is the load amount
*/ */
#define MR(a, b, c, d, e, f) { CalendarTime::DAYS_TILL_ORIGINAL_BASE_YEAR + a, TimerGameCalendar::Year{c}, TimerGameCalendar::Year{d}, b, 5, f, INVALID_CARGO, e, 0, 8, EngineMiscFlags{}, VehicleCallbackMasks{}, 0, {}, STR_EMPTY, Ticks::CARGO_AGING_TICKS, EngineID::Invalid() } #define MR(a, b, c, d, e, f) { CalendarTime::DAYS_TILL_ORIGINAL_BASE_YEAR + a, TimerGameCalendar::Year{c}, TimerGameCalendar::Year{d}, b, 5, f, INVALID_CARGO, e, CargoTypes{}, 8, EngineMiscFlags{}, VehicleCallbackMasks{}, 0, {}, STR_EMPTY, Ticks::CARGO_AGING_TICKS, EngineID::Invalid() }
/** /**
* Writes the properties of a ship into the EngineInfo struct. * Writes the properties of a ship into the EngineInfo struct.
@ -77,7 +77,7 @@
* @param f Bitmask of the climates * @param f Bitmask of the climates
* @note the 10 between b and f is the load amount * @note the 10 between b and f is the load amount
*/ */
#define MS(a, b, c, d, e, f) { CalendarTime::DAYS_TILL_ORIGINAL_BASE_YEAR + a, TimerGameCalendar::Year{c}, TimerGameCalendar::Year{d}, b, 10, f, INVALID_CARGO, e, 0, 8, EngineMiscFlags{}, VehicleCallbackMasks{}, 0, {}, STR_EMPTY, Ticks::CARGO_AGING_TICKS, EngineID::Invalid() } #define MS(a, b, c, d, e, f) { CalendarTime::DAYS_TILL_ORIGINAL_BASE_YEAR + a, TimerGameCalendar::Year{c}, TimerGameCalendar::Year{d}, b, 10, f, INVALID_CARGO, e, CargoTypes{}, 8, EngineMiscFlags{}, VehicleCallbackMasks{}, 0, {}, STR_EMPTY, Ticks::CARGO_AGING_TICKS, EngineID::Invalid() }
/** /**
* Writes the properties of an aeroplane into the EngineInfo struct. * Writes the properties of an aeroplane into the EngineInfo struct.
@ -88,7 +88,7 @@
* @param e Bitmask of the climates * @param e Bitmask of the climates
* @note the 20 between b and e is the load amount * @note the 20 between b and e is the load amount
*/ */
#define MA(a, b, c, d, e) { CalendarTime::DAYS_TILL_ORIGINAL_BASE_YEAR + a, TimerGameCalendar::Year{c}, TimerGameCalendar::Year{d}, b, 20, e, INVALID_CARGO, CT_INVALID, 0, 8, EngineMiscFlags{}, VehicleCallbackMasks{}, 0, {}, STR_EMPTY, Ticks::CARGO_AGING_TICKS, EngineID::Invalid() } #define MA(a, b, c, d, e) { CalendarTime::DAYS_TILL_ORIGINAL_BASE_YEAR + a, TimerGameCalendar::Year{c}, TimerGameCalendar::Year{d}, b, 20, e, INVALID_CARGO, CT_INVALID, CargoTypes{}, 8, EngineMiscFlags{}, VehicleCallbackMasks{}, 0, {}, STR_EMPTY, Ticks::CARGO_AGING_TICKS, EngineID::Invalid() }
/* Climates /* Climates
* T = Temperate * T = Temperate

View File

@ -270,8 +270,8 @@ static const NIFeature _nif_industrytile = {
/*** NewGRF industries ***/ /*** NewGRF industries ***/
#define NIP_PRODUCED_CARGO(prop, base_class, slot, type, name) { name, [] (const void *b) -> uint32_t { return static_cast<const base_class *>(b)->GetProduced(slot).cargo; }, prop, type } #define NIP_PRODUCED_CARGO(prop, base_class, slot, type, name) { name, [] (const void *b) -> uint32_t { return static_cast<const base_class *>(b)->GetProduced(slot).cargo.base(); }, prop, type }
#define NIP_ACCEPTED_CARGO(prop, base_class, slot, type, name) { name, [] (const void *b) -> uint32_t { return static_cast<const base_class *>(b)->GetAccepted(slot).cargo; }, prop, type } #define NIP_ACCEPTED_CARGO(prop, base_class, slot, type, name) { name, [] (const void *b) -> uint32_t { return static_cast<const base_class *>(b)->GetAccepted(slot).cargo.base(); }, prop, type }
static const NIProperty _nip_industries[] = { static const NIProperty _nip_industries[] = {
NIP_PRODUCED_CARGO(0x25, Industry, 0, NIT_CARGO, "produced cargo 0"), NIP_PRODUCED_CARGO(0x25, Industry, 0, NIT_CARGO, "produced cargo 0"),

View File

@ -1814,7 +1814,7 @@ static_assert(lengthof(_town_draw_tile_data) == (NEW_HOUSE_OFFSET) * 4 * 4);
{ca1, ca2, ca3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, \ {ca1, ca2, ca3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, \
{INVALID_CARGO, INVALID_CARGO, INVALID_CARGO, INVALID_CARGO, INVALID_CARGO, INVALID_CARGO, INVALID_CARGO, INVALID_CARGO, INVALID_CARGO, INVALID_CARGO, INVALID_CARGO, INVALID_CARGO, INVALID_CARGO, INVALID_CARGO, INVALID_CARGO, INVALID_CARGO}, \ {INVALID_CARGO, INVALID_CARGO, INVALID_CARGO, INVALID_CARGO, INVALID_CARGO, INVALID_CARGO, INVALID_CARGO, INVALID_CARGO, INVALID_CARGO, INVALID_CARGO, INVALID_CARGO, INVALID_CARGO, INVALID_CARGO, INVALID_CARGO, INVALID_CARGO, INVALID_CARGO}, \
bf, ba, true, SubstituteGRFFileProps(INVALID_HOUSE_ID), HouseCallbackMasks{}, {COLOUR_BEGIN, COLOUR_BEGIN, COLOUR_BEGIN, COLOUR_BEGIN}, \ bf, ba, true, SubstituteGRFFileProps(INVALID_HOUSE_ID), HouseCallbackMasks{}, {COLOUR_BEGIN, COLOUR_BEGIN, COLOUR_BEGIN, COLOUR_BEGIN}, \
16, HouseExtraFlags{}, HOUSE_NO_CLASS, AnimationInfo<void>{}, 0, 0, 0, {}, {cg1, cg2, cg3}, } 16, HouseExtraFlags{}, HOUSE_NO_CLASS, AnimationInfo<void>{}, 0, 0, CargoTypes{}, {}, {cg1, cg2, cg3}, }
/** House specifications from original data */ /** House specifications from original data */
extern const HouseSpec _original_house_specs[] = { extern const HouseSpec _original_house_specs[] = {
/** /**

View File

@ -167,7 +167,7 @@ inline void AddAcceptedCargo(TileIndex tile, CargoArray &acceptance, CargoTypes
{ {
AddAcceptedCargoProc *proc = _tile_type_procs[GetTileType(tile)]->add_accepted_cargo_proc; AddAcceptedCargoProc *proc = _tile_type_procs[GetTileType(tile)]->add_accepted_cargo_proc;
if (proc == nullptr) return; if (proc == nullptr) return;
CargoTypes dummy = 0; // use dummy bitmask so there don't need to be several 'always_accepted != nullptr' checks CargoTypes dummy{}; // use dummy bitmask so there don't need to be several 'always_accepted != nullptr' checks
proc(tile, acceptance, always_accepted == nullptr ? dummy : *always_accepted); proc(tile, acceptance, always_accepted == nullptr ? dummy : *always_accepted);
} }

View File

@ -74,7 +74,7 @@ struct Town : TownPool::PoolItem<&_town_pool> {
uint8_t exclusive_counter = 0; ///< months till the exclusivity expires uint8_t exclusive_counter = 0; ///< months till the exclusivity expires
TypedIndexContainer<std::array<int16_t, MAX_COMPANIES>, CompanyID> ratings{}; ///< ratings of each company for this town TypedIndexContainer<std::array<int16_t, MAX_COMPANIES>, CompanyID> ratings{}; ///< ratings of each company for this town
std::array<TransportedCargoStat<uint32_t>, NUM_CARGO> supplied{}; ///< Cargo statistics about supplied cargo. TypedIndexContainer<std::array<TransportedCargoStat<uint32_t>, NUM_CARGO>, CargoType> supplied{}; ///< Cargo statistics about supplied cargo.
std::array<TransportedCargoStat<uint16_t>, NUM_TAE> received{}; ///< Cargo statistics about received cargotypes. std::array<TransportedCargoStat<uint16_t>, NUM_TAE> received{}; ///< Cargo statistics about received cargotypes.
std::array<uint32_t, NUM_TAE> goal{}; ///< Amount of cargo required for the town to grow. std::array<uint32_t, NUM_TAE> goal{}; ///< Amount of cargo required for the town to grow.

View File

@ -775,7 +775,7 @@ static void AddAcceptedCargoSetMask(CargoType cargo, uint amount, CargoArray &ac
{ {
if (!IsValidCargoType(cargo) || amount == 0) return; if (!IsValidCargoType(cargo) || amount == 0) return;
acceptance[cargo] += amount; acceptance[cargo] += amount;
SetBit(always_accepted, cargo); always_accepted.Set(cargo);
} }
/** /**

View File

@ -400,7 +400,7 @@ public:
for (auto tpe : {TPE_PASSENGERS, TPE_MAIL}) { for (auto tpe : {TPE_PASSENGERS, TPE_MAIL}) {
for (const CargoSpec *cs : CargoSpec::town_production_cargoes[tpe]) { for (const CargoSpec *cs : CargoSpec::town_production_cargoes[tpe]) {
CargoType cargo_type = cs->Index(); CargoType cargo_type = cs->Index();
DrawString(tr, GetString(str_last_period, 1ULL << cargo_type, this->town->supplied[cargo_type].old_act, this->town->supplied[cargo_type].old_max)); DrawString(tr, GetString(str_last_period, CargoTypes{cargo_type}, this->town->supplied[cargo_type].old_act, this->town->supplied[cargo_type].old_max));
tr.top += GetCharacterHeight(FS_NORMAL); tr.top += GetCharacterHeight(FS_NORMAL);
} }
} }
@ -1616,12 +1616,12 @@ static CargoTypes GetProducedCargoOfHouse(const HouseSpec *hs)
uint amt = GB(callback, 0, 8); uint amt = GB(callback, 0, 8);
if (amt == 0) continue; if (amt == 0) continue;
SetBit(produced, cargo); produced.Set(cargo);
} }
} else { } else {
/* Cargo is not controlled by NewGRF, town production effect is used instead. */ /* Cargo is not controlled by NewGRF, town production effect is used instead. */
for (const CargoSpec *cs : CargoSpec::town_production_cargoes[TPE_PASSENGERS]) SetBit(produced, cs->Index()); for (const CargoSpec *cs : CargoSpec::town_production_cargoes[TPE_PASSENGERS]) produced.Set(cs->Index());
for (const CargoSpec *cs : CargoSpec::town_production_cargoes[TPE_MAIL]) SetBit(produced, cs->Index()); for (const CargoSpec *cs : CargoSpec::town_production_cargoes[TPE_MAIL]) produced.Set(cs->Index());
} }
return produced; return produced;
} }
@ -1707,7 +1707,7 @@ struct BuildHouseWindow : public PickerWindow {
} }
CargoTypes produced = GetProducedCargoOfHouse(hs); CargoTypes produced = GetProducedCargoOfHouse(hs);
if (produced != 0) { if (produced.Any()) {
line << "\n"; line << "\n";
line << GetString(STR_HOUSE_PICKER_CARGO_PRODUCED, produced); line << GetString(STR_HOUSE_PICKER_CARGO_PRODUCED, produced);
} }

View File

@ -246,10 +246,10 @@ bool Vehicle::NeedsServicing() const
CargoTypes available_cargo_types, union_mask; CargoTypes available_cargo_types, union_mask;
GetArticulatedRefitMasks(new_engine, true, &union_mask, &available_cargo_types); GetArticulatedRefitMasks(new_engine, true, &union_mask, &available_cargo_types);
/* Is there anything to refit? */ /* Is there anything to refit? */
if (union_mask != 0) { if (union_mask.Any()) {
CargoType cargo_type; CargoType cargo_type;
CargoTypes cargo_mask = GetCargoTypesOfArticulatedVehicle(v, &cargo_type); CargoTypes cargo_mask = GetCargoTypesOfArticulatedVehicle(v, &cargo_type);
if (!HasAtMostOneBit(cargo_mask)) { if (!HasAtMostOneBit(cargo_mask.base())) {
CargoTypes new_engine_default_cargoes = GetCargoTypesOfArticulatedParts(new_engine); CargoTypes new_engine_default_cargoes = GetCargoTypesOfArticulatedParts(new_engine);
if ((cargo_mask & new_engine_default_cargoes) != cargo_mask) { if ((cargo_mask & new_engine_default_cargoes) != cargo_mask) {
/* We cannot refit to mixed cargoes in an automated way */ /* We cannot refit to mixed cargoes in an automated way */
@ -260,7 +260,7 @@ bool Vehicle::NeedsServicing() const
/* Did the old vehicle carry anything? */ /* Did the old vehicle carry anything? */
if (IsValidCargoType(cargo_type)) { if (IsValidCargoType(cargo_type)) {
/* We can't refit the vehicle to carry the cargo we want */ /* We can't refit the vehicle to carry the cargo we want */
if (!HasBit(available_cargo_types, cargo_type)) continue; if (!available_cargo_types.Test(cargo_type)) continue;
} }
} }
} }

View File

@ -389,7 +389,7 @@ static std::tuple<CommandCost, uint, uint16_t, CargoArray> RefitVehicle(Vehicle
/* If the vehicle is not refittable, or does not allow automatic refitting, /* If the vehicle is not refittable, or does not allow automatic refitting,
* count its capacity nevertheless if the cargo matches */ * count its capacity nevertheless if the cargo matches */
bool refittable = HasBit(e->info.refit_mask, new_cargo_type) && (!auto_refit || e->info.misc_flags.Test(EngineMiscFlag::AutoRefit)); bool refittable = e->info.refit_mask.Test(new_cargo_type) && (!auto_refit || e->info.misc_flags.Test(EngineMiscFlag::AutoRefit));
if (!refittable && v->cargo_type != new_cargo_type) { if (!refittable && v->cargo_type != new_cargo_type) {
uint amount = e->DetermineCapacity(v, nullptr); uint amount = e->DetermineCapacity(v, nullptr);
if (amount > 0) cargo_capacities[v->cargo_type] += amount; if (amount > 0) cargo_capacities[v->cargo_type] += amount;

View File

@ -227,10 +227,10 @@ void BaseVehicleListWindow::BuildVehicleList()
GenerateVehicleSortList(&this->vehicles, this->vli); GenerateVehicleSortList(&this->vehicles, this->vli);
CargoTypes used = 0; CargoTypes used{};
for (const Vehicle *v : this->vehicles) { for (const Vehicle *v : this->vehicles) {
for (const Vehicle *u = v; u != nullptr; u = u->Next()) { for (const Vehicle *u = v; u != nullptr; u = u->Next()) {
if (u->cargo_cap > 0) SetBit(used, u->cargo_type); if (u->cargo_cap > 0) used.Set(u->cargo_type);
} }
} }
this->used_cargoes = used; this->used_cargoes = used;
@ -483,10 +483,10 @@ void BaseVehicleListWindow::OnInit()
StringID BaseVehicleListWindow::GetCargoFilterLabel(CargoType cargo_type) const StringID BaseVehicleListWindow::GetCargoFilterLabel(CargoType cargo_type) const
{ {
switch (cargo_type) { switch (cargo_type.base()) {
case CargoFilterCriteria::CF_ANY: return STR_CARGO_TYPE_FILTER_ALL; case CargoFilterCriteria::CF_ANY.base(): return STR_CARGO_TYPE_FILTER_ALL;
case CargoFilterCriteria::CF_FREIGHT: return STR_CARGO_TYPE_FILTER_FREIGHT; case CargoFilterCriteria::CF_FREIGHT.base(): return STR_CARGO_TYPE_FILTER_FREIGHT;
case CargoFilterCriteria::CF_NONE: return STR_CARGO_TYPE_FILTER_NONE; case CargoFilterCriteria::CF_NONE.base(): return STR_CARGO_TYPE_FILTER_NONE;
default: return CargoSpec::Get(cargo_type)->name; default: return CargoSpec::Get(cargo_type)->name;
} }
} }
@ -501,17 +501,17 @@ DropDownList BaseVehicleListWindow::BuildCargoDropDownList(bool full) const
DropDownList list; DropDownList list;
/* Add item for disabling filtering. */ /* Add item for disabling filtering. */
list.push_back(MakeDropDownListStringItem(this->GetCargoFilterLabel(CargoFilterCriteria::CF_ANY), CargoFilterCriteria::CF_ANY)); list.push_back(MakeDropDownListStringItem(this->GetCargoFilterLabel(CargoFilterCriteria::CF_ANY), CargoFilterCriteria::CF_ANY.base()));
/* Add item for freight (i.e. vehicles with cargo capacity and with no passenger capacity). */ /* Add item for freight (i.e. vehicles with cargo capacity and with no passenger capacity). */
list.push_back(MakeDropDownListStringItem(this->GetCargoFilterLabel(CargoFilterCriteria::CF_FREIGHT), CargoFilterCriteria::CF_FREIGHT)); list.push_back(MakeDropDownListStringItem(this->GetCargoFilterLabel(CargoFilterCriteria::CF_FREIGHT), CargoFilterCriteria::CF_FREIGHT.base()));
/* Add item for vehicles not carrying anything, e.g. train engines. */ /* Add item for vehicles not carrying anything, e.g. train engines. */
list.push_back(MakeDropDownListStringItem(this->GetCargoFilterLabel(CargoFilterCriteria::CF_NONE), CargoFilterCriteria::CF_NONE)); list.push_back(MakeDropDownListStringItem(this->GetCargoFilterLabel(CargoFilterCriteria::CF_NONE), CargoFilterCriteria::CF_NONE.base()));
/* Add cargos */ /* Add cargos */
Dimension d = GetLargestCargoIconSize(); Dimension d = GetLargestCargoIconSize();
for (const CargoSpec *cs : _sorted_cargo_specs) { for (const CargoSpec *cs : _sorted_cargo_specs) {
if (!full && !HasBit(this->used_cargoes, cs->Index())) continue; if (!full && !this->used_cargoes.Test(cs->Index())) continue;
list.push_back(MakeDropDownListIconItem(d, cs->GetCargoIcon(), PAL_NONE, cs->name, cs->Index(), false, !HasBit(this->used_cargoes, cs->Index()))); list.push_back(MakeDropDownListIconItem(d, cs->GetCargoIcon(), PAL_NONE, cs->name, cs->Index().base(), false, !this->used_cargoes.Test(cs->Index())));
} }
return list; return list;
@ -619,7 +619,7 @@ uint8_t GetBestFittingSubType(Vehicle *v_from, Vehicle *v_for, CargoType dest_ca
for (Vehicle *v = v_for; v != nullptr; v = v->HasArticulatedPart() ? v->GetNextArticulatedPart() : nullptr) { for (Vehicle *v = v_for; v != nullptr; v = v->HasArticulatedPart() ? v->GetNextArticulatedPart() : nullptr) {
const Engine *e = v->GetEngine(); const Engine *e = v->GetEngine();
if (!e->CanCarryCargo() || !e->info.callback_mask.Test(VehicleCallbackMask::CargoSuffix)) continue; if (!e->CanCarryCargo() || !e->info.callback_mask.Test(VehicleCallbackMask::CargoSuffix)) continue;
if (!HasBit(e->info.refit_mask, dest_cargo_type) && v->cargo_type != dest_cargo_type) continue; if (!e->info.refit_mask.Test(dest_cargo_type) && v->cargo_type != dest_cargo_type) continue;
CargoType old_cargo_type = v->cargo_type; CargoType old_cargo_type = v->cargo_type;
uint8_t old_cargo_subtype = v->cargo_subtype; uint8_t old_cargo_subtype = v->cargo_subtype;
@ -791,7 +791,7 @@ struct RefitWindow : public Window {
for (const auto &cs : _sorted_cargo_specs) { for (const auto &cs : _sorted_cargo_specs) {
CargoType cargo_type = cs->Index(); CargoType cargo_type = cs->Index();
/* Skip cargo type if it's not listed */ /* Skip cargo type if it's not listed */
if (!HasBit(cmask, cargo_type)) continue; if (!cmask.Test(cargo_type)) continue;
auto &list = this->refit_list[cargo_type]; auto &list = this->refit_list[cargo_type];
bool first_vehicle = list.empty(); bool first_vehicle = list.empty();
@ -1359,25 +1359,24 @@ void ShowVehicleRefitWindow(const Vehicle *v, VehicleOrderID order, Window *pare
uint ShowRefitOptionsList(int left, int right, int y, EngineID engine) uint ShowRefitOptionsList(int left, int right, int y, EngineID engine)
{ {
/* List of cargo types of this engine */ /* List of cargo types of this engine */
CargoTypes cmask = GetUnionOfArticulatedRefitMasks(engine, false); CargoTypes present = GetUnionOfArticulatedRefitMasks(engine, false);
/* List of cargo types available in this climate */
CargoTypes lmask = _cargo_mask;
/* Draw nothing if the engine is not refittable */ /* Draw nothing if the engine is not refittable */
if (HasAtMostOneBit(cmask)) return y; if (HasAtMostOneBit(present.base())) return y;
std::string str; std::string str;
if (cmask == lmask) { if (present == _cargo_mask) {
/* Engine can be refitted to all types in this climate */ /* Engine can be refitted to all types in this climate */
str = GetString(STR_PURCHASE_INFO_REFITTABLE_TO, STR_PURCHASE_INFO_ALL_TYPES, std::monostate{}); str = GetString(STR_PURCHASE_INFO_REFITTABLE_TO, STR_PURCHASE_INFO_ALL_TYPES, std::monostate{});
} else { } else {
/* Check if we are able to refit to more cargo types and unable to. If /* Check if we are able to refit to more cargo types and unable to. If
* so, invert the cargo types to list those that we can't refit to. */ * so, invert the cargo types to list those that we can't refit to. */
if (CountBits(cmask ^ lmask) < CountBits(cmask) && CountBits(cmask ^ lmask) <= 7) { CargoTypes excluded = CargoTypes{present}.Flip(_cargo_mask);
cmask ^= lmask; uint num_excluded = excluded.Count();
str = GetString(STR_PURCHASE_INFO_REFITTABLE_TO, STR_PURCHASE_INFO_ALL_BUT, cmask); if (num_excluded < present.Count() && num_excluded <= 7) {
str = GetString(STR_PURCHASE_INFO_REFITTABLE_TO, STR_PURCHASE_INFO_ALL_BUT, excluded);
} else { } else {
str = GetString(STR_PURCHASE_INFO_REFITTABLE_TO, STR_JUST_CARGO_LIST, cmask); str = GetString(STR_PURCHASE_INFO_REFITTABLE_TO, STR_JUST_CARGO_LIST, present);
} }
} }
@ -1793,12 +1792,12 @@ void BaseVehicleListWindow::DrawVehicleListItems(VehicleID selected_vehicle, int
if (_settings_client.gui.show_cargo_in_vehicle_lists) { if (_settings_client.gui.show_cargo_in_vehicle_lists) {
/* Get the cargoes the vehicle can carry */ /* Get the cargoes the vehicle can carry */
CargoTypes vehicle_cargoes = 0; CargoTypes vehicle_cargoes{};
for (auto u = v; u != nullptr; u = u->Next()) { for (auto u = v; u != nullptr; u = u->Next()) {
if (u->cargo_cap == 0) continue; if (u->cargo_cap == 0) continue;
SetBit(vehicle_cargoes, u->cargo_type); vehicle_cargoes.Set(u->cargo_type);
} }
if (!v->name.empty()) { if (!v->name.empty()) {
@ -2110,7 +2109,7 @@ public:
return; return;
case WID_VL_FILTER_BY_CARGO: // Cargo filter dropdown case WID_VL_FILTER_BY_CARGO: // Cargo filter dropdown
ShowDropDownList(this, this->BuildCargoDropDownList(false), this->cargo_filter_criteria, widget); ShowDropDownList(this, this->BuildCargoDropDownList(false), this->cargo_filter_criteria.base(), widget);
break; break;
case WID_VL_LIST: { // Matrix to show vehicles case WID_VL_LIST: { // Matrix to show vehicles
@ -2182,7 +2181,7 @@ public:
break; break;
case WID_VL_FILTER_BY_CARGO: case WID_VL_FILTER_BY_CARGO:
this->SetCargoFilter(index); this->SetCargoFilter(static_cast<CargoType>(index));
break; break;
case WID_VL_MANAGE_VEHICLES_DROPDOWN: case WID_VL_MANAGE_VEHICLES_DROPDOWN:

View File

@ -116,7 +116,7 @@ struct BaseVehicleListWindow : public Window {
void UpdateVehicleGroupBy(GroupBy group_by); void UpdateVehicleGroupBy(GroupBy group_by);
void SortVehicleList(); void SortVehicleList();
void BuildVehicleList(); void BuildVehicleList();
void SetCargoFilter(uint8_t index); void SetCargoFilter(CargoType index);
void SetCargoFilterArray(); void SetCargoFilterArray();
void FilterVehicleList(); void FilterVehicleList();
StringID GetCargoFilterLabel(CargoType cargo_type) const; StringID GetCargoFilterLabel(CargoType cargo_type) const;

View File

@ -1830,7 +1830,7 @@ void ViewportDoDraw(const Viewport &vp, int left, int top, int right, int bottom
dp.height = UnScaleByZoom(dp.height, zoom); dp.height = UnScaleByZoom(dp.height, zoom);
AutoRestoreBackup cur_dpi(_cur_dpi, &dp); AutoRestoreBackup cur_dpi(_cur_dpi, &dp);
if (vp.overlay != nullptr && vp.overlay->GetCargoMask() != 0 && vp.overlay->GetCompanyMask().Any()) { if (vp.overlay != nullptr && vp.overlay->GetCargoMask().Any() && vp.overlay->GetCompanyMask().Any()) {
/* translate to window coordinates */ /* translate to window coordinates */
dp.left = x; dp.left = x;
dp.top = y; dp.top = y;
@ -2504,7 +2504,7 @@ void RebuildViewportOverlay(Window *w)
{ {
if (w->viewport->overlay != nullptr && if (w->viewport->overlay != nullptr &&
w->viewport->overlay->GetCompanyMask().Any() && w->viewport->overlay->GetCompanyMask().Any() &&
w->viewport->overlay->GetCargoMask() != 0) { w->viewport->overlay->GetCargoMask().Any()) {
w->viewport->overlay->SetDirty(); w->viewport->overlay->SetDirty();
w->SetDirty(); w->SetDirty();
} }