Change: Display more useful information in sprite aligner than sprite ID. (#12439)

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.
master
Peter Nelson 3 months ago committed by GitHub
parent fbdf26800b
commit 34ba969c74
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

@ -254,6 +254,7 @@ add_files(
music_gui.cpp music_gui.cpp
newgrf.cpp newgrf.cpp
newgrf.h newgrf.h
newgrf_act5.h
newgrf_airport.cpp newgrf_airport.cpp
newgrf_airport.h newgrf_airport.h
newgrf_airporttiles.cpp newgrf_airporttiles.cpp

@ -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) STR_NEWGRF_INSPECT_QUERY_CAPTION :{WHITE}NewGRF variable 60+x parameter (hexadecimal)
# Sprite aligner window # 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_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_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 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_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_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_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 ###length 2
STR_SPRITE_ALIGNER_CENTRE_OFFSET :{BLACK}Offset centred STR_SPRITE_ALIGNER_CENTRE_OFFSET :{BLACK}Offset centred
@ -5833,6 +5836,7 @@ STR_JUST_DATE_ISO :{DATE_ISO}
STR_JUST_STRING :{STRING} STR_JUST_STRING :{STRING}
STR_JUST_STRING1 :{STRING1} STR_JUST_STRING1 :{STRING1}
STR_JUST_STRING2 :{STRING2} STR_JUST_STRING2 :{STRING2}
STR_JUST_STRING4 :{STRING4}
STR_JUST_STRING_STRING :{STRING}{STRING} STR_JUST_STRING_STRING :{STRING}{STRING}
STR_JUST_RAW_STRING :{RAW_STRING} STR_JUST_RAW_STRING :{RAW_STRING}
STR_JUST_BIG_RAW_STRING :{BIG_FONT}{RAW_STRING} STR_JUST_BIG_RAW_STRING :{BIG_FONT}{RAW_STRING}

@ -27,6 +27,7 @@
#include "newgrf_station.h" #include "newgrf_station.h"
#include "industrytype.h" #include "industrytype.h"
#include "industry_map.h" #include "industry_map.h"
#include "newgrf_act5.h"
#include "newgrf_canal.h" #include "newgrf_canal.h"
#include "newgrf_townname.h" #include "newgrf_townname.h"
#include "newgrf_industries.h" #include "newgrf_industries.h"
@ -6369,7 +6370,7 @@ static void FeatureNewName(ByteReader *buf)
* @param name Used for error warnings. * @param name Used for error warnings.
* @return The number of sprites that is going to be skipped. * @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) { 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. */ /** The information about action 5 types. */
static const Action5Type _action5_types[] = { static constexpr auto _action5_types = std::to_array<Action5Type>({
/* Note: min_sprites should not be changed. Therefore these constants are directly here and not in sprites.h */ /* 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" }, /* 0x00 */ { A5BLOCK_INVALID, 0, 0, 0, "Type 0x00" },
/* 0x01 */ { A5BLOCK_INVALID, 0, 0, 0, "Type 0x01" }, /* 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" }, /* 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" }, /* 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" }, /* 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<const Action5Type> GetAction5Types()
{
return _action5_types;
}
/* Action 0x05 */ /* Action 0x05 */
static void GraphicsNew(ByteReader *buf) static void GraphicsNew(ByteReader *buf)
@ -6468,7 +6463,7 @@ static void GraphicsNew(ByteReader *buf)
} }
/* Supported type? */ /* 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); GrfMsg(2, "GraphicsNew: Custom graphics (type 0x{:02X}) sprite block of length {} (unimplemented, ignoring)", type, num);
_cur.skip_sprites = num; _cur.skip_sprites = num;
return; return;

@ -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 <http://www.gnu.org/licenses/>.
*/
/** @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<const Action5Type> GetAction5Types();
#endif /* NEWGRF_ACT5_H */

@ -9,6 +9,7 @@
#include "stdafx.h" #include "stdafx.h"
#include "core/backup_type.hpp" #include "core/backup_type.hpp"
#include "core/geometry_func.hpp"
#include "window_gui.h" #include "window_gui.h"
#include "window_func.h" #include "window_func.h"
#include "random_access_file_type.h" #include "random_access_file_type.h"
@ -28,6 +29,7 @@
#include "train.h" #include "train.h"
#include "roadveh.h" #include "roadveh.h"
#include "newgrf_act5.h"
#include "newgrf_airport.h" #include "newgrf_airport.h"
#include "newgrf_airporttiles.h" #include "newgrf_airporttiles.h"
#include "newgrf_debug.h" #include "newgrf_debug.h"
@ -808,7 +810,6 @@ GrfSpecFeature GetGrfSpecFeature(VehicleType type)
} }
/**** Sprite Aligner ****/ /**** Sprite Aligner ****/
/** Window used for aligning sprites. */ /** Window used for aligning sprites. */
@ -822,6 +823,7 @@ struct SpriteAlignerWindow : Window {
static inline ZoomLevel zoom = ZOOM_LVL_END; static inline ZoomLevel zoom = ZOOM_LVL_END;
static bool centre; static bool centre;
static bool crosshair; static bool crosshair;
const Action5Type *act5_type = nullptr; ///< Sprite Area of current selected sprite.
SpriteAlignerWindow(WindowDesc *desc, WindowNumber wno) : Window(desc) SpriteAlignerWindow(WindowDesc *desc, WindowNumber wno) : Window(desc)
{ {
@ -829,6 +831,10 @@ struct SpriteAlignerWindow : Window {
if (SpriteAlignerWindow::zoom == ZOOM_LVL_END) SpriteAlignerWindow::zoom = _gui_zoom; 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); 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->CreateNestedTree();
this->vscroll = this->GetScrollbar(WID_SA_SCROLLBAR); this->vscroll = this->GetScrollbar(WID_SA_SCROLLBAR);
this->vscroll->SetCount(_newgrf_debug_sprite_picker.sprites.size()); 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_CENTRE, SpriteAlignerWindow::centre);
this->SetWidgetLoweredState(WID_SA_CROSSHAIR, SpriteAlignerWindow::crosshair); 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); this->InvalidateData(0, true);
} }
@ -848,8 +851,22 @@ struct SpriteAlignerWindow : Window {
const Sprite *spr = GetSprite(this->current_sprite, SpriteType::Normal); const Sprite *spr = GetSprite(this->current_sprite, SpriteType::Normal);
switch (widget) { switch (widget) {
case WID_SA_CAPTION: case WID_SA_CAPTION:
SetDParam(0, this->current_sprite); if (this->act5_type != nullptr) {
SetDParamStr(1, GetOriginFile(this->current_sprite)->GetSimplifiedFilename()); 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; break;
case WID_SA_OFFSETS_ABS: case WID_SA_OFFSETS_ABS:
@ -883,13 +900,21 @@ struct SpriteAlignerWindow : Window {
case WID_SA_SPRITE: case WID_SA_SPRITE:
size->height = ScaleGUITrad(200); size->height = ScaleGUITrad(200);
break; break;
case WID_SA_LIST:
SetDParamMaxDigits(0, 6); case WID_SA_LIST: {
size->width = GetStringBoundingBox(STR_JUST_COMMA).width + padding.width; 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->height = GetCharacterHeight(FS_NORMAL) + padding.height;
resize->width = 1; resize->width = 1;
fill->height = resize->height; fill->height = resize->height;
break; break;
}
default: default:
break; break;
} }
@ -941,8 +966,15 @@ struct SpriteAlignerWindow : Window {
Rect ir = r.Shrink(WidgetDimensions::scaled.matrix); Rect ir = r.Shrink(WidgetDimensions::scaled.matrix);
auto [first, last] = this->vscroll->GetVisibleRangeIterators(list); auto [first, last] = this->vscroll->GetVisibleRangeIterators(list);
for (auto it = first; it != last; ++it) { for (auto it = first; it != last; ++it) {
SetDParam(0, *it); const SpriteFile *file = GetOriginFile(*it);
DrawString(ir, STR_JUST_COMMA, *it == this->current_sprite ? TC_WHITE : TC_BLACK, SA_RIGHT | SA_FORCE); 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; ir.top += step_size;
} }
break; break;
@ -957,6 +989,7 @@ struct SpriteAlignerWindow : Window {
do { do {
this->current_sprite = (this->current_sprite == 0 ? GetMaxSpriteID() : this->current_sprite) - 1; this->current_sprite = (this->current_sprite == 0 ? GetMaxSpriteID() : this->current_sprite) - 1;
} while (GetSpriteType(this->current_sprite) != SpriteType::Normal); } while (GetSpriteType(this->current_sprite) != SpriteType::Normal);
this->SelectAction5Type();
this->SetDirty(); this->SetDirty();
break; break;
@ -968,6 +1001,7 @@ struct SpriteAlignerWindow : Window {
do { do {
this->current_sprite = (this->current_sprite + 1) % GetMaxSpriteID(); this->current_sprite = (this->current_sprite + 1) % GetMaxSpriteID();
} while (GetSpriteType(this->current_sprite) != SpriteType::Normal); } while (GetSpriteType(this->current_sprite) != SpriteType::Normal);
this->SelectAction5Type();
this->SetDirty(); this->SetDirty();
break; break;
@ -983,6 +1017,7 @@ struct SpriteAlignerWindow : Window {
SpriteID spr = *it; SpriteID spr = *it;
if (GetSpriteType(spr) == SpriteType::Normal) this->current_sprite = spr; if (GetSpriteType(spr) == SpriteType::Normal) this->current_sprite = spr;
} }
this->SelectAction5Type();
this->SetDirty(); this->SetDirty();
break; break;
} }
@ -1060,6 +1095,7 @@ struct SpriteAlignerWindow : Window {
while (GetSpriteType(this->current_sprite) != SpriteType::Normal) { while (GetSpriteType(this->current_sprite) != SpriteType::Normal) {
this->current_sprite = (this->current_sprite + 1) % GetMaxSpriteID(); this->current_sprite = (this->current_sprite + 1) % GetMaxSpriteID();
} }
this->SelectAction5Type();
this->SetDirty(); this->SetDirty();
} }
@ -1088,6 +1124,19 @@ struct SpriteAlignerWindow : Window {
{ {
this->vscroll->SetCapacityFromWidget(this, WID_SA_LIST); 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; bool SpriteAlignerWindow::centre = true;
@ -1096,7 +1145,7 @@ bool SpriteAlignerWindow::crosshair = true;
static constexpr NWidgetPart _nested_sprite_aligner_widgets[] = { static constexpr NWidgetPart _nested_sprite_aligner_widgets[] = {
NWidget(NWID_HORIZONTAL), NWidget(NWID_HORIZONTAL),
NWidget(WWT_CLOSEBOX, COLOUR_GREY), 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_SHADEBOX, COLOUR_GREY),
NWidget(WWT_STICKYBOX, COLOUR_GREY), NWidget(WWT_STICKYBOX, COLOUR_GREY),
EndContainer(), EndContainer(),

@ -74,6 +74,15 @@ static SpriteFile *GetCachedSpriteFileByName(const std::string &filename)
return nullptr; return nullptr;
} }
/**
* Get the list of cached SpriteFiles.
* @return Read-only list of cache SpriteFiles.
*/
std::span<const std::unique_ptr<SpriteFile>> GetCachedSpriteFiles()
{
return _sprite_files;
}
/** /**
* Open/get the SpriteFile that is cached for use in the sprite cache. * Open/get the SpriteFile that is cached for use in the sprite cache.
* @param filename Name of the file at the disk. * @param filename Name of the file at the disk.

@ -62,6 +62,7 @@ void GfxClearFontSpriteCache();
void IncreaseSpriteLRU(); void IncreaseSpriteLRU();
SpriteFile &OpenCachedSpriteFile(const std::string &filename, Subdirectory subdir, bool palette_remap); SpriteFile &OpenCachedSpriteFile(const std::string &filename, Subdirectory subdir, bool palette_remap);
std::span<const std::unique_ptr<SpriteFile>> GetCachedSpriteFiles();
void ReadGRFSpriteOffsets(SpriteFile &file); void ReadGRFSpriteOffsets(SpriteFile &file);
size_t GetGRFSpriteOffset(uint32_t id); size_t GetGRFSpriteOffset(uint32_t id);

Loading…
Cancel
Save