diff --git a/.github/workflows/prettier-ci.yml b/.github/workflows/prettier-ci.yml new file mode 100644 index 0000000..8e47972 --- /dev/null +++ b/.github/workflows/prettier-ci.yml @@ -0,0 +1,23 @@ +name: Prettier CI + +on: + pull_request: + +jobs: + prettier: + runs-on: ubuntu-latest + + steps: + - name: Checkout + uses: actions/checkout@v4 + with: + fetch-depth: 0 + ref: ${{ github.event.pull_request.head.ref }} + repository: ${{ github.event.pull_request.head.repo.full_name }} + persist-credentials: false + + - name: Prettify code + uses: creyD/prettier_action@v4.3 + with: + dry: true + prettier_options: --check . diff --git a/.prettierignore b/.prettierignore new file mode 100644 index 0000000..340cdcf --- /dev/null +++ b/.prettierignore @@ -0,0 +1,2 @@ +# Ignore markdown while issue is open: https://github.com/prettier/prettier/issues/5019 +*.md diff --git a/.prettierrc.json b/.prettierrc.json new file mode 100644 index 0000000..fa89313 --- /dev/null +++ b/.prettierrc.json @@ -0,0 +1,5 @@ +{ + "tabWidth": 4, + "printWidth": 100, + "proseWrap": "never" +} diff --git a/README.md b/README.md index 21bdfde..0646214 100644 --- a/README.md +++ b/README.md @@ -1,110 +1,23 @@ -# OpenRCT2 TypeScript Plugin Template +# Bad Thoughts Go BOOM! -A simple template for OpenRCT2 plugins using TypeScript based on [Basssiiie's OpenRCT2-Simple-Typescript-Template](https://github.com/Basssiiie/OpenRCT2-Simple-Typescript-Template) with a few changes to file content and structure. It's primarily designed for my own personal use, but feel free to use it if you like the changes I have made. +It is the Year 1, and guests have been enslaved by a higher being who forces them to visit amusement parks for eternity. It is an age of mandatory fun. -Also supports: -- Automatic plugin reload in OpenRCT2 (hot reload) -- Out of the box minification to improve file sizes -- Support for external NPM packages (like FlexUI) +As such, all guests *must* have fun, both physically and mentally. And if a guest has a single bad thought? The TNT implanted into them by their captors will automatically go off, creating a firey explosion that leaves nothing behind. -## How to start +## Getting the plugin -1. Install latest version of [Node](https://nodejs.org/en/) and make sure to include NPM and enable the "Add to PATH" option during installation. -2. Use the green "Use this template" button in the top right corner of this page, or download the project to a location of your choice on your PC. -3. Open a terminal or command prompt. -4. Use `cd` to change your current directory to the root folder of this project. -5. Run `npm ci` to install the project's dependencies. -6. Place `openrct2.d.ts` in the project's files [as described here](./lib/put-openrct2.d.ts-here.md). -7. In [info.js](./src/info.js), change plugin info, such as name and author, to your liking. +Download the `.js` file from the [latest release](https://github.com/KatieZeldaKat/openrct2-invention-manager/releases/latest) and place it in the "plugin" folder. This can be found by opening OpenRCT2 and selecting "Open custom content folder" under the toolbox in the main menu. -## Dependencies +## Known limitations -The following libraries and tools are used in this template: +- Guests only explode when walking around the park or sitting. + - Caused by [OpenRCT2 #7265](https://github.com/OpenRCT2/OpenRCT2/pull/7265). The PR mentions that other states than walking and sitting can be added if deemed "safe," though nothing has been implemented since. +- It can take quite a while after setting a guest to explode for them to actually do so, and when guests do eventually explode, it's often all at once in a large wave of fire. -- **NodeJS** is the JavaScript engine used to develop and run code when the game is not running. -- **NPM** is a library and package manager for JavasScript and TypeScript and can be used to install new packages and update existing packages in the project. -- **TypeScript** is a expansion language to JavaScript that adds type checking when you are writing the code. It allows you to specify rules for how objects and values look like, so TypeScript can report back if your code follows these rules (instead of crashes or errors in-game). -- **Rollup** bundles all source code, runs it through some plugins like TypeScript, and then outputs a single JavaScript plugin file. -- **Nodemon** is the program that can watch a folder for changes and then trigger a specified action. It is used by `npm start` to watch the `./src/` folder and triggers `npm run build:dev` if any changes occur. +## Potential future additions -## Commands - -Multiple commands are detailed below to help with development. They output files to different directories, which can be changed in `rollup.config.js`. Be sure to not commit any changes you should make to the output paths when collaborating with others. - -### Create release build - -`npm run build` - -This version is optimized for sharing with others, using Terser to make the file as small as possible. By default, the plugin will be outputted to `./dist/`. - -### Create dev build - -`npm run build:dev` - -This version is not optimized for sharing, but easier to read in case you want to see the outputted Javascript. By default, the plugin will be outputted in the plugin folder of the default [OpenRCT2 user directory](#openrct2-user-directory). - -### Run script to automatically create dev builds - -`npm start` or `npm run start` - -Will start a script that will automatically run `npm run build:dev` every time you make a change to any Typescript or Javascript file inside the `./src/` folder. - ---- - -## Access game logs - -When your plugin is not loading properly, it may be useful to be able to read the logs of the game to see if there are any errors. Furthermore, if you use the `console.log` function, the resulting logs can be read here as well. - -### Windows - -1. Navigate to the folder where [OpenRCT2 is installed](#openrct2-installation-directory). -2. Launch the `openrct2.com` file located there (the MS-DOS application). - - - If file extensions are hidden, make sure to [enable them](https://support.microsoft.com/en-us/windows/common-file-name-extensions-in-windows-da4a4430-8e76-89c5-59f7-1cdbbc75cb01). - -### MacOS - -1. Launch a terminal or another command-line prompt. -2. Using the `cd` command, navigate to the folder where [OpenRCT2 is installed](#openrct2-installation-directory). -3. Run `open OpenRCT2.app/Contents/MacOS/OpenRCT2` to launch OpenRCT2 with logging enabled. - -### Linux - -1. Launch a terminal or another command-line prompt. -2. Using the `cd` command, navigate to the folder where [OpenRCT2 is installed](#openrct2-installation-directory). -3. Run `./openrct2` to launch OpenRCT2 with logging enabled. - ---- - -## Hot reload - -This project supports the [OpenRCT2 hot reload feature](https://github.com/OpenRCT2/OpenRCT2/blob/master/distribution/scripting.md#writing-scripts) for development. - -1. Navigate to your [OpenRCT2 user directory](#openrct2-user-directory) and open the `config.ini` file. -2. Enable hot reload by setting `enable_hot_reloading = true` in the file. -3. Run `npm start` in the directory of this project to start the hot reload server. -4. Start the OpenRCT2 and load a save or start a new game. -5. Each time you save any of the files in `./src/`, the server will compile `./src/registerPlugin.ts` and place compiled plugin file inside your local OpenRCT2 plugin directory. -6. OpenRCT2 will notice file changes and it will reload the plugin. - ---- - -## Folders - -### OpenRCT2 installation directory - -This is the directory where the game is installed. - -- **Windows:** usually `C:/Users//Documents/OpenRCT2/bin/` when using the launcher or `C:/Program Files/OpenRCT2/` when an installer was used. -- **MacOS:** the folder where the `OpenRCT2.app` application file was placed. -- **Linux:** depends on the distro, but likely either `/usr/share/openrct2` when installed through a package manager, or mounted in `/tmp` when using an AppImage. - -### OpenRCT2 user directory - -This is the directory where the game stores user data, like save games and plugins. - -- **Windows:** usually `Documents/OpenRCT2/` or `C:/Users//Documents/OpenRCT2/`. -- **MacOS:** usually `/Users//Library/Application Support/OpenRCT2/`. Note that `Library` is a hidden folder in your user directory, so by default it will not show up in Finder. -- **Linux:** usually `/home//.config`, `$HOME/.config`, or where the environment variable `$XDG_CONFIG_HOME` points to if it's set. - -You can also open this folder from inside OpenRCT2, by selecting "Open custom content folder" in the dropdown under the red toolbox in the main menu. +- Option to disable the plugin in-game +- Change frequency at which guests are queried for their thoughts +- Make exploded guests affect the park rating (and option for how much) +- Ability for players to set what counts as a bad thought +- A dashboard to track the frequency of bad thoughts diff --git a/docs/CONTRIBUTING.md b/docs/CONTRIBUTING.md new file mode 100644 index 0000000..b09fca3 --- /dev/null +++ b/docs/CONTRIBUTING.md @@ -0,0 +1,115 @@ +# Contributing to this plugin + +Feel free to contribute through issues and/or pull requests. I will try my best to address them in a timely manner, though I do not promise any indefinite support. + +The instructions below are taken from [Basssiiie's Typescript Template](https://github.com/Basssiiie/OpenRCT2-Simple-Typescript-Template), though the template is slightly modified from the original. + +## How to start + +1. Install dependencies using `npm` +2. Clone this repository to your computer through git. +3. Open a terminal or command prompt. +4. Use `cd` to change your current directory to the root folder of this project. +5. Run `npm ci` to install the project's dependencies. +6. Place `openrct2.d.ts` in the project's files [as described here](./lib/put-openrct2.d.ts-here.md). + +--- + +## Dependencies + +The following libraries and tools are used in this template: + +- **NodeJS** is the JavaScript engine used to develop and run code when the game is not running. +- **NPM** is a library and package manager for JavasScript and TypeScript and can be used to install new packages and update existing packages in the project. +- **TypeScript** is a expansion language to JavaScript that adds type checking when you are writing the code. It allows you to specify rules for how objects and values look like, so TypeScript can report back if your code follows these rules (instead of crashes or errors in-game). +- **Rollup** bundles all source code, runs it through some plugins like TypeScript, and then outputs a single JavaScript plugin file. +- **Nodemon** is the program that can watch a folder for changes and then trigger a specified action. It is used by `npm start` to watch the `./src/` folder and triggers `npm run build:dev` if any changes occur. +- **Prettier** is the code formatter used to automatically clean up code to a specific format. This lets the entire repository be consistent and easier to maintain. Any code must be ran through this before it is approved to go into the repository. + +## Commands + +Multiple commands are detailed below to help with development. They output files to different directories, which can be changed in `rollup.config.js`. Be sure to not commit any changes you should make to the output paths when collaborating with others. + +### Create release build + +`npm run build` + +This version is optimized for sharing with others, using Terser to make the file as small as possible. By default, the plugin will be outputted to `./dist/`. + +### Create dev build + +`npm run build:dev` + +This version is not optimized for sharing, but easier to read in case you want to see the outputted Javascript. By default, the plugin will be outputted in the plugin folder of the default [OpenRCT2 user directory](#openrct2-user-directory). + +### Run script to automatically create dev builds + +`npm start` or `npm run start` + +Will start a script that will automatically run `npm run build:dev` every time you make a change to any Typescript or Javascript file inside the `./src/` folder. + +### Format code using Prettier + +`npx prettier --write .` + +Formats all files in accordance with [Prettier](https://prettier.io/). Alternatively, you could use an extension compatable with your code editor. Just ensure that it is pointing to the configuration file (`.prettierrc.json`) and the ignore file (`.prettierignore`). + +--- + +## Access game logs + +When your plugin is not loading properly, it may be useful to be able to read the logs of the game to see if there are any errors. Furthermore, if you use the `console.log` function, the resulting logs can be read here as well. + +### Windows + +1. Navigate to the folder where [OpenRCT2 is installed](#openrct2-installation-directory). +2. Launch the `openrct2.com` file located there (the MS-DOS application). + + - If file extensions are hidden, make sure to [enable them](https://support.microsoft.com/en-us/windows/common-file-name-extensions-in-windows-da4a4430-8e76-89c5-59f7-1cdbbc75cb01). + +### MacOS + +1. Launch a terminal or another command-line prompt. +2. Using the `cd` command, navigate to the folder where [OpenRCT2 is installed](#openrct2-installation-directory). +3. Run `open OpenRCT2.app/Contents/MacOS/OpenRCT2` to launch OpenRCT2 with logging enabled. + +### Linux + +1. Launch a terminal or another command-line prompt. +2. Using the `cd` command, navigate to the folder where [OpenRCT2 is installed](#openrct2-installation-directory). +3. Run `./openrct2` to launch OpenRCT2 with logging enabled. + +--- + +## Hot reload + +This project supports the [OpenRCT2 hot reload feature](https://github.com/OpenRCT2/OpenRCT2/blob/master/distribution/scripting.md#writing-scripts) for development. + +1. Navigate to your [OpenRCT2 user directory](#openrct2-user-directory) and open the `config.ini` file. +2. Enable hot reload by setting `enable_hot_reloading = true` in the file. +3. Run `npm start` in the directory of this project to start the hot reload server. +4. Start the OpenRCT2 and load a save or start a new game. +5. Each time you save any of the files in `./src/`, the server will compile `./src/registerPlugin.ts` and place compiled plugin file inside your local OpenRCT2 plugin directory. +6. OpenRCT2 will notice file changes and it will reload the plugin. + +--- + +## Folders + +### OpenRCT2 installation directory + +This is the directory where the game is installed. + +- **Windows:** usually `C:/Users//Documents/OpenRCT2/bin/` when using the launcher or `C:/Program Files/OpenRCT2/` when an installer was used. +- **MacOS:** the folder where the `OpenRCT2.app` application file was placed. +- **Linux:** depends on the distro, but likely either `/usr/share/openrct2` when installed through a package manager, or mounted in `/tmp` when using an AppImage. + +### OpenRCT2 user directory + +This is the directory where the game stores user data, like save games and plugins. + +- **Windows:** usually `Documents/OpenRCT2/` or `C:/Users//Documents/OpenRCT2/`. +- **MacOS:** usually `/Users//Library/Application Support/OpenRCT2/`. Note that `Library` is a hidden folder in your user directory, so by default it will not show up in Finder. +- **Linux:** usually `/home//.config`, `$HOME/.config`, or where the environment variable `$XDG_CONFIG_HOME` points to if it's set. + +You can also open this folder from inside OpenRCT2, by selecting "Open custom content folder" in the dropdown under the red toolbox in the main menu. diff --git a/package-lock.json b/package-lock.json index d45ddc3..c28659d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -13,6 +13,7 @@ "@rollup/plugin-terser": "^0.4.0", "@rollup/plugin-typescript": "^11.0.0", "nodemon": "^3.0.1", + "prettier": "3.2.5", "rollup": "^3.15.0", "tslib": "^2.5.0" } @@ -580,6 +581,21 @@ "url": "https://github.com/sponsors/jonschlinkert" } }, + "node_modules/prettier": { + "version": "3.2.5", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.2.5.tgz", + "integrity": "sha512-3/GWa9aOC0YeD7LUfvOG2NiDyhOWRvt1k+rcKhOuYnMY24iiCphgneUfJDyFXd6rZCAnuLBv6UeAULtrhT/F4A==", + "dev": true, + "bin": { + "prettier": "bin/prettier.cjs" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/prettier/prettier?sponsor=1" + } + }, "node_modules/pstree.remy": { "version": "1.1.8", "resolved": "https://registry.npmjs.org/pstree.remy/-/pstree.remy-1.1.8.tgz", diff --git a/package.json b/package.json index cf772f0..1f2c042 100644 --- a/package.json +++ b/package.json @@ -13,6 +13,7 @@ "@rollup/plugin-terser": "^0.4.0", "@rollup/plugin-typescript": "^11.0.0", "nodemon": "^3.0.1", + "prettier": "3.2.5", "rollup": "^3.15.0", "tslib": "^2.5.0" } diff --git a/src/data/guests.ts b/src/data/guests.ts new file mode 100644 index 0000000..be78e28 --- /dev/null +++ b/src/data/guests.ts @@ -0,0 +1,56 @@ +import { thoughtIsNegative } from "./thoughts"; + +const tickFrequency = 256; + +const queueAdd: number[] = []; +const queueRemove: number[] = []; + +let guests: number[] = []; +let queueExplode: number[] = []; + +export function initialize() { + guests = map + .getAllEntities("guest") + .filter((guest) => guest.id !== null) + .map((guest) => guest.id as number); + + context.subscribe("guest.generation", (e) => queueAdd.push(e.id)); + context.subscribe("interval.tick", () => processGuests()); +} + +function processGuests() { + const tick = date.ticksElapsed % tickFrequency; + if (tick === 0) { + updateQueues(); + } + + for (let index = tick; index < guests.length; index += tickFrequency) { + processGuest(guests[index]); + } +} + +function updateQueues() { + queueAdd.forEach((id) => guests.push(id)); + queueRemove.forEach((id) => guests.splice(guests.indexOf(id), 1)); + + // Clear queues after processing + queueExplode = queueExplode.filter((id) => queueRemove.indexOf(id) >= 0); + queueAdd.splice(0, queueAdd.length); + queueRemove.splice(0, queueRemove.length); +} + +function processGuest(id: number) { + const guest = map.getEntity(id) as Guest; + if (guest === null) { + queueRemove.push(id); + } else if (queueExplode.indexOf(id) >= 0) { + guest.setFlag("explode", true); + } else if (guest.thoughts !== undefined) { + guest.thoughts.forEach((thought) => { + if (thoughtIsNegative(thought.type)) { + queueExplode.push(id); + guest.setFlag("explode", true); + } + }); + } +} diff --git a/src/data/thoughts.ts b/src/data/thoughts.ts new file mode 100644 index 0000000..8aac309 --- /dev/null +++ b/src/data/thoughts.ts @@ -0,0 +1,148 @@ +/** + * Defines whether a thought is good, bad, or neutral: + * true = good + * false = bad + * null = neutral + * + * This is all subjective, so perhaps in the future, I can create a settings screen where + * a player can categorize the thoughts themselves. + */ +const connotation: { [thought in ThoughtType]: boolean | null } = { + cant_afford_ride: false, + spent_money: false, + sick: false, + very_sick: false, + more_thrilling: null, + intense: null, + havent_finished: null, + sickening: false, + bad_value: false, + go_home: false, + good_value: true, + already_got: null, + cant_afford_item: false, + not_hungry: null, + not_thirsty: null, + drowning: false, + lost: false, + was_great: true, + queuing_ages: false, + tired: false, + hungry: null, + thirsty: null, + toilet: null, + cant_find: false, + not_paying: false, + not_while_raining: null, + bad_litter: false, + cant_find_exit: false, + get_off: false, + get_out: false, + not_safe: false, + path_disgusting: false, + crowded: false, + vandalism: false, + scenery: true, + very_clean: true, + fountains: true, + music: true, + balloon: true, + toy: true, + map: true, + photo: true, + umbrella: true, + drink: true, + burger: true, + chips: true, + ice_cream: true, + candyfloss: true, + pizza: true, + popcorn: true, + hot_dog: true, + tentacle: true, + hat: true, + toffee_apple: true, + tshirt: true, + doughnut: true, + coffee: true, + chicken: true, + lemonade: true, + wow: true, + wow2: true, + watched: null, + balloon_much: false, + toy_much: false, + map_much: false, + photo_much: false, + umbrella_much: false, + drink_much: false, + burger_much: false, + chips_much: false, + ice_cream_much: false, + candyfloss_much: false, + pizza_much: false, + popcorn_much: false, + hot_dog_much: false, + tentacle_much: false, + hat_much: false, + toffee_apple_much: false, + tshirt_much: false, + doughnut_much: false, + coffee_much: false, + chicken_much: false, + lemonade_much: false, + photo2: true, + photo3: true, + photo4: true, + pretzel: true, + hot_chocolate: true, + iced_tea: true, + funnel_cake: true, + sunglasses: true, + beef_noodles: true, + fried_rice_noodles: true, + wonton_soup: true, + meatball_soup: true, + fruit_juice: true, + soybean_milk: true, + sujongkwa: true, + sub_sandwich: true, + cookie: true, + roast_sausage: true, + photo2_much: false, + photo3_much: false, + photo4_much: false, + pretzel_much: false, + hot_chocolate_much: false, + iced_tea_much: false, + funnel_cake_much: false, + sunglasses_much: false, + beef_noodles_much: false, + fried_rice_noodles_much: false, + wonton_soup_much: false, + meatball_soup_much: false, + fruit_juice_much: false, + soybean_milk_much: false, + sujongkwa_much: false, + sub_sandwich_much: false, + cookie_much: false, + roast_sausage_much: false, + help: false, + running_out: null, + new_ride: true, + nice_ride_deprecated: null, + excited_deprecated: true, + here_we_are: null, +}; + +export function thoughtIsPositive(type: ThoughtType): boolean { + return connotation[type] === true; +} + +export function thoughtIsNegative(type: ThoughtType): boolean { + return connotation[type] === false; +} + +export function thoughtIsNeutral(type: ThoughtType): boolean { + return connotation[type] === null; +} diff --git a/src/info.js b/src/info.js index 81b9eef..ed8a6d7 100644 --- a/src/info.js +++ b/src/info.js @@ -1,8 +1,8 @@ -export const name = "name-of-your-plugin"; -export const authors = ["Your name"]; +export const name = "bad-thoughts-go-boom"; +export const authors = ["Katherine Norton (KatieZeldaKat)"]; export const license = "MIT"; -export const version = "1.0.0"; +export const version = "0.1.0"; export const type = "remote"; /** diff --git a/src/startup.ts b/src/startup.ts index e4556b4..0970138 100644 --- a/src/startup.ts +++ b/src/startup.ts @@ -1,17 +1,5 @@ -// @ts-ignore -import * as info from "./info.js"; +import * as guests from "./data/guests"; export function startup() { - // Write code here that should happen on startup of the plugin. - console.log("Hello world!"); - - // Register a menu item under the map icon: - if (typeof ui !== "undefined") { - ui.registerMenuItem(info.name, () => onClickMenuItem()); - } -} - -function onClickMenuItem() { - // Write code here that should happen when the player clicks the menu item under the map icon. - console.log("Clicked menu item"); + guests.initialize(); }