TimerV

Version:

1.0

Added on:

22 Dec 2019 00:16

Tags:

Description:
Class that lets you schedule a function to be called later on with just a single line. No need to keep track of the timer manually, it will call your function automatically when the time has elapsed. Instructions and examples are included.

TimerV(time, callback);
/*
  Usage: To schedule a function to be called at some point in the future, call
  the TimerV constructor with the desired delay and function as arguments, e.g.:

    void myAlert() {
      jjAlert("A second has passed.");
    }
    void onLevelLoad() {
      TimerV(70, myAlert); //calls myAlert after one second (70 ticks)
    }
  
  Anonymous functions (or delegates) may be used instead:
  
    void onLevelLoad() {
      TimerV(70, function(){ jjAlert("A second has passed."); }); //same effect as above
    }
    
  For a recurring event, have the called function construct another timer for itself:
  
    void morphEveryone() {
      for (int i = 0; i < jjLocalPlayerCount; ++i)
        jjLocalPlayers[i].morph(true);
      TimerV(5 * 70, morphEveryone);
    }
    void onLevelLoad() {
      morphEveryone(); //morphs all local players every five seconds
    }
    
  To pass one or more arguments to the delayed function, use a function that takes a single
  dictionary@ as its sole argument, and pass that dictionary@ to the TimerV constructor:
  
    void onFunction0(uint8 triggerID) {
      if (!jjTriggers[triggerID]) {
        jjTriggers[triggerID] = true;
        TimerV( //turns on, then off, whichever trigger was specified by the offset parameter of the Text event
          2 * 70,
          function(arguments) {
            jjTriggers[uint8(arguments["ID"])] = false;
          },
          dictionary = {{"ID",triggerID}}
        );
      }
    }
    
  Another use of a dictionary argument is to limit the number of times the function is called:
  
    void playSound(dictionary@ arguments) {
      jjSamplePriority(SOUND::COMMON_COIN);
      int count = int(arguments["count"]) - 1;
      if (count > 0) {
        arguments["count"] = count;
        TimerV(40, playSound, arguments);
      }
    }
    void onLevelLoad() {
      playSound(dictionary = {{"count",5}}); //plays the COMMON_COIN sample exactly five times
    }
    
  As mentioned, the TimerV lines are actually constructors, not regular functions. If you have any
  reason to keep a TimerV object around after its construction, e.g. if there's a possibility you
  might need to cancel it, it provides the following API:
  
    const bool Active
      true iff this timer is still running
      
    const int Elapsed
      How many ticks have passed since the construction, or -1 if Active is false
      
    const int Total
      The original time parameter passed to the constructor, as a reminder, or -1 if Active is false
      
    const int Remaining
      (Total - Elapsed), or -1 if Active is false
      
    bool Paused
      While true, this timer will not advance, though Active will remain true
      
    bool Stop()
      Stops the timer early (WITHOUT executing the function) and returns true, or returns false if Active was false
*/


funcdef void TimerVDictionaryFunction(dictionary@);
class TimerV : jjBEHAVIORINTERFACE {
  TimerV(int time, jjVOIDFUNC@ callback) {
    @_callback = @callback;
    _start(time);
  }
  TimerV(int time, TimerVDictionaryFunction@ callback, dictionary@ arguments) {
    @_callbackWithArguments = @callback;
    @_arguments = @arguments;
    _start(time);
  }
  bool get_Active() const {
    return cast<jjBEHAVIORINTERFACE@>(_object.behavior) is this;
  }
  int get_Elapsed() const {
    if (Active) return _object.age;
    return -1;
  }
  int get_Total() const {
    if (Active) return _object.counter;
    return -1;
  }
  int get_Remaining() const {
    if (Active) return _object.counter - _object.age;
    return -1;
  }
  bool Stop() {
    if (Active && _object.age < _object.counter) {
      _object.delete();
      return true;
    }
    return false;
  }
  bool Paused = false;
  
  
  
  
  
  private jjVOIDFUNC@ _callback = null;
  private TimerVDictionaryFunction@ _callbackWithArguments;
  private dictionary@ _arguments;
  private jjOBJ@ _object;
  private int _startTime;
  private void _start(int time) {
    if (time > 0) {
      @_object = jjObjects[jjAddObject(OBJECT::BEES, -1000, -1000, 0, CREATOR::OBJECT, BEHAVIOR::BEES)];
      _object.behavior = this;
      _object.counter = time;
      _object.age = 0;
      _object.playerHandling = HANDLING::PARTICLE;
      _object.deactivates = false;
      _startTime = jjGameTicks;
    } else {
      @_object = jjObjects[0]; //avoid null pointer access
      _pickCallback();
    }
  }
  private void onBehave(jjOBJ@ obj) {
    if (!Paused && jjGameTicks > _startTime && obj is _object && ++_object.age >= _object.counter) {
      _pickCallback();
      _object.delete();
    }
  }
  private void _pickCallback() {
    if (_callback !is null)
      _callback();
    else
      _callbackWithArguments(_arguments);
  }
}