diff --git a/README.md b/README.md index 110507a..37f87b9 100644 --- a/README.md +++ b/README.md @@ -8,7 +8,7 @@ Your troubles are long gone with ***Ride Price Manager***! ## Using the Plugin - First, follow the installation instructions below -- Once installed, the plugin should work automatically in game. It will _automagically_ update the prices of all your rides. +- Once installed, the plugin should work automatically in game. It will _automagically_ update the prices of all your rides every in-game day. - By default, the plugin chooses the highest price that guests are willing to pay for a ride. - That number is determined by ride rating, ride age, and other factors. - You can configure the plugin via the "Ride Price Manager" _control panel_, which is available **under the Map Dropdown** on the top bar. There, you can: @@ -18,6 +18,9 @@ Your troubles are long gone with ***Ride Price Manager***! - add a "Lazy Tax" to decrease ride prices. This is for people that think this plugin is overpowered or just want to re-balance the game a little bit. - allow prices greater than $20.00 for high value rides. - Guests are willing to pay more than $20 for some rides, but you can't set a price to more than $20 via the UI. This allows the plugin to bypass that check. +- There are also two buttons in the _control panel_: + - "Make ALL Rides FREE" which does just that. This may be useful in conjunction with the "Ignore free rides" option. + - "Force Recalculate ALL Prices Now" also does what it says on the tin. Note that it doesn't use the "Ignore free rides" option, and recalculates free rides. ## Installation 1. Install a compatible version of OpenRCT2 (requires [`0.3.0-develop-bc33ef3`](https://openrct2.org/downloads/develop/v0.3.0-bc33ef3) released 2020/09/03 or [newer](https://openrct2.org/downloads/develop/latest)) @@ -29,9 +32,30 @@ Your troubles are long gone with ***Ride Price Manager***! 4. Run OpenRCT2! 5. Once in a scenario, open the options window via the Map dropdown to configure the plugin. +## Multiplayer +This plugin is now updated to work in Multiplayer! However, there are some things to know... + +- The plugin must be installed on the server to automatically update prices every in-game day. +- When the prices are updated daily, only the config on the server is used. +- Any player with the plugin can still use the "Force recalculate" and "Make rides free" buttons. There are no permissions. File an issue if you are dealing with griefers. +- For the recalculate button, the client's config is used, but will be bulldozed when server recalculates prices on the next in-game day. + +To set the server config, you will need to edit the `plugin.store.json` file (typically in `C:\Users\{User}\Documents\OpenRCT2`). If that file doesn't exist, use the following as the file contents. If some keys already exist, just add the `RidePriceManager` block to what's already there. And don't mess up the commas! + +``` +{ + "RidePriceManager": { + "pluginEnabled": true, + "ignoreFreeRidesEnabled": true, + "goodValueEnabled": false, + "lazyTaxFactor": 0, + "unboundPriceEnabled": false + } +} +``` + ## Possible Future Plans -- Check if this works with server play - - And make it work with server play +- Have better tools for managing the price of transport rides. - Automatically set park prices & allow configuration of a max admission price. - This would help early-game ramp-up. - Manage shop prices @@ -52,7 +76,7 @@ If you want to request a feature or find a bug, open an issue on GitHub (as long - Run a Command Prompt as Administrator - `cd` to the repository - `mklink .\lib\openrct2.d.ts "C:\Program Files\OpenRCT2\openrct2.d.ts"` -- Edit `ride-price-manager\rollup.config.dev.js` so that it puts the build in your plugin folder. +- Edit `ride-price-manager\rollup.config.dev.js` so that it puts the build in your plugin folder. Sorry that this is custom to me :O - Run `npm run watch` - Make your changes. - Make sure to update the version number in `package.json` and in `index.js`. @@ -60,6 +84,7 @@ If you want to request a feature or find a bug, open an issue on GitHub (as long ## Thanks - Thanks to [OpenRCT2](https://github.com/OpenRCT2/OpenRCT2) for revitalizing a sweet game & releasing the plugin API. -- Thanks to **oli414** for the [boilerplate template](https://github.com/oli414/openrct2-plugin-boilerplate). I adapted it to fit TypeScript for this project initally, before **wisnia74** came out with [a very well made and comprehensive TypeScript plugin template](https://github.com/wisnia74/openrct2-typescript-mod-template), which I now use for this plugin. You should definitely use that one if you want to make your own plugin. +- This plugin is powered by **wisnia74**'s [very well made and comprehensive TypeScript plugin template](https://github.com/wisnia74/openrct2-typescript-mod-template). I highly recommend you use that one if you want to make your own plugin. So thank you, wisnia! +- Originally, this plugin was powered by **oli414**'s [boilerplate template](https://github.com/oli414/openrct2-plugin-boilerplate) (which I adapted to support TypeScript, but it still wasn't as nice as the new plugin template). Thanks oli for helping me get the ball rolling :) - Thanks to [Marcel Vos](https://www.youtube.com/channel/UCBlXovStrlQkVA2xJEROUNg) for getting me back into RCT2. - And many many thanks to [IntelOrca](https://github.com/IntelOrca) for all of the Plugin support. The Plugin API is super fun, and I can't wait to see what all people make for it. diff --git a/package.json b/package.json index 0429f3d..7e376dc 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "ride-price-manager", - "version": "1.3.0", + "version": "1.3.1", "description": "Ride Price Manager is a plugin for OpenRCT2 that automagically updates the prices of all your rides", "main": "app.js", "scripts": { diff --git a/src/RidePrices.ts b/src/RidePrices.ts new file mode 100644 index 0000000..7823bcf --- /dev/null +++ b/src/RidePrices.ts @@ -0,0 +1,84 @@ +/// + +import config from './config'; + +export default class RidePrices { + public static updateRidePrices(): void { + if (!config.getPluginEnabled()) { + return; + } + map.rides.map((ride: Ride) => { // eslint-disable-line array-callback-return + if (ride.price[0] === 0 && config.getIgnoreFreeRidesEnabled()) { + // Ignore free rides. + return; + } + RidePrices.setRidePrice(ride); + }); + } + + public static forceUpdateRidePrices(): void { + map.rides.map((ride: Ride) => RidePrices.setRidePrice(ride)); + } + + public static makeRidesFree(): void { + map.rides.map((ride: Ride) => RidePrices.setRidePrice(ride, 0)); + } + + private static setRidePrice(ride: Ride, priceInDimes?: number): void { + if (!RidePrices.isOpenAndRatedRide(ride)) { + return; + } + + if (priceInDimes === undefined) { + priceInDimes = RidePrices.calculateRidePrice(ride); + } + + // Set the price via an action (so it works in multiplayer) + context.executeAction( + 'ridesetprice', + { + ride: ride.id, + price: priceInDimes, + isPrimaryPrice: true, + }, + () => { }, + ); + } + + private static isOpenAndRatedRide(ride: Ride): boolean { + // Ignore shops & facilites. + return ride.classification === 'ride' + // Ignore closed/testing rides. + && ride.status === 'open' + // Ignore unrated rides. + && ride.excitement !== -1; + } + + private static calculateRidePrice(ride: Ride): number { + // See /src/openrct2/peep/Guest.cpp for logic. + // if (peep_flags & PEEP_FLAGS_HAS_PAID_FOR_PARK_ENTRY) value /= 4; + const value = park.entranceFee > 0 && !park.getFlag('freeParkEntry') + ? Math.floor(ride.value / 4) + : ride.value; + + // See /src/openrct2/peep/Guest.cpp for logic. + // if (ridePrice > value * 2) ChoseNotToGoOnRide + // if (ridePrice <= value / 2) InsertNewThought(PEEP_THOUGHT_TYPE_GOOD_VALUE) + let priceInDimes = config.getGoodValueEnabled() + ? Math.floor(value / 2) + : value * 2; + + if (config.getLazyTaxFactor() > 0) { + priceInDimes *= (1 - config.getLazyTaxFactor()); + priceInDimes = Math.floor(priceInDimes); + } + + if (!config.getUnboundPriceEnabled()) { + // Max price is $20 via the UI. + priceInDimes = Math.min(priceInDimes, 200); + } + + return priceInDimes; + } +}; + diff --git a/src/main.ts b/src/main.ts index 87c74f8..7a0eb8a 100644 --- a/src/main.ts +++ b/src/main.ts @@ -1,18 +1,24 @@ /// -import { updateRidePrices } from './ridePriceFunctions'; +import RidePrices from './RidePrices'; import showWindow from './window'; function main(): void { - ui.registerMenuItem('Ride Price Manager', () => { - showWindow(); - }); + // Headless server homies don't need to register UI, you feel me? + if (ui) { + ui.registerMenuItem('Ride Price Manager', () => { + showWindow(); + }); + } - context.subscribe('interval.day', () => { - updateRidePrices(); - }); + // Only the server/singleplayer automatically triggers prices updates. + if (network.mode !== 'client') { + context.subscribe('interval.day', () => { + RidePrices.updateRidePrices(); + }); - updateRidePrices(); + RidePrices.updateRidePrices(); + } } export default main; diff --git a/src/registerPlugin.ts b/src/registerPlugin.ts index 8980c38..c594f03 100644 --- a/src/registerPlugin.ts +++ b/src/registerPlugin.ts @@ -4,7 +4,7 @@ import main from './main'; registerPlugin({ name: 'Ride Price Manager', - version: '1.3.0', + version: '1.3.1', authors: ['mgovea', 'Sadret'], type: 'remote', licence: 'MIT', diff --git a/src/ridePriceFunctions.ts b/src/ridePriceFunctions.ts deleted file mode 100644 index 37574af..0000000 --- a/src/ridePriceFunctions.ts +++ /dev/null @@ -1,82 +0,0 @@ -/// - -import config from './config'; - -function updateRidePrices(): void { - if (!config.getPluginEnabled()) { - return; - } - map.rides.map((ride: Ride) => { // eslint-disable-line array-callback-return - if (ride.price[0] === 0 && config.getIgnoreFreeRidesEnabled()) { - // Ignore free rides. - return; - } - updateRidePrice(ride); - }); -} - -function forceUpdateRidePrices(): void { - map.rides.map(updateRidePrice); -} - -function makeRidesFree(): void { - map.rides.map((ride: Ride) => { // eslint-disable-line array-callback-return - setRidePrice(ride, 0); - }); -} - -function updateRidePrice(ride: Ride): void { - if (ride.classification !== 'ride') { - // Ignore shops & facilites. - return; - } - - if (ride.status !== 'open' || ride.excitement === -1) { - // Don't change the price of closed/testing and unrated rides. - return; - } - - // See /src/openrct2/peep/Guest.cpp for logic. - // if (peep_flags & PEEP_FLAGS_HAS_PAID_FOR_PARK_ENTRY) value /= 4; - const value = park.entranceFee > 0 - ? Math.floor(ride.value / 4) - : ride.value; - - // See /src/openrct2/peep/Guest.cpp for logic. - // if (ridePrice > value * 2) ChoseNotToGoOnRide - // if (ridePrice <= value / 2) InsertNewThought(PEEP_THOUGHT_TYPE_GOOD_VALUE) - let priceInDimes = config.getGoodValueEnabled() - ? Math.floor(value / 2) - : value * 2; - - if (config.getLazyTaxFactor() > 0) { - priceInDimes *= (1 - config.getLazyTaxFactor()); - priceInDimes = Math.floor(priceInDimes); - } - - if (!config.getUnboundPriceEnabled()) { - // Max price is $20 via the UI. - priceInDimes = Math.min(priceInDimes, 200); - } - - setRidePrice(ride, priceInDimes); -} - -function setRidePrice(ride: Ride, priceInDimes: number): void { - // Set the price via an action (so it works in multiplayer) - context.executeAction( - 'ridesetprice', - { - ride: ride.id, - price: priceInDimes, - isPrimaryPrice: true, - }, - () => { }, - ); -} - -export { - updateRidePrices, - forceUpdateRidePrices, - makeRidesFree, -}; diff --git a/src/window.ts b/src/window.ts index d7a0646..e0230e1 100644 --- a/src/window.ts +++ b/src/window.ts @@ -1,7 +1,7 @@ /// import config, { LazyTaxOption, lazyTaxOptions } from './config'; // eslint-disable-line no-unused-vars -import { forceUpdateRidePrices, makeRidesFree } from './ridePriceFunctions'; +import RidePrices from './RidePrices'; const windowTag = 'ride_management'; const lQuote = decodeURI('%E2%80%9C'); @@ -157,7 +157,7 @@ function makeRecalculateButton(y: number): ButtonWidget { y, 'Immediately set the price for all rides, overriding the "Ignore Free" preference', 'Force Recalculate ALL Prices Now', - forceUpdateRidePrices, + RidePrices.forceUpdateRidePrices, ); } @@ -166,7 +166,7 @@ function makeAllRidesFreeButton(y: number): ButtonWidget { y, 'Immediately make all rides free.', 'Make ALL Rides FREE', - makeRidesFree, + RidePrices.makeRidesFree, ); }