diff options
-rw-r--r-- | src/lang/english.txt | 15 | ||||
-rw-r--r-- | src/newgrf_debug.h | 5 | ||||
-rw-r--r-- | src/newgrf_debug_gui.cpp | 200 | ||||
-rw-r--r-- | src/spritecache.cpp | 35 | ||||
-rw-r--r-- | src/spritecache.h | 5 | ||||
-rw-r--r-- | src/toolbar_gui.cpp | 4 | ||||
-rw-r--r-- | src/window_type.h | 1 |
7 files changed, 264 insertions, 1 deletions
diff --git a/src/lang/english.txt b/src/lang/english.txt index 3f2bec04a..fd175c3ad 100644 --- a/src/lang/english.txt +++ b/src/lang/english.txt @@ -442,6 +442,7 @@ STR_ABOUT_MENU_AI_DEBUG :AI debug STR_ABOUT_MENU_SCREENSHOT :Screenshot (Ctrl+S) STR_ABOUT_MENU_GIANT_SCREENSHOT :Giant screenshot (Ctrl+G) STR_ABOUT_MENU_ABOUT_OPENTTD :About 'OpenTTD' +STR_ABOUT_MENU_SPRITE_ALIGNER :Sprite aligner ############ range ends here ############ range for days starts (also used for the place in the highscore window) @@ -2375,6 +2376,20 @@ STR_NEWGRF_INSPECT_CAPTION_OBJECT_AT_RAIL_TYPE :Rail 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_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 at the end +STR_SPRITE_ALIGNER_GOTO_BUTTON :{BLACK}Go to sprite +STR_SPRITE_ALIGNER_GOTO_TOOLTIP :{BLACK}Go to the given sprite. If the sprite is not a normal sprite, proceed to the next normal sprite +STR_SPRITE_ALIGNER_PREVIOUS_BUTTON :{BLACK}Previous sprite +STR_SPRITE_ALIGNER_PREVIOUS_TOOLTIP :{BLACK}Proceed to the previous normal sprite, skipping any pseudo/recolour/font sprites and wrapping around at the begin +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 +STR_SPRITE_ALIGNER_OFFSETS :{BLACK}X offset: {NUM}, Y offset: {NUM} + +STR_SPRITE_ALIGNER_GOTO_CAPTION :{WHITE}Go to sprite + # NewGRF (self) generated warnings/errors STR_NEWGRF_ERROR_MSG_INFO :{SILVER}{RAW_STRING} STR_NEWGRF_ERROR_MSG_WARNING :{RED}Warning: {SILVER}{RAW_STRING} diff --git a/src/newgrf_debug.h b/src/newgrf_debug.h index 91f504a93..81af478ed 100644 --- a/src/newgrf_debug.h +++ b/src/newgrf_debug.h @@ -57,4 +57,9 @@ GrfSpecFeature GetGrfSpecFeature(TileIndex tile); */ GrfSpecFeature GetGrfSpecFeature(VehicleType type); +/** + * Show the window for aligning sprites. + */ +void ShowSpriteAlignerWindow(); + #endif /* NEWGRF_DEBUG_H */ diff --git a/src/newgrf_debug_gui.cpp b/src/newgrf_debug_gui.cpp index f40a11e54..637c5042d 100644 --- a/src/newgrf_debug_gui.cpp +++ b/src/newgrf_debug_gui.cpp @@ -13,7 +13,9 @@ #include <stdarg.h> #include "window_gui.h" #include "window_func.h" +#include "fileio_func.h" #include "gfx_func.h" +#include "spritecache.h" #include "string_func.h" #include "strings_func.h" #include "textbuf_gui.h" @@ -528,3 +530,201 @@ GrfSpecFeature GetGrfSpecFeature(VehicleType type) default: return GSF_INVALID; } } + + + +/**** Sprite Aligner ****/ + +/** Widgets we want (some) influence over. */ +enum SpriteAlignerWidgets { + SAW_CAPTION, ///< Caption of the window + SAW_PREVIOUS, ///< Skip to the previous sprite + SAW_GOTO, ///< Go to a given sprite + SAW_NEXT, ///< Skip to the next sprite + SAW_UP, ///< Move the sprite up + SAW_LEFT, ///< Move the sprite to the left + SAW_RIGHT, ///< Move the sprite to the right + SAW_DOWN, ///< Move the sprite down + SAW_SPRITE, ///< The actual sprite + SAW_OFFSETS, ///< The sprite offsets +}; + +/** Window used for aligning sprites. */ +struct SpriteAlignerWindow : Window { + SpriteID current_sprite; ///< The currently shown sprite + + SpriteAlignerWindow(const WindowDesc *desc, WindowNumber wno) : Window() + { + this->InitNested(desc, wno); + + /* Oh yes, we assume there is at least one normal sprite! */ + while (GetSpriteType(this->current_sprite) != ST_NORMAL) this->current_sprite++; + } + + virtual void SetStringParameters(int widget) const + { + switch (widget) { + case SAW_CAPTION: + SetDParam(0, this->current_sprite); + SetDParamStr(1, FioGetFilename(GetOriginFileSlot(this->current_sprite))); + break; + + case SAW_OFFSETS: { + const Sprite *spr = GetSprite(this->current_sprite, ST_NORMAL); + SetDParam(0, spr->x_offs); + SetDParam(1, spr->y_offs); + } break; + + default: + break; + } + } + + virtual void DrawWidget(const Rect &r, int widget) const + { + if (widget != SAW_SPRITE) return; + + /* Center the sprite ourselves */ + const Sprite *spr = GetSprite(this->current_sprite, ST_NORMAL); + int width = r.right - r.left + 1; + int height = r.bottom - r.top + 1; + int x = r.left - spr->x_offs + (width - spr->width) / 2; + int y = r.top - spr->y_offs + (height - spr->height) / 2; + + /* And draw only the part within the sprite area */ + SubSprite subspr = { + spr->x_offs + (spr->width - width) / 2 + 1, + spr->y_offs + (spr->height - height) / 2 + 1, + spr->x_offs + (spr->width + width) / 2 - 1, + spr->y_offs + (spr->height + height) / 2 - 1, + }; + + DrawSprite(this->current_sprite, PAL_NONE, x, y, &subspr); + } + + virtual void OnPaint() + { + this->DrawWidgets(); + } + + virtual void OnClick(Point pt, int widget, int click_count) + { + switch (widget) { + case SAW_PREVIOUS: + do { + this->current_sprite = (this->current_sprite == 0 ? GetMaxSpriteID() : this->current_sprite) - 1; + } while (GetSpriteType(this->current_sprite) != ST_NORMAL); + this->SetDirty(); + break; + + case SAW_GOTO: + ShowQueryString(STR_EMPTY, STR_SPRITE_ALIGNER_GOTO_CAPTION, 7, 150, this, CS_NUMERAL, QSF_NONE); + break; + + case SAW_NEXT: + do { + this->current_sprite = (this->current_sprite + 1) % GetMaxSpriteID(); + } while (GetSpriteType(this->current_sprite) != ST_NORMAL); + this->SetDirty(); + break; + + case SAW_UP: + case SAW_DOWN: + case SAW_LEFT: + case SAW_RIGHT: { + /* + * Yes... this is a hack. + * + * No... I don't think it is useful to make this less of a hack. + * + * If you want to align sprites, you just need the number. Generally + * the sprite caches are big enough to not remove the sprite from the + * cache. If that's not the case, just let the NewGRF developer + * increase the cache size instead of storing thousands of offsets + * for the incredibly small chance that it's actually going to be + * used by someone and the sprite cache isn't big enough for that + * particular NewGRF developer. + */ + Sprite *spr = const_cast<Sprite *>(GetSprite(this->current_sprite, ST_NORMAL)); + switch (widget) { + case SAW_UP: spr->y_offs--; break; + case SAW_DOWN: spr->y_offs++; break; + case SAW_LEFT: spr->x_offs--; break; + case SAW_RIGHT: spr->x_offs++; break; + } + /* Ofcourse, we need to redraw the sprite, but where is it used? + * Everywhere is a safe bet. */ + MarkWholeScreenDirty(); + } break; + } + } + + virtual void OnQueryTextFinished(char *str) + { + if (StrEmpty(str)) return; + + this->current_sprite = atoi(str); + if (this->current_sprite >= GetMaxSpriteID()) this->current_sprite = 0; + while (GetSpriteType(this->current_sprite) != ST_NORMAL) { + this->current_sprite = (this->current_sprite + 1) % GetMaxSpriteID(); + } + this->SetDirty(); + } +}; + +static const NWidgetPart _nested_sprite_aligner_widgets[] = { + NWidget(NWID_HORIZONTAL), + NWidget(WWT_CLOSEBOX, COLOUR_GREY), + NWidget(WWT_CAPTION, COLOUR_GREY, SAW_CAPTION), SetDataTip(STR_SPRITE_ALIGNER_CAPTION, STR_TOOLTIP_WINDOW_TITLE_DRAG_THIS), + NWidget(WWT_SHADEBOX, COLOUR_GREY), + NWidget(WWT_STICKYBOX, COLOUR_GREY), + EndContainer(), + NWidget(WWT_PANEL, COLOUR_GREY), + NWidget(NWID_VERTICAL), SetPIP(10, 5, 10), + NWidget(NWID_HORIZONTAL, NC_EQUALSIZE), SetPIP(10, 5, 10), + NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, SAW_PREVIOUS), SetDataTip(STR_SPRITE_ALIGNER_PREVIOUS_BUTTON, STR_SPRITE_ALIGNER_PREVIOUS_TOOLTIP), SetFill(1, 0), + NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, SAW_GOTO), SetDataTip(STR_SPRITE_ALIGNER_GOTO_BUTTON, STR_SPRITE_ALIGNER_GOTO_TOOLTIP), SetFill(1, 0), + NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, SAW_NEXT), SetDataTip(STR_SPRITE_ALIGNER_NEXT_BUTTON, STR_SPRITE_ALIGNER_NEXT_TOOLTIP), SetFill(1, 0), + EndContainer(), + NWidget(NWID_HORIZONTAL), SetPIP(10, 5, 10), + NWidget(NWID_SPACER), SetFill(1, 1), + NWidget(WWT_PUSHIMGBTN, COLOUR_GREY, SAW_UP), SetDataTip(SPR_ARROW_UP, STR_SPRITE_ALIGNER_MOVE_TOOLTIP), SetResize(0, 0), + NWidget(NWID_SPACER), SetFill(1, 1), + EndContainer(), + NWidget(NWID_HORIZONTAL_LTR), SetPIP(10, 5, 10), + NWidget(NWID_VERTICAL), + NWidget(NWID_SPACER), SetFill(1, 1), + NWidget(WWT_PUSHIMGBTN, COLOUR_GREY, SAW_LEFT), SetDataTip(SPR_ARROW_LEFT, STR_SPRITE_ALIGNER_MOVE_TOOLTIP), SetResize(0, 0), + NWidget(NWID_SPACER), SetFill(1, 1), + EndContainer(), + NWidget(WWT_PANEL, COLOUR_DARK_BLUE, SAW_SPRITE), SetDataTip(STR_NULL, STR_SPRITE_ALIGNER_SPRITE_TOOLTIP), SetMinimalSize(200, 200), + EndContainer(), + NWidget(NWID_VERTICAL), + NWidget(NWID_SPACER), SetFill(1, 1), + NWidget(WWT_PUSHIMGBTN, COLOUR_GREY, SAW_RIGHT), SetDataTip(SPR_ARROW_RIGHT, STR_SPRITE_ALIGNER_MOVE_TOOLTIP), SetResize(0, 0), + NWidget(NWID_SPACER), SetFill(1, 1), + EndContainer(), + EndContainer(), + NWidget(NWID_HORIZONTAL), SetPIP(10, 5, 10), + NWidget(NWID_SPACER), SetFill(1, 1), + NWidget(WWT_PUSHIMGBTN, COLOUR_GREY, SAW_DOWN), SetDataTip(SPR_ARROW_DOWN, STR_SPRITE_ALIGNER_MOVE_TOOLTIP), SetResize(0, 0), + NWidget(NWID_SPACER), SetFill(1, 1), + EndContainer(), + NWidget(NWID_HORIZONTAL), SetPIP(10, 5, 10), + NWidget(WWT_LABEL, COLOUR_GREY, SAW_OFFSETS), SetDataTip(STR_SPRITE_ALIGNER_OFFSETS, STR_NULL), SetFill(1, 0), + EndContainer(), + EndContainer(), + EndContainer(), +}; + +static const WindowDesc _sprite_aligner_desc( + WDP_AUTO, 400, 300, + WC_SPRITE_ALIGNER, WC_NONE, + WDF_UNCLICK_BUTTONS, + _nested_sprite_aligner_widgets, lengthof(_nested_sprite_aligner_widgets) +); + +void ShowSpriteAlignerWindow() +{ + AllocateWindowDescFront<SpriteAlignerWindow>(&_sprite_aligner_desc, 0); +} diff --git a/src/spritecache.cpp b/src/spritecache.cpp index 65b640c49..973150427 100644 --- a/src/spritecache.cpp +++ b/src/spritecache.cpp @@ -140,6 +140,41 @@ bool SpriteExists(SpriteID id) return !(GetSpriteCache(id)->file_pos == 0 && GetSpriteCache(id)->file_slot == 0); } +/** + * Get the sprite type of a given sprite. + * @param sprite The sprite to look at. + * @return the type of sprite. + */ +SpriteType GetSpriteType(SpriteID sprite) +{ + if (!SpriteExists(sprite)) return ST_INVALID; + return GetSpriteCache(sprite)->type; +} + +/** + * Get the (FIOS) file slot of a given sprite. + * @param sprite The sprite to look at. + * @return the FIOS file slot + */ +uint GetOriginFileSlot(SpriteID sprite) +{ + if (!SpriteExists(sprite)) return 0; + return GetSpriteCache(sprite)->file_slot; +} + +/** + * Get a reasonable (upper bound) estimate of the maximum + * SpriteID used in OpenTTD; there will be no sprites with + * a higher SpriteID, although there might be up to roughly + * a thousand unused SpriteIDs below this number. + * @note It's actually the number of spritecache items. + * @return maximum SpriteID + */ +uint GetMaxSpriteID() +{ + return _spritecache_items; +} + static void *AllocSprite(size_t); static void *ReadSprite(SpriteCache *sc, SpriteID id, SpriteType sprite_type) diff --git a/src/spritecache.h b/src/spritecache.h index 172702500..7801f5609 100644 --- a/src/spritecache.h +++ b/src/spritecache.h @@ -28,6 +28,11 @@ extern uint _sprite_cache_size; void *GetRawSprite(SpriteID sprite, SpriteType type); bool SpriteExists(SpriteID sprite); +SpriteType GetSpriteType(SpriteID sprite); +uint GetOriginFileSlot(SpriteID sprite); +uint GetMaxSpriteID(); + + static inline const Sprite *GetSprite(SpriteID sprite, SpriteType type) { assert(type != ST_RECOLOUR); diff --git a/src/toolbar_gui.cpp b/src/toolbar_gui.cpp index 700310c2f..a0d4427fa 100644 --- a/src/toolbar_gui.cpp +++ b/src/toolbar_gui.cpp @@ -44,6 +44,7 @@ #include "smallmap_gui.h" #include "graph_gui.h" #include "textbuf_gui.h" +#include "newgrf_debug.h" #include "network/network.h" #include "network/network_gui.h" @@ -759,7 +760,7 @@ static void MenuClickNewspaper(int index) static void ToolbarHelpClick(Window *w) { - PopupMainToolbMenu(w, TBN_HELP, STR_ABOUT_MENU_LAND_BLOCK_INFO, 7); + PopupMainToolbMenu(w, TBN_HELP, STR_ABOUT_MENU_LAND_BLOCK_INFO, _settings_client.gui.newgrf_developer_tools ? 8 : 7); } static void MenuClickSmallScreenshot() @@ -781,6 +782,7 @@ static void MenuClickHelp(int index) case 4: MenuClickSmallScreenshot(); break; case 5: MenuClickWorldScreenshot(); break; case 6: ShowAboutWindow(); break; + case 7: ShowSpriteAlignerWindow(); break; } } diff --git a/src/window_type.h b/src/window_type.h index dd6982909..8cd45c64f 100644 --- a/src/window_type.h +++ b/src/window_type.h @@ -107,6 +107,7 @@ enum WindowClass { WC_AI_LIST, WC_AI_SETTINGS, WC_NEWGRF_INSPECT, + WC_SPRITE_ALIGNER, WC_INVALID = 0xFFFF }; |