From b8df11ca687b8ea77f44eb27f3f4d08e3bf3a41b Mon Sep 17 00:00:00 2001 From: Peter Nelson Date: Sat, 6 Apr 2024 19:38:28 +0100 Subject: [PATCH] Change: Display more useful information in sprite aligner than sprite ID. Sprite IDs are not useful information given they change don't refer to anything outside the loaded game. Instead, include the filename and nfo line at minimum, and include action A or action 5 sprite replacement information if applicable. --- src/CMakeLists.txt | 1 + src/lang/english.txt | 6 +++- src/newgrf.cpp | 33 ++++++++---------- src/newgrf_act5.h | 31 +++++++++++++++++ src/newgrf_debug_gui.cpp | 75 +++++++++++++++++++++++++++++++++------- src/spritecache.cpp | 9 +++++ src/spritecache.h | 1 + 7 files changed, 123 insertions(+), 33 deletions(-) create mode 100644 src/newgrf_act5.h 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);