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

[Help] Script to tell and adjust in-game time

November 30 (10 years ago)

Edited November 30 (10 years ago)
I've created what I thought was a simple script for my campaign that would let me keep track of the in-game time with a few commands.

The commands are !ttsethour, !ttsetmin and !ttsetsec, which set the hour, the minute of the hour, and the second of the minute. A fourth command, !time, will tell the three values separated by colons.

My problem is that the variables I set for the three values become undefined outside of their respective functions, and I'm having no luck trying to store the variables globally for use. I'm quite novice at Javascript. Any help would be greatly appreciated.

EDIT: To clarify, my output when I use !time is this: Time: undefined:undefined:undefined.

The code thus far:

https://gist.github.com/7ca2560fdbeecb8c049f.git
November 30 (10 years ago)
Right, variables defined inside a scope (anywhere between { and }) are only visible within that scope. If this is the only script you intend to use, you can just set the variables to something outside of the relevant functions, and remove the "var" keyword which defines a new variable within the local scope. However, global variables don't play well with other scripts (if anyone uses the same name you did, you'll step on each other's toes), so you may want to look into the state object (Campaign().state) and store it within there.
December 01 (10 years ago)

Edited December 01 (10 years ago)
I might need an example on the (Campaign().state) [assuming that i cant figure it out myself], but thanks for the insight.

Regarding setting them outside the functions, that won't work either - at least not as static variables. I plan on adding more functions that will add and subtract time to the individual values, i.e. having a macro (!timeadd [hours] [minutes] [seconds]) that would allow me to easily add time to the clock as the players progress.

In other words, I need to be able to access and modify these variables anywhere in the script. It does not have to work outside the script, and I'll try to rename any global variables uniquely so they won't clash with anyone else's.

I'd really love to get this script developed and share it with you guys when it's done. It can be useful for more hardcore campaigns where time is very critical and needs to be measured precisely (and quickly).
December 01 (10 years ago)
The Aaron
Roll20 Production Team
API Scripter
It's just state, not Campaign().state. The state object is just an object that gets persisted in the database fairly often (ever 1-5 seconds?). It is shared by all scripts, so you need to be mindful when you use it.

The general etiquette is to create yourself a property on it with a reasonable name and only do things in that name.

Most of my scripts use the state object. Any script I have that does creates a property named after the script. You only want to create the property if it doesn't already exist. All of my scripts that use it have a function called CheckInstall() that handles creating a state property if necessary. Additionally, I store a version number in the object I assign to my state property. This version number represents the version of the format of the state property object, which allows me to easily tell if I'm looking at an older version of how I wanted to store information. If necessary, I could write logic to migrate between different schema versions.

Once set up, you can just access it with state.property.field. I'd suggest reading the section in the wiki, then look at some of my scripts for how they use it. I'll try to write something more cohesive when I'm not on my phone and holding a crying baby! :)
December 01 (10 years ago)
Thank you so much for the response. And I can relate. My daughter just loves to cuddle on my lap during campaigns. This is very insightful. I'll see what I can come up with and will post results soon.
December 01 (10 years ago)

Edited December 01 (10 years ago)
Okay. I'm making progress (I think?). I've created a state that can be read by the other functions ... now I just need to figure out how to modify it and shoot it back properly.

The last thing I want to do is turn this into Java 101, but again, any help for a noobie is superly appreciated!

https://gist.github.com/anonymous/98d5d25d17e7ef53fc00#file-makeyourtime

What's happening here is that I'm using the command "!!ttsethour 21" to set the hour to 21, but I get the output of "The hour 0 has been set" So at least its defined, but it wont change.
December 01 (10 years ago)

Edited December 01 (10 years ago)
Ok! I made some small changes and took out the individual functions' unnecessary functions and just redefined the state and it persisted! I can now set and read time just fine.

https://gist.github.com/anonymous/ca9057cfe353f69b4048#file-make-your-time

Now I'm on to making addition (and subtraction) sub functions as well as functions that automatically add minutes and hours when there's more than 60 seconds or minutes in a field.
December 01 (10 years ago)
Finished! Making a new thread. Thank you for all the help!
December 01 (10 years ago)
The Aaron
Roll20 Production Team
API Scripter
John, I'm glad you got it working the way you want.

I feel a bit bad because I did a bad job explaining the state object to you, and you aren't actually using it. =/

I'll try it again...

state is a variable in the global scope which is automatically created for the API. The Wiki doesn't do a great job of explaining it, but here is where you can find it: https://wiki.roll20.net/API:Objects#Global_Objects

Like everything in Javascript, it is an object. In this case, it is an object that starts out with nothing in it, but anything in it will be persisted in a database across restarts of the API. It is a shared resource, so every API script running in the same campaign uses the same object. This is why you need to be extra careful not to stomp on other scripts toes and pick your names carefully. Additionally, since it is persisted every second or so, you need to be mindful not to put too much into it. You should strive to store the absolute minimum amount of information in it. A while back, there was a script that stored all of the spells from Pathfinder in the state, which caused a big issue. Don't do that. =D

How I use state
For each of my scripts, I create a property in the state object:
state.MyScript = {};
However, you can't write that to the state object every time or you would overwrite whatever you have that you were intending to persist. So, you need to wrap this in a check:
if( !_.has(state,'MyScript')) {
	state.MyScript = {};
}
_.has() is basically the same as Object.hasOwnProperty(). This will see if state already has a property named 'MyScript', and will only add one if it isn't there already.

You can also setup your property with properties of its own:
if( !_.has(state,'MyScript')) {
	state.MyScript = {
		hour: 0,
		minute: 0,
		second: 0
	};
}

All that is left then is to do this at a point that makes sense. I do this in the on('ready') event, so it only gets run once per restart of the api:
on('ready',function(){
	if( !_.has(state,'MyScript')) {
		state.MyScript = {
			hour: 0,
			minute: 0,
			second: 0
		};
	}
});

Now in your script you can be assured that you can access these properties with impunity:
state.MyScript.minute += 20; 
state.MyScript.hour += Math.floor(state.MyScript.minute / 60);
state.MyScript.minute %= 60;

Hopefully that clears up the fallout from my rambling earlier explanation. =D Feel free to PM me if you want to chat on deeper topics!
December 01 (10 years ago)
Wow. So I must have accidentally used a global variable? I understand that's kind of bad practice. This looks a lot safer.

Thank you very much for the help. I'm going to keep this referenced so I can put this into practice on my next script, and as soon as I get the chance I'll try to clean up the other one.
December 01 (10 years ago)
The Aaron
Roll20 Production Team
API Scripter
Your solutions is actually somewhat clever, even if by accident. =D

In Javascript, everything is an object.
log(10.37.toString().length);
Will output 5, as the toString() method on the Number object prototype (which is the prototype for the number 10.37) will return the string "10.37", and then the length property from the String object prototype is populated with the length of that string, 5.

So, saying that, Functions are also objects. You are creating a property on each of those functions and then accessing them in each the functions. So technically, you have no more global variables than you did already by creating the functions.

You COULD have fewer still. I write my scripts in a style called a Javascript Module (at least, by Douglas Crockford). With the method I use, there is only one global variable created (and I could actually do it with no global variables if I wanted to). The idea is to create a closure, which is effectively a way of having private variables (which as you know can be objects of any type, including functions).

If you'd like to get deep into good Javascript, I highly recommend Javascript: The Good Parts by Douglas Crockford. It's a pretty quick read and will really give you a deep understanding of the good parts of javascript. I sucked at JS before reading it. I'm much better at it now. =D

And of course, feel free to chat with me! =D