(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.

This commit is contained in:
rubidium 2010-04-25 16:27:30 +00:00
parent 4868fb988f
commit c72e2dde60
7 changed files with 264 additions and 1 deletions

View File

@ -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}

View File

@ -57,4 +57,9 @@ GrfSpecFeature GetGrfSpecFeature(TileIndex tile);
*/
GrfSpecFeature GetGrfSpecFeature(VehicleType type);
/**
* Show the window for aligning sprites.
*/
void ShowSpriteAlignerWindow();
#endif /* NEWGRF_DEBUG_H */

View File

@ -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);
}

View File

@ -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)

View File

@ -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);

View File

@ -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;
}
}

View File

@ -107,6 +107,7 @@ enum WindowClass {
WC_AI_LIST,
WC_AI_SETTINGS,
WC_NEWGRF_INSPECT,
WC_SPRITE_ALIGNER,
WC_INVALID = 0xFFFF
};