diff --git a/CMakeLists.txt b/CMakeLists.txt index aefd9f46a3..388c7695c0 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -350,6 +350,10 @@ if(NOT OPTION_NO_SPLIT_LIB) target_link_libraries(openttd openttd_lib) target_link_libraries(openttd_test PRIVATE openttd_lib) + if(ANDROID) + target_link_libraries(openttd_test PRIVATE log) + endif() + include(Catch) catch_discover_tests(openttd_test) endif() diff --git a/changelog.txt b/changelog.txt index 93f314b3df..046c3868e4 100644 --- a/changelog.txt +++ b/changelog.txt @@ -1,3 +1,15 @@ +14.0-beta2 (2023-02-04) +------------------------------------------------------------------------ +Change: [NewGRF] Improved support for redefining default cargo types (#11719) +Fix #11982: Crash when trying to place signals on things other than plain rails (#11977) +Fix #11975: Inconsistent behaviour when changing first AI company settings (#11976) +Fix #11972: Year cut off in graph windows (#11974) +Fix #11968: Crash when opening orders window of new vehicles (#11973) +Fix #11966: Monospace text in windows may not have been fully scrollable (#11981) +Fix #11802: Made determining water region edge traversability more robust (#11986) +Fix: Second colour vehicle-type default liveries were not being updated (#11971) + + 14.0-beta1 (2023-02-03) ------------------------------------------------------------------------ Feature: Order option to unbunch vehicles at depot (#11945) diff --git a/src/ai/ai_gui.cpp b/src/ai/ai_gui.cpp index 28c2f8adc1..0ae1651574 100644 --- a/src/ai/ai_gui.cpp +++ b/src/ai/ai_gui.cpp @@ -163,6 +163,17 @@ struct AIConfigWindow : public Window { switch (widget) { case WID_AIC_LIST: { Rect tr = r.Shrink(WidgetDimensions::scaled.matrix); + int max_slot = GetGameSettings().difficulty.max_no_competitors; + if (_game_mode == GM_NORMAL) { + for (const Company *c : Company::Iterate()) { + if (c->is_ai) max_slot--; + } + for (CompanyID cid = COMPANY_FIRST; cid < (CompanyID)max_slot && cid < MAX_COMPANIES; cid++) { + if (Company::IsValidID(cid)) max_slot++; + } + } else { + max_slot++; // Slot 0 is human + } for (int i = this->vscroll->GetPosition(); this->vscroll->IsVisible(i) && i < MAX_COMPANIES; i++) { StringID text; @@ -179,13 +190,6 @@ struct AIConfigWindow : public Window { if (this->selected_slot == i) { tc = TC_WHITE; } else if (IsEditable((CompanyID)i)) { - int max_slot = GetGameSettings().difficulty.max_no_competitors; - for (const Company *c : Company::Iterate()) { - if (c->is_ai) max_slot--; - } - for (CompanyID cid = COMPANY_FIRST; cid < (CompanyID)max_slot && cid < MAX_COMPANIES; cid++) { - if (Company::IsValidHumanID(cid)) max_slot++; - } if (i < max_slot) tc = TC_ORANGE; } else if (Company::IsValidAiID(i)) { tc = TC_GREEN; diff --git a/src/aircraft_cmd.cpp b/src/aircraft_cmd.cpp index 8827197ef6..8c7259507a 100644 --- a/src/aircraft_cmd.cpp +++ b/src/aircraft_cmd.cpp @@ -319,11 +319,16 @@ CommandCost CmdBuildAircraft(TileIndex tile, DoCommandFlag flags, 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; diff --git a/src/articulated_vehicles.cpp b/src/articulated_vehicles.cpp index c970969f8e..a3bb481b66 100644 --- a/src/articulated_vehicles.cpp +++ b/src/articulated_vehicles.cpp @@ -479,6 +479,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 diff --git a/src/build_vehicle_gui.cpp b/src/build_vehicle_gui.cpp index 09ac34f811..49d1820946 100644 --- a/src/build_vehicle_gui.cpp +++ b/src/build_vehicle_gui.cpp @@ -1035,7 +1035,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 { @@ -1117,7 +1117,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; } diff --git a/src/cargo_type.h b/src/cargo_type.h index 31fa579667..852c9c1d6e 100644 --- a/src/cargo_type.h +++ b/src/cargo_type.h @@ -11,61 +11,67 @@ #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; #include #include /** * 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. @@ -88,7 +94,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; } diff --git a/src/cargotype.cpp b/src/cargotype.cpp index 894b685096..8160196b4c 100644 --- a/src/cargotype.cpp +++ b/src/cargotype.cpp @@ -15,6 +15,7 @@ #include "string_func.h" #include "strings_func.h" #include "settings_type.h" +#include "3rdparty/cpp-btree/btree_map.h" #include "table/sprites.h" #include "table/strings.h" @@ -36,6 +37,16 @@ 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 _default_cargo_labels; + +static btree::btree_map _cargo_label_map; ///< Translation map from CargoLabel to Cargo ID. +CargoID _cargo_id_passengers = INVALID_CARGO; +CargoID _cargo_id_mail = INVALID_CARGO; + /** * Set up the default cargo types for the given landscape type. * @param l Landscape @@ -45,18 +56,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(cl)) { /* Copy the default cargo by index. */ - *insert = _default_cargo[cl]; + *insert = _default_cargo[std::get(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(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 { @@ -65,65 +78,73 @@ 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())); + _cargo_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 add again. */ + if (_cargo_label_map.count(cs.label) != 0) continue; + + _cargo_label_map.insert(std::make_pair(cs.label, cs.Index())); } - return size; + _cargo_id_passengers = GetCargoIDByLabelUsingMap(CT_PASSENGERS); + _cargo_id_mail = GetCargoIDByLabelUsingMap(CT_MAIL); } /** - * 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)); + auto cs = CargoSpec::Get(cid); + if (!cs->IsValid()) return false; - if (ct == CT_INVALID) return INVALID_CARGO; - - 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())); } + return size; +} - /* No matching label was found, so it is invalid */ +CargoID GetCargoIDByLabelUsingMap(CargoLabel label) +{ + auto found = _cargo_label_map.find(label); + if (found != _cargo_label_map.end()) return found->second; return INVALID_CARGO; } - /** * Find the CargoID of a 'bitnum' value. * @param bitnum 'bitnum' to find. diff --git a/src/cargotype.h b/src/cargotype.h index 0756b49ca9..7381c16e25 100644 --- a/src/cargotype.h +++ b/src/cargotype.h @@ -18,9 +18,6 @@ #include "core/bitmath_func.hpp" #include -/** Globally unique label of a cargo type. */ -typedef uint32_t CargoLabel; - /** Town growth effect when delivering cargo. */ enum TownAcceptanceEffect : byte { TAE_BEGIN = 0, @@ -206,6 +203,7 @@ private: static CargoSpec array[NUM_CARGO]; ///< Array holding all CargoSpecs friend void SetupCargoForClimate(LandscapeID l); + friend void BuildCargoLabelMap(); friend void FinaliseCargoArray(); }; @@ -213,9 +211,21 @@ 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); + +CargoID GetCargoIDByLabelUsingMap(CargoLabel label); + +inline CargoID GetCargoIDByLabel(CargoLabel label) +{ + extern CargoID _cargo_id_passengers; + extern CargoID _cargo_id_mail; + if (label == CT_PASSENGERS) return _cargo_id_passengers; + if (label == CT_MAIL) return _cargo_id_mail; + return GetCargoIDByLabelUsingMap(label); +} + Dimension GetLargestCargoIconSize(); void InitializeSortedCargoSpecs(); diff --git a/src/console_cmds.cpp b/src/console_cmds.cpp index 5a3ecfd54e..fb0ae2be6b 100644 --- a/src/console_cmds.cpp +++ b/src/console_cmds.cpp @@ -3047,7 +3047,7 @@ DEF_CONSOLE_CMD(ConDumpCargoTypes) IConsolePrintF(CC_DEFAULT, " %02u Bit: %2u, Label: %c%c%c%c, Callback mask: 0x%02X, Cargo class: %c%c%c%c%c%c%c%c%c%c%c, GRF: %08X, %s", (uint) i, 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' : '-', @@ -3161,10 +3161,10 @@ DEF_CONSOLE_CMD(ConDumpGrfCargoTables) char *b = buffer; for (const CargoSpec *cs : CargoSpec::Iterate()) { if (grf->cargo_map[cs->Index()] == i) { - b += seprintf(b, lastof(buffer), "%s%02u[%c%c%c%c]", (b == buffer) ? ": " : ", ", cs->Index(), GB(cs->label, 24, 8), GB(cs->label, 16, 8), GB(cs->label, 8, 8), GB(cs->label, 0, 8)); + b += seprintf(b, lastof(buffer), "%s%02u[%c%c%c%c]", (b == buffer) ? ": " : ", ", cs->Index(), GB(cs->label.base(), 24, 8), GB(cs->label.base(), 16, 8), GB(cs->label.base(), 8, 8), GB(cs->label.base(), 0, 8)); } } - IConsolePrintF(CC_DEFAULT, " %c%c%c%c%s", GB(cl, 24, 8), GB(cl, 16, 8), GB(cl, 8, 8), GB(cl, 0, 8), buffer); + IConsolePrintF(CC_DEFAULT, " %c%c%c%c%s", GB(cl.base(), 24, 8), GB(cl.base(), 16, 8), GB(cl.base(), 8, 8), GB(cl.base(), 0, 8), buffer); i++; } } diff --git a/src/engine.cpp b/src/engine.cpp index 700115ab26..8534558332 100644 --- a/src/engine.cpp +++ b/src/engine.cpp @@ -283,8 +283,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(); @@ -301,8 +303,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; diff --git a/src/engine_gui.cpp b/src/engine_gui.cpp index 592113845f..7f36ca89aa 100644 --- a/src/engine_gui.cpp +++ b/src/engine_gui.cpp @@ -254,7 +254,7 @@ static StringID GetAircraftEngineInfoString(const Engine *e) SetDParam(9, mail_capacity > 0 ? STR_ENGINE_PREVIEW_CAPACITY_2 : STR_ENGINE_PREVIEW_CAPACITY); SetDParam(10, cargo); SetDParam(11, capacity); - SetDParam(12, CT_MAIL); + SetDParam(12, GetCargoIDByLabel(CT_MAIL)); SetDParam(13, mail_capacity); return STR_ENGINE_PREVIEW_TEXT4; diff --git a/src/engine_type.h b/src/engine_type.h index f0c09ccb53..b9b484fe30 100644 --- a/src/engine_type.h +++ b/src/engine_type.h @@ -149,6 +149,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 diff --git a/src/graph_gui.cpp b/src/graph_gui.cpp index e78d5acdc4..b86222ceee 100644 --- a/src/graph_gui.cpp +++ b/src/graph_gui.cpp @@ -570,7 +570,7 @@ public: SetDParam(1, INT64_MAX); uint y_label_width = GetStringBoundingBox(STR_GRAPH_Y_LABEL).width; - size->width = std::max(size->width, ScaleGUITrad(5) + y_label_width + this->num_on_x_axis * (x_label_width + ScaleGUITrad(5)) + ScaleGUITrad(9)); + size->width = std::max(size->width, ScaleGUITrad(5) + y_label_width + this->num_vert_lines * (x_label_width + ScaleGUITrad(5)) + ScaleGUITrad(9)); size->height = std::max(size->height, ScaleGUITrad(5) + (1 + MIN_GRAPH_NUM_LINES_Y * 2 + (this->draw_dates ? 3 : 1)) * GetCharacterHeight(FS_SMALL) + ScaleGUITrad(4)); size->height = std::max(size->height, size->width / 3); } @@ -1488,7 +1488,7 @@ struct PaymentRatesGraphWindow : BaseGraphWindow { for (const CargoSpec *cs : _sorted_standard_cargo_specs) { this->colours[i] = cs->legend_colour; - for (int j = 0; j != 20; j++) { + for (int j = 0; j != this->num_on_x_axis; j++) { const byte ctt = _cargo_payment_x_mode ? static_cast(factor / static_cast((j + 1) * this->x_values_increment)) : (j + 1) * 4; this->cost[i][j] = GetTransportedGoodsIncome(_cargo_payment_x_mode ? 1 : 10, _cargo_payment_x_mode ? 200 : 20, ctt, cs->Index()); } diff --git a/src/house.h b/src/house.h index 33794382f5..f13bdee4ff 100644 --- a/src/house.h +++ b/src/house.h @@ -113,6 +113,7 @@ struct HouseSpec { byte mail_generation; ///< mail generation multiplier (tile based, as the acceptances below) byte cargo_acceptance[HOUSE_NUM_ACCEPTS]; ///< acceptance level for the cargo slots CargoID accepts_cargo[HOUSE_NUM_ACCEPTS]; ///< input cargo slots + CargoLabel accepts_cargo_label[HOUSE_NUM_ACCEPTS]; ///< input landscape cargo slots BuildingFlags building_flags; ///< some flags that describe the house (size, stadium etc...) HouseZones building_availability; ///< where can it be built (climates, zones) bool enabled; ///< the house is available to build (true by default, but can be disabled by newgrf) diff --git a/src/industry_cmd.cpp b/src/industry_cmd.cpp index 210ad37585..61ddc47635 100644 --- a/src/industry_cmd.cpp +++ b/src/industry_cmd.cpp @@ -1025,7 +1025,7 @@ bool IsTileForestIndustry(TileIndex tile) /* Check for wood production */ for (uint i = 0; i < lengthof(ind->produced_cargo); i++) { /* The industry produces wood. */ - if (ind->produced_cargo[i] != INVALID_CARGO && CargoSpec::Get(ind->produced_cargo[i])->label == 'WOOD') return true; + if (ind->produced_cargo[i] != INVALID_CARGO && CargoSpec::Get(ind->produced_cargo[i])->label == CT_WOOD) return true; } return false; @@ -1914,7 +1914,7 @@ static void DoCreateNewIndustry(Industry *i, TileIndex tile, IndustryType type, uint maxcargoes = (indspec->behaviour & INDUSTRYBEH_CARGOTYPES_UNLIMITED) ? lengthof(i->accepts_cargo) : 3; for (uint j = 0; j < maxcargoes; j++) { uint16_t res = GetIndustryCallback(CBID_INDUSTRY_INPUT_CARGO_TYPES, j, 0, i, type, INVALID_TILE); - if (res == CALLBACK_FAILED || GB(res, 0, 8) == CT_INVALID) break; + if (res == CALLBACK_FAILED || GB(res, 0, 8) == UINT8_MAX) break; if (indspec->grf_prop.grffile->grf_version >= 8 && res >= 0x100) { ErrorUnknownCallbackResult(indspec->grf_prop.grffile->grfid, CBID_INDUSTRY_INPUT_CARGO_TYPES, res); break; @@ -1946,7 +1946,7 @@ static void DoCreateNewIndustry(Industry *i, TileIndex tile, IndustryType type, uint maxcargoes = (indspec->behaviour & INDUSTRYBEH_CARGOTYPES_UNLIMITED) ? lengthof(i->produced_cargo) : 2; for (uint j = 0; j < maxcargoes; j++) { uint16_t res = GetIndustryCallback(CBID_INDUSTRY_OUTPUT_CARGO_TYPES, j, 0, i, type, INVALID_TILE); - if (res == CALLBACK_FAILED || GB(res, 0, 8) == CT_INVALID) break; + if (res == CALLBACK_FAILED || GB(res, 0, 8) == UINT8_MAX) break; if (indspec->grf_prop.grffile->grf_version >= 8 && res >= 0x100) { ErrorUnknownCallbackResult(indspec->grf_prop.grffile->grfid, CBID_INDUSTRY_OUTPUT_CARGO_TYPES, res); break; @@ -2956,7 +2956,7 @@ static void ChangeIndustryProduction(Industry *i, bool monthly) /* Prevent production to overflow or Oil Rig passengers to be over-"produced" */ new_prod = Clamp(new_prod, 1, 255); - if (i->produced_cargo[j] == CT_PASSENGERS && !(indspec->behaviour & INDUSTRYBEH_NO_PAX_PROD_CLAMP)) { + if (IsValidCargoID(i->produced_cargo[j]) && i->produced_cargo[j] == GetCargoIDByLabel(CT_PASSENGERS) && !(indspec->behaviour & INDUSTRYBEH_NO_PAX_PROD_CLAMP)) { new_prod = Clamp(new_prod, 0, 16); } diff --git a/src/industrytype.h b/src/industrytype.h index c9b846a3d9..3644fa98a6 100644 --- a/src/industrytype.h +++ b/src/industrytype.h @@ -113,6 +113,7 @@ struct IndustrySpec { IndustryType conflicting[3]; ///< Industries this industry cannot be close to byte check_proc; ///< Index to a procedure to check for conflicting circumstances std::array produced_cargo{}; + std::array produced_cargo_label{}; std::array production_rate{}; /** * minimum amount of cargo transported to the stations. @@ -120,6 +121,7 @@ struct IndustrySpec { */ byte minimal_cargo; std::array accepts_cargo{}; ///< 16 accepted cargoes. + std::array accepts_cargo_label{}; uint16_t input_cargo_multiplier[INDUSTRY_NUM_INPUTS][INDUSTRY_NUM_OUTPUTS]; ///< Input cargo multipliers (multiply amount of incoming cargo for the produced cargoes) IndustryLifeType life_type; ///< This is also known as Industry production flag, in newgrf specs byte climate_availability; ///< Bitmask, giving landscape enums as bit position @@ -156,6 +158,7 @@ struct IndustrySpec { */ struct IndustryTileSpec { std::array accepts_cargo; ///< Cargo accepted by this tile + std::array accepts_cargo_label; std::array acceptance; ///< Level of acceptance per cargo type (signed, may be negative!) Slope slopes_refused; ///< slope pattern on which this tile cannot be built byte anim_production; ///< Animation frame to start when goods are produced diff --git a/src/misc_gui.cpp b/src/misc_gui.cpp index 64b0a8a95a..380f3dddf8 100644 --- a/src/misc_gui.cpp +++ b/src/misc_gui.cpp @@ -30,6 +30,7 @@ #include "viewport_func.h" #include "rev.h" #include "core/backup_type.hpp" +#include "pathfinder/water_regions.h" #include "widgets/misc_widget.h" @@ -145,6 +146,8 @@ public: DEBUG(misc, LANDINFOD_LEVEL, "m6 = %#x", _me[tile].m6); DEBUG(misc, LANDINFOD_LEVEL, "m7 = %#x", _me[tile].m7); DEBUG(misc, LANDINFOD_LEVEL, "m8 = %#x", _me[tile].m8); + + PrintWaterRegionDebugInfo(tile); } #undef LANDINFOD_LEVEL } diff --git a/src/network/network_client.cpp b/src/network/network_client.cpp index 3aa375f58c..61922baa2a 100644 --- a/src/network/network_client.cpp +++ b/src/network/network_client.cpp @@ -170,7 +170,6 @@ ClientNetworkGameSocketHandler::~ClientNetworkGameSocketHandler() ClientNetworkGameSocketHandler::my_client = nullptr; _network_settings_access = false; - delete this->savegame; delete this->GetInfo(); if (this->desync_log_file) { @@ -754,7 +753,7 @@ bool ClientNetworkGameSocketHandler::IsConnected() ************/ extern bool SafeLoad(const std::string &filename, SaveLoadOperation fop, DetailedFileType dft, GameMode newgm, Subdirectory subdir, - struct LoadFilter *lf = nullptr, std::string *error_detail = nullptr); + std::shared_ptr lf = nullptr, std::string *error_detail = nullptr); NetworkRecvStatus ClientNetworkGameSocketHandler::Receive_SERVER_FULL(Packet *) { @@ -985,7 +984,7 @@ NetworkRecvStatus ClientNetworkGameSocketHandler::Receive_SERVER_MAP_BEGIN(Packe if (this->savegame != nullptr) return NETWORK_RECV_STATUS_MALFORMED_PACKET; - this->savegame = new PacketReader(); + this->savegame = std::make_shared(); _frame_counter = _frame_counter_server = _frame_counter_max = p->Recv_uint32(); @@ -1031,21 +1030,13 @@ NetworkRecvStatus ClientNetworkGameSocketHandler::Receive_SERVER_MAP_DONE(Packet _network_join_status = NETWORK_JOIN_STATUS_PROCESSING; SetWindowDirty(WC_NETWORK_STATUS_WINDOW, WN_NETWORK_STATUS_WINDOW_JOIN); - /* - * Make sure everything is set for reading. - * - * We need the local copy and reset this->savegame because when - * loading fails the network gets reset upon loading the intro - * game, which would cause us to free this->savegame twice. - */ - LoadFilter *lf = this->savegame; - this->savegame = nullptr; - lf->Reset(); + this->savegame->Reset(); /* The map is done downloading, load it */ ClearErrorMessages(); std::string error_detail; - bool load_success = SafeLoad({}, SLO_LOAD, DFT_GAME_FILE, GM_NORMAL, NO_DIRECTORY, lf, &error_detail); + bool load_success = SafeLoad({}, SLO_LOAD, DFT_GAME_FILE, GM_NORMAL, NO_DIRECTORY, std::move(this->savegame), &error_detail); + this->savegame = nullptr; /* Long savegame loads shouldn't affect the lag calculation! */ this->last_packet = std::chrono::steady_clock::now(); diff --git a/src/network/network_client.h b/src/network/network_client.h index 4044e196f0..46958eabf2 100644 --- a/src/network/network_client.h +++ b/src/network/network_client.h @@ -16,7 +16,7 @@ class ClientNetworkGameSocketHandler : public NetworkGameSocketHandler { private: std::string connection_string; ///< Address we are connected to. - struct PacketReader *savegame; ///< Packet reader for reading the savegame. + std::shared_ptr savegame; ///< Packet reader for reading the savegame. byte token; ///< The token we need to send back to the server to prove we're the right client. NetworkSharedSecrets last_rcon_shared_secrets; ///< Keys for last rcon (and incoming replies) diff --git a/src/network/network_server.cpp b/src/network/network_server.cpp index cf57175cd6..aca678fb0f 100644 --- a/src/network/network_server.cpp +++ b/src/network/network_server.cpp @@ -642,7 +642,7 @@ NetworkRecvStatus ServerNetworkGameSocketHandler::SendMap() if (this->status == STATUS_AUTHORIZED) { WaitTillSaved(); - this->savegame = new PacketWriter(this); + this->savegame = std::make_shared(this); /* Now send the _frame_counter and how many packets are coming */ Packet *p = new Packet(PACKET_SERVER_MAP_BEGIN, SHRT_MAX); diff --git a/src/network/network_server.h b/src/network/network_server.h index 10831dc4c2..225d0519f0 100644 --- a/src/network/network_server.h +++ b/src/network/network_server.h @@ -85,7 +85,7 @@ public: bool settings_authed = false;///< Authorised to control all game settings bool supports_zstd = false; ///< Client supports zstd compression - struct PacketWriter *savegame; ///< Writer used to write the savegame. + std::shared_ptr savegame; ///< Writer used to write the savegame. NetworkAddress client_address; ///< IP-address of the client (so they can be banned) std::string desync_log; diff --git a/src/newgrf.cpp b/src/newgrf.cpp index 1667fef6d8..bfaee876fd 100644 --- a/src/newgrf.cpp +++ b/src/newgrf.cpp @@ -1105,6 +1105,7 @@ static ChangeInfoResult RailVehicleChangeInfo(uint engine, int numinfo, int prop ei->cargo_type = INVALID_CARGO; grfmsg(2, "RailVehicleChangeInfo: Invalid cargo type %d, using first refittable", ctype); } + ei->cargo_label = CT_INVALID; break; } @@ -1367,6 +1368,7 @@ static ChangeInfoResult RoadVehicleChangeInfo(uint engine, int numinfo, int prop ei->cargo_type = INVALID_CARGO; grfmsg(2, "RailVehicleChangeInfo: Invalid cargo type %d, using first refittable", ctype); } + ei->cargo_label = CT_INVALID; break; } @@ -1563,6 +1565,7 @@ static ChangeInfoResult ShipVehicleChangeInfo(uint engine, int numinfo, int prop ei->cargo_type = INVALID_CARGO; grfmsg(2, "ShipVehicleChangeInfo: Invalid cargo type %d, using first refittable", ctype); } + ei->cargo_label = CT_INVALID; break; } @@ -2421,7 +2424,9 @@ static ChangeInfoResult TownHouseChangeInfo(uint hid, int numinfo, int prop, con * climate. This can cause problems when copying the properties * of a house that accepts food, where the new house is valid * in the temperate climate. */ - if (!CargoSpec::Get(housespec->accepts_cargo[2])->IsValid()) { + CargoID cid = housespec->accepts_cargo[2]; + if (!IsValidCargoID(cid)) cid = GetCargoIDByLabel(housespec->accepts_cargo_label[2]); + if (!IsValidCargoID(cid) || !CargoSpec::Get(cid)->IsValid()) { housespec->cargo_acceptance[2] = 0; } } @@ -2457,13 +2462,14 @@ static ChangeInfoResult TownHouseChangeInfo(uint hid, int numinfo, int prop, con /* If value of goods is negative, it means in fact food or, if in toyland, fizzy_drink acceptance. * Else, we have "standard" 3rd cargo type, goods or candy, for toyland once more */ - CargoID cid = (goods >= 0) ? ((_settings_game.game_creation.landscape == LT_TOYLAND) ? CT_CANDY : CT_GOODS) : - ((_settings_game.game_creation.landscape == LT_TOYLAND) ? CT_FIZZY_DRINKS : CT_FOOD); + CargoID cid = (goods >= 0) ? ((_settings_game.game_creation.landscape == LT_TOYLAND) ? GetCargoIDByLabel(CT_CANDY) : GetCargoIDByLabel(CT_GOODS)) : + ((_settings_game.game_creation.landscape == LT_TOYLAND) ? GetCargoIDByLabel(CT_FIZZY_DRINKS) : GetCargoIDByLabel(CT_FOOD)); /* Make sure the cargo type is valid in this climate. */ if (!CargoSpec::Get(cid)->IsValid()) goods = 0; housespec->accepts_cargo[2] = cid; + housespec->accepts_cargo_label[2] = CT_INVALID; housespec->cargo_acceptance[2] = abs(goods); // but we do need positive value here break; } @@ -2631,7 +2637,7 @@ static ChangeInfoResult TownHouseChangeInfo(uint hid, int numinfo, int prop, con * @return ChangeInfoResult. */ template -static ChangeInfoResult LoadTranslationTable(uint gvid, int numinfo, ByteReader *buf, T &translation_table, const char *name) +static ChangeInfoResult LoadTranslationTable(uint gvid, int numinfo, ByteReader *buf, std::vector &translation_table, const char *name) { if (gvid != 0) { grfmsg(1, "LoadTranslationTable: %s translation table must start at zero", name); @@ -2640,8 +2646,7 @@ static ChangeInfoResult LoadTranslationTable(uint gvid, int numinfo, ByteReader translation_table.clear(); for (int i = 0; i < numinfo; i++) { - uint32_t item = buf->ReadDWord(); - translation_table.push_back(BSWAP32(item)); + translation_table.push_back(T(BSWAP32(buf->ReadDWord()))); } return CIR_SUCCESS; @@ -3029,6 +3034,7 @@ static ChangeInfoResult CargoChangeInfo(uint cid, int numinfo, int prop, const G } else { ClrBit(_cargo_mask, cid + i); } + BuildCargoLabelMap(); break; case 0x09: // String ID for cargo type name @@ -3096,8 +3102,8 @@ static ChangeInfoResult CargoChangeInfo(uint cid, int numinfo, int prop, const G break; case 0x17: // Cargo label - cs->label = buf->ReadDWord(); - cs->label = BSWAP32(cs->label); + cs->label = CargoLabel{BSWAP32(buf->ReadDWord())}; + BuildCargoLabelMap(); break; case 0x18: { // Town growth substitute type @@ -3704,12 +3710,14 @@ static ChangeInfoResult IndustriesChangeInfo(uint indid, int numinfo, int prop, case 0x10: // Production cargo types for (byte j = 0; j < 2; j++) { indsp->produced_cargo[j] = GetCargoTranslation(buf->ReadByte(), _cur.grffile); + indsp->produced_cargo_label[j] = CT_INVALID; } break; case 0x11: // Acceptance cargo types for (byte j = 0; j < 3; j++) { indsp->accepts_cargo[j] = GetCargoTranslation(buf->ReadByte(), _cur.grffile); + indsp->accepts_cargo_label[j] = CT_INVALID; } buf->ReadByte(); // Unnused, eat it up break; @@ -6359,18 +6367,18 @@ static CargoID TranslateCargo(uint8_t feature, uint8_t ctype) /* Look up the cargo label from the translation table */ CargoLabel cl = _cur.grffile->cargo_list[ctype]; - if (cl == 0) { + if (cl == CT_INVALID) { grfmsg(5, "TranslateCargo: Cargo type %d not available in this climate, skipping.", ctype); return INVALID_CARGO; } CargoID cid = GetCargoIDByLabel(cl); if (cid == INVALID_CARGO) { - grfmsg(5, "TranslateCargo: Cargo '%c%c%c%c' unsupported, skipping.", GB(cl, 24, 8), GB(cl, 16, 8), GB(cl, 8, 8), GB(cl, 0, 8)); + grfmsg(5, "TranslateCargo: Cargo '%c%c%c%c' unsupported, skipping.", GB(cl.base(), 24, 8), GB(cl.base(), 16, 8), GB(cl.base(), 8, 8), GB(cl.base(), 0, 8)); return INVALID_CARGO; } - grfmsg(6, "TranslateCargo: Cargo '%c%c%c%c' mapped to cargo type %d.", GB(cl, 24, 8), GB(cl, 16, 8), GB(cl, 8, 8), GB(cl, 0, 8), cid); + grfmsg(6, "TranslateCargo: Cargo '%c%c%c%c' mapped to cargo type %d.", GB(cl.base(), 24, 8), GB(cl.base(), 16, 8), GB(cl.base(), 8, 8), GB(cl.base(), 0, 8), cid); return cid; } @@ -7759,9 +7767,9 @@ static void SkipIf(ByteReader *buf) if (condtype >= 0x0B) { /* Tests that ignore 'param' */ switch (condtype) { - case 0x0B: result = GetCargoIDByLabel(BSWAP32(cond_val)) == INVALID_CARGO; + case 0x0B: result = !IsValidCargoID(GetCargoIDByLabel(CargoLabel(BSWAP32(cond_val)))); break; - case 0x0C: result = GetCargoIDByLabel(BSWAP32(cond_val)) != INVALID_CARGO; + case 0x0C: result = IsValidCargoID(GetCargoIDByLabel(CargoLabel(BSWAP32(cond_val)))); break; case 0x0D: result = GetRailTypeByLabel(BSWAP32(cond_val)) == INVALID_RAILTYPE; break; @@ -10612,9 +10620,8 @@ GRFFile::~GRFFile() static void CalculateRefitMasks() { CargoTypes original_known_cargoes = 0; - for (int ct = 0; ct != NUM_ORIGINAL_CARGO; ++ct) { - CargoID cid = GetDefaultCargoID(_settings_game.game_creation.landscape, static_cast(ct)); - if (cid != INVALID_CARGO) SetBit(original_known_cargoes, cid); + for (CargoID cid = 0; cid != NUM_CARGO; ++cid) { + if (IsDefaultCargo(cid)) SetBit(original_known_cargoes, cid); } for (Engine *e : Engine::Iterate()) { @@ -10622,6 +10629,11 @@ static void CalculateRefitMasks() EngineInfo *ei = &e->info; bool only_defaultcargo; ///< Set if the vehicle shall carry only the default cargo + /* Apply default cargo translation map if cargo type hasn't been set, either explicitly or by aircraft cargo handling. */ + if (!IsValidCargoID(e->info.cargo_type)) { + e->info.cargo_type = GetCargoIDByLabel(e->info.cargo_label); + } + /* If the NewGRF did not set any cargo properties, we apply default values. */ if (_gted[engine].defaultcargo_grf == nullptr) { /* If the vehicle has any capacity, apply the default refit masks */ @@ -10632,7 +10644,7 @@ static void CalculateRefitMasks() static constexpr byte Y = 1 << LT_TOYLAND; static const struct DefaultRefitMasks { byte climate; - CargoType cargo_type; + CargoLabel cargo_label; CargoTypes cargo_allowed; CargoTypes cargo_disallowed; } _default_refit_masks[] = { @@ -10656,13 +10668,13 @@ static void CalculateRefitMasks() _gted[engine].cargo_allowed = CC_PASSENGERS | CC_MAIL | CC_ARMOURED | CC_EXPRESS; _gted[engine].cargo_disallowed = CC_LIQUID; } else if (e->type == VEH_SHIP) { - switch (ei->cargo_type) { - case CT_PASSENGERS: + switch (ei->cargo_label.base()) { + case CT_PASSENGERS.base(): /* Ferries */ _gted[engine].cargo_allowed = CC_PASSENGERS; _gted[engine].cargo_disallowed = 0; break; - case CT_OIL: + case CT_OIL.base(): /* Tankers */ _gted[engine].cargo_allowed = CC_LIQUID; _gted[engine].cargo_disallowed = 0; @@ -10689,7 +10701,7 @@ static void CalculateRefitMasks() /* Train wagons and road vehicles are classified by their default cargo type */ for (const auto &drm : _default_refit_masks) { if (!HasBit(drm.climate, _settings_game.game_creation.landscape)) continue; - if (drm.cargo_type != ei->cargo_type) continue; + if (drm.cargo_label != ei->cargo_label) continue; _gted[engine].cargo_allowed = drm.cargo_allowed; _gted[engine].cargo_disallowed = drm.cargo_disallowed; @@ -10702,9 +10714,7 @@ static void CalculateRefitMasks() } _gted[engine].UpdateRefittability(_gted[engine].cargo_allowed != 0); - /* Translate cargo_type using the original climate-specific cargo table. */ - ei->cargo_type = GetDefaultCargoID(_settings_game.game_creation.landscape, static_cast(ei->cargo_type)); - if (ei->cargo_type != INVALID_CARGO) ClrBit(_gted[engine].ctt_exclude_mask, ei->cargo_type); + if (IsValidCargoID(ei->cargo_type)) ClrBit(_gted[engine].ctt_exclude_mask, ei->cargo_type); } /* Compute refittability */ @@ -10857,10 +10867,10 @@ void FinaliseCargoArray() for (CargoSpec &cs : CargoSpec::array) { if (cs.town_production_effect == INVALID_TPE) { /* Set default town production effect by cargo label. */ - switch (cs.label) { - case 'PASS': cs.town_production_effect = TPE_PASSENGERS; break; - case 'MAIL': cs.town_production_effect = TPE_MAIL; break; - default: cs.town_production_effect = TPE_NONE; break; + switch (cs.label.base()) { + case CT_PASSENGERS.base(): cs.town_production_effect = TPE_PASSENGERS; break; + case CT_MAIL.base(): cs.town_production_effect = TPE_MAIL; break; + default: cs.town_production_effect = TPE_NONE; break; } } if (!cs.IsValid()) { @@ -11003,6 +11013,13 @@ static void FinaliseHouseArray() * this one in the pool is properly handled as 1x1 house. */ hs->building_flags = TILE_NO_FLAG; } + + /* Apply default cargo translation map for unset cargo slots */ + for (uint i = 0; i < lengthof(hs->accepts_cargo); ++i) { + if (!IsValidCargoID(hs->accepts_cargo[i])) hs->accepts_cargo[i] = GetCargoIDByLabel(hs->accepts_cargo_label[i]); + /* Disable acceptance if cargo type is invalid. */ + if (!IsValidCargoID(hs->accepts_cargo[i])) hs->cargo_acceptance[i] = 0; + } } HouseZones climate_mask = (HouseZones)(1 << (_settings_game.game_creation.landscape + 12)); @@ -11077,6 +11094,21 @@ static void FinaliseIndustriesArray() if (!indsp.enabled) { indsp.name = STR_NEWGRF_INVALID_INDUSTRYTYPE; } + + /* Apply default cargo translation map for unset cargo slots */ + for (uint i = 0; i < lengthof(indsp.produced_cargo); ++i) { + if (!IsValidCargoID(indsp.produced_cargo[i])) indsp.produced_cargo[i] = GetCargoIDByLabel(indsp.produced_cargo_label[i]); + } + for (uint i = 0; i < lengthof(indsp.accepts_cargo); ++i) { + if (!IsValidCargoID(indsp.accepts_cargo[i])) indsp.accepts_cargo[i] = GetCargoIDByLabel(indsp.accepts_cargo_label[i]); + } + } + + for (auto &indtsp : _industry_tile_specs) { + /* Apply default cargo translation map for unset cargo slots */ + for (uint i = 0; i < lengthof(indtsp.accepts_cargo); ++i) { + if (!IsValidCargoID(indtsp.accepts_cargo[i])) indtsp.accepts_cargo[i] = GetCargoIDByLabel(indtsp.accepts_cargo_label[i]); + } } } diff --git a/src/newgrf_cargo.cpp b/src/newgrf_cargo.cpp index ffd1c44269..97eaebec87 100644 --- a/src/newgrf_cargo.cpp +++ b/src/newgrf_cargo.cpp @@ -30,7 +30,7 @@ GrfSpecFeature CargoResolverObject::GetFeature() const uint32_t CargoResolverObject::GetDebugID() const { - return this->cargospec->label; + return this->cargospec->label.base(); } /** diff --git a/src/newgrf_town.cpp b/src/newgrf_town.cpp index 665b21e07b..f7c5c67945 100644 --- a/src/newgrf_town.cpp +++ b/src/newgrf_town.cpp @@ -17,6 +17,7 @@ /* virtual */ uint32_t TownScopeResolver::GetVariable(uint16_t variable, uint32_t parameter, GetVariableExtra *extra) const { + CargoID cid; switch (variable) { /* Larger towns */ case 0x40: @@ -81,24 +82,24 @@ case 0xB2: return this->t->statues; case 0xB6: return ClampTo(this->t->cache.num_houses); case 0xB9: return this->t->growth_rate / TOWN_GROWTH_TICKS; - case 0xBA: return ClampTo(this->t->supplied[CT_PASSENGERS].new_max); - case 0xBB: return GB(ClampTo(this->t->supplied[CT_PASSENGERS].new_max), 8, 8); - case 0xBC: return ClampTo(this->t->supplied[CT_MAIL].new_max); - case 0xBD: return GB(ClampTo(this->t->supplied[CT_MAIL].new_max), 8, 8); - case 0xBE: return ClampTo(this->t->supplied[CT_PASSENGERS].new_act); - case 0xBF: return GB(ClampTo(this->t->supplied[CT_PASSENGERS].new_act), 8, 8); - case 0xC0: return ClampTo(this->t->supplied[CT_MAIL].new_act); - case 0xC1: return GB(ClampTo(this->t->supplied[CT_MAIL].new_act), 8, 8); - case 0xC2: return ClampTo(this->t->supplied[CT_PASSENGERS].old_max); - case 0xC3: return GB(ClampTo(this->t->supplied[CT_PASSENGERS].old_max), 8, 8); - case 0xC4: return ClampTo(this->t->supplied[CT_MAIL].old_max); - case 0xC5: return GB(ClampTo(this->t->supplied[CT_MAIL].old_max), 8, 8); - case 0xC6: return ClampTo(this->t->supplied[CT_PASSENGERS].old_act); - case 0xC7: return GB(ClampTo(this->t->supplied[CT_PASSENGERS].old_act), 8, 8); - case 0xC8: return ClampTo(this->t->supplied[CT_MAIL].old_act); - case 0xC9: return GB(ClampTo(this->t->supplied[CT_MAIL].old_act), 8, 8); - case 0xCA: return this->t->GetPercentTransported(CT_PASSENGERS); - case 0xCB: return this->t->GetPercentTransported(CT_MAIL); + case 0xBA: cid = GetCargoIDByLabel(CT_PASSENGERS); return IsValidCargoID(cid) ? ClampTo(this->t->supplied[cid].new_max) : 0; + case 0xBB: cid = GetCargoIDByLabel(CT_PASSENGERS); return IsValidCargoID(cid) ? GB(ClampTo(this->t->supplied[cid].new_max), 8, 8) : 0; + case 0xBC: cid = GetCargoIDByLabel(CT_MAIL); return IsValidCargoID(cid) ? ClampTo(this->t->supplied[cid].new_max) : 0; + case 0xBD: cid = GetCargoIDByLabel(CT_MAIL); return IsValidCargoID(cid) ? GB(ClampTo(this->t->supplied[cid].new_max), 8, 8) : 0; + case 0xBE: cid = GetCargoIDByLabel(CT_PASSENGERS); return IsValidCargoID(cid) ? ClampTo(this->t->supplied[cid].new_act) : 0; + case 0xBF: cid = GetCargoIDByLabel(CT_PASSENGERS); return IsValidCargoID(cid) ? GB(ClampTo(this->t->supplied[cid].new_act), 8, 8) : 0; + case 0xC0: cid = GetCargoIDByLabel(CT_MAIL); return IsValidCargoID(cid) ? ClampTo(this->t->supplied[cid].new_act) : 0; + case 0xC1: cid = GetCargoIDByLabel(CT_MAIL); return IsValidCargoID(cid) ? GB(ClampTo(this->t->supplied[cid].new_act), 8, 8) : 0; + case 0xC2: cid = GetCargoIDByLabel(CT_PASSENGERS); return IsValidCargoID(cid) ? ClampTo(this->t->supplied[cid].old_max) : 0; + case 0xC3: cid = GetCargoIDByLabel(CT_PASSENGERS); return IsValidCargoID(cid) ? GB(ClampTo(this->t->supplied[cid].old_max), 8, 8) : 0; + case 0xC4: cid = GetCargoIDByLabel(CT_MAIL); return IsValidCargoID(cid) ? ClampTo(this->t->supplied[cid].old_max) : 0; + case 0xC5: cid = GetCargoIDByLabel(CT_MAIL); return IsValidCargoID(cid) ? GB(ClampTo(this->t->supplied[cid].old_max), 8, 8) : 0; + case 0xC6: cid = GetCargoIDByLabel(CT_PASSENGERS); return IsValidCargoID(cid) ? ClampTo(this->t->supplied[cid].old_act) : 0; + case 0xC7: cid = GetCargoIDByLabel(CT_PASSENGERS); return IsValidCargoID(cid) ? GB(ClampTo(this->t->supplied[cid].old_act), 8, 8) : 0; + case 0xC8: cid = GetCargoIDByLabel(CT_MAIL); return IsValidCargoID(cid) ? ClampTo(this->t->supplied[cid].old_act) : 0; + case 0xC9: cid = GetCargoIDByLabel(CT_MAIL); return IsValidCargoID(cid) ? GB(ClampTo(this->t->supplied[cid].old_act), 8, 8) : 0; + case 0xCA: return this->t->GetPercentTransported(GetCargoIDByLabel(CT_PASSENGERS)); + case 0xCB: return this->t->GetPercentTransported(GetCargoIDByLabel(CT_MAIL)); case 0xCC: return this->t->received[TAE_FOOD].new_act; case 0xCD: return GB(this->t->received[TAE_FOOD].new_act, 8, 8); case 0xCE: return this->t->received[TAE_WATER].new_act; diff --git a/src/object_cmd.cpp b/src/object_cmd.cpp index fffe0f8ae3..b56313d3f1 100644 --- a/src/object_cmd.cpp +++ b/src/object_cmd.cpp @@ -819,23 +819,31 @@ static void AddAcceptedCargo_Object(TileIndex tile, CargoArray &acceptance, Carg /* Top town building generates 10, so to make HQ interesting, the top * type makes 20. */ - acceptance[CT_PASSENGERS] += std::max(1U, level); - SetBit(*always_accepted, CT_PASSENGERS); + CargoID pass = GetCargoIDByLabel(CT_PASSENGERS); + if (IsValidCargoID(pass)) { + acceptance[pass] += std::max(1U, level); + SetBit(*always_accepted, pass); + } /* Top town building generates 4, HQ can make up to 8. The * proportion passengers:mail is different because such a huge * commercial building generates unusually high amount of mail * correspondence per physical visitor. */ - acceptance[CT_MAIL] += std::max(1U, level / 2); - SetBit(*always_accepted, CT_MAIL); + CargoID mail = GetCargoIDByLabel(CT_MAIL); + if (IsValidCargoID(mail)) { + acceptance[mail] += std::max(1U, level / 2); + SetBit(*always_accepted, mail); + } } static void AddProducedCargo_Object(TileIndex tile, CargoArray &produced) { if (!IsObjectType(tile, OBJECT_HQ)) return; - produced[CT_PASSENGERS]++; - produced[CT_MAIL]++; + CargoID pass = GetCargoIDByLabel(CT_PASSENGERS); + if (IsValidCargoID(pass)) produced[pass]++; + CargoID mail = GetCargoIDByLabel(CT_MAIL); + if (IsValidCargoID(mail)) produced[mail]++; } @@ -979,19 +987,31 @@ static void TileLoop_Object(TileIndex tile) uint r = Random(); /* Top town buildings generate 250, so the top HQ type makes 256. */ - if (GB(r, 0, 8) < (256 / 4 / (6 - level))) { + CargoID pass = GetCargoIDByLabel(CT_PASSENGERS); + if (IsValidCargoID(pass) && GB(r, 0, 8) < (256 / 4 / (6 - level))) { uint amt = GB(r, 0, 8) / 8 / 4 + 1; if (EconomyIsInRecession()) amt = (amt + 1) >> 1; - MoveGoodsToStation(CT_PASSENGERS, amt, SourceType::Headquarters, GetTileOwner(tile), stations.GetStations()); + + /* Scale by cargo scale setting. */ + amt = _town_cargo_scaler.ScaleAllowTrunc(amt); + if (amt != 0) { + MoveGoodsToStation(pass, amt, SourceType::Headquarters, GetTileOwner(tile), stations.GetStations()); + } } /* Top town building generates 90, HQ can make up to 196. The * proportion passengers:mail is about the same as in the acceptance * equations. */ - if (GB(r, 8, 8) < (196 / 4 / (6 - level))) { + CargoID mail = GetCargoIDByLabel(CT_MAIL); + if (IsValidCargoID(mail) && GB(r, 8, 8) < (196 / 4 / (6 - level))) { uint amt = GB(r, 8, 8) / 8 / 4 + 1; if (EconomyIsInRecession()) amt = (amt + 1) >> 1; - MoveGoodsToStation(CT_MAIL, amt, SourceType::Headquarters, GetTileOwner(tile), stations.GetStations()); + + /* Scale by cargo scale setting. */ + amt = _town_cargo_scaler.ScaleAllowTrunc(amt); + if (amt != 0) { + MoveGoodsToStation(mail, amt, SourceType::Headquarters, GetTileOwner(tile), stations.GetStations()); + } } } diff --git a/src/openttd.cpp b/src/openttd.cpp index c68536eacf..82420705f0 100644 --- a/src/openttd.cpp +++ b/src/openttd.cpp @@ -1305,7 +1305,7 @@ static void MakeNewEditorWorld() * @param error_detail Optional string to fill with detaied error information. */ bool SafeLoad(const std::string &filename, SaveLoadOperation fop, DetailedFileType dft, GameMode newgm, Subdirectory subdir, - struct LoadFilter *lf = nullptr, std::string *error_detail = nullptr) + std::shared_ptr lf = nullptr, std::string *error_detail = nullptr) { assert(fop == SLO_LOAD); assert(dft == DFT_GAME_FILE || (lf == nullptr && dft == DFT_OLD_GAME_FILE)); @@ -1313,7 +1313,7 @@ bool SafeLoad(const std::string &filename, SaveLoadOperation fop, DetailedFileTy _game_mode = newgm; - SaveOrLoadResult result = (lf == nullptr) ? SaveOrLoad(filename, fop, dft, subdir) : LoadWithFilter(lf); + SaveOrLoadResult result = (lf == nullptr) ? SaveOrLoad(filename, fop, dft, subdir) : LoadWithFilter(std::move(lf)); if (result == SL_OK) return true; if (error_detail != nullptr) *error_detail = GetSaveLoadErrorString(); diff --git a/src/order_cmd.cpp b/src/order_cmd.cpp index ca4f364429..658cab1bed 100644 --- a/src/order_cmd.cpp +++ b/src/order_cmd.cpp @@ -3207,7 +3207,7 @@ VehicleOrderID ProcessConditionalOrder(const Order *order, const Vehicle *v, Pro // OrderConditionCompare ignores the last parameter for occ == OCC_IS_TRUE or occ == OCC_IS_FALSE. switch (order->GetConditionVariable()) { case OCV_LOAD_PERCENTAGE: skip_order = OrderConditionCompare(occ, CalcPercentVehicleFilled(v, nullptr), value); break; - case OCV_CARGO_LOAD_PERCENTAGE: skip_order = OrderConditionCompare(occ, CalcPercentVehicleFilledOfCargo(v, (CargoType) value), order->GetXData()); break; + case OCV_CARGO_LOAD_PERCENTAGE: skip_order = OrderConditionCompare(occ, CalcPercentVehicleFilledOfCargo(v, (CargoID)value), order->GetXData()); break; case OCV_RELIABILITY: skip_order = OrderConditionCompare(occ, ToPercent16(v->reliability), value); break; case OCV_MAX_RELIABILITY: skip_order = OrderConditionCompare(occ, ToPercent16(v->GetEngine()->reliability), value); break; case OCV_MAX_SPEED: skip_order = OrderConditionCompare(occ, v->GetDisplayMaxSpeed() * 10 / 16, value); break; diff --git a/src/pathfinder/water_regions.cpp b/src/pathfinder/water_regions.cpp index 814499c7fa..7937cf0de8 100644 --- a/src/pathfinder/water_regions.cpp +++ b/src/pathfinder/water_regions.cpp @@ -8,6 +8,7 @@ /** @file water_regions.cpp Handles dividing the water in the map into square regions to assist pathfinding. */ #include "stdafx.h" +#include "debug_fmt.h" #include "map_func.h" #include "water_regions.h" #include "map_func.h" @@ -86,6 +87,9 @@ class WaterRegion bool has_cross_region_aqueducts = false; TWaterRegionPatchLabel number_of_patches = 0; // 0 = no water, 1 = one single patch of water, etc... std::unique_ptr tile_patch_labels; + +public: + void Invalidate() { this->initialized = false; } }; static std::unique_ptr _spare_labels; @@ -186,6 +190,7 @@ public: } this->wr.tile_patch_labels->fill(INVALID_WATER_REGION_PATCH); + this->wr.edge_traversability_bits.fill(0); TWaterRegionPatchLabel current_label = 1; TWaterRegionPatchLabel highest_assigned_label = 0; @@ -222,7 +227,18 @@ public: for (const Trackdir dir : SetTrackdirBitIterator(valid_dirs)) { /* By using a TrackFollower we "play by the same rules" as the actual ship pathfinder */ CFollowTrackWater ft; - if (ft.Follow(tile, dir) && this->ContainsTile(ft.m_new_tile)) tiles_to_check.push_back(ft.m_new_tile); + if (ft.Follow(tile, dir)) { + if (this->ContainsTile(ft.m_new_tile)) { + tiles_to_check.push_back(ft.m_new_tile); + } else if (!ft.m_is_bridge) { + assert(DistanceManhattan(ft.m_new_tile, tile) == 1); + const auto side = DiagdirBetweenTiles(tile, ft.m_new_tile); + const int local_x_or_y = DiagDirToAxis(side) == AXIS_X ? TileY(tile) - this->tile_y : TileX(tile) - this->tile_x; + SetBit(this->wr.edge_traversability_bits[side], local_x_or_y); + } else { + this->wr.has_cross_region_aqueducts = true; + } + } } } @@ -232,18 +248,6 @@ public: this->wr.number_of_patches = highest_assigned_label; this->wr.initialized = true; - /* Calculate the traversability (whether the tile can be entered / exited) for all edges. Note that - * we always follow the same X and Y scanning direction, this is important for comparisons later on! */ - this->wr.edge_traversability_bits.fill(0); - const uint32_t top_x = this->tile_x; - const uint32_t top_y = this->tile_y; - for (uint32_t i = 0; i < WATER_REGION_EDGE_LENGTH; ++i) { - if (GetWaterTracks(TileXY(top_x + i, top_y)) & TRACK_BIT_3WAY_NW) SetBit(this->wr.edge_traversability_bits[DIAGDIR_NW], i); // NW edge - if (GetWaterTracks(TileXY(top_x + i, top_y + WATER_REGION_EDGE_LENGTH - 1)) & TRACK_BIT_3WAY_SE) SetBit(this->wr.edge_traversability_bits[DIAGDIR_SE], i); // SE edge - if (GetWaterTracks(TileXY(top_x, top_y + i)) & TRACK_BIT_3WAY_NE) SetBit(this->wr.edge_traversability_bits[DIAGDIR_NE], i); // NE edge - if (GetWaterTracks(TileXY(top_x + WATER_REGION_EDGE_LENGTH - 1, top_y + i)) & TRACK_BIT_3WAY_SW) SetBit(this->wr.edge_traversability_bits[DIAGDIR_SW], i); // SW edge - } - if (this->wr.number_of_patches == 0 || (this->wr.number_of_patches == 1 && !this->HasNonMatchingPatchLabel(1))) { /* No need for patch storage: trivial cases */ _spare_labels = std::move(this->wr.tile_patch_labels); @@ -273,6 +277,33 @@ public: } return out; } + + void PrintDebugInfo() + { + Debug(map, 9, "Water region {},{} labels and edge traversability = ...", this->tile_x / WATER_REGION_EDGE_LENGTH, this->tile_y / WATER_REGION_EDGE_LENGTH); + + const size_t max_element_width = std::to_string(this->wr.number_of_patches).size(); + + std::array traversability_NW{0}; + for (auto bitIndex : SetBitIterator(GetEdgeTraversabilityBits(DIAGDIR_NW))) *(traversability_NW.rbegin() + bitIndex) = 1; + Debug(map, 9, " {:{}}", fmt::join(traversability_NW, " "), max_element_width); + Debug(map, 9, " +{:->{}}+", "", WATER_REGION_EDGE_LENGTH * (max_element_width + 1) + 1); + + for (uint y = 0; y < WATER_REGION_EDGE_LENGTH; ++y) { + std::string line{}; + for (uint x = 0; x < WATER_REGION_EDGE_LENGTH; ++x) { + const auto label = this->GetLabel(TileXY(this->tile_x + x, this->tile_y + y)); + const std::string label_str = label == INVALID_WATER_REGION_PATCH ? "." : std::to_string(label); + line = fmt::format("{:{}}", label_str, max_element_width) + " " + line; + } + Debug(map, 9, "{} | {}| {}", GB(this->GetEdgeTraversabilityBits(DIAGDIR_SW), y, 1), line, GB(this->GetEdgeTraversabilityBits(DIAGDIR_NE), y, 1)); + } + + Debug(map, 9, " +{:->{}}+", "", WATER_REGION_EDGE_LENGTH * (max_element_width + 1) + 1); + std::array traversability_SE{0}; + for (auto bitIndex : SetBitIterator(this->GetEdgeTraversabilityBits(DIAGDIR_SE))) *(traversability_SE.rbegin() + bitIndex) = 1; + Debug(map, 9, " {:{}}", fmt::join(traversability_SE, " "), max_element_width); + } }; std::unique_ptr _water_regions; @@ -371,9 +402,20 @@ WaterRegionPatchDesc GetWaterRegionPatchInfo(TileIndex tile) */ void InvalidateWaterRegion(TileIndex tile) { - if (tile < MapSize()) { - GetWaterRegionRef(tile).Invalidate(); - } + if (tile >= MapSize()) return; + + const TWaterRegionIndex region = GetWaterRegionIndex(tile); + _water_regions[region].Invalidate(); + + /* When updating the water region we look into the first tile of adjacent water regions to determine edge + * traversability. This means that if we invalidate any region edge tiles we might also change the traversability + * of the adjacent region. This code ensures the adjacent regions also get invalidated in such a case. */ + const uint x = TileX(tile); + const uint y = TileY(tile); + if ((x & WATER_REGION_EDGE_MASK) == 0 && x > 0) _water_regions[region - 1].Invalidate(); + if ((x & WATER_REGION_EDGE_MASK) == WATER_REGION_EDGE_MASK && x < MapMaxX()) _water_regions[region + 1].Invalidate(); + if ((y & WATER_REGION_EDGE_MASK) == 0 && y > 0) _water_regions[region - GetWaterRegionMapSizeX()].Invalidate(); + if ((y & WATER_REGION_EDGE_MASK) == WATER_REGION_EDGE_MASK && y < MapMaxY()) _water_regions[region + GetWaterRegionMapSizeX()].Invalidate(); } /** @@ -553,3 +595,8 @@ void WaterRegionCheckCaches(std::function log) } #undef CCLOG } + +void PrintWaterRegionDebugInfo(TileIndex tile) +{ + if (_debug_map_level >= 9) GetUpdatedWaterRegion(tile).PrintDebugInfo(); +} diff --git a/src/pathfinder/water_regions.h b/src/pathfinder/water_regions.h index b8bddf6e7e..15f272e32a 100644 --- a/src/pathfinder/water_regions.h +++ b/src/pathfinder/water_regions.h @@ -79,4 +79,6 @@ struct WaterRegionSaveLoadInfo bool initialized; }; +void PrintWaterRegionDebugInfo(TileIndex tile); + #endif /* WATER_REGIONS_H */ diff --git a/src/roadveh_cmd.cpp b/src/roadveh_cmd.cpp index 093d0a0f25..a1987d8621 100644 --- a/src/roadveh_cmd.cpp +++ b/src/roadveh_cmd.cpp @@ -302,6 +302,7 @@ CommandCost CmdBuildRoadVehicle(TileIndex tile, DoCommandFlag flags, const Engin v->spritenum = rvi->image_index; v->cargo_type = e->GetDefaultCargoType(); + assert(IsValidCargoID(v->cargo_type)); v->cargo_cap = rvi->capacity; v->refit_cap = 0; diff --git a/src/saveload/town_sl.cpp b/src/saveload/town_sl.cpp index 474528b65a..7e14649a2f 100644 --- a/src/saveload/town_sl.cpp +++ b/src/saveload/town_sl.cpp @@ -133,22 +133,23 @@ static const SaveLoad _town_desc[] = { SLE_CONDARR(Town, unwanted, SLE_INT8, 8, SLV_4, SLV_104), SLE_CONDARR(Town, unwanted, SLE_INT8, MAX_COMPANIES, SLV_104, SL_MAX_VERSION), - SLE_CONDVAR(Town, supplied[CT_PASSENGERS].old_max, SLE_FILE_U16 | SLE_VAR_U32, SL_MIN_VERSION, SLV_9), - SLE_CONDVAR(Town, supplied[CT_PASSENGERS].old_max, SLE_UINT32, SLV_9, SLV_165), - SLE_CONDVAR(Town, supplied[CT_MAIL].old_max, SLE_FILE_U16 | SLE_VAR_U32, SL_MIN_VERSION, SLV_9), - SLE_CONDVAR(Town, supplied[CT_MAIL].old_max, SLE_UINT32, SLV_9, SLV_165), - SLE_CONDVAR(Town, supplied[CT_PASSENGERS].new_max, SLE_FILE_U16 | SLE_VAR_U32, SL_MIN_VERSION, SLV_9), - SLE_CONDVAR(Town, supplied[CT_PASSENGERS].new_max, SLE_UINT32, SLV_9, SLV_165), - SLE_CONDVAR(Town, supplied[CT_MAIL].new_max, SLE_FILE_U16 | SLE_VAR_U32, SL_MIN_VERSION, SLV_9), - SLE_CONDVAR(Town, supplied[CT_MAIL].new_max, SLE_UINT32, SLV_9, SLV_165), - SLE_CONDVAR(Town, supplied[CT_PASSENGERS].old_act, SLE_FILE_U16 | SLE_VAR_U32, SL_MIN_VERSION, SLV_9), - SLE_CONDVAR(Town, supplied[CT_PASSENGERS].old_act, SLE_UINT32, SLV_9, SLV_165), - SLE_CONDVAR(Town, supplied[CT_MAIL].old_act, SLE_FILE_U16 | SLE_VAR_U32, SL_MIN_VERSION, SLV_9), - SLE_CONDVAR(Town, supplied[CT_MAIL].old_act, SLE_UINT32, SLV_9, SLV_165), - SLE_CONDVAR(Town, supplied[CT_PASSENGERS].new_act, SLE_FILE_U16 | SLE_VAR_U32, SL_MIN_VERSION, SLV_9), - SLE_CONDVAR(Town, supplied[CT_PASSENGERS].new_act, SLE_UINT32, SLV_9, SLV_165), - SLE_CONDVAR(Town, supplied[CT_MAIL].new_act, SLE_FILE_U16 | SLE_VAR_U32, SL_MIN_VERSION, SLV_9), - SLE_CONDVAR(Town, supplied[CT_MAIL].new_act, SLE_UINT32, SLV_9, SLV_165), + /* Slots 0 and 2 are passengers and mail respectively for old saves. */ + SLE_CONDVARNAME(Town, supplied[0].old_max, "supplied[CT_PASSENGERS].old_max", SLE_FILE_U16 | SLE_VAR_U32, SL_MIN_VERSION, SLV_9), + SLE_CONDVARNAME(Town, supplied[0].old_max, "supplied[CT_PASSENGERS].old_max", SLE_UINT32, SLV_9, SLV_165), + SLE_CONDVARNAME(Town, supplied[2].old_max, "supplied[CT_MAIL].old_max", SLE_FILE_U16 | SLE_VAR_U32, SL_MIN_VERSION, SLV_9), + SLE_CONDVARNAME(Town, supplied[2].old_max, "supplied[CT_MAIL].old_max", SLE_UINT32, SLV_9, SLV_165), + SLE_CONDVARNAME(Town, supplied[0].new_max, "supplied[CT_PASSENGERS].new_max", SLE_FILE_U16 | SLE_VAR_U32, SL_MIN_VERSION, SLV_9), + SLE_CONDVARNAME(Town, supplied[0].new_max, "supplied[CT_PASSENGERS].new_max", SLE_UINT32, SLV_9, SLV_165), + SLE_CONDVARNAME(Town, supplied[2].new_max, "supplied[CT_MAIL].new_max", SLE_FILE_U16 | SLE_VAR_U32, SL_MIN_VERSION, SLV_9), + SLE_CONDVARNAME(Town, supplied[2].new_max, "supplied[CT_MAIL].new_max", SLE_UINT32, SLV_9, SLV_165), + SLE_CONDVARNAME(Town, supplied[0].old_act, "supplied[CT_PASSENGERS].old_act", SLE_FILE_U16 | SLE_VAR_U32, SL_MIN_VERSION, SLV_9), + SLE_CONDVARNAME(Town, supplied[0].old_act, "supplied[CT_PASSENGERS].old_act", SLE_UINT32, SLV_9, SLV_165), + SLE_CONDVARNAME(Town, supplied[2].old_act, "supplied[CT_MAIL].old_act", SLE_FILE_U16 | SLE_VAR_U32, SL_MIN_VERSION, SLV_9), + SLE_CONDVARNAME(Town, supplied[2].old_act, "supplied[CT_MAIL].old_act", SLE_UINT32, SLV_9, SLV_165), + SLE_CONDVARNAME(Town, supplied[0].new_act, "supplied[CT_PASSENGERS].new_act", SLE_FILE_U16 | SLE_VAR_U32, SL_MIN_VERSION, SLV_9), + SLE_CONDVARNAME(Town, supplied[0].new_act, "supplied[CT_PASSENGERS].new_act", SLE_UINT32, SLV_9, SLV_165), + SLE_CONDVARNAME(Town, supplied[2].new_act, "supplied[CT_MAIL].new_act", SLE_FILE_U16 | SLE_VAR_U32, SL_MIN_VERSION, SLV_9), + SLE_CONDVARNAME(Town, supplied[2].new_act, "supplied[CT_MAIL].new_act", SLE_UINT32, SLV_9, SLV_165), SLE_CONDVARNAME(Town, received[TAE_FOOD].old_act, "received[TE_FOOD].old_act", SLE_UINT16, SL_MIN_VERSION, SLV_165), SLE_CONDVARNAME(Town, received[TAE_WATER].old_act, "received[TE_WATER].old_act", SLE_UINT16, SL_MIN_VERSION, SLV_165), diff --git a/src/script/api/script_cargo.cpp b/src/script/api/script_cargo.cpp index c00c7df464..c1ae5beba2 100644 --- a/src/script/api/script_cargo.cpp +++ b/src/script/api/script_cargo.cpp @@ -44,7 +44,7 @@ * like "PASS", "COAL", "OIL_". New ones can be defined by NewGRFs */ std::string cargo_label; for (uint i = 0; i < sizeof(cargo->label); i++) { - cargo_label.push_back(GB(cargo->label, (uint8_t)(sizeof(cargo->label) - i - 1) * 8, 8)); + cargo_label.push_back(GB(cargo->label.base(), (uint8_t)(sizeof(cargo->label) - i - 1) * 8, 8)); } return cargo_label; } diff --git a/src/ship_cmd.cpp b/src/ship_cmd.cpp index 0ac0967800..5c3d26bc1e 100644 --- a/src/ship_cmd.cpp +++ b/src/ship_cmd.cpp @@ -1186,6 +1186,7 @@ CommandCost CmdBuildShip(TileIndex tile, DoCommandFlag flags, const Engine *e, V v->spritenum = svi->image_index; v->cargo_type = e->GetDefaultCargoType(); + assert(IsValidCargoID(v->cargo_type)); v->cargo_cap = svi->capacity; v->refit_cap = 0; diff --git a/src/sl/oldloader_sl.cpp b/src/sl/oldloader_sl.cpp index 2a836ee071..35acbd3728 100644 --- a/src/sl/oldloader_sl.cpp +++ b/src/sl/oldloader_sl.cpp @@ -589,14 +589,15 @@ static const OldChunks town_chunk[] = { OCL_SVAR( OC_FILE_U8 | OC_VAR_U16, Town, time_until_rebuild ), OCL_SVAR( OC_FILE_U8 | OC_VAR_U16, Town, growth_rate ), - OCL_SVAR( OC_FILE_U16 | OC_VAR_U32, Town, supplied[CT_PASSENGERS].new_max ), - OCL_SVAR( OC_FILE_U16 | OC_VAR_U32, Town, supplied[CT_MAIL].new_max ), - OCL_SVAR( OC_FILE_U16 | OC_VAR_U32, Town, supplied[CT_PASSENGERS].new_act ), - OCL_SVAR( OC_FILE_U16 | OC_VAR_U32, Town, supplied[CT_MAIL].new_act ), - OCL_SVAR( OC_FILE_U16 | OC_VAR_U32, Town, supplied[CT_PASSENGERS].old_max ), - OCL_SVAR( OC_FILE_U16 | OC_VAR_U32, Town, supplied[CT_MAIL].old_max ), - OCL_SVAR( OC_FILE_U16 | OC_VAR_U32, Town, supplied[CT_PASSENGERS].old_act ), - OCL_SVAR( OC_FILE_U16 | OC_VAR_U32, Town, supplied[CT_MAIL].old_act ), + /* Slots 0 and 2 are passengers and mail respectively for old saves. */ + OCL_SVAR( OC_FILE_U16 | OC_VAR_U32, Town, supplied[0].new_max ), + OCL_SVAR( OC_FILE_U16 | OC_VAR_U32, Town, supplied[2].new_max ), + OCL_SVAR( OC_FILE_U16 | OC_VAR_U32, Town, supplied[0].new_act ), + OCL_SVAR( OC_FILE_U16 | OC_VAR_U32, Town, supplied[2].new_act ), + OCL_SVAR( OC_FILE_U16 | OC_VAR_U32, Town, supplied[0].old_max ), + OCL_SVAR( OC_FILE_U16 | OC_VAR_U32, Town, supplied[2].old_max ), + OCL_SVAR( OC_FILE_U16 | OC_VAR_U32, Town, supplied[0].old_act ), + OCL_SVAR( OC_FILE_U16 | OC_VAR_U32, Town, supplied[2].old_act ), OCL_NULL( 2 ), ///< pct_pass_transported / pct_mail_transported, now computed on the fly @@ -1304,10 +1305,10 @@ bool LoadOldVehicle(LoadgameState *ls, int num) switch (v->spritenum) { case 2: // oil tanker && cargo type != oil - if (v->cargo_type != CT_OIL) v->spritenum = 0; // make it a coal/goods ship + if (v->cargo_type != 3) v->spritenum = 0; // make it a coal/goods ship break; case 4: // passenger ship && cargo type == mail - if (v->cargo_type == CT_MAIL) v->spritenum = 0; // make it a mail ship + if (v->cargo_type == 2) v->spritenum = 0; // make it a mail ship break; default: break; diff --git a/src/sl/saveload.cpp b/src/sl/saveload.cpp index 8b1c81294a..f2a3a62a3c 100644 --- a/src/sl/saveload.cpp +++ b/src/sl/saveload.cpp @@ -178,16 +178,16 @@ void MemoryDumper::AllocateBuffer() * Flush this dumper into a writer. * @param writer The filter we want to use. */ -void MemoryDumper::Flush(SaveFilter *writer) +void MemoryDumper::Flush(SaveFilter &writer) { this->FinaliseBlock(); size_t block_count = this->blocks.size(); for (size_t i = 0; i < block_count; i++) { - writer->Write(this->blocks[i].data, this->blocks[i].size); + writer.Write(this->blocks[i].data, this->blocks[i].size); } - writer->Finish(); + writer.Finish(); } void MemoryDumper::StartAutoLength() @@ -234,11 +234,11 @@ struct SaveLoadParams { uint32_t current_chunk_id; ///< Current chunk ID - MemoryDumper *dumper; ///< Memory dumper to write the savegame to. - SaveFilter *sf; ///< Filter to write the savegame to. + std::unique_ptr dumper;///< Memory dumper to write the savegame to. + std::shared_ptr sf; ///< Filter to write the savegame to. - ReadBuffer *reader; ///< Savegame reading buffer. - LoadFilter *lf; ///< Filter to read the savegame from. + std::unique_ptr reader; ///< Savegame reading buffer. + std::shared_ptr lf; ///< Filter to read the savegame from. StringID error_str; ///< the translatable error message to show std::string extra_msg; ///< the error message @@ -251,12 +251,12 @@ static SaveLoadParams _sl; ///< Parameters used for/at saveload. ReadBuffer *ReadBuffer::GetCurrent() { - return _sl.reader; + return _sl.reader.get(); } MemoryDumper *MemoryDumper::GetCurrent() { - return _sl.dumper; + return _sl.dumper.get(); } static const std::vector &ChunkHandlers() @@ -2743,9 +2743,6 @@ struct FileReader : LoadFilter { { if (this->file != nullptr) fclose(this->file); this->file = nullptr; - - /* Make sure we don't double free. */ - _sl.sf = nullptr; } size_t Read(byte *buf, size_t size) override @@ -2781,9 +2778,6 @@ struct FileWriter : SaveFilter { ~FileWriter() { this->Finish(); - - /* Make sure we don't double free. */ - _sl.sf = nullptr; } void Write(byte *buf, size_t size) override @@ -2817,7 +2811,7 @@ struct LZOLoadFilter : LoadFilter { * Initialise this filter. * @param chain The next filter in this chain. */ - LZOLoadFilter(LoadFilter *chain) : LoadFilter(chain) + LZOLoadFilter(std::shared_ptr chain) : LoadFilter(std::move(chain)) { if (lzo_init() != LZO_E_OK) SlError(STR_GAME_SAVELOAD_ERROR_BROKEN_INTERNAL_ERROR, "cannot initialize decompressor"); } @@ -2865,7 +2859,7 @@ struct LZOSaveFilter : SaveFilter { * @param chain The next filter in this chain. * @param compression_level The requested level of compression. */ - LZOSaveFilter(SaveFilter *chain, byte compression_level) : SaveFilter(chain) + LZOSaveFilter(std::shared_ptr chain, byte compression_level) : SaveFilter(std::move(chain)) { if (lzo_init() != LZO_E_OK) SlError(STR_GAME_SAVELOAD_ERROR_BROKEN_INTERNAL_ERROR, "cannot initialize compressor"); } @@ -2905,7 +2899,7 @@ struct NoCompLoadFilter : LoadFilter { * Initialise this filter. * @param chain The next filter in this chain. */ - NoCompLoadFilter(LoadFilter *chain) : LoadFilter(chain) + NoCompLoadFilter(std::shared_ptr chain) : LoadFilter(std::move(chain)) { } @@ -2922,7 +2916,7 @@ struct NoCompSaveFilter : SaveFilter { * @param chain The next filter in this chain. * @param compression_level The requested level of compression. */ - NoCompSaveFilter(SaveFilter *chain, byte compression_level) : SaveFilter(chain) + NoCompSaveFilter(std::shared_ptr chain, byte compression_level) : SaveFilter(std::move(chain)) { } @@ -2948,7 +2942,7 @@ struct ZlibLoadFilter : LoadFilter { * Initialise this filter. * @param chain The next filter in this chain. */ - ZlibLoadFilter(LoadFilter *chain) : LoadFilter(chain) + ZlibLoadFilter(std::shared_ptr chain) : LoadFilter(std::move(chain)) { memset(&this->z, 0, sizeof(this->z)); if (inflateInit(&this->z) != Z_OK) SlError(STR_GAME_SAVELOAD_ERROR_BROKEN_INTERNAL_ERROR, "cannot initialize decompressor"); @@ -2993,7 +2987,7 @@ struct ZlibSaveFilter : SaveFilter { * @param chain The next filter in this chain. * @param compression_level The requested level of compression. */ - ZlibSaveFilter(SaveFilter *chain, byte compression_level) : SaveFilter(chain) + ZlibSaveFilter(std::shared_ptr chain, byte compression_level) : SaveFilter(std::move(chain)) { memset(&this->z, 0, sizeof(this->z)); if (deflateInit(&this->z, compression_level) != Z_OK) SlError(STR_GAME_SAVELOAD_ERROR_BROKEN_INTERNAL_ERROR, "cannot initialize compressor"); @@ -3077,7 +3071,7 @@ struct LZMALoadFilter : LoadFilter { * Initialise this filter. * @param chain The next filter in this chain. */ - LZMALoadFilter(LoadFilter *chain) : LoadFilter(chain), lzma(_lzma_init) + LZMALoadFilter(std::shared_ptr chain) : LoadFilter(std::move(chain)), lzma(_lzma_init) { /* Allow saves up to 256 MB uncompressed */ if (lzma_auto_decoder(&this->lzma, 1 << 28, 0) != LZMA_OK) SlError(STR_GAME_SAVELOAD_ERROR_BROKEN_INTERNAL_ERROR, "cannot initialize decompressor"); @@ -3121,7 +3115,7 @@ struct LZMASaveFilter : SaveFilter { * @param chain The next filter in this chain. * @param compression_level The requested level of compression. */ - LZMASaveFilter(SaveFilter *chain, byte compression_level) : SaveFilter(chain), lzma(_lzma_init) + LZMASaveFilter(std::shared_ptr chain, byte compression_level) : SaveFilter(std::move(chain)), lzma(_lzma_init) { if (lzma_easy_encoder(&this->lzma, compression_level, LZMA_CHECK_CRC32) != LZMA_OK) SlError(STR_GAME_SAVELOAD_ERROR_BROKEN_INTERNAL_ERROR, "cannot initialize compressor"); } @@ -3190,7 +3184,7 @@ struct ZSTDLoadFilter : LoadFilter { * Initialise this filter. * @param chain The next filter in this chain. */ - ZSTDLoadFilter(LoadFilter *chain) : LoadFilter(chain) + ZSTDLoadFilter(std::shared_ptr chain) : LoadFilter(std::move(chain)) { this->zstd = ZSTD_createDCtx(); if (!this->zstd) SlError(STR_GAME_SAVELOAD_ERROR_BROKEN_INTERNAL_ERROR, "cannot initialize compressor"); @@ -3234,7 +3228,7 @@ struct ZSTDSaveFilter : SaveFilter { * @param chain The next filter in this chain. * @param compression_level The requested level of compression. */ - ZSTDSaveFilter(SaveFilter *chain, byte compression_level) : SaveFilter(chain) + ZSTDSaveFilter(std::shared_ptr chain, byte compression_level) : SaveFilter(std::move(chain)) { this->zstd = ZSTD_createCCtx(); if (!this->zstd) SlError(STR_GAME_SAVELOAD_ERROR_BROKEN_INTERNAL_ERROR, "cannot initialize compressor"); @@ -3302,8 +3296,8 @@ struct SaveLoadFormat { const char *name; ///< name of the compressor/decompressor (debug-only) uint32_t tag; ///< the 4-letter tag by which it is identified in the savegame - LoadFilter *(*init_load)(LoadFilter *chain); ///< Constructor for the load filter. - SaveFilter *(*init_write)(SaveFilter *chain, byte compression); ///< Constructor for the save filter. + std::shared_ptr (*init_load)(std::shared_ptr chain); ///< Constructor for the load filter. + std::shared_ptr (*init_write)(std::shared_ptr chain, byte compression); ///< Constructor for the save filter. byte min_compression; ///< the minimum compression level of this format byte default_compression; ///< the default compression level of this format @@ -3420,20 +3414,11 @@ static void ResetSaveloadData() */ static inline void ClearSaveLoadState() { - delete _sl.dumper; _sl.dumper = nullptr; - - delete _sl.sf; _sl.sf = nullptr; - - delete _sl.reader; _sl.reader = nullptr; - - delete _sl.lf; _sl.lf = nullptr; - _sl.save_flags = SMF_NONE; - _sl.current_chunk_id = 0; GamelogStopAnyAction(); @@ -3500,7 +3485,7 @@ static SaveOrLoadResult SaveFileToDisk(bool threaded) _sl.sf->Write((byte*)hdr, sizeof(hdr)); _sl.sf = fmt->init_write(_sl.sf, compression); - _sl.dumper->Flush(_sl.sf); + _sl.dumper->Flush(*(_sl.sf)); ClearSaveLoadState(); @@ -3542,12 +3527,12 @@ void WaitTillSaved() * @param threaded Whether to try to perform the saving asynchronously. * @return Return the result of the action. #SL_OK or #SL_ERROR */ -static SaveOrLoadResult DoSave(SaveFilter *writer, bool threaded) +static SaveOrLoadResult DoSave(std::shared_ptr writer, bool threaded) { assert(!_sl.saveinprogress); - _sl.dumper = new MemoryDumper(); - _sl.sf = writer; + _sl.dumper = std::make_unique(); + _sl.sf = std::move(writer); _sl_version = SAVEGAME_VERSION; SlXvSetCurrentState(); @@ -3576,12 +3561,12 @@ static SaveOrLoadResult DoSave(SaveFilter *writer, bool threaded) * @param flags Save mode flags. * @return Return the result of the action. #SL_OK or #SL_ERROR */ -SaveOrLoadResult SaveWithFilter(SaveFilter *writer, bool threaded, SaveModeFlags flags) +SaveOrLoadResult SaveWithFilter(std::shared_ptr writer, bool threaded, SaveModeFlags flags) { try { _sl.action = SLA_SAVE; _sl.save_flags = flags; - return DoSave(writer, threaded); + return DoSave(std::move(writer), threaded); } catch (...) { ClearSaveLoadState(); return SL_ERROR; @@ -3620,7 +3605,7 @@ struct ThreadedLoadFilter : LoadFilter { * Initialise this filter. * @param chain The next filter in this chain. */ - ThreadedLoadFilter(LoadFilter *chain) : LoadFilter(chain) + ThreadedLoadFilter(std::shared_ptr chain) : LoadFilter(std::move(chain)) { std::unique_lock lk(this->mutex); if (!StartNewThread(&this->read_thread, "ottd:loadgame", &ThreadedLoadFilter::RunThread, this)) { @@ -3710,9 +3695,9 @@ struct ThreadedLoadFilter : LoadFilter { * @param load_check Whether to perform the checking ("preview") or actually load the game. * @return Return the result of the action. #SL_OK or #SL_REINIT ("unload" the game) */ -static SaveOrLoadResult DoLoad(LoadFilter *reader, bool load_check) +static SaveOrLoadResult DoLoad(std::shared_ptr reader, bool load_check) { - _sl.lf = reader; + _sl.lf = std::move(reader); if (load_check) { /* Clear previous check data */ @@ -3804,11 +3789,11 @@ static SaveOrLoadResult DoLoad(LoadFilter *reader, bool load_check) SlError(STR_GAME_SAVELOAD_ERROR_BROKEN_INTERNAL_ERROR, err_str); } - _sl.lf = fmt->init_load(_sl.lf); + _sl.lf = fmt->init_load(std::move(_sl.lf)); if (!(fmt->flags & SLF_NO_THREADED_LOAD)) { - _sl.lf = new ThreadedLoadFilter(_sl.lf); + _sl.lf = std::make_shared(std::move(_sl.lf)); } - _sl.reader = new ReadBuffer(_sl.lf); + _sl.reader = std::make_unique(_sl.lf); _next_offs = 0; upstream_sl::SlResetLoadState(); @@ -3948,11 +3933,11 @@ static SaveOrLoadResult DoLoad(LoadFilter *reader, bool load_check) * @param reader The filter to read the savegame from. * @return Return the result of the action. #SL_OK or #SL_REINIT ("unload" the game) */ -SaveOrLoadResult LoadWithFilter(LoadFilter *reader) +SaveOrLoadResult LoadWithFilter(std::shared_ptr reader) { try { _sl.action = SLA_LOAD; - return DoLoad(reader, false); + return DoLoad(std::move(reader), false); } catch (...) { ClearSaveLoadState(); @@ -4045,13 +4030,13 @@ SaveOrLoadResult SaveOrLoad(const std::string &filename, SaveLoadOperation fop, DEBUG(desync, 1, "save: %s; %s", debug_date_dumper().HexDate(), filename.c_str()); if (!_settings_client.gui.threaded_saves) threaded = false; - return DoSave(new FileWriter(fh), threaded); + return DoSave(std::make_shared(fh), threaded); } /* LOAD game */ assert(fop == SLO_LOAD || fop == SLO_CHECK); DEBUG(desync, 1, "load: %s", filename.c_str()); - return DoLoad(new FileReader(fh), fop == SLO_CHECK); + return DoLoad(std::make_shared(fh), fop == SLO_CHECK); } catch (...) { /* This code may be executed both for old and new save games. */ ClearSaveLoadState(); diff --git a/src/sl/saveload.h b/src/sl/saveload.h index 1a5502e3f2..29688d27c0 100644 --- a/src/sl/saveload.h +++ b/src/sl/saveload.h @@ -76,8 +76,8 @@ void DoExitSave(); void DoAutoOrNetsave(FiosNumberedSaveName &counter, bool threaded, FiosNumberedSaveName *lt_counter = nullptr); -SaveOrLoadResult SaveWithFilter(struct SaveFilter *writer, bool threaded, SaveModeFlags flags); -SaveOrLoadResult LoadWithFilter(struct LoadFilter *reader); +SaveOrLoadResult SaveWithFilter(std::shared_ptr writer, bool threaded, SaveModeFlags flags); +SaveOrLoadResult LoadWithFilter(std::shared_ptr reader); bool IsNetworkServerSave(); bool IsScenarioSave(); diff --git a/src/sl/saveload_buffer.h b/src/sl/saveload_buffer.h index bb0894a332..e9d069687a 100644 --- a/src/sl/saveload_buffer.h +++ b/src/sl/saveload_buffer.h @@ -29,14 +29,14 @@ struct ReadBuffer { byte buf[MEMORY_CHUNK_SIZE]; ///< Buffer we're going to read from. byte *bufp; ///< Location we're at reading the buffer. byte *bufe; ///< End of the buffer we can read from. - LoadFilter *reader; ///< The filter used to actually read. + std::shared_ptr reader; ///< The filter used to actually read. size_t read; ///< The amount of read bytes so far from the filter. /** * Initialise our variables. * @param reader The filter to actually read data. */ - ReadBuffer(LoadFilter *reader) : bufp(nullptr), bufe(nullptr), reader(reader), read(0) + ReadBuffer(std::shared_ptr reader) : bufp(nullptr), bufe(nullptr), reader(std::move(reader)), read(0) { } @@ -265,7 +265,7 @@ struct MemoryDumper { this->buf += 8; } - void Flush(SaveFilter *writer); + void Flush(SaveFilter &writer); size_t GetSize() const; void StartAutoLength(); std::pair StopAutoLength(); diff --git a/src/sl/saveload_filter.h b/src/sl/saveload_filter.h index 1785167ddc..7d75082e45 100644 --- a/src/sl/saveload_filter.h +++ b/src/sl/saveload_filter.h @@ -13,20 +13,19 @@ /** Interface for filtering a savegame till it is loaded. */ struct LoadFilter { /** Chained to the (savegame) filters. */ - LoadFilter *chain; + std::shared_ptr chain; /** * Initialise this filter. * @param chain The next filter in this chain. */ - LoadFilter(LoadFilter *chain) : chain(chain) + LoadFilter(std::shared_ptr chain) : chain(std::move(chain)) { } /** Make sure the writers are properly closed. */ virtual ~LoadFilter() { - delete this->chain; } /** @@ -51,28 +50,27 @@ struct LoadFilter { * @param chain The next filter in this chain. * @tparam T The type of load filter to create. */ -template LoadFilter *CreateLoadFilter(LoadFilter *chain) +template std::shared_ptr CreateLoadFilter(std::shared_ptr chain) { - return new T(chain); + return std::make_shared(chain); } /** Interface for filtering a savegame till it is written. */ struct SaveFilter { /** Chained to the (savegame) filters. */ - SaveFilter *chain; + std::shared_ptr chain; /** * Initialise this filter. * @param chain The next filter in this chain. */ - SaveFilter(SaveFilter *chain) : chain(chain) + SaveFilter(std::shared_ptr chain) : chain(std::move(chain)) { } /** Make sure the writers are properly closed. */ virtual ~SaveFilter() { - delete this->chain; } /** @@ -97,9 +95,9 @@ struct SaveFilter { * @param compression_level The requested level of compression. * @tparam T The type of save filter to create. */ -template SaveFilter *CreateSaveFilter(SaveFilter *chain, byte compression_level) +template std::shared_ptr CreateSaveFilter(std::shared_ptr chain, byte compression_level) { - return new T(chain, compression_level); + return std::make_shared(chain, compression_level); } #endif /* SL_SAVELOAD_FILTER_H */ diff --git a/src/sl/town_sl.cpp b/src/sl/town_sl.cpp index 8e1a9c066b..ac0453ce2b 100644 --- a/src/sl/town_sl.cpp +++ b/src/sl/town_sl.cpp @@ -183,31 +183,31 @@ static const SaveLoad _town_desc[] = { SLE_CONDARR(Town, unwanted, SLE_INT8, 8, SLV_4, SLV_104), SLE_CONDARR(Town, unwanted, SLE_INT8, MAX_COMPANIES, SLV_104, SL_MAX_VERSION), - SLE_CONDVAR(Town, supplied[CT_PASSENGERS].old_max, SLE_FILE_U16 | SLE_VAR_U32, SL_MIN_VERSION, SLV_9), - SLE_CONDVAR(Town, supplied[CT_MAIL].old_max, SLE_FILE_U16 | SLE_VAR_U32, SL_MIN_VERSION, SLV_9), - SLE_CONDVAR(Town, supplied[CT_PASSENGERS].new_max, SLE_FILE_U16 | SLE_VAR_U32, SL_MIN_VERSION, SLV_9), - SLE_CONDVAR(Town, supplied[CT_MAIL].new_max, SLE_FILE_U16 | SLE_VAR_U32, SL_MIN_VERSION, SLV_9), - SLE_CONDVAR(Town, supplied[CT_PASSENGERS].old_act, SLE_FILE_U16 | SLE_VAR_U32, SL_MIN_VERSION, SLV_9), - SLE_CONDVAR(Town, supplied[CT_MAIL].old_act, SLE_FILE_U16 | SLE_VAR_U32, SL_MIN_VERSION, SLV_9), - SLE_CONDVAR(Town, supplied[CT_PASSENGERS].new_act, SLE_FILE_U16 | SLE_VAR_U32, SL_MIN_VERSION, SLV_9), - SLE_CONDVAR(Town, supplied[CT_MAIL].new_act, SLE_FILE_U16 | SLE_VAR_U32, SL_MIN_VERSION, SLV_9), + SLE_CONDVAR(Town, supplied[0].old_max, SLE_FILE_U16 | SLE_VAR_U32, SL_MIN_VERSION, SLV_9), + SLE_CONDVAR(Town, supplied[2].old_max, SLE_FILE_U16 | SLE_VAR_U32, SL_MIN_VERSION, SLV_9), + SLE_CONDVAR(Town, supplied[0].new_max, SLE_FILE_U16 | SLE_VAR_U32, SL_MIN_VERSION, SLV_9), + SLE_CONDVAR(Town, supplied[2].new_max, SLE_FILE_U16 | SLE_VAR_U32, SL_MIN_VERSION, SLV_9), + SLE_CONDVAR(Town, supplied[0].old_act, SLE_FILE_U16 | SLE_VAR_U32, SL_MIN_VERSION, SLV_9), + SLE_CONDVAR(Town, supplied[2].old_act, SLE_FILE_U16 | SLE_VAR_U32, SL_MIN_VERSION, SLV_9), + SLE_CONDVAR(Town, supplied[0].new_act, SLE_FILE_U16 | SLE_VAR_U32, SL_MIN_VERSION, SLV_9), + SLE_CONDVAR(Town, supplied[2].new_act, SLE_FILE_U16 | SLE_VAR_U32, SL_MIN_VERSION, SLV_9), SLE_CONDNULL_X(4, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_CHILLPP, SL_CHILLPP_232)), - SLE_CONDVAR(Town, supplied[CT_PASSENGERS].old_max, SLE_UINT32, SLV_9, SLV_165), + SLE_CONDVAR(Town, supplied[0].old_max, SLE_UINT32, SLV_9, SLV_165), SLE_CONDNULL_X(4, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_CHILLPP, SL_CHILLPP_232)), - SLE_CONDVAR(Town, supplied[CT_MAIL].old_max, SLE_UINT32, SLV_9, SLV_165), + SLE_CONDVAR(Town, supplied[2].old_max, SLE_UINT32, SLV_9, SLV_165), SLE_CONDNULL_X(8, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_CHILLPP, SL_CHILLPP_232)), - SLE_CONDVAR(Town, supplied[CT_PASSENGERS].new_max, SLE_UINT32, SLV_9, SLV_165), + SLE_CONDVAR(Town, supplied[0].new_max, SLE_UINT32, SLV_9, SLV_165), SLE_CONDNULL_X(4, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_CHILLPP, SL_CHILLPP_232)), - SLE_CONDVAR(Town, supplied[CT_MAIL].new_max, SLE_UINT32, SLV_9, SLV_165), + SLE_CONDVAR(Town, supplied[2].new_max, SLE_UINT32, SLV_9, SLV_165), SLE_CONDNULL_X(8, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_CHILLPP, SL_CHILLPP_232)), - SLE_CONDVAR(Town, supplied[CT_PASSENGERS].old_act, SLE_UINT32, SLV_9, SLV_165), + SLE_CONDVAR(Town, supplied[0].old_act, SLE_UINT32, SLV_9, SLV_165), SLE_CONDNULL_X(4, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_CHILLPP, SL_CHILLPP_232)), - SLE_CONDVAR(Town, supplied[CT_MAIL].old_act, SLE_UINT32, SLV_9, SLV_165), + SLE_CONDVAR(Town, supplied[2].old_act, SLE_UINT32, SLV_9, SLV_165), SLE_CONDNULL_X(4, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_CHILLPP, SL_CHILLPP_232)), - SLE_CONDVAR(Town, supplied[CT_PASSENGERS].new_act, SLE_UINT32, SLV_9, SLV_165), + SLE_CONDVAR(Town, supplied[0].new_act, SLE_UINT32, SLV_9, SLV_165), SLE_CONDNULL_X(4, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_CHILLPP, SL_CHILLPP_232)), - SLE_CONDVAR(Town, supplied[CT_MAIL].new_act, SLE_UINT32, SLV_9, SLV_165), + SLE_CONDVAR(Town, supplied[2].new_act, SLE_UINT32, SLV_9, SLV_165), SLE_CONDNULL(2, SL_MIN_VERSION, SLV_164), ///< pct_pass_transported / pct_mail_transported, now computed on the fly SLE_CONDNULL_X(3, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_CHILLPP, SL_CHILLPP_232)), diff --git a/src/table/build_industry.h b/src/table/build_industry.h index c9d4d77d01..9c44e5016e 100644 --- a/src/table/build_industry.h +++ b/src/table/build_industry.h @@ -1128,8 +1128,10 @@ enum IndustryTypes { #define MI(tbl, sndc, snd, d, pc, ai1, ai2, ai3, ai4, ag1, ag2, ag3, ag4, col, \ c1, c2, c3, proc, p1, r1, p2, r2, m, a1, im1, a2, im2, a3, im3, pr, clim, bev, in, intx, s1, s2, s3) \ {tbl, {}, d, 0, pc, {c1, c2, c3}, proc, \ + {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}, \ {p1, p2, CT_INVALID, CT_INVALID, CT_INVALID, CT_INVALID, CT_INVALID, CT_INVALID, CT_INVALID, CT_INVALID, CT_INVALID, CT_INVALID, CT_INVALID, CT_INVALID, CT_INVALID, CT_INVALID}, \ {r1, r2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, m, \ + {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}, \ {a1, a2, a3, CT_INVALID, CT_INVALID, CT_INVALID, CT_INVALID, CT_INVALID, CT_INVALID, CT_INVALID, CT_INVALID, CT_INVALID, CT_INVALID, CT_INVALID, CT_INVALID, CT_INVALID}, \ {{im1, 0}, {im2, 0}, {im3, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}}, \ pr, clim, bev, col, in, intx, s1, s2, s3, STR_UNDEFINED, {ai1, ai2, ai3, ai4}, {ag1, ag2, ag3, ag4}, \ @@ -1530,7 +1532,11 @@ static const IndustrySpec _origin_industry_specs[NEW_INDUSTRYOFFSET] = { * @param a2 next frame of animation * @param a3 chooses between animation or construction state */ -#define MT(ca1, c1, ca2, c2, ca3, c3, sl, a1, a2, a3) {{c1, c2, c3, CT_INVALID, CT_INVALID, CT_INVALID, CT_INVALID, CT_INVALID, CT_INVALID, CT_INVALID, CT_INVALID, CT_INVALID, CT_INVALID, CT_INVALID, CT_INVALID, CT_INVALID}, {ca1, ca2, ca3}, sl, a1, a2, a3, 0, {0, ANIM_STATUS_NO_ANIMATION, 2, 0}, INDTILE_SPECIAL_NONE, true, GRFFileProps(INVALID_INDUSTRYTILE)} +#define MT(ca1, c1, ca2, c2, ca3, c3, sl, a1, a2, a3) { \ + {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}, \ + {c1, c2, c3, CT_INVALID, CT_INVALID, CT_INVALID, CT_INVALID, CT_INVALID, CT_INVALID, CT_INVALID, CT_INVALID, CT_INVALID, CT_INVALID, CT_INVALID, CT_INVALID, CT_INVALID}, \ + {ca1, ca2, ca3}, sl, a1, a2, a3, 0, {0, ANIM_STATUS_NO_ANIMATION, 2, 0}, INDTILE_SPECIAL_NONE, true, GRFFileProps(INVALID_INDUSTRYTILE) \ +} static const IndustryTileSpec _origin_industry_tile_specs[NEW_INDUSTRYTILEOFFSET] = { /* Coal Mine */ MT(0, CT_INVALID, 0, CT_INVALID, 0, CT_INVALID, SLOPE_STEEP, INDUSTRYTILE_NOANIM, INDUSTRYTILE_NOANIM, false), diff --git a/src/table/cargo_const.h b/src/table/cargo_const.h index f182e039df..41965aebd4 100644 --- a/src/table/cargo_const.h +++ b/src/table/cargo_const.h @@ -7,6 +7,8 @@ /** @file cargo_const.h Table of all default cargo types */ +#include + /** Construction macros for the #CargoSpec StringID entries. */ #define MK_STR_CARGO_PLURAL(label_plural) STR_CARGO_PLURAL_ ## label_plural #define MK_STR_CARGO_SINGULAR(label_singular) STR_CARGO_SINGULAR_ ## label_singular @@ -50,56 +52,56 @@ /** Cargo types available by default. */ static const CargoSpec _default_cargo[] = { - MK( 0, 'PASS', 152, 1, 0x400, 3185, 0, 24, false, TAE_PASSENGERS, PASSENGERS, PASSENGER, STR_PASSENGERS, CC_PASSENGERS), - MK( 1, 'COAL', 6, 16, 0x100, 5916, 7, 255, true, TAE_NONE, COAL, COAL, STR_TONS, CC_BULK), - MK( 2, 'MAIL', 15, 4, 0x200, 4550, 20, 90, false, TAE_MAIL, MAIL, MAIL, STR_BAGS, CC_MAIL), + MK( 0, CT_PASSENGERS, 152, 1, 0x400, 3185, 0, 24, false, TAE_PASSENGERS, PASSENGERS, PASSENGER, STR_PASSENGERS, CC_PASSENGERS), + MK( 1, CT_COAL, 6, 16, 0x100, 5916, 7, 255, true, TAE_NONE, COAL, COAL, STR_TONS, CC_BULK), + MK( 2, CT_MAIL, 15, 4, 0x200, 4550, 20, 90, false, TAE_MAIL, MAIL, MAIL, STR_BAGS, CC_MAIL), /* Oil in temperate and arctic */ - MK( 3, 'OIL_', 174, 16, 0x100, 4437, 25, 255, true, TAE_NONE, OIL, OIL, STR_LITERS, CC_LIQUID), + MK( 3, CT_OIL, 174, 16, 0x100, 4437, 25, 255, true, TAE_NONE, OIL, OIL, STR_LITERS, CC_LIQUID), /* Oil in subtropic */ - MK( 3, 'OIL_', 174, 16, 0x100, 4892, 25, 255, true, TAE_NONE, OIL, OIL, STR_LITERS, CC_LIQUID), - MK( 4, 'LVST', 208, 3, 0x100, 4322, 4, 18, true, TAE_NONE, LIVESTOCK, LIVESTOCK, STR_ITEMS, CC_PIECE_GOODS), - MK( 5, 'GOOD', 194, 8, 0x200, 6144, 5, 28, true, TAE_GOODS, GOODS, GOODS, STR_CRATES, CC_EXPRESS), - MK( 6, 'GRAI', 191, 16, 0x100, 4778, 4, 40, true, TAE_NONE, GRAIN, GRAIN, STR_TONS, CC_BULK), - MK( 6, 'WHEA', 191, 16, 0x100, 4778, 4, 40, true, TAE_NONE, WHEAT, WHEAT, STR_TONS, CC_BULK), - MK( 6, 'MAIZ', 191, 16, 0x100, 4322, 4, 40, true, TAE_NONE, MAIZE, MAIZE, STR_TONS, CC_BULK), + MK( 3, CT_OIL, 174, 16, 0x100, 4892, 25, 255, true, TAE_NONE, OIL, OIL, STR_LITERS, CC_LIQUID), + MK( 4, CT_LIVESTOCK, 208, 3, 0x100, 4322, 4, 18, true, TAE_NONE, LIVESTOCK, LIVESTOCK, STR_ITEMS, CC_PIECE_GOODS), + MK( 5, CT_GOODS, 194, 8, 0x200, 6144, 5, 28, true, TAE_GOODS, GOODS, GOODS, STR_CRATES, CC_EXPRESS), + MK( 6, CT_GRAIN, 191, 16, 0x100, 4778, 4, 40, true, TAE_NONE, GRAIN, GRAIN, STR_TONS, CC_BULK), + MK( 6, CT_WHEAT, 191, 16, 0x100, 4778, 4, 40, true, TAE_NONE, WHEAT, WHEAT, STR_TONS, CC_BULK), + MK( 6, CT_MAIZE, 191, 16, 0x100, 4322, 4, 40, true, TAE_NONE, MAIZE, MAIZE, STR_TONS, CC_BULK), /* Wood in temperate and arctic */ - MK( 7, 'WOOD', 84, 16, 0x100, 5005, 15, 255, true, TAE_NONE, WOOD, WOOD, STR_TONS, CC_PIECE_GOODS), + MK( 7, CT_WOOD, 84, 16, 0x100, 5005, 15, 255, true, TAE_NONE, WOOD, WOOD, STR_TONS, CC_PIECE_GOODS), /* Wood in subtropic */ - MK( 7, 'WOOD', 84, 16, 0x100, 7964, 15, 255, true, TAE_NONE, WOOD, WOOD, STR_TONS, CC_PIECE_GOODS), - MK( 8, 'IORE', 184, 16, 0x100, 5120, 9, 255, true, TAE_NONE, IRON_ORE, IRON_ORE, STR_TONS, CC_BULK), - MK( 9, 'STEL', 10, 16, 0x100, 5688, 7, 255, true, TAE_NONE, STEEL, STEEL, STR_TONS, CC_PIECE_GOODS), - MK( 10, 'VALU', 202, 2, 0x100, 7509, 1, 32, true, TAE_NONE, VALUABLES, VALUABLES, STR_BAGS, CC_ARMOURED), - MK( 10, 'GOLD', 202, 8, 0x100, 5802, 10, 40, true, TAE_NONE, GOLD, GOLD, STR_BAGS, CC_ARMOURED), - MK( 10, 'DIAM', 202, 2, 0x100, 5802, 10, 255, true, TAE_NONE, DIAMONDS, DIAMOND, STR_BAGS, CC_ARMOURED), - MK( 11, 'PAPR', 10, 16, 0x100, 5461, 7, 60, true, TAE_NONE, PAPER, PAPER, STR_TONS, CC_PIECE_GOODS), - MK( 12, 'FOOD', 48, 16, 0x100, 5688, 0, 30, true, TAE_FOOD, FOOD, FOOD, STR_TONS, CC_EXPRESS | CC_REFRIGERATED), - MK( 13, 'FRUT', 208, 16, 0x100, 4209, 0, 15, true, TAE_NONE, FRUIT, FRUIT, STR_TONS, CC_BULK | CC_REFRIGERATED), - MK( 14, 'CORE', 184, 16, 0x100, 4892, 12, 255, true, TAE_NONE, COPPER_ORE, COPPER_ORE, STR_TONS, CC_BULK), - MK( 15, 'WATR', 10, 16, 0x100, 4664, 20, 80, true, TAE_WATER, WATER, WATER, STR_LITERS, CC_LIQUID), - MK( 16, 'RUBR', 6, 16, 0x100, 4437, 2, 20, true, TAE_NONE, RUBBER, RUBBER, STR_LITERS, CC_LIQUID), - MK( 17, 'SUGR', 6, 16, 0x100, 4437, 20, 255, true, TAE_NONE, SUGAR, SUGAR, STR_TONS, CC_BULK), - MK( 18, 'TOYS', 174, 2, 0x100, 5574, 25, 255, true, TAE_NONE, TOYS, TOY, STR_ITEMS, CC_PIECE_GOODS), - MK( 19, 'BATT', 208, 4, 0x100, 4322, 2, 30, true, TAE_NONE, BATTERIES, BATTERY, STR_ITEMS, CC_PIECE_GOODS), - MK( 20, 'SWET', 194, 5, 0x200, 6144, 8, 40, true, TAE_GOODS, SWEETS, SWEETS, STR_BAGS, CC_EXPRESS), - MK( 21, 'TOFF', 191, 16, 0x100, 4778, 14, 60, true, TAE_NONE, TOFFEE, TOFFEE, STR_TONS, CC_BULK), - MK( 22, 'COLA', 84, 16, 0x100, 4892, 5, 75, true, TAE_NONE, COLA, COLA, STR_LITERS, CC_LIQUID), - MK( 23, 'CTCD', 184, 16, 0x100, 5005, 10, 25, true, TAE_NONE, CANDYFLOSS, CANDYFLOSS, STR_TONS, CC_BULK), - MK( 24, 'BUBL', 10, 1, 0x100, 5077, 20, 80, true, TAE_NONE, BUBBLES, BUBBLE, STR_ITEMS, CC_PIECE_GOODS), - MK( 25, 'PLST', 202, 16, 0x100, 4664, 30, 255, true, TAE_NONE, PLASTIC, PLASTIC, STR_LITERS, CC_LIQUID), - MK( 26, 'FZDR', 48, 2, 0x100, 6250, 30, 50, true, TAE_FOOD, FIZZY_DRINKS, FIZZY_DRINK, STR_ITEMS, CC_PIECE_GOODS), + MK( 7, CT_WOOD, 84, 16, 0x100, 7964, 15, 255, true, TAE_NONE, WOOD, WOOD, STR_TONS, CC_PIECE_GOODS), + MK( 8, CT_IRON_ORE, 184, 16, 0x100, 5120, 9, 255, true, TAE_NONE, IRON_ORE, IRON_ORE, STR_TONS, CC_BULK), + MK( 9, CT_STEEL, 10, 16, 0x100, 5688, 7, 255, true, TAE_NONE, STEEL, STEEL, STR_TONS, CC_PIECE_GOODS), + MK( 10, CT_VALUABLES, 202, 2, 0x100, 7509, 1, 32, true, TAE_NONE, VALUABLES, VALUABLES, STR_BAGS, CC_ARMOURED), + MK( 10, CT_GOLD, 202, 8, 0x100, 5802, 10, 40, true, TAE_NONE, GOLD, GOLD, STR_BAGS, CC_ARMOURED), + MK( 10, CT_DIAMONDS, 202, 2, 0x100, 5802, 10, 255, true, TAE_NONE, DIAMONDS, DIAMOND, STR_BAGS, CC_ARMOURED), + MK( 11, CT_PAPER, 10, 16, 0x100, 5461, 7, 60, true, TAE_NONE, PAPER, PAPER, STR_TONS, CC_PIECE_GOODS), + MK( 12, CT_FOOD, 48, 16, 0x100, 5688, 0, 30, true, TAE_FOOD, FOOD, FOOD, STR_TONS, CC_EXPRESS | CC_REFRIGERATED), + MK( 13, CT_FRUIT, 208, 16, 0x100, 4209, 0, 15, true, TAE_NONE, FRUIT, FRUIT, STR_TONS, CC_BULK | CC_REFRIGERATED), + MK( 14, CT_COPPER_ORE, 184, 16, 0x100, 4892, 12, 255, true, TAE_NONE, COPPER_ORE, COPPER_ORE, STR_TONS, CC_BULK), + MK( 15, CT_WATER, 10, 16, 0x100, 4664, 20, 80, true, TAE_WATER, WATER, WATER, STR_LITERS, CC_LIQUID), + MK( 16, CT_RUBBER, 6, 16, 0x100, 4437, 2, 20, true, TAE_NONE, RUBBER, RUBBER, STR_LITERS, CC_LIQUID), + MK( 17, CT_SUGAR, 6, 16, 0x100, 4437, 20, 255, true, TAE_NONE, SUGAR, SUGAR, STR_TONS, CC_BULK), + MK( 18, CT_TOYS, 174, 2, 0x100, 5574, 25, 255, true, TAE_NONE, TOYS, TOY, STR_ITEMS, CC_PIECE_GOODS), + MK( 19, CT_BATTERIES, 208, 4, 0x100, 4322, 2, 30, true, TAE_NONE, BATTERIES, BATTERY, STR_ITEMS, CC_PIECE_GOODS), + MK( 20, CT_CANDY, 194, 5, 0x200, 6144, 8, 40, true, TAE_GOODS, SWEETS, SWEETS, STR_BAGS, CC_EXPRESS), + MK( 21, CT_TOFFEE, 191, 16, 0x100, 4778, 14, 60, true, TAE_NONE, TOFFEE, TOFFEE, STR_TONS, CC_BULK), + MK( 22, CT_COLA, 84, 16, 0x100, 4892, 5, 75, true, TAE_NONE, COLA, COLA, STR_LITERS, CC_LIQUID), + MK( 23, CT_COTTON_CANDY, 184, 16, 0x100, 5005, 10, 25, true, TAE_NONE, CANDYFLOSS, CANDYFLOSS, STR_TONS, CC_BULK), + MK( 24, CT_BUBBLES, 10, 1, 0x100, 5077, 20, 80, true, TAE_NONE, BUBBLES, BUBBLE, STR_ITEMS, CC_PIECE_GOODS), + MK( 25, CT_PLASTIC, 202, 16, 0x100, 4664, 30, 255, true, TAE_NONE, PLASTIC, PLASTIC, STR_LITERS, CC_LIQUID), + MK( 26, CT_FIZZY_DRINKS, 48, 2, 0x100, 6250, 30, 50, true, TAE_FOOD, FIZZY_DRINKS, FIZZY_DRINK, STR_ITEMS, CC_PIECE_GOODS), /* Void slot in temperate */ - MK(0xFF, 0, 1, 0, 0x100, 5688, 0, 30, true, TAE_NONE, NOTHING, NOTHING, STR_TONS, CC_NOAVAILABLE), + MK(0xFF, CT_INVALID, 1, 0, 0x100, 5688, 0, 30, true, TAE_NONE, NOTHING, NOTHING, STR_TONS, CC_NOAVAILABLE), /* Void slot in arctic */ - MK(0xFF, 0, 184, 0, 0x100, 5120, 9, 255, true, TAE_NONE, NOTHING, NOTHING, STR_TONS, CC_NOAVAILABLE), + MK(0xFF, CT_INVALID, 184, 0, 0x100, 5120, 9, 255, true, TAE_NONE, NOTHING, NOTHING, STR_TONS, CC_NOAVAILABLE), }; /** Table of cargo types available in each climate, by default */ -static const CargoLabel _default_climate_cargo[NUM_LANDSCAPE][NUM_ORIGINAL_CARGO] = { - { 'PASS', 'COAL', 'MAIL', 'OIL_', 'LVST', 'GOOD', 'GRAI', 'WOOD', 'IORE', 'STEL', 'VALU', 33, }, - { 'PASS', 'COAL', 'MAIL', 'OIL_', 'LVST', 'GOOD', 'WHEA', 'WOOD', 34, 'PAPR', 'GOLD', 'FOOD', }, - { 'PASS', 'RUBR', 'MAIL', 4, 'FRUT', 'GOOD', 'MAIZ', 11, 'CORE', 'WATR', 'DIAM', 'FOOD', }, - { 'PASS', 'SUGR', 'MAIL', 'TOYS', 'BATT', 'SWET', 'TOFF', 'COLA', 'CTCD', 'BUBL', 'PLST', 'FZDR', }, +static const std::variant _default_climate_cargo[NUM_LANDSCAPE][NUM_ORIGINAL_CARGO] = { + { CT_PASSENGERS, CT_COAL, CT_MAIL, CT_OIL, CT_LIVESTOCK, CT_GOODS, CT_GRAIN, CT_WOOD, CT_IRON_ORE, CT_STEEL, CT_VALUABLES, 33, }, + { CT_PASSENGERS, CT_COAL, CT_MAIL, CT_OIL, CT_LIVESTOCK, CT_GOODS, CT_WHEAT, CT_WOOD, 34, CT_PAPER, CT_GOLD, CT_FOOD, }, + { CT_PASSENGERS, CT_RUBBER, CT_MAIL, 4, CT_FRUIT, CT_GOODS, CT_MAIZE, 11, CT_COPPER_ORE, CT_WATER, CT_DIAMONDS, CT_FOOD, }, + { CT_PASSENGERS, CT_SUGAR, CT_MAIL, CT_TOYS, CT_BATTERIES, CT_CANDY, CT_TOFFEE, CT_COLA, CT_COTTON_CANDY, CT_BUBBLES, CT_PLASTIC, CT_FIZZY_DRINKS, }, }; diff --git a/src/table/engines.h b/src/table/engines.h index d385a439cd..4b3d9e1b2d 100644 --- a/src/table/engines.h +++ b/src/table/engines.h @@ -24,7 +24,7 @@ * @param f Bitmask of the climates * @note the 5 between b and f is the load amount */ -#define MT(a, b, c, d, e, f) { CalTime::DAYS_TILL_ORIGINAL_BASE_YEAR + a, c, d, b, 5, f, e, 0, 8, 0, 0, 0, STR_EMPTY, CARGO_AGING_TICKS, INVALID_ENGINE, ExtraEngineFlags::None } +#define MT(a, b, c, d, e, f) { CalTime::DAYS_TILL_ORIGINAL_BASE_YEAR + a, c, d, b, 5, f, INVALID_CARGO, e, 0, 8, 0, 0, 0, STR_EMPTY, CARGO_AGING_TICKS, INVALID_ENGINE, ExtraEngineFlags::None } /** * Writes the properties of a multiple-unit train into the EngineInfo struct. @@ -37,7 +37,7 @@ * @param f Bitmask of the climates * @note the 5 between b and f is the load amount */ -#define MM(a, b, c, d, e, f) { CalTime::DAYS_TILL_ORIGINAL_BASE_YEAR + a, c, d, b, 5, f, e, 0, 8, 1 << EF_RAIL_IS_MU, 0, 0, STR_EMPTY, CARGO_AGING_TICKS, INVALID_ENGINE, ExtraEngineFlags::None } +#define MM(a, b, c, d, e, f) { CalTime::DAYS_TILL_ORIGINAL_BASE_YEAR + a, c, d, b, 5, f, INVALID_CARGO, e, 0, 8, 1 << EF_RAIL_IS_MU, 0, 0, STR_EMPTY, CARGO_AGING_TICKS, INVALID_ENGINE, ExtraEngineFlags::None } /** * Writes the properties of a train carriage into the EngineInfo struct. @@ -50,7 +50,7 @@ * @see MT * @note the 5 between b and f is the load amount */ -#define MW(a, b, c, d, e, f) { CalTime::DAYS_TILL_ORIGINAL_BASE_YEAR + a, c, d, b, 5, f, e, 0, 8, 0, 0, 0, STR_EMPTY, CARGO_AGING_TICKS, INVALID_ENGINE, ExtraEngineFlags::None } +#define MW(a, b, c, d, e, f) { CalTime::DAYS_TILL_ORIGINAL_BASE_YEAR + a, c, d, b, 5, f, INVALID_CARGO, e, 0, 8, 0, 0, 0, STR_EMPTY, CARGO_AGING_TICKS, INVALID_ENGINE, ExtraEngineFlags::None } /** * Writes the properties of a road vehicle into the EngineInfo struct. @@ -63,7 +63,7 @@ * @param f Bitmask of the climates * @note the 5 between b and f is the load amount */ -#define MR(a, b, c, d, e, f) { CalTime::DAYS_TILL_ORIGINAL_BASE_YEAR + a, c, d, b, 5, f, e, 0, 8, 0, 0, 0, STR_EMPTY, CARGO_AGING_TICKS, INVALID_ENGINE, ExtraEngineFlags::None } +#define MR(a, b, c, d, e, f) { CalTime::DAYS_TILL_ORIGINAL_BASE_YEAR + a, c, d, b, 5, f, INVALID_CARGO, e, 0, 8, 0, 0, 0, STR_EMPTY, CARGO_AGING_TICKS, INVALID_ENGINE, ExtraEngineFlags::None } /** * Writes the properties of a ship into the EngineInfo struct. @@ -75,7 +75,7 @@ * @param f Bitmask of the climates * @note the 10 between b and f is the load amount */ -#define MS(a, b, c, d, e, f) { CalTime::DAYS_TILL_ORIGINAL_BASE_YEAR + a, c, d, b, 10, f, e, 0, 8, 0, 0, 0, STR_EMPTY, CARGO_AGING_TICKS, INVALID_ENGINE, ExtraEngineFlags::None } +#define MS(a, b, c, d, e, f) { CalTime::DAYS_TILL_ORIGINAL_BASE_YEAR + a, c, d, b, 10, f, INVALID_CARGO, e, 0, 8, 0, 0, 0, STR_EMPTY, CARGO_AGING_TICKS, INVALID_ENGINE, ExtraEngineFlags::None } /** * Writes the properties of an aeroplane into the EngineInfo struct. @@ -86,7 +86,7 @@ * @param e Bitmask of the climates * @note the 20 between b and e is the load amount */ -#define MA(a, b, c, d, e) { CalTime::DAYS_TILL_ORIGINAL_BASE_YEAR + a, c, d, b, 20, e, CT_INVALID, 0, 8, 0, 0, 0, STR_EMPTY, CARGO_AGING_TICKS, INVALID_ENGINE, ExtraEngineFlags::None } +#define MA(a, b, c, d, e) { CalTime::DAYS_TILL_ORIGINAL_BASE_YEAR + a, c, d, b, 20, e, INVALID_CARGO, CT_INVALID, 0, 8, 0, 0, 0, STR_EMPTY, CARGO_AGING_TICKS, INVALID_ENGINE, ExtraEngineFlags::None } /* Climates * T = Temperate @@ -102,33 +102,33 @@ static const EngineInfo _orig_engine_info[] = { * | decay_speed cargo_type * | | lifelength | climates * | | | | | | */ - MT( 1827, 20, 15, 30, 0 , T ), // 0 Kirby Paul Tank (Steam) - MT( 12784, 20, 22, 30, 0 , A|S ), // 1 MJS 250 (Diesel) - MT( 9497, 20, 20, 50, 0 , Y), // 2 Ploddyphut Choo-Choo - MT( 11688, 20, 20, 30, 0 , Y), // 3 Powernaut Choo-Choo - MT( 16802, 20, 20, 30, 0 , Y), // 4 Mightymover Choo-Choo - MT( 18993, 20, 20, 30, 0 , Y), // 5 Ploddyphut Diesel - MT( 20820, 20, 20, 30, 0 , Y), // 6 Powernaut Diesel - MT( 8766, 20, 20, 30, 0 , A|S ), // 7 Wills 2-8-0 (Steam) - MT( 5114, 20, 21, 30, 0 , T ), // 8 Chaney 'Jubilee' (Steam) - MT( 5479, 20, 20, 30, 0 , T ), // 9 Ginzu 'A4' (Steam) - MT( 12419, 20, 23, 25, 0 , T ), // 10 SH '8P' (Steam) + MT( 1827, 20, 15, 30, CT_NONE , T ), // 0 Kirby Paul Tank (Steam) + MT( 12784, 20, 22, 30, CT_NONE , A|S ), // 1 MJS 250 (Diesel) + MT( 9497, 20, 20, 50, CT_NONE , Y), // 2 Ploddyphut Choo-Choo + MT( 11688, 20, 20, 30, CT_NONE , Y), // 3 Powernaut Choo-Choo + MT( 16802, 20, 20, 30, CT_NONE , Y), // 4 Mightymover Choo-Choo + MT( 18993, 20, 20, 30, CT_NONE , Y), // 5 Ploddyphut Diesel + MT( 20820, 20, 20, 30, CT_NONE , Y), // 6 Powernaut Diesel + MT( 8766, 20, 20, 30, CT_NONE , A|S ), // 7 Wills 2-8-0 (Steam) + MT( 5114, 20, 21, 30, CT_NONE , T ), // 8 Chaney 'Jubilee' (Steam) + MT( 5479, 20, 20, 30, CT_NONE , T ), // 9 Ginzu 'A4' (Steam) + MT( 12419, 20, 23, 25, CT_NONE , T ), // 10 SH '8P' (Steam) MM( 13149, 20, 12, 30, CT_PASSENGERS , T ), // 11 Manley-Morel DMU (Diesel) MM( 23376, 20, 15, 35, CT_PASSENGERS , T ), // 12 'Dash' (Diesel) - MT( 14976, 20, 18, 28, 0 , T ), // 13 SH/Hendry '25' (Diesel) - MT( 14245, 20, 20, 30, 0 , T ), // 14 UU '37' (Diesel) - MT( 15341, 20, 22, 33, 0 , T ), // 15 Floss '47' (Diesel) - MT( 14976, 20, 20, 25, 0 , A|S ), // 16 CS 4000 (Diesel) - MT( 16437, 20, 20, 30, 0 , A|S ), // 17 CS 2400 (Diesel) - MT( 18993, 20, 22, 30, 0 , A|S ), // 18 Centennial (Diesel) - MT( 13880, 20, 22, 30, 0 , A|S ), // 19 Kelling 3100 (Diesel) - MM( 20454, 20, 22, 30, 0 , A|S ), // 20 Turner Turbo (Diesel) - MT( 16071, 20, 22, 30, 0 , A|S ), // 21 MJS 1000 (Diesel) + MT( 14976, 20, 18, 28, CT_NONE , T ), // 13 SH/Hendry '25' (Diesel) + MT( 14245, 20, 20, 30, CT_NONE , T ), // 14 UU '37' (Diesel) + MT( 15341, 20, 22, 33, CT_NONE , T ), // 15 Floss '47' (Diesel) + MT( 14976, 20, 20, 25, CT_NONE , A|S ), // 16 CS 4000 (Diesel) + MT( 16437, 20, 20, 30, CT_NONE , A|S ), // 17 CS 2400 (Diesel) + MT( 18993, 20, 22, 30, CT_NONE , A|S ), // 18 Centennial (Diesel) + MT( 13880, 20, 22, 30, CT_NONE , A|S ), // 19 Kelling 3100 (Diesel) + MM( 20454, 20, 22, 30, CT_NONE , A|S ), // 20 Turner Turbo (Diesel) + MT( 16071, 20, 22, 30, CT_NONE , A|S ), // 21 MJS 1000 (Diesel) MT( 20820, 20, 20, 25, CT_MAIL , T ), // 22 SH '125' (Diesel) - MT( 16437, 20, 23, 30, 0 , T ), // 23 SH '30' (Electric) - MT( 19359, 20, 23, 80, 0 , T ), // 24 SH '40' (Electric) - MM( 23376, 20, 25, 30, 0 , T ), // 25 'T.I.M.' (Electric) - MM( 26298, 20, 25, 50, 0 , T ), // 26 'AsiaStar' (Electric) + MT( 16437, 20, 23, 30, CT_NONE , T ), // 23 SH '30' (Electric) + MT( 19359, 20, 23, 80, CT_NONE , T ), // 24 SH '40' (Electric) + MM( 23376, 20, 25, 30, CT_NONE , T ), // 25 'T.I.M.' (Electric) + MM( 26298, 20, 25, 50, CT_NONE , T ), // 26 'AsiaStar' (Electric) MW( 1827, 20, 20, 50, CT_PASSENGERS , T|A|S|Y), // 27 Passenger Carriage MW( 1827, 20, 20, 50, CT_MAIL , T|A|S|Y), // 28 Mail Van MW( 1827, 20, 20, 50, CT_COAL , T|A ), // 29 Coal Truck @@ -156,9 +156,9 @@ static const EngineInfo _orig_engine_info[] = { MW( 1827, 20, 20, 50, CT_BATTERIES , Y), // 51 Battery Truck MW( 1827, 20, 20, 50, CT_FIZZY_DRINKS, Y), // 52 Fizzy Drink Truck MW( 1827, 20, 20, 50, CT_PLASTIC , Y), // 53 Plastic Truck - MT( 28490, 20, 20, 50, 0 , T|A|S ), // 54 'X2001' (Electric) + MT( 28490, 20, 20, 50, CT_NONE , T|A|S ), // 54 'X2001' (Electric) MT( 31047, 20, 20, 50, CT_PASSENGERS , T|A|S ), // 55 'Millennium Z1' (Electric) - MT( 28855, 20, 20, 50, 0 , Y), // 56 Wizzowow Z99 + MT( 28855, 20, 20, 50, CT_NONE , Y), // 56 Wizzowow Z99 MW( 1827, 20, 20, 50, CT_PASSENGERS , T|A|S|Y), // 57 Passenger Carriage MW( 1827, 20, 20, 50, CT_MAIL , T|A|S|Y), // 58 Mail Van MW( 1827, 20, 20, 50, CT_COAL , T|A ), // 59 Coal Truck @@ -186,11 +186,11 @@ static const EngineInfo _orig_engine_info[] = { MW( 1827, 20, 20, 50, CT_BATTERIES , Y), // 81 Battery Truck MW( 1827, 20, 20, 50, CT_FIZZY_DRINKS, Y), // 82 Fizzy Drink Truck MW( 1827, 20, 20, 50, CT_PLASTIC , Y), // 83 Plastic Truck - MT( 36525, 20, 20, 50, 0 , T|A|S ), // 84 Lev1 'Leviathan' (Electric) - MT( 39447, 20, 20, 50, 0 , T|A|S ), // 85 Lev2 'Cyclops' (Electric) - MT( 42004, 20, 20, 50, 0 , T|A|S ), // 86 Lev3 'Pegasus' (Electric) - MT( 42735, 20, 20, 50, 0 , T|A|S ), // 87 Lev4 'Chimaera' (Electric) - MT( 36891, 20, 20, 60, 0 , Y), // 88 Wizzowow Rocketeer + MT( 36525, 20, 20, 50, CT_NONE , T|A|S ), // 84 Lev1 'Leviathan' (Electric) + MT( 39447, 20, 20, 50, CT_NONE , T|A|S ), // 85 Lev2 'Cyclops' (Electric) + MT( 42004, 20, 20, 50, CT_NONE , T|A|S ), // 86 Lev3 'Pegasus' (Electric) + MT( 42735, 20, 20, 50, CT_NONE , T|A|S ), // 87 Lev4 'Chimaera' (Electric) + MT( 36891, 20, 20, 60, CT_NONE , Y), // 88 Wizzowow Rocketeer MW( 1827, 20, 20, 50, CT_PASSENGERS , T|A|S|Y), // 89 Passenger Carriage MW( 1827, 20, 20, 50, CT_MAIL , T|A|S|Y), // 90 Mail Van MW( 1827, 20, 20, 50, CT_COAL , T|A ), // 91 Coal Truck diff --git a/src/table/newgrf_debug_data.h b/src/table/newgrf_debug_data.h index 685463c70f..95c837bf72 100644 --- a/src/table/newgrf_debug_data.h +++ b/src/table/newgrf_debug_data.h @@ -1346,7 +1346,7 @@ class NIHCargo : public NIHelper { const CargoSpec *spec = CargoSpec::Get(index); seprintf(buffer, lastof(buffer), " Bit: %2u, Label: %c%c%c%c, Callback mask: 0x%02X", 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); output.print(buffer); int written = seprintf(buffer, lastof(buffer), " Cargo class: %s%s%s%s%s%s%s%s%s%s%s", diff --git a/src/table/town_land.h b/src/table/town_land.h index 6e888c55a7..323541b04d 100644 --- a/src/table/town_land.h +++ b/src/table/town_land.h @@ -1813,6 +1813,7 @@ static_assert(lengthof(_town_draw_tile_data) == (NEW_HOUSE_OFFSET) * 4 * 4); #define MS(mnd, mxd, p, rc, bn, rr, mg, ca1, ca2, ca3, bf, ba, cg1, cg2, cg3) \ {mnd, mxd, p, rc, bn, rr, mg, \ {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}, \ {cg1, cg2, cg3, CT_INVALID, CT_INVALID, CT_INVALID, CT_INVALID, CT_INVALID, CT_INVALID, CT_INVALID, CT_INVALID, CT_INVALID, CT_INVALID, CT_INVALID, CT_INVALID, CT_INVALID}, \ bf, ba, true, GRFFileProps(INVALID_HOUSE_ID), 0, {COLOUR_BEGIN, COLOUR_BEGIN, COLOUR_BEGIN, COLOUR_BEGIN}, \ 16, NO_EXTRA_FLAG, HCF_NONE, HOUSE_NO_CLASS, {0, 2, 0, 0}, 0, 0, 0} diff --git a/src/textfile_gui.cpp b/src/textfile_gui.cpp index 211f2dfa7d..479f29484d 100644 --- a/src/textfile_gui.cpp +++ b/src/textfile_gui.cpp @@ -857,6 +857,9 @@ void TextfileWindow::LoadText(std::string_view buf) this->AfterLoadText(); CheckForMissingGlyphs(true, this); + + /* The font may have changed when searching for glyphs, so ensure widget sizes are updated just in case. */ + this->ReInit(); } /** diff --git a/src/town.h b/src/town.h index 666e17e4f9..bf4e216bb8 100644 --- a/src/town.h +++ b/src/town.h @@ -110,7 +110,11 @@ struct Town : TownPool::PoolItem<&_town_pool> { std::string text; ///< General text with additional information. - inline byte GetPercentTransported(CargoID cid) const { return this->supplied[cid].old_act * 256 / (this->supplied[cid].old_max + 1); } + inline byte GetPercentTransported(CargoID cid) const + { + if (!IsValidCargoID(cid)) return 0; + return this->supplied[cid].old_act * 256 / (this->supplied[cid].old_max + 1); + } StationList stations_near; ///< NOSAVE: List of nearby stations. diff --git a/src/town_cmd.cpp b/src/town_cmd.cpp index a8be80a09c..8c01f4d0ee 100644 --- a/src/town_cmd.cpp +++ b/src/town_cmd.cpp @@ -945,7 +945,7 @@ void AddAcceptedHouseCargo(HouseID house_id, TileIndex tile, CargoArray &accepta AddAcceptedCargoSetMask(accepts[1], GB(callback, 4, 4), acceptance, always_accepted); if (_settings_game.game_creation.landscape != LT_TEMPERATE && HasBit(callback, 12)) { /* The 'S' bit indicates food instead of goods */ - AddAcceptedCargoSetMask(CT_FOOD, GB(callback, 8, 4), acceptance, always_accepted); + AddAcceptedCargoSetMask(GetCargoIDByLabel(CT_FOOD), GB(callback, 8, 4), acceptance, always_accepted); } else { AddAcceptedCargoSetMask(accepts[2], GB(callback, 8, 4), acceptance, always_accepted); } @@ -2184,8 +2184,12 @@ void UpdateTownRadii() void UpdateTownMaxPass(Town *t) { - t->supplied[CT_PASSENGERS].old_max = _town_cargo_scaler.Scale(t->cache.population >> 3); - t->supplied[CT_MAIL].old_max = _town_cargo_scaler.Scale(t->cache.population >> 4); + for (const CargoSpec *cs : CargoSpec::town_production_cargoes[TPE_PASSENGERS]) { + t->supplied[cs->Index()].old_max = _town_cargo_scaler.Scale(t->cache.population >> 3); + } + for (const CargoSpec *cs : CargoSpec::town_production_cargoes[TPE_MAIL]) { + t->supplied[cs->Index()].old_max = _town_cargo_scaler.Scale(t->cache.population >> 4); + } } static void UpdateTownGrowthRate(Town *t); @@ -4171,10 +4175,19 @@ static uint GetNormalGrowthRate(Town *t) * The growth rate can only be decreased by this setting, not increased. */ uint32_t inverse_m = UINT32_MAX / m; - auto calculate_cargo_ratio_fix15 = [](const TransportedCargoStat &stat) -> uint32_t { - return stat.old_max ? ((uint64_t) (stat.old_act << 15)) / stat.old_max : 1 << 15; - }; - uint32_t cargo_ratio_fix16 = calculate_cargo_ratio_fix15(t->supplied[CT_PASSENGERS]) + calculate_cargo_ratio_fix15(t->supplied[CT_MAIL]); + + uint32_t cargo_ratio_fix16 = 0; + for (auto tpe : {TPE_PASSENGERS, TPE_MAIL}) { + uint32_t old_max = 0; + uint32_t old_act = 0; + for (const CargoSpec *cs : CargoSpec::town_production_cargoes[tpe]) { + const TransportedCargoStat &stat = t->supplied[cs->Index()]; + old_max += stat.old_max; + old_act += stat.old_act; + } + cargo_ratio_fix16 += old_max ? ((uint64_t) (old_act << 15)) / old_max : 1 << 15; + } + uint64_t cargo_dependant_part = (((uint64_t) cargo_ratio_fix16) * ((uint64_t) inverse_m) * _settings_game.economy.town_growth_cargo_transported) >> 16; uint64_t non_cargo_dependant_part = ((uint64_t) inverse_m) * (100 - _settings_game.economy.town_growth_cargo_transported); uint64_t total = (cargo_dependant_part + non_cargo_dependant_part); diff --git a/src/train_cmd.cpp b/src/train_cmd.cpp index dcb94c5027..9793b74c8e 100644 --- a/src/train_cmd.cpp +++ b/src/train_cmd.cpp @@ -1477,6 +1477,7 @@ static CommandCost CmdBuildRailWagon(TileIndex tile, DoCommandFlag flags, const InvalidateWindowData(WC_VEHICLE_DEPOT, v->tile); v->cargo_type = e->GetDefaultCargoType(); + assert(IsValidCargoID(v->cargo_type)); v->cargo_cap = rvi->capacity; v->refit_cap = 0; @@ -1613,6 +1614,7 @@ CommandCost CmdBuildRailVehicle(TileIndex tile, DoCommandFlag flags, const Engin v->vehstatus = VS_HIDDEN | VS_STOPPED | VS_DEFPAL; v->spritenum = rvi->image_index; v->cargo_type = e->GetDefaultCargoType(); + assert(IsValidCargoID(v->cargo_type)); v->cargo_cap = rvi->capacity; v->refit_cap = 0; v->last_station_visited = INVALID_STATION; diff --git a/src/vehicle.cpp b/src/vehicle.cpp index 3ea7aeba57..cc99996d0d 100644 --- a/src/vehicle.cpp +++ b/src/vehicle.cpp @@ -3069,8 +3069,9 @@ LiveryScheme GetEngineLiveryScheme(EngineID engine_type, EngineID parent_engine_ /* Note: Luckily cargo_type is not needed for engines */ } - if (cargo_type == INVALID_CARGO) cargo_type = e->GetDefaultCargoType(); - if (cargo_type == INVALID_CARGO) cargo_type = CT_GOODS; // The vehicle does not carry anything, let's pick some freight cargo + if (!IsValidCargoID(cargo_type)) cargo_type = e->GetDefaultCargoType(); + if (!IsValidCargoID(cargo_type)) cargo_type = GetCargoIDByLabel(CT_GOODS); // The vehicle does not carry anything, let's pick some freight cargo + assert(IsValidCargoID(cargo_type)); if (e->u.rail.railveh_type == RAILVEH_WAGON) { if (!CargoSpec::Get(cargo_type)->is_freight) { if (parent_engine_type == INVALID_ENGINE) { @@ -3109,8 +3110,9 @@ LiveryScheme GetEngineLiveryScheme(EngineID engine_type, EngineID parent_engine_ e = Engine::Get(engine_type); cargo_type = v->First()->cargo_type; } - if (cargo_type == INVALID_CARGO) cargo_type = e->GetDefaultCargoType(); - if (cargo_type == INVALID_CARGO) cargo_type = CT_GOODS; // The vehicle does not carry anything, let's pick some freight cargo + if (!IsValidCargoID(cargo_type)) cargo_type = e->GetDefaultCargoType(); + if (!IsValidCargoID(cargo_type)) cargo_type = GetCargoIDByLabel(CT_GOODS); // The vehicle does not carry anything, let's pick some freight cargo + assert(IsValidCargoID(cargo_type)); /* Important: Use Tram Flag of front part. Luckily engine_type refers to the front part here. */ if (HasBit(e->info.misc_flags, EF_ROAD_TRAM)) { @@ -3122,8 +3124,9 @@ LiveryScheme GetEngineLiveryScheme(EngineID engine_type, EngineID parent_engine_ } case VEH_SHIP: - if (cargo_type == INVALID_CARGO) cargo_type = e->GetDefaultCargoType(); - if (cargo_type == INVALID_CARGO) cargo_type = CT_GOODS; // The vehicle does not carry anything, let's pick some freight cargo + if (!IsValidCargoID(cargo_type)) cargo_type = e->GetDefaultCargoType(); + if (!IsValidCargoID(cargo_type)) cargo_type = GetCargoIDByLabel(CT_GOODS); // The vehicle does not carry anything, let's pick some freight cargo + assert(IsValidCargoID(cargo_type)); return IsCargoInClass(cargo_type, CC_PASSENGERS) ? LS_PASSENGER_SHIP : LS_FREIGHT_SHIP; case VEH_AIRCRAFT: diff --git a/src/vehicle_cmd.cpp b/src/vehicle_cmd.cpp index 3dfd1a81e7..60a48eeeef 100644 --- a/src/vehicle_cmd.cpp +++ b/src/vehicle_cmd.cpp @@ -178,7 +178,8 @@ CommandCost CmdBuildVehicle(TileIndex tile, DoCommandFlag flags, uint32_t p1, ui _returned_refit_capacity = e->GetDisplayDefaultCapacity(&_returned_mail_refit_capacity); _returned_vehicle_capacities.Clear(); _returned_vehicle_capacities[default_cargo] = _returned_refit_capacity; - _returned_vehicle_capacities[CT_MAIL] = _returned_mail_refit_capacity; + CargoID mail = GetCargoIDByLabel(CT_MAIL); + if (IsValidCargoID(mail)) _returned_vehicle_capacities[mail] = _returned_mail_refit_capacity; } } @@ -438,7 +439,8 @@ static CommandCost RefitVehicle(Vehicle *v, bool only_this, uint8_t num_vehicles total_mail_capacity += mail_capacity; _returned_vehicle_capacities[new_cid] += amount; - _returned_vehicle_capacities[CT_MAIL] += mail_capacity; + CargoID mail = GetCargoIDByLabel(CT_MAIL); + if (IsValidCargoID(mail)) _returned_vehicle_capacities[mail] += mail_capacity; if (!refittable) continue; diff --git a/src/vehicle_gui.cpp b/src/vehicle_gui.cpp index ea90856571..6487a026c8 100644 --- a/src/vehicle_gui.cpp +++ b/src/vehicle_gui.cpp @@ -1040,7 +1040,7 @@ struct RefitWindow : public Window { Money money = cost.GetCost(); if (_returned_mail_refit_capacity > 0) { - SetDParam(2, CT_MAIL); + SetDParam(2, GetCargoIDByLabel(CT_MAIL)); SetDParam(3, _returned_mail_refit_capacity); if (this->order != INVALID_VEH_ORDER_ID) { /* No predictable cost */ diff --git a/src/zoning_cmd.cpp b/src/zoning_cmd.cpp index d815a83bb8..c2f3d6f6bc 100644 --- a/src/zoning_cmd.cpp +++ b/src/zoning_cmd.cpp @@ -193,14 +193,23 @@ SpriteID TileZoneCheckUnservedBuildingsEvaluation(TileIndex tile, Owner owner) return ZONING_INVALID_SPRITE_ID; } + auto has_town_cargo = [&](const CargoArray &dat) { + for (auto tpe : {TPE_PASSENGERS, TPE_MAIL}) { + for (const CargoSpec *cs : CargoSpec::town_production_cargoes[tpe]) { + if (dat[cs->Index()] > 0) return true; + } + } + return false; + }; + CargoArray dat{}; dat.Clear(); AddAcceptedCargo(tile, dat, nullptr); - if (dat[CT_MAIL] + dat[CT_PASSENGERS] == 0) { - // nothing is accepted, so now test if cargo is produced + if (!has_town_cargo(dat)) { + /* nothing is accepted, so now test if cargo is produced */ AddProducedCargo(tile, dat); - if (dat[CT_MAIL] + dat[CT_PASSENGERS] == 0) { - // total is still 0, so give up + if (!has_town_cargo(dat)) { + /* still don't have town cargo, so give up */ return ZONING_INVALID_SPRITE_ID; } }