Skip to content

Commit

Permalink
Change: Decouple and remove landscape-dependent cargo types.
Browse files Browse the repository at this point in the history
Cargo types of default engines, industries and houses are now specified in terms of label.
  • Loading branch information
PeterN committed Jan 28, 2024
1 parent 3b92290 commit eeff2ee
Show file tree
Hide file tree
Showing 32 changed files with 381 additions and 278 deletions.
9 changes: 7 additions & 2 deletions src/aircraft_cmd.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -306,11 +306,16 @@ CommandCost CmdBuildAircraft(DoCommandFlag flags, TileIndex tile, const Engine *

v->cargo_cap = avi->passenger_capacity;
v->refit_cap = 0;
u->cargo_cap = avi->mail_capacity;
u->refit_cap = 0;

v->cargo_type = e->GetDefaultCargoType();
u->cargo_type = CT_MAIL;
assert(IsValidCargoID(v->cargo_type));

CargoID mail = GetCargoIDByLabel(CT_MAIL);
if (IsValidCargoID(mail)) {
u->cargo_type = mail;
u->cargo_cap = avi->mail_capacity;
}

v->name.clear();
v->last_station_visited = INVALID_STATION;
Expand Down
1 change: 1 addition & 0 deletions src/articulated_vehicles.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -398,6 +398,7 @@ void AddArticulatedParts(Vehicle *first)
rv->spritenum = e_artic->u.road.image_index;
if (e_artic->CanCarryCargo()) {
rv->cargo_type = e_artic->GetDefaultCargoType();
assert(IsValidCargoID(rv->cargo_type));
rv->cargo_cap = e_artic->u.road.capacity; // Callback 36 is called when the consist is finished
} else {
rv->cargo_type = front->cargo_type; // Needed for livery selection
Expand Down
8 changes: 6 additions & 2 deletions src/build_vehicle_gui.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -820,7 +820,7 @@ static int DrawAircraftPurchaseInfo(int left, int right, int y, EngineID engine_
if (te.mail_capacity > 0) {
SetDParam(0, te.cargo);
SetDParam(1, te.capacity);
SetDParam(2, CT_MAIL);
SetDParam(2, GetCargoIDByLabel(CT_MAIL));
SetDParam(3, te.mail_capacity);
DrawString(left, right, y, STR_PURCHASE_INFO_AIRCRAFT_CAPACITY);
} else {
Expand Down Expand Up @@ -902,7 +902,11 @@ void TestedEngineDetails::FillDefaultCapacities(const Engine *e)
} else {
this->capacity = e->GetDisplayDefaultCapacity(&this->mail_capacity);
this->all_capacities[this->cargo] = this->capacity;
this->all_capacities[CT_MAIL] = this->mail_capacity;
if (IsValidCargoID(GetCargoIDByLabel(CT_MAIL))) {
this->all_capacities[GetCargoIDByLabel(CT_MAIL)] = this->mail_capacity;
} else {
this->mail_capacity = 0;
}
}
if (this->all_capacities.GetCount() == 0) this->cargo = INVALID_CARGO;
}
Expand Down
104 changes: 55 additions & 49 deletions src/cargo_type.h
Original file line number Diff line number Diff line change
Expand Up @@ -11,58 +11,64 @@
#define CARGO_TYPE_H

#include "core/enum_type.hpp"
#include "core/strong_typedef_type.hpp"

/** Globally unique label of a cargo type. */
using CargoLabel = StrongType::Typedef<uint32_t, struct CargoLabelTag, true, StrongType::CompareSelf>;

/**
* Cargo slots to indicate a cargo type within a game.
* Numbers are re-used between different climates.
* @see CargoTypes
*/
typedef byte CargoID;

/** Available types of cargo */
enum CargoType {
/* Temperate */
CT_PASSENGERS = 0,
CT_COAL = 1,
CT_MAIL = 2,
CT_OIL = 3,
CT_LIVESTOCK = 4,
CT_GOODS = 5,
CT_GRAIN = 6,
CT_WOOD = 7,
CT_IRON_ORE = 8,
CT_STEEL = 9,
CT_VALUABLES = 10,

/* Arctic */
CT_WHEAT = 6,
CT_HILLY_UNUSED = 8,
CT_PAPER = 9,
CT_GOLD = 10,
CT_FOOD = 11,

/* Tropic */
CT_RUBBER = 1,
CT_FRUIT = 4,
CT_MAIZE = 6,
CT_COPPER_ORE = 8,
CT_WATER = 9,
CT_DIAMONDS = 10,

/* Toyland */
CT_SUGAR = 1,
CT_TOYS = 3,
CT_BATTERIES = 4,
CT_CANDY = 5,
CT_TOFFEE = 6,
CT_COLA = 7,
CT_COTTON_CANDY = 8,
CT_BUBBLES = 9,
CT_PLASTIC = 10,
CT_FIZZY_DRINKS = 11,

CT_INVALID = 0xFF, ///< Invalid cargo type.
};
using CargoID = byte;

/**
* Available types of cargo
* Labels may be re-used between different climates.
*/

/* Temperate */
static constexpr CargoLabel CT_PASSENGERS = CargoLabel{'PASS'};
static constexpr CargoLabel CT_COAL = CargoLabel{'COAL'};
static constexpr CargoLabel CT_MAIL = CargoLabel{'MAIL'};
static constexpr CargoLabel CT_OIL = CargoLabel{'OIL_'};
static constexpr CargoLabel CT_LIVESTOCK = CargoLabel{'LVST'};
static constexpr CargoLabel CT_GOODS = CargoLabel{'GOOD'};
static constexpr CargoLabel CT_GRAIN = CargoLabel{'GRAI'};
static constexpr CargoLabel CT_WOOD = CargoLabel{'WOOD'};
static constexpr CargoLabel CT_IRON_ORE = CargoLabel{'IORE'};
static constexpr CargoLabel CT_STEEL = CargoLabel{'STEL'};
static constexpr CargoLabel CT_VALUABLES = CargoLabel{'VALU'};

/* Arctic */
static constexpr CargoLabel CT_WHEAT = CargoLabel{'WHEA'};
static constexpr CargoLabel CT_PAPER = CargoLabel{'PAPR'};
static constexpr CargoLabel CT_GOLD = CargoLabel{'GOLD'};
static constexpr CargoLabel CT_FOOD = CargoLabel{'FOOD'};

/* Tropic */
static constexpr CargoLabel CT_RUBBER = CargoLabel{'RUBR'};
static constexpr CargoLabel CT_FRUIT = CargoLabel{'FRUI'};
static constexpr CargoLabel CT_MAIZE = CargoLabel{'MAIZ'};
static constexpr CargoLabel CT_COPPER_ORE = CargoLabel{'CORE'};
static constexpr CargoLabel CT_WATER = CargoLabel{'WATR'};
static constexpr CargoLabel CT_DIAMONDS = CargoLabel{'DIAM'};

/* Toyland */
static constexpr CargoLabel CT_SUGAR = CargoLabel{'SUGR'};
static constexpr CargoLabel CT_TOYS = CargoLabel{'TOYS'};
static constexpr CargoLabel CT_BATTERIES = CargoLabel{'BATT'};
static constexpr CargoLabel CT_CANDY = CargoLabel{'SWET'};
static constexpr CargoLabel CT_TOFFEE = CargoLabel{'TOFF'};
static constexpr CargoLabel CT_COLA = CargoLabel{'COLA'};
static constexpr CargoLabel CT_COTTON_CANDY = CargoLabel{'CTCD'};
static constexpr CargoLabel CT_BUBBLES = CargoLabel{'BUBL'};
static constexpr CargoLabel CT_PLASTIC = CargoLabel{'PLST'};
static constexpr CargoLabel CT_FIZZY_DRINKS = CargoLabel{'FZDR'};

/** Dummy label for engines that carry no cargo; they actually carry 0 passengers. */
static constexpr CargoLabel CT_NONE = CT_PASSENGERS;

static constexpr CargoLabel CT_INVALID = CargoLabel{UINT32_MAX}; ///< Invalid cargo type.

static const CargoID NUM_ORIGINAL_CARGO = 12; ///< Original number of cargo types.
static const CargoID NUM_CARGO = 64; ///< Maximum number of cargo types in a game.
Expand All @@ -85,7 +91,7 @@ namespace CargoFilterCriteria {
};

/** Test whether cargo type is not CT_INVALID */
inline bool IsValidCargoType(CargoType t) { return t != CT_INVALID; }
inline bool IsValidCargoType(CargoLabel t) { return t != CT_INVALID; }
/** Test whether cargo type is not INVALID_CARGO */
inline bool IsValidCargoID(CargoID t) { return t != INVALID_CARGO; }

Expand Down
83 changes: 45 additions & 38 deletions src/cargotype.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,12 @@ CargoTypes _cargo_mask;
*/
CargoTypes _standard_cargo_mask;

/**
* List of default cargo labels, used when setting up cargo types for default vehicles.
* This is done by label so that a cargo label can be redefined in a different slot.
*/
static std::vector<CargoLabel> _default_cargo_labels;

/**
* Set up the default cargo types for the given landscape type.
* @param l Landscape
Expand All @@ -44,18 +50,20 @@ void SetupCargoForClimate(LandscapeID l)
assert(l < lengthof(_default_climate_cargo));

_cargo_mask = 0;
_default_cargo_labels.clear();

/* Copy from default cargo by label or index. */
auto insert = std::begin(CargoSpec::array);
for (const CargoLabel &cl : _default_climate_cargo[l]) {
for (const auto &cl : _default_climate_cargo[l]) {

/* Check if value is an index into the cargo table */
if (cl < lengthof(_default_cargo)) {
if (std::holds_alternative<int>(cl)) {
/* Copy the default cargo by index. */
*insert = _default_cargo[cl];
*insert = _default_cargo[std::get<int>(cl)];
} else {
/* Search for label in default cargo types and copy if found. */
auto found = std::find_if(std::begin(_default_cargo), std::end(_default_cargo), [&cl](const CargoSpec &cs) { return cs.label == cl; });
CargoLabel label = std::get<CargoLabel>(cl);
auto found = std::find_if(std::begin(_default_cargo), std::end(_default_cargo), [&label](const CargoSpec &cs) { return cs.label == label; });
if (found != std::end(_default_cargo)) {
*insert = *found;
} else {
Expand All @@ -64,65 +72,64 @@ void SetupCargoForClimate(LandscapeID l)
}
}

if (insert->IsValid()) SetBit(_cargo_mask, insert->Index());
if (insert->IsValid()) {
SetBit(_cargo_mask, insert->Index());
_default_cargo_labels.push_back(insert->label);
}
++insert;
}

/* Reset and disable remaining cargo types. */
std::fill(insert, std::end(CargoSpec::array), CargoSpec{});

BuildCargoLabelMap();
}

/**
* Get dimensions of largest cargo icon.
* @return Dimensions of largest cargo icon.
* Build cargo label map.
* This is called multiple times during NewGRF initialization as cargos are defined, so that TranslateRefitMask() and
* GetCargoTranslation(), also used during initialization, get the correct information.
*/
Dimension GetLargestCargoIconSize()
void BuildCargoLabelMap()
{
Dimension size = {0, 0};
for (const CargoSpec *cs : _sorted_cargo_specs) {
size = maxdim(size, GetSpriteSize(cs->GetCargoIcon()));
CargoSpec::label_map.clear();
for (const CargoSpec &cs : CargoSpec::array) {
/* During initialization, CargoSpec can be marked valid before the label has been set. */
if (!cs.IsValid() || cs.label == CargoLabel{0}) continue;
/* Label already exists, don't addd again. */
if (CargoSpec::label_map.count(cs.label) != 0) continue;

CargoSpec::label_map.insert(std::make_pair(cs.label, cs.Index()));
}
return size;
}

/**
* Get the cargo ID of a default cargo, if present.
* @param l Landscape
* @param ct Default cargo type.
* @return ID number if the cargo exists, else #INVALID_CARGO
* Test if a cargo is a default cargo type.
* @param cid Cargo ID.
* @returns true iff the cargo type is a default cargo type.
*/
CargoID GetDefaultCargoID(LandscapeID l, CargoType ct)
bool IsDefaultCargo(CargoID cid)
{
assert(l < lengthof(_default_climate_cargo));

if (!IsValidCargoType(ct)) return INVALID_CARGO;
auto cs = CargoSpec::Get(cid);
if (!cs->IsValid()) return false;

assert(ct < lengthof(_default_climate_cargo[0]));
CargoLabel cl = _default_climate_cargo[l][ct];
/* Bzzt: check if cl is just an index into the cargo table */
if (cl < lengthof(_default_cargo)) {
cl = _default_cargo[cl].label;
}

return GetCargoIDByLabel(cl);
CargoLabel label = cs->label;
return std::any_of(std::begin(_default_cargo_labels), std::end(_default_cargo_labels), [&label](const CargoLabel &cl) { return cl == label; });
}

/**
* Get the cargo ID by cargo label.
* @param cl Cargo type to get.
* @return ID number if the cargo exists, else #INVALID_CARGO
* Get dimensions of largest cargo icon.
* @return Dimensions of largest cargo icon.
*/
CargoID GetCargoIDByLabel(CargoLabel cl)
Dimension GetLargestCargoIconSize()
{
for (const CargoSpec *cs : CargoSpec::Iterate()) {
if (cs->label == cl) return cs->Index();
Dimension size = {0, 0};
for (const CargoSpec *cs : _sorted_cargo_specs) {
size = maxdim(size, GetSpriteSize(cs->GetCargoIcon()));
}

/* No matching label was found, so it is invalid */
return INVALID_CARGO;
return size;
}


/**
* Find the CargoID of a 'bitnum' value.
* @param bitnum 'bitnum' to find.
Expand Down
18 changes: 13 additions & 5 deletions src/cargotype.h
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,6 @@
#include "landscape_type.h"
#include "core/bitmath_func.hpp"

/** Globally unique label of a cargo type. */
typedef uint32_t CargoLabel;

/** Town growth effect when delivering cargo. */
enum TownAcceptanceEffect : byte {
TAE_BEGIN = 0,
Expand Down Expand Up @@ -191,18 +188,29 @@ struct CargoSpec {

private:
static CargoSpec array[NUM_CARGO]; ///< Array holding all CargoSpecs
static inline std::map<CargoLabel, CargoID> label_map{}; ///< Translation map from CargoLabel to Cargo ID.

friend void SetupCargoForClimate(LandscapeID l);
friend void BuildCargoLabelMap();
friend inline CargoID GetCargoIDByLabel(CargoLabel ct);
friend void FinaliseCargoArray();
};

extern CargoTypes _cargo_mask;
extern CargoTypes _standard_cargo_mask;

void SetupCargoForClimate(LandscapeID l);
CargoID GetCargoIDByLabel(CargoLabel cl);
bool IsDefaultCargo(CargoID cid);
void BuildCargoLabelMap();
CargoID GetCargoIDByBitnum(uint8_t bitnum);
CargoID GetDefaultCargoID(LandscapeID l, CargoType ct);

inline CargoID GetCargoIDByLabel(CargoLabel label)
{
auto found = CargoSpec::label_map.find(label);
if (found != std::end(CargoSpec::label_map)) return found->second;
return INVALID_CARGO;
}

Dimension GetLargestCargoIconSize();

void InitializeSortedCargoSpecs();
Expand Down
2 changes: 1 addition & 1 deletion src/console_cmds.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2535,7 +2535,7 @@ static void ConDumpCargoTypes()
IConsolePrint(CC_DEFAULT, " {:02d} Bit: {:2d}, Label: {:c}{:c}{:c}{:c}, Callback mask: 0x{:02X}, Cargo class: {}{}{}{}{}{}{}{}{}{}{}, GRF: {:08X}, {}",
spec->Index(),
spec->bitnum,
spec->label >> 24, spec->label >> 16, spec->label >> 8, spec->label,
spec->label.base() >> 24, spec->label.base() >> 16, spec->label.base() >> 8, spec->label.base(),
spec->callback_mask,
(spec->classes & CC_PASSENGERS) != 0 ? 'p' : '-',
(spec->classes & CC_MAIL) != 0 ? 'm' : '-',
Expand Down
10 changes: 6 additions & 4 deletions src/engine.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -242,8 +242,10 @@ uint Engine::DetermineCapacity(const Vehicle *v, uint16_t *mail_capacity) const
if (!IsCargoInClass(cargo_type, CC_PASSENGERS)) {
extra_mail_cap = GetEngineProperty(this->index, PROP_AIRCRAFT_MAIL_CAPACITY, this->u.air.mail_capacity, v);
}
if (!new_multipliers && cargo_type == CT_MAIL) return capacity + extra_mail_cap;
default_cargo = CT_PASSENGERS; // Always use 'passengers' wrt. cargo multipliers
if (IsValidCargoID(GetCargoIDByLabel(CT_MAIL))) {
if (!new_multipliers && cargo_type == GetCargoIDByLabel(CT_MAIL)) return capacity + extra_mail_cap;
}
default_cargo = GetCargoIDByLabel(CT_PASSENGERS); // Always use 'passengers' wrt. cargo multipliers
break;

default: NOT_REACHED();
Expand All @@ -260,8 +262,8 @@ uint Engine::DetermineCapacity(const Vehicle *v, uint16_t *mail_capacity) const
uint16_t default_multiplier = new_multipliers ? 0x100 : CargoSpec::Get(default_cargo)->multiplier;
uint16_t cargo_multiplier = CargoSpec::Get(cargo_type)->multiplier;
capacity *= cargo_multiplier;
if (extra_mail_cap > 0) {
uint mail_multiplier = CargoSpec::Get(CT_MAIL)->multiplier;
if (extra_mail_cap > 0 && IsValidCargoID(GetCargoIDByLabel(CT_MAIL))) {
uint mail_multiplier = CargoSpec::Get(GetCargoIDByLabel(CT_MAIL))->multiplier;
capacity += (default_multiplier * extra_mail_cap * cargo_multiplier + mail_multiplier / 2) / mail_multiplier;
}
capacity = (capacity + default_multiplier / 2) / default_multiplier;
Expand Down
2 changes: 1 addition & 1 deletion src/engine_gui.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -204,7 +204,7 @@ static StringID GetAircraftEngineInfoString(const Engine *e)
SetDParam(i++, capacity);

if (mail_capacity > 0) {
SetDParam(i++, CT_MAIL);
SetDParam(i++, GetCargoIDByLabel(CT_MAIL));
SetDParam(i++, mail_capacity);
SetDParam(i++, e->GetRunningCost());
return range > 0 ? STR_ENGINE_PREVIEW_COST_MAX_SPEED_TYPE_RANGE_CAP_CAP_RUNCOST : STR_ENGINE_PREVIEW_COST_MAX_SPEED_TYPE_CAP_CAP_RUNCOST;
Expand Down
1 change: 1 addition & 0 deletions src/engine_type.h
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,7 @@ struct EngineInfo {
byte load_amount;
byte climates; ///< Climates supported by the engine.
CargoID cargo_type;
CargoLabel cargo_label;
CargoTypes refit_mask;
byte refit_cost;
byte misc_flags; ///< Miscellaneous flags. @see EngineMiscFlags
Expand Down

0 comments on commit eeff2ee

Please sign in to comment.