Looping animation for Wait pointer

Started by Chicky, Tue 14/01/2014 18:29:59

Previous topic - Next topic

Chicky

I hope this isn't a dumb question and i'm missing something obvious, i couldn't find any mention of it on the forum.

Is there a way to have the wait cursor convincingly loop? In respect to how the animation is handled by AGS, rather than art related issues. My problem is when the script is blocked the cursor shows and animates, but if a line of code is run and then it's blocked again the cursor animation will replay from frame 0. Changing views (or variables in general) between blocking commands kills the looping impression.

Any help? I'm sure someone has ran into this problem before :)

monkey0506

I assume that you have some manually blocking code in a loop with a call to Wait(1), so that the normally animated cursors aren't working? That seems to be what you're describing, so that's what I'll go with.

You could update it yourself from within your loop. At the top of your GlobalScript.asc:

Code: ags
int waitCursorFrame = 0;
int waitCursorDelay = -1;

void UpdateWaitCursor(this Mouse*)
{
  ViewFrame *frame = Game.GetViewFrame(WAIT_CURSOR_VIEW, 1, waitCursorFrame); // snag the current frame
  if (frame == null) return; // safety net, shouldn't happen anyway
  if (waitCursorDelay == -1) waitCursorDelay = frame.Speed; // initialize our counter
  else // update our counter
  {
    waitCursorDelay--;
    if (waitCursorDelay == 0) // go to next frame
    {
      waitCursorFrame++;
      if (waitCursorFrame >= Game.GetFrameCountForLoop(WAIT_CURSOR_VIEW, 1)) waitCursorFrame = 0; // reached last frame, go back to first
      frame = Game.GetViewFrame(WAIT_CURSOR_VIEW, 1, waitCursorFrame);
      if (frame == null) return; // safety net
      waitCursorDelay = frame.Speed; // update counter to new frame
    }
  }
  mouse.ChangeModeView(eModeWait, -1); // make sure AGS doesn't try to animate the cursor
  mouse.ChangeModeGraphic(eModeWait, frame.Graphic); // update cursor sprite
}

void ResetWaitCursor(this Mouse*)
{
  waitCursorFrame = 0;
  waitCursorDelay = -1;
  ViewFrame *frame = Game.GetViewFrame(WAIT_CURSOR_VIEW, 1, 0);
  if (frame != null) mouse.ChangeModeGraphic(eModeWait, frame.Graphic);
  mouse.ChangeModeView(WAIT_CURSOR_VIEW);
}


Then, inside your loop:

Code: ags
while (blah)
{
  // ..stuff..
  mouse.UpdateWaitCursor();
  Wait(1);
}
mouse.ResetWaitCursor(); // make sure the cursor graphic/view is reset after the loop

Khris

You could count frames in repeatedly_execute and show a sprite depending on that value.

But, I actually tried that just now, and the mouse cursor does not change its graphic until after the blocking event has finished.
The only solution I can see is to create one view for each frame of the animation, basically 1234, 2341, 3412 and 4123.
Then count game loops in rep_ex_always and every loop, set the wait cursor view to the correct one, so that as soon as the game enters a blocking state, the correct animation is already assigned to the cursor. Off to test this now.

Edit:
@monkey:
No, that's not it.
Assuming you have:
Code: ags
  player.Walk(280, 200, eBlock, eAnywhere);
  player.Walk(0, 200, eBlock, eAnywhere);

The wait cursor animation will actually restart from the beginning after the first walk has finished. There don't even have to be other commands in between. This could be considered a bug, I guess.

monkey0506

Ah. What Khris says makes sense now. And in fact, they're actually related.

You're probably right, if the game is continuously blocked from one game loop to the next, the wait cursor should continuously animate, not start over just because a particular blocking function has finished.

Khris

Found a solution:

-remove all frames from the view but one
-make sure the frames are numbered in order in the sprite manager
-Put this above rep_ex_always:
Code: ags
int loop_cnt;

function AnimateWaitCursor() {
  int view = WAITCURSOR;
  int delay = 5;
  int frames = 8;
  int first_slot = 103;
  loop_cnt = (loop_cnt + 1) % (delay * frames);
  ViewFrame *vf = Game.GetViewFrame(view, 0, 0);
  vf.Graphic = first_slot + loop_cnt / delay;
}

-Call the function in rep_ex_always

Apparently, changing the mouse view's frame in rep_exe has an immediate effect. It seems to be the only way to change the mouse graphic manually during blocking events. I also found that for small delays, frames are skipped.

Chicky

#5
Thanks for the input guys! I will test your script Khris and report back once i've got my hands on AGS. It's a bit beyond my skills, so you may have to explain those last couple of lines but I'll go check the manual first :)

*edit:
It works perfectly, what a clever workaround! The animation does indeed skip on occasion but this isn't too much of an issue. I think I understand what the code is doing (changing the view frame of the wait cursor to that of the first frame sprite number + loop count, right?) but could you explain how the delay works? Specifically this bit:

loop_cnt = (loop_cnt + 1) % (delay * frames);

I've not used the % or * commands before, although i'm sure will need to again soon. From my understanding (a quick google), % would give you the remainder of two numbers divided and * is used as a multiplier. Not sure how that would work out though :p I must be wrong!

*Edit again: I read up on operators, found the mod button on my calculator now looking into mathematics. Eek.

Khris

The basic idea is (I'm going to use my numbers here): every 5 loops, switch to the next of a total of 8 animation frames.
So the entire animation will take 40 loops (frames * delay, 5 * 8).
The line you quoted increases the loop counter by 1, then resets it to 0 if it reached 40.
It's just short for:
loop_cnt++;
if (loop_cnt == 40) loop_cnt = 0;

Then, by using loop_cnt/delay, I'll get 0 for loops 0 to 4, then 1 for loops 5 to 9, etc. (since integer division cuts off decimals / always rounds down).
Finally, this sprite offset is added to the first slot to get the actual sprite.

Chicky

Cheers Khris, that makes complete sense. I realised the line i quoted was instead of a loop_cnt++; but I was completely thrown off by the decimals returned from the loop_cnt/delay. Good to know they round down.

Thanks again for your help, that makes two big hurdles you've helped me overcome on this project :)

SMF spam blocked by CleanTalk