(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

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