Skip to content

Commit

Permalink
feat(0.1.2): add cache, plugin window and clean up
Browse files Browse the repository at this point in the history
  • Loading branch information
Rolf Oldenkotte committed Jul 7, 2024
1 parent 5f42119 commit f76afdb
Show file tree
Hide file tree
Showing 12 changed files with 308 additions and 78 deletions.
3 changes: 2 additions & 1 deletion eslint.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,8 @@ export default [
"@typescript-eslint/no-inferrable-types": "off",
"@typescript-eslint/semi": ["error", "never"],
"@typescript-eslint/triple-slash-reference": "off",
"max-len": ["error", { code: 80, tabWidth: 4 }],
"max-len": ["error", { code: 100, tabWidth: 4 }],
"constructor-super": 0,
"prettier/prettier": 2
},
files: ["**/*.ts", "**/*.js"]
Expand Down
5 changes: 4 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "openrct2-name-generator",
"version": "0.1",
"version": "0.1.2",
"main": "app.js",
"license": "MIT",
"author": "rolfhermancoen",
Expand Down Expand Up @@ -52,5 +52,8 @@
"./tests/_setup.cjs"
],
"verbose": true
},
"dependencies": {
"openrct2-flexui": "^0.1.0-prerelease.0"
}
}
27 changes: 0 additions & 27 deletions src/dictionary/filter.ts

This file was deleted.

82 changes: 82 additions & 0 deletions src/enum/windowClass.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
export enum WindowClass {
MainWindow = 0,
TopToolbar = 1,
BottomToolbar = 2,
Tooltip = 5,
Dropdown = 6,
About = 8,
Error = 11,
Ride = 12,
RideConstruction = 13,
SavePrompt = 14,
RideList = 15,
ConstructRide = 16,
DemolishRidePrompt = 17,
Scenery = 18,
Options = 19,
Footpath = 20,
Land = 21,
Water = 22,
Peep = 23,
GuestList = 24,
StaffList = 25,
FirePrompt = 26,
ParkInformation = 27,
Finances = 28,
TitleMenu = 29,
TitleExit = 30,
RecentNews = 31,
ScenarioSelect = 32,
TrackDesignList = 33,
TrackDesignPlace = 34,
NewCampaign = 35,
KeyboardShortcutList = 36,
ChangeKeyboardShortcut = 37,
Map = 38,
TitleLogo = 39,
Banner = 40,
MapTooltip = 41,
EditorObjectSelection = 42,
EditorInventionList = 43,
EditorInventionListDrag = 44,
EditorScenarioOptions = 45,
EditorObjectiveOptions = 46,
ManageTrackDesign = 47,
TrackDeletePrompt = 48,
InstallTrack = 49,
ClearScenery = 50,
SceneryScatter = 51,
NotificationOptions = 109,
Cheats = 110,
Research = 111,
Viewport = 112,
Textinput = 113,
Mapgen = 114,
Loadsave = 115,
LoadsaveOverwritePrompt = 116,
TitleOptions = 117,
LandRights = 118,
Themes = 119,
TileInspector = 120,
Changelog = 121,
Multiplayer = 124,
Player = 125,
NetworkStatus = 126,
ServerList = 127,
ServerStart = 128,
CustomCurrencyConfig = 129,
DebugPaint = 130,
ViewClipping = 131,
ObjectLoadError = 132,
PatrolArea = 133,
Transparency = 134,
AssetPacks = 135,
ResetShortcutKeysPrompt = 136,
Staff = 220,
EditorTrackBottomToolbar = 221,
EditorScenarioBottomToolbar = 222,
Chat = 223,
Console = 224,
Custom = 225,
Null = 255
}
26 changes: 26 additions & 0 deletions src/filter.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import { RideType } from "./enum/rideType"
import { VehicleType } from "./enum/vehicleType"
import { NameSet } from "./generator"

export const filterNameSet = (
sets: NameSet[],
rideType?: RideType,
vehicleType?: VehicleType
) => {
const matchesRideType = (set: NameSet) => {
return (
rideType === undefined ||
!set.ride ||
set.ride?.some((t) => t.toString() === rideType.toString())
)
}

const matchesVehicleType = (set: NameSet) => {
return (
vehicleType === undefined ||
!set.vehicle ||
set.vehicle?.some((t) => t.toString() === vehicleType.toString())
)
}
return sets.filter((set) => matchesRideType(set) && matchesVehicleType(set))
}
77 changes: 62 additions & 15 deletions src/generator.ts
Original file line number Diff line number Diff line change
@@ -1,32 +1,79 @@
import adjectives from "./dictionary/adjective"
import { filterNameSet } from "./dictionary/filter"
import { filterNameSet } from "./filter"
import nouns from "./dictionary/nouns"
import suffix from "./dictionary/suffix"
import suffixes from "./dictionary/suffix"
import { RideType } from "./enum/rideType"
import { VehicleType } from "./enum/vehicleType"

// Define the NameSet type
export type NameSet = {
value: string
ride?: readonly RideType[]
vehicle?: readonly VehicleType[]
ride?: RideType[]
vehicle?: VehicleType[]
}

const getRand = (array: readonly NameSet[], emptyChange = 0) => {
if (Math.random() < emptyChange) {
// Type for the memoization cache key
type CacheKey =
`${"adjective" | "noun" | "suffix"}-${RideType | "none"}-${VehicleType | "none"}`

// Configuration object for empty chance values
const EMPTY_CHANCE_CONFIG = {
adjective: 0.1,
suffix: 0.8
}

// Utility function to get a random element from an array
const getRandomElement = (
array: NameSet[],
emptyChance = 0
): NameSet | null => {
if (Math.random() < emptyChance) {
return null
}

if (array.length === 0) {
return null
}
return array[Math.floor(Math.random() * array.length)]

const randomIndex = Math.floor(Math.random() * array.length)
return array[randomIndex] ?? null
}

// Memoize the filterNameSet results to improve performance
const memoizeFilterNameSet = (() => {
const cache: Record<CacheKey, NameSet[]> = {} as Record<CacheKey, NameSet[]>

return (
source: NameSet[],
type: "adjective" | "noun" | "suffix",
rideType?: RideType,
vehicleType?: VehicleType
): NameSet[] => {
const key: CacheKey = `${type}-${rideType ?? "none"}-${vehicleType ?? "none"}`

if (!cache[key]) {
cache[key] = filterNameSet(source, rideType, vehicleType)
}
return cache[key] || []
}
})()

// Generate a name based on optional rideType and vehicleType
export const generateName = (
rideType?: RideType,
vehicleType?: VehicleType
) => {
return [
getRand(filterNameSet(adjectives, rideType, vehicleType), 0.1)?.value,
getRand(filterNameSet(nouns, rideType, vehicleType))?.value,
getRand(filterNameSet(suffix, rideType, vehicleType), 0.8)?.value
]
.filter((val) => val !== undefined)
.join(" ")
): string => {
const adjective = getRandomElement(
memoizeFilterNameSet(adjectives, "adjective", rideType, vehicleType),
EMPTY_CHANCE_CONFIG.adjective
)?.value
const noun = getRandomElement(
memoizeFilterNameSet(nouns, "noun", rideType, vehicleType)
)?.value
const suffix = getRandomElement(
memoizeFilterNameSet(suffixes, "suffix", rideType, vehicleType),
EMPTY_CHANCE_CONFIG.suffix
)?.value

return [adjective, noun, suffix].filter(Boolean).join(" ")
}
104 changes: 89 additions & 15 deletions src/main.ts
Original file line number Diff line number Diff line change
@@ -1,25 +1,99 @@
import { RideType } from "./enum/rideType"
import { VehicleType } from "./enum/vehicleType"
import { generateName } from "./generator"
import { WindowClass } from "./enum/windowClass"
import { mainWindow } from "./ui/mainWindow"

const hasPremadeTrackDesignWindowOpen = () => {
for (let index = 0; index < ui.windows; index++) {
const w = ui.getWindow(index)

if (
w.classification == WindowClass.TrackDesignList ||
w.classification == WindowClass.TrackDesignPlace
) {
return true
}
}
return false
}

const getAllExistingRideNames = () => {
const rideNames = []
for (let i = 0; i <= map.numRides; i++) {
const ride = map.getRide(i)
if (ride) {
rideNames.push(ride.name)
}
}
return rideNames
}

const setRideName = (rideType: RideType, ride: number) => {
if (hasPremadeTrackDesignWindowOpen()) {
return
}

const existingNames = getAllExistingRideNames()

let foundName = null
while (!foundName) {
const name = generateName(rideType)

if (existingNames.every((n) => n !== name)) {
foundName = name
}
}

context.executeAction(
"ridesetname",
{ ride, name: foundName },
({ error, errorMessage, errorTitle }) => {
if (error) {
console.log(`${errorTitle}: ${errorMessage}`)
} else {
console.log(`Successfully named rideId ${ride} to ${foundName}`)
}
}
)
}

const openPluginWindow = () => {
// Check if game is up-to-date...
const version = context.apiVersion
if (version < 75) {
// 75 => https://github.com/OpenRCT2/OpenRCT2/pull/19305
showUpdateError(
"The version of OpenRCT2 you are currently playing is too old for this plugin."
)
return
}

// Show the current instance if one is active.
mainWindow.open()
}

/**
* Report to the player that they need to update the game,
* both ingame and in console.
*/
function showUpdateError(message: string): void {
const title = "Please update the game! "

ui.showError(title, message)
console.log("[NameGenerator] " + title + message)
}

export function main() {
// Register a menu item under the map icon:
// if (typeof ui !== "undefined") {
// ui.registerMenuItem("NameGenerator", () => onClickMenuItem())
// }
if (typeof ui !== "undefined") {
ui.registerMenuItem("NameGenerator", () => openPluginWindow())
}
context.subscribe("action.execute", (event) => {
switch (event.action) {
case "ridecreate": {
if ("rideObject" in event.args && "rideType" in event.args) {
const object = objectManager.getObject(
"ride",
event.args.rideObject as number
)
console.log(
generateName(
event.args.rideType as RideType,
object.identifier as VehicleType
)
if ("rideType" in event.args && "ride" in event.result) {
setRideName(
event.args.rideType as RideType,
event.result.ride as number
)
}
break
Expand Down
Loading

0 comments on commit f76afdb

Please sign in to comment.