mirror of https://github.com/OpenTTD/OpenTTD
(svn r19723) -Add: a simple sprite alignment helper. It does not store the new offsets anywhere so as soon as the sprite is reloaded the offsets are gone (use a bigger sprite cache if this happens). Also anything that reloads NewGRFs (new games, loading games or (re)applying NewGRFs) clears the sprite cache and as such resets the offsets.
parent
4868fb988f
commit
c72e2dde60
|
@ -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}
|
||||
|
|
|
@ -57,4 +57,9 @@ GrfSpecFeature GetGrfSpecFeature(TileIndex tile);
|
|||
*/
|
||||
GrfSpecFeature GetGrfSpecFeature(VehicleType type);
|
||||
|
||||
/**
|
||||
* Show the window for aligning sprites.
|
||||
*/
|
||||
void ShowSpriteAlignerWindow();
|
||||
|
||||
#endif /* NEWGRF_DEBUG_H */
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -107,6 +107,7 @@ enum WindowClass {
|
|||
WC_AI_LIST,
|
||||
WC_AI_SETTINGS,
|
||||
WC_NEWGRF_INSPECT,
|
||||
WC_SPRITE_ALIGNER,
|
||||
|
||||
WC_INVALID = 0xFFFF
|
||||
};
|
||||
|
|
Loading…
Reference in New Issue