Skip to content
Back to skills

Openrct2

ASecurity

Use when developing OpenRCT2 plugins in TypeScript - plugin registration and game actions, window and widget UI, event hooks, park, map, ride and entity access, network API, storage, or ES5 runtime constraints

  • 21 stars
  • 0 votes
  • 0 copies
  • 0 views
  • Added October 2, 2026
developmentjavascripttypescriptgojavanodeexpressdebugginggitapi

Works with

  • cli
  • api

Security analysis

A96/100
  • mediumInstalls packages at runtime which could introduce malicious dependencies

Pro scans all 4 files and shows the line behind each finding

Scanned October 2, 2026

npx -y skills add CodeAtCode/oss-ai-skills --skill openrct2 --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Openrct2?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Openrct2
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/codeatcode-openrct2/badge)](https://www.skillsdirectory.com/skills/codeatcode-openrct2)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
name: openrct2
description: Use when developing OpenRCT2 plugins in TypeScript - plugin registration and game actions, window and widget UI, event hooks, park, map, ride and entity access, network API, storage, or ES5 runtime constraints
metadata:
  author: mte90
  version: 2.0.0
  tags:
    - openrct2
    - plugin
    - javascript
    - typescript
    - game-modding
    - rollercoaster-tycoon
    - scripting
---

# OpenRCT2 Plugin Development

Develop plugins (scripts) for OpenRCT2 using JavaScript/TypeScript to extend the game with custom windows, game actions, event hooks, and multiplayer features.

## Overview

OpenRCT2 plugins run in Duktape (ES5 runtime). Key capabilities:

- **JavaScript/TypeScript** - ES5 compatible, use transpilers for ES6+
- **Game Actions** - Multiplayer-synchronized state mutations
- **UI Windows** - Custom windows with widgets
- **Hooks** - Subscribe to game events
- **Network API** - TCP sockets for localhost communication
- **Hot Reload** - Real-time plugin development

### Plugin Directory

Place `.js` files in the `plugin` directory:

- **Windows**: `C:\Users\YourName\Documents\OpenRCT2\plugin\`
- **Mac**: `/Users/YourName/Library/Application Support/OpenRCT2/plugin/`
- **Linux**: `$XDG_CONFIG_HOME/OpenRCT2/plugin/` or `$HOME/.config/OpenRCT2/plugin/`

Access via game: **Red toolbox button → Open custom content folder**

## Plugin Template

### Basic Plugin Structure

```javascript
function main() {
    console.log("Your plugin has started!");
    // Your plugin code here
}

registerPlugin({
    name: 'Your Plugin',
    version: '1.0',
    authors: ['Your Name'],
    type: 'remote',
    licence: 'MIT',
    targetApiVersion: 34,
    minApiVersion: 10,
    main: main
});
```

### TypeScript Setup

```typescript
// Install TypeScript and types
// npm install typescript --save-dev
// Copy openrct2.d.ts to your project

/// <reference path="openrct2.d.ts" />

function main() {
    console.log("TypeScript plugin loaded!");
}

registerPlugin({
    name: 'My TypeScript Plugin',
    version: '1.0',
    authors: ['Developer'],
    type: 'local',
    licence: 'MIT',
    targetApiVersion: 34,
    main: main
});
```

### tsconfig.json

```json
{
    "compilerOptions": {
        "target": "ES5",
        "module": "none",
        "outFile": "./dist/plugin.js",
        "strict": true,
        "esModuleInterop": true,
        "skipLibCheck": true
    },
    "include": ["src/**/*"],
    "exclude": ["node_modules"]
}
```

## Plugin Types

### Local Plugins

Load on any client with plugin installed. Cannot alter game state directly.

```javascript
registerPlugin({
    name: 'Local Info Plugin',
    version: '1.0',
    type: 'local',  // Available to all players in multiplayer
    main: function() {
        // Can only use game actions, not direct mutations
        // Good for: info windows, tools, dashboards
    }
});
```

### Remote Plugins

Load only on server, distributed to clients. Can mutate game state in execute context.

```javascript
registerPlugin({
    name: 'Remote Game Plugin',
    version: '1.0',
    type: 'remote',  // Server-side, synced to clients
    main: function() {
        // Can mutate game state in custom game action execute()
    }
});
```

### Intransient Plugins

Stay loaded across park changes and in title screen.

```javascript
registerPlugin({
    name: 'Global Plugin',
    version: '1.0',
    type: 'intransient',  // Never unloaded
    main: function() {
        // Active in title screen and across parks
        // Use context.sharedStorage for persistence
    }
});
```

## Game Actions

Game actions are the **recommended way** to mutate game state, ensuring multiplayer synchronization.

### Using Built-in Game Actions

```javascript
var action = {
    type: 'smallsceneryplace',
    args: {
        object: 0,           // Scenery object ID
        x: 32 * 10,          // X coordinate in map units
        y: 32 * 10,          // Y coordinate in map units
        z: 0,                // Z height
        direction: 0,        // Rotation (0-3)
        quadrant: 0,         // Quadrant for quarter tile scenery
        primaryColour: 0,    // Primary color
        secondaryColour: 0   // Secondary color
    }
};

context.executeAction(action, function(result) {
    if (result.error) {
        console.log("Failed to place scenery: " + result.error);
    } else {
        console.log("Scenery placed successfully");
    }
});
```

### Common Built-in Actions

```javascript
// Set park cash
context.executeAction({
    type: 'parksetcash',
    args: { cash: 100000 }
}, callback);

// Set guest count
context.executeAction({
    type: 'parksetguestgenerationrate',
    args: { generationRate: 100 }
}, callback);

// Change land height
context.executeAction({
    type: 'landsetheight',
    args: { x: 32 * 10, y: 32 * 10, height: 10 }
}, callback);

// Build ride
context.executeAction({
    type: 'trackplace',
    args: { ride: 0, trackType: 1, x: 32 * 10, y: 32 * 10, z: 0, direction: 0 }
}, callback);
```

### Custom Game Actions

```javascript
context.registerAction({
    id: 'myplugin.award_cash',
    query: function(args) {
        // Validation - return error object if invalid
        if (args.amount < 0) {
            return { error: 'Amount must be positive' };
        }
        if (args.amount > 100000) {
            return { error: 'Amount too large' };
        }
        return {};  // Success
    },
    execute: function(args) {
        // Actual game state mutation - only runs on server
        park.cash += args.amount;
        return {};  // Success
    }
});

// Use custom action
context.executeAction({
    type: 'myplugin.award_cash',
    args: { amount: 5000 }
}, function(result) {
    console.log(result.error || "Cash awarded!");
});
```

### Permission Checks

```javascript
context.registerAction({
    id: 'myplugin.admin_action',
    query: function(args) {
        // Check player permissions
        if (network.mode !== 'none') {
            var player = network.getPlayer(args.playerId);
            if (!player || !player.hasPermission('modify_park')) {
                return { error: 'No permission' };
            }
        }
        return {};
    },
    execute: function(args) {
        // Perform action
    }
});
```

## Best Practices

### 1. Check UI Availability

```javascript
function main() {
    if (typeof ui !== 'undefined') {
        ui.registerMenuItem('My Window', openWindow);
    }
    context.subscribe('interval.day', onDay);
}
```

### 2. Use Game Actions for Mutations

```javascript
// Good: Use game action
context.executeAction({
    type: 'parksetcash',
    args: { cash: 100000 }
}, callback);

// Bad: Direct mutation in local plugin
// park.cash = 100000;  // Will fail in multiplayer!
```

### 3. Namespace Your Data

Use namespaced keys to avoid conflicts. See `references/network-storage.md` for full storage API details.

### 4. Handle Errors

```javascript
context.executeAction(action, function(result) {
    if (result.error) {
        console.log('Action failed: ' + result.error);
        return;
    }
    // Success handling
});
```

### 5. Clean Up on Unload

```javascript
var intervals = [];

function main() {
    intervals.push(context.setInterval(update, 1000));
}

context.subscribe('map.changed', function() {
    intervals.forEach(clearInterval);
    intervals = [];
});
```

## ES5 Limitations

OpenRCT2 uses Duktape (ES5). ES6+ features require transpilation.

### Not Supported

```javascript
// Arrow functions - NO
var func = () => {};

// Classes - NO
class MyClass {}

// let/const - NO
let x = 1;

// Template literals - NO
`Hello ${name}`

// Spread operator - NO
[...arr]

// Destructuring - NO
var { x } = obj;

// find/includes - NO
arr.find(x => x > 0);
arr.includes(5);
```

### ES5 Alternatives

```javascript
// Function expressions - YES
var func = function() {};

// Constructor functions - YES
function MyClass() {}

// var - YES
var x = 1;

// String concatenation - YES
'Hello ' + name

// Array methods - YES
arr.filter(function(x) { return x > 0; })[0];
arr.indexOf(5) !== -1;
```

## Distribution

### Publishing

1. **GitHub Releases** - Recommended, attach compiled `.js`
2. **openrct2plugins.org** - Community plugin repository

### Versioning

```javascript
registerPlugin({
    name: 'My Plugin',
    version: '1.2.3',  // Semantic versioning
    minApiVersion: 34,  // Minimum OpenRCT2 API version
    targetApiVersion: 34,  // Target API for behavior
});
```

## Deep Dives

Load these reference files on demand for detailed API information:

- **UI Development** - Window creation, widgets (ListView, GroupBox, tabs), events → `references/ui.md`
- **Hooks and Map Access** - Event subscriptions, park info, map tiles, rides, entities → `references/hooks-map.md`
- **Network, Storage, Hot Reload, Debugging** - TCP sockets, storage APIs, development workflow → `references/network-storage.md`

## References

- **OpenRCT2 Scripting Docs**: https://github.com/OpenRCT2/OpenRCT2/blob/develop/distribution/scripting.md
- **API Types**: https://github.com/OpenRCT2/OpenRCT2/blob/develop/distribution/openrct2.d.ts
- **Plugin Samples**: https://github.com/OpenRCT2/plugin-samples
- **Community Plugins**: https://openrct2plugins.org/
- **Duktape Engine**: https://duktape.org/

Files in this skill

  • SKILL.md9.1 KB
  • references/hooks-map.md5 KB
  • references/network-storage.md3.5 KB
  • references/ui.md5.3 KB

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…