diff --git a/.gitignore b/.gitignore index 7ac5f3c..b95925d 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,4 @@ -.vscode -/*.js -js/ +.vscode/ node_modules/ docs/ +dist/ diff --git a/NODE.md b/NODE.md index 992e419..9b8d37e 100644 --- a/NODE.md +++ b/NODE.md @@ -8,6 +8,8 @@ Node.js is a program that lets you run JavaScript files locally. You can downloa This library has only been tested using version 21 of Node, so any version after that is recommended, but it should work fine on v20 and possibly older versions as well. +When installing it, it will ask if you want to also install various tools for native modules (Python 2, VS Build Tools, Boxstarter, Chocolatey). If you don't know whether you should tick the checkbox or not: It depends on what you want to do with Node. This library does not require any of this, so feel free to leave it unticked if that's all you want to use it for. If you want to use it for other things, it might be a good idea to tick it just in case you'll need a package that requires it at some point. + ## npm Node Package Manager (npm) is a separate program that is installed along with Node. It lets you easily install and manage dependencies for your project. @@ -44,7 +46,7 @@ To test that you have done everything correctly, let's try to make a very simple Save this code in a file called `example.js` inside the project folder: ```js import fs from 'node:fs/promises' -import { FXR, BasicNode, BillboardEx } from '@cccode/fxr' +import { FXR, BasicNode, BillboardEx, Game } from '@cccode/fxr' const fxr = new FXR(300) @@ -56,7 +58,7 @@ fxr.root.nodes = [ ]) ] -await fs.writeFile('example.fxr', Buffer.from(fxr.toArrayBuffer())) +await fs.writeFile('example.fxr', Buffer.from(fxr.toArrayBuffer(Game.EldenRing))) ``` This creates a new FXR file with the ID 300 and sets up a single red square as the effect. It then uses the Node file system module to write the file to the folder as `example.fxr`. @@ -128,7 +130,7 @@ If you get this warning when trying to run a JS file, you're running it as a Com ### "SyntaxError: Cannot use import statement outside a module" If you get this error when trying to run a JS file, you're running it as a CommonJS script instead of an ES module. To fix that, check out [the section about modules](#modules) above. -### Tools for Native Modules / Python 2 / VS Build Tools / Boxstater / Chocolatey +### Tools for Native Modules / Python 2 / VS Build Tools / Boxstarter / Chocolatey If you see a page in the Node installer mentioning this stuff and you don't know whether to tick the checkbox or not, it depends on what you are going to do with Node. If you are only going to use this library and nothing else, feel free to leave the checkbox unticked, the library doesn't require anything from it. If you plan to use other packages from NPM, it might be a good idea to tick it, since some of them might require it for compiling during installation. ### I have Node.js installed already, but I don't know what version it is diff --git a/README.md b/README.md index 3c050ab..7d620be 100644 --- a/README.md +++ b/README.md @@ -1,46 +1,44 @@ # FXR -This is a JavaScript library for creating and editing FXR files for Dark Souls 3, Sekiro, Elden Ring, and Armored Core 6. It does not require any dependencies and works in both the browser and in Node. +This is a JavaScript library for creating and editing FXR files (particle effects, lights, etc.) for Dark Souls 3, Sekiro, Elden Ring, and Armored Core 6. It does not require any dependencies, and works in both the browser and in Node. -While it does support all of those games, its focus is on Elden Ring and some utility features may not work for the other games right now. +It includes classes for most known structures in the file format that makes it relatively very easy to create brand new effects from scratch. It also has functions that allow you to modify existing effects in different ways: scaling, recoloring, converting between games, and more. ## Installation -While the library does not have any dependencies, it *does* have some dev dependencies for compiling TypeScript and generating docs. To avoid installing those, include `--omit=dev` when running the installation command: +The library is available on [npm](https://www.npmjs.com/package/@cccode/fxr), so you can use a package manager like npm, yarn or pnpm to install it. + +While the library does not have any dependencies, it *does* have some dev dependencies for compiling TypeScript and generating docs. To avoid installing those with npm, include `--omit=dev` when running the installation command: ``` npm i @cccode/fxr --omit=dev ``` If you are new to using Node.js and want to use the library locally, check out [the Node.js guide here](https://github.com/EvenTorset/fxr/blob/main/NODE.md). -## WitchyBND -While it has evolved quite a bit, this library started out as a JS port of [WitchyBND's C# FXR class](https://github.com/ividyon/WitchyBND/blob/main/WitchyFormats/Formats/RSFXR.cs). Most of the code for reading and writing the format is based on that. - -## Unknowns & to-dos -The FXR format is still being reverse-engineered, so there are a lot of unknown fields and properties everywhere, as well as some placeholder names (like Section10s) for things. This library does not hide anything that is still unknown, you are able to set any unknown value to whatever you want, allowing you to use it for your own research. If you discover something that is not documented by this library or the [ER FXR sheet](https://docs.google.com/spreadsheets/d/12hKQg5kBvOJ_M0Udoz5GqS_2RX-d8YtaBapwpSJ2Csg/edit#gid=1424830463), please make a comment on the sheet or a pull request to this repo. - -In addition to the unknowns, there are lots of known things that are not yet implemented in this library. The goal is to eventually have specialized subclasses for every action type, property function, and modifier type. That would make it much easier to work with FXR files. The actions that have specialized subclasses so far have been tested pretty thoroughly to make sure that everything is correct (in Elden Ring). - ## Documentation -Check out the auto-generated docs at https://fxr-docs.pages.dev/ to find more information about the specialized action classes and other things. +Check out the auto-generated docs at https://fxr-docs.pages.dev/ to find more information about the specialized action classes and other things. Also check out the [example directory](https://github.com/EvenTorset/fxr/tree/main/examples) in the GitHub repo, it includes examples for creating new effects as well as editing existing effects in various ways. ## Editing FXR files -To edit existing FXR files, all you need is an ArrayBuffer with the file's content. The example below is written for Node, but replacing how you get the ArrayBuffer and what you do with the output it should also work fine in the browser. +To edit existing FXR files, all you need is an ArrayBuffer or typed array with the file's content. The example below is written for Node, but by replacing how you get the buffer and what you do with the output it should also work fine in the browser. ```js import fs from 'node:fs/promises' -import { FXR } from '@cccode/fxr' +import { FXR, Game } from '@cccode/fxr' -// Get an ArrayBuffer of the file's content -const { buffer } = await fs.readFile('f000450360.fxr') +// Get an buffer of the original file's content +const buffer = await fs.readFile('f000450360.fxr') // Parse file -const fxr = FXR.read(buffer) +const fxr = FXR.read(buffer, Game.EldenRing) // Make changes to the FXR, for example scaling it so it's half as big: fxr.root.scale(0.5) -// Let's also recolor this effect. This function takes a function that remaps -// colors passed to it. You can use any remapping method you like, but here is -// one that works pretty well in most cases and only requires small tweaks -// depending on the FXR it's used with and what your target color is. +// Let's also recolor this effect. This is the color we want to change this to, +// a red-ish orange. +const targetColor = [1, 0.3, 0] + +// This function takes a function that remaps colors passed to it. You can use +// any remapping method you like, but here is one that works pretty well in +// most cases and only requires small tweaks depending on the FXR it's used +// with and what your target color is. fxr.root.recolor(([r, g, b, a]) => { // Colors in FXRs are allowed to go above 1, and it usually just adds a bloom // effect to the particles. We need to scale it back down to the 0-1 range @@ -50,20 +48,13 @@ fxr.root.recolor(([r, g, b, a]) => { g /= scale b /= scale - // Calculate saturation - It's better to use chroma from the Oklch space, but - // this is just an example, so using HSV "saturation" since it's simple. + // Calculate HSV value and saturation const min = Math.min(r, g, b) const max = Math.max(r, g, b) let s = max > 0 ? (max - min) / max : 0 - // This FXR (450360) is a pretty pale purple, so double the saturation value - // here so we get some more color out of it. Must be capped at 1. - s = Math.min(s * 2, 1) - - // The color we want to change this to, a red-ish orange. - const targetColor = [1, 0.3, 0] - - // Linear interpolation between the HSV "value" (max) and the target color. + // Linear interpolation between the HSV "value" (max) and the target color + // based on the saturation of the original color. r = max * (1 - s) + targetColor[0] * s g = max * (1 - s) + targetColor[1] * s b = max * (1 - s) + targetColor[2] * s @@ -77,8 +68,13 @@ fxr.root.recolor(([r, g, b, a]) => { return [r, g, b, a] }) +// Generate an ArrayBuffer of the modified file content. Note that you may +// change the game to output this for to any of the four supported games, as +// long as the game supports all of the features used in the effect. +const output = fxr.toArrayBuffer(Game.EldenRing) + // Write the modified file -await fs.writeFile('f000450360_edit.fxr', Buffer.from(fxr.toArrayBuffer())) +await fs.writeFile('f000450360_edit.fxr', Buffer.from(output)) ``` ## Creating new FXR files Creating brand new FXR files from scratch requires some knowledge about their structure, but below is an example to get started. The example creates lots of thin rectangular particles that change color over time in a cylindrical volume @@ -98,6 +94,7 @@ import { BlendMode, LinearProperty, Keyframe, + Game, } from '@cccode/fxr' // Let's make a replacement for the ghostflame torch effect in Elden Ring. @@ -128,12 +125,11 @@ fxr.root.nodes = [ // better for readability and they are a lot easier to create. // This is equivalent to the action created above: - new NodeTransform({ - translateY: 0.5 - }), + new NodeTransform({ translateY: 0.5 }), - new PeriodicEmitter(0.1, 10, -1), // Action 300, emitter - new CylinderEmitterShape(true, 0.2, 1, true), // Action 405, emitter shape + + new PeriodicEmitter({ interval: 0.1, perInterval: 10 }), // Action 300 + new CylinderEmitterShape({ radius: 0.2 }), // Action 405 new ParticleAttributes({ duration: 1 }), // Action 129 new BillboardEx({ // Action 603 blendMode: BlendMode.Normal, @@ -146,19 +142,23 @@ fxr.root.nodes = [ new Keyframe(1, [1, 0, 0, 1]), ]), color2: [1, 1, 1, 0.9], - }), + }) - // All of the action slots here have defaults, so you don't need to fill - // every slot. ]) ] +// Generate an ArrayBuffer of the new effect's file content. Note that you may +// change the game to output this for to any of the four supported games, as +// long as the game supports all of the features used in the effect. +const output = fxr.toArrayBuffer(Game.EldenRing) + // Write the new file -await fs.writeFile('f000402030.fxr', Buffer.from(fxr.toArrayBuffer())) +await fs.writeFile('f000402030.fxr', Buffer.from(output)) ``` ## Thanks -- ivi - WitchyBND, FXR research -- The12thAvenger - Help with figuring out various unknowns +- ivi - FXR research, and WitchyBND, whose [C# FXR class](https://github.com/ividyon/WitchyBND/blob/main/WitchyFormats/Formats/RSFXR.cs) formed the basis of this library's functions for reading and writing FXR files. +- The12thAvenger - Help with figuring out various unknowns. +- Rayan - Testing, feedback, FXR research. It's also worth mentioning everyone on [WitchyBND's contributors list](https://github.com/ividyon/WitchyBND?tab=readme-ov-file#contributors), especially anyone who has contributed to its FXR class. diff --git a/build/build.js b/build/build.js new file mode 100644 index 0000000..ce4114d --- /dev/null +++ b/build/build.js @@ -0,0 +1,254 @@ +import fs from 'node:fs' +import path from 'node:path' +import { fileURLToPath } from 'node:url' + +import yaml from 'yaml' + +const projectDir = path.dirname(path.dirname(fileURLToPath(import.meta.url))) +const srcDir = path.join(projectDir, 'src') +const distDir = path.join(projectDir, 'dist') +const actionsDir = path.join(srcDir, 'actions') + +const typeMap = { + int: 'number', + float: 'number', + bool: 'boolean' +} + +const fieldMap = { + int: 'FieldType.Integer', + float: 'FieldType.Float', + bool: 'FieldType.Boolean' +} + +const fieldTypeNameMap = { + int: 'integer', + float: 'float', + bool: 'boolean' +} + +const gameMap = { + DS3: 'Game.DarkSouls3', + SDT: 'Game.Sekiro', + ER: 'Game.EldenRing', + AC6: 'Game.ArmoredCore6', +} + +const argumentNames = { + Constant0: 'Constant 0', + InstanceAge: 'Instance age', + EmissionTime: 'Emission time', + EffectAge: 'Effect age', +} + +function naturalSorter(as, bs) { + let a, b, a1, b1, i = 0, n, L, + rx = /(\.\d+)|(\d+(\.\d+)?)|([^\d.]+)|(\.\D+)|(\.$)/g + if (as === bs) { + return 0 + } + if (typeof as !== 'string') { + a = as.toString().toLowerCase().match(rx) + } else { + a = as.toLowerCase().match(rx) + } + if (typeof bs !== 'string') { + b = bs.toString().toLowerCase().match(rx) + } else { + b = bs.toLowerCase().match(rx) + } + L = a.length + while (i < L) { + if (!b[i]) return 1 + a1 = a[i], + b1 = b[i++] + if (a1 !== b1) { + n = a1 - b1 + if (!isNaN(n)) return n + return a1 > b1 ? 1 : -1 + } + } + return b[i] ? -1 : 0 +} + +export default async function(writeToDist = true) { + + const actionTypes = [] + const actionDataEntries = [] + const classes = [] + const actionsListEntries = [] + const actionsExport = [] + + for (const fn of fs.readdirSync(actionsDir).sort(naturalSorter)) { + const data = yaml.parse(await fs.promises.readFile(path.join(actionsDir, fn), 'utf-8')) + + for (const prop of Object.keys(data.properties)) { + let found = false + search: + for (const game of Object.values(data.games)) if (typeof game === 'object') { + for (const list of Object.values(game)) if (Array.isArray(list)) { + if (list.includes(prop)) { + found = true + break search + } + } + } + if (!found) { + console.warn(`YAML Warning: Class property '${prop}' in action ${data.type} (${data.name}) is not used by any games.`) + } + } + for (const game of Object.values(data.games)) if (typeof game === 'object') { + for (const list of Object.values(game)) if (Array.isArray(list)) { + for (const prop of list) { + if (!(prop in data.properties)) { + console.warn(`YAML Warning: Action ${data.type} (${data.name}) is missing the '${prop}' class property.`) + } + } + } + } + + actionsExport.push(data.name) + + actionTypes.push(` + /** + * ${data.desc.trim().replace(/\n/g, '\n * ')} + * + * This action type has a specialized subclass: {@link ${data.name}} + */ + ${data.name} = ${data.type}, + `.trim().replace(/^\s{6}/gm, ' ')) + + actionDataEntries.push(` + [ActionType.${data.name}]: { + props: { + ${Object.entries(data.properties).map(([k, v]) => { + let defValue = v.default ?? 0 + if (Array.isArray(defValue)) { + defValue = `[${defValue.join(', ')}]` + } + return `${k}: { default: ${defValue}, paths: {}${'field' in v ? `, field: ${fieldMap[v.field]}` : ''}${'read' in v ? `, read: value => ${v.read}` : ''}${'write' in v ? `, write: value => ${v.write}` : ''} },` + }).join('\n ')} + }, + games: { + ${Object.entries(data.games).map(([k, v]) => { + if (typeof v === 'string') { + if (v === 'fallback') { + return `[${gameMap[k]}]: -2` + } + return `[${gameMap[k]}]: ${gameMap[v]}` + } + return ` + [${gameMap[k]}]: { + ${Object.entries(v).map(([lk, lv]) => { + if (Array.isArray(lv)) { + return `${lk}: [${lv.map(e => `'${e}'`).join(',')}]` + } else { + if (data.games[lv]?.[lk] === undefined) { + console.warn(`YAML Warning: ${data.type}.games.${k}.${lk} refers to a game without ${lk}: ${lv}`) + } + return `${lk}: ${gameMap[lv]}` + } + }).join(',\n ')} + } + `.trim().replace(/^\s{14}(?=\})/m, ' '.repeat(10)).replace(/^\s{16}/m, ' '.repeat(12)) + }).join(',\n ')} + } + } + `.trim().replace(/^\s{4}/gm, '')) + + if (!data.omitClass) { + classes.push(` + export interface ${data.name}Params { + ${Object.entries(data.properties).filter(e => !e[1].omitClassProp).map(([k, v]) => { + let defValue = v.default ?? 0 + if (typeof defValue === 'string') { + defValue = `{@link ${defValue}}` + } else if (Array.isArray(defValue)) { + defValue = `\`[${defValue.join(', ')}]\`` + } else { + defValue = `\`${defValue}\`` + } + return (` + /** + * ${'desc' in v ? v.desc.trim().replace(/\n/g, '\n * ') : `Unknown${'field' in v ? ` ${fieldTypeNameMap[v.field]}` : ''}.`} + * + * **Default**: ${defValue}${ + 'argument' in v ? ` + * + * **Argument**: {@link PropertyArgument.${v.argument} ${argumentNames[v.argument]}}`:''}${ + 'see' in v ? ` + * + * See also: + * - ${v.see.map(e => `{@link ${e}}`).join('\n * - ')}`:''} + */ + ` + ) + `${k}?: ${v.type ?? typeMap[v.field]}` + }) + .join('') + .trim() + .replace(/^\s{16}(?=\/\*\*| \*)|^\s{14}(?=\w)/gm, ' ') + } + } + `.trim() + .replace(/^\s{8}(?=\})/m, '') + .replace(/^\s{10}(?=\/|\w)/m, ' ') + ) + classes.push(` + /** + * ${data.desc.trim().replace(/\n/g, '\n * ')} + */ + class ${data.name} extends DataAction { + declare type: ActionType.${data.name} + ${Object.entries(data.properties).filter(e => !e[1].omitClassProp).map(([k, v]) => { + return ( + 'desc' in v ? ` + /** + * ${v.desc.trim().replace(/\n/g, '\n * ')}${ + 'argument' in v ? ` + * + * **Argument**: {@link PropertyArgument.${v.argument} ${argumentNames[v.argument]}}`:''}${ + 'see' in v ? ` + * + * See also: + * - ${v.see.map(e => `{@link ${e}}`).join('\n * - ')}`:''} + */ + ` : '\n ' + ) + `${k}: ${v.type ?? typeMap[v.field]}` + }) + .join('') + .trim() + .replace(/^\s{16}(?=\/\*\*| \*)|^\s{14}(?=\w)/gm, ' ') + } + constructor(props: ${data.name}Params = {}) { + super(ActionType.${data.name}) + this.assign(props) + } + } + `.trim() + .replace(/^\s{8}/gm, '') + ) + } + + actionsListEntries.push(`[ActionType.${data.name}]: ${data.name}, ${data.name},`) + } + + let libSrc = await fs.promises.readFile(path.join(srcDir, 'fxr.ts'), 'utf-8') + + const reReplacement = id => new RegExp(`( *)\\/\\*#${id} start\\*\\/[\\s\\S]*\\/\\*#${id} end\\*\\/`) + function replace(id, content) { + libSrc = libSrc.replace(reReplacement(id), `$1/*#${id} start*/\n${content}\n$1/*#${id} end*/`) + } + + replace('ActionType', ' '+actionTypes.join('\n ')) + replace('ActionData', ' '+actionDataEntries.join(',\n ')) + replace('ActionClasses', classes.join('\n\n')) + replace('ActionsList', ' '+actionsListEntries.join('\n ')) + replace('ActionsExport', ' '+actionsExport.join(',\n ') + ',') + + await fs.promises.writeFile(path.join(srcDir, 'fxr.ts'), libSrc) + if (writeToDist) { + await fs.promises.mkdir(distDir, { recursive: true }) + await fs.promises.writeFile(path.join(distDir, 'fxr.ts'), libSrc) + } + +} diff --git a/build/run.js b/build/run.js new file mode 100644 index 0000000..e3d5e64 --- /dev/null +++ b/build/run.js @@ -0,0 +1,3 @@ +import build from './build.js' + +await build() diff --git a/build/watch.js b/build/watch.js new file mode 100644 index 0000000..60845fb --- /dev/null +++ b/build/watch.js @@ -0,0 +1,39 @@ +import fs from 'node:fs' +import path from 'node:path' +import { fileURLToPath } from 'node:url' + +import chokidar from 'chokidar' + +import build from './build.js' + +const projectDir = path.dirname(path.dirname(fileURLToPath(import.meta.url))) +const srcDir = path.join(projectDir, 'src') +const distDir = path.join(projectDir, 'dist') +const fxrTSPath = path.resolve(path.join(srcDir, 'fxr.ts')) + +try { + await build() +} catch (err) { + console.error(err) +} + +const watcher = chokidar.watch(srcDir, { + awaitWriteFinish: { + stabilityThreshold: 600, + pollInterval: 100 + } +}) + +watcher.on('ready', () => { + watcher.on('all', async (event, filePath) => { + if (path.resolve(filePath) === fxrTSPath) { + await fs.promises.copyFile(fxrTSPath, path.join(distDir, 'fxr.ts')) + } else { + try { + await build(false) + } catch (err) { + console.error(err) + } + } + }) +}) diff --git a/examples/lightsaber.js b/examples/lightsaber.js index c9fcdbb..557a9c3 100644 --- a/examples/lightsaber.js +++ b/examples/lightsaber.js @@ -6,6 +6,7 @@ import { CylinderEmitterShape, ExternalValue, FXR, + InitialDirection, Keyframe, LinearProperty, MultiTextureBillboardEx, @@ -15,8 +16,9 @@ import { ParticleMovement, ParticleWindAcceleration, PeriodicEmitter, + PointEmitterShape, PointLight, - Property, + RandomProperty, State, Tracer, } from '@cccode/fxr' @@ -52,13 +54,16 @@ function crossguardSide(position) { translateY: 0.54, translateX: 0.135 * position }), + new PointEmitterShape({ direction: InitialDirection.LocalDown }), new MultiTextureBillboardEx({ - orientation: OrientationMode.ParentYaw, + orientation: OrientationMode.LocalYaw, blendMode: BlendMode.Source, height: 0.0225, width: 0.15, rgbMultiplier: 7.5, - bloomColor: color, + bloomRed: color[0], + bloomGreen: color[1], + bloomBlue: color[2], bloomStrength: bloomMultiplier * 0.5, octagonal: true, depthBlend: false @@ -77,13 +82,15 @@ function bladeCap(position) { }), new BillboardEx({ texture: 10011, - orientation: OrientationMode.ParentNegativeZ, + orientation: OrientationMode.LocalSouth, blendMode: BlendMode.Add, width: 0.0425, uniformScale: true, rgbMultiplier: 1, alphaMultiplier: 100, - bloomColor: color, + bloomRed: color[0], + bloomGreen: color[1], + bloomBlue: color[2], bloomStrength: bloomMultiplier * 0.5, depthOffset: 0.04, }) @@ -100,16 +107,14 @@ fxr.root.nodes = [ ], [ // Steam during rain/snow new BasicNode([ - new PeriodicEmitter(0.2, 1), - new CylinderEmitterShape(true, 0.1), + new PeriodicEmitter({ interval: 0.2 }), + new CylinderEmitterShape({ radius: 0.1 }), new ParticleAttributes({ duration: 4, attachment: AttachMode.None }), - new ParticleMovement({ - gravity: -0.1, - }), - new ParticleWindAcceleration(0.02), + new ParticleMovement({ gravity: -0.1 }), + new ParticleWindAcceleration({ acceleration: 0.02 }), new MultiTextureBillboardEx({ mask: 31261, columns: 8, @@ -122,7 +127,7 @@ fxr.root.nodes = [ new Keyframe(4, [1, 1, 1, 0]), ]), frameIndex: LinearProperty.basic(true, 4, 0, 32), - frameIndexOffset: Property.random(0, 32), + frameIndexOffset: RandomProperty(0, 32), rgbMultiplier: 5, layer1: 34020, layer1SpeedV: 0.5, @@ -148,12 +153,15 @@ fxr.root.nodes = [ // Blade new BasicNode([ + new PointEmitterShape({ direction: InitialDirection.LocalDown }), new BillboardEx({ - orientation: OrientationMode.ParentYaw, + orientation: OrientationMode.LocalYaw, blendMode: BlendMode.Add, height: 0.03, rgbMultiplier: 10, - bloomColor: color, + bloomRed: color[0], + bloomGreen: color[1], + bloomBlue: color[2], bloomStrength: bloomMultiplier * 0.5, }) ]).mapStates(0, 0), @@ -162,20 +170,26 @@ fxr.root.nodes = [ // Electric arcs new BasicNode([ - new PeriodicEmitter(0.1, 2), + new PeriodicEmitter({ interval: 0.1, perInterval: 2 }), + new CylinderEmitterShape({ + radius: 0.01, + height: 0.5, + direction: InitialDirection.LocalDown + }), new ParticleAttributes({ duration: 0.5 }), - new CylinderEmitterShape(true, 0.01, 0.5), new BillboardEx({ - orientation: OrientationMode.ParentYaw, + orientation: OrientationMode.LocalYaw, blendMode: BlendMode.Add, texture: 33020, totalFrames: 16, frameIndex: LinearProperty.basic(true, 0.5, 0, 16), - frameIndexOffset: Property.random(0, 15), + frameIndexOffset: RandomProperty(0, 15), height: 0.08, - width: Property.random(0.2, 0.5), + width: RandomProperty(0.2, 0.5), rgbMultiplier: 7.5, - bloomColor: color, + bloomRed: color[0], + bloomGreen: color[1], + bloomBlue: color[2], bloomStrength: bloomMultiplier * 0.5, alphaThreshold: new LinearProperty(false, [ new Keyframe(0, 255), @@ -187,23 +201,23 @@ fxr.root.nodes = [ // Trail new BasicNode([ - new NodeTransform({ - rotateX: 90 - }), + new NodeTransform({ rotateX: 90 }), new Tracer({ blendMode: BlendMode.Add, - length: 0.5, + width: 0.5, segmentDuration: 0.06, rgbMultiplier: 7.5, - bloomColor: color, + bloomRed: color[0], + bloomGreen: color[1], + bloomBlue: color[2], bloomStrength: bloomMultiplier * 1, }), ]).mapStates(0, 0), // Falling sparks new BasicNode([ - new PeriodicEmitter(0.1, 1), - new CylinderEmitterShape(true, 0.01), + new PeriodicEmitter({ interval: 0.1 }), + new CylinderEmitterShape({ radius: 0.01 }), new ParticleAttributes({ duration: 2, attachment: AttachMode.None @@ -212,16 +226,18 @@ fxr.root.nodes = [ gravity: 2, speed: 0.3, }), - new ParticleWindAcceleration(0.02), + new ParticleWindAcceleration({ acceleration: 0.02 }), new BillboardEx({ texture: 10020, blendMode: BlendMode.Add, - width: Property.random(0.008, 0.04), + width: RandomProperty(0.008, 0.04), uniformScale: true, alphaThreshold: LinearProperty.basic(false, 2, 0, 255), alphaMultiplier: 2, rgbMultiplier: 20, - bloomColor: color, + bloomRed: color[0], + bloomGreen: color[1], + bloomBlue: color[2], bloomStrength: bloomMultiplier * 0.5, }) ]).mapStates(0, 0), diff --git a/examples/rgb-ify.js b/examples/rgb-ify.js index ec4e54b..d4ec37b 100644 --- a/examples/rgb-ify.js +++ b/examples/rgb-ify.js @@ -1,5 +1,28 @@ import fs from 'node:fs/promises' -import { ActionType, EffectType, ExternalValue, ExternalValueModifier, FXR } from '@cccode/fxr' +import { + ActionType, + BasicEffect, + BillboardEx, + ConstantProperty, + Distortion, + DynamicTracer, + ExternalValue, + ExternalValueModifier, + FXR, + Game, + Keyframe, + Line, + Model, + MultiTextureBillboardEx, + PointLight, + PointSprite, + Property, + QuadLine, + RadialBlur, + RichModel, + SpotLight, + Tracer +} from '@cccode/fxr' // Input folder to grab the original files from const inputDir = 'sfx/sfxbnd_commoneffects-ffxbnd-dcx/effect' @@ -30,17 +53,20 @@ const ids = [ output color should be various shades of the colors in the modifier. */ const rainbowDuration = 4 -const partDuration = rainbowDuration / 6 +const partDuration = rainbowDuration / 360 function procProp(list, idx) { + if (!(list[idx] instanceof Property)) { + list[idx] = new ConstantProperty(...list[idx]) + } list[idx].modifiers.push( new ExternalValueModifier(ExternalValue.TimeOfDay, true, [ - { position: 0, value: [1, 0, 0, 1] }, - { position: partDuration, value: [1, 0, 1, 1] }, - { position: partDuration * 2, value: [0, 0, 1, 1] }, - { position: partDuration * 3, value: [0, 1, 1, 1] }, - { position: partDuration * 4, value: [0, 1, 0, 1] }, - { position: partDuration * 5, value: [1, 1, 0, 1] }, - { position: partDuration * 6, value: [1, 0, 0, 1] }, + new Keyframe(0, [1, 0, 0, 1]), + new Keyframe(partDuration, [1, 0, 1, 1]), + new Keyframe(partDuration * 2, [0, 0, 1, 1]), + new Keyframe(partDuration * 3, [0, 1, 1, 1]), + new Keyframe(partDuration * 4, [0, 1, 0, 1]), + new Keyframe(partDuration * 5, [1, 1, 0, 1]), + new Keyframe(partDuration * 6, [1, 0, 0, 1]), ]) ) } @@ -55,9 +81,10 @@ while (ids.length) { // Remove the first ID from the list and get the file name for that ID const id = ids.shift() const fileName = `f${id.toString().padStart(9, '0')}.fxr` - - // Read the original FXR file - const fxr = FXR.read((await fs.readFile(`${inputDir}/${fileName}`)).buffer) + + // Read the original FXR file. These effects are from Elden Ring, so make + // sure to let it know to parse it as an ER FXR. + const fxr = FXR.read(await fs.readFile(`${inputDir}/${fileName}`), Game.EldenRing) // First make the effect grayscale by setting the RGB values to the // percieved brightness of the original color @@ -70,46 +97,42 @@ while (ids.length) { Next, add the rainbow modifier to one of the color multipliers in the appearance action of all effects in the FXR. - Adding it to Action 131 (ParticleMultiplier) wouldn't work in all cases, + Adding it to Action 131 (ParticleModifier) wouldn't work in all cases, because not all appearance types are particles. */ for (const effect of fxr.root.walkEffects()) { - if (effect.type !== EffectType.Basic) continue - - const slot9 = effect.actions[9] - switch (slot9.type) { - case ActionType.PointSprite: - case ActionType.QuadLine: - procProp(slot9.properties1, 3) - break - case ActionType.Line: + if (!(effect instanceof BasicEffect)) continue + + const action = effect.appearance + if ( + action instanceof PointSprite || + action instanceof Line || + action instanceof QuadLine || + action instanceof BillboardEx || + action instanceof MultiTextureBillboardEx || + action instanceof Model || + action instanceof RichModel || + action instanceof Tracer || + action instanceof DynamicTracer + ) { + procProp(action, 'color1') + } else if ( + action instanceof PointLight || + action instanceof SpotLight + ) { + procProp(action, 'diffuseColor') + } else if ( + action instanceof Distortion || + action instanceof RadialBlur + ) { + procProp(action, 'color') + } else switch (action.type) { case ActionType.Unk10014_LensFlare: - procProp(slot9.properties1, 2) - break - case ActionType.BillboardEx: - case ActionType.Distortion: - case ActionType.RadialBlur: - procProp(slot9.properties1, 7) - break - case ActionType.MultiTextureBillboardEx: - procProp(slot9.properties1, 15) - break - case ActionType.Model: - procProp(slot9.properties1, 14) - break - case ActionType.Tracer: - case ActionType.Unk10012_Tracer: - procProp(slot9.properties1, 6) - break - case ActionType.PointLight: - case ActionType.SpotLight: - procProp(slot9.properties1, 0) // Diffuse - procProp(slot9.properties1, 1) // Specular + procProp(action.properties1, 2) break case ActionType.Unk10000_StandardParticle: case ActionType.Unk10001_StandardCorrectParticle: - case ActionType.Unk10015_RichModel: - procProp(slot9.properties1, 13) + procProp(action.properties1, 13) break } } @@ -117,8 +140,8 @@ while (ids.length) { // Update reference lists, since we used an external value to do this fxr.updateReferences() - // Write the modified file - await fs.writeFile(`${outputDir}/${fileName}`, Buffer.from(fxr.toArrayBuffer())) + // Write the modified file back to ER's FXR format + await fs.writeFile(`${outputDir}/${fileName}`, Buffer.from(fxr.toArrayBuffer(Game.EldenRing))) // Add this ID to the "done" list so it won't be processed again if something // else references it diff --git a/examples/scale.js b/examples/scale.js index 838c43c..9d500e1 100644 --- a/examples/scale.js +++ b/examples/scale.js @@ -1,5 +1,5 @@ import fs from 'node:fs/promises' -import { FXR } from '@cccode/fxr' +import { FXR, Game } from '@cccode/fxr' /* @@ -10,16 +10,13 @@ original size. You can change it to output to the same file, but make sure to back up any important effects before you overwrite them with this in case something goes wrong. -This does not work with Dark Souls 3 effects, and Sekiro effects have not been -tested. Elden Ring and Armored Core 6 effects should work fine. - */ -// Parse file -const fxr = FXR.read((await fs.readFile('f000450360.fxr')).buffer) +// Parse file, make sure to set the right game +const fxr = FXR.read(await fs.readFile('f000450360.fxr'), Game.EldenRing) // Scale the effect by some factor fxr.root.scale(0.5) -// Write the modified file -await fs.writeFile('f000450360_edit.fxr', Buffer.from(fxr.toArrayBuffer())) +// Write the modified file for whatever game you want to output this to +await fs.writeFile('f000450360_edit.fxr', Buffer.from(fxr.toArrayBuffer(Game.EldenRing))) diff --git a/examples/shared_emitter.js b/examples/shared_emitter.js index 0349407..cf0c099 100644 --- a/examples/shared_emitter.js +++ b/examples/shared_emitter.js @@ -35,8 +35,8 @@ fxr.root.nodes = [ seconds, and it will pick a new type every time. It's shaped like a cube, with particles only emitting from the surface. */ - new PeriodicEmitter(0.1, 10), - new BoxEmitterShape(false) + new PeriodicEmitter({ interval: 0.1, perInterval: 10 }), + new BoxEmitterShape({ emitInside: false }) ], [ new BasicNode([ // Child node 0 - red particles new ParticleAttributes({ duration: 1 }), diff --git a/examples/smoke_trail.js b/examples/smoke_trail.js index f3dc491..b9a550a 100644 --- a/examples/smoke_trail.js +++ b/examples/smoke_trail.js @@ -10,7 +10,7 @@ import { ParticleAttributes, ParticleMovement, PeriodicEmitter, - Property, + RandomProperty, } from '@cccode/fxr' /* @@ -32,8 +32,8 @@ const fxr = new FXR(402030) These are the two emitters we'll use: */ const emitters = [ - new PeriodicEmitter(0.05), - new EqualDistanceEmitter(0.1, 200) + new PeriodicEmitter({ interval: 0.05 }), + new EqualDistanceEmitter({ threshold: 0.1, maxConcurrent: 200 }) ] /* @@ -76,7 +76,7 @@ fxr.root.nodes = emitters.map(emitter => new BasicNode([ mask: 10061, layer1: 26020, layer1SpeedV: 0.1, - layer1OffsetU: Property.random(0, 1), + layer1OffsetU: RandomProperty(0, 1), alphaThreshold: new LinearProperty(false, [ new Keyframe(0, 255), new Keyframe(0.5, 0), diff --git a/examples/torch_fire.js b/examples/torch_fire.js index 060e49e..bd2d0fd 100644 --- a/examples/torch_fire.js +++ b/examples/torch_fire.js @@ -12,8 +12,8 @@ import { ParticleWindAcceleration, PeriodicEmitter, PointLight, - Property, QuadLine, + RandomProperty, SphereEmitterShape, } from '@cccode/fxr' @@ -21,8 +21,8 @@ const fxr = new FXR(402030) fxr.root.nodes = [ new BasicNode([ - new PeriodicEmitter(0.1, 1), - new SphereEmitterShape(true, 0.05), + new PeriodicEmitter({ interval: 0.1 }), + new SphereEmitterShape({ radius: 0.05 }), new ParticleAttributes({ duration: 1, attachment: AttachMode.None @@ -31,7 +31,7 @@ fxr.root.nodes = [ gravity: -0.1, followFactor: LinearProperty.basic(false, 1, 1, 0), }), - new ParticleWindAcceleration(0.085), + new ParticleWindAcceleration({ acceleration: 0.085 }), new MultiTextureBillboardEx({ mask: 31001, layer1: 34060, @@ -48,21 +48,21 @@ fxr.root.nodes = [ new Keyframe(1, 255), ]), frameIndex: LinearProperty.basic(true, 1.5, 0, 64), - frameIndexOffset: Property.random(0, 64), + frameIndexOffset: RandomProperty(0, 64), layer1SpeedV: 0.5, rgbMultiplier: 400, - offset: [0, 0.3, 0], - negativeDepthOffset: 0.25, + offsetY: 0.3, + unkDepthBlend2: 0.25, bloomStrength: 0.3, - bloomColor: [1, 0.7, 0.5], + bloomRed: 1, + bloomGreen: 0.7, + bloomBlue: 0.5 }), ]), new BasicNode([ - new NodeTransform({ - rotateX: 90, - }), - new PeriodicEmitter(0.1, 2), - new SphereEmitterShape(true, 0.075), + new NodeTransform({ rotateX: 90 }), + new PeriodicEmitter({ interval: 0.1, perInterval: 2 }), + new SphereEmitterShape({ radius: 0.075 }), new ParticleAttributes({ duration: 2, attachment: AttachMode.None @@ -73,13 +73,13 @@ fxr.root.nodes = [ followFactor: LinearProperty.basic(false, 0.75, 1, 0), followRotation: false, }), - new ParticleWindAcceleration(0.03), + new ParticleWindAcceleration({ acceleration: 0.03 }), new QuadLine({ blendMode: BlendMode.Add, startColor: [1, 0.15, 0.025, 1], endColor: [1, 0, 0, 0], - width: Property.random(0.002, 0.004, 91846942), - height: Property.random(0.02, 0.04, 91846942), + width: RandomProperty(0.002, 0.004, 91846942), + length: RandomProperty(0.02, 0.04, 91846942), color1: new LinearProperty(false, [ new Keyframe(0, [0, 0, 0, 0]), new Keyframe(0.5, [1, 1, 1, 1]), @@ -87,13 +87,13 @@ fxr.root.nodes = [ ]), rgbMultiplier: 20, bloomStrength: 0.8, - bloomColor: [1, 0.7, 0.5], + bloomRed: 1, + bloomGreen: 0.7, + bloomBlue: 0.5 }) ]), new BasicNode([ - new NodeTransform({ - translateY: 0.15 - }), + new NodeTransform({ translateY: 0.15 }), new PointLight({ diffuseColor: [1, 0.65, 0.475, 1], radius: 20, diff --git a/fxr.ts b/fxr.ts deleted file mode 100644 index 3afd3ae..0000000 --- a/fxr.ts +++ /dev/null @@ -1,14645 +0,0 @@ -enum FXRVersion { - /** - * Used in Dark Souls 3. - */ - DarkSouls3 = 4, - /** - * Used in Sekiro, Elden Ring, and Armored Core 6. - */ - Sekiro = 5, -} - -enum NodeType { - /** - * A root node. - * - * This node type has a specialized subclass: {@link RootNode} - */ - Root = 2000, - /** - * Acts as a node containing another FXR. - * - * This node type has a specialized subclass: {@link ProxyNode} - */ - Proxy = 2001, - /** - * A node that only displays one of its child nodes at a time based on - * distance thresholds for each. - * - * This node type has a specialized subclass: {@link LevelOfDetailNode} - */ - LevelOfDetail = 2002, - /** - * A basic node that can have transforms and child nodes, and emit particles. - * - * This node type has a specialized subclass: {@link BasicNode} - */ - Basic = 2200, - /** - * A node that overrides the emitter of its child nodes with its own, - * allowing a single emitter to emit multiple types of particles. - * - * This node type has a specialized subclass: {@link SharedEmitterNode} - */ - SharedEmitter = 2202, -} - -enum EffectType { - /** - * Manages the duration and thresholds for the - * {@link NodeType.LevelOfDetail level of detail node}. - * - * This effect type has a specialized subclass: {@link LevelOfDetailEffect} - */ - LevelOfDetail = 1002, - /** - * Effect used in {@link NodeType.Basic basic nodes} to apply transforms and - * emit particles of many different types. - * - * This effect type has a specialized subclass: {@link BasicEffect} - */ - Basic = 1004, - /** - * Effect used in {@link NodeType.SharedEmitter shared emitter nodes} to - * override emitters of child nodes and control which of the child nodes to use - * the particles of. - * - * This effect type has a specialized subclass: {@link SharedEmitterEffect} - */ - SharedEmitter = 1005, -} - -enum ActionType { - /** - * This action does nothing. It fits into most action slots and acts as a way - * to disable the effects of the other actions that go in those slots. - */ - None = 0, - /** - * Controls the movement of the node. - * - * This action type has a specialized subclass: {@link NodeMovement} - */ - NodeAcceleration = 1, - /** - * Translates the node with a property, meaning the offset can be animated. - * - * This action type has a specialized subclass: {@link NodeTranslation} - */ - NodeTranslation = 15, - /** - * Controls the rotation speed of the node. - * - * This action type has a specialized subclass: {@link NodeMovement} - */ - NodeSpin = 34, - /** - * Sets the translation and rotation of the node. - * - * This action type has a specialized subclass: {@link NodeTransform} - */ - StaticNodeTransform = 35, - /** - * Sets and randomizes the translation and rotation of the node. - * - * This action type has a specialized subclass: {@link NodeTransform} - */ - RandomNodeTransform = 36, - /** - * Attaches the node to the camera. - * - * This action type has a specialized subclass: {@link NodeAttachToCamera} - */ - NodeAttachToCamera = 46, - /** - * Controls the movement of particles. - * - * This action type has a specialized subclass: {@link ParticleMovement} - */ - ParticleAcceleration = 55, - /** - * Controls the movement of particles. - * - * This action type has a specialized subclass: {@link ParticleMovement} - */ - ParticleSpeed = 60, - /** - * Controls the movement of particles. - * - * This action type has a specialized subclass: {@link ParticleMovement} - */ - ParticleSpeedRandomTurns = 64, - /** - * Controls the movement of particles. - * - * This action type has a specialized subclass: {@link ParticleMovement} - */ - ParticleSpeedPartialFollow = 65, - /** - * Plays a sound effect. - * - * This action type has a specialized subclass: {@link PlaySound} - */ - PlaySound = 75, - /** - * Controls the movement of the node. - * - * This action type has a specialized subclass: {@link NodeMovement} - */ - NodeAccelerationRandomTurns = 83, - /** - * Controls the movement of particles. - * - * This action type has a specialized subclass: {@link ParticleMovement} - */ - ParticleAccelerationRandomTurns = 84, - /** - * Controls the movement of particles. - * - * This action type has a specialized subclass: {@link ParticleMovement} - */ - ParticleAccelerationPartialFollow = 105, - /** - * Controls the movement of the node. - * - * This action type has a specialized subclass: {@link NodeMovement} - */ - NodeAccelerationPartialFollow = 106, - /** - * Controls the movement of the node. - * - * This action type has a specialized subclass: {@link NodeMovement} - */ - NodeAccelerationSpin = 113, - /** - * Controls the movement of the node. - * - * This action type has a specialized subclass: {@link NodeMovement} - */ - NodeSpeed = 120, - /** - * Controls the movement of the node. - * - * This action type has a specialized subclass: {@link NodeMovement} - */ - NodeSpeedRandomTurns = 121, - /** - * Controls the movement of the node. - * - * This action type has a specialized subclass: {@link NodeMovement} - */ - NodeSpeedPartialFollow = 122, - /** - * Controls the movement of the node. - * - * This action type has a specialized subclass: {@link NodeMovement} - */ - NodeSpeedSpin = 123, - /** - * Controls various things about the node, like its duration, and how - * it is attached to the parent node. - * - * This action type has a specialized subclass: {@link NodeAttributes} - */ - NodeAttributes = 128, - /** - * Controls various things about the particles emitted by the effect, like - * their duration, and how they are attached to the parent node. - * - * This action type has a specialized subclass: {@link ParticleAttributes} - */ - ParticleAttributes = 129, - Unk130 = 130, - /** - * Controls various multipliers as well as the acceleration of particles. - * - * This action type has a specialized subclass: {@link ParticleMultiplier} - */ - ParticleMultiplier = 131, - /** - * References another FXR by its ID. - * - * This action type has a specialized subclass: {@link FXRReference} - */ - FXRReference = 132, - /** - * Used in the {@link EffectType.LevelOfDetail level of detail effect} to - * manage the duration and thresholds for the - * {@link NodeType.LevelOfDetail level of detail node}. - * - * This action type has a specialized subclass: - * {@link LevelOfDetailThresholds} - */ - LevelOfDetail = 133, - /** - * Maps states to effects in the parent node. - * - * This action type has a specialized subclass: {@link StateEffectMap} - */ - StateEffectMap = 199, - /** - * Used in {@link EffectType.SharedEmitter} to emit all particles from child - * nodes every time the shared emitter emits something. - * - * This action type has a specialized subclass: {@link EmitAllParticles} - */ - EmitAllParticles = 200, - /** - * Used in {@link EffectType.SharedEmitter} to emit a particle from a random - * child node every time the shared emitter emits something. - * - * This action type has a specialized subclass: {@link EmitRandomParticles} - */ - EmitRandomParticles = 201, - /** - * Emits particles periodically. - * - * This action type has a specialized subclass: {@link PeriodicEmitter} - */ - PeriodicEmitter = 300, - /** - * Emits particles once it has moved a certain distance from where it last - * emitted particles. - * - * This action type has a specialized subclass: {@link EqualDistanceEmitter} - */ - EqualDistanceEmitter = 301, - /** - * Emits one particle once. - * - * This action type has a specialized subclass: {@link OneTimeEmitter} - */ - OneTimeEmitter = 399, - /** - * Makes the emitter a single point. - * - * This action type has a specialized subclass: {@link PointEmitterShape} - */ - PointEmitterShape = 400, - /** - * Makes the emitter disk-shaped. - * - * This action type has a specialized subclass: {@link DiskEmitterShape} - */ - DiskEmitterShape = 401, - /** - * Makes the emitter rectangular. - * - * This action type has a specialized subclass: {@link RectangleEmitterShape} - */ - RectangleEmitterShape = 402, - /** - * Makes the emitter spherical. - * - * This action type has a specialized subclass: {@link SphereEmitterShape} - */ - SphereEmitterShape = 403, - /** - * Makes the emitter cuboidal. - * - * This action type has a specialized subclass: {@link BoxEmitterShape} - */ - BoxEmitterShape = 404, - /** - * Makes the emitter cylindrical. - * - * This action type has a specialized subclass: {@link CylinderEmitterShape} - */ - CylinderEmitterShape = 405, - /** - * Makes all particles use the default initial direction from the emitter. - * - * A particle's initial direction is used for various things that need a - * direction but lack an inherent direction, for example the speed from the - * {@link ActionType.ParticleMultiplier ParticleMultiplier} action. It is - * also used to set the rotation of {@link ActionType.Line Line} and - * {@link ActionType.QuadLine QuadLine} particles without any movement. The - * default initial direction is controlled by the emitter shape. - * - * This action type has a specialized subclass: {@link NoParticleSpread} - */ - NoParticleSpread = 500, - /** - * Gives each particle a random initial direction offset within a circular - * cone. - * - * A particle's initial direction is used for various things that need a - * direction but lack an inherent direction, for example the speed from the - * {@link ActionType.ParticleMultiplier ParticleMultiplier} action. It is - * also used to set the rotation of {@link ActionType.Line Line} and - * {@link ActionType.QuadLine QuadLine} particles without any movement. The - * default initial direction is controlled by the emitter shape. - * - * This action type has a specialized subclass: - * {@link CircularParticleSpread} - */ - CircularParticleSpread = 501, - /** - * Gives each particle a random initial direction offset within an elliptical - * cone. - * - * A particle's initial direction is used for various things that need a - * direction but lack an inherent direction, for example the speed from the - * {@link ActionType.ParticleMultiplier ParticleMultiplier} action. It is - * also used to set the rotation of {@link ActionType.Line Line} and - * {@link ActionType.QuadLine QuadLine} particles without any movement. The - * default initial direction is controlled by the emitter shape. - * - * This action type has a specialized subclass: - * {@link EllipticalParticleSpread} - */ - EllipticalParticleSpread = 502, - /** - * Gives each particle a random initial direction offset within a rectangular - * cone. - * - * A particle's initial direction is used for various things that need a - * direction but lack an inherent direction, for example the speed from the - * {@link ActionType.ParticleMultiplier ParticleMultiplier} action. It is - * also used to set the rotation of {@link ActionType.Line Line} and - * {@link ActionType.QuadLine QuadLine} particles without any movement. The - * default initial direction is controlled by the emitter shape. - * - * This action type has a specialized subclass: - * {@link RectangularParticleSpread} - */ - RectangularParticleSpread = 503, - /** - * Very basic point sprite particle. Similar to - * {@link ActionType.BillboardEx BillboardEx}, but far simpler. - * - * This action type has a specialized subclass: {@link PointSprite} - */ - PointSprite = 600, - /** - * Simple line particle. It automatically rotates to match the direction it's - * moving. - * - * This action type has a specialized subclass: {@link Line} - */ - Line = 601, - /** - * Simple rectangular particle, very similar to - * {@link ActionType.Line Line particles}, but has properties that control - * the width as well as the length. It automatically rotates to match the - * direction it's moving. - * - * This action type has a specialized subclass: {@link QuadLine} - */ - QuadLine = 602, - /** - * Particle with a texture that may animate. - * - * This action type has a specialized subclass: {@link BillboardEx} - */ - BillboardEx = 603, - /** - * Particle with multiple texture that can scroll. - * - * This action type has a specialized subclass: - * {@link MultiTextureBillboardEx} - */ - MultiTextureBillboardEx = 604, - /** - * Particle with a 3D model. - * - * This action type has a specialized subclass: {@link Model} - */ - Model = 605, - /** - * Creates a trail behind moving effects. - * - * This action type has a specialized subclass: {@link Tracer} - */ - Tracer = 606, - /** - * A particle that distorts anything seen through it. - * - * Note: This particle is not visible if the "Effects" setting is set to "Low". - * - * This action type has a specialized subclass: {@link Distortion} - */ - Distortion = 607, - /** - * A particle that applies a radial blur to anything seen through it. - * - * Note: This particle is not visible if the "Effects" setting is set to "Low". - * - * This action type has a specialized subclass: {@link RadialBlur} - */ - RadialBlur = 608, - /** - * Point light source. - * - * This action type has a specialized subclass: {@link PointLight} - */ - PointLight = 609, - Unk700 = 700, // Root node action - Unk701 = 701, // Root node action - Unk702 = 702, // Root node action - /** - * Controls how effective the wind is at pushing the node. - * - * This action type has a specialized subclass: {@link NodeWindSpeed} - */ - NodeWindSpeed = 731, - /** - * Controls how effective the wind is at pushing the particles emitted from - * the node. - * - * This action type has a specialized subclass: - * {@link ParticleWindSpeed} - */ - ParticleWindSpeed = 732, - /** - * Controls how effective the wind is at accelerating the node. - * - * This action type has a specialized subclass: {@link NodeWindAcceleration} - */ - NodeWindAcceleration = 733, - /** - * Controls how effective the wind is at accelerating the particles emitted - * from the node. - * - * Acceleration requires slot 10 to have an action that enables acceleration - * of the particles. - * - * This action type has a specialized subclass: - * {@link ParticleWindAcceleration} - */ - ParticleWindAcceleration = 734, - Unk800 = 800, - Unk10000_StandardParticle = 10000, - Unk10001_StandardCorrectParticle = 10001, - Unk10002_Fluid = 10002, - Unk10003_LightShaft = 10003, - Unk10008_SparkParticle = 10008, - Unk10009_SparkCorrectParticle = 10009, - Unk10010_Tracer = 10010, - Unk10012_Tracer = 10012, - /** - * Simulates an interaction with water, allowing effects to create ripples in - * nearby water. The interaction basically pushes water in a shape controlled - * by a texture down to a given depth and holds it there for a duration before - * releasing it. - * - * This action type has a specialized subclass: {@link WaterInteraction} - */ - WaterInteraction = 10013, - Unk10014_LensFlare = 10014, - Unk10015_RichModel = 10015, - Unk10100 = 10100, // Root node action - Unk10200_ForceFieldCancelArea = 10200, - Unk10300_ForceFieldWindArea = 10300, - Unk10301_ForceFieldGravityArea = 10301, - Unk10302_CollisionFieldArea = 10302, - Unk10303_ForceFieldTurbulenceArea = 10303, - Unk10400 = 10400, // Root node action - Unk10500 = 10500, // Root node action - /** - * Spot light source. - * - * This action type has a specialized subclass: {@link SpotLight} - */ - SpotLight = 11000, -} - -enum ValueType { - Scalar = 0, - Vector2 = 1, - Vector3 = 2, - Vector4 = 3 -} - -enum PropertyFunction { - /** - * Always returns 0 for each component. - * - * This property function has a specialized subclass: {@link ZeroProperty} - */ - Zero = 0, - /** - * Always returns 1 for each component. - * - * This property function has a specialized subclass: {@link OneProperty} - */ - One = 1, - /** - * Always returns the value in the property's fields. - * - * This property function has a specialized subclass: - * {@link ConstantProperty} - */ - Constant = 2, - /** - * Uses step interpolation to interpolate the property's values. - * - * This property function has a specialized subclass: {@link SteppedProperty} - */ - Stepped = 3, - /** - * Uses linear interpolation to interpolate the property's values. - * - * This property function has a specialized subclass: {@link LinearProperty} - */ - Linear = 4, - /** - * Uses a custom curve to interpolate the property's values. - * - * The difference between this and {@link Curve2} is currently unknown. - */ - Curve1 = 5, - /** - * Uses a custom curve to interpolate the property's values. - * - * The difference between this and {@link Curve1} is currently unknown. - * - * This property function has a specialized subclass: {@link Curve2Property} - */ - Curve2 = 6, - /** - * Same as {@link Curve1} or {@link Curve2} (not sure which), but allows each - * component of the value to have a different number of stops. - * - * Only available in Armored Core 6. - */ - CompCurve = 7 -} - -export type ValuePropertyFunction = - PropertyFunction.Zero | - PropertyFunction.One | - PropertyFunction.Constant - -export type KeyframePropertyFunction = - PropertyFunction.Stepped | - PropertyFunction.Linear | - PropertyFunction.Curve1 | - PropertyFunction.Curve2 - -export type ComponentKeyframePropertyFunction = PropertyFunction.CompCurve - -export type ValueTypeMap = { - [ValueType.Scalar]: number - [ValueType.Vector2]: Vector2 - [ValueType.Vector3]: Vector3 - [ValueType.Vector4]: Vector4 -} - -export interface IKeyframe { - position: number - value: ValueTypeMap[T] - unkTangent1?: ValueTypeMap[T] - unkTangent2?: ValueTypeMap[T] -} - -export interface IProperty { - valueType: T - function: F - componentCount: number - fieldCount: number - fields: NumericalField[] - toJSON(): any - scale(factor: number): void - power(exponent: number): void - add(summand: number): void - minify(): IProperty - valueAt(arg: number): ValueTypeMap[T] - clone(): IProperty -} - -export interface IValueProperty extends IProperty { - value: ValueTypeMap[T] - clone(): IValueProperty -} - -export interface IKeyframeProperty extends IProperty { - loop: boolean - keyframes: IKeyframe[] - sortKeyframes(): void - clone(): IKeyframeProperty -} - -export interface IModifiableProperty extends IProperty { - modifiers: Modifier[] -} - -export type Vector2 = [x: number, y: number] -export type Vector3 = [x: number, y: number, z: number] -export type Vector4 = [red: number, green: number, blue: number, alpha: number] -export type Vector = Vector2 | Vector3 | Vector4 -export type PropertyValue = number | Vector -export type AnyProperty = Property -export type ScalarProperty = Property -export type Vector2Property = Property -export type Vector3Property = Property -export type Vector4Property = Property -export type VectorProperty = Vector2Property | Vector3Property | Vector4Property -export type ScalarPropertyArg = number | ScalarProperty -export type Vector2PropertyArg = Vector2 | Vector2Property -export type Vector3PropertyArg = Vector3 | Vector3Property -export type Vector4PropertyArg = Vector4 | Vector4Property -export type VectorPropertyArg = Vector | VectorProperty - -enum ModifierType { - /** - * Adds a random value between `-x` and `x` to the property's value, where - * `x` is the "max change" value set in the modifier's fields. - * - * There is one RNG seed field for each component of the property value - * followed by one "max change" value per component. - */ - Randomizer1 = 21, - /** - * Adds a random value between `x` and `y` to the property's value, where - * `x` and `y` are the min/max change values set in the modifier's fields. - * - * There is one RNG seed field for each component of the property value - * followed by one "min change" value per component, and then one "max - * change" value per component. - */ - Randomizer2 = 24, - /** - * Multiplies the property's value based on some external value. The only - * field in the modifier controls which external value to check. For example, - * if the field is set to 10000, the external value will be based on what the - * "Display Blood" option in the in-game settings is set to. - * - * The factor is controlled by the modifier's only property. The external - * value is given as the property function's argument, so a linear property - * can be used to change the factor based on the external value. - * - * This modifier type has two specialized subclasses: - * - {@link ExternalValueModifier} - * - {@link BloodVisibilityModifier} - */ - ExternalValue1 = 38, - /** - * Same as {@link ExternalValue1}, except this only updates if the effect is - * recreated instead of updating instantly when the external value changes. - * - * Note: This type seems to only work with the - * {@link ExternalValue.DisplayBlood DisplayBlood external value}. - */ - ExternalValue2 = 39, - /** - * Adds a random fraction of the property's value to itself. The range of the - * fraction is controlled by the the latter half of the modifier's fields, - * where, if `x` is the value of the field, the possible range of the - * fraction will be `-x` to `x`. - * - * There is one RNG seed field for each component of the property value - * followed by one "max change" value per component. - */ - Randomizer3 = 53, -} - -enum FieldType { - Boolean, - Integer, - Float, -} - -enum BlendMode { - Unk0 = 0, - Source = 1, - Normal = 2, - Multiply = 3, - Add = 4, - Subtract = 5, - Unk6 = 6, - Screen = 7, -} - -enum ExternalValue { - /** - * This value will be set to 1 when the effect is meant to end due to the - * source of the effect going away, for example when a fire pot explodes and - * disappears. The value is otherwise 0. - */ - Terminate = 0, - /** - * In Elden Ring, this value is 1 if it's raining or snowing, and 0 otherwise. - */ - Precipitation = 1, - /** - * In Elden Ring, this represents the the time of day. At midnight, the value - * is 0, at noon it is 12, and then it goes up to 24 before wrapping back to - * 0, just like the hours on the clock. - */ - TimeOfDay = 2, - /** - * Used in AC6. - */ - Unk3 = 3, - /** - * This is based on the distance between the SFX and the camera. - * - * The range is 0-1, the distance is converted in some unknown way. - * - * It does not always work for all sources of effects. This is used by the - * beacon effect in Elden Ring, so it definitely works there. - */ - SFXDistance = 1000, - /** - * This value is set through the Special Attribute param field on weapons. - */ - HitEffectVariation = 2000, - Unk2100 = 2100, // Blood related? - Unk2200 = 2200, // Blood related? - /** - * Based on the "Display Blood" setting. - * - Off: `-1` - * - On: `0` - * - Mild: `1` - * - * This external value has a specialized modifier subclass: - * {@link BloodVisibilityModifier} - */ - DisplayBlood = 10000, - Unk20000 = 20000, - /** - * Used in AC6. - */ - Unk40000 = 40000, - /** - * Used in AC6. - */ - Unk70000 = 70000, - /** - * Used in AC6. - */ - Unk70010 = 70010, - /** - * Used in AC6. - */ - Unk70020 = 70020, - /** - * Used in AC6. - */ - Unk70200 = 70200, -} - -enum Operator { - NotEqual = 0, - Equal = 1, - GreaterThanOrEqual = 2, - GreaterThan = 3, - - /* - These two are not part of the format. The StateCondition class will just - switch the operands around and use the greater than operators automatically - when these are used. - */ - LessThanOrEqual = 4, - LessThan = 5, -} - -enum OperandType { - /** - * The field's value, literally. - */ - Literal = -4, - /** - * Gets an external value. - * - * The field refers to an {@link ExternalValue}. - */ - External = -3, - /** - * Based on movement in some way? - * - * Does not require a field. - */ - UnkMinus2 = -2, - /** - * The time since the effect changed state in seconds. - * - * Does not require a field. - */ - StateTime = -1, -} - -enum AttachMode { - /** - * Completely detached. - */ - None = 0, - /** - * Translates and rotates with the parent. - */ - Parent = 1, - /** - * Translates and rotates with the attachment point (dummypoly). Parent transformations are ignored. - */ - DummyPoly = 2, - /** - * Only translates with the parent. Rotations are entirely ignored. - */ - ParentTranslation = 3, - /** - * Only translates with the attachment point (dummypoly). Rotations are entirely ignored. - */ - DummyPolyTranslation = 4 -} - -enum PropertyArgument { - /** - * A constant value, usually just 0. - */ - Constant, - /** - * Time in seconds since the particle was emitted. - */ - ParticleAge, - /** - * Time in seconds since the {@link Effect} became active. - * - * An effect becoming active is for example the delay from - * {@link ActionType.NodeAttributes NodeAttributes} being over, or the active - * {@link State} changing, making a node change which of its effects is - * active. - */ - EffectAge, - /** - * Time in seconds between the effect being created and the particle being - * emitted. Stays constant per particle. - */ - EmissionTime -} - -enum OrientationMode { - /** - * Faces south. - * - * See also: - * - {@link UnkSouth} - */ - South = 0, - /** - * Faces the camera plane. - * - * See also: - * - {@link Camera} - */ - CameraPlane = 1, - /** - * Faces the -Z direction of the parent node. - */ - ParentNegativeZ = 2, - /** - * Faces south. - * - * Similar to {@link South}, but this seems to change the projection of the - * particle in some way. - */ - UnkSouth = 3, - /** - * Tries to face the camera, but is limited to rotation around the vertical - * axis. - */ - GlobalYaw = 4, - /** - * Faces east. - */ - East = 5, - /** - * Faces the camera. - * - * This is different from {@link CameraPlane}, as this makes it face the - * camera's position instead of the camera plane. - */ - Camera = 6, - /** - * Tries to face the camera, but is limited to rotation around the Y axis of - * the parent node. - */ - ParentYaw = 7, -} - -enum TracerOrientationMode { - /** - * The tracer source is perpendicular to the direction it's travelling and - * the direction of the camera. - */ - Travel = 0, - /** - * The tracer source is aligned with the local Z-axis, which is detenmined - * by the rotation of the node that emits the tracer. - */ - LocalZ = 1, - /** - * The tracer source is aligned with the global vertical axis. - */ - Vertical = 2, - /** - * The tracer source is aligned with the global X-axis. - */ - GlobalX = 3, - /** - * Creates two sources for the tracer with different orientation modes. One - * has {@link Vertical} and the other has {@link GlobalX}, forming a cross. - */ - Cross = 4, - /** - * The tracer source is parallel to the global diagonal (1, 1, 1). - */ - Diagonal = 5, -} - -enum LightingMode { - /** - * Same as {@link Lit}, but this seems to sometimes have an extra light - * source from somewhere? - */ - UnkMinus2 = -2, - /** - * Lighting does not affect the particles. No shadows or specular - * hightlights. - */ - Unlit = -1, - /** - * Lighting affects the particles just like most regular objects. - */ - Lit = 0, -} - -/** - * Used by {@link ActionType.Distortion distortion} particles to control what - * type of distortion to apply. - */ -enum DistortionMode { - /** - * Distorts the background as if you stuck something into it and stirred it. - * It is animated, and the stir speed is controlled by a property. - */ - Stir = 0, - /** - * Distorts the background based on the normal map. - */ - NormalMap = 1, - /** - * Distorts the background as if the edges were held in place and you grabbed - * the center and twisted it. - */ - Twist = 2, - /** - * Seemingly identical to {@link NormalMap}? - */ - Unk3 = 3, - /** - * This seems to just squeeze everything to the bottom left corner? - */ - Unk4 = 4, -} - -/** - * Possible shapes for {@link ActionType.Distortion distortion} particles. - */ -enum DistortionShape { - /** - * A flat rectangle. - */ - Rectangle = 0, - /** - * Half of an ellipsoid. (Like a hemisphere, but with three different radii.) - */ - Hemiellipsoid = 1, - /** - * An ellipsoid. (Like a sphere, but with three different radii.) - */ - Ellipsoid = 2, -} - -const EffectActionSlots = { - [EffectType.Basic]: [ - [ - ActionType.NodeAttributes - ], - [ - ActionType.StaticNodeTransform, - ActionType.RandomNodeTransform - ], - [ - ActionType.NodeAcceleration, - ActionType.NodeTranslation, - ActionType.NodeSpin, - ActionType.NodeAttachToCamera, - ActionType.NodeAccelerationRandomTurns, - ActionType.NodeAccelerationPartialFollow, - ActionType.NodeAccelerationSpin, - ActionType.NodeSpeed, - ActionType.NodeSpeedRandomTurns, - ActionType.NodeSpeedPartialFollow, - ActionType.NodeSpeedSpin - ], - [ - ActionType.PlaySound - ], - [ - ActionType.PeriodicEmitter, - ActionType.EqualDistanceEmitter, - ActionType.OneTimeEmitter - ], - [ - ActionType.PointEmitterShape, - ActionType.DiskEmitterShape, - ActionType.RectangleEmitterShape, - ActionType.SphereEmitterShape, - ActionType.BoxEmitterShape, - ActionType.CylinderEmitterShape - ], - [ - ActionType.NoParticleSpread, - ActionType.CircularParticleSpread, - ActionType.EllipticalParticleSpread, - ActionType.RectangularParticleSpread - ], - [ - ActionType.ParticleMultiplier - ], - [ - ActionType.ParticleAttributes - ], - [ - ActionType.PointSprite, - ActionType.Line, - ActionType.QuadLine, - ActionType.BillboardEx, - ActionType.MultiTextureBillboardEx, - ActionType.Model, - ActionType.Tracer, - ActionType.Distortion, - ActionType.RadialBlur, - ActionType.PointLight, - ActionType.Unk10000_StandardParticle, - ActionType.Unk10001_StandardCorrectParticle, - ActionType.Unk10002_Fluid, - ActionType.Unk10003_LightShaft, - ActionType.Unk10008_SparkParticle, - ActionType.Unk10009_SparkCorrectParticle, - ActionType.Unk10010_Tracer, - ActionType.Unk10012_Tracer, - ActionType.WaterInteraction, - ActionType.Unk10014_LensFlare, - ActionType.Unk10015_RichModel, - ActionType.Unk10200_ForceFieldCancelArea, - ActionType.Unk10300_ForceFieldWindArea, - ActionType.Unk10301_ForceFieldGravityArea, - ActionType.Unk10302_CollisionFieldArea, - ActionType.Unk10303_ForceFieldTurbulenceArea, - ActionType.SpotLight - ], - [ - ActionType.ParticleAcceleration, - ActionType.ParticleSpeed, - ActionType.ParticleSpeedRandomTurns, - ActionType.ParticleSpeedPartialFollow, - ActionType.ParticleAccelerationRandomTurns, - ActionType.ParticleAccelerationPartialFollow - ], - [], - [ - ActionType.Unk130 - ], - [ - ActionType.NodeWindSpeed, - ActionType.NodeWindAcceleration - ], - [ - ActionType.ParticleWindSpeed, - ActionType.ParticleWindAcceleration, - ActionType.Unk800 - ] - ], - [EffectType.SharedEmitter]: [ - [ - ActionType.NodeAttributes - ], - [ - ActionType.StaticNodeTransform, - ActionType.RandomNodeTransform - ], - [ - ActionType.NodeAcceleration, - ActionType.NodeTranslation, - ActionType.NodeSpin, - ActionType.NodeAttachToCamera, - ActionType.NodeAccelerationRandomTurns, - ActionType.NodeAccelerationPartialFollow, - ActionType.NodeAccelerationSpin, - ActionType.NodeSpeed, - ActionType.NodeSpeedRandomTurns, - ActionType.NodeSpeedSpin - ], - [ - ActionType.PlaySound - ], - [ - ActionType.PeriodicEmitter, - ActionType.EqualDistanceEmitter, - ActionType.OneTimeEmitter - ], - [ - ActionType.PointEmitterShape, - ActionType.DiskEmitterShape, - ActionType.RectangleEmitterShape, - ActionType.SphereEmitterShape, - ActionType.BoxEmitterShape, - ActionType.CylinderEmitterShape - ], - [ - ActionType.NoParticleSpread, - ActionType.CircularParticleSpread, - ActionType.EllipticalParticleSpread, - ActionType.RectangularParticleSpread - ], - [ - ActionType.EmitAllParticles, - ActionType.EmitRandomParticles - ], - [], - [ - ActionType.NodeWindSpeed, - ActionType.NodeWindAcceleration - ] - ] -} - -function arrayOf(size: number, func: (index: number) => T): T[] { - return Array(size).fill(null).map((e, i) => func(i)) -} - -function randomInt32() { - return Math.random() * 2**32 | 0 -} - -function setPropertyInList(list: Property[], index: number, value: Property | PropertyValue) { - if (value instanceof Property) { - list[index] = value - } else if (typeof value === 'number') { - list[index] = new ConstantProperty(value) - } else { - list[index] = new ConstantProperty(...value) - } -} - -function scalarFromArg(scalar: ScalarPropertyArg) { - return scalar instanceof Property ? scalar : new ConstantProperty(scalar) -} - -function vectorFromArg(vector: VectorPropertyArg) { - return vector instanceof Property ? vector : new ConstantProperty(...vector) -} - -function uniqueArray(a: T[]) { - return a.filter((e, i) => a.indexOf(e) === i) -} - -function lerp(a: number, b: number, c: number) { - return a * (1 - c) + b * c -} - -function readProperty | IModifiableProperty>( - br: BinaryReader, - modifierProp: T extends IModifiableProperty ? false : true -): T { - const typeEnumA = br.readInt16() - br.assertUint8(0) - br.assertUint8(1) - const type: ValueType = typeEnumA & 0b00000000_00000011 - const func: PropertyFunction = (typeEnumA & 0b00000000_11110000) >>> 4 - const loop: boolean = !!((typeEnumA & 0b00010000_00000000) >>> 12) - br.readInt32() // TypeEnumB - const count = br.readInt32() - br.assertInt32(0) - const offset = br.readInt32() - br.assertInt32(0) - const modifiers = [] - if (!modifierProp) { - const modOffset = br.readInt32() - br.assertInt32(0) - const modCount = br.readInt32() - br.assertInt32(0) - br.stepIn(modOffset) - for (let i = 0; i < modCount; ++i) { - modifiers.push(Modifier.read(br)) - } - br.stepOut() - } - const fields = Field.readManyAt(br, offset, count, this).map(f => f.value) - switch (func) { - case PropertyFunction.Zero: - case PropertyFunction.One: - case PropertyFunction.Constant: - return ValueProperty.fromFields(type, func, modifiers, fields) as unknown as T - case PropertyFunction.Stepped: - case PropertyFunction.Linear: - case PropertyFunction.Curve1: - case PropertyFunction.Curve2: - return KeyframeProperty.fromFields(type, func, loop, modifiers, fields) as unknown as T - case PropertyFunction.CompCurve: - return ComponentKeyframeProperty.fromFields(type, loop, modifiers, fields) as unknown as T - default: - throw new Error('Unknown property function: ' + func) - } -} - -function writeProperty(prop: IProperty, bw: BinaryWriter, properties: IProperty[], modifierProp: boolean) { - const count = properties.length - const typeEnumA = prop.valueType | prop.function << 4 | +('loop' in prop ? prop.loop : false) << 12 - const typeEnumB = prop.valueType | prop.function << 2 | +('loop' in prop ? prop.loop : false) << 4 - bw.writeInt16(typeEnumA) - bw.writeUint8(0) - bw.writeUint8(1) - bw.writeInt32(typeEnumB) - bw.writeInt32(prop.fieldCount) - bw.writeInt32(0) - bw.reserveInt32(`${modifierProp ? 'Property' : 'ModifiableProperty'}FieldsOffset${count}`) - bw.writeInt32(0) - if (!modifierProp) { - bw.reserveInt32(`PropertyModifiersOffset${count}`) - bw.writeInt32(0) - bw.writeInt32((prop as IModifiableProperty).modifiers.length) - bw.writeInt32(0) - } - properties.push(prop) -} - -function writePropertyModifiers(prop: IModifiableProperty, bw: BinaryWriter, index: number, modifiers: Modifier[]) { - bw.fill(`PropertyModifiersOffset${index}`, bw.position) - for (const modifier of prop.modifiers) { - modifier.write(bw, modifiers) - } -} - -function writePropertyFields(prop: IProperty, bw: BinaryWriter, index: number, modifierProp: boolean): number { - const offsetName = `${modifierProp ? 'Property' : 'ModifiableProperty'}FieldsOffset${index}` - const fieldCount = prop.fieldCount - if (fieldCount === 0) { - bw.fill(offsetName, 0) - } else { - bw.fill(offsetName, bw.position) - for (const field of prop.fields) { - field.write(bw) - } - } - return fieldCount -} - -class BinaryReader extends DataView { - - position: number = 0 - littleEndian: boolean = true - steps: number[] = [] - - getInt16(offset: number) { - return super.getInt16(offset, this.littleEndian) - } - - getUint16(offset: number) { - return super.getUint16(offset, this.littleEndian) - } - - getInt32(offset: number) { - return super.getInt32(offset, this.littleEndian) - } - - getUint32(offset: number) { - return super.getUint32(offset, this.littleEndian) - } - - getFloat32(offset: number) { - // Rounding to get rid of most errors from precision loss - return Math.round(super.getFloat32(offset, this.littleEndian) * 1e7) / 1e7 - } - - getFloat64(offset: number) { - return super.getFloat64(offset, this.littleEndian) - } - - readUint8() { - return this.getUint8(this.position++) - } - - readBool() { - const b = this.readUint8() - if (b <= 1) { - return !!b - } else { - throw new Error(`readBool encountered non-boolean value: 0x${b.toString(16).padStart(2, '0')}`) - } - } - - readInt16() { - const value = this.getInt16(this.position) - this.position += 2 - return value - } - - readUint16() { - const value = this.getUint16(this.position) - this.position += 2 - return value - } - - readInt32() { - const value = this.getInt32(this.position) - this.position += 4 - return value - } - - readUint32() { - const value = this.getUint32(this.position) - this.position += 4 - return value - } - - readFloat32() { - const value = this.getFloat32(this.position) - this.position += 4 - return value - } - - getInt32s(offset: number, count: number) { - return arrayOf(count, i => this.getInt32(offset + i * 4)) - } - - assertUint8(...ui8s: number[]) { - const ocp = this.position - const value = this.readUint8() - for (const ui8 of ui8s) { - if (value === ui8) return value - } - throw new Error(`Read: ${value} | Expected: ${ui8s.join(', ')} | Position: ${ocp}`) - } - - assertInt16(...i16s: number[]) { - const ocp = this.position - const value = this.readInt16() - for (const i16 of i16s) { - if (value === i16) return value - } - throw new Error(`Read: ${value} | Expected: ${i16s.join(', ')} | Position: ${ocp}`) - } - - assertUint16(...ui16s: number[]) { - const ocp = this.position - const value = this.readUint16() - for (const ui16 of ui16s) { - if (value === ui16) return value - } - throw new Error(`Read: ${value} | Expected: ${ui16s.join(', ')} | Position: ${ocp}`) - } - - assertInt32(...i32s: number[]) { - const ocp = this.position - const value = this.readInt32() - for (const i32 of i32s) { - if (value === i32) return value - } - throw new Error(`Read: ${value} | Expected: ${i32s.join(', ')} | Position: ${ocp}`) - } - - assertUint32(...ui32s: number[]) { - const ocp = this.position - const value = this.readUint32() - for (const ui32 of ui32s) { - if (value === ui32) return value - } - throw new Error(`Read: ${value} | Expected: ${ui32s.join(', ')} | Position: ${ocp}`) - } - - assertASCII(ascii: string) { - const ocp = this.position - const value: string = String.fromCharCode.apply(null, ascii.split('').map(() => this.readUint8())) - if (value !== ascii) { - throw new Error(`Read: ${value} | Expected: ${ascii} | Position: ${ocp}`) - } - return value - } - - stepIn(offset: number) { - this.steps.push(this.position) - this.position = offset - } - - stepOut() { - if (this.steps.length === 0) { - throw new Error('Reader is already stepped all the way out.') - } - this.position = this.steps.pop() ?? 0 - } - -} - -interface ReservationList { - [name: string]: { offset: number, size: 1 | 2 | 4, func?: string } -} - -class BinaryWriter { - - static #te = new TextEncoder - - littleEndian: boolean - array: number[] = [] - reservations: ReservationList = {} - - #transBuf = new ArrayBuffer(4) - #transDV = new DataView(this.#transBuf) - #transArr16 = new Int8Array(this.#transBuf, 0, 2) - #transArr32 = new Int8Array(this.#transBuf, 0, 4) - - constructor(littleEndian: boolean = true) { - this.littleEndian = littleEndian - } - - get position() { - return this.array.length - } - - writeBool(b: boolean) { - this.array.push(+b) - } - - writeInt8(i8: number) { - this.array.push(i8) - } - - writeUint8(ui8: number) { - this.array.push(ui8) - } - - writeInt16(i16: number) { - this.#transDV.setInt16(0, i16, this.littleEndian) - this.array.push(...this.#transArr16) - } - - writeUint16(ui16: number) { - this.#transDV.setUint16(0, ui16, this.littleEndian) - this.array.push(...this.#transArr16) - } - - writeInt32(i32: number) { - this.#transDV.setInt32(0, i32, this.littleEndian) - this.array.push(...this.#transArr32) - } - - writeUint32(ui32: number) { - this.#transDV.setUint32(0, ui32, this.littleEndian) - this.array.push(...this.#transArr32) - } - - writeFloat32(f32: number) { - // Make sure that the value is not -0 - f32 = f32 === 0 ? 0 : f32 - this.#transDV.setFloat32(0, f32, this.littleEndian) - this.array.push(...this.#transArr32) - } - - writeInt8s(i8s: number[]) { - for (const i8 of i8s) this.writeInt8(i8) - } - - writeUint8s(ui8s: number[]) { - for (const ui8 of ui8s) this.writeUint8(ui8) - } - - writeInt16s(i16s: number[]) { - for (const i16 of i16s) this.writeInt16(i16) - } - - writeUint16s(ui16s: number[]) { - for (const ui16 of ui16s) this.writeUint16(ui16) - } - - writeInt32s(i32s: number[]) { - for (const i32 of i32s) this.writeInt32(i32) - } - - writeUint32s(ui32s: number[]) { - for (const ui32 of ui32s) this.writeUint32(ui32) - } - - writeFloat32s(f32s: number[]) { - for (const f32 of f32s) this.writeFloat32(f32) - } - - writeString(s: string) { - this.array.push(...BinaryWriter.#te.encode(s)) - } - - reserveInt8(name: string) { - this.reservations[name] = { - offset: this.array.length, - size: 1 - } - this.writeInt8(0) - } - - reserveUint8(name: string) { - this.reservations[name] = { - offset: this.array.length, - size: 1 - } - this.writeUint8(0) - } - - reserveInt16(name: string) { - this.reservations[name] = { - offset: this.array.length, - size: 2, - func: 'setInt16' - } - this.writeInt16(0) - } - - reserveUint16(name: string) { - this.reservations[name] = { - offset: this.array.length, - size: 2, - func: 'setUint16' - } - this.writeUint16(0) - } - - reserveInt32(name: string) { - this.reservations[name] = { - offset: this.array.length, - size: 4, - func: 'setInt32' - } - this.writeInt32(0) - } - - reserveUint32(name: string) { - this.reservations[name] = { - offset: this.array.length, - size: 4, - func: 'setUint32' - } - this.writeUint32(0) - } - - reserveFloat32(name: string) { - this.reservations[name] = { - offset: this.array.length, - size: 4, - func: 'setFloat32' - } - this.writeFloat32(0) - } - - fill(name: string, value: number) { - if (!(name in this.reservations)) { - throw new Error('Key is not reserved: ' + name) - } - const reservation = this.reservations[name] - switch (reservation.size) { - case 1: { - this.array.splice(reservation.offset, 1, value) - break - } - case 2: { - this.#transDV[reservation.func](0, value, this.littleEndian) - this.array.splice(reservation.offset, 2, ...this.#transArr16) - break - } - case 4: { - this.#transDV[reservation.func](0, value, this.littleEndian) - this.array.splice(reservation.offset, 4, ...this.#transArr32) - break - } - } - delete this.reservations[name] - } - - pad(align: number) { - while (this.array.length % align > 0) this.writeInt8(0) - } - - getArrayBuffer() { - return new Uint8Array(this.array).buffer - } - -} - -class FXR { - - id: number - version: FXRVersion - - states: State[] - root: RootNode - - references: number[] - externalValues: number[] - unkBloodEnabler: number[] - // unkEmpty: number[] - - /** - * Creates a new effects resource (FXR) for FromSoftware's game engine. - */ - constructor( - id: number, - version = FXRVersion.Sekiro, - root: RootNode = new RootNode, - states: State[] = [ new State ], - references: number[] = [], - externalValues: number[] = [], - unkBloodEnabler: number[] = [], - // unkEmpty: number[] = [], - ) { - this.id = id - this.version = version - this.root = root - this.states = states - this.references = references - this.externalValues = externalValues - this.unkBloodEnabler = unkBloodEnabler - // this.unkEmpty = unkEmpty - } - - /** - * Parses an FXR file. - * @param buffer ArrayBuffer containing the contents of the FXR file. - */ - static read(buffer: ArrayBuffer, version: FXRVersion = null) { - const br = new BinaryReader(buffer) - - br.assertASCII('FXR\0') - br.assertInt16(0) - if (version !== null) { - br.assertInt16( - FXRVersion.DarkSouls3, - FXRVersion.Sekiro - ) - } else { - version = br.assertInt16( - FXRVersion.DarkSouls3, - FXRVersion.Sekiro - ) - } - br.assertInt32(1) - const id = br.readInt32() - const stateListOffset = br.readInt32() - br.assertInt32(1) // StateMachineCount - br.position += 4 * 4 - // br.readInt32() // StateOffset - // br.readInt32() // StateCount - // br.readInt32() // ConditionOffset - // br.readInt32() // ConditionCount - const nodeOffset = br.readInt32() - br.position += 15 * 4 - // br.readInt32() // NodeCount - // br.readInt32() // EffectOffset - // br.readInt32() // EffectCount - // br.readInt32() // ActionOffset - // br.readInt32() // ActionCount - // br.readInt32() // PropertyOffset - // br.readInt32() // PropertyCount - // br.readInt32() // Section8Offset - // br.readInt32() // Section8Count - // br.readInt32() // Section9Offset - // br.readInt32() // Section9Count - // br.readInt32() // Section10Offset - // br.readInt32() // Section10Count - // br.readInt32() // FieldOffset - // br.readInt32() // FieldCount - br.assertInt32(1) - br.assertInt32(0) - - let references: number[] = [] - let externalValues: number[] = [] - let unkBloodEnabler: number[] = [] - // let unkEmpty: number[] = [] - - if (version === FXRVersion.Sekiro) { - const referencesOffset = br.readInt32() - const referencesCount = br.readInt32() - const externalValuesOffset = br.readInt32() - const externalValuesCount = br.readInt32() - const unkBloodEnablerOffset = br.readInt32() - const unkBloodEnablerCount = br.readInt32() - br.readInt32() - br.assertInt32(0) - // const unkEmptyOffset = br.readInt32() - // const unkEmptyCount = br.readInt32() - - references = br.getInt32s(referencesOffset, referencesCount) - externalValues = br.getInt32s(externalValuesOffset, externalValuesCount) - unkBloodEnabler = br.getInt32s(unkBloodEnablerOffset, unkBloodEnablerCount) - // unkEmpty = br.getInt32s(unkEmptyOffset, unkEmptyCount) - } - - br.position = stateListOffset - br.assertInt32(0) - const stateCount = br.readInt32() - const statesOffset = br.readInt32() - br.assertInt32(0) - br.stepIn(statesOffset) - const states: State[] = [] - for (let i = 0; i < stateCount; ++i) { - states.push(State.read(br)) - } - br.stepOut() - - br.position = nodeOffset - const rootNode = Node.read(br, version) as RootNode - - return new FXR( - id, - version, - rootNode, - states, - references, - externalValues, - unkBloodEnabler, - // unkEmpty, - ) - } - - /** - * Serialize to the FXR file format. - * @returns ArrayBuffer containing the contents of the FXR file. - */ - toArrayBuffer() { - const bw = new BinaryWriter - bw.writeString('FXR\0') - bw.writeInt16(0) - bw.writeUint16(this.version) - bw.writeInt32(1) - bw.writeInt32(this.id) - bw.reserveInt32('StateListOffset') - bw.writeInt32(1) - bw.reserveInt32('StatesOffset1') - bw.writeInt32(this.states.length) - bw.reserveInt32('ConditionOffset') - bw.reserveInt32('ConditionCount') - bw.reserveInt32('NodeOffset') - bw.reserveInt32('NodeCount') - bw.reserveInt32('EffectOffset') - bw.reserveInt32('EffectCount') - bw.reserveInt32('ActionOffset') - bw.reserveInt32('ActionCount') - bw.reserveInt32('PropertyOffset') - bw.reserveInt32('PropertyCount') - bw.reserveInt32('Section8Offset') - bw.reserveInt32('Section8Count') - bw.reserveInt32('Section9Offset') - bw.reserveInt32('Section9Count') - bw.reserveInt32('Section10Offset') - bw.reserveInt32('Section10Count') - bw.reserveInt32('FieldOffset') - bw.reserveInt32('FieldCount') - bw.writeInt32(1) - bw.writeInt32(0) - - if (this.version === FXRVersion.Sekiro) { - bw.reserveInt32('ReferencesOffset') - bw.writeInt32(this.references.length) - bw.reserveInt32('ExternalValuesOffset') - bw.writeInt32(this.externalValues.length) - bw.reserveInt32('UnkBloodEnablerOffset') - bw.writeInt32(this.unkBloodEnabler.length) - // bw.reserveInt32('UnkEmptyOffset') - // bw.writeInt32(this.unkEmpty.length) - bw.writeInt32(0) - bw.writeInt32(0) - } - - bw.fill('StateListOffset', bw.position) - bw.writeInt32(0) - bw.writeInt32(this.states.length) - bw.reserveInt32('StatesOffset2') - bw.writeInt32(0) - bw.pad(16) - bw.fill('StatesOffset1', bw.position) - bw.fill('StatesOffset2', bw.position) - for (let i = 0; i < this.states.length; ++i) { - this.states[i].write(bw, i) - } - - bw.pad(16) - bw.fill('ConditionOffset', bw.position) - const conditions: StateCondition[] = [] - for (let i = 0; i < this.states.length; ++i) { - this.states[i].writeConditions(bw, i, conditions) - } - bw.fill('ConditionCount', conditions.length) - bw.pad(16) - bw.fill('NodeOffset', bw.position) - const nodes: Node[] = [] - this.root.write(bw, nodes) - this.root.writeNodes(bw, nodes) - bw.fill('NodeCount', nodes.length) - bw.pad(16) - bw.fill('EffectOffset', bw.position) - let counter = { value: 0 } - for (let i = 0; i < nodes.length; ++i) { - nodes[i].writeEffects(bw, i, counter) - } - bw.fill('EffectCount', counter.value) - bw.pad(16) - bw.fill('ActionOffset', bw.position) - counter.value = 0 - const actions: Action[] = [] - for (let i = 0; i < nodes.length; ++i) { - nodes[i].writeActions(bw, i, counter, actions) - } - bw.fill('ActionCount', actions.length) - bw.pad(16) - bw.fill('PropertyOffset', bw.position) - const properties: IModifiableProperty[] = [] - for (let i = 0; i < actions.length; ++i) { - actions[i].writeProperties(bw, i, properties) - } - bw.fill('PropertyCount', properties.length) - bw.pad(16) - bw.fill('Section8Offset', bw.position) - const modifiers: Modifier[] = [] - for (let i = 0; i < properties.length; ++i) { - writePropertyModifiers(properties[i], bw, i, modifiers) - } - bw.fill('Section8Count', modifiers.length) - bw.pad(16) - bw.fill('Section9Offset', bw.position) - const modProps: Property[] = [] - for (let i = 0; i < modifiers.length; ++i) { - modifiers[i].writeProperties(bw, i, modProps) - } - bw.fill('Section9Count', modProps.length) - bw.pad(16) - bw.fill('Section10Offset', bw.position) - const section10s: Section10[] = [] - for (let i = 0; i < actions.length; ++i) { - actions[i].writeSection10s(bw, i, section10s) - } - bw.fill('Section10Count', section10s.length) - bw.pad(16) - bw.fill('FieldOffset', bw.position) - let fieldCount = 0 - for (let i = 0; i < conditions.length; ++i) { - fieldCount += conditions[i].writeFields(bw, i) - } - for (let i = 0; i < actions.length; ++i) { - fieldCount += actions[i].writeFields(bw, i) - } - for (let i = 0; i < properties.length; ++i) { - fieldCount += writePropertyFields(properties[i], bw, i, false) - } - for (let i = 0; i < modifiers.length; ++i) { - fieldCount += modifiers[i].writeFields(bw, i) - } - for (let i = 0; i < modProps.length; ++i) { - fieldCount += writePropertyFields(modProps[i], bw, i, true) - } - for (let i = 0; i < section10s.length; ++i) { - fieldCount += section10s[i].writeFields(bw, i) - } - bw.fill('FieldCount', fieldCount) - bw.pad(16) - - if (this.version !== FXRVersion.Sekiro) { - return bw.getArrayBuffer() - } - - bw.fill('ReferencesOffset', bw.position) - bw.writeInt32s(this.references) - bw.pad(16) - - bw.fill('ExternalValuesOffset', bw.position) - bw.writeInt32s(this.externalValues) - bw.pad(16) - - bw.fill('UnkBloodEnablerOffset', bw.position) - bw.writeInt32s(this.unkBloodEnabler) - bw.pad(16) - - // if (this.unkEmpty.length > 0) { - // bw.fill('UnkEmptyOffset', bw.position) - // bw.writeInt32s(this.unkEmpty) - // bw.pad(16) - // } else { - // bw.fill('UnkEmptyOffset', 0) - // } - - return bw.getArrayBuffer() - } - - static fromJSON({ - id, - version, - states, - root, - references, - externalValues, - unkBloodEnabler - }: { - id: number - version: string - states: string[] - root: any - references: number[] - externalValues: number[] - unkBloodEnabler: number[] - unkEmpty: number[] - }) { - return new FXR( - id, - FXRVersion[version], - Node.fromJSON(root), - states.map(state => State.from(state)), - references, - externalValues, - unkBloodEnabler - ) - } - - toJSON() { - return { - id: this.id, - version: FXRVersion[this.version], - states: this.states.map(state => state.toString()), - references: this.references, - externalValues: this.externalValues, - unkBloodEnabler: this.unkBloodEnabler, - root: this.root.toJSON(), - } - } - - /** - * Creates a minified version of this FXR. - * - * The minified FXR might result in a smaller file size, but should be - * functionally identical to the FXR it was made from. - */ - minify() { - return new FXR( - this.id, - this.version, - this.root.minify(), - this.states, - this.references, - this.externalValues, - this.unkBloodEnabler - ) - } - - /** - * Automatically updates {@link references}, {@link externalValues}, and - * {@link unkBloodEnabler} with the values used in the FXR. - */ - updateReferences() { - const references: number[] = [] - const externalValues: number[] = [] - let unkBloodEnabler = false - for (const node of this.root.walk()) { - if (node.type === NodeType.Proxy) { - references.push(node.actions[0].fields1[0].value as number) - } - } - for (const prop of this.root.walkProperties()) { - for (const mod of prop.modifiers) { - if (mod.type === ModifierType.ExternalValue1) { - externalValues.push(mod.fields[0].value as number) - } else if (mod.type === ModifierType.ExternalValue2 && mod.fields[0].value === ExternalValue.DisplayBlood) { - unkBloodEnabler = true - } - } - } - for (const state of this.states) { - for (const condition of state.conditions) { - if (condition.leftOperandType === OperandType.External) { - externalValues.push(condition.leftOperandValue) - } - if (condition.rightOperandType === OperandType.External) { - externalValues.push(condition.rightOperandValue) - } - } - } - this.references = uniqueArray(references).sort((a, b) => a - b) - this.externalValues = uniqueArray(externalValues).sort((a, b) => a - b) - this.unkBloodEnabler = unkBloodEnabler ? [ExternalValue.DisplayBlood] : [] - return this - } - -} - -class State { - - conditions: StateCondition[] - - constructor(conditions: StateCondition[] = []) { - this.conditions = conditions - } - - /** - * Parses a logical expression in a string and creates a - * {@link State} from it. - * @param stateString A logical expression comprised of one or more - * conditions separated by `&&`. The state may only be active if all of its - * conditions are true. - * - * Syntax: - * ```text - * stateString = [ && [...]] - * ``` - * See {@link StateCondition.from} for more information about - * `conditionExpression`. - * - * Examples: - * ```text - * time < 0.5 else 1 && ext(2000) == 2 - * ext(0) < 1 && time < 2 && 1 == 1 - * ``` - * @returns The new state. - */ - static from(stateString: string) { - return new State(stateString.split('&&').filter(e => e.trim().length > 0).map(e => StateCondition.from(e))) - } - - static read(br: BinaryReader) { - br.assertInt32(0) - const count = br.readInt32() - const offset = br.readInt32() - br.assertInt32(0) - br.stepIn(offset) - const conditions = [] - for (let i = 0; i < count; ++i) { - conditions.push(StateCondition.read(br)) - } - br.stepOut() - return new State(conditions) - } - - write(bw: BinaryWriter, index: number) { - bw.writeInt32(0) - bw.writeInt32(this.conditions.length) - bw.reserveInt32(`StateConditionsOffset${index}`) - bw.writeInt32(0) - } - - writeConditions(bw: BinaryWriter, index: number, conditions: StateCondition[]) { - bw.fill(`StateConditionsOffset${index}`, bw.position) - for (const condition of this.conditions) { - condition.write(bw, conditions) - } - } - - toString() { - return this.conditions.map(c => c.toString()).join(' && ') - } - -} - -class StateCondition { - - /** - * A condition for a state. The state remains active if all of its conditions - * are true or if it has no conditions. If the condition is false, the state - * is deactivated and the {@link nextState next state} is activated. - * @param operator Controls what operation should be used for the condition. - * @param unk1 Unknown. Seems to always be 2 in vanilla Elden Ring. 3 seems - * to make the condition always true. - * @param nextState If the condition is false, the state at this index will - * be checked instead. Set it to -1 to disable the node if the condition - * is false. - * @param leftOperandType Controls what type of value the operand to the left - * of the operator should be. - * @param leftOperandValue This does different things depending on the - * {@link leftOperandType}: - * - {@link OperandType.Literal}: This value is the operand's value. - * - {@link OperandType.External}: This value refers to an external value to - * use as the operand's value. - * - {@link OperandType.UnkMinus2}: This value is ignored and should be null. - * - {@link OperandType.StateTime}: This value is ignored and should be null. - * @param rightOperandType Controls what type of value the operand to the - * right of the operator should be. - * @param rightOperandValue This does different things depending on the - * {@link rightOperandType}: - * - {@link OperandType.Literal}: This value is the operand's value. - * - {@link OperandType.External}: This value refers to an external value to - * use as the operand's value. - * - {@link OperandType.UnkMinus2}: This value is ignored and should be null. - * - {@link OperandType.StateTime}: This value is ignored and should be null. - */ - constructor( - public operator: Operator, - public unk1: number, - public nextState: number, - public leftOperandType: OperandType, - public leftOperandValue: number | null, - public rightOperandType: OperandType, - public rightOperandValue: number | null, - ) {} - - static #reExpression = /^\s*(?(?:state)?time|(?:unk)?minus2|ext(?:ernal)?\(\d+\)|-?\d+(?:\.\d+)?|-?\.\d+)\s*(?==?|<=?|>=?|!=)\s*(?(?:state)?time|(?:unk)?minus2|ext(?:ernal)?\(\d+\)|-?\d+(?:\.\d+)?|-?\.\d+)\s*(?:else(?:\sgoto)?\s+(?-?\d+|none))?\s*$/i - static #reLiteralOperand = /^-?\d+(?:\.\d+)?|-?\.\d+$/ - static #reExternalOperand = /^[Ee]xt(?:ernal)?\((\d+)\)$/ - - static #parseOperand(op: string) { - switch (op.toLowerCase()) { - case 'time': - case 'statetime': - return { - type: OperandType.StateTime, - value: null - } - case 'minus2': - case 'unkminus2': - return { - type: OperandType.UnkMinus2, - value: null - } - default: if (this.#reLiteralOperand.test(op)) { - return { - type: OperandType.Literal, - value: parseFloat(op) - } - } else { - return { - type: OperandType.External, - value: parseInt(op.match(this.#reExternalOperand)[1]) - } - } - } - } - - /** - * Parses a logical expression in a string and creates a - * {@link StateCondition} from it. - * @param expression A string with a logical expression and optionally an - * `else` statement with a state index. - * - * ## Syntax: - * ```text - * expression = [ else[ goto] ] - * operand = | External() | StateTime | UnkMinus2 - * operator = != | == | > | >= | < | <= - * stateIndex = | none - * ``` - * - * `External`, `StateTime`, and `UnkMinus2` are all case-insensitive and have - * shorter variations available. Here are some examples: - * ```text - * ext(0) - * stateTime - * time - * minus2 - * ``` - * - * ## Examples: - * ```text - * ext(0) > 1 - * time < 5 else goto 2 - * 1 != External(10000) else 1 - * ``` - * - * @returns A new {@link StateCondition} based on the expression. - */ - static from(expression: string) { - const m = expression.match(this.#reExpression) - if (m === null) { - throw new Error('Syntax error in condition expression: ' + expression) - } - let op: Operator - switch (m.groups.op) { - case '!=': op = Operator.NotEqual; break; - case '=': - case '==': op = Operator.Equal; break; - case '<': op = Operator.LessThan; break; - case '>': op = Operator.GreaterThan; break; - case '<=': op = Operator.LessThanOrEqual; break; - case '>=': op = Operator.GreaterThanOrEqual; break; - } - const left = this.#parseOperand(m.groups.left) - const right = this.#parseOperand(m.groups.right) - let nextState = -1 - if ('else' in m.groups) { - switch (m.groups.else) { - case '-1': - case 'none': - case undefined: - break - default: - nextState = parseInt(m.groups.else) - break - } - } - return new StateCondition(op, 2, nextState, left.type, left.value, right.type, right.value) - } - - static read(br: BinaryReader) { - const bf1 = br.readInt16() - const operator = bf1 & 0b11 - const unk1 = (bf1 & 0b1100) >>> 2 - br.assertUint8(0) - br.assertUint8(1) - br.assertInt32(0) - const targetStateIndex = br.readInt32() - br.assertInt32(0) - const leftOperand = br.assertInt16(-4, -3, -2, -1) - br.assertUint8(0) - br.assertUint8(1) - br.assertInt32(0) - const hasLeftValue = !!br.assertInt32(0, 1) - br.assertInt32(0) - const leftOffset = br.readInt32() - br.assertInt32(0) - br.assertInt32(0) - br.assertInt32(0) - br.assertInt32(0) - br.assertInt32(0) - const rightOperand = br.assertInt16(-4, -3, -2, -1) - br.assertUint8(0) - br.assertUint8(1) - br.assertInt32(0) - const hasRightValue = !!br.assertInt32(0, 1) - br.assertInt32(0) - const rightOffset = br.readInt32() - br.assertInt32(0) - br.assertInt32(0) - br.assertInt32(0) - br.assertInt32(0) - br.assertInt32(0) - return new StateCondition( - operator, - unk1, - targetStateIndex, - leftOperand, - hasLeftValue ? StateCondition.readOperandValue(br, leftOperand, leftOffset) : null, - rightOperand, - hasRightValue ? StateCondition.readOperandValue(br, rightOperand, rightOffset) : null, - ).sortOperands() - } - - static readOperandValue(br: BinaryReader, type: number, offset: number) { - switch (type) { - case OperandType.Literal: { - br.stepIn(offset) - const value = br.readFloat32() - br.stepOut() - return value - } - case OperandType.External: { - br.stepIn(offset) - const value = br.readInt32() - br.stepOut() - return value - } - case OperandType.UnkMinus2: - case OperandType.StateTime: - throw new Error('Unexpected value for operand without value: ' + OperandType[type]) - } - } - - /** - * Swaps the operands and changes the operator to match if the left operand - * is a literal value and the right operand is a non-literal value. This - * makes it a bit easier to read the expression, but doesn't affect - * functionality. - */ - sortOperands() { - if (this.leftOperandType !== OperandType.Literal || this.rightOperandType === OperandType.Literal) { - return this - } - ;[ - this.leftOperandType, - this.leftOperandValue, - this.rightOperandType, - this.rightOperandValue, - ] = [ - this.rightOperandType, - this.rightOperandValue, - this.leftOperandType, - this.leftOperandValue, - ] - switch (this.operator) { - case Operator.GreaterThan: this.operator = Operator.LessThan; break; - case Operator.GreaterThanOrEqual: this.operator = Operator.LessThanOrEqual; break; - case Operator.LessThan: this.operator = Operator.GreaterThan; break; - case Operator.LessThanOrEqual: this.operator = Operator.GreaterThanOrEqual; break; - } - return this - } - - /** - * The {@link Operator.LessThanOrEqual LessThanOrEqual} and - * {@link Operator.LessThan LessThan} operators are not valid operators in - * the FXR format. This method returns an equivalent condition that *is* - * valid. - */ - formatCondition() { - if (this.operator !== Operator.LessThan && this.operator !== Operator.LessThanOrEqual) { - return this - } - return new StateCondition( - this.operator - 2, this.unk1, this.nextState, - this.rightOperandType, this.rightOperandValue, - this.leftOperandType, this.leftOperandValue - ) - } - - #write(bw: BinaryWriter, conditions: StateCondition[]) { - const count = conditions.length - bw.writeInt16(this.operator | this.unk1 << 2) - bw.writeUint8(0) - bw.writeUint8(1) - bw.writeInt32(0) - bw.writeInt32(this.nextState) - bw.writeInt32(0) - bw.writeInt16(this.leftOperandType) - bw.writeInt8(0) - bw.writeInt8(1) - bw.writeInt32(0) - bw.writeInt32(+(this.leftOperandValue !== null)) - bw.writeInt32(0) - bw.reserveInt32(`ConditionLeftOffset${count}`) - bw.writeInt32(0) - bw.writeInt32(0) - bw.writeInt32(0) - bw.writeInt32(0) - bw.writeInt32(0) - bw.writeInt16(this.rightOperandType) - bw.writeInt8(0) - bw.writeInt8(1) - bw.writeInt32(0) - bw.writeInt32(+(this.rightOperandValue !== null)) - bw.writeInt32(0) - bw.reserveInt32(`ConditionRightOffset${count}`) - bw.writeInt32(0) - bw.writeInt32(0) - bw.writeInt32(0) - bw.writeInt32(0) - bw.writeInt32(0) - conditions.push(this) - } - - write(bw: BinaryWriter, conditions: StateCondition[]) { - this.formatCondition().#write(bw, conditions) - } - - writeFields(bw: BinaryWriter, index: number): number { - let count = 0 - if (this.leftOperandValue === null) { - bw.fill(`ConditionLeftOffset${index}`, 0) - } else { - if (this.leftOperandType === OperandType.Literal) { - bw.fill(`ConditionLeftOffset${index}`, bw.position) - bw.writeFloat32(this.leftOperandValue) - } else { - bw.fill(`ConditionLeftOffset${index}`, bw.position) - bw.writeInt32(this.leftOperandValue) - } - count++ - } - if (this.rightOperandValue === null) { - bw.fill(`ConditionRightOffset${index}`, 0) - } else { - if (this.rightOperandType === OperandType.Literal) { - bw.fill(`ConditionRightOffset${index}`, bw.position) - bw.writeFloat32(this.rightOperandValue) - } else { - bw.fill(`ConditionRightOffset${index}`, bw.position) - bw.writeInt32(this.rightOperandValue) - } - count++ - } - return count - } - - clone() { - return new StateCondition( - this.operator, this.unk1, this.nextState, - this.leftOperandType, this.leftOperandValue, - this.rightOperandType, this.rightOperandValue - ) - } - - #toString() { - let left: string | number, right: string | number - switch (this.leftOperandType) { - case OperandType.External: - left = `External(${this.leftOperandValue})` - break - case OperandType.UnkMinus2: - case OperandType.StateTime: - left = OperandType[this.leftOperandType] - break - case OperandType.Literal: - left = this.leftOperandValue - break - } - switch (this.rightOperandType) { - case OperandType.External: - right = `External(${this.rightOperandValue})` - break - case OperandType.UnkMinus2: - case OperandType.StateTime: - right = OperandType[this.rightOperandType] - break - case OperandType.Literal: - right = this.rightOperandValue - break - } - return `${left} ${['!=','==','>=','>','<=','<'][this.operator]} ${right} else ${this.nextState}` - } - - toString() { - return this.clone().sortOperands().#toString() - } - -} - -/** - * The base class for all nodes. - * - * A node is a container with actions, effects, and other nodes, and they form - * the general structure of the FXR. - */ -class Node { - - type: NodeType - actions: Action[] = [] - effects: Effect[] = [] - nodes: Node[] = [] - - constructor( - type: NodeType, - actions: Action[] = [], - effects: Effect[] = [], - nodes: Node[] = [] - ) { - this.type = type - this.actions = actions - this.effects = effects - this.nodes = nodes - } - - static read(br: BinaryReader, version: FXRVersion) { - const type = br.readInt16() - const node = new (type in Nodes ? Nodes[type] : Node) - node.type = type - br.assertUint8(0) - br.assertUint8(1) - br.assertInt32(0) - const effectCount = br.readInt32() - const actionCount = br.readInt32() - const nodeCount = br.readInt32() - br.assertInt32(0) - const effectOffset = br.readInt32() - br.assertInt32(0) - const actionOffset = br.readInt32() - br.assertInt32(0) - const nodeOffset = br.readInt32() - br.assertInt32(0) - br.stepIn(nodeOffset) - node.nodes = [] - for (let i = 0; i < nodeCount; ++i) { - node.nodes.push(Node.read(br, version)) - } - br.stepOut() - br.stepIn(effectOffset) - node.effects = [] - for (let i = 0; i < effectCount; ++i) { - node.effects.push(Effect.read(br, version)) - } - br.stepOut() - br.stepIn(actionOffset) - node.actions = [] - for (let i = 0; i < actionCount; ++i) { - node.actions.push(Action.read(br, version)) - } - br.stepOut() - return node - } - - write(bw: BinaryWriter, nodes: Node[]) { - const count = nodes.length - bw.writeInt16(this.type) - bw.writeUint8(0) - bw.writeUint8(1) - bw.writeInt32(0) - bw.writeInt32(this.effects.length) - bw.writeInt32(this.actions.length) - bw.writeInt32(this.nodes.length) - bw.writeInt32(0) - bw.reserveInt32(`NodeEffectsOffset${count}`) - bw.writeInt32(0) - bw.reserveInt32(`NodeActionsOffset${count}`) - bw.writeInt32(0) - bw.reserveInt32(`NodeChildNodesOffset${count}`) - bw.writeInt32(0) - nodes.push(this) - } - - writeNodes(bw: BinaryWriter, nodes: Node[]) { - const num = nodes.indexOf(this) - if (this.nodes.length === 0) { - bw.fill(`NodeChildNodesOffset${num}`, 0) - } else { - bw.fill(`NodeChildNodesOffset${num}`, bw.position) - for (const node of this.nodes) { - node.write(bw, nodes) - } - for (const node of this.nodes) { - node.writeNodes(bw, nodes) - } - } - } - - writeEffects(bw: BinaryWriter, index: number, effectCounter: { value: number }) { - if (this.effects.length === 0) { - bw.fill(`NodeEffectsOffset${index}`, 0) - } else { - bw.fill(`NodeEffectsOffset${index}`, bw.position) - for (let i = 0; i < this.effects.length; ++i) { - this.effects[i].write(bw, effectCounter.value + i) - } - effectCounter.value += this.effects.length - } - } - - writeActions(bw: BinaryWriter, index: number, effectCounter: { value: number }, actions: Action[]) { - bw.fill(`NodeActionsOffset${index}`, bw.position) - for (const action of this.actions) { - action.write(bw, actions) - } - for (let i = 0; i < this.effects.length; ++i) { - this.effects[i].writeActions(bw, effectCounter.value + i, actions) - } - effectCounter.value += this.effects.length - } - - static fromJSON({ - type, - actions, - effects, - nodes - }: { - type: number - actions: [] - effects: [] - nodes: [] - }) { - const node = new (type in Nodes ? Nodes[type] : Node) - node.type = type - node.actions = actions?.map(action => Action.fromJSON(action)) ?? [] - node.effects = effects?.map(effect => Effect.fromJSON(effect)) ?? [] - node.nodes = nodes?.map(node => Node.fromJSON(node)) ?? [] - return node - } - - toJSON() { - return { - type: this.type, - actions: this.actions.map(action => action.toJSON()), - effects: this.effects.map(effect => effect.toJSON()), - nodes: this.nodes.map(node => node.toJSON()), - } - } - - minify() { - return new Node( - this.type, - this.actions.map(action => action.minify()), - this.effects.map(effect => effect.minify()), - this.nodes.map(node => node.minify()) - ) - } - - /** - * Yields all nodes in this branch, including this node. - */ - *walk(): Generator { - yield this - for (const node of this.nodes) { - yield* node.walk() - } - } - - /** - * Yields all effects in this branch. - */ - *walkEffects() { - for (const node of this.walk()) { - yield* node.effects - } - } - - /** - * Yields all actions in this branch. - */ - *walkActions() { - for (const node of this.walk()) { - yield* node.actions - for (const effect of node.effects) { - yield* effect.actions - } - } - } - - /** - * Yields all properties in this branch, excluding properties inside - * modifiers. - */ - *walkProperties() { - for (const action of this.walkActions()) { - yield* action.properties1 - yield* action.properties2 - } - } - - /** - * Scales the entire branch by a factor. This updates all sizes, offsets, - * lengths, and radii of the actions in the branch, except certain - * multiplicative fields and properties. - * - * This will not work properly in Dark Souls 3 FXRs due to some actions - * having different indices for various properties and fields. - * @param factor The factor to scale the branch with. - */ - scale(factor: number) { - for (const effect of this.walkEffects()) if ( - effect.type === EffectType.Basic || effect.type === EffectType.SharedEmitter - ) { - const slot1 = effect.actions[1] as ActionWithNumericalFields - switch (slot1.type) { - case ActionType.RandomNodeTransform: - slot1.fields1[6] = new FloatField(slot1.fields1[6].value * factor) - slot1.fields1[7] = new FloatField(slot1.fields1[7].value * factor) - slot1.fields1[8] = new FloatField(slot1.fields1[8].value * factor) - case ActionType.StaticNodeTransform: - slot1.fields1[0] = new FloatField(slot1.fields1[0].value * factor) - slot1.fields1[1] = new FloatField(slot1.fields1[1].value * factor) - slot1.fields1[2] = new FloatField(slot1.fields1[2].value * factor) - break - } - const slot2 = effect.actions[2] - switch (slot2.type) { - case ActionType.NodeTranslation: - slot2.properties1[0].scale(factor) - break - case ActionType.NodeAcceleration: - case ActionType.NodeAccelerationRandomTurns: - case ActionType.NodeAccelerationPartialFollow: - case ActionType.NodeAccelerationSpin: - slot2.properties1[0].scale(factor) - slot2.properties1[1].scale(factor) - slot2.properties1[3].scale(factor) - break - case ActionType.NodeSpeed: - case ActionType.NodeSpeedRandomTurns: - case ActionType.NodeSpeedPartialFollow: - case ActionType.NodeSpeedSpin: - slot2.properties1[0].scale(factor) - slot2.properties1[2].scale(factor) - break - } - const slot4 = effect.actions[1] - if (slot4.type === ActionType.EqualDistanceEmitter) { - slot4.properties1[0].scale(factor) - } - const slot5 = effect.actions[5] - switch (slot5.type) { - case ActionType.BoxEmitterShape: - slot5.properties1[2].scale(factor) - case ActionType.RectangleEmitterShape: - case ActionType.CylinderEmitterShape: - slot5.properties1[1].scale(factor) - case ActionType.DiskEmitterShape: - case ActionType.SphereEmitterShape: - slot5.properties1[0].scale(factor) - break - } - - if (effect.type === EffectType.Basic) { - const slot7 = effect.actions[7] - slot7.properties1[0].scale(factor) - - const slot9 = effect.actions[9] as ActionWithNumericalFields - switch (slot9.type) { - case ActionType.PointSprite: - case ActionType.Tracer: - case ActionType.Unk10012_Tracer: - slot9.properties1[2].scale(factor) - break - case ActionType.Line: - slot9.properties1[1].scale(factor) - break - case ActionType.QuadLine: - slot9.properties1[1].scale(factor) - slot9.properties1[2].scale(factor) - break - case ActionType.BillboardEx: - slot9.properties1[2].scale(factor) - slot9.properties1[3].scale(factor) - slot9.properties1[4].scale(factor) - slot9.properties1[5].scale(factor) - slot9.properties1[6].scale(factor) - slot9.properties1[20].scale(factor) - break - case ActionType.MultiTextureBillboardEx: - slot9.properties1[1].scale(factor) - slot9.properties1[2].scale(factor) - slot9.properties1[3].scale(factor) - slot9.properties1[4].scale(factor) - slot9.properties1[5].scale(factor) - break - case ActionType.Model: - slot9.properties1[1].scale(factor) - slot9.properties1[2].scale(factor) - slot9.properties1[3].scale(factor) - break - case ActionType.Distortion: - slot9.properties1[1].scale(factor) - slot9.properties1[2].scale(factor) - slot9.properties1[3].scale(factor) - slot9.properties1[4].scale(factor) - slot9.properties1[5].scale(factor) - slot9.properties1[6].scale(factor) - break - case ActionType.RadialBlur: - slot9.properties1[2].scale(factor) - slot9.properties1[3].scale(factor) - slot9.properties1[4].scale(factor) - slot9.properties1[5].scale(factor) - slot9.properties1[6].scale(factor) - slot9.properties1[9].scale(factor) - break - case ActionType.PointLight: - slot9.properties1[2].scale(factor) - slot9.fields2[4] = new FloatField(slot9.fields2[4].value * factor) - slot9.fields2[5] = new FloatField(slot9.fields2[5].value * factor) - slot9.fields2[6] = new FloatField(slot9.fields2[6].value * factor) - break - case ActionType.SpotLight: - slot9.properties1[4].scale(factor) - slot9.properties1[5].scale(factor) - slot9.properties1[6].scale(factor) - slot9.properties1[7].scale(factor) - slot9.fields1[4] = new FloatField(slot9.fields1[4].value * factor) - slot9.fields1[5] = new FloatField(slot9.fields1[5].value * factor) - slot9.fields1[6] = new FloatField(slot9.fields1[6].value * factor) - break - } - switch (slot9.type) { - case ActionType.PointSprite: - case ActionType.Line: - case ActionType.QuadLine: - case ActionType.BillboardEx: - case ActionType.MultiTextureBillboardEx: - case ActionType.Model: - case ActionType.Tracer: - case ActionType.Distortion: - case ActionType.RadialBlur: - case ActionType.Unk10000_StandardParticle: - case ActionType.Unk10001_StandardCorrectParticle: - case ActionType.Unk10012_Tracer: - case ActionType.Unk10015_RichModel: - for (let i = 19; i >= 14; i--) if (slot9.fields2[i].value > 0) { - slot9.fields2[i] = new FloatField(slot9.fields2[i].value * factor) - } - slot9.fields2[26] = new FloatField(slot9.fields2[26].value * factor) - break - } - const slot10 = effect.actions[10] - switch (slot10.type) { - case ActionType.ParticleAcceleration: - case ActionType.ParticleSpeed: - case ActionType.ParticleSpeedRandomTurns: - case ActionType.ParticleSpeedPartialFollow: - case ActionType.ParticleAccelerationRandomTurns: - case ActionType.ParticleAccelerationPartialFollow: - slot10.properties1[0].scale(factor) - slot10.properties1[1].scale(factor) - break - } - const slot13 = effect.actions[13] - switch (slot13.type) { - case ActionType.NodeWindAcceleration: - case ActionType.NodeWindSpeed: - slot13.properties1[0].scale(factor) - break - } - const slot14 = effect.actions[14] - switch (slot14.type) { - case ActionType.ParticleWindAcceleration: - case ActionType.ParticleWindSpeed: - slot14.properties1[0].scale(factor) - break - } - } else { // Shared emitter effect - const slot9 = effect.actions[9] - switch (slot9.type) { - case ActionType.NodeWindAcceleration: - case ActionType.NodeWindSpeed: - slot9.properties1[0].scale(factor) - break - } - } - } - } - - /** - * Recolors the entire branch by modifying color properties and fields using - * a function. - * - * This will not work properly in Dark Souls 3 FXRs due to some actions - * having different indices for various properties and fields. - * @param func The function used to recolor the branch. It is passed the - * original color and should return the color to replace it with. - */ - recolor(func: (color: Vector4) => Vector4) { - const procFields = (list: NumericalField[], i: number, alpha = false) => { - const [r, g, b, a] = func([ - list[i ].value, - list[i+1].value, - list[i+2].value, - alpha ? list[i+3].value : 1 - ]) - list[i ] = new FloatField(r) - list[i+1] = new FloatField(g) - list[i+2] = new FloatField(b) - if (alpha) { - list[i+3] = new FloatField(a) - } - } - const procProp = (list: IProperty[], index: number) => { - let prop = list[index] - if (prop instanceof ValueProperty) { - if (prop.function <= PropertyFunction.One) { - prop.function = PropertyFunction.Constant - } - prop.value = func(prop.value) - } else if (prop instanceof KeyframeProperty) { - for (const keyframe of prop.keyframes) { - keyframe.value = func(keyframe.value as Vector4) - } - } else if (prop instanceof ComponentKeyframeProperty) { - const positions = new Set - for (const comp of prop.components) { - for (const keyframe of comp.keyframes) { - positions.add(keyframe.position) - } - } - const keyframes = Array.from(positions).sort((a, b) => a - b).map(e => new Keyframe(e, prop.valueAt(e))) - list[index] = new LinearProperty(prop.loop, keyframes) - if ('modifiers' in prop) { - (list[index] as IModifiableProperty).modifiers = prop.modifiers - } - prop = list[index] - } - if ('modifiers' in prop) { - for (const mod of (prop as IModifiableProperty).modifiers) { - switch (mod.type) { - case ModifierType.Randomizer2: - procFields(mod.fields, 8, true) - case ModifierType.Randomizer1: - case ModifierType.Randomizer3: - procFields(mod.fields, 4, true) - break - case ModifierType.ExternalValue1: - case ModifierType.ExternalValue2: - procProp(mod.properties, 0) - break - } - } - } - } - for (const effect of this.walkEffects()) if (effect.type === EffectType.Basic) { - procProp(effect.actions[7].properties1, 4) - const slot9 = effect.actions[9] as ActionWithNumericalFields - switch (slot9.type) { - case ActionType.PointSprite: - procProp(slot9.properties1, 3) - procProp(slot9.properties1, 4) - procProp(slot9.properties1, 5) - break - case ActionType.Line: - procProp(slot9.properties1, 2) - procProp(slot9.properties1, 3) - procProp(slot9.properties1, 4) - procProp(slot9.properties1, 5) - procProp(slot9.properties1, 7) - break - case ActionType.QuadLine: - procProp(slot9.properties1, 3) - procProp(slot9.properties1, 4) - procProp(slot9.properties1, 5) - procProp(slot9.properties1, 6) - procProp(slot9.properties1, 9) - break - case ActionType.BillboardEx: - procProp(slot9.properties1, 7) - procProp(slot9.properties1, 8) - procProp(slot9.properties1, 9) - break - case ActionType.MultiTextureBillboardEx: - procProp(slot9.properties1, 15) - procProp(slot9.properties1, 16) - procProp(slot9.properties1, 17) - procProp(slot9.properties1, 18) - procProp(slot9.properties1, 19) - procProp(slot9.properties1, 20) - break - case ActionType.Model: - procProp(slot9.properties1, 14) - procProp(slot9.properties1, 15) - procProp(slot9.properties1, 16) - break - case ActionType.Tracer: - case ActionType.Unk10012_Tracer: - procProp(slot9.properties1, 6) - procProp(slot9.properties1, 7) - procProp(slot9.properties1, 8) - break - case ActionType.Distortion: - case ActionType.RadialBlur: - procProp(slot9.properties1, 7) - procProp(slot9.properties1, 8) - break - case ActionType.PointLight: - case ActionType.SpotLight: - procProp(slot9.properties1, 0) - procProp(slot9.properties1, 1) - break - case ActionType.Unk10000_StandardParticle: - case ActionType.Unk10001_StandardCorrectParticle: - procProp(slot9.properties1, 13) - procProp(slot9.properties2, 3) - procProp(slot9.properties2, 4) - procProp(slot9.properties2, 5) - break - case ActionType.Unk10014_LensFlare: - procProp(slot9.properties1, 2) - procProp(slot9.properties1, 5) - procProp(slot9.properties1, 8) - procProp(slot9.properties1, 11) - break - case ActionType.Unk10015_RichModel: - procProp(slot9.properties1, 13) - procProp(slot9.properties1, 14) - procProp(slot9.properties1, 15) - } - switch (slot9.type) { - case ActionType.PointSprite: - case ActionType.Line: - case ActionType.QuadLine: - case ActionType.BillboardEx: - case ActionType.MultiTextureBillboardEx: - case ActionType.Model: - case ActionType.Tracer: - case ActionType.Distortion: - case ActionType.RadialBlur: - case ActionType.Unk10012_Tracer: - case ActionType.Unk10015_RichModel: - procProp(slot9.properties2, 3) - procProp(slot9.properties2, 4) - procProp(slot9.properties2, 5) - procFields(slot9.fields2, 5) - break - } - } - } - -} - -/** - * Simplifies the creation of new {@link NodeType.Root root nodes} by giving - * them default actions. - */ -class RootNode extends Node { - - constructor( - rateOfTime: ScalarPropertyArg = 1, - effects: Effect[] = [], - nodes: Node[] = [], - ) { - super(NodeType.Root, [ - new Action(ActionType.Unk700), - new Action(ActionType.Unk10100, arrayOf(56, () => new IntField(0))), - new Action(ActionType.Unk10400, arrayOf(65, () => new IntField(1))), - new Action(ActionType.Unk10500, arrayOf(10, () => new IntField(0)), [], [ - rateOfTime instanceof Property ? rateOfTime : new ConstantProperty(rateOfTime as number) - ]), - ], effects, nodes) - } - - get rateOfTime(): ScalarProperty { return this.actions.find(a => a.type === ActionType.Unk10500)?.properties1[0] } - set rateOfTime(value: ScalarPropertyArg) { - if (value instanceof Property) { - this.actions.find(a => a.type === ActionType.Unk10500).properties1[0] = value - } else { - this.actions.find(a => a.type === ActionType.Unk10500).properties1[0] = new ConstantProperty(value as number) - } - } - -} - -/** - * Acts as a node containing another FXR. - */ -class ProxyNode extends Node { - - declare actions: [FXRReference] - - /** - * @param fxrID The ID of the FXR that this node should act as a proxy for. - */ - constructor(fxrID: number) { - super(NodeType.Proxy, [ - new FXRReference(fxrID) - ]) - } - - /** - * The ID of the FXR that this node should act as a proxy for. - */ - get fxrID() { return this.actions[0].referenceID } - set fxrID(value) { this.actions[0].referenceID = value } - -} - -/** - * Super class for any type of node that contains {@link EffectType effects}. - */ -class NodeWithEffects extends Node { - - constructor(type: NodeType, effects: Effect[] = [], nodes: Node[] = []) { - super(type, [ new StateEffectMap ], effects, nodes) - } - - mapStates(...effectIndices: number[]) { - this.actions = [new StateEffectMap(...effectIndices)] - return this - } - -} - -/** - * A node that only displays one of its child nodes at a time based on - * distance thresholds for each. - * - * This node can only manage up to five levels of detail. If you need more - * levels, you can put another LOD node as the fifth child of this node and set - * higher thresholds in that. - */ -class LevelOfDetailNode extends NodeWithEffects { - - /** - * @param effects An array of {@link EffectType.LevelOfDetail LOD effects}. - * Other effect types do not work in this node type. - * @param nodes An array of child nodes. - */ - constructor(effects: Effect[] = [], nodes: Node[] = []) { - super(NodeType.LevelOfDetail, effects, nodes) - } - - /** - * Alternative method to construct the node. Use this if you don't need - * multiple effects or a non-infinite duration. - * @param thresholds An array of distance thresholds. Each threshold is used - * for the child node of the same index. - * @param nodes An array of child nodes. - */ - static withThresholds(thresholds: number[], nodes: Node[]) { - return new LevelOfDetailNode([ - new LevelOfDetailEffect(-1, thresholds) - ], nodes) - } - -} - -/** - * A basic node that can have transforms and child nodes, and emit particles. - */ -class BasicNode extends NodeWithEffects { - - /** - * @param effectsOrEffectActions This is either the list of effects to add - * to the node or a list of actions to create a {@link BasicEffect} with to - * add to the node. - * @param nodes A list of child nodes. - */ - constructor(effectsOrEffectActions: Effect[] | Action[] = [], nodes: Node[] = []) { - if (!Array.isArray(nodes) || nodes.some(e => !(e instanceof Node))) { - throw new Error('Non-node passed as node to BasicNode.') - } - if (effectsOrEffectActions.every(e => e instanceof Action)) { - super(NodeType.Basic, [ - new BasicEffect(effectsOrEffectActions as Action[]) - ], nodes) - } else { - super( - NodeType.Basic, - effectsOrEffectActions as Effect[], - nodes - ) - } - } - -} - -/** - * A node that overrides the emitter of its child nodes with its own, allowing - * a single emitter to emit multiple types of particles. - */ -class SharedEmitterNode extends NodeWithEffects { - - constructor(effectsOrEffectActions: Effect[] | Action[] = [], nodes: Node[] = []) { - if (!Array.isArray(nodes) || nodes.some(e => !(e instanceof Node))) { - throw new Error('Non-node passed as node to SharedEmitterNode.') - } - if (effectsOrEffectActions.every(e => e instanceof Action)) { - super(NodeType.SharedEmitter, [ - new SharedEmitterEffect(effectsOrEffectActions as Action[]) - ], nodes) - } else { - super( - NodeType.SharedEmitter, - effectsOrEffectActions as Effect[], - nodes - ) - } - } - -} - -const Nodes = { - [NodeType.Root]: RootNode, RootNode, - [NodeType.Proxy]: ProxyNode, ProxyNode, - [NodeType.LevelOfDetail]: LevelOfDetailNode, LevelOfDetailNode, - [NodeType.Basic]: BasicNode, BasicNode, -} - -class Effect { - - type: EffectType - actions: Action[] - - constructor(type: EffectType, actions: Action[] = []) { - this.type = type - this.actions = actions - } - - static read(br: BinaryReader, version: FXRVersion) { - const type = br.readInt16() - const effect = new (type in Effects ? Effects[type] : Effect) - effect.type = type - br.assertUint8(0) - br.assertUint8(1) - br.assertInt32(0) - br.assertInt32(0) - const actionCount = br.readInt32() - br.assertInt32(0) - br.assertInt32(0) - const actionOffset = br.readInt32() - br.assertInt32(0) - br.stepIn(actionOffset) - effect.actions = [] - for (let i = 0; i < actionCount; ++i) { - effect.actions.push(Action.read(br, version)) - } - br.stepOut() - return effect - } - - write(bw: BinaryWriter, index: number) { - bw.writeInt16(this.type) - bw.writeUint8(0) - bw.writeUint8(1) - bw.writeInt32(0) - bw.writeInt32(0) - bw.writeInt32(this.actions.length) - bw.writeInt32(0) - bw.writeInt32(0) - bw.reserveInt32(`EffectActionsOffset${index}`) - bw.writeInt32(0) - } - - writeActions(bw: BinaryWriter, index: number, actions: Action[]) { - bw.fill(`EffectActionsOffset${index}`, bw.position) - for (const action of this.actions) { - action.write(bw, actions) - } - } - - static fromJSON({ - type, - actions - }: { - type: number - actions: [] - }) { - const effect = new (type in Effects ? Effects[type] : Effect) - effect.type = type - effect.actions = actions?.map(action => Action.fromJSON(action)) ?? [] - return effect - } - - toJSON() { - return { - type: this.type, - actions: this.actions.map(action => action.toJSON()) - } - } - - minify() { - return new Effect(this.type, this.actions.map(action => action.minify())) - } - -} - -/** - * Manages the duration and thresholds for the - * {@link NodeType.LevelOfDetail level of detail node}. - */ -class LevelOfDetailEffect extends Effect { - - /** - * @param duration The duration for the node to stay active. Once its time is - * up, it will deactivate and none of the child nodes will be visible/audible - * anymore. - * @param thresholds An array of distance thresholds. Each threshold is used - * for the child node of the same index. - */ - constructor(duration: ScalarPropertyArg, thresholds: number[]) { - super(EffectType.LevelOfDetail, [ - new LevelOfDetailThresholds(duration, thresholds) - ]) - } - -} - -/** - * Effect used in {@link NodeType.Basic basic nodes} to apply transforms and - * emit particles of many different types. - * - * Default actions: - * Index | Action - * ------|---------- - * 0 | {@link ActionType.NodeAttributes NodeAttributes} - * 1 | {@link ActionType.None None} - * 2 | {@link ActionType.None None} - * 3 | {@link ActionType.None None} - * 4 | {@link ActionType.OneTimeEmitter OneTimeEmitter} - * 5 | {@link ActionType.PointEmitterShape PointEmitterShape} - * 6 | {@link ActionType.NoParticleSpread NoParticleSpread} - * 7 | {@link ActionType.ParticleMultiplier ParticleMultiplier} - * 8 | {@link ActionType.ParticleAttributes ParticleAttributes} - * 9 | {@link ActionType.None None} - * 10 | {@link ActionType.None None} - * 11 | {@link ActionType.None None} - * 12 | {@link ActionType.Unk130 Unk130} - * 13 | {@link ActionType.None None} - * 14 | {@link ActionType.None None} - */ -class BasicEffect extends Effect { - - /** - * @param actions Actions to use in the effect. The order does not matter, - * and it does not need to be a complete list. Actions will be placed in the - * slots they fit in. - */ - constructor(actions: Action[] = []) { - super(EffectType.Basic, [ - new NodeAttributes, - new Action, - new Action, - new Action, - new OneTimeEmitter, - new PointEmitterShape, - new NoParticleSpread, - new ParticleMultiplier, - new ParticleAttributes, - new Action, - new Action, - new Action, - new Action(ActionType.Unk130, [ - new IntField(1), - new Field, - new Field, - new Field, - new Field, - new Field, - new Field, - new Field, - new Field, - ], [], [ - new ZeroProperty, - new ZeroProperty, - new ZeroProperty, - new ZeroProperty, - new ZeroProperty, - new ZeroProperty, - new ZeroProperty, - new ZeroProperty, - ]), - new Action, - new Action, - ]) - for (const action of actions) { - const index = EffectActionSlots[EffectType.Basic].findIndex(a => a.includes(action.type)) - if (index >= 0) { - this.actions[index] = action - } else { - throw new Error('No slot for action: ' + action.type) - } - } - } - -} - -/** - * Effect used in {@link NodeType.SharedEmitter shared emitter nodes} to - * override emitters of child nodes and control which of the child nodes to use - * the particles of. - * - * Default actions: - * Index | Action - * ------|---------- - * 0 | {@link ActionType.NodeAttributes NodeAttributes} - * 1 | {@link ActionType.None None} - * 2 | {@link ActionType.None None} - * 3 | {@link ActionType.None None} - * 4 | {@link ActionType.OneTimeEmitter OneTimeEmitter} - * 5 | {@link ActionType.PointEmitterShape PointEmitterShape} - * 6 | {@link ActionType.NoParticleSpread NoParticleSpread} - * 7 | {@link ActionType.EmitAllParticles AllChildNodes} - * 13 | {@link ActionType.None None} - * 14 | {@link ActionType.None None} - */ -class SharedEmitterEffect extends Effect { - - /** - * @param actions Actions to use in the effect. The order does not matter, - * and it does not need to be a complete list. Actions will be placed in the - * slots they fit in. - */ - constructor(actions: Action[] = []) { - super(EffectType.SharedEmitter, [ - new NodeAttributes, - new Action, - new Action, - new Action, - new OneTimeEmitter, - new PointEmitterShape, - new NoParticleSpread, - new EmitAllParticles, - new Action, - new Action, - ]) - for (const action of actions) { - const index = EffectActionSlots[EffectType.SharedEmitter].findIndex(a => a.includes(action.type)) - if (index >= 0) { - this.actions[index] = action - } else { - throw new Error('No slot for action: ' + action.type) - } - } - } - -} - -const Effects = { - [EffectType.LevelOfDetail]: LevelOfDetailEffect, LevelOfDetailEffect, - [EffectType.Basic]: BasicEffect, BasicEffect, - [EffectType.SharedEmitter]: SharedEmitterEffect, SharedEmitterEffect, -} - -const commonFields2Types = [ - /* 0 */ null, - /* 1 */ FieldType.Boolean, - /* 2 */ FieldType.Integer, - /* 3 */ FieldType.Float, - /* 4 */ FieldType.Integer, - /* 5 */ FieldType.Float, // Bloom - Red multiplier - /* 6 */ FieldType.Float, // Bloom - Green multiplier - /* 7 */ FieldType.Float, // Bloom - Blue multiplier - /* 8 */ FieldType.Float, // Bloom strength - /* 9 */ null, - /* 10 */ null, - /* 11 */ null, - /* 12 */ null, - /* 13 */ null, - /* 14 */ FieldType.Float, // Distance Fade: Close 0 - /* 15 */ FieldType.Float, // Distance Fade: Close 1 - /* 16 */ FieldType.Float, // Distance Fade: Far 0 - /* 17 */ FieldType.Float, // Distance Fade: Far 1 - /* 18 */ FieldType.Float, // Minimum view distance - /* 19 */ FieldType.Float, // Maximum view distance - /* 20 */ null, - /* 21 */ null, - /* 22 */ null, - /* 23 */ null, - /* 24 */ null, - /* 25 */ FieldType.Float, // Depth blend 1 - /* 26 */ FieldType.Float, // Depth blend 2 - /* 27 */ FieldType.Boolean, - /* 28 */ null, - /* 29 */ FieldType.Float, - /* 30 */ FieldType.Float, // Shadow darkness - /* 31 */ null, - /* 32 */ FieldType.Boolean, - /* 33 */ FieldType.Integer, // Specular texture - /* 34 */ FieldType.Float, // Glossiness - /* 35 */ FieldType.Integer, // Lighting mode - /* 36 */ FieldType.Integer, - /* 37 */ null, - /* 38 */ FieldType.Float, // Specularity - /* 39 */ FieldType.Integer, - /* 40 */ null, - /* 41 */ null, - /* 42 */ null, - /* 43 */ null, - /* 44 */ null, -] -const ActionFieldTypes: { [index: string]: { Fields1: FieldType[], Fields2: FieldType[] } } = { - [ActionType.NodeTranslation]: { - Fields1: [ - FieldType.Integer - ], - Fields2: [] - }, - [ActionType.NodeSpin]: { - Fields1: [ - FieldType.Integer - ], - Fields2: [] - }, - [ActionType.StaticNodeTransform]: { - Fields1: [ - FieldType.Float, - FieldType.Float, - FieldType.Float, - FieldType.Float, - FieldType.Float, - FieldType.Float, - ], - Fields2: [] - }, - [ActionType.RandomNodeTransform]: { - Fields1: [ - FieldType.Float, - FieldType.Float, - FieldType.Float, - FieldType.Float, - FieldType.Float, - FieldType.Float, - FieldType.Float, - FieldType.Float, - FieldType.Float, - FieldType.Float, - FieldType.Float, - FieldType.Float, - ], - Fields2: [] - }, - [ActionType.NodeAttachToCamera]: { - Fields1: [ - FieldType.Boolean, - null, - ], - Fields2: [] - }, - [ActionType.ParticleAcceleration]: { - Fields1: [ - null, - FieldType.Float, - ], - Fields2: [] - }, - [ActionType.ParticleSpeed]: { - Fields1: [ - null, - FieldType.Float, - ], - Fields2: [] - }, - [ActionType.ParticleSpeedRandomTurns]: { - Fields1: [ - FieldType.Float, - FieldType.Integer, - ], - Fields2: [] - }, - [ActionType.ParticleSpeedPartialFollow]: { - Fields1: [ - null, - FieldType.Integer, - FieldType.Boolean - ], - Fields2: [] - }, - [ActionType.PlaySound]: { - Fields1: [ - FieldType.Integer, - FieldType.Float, - FieldType.Boolean - ], - Fields2: [] - }, - [ActionType.ParticleAccelerationRandomTurns]: { - Fields1: [ - FieldType.Float, - FieldType.Integer, - ], - Fields2: [] - }, - [ActionType.ParticleAccelerationPartialFollow]: { - Fields1: [ - null, - FieldType.Integer, - FieldType.Boolean - ], - Fields2: [] - }, - [ActionType.ParticleAttributes]: { - Fields1: [ - FieldType.Integer - ], - Fields2: [] - }, - [ActionType.ParticleMultiplier]: { - Fields1: [ - FieldType.Boolean - ], - Fields2: [] - }, - [ActionType.FXRReference]: { - Fields1: [ - FieldType.Integer - ], - Fields2: [] - }, - [ActionType.Unk130]: { - Fields1: [ - FieldType.Integer, - FieldType.Integer, - FieldType.Integer, - FieldType.Integer, - FieldType.Integer, - FieldType.Integer, - FieldType.Integer, - FieldType.Integer, - FieldType.Integer - ], - Fields2: [] - }, - [ActionType.PointEmitterShape]: { - Fields1: [ - FieldType.Integer - ], - Fields2: [] - }, - [ActionType.DiskEmitterShape]: { - Fields1: [ - FieldType.Integer - ], - Fields2: [] - }, - [ActionType.RectangleEmitterShape]: { - Fields1: [ - FieldType.Integer - ], - Fields2: [] - }, - [ActionType.SphereEmitterShape]: { - Fields1: [ - FieldType.Boolean, - ], - Fields2: [] - }, - [ActionType.BoxEmitterShape]: { - Fields1: [ - FieldType.Integer, - FieldType.Boolean, - ], - Fields2: [] - }, - [ActionType.CylinderEmitterShape]: { - Fields1: [ - FieldType.Integer, - FieldType.Boolean, - FieldType.Boolean, - ], - Fields2: [] - }, - [ActionType.CircularParticleSpread]: { - Fields1: [ - FieldType.Boolean, - ], - Fields2: [] - }, - [ActionType.EllipticalParticleSpread]: { - Fields1: [ - FieldType.Boolean, - ], - Fields2: [] - }, - [ActionType.PointSprite]: { - Fields1: [ - FieldType.Integer, - FieldType.Integer, - null, - null, - null, - ], - Fields2: commonFields2Types - }, - [ActionType.Line]: { - Fields1: [ - FieldType.Integer, - null, - null, - ], - Fields2: commonFields2Types - }, - [ActionType.QuadLine]: { - Fields1: [ - FieldType.Integer, - null, - null, - ], - Fields2: commonFields2Types - }, - [ActionType.BillboardEx]: { - Fields1: [ - FieldType.Integer, // Orientation mode - FieldType.Integer, // Normal map ID - FieldType.Float, // Random width mult - FieldType.Float, // Random height mult - FieldType.Boolean, // Uniform scale - FieldType.Integer, - FieldType.Integer, // Columns - FieldType.Integer, // Total frames - FieldType.Boolean, // Interpolate frames - FieldType.Integer, - FieldType.Integer, - FieldType.Float, - FieldType.Integer, - null, - null, - FieldType.Integer, - FieldType.Integer, - null, - ], - Fields2: commonFields2Types - }, - [ActionType.MultiTextureBillboardEx]: { - Fields1: [ - FieldType.Integer, // Orientation mode - FieldType.Integer, // Mask - FieldType.Integer, // Layer 1 - FieldType.Integer, // Layer 2 - FieldType.Boolean, // Uniform scale - null, - FieldType.Integer, // Columns - FieldType.Integer, // Total frames - FieldType.Boolean, // Interpolate frames - null, - null, - null, - FieldType.Boolean, - FieldType.Boolean, - null, - null, - null, - null, - ], - Fields2: commonFields2Types - }, - [ActionType.Model]: { - Fields1: [ - FieldType.Integer, - FieldType.Float, - FieldType.Float, - FieldType.Float, - FieldType.Boolean, - FieldType.Integer, - FieldType.Integer, - FieldType.Integer, - FieldType.Integer, - FieldType.Boolean, - FieldType.Boolean, - null, - null, - null, - null, - FieldType.Float, - null, - null, - null, - null, - ], - Fields2: commonFields2Types - }, - [ActionType.Tracer]: { - Fields1: [ - FieldType.Integer, - FieldType.Integer, - FieldType.Float, - FieldType.Float, - FieldType.Integer, - null, - null, - FieldType.Float, - FieldType.Integer, - FieldType.Integer, - FieldType.Boolean, - FieldType.Integer, - FieldType.Integer, - null, - FieldType.Integer, - FieldType.Integer, - null, - ], - Fields2: commonFields2Types - }, - [ActionType.Distortion]: { - Fields1: [ - FieldType.Integer, - FieldType.Integer, - FieldType.Integer, - FieldType.Integer, - FieldType.Integer, - FieldType.Integer, - FieldType.Float, - FieldType.Float, - FieldType.Float, - FieldType.Boolean, - FieldType.Integer, - null, - FieldType.Integer, - FieldType.Integer, - ], - Fields2: commonFields2Types - }, - [ActionType.RadialBlur]: { - Fields1: [ - FieldType.Boolean, - FieldType.Integer, - FieldType.Integer, - FieldType.Integer, - FieldType.Integer, - ], - Fields2: commonFields2Types - }, - [ActionType.PointLight]: { - Fields1: [ - null, - FieldType.Float, - ], - Fields2: [ - null, // Boolean? - FieldType.Boolean, // Toggle for the 8 floats below, which control some sort of animation - FieldType.Float, - FieldType.Float, - FieldType.Float, - FieldType.Float, - FieldType.Float, - FieldType.Float, - FieldType.Float, - FieldType.Float, - FieldType.Boolean, // Cast shadows - FieldType.Boolean, // Separate specular - FieldType.Integer, // Fade-out time - FieldType.Float, // Shadow darkness - FieldType.Integer, // Shadow caching behavior? - FieldType.Integer, // Always 2? - FieldType.Integer, - FieldType.Float, - FieldType.Float, - FieldType.Float, - FieldType.Integer, - FieldType.Integer, // Always 100? - null, - null, - FieldType.Float, // Glow - FieldType.Float, - null, - FieldType.Float, // Glow concentration - FieldType.Float, - FieldType.Integer, - FieldType.Float, - FieldType.Integer, - null, - ] - }, - [ActionType.NodeWindSpeed]: { - Fields1: [ - FieldType.Boolean, - ], - Fields2: [] - }, - [ActionType.ParticleWindSpeed]: { - Fields1: [ - FieldType.Boolean, - FieldType.Integer, - ], - Fields2: [] - }, - [ActionType.NodeWindAcceleration]: { - Fields1: [ - FieldType.Boolean, - ], - Fields2: [] - }, - [ActionType.ParticleWindAcceleration]: { - Fields1: [ - FieldType.Boolean, - FieldType.Integer, - ], - Fields2: [] - }, - [ActionType.WaterInteraction]: { - Fields1: [ - FieldType.Integer, - FieldType.Float, - FieldType.Float, - FieldType.Float, - FieldType.Float, - ], - Fields2: [] - }, - [ActionType.SpotLight]: { - Fields1: [ - null, - FieldType.Boolean, - FieldType.Float, - FieldType.Float, - FieldType.Float, - FieldType.Float, - FieldType.Float, - FieldType.Float, - FieldType.Float, - FieldType.Float, - FieldType.Boolean, - FieldType.Boolean, - FieldType.Float, - FieldType.Integer, - FieldType.Integer, - FieldType.Integer, - FieldType.Integer, - null, - FieldType.Float, - FieldType.Float, - FieldType.Float, - FieldType.Boolean, - FieldType.Float, - FieldType.Float, - FieldType.Integer, - FieldType.Float, - ], - Fields2: [] - } -} - -class Action { - - type: ActionType - fields1: Field[] - fields2: Field[] - properties1: AnyProperty[] - properties2: AnyProperty[] - section10s: Section10[] - - constructor( - type: number = ActionType.None, - fields1: Field[] = [], - fields2: Field[] = [], - properties1: AnyProperty[] = [], - properties2: AnyProperty[] = [], - section10s: Section10[] = [], - ) { - this.type = type - this.fields1 = fields1 - this.fields2 = fields2 - this.properties1 = properties1 - this.properties2 = properties2 - this.section10s = section10s - } - - static read(br: BinaryReader, version: FXRVersion): Action { - const type = br.readInt16() - br.position += 6 - // br.readUint8() // Unk02 - // br.readUint8() // Unk03 - // br.readInt32() // Unk04 - const fieldCount1 = br.readInt32() - const section10Count = br.readInt32() - const propertyCount1 = br.readInt32() - const fieldCount2 = br.readInt32() - br.assertInt32(0) - const propertyCount2 = br.readInt32() - const fieldOffset = br.readInt32() - br.assertInt32(0) - const section10Offset = br.readInt32() - br.assertInt32(0) - const propertyOffset = br.readInt32() - br.assertInt32(0) - br.assertInt32(0) - br.assertInt32(0) - - br.stepIn(propertyOffset) - const properties1: AnyProperty[] = [] - const properties2: AnyProperty[] = [] - for (let i = 0; i < propertyCount1; ++i) { - properties1.push(readProperty(br, false)) - } - for (let i = 0; i < propertyCount2; ++i) { - properties2.push(readProperty(br, false)) - } - br.stepOut() - - br.stepIn(section10Offset) - const section10s: Section10[] = [] - for (let i = 0; i < section10Count; ++i) { - section10s.push(Section10.read(br)) - } - br.stepOut() - - br.stepIn(fieldOffset) - let fields1: Field[], fields2: Field[] - if (version !== FXRVersion.DarkSouls3 && type in ActionFieldTypes) { - const pos = br.position - try { - fields1 = Field.readWithTypes(br, fieldCount1, ActionFieldTypes[type].Fields1, this) - fields2 = Field.readWithTypes(br, fieldCount2, ActionFieldTypes[type].Fields2, this) - } catch { - br.position = pos - fields1 = Field.readMany(br, fieldCount1, this) - fields2 = Field.readMany(br, fieldCount2, this) - } - } else { - fields1 = Field.readMany(br, fieldCount1, this) - fields2 = Field.readMany(br, fieldCount2, this) - } - br.stepOut() - if (type in Actions) { - const action = new Actions[type]() - action.type = type - action.fields1 = fields1 - action.fields2 = fields2 - action.properties1 = properties1 - action.properties2 = properties2 - action.section10s = section10s - return action - } else { - return new Action(type, fields1, fields2, properties1, properties2, section10s) - } - } - - write(bw: BinaryWriter, actions: Action[]) { - const count = actions.length - bw.writeInt16(this.type) - bw.writeUint8(0) // Unk02 - bw.writeUint8(0) // Unk03 - bw.writeInt32(0) // Unk04 - bw.writeInt32(this.fields1.length) - bw.writeInt32(this.section10s.length) - bw.writeInt32(this.properties1.length) - bw.writeInt32(this.fields2.length) - bw.writeInt32(0) - bw.writeInt32(this.properties2.length) - bw.reserveInt32(`ActionFieldsOffset${count}`) - bw.writeInt32(0) - bw.reserveInt32(`ActionSection10sOffset${count}`) - bw.writeInt32(0) - bw.reserveInt32(`ActionPropertiesOffset${count}`) - bw.writeInt32(0) - bw.writeInt32(0) - bw.writeInt32(0) - actions.push(this) - } - - writeProperties(bw: BinaryWriter, index: number, properties: IModifiableProperty[]) { - bw.fill(`ActionPropertiesOffset${index}`, bw.position) - for (const property of this.properties1) { - writeProperty(property, bw, properties, false) - } - for (const property of this.properties2) { - writeProperty(property, bw, properties, false) - } - } - - writeSection10s(bw: BinaryWriter, index: number, section10s: Section10[]) { - bw.fill(`ActionSection10sOffset${index}`, bw.position) - for (const section10 of this.section10s) { - section10.write(bw, section10s) - } - } - - writeFields(bw: BinaryWriter, index: number): number { - const count = this.fields1.length + this.fields2.length - if (count === 0) { - bw.fill(`ActionFieldsOffset${index}`, 0) - } else { - bw.fill(`ActionFieldsOffset${index}`, bw.position) - for (const field of this.fields1) { - field.write(bw) - } - for (const field of this.fields2) { - field.write(bw) - } - } - return count - } - - withFields1(...fields: { index: number, field: Field | number | boolean }[]) { - for (const { index, field } of fields) { - if (field instanceof Field) { - this.fields1[index] = field - } else { - this.fields1[index].value = field - } - } - return this - } - - withFields2(...fields: { index: number, field: Field | number | boolean }[]) { - for (const { index, field } of fields) { - if (field instanceof Field) { - this.fields2[index] = field - } else { - this.fields2[index].value = field - } - } - return this - } - - withProperties1(...props: { index: number, property: PropertyValue | AnyProperty }[]) { - for (const { index, property } of props) { - if (property instanceof Property) { - this.properties1[index] = property - } else { - if (Array.isArray(property)) { - this.properties1[index] = new ConstantProperty(...property) - } else { - this.properties1[index] = new ConstantProperty(property) - } - } - } - return this - } - - withProperties2(...props: { index: number, property: PropertyValue | AnyProperty }[]) { - for (const { index, property } of props) { - if (property instanceof Property) { - this.properties2[index] = property - } else { - if (Array.isArray(property)) { - this.properties2[index] = new ConstantProperty(...property) - } else { - this.properties2[index] = new ConstantProperty(property) - } - } - } - return this - } - - static fromJSON({ - type, - fields1 = [], - fields2 = [], - properties1 = [], - properties2 = [], - section10s = [], - }: { - type: ActionType - fields1?: [] - fields2?: [] - properties1?: [] - properties2?: [] - section10s?: [] - }) { - return new Action( - type, - fields1, - fields2, - properties1, - properties2, - section10s - ) - } - - toJSON() { - const o: { - type: ActionType - fields1?: any[] - fields2?: any[] - properties1?: any[] - properties2?: any[] - section10s?: any[] - } = { type: this.type } - if (this.fields1.length > 0) o.fields1 = this.fields1.map(field => field.toJSON()) - if (this.fields2.length > 0) o.fields2 = this.fields2.map(field => field.toJSON()) - if (this.properties1.length > 0) o.properties1 = this.properties1.map(prop => prop.toJSON()) - if (this.properties2.length > 0) o.properties2 = this.properties2.map(prop => prop.toJSON()) - if (this.section10s.length > 0) o.section10s = this.section10s.map(s10 => s10.toJSON()) - return o - } - - /** - * Creates a minified version of this action. - * - * Some actions can be minified to make the output smaller. This is done by - * creating a simpler action that is functionally equivalent to this action. - * - * Actions that can not be minified will not be changed. - */ - minify() { - return new Action( - this.type, - this.fields1, - this.fields2, - this.properties1.map(prop => prop.minify()), - this.properties2.map(prop => prop.minify()), - this.section10s - ) - } - -} - -export interface NodeMovementParams { - /** - * Controls how fast the node should spin around its X-axis in degrees per - * second. Defaults to 0. - * - * **Argument**: {@link PropertyArgument.EffectAge Effect age} - * - * Incompatible with the following parameters: - * - {@link maxTurnAngle} - * - {@link turnInterval} - * - {@link followFactor} - * - {@link followRotation} - * - * See also: - * - {@link spinXMultiplier} - */ - spinX?: ScalarPropertyArg - /** - * Multiplier for {@link spinX}. Defaults to 1. - * - * **Argument**: {@link PropertyArgument.EffectAge Effect age} - * - * Incompatible with the following parameters: - * - {@link maxTurnAngle} - * - {@link turnInterval} - * - {@link followFactor} - * - {@link followRotation} - */ - spinXMultiplier?: ScalarPropertyArg - /** - * Controls how fast the node should spin around its Y-axis in degrees per - * second. Defaults to 0. - * - * **Argument**: {@link PropertyArgument.EffectAge Effect age} - * - * Incompatible with the following parameters: - * - {@link maxTurnAngle} - * - {@link turnInterval} - * - {@link followFactor} - * - {@link followRotation} - * - * See also: - * - {@link spinYMultiplier} - */ - spinY?: ScalarPropertyArg - /** - * Multiplier for {@link spinY}. Defaults to 1. - * - * **Argument**: {@link PropertyArgument.EffectAge Effect age} - * - * Incompatible with the following parameters: - * - {@link maxTurnAngle} - * - {@link turnInterval} - * - {@link followFactor} - * - {@link followRotation} - */ - spinYMultiplier?: ScalarPropertyArg - /** - * Controls how fast the node should spin around its Z-axis in degrees per - * second. Defaults to 0. - * - * **Argument**: {@link PropertyArgument.EffectAge Effect age} - * - * Incompatible with the following parameters: - * - {@link maxTurnAngle} - * - {@link turnInterval} - * - {@link followFactor} - * - {@link followRotation} - * - * See also: - * - {@link spinZMultiplier} - */ - spinZ?: ScalarPropertyArg - /** - * Multiplier for {@link spinZ}. Defaults to 1. - * - * **Argument**: {@link PropertyArgument.EffectAge Effect age} - * - * Incompatible with the following parameters: - * - {@link maxTurnAngle} - * - {@link turnInterval} - * - {@link followFactor} - * - {@link followRotation} - */ - spinZMultiplier?: ScalarPropertyArg - /** - * Controls the speed of the node along its Z-axis. Defaults to 0. - * - * **Argument**: - * - If {@link speedZMultiplier} is set: - * {@link PropertyArgument.EffectAge Effect age} - * - If {@link speedZMultiplier} is **not** set: - * {@link PropertyArgument.Constant Constant 0} - * - * See also: - * - {@link speedZMultiplier} - */ - speedZ?: ScalarPropertyArg - /** - * Multiplier for {@link speedZ}. Defaults to 1. - * - * **Argument**: {@link PropertyArgument.EffectAge Effect age} - * - * Incompatible with the following parameters: - * - {@link accelerationZ} - * - {@link accelerationZMultiplier} - */ - speedZMultiplier?: ScalarPropertyArg - /** - * Controls the acceleration of the node in the +Z direction. This value - * cannot be negative. Defaults to 0. - * - * Incompatible with the following parameters: - * - {@link speedZMultiplier} - * - * See also: - * - {@link accelerationZMultiplier} - * - * **Argument**: {@link PropertyArgument.EffectAge Effect age} - */ - accelerationZ?: ScalarPropertyArg - /** - * Multiplier for {@link accelerationZ}. Defaults to 1. - * - * **Argument**: {@link PropertyArgument.EffectAge Effect age} - * - * Incompatible with the following parameters: - * - {@link speedZMultiplier} - */ - accelerationZMultiplier?: ScalarPropertyArg - /** - * Controls the acceleration of the node along its Y-axis. Defaults to 0. - * - * **Argument**: {@link PropertyArgument.EffectAge Effect age} - */ - accelerationY?: ScalarPropertyArg - /** - * The node will turn a random amount based on this value at intervals - * defined by {@link turnInterval}. Defaults to 0. - * - * **Argument**: {@link PropertyArgument.EffectAge Effect age} - * - * Incompatible with the following parameters: - * - {@link spinX} - * - {@link spinXMultiplier} - * - {@link spinY} - * - {@link spinYMultiplier} - * - {@link spinZ} - * - {@link spinZMultiplier} - */ - maxTurnAngle?: ScalarPropertyArg - /** - * The node will turn a random amount based on {@link maxTurnAngle} at - * this interval. The units are seconds, but due to how the field that stores - * this value works, the value will be rounded to the nearest 0.02 seconds. - * Defaults to 0. - * - * Incompatible with the following parameters: - * - {@link spinX} - * - {@link spinXMultiplier} - * - {@link spinY} - * - {@link spinYMultiplier} - * - {@link spinZ} - * - {@link spinZMultiplier} - */ - turnInterval?: number - /** - * Controls how well the node should follow the parent node if it is not - * attached. At 0, the node will not follow at all. At 1, the node will - * follow perfectly, as if attached to the parent node. Negative values will - * make the node move in the opposite direction compared to the parent node. - * Values greater than 1 will make the node exaggerate the parent node's - * movement. Defaults to 0. - * - * **Argument**: {@link PropertyArgument.EffectAge Effect age} - * - * Incompatible with the following parameters: - * - {@link spinX} - * - {@link spinXMultiplier} - * - {@link spinY} - * - {@link spinYMultiplier} - * - {@link spinZ} - * - {@link spinZMultiplier} - * - * See also: - * - {@link followRotation} - */ - followFactor?: ScalarPropertyArg - /** - * Disabling this will make {@link followFactor} only affect translation and - * not rotation. Defaults to true. - * - * **Argument**: {@link PropertyArgument.EffectAge Effect age} - * - * Incompatible with the following parameters: - * - {@link spinX} - * - {@link spinXMultiplier} - * - {@link spinY} - * - {@link spinYMultiplier} - * - {@link spinZ} - * - {@link spinZMultiplier} - */ - followRotation?: boolean -} -/** - * Controls the movement of the node. - * - * This class covers most of the Node Movement action types: - * - {@link ActionType.NodeSpin NodeSpin} - * - {@link ActionType.NodeAcceleration NodeAcceleration} - * - {@link ActionType.NodeAccelerationRandomTurns NodeAccelerationRandomTurns} - * - {@link ActionType.NodeAccelerationPartialFollow NodeAccelerationPartialFollow} - * - {@link ActionType.NodeAccelerationSpin NodeAccelerationSpin} - * - {@link ActionType.NodeSpeed NodeSpeed} - * - {@link ActionType.NodeSpeedRandomTurns NodeSpeedRandomTurns} - * - {@link ActionType.NodeSpeedPartialFollow NodeSpeedPartialFollow} - * - {@link ActionType.NodeSpeedSpin NodeSpeedSpin} - * - * Which one is produced by the constructor depends on what arguments are set. - * By default, the basic acceleration action is created. - */ -class NodeMovement extends Action { - - constructor({ - spinX = null, - spinXMultiplier = null, - spinY = null, - spinYMultiplier = null, - spinZ = null, - spinZMultiplier = null, - speedZ = null, - speedZMultiplier = null, - accelerationZ = null, - accelerationZMultiplier = null, - accelerationY = null, - maxTurnAngle = null, - turnInterval = null, - followFactor = null, - followRotation = null, - }: NodeMovementParams = {}) { - const speed = +(speedZMultiplier !== null) - const acceleration = +(accelerationZ !== null || accelerationZMultiplier !== null) << 1 - const spin = +[spinX, spinXMultiplier, spinY, spinYMultiplier, spinZ, spinZMultiplier].some(e => e !== null) << 2 - const randomTurns = +(maxTurnAngle !== null || turnInterval !== null) << 3 - const partialFollow = +(followFactor !== null || followRotation !== null) << 4 - const aos = +(speed || acceleration || speedZ !== null || accelerationY !== null) << 5 - spinX ??= 0 - spinXMultiplier ??= 1 - spinY ??= 0 - spinYMultiplier ??= 1 - spinZ ??= 0 - spinZMultiplier ??= 1 - speedZ ??= 0 - speedZMultiplier ??= 1 - accelerationZ ??= 0 - accelerationZMultiplier ??= 1 - accelerationY ??= 0 - maxTurnAngle ??= 0 - turnInterval ??= 0 - followFactor ??= 0 - followRotation ??= true - switch (speed | acceleration | spin | randomTurns | partialFollow | aos) { - case acceleration | aos: super(ActionType.NodeAcceleration, [ - new IntField, - new IntField, - new FloatField, - ], [], [ - scalarFromArg(speedZ), - scalarFromArg(accelerationZ), - scalarFromArg(accelerationZMultiplier), - scalarFromArg(accelerationY), - ]); break; - case spin: super(ActionType.NodeSpin, [ - new IntField(1), - ], [], [ - scalarFromArg(spinX), - scalarFromArg(spinXMultiplier), - scalarFromArg(spinY), - scalarFromArg(spinYMultiplier), - scalarFromArg(spinZ), - scalarFromArg(spinZMultiplier), - ]); break; - case acceleration | randomTurns | aos: super(ActionType.NodeAccelerationRandomTurns, [ - new IntField, - new IntField, - new IntField(Math.round(turnInterval * 50)), - ], [], [ - scalarFromArg(speedZ), - scalarFromArg(accelerationZ), - scalarFromArg(accelerationZMultiplier), - scalarFromArg(accelerationY), - scalarFromArg(maxTurnAngle), - ]); break; - case acceleration | partialFollow | aos: - case acceleration | partialFollow | randomTurns | aos: super(ActionType.NodeAccelerationPartialFollow, [ - new IntField, - new IntField(Math.round(turnInterval * 50)), - new BoolField(followRotation), - ], [], [ - scalarFromArg(speedZ), - scalarFromArg(accelerationZ), - scalarFromArg(accelerationZMultiplier), - scalarFromArg(accelerationY), - scalarFromArg(maxTurnAngle), - scalarFromArg(followFactor), - ]); break; - case acceleration | spin | aos: super(ActionType.NodeAccelerationSpin, [ - new IntField, - new IntField, - new IntField, - new IntField, - ], [], [ - scalarFromArg(speedZ), - scalarFromArg(accelerationZ), - scalarFromArg(accelerationZMultiplier), - scalarFromArg(accelerationY), - scalarFromArg(spinX), - scalarFromArg(spinXMultiplier), - scalarFromArg(spinY), - scalarFromArg(spinYMultiplier), - scalarFromArg(spinZ), - scalarFromArg(spinZMultiplier), - ]); break; - case speed | aos: super(ActionType.NodeSpeed, [ - new IntField, - new IntField, - new IntField, - ], [], [ - scalarFromArg(speedZ), - scalarFromArg(speedZMultiplier), - scalarFromArg(accelerationY), - ]); break; - case speed | randomTurns | aos: super(ActionType.NodeSpeedRandomTurns, [ - new IntField, - new IntField, - new IntField(Math.round(turnInterval * 50)), - ], [], [ - scalarFromArg(speedZ), - scalarFromArg(speedZMultiplier), - scalarFromArg(accelerationY), - scalarFromArg(maxTurnAngle), - ]); break; - case speed | partialFollow | aos: - case speed | partialFollow | randomTurns | aos: super(ActionType.NodeSpeedPartialFollow, [ - new IntField, - new IntField, - new IntField(Math.round(turnInterval * 50)), - new BoolField(followRotation), - ], [], [ - scalarFromArg(speedZ), - scalarFromArg(speedZMultiplier), - scalarFromArg(accelerationY), - scalarFromArg(maxTurnAngle), - scalarFromArg(followFactor), - ]); break; - case speed | spin | aos: super(ActionType.NodeSpeedSpin, [ - new IntField, - new IntField, - new IntField, - new IntField, - ], [], [ - scalarFromArg(speedZ), - scalarFromArg(speedZMultiplier), - scalarFromArg(accelerationY), - scalarFromArg(spinX), - scalarFromArg(spinXMultiplier), - scalarFromArg(spinY), - scalarFromArg(spinYMultiplier), - scalarFromArg(spinZ), - scalarFromArg(spinZMultiplier), - ]); break; - default: - if (speed && acceleration) { - throw new Error('Speed Z multiplier is not compatible with acceleration node movement actions.') - } - if (spin && (randomTurns || partialFollow)) { - throw new Error('Spin cannot be used together with random turns or partial follow in node movement actions.') - } - throw new Error('Incompatible arguments given to NodeMovement constructor.') - } - } - -} - -export interface ActionWithNumericalFields extends Action { - fields1: NumericalField[] - fields2: NumericalField[] -} - -/** - * Translates the node using a property, meaning it can be animated. This can - * be useful if you need the node to follow a specific path. - * - * Fields1: - * Index | Value - * ------|------ - * 0 | unkField - * - * Properties1: - * Index | Value - * ------|------ - * 0 | translation - */ -class NodeTranslation extends Action { - - /** - * @param translation A 3D vector used as an offset for the position of the - * node. Defaults to [0, 0, 0]. - * - * **Argument**: {@link PropertyArgument.EffectAge Effect age} - * @param unkField Unknown. Fields1, index 0. An integer that has at least - * three valid values: 0, 1, 2. Defaults to 0. - */ - constructor(translation: Vector3PropertyArg = [0, 0, 0], unkField: number = 0) { - super(ActionType.NodeTranslation, [ - new IntField(unkField) - ], [], [ - vectorFromArg(translation) - ]) - } - -} - -export interface NodeTransformParams { - /** - * Translates the node along the X-axis. Defaults to 0. - */ - translateX?: number - /** - * Translates the node along the Y-axis. Defaults to 0. - */ - translateY?: number - /** - * Translates the node along the Z-axis. Defaults to 0. - */ - translateZ?: number - /** - * Node rotation around the X-axis in degrees. Defaults to 0. - */ - rotateX?: number - /** - * Node rotation around the Y-axis in degrees. Defaults to 0. - */ - rotateY?: number - /** - * Node rotation around the Z-axis in degrees. Defaults to 0. - */ - rotateZ?: number - /** - * The maximum change in translation along the X-axis from - * {@link translateX the base value} caused by randomization. When the - * node is created, its translation along this axis will be a random - * number between the base value minus this value and the base value plus - * this value. Defaults to 0. - */ - randomTranslationX?: number - /** - * The maximum change in translation along the Y-axis from - * {@link translateY the base value} caused by randomization. When the - * node is created, its translation along this axis will be a random - * number between the base value minus this value and the base value plus - * this value. Defaults to 0. - */ - randomTranslationY?: number - /** - * The maximum change in translation along the Z-axis from - * {@link translateZ the base value} caused by randomization. When the - * node is created, its translation along this axis will be a random - * number between the base value minus this value and the base value plus - * this value. Defaults to 0. - */ - randomTranslationZ?: number - /** - * The maximum change in rotation around the X-axis from - * {@link rotateX the base value} caused by randomization. When the node - * is created, its rotation around this axis will be a random number between - * the base value minus this value and the base value plus this value. - * Defaults to 0. - */ - randomRotationX?: number - /** - * The maximum change in rotation around the Y-axis from - * {@link rotateY the base value} caused by randomization. When the node - * is created, its rotation around this axis will be a random number between - * the base value minus this value and the base value plus this value. - * Defaults to 0. - */ - randomRotationY?: number - /** - * The maximum change in rotation around the Z-axis from - * {@link rotateZ the base value} caused by randomization. When the node - * is created, its rotation around this axis will be a random number between - * the base value minus this value and the base value plus this value. - * Defaults to 0. - */ - randomRotationZ?: number -} -/** - * Sets the translation and rotation of the node. Optionally randomizes - * the translation and rotation. - * - * If any of the randomization parameters are not 0, it will create a - * {@link ActionType.RandomNodeTransform RandomNodeTransform action} instead of - * a {@link ActionType.StaticNodeTransform StaticNodeTransform action}, which - * have different numbers of fields. - * - * **Note about the X-axis**: - * Both of the action types represented by this class have the X-axis reversed. - * RandomNodeTransform only has it reversed for translation, not rotation. This - * class will automatically handle these strange inconsitencies and correct - * them when using its accessors or contructor parameters, but it is important - * to keep in mind if you are manually editing the fields of the action. - */ -class NodeTransform extends Action { - - declare fields1: FloatField[] - - constructor({ - translateX = 0, - translateY = 0, - translateZ = 0, - rotateX = 0, - rotateY = 0, - rotateZ = 0, - randomTranslationX = 0, - randomTranslationY = 0, - randomTranslationZ = 0, - randomRotationX = 0, - randomRotationY = 0, - randomRotationZ = 0, - }: NodeTransformParams = {}) { - if ( - randomTranslationX === 0 && - randomTranslationY === 0 && - randomTranslationZ === 0 && - randomRotationX === 0 && - randomRotationY === 0 && - randomRotationZ === 0 - ) { - super(ActionType.StaticNodeTransform, [ - // These two actions have their X-axis reversed for some reason - new FloatField(-translateX), - new FloatField(translateY), - new FloatField(translateZ), - new FloatField(-rotateX), - new FloatField(rotateY), - new FloatField(rotateZ), - ]) - } else { - super(ActionType.RandomNodeTransform, [ - new FloatField(-translateX), - new FloatField(translateY), - new FloatField(translateZ), - // While action 35 has the X-axis reversed for both translation and - // rotation, action 36 only has it reversed for translation - new FloatField(rotateX), - new FloatField(rotateY), - new FloatField(rotateZ), - new FloatField(randomTranslationX), - new FloatField(randomTranslationY), - new FloatField(randomTranslationZ), - new FloatField(randomRotationX), - new FloatField(randomRotationY), - new FloatField(randomRotationZ), - ]) - } - } - - get translateX() { return -this.fields1[0].value } - set translateX(value) { this.fields1[0].value = -value } - - get translateY() { return this.fields1[1].value } - set translateY(value) { this.fields1[1].value = value } - - get translateZ() { return this.fields1[2].value } - set translateZ(value) { this.fields1[2].value = value } - - get rotateX() { - switch (this.type) { - case ActionType.StaticNodeTransform: return -this.fields1[3].value - case ActionType.RandomNodeTransform: return this.fields1[3].value - } - } - set rotateX(value) { - switch (this.type) { - case ActionType.StaticNodeTransform: this.fields1[3].value = -value; break - case ActionType.RandomNodeTransform: this.fields1[3].value = value; break - } - } - - get rotateY() { return this.fields1[4].value } - set rotateY(value) { this.fields1[4].value = value } - - get rotateZ() { return this.fields1[5].value } - set rotateZ(value) { this.fields1[5].value = value } - - #setRandomizationField(index: number, value: number) { - if (value !== 0 && this.type === ActionType.StaticNodeTransform) { - this.type = ActionType.RandomNodeTransform - // The X rotation field needs to swap sign because it rotates the - // opposite direction in action 35 for some reason. - this.fields1[3].value *= -1 - this.fields1.push( - new FloatField(0), - new FloatField(0), - new FloatField(0), - new FloatField(0), - new FloatField(0), - new FloatField(0), - ) - } - this.fields1[index].value = value - } - - get randomTranslationX() { return this.fields1[6]?.value ?? 0 } - set randomTranslationX(value) { this.#setRandomizationField(6, value) } - - get randomTranslationY() { return this.fields1[7]?.value ?? 0 } - set randomTranslationY(value) { this.#setRandomizationField(7, value) } - - get randomTranslationZ() { return this.fields1[8]?.value ?? 0 } - set randomTranslationZ(value) { this.#setRandomizationField(8, value) } - - get randomRotationX() { return this.fields1[9]?.value ?? 0 } - set randomRotationX(value) { this.#setRandomizationField(9, value) } - - get randomRotationY() { return this.fields1[10]?.value ?? 0 } - set randomRotationY(value) { this.#setRandomizationField(10, value) } - - get randomRotationZ() { return this.fields1[11]?.value ?? 0 } - set randomRotationZ(value) { this.#setRandomizationField(11, value) } - - minify() { - if (this.type === ActionType.RandomNodeTransform) { - if ( - this.fields1[6].value === 0 && - this.fields1[7].value === 0 && - this.fields1[8].value === 0 && - this.fields1[9].value === 0 && - this.fields1[10].value === 0 && - this.fields1[11].value === 0 - ) { - if ( - this.fields1[0].value === 0 && - this.fields1[1].value === 0 && - this.fields1[2].value === 0 && - this.fields1[3].value === 0 && - this.fields1[4].value === 0 && - this.fields1[5].value === 0 - ) { - // This transform doesn't do anything, return a None action. - return new Action - } else { - // This doesn't use the randomization fields, return a static - // transform action instead - return new Action(ActionType.StaticNodeTransform, [ - this.fields1[0], - this.fields1[1], - this.fields1[2], - new FloatField(-this.fields1[3].value), - this.fields1[4], - this.fields1[5], - ]) - } - } else { - // This action can't be minified more without losing functionality, - // return it without any changes - return this - } - } else { - if ( - this.fields1[0].value === 0 && - this.fields1[1].value === 0 && - this.fields1[2].value === 0 && - this.fields1[3].value === 0 && - this.fields1[4].value === 0 && - this.fields1[5].value === 0 - ) { - // This transform doesn't do anything, return a None action. - return new Action - } else { - // This action can't be minified more without losing functionality, - // return it without any changes - return this - } - } - } - -} - -/** - * Attaches the node to the camera. - */ -class NodeAttachToCamera extends Action { - - declare fields1: [BoolField, IntField] - - /** - * @param followRotation Disable this to stop the node from following the - * rotation of the camera. Defaults to true. - * @param unkField1 Unknown. Fields1, index 1. Defaults to 1. - */ - constructor(followRotation: boolean = true, unkField1: number = 1) { - super(ActionType.NodeAttachToCamera, [ - new BoolField(followRotation), - new IntField(unkField1) - ]) - } - - /** - * Controls if the node should also follow the rotation of the camera or only - * the translation. - */ - get followRotation() { return this.fields1[0].value } - set followRotation(value) { this.fields1[0].value = value } - -} - -/** - * Plays a sound effect. - */ -class PlaySound extends Action { - - /** - * @param soundID The ID of the sound to play. - * @param repeat Controls whether the sound will repeat or not. - * - * Does not seem to work in Elden Ring. - * @param volume Volume multiplier. - * - * Does not seem to work in Elden Ring. - */ - constructor(soundID: number, repeat: boolean = false, volume: number = 1) { - super(ActionType.PlaySound, [ - new IntField(soundID), - new FloatField(volume), - new BoolField(repeat) - ]) - } - - /** - * The ID of the sound to play. - */ - get soundID() { return this.fields1[0].value as number } - set soundID(value) { this.fields1[0].value = value } - - /** - * Volume multiplier. - * - * Does not seem to work in Elden Ring. - */ - get volume() { return this.fields1[1].value as number } - set volume(value) { this.fields1[1].value = value } - - /** - * Controls whether the sound will repeat or not. - * - * Does not seem to work in Elden Ring. - */ - get repeat() { return this.fields1[2].value as number } - set repeat(value) { this.fields1[2].value = value } - -} - -export interface ParticleMovementParams { - /** - * Downwards acceleration. This will always point towards global down, even - * if the node is rotated. Defaults to 0. - * - * **Argument**: {@link PropertyArgument.ParticleAge Particle age} - */ - gravity?: ScalarPropertyArg - /** - * The acceleration for the particles. The direction depends on the emitter - * shape. Defaults to 0. - * - * This can not be used together with any of the speed properties: - * - {@link speed} - * - {@link speedMult} - * - * **Argument**: {@link PropertyArgument.ParticleAge Particle age} - */ - acceleration?: ScalarPropertyArg - /** - * Multiplier for the {@link acceleration} property. Defaults to 1. - * - * This can not be used together with any of the speed properties: - * - {@link speed} - * - {@link speedMult} - * - * **Argument**: {@link PropertyArgument.ParticleAge Particle age} - */ - accelerationMult?: ScalarPropertyArg - /** - * The speed that the particles will travel at. The direction depends on the - * emitter shape. Defaults to 0. - * - * This can not be used together with any of the acceleration properties: - * - {@link acceleration} - * - {@link accelerationMult} - * - * Setting this will produce one of the speed actions instead of one of the - * acceleration actions: - * - {@link ActionType.ParticleSpeed ParticleSpeed} - * - {@link ActionType.ParticleSpeedRandomTurns ParticleSpeedRandomTurns} - * - {@link ActionType.ParticleSpeedPartialFollow ParticleSpeedPartialFollow} - * - * **Argument**: {@link PropertyArgument.ParticleAge Particle age} - */ - speed?: ScalarPropertyArg - /** - * Multiplier for the {@link speed} property. Defaults to 1. - * - * This can not be used together with any of the acceleration properties: - * - {@link acceleration} - * - {@link accelerationMult} - * - * Setting this will produce one of the speed actions instead of one of the - * acceleration actions: - * - {@link ActionType.ParticleSpeed ParticleSpeed} - * - {@link ActionType.ParticleSpeedRandomTurns ParticleSpeedRandomTurns} - * - {@link ActionType.ParticleSpeedPartialFollow ParticleSpeedPartialFollow} - * - * **Argument**: {@link PropertyArgument.ParticleAge Particle age} - */ - speedMult?: ScalarPropertyArg - /** - * The particles will turn a random amount based on this value at intervals - * defined by {@link turnInterval}. Defaults to 0. - * - * Unless one of the partial follow parameters are set, setting this will - * produce one of the random turns actions: - * - {@link ActionType.ParticleSpeedRandomTurns ParticleSpeedRandomTurns} - * - {@link ActionType.ParticleAccelerationRandomTurns ParticleAccelerationRandomTurns} - * - * **Argument**: {@link PropertyArgument.ParticleAge Particle age} - */ - maxTurnAngle?: ScalarPropertyArg - /** - * The particles will turn a random amount based on {@link maxTurnAngle} at - * this interval. The units are seconds, but due to how the field that stores - * this value works, the value will be rounded to the nearest 0.02 seconds. - * Defaults to 0. - * - * Unless one of the partial follow parameters are set, setting this will - * produce one of the random turns actions: - * - {@link ActionType.ParticleSpeedRandomTurns ParticleSpeedRandomTurns} - * - {@link ActionType.ParticleAccelerationRandomTurns ParticleAccelerationRandomTurns} - */ - turnInterval?: number - /** - * Disabling this will make {@link followFactor} only affect translation and - * not rotation. Defaults to true. - * - * Setting this will produce one of the partial follow actions: - * - {@link ActionType.ParticleSpeedPartialFollow ParticleSpeedPartialFollow} - * - {@link ActionType.ParticleAccelerationPartialFollow ParticleAccelerationPartialFollow} - * - * **Argument**: {@link PropertyArgument.ParticleAge Particle age} - */ - followRotation?: boolean - /** - * Controls how well the particles should follow the node if they are not - * attached. At 0, particles will not follow at all. At 1, particles will - * follow perfectly, as if attached to the node. Negative values will make - * the particles move in the opposite direction compared to the node. Values - * greater than 1 will make the particles exaggerate the node's movement. - * Defaults to 0. - * - * Setting this will produce one of the partial follow actions: - * - {@link ActionType.ParticleSpeedPartialFollow ParticleSpeedPartialFollow} - * - {@link ActionType.ParticleAccelerationPartialFollow ParticleAccelerationPartialFollow} - * - * **Argument**: {@link PropertyArgument.ParticleAge Particle age} - * - * See also: - * - {@link followRotation} - */ - followFactor?: ScalarPropertyArg - /** - * Unknown. Fields1, index 0. - */ - unkField0?: number - /** - * Unknown. Fields1, index 1. - * - * Only used when creating one of the basic particle movement actions: - * - {@link ActionType.ParticleAcceleration} - * - {@link ActionType.ParticleSpeed} - */ - unkField1?: number -} -/** - * Controls how particles move. - * - * This class covers all of the Particle Movement action types: - * - {@link ActionType.ParticleAcceleration ParticleAcceleration} - * - {@link ActionType.ParticleSpeed ParticleSpeed} - * - {@link ActionType.ParticleSpeedRandomTurns ParticleSpeedRandomTurns} - * - {@link ActionType.ParticleSpeedPartialFollow ParticleSpeedPartialFollow} - * - {@link ActionType.ParticleAccelerationRandomTurns ParticleAccelerationRandomTurns} - * - {@link ActionType.ParticleAccelerationPartialFollow ParticleAccelerationPartialFollow} - * - * Which one is produced by the constructor depends on what arguments are set. - * By default, the basic acceleration action is created. - */ -class ParticleMovement extends Action { - - constructor({ - gravity = 0, - maxTurnAngle = null, - turnInterval = null, - acceleration = null, - accelerationMult = null, - speed = null, - speedMult = null, - followRotation = null, - followFactor = null, - unkField0 = 0, - unkField1 = null, - }: ParticleMovementParams = {}) { - let asProp: ScalarPropertyArg, asMultProp: ScalarPropertyArg - const isSpeedAct = speed !== null || speedMult !== null - if (isSpeedAct) { - if (acceleration !== null || accelerationMult !== null) { - throw new Error('The speed properties and the acceleration properties cannot be used together in a ParticleMovement action.') - } - asProp = speed ?? 0 - asMultProp = speedMult ?? 1 - } else { - asProp = acceleration ?? 0 - asMultProp = accelerationMult ?? 1 - } - if (followFactor !== null || followRotation !== null) { - turnInterval ??= 0 - maxTurnAngle ??= 0 - followRotation ??= true - followFactor ??= 0 - super( - isSpeedAct ? - ActionType.ParticleSpeedPartialFollow - : ActionType.ParticleAccelerationPartialFollow, - [ - new FloatField(unkField0), - new IntField(Math.round(turnInterval * 50)), - new BoolField(!followRotation), - ], [], [ - scalarFromArg(gravity), - scalarFromArg(asProp), - scalarFromArg(asMultProp), - scalarFromArg(maxTurnAngle), - scalarFromArg(followFactor), - ] - ) - } else if (turnInterval !== null || maxTurnAngle !== null) { - turnInterval ??= 0 - maxTurnAngle ??= 0 - super( - isSpeedAct ? - ActionType.ParticleSpeedRandomTurns - : ActionType.ParticleAccelerationRandomTurns, - [ - new FloatField(unkField0), - new IntField(Math.round(turnInterval * 50)), - ], [], [ - scalarFromArg(gravity), - scalarFromArg(asProp), - scalarFromArg(asMultProp), - scalarFromArg(maxTurnAngle), - ] - ) - } else { - unkField1 ??= 0 - super( - isSpeedAct ? - ActionType.ParticleSpeed - : ActionType.ParticleAcceleration, - [ - new FloatField(unkField0), - new FloatField(unkField1), - ], [], [ - scalarFromArg(gravity), - scalarFromArg(asProp), - scalarFromArg(asMultProp), - ] - ) - } - } - -} - -/** - * Controls various things about the node, like its duration, and how - * it is attached to the parent node. - * - * Fields1: - * Index | Value - * ------|------ - * 0 | delay - * 1 | unkField1 - * 2 | attachment - * 3 | unkField3 - * - * Properties1: - * Index | Value - * ------|------ - * 0 | duration - */ -class NodeAttributes extends Action { - - constructor({ - duration = -1, - delay = 0, - attachment = AttachMode.Parent, - unkField1 = 1, - unkField3 = 0, - }: { - /** - * The node duration in seconds. Defaults to -1 (infinite). - * - * **Argument**: {@link PropertyArgument.Constant Constant 0} - */ - duration?: ScalarPropertyArg - /** - * The delay before the node becomes active. Defaults to 0. - */ - delay?: number - /** - * Controls how the node is attached to its parent. Defaults to - * {@link AttachMode.Parent}. - */ - attachment?: AttachMode - /** - * Unknown int. Fields1, index 1. Possibly a boolean field. Defaults to 1. - */ - unkField1?: number - /** - * Unknown float. Fields1, index 3. Possibly a bit field. Defaults to 0. - */ - unkField3?: number - } = {}) { - super(ActionType.NodeAttributes, [ - new FloatField(delay), - new IntField(unkField1), - new IntField(attachment), - new FloatField(unkField3), - ], [], [ - scalarFromArg(duration) - ]) - } - -} - -/** - * Controls various things about the particles emitted by the effect, like - * their duration, and how they are attached to the parent node. - * - * Fields1: - * Index | Value - * ------|------ - * 0 | attachment - * - * Properties1: - * Index | Value - * ------|------ - * 0 | duration - */ -class ParticleAttributes extends Action { - - constructor({ - attachment = AttachMode.Parent, - duration = -1, - }: { - /** - * Controls how the particle is attached to its parent. Defaults to - * {@link AttachMode.Parent}. - */ - attachment?: AttachMode - /** - * The particle duration in seconds. Defaults to -1 (infinite). - * - * **Argument**: {@link PropertyArgument.Constant Constant 0} - */ - duration?: ScalarPropertyArg - } = {}) { - super(ActionType.ParticleAttributes, [ - new IntField(attachment) - ], [], [ - scalarFromArg(duration) - ]) - } - -} - -export interface ParticleMultiplierParams { - /** - * Scales the model uniformly based on {@link scaleX}. The other scale - * properties in this action have no effect when this is enabled. Defaults to - * false. - */ - uniformScale?: boolean - /** - * Controls the speed of the particles, but only if the effect has an action - * in slot 10 that enables acceleration of particles. The direction depends - * on the emitter shape. Defaults to 0. - * - * **Argument**: {@link PropertyArgument.EffectAge Effect age} - */ - speed?: ScalarPropertyArg - /** - * Multiplier for the scale along the X-axis. If {@link uniformScale} is - * enabled, this scales the particles uniformly. Defaults to 1. - * - * **Argument**: {@link PropertyArgument.EffectAge Effect age} - */ - scaleX?: ScalarPropertyArg - /** - * Multiplier for the scale along the Y-axis. If {@link uniformScale} is - * enabled, this property is ignored. Defaults to 1. - * - * **Argument**: {@link PropertyArgument.EffectAge Effect age} - */ - scaleY?: ScalarPropertyArg - /** - * Multiplier for the scale along the Z-axis. If {@link uniformScale} is - * enabled, this property is ignored. Defaults to 1. - * - * **Argument**: {@link PropertyArgument.EffectAge Effect age} - */ - scaleZ?: ScalarPropertyArg - /** - * Color multiplier. Defaults to [1, 1, 1, 1]. - * - * **Argument**: {@link PropertyArgument.EffectAge Effect age} - */ - color?: Vector4PropertyArg -} -/** - * Controls various multipliers as well as the speed of particles. - * - * Fields1: - * Index | Value - * ------|------ - * 0 | uniformScale - * - * Properties1: - * Index | Value - * ------|------ - * 0 | acceleration - * 1 | scaleX - * 2 | scaleY - * 3 | scaleZ - * 4 | color - */ -class ParticleMultiplier extends Action { - - declare fields1: [BoolField] - - constructor({ - uniformScale = false, - speed = 0, - scaleX = 1, - scaleY = 1, - scaleZ = 1, - color = [1, 1, 1, 1], - }: ParticleMultiplierParams = {}) { - super(ActionType.ParticleMultiplier, [ - new BoolField(uniformScale), - ], [], [ - scalarFromArg(speed), - scalarFromArg(scaleX), - scalarFromArg(scaleY), - scalarFromArg(scaleZ), - vectorFromArg(color), - ]) - } - - /** - * Scales the model uniformly based on {@link scaleX}. The other scale - * properties in this action have no effect when this is enabled. - */ - get uniformScale() { return this.fields1[0].value } - set uniformScale(value) { this.fields1[0].value = value } - - /** - * Controls the speed of the particles, but only if the effect has an action - * in slot 10 that enables acceleration of particles. The direction depends - * on the emitter shape. - * - * **Argument**: {@link PropertyArgument.EffectAge Effect age} - */ - get speed(): ScalarProperty { return this.properties1[0] } - set speed(value: ScalarPropertyArg) { setPropertyInList(this.properties1, 0, value) } - - /** - * Multiplier for the scale along the X-axis. If {@link uniformScale} is - * enabled, this scales the particles uniformly. - * - * **Argument**: {@link PropertyArgument.EffectAge Effect age} - */ - get scaleX(): ScalarProperty { return this.properties1[1] } - set scaleX(value: ScalarPropertyArg) { setPropertyInList(this.properties1, 1, value) } - - /** - * Multiplier for the scale along the Y-axis. If {@link uniformScale} is - * enabled, this property is ignored. - * - * **Argument**: {@link PropertyArgument.EffectAge Effect age} - */ - get scaleY(): ScalarProperty { return this.properties1[2] } - set scaleY(value: ScalarPropertyArg) { setPropertyInList(this.properties1, 2, value) } - - /** - * Multiplier for the scale along the Z-axis. If {@link uniformScale} is - * enabled, this property is ignored. - * - * **Argument**: {@link PropertyArgument.EffectAge Effect age} - */ - get scaleZ(): ScalarProperty { return this.properties1[3] } - set scaleZ(value: ScalarPropertyArg) { setPropertyInList(this.properties1, 3, value) } - - /** - * Color multiplier. - * - * **Argument**: {@link PropertyArgument.EffectAge Effect age} - */ - get color(): Vector4Property { return this.properties1[4] } - set color(value: Vector4PropertyArg) { setPropertyInList(this.properties1, 4, value) } - -} - -/** - * References another FXR by its ID. - */ -class FXRReference extends Action { - - declare fields1: [IntField] - - /** - * @param referenceID The ID of the referenced FXR. - */ - constructor(referenceID: number) { - super(ActionType.FXRReference, [ - new IntField(referenceID) - ]) - } - - /** - * The ID of the referenced FXR. - */ - get referenceID() { return this.fields1[0].value } - set referenceID(value) { this.fields1[0].value = value } - -} - -/** - * Used in the {@link EffectType.LevelOfDetail level of detail effect} to - * manage the duration and thresholds for the - * {@link NodeType.LevelOfDetail level of detail node}. - */ -class LevelOfDetailThresholds extends Action { - - declare fields1: IntField[] - - /** - * @param duration The duration for the node to stay active. Once its time is - * up, it will deactivate and none of the child nodes will be visible/audible - * anymore. - * @param thresholds An array of distance thresholds. Each threshold is used - * for the child node of the same index. - */ - constructor(duration: ScalarPropertyArg = -1, thresholds: number[] = []) { - thresholds = arrayOf(5, i => thresholds[i] ?? 1000) - super( - ActionType.LevelOfDetail, - thresholds.map(l => new IntField(l)), [], [ - scalarFromArg(duration) - ] - ) - } - -} - -/** - * Maps states to effects in the parent node. - * - * The index of each value represents the index of the state, and the value - * represents the index of the effect that should be active when the state is - * active. - */ -class StateEffectMap extends Action { - - constructor(...effectIndices: number[]) { - if (effectIndices.length === 0) { - effectIndices.push(0) - } - super(ActionType.StateEffectMap, [], [], [], [], [ - new Section10(effectIndices.map(i => new IntField(i))) - ]) - } - -} - -/** - * Used in {@link EffectType.SharedEmitter} to emit all particles from child - * nodes every time the shared emitter emits something. - */ -class EmitAllParticles extends Action { - - constructor() { - super(ActionType.EmitAllParticles) - } - -} - -/** - * Used in {@link EffectType.SharedEmitter} to emit a particle from a random - * child node every time the shared emitter emits something. - */ -class EmitRandomParticles extends Action { - - constructor(...weights: number[]) { - super(ActionType.EmitRandomParticles, [], [], [], [], [ - new Section10(weights.map(w => new IntField(w))) - ]) - } - -} - -/** - * Emits particles periodically. - * - * Fields1: - * Index | Value - * ------|------ - * 0 | unkField - * - * Properties1: - * Index | Value - * ------|------ - * 0 | interval - * 1 | perInterval - * 2 | totalIntervals - * 3 | maxConcurrent - */ -class PeriodicEmitter extends Action { - - /** - * @param interval Time between emitting new particles in seconds. - * @param perInterval The number of particles to emit per interval. They all - * spawn at the same time per interval. - * @param totalIntervals The total number of intervals to emit particles. - * Once this limit is reached, the emitter is will stop emitting. - * @param maxConcurrent Maximum number of concurrent particles. Defaults to - * -1 (infinite). - * @param unkField Unknown. Fields1, index 0. - */ - constructor( - interval: ScalarPropertyArg = 1, - perInterval: ScalarPropertyArg = 1, - totalIntervals: ScalarPropertyArg = -1, - maxConcurrent: ScalarPropertyArg = -1, - unkField: number = 1 - ) { - super(ActionType.PeriodicEmitter, [ - new IntField(unkField) - ], [], [ - interval instanceof Property ? interval : new ConstantProperty(interval as number), - perInterval instanceof Property ? perInterval : new ConstantProperty(perInterval as number), - totalIntervals instanceof Property ? totalIntervals : new ConstantProperty(totalIntervals as number), - maxConcurrent instanceof Property ? maxConcurrent : new ConstantProperty(maxConcurrent as number), - ]) - } - - get interval() { return this.properties1[0] } - set interval(value) { this.properties1[0] = value } - - get perInterval() { return this.properties1[1] } - set perInterval(value) { this.properties1[1] = value } - - get total() { return this.properties1[2] } - set total(value) { this.properties1[2] = value } - - get maxConcurrent() { return this.properties1[3] } - set maxConcurrent(value) { this.properties1[3] = value } - - get unkField() { return this.fields1[0].value } - set unkField(value) { this.fields1[0].value = value } - -} - -/** - * Emits particles once it has moved a certain distance from where it last - * emitted particles. - * - * Fields1: - * Index | Value - * ------|------ - * 0 | unkField0 - * 1 | unkField1 - * - * Properties1: - * Index | Value - * ------|------ - * 0 | threshold - * 1 | unkProp - * 2 | maxConcurrent - */ -class EqualDistanceEmitter extends Action { - - /** - * @param threshold How much the emitter must move to trigger emission. - * Defaults to 0.1. - * @param maxConcurrent How many particles from this emitter are allowed at - * the same time. Defaults to 100. - * @param unkField0 Unknown. Fields1, index 0. Defaults to 1. - * @param unkField1 Unknown. Fields1, index 1. Defaults to 0. - * @param unkProp Unknown. Properties1, index 1. Defaults to -1. - */ - constructor( - threshold: ScalarPropertyArg = 0.1, - maxConcurrent: ScalarPropertyArg = 100, - unkField0: number = 1, - unkField1: number = 0, - unkProp: ScalarPropertyArg = -1 - ) { - super(ActionType.EqualDistanceEmitter, [ - new IntField(unkField0), - new IntField(unkField1), - ], [], [ - threshold instanceof Property ? threshold : new ConstantProperty(threshold as number), - unkProp instanceof Property ? unkProp : new ConstantProperty(unkProp as number), - maxConcurrent instanceof Property ? maxConcurrent : new ConstantProperty(maxConcurrent as number), - ]) - } - -} - -/** - * Emits one particle once. - * - * Contains no fields or properties. - */ -class OneTimeEmitter extends Action { - - constructor() { - super(ActionType.OneTimeEmitter) - } - -} - -/** - * Makes the emitter a single point. - * - * Fields1: - * Index | Value - * ------|------ - * 0 | unkField - */ -class PointEmitterShape extends Action { - - /** - * @param unkField Unknown. Fields1, index 0. Defaults to 5. - */ - constructor(unkField: number = 5) { - super(ActionType.PointEmitterShape, [ - new IntField(unkField) - ]) - } - -} - -/** - * Makes the emitter disk-shaped. The normal of the disk is aligned with the - * Z-axis. - * - * Fields1: - * Index | Value - * ------|------ - * 0 | unkField - * - * Properties1: - * Index | Value - * ------|------ - * 0 | radius - * 1 | centerWeight - */ -class DiskEmitterShape extends Action { - - /** - * @param radius The radius of the disk in meters. Defaults to 1. - * @param centerWeight - * Controls the weight of the center of the disk for picking random - * positions to emit from. - * - At 0, particles are equally likely to emit from anywhere inside the - * disk. - * - At 1, particles have a 100% chance of being emitted from the center - * point. - * - At -1, particles have a 100% chance of being emitted from the perimeter - * circle of the disk. - * - * Defaults to 0. - * @param unkField Unknown. Fields1, index 0. Defaults to 5. - */ - constructor( - radius: ScalarPropertyArg = 1, - centerWeight: ScalarPropertyArg = 0, - unkField: number = 5 - ) { - super(ActionType.DiskEmitterShape, [ - new IntField(unkField) - ], [], [ - radius instanceof Property ? radius : new ConstantProperty(radius as number), - centerWeight instanceof Property ? centerWeight : new ConstantProperty(centerWeight as number), - ]) - } - -} - -/** - * Makes the emitter rectangular. The normal of the rectangle is aligned - * with the Z-axis. - * - * Fields1: - * Index | Value - * ------|------ - * 0 | unkField - * - * Properties1: - * Index | Value - * ------|------ - * 0 | sizeX - * 1 | sizeY - * 2 | centerWeight - */ -class RectangleEmitterShape extends Action { - - /** - * @param sizeX Width of the rectangle. Defaults to 1. - * @param sizeY Height of the rectangle. Defaults to sizeX. - * @param centerWeight - * Controls the weight of the center of the rectangle for picking random - * positions to emit from. - * - At 0, particles are equally likely to emit from anywhere inside the - * rectangle. - * - At 1, particles have a 100% chance of being emitted from the center - * point. - * - At -1, particles have a 100% chance of being emitted from the perimeter - * of the rectangle. - * - * Defaults to 0. - * @param unkField Unknown. Fields1, index 0. Defaults to 5. - */ - constructor( - sizeX: ScalarPropertyArg = 1, - sizeY: ScalarPropertyArg = sizeX instanceof Property ? sizeX.clone() : sizeX, - centerWeight: ScalarPropertyArg = 0, - unkField: number = 5 - ) { - super(ActionType.RectangleEmitterShape, [ - new IntField(unkField) - ], [], [ - sizeX instanceof Property ? sizeX : new ConstantProperty(sizeX as number), - sizeY instanceof Property ? sizeY : new ConstantProperty(sizeY as number), - centerWeight instanceof Property ? centerWeight : new ConstantProperty(centerWeight as number), - ]) - } - -} - -/** - * Makes the emitter spherical. - * - * Fields1: - * Index | Value - * ------|------ - * 0 | volume - * - * Properties1: - * Index | Value - * ------|------ - * 0 | radius - */ -class SphereEmitterShape extends Action { - - /** - * @param volume If true, particles will be emitted from anywhere within the - * sphere. Otherwise the particles will be emitted from the surface of the - * sphere. Defaults to true. - * @param radius The radius of the sphere in meters. Defaults to 1. - */ - constructor( - volume: boolean = true, - radius: ScalarPropertyArg = 1, - ) { - super(ActionType.SphereEmitterShape, [ - new BoolField(volume) - ], [], [ - radius instanceof Property ? radius : new ConstantProperty(radius as number), - ]) - } - -} - -/** - * Makes the emitter cuboidal. - * - * Fields1: - * Index | Value - * ------|------ - * 0 | unkField - * 1 | volume - * - * Properties1: - * Index | Value - * ------|------ - * 0 | sizeX - * 1 | sizeY - * 2 | sizeZ - */ -class BoxEmitterShape extends Action { - - /** - * @param volume If true, particles will be emitted from anywhere within the - * cuboid. Otherwise the particles will be emitted from the surface of the - * cuboid. Defaults to true. - * @param sizeX Width of the cuboid. Defaults to 1. - * @param sizeY Height of the cuboid. Defaults to sizeX. - * @param sizeZ Depth of the cuboid. Defaults to sizeX. - * @param unkField Unknown. Fields1, index 0. Defaults to 0. - */ - constructor( - volume: boolean = true, - sizeX: ScalarPropertyArg = 1, - sizeY: ScalarPropertyArg = sizeX instanceof Property ? sizeX.clone() : sizeX, - sizeZ: ScalarPropertyArg = sizeX instanceof Property ? sizeX.clone() : sizeX, - unkField: number = 0, - ) { - super(ActionType.BoxEmitterShape, [ - new IntField(unkField), - new BoolField(volume), - ], [], [ - sizeX instanceof Property ? sizeX : new ConstantProperty(sizeX as number), - sizeY instanceof Property ? sizeY : new ConstantProperty(sizeY as number), - sizeZ instanceof Property ? sizeZ : new ConstantProperty(sizeZ as number), - ]) - } - -} - -/** - * Makes the emitter cylindrical. - * - * Fields1: - * Index | Value - * ------|------ - * 0 | unkField - * 1 | volume - * 2 | yAxis - * - * Properties1: - * Index | Value - * ------|------ - * 0 | radius - * 1 | height - */ -class CylinderEmitterShape extends Action { - - /** - * @param volume If true, particles will be emitted from anywhere within the - * cylinder. Otherwise the particles will be emitted from the surface of the - * cylinder. Defaults to true. - * @param radius The radius of the cylinder. Defaults to 1. - * @param height The height of the cylinder. Defaults to 1. - * @param yAxis If true, the cylinder will be aligned with the Y-axis instead - * of the Z-axis. Defaults to true. - * @param unkField Unknown. Fields1, index 0. Defaults to 5. - */ - constructor( - volume: boolean = true, - radius: ScalarPropertyArg = 1, - height: ScalarPropertyArg = 1, - yAxis: boolean = true, - unkField: number = 5, - ) { - super(ActionType.CylinderEmitterShape, [ - new IntField(unkField), - new BoolField(volume), - new BoolField(yAxis), - ], [], [ - radius instanceof Property ? radius : new ConstantProperty(radius as number), - height instanceof Property ? height : new ConstantProperty(height as number), - ]) - } - -} - -/** - * Makes all particles use the default initial direction from the emitter. - * - * A particle's initial direction is used for various things that need a - * direction but lack an inherent direction, for example the speed from the - * {@link ActionType.ParticleMultiplier ParticleMultiplier} action. It is also - * used to set the rotation of {@link ActionType.Line Line} and - * {@link ActionType.QuadLine QuadLine} particles without any movement. The - * default initial direction is controlled by the emitter shape. - */ -class NoParticleSpread extends Action { - - constructor() { - super(ActionType.NoParticleSpread) - } - -} - -/** - * Gives each particle a random initial direction offset within a circular - * cone. - * - * A particle's initial direction is used for various things that need a - * direction but lack an inherent direction, for example the speed from the - * {@link ActionType.ParticleMultiplier ParticleMultiplier} action. It is also - * used to set the rotation of {@link ActionType.Line Line} and - * {@link ActionType.QuadLine QuadLine} particles without any movement. The - * default initial direction is controlled by the emitter shape. - */ -class CircularParticleSpread extends Action { - - declare fields1: [BoolField] - - /** - * @param angle The maximum change in direction in degrees, the angle of the - * cone. Defaults to 30. - * - * **Argument**: {@link PropertyArgument.EffectAge Effect age} - * @param distribution Controls the distribution of the random directions - * that can be chosen. - * - * - At 0, all directions within the cone have an equal chance of being - * chosen. - * - At 1, the default direction is guaranteed to be chosen. - * - At -1, the maximum change in direction is guaranteed, meaning the chosen - * direction will always be a fixed number of degrees away from the default - * direction based on {@link angle}. - * - Values between these values smoothly blend between them. - * - Values outside of the -1 to 1 range also work, but may do some - * unexpected things. - * - * Defaults to 0. - * - * **Argument**: {@link PropertyArgument.EffectAge Effect age} - * @param unkField No so much unknown, just unnamed. If enabled, this limits - * the possible directions to only positive values on one axis, effectively - * cutting the cone of possible directions in half. Defaults to false. - */ - constructor( - angle: ScalarPropertyArg = 30, - distribution: ScalarPropertyArg = 0, - unkField: boolean = false - ) { - super(ActionType.CircularParticleSpread, [ - new BoolField(unkField) - ], [], [ - scalarFromArg(angle), - scalarFromArg(distribution), - ]) - } - - /** - * The maximum change in direction in degrees, the angle of the cone. - * - * **Argument**: {@link PropertyArgument.EffectAge Effect age} - */ - get angle(): ScalarProperty { return this.properties1[0] } - set angle(value: ScalarPropertyArg) { setPropertyInList(this.properties1, 0, value) } - - /** - * Controls the distribution of the random directions that can be chosen. - * - * - At 0, all directions within the cone have an equal chance of being - * chosen. - * - At 1, the default direction is guaranteed to be chosen. - * - At -1, the maximum change in direction is guaranteed, meaning the chosen - * direction will always be a fixed number of degrees away from the default - * direction based on {@link angle}. - * - Values between these values smoothly blend between them. - * - Values outside of the -1 to 1 range also work, but may do some - * unexpected things. - * - * **Argument**: {@link PropertyArgument.EffectAge Effect age} - */ - get distribution(): ScalarProperty { return this.properties1[1] } - set distribution(value: ScalarPropertyArg) { setPropertyInList(this.properties1, 1, value) } - - /** - * No so much unknown, just unnamed. If enabled, this limits the possible - * directions to only positive values on one axis, effectively cutting the - * cone of possible directions in half. - */ - get unkField() { return this.fields1[0].value } - set unkField(value) { this.fields1[0].value = value } - -} - -/** - * Gives each particle a random initial direction offset within an elliptical - * cone. - * - * A particle's initial direction is used for various things that need a - * direction but lack an inherent direction, for example the speed from the - * {@link ActionType.ParticleMultiplier ParticleMultiplier} action. It is also - * used to set the rotation of {@link ActionType.Line Line} and - * {@link ActionType.QuadLine QuadLine} particles without any movement. The - * default initial direction is controlled by the emitter shape. - */ -class EllipticalParticleSpread extends Action { - - declare fields1: [BoolField] - - /** - * @param angleX The maximum change in direction in degrees, one of the - * angles of the elliptical cone. Defaults to 30. - * - * **Argument**: {@link PropertyArgument.EffectAge Effect age} - * - * See also: - * - {@link angleY} - * @param angleX The maximum change in direction in degrees, one of the - * angles of the elliptical cone. Defaults to 30. - * - * **Argument**: {@link PropertyArgument.EffectAge Effect age} - * - * See also: - * - {@link angleX} - * @param distribution Controls the distribution of the random directions - * that can be chosen. - * - * - At 0, all directions within the cone have an equal chance of being - * chosen. - * - At 1, the default direction is guaranteed to be chosen. - * - At -1, the maximum change in direction is guaranteed, meaning the chosen - * direction will always be a fixed number of degrees away from the default - * direction based on {@link angleX} and {@link angleY}. - * - Values between these values smoothly blend between them. - * - Values outside of the -1 to 1 range also work, but may do some - * unexpected things. - * - * Defaults to 0. - * - * **Argument**: {@link PropertyArgument.EffectAge Effect age} - * @param unkField No so much unknown, just unnamed. If enabled, this limits - * the possible directions to only positive values on one axis, effectively - * cutting the cone of possible directions in half. Defaults to false. - */ - constructor( - angleX: ScalarPropertyArg = 30, - angleY: ScalarPropertyArg = 30, - distribution: ScalarPropertyArg = 0, - unkField: boolean = false - ) { - super(ActionType.CircularParticleSpread, [ - new BoolField(unkField) - ], [], [ - scalarFromArg(angleX), - scalarFromArg(angleY), - scalarFromArg(distribution), - ]) - } - - /** - * The maximum change in direction in degrees, one of the angles of the - * elliptical cone. - * - * **Argument**: {@link PropertyArgument.EffectAge Effect age} - */ - get angleX(): ScalarProperty { return this.properties1[0] } - set angleX(value: ScalarPropertyArg) { setPropertyInList(this.properties1, 0, value) } - - /** - * The maximum change in direction in degrees, one of the angles of the - * elliptical cone. - * - * **Argument**: {@link PropertyArgument.EffectAge Effect age} - */ - get angleY(): ScalarProperty { return this.properties1[1] } - set angleY(value: ScalarPropertyArg) { setPropertyInList(this.properties1, 1, value) } - - /** - * Controls the distribution of the random directions that can be chosen. - * - * - At 0, all directions within the cone have an equal chance of being - * chosen. - * - At 1, the default direction is guaranteed to be chosen. - * - At -1, the maximum change in direction is guaranteed, meaning the chosen - * direction will always be a fixed number of degrees away from the default - * direction based on {@link angleX} and {@link angleY}. - * - Values between these values smoothly blend between them. - * - Values outside of the -1 to 1 range also work, but may do some - * unexpected things. - * - * **Argument**: {@link PropertyArgument.EffectAge Effect age} - */ - get distribution(): ScalarProperty { return this.properties1[2] } - set distribution(value: ScalarPropertyArg) { setPropertyInList(this.properties1, 2, value) } - - /** - * No so much unknown, just unnamed. If enabled, this limits the possible - * directions to only positive values on one axis, effectively cutting the - * cone of possible directions in half. - */ - get unkField() { return this.fields1[0].value } - set unkField(value) { this.fields1[0].value = value } - -} - -/** - * Gives each particle a random initial direction offset within a rectangular - * cone. - * - * A particle's initial direction is used for various things that need a - * direction but lack an inherent direction, for example the speed from the - * {@link ActionType.ParticleMultiplier ParticleMultiplier} action. It is also - * used to set the rotation of {@link ActionType.Line Line} and - * {@link ActionType.QuadLine QuadLine} particles without any movement. The - * default initial direction is controlled by the emitter shape. - */ -class RectangularParticleSpread extends Action { - - /** - * @param angleX The maximum change in direction in degrees, one of the - * angles of the rectangular cone. Defaults to 30. - * - * **Argument**: {@link PropertyArgument.EffectAge Effect age} - * - * See also: - * - {@link angleY} - * @param angleX The maximum change in direction in degrees, one of the - * angles of the rectangular cone. Defaults to 30. - * - * **Argument**: {@link PropertyArgument.EffectAge Effect age} - * - * See also: - * - {@link angleX} - * @param distribution Controls the distribution of the random directions - * that can be chosen. - * - * - At 0, all directions within the cone have an equal chance of being - * chosen. - * - At 1, the default direction is guaranteed to be chosen. - * - At -1, the maximum change in direction is guaranteed, meaning the chosen - * direction will always be a fixed number of degrees away from the default - * direction based on {@link angleX} and {@link angleY}. - * - Values between these values smoothly blend between them. - * - Values outside of the -1 to 1 range also work, but may do some - * unexpected things. - * - * Defaults to 0. - * - * **Argument**: {@link PropertyArgument.EffectAge Effect age} - */ - constructor( - angleX: ScalarPropertyArg = 30, - angleY: ScalarPropertyArg = 30, - distribution: ScalarPropertyArg = 0 - ) { - super(ActionType.CircularParticleSpread, [], [], [ - scalarFromArg(angleX), - scalarFromArg(angleY), - scalarFromArg(distribution), - ]) - } - - /** - * The maximum change in direction in degrees, one of the angles of the - * rectangular cone. - * - * **Argument**: {@link PropertyArgument.EffectAge Effect age} - */ - get angleX(): ScalarProperty { return this.properties1[0] } - set angleX(value: ScalarPropertyArg) { setPropertyInList(this.properties1, 0, value) } - - /** - * The maximum change in direction in degrees, one of the angles of the - * rectangular cone. - * - * **Argument**: {@link PropertyArgument.EffectAge Effect age} - */ - get angleY(): ScalarProperty { return this.properties1[1] } - set angleY(value: ScalarPropertyArg) { setPropertyInList(this.properties1, 1, value) } - - /** - * Controls the distribution of the random directions that can be chosen. - * - * - At 0, all directions within the cone have an equal chance of being - * chosen. - * - At 1, the default direction is guaranteed to be chosen. - * - At -1, the maximum change in direction is guaranteed, meaning the chosen - * direction will always be a fixed number of degrees away from the default - * direction based on {@link angleX} and {@link angleY}. - * - Values between these values smoothly blend between them. - * - Values outside of the -1 to 1 range also work, but may do some - * unexpected things. - * - * **Argument**: {@link PropertyArgument.EffectAge Effect age} - */ - get distribution(): ScalarProperty { return this.properties1[2] } - set distribution(value: ScalarPropertyArg) { setPropertyInList(this.properties1, 2, value) } - -} - -/** - * Super class for some of the 6xx actions that share part of their fields2 - * structure with other 6xx actions. - */ -class CommonFields2Action extends Action { - - /** - * Controls the color of the additional bloom effect. The colors of the - * particles will be multiplied with this color to get the final color - * of the bloom effect. - * - * Note: - * - This has no effect if the "Effects Quality" setting is set to "Low". - * - This does not affect the natural bloom effect from high color values. - * - * See also: - * - {@link bloomStrength} - */ - get bloomColor() { - return [ - this.fields2[5].value as number, - this.fields2[6].value as number, - this.fields2[7].value as number, - ] as Vector3 - } - set bloomColor([r, g, b]) { - this.fields2[5].value = r - this.fields2[6].value = g - this.fields2[7].value = b - } - - /** - * Controls the strength of the additional bloom effect. - * - * Note: - * - This has no effect if the "Effects Quality" setting is set to "Low". - * - This does not affect the natural bloom effect from high color values. - * - * See also: - * - {@link bloomColor} - */ - get bloomStrength() { return this.fields2[8].value as number } - set bloomStrength(value) { this.fields2[8].value = value } - - /** - * Minimum view distance. If the particle is closer than this distance from - * the camera, it will be hidden. Can be set to -1 to disable the limit. - * - * See also: - * - {@link maxDistance} - */ - get minDistance() { return this.fields2[18].value as number } - set minDistance(value) { this.fields2[18].value = value } - - /** - * Maximum view distance. If the particle is farther away than this distance - * from the camera, it will be hidden. Can be set to -1 to disable the limit. - * - * See also: - * - {@link minDistance} - */ - get maxDistance() { return this.fields2[19].value as number } - set maxDistance(value) { this.fields2[19].value = value } - -} - -export interface PointSpriteParams { - /** - * Texture ID. Defaults to 1. - * - * **Argument**: {@link PropertyArgument.Constant Constant 0} - */ - texture?: BlendMode | ScalarProperty - /** - * Blend mode. Defaults to {@link BlendMode.Normal}. - * - * **Argument**: {@link PropertyArgument.Constant Constant 0} - */ - blendMode?: BlendMode | ScalarProperty - /** - * Particle size. Defaults to 1. - * - * **Argument**: {@link PropertyArgument.ParticleAge Particle age} - */ - size?: ScalarPropertyArg - /** - * Color multiplier. Defaults to [1, 1, 1, 1]. - * - * **Argument**: {@link PropertyArgument.ParticleAge Particle age} - */ - color1?: Vector4PropertyArg - /** - * Color multiplier. Defaults to [1, 1, 1, 1]. - * - * **Argument**: {@link PropertyArgument.EmissionTime Emission time} - */ - color2?: Vector4PropertyArg - /** - * Color multiplier. Defaults to [1, 1, 1, 1]. - * - * **Argument**: {@link PropertyArgument.EffectAge Effect age} - */ - color3?: Vector4PropertyArg - /** - * Scalar multiplier for the color that does not affect the alpha. - * Effectively a brightness multiplier. Defaults to 1. - * - * **Argument**: {@link PropertyArgument.EffectAge Effect age} - */ - rgbMultiplier?: ScalarPropertyArg - /** - * Alpha multiplier. Defaults to 1. - * - * **Argument**: {@link PropertyArgument.EffectAge Effect age} - */ - alphaMultiplier?: ScalarPropertyArg - /** - * Controls the color of the additional bloom effect. The colors of the - * particles will be multiplied with this color to get the final color - * of the bloom effect. Defaults to [1, 1, 1]. - * - * Note: - * - This has no effect if the "Effects Quality" setting is set to "Low". - * - This does not affect the natural bloom effect from high color values. - * - * See also: - * - {@link bloomStrength} - */ - bloomColor?: Vector3 - /** - * Controls the strength of the additional bloom effect. Defaults to 0. - * - * Note: - * - This has no effect if the "Effects Quality" setting is set to "Low". - * - This does not affect the natural bloom effect from high color values. - * - * See also: - * - {@link bloomColor} - */ - bloomStrength?: number - /** - * Minimum view distance. If the particle is closer than this distance from - * the camera, it will be hidden. Can be set to -1 to disable the limit. - * Defaults to -1. - * - * See also: - * - {@link maxDistance} - */ - minDistance?: number - /** - * Maximum view distance. If the particle is farther away than this distance - * from the camera, it will be hidden. Can be set to -1 to disable the limit. - * Defaults to -1. - * - * See also: - * - {@link minDistance} - */ - maxDistance?: number -} -/** - * Very basic point sprite particle. Similar to - * {@link ActionType.BillboardEx BillboardEx}, but far simpler. - */ -class PointSprite extends CommonFields2Action { - - constructor({ - texture = 1, - blendMode = BlendMode.Normal, - size = 1, - color1 = [1, 1, 1, 1], - color2 = [1, 1, 1, 1], - color3 = [1, 1, 1, 1], - rgbMultiplier = 1, - alphaMultiplier = 1, - bloomColor = [1, 1, 1], - bloomStrength = 0, - minDistance = -1, - maxDistance = -1, - }: PointSpriteParams = {}) { - super(ActionType.PointSprite, [ - /* 0 */ new IntField(-2), - /* 1 */ new IntField(-2), - /* 2 */ new IntField(0), - /* 3 */ new IntField(1), - /* 4 */ new IntField(1), - ], [ - /* 0 */ new IntField(0), - /* 1 */ new IntField(0), - /* 2 */ new IntField(8), - /* 3 */ new IntField(0), - /* 4 */ new IntField(1), - /* 5 */ new FloatField(bloomColor[0]), - /* 6 */ new FloatField(bloomColor[1]), - /* 7 */ new FloatField(bloomColor[2]), - /* 8 */ new FloatField(bloomStrength), - /* 9 */ new IntField(0), - /* 10 */ new IntField(0), - /* 11 */ new IntField(0), - /* 12 */ new IntField(0), - /* 13 */ new IntField(0), - /* 14 */ new FloatField(-1), - /* 15 */ new FloatField(-1), - /* 16 */ new FloatField(-1), - /* 17 */ new FloatField(-1), - /* 18 */ new FloatField(minDistance), - /* 19 */ new FloatField(maxDistance), - /* 20 */ new IntField(0), - /* 21 */ new IntField(0), - /* 22 */ new IntField(0), - /* 23 */ new IntField(0), - /* 24 */ new IntField(0), - /* 25 */ new FloatField(1), - /* 26 */ new FloatField(0), - /* 27 */ new IntField(0), - /* 28 */ new IntField(0), - /* 29 */ new IntField(0), - /* 30 */ new IntField(0), - /* 31 */ new IntField(0), - /* 32 */ new IntField(0), - /* 33 */ new IntField(0), - /* 34 */ new FloatField(0), - /* 35 */ new IntField(-1), - /* 36 */ new IntField(-2), - /* 37 */ new IntField(0), - /* 38 */ new IntField(0), - /* 39 */ new IntField(0), - ], [ - /* 0 */ scalarFromArg(texture), - /* 1 */ scalarFromArg(blendMode), - /* 2 */ scalarFromArg(size), - /* 3 */ vectorFromArg(color1), - /* 4 */ vectorFromArg(color2), - /* 5 */ vectorFromArg(color3), - ], [ - /* 0 */ scalarFromArg(rgbMultiplier), - /* 1 */ scalarFromArg(alphaMultiplier), - /* 2 */ new ConstantProperty(0), - /* 3 */ new ConstantProperty(1, 1, 1, 1), - /* 4 */ new ConstantProperty(1, 1, 1, 1), - /* 5 */ new ConstantProperty(1, 1, 1, 1), - /* 6 */ new ConstantProperty(0), - ]) - } - - /** - * Texture ID. - * - * **Argument**: {@link PropertyArgument.Constant Constant 0} - */ - get texture() { return this.properties1[0].valueAt(0) as BlendMode } - set texture(value: ScalarPropertyArg) { setPropertyInList(this.properties1, 0, value) } - /** - * Texture ID. - * - * **Argument**: {@link PropertyArgument.Constant Constant 0} - */ - get textureProperty() { return this.properties1[0] } - - /** - * Blend mode. - * - * **Argument**: {@link PropertyArgument.Constant Constant 0} - */ - get blendMode() { return this.properties1[1].valueAt(0) as BlendMode } - set blendMode(value: ScalarPropertyArg) { setPropertyInList(this.properties1, 1, value) } - /** - * Blend mode. - * - * **Argument**: {@link PropertyArgument.Constant Constant 0} - */ - get blendModeProperty() { return this.properties1[1] } - - /** - * Particle size. - * - * **Argument**: {@link PropertyArgument.ParticleAge Particle age} - */ - get size() { return this.properties1[2] } - set size(value: ScalarPropertyArg) { setPropertyInList(this.properties1, 2, value) } - - /** - * Color multiplier. - * - * **Argument**: {@link PropertyArgument.ParticleAge Particle age} - */ - get color1() { return this.properties1[3] } - set color1(value: Vector4PropertyArg) { setPropertyInList(this.properties1, 3, value) } - - /** - * Color multiplier. - * - * **Argument**: {@link PropertyArgument.EmissionTime Emission time} - */ - get color2() { return this.properties1[4] } - set color2(value: Vector4PropertyArg) { setPropertyInList(this.properties1, 4, value) } - - /** - * Color multiplier. - * - * **Argument**: {@link PropertyArgument.EffectAge Effect age} - */ - get color3() { return this.properties1[5] } - set color3(value: Vector4PropertyArg) { setPropertyInList(this.properties1, 5, value) } - - /** - * Scalar multiplier for the color that does not affect the alpha. - * Effectively a brightness multiplier. - * - * **Argument**: {@link PropertyArgument.EffectAge Effect age} - */ - get rgbMultiplier() { return this.properties2[0] } - set rgbMultiplier(value: ScalarPropertyArg) { setPropertyInList(this.properties2, 0, value) } - - /** - * Alpha multiplier. - * - * **Argument**: {@link PropertyArgument.EffectAge Effect age} - */ - get alphaMultiplier() { return this.properties2[1] } - set alphaMultiplier(value: ScalarPropertyArg) { setPropertyInList(this.properties2, 1, value) } - -} - -export interface LineParams { - /** - * Blend mode. Defaults to {@link BlendMode.Normal}. - * - * **Argument**: {@link PropertyArgument.Constant Constant 0} - */ - blendMode?: BlendMode | ScalarProperty - /** - * The length of the line. Defaults to 1. - * - * **Argument**: {@link PropertyArgument.EmissionTime Emission time} - * - * See also: - * - {@link lengthMultiplier} - */ - length?: ScalarPropertyArg - /** - * Color multiplier for the entire line. Defaults to [1, 1, 1, 1]. - * - * Seemingly identical to {@link color2}? - * - * **Argument**: {@link PropertyArgument.ParticleAge Particle age} - */ - color1?: Vector4PropertyArg - /** - * Color multiplier for the entire line. Defaults to [1, 1, 1, 1]. - * - * Seemingly identical to {@link color1}? - * - * **Argument**: {@link PropertyArgument.ParticleAge Particle age} - */ - color2?: Vector4PropertyArg - /** - * The color for the start of the line. Defaults to [1, 1, 1, 1]. - * - * This color transitions linearly into the {@link endColor end color} across the line. - * - * **Argument**: {@link PropertyArgument.EffectAge Effect age} - */ - startColor?: Vector4PropertyArg - /** - * The color for the end of the line. Defaults to [1, 1, 1, 1]. - * - * This color transitions linearly into the {@link startColor start color} across the line. - * - * **Argument**: {@link PropertyArgument.EffectAge Effect age} - */ - endColor?: Vector4PropertyArg - /** - * Multiplier for the line {@link length}. Defaults to 1. - * - * **Argument**: {@link PropertyArgument.ParticleAge Particle age} - */ - lengthMultiplier?: ScalarPropertyArg - /** - * Color multiplier for the entire line. Defaults to [1, 1, 1, 1]. - * - * **Argument**: {@link PropertyArgument.EffectAge Effect age} - */ - color3?: Vector4PropertyArg - /** - * Scalar multiplier for the color that does not affect the alpha. - * Effectively a brightness multiplier. Defaults to 1. - * - * **Argument**: {@link PropertyArgument.EffectAge Effect age} - */ - rgbMultiplier?: ScalarPropertyArg - /** - * Alpha multiplier. Defaults to 1. - * - * **Argument**: {@link PropertyArgument.EffectAge Effect age} - */ - alphaMultiplier?: ScalarPropertyArg - /** - * Controls the color of the additional bloom effect. The colors of the - * particles will be multiplied with this color to get the final color - * of the bloom effect. Defaults to [1, 1, 1]. - * - * Note: - * - This has no effect if the "Effects Quality" setting is set to "Low". - * - This does not affect the natural bloom effect from high color values. - * - * See also: - * - {@link bloomStrength} - */ - bloomColor?: Vector3 - /** - * Controls the strength of the additional bloom effect. Defaults to 0. - * - * Note: - * - This has no effect if the "Effects Quality" setting is set to "Low". - * - This does not affect the natural bloom effect from high color values. - * - * See also: - * - {@link bloomColor} - */ - bloomStrength?: number - /** - * Minimum view distance. If the particle is closer than this distance from - * the camera, it will be hidden. Can be set to -1 to disable the limit. - * Defaults to -1. - * - * See also: - * - {@link maxDistance} - */ - minDistance?: number - /** - * Maximum view distance. If the particle is farther away than this distance - * from the camera, it will be hidden. Can be set to -1 to disable the limit. - * Defaults to -1. - * - * See also: - * - {@link minDistance} - */ - maxDistance?: number -} -/** - * Simple line particle. It automatically rotates to match the direction it's - * moving. - */ -class Line extends CommonFields2Action { - - constructor({ - blendMode = BlendMode.Normal, - length = 1, - color1 = [1, 1, 1, 1], - color2 = [1, 1, 1, 1], - startColor = [1, 1, 1, 1], - endColor = [1, 1, 1, 1], - lengthMultiplier = 1, - color3 = [1, 1, 1, 1], - rgbMultiplier = 1, - alphaMultiplier = 1, - bloomColor = [1, 1, 1], - bloomStrength = 0, - minDistance = -1, - maxDistance = -1, - }: LineParams = {}) { - super(ActionType.Line, [ - /* 0 */ new IntField(-1), - /* 1 */ new IntField(1), - /* 2 */ new IntField(1), - ], [ - /* 0 */ new IntField(0), - /* 1 */ new IntField(0), - /* 2 */ new IntField(8), - /* 3 */ new IntField(0), - /* 4 */ new IntField(1), - /* 5 */ new FloatField(bloomColor[0]), - /* 6 */ new FloatField(bloomColor[1]), - /* 7 */ new FloatField(bloomColor[2]), - /* 8 */ new FloatField(bloomStrength), - /* 9 */ new IntField(0), - /* 10 */ new IntField(0), - /* 11 */ new IntField(0), - /* 12 */ new IntField(0), - /* 13 */ new IntField(0), - /* 14 */ new FloatField(-1), - /* 15 */ new FloatField(-1), - /* 16 */ new FloatField(-1), - /* 17 */ new FloatField(-1), - /* 18 */ new FloatField(minDistance), - /* 19 */ new FloatField(maxDistance), - /* 20 */ new IntField(0), - /* 21 */ new IntField(0), - /* 22 */ new IntField(0), - /* 23 */ new IntField(0), - /* 24 */ new IntField(0), - /* 25 */ new FloatField(1), - /* 26 */ new FloatField(0), - /* 27 */ new IntField(0), - /* 28 */ new IntField(0), - /* 29 */ new IntField(0), - /* 30 */ new IntField(0), - /* 31 */ new IntField(0), - /* 32 */ new IntField(0), - /* 33 */ new IntField(0), - /* 34 */ new FloatField(0), - /* 35 */ new IntField(-2), - /* 36 */ new IntField(-2), - /* 37 */ new IntField(0), - /* 38 */ new IntField(0), - /* 39 */ new IntField(0), - ], [ - /* 0 */ scalarFromArg(blendMode), - /* 1 */ scalarFromArg(length), - /* 2 */ vectorFromArg(color1), - /* 3 */ vectorFromArg(color2), - /* 4 */ vectorFromArg(startColor), - /* 5 */ vectorFromArg(endColor), - /* 6 */ scalarFromArg(lengthMultiplier), - /* 7 */ vectorFromArg(color3), - ], [ - /* 0 */ scalarFromArg(rgbMultiplier), - /* 1 */ scalarFromArg(alphaMultiplier), - /* 2 */ new ConstantProperty(0), - /* 3 */ new ConstantProperty(1, 1, 1, 1), - /* 4 */ new ConstantProperty(1, 1, 1, 1), - /* 5 */ new ConstantProperty(1, 1, 1, 1), - /* 6 */ new ConstantProperty(0), - ]) - } - - /** - * Blend mode. - * - * **Argument**: {@link PropertyArgument.Constant Constant 0} - */ - get blendMode() { return this.properties1[0].valueAt(0) as BlendMode } - set blendMode(value: ScalarPropertyArg) { setPropertyInList(this.properties1, 0, value) } - /** - * Blend mode. - * - * **Argument**: {@link PropertyArgument.Constant Constant 0} - */ - get blendModeProperty() { return this.properties1[0] } - - /** - * The length of the line. - * - * **Argument**: {@link PropertyArgument.EmissionTime Emission time} - * - * See also: - * - {@link lengthMultiplier} - */ - get length() { return this.properties1[1] } - set length(value: ScalarPropertyArg) { setPropertyInList(this.properties1, 1, value) } - - /** - * Color multiplier for the entire line. - * - * Seemingly identical to {@link color2}? - * - * **Argument**: {@link PropertyArgument.ParticleAge Particle age} - */ - get color1() { return this.properties1[2] } - set color1(value: Vector4PropertyArg) { setPropertyInList(this.properties1, 2, value) } - - /** - * Color multiplier for the entire line. - * - * Seemingly identical to {@link color1}? - * - * **Argument**: {@link PropertyArgument.ParticleAge Particle age} - */ - get color2() { return this.properties1[3] } - set color2(value: Vector4PropertyArg) { setPropertyInList(this.properties1, 3, value) } - - /** - * The color for the start of the line. - * - * This color transitions linearly into the {@link endColor end color} across the line. - * - * **Argument**: {@link PropertyArgument.EffectAge Effect age} - */ - get startColor() { return this.properties1[4] } - set startColor(value: Vector4PropertyArg) { setPropertyInList(this.properties1, 4, value) } - - /** - * The color for the end of the line. - * - * This color transitions linearly into the {@link startColor start color} across the line. - * - * **Argument**: {@link PropertyArgument.EffectAge Effect age} - */ - get endColor() { return this.properties1[5] } - set endColor(value: Vector4PropertyArg) { setPropertyInList(this.properties1, 5, value) } - - /** - * Multiplier for the line {@link length}. - * - * **Argument**: {@link PropertyArgument.ParticleAge Particle age} - */ - get lengthMultiplier() { return this.properties1[6] } - set lengthMultiplier(value: ScalarPropertyArg) { setPropertyInList(this.properties1, 6, value) } - - /** - * Color multiplier for the entire line. - * - * **Argument**: {@link PropertyArgument.EffectAge Effect age} - */ - get color3() { return this.properties1[7] } - set color3(value: Vector4PropertyArg) { setPropertyInList(this.properties1, 7, value) } - - /** - * Scalar multiplier for the color that does not affect the alpha. - * Effectively a brightness multiplier. - * - * **Argument**: {@link PropertyArgument.EffectAge Effect age} - */ - get rgbMultiplier() { return this.properties2[0] } - set rgbMultiplier(value: ScalarPropertyArg) { setPropertyInList(this.properties2, 0, value) } - - /** - * Alpha multiplier. - * - * **Argument**: {@link PropertyArgument.EffectAge Effect age} - */ - get alphaMultiplier() { return this.properties2[1] } - set alphaMultiplier(value: ScalarPropertyArg) { setPropertyInList(this.properties2, 1, value) } - -} - -export interface QuadLineParams { - blendMode?: BlendMode | ScalarProperty - width?: ScalarPropertyArg - height?: ScalarPropertyArg - /** - * Color multiplier for the entire rectangle. - * - * Seemingly identical to {@link color2}? - * - * **Argument**: {@link PropertyArgument.ParticleAge Particle age} - */ - color1?: Vector4PropertyArg - /** - * Color multiplier for the entire rectangle. - * - * Seemingly identical to {@link color1}? - * - * **Argument**: {@link PropertyArgument.ParticleAge Particle age} - */ - color2?: Vector4PropertyArg - /** - * The color for the "start" edge of the rectangle. - * - * This color transitions linearly into the {@link endColor end color} across the rectangle. - * - * **Argument**: {@link PropertyArgument.EffectAge Effect age} - */ - startColor?: Vector4PropertyArg - /** - * The color for the "end" edge of the rectangle. - * - * This color transitions linearly into the {@link startColor start color} across the rectangle. - * - * **Argument**: {@link PropertyArgument.EffectAge Effect age} - */ - endColor?: Vector4PropertyArg - widthMultiplier?: ScalarPropertyArg - heightMultiplier?: ScalarPropertyArg - /** - * Color multiplier for the entire rectangle. - * - * **Argument**: {@link PropertyArgument.EffectAge Effect age} - */ - color3?: Vector4PropertyArg - rgbMultiplier?: ScalarPropertyArg - alphaMultiplier?: ScalarPropertyArg - /** - * Controls the color of the additional bloom effect. The colors of the - * particles will be multiplied with this color to get the final color - * of the bloom effect. Defaults to [1, 1, 1]. - * - * Note: - * - This has no effect if the "Effects Quality" setting is set to "Low". - * - This does not affect the natural bloom effect from high color values. - * - * See also: - * - {@link bloomStrength} - */ - bloomColor?: Vector3 - /** - * Controls the strength of the additional bloom effect. Defaults to 0. - * - * Note: - * - This has no effect if the "Effects Quality" setting is set to "Low". - * - This does not affect the natural bloom effect from high color values. - * - * See also: - * - {@link bloomColor} - */ - bloomStrength?: number - /** - * Minimum view distance. If the particle is closer than this distance from - * the camera, it will be hidden. Can be set to -1 to disable the limit. - * Defaults to -1. - * - * See also: - * - {@link maxDistance} - */ - minDistance?: number - /** - * Maximum view distance. If the particle is farther away than this distance - * from the camera, it will be hidden. Can be set to -1 to disable the limit. - * Defaults to -1. - * - * See also: - * - {@link minDistance} - */ - maxDistance?: number - unkScalarProp2_2?: ScalarPropertyArg - unkVec4Prop2_3?: Vector4PropertyArg - unkVec4Prop2_4?: Vector4PropertyArg - unkVec4Prop2_5?: Vector4PropertyArg - unkScalarProp2_6?: ScalarPropertyArg -} -/** - * Simple rectangular particle, very similar to - * {@link ActionType.Line Line particles}, but has properties that control - * the width as well as the length. It automatically rotates to match the - * direction it's moving. - */ -class QuadLine extends CommonFields2Action { - - constructor({ - blendMode = BlendMode.Normal, - width = 1, - height = 1, - color1 = [1, 1, 1, 1], - color2 = [1, 1, 1, 1], - startColor = [1, 1, 1, 1], - endColor = [1, 1, 1, 1], - widthMultiplier = 1, - heightMultiplier = 1, - color3 = [1, 1, 1, 1], - rgbMultiplier = 1, - alphaMultiplier = 1, - bloomColor = [1, 1, 1], - bloomStrength = 0, - minDistance = -1, - maxDistance = -1, - unkScalarProp2_2 = 0, - unkVec4Prop2_3 = [1, 1, 1, 1], - unkVec4Prop2_4 = [1, 1, 1, 1], - unkVec4Prop2_5 = [1, 1, 1, 1], - unkScalarProp2_6 = 0, - }: QuadLineParams = {}) { - super(ActionType.QuadLine, [ - /* 0 */ new IntField(-1), - /* 1 */ new IntField(1), - /* 2 */ new IntField(1), - ], [ - /* 0 */ new IntField(0), - /* 1 */ new IntField(0), - /* 2 */ new IntField(8), - /* 3 */ new IntField(0), - /* 4 */ new IntField(1), - /* 5 */ new FloatField(bloomColor[0]), - /* 6 */ new FloatField(bloomColor[1]), - /* 7 */ new FloatField(bloomColor[2]), - /* 8 */ new FloatField(bloomStrength), - /* 9 */ new IntField(0), - /* 10 */ new IntField(0), - /* 11 */ new IntField(0), - /* 12 */ new IntField(0), - /* 13 */ new IntField(0), - /* 14 */ new FloatField(-1), - /* 15 */ new FloatField(-1), - /* 16 */ new FloatField(-1), - /* 17 */ new FloatField(-1), - /* 18 */ new FloatField(minDistance), - /* 19 */ new FloatField(maxDistance), - /* 20 */ new IntField(0), - /* 21 */ new IntField(0), - /* 22 */ new IntField(0), - /* 23 */ new IntField(0), - /* 24 */ new IntField(0), - /* 25 */ new FloatField(1), - /* 26 */ new FloatField(0), - /* 27 */ new IntField(1), - /* 28 */ new IntField(0), - /* 29 */ new FloatField(5), - /* 30 */ new FloatField(0), - /* 31 */ new IntField(0), - /* 32 */ new IntField(1), - /* 33 */ new BoolField(false), - /* 34 */ new FloatField(0), - /* 35 */ new IntField(-1), - /* 36 */ new IntField(-2), - /* 37 */ new IntField(0), - /* 38 */ new FloatField(0), - /* 39 */ new IntField(1), - ], [ // Properties1 - /* 0 */ scalarFromArg(blendMode), - /* 1 */ scalarFromArg(width), - /* 2 */ scalarFromArg(height), - /* 3 */ vectorFromArg(color1), - /* 4 */ vectorFromArg(color2), - /* 5 */ vectorFromArg(startColor), - /* 6 */ vectorFromArg(endColor), - /* 7 */ scalarFromArg(widthMultiplier), - /* 8 */ scalarFromArg(heightMultiplier), - /* 9 */ vectorFromArg(color3), - ], [ // Properties2 - /* 0 */ scalarFromArg(rgbMultiplier), - /* 1 */ scalarFromArg(alphaMultiplier), - /* 2 */ scalarFromArg(unkScalarProp2_2), - /* 3 */ vectorFromArg(unkVec4Prop2_3), - /* 4 */ vectorFromArg(unkVec4Prop2_4), - /* 5 */ vectorFromArg(unkVec4Prop2_5), - /* 6 */ scalarFromArg(unkScalarProp2_6), - ]) - } - - get blendMode() { return this.properties1[0].valueAt(0) as BlendMode } - set blendMode(value: ScalarPropertyArg) { setPropertyInList(this.properties1, 0, value) } - get blendModeProperty() { return this.properties1[0] } - - get width() { return this.properties1[1] } - set width(value: ScalarPropertyArg) { setPropertyInList(this.properties1, 1, value) } - - get height() { return this.properties1[2] } - set height(value: ScalarPropertyArg) { setPropertyInList(this.properties1, 2, value) } - - /** - * Color multiplier for the entire rectangle. - * - * Seemingly identical to {@link color2}? - * - * **Argument**: {@link PropertyArgument.ParticleAge Particle age} - */ - get color1() { return this.properties1[3] } - set color1(value: Vector4PropertyArg) { setPropertyInList(this.properties1, 3, value) } - - /** - * Color multiplier for the entire rectangle. - * - * Seemingly identical to {@link color1}? - * - * **Argument**: {@link PropertyArgument.ParticleAge Particle age} - */ - get color2() { return this.properties1[4] } - set color2(value: Vector4PropertyArg) { setPropertyInList(this.properties1, 4, value) } - - /** - * The color for the "start" edge of the rectangle. - * - * This color transitions linearly into the {@link endColor end color} across the rectangle. - * - * **Argument**: {@link PropertyArgument.EffectAge Effect age} - */ - get startColor() { return this.properties1[5] } - set startColor(value: Vector4PropertyArg) { setPropertyInList(this.properties1, 5, value) } - - /** - * The color for the "end" edge of the rectangle. - * - * This color transitions linearly into the {@link startColor start color} across the rectangle. - * - * **Argument**: {@link PropertyArgument.EffectAge Effect age} - */ - get endColor() { return this.properties1[6] } - set endColor(value: Vector4PropertyArg) { setPropertyInList(this.properties1, 6, value) } - - get widthMultiplier() { return this.properties1[7] } - set widthMultiplier(value: ScalarPropertyArg) { setPropertyInList(this.properties1, 7, value) } - - get heightMultiplier() { return this.properties1[8] } - set heightMultiplier(value: ScalarPropertyArg) { setPropertyInList(this.properties1, 8, value) } - - /** - * Color multiplier for the entire rectangle. - * - * **Argument**: {@link PropertyArgument.EffectAge Effect age} - */ - get color3() { return this.properties1[9] } - set color3(value: Vector4PropertyArg) { setPropertyInList(this.properties1, 9, value) } - - get rgbMultiplier() { return this.properties2[0] } - set rgbMultiplier(value: ScalarPropertyArg) { setPropertyInList(this.properties2, 0, value) } - - get alphaMultiplier() { return this.properties2[1] } - set alphaMultiplier(value: ScalarPropertyArg) { setPropertyInList(this.properties2, 1, value) } - -} - -export interface BillboardExParams { - /** - * Texture ID. Defaults to 1. - * - * **Argument**: {@link PropertyArgument.Constant Constant 0} - */ - texture?: ScalarPropertyArg - /** - * Blend mode. Defaults to {@link BlendMode.Normal}. - * - * **Argument**: {@link PropertyArgument.Constant Constant 0} - */ - blendMode?: BlendMode | ScalarProperty - /** - * X position offset. Defaults to 0. - * - * **Argument**: {@link PropertyArgument.ParticleAge Particle age} - */ - offsetX?: ScalarPropertyArg - /** - * Y position offset. Defaults to 0. - * - * **Argument**: {@link PropertyArgument.ParticleAge Particle age} - */ - offsetY?: ScalarPropertyArg - /** - * Z position offset. Defaults to 0. - * - * **Argument**: {@link PropertyArgument.ParticleAge Particle age} - */ - offsetZ?: ScalarPropertyArg - /** - * The width of the particle. - * - * If {@link uniformScale} is enabled, this also controls the height. - * - * Defaults to 1. - * - * **Argument**: {@link PropertyArgument.ParticleAge Particle age} - */ - width?: ScalarPropertyArg - /** - * The height of the particle. - * - * If {@link uniformScale} is enabled, {@link width} also controls the - * height, and this property is ignored. - * - * Defaults to 1. - * - * **Argument**: {@link PropertyArgument.ParticleAge Particle age} - */ - height?: ScalarPropertyArg - /** - * Color multiplier. Defaults to [1, 1, 1, 1]. - * - * **Argument**: {@link PropertyArgument.ParticleAge Particle age} - */ - color1?: Vector4PropertyArg - /** - * Color multiplier. Defaults to [1, 1, 1, 1]. - * - * **Argument**: {@link PropertyArgument.EmissionTime Emission time} - */ - color2?: Vector4PropertyArg - /** - * Color multiplier. Defaults to [1, 1, 1, 1]. - * - * **Argument**: {@link PropertyArgument.EffectAge Effect age}. - */ - color3?: Vector4PropertyArg - /** - * Parts of the particle with less opacity than this threshold will be - * invisible. The range is 0-255. Defaults to 0. - * - * **Argument**: {@link PropertyArgument.EffectAge Effect age} - */ - alphaThreshold?: ScalarPropertyArg - /** - * Rotation around the X-axis in degrees. Defaults to 0. - * - * **Argument**: {@link PropertyArgument.Constant Constant 0} - * - * See also: - * - {@link rotationSpeedX} - * - {@link rotationSpeedMultiplierX} - */ - rotationX?: ScalarPropertyArg - /** - * Rotation around the Y-axis in degrees. Defaults to 0. - * - * **Argument**: {@link PropertyArgument.Constant Constant 0} - * - * See also: - * - {@link rotationSpeedY} - * - {@link rotationSpeedMultiplierY} - */ - rotationY?: ScalarPropertyArg - /** - * Rotation around the Z-axis in degrees. Defaults to 0. - * - * **Argument**: {@link PropertyArgument.Constant Constant 0} - * - * See also: - * - {@link rotationSpeedZ} - * - {@link rotationSpeedMultiplierZ} - */ - rotationZ?: ScalarPropertyArg - /** - * Rotation speed around the X-axis in degrees per second. Defaults to 0. - * - * **Argument**: {@link PropertyArgument.ParticleAge Particle age} - * - * See also: - * - {@link rotationX} - * - {@link rotationSpeedMultiplierX} - */ - rotationSpeedX?: ScalarPropertyArg - /** - * Rotation speed around the Y-axis in degrees per second. Defaults to 0. - * - * **Argument**: {@link PropertyArgument.ParticleAge Particle age} - * - * See also: - * - {@link rotationY} - * - {@link rotationSpeedMultiplierY} - */ - rotationSpeedY?: ScalarPropertyArg - /** - * Rotation speed around the Z-axis in degrees per second. Defaults to 0. - * - * **Argument**: {@link PropertyArgument.ParticleAge Particle age} - * - * See also: - * - {@link rotationZ} - * - {@link rotationSpeedMultiplierZ} - */ - rotationSpeedZ?: ScalarPropertyArg - /** - * Multiplier for the rotation speed around the X-axis. Defaults to 1. - * - * **Argument**: {@link PropertyArgument.ParticleAge Particle age} - * - * See also: - * - {@link rotationX} - * - {@link rotationSpeedX} - */ - rotationSpeedMultiplierX?: ScalarPropertyArg - /** - * Multiplier for the rotation speed around the Y-axis. Defaults to 1. - * - * **Argument**: {@link PropertyArgument.ParticleAge Particle age} - * - * See also: - * - {@link rotationY} - * - {@link rotationSpeedY} - */ - rotationSpeedMultiplierY?: ScalarPropertyArg - /** - * Multiplier for the rotation speed around the Z-axis. Defaults to 1. - * - * **Argument**: {@link PropertyArgument.ParticleAge Particle age} - * - * See also: - * - {@link rotationZ} - * - {@link rotationSpeedZ} - */ - rotationSpeedMultiplierZ?: ScalarPropertyArg - /** - * Positive values will make the particle draw in front of objects closer to - * the camera, while negative values will make it draw behind objects farther - * away from the camera. Defaults to 0. - * - * **Argument**: {@link PropertyArgument.ParticleAge Particle age} - * - * See also: - * - {@link negativeDepthOffset} - */ - depthOffset?: ScalarPropertyArg - /** - * The index of the frame to show from the texture atlas. Can be animated - * using a {@link PropertyFunction.Linear linear property} or similar. - * Defaults to 0. - * - * Seemingly identical to {@link frameIndexOffset}? The sum of these two - * properties is the actual frame index that gets used. - * - * **Argument**: {@link PropertyArgument.ParticleAge Particle age} - */ - frameIndex?: ScalarPropertyArg - /** - * Seemingly identical to {@link frameIndex}? The sum of these two properties - * is the actual frame index that gets used. Defaults to 0. - * - * **Argument**: {@link PropertyArgument.ParticleAge Particle age} - */ - frameIndexOffset?: ScalarPropertyArg - /** - * Scalar multiplier for the color that does not affect the alpha. - * Effectively a brightness multiplier. Defaults to 1. - * - * **Argument**: {@link PropertyArgument.EffectAge Effect age} - */ - rgbMultiplier?: ScalarPropertyArg - /** - * Alpha multiplier. Defaults to 1. - * - * **Argument**: {@link PropertyArgument.EffectAge Effect age} - */ - alphaMultiplier?: ScalarPropertyArg - /** - * Controls the orientation mode for the particles. See - * {@link OrientationMode} for more information. Defaults to - * {@link OrientationMode.CameraPlane}. - */ - orientation?: OrientationMode - /** - * Normal map ID. Defaults to 0. - */ - normalMap?: number - /** - * Each particle will pick a random number between this value and 1, and the - * width of the particle will be multiplied by this number. For example, - * setting this to 0.5 will make the particles randomly thinner, down to half - * width. Setting it to 2 will make them randomly wider, up to double width. - * Defaults to 1. - * - * If {@link uniformScale} is enabled, this also affects the height. - * - * See also: - * - {@link scaleVariationY} - */ - scaleVariationX?: number - /** - * Each particle will pick a random number between this value and 1, and the - * height of the particle will be multiplied by this number. For example, - * setting this to 0.5 will make the particles randomly shorter, down to half - * height. Setting it to 2 will make them randomly taller, up to double - * height. Defaults to 1. - * - * If {@link uniformScale} is enabled, {@link scaleVariationX} also affects - * the height, and this field is ignored. - */ - scaleVariationY?: number - /** - * If enabled, the particle width-related properties and fields will control - * both the width and height of the particles, and the height counterparts - * will be ignored. Defaults to false. - * - * See also: - * - {@link width} - * - {@link height} - * - {@link scaleVariationX} - * - {@link scaleVariationY} - */ - uniformScale?: boolean - /** - * To split the texture into multiple animation frames, this value must be - * set to the number of columns in the texture. It should equal - * `textureWidth / frameWidth`. Defaults to 1. - * - * See also: - * - {@link BillboardExParams.totalFrames totalFrames} - */ - columns?: number - /** - * To split the texture into multiple animation frames, this value must be - * set to the total number of frames in the texture. Defaults to 1. - * - * See also: - * - {@link BillboardExParams.columns columns} - */ - totalFrames?: number - /** - * If enabled, the texture animation will use linear interpolation to mix - * frames when the frame index is not a whole number. For example, if the - * frame index is 0.5, enabling this will cause the average of the first two - * frames to be shown instead of just the first frame. - * - * If disabled, the frame index will just be truncated to get a whole number. - * - * Defaults to true. - * - * See also: - * - {@link BillboardExParams.frameIndex frameIndex} - */ - interpolateFrames?: boolean - /** - * Controls the color of the additional bloom effect. The colors of the - * particles will be multiplied with this color to get the final color - * of the bloom effect. Defaults to [1, 1, 1]. - * - * Note: - * - This has no effect if the "Effects Quality" setting is set to "Low". - * - This does not affect the natural bloom effect from high color values. - * - * See also: - * - {@link bloomStrength} - */ - bloomColor?: Vector3 - /** - * Controls the strength of the additional bloom effect. Defaults to 0. - * - * Note: - * - This has no effect if the "Effects Quality" setting is set to "Low". - * - This does not affect the natural bloom effect from high color values. - * - * See also: - * - {@link bloomColor} - */ - bloomStrength?: number - /** - * Minimum view distance. If the particle is closer than this distance from - * the camera, it will be hidden. Can be set to -1 to disable the limit. - * Defaults to -1. - * - * See also: - * - {@link maxDistance} - */ - minDistance?: number - /** - * Maximum view distance. If the particle is farther away than this distance - * from the camera, it will be hidden. Can be set to -1 to disable the limit. - * Defaults to -1. - * - * See also: - * - {@link minDistance} - */ - maxDistance?: number - /** - * Negative values will make the particle draw in front of objects closer to - * the camera, while positive values will make it draw behind objects farther - * away from the camera. Defaults to 0. - * - * {@link ActionType.BillboardEx BillboardEx} has a - * {@link BillboardExParams.depthOffset property} that works the - * same way, but reversed. Since that property was discovered before this - * field, this field was given the "negative" name. - */ - negativeDepthOffset?: number - /** - * Controls how dark shaded parts of the particle are. Defaults to 0. - */ - shadowDarkness?: number - /** - * Specular texture ID. Defaults to 0. - * - * See also: - * - {@link lighting} - * - {@link glossiness} - * - {@link specularity} - */ - specular?: number - /** - * Controls how sharp the specular highlights are. Defaults to 0.25. - * - * See also: - * - {@link lighting} - * - {@link specular} - * - {@link specularity} - */ - glossiness?: number - /** - * Controls how the particles are lit. See {@link LightingMode} for more - * information. Defaults to {@link LightingMode.Unlit}. - */ - lighting?: LightingMode - /** - * Controls how bright the specular highlights are. Defaults to 0.5. - * - * See also: - * - {@link lighting} - * - {@link specular} - * - {@link glossiness} - */ - specularity?: number - unkScalarProp1_23?: ScalarPropertyArg - unkScalarProp1_24?: ScalarPropertyArg - unkScalarProp2_2?: ScalarPropertyArg - unkVec4Prop2_3?: Vector4PropertyArg - unkVec4Prop2_4?: Vector4PropertyArg - unkVec4Prop2_5?: Vector4PropertyArg - unkScalarProp2_6?: ScalarPropertyArg -} -/** - * Particle with a texture that may be animated. This is the most common - * particle type and it has a lot of useful fields and properties. - */ -class BillboardEx extends CommonFields2Action { - - constructor({ - texture = 1, - blendMode = BlendMode.Normal, - offsetX = 0, - offsetY = 0, - offsetZ = 0, - width = 1, - height = 1, - color1 = [1, 1, 1, 1], - color2 = [1, 1, 1, 1], - color3 = [1, 1, 1, 1], - alphaThreshold = 0, - rotationX = 0, - rotationY = 0, - rotationZ = 0, - rotationSpeedX = 0, - rotationSpeedY = 0, - rotationSpeedZ = 0, - rotationSpeedMultiplierX = 1, - rotationSpeedMultiplierY = 1, - rotationSpeedMultiplierZ = 1, - depthOffset = 0, - frameIndex = 0, - frameIndexOffset = 0, - rgbMultiplier = 1, - alphaMultiplier = 1, - orientation = OrientationMode.CameraPlane, - normalMap = 0, - scaleVariationX = 1, - scaleVariationY = 1, - uniformScale = false, - columns = 1, - totalFrames = 1, - interpolateFrames = true, - bloomColor = [1, 1, 1], - bloomStrength = 0, - minDistance = -1, - maxDistance = -1, - negativeDepthOffset = 0, - shadowDarkness = 0, - specular = 0, - glossiness = 0.25, - lighting = LightingMode.Unlit, - specularity = 0.5, - unkScalarProp1_23 = 0, - unkScalarProp1_24 = 0, - unkScalarProp2_2 = 0, - unkVec4Prop2_3 = [1, 1, 1, 1], - unkVec4Prop2_4 = [1, 1, 1, 1], - unkVec4Prop2_5 = [1, 1, 1, 1], - unkScalarProp2_6 = 0, - }: BillboardExParams = {}) { - super(ActionType.BillboardEx, [ - /* 0 */ new IntField(orientation), - /* 1 */ new IntField(normalMap), - /* 2 */ new FloatField(scaleVariationX), - /* 3 */ new FloatField(scaleVariationY), - /* 4 */ new BoolField(uniformScale), - /* 5 */ new IntField(0), - /* 6 */ new IntField(columns), - /* 7 */ new IntField(totalFrames), - /* 8 */ new BoolField(interpolateFrames), - /* 9 */ new IntField(0), - /* 10 */ new IntField(0), - /* 11 */ new FloatField(-1), - /* 12 */ new IntField(1), - /* 13 */ new IntField(0), - /* 14 */ new IntField(0), - /* 15 */ new IntField(1), - /* 16 */ new IntField(1), - /* 17 */ new IntField(0), - ], [ - /* 0 */ new IntField(0), - /* 1 */ new IntField(0), - /* 2 */ new IntField(8), - /* 3 */ new IntField(0), - /* 4 */ new IntField(1), - /* 5 */ new FloatField(bloomColor[0]), - /* 6 */ new FloatField(bloomColor[1]), - /* 7 */ new FloatField(bloomColor[2]), - /* 8 */ new FloatField(bloomStrength), - /* 9 */ new IntField(0), - /* 10 */ new IntField(0), - /* 11 */ new IntField(0), - /* 12 */ new IntField(0), - /* 13 */ new IntField(0), - /* 14 */ new FloatField(-1), - /* 15 */ new FloatField(-1), - /* 16 */ new FloatField(-1), - /* 17 */ new FloatField(-1), - /* 18 */ new FloatField(minDistance), - /* 19 */ new FloatField(maxDistance), - /* 20 */ new IntField(0), - /* 21 */ new IntField(0), - /* 22 */ new IntField(0), - /* 23 */ new IntField(0), - /* 24 */ new IntField(0), - /* 25 */ new FloatField(1), - /* 26 */ new FloatField(negativeDepthOffset), - /* 27 */ new IntField(1), - /* 28 */ new IntField(0), - /* 29 */ new FloatField(5), - /* 30 */ new FloatField(shadowDarkness), - /* 31 */ new IntField(0), - /* 32 */ new IntField(1), - /* 33 */ new IntField(specular), - /* 34 */ new FloatField(glossiness), - /* 35 */ new IntField(lighting), - /* 36 */ new IntField(-2), - /* 37 */ new IntField(0), - /* 38 */ new FloatField(specularity), - /* 39 */ new IntField(1), - /* 40 */ new IntField(0), - /* 41 */ new IntField(0), - /* 42 */ new IntField(0), - /* 43 */ new IntField(0), - /* 44 */ new IntField(0), - ], [ - /* 0 */ scalarFromArg(texture), - /* 1 */ scalarFromArg(blendMode), - /* 2 */ scalarFromArg(offsetX), - /* 3 */ scalarFromArg(offsetY), - /* 4 */ scalarFromArg(offsetZ), - /* 5 */ scalarFromArg(width), - /* 6 */ scalarFromArg(height), - /* 7 */ vectorFromArg(color1), - /* 8 */ vectorFromArg(color2), - /* 9 */ vectorFromArg(color3), - /* 10 */ scalarFromArg(alphaThreshold), - /* 11 */ scalarFromArg(rotationX), - /* 12 */ scalarFromArg(rotationY), - /* 13 */ scalarFromArg(rotationZ), - /* 14 */ scalarFromArg(rotationSpeedX), - /* 15 */ scalarFromArg(rotationSpeedMultiplierX), - /* 16 */ scalarFromArg(rotationSpeedY), - /* 17 */ scalarFromArg(rotationSpeedMultiplierY), - /* 18 */ scalarFromArg(rotationSpeedZ), - /* 19 */ scalarFromArg(rotationSpeedMultiplierZ), - /* 20 */ scalarFromArg(depthOffset), - /* 21 */ scalarFromArg(frameIndex), - /* 22 */ scalarFromArg(frameIndexOffset), - /* 23 */ scalarFromArg(unkScalarProp1_23), - /* 24 */ scalarFromArg(unkScalarProp1_24), - ], [ - /* 0 */ scalarFromArg(rgbMultiplier), - /* 1 */ scalarFromArg(alphaMultiplier), - /* 2 */ scalarFromArg(unkScalarProp2_2), - /* 3 */ vectorFromArg(unkVec4Prop2_3), - /* 4 */ vectorFromArg(unkVec4Prop2_4), - /* 5 */ vectorFromArg(unkVec4Prop2_5), - /* 6 */ scalarFromArg(unkScalarProp2_6), - ]) - } - - /** - * Texture ID. - * - * **Argument**: {@link PropertyArgument.Constant Constant 0} - */ - get texture() { return this.properties1[0] } - set texture(value) { setPropertyInList(this.properties1, 0, value) } - - /** - * Blend mode. See {@link BlendMode} for more information. - * - * **Argument**: {@link PropertyArgument.Constant Constant 0} - */ - get blendMode() { return this.properties1[1].valueAt(0) as BlendMode } - set blendMode(value: BlendMode | ScalarProperty) { setPropertyInList(this.properties1, 1, value) } - /** - * Blend mode. See {@link BlendMode} for more information. - * - * **Argument**: {@link PropertyArgument.Constant Constant 0} - */ - get blendModeProperty() { return this.properties1[1] } - - /** - * X position offset. - * - * **Argument**: {@link PropertyArgument.ParticleAge Particle age} - */ - get offsetX() { return this.properties1[2] } - set offsetX(value) { setPropertyInList(this.properties1, 2, value) } - - /** - * Y position offset. - * - * **Argument**: {@link PropertyArgument.ParticleAge Particle age} - */ - get offsetY() { return this.properties1[3] } - set offsetY(value) { setPropertyInList(this.properties1, 3, value) } - - /** - * Z position offset. - * - * **Argument**: {@link PropertyArgument.ParticleAge Particle age} - */ - get offsetZ() { return this.properties1[4] } - set offsetZ(value) { setPropertyInList(this.properties1, 4, value) } - - /** - * The width of the particle. - * - * If {@link uniformScale} is enabled, this also controls the height. - * - * **Argument**: {@link PropertyArgument.ParticleAge Particle age} - */ - get width() { return this.properties1[5] } - set width(value) { setPropertyInList(this.properties1, 5, value) } - - /** - * The height of the particle. - * - * If {@link uniformScale} is enabled, {@link width} also controls the - * height, and this property is ignored. - * - * **Argument**: {@link PropertyArgument.ParticleAge Particle age} - */ - get height() { return this.properties1[6] } - set height(value) { setPropertyInList(this.properties1, 6, value) } - - /** - * Color multiplier. - * - * **Argument**: {@link PropertyArgument.ParticleAge Particle age} - */ - get color1() { return this.properties1[7] } - set color1(value) { setPropertyInList(this.properties1, 7, value) } - - /** - * Color multiplier. - * - * **Argument**: {@link PropertyArgument.EmissionTime Emission time} - */ - get color2() { return this.properties1[8] } - set color2(value) { setPropertyInList(this.properties1, 8, value) } - - /** - * Color multiplier. - * - * **Argument**: {@link PropertyArgument.EffectAge Effect age}. - */ - get color3() { return this.properties1[9] } - set color3(value) { setPropertyInList(this.properties1, 9, value) } - - /** - * Parts of the particle with less opacity than this threshold will be - * invisible. The range is 0-255. - * - * **Argument**: {@link PropertyArgument.EffectAge Effect age} - */ - get alphaThreshold() { return this.properties1[10] } - set alphaThreshold(value) { setPropertyInList(this.properties1, 10, value) } - - /** - * Rotation around the X-axis in degrees. - * - * **Argument**: {@link PropertyArgument.Constant Constant 0} - * - * See also: - * - {@link rotationSpeedX} - * - {@link rotationSpeedMultiplierX} - */ - get rotationX() { return this.properties1[11] } - set rotationX(value) { setPropertyInList(this.properties1, 11, value) } - - /** - * Rotation around the Y-axis in degrees. - * - * **Argument**: {@link PropertyArgument.Constant Constant 0} - * - * See also: - * - {@link rotationSpeedY} - * - {@link rotationSpeedMultiplierY} - */ - get rotationY() { return this.properties1[12] } - set rotationY(value) { setPropertyInList(this.properties1, 12, value) } - - /** - * Rotation around the Z-axis in degrees. - * - * **Argument**: {@link PropertyArgument.Constant Constant 0} - * - * See also: - * - {@link rotationSpeedZ} - * - {@link rotationSpeedMultiplierZ} - */ - get rotationZ() { return this.properties1[13] } - set rotationZ(value) { setPropertyInList(this.properties1, 13, value) } - - /** - * Rotation speed around the X-axis in degrees per second. - * - * **Argument**: {@link PropertyArgument.ParticleAge Particle age} - * - * See also: - * - {@link rotationX} - * - {@link rotationSpeedMultiplierX} - */ - get rotationSpeedX() { return this.properties1[14] } - set rotationSpeedX(value) { setPropertyInList(this.properties1, 14, value) } - - /** - * Rotation speed around the Y-axis in degrees per second. - * - * **Argument**: {@link PropertyArgument.ParticleAge Particle age} - * - * See also: - * - {@link rotationY} - * - {@link rotationSpeedMultiplierY} - */ - get rotationSpeedY() { return this.properties1[16] } - set rotationSpeedY(value) { setPropertyInList(this.properties1, 16, value) } - - /** - * Rotation speed around the Z-axis in degrees per second. - * - * **Argument**: {@link PropertyArgument.ParticleAge Particle age} - * - * See also: - * - {@link rotationZ} - * - {@link rotationSpeedMultiplierZ} - */ - get rotationSpeedZ() { return this.properties1[18] } - set rotationSpeedZ(value) { setPropertyInList(this.properties1, 18, value) } - - /** - * Multiplier for the rotation speed around the X-axis. - * - * **Argument**: {@link PropertyArgument.ParticleAge Particle age} - * - * See also: - * - {@link rotationX} - * - {@link rotationSpeedX} - */ - get rotationSpeedMultiplierX() { return this.properties1[15] } - set rotationSpeedMultiplierX(value) { setPropertyInList(this.properties1, 15, value) } - - /** - * Multiplier for the rotation speed around the Y-axis. - * - * **Argument**: {@link PropertyArgument.ParticleAge Particle age} - * - * See also: - * - {@link rotationY} - * - {@link rotationSpeedY} - */ - get rotationSpeedMultiplierY() { return this.properties1[17] } - set rotationSpeedMultiplierY(value) { setPropertyInList(this.properties1, 17, value) } - - /** - * Multiplier for the rotation speed around the Z-axis. - * - * **Argument**: {@link PropertyArgument.ParticleAge Particle age} - * - * See also: - * - {@link rotationZ} - * - {@link rotationSpeedZ} - */ - get rotationSpeedMultiplierZ() { return this.properties1[19] } - set rotationSpeedMultiplierZ(value) { setPropertyInList(this.properties1, 19, value) } - - /** - * Positive values will make the particle draw in front of objects closer to - * the camera, while negative values will make it draw behind objects farther - * away from the camera. - * - * **Argument**: {@link PropertyArgument.ParticleAge Particle age} - * - * See also: - * - {@link negativeDepthOffset} - */ - get depthOffset() { return this.properties1[20] } - set depthOffset(value) { setPropertyInList(this.properties1, 20, value) } - - /** - * The index of the frame to show from the texture atlas. Can be animated - * using a {@link PropertyFunction.Linear linear property} or similar. - * - * Seemingly identical to {@link frameIndexOffset}? The sum of these two - * properties is the actual frame index that gets used. - * - * **Argument**: {@link PropertyArgument.ParticleAge Particle age} - */ - get frameIndex() { return this.properties1[21] } - set frameIndex(value) { setPropertyInList(this.properties1, 21, value) } - - /** - * Seemingly identical to {@link frameIndex}? The sum of these two properties - * is the actual frame index that gets used. - * - * **Argument**: {@link PropertyArgument.ParticleAge Particle age} - */ - get frameIndexOffset() { return this.properties1[22] } - set frameIndexOffset(value) { setPropertyInList(this.properties1, 22, value) } - - /** - * Scalar multiplier for the color that does not affect the alpha. - * Effectively a brightness multiplier. - * - * **Argument**: {@link PropertyArgument.EffectAge Effect age} - */ - get rgbMultiplier() { return this.properties2[0] } - set rgbMultiplier(value) { setPropertyInList(this.properties2, 0, value) } - - /** - * Alpha multiplier. - * - * **Argument**: {@link PropertyArgument.EffectAge Effect age} - */ - get alphaMultiplier() { return this.properties2[1] } - set alphaMultiplier(value) { setPropertyInList(this.properties2, 1, value) } - - /** - * Controls the orientation mode for the particles. See - * {@link OrientationMode} for more information. - */ - get orientation() { return this.fields1[0].value as OrientationMode } - set orientation(value) { this.fields1[0].value = value } - - /** - * Normal map ID. - */ - get normalMap() { return this.fields1[1].value as number } - set normalMap(value) { this.fields1[1].value = value } - - /** - * Each particle will pick a random number between this value and 1, and the - * width of the particle will be multiplied by this number. For example, - * setting this to 0.5 will make the particles randomly thinner, down to half - * width. Setting it to 2 will make them randomly wider, up to double width. - * - * If {@link uniformScale} is enabled, this also affects the height. - * - * See also: - * - {@link scaleVariationY} - */ - get scaleVariationX() { return this.fields1[2].value as number } - set scaleVariationX(value) { this.fields1[2].value = value } - - /** - * Each particle will pick a random number between this value and 1, and the - * height of the particle will be multiplied by this number. For example, - * setting this to 0.5 will make the particles randomly shorter, down to half - * height. Setting it to 2 will make them randomly taller, up to double - * height. - * - * If {@link uniformScale} is enabled, {@link scaleVariationX} also affects - * the height, and this field is ignored. - */ - get scaleVariationY() { return this.fields1[3].value as number } - set scaleVariationY(value) { this.fields1[3].value = value } - - /** - * If enabled, the particle width-related properties and fields will control - * both the width and height of the particles, and the height counterparts - * will be ignored. - * - * See also: - * - {@link width} - * - {@link height} - * - {@link scaleVariationX} - * - {@link scaleVariationY} - */ - get uniformScale() { return this.fields1[4].value as boolean } - set uniformScale(value) { this.fields1[4].value = value } - - /** - * To split the texture into multiple animation frames, this value must be - * set to the number of columns in the texture. It should equal - * `textureWidth / frameWidth`. - * - * See also: - * - {@link BillboardExParams.totalFrames totalFrames} - */ - get columns() { return this.fields1[6].value as number } - set columns(value) { this.fields1[6].value = value } - - /** - * To split the texture into multiple animation frames, this value must be - * set to the total number of frames in the texture. - * - * See also: - * - {@link BillboardExParams.columns columns} - */ - get totalFrames() { return this.fields1[7].value as number } - set totalFrames(value) { this.fields1[7].value = value } - - /** - * If enabled, the texture animation will use linear interpolation to mix - * frames when the frame index is not a whole number. For example, if the - * frame index is 0.5, enabling this will cause the average of the first two - * frames to be shown instead of just the first frame. - * - * If disabled, the frame index will just be truncated to get a whole number. - * - * See also: - * - {@link BillboardExParams.frameIndex frameIndex} - */ - get interpolateFrames() { return this.fields1[8].value as number } - set interpolateFrames(value) { this.fields1[8].value = value } - - /** - * Negative values will make the particle draw in front of objects closer to - * the camera, while positive values will make it draw behind objects farther - * away from the camera. - * - * {@link ActionType.BillboardEx BillboardEx} has a - * {@link BillboardExParams.depthOffset property} that works the - * same way, but reversed. Since that property was discovered before this - * field, this field was given the "negative" name. - */ - get negativeDepthOffset() { return this.fields2[26].value as number } - set negativeDepthOffset(value) { this.fields2[26].value = value } - - /** - * Controls how dark shaded parts of the particle are. - */ - get shadowDarkness() { return this.fields2[30].value as number } - set shadowDarkness(value) { this.fields2[30].value = value } - - /** - * Specular texture ID. Defaults to 0. - * - * See also: - * - {@link lighting} - * - {@link glossiness} - * - {@link specularity} - */ - get specular() { return this.fields2[33].value as number } - set specular(value) { this.fields2[33].value = value } - - /** - * Controls how sharp the specular highlights are. - * - * See also: - * - {@link lighting} - * - {@link specular} - * - {@link specularity} - */ - get glossiness() { return this.fields2[34].value as number } - set glossiness(value) { this.fields2[34].value = value } - - /** - * Controls how the particles are lit. See {@link LightingMode} for more - * information. - */ - get lighting() { return this.fields2[35].value as LightingMode } - set lighting(value) { this.fields2[35].value = value } - - /** - * Controls how bright the specular highlights are. - * - * See also: - * - {@link lighting} - * - {@link specular} - * - {@link glossiness} - */ - get specularity() { return this.fields2[38].value as number } - set specularity(value) { this.fields2[38].value = value } - -} - -export interface MultiTextureBillboardExParams { - /** - * Blend mode. Defaults to {@link BlendMode.Normal}. - * - * **Argument**: {@link PropertyArgument.Constant Constant 0} - */ - blendMode?: BlendMode | ScalarProperty - /** - * X position offset. Defaults to 0. - * - * **Argument**: {@link PropertyArgument.ParticleAge Particle age} - */ - offsetX?: ScalarPropertyArg - /** - * Y position offset. Defaults to 0. - * - * **Argument**: {@link PropertyArgument.ParticleAge Particle age} - */ - offsetY?: ScalarPropertyArg - /** - * Z position offset. Defaults to 0. - * - * **Argument**: {@link PropertyArgument.ParticleAge Particle age} - */ - offsetZ?: ScalarPropertyArg - /** - * The width of the particle. - * - * If {@link uniformScale} is enabled, this also controls the height. - * - * Defaults to 1. - * - * **Argument**: {@link PropertyArgument.ParticleAge Particle age} - */ - width?: ScalarPropertyArg - /** - * The height of the particle. - * - * If {@link uniformScale} is enabled, {@link width} also controls the - * height, and this property is ignored. - * - * Defaults to 1. - * - * **Argument**: {@link PropertyArgument.ParticleAge Particle age} - */ - height?: ScalarPropertyArg - /** - * Rotation around the X-axis in degrees. Defaults to 0. - * - * **Argument**: {@link PropertyArgument.Constant Constant 0} - * - * See also: - * - {@link rotationSpeedX} - * - {@link rotationSpeedMultiplierX} - */ - rotationX?: ScalarPropertyArg - /** - * Rotation around the Y-axis in degrees. Defaults to 0. - * - * **Argument**: {@link PropertyArgument.Constant Constant 0} - * - * See also: - * - {@link rotationSpeedY} - * - {@link rotationSpeedMultiplierY} - */ - rotationY?: ScalarPropertyArg - /** - * Rotation around the Z-axis in degrees. Defaults to 0. - * - * **Argument**: {@link PropertyArgument.Constant Constant 0} - * - * See also: - * - {@link rotationSpeedZ} - * - {@link rotationSpeedMultiplierZ} - */ - rotationZ?: ScalarPropertyArg - /** - * Rotation speed around the X-axis in degrees per second. Defaults to 0. - * - * **Argument**: {@link PropertyArgument.ParticleAge Particle age} - * - * See also: - * - {@link rotationX} - * - {@link rotationSpeedMultiplierX} - */ - rotationSpeedX?: ScalarPropertyArg - /** - * Rotation speed around the Y-axis in degrees per second. Defaults to 0. - * - * **Argument**: {@link PropertyArgument.ParticleAge Particle age} - * - * See also: - * - {@link rotationY} - * - {@link rotationSpeedMultiplierY} - */ - rotationSpeedY?: ScalarPropertyArg - /** - * Rotation speed around the Z-axis in degrees per second. Defaults to 0. - * - * **Argument**: {@link PropertyArgument.ParticleAge Particle age} - * - * See also: - * - {@link rotationZ} - * - {@link rotationSpeedMultiplierZ} - */ - rotationSpeedZ?: ScalarPropertyArg - /** - * Multiplier for the rotation speed around the X-axis. Defaults to 1. - * - * **Argument**: {@link PropertyArgument.ParticleAge Particle age} - * - * See also: - * - {@link rotationX} - * - {@link rotationSpeedX} - */ - rotationSpeedMultiplierX?: ScalarPropertyArg - /** - * Multiplier for the rotation speed around the Y-axis. Defaults to 1. - * - * **Argument**: {@link PropertyArgument.ParticleAge Particle age} - * - * See also: - * - {@link rotationY} - * - {@link rotationSpeedY} - */ - rotationSpeedMultiplierY?: ScalarPropertyArg - /** - * Multiplier for the rotation speed around the Z-axis. Defaults to 1. - * - * **Argument**: {@link PropertyArgument.ParticleAge Particle age} - * - * See also: - * - {@link rotationZ} - * - {@link rotationSpeedZ} - */ - rotationSpeedMultiplierZ?: ScalarPropertyArg - /** - * Color multiplier for the particle. Seemingly identical to {@link color3}? - * Defaults to [1, 1, 1, 1]. - * - * **Argument**: {@link PropertyArgument.ParticleAge Particle age} - */ - color1?: Vector4PropertyArg - /** - * Color multiplier. Defaults to [1, 1, 1, 1]. - * - * **Argument**: {@link PropertyArgument.EmissionTime Emission time} - */ - color2?: Vector4PropertyArg - /** - * Color multiplier for the particle. Seemingly identical to {@link color1}? - * Defaults to [1, 1, 1, 1]. - * - * **Argument**: {@link PropertyArgument.ParticleAge Particle age}. - */ - color3?: Vector4PropertyArg - /** - * Color multiplier for both of the texture layers. Defaults to [1, 1, 1, 1]. - * - * **Argument**: {@link PropertyArgument.ParticleAge Particle age} - */ - layersColor?: Vector4PropertyArg - /** - * Color multiplier for Layer 1. Defaults to [1, 1, 1, 1]. - * - * **Argument**: {@link PropertyArgument.ParticleAge Particle age} - */ - layer1Color?: Vector4PropertyArg - /** - * Color multiplier for Layer 2. Defaults to [1, 1, 1, 1]. - * - * **Argument**: {@link PropertyArgument.ParticleAge Particle age} - */ - layer2Color?: Vector4PropertyArg - /** - * Parts of the particle with less opacity than this threshold will be - * invisible. The range is 0-255. Defaults to 0. - * - * **Argument**: {@link PropertyArgument.EffectAge Effect age} - */ - alphaThreshold?: ScalarPropertyArg - /** - * The index of the frame to show from the texture atlas. Can be animated - * using a {@link PropertyFunction.Linear linear property} or similar. - * Defaults to 0. - * - * Seemingly identical to {@link frameIndexOffset}? The sum of these two - * properties is the actual frame index that gets used. - * - * **Argument**: {@link PropertyArgument.ParticleAge Particle age} - * - * See also: - * - {@link interpolateFrames} - */ - frameIndex?: ScalarPropertyArg - /** - * Seemingly identical to {@link frameIndex}? The sum of these two properties - * is the actual frame index that gets used. Defaults to 0. - * - * **Argument**: {@link PropertyArgument.ParticleAge Particle age} - * - * See also: - * - {@link interpolateFrames} - */ - frameIndexOffset?: ScalarPropertyArg - /** - * Horiztonal scroll speed for Layer 1. Defaults to 0. - * - * **Argument**: {@link PropertyArgument.ParticleAge Particle age} - */ - layer1SpeedU?: ScalarPropertyArg - /** - * Vertical scroll speed for Layer 1. Defaults to 0. - * - * **Argument**: {@link PropertyArgument.ParticleAge Particle age} - */ - layer1SpeedV?: ScalarPropertyArg - /** - * Horizontal offset for the UV coordinates of Layer 1. Defaults to 0. - * - * **Argument**: {@link PropertyArgument.Constant Constant 0} - */ - layer1OffsetU?: ScalarPropertyArg - /** - * Vertical offset for the UV coordinates of Layer 1. Defaults to 0. - * - * **Argument**: {@link PropertyArgument.Constant Constant 0} - */ - layer1OffsetV?: ScalarPropertyArg - /** - * Horizontal scale for the UV coordinates of Layer 1. Defaults to 1. - * - * **Argument**: {@link PropertyArgument.ParticleAge Particle age} - */ - layer1ScaleU?: ScalarPropertyArg - /** - * Vertical scale for the UV coordinates of Layer 1. Defaults to 1. - * - * **Argument**: {@link PropertyArgument.ParticleAge Particle age} - */ - layer1ScaleV?: ScalarPropertyArg - /** - * Horiztonal scroll speed for Layer 2. Defaults to 0. - * - * **Argument**: {@link PropertyArgument.ParticleAge Particle age} - */ - layer2SpeedU?: ScalarPropertyArg - /** - * Vertical scroll speed for Layer 2. Defaults to 0. - * - * **Argument**: {@link PropertyArgument.ParticleAge Particle age} - */ - layer2SpeedV?: ScalarPropertyArg - /** - * Horizontal offset for the UV coordinates of Layer 2. Defaults to 0. - * - * **Argument**: {@link PropertyArgument.Constant Constant 0} - */ - layer2OffsetU?: ScalarPropertyArg - /** - * Vertical offset for the UV coordinates of Layer 2. Defaults to 0. - * - * **Argument**: {@link PropertyArgument.Constant Constant 0} - */ - layer2OffsetV?: ScalarPropertyArg - /** - * Horizontal scale for the UV coordinates of Layer 2. Defaults to 1. - * - * **Argument**: {@link PropertyArgument.ParticleAge Particle age} - */ - layer2ScaleU?: ScalarPropertyArg - /** - * Vertical scale for the UV coordinates of Layer 2. Defaults to 1. - * - * **Argument**: {@link PropertyArgument.ParticleAge Particle age} - */ - layer2ScaleV?: ScalarPropertyArg - /** - * Scalar multiplier for the color that does not affect the alpha. - * Effectively a brightness multiplier. Defaults to 1. - * - * **Argument**: {@link PropertyArgument.EffectAge Effect age} - */ - rgbMultiplier?: ScalarPropertyArg - /** - * Alpha multiplier. Defaults to 1. - * - * **Argument**: {@link PropertyArgument.EffectAge Effect age} - */ - alphaMultiplier?: ScalarPropertyArg - /** - * Controls the orientation mode for the particles. See - * {@link OrientationMode} for more information. Defaults to - * {@link OrientationMode.CameraPlane}. - */ - orientation?: OrientationMode - /** - * Mask texture ID. Defaults to 1. - */ - mask?: number - /** - * Layer 1 texture ID. Defaults to 1. - */ - layer1?: number - /** - * Layer 2 texture ID. Defaults to 1. - */ - layer2?: number - /** - * If enabled, the particle width-related properties and fields will control - * both the width and height of the particles, and the height counterparts - * will be ignored. Defaults to false. - * - * See also: - * - {@link width} - * - {@link height} - */ - uniformScale?: boolean - /** - * To split the texture into multiple animation frames, this value must be - * set to the number of columns in the texture. It should equal - * `textureWidth / frameWidth`. Defaults to 1. - * - * See also: - * - {@link totalFrames} - */ - columns?: number - /** - * To split the texture into multiple animation frames, this value must be - * set to the total number of frames in the texture. Defaults to 1. - * - * See also: - * - {@link columns} - */ - totalFrames?: number - /** - * If enabled, the texture animation will use linear interpolation to mix - * frames when the frame index is not a whole number. For example, if the - * frame index is 0.5, enabling this will cause the average of the first two - * frames to be shown instead of just the first frame. - * - * If disabled, the frame index will just be truncated to get a whole number. - * - * Defaults to true. - * - * See also: - * - {@link frameIndex} - * - {@link frameIndexOffset} - */ - interpolateFrames?: boolean - /** - * Controls how the particles should intersect with objects they touch. If - * disabled, the particles will simply be cut off where they intersect - * objects. If enabled, they will instead display in front of the object if - * they are close enough, and will fade out with distance from the object's - * surface that is blocking the view of the particle. Defaults to true. - */ - depthBlend?: boolean - /** - * Controls the shape of the particles. If disabled, the particles will be - * rectangular. If enabled, they will be octagonal. Defaults to false. - */ - octagonal?: boolean - /** - * Controls the color of the additional bloom effect. The colors of the - * particles will be multiplied with this color to get the final color - * of the bloom effect. Defaults to [1, 1, 1]. - * - * Note: - * - This has no effect if the "Effects Quality" setting is set to "Low". - * - This does not affect the natural bloom effect from high color values. - * - * See also: - * - {@link bloomStrength} - */ - bloomColor?: Vector3 - /** - * Controls the strength of the additional bloom effect. Defaults to 0. - * - * Note: - * - This has no effect if the "Effects Quality" setting is set to "Low". - * - This does not affect the natural bloom effect from high color values. - * - * See also: - * - {@link bloomColor} - */ - bloomStrength?: number - /** - * Minimum view distance. If the particle is closer than this distance from - * the camera, it will be hidden. Can be set to -1 to disable the limit. - * Defaults to -1. - * - * See also: - * - {@link maxDistance} - */ - minDistance?: number - /** - * Maximum view distance. If the particle is farther away than this distance - * from the camera, it will be hidden. Can be set to -1 to disable the limit. - * Defaults to -1. - * - * See also: - * - {@link minDistance} - */ - maxDistance?: number - /** - * Negative values will make the particle draw in front of objects closer to - * the camera, while positive values will make it draw behind objects farther - * away from the camera. Defaults to 0. - * - * {@link ActionType.BillboardEx BillboardEx} has a - * {@link BillboardExParams.depthOffset property} that works the - * same way, but reversed. Since that property was discovered before this - * field, this field was given the "negative" name. - */ - negativeDepthOffset?: number - /** - * Controls how dark shaded parts of the particle are. Defaults to 0. - */ - shadowDarkness?: number - /** - * Specular texture ID. Defaults to 0. - * - * See also: - * - {@link lighting} - * - {@link glossiness} - * - {@link specularity} - */ - specular?: number - /** - * Controls how sharp the specular highlights are. Defaults to 0.25. - * - * See also: - * - {@link lighting} - * - {@link specular} - * - {@link specularity} - */ - glossiness?: number - /** - * Controls how the particles are lit. See {@link LightingMode} for more - * information. Defaults to {@link LightingMode.Unlit}. - */ - lighting?: LightingMode - /** - * Controls how bright the specular highlights are. Defaults to 0.5. - * - * See also: - * - {@link lighting} - * - {@link specular} - * - {@link glossiness} - */ - specularity?: number -} -/** - * Particle with multiple texture that can scroll. - */ -class MultiTextureBillboardEx extends CommonFields2Action { - - constructor({ - orientation = OrientationMode.CameraPlane, - mask = 1, - layer1 = 1, - layer2 = 1, - uniformScale = false, - columns = 1, - totalFrames = 1, - interpolateFrames = true, - depthBlend = true, - octagonal = false, - bloomColor = [1, 1, 1], - bloomStrength = 0, - minDistance = -1, - maxDistance = -1, - negativeDepthOffset = 0, - shadowDarkness = 0, - specular = 0, - glossiness = 0.25, - lighting = LightingMode.Unlit, - specularity = 0.5, - blendMode = BlendMode.Normal, - offsetX = 0, - offsetY = 0, - offsetZ = 0, - width = 1, - height = 1, - rotationX = 0, - rotationY = 0, - rotationZ = 0, - rotationSpeedX = 0, - rotationSpeedY = 0, - rotationSpeedZ = 0, - rotationSpeedMultiplierX = 1, - rotationSpeedMultiplierY = 1, - rotationSpeedMultiplierZ = 1, - color1 = [1, 1, 1, 1], - color2 = [1, 1, 1, 1], - color3 = [1, 1, 1, 1], - layersColor = [1, 1, 1, 1], - layer1Color = [1, 1, 1, 1], - layer2Color = [1, 1, 1, 1], - alphaThreshold = 0, - frameIndex = 0, - frameIndexOffset = 0, - layer1SpeedU = 0, - layer1SpeedV = 0, - layer1OffsetU = 0, - layer1OffsetV = 0, - layer1ScaleU = 1, - layer1ScaleV = 1, - layer2SpeedU = 0, - layer2SpeedV = 0, - layer2OffsetU = 0, - layer2OffsetV = 0, - layer2ScaleU = 1, - layer2ScaleV = 1, - rgbMultiplier = 1, - alphaMultiplier = 1, - }: MultiTextureBillboardExParams = {}) { - super(ActionType.MultiTextureBillboardEx, [ - /* 0 */ new IntField(orientation), - /* 1 */ new IntField(mask), - /* 2 */ new IntField(layer1), - /* 3 */ new IntField(layer2), - /* 4 */ new BoolField(uniformScale), - /* 5 */ new IntField(0), - /* 6 */ new IntField(columns), - /* 7 */ new IntField(totalFrames), - /* 8 */ new BoolField(interpolateFrames), - /* 9 */ new IntField(-2), - /* 10 */ new IntField(-2), - /* 11 */ new BoolField(depthBlend), - /* 12 */ new BoolField(octagonal), - /* 13 */ new IntField(0), - /* 14 */ new IntField(1), - /* 15 */ new IntField(1), - /* 16 */ new IntField(0), - ], [ - /* 0 */ new IntField(0), - /* 1 */ new IntField(0), - /* 2 */ new IntField(8), - /* 3 */ new IntField(0), - /* 4 */ new IntField(1), - /* 5 */ new FloatField(bloomColor[0]), - /* 6 */ new FloatField(bloomColor[1]), - /* 7 */ new FloatField(bloomColor[2]), - /* 8 */ new FloatField(bloomStrength), - /* 9 */ new IntField(0), - /* 10 */ new IntField(0), - /* 11 */ new IntField(0), - /* 12 */ new IntField(0), - /* 13 */ new IntField(0), - /* 14 */ new FloatField(-1), - /* 15 */ new FloatField(-1), - /* 16 */ new FloatField(-1), - /* 17 */ new FloatField(-1), - /* 18 */ new FloatField(minDistance), - /* 19 */ new FloatField(maxDistance), - /* 20 */ new IntField(0), - /* 21 */ new IntField(0), - /* 22 */ new IntField(0), - /* 23 */ new IntField(0), - /* 24 */ new IntField(0), - /* 25 */ new FloatField(1), - /* 26 */ new FloatField(negativeDepthOffset), - /* 27 */ new IntField(1), - /* 28 */ new IntField(0), - /* 29 */ new FloatField(5), - /* 30 */ new FloatField(shadowDarkness), - /* 31 */ new IntField(0), - /* 32 */ new IntField(1), - /* 33 */ new IntField(specular), - /* 34 */ new FloatField(glossiness), - /* 35 */ new IntField(lighting), - /* 36 */ new IntField(-2), - /* 37 */ new IntField(0), - /* 38 */ new FloatField(specularity), - /* 39 */ new IntField(1), - /* 40 */ new IntField(0), - /* 41 */ new IntField(0), - /* 42 */ new IntField(0), - /* 43 */ new IntField(0), - /* 44 */ new IntField(0), - /* 45 */ new IntField(0), - ], [ - /* 0 */ scalarFromArg(blendMode), - /* 1 */ scalarFromArg(offsetX), - /* 2 */ scalarFromArg(offsetY), - /* 3 */ scalarFromArg(offsetZ), - /* 4 */ scalarFromArg(width), - /* 5 */ scalarFromArg(height), - /* 6 */ scalarFromArg(rotationX), - /* 7 */ scalarFromArg(rotationY), - /* 8 */ scalarFromArg(rotationZ), - /* 9 */ scalarFromArg(rotationSpeedX), - /* 10 */ scalarFromArg(rotationSpeedMultiplierX), - /* 11 */ scalarFromArg(rotationSpeedY), - /* 12 */ scalarFromArg(rotationSpeedMultiplierY), - /* 13 */ scalarFromArg(rotationSpeedZ), - /* 14 */ scalarFromArg(rotationSpeedMultiplierZ), - /* 15 */ vectorFromArg(color1), - /* 16 */ vectorFromArg(color2), - /* 17 */ vectorFromArg(color3), - /* 18 */ vectorFromArg(layersColor), - /* 19 */ vectorFromArg(layer1Color), - /* 20 */ vectorFromArg(layer2Color), - /* 21 */ scalarFromArg(alphaThreshold), - /* 22 */ scalarFromArg(frameIndex), - /* 23 */ scalarFromArg(frameIndexOffset), - /* 24 */ new ConstantProperty(0), - /* 25 */ new ConstantProperty(0), - /* 26 */ new ConstantProperty(0), - /* 27 */ new ConstantProperty(0), - /* 28 */ new ConstantProperty(1), - /* 29 */ new ConstantProperty(1), - /* 30 */ scalarFromArg(layer1SpeedU), - /* 31 */ scalarFromArg(layer1SpeedV), - /* 32 */ scalarFromArg(layer1OffsetU), - /* 33 */ scalarFromArg(layer1OffsetV), - /* 34 */ scalarFromArg(layer1ScaleU), - /* 35 */ scalarFromArg(layer1ScaleV), - /* 30 */ scalarFromArg(layer2SpeedU), - /* 31 */ scalarFromArg(layer2SpeedV), - /* 38 */ scalarFromArg(layer2OffsetU), - /* 39 */ scalarFromArg(layer2OffsetV), - /* 34 */ scalarFromArg(layer2ScaleU), - /* 35 */ scalarFromArg(layer2ScaleV), - ], [ - /* 0 */ scalarFromArg(rgbMultiplier), - /* 1 */ scalarFromArg(alphaMultiplier), - /* 2 */ new ConstantProperty(0), - /* 3 */ new ConstantProperty(1, 1, 1, 1), - /* 4 */ new ConstantProperty(1, 1, 1, 1), - /* 5 */ new ConstantProperty(1, 1, 1, 1), - /* 6 */ new ConstantProperty(0), - ]) - } - - /** - * Blend mode. See {@link BlendMode} for more information. - * - * **Argument**: {@link PropertyArgument.Constant Constant 0} - */ - get blendMode() { return this.properties1[0].valueAt(0) as BlendMode } - set blendMode(value: BlendMode | ScalarProperty) { setPropertyInList(this.properties1, 0, value) } - /** - * Blend mode. See {@link BlendMode} for more information. - * - * **Argument**: {@link PropertyArgument.Constant Constant 0} - */ - get blendModeProperty() { return this.properties1[0] } - - /** - * X position offset. - * - * **Argument**: {@link PropertyArgument.ParticleAge Particle age} - */ - get offsetX() { return this.properties1[1] } - set offsetX(value) { setPropertyInList(this.properties1, 1, value) } - - /** - * Y position offset. - * - * **Argument**: {@link PropertyArgument.ParticleAge Particle age} - */ - get offsetY() { return this.properties1[2] } - set offsetY(value) { setPropertyInList(this.properties1, 2, value) } - - /** - * Z position offset. - * - * **Argument**: {@link PropertyArgument.ParticleAge Particle age} - */ - get offsetZ() { return this.properties1[3] } - set offsetZ(value) { setPropertyInList(this.properties1, 3, value) } - - /** - * The width of the particle. - * - * If {@link uniformScale} is enabled, this also controls the height. - * - * **Argument**: {@link PropertyArgument.ParticleAge Particle age} - */ - get width() { return this.properties1[4] } - set width(value) { setPropertyInList(this.properties1, 4, value) } - - /** - * The height of the particle. - * - * If {@link uniformScale} is enabled, {@link width} also controls the - * height, and this property is ignored. - * - * **Argument**: {@link PropertyArgument.ParticleAge Particle age} - */ - get height() { return this.properties1[5] } - set height(value) { setPropertyInList(this.properties1, 5, value) } - - /** - * Rotation around the X-axis in degrees. - * - * **Argument**: {@link PropertyArgument.Constant Constant 0} - * - * See also: - * - {@link rotationSpeedX} - * - {@link rotationSpeedMultiplierX} - */ - get rotationX() { return this.properties1[6] } - set rotationX(value) { setPropertyInList(this.properties1, 6, value) } - - /** - * Rotation around the Y-axis in degrees. - * - * **Argument**: {@link PropertyArgument.Constant Constant 0} - * - * See also: - * - {@link rotationSpeedY} - * - {@link rotationSpeedMultiplierY} - */ - get rotationY() { return this.properties1[7] } - set rotationY(value) { setPropertyInList(this.properties1, 7, value) } - - /** - * Rotation around the Z-axis in degrees. - * - * **Argument**: {@link PropertyArgument.Constant Constant 0} - * - * See also: - * - {@link rotationSpeedZ} - * - {@link rotationSpeedMultiplierZ} - */ - get rotationZ() { return this.properties1[8] } - set rotationZ(value) { setPropertyInList(this.properties1, 8, value) } - - /** - * Rotation speed around the X-axis in degrees per second. - * - * **Argument**: {@link PropertyArgument.ParticleAge Particle age} - * - * See also: - * - {@link rotationX} - * - {@link rotationSpeedMultiplierX} - */ - get rotationSpeedX() { return this.properties1[9] } - set rotationSpeedX(value) { setPropertyInList(this.properties1, 9, value) } - - /** - * Rotation speed around the Y-axis in degrees per second. - * - * **Argument**: {@link PropertyArgument.ParticleAge Particle age} - * - * See also: - * - {@link rotationY} - * - {@link rotationSpeedMultiplierY} - */ - get rotationSpeedY() { return this.properties1[11] } - set rotationSpeedY(value) { setPropertyInList(this.properties1, 11, value) } - - /** - * Rotation speed around the Z-axis in degrees per second. - * - * **Argument**: {@link PropertyArgument.ParticleAge Particle age} - * - * See also: - * - {@link rotationZ} - * - {@link rotationSpeedMultiplierZ} - */ - get rotationSpeedZ() { return this.properties1[13] } - set rotationSpeedZ(value) { setPropertyInList(this.properties1, 13, value) } - - /** - * Multiplier for the rotation speed around the X-axis. - * - * **Argument**: {@link PropertyArgument.ParticleAge Particle age} - * - * See also: - * - {@link rotationX} - * - {@link rotationSpeedX} - */ - get rotationSpeedMultiplierX() { return this.properties1[10] } - set rotationSpeedMultiplierX(value) { setPropertyInList(this.properties1, 10, value) } - - /** - * Multiplier for the rotation speed around the Y-axis. - * - * **Argument**: {@link PropertyArgument.ParticleAge Particle age} - * - * See also: - * - {@link rotationY} - * - {@link rotationSpeedY} - */ - get rotationSpeedMultiplierY() { return this.properties1[12] } - set rotationSpeedMultiplierY(value) { setPropertyInList(this.properties1, 12, value) } - - /** - * Multiplier for the rotation speed around the Z-axis. - * - * **Argument**: {@link PropertyArgument.ParticleAge Particle age} - * - * See also: - * - {@link rotationZ} - * - {@link rotationSpeedZ} - */ - get rotationSpeedMultiplierZ() { return this.properties1[14] } - set rotationSpeedMultiplierZ(value) { setPropertyInList(this.properties1, 14, value) } - - /** - * Color multiplier for the particle. Seemingly identical to {@link color3}? - * - * **Argument**: {@link PropertyArgument.ParticleAge Particle age} - */ - get color1() { return this.properties1[15] } - set color1(value) { setPropertyInList(this.properties1, 15, value) } - - /** - * Color multiplier. - * - * **Argument**: {@link PropertyArgument.EmissionTime Emission time} - */ - get color2() { return this.properties1[16] } - set color2(value) { setPropertyInList(this.properties1, 16, value) } - - /** - * Color multiplier for the particle. Seemingly identical to {@link color1}? - * - * **Argument**: {@link PropertyArgument.ParticleAge Particle age}. - */ - get color3() { return this.properties1[17] } - set color3(value) { setPropertyInList(this.properties1, 17, value) } - - /** - * Color multiplier for both of the texture layers. - * - * **Argument**: {@link PropertyArgument.ParticleAge Particle age} - */ - get layersColor() { return this.properties1[18] } - set layersColor(value) { setPropertyInList(this.properties1, 18, value) } - - /** - * Color multiplier for Layer 1. - * - * **Argument**: {@link PropertyArgument.ParticleAge Particle age} - */ - get layer1Color() { return this.properties1[19] } - set layer1Color(value) { setPropertyInList(this.properties1, 19, value) } - - /** - * Color multiplier for Layer 2. - * - * **Argument**: {@link PropertyArgument.ParticleAge Particle age} - */ - get layer2Color() { return this.properties1[20] } - set layer2Color(value) { setPropertyInList(this.properties1, 20, value) } - - /** - * Parts of the particle with less opacity than this threshold will be - * invisible. The range is 0-255. - * - * **Argument**: {@link PropertyArgument.EffectAge Effect age} - */ - get alphaThreshold() { return this.properties1[21] } - set alphaThreshold(value) { setPropertyInList(this.properties1, 21, value) } - - /** - * The index of the frame to show from the texture atlas. Can be animated - * using a {@link PropertyFunction.Linear linear property} or similar. - * - * Seemingly identical to {@link frameIndexOffset}? The sum of these two - * properties is the actual frame index that gets used. - * - * **Argument**: {@link PropertyArgument.ParticleAge Particle age} - * - * See also: - * - {@link interpolateFrames} - */ - get frameIndex() { return this.properties1[22] } - set frameIndex(value) { setPropertyInList(this.properties1, 22, value) } - - /** - * Seemingly identical to {@link frameIndex}? The sum of these two properties - * is the actual frame index that gets used. - * - * **Argument**: {@link PropertyArgument.ParticleAge Particle age} - * - * See also: - * - {@link interpolateFrames} - */ - get frameIndexOffset() { return this.properties1[23] } - set frameIndexOffset(value) { setPropertyInList(this.properties1, 23, value) } - - /** - * Horiztonal scroll speed for Layer 1. - * - * **Argument**: {@link PropertyArgument.ParticleAge Particle age} - */ - get layer1SpeedU() { return this.properties1[30] } - set layer1SpeedU(value) { setPropertyInList(this.properties1, 30, value) } - - /** - * Vertical scroll speed for Layer 1. - * - * **Argument**: {@link PropertyArgument.ParticleAge Particle age} - */ - get layer1SpeedV() { return this.properties1[31] } - set layer1SpeedV(value) { setPropertyInList(this.properties1, 31, value) } - - /** - * Horizontal offset for the UV coordinates of Layer 1. - * - * **Argument**: {@link PropertyArgument.Constant Constant 0} - */ - get layer1OffsetU() { return this.properties1[32] } - set layer1OffsetU(value) { setPropertyInList(this.properties1, 32, value) } - - /** - * Vertical offset for the UV coordinates of Layer 1. - * - * **Argument**: {@link PropertyArgument.Constant Constant 0} - */ - get layer1OffsetV() { return this.properties1[33] } - set layer1OffsetV(value) { setPropertyInList(this.properties1, 33, value) } - - /** - * Horizontal scale for the UV coordinates of Layer 1. - * - * **Argument**: {@link PropertyArgument.ParticleAge Particle age} - */ - get layer1ScaleU() { return this.properties1[34] } - set layer1ScaleU(value) { setPropertyInList(this.properties1, 34, value) } - - /** - * Vertical scale for the UV coordinates of Layer 1. - * - * **Argument**: {@link PropertyArgument.ParticleAge Particle age} - */ - get layer1ScaleV() { return this.properties1[35] } - set layer1ScaleV(value) { setPropertyInList(this.properties1, 35, value) } - - /** - * Horiztonal scroll speed for Layer 2. - * - * **Argument**: {@link PropertyArgument.ParticleAge Particle age} - */ - get layer2SpeedU() { return this.properties1[36] } - set layer2SpeedU(value) { setPropertyInList(this.properties1, 36, value) } - - /** - * Vertical scroll speed for Layer 2. - * - * **Argument**: {@link PropertyArgument.ParticleAge Particle age} - */ - get layer2SpeedV() { return this.properties1[37] } - set layer2SpeedV(value) { setPropertyInList(this.properties1, 37, value) } - - /** - * Horizontal offset for the UV coordinates of Layer 2. - * - * **Argument**: {@link PropertyArgument.Constant Constant 0} - */ - get layer2OffsetU() { return this.properties1[38] } - set layer2OffsetU(value) { setPropertyInList(this.properties1, 38, value) } - - /** - * Vertical offset for the UV coordinates of Layer 2. - * - * **Argument**: {@link PropertyArgument.Constant Constant 0} - */ - get layer2OffsetV() { return this.properties1[39] } - set layer2OffsetV(value) { setPropertyInList(this.properties1, 39, value) } - - /** - * Horizontal scale for the UV coordinates of Layer 2. - * - * **Argument**: {@link PropertyArgument.ParticleAge Particle age} - */ - get layer2ScaleU() { return this.properties1[40] } - set layer2ScaleU(value) { setPropertyInList(this.properties1, 40, value) } - - /** - * Vertical scale for the UV coordinates of Layer 2. - * - * **Argument**: {@link PropertyArgument.ParticleAge Particle age} - */ - get layer2ScaleV() { return this.properties1[41] } - set layer2ScaleV(value) { setPropertyInList(this.properties1, 41, value) } - - /** - * Scalar multiplier for the color that does not affect the alpha. - * Effectively a brightness multiplier. - * - * **Argument**: {@link PropertyArgument.EffectAge Effect age} - */ - get rgbMultiplier() { return this.properties2[0] } - set rgbMultiplier(value) { setPropertyInList(this.properties2, 0, value) } - - /** - * Alpha multiplier. - * - * **Argument**: {@link PropertyArgument.EffectAge Effect age} - */ - get alphaMultiplier() { return this.properties2[1] } - set alphaMultiplier(value) { setPropertyInList(this.properties2, 1, value) } - - /** - * Controls the orientation mode for the particles. See - * {@link OrientationMode} for more information. - */ - get orientation() { return this.fields1[0].value as OrientationMode } - set orientation(value) { this.fields1[0].value = value } - - /** - * Mask texture ID. - */ - get mask() { return this.fields1[1].value as number } - set mask(value) { this.fields1[1].value = value } - - /** - * Layer 1 texture ID. - */ - get layer1() { return this.fields1[2].value as number } - set layer1(value) { this.fields1[2].value = value } - - /** - * Layer 2 texture ID. - */ - get layer2() { return this.fields1[3].value as number } - set layer2(value) { this.fields1[3].value = value } - - /** - * If enabled, the particle width-related properties and fields will control - * both the width and height of the particles, and the height counterparts - * will be ignored. - * - * See also: - * - {@link width} - * - {@link height} - */ - get uniformScale() { return this.fields1[4].value as boolean } - set uniformScale(value) { this.fields1[4].value = value } - - /** - * To split the texture into multiple animation frames, this value must be - * set to the number of columns in the texture. It should equal - * `textureWidth / frameWidth`. - * - * See also: - * - {@link totalFrames} - */ - get columns() { return this.fields1[6].value as number } - set columns(value) { this.fields1[6].value = value } - - /** - * To split the texture into multiple animation frames, this value must be - * set to the total number of frames in the texture. - * - * See also: - * - {@link columns} - */ - get totalFrames() { return this.fields1[7].value as number } - set totalFrames(value) { this.fields1[7].value = value } - - /** - * If enabled, the texture animation will use linear interpolation to mix - * frames when the frame index is not a whole number. For example, if the - * frame index is 0.5, enabling this will cause the average of the first two - * frames to be shown instead of just the first frame. - * - * If disabled, the frame index will just be truncated to get a whole number. - * - * See also: - * - {@link frameIndex} - * - {@link frameIndexOffset} - */ - get interpolateFrames() { return this.fields1[8].value as number } - set interpolateFrames(value) { this.fields1[8].value = value } - - /** - * Controls how the particles should intersect with objects they touch. If - * disabled, the particles will simply be cut off where they intersect - * objects. If enabled, they will instead display in front of the object if - * they are close enough, and will fade out with distance from the object's - * surface that is blocking the view of the particle. - */ - get depthBlend() { return this.fields1[11].value as boolean } - set depthBlend(value) { this.fields1[11].value = value } - - /** - * Controls the shape of the particles. If disabled, the particles will be - * rectangular. If enabled, they will be octagonal. - */ - get octagonal() { return this.fields1[12].value as boolean } - set octagonal(value) { this.fields1[12].value = value } - - /** - * Negative values will make the particle draw in front of objects closer to - * the camera, while positive values will make it draw behind objects farther - * away from the camera. - * - * {@link ActionType.BillboardEx BillboardEx} has a - * {@link BillboardExParams.depthOffset property} that works the - * same way, but reversed. Since that property was discovered before this - * field, this field was given the "negative" name. - */ - get negativeDepthOffset() { return this.fields2[26].value as number } - set negativeDepthOffset(value) { this.fields2[26].value = value } - - /** - * Controls how dark shaded parts of the particle are. - */ - get shadowDarkness() { return this.fields2[30].value as number } - set shadowDarkness(value) { this.fields2[30].value = value } - - /** - * Specular texture ID. Defaults to 0. - * - * See also: - * - {@link lighting} - * - {@link glossiness} - * - {@link specularity} - */ - get specular() { return this.fields2[33].value as number } - set specular(value) { this.fields2[33].value = value } - - /** - * Controls how sharp the specular highlights are. - * - * See also: - * - {@link lighting} - * - {@link specular} - * - {@link specularity} - */ - get glossiness() { return this.fields2[34].value as number } - set glossiness(value) { this.fields2[34].value = value } - - /** - * Controls how the particles are lit. See {@link LightingMode} for more - * information. - */ - get lighting() { return this.fields2[35].value as LightingMode } - set lighting(value) { this.fields2[35].value = value } - - /** - * Controls how bright the specular highlights are. - * - * See also: - * - {@link lighting} - * - {@link specular} - * - {@link glossiness} - */ - get specularity() { return this.fields2[38].value as number } - set specularity(value) { this.fields2[38].value = value } - -} - -export interface ModelParams { - /** - * Controls the orientation mode for the particles. See - * {@link OrientationMode} for more information. Defaults to - * {@link OrientationMode.ParentNegativeZ}. - */ - orientation?: OrientationMode - /** - * Each particle will pick a random number between this value and 1, and the - * width of the particle will be multiplied by this number. For example, - * setting this to 0.5 will make the particles randomly thinner, down to half - * width. Setting it to 2 will make them randomly wider, up to double width. - * Defaults to 1. - * - * If {@link uniformScale} is enabled, this also affects the height and - * depth. - */ - scaleVariationX?: number - /** - * Each particle will pick a random number between this value and 1, and the - * height of the particle will be multiplied by this number. For example, - * setting this to 0.5 will make the particles randomly shorter, down to half - * height. Setting it to 2 will make them randomly taller, up to double - * height. Defaults to 1. - * - * If {@link uniformScale} is enabled, {@link scaleVariationX} also affects - * the height, and this field is ignored. - */ - scaleVariationY?: number - /** - * Each particle will pick a random number between this value and 1, and the - * depth of the particle will be multiplied by this number. For example, - * setting this to 0.5 will make the particles randomly shallower, down to - * half depth. Setting it to 2 will make them randomly deeper, up to double - * depth. Defaults to 1. - * - * If {@link uniformScale} is enabled, {@link scaleVariationX} also affects - * the depth, and this field is ignored. - */ - scaleVariationZ?: number - /** - * If enabled, the particle X scale-related properties and fields will - * control the scale in all axes, and the Y and Z counterparts will be - * ignored. Defaults to false. - * - * See also: - * - {@link sizeX} - * - {@link sizeY} - * - {@link sizeZ} - * - {@link scaleVariationX} - * - {@link scaleVariationY} - * - {@link scaleVariationZ} - */ - uniformScale?: boolean - /** - * To split the texture into multiple animation frames, this value must be - * set to the number of columns in the texture. It should equal - * `textureWidth / frameWidth`. Defaults to 1. - * - * Setting this to any value other thn 1 will disable UV offset properties: - * - {@link offsetU} - * - {@link offsetV} - * - {@link speedU} - * - {@link speedV} - * - * See also: - * - {@link totalFrames} - */ - columns?: number - /** - * To split the texture into multiple animation frames, this value must be - * set to the total number of frames in the texture. Defaults to 1. - * - * Setting this to any value other thn 1 will disable UV offset properties: - * - {@link offsetU} - * - {@link offsetV} - * - {@link speedU} - * - {@link speedV} - * - * See also: - * - {@link columns} - */ - totalFrames?: number - /** - * Controls the color of the additional bloom effect. The colors of the - * particles will be multiplied with this color to get the final color - * of the bloom effect. Defaults to [1, 1, 1]. - * - * Note: - * - This has no effect if the "Effects Quality" setting is set to "Low". - * - This does not affect the natural bloom effect from high color values. - * - * See also: - * - {@link bloomStrength} - */ - bloomColor?: Vector3 - /** - * Controls the strength of the additional bloom effect. Defaults to 0. - * - * Note: - * - This has no effect if the "Effects Quality" setting is set to "Low". - * - This does not affect the natural bloom effect from high color values. - * - * See also: - * - {@link bloomColor} - */ - bloomStrength?: number - /** - * Minimum view distance. If the particle is closer than this distance from - * the camera, it will be hidden. Can be set to -1 to disable the limit. - * Defaults to -1. - * - * See also: - * - {@link maxDistance} - */ - minDistance?: number - /** - * Maximum view distance. If the particle is farther away than this distance - * from the camera, it will be hidden. Can be set to -1 to disable the limit. - * Defaults to -1. - * - * See also: - * - {@link minDistance} - */ - maxDistance?: number - /** - * Controls how the particles are lit. See {@link LightingMode} for more - * information. Defaults to {@link LightingMode.Lit Lit}. - */ - lighting?: LightingMode - /** - * The ID of the model to use. Defaults to 80201. - * - * **Argument**: {@link PropertyArgument.Constant Constant 0} - */ - model?: ScalarPropertyArg - /** - * The width of the particle. - * - * If {@link uniformScale} is enabled, this also controls the height. - * - * Defaults to 1. - * - * **Argument**: {@link PropertyArgument.ParticleAge Particle age} - * - * See also: - * - {@link scaleVariationX} - */ - sizeX?: ScalarPropertyArg - /** - * The height of the particle. - * - * If {@link uniformScale} is enabled, {@link sizeX} also controls the - * height, and this property is ignored. - * - * Defaults to 1. - * - * **Argument**: {@link PropertyArgument.ParticleAge Particle age} - * - * See also: - * - {@link scaleVariationY} - */ - sizeY?: ScalarPropertyArg - /** - * The depth of the particle. - * - * If {@link uniformScale} is enabled, {@link sizeX} also controls the - * depth, and this property is ignored. - * - * Defaults to 1. - * - * **Argument**: {@link PropertyArgument.ParticleAge Particle age} - * - * See also: - * - {@link scaleVariationY} - */ - sizeZ?: ScalarPropertyArg - /** - * Rotation around the X-axis in degrees. Defaults to 0. - * - * **Argument**: {@link PropertyArgument.Constant Constant 0} - * - * See also: - * - {@link rotationSpeedX} - * - {@link rotationSpeedMultiplierX} - */ - rotationX?: ScalarPropertyArg - /** - * Rotation around the Y-axis in degrees. Defaults to 0. - * - * **Argument**: {@link PropertyArgument.Constant Constant 0} - * - * See also: - * - {@link rotationSpeedY} - * - {@link rotationSpeedMultiplierY} - */ - rotationY?: ScalarPropertyArg - /** - * Rotation around the Z-axis in degrees. Defaults to 0. - * - * **Argument**: {@link PropertyArgument.Constant Constant 0} - * - * See also: - * - {@link rotationSpeedZ} - * - {@link rotationSpeedMultiplierZ} - */ - rotationZ?: ScalarPropertyArg - /** - * Rotation speed around the X-axis in degrees per second. Defaults to 0. - * - * **Argument**: {@link PropertyArgument.ParticleAge Particle age} - * - * See also: - * - {@link rotationX} - * - {@link rotationSpeedMultiplierX} - */ - rotationSpeedX?: ScalarPropertyArg - /** - * Rotation speed around the Y-axis in degrees per second. Defaults to 0. - * - * **Argument**: {@link PropertyArgument.ParticleAge Particle age} - * - * See also: - * - {@link rotationY} - * - {@link rotationSpeedMultiplierY} - */ - rotationSpeedY?: ScalarPropertyArg - /** - * Rotation speed around the Z-axis in degrees per second. Defaults to 0. - * - * **Argument**: {@link PropertyArgument.ParticleAge Particle age} - * - * See also: - * - {@link rotationZ} - * - {@link rotationSpeedMultiplierZ} - */ - rotationSpeedZ?: ScalarPropertyArg - /** - * Multiplier for the rotation speed around the X-axis. Defaults to 1. - * - * **Argument**: {@link PropertyArgument.ParticleAge Particle age} - * - * See also: - * - {@link rotationX} - * - {@link rotationSpeedX} - */ - rotationSpeedMultiplierX?: ScalarPropertyArg - /** - * Multiplier for the rotation speed around the Y-axis. Defaults to 1. - * - * **Argument**: {@link PropertyArgument.ParticleAge Particle age} - * - * See also: - * - {@link rotationY} - * - {@link rotationSpeedY} - */ - rotationSpeedMultiplierY?: ScalarPropertyArg - /** - * Multiplier for the rotation speed around the Z-axis. Defaults to 1. - * - * **Argument**: {@link PropertyArgument.ParticleAge Particle age} - * - * See also: - * - {@link rotationZ} - * - {@link rotationSpeedZ} - */ - rotationSpeedMultiplierZ?: ScalarPropertyArg - /** - * Blend mode. Defaults to {@link BlendMode.Normal}. - * - * **Argument**: {@link PropertyArgument.Constant Constant 0} - */ - blendMode?: BlendMode | ScalarProperty - /** - * Color multiplier. Defaults to [1, 1, 1, 1]. - * - * **Argument**: {@link PropertyArgument.ParticleAge Particle age} - */ - color1?: Vector4PropertyArg - /** - * Color multiplier. Defaults to [1, 1, 1, 1]. - * - * **Argument**: {@link PropertyArgument.EmissionTime Emission time} - */ - color2?: Vector4PropertyArg - /** - * Color multiplier. Defaults to [1, 1, 1, 1]. - * - * **Argument**: {@link PropertyArgument.EffectAge Effect age}. - */ - color3?: Vector4PropertyArg - /** - * The index of the frame to show from the texture atlas. Can be animated - * using a {@link PropertyFunction.Linear linear property} or similar. - * Defaults to 0. - * - * Seemingly identical to {@link frameIndexOffset}? The sum of these two - * properties is the actual frame index that gets used. - * - * **Argument**: {@link PropertyArgument.ParticleAge Particle age} - */ - frameIndex?: ScalarPropertyArg - /** - * Seemingly identical to {@link frameIndex}? The sum of these two properties - * is the actual frame index that gets used. Defaults to 0. - * - * **Argument**: {@link PropertyArgument.ParticleAge Particle age} - */ - frameIndexOffset?: ScalarPropertyArg - /** - * Horizontal offset for the UV coordinates of the model. Defaults to 0. - * - * If the texture is an animation sheet that is split up into multiple frames - * using {@link columns} and/or {@link totalFrames}, this property has no - * effect. - * - * **Argument**: {@link PropertyArgument.Constant Constant 0} - */ - offsetU?: ScalarPropertyArg - /** - * Vertical offset for the UV coordinates of the model. Defaults to 0. - * - * If the texture is an animation sheet that is split up into multiple frames - * using {@link columns} and/or {@link totalFrames}, this property has no - * effect. - * - * **Argument**: {@link PropertyArgument.Constant Constant 0} - */ - offsetV?: ScalarPropertyArg - /** - * Horiztonal scroll speed for the model's texture. Defaults to 0. - * - * If the texture is an animation sheet that is split up into multiple frames - * using {@link columns} and/or {@link totalFrames}, this property has no - * effect. - * - * **Argument**: {@link PropertyArgument.ParticleAge Particle age} - */ - speedU?: ScalarPropertyArg - /** - * Multiplier for {@link speedU}. Defaults to 1. - * - * **Argument**: {@link PropertyArgument.ParticleAge Particle age} - */ - speedUMultiplier?: ScalarPropertyArg - /** - * Vertical scroll speed for the model's texture. Defaults to 0. - * - * If the texture is an animation sheet that is split up into multiple frames - * using {@link columns} and/or {@link totalFrames}, this property has no - * effect. - * - * **Argument**: {@link PropertyArgument.ParticleAge Particle age} - */ - speedV?: ScalarPropertyArg - /** - * Multiplier for {@link speedV}. Defaults to 1. - * - * **Argument**: {@link PropertyArgument.ParticleAge Particle age} - */ - speedVMultiplier?: ScalarPropertyArg - /** - * Scalar multiplier for the color that does not affect the alpha. - * Effectively a brightness multiplier. Defaults to 1. - * - * **Argument**: {@link PropertyArgument.EffectAge Effect age} - */ - rgbMultiplier?: ScalarPropertyArg - /** - * Alpha multiplier. Defaults to 1. - * - * **Argument**: {@link PropertyArgument.EffectAge Effect age} - */ - alphaMultiplier?: ScalarPropertyArg -} -/** - * Particle with a 3D model. - */ -class Model extends CommonFields2Action { - - constructor({ - orientation = OrientationMode.ParentNegativeZ, - scaleVariationX = 1, - scaleVariationY = 1, - scaleVariationZ = 1, - uniformScale = false, - columns = 1, - totalFrames = 1, - bloomColor = [1, 1, 1], - bloomStrength = 0, - minDistance = -1, - maxDistance = -1, - lighting = LightingMode.Lit, - model = 80201, - sizeX = 1, - sizeY = 1, - sizeZ = 1, - rotationX = 0, - rotationY = 0, - rotationZ = 0, - rotationSpeedX = 0, - rotationSpeedY = 0, - rotationSpeedZ = 0, - rotationSpeedMultiplierX = 1, - rotationSpeedMultiplierY = 1, - rotationSpeedMultiplierZ = 1, - blendMode = BlendMode.Normal, - color1 = [1, 1, 1, 1], - color2 = [1, 1, 1, 1], - color3 = [1, 1, 1, 1], - frameIndex = 0, - frameIndexOffset = 0, - offsetU = 0, - offsetV = 0, - speedU = 0, - speedUMultiplier = 1, - speedV = 0, - speedVMultiplier = 1, - rgbMultiplier = 1, - alphaMultiplier = 1, - }: ModelParams = {}) { - super(ActionType.Model, [ - /* 0 */ new IntField(orientation), - /* 1 */ new FloatField(scaleVariationX), - /* 2 */ new FloatField(scaleVariationY), - /* 3 */ new FloatField(scaleVariationZ), - /* 4 */ new BoolField(uniformScale), - /* 5 */ new IntField(columns), - /* 6 */ new IntField(totalFrames), - /* 7 */ new IntField(-2), - /* 8 */ new IntField(-2), - /* 9 */ new BoolField(true), - /* 10 */ new BoolField(true), - /* 11 */ new IntField(1), - /* 12 */ new IntField(0), - /* 13 */ new IntField(0), - /* 14 */ new IntField(1), - /* 15 */ new FloatField(1), - /* 16 */ new IntField(0), - /* 17 */ new IntField(1), - /* 18 */ new IntField(1), - /* 19 */ new IntField(0), - ], [ - /* 0 */ new IntField(0), - /* 1 */ new IntField(0), - /* 2 */ new IntField(8), - /* 3 */ new IntField(0), - /* 4 */ new IntField(1), - /* 5 */ new FloatField(bloomColor[0]), - /* 6 */ new FloatField(bloomColor[1]), - /* 7 */ new FloatField(bloomColor[2]), - /* 8 */ new FloatField(bloomStrength), - /* 9 */ new IntField(0), - /* 10 */ new IntField(0), - /* 11 */ new IntField(0), - /* 12 */ new IntField(0), - /* 13 */ new IntField(0), - /* 14 */ new FloatField(-1), - /* 15 */ new FloatField(-1), - /* 16 */ new FloatField(-1), - /* 17 */ new FloatField(-1), - /* 18 */ new FloatField(minDistance), - /* 19 */ new FloatField(maxDistance), - /* 20 */ new IntField(0), - /* 21 */ new IntField(0), - /* 22 */ new IntField(0), - /* 23 */ new IntField(0), - /* 24 */ new FloatField(0), - /* 25 */ new FloatField(1), - /* 26 */ new FloatField(0), - /* 27 */ new IntField(1), - /* 28 */ new IntField(0), - /* 29 */ new FloatField(0), - /* 30 */ new FloatField(0), - /* 31 */ new IntField(0), - /* 32 */ new IntField(1), - /* 33 */ new IntField(0), - /* 34 */ new FloatField(0), - /* 35 */ new IntField(lighting), - /* 36 */ new IntField(-2), - /* 37 */ new IntField(0), - ], [ - /* 0 */ scalarFromArg(model), - /* 1 */ scalarFromArg(sizeX), - /* 2 */ scalarFromArg(sizeY), - /* 3 */ scalarFromArg(sizeZ), - /* 4 */ scalarFromArg(rotationX), - /* 5 */ scalarFromArg(rotationY), - /* 6 */ scalarFromArg(rotationZ), - /* 7 */ scalarFromArg(rotationSpeedX), - /* 8 */ scalarFromArg(rotationSpeedMultiplierX), - /* 9 */ scalarFromArg(rotationSpeedY), - /* 10 */ scalarFromArg(rotationSpeedMultiplierY), - /* 11 */ scalarFromArg(rotationSpeedZ), - /* 12 */ scalarFromArg(rotationSpeedMultiplierZ), - /* 13 */ scalarFromArg(blendMode), - /* 14 */ vectorFromArg(color1), - /* 15 */ vectorFromArg(color2), - /* 16 */ vectorFromArg(color3), - /* 17 */ new ConstantProperty(0), - /* 18 */ scalarFromArg(frameIndex), - /* 19 */ scalarFromArg(frameIndexOffset), - /* 20 */ scalarFromArg(offsetU), - /* 21 */ scalarFromArg(offsetV), - /* 22 */ scalarFromArg(speedU), - /* 23 */ scalarFromArg(speedUMultiplier), - /* 24 */ scalarFromArg(speedV), - /* 25 */ scalarFromArg(speedVMultiplier), - /* 26 */ new ConstantProperty(0), - ], [ - /* 0 */ scalarFromArg(rgbMultiplier), - /* 1 */ scalarFromArg(alphaMultiplier), - /* 2 */ new ConstantProperty(0), - /* 3 */ new ConstantProperty(1, 1, 1, 1), - /* 4 */ new ConstantProperty(1, 1, 1, 1), - /* 5 */ new ConstantProperty(1, 1, 1, 1), - /* 6 */ new ConstantProperty(0), - ]) - } - - /** - * Controls the orientation mode for the particles. See - * {@link OrientationMode} for more information. - */ - get orientation() { return this.fields1[0].value as OrientationMode } - set orientation(value) { this.fields1[0].value = value } - - /** - * Each particle will pick a random number between this value and 1, and the - * width of the particle will be multiplied by this number. For example, - * setting this to 0.5 will make the particles randomly thinner, down to half - * width. Setting it to 2 will make them randomly wider, up to double width. - * - * If {@link uniformScale} is enabled, this also affects the height and - * depth. - */ - get scaleVariationX() { return this.fields1[1].value as number } - set scaleVariationX(value) { this.fields1[1].value = value } - - /** - * Each particle will pick a random number between this value and 1, and the - * height of the particle will be multiplied by this number. For example, - * setting this to 0.5 will make the particles randomly shorter, down to half - * height. Setting it to 2 will make them randomly taller, up to double - * height. - * - * If {@link uniformScale} is enabled, {@link scaleVariationX} also affects - * the height, and this field is ignored. - */ - get scaleVariationY() { return this.fields1[2].value as number } - set scaleVariationY(value) { this.fields1[2].value = value } - - /** - * Each particle will pick a random number between this value and 1, and the - * depth of the particle will be multiplied by this number. For example, - * setting this to 0.5 will make the particles randomly shallower, down to - * half depth. Setting it to 2 will make them randomly deeper, up to double - * depth. - * - * If {@link uniformScale} is enabled, {@link scaleVariationX} also affects - * the depth, and this field is ignored. - */ - get scaleVariationZ() { return this.fields1[3].value as number } - set scaleVariationZ(value) { this.fields1[3].value = value } - - /** - * If enabled, the particle X scale-related properties and fields will - * control the scale in all axes, and the Y and Z counterparts will be - * ignored. - * - * See also: - * - {@link sizeX} - * - {@link sizeY} - * - {@link sizeZ} - * - {@link scaleVariationX} - * - {@link scaleVariationY} - * - {@link scaleVariationZ} - */ - get uniformScale() { return this.fields1[4].value as boolean } - set uniformScale(value) { this.fields1[4].value = value } - - /** - * To split the texture into multiple animation frames, this value must be - * set to the number of columns in the texture. It should equal - * `textureWidth / frameWidth`. - * - * Setting this to any value other thn 1 will disable UV offset properties: - * - {@link offsetU} - * - {@link offsetV} - * - {@link speedU} - * - {@link speedV} - * - * See also: - * - {@link totalFrames} - */ - get columns() { return this.fields1[5].value as number } - set columns(value) { this.fields1[5].value = value } - - /** - * To split the texture into multiple animation frames, this value must be - * set to the total number of frames in the texture. - * - * Setting this to any value other thn 1 will disable UV offset properties: - * - {@link offsetU} - * - {@link offsetV} - * - {@link speedU} - * - {@link speedV} - * - * See also: - * - {@link columns} - */ - get totalFrames() { return this.fields1[6].value as number } - set totalFrames(value) { this.fields1[6].value = value } - - /** - * Controls how the particles are lit. See {@link LightingMode} for more - * information. - */ - get lighting() { return this.fields2[35].value as LightingMode } - set lighting(value) { this.fields2[35].value = value } - - /** - * The ID of the model to use. - * - * **Argument**: {@link PropertyArgument.Constant Constant 0} - */ - get model() { return this.properties1[0].valueAt(0) as number } - set model(value: ScalarPropertyArg) { setPropertyInList(this.properties1, 0, value) } - /** - * The ID of the model to use. - * - * **Argument**: {@link PropertyArgument.Constant Constant 0} - */ - get modelProperty() { return this.properties1[0] } - - /** - * The width of the particle. - * - * If {@link uniformScale} is enabled, this also controls the height. - * - * **Argument**: {@link PropertyArgument.ParticleAge Particle age} - * - * See also: - * - {@link scaleVariationX} - */ - get sizeX() { return this.properties1[1] } - set sizeX(value) { setPropertyInList(this.properties1, 1, value) } - - /** - * The height of the particle. - * - * If {@link uniformScale} is enabled, {@link sizeX} also controls the - * height, and this property is ignored. - * - * **Argument**: {@link PropertyArgument.ParticleAge Particle age} - * - * See also: - * - {@link scaleVariationY} - */ - get sizeY() { return this.properties1[2] } - set sizeY(value) { setPropertyInList(this.properties1, 2, value) } - - /** - * The depth of the particle. - * - * If {@link uniformScale} is enabled, {@link sizeX} also controls the - * depth, and this property is ignored. - * - * **Argument**: {@link PropertyArgument.ParticleAge Particle age} - * - * See also: - * - {@link scaleVariationY} - */ - get sizeZ() { return this.properties1[3] } - set sizeZ(value) { setPropertyInList(this.properties1, 3, value) } - - /** - * Rotation around the X-axis in degrees. - * - * **Argument**: {@link PropertyArgument.Constant Constant 0} - * - * See also: - * - {@link rotationSpeedX} - * - {@link rotationSpeedMultiplierX} - */ - get rotationX() { return this.properties1[4] } - set rotationX(value) { setPropertyInList(this.properties1, 4, value) } - - /** - * Rotation around the Y-axis in degrees. - * - * **Argument**: {@link PropertyArgument.Constant Constant 0} - * - * See also: - * - {@link rotationSpeedY} - * - {@link rotationSpeedMultiplierY} - */ - get rotationY() { return this.properties1[5] } - set rotationY(value) { setPropertyInList(this.properties1, 5, value) } - - /** - * Rotation around the Z-axis in degrees. - * - * **Argument**: {@link PropertyArgument.Constant Constant 0} - * - * See also: - * - {@link rotationSpeedZ} - * - {@link rotationSpeedMultiplierZ} - */ - get rotationZ() { return this.properties1[6] } - set rotationZ(value) { setPropertyInList(this.properties1, 6, value) } - - /** - * Rotation speed around the X-axis in degrees per second. - * - * **Argument**: {@link PropertyArgument.ParticleAge Particle age} - * - * See also: - * - {@link rotationX} - * - {@link rotationSpeedMultiplierX} - */ - get rotationSpeedX() { return this.properties1[7] } - set rotationSpeedX(value) { setPropertyInList(this.properties1, 7, value) } - - /** - * Rotation speed around the Y-axis in degrees per second. - * - * **Argument**: {@link PropertyArgument.ParticleAge Particle age} - * - * See also: - * - {@link rotationY} - * - {@link rotationSpeedMultiplierY} - */ - get rotationSpeedY() { return this.properties1[9] } - set rotationSpeedY(value) { setPropertyInList(this.properties1, 9, value) } - - /** - * Rotation speed around the Z-axis in degrees per second. - * - * **Argument**: {@link PropertyArgument.ParticleAge Particle age} - * - * See also: - * - {@link rotationZ} - * - {@link rotationSpeedMultiplierZ} - */ - get rotationSpeedZ() { return this.properties1[11] } - set rotationSpeedZ(value) { setPropertyInList(this.properties1, 11, value) } - - /** - * Multiplier for the rotation speed around the X-axis. - * - * **Argument**: {@link PropertyArgument.ParticleAge Particle age} - * - * See also: - * - {@link rotationX} - * - {@link rotationSpeedX} - */ - get rotationSpeedMultiplierX() { return this.properties1[8] } - set rotationSpeedMultiplierX(value) { setPropertyInList(this.properties1, 8, value) } - - /** - * Multiplier for the rotation speed around the Y-axis. Defaults to 1. - * - * **Argument**: {@link PropertyArgument.ParticleAge Particle age} - * - * See also: - * - {@link rotationY} - * - {@link rotationSpeedY} - */ - get rotationSpeedMultiplierY() { return this.properties1[10] } - set rotationSpeedMultiplierY(value) { setPropertyInList(this.properties1, 10, value) } - - /** - * Multiplier for the rotation speed around the Z-axis. - * - * **Argument**: {@link PropertyArgument.ParticleAge Particle age} - * - * See also: - * - {@link rotationZ} - * - {@link rotationSpeedZ} - */ - get rotationSpeedMultiplierZ() { return this.properties1[12] } - set rotationSpeedMultiplierZ(value) { setPropertyInList(this.properties1, 12, value) } - - /** - * Blend mode. - * - * **Argument**: {@link PropertyArgument.Constant Constant 0} - */ - get blendMode() { return this.properties1[13].valueAt(0) as BlendMode } - set blendMode(value: BlendMode | ScalarProperty) { setPropertyInList(this.properties1, 13, value) } - /** - * Blend mode. - * - * **Argument**: {@link PropertyArgument.Constant Constant 0} - */ - get blendModeProperty() { return this.properties1[13] } - - /** - * Color multiplier. - * - * **Argument**: {@link PropertyArgument.ParticleAge Particle age} - */ - get color1() { return this.properties1[14] } - set color1(value) { setPropertyInList(this.properties1, 14, value) } - - /** - * Color multiplier. - * - * **Argument**: {@link PropertyArgument.EffectAge Effect age}. - */ - get color2() { return this.properties1[15] } - set color2(value) { setPropertyInList(this.properties1, 15, value) } - - /** - * Color multiplier. - * - * **Argument**: {@link PropertyArgument.EffectAge Effect age}. - */ - get color3() { return this.properties1[16] } - set color3(value) { setPropertyInList(this.properties1, 16, value) } - - /** - * The index of the frame to show from the texture atlas. Can be animated - * using a {@link PropertyFunction.Linear linear property} or similar. - * - * Seemingly identical to {@link frameIndexOffset}? The sum of these two - * properties is the actual frame index that gets used. - * - * **Argument**: {@link PropertyArgument.ParticleAge Particle age} - */ - get frameIndex() { return this.properties1[18] } - set frameIndex(value) { setPropertyInList(this.properties1, 18, value) } - - /** - * Seemingly identical to {@link frameIndex}? The sum of these two properties - * is the actual frame index that gets used. - * - * **Argument**: {@link PropertyArgument.ParticleAge Particle age} - */ - get frameIndexOffset() { return this.properties1[19] } - set frameIndexOffset(value) { setPropertyInList(this.properties1, 19, value) } - - /** - * Horizontal offset for the UV coordinates of the model. - * - * If the texture is an animation sheet that is split up into multiple frames - * using {@link columns} and/or {@link totalFrames}, this property has no - * effect. - * - * **Argument**: {@link PropertyArgument.Constant Constant 0} - */ - get offsetU() { return this.properties1[20] } - set offsetU(value) { setPropertyInList(this.properties1, 20, value) } - - /** - * Vertical offset for the UV coordinates of the model. - * - * If the texture is an animation sheet that is split up into multiple frames - * using {@link columns} and/or {@link totalFrames}, this property has no - * effect. - * - * **Argument**: {@link PropertyArgument.Constant Constant 0} - */ - get offsetV() { return this.properties1[21] } - set offsetV(value) { setPropertyInList(this.properties1, 21, value) } - - /** - * Horizontal scroll speed for the model's texture. - * - * If the texture is an animation sheet that is split up into multiple frames - * using {@link columns} and/or {@link totalFrames}, this property has no - * effect. - * - * **Argument**: {@link PropertyArgument.ParticleAge Particle age} - */ - get speedU() { return this.properties1[22] } - set speedU(value) { setPropertyInList(this.properties1, 22, value) } - - /** - * Multiplier for {@link speedU}. - * - * **Argument**: {@link PropertyArgument.ParticleAge Particle age} - */ - get speedUMultiplier() { return this.properties1[23] } - set speedUMultiplier(value) { setPropertyInList(this.properties1, 23, value) } - - /** - * Vertical scroll speed for the model's texture. - * - * If the texture is an animation sheet that is split up into multiple frames - * using {@link columns} and/or {@link totalFrames}, this property has no - * effect. - * - * **Argument**: {@link PropertyArgument.ParticleAge Particle age} - */ - get speedV() { return this.properties1[24] } - set speedV(value) { setPropertyInList(this.properties1, 24, value) } - - /** - * Multiplier for {@link speedV}. - * - * **Argument**: {@link PropertyArgument.ParticleAge Particle age} - */ - get speedVMultiplier() { return this.properties1[25] } - set speedVMultiplier(value) { setPropertyInList(this.properties1, 25, value) } - - /** - * Scalar multiplier for the color that does not affect the alpha. - * Effectively a brightness multiplier. - * - * **Argument**: {@link PropertyArgument.EffectAge Effect age} - */ - get rgbMultiplier() { return this.properties2[0] } - set rgbMultiplier(value) { setPropertyInList(this.properties2, 0, value) } - - /** - * Alpha multiplier. - * - * **Argument**: {@link PropertyArgument.EffectAge Effect age} - */ - get alphaMultiplier() { return this.properties2[1] } - set alphaMultiplier(value) { setPropertyInList(this.properties2, 1, value) } - -} - -export interface TracerParams { - /** - * Tracer orientation mode. See {@link TracerOrientationMode} for more - * information. Defaults to {@link TracerOrientationMode.LocalZ}. - */ - orientation?: TracerOrientationMode - /** - * Normal map texture ID. Defaults to 0. - */ - normalMap?: number - /** - * The trail is made up of multiple quads, or *segments*. This controls how - * many seconds to wait between new segments being created. Lower values - * produce a smoother trail. Defaults to 0. - */ - segmentInterval?: number - /** - * The trail is made up of multiple quads, or *segments*. This controls how - * long each segment should last in seconds. Defaults to 1. - */ - segmentDuration?: number - /** - * The trail is made up of multiple quads, or *segments*. This controls how - * many segments may exist at the same time. Defaults to 50. - */ - concurrentSegments?: number - /** - * To split the texture into multiple animation frames, this value must be - * set to the number of columns in the texture. It should equal - * `textureWidth / frameWidth`. Defaults to 1. - * - * See also: - * - {@link totalFrames} - */ - columns?: number - /** - * To split the texture into multiple animation frames, this value must be - * set to the total number of frames in the texture. Defaults to 1. - * - * See also: - * - {@link columns} - */ - totalFrames?: number - /** - * Controls whether or not the UV of the trail should be attached to the node - * or not. If it is attached, the texture will slide along the segments to - * follow the source wherever it moves, as if it was a flag attached to a - * pole. If it is not attached, the texture will stay where it was when the - * segment was created, like a skid mark on a road where the road is the - * segments and the mark is the texture, it wouldn't follow the car/node. - * Defaults to false. - */ - attachedUV?: boolean - /** - * Controls the color of the additional bloom effect. The colors of the - * particles will be multiplied with this color to get the final color - * of the bloom effect. Defaults to [1, 1, 1]. - * - * Note: - * - This has no effect if the "Effects Quality" setting is set to "Low". - * - This does not affect the natural bloom effect from high color values. - * - * See also: - * - {@link bloomStrength} - */ - bloomColor?: Vector3 - /** - * Controls the strength of the additional bloom effect. Defaults to 0. - * - * Note: - * - This has no effect if the "Effects Quality" setting is set to "Low". - * - This does not affect the natural bloom effect from high color values. - * - * See also: - * - {@link bloomColor} - */ - bloomStrength?: number - /** - * Minimum view distance. If the particle is closer than this distance from - * the camera, it will be hidden. Can be set to -1 to disable the limit. - * Defaults to -1. - * - * See also: - * - {@link maxDistance} - */ - minDistance?: number - /** - * Maximum view distance. If the particle is farther away than this distance - * from the camera, it will be hidden. Can be set to -1 to disable the limit. - * Defaults to -1. - * - * See also: - * - {@link minDistance} - */ - maxDistance?: number - /** - * Negative values will make the particle draw in front of objects closer to - * the camera, while positive values will make it draw behind objects farther - * away from the camera. Defaults to 0. - * - * {@link ActionType.BillboardEx BillboardEx} has a - * {@link BillboardExParams.depthOffset property} that works the - * same way, but reversed. Since that property was discovered before this - * field, this field was given the "negative" name. - */ - negativeDepthOffset?: number - /** - * Controls how dark shaded parts of the particle are. Defaults to 0. - */ - shadowDarkness?: number - /** - * Specular texture ID. Defaults to 0. - * - * See also: - * - {@link lighting} - * - {@link glossiness} - * - {@link specularity} - */ - specular?: number - /** - * Controls how sharp the specular highlights are. Defaults to 0.25. - * - * See also: - * - {@link lighting} - * - {@link specular} - * - {@link specularity} - */ - glossiness?: number - /** - * Controls how the particles are lit. See {@link LightingMode} for more - * information. Defaults to {@link LightingMode.Unlit}. - */ - lighting?: LightingMode - /** - * Controls how bright the specular highlights are. Defaults to 0.5. - * - * See also: - * - {@link lighting} - * - {@link specular} - * - {@link glossiness} - */ - specularity?: number - /** - * The ID of the texture for the trail. Defaults to 1. - * - * **Argument**: {@link PropertyArgument.Constant Constant 0} - */ - texture?: ScalarPropertyArg - /** - * Blend mode. Defaults to {@link BlendMode.Normal}. - * - * **Argument**: {@link PropertyArgument.Constant Constant 0} - */ - blendMode?: ScalarPropertyArg - /** - * The width of the trail. Defaults to 1. - * - * **Argument**: {@link PropertyArgument.ParticleAge Particle age} - */ - width?: ScalarPropertyArg - /** - * Multiplier for {@link width}. Defaults to 1. - * - * **Argument**: {@link PropertyArgument.EmissionTime Emission time} - */ - widthMultiplier?: ScalarPropertyArg - /** - * Color multiplier. Defaults to [1, 1, 1, 1]. - * - * **Argument**: {@link PropertyArgument.ParticleAge Particle age} - */ - color1?: Vector4PropertyArg - /** - * Color multiplier. Defaults to [1, 1, 1, 1]. - * - * **Argument**: {@link PropertyArgument.EmissionTime Emission time} - */ - color2?: Vector4PropertyArg - /** - * Color multiplier. Defaults to [1, 1, 1, 1]. - * - * **Argument**: {@link PropertyArgument.EffectAge Effect age}. - */ - color3?: Vector4PropertyArg - /** - * Parts of the particle with less opacity than this threshold will be - * invisible. The range is 0-255. Defaults to 0. - * - * **Argument**: {@link PropertyArgument.EffectAge Effect age} - */ - alphaThreshold?: ScalarPropertyArg - /** - * The index of the frame to show from the texture atlas. Can be animated - * using a {@link PropertyFunction.Linear linear property} or similar. - * Defaults to 0. - * - * Seemingly identical to {@link frameIndexOffset}? The sum of these two - * properties is the actual frame index that gets used. - * - * **Argument**: {@link PropertyArgument.ParticleAge Particle age} - */ - frameIndex?: ScalarPropertyArg - /** - * Seemingly identical to {@link frameIndex}? The sum of these two properties - * is the actual frame index that gets used. Defaults to 0. - * - * **Argument**: {@link PropertyArgument.ParticleAge Particle age} - */ - frameIndexOffset?: ScalarPropertyArg - /** - * Controls how much of the texture's width is used per segment. If - * {@link attachedUV} is enabled, this instead controls how much of the - * texture's width to use for the entire trail. Defaults to 0.1. - * - * **Argument**: {@link PropertyArgument.ParticleAge Particle age} - */ - textureFraction?: ScalarPropertyArg - /** - * Controls how fast the UV coordinates should move horizontally. Defaults - * to 0. - * - * **Argument**: {@link PropertyArgument.ParticleAge Particle age} - */ - speedU?: ScalarPropertyArg - /** - * Controls how much the UV coordinates should be randomly offset by per - * segment. Defaults to 0. - * - * **Argument**: {@link PropertyArgument.ParticleAge Particle age} - */ - varianceV?: ScalarPropertyArg - /** - * Scalar multiplier for the color that does not affect the alpha. - * Effectively a brightness multiplier. Defaults to 1. - * - * **Argument**: {@link PropertyArgument.EffectAge Effect age} - */ - rgbMultiplier?: ScalarPropertyArg - /** - * Alpha multiplier. Defaults to 1. - * - * **Argument**: {@link PropertyArgument.EffectAge Effect age} - */ - alphaMultiplier?: ScalarPropertyArg -} -/** - * Creates a trail behind moving effects. - */ -class Tracer extends CommonFields2Action { - - constructor({ - orientation = TracerOrientationMode.LocalZ, - normalMap = 0, - segmentInterval = 0, - segmentDuration = 1, - concurrentSegments = 50, - columns = 1, - totalFrames = 1, - attachedUV = false, - bloomColor = [1, 1, 1], - bloomStrength = 0, - minDistance = -1, - maxDistance = -1, - negativeDepthOffset = 0, - shadowDarkness = 0, - specular = 0, - glossiness = 0.25, - lighting = LightingMode.Unlit, - specularity = 0.5, - texture = 1, - blendMode = BlendMode.Normal, - width = 1, - widthMultiplier = 1, - color1 = [1, 1, 1, 1], - color2 = [1, 1, 1, 1], - color3 = [1, 1, 1, 1], - alphaThreshold = 0, - frameIndex = 0, - frameIndexOffset = 0, - textureFraction = 0.1, - speedU = 0, - varianceV = 0, - rgbMultiplier = 1, - alphaMultiplier = 1, - }: TracerParams = {}) { - super(ActionType.Tracer, [ - /* 0 */ new IntField(orientation), - /* 1 */ new IntField(normalMap), - /* 2 */ new FloatField(segmentInterval), - /* 3 */ new FloatField(segmentDuration), - /* 4 */ new IntField(concurrentSegments), - /* 5 */ new IntField(0), - /* 6 */ new IntField(0), - /* 7 */ new FloatField(0), - /* 8 */ new IntField(columns), - /* 9 */ new IntField(totalFrames), - /* 10 */ new BoolField(attachedUV), - /* 11 */ new IntField(-1), - /* 12 */ new IntField(-1), - /* 13 */ new IntField(0), - /* 14 */ new IntField(1), - /* 15 */ new IntField(1), - /* 16 */ new IntField(0), - ], [ - /* 0 */ new IntField(0), - /* 1 */ new IntField(0), - /* 2 */ new IntField(8), - /* 3 */ new IntField(0), - /* 4 */ new IntField(1), - /* 5 */ new FloatField(bloomColor[0]), - /* 6 */ new FloatField(bloomColor[1]), - /* 7 */ new FloatField(bloomColor[2]), - /* 8 */ new FloatField(bloomStrength), - /* 9 */ new IntField(0), - /* 10 */ new IntField(0), - /* 11 */ new IntField(0), - /* 12 */ new IntField(0), - /* 13 */ new IntField(0), - /* 14 */ new FloatField(-1), - /* 15 */ new FloatField(-1), - /* 16 */ new FloatField(-1), - /* 17 */ new FloatField(-1), - /* 18 */ new FloatField(minDistance), - /* 19 */ new FloatField(maxDistance), - /* 20 */ new IntField(0), - /* 21 */ new IntField(0), - /* 22 */ new IntField(0), - /* 23 */ new IntField(0), - /* 24 */ new IntField(0), - /* 25 */ new FloatField(1), - /* 26 */ new FloatField(negativeDepthOffset), - /* 27 */ new IntField(1), - /* 28 */ new IntField(0), - /* 29 */ new FloatField(5), - /* 30 */ new FloatField(shadowDarkness), - /* 31 */ new IntField(0), - /* 32 */ new IntField(1), - /* 33 */ new IntField(specular), - /* 34 */ new FloatField(glossiness), - /* 35 */ new IntField(lighting), - /* 36 */ new IntField(-2), - /* 37 */ new IntField(0), - /* 38 */ new FloatField(specularity), - /* 39 */ new IntField(0), - ], [ - /* 0 */ scalarFromArg(texture), - /* 1 */ scalarFromArg(blendMode), - /* 2 */ scalarFromArg(width), - /* 3 */ scalarFromArg(widthMultiplier), - /* 4 */ new ConstantProperty(0), - /* 5 */ new ConstantProperty(0), - /* 6 */ vectorFromArg(color1), - /* 7 */ vectorFromArg(color2), - /* 8 */ vectorFromArg(color3), - /* 9 */ scalarFromArg(alphaThreshold), - /* 10 */ scalarFromArg(frameIndex), - /* 11 */ scalarFromArg(frameIndexOffset), - /* 12 */ scalarFromArg(textureFraction), - /* 13 */ scalarFromArg(speedU), - /* 14 */ scalarFromArg(varianceV), - /* 15 */ new ConstantProperty(-1), - ], [ - /* 0 */ scalarFromArg(rgbMultiplier), - /* 1 */ scalarFromArg(alphaMultiplier), - /* 2 */ new ConstantProperty(0), - /* 3 */ new ConstantProperty(1, 1, 1, 1), - /* 4 */ new ConstantProperty(1, 1, 1, 1), - /* 5 */ new ConstantProperty(1, 1, 1, 1), - /* 6 */ new ConstantProperty(0), - ]) - } - - /** - * Controls the orientation mode for the trail. Note that this is **not** - * {@link OrientationMode} - It works differently for this action, and not - * all of the values have been documented yet. - */ - get orientation() { return this.fields1[0].value as number } - set orientation(value) { this.fields1[0].value = value } - - /** - * Normal map texture ID. - */ - get normalMap() { return this.fields1[1].value as number } - set normalMap(value) { this.fields1[1].value = value } - - /** - * The trail is made up of multiple quads, or *segments*. This controls how - * many seconds to wait between new segments being created. Lower values - * produce a smoother trail. - */ - get segmentInterval() { return this.fields1[2].value as number } - set segmentInterval(value) { this.fields1[2].value = value } - - /** - * The trail is made up of multiple quads, or *segments*. This controls how - * long each segment should last in seconds. - */ - get segmentDuration() { return this.fields1[3].value as number } - set segmentDuration(value) { this.fields1[3].value = value } - - /** - * The trail is made up of multiple quads, or *segments*. This controls how - * many segments may exist at the same time. - */ - get concurrentSegments() { return this.fields1[4].value as number } - set concurrentSegments(value) { this.fields1[4].value = value } - - /** - * To split the texture into multiple animation frames, this value must be - * set to the number of columns in the texture. It should equal - * `textureWidth / frameWidth`. - * - * See also: - * - {@link totalFrames} - */ - get columns() { return this.fields1[8].value as number } - set columns(value) { this.fields1[8].value = value } - - /** - * To split the texture into multiple animation frames, this value must be - * set to the total number of frames in the texture. - * - * See also: - * - {@link columns} - */ - get totalFrames() { return this.fields1[9].value as number } - set totalFrames(value) { this.fields1[9].value = value } - - /** - * Controls whether or not the UV of the trail should be attached to the node - * or not. If it is attached, the texture will slide along the segments to - * follow the source wherever it moves, as if it was a flag attached to a - * pole. If it is not attached, the texture will stay where it was when the - * segment was created, like a skid mark on a road where the road is the - * segments and the mark is the texture, it wouldn't follow the car/node. - */ - get attachedUV() { return this.fields1[10].value as boolean } - set attachedUV(value) { this.fields1[10].value = value } - - /** - * Negative values will make the particle draw in front of objects closer to - * the camera, while positive values will make it draw behind objects farther - * away from the camera. - * - * {@link ActionType.BillboardEx BillboardEx} has a - * {@link BillboardExParams.depthOffset property} that works the - * same way, but reversed. Since that property was discovered before this - * field, this field was given the "negative" name. - */ - get negativeDepthOffset() { return this.fields2[26].value as number } - set negativeDepthOffset(value) { this.fields2[26].value = value } - - /** - * Controls how dark shaded parts of the particle are. - */ - get shadowDarkness() { return this.fields2[30].value as number } - set shadowDarkness(value) { this.fields2[30].value = value } - - /** - * Specular texture ID. Defaults to 0. - * - * See also: - * - {@link lighting} - * - {@link glossiness} - * - {@link specularity} - */ - get specular() { return this.fields2[33].value as number } - set specular(value) { this.fields2[33].value = value } - - /** - * Controls how sharp the specular highlights are. - * - * See also: - * - {@link lighting} - * - {@link specular} - * - {@link specularity} - */ - get glossiness() { return this.fields2[34].value as number } - set glossiness(value) { this.fields2[34].value = value } - - /** - * Controls how the particles are lit. See {@link LightingMode} for more - * information. - */ - get lighting() { return this.fields2[35].value as LightingMode } - set lighting(value) { this.fields2[35].value = value } - - /** - * Controls how bright the specular highlights are. - * - * See also: - * - {@link lighting} - * - {@link specular} - * - {@link glossiness} - */ - get specularity() { return this.fields2[38].value as number } - set specularity(value) { this.fields2[38].value = value } - - /** - * The ID of the texture for the trail. - * - * **Argument**: {@link PropertyArgument.Constant Constant 0} - */ - get texture() { return this.properties1[0] } - set texture(value) { setPropertyInList(this.properties1, 0, value) } - - /** - * Blend mode. - * - * **Argument**: {@link PropertyArgument.Constant Constant 0} - */ - get blendMode() { return this.properties1[1].valueAt(0) as BlendMode } - set blendMode(value: BlendMode | ScalarProperty) { setPropertyInList(this.properties1, 1, value) } - /** - * Blend mode. - * - * **Argument**: {@link PropertyArgument.Constant Constant 0} - */ - get blendModeProperty() { return this.properties1[1] } - - /** - * The width of the trail. - * - * **Argument**: {@link PropertyArgument.ParticleAge Particle age} - */ - get width() { return this.properties1[2] } - set width(value) { setPropertyInList(this.properties1, 2, value) } - - /** - * Multiplier for {@link width}. - * - * **Argument**: {@link PropertyArgument.EmissionTime Emission time} - */ - get widthMultiplier() { return this.properties1[3] } - set widthMultiplier(value) { setPropertyInList(this.properties1, 3, value) } - - /** - * Color multiplier. - * - * **Argument**: {@link PropertyArgument.ParticleAge Particle age} - */ - get color1() { return this.properties1[6] } - set color1(value) { setPropertyInList(this.properties1, 6, value) } - - /** - * Color multiplier. - * - * **Argument**: {@link PropertyArgument.EmissionTime Emission time} - */ - get color2() { return this.properties1[7] } - set color2(value) { setPropertyInList(this.properties1, 7, value) } - - /** - * Color multiplier. - * - * **Argument**: {@link PropertyArgument.EffectAge Effect age}. - */ - get color3() { return this.properties1[8] } - set color3(value) { setPropertyInList(this.properties1, 8, value) } - - /** - * Parts of the particle with less opacity than this threshold will be - * invisible. The range is 0-255. - * - * **Argument**: {@link PropertyArgument.EffectAge Effect age} - */ - get alphaThreshold() { return this.properties1[10] } - set alphaThreshold(value) { setPropertyInList(this.properties1, 10, value) } - - /** - * The index of the frame to show from the texture atlas. Can be animated - * using a {@link PropertyFunction.Linear linear property} or similar. - * - * Seemingly identical to {@link frameIndexOffset}? The sum of these two - * properties is the actual frame index that gets used. - * - * **Argument**: {@link PropertyArgument.ParticleAge Particle age} - */ - get frameIndex() { return this.properties1[10] } - set frameIndex(value) { setPropertyInList(this.properties1, 10, value) } - - /** - * Seemingly identical to {@link frameIndex}? The sum of these two properties - * is the actual frame index that gets used. - * - * **Argument**: {@link PropertyArgument.ParticleAge Particle age} - */ - get frameIndexOffset() { return this.properties1[11] } - set frameIndexOffset(value) { setPropertyInList(this.properties1, 11, value) } - - /** - * Controls how much of the texture's width is used per segment. If - * {@link attachedUV} is enabled, this instead controls how much of the - * texture's width to use for the entire trail. - * - * **Argument**: {@link PropertyArgument.ParticleAge Particle age} - */ - get textureFraction() { return this.properties1[12] } - set textureFraction(value) { setPropertyInList(this.properties1, 12, value) } - - /** - * Controls how fast the UV coordinates should move horizontally. - * - * **Argument**: {@link PropertyArgument.ParticleAge Particle age} - */ - get speedU() { return this.properties1[13] } - set speedU(value) { setPropertyInList(this.properties1, 13, value) } - - /** - * Controls how much the UV coordinates should be randomly offset by per - * segment. - * - * **Argument**: {@link PropertyArgument.ParticleAge Particle age} - */ - get varianceV() { return this.properties1[14] } - set varianceV(value) { setPropertyInList(this.properties1, 14, value) } - - /** - * Scalar multiplier for the color that does not affect the alpha. - * Effectively a brightness multiplier. - * - * **Argument**: {@link PropertyArgument.EffectAge Effect age} - */ - get rgbMultiplier() { return this.properties2[0] } - set rgbMultiplier(value) { setPropertyInList(this.properties2, 0, value) } - - /** - * Alpha multiplier. - * - * **Argument**: {@link PropertyArgument.EffectAge Effect age} - */ - get alphaMultiplier() { return this.properties2[1] } - set alphaMultiplier(value) { setPropertyInList(this.properties2, 1, value) } - -} - -export interface DistortionParams { - /** - * Controls what type of distortion to apply. See {@link DistortionMode} for - * more details. Defaults to {@link DistortionMode.NormalMap}. - */ - mode?: DistortionMode - /** - * Controls the shape of the particle. See {@link DistortionShape} for more - * information. Defaults to {@link DistortionShape.Rectangle}. - */ - shape?: DistortionShape - /** - * Controls the orientation mode for the particles. See - * {@link OrientationMode} for more information. Defaults to - * {@link OrientationMode.CameraPlane}. - */ - orientation?: OrientationMode - /** - * Texture ID. Defaults to 0 (no texture). - * - * This texture seems to completely hide the distortion effect. It's probably - * best to just leave it at 0 unless you are trying to figure out how to use - * it properly. - */ - texture?: number - /** - * Normal map ID. Defaults to 0 (no texture). - * - * Only used if the distortion {@link mode} is set to something that uses it. - */ - normalMap?: number - /** - * Mask texture ID. This texture is used to control the color and opacity of - * the particle. Defaults to 0 (no texture). - */ - mask?: number - /** - * Each particle will pick a random number between this value and 1, and the - * width of the particle will be multiplied by this number. For example, - * setting this to 0.5 will make the particles randomly thinner, down to half - * width. Setting it to 2 will make them randomly wider, up to double width. - * Defaults to 1. - * - * If {@link uniformScale} is enabled, this also affects the height. - * - * See also: - * - {@link scaleVariationY} - */ - scaleVariationX?: number - /** - * Each particle will pick a random number between this value and 1, and the - * height of the particle will be multiplied by this number. For example, - * setting this to 0.5 will make the particles randomly shorter, down to half - * height. Setting it to 2 will make them randomly taller, up to double - * height. Defaults to 1. - * - * If {@link uniformScale} is enabled, {@link scaleVariationX} also affects - * the height, and this field is ignored. - */ - scaleVariationY?: number - /** - * Each particle will pick a random number between this value and 1, and the - * depth of the particle will be multiplied by this number. For example, - * setting this to 0.5 will make the particles randomly shallower, down to - * half depth. Setting it to 2 will make them randomly deeper, up to double - * depth. Defaults to 1. - * - * If {@link uniformScale} is enabled, {@link scaleVariationX} also affects - * the depth, and this field is ignored. - */ - scaleVariationZ?: number - /** - * If enabled, the particle X scale-related properties and fields will - * control the scale in all axes, and the Y and Z counterparts will be - * ignored. Defaults to false. - * - * See also: - * - {@link sizeX} - * - {@link sizeY} - * - {@link sizeZ} - * - {@link scaleVariationX} - * - {@link scaleVariationY} - * - {@link scaleVariationZ} - */ - uniformScale?: boolean - /** - * Controls the color of the additional bloom effect. The colors of the - * particles will be multiplied with this color to get the final color - * of the bloom effect. Defaults to [1, 1, 1]. - * - * Note: - * - This has no effect if the "Effects Quality" setting is set to "Low". - * - This does not affect the natural bloom effect from high color values. - * - * See also: - * - {@link bloomStrength} - */ - bloomColor?: Vector3 - /** - * Controls the strength of the additional bloom effect. Defaults to 0. - * - * Note: - * - This has no effect if the "Effects Quality" setting is set to "Low". - * - This does not affect the natural bloom effect from high color values. - * - * See also: - * - {@link bloomColor} - */ - bloomStrength?: number - /** - * Minimum view distance. If the particle is closer than this distance from - * the camera, it will be hidden. Can be set to -1 to disable the limit. - * Defaults to -1. - * - * See also: - * - {@link maxDistance} - */ - minDistance?: number - /** - * Maximum view distance. If the particle is farther away than this distance - * from the camera, it will be hidden. Can be set to -1 to disable the limit. - * Defaults to -1. - * - * See also: - * - {@link minDistance} - */ - maxDistance?: number - /** - * Blend mode. Defaults to {@link BlendMode.Normal}. - * - * **Argument**: {@link PropertyArgument.Constant Constant 0} - */ - blendMode?: BlendMode | ScalarProperty - /** - * X position offset. Defaults to 0. - * - * **Argument**: {@link PropertyArgument.ParticleAge Particle age} - */ - offsetX?: ScalarPropertyArg - /** - * Y position offset. Defaults to 0. - * - * **Argument**: {@link PropertyArgument.ParticleAge Particle age} - */ - offsetY?: ScalarPropertyArg - /** - * Z position offset. Defaults to 0. - * - * **Argument**: {@link PropertyArgument.ParticleAge Particle age} - */ - offsetZ?: ScalarPropertyArg - /** - * The width of the particle. - * - * If {@link uniformScale} is enabled, this also controls the height. - * - * Defaults to 1. - * - * **Argument**: {@link PropertyArgument.ParticleAge Particle age} - */ - sizeX?: ScalarPropertyArg - /** - * The height of the particle. - * - * If {@link uniformScale} is enabled, {@link sizeX} also controls the - * height, and this property is ignored. - * - * Defaults to 1. - * - * **Argument**: {@link PropertyArgument.ParticleAge Particle age} - */ - sizeY?: ScalarPropertyArg - /** - * The depth of the particle. - * - * If {@link uniformScale} is enabled, {@link sizeX} also controls the - * depth, and this property is ignored. - * - * Defaults to 1. - * - * **Argument**: {@link PropertyArgument.ParticleAge Particle age} - */ - sizeZ?: ScalarPropertyArg - /** - * Color multiplier. Defaults to [1, 1, 1, 1]. - * - * **Argument**: {@link PropertyArgument.ParticleAge Particle age} - */ - color?: Vector4PropertyArg - /** - * Controls the intensity of the distortion effect. At 0, there is no - * distortion at all. Defaults to 1. - * - * **Argument**: {@link PropertyArgument.ParticleAge Particle age} - */ - intensity?: ScalarPropertyArg - /** - * Controls the speed of the stirring effect in radians per second. Requires - * {@link mode} to be set to {@link DistortionMode.Stir}. Defaults to 1. - * - * **Argument**: {@link PropertyArgument.ParticleAge Particle age} - */ - stirSpeed?: ScalarPropertyArg - /** - * The distortion effect is only applied to an ellipse inside the particle. - * This property controls how large this ellipse is. At 1, it inscribes the - * particle's rectangle. At values greater than 1, it is the same size as 1, - * but there might be strange artifacts around the edges of the distortion. - * Defaults to 1. - * - * **Argument**: {@link PropertyArgument.ParticleAge Particle age} - */ - radius?: ScalarPropertyArg - /** - * Horizontal offset for the {@link normalMap normal map}. Defaults to 0. - * - * **Argument**: {@link PropertyArgument.Constant Constant 0} - */ - normalMapOffsetU?: ScalarPropertyArg - /** - * Vertical offset for the {@link normalMap normal map}. Defaults to 0. - * - * **Argument**: {@link PropertyArgument.Constant Constant 0} - */ - normalMapOffsetV?: ScalarPropertyArg - /** - * Horizontal offset speed for the {@link normalMap normal map}. Defaults to - * 0. - * - * **Argument**: {@link PropertyArgument.ParticleAge Particle age} - */ - normalMapSpeedU?: ScalarPropertyArg - /** - * Vertical offset speed for the {@link normalMap normal map}. Defaults to 0. - * - * **Argument**: {@link PropertyArgument.ParticleAge Particle age} - */ - normalMapSpeedV?: ScalarPropertyArg - /** - * Scalar multiplier for the color that does not affect the alpha. - * Effectively a brightness multiplier. Defaults to 1. - * - * **Argument**: {@link PropertyArgument.EffectAge Effect age} - */ - rgbMultiplier?: ScalarPropertyArg - /** - * Alpha multiplier. Defaults to 1. - * - * **Argument**: {@link PropertyArgument.EffectAge Effect age} - */ - alphaMultiplier?: ScalarPropertyArg -} -/** - * A particle that distorts anything seen through it. - * - * Note: This particle is not visible if the "Effects" setting is set to "Low". - */ -class Distortion extends CommonFields2Action { - - constructor({ - mode = DistortionMode.NormalMap, - shape = DistortionShape.Rectangle, - orientation = OrientationMode.CameraPlane, - texture = 0, - normalMap = 0, - mask = 0, - scaleVariationX = 1, - scaleVariationY = 1, - scaleVariationZ = 1, - uniformScale = false, - bloomColor = [1, 1, 1], - bloomStrength = 0, - minDistance = -1, - maxDistance = -1, - blendMode = BlendMode.Normal, - offsetX = 0, - offsetY = 0, - offsetZ = 0, - sizeX = 1, - sizeY = 1, - sizeZ = 1, - color = [1, 1, 1, 1], - intensity = 1, - stirSpeed = 1, - radius = 1, - normalMapOffsetU = 0, - normalMapOffsetV = 0, - normalMapSpeedU = 0, - normalMapSpeedV = 0, - rgbMultiplier = 1, - alphaMultiplier = 1, - }: DistortionParams = {}) { - super(ActionType.Distortion, [ - new IntField(mode), - new IntField(shape), - new IntField(orientation), - new IntField(texture), - new IntField(normalMap), - new IntField(mask), - new FloatField(scaleVariationX), - new FloatField(scaleVariationY), - new FloatField(scaleVariationZ), - new BoolField(uniformScale), - new IntField(-2), - new IntField(0), - new IntField(1), - new IntField(1), - ], [ - new IntField(0), - new IntField(0), - new IntField(8), - new FloatField(1), - new IntField(0), - new FloatField(bloomColor[0]), - new FloatField(bloomColor[1]), - new FloatField(bloomColor[2]), - new FloatField(bloomStrength), - new IntField(0), - new IntField(0), - new IntField(0), - new IntField(0), - new IntField(0), - new FloatField(-1), - new FloatField(-1), - new FloatField(-1), - new FloatField(-1), - new FloatField(minDistance), - new FloatField(maxDistance), - new IntField(1), - new IntField(0), - new IntField(0), - new IntField(0), - new FloatField(0), - new IntField(0), - new IntField(1), - new IntField(0), - new IntField(0), - new IntField(0), - new IntField(0), - new IntField(0), - new IntField(0), - new IntField(0), - new IntField(0), - new IntField(-1), - new IntField(-2), - new IntField(0), - new IntField(0), - ], [ - scalarFromArg(blendMode), - scalarFromArg(offsetX), - scalarFromArg(offsetY), - scalarFromArg(offsetZ), - scalarFromArg(sizeX), - scalarFromArg(sizeY), - scalarFromArg(sizeZ), - vectorFromArg(color), - new ConstantProperty(1, 1, 1, 1), - scalarFromArg(intensity), - new ConstantProperty(0), - scalarFromArg(stirSpeed), - scalarFromArg(radius), - scalarFromArg(normalMapOffsetU), - scalarFromArg(normalMapOffsetV), - scalarFromArg(normalMapSpeedU), - scalarFromArg(normalMapSpeedV), - ], [ - scalarFromArg(rgbMultiplier), - scalarFromArg(alphaMultiplier), - new ConstantProperty(0), - new ConstantProperty(1, 1, 1, 1), - new ConstantProperty(1, 1, 1, 1), - new ConstantProperty(1, 1, 1, 1), - new ConstantProperty(0), - new ConstantProperty(1), - new ConstantProperty(1), - ]) - } - - /** - * Controls what type of distortion to apply. See {@link DistortionMode} for - * more details. - */ - get mode() { return this.fields1[0].value as DistortionMode } - set mode(value) { this.fields1[0].value = value } - - /** - * Controls the shape of the particle. See {@link DistortionShape} for more - * information. - */ - get shape() { return this.fields1[1].value as DistortionShape } - set shape(value) { this.fields1[1].value = value } - - /** - * Controls the orientation mode for the particles. See - * {@link OrientationMode} for more information. - */ - get orientation() { return this.fields1[2].value as OrientationMode } - set orientation(value) { this.fields1[2].value = value } - - /** - * Texture ID. - * - * This texture seems to completely hide the distortion effect. It's probably - * best to just leave it at 0 unless you are trying to figure out how to use - * it properly. - */ - get texture() { return this.fields1[3].value as number } - set texture(value) { this.fields1[3].value = value } - - /** - * Normal map ID. - * - * Only used if the distortion {@link mode} is set to something that uses it. - */ - get normalMap() { return this.fields1[4].value as number } - set normalMap(value) { this.fields1[4].value = value } - - /** - * Mask texture ID. This texture is used to control the color and opacity of - * the particle. - */ - get mask() { return this.fields1[5].value as number } - set mask(value) { this.fields1[5].value = value } - - /** - * Each particle will pick a random number between this value and 1, and the - * width of the particle will be multiplied by this number. For example, - * setting this to 0.5 will make the particles randomly thinner, down to half - * width. Setting it to 2 will make them randomly wider, up to double width. - * - * If {@link uniformScale} is enabled, this also affects the height. - * - * See also: - * - {@link scaleVariationY} - */ - get scaleVariationX() { return this.fields1[6].value as number } - set scaleVariationX(value) { this.fields1[6].value = value } - - /** - * Each particle will pick a random number between this value and 1, and the - * height of the particle will be multiplied by this number. For example, - * setting this to 0.5 will make the particles randomly shorter, down to half - * height. Setting it to 2 will make them randomly taller, up to double - * height. - * - * If {@link uniformScale} is enabled, {@link scaleVariationX} also affects - * the height, and this field is ignored. - */ - get scaleVariationY() { return this.fields1[7].value as number } - set scaleVariationY(value) { this.fields1[7].value = value } - - /** - * Each particle will pick a random number between this value and 1, and the - * depth of the particle will be multiplied by this number. For example, - * setting this to 0.5 will make the particles randomly shallower, down to - * half depth. Setting it to 2 will make them randomly deeper, up to double - * depth. - * - * If {@link uniformScale} is enabled, {@link scaleVariationX} also affects - * the depth, and this field is ignored. - */ - get scaleVariationZ() { return this.fields1[8].value as number } - set scaleVariationZ(value) { this.fields1[8].value = value } - - /** - * If enabled, the particle X scale-related properties and fields will - * control the scale in all axes, and the Y and Z counterparts will be - * ignored. - * - * See also: - * - {@link sizeX} - * - {@link sizeY} - * - {@link sizeZ} - * - {@link scaleVariationX} - * - {@link scaleVariationY} - * - {@link scaleVariationZ} - */ - get uniformScale() { return this.fields1[9].value as boolean } - set uniformScale(value) { this.fields1[9].value = value } - - /** - * Blend mode. See {@link BlendMode} for more information. - * - * **Argument**: {@link PropertyArgument.Constant Constant 0} - */ - get blendMode() { return this.properties1[0].valueAt(0) as BlendMode } - set blendMode(value: BlendMode | ScalarProperty) { setPropertyInList(this.properties1, 0, value) } - /** - * Blend mode. See {@link BlendMode} for more information. - * - * **Argument**: {@link PropertyArgument.Constant Constant 0} - */ - get blendModeProperty() { return this.properties1[0] } - - /** - * X position offset. - * - * **Argument**: {@link PropertyArgument.ParticleAge Particle age} - */ - get offsetX() { return this.properties1[1] } - set offsetX(value) { setPropertyInList(this.properties1, 1, value) } - - /** - * Y position offset. - * - * **Argument**: {@link PropertyArgument.ParticleAge Particle age} - */ - get offsetY() { return this.properties1[2] } - set offsetY(value) { setPropertyInList(this.properties1, 2, value) } - - /** - * Z position offset. - * - * **Argument**: {@link PropertyArgument.ParticleAge Particle age} - */ - get offsetZ() { return this.properties1[3] } - set offsetZ(value) { setPropertyInList(this.properties1, 3, value) } - - /** - * The width of the particle. - * - * If {@link uniformScale} is enabled, this also controls the height. - * - * **Argument**: {@link PropertyArgument.ParticleAge Particle age} - */ - get sizeX() { return this.properties1[4] } - set sizeX(value) { setPropertyInList(this.properties1, 4, value) } - - /** - * The height of the particle. - * - * If {@link uniformScale} is enabled, {@link sizeX} also controls the - * height, and this property is ignored. - * - * **Argument**: {@link PropertyArgument.ParticleAge Particle age} - */ - get sizeY() { return this.properties1[5] } - set sizeY(value) { setPropertyInList(this.properties1, 5, value) } - - /** - * The depth of the particle. - * - * If {@link uniformScale} is enabled, {@link sizeX} also controls the - * depth, and this property is ignored. - * - * **Argument**: {@link PropertyArgument.ParticleAge Particle age} - */ - get sizeZ() { return this.properties1[6] } - set sizeZ(value) { setPropertyInList(this.properties1, 6, value) } - - /** - * Color multiplier. - * - * **Argument**: {@link PropertyArgument.ParticleAge Particle age} - */ - get color() { return this.properties1[7] } - set color(value) { setPropertyInList(this.properties1, 7, value) } - - /** - * Controls the intensity of the distortion effect. At 0, there is no - * distortion at all. - * - * **Argument**: {@link PropertyArgument.ParticleAge Particle age} - */ - get intensity() { return this.properties1[9] } - set intensity(value) { setPropertyInList(this.properties1, 9, value) } - - /** - * Controls the speed of the stirring effect in radians per second. Requires - * {@link mode} to be set to {@link DistortionMode.Stir}. - * - * **Argument**: {@link PropertyArgument.ParticleAge Particle age} - */ - get stirSpeed() { return this.properties1[11] } - set stirSpeed(value) { setPropertyInList(this.properties1, 11, value) } - - /** - * The distortion effect is only applied to an ellipse inside the particle. - * This property controls how large this ellipse is. At 1, it inscribes the - * particle's rectangle. At values greater than 1, it is the same size as 1, - * but there might be strange artifacts around the edges of the distortion. - * - * **Argument**: {@link PropertyArgument.ParticleAge Particle age} - */ - get radius() { return this.properties1[12] } - set radius(value) { setPropertyInList(this.properties1, 12, value) } - - /** - * Horizontal offset for the {@link normalMap normal map}. - * - * **Argument**: {@link PropertyArgument.Constant Constant 0} - */ - get normalMapOffsetU() { return this.properties1[13] } - set normalMapOffsetU(value) { setPropertyInList(this.properties1, 13, value) } - - /** - * Vertical offset for the {@link normalMap normal map}. - * - * **Argument**: {@link PropertyArgument.Constant Constant 0} - */ - get normalMapOffsetV() { return this.properties1[14] } - set normalMapOffsetV(value) { setPropertyInList(this.properties1, 14, value) } - - /** - * Horizontal offset speed for the {@link normalMap normal map}. - * - * **Argument**: {@link PropertyArgument.ParticleAge Particle age} - */ - get normalMapSpeedU() { return this.properties1[15] } - set normalMapSpeedU(value) { setPropertyInList(this.properties1, 15, value) } - - /** - * Vertical offset speed for the {@link normalMap normal map}. - * - * **Argument**: {@link PropertyArgument.ParticleAge Particle age} - */ - get normalMapSpeedV() { return this.properties1[16] } - set normalMapSpeedV(value) { setPropertyInList(this.properties1, 16, value) } - - /** - * Scalar multiplier for the color that does not affect the alpha. - * Effectively a brightness multiplier. - * - * **Argument**: {@link PropertyArgument.EffectAge Effect age} - */ - get rgbMultiplier() { return this.properties2[0] } - set rgbMultiplier(value) { setPropertyInList(this.properties2, 0, value) } - - /** - * Alpha multiplier. - * - * **Argument**: {@link PropertyArgument.EffectAge Effect age} - */ - get alphaMultiplier() { return this.properties2[1] } - set alphaMultiplier(value) { setPropertyInList(this.properties2, 1, value) } - -} - -export interface RadialBlurParams { - /** - * If enabled, the particle width-related properties and fields will control - * both the width and height of the particles, and the height counterparts - * will be ignored. Defaults to false. - * - * See also: - * - {@link width} - * - {@link height} - */ - uniformScale?: boolean - /** - * Controls how many times to apply the effect. Higher values can have a - * significant impact on performance. Defaults to 1. - */ - iterations?: number - /** - * Controls the color of the additional bloom effect. The colors of the - * particles will be multiplied with this color to get the final color - * of the bloom effect. Defaults to [1, 1, 1]. - * - * Note: - * - This has no effect if the "Effects Quality" setting is set to "Low". - * - This does not affect the natural bloom effect from high color values. - * - * See also: - * - {@link bloomStrength} - */ - bloomColor?: Vector3 - /** - * Controls the strength of the additional bloom effect. Defaults to 0. - * - * Note: - * - This has no effect if the "Effects Quality" setting is set to "Low". - * - This does not affect the natural bloom effect from high color values. - * - * See also: - * - {@link bloomColor} - */ - bloomStrength?: number - /** - * Minimum view distance. If the particle is closer than this distance from - * the camera, it will be hidden. Can be set to -1 to disable the limit. - * Defaults to -1. - * - * See also: - * - {@link maxDistance} - */ - minDistance?: number - /** - * Maximum view distance. If the particle is farther away than this distance - * from the camera, it will be hidden. Can be set to -1 to disable the limit. - * Defaults to -1. - * - * See also: - * - {@link minDistance} - */ - maxDistance?: number - /** - * Blend mode. Defaults to {@link BlendMode.Normal}. - * - * **Argument**: {@link PropertyArgument.Constant Constant 0} - */ - blendMode?: BlendMode | ScalarProperty - /** - * Mask texture ID. This texture is used to control the opacity of the - * particle. Defaults to 1. - * - * **Argument**: {@link PropertyArgument.Constant Constant 0} - */ - mask?: ScalarPropertyArg - /** - * X position offset. Defaults to 0. - * - * **Argument**: {@link PropertyArgument.ParticleAge Particle age} - */ - offsetX?: ScalarPropertyArg - /** - * Y position offset. Defaults to 0. - * - * **Argument**: {@link PropertyArgument.ParticleAge Particle age} - */ - offsetY?: ScalarPropertyArg - /** - * Z position offset. Defaults to 0. - * - * **Argument**: {@link PropertyArgument.ParticleAge Particle age} - */ - offsetZ?: ScalarPropertyArg - /** - * The width of the particle. - * - * If {@link uniformScale} is enabled, this also controls the height. - * - * Defaults to 1. - * - * **Argument**: {@link PropertyArgument.ParticleAge Particle age} - */ - width?: ScalarPropertyArg - /** - * The height of the particle. - * - * If {@link uniformScale} is enabled, {@link width} also controls the - * height, and this property is ignored. - * - * Defaults to 1. - * - * **Argument**: {@link PropertyArgument.ParticleAge Particle age} - */ - height?: ScalarPropertyArg - /** - * Color multiplier. Defaults to [1, 1, 1, 1]. - * - * **Argument**: {@link PropertyArgument.ParticleAge Particle age} - */ - color?: Vector4PropertyArg - /** - * Controls the amount of blur to apply. Values greater than 1 may appear - * glitchy. Defaults to 0.5. - * - * **Argument**: {@link PropertyArgument.ParticleAge Particle age} - */ - blurRadius?: ScalarPropertyArg - /** - * Scalar multiplier for the color that does not affect the alpha. - * Effectively a brightness multiplier. Defaults to 1. - * - * **Argument**: {@link PropertyArgument.EffectAge Effect age} - */ - rgbMultiplier?: ScalarPropertyArg - /** - * Alpha multiplier. Defaults to 1. - * - * **Argument**: {@link PropertyArgument.EffectAge Effect age} - */ - alphaMultiplier?: ScalarPropertyArg -} -/** - * A particle that applies a radial blur to anything seen through it. - * - * Note: This particle is not visible if the "Effects" setting is set to "Low". - */ -class RadialBlur extends CommonFields2Action { - - constructor({ - uniformScale = false, - iterations = 1, - bloomColor = [1, 1, 1], - bloomStrength = 0, - minDistance = -1, - maxDistance = -1, - blendMode = BlendMode.Normal, - mask = 1, - offsetX = 0, - offsetY = 0, - offsetZ = 0, - width = 1, - height = 1, - color = [1, 1, 1, 1], - blurRadius = 0.5, - rgbMultiplier = 1, - alphaMultiplier = 1, - }: RadialBlurParams = {}) { - super(ActionType.RadialBlur, [ - /* 0 */ new BoolField(uniformScale), - /* 1 */ new IntField(iterations), - /* 2 */ new IntField(0), - /* 3 */ new IntField(1), - /* 4 */ new IntField(1), - ], [ - /* 0 */ new IntField(0), - /* 1 */ new IntField(0), - /* 2 */ new IntField(8), - /* 3 */ new FloatField(1), - /* 4 */ new IntField(0), - /* 5 */ new FloatField(bloomColor[0]), - /* 6 */ new FloatField(bloomColor[1]), - /* 7 */ new FloatField(bloomColor[2]), - /* 8 */ new FloatField(bloomStrength), - /* 9 */ new IntField(0), - /* 10 */ new IntField(0), - /* 11 */ new IntField(0), - /* 12 */ new IntField(0), - /* 13 */ new IntField(0), - /* 14 */ new FloatField(-1), - /* 15 */ new FloatField(-1), - /* 16 */ new FloatField(-1), - /* 17 */ new FloatField(-1), - /* 18 */ new FloatField(minDistance), - /* 19 */ new FloatField(maxDistance), - /* 20 */ new FloatField(0.5), - /* 21 */ new IntField(1), - /* 22 */ new IntField(0), - /* 23 */ new IntField(0), - /* 24 */ new IntField(0), - /* 25 */ new FloatField(1), // depth blend 1 - /* 26 */ new FloatField(0), // depth blend 2 - /* 27 */ new IntField(1), - /* 28 */ new IntField(0), - /* 29 */ new FloatField(0), - /* 30 */ new FloatField(0), - ], [ - /* 0 */ scalarFromArg(blendMode), - /* 1 */ scalarFromArg(mask), - /* 2 */ scalarFromArg(offsetX), - /* 3 */ scalarFromArg(offsetY), - /* 4 */ scalarFromArg(offsetZ), - /* 5 */ scalarFromArg(width), - /* 6 */ scalarFromArg(height), - /* 7 */ vectorFromArg(color), - /* 8 */ new ConstantProperty(1, 1, 1, 1), - /* 9 */ scalarFromArg(blurRadius), - ], [ - /* 0 */ scalarFromArg(rgbMultiplier), - /* 1 */ scalarFromArg(alphaMultiplier), - /* 2 */ new ConstantProperty(0), - /* 3 */ new ConstantProperty(1, 1, 1, 1), - /* 4 */ new ConstantProperty(1, 1, 1, 1), - /* 5 */ new ConstantProperty(1, 1, 1, 1), - /* 6 */ new ConstantProperty(0), - ]) - } - - /** - * If enabled, the particle width-related properties and fields will control - * both the width and height of the particles, and the height counterparts - * will be ignored. - * - * See also: - * - {@link width} - * - {@link height} - */ - get uniformScale() { return this.fields1[0].value as number } - set uniformScale(value) { this.fields1[0].value = value } - - /** - * Controls how many times to apply the effect. Higher values can have a - * significant impact on performance. Defaults to 1. - */ - get iterations() { return this.fields1[1].value as number } - set iterations(value) { this.fields1[1].value = value } - - /** - * Blend mode. See {@link BlendMode} for more information. - * - * **Argument**: {@link PropertyArgument.Constant Constant 0} - */ - get blendMode() { return this.properties1[0].valueAt(0) as BlendMode } - set blendMode(value: BlendMode | ScalarProperty) { setPropertyInList(this.properties1, 0, value) } - /** - * Blend mode. See {@link BlendMode} for more information. - * - * **Argument**: {@link PropertyArgument.Constant Constant 0} - */ - get blendModeProperty() { return this.properties1[0] } - - /** - * Mask texture ID. This texture is used to control the opacity of the - * particle. - * - * **Argument**: {@link PropertyArgument.Constant Constant 0} - */ - get mask() { return this.properties1[1].valueAt(0) } - set mask(value) { setPropertyInList(this.properties1, 1, value) } - /** - * Mask texture ID. This texture is used to control the opacity of the - * particle. - * - * **Argument**: {@link PropertyArgument.Constant Constant 0} - */ - get maskProperty() { return this.properties1[1] } - - /** - * X position offset. - * - * **Argument**: {@link PropertyArgument.ParticleAge Particle age} - */ - get offsetX() { return this.properties1[2] } - set offsetX(value) { setPropertyInList(this.properties1, 2, value) } - - /** - * Y position offset. - * - * **Argument**: {@link PropertyArgument.ParticleAge Particle age} - */ - get offsetY() { return this.properties1[3] } - set offsetY(value) { setPropertyInList(this.properties1, 3, value) } - - /** - * Z position offset. - * - * **Argument**: {@link PropertyArgument.ParticleAge Particle age} - */ - get offsetZ() { return this.properties1[4] } - set offsetZ(value) { setPropertyInList(this.properties1, 4, value) } - - /** - * The width of the particle. - * - * If {@link uniformScale} is enabled, this also controls the height. - * - * **Argument**: {@link PropertyArgument.ParticleAge Particle age} - */ - get width() { return this.properties1[5] } - set width(value) { setPropertyInList(this.properties1, 5, value) } - - /** - * The height of the particle. - * - * If {@link uniformScale} is enabled, {@link width} also controls the - * height, and this property is ignored. - * - * **Argument**: {@link PropertyArgument.ParticleAge Particle age} - */ - get height() { return this.properties1[6] } - set height(value) { setPropertyInList(this.properties1, 6, value) } - - /** - * Color multiplier. - * - * **Argument**: {@link PropertyArgument.ParticleAge Particle age} - */ - get color() { return this.properties1[7] } - set color(value) { setPropertyInList(this.properties1, 7, value) } - - /** - * Controls the amount of blur to apply. Values greater than 1 may appear - * glitchy. - * - * **Argument**: {@link PropertyArgument.ParticleAge Particle age} - */ - get blurRadius() { return this.properties1[9] } - set blurRadius(value) { setPropertyInList(this.properties1, 9, value) } - - /** - * Scalar multiplier for the color that does not affect the alpha. - * Effectively a brightness multiplier. - * - * **Argument**: {@link PropertyArgument.EffectAge Effect age} - */ - get rgbMultiplier() { return this.properties2[0] } - set rgbMultiplier(value) { setPropertyInList(this.properties2, 0, value) } - - /** - * Alpha multiplier. - * - * **Argument**: {@link PropertyArgument.EffectAge Effect age} - */ - get alphaMultiplier() { return this.properties2[1] } - set alphaMultiplier(value) { setPropertyInList(this.properties2, 1, value) } - -} - -export interface PointLightParams { - /** - * Controls the diffuse color of the light. - * - * If {@link separateSpecular} is disabled, this also controls the specular - * color of the light. - * - * Defaults to [1, 1, 1, 1]. - * - * **Argument**: {@link PropertyArgument.EffectAge Effect age} - */ - diffuseColor?: Vector4PropertyArg - /** - * Controls the specular color of the light. - * - * If {@link separateSpecular} is disabled, this property is ignored and - * {@link diffuseColor} controls both the diffuse as well as the specular - * color. - * - * Defaults to [1, 1, 1, 1]. - * - * **Argument**: {@link PropertyArgument.EffectAge Effect age} - */ - specularColor?: Vector4PropertyArg - /** - * The maximum distance that the light may travel from the source, and the - * radius of the sphere in which other effects caused by the light source - * (for example {@link volumeDensity} and its related fields) may act. Defaults - * to 10. - */ - radius?: ScalarPropertyArg - /** - * A scalar multiplier for the {@link diffuseColor diffuse color}. Good for - * easily adjusting the brightness of the light without changing the color. - * - * If {@link separateSpecular} is disabled, this also affects the specular - * color of the light. - * - * Defaults to 1. - * - * **Argument**: {@link PropertyArgument.EffectAge Effect age} - */ - diffuseMultiplier?: ScalarPropertyArg - /** - * A scalar multiplier for the {@link specularColor specular color}. - * - * If {@link separateSpecular} is disabled, this property is ignored. - * - * Defaults to 1. - * - * **Argument**: {@link PropertyArgument.EffectAge Effect age} - */ - specularMultiplier?: ScalarPropertyArg - /** - * Toggles the jitter and flicker animations for the light. Defaults to - * false. - * - * See also: - * - {@link jitterAcceleration} - * - {@link jitter} - * - {@link flickerIntervalMin} - * - {@link flickerIntervalMax} - * - {@link flickerBrightness} - */ - jitterAndFlicker?: boolean - /** - * Controls the acceleration of the jittering. Defaults to 1. - * - * See also: - * - {@link jitterAndFlicker} - * - {@link jitter} - */ - jitterAcceleration?: number - /** - * Controls how much the light should move around randomly. Defaults to - * [0, 0, 0]. - * - * See also: - * - {@link jitterAndFlicker} - * - {@link jitterAcceleration} - */ - jitter?: Vector3 - /** - * Controls the minimum interval for flickering. Defaults to 0. - * - * See also: - * - {@link jitterAndFlicker} - * - {@link flickerIntervalMax} - * - {@link flickerBrightness} - */ - flickerIntervalMin?: number - /** - * Controls the maximum interval for flickering. Defaults to 1. - * - * See also: - * - {@link jitterAndFlicker} - * - {@link flickerIntervalMin} - * - {@link flickerBrightness} - */ - flickerIntervalMax?: number - /** - * Brightness multiplier for the light when it flickers. Defaults to 0.5. - * - * See also: - * - {@link jitterAndFlicker} - * - {@link flickerIntervalMin} - * - {@link flickerIntervalMax} - */ - flickerBrightness?: number - /** - * Controls if the light should have shadows or not. - * - * Note: Objects also have a setting for casting shadows, and both must be - * enabled for an object to cast shadows from this light source. - * - * Defaults to false. - */ - shadows?: boolean - /** - * When enabled, this allows other properties and fields of the action to - * control the specular color independently of the diffuse color. When - * disabled, the diffuse counterpart of the properties or fields will affect - * both the diffuse and specular color. Defaults to false. - * - * See also: - * - {@link diffuseColor} - * - {@link specularColor} - * - {@link diffuseMultiplier} - * - {@link specularMultiplier} - */ - separateSpecular?: boolean - /** - * The number of seconds the light takes to fade to nothing after being - * destroyed. - * - * Due to how the field this represents works, the time will be rounded to - * the nearest multiple of 1/30s. The field itself is an integer with 1/30s - * as the units. - * - * Defaults to 0. - */ - fadeOutTime?: number - /** - * Controls how dark shadows from this light source are. At 0, the shadows - * will be entirely invisible. Defaults to 1. - */ - shadowDarkness?: number - /** - * Controls the density of some sort of fake fog in the volume hit by the - * light. The fog does not affect the actual light produced by the source and - * is not affected by shadows. Defaults to 0. - * - * See also: - * - {@link phaseFunction} - * - {@link asymmetryParam} - */ - volumeDensity?: number - /** - * Controls whether or not {@link asymmetryParam} affects the fake fog from - * {@link volumeDensity}. Defaults to true. - */ - phaseFunction?: boolean - /** - * Controls how the fake fog from {@link volumeDensity} scatters the light. This - * value is ignored if {@link phaseFunction} is disabled, and the fog will - * scatter the light equally in all directions. - * - * - At 0, the light is scattered equally in every direction. - * - As the value approaches 1, the light is scattered more and more forward, - * in the same direction as the light was already traveling. This means that - * the fake fog will be less visible from the side or behind, and more - * visible from in front of the light. - * - At 1, the fog will not scatter the light at all, so it will be entirely - * invisible. - * - Values above 1 produce unnatural-looking effects where the light darkens - * the fog instead. - * - * Defaults to 0.75. - */ - asymmetryParam?: number - /** - * Controls the falloff exponent of the light. - * - * Note: This is possibly something else, but the behavior is pretty similar - * to a falloff exponent in a few ways. - * - * Defaults to 1. - */ - falloffExponent?: number -} -/** - * Point light source. - */ -class PointLight extends Action { - - constructor({ - diffuseColor = [1, 1, 1, 1], - specularColor = diffuseColor instanceof Property ? diffuseColor.clone() : diffuseColor.slice() as Vector4, - radius = 10, - diffuseMultiplier = 1, - specularMultiplier = 1, - jitterAndFlicker = false, - jitterAcceleration = 1, - jitter = [0, 0, 0], - flickerIntervalMin = 0, - flickerIntervalMax = 1, - flickerBrightness = 0.5, - shadows = false, - separateSpecular = false, - fadeOutTime = 0, - shadowDarkness = 1, - volumeDensity = 0, - phaseFunction = true, - asymmetryParam = 0.5, - falloffExponent = 1, - }: PointLightParams = {}) { - super(ActionType.PointLight, [ - /* 0 */ new IntField(0), - /* 1 */ new FloatField(0), - ], [ // Fields 2 - /* 0 */ new IntField(0), - /* 1 */ new BoolField(jitterAndFlicker), - /* 2 */ new FloatField(jitterAcceleration), - /* 3 */ new FloatField(0), - /* 4 */ new FloatField(jitter[0]), - /* 5 */ new FloatField(jitter[1]), - /* 6 */ new FloatField(jitter[2]), - /* 7 */ new FloatField(flickerIntervalMin), - /* 8 */ new FloatField(flickerIntervalMax), - /* 9 */ new FloatField(flickerBrightness), - /* 10 */ new BoolField(shadows), - /* 11 */ new BoolField(separateSpecular), - /* 12 */ new IntField(Math.round(fadeOutTime * 30)), - /* 13 */ new FloatField(shadowDarkness), - /* 14 */ new BoolField(false), - /* 15 */ new IntField(2), - /* 16 */ new BoolField(false), - /* 17 */ new FloatField(0.5), - /* 18 */ new FloatField(0.5), - /* 19 */ new FloatField(0.5), - /* 20 */ new IntField(1), - /* 21 */ new IntField(100), - /* 22 */ new IntField(1), - /* 23 */ new IntField(1), - /* 24 */ new FloatField(volumeDensity), - /* 25 */ new FloatField(0), - /* 26 */ new BoolField(phaseFunction), - /* 27 */ new FloatField(asymmetryParam), - /* 28 */ new FloatField(falloffExponent), - /* 29 */ new IntField(1), - /* 30 */ new FloatField(1), - /* 31 */ new IntField(1), - /* 32 */ new IntField(1), - ], [ // Properties1 - /* 0 */ vectorFromArg(diffuseColor), - /* 1 */ vectorFromArg(specularColor), - /* 2 */ scalarFromArg(radius), - /* 3 */ new ConstantProperty(0), - /* 4 */ new ConstantProperty(0), - /* 5 */ new ConstantProperty(0), - /* 6 */ new ConstantProperty(0), - /* 7 */ new ConstantProperty(10), - /* 8 */ new ConstantProperty(10), - /* 9 */ new ConstantProperty(10), - ], [ // Properties2 - /* 0 */ new ConstantProperty(1), - /* 1 */ new ConstantProperty(1), - /* 2 */ new ConstantProperty(1), - /* 3 */ scalarFromArg(diffuseMultiplier), - /* 4 */ scalarFromArg(specularMultiplier), - ]) - } - - /** - * Controls the diffuse color of the light. - * - * If {@link separateSpecular} is disabled, this also controls the specular - * color of the light. - * - * **Argument**: {@link PropertyArgument.EffectAge Effect age} - */ - get diffuseColor() { return this.properties1[0] } - set diffuseColor(value) { setPropertyInList(this.properties1, 0, value) } - - /** - * Controls the specular color of the light. - * - * If {@link separateSpecular} is disabled, this property is ignored and - * {@link diffuseColor} controls both the diffuse as well as the specular - * color. - * - * **Argument**: {@link PropertyArgument.EffectAge Effect age} - */ - get specularColor() { return this.properties1[1] } - set specularColor(value) { setPropertyInList(this.properties1, 1, value) } - - /** - * The maximum distance that the light may travel from the source, and the - * radius of the sphere in which other effects caused by the light source - * (for example {@link volumeDensity} and its related fields) may act. - */ - get radius() { return this.properties1[2] } - set radius(value) { setPropertyInList(this.properties1, 2, value) } - - /** - * A scalar multiplier for the {@link diffuseColor diffuse color}. Good for - * easily adjusting the brightness of the light without changing the color. - * - * If {@link separateSpecular} is disabled, this also affects the specular - * color of the light. - * - * **Argument**: {@link PropertyArgument.EffectAge Effect age} - */ - get diffuseMultiplier() { return this.properties2[3] } - set diffuseMultiplier(value) { setPropertyInList(this.properties2, 3, value) } - - /** - * A scalar multiplier for the {@link specularColor specular color}. - * - * If {@link separateSpecular} is disabled, this property is ignored. - * - * **Argument**: {@link PropertyArgument.EffectAge Effect age} - */ - get specularMultiplier() { return this.properties1[4] } - set specularMultiplier(value) { setPropertyInList(this.properties2, 4, value) } - - /** - * Toggles the jitter and flicker animations for the light. Defaults to - * false. - * - * See also: - * - {@link jitterAcceleration} - * - {@link jitter} - * - {@link flickerIntervalMin} - * - {@link flickerIntervalMax} - * - {@link flickerBrightness} - */ - get jitterAndFlicker() { return this.fields2[1].value as boolean } - set jitterAndFlicker(value) { this.fields2[1].value = value } - - /** - * Controls the acceleration of the jittering. Defaults to 1. - * - * See also: - * - {@link jitterAndFlicker} - * - {@link jitter} - */ - get jitterAcceleration() { return this.fields2[2].value as number } - set jitterAcceleration(value) { this.fields2[2].value = value } - - /** - * Controls how much the light should move around randomly. - * - * See also: - * - {@link jitterAndFlicker} - * - {@link jitterAcceleration} - */ - get jitter() { return this.fields2.slice(4, 7).map(f => f.value) as Vector3 } - set jitter(value) { for (let i = 2; i >= 0; i--) this.fields2[4 + i].value = value[i] } - - /** - * Controls the minimum interval for flickering. - * - * See also: - * - {@link jitterAndFlicker} - * - {@link flickerIntervalMax} - * - {@link flickerBrightness} - */ - get flickerIntervalMin() { return this.fields2[7].value as number } - set flickerIntervalMin(value) { this.fields2[7].value = value } - - /** - * Controls the maximum interval for flickering. - * - * See also: - * - {@link jitterAndFlicker} - * - {@link flickerIntervalMin} - * - {@link flickerBrightness} - */ - get flickerIntervalMax() { return this.fields2[8].value as number } - set flickerIntervalMax(value) { this.fields2[8].value = value } - - /** - * Brightness multiplier for the light when it flickers. - * - * See also: - * - {@link jitterAndFlicker} - * - {@link flickerIntervalMin} - * - {@link flickerIntervalMax} - */ - get flickerBrightness() { return this.fields2[9].value as number } - set flickerBrightness(value) { this.fields2[9].value = value } - - /** - * Controls if the light should have shadows or not. - * - * Note: Objects also have a setting for casting shadows, and both must be - * enabled for an object to cast shadows from this light source. - */ - get shadows() { return this.fields2[10].value as boolean } - set shadows(value) { this.fields2[10].value = value } - - /** - * When enabled, this allows other properties and fields of the action to - * control the specular color independently of the diffuse color. When - * disabled, the diffuse counterpart of the properties or fields will affect - * both the diffuse and specular color. - * - * See also: - * - {@link diffuseColor} - * - {@link specularColor} - * - {@link diffuseMultiplier} - * - {@link specularMultiplier} - */ - get separateSpecular() { return this.fields2[11].value as boolean } - set separateSpecular(value) { this.fields2[11].value = value } - - /** - * The number of seconds the light takes to fade to nothing after being - * destroyed. - * - * Due to how the field this represents works, the time will be rounded to - * the nearest multiple of 1/30s. The field itself is an integer with 1/30s - * as the units. - */ - get fadeOutTime() { return (this.fields2[12].value as number) / 30 } - set fadeOutTime(value) { this.fields2[12].value = Math.round(value * 30) } - - /** - * Controls how dark shadows from this light source are. At 0, the shadows - * will be entirely invisible. - */ - get shadowDarkness() { return this.fields2[13].value as number } - set shadowDarkness(value) { this.fields2[13].value = value } - - /** - * Controls the density of some sort of fake fog in the volume hit by the - * light. The fog does not affect the actual light produced by the source and - * is not affected by shadows. - * - * See also: - * - {@link phaseFunction} - * - {@link asymmetryParam} - */ - get volumeDensity() { return this.fields2[24].value as number } - set volumeDensity(value) { this.fields2[24].value = value } - - /** - * Controls whether or not {@link asymmetryParam} affects the fake fog from - * {@link volumeDensity}. - */ - get phaseFunction() { return this.fields2[26].value as boolean } - set phaseFunction(value) { this.fields2[26].value = value } - - /** - * Controls how the fake fog from {@link volumeDensity} scatters the light. This - * value is ignored if {@link phaseFunction} is disabled, and the fog will - * scatter the light equally in all directions. - * - * - At 0, the light is scattered equally in every direction. - * - As the value approaches 1, the light is scattered more and more forward, - * in the same direction as the light was already traveling. This means that - * the fake fog will be less visible from the side or behind, and more - * visible from in front of the light. - * - At 1, the fog will not scatter the light at all, so it will be entirely - * invisible. - * - Values above 1 produce unnatural-looking effects where the light darkens - * the fog instead. - */ - get asymmetryParam() { return this.fields2[27].value as number } - set asymmetryParam(value) { this.fields2[27].value = value } - - /** - * Controls the falloff exponent of the light. - * - * Note: This is possibly something else, but the behavior is pretty similar - * to a falloff exponent in a few ways. - */ - get falloffExponent() { return this.fields2[28].value as number } - set falloffExponent(value) { this.fields2[28].value = value } - -} - -/** - * Controls how effective the wind is at pushing the node. - */ -class NodeWindSpeed extends Action { - - constructor( - /** - * The speed in the direction of the wind. Defaults to 0. - * - * **Argument**: {@link PropertyArgument.EffectAge Effect age} - */ - windSpeed: ScalarPropertyArg = 0, - /** - * A multiplier for - * {@link windSpeed the speed in the direction of the wind}. - * Defalts to 1. - * - * **Argument**: {@link PropertyArgument.EffectAge Effect age} - */ - windSpeedMult: ScalarPropertyArg = 1, - /** - * Controls whether the wind should have any effect at all or not. Defaults - * to true. - */ - enabled: boolean = true, - ) { - super(ActionType.NodeWindSpeed, [ - new BoolField(enabled), - ], [], [ - scalarFromArg(windSpeed), - scalarFromArg(windSpeedMult), - ]) - } - - /** - * The speed in the direction of the wind. - * - * **Argument**: {@link PropertyArgument.EffectAge Effect age} - */ - get windSpeed(): ScalarProperty { return this.properties1[0] } - set windSpeed(value: ScalarPropertyArg) { setPropertyInList(this.properties1, 0, value) } - - /** - * A multiplier for - * {@link windSpeed the speed in the direction of the wind}. - * - * **Argument**: {@link PropertyArgument.EffectAge Effect age} - */ - get windSpeedMult(): ScalarProperty { return this.properties1[1] } - set windSpeedMult(value: ScalarPropertyArg) { setPropertyInList(this.properties1, 1, value) } - - /** - * Controls whether the wind should have any effect at all or not. - */ - get enabled() { return this.fields1[0].value as boolean } - set enabled(value) { this.fields1[0].value = value } - - minify(): Action { - if (this.enabled) { - return this - } else { - return new Action - } - } - -} - -/** - * Controls how effective the wind is at pushing the particles emitted from - * the node. - */ -class ParticleWindSpeed extends Action { - - constructor( - /** - * The speed in the direction of the wind. Defaults to 0. - * - * **Argument**: {@link PropertyArgument.EffectAge Effect age} - */ - windSpeed: ScalarPropertyArg = 0, - /** - * A multiplier for - * {@link windSpeed the speed in the direction of the wind}. - * Defalts to 1. - * - * **Argument**: {@link PropertyArgument.EffectAge Effect age} - */ - windSpeedMult: ScalarPropertyArg = 1, - /** - * Controls whether the wind should have any effect at all or not. Defaults - * to true. - */ - enabled: boolean = true, - /** - * Unknown. Fields1, index 1. 0 and 1 seems to be valid values, while all - * other values cause the wind to not affect the particles. Defaults to 0. - */ - unkField1: number = 0 - ) { - super(ActionType.ParticleWindSpeed, [ - new BoolField(enabled), - new IntField(unkField1), - ], [], [ - scalarFromArg(windSpeed), - scalarFromArg(windSpeedMult), - ]) - } - - /** - * The speed in the direction of the wind. - * - * **Argument**: {@link PropertyArgument.EffectAge Effect age} - */ - get windSpeed(): ScalarProperty { return this.properties1[0] } - set windSpeed(value: ScalarPropertyArg) { setPropertyInList(this.properties1, 0, value) } - - /** - * A multiplier for - * {@link windSpeed the speed in the direction of the wind}. - * - * **Argument**: {@link PropertyArgument.EffectAge Effect age} - */ - get windSpeedMult(): ScalarProperty { return this.properties1[1] } - set windSpeedMult(value: ScalarPropertyArg) { setPropertyInList(this.properties1, 1, value) } - - /** - * Controls whether the wind should have any effect at all or not. - */ - get enabled() { return this.fields1[0].value as boolean } - set enabled(value) { this.fields1[0].value = value } - - minify(): Action { - if (this.enabled) { - return this - } else { - return new Action - } - } - -} - -/** - * Controls how effective the wind is at accelerating the node. - */ -class NodeWindAcceleration extends Action { - - constructor( - /** - * The acceleration in the direction of the wind. Defaults to 0. - * - * **Argument**: {@link PropertyArgument.EffectAge Effect age} - */ - windAcceleration: ScalarPropertyArg = 0, - /** - * A multiplier for - * {@link windAcceleration the acceleration in the direction of the wind}. - * Defalts to 1. - * - * **Argument**: {@link PropertyArgument.EffectAge Effect age} - */ - windAccelerationMult: ScalarPropertyArg = 1, - /** - * Controls whether the wind should have any effect at all or not. Defaults - * to true. - */ - enabled: boolean = true, - ) { - super(ActionType.NodeWindAcceleration, [ - new BoolField(enabled), - ], [], [ - scalarFromArg(windAcceleration), - scalarFromArg(windAccelerationMult), - ]) - } - - /** - * The acceleration in the direction of the wind. - * - * **Argument**: {@link PropertyArgument.EffectAge Effect age} - */ - get windAcceleration(): ScalarProperty { return this.properties1[0] } - set windAcceleration(value: ScalarPropertyArg) { setPropertyInList(this.properties1, 0, value) } - - /** - * A multiplier for - * {@link windAcceleration the acceleration in the direction of the wind}. - * - * **Argument**: {@link PropertyArgument.EffectAge Effect age} - */ - get windAccelerationMult(): ScalarProperty { return this.properties1[1] } - set windAccelerationMult(value: ScalarPropertyArg) { setPropertyInList(this.properties1, 1, value) } - - /** - * Controls whether the wind should have any effect at all or not. - */ - get enabled() { return this.fields1[0].value as boolean } - set enabled(value) { this.fields1[0].value = value } - - minify(): Action { - if (this.enabled) { - return this - } else { - return new Action - } - } - -} - -/** - * Controls how effective the wind is at accelerating the particles emitted - * from the node. - * - * Acceleration requires slot 10 to have an action that enables acceleration - * of the particles. - */ -class ParticleWindAcceleration extends Action { - - constructor( - /** - * The acceleration in the direction of the wind. Defaults to 0. - * - * **Argument**: {@link PropertyArgument.EffectAge Effect age} - */ - windAcceleration: ScalarPropertyArg = 0, - /** - * A multiplier for - * {@link windAcceleration the acceleration in the direction of the wind}. - * Defalts to 1. - * - * **Argument**: {@link PropertyArgument.EffectAge Effect age} - */ - windAccelerationMult: ScalarPropertyArg = 1, - /** - * Controls whether the wind should have any effect at all or not. Defaults - * to true. - */ - enabled: boolean = true, - /** - * Unknown. Fields1, index 1. 0 and 1 seems to be valid values, while all - * other values cause the wind to not affect the particles. Defaults to 0. - */ - unkField1: number = 0 - ) { - super(ActionType.ParticleWindAcceleration, [ - new BoolField(enabled), - new IntField(unkField1), - ], [], [ - scalarFromArg(windAcceleration), - scalarFromArg(windAccelerationMult), - ]) - } - - /** - * The acceleration in the direction of the wind. - * - * **Argument**: {@link PropertyArgument.EffectAge Effect age} - */ - get windAcceleration(): ScalarProperty { return this.properties1[0] } - set windAcceleration(value: ScalarPropertyArg) { setPropertyInList(this.properties1, 0, value) } - - /** - * A multiplier for - * {@link windAcceleration the acceleration in the direction of the wind}. - * - * **Argument**: {@link PropertyArgument.EffectAge Effect age} - */ - get windAccelerationMult(): ScalarProperty { return this.properties1[1] } - set windAccelerationMult(value: ScalarPropertyArg) { setPropertyInList(this.properties1, 1, value) } - - /** - * Controls whether the wind should have any effect at all or not. - */ - get enabled() { return this.fields1[0].value as boolean } - set enabled(value) { this.fields1[0].value = value } - - minify(): Action { - if (this.enabled) { - return this - } else { - return new Action - } - } - -} - -export interface WaterInteractionParams { - /** - * The ID for a texture that controls the shape of the interaction. Defaults - * to 50004. - */ - texture?: number - /** - * Controls how deep to push the water, or how intense the ripples caused by - * the interaction are. Defaults to 1. - */ - depth?: number - /** - * Controls the size of the interaction area. Ripples caused by the - * interaction may go outside of the area. Defaults to 1. - */ - scale?: number - /** - * The time it takes for the water to be pushed down to the {@link depth} in - * seconds. Defaults to 0.15. - */ - descent?: number - /** - * The duration of the interaction in seconds. Basically how long to hold the - * water pressed down. Defaults to 0.15. - */ - duration?: number -} -/** - * Simulates an interaction with water, allowing effects to create ripples in - * nearby water. The interaction basically pushes water in a shape controlled - * by a texture down to a given depth and holds it there for a duration before - * releasing it. - */ -class WaterInteraction extends Action { - - constructor({ - texture = 50004, - depth = 1, - scale = 1, - descent = 0.15, - duration = 0.15, - }: WaterInteractionParams = {}) { - super(ActionType.WaterInteraction, [ - new IntField(texture), - new FloatField(depth), - new FloatField(scale), - new FloatField(descent), - new FloatField(duration), - ]) - } - - /** - * The ID for a texture that controls the shape of the interaction. - */ - get texture() { return this.fields1[0].value as number } - set texture(value) { this.fields1[0].value = value } - - /** - * Controls how deep to push the water, or how intense the ripples caused by - * the interaction are. - */ - get depth() { return this.fields1[1].value as number } - set depth(value) { this.fields1[1].value = value } - - /** - * Controls the size of the interaction area. Ripples caused by the - * interaction may go outside of the area. - */ - get scale() { return this.fields1[2].value as number } - set scale(value) { this.fields1[2].value = value } - - /** - * The time it takes for the water to be pushed down to the {@link depth} in - * seconds. - */ - get descent() { return this.fields1[3].value as number } - set descent(value) { this.fields1[3].value = value } - - /** - * The duration of the interaction in seconds. Basically how long to hold the - * water pressed down. - */ - get duration() { return this.fields1[4].value as number } - set duration(value) { this.fields1[4].value = value } - -} - -export interface SpotLightParams { - /** - * Controls the diffuse color of the light. - * - * If {@link separateSpecular} is disabled, this also controls the specular - * color of the light. - * - * Defaults to [1, 1, 1, 1]. - * - * **Argument**: {@link PropertyArgument.EffectAge Effect age} - */ - diffuseColor?: Vector4PropertyArg - /** - * Controls the specular color of the light. - * - * If {@link separateSpecular} is disabled, this property is ignored and - * {@link diffuseColor} controls both the diffuse as well as the specular - * color. - * - * Defaults to [1, 1, 1, 1]. - * - * **Argument**: {@link PropertyArgument.EffectAge Effect age} - */ - specularColor?: Vector4PropertyArg - /** - * A scalar multiplier for the {@link diffuseColor diffuse color}. Good for - * easily adjusting the brightness of the light without changing the color. - * - * If {@link separateSpecular} is disabled, this also affects the specular - * color of the light. - * - * Defaults to 100. - * - * **Argument**: {@link PropertyArgument.EffectAge Effect age} - */ - diffuseMultiplier?: ScalarPropertyArg - /** - * A scalar multiplier for the {@link specularColor specular color}. - * - * If {@link separateSpecular} is disabled, this property is ignored. - * - * Defaults to 100. - * - * **Argument**: {@link PropertyArgument.EffectAge Effect age} - */ - specularMultiplier?: ScalarPropertyArg - /** - * Controls where the light starts in the cone. It bascially "slices off" the - * tip of the cone. If set to 0, it acts as if it is set to 0.5. Defaults to - * 0.01. - * - * **Argument**: {@link PropertyArgument.EffectAge Effect age} - */ - near?: ScalarPropertyArg - /** - * Controls how far away the base of the cone is from the light source. - * Defaults to 50. - * - * **Argument**: {@link PropertyArgument.EffectAge Effect age} - */ - far?: ScalarPropertyArg - /** - * The default value for {@link xRadius} and {@link yRadius}. Just a - * convenient way to control both at the same time. This value is not used if - * {@link xRadius} and {@link yRadius} are given values. Defaults to 50. - */ - radius?: ScalarPropertyArg - /** - * The X radius for the elliptic base of the cone. Defaults to - * {@link radius}. - * - * **Argument**: {@link PropertyArgument.EffectAge Effect age} - */ - xRadius?: ScalarPropertyArg - /** - * The Y radius for the elliptic base of the cone. Defaults to - * {@link radius}. - * - * **Argument**: {@link PropertyArgument.EffectAge Effect age} - */ - yRadius?: ScalarPropertyArg - /** - * Toggles the jitter and flicker animations for the light. Defaults to - * false. - * - * See also: - * - {@link jitterAcceleration} - * - {@link jitter} - * - {@link flickerIntervalMin} - * - {@link flickerIntervalMax} - * - {@link flickerBrightness} - */ - jitterAndFlicker?: boolean - /** - * Controls the acceleration of the jittering. Defaults to 1. - * - * See also: - * - {@link jitterAndFlicker} - * - {@link jitter} - */ - jitterAcceleration?: number - /** - * Controls how much the light should move around randomly. Defaults to - * [0, 0, 0]. - * - * See also: - * - {@link jitterAndFlicker} - * - {@link jitterAcceleration} - */ - jitter?: Vector3 - /** - * Controls the minimum interval for flickering. Defaults to 0. - * - * See also: - * - {@link jitterAndFlicker} - * - {@link flickerIntervalMax} - * - {@link flickerBrightness} - */ - flickerIntervalMin?: number - /** - * Controls the maximum interval for flickering. Defaults to 1. - * - * See also: - * - {@link jitterAndFlicker} - * - {@link flickerIntervalMin} - * - {@link flickerBrightness} - */ - flickerIntervalMax?: number - /** - * Brightness multiplier for the light when it flickers. Defaults to 0.5. - * - * See also: - * - {@link jitterAndFlicker} - * - {@link flickerIntervalMin} - * - {@link flickerIntervalMax} - */ - flickerBrightness?: number - /** - * Controls if the light should have shadows or not. - * - * Note: Objects also have a setting for casting shadows, and both must be - * enabled for an object to cast shadows from this light source. - * - * Defaults to false. - */ - shadows?: boolean - /** - * When enabled, this allows other properties and fields of the action to - * control the specular color independently of the diffuse color. When - * disabled, the diffuse counterpart of the properties or fields will affect - * both the diffuse and specular color. Defaults to false. - * - * See also: - * - {@link diffuseColor} - * - {@link specularColor} - * - {@link diffuseMultiplier} - * - {@link specularMultiplier} - */ - separateSpecular?: boolean - /** - * Controls how dark shadows from this light source are. At 0, the shadows - * will be entirely invisible. Defaults to 1. - */ - shadowDarkness?: number - /** - * The number of seconds the light takes to fade to nothing after being - * destroyed. - * - * Due to how the field this represents works, the time will be rounded to - * the nearest multiple of 1/30s. The field itself is an integer with 1/30s - * as the units. - * - * Defaults to 0. - */ - fadeOutTime?: number - /** - * Controls the density of some sort of fake fog in the volume hit by the - * light. The fog does not affect the actual light produced by the source and - * is not affected by shadows. Defaults to 0. - * - * See also: - * - {@link phaseFunction} - * - {@link asymmetryParam} - */ - volumeDensity?: number - /** - * Controls whether or not {@link asymmetryParam} affects the fake fog from - * {@link volumeDensity}. Defaults to true. - */ - phaseFunction?: boolean - /** - * Controls how the fake fog from {@link volumeDensity} scatters the light. This - * value is ignored if {@link phaseFunction} is disabled, and the fog will - * scatter the light equally in all directions. - * - * - At 0, the light is scattered equally in every direction. - * - As the value approaches 1, the light is scattered more and more forward, - * in the same direction as the light was already traveling. This means that - * the fake fog will be less visible from the side or behind, and more - * visible from in front of the light. - * - At 1, the fog will not scatter the light at all, so it will be entirely - * invisible. - * - Values above 1 produce unnatural-looking effects where the light darkens - * the fog instead. - * - * Defaults to 0.75. - */ - asymmetryParam?: number - /** - * Controls the falloff exponent of the light. - * - * Note: This is possibly something else, but the behavior is pretty similar - * to a falloff exponent in a few ways. - * - * Defaults to 1. - */ - falloffExponent?: number - unkScalarProp8?: ScalarPropertyArg - unkScalarProp9?: ScalarPropertyArg - unkScalarProp10?: ScalarPropertyArg -} -/** - * Light source with an elliptic cone shape, a spot light. - * - * It points towards +Z. - */ -class SpotLight extends Action { - - constructor({ - diffuseColor = [1, 1, 1, 1], - specularColor = [1, 1, 1, 1], - diffuseMultiplier = 100, - specularMultiplier = 100, - near = 0.01, - far = 50, - radius = 50, - xRadius = radius instanceof Property ? radius.clone() : radius, - yRadius = radius instanceof Property ? radius.clone() : radius, - jitterAndFlicker = false, - jitterAcceleration = 1, - jitter = [0, 0, 0], - flickerIntervalMin = 0, - flickerIntervalMax = 1, - flickerBrightness = 0.5, - shadows = false, - separateSpecular = false, - shadowDarkness = 1, - fadeOutTime = 0, - volumeDensity = 0, - phaseFunction = true, - asymmetryParam = 0.75, - falloffExponent = 1, - unkScalarProp8 = 1, - unkScalarProp9 = 1, - unkScalarProp10 = 1, - }: SpotLightParams = {}) { - super(ActionType.SpotLight, [ - /* 0 */ new IntField(0), - /* 1 */ new BoolField(jitterAndFlicker), - /* 2 */ new FloatField(jitterAcceleration), - /* 3 */ new FloatField(0), - /* 4 */ new FloatField(jitter[0]), - /* 5 */ new FloatField(jitter[1]), - /* 6 */ new FloatField(jitter[2]), - /* 7 */ new FloatField(flickerIntervalMin), - /* 8 */ new FloatField(flickerIntervalMax), - /* 9 */ new FloatField(flickerBrightness), - /* 10 */ new BoolField(shadows), - /* 11 */ new BoolField(separateSpecular), - /* 12 */ new FloatField(shadowDarkness), - /* 13 */ new IntField(2), - /* 14 */ new IntField(1), - /* 15 */ new IntField(Math.round(fadeOutTime * 30)), - /* 16 */ new IntField(100), - /* 17 */ new IntField(0), - /* 18 */ new FloatField(0), - /* 19 */ new FloatField(volumeDensity), - /* 20 */ new FloatField(0), - /* 21 */ new BoolField(phaseFunction), - /* 22 */ new FloatField(asymmetryParam), - /* 23 */ new FloatField(falloffExponent), - /* 24 */ new IntField(1), - /* 25 */ new FloatField(1), - ], [], [ - /* 0 */ vectorFromArg(diffuseColor), - /* 1 */ vectorFromArg(specularColor), - /* 2 */ scalarFromArg(diffuseMultiplier), - /* 3 */ scalarFromArg(specularMultiplier), - /* 4 */ scalarFromArg(near), - /* 5 */ scalarFromArg(far), - /* 6 */ scalarFromArg(xRadius), - /* 7 */ scalarFromArg(yRadius), - /* 8 */ scalarFromArg(unkScalarProp8), - /* 9 */ scalarFromArg(unkScalarProp9), - /* 10 */ scalarFromArg(unkScalarProp10), - ]) - } - - /** - * Controls the diffuse color of the light. - * - * If {@link separateSpecular} is disabled, this also controls the specular - * color of the light. - * - * **Argument**: {@link PropertyArgument.EffectAge Effect age} - */ - get diffuseColor() { return this.properties1[0] } - set diffuseColor(value) { setPropertyInList(this.properties1, 0, value) } - - /** - * Controls the specular color of the light. - * - * If {@link separateSpecular} is disabled, this property is ignored and - * {@link diffuseColor} controls both the diffuse as well as the specular - * color. - * - * **Argument**: {@link PropertyArgument.EffectAge Effect age} - */ - get specularColor() { return this.properties1[1] } - set specularColor(value) { setPropertyInList(this.properties1, 1, value) } - - /** - * A scalar multiplier for the {@link diffuseColor diffuse color}. Good for - * easily adjusting the brightness of the light without changing the color. - * - * If {@link separateSpecular} is disabled, this also affects the specular - * color of the light. - * - * **Argument**: {@link PropertyArgument.EffectAge Effect age} - */ - get diffuseMultiplier() { return this.properties1[2] } - set diffuseMultiplier(value) { setPropertyInList(this.properties1, 2, value) } - - /** - * A scalar multiplier for the {@link specularColor specular color}. - * - * If {@link separateSpecular} is disabled, this property is ignored. - * - * **Argument**: {@link PropertyArgument.EffectAge Effect age} - */ - get specularMultiplier() { return this.properties1[3] } - set specularMultiplier(value) { setPropertyInList(this.properties1, 3, value) } - - /** - * Controls where the light starts in the cone. It bascially "slices off" the - * tip of the cone. If set to 0, it acts as if it is set to 0.5. - * - * **Argument**: {@link PropertyArgument.EffectAge Effect age} - */ - get near() { return this.properties1[4] } - set near(value) { setPropertyInList(this.properties1, 4, value) } - - /** - * Controls how far away the base of the cone is from the light source. - * - * **Argument**: {@link PropertyArgument.EffectAge Effect age} - */ - get far() { return this.properties1[5] } - set far(value) { setPropertyInList(this.properties1, 5, value) } - - /** - * The X radius for the elliptic base of the cone. - * - * **Argument**: {@link PropertyArgument.EffectAge Effect age} - */ - get xRadius() { return this.properties1[6] } - set xRadius(value) { setPropertyInList(this.properties1, 6, value) } - - /** - * The Y radius for the elliptic base of the cone. - * - * **Argument**: {@link PropertyArgument.EffectAge Effect age} - */ - get yRadius() { return this.properties1[7] } - set yRadius(value) { setPropertyInList(this.properties1, 7, value) } - - /** - * Toggles the jitter and flicker animations for the light. Defaults to - * false. - * - * See also: - * - {@link jitterAcceleration} - * - {@link jitter} - * - {@link flickerIntervalMin} - * - {@link flickerIntervalMax} - * - {@link flickerBrightness} - */ - get jitterAndFlicker() { return this.fields2[1].value as boolean } - set jitterAndFlicker(value) { this.fields2[1].value = value } - - /** - * Controls the acceleration of the jittering. Defaults to 1. - * - * See also: - * - {@link jitterAndFlicker} - * - {@link jitter} - */ - get jitterAcceleration() { return this.fields2[2].value as number } - set jitterAcceleration(value) { this.fields2[2].value = value } - - /** - * Controls how much the light should move around randomly. - * - * See also: - * - {@link jitterAndFlicker} - * - {@link jitterAcceleration} - */ - get jitter() { return this.fields2.slice(4, 7).map(f => f.value) as Vector3 } - set jitter(value) { for (let i = 2; i >= 0; i--) this.fields2[4 + i].value = value[i] } - - /** - * Controls the minimum interval for flickering. - * - * See also: - * - {@link jitterAndFlicker} - * - {@link flickerIntervalMax} - * - {@link flickerBrightness} - */ - get flickerIntervalMin() { return this.fields2[7].value as number } - set flickerIntervalMin(value) { this.fields2[7].value = value } - - /** - * Controls the maximum interval for flickering. - * - * See also: - * - {@link jitterAndFlicker} - * - {@link flickerIntervalMin} - * - {@link flickerBrightness} - */ - get flickerIntervalMax() { return this.fields2[8].value as number } - set flickerIntervalMax(value) { this.fields2[8].value = value } - - /** - * Brightness multiplier for the light when it flickers. - * - * See also: - * - {@link jitterAndFlicker} - * - {@link flickerIntervalMin} - * - {@link flickerIntervalMax} - */ - get flickerBrightness() { return this.fields2[9].value as number } - set flickerBrightness(value) { this.fields2[9].value = value } - - /** - * Controls if the light should have shadows or not. - * - * Note: Objects also have a setting for casting shadows, and both must be - * enabled for an object to cast shadows from this light source. - */ - get shadows() { return this.fields1[10].value as boolean } - set shadows(value) { this.fields1[10].value = value } - - /** - * When enabled, this allows other properties and fields of the action to - * control the specular color independently of the diffuse color. When - * disabled, the diffuse counterpart of the properties or fields will affect - * both the diffuse and specular color. - * - * See also: - * - {@link diffuseColor} - * - {@link specularColor} - * - {@link diffuseMultiplier} - * - {@link specularMultiplier} - */ - get separateSpecular() { return this.fields1[11].value as boolean } - set separateSpecular(value) { this.fields1[11].value = value } - - /** - * Controls how dark shadows from this light source are. At 0, the shadows - * will be entirely invisible. - */ - get shadowDarkness() { return this.fields1[12].value as number } - set shadowDarkness(value) { this.fields1[12].value = value } - - /** - * The number of seconds the light takes to fade to nothing after being - * destroyed. - * - * Due to how the field this represents works, the time will be rounded to - * the nearest multiple of 1/30s. The field itself is an integer with 1/30s - * as the units. - */ - get fadeOutTime() { return (this.fields1[15].value as number) / 30 } - set fadeOutTime(value) { this.fields1[15].value = Math.round(value * 30) } - - /** - * Controls the density of some sort of fake fog in the volume hit by the - * light. The fog does not affect the actual light produced by the source and - * is not affected by shadows. - * - * See also: - * - {@link phaseFunction} - * - {@link asymmetryParam} - */ - get volumeDensity() { return this.fields1[19].value as number } - set volumeDensity(value) { this.fields1[19].value = value } - - /** - * Controls whether or not {@link asymmetryParam} affects the fake fog from - * {@link volumeDensity}. - */ - get phaseFunction() { return this.fields1[21].value as boolean } - set phaseFunction(value) { this.fields1[21].value = value } - - /** - * Controls how the fake fog from {@link volumeDensity} scatters the light. This - * value is ignored if {@link phaseFunction} is disabled, and the fog will - * scatter the light equally in all directions. - * - * - At 0, the light is scattered equally in every direction. - * - As the value approaches 1, the light is scattered more and more forward, - * in the same direction as the light was already traveling. This means that - * the fake fog will be less visible from the side or behind, and more - * visible from in front of the light. - * - At 1, the fog will not scatter the light at all, so it will be entirely - * invisible. - * - Values above 1 produce unnatural-looking effects where the light darkens - * the fog instead. - */ - get asymmetryParam() { return this.fields1[22].value as number } - set asymmetryParam(value) { this.fields1[22].value = value } - - /** - * Controls the falloff exponent of the light. - * - * Note: This is possibly something else, but the behavior is pretty similar - * to a falloff exponent in a few ways. - */ - get falloffExponent() { return this.fields1[23].value as number } - set falloffExponent(value) { this.fields1[23].value = value } - -} - -const Actions = { - NodeMovement, - [ActionType.NodeAcceleration]: NodeMovement, NodeAcceleration: NodeMovement, - [ActionType.NodeSpin]: NodeMovement, NodeSpin: NodeMovement, - [ActionType.NodeAccelerationRandomTurns]: NodeMovement, NodeAccelerationRandomTurns: NodeMovement, - [ActionType.NodeAccelerationPartialFollow]: NodeMovement, NodeAccelerationPartialFollow: NodeMovement, - [ActionType.NodeAccelerationSpin]: NodeMovement, NodeAccelerationSpin: NodeMovement, - [ActionType.NodeSpeed]: NodeMovement, NodeSpeed: NodeMovement, - [ActionType.NodeSpeedRandomTurns]: NodeMovement, NodeSpeedRandomTurns: NodeMovement, - [ActionType.NodeSpeedPartialFollow]: NodeMovement, NodeSpeedPartialFollow: NodeMovement, - [ActionType.NodeSpeedSpin]: NodeMovement, NodeSpeedSpin: NodeMovement, - - NodeTransform, - [ActionType.StaticNodeTransform]: NodeTransform, StaticNodeTransform: NodeTransform, - [ActionType.RandomNodeTransform]: NodeTransform, RandomNodeTransform: NodeTransform, - - ParticleMovement, - [ActionType.ParticleAcceleration]: ParticleMovement, ParticleAcceleration: ParticleMovement, - [ActionType.ParticleSpeed]: ParticleMovement, ParticleSpeed: ParticleMovement, - [ActionType.ParticleSpeedRandomTurns]: ParticleMovement, ParticleSpeedRandomTurns: ParticleMovement, - [ActionType.ParticleSpeedPartialFollow]: ParticleMovement, ParticleSpeedPartialFollow: ParticleMovement, - [ActionType.ParticleAccelerationRandomTurns]: ParticleMovement, ParticleAccelerationRandomTurns: ParticleMovement, - [ActionType.ParticleAccelerationPartialFollow]: ParticleMovement, ParticleAccelerationPartialFollow: ParticleMovement, - - [ActionType.NodeTranslation]: NodeTranslation, NodeTranslation, - [ActionType.NodeAttachToCamera]: NodeAttachToCamera, NodeAttachToCamera, - [ActionType.PlaySound]: PlaySound, PlaySound, - [ActionType.NodeAttributes]: NodeAttributes, NodeAttributes, - [ActionType.ParticleAttributes]: ParticleAttributes, ParticleAttributes, - [ActionType.ParticleMultiplier]: ParticleMultiplier, ParticleMultiplier, - [ActionType.FXRReference]: FXRReference, FXRReference, - [ActionType.StateEffectMap]: StateEffectMap, StateEffectMap, - [ActionType.EmitAllParticles]: EmitAllParticles, EmitAllParticles, - [ActionType.EmitRandomParticles]: EmitRandomParticles, EmitRandomParticles, - [ActionType.PeriodicEmitter]: PeriodicEmitter, PeriodicEmitter, - [ActionType.EqualDistanceEmitter]: EqualDistanceEmitter, EqualDistanceEmitter, - [ActionType.OneTimeEmitter]: OneTimeEmitter, OneTimeEmitter, - [ActionType.PointEmitterShape]: PointEmitterShape, PointEmitterShape, - [ActionType.DiskEmitterShape]: DiskEmitterShape, DiskEmitterShape, - [ActionType.RectangleEmitterShape]: RectangleEmitterShape, RectangleEmitterShape, - [ActionType.SphereEmitterShape]: SphereEmitterShape, SphereEmitterShape, - [ActionType.BoxEmitterShape]: BoxEmitterShape, BoxEmitterShape, - [ActionType.CylinderEmitterShape]: CylinderEmitterShape, CylinderEmitterShape, - [ActionType.NoParticleSpread]: NoParticleSpread, NoParticleSpread, - [ActionType.CircularParticleSpread]: CircularParticleSpread, CircularParticleSpread, - [ActionType.EllipticalParticleSpread]: EllipticalParticleSpread, EllipticalParticleSpread, - [ActionType.RectangularParticleSpread]: RectangularParticleSpread, RectangularParticleSpread, - [ActionType.PointSprite]: PointSprite, PointSprite, - [ActionType.Line]: Line, Line, - [ActionType.QuadLine]: QuadLine, QuadLine, - [ActionType.BillboardEx]: BillboardEx, BillboardEx, - [ActionType.MultiTextureBillboardEx]: MultiTextureBillboardEx, MultiTextureBillboardEx, - [ActionType.Model]: Model, Model, - [ActionType.Tracer]: Tracer, Tracer, - [ActionType.Distortion]: Distortion, Distortion, - [ActionType.RadialBlur]: RadialBlur, RadialBlur, - [ActionType.PointLight]: PointLight, PointLight, - [ActionType.NodeWindSpeed]: NodeWindSpeed, NodeWindSpeed, - [ActionType.ParticleWindSpeed]: ParticleWindSpeed, ParticleWindSpeed, - [ActionType.NodeWindAcceleration]: NodeWindAcceleration, NodeWindAcceleration, - [ActionType.ParticleWindAcceleration]: ParticleWindAcceleration, ParticleWindAcceleration, - [ActionType.WaterInteraction]: WaterInteraction, WaterInteraction, - [ActionType.SpotLight]: SpotLight, SpotLight, -} - -class Field { - - type: FieldType - value: number | boolean - - constructor(type: FieldType = FieldType.Integer, value: number | boolean = 0) { - this.type = type - this.value = value - } - - static read(br: BinaryReader, context: any, index: number) { - let ffxField: NumericalField = null - let isInt = false - - if (context instanceof Property) { - if (context.function === PropertyFunction.CompCurve) { - isInt = index > 0 && index <= context.valueType + 1 - } else if (context.function !== PropertyFunction.Constant) { - isInt = !index - } - } else if (context instanceof StateCondition) { - if (index === 0) { - isInt = (context.leftOperandType & 3) > 0 - } else { - isInt = (context.rightOperandType & 3) > 0 - } - } else { - const single = br.getFloat32(br.position) - if (single >= 9.99999974737875E-05 && single < 1000000.0 || - single <= -9.99999974737875E-05 && single > -1000000.0 - ) { - ffxField = new FloatField(single) - } else { - isInt = true - } - } - - if (ffxField === null) { - if (isInt) { - ffxField = new IntField(br.getInt32(br.position)) - } else { - ffxField = new FloatField(br.getFloat32(br.position)) - } - } - - br.position += 4 - return ffxField - } - - static readAt(br: BinaryReader, offset: number, context: any, index: number) { - br.stepIn(offset) - const field = Field.read(br, context, index) - br.stepOut() - return field - } - - static readMany(br: BinaryReader, count: number, context: any) { - const ffxFieldList: NumericalField[] = [] - for (let i = 0; i < count; ++i) { - ffxFieldList.push(Field.read(br, context, i)) - } - return ffxFieldList - } - - static readManyAt(br: BinaryReader, offset: number, count: number, context: any) { - br.stepIn(offset) - const ffxFieldList = Field.readMany(br, count, context) - br.stepOut() - return ffxFieldList - } - - static readWithTypes(br: BinaryReader, count: number, types: any[], context: any) { - return arrayOf(count, i => { - switch (types[i]) { - case FieldType.Boolean: - return new BoolField(!!br.assertInt32(0, 1)) - case FieldType.Integer: - return new IntField(br.readInt32()) - case FieldType.Float: - return new FloatField(br.readFloat32()) - default: - return Field.read(br, context, 0) - } - }) - } - - /** - * Creates a copy of a field. - * @param field The field to copy. - * @returns The copy. - */ - static copy(field: Field) { - return new Field(field.type, field.value) - } - - write(bw: BinaryWriter) { - switch (this.type) { - case FieldType.Boolean: - return bw.writeInt32(+this.value) - case FieldType.Integer: - return bw.writeInt32(this.value as number) - case FieldType.Float: - return bw.writeFloat32(this.value as number) - default: - throw new Error('Invalid field type: ' + this.type) - } - } - - static fromJSON({ - type, - value - }: { - type: string - value: number - }) { - return new Field(FieldType[type], value) - } - - toJSON() { - return { - type: FieldType[this.type], - value: this.value - } - } - -} - -export interface NumericalField extends Field { - value: number -} - -class BoolField extends Field { - - declare value: boolean - - constructor(value: boolean = false) { - super(FieldType.Boolean, value) - } - -} - -class IntField extends Field implements NumericalField { - - declare value: number - - constructor(value: number = 0) { - super(FieldType.Integer, value) - } - -} - -class FloatField extends Field implements NumericalField { - - declare value: number - - constructor(value: number = 0) { - super(FieldType.Float, value) - } - -} - -class Keyframe implements IKeyframe { - - position: number - value: ValueTypeMap[T] - unkTangent1?: ValueTypeMap[T] - unkTangent2?: ValueTypeMap[T] - - constructor( - position: number, - value: ValueTypeMap[T], - unkTangent1?: ValueTypeMap[T], - unkTangent2?: ValueTypeMap[T] - ) { - this.position = position - this.value = value - this.unkTangent1 = unkTangent1 - this.unkTangent2 = unkTangent2 - } - - static copy(orig: IKeyframe) { - return new Keyframe(orig.position, orig.value, orig.unkTangent1, orig.unkTangent2) - } - -} - -abstract class Property implements IModifiableProperty { - - valueType: T - function: F - modifiers: Modifier[] - - constructor( - valueType: T, - func: F, - modifiers: Modifier[] = [] - ) { - this.valueType = valueType - this.function = func - this.modifiers = modifiers - } - - get componentCount() { return this.valueType + 1 as 1 | 2 | 3 | 4 } - - withModifiers(...modifiers: Modifier[]) { - this.modifiers.push(...modifiers) - return this - } - - static fromJSON(obj: { - function: string - }) { - switch (PropertyFunction[obj.function]) { - case PropertyFunction.Constant: - return ValueProperty.fromJSON(obj) - case PropertyFunction.Stepped: - case PropertyFunction.Linear: - case PropertyFunction.Curve1: - case PropertyFunction.Curve2: - return KeyframeProperty.fromJSON(obj) - case PropertyFunction.CompCurve: - return ComponentKeyframeProperty.fromJSON(obj) - } - } - - /** - * Creates a {@link ZeroProperty} with a {@link RandomizerModifier}, - * effectively creating a property with a random value in a given range. - * @param minValue The lower bound of the range of possible values for the - * property. - * @param maxValue The upper bound of the range of possible values for the - * property. - * @param seed A seed or set of seeds for the random number generator to use - * to generate the random property values. - * @returns - */ - static random(minValue: PropertyValue, maxValue: PropertyValue, seed: PropertyValue = randomInt32()) { - return new ZeroProperty(Array.isArray(minValue) ? minValue.length - 1 : ValueType.Scalar, [ - new RandomizerModifier(minValue, maxValue, seed) - ]) - } - - /** - * Generates a rainbow color animation with a configurable duration. - * @param duration How long it takes to go around the entire hue circle in - * seconds. Defaults to 4 seconds. - * @param loop Controls whether the animation should loop or not. Defaults to - * true. - * @returns - */ - static rainbow(duration: number = 4, loop: boolean = true) { - const unit = duration / 6 - return new LinearProperty(loop, [ - new Keyframe(0, [1, 0, 0, 1]), - new Keyframe(unit, [1, 0, 1, 1]), - new Keyframe(unit * 2, [0, 0, 1, 1]), - new Keyframe(unit * 3, [0, 1, 1, 1]), - new Keyframe(unit * 4, [0, 1, 0, 1]), - new Keyframe(unit * 5, [1, 1, 0, 1]), - new Keyframe(unit * 6, [1, 0, 0, 1]), - ]) - } - - abstract fieldCount: number - abstract fields: NumericalField[] - abstract toJSON(): any - abstract scale(factor: number): void - abstract power(exponent: number): void - abstract add(summand: number): void - abstract minify(): Property - abstract valueAt(arg: number): ValueTypeMap[T] - abstract clone(): Property - -} - -class ValueProperty - extends Property - implements IModifiableProperty, IValueProperty { - - value: ValueTypeMap[T] - - constructor( - valueType: T, - func: ValuePropertyFunction, - value: ValueTypeMap[T], - modifiers: Modifier[] = [] - ) { - super(valueType, func, modifiers) - this.value = value - } - - get fieldCount(): number { - switch (this.function) { - case PropertyFunction.Zero: - case PropertyFunction.One: - return 0 - case PropertyFunction.Constant: - return this.componentCount - } - } - - get fields(): NumericalField[] { - switch (this.function) { - case PropertyFunction.Zero: - case PropertyFunction.One: - return [] - case PropertyFunction.Constant: - if (this.valueType === ValueType.Scalar) { - return [ new FloatField(this.value as number) ] - } - return (this.value as Vector).map(e => new FloatField(e)) - default: - throw new Error('Incompatible or unknown function in value property: ' + this.function) - } - } - - static fromFields( - valueType: T, - func: ValuePropertyFunction, - modifiers: Modifier[], - fieldValues: number[] - ): ValueProperty { - switch (func) { - case PropertyFunction.Zero: - return new ZeroProperty(valueType).withModifiers( - ...modifiers - ) as unknown as ValueProperty - case PropertyFunction.One: - return new OneProperty(valueType).withModifiers( - ...modifiers - ) as unknown as ValueProperty - case PropertyFunction.Constant: - return new ConstantProperty(...(fieldValues as [number] | Vector)).withModifiers( - ...modifiers - ) as unknown as ValueProperty - default: - throw new Error('Incompatible or unknown function in property: ' + func) - } - } - - static fromJSON(obj: { - function: string - value?: PropertyValue - loop?: boolean - modifiers?: any[] - }) { - return new ConstantProperty(...(Array.isArray(obj.value) ? obj.value : [obj.value])).withModifiers( - ...(obj.modifiers ?? []).map(e => Modifier.fromJSON(e)) - ) - } - - toJSON() { - switch (this.function) { - case PropertyFunction.Zero: - case PropertyFunction.One: { - const o: { - function: string - value: PropertyValue - modifiers?: any[] - } = { - function: 'Constant', - value: this.valueType === ValueType.Scalar ? this.function : Array(this.componentCount).fill(this.function) as Vector, - } - if (this.modifiers.length > 0) o.modifiers = this.modifiers.map(mod => mod.toJSON()) - return o - } - case PropertyFunction.Constant: { - const o: { - function: string - value: PropertyValue - modifiers?: any[] - } = { - function: PropertyFunction[this.function], - value: this.value, - } - if (this.modifiers.length > 0) o.modifiers = this.modifiers.map(mod => mod.toJSON()) - return o - } - } - } - - scale(factor: number) { - this.function = PropertyFunction.Constant - if (this.valueType === ValueType.Scalar) { - (this.value as number) *= factor - } else { - this.value = (this.value as Vector).map(e => e * factor) as ValueTypeMap[T] - } - } - - power(exponent: number) { - this.function = PropertyFunction.Constant - if (this.valueType === ValueType.Scalar) { - (this.value as number) **= exponent - } else { - this.value = (this.value as Vector).map(e => e ** exponent) as ValueTypeMap[T] - } - } - - add(summand: number) { - this.function = PropertyFunction.Constant - if (this.valueType === ValueType.Scalar) { - (this.value as number) += summand - } else { - this.value = (this.value as Vector).map(e => e + summand) as ValueTypeMap[T] - } - } - - minify(): ValueProperty { - switch (this.function) { - case PropertyFunction.Zero: - case PropertyFunction.One: - return this - case PropertyFunction.Constant: - if ( - Array.isArray(this.value) && - this.value.every(e => e === 0) || - this.value === 0 - ) { - return new ZeroProperty(this.valueType) - } - if ( - Array.isArray(this.value) && - this.value.every(e => e === 1) || - this.value === 1 - ) { - return new OneProperty(this.valueType) - } - return this - } - } - - valueAt(arg: number): ValueTypeMap[T] { - return this.value - } - - clone(): ValueProperty { - return new ValueProperty(this.valueType, this.function, this.value, this.modifiers.map(e => Modifier.copy(e))) - } - -} - -class KeyframeProperty - extends Property - implements IModifiableProperty, IKeyframeProperty { - - loop: boolean - keyframes: IKeyframe[] - - constructor( - valueType: T, - func: F, - loop: boolean = false, - keyframes: IKeyframe[] = [], - modifiers: Modifier[] = [] - ) { - super(valueType, func, modifiers) - this.loop = loop - this.keyframes = keyframes - } - - sortKeyframes() { - this.keyframes.sort((a, b) => a.position - b.position) - } - - get fieldCount(): number { - switch (this.function) { - case PropertyFunction.Stepped: - case PropertyFunction.Linear: - return 1 + 2 * this.componentCount + (1 + this.componentCount) * this.keyframes.length - case PropertyFunction.Curve1: - case PropertyFunction.Curve2: - return 1 + 2 * this.componentCount + (1 + 3 * this.componentCount) * this.keyframes.length - } - } - - get fields(): NumericalField[] { - switch (this.function) { - case PropertyFunction.Stepped: - case PropertyFunction.Linear: - this.sortKeyframes() - return [ - new IntField(this.keyframes.length), - ...arrayOf(2 * this.componentCount, () => new FloatField), - ...this.keyframes.map(e => new FloatField(e.position)), - ...this.keyframes.flatMap( - this.valueType === ValueType.Scalar ? - e => [ new FloatField(e.value as number) ] : - e => (e.value as Vector).map(e => new FloatField(e)) - ) - ] - case PropertyFunction.Curve1: - case PropertyFunction.Curve2: - this.sortKeyframes() - return [ - new IntField(this.keyframes.length), - ...arrayOf(2 * this.componentCount, () => new FloatField), - ...this.keyframes.map(e => new FloatField(e.position)), - ...this.keyframes.flatMap( - this.valueType === ValueType.Scalar ? - e => [ new FloatField(e.value as number) ] : - e => (e.value as Vector).map(e => new FloatField(e)) - ), - ...this.keyframes.flatMap( - this.valueType === ValueType.Scalar ? - e => [ new FloatField(e.unkTangent2 as number) ] : - e => (e.unkTangent2 as Vector).map(e => new FloatField(e)) - ), - ...this.keyframes.flatMap( - this.valueType === ValueType.Scalar ? - e => [ new FloatField(e.unkTangent1 as number) ] : - e => (e.unkTangent1 as Vector).map(e => new FloatField(e)) - ), - ] - default: - throw new Error('Incompatible or unknown function in property: ' + this.function) - } - } - - static fromFields( - valueType: T, - func: F, - loop: boolean, - modifiers: Modifier[], - fieldValues: number[] - ): KeyframeProperty { - switch (func) { - case PropertyFunction.Stepped: - case PropertyFunction.Linear: - return new KeyframeProperty(valueType, func, loop, arrayOf( - fieldValues[0], - i => new Keyframe( - fieldValues[1 + 2 * (valueType + 1) + i], - (valueType === ValueType.Scalar ? - fieldValues[1 + (2 + i) * (valueType + 1) + fieldValues[0]] : - fieldValues.slice(1 + (2 + i) * (valueType + 1) + fieldValues[0], 1 + (2 + i) * (valueType + 1) + fieldValues[0] + (valueType + 1)) as Vector - ) as ValueTypeMap[T] - ) - ), modifiers) - case PropertyFunction.Curve1: - case PropertyFunction.Curve2: - return new KeyframeProperty(valueType, func, loop, arrayOf( - fieldValues[0], - i => new Keyframe( - fieldValues[1 + 2 * (valueType + 1) + i], - (valueType === ValueType.Scalar ? - fieldValues[1 + (2 + i) * (valueType + 1) + fieldValues[0]] : - fieldValues.slice(1 + (2 + i) * (valueType + 1) + fieldValues[0], 1 + (2 + i) * (valueType + 1) + fieldValues[0] + (valueType + 1)) as Vector - ) as ValueTypeMap[T], - (valueType === ValueType.Scalar ? - fieldValues[1 + (2 + i + fieldValues[0]) * (valueType + 1) + fieldValues[0]] : - fieldValues.slice(1 + (2 + i + fieldValues[0]) * (valueType + 1) + fieldValues[0], 1 + (2 + i + fieldValues[0]) * (valueType + 1) + fieldValues[0] + (valueType + 1)) as Vector - ) as ValueTypeMap[T], - (valueType === ValueType.Scalar ? - fieldValues[1 + (2 + i + 2 * fieldValues[0]) * (valueType + 1) + fieldValues[0]] : - fieldValues.slice(1 + (2 + i + 2 * fieldValues[0]) * (valueType + 1) + fieldValues[0], 1 + (2 + i + 2 * fieldValues[0]) * (valueType + 1) + fieldValues[0] + (valueType + 1)) as Vector - ) as ValueTypeMap[T], - ) - ), modifiers) - default: - throw new Error('Incompatible or unknown function in property: ' + func) - } - } - - static fromJSON(obj: { - function: string - modifiers?: any[] - keyframes?: IKeyframe[] - loop?: boolean - }) { - return new KeyframeProperty( - Array.isArray(obj.keyframes[0].value) ? obj.keyframes[0].value.length - 1 : ValueType.Scalar, - PropertyFunction[obj.function], - obj.loop ?? false, - obj.keyframes - ) - } - - toJSON() { - switch (this.function) { - case PropertyFunction.Stepped: - case PropertyFunction.Linear: { - const o: { - function: string - loop?: boolean - keyframes?: any[] - modifiers?: any[] - } = { - function: PropertyFunction[this.function], - } - if (this.function > PropertyFunction.Constant) o.loop = this.loop - o.keyframes = this.keyframes.map(e => ({ - position: e.position, - value: e.value - })) - if (this.modifiers.length > 0) o.modifiers = this.modifiers.map(mod => mod.toJSON()) - return o - } - case PropertyFunction.Curve1: - case PropertyFunction.Curve2: { - const o: { - function: string - loop?: boolean - keyframes?: any[] - modifiers?: any[] - } = { - function: PropertyFunction[this.function], - } - if (this.function > PropertyFunction.Constant) o.loop = this.loop - o.keyframes = this.keyframes.map(e => ({ - position: e.position, - value: e.value, - unkTangent1: e.unkTangent1, - unkTangent2: e.unkTangent2, - })) - if (this.modifiers.length > 0) o.modifiers = this.modifiers.map(mod => mod.toJSON()) - return o - } - } - } - - scale(factor: number) { - for (const kf of this.keyframes) { - if (this.valueType === ValueType.Scalar) { - (kf.value as number) *= factor - } else { - kf.value = (kf.value as Vector).map(e => e * factor) as ValueTypeMap[T] - } - } - } - - power(exponent: number) { - for (const kf of this.keyframes) { - if (this.valueType === ValueType.Scalar) { - (kf.value as number) **= exponent - } else { - kf.value = (kf.value as Vector).map(e => e ** exponent) as ValueTypeMap[T] - } - } - } - - add(summand: number) { - for (const kf of this.keyframes) { - if (this.valueType === ValueType.Scalar) { - (kf.value as number) += summand - } else { - kf.value = (kf.value as Vector).map(e => e + summand) as ValueTypeMap[T] - } - } - } - - minify(): this { - return this - } - - valueAt(arg: number): ValueTypeMap[T] { - switch (this.function) { - case PropertyFunction.Stepped: { - let i = 0 - while ( - i < this.keyframes.length - 1 && this.keyframes[i].position > arg - ) i++ - return this.keyframes[i].value - } - case PropertyFunction.Linear: - case PropertyFunction.Curve1: - case PropertyFunction.Curve2: { - //TODO: Implement better approximations for Curve1 and Curve2 prop values - let i = 0 - while ( - i < this.keyframes.length - 1 && this.keyframes[i].position > arg - ) i++ - if (i < this.keyframes.length - 1) { - const d = this.keyframes[i+1].position - this.keyframes[i].position - if (d === 0) return this.keyframes[i].value - const p = (arg - this.keyframes[i].position) / d - return (this.valueType === ValueType.Scalar ? - lerp( - this.keyframes[i].value as number, - this.keyframes[i+1].value as number, - p - ) - : arrayOf(this.componentCount, j => lerp( - this.keyframes[i].value[j], - this.keyframes[i+1].value[j], - p - )) - ) as ValueTypeMap[T] - } - return this.keyframes[i].value - } - } - } - - clone(): KeyframeProperty { - return new KeyframeProperty( - this.valueType, - this.function, - this.loop, - this.keyframes.map(e => Keyframe.copy(e)), - this.modifiers.map(e => Modifier.copy(e)) - ) - } - -} - -class ComponentKeyframeProperty - extends Property - implements IModifiableProperty { - - declare function: PropertyFunction.CompCurve - loop: boolean - components: IKeyframeProperty[] - - constructor( - valueType: T, - loop: boolean = false, - components: IKeyframeProperty[], - modifiers: Modifier[] = [] - ) { - super(valueType, PropertyFunction.CompCurve, modifiers) - this.loop = loop - this.components = components - } - - sortComponentKeyframes() { - for (const comp of this.components) { - comp.sortKeyframes() - } - } - - get fieldCount(): number { - return 1 + 3 * this.componentCount + this.components.reduce((a, e) => a + 4 * e.keyframes.length, 0) - } - - get fields(): NumericalField[] { - this.sortComponentKeyframes() - return [ - new FloatField(this.components.reduce( - (a, e) => Math.max(a, e.keyframes[e.keyframes.length - 1].position), - 0 - )), - ...this.components.map(e => new IntField(e.keyframes.length)), - ...arrayOf(2 * this.componentCount, () => new FloatField), - ...this.components.flatMap(comp => [ - ...comp.keyframes.map(e => new FloatField(e.position)), - ...comp.keyframes.map(e => new FloatField(e.value)), - ...comp.keyframes.map(e => new FloatField(e.unkTangent1)), - ...comp.keyframes.map(e => new FloatField(e.unkTangent2)), - ]) - ] - } - - static fromFields( - valueType: T, - loop: boolean, - modifiers: Modifier[], - fieldValues: number[] - ): ComponentKeyframeProperty { - let offset = 1 + 3 * (valueType + 1) - return new ComponentKeyframeProperty(valueType, loop, arrayOf(valueType + 1, i => { - return KeyframeProperty.fromFields(ValueType.Scalar, PropertyFunction.Curve2, false, [], [ - fieldValues[1 + i], 0, 0, - ...fieldValues.slice(offset, offset = offset + 4 * fieldValues[1 + i]) - ]) - }), modifiers) - } - - toJSON() { - const o: { - function: 'CompCurve' - components: IKeyframe[][] - loop?: boolean - modifiers?: any[] - } = { - function: 'CompCurve', - components: [] - } - if (this.loop) o.loop = true - o.components = this.components.map(e => e.keyframes.map(f => ({ - position: f.position, - value: f.value, - unkTangent1: f.unkTangent1, - unkTangent2: f.unkTangent2, - }))) - if (this.modifiers.length > 0) o.modifiers = this.modifiers.map(e => e.toJSON()) - return o - } - - scale(factor: number) { - for (const comp of this.components) { - comp.scale(factor) - } - } - - power(exponent: number) { - for (const comp of this.components) { - comp.power(exponent) - } - } - - add(summand: number) { - for (const comp of this.components) { - comp.add(summand) - } - } - - minify(): this { - return this - } - - valueAt(arg: number): ValueTypeMap[T] { - return ( - this.valueType === ValueType.Scalar ? - this.components[0].valueAt(arg) - : this.components.map(e => e.valueAt(arg)) - ) as ValueTypeMap[T] - } - - clone(): ComponentKeyframeProperty { - return new ComponentKeyframeProperty( - this.valueType, - this.loop, - this.components.map(e => e.clone()), - this.modifiers.map(e => Modifier.copy(e)) - ) - } - -} - -class ZeroProperty extends ValueProperty { - constructor(valueType: ValueType = ValueType.Scalar, modifiers: Modifier[] = []) { - super( - valueType, - PropertyFunction.Zero, - valueType === ValueType.Scalar ? 0 : Array(valueType + 1).fill(0), - modifiers - ) - } -} - -class OneProperty extends ValueProperty { - constructor(valueType: ValueType = ValueType.Scalar, modifiers: Modifier[] = []) { - super( - valueType, - PropertyFunction.One, - valueType === ValueType.Scalar ? 1 : Array(valueType + 1).fill(1), - modifiers - ) - } -} - -class ConstantProperty extends ValueProperty { - - constructor( - ...args: - T extends ValueType.Scalar ? [number] : - T extends ValueType.Vector2 ? Vector2 : - T extends ValueType.Vector3 ? Vector3 : - Vector4 - ) { - super(args.length - 1 as T, PropertyFunction.Constant, (args.length === 1 ? args[0] : args) as ValueTypeMap[T]) - } - -} - -class SteppedProperty extends KeyframeProperty { - - constructor(loop: boolean, keyframes: IKeyframe[]) { - if (keyframes.length < 2) { - throw new Error ('Properties with a stepped function must have at least 2 stops.') - } - const comps = Array.isArray(keyframes[0].value) ? keyframes[0].value.length : 1 - super(comps - 1 as T, PropertyFunction.Stepped, loop, keyframes) - } - -} - -class LinearProperty extends KeyframeProperty { - - constructor(loop: boolean, keyframes: IKeyframe[]) { - if (keyframes.length < 2) { - throw new Error ('Properties with a linear function must have at least 2 stops.') - } - const comps = Array.isArray(keyframes[0].value) ? keyframes[0].value.length : 1 - super(comps - 1 as T, PropertyFunction.Linear, loop, keyframes) - } - - /** - * Creates a new linear property with only two steps. - * @param loop Controls whether the property should loop or not. - * @param endPosition The position of the second stop. - * @param startValue The value of the first stop. - * @param endValue The value of the second stop. - * @returns The new linear property. - */ - static basic( - loop: boolean, - endPosition: number, - startValue: PropertyValue, - endValue: PropertyValue - ) { - return new LinearProperty(loop, [ - new Keyframe(0, startValue), - new Keyframe(endPosition, endValue), - ]) - } - - /** - * Creates a new linear property that approximates a power function. - * @param loop Controls whether the property should loop or not. - * @param exponent The exponent used in the power function. For example, - * setting this to values greater than 1 will make the property value change - * slowly at the start, but get faster and faster until it reaches the end. - * @param stops How many stops to use. Must be greater than or equal to 2. - * Using higher values will produce a smoother curve. Setting it to 2 will - * make it linear, which means you might as well use the {@link basic} - * method instead of this. - * @param endPosition The position of the last stop. - * @param startValue The value of the first stop. - * @param endValue The value of the last stop. - * @returns The new linear property. - */ - static power( - loop: boolean, - exponent: number, - stops: number, - endPosition: number, - startValue: PropertyValue, - endValue: PropertyValue - ) { - if (stops < 2) { - throw new Error('Property stop count must be greater than or equal to 2.') - } - if (Array.isArray(startValue) && Array.isArray(endValue)) { - return new LinearProperty(loop, arrayOf(stops, i => new Keyframe( - i / (stops - 1) * endPosition, - startValue.map((e: number, j: number) => lerp(e, endValue[j], (i / (stops - 1)) ** exponent)) as Vector - ))) - } else if (typeof startValue === 'number' && typeof endValue === 'number') { - return new LinearProperty(loop, arrayOf(stops, i => new Keyframe( - i / (stops - 1) * endPosition, - lerp(startValue, endValue, (i / (stops - 1)) ** exponent) - ))) - } else { - throw new Error('startValue and endValue must be of the same type.') - } - } - - /** - * Creates a new linear property that approximates a sine wave. - * @param min The value used when the sine wave is at its minimum. - * @param max The value used when the sine wave is at its maximum. - * @param period The period of the sine wave. - * @param stops The number of stops to use to approximate the sine wave. - * Higher values result in a smoother curve. Defaults to 21. - * @returns The new linear property. - */ - static sine( - min: PropertyValue, - max: PropertyValue, - period: number, - stops: number = 21 - ) { - if (Array.isArray(min) && Array.isArray(max)) { - return new LinearProperty(true, arrayOf(stops, i => new Keyframe( - i / (stops - 1) * period, - min.map((e, j) => (max[j] + e) / 2 + (max[j] - e) / 2 * Math.sin(i / (stops - 1) * Math.PI * 2)) as Vector - ))) - } else if (typeof min === 'number' && typeof max === 'number') { - return new LinearProperty(true, arrayOf(stops, i => new Keyframe( - i / (stops - 1) * period, - (max + min) / 2 + (max - min) / 2 * Math.sin(i / (stops - 1) * Math.PI * 2) - ))) - } else { - throw new Error('min and max must be of the same type.') - } - } - -} - -class Curve2Property extends KeyframeProperty { - - constructor(loop: boolean, keyframes: IKeyframe[]) { - if (keyframes.length < 2) { - throw new Error ('Properties with a curve function must have at least 2 stops.') - } - const comps = Array.isArray(keyframes[0].value) ? keyframes[0].value.length : 1 - super(comps - 1 as T, PropertyFunction.Curve2, loop, keyframes) - } - -} - -class Modifier { - - static #typeEnumBValues = { - [ModifierType.Randomizer1]: 0, - [ModifierType.Randomizer2]: 4, - [ModifierType.ExternalValue1]: 8, - [ModifierType.ExternalValue2]: 12, - [ModifierType.Randomizer3]: 16, - } - - typeEnumA: number - typeEnumB: number - fields: NumericalField[] - properties: IProperty[] - - constructor( - type: ModifierType, - valueType: ValueType, - fields: NumericalField[] = [], - properties: IProperty[] = [] - ) { - this.type = type - this.valueType = valueType - this.fields = fields - this.properties = properties - } - - static typeEnumAToModifierType(typeEnumA: number): ModifierType { - return (typeEnumA >>> 12 & 0b11) << 4 | typeEnumA >>> 4 & 0b1111 - } - - static modifierTypeToTypeEnumA(type: ModifierType, valueType: ValueType = ValueType.Scalar) { - return (type >>> 4 | 0b1100) << 12 | (type & 0b1111) << 4 | valueType - } - - static read(br: BinaryReader) { - const typeEnumA = br.readUint16() - const teA1 = typeEnumA >>> 12 & 0b11 - const teA2 = typeEnumA >>> 4 & 0b1111 - const modifierType = teA1 << 4 | teA2 - const valueType = typeEnumA & 0b11 - if (!(modifierType in ModifierType)) { - throw new Error('Unknown property modifier type enum A: ' + typeEnumA) - } - br.assertUint8(0) - br.assertUint8(1) - const typeEnumB = br.readUint32() - const fieldCount = br.readInt32() - const propertyCount = br.readInt32() - const fieldOffset = br.readInt32() - br.assertInt32(0) - const propertyOffset = br.readInt32() - br.assertInt32(0) - br.stepIn(propertyOffset) - const properties = [] - for (let i = 0; i < propertyCount; ++i) { - properties.push(readProperty(br, true)) - } - br.stepOut() - const fields = Field.readManyAt(br, fieldOffset, fieldCount, this) - const mod = new Modifier(modifierType, valueType, fields, properties) - mod.typeEnumB = typeEnumB - return mod - } - - static copy(mod: Modifier) { - const copy = new Modifier( - mod.type, - mod.valueType, - mod.fields.map(f => Field.copy(f) as NumericalField), - mod.properties.map(p => p.clone()) - ) - copy.typeEnumB = mod.typeEnumB - return copy - } - - write(bw: BinaryWriter, modifiers: Modifier[]) { - const count = modifiers.length - bw.writeInt16(this.typeEnumA) - bw.writeUint8(0) - bw.writeUint8(1) - bw.writeInt32(this.typeEnumB) - bw.writeInt32(this.fields.length) - bw.writeInt32(this.properties.length) - bw.reserveInt32(`Section8FieldsOffset${count}`) - bw.writeInt32(0) - bw.reserveInt32(`Section8Section9sOffset${count}`) - bw.writeInt32(0) - modifiers.push(this) - } - - writeProperties(bw: BinaryWriter, index: number, properties: IProperty[]) { - bw.fill(`Section8Section9sOffset${index}`, bw.position) - for (const property of this.properties) { - writeProperty(property, bw, properties, true) - } - } - - writeFields(bw: BinaryWriter, index: number): number { - bw.fill(`Section8FieldsOffset${index}`, bw.position) - for (const field of this.fields) { - field.write(bw) - } - return this.fields.length - } - - get type(): ModifierType { - return Modifier.typeEnumAToModifierType(this.typeEnumA) - } - - set type(value) { - const valueType = this.valueType - this.typeEnumA = Modifier.modifierTypeToTypeEnumA(value, valueType) - this.typeEnumB = Modifier.#typeEnumBValues[value] | valueType - } - - /** - * Sets the modifier type and returns the modifier. - * @param type The new type for the modifier. - */ - withType(type: ModifierType) { - this.type = type - return this - } - - get valueType(): ValueType { - return this.typeEnumA & 0b11 - } - - set valueType(value) { - this.typeEnumA = (this.typeEnumA & 0xfffc) | value - this.typeEnumB = (this.typeEnumB & 0xfffffffc) | value - } - - /** - * Sets the value type and returns the modifier. - * @param type The new value type for the modifier. - */ - withValueType(type: ValueType) { - this.valueType = type - return this - } - - static fromJSON({ - typeEnumA, - typeEnumB, - fields, - properties = [], - }: { - typeEnumA: number - typeEnumB: number - fields: [] - properties?: [] - }): Modifier { - return new Modifier( - typeEnumA, - typeEnumB, - fields.map(field => Field.fromJSON(field) as NumericalField), - properties.map(prop => Property.fromJSON(prop)) - ) - } - - toJSON() { - const o: { - typeEnumA: number - typeEnumB: number - fields: any[] - properties?: any[] - } = { - typeEnumA: this.typeEnumA, - typeEnumB: this.typeEnumB, - fields: this.fields.map(field => field.toJSON()), - } - if (this.properties.length > 0) o.properties = this.properties.map(prop => prop.toJSON()) - return o - } - -} - -/** - * A property modifier that changes the property value depending on an - * {@link ExternalValue external value}. - * - * The property value wil be multiplied by the values in this modifier. - */ -class ExternalValueModifier extends Modifier { - - /** - * @param extVal The ID of the external value to use. - * @param loop Controls if the modifier property should loop or not. - * @param stops An array of objects with `position` and `value` properties - * representing the external value and the modifier value it maps to. For - * example, the value of {@link ExternalValue.DisplayBlood} is -1 when the - * "Display Blood" option is off, so the `position` for the modifier value - * should be -1 to change the property based on that. - * @param type Controls what type of modifier to use. Defaults to - * {@link ModifierType.ExternalValue1}. - */ - constructor( - extVal: ExternalValue, - loop: boolean, - stops: { position: number, value: PropertyValue }[], - type: ModifierType.ExternalValue1 | ModifierType.ExternalValue2 = ModifierType.ExternalValue1 - ) { - const valueType = typeof stops[0].value === 'number' ? 0 : stops[0].value.length - 1 - super(type, valueType, [ - new IntField(extVal) - ], [ - new LinearProperty(loop, stops) - ]) - } - - get externalValue() { return this.fields[0].value as number } - set externalValue(value) { this.fields[0].value = value } - - valueAt(arg: number) { return this.properties[0].valueAt(arg) } - -} - -/** - * A property modifier that changes the property value depending on the - * "Display Blood" option. - * - * The property value wil be multiplied by the values in this modifier. - */ -class BloodVisibilityModifier extends ExternalValueModifier { - - declare properties: [KeyframeProperty] - - /** - * @param onValue The value when "Display Blood" is set to "On". - * @param mildValue The value when "Display Blood" is set to "Mild". - * @param offValue The value when "Display Blood" is set to "Off". - * @param type Controls what type of modifier to use. Defaults to - * {@link ModifierType.ExternalValue1}. - */ - constructor( - onValue: PropertyValue, - mildValue: PropertyValue, - offValue: PropertyValue, - type: ModifierType.ExternalValue1 | ModifierType.ExternalValue2 = ModifierType.ExternalValue1 - ) { - super(ExternalValue.DisplayBlood, false, [ - new Keyframe(-1, offValue), - new Keyframe(0, onValue), - new Keyframe(1, mildValue), - ], type) - } - - get offValue() { return this.properties[0].keyframes[0].value } - set offValue(value) { this.properties[0].keyframes[0].value = value } - - get onValue() { return this.properties[0].keyframes[1].value } - set onValue(value) { this.properties[0].keyframes[1].value = value } - - get mildValue() { return this.properties[0].keyframes[2].value } - set mildValue(value) { this.properties[0].keyframes[2].value = value } - -} - -/** - * A property modifier that changes the property's value based on the weather. - * - * Only functional in Elden Ring. - */ -class PrecipitationModifier extends ExternalValueModifier { - - /** - * @param clear The value when it's not raining or snowing. - * @param precip The value when it's raining or snowing. - */ - constructor(clear: PropertyValue, precip: PropertyValue) { - super(ExternalValue.Precipitation, false, [ - { position: 0, value: clear }, - { position: 1, value: precip }, - ]) - } - -} - -/** - * A property modifer that changes the property value by a random amount in a - * given range. - */ -class RandomizerModifier extends Modifier { - - constructor(minValue: PropertyValue, maxValue: PropertyValue, seed: PropertyValue = randomInt32()) { - if (Array.isArray(minValue)) { - if (!Array.isArray(maxValue) || maxValue.length !== minValue.length) { - throw new Error(`Incompatible min and max values for randomizer modifier: Min: ${minValue.toString()}, Max: ${maxValue.toString()}`) - } - if (minValue.length < 1 || minValue.length > 4) { - throw new Error(`Invalid number of vector components: ${minValue.length}`) - } - const seedArray = Array.isArray(seed) ? seed : [seed] - const valueType = minValue.length - 1 - super(ModifierType.Randomizer2, valueType, [ - ...arrayOf(minValue.length, i => new IntField(seedArray[i % seedArray.length])), - ...minValue.map(e => new FloatField(e)), - ...maxValue.map(e => new FloatField(e)), - ]) - } else { - if (Array.isArray(maxValue)) { - throw new Error(`Incompatible min and max values for randomizer modifier: Min: ${minValue}, Max: ${maxValue.toString()}`) - } - if (Array.isArray(seed)) { - throw new Error('Random scalar modifiers cannot use vector seeds.') - } - super(ModifierType.Randomizer2, ValueType.Scalar, [ - new IntField(seed), - new FloatField(minValue), - new FloatField(maxValue), - ]) - } - } - -} - -class Section10 { - - fields: Field[] - - constructor(fields: Field[]) { - this.fields = fields - } - - static read(br: BinaryReader) { - const offset = br.readInt32() - br.assertInt32(0) - const count = br.readInt32() - br.assertInt32(0) - return new Section10(Field.readManyAt(br, offset, count, this)) - } - - write(bw: BinaryWriter, section10s: Section10[]) { - const count = section10s.length - bw.reserveInt32(`Section10FieldsOffset${count}`) - bw.writeInt32(0) - bw.writeInt32(this.fields.length) - bw.writeInt32(0) - section10s.push(this) - } - - writeFields(bw: BinaryWriter, index: number): number { - bw.fill(`Section10FieldsOffset${index}`, bw.position) - for (const field of this.fields) { - field.write(bw) - } - return this.fields.length - } - - static fromJSON(fields: []) { - return new Section10(fields.map(field => Field.fromJSON(field))) - } - - toJSON() { - return this.fields.map(field => field.toJSON()) - } - -} - -export { - FXRVersion, - NodeType, - EffectType, - ActionType, - ValueType, - PropertyFunction, - ModifierType, - FieldType, - BlendMode, - ExternalValue, - Operator, - OperandType, - AttachMode, - PropertyArgument, - OrientationMode, - TracerOrientationMode, - LightingMode, - DistortionMode, - DistortionShape, - - Nodes, - Effects, - EffectActionSlots, - Actions, - - FXR, - - State, - StateCondition, - - Node, - NodeWithEffects, - RootNode, - ProxyNode, - LevelOfDetailNode, - BasicNode, - SharedEmitterNode, - - Effect, - LevelOfDetailEffect, - BasicEffect, - SharedEmitterEffect, - - Action, - NodeMovement, - NodeTranslation, - NodeTransform, - NodeAttachToCamera, - PlaySound, - ParticleMovement, - NodeAttributes, - ParticleAttributes, - ParticleMultiplier, - FXRReference, - LevelOfDetailThresholds, - StateEffectMap, - EmitAllParticles, - EmitRandomParticles, - PeriodicEmitter, - EqualDistanceEmitter, - OneTimeEmitter, - PointEmitterShape, - DiskEmitterShape, - RectangleEmitterShape, - SphereEmitterShape, - BoxEmitterShape, - CylinderEmitterShape, - NoParticleSpread, - CircularParticleSpread, - EllipticalParticleSpread, - RectangularParticleSpread, - CommonFields2Action, - PointSprite, - Line, - QuadLine, - BillboardEx, - MultiTextureBillboardEx, - Model, - Tracer, - Distortion, - RadialBlur, - PointLight, - NodeWindSpeed, - ParticleWindSpeed, - NodeWindAcceleration, - ParticleWindAcceleration, - WaterInteraction, - SpotLight, - - Field, - BoolField, - IntField, - FloatField, - - Keyframe, - Property, - ValueProperty, - KeyframeProperty, - ComponentKeyframeProperty, - ZeroProperty, - OneProperty, - ConstantProperty, - SteppedProperty, - LinearProperty, - Curve2Property, - - Modifier, - ExternalValueModifier, - BloodVisibilityModifier, - PrecipitationModifier, - RandomizerModifier, - - Section10 -} diff --git a/package-lock.json b/package-lock.json index ca461fc..9cd5f14 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,18 +1,21 @@ { "name": "@cccode/fxr", - "version": "5.0.1", + "version": "6.0.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@cccode/fxr", - "version": "5.0.1", + "version": "6.0.0", "license": "Unlicense", "devDependencies": { + "chokidar": "^3.6.0", "cpy-cli": "^5.0.0", - "terser": "^5.26.0", - "typedoc": "^0.25.6", - "typescript": "^5.3.3" + "npm-run-all2": "^6.1.2", + "terser": "^5.29.2", + "typedoc": "^0.25.12", + "typescript": "^5.4.3", + "yaml": "^2.4.1" } }, "node_modules/@jridgewell/gen-mapping": { @@ -142,6 +145,31 @@ "integrity": "sha512-vJXt3yiaUL4UU546s3rPXlsry/RnM730G1+HkpKE012AN0sx1eOrxSu95oKDIonskeLTijMgqWZ3uDEe3NFvyg==", "dev": true }, + "node_modules/ansi-styles": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", + "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/anymatch": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", + "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", + "dev": true, + "dependencies": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + }, + "engines": { + "node": ">= 8" + } + }, "node_modules/arrify": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/arrify/-/arrify-3.0.0.tgz", @@ -160,6 +188,18 @@ "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", "dev": true }, + "node_modules/binary-extensions": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz", + "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==", + "dev": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/brace-expansion": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", @@ -187,6 +227,30 @@ "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", "dev": true }, + "node_modules/chokidar": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", + "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", + "dev": true, + "dependencies": { + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" + }, + "engines": { + "node": ">= 8.10.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + } + }, "node_modules/clean-stack": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-4.2.0.tgz", @@ -266,6 +330,20 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/cross-spawn": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", + "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "dev": true, + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, "node_modules/dir-glob": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", @@ -327,6 +405,20 @@ "node": ">=8" } }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, "node_modules/glob-parent": { "version": "5.1.2", "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", @@ -385,6 +477,18 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/is-binary-path": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "dev": true, + "dependencies": { + "binary-extensions": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/is-extglob": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", @@ -415,6 +519,21 @@ "node": ">=0.12.0" } }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true + }, + "node_modules/json-parse-even-better-errors": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-3.0.1.tgz", + "integrity": "sha512-aatBvbL26wVUCLmbWdCpeu9iF5wOyWpagiKkInA+kfws3sWdBrTnsvN2CKcyCYyUrc7rebNBlK6+kteg7ksecg==", + "dev": true, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, "node_modules/jsonc-parser": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.2.0.tgz", @@ -451,6 +570,15 @@ "node": ">= 12" } }, + "node_modules/memorystream": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/memorystream/-/memorystream-0.3.1.tgz", + "integrity": "sha512-S3UwM3yj5mtUSEfP41UZmt/0SCoVYUcU1rkXv+BQ5Ig8ndL4sPoJNBUJERafdPb5jjHJGuMgytgKvKIf58XNBw==", + "dev": true, + "engines": { + "node": ">= 0.10.0" + } + }, "node_modules/meow": { "version": "12.1.1", "resolved": "https://registry.npmjs.org/meow/-/meow-12.1.1.tgz", @@ -506,6 +634,49 @@ "integrity": "sha512-9iN1ka/9zmX1ZvLV9ewJYEk9h7RyRRtqdK0woXcqohu8EWIerfPUjYJPg0ULy0UqP7cslmdGc8xKDJcojlKiaw==", "dev": true }, + "node_modules/normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/npm-normalize-package-bin": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/npm-normalize-package-bin/-/npm-normalize-package-bin-3.0.1.tgz", + "integrity": "sha512-dMxCf+zZ+3zeQZXKxmyuCKlIDPGuv8EF940xbkC4kQVDTtqoh6rJFO+JTKSA6/Rwi0getWmtuy4Itup0AMcaDQ==", + "dev": true, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/npm-run-all2": { + "version": "6.1.2", + "resolved": "https://registry.npmjs.org/npm-run-all2/-/npm-run-all2-6.1.2.tgz", + "integrity": "sha512-WwwnS8Ft+RpXve6T2EIEVpFLSqN+ORHRvgNk3H9N62SZXjmzKoRhMFg3I17TK3oMaAEr+XFbRirWS2Fn3BCPSg==", + "dev": true, + "dependencies": { + "ansi-styles": "^6.2.1", + "cross-spawn": "^7.0.3", + "memorystream": "^0.3.1", + "minimatch": "^9.0.0", + "pidtree": "^0.6.0", + "read-package-json-fast": "^3.0.2", + "shell-quote": "^1.7.3" + }, + "bin": { + "npm-run-all": "bin/npm-run-all/index.js", + "npm-run-all2": "bin/npm-run-all/index.js", + "run-p": "bin/run-p/index.js", + "run-s": "bin/run-s/index.js" + }, + "engines": { + "node": "^14.18.0 || >=16.0.0", + "npm": ">= 8" + } + }, "node_modules/p-event": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/p-event/-/p-event-5.0.1.tgz", @@ -575,6 +746,15 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true, + "engines": { + "node": ">=8" + } + }, "node_modules/path-type": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", @@ -596,6 +776,18 @@ "url": "https://github.com/sponsors/jonschlinkert" } }, + "node_modules/pidtree": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/pidtree/-/pidtree-0.6.0.tgz", + "integrity": "sha512-eG2dWTVw5bzqGRztnHExczNxt5VGsE6OwTeCG3fdUf9KBsZzO3R5OIIIzWR+iZA0NtZ+RDVdaoE2dK1cn6jH4g==", + "dev": true, + "bin": { + "pidtree": "bin/pidtree.js" + }, + "engines": { + "node": ">=0.10" + } + }, "node_modules/queue-microtask": { "version": "1.2.3", "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", @@ -616,6 +808,31 @@ } ] }, + "node_modules/read-package-json-fast": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/read-package-json-fast/-/read-package-json-fast-3.0.2.tgz", + "integrity": "sha512-0J+Msgym3vrLOUB3hzQCuZHII0xkNGCtz/HJH9xZshwv9DbDwkw1KaE3gx/e2J5rpEY5rtOy6cyhKOPrkP7FZw==", + "dev": true, + "dependencies": { + "json-parse-even-better-errors": "^3.0.0", + "npm-normalize-package-bin": "^3.0.0" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/readdirp": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "dev": true, + "dependencies": { + "picomatch": "^2.2.1" + }, + "engines": { + "node": ">=8.10.0" + } + }, "node_modules/reusify": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", @@ -649,6 +866,36 @@ "queue-microtask": "^1.2.2" } }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/shell-quote": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.8.1.tgz", + "integrity": "sha512-6j1W9l1iAs/4xYBI1SYOVZyFcCis9b4KCLQ8fgAGG07QvzaRLVVRQvAy85yNmmZSjYjg4MWh4gNvlPujU/5LpA==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/shiki": { "version": "0.14.7", "resolved": "https://registry.npmjs.org/shiki/-/shiki-0.14.7.tgz", @@ -693,9 +940,9 @@ } }, "node_modules/terser": { - "version": "5.26.0", - "resolved": "https://registry.npmjs.org/terser/-/terser-5.26.0.tgz", - "integrity": "sha512-dytTGoE2oHgbNV9nTzgBEPaqAWvcJNl66VZ0BkJqlvp71IjO8CxdBx/ykCNb47cLnCmCvRZ6ZR0tLkqvZCdVBQ==", + "version": "5.29.2", + "resolved": "https://registry.npmjs.org/terser/-/terser-5.29.2.tgz", + "integrity": "sha512-ZiGkhUBIM+7LwkNjXYJq8svgkd+QK3UUr0wJqY4MieaezBSAIPgbSPZyIx0idM6XWK5CMzSWa8MJIzmRcB8Caw==", "dev": true, "dependencies": { "@jridgewell/source-map": "^0.3.3", @@ -723,9 +970,9 @@ } }, "node_modules/typedoc": { - "version": "0.25.6", - "resolved": "https://registry.npmjs.org/typedoc/-/typedoc-0.25.6.tgz", - "integrity": "sha512-1rdionQMpOkpA58qfym1J+YD+ukyA1IEIa4VZahQI2ZORez7dhOvEyUotQL/8rSoMBopdzOS+vAIsORpQO4cTA==", + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/typedoc/-/typedoc-0.25.12.tgz", + "integrity": "sha512-F+qhkK2VoTweDXd1c42GS/By2DvI2uDF4/EpG424dTexSHdtCH52C6IcAvMA6jR3DzAWZjHpUOW+E02kyPNUNw==", "dev": true, "dependencies": { "lunr": "^2.3.9", @@ -740,13 +987,13 @@ "node": ">= 16" }, "peerDependencies": { - "typescript": "4.6.x || 4.7.x || 4.8.x || 4.9.x || 5.0.x || 5.1.x || 5.2.x || 5.3.x" + "typescript": "4.6.x || 4.7.x || 4.8.x || 4.9.x || 5.0.x || 5.1.x || 5.2.x || 5.3.x || 5.4.x" } }, "node_modules/typescript": { - "version": "5.3.3", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.3.3.tgz", - "integrity": "sha512-pXWcraxM0uxAS+tN0AG/BF2TyqmHO014Z070UsJ+pFvYuRSq8KH8DmWpnbXe0pEPDHXZV3FcAbJkijJ5oNEnWw==", + "version": "5.4.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.4.3.tgz", + "integrity": "sha512-KrPd3PKaCLr78MalgiwJnA25Nm8HAmdwN3mYUYZgG/wizIo9EainNVQI9/yDavtVFRN2h3k8uf3GLHuhDMgEHg==", "dev": true, "bin": { "tsc": "bin/tsc", @@ -767,6 +1014,33 @@ "resolved": "https://registry.npmjs.org/vscode-textmate/-/vscode-textmate-8.0.0.tgz", "integrity": "sha512-AFbieoL7a5LMqcnOF04ji+rpXadgOXnZsxQr//r83kLPr7biP7am3g9zbaZIaBGwBRWeSvoMD4mgPdX3e4NWBg==", "dev": true + }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/yaml": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.4.1.tgz", + "integrity": "sha512-pIXzoImaqmfOrL7teGUBt/T7ZDnyeGBWyXQBvOVhLkWLN37GXv8NMLK406UY6dS51JfcQHsmcW5cJ441bHg6Lg==", + "dev": true, + "bin": { + "yaml": "bin.mjs" + }, + "engines": { + "node": ">= 14" + } } } } diff --git a/package.json b/package.json index d3b2a21..87bd9fd 100644 --- a/package.json +++ b/package.json @@ -1,24 +1,30 @@ { "name": "@cccode/fxr", - "version": "5.0.1", - "description": "JavaScript library for editing FXR files from Dark Souls 3, Sekiro, Elden Ring, and Armored Core 6.", + "version": "6.0.0", + "description": "JavaScript library for creating and editing FXR files for Dark Souls 3, Sekiro, Elden Ring, and Armored Core 6.", "author": "CCCode", "type": "module", - "main": "./js/fxr.js", - "module": "./js/fxr.js", + "main": "./dist/esm/fxr.js", + "module": "./dist/esm/fxr.js", "exports": { - "import": "./js/fxr.js", - "require": "./js/cjs/fxr.js" + "import": "./dist/esm/fxr.js", + "require": "./dist/cjs/fxr.js" }, - "types": "./js/fxr.d.ts", + "types": "./dist/esm/fxr.d.ts", "scripts": { - "docs": "npx typedoc", - "build": "npx tsc && npx terser js/fxr.js -o js/fxr.min.js --ecma 2020 -m \"module\" -c \"keep_fargs,keep_infinity,passes=2,module\" --source-map \"content='js/fxr.js.map',filename='js/fxr.min.js.map'\" && npx cpy js/fxr.d.ts --rename=fxr.min.d.ts --flat js && npx tsc --p tsconfig-cjs.json && npx terser js/cjs/fxr.js -o js/cjs/fxr.min.js --ecma 2020 -m \"module\" -c \"keep_fargs,keep_infinity,passes=2,module\" --source-map \"content='js/cjs/fxr.js.map',filename='js/cjs/fxr.min.js.map'\" && npx cpy js/cjs/fxr.d.ts --rename=fxr.min.d.ts --flat js/cjs && echo {\"type\":\"commonjs\"} > js/cjs/package.json", - "watch": "npx tsc --watch" + "build:yaml": "node build/run.js", + "build:ts:esm": "npx tsc --p tsconfig-esm.json", + "build:ts:cjs": "npx tsc --p tsconfig-cjs.json && echo {\"type\":\"commonjs\"} > dist/cjs/package.json", + "build:ts": "npm-run-all --parallel build:ts:*", + "build:minify": "npx terser dist/esm/fxr.js -o dist/esm/fxr.min.js --ecma 2020 -m \"module\" -c \"keep_fargs,keep_infinity,passes=2,module\" --source-map \"content='dist/esm/fxr.js.map',filename='dist/esm/fxr.min.js.map'\" && npx cpy dist/esm/fxr.d.ts --rename=fxr.min.d.ts --flat dist/esm && npx terser dist/cjs/fxr.js -o dist/cjs/fxr.min.js --ecma 2020 -m \"module\" -c \"keep_fargs,keep_infinity,passes=2,module\" --source-map \"content='dist/cjs/fxr.js.map',filename='dist/cjs/fxr.min.js.map'\" && npx cpy dist/cjs/fxr.d.ts --rename=fxr.min.d.ts --flat dist/cjs", + "build": "npm-run-all build:yaml build:ts build:minify", + "watch:dist": "npx tsc --p tsconfig-esm.json --watch", + "watch:src": "node build/watch.js", + "watch": "npm-run-all --parallel watch:*", + "docs": "npm run build:yaml && npx typedoc --tsconfig tsconfig.json" }, "files": [ - "/js", - "/fxr.ts" + "/dist" ], "keywords": [ "typescript", @@ -35,9 +41,12 @@ }, "homepage": "https://fxr-docs.pages.dev/", "devDependencies": { + "chokidar": "^3.6.0", "cpy-cli": "^5.0.0", - "terser": "^5.26.0", - "typedoc": "^0.25.6", - "typescript": "^5.3.3" + "npm-run-all2": "^6.1.2", + "terser": "^5.29.2", + "typedoc": "^0.25.12", + "typescript": "^5.4.3", + "yaml": "^2.4.1" } } diff --git a/src/actions/10012.yml b/src/actions/10012.yml new file mode 100644 index 0000000..94d827f --- /dev/null +++ b/src/actions/10012.yml @@ -0,0 +1,599 @@ +type: 10012 +name: DynamicTracer +desc: | + Creates a trail behind moving effects. + + This is slightly different from {@link Tracer}, as the trail from this is less visible when it's moving slower. +properties: + orientation: + type: TracerOrientationMode + field: int + default: TracerOrientationMode.LocalZ + desc: | + Tracer orientation mode. See {@link TracerOrientationMode} for more information. + normalMap: + field: int + default: 0 + desc: | + Normal map texture ID. + + This is used to control the distortion effect of the trail. + see: + - distortionIntensity + segmentInterval: + field: float + default: 0 + desc: | + The trail is made up of multiple quads, or *segments*. This controls how many seconds to wait between new segments being created. Lower values produce a smoother trail. + segmentDuration: + field: float + default: 1 + desc: | + The trail is made up of multiple quads, or *segments*. This controls how long each segment should last in seconds. + concurrentSegments: + field: int + default: 100 + desc: | + The trail is made up of multiple quads, or *segments*. This controls how many segments may exist at the same time. + columns: + field: int + default: 1 + desc: | + To split the texture into multiple animation frames, this value must be set to the number of columns in the texture. It should equal `textureWidth / frameWidth`. + see: + - totalFrames + totalFrames: + field: int + default: 1 + desc: | + To split the texture into multiple animation frames, this value must be set to the total number of frames in the texture. + see: + - columns + attachedUV: + field: bool + default: 1 + desc: | + Controls whether or not the UV of the trail should be attached to the node or not. If it is attached, the texture will slide along the segments to follow the source wherever it moves, as if it was a flag attached to a pole. If it is not attached, the texture will stay where it was when the segment was created, like a skid mark on a road where the road is the segments and the mark is the texture, it wouldn't follow the car/node that made it. + bloomRed: + field: float + default: 1 + desc: | + Controls the redness of the color of the additional bloom effect. The colors of the particle will be multiplied with this color to get the final color of the bloom effect. + + Note: + - This has no effect if the "Effects Quality" setting is set to "Low". + - This does not affect the natural bloom effect from high color values. + see: + - bloomGreen + - bloomBlue + - bloomStrength + bloomGreen: + field: float + default: 1 + desc: | + Controls the greenness of the color of the additional bloom effect. The colors of the particle will be multiplied with this color to get the final color of the bloom effect. + + Note: + - This has no effect if the "Effects Quality" setting is set to "Low". + - This does not affect the natural bloom effect from high color values. + see: + - bloomRed + - bloomBlue + - bloomStrength + bloomBlue: + field: float + default: 1 + desc: | + Controls the blueness of the color of the additional bloom effect. The colors of the particle will be multiplied with this color to get the final color of the bloom effect. + + Note: + - This has no effect if the "Effects Quality" setting is set to "Low". + - This does not affect the natural bloom effect from high color values. + see: + - bloomRed + - bloomGreen + - bloomStrength + bloomStrength: + field: float + desc: | + Controls the strength of the additional bloom effect. + + Note: + - This has no effect if the "Effects Quality" setting is set to "Low". + - This does not affect the natural bloom effect from high color values. + see: + - bloomRed + - bloomGreen + - bloomBlue + minDistance: + field: float + default: -1 + desc: | + Minimum view distance. If the trail is closer than this distance from the camera, it will be hidden. Can be set to -1 to disable the limit. + see: + - maxDistance + maxDistance: + field: float + default: -1 + desc: | + Maximum view distance. If the trail is farther away than this distance from the camera, it will be hidden. Can be set to -1 to disable the limit. + see: + - minDistance + shadowDarkness: + field: float + desc: | + Controls how dark shaded parts of the trail are. + specular: + field: int + desc: | + Specular texture ID. + see: + - lighting + - glossiness + - specularity + glossiness: + field: float + default: 0.25 + desc: | + Controls how sharp the specular highlights are. + see: + - lighting + - specular + - specularity + lighting: + type: LightingMode + field: int + default: LightingMode.Unlit + desc: | + Controls how the trail is lit. See {@link LightingMode} for more information. + specularity: + field: float + default: 0.5 + desc: | + Controls how bright the specular highlights are. + see: + - lighting + - specular + - glossiness + texture: + type: ScalarValue + field: int + default: 1 + argument: Constant0 + desc: | + Texture ID. + blendMode: + type: BlendMode | ScalarProperty + field: int + default: BlendMode.Normal + argument: Constant0 + desc: | + Blend mode. + width: + type: ScalarValue + default: 1 + argument: InstanceAge + desc: | + The width of the trail. + widthMultiplier: + type: ScalarValue + default: 1 + argument: InstanceAge + desc: | + Multiplier for {@link width}. + color1: + type: Vector4Value + default: [1, 1, 1, 1] + argument: InstanceAge + desc: | + Color multiplier. + color2: + type: Vector4Value + default: [1, 1, 1, 1] + argument: EmissionTime + desc: | + Color multiplier. + color3: + type: Vector4Value + default: [1, 1, 1, 1] + argument: InstanceAge + desc: | + Color multiplier. + alphaThreshold: + type: ScalarValue + argument: InstanceAge + desc: | + Parts of the particle with less opacity than this threshold will be invisible. The range is 0-255. + frameIndex: + type: ScalarValue + argument: InstanceAge + desc: | + The index of the frame to show from the texture atlas. Can be animated using a {@link PropertyFunction.Linear linear property} or similar. + + Seemingly identical to {@link frameIndexOffset}? The sum of these two properties is the actual frame index that gets used. + frameIndexOffset: + type: ScalarValue + argument: InstanceAge + desc: | + Seemingly identical to {@link frameIndex}? The sum of these two properties is the actual frame index that gets used. + textureFraction: + type: ScalarValue + default: 0.1 + argument: InstanceAge + desc: | + Controls how much of the texture's width is used per segment. If {@link attachedUV} is enabled, this instead controls how much of the texture's width to use for the entire trail. + speedU: + type: ScalarValue + argument: InstanceAge + desc: | + Controls how fast the UV coordinates should move horizontally. + varianceV: + type: ScalarValue + argument: InstanceAge + desc: | + Controls how much the UV coordinates should be randomly offset by per segment. + rgbMultiplier: + type: ScalarValue + default: 1 + argument: EffectAge + desc: | + Scalar multiplier for the color that does not affect the alpha. Effectively a brightness multiplier. + alphaMultiplier: + type: ScalarValue + default: 1 + argument: EffectAge + desc: | + Alpha multiplier. + unk_ds3_f1_7: + field: int + unk_ds3_f1_8: + field: int + unk_ds3_f1_9: + field: float + unk_ds3_f1_13: + field: int + default: -1 + unk_ds3_f1_14: + field: int + default: -1 + unk_ds3_f1_15: + field: int + unk_ds3_f2_0: + field: int + unk_ds3_f2_1: + field: bool + unk_ds3_f2_2: + field: int + default: 8 + unk_ds3_f2_3: + field: int + unk_ds3_f2_4: + field: int + default: 1 + unk_ds3_f2_9: + field: int + unk_ds3_f2_10: + field: int + unk_ds3_f2_11: + field: int + unk_ds3_f2_12: + field: int + unk_ds3_f2_13: + field: int + unkDistFadeClose0: + field: float + default: -1 + unkDistFadeClose1: + field: float + default: -1 + unkDistFadeFar0: + field: float + default: -1 + unkDistFadeFar1: + field: float + default: -1 + unk_ds3_f2_20: + field: int + unk_ds3_f2_21: + field: int + unk_ds3_f2_22: + field: int + unk_ds3_f2_23: + field: int + unk_ds3_f2_24: + field: int + unkDepthBlend1: + field: float + default: 1 + unkDepthBlend2: + field: float + unk_ds3_f2_27: + field: int + default: 1 + unk_ds3_f2_28: + field: int + unk_ds3_f2_29: + field: float + default: 5 + unk_ds3_p1_2: + type: ScalarValue + unk_ds3_p1_3: + type: ScalarValue + unk_ds3_p1_13: + type: ScalarValue + default: -1 + distortionIntensity: + type: ScalarValue + argument: EffectAge + desc: | + Controls the intensity of the distortion effect. At 0, there is no distortion at all. + see: + - normalMap + unk_ds3_p2_3: + type: Vector4Value + default: [1, 1, 1, 1] + unk_ds3_p2_4: + type: Vector4Value + default: [1, 1, 1, 1] + unk_ds3_p2_5: + type: Vector4Value + default: [1, 1, 1, 1] + unk_ds3_p2_6: + type: ScalarValue + unk_sdt_f2_31: + field: int + unk_sdt_f2_32: + field: int + default: 1 + unk_sdt_f2_36: + field: int + default: -2 + unk_sdt_f2_37: + field: int + unk_er_f1_18: + field: int + default: 1 + unk_er_f1_19: + field: int + default: 1 + unk_er_f1_20: + field: int + unk_er_f1_21: + field: int + unk_er_f2_39: + field: int + unk_er_f2_40: + field: float + default: 1 + unk_sdt_f1_14: + field: int + default: 1 + unk_sdt_f1_15: + field: float + default: 1 + unk_sdt_f1_16: + field: float + default: 1 + unk_sdt_f1_17: + field: float + default: 1 +games: + DS3: + fields1: + - orientation + - texture + - normalMap + - blendMode + - segmentInterval + - segmentDuration + - concurrentSegments + - unk_ds3_f1_7 + - unk_ds3_f1_8 + - unk_ds3_f1_9 + - columns + - totalFrames + - attachedUV + - unk_ds3_f1_13 + - unk_ds3_f1_14 + - unk_ds3_f1_15 + fields2: + - unk_ds3_f2_0 + - unk_ds3_f2_1 + - unk_ds3_f2_2 + - unk_ds3_f2_3 + - unk_ds3_f2_4 + - bloomRed + - bloomGreen + - bloomBlue + - bloomStrength + - unk_ds3_f2_9 + - unk_ds3_f2_10 + - unk_ds3_f2_11 + - unk_ds3_f2_12 + - unk_ds3_f2_13 + - unkDistFadeClose0 + - unkDistFadeClose1 + - unkDistFadeFar0 + - unkDistFadeFar1 + - minDistance + - maxDistance + - unk_ds3_f2_20 + - unk_ds3_f2_21 + - unk_ds3_f2_22 + - unk_ds3_f2_23 + - unk_ds3_f2_24 + - unkDepthBlend1 + - unkDepthBlend2 + - unk_ds3_f2_27 + - unk_ds3_f2_28 + - unk_ds3_f2_29 + properties1: + - width + - widthMultiplier + - unk_ds3_p1_2 + - unk_ds3_p1_3 + - color1 + - color2 + - color3 + - alphaThreshold + - frameIndex + - frameIndexOffset + - textureFraction + - speedU + - varianceV + - unk_ds3_p1_13 + properties2: + - rgbMultiplier + - alphaMultiplier + - distortionIntensity + - unk_ds3_p2_3 + - unk_ds3_p2_4 + - unk_ds3_p2_5 + - unk_ds3_p2_6 + SDT: + fields1: + - orientation + - normalMap + - segmentInterval + - segmentDuration + - concurrentSegments + - unk_ds3_f1_7 + - unk_ds3_f1_8 + - unk_ds3_f1_9 + - columns + - totalFrames + - attachedUV + - unk_ds3_f1_13 + - unk_ds3_f1_14 + - unk_ds3_f1_15 + - unk_sdt_f1_14 + - unk_sdt_f1_15 + - unk_sdt_f1_16 + - unk_sdt_f1_17 + fields2: + - unk_ds3_f2_0 + - unk_ds3_f2_1 + - unk_ds3_f2_2 + - unk_ds3_f2_3 + - unk_ds3_f2_4 + - bloomRed + - bloomGreen + - bloomBlue + - bloomStrength + - unk_ds3_f2_9 + - unk_ds3_f2_10 + - unk_ds3_f2_11 + - unk_ds3_f2_12 + - unk_ds3_f2_13 + - unkDistFadeClose0 + - unkDistFadeClose1 + - unkDistFadeFar0 + - unkDistFadeFar1 + - minDistance + - maxDistance + - unk_ds3_f2_20 + - unk_ds3_f2_21 + - unk_ds3_f2_22 + - unk_ds3_f2_23 + - unk_ds3_f2_24 + - unkDepthBlend1 + - unkDepthBlend2 + - unk_ds3_f2_27 + - unk_ds3_f2_28 + - unk_ds3_f2_29 + - shadowDarkness + - unk_sdt_f2_31 + - unk_sdt_f2_32 + - specular + - glossiness + - lighting + - unk_sdt_f2_36 + - unk_sdt_f2_37 + - specularity + properties1: + - texture + - blendMode + - width + - widthMultiplier + - unk_ds3_p1_2 + - unk_ds3_p1_3 + - color1 + - color2 + - color3 + - alphaThreshold + - frameIndex + - frameIndexOffset + - textureFraction + - speedU + - varianceV + - unk_ds3_p1_13 + properties2: DS3 + ER: + fields1: + - orientation + - normalMap + - segmentInterval + - segmentDuration + - concurrentSegments + - unk_ds3_f1_7 + - unk_ds3_f1_8 + - unk_ds3_f1_9 + - columns + - totalFrames + - attachedUV + - unk_ds3_f1_13 + - unk_ds3_f1_14 + - unk_ds3_f1_15 + - unk_sdt_f1_14 + - unk_sdt_f1_15 + - unk_sdt_f1_16 + - unk_sdt_f1_17 + - unk_er_f1_18 + - unk_er_f1_19 + - unk_er_f1_20 + - unk_er_f1_21 + fields2: + - unk_ds3_f2_0 + - unk_ds3_f2_1 + - unk_ds3_f2_2 + - unk_ds3_f2_3 + - unk_ds3_f2_4 + - bloomRed + - bloomGreen + - bloomBlue + - bloomStrength + - unk_ds3_f2_9 + - unk_ds3_f2_10 + - unk_ds3_f2_11 + - unk_ds3_f2_12 + - unk_ds3_f2_13 + - unkDistFadeClose0 + - unkDistFadeClose1 + - unkDistFadeFar0 + - unkDistFadeFar1 + - minDistance + - maxDistance + - unk_ds3_f2_20 + - unk_ds3_f2_21 + - unk_ds3_f2_22 + - unk_ds3_f2_23 + - unk_ds3_f2_24 + - unkDepthBlend1 + - unkDepthBlend2 + - unk_ds3_f2_27 + - unk_ds3_f2_28 + - unk_ds3_f2_29 + - shadowDarkness + - unk_sdt_f2_31 + - unk_sdt_f2_32 + - specular + - glossiness + - lighting + - unk_sdt_f2_36 + - unk_sdt_f2_37 + - specularity + - unk_er_f2_39 + - unk_er_f2_40 + properties1: SDT + properties2: DS3 + AC6: ER diff --git a/src/actions/10013.yml b/src/actions/10013.yml new file mode 100644 index 0000000..6956e4c --- /dev/null +++ b/src/actions/10013.yml @@ -0,0 +1,40 @@ +type: 10013 +name: WaterInteraction +desc: | + Simulates an interaction with water, allowing effects to create ripples in nearby water. The interaction basically pushes water in a shape controlled by a texture down to a given depth and holds it there for a duration before releasing it. +properties: + texture: + field: int + default: 50004 + desc: | + The ID for a texture that controls the shape of the interaction. + depth: + field: float + default: 1 + desc: | + Controls how deep to push the water, or how intense the ripples caused by the interaction are. + scale: + field: float + default: 1 + desc: | + Controls the size of the interaction area. Ripples caused by the interaction may go outside of the area. + descent: + field: float + default: 0.15 + desc: | + The time it takes for the water to be pushed down to the {@link depth} in seconds. + duration: + field: float + default: 0.15 + desc: | + The duration of the interaction in seconds. Basically how long to hold the water pressed down. +games: + SDT: + fields1: + - texture + - depth + - scale + - descent + - duration + ER: SDT + AC6: SDT diff --git a/src/actions/10015.yml b/src/actions/10015.yml new file mode 100644 index 0000000..c3b8290 --- /dev/null +++ b/src/actions/10015.yml @@ -0,0 +1,706 @@ +type: 10015 +name: RichModel +desc: | + Particle with a 3D model. Similar to {@link Model}, but with some different options and seemingly no way to change the blend mode. +properties: + orientation: + type: OrientationMode + field: int + default: OrientationMode.LocalSouth + desc: | + Controls the orientation mode for the particles. See {@link OrientationMode} for more information. + scaleVariationX: + field: float + default: 1 + desc: | + Each particle will pick a random number between this value and 1, and the width of the particle will be multiplied by this number. For example, setting this to 0.5 will make the particles randomly thinner, down to half width. Setting it to 2 will make them randomly wider, up to double width. + + If {@link uniformScale} is enabled, this also affects the height. + see: + - scaleVariationY + - scaleVariationZ + scaleVariationY: + field: float + default: 1 + desc: | + Each particle will pick a random number between this value and 1, and the height of the particle will be multiplied by this number. For example, setting this to 0.5 will make the particles randomly shorter, down to half height. Setting it to 2 will make them randomly taller, up to double height. + + If {@link uniformScale} is enabled, {@link scaleVariationX} also affects the height, and this field is ignored. + see: + - scaleVariationX + - scaleVariationZ + scaleVariationZ: + field: float + default: 1 + desc: | + Each particle will pick a random number between this value and 1, and the depth of the particle will be multiplied by this number. For example, setting this to 0.5 will make the particles randomly shallower, down to half depth. Setting it to 2 will make them randomly deeper, up to double depth. + + If {@link uniformScale} is enabled, {@link scaleVariationX} also affects the depth, and this field is ignored. + see: + - scaleVariationX + - scaleVariationY + uniformScale: + field: bool + default: false + desc: | + If enabled, the particle X scale-related properties and fields will control the scale in all axes, and the Y and Z counterparts will be ignored. + see: + - sizeX + - sizeY + - sizeZ + - scaleVariationX + - scaleVariationY + - scaleVariationZ + bloomRed: + field: float + default: 1 + desc: | + Controls the redness of the color of the additional bloom effect. The colors of the particle will be multiplied with this color to get the final color of the bloom effect. + + Note: + - This has no effect if the "Effects Quality" setting is set to "Low". + - This does not affect the natural bloom effect from high color values. + see: + - bloomGreen + - bloomBlue + - bloomStrength + bloomGreen: + field: float + default: 1 + desc: | + Controls the greenness of the color of the additional bloom effect. The colors of the particle will be multiplied with this color to get the final color of the bloom effect. + + Note: + - This has no effect if the "Effects Quality" setting is set to "Low". + - This does not affect the natural bloom effect from high color values. + see: + - bloomRed + - bloomBlue + - bloomStrength + bloomBlue: + field: float + default: 1 + desc: | + Controls the blueness of the color of the additional bloom effect. The colors of the particle will be multiplied with this color to get the final color of the bloom effect. + + Note: + - This has no effect if the "Effects Quality" setting is set to "Low". + - This does not affect the natural bloom effect from high color values. + see: + - bloomRed + - bloomGreen + - bloomStrength + bloomStrength: + field: float + desc: | + Controls the strength of the additional bloom effect. + + Note: + - This has no effect if the "Effects Quality" setting is set to "Low". + - This does not affect the natural bloom effect from high color values. + see: + - bloomRed + - bloomGreen + - bloomBlue + minDistance: + field: float + default: -1 + desc: | + Minimum view distance. If the particle is closer than this distance from the camera, it will be hidden. Can be set to -1 to disable the limit. + see: + - maxDistance + maxDistance: + field: float + default: -1 + desc: | + Maximum view distance. If the particle is farther away than this distance from the camera, it will be hidden. Can be set to -1 to disable the limit. + see: + - minDistance + model: + type: ScalarValue + field: int + default: 80201 + argument: Constant0 + desc: | + Model ID. + sizeX: + type: ScalarValue + default: 1 + argument: InstanceAge + desc: | + The width of the particle. + + If {@link uniformScale} is enabled, this also controls the height and depth. + see: + - scaleVariationX + - sizeY + - sizeZ + sizeY: + type: ScalarValue + default: 1 + argument: InstanceAge + desc: | + The height of the particle. + + If {@link uniformScale} is enabled, {@link sizeX} also controls the height, and this property is ignored. + see: + - scaleVariationY + - sizeX + - sizeZ + sizeZ: + type: ScalarValue + default: 1 + argument: InstanceAge + desc: | + The depth of the particle. + + If {@link uniformScale} is enabled, {@link sizeX} also controls the depth, and this property is ignored. + see: + - scaleVariationZ + - sizeX + - sizeY + rotationX: + type: ScalarValue + argument: Constant0 + desc: | + Rotation around the X-axis in degrees. + see: + - rotationSpeedX + - rotationSpeedMultiplierX + rotationY: + type: ScalarValue + argument: Constant0 + desc: | + Rotation around the Y-axis in degrees. + see: + - rotationSpeedY + - rotationSpeedMultiplierY + rotationZ: + type: ScalarValue + argument: Constant0 + desc: | + Rotation around the Z-axis in degrees. + see: + - rotationSpeedZ + - rotationSpeedMultiplierZ + rotationSpeedX: + type: ScalarValue + argument: InstanceAge + desc: | + Rotation speed around the X-axis in degrees per second. + see: + - rotationX + - rotationSpeedMultiplierX + rotationSpeedY: + type: ScalarValue + argument: InstanceAge + desc: | + Rotation speed around the Y-axis in degrees per second. + see: + - rotationY + - rotationSpeedMultiplierY + rotationSpeedZ: + type: ScalarValue + argument: InstanceAge + desc: | + Rotation speed around the Z-axis in degrees per second. + see: + - rotationZ + - rotationSpeedMultiplierZ + rotationSpeedMultiplierX: + type: ScalarValue + default: 1 + argument: InstanceAge + desc: | + Multiplier for {@link rotationSpeedX}. + see: + - rotationX + rotationSpeedMultiplierY: + type: ScalarValue + default: 1 + argument: InstanceAge + desc: | + Multiplier for {@link rotationSpeedY}. + see: + - rotationY + rotationSpeedMultiplierZ: + type: ScalarValue + default: 1 + argument: InstanceAge + desc: | + Multiplier for {@link rotationSpeedZ}. + see: + - rotationZ + color1: + type: Vector4Value + default: [1, 1, 1, 1] + argument: InstanceAge + desc: | + Color multiplier for the particle. + color2: + type: Vector4Value + default: [1, 1, 1, 1] + argument: EmissionTime + desc: | + Color multiplier for the particle. + color3: + type: Vector4Value + default: [1, 1, 1, 1] + argument: InstanceAge + desc: | + Color multiplier for the particle. + uOffset: + omitClassProp: true + type: ScalarValue + default: 0 + argument: Constant0 + desc: | + Horizontal offset for the UV coordinates of the model. + see: + - uSpeed + - vOffset + vOffset: + omitClassProp: true + type: ScalarValue + default: 0 + argument: Constant0 + desc: | + Vertical offset for the UV coordinates of the model. + uSpeed: + omitClassProp: true + type: ScalarValue + default: 0 + argument: InstanceAge + desc: | + Horiztonal scroll speed for the model's texture. + see: + - uSpeedMultiplier + - uOffset + uSpeedMultiplier: + omitClassProp: true + type: ScalarValue + default: 0 + argument: InstanceAge + desc: | + Multiplier for {@link uSpeed}. + vSpeed: + omitClassProp: true + type: ScalarValue + default: 0 + argument: InstanceAge + desc: | + Vertical scroll speed for the model's texture. + see: + - vSpeedMultiplier + - vOffset + vSpeedMultiplier: + omitClassProp: true + type: ScalarValue + default: 0 + argument: InstanceAge + desc: | + Multiplier for {@link vSpeed}. + rgbMultiplier: + type: ScalarValue + default: 1 + argument: EffectAge + desc: | + Scalar multiplier for the color that does not affect the alpha. Effectively a brightness multiplier. + alphaMultiplier: + type: ScalarValue + default: 1 + argument: EffectAge + desc: | + Alpha multiplier. + animation: + field: int + desc: | + Animation ID. + see: + - loopAnimation + - animationSpeed + loopAnimation: + field: bool + default: 1 + desc: | + If disabled, the animation will only play once and then freeze on the last frame. If enabled, the animation will loop. + see: + - animation + - animationSpeed + animationSpeed: + field: float + default: 1 + desc: | + Controls the speed at which the {@link animation} plays. + see: + - animation + - loopAnimation + unk_er_f1_5: + field: int + default: 1 + unk_er_f1_6: + field: int + default: 1 + unk_er_f1_7: + field: int + unk_er_f1_8: + field: int + default: -2 + unk_er_f1_9: + field: int + default: -2 + unk_er_f1_11: + field: int + desc: | + Unknown. Probably a boolean. Seems to just disable the {@link animation} if set to anything but 0. + unk_er_f1_14: + field: int + unk_er_f1_15: + field: int + unk_er_f1_16: + field: int + unk_er_f1_17: + field: int + unk_er_f1_18: + field: int + unk_er_f1_19: + field: int + unk_er_f1_20: + field: int + unk_er_f1_21: + field: int + unk_er_f1_22: + field: int + unk_er_f1_23: + field: int + unk_er_f1_24: + field: int + unk_er_f1_25: + field: int + default: 1 + unk_er_f2_0: + field: int + unk_er_f2_1: + field: int + unk_er_f2_2: + field: int + default: 8 + unk_er_f2_3: + field: int + unk_er_f2_8: + field: int + unk_er_f2_9: + field: int + unk_er_f2_10: + field: int + unk_er_f2_11: + field: int + unk_er_f2_12: + field: int + unk_er_f2_13: + field: int + unkDistFadeClose0: + field: float + default: -1 + unkDistFadeClose1: + field: float + default: -1 + unkDistFadeFar0: + field: float + default: -1 + unkDistFadeFar1: + field: float + default: -1 + unk_er_f2_20: + field: int + unk_er_f2_21: + field: int + unk_er_f2_22: + field: int + unk_er_f2_23: + field: int + unk_er_f2_24: + field: int + unkDepthBlend1: + field: float + default: 1 + unkDepthBlend2: + field: float + unk_er_f2_27: + field: int + unk_er_f2_28: + field: int + default: 1 + unk_er_f2_29: + field: int + unk_er_f2_30: + field: int + unk_er_f2_31: + field: float + default: 1 + unk_er_f2_32: + field: int + unk_er_f2_33: + field: int + unk_er_f2_34: + field: float + default: 0.5 + unk_er_f2_35: + field: int + default: -2 + unk_er_f2_36: + field: int + default: -2 + unk_er_f2_37: + field: int + unk_er_p1_16: + type: ScalarValue + unk_er_p1_17: + type: ScalarValue + rgbMultiplier2: + type: ScalarValue + default: 1 + argument: EffectAge + desc: | + Seemingly identical to {@link rgbMultiplier}? + unk_er_p1_19: + type: ScalarValue + unk_er_p1_20: + type: ScalarValue + unk_ds3_p2_2: + type: ScalarValue + unk_ds3_p2_3: + type: Vector4Value + default: [1, 1, 1, 1] + unk_ds3_p2_4: + type: Vector4Value + default: [1, 1, 1, 1] + unk_ds3_p2_5: + type: Vector4Value + default: [1, 1, 1, 1] + unk_ds3_p2_6: + type: ScalarValue + unk_ac6_f1_24: + field: float + unk_ac6_f1_25: + field: float + default: -1 + unk_ac6_f1_26: + field: float + default: -1 + unk_ac6_f1_27: + field: float + default: -1 + unk_ac6_f1_28: + field: float + default: -1 + unk_ac6_f1_29: + field: int + unk_ac6_f1_30: + field: int + unk_ac6_f1_31: + field: int + unk_ac6_f1_32: + field: int + unk_ac6_f1_33: + field: int + default: 1 + unk_ac6_f1_34: + field: int + uvOffset: + type: Vector2Value + default: [0, 0] + argument: Constant0 + desc: | + Offset for the UV coordinates of the model. + see: + - uvSpeed + uvSpeed: + type: Vector2Value + default: [0, 0] + argument: InstanceAge + desc: | + Scroll speed for the model's texture. + see: + - uvSpeedMultiplier + uvSpeedMultiplier: + type: Vector2Value + default: [1, 1] + argument: InstanceAge + desc: | + Multiplier for {@link uvSpeed} +games: + ER: + fields1: + - orientation + - scaleVariationX + - scaleVariationY + - scaleVariationZ + - uniformScale + - unk_er_f1_5 + - unk_er_f1_6 + - unk_er_f1_7 + - unk_er_f1_8 + - unk_er_f1_9 + - animation + - unk_er_f1_11 + - loopAnimation + - animationSpeed + - unk_er_f1_14 + - unk_er_f1_15 + - unk_er_f1_16 + - unk_er_f1_17 + - unk_er_f1_18 + - unk_er_f1_19 + - unk_er_f1_20 + - unk_er_f1_21 + - unk_er_f1_22 + - unk_er_f1_23 + - unk_er_f1_24 + - unk_er_f1_25 + fields2: + - unk_er_f2_0 + - unk_er_f2_1 + - unk_er_f2_2 + - unk_er_f2_3 + - bloomRed + - bloomGreen + - bloomBlue + - bloomStrength + - unk_er_f2_8 + - unk_er_f2_9 + - unk_er_f2_10 + - unk_er_f2_11 + - unk_er_f2_12 + - unk_er_f2_13 + - unkDistFadeClose0 + - unkDistFadeClose1 + - unkDistFadeFar0 + - unkDistFadeFar1 + - minDistance + - maxDistance + - unk_er_f2_20 + - unk_er_f2_21 + - unk_er_f2_22 + - unk_er_f2_23 + - unk_er_f2_24 + - unkDepthBlend1 + - unkDepthBlend2 + - unk_er_f2_27 + - unk_er_f2_28 + - unk_er_f2_29 + - unk_er_f2_30 + - unk_er_f2_31 + - unk_er_f2_32 + - unk_er_f2_33 + - unk_er_f2_34 + - unk_er_f2_35 + - unk_er_f2_36 + - unk_er_f2_37 + properties1: + - model + - sizeX + - sizeY + - sizeZ + - rotationX + - rotationY + - rotationZ + - rotationSpeedX + - rotationSpeedMultiplierX + - rotationSpeedY + - rotationSpeedMultiplierY + - rotationSpeedZ + - rotationSpeedMultiplierZ + - color1 + - color2 + - color3 + - unk_er_p1_16 + - unk_er_p1_17 + - rgbMultiplier2 + - unk_er_p1_19 + - unk_er_p1_20 + - uOffset + - vOffset + - uSpeed + - uSpeedMultiplier + - vSpeed + - vSpeedMultiplier + properties2: + - rgbMultiplier + - alphaMultiplier + - unk_ds3_p2_2 + - unk_ds3_p2_3 + - unk_ds3_p2_4 + - unk_ds3_p2_5 + - unk_ds3_p2_6 + AC6: + fields1: + - orientation + - scaleVariationX + - scaleVariationY + - scaleVariationZ + - uniformScale + - unk_er_f1_5 + - unk_er_f1_6 + - unk_er_f1_7 + - unk_er_f1_8 + - unk_er_f1_9 + - animation + - unk_er_f1_11 + - loopAnimation + - animationSpeed + - unk_er_f1_14 + - unk_er_f1_15 + - unk_er_f1_16 + - unk_er_f1_17 + - unk_er_f1_18 + - unk_er_f1_19 + - unk_er_f1_20 + - unk_er_f1_21 + - unk_er_f1_22 + - unk_er_f1_23 + - unk_ac6_f1_24 + - unk_ac6_f1_25 + - unk_ac6_f1_26 + - unk_ac6_f1_27 + - unk_ac6_f1_28 + - unk_ac6_f1_29 + - unk_ac6_f1_30 + - unk_ac6_f1_31 + - unk_ac6_f1_32 + - unk_ac6_f1_33 + - unk_ac6_f1_34 + fields2: ER + properties1: + - model + - sizeX + - sizeY + - sizeZ + - rotationX + - rotationY + - rotationZ + - rotationSpeedX + - rotationSpeedMultiplierX + - rotationSpeedY + - rotationSpeedMultiplierY + - rotationSpeedZ + - rotationSpeedMultiplierZ + - color1 + - color2 + - color3 + - unk_er_p1_16 + - unk_er_p1_17 + - rgbMultiplier2 + - unk_er_p1_19 + - unk_er_p1_20 + - uvOffset + - uvSpeed + - uvSpeedMultiplier + properties2: + - rgbMultiplier + - alphaMultiplier + - unk_ds3_p2_2 + - unk_ds3_p2_3 + - unk_ds3_p2_4 + - unk_ds3_p2_5 + - unk_ds3_p2_6 diff --git a/src/actions/10500.yml b/src/actions/10500.yml new file mode 100644 index 0000000..1f121da --- /dev/null +++ b/src/actions/10500.yml @@ -0,0 +1,82 @@ +type: 10500 +name: Unk10500 +desc: > + Unknown root node action. +properties: + rateOfTime: + type: ScalarValue + field: float + default: 1 + argument: EffectAge + desc: > + Controls how fast time passes for the entire effect. + unk_ds3_f1_0: + field: int + default: 0 + unk_ds3_f1_1: + field: float + default: 0 + unk_ds3_f1_2: + field: int + default: 0 + unk_ds3_f1_3: + field: float + default: 0 + unk_ds3_f1_4: + field: float + default: 0 + unk_ds3_f1_5: + field: float + default: 0 + unk_ds3_f1_6: + field: int + default: 0 + unk_ds3_f1_7: + field: int + default: 0 + unk_ds3_f1_8: + field: int + default: 0 + unk_sdt_f1_9: + field: int + default: 0 +games: + DS3: + fields1: + - unk_ds3_f1_0 + - unk_ds3_f1_1 + - unk_ds3_f1_2 + - unk_ds3_f1_3 + - unk_ds3_f1_4 + - unk_ds3_f1_5 + - unk_ds3_f1_6 + - unk_ds3_f1_7 + - unk_ds3_f1_8 + SDT: + fields1: + - unk_ds3_f1_0 + - unk_ds3_f1_1 + - unk_ds3_f1_2 + - unk_ds3_f1_3 + - unk_ds3_f1_4 + - unk_ds3_f1_5 + - unk_ds3_f1_6 + - unk_ds3_f1_7 + - unk_ds3_f1_8 + - unk_sdt_f1_9 + - rateOfTime + ER: + fields1: + - unk_ds3_f1_0 + - unk_ds3_f1_1 + - unk_ds3_f1_2 + - unk_ds3_f1_3 + - unk_ds3_f1_4 + - unk_ds3_f1_5 + - unk_ds3_f1_6 + - unk_ds3_f1_7 + - unk_ds3_f1_8 + - unk_sdt_f1_9 + properties1: + - rateOfTime + AC6: ER diff --git a/src/actions/11000.yml b/src/actions/11000.yml new file mode 100644 index 0000000..af6fee0 --- /dev/null +++ b/src/actions/11000.yml @@ -0,0 +1,364 @@ +type: 11000 +name: SpotLight +desc: | + Light source with an elliptic cone shape, a spot light. +properties: + diffuseColor: + type: Vector4Value + default: [1, 1, 1, 1] + argument: EffectAge + desc: | + Controls the diffuse color of the light. + + If {@link separateSpecular} is disabled, this also controls the specular color of the light. + specularColor: + type: Vector4Value + default: [1, 1, 1, 1] + argument: EffectAge + desc: | + Controls the specular color of the light. + + If {@link separateSpecular} is disabled, this property is ignored and {@link diffuseColor} controls both the diffuse as well as the specular color. + diffuseMultiplier: + type: ScalarValue + default: 1 + argument: EffectAge + desc: | + A scalar multiplier for the {@link diffuseColor diffuse color}. Good for easily adjusting the brightness of the light without changing the color. + + If {@link separateSpecular} is disabled, this also affects the specular color of the light. + specularMultiplier: + type: ScalarValue + default: 1 + argument: EffectAge + desc: | + A scalar multiplier for the {@link specularColor specular color}. + + If {@link separateSpecular} is disabled, this property is ignored. + near: + type: ScalarValue + default: 0.01 + argument: EffectAge + desc: | + Controls where the light starts in the cone. It bascially "slices off" the tip of the cone. If set to 0, it acts as if it is set to 0.5. + far: + type: ScalarValue + default: 50 + argument: EffectAge + desc: | + Controls how far away the base of the cone is from the light source. + radiusX: + type: ScalarValue + default: 50 + argument: EffectAge + desc: | + The X radius for the elliptic base of the cone. + radiusY: + type: ScalarValue + default: 50 + argument: EffectAge + desc: | + The Y radius for the elliptic base of the cone. + jitterAndFlicker: + field: bool + default: false + desc: | + Toggles the jitter and flicker animations for the light. + see: + - jitterAcceleration + - jitterX + - jitterY + - jitterZ + - flickerIntervalMin + - flickerIntervalMax + - flickerBrightness + jitterAcceleration: + field: float + default: 1 + desc: | + Controls the acceleration of the jittering. + see: + - jitterAndFlicker + - jitterX + - jitterY + - jitterZ + jitterX: + field: float + desc: | + Controls how much the light should move around randomly on the X-axis. + see: + - jitterAndFlicker + - jitterAcceleration + - jitterY + - jitterZ + jitterY: + field: float + desc: | + Controls how much the light should move around randomly on the Y-axis. + see: + - jitterAndFlicker + - jitterAcceleration + - jitterX + - jitterZ + jitterZ: + field: float + desc: | + Controls how much the light should move around randomly on the Z-axis. + see: + - jitterAndFlicker + - jitterAcceleration + - jitterX + - jitterY + flickerIntervalMin: + field: float + desc: | + Controls the minimum interval for flickering. + see: + - jitterAndFlicker + - flickerIntervalMax + - flickerBrightness + flickerIntervalMax: + field: float + default: 1 + desc: | + Controls the maximum interval for flickering. + see: + - jitterAndFlicker + - flickerIntervalMin + - flickerBrightness + flickerBrightness: + field: float + default: 0.5 + desc: | + Brightness multiplier for the light when it flickers. + see: + - jitterAndFlicker + - flickerIntervalMin + - flickerIntervalMax + shadows: + field: bool + default: false + desc: | + Controls if the light should have shadows or not. + + Note: Map objects also have a setting for casting shadows, and both must be enabled for an object to cast shadows from the light source. + separateSpecular: + field: bool + default: false + desc: | + When enabled, this allows other properties and fields of the action to control the specular color independently of the diffuse color. When disabled, the diffuse counterpart of the properties or fields will affect both the diffuse and specular color. + see: + - diffuseColor + - specularColor + - diffuseMultiplier + - specularMultiplier + fadeOutTime: + field: int + default: 0 + desc: | + The number of seconds the light takes to fade to nothing after being destroyed. + + Due to how the field this represents works, the time will be rounded to the nearest multiple of 1/30s. + shadowDarkness: + field: float + default: 1 + desc: | + Controls how dark shadows from this light source are. At 0, the shadows will be entirely invisible. + volumeDensity: + field: float + default: 0 + desc: | + Controls the density of some sort of fake fog in the volume hit by the light. The fog does not affect the actual light produced by the source and is not affected by shadows. + see: + - phaseFunction + - asymmetryParam + phaseFunction: + field: bool + default: true + desc: | + Controls whether or not {@link asymmetryParam} affects the fake fog from {@link volumeDensity}. + asymmetryParam: + field: float + default: 0.75 + desc: | + Controls how the fake fog from {@link volumeDensity} scatters the light. This value is ignored if {@link phaseFunction} is disabled, and the fog will scatter the light equally in all directions. + + - At 0, the light is scattered equally in every direction. + - As the value approaches 1, the light is scattered more and more forward, in the same direction as the light was already traveling. This means that the fake fog will be less visible from the side or behind, and more visible from in front of the light. + - At 1, the fog will not scatter the light at all, so it will be entirely invisible. + - Values above 1 produce unnatural-looking effects where the light darkens the fog instead. + falloffExponent: + field: float + default: 1 + desc: | + Controls the falloff exponent of the light. + + Note: This is possibly something else, but the behavior is pretty similar to a falloff exponent in a few ways. + unk_ds3_f1_0: + field: int + default: 1 + unk_ds3_f1_3: + field: int + default: 2 + unk_ds3_f1_4: + field: int + default: 1 + unk_ds3_f1_5: + field: float + default: 1 + unk_ds3_f1_7: + field: int + unk_ds3_f1_8: + field: int + unk_ds3_p1_6: + type: ScalarValue + default: 1 + unk_ds3_p1_7: + type: ScalarValue + default: 1 + unk_sdt_f1_0: + field: int + unk_sdt_f1_3: + field: float + unk_sdt_f1_16: + field: int + default: 100 + unk_sdt_f1_17: + field: int + unk_sdt_f1_18: + field: float + unk_sdt_f1_20: + field: float + unk_sdt_p1_10: + type: ScalarValue + default: 1 + unk_er_f1_24: + field: int + default: 1 + unk_er_f1_25: + field: float + default: 1 + unk_ac6_f1_26: + field: int + default: 1 + unk_ac6_f1_27: + field: int +games: + DS3: + fields1: + - unk_ds3_f1_0 + - shadows + - shadowDarkness + - unk_ds3_f1_3 + - unk_ds3_f1_4 + - unk_ds3_f1_5 + - fadeOutTime + - unk_ds3_f1_7 + - unk_ds3_f1_8 + properties1: + - diffuseColor + - specularColor + - near + - far + - radiusX + - radiusY + - unk_ds3_p1_6 + - unk_ds3_p1_7 + SDT: + fields1: + - unk_sdt_f1_0 + - jitterAndFlicker + - jitterAcceleration + - unk_sdt_f1_3 + - jitterX + - jitterY + - jitterZ + - flickerIntervalMin + - flickerIntervalMax + - flickerBrightness + - shadows + - separateSpecular + - shadowDarkness + - unk_ds3_f1_3 + - unk_ds3_f1_4 + - fadeOutTime + - unk_sdt_f1_16 + - unk_sdt_f1_17 + - unk_sdt_f1_18 + - volumeDensity + - unk_sdt_f1_20 + - phaseFunction + - asymmetryParam + - falloffExponent + properties1: + - diffuseColor + - specularColor + - diffuseMultiplier + - specularMultiplier + - near + - far + - radiusX + - radiusY + - unk_ds3_p1_6 + - unk_ds3_p1_7 + - unk_sdt_p1_10 + ER: + fields1: + - unk_sdt_f1_0 + - jitterAndFlicker + - jitterAcceleration + - unk_sdt_f1_3 + - jitterX + - jitterY + - jitterZ + - flickerIntervalMin + - flickerIntervalMax + - flickerBrightness + - shadows + - separateSpecular + - shadowDarkness + - unk_ds3_f1_3 + - unk_ds3_f1_4 + - fadeOutTime + - unk_sdt_f1_16 + - unk_sdt_f1_17 + - unk_sdt_f1_18 + - volumeDensity + - unk_sdt_f1_20 + - phaseFunction + - asymmetryParam + - falloffExponent + - unk_er_f1_24 + - unk_er_f1_25 + properties1: SDT + AC6: + fields1: + - unk_sdt_f1_0 + - jitterAndFlicker + - jitterAcceleration + - unk_sdt_f1_3 + - jitterX + - jitterY + - jitterZ + - flickerIntervalMin + - flickerIntervalMax + - flickerBrightness + - shadows + - separateSpecular + - shadowDarkness + - unk_ds3_f1_3 + - unk_ds3_f1_4 + - fadeOutTime + - unk_sdt_f1_16 + - unk_sdt_f1_17 + - unk_sdt_f1_18 + - volumeDensity + - unk_sdt_f1_20 + - phaseFunction + - asymmetryParam + - falloffExponent + - unk_er_f1_24 + - unk_er_f1_25 + - unk_ac6_f1_26 + - unk_ac6_f1_27 + properties1: SDT diff --git a/src/actions/128.yml b/src/actions/128.yml new file mode 100644 index 0000000..d1c05b9 --- /dev/null +++ b/src/actions/128.yml @@ -0,0 +1,38 @@ +type: 128 +name: NodeAttributes +desc: | + Controls various things about the node, like its duration, and how it is attached to the parent node. +properties: + attachment: + type: AttachMode + field: int + default: AttachMode.Parent + desc: | + Controls how the node is attached to the parent node. + duration: + type: ScalarValue + default: -1 + argument: Constant0 + desc: | + The node duration in seconds. Can be set to -1 to make the node last forever. + delay: + field: float + desc: | + The delay in seconds before the node becomes active. + unk_ds3_f1_1: + field: int + default: 1 + unk_ds3_f1_3: + field: float +games: + DS3: + fields1: + - delay + - unk_ds3_f1_1 + - attachment + - unk_ds3_f1_3 + properties1: + - duration + SDT: DS3 + ER: DS3 + AC6: DS3 diff --git a/src/actions/129.yml b/src/actions/129.yml new file mode 100644 index 0000000..1ae9b4c --- /dev/null +++ b/src/actions/129.yml @@ -0,0 +1,26 @@ +type: 129 +name: ParticleAttributes +desc: | + Controls the duration of particles emitted by the node, and how the particles are attached to the node. +properties: + attachment: + type: AttachMode + field: int + default: AttachMode.Parent + desc: | + Controls how the particles are attached to the node. + duration: + type: ScalarValue + default: -1 + argument: Constant0 + desc: | + The particle duration in seconds. Can be set to -1 to make particles last forever. +games: + DS3: + fields1: + - attachment + properties1: + - duration + SDT: DS3 + ER: DS3 + AC6: DS3 diff --git a/src/actions/130.yml b/src/actions/130.yml new file mode 100644 index 0000000..66bb3ab --- /dev/null +++ b/src/actions/130.yml @@ -0,0 +1,64 @@ +type: 130 +name: Unk130 +desc: | + Unknown action that is in every basic effect in every game, and still literally nothing is known about it. +properties: + unk_ds3_f1_0: + field: int + default: 1 + unk_ds3_f1_1: + field: int + unk_ds3_f1_2: + field: int + unk_ds3_f1_3: + field: int + unk_ds3_f1_4: + field: int + unk_ds3_f1_5: + field: int + unk_ds3_f1_6: + field: int + unk_ds3_f1_7: + field: int + unk_ds3_f1_8: + field: int + unk_ds3_p1_0: + type: ScalarValue + unk_ds3_p1_1: + type: ScalarValue + unk_ds3_p1_2: + type: ScalarValue + unk_ds3_p1_3: + type: ScalarValue + unk_ds3_p1_4: + type: ScalarValue + unk_ds3_p1_5: + type: ScalarValue + unk_ds3_p1_6: + type: ScalarValue + unk_ds3_p1_7: + type: ScalarValue +games: + DS3: + fields1: + - unk_ds3_f1_0 + - unk_ds3_f1_1 + - unk_ds3_f1_2 + - unk_ds3_f1_3 + - unk_ds3_f1_4 + - unk_ds3_f1_5 + - unk_ds3_f1_6 + - unk_ds3_f1_7 + - unk_ds3_f1_8 + properties1: + - unk_ds3_p1_0 + - unk_ds3_p1_1 + - unk_ds3_p1_2 + - unk_ds3_p1_3 + - unk_ds3_p1_4 + - unk_ds3_p1_5 + - unk_ds3_p1_6 + - unk_ds3_p1_7 + SDT: DS3 + ER: DS3 + AC6: DS3 diff --git a/src/actions/131.yml b/src/actions/131.yml new file mode 100644 index 0000000..46c645b --- /dev/null +++ b/src/actions/131.yml @@ -0,0 +1,64 @@ +type: 131 +name: ParticleModifier +desc: | + Modifies particles in various ways. + + Note: This is **not** a {@link Modifier property modifier}, it is an action that modifies particles emitted from the same node. +properties: + uniformScale: + field: bool + default: false + desc: | + Scales the particles emitted from this node uniformly based on {@link scaleX}. The other scale properties in this action have no effect when this is enabled. + see: + - scaleX + - scaleY + - scaleZ + speed: + type: ScalarValue + argument: EffectAge + desc: | + Controls the speed of the particles emitted from this node, but only if the effect has an action in slot 10 that enables acceleration of particles. The direction is the {@link InitialDirection initial particle direction}. + scaleX: + type: ScalarValue + default: 1 + argument: EffectAge + desc: | + Multiplier for the scale along the X-axis for the particles emitted from this node. + + If {@link uniformScale} is enabled, this also affects the Y and Z axes. + scaleY: + type: ScalarValue + default: 1 + argument: EffectAge + desc: | + Multiplier for the scale along the Y-axis for the particles emitted from this node. + + If {@link uniformScale} is enabled, {@link scaleX} also affects the Y-axis, and this property is ignored. + scaleZ: + type: ScalarValue + default: 1 + argument: EffectAge + desc: | + Multiplier for the scale along the Z-axis for the particles emitted from this node. + + If {@link uniformScale} is enabled, {@link scaleX} also affects the Z-axis, and this property is ignored. + color: + type: Vector4Value + default: 1 + argument: EffectAge + desc: | + Color multiplier for the particles emitted from this node. +games: + DS3: + fields1: + - uniformScale + properties1: + - speed + - scaleX + - scaleY + - scaleZ + - color + SDT: DS3 + ER: DS3 + AC6: DS3 diff --git a/src/actions/132.yml b/src/actions/132.yml new file mode 100644 index 0000000..83ebb27 --- /dev/null +++ b/src/actions/132.yml @@ -0,0 +1,18 @@ +type: 132 +name: SFXReference +desc: | + References another SFX by its ID. +omitClass: true +properties: + sfx: + field: int + default: 0 + desc: | + The ID of the referenced SFX. +games: + DS3: + fields1: + - sfx + SDT: DS3 + ER: DS3 + AC6: DS3 diff --git a/src/actions/133.yml b/src/actions/133.yml new file mode 100644 index 0000000..1807484 --- /dev/null +++ b/src/actions/133.yml @@ -0,0 +1,49 @@ +type: 133 +name: LevelOfDetailThresholds +desc: | + Used in the {@link EffectType.LevelOfDetail level of detail effect} to manage the duration and thresholds for the {@link NodeType.LevelOfDetail level of detail node}. +properties: + duration: + type: ScalarValue + default: -1 + argument: Constant0 + desc: | + The node duration in seconds. Can be set to -1 to make the node last forever. + threshold0: + field: int + default: 1000 + desc: | + Distance threshold for child node 0. + threshold1: + field: int + default: 1000 + desc: | + Distance threshold for child node 1. + threshold2: + field: int + default: 1000 + desc: | + Distance threshold for child node 2. + threshold3: + field: int + default: 1000 + desc: | + Distance threshold for child node 3. + threshold4: + field: int + default: 1000 + desc: | + Distance threshold for child node 4. +games: + DS3: + fields1: + - threshold0 + - threshold1 + - threshold2 + - threshold3 + - threshold4 + properties1: + - duration + SDT: DS3 + ER: DS3 + AC6: DS3 diff --git a/src/actions/15.yml b/src/actions/15.yml new file mode 100644 index 0000000..43d4c4c --- /dev/null +++ b/src/actions/15.yml @@ -0,0 +1,25 @@ +type: 15 +name: NodeTranslation +desc: | + Translates the node using a property, meaning it can be animated. This can be useful if you need the node to follow a specific path. +properties: + translation: + type: Vector3Value + default: [0, 0, 0] + argument: EffectAge + desc: | + An offset for the position of the node. + unk_er_f1_0: + field: int + desc: | + Unknown. An integer that has at least three valid values: 0, 1, 2. It did not exist until Elden Ring. +games: + DS3: + properties1: + - translation + SDT: DS3 + ER: + fields1: + - unk_er_f1_0 + properties1: DS3 + AC6: ER diff --git a/src/actions/300.yml b/src/actions/300.yml new file mode 100644 index 0000000..97cb5d3 --- /dev/null +++ b/src/actions/300.yml @@ -0,0 +1,52 @@ +type: 300 +name: PeriodicEmitter +desc: > + Emits particles periodically. +properties: + interval: + type: ScalarValue + default: 1 + argument: EffectAge + desc: > + Time between emitting new particles in seconds. + perInterval: + type: ScalarValue + default: 1 + argument: EffectAge + desc: > + The number of particles to emit per interval. They all spawn at the same time per interval. + totalIntervals: + type: ScalarValue + default: -1 + argument: EffectAge + desc: > + The total number of intervals to emit particles. Once this limit is reached, the emitter is will stop emitting. Can be set to -1 to disable the limit. + maxConcurrent: + type: ScalarValue + field: int + default: -1 + argument: EffectAge + desc: > + Maximum number of concurrent particles. Can be set to -1 to disable the limit. + unk_ds3_f1_1: + field: int + default: 1 +games: + DS3: + fields1: + - maxConcurrent + - unk_ds3_f1_1 + properties1: + - interval + - perInterval + - totalIntervals + SDT: + fields1: + - unk_ds3_f1_1 + properties1: + - interval + - perInterval + - totalIntervals + - maxConcurrent + ER: SDT + AC6: SDT diff --git a/src/actions/301.yml b/src/actions/301.yml new file mode 100644 index 0000000..ae7fd1a --- /dev/null +++ b/src/actions/301.yml @@ -0,0 +1,39 @@ +type: 301 +name: EqualDistanceEmitter +desc: > + Emits particles once it has moved a certain distance from where it last emitted particles. +properties: + threshold: + type: ScalarValue + default: 0.1 + argument: EffectAge + desc: > + How much the emitter must move to trigger emission. + maxConcurrent: + type: ScalarValue + default: -1 + argument: EffectAge + desc: > + Maximum number of concurrent particles. Can be set to -1 to disable the limit. + unk_ds3_f1_0: + field: int + default: 1 + unk_ds3_f1_1: + field: int + default: 0 + unk_ds3_p1_1: + type: ScalarValue + default: -1 + argument: EffectAge +games: + DS3: + fields1: + - unk_ds3_f1_0 + - unk_ds3_f1_1 + properties1: + - threshold + - unk_ds3_p1_1 + - maxConcurrent + SDT: DS3 + ER: DS3 + AC6: DS3 diff --git a/src/actions/400.yml b/src/actions/400.yml new file mode 100644 index 0000000..af02c1b --- /dev/null +++ b/src/actions/400.yml @@ -0,0 +1,18 @@ +type: 400 +name: PointEmitterShape +desc: | + Makes the emitter a single point. +properties: + direction: + type: InitialDirection + field: int + default: InitialDirection.Emitter + desc: | + Controls the initial direction for particles. See {@link InitialDirection} for more information. +games: + DS3: + fields1: + - direction + SDT: DS3 + ER: DS3 + AC6: DS3 diff --git a/src/actions/401.yml b/src/actions/401.yml new file mode 100644 index 0000000..23e0ada --- /dev/null +++ b/src/actions/401.yml @@ -0,0 +1,37 @@ +type: 401 +name: DiskEmitterShape +desc: | + Makes the emitter disk-shaped. +properties: + direction: + type: InitialDirection + field: int + default: InitialDirection.Emitter + desc: | + Controls the initial direction for particles. See {@link InitialDirection} for more information. + radius: + type: ScalarValue + default: 1 + argument: EffectAge + desc: | + Radius of the disk. + distribution: + type: ScalarValue + default: 0 + argument: EffectAge + desc: | + Controls how the random emission points are distributed within the disk. + - At 0, particles are equally likely to emit from anywhere inside the disk. + - At 1, particles have a 100% chance of being emitted from the center point. + - At -1, particles have a 100% chance of being emitted from the perimeter circle of the disk. + - Values between these smoothly blend between them. +games: + DS3: + fields1: + - direction + properties1: + - radius + - distribution + SDT: DS3 + ER: DS3 + AC6: DS3 diff --git a/src/actions/402.yml b/src/actions/402.yml new file mode 100644 index 0000000..92357a2 --- /dev/null +++ b/src/actions/402.yml @@ -0,0 +1,44 @@ +type: 402 +name: RectangleEmitterShape +desc: | + Makes the emitter rectangular. +properties: + direction: + type: InitialDirection + field: int + default: InitialDirection.Emitter + desc: | + Controls the initial direction for particles. See {@link InitialDirection} for more information. + sizeX: + type: ScalarValue + default: 1 + argument: EffectAge + desc: | + Width of the rectangle. + sizeY: + type: ScalarValue + default: 1 + argument: EffectAge + desc: | + Height of the rectangle. + distribution: + type: ScalarValue + default: 0 + argument: EffectAge + desc: | + Controls how the random emission points are distributed within the rectangle. + - At 0, particles are equally likely to emit from anywhere inside the rectangle. + - At 1, particles have a 100% chance of being emitted from the center point. + - At -1, particles have a 100% chance of being emitted from the perimeter of the rectangle. + - Values between these smoothly blend between them. +games: + DS3: + fields1: + - direction + properties1: + - sizeX + - sizeY + - distribution + SDT: DS3 + ER: DS3 + AC6: DS3 diff --git a/src/actions/403.yml b/src/actions/403.yml new file mode 100644 index 0000000..1e40377 --- /dev/null +++ b/src/actions/403.yml @@ -0,0 +1,25 @@ +type: 403 +name: SphereEmitterShape +desc: | + Makes the emitter spherical. +properties: + emitInside: + field: bool + default: true + desc: | + If true, particles will be emitted from anywhere within the sphere. Otherwise the particles will be emitted only from the surface of the sphere. + radius: + type: ScalarValue + default: 1 + argument: EffectAge + desc: | + Radius of the sphere. +games: + DS3: + fields1: + - emitInside + properties1: + - radius + SDT: DS3 + ER: DS3 + AC6: DS3 diff --git a/src/actions/404.yml b/src/actions/404.yml new file mode 100644 index 0000000..462a9a1 --- /dev/null +++ b/src/actions/404.yml @@ -0,0 +1,46 @@ +type: 404 +name: BoxEmitterShape +desc: | + Makes the emitter cuboidal. +properties: + direction: + type: InitialDirection + field: int + default: InitialDirection.Emitter + desc: | + Controls the initial direction for particles. See {@link InitialDirection} for more information. + emitInside: + field: bool + default: true + desc: | + If true, particles will be emitted from anywhere within the cuboid. Otherwise the particles will be emitted only from the surface of the cuboid. + sizeX: + type: ScalarValue + default: 1 + argument: EffectAge + desc: | + Width of the cuboid. + sizeY: + type: ScalarValue + default: 1 + argument: EffectAge + desc: | + Height of the cuboid. + sizeZ: + type: ScalarValue + default: 1 + argument: EffectAge + desc: | + Depth of the cuboid. +games: + DS3: + fields1: + - direction + - emitInside + properties1: + - sizeX + - sizeY + - sizeZ + SDT: DS3 + ER: DS3 + AC6: DS3 diff --git a/src/actions/405.yml b/src/actions/405.yml new file mode 100644 index 0000000..f2e4f7c --- /dev/null +++ b/src/actions/405.yml @@ -0,0 +1,44 @@ +type: 405 +name: CylinderEmitterShape +desc: | + Makes the emitter cylindrical. +properties: + direction: + type: InitialDirection + field: int + default: InitialDirection.Emitter + desc: | + Controls the initial direction for particles. See {@link InitialDirection} for more information. + emitInside: + field: bool + default: true + desc: | + If true, particles will be emitted from anywhere within the cylinder. Otherwise the particles will be emitted only from the surface of the cylinder, excluding the ends. + yAxis: + field: bool + default: true + desc: | + If true, the cylinder will be aligned with the Y-axis instead of the Z-axis. + radius: + type: ScalarValue + default: 1 + argument: EffectAge + desc: | + The radius of the cylinder. + height: + type: ScalarValue + default: 1 + argument: EffectAge + desc: | + The height of the cylinder. +games: + SDT: + fields1: + - direction + - emitInside + - yAxis + properties1: + - radius + - height + ER: SDT + AC6: SDT diff --git a/src/actions/46.yml b/src/actions/46.yml new file mode 100644 index 0000000..aa0b0ce --- /dev/null +++ b/src/actions/46.yml @@ -0,0 +1,21 @@ +type: 46 +name: NodeAttachToCamera +desc: | + Attaches the node to the camera. +properties: + followRotation: + field: bool + default: true + desc: | + Disable this to stop the node from following the rotation of the camera. + unk_ds3_f1_1: + field: int + default: 1 +games: + DS3: + fields1: + - followRotation + - unk_ds3_f1_1 + SDT: DS3 + ER: DS3 + AC6: DS3 diff --git a/src/actions/501.yml b/src/actions/501.yml new file mode 100644 index 0000000..502c23f --- /dev/null +++ b/src/actions/501.yml @@ -0,0 +1,38 @@ +type: 501 +name: CircularParticleSpread +desc: | + Gives each particle a random initial direction offset within a circular cone. See {@link InitialDirection} for more information. +properties: + unk_er_f1_0: + field: bool + default: false + desc: | + No so much unknown, just unnamed. If enabled, this limits the possible directions to only positive values on one axis, effectively cutting the cone of possible directions in half. + angle: + type: ScalarValue + default: 30 + argument: EffectAge + desc: | + The maximum change in direction in degrees, the angle of the cone. + distribution: + type: ScalarValue + default: 0 + argument: EffectAge + desc: | + Controls the distribution of the random directions that can be chosen. + - At 0, all directions within the cone have an equal chance of being chosen. + - At 1, the default direction is guaranteed to be chosen. + - At -1, the maximum change in direction is guaranteed, meaning the chosen direction will always be a fixed number of degrees away from the default direction based on {@link angle}. + - Values between these values smoothly blend between them. + - Values outside of the -1 to 1 range also work, but may do some unexpected things. +games: + DS3: + properties1: + - angle + - distribution + SDT: DS3 + ER: + fields1: + - unk_er_f1_0 + properties1: DS3 + AC6: ER diff --git a/src/actions/502.yml b/src/actions/502.yml new file mode 100644 index 0000000..d5c9928 --- /dev/null +++ b/src/actions/502.yml @@ -0,0 +1,49 @@ +type: 502 +name: EllipticalParticleSpread +desc: | + Gives each particle a random initial direction offset within an elliptical cone. See {@link InitialDirection} for more information. +properties: + unk_er_f1_0: + field: bool + default: false + desc: | + No so much unknown, just unnamed. If enabled, this limits the possible directions to only positive values on one axis, effectively cutting the cone of possible directions in half. + angleX: + type: ScalarValue + default: 30 + argument: EffectAge + desc: | + The maximum change in direction in degrees, one of the angles of the elliptical cone. + see: + - angleY + angleY: + type: ScalarValue + default: 30 + argument: EffectAge + desc: | + The maximum change in direction in degrees, one of the angles of the elliptical cone. + see: + - angleY + distribution: + type: ScalarValue + default: 0 + argument: EffectAge + desc: | + Controls the distribution of the random directions that can be chosen. + - At 0, all directions within the cone have an equal chance of being chosen. + - At 1, the default direction is guaranteed to be chosen. + - At -1, the maximum change in direction is guaranteed, meaning the chosen direction will always be a fixed number of degrees away from the default direction based on {@link angleX} and {@link angleY}. + - Values between these values smoothly blend between them. + - Values outside of the -1 to 1 range also work, but may do some unexpected things. +games: + DS3: + properties1: + - angleX + - angleY + - distribution + SDT: DS3 + ER: + fields1: + - unk_er_f1_0 + properties1: DS3 + AC6: ER diff --git a/src/actions/503.yml b/src/actions/503.yml new file mode 100644 index 0000000..582db50 --- /dev/null +++ b/src/actions/503.yml @@ -0,0 +1,41 @@ +type: 503 +name: RectangularParticleSpread +desc: | + Gives each particle a random initial direction offset within a rectangular cone. See {@link InitialDirection} for more information. +properties: + angleX: + type: ScalarValue + default: 30 + argument: EffectAge + desc: | + The maximum change in direction in degrees, one of the angles of the elliptical cone. + see: + - angleY + angleY: + type: ScalarValue + default: 30 + argument: EffectAge + desc: | + The maximum change in direction in degrees, one of the angles of the elliptical cone. + see: + - angleX + distribution: + type: ScalarValue + default: 0 + argument: EffectAge + desc: | + Controls the distribution of the random directions that can be chosen. + - At 0, all directions within the cone have an equal chance of being chosen. + - At 1, the default direction is guaranteed to be chosen. + - At -1, the maximum change in direction is guaranteed, meaning the chosen direction will always be a fixed number of degrees away from the default direction based on {@link angleX} and {@link angleY}. + - Values between these values smoothly blend between them. + - Values outside of the -1 to 1 range also work, but may do some unexpected things. +games: + DS3: + properties1: + - angleX + - angleY + - distribution + SDT: DS3 + ER: DS3 + AC6: DS3 diff --git a/src/actions/600.yml b/src/actions/600.yml new file mode 100644 index 0000000..868528b --- /dev/null +++ b/src/actions/600.yml @@ -0,0 +1,408 @@ +type: 600 +name: PointSprite +desc: | + Very basic point sprite particle. Similar to {@link ActionType.BillboardEx BillboardEx}, but far simpler. +properties: + texture: + type: ScalarValue + field: int + default: 1 + argument: Constant0 + desc: | + Texture ID. + blendMode: + type: BlendMode | ScalarProperty + field: int + default: BlendMode.Normal + argument: Constant0 + desc: | + Blend mode. + size: + type: ScalarValue + default: 1 + argument: InstanceAge + desc: | + Particle size. + color1: + type: Vector4Value + default: [1, 1, 1, 1] + argument: InstanceAge + desc: | + Color multiplier. + color2: + type: Vector4Value + default: [1, 1, 1, 1] + argument: EmissionTime + desc: | + Color multiplier. + color3: + type: Vector4Value + default: [1, 1, 1, 1] + argument: EffectAge + desc: | + Color multiplier. + rgbMultiplier: + type: ScalarValue + default: 1 + argument: EffectAge + desc: | + Scalar multiplier for the color that does not affect the alpha. Effectively a brightness multiplier. + alphaMultiplier: + type: ScalarValue + default: 1 + argument: EffectAge + desc: | + Alpha multiplier. + bloomRed: + field: float + default: 1 + desc: | + Controls the redness of the color of the additional bloom effect. The colors of the particle will be multiplied with this color to get the final color of the bloom effect. + + Note: + - This has no effect if the "Effects Quality" setting is set to "Low". + - This does not affect the natural bloom effect from high color values. + see: + - bloomGreen + - bloomBlue + - bloomStrength + bloomGreen: + field: float + default: 1 + desc: | + Controls the greenness of the color of the additional bloom effect. The colors of the particle will be multiplied with this color to get the final color of the bloom effect. + + Note: + - This has no effect if the "Effects Quality" setting is set to "Low". + - This does not affect the natural bloom effect from high color values. + see: + - bloomRed + - bloomBlue + - bloomStrength + bloomBlue: + field: float + default: 1 + desc: | + Controls the blueness of the color of the additional bloom effect. The colors of the particle will be multiplied with this color to get the final color of the bloom effect. + + Note: + - This has no effect if the "Effects Quality" setting is set to "Low". + - This does not affect the natural bloom effect from high color values. + see: + - bloomRed + - bloomGreen + - bloomStrength + bloomStrength: + field: float + desc: | + Controls the strength of the additional bloom effect. + + Note: + - This has no effect if the "Effects Quality" setting is set to "Low". + - This does not affect the natural bloom effect from high color values. + see: + - bloomRed + - bloomGreen + - bloomBlue + minDistance: + field: float + default: -1 + desc: | + Minimum view distance. If the particle is closer than this distance from the camera, it will be hidden. Can be set to -1 to disable the limit. + see: + - maxDistance + maxDistance: + field: float + default: -1 + desc: | + Maximum view distance. If the particle is farther away than this distance from the camera, it will be hidden. Can be set to -1 to disable the limit. + see: + - minDistance + unk_ds3_f1_2: + field: int + default: -2 + unk_ds3_f1_3: + field: int + default: -2 + unk_ds3_f1_4: + field: int + default: 0 + unk_ds3_f2_0: + field: int + default: 0 + unk_ds3_f2_1: + field: int + default: 0 + unk_ds3_f2_2: + field: int + default: 8 + unk_ds3_f2_3: + field: int + default: 0 + unk_ds3_f2_4: + field: int + default: 1 + unk_ds3_f2_9: + field: int + default: 0 + unk_ds3_f2_10: + field: int + default: 0 + unk_ds3_f2_11: + field: int + default: 0 + unk_ds3_f2_12: + field: int + default: 0 + unk_ds3_f2_13: + field: int + default: 0 + unkDistFadeClose0: + field: float + default: -1 + unkDistFadeClose1: + field: float + default: -1 + unkDistFadeFar0: + field: float + default: -1 + unkDistFadeFar1: + field: float + default: -1 + unk_ds3_f2_20: + field: int + default: 0 + unk_ds3_f2_21: + field: int + default: 0 + unk_ds3_f2_22: + field: int + default: 0 + unk_ds3_f2_23: + field: int + default: 0 + unk_ds3_f2_24: + field: int + default: 0 + unkDepthBlend1: + field: float + default: 1 + unkDepthBlend2: + field: float + default: 0 + unk_ds3_f2_27: + field: int + default: 0 + unk_ds3_f2_28: + field: int + default: 0 + unk_ds3_f2_29: + field: int + default: 0 + unk_ds3_p2_2: + type: ScalarValue + default: 0 + unk_ds3_p2_3: + type: Vector4Value + default: [1, 1, 1, 1] + unk_ds3_p2_4: + type: Vector4Value + default: [1, 1, 1, 1] + unk_ds3_p2_5: + type: Vector4Value + default: [1, 1, 1, 1] + unk_ds3_p2_6: + type: ScalarValue + default: 0 + unk_sdt_f2_30: + field: int + default: 0 + unk_sdt_f2_31: + field: int + default: 0 + unk_sdt_f2_32: + field: int + default: 0 + unk_sdt_f2_33: + field: int + default: 0 + unk_sdt_f2_34: + field: float + default: 0 + unk_sdt_f2_35: + field: int + default: -1 + unk_sdt_f2_36: + field: int + default: -2 + unk_sdt_f2_37: + field: int + default: 0 + unk_sdt_f2_38: + field: int + default: 0 + unk_er_f1_3: + field: int + default: 1 + unk_er_f1_4: + field: int + default: 1 + unk_er_f2_39: + field: int + default: 0 +games: + DS3: + fields1: + - texture + - blendMode + - unk_ds3_f1_2 + - unk_ds3_f1_3 + - unk_ds3_f1_4 + fields2: + - unk_ds3_f2_0 + - unk_ds3_f2_1 + - unk_ds3_f2_2 + - unk_ds3_f2_3 + - unk_ds3_f2_4 + - bloomRed + - bloomGreen + - bloomBlue + - bloomStrength + - unk_ds3_f2_9 + - unk_ds3_f2_10 + - unk_ds3_f2_11 + - unk_ds3_f2_12 + - unk_ds3_f2_13 + - unkDistFadeClose0 + - unkDistFadeClose1 + - unkDistFadeFar0 + - unkDistFadeFar1 + - minDistance + - maxDistance + - unk_ds3_f2_20 + - unk_ds3_f2_21 + - unk_ds3_f2_22 + - unk_ds3_f2_23 + - unk_ds3_f2_24 + - unkDepthBlend1 + - unkDepthBlend2 + - unk_ds3_f2_27 + - unk_ds3_f2_28 + - unk_ds3_f2_29 + properties1: + - size + - color1 + - color2 + - color3 + properties2: + - rgbMultiplier + - alphaMultiplier + - unk_ds3_p2_2 + - unk_ds3_p2_3 + - unk_ds3_p2_4 + - unk_ds3_p2_5 + - unk_ds3_p2_6 + SDT: + fields1: + - unk_ds3_f1_2 + - unk_ds3_f1_3 + - unk_ds3_f1_4 + fields2: + - unk_ds3_f2_0 + - unk_ds3_f2_1 + - unk_ds3_f2_2 + - unk_ds3_f2_3 + - unk_ds3_f2_4 + - bloomRed + - bloomGreen + - bloomBlue + - bloomStrength + - unk_ds3_f2_9 + - unk_ds3_f2_10 + - unk_ds3_f2_11 + - unk_ds3_f2_12 + - unk_ds3_f2_13 + - unkDistFadeClose0 + - unkDistFadeClose1 + - unkDistFadeFar0 + - unkDistFadeFar1 + - minDistance + - maxDistance + - unk_ds3_f2_20 + - unk_ds3_f2_21 + - unk_ds3_f2_22 + - unk_ds3_f2_23 + - unk_ds3_f2_24 + - unkDepthBlend1 + - unkDepthBlend2 + - unk_ds3_f2_27 + - unk_ds3_f2_28 + - unk_ds3_f2_29 + - unk_sdt_f2_30 + - unk_sdt_f2_31 + - unk_sdt_f2_32 + - unk_sdt_f2_33 + - unk_sdt_f2_34 + - unk_sdt_f2_35 + - unk_sdt_f2_36 + - unk_sdt_f2_37 + - unk_sdt_f2_38 + properties1: + - texture + - blendMode + - size + - color1 + - color2 + - color3 + properties2: DS3 + ER: + fields1: + - unk_ds3_f1_2 + - unk_ds3_f1_3 + - unk_ds3_f1_4 + - unk_er_f1_3 + - unk_er_f1_4 + fields2: + - unk_ds3_f2_0 + - unk_ds3_f2_1 + - unk_ds3_f2_2 + - unk_ds3_f2_3 + - unk_ds3_f2_4 + - bloomRed + - bloomGreen + - bloomBlue + - bloomStrength + - unk_ds3_f2_9 + - unk_ds3_f2_10 + - unk_ds3_f2_11 + - unk_ds3_f2_12 + - unk_ds3_f2_13 + - unkDistFadeClose0 + - unkDistFadeClose1 + - unkDistFadeFar0 + - unkDistFadeFar1 + - minDistance + - maxDistance + - unk_ds3_f2_20 + - unk_ds3_f2_21 + - unk_ds3_f2_22 + - unk_ds3_f2_23 + - unk_ds3_f2_24 + - unkDepthBlend1 + - unkDepthBlend2 + - unk_ds3_f2_27 + - unk_ds3_f2_28 + - unk_ds3_f2_29 + - unk_sdt_f2_30 + - unk_sdt_f2_31 + - unk_sdt_f2_32 + - unk_sdt_f2_33 + - unk_sdt_f2_34 + - unk_sdt_f2_35 + - unk_sdt_f2_36 + - unk_sdt_f2_37 + - unk_sdt_f2_38 + - unk_er_f2_39 + properties1: SDT + properties2: DS3 + AC6: ER diff --git a/src/actions/601.yml b/src/actions/601.yml new file mode 100644 index 0000000..6a71496 --- /dev/null +++ b/src/actions/601.yml @@ -0,0 +1,374 @@ +type: 601 +name: Line +desc: | + Simple line particle. It automatically rotates to match the direction it's moving. +properties: + blendMode: + type: BlendMode | ScalarProperty + field: int + default: BlendMode.Normal + argument: Constant0 + desc: | + Blend mode. + length: + type: ScalarValue + default: 1 + argument: EmissionTime + desc: | + The length of the line. + see: + - lengthMultiplier + color1: + type: Vector4Value + default: [1, 1, 1, 1] + argument: InstanceAge + desc: | + Color multiplier. + color2: + type: Vector4Value + default: [1, 1, 1, 1] + argument: InstanceAge + desc: | + Color multiplier. + startColor: + type: Vector4Value + default: [1, 1, 1, 1] + argument: EffectAge + desc: | + The color for the start of the line. + endColor: + type: Vector4Value + default: [1, 1, 1, 1] + argument: EffectAge + desc: | + The color for the end of the line. + lengthMultiplier: + type: ScalarValue + default: 1 + argument: EffectAge + desc: | + Multiplier for the line {@link length}. + color3: + type: Vector4Value + default: [1, 1, 1, 1] + argument: EffectAge + desc: | + Color multiplier. + rgbMultiplier: + type: ScalarValue + default: 1 + argument: EffectAge + desc: | + Scalar multiplier for the color that does not affect the alpha. Effectively a brightness multiplier. + alphaMultiplier: + type: ScalarValue + default: 1 + argument: EffectAge + desc: | + Alpha multiplier. + bloomRed: + field: float + default: 1 + desc: | + Controls the redness of the color of the additional bloom effect. The colors of the particle will be multiplied with this color to get the final color of the bloom effect. + + Note: + - This has no effect if the "Effects Quality" setting is set to "Low". + - This does not affect the natural bloom effect from high color values. + see: + - bloomGreen + - bloomBlue + - bloomStrength + bloomGreen: + field: float + default: 1 + desc: | + Controls the greenness of the color of the additional bloom effect. The colors of the particle will be multiplied with this color to get the final color of the bloom effect. + + Note: + - This has no effect if the "Effects Quality" setting is set to "Low". + - This does not affect the natural bloom effect from high color values. + see: + - bloomRed + - bloomBlue + - bloomStrength + bloomBlue: + field: float + default: 1 + desc: | + Controls the blueness of the color of the additional bloom effect. The colors of the particle will be multiplied with this color to get the final color of the bloom effect. + + Note: + - This has no effect if the "Effects Quality" setting is set to "Low". + - This does not affect the natural bloom effect from high color values. + see: + - bloomRed + - bloomGreen + - bloomStrength + bloomStrength: + field: float + desc: | + Controls the strength of the additional bloom effect. + + Note: + - This has no effect if the "Effects Quality" setting is set to "Low". + - This does not affect the natural bloom effect from high color values. + see: + - bloomRed + - bloomGreen + - bloomBlue + minDistance: + field: float + default: -1 + desc: | + Minimum view distance. If the particle is closer than this distance from the camera, it will be hidden. Can be set to -1 to disable the limit. + see: + - maxDistance + maxDistance: + field: float + default: -1 + desc: | + Maximum view distance. If the particle is farther away than this distance from the camera, it will be hidden. Can be set to -1 to disable the limit. + see: + - minDistance + unk_ds3_f1_1: + field: int + default: -1 + unk_ds3_f2_0: + field: int + default: 0 + unk_ds3_f2_1: + field: int + default: 0 + unk_ds3_f2_2: + field: int + default: 8 + unk_ds3_f2_3: + field: int + default: 0 + unk_ds3_f2_4: + field: int + default: 1 + unk_ds3_f2_9: + field: int + default: 0 + unk_ds3_f2_10: + field: int + default: 0 + unk_ds3_f2_11: + field: int + default: 0 + unk_ds3_f2_12: + field: int + default: 0 + unk_ds3_f2_13: + field: int + default: 0 + unkDistFadeClose0: + field: float + default: -1 + unkDistFadeClose1: + field: float + default: -1 + unkDistFadeFar0: + field: float + default: -1 + unkDistFadeFar1: + field: float + default: -1 + unk_ds3_f2_20: + field: int + default: 0 + unk_ds3_f2_21: + field: int + default: 0 + unk_ds3_f2_22: + field: int + default: 0 + unk_ds3_f2_23: + field: int + default: 0 + unk_ds3_f2_24: + field: int + default: 0 + unkDepthBlend1: + field: float + default: 1 + unkDepthBlend2: + field: float + default: 0 + unk_ds3_f2_27: + field: int + default: 0 + unk_ds3_f2_28: + field: int + default: 0 + unk_ds3_f2_29: + field: int + default: 0 + unk_ds3_p2_2: + type: ScalarValue + default: 0 + unk_ds3_p2_3: + type: Vector4Value + default: [1, 1, 1, 1] + unk_ds3_p2_4: + type: Vector4Value + default: [1, 1, 1, 1] + unk_ds3_p2_5: + type: Vector4Value + default: [1, 1, 1, 1] + unk_ds3_p2_6: + type: ScalarValue + default: 0 + unk_sdt_f2_30: + field: int + default: 0 + unk_sdt_f2_31: + field: int + default: 0 + unk_sdt_f2_32: + field: int + default: 0 + unk_sdt_f2_33: + field: int + default: 0 + unk_sdt_f2_34: + field: float + default: 0 + unk_sdt_f2_35: + field: int + default: -2 + unk_sdt_f2_36: + field: int + default: -2 + unk_sdt_f2_37: + field: int + default: 0 + unk_sdt_f2_38: + field: int + default: 0 + unk_sdt_f2_39: + field: int + default: 0 + unk_er_f1_1: + field: int + default: 1 + unk_er_f1_2: + field: int + default: 1 +games: + DS3: + fields1: + - blendMode + - unk_ds3_f1_1 + fields2: + - unk_ds3_f2_0 + - unk_ds3_f2_1 + - unk_ds3_f2_2 + - unk_ds3_f2_3 + - unk_ds3_f2_4 + - bloomRed + - bloomGreen + - bloomBlue + - bloomStrength + - unk_ds3_f2_9 + - unk_ds3_f2_10 + - unk_ds3_f2_11 + - unk_ds3_f2_12 + - unk_ds3_f2_13 + - unkDistFadeClose0 + - unkDistFadeClose1 + - unkDistFadeFar0 + - unkDistFadeFar1 + - minDistance + - maxDistance + - unk_ds3_f2_20 + - unk_ds3_f2_21 + - unk_ds3_f2_22 + - unk_ds3_f2_23 + - unk_ds3_f2_24 + - unkDepthBlend1 + - unkDepthBlend2 + - unk_ds3_f2_27 + - unk_ds3_f2_28 + - unk_ds3_f2_29 + properties1: + - length + - color1 + - color2 + - startColor + - endColor + - lengthMultiplier + - color3 + properties2: + - rgbMultiplier + - alphaMultiplier + - unk_ds3_p2_2 + - unk_ds3_p2_3 + - unk_ds3_p2_4 + - unk_ds3_p2_5 + - unk_ds3_p2_6 + SDT: + fields1: + - unk_ds3_f1_1 + fields2: + - unk_ds3_f2_0 + - unk_ds3_f2_1 + - unk_ds3_f2_2 + - unk_ds3_f2_3 + - unk_ds3_f2_4 + - bloomRed + - bloomGreen + - bloomBlue + - bloomStrength + - unk_ds3_f2_9 + - unk_ds3_f2_10 + - unk_ds3_f2_11 + - unk_ds3_f2_12 + - unk_ds3_f2_13 + - unkDistFadeClose0 + - unkDistFadeClose1 + - unkDistFadeFar0 + - unkDistFadeFar1 + - minDistance + - maxDistance + - unk_ds3_f2_20 + - unk_ds3_f2_21 + - unk_ds3_f2_22 + - unk_ds3_f2_23 + - unk_ds3_f2_24 + - unkDepthBlend1 + - unkDepthBlend2 + - unk_ds3_f2_27 + - unk_ds3_f2_28 + - unk_ds3_f2_29 + - unk_sdt_f2_30 + - unk_sdt_f2_31 + - unk_sdt_f2_32 + - unk_sdt_f2_33 + - unk_sdt_f2_34 + - unk_sdt_f2_35 + - unk_sdt_f2_36 + - unk_sdt_f2_37 + - unk_sdt_f2_38 + - unk_sdt_f2_39 + properties1: + - blendMode + - length + - color1 + - color2 + - startColor + - endColor + - lengthMultiplier + - color3 + properties2: DS3 + ER: + fields1: + - unk_ds3_f1_1 + - unk_er_f1_1 + - unk_er_f1_2 + fields2: SDT + properties1: SDT + properties2: DS3 + AC6: ER diff --git a/src/actions/602.yml b/src/actions/602.yml new file mode 100644 index 0000000..4b0f7ee --- /dev/null +++ b/src/actions/602.yml @@ -0,0 +1,392 @@ +type: 602 +name: QuadLine +desc: | + Simple rectangular particle, very similar to {@link ActionType.Line Line particles}, but has properties that control the width as well as the length. It automatically rotates to match the direction it's moving. +properties: + blendMode: + type: BlendMode | ScalarProperty + field: int + default: BlendMode.Normal + argument: Constant0 + desc: | + Blend mode. + width: + type: ScalarValue + default: 1 + argument: EmissionTime + desc: | + The width of the line. + see: + - widthMultiplier + length: + type: ScalarValue + default: 1 + argument: EmissionTime + desc: | + The length of the line. + see: + - lengthMultiplier + color1: + type: Vector4Value + default: [1, 1, 1, 1] + argument: InstanceAge + desc: | + Color multiplier. + color2: + type: Vector4Value + default: [1, 1, 1, 1] + argument: InstanceAge + desc: | + Color multiplier. + startColor: + type: Vector4Value + default: [1, 1, 1, 1] + argument: EffectAge + desc: | + The color for the leading edge of the quad. + endColor: + type: Vector4Value + default: [1, 1, 1, 1] + argument: EffectAge + desc: | + The color for the trailing edge of the quad. + widthMultiplier: + type: ScalarValue + default: 1 + argument: EffectAge + desc: | + Multiplier for the line {@link width}. + lengthMultiplier: + type: ScalarValue + default: 1 + argument: EffectAge + desc: | + Multiplier for the line {@link length}. + color3: + type: Vector4Value + default: [1, 1, 1, 1] + argument: EffectAge + desc: | + Color multiplier. + rgbMultiplier: + type: ScalarValue + default: 1 + argument: EffectAge + desc: | + Scalar multiplier for the color that does not affect the alpha. Effectively a brightness multiplier. + alphaMultiplier: + type: ScalarValue + default: 1 + argument: EffectAge + desc: | + Alpha multiplier. + bloomRed: + field: float + default: 1 + desc: | + Controls the redness of the color of the additional bloom effect. The colors of the particle will be multiplied with this color to get the final color of the bloom effect. + + Note: + - This has no effect if the "Effects Quality" setting is set to "Low". + - This does not affect the natural bloom effect from high color values. + see: + - bloomGreen + - bloomBlue + - bloomStrength + bloomGreen: + field: float + default: 1 + desc: | + Controls the greenness of the color of the additional bloom effect. The colors of the particle will be multiplied with this color to get the final color of the bloom effect. + + Note: + - This has no effect if the "Effects Quality" setting is set to "Low". + - This does not affect the natural bloom effect from high color values. + see: + - bloomRed + - bloomBlue + - bloomStrength + bloomBlue: + field: float + default: 1 + desc: | + Controls the blueness of the color of the additional bloom effect. The colors of the particle will be multiplied with this color to get the final color of the bloom effect. + + Note: + - This has no effect if the "Effects Quality" setting is set to "Low". + - This does not affect the natural bloom effect from high color values. + see: + - bloomRed + - bloomGreen + - bloomStrength + bloomStrength: + field: float + desc: | + Controls the strength of the additional bloom effect. + + Note: + - This has no effect if the "Effects Quality" setting is set to "Low". + - This does not affect the natural bloom effect from high color values. + see: + - bloomRed + - bloomGreen + - bloomBlue + minDistance: + field: float + default: -1 + desc: | + Minimum view distance. If the particle is closer than this distance from the camera, it will be hidden. Can be set to -1 to disable the limit. + see: + - maxDistance + maxDistance: + field: float + default: -1 + desc: | + Maximum view distance. If the particle is farther away than this distance from the camera, it will be hidden. Can be set to -1 to disable the limit. + see: + - minDistance + unk_ds3_f1_1: + field: int + default: -1 + unk_ds3_f2_0: + field: int + default: 0 + unk_ds3_f2_1: + field: int + default: 0 + unk_ds3_f2_2: + field: int + default: 8 + unk_ds3_f2_3: + field: int + default: 0 + unk_ds3_f2_4: + field: int + default: 1 + unk_ds3_f2_9: + field: int + default: 0 + unk_ds3_f2_10: + field: int + default: 0 + unk_ds3_f2_11: + field: int + default: 0 + unk_ds3_f2_12: + field: int + default: 0 + unk_ds3_f2_13: + field: int + default: 0 + unkDistFadeClose0: + field: float + default: -1 + unkDistFadeClose1: + field: float + default: -1 + unkDistFadeFar0: + field: float + default: -1 + unkDistFadeFar1: + field: float + default: -1 + unk_ds3_f2_20: + field: int + default: 0 + unk_ds3_f2_21: + field: int + default: 0 + unk_ds3_f2_22: + field: int + default: 0 + unk_ds3_f2_23: + field: int + default: 0 + unk_ds3_f2_24: + field: int + default: 0 + unkDepthBlend1: + field: float + default: 1 + unkDepthBlend2: + field: float + default: 0 + unk_ds3_f2_27: + field: int + default: 0 + unk_ds3_f2_28: + field: int + default: 0 + unk_ds3_f2_29: + field: int + default: 0 + unk_ds3_p2_2: + type: ScalarValue + default: 0 + unk_ds3_p2_3: + type: Vector4Value + default: [1, 1, 1, 1] + unk_ds3_p2_4: + type: Vector4Value + default: [1, 1, 1, 1] + unk_ds3_p2_5: + type: Vector4Value + default: [1, 1, 1, 1] + unk_ds3_p2_6: + type: ScalarValue + default: 0 + unk_sdt_f2_30: + field: int + default: 0 + unk_sdt_f2_31: + field: int + default: 0 + unk_sdt_f2_32: + field: int + default: 0 + unk_sdt_f2_33: + field: int + default: 0 + unk_sdt_f2_34: + field: float + default: 0 + unk_sdt_f2_35: + field: int + default: -2 + unk_sdt_f2_36: + field: int + default: -2 + unk_sdt_f2_37: + field: int + default: 0 + unk_sdt_f2_38: + field: int + default: 0 + unk_sdt_f2_39: + field: int + default: 0 + unk_er_f1_1: + field: int + default: 1 + unk_er_f1_2: + field: int + default: 1 +games: + DS3: + fields1: + - blendMode + - unk_ds3_f1_1 + fields2: + - unk_ds3_f2_0 + - unk_ds3_f2_1 + - unk_ds3_f2_2 + - unk_ds3_f2_3 + - unk_ds3_f2_4 + - bloomRed + - bloomGreen + - bloomBlue + - bloomStrength + - unk_ds3_f2_9 + - unk_ds3_f2_10 + - unk_ds3_f2_11 + - unk_ds3_f2_12 + - unk_ds3_f2_13 + - unkDistFadeClose0 + - unkDistFadeClose1 + - unkDistFadeFar0 + - unkDistFadeFar1 + - minDistance + - maxDistance + - unk_ds3_f2_20 + - unk_ds3_f2_21 + - unk_ds3_f2_22 + - unk_ds3_f2_23 + - unk_ds3_f2_24 + - unkDepthBlend1 + - unkDepthBlend2 + - unk_ds3_f2_27 + - unk_ds3_f2_28 + - unk_ds3_f2_29 + properties1: + - width + - length + - color1 + - color2 + - startColor + - endColor + - widthMultiplier + - lengthMultiplier + - color3 + properties2: + - rgbMultiplier + - alphaMultiplier + - unk_ds3_p2_2 + - unk_ds3_p2_3 + - unk_ds3_p2_4 + - unk_ds3_p2_5 + - unk_ds3_p2_6 + SDT: + fields1: + - unk_ds3_f1_1 + fields2: + - unk_ds3_f2_0 + - unk_ds3_f2_1 + - unk_ds3_f2_2 + - unk_ds3_f2_3 + - unk_ds3_f2_4 + - bloomRed + - bloomGreen + - bloomBlue + - bloomStrength + - unk_ds3_f2_9 + - unk_ds3_f2_10 + - unk_ds3_f2_11 + - unk_ds3_f2_12 + - unk_ds3_f2_13 + - unkDistFadeClose0 + - unkDistFadeClose1 + - unkDistFadeFar0 + - unkDistFadeFar1 + - minDistance + - maxDistance + - unk_ds3_f2_20 + - unk_ds3_f2_21 + - unk_ds3_f2_22 + - unk_ds3_f2_23 + - unk_ds3_f2_24 + - unkDepthBlend1 + - unkDepthBlend2 + - unk_ds3_f2_27 + - unk_ds3_f2_28 + - unk_ds3_f2_29 + - unk_sdt_f2_30 + - unk_sdt_f2_31 + - unk_sdt_f2_32 + - unk_sdt_f2_33 + - unk_sdt_f2_34 + - unk_sdt_f2_35 + - unk_sdt_f2_36 + - unk_sdt_f2_37 + - unk_sdt_f2_38 + - unk_sdt_f2_39 + properties1: + - blendMode + - width + - length + - color1 + - color2 + - startColor + - endColor + - widthMultiplier + - lengthMultiplier + - color3 + properties2: DS3 + ER: + fields1: + - unk_ds3_f1_1 + - unk_er_f1_1 + - unk_er_f1_2 + fields2: SDT + properties1: SDT + properties2: DS3 + AC6: ER diff --git a/src/actions/603.yml b/src/actions/603.yml new file mode 100644 index 0000000..4607f94 --- /dev/null +++ b/src/actions/603.yml @@ -0,0 +1,640 @@ +type: 603 +name: BillboardEx +desc: | + Particle with a texture that may be animated. This is the most common particle type and it has a lot of useful fields and properties. +properties: + texture: + type: ScalarValue + field: int + default: 1 + argument: Constant0 + desc: | + Texture ID. + blendMode: + type: BlendMode | ScalarProperty + field: int + default: BlendMode.Normal + argument: Constant0 + desc: | + Blend mode. + offsetX: + type: ScalarValue + argument: InstanceAge + desc: | + X position offset. + offsetY: + type: ScalarValue + argument: InstanceAge + desc: | + Y position offset. + offsetZ: + type: ScalarValue + argument: InstanceAge + desc: | + Z position offset. + width: + type: ScalarValue + default: 1 + argument: InstanceAge + desc: | + The width of the particle. + + If {@link uniformScale} is enabled, this also controls the height. + see: + - scaleVariationX + height: + type: ScalarValue + default: 1 + argument: InstanceAge + desc: | + The height of the particle. + + If {@link uniformScale} is enabled, {@link width} also controls the height, and this property is ignored. + see: + - scaleVariationY + color1: + type: Vector4Value + default: [1, 1, 1, 1] + argument: InstanceAge + desc: | + Color multiplier. + color2: + type: Vector4Value + default: [1, 1, 1, 1] + argument: EmissionTime + desc: | + Color multiplier. + color3: + type: Vector4Value + default: [1, 1, 1, 1] + argument: InstanceAge + desc: | + Color multiplier. + alphaThreshold: + type: ScalarValue + argument: InstanceAge + desc: | + Parts of the particle with less opacity than this threshold will be invisible. The range is 0-255. + rotationX: + type: ScalarValue + argument: Constant0 + desc: | + Rotation around the X-axis in degrees. + see: + - rotationSpeedX + - rotationSpeedMultiplierX + rotationY: + type: ScalarValue + argument: Constant0 + desc: | + Rotation around the Y-axis in degrees. + see: + - rotationSpeedY + - rotationSpeedMultiplierY + rotationZ: + type: ScalarValue + argument: Constant0 + desc: | + Rotation around the Z-axis in degrees. + see: + - rotationSpeedZ + - rotationSpeedMultiplierZ + rotationSpeedX: + type: ScalarValue + argument: InstanceAge + desc: | + Rotation speed around the X-axis in degrees per second. + see: + - rotationX + - rotationSpeedMultiplierX + rotationSpeedY: + type: ScalarValue + argument: InstanceAge + desc: | + Rotation speed around the Y-axis in degrees per second. + see: + - rotationY + - rotationSpeedMultiplierY + rotationSpeedZ: + type: ScalarValue + argument: InstanceAge + desc: | + Rotation speed around the Z-axis in degrees per second. + see: + - rotationZ + - rotationSpeedMultiplierZ + rotationSpeedMultiplierX: + type: ScalarValue + default: 1 + argument: InstanceAge + desc: | + Multiplier for {@link rotationSpeedX}. + see: + - rotationX + rotationSpeedMultiplierY: + type: ScalarValue + default: 1 + argument: InstanceAge + desc: | + Multiplier for {@link rotationSpeedY}. + see: + - rotationY + rotationSpeedMultiplierZ: + type: ScalarValue + default: 1 + argument: InstanceAge + desc: | + Multiplier for {@link rotationSpeedZ}. + see: + - rotationZ + depthOffset: + type: ScalarValue + argument: InstanceAge + desc: | + Positive values will make the particle draw in front of objects closer to the camera, while negative values will make it draw behind objects farther away from the camera. + frameIndex: + type: ScalarValue + argument: InstanceAge + desc: | + The index of the frame to show from the texture atlas. Can be animated using a {@link PropertyFunction.Linear linear property} or similar. + + Seemingly identical to {@link frameIndexOffset}? The sum of these two properties is the actual frame index that gets used. + frameIndexOffset: + type: ScalarValue + argument: InstanceAge + desc: | + Seemingly identical to {@link frameIndex}? The sum of these two properties is the actual frame index that gets used. + rgbMultiplier: + type: ScalarValue + default: 1 + argument: EffectAge + desc: | + Scalar multiplier for the color that does not affect the alpha. Effectively a brightness multiplier. + alphaMultiplier: + type: ScalarValue + default: 1 + argument: EffectAge + desc: | + Alpha multiplier. + orientation: + type: OrientationMode + field: int + default: OrientationMode.CameraPlane + desc: | + Controls the orientation mode for the particles. See {@link OrientationMode} for more information. + normalMap: + field: int + desc: | + Normal map texture ID. + scaleVariationX: + field: float + default: 1 + desc: | + Each particle will pick a random number between this value and 1, and the width of the particle will be multiplied by this number. For example, setting this to 0.5 will make the particles randomly thinner, down to half width. Setting it to 2 will make them randomly wider, up to double width. + + If {@link uniformScale} is enabled, this also affects the height. + see: + - scaleVariationY + scaleVariationY: + field: float + default: 1 + desc: | + Each particle will pick a random number between this value and 1, and the height of the particle will be multiplied by this number. For example, setting this to 0.5 will make the particles randomly shorter, down to half height. Setting it to 2 will make them randomly taller, up to double height. + + If {@link uniformScale} is enabled, {@link scaleVariationX} also affects the height, and this field is ignored. + see: + - scaleVariationX + uniformScale: + field: bool + default: false + desc: | + If enabled, the particle width-related properties and fields will control both the width and height of the particles, and the height counterparts will be ignored. + see: + - width + - height + - scaleVariationX + - scaleVariationY + columns: + field: int + default: 1 + desc: | + To split the texture into multiple animation frames, this value must be set to the number of columns in the texture. It should equal `textureWidth / frameWidth`. + see: + - totalFrames + totalFrames: + field: int + default: 1 + desc: | + To split the texture into multiple animation frames, this value must be set to the total number of frames in the texture. + see: + - columns + interpolateFrames: + field: bool + default: true + desc: | + If enabled, the texture animation will use linear interpolation to mix frames when the frame index is not a whole number. For example, if the frame index is 0.5, enabling this will cause the average of the first two frames to be shown instead of just the first frame. + + If disabled, the frame index will be truncated to get a whole number. + see: + - frameIndex + - frameIndexOffset + bloomRed: + field: float + default: 1 + desc: | + Controls the redness of the color of the additional bloom effect. The colors of the particle will be multiplied with this color to get the final color of the bloom effect. + + Note: + - This has no effect if the "Effects Quality" setting is set to "Low". + - This does not affect the natural bloom effect from high color values. + see: + - bloomGreen + - bloomBlue + - bloomStrength + bloomGreen: + field: float + default: 1 + desc: | + Controls the greenness of the color of the additional bloom effect. The colors of the particle will be multiplied with this color to get the final color of the bloom effect. + + Note: + - This has no effect if the "Effects Quality" setting is set to "Low". + - This does not affect the natural bloom effect from high color values. + see: + - bloomRed + - bloomBlue + - bloomStrength + bloomBlue: + field: float + default: 1 + desc: | + Controls the blueness of the color of the additional bloom effect. The colors of the particle will be multiplied with this color to get the final color of the bloom effect. + + Note: + - This has no effect if the "Effects Quality" setting is set to "Low". + - This does not affect the natural bloom effect from high color values. + see: + - bloomRed + - bloomGreen + - bloomStrength + bloomStrength: + field: float + desc: | + Controls the strength of the additional bloom effect. + + Note: + - This has no effect if the "Effects Quality" setting is set to "Low". + - This does not affect the natural bloom effect from high color values. + see: + - bloomRed + - bloomGreen + - bloomBlue + minDistance: + field: float + default: -1 + desc: | + Minimum view distance. If the particle is closer than this distance from the camera, it will be hidden. Can be set to -1 to disable the limit. + see: + - maxDistance + maxDistance: + field: float + default: -1 + desc: | + Maximum view distance. If the particle is farther away than this distance from the camera, it will be hidden. Can be set to -1 to disable the limit. + see: + - minDistance + shadowDarkness: + field: float + desc: | + Controls how dark shaded parts of the particle are. + specular: + field: int + desc: | + Specular texture ID. + see: + - lighting + - glossiness + - specularity + glossiness: + field: float + default: 0.25 + desc: | + Controls how sharp the specular highlights are. + see: + - lighting + - specular + - specularity + lighting: + type: LightingMode + field: int + default: LightingMode.Unlit + desc: | + Controls how the particles are lit. See {@link LightingMode} for more information. + specularity: + field: float + default: 0.5 + desc: | + Controls how bright the specular highlights are. + see: + - lighting + - specular + - glossiness + unk_ds3_f1_7: + field: int + unk_ds3_f1_11: + field: int + unk_ds3_f1_12: + field: int + unk_ds3_f1_13: + field: float + default: -1 + unk_ds3_f1_14: + field: int + default: 1 + unk_ds3_f1_15: + field: int + unk_ds3_f1_16: + field: int + unk_ds3_f2_0: + field: int + unk_ds3_f2_1: + field: bool + unk_ds3_f2_2: + field: int + default: 8 + unk_ds3_f2_3: + field: int + unk_ds3_f2_4: + field: int + default: 1 + unk_ds3_f2_9: + field: int + unk_ds3_f2_10: + field: int + unk_ds3_f2_11: + field: int + unk_ds3_f2_12: + field: int + unk_ds3_f2_13: + field: int + unkDistFadeClose0: + field: float + default: -1 + unkDistFadeClose1: + field: float + default: -1 + unkDistFadeFar0: + field: float + default: -1 + unkDistFadeFar1: + field: float + default: -1 + unk_ds3_f2_20: + field: int + unk_ds3_f2_21: + field: int + unk_ds3_f2_22: + field: int + unk_ds3_f2_23: + field: int + unk_ds3_f2_24: + field: int + unkDepthBlend1: + field: float + default: 1 + unkDepthBlend2: + field: float + unk_ds3_f2_27: + field: int + default: 1 + unk_ds3_f2_28: + field: int + unk_ds3_f2_29: + field: float + default: 5 + unk_ds3_p1_21: + type: ScalarValue + unk_ds3_p1_22: + type: ScalarValue + unk_ds3_p2_2: + type: ScalarValue + unk_ds3_p2_3: + type: Vector4Value + default: [1, 1, 1, 1] + unk_ds3_p2_4: + type: Vector4Value + default: [1, 1, 1, 1] + unk_ds3_p2_5: + type: Vector4Value + default: [1, 1, 1, 1] + unk_ds3_p2_6: + type: ScalarValue + unk_sdt_f1_15: + field: int + default: 1 + unk_sdt_f1_16: + field: int + default: 1 + unk_sdt_f1_17: + field: int + unk_sdt_f2_31: + field: int + unk_sdt_f2_32: + field: int + default: 1 + unk_sdt_f2_36: + field: int + default: -2 + unk_sdt_f2_37: + field: int + unk_sdt_f2_39: + field: int + default: 1 + unk_sdt_f2_40: + field: int + unk_sdt_f2_41: + field: int + unk_sdt_f2_42: + field: int + unk_sdt_f2_43: + field: int + unk_sdt_f2_44: + field: int +games: + DS3: + fields1: + - orientation + - texture + - normalMap + - blendMode + - scaleVariationX + - scaleVariationY + - uniformScale + - unk_ds3_f1_7 + - columns + - totalFrames + - interpolateFrames + - unk_ds3_f1_11 + - unk_ds3_f1_12 + - unk_ds3_f1_13 + - unk_ds3_f1_14 + - unk_ds3_f1_15 + - unk_ds3_f1_16 + fields2: + - unk_ds3_f2_0 + - unk_ds3_f2_1 + - unk_ds3_f2_2 + - unk_ds3_f2_3 + - unk_ds3_f2_4 + - bloomRed + - bloomGreen + - bloomBlue + - bloomStrength + - unk_ds3_f2_9 + - unk_ds3_f2_10 + - unk_ds3_f2_11 + - unk_ds3_f2_12 + - unk_ds3_f2_13 + - unkDistFadeClose0 + - unkDistFadeClose1 + - unkDistFadeFar0 + - unkDistFadeFar1 + - minDistance + - maxDistance + - unk_ds3_f2_20 + - unk_ds3_f2_21 + - unk_ds3_f2_22 + - unk_ds3_f2_23 + - unk_ds3_f2_24 + - unkDepthBlend1 + - unkDepthBlend2 + - unk_ds3_f2_27 + - unk_ds3_f2_28 + - unk_ds3_f2_29 + properties1: + - offsetX + - offsetY + - offsetZ + - width + - height + - color1 + - color2 + - color3 + - alphaThreshold + - rotationX + - rotationY + - rotationZ + - rotationSpeedX + - rotationSpeedMultiplierX + - rotationSpeedY + - rotationSpeedMultiplierY + - rotationSpeedZ + - rotationSpeedMultiplierZ + - depthOffset + - frameIndex + - frameIndexOffset + - unk_ds3_p1_21 + - unk_ds3_p1_22 + properties2: + - rgbMultiplier + - alphaMultiplier + - unk_ds3_p2_2 + - unk_ds3_p2_3 + - unk_ds3_p2_4 + - unk_ds3_p2_5 + - unk_ds3_p2_6 + SDT: + fields1: + - orientation + - normalMap + - scaleVariationX + - scaleVariationY + - uniformScale + - unk_ds3_f1_7 + - columns + - totalFrames + - interpolateFrames + - unk_ds3_f1_11 + - unk_ds3_f1_12 + - unk_ds3_f1_13 + - unk_ds3_f1_14 + - unk_ds3_f1_15 + - unk_ds3_f1_16 + - unk_sdt_f1_15 + - unk_sdt_f1_16 + - unk_sdt_f1_17 + fields2: + - unk_ds3_f2_0 + - unk_ds3_f2_1 + - unk_ds3_f2_2 + - unk_ds3_f2_3 + - unk_ds3_f2_4 + - bloomRed + - bloomGreen + - bloomBlue + - bloomStrength + - unk_ds3_f2_9 + - unk_ds3_f2_10 + - unk_ds3_f2_11 + - unk_ds3_f2_12 + - unk_ds3_f2_13 + - unkDistFadeClose0 + - unkDistFadeClose1 + - unkDistFadeFar0 + - unkDistFadeFar1 + - minDistance + - maxDistance + - unk_ds3_f2_20 + - unk_ds3_f2_21 + - unk_ds3_f2_22 + - unk_ds3_f2_23 + - unk_ds3_f2_24 + - unkDepthBlend1 + - unkDepthBlend2 + - unk_ds3_f2_27 + - unk_ds3_f2_28 + - unk_ds3_f2_29 + - shadowDarkness + - unk_sdt_f2_31 + - unk_sdt_f2_32 + - specular + - glossiness + - lighting + - unk_sdt_f2_36 + - unk_sdt_f2_37 + - specularity + - unk_sdt_f2_39 + - unk_sdt_f2_40 + - unk_sdt_f2_41 + - unk_sdt_f2_42 + - unk_sdt_f2_43 + - unk_sdt_f2_44 + properties1: + - texture + - blendMode + - offsetX + - offsetY + - offsetZ + - width + - height + - color1 + - color2 + - color3 + - alphaThreshold + - rotationX + - rotationY + - rotationZ + - rotationSpeedX + - rotationSpeedMultiplierX + - rotationSpeedY + - rotationSpeedMultiplierY + - rotationSpeedZ + - rotationSpeedMultiplierZ + - depthOffset + - frameIndex + - frameIndexOffset + - unk_ds3_p1_21 + - unk_ds3_p1_22 + properties2: DS3 + ER: SDT + AC6: SDT diff --git a/src/actions/604.yml b/src/actions/604.yml new file mode 100644 index 0000000..0b76158 --- /dev/null +++ b/src/actions/604.yml @@ -0,0 +1,861 @@ +type: 604 +name: MultiTextureBillboardEx +desc: | + Particle with multiple textures that can scroll. +properties: + orientation: + type: OrientationMode + field: int + default: OrientationMode.CameraPlane + desc: | + Controls the orientation mode for the particles. See {@link OrientationMode} for more information. + mask: + field: int + default: 1 + desc: | + Mask texture ID. + layer1: + field: int + default: 1 + desc: | + Layer 1 texture ID. + layer2: + field: int + default: 1 + desc: | + Layer 2 texture ID. + uniformScale: + field: bool + default: false + desc: | + If enabled, the particle width-related properties and fields will control both the width and height of the particles, and the height counterparts will be ignored. + see: + - width + - height + columns: + field: int + default: 1 + desc: | + To split the texture into multiple animation frames, this value must be set to the number of columns in the texture. It should equal `textureWidth / frameWidth`. + see: + - totalFrames + totalFrames: + field: int + default: 1 + desc: | + To split the texture into multiple animation frames, this value must be set to the total number of frames in the texture. + see: + - columns + interpolateFrames: + field: bool + default: true + desc: | + If enabled, the texture animation will use linear interpolation to mix frames when the frame index is not a whole number. For example, if the frame index is 0.5, enabling this will cause the average of the first two frames to be shown instead of just the first frame. + + If disabled, the frame index will be truncated to get a whole number. + see: + - frameIndex + - frameIndexOffset + depthBlend: + field: bool + default: true + desc: | + Controls how the particles should render when behind something else. If disabled, the particles will simply be drawn behind anything they are behind in the world. If enabled, they will instead display in front of the object if they are close enough, and will fade out with distance from the object's surface that is blocking the view of the particle. + octagonal: + field: bool + default: false + desc: | + Controls the shape of the particles. If disabled, the particles will be rectangular. If enabled, they will be octagonal. + bloomRed: + field: float + default: 1 + desc: | + Controls the redness of the color of the additional bloom effect. The colors of the particle will be multiplied with this color to get the final color of the bloom effect. + + Note: + - This has no effect if the "Effects Quality" setting is set to "Low". + - This does not affect the natural bloom effect from high color values. + see: + - bloomGreen + - bloomBlue + - bloomStrength + bloomGreen: + field: float + default: 1 + desc: | + Controls the greenness of the color of the additional bloom effect. The colors of the particle will be multiplied with this color to get the final color of the bloom effect. + + Note: + - This has no effect if the "Effects Quality" setting is set to "Low". + - This does not affect the natural bloom effect from high color values. + see: + - bloomRed + - bloomBlue + - bloomStrength + bloomBlue: + field: float + default: 1 + desc: | + Controls the blueness of the color of the additional bloom effect. The colors of the particle will be multiplied with this color to get the final color of the bloom effect. + + Note: + - This has no effect if the "Effects Quality" setting is set to "Low". + - This does not affect the natural bloom effect from high color values. + see: + - bloomRed + - bloomGreen + - bloomStrength + bloomStrength: + field: float + desc: | + Controls the strength of the additional bloom effect. + + Note: + - This has no effect if the "Effects Quality" setting is set to "Low". + - This does not affect the natural bloom effect from high color values. + see: + - bloomRed + - bloomGreen + - bloomBlue + minDistance: + field: float + default: -1 + desc: | + Minimum view distance. If the particle is closer than this distance from the camera, it will be hidden. Can be set to -1 to disable the limit. + see: + - maxDistance + maxDistance: + field: float + default: -1 + desc: | + Maximum view distance. If the particle is farther away than this distance from the camera, it will be hidden. Can be set to -1 to disable the limit. + see: + - minDistance + shadowDarkness: + field: float + default: 0 + desc: | + Controls how dark shaded parts of the particle are. + specular: + field: int + default: 0 + desc: | + Specular texture ID. + see: + - lighting + - glossiness + glossiness: + field: float + default: 0.25 + desc: | + Controls how sharp the specular highlights are. + see: + - lighting + - specular + lighting: + type: LightingMode + field: int + default: LightingMode.Unlit + desc: | + Controls how the particles are lit. See {@link LightingMode} for more information. + unk_sdt_f2_38: + field: int + default: 1 + blendMode: + type: BlendMode | ScalarProperty + field: int + default: BlendMode.Normal + argument: Constant0 + desc: | + Blend mode. + offsetX: + type: ScalarValue + argument: InstanceAge + desc: | + X position offset. + offsetY: + type: ScalarValue + argument: InstanceAge + desc: | + Y position offset. + offsetZ: + type: ScalarValue + argument: InstanceAge + desc: | + Z position offset. + width: + type: ScalarValue + default: 1 + argument: InstanceAge + desc: | + The width of the particle. + + If {@link uniformScale} is enabled, this also controls the height. + height: + type: ScalarValue + default: 1 + argument: InstanceAge + desc: | + The height of the particle. + + If {@link uniformScale} is enabled, {@link width} also controls the height, and this property is ignored. + rotationX: + type: ScalarValue + argument: Constant0 + desc: | + Rotation around the X-axis in degrees. + see: + - rotationSpeedX + - rotationSpeedMultiplierX + rotationY: + type: ScalarValue + argument: Constant0 + desc: | + Rotation around the Y-axis in degrees. + see: + - rotationSpeedY + - rotationSpeedMultiplierY + rotationZ: + type: ScalarValue + argument: Constant0 + desc: | + Rotation around the Z-axis in degrees. + see: + - rotationSpeedZ + - rotationSpeedMultiplierZ + rotationSpeedX: + type: ScalarValue + argument: InstanceAge + desc: | + Rotation speed around the X-axis in degrees per second. + see: + - rotationX + - rotationSpeedMultiplierX + rotationSpeedY: + type: ScalarValue + argument: InstanceAge + desc: | + Rotation speed around the Y-axis in degrees per second. + see: + - rotationY + - rotationSpeedMultiplierY + rotationSpeedZ: + type: ScalarValue + argument: InstanceAge + desc: | + Rotation speed around the Z-axis in degrees per second. + see: + - rotationZ + - rotationSpeedMultiplierZ + rotationSpeedMultiplierX: + type: ScalarValue + default: 1 + argument: InstanceAge + desc: | + Multiplier for {@link rotationSpeedX}. + see: + - rotationX + rotationSpeedMultiplierY: + type: ScalarValue + default: 1 + argument: InstanceAge + desc: | + Multiplier for {@link rotationSpeedY}. + see: + - rotationY + rotationSpeedMultiplierZ: + type: ScalarValue + default: 1 + argument: InstanceAge + desc: | + Multiplier for {@link rotationSpeedZ}. + see: + - rotationZ + color1: + type: Vector4Value + default: [1, 1, 1, 1] + argument: InstanceAge + desc: | + Color multiplier for the particle. + color2: + type: Vector4Value + default: [1, 1, 1, 1] + argument: EmissionTime + desc: | + Color multiplier for the particle. + color3: + type: Vector4Value + default: [1, 1, 1, 1] + argument: InstanceAge + desc: | + Color multiplier for the particle. + layersColor: + type: Vector4Value + default: [1, 1, 1, 1] + argument: InstanceAge + desc: | + Color multiplier for both of the texture layers. + layer1Color: + type: Vector4Value + default: [1, 1, 1, 1] + argument: InstanceAge + desc: | + Color multiplier for Layer 1. + layer2Color: + type: Vector4Value + default: [1, 1, 1, 1] + argument: InstanceAge + desc: | + Color multiplier for Layer 2. + alphaThreshold: + type: ScalarValue + argument: InstanceAge + desc: | + Parts of the particle with less opacity than this threshold will be invisible. The range is 0-255. + frameIndex: + type: ScalarValue + argument: InstanceAge + desc: | + The index of the frame to show from the texture atlas. Can be animated using a {@link PropertyFunction.Linear linear property} or similar. + + Seemingly identical to {@link frameIndexOffset}? The sum of these two properties is the actual frame index that gets used. + frameIndexOffset: + type: ScalarValue + argument: InstanceAge + desc: | + Seemingly identical to {@link frameIndex}? The sum of these two properties is the actual frame index that gets used. + layer1SpeedU: + type: ScalarValue + default: 0 + argument: InstanceAge + desc: | + Horiztonal scroll speed for Layer 1. + layer1SpeedV: + type: ScalarValue + default: 0 + argument: InstanceAge + desc: | + Vertical scroll speed for Layer 1. + layer1OffsetU: + type: ScalarValue + default: 0 + argument: Constant0 + desc: | + Horizontal offset for the UV coordinates of Layer 1. + layer1OffsetV: + type: ScalarValue + default: 0 + argument: Constant0 + desc: | + Vertical offset for the UV coordinates of Layer 1. + layer1ScaleU: + type: ScalarValue + default: 1 + argument: InstanceAge + desc: | + Horizontal scale for the UV coordinates of Layer 1. + layer1ScaleV: + type: ScalarValue + default: 1 + argument: InstanceAge + desc: | + Vertical scale for the UV coordinates of Layer 1. + layer2SpeedU: + type: ScalarValue + default: 0 + argument: InstanceAge + desc: | + Horiztonal scroll speed for Layer 2. + layer2SpeedV: + type: ScalarValue + default: 0 + argument: InstanceAge + desc: | + Vertical scroll speed for Layer 2. + layer2OffsetU: + type: ScalarValue + default: 0 + argument: Constant0 + desc: | + Horizontal offset for the UV coordinates of Layer 2. + layer2OffsetV: + type: ScalarValue + default: 0 + argument: Constant0 + desc: | + Vertical offset for the UV coordinates of Layer 2. + layer2ScaleU: + type: ScalarValue + default: 1 + argument: InstanceAge + desc: | + Horizontal scale for the UV coordinates of Layer 2. + layer2ScaleV: + type: ScalarValue + default: 1 + argument: InstanceAge + desc: | + Vertical scale for the UV coordinates of Layer 2. + rgbMultiplier: + type: ScalarValue + default: 1 + argument: EffectAge + desc: | + Scalar multiplier for the color that does not affect the alpha. Effectively a brightness multiplier. + alphaMultiplier: + type: ScalarValue + default: 1 + argument: EffectAge + desc: | + Alpha multiplier. + unk_ds3_f1_6: + field: int + unk_ds3_f1_10: + field: int + default: -2 + unk_ds3_f1_11: + field: int + default: -2 + unk_ds3_f1_14: + field: int + unk_ds3_f2_0: + field: int + unk_ds3_f2_1: + field: int + unk_ds3_f2_2: + field: int + default: 8 + unk_ds3_f2_3: + field: int + unk_ds3_f2_4: + field: int + default: 1 + unk_ds3_f2_9: + field: int + unk_ds3_f2_10: + field: int + unk_ds3_f2_11: + field: int + unk_ds3_f2_12: + field: int + unk_ds3_f2_13: + field: int + unkDistFadeClose0: + field: float + default: -1 + unkDistFadeClose1: + field: float + default: -1 + unkDistFadeFar0: + field: float + default: -1 + unkDistFadeFar1: + field: float + default: -1 + unk_ds3_f2_20: + field: int + unk_ds3_f2_21: + field: int + unk_ds3_f2_22: + field: int + unk_ds3_f2_23: + field: int + unk_ds3_f2_24: + field: int + unkDepthBlend1: + field: float + default: 1 + unkDepthBlend2: + field: float + unk_ds3_f2_27: + field: int + default: 1 + unk_ds3_f2_28: + field: int + unk_ds3_f2_29: + field: float + default: 5 + unk_ds3_p1_23: + type: ScalarValue + unk_ds3_p1_24: + type: ScalarValue + unk_ds3_p1_25: + type: ScalarValue + unk_ds3_p1_26: + type: ScalarValue + unk_ds3_p1_27: + type: ScalarValue + default: 1 + unk_ds3_p1_28: + type: ScalarValue + default: 1 + unk_ds3_p2_2: + type: ScalarValue + unk_ds3_p2_3: + type: Vector4Value + default: [1, 1, 1, 1] + unk_ds3_p2_4: + type: Vector4Value + default: [1, 1, 1, 1] + unk_ds3_p2_5: + type: Vector4Value + default: [1, 1, 1, 1] + unk_ds3_p2_6: + type: ScalarValue + unk_sdt_f2_31: + field: int + unk_sdt_f2_32: + field: int + default: 1 + unk_sdt_f2_36: + field: int + default: -2 + unk_sdt_f2_37: + field: int + unk_sdt_f2_39: + field: int + default: 1 + unk_sdt_f2_40: + field: int + unk_sdt_f2_41: + field: int + unk_er_f1_14: + field: int + default: 1 + unk_er_f1_15: + field: int + default: 1 + unk_er_f1_16: + field: int + unk_er_f2_42: + field: int + unk_er_f2_43: + field: int + unk_er_f2_44: + field: int + unk_er_f2_45: + field: int + unk_ac6_f2_46: + field: int +games: + DS3: + fields1: + - orientation + - mask + - layer1 + - layer2 + - blendMode + - uniformScale + - unk_ds3_f1_6 + - columns + - totalFrames + - interpolateFrames + - unk_ds3_f1_10 + - unk_ds3_f1_11 + - depthBlend + - octagonal + - unk_ds3_f1_14 + fields2: + - unk_ds3_f2_0 + - unk_ds3_f2_1 + - unk_ds3_f2_2 + - unk_ds3_f2_3 + - unk_ds3_f2_4 + - bloomRed + - bloomGreen + - bloomBlue + - bloomStrength + - unk_ds3_f2_9 + - unk_ds3_f2_10 + - unk_ds3_f2_11 + - unk_ds3_f2_12 + - unk_ds3_f2_13 + - unkDistFadeClose0 + - unkDistFadeClose1 + - unkDistFadeFar0 + - unkDistFadeFar1 + - minDistance + - maxDistance + - unk_ds3_f2_20 + - unk_ds3_f2_21 + - unk_ds3_f2_22 + - unk_ds3_f2_23 + - unk_ds3_f2_24 + - unkDepthBlend1 + - unkDepthBlend2 + - unk_ds3_f2_27 + - unk_ds3_f2_28 + - unk_ds3_f2_29 + properties1: + - offsetX + - offsetY + - offsetZ + - width + - height + - rotationX + - rotationY + - rotationZ + - rotationSpeedX + - rotationSpeedMultiplierX + - rotationSpeedY + - rotationSpeedMultiplierY + - rotationSpeedZ + - rotationSpeedMultiplierZ + - color1 + - color2 + - color3 + - layersColor + - layer1Color + - layer2Color + - alphaThreshold + - frameIndex + - frameIndexOffset + - unk_ds3_p1_23 + - unk_ds3_p1_24 + - unk_ds3_p1_25 + - unk_ds3_p1_26 + - unk_ds3_p1_27 + - unk_ds3_p1_28 + - layer1SpeedU + - layer1SpeedV + - layer1OffsetU + - layer1OffsetV + - layer1ScaleU + - layer1ScaleV + - layer2SpeedU + - layer2SpeedV + - layer2OffsetU + - layer2OffsetV + - layer2ScaleU + - layer2ScaleV + properties2: + - rgbMultiplier + - alphaMultiplier + - unk_ds3_p2_2 + - unk_ds3_p2_3 + - unk_ds3_p2_4 + - unk_ds3_p2_5 + - unk_ds3_p2_6 + SDT: + fields1: + - orientation + - mask + - layer1 + - layer2 + - uniformScale + - unk_ds3_f1_6 + - columns + - totalFrames + - interpolateFrames + - unk_ds3_f1_10 + - unk_ds3_f1_11 + - depthBlend + - octagonal + - unk_ds3_f1_14 + fields2: + - unk_ds3_f2_0 + - unk_ds3_f2_1 + - unk_ds3_f2_2 + - unk_ds3_f2_3 + - unk_ds3_f2_4 + - bloomRed + - bloomGreen + - bloomBlue + - bloomStrength + - unk_ds3_f2_9 + - unk_ds3_f2_10 + - unk_ds3_f2_11 + - unk_ds3_f2_12 + - unk_ds3_f2_13 + - unkDistFadeClose0 + - unkDistFadeClose1 + - unkDistFadeFar0 + - unkDistFadeFar1 + - minDistance + - maxDistance + - unk_ds3_f2_20 + - unk_ds3_f2_21 + - unk_ds3_f2_22 + - unk_ds3_f2_23 + - unk_ds3_f2_24 + - unkDepthBlend1 + - unkDepthBlend2 + - unk_ds3_f2_27 + - unk_ds3_f2_28 + - unk_ds3_f2_29 + - shadowDarkness + - unk_sdt_f2_31 + - unk_sdt_f2_32 + - specular + - glossiness + - lighting + - unk_sdt_f2_36 + - unk_sdt_f2_37 + - unk_sdt_f2_38 + - unk_sdt_f2_39 + - unk_sdt_f2_40 + - unk_sdt_f2_41 + properties1: + - blendMode + - offsetX + - offsetY + - offsetZ + - width + - height + - rotationX + - rotationY + - rotationZ + - rotationSpeedX + - rotationSpeedMultiplierX + - rotationSpeedY + - rotationSpeedMultiplierY + - rotationSpeedZ + - rotationSpeedMultiplierZ + - color1 + - color2 + - color3 + - layersColor + - layer1Color + - layer2Color + - alphaThreshold + - frameIndex + - frameIndexOffset + - unk_ds3_p1_23 + - unk_ds3_p1_24 + - unk_ds3_p1_25 + - unk_ds3_p1_26 + - unk_ds3_p1_27 + - unk_ds3_p1_28 + - layer1SpeedU + - layer1SpeedV + - layer1OffsetU + - layer1OffsetV + - layer1ScaleU + - layer1ScaleV + - layer2SpeedU + - layer2SpeedV + - layer2OffsetU + - layer2OffsetV + - layer2ScaleU + - layer2ScaleV + properties2: DS3 + ER: + fields1: + - orientation + - mask + - layer1 + - layer2 + - uniformScale + - unk_ds3_f1_6 + - columns + - totalFrames + - interpolateFrames + - unk_ds3_f1_10 + - unk_ds3_f1_11 + - depthBlend + - octagonal + - unk_ds3_f1_14 + - unk_er_f1_14 + - unk_er_f1_15 + - unk_er_f1_16 + fields2: + - unk_ds3_f2_0 + - unk_ds3_f2_1 + - unk_ds3_f2_2 + - unk_ds3_f2_3 + - unk_ds3_f2_4 + - bloomRed + - bloomGreen + - bloomBlue + - bloomStrength + - unk_ds3_f2_9 + - unk_ds3_f2_10 + - unk_ds3_f2_11 + - unk_ds3_f2_12 + - unk_ds3_f2_13 + - unkDistFadeClose0 + - unkDistFadeClose1 + - unkDistFadeFar0 + - unkDistFadeFar1 + - minDistance + - maxDistance + - unk_ds3_f2_20 + - unk_ds3_f2_21 + - unk_ds3_f2_22 + - unk_ds3_f2_23 + - unk_ds3_f2_24 + - unkDepthBlend1 + - unkDepthBlend2 + - unk_ds3_f2_27 + - unk_ds3_f2_28 + - unk_ds3_f2_29 + - shadowDarkness + - unk_sdt_f2_31 + - unk_sdt_f2_32 + - specular + - glossiness + - lighting + - unk_sdt_f2_36 + - unk_sdt_f2_37 + - unk_sdt_f2_38 + - unk_sdt_f2_39 + - unk_sdt_f2_40 + - unk_sdt_f2_41 + - unk_er_f2_42 + - unk_er_f2_43 + - unk_er_f2_44 + - unk_er_f2_45 + properties1: SDT + properties2: DS3 + AC6: + fields1: ER + fields2: + - unk_ds3_f2_0 + - unk_ds3_f2_1 + - unk_ds3_f2_2 + - unk_ds3_f2_3 + - unk_ds3_f2_4 + - bloomRed + - bloomGreen + - bloomBlue + - bloomStrength + - unk_ds3_f2_9 + - unk_ds3_f2_10 + - unk_ds3_f2_11 + - unk_ds3_f2_12 + - unk_ds3_f2_13 + - unkDistFadeClose0 + - unkDistFadeClose1 + - unkDistFadeFar0 + - unkDistFadeFar1 + - minDistance + - maxDistance + - unk_ds3_f2_20 + - unk_ds3_f2_21 + - unk_ds3_f2_22 + - unk_ds3_f2_23 + - unk_ds3_f2_24 + - unkDepthBlend1 + - unkDepthBlend2 + - unk_ds3_f2_27 + - unk_ds3_f2_28 + - unk_ds3_f2_29 + - shadowDarkness + - unk_sdt_f2_31 + - unk_sdt_f2_32 + - specular + - glossiness + - lighting + - unk_sdt_f2_36 + - unk_sdt_f2_37 + - unk_sdt_f2_38 + - unk_sdt_f2_39 + - unk_sdt_f2_40 + - unk_sdt_f2_41 + - unk_er_f2_42 + - unk_er_f2_43 + - unk_er_f2_44 + - unk_er_f2_45 + - unk_ac6_f2_46 + properties1: SDT + properties2: DS3 diff --git a/src/actions/605.yml b/src/actions/605.yml new file mode 100644 index 0000000..940b3b4 --- /dev/null +++ b/src/actions/605.yml @@ -0,0 +1,778 @@ +type: 605 +name: Model +desc: | + Particle with a 3D model. +properties: + orientation: + type: OrientationMode + field: int + default: OrientationMode.LocalSouth + desc: | + Controls the orientation mode for the particles. See {@link OrientationMode} for more information. + scaleVariationX: + field: float + default: 1 + desc: | + Each particle will pick a random number between this value and 1, and the width of the particle will be multiplied by this number. For example, setting this to 0.5 will make the particles randomly thinner, down to half width. Setting it to 2 will make them randomly wider, up to double width. + + If {@link uniformScale} is enabled, this also affects the height. + see: + - scaleVariationY + - scaleVariationZ + scaleVariationY: + field: float + default: 1 + desc: | + Each particle will pick a random number between this value and 1, and the height of the particle will be multiplied by this number. For example, setting this to 0.5 will make the particles randomly shorter, down to half height. Setting it to 2 will make them randomly taller, up to double height. + + If {@link uniformScale} is enabled, {@link scaleVariationX} also affects the height, and this field is ignored. + see: + - scaleVariationX + - scaleVariationZ + scaleVariationZ: + field: float + default: 1 + desc: | + Each particle will pick a random number between this value and 1, and the depth of the particle will be multiplied by this number. For example, setting this to 0.5 will make the particles randomly shallower, down to half depth. Setting it to 2 will make them randomly deeper, up to double depth. + + If {@link uniformScale} is enabled, {@link scaleVariationX} also affects the depth, and this field is ignored. + see: + - scaleVariationX + - scaleVariationY + uniformScale: + field: bool + default: false + desc: | + If enabled, the particle X scale-related properties and fields will control the scale in all axes, and the Y and Z counterparts will be ignored. + see: + - sizeX + - sizeY + - sizeZ + - scaleVariationX + - scaleVariationY + - scaleVariationZ + columns: + field: int + default: 1 + desc: | + To split the texture into multiple animation frames, this value must be set to the number of columns in the texture. It should equal `textureWidth / frameWidth`. + see: + - totalFrames + totalFrames: + field: int + default: 1 + desc: | + To split the texture into multiple animation frames, this value must be set to the total number of frames in the texture. + see: + - columns + bloomRed: + field: float + default: 1 + desc: | + Controls the redness of the color of the additional bloom effect. The colors of the particle will be multiplied with this color to get the final color of the bloom effect. + + Note: + - This has no effect if the "Effects Quality" setting is set to "Low". + - This does not affect the natural bloom effect from high color values. + see: + - bloomGreen + - bloomBlue + - bloomStrength + bloomGreen: + field: float + default: 1 + desc: | + Controls the greenness of the color of the additional bloom effect. The colors of the particle will be multiplied with this color to get the final color of the bloom effect. + + Note: + - This has no effect if the "Effects Quality" setting is set to "Low". + - This does not affect the natural bloom effect from high color values. + see: + - bloomRed + - bloomBlue + - bloomStrength + bloomBlue: + field: float + default: 1 + desc: | + Controls the blueness of the color of the additional bloom effect. The colors of the particle will be multiplied with this color to get the final color of the bloom effect. + + Note: + - This has no effect if the "Effects Quality" setting is set to "Low". + - This does not affect the natural bloom effect from high color values. + see: + - bloomRed + - bloomGreen + - bloomStrength + bloomStrength: + field: float + desc: | + Controls the strength of the additional bloom effect. + + Note: + - This has no effect if the "Effects Quality" setting is set to "Low". + - This does not affect the natural bloom effect from high color values. + see: + - bloomRed + - bloomGreen + - bloomBlue + minDistance: + field: float + default: -1 + desc: | + Minimum view distance. If the particle is closer than this distance from the camera, it will be hidden. Can be set to -1 to disable the limit. + see: + - maxDistance + maxDistance: + field: float + default: -1 + desc: | + Maximum view distance. If the particle is farther away than this distance from the camera, it will be hidden. Can be set to -1 to disable the limit. + see: + - minDistance + model: + type: ScalarValue + field: int + default: 80201 + argument: Constant0 + desc: | + Model ID. + sizeX: + type: ScalarValue + default: 1 + argument: InstanceAge + desc: | + The width of the particle. + + If {@link uniformScale} is enabled, this also controls the height and depth. + see: + - scaleVariationX + - sizeY + - sizeZ + sizeY: + type: ScalarValue + default: 1 + argument: InstanceAge + desc: | + The height of the particle. + + If {@link uniformScale} is enabled, {@link sizeX} also controls the height, and this property is ignored. + see: + - scaleVariationY + - sizeX + - sizeZ + sizeZ: + type: ScalarValue + default: 1 + argument: InstanceAge + desc: | + The depth of the particle. + + If {@link uniformScale} is enabled, {@link sizeX} also controls the depth, and this property is ignored. + see: + - scaleVariationZ + - sizeX + - sizeY + rotationX: + type: ScalarValue + argument: Constant0 + desc: | + Rotation around the X-axis in degrees. + see: + - rotationSpeedX + - rotationSpeedMultiplierX + rotationY: + type: ScalarValue + argument: Constant0 + desc: | + Rotation around the Y-axis in degrees. + see: + - rotationSpeedY + - rotationSpeedMultiplierY + rotationZ: + type: ScalarValue + argument: Constant0 + desc: | + Rotation around the Z-axis in degrees. + see: + - rotationSpeedZ + - rotationSpeedMultiplierZ + rotationSpeedX: + type: ScalarValue + argument: InstanceAge + desc: | + Rotation speed around the X-axis in degrees per second. + see: + - rotationX + - rotationSpeedMultiplierX + rotationSpeedY: + type: ScalarValue + argument: InstanceAge + desc: | + Rotation speed around the Y-axis in degrees per second. + see: + - rotationY + - rotationSpeedMultiplierY + rotationSpeedZ: + type: ScalarValue + argument: InstanceAge + desc: | + Rotation speed around the Z-axis in degrees per second. + see: + - rotationZ + - rotationSpeedMultiplierZ + rotationSpeedMultiplierX: + type: ScalarValue + default: 1 + argument: InstanceAge + desc: | + Multiplier for {@link rotationSpeedX}. + see: + - rotationX + rotationSpeedMultiplierY: + type: ScalarValue + default: 1 + argument: InstanceAge + desc: | + Multiplier for {@link rotationSpeedY}. + see: + - rotationY + rotationSpeedMultiplierZ: + type: ScalarValue + default: 1 + argument: InstanceAge + desc: | + Multiplier for {@link rotationSpeedZ}. + see: + - rotationZ + blendMode: + type: BlendMode | ScalarProperty + field: int + default: BlendMode.Normal + argument: Constant0 + desc: | + Blend mode. + + Note that the materials used by the model may affect how the different blend modes work. Don't expect the blend modes to always work exactly like they do in other types of instances. + color1: + type: Vector4Value + default: [1, 1, 1, 1] + argument: InstanceAge + desc: | + Color multiplier for the particle. + color2: + type: Vector4Value + default: [1, 1, 1, 1] + argument: EmissionTime + desc: | + Color multiplier for the particle. + color3: + type: Vector4Value + default: [1, 1, 1, 1] + argument: InstanceAge + desc: | + Color multiplier for the particle. + frameIndex: + type: ScalarValue + argument: InstanceAge + desc: | + The index of the frame to show from the texture atlas. Can be animated using a {@link PropertyFunction.Linear linear property} or similar. + + Seemingly identical to {@link frameIndexOffset}? The sum of these two properties is the actual frame index that gets used. + frameIndexOffset: + type: ScalarValue + argument: InstanceAge + desc: | + Seemingly identical to {@link frameIndex}? The sum of these two properties is the actual frame index that gets used. + offsetU: + type: ScalarValue + default: 0 + argument: Constant0 + desc: | + Horizontal offset for the UV coordinates of the model. + + If the texture is an animation sheet that is split up into multiple frames using {@link columns} and/or {@link totalFrames}, this property has no effect. + see: + - speedU + - offsetV + offsetV: + type: ScalarValue + default: 0 + argument: Constant0 + desc: | + Vertical offset for the UV coordinates of the model. + + If the texture is an animation sheet that is split up into multiple frames using {@link columns} and/or {@link totalFrames}, this property has no effect. + speedU: + type: ScalarValue + default: 0 + argument: InstanceAge + desc: | + Horiztonal scroll speed for the model's texture. + + If the texture is an animation sheet that is split up into multiple frames using {@link columns} and/or {@link totalFrames}, this property has no effect. + see: + - speedMultiplierU + - offsetU + speedMultiplierU: + type: ScalarValue + default: 0 + argument: InstanceAge + desc: | + Multiplier for {@link speedU}. + speedV: + type: ScalarValue + default: 0 + argument: InstanceAge + desc: | + Vertical scroll speed for the model's texture. + + If the texture is an animation sheet that is split up into multiple frames using {@link columns} and/or {@link totalFrames}, this property has no effect. + see: + - speedMultiplierV + - offsetV + speedMultiplierV: + type: ScalarValue + default: 0 + argument: InstanceAge + desc: | + Multiplier for {@link speedV}. + rgbMultiplier: + type: ScalarValue + default: 1 + argument: EffectAge + desc: | + Scalar multiplier for the color that does not affect the alpha. Effectively a brightness multiplier. + alphaMultiplier: + type: ScalarValue + default: 1 + argument: EffectAge + desc: | + Alpha multiplier. + unk_ds3_f1_9: + field: int + default: -2 + unk_ds3_f1_10: + field: int + default: -2 + unk_ds3_f1_11: + field: bool + default: true + unk_ds3_f1_12: + field: bool + default: true + unk_ds3_f1_13: + field: int + default: 1 + animation: + field: int + desc: | + Animation ID. + see: + - loopAnimation + - animationSpeed + unk_ds3_f1_15: + field: int + desc: | + Unknown. Probably a boolean. Seems to just disable the {@link animation} if set to anything but 0. + loopAnimation: + field: bool + default: 1 + desc: | + If disabled, the animation will only play once and then freeze on the last frame. If enabled, the animation will loop. + see: + - animation + - animationSpeed + animationSpeed: + field: float + default: 1 + desc: | + Controls the speed at which the {@link animation} plays. + see: + - animation + - loopAnimation + unk_ds3_f1_18: + field: int + unk_ds3_f2_0: + field: int + unk_ds3_f2_1: + field: int + unk_ds3_f2_2: + field: int + default: 8 + unk_ds3_f2_3: + field: int + unk_ds3_f2_4: + field: int + default: 1 + unk_ds3_f2_9: + field: int + unk_ds3_f2_10: + field: int + unk_ds3_f2_11: + field: int + unk_ds3_f2_12: + field: int + unk_ds3_f2_13: + field: int + unkDistFadeClose0: + field: float + default: -1 + unkDistFadeClose1: + field: float + default: -1 + unkDistFadeFar0: + field: float + default: -1 + unkDistFadeFar1: + field: float + default: -1 + unk_ds3_f2_20: + field: int + unk_ds3_f2_21: + field: int + unk_ds3_f2_22: + field: int + unk_ds3_f2_23: + field: int + unk_ds3_f2_24: + field: float + unkDepthBlend1: + field: float + default: 1 + unkDepthBlend2: + field: float + unk_ds3_f2_27: + field: int + default: 1 + unk_ds3_f2_28: + field: int + unk_ds3_p1_15: + type: ScalarValue + unk_ds3_p1_24: + type: ScalarValue + unk_ds3_p2_2: + type: ScalarValue + unk_ds3_p2_3: + type: Vector4Value + default: [1, 1, 1, 1] + unk_ds3_p2_4: + type: Vector4Value + default: [1, 1, 1, 1] + unk_ds3_p2_5: + type: Vector4Value + default: [1, 1, 1, 1] + unk_ds3_p2_6: + type: ScalarValue + unk_sdt_f2_29: + field: float + unk_sdt_f2_30: + field: float + unk_sdt_f2_31: + field: int + unk_sdt_f2_32: + field: int + default: 1 + unk_sdt_f2_33: + field: int + unk_sdt_f2_34: + field: float + unk_sdt_f2_35: + field: int + default: -2 + unk_sdt_f2_36: + field: int + default: -2 + unk_sdt_f2_37: + field: int + unk_er_f1_17: + field: int + default: 1 + unk_er_f1_18: + field: int + default: 1 + unk_er_f1_19: + field: int + unk_ac6_f2_38: + field: int +games: + DS3: + fields1: + - orientation + - model + - scaleVariationX + - scaleVariationY + - scaleVariationZ + - uniformScale + - blendMode + - columns + - totalFrames + - unk_ds3_f1_9 + - unk_ds3_f1_10 + - unk_ds3_f1_11 + - unk_ds3_f1_12 + - unk_ds3_f1_13 + - animation + - unk_ds3_f1_15 + - loopAnimation + - animationSpeed + - unk_ds3_f1_18 + fields2: + - unk_ds3_f2_0 + - unk_ds3_f2_1 + - unk_ds3_f2_2 + - unk_ds3_f2_3 + - unk_ds3_f2_4 + - bloomRed + - bloomGreen + - bloomBlue + - bloomStrength + - unk_ds3_f2_9 + - unk_ds3_f2_10 + - unk_ds3_f2_11 + - unk_ds3_f2_12 + - unk_ds3_f2_13 + - unkDistFadeClose0 + - unkDistFadeClose1 + - unkDistFadeFar0 + - unkDistFadeFar1 + - minDistance + - maxDistance + - unk_ds3_f2_20 + - unk_ds3_f2_21 + - unk_ds3_f2_22 + - unk_ds3_f2_23 + - unk_ds3_f2_24 + - unkDepthBlend1 + - unkDepthBlend2 + - unk_ds3_f2_27 + - unk_ds3_f2_28 + properties1: + - sizeX + - sizeY + - sizeZ + - rotationX + - rotationY + - rotationZ + - rotationSpeedX + - rotationSpeedMultiplierX + - rotationSpeedY + - rotationSpeedMultiplierY + - rotationSpeedZ + - rotationSpeedMultiplierZ + - color1 + - color2 + - color3 + - unk_ds3_p1_15 + - frameIndex + - frameIndexOffset + - offsetU + - offsetV + - speedU + - speedMultiplierU + - speedV + - speedMultiplierV + - unk_ds3_p1_24 + properties2: + - rgbMultiplier + - alphaMultiplier + - unk_ds3_p2_2 + - unk_ds3_p2_3 + - unk_ds3_p2_4 + - unk_ds3_p2_5 + - unk_ds3_p2_6 + SDT: + fields1: + - orientation + - scaleVariationX + - scaleVariationY + - scaleVariationZ + - uniformScale + - columns + - totalFrames + - unk_ds3_f1_9 + - unk_ds3_f1_10 + - unk_ds3_f1_11 + - unk_ds3_f1_12 + - unk_ds3_f1_13 + - animation + - unk_ds3_f1_15 + - loopAnimation + - animationSpeed + - unk_ds3_f1_18 + fields2: + - unk_ds3_f2_0 + - unk_ds3_f2_1 + - unk_ds3_f2_2 + - unk_ds3_f2_3 + - unk_ds3_f2_4 + - bloomRed + - bloomGreen + - bloomBlue + - bloomStrength + - unk_ds3_f2_9 + - unk_ds3_f2_10 + - unk_ds3_f2_11 + - unk_ds3_f2_12 + - unk_ds3_f2_13 + - unkDistFadeClose0 + - unkDistFadeClose1 + - unkDistFadeFar0 + - unkDistFadeFar1 + - minDistance + - maxDistance + - unk_ds3_f2_20 + - unk_ds3_f2_21 + - unk_ds3_f2_22 + - unk_ds3_f2_23 + - unk_ds3_f2_24 + - unkDepthBlend1 + - unkDepthBlend2 + - unk_ds3_f2_27 + - unk_ds3_f2_28 + - unk_sdt_f2_29 + - unk_sdt_f2_30 + - unk_sdt_f2_31 + - unk_sdt_f2_32 + - unk_sdt_f2_33 + - unk_sdt_f2_34 + - unk_sdt_f2_35 + - unk_sdt_f2_36 + - unk_sdt_f2_37 + properties1: + - model + - sizeX + - sizeY + - sizeZ + - rotationX + - rotationY + - rotationZ + - rotationSpeedX + - rotationSpeedMultiplierX + - rotationSpeedY + - rotationSpeedMultiplierY + - rotationSpeedZ + - rotationSpeedMultiplierZ + - blendMode + - color1 + - color2 + - color3 + - unk_ds3_p1_15 + - frameIndex + - frameIndexOffset + - offsetU + - offsetV + - speedU + - speedMultiplierU + - speedV + - speedMultiplierV + - unk_ds3_p1_24 + properties2: DS3 + ER: + fields1: + - orientation + - scaleVariationX + - scaleVariationY + - scaleVariationZ + - uniformScale + - columns + - totalFrames + - unk_ds3_f1_9 + - unk_ds3_f1_10 + - unk_ds3_f1_11 + - unk_ds3_f1_12 + - unk_ds3_f1_13 + - animation + - unk_ds3_f1_15 + - loopAnimation + - animationSpeed + - unk_ds3_f1_18 + - unk_er_f1_17 + - unk_er_f1_18 + - unk_er_f1_19 + fields2: + - unk_ds3_f2_0 + - unk_ds3_f2_1 + - unk_ds3_f2_2 + - unk_ds3_f2_3 + - unk_ds3_f2_4 + - bloomRed + - bloomGreen + - bloomBlue + - bloomStrength + - unk_ds3_f2_9 + - unk_ds3_f2_10 + - unk_ds3_f2_11 + - unk_ds3_f2_12 + - unk_ds3_f2_13 + - unkDistFadeClose0 + - unkDistFadeClose1 + - unkDistFadeFar0 + - unkDistFadeFar1 + - minDistance + - maxDistance + - unk_ds3_f2_20 + - unk_ds3_f2_21 + - unk_ds3_f2_22 + - unk_ds3_f2_23 + - unk_ds3_f2_24 + - unkDepthBlend1 + - unkDepthBlend2 + - unk_ds3_f2_27 + - unk_ds3_f2_28 + - unk_sdt_f2_29 + - unk_sdt_f2_30 + - unk_sdt_f2_31 + - unk_sdt_f2_32 + - unk_sdt_f2_33 + - unk_sdt_f2_34 + - unk_sdt_f2_35 + - unk_sdt_f2_36 + - unk_sdt_f2_37 + - unk_ac6_f2_38 + properties1: SDT + properties2: DS3 + AC6: + fields1: ER + fields2: + - unk_ds3_f2_0 + - unk_ds3_f2_1 + - unk_ds3_f2_2 + - unk_ds3_f2_3 + - unk_ds3_f2_4 + - bloomRed + - bloomGreen + - bloomBlue + - bloomStrength + - unk_ds3_f2_9 + - unk_ds3_f2_10 + - unk_ds3_f2_11 + - unk_ds3_f2_12 + - unk_ds3_f2_13 + - unkDistFadeClose0 + - unkDistFadeClose1 + - unkDistFadeFar0 + - unkDistFadeFar1 + - minDistance + - maxDistance + - unk_ds3_f2_20 + - unk_ds3_f2_21 + - unk_ds3_f2_22 + - unk_ds3_f2_23 + - unk_ds3_f2_24 + - unkDepthBlend1 + - unkDepthBlend2 + - unk_ds3_f2_27 + - unk_ds3_f2_28 + - unk_sdt_f2_29 + - unk_sdt_f2_30 + - unk_sdt_f2_31 + - unk_sdt_f2_32 + - unk_sdt_f2_33 + - unk_sdt_f2_34 + - unk_sdt_f2_35 + - unk_sdt_f2_36 + - unk_sdt_f2_37 + - unk_ac6_f2_38 + properties1: SDT + properties2: DS3 diff --git a/src/actions/606.yml b/src/actions/606.yml new file mode 100644 index 0000000..e7f5a21 --- /dev/null +++ b/src/actions/606.yml @@ -0,0 +1,570 @@ +type: 606 +name: Tracer +desc: | + Creates a trail behind moving effects. +properties: + orientation: + type: TracerOrientationMode + field: int + default: TracerOrientationMode.LocalZ + desc: | + Tracer orientation mode. See {@link TracerOrientationMode} for more information. + normalMap: + field: int + default: 0 + desc: | + Normal map texture ID. + + This is used to control the distortion effect of the trail. + see: + - distortionIntensity + segmentInterval: + field: float + default: 0 + desc: | + The trail is made up of multiple quads, or *segments*. This controls how many seconds to wait between new segments being created. Lower values produce a smoother trail. + segmentDuration: + field: float + default: 1 + desc: | + The trail is made up of multiple quads, or *segments*. This controls how long each segment should last in seconds. + concurrentSegments: + field: int + default: 100 + desc: | + The trail is made up of multiple quads, or *segments*. This controls how many segments may exist at the same time. + columns: + field: int + default: 1 + desc: | + To split the texture into multiple animation frames, this value must be set to the number of columns in the texture. It should equal `textureWidth / frameWidth`. + see: + - totalFrames + totalFrames: + field: int + default: 1 + desc: | + To split the texture into multiple animation frames, this value must be set to the total number of frames in the texture. + see: + - columns + attachedUV: + field: bool + default: 1 + desc: | + Controls whether or not the UV of the trail should be attached to the node or not. If it is attached, the texture will slide along the segments to follow the source wherever it moves, as if it was a flag attached to a pole. If it is not attached, the texture will stay where it was when the segment was created, like a skid mark on a road where the road is the segments and the mark is the texture, it wouldn't follow the car/node that made it. + bloomRed: + field: float + default: 1 + desc: | + Controls the redness of the color of the additional bloom effect. The colors of the particle will be multiplied with this color to get the final color of the bloom effect. + + Note: + - This has no effect if the "Effects Quality" setting is set to "Low". + - This does not affect the natural bloom effect from high color values. + see: + - bloomGreen + - bloomBlue + - bloomStrength + bloomGreen: + field: float + default: 1 + desc: | + Controls the greenness of the color of the additional bloom effect. The colors of the particle will be multiplied with this color to get the final color of the bloom effect. + + Note: + - This has no effect if the "Effects Quality" setting is set to "Low". + - This does not affect the natural bloom effect from high color values. + see: + - bloomRed + - bloomBlue + - bloomStrength + bloomBlue: + field: float + default: 1 + desc: | + Controls the blueness of the color of the additional bloom effect. The colors of the particle will be multiplied with this color to get the final color of the bloom effect. + + Note: + - This has no effect if the "Effects Quality" setting is set to "Low". + - This does not affect the natural bloom effect from high color values. + see: + - bloomRed + - bloomGreen + - bloomStrength + bloomStrength: + field: float + desc: | + Controls the strength of the additional bloom effect. + + Note: + - This has no effect if the "Effects Quality" setting is set to "Low". + - This does not affect the natural bloom effect from high color values. + see: + - bloomRed + - bloomGreen + - bloomBlue + minDistance: + field: float + default: -1 + desc: | + Minimum view distance. If the trail is closer than this distance from the camera, it will be hidden. Can be set to -1 to disable the limit. + see: + - maxDistance + maxDistance: + field: float + default: -1 + desc: | + Maximum view distance. If the trail is farther away than this distance from the camera, it will be hidden. Can be set to -1 to disable the limit. + see: + - minDistance + shadowDarkness: + field: float + desc: | + Controls how dark shaded parts of the trail are. + specular: + field: int + desc: | + Specular texture ID. + see: + - lighting + - glossiness + - specularity + glossiness: + field: float + default: 0.25 + desc: | + Controls how sharp the specular highlights are. + see: + - lighting + - specular + - specularity + lighting: + type: LightingMode + field: int + default: LightingMode.Unlit + desc: | + Controls how the trail is lit. See {@link LightingMode} for more information. + specularity: + field: float + default: 0.5 + desc: | + Controls how bright the specular highlights are. + see: + - lighting + - specular + - glossiness + texture: + type: ScalarValue + field: int + default: 1 + argument: Constant0 + desc: | + Texture ID. + blendMode: + type: BlendMode | ScalarProperty + field: int + default: BlendMode.Normal + argument: Constant0 + desc: | + Blend mode. + width: + type: ScalarValue + default: 1 + argument: InstanceAge + desc: | + The width of the trail. + widthMultiplier: + type: ScalarValue + default: 1 + argument: InstanceAge + desc: | + Multiplier for {@link width}. + color1: + type: Vector4Value + default: [1, 1, 1, 1] + argument: InstanceAge + desc: | + Color multiplier. + color2: + type: Vector4Value + default: [1, 1, 1, 1] + argument: EmissionTime + desc: | + Color multiplier. + color3: + type: Vector4Value + default: [1, 1, 1, 1] + argument: InstanceAge + desc: | + Color multiplier. + alphaThreshold: + type: ScalarValue + argument: InstanceAge + desc: | + Parts of the particle with less opacity than this threshold will be invisible. The range is 0-255. + frameIndex: + type: ScalarValue + argument: InstanceAge + desc: | + The index of the frame to show from the texture atlas. Can be animated using a {@link PropertyFunction.Linear linear property} or similar. + + Seemingly identical to {@link frameIndexOffset}? The sum of these two properties is the actual frame index that gets used. + frameIndexOffset: + type: ScalarValue + argument: InstanceAge + desc: | + Seemingly identical to {@link frameIndex}? The sum of these two properties is the actual frame index that gets used. + textureFraction: + type: ScalarValue + default: 0.1 + argument: InstanceAge + desc: | + Controls how much of the texture's width is used per segment. If {@link attachedUV} is enabled, this instead controls how much of the texture's width to use for the entire trail. + speedU: + type: ScalarValue + argument: InstanceAge + desc: | + Controls how fast the UV coordinates should move horizontally. + varianceV: + type: ScalarValue + argument: InstanceAge + desc: | + Controls how much the UV coordinates should be randomly offset by per segment. + rgbMultiplier: + type: ScalarValue + default: 1 + argument: EffectAge + desc: | + Scalar multiplier for the color that does not affect the alpha. Effectively a brightness multiplier. + alphaMultiplier: + type: ScalarValue + default: 1 + argument: EffectAge + desc: | + Alpha multiplier. + unk_ds3_f1_7: + field: int + unk_ds3_f1_8: + field: int + unk_ds3_f1_9: + field: float + unk_ds3_f1_13: + field: int + default: -1 + unk_ds3_f1_14: + field: int + default: -1 + unk_ds3_f1_15: + field: int + unk_ds3_f2_0: + field: int + unk_ds3_f2_1: + field: bool + unk_ds3_f2_2: + field: int + default: 8 + unk_ds3_f2_3: + field: int + unk_ds3_f2_4: + field: int + default: 1 + unk_ds3_f2_9: + field: int + unk_ds3_f2_10: + field: int + unk_ds3_f2_11: + field: int + unk_ds3_f2_12: + field: int + unk_ds3_f2_13: + field: int + unkDistFadeClose0: + field: float + default: -1 + unkDistFadeClose1: + field: float + default: -1 + unkDistFadeFar0: + field: float + default: -1 + unkDistFadeFar1: + field: float + default: -1 + unk_ds3_f2_20: + field: int + unk_ds3_f2_21: + field: int + unk_ds3_f2_22: + field: int + unk_ds3_f2_23: + field: int + unk_ds3_f2_24: + field: int + unkDepthBlend1: + field: float + default: 1 + unkDepthBlend2: + field: float + unk_ds3_f2_27: + field: int + default: 1 + unk_ds3_f2_28: + field: int + unk_ds3_f2_29: + field: float + default: 5 + unk_ds3_p1_2: + type: ScalarValue + unk_ds3_p1_3: + type: ScalarValue + unk_ds3_p1_13: + type: ScalarValue + default: -1 + distortionIntensity: + type: ScalarValue + argument: EffectAge + desc: | + Controls the intensity of the distortion effect. At 0, there is no distortion at all. + see: + - normalMap + unk_ds3_p2_3: + type: Vector4Value + default: [1, 1, 1, 1] + unk_ds3_p2_4: + type: Vector4Value + default: [1, 1, 1, 1] + unk_ds3_p2_5: + type: Vector4Value + default: [1, 1, 1, 1] + unk_ds3_p2_6: + type: ScalarValue + unk_sdt_f2_31: + field: int + unk_sdt_f2_32: + field: int + default: 1 + unk_sdt_f2_36: + field: int + default: -2 + unk_sdt_f2_37: + field: int + unk_er_f1_14: + field: int + default: 1 + unk_er_f1_15: + field: int + default: 1 + unk_er_f1_16: + field: int + unk_er_f2_39: + field: int +games: + DS3: + fields1: + - orientation + - texture + - normalMap + - blendMode + - segmentInterval + - segmentDuration + - concurrentSegments + - unk_ds3_f1_7 + - unk_ds3_f1_8 + - unk_ds3_f1_9 + - columns + - totalFrames + - attachedUV + - unk_ds3_f1_13 + - unk_ds3_f1_14 + - unk_ds3_f1_15 + fields2: + - unk_ds3_f2_0 + - unk_ds3_f2_1 + - unk_ds3_f2_2 + - unk_ds3_f2_3 + - unk_ds3_f2_4 + - bloomRed + - bloomGreen + - bloomBlue + - bloomStrength + - unk_ds3_f2_9 + - unk_ds3_f2_10 + - unk_ds3_f2_11 + - unk_ds3_f2_12 + - unk_ds3_f2_13 + - unkDistFadeClose0 + - unkDistFadeClose1 + - unkDistFadeFar0 + - unkDistFadeFar1 + - minDistance + - maxDistance + - unk_ds3_f2_20 + - unk_ds3_f2_21 + - unk_ds3_f2_22 + - unk_ds3_f2_23 + - unk_ds3_f2_24 + - unkDepthBlend1 + - unkDepthBlend2 + - unk_ds3_f2_27 + - unk_ds3_f2_28 + - unk_ds3_f2_29 + properties1: + - width + - widthMultiplier + - unk_ds3_p1_2 + - unk_ds3_p1_3 + - color1 + - color2 + - color3 + - alphaThreshold + - frameIndex + - frameIndexOffset + - textureFraction + - speedU + - varianceV + - unk_ds3_p1_13 + properties2: + - rgbMultiplier + - alphaMultiplier + - distortionIntensity + - unk_ds3_p2_3 + - unk_ds3_p2_4 + - unk_ds3_p2_5 + - unk_ds3_p2_6 + SDT: + fields1: + - orientation + - normalMap + - segmentInterval + - segmentDuration + - concurrentSegments + - unk_ds3_f1_7 + - unk_ds3_f1_8 + - unk_ds3_f1_9 + - columns + - totalFrames + - attachedUV + - unk_ds3_f1_13 + - unk_ds3_f1_14 + - unk_ds3_f1_15 + fields2: + - unk_ds3_f2_0 + - unk_ds3_f2_1 + - unk_ds3_f2_2 + - unk_ds3_f2_3 + - unk_ds3_f2_4 + - bloomRed + - bloomGreen + - bloomBlue + - bloomStrength + - unk_ds3_f2_9 + - unk_ds3_f2_10 + - unk_ds3_f2_11 + - unk_ds3_f2_12 + - unk_ds3_f2_13 + - unkDistFadeClose0 + - unkDistFadeClose1 + - unkDistFadeFar0 + - unkDistFadeFar1 + - minDistance + - maxDistance + - unk_ds3_f2_20 + - unk_ds3_f2_21 + - unk_ds3_f2_22 + - unk_ds3_f2_23 + - unk_ds3_f2_24 + - unkDepthBlend1 + - unkDepthBlend2 + - unk_ds3_f2_27 + - unk_ds3_f2_28 + - unk_ds3_f2_29 + - shadowDarkness + - unk_sdt_f2_31 + - unk_sdt_f2_32 + - specular + - glossiness + - lighting + - unk_sdt_f2_36 + - unk_sdt_f2_37 + - specularity + properties1: + - texture + - blendMode + - width + - widthMultiplier + - unk_ds3_p1_2 + - unk_ds3_p1_3 + - color1 + - color2 + - color3 + - alphaThreshold + - frameIndex + - frameIndexOffset + - textureFraction + - speedU + - varianceV + - unk_ds3_p1_13 + properties2: DS3 + ER: + fields1: + - orientation + - normalMap + - segmentInterval + - segmentDuration + - concurrentSegments + - unk_ds3_f1_7 + - unk_ds3_f1_8 + - unk_ds3_f1_9 + - columns + - totalFrames + - attachedUV + - unk_ds3_f1_13 + - unk_ds3_f1_14 + - unk_ds3_f1_15 + - unk_er_f1_14 + - unk_er_f1_15 + - unk_er_f1_16 + fields2: + - unk_ds3_f2_0 + - unk_ds3_f2_1 + - unk_ds3_f2_2 + - unk_ds3_f2_3 + - unk_ds3_f2_4 + - bloomRed + - bloomGreen + - bloomBlue + - bloomStrength + - unk_ds3_f2_9 + - unk_ds3_f2_10 + - unk_ds3_f2_11 + - unk_ds3_f2_12 + - unk_ds3_f2_13 + - unkDistFadeClose0 + - unkDistFadeClose1 + - unkDistFadeFar0 + - unkDistFadeFar1 + - minDistance + - maxDistance + - unk_ds3_f2_20 + - unk_ds3_f2_21 + - unk_ds3_f2_22 + - unk_ds3_f2_23 + - unk_ds3_f2_24 + - unkDepthBlend1 + - unkDepthBlend2 + - unk_ds3_f2_27 + - unk_ds3_f2_28 + - unk_ds3_f2_29 + - shadowDarkness + - unk_sdt_f2_31 + - unk_sdt_f2_32 + - specular + - glossiness + - lighting + - unk_sdt_f2_36 + - unk_sdt_f2_37 + - specularity + - unk_er_f2_39 + properties1: SDT + properties2: DS3 + AC6: ER diff --git a/src/actions/607.yml b/src/actions/607.yml new file mode 100644 index 0000000..a2506ba --- /dev/null +++ b/src/actions/607.yml @@ -0,0 +1,553 @@ +type: 607 +name: Distortion +desc: | + A particle that distorts anything seen through it. + + Note: This particle is not visible if the "Effects" setting is set to "Low". +properties: + mode: + type: DistortionMode + field: int + default: DistortionMode.NormalMap + desc: | + Controls what type of distortion to apply. See {@link DistortionMode} for more details. + shape: + type: DistortionShape + field: int + default: DistortionShape.Rectangle + desc: | + Controls the shape of the particle. See {@link DistortionShape} for more information. + orientation: + type: OrientationMode + field: int + default: OrientationMode.CameraPlane + desc: | + Controls the orientation mode for the particles. See {@link OrientationMode} for more information. + texture: + field: int + default: 0 + desc: | + Texture ID. + + This texture seems to completely hide the distortion effect. It's probably best to just leave it at 0 unless you are trying to figure out how to use it properly. + normalMap: + field: int + default: 0 + desc: | + Normal map texture ID. + + Only used if the distortion {@link mode} is set to something that uses it. + mask: + field: int + default: 0 + desc: | + Mask texture ID. This texture is used to control the color and opacity of the particle. + scaleVariationX: + field: float + default: 1 + desc: | + Each particle will pick a random number between this value and 1, and the width of the particle will be multiplied by this number. For example, setting this to 0.5 will make the particles randomly thinner, down to half width. Setting it to 2 will make them randomly wider, up to double width. + + If {@link uniformScale} is enabled, this also affects the height. + see: + - scaleVariationY + - scaleVariationZ + scaleVariationY: + field: float + default: 1 + desc: | + Each particle will pick a random number between this value and 1, and the height of the particle will be multiplied by this number. For example, setting this to 0.5 will make the particles randomly shorter, down to half height. Setting it to 2 will make them randomly taller, up to double height. + + If {@link uniformScale} is enabled, {@link scaleVariationX} also affects the height, and this field is ignored. + see: + - scaleVariationX + - scaleVariationZ + scaleVariationZ: + field: float + default: 1 + desc: | + Each particle will pick a random number between this value and 1, and the depth of the particle will be multiplied by this number. For example, setting this to 0.5 will make the particles randomly shallower, down to half depth. Setting it to 2 will make them randomly deeper, up to double depth. + + If {@link uniformScale} is enabled, {@link scaleVariationX} also affects the depth, and this field is ignored. + see: + - scaleVariationX + - scaleVariationY + uniformScale: + field: bool + default: false + desc: | + If enabled, the particle X scale-related properties and fields will control the scale in all axes, and the Y and Z counterparts will be ignored. + see: + - sizeX + - sizeY + - sizeZ + - scaleVariationX + - scaleVariationY + - scaleVariationZ + bloomRed: + field: float + default: 1 + desc: | + Controls the redness of the color of the additional bloom effect. The colors of the particle will be multiplied with this color to get the final color of the bloom effect. + + Note: + - This has no effect if the "Effects Quality" setting is set to "Low". + - This does not affect the natural bloom effect from high color values. + see: + - bloomGreen + - bloomBlue + - bloomStrength + bloomGreen: + field: float + default: 1 + desc: | + Controls the greenness of the color of the additional bloom effect. The colors of the particle will be multiplied with this color to get the final color of the bloom effect. + + Note: + - This has no effect if the "Effects Quality" setting is set to "Low". + - This does not affect the natural bloom effect from high color values. + see: + - bloomRed + - bloomBlue + - bloomStrength + bloomBlue: + field: float + default: 1 + desc: | + Controls the blueness of the color of the additional bloom effect. The colors of the particle will be multiplied with this color to get the final color of the bloom effect. + + Note: + - This has no effect if the "Effects Quality" setting is set to "Low". + - This does not affect the natural bloom effect from high color values. + see: + - bloomRed + - bloomGreen + - bloomStrength + bloomStrength: + field: float + desc: | + Controls the strength of the additional bloom effect. + + Note: + - This has no effect if the "Effects Quality" setting is set to "Low". + - This does not affect the natural bloom effect from high color values. + see: + - bloomRed + - bloomGreen + - bloomBlue + minDistance: + field: float + default: -1 + desc: | + Minimum view distance. If the particle is closer than this distance from the camera, it will be hidden. Can be set to -1 to disable the limit. + see: + - maxDistance + maxDistance: + field: float + default: -1 + desc: | + Maximum view distance. If the particle is farther away than this distance from the camera, it will be hidden. Can be set to -1 to disable the limit. + see: + - minDistance + blendMode: + type: BlendMode | ScalarProperty + field: int + default: BlendMode.Normal + argument: Constant0 + desc: | + Blend mode. + offsetX: + type: ScalarValue + argument: InstanceAge + desc: | + X position offset. + offsetY: + type: ScalarValue + argument: InstanceAge + desc: | + Y position offset. + offsetZ: + type: ScalarValue + argument: InstanceAge + desc: | + Z position offset. + sizeX: + type: ScalarValue + default: 1 + argument: InstanceAge + desc: | + The width of the particle. + + If {@link uniformScale} is enabled, this also controls the height and depth. + see: + - scaleVariationX + - sizeY + - sizeZ + sizeY: + type: ScalarValue + default: 1 + argument: InstanceAge + desc: | + The height of the particle. + + If {@link uniformScale} is enabled, {@link sizeX} also controls the height, and this property is ignored. + see: + - scaleVariationY + - sizeX + - sizeZ + sizeZ: + type: ScalarValue + default: 1 + argument: InstanceAge + desc: | + The depth of the particle. + + If {@link uniformScale} is enabled, {@link sizeX} also controls the depth, and this property is ignored. + + If the distortion {@link shape} is set to {@link DistortionShape.Rectangle Rectangle}, this property won't have any effect since the rectangle is 2-dimensional. + see: + - scaleVariationZ + - sizeX + - sizeY + color: + type: Vector4Value + default: [1, 1, 1, 1] + argument: InstanceAge + desc: | + Color multiplier. + intensity: + type: ScalarValue + default: 1 + argument: InstanceAge + desc: | + Controls the intensity of the distortion effect. At 0, there is no distortion at all. + stirSpeed: + type: ScalarValue + default: 1 + argument: InstanceAge + desc: | + Controls the speed of the stirring effect in radians per second. Requires {@link mode} to be set to {@link DistortionMode.Stir}. + radius: + type: ScalarValue + default: 1 + argument: InstanceAge + desc: | + The distortion effect is only applied to an ellipse inside the particle. This property controls how large this ellipse is. At 1, it inscribes the particle's rectangle. At values greater than 1, it is the same size as 1, but there might be strange artifacts around the edges of the distortion. + normalMapOffsetU: + type: ScalarValue + argument: Constant0 + desc: | + Horizontal offset for the {@link normalMap normal map}. + normalMapOffsetV: + type: ScalarValue + argument: Constant0 + desc: | + Vertical offset for the {@link normalMap normal map}. + normalMapSpeedU: + type: ScalarValue + argument: InstanceAge + desc: | + Horizontal offset speed for the {@link normalMap normal map}. + normalMapSpeedV: + type: ScalarValue + argument: InstanceAge + desc: | + Vertical offset speed for the {@link normalMap normal map}. + rgbMultiplier: + type: ScalarValue + default: 1 + argument: EffectAge + desc: | + Scalar multiplier for the color that does not affect the alpha. Effectively a brightness multiplier. + alphaMultiplier: + type: ScalarValue + default: 1 + argument: EffectAge + desc: | + Alpha multiplier. + unk_ds3_f1_11: + field: int + default: -2 + unk_ds3_f1_12: + field: int + unk_ds3_f2_0: + field: int + unk_ds3_f2_1: + field: int + unk_ds3_f2_2: + field: int + default: 8 + unk_ds3_f2_3: + field: float + default: 1 + unk_ds3_f2_4: + field: int + unk_ds3_f2_9: + field: int + unk_ds3_f2_10: + field: int + unk_ds3_f2_11: + field: int + unk_ds3_f2_12: + field: int + unk_ds3_f2_13: + field: int + unkDistFadeClose0: + field: float + default: -1 + unkDistFadeClose1: + field: float + default: -1 + unkDistFadeFar0: + field: float + default: -1 + unkDistFadeFar1: + field: float + default: -1 + unk_ds3_f2_20: + field: int + default: 1 + unk_ds3_f2_21: + field: int + unk_ds3_f2_22: + field: int + unk_ds3_f2_23: + field: int + unk_ds3_f2_24: + field: int + unkDepthBlend1: + field: float + default: 1 + unkDepthBlend2: + field: float + unk_ds3_f2_27: + field: int + default: 1 + unk_ds3_f2_28: + field: int + unk_ds3_f2_29: + field: int + unk_ds3_p1_7: + type: Vector4Value + default: [1, 1, 1, 1] + unk_ds3_p1_9: + type: ScalarValue + unk_ds3_p2_2: + type: ScalarValue + unk_ds3_p2_3: + type: Vector4Value + default: [1, 1, 1, 1] + unk_ds3_p2_4: + type: Vector4Value + default: [1, 1, 1, 1] + unk_ds3_p2_5: + type: Vector4Value + default: [1, 1, 1, 1] + unk_ds3_p2_6: + type: ScalarValue + unk_sdt_f2_30: + field: int + unk_sdt_f2_31: + field: int + unk_sdt_f2_32: + field: int + unk_sdt_f2_33: + field: int + unk_sdt_f2_34: + field: int + unk_sdt_f2_35: + field: int + default: -1 + unk_sdt_f2_36: + field: int + default: -2 + unk_sdt_f2_37: + field: int + unk_sdt_f2_38: + field: int + unk_er_f1_12: + field: int + default: 1 + unk_er_f1_13: + field: int + default: 1 + unk_er_p2_7: + type: ScalarValue + default: 1 + unk_er_p2_8: + type: ScalarValue + default: 1 +games: + DS3: + fields1: + - mode + - shape + - orientation + - texture + - normalMap + - mask + - blendMode + - scaleVariationX + - scaleVariationY + - scaleVariationZ + - uniformScale + - unk_ds3_f1_11 + - unk_ds3_f1_12 + fields2: + - unk_ds3_f2_0 + - unk_ds3_f2_1 + - unk_ds3_f2_2 + - unk_ds3_f2_3 + - unk_ds3_f2_4 + - bloomRed + - bloomGreen + - bloomBlue + - bloomStrength + - unk_ds3_f2_9 + - unk_ds3_f2_10 + - unk_ds3_f2_11 + - unk_ds3_f2_12 + - unk_ds3_f2_13 + - unkDistFadeClose0 + - unkDistFadeClose1 + - unkDistFadeFar0 + - unkDistFadeFar1 + - minDistance + - maxDistance + - unk_ds3_f2_20 + - unk_ds3_f2_21 + - unk_ds3_f2_22 + - unk_ds3_f2_23 + - unk_ds3_f2_24 + - unkDepthBlend1 + - unkDepthBlend2 + - unk_ds3_f2_27 + - unk_ds3_f2_28 + - unk_ds3_f2_29 + properties1: + - offsetX + - offsetY + - offsetZ + - sizeX + - sizeY + - sizeZ + - color + - unk_ds3_p1_7 + - intensity + - unk_ds3_p1_9 + - stirSpeed + - radius + - normalMapOffsetU + - normalMapOffsetV + - normalMapSpeedU + - normalMapSpeedV + properties2: + - rgbMultiplier + - alphaMultiplier + - unk_ds3_p2_2 + - unk_ds3_p2_3 + - unk_ds3_p2_4 + - unk_ds3_p2_5 + - unk_ds3_p2_6 + SDT: + fields1: + - mode + - shape + - orientation + - texture + - normalMap + - mask + - scaleVariationX + - scaleVariationY + - scaleVariationZ + - uniformScale + - unk_ds3_f1_11 + - unk_ds3_f1_12 + fields2: + - unk_ds3_f2_0 + - unk_ds3_f2_1 + - unk_ds3_f2_2 + - unk_ds3_f2_3 + - unk_ds3_f2_4 + - bloomRed + - bloomGreen + - bloomBlue + - bloomStrength + - unk_ds3_f2_9 + - unk_ds3_f2_10 + - unk_ds3_f2_11 + - unk_ds3_f2_12 + - unk_ds3_f2_13 + - unkDistFadeClose0 + - unkDistFadeClose1 + - unkDistFadeFar0 + - unkDistFadeFar1 + - minDistance + - maxDistance + - unk_ds3_f2_20 + - unk_ds3_f2_21 + - unk_ds3_f2_22 + - unk_ds3_f2_23 + - unk_ds3_f2_24 + - unkDepthBlend1 + - unkDepthBlend2 + - unk_ds3_f2_27 + - unk_ds3_f2_28 + - unk_ds3_f2_29 + - unk_sdt_f2_30 + - unk_sdt_f2_31 + - unk_sdt_f2_32 + - unk_sdt_f2_33 + - unk_sdt_f2_34 + - unk_sdt_f2_35 + - unk_sdt_f2_36 + - unk_sdt_f2_37 + - unk_sdt_f2_38 + properties1: + - blendMode + - offsetX + - offsetY + - offsetZ + - sizeX + - sizeY + - sizeZ + - color + - unk_ds3_p1_7 + - intensity + - unk_ds3_p1_9 + - stirSpeed + - radius + - normalMapOffsetU + - normalMapOffsetV + - normalMapSpeedU + - normalMapSpeedV + properties2: DS3 + ER: + fields1: + - mode + - shape + - orientation + - texture + - normalMap + - mask + - scaleVariationX + - scaleVariationY + - scaleVariationZ + - uniformScale + - unk_ds3_f1_11 + - unk_ds3_f1_12 + - unk_er_f1_12 + - unk_er_f1_13 + fields2: SDT + properties1: SDT + properties2: + - rgbMultiplier + - alphaMultiplier + - unk_ds3_p2_2 + - unk_ds3_p2_3 + - unk_ds3_p2_4 + - unk_ds3_p2_5 + - unk_ds3_p2_6 + - unk_er_p2_7 + - unk_er_p2_8 + AC6: ER diff --git a/src/actions/608.yml b/src/actions/608.yml new file mode 100644 index 0000000..4774011 --- /dev/null +++ b/src/actions/608.yml @@ -0,0 +1,367 @@ +type: 608 +name: RadialBlur +desc: | + A particle that applies a radial blur to anything seen through it. + + Note: This particle is not visible if the "Effects" setting is set to "Low". +properties: + uniformScale: + field: bool + default: false + desc: | + If enabled, the particle width-related properties and fields will control both the width and height of the particles, and the height counterparts will be ignored. + see: + - width + - height + iterations: + field: int + default: 1 + desc: | + Controls how many times to apply the effect. Higher values can have a significant impact on performance. + bloomRed: + field: float + default: 1 + desc: | + Controls the redness of the color of the additional bloom effect. The colors of the particle will be multiplied with this color to get the final color of the bloom effect. + + Note: + - This has no effect if the "Effects Quality" setting is set to "Low". + - This does not affect the natural bloom effect from high color values. + see: + - bloomGreen + - bloomBlue + - bloomStrength + bloomGreen: + field: float + default: 1 + desc: | + Controls the greenness of the color of the additional bloom effect. The colors of the particle will be multiplied with this color to get the final color of the bloom effect. + + Note: + - This has no effect if the "Effects Quality" setting is set to "Low". + - This does not affect the natural bloom effect from high color values. + see: + - bloomRed + - bloomBlue + - bloomStrength + bloomBlue: + field: float + default: 1 + desc: | + Controls the blueness of the color of the additional bloom effect. The colors of the particle will be multiplied with this color to get the final color of the bloom effect. + + Note: + - This has no effect if the "Effects Quality" setting is set to "Low". + - This does not affect the natural bloom effect from high color values. + see: + - bloomRed + - bloomGreen + - bloomStrength + bloomStrength: + field: float + desc: | + Controls the strength of the additional bloom effect. + + Note: + - This has no effect if the "Effects Quality" setting is set to "Low". + - This does not affect the natural bloom effect from high color values. + see: + - bloomRed + - bloomGreen + - bloomBlue + minDistance: + field: float + default: -1 + desc: | + Minimum view distance. If the particle is closer than this distance from the camera, it will be hidden. Can be set to -1 to disable the limit. + see: + - maxDistance + maxDistance: + field: float + default: -1 + desc: | + Maximum view distance. If the particle is farther away than this distance from the camera, it will be hidden. Can be set to -1 to disable the limit. + see: + - minDistance + blendMode: + type: BlendMode | ScalarProperty + field: int + default: BlendMode.Normal + argument: Constant0 + desc: | + Blend mode. + mask: + type: ScalarValue + field: int + default: 1 + argument: Constant0 + desc: | + Mask texture ID. This texture is used to control the opacity of the particle. + offsetX: + type: ScalarValue + argument: InstanceAge + desc: | + X position offset. + see: + - offsetY + - offsetZ + offsetY: + type: ScalarValue + argument: InstanceAge + desc: | + Y position offset. + see: + - offsetX + - offsetZ + offsetZ: + type: ScalarValue + argument: InstanceAge + desc: | + Z position offset. + see: + - offsetX + - offsetY + width: + type: ScalarValue + default: 1 + argument: InstanceAge + desc: | + The width of the particle. + + If {@link uniformScale} is enabled, this also controls the height. + see: + - height + height: + type: ScalarValue + default: 1 + argument: InstanceAge + desc: | + The height of the particle. + + If {@link uniformScale} is enabled, {@link width} also controls the height, and this property is ignored. + see: + - width + color: + type: Vector4Value + default: [1, 1, 1, 1] + argument: InstanceAge + desc: | + Color multiplier. + blurRadius: + type: ScalarValue + default: 0.5 + argument: InstanceAge + desc: | + Controls the amount of blur to apply. Values greater than 1 may appear glitchy. + rgbMultiplier: + type: ScalarValue + default: 1 + argument: EffectAge + desc: | + Scalar multiplier for the color that does not affect the alpha. Effectively a brightness multiplier. + alphaMultiplier: + type: ScalarValue + default: 1 + argument: EffectAge + desc: | + Alpha multiplier. + unk_ds3_f1_4: + field: int + unk_ds3_f2_0: + field: int + unk_ds3_f2_1: + field: int + unk_ds3_f2_2: + field: int + default: 8 + unk_ds3_f2_3: + field: float + default: 1 + unk_ds3_f2_4: + field: int + unk_ds3_f2_9: + field: int + unk_ds3_f2_10: + field: int + unk_ds3_f2_11: + field: int + unk_ds3_f2_12: + field: int + unk_ds3_f2_13: + field: int + unkDistFadeClose0: + field: float + default: -1 + unkDistFadeClose1: + field: float + default: -1 + unkDistFadeFar0: + field: float + default: -1 + unkDistFadeFar1: + field: float + default: -1 + unk_ds3_f2_20: + field: float + default: 0.5 + unk_ds3_f2_21: + field: int + default: 1 + unk_ds3_f2_22: + field: int + unk_ds3_f2_23: + field: int + unk_ds3_f2_24: + field: int + unkDepthBlend1: + field: float + default: 1 + unkDepthBlend2: + field: float + unk_ds3_f2_27: + field: int + default: 1 + unk_ds3_f2_28: + field: int + unk_ds3_f2_29: + field: float + unk_ds3_p1_6: + type: Vector4Value + default: [1, 1, 1, 1] + unk_ds3_p2_2: + type: ScalarValue + unk_ds3_p2_3: + type: Vector4Value + default: [1, 1, 1, 1] + unk_ds3_p2_4: + type: Vector4Value + default: [1, 1, 1, 1] + unk_ds3_p2_5: + type: Vector4Value + default: [1, 1, 1, 1] + unk_ds3_p2_6: + type: ScalarValue + unk_sdt_f2_30: + field: float + unk_er_f1_3: + field: int + default: 1 + unk_er_f1_4: + field: int + default: 1 +games: + DS3: + fields1: + - blendMode + - mask + - uniformScale + - iterations + - unk_ds3_f1_4 + fields2: + - unk_ds3_f2_0 + - unk_ds3_f2_1 + - unk_ds3_f2_2 + - unk_ds3_f2_3 + - unk_ds3_f2_4 + - bloomRed + - bloomGreen + - bloomBlue + - bloomStrength + - unk_ds3_f2_9 + - unk_ds3_f2_10 + - unk_ds3_f2_11 + - unk_ds3_f2_12 + - unk_ds3_f2_13 + - unkDistFadeClose0 + - unkDistFadeClose1 + - unkDistFadeFar0 + - unkDistFadeFar1 + - minDistance + - maxDistance + - unk_ds3_f2_20 + - unk_ds3_f2_21 + - unk_ds3_f2_22 + - unk_ds3_f2_23 + - unk_ds3_f2_24 + - unkDepthBlend1 + - unkDepthBlend2 + - unk_ds3_f2_27 + - unk_ds3_f2_28 + - unk_ds3_f2_29 + properties1: + - offsetX + - offsetY + - offsetZ + - width + - height + - color + - unk_ds3_p1_6 + - blurRadius + properties2: + - rgbMultiplier + - alphaMultiplier + - unk_ds3_p2_2 + - unk_ds3_p2_3 + - unk_ds3_p2_4 + - unk_ds3_p2_5 + - unk_ds3_p2_6 + SDT: + fields1: + - uniformScale + - iterations + - unk_ds3_f1_4 + fields2: + - unk_ds3_f2_0 + - unk_ds3_f2_1 + - unk_ds3_f2_2 + - unk_ds3_f2_3 + - unk_ds3_f2_4 + - bloomRed + - bloomGreen + - bloomBlue + - bloomStrength + - unk_ds3_f2_9 + - unk_ds3_f2_10 + - unk_ds3_f2_11 + - unk_ds3_f2_12 + - unk_ds3_f2_13 + - unkDistFadeClose0 + - unkDistFadeClose1 + - unkDistFadeFar0 + - unkDistFadeFar1 + - minDistance + - maxDistance + - unk_ds3_f2_20 + - unk_ds3_f2_21 + - unk_ds3_f2_22 + - unk_ds3_f2_23 + - unk_ds3_f2_24 + - unkDepthBlend1 + - unkDepthBlend2 + - unk_ds3_f2_27 + - unk_ds3_f2_28 + - unk_ds3_f2_29 + - unk_sdt_f2_30 + properties1: + - blendMode + - mask + - offsetX + - offsetY + - offsetZ + - width + - height + - color + - unk_ds3_p1_6 + - blurRadius + properties2: DS3 + ER: + fields1: + - uniformScale + - iterations + - unk_ds3_f1_4 + - unk_er_f1_3 + - unk_er_f1_4 + fields2: SDT + properties1: SDT + properties2: DS3 + AC6: ER diff --git a/src/actions/609.yml b/src/actions/609.yml new file mode 100644 index 0000000..6795dd5 --- /dev/null +++ b/src/actions/609.yml @@ -0,0 +1,379 @@ +type: 609 +name: PointLight +desc: | + Point light source. +properties: + diffuseColor: + type: Vector4Value + default: [1, 1, 1, 1] + argument: EffectAge + desc: | + Controls the diffuse color of the light. + + If {@link separateSpecular} is disabled, this also controls the specular color of the light. + see: + - specularColor + specularColor: + type: Vector4Value + default: [1, 1, 1, 1] + argument: EffectAge + desc: | + Controls the specular color of the light. + + If {@link separateSpecular} is disabled, this property is ignored and {@link diffuseColor} controls both the diffuse as well as the specular color. + radius: + type: ScalarValue + default: 10 + argument: EffectAge + desc: | + The maximum distance that the light may travel from the source, and the radius of the sphere in which other effects caused by the light source (for example {@link volumeDensity} and its related fields) may act. + diffuseMultiplier: + type: ScalarValue + default: 1 + argument: EffectAge + desc: | + A scalar multiplier for the {@link diffuseColor diffuse color}. Good for easily adjusting the brightness of the light without changing the color. + + If {@link separateSpecular} is disabled, this also affects the specular color of the light. + specularMultiplier: + type: ScalarValue + default: 1 + argument: EffectAge + desc: | + A scalar multiplier for the {@link specularColor specular color}. + + If {@link separateSpecular} is disabled, this property is ignored. + jitterAndFlicker: + field: bool + default: false + desc: | + Toggles the jitter and flicker animations for the light. + see: + - jitterAcceleration + - jitterX + - jitterY + - jitterZ + - flickerIntervalMin + - flickerIntervalMax + - flickerBrightness + jitterAcceleration: + field: float + default: 1 + desc: | + Controls the acceleration of the jittering. + see: + - jitterAndFlicker + - jitterX + - jitterY + - jitterZ + jitterX: + field: float + desc: | + Controls how much the light should move around randomly on the X-axis. + see: + - jitterAndFlicker + - jitterAcceleration + - jitterY + - jitterZ + jitterY: + field: float + desc: | + Controls how much the light should move around randomly on the Y-axis. + see: + - jitterAndFlicker + - jitterAcceleration + - jitterX + - jitterZ + jitterZ: + field: float + desc: | + Controls how much the light should move around randomly on the Z-axis. + see: + - jitterAndFlicker + - jitterAcceleration + - jitterX + - jitterY + flickerIntervalMin: + field: float + desc: | + Controls the minimum interval for flickering. + see: + - jitterAndFlicker + - flickerIntervalMax + - flickerBrightness + flickerIntervalMax: + field: float + default: 1 + desc: | + Controls the maximum interval for flickering. + see: + - jitterAndFlicker + - flickerIntervalMin + - flickerBrightness + flickerBrightness: + field: float + default: 0.5 + desc: | + Brightness multiplier for the light when it flickers. + see: + - jitterAndFlicker + - flickerIntervalMin + - flickerIntervalMax + shadows: + field: bool + default: false + desc: | + Controls if the light should have shadows or not. + + Note: Map objects also have a setting for casting shadows, and both must be enabled for an object to cast shadows from the light source. + separateSpecular: + field: bool + default: false + desc: | + When enabled, this allows other properties and fields of the action to control the specular color independently of the diffuse color. When disabled, the diffuse counterpart of the properties or fields will affect both the diffuse and specular color. + see: + - diffuseColor + - specularColor + - diffuseMultiplier + - specularMultiplier + fadeOutTime: + field: int + default: 0 + desc: | + The number of seconds the light takes to fade to nothing after being destroyed. + + Due to how the field this represents works, the time will be rounded to the nearest multiple of 1/30s. + shadowDarkness: + field: float + default: 1 + desc: | + Controls how dark shadows from this light source are. At 0, the shadows will be entirely invisible. + volumeDensity: + field: float + default: 0 + desc: | + Controls the density of some sort of fake fog in the volume hit by the light. The fog does not affect the actual light produced by the source and is not affected by shadows. + see: + - phaseFunction + - asymmetryParam + phaseFunction: + field: bool + default: true + desc: | + Controls whether or not {@link asymmetryParam} affects the fake fog from {@link volumeDensity}. + asymmetryParam: + field: float + default: 0.75 + desc: | + Controls how the fake fog from {@link volumeDensity} scatters the light. This value is ignored if {@link phaseFunction} is disabled, and the fog will scatter the light equally in all directions. + + - At 0, the light is scattered equally in every direction. + - As the value approaches 1, the light is scattered more and more forward, in the same direction as the light was already traveling. This means that the fake fog will be less visible from the side or behind, and more visible from in front of the light. + - At 1, the fog will not scatter the light at all, so it will be entirely invisible. + - Values above 1 produce unnatural-looking effects where the light darkens the fog instead. + falloffExponent: + field: float + default: 1 + desc: | + Controls the falloff exponent of the light. + + Note: This is possibly something else, but the behavior is pretty similar to a falloff exponent in a few ways. + unk_ds3_f1_0: + field: int + unk_ds3_f1_1: + field: float + unk_ds3_f2_0: + field: int + unk_ds3_f2_3: + field: float + unk_ds3_f2_12: + field: float + default: 1 + desc: | + Unknown. Only used in Dark Souls 3. + unk_ds3_f2_15: + field: bool + default: false + unk_ds3_f2_16: + field: int + default: 2 + unk_ds3_f2_17: + field: bool + default: true + unk_ds3_f2_18: + field: float + unk_ds3_f2_19: + field: float + unk_ds3_f2_20: + field: float + unk_ds3_f2_21: + field: int + unk_ds3_f2_22: + field: int + default: 100 + unk_ds3_f2_23: + field: int + unk_ds3_f2_24: + field: int + unk_ds3_p1_3: + type: ScalarValue + unk_ds3_p1_4: + type: ScalarValue + unk_ds3_p1_5: + type: ScalarValue + unk_ds3_p1_6: + type: ScalarValue + unk_ds3_p1_7: + type: ScalarValue + default: 10 + unk_ds3_p1_8: + type: ScalarValue + default: 10 + unk_ds3_p1_9: + type: ScalarValue + default: 10 + unk_ds3_p2_0: + type: ScalarValue + default: 1 + unk_ds3_p2_1: + type: ScalarValue + default: 1 + unk_sdt_f2_25: + field: float + unk_sdt_p2_2: + type: ScalarValue + default: 1 + unk_er_f2_29: + field: int + default: 1 + unk_er_f2_30: + field: float + default: 1 + unk_er_f2_31: + field: int + default: 1 + unk_er_f2_32: + field: int +games: + DS3: + fields1: + - unk_ds3_f1_0 + - unk_ds3_f1_1 + fields2: + - unk_ds3_f2_0 + - jitterAndFlicker + - jitterAcceleration + - unk_ds3_f2_3 + - jitterX + - jitterY + - jitterZ + - flickerIntervalMin + - flickerIntervalMax + - flickerBrightness + - shadows + - separateSpecular + - unk_ds3_f2_12 + - fadeOutTime + - shadowDarkness + - unk_ds3_f2_15 + - unk_ds3_f2_16 + - unk_ds3_f2_17 + - unk_ds3_f2_18 + - unk_ds3_f2_19 + - unk_ds3_f2_20 + - unk_ds3_f2_21 + - unk_ds3_f2_22 + - unk_ds3_f2_23 + - unk_ds3_f2_24 + properties1: + - diffuseColor + - specularColor + - radius + - unk_ds3_p1_3 + - unk_ds3_p1_4 + - unk_ds3_p1_5 + - unk_ds3_p1_6 + - unk_ds3_p1_7 + - unk_ds3_p1_8 + - unk_ds3_p1_9 + properties2: + - unk_ds3_p2_0 + - unk_ds3_p2_1 + SDT: + fields1: DS3 + fields2: + - unk_ds3_f2_0 + - jitterAndFlicker + - jitterAcceleration + - unk_ds3_f2_3 + - jitterX + - jitterY + - jitterZ + - flickerIntervalMin + - flickerIntervalMax + - flickerBrightness + - shadows + - separateSpecular + - fadeOutTime + - shadowDarkness + - unk_ds3_f2_15 + - unk_ds3_f2_16 + - unk_ds3_f2_17 + - unk_ds3_f2_18 + - unk_ds3_f2_19 + - unk_ds3_f2_20 + - unk_ds3_f2_21 + - unk_ds3_f2_22 + - unk_ds3_f2_23 + - unk_ds3_f2_24 + - volumeDensity + - unk_sdt_f2_25 + - phaseFunction + - asymmetryParam + - falloffExponent + properties1: DS3 + properties2: + - unk_ds3_p2_0 + - unk_ds3_p2_1 + - unk_sdt_p2_2 + - diffuseMultiplier + - specularMultiplier + ER: + fields1: DS3 + fields2: + - unk_ds3_f2_0 + - jitterAndFlicker + - jitterAcceleration + - unk_ds3_f2_3 + - jitterX + - jitterY + - jitterZ + - flickerIntervalMin + - flickerIntervalMax + - flickerBrightness + - shadows + - separateSpecular + - fadeOutTime + - shadowDarkness + - unk_ds3_f2_15 + - unk_ds3_f2_16 + - unk_ds3_f2_17 + - unk_ds3_f2_18 + - unk_ds3_f2_19 + - unk_ds3_f2_20 + - unk_ds3_f2_21 + - unk_ds3_f2_22 + - unk_ds3_f2_23 + - unk_ds3_f2_24 + - volumeDensity + - unk_sdt_f2_25 + - phaseFunction + - asymmetryParam + - falloffExponent + - unk_er_f2_29 + - unk_er_f2_30 + - unk_er_f2_31 + - unk_er_f2_32 + properties1: DS3 + properties2: SDT + AC6: ER diff --git a/src/actions/701.yml b/src/actions/701.yml new file mode 100644 index 0000000..2edb20b --- /dev/null +++ b/src/actions/701.yml @@ -0,0 +1,13 @@ +type: 701 +name: Unk701 +desc: | + Unknown root node action that was introduced in Elden Ring. +properties: + unk_er_p1_0: + type: ScalarValue + default: 1 +games: + ER: + properties1: + - unk_er_p1_0 + AC6: ER diff --git a/src/actions/731.yml b/src/actions/731.yml new file mode 100644 index 0000000..3c872da --- /dev/null +++ b/src/actions/731.yml @@ -0,0 +1,31 @@ +type: 731 +name: NodeWindSpeed +desc: | + Controls how effective the wind is at pushing the node. +properties: + speed: + type: ScalarValue + argument: EffectAge + desc: | + The speed in the direction of the wind. + speedMultiplier: + type: ScalarValue + default: 1 + argument: EffectAge + desc: | + A multiplier for {@link speed the speed in the direction of the wind}. + enabled: + field: bool + default: true + desc: | + Controls whether the wind should have any effect at all or not. +games: + DS3: fallback + SDT: + fields1: + - enabled + properties1: + - speed + - speedMultiplier + ER: SDT + AC6: SDT diff --git a/src/actions/732.yml b/src/actions/732.yml new file mode 100644 index 0000000..7b74657 --- /dev/null +++ b/src/actions/732.yml @@ -0,0 +1,36 @@ +type: 732 +name: ParticleWindSpeed +desc: | + Controls how effective the wind is at pushing the particles emitted from the node. +properties: + speed: + type: ScalarValue + argument: EffectAge + desc: | + The speed in the direction of the wind. + speedMultiplier: + type: ScalarValue + default: 1 + argument: EffectAge + desc: | + A multiplier for {@link speed the speed in the direction of the wind}. + enabled: + field: bool + default: true + desc: | + Controls whether the wind should have any effect at all or not. + unk_sdt_f1_1: + field: int + desc: | + Unknown. 0 and 1 seems to be valid values, while all other values cause the wind to not affect the particles. +games: + DS3: fallback + SDT: + fields1: + - enabled + - unk_sdt_f1_1 + properties1: + - speed + - speedMultiplier + ER: SDT + AC6: SDT diff --git a/src/actions/733.yml b/src/actions/733.yml new file mode 100644 index 0000000..a384365 --- /dev/null +++ b/src/actions/733.yml @@ -0,0 +1,31 @@ +type: 733 +name: NodeWindAcceleration +desc: | + Controls how effective the wind is at accelerating the node. +properties: + acceleration: + type: ScalarValue + argument: EffectAge + desc: | + The acceleration in the direction of the wind. + accelerationMultiplier: + type: ScalarValue + default: 1 + argument: EffectAge + desc: | + A multiplier for {@link acceleration the acceleration in the direction of the wind}. + enabled: + field: bool + default: true + desc: | + Controls whether the wind should have any effect at all or not. +games: + DS3: fallback + SDT: + fields1: + - enabled + properties1: + - acceleration + - accelerationMultiplier + ER: SDT + AC6: SDT diff --git a/src/actions/734.yml b/src/actions/734.yml new file mode 100644 index 0000000..59b9e08 --- /dev/null +++ b/src/actions/734.yml @@ -0,0 +1,38 @@ +type: 734 +name: ParticleWindAcceleration +desc: | + Controls how effective the wind is at accelerating the particles emitted from the node. + + Acceleration requires slot 10 to have an action that enables acceleration of the particles. +properties: + acceleration: + type: ScalarValue + argument: EffectAge + desc: | + The acceleration in the direction of the wind. + accelerationMultiplier: + type: ScalarValue + default: 1 + argument: EffectAge + desc: | + A multiplier for {@link acceleration the acceleration in the direction of the wind}. + enabled: + field: bool + default: true + desc: | + Controls whether the wind should have any effect at all or not. + unk_sdt_f1_1: + field: int + desc: | + Unknown. 0 and 1 seems to be valid values, while all other values cause the wind to not affect the particles. +games: + DS3: fallback + SDT: + fields1: + - enabled + - unk_sdt_f1_1 + properties1: + - acceleration + - accelerationMultiplier + ER: SDT + AC6: SDT diff --git a/src/actions/75.yml b/src/actions/75.yml new file mode 100644 index 0000000..c2a27b3 --- /dev/null +++ b/src/actions/75.yml @@ -0,0 +1,32 @@ +type: 75 +name: PlaySound +desc: | + Plays a sound effect. +properties: + sound: + field: int + desc: | + The ID of the sound to play. + repeat: + field: bool + default: false + desc: | + Controls whether the sound will repeat or not. + + Does not seem to work in Elden Ring, and probably doesn't in Armored Core 6 either. + volume: + field: float + default: 1 + desc: | + Volume multiplier. + + Does not seem to work in Elden Ring, and probably doesn't in Armored Core 6 either. +games: + DS3: + fields1: + - sound + - volume + - repeat + SDT: DS3 + ER: DS3 + AC6: DS3 diff --git a/src/actions/800.yml b/src/actions/800.yml new file mode 100644 index 0000000..103b971 --- /dev/null +++ b/src/actions/800.yml @@ -0,0 +1,20 @@ +type: 800 +name: Unk800 +desc: | + Unknown action that was added in Armored Core 6 and can go into the same slot as the particle wind actions. +properties: + unk_ac6_f1_0: + field: float + default: 1 + unk_ac6_f1_1: + field: float + default: 0.2 + unk_ac6_f1_2: + field: float + default: 0.25 +games: + AC6: + fields1: + - unk_ac6_f1_0 + - unk_ac6_f1_1 + - unk_ac6_f1_2 diff --git a/src/fxr.ts b/src/fxr.ts new file mode 100644 index 0000000..c87eaa7 --- /dev/null +++ b/src/fxr.ts @@ -0,0 +1,22541 @@ +enum Game { + /** + * Does not represent any specific game. + * + * Using this with the {@link FXR.read} method will cause it to parse + * everything as generic classes. This means that none of the methods in the + * library that manipulate things that depend on the game version will work, + * like the {@link Node.scale} and {@link Node.recolor} methods. It also + * means that the library will not be able to convert it to work with other + * games. + * + * This is intended to be used only for research or for parsing modded files + * that may not be structured correctly. + * + * Note that this does not work with the {@link FXR.toArrayBuffer} method + * unless the FXR only contains generic classes. If it contains any node + * classes other than {@link GenericNode}, or any {@link DataAction}s, it + * must be given a specific game to write to. + */ + Generic = -1, + /** + * Dark Souls III + */ + DarkSouls3 = 0, + /** + * Sekiro: Shadows Die Twice + */ + Sekiro = 1, + /** + * Elden Ring + */ + EldenRing = 2, + /** + * Armored Core VI Fires of Rubicon + */ + ArmoredCore6 = 3 +} + +enum FXRVersion { + /** + * Used in Dark Souls 3. + */ + DarkSouls3 = 4, + /** + * Used in Sekiro, Elden Ring, and Armored Core 6. + */ + Sekiro = 5, +} + +const GameVersionMap = { + [Game.DarkSouls3]: FXRVersion.DarkSouls3, + [Game.Sekiro]: FXRVersion.Sekiro, + [Game.EldenRing]: FXRVersion.Sekiro, + [Game.ArmoredCore6]: FXRVersion.Sekiro, +} + +enum NodeType { + /** + * The root of the FXR tree structure. + * + * This node type has a specialized subclass: {@link RootNode} + */ + Root = 2000, + /** + * Acts as a node containing another SFX. + * + * This node type has a specialized subclass: {@link ProxyNode} + */ + Proxy = 2001, + /** + * A node that only displays one of its child nodes at a time based on + * distance thresholds for each. + * + * This node type has a specialized subclass: {@link LevelOfDetailNode} + */ + LevelOfDetail = 2002, + /** + * A basic node that can have transforms and child nodes, and emit particles. + * + * This node type has a specialized subclass: {@link BasicNode} + */ + Basic = 2200, + /** + * A node that overrides the emitter of its child nodes with its own, + * allowing a single emitter to emit multiple types of particles. + * + * This node type has a specialized subclass: {@link SharedEmitterNode} + */ + SharedEmitter = 2202, +} + +enum EffectType { + /** + * Manages the duration and thresholds for the + * {@link NodeType.LevelOfDetail level of detail node}. + * + * This effect type has a specialized subclass: {@link LevelOfDetailEffect} + */ + LevelOfDetail = 1002, + /** + * Effect used in {@link NodeType.Basic basic nodes} to apply transforms and + * emit particles of many different types. + * + * This effect type has a specialized subclass: {@link BasicEffect} + */ + Basic = 1004, + /** + * Effect used in {@link NodeType.SharedEmitter shared emitter nodes} to + * override emitters of child nodes and control which of the child nodes to use + * the particles of. + * + * This effect type has a specialized subclass: {@link SharedEmitterEffect} + */ + SharedEmitter = 1005, +} + +enum ActionType { + /** + * This action does nothing. It fits into most action slots and acts as a way + * to disable the effects of the other actions that go in those slots. + */ + None = 0, + /** + * Controls the movement of the node. + * + * This action type has a specialized subclass: {@link NodeMovement} + */ + NodeAcceleration = 1, + /** + * Controls the rotation speed of the node. + * + * This action type has a specialized subclass: {@link NodeMovement} + */ + NodeSpin = 34, + /** + * Sets the translation and rotation of the node. + * + * This action type has a specialized subclass: {@link NodeTransform} + */ + StaticNodeTransform = 35, + /** + * Sets and randomizes the translation and rotation of the node. + * + * This action type has a specialized subclass: {@link NodeTransform} + */ + RandomNodeTransform = 36, + /** + * Controls the movement of particles. + * + * This action type has a specialized subclass: {@link ParticleMovement} + */ + ParticleAcceleration = 55, + /** + * Controls the movement of particles. + * + * This action type has a specialized subclass: {@link ParticleMovement} + */ + ParticleSpeed = 60, + /** + * Controls the movement of particles. + * + * This action type has a specialized subclass: {@link ParticleMovement} + */ + ParticleSpeedRandomTurns = 64, + /** + * Controls the movement of particles. + * + * This action type has a specialized subclass: {@link ParticleMovement} + */ + ParticleSpeedPartialFollow = 65, + /** + * Controls the movement of the node. + * + * This action type has a specialized subclass: {@link NodeMovement} + */ + NodeAccelerationRandomTurns = 83, + /** + * Controls the movement of particles. + * + * This action type has a specialized subclass: {@link ParticleMovement} + */ + ParticleAccelerationRandomTurns = 84, + /** + * Controls the movement of particles. + * + * This action type has a specialized subclass: {@link ParticleMovement} + */ + ParticleAccelerationPartialFollow = 105, + /** + * Controls the movement of the node. + * + * This action type has a specialized subclass: {@link NodeMovement} + */ + NodeAccelerationPartialFollow = 106, + /** + * Controls the movement of the node. + * + * This action type has a specialized subclass: {@link NodeMovement} + */ + NodeAccelerationSpin = 113, + /** + * Controls the movement of the node. + * + * This action type has a specialized subclass: {@link NodeMovement} + */ + NodeSpeed = 120, + /** + * Controls the movement of the node. + * + * This action type has a specialized subclass: {@link NodeMovement} + */ + NodeSpeedRandomTurns = 121, + /** + * Controls the movement of the node. + * + * This action type has a specialized subclass: {@link NodeMovement} + */ + NodeSpeedPartialFollow = 122, + /** + * Controls the movement of the node. + * + * This action type has a specialized subclass: {@link NodeMovement} + */ + NodeSpeedSpin = 123, + /** + * Maps states to effects in the parent node. + * + * This action type has a specialized subclass: {@link StateEffectMap} + */ + StateEffectMap = 199, + /** + * Used in {@link EffectType.SharedEmitter} to emit all particles from child + * nodes every time the shared emitter emits something. + * + * This action type has a specialized subclass: {@link EmitAllParticles} + */ + EmitAllParticles = 200, + /** + * Used in {@link EffectType.SharedEmitter} to emit a particle from a random + * child node every time the shared emitter emits something. + * + * This action type has a specialized subclass: {@link EmitRandomParticles} + */ + EmitRandomParticles = 201, + /** + * Emits one particle once. + * + * This action type has a specialized subclass: {@link OneTimeEmitter} + */ + OneTimeEmitter = 399, + /** + * Makes all particles use the default initial direction from the emitter. + * See {@link InitialDirection} for more information. + * + * This action type has a specialized subclass: {@link NoParticleSpread} + */ + NoParticleSpread = 500, + Unk700 = 700, // Root node action + Unk702 = 702, // Root node action + Unk10000_StandardParticle = 10000, + Unk10001_StandardCorrectParticle = 10001, + Unk10002_Fluid = 10002, + Unk10003_LightShaft = 10003, + Unk10008_SparkParticle = 10008, + Unk10009_SparkCorrectParticle = 10009, + Unk10010_Tracer = 10010, + Unk10014_LensFlare = 10014, + Unk10100 = 10100, // Root node action + Unk10200_ForceFieldCancelArea = 10200, + Unk10300_ForceFieldWindArea = 10300, + Unk10301_ForceFieldGravityArea = 10301, + Unk10302_CollisionFieldArea = 10302, + Unk10303_ForceFieldTurbulenceArea = 10303, + Unk10400 = 10400, // Root node action + + // Data Actions + /*#ActionType start*/ + /** + * Translates the node using a property, meaning it can be animated. This can be useful if you need the node to follow a specific path. + * + * This action type has a specialized subclass: {@link NodeTranslation} + */ + NodeTranslation = 15, + /** + * Attaches the node to the camera. + * + * This action type has a specialized subclass: {@link NodeAttachToCamera} + */ + NodeAttachToCamera = 46, + /** + * Plays a sound effect. + * + * This action type has a specialized subclass: {@link PlaySound} + */ + PlaySound = 75, + /** + * Controls various things about the node, like its duration, and how it is attached to the parent node. + * + * This action type has a specialized subclass: {@link NodeAttributes} + */ + NodeAttributes = 128, + /** + * Controls the duration of particles emitted by the node, and how the particles are attached to the node. + * + * This action type has a specialized subclass: {@link ParticleAttributes} + */ + ParticleAttributes = 129, + /** + * Unknown action that is in every basic effect in every game, and still literally nothing is known about it. + * + * This action type has a specialized subclass: {@link Unk130} + */ + Unk130 = 130, + /** + * Modifies particles in various ways. + * + * Note: This is **not** a {@link Modifier property modifier}, it is an action that modifies particles emitted from the same node. + * + * This action type has a specialized subclass: {@link ParticleModifier} + */ + ParticleModifier = 131, + /** + * References another SFX by its ID. + * + * This action type has a specialized subclass: {@link SFXReference} + */ + SFXReference = 132, + /** + * Used in the {@link EffectType.LevelOfDetail level of detail effect} to manage the duration and thresholds for the {@link NodeType.LevelOfDetail level of detail node}. + * + * This action type has a specialized subclass: {@link LevelOfDetailThresholds} + */ + LevelOfDetailThresholds = 133, + /** + * Emits particles periodically. + * + * This action type has a specialized subclass: {@link PeriodicEmitter} + */ + PeriodicEmitter = 300, + /** + * Emits particles once it has moved a certain distance from where it last emitted particles. + * + * This action type has a specialized subclass: {@link EqualDistanceEmitter} + */ + EqualDistanceEmitter = 301, + /** + * Makes the emitter a single point. + * + * This action type has a specialized subclass: {@link PointEmitterShape} + */ + PointEmitterShape = 400, + /** + * Makes the emitter disk-shaped. + * + * This action type has a specialized subclass: {@link DiskEmitterShape} + */ + DiskEmitterShape = 401, + /** + * Makes the emitter rectangular. + * + * This action type has a specialized subclass: {@link RectangleEmitterShape} + */ + RectangleEmitterShape = 402, + /** + * Makes the emitter spherical. + * + * This action type has a specialized subclass: {@link SphereEmitterShape} + */ + SphereEmitterShape = 403, + /** + * Makes the emitter cuboidal. + * + * This action type has a specialized subclass: {@link BoxEmitterShape} + */ + BoxEmitterShape = 404, + /** + * Makes the emitter cylindrical. + * + * This action type has a specialized subclass: {@link CylinderEmitterShape} + */ + CylinderEmitterShape = 405, + /** + * Gives each particle a random initial direction offset within a circular cone. See {@link InitialDirection} for more information. + * + * This action type has a specialized subclass: {@link CircularParticleSpread} + */ + CircularParticleSpread = 501, + /** + * Gives each particle a random initial direction offset within an elliptical cone. See {@link InitialDirection} for more information. + * + * This action type has a specialized subclass: {@link EllipticalParticleSpread} + */ + EllipticalParticleSpread = 502, + /** + * Gives each particle a random initial direction offset within a rectangular cone. See {@link InitialDirection} for more information. + * + * This action type has a specialized subclass: {@link RectangularParticleSpread} + */ + RectangularParticleSpread = 503, + /** + * Very basic point sprite particle. Similar to {@link ActionType.BillboardEx BillboardEx}, but far simpler. + * + * This action type has a specialized subclass: {@link PointSprite} + */ + PointSprite = 600, + /** + * Simple line particle. It automatically rotates to match the direction it's moving. + * + * This action type has a specialized subclass: {@link Line} + */ + Line = 601, + /** + * Simple rectangular particle, very similar to {@link ActionType.Line Line particles}, but has properties that control the width as well as the length. It automatically rotates to match the direction it's moving. + * + * This action type has a specialized subclass: {@link QuadLine} + */ + QuadLine = 602, + /** + * Particle with a texture that may be animated. This is the most common particle type and it has a lot of useful fields and properties. + * + * This action type has a specialized subclass: {@link BillboardEx} + */ + BillboardEx = 603, + /** + * Particle with multiple textures that can scroll. + * + * This action type has a specialized subclass: {@link MultiTextureBillboardEx} + */ + MultiTextureBillboardEx = 604, + /** + * Particle with a 3D model. + * + * This action type has a specialized subclass: {@link Model} + */ + Model = 605, + /** + * Creates a trail behind moving effects. + * + * This action type has a specialized subclass: {@link Tracer} + */ + Tracer = 606, + /** + * A particle that distorts anything seen through it. + * + * Note: This particle is not visible if the "Effects" setting is set to "Low". + * + * This action type has a specialized subclass: {@link Distortion} + */ + Distortion = 607, + /** + * A particle that applies a radial blur to anything seen through it. + * + * Note: This particle is not visible if the "Effects" setting is set to "Low". + * + * This action type has a specialized subclass: {@link RadialBlur} + */ + RadialBlur = 608, + /** + * Point light source. + * + * This action type has a specialized subclass: {@link PointLight} + */ + PointLight = 609, + /** + * Unknown root node action that was introduced in Elden Ring. + * + * This action type has a specialized subclass: {@link Unk701} + */ + Unk701 = 701, + /** + * Controls how effective the wind is at pushing the node. + * + * This action type has a specialized subclass: {@link NodeWindSpeed} + */ + NodeWindSpeed = 731, + /** + * Controls how effective the wind is at pushing the particles emitted from the node. + * + * This action type has a specialized subclass: {@link ParticleWindSpeed} + */ + ParticleWindSpeed = 732, + /** + * Controls how effective the wind is at accelerating the node. + * + * This action type has a specialized subclass: {@link NodeWindAcceleration} + */ + NodeWindAcceleration = 733, + /** + * Controls how effective the wind is at accelerating the particles emitted from the node. + * + * Acceleration requires slot 10 to have an action that enables acceleration of the particles. + * + * This action type has a specialized subclass: {@link ParticleWindAcceleration} + */ + ParticleWindAcceleration = 734, + /** + * Unknown action that was added in Armored Core 6 and can go into the same slot as the particle wind actions. + * + * This action type has a specialized subclass: {@link Unk800} + */ + Unk800 = 800, + /** + * Creates a trail behind moving effects. + * + * This is slightly different from {@link Tracer}, as the trail from this is less visible when it's moving slower. + * + * This action type has a specialized subclass: {@link DynamicTracer} + */ + DynamicTracer = 10012, + /** + * Simulates an interaction with water, allowing effects to create ripples in nearby water. The interaction basically pushes water in a shape controlled by a texture down to a given depth and holds it there for a duration before releasing it. + * + * This action type has a specialized subclass: {@link WaterInteraction} + */ + WaterInteraction = 10013, + /** + * Particle with a 3D model. Similar to {@link Model}, but with some different options and seemingly no way to change the blend mode. + * + * This action type has a specialized subclass: {@link RichModel} + */ + RichModel = 10015, + /** + * Unknown root node action. + * + * This action type has a specialized subclass: {@link Unk10500} + */ + Unk10500 = 10500, + /** + * Light source with an elliptic cone shape, a spot light. + * + * This action type has a specialized subclass: {@link SpotLight} + */ + SpotLight = 11000, + /*#ActionType end*/ +} + +enum ValueType { + Scalar = 0, + Vector2 = 1, + Vector3 = 2, + Vector4 = 3 +} + +enum PropertyFunction { + /** + * Always returns 0 for each component. + */ + Zero = 0, + /** + * Always returns 1 for each component. + */ + One = 1, + /** + * Always returns the value in the property's fields. + * + * This property function has a specialized subclass: + * {@link ConstantProperty} + */ + Constant = 2, + /** + * Uses step interpolation to interpolate the property's values. + * + * This property function has a specialized subclass: {@link SteppedProperty} + */ + Stepped = 3, + /** + * Uses linear interpolation to interpolate the property's values. + * + * This property function has a specialized subclass: {@link LinearProperty} + */ + Linear = 4, + /** + * Uses a custom curve to interpolate the property's values. + * + * The difference between this and {@link Curve2} is currently unknown. + */ + Curve1 = 5, + /** + * Uses a custom curve to interpolate the property's values. + * + * The difference between this and {@link Curve1} is currently unknown. + * + * This property function has a specialized subclass: {@link Curve2Property} + */ + Curve2 = 6, + /** + * Same as {@link Curve1} or {@link Curve2} (not sure which), but allows each + * component of the value to have a different number of stops. + * + * Only available in Armored Core 6. + */ + CompCurve = 7 +} + +export type ValuePropertyFunction = + PropertyFunction.Zero | + PropertyFunction.One | + PropertyFunction.Constant + +export type SequencePropertyFunction = + PropertyFunction.Stepped | + PropertyFunction.Linear | + PropertyFunction.Curve1 | + PropertyFunction.Curve2 + +export type ComponentSequencePropertyFunction = PropertyFunction.CompCurve + +export type ValueTypeMap = { + [ValueType.Scalar]: number + [ValueType.Vector2]: Vector2 + [ValueType.Vector3]: Vector3 + [ValueType.Vector4]: Vector4 +} + +export interface IKeyframe { + position: number + value: ValueTypeMap[T] + unkTangent1?: ValueTypeMap[T] + unkTangent2?: ValueTypeMap[T] +} + +export interface IProperty { + valueType: T + function: F + componentCount: number + fieldCount: number + fields: NumericalField[] + toJSON(): any + scale(factor: number): this + power(exponent: number): this + add(summand: number): this + valueAt(arg: number): ValueTypeMap[T] + clone(): IProperty + separateComponents(): IProperty[] +} + +export interface IValueProperty extends IProperty { + value: ValueTypeMap[T] + clone(): IValueProperty +} + +export interface ISequenceProperty extends IProperty { + loop: boolean + keyframes: IKeyframe[] + sortKeyframes(): void + clone(): ISequenceProperty + duration: number +} + +export interface IModifiableProperty extends IProperty { + modifiers: Modifier[] +} + +export interface IAction { + readonly type: ActionType + toJSON(): any + minify(): typeof this +} + +export interface IEffect { + readonly type: EffectType + getActionCount(game: Game): number + getActions(game: Game): IAction[] + toJSON(): any + minify(): typeof this + walkActions(): Generator +} + +export type Vector2 = [x: number, y: number] +export type Vector3 = [x: number, y: number, z: number] +export type Vector4 = [red: number, green: number, blue: number, alpha: number] +export type Vector = Vector2 | Vector3 | Vector4 +export type PropertyValue = number | Vector +export type AnyProperty = Property +export type ScalarProperty = Property +export type Vector2Property = Property +export type Vector3Property = Property +export type Vector4Property = Property +export type VectorProperty = Vector2Property | Vector3Property | Vector4Property +export type AnyValue = AnyProperty | PropertyValue +export type ScalarValue = number | ScalarProperty +export type Vector2Value = Vector2 | Vector2Property +export type Vector3Value = Vector3 | Vector3Property +export type Vector4Value = Vector4 | Vector4Property +export type VectorValue = Vector | VectorProperty + +enum ModifierType { + /** + * Adds a random value between `-x` and `x` to the property's value, where + * `x` is the "max change" value set in the modifier's fields. + * + * There is one RNG seed field for each component of the property value + * followed by one "max change" value per component. + */ + Randomizer1 = 21, + /** + * Adds a random value between `x` and `y` to the property's value, where + * `x` and `y` are the min/max change values set in the modifier's fields. + * + * There is one RNG seed field for each component of the property value + * followed by one "min change" value per component, and then one "max + * change" value per component. + */ + Randomizer2 = 24, + /** + * Multiplies the property's value based on some external value. The only + * field in the modifier controls which external value to check. For example, + * if the field is set to 10000, the external value will be based on what the + * "Display Blood" option in the in-game settings is set to. + * + * The factor is controlled by the modifier's only property. The external + * value is given as the property function's argument, so a linear property + * can be used to change the factor based on the external value. + * + * This modifier type has two specialized subclasses: + * - {@link ExternalValueModifier} + * - {@link BloodVisibilityModifier} + */ + ExternalValue1 = 38, + /** + * Same as {@link ExternalValue1}, except this only updates if the effect is + * recreated instead of updating instantly when the external value changes. + * + * Note: This type seems to only work with the + * {@link ExternalValue.DisplayBlood DisplayBlood external value}. + */ + ExternalValue2 = 39, + /** + * Adds a random fraction of the property's value to itself. The range of the + * fraction is controlled by the the latter half of the modifier's fields, + * where, if `x` is the value of the field, the possible range of the + * fraction will be `-x` to `x`. + * + * There is one RNG seed field for each component of the property value + * followed by one "max change" value per component. + */ + Randomizer3 = 53, +} + +enum FieldType { + Boolean, + Integer, + Float, +} + +enum BlendMode { + Unk0 = 0, + Source = 1, + Normal = 2, + Multiply = 3, + Add = 4, + Subtract = 5, + Unk6 = 6, + Screen = 7, +} + +enum ExternalValue { + /** + * This value will be set to 1 when the effect is meant to end due to the + * source of the effect going away, for example when a fire pot explodes and + * disappears. The value is otherwise 0. + */ + Terminate = 0, + /** + * In Elden Ring, this value is 1 if it's raining or snowing, and 0 otherwise. + */ + Precipitation = 1, + /** + * In Elden Ring, this represents the the time of day. At midnight, the value + * is 0, at noon it is 12, and then it goes up to 24 before wrapping back to + * 0, just like the hours on the clock. + */ + TimeOfDay = 2, + /** + * Used in AC6. + */ + Unk3 = 3, + /** + * This is based on the distance between the SFX and the camera. + * + * The range is 0-1, the distance is converted in some unknown way. + * + * It does not always work for all sources of effects. This is used by the + * beacon effect in Elden Ring, so it definitely works there. + */ + SFXDistance = 1000, + /** + * This value is set through the Special Attribute param field on weapons. + */ + HitEffectVariation = 2000, + Unk2100 = 2100, // Blood related? + Unk2200 = 2200, // Blood related? + /** + * Based on the "Display Blood" setting. + * - Off: `-1` + * - On: `0` + * - Mild: `1` + * + * This external value has a specialized modifier subclass: + * {@link BloodVisibilityModifier} + */ + DisplayBlood = 10000, + Unk20000 = 20000, + /** + * Used in AC6. + */ + Unk40000 = 40000, + /** + * Used in AC6. + */ + Unk70000 = 70000, + /** + * Used in AC6. + */ + Unk70010 = 70010, + /** + * Used in AC6. + */ + Unk70020 = 70020, + /** + * Used in AC6. + */ + Unk70200 = 70200, +} + +enum Operator { + NotEqual = 0, + Equal = 1, + GreaterThanOrEqual = 2, + GreaterThan = 3, + + /* + These two are not part of the format. The StateCondition class will just + switch the operands around and use the greater than operators automatically + when these are used. + */ + LessThanOrEqual = 4, + LessThan = 5, +} + +enum OperandType { + /** + * The field's value, literally. + */ + Literal = -4, + /** + * Gets an external value. + * + * The field refers to an {@link ExternalValue}. + */ + External = -3, + /** + * Based on movement in some way? + * + * Does not require a field. + */ + UnkMinus2 = -2, + /** + * The time since the effect changed state in seconds. + * + * Does not require a field. + */ + StateTime = -1, +} + +enum AttachMode { + /** + * Completely detached. + */ + None = 0, + /** + * Translates and rotates with the parent. + */ + Parent = 1, + /** + * Translates and rotates with the attachment point (dummypoly). Parent transformations are ignored. + */ + DummyPoly = 2, + /** + * Only translates with the parent. Rotations are entirely ignored. + */ + ParentTranslation = 3, + /** + * Only translates with the attachment point (dummypoly). Rotations are entirely ignored. + */ + DummyPolyTranslation = 4 +} + +enum PropertyArgument { + /** + * A constant value of 0. + */ + Constant0, + /** + * Time in seconds since the instance (particle or other visual part) was emitted. + */ + InstanceAge, + /** + * Time in seconds since the {@link IEffect Effect} became active. + * + * An effect becoming active is for example the delay from + * {@link ActionType.NodeAttributes NodeAttributes} being over, or the active + * {@link State} changing, making a node change which of its effects is + * active. + */ + EffectAge, + /** + * Time in seconds between the effect being created and the particle being + * emitted. Stays constant per particle. + */ + EmissionTime +} + +enum OrientationMode { + /** + * Faces global south. + * + * See also: + * - {@link UnkSouth} + */ + South = 0, + /** + * Faces the camera plane. + * + * See also: + * - {@link Camera} + */ + CameraPlane = 1, + /** + * Faces the -Z direction of the parent node. + */ + LocalSouth = 2, + /** + * Faces global south. + * + * Similar to {@link South}, but this seems to change the projection of the + * particle in some way. + */ + UnkSouth = 3, + /** + * Tries to face the camera, but is limited to rotation around the vertical + * axis. + */ + GlobalYaw = 4, + /** + * Faces global east. + */ + East = 5, + /** + * Faces the camera. + * + * This is different from {@link CameraPlane}, as this makes it face the + * camera's position instead of the camera plane. + */ + Camera = 6, + /** + * Tries to face the camera, but is limited to rotation around the Y axis of + * the parent node. + */ + LocalYaw = 7, +} + +enum TracerOrientationMode { + /** + * The tracer source is perpendicular to the direction it's travelling and + * the direction of the camera. + */ + Travel = 0, + /** + * The tracer source is aligned with the local Z-axis, which is detenmined + * by the rotation of the node that emits the tracer. + */ + LocalZ = 1, + /** + * The tracer source is aligned with the global vertical axis. + */ + Vertical = 2, + /** + * The tracer source is aligned with the global X-axis. + */ + GlobalX = 3, + /** + * Creates two sources for the tracer with different orientation modes. One + * has {@link Vertical} and the other has {@link GlobalX}, forming a cross. + */ + Cross = 4, + /** + * The tracer source is parallel to the global diagonal (1, 1, 1). + */ + Diagonal = 5, +} + +enum LightingMode { + /** + * Same as {@link Lit}, but this seems to sometimes have an extra light + * source from somewhere? + */ + UnkMinus2 = -2, + /** + * Lighting does not affect the particles. No shadows or specular + * hightlights. + */ + Unlit = -1, + /** + * Lighting affects the particles just like most regular objects. + */ + Lit = 0, +} + +/** + * Used by {@link ActionType.Distortion distortion} particles to control what + * type of distortion to apply. + */ +enum DistortionMode { + /** + * Distorts the background as if you stuck something into it and stirred it. + * It is animated, and the stir speed is controlled by a property. + */ + Stir = 0, + /** + * Distorts the background based on the normal map. + */ + NormalMap = 1, + /** + * Distorts the background as if the edges were held in place and you grabbed + * the center and twisted it. + */ + Twist = 2, + /** + * Seemingly identical to {@link NormalMap}? + */ + Unk3 = 3, + /** + * This seems to just squeeze everything to the bottom left corner? + */ + Unk4 = 4, +} + +/** + * Possible shapes for {@link ActionType.Distortion distortion} particles. + */ +enum DistortionShape { + /** + * A flat rectangle. + */ + Rectangle = 0, + /** + * Half of an ellipsoid. (Like a hemisphere, but with three different radii.) + */ + Hemiellipsoid = 1, + /** + * An ellipsoid. (Like a sphere, but with three different radii.) + */ + Ellipsoid = 2, +} + +/** + * A particle's initial direction is used for various things that require a + * direction, but does not have a set one to follow. + * - {@link ActionType.ParticleModifier ParticleModifier action}'s + * {@link ParticleModifierParams.speed speed}. + * - {@link ActionType.Line Line action}'s initial rotation. + * - {@link ActionType.QuadLine QuadLine action}'s initial rotation. + * + * The initial direction can be further modified by the following actions: + * - {@link ActionType.NoParticleSpread NoParticleSpread} + * - {@link ActionType.CircularParticleSpread CircularParticleSpread} + * - {@link ActionType.EllipticalParticleSpread EllipticalParticleSpread} + * - {@link ActionType.RectangularParticleSpread RectangularParticleSpread} + */ +enum InitialDirection { + /** + * The direction will depend on the emitter shape. + * | Emitter Shape | Direction | + * |:-|:-| + * | {@link ActionType.PointEmitterShape Point} | Same as {@link LocalNorth}. | + * | {@link ActionType.DiskEmitterShape Disk} | Same as {@link LocalNorth}. | + * | {@link ActionType.RectangleEmitterShape Rectangle} | Same as {@link LocalNorth}. | + * | {@link ActionType.SphereEmitterShape Sphere} | The direction cannot be changed for this emitter shape. | + * | {@link ActionType.BoxEmitterShape Box} | If {@link BoxEmitterShape.emitInside emitInside} is true, it picks a direction parallel to a random local axis. If it is false, the direction will be out from the box, perpendicular to the side where the particle was emitted. | + * | {@link ActionType.CylinderEmitterShape Cylinder} | Out from the cylinder's axis. | + */ + Emitter = 0, + /** + * Global up. (+Y) + */ + Up = 1, + /** + * Global down. (-Y) + */ + Down = 2, + /** + * Global north. (+Z) + */ + North = 3, + /** + * Local up. (+Y) + */ + LocalUp = 4, + /** + * Local down. (-Y) + */ + LocalDown = 5, + /** + * Local north. (+Z) + */ + LocalNorth = 6, +} + +export type ActionGameDataEntry = { + fields1?: string[] | Game + fields2?: string[] | Game + properties1?: string[] | Game + properties2?: string[] | Game +} +const ActionData: { + [x: string]: { + props: { + [name: string]: { + default: any + field?: FieldType + paths: { + [game: string]: [string, number] + } + } + } + games: { + [game: string]: ActionGameDataEntry | Game | -2 + } + } +} = { + /*#ActionData start*/ + [ActionType.NodeTranslation]: { + props: { + translation: { default: [0, 0, 0], paths: {} }, + unk_er_f1_0: { default: 0, paths: {}, field: FieldType.Integer }, + }, + games: { + [Game.DarkSouls3]: { + properties1: ['translation'] + }, + [Game.Sekiro]: Game.DarkSouls3, + [Game.EldenRing]: { + fields1: ['unk_er_f1_0'], + properties1: Game.DarkSouls3 + }, + [Game.ArmoredCore6]: Game.EldenRing + } + }, + [ActionType.NodeAttachToCamera]: { + props: { + followRotation: { default: true, paths: {}, field: FieldType.Boolean }, + unk_ds3_f1_1: { default: 1, paths: {}, field: FieldType.Integer }, + }, + games: { + [Game.DarkSouls3]: { + fields1: ['followRotation','unk_ds3_f1_1'] + }, + [Game.Sekiro]: Game.DarkSouls3, + [Game.EldenRing]: Game.DarkSouls3, + [Game.ArmoredCore6]: Game.DarkSouls3 + } + }, + [ActionType.PlaySound]: { + props: { + sound: { default: 0, paths: {}, field: FieldType.Integer }, + repeat: { default: false, paths: {}, field: FieldType.Boolean }, + volume: { default: 1, paths: {}, field: FieldType.Float }, + }, + games: { + [Game.DarkSouls3]: { + fields1: ['sound','volume','repeat'] + }, + [Game.Sekiro]: Game.DarkSouls3, + [Game.EldenRing]: Game.DarkSouls3, + [Game.ArmoredCore6]: Game.DarkSouls3 + } + }, + [ActionType.NodeAttributes]: { + props: { + attachment: { default: AttachMode.Parent, paths: {}, field: FieldType.Integer }, + duration: { default: -1, paths: {} }, + delay: { default: 0, paths: {}, field: FieldType.Float }, + unk_ds3_f1_1: { default: 1, paths: {}, field: FieldType.Integer }, + unk_ds3_f1_3: { default: 0, paths: {}, field: FieldType.Float }, + }, + games: { + [Game.DarkSouls3]: { + fields1: ['delay','unk_ds3_f1_1','attachment','unk_ds3_f1_3'], + properties1: ['duration'] + }, + [Game.Sekiro]: Game.DarkSouls3, + [Game.EldenRing]: Game.DarkSouls3, + [Game.ArmoredCore6]: Game.DarkSouls3 + } + }, + [ActionType.ParticleAttributes]: { + props: { + attachment: { default: AttachMode.Parent, paths: {}, field: FieldType.Integer }, + duration: { default: -1, paths: {} }, + }, + games: { + [Game.DarkSouls3]: { + fields1: ['attachment'], + properties1: ['duration'] + }, + [Game.Sekiro]: Game.DarkSouls3, + [Game.EldenRing]: Game.DarkSouls3, + [Game.ArmoredCore6]: Game.DarkSouls3 + } + }, + [ActionType.Unk130]: { + props: { + unk_ds3_f1_0: { default: 1, paths: {}, field: FieldType.Integer }, + unk_ds3_f1_1: { default: 0, paths: {}, field: FieldType.Integer }, + unk_ds3_f1_2: { default: 0, paths: {}, field: FieldType.Integer }, + unk_ds3_f1_3: { default: 0, paths: {}, field: FieldType.Integer }, + unk_ds3_f1_4: { default: 0, paths: {}, field: FieldType.Integer }, + unk_ds3_f1_5: { default: 0, paths: {}, field: FieldType.Integer }, + unk_ds3_f1_6: { default: 0, paths: {}, field: FieldType.Integer }, + unk_ds3_f1_7: { default: 0, paths: {}, field: FieldType.Integer }, + unk_ds3_f1_8: { default: 0, paths: {}, field: FieldType.Integer }, + unk_ds3_p1_0: { default: 0, paths: {} }, + unk_ds3_p1_1: { default: 0, paths: {} }, + unk_ds3_p1_2: { default: 0, paths: {} }, + unk_ds3_p1_3: { default: 0, paths: {} }, + unk_ds3_p1_4: { default: 0, paths: {} }, + unk_ds3_p1_5: { default: 0, paths: {} }, + unk_ds3_p1_6: { default: 0, paths: {} }, + unk_ds3_p1_7: { default: 0, paths: {} }, + }, + games: { + [Game.DarkSouls3]: { + fields1: ['unk_ds3_f1_0','unk_ds3_f1_1','unk_ds3_f1_2','unk_ds3_f1_3','unk_ds3_f1_4','unk_ds3_f1_5','unk_ds3_f1_6','unk_ds3_f1_7','unk_ds3_f1_8'], + properties1: ['unk_ds3_p1_0','unk_ds3_p1_1','unk_ds3_p1_2','unk_ds3_p1_3','unk_ds3_p1_4','unk_ds3_p1_5','unk_ds3_p1_6','unk_ds3_p1_7'] + }, + [Game.Sekiro]: Game.DarkSouls3, + [Game.EldenRing]: Game.DarkSouls3, + [Game.ArmoredCore6]: Game.DarkSouls3 + } + }, + [ActionType.ParticleModifier]: { + props: { + uniformScale: { default: false, paths: {}, field: FieldType.Boolean }, + speed: { default: 0, paths: {} }, + scaleX: { default: 1, paths: {} }, + scaleY: { default: 1, paths: {} }, + scaleZ: { default: 1, paths: {} }, + color: { default: 1, paths: {} }, + }, + games: { + [Game.DarkSouls3]: { + fields1: ['uniformScale'], + properties1: ['speed','scaleX','scaleY','scaleZ','color'] + }, + [Game.Sekiro]: Game.DarkSouls3, + [Game.EldenRing]: Game.DarkSouls3, + [Game.ArmoredCore6]: Game.DarkSouls3 + } + }, + [ActionType.SFXReference]: { + props: { + sfx: { default: 0, paths: {}, field: FieldType.Integer }, + }, + games: { + [Game.DarkSouls3]: { + fields1: ['sfx'] + }, + [Game.Sekiro]: Game.DarkSouls3, + [Game.EldenRing]: Game.DarkSouls3, + [Game.ArmoredCore6]: Game.DarkSouls3 + } + }, + [ActionType.LevelOfDetailThresholds]: { + props: { + duration: { default: -1, paths: {} }, + threshold0: { default: 1000, paths: {}, field: FieldType.Integer }, + threshold1: { default: 1000, paths: {}, field: FieldType.Integer }, + threshold2: { default: 1000, paths: {}, field: FieldType.Integer }, + threshold3: { default: 1000, paths: {}, field: FieldType.Integer }, + threshold4: { default: 1000, paths: {}, field: FieldType.Integer }, + }, + games: { + [Game.DarkSouls3]: { + fields1: ['threshold0','threshold1','threshold2','threshold3','threshold4'], + properties1: ['duration'] + }, + [Game.Sekiro]: Game.DarkSouls3, + [Game.EldenRing]: Game.DarkSouls3, + [Game.ArmoredCore6]: Game.DarkSouls3 + } + }, + [ActionType.PeriodicEmitter]: { + props: { + interval: { default: 1, paths: {} }, + perInterval: { default: 1, paths: {} }, + totalIntervals: { default: -1, paths: {} }, + maxConcurrent: { default: -1, paths: {}, field: FieldType.Integer }, + unk_ds3_f1_1: { default: 1, paths: {}, field: FieldType.Integer }, + }, + games: { + [Game.DarkSouls3]: { + fields1: ['maxConcurrent','unk_ds3_f1_1'], + properties1: ['interval','perInterval','totalIntervals'] + }, + [Game.Sekiro]: { + fields1: ['unk_ds3_f1_1'], + properties1: ['interval','perInterval','totalIntervals','maxConcurrent'] + }, + [Game.EldenRing]: Game.Sekiro, + [Game.ArmoredCore6]: Game.Sekiro + } + }, + [ActionType.EqualDistanceEmitter]: { + props: { + threshold: { default: 0.1, paths: {} }, + maxConcurrent: { default: -1, paths: {} }, + unk_ds3_f1_0: { default: 1, paths: {}, field: FieldType.Integer }, + unk_ds3_f1_1: { default: 0, paths: {}, field: FieldType.Integer }, + unk_ds3_p1_1: { default: -1, paths: {} }, + }, + games: { + [Game.DarkSouls3]: { + fields1: ['unk_ds3_f1_0','unk_ds3_f1_1'], + properties1: ['threshold','unk_ds3_p1_1','maxConcurrent'] + }, + [Game.Sekiro]: Game.DarkSouls3, + [Game.EldenRing]: Game.DarkSouls3, + [Game.ArmoredCore6]: Game.DarkSouls3 + } + }, + [ActionType.PointEmitterShape]: { + props: { + direction: { default: InitialDirection.Emitter, paths: {}, field: FieldType.Integer }, + }, + games: { + [Game.DarkSouls3]: { + fields1: ['direction'] + }, + [Game.Sekiro]: Game.DarkSouls3, + [Game.EldenRing]: Game.DarkSouls3, + [Game.ArmoredCore6]: Game.DarkSouls3 + } + }, + [ActionType.DiskEmitterShape]: { + props: { + direction: { default: InitialDirection.Emitter, paths: {}, field: FieldType.Integer }, + radius: { default: 1, paths: {} }, + distribution: { default: 0, paths: {} }, + }, + games: { + [Game.DarkSouls3]: { + fields1: ['direction'], + properties1: ['radius','distribution'] + }, + [Game.Sekiro]: Game.DarkSouls3, + [Game.EldenRing]: Game.DarkSouls3, + [Game.ArmoredCore6]: Game.DarkSouls3 + } + }, + [ActionType.RectangleEmitterShape]: { + props: { + direction: { default: InitialDirection.Emitter, paths: {}, field: FieldType.Integer }, + sizeX: { default: 1, paths: {} }, + sizeY: { default: 1, paths: {} }, + distribution: { default: 0, paths: {} }, + }, + games: { + [Game.DarkSouls3]: { + fields1: ['direction'], + properties1: ['sizeX','sizeY','distribution'] + }, + [Game.Sekiro]: Game.DarkSouls3, + [Game.EldenRing]: Game.DarkSouls3, + [Game.ArmoredCore6]: Game.DarkSouls3 + } + }, + [ActionType.SphereEmitterShape]: { + props: { + emitInside: { default: true, paths: {}, field: FieldType.Boolean }, + radius: { default: 1, paths: {} }, + }, + games: { + [Game.DarkSouls3]: { + fields1: ['emitInside'], + properties1: ['radius'] + }, + [Game.Sekiro]: Game.DarkSouls3, + [Game.EldenRing]: Game.DarkSouls3, + [Game.ArmoredCore6]: Game.DarkSouls3 + } + }, + [ActionType.BoxEmitterShape]: { + props: { + direction: { default: InitialDirection.Emitter, paths: {}, field: FieldType.Integer }, + emitInside: { default: true, paths: {}, field: FieldType.Boolean }, + sizeX: { default: 1, paths: {} }, + sizeY: { default: 1, paths: {} }, + sizeZ: { default: 1, paths: {} }, + }, + games: { + [Game.DarkSouls3]: { + fields1: ['direction','emitInside'], + properties1: ['sizeX','sizeY','sizeZ'] + }, + [Game.Sekiro]: Game.DarkSouls3, + [Game.EldenRing]: Game.DarkSouls3, + [Game.ArmoredCore6]: Game.DarkSouls3 + } + }, + [ActionType.CylinderEmitterShape]: { + props: { + direction: { default: InitialDirection.Emitter, paths: {}, field: FieldType.Integer }, + emitInside: { default: true, paths: {}, field: FieldType.Boolean }, + yAxis: { default: true, paths: {}, field: FieldType.Boolean }, + radius: { default: 1, paths: {} }, + height: { default: 1, paths: {} }, + }, + games: { + [Game.Sekiro]: { + fields1: ['direction','emitInside','yAxis'], + properties1: ['radius','height'] + }, + [Game.EldenRing]: Game.Sekiro, + [Game.ArmoredCore6]: Game.Sekiro + } + }, + [ActionType.CircularParticleSpread]: { + props: { + unk_er_f1_0: { default: false, paths: {}, field: FieldType.Boolean }, + angle: { default: 30, paths: {} }, + distribution: { default: 0, paths: {} }, + }, + games: { + [Game.DarkSouls3]: { + properties1: ['angle','distribution'] + }, + [Game.Sekiro]: Game.DarkSouls3, + [Game.EldenRing]: { + fields1: ['unk_er_f1_0'], + properties1: Game.DarkSouls3 + }, + [Game.ArmoredCore6]: Game.EldenRing + } + }, + [ActionType.EllipticalParticleSpread]: { + props: { + unk_er_f1_0: { default: false, paths: {}, field: FieldType.Boolean }, + angleX: { default: 30, paths: {} }, + angleY: { default: 30, paths: {} }, + distribution: { default: 0, paths: {} }, + }, + games: { + [Game.DarkSouls3]: { + properties1: ['angleX','angleY','distribution'] + }, + [Game.Sekiro]: Game.DarkSouls3, + [Game.EldenRing]: { + fields1: ['unk_er_f1_0'], + properties1: Game.DarkSouls3 + }, + [Game.ArmoredCore6]: Game.EldenRing + } + }, + [ActionType.RectangularParticleSpread]: { + props: { + angleX: { default: 30, paths: {} }, + angleY: { default: 30, paths: {} }, + distribution: { default: 0, paths: {} }, + }, + games: { + [Game.DarkSouls3]: { + properties1: ['angleX','angleY','distribution'] + }, + [Game.Sekiro]: Game.DarkSouls3, + [Game.EldenRing]: Game.DarkSouls3, + [Game.ArmoredCore6]: Game.DarkSouls3 + } + }, + [ActionType.PointSprite]: { + props: { + texture: { default: 1, paths: {}, field: FieldType.Integer }, + blendMode: { default: BlendMode.Normal, paths: {}, field: FieldType.Integer }, + size: { default: 1, paths: {} }, + color1: { default: [1, 1, 1, 1], paths: {} }, + color2: { default: [1, 1, 1, 1], paths: {} }, + color3: { default: [1, 1, 1, 1], paths: {} }, + rgbMultiplier: { default: 1, paths: {} }, + alphaMultiplier: { default: 1, paths: {} }, + bloomRed: { default: 1, paths: {}, field: FieldType.Float }, + bloomGreen: { default: 1, paths: {}, field: FieldType.Float }, + bloomBlue: { default: 1, paths: {}, field: FieldType.Float }, + bloomStrength: { default: 0, paths: {}, field: FieldType.Float }, + minDistance: { default: -1, paths: {}, field: FieldType.Float }, + maxDistance: { default: -1, paths: {}, field: FieldType.Float }, + unk_ds3_f1_2: { default: -2, paths: {}, field: FieldType.Integer }, + unk_ds3_f1_3: { default: -2, paths: {}, field: FieldType.Integer }, + unk_ds3_f1_4: { default: 0, paths: {}, field: FieldType.Integer }, + unk_ds3_f2_0: { default: 0, paths: {}, field: FieldType.Integer }, + unk_ds3_f2_1: { default: 0, paths: {}, field: FieldType.Integer }, + unk_ds3_f2_2: { default: 8, paths: {}, field: FieldType.Integer }, + unk_ds3_f2_3: { default: 0, paths: {}, field: FieldType.Integer }, + unk_ds3_f2_4: { default: 1, paths: {}, field: FieldType.Integer }, + unk_ds3_f2_9: { default: 0, paths: {}, field: FieldType.Integer }, + unk_ds3_f2_10: { default: 0, paths: {}, field: FieldType.Integer }, + unk_ds3_f2_11: { default: 0, paths: {}, field: FieldType.Integer }, + unk_ds3_f2_12: { default: 0, paths: {}, field: FieldType.Integer }, + unk_ds3_f2_13: { default: 0, paths: {}, field: FieldType.Integer }, + unkDistFadeClose0: { default: -1, paths: {}, field: FieldType.Float }, + unkDistFadeClose1: { default: -1, paths: {}, field: FieldType.Float }, + unkDistFadeFar0: { default: -1, paths: {}, field: FieldType.Float }, + unkDistFadeFar1: { default: -1, paths: {}, field: FieldType.Float }, + unk_ds3_f2_20: { default: 0, paths: {}, field: FieldType.Integer }, + unk_ds3_f2_21: { default: 0, paths: {}, field: FieldType.Integer }, + unk_ds3_f2_22: { default: 0, paths: {}, field: FieldType.Integer }, + unk_ds3_f2_23: { default: 0, paths: {}, field: FieldType.Integer }, + unk_ds3_f2_24: { default: 0, paths: {}, field: FieldType.Integer }, + unkDepthBlend1: { default: 1, paths: {}, field: FieldType.Float }, + unkDepthBlend2: { default: 0, paths: {}, field: FieldType.Float }, + unk_ds3_f2_27: { default: 0, paths: {}, field: FieldType.Integer }, + unk_ds3_f2_28: { default: 0, paths: {}, field: FieldType.Integer }, + unk_ds3_f2_29: { default: 0, paths: {}, field: FieldType.Integer }, + unk_ds3_p2_2: { default: 0, paths: {} }, + unk_ds3_p2_3: { default: [1, 1, 1, 1], paths: {} }, + unk_ds3_p2_4: { default: [1, 1, 1, 1], paths: {} }, + unk_ds3_p2_5: { default: [1, 1, 1, 1], paths: {} }, + unk_ds3_p2_6: { default: 0, paths: {} }, + unk_sdt_f2_30: { default: 0, paths: {}, field: FieldType.Integer }, + unk_sdt_f2_31: { default: 0, paths: {}, field: FieldType.Integer }, + unk_sdt_f2_32: { default: 0, paths: {}, field: FieldType.Integer }, + unk_sdt_f2_33: { default: 0, paths: {}, field: FieldType.Integer }, + unk_sdt_f2_34: { default: 0, paths: {}, field: FieldType.Float }, + unk_sdt_f2_35: { default: -1, paths: {}, field: FieldType.Integer }, + unk_sdt_f2_36: { default: -2, paths: {}, field: FieldType.Integer }, + unk_sdt_f2_37: { default: 0, paths: {}, field: FieldType.Integer }, + unk_sdt_f2_38: { default: 0, paths: {}, field: FieldType.Integer }, + unk_er_f1_3: { default: 1, paths: {}, field: FieldType.Integer }, + unk_er_f1_4: { default: 1, paths: {}, field: FieldType.Integer }, + unk_er_f2_39: { default: 0, paths: {}, field: FieldType.Integer }, + }, + games: { + [Game.DarkSouls3]: { + fields1: ['texture','blendMode','unk_ds3_f1_2','unk_ds3_f1_3','unk_ds3_f1_4'], + fields2: ['unk_ds3_f2_0','unk_ds3_f2_1','unk_ds3_f2_2','unk_ds3_f2_3','unk_ds3_f2_4','bloomRed','bloomGreen','bloomBlue','bloomStrength','unk_ds3_f2_9','unk_ds3_f2_10','unk_ds3_f2_11','unk_ds3_f2_12','unk_ds3_f2_13','unkDistFadeClose0','unkDistFadeClose1','unkDistFadeFar0','unkDistFadeFar1','minDistance','maxDistance','unk_ds3_f2_20','unk_ds3_f2_21','unk_ds3_f2_22','unk_ds3_f2_23','unk_ds3_f2_24','unkDepthBlend1','unkDepthBlend2','unk_ds3_f2_27','unk_ds3_f2_28','unk_ds3_f2_29'], + properties1: ['size','color1','color2','color3'], + properties2: ['rgbMultiplier','alphaMultiplier','unk_ds3_p2_2','unk_ds3_p2_3','unk_ds3_p2_4','unk_ds3_p2_5','unk_ds3_p2_6'] + }, + [Game.Sekiro]: { + fields1: ['unk_ds3_f1_2','unk_ds3_f1_3','unk_ds3_f1_4'], + fields2: ['unk_ds3_f2_0','unk_ds3_f2_1','unk_ds3_f2_2','unk_ds3_f2_3','unk_ds3_f2_4','bloomRed','bloomGreen','bloomBlue','bloomStrength','unk_ds3_f2_9','unk_ds3_f2_10','unk_ds3_f2_11','unk_ds3_f2_12','unk_ds3_f2_13','unkDistFadeClose0','unkDistFadeClose1','unkDistFadeFar0','unkDistFadeFar1','minDistance','maxDistance','unk_ds3_f2_20','unk_ds3_f2_21','unk_ds3_f2_22','unk_ds3_f2_23','unk_ds3_f2_24','unkDepthBlend1','unkDepthBlend2','unk_ds3_f2_27','unk_ds3_f2_28','unk_ds3_f2_29','unk_sdt_f2_30','unk_sdt_f2_31','unk_sdt_f2_32','unk_sdt_f2_33','unk_sdt_f2_34','unk_sdt_f2_35','unk_sdt_f2_36','unk_sdt_f2_37','unk_sdt_f2_38'], + properties1: ['texture','blendMode','size','color1','color2','color3'], + properties2: Game.DarkSouls3 + }, + [Game.EldenRing]: { + fields1: ['unk_ds3_f1_2','unk_ds3_f1_3','unk_ds3_f1_4','unk_er_f1_3','unk_er_f1_4'], + fields2: ['unk_ds3_f2_0','unk_ds3_f2_1','unk_ds3_f2_2','unk_ds3_f2_3','unk_ds3_f2_4','bloomRed','bloomGreen','bloomBlue','bloomStrength','unk_ds3_f2_9','unk_ds3_f2_10','unk_ds3_f2_11','unk_ds3_f2_12','unk_ds3_f2_13','unkDistFadeClose0','unkDistFadeClose1','unkDistFadeFar0','unkDistFadeFar1','minDistance','maxDistance','unk_ds3_f2_20','unk_ds3_f2_21','unk_ds3_f2_22','unk_ds3_f2_23','unk_ds3_f2_24','unkDepthBlend1','unkDepthBlend2','unk_ds3_f2_27','unk_ds3_f2_28','unk_ds3_f2_29','unk_sdt_f2_30','unk_sdt_f2_31','unk_sdt_f2_32','unk_sdt_f2_33','unk_sdt_f2_34','unk_sdt_f2_35','unk_sdt_f2_36','unk_sdt_f2_37','unk_sdt_f2_38','unk_er_f2_39'], + properties1: Game.Sekiro, + properties2: Game.DarkSouls3 + }, + [Game.ArmoredCore6]: Game.EldenRing + } + }, + [ActionType.Line]: { + props: { + blendMode: { default: BlendMode.Normal, paths: {}, field: FieldType.Integer }, + length: { default: 1, paths: {} }, + color1: { default: [1, 1, 1, 1], paths: {} }, + color2: { default: [1, 1, 1, 1], paths: {} }, + startColor: { default: [1, 1, 1, 1], paths: {} }, + endColor: { default: [1, 1, 1, 1], paths: {} }, + lengthMultiplier: { default: 1, paths: {} }, + color3: { default: [1, 1, 1, 1], paths: {} }, + rgbMultiplier: { default: 1, paths: {} }, + alphaMultiplier: { default: 1, paths: {} }, + bloomRed: { default: 1, paths: {}, field: FieldType.Float }, + bloomGreen: { default: 1, paths: {}, field: FieldType.Float }, + bloomBlue: { default: 1, paths: {}, field: FieldType.Float }, + bloomStrength: { default: 0, paths: {}, field: FieldType.Float }, + minDistance: { default: -1, paths: {}, field: FieldType.Float }, + maxDistance: { default: -1, paths: {}, field: FieldType.Float }, + unk_ds3_f1_1: { default: -1, paths: {}, field: FieldType.Integer }, + unk_ds3_f2_0: { default: 0, paths: {}, field: FieldType.Integer }, + unk_ds3_f2_1: { default: 0, paths: {}, field: FieldType.Integer }, + unk_ds3_f2_2: { default: 8, paths: {}, field: FieldType.Integer }, + unk_ds3_f2_3: { default: 0, paths: {}, field: FieldType.Integer }, + unk_ds3_f2_4: { default: 1, paths: {}, field: FieldType.Integer }, + unk_ds3_f2_9: { default: 0, paths: {}, field: FieldType.Integer }, + unk_ds3_f2_10: { default: 0, paths: {}, field: FieldType.Integer }, + unk_ds3_f2_11: { default: 0, paths: {}, field: FieldType.Integer }, + unk_ds3_f2_12: { default: 0, paths: {}, field: FieldType.Integer }, + unk_ds3_f2_13: { default: 0, paths: {}, field: FieldType.Integer }, + unkDistFadeClose0: { default: -1, paths: {}, field: FieldType.Float }, + unkDistFadeClose1: { default: -1, paths: {}, field: FieldType.Float }, + unkDistFadeFar0: { default: -1, paths: {}, field: FieldType.Float }, + unkDistFadeFar1: { default: -1, paths: {}, field: FieldType.Float }, + unk_ds3_f2_20: { default: 0, paths: {}, field: FieldType.Integer }, + unk_ds3_f2_21: { default: 0, paths: {}, field: FieldType.Integer }, + unk_ds3_f2_22: { default: 0, paths: {}, field: FieldType.Integer }, + unk_ds3_f2_23: { default: 0, paths: {}, field: FieldType.Integer }, + unk_ds3_f2_24: { default: 0, paths: {}, field: FieldType.Integer }, + unkDepthBlend1: { default: 1, paths: {}, field: FieldType.Float }, + unkDepthBlend2: { default: 0, paths: {}, field: FieldType.Float }, + unk_ds3_f2_27: { default: 0, paths: {}, field: FieldType.Integer }, + unk_ds3_f2_28: { default: 0, paths: {}, field: FieldType.Integer }, + unk_ds3_f2_29: { default: 0, paths: {}, field: FieldType.Integer }, + unk_ds3_p2_2: { default: 0, paths: {} }, + unk_ds3_p2_3: { default: [1, 1, 1, 1], paths: {} }, + unk_ds3_p2_4: { default: [1, 1, 1, 1], paths: {} }, + unk_ds3_p2_5: { default: [1, 1, 1, 1], paths: {} }, + unk_ds3_p2_6: { default: 0, paths: {} }, + unk_sdt_f2_30: { default: 0, paths: {}, field: FieldType.Integer }, + unk_sdt_f2_31: { default: 0, paths: {}, field: FieldType.Integer }, + unk_sdt_f2_32: { default: 0, paths: {}, field: FieldType.Integer }, + unk_sdt_f2_33: { default: 0, paths: {}, field: FieldType.Integer }, + unk_sdt_f2_34: { default: 0, paths: {}, field: FieldType.Float }, + unk_sdt_f2_35: { default: -2, paths: {}, field: FieldType.Integer }, + unk_sdt_f2_36: { default: -2, paths: {}, field: FieldType.Integer }, + unk_sdt_f2_37: { default: 0, paths: {}, field: FieldType.Integer }, + unk_sdt_f2_38: { default: 0, paths: {}, field: FieldType.Integer }, + unk_sdt_f2_39: { default: 0, paths: {}, field: FieldType.Integer }, + unk_er_f1_1: { default: 1, paths: {}, field: FieldType.Integer }, + unk_er_f1_2: { default: 1, paths: {}, field: FieldType.Integer }, + }, + games: { + [Game.DarkSouls3]: { + fields1: ['blendMode','unk_ds3_f1_1'], + fields2: ['unk_ds3_f2_0','unk_ds3_f2_1','unk_ds3_f2_2','unk_ds3_f2_3','unk_ds3_f2_4','bloomRed','bloomGreen','bloomBlue','bloomStrength','unk_ds3_f2_9','unk_ds3_f2_10','unk_ds3_f2_11','unk_ds3_f2_12','unk_ds3_f2_13','unkDistFadeClose0','unkDistFadeClose1','unkDistFadeFar0','unkDistFadeFar1','minDistance','maxDistance','unk_ds3_f2_20','unk_ds3_f2_21','unk_ds3_f2_22','unk_ds3_f2_23','unk_ds3_f2_24','unkDepthBlend1','unkDepthBlend2','unk_ds3_f2_27','unk_ds3_f2_28','unk_ds3_f2_29'], + properties1: ['length','color1','color2','startColor','endColor','lengthMultiplier','color3'], + properties2: ['rgbMultiplier','alphaMultiplier','unk_ds3_p2_2','unk_ds3_p2_3','unk_ds3_p2_4','unk_ds3_p2_5','unk_ds3_p2_6'] + }, + [Game.Sekiro]: { + fields1: ['unk_ds3_f1_1'], + fields2: ['unk_ds3_f2_0','unk_ds3_f2_1','unk_ds3_f2_2','unk_ds3_f2_3','unk_ds3_f2_4','bloomRed','bloomGreen','bloomBlue','bloomStrength','unk_ds3_f2_9','unk_ds3_f2_10','unk_ds3_f2_11','unk_ds3_f2_12','unk_ds3_f2_13','unkDistFadeClose0','unkDistFadeClose1','unkDistFadeFar0','unkDistFadeFar1','minDistance','maxDistance','unk_ds3_f2_20','unk_ds3_f2_21','unk_ds3_f2_22','unk_ds3_f2_23','unk_ds3_f2_24','unkDepthBlend1','unkDepthBlend2','unk_ds3_f2_27','unk_ds3_f2_28','unk_ds3_f2_29','unk_sdt_f2_30','unk_sdt_f2_31','unk_sdt_f2_32','unk_sdt_f2_33','unk_sdt_f2_34','unk_sdt_f2_35','unk_sdt_f2_36','unk_sdt_f2_37','unk_sdt_f2_38','unk_sdt_f2_39'], + properties1: ['blendMode','length','color1','color2','startColor','endColor','lengthMultiplier','color3'], + properties2: Game.DarkSouls3 + }, + [Game.EldenRing]: { + fields1: ['unk_ds3_f1_1','unk_er_f1_1','unk_er_f1_2'], + fields2: Game.Sekiro, + properties1: Game.Sekiro, + properties2: Game.DarkSouls3 + }, + [Game.ArmoredCore6]: Game.EldenRing + } + }, + [ActionType.QuadLine]: { + props: { + blendMode: { default: BlendMode.Normal, paths: {}, field: FieldType.Integer }, + width: { default: 1, paths: {} }, + length: { default: 1, paths: {} }, + color1: { default: [1, 1, 1, 1], paths: {} }, + color2: { default: [1, 1, 1, 1], paths: {} }, + startColor: { default: [1, 1, 1, 1], paths: {} }, + endColor: { default: [1, 1, 1, 1], paths: {} }, + widthMultiplier: { default: 1, paths: {} }, + lengthMultiplier: { default: 1, paths: {} }, + color3: { default: [1, 1, 1, 1], paths: {} }, + rgbMultiplier: { default: 1, paths: {} }, + alphaMultiplier: { default: 1, paths: {} }, + bloomRed: { default: 1, paths: {}, field: FieldType.Float }, + bloomGreen: { default: 1, paths: {}, field: FieldType.Float }, + bloomBlue: { default: 1, paths: {}, field: FieldType.Float }, + bloomStrength: { default: 0, paths: {}, field: FieldType.Float }, + minDistance: { default: -1, paths: {}, field: FieldType.Float }, + maxDistance: { default: -1, paths: {}, field: FieldType.Float }, + unk_ds3_f1_1: { default: -1, paths: {}, field: FieldType.Integer }, + unk_ds3_f2_0: { default: 0, paths: {}, field: FieldType.Integer }, + unk_ds3_f2_1: { default: 0, paths: {}, field: FieldType.Integer }, + unk_ds3_f2_2: { default: 8, paths: {}, field: FieldType.Integer }, + unk_ds3_f2_3: { default: 0, paths: {}, field: FieldType.Integer }, + unk_ds3_f2_4: { default: 1, paths: {}, field: FieldType.Integer }, + unk_ds3_f2_9: { default: 0, paths: {}, field: FieldType.Integer }, + unk_ds3_f2_10: { default: 0, paths: {}, field: FieldType.Integer }, + unk_ds3_f2_11: { default: 0, paths: {}, field: FieldType.Integer }, + unk_ds3_f2_12: { default: 0, paths: {}, field: FieldType.Integer }, + unk_ds3_f2_13: { default: 0, paths: {}, field: FieldType.Integer }, + unkDistFadeClose0: { default: -1, paths: {}, field: FieldType.Float }, + unkDistFadeClose1: { default: -1, paths: {}, field: FieldType.Float }, + unkDistFadeFar0: { default: -1, paths: {}, field: FieldType.Float }, + unkDistFadeFar1: { default: -1, paths: {}, field: FieldType.Float }, + unk_ds3_f2_20: { default: 0, paths: {}, field: FieldType.Integer }, + unk_ds3_f2_21: { default: 0, paths: {}, field: FieldType.Integer }, + unk_ds3_f2_22: { default: 0, paths: {}, field: FieldType.Integer }, + unk_ds3_f2_23: { default: 0, paths: {}, field: FieldType.Integer }, + unk_ds3_f2_24: { default: 0, paths: {}, field: FieldType.Integer }, + unkDepthBlend1: { default: 1, paths: {}, field: FieldType.Float }, + unkDepthBlend2: { default: 0, paths: {}, field: FieldType.Float }, + unk_ds3_f2_27: { default: 0, paths: {}, field: FieldType.Integer }, + unk_ds3_f2_28: { default: 0, paths: {}, field: FieldType.Integer }, + unk_ds3_f2_29: { default: 0, paths: {}, field: FieldType.Integer }, + unk_ds3_p2_2: { default: 0, paths: {} }, + unk_ds3_p2_3: { default: [1, 1, 1, 1], paths: {} }, + unk_ds3_p2_4: { default: [1, 1, 1, 1], paths: {} }, + unk_ds3_p2_5: { default: [1, 1, 1, 1], paths: {} }, + unk_ds3_p2_6: { default: 0, paths: {} }, + unk_sdt_f2_30: { default: 0, paths: {}, field: FieldType.Integer }, + unk_sdt_f2_31: { default: 0, paths: {}, field: FieldType.Integer }, + unk_sdt_f2_32: { default: 0, paths: {}, field: FieldType.Integer }, + unk_sdt_f2_33: { default: 0, paths: {}, field: FieldType.Integer }, + unk_sdt_f2_34: { default: 0, paths: {}, field: FieldType.Float }, + unk_sdt_f2_35: { default: -2, paths: {}, field: FieldType.Integer }, + unk_sdt_f2_36: { default: -2, paths: {}, field: FieldType.Integer }, + unk_sdt_f2_37: { default: 0, paths: {}, field: FieldType.Integer }, + unk_sdt_f2_38: { default: 0, paths: {}, field: FieldType.Integer }, + unk_sdt_f2_39: { default: 0, paths: {}, field: FieldType.Integer }, + unk_er_f1_1: { default: 1, paths: {}, field: FieldType.Integer }, + unk_er_f1_2: { default: 1, paths: {}, field: FieldType.Integer }, + }, + games: { + [Game.DarkSouls3]: { + fields1: ['blendMode','unk_ds3_f1_1'], + fields2: ['unk_ds3_f2_0','unk_ds3_f2_1','unk_ds3_f2_2','unk_ds3_f2_3','unk_ds3_f2_4','bloomRed','bloomGreen','bloomBlue','bloomStrength','unk_ds3_f2_9','unk_ds3_f2_10','unk_ds3_f2_11','unk_ds3_f2_12','unk_ds3_f2_13','unkDistFadeClose0','unkDistFadeClose1','unkDistFadeFar0','unkDistFadeFar1','minDistance','maxDistance','unk_ds3_f2_20','unk_ds3_f2_21','unk_ds3_f2_22','unk_ds3_f2_23','unk_ds3_f2_24','unkDepthBlend1','unkDepthBlend2','unk_ds3_f2_27','unk_ds3_f2_28','unk_ds3_f2_29'], + properties1: ['width','length','color1','color2','startColor','endColor','widthMultiplier','lengthMultiplier','color3'], + properties2: ['rgbMultiplier','alphaMultiplier','unk_ds3_p2_2','unk_ds3_p2_3','unk_ds3_p2_4','unk_ds3_p2_5','unk_ds3_p2_6'] + }, + [Game.Sekiro]: { + fields1: ['unk_ds3_f1_1'], + fields2: ['unk_ds3_f2_0','unk_ds3_f2_1','unk_ds3_f2_2','unk_ds3_f2_3','unk_ds3_f2_4','bloomRed','bloomGreen','bloomBlue','bloomStrength','unk_ds3_f2_9','unk_ds3_f2_10','unk_ds3_f2_11','unk_ds3_f2_12','unk_ds3_f2_13','unkDistFadeClose0','unkDistFadeClose1','unkDistFadeFar0','unkDistFadeFar1','minDistance','maxDistance','unk_ds3_f2_20','unk_ds3_f2_21','unk_ds3_f2_22','unk_ds3_f2_23','unk_ds3_f2_24','unkDepthBlend1','unkDepthBlend2','unk_ds3_f2_27','unk_ds3_f2_28','unk_ds3_f2_29','unk_sdt_f2_30','unk_sdt_f2_31','unk_sdt_f2_32','unk_sdt_f2_33','unk_sdt_f2_34','unk_sdt_f2_35','unk_sdt_f2_36','unk_sdt_f2_37','unk_sdt_f2_38','unk_sdt_f2_39'], + properties1: ['blendMode','width','length','color1','color2','startColor','endColor','widthMultiplier','lengthMultiplier','color3'], + properties2: Game.DarkSouls3 + }, + [Game.EldenRing]: { + fields1: ['unk_ds3_f1_1','unk_er_f1_1','unk_er_f1_2'], + fields2: Game.Sekiro, + properties1: Game.Sekiro, + properties2: Game.DarkSouls3 + }, + [Game.ArmoredCore6]: Game.EldenRing + } + }, + [ActionType.BillboardEx]: { + props: { + texture: { default: 1, paths: {}, field: FieldType.Integer }, + blendMode: { default: BlendMode.Normal, paths: {}, field: FieldType.Integer }, + offsetX: { default: 0, paths: {} }, + offsetY: { default: 0, paths: {} }, + offsetZ: { default: 0, paths: {} }, + width: { default: 1, paths: {} }, + height: { default: 1, paths: {} }, + color1: { default: [1, 1, 1, 1], paths: {} }, + color2: { default: [1, 1, 1, 1], paths: {} }, + color3: { default: [1, 1, 1, 1], paths: {} }, + alphaThreshold: { default: 0, paths: {} }, + rotationX: { default: 0, paths: {} }, + rotationY: { default: 0, paths: {} }, + rotationZ: { default: 0, paths: {} }, + rotationSpeedX: { default: 0, paths: {} }, + rotationSpeedY: { default: 0, paths: {} }, + rotationSpeedZ: { default: 0, paths: {} }, + rotationSpeedMultiplierX: { default: 1, paths: {} }, + rotationSpeedMultiplierY: { default: 1, paths: {} }, + rotationSpeedMultiplierZ: { default: 1, paths: {} }, + depthOffset: { default: 0, paths: {} }, + frameIndex: { default: 0, paths: {} }, + frameIndexOffset: { default: 0, paths: {} }, + rgbMultiplier: { default: 1, paths: {} }, + alphaMultiplier: { default: 1, paths: {} }, + orientation: { default: OrientationMode.CameraPlane, paths: {}, field: FieldType.Integer }, + normalMap: { default: 0, paths: {}, field: FieldType.Integer }, + scaleVariationX: { default: 1, paths: {}, field: FieldType.Float }, + scaleVariationY: { default: 1, paths: {}, field: FieldType.Float }, + uniformScale: { default: false, paths: {}, field: FieldType.Boolean }, + columns: { default: 1, paths: {}, field: FieldType.Integer }, + totalFrames: { default: 1, paths: {}, field: FieldType.Integer }, + interpolateFrames: { default: true, paths: {}, field: FieldType.Boolean }, + bloomRed: { default: 1, paths: {}, field: FieldType.Float }, + bloomGreen: { default: 1, paths: {}, field: FieldType.Float }, + bloomBlue: { default: 1, paths: {}, field: FieldType.Float }, + bloomStrength: { default: 0, paths: {}, field: FieldType.Float }, + minDistance: { default: -1, paths: {}, field: FieldType.Float }, + maxDistance: { default: -1, paths: {}, field: FieldType.Float }, + shadowDarkness: { default: 0, paths: {}, field: FieldType.Float }, + specular: { default: 0, paths: {}, field: FieldType.Integer }, + glossiness: { default: 0.25, paths: {}, field: FieldType.Float }, + lighting: { default: LightingMode.Unlit, paths: {}, field: FieldType.Integer }, + specularity: { default: 0.5, paths: {}, field: FieldType.Float }, + unk_ds3_f1_7: { default: 0, paths: {}, field: FieldType.Integer }, + unk_ds3_f1_11: { default: 0, paths: {}, field: FieldType.Integer }, + unk_ds3_f1_12: { default: 0, paths: {}, field: FieldType.Integer }, + unk_ds3_f1_13: { default: -1, paths: {}, field: FieldType.Float }, + unk_ds3_f1_14: { default: 1, paths: {}, field: FieldType.Integer }, + unk_ds3_f1_15: { default: 0, paths: {}, field: FieldType.Integer }, + unk_ds3_f1_16: { default: 0, paths: {}, field: FieldType.Integer }, + unk_ds3_f2_0: { default: 0, paths: {}, field: FieldType.Integer }, + unk_ds3_f2_1: { default: 0, paths: {}, field: FieldType.Boolean }, + unk_ds3_f2_2: { default: 8, paths: {}, field: FieldType.Integer }, + unk_ds3_f2_3: { default: 0, paths: {}, field: FieldType.Integer }, + unk_ds3_f2_4: { default: 1, paths: {}, field: FieldType.Integer }, + unk_ds3_f2_9: { default: 0, paths: {}, field: FieldType.Integer }, + unk_ds3_f2_10: { default: 0, paths: {}, field: FieldType.Integer }, + unk_ds3_f2_11: { default: 0, paths: {}, field: FieldType.Integer }, + unk_ds3_f2_12: { default: 0, paths: {}, field: FieldType.Integer }, + unk_ds3_f2_13: { default: 0, paths: {}, field: FieldType.Integer }, + unkDistFadeClose0: { default: -1, paths: {}, field: FieldType.Float }, + unkDistFadeClose1: { default: -1, paths: {}, field: FieldType.Float }, + unkDistFadeFar0: { default: -1, paths: {}, field: FieldType.Float }, + unkDistFadeFar1: { default: -1, paths: {}, field: FieldType.Float }, + unk_ds3_f2_20: { default: 0, paths: {}, field: FieldType.Integer }, + unk_ds3_f2_21: { default: 0, paths: {}, field: FieldType.Integer }, + unk_ds3_f2_22: { default: 0, paths: {}, field: FieldType.Integer }, + unk_ds3_f2_23: { default: 0, paths: {}, field: FieldType.Integer }, + unk_ds3_f2_24: { default: 0, paths: {}, field: FieldType.Integer }, + unkDepthBlend1: { default: 1, paths: {}, field: FieldType.Float }, + unkDepthBlend2: { default: 0, paths: {}, field: FieldType.Float }, + unk_ds3_f2_27: { default: 1, paths: {}, field: FieldType.Integer }, + unk_ds3_f2_28: { default: 0, paths: {}, field: FieldType.Integer }, + unk_ds3_f2_29: { default: 5, paths: {}, field: FieldType.Float }, + unk_ds3_p1_21: { default: 0, paths: {} }, + unk_ds3_p1_22: { default: 0, paths: {} }, + unk_ds3_p2_2: { default: 0, paths: {} }, + unk_ds3_p2_3: { default: [1, 1, 1, 1], paths: {} }, + unk_ds3_p2_4: { default: [1, 1, 1, 1], paths: {} }, + unk_ds3_p2_5: { default: [1, 1, 1, 1], paths: {} }, + unk_ds3_p2_6: { default: 0, paths: {} }, + unk_sdt_f1_15: { default: 1, paths: {}, field: FieldType.Integer }, + unk_sdt_f1_16: { default: 1, paths: {}, field: FieldType.Integer }, + unk_sdt_f1_17: { default: 0, paths: {}, field: FieldType.Integer }, + unk_sdt_f2_31: { default: 0, paths: {}, field: FieldType.Integer }, + unk_sdt_f2_32: { default: 1, paths: {}, field: FieldType.Integer }, + unk_sdt_f2_36: { default: -2, paths: {}, field: FieldType.Integer }, + unk_sdt_f2_37: { default: 0, paths: {}, field: FieldType.Integer }, + unk_sdt_f2_39: { default: 1, paths: {}, field: FieldType.Integer }, + unk_sdt_f2_40: { default: 0, paths: {}, field: FieldType.Integer }, + unk_sdt_f2_41: { default: 0, paths: {}, field: FieldType.Integer }, + unk_sdt_f2_42: { default: 0, paths: {}, field: FieldType.Integer }, + unk_sdt_f2_43: { default: 0, paths: {}, field: FieldType.Integer }, + unk_sdt_f2_44: { default: 0, paths: {}, field: FieldType.Integer }, + }, + games: { + [Game.DarkSouls3]: { + fields1: ['orientation','texture','normalMap','blendMode','scaleVariationX','scaleVariationY','uniformScale','unk_ds3_f1_7','columns','totalFrames','interpolateFrames','unk_ds3_f1_11','unk_ds3_f1_12','unk_ds3_f1_13','unk_ds3_f1_14','unk_ds3_f1_15','unk_ds3_f1_16'], + fields2: ['unk_ds3_f2_0','unk_ds3_f2_1','unk_ds3_f2_2','unk_ds3_f2_3','unk_ds3_f2_4','bloomRed','bloomGreen','bloomBlue','bloomStrength','unk_ds3_f2_9','unk_ds3_f2_10','unk_ds3_f2_11','unk_ds3_f2_12','unk_ds3_f2_13','unkDistFadeClose0','unkDistFadeClose1','unkDistFadeFar0','unkDistFadeFar1','minDistance','maxDistance','unk_ds3_f2_20','unk_ds3_f2_21','unk_ds3_f2_22','unk_ds3_f2_23','unk_ds3_f2_24','unkDepthBlend1','unkDepthBlend2','unk_ds3_f2_27','unk_ds3_f2_28','unk_ds3_f2_29'], + properties1: ['offsetX','offsetY','offsetZ','width','height','color1','color2','color3','alphaThreshold','rotationX','rotationY','rotationZ','rotationSpeedX','rotationSpeedMultiplierX','rotationSpeedY','rotationSpeedMultiplierY','rotationSpeedZ','rotationSpeedMultiplierZ','depthOffset','frameIndex','frameIndexOffset','unk_ds3_p1_21','unk_ds3_p1_22'], + properties2: ['rgbMultiplier','alphaMultiplier','unk_ds3_p2_2','unk_ds3_p2_3','unk_ds3_p2_4','unk_ds3_p2_5','unk_ds3_p2_6'] + }, + [Game.Sekiro]: { + fields1: ['orientation','normalMap','scaleVariationX','scaleVariationY','uniformScale','unk_ds3_f1_7','columns','totalFrames','interpolateFrames','unk_ds3_f1_11','unk_ds3_f1_12','unk_ds3_f1_13','unk_ds3_f1_14','unk_ds3_f1_15','unk_ds3_f1_16','unk_sdt_f1_15','unk_sdt_f1_16','unk_sdt_f1_17'], + fields2: ['unk_ds3_f2_0','unk_ds3_f2_1','unk_ds3_f2_2','unk_ds3_f2_3','unk_ds3_f2_4','bloomRed','bloomGreen','bloomBlue','bloomStrength','unk_ds3_f2_9','unk_ds3_f2_10','unk_ds3_f2_11','unk_ds3_f2_12','unk_ds3_f2_13','unkDistFadeClose0','unkDistFadeClose1','unkDistFadeFar0','unkDistFadeFar1','minDistance','maxDistance','unk_ds3_f2_20','unk_ds3_f2_21','unk_ds3_f2_22','unk_ds3_f2_23','unk_ds3_f2_24','unkDepthBlend1','unkDepthBlend2','unk_ds3_f2_27','unk_ds3_f2_28','unk_ds3_f2_29','shadowDarkness','unk_sdt_f2_31','unk_sdt_f2_32','specular','glossiness','lighting','unk_sdt_f2_36','unk_sdt_f2_37','specularity','unk_sdt_f2_39','unk_sdt_f2_40','unk_sdt_f2_41','unk_sdt_f2_42','unk_sdt_f2_43','unk_sdt_f2_44'], + properties1: ['texture','blendMode','offsetX','offsetY','offsetZ','width','height','color1','color2','color3','alphaThreshold','rotationX','rotationY','rotationZ','rotationSpeedX','rotationSpeedMultiplierX','rotationSpeedY','rotationSpeedMultiplierY','rotationSpeedZ','rotationSpeedMultiplierZ','depthOffset','frameIndex','frameIndexOffset','unk_ds3_p1_21','unk_ds3_p1_22'], + properties2: Game.DarkSouls3 + }, + [Game.EldenRing]: Game.Sekiro, + [Game.ArmoredCore6]: Game.Sekiro + } + }, + [ActionType.MultiTextureBillboardEx]: { + props: { + orientation: { default: OrientationMode.CameraPlane, paths: {}, field: FieldType.Integer }, + mask: { default: 1, paths: {}, field: FieldType.Integer }, + layer1: { default: 1, paths: {}, field: FieldType.Integer }, + layer2: { default: 1, paths: {}, field: FieldType.Integer }, + uniformScale: { default: false, paths: {}, field: FieldType.Boolean }, + columns: { default: 1, paths: {}, field: FieldType.Integer }, + totalFrames: { default: 1, paths: {}, field: FieldType.Integer }, + interpolateFrames: { default: true, paths: {}, field: FieldType.Boolean }, + depthBlend: { default: true, paths: {}, field: FieldType.Boolean }, + octagonal: { default: false, paths: {}, field: FieldType.Boolean }, + bloomRed: { default: 1, paths: {}, field: FieldType.Float }, + bloomGreen: { default: 1, paths: {}, field: FieldType.Float }, + bloomBlue: { default: 1, paths: {}, field: FieldType.Float }, + bloomStrength: { default: 0, paths: {}, field: FieldType.Float }, + minDistance: { default: -1, paths: {}, field: FieldType.Float }, + maxDistance: { default: -1, paths: {}, field: FieldType.Float }, + shadowDarkness: { default: 0, paths: {}, field: FieldType.Float }, + specular: { default: 0, paths: {}, field: FieldType.Integer }, + glossiness: { default: 0.25, paths: {}, field: FieldType.Float }, + lighting: { default: LightingMode.Unlit, paths: {}, field: FieldType.Integer }, + unk_sdt_f2_38: { default: 1, paths: {}, field: FieldType.Integer }, + blendMode: { default: BlendMode.Normal, paths: {}, field: FieldType.Integer }, + offsetX: { default: 0, paths: {} }, + offsetY: { default: 0, paths: {} }, + offsetZ: { default: 0, paths: {} }, + width: { default: 1, paths: {} }, + height: { default: 1, paths: {} }, + rotationX: { default: 0, paths: {} }, + rotationY: { default: 0, paths: {} }, + rotationZ: { default: 0, paths: {} }, + rotationSpeedX: { default: 0, paths: {} }, + rotationSpeedY: { default: 0, paths: {} }, + rotationSpeedZ: { default: 0, paths: {} }, + rotationSpeedMultiplierX: { default: 1, paths: {} }, + rotationSpeedMultiplierY: { default: 1, paths: {} }, + rotationSpeedMultiplierZ: { default: 1, paths: {} }, + color1: { default: [1, 1, 1, 1], paths: {} }, + color2: { default: [1, 1, 1, 1], paths: {} }, + color3: { default: [1, 1, 1, 1], paths: {} }, + layersColor: { default: [1, 1, 1, 1], paths: {} }, + layer1Color: { default: [1, 1, 1, 1], paths: {} }, + layer2Color: { default: [1, 1, 1, 1], paths: {} }, + alphaThreshold: { default: 0, paths: {} }, + frameIndex: { default: 0, paths: {} }, + frameIndexOffset: { default: 0, paths: {} }, + layer1SpeedU: { default: 0, paths: {} }, + layer1SpeedV: { default: 0, paths: {} }, + layer1OffsetU: { default: 0, paths: {} }, + layer1OffsetV: { default: 0, paths: {} }, + layer1ScaleU: { default: 1, paths: {} }, + layer1ScaleV: { default: 1, paths: {} }, + layer2SpeedU: { default: 0, paths: {} }, + layer2SpeedV: { default: 0, paths: {} }, + layer2OffsetU: { default: 0, paths: {} }, + layer2OffsetV: { default: 0, paths: {} }, + layer2ScaleU: { default: 1, paths: {} }, + layer2ScaleV: { default: 1, paths: {} }, + rgbMultiplier: { default: 1, paths: {} }, + alphaMultiplier: { default: 1, paths: {} }, + unk_ds3_f1_6: { default: 0, paths: {}, field: FieldType.Integer }, + unk_ds3_f1_10: { default: -2, paths: {}, field: FieldType.Integer }, + unk_ds3_f1_11: { default: -2, paths: {}, field: FieldType.Integer }, + unk_ds3_f1_14: { default: 0, paths: {}, field: FieldType.Integer }, + unk_ds3_f2_0: { default: 0, paths: {}, field: FieldType.Integer }, + unk_ds3_f2_1: { default: 0, paths: {}, field: FieldType.Integer }, + unk_ds3_f2_2: { default: 8, paths: {}, field: FieldType.Integer }, + unk_ds3_f2_3: { default: 0, paths: {}, field: FieldType.Integer }, + unk_ds3_f2_4: { default: 1, paths: {}, field: FieldType.Integer }, + unk_ds3_f2_9: { default: 0, paths: {}, field: FieldType.Integer }, + unk_ds3_f2_10: { default: 0, paths: {}, field: FieldType.Integer }, + unk_ds3_f2_11: { default: 0, paths: {}, field: FieldType.Integer }, + unk_ds3_f2_12: { default: 0, paths: {}, field: FieldType.Integer }, + unk_ds3_f2_13: { default: 0, paths: {}, field: FieldType.Integer }, + unkDistFadeClose0: { default: -1, paths: {}, field: FieldType.Float }, + unkDistFadeClose1: { default: -1, paths: {}, field: FieldType.Float }, + unkDistFadeFar0: { default: -1, paths: {}, field: FieldType.Float }, + unkDistFadeFar1: { default: -1, paths: {}, field: FieldType.Float }, + unk_ds3_f2_20: { default: 0, paths: {}, field: FieldType.Integer }, + unk_ds3_f2_21: { default: 0, paths: {}, field: FieldType.Integer }, + unk_ds3_f2_22: { default: 0, paths: {}, field: FieldType.Integer }, + unk_ds3_f2_23: { default: 0, paths: {}, field: FieldType.Integer }, + unk_ds3_f2_24: { default: 0, paths: {}, field: FieldType.Integer }, + unkDepthBlend1: { default: 1, paths: {}, field: FieldType.Float }, + unkDepthBlend2: { default: 0, paths: {}, field: FieldType.Float }, + unk_ds3_f2_27: { default: 1, paths: {}, field: FieldType.Integer }, + unk_ds3_f2_28: { default: 0, paths: {}, field: FieldType.Integer }, + unk_ds3_f2_29: { default: 5, paths: {}, field: FieldType.Float }, + unk_ds3_p1_23: { default: 0, paths: {} }, + unk_ds3_p1_24: { default: 0, paths: {} }, + unk_ds3_p1_25: { default: 0, paths: {} }, + unk_ds3_p1_26: { default: 0, paths: {} }, + unk_ds3_p1_27: { default: 1, paths: {} }, + unk_ds3_p1_28: { default: 1, paths: {} }, + unk_ds3_p2_2: { default: 0, paths: {} }, + unk_ds3_p2_3: { default: [1, 1, 1, 1], paths: {} }, + unk_ds3_p2_4: { default: [1, 1, 1, 1], paths: {} }, + unk_ds3_p2_5: { default: [1, 1, 1, 1], paths: {} }, + unk_ds3_p2_6: { default: 0, paths: {} }, + unk_sdt_f2_31: { default: 0, paths: {}, field: FieldType.Integer }, + unk_sdt_f2_32: { default: 1, paths: {}, field: FieldType.Integer }, + unk_sdt_f2_36: { default: -2, paths: {}, field: FieldType.Integer }, + unk_sdt_f2_37: { default: 0, paths: {}, field: FieldType.Integer }, + unk_sdt_f2_39: { default: 1, paths: {}, field: FieldType.Integer }, + unk_sdt_f2_40: { default: 0, paths: {}, field: FieldType.Integer }, + unk_sdt_f2_41: { default: 0, paths: {}, field: FieldType.Integer }, + unk_er_f1_14: { default: 1, paths: {}, field: FieldType.Integer }, + unk_er_f1_15: { default: 1, paths: {}, field: FieldType.Integer }, + unk_er_f1_16: { default: 0, paths: {}, field: FieldType.Integer }, + unk_er_f2_42: { default: 0, paths: {}, field: FieldType.Integer }, + unk_er_f2_43: { default: 0, paths: {}, field: FieldType.Integer }, + unk_er_f2_44: { default: 0, paths: {}, field: FieldType.Integer }, + unk_er_f2_45: { default: 0, paths: {}, field: FieldType.Integer }, + unk_ac6_f2_46: { default: 0, paths: {}, field: FieldType.Integer }, + }, + games: { + [Game.DarkSouls3]: { + fields1: ['orientation','mask','layer1','layer2','blendMode','uniformScale','unk_ds3_f1_6','columns','totalFrames','interpolateFrames','unk_ds3_f1_10','unk_ds3_f1_11','depthBlend','octagonal','unk_ds3_f1_14'], + fields2: ['unk_ds3_f2_0','unk_ds3_f2_1','unk_ds3_f2_2','unk_ds3_f2_3','unk_ds3_f2_4','bloomRed','bloomGreen','bloomBlue','bloomStrength','unk_ds3_f2_9','unk_ds3_f2_10','unk_ds3_f2_11','unk_ds3_f2_12','unk_ds3_f2_13','unkDistFadeClose0','unkDistFadeClose1','unkDistFadeFar0','unkDistFadeFar1','minDistance','maxDistance','unk_ds3_f2_20','unk_ds3_f2_21','unk_ds3_f2_22','unk_ds3_f2_23','unk_ds3_f2_24','unkDepthBlend1','unkDepthBlend2','unk_ds3_f2_27','unk_ds3_f2_28','unk_ds3_f2_29'], + properties1: ['offsetX','offsetY','offsetZ','width','height','rotationX','rotationY','rotationZ','rotationSpeedX','rotationSpeedMultiplierX','rotationSpeedY','rotationSpeedMultiplierY','rotationSpeedZ','rotationSpeedMultiplierZ','color1','color2','color3','layersColor','layer1Color','layer2Color','alphaThreshold','frameIndex','frameIndexOffset','unk_ds3_p1_23','unk_ds3_p1_24','unk_ds3_p1_25','unk_ds3_p1_26','unk_ds3_p1_27','unk_ds3_p1_28','layer1SpeedU','layer1SpeedV','layer1OffsetU','layer1OffsetV','layer1ScaleU','layer1ScaleV','layer2SpeedU','layer2SpeedV','layer2OffsetU','layer2OffsetV','layer2ScaleU','layer2ScaleV'], + properties2: ['rgbMultiplier','alphaMultiplier','unk_ds3_p2_2','unk_ds3_p2_3','unk_ds3_p2_4','unk_ds3_p2_5','unk_ds3_p2_6'] + }, + [Game.Sekiro]: { + fields1: ['orientation','mask','layer1','layer2','uniformScale','unk_ds3_f1_6','columns','totalFrames','interpolateFrames','unk_ds3_f1_10','unk_ds3_f1_11','depthBlend','octagonal','unk_ds3_f1_14'], + fields2: ['unk_ds3_f2_0','unk_ds3_f2_1','unk_ds3_f2_2','unk_ds3_f2_3','unk_ds3_f2_4','bloomRed','bloomGreen','bloomBlue','bloomStrength','unk_ds3_f2_9','unk_ds3_f2_10','unk_ds3_f2_11','unk_ds3_f2_12','unk_ds3_f2_13','unkDistFadeClose0','unkDistFadeClose1','unkDistFadeFar0','unkDistFadeFar1','minDistance','maxDistance','unk_ds3_f2_20','unk_ds3_f2_21','unk_ds3_f2_22','unk_ds3_f2_23','unk_ds3_f2_24','unkDepthBlend1','unkDepthBlend2','unk_ds3_f2_27','unk_ds3_f2_28','unk_ds3_f2_29','shadowDarkness','unk_sdt_f2_31','unk_sdt_f2_32','specular','glossiness','lighting','unk_sdt_f2_36','unk_sdt_f2_37','unk_sdt_f2_38','unk_sdt_f2_39','unk_sdt_f2_40','unk_sdt_f2_41'], + properties1: ['blendMode','offsetX','offsetY','offsetZ','width','height','rotationX','rotationY','rotationZ','rotationSpeedX','rotationSpeedMultiplierX','rotationSpeedY','rotationSpeedMultiplierY','rotationSpeedZ','rotationSpeedMultiplierZ','color1','color2','color3','layersColor','layer1Color','layer2Color','alphaThreshold','frameIndex','frameIndexOffset','unk_ds3_p1_23','unk_ds3_p1_24','unk_ds3_p1_25','unk_ds3_p1_26','unk_ds3_p1_27','unk_ds3_p1_28','layer1SpeedU','layer1SpeedV','layer1OffsetU','layer1OffsetV','layer1ScaleU','layer1ScaleV','layer2SpeedU','layer2SpeedV','layer2OffsetU','layer2OffsetV','layer2ScaleU','layer2ScaleV'], + properties2: Game.DarkSouls3 + }, + [Game.EldenRing]: { + fields1: ['orientation','mask','layer1','layer2','uniformScale','unk_ds3_f1_6','columns','totalFrames','interpolateFrames','unk_ds3_f1_10','unk_ds3_f1_11','depthBlend','octagonal','unk_ds3_f1_14','unk_er_f1_14','unk_er_f1_15','unk_er_f1_16'], + fields2: ['unk_ds3_f2_0','unk_ds3_f2_1','unk_ds3_f2_2','unk_ds3_f2_3','unk_ds3_f2_4','bloomRed','bloomGreen','bloomBlue','bloomStrength','unk_ds3_f2_9','unk_ds3_f2_10','unk_ds3_f2_11','unk_ds3_f2_12','unk_ds3_f2_13','unkDistFadeClose0','unkDistFadeClose1','unkDistFadeFar0','unkDistFadeFar1','minDistance','maxDistance','unk_ds3_f2_20','unk_ds3_f2_21','unk_ds3_f2_22','unk_ds3_f2_23','unk_ds3_f2_24','unkDepthBlend1','unkDepthBlend2','unk_ds3_f2_27','unk_ds3_f2_28','unk_ds3_f2_29','shadowDarkness','unk_sdt_f2_31','unk_sdt_f2_32','specular','glossiness','lighting','unk_sdt_f2_36','unk_sdt_f2_37','unk_sdt_f2_38','unk_sdt_f2_39','unk_sdt_f2_40','unk_sdt_f2_41','unk_er_f2_42','unk_er_f2_43','unk_er_f2_44','unk_er_f2_45'], + properties1: Game.Sekiro, + properties2: Game.DarkSouls3 + }, + [Game.ArmoredCore6]: { + fields1: Game.EldenRing, + fields2: ['unk_ds3_f2_0','unk_ds3_f2_1','unk_ds3_f2_2','unk_ds3_f2_3','unk_ds3_f2_4','bloomRed','bloomGreen','bloomBlue','bloomStrength','unk_ds3_f2_9','unk_ds3_f2_10','unk_ds3_f2_11','unk_ds3_f2_12','unk_ds3_f2_13','unkDistFadeClose0','unkDistFadeClose1','unkDistFadeFar0','unkDistFadeFar1','minDistance','maxDistance','unk_ds3_f2_20','unk_ds3_f2_21','unk_ds3_f2_22','unk_ds3_f2_23','unk_ds3_f2_24','unkDepthBlend1','unkDepthBlend2','unk_ds3_f2_27','unk_ds3_f2_28','unk_ds3_f2_29','shadowDarkness','unk_sdt_f2_31','unk_sdt_f2_32','specular','glossiness','lighting','unk_sdt_f2_36','unk_sdt_f2_37','unk_sdt_f2_38','unk_sdt_f2_39','unk_sdt_f2_40','unk_sdt_f2_41','unk_er_f2_42','unk_er_f2_43','unk_er_f2_44','unk_er_f2_45','unk_ac6_f2_46'], + properties1: Game.Sekiro, + properties2: Game.DarkSouls3 + } + } + }, + [ActionType.Model]: { + props: { + orientation: { default: OrientationMode.LocalSouth, paths: {}, field: FieldType.Integer }, + scaleVariationX: { default: 1, paths: {}, field: FieldType.Float }, + scaleVariationY: { default: 1, paths: {}, field: FieldType.Float }, + scaleVariationZ: { default: 1, paths: {}, field: FieldType.Float }, + uniformScale: { default: false, paths: {}, field: FieldType.Boolean }, + columns: { default: 1, paths: {}, field: FieldType.Integer }, + totalFrames: { default: 1, paths: {}, field: FieldType.Integer }, + bloomRed: { default: 1, paths: {}, field: FieldType.Float }, + bloomGreen: { default: 1, paths: {}, field: FieldType.Float }, + bloomBlue: { default: 1, paths: {}, field: FieldType.Float }, + bloomStrength: { default: 0, paths: {}, field: FieldType.Float }, + minDistance: { default: -1, paths: {}, field: FieldType.Float }, + maxDistance: { default: -1, paths: {}, field: FieldType.Float }, + model: { default: 80201, paths: {}, field: FieldType.Integer }, + sizeX: { default: 1, paths: {} }, + sizeY: { default: 1, paths: {} }, + sizeZ: { default: 1, paths: {} }, + rotationX: { default: 0, paths: {} }, + rotationY: { default: 0, paths: {} }, + rotationZ: { default: 0, paths: {} }, + rotationSpeedX: { default: 0, paths: {} }, + rotationSpeedY: { default: 0, paths: {} }, + rotationSpeedZ: { default: 0, paths: {} }, + rotationSpeedMultiplierX: { default: 1, paths: {} }, + rotationSpeedMultiplierY: { default: 1, paths: {} }, + rotationSpeedMultiplierZ: { default: 1, paths: {} }, + blendMode: { default: BlendMode.Normal, paths: {}, field: FieldType.Integer }, + color1: { default: [1, 1, 1, 1], paths: {} }, + color2: { default: [1, 1, 1, 1], paths: {} }, + color3: { default: [1, 1, 1, 1], paths: {} }, + frameIndex: { default: 0, paths: {} }, + frameIndexOffset: { default: 0, paths: {} }, + offsetU: { default: 0, paths: {} }, + offsetV: { default: 0, paths: {} }, + speedU: { default: 0, paths: {} }, + speedMultiplierU: { default: 0, paths: {} }, + speedV: { default: 0, paths: {} }, + speedMultiplierV: { default: 0, paths: {} }, + rgbMultiplier: { default: 1, paths: {} }, + alphaMultiplier: { default: 1, paths: {} }, + unk_ds3_f1_9: { default: -2, paths: {}, field: FieldType.Integer }, + unk_ds3_f1_10: { default: -2, paths: {}, field: FieldType.Integer }, + unk_ds3_f1_11: { default: true, paths: {}, field: FieldType.Boolean }, + unk_ds3_f1_12: { default: true, paths: {}, field: FieldType.Boolean }, + unk_ds3_f1_13: { default: 1, paths: {}, field: FieldType.Integer }, + animation: { default: 0, paths: {}, field: FieldType.Integer }, + unk_ds3_f1_15: { default: 0, paths: {}, field: FieldType.Integer }, + loopAnimation: { default: 1, paths: {}, field: FieldType.Boolean }, + animationSpeed: { default: 1, paths: {}, field: FieldType.Float }, + unk_ds3_f1_18: { default: 0, paths: {}, field: FieldType.Integer }, + unk_ds3_f2_0: { default: 0, paths: {}, field: FieldType.Integer }, + unk_ds3_f2_1: { default: 0, paths: {}, field: FieldType.Integer }, + unk_ds3_f2_2: { default: 8, paths: {}, field: FieldType.Integer }, + unk_ds3_f2_3: { default: 0, paths: {}, field: FieldType.Integer }, + unk_ds3_f2_4: { default: 1, paths: {}, field: FieldType.Integer }, + unk_ds3_f2_9: { default: 0, paths: {}, field: FieldType.Integer }, + unk_ds3_f2_10: { default: 0, paths: {}, field: FieldType.Integer }, + unk_ds3_f2_11: { default: 0, paths: {}, field: FieldType.Integer }, + unk_ds3_f2_12: { default: 0, paths: {}, field: FieldType.Integer }, + unk_ds3_f2_13: { default: 0, paths: {}, field: FieldType.Integer }, + unkDistFadeClose0: { default: -1, paths: {}, field: FieldType.Float }, + unkDistFadeClose1: { default: -1, paths: {}, field: FieldType.Float }, + unkDistFadeFar0: { default: -1, paths: {}, field: FieldType.Float }, + unkDistFadeFar1: { default: -1, paths: {}, field: FieldType.Float }, + unk_ds3_f2_20: { default: 0, paths: {}, field: FieldType.Integer }, + unk_ds3_f2_21: { default: 0, paths: {}, field: FieldType.Integer }, + unk_ds3_f2_22: { default: 0, paths: {}, field: FieldType.Integer }, + unk_ds3_f2_23: { default: 0, paths: {}, field: FieldType.Integer }, + unk_ds3_f2_24: { default: 0, paths: {}, field: FieldType.Float }, + unkDepthBlend1: { default: 1, paths: {}, field: FieldType.Float }, + unkDepthBlend2: { default: 0, paths: {}, field: FieldType.Float }, + unk_ds3_f2_27: { default: 1, paths: {}, field: FieldType.Integer }, + unk_ds3_f2_28: { default: 0, paths: {}, field: FieldType.Integer }, + unk_ds3_p1_15: { default: 0, paths: {} }, + unk_ds3_p1_24: { default: 0, paths: {} }, + unk_ds3_p2_2: { default: 0, paths: {} }, + unk_ds3_p2_3: { default: [1, 1, 1, 1], paths: {} }, + unk_ds3_p2_4: { default: [1, 1, 1, 1], paths: {} }, + unk_ds3_p2_5: { default: [1, 1, 1, 1], paths: {} }, + unk_ds3_p2_6: { default: 0, paths: {} }, + unk_sdt_f2_29: { default: 0, paths: {}, field: FieldType.Float }, + unk_sdt_f2_30: { default: 0, paths: {}, field: FieldType.Float }, + unk_sdt_f2_31: { default: 0, paths: {}, field: FieldType.Integer }, + unk_sdt_f2_32: { default: 1, paths: {}, field: FieldType.Integer }, + unk_sdt_f2_33: { default: 0, paths: {}, field: FieldType.Integer }, + unk_sdt_f2_34: { default: 0, paths: {}, field: FieldType.Float }, + unk_sdt_f2_35: { default: -2, paths: {}, field: FieldType.Integer }, + unk_sdt_f2_36: { default: -2, paths: {}, field: FieldType.Integer }, + unk_sdt_f2_37: { default: 0, paths: {}, field: FieldType.Integer }, + unk_er_f1_17: { default: 1, paths: {}, field: FieldType.Integer }, + unk_er_f1_18: { default: 1, paths: {}, field: FieldType.Integer }, + unk_er_f1_19: { default: 0, paths: {}, field: FieldType.Integer }, + unk_ac6_f2_38: { default: 0, paths: {}, field: FieldType.Integer }, + }, + games: { + [Game.DarkSouls3]: { + fields1: ['orientation','model','scaleVariationX','scaleVariationY','scaleVariationZ','uniformScale','blendMode','columns','totalFrames','unk_ds3_f1_9','unk_ds3_f1_10','unk_ds3_f1_11','unk_ds3_f1_12','unk_ds3_f1_13','animation','unk_ds3_f1_15','loopAnimation','animationSpeed','unk_ds3_f1_18'], + fields2: ['unk_ds3_f2_0','unk_ds3_f2_1','unk_ds3_f2_2','unk_ds3_f2_3','unk_ds3_f2_4','bloomRed','bloomGreen','bloomBlue','bloomStrength','unk_ds3_f2_9','unk_ds3_f2_10','unk_ds3_f2_11','unk_ds3_f2_12','unk_ds3_f2_13','unkDistFadeClose0','unkDistFadeClose1','unkDistFadeFar0','unkDistFadeFar1','minDistance','maxDistance','unk_ds3_f2_20','unk_ds3_f2_21','unk_ds3_f2_22','unk_ds3_f2_23','unk_ds3_f2_24','unkDepthBlend1','unkDepthBlend2','unk_ds3_f2_27','unk_ds3_f2_28'], + properties1: ['sizeX','sizeY','sizeZ','rotationX','rotationY','rotationZ','rotationSpeedX','rotationSpeedMultiplierX','rotationSpeedY','rotationSpeedMultiplierY','rotationSpeedZ','rotationSpeedMultiplierZ','color1','color2','color3','unk_ds3_p1_15','frameIndex','frameIndexOffset','offsetU','offsetV','speedU','speedMultiplierU','speedV','speedMultiplierV','unk_ds3_p1_24'], + properties2: ['rgbMultiplier','alphaMultiplier','unk_ds3_p2_2','unk_ds3_p2_3','unk_ds3_p2_4','unk_ds3_p2_5','unk_ds3_p2_6'] + }, + [Game.Sekiro]: { + fields1: ['orientation','scaleVariationX','scaleVariationY','scaleVariationZ','uniformScale','columns','totalFrames','unk_ds3_f1_9','unk_ds3_f1_10','unk_ds3_f1_11','unk_ds3_f1_12','unk_ds3_f1_13','animation','unk_ds3_f1_15','loopAnimation','animationSpeed','unk_ds3_f1_18'], + fields2: ['unk_ds3_f2_0','unk_ds3_f2_1','unk_ds3_f2_2','unk_ds3_f2_3','unk_ds3_f2_4','bloomRed','bloomGreen','bloomBlue','bloomStrength','unk_ds3_f2_9','unk_ds3_f2_10','unk_ds3_f2_11','unk_ds3_f2_12','unk_ds3_f2_13','unkDistFadeClose0','unkDistFadeClose1','unkDistFadeFar0','unkDistFadeFar1','minDistance','maxDistance','unk_ds3_f2_20','unk_ds3_f2_21','unk_ds3_f2_22','unk_ds3_f2_23','unk_ds3_f2_24','unkDepthBlend1','unkDepthBlend2','unk_ds3_f2_27','unk_ds3_f2_28','unk_sdt_f2_29','unk_sdt_f2_30','unk_sdt_f2_31','unk_sdt_f2_32','unk_sdt_f2_33','unk_sdt_f2_34','unk_sdt_f2_35','unk_sdt_f2_36','unk_sdt_f2_37'], + properties1: ['model','sizeX','sizeY','sizeZ','rotationX','rotationY','rotationZ','rotationSpeedX','rotationSpeedMultiplierX','rotationSpeedY','rotationSpeedMultiplierY','rotationSpeedZ','rotationSpeedMultiplierZ','blendMode','color1','color2','color3','unk_ds3_p1_15','frameIndex','frameIndexOffset','offsetU','offsetV','speedU','speedMultiplierU','speedV','speedMultiplierV','unk_ds3_p1_24'], + properties2: Game.DarkSouls3 + }, + [Game.EldenRing]: { + fields1: ['orientation','scaleVariationX','scaleVariationY','scaleVariationZ','uniformScale','columns','totalFrames','unk_ds3_f1_9','unk_ds3_f1_10','unk_ds3_f1_11','unk_ds3_f1_12','unk_ds3_f1_13','animation','unk_ds3_f1_15','loopAnimation','animationSpeed','unk_ds3_f1_18','unk_er_f1_17','unk_er_f1_18','unk_er_f1_19'], + fields2: ['unk_ds3_f2_0','unk_ds3_f2_1','unk_ds3_f2_2','unk_ds3_f2_3','unk_ds3_f2_4','bloomRed','bloomGreen','bloomBlue','bloomStrength','unk_ds3_f2_9','unk_ds3_f2_10','unk_ds3_f2_11','unk_ds3_f2_12','unk_ds3_f2_13','unkDistFadeClose0','unkDistFadeClose1','unkDistFadeFar0','unkDistFadeFar1','minDistance','maxDistance','unk_ds3_f2_20','unk_ds3_f2_21','unk_ds3_f2_22','unk_ds3_f2_23','unk_ds3_f2_24','unkDepthBlend1','unkDepthBlend2','unk_ds3_f2_27','unk_ds3_f2_28','unk_sdt_f2_29','unk_sdt_f2_30','unk_sdt_f2_31','unk_sdt_f2_32','unk_sdt_f2_33','unk_sdt_f2_34','unk_sdt_f2_35','unk_sdt_f2_36','unk_sdt_f2_37','unk_ac6_f2_38'], + properties1: Game.Sekiro, + properties2: Game.DarkSouls3 + }, + [Game.ArmoredCore6]: { + fields1: Game.EldenRing, + fields2: ['unk_ds3_f2_0','unk_ds3_f2_1','unk_ds3_f2_2','unk_ds3_f2_3','unk_ds3_f2_4','bloomRed','bloomGreen','bloomBlue','bloomStrength','unk_ds3_f2_9','unk_ds3_f2_10','unk_ds3_f2_11','unk_ds3_f2_12','unk_ds3_f2_13','unkDistFadeClose0','unkDistFadeClose1','unkDistFadeFar0','unkDistFadeFar1','minDistance','maxDistance','unk_ds3_f2_20','unk_ds3_f2_21','unk_ds3_f2_22','unk_ds3_f2_23','unk_ds3_f2_24','unkDepthBlend1','unkDepthBlend2','unk_ds3_f2_27','unk_ds3_f2_28','unk_sdt_f2_29','unk_sdt_f2_30','unk_sdt_f2_31','unk_sdt_f2_32','unk_sdt_f2_33','unk_sdt_f2_34','unk_sdt_f2_35','unk_sdt_f2_36','unk_sdt_f2_37','unk_ac6_f2_38'], + properties1: Game.Sekiro, + properties2: Game.DarkSouls3 + } + } + }, + [ActionType.Tracer]: { + props: { + orientation: { default: TracerOrientationMode.LocalZ, paths: {}, field: FieldType.Integer }, + normalMap: { default: 0, paths: {}, field: FieldType.Integer }, + segmentInterval: { default: 0, paths: {}, field: FieldType.Float }, + segmentDuration: { default: 1, paths: {}, field: FieldType.Float }, + concurrentSegments: { default: 100, paths: {}, field: FieldType.Integer }, + columns: { default: 1, paths: {}, field: FieldType.Integer }, + totalFrames: { default: 1, paths: {}, field: FieldType.Integer }, + attachedUV: { default: 1, paths: {}, field: FieldType.Boolean }, + bloomRed: { default: 1, paths: {}, field: FieldType.Float }, + bloomGreen: { default: 1, paths: {}, field: FieldType.Float }, + bloomBlue: { default: 1, paths: {}, field: FieldType.Float }, + bloomStrength: { default: 0, paths: {}, field: FieldType.Float }, + minDistance: { default: -1, paths: {}, field: FieldType.Float }, + maxDistance: { default: -1, paths: {}, field: FieldType.Float }, + shadowDarkness: { default: 0, paths: {}, field: FieldType.Float }, + specular: { default: 0, paths: {}, field: FieldType.Integer }, + glossiness: { default: 0.25, paths: {}, field: FieldType.Float }, + lighting: { default: LightingMode.Unlit, paths: {}, field: FieldType.Integer }, + specularity: { default: 0.5, paths: {}, field: FieldType.Float }, + texture: { default: 1, paths: {}, field: FieldType.Integer }, + blendMode: { default: BlendMode.Normal, paths: {}, field: FieldType.Integer }, + width: { default: 1, paths: {} }, + widthMultiplier: { default: 1, paths: {} }, + color1: { default: [1, 1, 1, 1], paths: {} }, + color2: { default: [1, 1, 1, 1], paths: {} }, + color3: { default: [1, 1, 1, 1], paths: {} }, + alphaThreshold: { default: 0, paths: {} }, + frameIndex: { default: 0, paths: {} }, + frameIndexOffset: { default: 0, paths: {} }, + textureFraction: { default: 0.1, paths: {} }, + speedU: { default: 0, paths: {} }, + varianceV: { default: 0, paths: {} }, + rgbMultiplier: { default: 1, paths: {} }, + alphaMultiplier: { default: 1, paths: {} }, + unk_ds3_f1_7: { default: 0, paths: {}, field: FieldType.Integer }, + unk_ds3_f1_8: { default: 0, paths: {}, field: FieldType.Integer }, + unk_ds3_f1_9: { default: 0, paths: {}, field: FieldType.Float }, + unk_ds3_f1_13: { default: -1, paths: {}, field: FieldType.Integer }, + unk_ds3_f1_14: { default: -1, paths: {}, field: FieldType.Integer }, + unk_ds3_f1_15: { default: 0, paths: {}, field: FieldType.Integer }, + unk_ds3_f2_0: { default: 0, paths: {}, field: FieldType.Integer }, + unk_ds3_f2_1: { default: 0, paths: {}, field: FieldType.Boolean }, + unk_ds3_f2_2: { default: 8, paths: {}, field: FieldType.Integer }, + unk_ds3_f2_3: { default: 0, paths: {}, field: FieldType.Integer }, + unk_ds3_f2_4: { default: 1, paths: {}, field: FieldType.Integer }, + unk_ds3_f2_9: { default: 0, paths: {}, field: FieldType.Integer }, + unk_ds3_f2_10: { default: 0, paths: {}, field: FieldType.Integer }, + unk_ds3_f2_11: { default: 0, paths: {}, field: FieldType.Integer }, + unk_ds3_f2_12: { default: 0, paths: {}, field: FieldType.Integer }, + unk_ds3_f2_13: { default: 0, paths: {}, field: FieldType.Integer }, + unkDistFadeClose0: { default: -1, paths: {}, field: FieldType.Float }, + unkDistFadeClose1: { default: -1, paths: {}, field: FieldType.Float }, + unkDistFadeFar0: { default: -1, paths: {}, field: FieldType.Float }, + unkDistFadeFar1: { default: -1, paths: {}, field: FieldType.Float }, + unk_ds3_f2_20: { default: 0, paths: {}, field: FieldType.Integer }, + unk_ds3_f2_21: { default: 0, paths: {}, field: FieldType.Integer }, + unk_ds3_f2_22: { default: 0, paths: {}, field: FieldType.Integer }, + unk_ds3_f2_23: { default: 0, paths: {}, field: FieldType.Integer }, + unk_ds3_f2_24: { default: 0, paths: {}, field: FieldType.Integer }, + unkDepthBlend1: { default: 1, paths: {}, field: FieldType.Float }, + unkDepthBlend2: { default: 0, paths: {}, field: FieldType.Float }, + unk_ds3_f2_27: { default: 1, paths: {}, field: FieldType.Integer }, + unk_ds3_f2_28: { default: 0, paths: {}, field: FieldType.Integer }, + unk_ds3_f2_29: { default: 5, paths: {}, field: FieldType.Float }, + unk_ds3_p1_2: { default: 0, paths: {} }, + unk_ds3_p1_3: { default: 0, paths: {} }, + unk_ds3_p1_13: { default: -1, paths: {} }, + distortionIntensity: { default: 0, paths: {} }, + unk_ds3_p2_3: { default: [1, 1, 1, 1], paths: {} }, + unk_ds3_p2_4: { default: [1, 1, 1, 1], paths: {} }, + unk_ds3_p2_5: { default: [1, 1, 1, 1], paths: {} }, + unk_ds3_p2_6: { default: 0, paths: {} }, + unk_sdt_f2_31: { default: 0, paths: {}, field: FieldType.Integer }, + unk_sdt_f2_32: { default: 1, paths: {}, field: FieldType.Integer }, + unk_sdt_f2_36: { default: -2, paths: {}, field: FieldType.Integer }, + unk_sdt_f2_37: { default: 0, paths: {}, field: FieldType.Integer }, + unk_er_f1_14: { default: 1, paths: {}, field: FieldType.Integer }, + unk_er_f1_15: { default: 1, paths: {}, field: FieldType.Integer }, + unk_er_f1_16: { default: 0, paths: {}, field: FieldType.Integer }, + unk_er_f2_39: { default: 0, paths: {}, field: FieldType.Integer }, + }, + games: { + [Game.DarkSouls3]: { + fields1: ['orientation','texture','normalMap','blendMode','segmentInterval','segmentDuration','concurrentSegments','unk_ds3_f1_7','unk_ds3_f1_8','unk_ds3_f1_9','columns','totalFrames','attachedUV','unk_ds3_f1_13','unk_ds3_f1_14','unk_ds3_f1_15'], + fields2: ['unk_ds3_f2_0','unk_ds3_f2_1','unk_ds3_f2_2','unk_ds3_f2_3','unk_ds3_f2_4','bloomRed','bloomGreen','bloomBlue','bloomStrength','unk_ds3_f2_9','unk_ds3_f2_10','unk_ds3_f2_11','unk_ds3_f2_12','unk_ds3_f2_13','unkDistFadeClose0','unkDistFadeClose1','unkDistFadeFar0','unkDistFadeFar1','minDistance','maxDistance','unk_ds3_f2_20','unk_ds3_f2_21','unk_ds3_f2_22','unk_ds3_f2_23','unk_ds3_f2_24','unkDepthBlend1','unkDepthBlend2','unk_ds3_f2_27','unk_ds3_f2_28','unk_ds3_f2_29'], + properties1: ['width','widthMultiplier','unk_ds3_p1_2','unk_ds3_p1_3','color1','color2','color3','alphaThreshold','frameIndex','frameIndexOffset','textureFraction','speedU','varianceV','unk_ds3_p1_13'], + properties2: ['rgbMultiplier','alphaMultiplier','distortionIntensity','unk_ds3_p2_3','unk_ds3_p2_4','unk_ds3_p2_5','unk_ds3_p2_6'] + }, + [Game.Sekiro]: { + fields1: ['orientation','normalMap','segmentInterval','segmentDuration','concurrentSegments','unk_ds3_f1_7','unk_ds3_f1_8','unk_ds3_f1_9','columns','totalFrames','attachedUV','unk_ds3_f1_13','unk_ds3_f1_14','unk_ds3_f1_15'], + fields2: ['unk_ds3_f2_0','unk_ds3_f2_1','unk_ds3_f2_2','unk_ds3_f2_3','unk_ds3_f2_4','bloomRed','bloomGreen','bloomBlue','bloomStrength','unk_ds3_f2_9','unk_ds3_f2_10','unk_ds3_f2_11','unk_ds3_f2_12','unk_ds3_f2_13','unkDistFadeClose0','unkDistFadeClose1','unkDistFadeFar0','unkDistFadeFar1','minDistance','maxDistance','unk_ds3_f2_20','unk_ds3_f2_21','unk_ds3_f2_22','unk_ds3_f2_23','unk_ds3_f2_24','unkDepthBlend1','unkDepthBlend2','unk_ds3_f2_27','unk_ds3_f2_28','unk_ds3_f2_29','shadowDarkness','unk_sdt_f2_31','unk_sdt_f2_32','specular','glossiness','lighting','unk_sdt_f2_36','unk_sdt_f2_37','specularity'], + properties1: ['texture','blendMode','width','widthMultiplier','unk_ds3_p1_2','unk_ds3_p1_3','color1','color2','color3','alphaThreshold','frameIndex','frameIndexOffset','textureFraction','speedU','varianceV','unk_ds3_p1_13'], + properties2: Game.DarkSouls3 + }, + [Game.EldenRing]: { + fields1: ['orientation','normalMap','segmentInterval','segmentDuration','concurrentSegments','unk_ds3_f1_7','unk_ds3_f1_8','unk_ds3_f1_9','columns','totalFrames','attachedUV','unk_ds3_f1_13','unk_ds3_f1_14','unk_ds3_f1_15','unk_er_f1_14','unk_er_f1_15','unk_er_f1_16'], + fields2: ['unk_ds3_f2_0','unk_ds3_f2_1','unk_ds3_f2_2','unk_ds3_f2_3','unk_ds3_f2_4','bloomRed','bloomGreen','bloomBlue','bloomStrength','unk_ds3_f2_9','unk_ds3_f2_10','unk_ds3_f2_11','unk_ds3_f2_12','unk_ds3_f2_13','unkDistFadeClose0','unkDistFadeClose1','unkDistFadeFar0','unkDistFadeFar1','minDistance','maxDistance','unk_ds3_f2_20','unk_ds3_f2_21','unk_ds3_f2_22','unk_ds3_f2_23','unk_ds3_f2_24','unkDepthBlend1','unkDepthBlend2','unk_ds3_f2_27','unk_ds3_f2_28','unk_ds3_f2_29','shadowDarkness','unk_sdt_f2_31','unk_sdt_f2_32','specular','glossiness','lighting','unk_sdt_f2_36','unk_sdt_f2_37','specularity','unk_er_f2_39'], + properties1: Game.Sekiro, + properties2: Game.DarkSouls3 + }, + [Game.ArmoredCore6]: Game.EldenRing + } + }, + [ActionType.Distortion]: { + props: { + mode: { default: DistortionMode.NormalMap, paths: {}, field: FieldType.Integer }, + shape: { default: DistortionShape.Rectangle, paths: {}, field: FieldType.Integer }, + orientation: { default: OrientationMode.CameraPlane, paths: {}, field: FieldType.Integer }, + texture: { default: 0, paths: {}, field: FieldType.Integer }, + normalMap: { default: 0, paths: {}, field: FieldType.Integer }, + mask: { default: 0, paths: {}, field: FieldType.Integer }, + scaleVariationX: { default: 1, paths: {}, field: FieldType.Float }, + scaleVariationY: { default: 1, paths: {}, field: FieldType.Float }, + scaleVariationZ: { default: 1, paths: {}, field: FieldType.Float }, + uniformScale: { default: false, paths: {}, field: FieldType.Boolean }, + bloomRed: { default: 1, paths: {}, field: FieldType.Float }, + bloomGreen: { default: 1, paths: {}, field: FieldType.Float }, + bloomBlue: { default: 1, paths: {}, field: FieldType.Float }, + bloomStrength: { default: 0, paths: {}, field: FieldType.Float }, + minDistance: { default: -1, paths: {}, field: FieldType.Float }, + maxDistance: { default: -1, paths: {}, field: FieldType.Float }, + blendMode: { default: BlendMode.Normal, paths: {}, field: FieldType.Integer }, + offsetX: { default: 0, paths: {} }, + offsetY: { default: 0, paths: {} }, + offsetZ: { default: 0, paths: {} }, + sizeX: { default: 1, paths: {} }, + sizeY: { default: 1, paths: {} }, + sizeZ: { default: 1, paths: {} }, + color: { default: [1, 1, 1, 1], paths: {} }, + intensity: { default: 1, paths: {} }, + stirSpeed: { default: 1, paths: {} }, + radius: { default: 1, paths: {} }, + normalMapOffsetU: { default: 0, paths: {} }, + normalMapOffsetV: { default: 0, paths: {} }, + normalMapSpeedU: { default: 0, paths: {} }, + normalMapSpeedV: { default: 0, paths: {} }, + rgbMultiplier: { default: 1, paths: {} }, + alphaMultiplier: { default: 1, paths: {} }, + unk_ds3_f1_11: { default: -2, paths: {}, field: FieldType.Integer }, + unk_ds3_f1_12: { default: 0, paths: {}, field: FieldType.Integer }, + unk_ds3_f2_0: { default: 0, paths: {}, field: FieldType.Integer }, + unk_ds3_f2_1: { default: 0, paths: {}, field: FieldType.Integer }, + unk_ds3_f2_2: { default: 8, paths: {}, field: FieldType.Integer }, + unk_ds3_f2_3: { default: 1, paths: {}, field: FieldType.Float }, + unk_ds3_f2_4: { default: 0, paths: {}, field: FieldType.Integer }, + unk_ds3_f2_9: { default: 0, paths: {}, field: FieldType.Integer }, + unk_ds3_f2_10: { default: 0, paths: {}, field: FieldType.Integer }, + unk_ds3_f2_11: { default: 0, paths: {}, field: FieldType.Integer }, + unk_ds3_f2_12: { default: 0, paths: {}, field: FieldType.Integer }, + unk_ds3_f2_13: { default: 0, paths: {}, field: FieldType.Integer }, + unkDistFadeClose0: { default: -1, paths: {}, field: FieldType.Float }, + unkDistFadeClose1: { default: -1, paths: {}, field: FieldType.Float }, + unkDistFadeFar0: { default: -1, paths: {}, field: FieldType.Float }, + unkDistFadeFar1: { default: -1, paths: {}, field: FieldType.Float }, + unk_ds3_f2_20: { default: 1, paths: {}, field: FieldType.Integer }, + unk_ds3_f2_21: { default: 0, paths: {}, field: FieldType.Integer }, + unk_ds3_f2_22: { default: 0, paths: {}, field: FieldType.Integer }, + unk_ds3_f2_23: { default: 0, paths: {}, field: FieldType.Integer }, + unk_ds3_f2_24: { default: 0, paths: {}, field: FieldType.Integer }, + unkDepthBlend1: { default: 1, paths: {}, field: FieldType.Float }, + unkDepthBlend2: { default: 0, paths: {}, field: FieldType.Float }, + unk_ds3_f2_27: { default: 1, paths: {}, field: FieldType.Integer }, + unk_ds3_f2_28: { default: 0, paths: {}, field: FieldType.Integer }, + unk_ds3_f2_29: { default: 0, paths: {}, field: FieldType.Integer }, + unk_ds3_p1_7: { default: [1, 1, 1, 1], paths: {} }, + unk_ds3_p1_9: { default: 0, paths: {} }, + unk_ds3_p2_2: { default: 0, paths: {} }, + unk_ds3_p2_3: { default: [1, 1, 1, 1], paths: {} }, + unk_ds3_p2_4: { default: [1, 1, 1, 1], paths: {} }, + unk_ds3_p2_5: { default: [1, 1, 1, 1], paths: {} }, + unk_ds3_p2_6: { default: 0, paths: {} }, + unk_sdt_f2_30: { default: 0, paths: {}, field: FieldType.Integer }, + unk_sdt_f2_31: { default: 0, paths: {}, field: FieldType.Integer }, + unk_sdt_f2_32: { default: 0, paths: {}, field: FieldType.Integer }, + unk_sdt_f2_33: { default: 0, paths: {}, field: FieldType.Integer }, + unk_sdt_f2_34: { default: 0, paths: {}, field: FieldType.Integer }, + unk_sdt_f2_35: { default: -1, paths: {}, field: FieldType.Integer }, + unk_sdt_f2_36: { default: -2, paths: {}, field: FieldType.Integer }, + unk_sdt_f2_37: { default: 0, paths: {}, field: FieldType.Integer }, + unk_sdt_f2_38: { default: 0, paths: {}, field: FieldType.Integer }, + unk_er_f1_12: { default: 1, paths: {}, field: FieldType.Integer }, + unk_er_f1_13: { default: 1, paths: {}, field: FieldType.Integer }, + unk_er_p2_7: { default: 1, paths: {} }, + unk_er_p2_8: { default: 1, paths: {} }, + }, + games: { + [Game.DarkSouls3]: { + fields1: ['mode','shape','orientation','texture','normalMap','mask','blendMode','scaleVariationX','scaleVariationY','scaleVariationZ','uniformScale','unk_ds3_f1_11','unk_ds3_f1_12'], + fields2: ['unk_ds3_f2_0','unk_ds3_f2_1','unk_ds3_f2_2','unk_ds3_f2_3','unk_ds3_f2_4','bloomRed','bloomGreen','bloomBlue','bloomStrength','unk_ds3_f2_9','unk_ds3_f2_10','unk_ds3_f2_11','unk_ds3_f2_12','unk_ds3_f2_13','unkDistFadeClose0','unkDistFadeClose1','unkDistFadeFar0','unkDistFadeFar1','minDistance','maxDistance','unk_ds3_f2_20','unk_ds3_f2_21','unk_ds3_f2_22','unk_ds3_f2_23','unk_ds3_f2_24','unkDepthBlend1','unkDepthBlend2','unk_ds3_f2_27','unk_ds3_f2_28','unk_ds3_f2_29'], + properties1: ['offsetX','offsetY','offsetZ','sizeX','sizeY','sizeZ','color','unk_ds3_p1_7','intensity','unk_ds3_p1_9','stirSpeed','radius','normalMapOffsetU','normalMapOffsetV','normalMapSpeedU','normalMapSpeedV'], + properties2: ['rgbMultiplier','alphaMultiplier','unk_ds3_p2_2','unk_ds3_p2_3','unk_ds3_p2_4','unk_ds3_p2_5','unk_ds3_p2_6'] + }, + [Game.Sekiro]: { + fields1: ['mode','shape','orientation','texture','normalMap','mask','scaleVariationX','scaleVariationY','scaleVariationZ','uniformScale','unk_ds3_f1_11','unk_ds3_f1_12'], + fields2: ['unk_ds3_f2_0','unk_ds3_f2_1','unk_ds3_f2_2','unk_ds3_f2_3','unk_ds3_f2_4','bloomRed','bloomGreen','bloomBlue','bloomStrength','unk_ds3_f2_9','unk_ds3_f2_10','unk_ds3_f2_11','unk_ds3_f2_12','unk_ds3_f2_13','unkDistFadeClose0','unkDistFadeClose1','unkDistFadeFar0','unkDistFadeFar1','minDistance','maxDistance','unk_ds3_f2_20','unk_ds3_f2_21','unk_ds3_f2_22','unk_ds3_f2_23','unk_ds3_f2_24','unkDepthBlend1','unkDepthBlend2','unk_ds3_f2_27','unk_ds3_f2_28','unk_ds3_f2_29','unk_sdt_f2_30','unk_sdt_f2_31','unk_sdt_f2_32','unk_sdt_f2_33','unk_sdt_f2_34','unk_sdt_f2_35','unk_sdt_f2_36','unk_sdt_f2_37','unk_sdt_f2_38'], + properties1: ['blendMode','offsetX','offsetY','offsetZ','sizeX','sizeY','sizeZ','color','unk_ds3_p1_7','intensity','unk_ds3_p1_9','stirSpeed','radius','normalMapOffsetU','normalMapOffsetV','normalMapSpeedU','normalMapSpeedV'], + properties2: Game.DarkSouls3 + }, + [Game.EldenRing]: { + fields1: ['mode','shape','orientation','texture','normalMap','mask','scaleVariationX','scaleVariationY','scaleVariationZ','uniformScale','unk_ds3_f1_11','unk_ds3_f1_12','unk_er_f1_12','unk_er_f1_13'], + fields2: Game.Sekiro, + properties1: Game.Sekiro, + properties2: ['rgbMultiplier','alphaMultiplier','unk_ds3_p2_2','unk_ds3_p2_3','unk_ds3_p2_4','unk_ds3_p2_5','unk_ds3_p2_6','unk_er_p2_7','unk_er_p2_8'] + }, + [Game.ArmoredCore6]: Game.EldenRing + } + }, + [ActionType.RadialBlur]: { + props: { + uniformScale: { default: false, paths: {}, field: FieldType.Boolean }, + iterations: { default: 1, paths: {}, field: FieldType.Integer }, + bloomRed: { default: 1, paths: {}, field: FieldType.Float }, + bloomGreen: { default: 1, paths: {}, field: FieldType.Float }, + bloomBlue: { default: 1, paths: {}, field: FieldType.Float }, + bloomStrength: { default: 0, paths: {}, field: FieldType.Float }, + minDistance: { default: -1, paths: {}, field: FieldType.Float }, + maxDistance: { default: -1, paths: {}, field: FieldType.Float }, + blendMode: { default: BlendMode.Normal, paths: {}, field: FieldType.Integer }, + mask: { default: 1, paths: {}, field: FieldType.Integer }, + offsetX: { default: 0, paths: {} }, + offsetY: { default: 0, paths: {} }, + offsetZ: { default: 0, paths: {} }, + width: { default: 1, paths: {} }, + height: { default: 1, paths: {} }, + color: { default: [1, 1, 1, 1], paths: {} }, + blurRadius: { default: 0.5, paths: {} }, + rgbMultiplier: { default: 1, paths: {} }, + alphaMultiplier: { default: 1, paths: {} }, + unk_ds3_f1_4: { default: 0, paths: {}, field: FieldType.Integer }, + unk_ds3_f2_0: { default: 0, paths: {}, field: FieldType.Integer }, + unk_ds3_f2_1: { default: 0, paths: {}, field: FieldType.Integer }, + unk_ds3_f2_2: { default: 8, paths: {}, field: FieldType.Integer }, + unk_ds3_f2_3: { default: 1, paths: {}, field: FieldType.Float }, + unk_ds3_f2_4: { default: 0, paths: {}, field: FieldType.Integer }, + unk_ds3_f2_9: { default: 0, paths: {}, field: FieldType.Integer }, + unk_ds3_f2_10: { default: 0, paths: {}, field: FieldType.Integer }, + unk_ds3_f2_11: { default: 0, paths: {}, field: FieldType.Integer }, + unk_ds3_f2_12: { default: 0, paths: {}, field: FieldType.Integer }, + unk_ds3_f2_13: { default: 0, paths: {}, field: FieldType.Integer }, + unkDistFadeClose0: { default: -1, paths: {}, field: FieldType.Float }, + unkDistFadeClose1: { default: -1, paths: {}, field: FieldType.Float }, + unkDistFadeFar0: { default: -1, paths: {}, field: FieldType.Float }, + unkDistFadeFar1: { default: -1, paths: {}, field: FieldType.Float }, + unk_ds3_f2_20: { default: 0.5, paths: {}, field: FieldType.Float }, + unk_ds3_f2_21: { default: 1, paths: {}, field: FieldType.Integer }, + unk_ds3_f2_22: { default: 0, paths: {}, field: FieldType.Integer }, + unk_ds3_f2_23: { default: 0, paths: {}, field: FieldType.Integer }, + unk_ds3_f2_24: { default: 0, paths: {}, field: FieldType.Integer }, + unkDepthBlend1: { default: 1, paths: {}, field: FieldType.Float }, + unkDepthBlend2: { default: 0, paths: {}, field: FieldType.Float }, + unk_ds3_f2_27: { default: 1, paths: {}, field: FieldType.Integer }, + unk_ds3_f2_28: { default: 0, paths: {}, field: FieldType.Integer }, + unk_ds3_f2_29: { default: 0, paths: {}, field: FieldType.Float }, + unk_ds3_p1_6: { default: [1, 1, 1, 1], paths: {} }, + unk_ds3_p2_2: { default: 0, paths: {} }, + unk_ds3_p2_3: { default: [1, 1, 1, 1], paths: {} }, + unk_ds3_p2_4: { default: [1, 1, 1, 1], paths: {} }, + unk_ds3_p2_5: { default: [1, 1, 1, 1], paths: {} }, + unk_ds3_p2_6: { default: 0, paths: {} }, + unk_sdt_f2_30: { default: 0, paths: {}, field: FieldType.Float }, + unk_er_f1_3: { default: 1, paths: {}, field: FieldType.Integer }, + unk_er_f1_4: { default: 1, paths: {}, field: FieldType.Integer }, + }, + games: { + [Game.DarkSouls3]: { + fields1: ['blendMode','mask','uniformScale','iterations','unk_ds3_f1_4'], + fields2: ['unk_ds3_f2_0','unk_ds3_f2_1','unk_ds3_f2_2','unk_ds3_f2_3','unk_ds3_f2_4','bloomRed','bloomGreen','bloomBlue','bloomStrength','unk_ds3_f2_9','unk_ds3_f2_10','unk_ds3_f2_11','unk_ds3_f2_12','unk_ds3_f2_13','unkDistFadeClose0','unkDistFadeClose1','unkDistFadeFar0','unkDistFadeFar1','minDistance','maxDistance','unk_ds3_f2_20','unk_ds3_f2_21','unk_ds3_f2_22','unk_ds3_f2_23','unk_ds3_f2_24','unkDepthBlend1','unkDepthBlend2','unk_ds3_f2_27','unk_ds3_f2_28','unk_ds3_f2_29'], + properties1: ['offsetX','offsetY','offsetZ','width','height','color','unk_ds3_p1_6','blurRadius'], + properties2: ['rgbMultiplier','alphaMultiplier','unk_ds3_p2_2','unk_ds3_p2_3','unk_ds3_p2_4','unk_ds3_p2_5','unk_ds3_p2_6'] + }, + [Game.Sekiro]: { + fields1: ['uniformScale','iterations','unk_ds3_f1_4'], + fields2: ['unk_ds3_f2_0','unk_ds3_f2_1','unk_ds3_f2_2','unk_ds3_f2_3','unk_ds3_f2_4','bloomRed','bloomGreen','bloomBlue','bloomStrength','unk_ds3_f2_9','unk_ds3_f2_10','unk_ds3_f2_11','unk_ds3_f2_12','unk_ds3_f2_13','unkDistFadeClose0','unkDistFadeClose1','unkDistFadeFar0','unkDistFadeFar1','minDistance','maxDistance','unk_ds3_f2_20','unk_ds3_f2_21','unk_ds3_f2_22','unk_ds3_f2_23','unk_ds3_f2_24','unkDepthBlend1','unkDepthBlend2','unk_ds3_f2_27','unk_ds3_f2_28','unk_ds3_f2_29','unk_sdt_f2_30'], + properties1: ['blendMode','mask','offsetX','offsetY','offsetZ','width','height','color','unk_ds3_p1_6','blurRadius'], + properties2: Game.DarkSouls3 + }, + [Game.EldenRing]: { + fields1: ['uniformScale','iterations','unk_ds3_f1_4','unk_er_f1_3','unk_er_f1_4'], + fields2: Game.Sekiro, + properties1: Game.Sekiro, + properties2: Game.DarkSouls3 + }, + [Game.ArmoredCore6]: Game.EldenRing + } + }, + [ActionType.PointLight]: { + props: { + diffuseColor: { default: [1, 1, 1, 1], paths: {} }, + specularColor: { default: [1, 1, 1, 1], paths: {} }, + radius: { default: 10, paths: {} }, + diffuseMultiplier: { default: 1, paths: {} }, + specularMultiplier: { default: 1, paths: {} }, + jitterAndFlicker: { default: false, paths: {}, field: FieldType.Boolean }, + jitterAcceleration: { default: 1, paths: {}, field: FieldType.Float }, + jitterX: { default: 0, paths: {}, field: FieldType.Float }, + jitterY: { default: 0, paths: {}, field: FieldType.Float }, + jitterZ: { default: 0, paths: {}, field: FieldType.Float }, + flickerIntervalMin: { default: 0, paths: {}, field: FieldType.Float }, + flickerIntervalMax: { default: 1, paths: {}, field: FieldType.Float }, + flickerBrightness: { default: 0.5, paths: {}, field: FieldType.Float }, + shadows: { default: false, paths: {}, field: FieldType.Boolean }, + separateSpecular: { default: false, paths: {}, field: FieldType.Boolean }, + fadeOutTime: { default: 0, paths: {}, field: FieldType.Integer }, + shadowDarkness: { default: 1, paths: {}, field: FieldType.Float }, + volumeDensity: { default: 0, paths: {}, field: FieldType.Float }, + phaseFunction: { default: true, paths: {}, field: FieldType.Boolean }, + asymmetryParam: { default: 0.75, paths: {}, field: FieldType.Float }, + falloffExponent: { default: 1, paths: {}, field: FieldType.Float }, + unk_ds3_f1_0: { default: 0, paths: {}, field: FieldType.Integer }, + unk_ds3_f1_1: { default: 0, paths: {}, field: FieldType.Float }, + unk_ds3_f2_0: { default: 0, paths: {}, field: FieldType.Integer }, + unk_ds3_f2_3: { default: 0, paths: {}, field: FieldType.Float }, + unk_ds3_f2_12: { default: 1, paths: {}, field: FieldType.Float }, + unk_ds3_f2_15: { default: false, paths: {}, field: FieldType.Boolean }, + unk_ds3_f2_16: { default: 2, paths: {}, field: FieldType.Integer }, + unk_ds3_f2_17: { default: true, paths: {}, field: FieldType.Boolean }, + unk_ds3_f2_18: { default: 0, paths: {}, field: FieldType.Float }, + unk_ds3_f2_19: { default: 0, paths: {}, field: FieldType.Float }, + unk_ds3_f2_20: { default: 0, paths: {}, field: FieldType.Float }, + unk_ds3_f2_21: { default: 0, paths: {}, field: FieldType.Integer }, + unk_ds3_f2_22: { default: 100, paths: {}, field: FieldType.Integer }, + unk_ds3_f2_23: { default: 0, paths: {}, field: FieldType.Integer }, + unk_ds3_f2_24: { default: 0, paths: {}, field: FieldType.Integer }, + unk_ds3_p1_3: { default: 0, paths: {} }, + unk_ds3_p1_4: { default: 0, paths: {} }, + unk_ds3_p1_5: { default: 0, paths: {} }, + unk_ds3_p1_6: { default: 0, paths: {} }, + unk_ds3_p1_7: { default: 10, paths: {} }, + unk_ds3_p1_8: { default: 10, paths: {} }, + unk_ds3_p1_9: { default: 10, paths: {} }, + unk_ds3_p2_0: { default: 1, paths: {} }, + unk_ds3_p2_1: { default: 1, paths: {} }, + unk_sdt_f2_25: { default: 0, paths: {}, field: FieldType.Float }, + unk_sdt_p2_2: { default: 1, paths: {} }, + unk_er_f2_29: { default: 1, paths: {}, field: FieldType.Integer }, + unk_er_f2_30: { default: 1, paths: {}, field: FieldType.Float }, + unk_er_f2_31: { default: 1, paths: {}, field: FieldType.Integer }, + unk_er_f2_32: { default: 0, paths: {}, field: FieldType.Integer }, + }, + games: { + [Game.DarkSouls3]: { + fields1: ['unk_ds3_f1_0','unk_ds3_f1_1'], + fields2: ['unk_ds3_f2_0','jitterAndFlicker','jitterAcceleration','unk_ds3_f2_3','jitterX','jitterY','jitterZ','flickerIntervalMin','flickerIntervalMax','flickerBrightness','shadows','separateSpecular','unk_ds3_f2_12','fadeOutTime','shadowDarkness','unk_ds3_f2_15','unk_ds3_f2_16','unk_ds3_f2_17','unk_ds3_f2_18','unk_ds3_f2_19','unk_ds3_f2_20','unk_ds3_f2_21','unk_ds3_f2_22','unk_ds3_f2_23','unk_ds3_f2_24'], + properties1: ['diffuseColor','specularColor','radius','unk_ds3_p1_3','unk_ds3_p1_4','unk_ds3_p1_5','unk_ds3_p1_6','unk_ds3_p1_7','unk_ds3_p1_8','unk_ds3_p1_9'], + properties2: ['unk_ds3_p2_0','unk_ds3_p2_1'] + }, + [Game.Sekiro]: { + fields1: Game.DarkSouls3, + fields2: ['unk_ds3_f2_0','jitterAndFlicker','jitterAcceleration','unk_ds3_f2_3','jitterX','jitterY','jitterZ','flickerIntervalMin','flickerIntervalMax','flickerBrightness','shadows','separateSpecular','fadeOutTime','shadowDarkness','unk_ds3_f2_15','unk_ds3_f2_16','unk_ds3_f2_17','unk_ds3_f2_18','unk_ds3_f2_19','unk_ds3_f2_20','unk_ds3_f2_21','unk_ds3_f2_22','unk_ds3_f2_23','unk_ds3_f2_24','volumeDensity','unk_sdt_f2_25','phaseFunction','asymmetryParam','falloffExponent'], + properties1: Game.DarkSouls3, + properties2: ['unk_ds3_p2_0','unk_ds3_p2_1','unk_sdt_p2_2','diffuseMultiplier','specularMultiplier'] + }, + [Game.EldenRing]: { + fields1: Game.DarkSouls3, + fields2: ['unk_ds3_f2_0','jitterAndFlicker','jitterAcceleration','unk_ds3_f2_3','jitterX','jitterY','jitterZ','flickerIntervalMin','flickerIntervalMax','flickerBrightness','shadows','separateSpecular','fadeOutTime','shadowDarkness','unk_ds3_f2_15','unk_ds3_f2_16','unk_ds3_f2_17','unk_ds3_f2_18','unk_ds3_f2_19','unk_ds3_f2_20','unk_ds3_f2_21','unk_ds3_f2_22','unk_ds3_f2_23','unk_ds3_f2_24','volumeDensity','unk_sdt_f2_25','phaseFunction','asymmetryParam','falloffExponent','unk_er_f2_29','unk_er_f2_30','unk_er_f2_31','unk_er_f2_32'], + properties1: Game.DarkSouls3, + properties2: Game.Sekiro + }, + [Game.ArmoredCore6]: Game.EldenRing + } + }, + [ActionType.Unk701]: { + props: { + unk_er_p1_0: { default: 1, paths: {} }, + }, + games: { + [Game.EldenRing]: { + properties1: ['unk_er_p1_0'] + }, + [Game.ArmoredCore6]: Game.EldenRing + } + }, + [ActionType.NodeWindSpeed]: { + props: { + speed: { default: 0, paths: {} }, + speedMultiplier: { default: 1, paths: {} }, + enabled: { default: true, paths: {}, field: FieldType.Boolean }, + }, + games: { + [Game.DarkSouls3]: -2, + [Game.Sekiro]: { + fields1: ['enabled'], + properties1: ['speed','speedMultiplier'] + }, + [Game.EldenRing]: Game.Sekiro, + [Game.ArmoredCore6]: Game.Sekiro + } + }, + [ActionType.ParticleWindSpeed]: { + props: { + speed: { default: 0, paths: {} }, + speedMultiplier: { default: 1, paths: {} }, + enabled: { default: true, paths: {}, field: FieldType.Boolean }, + unk_sdt_f1_1: { default: 0, paths: {}, field: FieldType.Integer }, + }, + games: { + [Game.DarkSouls3]: -2, + [Game.Sekiro]: { + fields1: ['enabled','unk_sdt_f1_1'], + properties1: ['speed','speedMultiplier'] + }, + [Game.EldenRing]: Game.Sekiro, + [Game.ArmoredCore6]: Game.Sekiro + } + }, + [ActionType.NodeWindAcceleration]: { + props: { + acceleration: { default: 0, paths: {} }, + accelerationMultiplier: { default: 1, paths: {} }, + enabled: { default: true, paths: {}, field: FieldType.Boolean }, + }, + games: { + [Game.DarkSouls3]: -2, + [Game.Sekiro]: { + fields1: ['enabled'], + properties1: ['acceleration','accelerationMultiplier'] + }, + [Game.EldenRing]: Game.Sekiro, + [Game.ArmoredCore6]: Game.Sekiro + } + }, + [ActionType.ParticleWindAcceleration]: { + props: { + acceleration: { default: 0, paths: {} }, + accelerationMultiplier: { default: 1, paths: {} }, + enabled: { default: true, paths: {}, field: FieldType.Boolean }, + unk_sdt_f1_1: { default: 0, paths: {}, field: FieldType.Integer }, + }, + games: { + [Game.DarkSouls3]: -2, + [Game.Sekiro]: { + fields1: ['enabled','unk_sdt_f1_1'], + properties1: ['acceleration','accelerationMultiplier'] + }, + [Game.EldenRing]: Game.Sekiro, + [Game.ArmoredCore6]: Game.Sekiro + } + }, + [ActionType.Unk800]: { + props: { + unk_ac6_f1_0: { default: 1, paths: {}, field: FieldType.Float }, + unk_ac6_f1_1: { default: 0.2, paths: {}, field: FieldType.Float }, + unk_ac6_f1_2: { default: 0.25, paths: {}, field: FieldType.Float }, + }, + games: { + [Game.ArmoredCore6]: { + fields1: ['unk_ac6_f1_0','unk_ac6_f1_1','unk_ac6_f1_2'] + } + } + }, + [ActionType.DynamicTracer]: { + props: { + orientation: { default: TracerOrientationMode.LocalZ, paths: {}, field: FieldType.Integer }, + normalMap: { default: 0, paths: {}, field: FieldType.Integer }, + segmentInterval: { default: 0, paths: {}, field: FieldType.Float }, + segmentDuration: { default: 1, paths: {}, field: FieldType.Float }, + concurrentSegments: { default: 100, paths: {}, field: FieldType.Integer }, + columns: { default: 1, paths: {}, field: FieldType.Integer }, + totalFrames: { default: 1, paths: {}, field: FieldType.Integer }, + attachedUV: { default: 1, paths: {}, field: FieldType.Boolean }, + bloomRed: { default: 1, paths: {}, field: FieldType.Float }, + bloomGreen: { default: 1, paths: {}, field: FieldType.Float }, + bloomBlue: { default: 1, paths: {}, field: FieldType.Float }, + bloomStrength: { default: 0, paths: {}, field: FieldType.Float }, + minDistance: { default: -1, paths: {}, field: FieldType.Float }, + maxDistance: { default: -1, paths: {}, field: FieldType.Float }, + shadowDarkness: { default: 0, paths: {}, field: FieldType.Float }, + specular: { default: 0, paths: {}, field: FieldType.Integer }, + glossiness: { default: 0.25, paths: {}, field: FieldType.Float }, + lighting: { default: LightingMode.Unlit, paths: {}, field: FieldType.Integer }, + specularity: { default: 0.5, paths: {}, field: FieldType.Float }, + texture: { default: 1, paths: {}, field: FieldType.Integer }, + blendMode: { default: BlendMode.Normal, paths: {}, field: FieldType.Integer }, + width: { default: 1, paths: {} }, + widthMultiplier: { default: 1, paths: {} }, + color1: { default: [1, 1, 1, 1], paths: {} }, + color2: { default: [1, 1, 1, 1], paths: {} }, + color3: { default: [1, 1, 1, 1], paths: {} }, + alphaThreshold: { default: 0, paths: {} }, + frameIndex: { default: 0, paths: {} }, + frameIndexOffset: { default: 0, paths: {} }, + textureFraction: { default: 0.1, paths: {} }, + speedU: { default: 0, paths: {} }, + varianceV: { default: 0, paths: {} }, + rgbMultiplier: { default: 1, paths: {} }, + alphaMultiplier: { default: 1, paths: {} }, + unk_ds3_f1_7: { default: 0, paths: {}, field: FieldType.Integer }, + unk_ds3_f1_8: { default: 0, paths: {}, field: FieldType.Integer }, + unk_ds3_f1_9: { default: 0, paths: {}, field: FieldType.Float }, + unk_ds3_f1_13: { default: -1, paths: {}, field: FieldType.Integer }, + unk_ds3_f1_14: { default: -1, paths: {}, field: FieldType.Integer }, + unk_ds3_f1_15: { default: 0, paths: {}, field: FieldType.Integer }, + unk_ds3_f2_0: { default: 0, paths: {}, field: FieldType.Integer }, + unk_ds3_f2_1: { default: 0, paths: {}, field: FieldType.Boolean }, + unk_ds3_f2_2: { default: 8, paths: {}, field: FieldType.Integer }, + unk_ds3_f2_3: { default: 0, paths: {}, field: FieldType.Integer }, + unk_ds3_f2_4: { default: 1, paths: {}, field: FieldType.Integer }, + unk_ds3_f2_9: { default: 0, paths: {}, field: FieldType.Integer }, + unk_ds3_f2_10: { default: 0, paths: {}, field: FieldType.Integer }, + unk_ds3_f2_11: { default: 0, paths: {}, field: FieldType.Integer }, + unk_ds3_f2_12: { default: 0, paths: {}, field: FieldType.Integer }, + unk_ds3_f2_13: { default: 0, paths: {}, field: FieldType.Integer }, + unkDistFadeClose0: { default: -1, paths: {}, field: FieldType.Float }, + unkDistFadeClose1: { default: -1, paths: {}, field: FieldType.Float }, + unkDistFadeFar0: { default: -1, paths: {}, field: FieldType.Float }, + unkDistFadeFar1: { default: -1, paths: {}, field: FieldType.Float }, + unk_ds3_f2_20: { default: 0, paths: {}, field: FieldType.Integer }, + unk_ds3_f2_21: { default: 0, paths: {}, field: FieldType.Integer }, + unk_ds3_f2_22: { default: 0, paths: {}, field: FieldType.Integer }, + unk_ds3_f2_23: { default: 0, paths: {}, field: FieldType.Integer }, + unk_ds3_f2_24: { default: 0, paths: {}, field: FieldType.Integer }, + unkDepthBlend1: { default: 1, paths: {}, field: FieldType.Float }, + unkDepthBlend2: { default: 0, paths: {}, field: FieldType.Float }, + unk_ds3_f2_27: { default: 1, paths: {}, field: FieldType.Integer }, + unk_ds3_f2_28: { default: 0, paths: {}, field: FieldType.Integer }, + unk_ds3_f2_29: { default: 5, paths: {}, field: FieldType.Float }, + unk_ds3_p1_2: { default: 0, paths: {} }, + unk_ds3_p1_3: { default: 0, paths: {} }, + unk_ds3_p1_13: { default: -1, paths: {} }, + distortionIntensity: { default: 0, paths: {} }, + unk_ds3_p2_3: { default: [1, 1, 1, 1], paths: {} }, + unk_ds3_p2_4: { default: [1, 1, 1, 1], paths: {} }, + unk_ds3_p2_5: { default: [1, 1, 1, 1], paths: {} }, + unk_ds3_p2_6: { default: 0, paths: {} }, + unk_sdt_f2_31: { default: 0, paths: {}, field: FieldType.Integer }, + unk_sdt_f2_32: { default: 1, paths: {}, field: FieldType.Integer }, + unk_sdt_f2_36: { default: -2, paths: {}, field: FieldType.Integer }, + unk_sdt_f2_37: { default: 0, paths: {}, field: FieldType.Integer }, + unk_er_f1_18: { default: 1, paths: {}, field: FieldType.Integer }, + unk_er_f1_19: { default: 1, paths: {}, field: FieldType.Integer }, + unk_er_f1_20: { default: 0, paths: {}, field: FieldType.Integer }, + unk_er_f1_21: { default: 0, paths: {}, field: FieldType.Integer }, + unk_er_f2_39: { default: 0, paths: {}, field: FieldType.Integer }, + unk_er_f2_40: { default: 1, paths: {}, field: FieldType.Float }, + unk_sdt_f1_14: { default: 1, paths: {}, field: FieldType.Integer }, + unk_sdt_f1_15: { default: 1, paths: {}, field: FieldType.Float }, + unk_sdt_f1_16: { default: 1, paths: {}, field: FieldType.Float }, + unk_sdt_f1_17: { default: 1, paths: {}, field: FieldType.Float }, + }, + games: { + [Game.DarkSouls3]: { + fields1: ['orientation','texture','normalMap','blendMode','segmentInterval','segmentDuration','concurrentSegments','unk_ds3_f1_7','unk_ds3_f1_8','unk_ds3_f1_9','columns','totalFrames','attachedUV','unk_ds3_f1_13','unk_ds3_f1_14','unk_ds3_f1_15'], + fields2: ['unk_ds3_f2_0','unk_ds3_f2_1','unk_ds3_f2_2','unk_ds3_f2_3','unk_ds3_f2_4','bloomRed','bloomGreen','bloomBlue','bloomStrength','unk_ds3_f2_9','unk_ds3_f2_10','unk_ds3_f2_11','unk_ds3_f2_12','unk_ds3_f2_13','unkDistFadeClose0','unkDistFadeClose1','unkDistFadeFar0','unkDistFadeFar1','minDistance','maxDistance','unk_ds3_f2_20','unk_ds3_f2_21','unk_ds3_f2_22','unk_ds3_f2_23','unk_ds3_f2_24','unkDepthBlend1','unkDepthBlend2','unk_ds3_f2_27','unk_ds3_f2_28','unk_ds3_f2_29'], + properties1: ['width','widthMultiplier','unk_ds3_p1_2','unk_ds3_p1_3','color1','color2','color3','alphaThreshold','frameIndex','frameIndexOffset','textureFraction','speedU','varianceV','unk_ds3_p1_13'], + properties2: ['rgbMultiplier','alphaMultiplier','distortionIntensity','unk_ds3_p2_3','unk_ds3_p2_4','unk_ds3_p2_5','unk_ds3_p2_6'] + }, + [Game.Sekiro]: { + fields1: ['orientation','normalMap','segmentInterval','segmentDuration','concurrentSegments','unk_ds3_f1_7','unk_ds3_f1_8','unk_ds3_f1_9','columns','totalFrames','attachedUV','unk_ds3_f1_13','unk_ds3_f1_14','unk_ds3_f1_15','unk_sdt_f1_14','unk_sdt_f1_15','unk_sdt_f1_16','unk_sdt_f1_17'], + fields2: ['unk_ds3_f2_0','unk_ds3_f2_1','unk_ds3_f2_2','unk_ds3_f2_3','unk_ds3_f2_4','bloomRed','bloomGreen','bloomBlue','bloomStrength','unk_ds3_f2_9','unk_ds3_f2_10','unk_ds3_f2_11','unk_ds3_f2_12','unk_ds3_f2_13','unkDistFadeClose0','unkDistFadeClose1','unkDistFadeFar0','unkDistFadeFar1','minDistance','maxDistance','unk_ds3_f2_20','unk_ds3_f2_21','unk_ds3_f2_22','unk_ds3_f2_23','unk_ds3_f2_24','unkDepthBlend1','unkDepthBlend2','unk_ds3_f2_27','unk_ds3_f2_28','unk_ds3_f2_29','shadowDarkness','unk_sdt_f2_31','unk_sdt_f2_32','specular','glossiness','lighting','unk_sdt_f2_36','unk_sdt_f2_37','specularity'], + properties1: ['texture','blendMode','width','widthMultiplier','unk_ds3_p1_2','unk_ds3_p1_3','color1','color2','color3','alphaThreshold','frameIndex','frameIndexOffset','textureFraction','speedU','varianceV','unk_ds3_p1_13'], + properties2: Game.DarkSouls3 + }, + [Game.EldenRing]: { + fields1: ['orientation','normalMap','segmentInterval','segmentDuration','concurrentSegments','unk_ds3_f1_7','unk_ds3_f1_8','unk_ds3_f1_9','columns','totalFrames','attachedUV','unk_ds3_f1_13','unk_ds3_f1_14','unk_ds3_f1_15','unk_sdt_f1_14','unk_sdt_f1_15','unk_sdt_f1_16','unk_sdt_f1_17','unk_er_f1_18','unk_er_f1_19','unk_er_f1_20','unk_er_f1_21'], + fields2: ['unk_ds3_f2_0','unk_ds3_f2_1','unk_ds3_f2_2','unk_ds3_f2_3','unk_ds3_f2_4','bloomRed','bloomGreen','bloomBlue','bloomStrength','unk_ds3_f2_9','unk_ds3_f2_10','unk_ds3_f2_11','unk_ds3_f2_12','unk_ds3_f2_13','unkDistFadeClose0','unkDistFadeClose1','unkDistFadeFar0','unkDistFadeFar1','minDistance','maxDistance','unk_ds3_f2_20','unk_ds3_f2_21','unk_ds3_f2_22','unk_ds3_f2_23','unk_ds3_f2_24','unkDepthBlend1','unkDepthBlend2','unk_ds3_f2_27','unk_ds3_f2_28','unk_ds3_f2_29','shadowDarkness','unk_sdt_f2_31','unk_sdt_f2_32','specular','glossiness','lighting','unk_sdt_f2_36','unk_sdt_f2_37','specularity','unk_er_f2_39','unk_er_f2_40'], + properties1: Game.Sekiro, + properties2: Game.DarkSouls3 + }, + [Game.ArmoredCore6]: Game.EldenRing + } + }, + [ActionType.WaterInteraction]: { + props: { + texture: { default: 50004, paths: {}, field: FieldType.Integer }, + depth: { default: 1, paths: {}, field: FieldType.Float }, + scale: { default: 1, paths: {}, field: FieldType.Float }, + descent: { default: 0.15, paths: {}, field: FieldType.Float }, + duration: { default: 0.15, paths: {}, field: FieldType.Float }, + }, + games: { + [Game.Sekiro]: { + fields1: ['texture','depth','scale','descent','duration'] + }, + [Game.EldenRing]: Game.Sekiro, + [Game.ArmoredCore6]: Game.Sekiro + } + }, + [ActionType.RichModel]: { + props: { + orientation: { default: OrientationMode.LocalSouth, paths: {}, field: FieldType.Integer }, + scaleVariationX: { default: 1, paths: {}, field: FieldType.Float }, + scaleVariationY: { default: 1, paths: {}, field: FieldType.Float }, + scaleVariationZ: { default: 1, paths: {}, field: FieldType.Float }, + uniformScale: { default: false, paths: {}, field: FieldType.Boolean }, + bloomRed: { default: 1, paths: {}, field: FieldType.Float }, + bloomGreen: { default: 1, paths: {}, field: FieldType.Float }, + bloomBlue: { default: 1, paths: {}, field: FieldType.Float }, + bloomStrength: { default: 0, paths: {}, field: FieldType.Float }, + minDistance: { default: -1, paths: {}, field: FieldType.Float }, + maxDistance: { default: -1, paths: {}, field: FieldType.Float }, + model: { default: 80201, paths: {}, field: FieldType.Integer }, + sizeX: { default: 1, paths: {} }, + sizeY: { default: 1, paths: {} }, + sizeZ: { default: 1, paths: {} }, + rotationX: { default: 0, paths: {} }, + rotationY: { default: 0, paths: {} }, + rotationZ: { default: 0, paths: {} }, + rotationSpeedX: { default: 0, paths: {} }, + rotationSpeedY: { default: 0, paths: {} }, + rotationSpeedZ: { default: 0, paths: {} }, + rotationSpeedMultiplierX: { default: 1, paths: {} }, + rotationSpeedMultiplierY: { default: 1, paths: {} }, + rotationSpeedMultiplierZ: { default: 1, paths: {} }, + color1: { default: [1, 1, 1, 1], paths: {} }, + color2: { default: [1, 1, 1, 1], paths: {} }, + color3: { default: [1, 1, 1, 1], paths: {} }, + uOffset: { default: 0, paths: {} }, + vOffset: { default: 0, paths: {} }, + uSpeed: { default: 0, paths: {} }, + uSpeedMultiplier: { default: 0, paths: {} }, + vSpeed: { default: 0, paths: {} }, + vSpeedMultiplier: { default: 0, paths: {} }, + rgbMultiplier: { default: 1, paths: {} }, + alphaMultiplier: { default: 1, paths: {} }, + animation: { default: 0, paths: {}, field: FieldType.Integer }, + loopAnimation: { default: 1, paths: {}, field: FieldType.Boolean }, + animationSpeed: { default: 1, paths: {}, field: FieldType.Float }, + unk_er_f1_5: { default: 1, paths: {}, field: FieldType.Integer }, + unk_er_f1_6: { default: 1, paths: {}, field: FieldType.Integer }, + unk_er_f1_7: { default: 0, paths: {}, field: FieldType.Integer }, + unk_er_f1_8: { default: -2, paths: {}, field: FieldType.Integer }, + unk_er_f1_9: { default: -2, paths: {}, field: FieldType.Integer }, + unk_er_f1_11: { default: 0, paths: {}, field: FieldType.Integer }, + unk_er_f1_14: { default: 0, paths: {}, field: FieldType.Integer }, + unk_er_f1_15: { default: 0, paths: {}, field: FieldType.Integer }, + unk_er_f1_16: { default: 0, paths: {}, field: FieldType.Integer }, + unk_er_f1_17: { default: 0, paths: {}, field: FieldType.Integer }, + unk_er_f1_18: { default: 0, paths: {}, field: FieldType.Integer }, + unk_er_f1_19: { default: 0, paths: {}, field: FieldType.Integer }, + unk_er_f1_20: { default: 0, paths: {}, field: FieldType.Integer }, + unk_er_f1_21: { default: 0, paths: {}, field: FieldType.Integer }, + unk_er_f1_22: { default: 0, paths: {}, field: FieldType.Integer }, + unk_er_f1_23: { default: 0, paths: {}, field: FieldType.Integer }, + unk_er_f1_24: { default: 0, paths: {}, field: FieldType.Integer }, + unk_er_f1_25: { default: 1, paths: {}, field: FieldType.Integer }, + unk_er_f2_0: { default: 0, paths: {}, field: FieldType.Integer }, + unk_er_f2_1: { default: 0, paths: {}, field: FieldType.Integer }, + unk_er_f2_2: { default: 8, paths: {}, field: FieldType.Integer }, + unk_er_f2_3: { default: 0, paths: {}, field: FieldType.Integer }, + unk_er_f2_8: { default: 0, paths: {}, field: FieldType.Integer }, + unk_er_f2_9: { default: 0, paths: {}, field: FieldType.Integer }, + unk_er_f2_10: { default: 0, paths: {}, field: FieldType.Integer }, + unk_er_f2_11: { default: 0, paths: {}, field: FieldType.Integer }, + unk_er_f2_12: { default: 0, paths: {}, field: FieldType.Integer }, + unk_er_f2_13: { default: 0, paths: {}, field: FieldType.Integer }, + unkDistFadeClose0: { default: -1, paths: {}, field: FieldType.Float }, + unkDistFadeClose1: { default: -1, paths: {}, field: FieldType.Float }, + unkDistFadeFar0: { default: -1, paths: {}, field: FieldType.Float }, + unkDistFadeFar1: { default: -1, paths: {}, field: FieldType.Float }, + unk_er_f2_20: { default: 0, paths: {}, field: FieldType.Integer }, + unk_er_f2_21: { default: 0, paths: {}, field: FieldType.Integer }, + unk_er_f2_22: { default: 0, paths: {}, field: FieldType.Integer }, + unk_er_f2_23: { default: 0, paths: {}, field: FieldType.Integer }, + unk_er_f2_24: { default: 0, paths: {}, field: FieldType.Integer }, + unkDepthBlend1: { default: 1, paths: {}, field: FieldType.Float }, + unkDepthBlend2: { default: 0, paths: {}, field: FieldType.Float }, + unk_er_f2_27: { default: 0, paths: {}, field: FieldType.Integer }, + unk_er_f2_28: { default: 1, paths: {}, field: FieldType.Integer }, + unk_er_f2_29: { default: 0, paths: {}, field: FieldType.Integer }, + unk_er_f2_30: { default: 0, paths: {}, field: FieldType.Integer }, + unk_er_f2_31: { default: 1, paths: {}, field: FieldType.Float }, + unk_er_f2_32: { default: 0, paths: {}, field: FieldType.Integer }, + unk_er_f2_33: { default: 0, paths: {}, field: FieldType.Integer }, + unk_er_f2_34: { default: 0.5, paths: {}, field: FieldType.Float }, + unk_er_f2_35: { default: -2, paths: {}, field: FieldType.Integer }, + unk_er_f2_36: { default: -2, paths: {}, field: FieldType.Integer }, + unk_er_f2_37: { default: 0, paths: {}, field: FieldType.Integer }, + unk_er_p1_16: { default: 0, paths: {} }, + unk_er_p1_17: { default: 0, paths: {} }, + rgbMultiplier2: { default: 1, paths: {} }, + unk_er_p1_19: { default: 0, paths: {} }, + unk_er_p1_20: { default: 0, paths: {} }, + unk_ds3_p2_2: { default: 0, paths: {} }, + unk_ds3_p2_3: { default: [1, 1, 1, 1], paths: {} }, + unk_ds3_p2_4: { default: [1, 1, 1, 1], paths: {} }, + unk_ds3_p2_5: { default: [1, 1, 1, 1], paths: {} }, + unk_ds3_p2_6: { default: 0, paths: {} }, + unk_ac6_f1_24: { default: 0, paths: {}, field: FieldType.Float }, + unk_ac6_f1_25: { default: -1, paths: {}, field: FieldType.Float }, + unk_ac6_f1_26: { default: -1, paths: {}, field: FieldType.Float }, + unk_ac6_f1_27: { default: -1, paths: {}, field: FieldType.Float }, + unk_ac6_f1_28: { default: -1, paths: {}, field: FieldType.Float }, + unk_ac6_f1_29: { default: 0, paths: {}, field: FieldType.Integer }, + unk_ac6_f1_30: { default: 0, paths: {}, field: FieldType.Integer }, + unk_ac6_f1_31: { default: 0, paths: {}, field: FieldType.Integer }, + unk_ac6_f1_32: { default: 0, paths: {}, field: FieldType.Integer }, + unk_ac6_f1_33: { default: 1, paths: {}, field: FieldType.Integer }, + unk_ac6_f1_34: { default: 0, paths: {}, field: FieldType.Integer }, + uvOffset: { default: [0, 0], paths: {} }, + uvSpeed: { default: [0, 0], paths: {} }, + uvSpeedMultiplier: { default: [1, 1], paths: {} }, + }, + games: { + [Game.EldenRing]: { + fields1: ['orientation','scaleVariationX','scaleVariationY','scaleVariationZ','uniformScale','unk_er_f1_5','unk_er_f1_6','unk_er_f1_7','unk_er_f1_8','unk_er_f1_9','animation','unk_er_f1_11','loopAnimation','animationSpeed','unk_er_f1_14','unk_er_f1_15','unk_er_f1_16','unk_er_f1_17','unk_er_f1_18','unk_er_f1_19','unk_er_f1_20','unk_er_f1_21','unk_er_f1_22','unk_er_f1_23','unk_er_f1_24','unk_er_f1_25'], + fields2: ['unk_er_f2_0','unk_er_f2_1','unk_er_f2_2','unk_er_f2_3','bloomRed','bloomGreen','bloomBlue','bloomStrength','unk_er_f2_8','unk_er_f2_9','unk_er_f2_10','unk_er_f2_11','unk_er_f2_12','unk_er_f2_13','unkDistFadeClose0','unkDistFadeClose1','unkDistFadeFar0','unkDistFadeFar1','minDistance','maxDistance','unk_er_f2_20','unk_er_f2_21','unk_er_f2_22','unk_er_f2_23','unk_er_f2_24','unkDepthBlend1','unkDepthBlend2','unk_er_f2_27','unk_er_f2_28','unk_er_f2_29','unk_er_f2_30','unk_er_f2_31','unk_er_f2_32','unk_er_f2_33','unk_er_f2_34','unk_er_f2_35','unk_er_f2_36','unk_er_f2_37'], + properties1: ['model','sizeX','sizeY','sizeZ','rotationX','rotationY','rotationZ','rotationSpeedX','rotationSpeedMultiplierX','rotationSpeedY','rotationSpeedMultiplierY','rotationSpeedZ','rotationSpeedMultiplierZ','color1','color2','color3','unk_er_p1_16','unk_er_p1_17','rgbMultiplier2','unk_er_p1_19','unk_er_p1_20','uOffset','vOffset','uSpeed','uSpeedMultiplier','vSpeed','vSpeedMultiplier'], + properties2: ['rgbMultiplier','alphaMultiplier','unk_ds3_p2_2','unk_ds3_p2_3','unk_ds3_p2_4','unk_ds3_p2_5','unk_ds3_p2_6'] + }, + [Game.ArmoredCore6]: { + fields1: ['orientation','scaleVariationX','scaleVariationY','scaleVariationZ','uniformScale','unk_er_f1_5','unk_er_f1_6','unk_er_f1_7','unk_er_f1_8','unk_er_f1_9','animation','unk_er_f1_11','loopAnimation','animationSpeed','unk_er_f1_14','unk_er_f1_15','unk_er_f1_16','unk_er_f1_17','unk_er_f1_18','unk_er_f1_19','unk_er_f1_20','unk_er_f1_21','unk_er_f1_22','unk_er_f1_23','unk_ac6_f1_24','unk_ac6_f1_25','unk_ac6_f1_26','unk_ac6_f1_27','unk_ac6_f1_28','unk_ac6_f1_29','unk_ac6_f1_30','unk_ac6_f1_31','unk_ac6_f1_32','unk_ac6_f1_33','unk_ac6_f1_34'], + fields2: Game.EldenRing, + properties1: ['model','sizeX','sizeY','sizeZ','rotationX','rotationY','rotationZ','rotationSpeedX','rotationSpeedMultiplierX','rotationSpeedY','rotationSpeedMultiplierY','rotationSpeedZ','rotationSpeedMultiplierZ','color1','color2','color3','unk_er_p1_16','unk_er_p1_17','rgbMultiplier2','unk_er_p1_19','unk_er_p1_20','uvOffset','uvSpeed','uvSpeedMultiplier'], + properties2: ['rgbMultiplier','alphaMultiplier','unk_ds3_p2_2','unk_ds3_p2_3','unk_ds3_p2_4','unk_ds3_p2_5','unk_ds3_p2_6'] + } + } + }, + [ActionType.Unk10500]: { + props: { + rateOfTime: { default: 1, paths: {}, field: FieldType.Float }, + unk_ds3_f1_0: { default: 0, paths: {}, field: FieldType.Integer }, + unk_ds3_f1_1: { default: 0, paths: {}, field: FieldType.Float }, + unk_ds3_f1_2: { default: 0, paths: {}, field: FieldType.Integer }, + unk_ds3_f1_3: { default: 0, paths: {}, field: FieldType.Float }, + unk_ds3_f1_4: { default: 0, paths: {}, field: FieldType.Float }, + unk_ds3_f1_5: { default: 0, paths: {}, field: FieldType.Float }, + unk_ds3_f1_6: { default: 0, paths: {}, field: FieldType.Integer }, + unk_ds3_f1_7: { default: 0, paths: {}, field: FieldType.Integer }, + unk_ds3_f1_8: { default: 0, paths: {}, field: FieldType.Integer }, + unk_sdt_f1_9: { default: 0, paths: {}, field: FieldType.Integer }, + }, + games: { + [Game.DarkSouls3]: { + fields1: ['unk_ds3_f1_0','unk_ds3_f1_1','unk_ds3_f1_2','unk_ds3_f1_3','unk_ds3_f1_4','unk_ds3_f1_5','unk_ds3_f1_6','unk_ds3_f1_7','unk_ds3_f1_8'] + }, + [Game.Sekiro]: { + fields1: ['unk_ds3_f1_0','unk_ds3_f1_1','unk_ds3_f1_2','unk_ds3_f1_3','unk_ds3_f1_4','unk_ds3_f1_5','unk_ds3_f1_6','unk_ds3_f1_7','unk_ds3_f1_8','unk_sdt_f1_9','rateOfTime'] + }, + [Game.EldenRing]: { + fields1: ['unk_ds3_f1_0','unk_ds3_f1_1','unk_ds3_f1_2','unk_ds3_f1_3','unk_ds3_f1_4','unk_ds3_f1_5','unk_ds3_f1_6','unk_ds3_f1_7','unk_ds3_f1_8','unk_sdt_f1_9'], + properties1: ['rateOfTime'] + }, + [Game.ArmoredCore6]: Game.EldenRing + } + }, + [ActionType.SpotLight]: { + props: { + diffuseColor: { default: [1, 1, 1, 1], paths: {} }, + specularColor: { default: [1, 1, 1, 1], paths: {} }, + diffuseMultiplier: { default: 1, paths: {} }, + specularMultiplier: { default: 1, paths: {} }, + near: { default: 0.01, paths: {} }, + far: { default: 50, paths: {} }, + radiusX: { default: 50, paths: {} }, + radiusY: { default: 50, paths: {} }, + jitterAndFlicker: { default: false, paths: {}, field: FieldType.Boolean }, + jitterAcceleration: { default: 1, paths: {}, field: FieldType.Float }, + jitterX: { default: 0, paths: {}, field: FieldType.Float }, + jitterY: { default: 0, paths: {}, field: FieldType.Float }, + jitterZ: { default: 0, paths: {}, field: FieldType.Float }, + flickerIntervalMin: { default: 0, paths: {}, field: FieldType.Float }, + flickerIntervalMax: { default: 1, paths: {}, field: FieldType.Float }, + flickerBrightness: { default: 0.5, paths: {}, field: FieldType.Float }, + shadows: { default: false, paths: {}, field: FieldType.Boolean }, + separateSpecular: { default: false, paths: {}, field: FieldType.Boolean }, + fadeOutTime: { default: 0, paths: {}, field: FieldType.Integer }, + shadowDarkness: { default: 1, paths: {}, field: FieldType.Float }, + volumeDensity: { default: 0, paths: {}, field: FieldType.Float }, + phaseFunction: { default: true, paths: {}, field: FieldType.Boolean }, + asymmetryParam: { default: 0.75, paths: {}, field: FieldType.Float }, + falloffExponent: { default: 1, paths: {}, field: FieldType.Float }, + unk_ds3_f1_0: { default: 1, paths: {}, field: FieldType.Integer }, + unk_ds3_f1_3: { default: 2, paths: {}, field: FieldType.Integer }, + unk_ds3_f1_4: { default: 1, paths: {}, field: FieldType.Integer }, + unk_ds3_f1_5: { default: 1, paths: {}, field: FieldType.Float }, + unk_ds3_f1_7: { default: 0, paths: {}, field: FieldType.Integer }, + unk_ds3_f1_8: { default: 0, paths: {}, field: FieldType.Integer }, + unk_ds3_p1_6: { default: 1, paths: {} }, + unk_ds3_p1_7: { default: 1, paths: {} }, + unk_sdt_f1_0: { default: 0, paths: {}, field: FieldType.Integer }, + unk_sdt_f1_3: { default: 0, paths: {}, field: FieldType.Float }, + unk_sdt_f1_16: { default: 100, paths: {}, field: FieldType.Integer }, + unk_sdt_f1_17: { default: 0, paths: {}, field: FieldType.Integer }, + unk_sdt_f1_18: { default: 0, paths: {}, field: FieldType.Float }, + unk_sdt_f1_20: { default: 0, paths: {}, field: FieldType.Float }, + unk_sdt_p1_10: { default: 1, paths: {} }, + unk_er_f1_24: { default: 1, paths: {}, field: FieldType.Integer }, + unk_er_f1_25: { default: 1, paths: {}, field: FieldType.Float }, + unk_ac6_f1_26: { default: 1, paths: {}, field: FieldType.Integer }, + unk_ac6_f1_27: { default: 0, paths: {}, field: FieldType.Integer }, + }, + games: { + [Game.DarkSouls3]: { + fields1: ['unk_ds3_f1_0','shadows','shadowDarkness','unk_ds3_f1_3','unk_ds3_f1_4','unk_ds3_f1_5','fadeOutTime','unk_ds3_f1_7','unk_ds3_f1_8'], + properties1: ['diffuseColor','specularColor','near','far','radiusX','radiusY','unk_ds3_p1_6','unk_ds3_p1_7'] + }, + [Game.Sekiro]: { + fields1: ['unk_sdt_f1_0','jitterAndFlicker','jitterAcceleration','unk_sdt_f1_3','jitterX','jitterY','jitterZ','flickerIntervalMin','flickerIntervalMax','flickerBrightness','shadows','separateSpecular','shadowDarkness','unk_ds3_f1_3','unk_ds3_f1_4','fadeOutTime','unk_sdt_f1_16','unk_sdt_f1_17','unk_sdt_f1_18','volumeDensity','unk_sdt_f1_20','phaseFunction','asymmetryParam','falloffExponent'], + properties1: ['diffuseColor','specularColor','diffuseMultiplier','specularMultiplier','near','far','radiusX','radiusY','unk_ds3_p1_6','unk_ds3_p1_7','unk_sdt_p1_10'] + }, + [Game.EldenRing]: { + fields1: ['unk_sdt_f1_0','jitterAndFlicker','jitterAcceleration','unk_sdt_f1_3','jitterX','jitterY','jitterZ','flickerIntervalMin','flickerIntervalMax','flickerBrightness','shadows','separateSpecular','shadowDarkness','unk_ds3_f1_3','unk_ds3_f1_4','fadeOutTime','unk_sdt_f1_16','unk_sdt_f1_17','unk_sdt_f1_18','volumeDensity','unk_sdt_f1_20','phaseFunction','asymmetryParam','falloffExponent','unk_er_f1_24','unk_er_f1_25'], + properties1: Game.Sekiro + }, + [Game.ArmoredCore6]: { + fields1: ['unk_sdt_f1_0','jitterAndFlicker','jitterAcceleration','unk_sdt_f1_3','jitterX','jitterY','jitterZ','flickerIntervalMin','flickerIntervalMax','flickerBrightness','shadows','separateSpecular','shadowDarkness','unk_ds3_f1_3','unk_ds3_f1_4','fadeOutTime','unk_sdt_f1_16','unk_sdt_f1_17','unk_sdt_f1_18','volumeDensity','unk_sdt_f1_20','phaseFunction','asymmetryParam','falloffExponent','unk_er_f1_24','unk_er_f1_25','unk_ac6_f1_26','unk_ac6_f1_27'], + properties1: Game.Sekiro + } + } + } + /*#ActionData end*/ +} +for (const [type, action] of Object.entries(ActionData)) { + for (const game of Object.keys(action.games)) { + const gameData = getActionGameData(type as unknown as ActionType, game as unknown as Game) + for (const [name, prop] of Object.entries(action.props)) { + for (const [k, list] of Object.entries(gameData)) { + const i = list.indexOf(name) + if (i >= 0) { + prop.paths[game] = [k, i] + break + } + } + } + } +} + +const EffectActionSlots = { + [EffectType.Basic]: [ + [ + ActionType.NodeAttributes + ], + [ + ActionType.StaticNodeTransform, + ActionType.RandomNodeTransform + ], + [ + ActionType.NodeAcceleration, + ActionType.NodeTranslation, + ActionType.NodeSpin, + ActionType.NodeAttachToCamera, + ActionType.NodeAccelerationRandomTurns, + ActionType.NodeAccelerationPartialFollow, + ActionType.NodeAccelerationSpin, + ActionType.NodeSpeed, + ActionType.NodeSpeedRandomTurns, + ActionType.NodeSpeedPartialFollow, + ActionType.NodeSpeedSpin + ], + [ + ActionType.PlaySound + ], + [ + ActionType.PeriodicEmitter, + ActionType.EqualDistanceEmitter, + ActionType.OneTimeEmitter + ], + [ + ActionType.PointEmitterShape, + ActionType.DiskEmitterShape, + ActionType.RectangleEmitterShape, + ActionType.SphereEmitterShape, + ActionType.BoxEmitterShape, + ActionType.CylinderEmitterShape + ], + [ + ActionType.NoParticleSpread, + ActionType.CircularParticleSpread, + ActionType.EllipticalParticleSpread, + ActionType.RectangularParticleSpread + ], + [ + ActionType.ParticleModifier + ], + [ + ActionType.ParticleAttributes + ], + [ + ActionType.PointSprite, + ActionType.Line, + ActionType.QuadLine, + ActionType.BillboardEx, + ActionType.MultiTextureBillboardEx, + ActionType.Model, + ActionType.Tracer, + ActionType.Distortion, + ActionType.RadialBlur, + ActionType.PointLight, + ActionType.Unk10000_StandardParticle, + ActionType.Unk10001_StandardCorrectParticle, + ActionType.Unk10002_Fluid, + ActionType.Unk10003_LightShaft, + ActionType.Unk10008_SparkParticle, + ActionType.Unk10009_SparkCorrectParticle, + ActionType.Unk10010_Tracer, + ActionType.DynamicTracer, + ActionType.WaterInteraction, + ActionType.Unk10014_LensFlare, + ActionType.RichModel, + ActionType.Unk10200_ForceFieldCancelArea, + ActionType.Unk10300_ForceFieldWindArea, + ActionType.Unk10301_ForceFieldGravityArea, + ActionType.Unk10302_CollisionFieldArea, + ActionType.Unk10303_ForceFieldTurbulenceArea, + ActionType.SpotLight + ], + [ + ActionType.ParticleAcceleration, + ActionType.ParticleSpeed, + ActionType.ParticleSpeedRandomTurns, + ActionType.ParticleSpeedPartialFollow, + ActionType.ParticleAccelerationRandomTurns, + ActionType.ParticleAccelerationPartialFollow + ], + [], + [ + ActionType.Unk130 + ], + [ + ActionType.NodeWindSpeed, + ActionType.NodeWindAcceleration + ], + [ + ActionType.ParticleWindSpeed, + ActionType.ParticleWindAcceleration, + ActionType.Unk800 + ] + ], + [EffectType.SharedEmitter]: [ + [ + ActionType.NodeAttributes + ], + [ + ActionType.StaticNodeTransform, + ActionType.RandomNodeTransform + ], + [ + ActionType.NodeAcceleration, + ActionType.NodeTranslation, + ActionType.NodeSpin, + ActionType.NodeAttachToCamera, + ActionType.NodeAccelerationRandomTurns, + ActionType.NodeAccelerationPartialFollow, + ActionType.NodeAccelerationSpin, + ActionType.NodeSpeed, + ActionType.NodeSpeedRandomTurns, + ActionType.NodeSpeedSpin + ], + [ + ActionType.PlaySound + ], + [ + ActionType.PeriodicEmitter, + ActionType.EqualDistanceEmitter, + ActionType.OneTimeEmitter + ], + [ + ActionType.PointEmitterShape, + ActionType.DiskEmitterShape, + ActionType.RectangleEmitterShape, + ActionType.SphereEmitterShape, + ActionType.BoxEmitterShape, + ActionType.CylinderEmitterShape + ], + [ + ActionType.NoParticleSpread, + ActionType.CircularParticleSpread, + ActionType.EllipticalParticleSpread, + ActionType.RectangularParticleSpread + ], + [ + ActionType.EmitAllParticles, + ActionType.EmitRandomParticles + ], + [], + [ + ActionType.NodeWindSpeed, + ActionType.NodeWindAcceleration + ] + ] +} + +function arrayOf(size: number, func: (index: number) => T): T[] { + return Array(size).fill(null).map((e, i) => func(i)) +} + +function randomInt32() { + return Math.random() * 2**32 | 0 +} + +function scalarFromArg(scalar: ScalarValue) { + return scalar instanceof Property ? scalar : new ConstantProperty(scalar) +} + +function vectorFromArg(vector: VectorValue) { + return vector instanceof Property ? vector : new ConstantProperty(...vector) +} + +function uniqueArray(a: T[]) { + return a.filter((e, i) => a.indexOf(e) === i) +} + +function lerp(a: number, b: number, c: number) { + return a * (1 - c) + b * c +} + +function readProperty | IModifiableProperty>( + br: BinaryReader, + modifierProp: T extends IModifiableProperty ? false : true +): T { + const typeEnumA = br.readInt16() + br.assertUint8(0) + br.assertUint8(1) + const type: ValueType = typeEnumA & 0b00000000_00000011 + const func: PropertyFunction = (typeEnumA & 0b00000000_11110000) >>> 4 + const loop: boolean = !!((typeEnumA & 0b00010000_00000000) >>> 12) + br.position += 4 // TypeEnumB + const count = br.readInt32() + br.assertInt32(0) + const offset = br.readInt32() + br.assertInt32(0) + const modifiers: Modifier[] = [] + if (!modifierProp) { + const modOffset = br.readInt32() + br.assertInt32(0) + const modCount = br.readInt32() + br.assertInt32(0) + br.stepIn(modOffset) + for (let i = 0; i < modCount; ++i) { + modifiers.push(readModifier(br)) + } + br.stepOut() + } + const fields = readFieldsAt(br, offset, count, [func, type]).map(f => f.value) + switch (func) { + case PropertyFunction.Zero: + case PropertyFunction.One: + case PropertyFunction.Constant: + return ValueProperty.fromFields(type, func, modifiers, fields) as unknown as T + case PropertyFunction.Stepped: + case PropertyFunction.Linear: + case PropertyFunction.Curve1: + case PropertyFunction.Curve2: + return SequenceProperty.fromFields(type, func, loop, modifiers, fields) as unknown as T + case PropertyFunction.CompCurve: + return ComponentSequenceProperty.fromFields(type, loop, modifiers, fields) as unknown as T + default: + throw new Error('Unknown property function: ' + func) + } +} + +function writeProperty(prop: IProperty, bw: BinaryWriter, properties: IProperty[], modifierProp: boolean) { + const count = properties.length + let func: PropertyFunction = prop instanceof ValueProperty ? + prop.isZero ? PropertyFunction.Zero : + prop.isOne ? PropertyFunction.One : + PropertyFunction.Constant : + prop.function + const loop = 'loop' in prop ? prop.loop as boolean : false + const typeEnumA = prop.valueType | func << 4 | +loop << 12 + const typeEnumB = prop.valueType | func << 2 | +loop << 4 + bw.writeInt16(typeEnumA) + bw.writeUint8(0) + bw.writeUint8(1) + bw.writeInt32(typeEnumB) + bw.writeInt32(prop.fieldCount) + bw.writeInt32(0) + bw.reserveInt32(`${modifierProp ? 'Property' : 'ModifiableProperty'}FieldsOffset${count}`) + bw.writeInt32(0) + if (!modifierProp) { + bw.reserveInt32(`PropertyModifiersOffset${count}`) + bw.writeInt32(0) + bw.writeInt32((prop as IModifiableProperty).modifiers.length) + bw.writeInt32(0) + } + properties.push(prop) +} + +function writePropertyModifiers(prop: IModifiableProperty, bw: BinaryWriter, index: number, modifiers: Modifier[]) { + bw.fill(`PropertyModifiersOffset${index}`, bw.position) + for (const modifier of prop.modifiers) { + writeModifier.call(modifier, bw, modifiers) + } +} + +function writePropertyFields(prop: IProperty, bw: BinaryWriter, index: number, modifierProp: boolean): number { + const offsetName = `${modifierProp ? 'Property' : 'ModifiableProperty'}FieldsOffset${index}` + const fieldCount = prop.fieldCount + if (fieldCount === 0) { + bw.fill(offsetName, 0) + } else { + bw.fill(offsetName, bw.position) + for (const field of prop.fields) { + writeField.call(field, bw) + } + } + return fieldCount +} + +function readAction(br: BinaryReader, type: number, fieldCount1: number, propertyCount1: number, fieldCount2: number, propertyCount2: number, section10Count: number): Action { + const fieldOffset = br.readInt32() + br.assertInt32(0) + const section10Offset = br.readInt32() + br.assertInt32(0) + const propertyOffset = br.readInt32() + br.assertInt32(0) + br.assertInt32(0) + br.assertInt32(0) + + br.stepIn(propertyOffset) + const properties1: AnyProperty[] = [] + const properties2: AnyProperty[] = [] + for (let i = 0; i < propertyCount1; ++i) { + properties1.push(readProperty(br, false)) + } + for (let i = 0; i < propertyCount2; ++i) { + properties2.push(readProperty(br, false)) + } + br.stepOut() + + br.stepIn(section10Offset) + const section10s: Section10[] = [] + for (let i = 0; i < section10Count; ++i) { + section10s.push(readSection10(br)) + } + br.stepOut() + + br.stepIn(fieldOffset) + const fields1 = readFields(br, fieldCount1, Action) + const fields2 = readFields(br, fieldCount2, Action) + br.stepOut() + if (type in Actions) { + const action = new Actions[type]() + action.type = type + action.fields1 = fields1 + action.fields2 = fields2 + action.properties1 = properties1 + action.properties2 = properties2 + action.section10s = section10s + return action + } else { + return new Action(type, fields1, fields2, properties1, properties2, section10s) + } +} + +function writeAction(this: Action, bw: BinaryWriter, game: Game, actions: IAction[]) { + const count = actions.length + bw.writeInt16(this.type) + bw.writeUint8(0) // Unk02 + bw.writeUint8(0) // Unk03 + bw.writeInt32(0) // Unk04 + bw.writeInt32(this.fields1.length) + bw.writeInt32(this.section10s.length) + bw.writeInt32(this.properties1.length) + bw.writeInt32(this.fields2.length) + bw.writeInt32(0) + bw.writeInt32(this.properties2.length) + bw.reserveInt32(`ActionFieldsOffset${count}`) + bw.writeInt32(0) + bw.reserveInt32(`ActionSection10sOffset${count}`) + bw.writeInt32(0) + bw.reserveInt32(`ActionPropertiesOffset${count}`) + bw.writeInt32(0) + bw.writeInt32(0) + bw.writeInt32(0) + actions.push(this) +} + +function writeActionProperties(this: Action, bw: BinaryWriter, game: Game, index: number, properties: IModifiableProperty[]) { + bw.fill(`ActionPropertiesOffset${index}`, bw.position) + for (const property of this.properties1) { + writeProperty(property, bw, properties, false) + } + for (const property of this.properties2) { + writeProperty(property, bw, properties, false) + } +} + +function writeActionSection10s(this: Action, bw: BinaryWriter, index: number, section10s: Section10[]) { + bw.fill(`ActionSection10sOffset${index}`, bw.position) + for (const section10 of this.section10s) { + writeSection10.call(section10, bw, section10s) + } +} + +function writeActionFields(this: Action, bw: BinaryWriter, game: Game, index: number): number { + const count: number = this.fields1.length + this.fields2.length + if (count === 0) { + bw.fill(`ActionFieldsOffset${index}`, 0) + } else { + bw.fill(`ActionFieldsOffset${index}`, bw.position) + for (const field of this.fields1) { + writeField.call(field, bw) + } + for (const field of this.fields2) { + writeField.call(field, bw) + } + } + return count +} + +function readDataAction(br: BinaryReader, game: Game, type: number, fieldCount1: number, propertyCount1: number, fieldCount2: number, propertyCount2: number): DataAction { + const fieldOffset = br.readInt32() + br.assertInt32(0) + br.position += 4 // Section10 offset + br.assertInt32(0) + const propertyOffset = br.readInt32() + br.assertInt32(0) + br.assertInt32(0) + br.assertInt32(0) + + const c: { + fields1: Field[] + fields2: Field[] + properties1: AnyProperty[] + properties2: AnyProperty[] + } = { + fields1: [], + fields2: [], + properties1: [], + properties2: [], + } + + br.stepIn(propertyOffset) + for (let i = 0; i < propertyCount1; ++i) { + c.properties1.push(readProperty(br, false)) + } + for (let i = 0; i < propertyCount2; ++i) { + c.properties2.push(readProperty(br, false)) + } + br.stepOut() + + br.stepIn(fieldOffset) + const adt = ActionData[type] + const gameData = getActionGameData(type, game) + if ('fields1' in gameData) { + c.fields1 = readFieldsWithTypes(br, fieldCount1, gameData.fields1.map(e => adt.props[e].field), this) + } else { + c.fields1 = readFields(br, fieldCount1, this) + } + if ('fields2' in gameData) { + c.fields2 = readFieldsWithTypes(br, fieldCount2, gameData.fields2.map(e => adt.props[e].field), this) + } else { + c.fields2 = readFields(br, fieldCount2, this) + } + br.stepOut() + let params = Object.fromEntries( + Object.entries(adt.props).filter(([name, prop]) => game in prop.paths).map(([name, prop]) => { + const v = c[prop.paths[game][0]][prop.paths[game][1]] + if (v instanceof Field) { + return [name, v.value] + } else { + return [name, v] + } + }) + ) + if (type in ActionDataConversion && 'read' in ActionDataConversion[type]) { + params = ActionDataConversion[type].read(params) + } + return new DataActions[type](params) +} + +function writeDataAction(this: DataAction, bw: BinaryWriter, game: Game, actions: IAction[]) { + if (game === Game.Generic) { + throw new Error('DataActions cannot be formatted for Game.Generic.') + } + bw.convertedDataActions.set(this, + this.type in ActionDataConversion && 'write' in ActionDataConversion[this.type] ? + ActionDataConversion[this.type].write(Object.assign(Object.create(null), this), game) : + this + ) + const data = getActionGameData(this.type, game) + const count = actions.length + bw.writeInt16(this.type) + bw.writeUint8(0) // Unk02 + bw.writeUint8(0) // Unk03 + bw.writeInt32(0) // Unk04 + bw.writeInt32(data.fields1.length) + bw.writeInt32(0) // Section10 count + bw.writeInt32(data.properties1.length) + bw.writeInt32(data.fields2.length) + bw.writeInt32(0) + bw.writeInt32(data.properties2.length) + bw.reserveInt32(`ActionFieldsOffset${count}`) + bw.writeInt32(0) + bw.reserveInt32(`ActionSection10sOffset${count}`) + bw.writeInt32(0) + bw.reserveInt32(`ActionPropertiesOffset${count}`) + bw.writeInt32(0) + bw.writeInt32(0) + bw.writeInt32(0) + actions.push(this) +} + +function writeDataActionProperties(this: DataAction, bw: BinaryWriter, game: Game, index: number, properties: IModifiableProperty[]) { + const conProps = bw.convertedDataActions.get(this) + const properties1: AnyProperty[] = this.getProperties.call(conProps, game, 'properties1') + const properties2: AnyProperty[] = this.getProperties.call(conProps, game, 'properties2') + bw.fill(`ActionPropertiesOffset${index}`, bw.position) + for (const property of properties1) { + writeProperty(property, bw, properties, false) + } + for (const property of properties2) { + writeProperty(property, bw, properties, false) + } +} + +function writeDataActionSection10s(this: DataAction, bw: BinaryWriter, index: number, section10s: Section10[]) { + bw.fill(`ActionSection10sOffset${index}`, bw.position) +} + +function writeDataActionFields(this: DataAction, bw: BinaryWriter, game: Game, index: number): number { + const conProps = bw.convertedDataActions.get(this) + const fields1: Field[] = this.getFields.call(conProps, game, 'fields1') + const fields2: Field[] = this.getFields.call(conProps, game, 'fields2') + const count = fields1.length + fields2.length + if (count === 0) { + bw.fill(`ActionFieldsOffset${index}`, 0) + } else { + bw.fill(`ActionFieldsOffset${index}`, bw.position) + for (const field of fields1) { + writeField.call(field, bw) + } + for (const field of fields2) { + writeField.call(field, bw) + } + } + return count +} + +function readAnyAction(br: BinaryReader, game: Game): IAction { + const type = br.readInt16() + br.position += 6 + // br.readUint8() // Unk02 + // br.readUint8() // Unk03 + // br.readInt32() // Unk04 + const fieldCount1 = br.readInt32() + const section10Count = br.readInt32() + const propertyCount1 = br.readInt32() + const fieldCount2 = br.readInt32() + br.assertInt32(0) + const propertyCount2 = br.readInt32() + + if (game !== Game.Generic && type in ActionData) { + const data = getActionGameData(type, game) + if ( + section10Count === 0 && + fieldCount1 <= data.fields1.length && + fieldCount2 <= data.fields2.length && + propertyCount1 <= data.properties1.length && + propertyCount2 <= data.properties2.length + ) { + return readDataAction(br, game, type, fieldCount1, propertyCount1, fieldCount2, propertyCount2) + } else { + return readAction(br, type, fieldCount1, propertyCount1, fieldCount2, propertyCount2, section10Count) + } + } else { + return readAction(br, type, fieldCount1, propertyCount1, fieldCount2, propertyCount2, section10Count) + } +} + +function writeAnyAction(this: IAction, bw: BinaryWriter, game: Game, actions: IAction[]) { + if (this instanceof DataAction) { + if (ActionData[this.type].games[game] === -2) { + writeAction.call(ActionDataConversion[this.type].fallback(this, game), bw, game, actions) + } else { + writeDataAction.call(this, bw, game, actions) + } + } else { + writeAction.call(this, bw, game, actions) + } +} + +function writeAnyActionProperties(this: IAction, bw: BinaryWriter, game: Game, index: number, properties: IModifiableProperty[]) { + ;(this instanceof Action ? writeActionProperties : writeDataActionProperties).call(this, bw, game, index, properties) +} + +function writeAnyActionSection10s(this: IAction, bw: BinaryWriter, index: number, section10s: Section10[]) { + ;(this instanceof Action ? writeActionSection10s : writeDataActionSection10s).call(this, bw, index, section10s) +} + +function writeAnyActionFields(this: IAction, bw: BinaryWriter, game: Game, index: number): number { + return (this instanceof Action ? writeActionFields : writeDataActionFields).call(this, bw, game, index) +} + +function readNode(br: BinaryReader, game: Game): Node { + const type = br.readInt16() + br.assertUint8(0) + br.assertUint8(1) + br.assertInt32(0) + const effectCount = br.readInt32() + const actionCount = br.readInt32() + const nodeCount = br.readInt32() + br.assertInt32(0) + const effectOffset = br.readInt32() + br.assertInt32(0) + const actionOffset = br.readInt32() + br.assertInt32(0) + const nodeOffset = br.readInt32() + br.assertInt32(0) + br.stepIn(nodeOffset) + const nodes = [] + for (let i = 0; i < nodeCount; ++i) { + nodes.push(readNode(br, game)) + } + br.stepOut() + br.stepIn(effectOffset) + const effects = [] + for (let i = 0; i < effectCount; ++i) { + effects.push(readEffect(br, game)) + } + br.stepOut() + br.stepIn(actionOffset) + const actions = [] + for (let i = 0; i < actionCount; ++i) { + actions.push(readAnyAction(br, game)) + } + br.stepOut() + if (game !== Game.Generic) switch (type) { + case NodeType.Root: + if (effectCount === 0 && actionCount === (game === Game.DarkSouls3 || game === Game.Sekiro ? 3 : 4)) { + return new RootNode( + nodes, + game === Game.DarkSouls3 || game === Game.Sekiro ? null : + actions.find(e => e.type >= 700 && e.type <= 702) ?? new Action(ActionType.Unk700), + actions.find(e => e.type === ActionType.Unk10100), + actions.find(e => e.type === ActionType.Unk10400), + actions.find(e => e.type === ActionType.Unk10500) + ) + } + break + case NodeType.Proxy: + if (effectCount === 0 && actionCount === 1 && actions[0] instanceof SFXReference) { + return new ProxyNode(actions[0].sfx) + } + break + case NodeType.LevelOfDetail: + if (actionCount === 1 && actions[0] instanceof StateEffectMap) { + return new LevelOfDetailNode(effects, nodes).mapStates(...actions[0].effectIndices) + } + break + case NodeType.Basic: + if (actionCount === 1 && actions[0] instanceof StateEffectMap) { + return new BasicNode(effects, nodes).mapStates(...actions[0].effectIndices) + } + break + case NodeType.SharedEmitter: + if (actionCount === 1 && actions[0] instanceof StateEffectMap) { + return new SharedEmitterNode(effects, nodes).mapStates(...actions[0].effectIndices) + } + break + } + return new GenericNode(type, actions, effects, nodes) +} + +function writeNode(this: Node, bw: BinaryWriter, game: Game, nodes: Node[]) { + if (game === Game.Generic && !(this instanceof GenericNode)) { + throw new Error('Non-generic node classes cannot be formatted for Game.Generic.') + } + const count = nodes.length + let effectCount = 0 + let actionCount = 0 + let childCount = 0 + if (this instanceof GenericNode) { + effectCount = this.effects.length + actionCount = this.actions.length + childCount = this.nodes.length + } else if (this instanceof NodeWithEffects) { + effectCount = this.effects.length + actionCount = 1 + childCount = this.nodes.length + } else if (this instanceof RootNode) { + actionCount = game === Game.DarkSouls3 || game === Game.Sekiro ? 3 : 4 + childCount = this.nodes.length + } else if (this instanceof ProxyNode) { + actionCount = 1 + } + bw.writeInt16(this.type) + bw.writeUint8(0) + bw.writeUint8(1) + bw.writeInt32(0) + bw.writeInt32(effectCount) + bw.writeInt32(actionCount) + bw.writeInt32(childCount) + bw.writeInt32(0) + bw.reserveInt32(`NodeEffectsOffset${count}`) + bw.writeInt32(0) + bw.reserveInt32(`NodeActionsOffset${count}`) + bw.writeInt32(0) + bw.reserveInt32(`NodeChildNodesOffset${count}`) + bw.writeInt32(0) + nodes.push(this) +} + +function writeNodeChildren(this: Node, bw: BinaryWriter, game: Game, nodes: Node[]) { + const num = nodes.indexOf(this) + let childCount = 0 + if (this instanceof GenericNode || this instanceof NodeWithEffects || this instanceof RootNode) { + childCount = this.nodes.length + } + if (childCount === 0) { + bw.fill(`NodeChildNodesOffset${num}`, 0) + } else { + bw.fill(`NodeChildNodesOffset${num}`, bw.position) + const children = (this as Node).getNodes(game) + for (const node of children) { + writeNode.call(node, bw, game, nodes) + } + for (const node of children) { + writeNodeChildren.call(node, bw, game, nodes) + } + } +} + +function writeNodeEffects(this: Node, bw: BinaryWriter, game: Game, index: number, effectCounter: { value: number }) { + let effectCount = 0 + if (this instanceof GenericNode || this instanceof NodeWithEffects) { + effectCount = this.effects.length + } + if (effectCount === 0) { + bw.fill(`NodeEffectsOffset${index}`, 0) + } else { + bw.fill(`NodeEffectsOffset${index}`, bw.position) + const nodeEffects = this.getEffects(game) + for (let i = 0; i < nodeEffects.length; ++i) { + writeEffect.call(nodeEffects[i], bw, game, effectCounter.value + i) + } + effectCounter.value += nodeEffects.length + } +} + +function writeNodeActions(this: Node, bw: BinaryWriter, game: Game, index: number, effectCounter: { value: number }, actions: Action[]) { + bw.fill(`NodeActionsOffset${index}`, bw.position) + const nodeActions = this.getActions(game) + const nodeEffects = this.getEffects(game) + for (const action of nodeActions) { + writeAnyAction.call(action, bw, game, actions) + } + for (let i = 0; i < nodeEffects.length; ++i) { + writeEffectActions.call(nodeEffects[i], bw, game, effectCounter.value + i, actions) + } + effectCounter.value += nodeEffects.length +} + +function readEffect(br: BinaryReader, game: Game): IEffect { + const type = br.readInt16() + br.assertUint8(0) + br.assertUint8(1) + br.assertInt32(0) + br.assertInt32(0) + const actionCount = br.readInt32() + br.assertInt32(0) + br.assertInt32(0) + const actionOffset = br.readInt32() + br.assertInt32(0) + br.stepIn(actionOffset) + const actions = [] + for (let i = 0; i < actionCount; ++i) { + actions.push(readAnyAction(br, game)) + } + br.stepOut() + if (game === Game.Generic) { + return new Effect(type, actions) + } else if (type === EffectType.LevelOfDetail && actionCount === 1 && actions[0] instanceof LevelOfDetailThresholds) { + const lod = actions[0] + return new LevelOfDetailEffect(lod.duration, [ + lod.threshold0, + lod.threshold1, + lod.threshold2, + lod.threshold3, + lod.threshold4, + ]) + } else if (type === EffectType.Basic && actionCount <= 15) { + return new BasicEffect(actions) + } else if (type === EffectType.SharedEmitter && actionCount <= 10) { + return new SharedEmitterEffect(actions) + } else { + return new Effect(type, actions) + } +} + +function writeEffect(this: IEffect, bw: BinaryWriter, game: Game, index: number) { + bw.writeInt16(this.type) + bw.writeUint8(0) + bw.writeUint8(1) + bw.writeInt32(0) + bw.writeInt32(0) + bw.writeInt32(this.getActionCount(game)) + bw.writeInt32(0) + bw.writeInt32(0) + bw.reserveInt32(`EffectActionsOffset${index}`) + bw.writeInt32(0) +} + +function writeEffectActions(this: IEffect, bw: BinaryWriter, game: Game, index: number, actions: Action[]) { + bw.fill(`EffectActionsOffset${index}`, bw.position) + for (const action of this.getActions(game)) { + writeAnyAction.call(action, bw, game, actions) + } +} + +function readModifier(br: BinaryReader) { + const typeEnumA = br.readUint16() + const teA1 = typeEnumA >>> 12 & 0b11 + const teA2 = typeEnumA >>> 4 & 0b1111 + const modifierType = teA1 << 4 | teA2 + const valueType = typeEnumA & 0b11 + if (!(modifierType in ModifierType)) { + throw new Error('Unknown property modifier type enum A: ' + typeEnumA) + } + br.assertUint8(0) + br.assertUint8(1) + const typeEnumB = br.readUint32() + const fieldCount = br.readInt32() + const propertyCount = br.readInt32() + const fieldOffset = br.readInt32() + br.assertInt32(0) + const propertyOffset = br.readInt32() + br.assertInt32(0) + br.stepIn(propertyOffset) + const properties = [] + for (let i = 0; i < propertyCount; ++i) { + properties.push(readProperty(br, true)) + } + br.stepOut() + const fields = readFieldsAt(br, fieldOffset, fieldCount, this) + const mod = new Modifier(modifierType, valueType, fields, properties) + mod.typeEnumB = typeEnumB + return mod +} + +function writeModifier(this: Modifier, bw: BinaryWriter, modifiers: Modifier[]) { + const count = modifiers.length + bw.writeInt16(this.typeEnumA) + bw.writeUint8(0) + bw.writeUint8(1) + bw.writeInt32(this.typeEnumB) + bw.writeInt32(this.fields.length) + bw.writeInt32(this.properties.length) + bw.reserveInt32(`Section8FieldsOffset${count}`) + bw.writeInt32(0) + bw.reserveInt32(`Section8Section9sOffset${count}`) + bw.writeInt32(0) + modifiers.push(this) +} + +function writeModifierProperties(this: Modifier, bw: BinaryWriter, index: number, properties: IProperty[]) { + bw.fill(`Section8Section9sOffset${index}`, bw.position) + for (const property of this.properties) { + writeProperty(property, bw, properties, true) + } +} + +function writeModifierFields(this: Modifier, bw: BinaryWriter, index: number): number { + bw.fill(`Section8FieldsOffset${index}`, bw.position) + for (const field of (this as Modifier).fields) { + writeField.call(field, bw) + } + return this.fields.length +} + +function readSection10(br: BinaryReader) { + const offset = br.readInt32() + br.assertInt32(0) + const count = br.readInt32() + br.assertInt32(0) + return new Section10(readFieldsAt(br, offset, count, this)) +} + +function writeSection10(this: Section10, bw: BinaryWriter, section10s: Section10[]) { + const count = section10s.length + bw.reserveInt32(`Section10FieldsOffset${count}`) + bw.writeInt32(0) + bw.writeInt32(this.fields.length) + bw.writeInt32(0) + section10s.push(this) +} + +function writeSection10Fields(this: Section10, bw: BinaryWriter, index: number): number { + bw.fill(`Section10FieldsOffset${index}`, bw.position) + for (const field of (this as Section10).fields) { + writeField.call(field, bw) + } + return this.fields.length +} + +function readState(br: BinaryReader) { + br.assertInt32(0) + const count = br.readInt32() + const offset = br.readInt32() + br.assertInt32(0) + br.stepIn(offset) + const conditions: StateCondition[] = [] + for (let i = 0; i < count; ++i) { + conditions.push(readStateCondition(br)) + } + br.stepOut() + return new State(conditions) +} + +function writeState(this: State, bw: BinaryWriter, index: number) { + bw.writeInt32(0) + bw.writeInt32(this.conditions.length) + bw.reserveInt32(`StateConditionsOffset${index}`) + bw.writeInt32(0) +} + +function writeStateConditions(this: State, bw: BinaryWriter, index: number, conditions: StateCondition[]) { + bw.fill(`StateConditionsOffset${index}`, bw.position) + for (const condition of (this as State).conditions) { + writeStateCondition.call(condition, bw, conditions) + } +} + +function readStateCondition(br: BinaryReader) { + const bf1 = br.readInt16() + const operator = bf1 & 0b11 + const unk1 = (bf1 & 0b1100) >>> 2 + br.assertUint8(0) + br.assertUint8(1) + br.assertInt32(0) + const targetStateIndex = br.readInt32() + br.assertInt32(0) + const leftOperand = br.assertInt16(-4, -3, -2, -1) + br.assertUint8(0) + br.assertUint8(1) + br.assertInt32(0) + const hasLeftValue = !!br.assertInt32(0, 1) + br.assertInt32(0) + const leftOffset = br.readInt32() + br.assertInt32(0) + br.assertInt32(0) + br.assertInt32(0) + br.assertInt32(0) + br.assertInt32(0) + const rightOperand = br.assertInt16(-4, -3, -2, -1) + br.assertUint8(0) + br.assertUint8(1) + br.assertInt32(0) + const hasRightValue = !!br.assertInt32(0, 1) + br.assertInt32(0) + const rightOffset = br.readInt32() + br.assertInt32(0) + br.assertInt32(0) + br.assertInt32(0) + br.assertInt32(0) + br.assertInt32(0) + return new StateCondition( + operator, + unk1, + targetStateIndex, + leftOperand, + hasLeftValue ? readStateConditionOperandValue(br, leftOperand, leftOffset) : null, + rightOperand, + hasRightValue ? readStateConditionOperandValue(br, rightOperand, rightOffset) : null, + ).sortOperands() +} + +function readStateConditionOperandValue(br: BinaryReader, type: number, offset: number) { + switch (type) { + case OperandType.Literal: { + br.stepIn(offset) + const value = br.readFloat32() + br.stepOut() + return value + } + case OperandType.External: { + br.stepIn(offset) + const value = br.readInt32() + br.stepOut() + return value + } + case OperandType.UnkMinus2: + case OperandType.StateTime: + throw new Error('Unexpected value for operand without value: ' + OperandType[type]) + } +} + +function writeFormattedStateCondition(this: StateCondition, bw: BinaryWriter, conditions: StateCondition[]) { + const count = conditions.length + bw.writeInt16(this.operator | this.unk1 << 2) + bw.writeUint8(0) + bw.writeUint8(1) + bw.writeInt32(0) + bw.writeInt32(this.nextState) + bw.writeInt32(0) + bw.writeInt16(this.leftOperandType) + bw.writeInt8(0) + bw.writeInt8(1) + bw.writeInt32(0) + bw.writeInt32(+(this.leftOperandValue !== null)) + bw.writeInt32(0) + bw.reserveInt32(`ConditionLeftOffset${count}`) + bw.writeInt32(0) + bw.writeInt32(0) + bw.writeInt32(0) + bw.writeInt32(0) + bw.writeInt32(0) + bw.writeInt16(this.rightOperandType) + bw.writeInt8(0) + bw.writeInt8(1) + bw.writeInt32(0) + bw.writeInt32(+(this.rightOperandValue !== null)) + bw.writeInt32(0) + bw.reserveInt32(`ConditionRightOffset${count}`) + bw.writeInt32(0) + bw.writeInt32(0) + bw.writeInt32(0) + bw.writeInt32(0) + bw.writeInt32(0) + conditions.push(this) +} + +function writeStateCondition(this: StateCondition, bw: BinaryWriter, conditions: StateCondition[]) { + writeFormattedStateCondition.call((this as StateCondition).formatCondition(), bw, conditions) +} + +function writeStateConditionFields(this: StateCondition, bw: BinaryWriter, index: number): number { + let count = 0 + if (this.leftOperandValue === null) { + bw.fill(`ConditionLeftOffset${index}`, 0) + } else { + if (this.leftOperandType === OperandType.Literal) { + bw.fill(`ConditionLeftOffset${index}`, bw.position) + bw.writeFloat32(this.leftOperandValue) + } else { + bw.fill(`ConditionLeftOffset${index}`, bw.position) + bw.writeInt32(this.leftOperandValue) + } + count++ + } + if (this.rightOperandValue === null) { + bw.fill(`ConditionRightOffset${index}`, 0) + } else { + if (this.rightOperandType === OperandType.Literal) { + bw.fill(`ConditionRightOffset${index}`, bw.position) + bw.writeFloat32(this.rightOperandValue) + } else { + bw.fill(`ConditionRightOffset${index}`, bw.position) + bw.writeInt32(this.rightOperandValue) + } + count++ + } + return count +} + +function readField(br: BinaryReader, context: any, index: number) { + let ffxField: NumericalField = null + let isInt = false + + if (context?.[0] in PropertyFunction) { + if (context[0] === PropertyFunction.CompCurve) { + isInt = index > 0 && index <= context[1] + 1 + } else if (context[0] !== PropertyFunction.Constant) { + isInt = !index + } + } else if (context instanceof StateCondition) { + if (index === 0) { + isInt = (context.leftOperandType & 3) > 0 + } else { + isInt = (context.rightOperandType & 3) > 0 + } + } else { + const single = br.getFloat32(br.position) + if (single >= 9.99999974737875E-05 && single < 1000000.0 || + single <= -9.99999974737875E-05 && single > -1000000.0 + ) { + ffxField = new FloatField(single) + } else { + isInt = true + } + } + + if (ffxField === null) { + if (isInt) { + ffxField = new IntField(br.getInt32(br.position)) + } else { + ffxField = new FloatField(br.getFloat32(br.position)) + } + } + + br.position += 4 + return ffxField +} + +function readFields(br: BinaryReader, count: number, context: any) { + const ffxFieldList: NumericalField[] = [] + for (let i = 0; i < count; ++i) { + ffxFieldList.push(readField(br, context, i)) + } + return ffxFieldList +} + +function readFieldsAt(br: BinaryReader, offset: number, count: number, context: any) { + br.stepIn(offset) + const ffxFieldList = readFields(br, count, context) + br.stepOut() + return ffxFieldList +} + +function readFieldsWithTypes(br: BinaryReader, count: number, types: any[], context: any): Field[] { + return arrayOf(count, i => { + switch (types[i]) { + case FieldType.Boolean: + return new BoolField(!!br.assertInt32(0, 1)) + case FieldType.Integer: + return new IntField(br.readInt32()) + case FieldType.Float: + return new FloatField(br.readFloat32()) + default: + return readField(br, context, 0) + } + }) +} + +function writeField(this: Field, bw: BinaryWriter) { + switch (this.type) { + case FieldType.Boolean: + return bw.writeInt32(+this.value) + case FieldType.Integer: + return bw.writeInt32(this.value as number) + case FieldType.Float: + return bw.writeFloat32(this.value as number) + default: + throw new Error('Invalid field type: ' + this.type) + } +} + +function modScalarToVec(mod: Modifier, vt: ValueType) { + const cc = vt + 1 + switch (mod.type) { + case ModifierType.Randomizer1: + case ModifierType.Randomizer3: + return new Modifier(mod.type, vt, [ + ...arrayOf(cc, () => new IntField(mod.fields[0].value)), + ...arrayOf(cc, () => new FloatField(mod.fields[1].value)) + ]) + case ModifierType.Randomizer2: + return new Modifier(mod.type, vt, [ + ...arrayOf(cc, () => new IntField(mod.fields[0].value)), + ...arrayOf(cc, () => new FloatField(mod.fields[1].value)), + ...arrayOf(cc, () => new FloatField(mod.fields[2].value)) + ]) + case ModifierType.ExternalValue1: + case ModifierType.ExternalValue2: + let prop = mod.properties[0] + if (prop instanceof ValueProperty) { + prop = new ValueProperty(vt, arrayOf(cc, + () => (prop as ValueProperty).value as number + ) as Vector) + } else if (prop instanceof SequenceProperty) { + prop = new SequenceProperty(vt, prop.function, prop.loop, prop.keyframes.map(kf => + new Keyframe(kf.position, arrayOf(cc, () => kf.value) as Vector) + )) + } else if (prop instanceof ComponentSequenceProperty) { + prop = new ComponentSequenceProperty(vt, prop.loop, + arrayOf(cc, () => (prop as ComponentSequenceProperty).components[0].clone()) + ) + } + return new Modifier(mod.type, vt, mod.fields, [ prop ]) + } +} + +function modMultPropVal(mod: Modifier, v: PropertyValue) { + mod = Modifier.copy(mod) + const cc = mod.valueType + 1 + if (typeof v === 'number') { + switch (mod.type) { + case ModifierType.Randomizer2: + for (let i = cc - 1; i >= 0; i--) { + mod.fields[cc * 2 + i].value *= v + } + case ModifierType.Randomizer1: + for (let i = cc - 1; i >= 0; i--) { + mod.fields[cc + i].value *= v + } + } + } else { + switch (mod.type) { + case ModifierType.Randomizer2: + for (let i = cc - 1; i >= 0; i--) { + mod.fields[cc * 2 + i].value *= v[i] + } + case ModifierType.Randomizer1: + for (let i = cc - 1; i >= 0; i--) { + mod.fields[cc + i].value *= v[i] + } + } + } + return mod +} + +/** + * Multiplies one number, vector, or a property of either kind by another + * number, vector, or property. + * + * Multiplying two vectors of different dimensionalities is not supported, but + * a vector and a scalar will work. + * @param p1 + * @param p2 + * @returns + */ +function anyValueMult(p1: AnyValue, p2: AnyValue): T { + if (p1 instanceof ComponentSequenceProperty) { + p1 = p1.combineComponents() + } + if (p2 instanceof ComponentSequenceProperty) { + p2 = p2.combineComponents() + } + if (typeof p1 === 'number') { + if (typeof p2 === 'number') { + return p1 * p2 as T + } else if (Array.isArray(p2)) { + return p2.map(e => e * p1) as unknown as T + } else if (p2 instanceof ValueProperty) { + return new ValueProperty( + p2.valueType, + anyValueMult(p1, p2.value), + p2.modifiers.map(mod => modMultPropVal(mod, p1)) + ) as unknown as T + } else if (p2 instanceof SequenceProperty) { + return new SequenceProperty( + p2.valueType, + p2.function, + p2.loop, + p2.keyframes.map(kf => new Keyframe( + kf.position, + (Array.isArray(kf.value) ? kf.value.map(e => e * p1) : kf.value * p1) as PropertyValue + )), + p2.modifiers.map(mod => modMultPropVal(mod, p1)) + ) as unknown as T + } + } else if (Array.isArray(p1)) { + if (Array.isArray(p2)) { + return p2.map((e, i) => e * p1[i]) as unknown as T + } else if (p2 instanceof ValueProperty) { + let mods = p2.modifiers + if (p2.valueType === ValueType.Scalar) { + mods = mods.map(mod => modScalarToVec(mod, p1.length - 1)) + } + return new ValueProperty( + p1.length - 1, + anyValueMult(p1, p2.value), + mods.map(mod => modMultPropVal(mod, p1)) + ) as unknown as T + } else if (p2 instanceof SequenceProperty) { + let mods = p2.modifiers + if (p2.valueType === ValueType.Scalar) { + mods = mods.map(mod => modScalarToVec(mod, p1.length - 1)) + } + return new SequenceProperty( + p1.length - 1, + p2.function, + p2.loop, + p2.keyframes.map(kf => new Keyframe( + kf.position, + (Array.isArray(kf.value) ? + kf.value.map((e, i) => e * p1[i]) : + p1.map(e => e * kf.value) + ) as PropertyValue + )), + mods.map(mod => modMultPropVal(mod, p1)) + ) as unknown as T + } + } else if (p1 instanceof ValueProperty) { + if (p2 instanceof ValueProperty) { + let p1Mods = p1.modifiers + let p2Mods = p2.modifiers + const vt = Math.max(p1.valueType, p2.valueType) + if (vt > ValueType.Scalar && p1.valueType === ValueType.Scalar) { + p1Mods = p1Mods.map(mod => modScalarToVec(mod, vt)) + } + if (vt > ValueType.Scalar && p2.valueType === ValueType.Scalar) { + p2Mods = p2Mods.map(mod => modScalarToVec(mod, vt)) + } + return new ValueProperty(vt, anyValueMult(p1.value, p2.value), [ + ...p1Mods.map(mod => modMultPropVal(mod, p2.value)), + ...p2Mods.map(mod => modMultPropVal(mod, p1.value)), + ]) as unknown as T + } else if (p2 instanceof SequenceProperty) { + let p1Mods = p1.modifiers + let p2Mods = p2.modifiers + const vt = Math.max(p1.valueType, p2.valueType) + if (vt > ValueType.Scalar && p1.valueType === ValueType.Scalar) { + p1Mods = p1Mods.map(mod => modScalarToVec(mod, vt)) + } + if (vt > ValueType.Scalar && p2.valueType === ValueType.Scalar) { + p2Mods = p2Mods.map(mod => modScalarToVec(mod, vt)) + } + return new SequenceProperty( + p2.valueType, + p2.function, + p2.loop, + p2.keyframes.map(kf => new Keyframe( + kf.position, + (Array.isArray(kf.value) ? + kf.value.map((e, i) => e * p1.value[i]) : + p1.value.map(e => e * kf.value) + ) as PropertyValue + )), + [ + ...p1Mods.map(mod => modMultPropVal(mod, p2.valueAt(0))), + ...p2Mods.map(mod => modMultPropVal(mod, p1.value)), + ] + ) as unknown as T + } + } else if (p1 instanceof SequenceProperty && p2 instanceof SequenceProperty) { + const positions = new Set + for (const keyframe of p1.keyframes) { + positions.add(keyframe.position) + } + for (const keyframe of p2.keyframes) { + positions.add(keyframe.position) + } + let p1Mods = p1.modifiers + let p2Mods = p2.modifiers + const vt = Math.max(p1.valueType, p2.valueType) + if (vt > ValueType.Scalar && p1.valueType === ValueType.Scalar) { + p1Mods = p1Mods.map(mod => modScalarToVec(mod, vt)) + } + if (vt > ValueType.Scalar && p2.valueType === ValueType.Scalar) { + p2Mods = p2Mods.map(mod => modScalarToVec(mod, vt)) + } + return new LinearProperty( + this.loop, + Array.from(positions) + .sort((a, b) => a - b) + .map(e => new Keyframe(e, + anyValueMult(p1.valueAt(e), p2.valueAt(e)) as PropertyValue + )) + ).withModifiers( + ...p1Mods.map(mod => modMultPropVal(mod, p2.valueAt(0))), + ...p2Mods.map(mod => modMultPropVal(mod, p1.valueAt(0))), + ) as unknown as T + } + // If none of the stuff above returned, p1 is more complex than p2, so just + // swap them and return the result of that instead. + return anyValueMult(p2, p1) +} + +function getActionGameData(type: ActionType, game: Game) { + const adt = ActionData[type] + if (!(game in adt.games)) { + throw new Error(`${ActionType[type]} does not have game data for ${Game[game]}! This either means that the game does not support this type of action, or that the library is missing data for this action in that game for some other reason.`) + } + const data = {...((typeof adt.games[game] === 'number' ? adt.games[adt.games[game] as number] : adt.games[game]) as ActionGameDataEntry)} + data.fields1 ??= [] + data.fields2 ??= [] + data.properties1 ??= [] + data.properties2 ??= [] + if (typeof data.fields1 === 'number') { + data.fields1 = (adt.games[data.fields1] as ActionGameDataEntry).fields1 + } + if (typeof data.fields2 === 'number') { + data.fields2 = (adt.games[data.fields2] as ActionGameDataEntry).fields2 + } + if (typeof data.properties1 === 'number') { + data.properties1 = (adt.games[data.properties1] as ActionGameDataEntry).properties1 + } + if (typeof data.properties2 === 'number') { + data.properties2 = (adt.games[data.properties2] as ActionGameDataEntry).properties2 + } + return data as { + fields1: string[] + fields2: string[] + properties1: string[] + properties2: string[] + } +} + +function steppedToLinearProperty(prop: SequenceProperty) { + return new LinearProperty(prop.loop, prop.keyframes.flatMap((kf, i, a) => [ + new Keyframe(i === 0 ? 0 : a[i].position - 0.001, kf.value), + Keyframe.copy(kf) + ]).slice(1, -1)) +} + +function combineComponents(...comps: ScalarValue[]): VectorValue { + comps = comps.map(c => c instanceof SequenceProperty && c.function === PropertyFunction.Stepped ? steppedToLinearProperty(c) : c) + if (!comps.some(c => c instanceof Property)) { + return comps as Vector + } + const positions = new Set + for (const comp of comps) { + if (comp instanceof SequenceProperty) { + for (const keyframe of comp.keyframes) { + positions.add(keyframe.position) + } + } else if (comp instanceof ComponentSequenceProperty) { + for (const keyframe of comp.components[0].keyframes) { + positions.add(keyframe.position) + } + } + } + const keyframes = Array.from(positions).sort((a, b) => a - b) + .map(e => new Keyframe(e, comps.map(c => c instanceof Property ? c.valueAt(e) : c) as Vector)) + const vt: ValueType = comps.length - 1 + return new LinearProperty(this.loop, keyframes).withModifiers(...comps.flatMap((c, i) => { + if (typeof c === 'number') { + return [] + } + return c.modifiers.map(mod => { + switch (mod.type) { + case ModifierType.ExternalValue1: + case ModifierType.ExternalValue2: + return new Modifier(mod.type, vt, mod.fields.map(e => Field.copy(e)), [ + combineComponents(...arrayOf(comps.length, j => j === i ? mod.properties[0] as ScalarProperty : 1)) as VectorProperty + ]) + case ModifierType.Randomizer1: + case ModifierType.Randomizer3: + return new Modifier(mod.type, vt, [ + ...arrayOf(comps.length, j => j === i ? Field.copy(mod.fields[0]) : new IntField), + ...arrayOf(comps.length, j => j === i ? Field.copy(mod.fields[1]) : new FloatField), + ]) + case ModifierType.Randomizer2: + return new Modifier(mod.type, vt, [ + ...arrayOf(comps.length, j => j === i ? Field.copy(mod.fields[0]) : new IntField), + ...arrayOf(comps.length, j => j === i ? Field.copy(mod.fields[1]) : new FloatField), + ...arrayOf(comps.length, j => j === i ? Field.copy(mod.fields[2]) : new FloatField), + ]) + } + }) + })) as VectorProperty +} + +function separateComponents(value: VectorValue): ScalarValue[] { + if (value instanceof Property) { + return value.separateComponents() + } else { + return value + } +} + +const ActionDataConversion = { + [ActionType.SFXReference]: { + read(props: { sfx: number }, game: Game) { + return props.sfx + } + }, + [ActionType.PointLight]: { + read(props: PointLightParams, game: Game) { + props.fadeOutTime = props.fadeOutTime / 30 + return props + }, + write(props: PointLightParams, game: Game) { + props.fadeOutTime = Math.round(props.fadeOutTime * 30) + return props + } + }, + [ActionType.NodeWindSpeed]: { + fallback(action: NodeWindSpeed, game: Game) { + return new Action + } + }, + [ActionType.ParticleWindSpeed]: { + fallback(action: ParticleWindSpeed, game: Game) { + return new Action + } + }, + [ActionType.NodeWindAcceleration]: { + fallback(action: NodeWindAcceleration, game: Game) { + return new Action + } + }, + [ActionType.ParticleWindAcceleration]: { + fallback(action: ParticleWindAcceleration, game: Game) { + return new Action + } + }, + [ActionType.RichModel]: { + read(props: any, game: Game) { + if (game === Game.EldenRing) { + props.uvOffset = combineComponents(props.uOffset, props.vOffset) + props.uvSpeed = combineComponents(props.uSpeed, props.vSpeed) + props.uvSpeedMultiplier = combineComponents(props.uSpeedMultiplier, props.vSpeedMultiplier) + } + return props + }, + write(props: any, game: Game) { + if (game === Game.EldenRing) { + ;[props.uOffset, props.vOffset] = separateComponents(props.uvOffset) + ;[props.uSpeed, props.vSpeed] = separateComponents(props.uvSpeed) + ;[props.uSpeedMultiplier, props.vSpeedMultiplier] = separateComponents(props.uvSpeedMultiplier) + } + return props + } + }, + [ActionType.SpotLight]: { + read(props: SpotLightParams, game: Game) { + props.fadeOutTime = props.fadeOutTime / 30 + if (game === Game.DarkSouls3) { + props.diffuseColor = anyValueMult(1/255, props.diffuseColor) as Vector4Value + props.specularColor = anyValueMult(1/255, props.specularColor) as Vector4Value + props.diffuseMultiplier = 255/100 + props.specularMultiplier = 255/100 + } else { + props.diffuseMultiplier = anyValueMult(1/100, props.diffuseMultiplier) as ScalarValue + props.specularMultiplier = anyValueMult(1/100, props.specularMultiplier) as ScalarValue + } + return props + }, + write(props: SpotLightParams, game: Game) { + props.fadeOutTime = Math.round(props.fadeOutTime * 30) + if (game === Game.DarkSouls3) { + props.diffuseColor = anyValueMult(anyValueMult(100, props.diffuseMultiplier), props.diffuseColor) as Vector4Value + if (!props.separateSpecular) { + props.specularColor = props.diffuseColor instanceof Property ? props.diffuseColor.clone() : props.diffuseColor + } else { + props.specularColor = anyValueMult(anyValueMult(100, props.specularMultiplier), props.specularColor) as Vector4Value + } + } else { + props.diffuseMultiplier = anyValueMult(100, props.diffuseMultiplier) as ScalarValue + props.specularMultiplier = anyValueMult(100, props.specularMultiplier) as ScalarValue + } + return props + } + } +} + +export type TypedArray = + | Int8Array + | Uint8Array + | Uint8ClampedArray + | Int16Array + | Uint16Array + | Int32Array + | Uint32Array + | Float32Array + | Float64Array + | BigInt64Array + | BigUint64Array + +class BinaryReader extends DataView { + + position: number = 0 + littleEndian: boolean = true + steps: number[] = [] + + getInt16(offset: number) { + return super.getInt16(offset, this.littleEndian) + } + + getUint16(offset: number) { + return super.getUint16(offset, this.littleEndian) + } + + getInt32(offset: number) { + return super.getInt32(offset, this.littleEndian) + } + + getUint32(offset: number) { + return super.getUint32(offset, this.littleEndian) + } + + getFloat32(offset: number) { + return super.getFloat32(offset, this.littleEndian) + } + + getFloat64(offset: number) { + return super.getFloat64(offset, this.littleEndian) + } + + readUint8() { + return this.getUint8(this.position++) + } + + readBool() { + const b = this.readUint8() + if (b <= 1) { + return !!b + } else { + throw new Error(`readBool encountered non-boolean value: 0x${b.toString(16).padStart(2, '0')}`) + } + } + + readInt16() { + const value = this.getInt16(this.position) + this.position += 2 + return value + } + + readUint16() { + const value = this.getUint16(this.position) + this.position += 2 + return value + } + + readInt32() { + const value = this.getInt32(this.position) + this.position += 4 + return value + } + + readUint32() { + const value = this.getUint32(this.position) + this.position += 4 + return value + } + + readFloat32() { + const value = this.getFloat32(this.position) + this.position += 4 + return value + } + + getInt32s(offset: number, count: number) { + return arrayOf(count, i => this.getInt32(offset + i * 4)) + } + + assertUint8(...ui8s: number[]) { + const ocp = this.position + const value = this.readUint8() + for (const ui8 of ui8s) { + if (value === ui8) return value + } + throw new Error(`Read: ${value} | Expected: ${ui8s.join(', ')} | Position: ${ocp}`) + } + + assertInt16(...i16s: number[]) { + const ocp = this.position + const value = this.readInt16() + for (const i16 of i16s) { + if (value === i16) return value + } + throw new Error(`Read: ${value} | Expected: ${i16s.join(', ')} | Position: ${ocp}`) + } + + assertUint16(...ui16s: number[]) { + const ocp = this.position + const value = this.readUint16() + for (const ui16 of ui16s) { + if (value === ui16) return value + } + throw new Error(`Read: ${value} | Expected: ${ui16s.join(', ')} | Position: ${ocp}`) + } + + assertInt32(...i32s: number[]) { + const ocp = this.position + const value = this.readInt32() + for (const i32 of i32s) { + if (value === i32) return value + } + throw new Error(`Read: ${value} | Expected: ${i32s.join(', ')} | Position: ${ocp}`) + } + + assertUint32(...ui32s: number[]) { + const ocp = this.position + const value = this.readUint32() + for (const ui32 of ui32s) { + if (value === ui32) return value + } + throw new Error(`Read: ${value} | Expected: ${ui32s.join(', ')} | Position: ${ocp}`) + } + + assertASCII(ascii: string) { + const ocp = this.position + const value: string = String.fromCharCode.apply(null, ascii.split('').map(() => this.readUint8())) + if (value !== ascii) { + throw new Error(`Read: ${value} | Expected: ${ascii} | Position: ${ocp}`) + } + return value + } + + stepIn(offset: number) { + this.steps.push(this.position) + this.position = offset + } + + stepOut() { + if (this.steps.length === 0) { + throw new Error('Reader is already stepped all the way out.') + } + this.position = this.steps.pop() ?? 0 + } + +} + +interface ReservationList { + [name: string]: { offset: number, size: 1 | 2 | 4, func?: string } +} + +class BinaryWriter { + + static #te = new TextEncoder + + littleEndian: boolean + array: number[] = [] + reservations: ReservationList = {} + + #transBuf = new ArrayBuffer(4) + #transDV = new DataView(this.#transBuf) + #transArr16 = new Int8Array(this.#transBuf, 0, 2) + #transArr32 = new Int8Array(this.#transBuf, 0, 4) + + convertedDataActions = new Map + + constructor(littleEndian: boolean = true) { + this.littleEndian = littleEndian + } + + get position() { + return this.array.length + } + + writeBool(b: boolean) { + this.array.push(+b) + } + + writeInt8(i8: number) { + this.array.push(i8) + } + + writeUint8(ui8: number) { + this.array.push(ui8) + } + + writeInt16(i16: number) { + this.#transDV.setInt16(0, i16, this.littleEndian) + this.array.push(...this.#transArr16) + } + + writeUint16(ui16: number) { + this.#transDV.setUint16(0, ui16, this.littleEndian) + this.array.push(...this.#transArr16) + } + + writeInt32(i32: number) { + this.#transDV.setInt32(0, i32, this.littleEndian) + this.array.push(...this.#transArr32) + } + + writeUint32(ui32: number) { + this.#transDV.setUint32(0, ui32, this.littleEndian) + this.array.push(...this.#transArr32) + } + + writeFloat32(f32: number) { + // Make sure that the value is not -0 + f32 = f32 === 0 ? 0 : f32 + this.#transDV.setFloat32(0, f32, this.littleEndian) + this.array.push(...this.#transArr32) + } + + writeInt8s(i8s: number[]) { + for (const i8 of i8s) this.writeInt8(i8) + } + + writeUint8s(ui8s: number[]) { + for (const ui8 of ui8s) this.writeUint8(ui8) + } + + writeInt16s(i16s: number[]) { + for (const i16 of i16s) this.writeInt16(i16) + } + + writeUint16s(ui16s: number[]) { + for (const ui16 of ui16s) this.writeUint16(ui16) + } + + writeInt32s(i32s: number[]) { + for (const i32 of i32s) this.writeInt32(i32) + } + + writeUint32s(ui32s: number[]) { + for (const ui32 of ui32s) this.writeUint32(ui32) + } + + writeFloat32s(f32s: number[]) { + for (const f32 of f32s) this.writeFloat32(f32) + } + + writeString(s: string) { + this.array.push(...BinaryWriter.#te.encode(s)) + } + + reserveInt8(name: string) { + this.reservations[name] = { + offset: this.array.length, + size: 1 + } + this.writeInt8(0) + } + + reserveUint8(name: string) { + this.reservations[name] = { + offset: this.array.length, + size: 1 + } + this.writeUint8(0) + } + + reserveInt16(name: string) { + this.reservations[name] = { + offset: this.array.length, + size: 2, + func: 'setInt16' + } + this.writeInt16(0) + } + + reserveUint16(name: string) { + this.reservations[name] = { + offset: this.array.length, + size: 2, + func: 'setUint16' + } + this.writeUint16(0) + } + + reserveInt32(name: string) { + this.reservations[name] = { + offset: this.array.length, + size: 4, + func: 'setInt32' + } + this.writeInt32(0) + } + + reserveUint32(name: string) { + this.reservations[name] = { + offset: this.array.length, + size: 4, + func: 'setUint32' + } + this.writeUint32(0) + } + + reserveFloat32(name: string) { + this.reservations[name] = { + offset: this.array.length, + size: 4, + func: 'setFloat32' + } + this.writeFloat32(0) + } + + fill(name: string, value: number) { + if (!(name in this.reservations)) { + throw new Error('Key is not reserved: ' + name) + } + const reservation = this.reservations[name] + switch (reservation.size) { + case 1: { + this.array.splice(reservation.offset, 1, value) + break + } + case 2: { + this.#transDV[reservation.func as string](0, value, this.littleEndian) + this.array.splice(reservation.offset, 2, ...this.#transArr16) + break + } + case 4: { + this.#transDV[reservation.func as string](0, value, this.littleEndian) + this.array.splice(reservation.offset, 4, ...this.#transArr32) + break + } + } + delete this.reservations[name] + } + + pad(align: number) { + while (this.array.length % align > 0) this.writeInt8(0) + } + + getArrayBuffer() { + return new Uint8Array(this.array).buffer + } + +} + +class FXR { + + id: number + + states: State[] + root: RootNode | GenericNode + + references: number[] + externalValues: number[] + unkBloodEnabler: number[] + // unkEmpty: number[] + + /** + * Creates a new effects resource (FXR) for FromSoftware's game engine. + */ + constructor( + id: number, + root: RootNode | GenericNode = new RootNode, + states: State[] = [ new State ], + references: number[] = [], + externalValues: number[] = [], + unkBloodEnabler: number[] = [], + // unkEmpty: number[] = [], + ) { + this.id = id + this.root = root + this.states = states + this.references = references + this.externalValues = externalValues + this.unkBloodEnabler = unkBloodEnabler + // this.unkEmpty = unkEmpty + } + + /** + * Parses an FXR file. + * @param buffer ArrayBuffer or TypedArray containing the contents of the FXR file. + */ + static read(buffer: ArrayBuffer | TypedArray, game: Game = Game.EldenRing) { + if (!(buffer instanceof ArrayBuffer)) { + buffer = buffer.buffer + } + const br = new BinaryReader(buffer) + + br.assertASCII('FXR\0') + br.assertInt16(0) + const version = br.assertInt16( + FXRVersion.DarkSouls3, + FXRVersion.Sekiro + ) + br.assertInt32(1) + const id = br.readInt32() + const stateListOffset = br.readInt32() + br.assertInt32(1) // StateMachineCount + br.position += 4 * 4 + // br.readInt32() // StateOffset + // br.readInt32() // StateCount + // br.readInt32() // ConditionOffset + // br.readInt32() // ConditionCount + const nodeOffset = br.readInt32() + br.position += 15 * 4 + // br.readInt32() // NodeCount + // br.readInt32() // EffectOffset + // br.readInt32() // EffectCount + // br.readInt32() // ActionOffset + // br.readInt32() // ActionCount + // br.readInt32() // PropertyOffset + // br.readInt32() // PropertyCount + // br.readInt32() // Section8Offset + // br.readInt32() // Section8Count + // br.readInt32() // Section9Offset + // br.readInt32() // Section9Count + // br.readInt32() // Section10Offset + // br.readInt32() // Section10Count + // br.readInt32() // FieldOffset + // br.readInt32() // FieldCount + br.assertInt32(1) + br.assertInt32(0) + + let references: number[] = [] + let externalValues: number[] = [] + let unkBloodEnabler: number[] = [] + // let unkEmpty: number[] = [] + + if (version === FXRVersion.Sekiro) { + const referencesOffset = br.readInt32() + const referencesCount = br.readInt32() + const externalValuesOffset = br.readInt32() + const externalValuesCount = br.readInt32() + const unkBloodEnablerOffset = br.readInt32() + const unkBloodEnablerCount = br.readInt32() + br.readInt32() + br.assertInt32(0) + // const unkEmptyOffset = br.readInt32() + // const unkEmptyCount = br.readInt32() + + references = br.getInt32s(referencesOffset, referencesCount) + externalValues = br.getInt32s(externalValuesOffset, externalValuesCount) + unkBloodEnabler = br.getInt32s(unkBloodEnablerOffset, unkBloodEnablerCount) + // unkEmpty = br.getInt32s(unkEmptyOffset, unkEmptyCount) + } + + br.position = stateListOffset + br.assertInt32(0) + const stateCount = br.readInt32() + const statesOffset = br.readInt32() + br.assertInt32(0) + br.stepIn(statesOffset) + const states: State[] = [] + for (let i = 0; i < stateCount; ++i) { + states.push(readState(br)) + } + br.stepOut() + + br.position = nodeOffset + const rootNode = readNode(br, game) as RootNode | GenericNode + + return new FXR( + id, + rootNode, + states, + references, + externalValues, + unkBloodEnabler, + // unkEmpty, + ) + } + + /** + * Serialize to the FXR file format. + * @returns ArrayBuffer containing the contents of the FXR file. + */ + toArrayBuffer(game: Game = Game.EldenRing) { + const version = GameVersionMap[game] + const bw = new BinaryWriter + bw.writeString('FXR\0') + bw.writeInt16(0) + bw.writeUint16(version) + bw.writeInt32(1) + bw.writeInt32(this.id) + bw.reserveInt32('StateListOffset') + bw.writeInt32(1) + bw.reserveInt32('StatesOffset1') + bw.writeInt32(this.states.length) + bw.reserveInt32('ConditionOffset') + bw.reserveInt32('ConditionCount') + bw.reserveInt32('NodeOffset') + bw.reserveInt32('NodeCount') + bw.reserveInt32('EffectOffset') + bw.reserveInt32('EffectCount') + bw.reserveInt32('ActionOffset') + bw.reserveInt32('ActionCount') + bw.reserveInt32('PropertyOffset') + bw.reserveInt32('PropertyCount') + bw.reserveInt32('Section8Offset') + bw.reserveInt32('Section8Count') + bw.reserveInt32('Section9Offset') + bw.reserveInt32('Section9Count') + bw.reserveInt32('Section10Offset') + bw.reserveInt32('Section10Count') + bw.reserveInt32('FieldOffset') + bw.reserveInt32('FieldCount') + bw.writeInt32(1) + bw.writeInt32(0) + + if (version === FXRVersion.Sekiro) { + bw.reserveInt32('ReferencesOffset') + bw.writeInt32(this.references.length) + bw.reserveInt32('ExternalValuesOffset') + bw.writeInt32(this.externalValues.length) + bw.reserveInt32('UnkBloodEnablerOffset') + bw.writeInt32(this.unkBloodEnabler.length) + // bw.reserveInt32('UnkEmptyOffset') + // bw.writeInt32(this.unkEmpty.length) + bw.writeInt32(0) + bw.writeInt32(0) + } + + bw.fill('StateListOffset', bw.position) + bw.writeInt32(0) + bw.writeInt32(this.states.length) + bw.reserveInt32('StatesOffset2') + bw.writeInt32(0) + bw.pad(16) + bw.fill('StatesOffset1', bw.position) + bw.fill('StatesOffset2', bw.position) + for (let i = 0; i < this.states.length; ++i) { + writeState.call(this.states[i], bw, i) + } + + bw.pad(16) + bw.fill('ConditionOffset', bw.position) + const conditions: StateCondition[] = [] + for (let i = 0; i < this.states.length; ++i) { + writeStateConditions.call(this.states[i], bw, i, conditions) + } + bw.fill('ConditionCount', conditions.length) + bw.pad(16) + bw.fill('NodeOffset', bw.position) + const nodes: Node[] = [] + writeNode.call(this.root, bw, game, nodes) + writeNodeChildren.call(this.root, bw, game, nodes) + bw.fill('NodeCount', nodes.length) + bw.pad(16) + bw.fill('EffectOffset', bw.position) + let counter = { value: 0 } + for (let i = 0; i < nodes.length; ++i) { + writeNodeEffects.call(nodes[i], bw, game, i, counter) + } + bw.fill('EffectCount', counter.value) + bw.pad(16) + bw.fill('ActionOffset', bw.position) + counter.value = 0 + const actions: Action[] = [] + for (let i = 0; i < nodes.length; ++i) { + writeNodeActions.call(nodes[i], bw, game, i, counter, actions) + } + bw.fill('ActionCount', actions.length) + bw.pad(16) + bw.fill('PropertyOffset', bw.position) + const properties: IModifiableProperty[] = [] + for (let i = 0; i < actions.length; ++i) { + writeAnyActionProperties.call(actions[i], bw, game, i, properties) + } + bw.fill('PropertyCount', properties.length) + bw.pad(16) + bw.fill('Section8Offset', bw.position) + const modifiers: Modifier[] = [] + for (let i = 0; i < properties.length; ++i) { + writePropertyModifiers(properties[i], bw, i, modifiers) + } + bw.fill('Section8Count', modifiers.length) + bw.pad(16) + bw.fill('Section9Offset', bw.position) + const modProps: Property[] = [] + for (let i = 0; i < modifiers.length; ++i) { + writeModifierProperties.call(modifiers[i], bw, i, modProps) + } + bw.fill('Section9Count', modProps.length) + bw.pad(16) + bw.fill('Section10Offset', bw.position) + const section10s: Section10[] = [] + for (let i = 0; i < actions.length; ++i) { + writeAnyActionSection10s.call(actions[i], bw, i, section10s) + } + bw.fill('Section10Count', section10s.length) + bw.pad(16) + bw.fill('FieldOffset', bw.position) + let fieldCount = 0 + for (let i = 0; i < conditions.length; ++i) { + fieldCount += writeStateConditionFields.call(conditions[i], bw, i) + } + for (let i = 0; i < actions.length; ++i) { + fieldCount += writeAnyActionFields.call(actions[i], bw, game, i) + } + for (let i = 0; i < properties.length; ++i) { + fieldCount += writePropertyFields(properties[i], bw, i, false) + } + for (let i = 0; i < modifiers.length; ++i) { + fieldCount += writeModifierFields.call(modifiers[i], bw, i) + } + for (let i = 0; i < modProps.length; ++i) { + fieldCount += writePropertyFields(modProps[i], bw, i, true) + } + for (let i = 0; i < section10s.length; ++i) { + fieldCount += writeSection10Fields.call(section10s[i], bw, i) + } + bw.fill('FieldCount', fieldCount) + bw.pad(16) + + if (version !== FXRVersion.Sekiro) { + return bw.getArrayBuffer() + } + + bw.fill('ReferencesOffset', bw.position) + bw.writeInt32s(this.references) + bw.pad(16) + + bw.fill('ExternalValuesOffset', bw.position) + bw.writeInt32s(this.externalValues) + bw.pad(16) + + bw.fill('UnkBloodEnablerOffset', bw.position) + bw.writeInt32s(this.unkBloodEnabler) + bw.pad(16) + + // if (this.unkEmpty.length > 0) { + // bw.fill('UnkEmptyOffset', bw.position) + // bw.writeInt32s(this.unkEmpty) + // bw.pad(16) + // } else { + // bw.fill('UnkEmptyOffset', 0) + // } + + return bw.getArrayBuffer() + } + + static fromJSON({ + id, + states, + root, + references, + externalValues, + unkBloodEnabler + }: { + id: number + states: string[] + root: any + references: number[] + externalValues: number[] + unkBloodEnabler: number[] + unkEmpty: number[] + }) { + return new FXR( + id, + Node.fromJSON(root) as RootNode | GenericNode, + states.map(state => State.from(state)), + references, + externalValues, + unkBloodEnabler + ) + } + + toJSON() { + return { + id: this.id, + states: this.states.map(state => state.toString()), + references: this.references, + externalValues: this.externalValues, + unkBloodEnabler: this.unkBloodEnabler, + root: this.root.toJSON(), + } + } + + /** + * Creates a minified version of this FXR. + * + * The minified FXR might result in a smaller file size, but should be + * functionally identical to the FXR it was made from. + */ + minify() { + return new FXR( + this.id, + this.root.minify() as RootNode | GenericNode, + this.states, + this.references, + this.externalValues, + this.unkBloodEnabler + ) + } + + /** + * Updates {@link references}, {@link externalValues}, and + * {@link unkBloodEnabler} with the values used in the FXR. + */ + updateReferences() { + const references: number[] = [] + const externalValues: number[] = [] + let unkBloodEnabler = false + for (const node of this.root.walk()) { + if (node instanceof ProxyNode) { + references.push(node.sfx) + } + } + for (const prop of this.root.walkProperties()) { + for (const mod of prop.modifiers) { + if (mod.type === ModifierType.ExternalValue1) { + externalValues.push(mod.fields[0].value as number) + } else if (mod.type === ModifierType.ExternalValue2 && mod.fields[0].value === ExternalValue.DisplayBlood) { + unkBloodEnabler = true + } + } + } + for (const state of this.states) { + for (const condition of state.conditions) { + if (condition.leftOperandType === OperandType.External) { + externalValues.push(condition.leftOperandValue as number) + } + if (condition.rightOperandType === OperandType.External) { + externalValues.push(condition.rightOperandValue as number) + } + } + } + this.references = uniqueArray(references).sort((a, b) => a - b) + this.externalValues = uniqueArray(externalValues).sort((a, b) => a - b) + this.unkBloodEnabler = unkBloodEnabler ? [ExternalValue.DisplayBlood] : [] + return this + } + + /** + * Lists all resources (textures, models, animations, sounds) used in the + * FXR. Useful for finding out what resources must exist for the effect to + * work correctly, which is often needed when converting from one game to + * another. + */ + getResources() { + const list = [] + for (const effect of this.root.walkEffects()) if (effect instanceof BasicEffect) { + if (effect.audio instanceof PlaySound) { + list.push(effect.audio.sound) + } + const action = effect.appearance + if (action instanceof PointSprite) { + list.push(action.texture) + } else if (action instanceof BillboardEx) { + list.push(action.texture) + list.push(action.normalMap) + list.push(action.specular) + } else if (action instanceof MultiTextureBillboardEx) { + list.push(action.mask) + list.push(action.layer1) + list.push(action.layer2) + list.push(action.specular) + } else if (action instanceof Model || action instanceof RichModel) { + list.push(action.model) + list.push(action.animation) + } else if (action instanceof Tracer || action instanceof DynamicTracer) { + list.push(action.texture) + list.push(action.normalMap) + } else if (action instanceof Distortion) { + list.push(action.texture) + list.push(action.normalMap) + list.push(action.mask) + } else if (action instanceof RadialBlur) { + list.push(action.mask) + } else if (action instanceof Action) switch (action.type) { + case ActionType.Unk10000_StandardParticle: + case ActionType.Unk10001_StandardCorrectParticle: + list.push(action.fields1[1].value) + list.push(action.fields1[2].value) + list.push(action.fields1[3].value) + break + case ActionType.Unk10014_LensFlare: + list.push(action.fields1[0].value) + list.push(action.fields1[1].value) + break + } + } + return uniqueArray(list.map(e => { + if (e instanceof Property) { + const obj = e.toJSON() + if (typeof obj === 'number') { + return obj + } else { + return JSON.stringify(obj) + } + } else { + return e + } + }).filter(e => e !== 0)).map(e => { + if (typeof e === 'string') { + return Property.fromJSON(JSON.parse(e)) + } else { + return e + } + }).sort((a, b) => { + if (a instanceof Property) { + if (b instanceof Property) { + return a.valueAt(0) - b.valueAt(0) + } else { + return a.valueAt(0) - b + } + } else { + if (b instanceof Property) { + return a - b.valueAt(0) + } else { + return a - b + } + } + }) + } + +} + +class State { + + conditions: StateCondition[] + + constructor(conditions: StateCondition[] = []) { + this.conditions = conditions + } + + /** + * Parses a logical expression in a string and creates a + * {@link State} from it. + * @param stateString A logical expression comprised of one or more + * conditions separated by `&&`. The state may only be active if all of its + * conditions are true. + * + * Syntax: + * ```text + * stateString = [ && [...]] + * ``` + * See {@link StateCondition.from} for more information about + * `conditionExpression`. + * + * Examples: + * ```text + * time < 0.5 else 1 && ext(2000) == 2 + * ext(0) < 1 && time < 2 && 1 == 1 + * ``` + * @returns The new state. + */ + static from(stateString: string) { + return new State(stateString.split('&&').filter(e => e.trim().length > 0).map(e => StateCondition.from(e))) + } + + toString() { + return this.conditions.map(c => c.toString()).join(' && ') + } + +} + +class StateCondition { + + /** + * A condition for a state. The state remains active if all of its conditions + * are true or if it has no conditions. If the condition is false, the state + * is deactivated and the {@link nextState next state} is activated. + * @param operator Controls what operation should be used for the condition. + * @param unk1 Unknown. Seems to always be 2 in vanilla Elden Ring. 3 seems + * to make the condition always true. + * @param nextState If the condition is false, the state at this index will + * be checked instead. Set it to -1 to disable the node if the condition + * is false. + * @param leftOperandType Controls what type of value the operand to the left + * of the operator should be. + * @param leftOperandValue This does different things depending on the + * {@link leftOperandType}: + * - {@link OperandType.Literal}: This value is the operand's value. + * - {@link OperandType.External}: This value refers to an external value to + * use as the operand's value. + * - {@link OperandType.UnkMinus2}: This value is ignored and should be null. + * - {@link OperandType.StateTime}: This value is ignored and should be null. + * @param rightOperandType Controls what type of value the operand to the + * right of the operator should be. + * @param rightOperandValue This does different things depending on the + * {@link rightOperandType}: + * - {@link OperandType.Literal}: This value is the operand's value. + * - {@link OperandType.External}: This value refers to an external value to + * use as the operand's value. + * - {@link OperandType.UnkMinus2}: This value is ignored and should be null. + * - {@link OperandType.StateTime}: This value is ignored and should be null. + */ + constructor( + public operator: Operator, + public unk1: number, + public nextState: number, + public leftOperandType: OperandType, + public leftOperandValue: number | null, + public rightOperandType: OperandType, + public rightOperandValue: number | null, + ) {} + + static #reExpression = /^\s*(?(?:state)?time|(?:unk)?minus2|ext(?:ernal)?\(\d+\)|-?\d+(?:\.\d+)?|-?\.\d+)\s*(?==?|<=?|>=?|!=)\s*(?(?:state)?time|(?:unk)?minus2|ext(?:ernal)?\(\d+\)|-?\d+(?:\.\d+)?|-?\.\d+)\s*(?:else(?:\sgoto)?\s+(?-?\d+|none))?\s*$/i + static #reLiteralOperand = /^-?\d+(?:\.\d+)?|-?\.\d+$/ + static #reExternalOperand = /^[Ee]xt(?:ernal)?\((\d+)\)$/ + + static #parseOperand(op: string) { + switch (op.toLowerCase()) { + case 'time': + case 'statetime': + return { + type: OperandType.StateTime, + value: null + } + case 'minus2': + case 'unkminus2': + return { + type: OperandType.UnkMinus2, + value: null + } + default: if (this.#reLiteralOperand.test(op)) { + return { + type: OperandType.Literal, + value: parseFloat(op) + } + } else { + return { + type: OperandType.External, + value: parseInt(op.match(this.#reExternalOperand)[1]) + } + } + } + } + + /** + * Parses a logical expression in a string and creates a + * {@link StateCondition} from it. + * @param expression A string with a logical expression and optionally an + * `else` statement with a state index. + * + * ## Syntax: + * ```text + * expression = [ else[ goto] ] + * operand = | External() | StateTime | UnkMinus2 + * operator = != | == | > | >= | < | <= + * stateIndex = | none + * ``` + * + * `External`, `StateTime`, and `UnkMinus2` are all case-insensitive and have + * shorter variations available. Here are some examples: + * ```text + * ext(0) + * stateTime + * time + * minus2 + * ``` + * + * ## Examples: + * ```text + * ext(0) > 1 + * time < 5 else goto 2 + * 1 != External(10000) else 1 + * ``` + * + * @returns A new {@link StateCondition} based on the expression. + */ + static from(expression: string) { + const m = expression.match(this.#reExpression) + if (m === null) { + throw new Error('Syntax error in condition expression: ' + expression) + } + let op: Operator + switch (m.groups.op) { + case '!=': op = Operator.NotEqual; break; + case '=': + case '==': op = Operator.Equal; break; + case '<': op = Operator.LessThan; break; + case '>': op = Operator.GreaterThan; break; + case '<=': op = Operator.LessThanOrEqual; break; + case '>=': op = Operator.GreaterThanOrEqual; break; + } + const left = this.#parseOperand(m.groups.left) + const right = this.#parseOperand(m.groups.right) + let nextState = -1 + if ('else' in m.groups) { + switch (m.groups.else) { + case '-1': + case 'none': + case undefined: + break + default: + nextState = parseInt(m.groups.else) + break + } + } + return new StateCondition(op, 2, nextState, left.type, left.value, right.type, right.value) + } + + /** + * Swaps the operands and changes the operator to match if the left operand + * is a literal value and the right operand is a non-literal value. This + * makes it a bit easier to read the expression, but doesn't affect + * functionality. + */ + sortOperands() { + if (this.leftOperandType !== OperandType.Literal || this.rightOperandType === OperandType.Literal) { + return this + } + ;[ + this.leftOperandType, + this.leftOperandValue, + this.rightOperandType, + this.rightOperandValue, + ] = [ + this.rightOperandType, + this.rightOperandValue, + this.leftOperandType, + this.leftOperandValue, + ] + switch (this.operator) { + case Operator.GreaterThan: this.operator = Operator.LessThan; break; + case Operator.GreaterThanOrEqual: this.operator = Operator.LessThanOrEqual; break; + case Operator.LessThan: this.operator = Operator.GreaterThan; break; + case Operator.LessThanOrEqual: this.operator = Operator.GreaterThanOrEqual; break; + } + return this + } + + /** + * The {@link Operator.LessThanOrEqual LessThanOrEqual} and + * {@link Operator.LessThan LessThan} operators are not valid operators in + * the FXR format. This method returns an equivalent condition that *is* + * valid. + */ + formatCondition() { + if (this.operator !== Operator.LessThan && this.operator !== Operator.LessThanOrEqual) { + return this + } + return new StateCondition( + this.operator - 2, this.unk1, this.nextState, + this.rightOperandType, this.rightOperandValue, + this.leftOperandType, this.leftOperandValue + ) + } + + clone() { + return new StateCondition( + this.operator, this.unk1, this.nextState, + this.leftOperandType, this.leftOperandValue, + this.rightOperandType, this.rightOperandValue + ) + } + + #toString() { + let left: string | number, right: string | number + switch (this.leftOperandType) { + case OperandType.External: + left = `External(${this.leftOperandValue})` + break + case OperandType.UnkMinus2: + case OperandType.StateTime: + left = OperandType[this.leftOperandType] + break + case OperandType.Literal: + left = this.leftOperandValue + break + } + switch (this.rightOperandType) { + case OperandType.External: + right = `External(${this.rightOperandValue})` + break + case OperandType.UnkMinus2: + case OperandType.StateTime: + right = OperandType[this.rightOperandType] + break + case OperandType.Literal: + right = this.rightOperandValue + break + } + return `${left} ${['!=','==','>=','>','<=','<'][this.operator]} ${right} else ${this.nextState}` + } + + toString() { + return this.clone().sortOperands().#toString() + } + +} + +/** + * The base class for all nodes. + * + * A node is a container with actions, effects, and other nodes, and they form + * the tree structure of the FXR. + */ +abstract class Node { + + constructor(public readonly type: NodeType) {} + + abstract getActions(game: Game): IAction[] + getEffects(game: Game): IEffect[] { return [] } + getNodes(game: Game): Node[] { return [] } + abstract toJSON(): any + minify(): Node { return this } + + static fromJSON(obj: any): Node { + switch (obj.type) { + case NodeType.Root: + return RootNode.fromJSON(obj) + case NodeType.Proxy: + return ProxyNode.fromJSON(obj) + case NodeType.LevelOfDetail: + return LevelOfDetailNode.fromJSON(obj) + case NodeType.Basic: + return BasicNode.fromJSON(obj) + case NodeType.SharedEmitter: + return SharedEmitterNode.fromJSON(obj) + default: + return GenericNode.fromJSON(obj) + } + } + + /** + * Yields all nodes in this branch, including this node. + */ + *walk(): Generator { + yield this + if ( + this instanceof GenericNode || + this instanceof RootNode || + this instanceof NodeWithEffects + ) { + for (const node of this.nodes) { + yield* node.walk() + } + } + } + + /** + * Yields all effects in this branch. + */ + *walkEffects() { + for (const node of this.walk()) { + if (node instanceof NodeWithEffects) { + yield* node.effects + } + } + } + + /** + * Yields all actions in this branch, excluding node actions from + * {@link NodeWithEffects nodes with effects}, as those are not stored as + * actions internally. + */ + *walkActions() { + for (const node of this.walk()) { + if (node instanceof GenericNode) { + yield* node.actions + } else if (node instanceof RootNode) { + yield node.unk70x + yield node.unk10100 + yield node.unk10400 + yield node.unk10500 + } + if (node instanceof GenericNode || node instanceof NodeWithEffects) { + for (const effect of node.effects) { + yield* effect.walkActions() + } + } + } + } + + /** + * Yields all properties in this branch, excluding properties inside + * modifiers and properties in the form of {@link PropertyValue}s in + * {@link DataAction}s. + */ + *walkProperties(): Generator { + for (const action of this.walkActions()) { + if (action instanceof Action) { + yield* action.properties1 + yield* action.properties2 + } else { + for (const prop of Object.keys(ActionData[action.type].props)) { + if (action[prop] instanceof Property) { + yield action[prop] + } + } + } + } + } + + /** + * Scales the entire branch by a factor. This updates all sizes, offsets, + * lengths, and radii of the actions in the branch, except certain + * multiplicative fields and properties. + * @param factor The factor to scale the branch with. + */ + scale(factor: number) { + for (const effect of this.walkEffects()) if ( + effect instanceof BasicEffect || effect instanceof SharedEmitterEffect + ) { + const slot1 = effect.nodeTransform as ActionWithNumericalFields + switch (slot1.type) { + case ActionType.RandomNodeTransform: + slot1.fields1[6] = new FloatField(slot1.fields1[6].value * factor) + slot1.fields1[7] = new FloatField(slot1.fields1[7].value * factor) + slot1.fields1[8] = new FloatField(slot1.fields1[8].value * factor) + case ActionType.StaticNodeTransform: + slot1.fields1[0] = new FloatField(slot1.fields1[0].value * factor) + slot1.fields1[1] = new FloatField(slot1.fields1[1].value * factor) + slot1.fields1[2] = new FloatField(slot1.fields1[2].value * factor) + break + } + const slot2 = effect.nodeMovement + if (slot2 instanceof NodeTranslation) { + slot2.translation = anyValueMult(factor, slot2.translation) + } else switch (slot2.type) { + case ActionType.NodeAcceleration: + case ActionType.NodeAccelerationRandomTurns: + case ActionType.NodeAccelerationPartialFollow: + case ActionType.NodeAccelerationSpin: + slot2.properties1[0].scale(factor) + slot2.properties1[1].scale(factor) + slot2.properties1[3].scale(factor) + break + case ActionType.NodeSpeed: + case ActionType.NodeSpeedRandomTurns: + case ActionType.NodeSpeedPartialFollow: + case ActionType.NodeSpeedSpin: + slot2.properties1[0].scale(factor) + slot2.properties1[2].scale(factor) + break + } + const slot4 = effect.emitter + if (slot4 instanceof EqualDistanceEmitter) { + slot4.threshold = anyValueMult(factor, slot4.threshold) + } + const slot5 = effect.emitterShape + if (slot5 instanceof DiskEmitterShape || slot5 instanceof SphereEmitterShape) { + slot5.radius = anyValueMult(factor, slot5.radius) + } else if (slot5 instanceof RectangleEmitterShape) { + slot5.sizeX = anyValueMult(factor, slot5.sizeX) + slot5.sizeY = anyValueMult(factor, slot5.sizeY) + } else if (slot5 instanceof BoxEmitterShape) { + slot5.sizeX = anyValueMult(factor, slot5.sizeX) + slot5.sizeY = anyValueMult(factor, slot5.sizeY) + slot5.sizeZ = anyValueMult(factor, slot5.sizeZ) + } else if (slot5 instanceof CylinderEmitterShape) { + slot5.radius = anyValueMult(factor, slot5.radius) + slot5.height = anyValueMult(factor, slot5.height) + } + + if (effect instanceof BasicEffect) { + const slot7 = effect.particleModifier + if (slot7 instanceof ParticleModifier) { + slot7.speed = anyValueMult(factor, slot7.speed) + } + + const slot9 = effect.appearance + if (slot9 instanceof PointSprite) { + slot9.size = anyValueMult(factor, slot9.size) + } else if (slot9 instanceof Line) { + slot9.length = anyValueMult(factor, slot9.length) + } else if (slot9 instanceof QuadLine) { + slot9.width = anyValueMult(factor, slot9.width) + slot9.length = anyValueMult(factor, slot9.length) + } else if (slot9 instanceof BillboardEx) { + slot9.offsetX = anyValueMult(factor, slot9.offsetX) + slot9.offsetY = anyValueMult(factor, slot9.offsetY) + slot9.offsetZ = anyValueMult(factor, slot9.offsetZ) + slot9.width = anyValueMult(factor, slot9.width) + slot9.height = anyValueMult(factor, slot9.height) + slot9.depthOffset = anyValueMult(factor, slot9.depthOffset) + } else if (slot9 instanceof MultiTextureBillboardEx) { + slot9.offsetX = anyValueMult(factor, slot9.offsetX) + slot9.offsetY = anyValueMult(factor, slot9.offsetY) + slot9.offsetZ = anyValueMult(factor, slot9.offsetZ) + slot9.width = anyValueMult(factor, slot9.width) + slot9.height = anyValueMult(factor, slot9.height) + } else if (slot9 instanceof Model) { + slot9.sizeX = anyValueMult(factor, slot9.sizeX) + slot9.sizeY = anyValueMult(factor, slot9.sizeY) + slot9.sizeZ = anyValueMult(factor, slot9.sizeZ) + } else if (slot9 instanceof Tracer || slot9 instanceof DynamicTracer) { + slot9.width = anyValueMult(factor, slot9.width) + } else if (slot9 instanceof Distortion) { + slot9.offsetX = anyValueMult(factor, slot9.offsetX) + slot9.offsetY = anyValueMult(factor, slot9.offsetY) + slot9.offsetZ = anyValueMult(factor, slot9.offsetZ) + slot9.sizeX = anyValueMult(factor, slot9.sizeX) + slot9.sizeY = anyValueMult(factor, slot9.sizeY) + slot9.sizeZ = anyValueMult(factor, slot9.sizeZ) + } else if (slot9 instanceof RadialBlur) { + slot9.offsetX = anyValueMult(factor, slot9.offsetX) + slot9.offsetY = anyValueMult(factor, slot9.offsetY) + slot9.offsetZ = anyValueMult(factor, slot9.offsetZ) + slot9.width = anyValueMult(factor, slot9.width) + slot9.height = anyValueMult(factor, slot9.height) + slot9.blurRadius = anyValueMult(factor, slot9.blurRadius) + } else if (slot9 instanceof PointLight) { + slot9.radius = anyValueMult(factor, slot9.radius) + slot9.jitterAcceleration *= factor + slot9.jitterX *= factor + slot9.jitterY *= factor + slot9.jitterZ *= factor + } else if (slot9 instanceof RichModel) { + slot9.sizeX = anyValueMult(factor, slot9.sizeX) + slot9.sizeY = anyValueMult(factor, slot9.sizeY) + slot9.sizeZ = anyValueMult(factor, slot9.sizeZ) + } else if (slot9 instanceof SpotLight) { + slot9.near = anyValueMult(factor, slot9.near) + slot9.far = anyValueMult(factor, slot9.far) + slot9.radiusX = anyValueMult(factor, slot9.radiusX) + slot9.radiusY = anyValueMult(factor, slot9.radiusY) + slot9.jitterAcceleration *= factor + slot9.jitterX *= factor + slot9.jitterY *= factor + slot9.jitterZ *= factor + } + if ( + slot9 instanceof PointSprite || + slot9 instanceof Line || + slot9 instanceof QuadLine || + slot9 instanceof BillboardEx || + slot9 instanceof MultiTextureBillboardEx || + slot9 instanceof Model || + slot9 instanceof Tracer || + slot9 instanceof Distortion || + slot9 instanceof RadialBlur || + slot9 instanceof DynamicTracer || + slot9 instanceof RichModel + ) { + for (const prop of [ + 'unkDistFadeClose0', + 'unkDistFadeClose1', + 'unkDistFadeFar0', + 'unkDistFadeFar1', + 'minDistance', + 'maxDistance', + ]) if (slot9[prop] > 0) { + slot9[prop] *= factor + } + slot9.unkDepthBlend2 *= factor + } + + const slot10 = effect.particleMovement + switch (slot10.type) { + case ActionType.ParticleAcceleration: + case ActionType.ParticleSpeed: + case ActionType.ParticleSpeedRandomTurns: + case ActionType.ParticleSpeedPartialFollow: + case ActionType.ParticleAccelerationRandomTurns: + case ActionType.ParticleAccelerationPartialFollow: + slot10.properties1[0].scale(factor) + slot10.properties1[1].scale(factor) + break + } + + const slot13 = effect.nodeWind + if (slot13 instanceof NodeWindAcceleration) { + slot13.acceleration = anyValueMult(factor, slot13.acceleration) + } else if (slot13 instanceof NodeWindSpeed) { + slot13.speed = anyValueMult(factor, slot13.speed) + } + + const slot14 = effect.particleWind + if (slot14 instanceof ParticleWindAcceleration) { + slot14.acceleration = anyValueMult(factor, slot14.acceleration) + } else if (slot14 instanceof ParticleWindSpeed) { + slot14.speed = anyValueMult(factor, slot14.speed) + } + } else { // Shared emitter effect + const slot9 = effect.nodeWind + if (slot9 instanceof NodeWindAcceleration) { + slot9.acceleration = anyValueMult(factor, slot9.acceleration) + } else if (slot9 instanceof NodeWindSpeed) { + slot9.speed = anyValueMult(factor, slot9.speed) + } + } + } + } + + /** + * Recolors the entire branch by modifying color properties and fields using + * a function. + * @param func The function used to recolor the branch. It is passed the + * original color and should return the color to replace it with. + */ + recolor(func: (color: Vector4) => Vector4) { + const procFields = (list: NumericalField[], i: number, alpha = false) => { + const [r, g, b, a] = func([ + list[i ].value, + list[i+1].value, + list[i+2].value, + alpha ? list[i+3].value : 1 + ]) + list[i ] = new FloatField(r) + list[i+1] = new FloatField(g) + list[i+2] = new FloatField(b) + if (alpha) { + list[i+3] = new FloatField(a) + } + } + const procProp = (container: IProperty[] | DataAction, key: number | string) => { + let prop = container[key] + if (prop instanceof ComponentSequenceProperty) { + prop = container[key] = prop.combineComponents() + } + if (prop instanceof ValueProperty) { + prop.value = func(prop.value) + } else if (prop instanceof SequenceProperty) { + for (const keyframe of prop.keyframes) { + keyframe.value = func(keyframe.value as Vector4) + } + } + if ('modifiers' in prop) { + for (const mod of (prop as IModifiableProperty).modifiers) { + switch (mod.type) { + case ModifierType.Randomizer2: + procFields(mod.fields, 8, true) + case ModifierType.Randomizer1: + case ModifierType.Randomizer3: + procFields(mod.fields, 4, true) + break + case ModifierType.ExternalValue1: + case ModifierType.ExternalValue2: + procProp(mod.properties, 0) + break + } + } + } + } + const procDataProp = (action: DataAction, prop: string) => { + if (action[prop] instanceof Property) { + procProp(action, prop) + } else if (Array.isArray(action[prop])) { + action[prop] = func(action[prop]) + } + } + for (const effect of this.walkEffects()) if (effect instanceof BasicEffect) { + if (effect.particleModifier instanceof ParticleModifier) { + procDataProp(effect.particleModifier, 'color') + } + const slot9 = effect.appearance as ActionWithNumericalFields | DataAction + if (slot9 instanceof Action) switch (slot9.type) { + case ActionType.Unk10000_StandardParticle: + case ActionType.Unk10001_StandardCorrectParticle: + procProp(slot9.properties1, 13) + break + case ActionType.Unk10014_LensFlare: + procProp(slot9.properties1, 2) + procProp(slot9.properties1, 5) + procProp(slot9.properties1, 8) + procProp(slot9.properties1, 11) + break + } else if ( + slot9 instanceof PointSprite || + slot9 instanceof BillboardEx || + slot9 instanceof Model || + slot9 instanceof Tracer || + slot9 instanceof DynamicTracer || + slot9 instanceof RichModel + ) { + procDataProp(slot9, 'color1') + procDataProp(slot9, 'color2') + procDataProp(slot9, 'color3') + } else if (slot9 instanceof Line || slot9 instanceof QuadLine) { + procDataProp(slot9, 'color1') + procDataProp(slot9, 'color2') + procDataProp(slot9, 'startColor') + procDataProp(slot9, 'endColor') + procDataProp(slot9, 'color3') + } else if (slot9 instanceof MultiTextureBillboardEx) { + procDataProp(slot9, 'color1') + procDataProp(slot9, 'color2') + procDataProp(slot9, 'color3') + procDataProp(slot9, 'layersColor') + procDataProp(slot9, 'layer1Color') + procDataProp(slot9, 'layer2Color') + } else if (slot9 instanceof Distortion || slot9 instanceof RadialBlur) { + procDataProp(slot9, 'color') + } else if (slot9 instanceof PointLight || slot9 instanceof SpotLight) { + procDataProp(slot9, 'diffuseColor') + procDataProp(slot9, 'specularColor') + } + if ( + slot9 instanceof PointSprite || + slot9 instanceof Line || + slot9 instanceof QuadLine || + slot9 instanceof BillboardEx || + slot9 instanceof MultiTextureBillboardEx || + slot9 instanceof Model || + slot9 instanceof Tracer || + slot9 instanceof Distortion || + slot9 instanceof RadialBlur || + slot9 instanceof DynamicTracer || + slot9 instanceof RichModel + ) { + ;[slot9.bloomRed, slot9.bloomGreen, slot9.bloomBlue, slot9.bloomStrength] = func( + [slot9.bloomRed, slot9.bloomGreen, slot9.bloomBlue, slot9.bloomStrength] + ) + } + } + } + +} + +/** + * This class has the same structure as a node in the FXR file format, meaning + * it can be used to hold any information that a node from the format can. It + * is meant to only be used as a fallback if none of the more specific node + * classes can be used for something. + */ +class GenericNode extends Node { + + constructor( + type: NodeType, + public actions: IAction[], + public effects: IEffect[], + public nodes: Node[] + ) { + super(type) + } + + getActions(game: Game): IAction[] { return this.actions } + getEffects(game: Game): IEffect[] { return this.effects } + getNodes(game: Game): Node[] { return this.nodes } + + static fromJSON({ + type, + actions, + effects, + nodes + }: { + type: number + actions: [] + effects: [] + nodes: [] + }) { + const node = new (type in Nodes ? Nodes[type] : Node) + node.type = type + node.actions = actions?.map(action => Action.fromJSON(action)) ?? [] + node.effects = effects?.map(effect => Effect.fromJSON(effect)) ?? [] + node.nodes = nodes?.map(node => Node.fromJSON(node)) ?? [] + return node + } + + toJSON() { + return { + type: this.type, + actions: this.actions.map(action => action.toJSON()), + effects: this.effects.map(effect => effect.toJSON()), + nodes: this.nodes.map(node => node.toJSON()), + } + } + + minify() { + return new GenericNode( + this.type, + this.actions.map(action => action.minify()), + this.effects.map(effect => effect.minify()), + this.nodes.map(node => node.minify()) + ) + } + +} + +/** + * The root of the FXR tree structure. + */ +class RootNode extends Node { + + unk70x: IAction = new Action(ActionType.Unk700) + + constructor( + public nodes: Node[] = [], + unk70x: IAction = null, + public unk10100: IAction = new Action(ActionType.Unk10100, arrayOf(56, () => new IntField(0))), + public unk10400: IAction = new Action(ActionType.Unk10400, arrayOf(65, () => new IntField(1))), + public unk10500: IAction = new Unk10500 + ) { + super(NodeType.Root) + if (unk70x !== null) { + this.unk70x = unk70x + } + } + + getActions(game: Game): IAction[] { + switch (game) { + case Game.DarkSouls3: + case Game.Sekiro: + return [ + this.unk10100, + this.unk10400, + this.unk10500 + ] + case Game.EldenRing: + case Game.ArmoredCore6: + return [ + this.unk70x, + this.unk10100, + this.unk10400, + this.unk10500 + ] + } + } + + getEffects(game: Game): IEffect[] { return [] } + getNodes(game: Game): Node[] { return this.nodes } + + minify(): Node { + return new RootNode( + this.nodes.map(node => node.minify()), + this.unk70x.minify(), + this.unk10100.minify(), + this.unk10400.minify(), + this.unk10500.minify() + ) + } + + static fromJSON(obj: any): Node { + if (!( + 'nodes' in obj || + 'unk70x' in obj || + 'unk10100' in obj || + 'unk10400' in obj || + 'unk10500' in obj + )) return GenericNode.fromJSON(obj) + return new RootNode( + obj.nodes, + obj.unk70x, + obj.unk10100, + obj.unk10400, + obj.unk10500 + ) + } + + toJSON() { + return { + type: this.type, + unk70x: this.unk70x.toJSON(), + unk10100: this.unk10100.toJSON(), + unk10400: this.unk10400.toJSON(), + unk10500: this.unk10500.toJSON(), + nodes: this.nodes.map(node => node.toJSON()) + } + } + + get rateOfTime() { + if (this.unk10500 instanceof Unk10500) { + return this.unk10500.rateOfTime + } else { + return null + } + } + set rateOfTime(value: ScalarValue) { + if (this.unk10500 instanceof Unk10500) { + this.unk10500.rateOfTime = value + } + } + +} + +/** + * Acts as a node containing another SFX. + */ +class ProxyNode extends Node { + + /** + * @param sfx The ID of the SFX that this node should act as a proxy for. + */ + constructor(public sfx: number) { super(NodeType.Proxy) } + + getActions(game: Game): IAction[] { + return [ new SFXReference(this.sfx) ] + } + + toJSON() { + return { + type: this.type, + sfxID: this.sfx + } + } + +} + +/** + * Super class for any type of node that contains {@link EffectType effects}. + */ +class NodeWithEffects extends Node { + + stateEffectMap: number[] = [0] + + constructor(type: NodeType, public effects: IEffect[], public nodes: Node[]) { + super(type) + } + + getActions(game: Game): IAction[] { + return [ new StateEffectMap(...this.stateEffectMap) ] + } + + getEffects(game: Game): IEffect[] { + return this.effects + } + + getNodes(game: Game): Node[] { + return this.nodes + } + + toJSON() { + return { + type: this.type, + stateEffectMap: this.stateEffectMap, + effects: this.effects, + nodes: this.nodes + } + } + + mapStates(...effectIndices: number[]) { + this.stateEffectMap = effectIndices + return this + } + +} + +/** + * A node that only displays one of its child nodes at a time based on + * distance thresholds for each. + * + * This node can only manage up to five levels of detail. If you need more + * levels, you can put another LOD node as the fifth child of this node and set + * higher thresholds in that. + */ +class LevelOfDetailNode extends NodeWithEffects { + + /** + * @param effects An array of {@link EffectType.LevelOfDetail LOD effects}. + * Other effect types do not work in this node type. + * @param nodes An array of child nodes. + */ + constructor(effects: IEffect[] = [], nodes: Node[] = []) { + super(NodeType.LevelOfDetail, effects, nodes) + } + + /** + * Alternative method to construct the node. Use this if you don't need + * multiple effects or a finite duration. + * @param thresholds An array of distance thresholds. Each threshold is used + * for the child node of the same index. + * @param nodes An array of child nodes. + */ + static withThresholds(thresholds: number[], nodes: Node[]) { + return new LevelOfDetailNode([ + new LevelOfDetailEffect(-1, thresholds) + ], nodes) + } + + static fromJSON(obj: any): Node { + return new LevelOfDetailNode( + obj.effects.map((e: any) => Effect.fromJSON(e)), + obj.nodes.map((e: any) => Node.fromJSON(e)) + ).mapStates(...obj.stateEffectMap) + } + + minify(): Node { + return new LevelOfDetailNode( + this.effects.map(e => e.minify()), + this.nodes.map(e => e.minify()) + ).mapStates(...this.stateEffectMap) + } + +} + +/** + * A basic node that can have transforms and child nodes, and emit particles. + */ +class BasicNode extends NodeWithEffects { + + /** + * @param effectsOrEffectActions This is either the list of effects to add + * to the node or a list of actions to create a {@link BasicEffect} with to + * add to the node. + * @param nodes A list of child nodes. + */ + constructor(effectsOrEffectActions: IEffect[] | IAction[] = [], nodes: Node[] = []) { + if (!Array.isArray(nodes) || nodes.some(e => !(e instanceof Node))) { + throw new Error('Non-node passed as node to BasicNode.') + } + if (effectsOrEffectActions.every(e => e instanceof Action || e instanceof DataAction)) { + super(NodeType.Basic, [ + new BasicEffect(effectsOrEffectActions as IAction[]) + ], nodes) + } else { + super( + NodeType.Basic, + effectsOrEffectActions as IEffect[], + nodes + ) + } + } + + static fromJSON(obj: any): Node { + return new BasicNode( + obj.effects.map((e: any) => Effect.fromJSON(e)), + obj.nodes.map((e: any) => Node.fromJSON(e)) + ).mapStates(...obj.stateEffectMap) + } + + minify(): Node { + return new BasicNode( + this.effects.map(e => e.minify()), + this.nodes.map(e => e.minify()) + ).mapStates(...this.stateEffectMap) + } + +} + +/** + * A node that overrides the emitter of its child nodes with its own, allowing + * a single emitter to emit multiple types of particles. + */ +class SharedEmitterNode extends NodeWithEffects { + + constructor(effectsOrEffectActions: IEffect[] | Action[] = [], nodes: Node[] = []) { + if (!Array.isArray(nodes) || nodes.some(e => !(e instanceof Node))) { + throw new Error('Non-node passed as node to SharedEmitterNode.') + } + if (effectsOrEffectActions.every(e => e instanceof Action || e instanceof DataAction)) { + super(NodeType.SharedEmitter, [ + new SharedEmitterEffect(effectsOrEffectActions as Action[]) + ], nodes) + } else { + super( + NodeType.SharedEmitter, + effectsOrEffectActions as IEffect[], + nodes + ) + } + } + + static fromJSON(obj: any): Node { + return new SharedEmitterNode( + obj.effects.map((e: any) => Effect.fromJSON(e)), + obj.nodes.map((e: any) => Node.fromJSON(e)) + ).mapStates(...obj.stateEffectMap) + } + + minify(): Node { + return new SharedEmitterNode( + this.effects.map(e => e.minify()), + this.nodes.map(e => e.minify()) + ).mapStates(...this.stateEffectMap) + } + +} + +const Nodes = { + [NodeType.Root]: RootNode, RootNode, + [NodeType.Proxy]: ProxyNode, ProxyNode, + [NodeType.LevelOfDetail]: LevelOfDetailNode, LevelOfDetailNode, + [NodeType.Basic]: BasicNode, BasicNode, +} + +/** + * Generic effect class that uses the same structure as the file format. Only + * for use with undocumented effect types. Use one of the other effect classes + * for effects that are known: + * - {@link LevelOfDetailEffect} + * - {@link BasicEffect} + * - {@link SharedEmitterEffect} + */ +class Effect implements IEffect { + + constructor(public type: EffectType, public actions: IAction[]) {} + + getActionCount(game: Game): number { + return this.actions.length + } + + getActions(game: Game): IAction[] { + return this.actions + } + + toJSON() { + return { + type: this.type, + actions: this.actions.map(action => action.toJSON()) + } + } + + minify() { + this.actions = this.actions.map(action => action.minify()) + return this + } + + *walkActions() { + yield* this.actions + } + + static fromJSON(obj: any): IEffect { + if ('actions' in obj) { + return new Effect(obj.type, obj.actions.map((e: any) => Action.fromJSON(e))) + } else switch (obj.type) { + case EffectType.LevelOfDetail: + return new LevelOfDetailEffect(Property.fromJSON(obj.duration), [ + obj.threshold0, + obj.threshold1, + obj.threshold2, + obj.threshold3, + obj.threshold4, + ]) + case EffectType.Basic: + return new BasicEffect({ + nodeAttributes: Action.fromJSON(obj.nodeAttributes), + nodeTransform: Action.fromJSON(obj.nodeTransform), + nodeMovement: Action.fromJSON(obj.nodeMovement), + audio: Action.fromJSON(obj.audio), + emitter: Action.fromJSON(obj.emitter), + emitterShape: Action.fromJSON(obj.emitterShape), + particleDirection: Action.fromJSON(obj.particleDirection), + particleModifier: Action.fromJSON(obj.particleModifier), + particleAttributes: Action.fromJSON(obj.particleAttributes), + appearance: Action.fromJSON(obj.appearance), + particleMovement: Action.fromJSON(obj.particleMovement), + slot11: Action.fromJSON(obj.slot11), + slot12: Action.fromJSON(obj.slot12), + nodeWind: Action.fromJSON(obj.nodeWind), + particleWind: Action.fromJSON(obj.particleWind), + }) + case EffectType.SharedEmitter: + return new SharedEmitterEffect({ + nodeAttributes: Action.fromJSON(obj.nodeAttributes), + nodeTransform: Action.fromJSON(obj.nodeTransform), + nodeMovement: Action.fromJSON(obj.nodeMovement), + audio: Action.fromJSON(obj.audio), + emitter: Action.fromJSON(obj.emitter), + emitterShape: Action.fromJSON(obj.emitterShape), + particleDirection: Action.fromJSON(obj.particleDirection), + behavior: Action.fromJSON(obj.behavior), + slot8: Action.fromJSON(obj.slot8), + nodeWind: Action.fromJSON(obj.nodeWind), + }) + } + throw new Error('Invalid effect JSON: ' + JSON.stringify(obj)) + } +} + +/** + * Manages the duration and thresholds for the + * {@link NodeType.LevelOfDetail level of detail node}. + */ +class LevelOfDetailEffect implements IEffect { + readonly type = EffectType.LevelOfDetail + + /** + * @param duration The duration for the node to stay active. Once its time is + * up, it will deactivate and none of the child nodes will be visible/audible + * anymore. Can be set to -1 to make the node last forever. + * @param thresholds An array of distance thresholds. Each threshold is used + * for the child node of the same index. + */ + constructor(public duration: ScalarValue, public thresholds: number[]) {} + + getActionCount(game: Game): number { + return 1 + } + + getActions(game: Game): IAction[] { + return [ + new LevelOfDetailThresholds({ + duration: this.duration, + threshold0: this.thresholds[0], + threshold1: this.thresholds[1], + threshold2: this.thresholds[2], + threshold3: this.thresholds[3], + threshold4: this.thresholds[4], + }) + ] + } + + toJSON() { + return { + type: this.type, + duration: this.duration instanceof Property ? this.duration.toJSON() : this.duration, + thresholds: this.thresholds + } + } + + minify() { + return this + } + + *walkActions() {} + +} + +export interface BasicEffectParams { + nodeAttributes?: Action | NodeAttributes + nodeTransform?: Action + nodeMovement?: Action | NodeTranslation | NodeAttachToCamera + audio?: Action | PlaySound + emitter?: Action | PeriodicEmitter | EqualDistanceEmitter + emitterShape?: + Action | + PointEmitterShape | + DiskEmitterShape | + RectangleEmitterShape | + SphereEmitterShape | + BoxEmitterShape | + CylinderEmitterShape + particleDirection?: + Action | + CircularParticleSpread | + EllipticalParticleSpread | + RectangularParticleSpread + particleModifier?: Action | ParticleModifier + particleAttributes?: Action | ParticleAttributes + appearance?: + Action | + PointSprite | + Line | + QuadLine | + BillboardEx | + MultiTextureBillboardEx | + Model | + Tracer | + Distortion | + RadialBlur | + PointLight | + DynamicTracer | + WaterInteraction | + RichModel | + SpotLight + particleMovement?: Action + slot11?: Action + slot12?: Action | Unk130 + nodeWind?: Action | NodeWindAcceleration | NodeWindSpeed + particleWind?: Action | ParticleWindAcceleration | ParticleWindSpeed | Unk800 +} + +/** + * Effect used in {@link NodeType.Basic basic nodes} to apply transforms and + * emit particles of many different types. + * + * Default actions: + * Index | Action + * ------|---------- + * 0 | {@link ActionType.NodeAttributes NodeAttributes} + * 1 | {@link ActionType.None None} + * 2 | {@link ActionType.None None} + * 3 | {@link ActionType.None None} + * 4 | {@link ActionType.OneTimeEmitter OneTimeEmitter} + * 5 | {@link ActionType.PointEmitterShape PointEmitterShape} + * 6 | {@link ActionType.NoParticleSpread NoParticleSpread} + * 7 | {@link ActionType.ParticleModifier ParticleModifier} + * 8 | {@link ActionType.ParticleAttributes ParticleAttributes} + * 9 | {@link ActionType.None None} + * 10 | {@link ActionType.None None} + * 11 | {@link ActionType.None None} + * 12 | {@link ActionType.Unk130 Unk130} + * 13 | {@link ActionType.None None} + * 14 | {@link ActionType.None None} + */ +class BasicEffect implements IEffect { + readonly type = EffectType.Basic + + nodeAttributes: Action | NodeAttributes = new NodeAttributes + nodeTransform: Action = new Action + nodeMovement: Action | NodeTranslation | NodeAttachToCamera = new Action + audio: Action | PlaySound = new Action + emitter: Action | PeriodicEmitter | EqualDistanceEmitter = new OneTimeEmitter + emitterShape: + Action | + PointEmitterShape | + DiskEmitterShape | + RectangleEmitterShape | + SphereEmitterShape | + BoxEmitterShape | + CylinderEmitterShape + = new PointEmitterShape + particleDirection: + Action | + CircularParticleSpread | + EllipticalParticleSpread | + RectangularParticleSpread + = new NoParticleSpread + particleModifier: Action | ParticleModifier = new ParticleModifier + particleAttributes: Action | ParticleAttributes = new ParticleAttributes + appearance: + Action | + PointSprite | + Line | + QuadLine | + BillboardEx | + MultiTextureBillboardEx | + Model | + Tracer | + Distortion | + RadialBlur | + PointLight | + DynamicTracer | + WaterInteraction | + RichModel | + SpotLight + = new Action + particleMovement: Action = new Action + slot11: Action = new Action + slot12: Action | Unk130 = new Unk130 + nodeWind: Action | NodeWindAcceleration | NodeWindSpeed = new Action + particleWind: + Action | + ParticleWindAcceleration | + ParticleWindSpeed | + Unk800 + = new Action + + constructor(params: BasicEffectParams | IAction[]) { + if (Array.isArray(params)) { + for (const action of params) { + const index = EffectActionSlots[EffectType.Basic].findIndex(a => a.includes(action.type)) + switch (index) { + case 0: this.nodeAttributes = action as Action; break; + case 1: this.nodeTransform = action as Action; break; + case 2: this.nodeMovement = action as Action; break; + case 3: this.audio = action as Action; break; + case 4: this.emitter = action as Action; break; + case 5: this.emitterShape = action as Action; break; + case 6: this.particleDirection = action as Action; break; + case 7: this.particleModifier = action as Action; break; + case 8: this.particleAttributes = action as Action; break; + case 9: this.appearance = action as Action; break; + case 10: this.particleMovement = action as Action; break; + case 11: this.slot11 = action as Action; break; + case 12: this.slot12 = action as Action; break; + case 13: this.nodeWind = action as Action; break; + case 14: this.particleWind = action as Action; break; + } + } + } else { + if ('nodeAttributes' in params) this.nodeAttributes = params.nodeAttributes + if ('nodeTransform' in params) this.nodeTransform = params.nodeTransform + if ('nodeMovement' in params) this.nodeMovement = params.nodeMovement + if ('audio' in params) this.audio = params.audio + if ('emitter' in params) this.emitter = params.emitter + if ('emitterShape' in params) this.emitterShape = params.emitterShape + if ('particleDirection' in params) this.particleDirection = params.particleDirection + if ('particleModifier' in params) this.particleModifier = params.particleModifier + if ('particleAttributes' in params) this.particleAttributes = params.particleAttributes + if ('appearance' in params) this.appearance = params.appearance + if ('particleMovement' in params) this.particleMovement = params.particleMovement + if ('slot11' in params) this.slot11 = params.slot11 + if ('slot12' in params) this.slot12 = params.slot12 + if ('nodeWind' in params) this.nodeWind = params.nodeWind + if ('particleWind' in params) this.particleWind = params.particleWind + } + } + + getActionCount(game: Game): number { + return game === Game.DarkSouls3 ? 13 : 15 + } + + getActions(game: Game): IAction[] { + return [ + this.nodeAttributes, + this.nodeTransform, + this.nodeMovement, + this.audio, + this.emitter, + this.emitterShape, + this.particleDirection, + this.particleModifier, + this.particleAttributes, + this.appearance, + this.particleMovement, + this.slot11, + this.slot12, + game === Game.DarkSouls3 ? [] : [ + this.nodeWind, + this.particleWind, + ] + ].flat() + } + + toJSON() { + return { + type: this.type, + nodeAttributes: this.nodeAttributes.toJSON(), + nodeTransform: this.nodeTransform.toJSON(), + nodeMovement: this.nodeMovement.toJSON(), + audio: this.audio.toJSON(), + emitter: this.emitter.toJSON(), + emitterShape: this.emitterShape.toJSON(), + particleDirection: this.particleDirection.toJSON(), + particleModifier: this.particleModifier.toJSON(), + particleAttributes: this.particleAttributes.toJSON(), + appearance: this.appearance.toJSON(), + particleMovement: this.particleMovement.toJSON(), + slot11: this.slot11.toJSON(), + slot12: this.slot12.toJSON(), + nodeWind: this.nodeWind.toJSON(), + particleWind: this.particleWind.toJSON(), + } + } + + minify() { + this.nodeAttributes = this.nodeAttributes.minify() + this.nodeTransform = this.nodeTransform.minify() + this.nodeMovement = this.nodeMovement.minify() + this.audio = this.audio.minify() + this.emitter = this.emitter.minify() + this.emitterShape = this.emitterShape.minify() + this.particleDirection = this.particleDirection.minify() + this.particleModifier = this.particleModifier.minify() + this.particleAttributes = this.particleAttributes.minify() + this.appearance = this.appearance.minify() + this.particleMovement = this.particleMovement.minify() + this.slot11 = this.slot11.minify() + this.slot12 = this.slot12.minify() + this.nodeWind = this.nodeWind.minify() + this.particleWind = this.particleWind.minify() + return this + } + + *walkActions() { + yield this.nodeAttributes + yield this.nodeTransform + yield this.nodeMovement + yield this.audio + yield this.emitter + yield this.emitterShape + yield this.particleDirection + yield this.particleModifier + yield this.particleAttributes + yield this.appearance + yield this.particleMovement + yield this.slot11 + yield this.slot12 + yield this.nodeWind + yield this.particleWind + } + +} + +export interface SharedEmitterEffectParams { + nodeAttributes?: Action | NodeAttributes + nodeTransform?: Action + nodeMovement?: Action | NodeTranslation | NodeAttachToCamera + audio?: Action | PlaySound + emitter?: Action | PeriodicEmitter | EqualDistanceEmitter + emitterShape?: + Action | + PointEmitterShape | + DiskEmitterShape | + RectangleEmitterShape | + SphereEmitterShape | + BoxEmitterShape | + CylinderEmitterShape + particleDirection?: + Action | + CircularParticleSpread | + EllipticalParticleSpread | + RectangularParticleSpread + behavior?: Action + slot8?: Action + nodeWind?: Action | NodeWindAcceleration | NodeWindSpeed +} + +/** + * Effect used in {@link NodeType.SharedEmitter shared emitter nodes} to + * override emitters of child nodes and control which of the child nodes to use + * the particles of. + * + * Default actions: + * Index | Action + * ------|---------- + * 0 | {@link ActionType.NodeAttributes NodeAttributes} + * 1 | {@link ActionType.None None} + * 2 | {@link ActionType.None None} + * 3 | {@link ActionType.None None} + * 4 | {@link ActionType.OneTimeEmitter OneTimeEmitter} + * 5 | {@link ActionType.PointEmitterShape PointEmitterShape} + * 6 | {@link ActionType.NoParticleSpread NoParticleSpread} + * 7 | {@link ActionType.EmitAllParticles AllChildNodes} + * 8 | {@link ActionType.None None} + * 9 | {@link ActionType.None None} + */ +class SharedEmitterEffect implements IEffect { + readonly type = EffectType.SharedEmitter + + nodeAttributes: Action | NodeAttributes = new NodeAttributes + nodeTransform: Action = new Action + nodeMovement: Action | NodeTranslation | NodeAttachToCamera = new Action + audio: Action | PlaySound = new Action + emitter: Action | PeriodicEmitter | EqualDistanceEmitter = new OneTimeEmitter + emitterShape: + Action | + PointEmitterShape | + DiskEmitterShape | + RectangleEmitterShape | + SphereEmitterShape | + BoxEmitterShape | + CylinderEmitterShape + = new PointEmitterShape + particleDirection: + Action | + CircularParticleSpread | + EllipticalParticleSpread | + RectangularParticleSpread + = new NoParticleSpread + behavior: Action = new EmitAllParticles + slot8: Action = new Action + nodeWind: Action | NodeWindAcceleration | NodeWindSpeed = new Action + + constructor(params: SharedEmitterEffectParams | IAction[]) { + if (Array.isArray(params)) { + for (const action of params) { + const index = EffectActionSlots[EffectType.SharedEmitter].findIndex(a => a.includes(action.type)) + switch (index) { + case 0: this.nodeAttributes = action as Action; break; + case 1: this.nodeTransform = action as Action; break; + case 2: this.nodeMovement = action as Action; break; + case 3: this.audio = action as Action; break; + case 4: this.emitter = action as Action; break; + case 5: this.emitterShape = action as Action; break; + case 6: this.particleDirection = action as Action; break; + case 7: this.behavior = action as Action; break; + case 8: this.slot8 = action as Action; break; + case 9: this.nodeWind = action as Action; break; + } + } + } else { + if ('nodeAttributes' in params) this.nodeAttributes = params.nodeAttributes + if ('nodeTransform' in params) this.nodeTransform = params.nodeTransform + if ('nodeMovement' in params) this.nodeMovement = params.nodeMovement + if ('audio' in params) this.audio = params.audio + if ('emitter' in params) this.emitter = params.emitter + if ('emitterShape' in params) this.emitterShape = params.emitterShape + if ('particleDirection' in params) this.particleDirection = params.particleDirection + if ('behavior' in params) this.behavior = params.behavior + if ('slot8' in params) this.slot8 = params.slot8 + if ('nodeWind' in params) this.nodeWind = params.nodeWind + } + } + + getActionCount(game: Game): number { + return 10 + } + + getActions(game: Game): IAction[] { + if (game === Game.DarkSouls3) { + throw new Error('SharedEmitterEffect is not available in Dark Souls 3.') + } + return [ + this.nodeAttributes, + this.nodeTransform, + this.nodeMovement, + this.audio, + this.emitter, + this.emitterShape, + this.particleDirection, + this.behavior, + this.slot8, + this.nodeWind + ] + } + + toJSON() { + return { + type: this.type, + nodeAttributes: this.nodeAttributes.toJSON(), + nodeTransform: this.nodeTransform.toJSON(), + nodeMovement: this.nodeMovement.toJSON(), + audio: this.audio.toJSON(), + emitter: this.emitter.toJSON(), + emitterShape: this.emitterShape.toJSON(), + particleDirection: this.particleDirection.toJSON(), + behavior: this.behavior.toJSON(), + slot8: this.slot8.toJSON(), + nodeWind: this.nodeWind.toJSON(), + } + } + + minify() { + this.nodeAttributes = this.nodeAttributes.minify() + this.nodeTransform = this.nodeTransform.minify() + this.nodeMovement = this.nodeMovement.minify() + this.audio = this.audio.minify() + this.emitter = this.emitter.minify() + this.emitterShape = this.emitterShape.minify() + this.particleDirection = this.particleDirection.minify() + this.behavior = this.behavior.minify() + this.slot8 = this.slot8.minify() + this.nodeWind = this.nodeWind.minify() + return this + } + + *walkActions() { + yield this.nodeAttributes + yield this.nodeTransform + yield this.nodeMovement + yield this.audio + yield this.emitter + yield this.emitterShape + yield this.particleDirection + yield this.behavior + yield this.slot8 + yield this.nodeWind + } + +} + +class Action implements IAction { + + type: ActionType + fields1: Field[] + fields2: Field[] + properties1: AnyProperty[] + properties2: AnyProperty[] + section10s: Section10[] + + constructor( + type: number = ActionType.None, + fields1: Field[] = [], + fields2: Field[] = [], + properties1: AnyProperty[] = [], + properties2: AnyProperty[] = [], + section10s: Section10[] = [], + ) { + this.type = type + this.fields1 = fields1 + this.fields2 = fields2 + this.properties1 = properties1 + this.properties2 = properties2 + this.section10s = section10s + } + + withFields1(...fields: { index: number, field: Field | number | boolean }[]) { + for (const { index, field } of fields) { + if (field instanceof Field) { + this.fields1[index] = field + } else { + this.fields1[index].value = field + } + } + return this + } + + withFields2(...fields: { index: number, field: Field | number | boolean }[]) { + for (const { index, field } of fields) { + if (field instanceof Field) { + this.fields2[index] = field + } else { + this.fields2[index].value = field + } + } + return this + } + + withProperties1(...props: { index: number, property: PropertyValue | AnyProperty }[]) { + for (const { index, property } of props) { + if (property instanceof Property) { + this.properties1[index] = property + } else { + if (Array.isArray(property)) { + this.properties1[index] = new ConstantProperty(...property) + } else { + this.properties1[index] = new ConstantProperty(property) + } + } + } + return this + } + + withProperties2(...props: { index: number, property: PropertyValue | AnyProperty }[]) { + for (const { index, property } of props) { + if (property instanceof Property) { + this.properties2[index] = property + } else { + if (Array.isArray(property)) { + this.properties2[index] = new ConstantProperty(...property) + } else { + this.properties2[index] = new ConstantProperty(property) + } + } + } + return this + } + + static fromJSON({ + type, + fields1 = [], + fields2 = [], + properties1 = [], + properties2 = [], + section10s = [], + }: { + type: ActionType + fields1?: [] + fields2?: [] + properties1?: [] + properties2?: [] + section10s?: [] + }) { + return new Action( + type, + fields1.map(e => Field.fromJSON(e)), + fields2.map(e => Field.fromJSON(e)), + properties1.map(e => Property.fromJSON(e)), + properties2.map(e => Property.fromJSON(e)), + section10s.map(e => Section10.fromJSON(e)) + ) + } + + toJSON() { + const o: { + type: ActionType + fields1?: any[] + fields2?: any[] + properties1?: any[] + properties2?: any[] + section10s?: any[] + } = { type: this.type } + if (this.fields1.length > 0) o.fields1 = this.fields1.map(field => field.toJSON()) + if (this.fields2.length > 0) o.fields2 = this.fields2.map(field => field.toJSON()) + if (this.properties1.length > 0) o.properties1 = this.properties1.map(prop => prop.toJSON()) + if (this.properties2.length > 0) o.properties2 = this.properties2.map(prop => prop.toJSON()) + if (this.section10s.length > 0) o.section10s = this.section10s.map(s10 => s10.toJSON()) + return o + } + + /** + * Creates a minified version of this action. + * + * Some actions can be minified to make the output smaller. This is done by + * creating a simpler action that is functionally equivalent to this action. + * + * Actions that can not be minified will not be changed. + */ + minify(): Action { + return this + } + +} + +export interface ActionWithNumericalFields extends Action { + fields1: NumericalField[] + fields2: NumericalField[] +} + +/** + * Base class for all actions that are defined in {@link ActionData}. The main + * difference is that these actions don't use fields or properties, and cannot + * have Section10s. Instead, these actions will automatically generate the + * lists of fields and properties based on the game they're being written for. + * + * Aside from from these actions using class properties instead of action + * fields and properties, there is not much different to the end user. It is + * mainly just a different way to define actions in the source code that + * allows those actions to work in multiple games. + */ +class DataAction implements IAction { + + constructor(public readonly type: ActionType) {} + + assign(props: any = {}) { + for (const [k, v] of Object.entries(ActionData[this.type].props)) { + this[k] = props[k] ?? v.default + } + return this + } + + toJSON() { + return { + type: this.type, + ...Object.fromEntries( + Object.keys(ActionData[this.type].props).filter(prop => prop in this).map(prop => { + const v = this[prop] + if (v instanceof Property) { + return [prop, v.toJSON()] + } + return [prop, v] + }) + ) + } + } + + minify() { + return this + } + + getFields(game: Game, list: 'fields1' | 'fields2'): Field[] { + const data = getActionGameData(this.type, game) + return data[list].map((name: string) => { + const prop = ActionData[this.type].props[name] + return new Field(prop.field, this[name] instanceof Property ? this[name].valueAt(0) : this[name]) + }) + } + + getProperties(game: Game, list: 'properties1' | 'properties2'): AnyProperty[] { + const data = getActionGameData(this.type, game) + return (data[list] ?? []).map((name: string) => { + const prop = ActionData[this.type].props[name] + return Array.isArray(prop.default) ? + vectorFromArg(this[name]) : + scalarFromArg(this[name]) + }) + } + + toAction(game: Game) { + return new Action( + this.type, + this.getFields(game, 'fields1'), + this.getFields(game, 'fields2'), + this.getProperties(game, 'properties1'), + this.getProperties(game, 'properties2') + ) + } + +} + +export interface NodeMovementParams { + /** + * Controls how fast the node should spin around its X-axis in degrees per + * second. Defaults to 0. + * + * **Argument**: {@link PropertyArgument.EffectAge Effect age} + * + * Incompatible with the following parameters: + * - {@link maxTurnAngle} + * - {@link turnInterval} + * - {@link followFactor} + * - {@link followRotation} + * + * See also: + * - {@link spinXMultiplier} + */ + spinX?: ScalarValue + /** + * Multiplier for {@link spinX}. Defaults to 1. + * + * **Argument**: {@link PropertyArgument.EffectAge Effect age} + * + * Incompatible with the following parameters: + * - {@link maxTurnAngle} + * - {@link turnInterval} + * - {@link followFactor} + * - {@link followRotation} + */ + spinXMultiplier?: ScalarValue + /** + * Controls how fast the node should spin around its Y-axis in degrees per + * second. Defaults to 0. + * + * **Argument**: {@link PropertyArgument.EffectAge Effect age} + * + * Incompatible with the following parameters: + * - {@link maxTurnAngle} + * - {@link turnInterval} + * - {@link followFactor} + * - {@link followRotation} + * + * See also: + * - {@link spinYMultiplier} + */ + spinY?: ScalarValue + /** + * Multiplier for {@link spinY}. Defaults to 1. + * + * **Argument**: {@link PropertyArgument.EffectAge Effect age} + * + * Incompatible with the following parameters: + * - {@link maxTurnAngle} + * - {@link turnInterval} + * - {@link followFactor} + * - {@link followRotation} + */ + spinYMultiplier?: ScalarValue + /** + * Controls how fast the node should spin around its Z-axis in degrees per + * second. Defaults to 0. + * + * **Argument**: {@link PropertyArgument.EffectAge Effect age} + * + * Incompatible with the following parameters: + * - {@link maxTurnAngle} + * - {@link turnInterval} + * - {@link followFactor} + * - {@link followRotation} + * + * See also: + * - {@link spinZMultiplier} + */ + spinZ?: ScalarValue + /** + * Multiplier for {@link spinZ}. Defaults to 1. + * + * **Argument**: {@link PropertyArgument.EffectAge Effect age} + * + * Incompatible with the following parameters: + * - {@link maxTurnAngle} + * - {@link turnInterval} + * - {@link followFactor} + * - {@link followRotation} + */ + spinZMultiplier?: ScalarValue + /** + * Controls the speed of the node along its Z-axis. Defaults to 0. + * + * **Argument**: + * - If {@link speedZMultiplier} is set: + * {@link PropertyArgument.EffectAge Effect age} + * - If {@link speedZMultiplier} is **not** set: + * {@link PropertyArgument.Constant0 Constant 0} + * + * See also: + * - {@link speedZMultiplier} + */ + speedZ?: ScalarValue + /** + * Multiplier for {@link speedZ}. Defaults to 1. + * + * **Argument**: {@link PropertyArgument.EffectAge Effect age} + * + * Incompatible with the following parameters: + * - {@link accelerationZ} + * - {@link accelerationZMultiplier} + */ + speedZMultiplier?: ScalarValue + /** + * Controls the acceleration of the node in the +Z direction. This value + * cannot be negative. Defaults to 0. + * + * Incompatible with the following parameters: + * - {@link speedZMultiplier} + * + * See also: + * - {@link accelerationZMultiplier} + * + * **Argument**: {@link PropertyArgument.EffectAge Effect age} + */ + accelerationZ?: ScalarValue + /** + * Multiplier for {@link accelerationZ}. Defaults to 1. + * + * **Argument**: {@link PropertyArgument.EffectAge Effect age} + * + * Incompatible with the following parameters: + * - {@link speedZMultiplier} + */ + accelerationZMultiplier?: ScalarValue + /** + * Controls the acceleration of the node along its Y-axis. Defaults to 0. + * + * **Argument**: {@link PropertyArgument.EffectAge Effect age} + */ + accelerationY?: ScalarValue + /** + * The node will turn a random amount based on this value at intervals + * defined by {@link turnInterval}. Defaults to 0. + * + * **Argument**: {@link PropertyArgument.EffectAge Effect age} + * + * Incompatible with the following parameters: + * - {@link spinX} + * - {@link spinXMultiplier} + * - {@link spinY} + * - {@link spinYMultiplier} + * - {@link spinZ} + * - {@link spinZMultiplier} + */ + maxTurnAngle?: ScalarValue + /** + * The node will turn a random amount based on {@link maxTurnAngle} at + * this interval. The units are seconds, but due to how the field that stores + * this value works, the value will be rounded to the nearest 0.02 seconds. + * Defaults to 0. + * + * Incompatible with the following parameters: + * - {@link spinX} + * - {@link spinXMultiplier} + * - {@link spinY} + * - {@link spinYMultiplier} + * - {@link spinZ} + * - {@link spinZMultiplier} + */ + turnInterval?: number + /** + * Controls how well the node should follow the parent node if it is not + * attached. At 0, the node will not follow at all. At 1, the node will + * follow perfectly, as if attached to the parent node. Negative values will + * make the node move in the opposite direction compared to the parent node. + * Values greater than 1 will make the node exaggerate the parent node's + * movement. Defaults to 0. + * + * **Argument**: {@link PropertyArgument.EffectAge Effect age} + * + * Incompatible with the following parameters: + * - {@link spinX} + * - {@link spinXMultiplier} + * - {@link spinY} + * - {@link spinYMultiplier} + * - {@link spinZ} + * - {@link spinZMultiplier} + * + * See also: + * - {@link followRotation} + */ + followFactor?: ScalarValue + /** + * Disabling this will make {@link followFactor} only affect translation and + * not rotation. Defaults to true. + * + * **Argument**: {@link PropertyArgument.EffectAge Effect age} + * + * Incompatible with the following parameters: + * - {@link spinX} + * - {@link spinXMultiplier} + * - {@link spinY} + * - {@link spinYMultiplier} + * - {@link spinZ} + * - {@link spinZMultiplier} + */ + followRotation?: boolean +} +/** + * Controls the movement of the node. + * + * This class covers most of the Node Movement action types: + * - {@link ActionType.NodeSpin NodeSpin} + * - {@link ActionType.NodeAcceleration NodeAcceleration} + * - {@link ActionType.NodeAccelerationRandomTurns NodeAccelerationRandomTurns} + * - {@link ActionType.NodeAccelerationPartialFollow NodeAccelerationPartialFollow} + * - {@link ActionType.NodeAccelerationSpin NodeAccelerationSpin} + * - {@link ActionType.NodeSpeed NodeSpeed} + * - {@link ActionType.NodeSpeedRandomTurns NodeSpeedRandomTurns} + * - {@link ActionType.NodeSpeedPartialFollow NodeSpeedPartialFollow} + * - {@link ActionType.NodeSpeedSpin NodeSpeedSpin} + * + * Which one is produced by the constructor depends on what arguments are set. + * By default, the basic acceleration action is created. + */ +class NodeMovement extends Action { + + constructor({ + spinX = null, + spinXMultiplier = null, + spinY = null, + spinYMultiplier = null, + spinZ = null, + spinZMultiplier = null, + speedZ = null, + speedZMultiplier = null, + accelerationZ = null, + accelerationZMultiplier = null, + accelerationY = null, + maxTurnAngle = null, + turnInterval = null, + followFactor = null, + followRotation = null, + }: NodeMovementParams = {}) { + const speed = +(speedZMultiplier !== null) + const acceleration = +(accelerationZ !== null || accelerationZMultiplier !== null) << 1 + const spin = +[spinX, spinXMultiplier, spinY, spinYMultiplier, spinZ, spinZMultiplier].some(e => e !== null) << 2 + const randomTurns = +(maxTurnAngle !== null || turnInterval !== null) << 3 + const partialFollow = +(followFactor !== null || followRotation !== null) << 4 + const aos = +(speed || acceleration || speedZ !== null || accelerationY !== null) << 5 + spinX ??= 0 + spinXMultiplier ??= 1 + spinY ??= 0 + spinYMultiplier ??= 1 + spinZ ??= 0 + spinZMultiplier ??= 1 + speedZ ??= 0 + speedZMultiplier ??= 1 + accelerationZ ??= 0 + accelerationZMultiplier ??= 1 + accelerationY ??= 0 + maxTurnAngle ??= 0 + turnInterval ??= 0 + followFactor ??= 0 + followRotation ??= true + switch (speed | acceleration | spin | randomTurns | partialFollow | aos) { + case acceleration | aos: super(ActionType.NodeAcceleration, [ + new IntField, + new IntField, + new FloatField, + ], [], [ + scalarFromArg(speedZ), + scalarFromArg(accelerationZ), + scalarFromArg(accelerationZMultiplier), + scalarFromArg(accelerationY), + ]); break; + case spin: super(ActionType.NodeSpin, [ + new IntField(1), + ], [], [ + scalarFromArg(spinX), + scalarFromArg(spinXMultiplier), + scalarFromArg(spinY), + scalarFromArg(spinYMultiplier), + scalarFromArg(spinZ), + scalarFromArg(spinZMultiplier), + ]); break; + case acceleration | randomTurns | aos: super(ActionType.NodeAccelerationRandomTurns, [ + new IntField, + new IntField, + new IntField(Math.round(turnInterval * 50)), + ], [], [ + scalarFromArg(speedZ), + scalarFromArg(accelerationZ), + scalarFromArg(accelerationZMultiplier), + scalarFromArg(accelerationY), + scalarFromArg(maxTurnAngle), + ]); break; + case acceleration | partialFollow | aos: + case acceleration | partialFollow | randomTurns | aos: super(ActionType.NodeAccelerationPartialFollow, [ + new IntField, + new IntField(Math.round(turnInterval * 50)), + new BoolField(followRotation), + ], [], [ + scalarFromArg(speedZ), + scalarFromArg(accelerationZ), + scalarFromArg(accelerationZMultiplier), + scalarFromArg(accelerationY), + scalarFromArg(maxTurnAngle), + scalarFromArg(followFactor), + ]); break; + case acceleration | spin | aos: super(ActionType.NodeAccelerationSpin, [ + new IntField, + new IntField, + new IntField, + new IntField, + ], [], [ + scalarFromArg(speedZ), + scalarFromArg(accelerationZ), + scalarFromArg(accelerationZMultiplier), + scalarFromArg(accelerationY), + scalarFromArg(spinX), + scalarFromArg(spinXMultiplier), + scalarFromArg(spinY), + scalarFromArg(spinYMultiplier), + scalarFromArg(spinZ), + scalarFromArg(spinZMultiplier), + ]); break; + case speed | aos: super(ActionType.NodeSpeed, [ + new IntField, + new IntField, + new IntField, + ], [], [ + scalarFromArg(speedZ), + scalarFromArg(speedZMultiplier), + scalarFromArg(accelerationY), + ]); break; + case speed | randomTurns | aos: super(ActionType.NodeSpeedRandomTurns, [ + new IntField, + new IntField, + new IntField(Math.round(turnInterval * 50)), + ], [], [ + scalarFromArg(speedZ), + scalarFromArg(speedZMultiplier), + scalarFromArg(accelerationY), + scalarFromArg(maxTurnAngle), + ]); break; + case speed | partialFollow | aos: + case speed | partialFollow | randomTurns | aos: super(ActionType.NodeSpeedPartialFollow, [ + new IntField, + new IntField, + new IntField(Math.round(turnInterval * 50)), + new BoolField(followRotation), + ], [], [ + scalarFromArg(speedZ), + scalarFromArg(speedZMultiplier), + scalarFromArg(accelerationY), + scalarFromArg(maxTurnAngle), + scalarFromArg(followFactor), + ]); break; + case speed | spin | aos: super(ActionType.NodeSpeedSpin, [ + new IntField, + new IntField, + new IntField, + new IntField, + ], [], [ + scalarFromArg(speedZ), + scalarFromArg(speedZMultiplier), + scalarFromArg(accelerationY), + scalarFromArg(spinX), + scalarFromArg(spinXMultiplier), + scalarFromArg(spinY), + scalarFromArg(spinYMultiplier), + scalarFromArg(spinZ), + scalarFromArg(spinZMultiplier), + ]); break; + default: + if (speed && acceleration) { + throw new Error('Speed Z multiplier is not compatible with acceleration node movement actions.') + } + if (spin && (randomTurns || partialFollow)) { + throw new Error('Spin cannot be used together with random turns or partial follow in node movement actions.') + } + throw new Error('Incompatible arguments given to NodeMovement constructor.') + } + } + +} + +export interface NodeTransformParams { + /** + * Translates the node along the X-axis. Defaults to 0. + */ + translateX?: number + /** + * Translates the node along the Y-axis. Defaults to 0. + */ + translateY?: number + /** + * Translates the node along the Z-axis. Defaults to 0. + */ + translateZ?: number + /** + * Node rotation around the X-axis in degrees. Defaults to 0. + */ + rotateX?: number + /** + * Node rotation around the Y-axis in degrees. Defaults to 0. + */ + rotateY?: number + /** + * Node rotation around the Z-axis in degrees. Defaults to 0. + */ + rotateZ?: number + /** + * The maximum change in translation along the X-axis from + * {@link translateX the base value} caused by randomization. When the + * node is created, its translation along this axis will be a random + * number between the base value minus this value and the base value plus + * this value. Defaults to 0. + */ + randomTranslationX?: number + /** + * The maximum change in translation along the Y-axis from + * {@link translateY the base value} caused by randomization. When the + * node is created, its translation along this axis will be a random + * number between the base value minus this value and the base value plus + * this value. Defaults to 0. + */ + randomTranslationY?: number + /** + * The maximum change in translation along the Z-axis from + * {@link translateZ the base value} caused by randomization. When the + * node is created, its translation along this axis will be a random + * number between the base value minus this value and the base value plus + * this value. Defaults to 0. + */ + randomTranslationZ?: number + /** + * The maximum change in rotation around the X-axis from + * {@link rotateX the base value} caused by randomization. When the node + * is created, its rotation around this axis will be a random number between + * the base value minus this value and the base value plus this value. + * Defaults to 0. + */ + randomRotationX?: number + /** + * The maximum change in rotation around the Y-axis from + * {@link rotateY the base value} caused by randomization. When the node + * is created, its rotation around this axis will be a random number between + * the base value minus this value and the base value plus this value. + * Defaults to 0. + */ + randomRotationY?: number + /** + * The maximum change in rotation around the Z-axis from + * {@link rotateZ the base value} caused by randomization. When the node + * is created, its rotation around this axis will be a random number between + * the base value minus this value and the base value plus this value. + * Defaults to 0. + */ + randomRotationZ?: number +} +/** + * Sets the translation and rotation of the node. Optionally randomizes + * the translation and rotation. + * + * If any of the randomization parameters are not 0, it will create a + * {@link ActionType.RandomNodeTransform RandomNodeTransform action} instead of + * a {@link ActionType.StaticNodeTransform StaticNodeTransform action}, which + * have different numbers of fields. + * + * **Note about the X-axis**: + * Both of the action types represented by this class have the X-axis reversed. + * RandomNodeTransform only has it reversed for translation, not rotation. This + * class will automatically handle these strange inconsitencies and correct + * them when using its accessors or contructor parameters, but it is important + * to keep in mind if you are manually editing the fields of the action. + */ +class NodeTransform extends Action { + + declare fields1: FloatField[] + + constructor({ + translateX = 0, + translateY = 0, + translateZ = 0, + rotateX = 0, + rotateY = 0, + rotateZ = 0, + randomTranslationX = 0, + randomTranslationY = 0, + randomTranslationZ = 0, + randomRotationX = 0, + randomRotationY = 0, + randomRotationZ = 0, + }: NodeTransformParams = {}) { + if ( + randomTranslationX === 0 && + randomTranslationY === 0 && + randomTranslationZ === 0 && + randomRotationX === 0 && + randomRotationY === 0 && + randomRotationZ === 0 + ) { + super(ActionType.StaticNodeTransform, [ + // These two actions have their X-axis reversed for some reason + new FloatField(-translateX), + new FloatField(translateY), + new FloatField(translateZ), + new FloatField(-rotateX), + new FloatField(rotateY), + new FloatField(rotateZ), + ]) + } else { + super(ActionType.RandomNodeTransform, [ + new FloatField(-translateX), + new FloatField(translateY), + new FloatField(translateZ), + // While action 35 has the X-axis reversed for both translation and + // rotation, action 36 only has it reversed for translation + new FloatField(rotateX), + new FloatField(rotateY), + new FloatField(rotateZ), + new FloatField(randomTranslationX), + new FloatField(randomTranslationY), + new FloatField(randomTranslationZ), + new FloatField(randomRotationX), + new FloatField(randomRotationY), + new FloatField(randomRotationZ), + ]) + } + } + + get translateX() { return -this.fields1[0].value } + set translateX(value) { this.fields1[0].value = -value } + + get translateY() { return this.fields1[1].value } + set translateY(value) { this.fields1[1].value = value } + + get translateZ() { return this.fields1[2].value } + set translateZ(value) { this.fields1[2].value = value } + + get rotateX() { + switch (this.type) { + case ActionType.StaticNodeTransform: return -this.fields1[3].value + case ActionType.RandomNodeTransform: return this.fields1[3].value + } + } + set rotateX(value) { + switch (this.type) { + case ActionType.StaticNodeTransform: this.fields1[3].value = -value; break + case ActionType.RandomNodeTransform: this.fields1[3].value = value; break + } + } + + get rotateY() { return this.fields1[4].value } + set rotateY(value) { this.fields1[4].value = value } + + get rotateZ() { return this.fields1[5].value } + set rotateZ(value) { this.fields1[5].value = value } + + #setRandomizationField(index: number, value: number) { + if (value !== 0 && this.type === ActionType.StaticNodeTransform) { + this.type = ActionType.RandomNodeTransform + // The X rotation field needs to swap sign because it rotates the + // opposite direction in action 35 for some reason. + this.fields1[3].value *= -1 + this.fields1.push( + new FloatField(0), + new FloatField(0), + new FloatField(0), + new FloatField(0), + new FloatField(0), + new FloatField(0), + ) + } + this.fields1[index].value = value + } + + get randomTranslationX() { return this.fields1[6]?.value ?? 0 } + set randomTranslationX(value) { this.#setRandomizationField(6, value) } + + get randomTranslationY() { return this.fields1[7]?.value ?? 0 } + set randomTranslationY(value) { this.#setRandomizationField(7, value) } + + get randomTranslationZ() { return this.fields1[8]?.value ?? 0 } + set randomTranslationZ(value) { this.#setRandomizationField(8, value) } + + get randomRotationX() { return this.fields1[9]?.value ?? 0 } + set randomRotationX(value) { this.#setRandomizationField(9, value) } + + get randomRotationY() { return this.fields1[10]?.value ?? 0 } + set randomRotationY(value) { this.#setRandomizationField(10, value) } + + get randomRotationZ() { return this.fields1[11]?.value ?? 0 } + set randomRotationZ(value) { this.#setRandomizationField(11, value) } + + minify() { + if (this.type === ActionType.RandomNodeTransform) { + if ( + this.fields1[6].value === 0 && + this.fields1[7].value === 0 && + this.fields1[8].value === 0 && + this.fields1[9].value === 0 && + this.fields1[10].value === 0 && + this.fields1[11].value === 0 + ) { + if ( + this.fields1[0].value === 0 && + this.fields1[1].value === 0 && + this.fields1[2].value === 0 && + this.fields1[3].value === 0 && + this.fields1[4].value === 0 && + this.fields1[5].value === 0 + ) { + // This transform doesn't do anything, return a None action. + return new Action + } else { + // This doesn't use the randomization fields, return a static + // transform action instead + return new Action(ActionType.StaticNodeTransform, [ + this.fields1[0], + this.fields1[1], + this.fields1[2], + new FloatField(-this.fields1[3].value), + this.fields1[4], + this.fields1[5], + ]) + } + } else { + // This action can't be minified more without losing functionality, + // return it without any changes + return this + } + } else { + if ( + this.fields1[0].value === 0 && + this.fields1[1].value === 0 && + this.fields1[2].value === 0 && + this.fields1[3].value === 0 && + this.fields1[4].value === 0 && + this.fields1[5].value === 0 + ) { + // This transform doesn't do anything, return a None action. + return new Action + } else { + // This action can't be minified more without losing functionality, + // return it without any changes + return this + } + } + } + +} + +export interface ParticleMovementParams { + /** + * Downwards acceleration. This will always point towards global down, even + * if the node is rotated. Defaults to 0. + * + * **Argument**: {@link PropertyArgument.InstanceAge Instance age} + */ + gravity?: ScalarValue + /** + * The acceleration for the particles. The direction depends on the emitter + * shape. Defaults to 0. + * + * This can not be used together with any of the speed properties: + * - {@link speed} + * - {@link speedMultiplier} + * + * **Argument**: {@link PropertyArgument.InstanceAge Instance age} + */ + acceleration?: ScalarValue + /** + * Multiplier for the {@link acceleration} property. Defaults to 1. + * + * This can not be used together with any of the speed properties: + * - {@link speed} + * - {@link speedMultiplier} + * + * **Argument**: {@link PropertyArgument.InstanceAge Instance age} + */ + accelerationMultiplier?: ScalarValue + /** + * The speed that the particles will travel at. The direction depends on the + * emitter shape. Defaults to 0. + * + * This can not be used together with any of the acceleration properties: + * - {@link acceleration} + * - {@link accelerationMultiplier} + * + * Setting this will produce one of the speed actions instead of one of the + * acceleration actions: + * - {@link ActionType.ParticleSpeed ParticleSpeed} + * - {@link ActionType.ParticleSpeedRandomTurns ParticleSpeedRandomTurns} + * - {@link ActionType.ParticleSpeedPartialFollow ParticleSpeedPartialFollow} + * + * **Argument**: {@link PropertyArgument.InstanceAge Instance age} + */ + speed?: ScalarValue + /** + * Multiplier for the {@link speed} property. Defaults to 1. + * + * This can not be used together with any of the acceleration properties: + * - {@link acceleration} + * - {@link accelerationMultiplier} + * + * Setting this will produce one of the speed actions instead of one of the + * acceleration actions: + * - {@link ActionType.ParticleSpeed ParticleSpeed} + * - {@link ActionType.ParticleSpeedRandomTurns ParticleSpeedRandomTurns} + * - {@link ActionType.ParticleSpeedPartialFollow ParticleSpeedPartialFollow} + * + * **Argument**: {@link PropertyArgument.InstanceAge Instance age} + */ + speedMultiplier?: ScalarValue + /** + * The particles will turn a random amount based on this value at intervals + * defined by {@link turnInterval}. Defaults to 0. + * + * Unless one of the partial follow parameters are set, setting this will + * produce one of the random turns actions: + * - {@link ActionType.ParticleSpeedRandomTurns ParticleSpeedRandomTurns} + * - {@link ActionType.ParticleAccelerationRandomTurns ParticleAccelerationRandomTurns} + * + * **Argument**: {@link PropertyArgument.InstanceAge Instance age} + */ + maxTurnAngle?: ScalarValue + /** + * The particles will turn a random amount based on {@link maxTurnAngle} at + * this interval. The units are seconds, but due to how the field that stores + * this value works, the value will be rounded to the nearest 0.02 seconds. + * Defaults to 0. + * + * Unless one of the partial follow parameters are set, setting this will + * produce one of the random turns actions: + * - {@link ActionType.ParticleSpeedRandomTurns ParticleSpeedRandomTurns} + * - {@link ActionType.ParticleAccelerationRandomTurns ParticleAccelerationRandomTurns} + */ + turnInterval?: number + /** + * Disabling this will make {@link followFactor} only affect translation and + * not rotation. Defaults to true. + * + * Setting this will produce one of the partial follow actions: + * - {@link ActionType.ParticleSpeedPartialFollow ParticleSpeedPartialFollow} + * - {@link ActionType.ParticleAccelerationPartialFollow ParticleAccelerationPartialFollow} + * + * **Argument**: {@link PropertyArgument.InstanceAge Instance age} + */ + followRotation?: boolean + /** + * Controls how well the particles should follow the node if they are not + * attached. At 0, particles will not follow at all. At 1, particles will + * follow perfectly, as if attached to the node. Negative values will make + * the particles move in the opposite direction compared to the node. Values + * greater than 1 will make the particles exaggerate the node's movement. + * Defaults to 0. + * + * Setting this will produce one of the partial follow actions: + * - {@link ActionType.ParticleSpeedPartialFollow ParticleSpeedPartialFollow} + * - {@link ActionType.ParticleAccelerationPartialFollow ParticleAccelerationPartialFollow} + * + * **Argument**: {@link PropertyArgument.InstanceAge Instance age} + * + * See also: + * - {@link followRotation} + */ + followFactor?: ScalarValue + /** + * Unknown. Fields1, index 0. + */ + unkField0?: number + /** + * Unknown. Fields1, index 1. + * + * Only used when creating one of the basic particle movement actions: + * - {@link ActionType.ParticleAcceleration} + * - {@link ActionType.ParticleSpeed} + */ + unkField1?: number +} +/** + * Controls how particles move. + * + * This class covers all of the Particle Movement action types: + * - {@link ActionType.ParticleAcceleration ParticleAcceleration} + * - {@link ActionType.ParticleSpeed ParticleSpeed} + * - {@link ActionType.ParticleSpeedRandomTurns ParticleSpeedRandomTurns} + * - {@link ActionType.ParticleSpeedPartialFollow ParticleSpeedPartialFollow} + * - {@link ActionType.ParticleAccelerationRandomTurns ParticleAccelerationRandomTurns} + * - {@link ActionType.ParticleAccelerationPartialFollow ParticleAccelerationPartialFollow} + * + * Which one is produced by the constructor depends on what arguments are set. + * By default, the basic acceleration action is created. + */ +class ParticleMovement extends Action { + + constructor({ + gravity = 0, + maxTurnAngle = null, + turnInterval = null, + acceleration = null, + accelerationMultiplier = null, + speed = null, + speedMultiplier = null, + followRotation = null, + followFactor = null, + unkField0 = 0, + unkField1 = null, + }: ParticleMovementParams = {}) { + let asProp: ScalarValue, asMultProp: ScalarValue + const isSpeedAct = speed !== null || speedMultiplier !== null + if (isSpeedAct) { + if (acceleration !== null || accelerationMultiplier !== null) { + throw new Error('The speed properties and the acceleration properties cannot be used together in a ParticleMovement action.') + } + asProp = speed ?? 0 + asMultProp = speedMultiplier ?? 1 + } else { + asProp = acceleration ?? 0 + asMultProp = accelerationMultiplier ?? 1 + } + if (followFactor !== null || followRotation !== null) { + turnInterval ??= 0 + maxTurnAngle ??= 0 + followRotation ??= true + followFactor ??= 0 + super( + isSpeedAct ? + ActionType.ParticleSpeedPartialFollow + : ActionType.ParticleAccelerationPartialFollow, + [ + new FloatField(unkField0), + new IntField(Math.round(turnInterval * 50)), + new BoolField(!followRotation), + ], [], [ + scalarFromArg(gravity), + scalarFromArg(asProp), + scalarFromArg(asMultProp), + scalarFromArg(maxTurnAngle), + scalarFromArg(followFactor), + ] + ) + } else if (turnInterval !== null || maxTurnAngle !== null) { + turnInterval ??= 0 + maxTurnAngle ??= 0 + super( + isSpeedAct ? + ActionType.ParticleSpeedRandomTurns + : ActionType.ParticleAccelerationRandomTurns, + [ + new FloatField(unkField0), + new IntField(Math.round(turnInterval * 50)), + ], [], [ + scalarFromArg(gravity), + scalarFromArg(asProp), + scalarFromArg(asMultProp), + scalarFromArg(maxTurnAngle), + ] + ) + } else { + unkField1 ??= 0 + super( + isSpeedAct ? + ActionType.ParticleSpeed + : ActionType.ParticleAcceleration, + [ + new FloatField(unkField0), + new FloatField(unkField1), + ], [], [ + scalarFromArg(gravity), + scalarFromArg(asProp), + scalarFromArg(asMultProp), + ] + ) + } + } + +} + +/** + * References another SFX by its ID. + */ +class SFXReference extends DataAction { + + /** + * The ID of the referenced SFX. + */ + sfx: number + + /** + * @param sfx The ID of the referenced SFX. + */ + constructor(sfx: number) { + super(ActionType.SFXReference) + this.assign({ sfx }) + } + +} + +/** + * Maps states to effects in the parent node. + * + * The index of each value represents the index of the state, and the value + * represents the index of the effect that should be active when the state is + * active. + */ +class StateEffectMap extends Action { + + constructor(...effectIndices: number[]) { + if (effectIndices.length === 0) { + effectIndices.push(0) + } + super(ActionType.StateEffectMap, [], [], [], [], [ + new Section10(effectIndices.map(i => new IntField(i))) + ]) + } + + get effectIndices() { return this.section10s[0].fields.map(e => e.value) } + set effectIndices(value: number[]) { + this.section10s[0].fields = value.map(e => new IntField(e)) + } + +} + +/** + * Used in {@link EffectType.SharedEmitter} to emit all particles from child + * nodes every time the shared emitter emits something. + */ +class EmitAllParticles extends Action { + + constructor() { + super(ActionType.EmitAllParticles) + } + +} + +/** + * Used in {@link EffectType.SharedEmitter} to emit a particle from a random + * child node every time the shared emitter emits something. + */ +class EmitRandomParticles extends Action { + + constructor(...weights: number[]) { + super(ActionType.EmitRandomParticles, [], [], [], [], [ + new Section10(weights.map(w => new IntField(w))) + ]) + } + +} + +/** + * Emits one particle once. + * + * Contains no fields or properties. + */ +class OneTimeEmitter extends Action { + + constructor() { + super(ActionType.OneTimeEmitter) + } + +} + +/** + * Makes all particles use the default initial direction from the emitter. See + * {@link InitialDirection} for more information. + */ +class NoParticleSpread extends Action { + + constructor() { + super(ActionType.NoParticleSpread) + } + +} + +/*#ActionClasses start*/ +export interface NodeTranslationParams { + /** + * An offset for the position of the node. + * + * **Default**: `[0, 0, 0]` + * + * **Argument**: {@link PropertyArgument.EffectAge Effect age} + */ + translation?: Vector3Value + /** + * Unknown. An integer that has at least three valid values: 0, 1, 2. It did not exist until Elden Ring. + * + * **Default**: `0` + */ + unk_er_f1_0?: number +} + +/** + * Translates the node using a property, meaning it can be animated. This can be useful if you need the node to follow a specific path. + */ +class NodeTranslation extends DataAction { + declare type: ActionType.NodeTranslation + /** + * An offset for the position of the node. + * + * **Argument**: {@link PropertyArgument.EffectAge Effect age} + */ + translation: Vector3Value + /** + * Unknown. An integer that has at least three valid values: 0, 1, 2. It did not exist until Elden Ring. + */ + unk_er_f1_0: number + constructor(props: NodeTranslationParams = {}) { + super(ActionType.NodeTranslation) + this.assign(props) + } +} + +export interface NodeAttachToCameraParams { + /** + * Disable this to stop the node from following the rotation of the camera. + * + * **Default**: `true` + */ + followRotation?: boolean + /** + * Unknown integer. + * + * **Default**: `1` + */ + unk_ds3_f1_1?: number +} + +/** + * Attaches the node to the camera. + */ +class NodeAttachToCamera extends DataAction { + declare type: ActionType.NodeAttachToCamera + /** + * Disable this to stop the node from following the rotation of the camera. + */ + followRotation: boolean + unk_ds3_f1_1: number + constructor(props: NodeAttachToCameraParams = {}) { + super(ActionType.NodeAttachToCamera) + this.assign(props) + } +} + +export interface PlaySoundParams { + /** + * The ID of the sound to play. + * + * **Default**: `0` + */ + sound?: number + /** + * Controls whether the sound will repeat or not. + * + * Does not seem to work in Elden Ring, and probably doesn't in Armored Core 6 either. + * + * **Default**: `false` + */ + repeat?: boolean + /** + * Volume multiplier. + * + * Does not seem to work in Elden Ring, and probably doesn't in Armored Core 6 either. + * + * **Default**: `1` + */ + volume?: number +} + +/** + * Plays a sound effect. + */ +class PlaySound extends DataAction { + declare type: ActionType.PlaySound + /** + * The ID of the sound to play. + */ + sound: number + /** + * Controls whether the sound will repeat or not. + * + * Does not seem to work in Elden Ring, and probably doesn't in Armored Core 6 either. + */ + repeat: boolean + /** + * Volume multiplier. + * + * Does not seem to work in Elden Ring, and probably doesn't in Armored Core 6 either. + */ + volume: number + constructor(props: PlaySoundParams = {}) { + super(ActionType.PlaySound) + this.assign(props) + } +} + +export interface NodeAttributesParams { + /** + * Controls how the node is attached to the parent node. + * + * **Default**: {@link AttachMode.Parent} + */ + attachment?: AttachMode + /** + * The node duration in seconds. Can be set to -1 to make the node last forever. + * + * **Default**: `-1` + * + * **Argument**: {@link PropertyArgument.Constant0 Constant 0} + */ + duration?: ScalarValue + /** + * The delay in seconds before the node becomes active. + * + * **Default**: `0` + */ + delay?: number + /** + * Unknown integer. + * + * **Default**: `1` + */ + unk_ds3_f1_1?: number + /** + * Unknown float. + * + * **Default**: `0` + */ + unk_ds3_f1_3?: number +} + +/** + * Controls various things about the node, like its duration, and how it is attached to the parent node. + */ +class NodeAttributes extends DataAction { + declare type: ActionType.NodeAttributes + /** + * Controls how the node is attached to the parent node. + */ + attachment: AttachMode + /** + * The node duration in seconds. Can be set to -1 to make the node last forever. + * + * **Argument**: {@link PropertyArgument.Constant0 Constant 0} + */ + duration: ScalarValue + /** + * The delay in seconds before the node becomes active. + */ + delay: number + unk_ds3_f1_1: number + unk_ds3_f1_3: number + constructor(props: NodeAttributesParams = {}) { + super(ActionType.NodeAttributes) + this.assign(props) + } +} + +export interface ParticleAttributesParams { + /** + * Controls how the particles are attached to the node. + * + * **Default**: {@link AttachMode.Parent} + */ + attachment?: AttachMode + /** + * The particle duration in seconds. Can be set to -1 to make particles last forever. + * + * **Default**: `-1` + * + * **Argument**: {@link PropertyArgument.Constant0 Constant 0} + */ + duration?: ScalarValue +} + +/** + * Controls the duration of particles emitted by the node, and how the particles are attached to the node. + */ +class ParticleAttributes extends DataAction { + declare type: ActionType.ParticleAttributes + /** + * Controls how the particles are attached to the node. + */ + attachment: AttachMode + /** + * The particle duration in seconds. Can be set to -1 to make particles last forever. + * + * **Argument**: {@link PropertyArgument.Constant0 Constant 0} + */ + duration: ScalarValue + constructor(props: ParticleAttributesParams = {}) { + super(ActionType.ParticleAttributes) + this.assign(props) + } +} + +export interface Unk130Params { + /** + * Unknown integer. + * + * **Default**: `1` + */ + unk_ds3_f1_0?: number + /** + * Unknown integer. + * + * **Default**: `0` + */ + unk_ds3_f1_1?: number + /** + * Unknown integer. + * + * **Default**: `0` + */ + unk_ds3_f1_2?: number + /** + * Unknown integer. + * + * **Default**: `0` + */ + unk_ds3_f1_3?: number + /** + * Unknown integer. + * + * **Default**: `0` + */ + unk_ds3_f1_4?: number + /** + * Unknown integer. + * + * **Default**: `0` + */ + unk_ds3_f1_5?: number + /** + * Unknown integer. + * + * **Default**: `0` + */ + unk_ds3_f1_6?: number + /** + * Unknown integer. + * + * **Default**: `0` + */ + unk_ds3_f1_7?: number + /** + * Unknown integer. + * + * **Default**: `0` + */ + unk_ds3_f1_8?: number + /** + * Unknown. + * + * **Default**: `0` + */ + unk_ds3_p1_0?: ScalarValue + /** + * Unknown. + * + * **Default**: `0` + */ + unk_ds3_p1_1?: ScalarValue + /** + * Unknown. + * + * **Default**: `0` + */ + unk_ds3_p1_2?: ScalarValue + /** + * Unknown. + * + * **Default**: `0` + */ + unk_ds3_p1_3?: ScalarValue + /** + * Unknown. + * + * **Default**: `0` + */ + unk_ds3_p1_4?: ScalarValue + /** + * Unknown. + * + * **Default**: `0` + */ + unk_ds3_p1_5?: ScalarValue + /** + * Unknown. + * + * **Default**: `0` + */ + unk_ds3_p1_6?: ScalarValue + /** + * Unknown. + * + * **Default**: `0` + */ + unk_ds3_p1_7?: ScalarValue +} + +/** + * Unknown action that is in every basic effect in every game, and still literally nothing is known about it. + */ +class Unk130 extends DataAction { + declare type: ActionType.Unk130 + unk_ds3_f1_0: number + unk_ds3_f1_1: number + unk_ds3_f1_2: number + unk_ds3_f1_3: number + unk_ds3_f1_4: number + unk_ds3_f1_5: number + unk_ds3_f1_6: number + unk_ds3_f1_7: number + unk_ds3_f1_8: number + unk_ds3_p1_0: ScalarValue + unk_ds3_p1_1: ScalarValue + unk_ds3_p1_2: ScalarValue + unk_ds3_p1_3: ScalarValue + unk_ds3_p1_4: ScalarValue + unk_ds3_p1_5: ScalarValue + unk_ds3_p1_6: ScalarValue + unk_ds3_p1_7: ScalarValue + constructor(props: Unk130Params = {}) { + super(ActionType.Unk130) + this.assign(props) + } +} + +export interface ParticleModifierParams { + /** + * Scales the particles emitted from this node uniformly based on {@link scaleX}. The other scale properties in this action have no effect when this is enabled. + * + * **Default**: `false` + * + * See also: + * - {@link scaleX} + * - {@link scaleY} + * - {@link scaleZ} + */ + uniformScale?: boolean + /** + * Controls the speed of the particles emitted from this node, but only if the effect has an action in slot 10 that enables acceleration of particles. The direction is the {@link InitialDirection initial particle direction}. + * + * **Default**: `0` + * + * **Argument**: {@link PropertyArgument.EffectAge Effect age} + */ + speed?: ScalarValue + /** + * Multiplier for the scale along the X-axis for the particles emitted from this node. + * + * If {@link uniformScale} is enabled, this also affects the Y and Z axes. + * + * **Default**: `1` + * + * **Argument**: {@link PropertyArgument.EffectAge Effect age} + */ + scaleX?: ScalarValue + /** + * Multiplier for the scale along the Y-axis for the particles emitted from this node. + * + * If {@link uniformScale} is enabled, {@link scaleX} also affects the Y-axis, and this property is ignored. + * + * **Default**: `1` + * + * **Argument**: {@link PropertyArgument.EffectAge Effect age} + */ + scaleY?: ScalarValue + /** + * Multiplier for the scale along the Z-axis for the particles emitted from this node. + * + * If {@link uniformScale} is enabled, {@link scaleX} also affects the Z-axis, and this property is ignored. + * + * **Default**: `1` + * + * **Argument**: {@link PropertyArgument.EffectAge Effect age} + */ + scaleZ?: ScalarValue + /** + * Color multiplier for the particles emitted from this node. + * + * **Default**: `1` + * + * **Argument**: {@link PropertyArgument.EffectAge Effect age} + */ + color?: Vector4Value +} + +/** + * Modifies particles in various ways. + * + * Note: This is **not** a {@link Modifier property modifier}, it is an action that modifies particles emitted from the same node. + */ +class ParticleModifier extends DataAction { + declare type: ActionType.ParticleModifier + /** + * Scales the particles emitted from this node uniformly based on {@link scaleX}. The other scale properties in this action have no effect when this is enabled. + * + * See also: + * - {@link scaleX} + * - {@link scaleY} + * - {@link scaleZ} + */ + uniformScale: boolean + /** + * Controls the speed of the particles emitted from this node, but only if the effect has an action in slot 10 that enables acceleration of particles. The direction is the {@link InitialDirection initial particle direction}. + * + * **Argument**: {@link PropertyArgument.EffectAge Effect age} + */ + speed: ScalarValue + /** + * Multiplier for the scale along the X-axis for the particles emitted from this node. + * + * If {@link uniformScale} is enabled, this also affects the Y and Z axes. + * + * **Argument**: {@link PropertyArgument.EffectAge Effect age} + */ + scaleX: ScalarValue + /** + * Multiplier for the scale along the Y-axis for the particles emitted from this node. + * + * If {@link uniformScale} is enabled, {@link scaleX} also affects the Y-axis, and this property is ignored. + * + * **Argument**: {@link PropertyArgument.EffectAge Effect age} + */ + scaleY: ScalarValue + /** + * Multiplier for the scale along the Z-axis for the particles emitted from this node. + * + * If {@link uniformScale} is enabled, {@link scaleX} also affects the Z-axis, and this property is ignored. + * + * **Argument**: {@link PropertyArgument.EffectAge Effect age} + */ + scaleZ: ScalarValue + /** + * Color multiplier for the particles emitted from this node. + * + * **Argument**: {@link PropertyArgument.EffectAge Effect age} + */ + color: Vector4Value + constructor(props: ParticleModifierParams = {}) { + super(ActionType.ParticleModifier) + this.assign(props) + } +} + +export interface LevelOfDetailThresholdsParams { + /** + * The node duration in seconds. Can be set to -1 to make the node last forever. + * + * **Default**: `-1` + * + * **Argument**: {@link PropertyArgument.Constant0 Constant 0} + */ + duration?: ScalarValue + /** + * Distance threshold for child node 0. + * + * **Default**: `1000` + */ + threshold0?: number + /** + * Distance threshold for child node 1. + * + * **Default**: `1000` + */ + threshold1?: number + /** + * Distance threshold for child node 2. + * + * **Default**: `1000` + */ + threshold2?: number + /** + * Distance threshold for child node 3. + * + * **Default**: `1000` + */ + threshold3?: number + /** + * Distance threshold for child node 4. + * + * **Default**: `1000` + */ + threshold4?: number +} + +/** + * Used in the {@link EffectType.LevelOfDetail level of detail effect} to manage the duration and thresholds for the {@link NodeType.LevelOfDetail level of detail node}. + */ +class LevelOfDetailThresholds extends DataAction { + declare type: ActionType.LevelOfDetailThresholds + /** + * The node duration in seconds. Can be set to -1 to make the node last forever. + * + * **Argument**: {@link PropertyArgument.Constant0 Constant 0} + */ + duration: ScalarValue + /** + * Distance threshold for child node 0. + */ + threshold0: number + /** + * Distance threshold for child node 1. + */ + threshold1: number + /** + * Distance threshold for child node 2. + */ + threshold2: number + /** + * Distance threshold for child node 3. + */ + threshold3: number + /** + * Distance threshold for child node 4. + */ + threshold4: number + constructor(props: LevelOfDetailThresholdsParams = {}) { + super(ActionType.LevelOfDetailThresholds) + this.assign(props) + } +} + +export interface PeriodicEmitterParams { + /** + * Time between emitting new particles in seconds. + * + * **Default**: `1` + * + * **Argument**: {@link PropertyArgument.EffectAge Effect age} + */ + interval?: ScalarValue + /** + * The number of particles to emit per interval. They all spawn at the same time per interval. + * + * **Default**: `1` + * + * **Argument**: {@link PropertyArgument.EffectAge Effect age} + */ + perInterval?: ScalarValue + /** + * The total number of intervals to emit particles. Once this limit is reached, the emitter is will stop emitting. Can be set to -1 to disable the limit. + * + * **Default**: `-1` + * + * **Argument**: {@link PropertyArgument.EffectAge Effect age} + */ + totalIntervals?: ScalarValue + /** + * Maximum number of concurrent particles. Can be set to -1 to disable the limit. + * + * **Default**: `-1` + * + * **Argument**: {@link PropertyArgument.EffectAge Effect age} + */ + maxConcurrent?: ScalarValue + /** + * Unknown integer. + * + * **Default**: `1` + */ + unk_ds3_f1_1?: number +} + +/** + * Emits particles periodically. + */ +class PeriodicEmitter extends DataAction { + declare type: ActionType.PeriodicEmitter + /** + * Time between emitting new particles in seconds. + * + * **Argument**: {@link PropertyArgument.EffectAge Effect age} + */ + interval: ScalarValue + /** + * The number of particles to emit per interval. They all spawn at the same time per interval. + * + * **Argument**: {@link PropertyArgument.EffectAge Effect age} + */ + perInterval: ScalarValue + /** + * The total number of intervals to emit particles. Once this limit is reached, the emitter is will stop emitting. Can be set to -1 to disable the limit. + * + * **Argument**: {@link PropertyArgument.EffectAge Effect age} + */ + totalIntervals: ScalarValue + /** + * Maximum number of concurrent particles. Can be set to -1 to disable the limit. + * + * **Argument**: {@link PropertyArgument.EffectAge Effect age} + */ + maxConcurrent: ScalarValue + unk_ds3_f1_1: number + constructor(props: PeriodicEmitterParams = {}) { + super(ActionType.PeriodicEmitter) + this.assign(props) + } +} + +export interface EqualDistanceEmitterParams { + /** + * How much the emitter must move to trigger emission. + * + * **Default**: `0.1` + * + * **Argument**: {@link PropertyArgument.EffectAge Effect age} + */ + threshold?: ScalarValue + /** + * Maximum number of concurrent particles. Can be set to -1 to disable the limit. + * + * **Default**: `-1` + * + * **Argument**: {@link PropertyArgument.EffectAge Effect age} + */ + maxConcurrent?: ScalarValue + /** + * Unknown integer. + * + * **Default**: `1` + */ + unk_ds3_f1_0?: number + /** + * Unknown integer. + * + * **Default**: `0` + */ + unk_ds3_f1_1?: number + /** + * Unknown. + * + * **Default**: `-1` + * + * **Argument**: {@link PropertyArgument.EffectAge Effect age} + */ + unk_ds3_p1_1?: ScalarValue +} + +/** + * Emits particles once it has moved a certain distance from where it last emitted particles. + */ +class EqualDistanceEmitter extends DataAction { + declare type: ActionType.EqualDistanceEmitter + /** + * How much the emitter must move to trigger emission. + * + * **Argument**: {@link PropertyArgument.EffectAge Effect age} + */ + threshold: ScalarValue + /** + * Maximum number of concurrent particles. Can be set to -1 to disable the limit. + * + * **Argument**: {@link PropertyArgument.EffectAge Effect age} + */ + maxConcurrent: ScalarValue + unk_ds3_f1_0: number + unk_ds3_f1_1: number + unk_ds3_p1_1: ScalarValue + constructor(props: EqualDistanceEmitterParams = {}) { + super(ActionType.EqualDistanceEmitter) + this.assign(props) + } +} + +export interface PointEmitterShapeParams { + /** + * Controls the initial direction for particles. See {@link InitialDirection} for more information. + * + * **Default**: {@link InitialDirection.Emitter} + */ + direction?: InitialDirection +} + +/** + * Makes the emitter a single point. + */ +class PointEmitterShape extends DataAction { + declare type: ActionType.PointEmitterShape + /** + * Controls the initial direction for particles. See {@link InitialDirection} for more information. + */ + direction: InitialDirection + constructor(props: PointEmitterShapeParams = {}) { + super(ActionType.PointEmitterShape) + this.assign(props) + } +} + +export interface DiskEmitterShapeParams { + /** + * Controls the initial direction for particles. See {@link InitialDirection} for more information. + * + * **Default**: {@link InitialDirection.Emitter} + */ + direction?: InitialDirection + /** + * Radius of the disk. + * + * **Default**: `1` + * + * **Argument**: {@link PropertyArgument.EffectAge Effect age} + */ + radius?: ScalarValue + /** + * Controls how the random emission points are distributed within the disk. + * - At 0, particles are equally likely to emit from anywhere inside the disk. + * - At 1, particles have a 100% chance of being emitted from the center point. + * - At -1, particles have a 100% chance of being emitted from the perimeter circle of the disk. + * - Values between these smoothly blend between them. + * + * **Default**: `0` + * + * **Argument**: {@link PropertyArgument.EffectAge Effect age} + */ + distribution?: ScalarValue +} + +/** + * Makes the emitter disk-shaped. + */ +class DiskEmitterShape extends DataAction { + declare type: ActionType.DiskEmitterShape + /** + * Controls the initial direction for particles. See {@link InitialDirection} for more information. + */ + direction: InitialDirection + /** + * Radius of the disk. + * + * **Argument**: {@link PropertyArgument.EffectAge Effect age} + */ + radius: ScalarValue + /** + * Controls how the random emission points are distributed within the disk. + * - At 0, particles are equally likely to emit from anywhere inside the disk. + * - At 1, particles have a 100% chance of being emitted from the center point. + * - At -1, particles have a 100% chance of being emitted from the perimeter circle of the disk. + * - Values between these smoothly blend between them. + * + * **Argument**: {@link PropertyArgument.EffectAge Effect age} + */ + distribution: ScalarValue + constructor(props: DiskEmitterShapeParams = {}) { + super(ActionType.DiskEmitterShape) + this.assign(props) + } +} + +export interface RectangleEmitterShapeParams { + /** + * Controls the initial direction for particles. See {@link InitialDirection} for more information. + * + * **Default**: {@link InitialDirection.Emitter} + */ + direction?: InitialDirection + /** + * Width of the rectangle. + * + * **Default**: `1` + * + * **Argument**: {@link PropertyArgument.EffectAge Effect age} + */ + sizeX?: ScalarValue + /** + * Height of the rectangle. + * + * **Default**: `1` + * + * **Argument**: {@link PropertyArgument.EffectAge Effect age} + */ + sizeY?: ScalarValue + /** + * Controls how the random emission points are distributed within the rectangle. + * - At 0, particles are equally likely to emit from anywhere inside the rectangle. + * - At 1, particles have a 100% chance of being emitted from the center point. + * - At -1, particles have a 100% chance of being emitted from the perimeter of the rectangle. + * - Values between these smoothly blend between them. + * + * **Default**: `0` + * + * **Argument**: {@link PropertyArgument.EffectAge Effect age} + */ + distribution?: ScalarValue +} + +/** + * Makes the emitter rectangular. + */ +class RectangleEmitterShape extends DataAction { + declare type: ActionType.RectangleEmitterShape + /** + * Controls the initial direction for particles. See {@link InitialDirection} for more information. + */ + direction: InitialDirection + /** + * Width of the rectangle. + * + * **Argument**: {@link PropertyArgument.EffectAge Effect age} + */ + sizeX: ScalarValue + /** + * Height of the rectangle. + * + * **Argument**: {@link PropertyArgument.EffectAge Effect age} + */ + sizeY: ScalarValue + /** + * Controls how the random emission points are distributed within the rectangle. + * - At 0, particles are equally likely to emit from anywhere inside the rectangle. + * - At 1, particles have a 100% chance of being emitted from the center point. + * - At -1, particles have a 100% chance of being emitted from the perimeter of the rectangle. + * - Values between these smoothly blend between them. + * + * **Argument**: {@link PropertyArgument.EffectAge Effect age} + */ + distribution: ScalarValue + constructor(props: RectangleEmitterShapeParams = {}) { + super(ActionType.RectangleEmitterShape) + this.assign(props) + } +} + +export interface SphereEmitterShapeParams { + /** + * If true, particles will be emitted from anywhere within the sphere. Otherwise the particles will be emitted only from the surface of the sphere. + * + * **Default**: `true` + */ + emitInside?: boolean + /** + * Radius of the sphere. + * + * **Default**: `1` + * + * **Argument**: {@link PropertyArgument.EffectAge Effect age} + */ + radius?: ScalarValue +} + +/** + * Makes the emitter spherical. + */ +class SphereEmitterShape extends DataAction { + declare type: ActionType.SphereEmitterShape + /** + * If true, particles will be emitted from anywhere within the sphere. Otherwise the particles will be emitted only from the surface of the sphere. + */ + emitInside: boolean + /** + * Radius of the sphere. + * + * **Argument**: {@link PropertyArgument.EffectAge Effect age} + */ + radius: ScalarValue + constructor(props: SphereEmitterShapeParams = {}) { + super(ActionType.SphereEmitterShape) + this.assign(props) + } +} + +export interface BoxEmitterShapeParams { + /** + * Controls the initial direction for particles. See {@link InitialDirection} for more information. + * + * **Default**: {@link InitialDirection.Emitter} + */ + direction?: InitialDirection + /** + * If true, particles will be emitted from anywhere within the cuboid. Otherwise the particles will be emitted only from the surface of the cuboid. + * + * **Default**: `true` + */ + emitInside?: boolean + /** + * Width of the cuboid. + * + * **Default**: `1` + * + * **Argument**: {@link PropertyArgument.EffectAge Effect age} + */ + sizeX?: ScalarValue + /** + * Height of the cuboid. + * + * **Default**: `1` + * + * **Argument**: {@link PropertyArgument.EffectAge Effect age} + */ + sizeY?: ScalarValue + /** + * Depth of the cuboid. + * + * **Default**: `1` + * + * **Argument**: {@link PropertyArgument.EffectAge Effect age} + */ + sizeZ?: ScalarValue +} + +/** + * Makes the emitter cuboidal. + */ +class BoxEmitterShape extends DataAction { + declare type: ActionType.BoxEmitterShape + /** + * Controls the initial direction for particles. See {@link InitialDirection} for more information. + */ + direction: InitialDirection + /** + * If true, particles will be emitted from anywhere within the cuboid. Otherwise the particles will be emitted only from the surface of the cuboid. + */ + emitInside: boolean + /** + * Width of the cuboid. + * + * **Argument**: {@link PropertyArgument.EffectAge Effect age} + */ + sizeX: ScalarValue + /** + * Height of the cuboid. + * + * **Argument**: {@link PropertyArgument.EffectAge Effect age} + */ + sizeY: ScalarValue + /** + * Depth of the cuboid. + * + * **Argument**: {@link PropertyArgument.EffectAge Effect age} + */ + sizeZ: ScalarValue + constructor(props: BoxEmitterShapeParams = {}) { + super(ActionType.BoxEmitterShape) + this.assign(props) + } +} + +export interface CylinderEmitterShapeParams { + /** + * Controls the initial direction for particles. See {@link InitialDirection} for more information. + * + * **Default**: {@link InitialDirection.Emitter} + */ + direction?: InitialDirection + /** + * If true, particles will be emitted from anywhere within the cylinder. Otherwise the particles will be emitted only from the surface of the cylinder, excluding the ends. + * + * **Default**: `true` + */ + emitInside?: boolean + /** + * If true, the cylinder will be aligned with the Y-axis instead of the Z-axis. + * + * **Default**: `true` + */ + yAxis?: boolean + /** + * The radius of the cylinder. + * + * **Default**: `1` + * + * **Argument**: {@link PropertyArgument.EffectAge Effect age} + */ + radius?: ScalarValue + /** + * The height of the cylinder. + * + * **Default**: `1` + * + * **Argument**: {@link PropertyArgument.EffectAge Effect age} + */ + height?: ScalarValue +} + +/** + * Makes the emitter cylindrical. + */ +class CylinderEmitterShape extends DataAction { + declare type: ActionType.CylinderEmitterShape + /** + * Controls the initial direction for particles. See {@link InitialDirection} for more information. + */ + direction: InitialDirection + /** + * If true, particles will be emitted from anywhere within the cylinder. Otherwise the particles will be emitted only from the surface of the cylinder, excluding the ends. + */ + emitInside: boolean + /** + * If true, the cylinder will be aligned with the Y-axis instead of the Z-axis. + */ + yAxis: boolean + /** + * The radius of the cylinder. + * + * **Argument**: {@link PropertyArgument.EffectAge Effect age} + */ + radius: ScalarValue + /** + * The height of the cylinder. + * + * **Argument**: {@link PropertyArgument.EffectAge Effect age} + */ + height: ScalarValue + constructor(props: CylinderEmitterShapeParams = {}) { + super(ActionType.CylinderEmitterShape) + this.assign(props) + } +} + +export interface CircularParticleSpreadParams { + /** + * No so much unknown, just unnamed. If enabled, this limits the possible directions to only positive values on one axis, effectively cutting the cone of possible directions in half. + * + * **Default**: `false` + */ + unk_er_f1_0?: boolean + /** + * The maximum change in direction in degrees, the angle of the cone. + * + * **Default**: `30` + * + * **Argument**: {@link PropertyArgument.EffectAge Effect age} + */ + angle?: ScalarValue + /** + * Controls the distribution of the random directions that can be chosen. + * - At 0, all directions within the cone have an equal chance of being chosen. + * - At 1, the default direction is guaranteed to be chosen. + * - At -1, the maximum change in direction is guaranteed, meaning the chosen direction will always be a fixed number of degrees away from the default direction based on {@link angle}. + * - Values between these values smoothly blend between them. + * - Values outside of the -1 to 1 range also work, but may do some unexpected things. + * + * **Default**: `0` + * + * **Argument**: {@link PropertyArgument.EffectAge Effect age} + */ + distribution?: ScalarValue +} + +/** + * Gives each particle a random initial direction offset within a circular cone. See {@link InitialDirection} for more information. + */ +class CircularParticleSpread extends DataAction { + declare type: ActionType.CircularParticleSpread + /** + * No so much unknown, just unnamed. If enabled, this limits the possible directions to only positive values on one axis, effectively cutting the cone of possible directions in half. + */ + unk_er_f1_0: boolean + /** + * The maximum change in direction in degrees, the angle of the cone. + * + * **Argument**: {@link PropertyArgument.EffectAge Effect age} + */ + angle: ScalarValue + /** + * Controls the distribution of the random directions that can be chosen. + * - At 0, all directions within the cone have an equal chance of being chosen. + * - At 1, the default direction is guaranteed to be chosen. + * - At -1, the maximum change in direction is guaranteed, meaning the chosen direction will always be a fixed number of degrees away from the default direction based on {@link angle}. + * - Values between these values smoothly blend between them. + * - Values outside of the -1 to 1 range also work, but may do some unexpected things. + * + * **Argument**: {@link PropertyArgument.EffectAge Effect age} + */ + distribution: ScalarValue + constructor(props: CircularParticleSpreadParams = {}) { + super(ActionType.CircularParticleSpread) + this.assign(props) + } +} + +export interface EllipticalParticleSpreadParams { + /** + * No so much unknown, just unnamed. If enabled, this limits the possible directions to only positive values on one axis, effectively cutting the cone of possible directions in half. + * + * **Default**: `false` + */ + unk_er_f1_0?: boolean + /** + * The maximum change in direction in degrees, one of the angles of the elliptical cone. + * + * **Default**: `30` + * + * **Argument**: {@link PropertyArgument.EffectAge Effect age} + * + * See also: + * - {@link angleY} + */ + angleX?: ScalarValue + /** + * The maximum change in direction in degrees, one of the angles of the elliptical cone. + * + * **Default**: `30` + * + * **Argument**: {@link PropertyArgument.EffectAge Effect age} + * + * See also: + * - {@link angleY} + */ + angleY?: ScalarValue + /** + * Controls the distribution of the random directions that can be chosen. + * - At 0, all directions within the cone have an equal chance of being chosen. + * - At 1, the default direction is guaranteed to be chosen. + * - At -1, the maximum change in direction is guaranteed, meaning the chosen direction will always be a fixed number of degrees away from the default direction based on {@link angleX} and {@link angleY}. + * - Values between these values smoothly blend between them. + * - Values outside of the -1 to 1 range also work, but may do some unexpected things. + * + * **Default**: `0` + * + * **Argument**: {@link PropertyArgument.EffectAge Effect age} + */ + distribution?: ScalarValue +} + +/** + * Gives each particle a random initial direction offset within an elliptical cone. See {@link InitialDirection} for more information. + */ +class EllipticalParticleSpread extends DataAction { + declare type: ActionType.EllipticalParticleSpread + /** + * No so much unknown, just unnamed. If enabled, this limits the possible directions to only positive values on one axis, effectively cutting the cone of possible directions in half. + */ + unk_er_f1_0: boolean + /** + * The maximum change in direction in degrees, one of the angles of the elliptical cone. + * + * **Argument**: {@link PropertyArgument.EffectAge Effect age} + * + * See also: + * - {@link angleY} + */ + angleX: ScalarValue + /** + * The maximum change in direction in degrees, one of the angles of the elliptical cone. + * + * **Argument**: {@link PropertyArgument.EffectAge Effect age} + * + * See also: + * - {@link angleY} + */ + angleY: ScalarValue + /** + * Controls the distribution of the random directions that can be chosen. + * - At 0, all directions within the cone have an equal chance of being chosen. + * - At 1, the default direction is guaranteed to be chosen. + * - At -1, the maximum change in direction is guaranteed, meaning the chosen direction will always be a fixed number of degrees away from the default direction based on {@link angleX} and {@link angleY}. + * - Values between these values smoothly blend between them. + * - Values outside of the -1 to 1 range also work, but may do some unexpected things. + * + * **Argument**: {@link PropertyArgument.EffectAge Effect age} + */ + distribution: ScalarValue + constructor(props: EllipticalParticleSpreadParams = {}) { + super(ActionType.EllipticalParticleSpread) + this.assign(props) + } +} + +export interface RectangularParticleSpreadParams { + /** + * The maximum change in direction in degrees, one of the angles of the elliptical cone. + * + * **Default**: `30` + * + * **Argument**: {@link PropertyArgument.EffectAge Effect age} + * + * See also: + * - {@link angleY} + */ + angleX?: ScalarValue + /** + * The maximum change in direction in degrees, one of the angles of the elliptical cone. + * + * **Default**: `30` + * + * **Argument**: {@link PropertyArgument.EffectAge Effect age} + * + * See also: + * - {@link angleX} + */ + angleY?: ScalarValue + /** + * Controls the distribution of the random directions that can be chosen. + * - At 0, all directions within the cone have an equal chance of being chosen. + * - At 1, the default direction is guaranteed to be chosen. + * - At -1, the maximum change in direction is guaranteed, meaning the chosen direction will always be a fixed number of degrees away from the default direction based on {@link angleX} and {@link angleY}. + * - Values between these values smoothly blend between them. + * - Values outside of the -1 to 1 range also work, but may do some unexpected things. + * + * **Default**: `0` + * + * **Argument**: {@link PropertyArgument.EffectAge Effect age} + */ + distribution?: ScalarValue +} + +/** + * Gives each particle a random initial direction offset within a rectangular cone. See {@link InitialDirection} for more information. + */ +class RectangularParticleSpread extends DataAction { + declare type: ActionType.RectangularParticleSpread + /** + * The maximum change in direction in degrees, one of the angles of the elliptical cone. + * + * **Argument**: {@link PropertyArgument.EffectAge Effect age} + * + * See also: + * - {@link angleY} + */ + angleX: ScalarValue + /** + * The maximum change in direction in degrees, one of the angles of the elliptical cone. + * + * **Argument**: {@link PropertyArgument.EffectAge Effect age} + * + * See also: + * - {@link angleX} + */ + angleY: ScalarValue + /** + * Controls the distribution of the random directions that can be chosen. + * - At 0, all directions within the cone have an equal chance of being chosen. + * - At 1, the default direction is guaranteed to be chosen. + * - At -1, the maximum change in direction is guaranteed, meaning the chosen direction will always be a fixed number of degrees away from the default direction based on {@link angleX} and {@link angleY}. + * - Values between these values smoothly blend between them. + * - Values outside of the -1 to 1 range also work, but may do some unexpected things. + * + * **Argument**: {@link PropertyArgument.EffectAge Effect age} + */ + distribution: ScalarValue + constructor(props: RectangularParticleSpreadParams = {}) { + super(ActionType.RectangularParticleSpread) + this.assign(props) + } +} + +export interface PointSpriteParams { + /** + * Texture ID. + * + * **Default**: `1` + * + * **Argument**: {@link PropertyArgument.Constant0 Constant 0} + */ + texture?: ScalarValue + /** + * Blend mode. + * + * **Default**: {@link BlendMode.Normal} + * + * **Argument**: {@link PropertyArgument.Constant0 Constant 0} + */ + blendMode?: BlendMode | ScalarProperty + /** + * Particle size. + * + * **Default**: `1` + * + * **Argument**: {@link PropertyArgument.InstanceAge Instance age} + */ + size?: ScalarValue + /** + * Color multiplier. + * + * **Default**: `[1, 1, 1, 1]` + * + * **Argument**: {@link PropertyArgument.InstanceAge Instance age} + */ + color1?: Vector4Value + /** + * Color multiplier. + * + * **Default**: `[1, 1, 1, 1]` + * + * **Argument**: {@link PropertyArgument.EmissionTime Emission time} + */ + color2?: Vector4Value + /** + * Color multiplier. + * + * **Default**: `[1, 1, 1, 1]` + * + * **Argument**: {@link PropertyArgument.EffectAge Effect age} + */ + color3?: Vector4Value + /** + * Scalar multiplier for the color that does not affect the alpha. Effectively a brightness multiplier. + * + * **Default**: `1` + * + * **Argument**: {@link PropertyArgument.EffectAge Effect age} + */ + rgbMultiplier?: ScalarValue + /** + * Alpha multiplier. + * + * **Default**: `1` + * + * **Argument**: {@link PropertyArgument.EffectAge Effect age} + */ + alphaMultiplier?: ScalarValue + /** + * Controls the redness of the color of the additional bloom effect. The colors of the particle will be multiplied with this color to get the final color of the bloom effect. + * + * Note: + * - This has no effect if the "Effects Quality" setting is set to "Low". + * - This does not affect the natural bloom effect from high color values. + * + * **Default**: `1` + * + * See also: + * - {@link bloomGreen} + * - {@link bloomBlue} + * - {@link bloomStrength} + */ + bloomRed?: number + /** + * Controls the greenness of the color of the additional bloom effect. The colors of the particle will be multiplied with this color to get the final color of the bloom effect. + * + * Note: + * - This has no effect if the "Effects Quality" setting is set to "Low". + * - This does not affect the natural bloom effect from high color values. + * + * **Default**: `1` + * + * See also: + * - {@link bloomRed} + * - {@link bloomBlue} + * - {@link bloomStrength} + */ + bloomGreen?: number + /** + * Controls the blueness of the color of the additional bloom effect. The colors of the particle will be multiplied with this color to get the final color of the bloom effect. + * + * Note: + * - This has no effect if the "Effects Quality" setting is set to "Low". + * - This does not affect the natural bloom effect from high color values. + * + * **Default**: `1` + * + * See also: + * - {@link bloomRed} + * - {@link bloomGreen} + * - {@link bloomStrength} + */ + bloomBlue?: number + /** + * Controls the strength of the additional bloom effect. + * + * Note: + * - This has no effect if the "Effects Quality" setting is set to "Low". + * - This does not affect the natural bloom effect from high color values. + * + * **Default**: `0` + * + * See also: + * - {@link bloomRed} + * - {@link bloomGreen} + * - {@link bloomBlue} + */ + bloomStrength?: number + /** + * Minimum view distance. If the particle is closer than this distance from the camera, it will be hidden. Can be set to -1 to disable the limit. + * + * **Default**: `-1` + * + * See also: + * - {@link maxDistance} + */ + minDistance?: number + /** + * Maximum view distance. If the particle is farther away than this distance from the camera, it will be hidden. Can be set to -1 to disable the limit. + * + * **Default**: `-1` + * + * See also: + * - {@link minDistance} + */ + maxDistance?: number + /** + * Unknown integer. + * + * **Default**: `-2` + */ + unk_ds3_f1_2?: number + /** + * Unknown integer. + * + * **Default**: `-2` + */ + unk_ds3_f1_3?: number + /** + * Unknown integer. + * + * **Default**: `0` + */ + unk_ds3_f1_4?: number + /** + * Unknown integer. + * + * **Default**: `0` + */ + unk_ds3_f2_0?: number + /** + * Unknown integer. + * + * **Default**: `0` + */ + unk_ds3_f2_1?: number + /** + * Unknown integer. + * + * **Default**: `8` + */ + unk_ds3_f2_2?: number + /** + * Unknown integer. + * + * **Default**: `0` + */ + unk_ds3_f2_3?: number + /** + * Unknown integer. + * + * **Default**: `1` + */ + unk_ds3_f2_4?: number + /** + * Unknown integer. + * + * **Default**: `0` + */ + unk_ds3_f2_9?: number + /** + * Unknown integer. + * + * **Default**: `0` + */ + unk_ds3_f2_10?: number + /** + * Unknown integer. + * + * **Default**: `0` + */ + unk_ds3_f2_11?: number + /** + * Unknown integer. + * + * **Default**: `0` + */ + unk_ds3_f2_12?: number + /** + * Unknown integer. + * + * **Default**: `0` + */ + unk_ds3_f2_13?: number + /** + * Unknown float. + * + * **Default**: `-1` + */ + unkDistFadeClose0?: number + /** + * Unknown float. + * + * **Default**: `-1` + */ + unkDistFadeClose1?: number + /** + * Unknown float. + * + * **Default**: `-1` + */ + unkDistFadeFar0?: number + /** + * Unknown float. + * + * **Default**: `-1` + */ + unkDistFadeFar1?: number + /** + * Unknown integer. + * + * **Default**: `0` + */ + unk_ds3_f2_20?: number + /** + * Unknown integer. + * + * **Default**: `0` + */ + unk_ds3_f2_21?: number + /** + * Unknown integer. + * + * **Default**: `0` + */ + unk_ds3_f2_22?: number + /** + * Unknown integer. + * + * **Default**: `0` + */ + unk_ds3_f2_23?: number + /** + * Unknown integer. + * + * **Default**: `0` + */ + unk_ds3_f2_24?: number + /** + * Unknown float. + * + * **Default**: `1` + */ + unkDepthBlend1?: number + /** + * Unknown float. + * + * **Default**: `0` + */ + unkDepthBlend2?: number + /** + * Unknown integer. + * + * **Default**: `0` + */ + unk_ds3_f2_27?: number + /** + * Unknown integer. + * + * **Default**: `0` + */ + unk_ds3_f2_28?: number + /** + * Unknown integer. + * + * **Default**: `0` + */ + unk_ds3_f2_29?: number + /** + * Unknown. + * + * **Default**: `0` + */ + unk_ds3_p2_2?: ScalarValue + /** + * Unknown. + * + * **Default**: `[1, 1, 1, 1]` + */ + unk_ds3_p2_3?: Vector4Value + /** + * Unknown. + * + * **Default**: `[1, 1, 1, 1]` + */ + unk_ds3_p2_4?: Vector4Value + /** + * Unknown. + * + * **Default**: `[1, 1, 1, 1]` + */ + unk_ds3_p2_5?: Vector4Value + /** + * Unknown. + * + * **Default**: `0` + */ + unk_ds3_p2_6?: ScalarValue + /** + * Unknown integer. + * + * **Default**: `0` + */ + unk_sdt_f2_30?: number + /** + * Unknown integer. + * + * **Default**: `0` + */ + unk_sdt_f2_31?: number + /** + * Unknown integer. + * + * **Default**: `0` + */ + unk_sdt_f2_32?: number + /** + * Unknown integer. + * + * **Default**: `0` + */ + unk_sdt_f2_33?: number + /** + * Unknown float. + * + * **Default**: `0` + */ + unk_sdt_f2_34?: number + /** + * Unknown integer. + * + * **Default**: `-1` + */ + unk_sdt_f2_35?: number + /** + * Unknown integer. + * + * **Default**: `-2` + */ + unk_sdt_f2_36?: number + /** + * Unknown integer. + * + * **Default**: `0` + */ + unk_sdt_f2_37?: number + /** + * Unknown integer. + * + * **Default**: `0` + */ + unk_sdt_f2_38?: number + /** + * Unknown integer. + * + * **Default**: `1` + */ + unk_er_f1_3?: number + /** + * Unknown integer. + * + * **Default**: `1` + */ + unk_er_f1_4?: number + /** + * Unknown integer. + * + * **Default**: `0` + */ + unk_er_f2_39?: number +} + +/** + * Very basic point sprite particle. Similar to {@link ActionType.BillboardEx BillboardEx}, but far simpler. + */ +class PointSprite extends DataAction { + declare type: ActionType.PointSprite + /** + * Texture ID. + * + * **Argument**: {@link PropertyArgument.Constant0 Constant 0} + */ + texture: ScalarValue + /** + * Blend mode. + * + * **Argument**: {@link PropertyArgument.Constant0 Constant 0} + */ + blendMode: BlendMode | ScalarProperty + /** + * Particle size. + * + * **Argument**: {@link PropertyArgument.InstanceAge Instance age} + */ + size: ScalarValue + /** + * Color multiplier. + * + * **Argument**: {@link PropertyArgument.InstanceAge Instance age} + */ + color1: Vector4Value + /** + * Color multiplier. + * + * **Argument**: {@link PropertyArgument.EmissionTime Emission time} + */ + color2: Vector4Value + /** + * Color multiplier. + * + * **Argument**: {@link PropertyArgument.EffectAge Effect age} + */ + color3: Vector4Value + /** + * Scalar multiplier for the color that does not affect the alpha. Effectively a brightness multiplier. + * + * **Argument**: {@link PropertyArgument.EffectAge Effect age} + */ + rgbMultiplier: ScalarValue + /** + * Alpha multiplier. + * + * **Argument**: {@link PropertyArgument.EffectAge Effect age} + */ + alphaMultiplier: ScalarValue + /** + * Controls the redness of the color of the additional bloom effect. The colors of the particle will be multiplied with this color to get the final color of the bloom effect. + * + * Note: + * - This has no effect if the "Effects Quality" setting is set to "Low". + * - This does not affect the natural bloom effect from high color values. + * + * See also: + * - {@link bloomGreen} + * - {@link bloomBlue} + * - {@link bloomStrength} + */ + bloomRed: number + /** + * Controls the greenness of the color of the additional bloom effect. The colors of the particle will be multiplied with this color to get the final color of the bloom effect. + * + * Note: + * - This has no effect if the "Effects Quality" setting is set to "Low". + * - This does not affect the natural bloom effect from high color values. + * + * See also: + * - {@link bloomRed} + * - {@link bloomBlue} + * - {@link bloomStrength} + */ + bloomGreen: number + /** + * Controls the blueness of the color of the additional bloom effect. The colors of the particle will be multiplied with this color to get the final color of the bloom effect. + * + * Note: + * - This has no effect if the "Effects Quality" setting is set to "Low". + * - This does not affect the natural bloom effect from high color values. + * + * See also: + * - {@link bloomRed} + * - {@link bloomGreen} + * - {@link bloomStrength} + */ + bloomBlue: number + /** + * Controls the strength of the additional bloom effect. + * + * Note: + * - This has no effect if the "Effects Quality" setting is set to "Low". + * - This does not affect the natural bloom effect from high color values. + * + * See also: + * - {@link bloomRed} + * - {@link bloomGreen} + * - {@link bloomBlue} + */ + bloomStrength: number + /** + * Minimum view distance. If the particle is closer than this distance from the camera, it will be hidden. Can be set to -1 to disable the limit. + * + * See also: + * - {@link maxDistance} + */ + minDistance: number + /** + * Maximum view distance. If the particle is farther away than this distance from the camera, it will be hidden. Can be set to -1 to disable the limit. + * + * See also: + * - {@link minDistance} + */ + maxDistance: number + unk_ds3_f1_2: number + unk_ds3_f1_3: number + unk_ds3_f1_4: number + unk_ds3_f2_0: number + unk_ds3_f2_1: number + unk_ds3_f2_2: number + unk_ds3_f2_3: number + unk_ds3_f2_4: number + unk_ds3_f2_9: number + unk_ds3_f2_10: number + unk_ds3_f2_11: number + unk_ds3_f2_12: number + unk_ds3_f2_13: number + unkDistFadeClose0: number + unkDistFadeClose1: number + unkDistFadeFar0: number + unkDistFadeFar1: number + unk_ds3_f2_20: number + unk_ds3_f2_21: number + unk_ds3_f2_22: number + unk_ds3_f2_23: number + unk_ds3_f2_24: number + unkDepthBlend1: number + unkDepthBlend2: number + unk_ds3_f2_27: number + unk_ds3_f2_28: number + unk_ds3_f2_29: number + unk_ds3_p2_2: ScalarValue + unk_ds3_p2_3: Vector4Value + unk_ds3_p2_4: Vector4Value + unk_ds3_p2_5: Vector4Value + unk_ds3_p2_6: ScalarValue + unk_sdt_f2_30: number + unk_sdt_f2_31: number + unk_sdt_f2_32: number + unk_sdt_f2_33: number + unk_sdt_f2_34: number + unk_sdt_f2_35: number + unk_sdt_f2_36: number + unk_sdt_f2_37: number + unk_sdt_f2_38: number + unk_er_f1_3: number + unk_er_f1_4: number + unk_er_f2_39: number + constructor(props: PointSpriteParams = {}) { + super(ActionType.PointSprite) + this.assign(props) + } +} + +export interface LineParams { + /** + * Blend mode. + * + * **Default**: {@link BlendMode.Normal} + * + * **Argument**: {@link PropertyArgument.Constant0 Constant 0} + */ + blendMode?: BlendMode | ScalarProperty + /** + * The length of the line. + * + * **Default**: `1` + * + * **Argument**: {@link PropertyArgument.EmissionTime Emission time} + * + * See also: + * - {@link lengthMultiplier} + */ + length?: ScalarValue + /** + * Color multiplier. + * + * **Default**: `[1, 1, 1, 1]` + * + * **Argument**: {@link PropertyArgument.InstanceAge Instance age} + */ + color1?: Vector4Value + /** + * Color multiplier. + * + * **Default**: `[1, 1, 1, 1]` + * + * **Argument**: {@link PropertyArgument.InstanceAge Instance age} + */ + color2?: Vector4Value + /** + * The color for the start of the line. + * + * **Default**: `[1, 1, 1, 1]` + * + * **Argument**: {@link PropertyArgument.EffectAge Effect age} + */ + startColor?: Vector4Value + /** + * The color for the end of the line. + * + * **Default**: `[1, 1, 1, 1]` + * + * **Argument**: {@link PropertyArgument.EffectAge Effect age} + */ + endColor?: Vector4Value + /** + * Multiplier for the line {@link length}. + * + * **Default**: `1` + * + * **Argument**: {@link PropertyArgument.EffectAge Effect age} + */ + lengthMultiplier?: ScalarValue + /** + * Color multiplier. + * + * **Default**: `[1, 1, 1, 1]` + * + * **Argument**: {@link PropertyArgument.EffectAge Effect age} + */ + color3?: Vector4Value + /** + * Scalar multiplier for the color that does not affect the alpha. Effectively a brightness multiplier. + * + * **Default**: `1` + * + * **Argument**: {@link PropertyArgument.EffectAge Effect age} + */ + rgbMultiplier?: ScalarValue + /** + * Alpha multiplier. + * + * **Default**: `1` + * + * **Argument**: {@link PropertyArgument.EffectAge Effect age} + */ + alphaMultiplier?: ScalarValue + /** + * Controls the redness of the color of the additional bloom effect. The colors of the particle will be multiplied with this color to get the final color of the bloom effect. + * + * Note: + * - This has no effect if the "Effects Quality" setting is set to "Low". + * - This does not affect the natural bloom effect from high color values. + * + * **Default**: `1` + * + * See also: + * - {@link bloomGreen} + * - {@link bloomBlue} + * - {@link bloomStrength} + */ + bloomRed?: number + /** + * Controls the greenness of the color of the additional bloom effect. The colors of the particle will be multiplied with this color to get the final color of the bloom effect. + * + * Note: + * - This has no effect if the "Effects Quality" setting is set to "Low". + * - This does not affect the natural bloom effect from high color values. + * + * **Default**: `1` + * + * See also: + * - {@link bloomRed} + * - {@link bloomBlue} + * - {@link bloomStrength} + */ + bloomGreen?: number + /** + * Controls the blueness of the color of the additional bloom effect. The colors of the particle will be multiplied with this color to get the final color of the bloom effect. + * + * Note: + * - This has no effect if the "Effects Quality" setting is set to "Low". + * - This does not affect the natural bloom effect from high color values. + * + * **Default**: `1` + * + * See also: + * - {@link bloomRed} + * - {@link bloomGreen} + * - {@link bloomStrength} + */ + bloomBlue?: number + /** + * Controls the strength of the additional bloom effect. + * + * Note: + * - This has no effect if the "Effects Quality" setting is set to "Low". + * - This does not affect the natural bloom effect from high color values. + * + * **Default**: `0` + * + * See also: + * - {@link bloomRed} + * - {@link bloomGreen} + * - {@link bloomBlue} + */ + bloomStrength?: number + /** + * Minimum view distance. If the particle is closer than this distance from the camera, it will be hidden. Can be set to -1 to disable the limit. + * + * **Default**: `-1` + * + * See also: + * - {@link maxDistance} + */ + minDistance?: number + /** + * Maximum view distance. If the particle is farther away than this distance from the camera, it will be hidden. Can be set to -1 to disable the limit. + * + * **Default**: `-1` + * + * See also: + * - {@link minDistance} + */ + maxDistance?: number + /** + * Unknown integer. + * + * **Default**: `-1` + */ + unk_ds3_f1_1?: number + /** + * Unknown integer. + * + * **Default**: `0` + */ + unk_ds3_f2_0?: number + /** + * Unknown integer. + * + * **Default**: `0` + */ + unk_ds3_f2_1?: number + /** + * Unknown integer. + * + * **Default**: `8` + */ + unk_ds3_f2_2?: number + /** + * Unknown integer. + * + * **Default**: `0` + */ + unk_ds3_f2_3?: number + /** + * Unknown integer. + * + * **Default**: `1` + */ + unk_ds3_f2_4?: number + /** + * Unknown integer. + * + * **Default**: `0` + */ + unk_ds3_f2_9?: number + /** + * Unknown integer. + * + * **Default**: `0` + */ + unk_ds3_f2_10?: number + /** + * Unknown integer. + * + * **Default**: `0` + */ + unk_ds3_f2_11?: number + /** + * Unknown integer. + * + * **Default**: `0` + */ + unk_ds3_f2_12?: number + /** + * Unknown integer. + * + * **Default**: `0` + */ + unk_ds3_f2_13?: number + /** + * Unknown float. + * + * **Default**: `-1` + */ + unkDistFadeClose0?: number + /** + * Unknown float. + * + * **Default**: `-1` + */ + unkDistFadeClose1?: number + /** + * Unknown float. + * + * **Default**: `-1` + */ + unkDistFadeFar0?: number + /** + * Unknown float. + * + * **Default**: `-1` + */ + unkDistFadeFar1?: number + /** + * Unknown integer. + * + * **Default**: `0` + */ + unk_ds3_f2_20?: number + /** + * Unknown integer. + * + * **Default**: `0` + */ + unk_ds3_f2_21?: number + /** + * Unknown integer. + * + * **Default**: `0` + */ + unk_ds3_f2_22?: number + /** + * Unknown integer. + * + * **Default**: `0` + */ + unk_ds3_f2_23?: number + /** + * Unknown integer. + * + * **Default**: `0` + */ + unk_ds3_f2_24?: number + /** + * Unknown float. + * + * **Default**: `1` + */ + unkDepthBlend1?: number + /** + * Unknown float. + * + * **Default**: `0` + */ + unkDepthBlend2?: number + /** + * Unknown integer. + * + * **Default**: `0` + */ + unk_ds3_f2_27?: number + /** + * Unknown integer. + * + * **Default**: `0` + */ + unk_ds3_f2_28?: number + /** + * Unknown integer. + * + * **Default**: `0` + */ + unk_ds3_f2_29?: number + /** + * Unknown. + * + * **Default**: `0` + */ + unk_ds3_p2_2?: ScalarValue + /** + * Unknown. + * + * **Default**: `[1, 1, 1, 1]` + */ + unk_ds3_p2_3?: Vector4Value + /** + * Unknown. + * + * **Default**: `[1, 1, 1, 1]` + */ + unk_ds3_p2_4?: Vector4Value + /** + * Unknown. + * + * **Default**: `[1, 1, 1, 1]` + */ + unk_ds3_p2_5?: Vector4Value + /** + * Unknown. + * + * **Default**: `0` + */ + unk_ds3_p2_6?: ScalarValue + /** + * Unknown integer. + * + * **Default**: `0` + */ + unk_sdt_f2_30?: number + /** + * Unknown integer. + * + * **Default**: `0` + */ + unk_sdt_f2_31?: number + /** + * Unknown integer. + * + * **Default**: `0` + */ + unk_sdt_f2_32?: number + /** + * Unknown integer. + * + * **Default**: `0` + */ + unk_sdt_f2_33?: number + /** + * Unknown float. + * + * **Default**: `0` + */ + unk_sdt_f2_34?: number + /** + * Unknown integer. + * + * **Default**: `-2` + */ + unk_sdt_f2_35?: number + /** + * Unknown integer. + * + * **Default**: `-2` + */ + unk_sdt_f2_36?: number + /** + * Unknown integer. + * + * **Default**: `0` + */ + unk_sdt_f2_37?: number + /** + * Unknown integer. + * + * **Default**: `0` + */ + unk_sdt_f2_38?: number + /** + * Unknown integer. + * + * **Default**: `0` + */ + unk_sdt_f2_39?: number + /** + * Unknown integer. + * + * **Default**: `1` + */ + unk_er_f1_1?: number + /** + * Unknown integer. + * + * **Default**: `1` + */ + unk_er_f1_2?: number +} + +/** + * Simple line particle. It automatically rotates to match the direction it's moving. + */ +class Line extends DataAction { + declare type: ActionType.Line + /** + * Blend mode. + * + * **Argument**: {@link PropertyArgument.Constant0 Constant 0} + */ + blendMode: BlendMode | ScalarProperty + /** + * The length of the line. + * + * **Argument**: {@link PropertyArgument.EmissionTime Emission time} + * + * See also: + * - {@link lengthMultiplier} + */ + length: ScalarValue + /** + * Color multiplier. + * + * **Argument**: {@link PropertyArgument.InstanceAge Instance age} + */ + color1: Vector4Value + /** + * Color multiplier. + * + * **Argument**: {@link PropertyArgument.InstanceAge Instance age} + */ + color2: Vector4Value + /** + * The color for the start of the line. + * + * **Argument**: {@link PropertyArgument.EffectAge Effect age} + */ + startColor: Vector4Value + /** + * The color for the end of the line. + * + * **Argument**: {@link PropertyArgument.EffectAge Effect age} + */ + endColor: Vector4Value + /** + * Multiplier for the line {@link length}. + * + * **Argument**: {@link PropertyArgument.EffectAge Effect age} + */ + lengthMultiplier: ScalarValue + /** + * Color multiplier. + * + * **Argument**: {@link PropertyArgument.EffectAge Effect age} + */ + color3: Vector4Value + /** + * Scalar multiplier for the color that does not affect the alpha. Effectively a brightness multiplier. + * + * **Argument**: {@link PropertyArgument.EffectAge Effect age} + */ + rgbMultiplier: ScalarValue + /** + * Alpha multiplier. + * + * **Argument**: {@link PropertyArgument.EffectAge Effect age} + */ + alphaMultiplier: ScalarValue + /** + * Controls the redness of the color of the additional bloom effect. The colors of the particle will be multiplied with this color to get the final color of the bloom effect. + * + * Note: + * - This has no effect if the "Effects Quality" setting is set to "Low". + * - This does not affect the natural bloom effect from high color values. + * + * See also: + * - {@link bloomGreen} + * - {@link bloomBlue} + * - {@link bloomStrength} + */ + bloomRed: number + /** + * Controls the greenness of the color of the additional bloom effect. The colors of the particle will be multiplied with this color to get the final color of the bloom effect. + * + * Note: + * - This has no effect if the "Effects Quality" setting is set to "Low". + * - This does not affect the natural bloom effect from high color values. + * + * See also: + * - {@link bloomRed} + * - {@link bloomBlue} + * - {@link bloomStrength} + */ + bloomGreen: number + /** + * Controls the blueness of the color of the additional bloom effect. The colors of the particle will be multiplied with this color to get the final color of the bloom effect. + * + * Note: + * - This has no effect if the "Effects Quality" setting is set to "Low". + * - This does not affect the natural bloom effect from high color values. + * + * See also: + * - {@link bloomRed} + * - {@link bloomGreen} + * - {@link bloomStrength} + */ + bloomBlue: number + /** + * Controls the strength of the additional bloom effect. + * + * Note: + * - This has no effect if the "Effects Quality" setting is set to "Low". + * - This does not affect the natural bloom effect from high color values. + * + * See also: + * - {@link bloomRed} + * - {@link bloomGreen} + * - {@link bloomBlue} + */ + bloomStrength: number + /** + * Minimum view distance. If the particle is closer than this distance from the camera, it will be hidden. Can be set to -1 to disable the limit. + * + * See also: + * - {@link maxDistance} + */ + minDistance: number + /** + * Maximum view distance. If the particle is farther away than this distance from the camera, it will be hidden. Can be set to -1 to disable the limit. + * + * See also: + * - {@link minDistance} + */ + maxDistance: number + unk_ds3_f1_1: number + unk_ds3_f2_0: number + unk_ds3_f2_1: number + unk_ds3_f2_2: number + unk_ds3_f2_3: number + unk_ds3_f2_4: number + unk_ds3_f2_9: number + unk_ds3_f2_10: number + unk_ds3_f2_11: number + unk_ds3_f2_12: number + unk_ds3_f2_13: number + unkDistFadeClose0: number + unkDistFadeClose1: number + unkDistFadeFar0: number + unkDistFadeFar1: number + unk_ds3_f2_20: number + unk_ds3_f2_21: number + unk_ds3_f2_22: number + unk_ds3_f2_23: number + unk_ds3_f2_24: number + unkDepthBlend1: number + unkDepthBlend2: number + unk_ds3_f2_27: number + unk_ds3_f2_28: number + unk_ds3_f2_29: number + unk_ds3_p2_2: ScalarValue + unk_ds3_p2_3: Vector4Value + unk_ds3_p2_4: Vector4Value + unk_ds3_p2_5: Vector4Value + unk_ds3_p2_6: ScalarValue + unk_sdt_f2_30: number + unk_sdt_f2_31: number + unk_sdt_f2_32: number + unk_sdt_f2_33: number + unk_sdt_f2_34: number + unk_sdt_f2_35: number + unk_sdt_f2_36: number + unk_sdt_f2_37: number + unk_sdt_f2_38: number + unk_sdt_f2_39: number + unk_er_f1_1: number + unk_er_f1_2: number + constructor(props: LineParams = {}) { + super(ActionType.Line) + this.assign(props) + } +} + +export interface QuadLineParams { + /** + * Blend mode. + * + * **Default**: {@link BlendMode.Normal} + * + * **Argument**: {@link PropertyArgument.Constant0 Constant 0} + */ + blendMode?: BlendMode | ScalarProperty + /** + * The width of the line. + * + * **Default**: `1` + * + * **Argument**: {@link PropertyArgument.EmissionTime Emission time} + * + * See also: + * - {@link widthMultiplier} + */ + width?: ScalarValue + /** + * The length of the line. + * + * **Default**: `1` + * + * **Argument**: {@link PropertyArgument.EmissionTime Emission time} + * + * See also: + * - {@link lengthMultiplier} + */ + length?: ScalarValue + /** + * Color multiplier. + * + * **Default**: `[1, 1, 1, 1]` + * + * **Argument**: {@link PropertyArgument.InstanceAge Instance age} + */ + color1?: Vector4Value + /** + * Color multiplier. + * + * **Default**: `[1, 1, 1, 1]` + * + * **Argument**: {@link PropertyArgument.InstanceAge Instance age} + */ + color2?: Vector4Value + /** + * The color for the leading edge of the quad. + * + * **Default**: `[1, 1, 1, 1]` + * + * **Argument**: {@link PropertyArgument.EffectAge Effect age} + */ + startColor?: Vector4Value + /** + * The color for the trailing edge of the quad. + * + * **Default**: `[1, 1, 1, 1]` + * + * **Argument**: {@link PropertyArgument.EffectAge Effect age} + */ + endColor?: Vector4Value + /** + * Multiplier for the line {@link width}. + * + * **Default**: `1` + * + * **Argument**: {@link PropertyArgument.EffectAge Effect age} + */ + widthMultiplier?: ScalarValue + /** + * Multiplier for the line {@link length}. + * + * **Default**: `1` + * + * **Argument**: {@link PropertyArgument.EffectAge Effect age} + */ + lengthMultiplier?: ScalarValue + /** + * Color multiplier. + * + * **Default**: `[1, 1, 1, 1]` + * + * **Argument**: {@link PropertyArgument.EffectAge Effect age} + */ + color3?: Vector4Value + /** + * Scalar multiplier for the color that does not affect the alpha. Effectively a brightness multiplier. + * + * **Default**: `1` + * + * **Argument**: {@link PropertyArgument.EffectAge Effect age} + */ + rgbMultiplier?: ScalarValue + /** + * Alpha multiplier. + * + * **Default**: `1` + * + * **Argument**: {@link PropertyArgument.EffectAge Effect age} + */ + alphaMultiplier?: ScalarValue + /** + * Controls the redness of the color of the additional bloom effect. The colors of the particle will be multiplied with this color to get the final color of the bloom effect. + * + * Note: + * - This has no effect if the "Effects Quality" setting is set to "Low". + * - This does not affect the natural bloom effect from high color values. + * + * **Default**: `1` + * + * See also: + * - {@link bloomGreen} + * - {@link bloomBlue} + * - {@link bloomStrength} + */ + bloomRed?: number + /** + * Controls the greenness of the color of the additional bloom effect. The colors of the particle will be multiplied with this color to get the final color of the bloom effect. + * + * Note: + * - This has no effect if the "Effects Quality" setting is set to "Low". + * - This does not affect the natural bloom effect from high color values. + * + * **Default**: `1` + * + * See also: + * - {@link bloomRed} + * - {@link bloomBlue} + * - {@link bloomStrength} + */ + bloomGreen?: number + /** + * Controls the blueness of the color of the additional bloom effect. The colors of the particle will be multiplied with this color to get the final color of the bloom effect. + * + * Note: + * - This has no effect if the "Effects Quality" setting is set to "Low". + * - This does not affect the natural bloom effect from high color values. + * + * **Default**: `1` + * + * See also: + * - {@link bloomRed} + * - {@link bloomGreen} + * - {@link bloomStrength} + */ + bloomBlue?: number + /** + * Controls the strength of the additional bloom effect. + * + * Note: + * - This has no effect if the "Effects Quality" setting is set to "Low". + * - This does not affect the natural bloom effect from high color values. + * + * **Default**: `0` + * + * See also: + * - {@link bloomRed} + * - {@link bloomGreen} + * - {@link bloomBlue} + */ + bloomStrength?: number + /** + * Minimum view distance. If the particle is closer than this distance from the camera, it will be hidden. Can be set to -1 to disable the limit. + * + * **Default**: `-1` + * + * See also: + * - {@link maxDistance} + */ + minDistance?: number + /** + * Maximum view distance. If the particle is farther away than this distance from the camera, it will be hidden. Can be set to -1 to disable the limit. + * + * **Default**: `-1` + * + * See also: + * - {@link minDistance} + */ + maxDistance?: number + /** + * Unknown integer. + * + * **Default**: `-1` + */ + unk_ds3_f1_1?: number + /** + * Unknown integer. + * + * **Default**: `0` + */ + unk_ds3_f2_0?: number + /** + * Unknown integer. + * + * **Default**: `0` + */ + unk_ds3_f2_1?: number + /** + * Unknown integer. + * + * **Default**: `8` + */ + unk_ds3_f2_2?: number + /** + * Unknown integer. + * + * **Default**: `0` + */ + unk_ds3_f2_3?: number + /** + * Unknown integer. + * + * **Default**: `1` + */ + unk_ds3_f2_4?: number + /** + * Unknown integer. + * + * **Default**: `0` + */ + unk_ds3_f2_9?: number + /** + * Unknown integer. + * + * **Default**: `0` + */ + unk_ds3_f2_10?: number + /** + * Unknown integer. + * + * **Default**: `0` + */ + unk_ds3_f2_11?: number + /** + * Unknown integer. + * + * **Default**: `0` + */ + unk_ds3_f2_12?: number + /** + * Unknown integer. + * + * **Default**: `0` + */ + unk_ds3_f2_13?: number + /** + * Unknown float. + * + * **Default**: `-1` + */ + unkDistFadeClose0?: number + /** + * Unknown float. + * + * **Default**: `-1` + */ + unkDistFadeClose1?: number + /** + * Unknown float. + * + * **Default**: `-1` + */ + unkDistFadeFar0?: number + /** + * Unknown float. + * + * **Default**: `-1` + */ + unkDistFadeFar1?: number + /** + * Unknown integer. + * + * **Default**: `0` + */ + unk_ds3_f2_20?: number + /** + * Unknown integer. + * + * **Default**: `0` + */ + unk_ds3_f2_21?: number + /** + * Unknown integer. + * + * **Default**: `0` + */ + unk_ds3_f2_22?: number + /** + * Unknown integer. + * + * **Default**: `0` + */ + unk_ds3_f2_23?: number + /** + * Unknown integer. + * + * **Default**: `0` + */ + unk_ds3_f2_24?: number + /** + * Unknown float. + * + * **Default**: `1` + */ + unkDepthBlend1?: number + /** + * Unknown float. + * + * **Default**: `0` + */ + unkDepthBlend2?: number + /** + * Unknown integer. + * + * **Default**: `0` + */ + unk_ds3_f2_27?: number + /** + * Unknown integer. + * + * **Default**: `0` + */ + unk_ds3_f2_28?: number + /** + * Unknown integer. + * + * **Default**: `0` + */ + unk_ds3_f2_29?: number + /** + * Unknown. + * + * **Default**: `0` + */ + unk_ds3_p2_2?: ScalarValue + /** + * Unknown. + * + * **Default**: `[1, 1, 1, 1]` + */ + unk_ds3_p2_3?: Vector4Value + /** + * Unknown. + * + * **Default**: `[1, 1, 1, 1]` + */ + unk_ds3_p2_4?: Vector4Value + /** + * Unknown. + * + * **Default**: `[1, 1, 1, 1]` + */ + unk_ds3_p2_5?: Vector4Value + /** + * Unknown. + * + * **Default**: `0` + */ + unk_ds3_p2_6?: ScalarValue + /** + * Unknown integer. + * + * **Default**: `0` + */ + unk_sdt_f2_30?: number + /** + * Unknown integer. + * + * **Default**: `0` + */ + unk_sdt_f2_31?: number + /** + * Unknown integer. + * + * **Default**: `0` + */ + unk_sdt_f2_32?: number + /** + * Unknown integer. + * + * **Default**: `0` + */ + unk_sdt_f2_33?: number + /** + * Unknown float. + * + * **Default**: `0` + */ + unk_sdt_f2_34?: number + /** + * Unknown integer. + * + * **Default**: `-2` + */ + unk_sdt_f2_35?: number + /** + * Unknown integer. + * + * **Default**: `-2` + */ + unk_sdt_f2_36?: number + /** + * Unknown integer. + * + * **Default**: `0` + */ + unk_sdt_f2_37?: number + /** + * Unknown integer. + * + * **Default**: `0` + */ + unk_sdt_f2_38?: number + /** + * Unknown integer. + * + * **Default**: `0` + */ + unk_sdt_f2_39?: number + /** + * Unknown integer. + * + * **Default**: `1` + */ + unk_er_f1_1?: number + /** + * Unknown integer. + * + * **Default**: `1` + */ + unk_er_f1_2?: number +} + +/** + * Simple rectangular particle, very similar to {@link ActionType.Line Line particles}, but has properties that control the width as well as the length. It automatically rotates to match the direction it's moving. + */ +class QuadLine extends DataAction { + declare type: ActionType.QuadLine + /** + * Blend mode. + * + * **Argument**: {@link PropertyArgument.Constant0 Constant 0} + */ + blendMode: BlendMode | ScalarProperty + /** + * The width of the line. + * + * **Argument**: {@link PropertyArgument.EmissionTime Emission time} + * + * See also: + * - {@link widthMultiplier} + */ + width: ScalarValue + /** + * The length of the line. + * + * **Argument**: {@link PropertyArgument.EmissionTime Emission time} + * + * See also: + * - {@link lengthMultiplier} + */ + length: ScalarValue + /** + * Color multiplier. + * + * **Argument**: {@link PropertyArgument.InstanceAge Instance age} + */ + color1: Vector4Value + /** + * Color multiplier. + * + * **Argument**: {@link PropertyArgument.InstanceAge Instance age} + */ + color2: Vector4Value + /** + * The color for the leading edge of the quad. + * + * **Argument**: {@link PropertyArgument.EffectAge Effect age} + */ + startColor: Vector4Value + /** + * The color for the trailing edge of the quad. + * + * **Argument**: {@link PropertyArgument.EffectAge Effect age} + */ + endColor: Vector4Value + /** + * Multiplier for the line {@link width}. + * + * **Argument**: {@link PropertyArgument.EffectAge Effect age} + */ + widthMultiplier: ScalarValue + /** + * Multiplier for the line {@link length}. + * + * **Argument**: {@link PropertyArgument.EffectAge Effect age} + */ + lengthMultiplier: ScalarValue + /** + * Color multiplier. + * + * **Argument**: {@link PropertyArgument.EffectAge Effect age} + */ + color3: Vector4Value + /** + * Scalar multiplier for the color that does not affect the alpha. Effectively a brightness multiplier. + * + * **Argument**: {@link PropertyArgument.EffectAge Effect age} + */ + rgbMultiplier: ScalarValue + /** + * Alpha multiplier. + * + * **Argument**: {@link PropertyArgument.EffectAge Effect age} + */ + alphaMultiplier: ScalarValue + /** + * Controls the redness of the color of the additional bloom effect. The colors of the particle will be multiplied with this color to get the final color of the bloom effect. + * + * Note: + * - This has no effect if the "Effects Quality" setting is set to "Low". + * - This does not affect the natural bloom effect from high color values. + * + * See also: + * - {@link bloomGreen} + * - {@link bloomBlue} + * - {@link bloomStrength} + */ + bloomRed: number + /** + * Controls the greenness of the color of the additional bloom effect. The colors of the particle will be multiplied with this color to get the final color of the bloom effect. + * + * Note: + * - This has no effect if the "Effects Quality" setting is set to "Low". + * - This does not affect the natural bloom effect from high color values. + * + * See also: + * - {@link bloomRed} + * - {@link bloomBlue} + * - {@link bloomStrength} + */ + bloomGreen: number + /** + * Controls the blueness of the color of the additional bloom effect. The colors of the particle will be multiplied with this color to get the final color of the bloom effect. + * + * Note: + * - This has no effect if the "Effects Quality" setting is set to "Low". + * - This does not affect the natural bloom effect from high color values. + * + * See also: + * - {@link bloomRed} + * - {@link bloomGreen} + * - {@link bloomStrength} + */ + bloomBlue: number + /** + * Controls the strength of the additional bloom effect. + * + * Note: + * - This has no effect if the "Effects Quality" setting is set to "Low". + * - This does not affect the natural bloom effect from high color values. + * + * See also: + * - {@link bloomRed} + * - {@link bloomGreen} + * - {@link bloomBlue} + */ + bloomStrength: number + /** + * Minimum view distance. If the particle is closer than this distance from the camera, it will be hidden. Can be set to -1 to disable the limit. + * + * See also: + * - {@link maxDistance} + */ + minDistance: number + /** + * Maximum view distance. If the particle is farther away than this distance from the camera, it will be hidden. Can be set to -1 to disable the limit. + * + * See also: + * - {@link minDistance} + */ + maxDistance: number + unk_ds3_f1_1: number + unk_ds3_f2_0: number + unk_ds3_f2_1: number + unk_ds3_f2_2: number + unk_ds3_f2_3: number + unk_ds3_f2_4: number + unk_ds3_f2_9: number + unk_ds3_f2_10: number + unk_ds3_f2_11: number + unk_ds3_f2_12: number + unk_ds3_f2_13: number + unkDistFadeClose0: number + unkDistFadeClose1: number + unkDistFadeFar0: number + unkDistFadeFar1: number + unk_ds3_f2_20: number + unk_ds3_f2_21: number + unk_ds3_f2_22: number + unk_ds3_f2_23: number + unk_ds3_f2_24: number + unkDepthBlend1: number + unkDepthBlend2: number + unk_ds3_f2_27: number + unk_ds3_f2_28: number + unk_ds3_f2_29: number + unk_ds3_p2_2: ScalarValue + unk_ds3_p2_3: Vector4Value + unk_ds3_p2_4: Vector4Value + unk_ds3_p2_5: Vector4Value + unk_ds3_p2_6: ScalarValue + unk_sdt_f2_30: number + unk_sdt_f2_31: number + unk_sdt_f2_32: number + unk_sdt_f2_33: number + unk_sdt_f2_34: number + unk_sdt_f2_35: number + unk_sdt_f2_36: number + unk_sdt_f2_37: number + unk_sdt_f2_38: number + unk_sdt_f2_39: number + unk_er_f1_1: number + unk_er_f1_2: number + constructor(props: QuadLineParams = {}) { + super(ActionType.QuadLine) + this.assign(props) + } +} + +export interface BillboardExParams { + /** + * Texture ID. + * + * **Default**: `1` + * + * **Argument**: {@link PropertyArgument.Constant0 Constant 0} + */ + texture?: ScalarValue + /** + * Blend mode. + * + * **Default**: {@link BlendMode.Normal} + * + * **Argument**: {@link PropertyArgument.Constant0 Constant 0} + */ + blendMode?: BlendMode | ScalarProperty + /** + * X position offset. + * + * **Default**: `0` + * + * **Argument**: {@link PropertyArgument.InstanceAge Instance age} + */ + offsetX?: ScalarValue + /** + * Y position offset. + * + * **Default**: `0` + * + * **Argument**: {@link PropertyArgument.InstanceAge Instance age} + */ + offsetY?: ScalarValue + /** + * Z position offset. + * + * **Default**: `0` + * + * **Argument**: {@link PropertyArgument.InstanceAge Instance age} + */ + offsetZ?: ScalarValue + /** + * The width of the particle. + * + * If {@link uniformScale} is enabled, this also controls the height. + * + * **Default**: `1` + * + * **Argument**: {@link PropertyArgument.InstanceAge Instance age} + * + * See also: + * - {@link scaleVariationX} + */ + width?: ScalarValue + /** + * The height of the particle. + * + * If {@link uniformScale} is enabled, {@link width} also controls the height, and this property is ignored. + * + * **Default**: `1` + * + * **Argument**: {@link PropertyArgument.InstanceAge Instance age} + * + * See also: + * - {@link scaleVariationY} + */ + height?: ScalarValue + /** + * Color multiplier. + * + * **Default**: `[1, 1, 1, 1]` + * + * **Argument**: {@link PropertyArgument.InstanceAge Instance age} + */ + color1?: Vector4Value + /** + * Color multiplier. + * + * **Default**: `[1, 1, 1, 1]` + * + * **Argument**: {@link PropertyArgument.EmissionTime Emission time} + */ + color2?: Vector4Value + /** + * Color multiplier. + * + * **Default**: `[1, 1, 1, 1]` + * + * **Argument**: {@link PropertyArgument.InstanceAge Instance age} + */ + color3?: Vector4Value + /** + * Parts of the particle with less opacity than this threshold will be invisible. The range is 0-255. + * + * **Default**: `0` + * + * **Argument**: {@link PropertyArgument.InstanceAge Instance age} + */ + alphaThreshold?: ScalarValue + /** + * Rotation around the X-axis in degrees. + * + * **Default**: `0` + * + * **Argument**: {@link PropertyArgument.Constant0 Constant 0} + * + * See also: + * - {@link rotationSpeedX} + * - {@link rotationSpeedMultiplierX} + */ + rotationX?: ScalarValue + /** + * Rotation around the Y-axis in degrees. + * + * **Default**: `0` + * + * **Argument**: {@link PropertyArgument.Constant0 Constant 0} + * + * See also: + * - {@link rotationSpeedY} + * - {@link rotationSpeedMultiplierY} + */ + rotationY?: ScalarValue + /** + * Rotation around the Z-axis in degrees. + * + * **Default**: `0` + * + * **Argument**: {@link PropertyArgument.Constant0 Constant 0} + * + * See also: + * - {@link rotationSpeedZ} + * - {@link rotationSpeedMultiplierZ} + */ + rotationZ?: ScalarValue + /** + * Rotation speed around the X-axis in degrees per second. + * + * **Default**: `0` + * + * **Argument**: {@link PropertyArgument.InstanceAge Instance age} + * + * See also: + * - {@link rotationX} + * - {@link rotationSpeedMultiplierX} + */ + rotationSpeedX?: ScalarValue + /** + * Rotation speed around the Y-axis in degrees per second. + * + * **Default**: `0` + * + * **Argument**: {@link PropertyArgument.InstanceAge Instance age} + * + * See also: + * - {@link rotationY} + * - {@link rotationSpeedMultiplierY} + */ + rotationSpeedY?: ScalarValue + /** + * Rotation speed around the Z-axis in degrees per second. + * + * **Default**: `0` + * + * **Argument**: {@link PropertyArgument.InstanceAge Instance age} + * + * See also: + * - {@link rotationZ} + * - {@link rotationSpeedMultiplierZ} + */ + rotationSpeedZ?: ScalarValue + /** + * Multiplier for {@link rotationSpeedX}. + * + * **Default**: `1` + * + * **Argument**: {@link PropertyArgument.InstanceAge Instance age} + * + * See also: + * - {@link rotationX} + */ + rotationSpeedMultiplierX?: ScalarValue + /** + * Multiplier for {@link rotationSpeedY}. + * + * **Default**: `1` + * + * **Argument**: {@link PropertyArgument.InstanceAge Instance age} + * + * See also: + * - {@link rotationY} + */ + rotationSpeedMultiplierY?: ScalarValue + /** + * Multiplier for {@link rotationSpeedZ}. + * + * **Default**: `1` + * + * **Argument**: {@link PropertyArgument.InstanceAge Instance age} + * + * See also: + * - {@link rotationZ} + */ + rotationSpeedMultiplierZ?: ScalarValue + /** + * Positive values will make the particle draw in front of objects closer to the camera, while negative values will make it draw behind objects farther away from the camera. + * + * **Default**: `0` + * + * **Argument**: {@link PropertyArgument.InstanceAge Instance age} + */ + depthOffset?: ScalarValue + /** + * The index of the frame to show from the texture atlas. Can be animated using a {@link PropertyFunction.Linear linear property} or similar. + * + * Seemingly identical to {@link frameIndexOffset}? The sum of these two properties is the actual frame index that gets used. + * + * **Default**: `0` + * + * **Argument**: {@link PropertyArgument.InstanceAge Instance age} + */ + frameIndex?: ScalarValue + /** + * Seemingly identical to {@link frameIndex}? The sum of these two properties is the actual frame index that gets used. + * + * **Default**: `0` + * + * **Argument**: {@link PropertyArgument.InstanceAge Instance age} + */ + frameIndexOffset?: ScalarValue + /** + * Scalar multiplier for the color that does not affect the alpha. Effectively a brightness multiplier. + * + * **Default**: `1` + * + * **Argument**: {@link PropertyArgument.EffectAge Effect age} + */ + rgbMultiplier?: ScalarValue + /** + * Alpha multiplier. + * + * **Default**: `1` + * + * **Argument**: {@link PropertyArgument.EffectAge Effect age} + */ + alphaMultiplier?: ScalarValue + /** + * Controls the orientation mode for the particles. See {@link OrientationMode} for more information. + * + * **Default**: {@link OrientationMode.CameraPlane} + */ + orientation?: OrientationMode + /** + * Normal map texture ID. + * + * **Default**: `0` + */ + normalMap?: number + /** + * Each particle will pick a random number between this value and 1, and the width of the particle will be multiplied by this number. For example, setting this to 0.5 will make the particles randomly thinner, down to half width. Setting it to 2 will make them randomly wider, up to double width. + * + * If {@link uniformScale} is enabled, this also affects the height. + * + * **Default**: `1` + * + * See also: + * - {@link scaleVariationY} + */ + scaleVariationX?: number + /** + * Each particle will pick a random number between this value and 1, and the height of the particle will be multiplied by this number. For example, setting this to 0.5 will make the particles randomly shorter, down to half height. Setting it to 2 will make them randomly taller, up to double height. + * + * If {@link uniformScale} is enabled, {@link scaleVariationX} also affects the height, and this field is ignored. + * + * **Default**: `1` + * + * See also: + * - {@link scaleVariationX} + */ + scaleVariationY?: number + /** + * If enabled, the particle width-related properties and fields will control both the width and height of the particles, and the height counterparts will be ignored. + * + * **Default**: `false` + * + * See also: + * - {@link width} + * - {@link height} + * - {@link scaleVariationX} + * - {@link scaleVariationY} + */ + uniformScale?: boolean + /** + * To split the texture into multiple animation frames, this value must be set to the number of columns in the texture. It should equal `textureWidth / frameWidth`. + * + * **Default**: `1` + * + * See also: + * - {@link totalFrames} + */ + columns?: number + /** + * To split the texture into multiple animation frames, this value must be set to the total number of frames in the texture. + * + * **Default**: `1` + * + * See also: + * - {@link columns} + */ + totalFrames?: number + /** + * If enabled, the texture animation will use linear interpolation to mix frames when the frame index is not a whole number. For example, if the frame index is 0.5, enabling this will cause the average of the first two frames to be shown instead of just the first frame. + * + * If disabled, the frame index will be truncated to get a whole number. + * + * **Default**: `true` + * + * See also: + * - {@link frameIndex} + * - {@link frameIndexOffset} + */ + interpolateFrames?: boolean + /** + * Controls the redness of the color of the additional bloom effect. The colors of the particle will be multiplied with this color to get the final color of the bloom effect. + * + * Note: + * - This has no effect if the "Effects Quality" setting is set to "Low". + * - This does not affect the natural bloom effect from high color values. + * + * **Default**: `1` + * + * See also: + * - {@link bloomGreen} + * - {@link bloomBlue} + * - {@link bloomStrength} + */ + bloomRed?: number + /** + * Controls the greenness of the color of the additional bloom effect. The colors of the particle will be multiplied with this color to get the final color of the bloom effect. + * + * Note: + * - This has no effect if the "Effects Quality" setting is set to "Low". + * - This does not affect the natural bloom effect from high color values. + * + * **Default**: `1` + * + * See also: + * - {@link bloomRed} + * - {@link bloomBlue} + * - {@link bloomStrength} + */ + bloomGreen?: number + /** + * Controls the blueness of the color of the additional bloom effect. The colors of the particle will be multiplied with this color to get the final color of the bloom effect. + * + * Note: + * - This has no effect if the "Effects Quality" setting is set to "Low". + * - This does not affect the natural bloom effect from high color values. + * + * **Default**: `1` + * + * See also: + * - {@link bloomRed} + * - {@link bloomGreen} + * - {@link bloomStrength} + */ + bloomBlue?: number + /** + * Controls the strength of the additional bloom effect. + * + * Note: + * - This has no effect if the "Effects Quality" setting is set to "Low". + * - This does not affect the natural bloom effect from high color values. + * + * **Default**: `0` + * + * See also: + * - {@link bloomRed} + * - {@link bloomGreen} + * - {@link bloomBlue} + */ + bloomStrength?: number + /** + * Minimum view distance. If the particle is closer than this distance from the camera, it will be hidden. Can be set to -1 to disable the limit. + * + * **Default**: `-1` + * + * See also: + * - {@link maxDistance} + */ + minDistance?: number + /** + * Maximum view distance. If the particle is farther away than this distance from the camera, it will be hidden. Can be set to -1 to disable the limit. + * + * **Default**: `-1` + * + * See also: + * - {@link minDistance} + */ + maxDistance?: number + /** + * Controls how dark shaded parts of the particle are. + * + * **Default**: `0` + */ + shadowDarkness?: number + /** + * Specular texture ID. + * + * **Default**: `0` + * + * See also: + * - {@link lighting} + * - {@link glossiness} + * - {@link specularity} + */ + specular?: number + /** + * Controls how sharp the specular highlights are. + * + * **Default**: `0.25` + * + * See also: + * - {@link lighting} + * - {@link specular} + * - {@link specularity} + */ + glossiness?: number + /** + * Controls how the particles are lit. See {@link LightingMode} for more information. + * + * **Default**: {@link LightingMode.Unlit} + */ + lighting?: LightingMode + /** + * Controls how bright the specular highlights are. + * + * **Default**: `0.5` + * + * See also: + * - {@link lighting} + * - {@link specular} + * - {@link glossiness} + */ + specularity?: number + /** + * Unknown integer. + * + * **Default**: `0` + */ + unk_ds3_f1_7?: number + /** + * Unknown integer. + * + * **Default**: `0` + */ + unk_ds3_f1_11?: number + /** + * Unknown integer. + * + * **Default**: `0` + */ + unk_ds3_f1_12?: number + /** + * Unknown float. + * + * **Default**: `-1` + */ + unk_ds3_f1_13?: number + /** + * Unknown integer. + * + * **Default**: `1` + */ + unk_ds3_f1_14?: number + /** + * Unknown integer. + * + * **Default**: `0` + */ + unk_ds3_f1_15?: number + /** + * Unknown integer. + * + * **Default**: `0` + */ + unk_ds3_f1_16?: number + /** + * Unknown integer. + * + * **Default**: `0` + */ + unk_ds3_f2_0?: number + /** + * Unknown boolean. + * + * **Default**: `0` + */ + unk_ds3_f2_1?: boolean + /** + * Unknown integer. + * + * **Default**: `8` + */ + unk_ds3_f2_2?: number + /** + * Unknown integer. + * + * **Default**: `0` + */ + unk_ds3_f2_3?: number + /** + * Unknown integer. + * + * **Default**: `1` + */ + unk_ds3_f2_4?: number + /** + * Unknown integer. + * + * **Default**: `0` + */ + unk_ds3_f2_9?: number + /** + * Unknown integer. + * + * **Default**: `0` + */ + unk_ds3_f2_10?: number + /** + * Unknown integer. + * + * **Default**: `0` + */ + unk_ds3_f2_11?: number + /** + * Unknown integer. + * + * **Default**: `0` + */ + unk_ds3_f2_12?: number + /** + * Unknown integer. + * + * **Default**: `0` + */ + unk_ds3_f2_13?: number + /** + * Unknown float. + * + * **Default**: `-1` + */ + unkDistFadeClose0?: number + /** + * Unknown float. + * + * **Default**: `-1` + */ + unkDistFadeClose1?: number + /** + * Unknown float. + * + * **Default**: `-1` + */ + unkDistFadeFar0?: number + /** + * Unknown float. + * + * **Default**: `-1` + */ + unkDistFadeFar1?: number + /** + * Unknown integer. + * + * **Default**: `0` + */ + unk_ds3_f2_20?: number + /** + * Unknown integer. + * + * **Default**: `0` + */ + unk_ds3_f2_21?: number + /** + * Unknown integer. + * + * **Default**: `0` + */ + unk_ds3_f2_22?: number + /** + * Unknown integer. + * + * **Default**: `0` + */ + unk_ds3_f2_23?: number + /** + * Unknown integer. + * + * **Default**: `0` + */ + unk_ds3_f2_24?: number + /** + * Unknown float. + * + * **Default**: `1` + */ + unkDepthBlend1?: number + /** + * Unknown float. + * + * **Default**: `0` + */ + unkDepthBlend2?: number + /** + * Unknown integer. + * + * **Default**: `1` + */ + unk_ds3_f2_27?: number + /** + * Unknown integer. + * + * **Default**: `0` + */ + unk_ds3_f2_28?: number + /** + * Unknown float. + * + * **Default**: `5` + */ + unk_ds3_f2_29?: number + /** + * Unknown. + * + * **Default**: `0` + */ + unk_ds3_p1_21?: ScalarValue + /** + * Unknown. + * + * **Default**: `0` + */ + unk_ds3_p1_22?: ScalarValue + /** + * Unknown. + * + * **Default**: `0` + */ + unk_ds3_p2_2?: ScalarValue + /** + * Unknown. + * + * **Default**: `[1, 1, 1, 1]` + */ + unk_ds3_p2_3?: Vector4Value + /** + * Unknown. + * + * **Default**: `[1, 1, 1, 1]` + */ + unk_ds3_p2_4?: Vector4Value + /** + * Unknown. + * + * **Default**: `[1, 1, 1, 1]` + */ + unk_ds3_p2_5?: Vector4Value + /** + * Unknown. + * + * **Default**: `0` + */ + unk_ds3_p2_6?: ScalarValue + /** + * Unknown integer. + * + * **Default**: `1` + */ + unk_sdt_f1_15?: number + /** + * Unknown integer. + * + * **Default**: `1` + */ + unk_sdt_f1_16?: number + /** + * Unknown integer. + * + * **Default**: `0` + */ + unk_sdt_f1_17?: number + /** + * Unknown integer. + * + * **Default**: `0` + */ + unk_sdt_f2_31?: number + /** + * Unknown integer. + * + * **Default**: `1` + */ + unk_sdt_f2_32?: number + /** + * Unknown integer. + * + * **Default**: `-2` + */ + unk_sdt_f2_36?: number + /** + * Unknown integer. + * + * **Default**: `0` + */ + unk_sdt_f2_37?: number + /** + * Unknown integer. + * + * **Default**: `1` + */ + unk_sdt_f2_39?: number + /** + * Unknown integer. + * + * **Default**: `0` + */ + unk_sdt_f2_40?: number + /** + * Unknown integer. + * + * **Default**: `0` + */ + unk_sdt_f2_41?: number + /** + * Unknown integer. + * + * **Default**: `0` + */ + unk_sdt_f2_42?: number + /** + * Unknown integer. + * + * **Default**: `0` + */ + unk_sdt_f2_43?: number + /** + * Unknown integer. + * + * **Default**: `0` + */ + unk_sdt_f2_44?: number +} + +/** + * Particle with a texture that may be animated. This is the most common particle type and it has a lot of useful fields and properties. + */ +class BillboardEx extends DataAction { + declare type: ActionType.BillboardEx + /** + * Texture ID. + * + * **Argument**: {@link PropertyArgument.Constant0 Constant 0} + */ + texture: ScalarValue + /** + * Blend mode. + * + * **Argument**: {@link PropertyArgument.Constant0 Constant 0} + */ + blendMode: BlendMode | ScalarProperty + /** + * X position offset. + * + * **Argument**: {@link PropertyArgument.InstanceAge Instance age} + */ + offsetX: ScalarValue + /** + * Y position offset. + * + * **Argument**: {@link PropertyArgument.InstanceAge Instance age} + */ + offsetY: ScalarValue + /** + * Z position offset. + * + * **Argument**: {@link PropertyArgument.InstanceAge Instance age} + */ + offsetZ: ScalarValue + /** + * The width of the particle. + * + * If {@link uniformScale} is enabled, this also controls the height. + * + * **Argument**: {@link PropertyArgument.InstanceAge Instance age} + * + * See also: + * - {@link scaleVariationX} + */ + width: ScalarValue + /** + * The height of the particle. + * + * If {@link uniformScale} is enabled, {@link width} also controls the height, and this property is ignored. + * + * **Argument**: {@link PropertyArgument.InstanceAge Instance age} + * + * See also: + * - {@link scaleVariationY} + */ + height: ScalarValue + /** + * Color multiplier. + * + * **Argument**: {@link PropertyArgument.InstanceAge Instance age} + */ + color1: Vector4Value + /** + * Color multiplier. + * + * **Argument**: {@link PropertyArgument.EmissionTime Emission time} + */ + color2: Vector4Value + /** + * Color multiplier. + * + * **Argument**: {@link PropertyArgument.InstanceAge Instance age} + */ + color3: Vector4Value + /** + * Parts of the particle with less opacity than this threshold will be invisible. The range is 0-255. + * + * **Argument**: {@link PropertyArgument.InstanceAge Instance age} + */ + alphaThreshold: ScalarValue + /** + * Rotation around the X-axis in degrees. + * + * **Argument**: {@link PropertyArgument.Constant0 Constant 0} + * + * See also: + * - {@link rotationSpeedX} + * - {@link rotationSpeedMultiplierX} + */ + rotationX: ScalarValue + /** + * Rotation around the Y-axis in degrees. + * + * **Argument**: {@link PropertyArgument.Constant0 Constant 0} + * + * See also: + * - {@link rotationSpeedY} + * - {@link rotationSpeedMultiplierY} + */ + rotationY: ScalarValue + /** + * Rotation around the Z-axis in degrees. + * + * **Argument**: {@link PropertyArgument.Constant0 Constant 0} + * + * See also: + * - {@link rotationSpeedZ} + * - {@link rotationSpeedMultiplierZ} + */ + rotationZ: ScalarValue + /** + * Rotation speed around the X-axis in degrees per second. + * + * **Argument**: {@link PropertyArgument.InstanceAge Instance age} + * + * See also: + * - {@link rotationX} + * - {@link rotationSpeedMultiplierX} + */ + rotationSpeedX: ScalarValue + /** + * Rotation speed around the Y-axis in degrees per second. + * + * **Argument**: {@link PropertyArgument.InstanceAge Instance age} + * + * See also: + * - {@link rotationY} + * - {@link rotationSpeedMultiplierY} + */ + rotationSpeedY: ScalarValue + /** + * Rotation speed around the Z-axis in degrees per second. + * + * **Argument**: {@link PropertyArgument.InstanceAge Instance age} + * + * See also: + * - {@link rotationZ} + * - {@link rotationSpeedMultiplierZ} + */ + rotationSpeedZ: ScalarValue + /** + * Multiplier for {@link rotationSpeedX}. + * + * **Argument**: {@link PropertyArgument.InstanceAge Instance age} + * + * See also: + * - {@link rotationX} + */ + rotationSpeedMultiplierX: ScalarValue + /** + * Multiplier for {@link rotationSpeedY}. + * + * **Argument**: {@link PropertyArgument.InstanceAge Instance age} + * + * See also: + * - {@link rotationY} + */ + rotationSpeedMultiplierY: ScalarValue + /** + * Multiplier for {@link rotationSpeedZ}. + * + * **Argument**: {@link PropertyArgument.InstanceAge Instance age} + * + * See also: + * - {@link rotationZ} + */ + rotationSpeedMultiplierZ: ScalarValue + /** + * Positive values will make the particle draw in front of objects closer to the camera, while negative values will make it draw behind objects farther away from the camera. + * + * **Argument**: {@link PropertyArgument.InstanceAge Instance age} + */ + depthOffset: ScalarValue + /** + * The index of the frame to show from the texture atlas. Can be animated using a {@link PropertyFunction.Linear linear property} or similar. + * + * Seemingly identical to {@link frameIndexOffset}? The sum of these two properties is the actual frame index that gets used. + * + * **Argument**: {@link PropertyArgument.InstanceAge Instance age} + */ + frameIndex: ScalarValue + /** + * Seemingly identical to {@link frameIndex}? The sum of these two properties is the actual frame index that gets used. + * + * **Argument**: {@link PropertyArgument.InstanceAge Instance age} + */ + frameIndexOffset: ScalarValue + /** + * Scalar multiplier for the color that does not affect the alpha. Effectively a brightness multiplier. + * + * **Argument**: {@link PropertyArgument.EffectAge Effect age} + */ + rgbMultiplier: ScalarValue + /** + * Alpha multiplier. + * + * **Argument**: {@link PropertyArgument.EffectAge Effect age} + */ + alphaMultiplier: ScalarValue + /** + * Controls the orientation mode for the particles. See {@link OrientationMode} for more information. + */ + orientation: OrientationMode + /** + * Normal map texture ID. + */ + normalMap: number + /** + * Each particle will pick a random number between this value and 1, and the width of the particle will be multiplied by this number. For example, setting this to 0.5 will make the particles randomly thinner, down to half width. Setting it to 2 will make them randomly wider, up to double width. + * + * If {@link uniformScale} is enabled, this also affects the height. + * + * See also: + * - {@link scaleVariationY} + */ + scaleVariationX: number + /** + * Each particle will pick a random number between this value and 1, and the height of the particle will be multiplied by this number. For example, setting this to 0.5 will make the particles randomly shorter, down to half height. Setting it to 2 will make them randomly taller, up to double height. + * + * If {@link uniformScale} is enabled, {@link scaleVariationX} also affects the height, and this field is ignored. + * + * See also: + * - {@link scaleVariationX} + */ + scaleVariationY: number + /** + * If enabled, the particle width-related properties and fields will control both the width and height of the particles, and the height counterparts will be ignored. + * + * See also: + * - {@link width} + * - {@link height} + * - {@link scaleVariationX} + * - {@link scaleVariationY} + */ + uniformScale: boolean + /** + * To split the texture into multiple animation frames, this value must be set to the number of columns in the texture. It should equal `textureWidth / frameWidth`. + * + * See also: + * - {@link totalFrames} + */ + columns: number + /** + * To split the texture into multiple animation frames, this value must be set to the total number of frames in the texture. + * + * See also: + * - {@link columns} + */ + totalFrames: number + /** + * If enabled, the texture animation will use linear interpolation to mix frames when the frame index is not a whole number. For example, if the frame index is 0.5, enabling this will cause the average of the first two frames to be shown instead of just the first frame. + * + * If disabled, the frame index will be truncated to get a whole number. + * + * See also: + * - {@link frameIndex} + * - {@link frameIndexOffset} + */ + interpolateFrames: boolean + /** + * Controls the redness of the color of the additional bloom effect. The colors of the particle will be multiplied with this color to get the final color of the bloom effect. + * + * Note: + * - This has no effect if the "Effects Quality" setting is set to "Low". + * - This does not affect the natural bloom effect from high color values. + * + * See also: + * - {@link bloomGreen} + * - {@link bloomBlue} + * - {@link bloomStrength} + */ + bloomRed: number + /** + * Controls the greenness of the color of the additional bloom effect. The colors of the particle will be multiplied with this color to get the final color of the bloom effect. + * + * Note: + * - This has no effect if the "Effects Quality" setting is set to "Low". + * - This does not affect the natural bloom effect from high color values. + * + * See also: + * - {@link bloomRed} + * - {@link bloomBlue} + * - {@link bloomStrength} + */ + bloomGreen: number + /** + * Controls the blueness of the color of the additional bloom effect. The colors of the particle will be multiplied with this color to get the final color of the bloom effect. + * + * Note: + * - This has no effect if the "Effects Quality" setting is set to "Low". + * - This does not affect the natural bloom effect from high color values. + * + * See also: + * - {@link bloomRed} + * - {@link bloomGreen} + * - {@link bloomStrength} + */ + bloomBlue: number + /** + * Controls the strength of the additional bloom effect. + * + * Note: + * - This has no effect if the "Effects Quality" setting is set to "Low". + * - This does not affect the natural bloom effect from high color values. + * + * See also: + * - {@link bloomRed} + * - {@link bloomGreen} + * - {@link bloomBlue} + */ + bloomStrength: number + /** + * Minimum view distance. If the particle is closer than this distance from the camera, it will be hidden. Can be set to -1 to disable the limit. + * + * See also: + * - {@link maxDistance} + */ + minDistance: number + /** + * Maximum view distance. If the particle is farther away than this distance from the camera, it will be hidden. Can be set to -1 to disable the limit. + * + * See also: + * - {@link minDistance} + */ + maxDistance: number + /** + * Controls how dark shaded parts of the particle are. + */ + shadowDarkness: number + /** + * Specular texture ID. + * + * See also: + * - {@link lighting} + * - {@link glossiness} + * - {@link specularity} + */ + specular: number + /** + * Controls how sharp the specular highlights are. + * + * See also: + * - {@link lighting} + * - {@link specular} + * - {@link specularity} + */ + glossiness: number + /** + * Controls how the particles are lit. See {@link LightingMode} for more information. + */ + lighting: LightingMode + /** + * Controls how bright the specular highlights are. + * + * See also: + * - {@link lighting} + * - {@link specular} + * - {@link glossiness} + */ + specularity: number + unk_ds3_f1_7: number + unk_ds3_f1_11: number + unk_ds3_f1_12: number + unk_ds3_f1_13: number + unk_ds3_f1_14: number + unk_ds3_f1_15: number + unk_ds3_f1_16: number + unk_ds3_f2_0: number + unk_ds3_f2_1: boolean + unk_ds3_f2_2: number + unk_ds3_f2_3: number + unk_ds3_f2_4: number + unk_ds3_f2_9: number + unk_ds3_f2_10: number + unk_ds3_f2_11: number + unk_ds3_f2_12: number + unk_ds3_f2_13: number + unkDistFadeClose0: number + unkDistFadeClose1: number + unkDistFadeFar0: number + unkDistFadeFar1: number + unk_ds3_f2_20: number + unk_ds3_f2_21: number + unk_ds3_f2_22: number + unk_ds3_f2_23: number + unk_ds3_f2_24: number + unkDepthBlend1: number + unkDepthBlend2: number + unk_ds3_f2_27: number + unk_ds3_f2_28: number + unk_ds3_f2_29: number + unk_ds3_p1_21: ScalarValue + unk_ds3_p1_22: ScalarValue + unk_ds3_p2_2: ScalarValue + unk_ds3_p2_3: Vector4Value + unk_ds3_p2_4: Vector4Value + unk_ds3_p2_5: Vector4Value + unk_ds3_p2_6: ScalarValue + unk_sdt_f1_15: number + unk_sdt_f1_16: number + unk_sdt_f1_17: number + unk_sdt_f2_31: number + unk_sdt_f2_32: number + unk_sdt_f2_36: number + unk_sdt_f2_37: number + unk_sdt_f2_39: number + unk_sdt_f2_40: number + unk_sdt_f2_41: number + unk_sdt_f2_42: number + unk_sdt_f2_43: number + unk_sdt_f2_44: number + constructor(props: BillboardExParams = {}) { + super(ActionType.BillboardEx) + this.assign(props) + } +} + +export interface MultiTextureBillboardExParams { + /** + * Controls the orientation mode for the particles. See {@link OrientationMode} for more information. + * + * **Default**: {@link OrientationMode.CameraPlane} + */ + orientation?: OrientationMode + /** + * Mask texture ID. + * + * **Default**: `1` + */ + mask?: number + /** + * Layer 1 texture ID. + * + * **Default**: `1` + */ + layer1?: number + /** + * Layer 2 texture ID. + * + * **Default**: `1` + */ + layer2?: number + /** + * If enabled, the particle width-related properties and fields will control both the width and height of the particles, and the height counterparts will be ignored. + * + * **Default**: `false` + * + * See also: + * - {@link width} + * - {@link height} + */ + uniformScale?: boolean + /** + * To split the texture into multiple animation frames, this value must be set to the number of columns in the texture. It should equal `textureWidth / frameWidth`. + * + * **Default**: `1` + * + * See also: + * - {@link totalFrames} + */ + columns?: number + /** + * To split the texture into multiple animation frames, this value must be set to the total number of frames in the texture. + * + * **Default**: `1` + * + * See also: + * - {@link columns} + */ + totalFrames?: number + /** + * If enabled, the texture animation will use linear interpolation to mix frames when the frame index is not a whole number. For example, if the frame index is 0.5, enabling this will cause the average of the first two frames to be shown instead of just the first frame. + * + * If disabled, the frame index will be truncated to get a whole number. + * + * **Default**: `true` + * + * See also: + * - {@link frameIndex} + * - {@link frameIndexOffset} + */ + interpolateFrames?: boolean + /** + * Controls how the particles should render when behind something else. If disabled, the particles will simply be drawn behind anything they are behind in the world. If enabled, they will instead display in front of the object if they are close enough, and will fade out with distance from the object's surface that is blocking the view of the particle. + * + * **Default**: `true` + */ + depthBlend?: boolean + /** + * Controls the shape of the particles. If disabled, the particles will be rectangular. If enabled, they will be octagonal. + * + * **Default**: `false` + */ + octagonal?: boolean + /** + * Controls the redness of the color of the additional bloom effect. The colors of the particle will be multiplied with this color to get the final color of the bloom effect. + * + * Note: + * - This has no effect if the "Effects Quality" setting is set to "Low". + * - This does not affect the natural bloom effect from high color values. + * + * **Default**: `1` + * + * See also: + * - {@link bloomGreen} + * - {@link bloomBlue} + * - {@link bloomStrength} + */ + bloomRed?: number + /** + * Controls the greenness of the color of the additional bloom effect. The colors of the particle will be multiplied with this color to get the final color of the bloom effect. + * + * Note: + * - This has no effect if the "Effects Quality" setting is set to "Low". + * - This does not affect the natural bloom effect from high color values. + * + * **Default**: `1` + * + * See also: + * - {@link bloomRed} + * - {@link bloomBlue} + * - {@link bloomStrength} + */ + bloomGreen?: number + /** + * Controls the blueness of the color of the additional bloom effect. The colors of the particle will be multiplied with this color to get the final color of the bloom effect. + * + * Note: + * - This has no effect if the "Effects Quality" setting is set to "Low". + * - This does not affect the natural bloom effect from high color values. + * + * **Default**: `1` + * + * See also: + * - {@link bloomRed} + * - {@link bloomGreen} + * - {@link bloomStrength} + */ + bloomBlue?: number + /** + * Controls the strength of the additional bloom effect. + * + * Note: + * - This has no effect if the "Effects Quality" setting is set to "Low". + * - This does not affect the natural bloom effect from high color values. + * + * **Default**: `0` + * + * See also: + * - {@link bloomRed} + * - {@link bloomGreen} + * - {@link bloomBlue} + */ + bloomStrength?: number + /** + * Minimum view distance. If the particle is closer than this distance from the camera, it will be hidden. Can be set to -1 to disable the limit. + * + * **Default**: `-1` + * + * See also: + * - {@link maxDistance} + */ + minDistance?: number + /** + * Maximum view distance. If the particle is farther away than this distance from the camera, it will be hidden. Can be set to -1 to disable the limit. + * + * **Default**: `-1` + * + * See also: + * - {@link minDistance} + */ + maxDistance?: number + /** + * Controls how dark shaded parts of the particle are. + * + * **Default**: `0` + */ + shadowDarkness?: number + /** + * Specular texture ID. + * + * **Default**: `0` + * + * See also: + * - {@link lighting} + * - {@link glossiness} + */ + specular?: number + /** + * Controls how sharp the specular highlights are. + * + * **Default**: `0.25` + * + * See also: + * - {@link lighting} + * - {@link specular} + */ + glossiness?: number + /** + * Controls how the particles are lit. See {@link LightingMode} for more information. + * + * **Default**: {@link LightingMode.Unlit} + */ + lighting?: LightingMode + /** + * Unknown integer. + * + * **Default**: `1` + */ + unk_sdt_f2_38?: number + /** + * Blend mode. + * + * **Default**: {@link BlendMode.Normal} + * + * **Argument**: {@link PropertyArgument.Constant0 Constant 0} + */ + blendMode?: BlendMode | ScalarProperty + /** + * X position offset. + * + * **Default**: `0` + * + * **Argument**: {@link PropertyArgument.InstanceAge Instance age} + */ + offsetX?: ScalarValue + /** + * Y position offset. + * + * **Default**: `0` + * + * **Argument**: {@link PropertyArgument.InstanceAge Instance age} + */ + offsetY?: ScalarValue + /** + * Z position offset. + * + * **Default**: `0` + * + * **Argument**: {@link PropertyArgument.InstanceAge Instance age} + */ + offsetZ?: ScalarValue + /** + * The width of the particle. + * + * If {@link uniformScale} is enabled, this also controls the height. + * + * **Default**: `1` + * + * **Argument**: {@link PropertyArgument.InstanceAge Instance age} + */ + width?: ScalarValue + /** + * The height of the particle. + * + * If {@link uniformScale} is enabled, {@link width} also controls the height, and this property is ignored. + * + * **Default**: `1` + * + * **Argument**: {@link PropertyArgument.InstanceAge Instance age} + */ + height?: ScalarValue + /** + * Rotation around the X-axis in degrees. + * + * **Default**: `0` + * + * **Argument**: {@link PropertyArgument.Constant0 Constant 0} + * + * See also: + * - {@link rotationSpeedX} + * - {@link rotationSpeedMultiplierX} + */ + rotationX?: ScalarValue + /** + * Rotation around the Y-axis in degrees. + * + * **Default**: `0` + * + * **Argument**: {@link PropertyArgument.Constant0 Constant 0} + * + * See also: + * - {@link rotationSpeedY} + * - {@link rotationSpeedMultiplierY} + */ + rotationY?: ScalarValue + /** + * Rotation around the Z-axis in degrees. + * + * **Default**: `0` + * + * **Argument**: {@link PropertyArgument.Constant0 Constant 0} + * + * See also: + * - {@link rotationSpeedZ} + * - {@link rotationSpeedMultiplierZ} + */ + rotationZ?: ScalarValue + /** + * Rotation speed around the X-axis in degrees per second. + * + * **Default**: `0` + * + * **Argument**: {@link PropertyArgument.InstanceAge Instance age} + * + * See also: + * - {@link rotationX} + * - {@link rotationSpeedMultiplierX} + */ + rotationSpeedX?: ScalarValue + /** + * Rotation speed around the Y-axis in degrees per second. + * + * **Default**: `0` + * + * **Argument**: {@link PropertyArgument.InstanceAge Instance age} + * + * See also: + * - {@link rotationY} + * - {@link rotationSpeedMultiplierY} + */ + rotationSpeedY?: ScalarValue + /** + * Rotation speed around the Z-axis in degrees per second. + * + * **Default**: `0` + * + * **Argument**: {@link PropertyArgument.InstanceAge Instance age} + * + * See also: + * - {@link rotationZ} + * - {@link rotationSpeedMultiplierZ} + */ + rotationSpeedZ?: ScalarValue + /** + * Multiplier for {@link rotationSpeedX}. + * + * **Default**: `1` + * + * **Argument**: {@link PropertyArgument.InstanceAge Instance age} + * + * See also: + * - {@link rotationX} + */ + rotationSpeedMultiplierX?: ScalarValue + /** + * Multiplier for {@link rotationSpeedY}. + * + * **Default**: `1` + * + * **Argument**: {@link PropertyArgument.InstanceAge Instance age} + * + * See also: + * - {@link rotationY} + */ + rotationSpeedMultiplierY?: ScalarValue + /** + * Multiplier for {@link rotationSpeedZ}. + * + * **Default**: `1` + * + * **Argument**: {@link PropertyArgument.InstanceAge Instance age} + * + * See also: + * - {@link rotationZ} + */ + rotationSpeedMultiplierZ?: ScalarValue + /** + * Color multiplier for the particle. + * + * **Default**: `[1, 1, 1, 1]` + * + * **Argument**: {@link PropertyArgument.InstanceAge Instance age} + */ + color1?: Vector4Value + /** + * Color multiplier for the particle. + * + * **Default**: `[1, 1, 1, 1]` + * + * **Argument**: {@link PropertyArgument.EmissionTime Emission time} + */ + color2?: Vector4Value + /** + * Color multiplier for the particle. + * + * **Default**: `[1, 1, 1, 1]` + * + * **Argument**: {@link PropertyArgument.InstanceAge Instance age} + */ + color3?: Vector4Value + /** + * Color multiplier for both of the texture layers. + * + * **Default**: `[1, 1, 1, 1]` + * + * **Argument**: {@link PropertyArgument.InstanceAge Instance age} + */ + layersColor?: Vector4Value + /** + * Color multiplier for Layer 1. + * + * **Default**: `[1, 1, 1, 1]` + * + * **Argument**: {@link PropertyArgument.InstanceAge Instance age} + */ + layer1Color?: Vector4Value + /** + * Color multiplier for Layer 2. + * + * **Default**: `[1, 1, 1, 1]` + * + * **Argument**: {@link PropertyArgument.InstanceAge Instance age} + */ + layer2Color?: Vector4Value + /** + * Parts of the particle with less opacity than this threshold will be invisible. The range is 0-255. + * + * **Default**: `0` + * + * **Argument**: {@link PropertyArgument.InstanceAge Instance age} + */ + alphaThreshold?: ScalarValue + /** + * The index of the frame to show from the texture atlas. Can be animated using a {@link PropertyFunction.Linear linear property} or similar. + * + * Seemingly identical to {@link frameIndexOffset}? The sum of these two properties is the actual frame index that gets used. + * + * **Default**: `0` + * + * **Argument**: {@link PropertyArgument.InstanceAge Instance age} + */ + frameIndex?: ScalarValue + /** + * Seemingly identical to {@link frameIndex}? The sum of these two properties is the actual frame index that gets used. + * + * **Default**: `0` + * + * **Argument**: {@link PropertyArgument.InstanceAge Instance age} + */ + frameIndexOffset?: ScalarValue + /** + * Horiztonal scroll speed for Layer 1. + * + * **Default**: `0` + * + * **Argument**: {@link PropertyArgument.InstanceAge Instance age} + */ + layer1SpeedU?: ScalarValue + /** + * Vertical scroll speed for Layer 1. + * + * **Default**: `0` + * + * **Argument**: {@link PropertyArgument.InstanceAge Instance age} + */ + layer1SpeedV?: ScalarValue + /** + * Horizontal offset for the UV coordinates of Layer 1. + * + * **Default**: `0` + * + * **Argument**: {@link PropertyArgument.Constant0 Constant 0} + */ + layer1OffsetU?: ScalarValue + /** + * Vertical offset for the UV coordinates of Layer 1. + * + * **Default**: `0` + * + * **Argument**: {@link PropertyArgument.Constant0 Constant 0} + */ + layer1OffsetV?: ScalarValue + /** + * Horizontal scale for the UV coordinates of Layer 1. + * + * **Default**: `1` + * + * **Argument**: {@link PropertyArgument.InstanceAge Instance age} + */ + layer1ScaleU?: ScalarValue + /** + * Vertical scale for the UV coordinates of Layer 1. + * + * **Default**: `1` + * + * **Argument**: {@link PropertyArgument.InstanceAge Instance age} + */ + layer1ScaleV?: ScalarValue + /** + * Horiztonal scroll speed for Layer 2. + * + * **Default**: `0` + * + * **Argument**: {@link PropertyArgument.InstanceAge Instance age} + */ + layer2SpeedU?: ScalarValue + /** + * Vertical scroll speed for Layer 2. + * + * **Default**: `0` + * + * **Argument**: {@link PropertyArgument.InstanceAge Instance age} + */ + layer2SpeedV?: ScalarValue + /** + * Horizontal offset for the UV coordinates of Layer 2. + * + * **Default**: `0` + * + * **Argument**: {@link PropertyArgument.Constant0 Constant 0} + */ + layer2OffsetU?: ScalarValue + /** + * Vertical offset for the UV coordinates of Layer 2. + * + * **Default**: `0` + * + * **Argument**: {@link PropertyArgument.Constant0 Constant 0} + */ + layer2OffsetV?: ScalarValue + /** + * Horizontal scale for the UV coordinates of Layer 2. + * + * **Default**: `1` + * + * **Argument**: {@link PropertyArgument.InstanceAge Instance age} + */ + layer2ScaleU?: ScalarValue + /** + * Vertical scale for the UV coordinates of Layer 2. + * + * **Default**: `1` + * + * **Argument**: {@link PropertyArgument.InstanceAge Instance age} + */ + layer2ScaleV?: ScalarValue + /** + * Scalar multiplier for the color that does not affect the alpha. Effectively a brightness multiplier. + * + * **Default**: `1` + * + * **Argument**: {@link PropertyArgument.EffectAge Effect age} + */ + rgbMultiplier?: ScalarValue + /** + * Alpha multiplier. + * + * **Default**: `1` + * + * **Argument**: {@link PropertyArgument.EffectAge Effect age} + */ + alphaMultiplier?: ScalarValue + /** + * Unknown integer. + * + * **Default**: `0` + */ + unk_ds3_f1_6?: number + /** + * Unknown integer. + * + * **Default**: `-2` + */ + unk_ds3_f1_10?: number + /** + * Unknown integer. + * + * **Default**: `-2` + */ + unk_ds3_f1_11?: number + /** + * Unknown integer. + * + * **Default**: `0` + */ + unk_ds3_f1_14?: number + /** + * Unknown integer. + * + * **Default**: `0` + */ + unk_ds3_f2_0?: number + /** + * Unknown integer. + * + * **Default**: `0` + */ + unk_ds3_f2_1?: number + /** + * Unknown integer. + * + * **Default**: `8` + */ + unk_ds3_f2_2?: number + /** + * Unknown integer. + * + * **Default**: `0` + */ + unk_ds3_f2_3?: number + /** + * Unknown integer. + * + * **Default**: `1` + */ + unk_ds3_f2_4?: number + /** + * Unknown integer. + * + * **Default**: `0` + */ + unk_ds3_f2_9?: number + /** + * Unknown integer. + * + * **Default**: `0` + */ + unk_ds3_f2_10?: number + /** + * Unknown integer. + * + * **Default**: `0` + */ + unk_ds3_f2_11?: number + /** + * Unknown integer. + * + * **Default**: `0` + */ + unk_ds3_f2_12?: number + /** + * Unknown integer. + * + * **Default**: `0` + */ + unk_ds3_f2_13?: number + /** + * Unknown float. + * + * **Default**: `-1` + */ + unkDistFadeClose0?: number + /** + * Unknown float. + * + * **Default**: `-1` + */ + unkDistFadeClose1?: number + /** + * Unknown float. + * + * **Default**: `-1` + */ + unkDistFadeFar0?: number + /** + * Unknown float. + * + * **Default**: `-1` + */ + unkDistFadeFar1?: number + /** + * Unknown integer. + * + * **Default**: `0` + */ + unk_ds3_f2_20?: number + /** + * Unknown integer. + * + * **Default**: `0` + */ + unk_ds3_f2_21?: number + /** + * Unknown integer. + * + * **Default**: `0` + */ + unk_ds3_f2_22?: number + /** + * Unknown integer. + * + * **Default**: `0` + */ + unk_ds3_f2_23?: number + /** + * Unknown integer. + * + * **Default**: `0` + */ + unk_ds3_f2_24?: number + /** + * Unknown float. + * + * **Default**: `1` + */ + unkDepthBlend1?: number + /** + * Unknown float. + * + * **Default**: `0` + */ + unkDepthBlend2?: number + /** + * Unknown integer. + * + * **Default**: `1` + */ + unk_ds3_f2_27?: number + /** + * Unknown integer. + * + * **Default**: `0` + */ + unk_ds3_f2_28?: number + /** + * Unknown float. + * + * **Default**: `5` + */ + unk_ds3_f2_29?: number + /** + * Unknown. + * + * **Default**: `0` + */ + unk_ds3_p1_23?: ScalarValue + /** + * Unknown. + * + * **Default**: `0` + */ + unk_ds3_p1_24?: ScalarValue + /** + * Unknown. + * + * **Default**: `0` + */ + unk_ds3_p1_25?: ScalarValue + /** + * Unknown. + * + * **Default**: `0` + */ + unk_ds3_p1_26?: ScalarValue + /** + * Unknown. + * + * **Default**: `1` + */ + unk_ds3_p1_27?: ScalarValue + /** + * Unknown. + * + * **Default**: `1` + */ + unk_ds3_p1_28?: ScalarValue + /** + * Unknown. + * + * **Default**: `0` + */ + unk_ds3_p2_2?: ScalarValue + /** + * Unknown. + * + * **Default**: `[1, 1, 1, 1]` + */ + unk_ds3_p2_3?: Vector4Value + /** + * Unknown. + * + * **Default**: `[1, 1, 1, 1]` + */ + unk_ds3_p2_4?: Vector4Value + /** + * Unknown. + * + * **Default**: `[1, 1, 1, 1]` + */ + unk_ds3_p2_5?: Vector4Value + /** + * Unknown. + * + * **Default**: `0` + */ + unk_ds3_p2_6?: ScalarValue + /** + * Unknown integer. + * + * **Default**: `0` + */ + unk_sdt_f2_31?: number + /** + * Unknown integer. + * + * **Default**: `1` + */ + unk_sdt_f2_32?: number + /** + * Unknown integer. + * + * **Default**: `-2` + */ + unk_sdt_f2_36?: number + /** + * Unknown integer. + * + * **Default**: `0` + */ + unk_sdt_f2_37?: number + /** + * Unknown integer. + * + * **Default**: `1` + */ + unk_sdt_f2_39?: number + /** + * Unknown integer. + * + * **Default**: `0` + */ + unk_sdt_f2_40?: number + /** + * Unknown integer. + * + * **Default**: `0` + */ + unk_sdt_f2_41?: number + /** + * Unknown integer. + * + * **Default**: `1` + */ + unk_er_f1_14?: number + /** + * Unknown integer. + * + * **Default**: `1` + */ + unk_er_f1_15?: number + /** + * Unknown integer. + * + * **Default**: `0` + */ + unk_er_f1_16?: number + /** + * Unknown integer. + * + * **Default**: `0` + */ + unk_er_f2_42?: number + /** + * Unknown integer. + * + * **Default**: `0` + */ + unk_er_f2_43?: number + /** + * Unknown integer. + * + * **Default**: `0` + */ + unk_er_f2_44?: number + /** + * Unknown integer. + * + * **Default**: `0` + */ + unk_er_f2_45?: number + /** + * Unknown integer. + * + * **Default**: `0` + */ + unk_ac6_f2_46?: number +} + +/** + * Particle with multiple textures that can scroll. + */ +class MultiTextureBillboardEx extends DataAction { + declare type: ActionType.MultiTextureBillboardEx + /** + * Controls the orientation mode for the particles. See {@link OrientationMode} for more information. + */ + orientation: OrientationMode + /** + * Mask texture ID. + */ + mask: number + /** + * Layer 1 texture ID. + */ + layer1: number + /** + * Layer 2 texture ID. + */ + layer2: number + /** + * If enabled, the particle width-related properties and fields will control both the width and height of the particles, and the height counterparts will be ignored. + * + * See also: + * - {@link width} + * - {@link height} + */ + uniformScale: boolean + /** + * To split the texture into multiple animation frames, this value must be set to the number of columns in the texture. It should equal `textureWidth / frameWidth`. + * + * See also: + * - {@link totalFrames} + */ + columns: number + /** + * To split the texture into multiple animation frames, this value must be set to the total number of frames in the texture. + * + * See also: + * - {@link columns} + */ + totalFrames: number + /** + * If enabled, the texture animation will use linear interpolation to mix frames when the frame index is not a whole number. For example, if the frame index is 0.5, enabling this will cause the average of the first two frames to be shown instead of just the first frame. + * + * If disabled, the frame index will be truncated to get a whole number. + * + * See also: + * - {@link frameIndex} + * - {@link frameIndexOffset} + */ + interpolateFrames: boolean + /** + * Controls how the particles should render when behind something else. If disabled, the particles will simply be drawn behind anything they are behind in the world. If enabled, they will instead display in front of the object if they are close enough, and will fade out with distance from the object's surface that is blocking the view of the particle. + */ + depthBlend: boolean + /** + * Controls the shape of the particles. If disabled, the particles will be rectangular. If enabled, they will be octagonal. + */ + octagonal: boolean + /** + * Controls the redness of the color of the additional bloom effect. The colors of the particle will be multiplied with this color to get the final color of the bloom effect. + * + * Note: + * - This has no effect if the "Effects Quality" setting is set to "Low". + * - This does not affect the natural bloom effect from high color values. + * + * See also: + * - {@link bloomGreen} + * - {@link bloomBlue} + * - {@link bloomStrength} + */ + bloomRed: number + /** + * Controls the greenness of the color of the additional bloom effect. The colors of the particle will be multiplied with this color to get the final color of the bloom effect. + * + * Note: + * - This has no effect if the "Effects Quality" setting is set to "Low". + * - This does not affect the natural bloom effect from high color values. + * + * See also: + * - {@link bloomRed} + * - {@link bloomBlue} + * - {@link bloomStrength} + */ + bloomGreen: number + /** + * Controls the blueness of the color of the additional bloom effect. The colors of the particle will be multiplied with this color to get the final color of the bloom effect. + * + * Note: + * - This has no effect if the "Effects Quality" setting is set to "Low". + * - This does not affect the natural bloom effect from high color values. + * + * See also: + * - {@link bloomRed} + * - {@link bloomGreen} + * - {@link bloomStrength} + */ + bloomBlue: number + /** + * Controls the strength of the additional bloom effect. + * + * Note: + * - This has no effect if the "Effects Quality" setting is set to "Low". + * - This does not affect the natural bloom effect from high color values. + * + * See also: + * - {@link bloomRed} + * - {@link bloomGreen} + * - {@link bloomBlue} + */ + bloomStrength: number + /** + * Minimum view distance. If the particle is closer than this distance from the camera, it will be hidden. Can be set to -1 to disable the limit. + * + * See also: + * - {@link maxDistance} + */ + minDistance: number + /** + * Maximum view distance. If the particle is farther away than this distance from the camera, it will be hidden. Can be set to -1 to disable the limit. + * + * See also: + * - {@link minDistance} + */ + maxDistance: number + /** + * Controls how dark shaded parts of the particle are. + */ + shadowDarkness: number + /** + * Specular texture ID. + * + * See also: + * - {@link lighting} + * - {@link glossiness} + */ + specular: number + /** + * Controls how sharp the specular highlights are. + * + * See also: + * - {@link lighting} + * - {@link specular} + */ + glossiness: number + /** + * Controls how the particles are lit. See {@link LightingMode} for more information. + */ + lighting: LightingMode + unk_sdt_f2_38: number + /** + * Blend mode. + * + * **Argument**: {@link PropertyArgument.Constant0 Constant 0} + */ + blendMode: BlendMode | ScalarProperty + /** + * X position offset. + * + * **Argument**: {@link PropertyArgument.InstanceAge Instance age} + */ + offsetX: ScalarValue + /** + * Y position offset. + * + * **Argument**: {@link PropertyArgument.InstanceAge Instance age} + */ + offsetY: ScalarValue + /** + * Z position offset. + * + * **Argument**: {@link PropertyArgument.InstanceAge Instance age} + */ + offsetZ: ScalarValue + /** + * The width of the particle. + * + * If {@link uniformScale} is enabled, this also controls the height. + * + * **Argument**: {@link PropertyArgument.InstanceAge Instance age} + */ + width: ScalarValue + /** + * The height of the particle. + * + * If {@link uniformScale} is enabled, {@link width} also controls the height, and this property is ignored. + * + * **Argument**: {@link PropertyArgument.InstanceAge Instance age} + */ + height: ScalarValue + /** + * Rotation around the X-axis in degrees. + * + * **Argument**: {@link PropertyArgument.Constant0 Constant 0} + * + * See also: + * - {@link rotationSpeedX} + * - {@link rotationSpeedMultiplierX} + */ + rotationX: ScalarValue + /** + * Rotation around the Y-axis in degrees. + * + * **Argument**: {@link PropertyArgument.Constant0 Constant 0} + * + * See also: + * - {@link rotationSpeedY} + * - {@link rotationSpeedMultiplierY} + */ + rotationY: ScalarValue + /** + * Rotation around the Z-axis in degrees. + * + * **Argument**: {@link PropertyArgument.Constant0 Constant 0} + * + * See also: + * - {@link rotationSpeedZ} + * - {@link rotationSpeedMultiplierZ} + */ + rotationZ: ScalarValue + /** + * Rotation speed around the X-axis in degrees per second. + * + * **Argument**: {@link PropertyArgument.InstanceAge Instance age} + * + * See also: + * - {@link rotationX} + * - {@link rotationSpeedMultiplierX} + */ + rotationSpeedX: ScalarValue + /** + * Rotation speed around the Y-axis in degrees per second. + * + * **Argument**: {@link PropertyArgument.InstanceAge Instance age} + * + * See also: + * - {@link rotationY} + * - {@link rotationSpeedMultiplierY} + */ + rotationSpeedY: ScalarValue + /** + * Rotation speed around the Z-axis in degrees per second. + * + * **Argument**: {@link PropertyArgument.InstanceAge Instance age} + * + * See also: + * - {@link rotationZ} + * - {@link rotationSpeedMultiplierZ} + */ + rotationSpeedZ: ScalarValue + /** + * Multiplier for {@link rotationSpeedX}. + * + * **Argument**: {@link PropertyArgument.InstanceAge Instance age} + * + * See also: + * - {@link rotationX} + */ + rotationSpeedMultiplierX: ScalarValue + /** + * Multiplier for {@link rotationSpeedY}. + * + * **Argument**: {@link PropertyArgument.InstanceAge Instance age} + * + * See also: + * - {@link rotationY} + */ + rotationSpeedMultiplierY: ScalarValue + /** + * Multiplier for {@link rotationSpeedZ}. + * + * **Argument**: {@link PropertyArgument.InstanceAge Instance age} + * + * See also: + * - {@link rotationZ} + */ + rotationSpeedMultiplierZ: ScalarValue + /** + * Color multiplier for the particle. + * + * **Argument**: {@link PropertyArgument.InstanceAge Instance age} + */ + color1: Vector4Value + /** + * Color multiplier for the particle. + * + * **Argument**: {@link PropertyArgument.EmissionTime Emission time} + */ + color2: Vector4Value + /** + * Color multiplier for the particle. + * + * **Argument**: {@link PropertyArgument.InstanceAge Instance age} + */ + color3: Vector4Value + /** + * Color multiplier for both of the texture layers. + * + * **Argument**: {@link PropertyArgument.InstanceAge Instance age} + */ + layersColor: Vector4Value + /** + * Color multiplier for Layer 1. + * + * **Argument**: {@link PropertyArgument.InstanceAge Instance age} + */ + layer1Color: Vector4Value + /** + * Color multiplier for Layer 2. + * + * **Argument**: {@link PropertyArgument.InstanceAge Instance age} + */ + layer2Color: Vector4Value + /** + * Parts of the particle with less opacity than this threshold will be invisible. The range is 0-255. + * + * **Argument**: {@link PropertyArgument.InstanceAge Instance age} + */ + alphaThreshold: ScalarValue + /** + * The index of the frame to show from the texture atlas. Can be animated using a {@link PropertyFunction.Linear linear property} or similar. + * + * Seemingly identical to {@link frameIndexOffset}? The sum of these two properties is the actual frame index that gets used. + * + * **Argument**: {@link PropertyArgument.InstanceAge Instance age} + */ + frameIndex: ScalarValue + /** + * Seemingly identical to {@link frameIndex}? The sum of these two properties is the actual frame index that gets used. + * + * **Argument**: {@link PropertyArgument.InstanceAge Instance age} + */ + frameIndexOffset: ScalarValue + /** + * Horiztonal scroll speed for Layer 1. + * + * **Argument**: {@link PropertyArgument.InstanceAge Instance age} + */ + layer1SpeedU: ScalarValue + /** + * Vertical scroll speed for Layer 1. + * + * **Argument**: {@link PropertyArgument.InstanceAge Instance age} + */ + layer1SpeedV: ScalarValue + /** + * Horizontal offset for the UV coordinates of Layer 1. + * + * **Argument**: {@link PropertyArgument.Constant0 Constant 0} + */ + layer1OffsetU: ScalarValue + /** + * Vertical offset for the UV coordinates of Layer 1. + * + * **Argument**: {@link PropertyArgument.Constant0 Constant 0} + */ + layer1OffsetV: ScalarValue + /** + * Horizontal scale for the UV coordinates of Layer 1. + * + * **Argument**: {@link PropertyArgument.InstanceAge Instance age} + */ + layer1ScaleU: ScalarValue + /** + * Vertical scale for the UV coordinates of Layer 1. + * + * **Argument**: {@link PropertyArgument.InstanceAge Instance age} + */ + layer1ScaleV: ScalarValue + /** + * Horiztonal scroll speed for Layer 2. + * + * **Argument**: {@link PropertyArgument.InstanceAge Instance age} + */ + layer2SpeedU: ScalarValue + /** + * Vertical scroll speed for Layer 2. + * + * **Argument**: {@link PropertyArgument.InstanceAge Instance age} + */ + layer2SpeedV: ScalarValue + /** + * Horizontal offset for the UV coordinates of Layer 2. + * + * **Argument**: {@link PropertyArgument.Constant0 Constant 0} + */ + layer2OffsetU: ScalarValue + /** + * Vertical offset for the UV coordinates of Layer 2. + * + * **Argument**: {@link PropertyArgument.Constant0 Constant 0} + */ + layer2OffsetV: ScalarValue + /** + * Horizontal scale for the UV coordinates of Layer 2. + * + * **Argument**: {@link PropertyArgument.InstanceAge Instance age} + */ + layer2ScaleU: ScalarValue + /** + * Vertical scale for the UV coordinates of Layer 2. + * + * **Argument**: {@link PropertyArgument.InstanceAge Instance age} + */ + layer2ScaleV: ScalarValue + /** + * Scalar multiplier for the color that does not affect the alpha. Effectively a brightness multiplier. + * + * **Argument**: {@link PropertyArgument.EffectAge Effect age} + */ + rgbMultiplier: ScalarValue + /** + * Alpha multiplier. + * + * **Argument**: {@link PropertyArgument.EffectAge Effect age} + */ + alphaMultiplier: ScalarValue + unk_ds3_f1_6: number + unk_ds3_f1_10: number + unk_ds3_f1_11: number + unk_ds3_f1_14: number + unk_ds3_f2_0: number + unk_ds3_f2_1: number + unk_ds3_f2_2: number + unk_ds3_f2_3: number + unk_ds3_f2_4: number + unk_ds3_f2_9: number + unk_ds3_f2_10: number + unk_ds3_f2_11: number + unk_ds3_f2_12: number + unk_ds3_f2_13: number + unkDistFadeClose0: number + unkDistFadeClose1: number + unkDistFadeFar0: number + unkDistFadeFar1: number + unk_ds3_f2_20: number + unk_ds3_f2_21: number + unk_ds3_f2_22: number + unk_ds3_f2_23: number + unk_ds3_f2_24: number + unkDepthBlend1: number + unkDepthBlend2: number + unk_ds3_f2_27: number + unk_ds3_f2_28: number + unk_ds3_f2_29: number + unk_ds3_p1_23: ScalarValue + unk_ds3_p1_24: ScalarValue + unk_ds3_p1_25: ScalarValue + unk_ds3_p1_26: ScalarValue + unk_ds3_p1_27: ScalarValue + unk_ds3_p1_28: ScalarValue + unk_ds3_p2_2: ScalarValue + unk_ds3_p2_3: Vector4Value + unk_ds3_p2_4: Vector4Value + unk_ds3_p2_5: Vector4Value + unk_ds3_p2_6: ScalarValue + unk_sdt_f2_31: number + unk_sdt_f2_32: number + unk_sdt_f2_36: number + unk_sdt_f2_37: number + unk_sdt_f2_39: number + unk_sdt_f2_40: number + unk_sdt_f2_41: number + unk_er_f1_14: number + unk_er_f1_15: number + unk_er_f1_16: number + unk_er_f2_42: number + unk_er_f2_43: number + unk_er_f2_44: number + unk_er_f2_45: number + unk_ac6_f2_46: number + constructor(props: MultiTextureBillboardExParams = {}) { + super(ActionType.MultiTextureBillboardEx) + this.assign(props) + } +} + +export interface ModelParams { + /** + * Controls the orientation mode for the particles. See {@link OrientationMode} for more information. + * + * **Default**: {@link OrientationMode.LocalSouth} + */ + orientation?: OrientationMode + /** + * Each particle will pick a random number between this value and 1, and the width of the particle will be multiplied by this number. For example, setting this to 0.5 will make the particles randomly thinner, down to half width. Setting it to 2 will make them randomly wider, up to double width. + * + * If {@link uniformScale} is enabled, this also affects the height. + * + * **Default**: `1` + * + * See also: + * - {@link scaleVariationY} + * - {@link scaleVariationZ} + */ + scaleVariationX?: number + /** + * Each particle will pick a random number between this value and 1, and the height of the particle will be multiplied by this number. For example, setting this to 0.5 will make the particles randomly shorter, down to half height. Setting it to 2 will make them randomly taller, up to double height. + * + * If {@link uniformScale} is enabled, {@link scaleVariationX} also affects the height, and this field is ignored. + * + * **Default**: `1` + * + * See also: + * - {@link scaleVariationX} + * - {@link scaleVariationZ} + */ + scaleVariationY?: number + /** + * Each particle will pick a random number between this value and 1, and the depth of the particle will be multiplied by this number. For example, setting this to 0.5 will make the particles randomly shallower, down to half depth. Setting it to 2 will make them randomly deeper, up to double depth. + * + * If {@link uniformScale} is enabled, {@link scaleVariationX} also affects the depth, and this field is ignored. + * + * **Default**: `1` + * + * See also: + * - {@link scaleVariationX} + * - {@link scaleVariationY} + */ + scaleVariationZ?: number + /** + * If enabled, the particle X scale-related properties and fields will control the scale in all axes, and the Y and Z counterparts will be ignored. + * + * **Default**: `false` + * + * See also: + * - {@link sizeX} + * - {@link sizeY} + * - {@link sizeZ} + * - {@link scaleVariationX} + * - {@link scaleVariationY} + * - {@link scaleVariationZ} + */ + uniformScale?: boolean + /** + * To split the texture into multiple animation frames, this value must be set to the number of columns in the texture. It should equal `textureWidth / frameWidth`. + * + * **Default**: `1` + * + * See also: + * - {@link totalFrames} + */ + columns?: number + /** + * To split the texture into multiple animation frames, this value must be set to the total number of frames in the texture. + * + * **Default**: `1` + * + * See also: + * - {@link columns} + */ + totalFrames?: number + /** + * Controls the redness of the color of the additional bloom effect. The colors of the particle will be multiplied with this color to get the final color of the bloom effect. + * + * Note: + * - This has no effect if the "Effects Quality" setting is set to "Low". + * - This does not affect the natural bloom effect from high color values. + * + * **Default**: `1` + * + * See also: + * - {@link bloomGreen} + * - {@link bloomBlue} + * - {@link bloomStrength} + */ + bloomRed?: number + /** + * Controls the greenness of the color of the additional bloom effect. The colors of the particle will be multiplied with this color to get the final color of the bloom effect. + * + * Note: + * - This has no effect if the "Effects Quality" setting is set to "Low". + * - This does not affect the natural bloom effect from high color values. + * + * **Default**: `1` + * + * See also: + * - {@link bloomRed} + * - {@link bloomBlue} + * - {@link bloomStrength} + */ + bloomGreen?: number + /** + * Controls the blueness of the color of the additional bloom effect. The colors of the particle will be multiplied with this color to get the final color of the bloom effect. + * + * Note: + * - This has no effect if the "Effects Quality" setting is set to "Low". + * - This does not affect the natural bloom effect from high color values. + * + * **Default**: `1` + * + * See also: + * - {@link bloomRed} + * - {@link bloomGreen} + * - {@link bloomStrength} + */ + bloomBlue?: number + /** + * Controls the strength of the additional bloom effect. + * + * Note: + * - This has no effect if the "Effects Quality" setting is set to "Low". + * - This does not affect the natural bloom effect from high color values. + * + * **Default**: `0` + * + * See also: + * - {@link bloomRed} + * - {@link bloomGreen} + * - {@link bloomBlue} + */ + bloomStrength?: number + /** + * Minimum view distance. If the particle is closer than this distance from the camera, it will be hidden. Can be set to -1 to disable the limit. + * + * **Default**: `-1` + * + * See also: + * - {@link maxDistance} + */ + minDistance?: number + /** + * Maximum view distance. If the particle is farther away than this distance from the camera, it will be hidden. Can be set to -1 to disable the limit. + * + * **Default**: `-1` + * + * See also: + * - {@link minDistance} + */ + maxDistance?: number + /** + * Model ID. + * + * **Default**: `80201` + * + * **Argument**: {@link PropertyArgument.Constant0 Constant 0} + */ + model?: ScalarValue + /** + * The width of the particle. + * + * If {@link uniformScale} is enabled, this also controls the height and depth. + * + * **Default**: `1` + * + * **Argument**: {@link PropertyArgument.InstanceAge Instance age} + * + * See also: + * - {@link scaleVariationX} + * - {@link sizeY} + * - {@link sizeZ} + */ + sizeX?: ScalarValue + /** + * The height of the particle. + * + * If {@link uniformScale} is enabled, {@link sizeX} also controls the height, and this property is ignored. + * + * **Default**: `1` + * + * **Argument**: {@link PropertyArgument.InstanceAge Instance age} + * + * See also: + * - {@link scaleVariationY} + * - {@link sizeX} + * - {@link sizeZ} + */ + sizeY?: ScalarValue + /** + * The depth of the particle. + * + * If {@link uniformScale} is enabled, {@link sizeX} also controls the depth, and this property is ignored. + * + * **Default**: `1` + * + * **Argument**: {@link PropertyArgument.InstanceAge Instance age} + * + * See also: + * - {@link scaleVariationZ} + * - {@link sizeX} + * - {@link sizeY} + */ + sizeZ?: ScalarValue + /** + * Rotation around the X-axis in degrees. + * + * **Default**: `0` + * + * **Argument**: {@link PropertyArgument.Constant0 Constant 0} + * + * See also: + * - {@link rotationSpeedX} + * - {@link rotationSpeedMultiplierX} + */ + rotationX?: ScalarValue + /** + * Rotation around the Y-axis in degrees. + * + * **Default**: `0` + * + * **Argument**: {@link PropertyArgument.Constant0 Constant 0} + * + * See also: + * - {@link rotationSpeedY} + * - {@link rotationSpeedMultiplierY} + */ + rotationY?: ScalarValue + /** + * Rotation around the Z-axis in degrees. + * + * **Default**: `0` + * + * **Argument**: {@link PropertyArgument.Constant0 Constant 0} + * + * See also: + * - {@link rotationSpeedZ} + * - {@link rotationSpeedMultiplierZ} + */ + rotationZ?: ScalarValue + /** + * Rotation speed around the X-axis in degrees per second. + * + * **Default**: `0` + * + * **Argument**: {@link PropertyArgument.InstanceAge Instance age} + * + * See also: + * - {@link rotationX} + * - {@link rotationSpeedMultiplierX} + */ + rotationSpeedX?: ScalarValue + /** + * Rotation speed around the Y-axis in degrees per second. + * + * **Default**: `0` + * + * **Argument**: {@link PropertyArgument.InstanceAge Instance age} + * + * See also: + * - {@link rotationY} + * - {@link rotationSpeedMultiplierY} + */ + rotationSpeedY?: ScalarValue + /** + * Rotation speed around the Z-axis in degrees per second. + * + * **Default**: `0` + * + * **Argument**: {@link PropertyArgument.InstanceAge Instance age} + * + * See also: + * - {@link rotationZ} + * - {@link rotationSpeedMultiplierZ} + */ + rotationSpeedZ?: ScalarValue + /** + * Multiplier for {@link rotationSpeedX}. + * + * **Default**: `1` + * + * **Argument**: {@link PropertyArgument.InstanceAge Instance age} + * + * See also: + * - {@link rotationX} + */ + rotationSpeedMultiplierX?: ScalarValue + /** + * Multiplier for {@link rotationSpeedY}. + * + * **Default**: `1` + * + * **Argument**: {@link PropertyArgument.InstanceAge Instance age} + * + * See also: + * - {@link rotationY} + */ + rotationSpeedMultiplierY?: ScalarValue + /** + * Multiplier for {@link rotationSpeedZ}. + * + * **Default**: `1` + * + * **Argument**: {@link PropertyArgument.InstanceAge Instance age} + * + * See also: + * - {@link rotationZ} + */ + rotationSpeedMultiplierZ?: ScalarValue + /** + * Blend mode. + * + * Note that the materials used by the model may affect how the different blend modes work. Don't expect the blend modes to always work exactly like they do in other types of instances. + * + * **Default**: {@link BlendMode.Normal} + * + * **Argument**: {@link PropertyArgument.Constant0 Constant 0} + */ + blendMode?: BlendMode | ScalarProperty + /** + * Color multiplier for the particle. + * + * **Default**: `[1, 1, 1, 1]` + * + * **Argument**: {@link PropertyArgument.InstanceAge Instance age} + */ + color1?: Vector4Value + /** + * Color multiplier for the particle. + * + * **Default**: `[1, 1, 1, 1]` + * + * **Argument**: {@link PropertyArgument.EmissionTime Emission time} + */ + color2?: Vector4Value + /** + * Color multiplier for the particle. + * + * **Default**: `[1, 1, 1, 1]` + * + * **Argument**: {@link PropertyArgument.InstanceAge Instance age} + */ + color3?: Vector4Value + /** + * The index of the frame to show from the texture atlas. Can be animated using a {@link PropertyFunction.Linear linear property} or similar. + * + * Seemingly identical to {@link frameIndexOffset}? The sum of these two properties is the actual frame index that gets used. + * + * **Default**: `0` + * + * **Argument**: {@link PropertyArgument.InstanceAge Instance age} + */ + frameIndex?: ScalarValue + /** + * Seemingly identical to {@link frameIndex}? The sum of these two properties is the actual frame index that gets used. + * + * **Default**: `0` + * + * **Argument**: {@link PropertyArgument.InstanceAge Instance age} + */ + frameIndexOffset?: ScalarValue + /** + * Horizontal offset for the UV coordinates of the model. + * + * If the texture is an animation sheet that is split up into multiple frames using {@link columns} and/or {@link totalFrames}, this property has no effect. + * + * **Default**: `0` + * + * **Argument**: {@link PropertyArgument.Constant0 Constant 0} + * + * See also: + * - {@link speedU} + * - {@link offsetV} + */ + offsetU?: ScalarValue + /** + * Vertical offset for the UV coordinates of the model. + * + * If the texture is an animation sheet that is split up into multiple frames using {@link columns} and/or {@link totalFrames}, this property has no effect. + * + * **Default**: `0` + * + * **Argument**: {@link PropertyArgument.Constant0 Constant 0} + */ + offsetV?: ScalarValue + /** + * Horiztonal scroll speed for the model's texture. + * + * If the texture is an animation sheet that is split up into multiple frames using {@link columns} and/or {@link totalFrames}, this property has no effect. + * + * **Default**: `0` + * + * **Argument**: {@link PropertyArgument.InstanceAge Instance age} + * + * See also: + * - {@link speedMultiplierU} + * - {@link offsetU} + */ + speedU?: ScalarValue + /** + * Multiplier for {@link speedU}. + * + * **Default**: `0` + * + * **Argument**: {@link PropertyArgument.InstanceAge Instance age} + */ + speedMultiplierU?: ScalarValue + /** + * Vertical scroll speed for the model's texture. + * + * If the texture is an animation sheet that is split up into multiple frames using {@link columns} and/or {@link totalFrames}, this property has no effect. + * + * **Default**: `0` + * + * **Argument**: {@link PropertyArgument.InstanceAge Instance age} + * + * See also: + * - {@link speedMultiplierV} + * - {@link offsetV} + */ + speedV?: ScalarValue + /** + * Multiplier for {@link speedV}. + * + * **Default**: `0` + * + * **Argument**: {@link PropertyArgument.InstanceAge Instance age} + */ + speedMultiplierV?: ScalarValue + /** + * Scalar multiplier for the color that does not affect the alpha. Effectively a brightness multiplier. + * + * **Default**: `1` + * + * **Argument**: {@link PropertyArgument.EffectAge Effect age} + */ + rgbMultiplier?: ScalarValue + /** + * Alpha multiplier. + * + * **Default**: `1` + * + * **Argument**: {@link PropertyArgument.EffectAge Effect age} + */ + alphaMultiplier?: ScalarValue + /** + * Unknown integer. + * + * **Default**: `-2` + */ + unk_ds3_f1_9?: number + /** + * Unknown integer. + * + * **Default**: `-2` + */ + unk_ds3_f1_10?: number + /** + * Unknown boolean. + * + * **Default**: `true` + */ + unk_ds3_f1_11?: boolean + /** + * Unknown boolean. + * + * **Default**: `true` + */ + unk_ds3_f1_12?: boolean + /** + * Unknown integer. + * + * **Default**: `1` + */ + unk_ds3_f1_13?: number + /** + * Animation ID. + * + * **Default**: `0` + * + * See also: + * - {@link loopAnimation} + * - {@link animationSpeed} + */ + animation?: number + /** + * Unknown. Probably a boolean. Seems to just disable the {@link animation} if set to anything but 0. + * + * **Default**: `0` + */ + unk_ds3_f1_15?: number + /** + * If disabled, the animation will only play once and then freeze on the last frame. If enabled, the animation will loop. + * + * **Default**: `1` + * + * See also: + * - {@link animation} + * - {@link animationSpeed} + */ + loopAnimation?: boolean + /** + * Controls the speed at which the {@link animation} plays. + * + * **Default**: `1` + * + * See also: + * - {@link animation} + * - {@link loopAnimation} + */ + animationSpeed?: number + /** + * Unknown integer. + * + * **Default**: `0` + */ + unk_ds3_f1_18?: number + /** + * Unknown integer. + * + * **Default**: `0` + */ + unk_ds3_f2_0?: number + /** + * Unknown integer. + * + * **Default**: `0` + */ + unk_ds3_f2_1?: number + /** + * Unknown integer. + * + * **Default**: `8` + */ + unk_ds3_f2_2?: number + /** + * Unknown integer. + * + * **Default**: `0` + */ + unk_ds3_f2_3?: number + /** + * Unknown integer. + * + * **Default**: `1` + */ + unk_ds3_f2_4?: number + /** + * Unknown integer. + * + * **Default**: `0` + */ + unk_ds3_f2_9?: number + /** + * Unknown integer. + * + * **Default**: `0` + */ + unk_ds3_f2_10?: number + /** + * Unknown integer. + * + * **Default**: `0` + */ + unk_ds3_f2_11?: number + /** + * Unknown integer. + * + * **Default**: `0` + */ + unk_ds3_f2_12?: number + /** + * Unknown integer. + * + * **Default**: `0` + */ + unk_ds3_f2_13?: number + /** + * Unknown float. + * + * **Default**: `-1` + */ + unkDistFadeClose0?: number + /** + * Unknown float. + * + * **Default**: `-1` + */ + unkDistFadeClose1?: number + /** + * Unknown float. + * + * **Default**: `-1` + */ + unkDistFadeFar0?: number + /** + * Unknown float. + * + * **Default**: `-1` + */ + unkDistFadeFar1?: number + /** + * Unknown integer. + * + * **Default**: `0` + */ + unk_ds3_f2_20?: number + /** + * Unknown integer. + * + * **Default**: `0` + */ + unk_ds3_f2_21?: number + /** + * Unknown integer. + * + * **Default**: `0` + */ + unk_ds3_f2_22?: number + /** + * Unknown integer. + * + * **Default**: `0` + */ + unk_ds3_f2_23?: number + /** + * Unknown float. + * + * **Default**: `0` + */ + unk_ds3_f2_24?: number + /** + * Unknown float. + * + * **Default**: `1` + */ + unkDepthBlend1?: number + /** + * Unknown float. + * + * **Default**: `0` + */ + unkDepthBlend2?: number + /** + * Unknown integer. + * + * **Default**: `1` + */ + unk_ds3_f2_27?: number + /** + * Unknown integer. + * + * **Default**: `0` + */ + unk_ds3_f2_28?: number + /** + * Unknown. + * + * **Default**: `0` + */ + unk_ds3_p1_15?: ScalarValue + /** + * Unknown. + * + * **Default**: `0` + */ + unk_ds3_p1_24?: ScalarValue + /** + * Unknown. + * + * **Default**: `0` + */ + unk_ds3_p2_2?: ScalarValue + /** + * Unknown. + * + * **Default**: `[1, 1, 1, 1]` + */ + unk_ds3_p2_3?: Vector4Value + /** + * Unknown. + * + * **Default**: `[1, 1, 1, 1]` + */ + unk_ds3_p2_4?: Vector4Value + /** + * Unknown. + * + * **Default**: `[1, 1, 1, 1]` + */ + unk_ds3_p2_5?: Vector4Value + /** + * Unknown. + * + * **Default**: `0` + */ + unk_ds3_p2_6?: ScalarValue + /** + * Unknown float. + * + * **Default**: `0` + */ + unk_sdt_f2_29?: number + /** + * Unknown float. + * + * **Default**: `0` + */ + unk_sdt_f2_30?: number + /** + * Unknown integer. + * + * **Default**: `0` + */ + unk_sdt_f2_31?: number + /** + * Unknown integer. + * + * **Default**: `1` + */ + unk_sdt_f2_32?: number + /** + * Unknown integer. + * + * **Default**: `0` + */ + unk_sdt_f2_33?: number + /** + * Unknown float. + * + * **Default**: `0` + */ + unk_sdt_f2_34?: number + /** + * Unknown integer. + * + * **Default**: `-2` + */ + unk_sdt_f2_35?: number + /** + * Unknown integer. + * + * **Default**: `-2` + */ + unk_sdt_f2_36?: number + /** + * Unknown integer. + * + * **Default**: `0` + */ + unk_sdt_f2_37?: number + /** + * Unknown integer. + * + * **Default**: `1` + */ + unk_er_f1_17?: number + /** + * Unknown integer. + * + * **Default**: `1` + */ + unk_er_f1_18?: number + /** + * Unknown integer. + * + * **Default**: `0` + */ + unk_er_f1_19?: number + /** + * Unknown integer. + * + * **Default**: `0` + */ + unk_ac6_f2_38?: number +} + +/** + * Particle with a 3D model. + */ +class Model extends DataAction { + declare type: ActionType.Model + /** + * Controls the orientation mode for the particles. See {@link OrientationMode} for more information. + */ + orientation: OrientationMode + /** + * Each particle will pick a random number between this value and 1, and the width of the particle will be multiplied by this number. For example, setting this to 0.5 will make the particles randomly thinner, down to half width. Setting it to 2 will make them randomly wider, up to double width. + * + * If {@link uniformScale} is enabled, this also affects the height. + * + * See also: + * - {@link scaleVariationY} + * - {@link scaleVariationZ} + */ + scaleVariationX: number + /** + * Each particle will pick a random number between this value and 1, and the height of the particle will be multiplied by this number. For example, setting this to 0.5 will make the particles randomly shorter, down to half height. Setting it to 2 will make them randomly taller, up to double height. + * + * If {@link uniformScale} is enabled, {@link scaleVariationX} also affects the height, and this field is ignored. + * + * See also: + * - {@link scaleVariationX} + * - {@link scaleVariationZ} + */ + scaleVariationY: number + /** + * Each particle will pick a random number between this value and 1, and the depth of the particle will be multiplied by this number. For example, setting this to 0.5 will make the particles randomly shallower, down to half depth. Setting it to 2 will make them randomly deeper, up to double depth. + * + * If {@link uniformScale} is enabled, {@link scaleVariationX} also affects the depth, and this field is ignored. + * + * See also: + * - {@link scaleVariationX} + * - {@link scaleVariationY} + */ + scaleVariationZ: number + /** + * If enabled, the particle X scale-related properties and fields will control the scale in all axes, and the Y and Z counterparts will be ignored. + * + * See also: + * - {@link sizeX} + * - {@link sizeY} + * - {@link sizeZ} + * - {@link scaleVariationX} + * - {@link scaleVariationY} + * - {@link scaleVariationZ} + */ + uniformScale: boolean + /** + * To split the texture into multiple animation frames, this value must be set to the number of columns in the texture. It should equal `textureWidth / frameWidth`. + * + * See also: + * - {@link totalFrames} + */ + columns: number + /** + * To split the texture into multiple animation frames, this value must be set to the total number of frames in the texture. + * + * See also: + * - {@link columns} + */ + totalFrames: number + /** + * Controls the redness of the color of the additional bloom effect. The colors of the particle will be multiplied with this color to get the final color of the bloom effect. + * + * Note: + * - This has no effect if the "Effects Quality" setting is set to "Low". + * - This does not affect the natural bloom effect from high color values. + * + * See also: + * - {@link bloomGreen} + * - {@link bloomBlue} + * - {@link bloomStrength} + */ + bloomRed: number + /** + * Controls the greenness of the color of the additional bloom effect. The colors of the particle will be multiplied with this color to get the final color of the bloom effect. + * + * Note: + * - This has no effect if the "Effects Quality" setting is set to "Low". + * - This does not affect the natural bloom effect from high color values. + * + * See also: + * - {@link bloomRed} + * - {@link bloomBlue} + * - {@link bloomStrength} + */ + bloomGreen: number + /** + * Controls the blueness of the color of the additional bloom effect. The colors of the particle will be multiplied with this color to get the final color of the bloom effect. + * + * Note: + * - This has no effect if the "Effects Quality" setting is set to "Low". + * - This does not affect the natural bloom effect from high color values. + * + * See also: + * - {@link bloomRed} + * - {@link bloomGreen} + * - {@link bloomStrength} + */ + bloomBlue: number + /** + * Controls the strength of the additional bloom effect. + * + * Note: + * - This has no effect if the "Effects Quality" setting is set to "Low". + * - This does not affect the natural bloom effect from high color values. + * + * See also: + * - {@link bloomRed} + * - {@link bloomGreen} + * - {@link bloomBlue} + */ + bloomStrength: number + /** + * Minimum view distance. If the particle is closer than this distance from the camera, it will be hidden. Can be set to -1 to disable the limit. + * + * See also: + * - {@link maxDistance} + */ + minDistance: number + /** + * Maximum view distance. If the particle is farther away than this distance from the camera, it will be hidden. Can be set to -1 to disable the limit. + * + * See also: + * - {@link minDistance} + */ + maxDistance: number + /** + * Model ID. + * + * **Argument**: {@link PropertyArgument.Constant0 Constant 0} + */ + model: ScalarValue + /** + * The width of the particle. + * + * If {@link uniformScale} is enabled, this also controls the height and depth. + * + * **Argument**: {@link PropertyArgument.InstanceAge Instance age} + * + * See also: + * - {@link scaleVariationX} + * - {@link sizeY} + * - {@link sizeZ} + */ + sizeX: ScalarValue + /** + * The height of the particle. + * + * If {@link uniformScale} is enabled, {@link sizeX} also controls the height, and this property is ignored. + * + * **Argument**: {@link PropertyArgument.InstanceAge Instance age} + * + * See also: + * - {@link scaleVariationY} + * - {@link sizeX} + * - {@link sizeZ} + */ + sizeY: ScalarValue + /** + * The depth of the particle. + * + * If {@link uniformScale} is enabled, {@link sizeX} also controls the depth, and this property is ignored. + * + * **Argument**: {@link PropertyArgument.InstanceAge Instance age} + * + * See also: + * - {@link scaleVariationZ} + * - {@link sizeX} + * - {@link sizeY} + */ + sizeZ: ScalarValue + /** + * Rotation around the X-axis in degrees. + * + * **Argument**: {@link PropertyArgument.Constant0 Constant 0} + * + * See also: + * - {@link rotationSpeedX} + * - {@link rotationSpeedMultiplierX} + */ + rotationX: ScalarValue + /** + * Rotation around the Y-axis in degrees. + * + * **Argument**: {@link PropertyArgument.Constant0 Constant 0} + * + * See also: + * - {@link rotationSpeedY} + * - {@link rotationSpeedMultiplierY} + */ + rotationY: ScalarValue + /** + * Rotation around the Z-axis in degrees. + * + * **Argument**: {@link PropertyArgument.Constant0 Constant 0} + * + * See also: + * - {@link rotationSpeedZ} + * - {@link rotationSpeedMultiplierZ} + */ + rotationZ: ScalarValue + /** + * Rotation speed around the X-axis in degrees per second. + * + * **Argument**: {@link PropertyArgument.InstanceAge Instance age} + * + * See also: + * - {@link rotationX} + * - {@link rotationSpeedMultiplierX} + */ + rotationSpeedX: ScalarValue + /** + * Rotation speed around the Y-axis in degrees per second. + * + * **Argument**: {@link PropertyArgument.InstanceAge Instance age} + * + * See also: + * - {@link rotationY} + * - {@link rotationSpeedMultiplierY} + */ + rotationSpeedY: ScalarValue + /** + * Rotation speed around the Z-axis in degrees per second. + * + * **Argument**: {@link PropertyArgument.InstanceAge Instance age} + * + * See also: + * - {@link rotationZ} + * - {@link rotationSpeedMultiplierZ} + */ + rotationSpeedZ: ScalarValue + /** + * Multiplier for {@link rotationSpeedX}. + * + * **Argument**: {@link PropertyArgument.InstanceAge Instance age} + * + * See also: + * - {@link rotationX} + */ + rotationSpeedMultiplierX: ScalarValue + /** + * Multiplier for {@link rotationSpeedY}. + * + * **Argument**: {@link PropertyArgument.InstanceAge Instance age} + * + * See also: + * - {@link rotationY} + */ + rotationSpeedMultiplierY: ScalarValue + /** + * Multiplier for {@link rotationSpeedZ}. + * + * **Argument**: {@link PropertyArgument.InstanceAge Instance age} + * + * See also: + * - {@link rotationZ} + */ + rotationSpeedMultiplierZ: ScalarValue + /** + * Blend mode. + * + * Note that the materials used by the model may affect how the different blend modes work. Don't expect the blend modes to always work exactly like they do in other types of instances. + * + * **Argument**: {@link PropertyArgument.Constant0 Constant 0} + */ + blendMode: BlendMode | ScalarProperty + /** + * Color multiplier for the particle. + * + * **Argument**: {@link PropertyArgument.InstanceAge Instance age} + */ + color1: Vector4Value + /** + * Color multiplier for the particle. + * + * **Argument**: {@link PropertyArgument.EmissionTime Emission time} + */ + color2: Vector4Value + /** + * Color multiplier for the particle. + * + * **Argument**: {@link PropertyArgument.InstanceAge Instance age} + */ + color3: Vector4Value + /** + * The index of the frame to show from the texture atlas. Can be animated using a {@link PropertyFunction.Linear linear property} or similar. + * + * Seemingly identical to {@link frameIndexOffset}? The sum of these two properties is the actual frame index that gets used. + * + * **Argument**: {@link PropertyArgument.InstanceAge Instance age} + */ + frameIndex: ScalarValue + /** + * Seemingly identical to {@link frameIndex}? The sum of these two properties is the actual frame index that gets used. + * + * **Argument**: {@link PropertyArgument.InstanceAge Instance age} + */ + frameIndexOffset: ScalarValue + /** + * Horizontal offset for the UV coordinates of the model. + * + * If the texture is an animation sheet that is split up into multiple frames using {@link columns} and/or {@link totalFrames}, this property has no effect. + * + * **Argument**: {@link PropertyArgument.Constant0 Constant 0} + * + * See also: + * - {@link speedU} + * - {@link offsetV} + */ + offsetU: ScalarValue + /** + * Vertical offset for the UV coordinates of the model. + * + * If the texture is an animation sheet that is split up into multiple frames using {@link columns} and/or {@link totalFrames}, this property has no effect. + * + * **Argument**: {@link PropertyArgument.Constant0 Constant 0} + */ + offsetV: ScalarValue + /** + * Horiztonal scroll speed for the model's texture. + * + * If the texture is an animation sheet that is split up into multiple frames using {@link columns} and/or {@link totalFrames}, this property has no effect. + * + * **Argument**: {@link PropertyArgument.InstanceAge Instance age} + * + * See also: + * - {@link speedMultiplierU} + * - {@link offsetU} + */ + speedU: ScalarValue + /** + * Multiplier for {@link speedU}. + * + * **Argument**: {@link PropertyArgument.InstanceAge Instance age} + */ + speedMultiplierU: ScalarValue + /** + * Vertical scroll speed for the model's texture. + * + * If the texture is an animation sheet that is split up into multiple frames using {@link columns} and/or {@link totalFrames}, this property has no effect. + * + * **Argument**: {@link PropertyArgument.InstanceAge Instance age} + * + * See also: + * - {@link speedMultiplierV} + * - {@link offsetV} + */ + speedV: ScalarValue + /** + * Multiplier for {@link speedV}. + * + * **Argument**: {@link PropertyArgument.InstanceAge Instance age} + */ + speedMultiplierV: ScalarValue + /** + * Scalar multiplier for the color that does not affect the alpha. Effectively a brightness multiplier. + * + * **Argument**: {@link PropertyArgument.EffectAge Effect age} + */ + rgbMultiplier: ScalarValue + /** + * Alpha multiplier. + * + * **Argument**: {@link PropertyArgument.EffectAge Effect age} + */ + alphaMultiplier: ScalarValue + unk_ds3_f1_9: number + unk_ds3_f1_10: number + unk_ds3_f1_11: boolean + unk_ds3_f1_12: boolean + unk_ds3_f1_13: number + /** + * Animation ID. + * + * See also: + * - {@link loopAnimation} + * - {@link animationSpeed} + */ + animation: number + /** + * Unknown. Probably a boolean. Seems to just disable the {@link animation} if set to anything but 0. + */ + unk_ds3_f1_15: number + /** + * If disabled, the animation will only play once and then freeze on the last frame. If enabled, the animation will loop. + * + * See also: + * - {@link animation} + * - {@link animationSpeed} + */ + loopAnimation: boolean + /** + * Controls the speed at which the {@link animation} plays. + * + * See also: + * - {@link animation} + * - {@link loopAnimation} + */ + animationSpeed: number + unk_ds3_f1_18: number + unk_ds3_f2_0: number + unk_ds3_f2_1: number + unk_ds3_f2_2: number + unk_ds3_f2_3: number + unk_ds3_f2_4: number + unk_ds3_f2_9: number + unk_ds3_f2_10: number + unk_ds3_f2_11: number + unk_ds3_f2_12: number + unk_ds3_f2_13: number + unkDistFadeClose0: number + unkDistFadeClose1: number + unkDistFadeFar0: number + unkDistFadeFar1: number + unk_ds3_f2_20: number + unk_ds3_f2_21: number + unk_ds3_f2_22: number + unk_ds3_f2_23: number + unk_ds3_f2_24: number + unkDepthBlend1: number + unkDepthBlend2: number + unk_ds3_f2_27: number + unk_ds3_f2_28: number + unk_ds3_p1_15: ScalarValue + unk_ds3_p1_24: ScalarValue + unk_ds3_p2_2: ScalarValue + unk_ds3_p2_3: Vector4Value + unk_ds3_p2_4: Vector4Value + unk_ds3_p2_5: Vector4Value + unk_ds3_p2_6: ScalarValue + unk_sdt_f2_29: number + unk_sdt_f2_30: number + unk_sdt_f2_31: number + unk_sdt_f2_32: number + unk_sdt_f2_33: number + unk_sdt_f2_34: number + unk_sdt_f2_35: number + unk_sdt_f2_36: number + unk_sdt_f2_37: number + unk_er_f1_17: number + unk_er_f1_18: number + unk_er_f1_19: number + unk_ac6_f2_38: number + constructor(props: ModelParams = {}) { + super(ActionType.Model) + this.assign(props) + } +} + +export interface TracerParams { + /** + * Tracer orientation mode. See {@link TracerOrientationMode} for more information. + * + * **Default**: {@link TracerOrientationMode.LocalZ} + */ + orientation?: TracerOrientationMode + /** + * Normal map texture ID. + * + * This is used to control the distortion effect of the trail. + * + * **Default**: `0` + * + * See also: + * - {@link distortionIntensity} + */ + normalMap?: number + /** + * The trail is made up of multiple quads, or *segments*. This controls how many seconds to wait between new segments being created. Lower values produce a smoother trail. + * + * **Default**: `0` + */ + segmentInterval?: number + /** + * The trail is made up of multiple quads, or *segments*. This controls how long each segment should last in seconds. + * + * **Default**: `1` + */ + segmentDuration?: number + /** + * The trail is made up of multiple quads, or *segments*. This controls how many segments may exist at the same time. + * + * **Default**: `100` + */ + concurrentSegments?: number + /** + * To split the texture into multiple animation frames, this value must be set to the number of columns in the texture. It should equal `textureWidth / frameWidth`. + * + * **Default**: `1` + * + * See also: + * - {@link totalFrames} + */ + columns?: number + /** + * To split the texture into multiple animation frames, this value must be set to the total number of frames in the texture. + * + * **Default**: `1` + * + * See also: + * - {@link columns} + */ + totalFrames?: number + /** + * Controls whether or not the UV of the trail should be attached to the node or not. If it is attached, the texture will slide along the segments to follow the source wherever it moves, as if it was a flag attached to a pole. If it is not attached, the texture will stay where it was when the segment was created, like a skid mark on a road where the road is the segments and the mark is the texture, it wouldn't follow the car/node that made it. + * + * **Default**: `1` + */ + attachedUV?: boolean + /** + * Controls the redness of the color of the additional bloom effect. The colors of the particle will be multiplied with this color to get the final color of the bloom effect. + * + * Note: + * - This has no effect if the "Effects Quality" setting is set to "Low". + * - This does not affect the natural bloom effect from high color values. + * + * **Default**: `1` + * + * See also: + * - {@link bloomGreen} + * - {@link bloomBlue} + * - {@link bloomStrength} + */ + bloomRed?: number + /** + * Controls the greenness of the color of the additional bloom effect. The colors of the particle will be multiplied with this color to get the final color of the bloom effect. + * + * Note: + * - This has no effect if the "Effects Quality" setting is set to "Low". + * - This does not affect the natural bloom effect from high color values. + * + * **Default**: `1` + * + * See also: + * - {@link bloomRed} + * - {@link bloomBlue} + * - {@link bloomStrength} + */ + bloomGreen?: number + /** + * Controls the blueness of the color of the additional bloom effect. The colors of the particle will be multiplied with this color to get the final color of the bloom effect. + * + * Note: + * - This has no effect if the "Effects Quality" setting is set to "Low". + * - This does not affect the natural bloom effect from high color values. + * + * **Default**: `1` + * + * See also: + * - {@link bloomRed} + * - {@link bloomGreen} + * - {@link bloomStrength} + */ + bloomBlue?: number + /** + * Controls the strength of the additional bloom effect. + * + * Note: + * - This has no effect if the "Effects Quality" setting is set to "Low". + * - This does not affect the natural bloom effect from high color values. + * + * **Default**: `0` + * + * See also: + * - {@link bloomRed} + * - {@link bloomGreen} + * - {@link bloomBlue} + */ + bloomStrength?: number + /** + * Minimum view distance. If the trail is closer than this distance from the camera, it will be hidden. Can be set to -1 to disable the limit. + * + * **Default**: `-1` + * + * See also: + * - {@link maxDistance} + */ + minDistance?: number + /** + * Maximum view distance. If the trail is farther away than this distance from the camera, it will be hidden. Can be set to -1 to disable the limit. + * + * **Default**: `-1` + * + * See also: + * - {@link minDistance} + */ + maxDistance?: number + /** + * Controls how dark shaded parts of the trail are. + * + * **Default**: `0` + */ + shadowDarkness?: number + /** + * Specular texture ID. + * + * **Default**: `0` + * + * See also: + * - {@link lighting} + * - {@link glossiness} + * - {@link specularity} + */ + specular?: number + /** + * Controls how sharp the specular highlights are. + * + * **Default**: `0.25` + * + * See also: + * - {@link lighting} + * - {@link specular} + * - {@link specularity} + */ + glossiness?: number + /** + * Controls how the trail is lit. See {@link LightingMode} for more information. + * + * **Default**: {@link LightingMode.Unlit} + */ + lighting?: LightingMode + /** + * Controls how bright the specular highlights are. + * + * **Default**: `0.5` + * + * See also: + * - {@link lighting} + * - {@link specular} + * - {@link glossiness} + */ + specularity?: number + /** + * Texture ID. + * + * **Default**: `1` + * + * **Argument**: {@link PropertyArgument.Constant0 Constant 0} + */ + texture?: ScalarValue + /** + * Blend mode. + * + * **Default**: {@link BlendMode.Normal} + * + * **Argument**: {@link PropertyArgument.Constant0 Constant 0} + */ + blendMode?: BlendMode | ScalarProperty + /** + * The width of the trail. + * + * **Default**: `1` + * + * **Argument**: {@link PropertyArgument.InstanceAge Instance age} + */ + width?: ScalarValue + /** + * Multiplier for {@link width}. + * + * **Default**: `1` + * + * **Argument**: {@link PropertyArgument.InstanceAge Instance age} + */ + widthMultiplier?: ScalarValue + /** + * Color multiplier. + * + * **Default**: `[1, 1, 1, 1]` + * + * **Argument**: {@link PropertyArgument.InstanceAge Instance age} + */ + color1?: Vector4Value + /** + * Color multiplier. + * + * **Default**: `[1, 1, 1, 1]` + * + * **Argument**: {@link PropertyArgument.EmissionTime Emission time} + */ + color2?: Vector4Value + /** + * Color multiplier. + * + * **Default**: `[1, 1, 1, 1]` + * + * **Argument**: {@link PropertyArgument.InstanceAge Instance age} + */ + color3?: Vector4Value + /** + * Parts of the particle with less opacity than this threshold will be invisible. The range is 0-255. + * + * **Default**: `0` + * + * **Argument**: {@link PropertyArgument.InstanceAge Instance age} + */ + alphaThreshold?: ScalarValue + /** + * The index of the frame to show from the texture atlas. Can be animated using a {@link PropertyFunction.Linear linear property} or similar. + * + * Seemingly identical to {@link frameIndexOffset}? The sum of these two properties is the actual frame index that gets used. + * + * **Default**: `0` + * + * **Argument**: {@link PropertyArgument.InstanceAge Instance age} + */ + frameIndex?: ScalarValue + /** + * Seemingly identical to {@link frameIndex}? The sum of these two properties is the actual frame index that gets used. + * + * **Default**: `0` + * + * **Argument**: {@link PropertyArgument.InstanceAge Instance age} + */ + frameIndexOffset?: ScalarValue + /** + * Controls how much of the texture's width is used per segment. If {@link attachedUV} is enabled, this instead controls how much of the texture's width to use for the entire trail. + * + * **Default**: `0.1` + * + * **Argument**: {@link PropertyArgument.InstanceAge Instance age} + */ + textureFraction?: ScalarValue + /** + * Controls how fast the UV coordinates should move horizontally. + * + * **Default**: `0` + * + * **Argument**: {@link PropertyArgument.InstanceAge Instance age} + */ + speedU?: ScalarValue + /** + * Controls how much the UV coordinates should be randomly offset by per segment. + * + * **Default**: `0` + * + * **Argument**: {@link PropertyArgument.InstanceAge Instance age} + */ + varianceV?: ScalarValue + /** + * Scalar multiplier for the color that does not affect the alpha. Effectively a brightness multiplier. + * + * **Default**: `1` + * + * **Argument**: {@link PropertyArgument.EffectAge Effect age} + */ + rgbMultiplier?: ScalarValue + /** + * Alpha multiplier. + * + * **Default**: `1` + * + * **Argument**: {@link PropertyArgument.EffectAge Effect age} + */ + alphaMultiplier?: ScalarValue + /** + * Unknown integer. + * + * **Default**: `0` + */ + unk_ds3_f1_7?: number + /** + * Unknown integer. + * + * **Default**: `0` + */ + unk_ds3_f1_8?: number + /** + * Unknown float. + * + * **Default**: `0` + */ + unk_ds3_f1_9?: number + /** + * Unknown integer. + * + * **Default**: `-1` + */ + unk_ds3_f1_13?: number + /** + * Unknown integer. + * + * **Default**: `-1` + */ + unk_ds3_f1_14?: number + /** + * Unknown integer. + * + * **Default**: `0` + */ + unk_ds3_f1_15?: number + /** + * Unknown integer. + * + * **Default**: `0` + */ + unk_ds3_f2_0?: number + /** + * Unknown boolean. + * + * **Default**: `0` + */ + unk_ds3_f2_1?: boolean + /** + * Unknown integer. + * + * **Default**: `8` + */ + unk_ds3_f2_2?: number + /** + * Unknown integer. + * + * **Default**: `0` + */ + unk_ds3_f2_3?: number + /** + * Unknown integer. + * + * **Default**: `1` + */ + unk_ds3_f2_4?: number + /** + * Unknown integer. + * + * **Default**: `0` + */ + unk_ds3_f2_9?: number + /** + * Unknown integer. + * + * **Default**: `0` + */ + unk_ds3_f2_10?: number + /** + * Unknown integer. + * + * **Default**: `0` + */ + unk_ds3_f2_11?: number + /** + * Unknown integer. + * + * **Default**: `0` + */ + unk_ds3_f2_12?: number + /** + * Unknown integer. + * + * **Default**: `0` + */ + unk_ds3_f2_13?: number + /** + * Unknown float. + * + * **Default**: `-1` + */ + unkDistFadeClose0?: number + /** + * Unknown float. + * + * **Default**: `-1` + */ + unkDistFadeClose1?: number + /** + * Unknown float. + * + * **Default**: `-1` + */ + unkDistFadeFar0?: number + /** + * Unknown float. + * + * **Default**: `-1` + */ + unkDistFadeFar1?: number + /** + * Unknown integer. + * + * **Default**: `0` + */ + unk_ds3_f2_20?: number + /** + * Unknown integer. + * + * **Default**: `0` + */ + unk_ds3_f2_21?: number + /** + * Unknown integer. + * + * **Default**: `0` + */ + unk_ds3_f2_22?: number + /** + * Unknown integer. + * + * **Default**: `0` + */ + unk_ds3_f2_23?: number + /** + * Unknown integer. + * + * **Default**: `0` + */ + unk_ds3_f2_24?: number + /** + * Unknown float. + * + * **Default**: `1` + */ + unkDepthBlend1?: number + /** + * Unknown float. + * + * **Default**: `0` + */ + unkDepthBlend2?: number + /** + * Unknown integer. + * + * **Default**: `1` + */ + unk_ds3_f2_27?: number + /** + * Unknown integer. + * + * **Default**: `0` + */ + unk_ds3_f2_28?: number + /** + * Unknown float. + * + * **Default**: `5` + */ + unk_ds3_f2_29?: number + /** + * Unknown. + * + * **Default**: `0` + */ + unk_ds3_p1_2?: ScalarValue + /** + * Unknown. + * + * **Default**: `0` + */ + unk_ds3_p1_3?: ScalarValue + /** + * Unknown. + * + * **Default**: `-1` + */ + unk_ds3_p1_13?: ScalarValue + /** + * Controls the intensity of the distortion effect. At 0, there is no distortion at all. + * + * **Default**: `0` + * + * **Argument**: {@link PropertyArgument.EffectAge Effect age} + * + * See also: + * - {@link normalMap} + */ + distortionIntensity?: ScalarValue + /** + * Unknown. + * + * **Default**: `[1, 1, 1, 1]` + */ + unk_ds3_p2_3?: Vector4Value + /** + * Unknown. + * + * **Default**: `[1, 1, 1, 1]` + */ + unk_ds3_p2_4?: Vector4Value + /** + * Unknown. + * + * **Default**: `[1, 1, 1, 1]` + */ + unk_ds3_p2_5?: Vector4Value + /** + * Unknown. + * + * **Default**: `0` + */ + unk_ds3_p2_6?: ScalarValue + /** + * Unknown integer. + * + * **Default**: `0` + */ + unk_sdt_f2_31?: number + /** + * Unknown integer. + * + * **Default**: `1` + */ + unk_sdt_f2_32?: number + /** + * Unknown integer. + * + * **Default**: `-2` + */ + unk_sdt_f2_36?: number + /** + * Unknown integer. + * + * **Default**: `0` + */ + unk_sdt_f2_37?: number + /** + * Unknown integer. + * + * **Default**: `1` + */ + unk_er_f1_14?: number + /** + * Unknown integer. + * + * **Default**: `1` + */ + unk_er_f1_15?: number + /** + * Unknown integer. + * + * **Default**: `0` + */ + unk_er_f1_16?: number + /** + * Unknown integer. + * + * **Default**: `0` + */ + unk_er_f2_39?: number +} + +/** + * Creates a trail behind moving effects. + */ +class Tracer extends DataAction { + declare type: ActionType.Tracer + /** + * Tracer orientation mode. See {@link TracerOrientationMode} for more information. + */ + orientation: TracerOrientationMode + /** + * Normal map texture ID. + * + * This is used to control the distortion effect of the trail. + * + * See also: + * - {@link distortionIntensity} + */ + normalMap: number + /** + * The trail is made up of multiple quads, or *segments*. This controls how many seconds to wait between new segments being created. Lower values produce a smoother trail. + */ + segmentInterval: number + /** + * The trail is made up of multiple quads, or *segments*. This controls how long each segment should last in seconds. + */ + segmentDuration: number + /** + * The trail is made up of multiple quads, or *segments*. This controls how many segments may exist at the same time. + */ + concurrentSegments: number + /** + * To split the texture into multiple animation frames, this value must be set to the number of columns in the texture. It should equal `textureWidth / frameWidth`. + * + * See also: + * - {@link totalFrames} + */ + columns: number + /** + * To split the texture into multiple animation frames, this value must be set to the total number of frames in the texture. + * + * See also: + * - {@link columns} + */ + totalFrames: number + /** + * Controls whether or not the UV of the trail should be attached to the node or not. If it is attached, the texture will slide along the segments to follow the source wherever it moves, as if it was a flag attached to a pole. If it is not attached, the texture will stay where it was when the segment was created, like a skid mark on a road where the road is the segments and the mark is the texture, it wouldn't follow the car/node that made it. + */ + attachedUV: boolean + /** + * Controls the redness of the color of the additional bloom effect. The colors of the particle will be multiplied with this color to get the final color of the bloom effect. + * + * Note: + * - This has no effect if the "Effects Quality" setting is set to "Low". + * - This does not affect the natural bloom effect from high color values. + * + * See also: + * - {@link bloomGreen} + * - {@link bloomBlue} + * - {@link bloomStrength} + */ + bloomRed: number + /** + * Controls the greenness of the color of the additional bloom effect. The colors of the particle will be multiplied with this color to get the final color of the bloom effect. + * + * Note: + * - This has no effect if the "Effects Quality" setting is set to "Low". + * - This does not affect the natural bloom effect from high color values. + * + * See also: + * - {@link bloomRed} + * - {@link bloomBlue} + * - {@link bloomStrength} + */ + bloomGreen: number + /** + * Controls the blueness of the color of the additional bloom effect. The colors of the particle will be multiplied with this color to get the final color of the bloom effect. + * + * Note: + * - This has no effect if the "Effects Quality" setting is set to "Low". + * - This does not affect the natural bloom effect from high color values. + * + * See also: + * - {@link bloomRed} + * - {@link bloomGreen} + * - {@link bloomStrength} + */ + bloomBlue: number + /** + * Controls the strength of the additional bloom effect. + * + * Note: + * - This has no effect if the "Effects Quality" setting is set to "Low". + * - This does not affect the natural bloom effect from high color values. + * + * See also: + * - {@link bloomRed} + * - {@link bloomGreen} + * - {@link bloomBlue} + */ + bloomStrength: number + /** + * Minimum view distance. If the trail is closer than this distance from the camera, it will be hidden. Can be set to -1 to disable the limit. + * + * See also: + * - {@link maxDistance} + */ + minDistance: number + /** + * Maximum view distance. If the trail is farther away than this distance from the camera, it will be hidden. Can be set to -1 to disable the limit. + * + * See also: + * - {@link minDistance} + */ + maxDistance: number + /** + * Controls how dark shaded parts of the trail are. + */ + shadowDarkness: number + /** + * Specular texture ID. + * + * See also: + * - {@link lighting} + * - {@link glossiness} + * - {@link specularity} + */ + specular: number + /** + * Controls how sharp the specular highlights are. + * + * See also: + * - {@link lighting} + * - {@link specular} + * - {@link specularity} + */ + glossiness: number + /** + * Controls how the trail is lit. See {@link LightingMode} for more information. + */ + lighting: LightingMode + /** + * Controls how bright the specular highlights are. + * + * See also: + * - {@link lighting} + * - {@link specular} + * - {@link glossiness} + */ + specularity: number + /** + * Texture ID. + * + * **Argument**: {@link PropertyArgument.Constant0 Constant 0} + */ + texture: ScalarValue + /** + * Blend mode. + * + * **Argument**: {@link PropertyArgument.Constant0 Constant 0} + */ + blendMode: BlendMode | ScalarProperty + /** + * The width of the trail. + * + * **Argument**: {@link PropertyArgument.InstanceAge Instance age} + */ + width: ScalarValue + /** + * Multiplier for {@link width}. + * + * **Argument**: {@link PropertyArgument.InstanceAge Instance age} + */ + widthMultiplier: ScalarValue + /** + * Color multiplier. + * + * **Argument**: {@link PropertyArgument.InstanceAge Instance age} + */ + color1: Vector4Value + /** + * Color multiplier. + * + * **Argument**: {@link PropertyArgument.EmissionTime Emission time} + */ + color2: Vector4Value + /** + * Color multiplier. + * + * **Argument**: {@link PropertyArgument.InstanceAge Instance age} + */ + color3: Vector4Value + /** + * Parts of the particle with less opacity than this threshold will be invisible. The range is 0-255. + * + * **Argument**: {@link PropertyArgument.InstanceAge Instance age} + */ + alphaThreshold: ScalarValue + /** + * The index of the frame to show from the texture atlas. Can be animated using a {@link PropertyFunction.Linear linear property} or similar. + * + * Seemingly identical to {@link frameIndexOffset}? The sum of these two properties is the actual frame index that gets used. + * + * **Argument**: {@link PropertyArgument.InstanceAge Instance age} + */ + frameIndex: ScalarValue + /** + * Seemingly identical to {@link frameIndex}? The sum of these two properties is the actual frame index that gets used. + * + * **Argument**: {@link PropertyArgument.InstanceAge Instance age} + */ + frameIndexOffset: ScalarValue + /** + * Controls how much of the texture's width is used per segment. If {@link attachedUV} is enabled, this instead controls how much of the texture's width to use for the entire trail. + * + * **Argument**: {@link PropertyArgument.InstanceAge Instance age} + */ + textureFraction: ScalarValue + /** + * Controls how fast the UV coordinates should move horizontally. + * + * **Argument**: {@link PropertyArgument.InstanceAge Instance age} + */ + speedU: ScalarValue + /** + * Controls how much the UV coordinates should be randomly offset by per segment. + * + * **Argument**: {@link PropertyArgument.InstanceAge Instance age} + */ + varianceV: ScalarValue + /** + * Scalar multiplier for the color that does not affect the alpha. Effectively a brightness multiplier. + * + * **Argument**: {@link PropertyArgument.EffectAge Effect age} + */ + rgbMultiplier: ScalarValue + /** + * Alpha multiplier. + * + * **Argument**: {@link PropertyArgument.EffectAge Effect age} + */ + alphaMultiplier: ScalarValue + unk_ds3_f1_7: number + unk_ds3_f1_8: number + unk_ds3_f1_9: number + unk_ds3_f1_13: number + unk_ds3_f1_14: number + unk_ds3_f1_15: number + unk_ds3_f2_0: number + unk_ds3_f2_1: boolean + unk_ds3_f2_2: number + unk_ds3_f2_3: number + unk_ds3_f2_4: number + unk_ds3_f2_9: number + unk_ds3_f2_10: number + unk_ds3_f2_11: number + unk_ds3_f2_12: number + unk_ds3_f2_13: number + unkDistFadeClose0: number + unkDistFadeClose1: number + unkDistFadeFar0: number + unkDistFadeFar1: number + unk_ds3_f2_20: number + unk_ds3_f2_21: number + unk_ds3_f2_22: number + unk_ds3_f2_23: number + unk_ds3_f2_24: number + unkDepthBlend1: number + unkDepthBlend2: number + unk_ds3_f2_27: number + unk_ds3_f2_28: number + unk_ds3_f2_29: number + unk_ds3_p1_2: ScalarValue + unk_ds3_p1_3: ScalarValue + unk_ds3_p1_13: ScalarValue + /** + * Controls the intensity of the distortion effect. At 0, there is no distortion at all. + * + * **Argument**: {@link PropertyArgument.EffectAge Effect age} + * + * See also: + * - {@link normalMap} + */ + distortionIntensity: ScalarValue + unk_ds3_p2_3: Vector4Value + unk_ds3_p2_4: Vector4Value + unk_ds3_p2_5: Vector4Value + unk_ds3_p2_6: ScalarValue + unk_sdt_f2_31: number + unk_sdt_f2_32: number + unk_sdt_f2_36: number + unk_sdt_f2_37: number + unk_er_f1_14: number + unk_er_f1_15: number + unk_er_f1_16: number + unk_er_f2_39: number + constructor(props: TracerParams = {}) { + super(ActionType.Tracer) + this.assign(props) + } +} + +export interface DistortionParams { + /** + * Controls what type of distortion to apply. See {@link DistortionMode} for more details. + * + * **Default**: {@link DistortionMode.NormalMap} + */ + mode?: DistortionMode + /** + * Controls the shape of the particle. See {@link DistortionShape} for more information. + * + * **Default**: {@link DistortionShape.Rectangle} + */ + shape?: DistortionShape + /** + * Controls the orientation mode for the particles. See {@link OrientationMode} for more information. + * + * **Default**: {@link OrientationMode.CameraPlane} + */ + orientation?: OrientationMode + /** + * Texture ID. + * + * This texture seems to completely hide the distortion effect. It's probably best to just leave it at 0 unless you are trying to figure out how to use it properly. + * + * **Default**: `0` + */ + texture?: number + /** + * Normal map texture ID. + * + * Only used if the distortion {@link mode} is set to something that uses it. + * + * **Default**: `0` + */ + normalMap?: number + /** + * Mask texture ID. This texture is used to control the color and opacity of the particle. + * + * **Default**: `0` + */ + mask?: number + /** + * Each particle will pick a random number between this value and 1, and the width of the particle will be multiplied by this number. For example, setting this to 0.5 will make the particles randomly thinner, down to half width. Setting it to 2 will make them randomly wider, up to double width. + * + * If {@link uniformScale} is enabled, this also affects the height. + * + * **Default**: `1` + * + * See also: + * - {@link scaleVariationY} + * - {@link scaleVariationZ} + */ + scaleVariationX?: number + /** + * Each particle will pick a random number between this value and 1, and the height of the particle will be multiplied by this number. For example, setting this to 0.5 will make the particles randomly shorter, down to half height. Setting it to 2 will make them randomly taller, up to double height. + * + * If {@link uniformScale} is enabled, {@link scaleVariationX} also affects the height, and this field is ignored. + * + * **Default**: `1` + * + * See also: + * - {@link scaleVariationX} + * - {@link scaleVariationZ} + */ + scaleVariationY?: number + /** + * Each particle will pick a random number between this value and 1, and the depth of the particle will be multiplied by this number. For example, setting this to 0.5 will make the particles randomly shallower, down to half depth. Setting it to 2 will make them randomly deeper, up to double depth. + * + * If {@link uniformScale} is enabled, {@link scaleVariationX} also affects the depth, and this field is ignored. + * + * **Default**: `1` + * + * See also: + * - {@link scaleVariationX} + * - {@link scaleVariationY} + */ + scaleVariationZ?: number + /** + * If enabled, the particle X scale-related properties and fields will control the scale in all axes, and the Y and Z counterparts will be ignored. + * + * **Default**: `false` + * + * See also: + * - {@link sizeX} + * - {@link sizeY} + * - {@link sizeZ} + * - {@link scaleVariationX} + * - {@link scaleVariationY} + * - {@link scaleVariationZ} + */ + uniformScale?: boolean + /** + * Controls the redness of the color of the additional bloom effect. The colors of the particle will be multiplied with this color to get the final color of the bloom effect. + * + * Note: + * - This has no effect if the "Effects Quality" setting is set to "Low". + * - This does not affect the natural bloom effect from high color values. + * + * **Default**: `1` + * + * See also: + * - {@link bloomGreen} + * - {@link bloomBlue} + * - {@link bloomStrength} + */ + bloomRed?: number + /** + * Controls the greenness of the color of the additional bloom effect. The colors of the particle will be multiplied with this color to get the final color of the bloom effect. + * + * Note: + * - This has no effect if the "Effects Quality" setting is set to "Low". + * - This does not affect the natural bloom effect from high color values. + * + * **Default**: `1` + * + * See also: + * - {@link bloomRed} + * - {@link bloomBlue} + * - {@link bloomStrength} + */ + bloomGreen?: number + /** + * Controls the blueness of the color of the additional bloom effect. The colors of the particle will be multiplied with this color to get the final color of the bloom effect. + * + * Note: + * - This has no effect if the "Effects Quality" setting is set to "Low". + * - This does not affect the natural bloom effect from high color values. + * + * **Default**: `1` + * + * See also: + * - {@link bloomRed} + * - {@link bloomGreen} + * - {@link bloomStrength} + */ + bloomBlue?: number + /** + * Controls the strength of the additional bloom effect. + * + * Note: + * - This has no effect if the "Effects Quality" setting is set to "Low". + * - This does not affect the natural bloom effect from high color values. + * + * **Default**: `0` + * + * See also: + * - {@link bloomRed} + * - {@link bloomGreen} + * - {@link bloomBlue} + */ + bloomStrength?: number + /** + * Minimum view distance. If the particle is closer than this distance from the camera, it will be hidden. Can be set to -1 to disable the limit. + * + * **Default**: `-1` + * + * See also: + * - {@link maxDistance} + */ + minDistance?: number + /** + * Maximum view distance. If the particle is farther away than this distance from the camera, it will be hidden. Can be set to -1 to disable the limit. + * + * **Default**: `-1` + * + * See also: + * - {@link minDistance} + */ + maxDistance?: number + /** + * Blend mode. + * + * **Default**: {@link BlendMode.Normal} + * + * **Argument**: {@link PropertyArgument.Constant0 Constant 0} + */ + blendMode?: BlendMode | ScalarProperty + /** + * X position offset. + * + * **Default**: `0` + * + * **Argument**: {@link PropertyArgument.InstanceAge Instance age} + */ + offsetX?: ScalarValue + /** + * Y position offset. + * + * **Default**: `0` + * + * **Argument**: {@link PropertyArgument.InstanceAge Instance age} + */ + offsetY?: ScalarValue + /** + * Z position offset. + * + * **Default**: `0` + * + * **Argument**: {@link PropertyArgument.InstanceAge Instance age} + */ + offsetZ?: ScalarValue + /** + * The width of the particle. + * + * If {@link uniformScale} is enabled, this also controls the height and depth. + * + * **Default**: `1` + * + * **Argument**: {@link PropertyArgument.InstanceAge Instance age} + * + * See also: + * - {@link scaleVariationX} + * - {@link sizeY} + * - {@link sizeZ} + */ + sizeX?: ScalarValue + /** + * The height of the particle. + * + * If {@link uniformScale} is enabled, {@link sizeX} also controls the height, and this property is ignored. + * + * **Default**: `1` + * + * **Argument**: {@link PropertyArgument.InstanceAge Instance age} + * + * See also: + * - {@link scaleVariationY} + * - {@link sizeX} + * - {@link sizeZ} + */ + sizeY?: ScalarValue + /** + * The depth of the particle. + * + * If {@link uniformScale} is enabled, {@link sizeX} also controls the depth, and this property is ignored. + * + * If the distortion {@link shape} is set to {@link DistortionShape.Rectangle Rectangle}, this property won't have any effect since the rectangle is 2-dimensional. + * + * **Default**: `1` + * + * **Argument**: {@link PropertyArgument.InstanceAge Instance age} + * + * See also: + * - {@link scaleVariationZ} + * - {@link sizeX} + * - {@link sizeY} + */ + sizeZ?: ScalarValue + /** + * Color multiplier. + * + * **Default**: `[1, 1, 1, 1]` + * + * **Argument**: {@link PropertyArgument.InstanceAge Instance age} + */ + color?: Vector4Value + /** + * Controls the intensity of the distortion effect. At 0, there is no distortion at all. + * + * **Default**: `1` + * + * **Argument**: {@link PropertyArgument.InstanceAge Instance age} + */ + intensity?: ScalarValue + /** + * Controls the speed of the stirring effect in radians per second. Requires {@link mode} to be set to {@link DistortionMode.Stir}. + * + * **Default**: `1` + * + * **Argument**: {@link PropertyArgument.InstanceAge Instance age} + */ + stirSpeed?: ScalarValue + /** + * The distortion effect is only applied to an ellipse inside the particle. This property controls how large this ellipse is. At 1, it inscribes the particle's rectangle. At values greater than 1, it is the same size as 1, but there might be strange artifacts around the edges of the distortion. + * + * **Default**: `1` + * + * **Argument**: {@link PropertyArgument.InstanceAge Instance age} + */ + radius?: ScalarValue + /** + * Horizontal offset for the {@link normalMap normal map}. + * + * **Default**: `0` + * + * **Argument**: {@link PropertyArgument.Constant0 Constant 0} + */ + normalMapOffsetU?: ScalarValue + /** + * Vertical offset for the {@link normalMap normal map}. + * + * **Default**: `0` + * + * **Argument**: {@link PropertyArgument.Constant0 Constant 0} + */ + normalMapOffsetV?: ScalarValue + /** + * Horizontal offset speed for the {@link normalMap normal map}. + * + * **Default**: `0` + * + * **Argument**: {@link PropertyArgument.InstanceAge Instance age} + */ + normalMapSpeedU?: ScalarValue + /** + * Vertical offset speed for the {@link normalMap normal map}. + * + * **Default**: `0` + * + * **Argument**: {@link PropertyArgument.InstanceAge Instance age} + */ + normalMapSpeedV?: ScalarValue + /** + * Scalar multiplier for the color that does not affect the alpha. Effectively a brightness multiplier. + * + * **Default**: `1` + * + * **Argument**: {@link PropertyArgument.EffectAge Effect age} + */ + rgbMultiplier?: ScalarValue + /** + * Alpha multiplier. + * + * **Default**: `1` + * + * **Argument**: {@link PropertyArgument.EffectAge Effect age} + */ + alphaMultiplier?: ScalarValue + /** + * Unknown integer. + * + * **Default**: `-2` + */ + unk_ds3_f1_11?: number + /** + * Unknown integer. + * + * **Default**: `0` + */ + unk_ds3_f1_12?: number + /** + * Unknown integer. + * + * **Default**: `0` + */ + unk_ds3_f2_0?: number + /** + * Unknown integer. + * + * **Default**: `0` + */ + unk_ds3_f2_1?: number + /** + * Unknown integer. + * + * **Default**: `8` + */ + unk_ds3_f2_2?: number + /** + * Unknown float. + * + * **Default**: `1` + */ + unk_ds3_f2_3?: number + /** + * Unknown integer. + * + * **Default**: `0` + */ + unk_ds3_f2_4?: number + /** + * Unknown integer. + * + * **Default**: `0` + */ + unk_ds3_f2_9?: number + /** + * Unknown integer. + * + * **Default**: `0` + */ + unk_ds3_f2_10?: number + /** + * Unknown integer. + * + * **Default**: `0` + */ + unk_ds3_f2_11?: number + /** + * Unknown integer. + * + * **Default**: `0` + */ + unk_ds3_f2_12?: number + /** + * Unknown integer. + * + * **Default**: `0` + */ + unk_ds3_f2_13?: number + /** + * Unknown float. + * + * **Default**: `-1` + */ + unkDistFadeClose0?: number + /** + * Unknown float. + * + * **Default**: `-1` + */ + unkDistFadeClose1?: number + /** + * Unknown float. + * + * **Default**: `-1` + */ + unkDistFadeFar0?: number + /** + * Unknown float. + * + * **Default**: `-1` + */ + unkDistFadeFar1?: number + /** + * Unknown integer. + * + * **Default**: `1` + */ + unk_ds3_f2_20?: number + /** + * Unknown integer. + * + * **Default**: `0` + */ + unk_ds3_f2_21?: number + /** + * Unknown integer. + * + * **Default**: `0` + */ + unk_ds3_f2_22?: number + /** + * Unknown integer. + * + * **Default**: `0` + */ + unk_ds3_f2_23?: number + /** + * Unknown integer. + * + * **Default**: `0` + */ + unk_ds3_f2_24?: number + /** + * Unknown float. + * + * **Default**: `1` + */ + unkDepthBlend1?: number + /** + * Unknown float. + * + * **Default**: `0` + */ + unkDepthBlend2?: number + /** + * Unknown integer. + * + * **Default**: `1` + */ + unk_ds3_f2_27?: number + /** + * Unknown integer. + * + * **Default**: `0` + */ + unk_ds3_f2_28?: number + /** + * Unknown integer. + * + * **Default**: `0` + */ + unk_ds3_f2_29?: number + /** + * Unknown. + * + * **Default**: `[1, 1, 1, 1]` + */ + unk_ds3_p1_7?: Vector4Value + /** + * Unknown. + * + * **Default**: `0` + */ + unk_ds3_p1_9?: ScalarValue + /** + * Unknown. + * + * **Default**: `0` + */ + unk_ds3_p2_2?: ScalarValue + /** + * Unknown. + * + * **Default**: `[1, 1, 1, 1]` + */ + unk_ds3_p2_3?: Vector4Value + /** + * Unknown. + * + * **Default**: `[1, 1, 1, 1]` + */ + unk_ds3_p2_4?: Vector4Value + /** + * Unknown. + * + * **Default**: `[1, 1, 1, 1]` + */ + unk_ds3_p2_5?: Vector4Value + /** + * Unknown. + * + * **Default**: `0` + */ + unk_ds3_p2_6?: ScalarValue + /** + * Unknown integer. + * + * **Default**: `0` + */ + unk_sdt_f2_30?: number + /** + * Unknown integer. + * + * **Default**: `0` + */ + unk_sdt_f2_31?: number + /** + * Unknown integer. + * + * **Default**: `0` + */ + unk_sdt_f2_32?: number + /** + * Unknown integer. + * + * **Default**: `0` + */ + unk_sdt_f2_33?: number + /** + * Unknown integer. + * + * **Default**: `0` + */ + unk_sdt_f2_34?: number + /** + * Unknown integer. + * + * **Default**: `-1` + */ + unk_sdt_f2_35?: number + /** + * Unknown integer. + * + * **Default**: `-2` + */ + unk_sdt_f2_36?: number + /** + * Unknown integer. + * + * **Default**: `0` + */ + unk_sdt_f2_37?: number + /** + * Unknown integer. + * + * **Default**: `0` + */ + unk_sdt_f2_38?: number + /** + * Unknown integer. + * + * **Default**: `1` + */ + unk_er_f1_12?: number + /** + * Unknown integer. + * + * **Default**: `1` + */ + unk_er_f1_13?: number + /** + * Unknown. + * + * **Default**: `1` + */ + unk_er_p2_7?: ScalarValue + /** + * Unknown. + * + * **Default**: `1` + */ + unk_er_p2_8?: ScalarValue +} + +/** + * A particle that distorts anything seen through it. + * + * Note: This particle is not visible if the "Effects" setting is set to "Low". + */ +class Distortion extends DataAction { + declare type: ActionType.Distortion + /** + * Controls what type of distortion to apply. See {@link DistortionMode} for more details. + */ + mode: DistortionMode + /** + * Controls the shape of the particle. See {@link DistortionShape} for more information. + */ + shape: DistortionShape + /** + * Controls the orientation mode for the particles. See {@link OrientationMode} for more information. + */ + orientation: OrientationMode + /** + * Texture ID. + * + * This texture seems to completely hide the distortion effect. It's probably best to just leave it at 0 unless you are trying to figure out how to use it properly. + */ + texture: number + /** + * Normal map texture ID. + * + * Only used if the distortion {@link mode} is set to something that uses it. + */ + normalMap: number + /** + * Mask texture ID. This texture is used to control the color and opacity of the particle. + */ + mask: number + /** + * Each particle will pick a random number between this value and 1, and the width of the particle will be multiplied by this number. For example, setting this to 0.5 will make the particles randomly thinner, down to half width. Setting it to 2 will make them randomly wider, up to double width. + * + * If {@link uniformScale} is enabled, this also affects the height. + * + * See also: + * - {@link scaleVariationY} + * - {@link scaleVariationZ} + */ + scaleVariationX: number + /** + * Each particle will pick a random number between this value and 1, and the height of the particle will be multiplied by this number. For example, setting this to 0.5 will make the particles randomly shorter, down to half height. Setting it to 2 will make them randomly taller, up to double height. + * + * If {@link uniformScale} is enabled, {@link scaleVariationX} also affects the height, and this field is ignored. + * + * See also: + * - {@link scaleVariationX} + * - {@link scaleVariationZ} + */ + scaleVariationY: number + /** + * Each particle will pick a random number between this value and 1, and the depth of the particle will be multiplied by this number. For example, setting this to 0.5 will make the particles randomly shallower, down to half depth. Setting it to 2 will make them randomly deeper, up to double depth. + * + * If {@link uniformScale} is enabled, {@link scaleVariationX} also affects the depth, and this field is ignored. + * + * See also: + * - {@link scaleVariationX} + * - {@link scaleVariationY} + */ + scaleVariationZ: number + /** + * If enabled, the particle X scale-related properties and fields will control the scale in all axes, and the Y and Z counterparts will be ignored. + * + * See also: + * - {@link sizeX} + * - {@link sizeY} + * - {@link sizeZ} + * - {@link scaleVariationX} + * - {@link scaleVariationY} + * - {@link scaleVariationZ} + */ + uniformScale: boolean + /** + * Controls the redness of the color of the additional bloom effect. The colors of the particle will be multiplied with this color to get the final color of the bloom effect. + * + * Note: + * - This has no effect if the "Effects Quality" setting is set to "Low". + * - This does not affect the natural bloom effect from high color values. + * + * See also: + * - {@link bloomGreen} + * - {@link bloomBlue} + * - {@link bloomStrength} + */ + bloomRed: number + /** + * Controls the greenness of the color of the additional bloom effect. The colors of the particle will be multiplied with this color to get the final color of the bloom effect. + * + * Note: + * - This has no effect if the "Effects Quality" setting is set to "Low". + * - This does not affect the natural bloom effect from high color values. + * + * See also: + * - {@link bloomRed} + * - {@link bloomBlue} + * - {@link bloomStrength} + */ + bloomGreen: number + /** + * Controls the blueness of the color of the additional bloom effect. The colors of the particle will be multiplied with this color to get the final color of the bloom effect. + * + * Note: + * - This has no effect if the "Effects Quality" setting is set to "Low". + * - This does not affect the natural bloom effect from high color values. + * + * See also: + * - {@link bloomRed} + * - {@link bloomGreen} + * - {@link bloomStrength} + */ + bloomBlue: number + /** + * Controls the strength of the additional bloom effect. + * + * Note: + * - This has no effect if the "Effects Quality" setting is set to "Low". + * - This does not affect the natural bloom effect from high color values. + * + * See also: + * - {@link bloomRed} + * - {@link bloomGreen} + * - {@link bloomBlue} + */ + bloomStrength: number + /** + * Minimum view distance. If the particle is closer than this distance from the camera, it will be hidden. Can be set to -1 to disable the limit. + * + * See also: + * - {@link maxDistance} + */ + minDistance: number + /** + * Maximum view distance. If the particle is farther away than this distance from the camera, it will be hidden. Can be set to -1 to disable the limit. + * + * See also: + * - {@link minDistance} + */ + maxDistance: number + /** + * Blend mode. + * + * **Argument**: {@link PropertyArgument.Constant0 Constant 0} + */ + blendMode: BlendMode | ScalarProperty + /** + * X position offset. + * + * **Argument**: {@link PropertyArgument.InstanceAge Instance age} + */ + offsetX: ScalarValue + /** + * Y position offset. + * + * **Argument**: {@link PropertyArgument.InstanceAge Instance age} + */ + offsetY: ScalarValue + /** + * Z position offset. + * + * **Argument**: {@link PropertyArgument.InstanceAge Instance age} + */ + offsetZ: ScalarValue + /** + * The width of the particle. + * + * If {@link uniformScale} is enabled, this also controls the height and depth. + * + * **Argument**: {@link PropertyArgument.InstanceAge Instance age} + * + * See also: + * - {@link scaleVariationX} + * - {@link sizeY} + * - {@link sizeZ} + */ + sizeX: ScalarValue + /** + * The height of the particle. + * + * If {@link uniformScale} is enabled, {@link sizeX} also controls the height, and this property is ignored. + * + * **Argument**: {@link PropertyArgument.InstanceAge Instance age} + * + * See also: + * - {@link scaleVariationY} + * - {@link sizeX} + * - {@link sizeZ} + */ + sizeY: ScalarValue + /** + * The depth of the particle. + * + * If {@link uniformScale} is enabled, {@link sizeX} also controls the depth, and this property is ignored. + * + * If the distortion {@link shape} is set to {@link DistortionShape.Rectangle Rectangle}, this property won't have any effect since the rectangle is 2-dimensional. + * + * **Argument**: {@link PropertyArgument.InstanceAge Instance age} + * + * See also: + * - {@link scaleVariationZ} + * - {@link sizeX} + * - {@link sizeY} + */ + sizeZ: ScalarValue + /** + * Color multiplier. + * + * **Argument**: {@link PropertyArgument.InstanceAge Instance age} + */ + color: Vector4Value + /** + * Controls the intensity of the distortion effect. At 0, there is no distortion at all. + * + * **Argument**: {@link PropertyArgument.InstanceAge Instance age} + */ + intensity: ScalarValue + /** + * Controls the speed of the stirring effect in radians per second. Requires {@link mode} to be set to {@link DistortionMode.Stir}. + * + * **Argument**: {@link PropertyArgument.InstanceAge Instance age} + */ + stirSpeed: ScalarValue + /** + * The distortion effect is only applied to an ellipse inside the particle. This property controls how large this ellipse is. At 1, it inscribes the particle's rectangle. At values greater than 1, it is the same size as 1, but there might be strange artifacts around the edges of the distortion. + * + * **Argument**: {@link PropertyArgument.InstanceAge Instance age} + */ + radius: ScalarValue + /** + * Horizontal offset for the {@link normalMap normal map}. + * + * **Argument**: {@link PropertyArgument.Constant0 Constant 0} + */ + normalMapOffsetU: ScalarValue + /** + * Vertical offset for the {@link normalMap normal map}. + * + * **Argument**: {@link PropertyArgument.Constant0 Constant 0} + */ + normalMapOffsetV: ScalarValue + /** + * Horizontal offset speed for the {@link normalMap normal map}. + * + * **Argument**: {@link PropertyArgument.InstanceAge Instance age} + */ + normalMapSpeedU: ScalarValue + /** + * Vertical offset speed for the {@link normalMap normal map}. + * + * **Argument**: {@link PropertyArgument.InstanceAge Instance age} + */ + normalMapSpeedV: ScalarValue + /** + * Scalar multiplier for the color that does not affect the alpha. Effectively a brightness multiplier. + * + * **Argument**: {@link PropertyArgument.EffectAge Effect age} + */ + rgbMultiplier: ScalarValue + /** + * Alpha multiplier. + * + * **Argument**: {@link PropertyArgument.EffectAge Effect age} + */ + alphaMultiplier: ScalarValue + unk_ds3_f1_11: number + unk_ds3_f1_12: number + unk_ds3_f2_0: number + unk_ds3_f2_1: number + unk_ds3_f2_2: number + unk_ds3_f2_3: number + unk_ds3_f2_4: number + unk_ds3_f2_9: number + unk_ds3_f2_10: number + unk_ds3_f2_11: number + unk_ds3_f2_12: number + unk_ds3_f2_13: number + unkDistFadeClose0: number + unkDistFadeClose1: number + unkDistFadeFar0: number + unkDistFadeFar1: number + unk_ds3_f2_20: number + unk_ds3_f2_21: number + unk_ds3_f2_22: number + unk_ds3_f2_23: number + unk_ds3_f2_24: number + unkDepthBlend1: number + unkDepthBlend2: number + unk_ds3_f2_27: number + unk_ds3_f2_28: number + unk_ds3_f2_29: number + unk_ds3_p1_7: Vector4Value + unk_ds3_p1_9: ScalarValue + unk_ds3_p2_2: ScalarValue + unk_ds3_p2_3: Vector4Value + unk_ds3_p2_4: Vector4Value + unk_ds3_p2_5: Vector4Value + unk_ds3_p2_6: ScalarValue + unk_sdt_f2_30: number + unk_sdt_f2_31: number + unk_sdt_f2_32: number + unk_sdt_f2_33: number + unk_sdt_f2_34: number + unk_sdt_f2_35: number + unk_sdt_f2_36: number + unk_sdt_f2_37: number + unk_sdt_f2_38: number + unk_er_f1_12: number + unk_er_f1_13: number + unk_er_p2_7: ScalarValue + unk_er_p2_8: ScalarValue + constructor(props: DistortionParams = {}) { + super(ActionType.Distortion) + this.assign(props) + } +} + +export interface RadialBlurParams { + /** + * If enabled, the particle width-related properties and fields will control both the width and height of the particles, and the height counterparts will be ignored. + * + * **Default**: `false` + * + * See also: + * - {@link width} + * - {@link height} + */ + uniformScale?: boolean + /** + * Controls how many times to apply the effect. Higher values can have a significant impact on performance. + * + * **Default**: `1` + */ + iterations?: number + /** + * Controls the redness of the color of the additional bloom effect. The colors of the particle will be multiplied with this color to get the final color of the bloom effect. + * + * Note: + * - This has no effect if the "Effects Quality" setting is set to "Low". + * - This does not affect the natural bloom effect from high color values. + * + * **Default**: `1` + * + * See also: + * - {@link bloomGreen} + * - {@link bloomBlue} + * - {@link bloomStrength} + */ + bloomRed?: number + /** + * Controls the greenness of the color of the additional bloom effect. The colors of the particle will be multiplied with this color to get the final color of the bloom effect. + * + * Note: + * - This has no effect if the "Effects Quality" setting is set to "Low". + * - This does not affect the natural bloom effect from high color values. + * + * **Default**: `1` + * + * See also: + * - {@link bloomRed} + * - {@link bloomBlue} + * - {@link bloomStrength} + */ + bloomGreen?: number + /** + * Controls the blueness of the color of the additional bloom effect. The colors of the particle will be multiplied with this color to get the final color of the bloom effect. + * + * Note: + * - This has no effect if the "Effects Quality" setting is set to "Low". + * - This does not affect the natural bloom effect from high color values. + * + * **Default**: `1` + * + * See also: + * - {@link bloomRed} + * - {@link bloomGreen} + * - {@link bloomStrength} + */ + bloomBlue?: number + /** + * Controls the strength of the additional bloom effect. + * + * Note: + * - This has no effect if the "Effects Quality" setting is set to "Low". + * - This does not affect the natural bloom effect from high color values. + * + * **Default**: `0` + * + * See also: + * - {@link bloomRed} + * - {@link bloomGreen} + * - {@link bloomBlue} + */ + bloomStrength?: number + /** + * Minimum view distance. If the particle is closer than this distance from the camera, it will be hidden. Can be set to -1 to disable the limit. + * + * **Default**: `-1` + * + * See also: + * - {@link maxDistance} + */ + minDistance?: number + /** + * Maximum view distance. If the particle is farther away than this distance from the camera, it will be hidden. Can be set to -1 to disable the limit. + * + * **Default**: `-1` + * + * See also: + * - {@link minDistance} + */ + maxDistance?: number + /** + * Blend mode. + * + * **Default**: {@link BlendMode.Normal} + * + * **Argument**: {@link PropertyArgument.Constant0 Constant 0} + */ + blendMode?: BlendMode | ScalarProperty + /** + * Mask texture ID. This texture is used to control the opacity of the particle. + * + * **Default**: `1` + * + * **Argument**: {@link PropertyArgument.Constant0 Constant 0} + */ + mask?: ScalarValue + /** + * X position offset. + * + * **Default**: `0` + * + * **Argument**: {@link PropertyArgument.InstanceAge Instance age} + * + * See also: + * - {@link offsetY} + * - {@link offsetZ} + */ + offsetX?: ScalarValue + /** + * Y position offset. + * + * **Default**: `0` + * + * **Argument**: {@link PropertyArgument.InstanceAge Instance age} + * + * See also: + * - {@link offsetX} + * - {@link offsetZ} + */ + offsetY?: ScalarValue + /** + * Z position offset. + * + * **Default**: `0` + * + * **Argument**: {@link PropertyArgument.InstanceAge Instance age} + * + * See also: + * - {@link offsetX} + * - {@link offsetY} + */ + offsetZ?: ScalarValue + /** + * The width of the particle. + * + * If {@link uniformScale} is enabled, this also controls the height. + * + * **Default**: `1` + * + * **Argument**: {@link PropertyArgument.InstanceAge Instance age} + * + * See also: + * - {@link height} + */ + width?: ScalarValue + /** + * The height of the particle. + * + * If {@link uniformScale} is enabled, {@link width} also controls the height, and this property is ignored. + * + * **Default**: `1` + * + * **Argument**: {@link PropertyArgument.InstanceAge Instance age} + * + * See also: + * - {@link width} + */ + height?: ScalarValue + /** + * Color multiplier. + * + * **Default**: `[1, 1, 1, 1]` + * + * **Argument**: {@link PropertyArgument.InstanceAge Instance age} + */ + color?: Vector4Value + /** + * Controls the amount of blur to apply. Values greater than 1 may appear glitchy. + * + * **Default**: `0.5` + * + * **Argument**: {@link PropertyArgument.InstanceAge Instance age} + */ + blurRadius?: ScalarValue + /** + * Scalar multiplier for the color that does not affect the alpha. Effectively a brightness multiplier. + * + * **Default**: `1` + * + * **Argument**: {@link PropertyArgument.EffectAge Effect age} + */ + rgbMultiplier?: ScalarValue + /** + * Alpha multiplier. + * + * **Default**: `1` + * + * **Argument**: {@link PropertyArgument.EffectAge Effect age} + */ + alphaMultiplier?: ScalarValue + /** + * Unknown integer. + * + * **Default**: `0` + */ + unk_ds3_f1_4?: number + /** + * Unknown integer. + * + * **Default**: `0` + */ + unk_ds3_f2_0?: number + /** + * Unknown integer. + * + * **Default**: `0` + */ + unk_ds3_f2_1?: number + /** + * Unknown integer. + * + * **Default**: `8` + */ + unk_ds3_f2_2?: number + /** + * Unknown float. + * + * **Default**: `1` + */ + unk_ds3_f2_3?: number + /** + * Unknown integer. + * + * **Default**: `0` + */ + unk_ds3_f2_4?: number + /** + * Unknown integer. + * + * **Default**: `0` + */ + unk_ds3_f2_9?: number + /** + * Unknown integer. + * + * **Default**: `0` + */ + unk_ds3_f2_10?: number + /** + * Unknown integer. + * + * **Default**: `0` + */ + unk_ds3_f2_11?: number + /** + * Unknown integer. + * + * **Default**: `0` + */ + unk_ds3_f2_12?: number + /** + * Unknown integer. + * + * **Default**: `0` + */ + unk_ds3_f2_13?: number + /** + * Unknown float. + * + * **Default**: `-1` + */ + unkDistFadeClose0?: number + /** + * Unknown float. + * + * **Default**: `-1` + */ + unkDistFadeClose1?: number + /** + * Unknown float. + * + * **Default**: `-1` + */ + unkDistFadeFar0?: number + /** + * Unknown float. + * + * **Default**: `-1` + */ + unkDistFadeFar1?: number + /** + * Unknown float. + * + * **Default**: `0.5` + */ + unk_ds3_f2_20?: number + /** + * Unknown integer. + * + * **Default**: `1` + */ + unk_ds3_f2_21?: number + /** + * Unknown integer. + * + * **Default**: `0` + */ + unk_ds3_f2_22?: number + /** + * Unknown integer. + * + * **Default**: `0` + */ + unk_ds3_f2_23?: number + /** + * Unknown integer. + * + * **Default**: `0` + */ + unk_ds3_f2_24?: number + /** + * Unknown float. + * + * **Default**: `1` + */ + unkDepthBlend1?: number + /** + * Unknown float. + * + * **Default**: `0` + */ + unkDepthBlend2?: number + /** + * Unknown integer. + * + * **Default**: `1` + */ + unk_ds3_f2_27?: number + /** + * Unknown integer. + * + * **Default**: `0` + */ + unk_ds3_f2_28?: number + /** + * Unknown float. + * + * **Default**: `0` + */ + unk_ds3_f2_29?: number + /** + * Unknown. + * + * **Default**: `[1, 1, 1, 1]` + */ + unk_ds3_p1_6?: Vector4Value + /** + * Unknown. + * + * **Default**: `0` + */ + unk_ds3_p2_2?: ScalarValue + /** + * Unknown. + * + * **Default**: `[1, 1, 1, 1]` + */ + unk_ds3_p2_3?: Vector4Value + /** + * Unknown. + * + * **Default**: `[1, 1, 1, 1]` + */ + unk_ds3_p2_4?: Vector4Value + /** + * Unknown. + * + * **Default**: `[1, 1, 1, 1]` + */ + unk_ds3_p2_5?: Vector4Value + /** + * Unknown. + * + * **Default**: `0` + */ + unk_ds3_p2_6?: ScalarValue + /** + * Unknown float. + * + * **Default**: `0` + */ + unk_sdt_f2_30?: number + /** + * Unknown integer. + * + * **Default**: `1` + */ + unk_er_f1_3?: number + /** + * Unknown integer. + * + * **Default**: `1` + */ + unk_er_f1_4?: number +} + +/** + * A particle that applies a radial blur to anything seen through it. + * + * Note: This particle is not visible if the "Effects" setting is set to "Low". + */ +class RadialBlur extends DataAction { + declare type: ActionType.RadialBlur + /** + * If enabled, the particle width-related properties and fields will control both the width and height of the particles, and the height counterparts will be ignored. + * + * See also: + * - {@link width} + * - {@link height} + */ + uniformScale: boolean + /** + * Controls how many times to apply the effect. Higher values can have a significant impact on performance. + */ + iterations: number + /** + * Controls the redness of the color of the additional bloom effect. The colors of the particle will be multiplied with this color to get the final color of the bloom effect. + * + * Note: + * - This has no effect if the "Effects Quality" setting is set to "Low". + * - This does not affect the natural bloom effect from high color values. + * + * See also: + * - {@link bloomGreen} + * - {@link bloomBlue} + * - {@link bloomStrength} + */ + bloomRed: number + /** + * Controls the greenness of the color of the additional bloom effect. The colors of the particle will be multiplied with this color to get the final color of the bloom effect. + * + * Note: + * - This has no effect if the "Effects Quality" setting is set to "Low". + * - This does not affect the natural bloom effect from high color values. + * + * See also: + * - {@link bloomRed} + * - {@link bloomBlue} + * - {@link bloomStrength} + */ + bloomGreen: number + /** + * Controls the blueness of the color of the additional bloom effect. The colors of the particle will be multiplied with this color to get the final color of the bloom effect. + * + * Note: + * - This has no effect if the "Effects Quality" setting is set to "Low". + * - This does not affect the natural bloom effect from high color values. + * + * See also: + * - {@link bloomRed} + * - {@link bloomGreen} + * - {@link bloomStrength} + */ + bloomBlue: number + /** + * Controls the strength of the additional bloom effect. + * + * Note: + * - This has no effect if the "Effects Quality" setting is set to "Low". + * - This does not affect the natural bloom effect from high color values. + * + * See also: + * - {@link bloomRed} + * - {@link bloomGreen} + * - {@link bloomBlue} + */ + bloomStrength: number + /** + * Minimum view distance. If the particle is closer than this distance from the camera, it will be hidden. Can be set to -1 to disable the limit. + * + * See also: + * - {@link maxDistance} + */ + minDistance: number + /** + * Maximum view distance. If the particle is farther away than this distance from the camera, it will be hidden. Can be set to -1 to disable the limit. + * + * See also: + * - {@link minDistance} + */ + maxDistance: number + /** + * Blend mode. + * + * **Argument**: {@link PropertyArgument.Constant0 Constant 0} + */ + blendMode: BlendMode | ScalarProperty + /** + * Mask texture ID. This texture is used to control the opacity of the particle. + * + * **Argument**: {@link PropertyArgument.Constant0 Constant 0} + */ + mask: ScalarValue + /** + * X position offset. + * + * **Argument**: {@link PropertyArgument.InstanceAge Instance age} + * + * See also: + * - {@link offsetY} + * - {@link offsetZ} + */ + offsetX: ScalarValue + /** + * Y position offset. + * + * **Argument**: {@link PropertyArgument.InstanceAge Instance age} + * + * See also: + * - {@link offsetX} + * - {@link offsetZ} + */ + offsetY: ScalarValue + /** + * Z position offset. + * + * **Argument**: {@link PropertyArgument.InstanceAge Instance age} + * + * See also: + * - {@link offsetX} + * - {@link offsetY} + */ + offsetZ: ScalarValue + /** + * The width of the particle. + * + * If {@link uniformScale} is enabled, this also controls the height. + * + * **Argument**: {@link PropertyArgument.InstanceAge Instance age} + * + * See also: + * - {@link height} + */ + width: ScalarValue + /** + * The height of the particle. + * + * If {@link uniformScale} is enabled, {@link width} also controls the height, and this property is ignored. + * + * **Argument**: {@link PropertyArgument.InstanceAge Instance age} + * + * See also: + * - {@link width} + */ + height: ScalarValue + /** + * Color multiplier. + * + * **Argument**: {@link PropertyArgument.InstanceAge Instance age} + */ + color: Vector4Value + /** + * Controls the amount of blur to apply. Values greater than 1 may appear glitchy. + * + * **Argument**: {@link PropertyArgument.InstanceAge Instance age} + */ + blurRadius: ScalarValue + /** + * Scalar multiplier for the color that does not affect the alpha. Effectively a brightness multiplier. + * + * **Argument**: {@link PropertyArgument.EffectAge Effect age} + */ + rgbMultiplier: ScalarValue + /** + * Alpha multiplier. + * + * **Argument**: {@link PropertyArgument.EffectAge Effect age} + */ + alphaMultiplier: ScalarValue + unk_ds3_f1_4: number + unk_ds3_f2_0: number + unk_ds3_f2_1: number + unk_ds3_f2_2: number + unk_ds3_f2_3: number + unk_ds3_f2_4: number + unk_ds3_f2_9: number + unk_ds3_f2_10: number + unk_ds3_f2_11: number + unk_ds3_f2_12: number + unk_ds3_f2_13: number + unkDistFadeClose0: number + unkDistFadeClose1: number + unkDistFadeFar0: number + unkDistFadeFar1: number + unk_ds3_f2_20: number + unk_ds3_f2_21: number + unk_ds3_f2_22: number + unk_ds3_f2_23: number + unk_ds3_f2_24: number + unkDepthBlend1: number + unkDepthBlend2: number + unk_ds3_f2_27: number + unk_ds3_f2_28: number + unk_ds3_f2_29: number + unk_ds3_p1_6: Vector4Value + unk_ds3_p2_2: ScalarValue + unk_ds3_p2_3: Vector4Value + unk_ds3_p2_4: Vector4Value + unk_ds3_p2_5: Vector4Value + unk_ds3_p2_6: ScalarValue + unk_sdt_f2_30: number + unk_er_f1_3: number + unk_er_f1_4: number + constructor(props: RadialBlurParams = {}) { + super(ActionType.RadialBlur) + this.assign(props) + } +} + +export interface PointLightParams { + /** + * Controls the diffuse color of the light. + * + * If {@link separateSpecular} is disabled, this also controls the specular color of the light. + * + * **Default**: `[1, 1, 1, 1]` + * + * **Argument**: {@link PropertyArgument.EffectAge Effect age} + * + * See also: + * - {@link specularColor} + */ + diffuseColor?: Vector4Value + /** + * Controls the specular color of the light. + * + * If {@link separateSpecular} is disabled, this property is ignored and {@link diffuseColor} controls both the diffuse as well as the specular color. + * + * **Default**: `[1, 1, 1, 1]` + * + * **Argument**: {@link PropertyArgument.EffectAge Effect age} + */ + specularColor?: Vector4Value + /** + * The maximum distance that the light may travel from the source, and the radius of the sphere in which other effects caused by the light source (for example {@link volumeDensity} and its related fields) may act. + * + * **Default**: `10` + * + * **Argument**: {@link PropertyArgument.EffectAge Effect age} + */ + radius?: ScalarValue + /** + * A scalar multiplier for the {@link diffuseColor diffuse color}. Good for easily adjusting the brightness of the light without changing the color. + * + * If {@link separateSpecular} is disabled, this also affects the specular color of the light. + * + * **Default**: `1` + * + * **Argument**: {@link PropertyArgument.EffectAge Effect age} + */ + diffuseMultiplier?: ScalarValue + /** + * A scalar multiplier for the {@link specularColor specular color}. + * + * If {@link separateSpecular} is disabled, this property is ignored. + * + * **Default**: `1` + * + * **Argument**: {@link PropertyArgument.EffectAge Effect age} + */ + specularMultiplier?: ScalarValue + /** + * Toggles the jitter and flicker animations for the light. + * + * **Default**: `false` + * + * See also: + * - {@link jitterAcceleration} + * - {@link jitterX} + * - {@link jitterY} + * - {@link jitterZ} + * - {@link flickerIntervalMin} + * - {@link flickerIntervalMax} + * - {@link flickerBrightness} + */ + jitterAndFlicker?: boolean + /** + * Controls the acceleration of the jittering. + * + * **Default**: `1` + * + * See also: + * - {@link jitterAndFlicker} + * - {@link jitterX} + * - {@link jitterY} + * - {@link jitterZ} + */ + jitterAcceleration?: number + /** + * Controls how much the light should move around randomly on the X-axis. + * + * **Default**: `0` + * + * See also: + * - {@link jitterAndFlicker} + * - {@link jitterAcceleration} + * - {@link jitterY} + * - {@link jitterZ} + */ + jitterX?: number + /** + * Controls how much the light should move around randomly on the Y-axis. + * + * **Default**: `0` + * + * See also: + * - {@link jitterAndFlicker} + * - {@link jitterAcceleration} + * - {@link jitterX} + * - {@link jitterZ} + */ + jitterY?: number + /** + * Controls how much the light should move around randomly on the Z-axis. + * + * **Default**: `0` + * + * See also: + * - {@link jitterAndFlicker} + * - {@link jitterAcceleration} + * - {@link jitterX} + * - {@link jitterY} + */ + jitterZ?: number + /** + * Controls the minimum interval for flickering. + * + * **Default**: `0` + * + * See also: + * - {@link jitterAndFlicker} + * - {@link flickerIntervalMax} + * - {@link flickerBrightness} + */ + flickerIntervalMin?: number + /** + * Controls the maximum interval for flickering. + * + * **Default**: `1` + * + * See also: + * - {@link jitterAndFlicker} + * - {@link flickerIntervalMin} + * - {@link flickerBrightness} + */ + flickerIntervalMax?: number + /** + * Brightness multiplier for the light when it flickers. + * + * **Default**: `0.5` + * + * See also: + * - {@link jitterAndFlicker} + * - {@link flickerIntervalMin} + * - {@link flickerIntervalMax} + */ + flickerBrightness?: number + /** + * Controls if the light should have shadows or not. + * + * Note: Map objects also have a setting for casting shadows, and both must be enabled for an object to cast shadows from the light source. + * + * **Default**: `false` + */ + shadows?: boolean + /** + * When enabled, this allows other properties and fields of the action to control the specular color independently of the diffuse color. When disabled, the diffuse counterpart of the properties or fields will affect both the diffuse and specular color. + * + * **Default**: `false` + * + * See also: + * - {@link diffuseColor} + * - {@link specularColor} + * - {@link diffuseMultiplier} + * - {@link specularMultiplier} + */ + separateSpecular?: boolean + /** + * The number of seconds the light takes to fade to nothing after being destroyed. + * + * Due to how the field this represents works, the time will be rounded to the nearest multiple of 1/30s. + * + * **Default**: `0` + */ + fadeOutTime?: number + /** + * Controls how dark shadows from this light source are. At 0, the shadows will be entirely invisible. + * + * **Default**: `1` + */ + shadowDarkness?: number + /** + * Controls the density of some sort of fake fog in the volume hit by the light. The fog does not affect the actual light produced by the source and is not affected by shadows. + * + * **Default**: `0` + * + * See also: + * - {@link phaseFunction} + * - {@link asymmetryParam} + */ + volumeDensity?: number + /** + * Controls whether or not {@link asymmetryParam} affects the fake fog from {@link volumeDensity}. + * + * **Default**: `true` + */ + phaseFunction?: boolean + /** + * Controls how the fake fog from {@link volumeDensity} scatters the light. This value is ignored if {@link phaseFunction} is disabled, and the fog will scatter the light equally in all directions. + * + * - At 0, the light is scattered equally in every direction. + * - As the value approaches 1, the light is scattered more and more forward, in the same direction as the light was already traveling. This means that the fake fog will be less visible from the side or behind, and more visible from in front of the light. + * - At 1, the fog will not scatter the light at all, so it will be entirely invisible. + * - Values above 1 produce unnatural-looking effects where the light darkens the fog instead. + * + * **Default**: `0.75` + */ + asymmetryParam?: number + /** + * Controls the falloff exponent of the light. + * + * Note: This is possibly something else, but the behavior is pretty similar to a falloff exponent in a few ways. + * + * **Default**: `1` + */ + falloffExponent?: number + /** + * Unknown integer. + * + * **Default**: `0` + */ + unk_ds3_f1_0?: number + /** + * Unknown float. + * + * **Default**: `0` + */ + unk_ds3_f1_1?: number + /** + * Unknown integer. + * + * **Default**: `0` + */ + unk_ds3_f2_0?: number + /** + * Unknown float. + * + * **Default**: `0` + */ + unk_ds3_f2_3?: number + /** + * Unknown. Only used in Dark Souls 3. + * + * **Default**: `1` + */ + unk_ds3_f2_12?: number + /** + * Unknown boolean. + * + * **Default**: `false` + */ + unk_ds3_f2_15?: boolean + /** + * Unknown integer. + * + * **Default**: `2` + */ + unk_ds3_f2_16?: number + /** + * Unknown boolean. + * + * **Default**: `true` + */ + unk_ds3_f2_17?: boolean + /** + * Unknown float. + * + * **Default**: `0` + */ + unk_ds3_f2_18?: number + /** + * Unknown float. + * + * **Default**: `0` + */ + unk_ds3_f2_19?: number + /** + * Unknown float. + * + * **Default**: `0` + */ + unk_ds3_f2_20?: number + /** + * Unknown integer. + * + * **Default**: `0` + */ + unk_ds3_f2_21?: number + /** + * Unknown integer. + * + * **Default**: `100` + */ + unk_ds3_f2_22?: number + /** + * Unknown integer. + * + * **Default**: `0` + */ + unk_ds3_f2_23?: number + /** + * Unknown integer. + * + * **Default**: `0` + */ + unk_ds3_f2_24?: number + /** + * Unknown. + * + * **Default**: `0` + */ + unk_ds3_p1_3?: ScalarValue + /** + * Unknown. + * + * **Default**: `0` + */ + unk_ds3_p1_4?: ScalarValue + /** + * Unknown. + * + * **Default**: `0` + */ + unk_ds3_p1_5?: ScalarValue + /** + * Unknown. + * + * **Default**: `0` + */ + unk_ds3_p1_6?: ScalarValue + /** + * Unknown. + * + * **Default**: `10` + */ + unk_ds3_p1_7?: ScalarValue + /** + * Unknown. + * + * **Default**: `10` + */ + unk_ds3_p1_8?: ScalarValue + /** + * Unknown. + * + * **Default**: `10` + */ + unk_ds3_p1_9?: ScalarValue + /** + * Unknown. + * + * **Default**: `1` + */ + unk_ds3_p2_0?: ScalarValue + /** + * Unknown. + * + * **Default**: `1` + */ + unk_ds3_p2_1?: ScalarValue + /** + * Unknown float. + * + * **Default**: `0` + */ + unk_sdt_f2_25?: number + /** + * Unknown. + * + * **Default**: `1` + */ + unk_sdt_p2_2?: ScalarValue + /** + * Unknown integer. + * + * **Default**: `1` + */ + unk_er_f2_29?: number + /** + * Unknown float. + * + * **Default**: `1` + */ + unk_er_f2_30?: number + /** + * Unknown integer. + * + * **Default**: `1` + */ + unk_er_f2_31?: number + /** + * Unknown integer. + * + * **Default**: `0` + */ + unk_er_f2_32?: number +} + +/** + * Point light source. + */ +class PointLight extends DataAction { + declare type: ActionType.PointLight + /** + * Controls the diffuse color of the light. + * + * If {@link separateSpecular} is disabled, this also controls the specular color of the light. + * + * **Argument**: {@link PropertyArgument.EffectAge Effect age} + * + * See also: + * - {@link specularColor} + */ + diffuseColor: Vector4Value + /** + * Controls the specular color of the light. + * + * If {@link separateSpecular} is disabled, this property is ignored and {@link diffuseColor} controls both the diffuse as well as the specular color. + * + * **Argument**: {@link PropertyArgument.EffectAge Effect age} + */ + specularColor: Vector4Value + /** + * The maximum distance that the light may travel from the source, and the radius of the sphere in which other effects caused by the light source (for example {@link volumeDensity} and its related fields) may act. + * + * **Argument**: {@link PropertyArgument.EffectAge Effect age} + */ + radius: ScalarValue + /** + * A scalar multiplier for the {@link diffuseColor diffuse color}. Good for easily adjusting the brightness of the light without changing the color. + * + * If {@link separateSpecular} is disabled, this also affects the specular color of the light. + * + * **Argument**: {@link PropertyArgument.EffectAge Effect age} + */ + diffuseMultiplier: ScalarValue + /** + * A scalar multiplier for the {@link specularColor specular color}. + * + * If {@link separateSpecular} is disabled, this property is ignored. + * + * **Argument**: {@link PropertyArgument.EffectAge Effect age} + */ + specularMultiplier: ScalarValue + /** + * Toggles the jitter and flicker animations for the light. + * + * See also: + * - {@link jitterAcceleration} + * - {@link jitterX} + * - {@link jitterY} + * - {@link jitterZ} + * - {@link flickerIntervalMin} + * - {@link flickerIntervalMax} + * - {@link flickerBrightness} + */ + jitterAndFlicker: boolean + /** + * Controls the acceleration of the jittering. + * + * See also: + * - {@link jitterAndFlicker} + * - {@link jitterX} + * - {@link jitterY} + * - {@link jitterZ} + */ + jitterAcceleration: number + /** + * Controls how much the light should move around randomly on the X-axis. + * + * See also: + * - {@link jitterAndFlicker} + * - {@link jitterAcceleration} + * - {@link jitterY} + * - {@link jitterZ} + */ + jitterX: number + /** + * Controls how much the light should move around randomly on the Y-axis. + * + * See also: + * - {@link jitterAndFlicker} + * - {@link jitterAcceleration} + * - {@link jitterX} + * - {@link jitterZ} + */ + jitterY: number + /** + * Controls how much the light should move around randomly on the Z-axis. + * + * See also: + * - {@link jitterAndFlicker} + * - {@link jitterAcceleration} + * - {@link jitterX} + * - {@link jitterY} + */ + jitterZ: number + /** + * Controls the minimum interval for flickering. + * + * See also: + * - {@link jitterAndFlicker} + * - {@link flickerIntervalMax} + * - {@link flickerBrightness} + */ + flickerIntervalMin: number + /** + * Controls the maximum interval for flickering. + * + * See also: + * - {@link jitterAndFlicker} + * - {@link flickerIntervalMin} + * - {@link flickerBrightness} + */ + flickerIntervalMax: number + /** + * Brightness multiplier for the light when it flickers. + * + * See also: + * - {@link jitterAndFlicker} + * - {@link flickerIntervalMin} + * - {@link flickerIntervalMax} + */ + flickerBrightness: number + /** + * Controls if the light should have shadows or not. + * + * Note: Map objects also have a setting for casting shadows, and both must be enabled for an object to cast shadows from the light source. + */ + shadows: boolean + /** + * When enabled, this allows other properties and fields of the action to control the specular color independently of the diffuse color. When disabled, the diffuse counterpart of the properties or fields will affect both the diffuse and specular color. + * + * See also: + * - {@link diffuseColor} + * - {@link specularColor} + * - {@link diffuseMultiplier} + * - {@link specularMultiplier} + */ + separateSpecular: boolean + /** + * The number of seconds the light takes to fade to nothing after being destroyed. + * + * Due to how the field this represents works, the time will be rounded to the nearest multiple of 1/30s. + */ + fadeOutTime: number + /** + * Controls how dark shadows from this light source are. At 0, the shadows will be entirely invisible. + */ + shadowDarkness: number + /** + * Controls the density of some sort of fake fog in the volume hit by the light. The fog does not affect the actual light produced by the source and is not affected by shadows. + * + * See also: + * - {@link phaseFunction} + * - {@link asymmetryParam} + */ + volumeDensity: number + /** + * Controls whether or not {@link asymmetryParam} affects the fake fog from {@link volumeDensity}. + */ + phaseFunction: boolean + /** + * Controls how the fake fog from {@link volumeDensity} scatters the light. This value is ignored if {@link phaseFunction} is disabled, and the fog will scatter the light equally in all directions. + * + * - At 0, the light is scattered equally in every direction. + * - As the value approaches 1, the light is scattered more and more forward, in the same direction as the light was already traveling. This means that the fake fog will be less visible from the side or behind, and more visible from in front of the light. + * - At 1, the fog will not scatter the light at all, so it will be entirely invisible. + * - Values above 1 produce unnatural-looking effects where the light darkens the fog instead. + */ + asymmetryParam: number + /** + * Controls the falloff exponent of the light. + * + * Note: This is possibly something else, but the behavior is pretty similar to a falloff exponent in a few ways. + */ + falloffExponent: number + unk_ds3_f1_0: number + unk_ds3_f1_1: number + unk_ds3_f2_0: number + unk_ds3_f2_3: number + /** + * Unknown. Only used in Dark Souls 3. + */ + unk_ds3_f2_12: number + unk_ds3_f2_15: boolean + unk_ds3_f2_16: number + unk_ds3_f2_17: boolean + unk_ds3_f2_18: number + unk_ds3_f2_19: number + unk_ds3_f2_20: number + unk_ds3_f2_21: number + unk_ds3_f2_22: number + unk_ds3_f2_23: number + unk_ds3_f2_24: number + unk_ds3_p1_3: ScalarValue + unk_ds3_p1_4: ScalarValue + unk_ds3_p1_5: ScalarValue + unk_ds3_p1_6: ScalarValue + unk_ds3_p1_7: ScalarValue + unk_ds3_p1_8: ScalarValue + unk_ds3_p1_9: ScalarValue + unk_ds3_p2_0: ScalarValue + unk_ds3_p2_1: ScalarValue + unk_sdt_f2_25: number + unk_sdt_p2_2: ScalarValue + unk_er_f2_29: number + unk_er_f2_30: number + unk_er_f2_31: number + unk_er_f2_32: number + constructor(props: PointLightParams = {}) { + super(ActionType.PointLight) + this.assign(props) + } +} + +export interface Unk701Params { + /** + * Unknown. + * + * **Default**: `1` + */ + unk_er_p1_0?: ScalarValue +} + +/** + * Unknown root node action that was introduced in Elden Ring. + */ +class Unk701 extends DataAction { + declare type: ActionType.Unk701 + unk_er_p1_0: ScalarValue + constructor(props: Unk701Params = {}) { + super(ActionType.Unk701) + this.assign(props) + } +} + +export interface NodeWindSpeedParams { + /** + * The speed in the direction of the wind. + * + * **Default**: `0` + * + * **Argument**: {@link PropertyArgument.EffectAge Effect age} + */ + speed?: ScalarValue + /** + * A multiplier for {@link speed the speed in the direction of the wind}. + * + * **Default**: `1` + * + * **Argument**: {@link PropertyArgument.EffectAge Effect age} + */ + speedMultiplier?: ScalarValue + /** + * Controls whether the wind should have any effect at all or not. + * + * **Default**: `true` + */ + enabled?: boolean +} + +/** + * Controls how effective the wind is at pushing the node. + */ +class NodeWindSpeed extends DataAction { + declare type: ActionType.NodeWindSpeed + /** + * The speed in the direction of the wind. + * + * **Argument**: {@link PropertyArgument.EffectAge Effect age} + */ + speed: ScalarValue + /** + * A multiplier for {@link speed the speed in the direction of the wind}. + * + * **Argument**: {@link PropertyArgument.EffectAge Effect age} + */ + speedMultiplier: ScalarValue + /** + * Controls whether the wind should have any effect at all or not. + */ + enabled: boolean + constructor(props: NodeWindSpeedParams = {}) { + super(ActionType.NodeWindSpeed) + this.assign(props) + } +} + +export interface ParticleWindSpeedParams { + /** + * The speed in the direction of the wind. + * + * **Default**: `0` + * + * **Argument**: {@link PropertyArgument.EffectAge Effect age} + */ + speed?: ScalarValue + /** + * A multiplier for {@link speed the speed in the direction of the wind}. + * + * **Default**: `1` + * + * **Argument**: {@link PropertyArgument.EffectAge Effect age} + */ + speedMultiplier?: ScalarValue + /** + * Controls whether the wind should have any effect at all or not. + * + * **Default**: `true` + */ + enabled?: boolean + /** + * Unknown. 0 and 1 seems to be valid values, while all other values cause the wind to not affect the particles. + * + * **Default**: `0` + */ + unk_sdt_f1_1?: number +} + +/** + * Controls how effective the wind is at pushing the particles emitted from the node. + */ +class ParticleWindSpeed extends DataAction { + declare type: ActionType.ParticleWindSpeed + /** + * The speed in the direction of the wind. + * + * **Argument**: {@link PropertyArgument.EffectAge Effect age} + */ + speed: ScalarValue + /** + * A multiplier for {@link speed the speed in the direction of the wind}. + * + * **Argument**: {@link PropertyArgument.EffectAge Effect age} + */ + speedMultiplier: ScalarValue + /** + * Controls whether the wind should have any effect at all or not. + */ + enabled: boolean + /** + * Unknown. 0 and 1 seems to be valid values, while all other values cause the wind to not affect the particles. + */ + unk_sdt_f1_1: number + constructor(props: ParticleWindSpeedParams = {}) { + super(ActionType.ParticleWindSpeed) + this.assign(props) + } +} + +export interface NodeWindAccelerationParams { + /** + * The acceleration in the direction of the wind. + * + * **Default**: `0` + * + * **Argument**: {@link PropertyArgument.EffectAge Effect age} + */ + acceleration?: ScalarValue + /** + * A multiplier for {@link acceleration the acceleration in the direction of the wind}. + * + * **Default**: `1` + * + * **Argument**: {@link PropertyArgument.EffectAge Effect age} + */ + accelerationMultiplier?: ScalarValue + /** + * Controls whether the wind should have any effect at all or not. + * + * **Default**: `true` + */ + enabled?: boolean +} + +/** + * Controls how effective the wind is at accelerating the node. + */ +class NodeWindAcceleration extends DataAction { + declare type: ActionType.NodeWindAcceleration + /** + * The acceleration in the direction of the wind. + * + * **Argument**: {@link PropertyArgument.EffectAge Effect age} + */ + acceleration: ScalarValue + /** + * A multiplier for {@link acceleration the acceleration in the direction of the wind}. + * + * **Argument**: {@link PropertyArgument.EffectAge Effect age} + */ + accelerationMultiplier: ScalarValue + /** + * Controls whether the wind should have any effect at all or not. + */ + enabled: boolean + constructor(props: NodeWindAccelerationParams = {}) { + super(ActionType.NodeWindAcceleration) + this.assign(props) + } +} + +export interface ParticleWindAccelerationParams { + /** + * The acceleration in the direction of the wind. + * + * **Default**: `0` + * + * **Argument**: {@link PropertyArgument.EffectAge Effect age} + */ + acceleration?: ScalarValue + /** + * A multiplier for {@link acceleration the acceleration in the direction of the wind}. + * + * **Default**: `1` + * + * **Argument**: {@link PropertyArgument.EffectAge Effect age} + */ + accelerationMultiplier?: ScalarValue + /** + * Controls whether the wind should have any effect at all or not. + * + * **Default**: `true` + */ + enabled?: boolean + /** + * Unknown. 0 and 1 seems to be valid values, while all other values cause the wind to not affect the particles. + * + * **Default**: `0` + */ + unk_sdt_f1_1?: number +} + +/** + * Controls how effective the wind is at accelerating the particles emitted from the node. + * + * Acceleration requires slot 10 to have an action that enables acceleration of the particles. + */ +class ParticleWindAcceleration extends DataAction { + declare type: ActionType.ParticleWindAcceleration + /** + * The acceleration in the direction of the wind. + * + * **Argument**: {@link PropertyArgument.EffectAge Effect age} + */ + acceleration: ScalarValue + /** + * A multiplier for {@link acceleration the acceleration in the direction of the wind}. + * + * **Argument**: {@link PropertyArgument.EffectAge Effect age} + */ + accelerationMultiplier: ScalarValue + /** + * Controls whether the wind should have any effect at all or not. + */ + enabled: boolean + /** + * Unknown. 0 and 1 seems to be valid values, while all other values cause the wind to not affect the particles. + */ + unk_sdt_f1_1: number + constructor(props: ParticleWindAccelerationParams = {}) { + super(ActionType.ParticleWindAcceleration) + this.assign(props) + } +} + +export interface Unk800Params { + /** + * Unknown float. + * + * **Default**: `1` + */ + unk_ac6_f1_0?: number + /** + * Unknown float. + * + * **Default**: `0.2` + */ + unk_ac6_f1_1?: number + /** + * Unknown float. + * + * **Default**: `0.25` + */ + unk_ac6_f1_2?: number +} + +/** + * Unknown action that was added in Armored Core 6 and can go into the same slot as the particle wind actions. + */ +class Unk800 extends DataAction { + declare type: ActionType.Unk800 + unk_ac6_f1_0: number + unk_ac6_f1_1: number + unk_ac6_f1_2: number + constructor(props: Unk800Params = {}) { + super(ActionType.Unk800) + this.assign(props) + } +} + +export interface DynamicTracerParams { + /** + * Tracer orientation mode. See {@link TracerOrientationMode} for more information. + * + * **Default**: {@link TracerOrientationMode.LocalZ} + */ + orientation?: TracerOrientationMode + /** + * Normal map texture ID. + * + * This is used to control the distortion effect of the trail. + * + * **Default**: `0` + * + * See also: + * - {@link distortionIntensity} + */ + normalMap?: number + /** + * The trail is made up of multiple quads, or *segments*. This controls how many seconds to wait between new segments being created. Lower values produce a smoother trail. + * + * **Default**: `0` + */ + segmentInterval?: number + /** + * The trail is made up of multiple quads, or *segments*. This controls how long each segment should last in seconds. + * + * **Default**: `1` + */ + segmentDuration?: number + /** + * The trail is made up of multiple quads, or *segments*. This controls how many segments may exist at the same time. + * + * **Default**: `100` + */ + concurrentSegments?: number + /** + * To split the texture into multiple animation frames, this value must be set to the number of columns in the texture. It should equal `textureWidth / frameWidth`. + * + * **Default**: `1` + * + * See also: + * - {@link totalFrames} + */ + columns?: number + /** + * To split the texture into multiple animation frames, this value must be set to the total number of frames in the texture. + * + * **Default**: `1` + * + * See also: + * - {@link columns} + */ + totalFrames?: number + /** + * Controls whether or not the UV of the trail should be attached to the node or not. If it is attached, the texture will slide along the segments to follow the source wherever it moves, as if it was a flag attached to a pole. If it is not attached, the texture will stay where it was when the segment was created, like a skid mark on a road where the road is the segments and the mark is the texture, it wouldn't follow the car/node that made it. + * + * **Default**: `1` + */ + attachedUV?: boolean + /** + * Controls the redness of the color of the additional bloom effect. The colors of the particle will be multiplied with this color to get the final color of the bloom effect. + * + * Note: + * - This has no effect if the "Effects Quality" setting is set to "Low". + * - This does not affect the natural bloom effect from high color values. + * + * **Default**: `1` + * + * See also: + * - {@link bloomGreen} + * - {@link bloomBlue} + * - {@link bloomStrength} + */ + bloomRed?: number + /** + * Controls the greenness of the color of the additional bloom effect. The colors of the particle will be multiplied with this color to get the final color of the bloom effect. + * + * Note: + * - This has no effect if the "Effects Quality" setting is set to "Low". + * - This does not affect the natural bloom effect from high color values. + * + * **Default**: `1` + * + * See also: + * - {@link bloomRed} + * - {@link bloomBlue} + * - {@link bloomStrength} + */ + bloomGreen?: number + /** + * Controls the blueness of the color of the additional bloom effect. The colors of the particle will be multiplied with this color to get the final color of the bloom effect. + * + * Note: + * - This has no effect if the "Effects Quality" setting is set to "Low". + * - This does not affect the natural bloom effect from high color values. + * + * **Default**: `1` + * + * See also: + * - {@link bloomRed} + * - {@link bloomGreen} + * - {@link bloomStrength} + */ + bloomBlue?: number + /** + * Controls the strength of the additional bloom effect. + * + * Note: + * - This has no effect if the "Effects Quality" setting is set to "Low". + * - This does not affect the natural bloom effect from high color values. + * + * **Default**: `0` + * + * See also: + * - {@link bloomRed} + * - {@link bloomGreen} + * - {@link bloomBlue} + */ + bloomStrength?: number + /** + * Minimum view distance. If the trail is closer than this distance from the camera, it will be hidden. Can be set to -1 to disable the limit. + * + * **Default**: `-1` + * + * See also: + * - {@link maxDistance} + */ + minDistance?: number + /** + * Maximum view distance. If the trail is farther away than this distance from the camera, it will be hidden. Can be set to -1 to disable the limit. + * + * **Default**: `-1` + * + * See also: + * - {@link minDistance} + */ + maxDistance?: number + /** + * Controls how dark shaded parts of the trail are. + * + * **Default**: `0` + */ + shadowDarkness?: number + /** + * Specular texture ID. + * + * **Default**: `0` + * + * See also: + * - {@link lighting} + * - {@link glossiness} + * - {@link specularity} + */ + specular?: number + /** + * Controls how sharp the specular highlights are. + * + * **Default**: `0.25` + * + * See also: + * - {@link lighting} + * - {@link specular} + * - {@link specularity} + */ + glossiness?: number + /** + * Controls how the trail is lit. See {@link LightingMode} for more information. + * + * **Default**: {@link LightingMode.Unlit} + */ + lighting?: LightingMode + /** + * Controls how bright the specular highlights are. + * + * **Default**: `0.5` + * + * See also: + * - {@link lighting} + * - {@link specular} + * - {@link glossiness} + */ + specularity?: number + /** + * Texture ID. + * + * **Default**: `1` + * + * **Argument**: {@link PropertyArgument.Constant0 Constant 0} + */ + texture?: ScalarValue + /** + * Blend mode. + * + * **Default**: {@link BlendMode.Normal} + * + * **Argument**: {@link PropertyArgument.Constant0 Constant 0} + */ + blendMode?: BlendMode | ScalarProperty + /** + * The width of the trail. + * + * **Default**: `1` + * + * **Argument**: {@link PropertyArgument.InstanceAge Instance age} + */ + width?: ScalarValue + /** + * Multiplier for {@link width}. + * + * **Default**: `1` + * + * **Argument**: {@link PropertyArgument.InstanceAge Instance age} + */ + widthMultiplier?: ScalarValue + /** + * Color multiplier. + * + * **Default**: `[1, 1, 1, 1]` + * + * **Argument**: {@link PropertyArgument.InstanceAge Instance age} + */ + color1?: Vector4Value + /** + * Color multiplier. + * + * **Default**: `[1, 1, 1, 1]` + * + * **Argument**: {@link PropertyArgument.EmissionTime Emission time} + */ + color2?: Vector4Value + /** + * Color multiplier. + * + * **Default**: `[1, 1, 1, 1]` + * + * **Argument**: {@link PropertyArgument.InstanceAge Instance age} + */ + color3?: Vector4Value + /** + * Parts of the particle with less opacity than this threshold will be invisible. The range is 0-255. + * + * **Default**: `0` + * + * **Argument**: {@link PropertyArgument.InstanceAge Instance age} + */ + alphaThreshold?: ScalarValue + /** + * The index of the frame to show from the texture atlas. Can be animated using a {@link PropertyFunction.Linear linear property} or similar. + * + * Seemingly identical to {@link frameIndexOffset}? The sum of these two properties is the actual frame index that gets used. + * + * **Default**: `0` + * + * **Argument**: {@link PropertyArgument.InstanceAge Instance age} + */ + frameIndex?: ScalarValue + /** + * Seemingly identical to {@link frameIndex}? The sum of these two properties is the actual frame index that gets used. + * + * **Default**: `0` + * + * **Argument**: {@link PropertyArgument.InstanceAge Instance age} + */ + frameIndexOffset?: ScalarValue + /** + * Controls how much of the texture's width is used per segment. If {@link attachedUV} is enabled, this instead controls how much of the texture's width to use for the entire trail. + * + * **Default**: `0.1` + * + * **Argument**: {@link PropertyArgument.InstanceAge Instance age} + */ + textureFraction?: ScalarValue + /** + * Controls how fast the UV coordinates should move horizontally. + * + * **Default**: `0` + * + * **Argument**: {@link PropertyArgument.InstanceAge Instance age} + */ + speedU?: ScalarValue + /** + * Controls how much the UV coordinates should be randomly offset by per segment. + * + * **Default**: `0` + * + * **Argument**: {@link PropertyArgument.InstanceAge Instance age} + */ + varianceV?: ScalarValue + /** + * Scalar multiplier for the color that does not affect the alpha. Effectively a brightness multiplier. + * + * **Default**: `1` + * + * **Argument**: {@link PropertyArgument.EffectAge Effect age} + */ + rgbMultiplier?: ScalarValue + /** + * Alpha multiplier. + * + * **Default**: `1` + * + * **Argument**: {@link PropertyArgument.EffectAge Effect age} + */ + alphaMultiplier?: ScalarValue + /** + * Unknown integer. + * + * **Default**: `0` + */ + unk_ds3_f1_7?: number + /** + * Unknown integer. + * + * **Default**: `0` + */ + unk_ds3_f1_8?: number + /** + * Unknown float. + * + * **Default**: `0` + */ + unk_ds3_f1_9?: number + /** + * Unknown integer. + * + * **Default**: `-1` + */ + unk_ds3_f1_13?: number + /** + * Unknown integer. + * + * **Default**: `-1` + */ + unk_ds3_f1_14?: number + /** + * Unknown integer. + * + * **Default**: `0` + */ + unk_ds3_f1_15?: number + /** + * Unknown integer. + * + * **Default**: `0` + */ + unk_ds3_f2_0?: number + /** + * Unknown boolean. + * + * **Default**: `0` + */ + unk_ds3_f2_1?: boolean + /** + * Unknown integer. + * + * **Default**: `8` + */ + unk_ds3_f2_2?: number + /** + * Unknown integer. + * + * **Default**: `0` + */ + unk_ds3_f2_3?: number + /** + * Unknown integer. + * + * **Default**: `1` + */ + unk_ds3_f2_4?: number + /** + * Unknown integer. + * + * **Default**: `0` + */ + unk_ds3_f2_9?: number + /** + * Unknown integer. + * + * **Default**: `0` + */ + unk_ds3_f2_10?: number + /** + * Unknown integer. + * + * **Default**: `0` + */ + unk_ds3_f2_11?: number + /** + * Unknown integer. + * + * **Default**: `0` + */ + unk_ds3_f2_12?: number + /** + * Unknown integer. + * + * **Default**: `0` + */ + unk_ds3_f2_13?: number + /** + * Unknown float. + * + * **Default**: `-1` + */ + unkDistFadeClose0?: number + /** + * Unknown float. + * + * **Default**: `-1` + */ + unkDistFadeClose1?: number + /** + * Unknown float. + * + * **Default**: `-1` + */ + unkDistFadeFar0?: number + /** + * Unknown float. + * + * **Default**: `-1` + */ + unkDistFadeFar1?: number + /** + * Unknown integer. + * + * **Default**: `0` + */ + unk_ds3_f2_20?: number + /** + * Unknown integer. + * + * **Default**: `0` + */ + unk_ds3_f2_21?: number + /** + * Unknown integer. + * + * **Default**: `0` + */ + unk_ds3_f2_22?: number + /** + * Unknown integer. + * + * **Default**: `0` + */ + unk_ds3_f2_23?: number + /** + * Unknown integer. + * + * **Default**: `0` + */ + unk_ds3_f2_24?: number + /** + * Unknown float. + * + * **Default**: `1` + */ + unkDepthBlend1?: number + /** + * Unknown float. + * + * **Default**: `0` + */ + unkDepthBlend2?: number + /** + * Unknown integer. + * + * **Default**: `1` + */ + unk_ds3_f2_27?: number + /** + * Unknown integer. + * + * **Default**: `0` + */ + unk_ds3_f2_28?: number + /** + * Unknown float. + * + * **Default**: `5` + */ + unk_ds3_f2_29?: number + /** + * Unknown. + * + * **Default**: `0` + */ + unk_ds3_p1_2?: ScalarValue + /** + * Unknown. + * + * **Default**: `0` + */ + unk_ds3_p1_3?: ScalarValue + /** + * Unknown. + * + * **Default**: `-1` + */ + unk_ds3_p1_13?: ScalarValue + /** + * Controls the intensity of the distortion effect. At 0, there is no distortion at all. + * + * **Default**: `0` + * + * **Argument**: {@link PropertyArgument.EffectAge Effect age} + * + * See also: + * - {@link normalMap} + */ + distortionIntensity?: ScalarValue + /** + * Unknown. + * + * **Default**: `[1, 1, 1, 1]` + */ + unk_ds3_p2_3?: Vector4Value + /** + * Unknown. + * + * **Default**: `[1, 1, 1, 1]` + */ + unk_ds3_p2_4?: Vector4Value + /** + * Unknown. + * + * **Default**: `[1, 1, 1, 1]` + */ + unk_ds3_p2_5?: Vector4Value + /** + * Unknown. + * + * **Default**: `0` + */ + unk_ds3_p2_6?: ScalarValue + /** + * Unknown integer. + * + * **Default**: `0` + */ + unk_sdt_f2_31?: number + /** + * Unknown integer. + * + * **Default**: `1` + */ + unk_sdt_f2_32?: number + /** + * Unknown integer. + * + * **Default**: `-2` + */ + unk_sdt_f2_36?: number + /** + * Unknown integer. + * + * **Default**: `0` + */ + unk_sdt_f2_37?: number + /** + * Unknown integer. + * + * **Default**: `1` + */ + unk_er_f1_18?: number + /** + * Unknown integer. + * + * **Default**: `1` + */ + unk_er_f1_19?: number + /** + * Unknown integer. + * + * **Default**: `0` + */ + unk_er_f1_20?: number + /** + * Unknown integer. + * + * **Default**: `0` + */ + unk_er_f1_21?: number + /** + * Unknown integer. + * + * **Default**: `0` + */ + unk_er_f2_39?: number + /** + * Unknown float. + * + * **Default**: `1` + */ + unk_er_f2_40?: number + /** + * Unknown integer. + * + * **Default**: `1` + */ + unk_sdt_f1_14?: number + /** + * Unknown float. + * + * **Default**: `1` + */ + unk_sdt_f1_15?: number + /** + * Unknown float. + * + * **Default**: `1` + */ + unk_sdt_f1_16?: number + /** + * Unknown float. + * + * **Default**: `1` + */ + unk_sdt_f1_17?: number +} + +/** + * Creates a trail behind moving effects. + * + * This is slightly different from {@link Tracer}, as the trail from this is less visible when it's moving slower. + */ +class DynamicTracer extends DataAction { + declare type: ActionType.DynamicTracer + /** + * Tracer orientation mode. See {@link TracerOrientationMode} for more information. + */ + orientation: TracerOrientationMode + /** + * Normal map texture ID. + * + * This is used to control the distortion effect of the trail. + * + * See also: + * - {@link distortionIntensity} + */ + normalMap: number + /** + * The trail is made up of multiple quads, or *segments*. This controls how many seconds to wait between new segments being created. Lower values produce a smoother trail. + */ + segmentInterval: number + /** + * The trail is made up of multiple quads, or *segments*. This controls how long each segment should last in seconds. + */ + segmentDuration: number + /** + * The trail is made up of multiple quads, or *segments*. This controls how many segments may exist at the same time. + */ + concurrentSegments: number + /** + * To split the texture into multiple animation frames, this value must be set to the number of columns in the texture. It should equal `textureWidth / frameWidth`. + * + * See also: + * - {@link totalFrames} + */ + columns: number + /** + * To split the texture into multiple animation frames, this value must be set to the total number of frames in the texture. + * + * See also: + * - {@link columns} + */ + totalFrames: number + /** + * Controls whether or not the UV of the trail should be attached to the node or not. If it is attached, the texture will slide along the segments to follow the source wherever it moves, as if it was a flag attached to a pole. If it is not attached, the texture will stay where it was when the segment was created, like a skid mark on a road where the road is the segments and the mark is the texture, it wouldn't follow the car/node that made it. + */ + attachedUV: boolean + /** + * Controls the redness of the color of the additional bloom effect. The colors of the particle will be multiplied with this color to get the final color of the bloom effect. + * + * Note: + * - This has no effect if the "Effects Quality" setting is set to "Low". + * - This does not affect the natural bloom effect from high color values. + * + * See also: + * - {@link bloomGreen} + * - {@link bloomBlue} + * - {@link bloomStrength} + */ + bloomRed: number + /** + * Controls the greenness of the color of the additional bloom effect. The colors of the particle will be multiplied with this color to get the final color of the bloom effect. + * + * Note: + * - This has no effect if the "Effects Quality" setting is set to "Low". + * - This does not affect the natural bloom effect from high color values. + * + * See also: + * - {@link bloomRed} + * - {@link bloomBlue} + * - {@link bloomStrength} + */ + bloomGreen: number + /** + * Controls the blueness of the color of the additional bloom effect. The colors of the particle will be multiplied with this color to get the final color of the bloom effect. + * + * Note: + * - This has no effect if the "Effects Quality" setting is set to "Low". + * - This does not affect the natural bloom effect from high color values. + * + * See also: + * - {@link bloomRed} + * - {@link bloomGreen} + * - {@link bloomStrength} + */ + bloomBlue: number + /** + * Controls the strength of the additional bloom effect. + * + * Note: + * - This has no effect if the "Effects Quality" setting is set to "Low". + * - This does not affect the natural bloom effect from high color values. + * + * See also: + * - {@link bloomRed} + * - {@link bloomGreen} + * - {@link bloomBlue} + */ + bloomStrength: number + /** + * Minimum view distance. If the trail is closer than this distance from the camera, it will be hidden. Can be set to -1 to disable the limit. + * + * See also: + * - {@link maxDistance} + */ + minDistance: number + /** + * Maximum view distance. If the trail is farther away than this distance from the camera, it will be hidden. Can be set to -1 to disable the limit. + * + * See also: + * - {@link minDistance} + */ + maxDistance: number + /** + * Controls how dark shaded parts of the trail are. + */ + shadowDarkness: number + /** + * Specular texture ID. + * + * See also: + * - {@link lighting} + * - {@link glossiness} + * - {@link specularity} + */ + specular: number + /** + * Controls how sharp the specular highlights are. + * + * See also: + * - {@link lighting} + * - {@link specular} + * - {@link specularity} + */ + glossiness: number + /** + * Controls how the trail is lit. See {@link LightingMode} for more information. + */ + lighting: LightingMode + /** + * Controls how bright the specular highlights are. + * + * See also: + * - {@link lighting} + * - {@link specular} + * - {@link glossiness} + */ + specularity: number + /** + * Texture ID. + * + * **Argument**: {@link PropertyArgument.Constant0 Constant 0} + */ + texture: ScalarValue + /** + * Blend mode. + * + * **Argument**: {@link PropertyArgument.Constant0 Constant 0} + */ + blendMode: BlendMode | ScalarProperty + /** + * The width of the trail. + * + * **Argument**: {@link PropertyArgument.InstanceAge Instance age} + */ + width: ScalarValue + /** + * Multiplier for {@link width}. + * + * **Argument**: {@link PropertyArgument.InstanceAge Instance age} + */ + widthMultiplier: ScalarValue + /** + * Color multiplier. + * + * **Argument**: {@link PropertyArgument.InstanceAge Instance age} + */ + color1: Vector4Value + /** + * Color multiplier. + * + * **Argument**: {@link PropertyArgument.EmissionTime Emission time} + */ + color2: Vector4Value + /** + * Color multiplier. + * + * **Argument**: {@link PropertyArgument.InstanceAge Instance age} + */ + color3: Vector4Value + /** + * Parts of the particle with less opacity than this threshold will be invisible. The range is 0-255. + * + * **Argument**: {@link PropertyArgument.InstanceAge Instance age} + */ + alphaThreshold: ScalarValue + /** + * The index of the frame to show from the texture atlas. Can be animated using a {@link PropertyFunction.Linear linear property} or similar. + * + * Seemingly identical to {@link frameIndexOffset}? The sum of these two properties is the actual frame index that gets used. + * + * **Argument**: {@link PropertyArgument.InstanceAge Instance age} + */ + frameIndex: ScalarValue + /** + * Seemingly identical to {@link frameIndex}? The sum of these two properties is the actual frame index that gets used. + * + * **Argument**: {@link PropertyArgument.InstanceAge Instance age} + */ + frameIndexOffset: ScalarValue + /** + * Controls how much of the texture's width is used per segment. If {@link attachedUV} is enabled, this instead controls how much of the texture's width to use for the entire trail. + * + * **Argument**: {@link PropertyArgument.InstanceAge Instance age} + */ + textureFraction: ScalarValue + /** + * Controls how fast the UV coordinates should move horizontally. + * + * **Argument**: {@link PropertyArgument.InstanceAge Instance age} + */ + speedU: ScalarValue + /** + * Controls how much the UV coordinates should be randomly offset by per segment. + * + * **Argument**: {@link PropertyArgument.InstanceAge Instance age} + */ + varianceV: ScalarValue + /** + * Scalar multiplier for the color that does not affect the alpha. Effectively a brightness multiplier. + * + * **Argument**: {@link PropertyArgument.EffectAge Effect age} + */ + rgbMultiplier: ScalarValue + /** + * Alpha multiplier. + * + * **Argument**: {@link PropertyArgument.EffectAge Effect age} + */ + alphaMultiplier: ScalarValue + unk_ds3_f1_7: number + unk_ds3_f1_8: number + unk_ds3_f1_9: number + unk_ds3_f1_13: number + unk_ds3_f1_14: number + unk_ds3_f1_15: number + unk_ds3_f2_0: number + unk_ds3_f2_1: boolean + unk_ds3_f2_2: number + unk_ds3_f2_3: number + unk_ds3_f2_4: number + unk_ds3_f2_9: number + unk_ds3_f2_10: number + unk_ds3_f2_11: number + unk_ds3_f2_12: number + unk_ds3_f2_13: number + unkDistFadeClose0: number + unkDistFadeClose1: number + unkDistFadeFar0: number + unkDistFadeFar1: number + unk_ds3_f2_20: number + unk_ds3_f2_21: number + unk_ds3_f2_22: number + unk_ds3_f2_23: number + unk_ds3_f2_24: number + unkDepthBlend1: number + unkDepthBlend2: number + unk_ds3_f2_27: number + unk_ds3_f2_28: number + unk_ds3_f2_29: number + unk_ds3_p1_2: ScalarValue + unk_ds3_p1_3: ScalarValue + unk_ds3_p1_13: ScalarValue + /** + * Controls the intensity of the distortion effect. At 0, there is no distortion at all. + * + * **Argument**: {@link PropertyArgument.EffectAge Effect age} + * + * See also: + * - {@link normalMap} + */ + distortionIntensity: ScalarValue + unk_ds3_p2_3: Vector4Value + unk_ds3_p2_4: Vector4Value + unk_ds3_p2_5: Vector4Value + unk_ds3_p2_6: ScalarValue + unk_sdt_f2_31: number + unk_sdt_f2_32: number + unk_sdt_f2_36: number + unk_sdt_f2_37: number + unk_er_f1_18: number + unk_er_f1_19: number + unk_er_f1_20: number + unk_er_f1_21: number + unk_er_f2_39: number + unk_er_f2_40: number + unk_sdt_f1_14: number + unk_sdt_f1_15: number + unk_sdt_f1_16: number + unk_sdt_f1_17: number + constructor(props: DynamicTracerParams = {}) { + super(ActionType.DynamicTracer) + this.assign(props) + } +} + +export interface WaterInteractionParams { + /** + * The ID for a texture that controls the shape of the interaction. + * + * **Default**: `50004` + */ + texture?: number + /** + * Controls how deep to push the water, or how intense the ripples caused by the interaction are. + * + * **Default**: `1` + */ + depth?: number + /** + * Controls the size of the interaction area. Ripples caused by the interaction may go outside of the area. + * + * **Default**: `1` + */ + scale?: number + /** + * The time it takes for the water to be pushed down to the {@link depth} in seconds. + * + * **Default**: `0.15` + */ + descent?: number + /** + * The duration of the interaction in seconds. Basically how long to hold the water pressed down. + * + * **Default**: `0.15` + */ + duration?: number +} + +/** + * Simulates an interaction with water, allowing effects to create ripples in nearby water. The interaction basically pushes water in a shape controlled by a texture down to a given depth and holds it there for a duration before releasing it. + */ +class WaterInteraction extends DataAction { + declare type: ActionType.WaterInteraction + /** + * The ID for a texture that controls the shape of the interaction. + */ + texture: number + /** + * Controls how deep to push the water, or how intense the ripples caused by the interaction are. + */ + depth: number + /** + * Controls the size of the interaction area. Ripples caused by the interaction may go outside of the area. + */ + scale: number + /** + * The time it takes for the water to be pushed down to the {@link depth} in seconds. + */ + descent: number + /** + * The duration of the interaction in seconds. Basically how long to hold the water pressed down. + */ + duration: number + constructor(props: WaterInteractionParams = {}) { + super(ActionType.WaterInteraction) + this.assign(props) + } +} + +export interface RichModelParams { + /** + * Controls the orientation mode for the particles. See {@link OrientationMode} for more information. + * + * **Default**: {@link OrientationMode.LocalSouth} + */ + orientation?: OrientationMode + /** + * Each particle will pick a random number between this value and 1, and the width of the particle will be multiplied by this number. For example, setting this to 0.5 will make the particles randomly thinner, down to half width. Setting it to 2 will make them randomly wider, up to double width. + * + * If {@link uniformScale} is enabled, this also affects the height. + * + * **Default**: `1` + * + * See also: + * - {@link scaleVariationY} + * - {@link scaleVariationZ} + */ + scaleVariationX?: number + /** + * Each particle will pick a random number between this value and 1, and the height of the particle will be multiplied by this number. For example, setting this to 0.5 will make the particles randomly shorter, down to half height. Setting it to 2 will make them randomly taller, up to double height. + * + * If {@link uniformScale} is enabled, {@link scaleVariationX} also affects the height, and this field is ignored. + * + * **Default**: `1` + * + * See also: + * - {@link scaleVariationX} + * - {@link scaleVariationZ} + */ + scaleVariationY?: number + /** + * Each particle will pick a random number between this value and 1, and the depth of the particle will be multiplied by this number. For example, setting this to 0.5 will make the particles randomly shallower, down to half depth. Setting it to 2 will make them randomly deeper, up to double depth. + * + * If {@link uniformScale} is enabled, {@link scaleVariationX} also affects the depth, and this field is ignored. + * + * **Default**: `1` + * + * See also: + * - {@link scaleVariationX} + * - {@link scaleVariationY} + */ + scaleVariationZ?: number + /** + * If enabled, the particle X scale-related properties and fields will control the scale in all axes, and the Y and Z counterparts will be ignored. + * + * **Default**: `false` + * + * See also: + * - {@link sizeX} + * - {@link sizeY} + * - {@link sizeZ} + * - {@link scaleVariationX} + * - {@link scaleVariationY} + * - {@link scaleVariationZ} + */ + uniformScale?: boolean + /** + * Controls the redness of the color of the additional bloom effect. The colors of the particle will be multiplied with this color to get the final color of the bloom effect. + * + * Note: + * - This has no effect if the "Effects Quality" setting is set to "Low". + * - This does not affect the natural bloom effect from high color values. + * + * **Default**: `1` + * + * See also: + * - {@link bloomGreen} + * - {@link bloomBlue} + * - {@link bloomStrength} + */ + bloomRed?: number + /** + * Controls the greenness of the color of the additional bloom effect. The colors of the particle will be multiplied with this color to get the final color of the bloom effect. + * + * Note: + * - This has no effect if the "Effects Quality" setting is set to "Low". + * - This does not affect the natural bloom effect from high color values. + * + * **Default**: `1` + * + * See also: + * - {@link bloomRed} + * - {@link bloomBlue} + * - {@link bloomStrength} + */ + bloomGreen?: number + /** + * Controls the blueness of the color of the additional bloom effect. The colors of the particle will be multiplied with this color to get the final color of the bloom effect. + * + * Note: + * - This has no effect if the "Effects Quality" setting is set to "Low". + * - This does not affect the natural bloom effect from high color values. + * + * **Default**: `1` + * + * See also: + * - {@link bloomRed} + * - {@link bloomGreen} + * - {@link bloomStrength} + */ + bloomBlue?: number + /** + * Controls the strength of the additional bloom effect. + * + * Note: + * - This has no effect if the "Effects Quality" setting is set to "Low". + * - This does not affect the natural bloom effect from high color values. + * + * **Default**: `0` + * + * See also: + * - {@link bloomRed} + * - {@link bloomGreen} + * - {@link bloomBlue} + */ + bloomStrength?: number + /** + * Minimum view distance. If the particle is closer than this distance from the camera, it will be hidden. Can be set to -1 to disable the limit. + * + * **Default**: `-1` + * + * See also: + * - {@link maxDistance} + */ + minDistance?: number + /** + * Maximum view distance. If the particle is farther away than this distance from the camera, it will be hidden. Can be set to -1 to disable the limit. + * + * **Default**: `-1` + * + * See also: + * - {@link minDistance} + */ + maxDistance?: number + /** + * Model ID. + * + * **Default**: `80201` + * + * **Argument**: {@link PropertyArgument.Constant0 Constant 0} + */ + model?: ScalarValue + /** + * The width of the particle. + * + * If {@link uniformScale} is enabled, this also controls the height and depth. + * + * **Default**: `1` + * + * **Argument**: {@link PropertyArgument.InstanceAge Instance age} + * + * See also: + * - {@link scaleVariationX} + * - {@link sizeY} + * - {@link sizeZ} + */ + sizeX?: ScalarValue + /** + * The height of the particle. + * + * If {@link uniformScale} is enabled, {@link sizeX} also controls the height, and this property is ignored. + * + * **Default**: `1` + * + * **Argument**: {@link PropertyArgument.InstanceAge Instance age} + * + * See also: + * - {@link scaleVariationY} + * - {@link sizeX} + * - {@link sizeZ} + */ + sizeY?: ScalarValue + /** + * The depth of the particle. + * + * If {@link uniformScale} is enabled, {@link sizeX} also controls the depth, and this property is ignored. + * + * **Default**: `1` + * + * **Argument**: {@link PropertyArgument.InstanceAge Instance age} + * + * See also: + * - {@link scaleVariationZ} + * - {@link sizeX} + * - {@link sizeY} + */ + sizeZ?: ScalarValue + /** + * Rotation around the X-axis in degrees. + * + * **Default**: `0` + * + * **Argument**: {@link PropertyArgument.Constant0 Constant 0} + * + * See also: + * - {@link rotationSpeedX} + * - {@link rotationSpeedMultiplierX} + */ + rotationX?: ScalarValue + /** + * Rotation around the Y-axis in degrees. + * + * **Default**: `0` + * + * **Argument**: {@link PropertyArgument.Constant0 Constant 0} + * + * See also: + * - {@link rotationSpeedY} + * - {@link rotationSpeedMultiplierY} + */ + rotationY?: ScalarValue + /** + * Rotation around the Z-axis in degrees. + * + * **Default**: `0` + * + * **Argument**: {@link PropertyArgument.Constant0 Constant 0} + * + * See also: + * - {@link rotationSpeedZ} + * - {@link rotationSpeedMultiplierZ} + */ + rotationZ?: ScalarValue + /** + * Rotation speed around the X-axis in degrees per second. + * + * **Default**: `0` + * + * **Argument**: {@link PropertyArgument.InstanceAge Instance age} + * + * See also: + * - {@link rotationX} + * - {@link rotationSpeedMultiplierX} + */ + rotationSpeedX?: ScalarValue + /** + * Rotation speed around the Y-axis in degrees per second. + * + * **Default**: `0` + * + * **Argument**: {@link PropertyArgument.InstanceAge Instance age} + * + * See also: + * - {@link rotationY} + * - {@link rotationSpeedMultiplierY} + */ + rotationSpeedY?: ScalarValue + /** + * Rotation speed around the Z-axis in degrees per second. + * + * **Default**: `0` + * + * **Argument**: {@link PropertyArgument.InstanceAge Instance age} + * + * See also: + * - {@link rotationZ} + * - {@link rotationSpeedMultiplierZ} + */ + rotationSpeedZ?: ScalarValue + /** + * Multiplier for {@link rotationSpeedX}. + * + * **Default**: `1` + * + * **Argument**: {@link PropertyArgument.InstanceAge Instance age} + * + * See also: + * - {@link rotationX} + */ + rotationSpeedMultiplierX?: ScalarValue + /** + * Multiplier for {@link rotationSpeedY}. + * + * **Default**: `1` + * + * **Argument**: {@link PropertyArgument.InstanceAge Instance age} + * + * See also: + * - {@link rotationY} + */ + rotationSpeedMultiplierY?: ScalarValue + /** + * Multiplier for {@link rotationSpeedZ}. + * + * **Default**: `1` + * + * **Argument**: {@link PropertyArgument.InstanceAge Instance age} + * + * See also: + * - {@link rotationZ} + */ + rotationSpeedMultiplierZ?: ScalarValue + /** + * Color multiplier for the particle. + * + * **Default**: `[1, 1, 1, 1]` + * + * **Argument**: {@link PropertyArgument.InstanceAge Instance age} + */ + color1?: Vector4Value + /** + * Color multiplier for the particle. + * + * **Default**: `[1, 1, 1, 1]` + * + * **Argument**: {@link PropertyArgument.EmissionTime Emission time} + */ + color2?: Vector4Value + /** + * Color multiplier for the particle. + * + * **Default**: `[1, 1, 1, 1]` + * + * **Argument**: {@link PropertyArgument.InstanceAge Instance age} + */ + color3?: Vector4Value + /** + * Scalar multiplier for the color that does not affect the alpha. Effectively a brightness multiplier. + * + * **Default**: `1` + * + * **Argument**: {@link PropertyArgument.EffectAge Effect age} + */ + rgbMultiplier?: ScalarValue + /** + * Alpha multiplier. + * + * **Default**: `1` + * + * **Argument**: {@link PropertyArgument.EffectAge Effect age} + */ + alphaMultiplier?: ScalarValue + /** + * Animation ID. + * + * **Default**: `0` + * + * See also: + * - {@link loopAnimation} + * - {@link animationSpeed} + */ + animation?: number + /** + * If disabled, the animation will only play once and then freeze on the last frame. If enabled, the animation will loop. + * + * **Default**: `1` + * + * See also: + * - {@link animation} + * - {@link animationSpeed} + */ + loopAnimation?: boolean + /** + * Controls the speed at which the {@link animation} plays. + * + * **Default**: `1` + * + * See also: + * - {@link animation} + * - {@link loopAnimation} + */ + animationSpeed?: number + /** + * Unknown integer. + * + * **Default**: `1` + */ + unk_er_f1_5?: number + /** + * Unknown integer. + * + * **Default**: `1` + */ + unk_er_f1_6?: number + /** + * Unknown integer. + * + * **Default**: `0` + */ + unk_er_f1_7?: number + /** + * Unknown integer. + * + * **Default**: `-2` + */ + unk_er_f1_8?: number + /** + * Unknown integer. + * + * **Default**: `-2` + */ + unk_er_f1_9?: number + /** + * Unknown. Probably a boolean. Seems to just disable the {@link animation} if set to anything but 0. + * + * **Default**: `0` + */ + unk_er_f1_11?: number + /** + * Unknown integer. + * + * **Default**: `0` + */ + unk_er_f1_14?: number + /** + * Unknown integer. + * + * **Default**: `0` + */ + unk_er_f1_15?: number + /** + * Unknown integer. + * + * **Default**: `0` + */ + unk_er_f1_16?: number + /** + * Unknown integer. + * + * **Default**: `0` + */ + unk_er_f1_17?: number + /** + * Unknown integer. + * + * **Default**: `0` + */ + unk_er_f1_18?: number + /** + * Unknown integer. + * + * **Default**: `0` + */ + unk_er_f1_19?: number + /** + * Unknown integer. + * + * **Default**: `0` + */ + unk_er_f1_20?: number + /** + * Unknown integer. + * + * **Default**: `0` + */ + unk_er_f1_21?: number + /** + * Unknown integer. + * + * **Default**: `0` + */ + unk_er_f1_22?: number + /** + * Unknown integer. + * + * **Default**: `0` + */ + unk_er_f1_23?: number + /** + * Unknown integer. + * + * **Default**: `0` + */ + unk_er_f1_24?: number + /** + * Unknown integer. + * + * **Default**: `1` + */ + unk_er_f1_25?: number + /** + * Unknown integer. + * + * **Default**: `0` + */ + unk_er_f2_0?: number + /** + * Unknown integer. + * + * **Default**: `0` + */ + unk_er_f2_1?: number + /** + * Unknown integer. + * + * **Default**: `8` + */ + unk_er_f2_2?: number + /** + * Unknown integer. + * + * **Default**: `0` + */ + unk_er_f2_3?: number + /** + * Unknown integer. + * + * **Default**: `0` + */ + unk_er_f2_8?: number + /** + * Unknown integer. + * + * **Default**: `0` + */ + unk_er_f2_9?: number + /** + * Unknown integer. + * + * **Default**: `0` + */ + unk_er_f2_10?: number + /** + * Unknown integer. + * + * **Default**: `0` + */ + unk_er_f2_11?: number + /** + * Unknown integer. + * + * **Default**: `0` + */ + unk_er_f2_12?: number + /** + * Unknown integer. + * + * **Default**: `0` + */ + unk_er_f2_13?: number + /** + * Unknown float. + * + * **Default**: `-1` + */ + unkDistFadeClose0?: number + /** + * Unknown float. + * + * **Default**: `-1` + */ + unkDistFadeClose1?: number + /** + * Unknown float. + * + * **Default**: `-1` + */ + unkDistFadeFar0?: number + /** + * Unknown float. + * + * **Default**: `-1` + */ + unkDistFadeFar1?: number + /** + * Unknown integer. + * + * **Default**: `0` + */ + unk_er_f2_20?: number + /** + * Unknown integer. + * + * **Default**: `0` + */ + unk_er_f2_21?: number + /** + * Unknown integer. + * + * **Default**: `0` + */ + unk_er_f2_22?: number + /** + * Unknown integer. + * + * **Default**: `0` + */ + unk_er_f2_23?: number + /** + * Unknown integer. + * + * **Default**: `0` + */ + unk_er_f2_24?: number + /** + * Unknown float. + * + * **Default**: `1` + */ + unkDepthBlend1?: number + /** + * Unknown float. + * + * **Default**: `0` + */ + unkDepthBlend2?: number + /** + * Unknown integer. + * + * **Default**: `0` + */ + unk_er_f2_27?: number + /** + * Unknown integer. + * + * **Default**: `1` + */ + unk_er_f2_28?: number + /** + * Unknown integer. + * + * **Default**: `0` + */ + unk_er_f2_29?: number + /** + * Unknown integer. + * + * **Default**: `0` + */ + unk_er_f2_30?: number + /** + * Unknown float. + * + * **Default**: `1` + */ + unk_er_f2_31?: number + /** + * Unknown integer. + * + * **Default**: `0` + */ + unk_er_f2_32?: number + /** + * Unknown integer. + * + * **Default**: `0` + */ + unk_er_f2_33?: number + /** + * Unknown float. + * + * **Default**: `0.5` + */ + unk_er_f2_34?: number + /** + * Unknown integer. + * + * **Default**: `-2` + */ + unk_er_f2_35?: number + /** + * Unknown integer. + * + * **Default**: `-2` + */ + unk_er_f2_36?: number + /** + * Unknown integer. + * + * **Default**: `0` + */ + unk_er_f2_37?: number + /** + * Unknown. + * + * **Default**: `0` + */ + unk_er_p1_16?: ScalarValue + /** + * Unknown. + * + * **Default**: `0` + */ + unk_er_p1_17?: ScalarValue + /** + * Seemingly identical to {@link rgbMultiplier}? + * + * **Default**: `1` + * + * **Argument**: {@link PropertyArgument.EffectAge Effect age} + */ + rgbMultiplier2?: ScalarValue + /** + * Unknown. + * + * **Default**: `0` + */ + unk_er_p1_19?: ScalarValue + /** + * Unknown. + * + * **Default**: `0` + */ + unk_er_p1_20?: ScalarValue + /** + * Unknown. + * + * **Default**: `0` + */ + unk_ds3_p2_2?: ScalarValue + /** + * Unknown. + * + * **Default**: `[1, 1, 1, 1]` + */ + unk_ds3_p2_3?: Vector4Value + /** + * Unknown. + * + * **Default**: `[1, 1, 1, 1]` + */ + unk_ds3_p2_4?: Vector4Value + /** + * Unknown. + * + * **Default**: `[1, 1, 1, 1]` + */ + unk_ds3_p2_5?: Vector4Value + /** + * Unknown. + * + * **Default**: `0` + */ + unk_ds3_p2_6?: ScalarValue + /** + * Unknown float. + * + * **Default**: `0` + */ + unk_ac6_f1_24?: number + /** + * Unknown float. + * + * **Default**: `-1` + */ + unk_ac6_f1_25?: number + /** + * Unknown float. + * + * **Default**: `-1` + */ + unk_ac6_f1_26?: number + /** + * Unknown float. + * + * **Default**: `-1` + */ + unk_ac6_f1_27?: number + /** + * Unknown float. + * + * **Default**: `-1` + */ + unk_ac6_f1_28?: number + /** + * Unknown integer. + * + * **Default**: `0` + */ + unk_ac6_f1_29?: number + /** + * Unknown integer. + * + * **Default**: `0` + */ + unk_ac6_f1_30?: number + /** + * Unknown integer. + * + * **Default**: `0` + */ + unk_ac6_f1_31?: number + /** + * Unknown integer. + * + * **Default**: `0` + */ + unk_ac6_f1_32?: number + /** + * Unknown integer. + * + * **Default**: `1` + */ + unk_ac6_f1_33?: number + /** + * Unknown integer. + * + * **Default**: `0` + */ + unk_ac6_f1_34?: number + /** + * Offset for the UV coordinates of the model. + * + * **Default**: `[0, 0]` + * + * **Argument**: {@link PropertyArgument.Constant0 Constant 0} + * + * See also: + * - {@link uvSpeed} + */ + uvOffset?: Vector2Value + /** + * Scroll speed for the model's texture. + * + * **Default**: `[0, 0]` + * + * **Argument**: {@link PropertyArgument.InstanceAge Instance age} + * + * See also: + * - {@link uvSpeedMultiplier} + */ + uvSpeed?: Vector2Value + /** + * Multiplier for {@link uvSpeed} + * + * **Default**: `[1, 1]` + * + * **Argument**: {@link PropertyArgument.InstanceAge Instance age} + */ + uvSpeedMultiplier?: Vector2Value +} + +/** + * Particle with a 3D model. Similar to {@link Model}, but with some different options and seemingly no way to change the blend mode. + */ +class RichModel extends DataAction { + declare type: ActionType.RichModel + /** + * Controls the orientation mode for the particles. See {@link OrientationMode} for more information. + */ + orientation: OrientationMode + /** + * Each particle will pick a random number between this value and 1, and the width of the particle will be multiplied by this number. For example, setting this to 0.5 will make the particles randomly thinner, down to half width. Setting it to 2 will make them randomly wider, up to double width. + * + * If {@link uniformScale} is enabled, this also affects the height. + * + * See also: + * - {@link scaleVariationY} + * - {@link scaleVariationZ} + */ + scaleVariationX: number + /** + * Each particle will pick a random number between this value and 1, and the height of the particle will be multiplied by this number. For example, setting this to 0.5 will make the particles randomly shorter, down to half height. Setting it to 2 will make them randomly taller, up to double height. + * + * If {@link uniformScale} is enabled, {@link scaleVariationX} also affects the height, and this field is ignored. + * + * See also: + * - {@link scaleVariationX} + * - {@link scaleVariationZ} + */ + scaleVariationY: number + /** + * Each particle will pick a random number between this value and 1, and the depth of the particle will be multiplied by this number. For example, setting this to 0.5 will make the particles randomly shallower, down to half depth. Setting it to 2 will make them randomly deeper, up to double depth. + * + * If {@link uniformScale} is enabled, {@link scaleVariationX} also affects the depth, and this field is ignored. + * + * See also: + * - {@link scaleVariationX} + * - {@link scaleVariationY} + */ + scaleVariationZ: number + /** + * If enabled, the particle X scale-related properties and fields will control the scale in all axes, and the Y and Z counterparts will be ignored. + * + * See also: + * - {@link sizeX} + * - {@link sizeY} + * - {@link sizeZ} + * - {@link scaleVariationX} + * - {@link scaleVariationY} + * - {@link scaleVariationZ} + */ + uniformScale: boolean + /** + * Controls the redness of the color of the additional bloom effect. The colors of the particle will be multiplied with this color to get the final color of the bloom effect. + * + * Note: + * - This has no effect if the "Effects Quality" setting is set to "Low". + * - This does not affect the natural bloom effect from high color values. + * + * See also: + * - {@link bloomGreen} + * - {@link bloomBlue} + * - {@link bloomStrength} + */ + bloomRed: number + /** + * Controls the greenness of the color of the additional bloom effect. The colors of the particle will be multiplied with this color to get the final color of the bloom effect. + * + * Note: + * - This has no effect if the "Effects Quality" setting is set to "Low". + * - This does not affect the natural bloom effect from high color values. + * + * See also: + * - {@link bloomRed} + * - {@link bloomBlue} + * - {@link bloomStrength} + */ + bloomGreen: number + /** + * Controls the blueness of the color of the additional bloom effect. The colors of the particle will be multiplied with this color to get the final color of the bloom effect. + * + * Note: + * - This has no effect if the "Effects Quality" setting is set to "Low". + * - This does not affect the natural bloom effect from high color values. + * + * See also: + * - {@link bloomRed} + * - {@link bloomGreen} + * - {@link bloomStrength} + */ + bloomBlue: number + /** + * Controls the strength of the additional bloom effect. + * + * Note: + * - This has no effect if the "Effects Quality" setting is set to "Low". + * - This does not affect the natural bloom effect from high color values. + * + * See also: + * - {@link bloomRed} + * - {@link bloomGreen} + * - {@link bloomBlue} + */ + bloomStrength: number + /** + * Minimum view distance. If the particle is closer than this distance from the camera, it will be hidden. Can be set to -1 to disable the limit. + * + * See also: + * - {@link maxDistance} + */ + minDistance: number + /** + * Maximum view distance. If the particle is farther away than this distance from the camera, it will be hidden. Can be set to -1 to disable the limit. + * + * See also: + * - {@link minDistance} + */ + maxDistance: number + /** + * Model ID. + * + * **Argument**: {@link PropertyArgument.Constant0 Constant 0} + */ + model: ScalarValue + /** + * The width of the particle. + * + * If {@link uniformScale} is enabled, this also controls the height and depth. + * + * **Argument**: {@link PropertyArgument.InstanceAge Instance age} + * + * See also: + * - {@link scaleVariationX} + * - {@link sizeY} + * - {@link sizeZ} + */ + sizeX: ScalarValue + /** + * The height of the particle. + * + * If {@link uniformScale} is enabled, {@link sizeX} also controls the height, and this property is ignored. + * + * **Argument**: {@link PropertyArgument.InstanceAge Instance age} + * + * See also: + * - {@link scaleVariationY} + * - {@link sizeX} + * - {@link sizeZ} + */ + sizeY: ScalarValue + /** + * The depth of the particle. + * + * If {@link uniformScale} is enabled, {@link sizeX} also controls the depth, and this property is ignored. + * + * **Argument**: {@link PropertyArgument.InstanceAge Instance age} + * + * See also: + * - {@link scaleVariationZ} + * - {@link sizeX} + * - {@link sizeY} + */ + sizeZ: ScalarValue + /** + * Rotation around the X-axis in degrees. + * + * **Argument**: {@link PropertyArgument.Constant0 Constant 0} + * + * See also: + * - {@link rotationSpeedX} + * - {@link rotationSpeedMultiplierX} + */ + rotationX: ScalarValue + /** + * Rotation around the Y-axis in degrees. + * + * **Argument**: {@link PropertyArgument.Constant0 Constant 0} + * + * See also: + * - {@link rotationSpeedY} + * - {@link rotationSpeedMultiplierY} + */ + rotationY: ScalarValue + /** + * Rotation around the Z-axis in degrees. + * + * **Argument**: {@link PropertyArgument.Constant0 Constant 0} + * + * See also: + * - {@link rotationSpeedZ} + * - {@link rotationSpeedMultiplierZ} + */ + rotationZ: ScalarValue + /** + * Rotation speed around the X-axis in degrees per second. + * + * **Argument**: {@link PropertyArgument.InstanceAge Instance age} + * + * See also: + * - {@link rotationX} + * - {@link rotationSpeedMultiplierX} + */ + rotationSpeedX: ScalarValue + /** + * Rotation speed around the Y-axis in degrees per second. + * + * **Argument**: {@link PropertyArgument.InstanceAge Instance age} + * + * See also: + * - {@link rotationY} + * - {@link rotationSpeedMultiplierY} + */ + rotationSpeedY: ScalarValue + /** + * Rotation speed around the Z-axis in degrees per second. + * + * **Argument**: {@link PropertyArgument.InstanceAge Instance age} + * + * See also: + * - {@link rotationZ} + * - {@link rotationSpeedMultiplierZ} + */ + rotationSpeedZ: ScalarValue + /** + * Multiplier for {@link rotationSpeedX}. + * + * **Argument**: {@link PropertyArgument.InstanceAge Instance age} + * + * See also: + * - {@link rotationX} + */ + rotationSpeedMultiplierX: ScalarValue + /** + * Multiplier for {@link rotationSpeedY}. + * + * **Argument**: {@link PropertyArgument.InstanceAge Instance age} + * + * See also: + * - {@link rotationY} + */ + rotationSpeedMultiplierY: ScalarValue + /** + * Multiplier for {@link rotationSpeedZ}. + * + * **Argument**: {@link PropertyArgument.InstanceAge Instance age} + * + * See also: + * - {@link rotationZ} + */ + rotationSpeedMultiplierZ: ScalarValue + /** + * Color multiplier for the particle. + * + * **Argument**: {@link PropertyArgument.InstanceAge Instance age} + */ + color1: Vector4Value + /** + * Color multiplier for the particle. + * + * **Argument**: {@link PropertyArgument.EmissionTime Emission time} + */ + color2: Vector4Value + /** + * Color multiplier for the particle. + * + * **Argument**: {@link PropertyArgument.InstanceAge Instance age} + */ + color3: Vector4Value + /** + * Scalar multiplier for the color that does not affect the alpha. Effectively a brightness multiplier. + * + * **Argument**: {@link PropertyArgument.EffectAge Effect age} + */ + rgbMultiplier: ScalarValue + /** + * Alpha multiplier. + * + * **Argument**: {@link PropertyArgument.EffectAge Effect age} + */ + alphaMultiplier: ScalarValue + /** + * Animation ID. + * + * See also: + * - {@link loopAnimation} + * - {@link animationSpeed} + */ + animation: number + /** + * If disabled, the animation will only play once and then freeze on the last frame. If enabled, the animation will loop. + * + * See also: + * - {@link animation} + * - {@link animationSpeed} + */ + loopAnimation: boolean + /** + * Controls the speed at which the {@link animation} plays. + * + * See also: + * - {@link animation} + * - {@link loopAnimation} + */ + animationSpeed: number + unk_er_f1_5: number + unk_er_f1_6: number + unk_er_f1_7: number + unk_er_f1_8: number + unk_er_f1_9: number + /** + * Unknown. Probably a boolean. Seems to just disable the {@link animation} if set to anything but 0. + */ + unk_er_f1_11: number + unk_er_f1_14: number + unk_er_f1_15: number + unk_er_f1_16: number + unk_er_f1_17: number + unk_er_f1_18: number + unk_er_f1_19: number + unk_er_f1_20: number + unk_er_f1_21: number + unk_er_f1_22: number + unk_er_f1_23: number + unk_er_f1_24: number + unk_er_f1_25: number + unk_er_f2_0: number + unk_er_f2_1: number + unk_er_f2_2: number + unk_er_f2_3: number + unk_er_f2_8: number + unk_er_f2_9: number + unk_er_f2_10: number + unk_er_f2_11: number + unk_er_f2_12: number + unk_er_f2_13: number + unkDistFadeClose0: number + unkDistFadeClose1: number + unkDistFadeFar0: number + unkDistFadeFar1: number + unk_er_f2_20: number + unk_er_f2_21: number + unk_er_f2_22: number + unk_er_f2_23: number + unk_er_f2_24: number + unkDepthBlend1: number + unkDepthBlend2: number + unk_er_f2_27: number + unk_er_f2_28: number + unk_er_f2_29: number + unk_er_f2_30: number + unk_er_f2_31: number + unk_er_f2_32: number + unk_er_f2_33: number + unk_er_f2_34: number + unk_er_f2_35: number + unk_er_f2_36: number + unk_er_f2_37: number + unk_er_p1_16: ScalarValue + unk_er_p1_17: ScalarValue + /** + * Seemingly identical to {@link rgbMultiplier}? + * + * **Argument**: {@link PropertyArgument.EffectAge Effect age} + */ + rgbMultiplier2: ScalarValue + unk_er_p1_19: ScalarValue + unk_er_p1_20: ScalarValue + unk_ds3_p2_2: ScalarValue + unk_ds3_p2_3: Vector4Value + unk_ds3_p2_4: Vector4Value + unk_ds3_p2_5: Vector4Value + unk_ds3_p2_6: ScalarValue + unk_ac6_f1_24: number + unk_ac6_f1_25: number + unk_ac6_f1_26: number + unk_ac6_f1_27: number + unk_ac6_f1_28: number + unk_ac6_f1_29: number + unk_ac6_f1_30: number + unk_ac6_f1_31: number + unk_ac6_f1_32: number + unk_ac6_f1_33: number + unk_ac6_f1_34: number + /** + * Offset for the UV coordinates of the model. + * + * **Argument**: {@link PropertyArgument.Constant0 Constant 0} + * + * See also: + * - {@link uvSpeed} + */ + uvOffset: Vector2Value + /** + * Scroll speed for the model's texture. + * + * **Argument**: {@link PropertyArgument.InstanceAge Instance age} + * + * See also: + * - {@link uvSpeedMultiplier} + */ + uvSpeed: Vector2Value + /** + * Multiplier for {@link uvSpeed} + * + * **Argument**: {@link PropertyArgument.InstanceAge Instance age} + */ + uvSpeedMultiplier: Vector2Value + constructor(props: RichModelParams = {}) { + super(ActionType.RichModel) + this.assign(props) + } +} + +export interface Unk10500Params { + /** + * Controls how fast time passes for the entire effect. + * + * **Default**: `1` + * + * **Argument**: {@link PropertyArgument.EffectAge Effect age} + */ + rateOfTime?: ScalarValue + /** + * Unknown integer. + * + * **Default**: `0` + */ + unk_ds3_f1_0?: number + /** + * Unknown float. + * + * **Default**: `0` + */ + unk_ds3_f1_1?: number + /** + * Unknown integer. + * + * **Default**: `0` + */ + unk_ds3_f1_2?: number + /** + * Unknown float. + * + * **Default**: `0` + */ + unk_ds3_f1_3?: number + /** + * Unknown float. + * + * **Default**: `0` + */ + unk_ds3_f1_4?: number + /** + * Unknown float. + * + * **Default**: `0` + */ + unk_ds3_f1_5?: number + /** + * Unknown integer. + * + * **Default**: `0` + */ + unk_ds3_f1_6?: number + /** + * Unknown integer. + * + * **Default**: `0` + */ + unk_ds3_f1_7?: number + /** + * Unknown integer. + * + * **Default**: `0` + */ + unk_ds3_f1_8?: number + /** + * Unknown integer. + * + * **Default**: `0` + */ + unk_sdt_f1_9?: number +} + +/** + * Unknown root node action. + */ +class Unk10500 extends DataAction { + declare type: ActionType.Unk10500 + /** + * Controls how fast time passes for the entire effect. + * + * **Argument**: {@link PropertyArgument.EffectAge Effect age} + */ + rateOfTime: ScalarValue + unk_ds3_f1_0: number + unk_ds3_f1_1: number + unk_ds3_f1_2: number + unk_ds3_f1_3: number + unk_ds3_f1_4: number + unk_ds3_f1_5: number + unk_ds3_f1_6: number + unk_ds3_f1_7: number + unk_ds3_f1_8: number + unk_sdt_f1_9: number + constructor(props: Unk10500Params = {}) { + super(ActionType.Unk10500) + this.assign(props) + } +} + +export interface SpotLightParams { + /** + * Controls the diffuse color of the light. + * + * If {@link separateSpecular} is disabled, this also controls the specular color of the light. + * + * **Default**: `[1, 1, 1, 1]` + * + * **Argument**: {@link PropertyArgument.EffectAge Effect age} + */ + diffuseColor?: Vector4Value + /** + * Controls the specular color of the light. + * + * If {@link separateSpecular} is disabled, this property is ignored and {@link diffuseColor} controls both the diffuse as well as the specular color. + * + * **Default**: `[1, 1, 1, 1]` + * + * **Argument**: {@link PropertyArgument.EffectAge Effect age} + */ + specularColor?: Vector4Value + /** + * A scalar multiplier for the {@link diffuseColor diffuse color}. Good for easily adjusting the brightness of the light without changing the color. + * + * If {@link separateSpecular} is disabled, this also affects the specular color of the light. + * + * **Default**: `1` + * + * **Argument**: {@link PropertyArgument.EffectAge Effect age} + */ + diffuseMultiplier?: ScalarValue + /** + * A scalar multiplier for the {@link specularColor specular color}. + * + * If {@link separateSpecular} is disabled, this property is ignored. + * + * **Default**: `1` + * + * **Argument**: {@link PropertyArgument.EffectAge Effect age} + */ + specularMultiplier?: ScalarValue + /** + * Controls where the light starts in the cone. It bascially "slices off" the tip of the cone. If set to 0, it acts as if it is set to 0.5. + * + * **Default**: `0.01` + * + * **Argument**: {@link PropertyArgument.EffectAge Effect age} + */ + near?: ScalarValue + /** + * Controls how far away the base of the cone is from the light source. + * + * **Default**: `50` + * + * **Argument**: {@link PropertyArgument.EffectAge Effect age} + */ + far?: ScalarValue + /** + * The X radius for the elliptic base of the cone. + * + * **Default**: `50` + * + * **Argument**: {@link PropertyArgument.EffectAge Effect age} + */ + radiusX?: ScalarValue + /** + * The Y radius for the elliptic base of the cone. + * + * **Default**: `50` + * + * **Argument**: {@link PropertyArgument.EffectAge Effect age} + */ + radiusY?: ScalarValue + /** + * Toggles the jitter and flicker animations for the light. + * + * **Default**: `false` + * + * See also: + * - {@link jitterAcceleration} + * - {@link jitterX} + * - {@link jitterY} + * - {@link jitterZ} + * - {@link flickerIntervalMin} + * - {@link flickerIntervalMax} + * - {@link flickerBrightness} + */ + jitterAndFlicker?: boolean + /** + * Controls the acceleration of the jittering. + * + * **Default**: `1` + * + * See also: + * - {@link jitterAndFlicker} + * - {@link jitterX} + * - {@link jitterY} + * - {@link jitterZ} + */ + jitterAcceleration?: number + /** + * Controls how much the light should move around randomly on the X-axis. + * + * **Default**: `0` + * + * See also: + * - {@link jitterAndFlicker} + * - {@link jitterAcceleration} + * - {@link jitterY} + * - {@link jitterZ} + */ + jitterX?: number + /** + * Controls how much the light should move around randomly on the Y-axis. + * + * **Default**: `0` + * + * See also: + * - {@link jitterAndFlicker} + * - {@link jitterAcceleration} + * - {@link jitterX} + * - {@link jitterZ} + */ + jitterY?: number + /** + * Controls how much the light should move around randomly on the Z-axis. + * + * **Default**: `0` + * + * See also: + * - {@link jitterAndFlicker} + * - {@link jitterAcceleration} + * - {@link jitterX} + * - {@link jitterY} + */ + jitterZ?: number + /** + * Controls the minimum interval for flickering. + * + * **Default**: `0` + * + * See also: + * - {@link jitterAndFlicker} + * - {@link flickerIntervalMax} + * - {@link flickerBrightness} + */ + flickerIntervalMin?: number + /** + * Controls the maximum interval for flickering. + * + * **Default**: `1` + * + * See also: + * - {@link jitterAndFlicker} + * - {@link flickerIntervalMin} + * - {@link flickerBrightness} + */ + flickerIntervalMax?: number + /** + * Brightness multiplier for the light when it flickers. + * + * **Default**: `0.5` + * + * See also: + * - {@link jitterAndFlicker} + * - {@link flickerIntervalMin} + * - {@link flickerIntervalMax} + */ + flickerBrightness?: number + /** + * Controls if the light should have shadows or not. + * + * Note: Map objects also have a setting for casting shadows, and both must be enabled for an object to cast shadows from the light source. + * + * **Default**: `false` + */ + shadows?: boolean + /** + * When enabled, this allows other properties and fields of the action to control the specular color independently of the diffuse color. When disabled, the diffuse counterpart of the properties or fields will affect both the diffuse and specular color. + * + * **Default**: `false` + * + * See also: + * - {@link diffuseColor} + * - {@link specularColor} + * - {@link diffuseMultiplier} + * - {@link specularMultiplier} + */ + separateSpecular?: boolean + /** + * The number of seconds the light takes to fade to nothing after being destroyed. + * + * Due to how the field this represents works, the time will be rounded to the nearest multiple of 1/30s. + * + * **Default**: `0` + */ + fadeOutTime?: number + /** + * Controls how dark shadows from this light source are. At 0, the shadows will be entirely invisible. + * + * **Default**: `1` + */ + shadowDarkness?: number + /** + * Controls the density of some sort of fake fog in the volume hit by the light. The fog does not affect the actual light produced by the source and is not affected by shadows. + * + * **Default**: `0` + * + * See also: + * - {@link phaseFunction} + * - {@link asymmetryParam} + */ + volumeDensity?: number + /** + * Controls whether or not {@link asymmetryParam} affects the fake fog from {@link volumeDensity}. + * + * **Default**: `true` + */ + phaseFunction?: boolean + /** + * Controls how the fake fog from {@link volumeDensity} scatters the light. This value is ignored if {@link phaseFunction} is disabled, and the fog will scatter the light equally in all directions. + * + * - At 0, the light is scattered equally in every direction. + * - As the value approaches 1, the light is scattered more and more forward, in the same direction as the light was already traveling. This means that the fake fog will be less visible from the side or behind, and more visible from in front of the light. + * - At 1, the fog will not scatter the light at all, so it will be entirely invisible. + * - Values above 1 produce unnatural-looking effects where the light darkens the fog instead. + * + * **Default**: `0.75` + */ + asymmetryParam?: number + /** + * Controls the falloff exponent of the light. + * + * Note: This is possibly something else, but the behavior is pretty similar to a falloff exponent in a few ways. + * + * **Default**: `1` + */ + falloffExponent?: number + /** + * Unknown integer. + * + * **Default**: `1` + */ + unk_ds3_f1_0?: number + /** + * Unknown integer. + * + * **Default**: `2` + */ + unk_ds3_f1_3?: number + /** + * Unknown integer. + * + * **Default**: `1` + */ + unk_ds3_f1_4?: number + /** + * Unknown float. + * + * **Default**: `1` + */ + unk_ds3_f1_5?: number + /** + * Unknown integer. + * + * **Default**: `0` + */ + unk_ds3_f1_7?: number + /** + * Unknown integer. + * + * **Default**: `0` + */ + unk_ds3_f1_8?: number + /** + * Unknown. + * + * **Default**: `1` + */ + unk_ds3_p1_6?: ScalarValue + /** + * Unknown. + * + * **Default**: `1` + */ + unk_ds3_p1_7?: ScalarValue + /** + * Unknown integer. + * + * **Default**: `0` + */ + unk_sdt_f1_0?: number + /** + * Unknown float. + * + * **Default**: `0` + */ + unk_sdt_f1_3?: number + /** + * Unknown integer. + * + * **Default**: `100` + */ + unk_sdt_f1_16?: number + /** + * Unknown integer. + * + * **Default**: `0` + */ + unk_sdt_f1_17?: number + /** + * Unknown float. + * + * **Default**: `0` + */ + unk_sdt_f1_18?: number + /** + * Unknown float. + * + * **Default**: `0` + */ + unk_sdt_f1_20?: number + /** + * Unknown. + * + * **Default**: `1` + */ + unk_sdt_p1_10?: ScalarValue + /** + * Unknown integer. + * + * **Default**: `1` + */ + unk_er_f1_24?: number + /** + * Unknown float. + * + * **Default**: `1` + */ + unk_er_f1_25?: number + /** + * Unknown integer. + * + * **Default**: `1` + */ + unk_ac6_f1_26?: number + /** + * Unknown integer. + * + * **Default**: `0` + */ + unk_ac6_f1_27?: number +} + +/** + * Light source with an elliptic cone shape, a spot light. + */ +class SpotLight extends DataAction { + declare type: ActionType.SpotLight + /** + * Controls the diffuse color of the light. + * + * If {@link separateSpecular} is disabled, this also controls the specular color of the light. + * + * **Argument**: {@link PropertyArgument.EffectAge Effect age} + */ + diffuseColor: Vector4Value + /** + * Controls the specular color of the light. + * + * If {@link separateSpecular} is disabled, this property is ignored and {@link diffuseColor} controls both the diffuse as well as the specular color. + * + * **Argument**: {@link PropertyArgument.EffectAge Effect age} + */ + specularColor: Vector4Value + /** + * A scalar multiplier for the {@link diffuseColor diffuse color}. Good for easily adjusting the brightness of the light without changing the color. + * + * If {@link separateSpecular} is disabled, this also affects the specular color of the light. + * + * **Argument**: {@link PropertyArgument.EffectAge Effect age} + */ + diffuseMultiplier: ScalarValue + /** + * A scalar multiplier for the {@link specularColor specular color}. + * + * If {@link separateSpecular} is disabled, this property is ignored. + * + * **Argument**: {@link PropertyArgument.EffectAge Effect age} + */ + specularMultiplier: ScalarValue + /** + * Controls where the light starts in the cone. It bascially "slices off" the tip of the cone. If set to 0, it acts as if it is set to 0.5. + * + * **Argument**: {@link PropertyArgument.EffectAge Effect age} + */ + near: ScalarValue + /** + * Controls how far away the base of the cone is from the light source. + * + * **Argument**: {@link PropertyArgument.EffectAge Effect age} + */ + far: ScalarValue + /** + * The X radius for the elliptic base of the cone. + * + * **Argument**: {@link PropertyArgument.EffectAge Effect age} + */ + radiusX: ScalarValue + /** + * The Y radius for the elliptic base of the cone. + * + * **Argument**: {@link PropertyArgument.EffectAge Effect age} + */ + radiusY: ScalarValue + /** + * Toggles the jitter and flicker animations for the light. + * + * See also: + * - {@link jitterAcceleration} + * - {@link jitterX} + * - {@link jitterY} + * - {@link jitterZ} + * - {@link flickerIntervalMin} + * - {@link flickerIntervalMax} + * - {@link flickerBrightness} + */ + jitterAndFlicker: boolean + /** + * Controls the acceleration of the jittering. + * + * See also: + * - {@link jitterAndFlicker} + * - {@link jitterX} + * - {@link jitterY} + * - {@link jitterZ} + */ + jitterAcceleration: number + /** + * Controls how much the light should move around randomly on the X-axis. + * + * See also: + * - {@link jitterAndFlicker} + * - {@link jitterAcceleration} + * - {@link jitterY} + * - {@link jitterZ} + */ + jitterX: number + /** + * Controls how much the light should move around randomly on the Y-axis. + * + * See also: + * - {@link jitterAndFlicker} + * - {@link jitterAcceleration} + * - {@link jitterX} + * - {@link jitterZ} + */ + jitterY: number + /** + * Controls how much the light should move around randomly on the Z-axis. + * + * See also: + * - {@link jitterAndFlicker} + * - {@link jitterAcceleration} + * - {@link jitterX} + * - {@link jitterY} + */ + jitterZ: number + /** + * Controls the minimum interval for flickering. + * + * See also: + * - {@link jitterAndFlicker} + * - {@link flickerIntervalMax} + * - {@link flickerBrightness} + */ + flickerIntervalMin: number + /** + * Controls the maximum interval for flickering. + * + * See also: + * - {@link jitterAndFlicker} + * - {@link flickerIntervalMin} + * - {@link flickerBrightness} + */ + flickerIntervalMax: number + /** + * Brightness multiplier for the light when it flickers. + * + * See also: + * - {@link jitterAndFlicker} + * - {@link flickerIntervalMin} + * - {@link flickerIntervalMax} + */ + flickerBrightness: number + /** + * Controls if the light should have shadows or not. + * + * Note: Map objects also have a setting for casting shadows, and both must be enabled for an object to cast shadows from the light source. + */ + shadows: boolean + /** + * When enabled, this allows other properties and fields of the action to control the specular color independently of the diffuse color. When disabled, the diffuse counterpart of the properties or fields will affect both the diffuse and specular color. + * + * See also: + * - {@link diffuseColor} + * - {@link specularColor} + * - {@link diffuseMultiplier} + * - {@link specularMultiplier} + */ + separateSpecular: boolean + /** + * The number of seconds the light takes to fade to nothing after being destroyed. + * + * Due to how the field this represents works, the time will be rounded to the nearest multiple of 1/30s. + */ + fadeOutTime: number + /** + * Controls how dark shadows from this light source are. At 0, the shadows will be entirely invisible. + */ + shadowDarkness: number + /** + * Controls the density of some sort of fake fog in the volume hit by the light. The fog does not affect the actual light produced by the source and is not affected by shadows. + * + * See also: + * - {@link phaseFunction} + * - {@link asymmetryParam} + */ + volumeDensity: number + /** + * Controls whether or not {@link asymmetryParam} affects the fake fog from {@link volumeDensity}. + */ + phaseFunction: boolean + /** + * Controls how the fake fog from {@link volumeDensity} scatters the light. This value is ignored if {@link phaseFunction} is disabled, and the fog will scatter the light equally in all directions. + * + * - At 0, the light is scattered equally in every direction. + * - As the value approaches 1, the light is scattered more and more forward, in the same direction as the light was already traveling. This means that the fake fog will be less visible from the side or behind, and more visible from in front of the light. + * - At 1, the fog will not scatter the light at all, so it will be entirely invisible. + * - Values above 1 produce unnatural-looking effects where the light darkens the fog instead. + */ + asymmetryParam: number + /** + * Controls the falloff exponent of the light. + * + * Note: This is possibly something else, but the behavior is pretty similar to a falloff exponent in a few ways. + */ + falloffExponent: number + unk_ds3_f1_0: number + unk_ds3_f1_3: number + unk_ds3_f1_4: number + unk_ds3_f1_5: number + unk_ds3_f1_7: number + unk_ds3_f1_8: number + unk_ds3_p1_6: ScalarValue + unk_ds3_p1_7: ScalarValue + unk_sdt_f1_0: number + unk_sdt_f1_3: number + unk_sdt_f1_16: number + unk_sdt_f1_17: number + unk_sdt_f1_18: number + unk_sdt_f1_20: number + unk_sdt_p1_10: ScalarValue + unk_er_f1_24: number + unk_er_f1_25: number + unk_ac6_f1_26: number + unk_ac6_f1_27: number + constructor(props: SpotLightParams = {}) { + super(ActionType.SpotLight) + this.assign(props) + } +} +/*#ActionClasses end*/ + +const Actions = { + NodeMovement, + [ActionType.NodeAcceleration]: NodeMovement, NodeAcceleration: NodeMovement, + [ActionType.NodeSpin]: NodeMovement, NodeSpin: NodeMovement, + [ActionType.NodeAccelerationRandomTurns]: NodeMovement, NodeAccelerationRandomTurns: NodeMovement, + [ActionType.NodeAccelerationPartialFollow]: NodeMovement, NodeAccelerationPartialFollow: NodeMovement, + [ActionType.NodeAccelerationSpin]: NodeMovement, NodeAccelerationSpin: NodeMovement, + [ActionType.NodeSpeed]: NodeMovement, NodeSpeed: NodeMovement, + [ActionType.NodeSpeedRandomTurns]: NodeMovement, NodeSpeedRandomTurns: NodeMovement, + [ActionType.NodeSpeedPartialFollow]: NodeMovement, NodeSpeedPartialFollow: NodeMovement, + [ActionType.NodeSpeedSpin]: NodeMovement, NodeSpeedSpin: NodeMovement, + + NodeTransform, + [ActionType.StaticNodeTransform]: NodeTransform, StaticNodeTransform: NodeTransform, + [ActionType.RandomNodeTransform]: NodeTransform, RandomNodeTransform: NodeTransform, + + ParticleMovement, + [ActionType.ParticleAcceleration]: ParticleMovement, ParticleAcceleration: ParticleMovement, + [ActionType.ParticleSpeed]: ParticleMovement, ParticleSpeed: ParticleMovement, + [ActionType.ParticleSpeedRandomTurns]: ParticleMovement, ParticleSpeedRandomTurns: ParticleMovement, + [ActionType.ParticleSpeedPartialFollow]: ParticleMovement, ParticleSpeedPartialFollow: ParticleMovement, + [ActionType.ParticleAccelerationRandomTurns]: ParticleMovement, ParticleAccelerationRandomTurns: ParticleMovement, + [ActionType.ParticleAccelerationPartialFollow]: ParticleMovement, ParticleAccelerationPartialFollow: ParticleMovement, + + [ActionType.StateEffectMap]: StateEffectMap, StateEffectMap, + [ActionType.EmitAllParticles]: EmitAllParticles, EmitAllParticles, + [ActionType.EmitRandomParticles]: EmitRandomParticles, EmitRandomParticles, + [ActionType.OneTimeEmitter]: OneTimeEmitter, OneTimeEmitter, + [ActionType.NoParticleSpread]: NoParticleSpread, NoParticleSpread, +} + +const DataActions = { + /*#ActionsList start*/ + [ActionType.NodeTranslation]: NodeTranslation, NodeTranslation, + [ActionType.NodeAttachToCamera]: NodeAttachToCamera, NodeAttachToCamera, + [ActionType.PlaySound]: PlaySound, PlaySound, + [ActionType.NodeAttributes]: NodeAttributes, NodeAttributes, + [ActionType.ParticleAttributes]: ParticleAttributes, ParticleAttributes, + [ActionType.Unk130]: Unk130, Unk130, + [ActionType.ParticleModifier]: ParticleModifier, ParticleModifier, + [ActionType.SFXReference]: SFXReference, SFXReference, + [ActionType.LevelOfDetailThresholds]: LevelOfDetailThresholds, LevelOfDetailThresholds, + [ActionType.PeriodicEmitter]: PeriodicEmitter, PeriodicEmitter, + [ActionType.EqualDistanceEmitter]: EqualDistanceEmitter, EqualDistanceEmitter, + [ActionType.PointEmitterShape]: PointEmitterShape, PointEmitterShape, + [ActionType.DiskEmitterShape]: DiskEmitterShape, DiskEmitterShape, + [ActionType.RectangleEmitterShape]: RectangleEmitterShape, RectangleEmitterShape, + [ActionType.SphereEmitterShape]: SphereEmitterShape, SphereEmitterShape, + [ActionType.BoxEmitterShape]: BoxEmitterShape, BoxEmitterShape, + [ActionType.CylinderEmitterShape]: CylinderEmitterShape, CylinderEmitterShape, + [ActionType.CircularParticleSpread]: CircularParticleSpread, CircularParticleSpread, + [ActionType.EllipticalParticleSpread]: EllipticalParticleSpread, EllipticalParticleSpread, + [ActionType.RectangularParticleSpread]: RectangularParticleSpread, RectangularParticleSpread, + [ActionType.PointSprite]: PointSprite, PointSprite, + [ActionType.Line]: Line, Line, + [ActionType.QuadLine]: QuadLine, QuadLine, + [ActionType.BillboardEx]: BillboardEx, BillboardEx, + [ActionType.MultiTextureBillboardEx]: MultiTextureBillboardEx, MultiTextureBillboardEx, + [ActionType.Model]: Model, Model, + [ActionType.Tracer]: Tracer, Tracer, + [ActionType.Distortion]: Distortion, Distortion, + [ActionType.RadialBlur]: RadialBlur, RadialBlur, + [ActionType.PointLight]: PointLight, PointLight, + [ActionType.Unk701]: Unk701, Unk701, + [ActionType.NodeWindSpeed]: NodeWindSpeed, NodeWindSpeed, + [ActionType.ParticleWindSpeed]: ParticleWindSpeed, ParticleWindSpeed, + [ActionType.NodeWindAcceleration]: NodeWindAcceleration, NodeWindAcceleration, + [ActionType.ParticleWindAcceleration]: ParticleWindAcceleration, ParticleWindAcceleration, + [ActionType.Unk800]: Unk800, Unk800, + [ActionType.DynamicTracer]: DynamicTracer, DynamicTracer, + [ActionType.WaterInteraction]: WaterInteraction, WaterInteraction, + [ActionType.RichModel]: RichModel, RichModel, + [ActionType.Unk10500]: Unk10500, Unk10500, + [ActionType.SpotLight]: SpotLight, SpotLight, + /*#ActionsList end*/ +} + +class Field { + + type: FieldType + value: number | boolean + + constructor(type: FieldType = FieldType.Integer, value: number | boolean = 0) { + this.type = type + this.value = value + } + + /** + * Creates a copy of a field. + * @param field The field to copy. + * @returns The copy. + */ + static copy(field: T): T { + return new Field(field.type, field.value) as T + } + + static fromJSON({ + type, + value + }: { + type: string + value: number + }) { + return new Field(FieldType[type], value) + } + + toJSON() { + return { + type: FieldType[this.type], + value: this.value + } + } + +} + +export interface NumericalField extends Field { + value: number +} + +class BoolField extends Field { + + declare value: boolean + + constructor(value: boolean = false) { + super(FieldType.Boolean, value) + } + +} + +class IntField extends Field implements NumericalField { + + declare value: number + + constructor(value: number = 0) { + super(FieldType.Integer, value) + } + +} + +class FloatField extends Field implements NumericalField { + + declare value: number + + constructor(value: number = 0) { + super(FieldType.Float, value) + } + +} + +class Keyframe implements IKeyframe { + + position: number + value: ValueTypeMap[T] + unkTangent1?: ValueTypeMap[T] + unkTangent2?: ValueTypeMap[T] + + constructor( + position: number, + value: ValueTypeMap[T], + unkTangent1?: ValueTypeMap[T], + unkTangent2?: ValueTypeMap[T] + ) { + this.position = position + this.value = value + this.unkTangent1 = unkTangent1 + this.unkTangent2 = unkTangent2 + } + + static copy(orig: IKeyframe) { + return new Keyframe(orig.position, orig.value, orig.unkTangent1, orig.unkTangent2) + } + +} + +abstract class Property implements IModifiableProperty { + + valueType: T + function: F + modifiers: Modifier[] + + constructor( + valueType: T, + func: F, + modifiers: Modifier[] = [] + ) { + this.valueType = valueType + this.function = func + this.modifiers = modifiers + } + + get componentCount() { return this.valueType + 1 as 1 | 2 | 3 | 4 } + + withModifiers(...modifiers: Modifier[]) { + this.modifiers.push(...modifiers) + return this + } + + protected modifiersScale(factor: number) { + for (const mod of this.modifiers) { + const cc = mod.valueType + 1 + switch (mod.type) { + case ModifierType.Randomizer2: + for (let i = cc * 3 - 1; i >= cc * 2; i--) { + mod.fields[i].value *= factor + } + case ModifierType.Randomizer1: + for (let i = cc * 2 - 1; i >= cc; i--) { + mod.fields[i].value *= factor + } + } + } + } + + static fromJSON(obj: any) { + if (typeof obj === 'object' && 'function' in obj) { + switch (PropertyFunction[obj.function as string]) { + case PropertyFunction.Stepped: + case PropertyFunction.Linear: + case PropertyFunction.Curve1: + case PropertyFunction.Curve2: + return SequenceProperty.fromJSON(obj) + case PropertyFunction.CompCurve: + return ComponentSequenceProperty.fromJSON(obj) + } + } else { + return ValueProperty.fromJSON(obj) + } + } + + abstract fieldCount: number + abstract fields: NumericalField[] + abstract toJSON(): any + abstract scale(factor: number): this + abstract power(exponent: number): this + abstract add(summand: number): this + abstract valueAt(arg: number): ValueTypeMap[T] + abstract clone(): Property + abstract separateComponents(): Property[] + +} + +class ValueProperty + extends Property + implements IModifiableProperty, IValueProperty { + + value: ValueTypeMap[T] + + constructor( + valueType: T, + value: ValueTypeMap[T], + modifiers: Modifier[] = [] + ) { + super(valueType, PropertyFunction.Constant, modifiers) + this.value = value + } + + get isZero() { return Array.isArray(this.value) ? this.value.every(e => e === 0) : this.value === 0 } + get isOne() { return Array.isArray(this.value) ? this.value.every(e => e === 1) : this.value === 1 } + + get fieldCount(): number { + if (this.isZero || this.isOne) { + return 0 + } + return this.componentCount + } + + get fields(): NumericalField[] { + if (this.isZero || this.isOne) { + return [] + } + if (this.valueType === ValueType.Scalar) { + return [ new FloatField(this.value as number) ] + } + return (this.value as Vector).map(e => new FloatField(e)) + } + + static fromFields( + valueType: T, + func: ValuePropertyFunction, + modifiers: Modifier[], + fieldValues: number[] + ): ValueProperty { + switch (func) { + case PropertyFunction.Zero: + return new ConstantProperty(...(Array(valueType + 1).fill(0) as [number] | Vector)).withModifiers( + ...modifiers + ) as unknown as ValueProperty + case PropertyFunction.One: + return new ConstantProperty(...(Array(valueType + 1).fill(1) as [number] | Vector)).withModifiers( + ...modifiers + ) as unknown as ValueProperty + case PropertyFunction.Constant: + return new ConstantProperty(...(fieldValues as [number] | Vector)).withModifiers( + ...modifiers + ) as unknown as ValueProperty + default: + throw new Error('Incompatible or unknown function in property: ' + func) + } + } + + static fromJSON(obj: { + value: PropertyValue + modifiers?: any[] + } | PropertyValue) { + if (Array.isArray(obj)) { + return new ConstantProperty(...obj) + } else if (typeof obj === 'number') { + return new ConstantProperty(obj) + } + return new ConstantProperty(...(Array.isArray(obj.value) ? obj.value : [obj.value])).withModifiers( + ...(obj.modifiers ?? []).map(e => Modifier.fromJSON(e)) + ) + } + + toJSON() { + if (this.modifiers.length > 0) { + return { + value: this.value, + modifiers: this.modifiers.map(mod => mod.toJSON()) + } + } else { + return this.value + } + } + + scale(factor: number) { + if (this.valueType === ValueType.Scalar) { + (this.value as number) *= factor + } else { + this.value = (this.value as Vector).map(e => e * factor) as ValueTypeMap[T] + } + this.modifiersScale(factor) + return this + } + + power(exponent: number) { + if (this.valueType === ValueType.Scalar) { + (this.value as number) **= exponent + } else { + this.value = (this.value as Vector).map(e => e ** exponent) as ValueTypeMap[T] + } + return this + } + + add(summand: number) { + if (this.valueType === ValueType.Scalar) { + (this.value as number) += summand + } else { + this.value = (this.value as Vector).map(e => e + summand) as ValueTypeMap[T] + } + return this + } + + valueAt(arg: number): ValueTypeMap[T] { + return this.value + } + + clone(): ValueProperty { + return new ValueProperty(this.valueType, this.value, this.modifiers.map(e => Modifier.copy(e))) + } + + separateComponents(): ValueProperty[] { + if (this.valueType === ValueType.Scalar) { + return [this.clone() as ValueProperty] + } else { + const mods = this.modifiers.map(e => e.separateComponents()) + return (this.value as Vector).map((e, i) => new ConstantProperty(e).withModifiers( + ...mods.map(comps => comps[i]) + )) + } + } + +} + +class SequenceProperty + extends Property + implements IModifiableProperty, ISequenceProperty { + + loop: boolean + keyframes: IKeyframe[] + + constructor( + valueType: T, + func: F, + loop: boolean = false, + keyframes: IKeyframe[] = [], + modifiers: Modifier[] = [] + ) { + super(valueType, func, modifiers) + this.loop = loop + this.keyframes = keyframes + } + + sortKeyframes() { + this.keyframes.sort((a, b) => a.position - b.position) + } + + get fieldCount(): number { + switch (this.function) { + case PropertyFunction.Stepped: + case PropertyFunction.Linear: + return 1 + 2 * this.componentCount + (1 + this.componentCount) * this.keyframes.length + case PropertyFunction.Curve1: + case PropertyFunction.Curve2: + return 1 + 2 * this.componentCount + (1 + 3 * this.componentCount) * this.keyframes.length + } + } + + get fields(): NumericalField[] { + const cc = this.componentCount + switch (this.function) { + case PropertyFunction.Stepped: + case PropertyFunction.Linear: + this.sortKeyframes() + return [ + new IntField(this.keyframes.length), + ...(cc === 1 ? + [Math.min(...this.keyframes.map(e => e.value as number))] : + arrayOf(cc, i => Math.min(...this.keyframes.map(e => e.value[i]))) + ).map(e => new FloatField(e)), + ...(cc === 1 ? + [Math.max(...this.keyframes.map(e => e.value as number))] : + arrayOf(cc, i => Math.max(...this.keyframes.map(e => e.value[i]))) + ).map(e => new FloatField(e)), + ...this.keyframes.map(e => new FloatField(e.position)), + ...this.keyframes.flatMap( + this.valueType === ValueType.Scalar ? + e => [ new FloatField(e.value as number) ] : + e => (e.value as Vector).map(e => new FloatField(e)) + ) + ] + case PropertyFunction.Curve1: + case PropertyFunction.Curve2: + this.sortKeyframes() + return [ + new IntField(this.keyframes.length), + ...(cc === 1 ? + [Math.min(...this.keyframes.map(e => e.value as number))] : + arrayOf(cc, i => Math.min(...this.keyframes.map(e => e.value[i]))) + ).map(e => new FloatField(e)), + ...(cc === 1 ? + [Math.max(...this.keyframes.map(e => e.value as number))] : + arrayOf(cc, i => Math.max(...this.keyframes.map(e => e.value[i]))) + ).map(e => new FloatField(e)), + ...this.keyframes.map(e => new FloatField(e.position)), + ...this.keyframes.flatMap( + this.valueType === ValueType.Scalar ? + e => [ new FloatField(e.value as number) ] : + e => (e.value as Vector).map(e => new FloatField(e)) + ), + ...this.keyframes.flatMap( + this.valueType === ValueType.Scalar ? + e => [ new FloatField(e.unkTangent1 as number) ] : + e => (e.unkTangent1 as Vector).map(e => new FloatField(e)) + ), + ...this.keyframes.flatMap( + this.valueType === ValueType.Scalar ? + e => [ new FloatField(e.unkTangent2 as number) ] : + e => (e.unkTangent2 as Vector).map(e => new FloatField(e)) + ), + ] + default: + throw new Error('Incompatible or unknown function in property: ' + this.function) + } + } + + static fromFields( + valueType: T, + func: F, + loop: boolean, + modifiers: Modifier[], + fieldValues: number[] + ): SequenceProperty { + switch (func) { + case PropertyFunction.Stepped: + case PropertyFunction.Linear: + return new SequenceProperty(valueType, func, loop, arrayOf( + fieldValues[0], + i => new Keyframe( + fieldValues[1 + 2 * (valueType + 1) + i], + (valueType === ValueType.Scalar ? + fieldValues[1 + (2 + i) * (valueType + 1) + fieldValues[0]] : + fieldValues.slice(1 + (2 + i) * (valueType + 1) + fieldValues[0], 1 + (2 + i) * (valueType + 1) + fieldValues[0] + (valueType + 1)) as Vector + ) as ValueTypeMap[T] + ) + ), modifiers) + case PropertyFunction.Curve1: + case PropertyFunction.Curve2: + return new SequenceProperty(valueType, func, loop, arrayOf( + fieldValues[0], + i => new Keyframe( + fieldValues[1 + 2 * (valueType + 1) + i], + (valueType === ValueType.Scalar ? + fieldValues[1 + (2 + i) * (valueType + 1) + fieldValues[0]] : + fieldValues.slice(1 + (2 + i) * (valueType + 1) + fieldValues[0], 1 + (2 + i) * (valueType + 1) + fieldValues[0] + (valueType + 1)) as Vector + ) as ValueTypeMap[T], + (valueType === ValueType.Scalar ? + fieldValues[1 + (2 + i + fieldValues[0]) * (valueType + 1) + fieldValues[0]] : + fieldValues.slice(1 + (2 + i + fieldValues[0]) * (valueType + 1) + fieldValues[0], 1 + (2 + i + fieldValues[0]) * (valueType + 1) + fieldValues[0] + (valueType + 1)) as Vector + ) as ValueTypeMap[T], + (valueType === ValueType.Scalar ? + fieldValues[1 + (2 + i + 2 * fieldValues[0]) * (valueType + 1) + fieldValues[0]] : + fieldValues.slice(1 + (2 + i + 2 * fieldValues[0]) * (valueType + 1) + fieldValues[0], 1 + (2 + i + 2 * fieldValues[0]) * (valueType + 1) + fieldValues[0] + (valueType + 1)) as Vector + ) as ValueTypeMap[T], + ) + ), modifiers) + default: + throw new Error('Incompatible or unknown function in property: ' + func) + } + } + + static fromJSON(obj: { + function: string + modifiers?: any[] + keyframes?: IKeyframe[] + loop?: boolean + }) { + return new SequenceProperty( + Array.isArray(obj.keyframes[0].value) ? obj.keyframes[0].value.length - 1 : ValueType.Scalar, + PropertyFunction[obj.function], + obj.loop ?? false, + obj.keyframes + ) + } + + toJSON() { + switch (this.function) { + case PropertyFunction.Stepped: + case PropertyFunction.Linear: { + const o: { + function: string + loop?: boolean + keyframes?: any[] + modifiers?: any[] + } = { + function: PropertyFunction[this.function], + } + if (this.function > PropertyFunction.Constant) o.loop = this.loop + o.keyframes = this.keyframes.map(e => ({ + position: e.position, + value: e.value + })) + if (this.modifiers.length > 0) o.modifiers = this.modifiers.map(mod => mod.toJSON()) + return o + } + case PropertyFunction.Curve1: + case PropertyFunction.Curve2: { + const o: { + function: string + loop?: boolean + keyframes?: any[] + modifiers?: any[] + } = { + function: PropertyFunction[this.function], + } + if (this.function > PropertyFunction.Constant) o.loop = this.loop + o.keyframes = this.keyframes.map(e => ({ + position: e.position, + value: e.value, + unkTangent1: e.unkTangent1, + unkTangent2: e.unkTangent2, + })) + if (this.modifiers.length > 0) o.modifiers = this.modifiers.map(mod => mod.toJSON()) + return o + } + } + } + + scale(factor: number) { + for (const kf of this.keyframes) { + if (this.valueType === ValueType.Scalar) { + (kf.value as number) *= factor + } else { + kf.value = (kf.value as Vector).map(e => e * factor) as ValueTypeMap[T] + } + } + this.modifiersScale(factor) + return this + } + + power(exponent: number) { + for (const kf of this.keyframes) { + if (this.valueType === ValueType.Scalar) { + (kf.value as number) **= exponent + } else { + kf.value = (kf.value as Vector).map(e => e ** exponent) as ValueTypeMap[T] + } + } + return this + } + + add(summand: number) { + for (const kf of this.keyframes) { + if (this.valueType === ValueType.Scalar) { + (kf.value as number) += summand + } else { + kf.value = (kf.value as Vector).map(e => e + summand) as ValueTypeMap[T] + } + } + return this + } + + valueAt(arg: number): ValueTypeMap[T] { + switch (this.function) { + case PropertyFunction.Stepped: { + let i = 0 + while ( + i < this.keyframes.length - 1 && this.keyframes[i].position > arg + ) i++ + return this.keyframes[i].value + } + case PropertyFunction.Linear: + case PropertyFunction.Curve1: + case PropertyFunction.Curve2: { + //TODO: Implement better approximations for Curve1 and Curve2 prop values + let i = 0 + while ( + i < this.keyframes.length - 1 && this.keyframes[i].position > arg + ) i++ + if (i < this.keyframes.length - 1) { + const d = this.keyframes[i+1].position - this.keyframes[i].position + if (d === 0) return this.keyframes[i].value + const p = (arg - this.keyframes[i].position) / d + return (this.valueType === ValueType.Scalar ? + lerp( + this.keyframes[i].value as number, + this.keyframes[i+1].value as number, + p + ) + : arrayOf(this.componentCount, j => lerp( + this.keyframes[i].value[j], + this.keyframes[i+1].value[j], + p + )) + ) as ValueTypeMap[T] + } + return this.keyframes[i].value + } + } + } + + clone(): SequenceProperty { + return new SequenceProperty( + this.valueType, + this.function, + this.loop, + this.keyframes.map(e => Keyframe.copy(e)), + this.modifiers.map(e => Modifier.copy(e)) + ) + } + + separateComponents(): SequenceProperty[] { + if (this.valueType === ValueType.Scalar) { + return [this.clone() as SequenceProperty] + } else { + const mods = this.modifiers.map(e => e.separateComponents()) + return arrayOf(this.componentCount, i => new SequenceProperty( + ValueType.Scalar, + this.function, + this.loop, + this.keyframes.map(kf => new Keyframe( + kf.position, + kf.value[i], + kf.unkTangent1?.[i], + kf.unkTangent2?.[i] + ))).withModifiers( + ...mods.map(comps => comps[i]) + ) + ) + } + } + + get duration() { return Math.max(...this.keyframes.map(kf => kf.position)) } + set duration(value: number) { + const factor = value / this.duration + for (const kf of this.keyframes) { + kf.position *= factor + } + } + +} + +class ComponentSequenceProperty + extends Property + implements IModifiableProperty { + + declare function: PropertyFunction.CompCurve + loop: boolean + components: ISequenceProperty[] + + constructor( + valueType: T, + loop: boolean = false, + components: ISequenceProperty[], + modifiers: Modifier[] = [] + ) { + super(valueType, PropertyFunction.CompCurve, modifiers) + this.loop = loop + this.components = components + } + + sortComponentKeyframes() { + for (const comp of this.components) { + comp.sortKeyframes() + } + } + + get fieldCount(): number { + return 1 + 3 * this.componentCount + this.components.reduce((a, e) => a + 4 * e.keyframes.length, 0) + } + + get fields(): NumericalField[] { + this.sortComponentKeyframes() + return [ + new FloatField(this.components.reduce( + (a, e) => Math.max(a, e.keyframes[e.keyframes.length - 1].position), + 0 + )), + ...this.components.map(e => new IntField(e.keyframes.length)), + ...this.components.map(comp => new FloatField(Math.min(...comp.keyframes.map(e => e.value)))), + ...this.components.map(comp => new FloatField(Math.max(...comp.keyframes.map(e => e.value)))), + ...this.components.flatMap(comp => [ + ...comp.keyframes.map(e => new FloatField(e.position)), + ...comp.keyframes.map(e => new FloatField(e.value)), + ...comp.keyframes.map(e => new FloatField(e.unkTangent1)), + ...comp.keyframes.map(e => new FloatField(e.unkTangent2)), + ]) + ] + } + + static fromFields( + valueType: T, + loop: boolean, + modifiers: Modifier[], + fieldValues: number[] + ): ComponentSequenceProperty { + let offset = 1 + 3 * (valueType + 1) + return new ComponentSequenceProperty(valueType, loop, arrayOf(valueType + 1, i => { + return SequenceProperty.fromFields(ValueType.Scalar, PropertyFunction.Curve2, false, [], [ + fieldValues[1 + i], 0, 0, + ...fieldValues.slice(offset, offset = offset + 4 * fieldValues[1 + i]) + ]) + }), modifiers) + } + + toJSON() { + const o: { + function: 'CompCurve' + components: IKeyframe[][] + loop?: boolean + modifiers?: any[] + } = { + function: 'CompCurve', + components: [] + } + if (this.loop) o.loop = true + o.components = this.components.map(e => e.keyframes.map(f => ({ + position: f.position, + value: f.value, + unkTangent1: f.unkTangent1, + unkTangent2: f.unkTangent2, + }))) + if (this.modifiers.length > 0) o.modifiers = this.modifiers.map(e => e.toJSON()) + return o + } + + scale(factor: number) { + for (const comp of this.components) { + comp.scale(factor) + } + this.modifiersScale(factor) + return this + } + + power(exponent: number) { + for (const comp of this.components) { + comp.power(exponent) + } + return this + } + + add(summand: number) { + for (const comp of this.components) { + comp.add(summand) + } + return this + } + + valueAt(arg: number): ValueTypeMap[T] { + return ( + this.valueType === ValueType.Scalar ? + this.components[0].valueAt(arg) + : this.components.map(e => e.valueAt(arg)) + ) as ValueTypeMap[T] + } + + clone(): ComponentSequenceProperty { + return new ComponentSequenceProperty( + this.valueType, + this.loop, + this.components.map(e => e.clone()), + this.modifiers.map(e => Modifier.copy(e)) + ) + } + + separateComponents(): ComponentSequenceProperty[] { + if (this.valueType === ValueType.Scalar) { + return [this.clone() as ComponentSequenceProperty] + } else { + const mods = this.modifiers.map(e => e.separateComponents()) + return arrayOf(this.componentCount, i => new ComponentSequenceProperty( + ValueType.Scalar, + this.loop, + [this.components[i]], + mods.map(comps => comps[i]) + )) + } + } + + /** + * Combines the components to form a new {@link SequenceProperty} with + * roughly the same values and the same modifiers. + * + * Note that linear interpolation is used to approximate both sampling of the + * components as well as the function of the output property, meaning that + * non-linear curves used are lost in this conversion. + */ + combineComponents() { + const positions = new Set + for (const comp of this.components) { + for (const keyframe of comp.keyframes) { + positions.add(keyframe.position) + } + } + const keyframes = Array.from(positions).sort((a, b) => a - b).map(e => new Keyframe(e, this.valueAt(e))) + return new LinearProperty(this.loop, keyframes).withModifiers(...this.modifiers.map(mod => Modifier.copy(mod))) + } + + get duration() { return Math.max(...this.components.flatMap(c => c.keyframes.map(kf => kf.position))) } + set duration(value: number) { + const factor = value / this.duration + for (const c of this.components) { + for (const kf of c.keyframes) { + kf.position *= factor + } + } + } + +} + +class ConstantProperty extends ValueProperty { + + constructor(...args: number[]) { + args = args.slice(0, 4) + super(args.length - 1 as T, (args.length === 1 ? args[0] : args) as ValueTypeMap[T]) + } + +} + +class SteppedProperty extends SequenceProperty { + + constructor(loop: boolean, keyframes: IKeyframe[]) { + if (keyframes.length < 2) { + throw new Error ('Properties with a stepped function must have at least 2 stops.') + } + const comps = Array.isArray(keyframes[0].value) ? keyframes[0].value.length : 1 + super(comps - 1 as T, PropertyFunction.Stepped, loop, keyframes) + } + +} + +class LinearProperty extends SequenceProperty { + + constructor(loop: boolean, keyframes: IKeyframe[]) { + if (keyframes.length < 2) { + throw new Error ('Properties with a linear function must have at least 2 stops.') + } + const comps = Array.isArray(keyframes[0].value) ? keyframes[0].value.length : 1 + super(comps - 1 as T, PropertyFunction.Linear, loop, keyframes) + } + + /** + * Creates a new linear property with only two steps. + * @param loop Controls whether the property should loop or not. + * @param endPosition The position of the second stop. + * @param startValue The value of the first stop. + * @param endValue The value of the second stop. + * @returns The new linear property. + */ + static basic( + loop: boolean, + endPosition: number, + startValue: PropertyValue, + endValue: PropertyValue + ) { + return new LinearProperty(loop, [ + new Keyframe(0, startValue), + new Keyframe(endPosition, endValue), + ]) + } + + /** + * Creates a new linear property that approximates a power function. + * @param loop Controls whether the property should loop or not. + * @param exponent The exponent used in the power function. For example, + * setting this to values greater than 1 will make the property value change + * slowly at the start, but get faster and faster until it reaches the end. + * @param stops How many stops to use. Must be greater than or equal to 2. + * Using higher values will produce a smoother curve. Setting it to 2 will + * make it linear, which means you might as well use the {@link basic} + * method instead of this. + * @param endPosition The position of the last stop. + * @param startValue The value of the first stop. + * @param endValue The value of the last stop. + * @returns The new linear property. + */ + static power( + loop: boolean, + exponent: number, + stops: number, + endPosition: number, + startValue: PropertyValue, + endValue: PropertyValue + ) { + if (stops < 2) { + throw new Error('Property stop count must be greater than or equal to 2.') + } + if (Array.isArray(startValue) && Array.isArray(endValue)) { + return new LinearProperty(loop, arrayOf(stops, i => new Keyframe( + i / (stops - 1) * endPosition, + startValue.map((e: number, j: number) => lerp(e, endValue[j], (i / (stops - 1)) ** exponent)) as Vector + ))) + } else if (typeof startValue === 'number' && typeof endValue === 'number') { + return new LinearProperty(loop, arrayOf(stops, i => new Keyframe( + i / (stops - 1) * endPosition, + lerp(startValue, endValue, (i / (stops - 1)) ** exponent) + ))) + } else { + throw new Error('startValue and endValue must be of the same type.') + } + } + + /** + * Creates a new linear property that approximates a sine wave. + * @param min The value used when the sine wave is at its minimum. + * @param max The value used when the sine wave is at its maximum. + * @param period The period of the sine wave. + * @param stops The number of stops to use to approximate the sine wave. + * Higher values result in a smoother curve. Defaults to 21. + * @returns The new linear property. + */ + static sine( + min: PropertyValue, + max: PropertyValue, + period: number, + stops: number = 21 + ) { + if (Array.isArray(min) && Array.isArray(max)) { + return new LinearProperty(true, arrayOf(stops, i => new Keyframe( + i / (stops - 1) * period, + min.map((e, j) => (max[j] + e) / 2 + (max[j] - e) / 2 * Math.sin(i / (stops - 1) * Math.PI * 2)) as Vector + ))) + } else if (typeof min === 'number' && typeof max === 'number') { + return new LinearProperty(true, arrayOf(stops, i => new Keyframe( + i / (stops - 1) * period, + (max + min) / 2 + (max - min) / 2 * Math.sin(i / (stops - 1) * Math.PI * 2) + ))) + } else { + throw new Error('min and max must be of the same type.') + } + } + +} + +class Curve2Property extends SequenceProperty { + + constructor(loop: boolean, keyframes: IKeyframe[]) { + if (keyframes.length < 2) { + throw new Error ('Properties with a curve function must have at least 2 stops.') + } + const comps = Array.isArray(keyframes[0].value) ? keyframes[0].value.length : 1 + super(comps - 1 as T, PropertyFunction.Curve2, loop, keyframes) + } + +} + +/** + * Creates a property with a {@link RandomizerModifier} and no value, + * effectively creating a property with a random value in a given range. + * @param minValue The lower bound of the range of possible values for the + * property. + * @param maxValue The upper bound of the range of possible values for the + * property. + * @param seed A seed or set of seeds for the random number generator to use + * to generate the random property values. + * @returns + */ +function RandomProperty(minValue: PropertyValue, maxValue: PropertyValue, seed: PropertyValue = randomInt32()) { + return new ValueProperty( + (Array.isArray(minValue) ? minValue.length - 1 : ValueType.Scalar) as ValueType, + Array.isArray(minValue) ? Array(minValue.length).fill(0) as Vector : 0, + [ new RandomizerModifier(minValue, maxValue, seed) ] + ) +} + +/** + * Generates a rainbow color animation property with a configurable duration. + * @param duration How long it takes to go around the entire hue circle in + * seconds. Defaults to 4 seconds. + * @param loop Controls whether the animation should loop or not. Defaults to + * true. + * @returns + */ +function RainbowProperty(duration: number = 4, loop: boolean = true) { + const unit = duration / 6 + return new LinearProperty(loop, [ + new Keyframe(0, [1, 0, 0, 1]), + new Keyframe(unit, [1, 0, 1, 1]), + new Keyframe(unit * 2, [0, 0, 1, 1]), + new Keyframe(unit * 3, [0, 1, 1, 1]), + new Keyframe(unit * 4, [0, 1, 0, 1]), + new Keyframe(unit * 5, [1, 1, 0, 1]), + new Keyframe(unit * 6, [1, 0, 0, 1]), + ]) +} + +class Modifier { + + static #typeEnumBValues = { + [ModifierType.Randomizer1]: 0, + [ModifierType.Randomizer2]: 4, + [ModifierType.ExternalValue1]: 8, + [ModifierType.ExternalValue2]: 12, + [ModifierType.Randomizer3]: 16, + } + + typeEnumA: number + typeEnumB: number + fields: NumericalField[] + properties: IProperty[] + + constructor( + type: ModifierType, + valueType: ValueType, + fields: NumericalField[] = [], + properties: IProperty[] = [] + ) { + this.type = type + this.valueType = valueType + this.fields = fields + this.properties = properties + } + + static typeEnumAToModifierType(typeEnumA: number): ModifierType { + return (typeEnumA >>> 12 & 0b11) << 4 | typeEnumA >>> 4 & 0b1111 + } + + static modifierTypeToTypeEnumA(type: ModifierType, valueType: ValueType = ValueType.Scalar) { + return (type >>> 4 | 0b1100) << 12 | (type & 0b1111) << 4 | valueType + } + + static copy(mod: Modifier) { + const copy = new Modifier( + mod.type, + mod.valueType, + mod.fields.map(f => Field.copy(f) as NumericalField), + mod.properties.map(p => p.clone()) + ) + copy.typeEnumB = mod.typeEnumB + return copy + } + + get type(): ModifierType { + return Modifier.typeEnumAToModifierType(this.typeEnumA) + } + + set type(value) { + const valueType = this.valueType + this.typeEnumA = Modifier.modifierTypeToTypeEnumA(value, valueType) + this.typeEnumB = Modifier.#typeEnumBValues[value] | valueType + } + + /** + * Sets the modifier type and returns the modifier. + * @param type The new type for the modifier. + */ + withType(type: ModifierType) { + this.type = type + return this + } + + get valueType(): ValueType { + return this.typeEnumA & 0b11 + } + + set valueType(value) { + this.typeEnumA = (this.typeEnumA & 0xfffc) | value + this.typeEnumB = (this.typeEnumB & 0xfffffffc) | value + } + + /** + * Sets the value type and returns the modifier. + * @param type The new value type for the modifier. + */ + withValueType(type: ValueType) { + this.valueType = type + return this + } + + static fromJSON({ + typeEnumA, + typeEnumB, + fields, + properties = [], + }: { + typeEnumA: number + typeEnumB: number + fields: [] + properties?: [] + }): Modifier { + const mod = new Modifier( + Modifier.typeEnumAToModifierType(typeEnumA), + typeEnumA & 0b11, + fields.map(field => Field.fromJSON(field) as NumericalField), + properties.map(prop => Property.fromJSON(prop)) + ) + mod.typeEnumB = typeEnumB + return mod + } + + toJSON() { + const o: { + typeEnumA: number + typeEnumB: number + fields: any[] + properties?: any[] + } = { + typeEnumA: this.typeEnumA, + typeEnumB: this.typeEnumB, + fields: this.fields.map(field => field.toJSON()), + } + if (this.properties.length > 0) o.properties = this.properties.map(prop => prop.toJSON()) + return o + } + + separateComponents(): Modifier[] { + if (this.valueType === ValueType.Scalar) { + return [ Modifier.copy(this) ] + } else { + const cc = this.valueType + 1 + const type = this.type + switch (type) { + case ModifierType.ExternalValue1: + case ModifierType.ExternalValue2: + const props = this.properties[0].separateComponents() + return arrayOf(cc, i => new Modifier(type, ValueType.Scalar, [Field.copy(this.fields[0])], [props[i]])) + case ModifierType.Randomizer1: + case ModifierType.Randomizer3: + return arrayOf(cc, i => new Modifier(type, ValueType.Scalar, [ + Field.copy(this.fields[i]), + Field.copy(this.fields[cc + i]), + ])) + case ModifierType.Randomizer2: + return arrayOf(cc, i => new Modifier(type, ValueType.Scalar, [ + Field.copy(this.fields[i]), + Field.copy(this.fields[cc + i]), + Field.copy(this.fields[2 * cc + i]), + ])) + } + } + } + +} + +/** + * A property modifier that changes the property value depending on an + * {@link ExternalValue external value}. + * + * The property value wil be multiplied by the values in this modifier. + */ +class ExternalValueModifier extends Modifier { + + /** + * @param extVal The ID of the external value to use. + * @param loop Controls if the modifier property should loop or not. + * @param keyframes An array of keyframes with positions representing the + * external value and the keyframe value representing the modifier value it + * maps to. + * @param type Controls what type of modifier to use. Defaults to + * {@link ModifierType.ExternalValue1}. + */ + constructor( + extVal: ExternalValue, + loop: boolean, + keyframes: IKeyframe[], + type: ModifierType.ExternalValue1 | ModifierType.ExternalValue2 = ModifierType.ExternalValue1 + ) { + const valueType = typeof keyframes[0].value === 'number' ? 0 : keyframes[0].value.length - 1 + super(type, valueType, [ + new IntField(extVal) + ], [ + new LinearProperty(loop, keyframes) + ]) + } + + get externalValue() { return this.fields[0].value as number } + set externalValue(value) { this.fields[0].value = value } + + valueAt(arg: number) { return this.properties[0].valueAt(arg) } + +} + +/** + * A property modifier that changes the property value depending on the + * "Display Blood" option. + * + * The property value wil be multiplied by the values in this modifier. + */ +class BloodVisibilityModifier extends ExternalValueModifier { + + declare properties: [SequenceProperty] + + /** + * @param onValue The value when "Display Blood" is set to "On". + * @param mildValue The value when "Display Blood" is set to "Mild". + * @param offValue The value when "Display Blood" is set to "Off". + * @param type Controls what type of modifier to use. Defaults to + * {@link ModifierType.ExternalValue1}. + */ + constructor( + onValue: ValueTypeMap[T], + mildValue: ValueTypeMap[T], + offValue: ValueTypeMap[T], + type: ModifierType.ExternalValue1 | ModifierType.ExternalValue2 = ModifierType.ExternalValue1 + ) { + super(ExternalValue.DisplayBlood, false, [ + new Keyframe(-1, offValue), + new Keyframe(0, onValue), + new Keyframe(1, mildValue), + ], type) + } + + get offValue() { return this.properties[0].keyframes[0].value } + set offValue(value) { this.properties[0].keyframes[0].value = value } + + get onValue() { return this.properties[0].keyframes[1].value } + set onValue(value) { this.properties[0].keyframes[1].value = value } + + get mildValue() { return this.properties[0].keyframes[2].value } + set mildValue(value) { this.properties[0].keyframes[2].value = value } + +} + +/** + * A property modifier that changes the property's value based on the weather. + * + * Only functional in Elden Ring. + */ +class PrecipitationModifier extends ExternalValueModifier { + + /** + * @param clear The value when it's not raining or snowing. + * @param precip The value when it's raining or snowing. + */ + constructor(clear: ValueTypeMap[T], precip: ValueTypeMap[T]) { + super(ExternalValue.Precipitation, false, [ + { position: 0, value: clear }, + { position: 1, value: precip }, + ]) + } + +} + +/** + * A property modifer that changes the property value by a random amount in a + * given range. + */ +class RandomizerModifier extends Modifier { + + constructor(minValue: PropertyValue, maxValue: PropertyValue, seed: PropertyValue = randomInt32()) { + if (Array.isArray(minValue)) { + if (!Array.isArray(maxValue) || maxValue.length !== minValue.length) { + throw new Error(`Incompatible min and max values for randomizer modifier: Min: ${minValue.toString()}, Max: ${maxValue.toString()}`) + } + if (minValue.length < 1 || minValue.length > 4) { + throw new Error(`Invalid number of vector components: ${minValue.length}`) + } + const seedArray = Array.isArray(seed) ? seed : [seed] + const valueType = minValue.length - 1 + super(ModifierType.Randomizer2, valueType, [ + ...arrayOf(minValue.length, i => new IntField(seedArray[i % seedArray.length])), + ...minValue.map(e => new FloatField(e)), + ...maxValue.map(e => new FloatField(e)), + ]) + } else { + if (Array.isArray(maxValue)) { + throw new Error(`Incompatible min and max values for randomizer modifier: Min: ${minValue}, Max: ${maxValue.toString()}`) + } + if (Array.isArray(seed)) { + throw new Error('Random scalar modifiers cannot use vector seeds.') + } + super(ModifierType.Randomizer2, ValueType.Scalar, [ + new IntField(seed), + new FloatField(minValue), + new FloatField(maxValue), + ]) + } + } + +} + +class Section10 { + + fields: NumericalField[] + + constructor(fields: NumericalField[]) { + this.fields = fields + } + + static fromJSON(fields: []) { + return new Section10(fields.map(field => Field.fromJSON(field) as NumericalField)) + } + + toJSON() { + return this.fields.map(field => field.toJSON()) + } + +} + +export { + Game, + FXRVersion, + NodeType, + EffectType, + ActionType, + ValueType, + PropertyFunction, + ModifierType, + FieldType, + BlendMode, + ExternalValue, + Operator, + OperandType, + AttachMode, + PropertyArgument, + OrientationMode, + TracerOrientationMode, + LightingMode, + DistortionMode, + DistortionShape, + InitialDirection, + + Nodes, + EffectActionSlots, + ActionData, + Actions, + DataActions, + + FXR, + + State, + StateCondition, + + Node, + GenericNode, + NodeWithEffects, + RootNode, + ProxyNode, + LevelOfDetailNode, + BasicNode, + SharedEmitterNode, + + Effect, + LevelOfDetailEffect, + BasicEffect, + SharedEmitterEffect, + + Action, + DataAction, + NodeMovement, + NodeTransform, + ParticleMovement, + StateEffectMap, + EmitAllParticles, + EmitRandomParticles, + OneTimeEmitter, + NoParticleSpread, + /*#ActionsExport start*/ + NodeTranslation, + NodeAttachToCamera, + PlaySound, + NodeAttributes, + ParticleAttributes, + Unk130, + ParticleModifier, + SFXReference, + LevelOfDetailThresholds, + PeriodicEmitter, + EqualDistanceEmitter, + PointEmitterShape, + DiskEmitterShape, + RectangleEmitterShape, + SphereEmitterShape, + BoxEmitterShape, + CylinderEmitterShape, + CircularParticleSpread, + EllipticalParticleSpread, + RectangularParticleSpread, + PointSprite, + Line, + QuadLine, + BillboardEx, + MultiTextureBillboardEx, + Model, + Tracer, + Distortion, + RadialBlur, + PointLight, + Unk701, + NodeWindSpeed, + ParticleWindSpeed, + NodeWindAcceleration, + ParticleWindAcceleration, + Unk800, + DynamicTracer, + WaterInteraction, + RichModel, + Unk10500, + SpotLight, + /*#ActionsExport end*/ + + Field, + BoolField, + IntField, + FloatField, + + Keyframe, + Property, + ValueProperty, + SequenceProperty, + ComponentSequenceProperty, + ConstantProperty, + SteppedProperty, + LinearProperty, + Curve2Property, + RandomProperty, + RainbowProperty, + anyValueMult, + combineComponents, + separateComponents, + + Modifier, + ExternalValueModifier, + BloodVisibilityModifier, + PrecipitationModifier, + RandomizerModifier, + + Section10 +} diff --git a/tsconfig-base.json b/tsconfig-base.json index 341450b..5fe7419 100644 --- a/tsconfig-base.json +++ b/tsconfig-base.json @@ -4,8 +4,5 @@ "declaration": true, "sourceMap": true }, - "typedocOptions": { - "out": "docs", - "entryPoints": ["fxr.ts"] - } + "files": ["dist/fxr.ts"] } diff --git a/tsconfig-cjs.json b/tsconfig-cjs.json index 0148c4b..401f756 100644 --- a/tsconfig-cjs.json +++ b/tsconfig-cjs.json @@ -3,6 +3,6 @@ "compilerOptions": { "target": "ES2022", "module": "commonjs", - "outDir": "js/cjs" + "outDir": "dist/cjs" } } diff --git a/tsconfig-esm.json b/tsconfig-esm.json new file mode 100644 index 0000000..e0f71f9 --- /dev/null +++ b/tsconfig-esm.json @@ -0,0 +1,8 @@ +{ + "extends": "./tsconfig-base.json", + "compilerOptions": { + "target": "ES2022", + "module": "ES2022", + "outDir": "dist/esm" + } +} diff --git a/tsconfig.json b/tsconfig.json index cd295b2..7b2a1f0 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -2,7 +2,11 @@ "extends": "./tsconfig-base.json", "compilerOptions": { "target": "ES2022", - "module": "ES2022", - "outDir": "js" + "module": "ES2022" + }, + "files": ["src/fxr.ts"], + "typedocOptions": { + "out": "docs", + "entryPoints": ["src/fxr.ts"] } }