Roll20 uses cookies to improve your experience on our site. Cookies enable you to enjoy certain features, social sharing functionality, and tailor message and display ads to your interests on our site and others. They also help us understand how our site is being used. By continuing to use our site, you consent to our use of cookies. Update your cookie preferences .
×
Create a free account

Chronos Time Keeper

1510668632

Edited 1510669743
Is anyone familiar with this script? Im trying to use it with the Faerun Calendar set up. I think it works so far but I'm trying to get it to display the Month with its Name instead of with the number of the month. This is what it looks like now "(From Chronos): 10th Day, 1343.8.2.3.41." I would like it to cleaner and display the names instead of all those numbers. /* 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 || 0; 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 = [['Hammer',30],[ 'Midwinter',1],['Alturiak',30],["Ches",30],['Tarsakh',30],[ 'Greengrass',1],['Mirtul',30],['Kythorn',30],['Flamerule',30],[ 'Midsummer',1],['Eleasis',30],['Eleint',30],[ 'Highharvesttide',1],['Marpenoth',30],['Uktar',30],[ 'The Harvest of The Moon',1],['Nightal',30]]; var lengthOfYear = lengthOfYear(); var week = ['1st Day','2nd Day','3rd Day','4th Day','5th Day','6th Day','7th Day','8th Day','9th Day','10th Day']; 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: ""}); }; }; });
saidly not sure myself i am using a basic system myself with the calendar  of harptos image as a map and tokens i start my game with it set to a date then adjust it according to how much time passed in that session.  Harptos calendar
1510681721
Kirsty
Pro
Sheet Author
I'm not sure if this is exactly what you guys are looking for, but I have a Harptos Calendar script up on GitHub. It shows moon phases, rolls weather and tracks down days (or days passed in game). The most current version is  here .
Kirsty said: I'm not sure if this is exactly what you guys are looking for, but I have a Harptos Calendar script up on GitHub. It shows moon phases, rolls weather and tracks down days (or days passed in game). The most current version is  here . just after posting i found the API :D lovely coding thanks for sharing it
1510762694
Kirsty
Pro
Sheet Author
You're very welcome!
Kirsty said: You're very welcome! now how hard would it be to modify the script to have a days until full moon output for the faerun Calendar? 
1512682942
Kirsty
Pro
Sheet Author
Hmmmm, interesting. It shouldn't be very difficult, Faerun's moon cycles are regular so it would probably just be a matter of adding a counter. I'll have a look and see what I can come up with.
1512745699
Kirsty
Pro
Sheet Author
Well I had a look at it this morning. Faerun is actually on a leap year schedule, so it's not just a matter of tracking days. It is possible, but I'm swamped with Christmas stuff at the moment. If you send me a reminder in the New Year, I'll be happy to make that adjustment for you. Unfortunately, I'm not very good at javascript, so I'm very slow. :(
Kirsty said: Well I had a look at it this morning. Faerun is actually on a leap year schedule, so it's not just a matter of tracking days. It is possible, but I'm swamped with Christmas stuff at the moment. If you send me a reminder in the New Year, I'll be happy to make that adjustment for you. Unfortunately, I'm not very good at javascript, so I'm very slow. :( No rush and will stick a pin in it for now. Will definitely remind you after the hangover 😀
Kirsty said: Well I had a look at it this morning. Faerun is actually on a leap year schedule, so it's not just a matter of tracking days. It is possible, but I'm swamped with Christmas stuff at the moment. If you send me a reminder in the New Year, I'll be happy to make that adjustment for you. Unfortunately, I'm not very good at javascript, so I'm very slow. :( i hope you enjoyed your holiday no rush but as requested the pin is reminded :D 
1514932933

Edited 1514932950
Kirsty
Pro
Sheet Author
Thank you for the reminder! I'll start working on this tomorrow. :)
1515376054
Kirsty
Pro
Sheet Author
@lordmage The change you requested has been implemented in the  latest version of the multi-world calendar. I've also made some major changes in how the moon cycle is calculated, so both Faerun and Greyhawk moons match up with their official calendars.
I love this calendar! Currently, I'm using it in combination with the  Timetracker-Script - mainly because of the event function. I wonder if you can implement a similar functionality in your calendar? So essentially some timer countdowns (I use it to keep track of spells that go over several hours, night watches, sunset and sunrise and things like that)...
Kirsty said: @lordmage The change you requested has been implemented in the  latest version of the multi-world calendar. I've also made some major changes in how the moon cycle is calculated, so both Faerun and Greyhawk moons match up with their official calendars. Sweet thanks
1515427675
Kirsty
Pro
Sheet Author
@Arthur B I'm so glad you're finding the script useful! I'm actually working on a version of the script right now that will incorporate events. Unfortunately, it's not going to be as detailed as the Timetracker script that you're currently using. As of right now, it's only going to track event by day. Your request is probably a bit beyond the scope of where I want to go with this calendar, but I will keep it on the list of requests. @lordmage You're welcome!
@Kirsty have you worked in a system for the campaign setting released by matt mercer i know it is a matter of changing names and such but i have a fellow dm that is plainning a game using that systm&nbsp;<a href="http://criticalrole.wikia.com/wiki/Calendar_of_Tal%27Dorei" rel="nofollow">http://criticalrole.wikia.com/wiki/Calendar_of_Tal%27Dorei</a>&nbsp;
1515904971

Edited 1515905028
Kirsty
Pro
Sheet Author
@lordmage I dont have the Tal Dorei setting included, but I understand it's popular with critters. I'm happy to include it if you can give me details on months/lunar cycles. Edit: The link has everything I need. I'll include it in the next update.
sweet im sure Critter everywhere will love the inclusion of this i know of at least one GM who will. i am assiting him in running the game behind the scenes but yeah im a player :D&nbsp;
1515905552
Kirsty
Pro
Sheet Author
Awesome! I'm excited to see the beginning of the new campaign.&nbsp; I have a game on Thursdays so I'm waiting for Monday's YouTube release. :)
1515963199
Kirsty
Pro
Sheet Author
The calendar has been updated to include Tal'Dorai. I couldn't find any information regarding the lunar cycles online. I'll be happy to include them at a later date if someone can provide details. Latest version
Kirsty said: The calendar has been updated to include Tal'Dorai. I couldn't find any information regarding the lunar cycles online. I'll be happy to include them at a later date if someone can provide details. Latest version one slight notice once set to the new calendar the start day still references the Faerun calendar should this not adjust to the selected system.&nbsp;&nbsp;
1516026957
Kirsty
Pro
Sheet Author
The start date has to be entered manually. If it switched when you switched worlds, it would overwrite the setting and you could potentially lose data.
ok i will make sure we keep that in mind then... this was a first use in a separate game i figured when i selected tal`dore it would revert to a default day one for that system&nbsp;&nbsp;
1516030489
Kirsty
Pro
Sheet Author
Unfortunately, I've got no way of telling how long the script has been installed, so I decided it was safer to leave a placeholder so that the DM can input their exact start date. At this point, the start date doesn't really do anything. The (eventual) plan is for the calendar to keep track of the number of days passed in game.&nbsp;