Skip to content

Instantly share code, notes, and snippets.

@jdoleary
Created September 29, 2017 03:03
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save jdoleary/b6bb8236dd3ee384bccdb337b18a5cab to your computer and use it in GitHub Desktop.
Save jdoleary/b6bb8236dd3ee384bccdb337b18a5cab to your computer and use it in GitHub Desktop.
import _ from 'lodash';
import uid from 'uuid/v4';
export default function(){
return {
// Current time in millis
time:0,
// one 'day' occurs every x milliseconds:
millisPerDay: 60000,
events:[],
//recurringEvents:{},
now(){
return this.time;
},
// set the time
setTime(millis){
if(!_.isNumber(millis)){
console.log(`Error: Attempting to set time equal to ${millis} which is not a number.`);
}
this.time = millis;
// Manage recurring events during the time jump:
// Remove all recurring events:
let recurringEvents = _.remove(this.events, (o) => o.recur);
// Add in sigular events for each recurring event in the past before the fast forward:
recurringEvents.forEach((o) => {
if(o.recurInterval > 0){
while(o.time <= this.time){
// Add a singular, non-recurring event with the recurring event's callback
this.addEventAtAbsolute(o.callback,o.time,o.owner);
// Increment time to check the next occurence:
o.time += o.recurInterval;
}
// Push the recurring event back in where it belongs in the future:
this.events.push(o);
}
});
// Fire events that happened during the time jump:
this.fireEvents();
},
// fast forward time
fastForward(millis){
this.setTime(this.time + millis);
},
fastForwardOneDay(){
this.fastForward(this.millisPerDay);
},
removeEvent(id){
// If a loop is active, remove it at the end of the loop
if(this.loopActive){
let e = _.find(this.events, (o)=>o.id === id);
e.flagForRemoval = true;
}else{
// if not, remove immediately:
_.remove(this.events, (o)=>o.id === id);
}
},
removeEventsFromOwner(owner){
let events = _.filter(this.events,(o)=>o.owner && o.owner.id == owner.id);
events.forEach((e)=>{
//console.log('event removed from ' + e.owner.name + ' ' + e.id);
this.removeEvent(e.id);
});
},
// Adds an event to be fired at a time "recurInterval" milliseconds from the current time
// and to repeat every "recurInterval" milliseconds.
addEventRecurring(callback,recurInterval,owner){
if(!_.isNumber(recurInterval)){
console.log(`Error: Attempting to set event time to ${recurInterval}, which is not a number.`);
}
let id = uid();
let e = {
time:this.now()+recurInterval, // time that event will be fired
callback:callback, // callback fired when time is passed
recur:true, // bool, if event should repeat
recurInterval:recurInterval, // interval of millis that event should repeat
id:id, // used for removing event
owner
};
this.events.push(e);
return id;
},
// Adds an event to be fired at a time "absoluteTime"
addEventAtAbsolute(callback,absoluteTime,owner){
if(!_.isNumber(absoluteTime)){
console.log(`Error: Attempting to set event time to ${absoluteTime}, which is not a number.`);
}
let e = {
id:uid(),
time:absoluteTime, // time that event will be fired
callback:callback, // callback fired when time is passed
owner
};
this.events.push(e);
},
// Adds an event to be fired at a time "MillisFromNow" milliseconds from the current time
addEvent(callback,MillisFromNow,owner){
if(!_.isNumber(MillisFromNow)){
console.log(`Error: Attempting to set event time with ${MillisFromNow}, which is not a number.`);
}
let e = {
id:uid(),
time:this.now()+MillisFromNow, // time that event will be fired
callback:callback, // callback fired when time is passed
owner
};
this.events.push(e);
},
fireEvents(){
// Sort events by time for efficient firing
// (Ordered by descending because list is iterated upside down)
this.events = _.orderBy(this.events,'time',['desc']);
let i = this.events.length;
// Prevents event removal from occuring inside of loop:
this.loopActive = true;
while (i--) {
let e = this.events[i];
if(e.flagForRemoval){
// Do not call events that are flaggedForRemoval:
continue;
}
if(this.now() >= e.time){
// Fire event callback and remove event:
if(e.callback){
// If the event doesn't have an owner, or it does but the
// owner is not destroyed, then call the callback, otherwise
// ignore the callback.
if(!e.owner || (e.owner && !e.owner.destroyed)){
e.callback();
}
}
e.flagForRemoval = true;
}else{
// All subsequent events are in the future so break
// we know this because the events are sorted by time
break;
}
}
i = this.events.length;
while(i--){
if(this.events[i].flagForRemoval){
this.events.splice(i, 1);
}
}
this.loopActive = false;
},
};
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment