-
-
Notifications
You must be signed in to change notification settings - Fork 127
Adding New Parks
All parks supported in the library must support both getting wait times and park opening times at a minimum.
It is recommended to use VSCode for development on themeparks, but any IDE that can support running eslint is fine.
We use eslint on themeparks. This helps to keep a relatively consistent coding style throughout.
Our eslint settings are in .eslintrc.json in the main branch. VSCode should be able to automatically use this to highlight lint errors.
Pull requests will be automatically tested against eslint and will fail any merge attempt if they are not linter error free.
There are various tests available in the library.
- Offline Test
- npm run test
- This will test the library syntax and core systems to ensure they still behave as expected
- Online Test
- PARKID=[PARK ID] PRINTDATA=true NODE_DEBUG=themeparks npm run testonline
- This will test getting wait times and opening times for the supplied park
- Run eslint
- npm run lint
- This will run eslint against the project and return any errors found
To implement a new park, you'll want to create a new directory inside the lib/ folder of the source code matching the park's name (or group name). For example, I am implementing the Universal Studios parks, so I've made a new folder called "universal" inside lib/. If this were a single park that shares no common API with any other park, I would name the directory after the park itself, eg. europapark.
Avoid using index.js, so that it is easier to debug and find the correct file when developing and maintaining the library. If you are developing a base class that multiple parks will use, call the base class something like "universalparkbase.js" or similar.
If unsure, look at the existing parks in the lib/ directory for examples.
Each park should extend the core Park object and override the constructor. Call super(options) to call the base Park.constructor where appropriate.
// include core Park class
const Park = require('../park');
/**
* Implements the Universal API framework. All Universal parks use this one API.
* @class
* @extends Park
*/
class UniversalPark extends Park {
constructor(options = {}) {
options.name = options.name || 'Universal Park';
// inherit from base class
super(options);
}
}
// export the class
module.exports = UniversalPark;
See other park implementations for examples of how to setup parks further, but this should always be the base framework to use.
We use the needle library for making HTTP requests. However, we wrap this in our own mini-library called "HTTP", which helps us with network errors and some automated niceties to make life a little easier (such as parsing JSON for us if we detect a JSON object response).
Each park has a helper function called simply "HTTP". This passes on the park's User-Agent string for you. Call it like so (see docs for all options):
this.HTTP({
url: 'http://api.com/waittimes',
data: {
token: this[s_sessionToken],
date: 'now'
}
}).then((result) => {
// use data
});
HTTP uses Promises. Usage can be seen through other park implementations or in the needle documentation. HTTP passes options through to needle.request. https://github.com/tomas/needle
To implement wait times, create a function in your new class called "FetchWaitTimes". This should return a Promise, which will resolve and reject with no additional arguments based on whether it was successful.
Once your FetchWaitTimes function has fetched the wait time data it needs, you'll need to setup each ride object and update it's WaitTime status.
An example loop is below:
waitTimeData.forEach((ride) => {
// get the ride object for updating (if this ride doesn't exist, it'll automatically make a new one)
this.UpdateRide(ride.id, {
name: ride.name,
waitTime: ride.wait_time || -1,
});
});
This will obviously change from park to park, but the core loop should be roughly the same.
In this loop you can also update the ride FastPass status (if the park supports FastPass, make sure you override get FastPass()
so the park is labelled as such)
You can also apply arbitrary meta data to each ride, in case you wish to record park-specific data that doesn't otherwise fit the data structures the library provides.
this.UpdateRide(ride.id, {
name: ride.name,
waitTime: ride.wait_time || -1,
fastPass: ride.fastPassAvailable,
meta: {
singleRider: ride.hasSingleRiderQueue,
}
});
See API docs for more information about how to use the Ride object. https://cubehouse.github.io/themeparks/Ride.html
To implement park opening times, create a function in your new class called "FetchOpeningTimes". This should return a Promise, which will resolve and reject with no additional arguments based on whether it was successful.
The schedule data for each park is contained in this.Schedule.
After fetching opening hours from the API, call this.Schedule for each date or date-range you have. For example:
this.Schedule.SetDate({
date: Moment.tz(openingTime.date, "YYYY-MM-DD", this.Timezone),
openingTime: Moment.tz(openingTime.open, "YYY-MM-DDTHH:mm:ss", this.Timezone),
openingTime: Moment.tz(openingTime.close, "YYY-MM-DDTHH:mm:ss", this.Timezone),
type: "Operating"
});
You can also set ranges of dates in one go with SetRange. For example:
openingTimes.forEach((sched) => {
this.Schedule.SetRange({
startDate: Moment.tz(sched.from, "YYYY-MM-DD", this.Timezone),
endDate: Moment.tz(sched.until, "YYYY-MM-DD", this.Timezone),
openingTime: Moment.tz(sched.start, "HH:mm", this.Timezone),
closingTime: Moment.tz(sched.end, "HH:mm", this.Timezone)
});
});
See already implemented parks for good examples of various ways of setting this. Also the documentation provides good descriptions of what you can pass into SetDate and SetRange. Themeparks Schedule Documentation