MODULE: Timer v3.01 - Now OO! Updated 09 August 2013

Started by monkey0506, Tue 07/11/2006 18:22:54

Previous topic - Next topic

Dualnames

Worked on Strangeland, Primordia, Hob's Barrow, The Cat Lady, Mage's Initiation, Until I Have You, Downfall, Hunie Pop, and every game in the Wadjet Eye Games catalogue (porting)

Alarconte

"Tiny pixelated boobies are the heart and soul of Castlevania"

Galactic Battlefare Capital Choice Part 1 , finished, releasing soon
GBF CC Part 2, WIP

monkey0506

#22
Six year bump baby!

Update in the first post. Download link

xil

Two and half year bump baby!

I don't suppose anyone has a working link for 3.01 by any chance?
Calico Reverie - Independent Game Development, Pixel Art & Other Stuff
Games: Mi - Starlit Grave - IAMJASON - Aractaur - blind to siberia - Wrong Channel - Memoriae - Point Of No Return

BrainMayhem

I would like a link for the v3.01 as well, please :)

Crimson Wizard

Oh, what a coincidence, I was recently thinking that I would use such module in the game I am working on too :).

Crimson Wizard

#26
Meanwhile I decided to write my own timer implementation. I can release it as an alternative module later, if people find it usable.

Since AGS 3.4.0 supports custom managed structs, I made timer a pointer type. That adds bit complexity, but hopefully not too much to not use it (you are using pointers to Characters and others too, so...). And this will let you pass Timer objects around your script too, assigning variable to variable, as function parameter or return value.

Here is the code to the header and script body:

EDIT: updated the code

Timer.ash
Code: ags


// Maximal number of timers, change at will (not related to built-in AGS limit).
#define MAX_TIMERS 20

managed struct Timer
{
  /// Start the timer, giving timeout in game ticks.
  import static Timer *Start(int timeout, RepeatStyle repeat = eOnce);
  /// Start the timer, giving timeout in real time (seconds).
  /// Remember that timer can be only as precise as your GameSpeed (40 checks per
  /// second, or 0.025s by default).
  import static Timer *StartRT(float timeout_s, RepeatStyle repeat = eOnce);
  /// Stop the timer
  import void Stop();
  
  /// Tells whether timer is currently active (counting down).
  import readonly attribute bool  IsActive;
  /// Signal property telling that the timer has expired. This flag will remain set
  /// for one game tick only and self-reset afterwards.
  import readonly attribute bool  EvtExpired;
  
  /// Tells whether timer has just expired. Safe to pass null-pointer.
  import static bool   HasExpired(Timer *t);
  /// Stops the running timer. Safe to pass null-pointer.
  import static void   StopIt(Timer *t);
  
  protected int   _id; // internal ID of the timer
  protected bool  _realtime; // is timeout in seconds (otherwise in game ticks)
  protected float _timeout; // timeout (ticks or ms)
  protected bool  _repeat; // should auto-repeat or not
  protected float _remains; // time remaining (ticks or seconds)
  protected bool  _evt; // expired event flag
};


Timer.asc
Code: ags

// Check against this value instead of 0.0 to reduce potential floating-point mistakes
#define TINY_FLOAT 0.00001

// Internal timer references, need them to actually count down
Timer *Timers[MAX_TIMERS];
// Number of seconds in a game tick (updated)
float GameTickTime;


// Find free timer slot, returns internal ID, or -1 if timers limit reached
int FindFreeSlot()
{
  int i;
  for (i = 0; i < MAX_TIMERS; i++)
  {
    if (Timers[i] == null)
      return i;
  }
  return -1;
}
// Remove timer reference
void RemoveRef(this Timer*)
{
  if (this._id >= 0)
  {
    Timers[this._id] = null;
    this._id = -1;
  }
}
// Stop the timer
void Timer::Stop()
{
  this.RemoveRef();
  this._evt = false;
}
// Init timer parameters
void Init(this Timer*, int id, bool realtime, float timeout, RepeatStyle repeat)
{
  this._id = id;
  this._realtime = realtime;
  this._timeout = timeout;
  this._repeat = repeat;
  this._remains = timeout;
  this._evt = false;
}
// Start the timer with the given parameters
Timer *StartTimer(bool realtime, float timeout, RepeatStyle repeat)
{
  int id = FindFreeSlot();
  if (id == -1)
  {
    Display("Timer.asc: timers limit reached, cannot start another timer before any of the active ones has stopped.");
    return null;
  }
  Timer *timer = new Timer;
  timer.Init(id, realtime, timeout, repeat);
  Timers[id] = timer;
  return timer;
}
// Start the timer, giving timeout in game ticks.
static Timer *Timer::Start(int timeout, RepeatStyle repeat)
{
  return StartTimer(false, IntToFloat(timeout), repeat);
}
// Start the timer, giving timeout in real time (seconds).
// Remember that timer can be only as precise as your GameSpeed (40 checks per
// second, or 0.025s by default).
static Timer *Timer::StartRT(float timeout_s, RepeatStyle repeat)
{
  return StartTimer(true, timeout_s, repeat);
}
// Tells whether timer is currently active (counting down).
bool get_IsActive(this Timer*)
{
  return this._id >= 0;
}
// Check to know if timer has expired. This property will only return TRUE once,
// and will reset afterwards until timer restarts (automatically or manually).
bool get_EvtExpired(this Timer*)
{
  return this._evt;
}
// Tells whether timer has just expired. Safe to pass null-pointer.
static bool Timer::HasExpired(Timer *t)
{
  return t != null && t.get_EvtExpired();
}
// Stops the running timer. Safe to pass null-pointer.
static void Timer::StopIt(Timer *t)
{
  if (t != null)
    t.Stop();
}
// Countdown once and check if timeout was reached
bool Countdown(this Timer*)
{
  // Otherwise, counting down
  if (this._realtime)
    this._remains -= GameTickTime;
  else
    this._remains -= 1.0;
  // If timer just ran out, set event flag
  if (this._remains <= TINY_FLOAT)
  {
    this._evt = true;
    if (this._repeat)
      this._remains = this._timeout;
    return this._repeat; // keep the timer only if it is repeating one
  }
  this._evt = false;
  return true;
}

// Repeat each game tick, even if paused or during blocking action
function repeatedly_execute_always()
{
  // We have to update value of GameTickTime each time, unfortunately, in case game speed changed
  GameTickTime = 1.0 / IntToFloat(GetGameSpeed());

  int i;
  for (i = 0; i < MAX_TIMERS; i++)
  {
    Timer *timer = Timers[i];
    if (timer != null)
    {
      if (!timer.Countdown())
      {
        // If timer has stopped, remove its reference from the array
        // (but keep event flag to let other scripts check it)
        timer.RemoveRef();
      }
    }
  }
}


Use example:
Code: ags

Timer *tSay;
function room_AfterFadeIn()
{
  tSay = Timer.StartRT(3.0, eRepeat);
}
function room_RepExec()
{
  if (Timer.HasExpired(tSay))
  {
    cBman.SayBackground("hello!");
  }
}

eri0o

Hey, just wanted to shout that I REALLY LIKE this module and can't live without it now. Thanks for making it! (nod)

Dave Gilbert

#28
How. Did I. Not see this. Before now.

This would have saved me so much aggravation. Downloading and experimenting with it now!

eri0o

My main use for Crimson Wizard module is doing something after a tween is finished when that tween must be nonblocking. Example: Turn the visible property of a GUI false once I have tweened it's Transparency to 100.

Crimson Wizard

Quote from: eri0o on Wed 13/09/2017 22:33:50
My main use for Crimson Wizard module is doing something after a tween is finished when that tween must be nonblocking. Example: Turn the visible property of a GUI false once I have tweened it's Transparency to 100.
I never used Tween module, but is not there a way to just check if tween has ended? If not maybe Edmundito could add a kind of end signal to the tween, similar to ones I use in my modules, so that you could just check it in repeatedly execute, without need of extra module.

Monsieur OUXX

#31
Quote from: Crimson Wizard on Wed 13/09/2017 22:48:11
I never used Tween module, but is not there a way to just check if tween has ended?

I was using the old Tweens (1.5 I think?) and I remember it was a pain to check if a tween was finished -- not sayin' that feature was not available, but if it was, it wasn't obvious to find, 'cause I never found it.
I've just started thinking of migrating to the latest Tweens and didn't check if it was there, thinking "of course it will be there, how could it not? the module is mature and ending a tween is as important as starting it". You're scaring me now.
 

SMF spam blocked by CleanTalk