As the title suggests - I'm looking for a recommended workaround that would allow me to choose the starting frame for a Button animation. The start frame option was added to Object and Character a few years ago, but Button fell by the wayside.
Because there is no function overloading allowed, what I'm asking for in the topic isn't possible in AGS. It seems that rewriting the entire Animate function, and replicating (in a new module) what Button.Animate does may be necessary. Can someone recommend a workaround that falls short of rewriting the entire Button.Animate implementation?
The "obvious" workaround to me seems to be to manually animate the button.
Set the start frame as default, then change the button sprite to the next one with a while/for condition on a timer.
Just for kicks...
animate_btn.ashSpoiler
// new module header
import void AnimateEx(this Button*, int view, int loop = 0, int delay = 1, RepeatStyle style = eOnce, int frame = 0);
import void Stop(this Button*);
animate_btn.ascSpoiler
// new module script
#define MAX_ANIM_BTNS 32
#define ITERATE_BTNS for(int i=0; i<_btn_count; i++)
struct animated_ex_btn {
int Frame;
int Loop;
int View;
int Delay;
int Ticks;
RepeatStyle RepeatStyle;
Button* Button;
};
int _btn_count;
animated_ex_btn _btn[MAX_ANIM_BTNS];
void _remove_btn(Button* btn)
{
int k = MAX_ANIM_BTNS + 1;
ITERATE_BTNS
{
if(_btn[i].Button == btn)
{
k = i;
}
if(i > k)
{
_btn[i-1].Frame = _btn[i].Frame;
_btn[i-1].Loop = _btn[i].Loop;
_btn[i-1].View = _btn[i].View;
_btn[i-1].Delay = _btn[i].Delay;
_btn[i-1].Ticks = _btn[i].Ticks;
_btn[i-1].RepeatStyle = _btn[i].RepeatStyle;
_btn[i-1].Button = _btn[i].Button;
}
}
if(k <= MAX_ANIM_BTNS) {
_btn_count--;
}
}
void _add_btn(Button* btn, int view, int loop, int delay, RepeatStyle style, int frame)
{
_btn[_btn_count].Frame = frame;
_btn[_btn_count].Loop = loop;
_btn[_btn_count].View = view;
_btn[_btn_count].Delay = delay;
_btn[_btn_count].RepeatStyle = style;
_btn[_btn_count].Button = btn;
_btn[_btn_count].Ticks = 0;
_btn_count++;
}
void repeatedly_execute_always()
{
ITERATE_BTNS
{
_btn[i].Ticks++;
if(_btn[i].Ticks < _btn[i].Delay) continue;
int loop = _btn[i].Loop;
int view = _btn[i].View;
int loop_count = Game.GetLoopCountForView(view);
if(loop >= loop_count) continue;
int frame_count = Game.GetFrameCountForLoop(view, loop);
_btn[i].Ticks = 0;
_btn[i].Frame++;
if(_btn[i].Frame >= frame_count) _btn[i].Frame = 0;
int frame = _btn[i].Frame;
ViewFrame* vf = Game.GetViewFrame(view, loop, frame);
int sprite = vf.Graphic;
_btn[i].Button.NormalGraphic = sprite;
}
}
// public interface
void AnimateEx(this Button*, int view, int loop, int delay, RepeatStyle style, int frame) {
_add_btn(this, view, loop, delay, style, frame);
}
void Stop(this Button*) {
_remove_btn(this);
}
Not sure how well it works, but hey! It was a shot!
Haha wow @eri0o! Looks like you wrote almost the exact same implementation as me, and you solved a few of my problems with Views along the way! I'll give this a shot and provide an update in a bit.
Hey, the code has no safeguards, like you can make it wrong if you animate something twice without stopping before, there's no easy way to read the current frame and things like that.
If you think this is somewhat useful I may properly put this in a module and try to add the safeguards. I was kinda trying to see how small I could code this and it wasn't as small as I thought but still reasonably compact. :P
There's a reasonable chance this will be featured in a beta release in a near future.
Excellent! I'm still in the process of rewriting it for my code styling. Will keep you posted.
For information, 3.6.0 Beta now includes extended Button.Animate that has "starting frame" among the new parameters.