diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 597c4331f5..56cb816248 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -254,6 +254,7 @@ add_files( music_gui.cpp newgrf.cpp newgrf.h + newgrf_act5.h newgrf_airport.cpp newgrf_airport.h newgrf_airporttiles.cpp diff --git a/src/lang/english.txt b/src/lang/english.txt index 0142ed31d4..860915ac3c 100644 --- a/src/lang/english.txt +++ b/src/lang/english.txt @@ -3488,7 +3488,9 @@ STR_NEWGRF_INSPECT_CAPTION_OBJECT_AT_ROAD_TYPE :Road type STR_NEWGRF_INSPECT_QUERY_CAPTION :{WHITE}NewGRF variable 60+x parameter (hexadecimal) # Sprite aligner window -STR_SPRITE_ALIGNER_CAPTION :{WHITE}Aligning sprite {COMMA} ({RAW_STRING}) +STR_SPRITE_ALIGNER_CAPTION_NO_ACTION :{WHITE}Aligning sprite: ({RAW_STRING}:{NUM}) +STR_SPRITE_ALIGNER_CAPTION_ACTIONA :{WHITE}Aligning sprite: Action 0xA, {COMMA} ({RAW_STRING}:{NUM}) +STR_SPRITE_ALIGNER_CAPTION_ACTION5 :{WHITE}Aligning sprite: Action 0x5, type {HEX}, {COMMA} ({RAW_STRING}:{NUM}) STR_SPRITE_ALIGNER_NEXT_BUTTON :{BLACK}Next sprite STR_SPRITE_ALIGNER_NEXT_TOOLTIP :{BLACK}Proceed to the next normal sprite, skipping any pseudo/recolour/font sprites and wrapping around from the last sprite to the first STR_SPRITE_ALIGNER_GOTO_BUTTON :{BLACK}Go to sprite @@ -3497,6 +3499,7 @@ STR_SPRITE_ALIGNER_PREVIOUS_BUTTON :{BLACK}Previous STR_SPRITE_ALIGNER_PREVIOUS_TOOLTIP :{BLACK}Proceed to the previous normal sprite, skipping any pseudo/recolour/font sprites and wrapping around from the first sprite to the last STR_SPRITE_ALIGNER_SPRITE_TOOLTIP :{BLACK}Representation of the currently selected sprite. The alignment is ignored when drawing this sprite STR_SPRITE_ALIGNER_MOVE_TOOLTIP :{BLACK}Move the sprite around, changing the X and Y offsets. Ctrl+Click to move the sprite eight units at a time +STR_SPRITE_ALIGNER_SPRITE :{RAW_STRING}:{NUM} ###length 2 STR_SPRITE_ALIGNER_CENTRE_OFFSET :{BLACK}Offset centred @@ -5833,6 +5836,7 @@ STR_JUST_DATE_ISO :{DATE_ISO} STR_JUST_STRING :{STRING} STR_JUST_STRING1 :{STRING1} STR_JUST_STRING2 :{STRING2} +STR_JUST_STRING4 :{STRING4} STR_JUST_STRING_STRING :{STRING}{STRING} STR_JUST_RAW_STRING :{RAW_STRING} STR_JUST_BIG_RAW_STRING :{BIG_FONT}{RAW_STRING} diff --git a/src/newgrf.cpp b/src/newgrf.cpp index f3ba26801d..1203a19ef9 100644 --- a/src/newgrf.cpp +++ b/src/newgrf.cpp @@ -27,6 +27,7 @@ #include "newgrf_station.h" #include "industrytype.h" #include "industry_map.h" +#include "newgrf_act5.h" #include "newgrf_canal.h" #include "newgrf_townname.h" #include "newgrf_industries.h" @@ -6369,7 +6370,7 @@ static void FeatureNewName(ByteReader *buf) * @param name Used for error warnings. * @return The number of sprites that is going to be skipped. */ -static uint16_t SanitizeSpriteOffset(uint16_t &num, uint16_t offset, int max_sprites, const char *name) +static uint16_t SanitizeSpriteOffset(uint16_t &num, uint16_t offset, int max_sprites, const std::string_view name) { if (offset >= max_sprites) { @@ -6390,23 +6391,8 @@ static uint16_t SanitizeSpriteOffset(uint16_t &num, uint16_t offset, int max_spr } -/** The type of action 5 type. */ -enum Action5BlockType { - A5BLOCK_FIXED, ///< Only allow replacing a whole block of sprites. (TTDP compatible) - A5BLOCK_ALLOW_OFFSET, ///< Allow replacing any subset by specifiing an offset. - A5BLOCK_INVALID, ///< unknown/not-implemented type -}; -/** Information about a single action 5 type. */ -struct Action5Type { - Action5BlockType block_type; ///< How is this Action5 type processed? - SpriteID sprite_base; ///< Load the sprites starting from this sprite. - uint16_t min_sprites; ///< If the Action5 contains less sprites, the whole block will be ignored. - uint16_t max_sprites; ///< If the Action5 contains more sprites, only the first max_sprites sprites will be used. - const char *name; ///< Name for error messages. -}; - /** The information about action 5 types. */ -static const Action5Type _action5_types[] = { +static constexpr auto _action5_types = std::to_array({ /* Note: min_sprites should not be changed. Therefore these constants are directly here and not in sprites.h */ /* 0x00 */ { A5BLOCK_INVALID, 0, 0, 0, "Type 0x00" }, /* 0x01 */ { A5BLOCK_INVALID, 0, 0, 0, "Type 0x01" }, @@ -6433,7 +6419,16 @@ static const Action5Type _action5_types[] = { /* 0x16 */ { A5BLOCK_ALLOW_OFFSET, SPR_AIRPORT_PREVIEW_BASE, 1, SPR_AIRPORT_PREVIEW_COUNT, "Airport preview graphics" }, /* 0x17 */ { A5BLOCK_ALLOW_OFFSET, SPR_RAILTYPE_TUNNEL_BASE, 1, RAILTYPE_TUNNEL_BASE_COUNT, "Railtype tunnel base" }, /* 0x18 */ { A5BLOCK_ALLOW_OFFSET, SPR_PALETTE_BASE, 1, PALETTE_SPRITE_COUNT, "Palette" }, -}; +}); + +/** + * Get list of all action 5 types + * @return Read-only span of action 5 type information. + */ +std::span GetAction5Types() +{ + return _action5_types; +} /* Action 0x05 */ static void GraphicsNew(ByteReader *buf) @@ -6468,7 +6463,7 @@ static void GraphicsNew(ByteReader *buf) } /* Supported type? */ - if ((type >= lengthof(_action5_types)) || (_action5_types[type].block_type == A5BLOCK_INVALID)) { + if ((type >= std::size(_action5_types)) || (_action5_types[type].block_type == A5BLOCK_INVALID)) { GrfMsg(2, "GraphicsNew: Custom graphics (type 0x{:02X}) sprite block of length {} (unimplemented, ignoring)", type, num); _cur.skip_sprites = num; return; diff --git a/src/newgrf_act5.h b/src/newgrf_act5.h new file mode 100644 index 0000000000..f16872424f --- /dev/null +++ b/src/newgrf_act5.h @@ -0,0 +1,31 @@ +/* + * This file is part of OpenTTD. + * OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2. + * OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see . + */ + +/** @file newgrf_act5.h Information about NewGRF Action 5. */ + +#ifndef NEWGRF_ACT5_H +#define NEWGRF_ACT5_H + +/** The type of action 5 type. */ +enum Action5BlockType { + A5BLOCK_FIXED, ///< Only allow replacing a whole block of sprites. (TTDP compatible) + A5BLOCK_ALLOW_OFFSET, ///< Allow replacing any subset by specifiing an offset. + A5BLOCK_INVALID, ///< unknown/not-implemented type +}; + +/** Information about a single action 5 type. */ +struct Action5Type { + Action5BlockType block_type; ///< How is this Action5 type processed? + SpriteID sprite_base; ///< Load the sprites starting from this sprite. + uint16_t min_sprites; ///< If the Action5 contains less sprites, the whole block will be ignored. + uint16_t max_sprites; ///< If the Action5 contains more sprites, only the first max_sprites sprites will be used. + const std::string_view name; ///< Name for error messages. +}; + +std::span GetAction5Types(); + +#endif /* NEWGRF_ACT5_H */ diff --git a/src/newgrf_debug_gui.cpp b/src/newgrf_debug_gui.cpp index fe9447edc1..c1ed16261d 100644 --- a/src/newgrf_debug_gui.cpp +++ b/src/newgrf_debug_gui.cpp @@ -9,6 +9,7 @@ #include "stdafx.h" #include "core/backup_type.hpp" +#include "core/geometry_func.hpp" #include "window_gui.h" #include "window_func.h" #include "random_access_file_type.h" @@ -28,6 +29,7 @@ #include "train.h" #include "roadveh.h" +#include "newgrf_act5.h" #include "newgrf_airport.h" #include "newgrf_airporttiles.h" #include "newgrf_debug.h" @@ -808,7 +810,6 @@ GrfSpecFeature GetGrfSpecFeature(VehicleType type) } - /**** Sprite Aligner ****/ /** Window used for aligning sprites. */ @@ -822,6 +823,7 @@ struct SpriteAlignerWindow : Window { static inline ZoomLevel zoom = ZOOM_LVL_END; static bool centre; static bool crosshair; + const Action5Type *act5_type = nullptr; ///< Sprite Area of current selected sprite. SpriteAlignerWindow(WindowDesc *desc, WindowNumber wno) : Window(desc) { @@ -829,6 +831,10 @@ struct SpriteAlignerWindow : Window { if (SpriteAlignerWindow::zoom == ZOOM_LVL_END) SpriteAlignerWindow::zoom = _gui_zoom; SpriteAlignerWindow::zoom = Clamp(SpriteAlignerWindow::zoom, _settings_client.gui.zoom_min, _settings_client.gui.zoom_max); + /* Oh yes, we assume there is at least one normal sprite! */ + while (GetSpriteType(this->current_sprite) != SpriteType::Normal) this->current_sprite++; + this->SelectAction5Type(); + this->CreateNestedTree(); this->vscroll = this->GetScrollbar(WID_SA_SCROLLBAR); this->vscroll->SetCount(_newgrf_debug_sprite_picker.sprites.size()); @@ -837,9 +843,6 @@ struct SpriteAlignerWindow : Window { this->SetWidgetLoweredState(WID_SA_CENTRE, SpriteAlignerWindow::centre); this->SetWidgetLoweredState(WID_SA_CROSSHAIR, SpriteAlignerWindow::crosshair); - /* Oh yes, we assume there is at least one normal sprite! */ - while (GetSpriteType(this->current_sprite) != SpriteType::Normal) this->current_sprite++; - this->InvalidateData(0, true); } @@ -848,8 +851,22 @@ struct SpriteAlignerWindow : Window { const Sprite *spr = GetSprite(this->current_sprite, SpriteType::Normal); switch (widget) { case WID_SA_CAPTION: - SetDParam(0, this->current_sprite); - SetDParamStr(1, GetOriginFile(this->current_sprite)->GetSimplifiedFilename()); + if (this->act5_type != nullptr) { + SetDParam(0, STR_SPRITE_ALIGNER_CAPTION_ACTION5); + SetDParam(1, this->act5_type - GetAction5Types().data()); + SetDParam(2, this->current_sprite - this->act5_type->sprite_base); + SetDParamStr(3, GetOriginFile(this->current_sprite)->GetSimplifiedFilename()); + SetDParam(4, GetSpriteLocalID(this->current_sprite)); + } else if (this->current_sprite < SPR_OPENTTD_BASE) { + SetDParam(0, STR_SPRITE_ALIGNER_CAPTION_ACTIONA); + SetDParam(1, this->current_sprite); + SetDParamStr(2, GetOriginFile(this->current_sprite)->GetSimplifiedFilename()); + SetDParam(3, GetSpriteLocalID(this->current_sprite)); + } else { + SetDParam(0, STR_SPRITE_ALIGNER_CAPTION_NO_ACTION); + SetDParamStr(1, GetOriginFile(this->current_sprite)->GetSimplifiedFilename()); + SetDParam(2, GetSpriteLocalID(this->current_sprite)); + } break; case WID_SA_OFFSETS_ABS: @@ -883,13 +900,21 @@ struct SpriteAlignerWindow : Window { case WID_SA_SPRITE: size->height = ScaleGUITrad(200); break; - case WID_SA_LIST: - SetDParamMaxDigits(0, 6); - size->width = GetStringBoundingBox(STR_JUST_COMMA).width + padding.width; + + case WID_SA_LIST: { + Dimension d = {}; + for (const auto &spritefile : GetCachedSpriteFiles()) { + SetDParamStr(0, spritefile->GetSimplifiedFilename()); + SetDParamMaxDigits(1, 6); + d = maxdim(d, GetStringBoundingBox(STR_SPRITE_ALIGNER_SPRITE)); + } + size->width = d.width + padding.width; resize->height = GetCharacterHeight(FS_NORMAL) + padding.height; - resize->width = 1; + resize->width = 1; fill->height = resize->height; break; + } + default: break; } @@ -941,8 +966,15 @@ struct SpriteAlignerWindow : Window { Rect ir = r.Shrink(WidgetDimensions::scaled.matrix); auto [first, last] = this->vscroll->GetVisibleRangeIterators(list); for (auto it = first; it != last; ++it) { - SetDParam(0, *it); - DrawString(ir, STR_JUST_COMMA, *it == this->current_sprite ? TC_WHITE : TC_BLACK, SA_RIGHT | SA_FORCE); + const SpriteFile *file = GetOriginFile(*it); + if (file == nullptr) { + SetDParam(0, *it); + DrawString(ir, STR_JUST_COMMA, *it == this->current_sprite ? TC_WHITE : (TC_GREY | TC_NO_SHADE), SA_RIGHT | SA_FORCE); + } else { + SetDParamStr(0, file->GetSimplifiedFilename()); + SetDParam(1, GetSpriteLocalID(*it)); + DrawString(ir, STR_SPRITE_ALIGNER_SPRITE, *it == this->current_sprite ? TC_WHITE : TC_BLACK); + } ir.top += step_size; } break; @@ -957,6 +989,7 @@ struct SpriteAlignerWindow : Window { do { this->current_sprite = (this->current_sprite == 0 ? GetMaxSpriteID() : this->current_sprite) - 1; } while (GetSpriteType(this->current_sprite) != SpriteType::Normal); + this->SelectAction5Type(); this->SetDirty(); break; @@ -968,6 +1001,7 @@ struct SpriteAlignerWindow : Window { do { this->current_sprite = (this->current_sprite + 1) % GetMaxSpriteID(); } while (GetSpriteType(this->current_sprite) != SpriteType::Normal); + this->SelectAction5Type(); this->SetDirty(); break; @@ -983,6 +1017,7 @@ struct SpriteAlignerWindow : Window { SpriteID spr = *it; if (GetSpriteType(spr) == SpriteType::Normal) this->current_sprite = spr; } + this->SelectAction5Type(); this->SetDirty(); break; } @@ -1060,6 +1095,7 @@ struct SpriteAlignerWindow : Window { while (GetSpriteType(this->current_sprite) != SpriteType::Normal) { this->current_sprite = (this->current_sprite + 1) % GetMaxSpriteID(); } + this->SelectAction5Type(); this->SetDirty(); } @@ -1088,6 +1124,19 @@ struct SpriteAlignerWindow : Window { { this->vscroll->SetCapacityFromWidget(this, WID_SA_LIST); } + +private: + void SelectAction5Type() + { + const auto act5types = GetAction5Types(); + for (auto it = std::begin(act5types); it != std::end(act5types); ++it) { + if (it->sprite_base <= this->current_sprite && this->current_sprite < it->sprite_base + it->max_sprites) { + this->act5_type = &*it; + return; + } + } + this->act5_type = nullptr; + } }; bool SpriteAlignerWindow::centre = true; @@ -1096,7 +1145,7 @@ bool SpriteAlignerWindow::crosshair = true; static constexpr NWidgetPart _nested_sprite_aligner_widgets[] = { NWidget(NWID_HORIZONTAL), NWidget(WWT_CLOSEBOX, COLOUR_GREY), - NWidget(WWT_CAPTION, COLOUR_GREY, WID_SA_CAPTION), SetDataTip(STR_SPRITE_ALIGNER_CAPTION, STR_TOOLTIP_WINDOW_TITLE_DRAG_THIS), + NWidget(WWT_CAPTION, COLOUR_GREY, WID_SA_CAPTION), SetDataTip(STR_JUST_STRING4, STR_TOOLTIP_WINDOW_TITLE_DRAG_THIS), NWidget(WWT_SHADEBOX, COLOUR_GREY), NWidget(WWT_STICKYBOX, COLOUR_GREY), EndContainer(), diff --git a/src/spritecache.cpp b/src/spritecache.cpp index 28cd97bac8..ca7fbba6c9 100644 --- a/src/spritecache.cpp +++ b/src/spritecache.cpp @@ -74,6 +74,15 @@ static SpriteFile *GetCachedSpriteFileByName(const std::string &filename) return nullptr; } +/** + * Get the list of cached SpriteFiles. + * @return Read-only list of cache SpriteFiles. + */ +std::span> GetCachedSpriteFiles() +{ + return _sprite_files; +} + /** * Open/get the SpriteFile that is cached for use in the sprite cache. * @param filename Name of the file at the disk. diff --git a/src/spritecache.h b/src/spritecache.h index 48af9a54e4..78447d700d 100644 --- a/src/spritecache.h +++ b/src/spritecache.h @@ -62,6 +62,7 @@ void GfxClearFontSpriteCache(); void IncreaseSpriteLRU(); SpriteFile &OpenCachedSpriteFile(const std::string &filename, Subdirectory subdir, bool palette_remap); +std::span> GetCachedSpriteFiles(); void ReadGRFSpriteOffsets(SpriteFile &file); size_t GetGRFSpriteOffset(uint32_t id);