Skip to content

Instantly share code, notes, and snippets.

@bripod
Last active July 12, 2016 07:05
Show Gist options
  • Save bripod/6902575 to your computer and use it in GitHub Desktop.
Save bripod/6902575 to your computer and use it in GitHub Desktop.
A persistent time tracker and basic calendar system for roll20.net
/* Chronos Timekeeper: Calendar and Timekeeper Script for roll20.net
*
* !chronos returns elapsed time so far (needs updating)
* !chronos #y,#m,#d,#h,#n adds a number of years, months, days, hours, minutes (n) to clock.
* !chronos #y,#m,#d,#h,#n -xyz parameters: t for torch tracking, r for regular rest, h for bedrest
* !chronos 6:00am move timer to next occurrence of given hour (not implemented yet)
*
*/
state.bpd_chronos = state.bpd_chronos || {};
state.bpd_chronos.years = state.bpd_chronos.years || 0;
state.bpd_chronos.months = state.bpd_chronos.months || 1;
state.bpd_chronos.days = state.bpd_chronos.days || 1;
state.bpd_chronos.hours = state.bpd_chronos.hours || 0;
state.bpd_chronos.minutes = state.bpd_chronos.minutes || 0;
state.bpd_chronos.weekday = state.bpd_chronos.weekday || 0;
var month = [['January',31],['February',28],["March",31],['April',30],['May',31],['June',30],['July',31],['August',31],['September',30],['October',31],['November',30],['December',31]];
var lengthOfYear = lengthOfYear();
var week = ['Sunday','Monday','Tuesday','Wednesday','Thursday','Friday','Saturday'];
var weekday = 0;
var attributesToHeal = ['HP','Strength','Agility','Stamina','Personality','Intelligence'];
/*
healingData objects:
healingData[0][0]: minimum hours of rest for healing
healingData[0][1]: hp for minimum rest
healingData[0][2]: attributes points for minimum rest
healingData[1][0]: hours required for full rest
healingData[1][1]: hp healed for full rest
healingData[1][2]: attribute points for full tended rest
*/
var healingData = [[8,1,1],[1,2,2]];
function lengthOfYear() {
n = 0;
for (i = 0; i <= month.length -1; i++) {
if (month[i][2] == undefined) {
n = parseInt(n) + parseInt(month[i][1]);
};
};
return n;
};
function updateChronos(years,months,days,hours,minutes) {
if (minutes != 0) {
var chronosMinuteMax = 60; // hard-coding this for now
if (state.bpd_chronos.minutes + parseInt(minutes) >= parseInt(chronosMinuteMax)) {
hours = parseInt(hours) + Math.floor((state.bpd_chronos.minutes + parseInt(minutes)) / chronosMinuteMax);
state.bpd_chronos.minutes = Math.floor((state.bpd_chronos.minutes + parseInt(minutes)) % chronosMinuteMax);
} else {
state.bpd_chronos.minutes = parseInt(state.bpd_chronos.minutes) + parseInt(minutes)
};
};
if (hours != 0) {
var chronosHourMax = 24; // hard-coding this for now, under new state setup
if (state.bpd_chronos.hours + parseInt(hours) >= parseInt(chronosHourMax)) {
days = parseInt(days) + Math.floor((state.bpd_chronos.hours + parseInt(hours)) / chronosHourMax);
state.bpd_chronos.hours = Math.floor((state.bpd_chronos.hours + parseInt(hours)) % chronosHourMax);
} else {
state.bpd_chronos.hours = parseInt(state.bpd_chronos.hours) + parseInt(hours)
};
};
if (days != 0) {
// this currently won't work if you add enough days to move the timer forward more than one month
var chronosDayMax = month[state.bpd_chronos.months -1][1];
if (state.bpd_chronos.days + parseInt(days) > parseInt(chronosDayMax) && month[state.bpd_chronos.months-1][1] != 1) {
months = parseInt(months) + Math.floor((state.bpd_chronos.days + parseInt(days)) / chronosDayMax);
state.bpd_chronos.days = Math.floor((state.bpd_chronos.days + parseInt(days)) % chronosDayMax);
} else if (state.bpd_chronos.days + parseInt(days) > parseInt(chronosDayMax) && month[state.bpd_chronos.months-1][1] == 1) {
months = parseInt(months) + 1;
state.bpd_chronos.days = Math.floor((state.bpd_chronos.days + parseInt(days)) - 1);
} else {
state.bpd_chronos.days = parseInt(state.bpd_chronos.days) + parseInt(days);
};
};
if (months != 0) {
var chronosMonthMax = month.length -1;
if (state.bpd_chronos.months + parseInt(months) > parseInt(chronosMonthMax)) {
years = parseInt(years) + Math.floor((state.bpd_chronos.months + parseInt(months)) / chronosMonthMax);
state.bpd_chronos.months = Math.floor((state.bpd_chronos.months + parseInt(months)) % chronosMonthMax);
} else {
state.bpd_chronos.months = parseInt(state.bpd_chronos.months) + parseInt(months)
};
};
if (years != 0) {
state.bpd_chronos.years = parseInt(state.bpd_chronos.years) + parseInt(years);
};
n = 0;
for (i = 0; i < state.bpd_chronos.months -1; i++) {
// get all the days in preceding months
if (month[i][2] == undefined) {
n = parseInt(n) + parseInt(month[i][1]);
};
};
state.bpd_chronos.weekday = (((lengthOfYear * state.bpd_chronos.years) + n + state.bpd_chronos.days) % week.length);
sendChat("Chronos","/w gm " + week[state.bpd_chronos.weekday] + ", " + state.bpd_chronos.years + "." + state.bpd_chronos.months + "." + state.bpd_chronos.days + "." + state.bpd_chronos.hours + "." + state.bpd_chronos.minutes + ".");
};
function healCharacters(hd,lengthOfRest,typeOfHealing) {
// healCharacters function, called when parameter includes "r" for rest, "b" for bedrest
// lengthOfRest = number of hours or days to rest
// hoursOrDays = h or d
// typeOfHealing = normal, bedrest
// put all the tokens on the page that represent characters into charactersToHeal[] array
var charactersToHeal = filterObjs(function(obj) {
if(obj.get("_pageid") == Campaign().get("playerpageid") && obj.get("_subtype") == "token" && obj.get("_represents") != "") return true;
else return false;
});
// get attributes to heal, any healingData related attributes
for (i = 0; i < charactersToHeal.length; i++) {
var character = getObj("character", charactersToHeal[i].get("represents"));
var attributes = filterObjs(function(obj){
if (obj.get("_type") == "attribute" && obj.get("_characterid") == character.get("_id") && attributesToHeal.indexOf(obj.get("name")) != -1) return true;
else return false;
});
for (i = 0; i <= attributes.length -1; i++) {
// now go through all the attributes for this character
// and update based on the right parameters
// if DCC, then [[8,1,1],[24,2,2]]
// if BFRPG, then [[6,1,1],[24,2,1]]
// if 3.5E, then [[8,1,'LVL'],[24,2,'LVL']]
if (typeOfHealing == "normal") {
if (lengthOfRest > 0 && hd == "d") {
var amountToHeal = healingData[0][1] * lengthOfRest;
} else if (lengthOfRest >= healingData[0][0] && hd == "h") {
var amountToHeal = healingData[0][1];
} else {
sendChat("Chronos","/w gm Not enough time for normal rest, aborting.");
return;
};
} else if (typeOfHealing == "bedrest") {
if (lengthOfRest > 0 && hd == "d") {
var amountToHeal = healingData[1][1] * lengthOfRest;
} else {
sendChat("Chronos","/w gm Not enough time for bedrest, aborting.");
return;
};
};
if (parseInt(attributes[i].get("current")) < parseInt(attributes[i].get("max"))) {
attributes[i].set("current", parseInt(attributes[i].get("current")) + parseInt(amountToHeal));
if (attributes[i].get("current") > attributes[i].get("max")) {
attributes[i].set("current", attributes[i].get("max"));
};
};
};
};
};
function updateLights(lightSource,elapsedTime) {
var lightSourceName = lightSource.get("name");
var brightRadius = lightSource.get("bar3_max");
var dimRadius = lightSource.get("bar3_value");
var burningTimeTotal = lightSource.get("bar1_max");
var burningTimeRemaining = lightSource.get("bar1_value");
var trigger_dim = Math.floor(burningTimeTotal * 0.3);
var trigger_flicker = Math.floor(burningTimeTotal * 0.15);
var trigger_almostOut = Math.floor(burningTimeTotal * 0.05);
var newBurningTimeRemaining = burningTimeRemaining - (1.0 * elapsedTime);
if (burningTimeRemaining == 0 || lightSource.get("bar2_value") == 0) return; // make sure light source is lit & has fuel remaining
if (newBurningTimeRemaining >= trigger_dim && lightSource.get("light_radius") !== brightRadius) {
lightSource.set({ light_radius: brightRadius, light_dimradius: dimRadius});
} else if (newBurningTimeRemaining <= trigger_dim && newBurningTimeRemaining > trigger_flicker && (burningTimeRemaining > trigger_dim || burningTimeRemaining == newBurningTimeRemaining)) {
sendChat("","/emas " + lightSourceName + " grows dim. ");
lightSource.set({ light_dimradius: Math.floor(dimRadius * 0.66)});
} else if (newBurningTimeRemaining <= trigger_flicker && newBurningTimeRemaining > trigger_almostOut && (burningTimeRemaining > trigger_flicker || burningTimeRemaining == newBurningTimeRemaining)) {
sendChat("","/emas " + lightSourceName + " begins to flicker. ");
lightSource.set({ light_radius: Math.floor(brightRadius * 0.75), light_dimradius: Math.floor(dimRadius * 0.5)});
} else if (newBurningTimeRemaining <= trigger_almostOut && newBurningTimeRemaining > 0&& (burningTimeRemaining > trigger_almostOut || burningTimeRemaining == newBurningTimeRemaining)) {
sendChat("","/emas " + lightSourceName + " is about to go out. ");
lightSource.set({ light_radius: Math.floor(brightRadius * 0.5), light_dimradius: 0 });
} else if (newBurningTimeRemaining <= 0) {
newBurningTimeRemaining = 0;
sendChat("","/emas " + lightSourceName + " goes out!");
lightSource.set({
bar2_value: 0,
light_radius: "",
light_dimradius: ""
});
};
lightSource.set({
bar1_value: newBurningTimeRemaining
});
};
function findLights(numRounds) {
var playerVisibleLights = filterObjs(function(obj) {
if(obj.get("_pageid") == Campaign().get("playerpageid") && obj.get("_subtype") == "token" && obj.get("bar2_value") == "1" && obj.get("bar2_max") == "1" && obj.get("light_otherplayers") == true) return true;
else return false;
});
_.each(playerVisibleLights, function(obj) {
updateLights(obj,numRounds);
});
};
/*
on('ready', function() {
state.chronosid = findObjs({_type: "character", name: "Chronos"})[0].get("_id");
});
*/
on("chat:message", function(msg) {
if (msg.type == "api" && msg.who.indexOf("(GM)") !== -1 && msg.content.indexOf("!chronos") !== -1) {
var years = 0, months = 0, days = 0, hours = 0, minutes = 0;
var n = msg.content.split(" ");
if (n[1] == undefined) { // i.e., just "!chronos"
updateChronos(years,months,days,hours,minutes);
return;
} else if (n[2] == undefined) { // i.e., "!chronos 1d" or "!chronos 1d,4h"
var time = n[1].split(",");
for (i = 0; i < time.length; i++) {
if (time[i].indexOf("y") != -1) { years = time[i].slice(0, time[i].indexOf("y")); };
if (time[i].indexOf("m") != -1) { months = time[i].slice(0, time[i].indexOf("m")); };
if (time[i].indexOf("d") != -1) { days = time[i].slice(0, time[i].indexOf("d")); };
if (time[i].indexOf("h") != -1) { hours = time[i].slice(0, time[i].indexOf("h")); };
if (time[i].indexOf("n") != -1) { minutes = time[i].slice(0, time[i].indexOf("n")); };
};
} else { // i.e., params (only makes sense with values for time, should add some error catching)
var time = n[1].split(",");
var params = n[2].slice(1);
for (i = 0; i < time.length; i++) {
if (time[i].indexOf("y") != -1) { years = time[i].slice(0, time[i].indexOf("y")); };
if (time[i].indexOf("m") != -1) { months = time[i].slice(0, time[i].indexOf("m")); };
if (time[i].indexOf("d") != -1) { days = time[i].slice(0, time[i].indexOf("d")); };
if (time[i].indexOf("h") != -1) { hours = time[i].slice(0, time[i].indexOf("h")); };
if (time[i].indexOf("n") != -1) { minutes = time[i].slice(0, time[i].indexOf("n")); };
};
// kick off torches burning, healing, etc. here
for (i = 0; i < params.length; i++) {
switch (params[i]) {
case "h": // heal, i.e. bedrest for 24+ hours
healCharacters("d",days,"bedrest");
break;
case "r": // rest, i.e. sleep for 8+ hours
if (_.contains(params, "h")) {
sendChat("Chronos","/w gm Cannot use 'h' and 'r' in the same command, using 'h'.");
} else {
if (days > 0) {
healCharacters("d",days,"normal");
} else {
healCharacters("h",hours,"normal");
};
};
break;
case "t": // torches, i.e. track light source's fuel
findLights(minutes);
break;
};
};
};
updateChronos(years,months,days,hours,minutes);
};
});
on("change:graphic:bar1_value", function(obj) {
if (obj.get("_pageid") == Campaign().get("playerpageid") && obj.get("_subtype") == "token" && obj.get("bar2_max") == "1" && obj.get("light_otherplayers") == true) {
var maxValue = obj.get("bar1_max");
if (obj.get("bar1_value") > maxValue ) {
obj.set({bar1_value: maxValue});
};
updateLights(obj,0);
};
});
on("change:graphic:bar2_value", function(obj) {
if (obj.get("_pageid") == Campaign().get("playerpageid") && obj.get("_subtype") == "token" && obj.get("bar2_max") == "1" && obj.get("light_otherplayers") == true) {
if (obj.get("bar2_value") > 0 ) {
if (obj.get("bar1_value") > 0) {
obj.set({bar2_value: 1});
} else {
obj.set({bar2_value: 0})
};
updateLights(obj,0);
} else if (obj.get("bar2_value") <= 0 ) {
obj.set({bar2_value: 0});
obj.set({light_radius: "",light_dimradius: ""});
};
};
});
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment