From 5a9a7dd0ac313597acb40288da07934256adb66e Mon Sep 17 00:00:00 2001 From: mgovea <1916712+mgovea@users.noreply.github.com> Date: Wed, 31 Mar 2021 16:09:56 -0600 Subject: [PATCH 1/8] Don't call UI for servers where it doesn't exist --- src/main.ts | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/main.ts b/src/main.ts index 87c74f8..55650c0 100644 --- a/src/main.ts +++ b/src/main.ts @@ -4,9 +4,11 @@ import { updateRidePrices } from './ridePriceFunctions'; import showWindow from './window'; function main(): void { - ui.registerMenuItem('Ride Price Manager', () => { - showWindow(); - }); + if (ui) { + ui.registerMenuItem('Ride Price Manager', () => { + showWindow(); + }); + } context.subscribe('interval.day', () => { updateRidePrices(); From 896fd2661c5de39ac23ed7919cc75d4c1bd275ee Mon Sep 17 00:00:00 2001 From: mgovea <1916712+mgovea@users.noreply.github.com> Date: Wed, 31 Mar 2021 16:29:14 -0600 Subject: [PATCH 2/8] refactor ride price functions into a static class --- src/RidePrices.ts | 79 +++++++++++++++++++++++++++++++++++++ src/main.ts | 6 +-- src/ridePriceFunctions.ts | 82 --------------------------------------- src/window.ts | 6 +-- 4 files changed, 85 insertions(+), 88 deletions(-) create mode 100644 src/RidePrices.ts delete mode 100644 src/ridePriceFunctions.ts diff --git a/src/RidePrices.ts b/src/RidePrices.ts new file mode 100644 index 0000000..b1e876c --- /dev/null +++ b/src/RidePrices.ts @@ -0,0 +1,79 @@ +/// + +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.updateRidePrice(ride); + }); + } + + public static forceUpdateRidePrices(): void { + map.rides.map(RidePrices.updateRidePrice); + } + + public static makeRidesFree(): void { + map.rides.map((ride: Ride) => { // eslint-disable-line array-callback-return + RidePrices.mutateRidePrice(ride, 0); + }); + } + + private static 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); + } + + RidePrices.mutateRidePrice(ride, priceInDimes); + } + + private static mutateRidePrice(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, + }, + () => { }, + ); + } +}; + diff --git a/src/main.ts b/src/main.ts index 55650c0..423dfd7 100644 --- a/src/main.ts +++ b/src/main.ts @@ -1,6 +1,6 @@ /// -import { updateRidePrices } from './ridePriceFunctions'; +import RidePrices from './RidePrices'; import showWindow from './window'; function main(): void { @@ -11,10 +11,10 @@ function main(): void { } context.subscribe('interval.day', () => { - updateRidePrices(); + RidePrices.updateRidePrices(); }); - updateRidePrices(); + RidePrices.updateRidePrices(); } export default main; 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, ); } From 6ea120a3f8fa9b1d62e52c059654aa0d9fbafb2b Mon Sep 17 00:00:00 2001 From: mgovea <1916712+mgovea@users.noreply.github.com> Date: Wed, 31 Mar 2021 16:45:21 -0600 Subject: [PATCH 3/8] refactor the inside of RidePrices a bit --- src/RidePrices.ts | 55 ++++++++++++++++++++++++++--------------------- 1 file changed, 30 insertions(+), 25 deletions(-) diff --git a/src/RidePrices.ts b/src/RidePrices.ts index b1e876c..cf30a22 100644 --- a/src/RidePrices.ts +++ b/src/RidePrices.ts @@ -12,31 +12,49 @@ export default class RidePrices { // Ignore free rides. return; } - RidePrices.updateRidePrice(ride); + RidePrices.setRidePrice(ride); }); } public static forceUpdateRidePrices(): void { - map.rides.map(RidePrices.updateRidePrice); + map.rides.map(RidePrices.setRidePrice); } public static makeRidesFree(): void { - map.rides.map((ride: Ride) => { // eslint-disable-line array-callback-return - RidePrices.mutateRidePrice(ride, 0); - }); + map.rides.map((ride: Ride) => RidePrices.setRidePrice(ride, 0)); } - private static updateRidePrice(ride: Ride): void { - if (ride.classification !== 'ride') { - // Ignore shops & facilites. + private static setRidePrice(ride: Ride, priceInDimes?: number): void { + if (!RidePrices.isOpenAndRatedRide(ride)) { return; } - if (ride.status !== 'open' || ride.excitement === -1) { - // Don't change the price of closed/testing and unrated rides. - 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 @@ -60,20 +78,7 @@ export default class RidePrices { priceInDimes = Math.min(priceInDimes, 200); } - RidePrices.mutateRidePrice(ride, priceInDimes); - } - - private static mutateRidePrice(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, - }, - () => { }, - ); + return priceInDimes; } }; From 3ca9a1462314c8229b248be15f3d00d1299cea7c Mon Sep 17 00:00:00 2001 From: mgovea <1916712+mgovea@users.noreply.github.com> Date: Wed, 31 Mar 2021 17:12:34 -0600 Subject: [PATCH 4/8] fix bug with force-set-price button where price=index --- src/RidePrices.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/RidePrices.ts b/src/RidePrices.ts index cf30a22..a8b266f 100644 --- a/src/RidePrices.ts +++ b/src/RidePrices.ts @@ -17,7 +17,7 @@ export default class RidePrices { } public static forceUpdateRidePrices(): void { - map.rides.map(RidePrices.setRidePrice); + map.rides.map((ride: Ride) => RidePrices.setRidePrice(ride)); } public static makeRidesFree(): void { From 384ca5e97f8c9651b9d9176837a431196b200826 Mon Sep 17 00:00:00 2001 From: mgovea <1916712+mgovea@users.noreply.github.com> Date: Wed, 31 Mar 2021 23:51:16 -0600 Subject: [PATCH 5/8] only servers automatically update prices --- src/main.ts | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/src/main.ts b/src/main.ts index 423dfd7..7a0eb8a 100644 --- a/src/main.ts +++ b/src/main.ts @@ -4,17 +4,21 @@ import RidePrices from './RidePrices'; import showWindow from './window'; function main(): void { + // Headless server homies don't need to register UI, you feel me? if (ui) { ui.registerMenuItem('Ride Price Manager', () => { showWindow(); }); } - context.subscribe('interval.day', () => { - RidePrices.updateRidePrices(); - }); + // Only the server/singleplayer automatically triggers prices updates. + if (network.mode !== 'client') { + context.subscribe('interval.day', () => { + RidePrices.updateRidePrices(); + }); - RidePrices.updateRidePrices(); + RidePrices.updateRidePrices(); + } } export default main; From e7380776b3298203d964b4e5ecaa1beb32472896 Mon Sep 17 00:00:00 2001 From: mgovea <1916712+mgovea@users.noreply.github.com> Date: Thu, 1 Apr 2021 17:53:53 -0600 Subject: [PATCH 6/8] bugfix for parks with hidden non-zero admission --- src/RidePrices.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/RidePrices.ts b/src/RidePrices.ts index a8b266f..7823bcf 100644 --- a/src/RidePrices.ts +++ b/src/RidePrices.ts @@ -57,7 +57,7 @@ export default class RidePrices { 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 + const value = park.entranceFee > 0 && !park.getFlag('freeParkEntry') ? Math.floor(ride.value / 4) : ride.value; From d1a19714050ff3da136e0a03aeb3de9d0798b868 Mon Sep 17 00:00:00 2001 From: mgovea <1916712+mgovea@users.noreply.github.com> Date: Thu, 1 Apr 2021 17:54:16 -0600 Subject: [PATCH 7/8] increment version number --- package.json | 2 +- src/registerPlugin.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) 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/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', From 66222e1ea642b5236b281e49903e130fc1e64c92 Mon Sep 17 00:00:00 2001 From: mgovea <1916712+mgovea@users.noreply.github.com> Date: Thu, 1 Apr 2021 17:54:38 -0600 Subject: [PATCH 8/8] update readme! --- README.md | 35 ++++++++++++++++++++++++++++++----- 1 file changed, 30 insertions(+), 5 deletions(-) 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.